commit 1928af7d8d2fc7d225df22caeb7ea93964f48bde
Author: David Vossel <dvossel@redhat.com>
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);