Blob Blame History Raw
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);