commit 1928af7d8d2fc7d225df22caeb7ea93964f48bde Author: David Vossel Date: Wed Apr 16 10:28:46 2014 -0500 High: fencing: Fence using all required devices (cherry picked from commit 1f39dfe13d8cbfb5dd22f8324735319f81230fdb) Conflicts: fencing/main.c diff --git a/fencing/commands.c b/fencing/commands.c index 41690f7..8efb156 100644 --- a/fencing/commands.c +++ b/fencing/commands.c @@ -111,6 +111,20 @@ typedef struct async_command_s { static xmlNode *stonith_construct_async_reply(async_command_t * cmd, const char *output, xmlNode * data, int rc); +static gboolean +is_action_required(const char *action, stonith_device_t *device) +{ + if (device->required_actions == NULL) { + return FALSE; + } + + if (strstr(device->required_actions, action)) { + return TRUE; + } + + return FALSE; +} + static int get_action_timeout(stonith_device_t * device, const char *action, int default_timeout) { @@ -318,6 +332,7 @@ free_device(gpointer data) free_xml(device->agent_metadata); free(device->namespace); free(device->on_target_actions); + free(device->required_actions); free(device->agent); free(device->id); free(device); @@ -525,10 +540,24 @@ is_nodeid_required(xmlNode * xml) return TRUE; } +static char * +add_action(char *actions, const char *action) +{ + static size_t len = 256; + if (actions == NULL) { + actions = calloc(1, len); + } + if (strlen(actions)) { + g_strlcat(actions, " ", len); + } + g_strlcat(actions, action, len); + + return actions; +} + static void -get_on_target_actions(stonith_device_t *device) +read_action_metadata(stonith_device_t *device) { - char *actions = NULL; xmlXPathObjectPtr xpath = NULL; int max = 0; int lpc = 0; @@ -545,40 +574,38 @@ get_on_target_actions(stonith_device_t *device) return; } - actions = calloc(1, 512); - for (lpc = 0; lpc < max; lpc++) { const char *on_target = NULL; const char *action = NULL; + const char *automatic = NULL; + const char *required = NULL; xmlNode *match = getXpathResult(xpath, lpc); CRM_CHECK(match != NULL, continue); on_target = crm_element_value(match, "on_target"); action = crm_element_value(match, "name"); + automatic = crm_element_value(match, "automatic"); + required = crm_element_value(match, "required"); if(safe_str_eq(action, "list")) { set_bit(device->flags, st_device_supports_list); } else if(safe_str_eq(action, "status")) { set_bit(device->flags, st_device_supports_status); + } else if(safe_str_eq(action, "on") && (crm_is_true(automatic))) { + /* this setting implies required=true for unfencing */ + required = "true"; } if (action && crm_is_true(on_target)) { - if (strlen(actions)) { - g_strlcat(actions, " ", 512); - } - g_strlcat(actions, action, 512); + device->on_target_actions = add_action(device->on_target_actions, action); + } + if (action && crm_is_true(required)) { + device->required_actions = add_action(device->required_actions, action); } } freeXpathObject(xpath); - - if (!strlen(actions)) { - free(actions); - actions = NULL; - } - - device->on_target_actions = actions; } static stonith_device_t * @@ -603,13 +630,23 @@ build_device_from_xml(xmlNode * msg) device->aliases = build_port_aliases(value, &(device->targets)); device->agent_metadata = get_agent_metadata(device->agent); - get_on_target_actions(device); + read_action_metadata(device); value = g_hash_table_lookup(device->params, "nodeid"); if (!value) { device->include_nodeid = is_nodeid_required(device->agent_metadata); } + value = crm_element_value(dev, "rsc_provides"); + if (safe_str_eq(value, "unfencing")) { + /* if this agent requires unfencing, 'on' is considered a required action */ + device->required_actions = add_action(device->required_actions, "on"); + } + + if (is_action_required("on", device)) { + crm_info("The fencing device '%s' requires unfencing", device->id); + } + if (device->on_target_actions) { crm_info("The fencing device '%s' requires actions (%s) to be executed on the target node", device->id, device->on_target_actions); @@ -1460,6 +1497,7 @@ static void st_child_done(GPid pid, int rc, const char *output, gpointer user_data) { stonith_device_t *device = NULL; + stonith_device_t *next_device = NULL; async_command_t *cmd = user_data; GListPtr gIter = NULL; @@ -1483,21 +1521,37 @@ st_child_done(GPid pid, int rc, const char *output, gpointer user_data) mainloop_set_trigger(device->work); } - crm_trace("Operation %s on %s completed with rc=%d (%d remaining)", + crm_debug("Operation '%s' on '%s' completed with rc=%d (%d remaining)", cmd->action, cmd->device, rc, g_list_length(cmd->device_next)); - if (rc != 0 && cmd->device_next) { - stonith_device_t *dev = g_hash_table_lookup(device_list, cmd->device_next->data); - - if (dev) { - log_operation(cmd, rc, pid, dev->id, output); + if (rc == 0) { + GListPtr iter; + /* see if there are any required devices left to execute for this op */ + for (iter = cmd->device_next; iter != NULL; iter = iter->next) { + next_device = g_hash_table_lookup(device_list, iter->data); - cmd->device_next = cmd->device_next->next; - schedule_stonith_command(cmd, dev); - /* Prevent cmd from being freed */ - cmd = NULL; - goto done; + if (next_device != NULL && is_action_required(cmd->action, next_device)) { + cmd->device_next = iter->next; + break; + } + next_device = NULL; } + + } else if (rc != 0 && cmd->device_next && (is_action_required(cmd->action, device) == FALSE)) { + /* if this device didn't work out, see if there are any others we can try. + * if the failed device was 'required', we can't pick another device. */ + next_device = g_hash_table_lookup(device_list, cmd->device_next->data); + cmd->device_next = cmd->device_next->next; + } + + /* this operation requires more fencing, hooray! */ + if (next_device) { + log_operation(cmd, rc, pid, device->id, output); + + schedule_stonith_command(cmd, next_device); + /* Prevent cmd from being freed */ + cmd = NULL; + goto done; } if (rc > 0) { diff --git a/fencing/internal.h b/fencing/internal.h index 4e0525c..971a32f 100644 --- a/fencing/internal.h +++ b/fencing/internal.h @@ -25,6 +25,7 @@ typedef struct stonith_device_s { /*! list of actions that must execute on the target node. Used for unfencing */ char *on_target_actions; + char *required_actions; GListPtr targets; time_t targets_age; gboolean has_attr_map; diff --git a/fencing/main.c b/fencing/main.c index e002125..16713fe 100644 --- a/fencing/main.c +++ b/fencing/main.c @@ -645,9 +645,13 @@ static void cib_device_update(resource_t *rsc, pe_working_set_t *data_set) const char *name = NULL; const char *agent = crm_element_value(rsc->xml, XML_EXPR_ATTR_TYPE); const char *provider = crm_element_value(rsc->xml, XML_AGENT_ATTR_PROVIDER); + const char *rsc_provides = NULL; crm_info("Device %s is allowed on %s: score=%d", rsc->id, stonith_our_uname, node->weight); get_rsc_attributes(rsc->parameters, rsc, node, data_set); + get_meta_attributes(rsc->meta, rsc, node, data_set); + + rsc_provides = g_hash_table_lookup(rsc->meta, XML_RSC_ATTR_PROVIDES); g_hash_table_iter_init(&gIter, rsc->parameters); while (g_hash_table_iter_next(&gIter, (gpointer *) & name, (gpointer *) & value)) { @@ -658,7 +662,7 @@ static void cib_device_update(resource_t *rsc, pe_working_set_t *data_set) crm_trace(" %s=%s", name, value); } - data = create_device_registration_xml(rsc_name(rsc), provider, agent, params); + data = create_device_registration_xml(rsc_name(rsc), provider, agent, params, rsc_provides); stonith_device_register(data, NULL, TRUE); stonith_key_value_freeall(params, 1, 1); diff --git a/include/crm/fencing/internal.h b/include/crm/fencing/internal.h index 4f30fed..2822e9a 100644 --- a/include/crm/fencing/internal.h +++ b/include/crm/fencing/internal.h @@ -46,7 +46,7 @@ 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); + stonith_key_value_t * params, const char *rsc_provides); # define ST_LEVEL_MAX 10 diff --git a/lib/fencing/st_client.c b/lib/fencing/st_client.c index 2be854f..06e0726 100644 --- a/lib/fencing/st_client.c +++ b/lib/fencing/st_client.c @@ -178,7 +178,7 @@ stonith_connection_destroy(gpointer user_data) xmlNode * create_device_registration_xml(const char *id, const char *namespace, const char *agent, - stonith_key_value_t * params) + stonith_key_value_t * params, const char *rsc_provides) { xmlNode *data = create_xml_node(NULL, F_STONITH_DEVICE); xmlNode *args = create_xml_node(data, XML_TAG_ATTRS); @@ -195,6 +195,9 @@ create_device_registration_xml(const char *id, const char *namespace, const char crm_xml_add(data, "origin", __FUNCTION__); crm_xml_add(data, "agent", agent); crm_xml_add(data, "namespace", namespace); + if (rsc_provides) { + crm_xml_add(data, "rsc_provides", rsc_provides); + } for (; params; params = params->next) { hash2field((gpointer) params->key, (gpointer) params->value, args); @@ -211,7 +214,7 @@ stonith_api_register_device(stonith_t * st, int call_options, int rc = 0; xmlNode *data = NULL; - data = create_device_registration_xml(id, namespace, agent, params); + data = create_device_registration_xml(id, namespace, agent, params, NULL); rc = stonith_send_command(st, STONITH_OP_DEVICE_ADD, data, NULL, call_options, 0); free_xml(data);