diff --git a/TODO.markdown b/TODO.markdown index 6642074efb..ac0b0cafdc 100644 --- a/TODO.markdown +++ b/TODO.markdown @@ -1,59 +1,61 @@ # Semi-random collection of tasks we'd like to get done ## Targeted for 1.2 - Have crm_mon listen for stonith events (and send email/snmp traps) - Block after N stonith failures (per node) - Move more private functions out of public headers (eg. much of include/common/util.h) - Need a way to indicate when unfencing operations need to be initiated from the host to be unfenced - Figure out how to sanely allow nodes with corosync but no pacemaker - sysconfig variable that tells pacemaker whether to fence members without pacemaker? - Arrange for nodes that didnt complete the join to be fenced - Remove instance numbers from anonymous clones +- Create a proper struct for stonith notify events ## Targeted for 1.2.x - Check for uppercase letters in node names, warn if found - Imply startup-failure-is-fatal from on-fail="restart" - Show an english version of the config with crm_resource --rules - Convert cts/CIB.py into a supported Python API for the CIB - Use crm_log_tag() in the PE to allow per-resource trace logging - Reduce the amount of stonith-ng logging - Reduce the amount of attrd logging - Use dlopen for snmp + ## Targeted for 1.4 - Support A colocated with (B || C || D) - Implement a truely atomic version of attrd - Support rolling average values in attrd - Support heartbeat with the mcp - Freeze/Thaw - Create Pacemaker plugin for snmpd - http://www.net-snmp.org/ - Investigate using a DB as the back-end for the CIB # Testing - Write a regression test for Stonith-NG - Convert BandwidthTest CTS test into a Scenario wrapper - find_operations() is not covered by PE regression tests - Some node states in determine_online_status_fencing() are untested by PE regression tests - no_quorum_policy==suicide is not covered by PE regression tests - parse_xml_duration() is not covered by PE regression tests - phase_of_the_moon() is not covered by PE regression tests - test_role_expression() is not covered by PE regression tests - native_parameter() is not covered by PE regression tests - clone_resource_state() is not covered by PE regression tests - clone_active() is not covered by PE regression tests - convert_non_atomic_task() in native.c is not covered by PE regression tests - clone_rsc_colocation_lh() is not covered by PE regression tests - group_rsc_colocation_lh() is not covered by PE regression tests - Test on-fail=standby # Documentation - Clusters from Scratch: Mail - Clusters from Scratch: MySQL - Document reload in Pacemaker Explained - Use ann:defaultValue="..." instead of in the schema more often - Allow Clusters from Scratch to be built in two flavors - pcs and crm shell diff --git a/crmd/te_utils.c b/crmd/te_utils.c index 66b111b752..e7d33574bc 100644 --- a/crmd/te_utils.c +++ b/crmd/te_utils.c @@ -1,416 +1,417 @@ /* * Copyright (C) 2004 Andrew Beekhof * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include +#include crm_trigger_t *stonith_reconnect = NULL; GListPtr stonith_cleanup_list = NULL; static gboolean fail_incompletable_stonith(crm_graph_t * graph) { GListPtr lpc = NULL; const char *task = NULL; xmlNode *last_action = NULL; if (graph == NULL) { return FALSE; } for (lpc = graph->synapses; lpc != NULL; lpc = lpc->next) { GListPtr lpc2 = NULL; synapse_t *synapse = (synapse_t *) lpc->data; if (synapse->confirmed) { continue; } for (lpc2 = synapse->actions; lpc2 != NULL; lpc2 = lpc2->next) { crm_action_t *action = (crm_action_t *) lpc2->data; if (action->type != action_type_crm || action->confirmed) { continue; } task = crm_element_value(action->xml, XML_LRM_ATTR_TASK); if (task && safe_str_eq(task, CRM_OP_FENCE)) { action->failed = TRUE; last_action = action->xml; update_graph(graph, action); crm_notice("Failing action %d (%s): STONITHd terminated", action->id, ID(action->xml)); } } } if (last_action != NULL) { crm_warn("STONITHd failure resulted in un-runnable actions"); abort_transition(INFINITY, tg_restart, "Stonith failure", last_action); return TRUE; } return FALSE; } static void tengine_stonith_connection_destroy(stonith_t * st, const char *event, xmlNode * msg) { if (is_set(fsa_input_register, R_ST_REQUIRED)) { crm_crit("Fencing daemon connection failed"); mainloop_set_trigger(stonith_reconnect); } else { crm_info("Fencing daemon disconnected"); } /* cbchan will be garbage at this point, arrange for it to be reset */ stonith_api->state = stonith_disconnected; if (AM_I_DC) { fail_incompletable_stonith(transition_graph); trigger_graph(); } } /* */ #if SUPPORT_CMAN # include #endif static void tengine_stonith_notify(stonith_t * st, const char *event, xmlNode * msg) { int rc = -99; const char *origin = NULL; const char *target = NULL; const char *executioner = NULL; xmlNode *action = get_xpath_object("//st-data", msg, LOG_ERR); if (action == NULL) { crm_log_xml_err(msg, "Notify data not found"); return; } crm_log_xml_debug(msg, "stonith_notify"); crm_element_value_int(msg, F_STONITH_RC, &rc); origin = crm_element_value(action, F_STONITH_ORIGIN); target = crm_element_value(action, F_STONITH_TARGET); executioner = crm_element_value(action, F_STONITH_DELEGATE); if (rc == pcmk_ok && crm_str_eq(target, fsa_our_uname, TRUE)) { crm_err("We were alegedly just fenced by %s for %s!", executioner, origin); register_fsa_error_adv(C_FSA_INTERNAL, I_ERROR, NULL, NULL, __FUNCTION__); return; } crm_notice("Peer %s was%s terminated (%s) by %s for %s: %s (ref=%s)", target, rc == pcmk_ok?"":" not", crm_element_value(action, F_STONITH_OPERATION), executioner ? executioner : "", origin, pcmk_strerror(rc), crm_element_value(action, F_STONITH_REMOTE)); #if SUPPORT_CMAN if (rc == pcmk_ok && is_cman_cluster()) { int local_rc = 0; int confirm = 0; char *target_copy = strdup(target); /* In case fenced hasn't noticed yet */ local_rc = fenced_external(target_copy); if (local_rc != 0) { crm_err("Could not notify CMAN that '%s' is now fenced: %d", target, local_rc); } else { crm_notice("Notified CMAN that '%s' is now fenced", target); } /* In case fenced is already trying to shoot it */ confirm = open("/var/run/cluster/fenced_override", O_NONBLOCK|O_WRONLY); if (confirm) { int ignore = 0; int len = strlen(target_copy); errno = 0; local_rc = write(confirm, target_copy, len); ignore = write(confirm, "\n", 1); if(errno == EBADF) { crm_trace("CMAN not expecting %s to be fenced (yet)", target); } else if (local_rc < len) { crm_perror(LOG_ERR, "Confirmation of CMAN fencing event for '%s' failed: %d", target, local_rc); } else { fsync(confirm); crm_notice("Confirmed CMAN fencing event for '%s'", target); } close(confirm); } } #endif if (rc == pcmk_ok) { crm_trace("target=%s dc=%s", target, fsa_our_dc); if (fsa_our_dc == NULL || safe_str_eq(fsa_our_dc, target)) { crm_notice("Target %s our leader %s (recorded: %s)", fsa_our_dc?"was":"may have been", target, fsa_our_dc ? fsa_our_dc : ""); /* Given the CIB resyncing that occurs around elections, * have one node update the CIB now and, if the new DC is different, * have them do so too after the election */ if (safe_str_eq(executioner, fsa_our_uname)) { const char *uuid = get_uuid(target); send_stonith_update(NULL, target, uuid); } stonith_cleanup_list = g_list_append(stonith_cleanup_list, strdup(target)); } } } gboolean te_connect_stonith(gpointer user_data) { int lpc = 0; int rc = pcmk_ok; if (stonith_api == NULL) { stonith_api = stonith_api_new(); } if (stonith_api->state != stonith_disconnected) { crm_trace("Still connected"); return TRUE; } for (lpc = 0; lpc < 30; lpc++) { crm_debug("Attempting connection to fencing daemon..."); sleep(1); rc = stonith_api->cmds->connect(stonith_api, crm_system_name, NULL); if (rc == pcmk_ok) { break; } if (user_data != NULL) { crm_err("Sign-in failed: triggered a retry"); mainloop_set_trigger(stonith_reconnect); return TRUE; } crm_err("Sign-in failed: pausing and trying again in 2s..."); sleep(1); } CRM_CHECK(rc == pcmk_ok, return TRUE); /* If not, we failed 30 times... just get out */ stonith_api->cmds->register_notification(stonith_api, T_STONITH_NOTIFY_DISCONNECT, tengine_stonith_connection_destroy); stonith_api->cmds->register_notification(stonith_api, STONITH_OP_FENCE, tengine_stonith_notify); crm_trace("Connected"); return TRUE; } gboolean stop_te_timer(crm_action_timer_t * timer) { const char *timer_desc = "action timer"; if (timer == NULL) { return FALSE; } if (timer->reason == timeout_abort) { timer_desc = "global timer"; crm_trace("Stopping %s", timer_desc); } if (timer->source_id != 0) { crm_trace("Stopping %s", timer_desc); g_source_remove(timer->source_id); timer->source_id = 0; } else { crm_trace("%s was already stopped", timer_desc); return FALSE; } return TRUE; } gboolean te_graph_trigger(gpointer user_data) { enum transition_status graph_rc = -1; if (transition_graph == NULL) { crm_debug("Nothing to do"); return TRUE; } crm_trace("Invoking graph %d in state %s", transition_graph->id, fsa_state2string(fsa_state)); switch (fsa_state) { case S_STARTING: case S_PENDING: case S_NOT_DC: case S_HALT: case S_ILLEGAL: case S_STOPPING: case S_TERMINATE: return TRUE; break; default: break; } if (transition_graph->complete == FALSE) { graph_rc = run_graph(transition_graph); print_graph(LOG_DEBUG_3, transition_graph); if (graph_rc == transition_active) { crm_trace("Transition not yet complete"); return TRUE; } else if (graph_rc == transition_pending) { crm_trace("Transition not yet complete - no actions fired"); return TRUE; } if (graph_rc != transition_complete) { crm_err("Transition failed: %s", transition_status(graph_rc)); print_graph(LOG_WARNING, transition_graph); } } crm_debug("Transition %d is now complete", transition_graph->id); transition_graph->complete = TRUE; notify_crmd(transition_graph); return TRUE; } void trigger_graph_processing(const char *fn, int line) { mainloop_set_trigger(transition_trigger); crm_trace("%s:%d - Triggered graph processing", fn, line); } void abort_transition_graph(int abort_priority, enum transition_action abort_action, const char *abort_text, xmlNode * reason, const char *fn, int line) { const char *magic = NULL; CRM_CHECK(transition_graph != NULL, return); if (reason) { int diff_add_updates = 0; int diff_add_epoch = 0; int diff_add_admin_epoch = 0; int diff_del_updates = 0; int diff_del_epoch = 0; int diff_del_admin_epoch = 0; xmlNode *diff = get_xpath_object("//" F_CIB_UPDATE_RESULT "//diff", reason, LOG_DEBUG_2); magic = crm_element_value(reason, XML_ATTR_TRANSITION_MAGIC); if (diff) { cib_diff_version_details(diff, &diff_add_admin_epoch, &diff_add_epoch, &diff_add_updates, &diff_del_admin_epoch, &diff_del_epoch, &diff_del_updates); if (crm_str_eq(TYPE(reason), XML_CIB_TAG_NVPAIR, TRUE)) { crm_info("%s:%d - Triggered transition abort (complete=%d, tag=%s, id=%s, name=%s, value=%s, magic=%s, cib=%d.%d.%d) : %s", fn, line, transition_graph->complete, TYPE(reason), ID(reason), NAME(reason), VALUE(reason), magic ? magic : "NA", diff_add_admin_epoch, diff_add_epoch, diff_add_updates, abort_text); } else { crm_info("%s:%d - Triggered transition abort (complete=%d, tag=%s, id=%s, magic=%s, cib=%d.%d.%d) : %s", fn, line, transition_graph->complete, TYPE(reason), ID(reason), magic ? magic : "NA", diff_add_admin_epoch, diff_add_epoch, diff_add_updates, abort_text); } } else { crm_info("%s:%d - Triggered transition abort (complete=%d, tag=%s, id=%s, magic=%s) : %s", fn, line, transition_graph->complete, TYPE(reason), ID(reason), magic ? magic : "NA", abort_text); } } else { crm_info("%s:%d - Triggered transition abort (complete=%d) : %s", fn, line, transition_graph->complete, abort_text); } switch (fsa_state) { case S_STARTING: case S_PENDING: case S_NOT_DC: case S_HALT: case S_ILLEGAL: case S_STOPPING: case S_TERMINATE: crm_info("Abort suppressed: state=%s (complete=%d)", fsa_state2string(fsa_state), transition_graph->complete); return; default: break; } if (magic == NULL && reason != NULL) { crm_log_xml_debug(reason, "Cause"); } /* Make sure any queued calculations are discarded ASAP */ free(fsa_pe_ref); fsa_pe_ref = NULL; if (transition_graph->complete) { if (transition_timer->period_ms > 0) { crm_timer_stop(transition_timer); crm_timer_start(transition_timer); } else { register_fsa_input(C_FSA_INTERNAL, I_PE_CALC, NULL); } return; } update_abort_priority(transition_graph, abort_priority, abort_action, abort_text); mainloop_set_trigger(transition_trigger); } diff --git a/include/crm/fencing/internal.h b/include/crm/fencing/internal.h index 59f1ce449a..2f022cce56 100644 --- a/include/crm/fencing/internal.h +++ b/include/crm/fencing/internal.h @@ -1,63 +1,120 @@ /* * Copyright (C) 2011 Andrew Beekhof * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef STONITH_NG_INTERNAL__H # define STONITH_NG_INTERNAL__H # include # include typedef struct async_command_s { int id; int pid; int stdout; int options; int timeout; char *op; char *origin; char *client; char *remote; char *victim; char *action; char *device; char *mode; GListPtr device_list; GListPtr device_next; void (*done)(GPid pid, gint status, gpointer user_data); guint timer_sigterm; guint timer_sigkill; } async_command_t; int run_stonith_agent(const char *agent, const char *action, const char *victim, GHashTable * dev_hash, GHashTable * port_map, int *agent_result, char **output, async_command_t * track); gboolean is_redhat_agent(const char *agent); xmlNode *create_level_registration_xml(const char *node, int level, stonith_key_value_t * device_list); xmlNode *create_device_registration_xml(const char *id, const char *namespace, const char *agent, stonith_key_value_t * params); +#define ST_LEVEL_MAX 10 + +#define F_STONITH_CLIENTID "st_clientid" +#define F_STONITH_CALLOPTS "st_callopt" +#define F_STONITH_CALLID "st_callid" +#define F_STONITH_CALLDATA "st_calldata" +#define F_STONITH_OPERATION "st_op" +#define F_STONITH_TARGET "st_target" +#define F_STONITH_REMOTE "st_remote_op" +#define F_STONITH_RC "st_rc" +#define F_STONITH_TIMEOUT "st_timeout" +#define F_STONITH_CALLBACK_TOKEN "st_async_id" +#define F_STONITH_CLIENTNAME "st_clientname" +#define F_STONITH_NOTIFY_TYPE "st_notify_type" +#define F_STONITH_NOTIFY_ACTIVATE "st_notify_activate" +#define F_STONITH_NOTIFY_DEACTIVATE "st_notify_deactivate" +#define F_STONITH_DELEGATE "st_delegate" +#define F_STONITH_ORIGIN "st_origin" +#define F_STONITH_HISTORY_LIST "st_history" +#define F_STONITH_DATE "st_date" +#define F_STONITH_STATE "st_state" +#define F_STONITH_LEVEL "st_level" +#define F_STONITH_OWNER "st_owner" + +#define F_STONITH_DEVICE "st_device_id" +#define F_STONITH_ACTION "st_device_action" +#define F_STONITH_MODE "st_mode" + +#define T_STONITH_NG "stonith-ng" +#define T_STONITH_REPLY "st-reply" + +#define T_STONITH_NOTIFY "st_notify" +#define T_STONITH_NOTIFY_DISCONNECT "st_notify_disconnect" + +#define STONITH_ATTR_ARGMAP "pcmk_arg_map" +#define STONITH_ATTR_HOSTARG "pcmk_host_argument" +#define STONITH_ATTR_HOSTMAP "pcmk_host_map" +#define STONITH_ATTR_HOSTLIST "pcmk_host_list" +#define STONITH_ATTR_HOSTCHECK "pcmk_host_check" + +#define STONITH_ATTR_ACTION_OP "option" /* To be replaced by 'action' at some point */ + +#define STONITH_OP_EXEC "st_execute" +#define STONITH_OP_QUERY "st_query" +#define STONITH_OP_FENCE "st_fence" +#define STONITH_OP_RELAY "st_relay" +#define STONITH_OP_CONFIRM "st_confirm" +#define STONITH_OP_DEVICE_ADD "st_device_register" +#define STONITH_OP_DEVICE_DEL "st_device_remove" +#define STONITH_OP_DEVICE_METADATA "st_device_metadata" +#define STONITH_OP_FENCE_HISTORY "st_fence_history" +#define STONITH_OP_LEVEL_ADD "st_level_add" +#define STONITH_OP_LEVEL_DEL "st_level_remove" + +#define stonith_channel "st_command" +#define stonith_channel_callback "st_callback" + #endif diff --git a/include/crm/stonith-ng.h b/include/crm/stonith-ng.h index 3a2ebb7cfe..9c1df6e382 100644 --- a/include/crm/stonith-ng.h +++ b/include/crm/stonith-ng.h @@ -1,294 +1,237 @@ /* * Copyright (C) 2004 Andrew Beekhof * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef STONITH_NG__H # define STONITH_NG__H # include # include /* TO-DO: Work out how to drop this requirement */ # include /* *INDENT-OFF* */ enum stonith_state { stonith_connected_command, stonith_connected_query, stonith_disconnected, }; enum stonith_call_options { st_opt_none = 0x00000000, st_opt_verbose = 0x00000001, st_opt_allow_suicide = 0x00000002, st_opt_manual_ack = 0x00000008, st_opt_discard_reply = 0x00000010, st_opt_all_replies = 0x00000020, st_opt_topology = 0x00000040, st_opt_scope_local = 0x00000100, st_opt_cs_nodeid = 0x00000200, st_opt_sync_call = 0x00001000, }; #define stonith_default_options = stonith_none -#define ST_LEVEL_MAX 10 - -#define F_STONITH_CLIENTID "st_clientid" -#define F_STONITH_CALLOPTS "st_callopt" -#define F_STONITH_CALLID "st_callid" -#define F_STONITH_CALLDATA "st_calldata" -#define F_STONITH_OPERATION "st_op" -#define F_STONITH_TARGET "st_target" -#define F_STONITH_REMOTE "st_remote_op" -#define F_STONITH_RC "st_rc" -#define F_STONITH_TIMEOUT "st_timeout" -#define F_STONITH_CALLBACK_TOKEN "st_async_id" -#define F_STONITH_CLIENTNAME "st_clientname" -#define F_STONITH_NOTIFY_TYPE "st_notify_type" -#define F_STONITH_NOTIFY_ACTIVATE "st_notify_activate" -#define F_STONITH_NOTIFY_DEACTIVATE "st_notify_deactivate" -#define F_STONITH_DELEGATE "st_delegate" -#define F_STONITH_ORIGIN "st_origin" -#define F_STONITH_HISTORY_LIST "st_history" -#define F_STONITH_DATE "st_date" -#define F_STONITH_STATE "st_state" -#define F_STONITH_LEVEL "st_level" -#define F_STONITH_OWNER "st_owner" - -#define T_STONITH_NG "stonith-ng" -#define T_STONITH_REPLY "st-reply" - -#define F_STONITH_DEVICE "st_device_id" -#define F_STONITH_ACTION "st_device_action" -#define F_STONITH_MODE "st_mode" - - -#define T_STONITH_NOTIFY "st_notify" -#define T_STONITH_NOTIFY_DISCONNECT "st_notify_disconnect" - -#define STONITH_ATTR_ARGMAP "pcmk_arg_map" -#define STONITH_ATTR_HOSTARG "pcmk_host_argument" -#define STONITH_ATTR_HOSTMAP "pcmk_host_map" -#define STONITH_ATTR_HOSTLIST "pcmk_host_list" -#define STONITH_ATTR_HOSTCHECK "pcmk_host_check" - -#define STONITH_ATTR_ACTION_OP "option" /* To be replaced by 'action' at some point */ - -#define STONITH_OP_EXEC "st_execute" -#define STONITH_OP_QUERY "st_query" -#define STONITH_OP_FENCE "st_fence" -#define STONITH_OP_RELAY "st_relay" -#define STONITH_OP_CONFIRM "st_confirm" -#define STONITH_OP_DEVICE_ADD "st_device_register" -#define STONITH_OP_DEVICE_DEL "st_device_remove" -#define STONITH_OP_DEVICE_METADATA "st_device_metadata" -#define STONITH_OP_FENCE_HISTORY "st_fence_history" -#define STONITH_OP_LEVEL_ADD "st_level_add" -#define STONITH_OP_LEVEL_DEL "st_level_remove" - -#define stonith_channel "st_command" -#define stonith_channel_callback "st_callback" enum op_state { st_query, st_exec, st_done, st_failed, }; typedef struct stonith_key_value_s { char *key; char *value; struct stonith_key_value_s *next; } stonith_key_value_t; typedef struct stonith_history_s { char *target; char *action; char *origin; char *delegate; int completed; int state; struct stonith_history_s *next; } stonith_history_t; typedef struct stonith_s stonith_t; typedef struct stonith_api_operations_s { int (*free) (stonith_t *st); int (*connect) (stonith_t *st, const char *name, int *stonith_fd); int (*disconnect)(stonith_t *st); int (*remove_device)( stonith_t *st, int options, const char *name); int (*register_device)( stonith_t *st, int options, const char *id, const char *namespace, const char *agent, stonith_key_value_t *params); int (*remove_level)( stonith_t *st, int options, const char *node, int level); int (*register_level)( stonith_t *st, int options, const char *node, int level, stonith_key_value_t *device_list); int (*metadata)(stonith_t *st, int options, const char *device, const char *namespace, char **output, int timeout); int (*list)(stonith_t *stonith, int call_options, const char *namespace, stonith_key_value_t **devices, int timeout); int (*call)(stonith_t *st, int options, const char *id, const char *action, const char *port, int timeout); int (*query)(stonith_t *st, int options, const char *node, stonith_key_value_t **devices, int timeout); int (*fence)(stonith_t *st, int options, const char *node, const char *action, int timeout); int (*confirm)(stonith_t *st, int options, const char *node); int (*history)(stonith_t *st, int options, const char *node, stonith_history_t **output, int timeout); int (*register_notification)( stonith_t *st, const char *event, void (*notify)(stonith_t *st, const char *event, xmlNode *msg)); int (*remove_notification)(stonith_t *st, const char *event); int (*register_callback)( stonith_t *st, int call_id, int timeout, bool only_success, void *userdata, const char *callback_name, void (*callback)(stonith_t *st, const xmlNode *msg, int call, int rc, xmlNode *output, void *userdata)); int (*remove_callback)(stonith_t *st, int call_id, bool all_callbacks); } stonith_api_operations_t; struct stonith_s { enum stonith_state state; int call_id; int call_timeout; void *private; stonith_api_operations_t *cmds; }; /* *INDENT-ON* */ /* Core functions */ stonith_t *stonith_api_new(void); void stonith_api_delete(stonith_t * st); void stonith_dump_pending_callbacks(stonith_t * st); const char *get_stonith_provider(const char *agent, const char *provider); bool stonith_dispatch(stonith_t * st); stonith_key_value_t *stonith_key_value_add(stonith_key_value_t * kvp, const char *key, const char *value); void stonith_key_value_freeall(stonith_key_value_t * kvp, int keys, int values); /* Basic helpers that allows nodes to be fenced and the history to be * queried without mainloop or the caller understanding the full API * * At least one of nodeid and uname are required */ int stonith_api_kick(int nodeid, const char *uname, int timeout, bool off); time_t stonith_api_time(int nodeid, const char *uname, bool in_progress); /* * Helpers for using the above functions without install-time dependancies * * Usage: * #include * * To turn a node off by corosync nodeid: * stonith_api_kick_helper(nodeid, 120, 1); * * To check the last fence date/time (also by nodeid): * last = stonith_api_time_helper(nodeid, 0); * * To check if fencing is in progress: * if(stonith_api_time_helper(nodeid, 1) > 0) { ... } * * eg. #include #include #include int main(int argc, char ** argv) { int rc = 0; int nodeid = 102; rc = stonith_api_time_helper(nodeid, 0); printf("%d last fenced at %s\n", nodeid, ctime(rc)); rc = stonith_api_kick_helper(nodeid, 120, 1); printf("%d fence result: %d\n", nodeid, rc); rc = stonith_api_time_helper(nodeid, 0); printf("%d last fenced at %s\n", nodeid, ctime(rc)); return 0; } */ # define STONITH_LIBRARY "libstonithd.so.1" static inline int stonith_api_kick_helper(int nodeid, int timeout, bool off) { static void *st_library = NULL; static int (*st_kick_fn) (int nodeid, const char *uname, int timeout, bool off) = NULL; if (st_library == NULL) { st_library = dlopen(STONITH_LIBRARY, RTLD_LAZY); } if (st_library && st_kick_fn == NULL) { st_kick_fn = dlsym(st_library, "stonith_api_kick"); } if (st_kick_fn == NULL) { return -ELIBACC; } return (*st_kick_fn) (nodeid, NULL, timeout, off); } static inline time_t stonith_api_time_helper(int nodeid, bool in_progress) { static void *st_library = NULL; static time_t(*st_time_fn) (int nodeid, const char *uname, bool in_progress) = NULL; if (st_library == NULL) { st_library = dlopen(STONITH_LIBRARY, RTLD_LAZY); } if (st_library && st_time_fn == NULL) { st_time_fn = dlsym(st_library, "stonith_api_time"); } if (st_time_fn == NULL) { return 0; } return (*st_time_fn) (nodeid, NULL, in_progress); } #endif