commit 7c63f4cb17ffe7cb1c806c3d855b5f2f3ce91cbb
Author: Gao,Yan <ygao@suse.com>
Date:   Thu Sep 10 14:29:25 2015 +0200

    Feature: fencing: Support concurrent fencing actions on each device
    
    A "pcmk_action_limit" parameter can be configured for a fencing resource to
    specify the maximum number of actions can be performed in parallel on
    this device. It defaults to "1". "-1" is unlimited.

diff --git a/fencing/commands.c b/fencing/commands.c
index adf655b..6bafb83 100644
--- a/fencing/commands.c
+++ b/fencing/commands.c
@@ -252,6 +252,24 @@ create_async_command(xmlNode * msg)
     return cmd;
 }
 
+static int
+get_action_limit(stonith_device_t * device)
+{
+    const char *value = NULL;
+    int action_limit = 1;
+
+    value = g_hash_table_lookup(device->params, STONITH_ATTR_ACTION_LIMIT);
+    if (value) {
+       action_limit = crm_parse_int(value, "1");
+       if (action_limit == 0) {
+           /* pcmk_action_limit should not be 0. Enforce it to be 1. */
+           action_limit = 1;
+       }
+    }
+
+    return action_limit;
+}
+
 static gboolean
 stonith_device_execute(stonith_device_t * device)
 {
@@ -259,11 +277,16 @@ stonith_device_execute(stonith_device_t * device)
     const char *action_str = NULL;
     async_command_t *cmd = NULL;
     stonith_action_t *action = NULL;
+    guint active_pids = 0;
+    int action_limit = 0;
 
     CRM_CHECK(device != NULL, return FALSE);
 
-    if (device->active_pid) {
-        crm_trace("%s is still active with pid %u", device->id, device->active_pid);
+    active_pids = g_list_length(device->active_pids);
+    action_limit = get_action_limit(device);
+    if (action_limit > -1 && active_pids >= action_limit) {
+        crm_trace("%s is over its action limit of %d (%u active pid%s)",
+                  device->id, action_limit, active_pids, active_pids > 1 ? "s" : "");
         return TRUE;
     }
 
@@ -340,7 +363,7 @@ stonith_device_execute(stonith_device_t * device)
         crm_debug("Operation %s%s%s on %s now running with pid=%d, timeout=%ds",
                   cmd->action, cmd->victim ? " for node " : "", cmd->victim ? cmd->victim : "",
                   device->id, exec_rc, cmd->timeout);
-        device->active_pid = exec_rc;
+        device->active_pids = g_list_append(device->active_pids, GINT_TO_POINTER(exec_rc));
 
     } else {
         crm_warn("Operation %s%s%s on %s failed: %s (%d)",
@@ -431,6 +454,7 @@ free_device(gpointer data)
         free_async_command(cmd);
     }
     g_list_free(device->pending_ops);
+    g_list_free(device->active_pids);
 
     g_list_free_full(device->targets, free);
 
@@ -873,7 +897,7 @@ status_search_cb(GPid pid, int rc, const char *output, gpointer user_data)
         return;
     }
 
-    dev->active_pid = 0;
+    dev->active_pids = g_list_remove(dev->active_pids, GINT_TO_POINTER(pid));
     mainloop_set_trigger(dev->work);
 
     if (rc == 1 /* unknown */ ) {
@@ -910,7 +934,7 @@ dynamic_list_search_cb(GPid pid, int rc, const char *output, gpointer user_data)
         return;
     }
 
-    dev->active_pid = 0;
+    dev->active_pids = g_list_remove(dev->active_pids, GINT_TO_POINTER(pid));
     mainloop_set_trigger(dev->work);
 
     /* If we successfully got the targets earlier, don't disable. */
@@ -1771,7 +1795,7 @@ unfence_cb(GPid pid, int rc, const char *output, gpointer user_data)
     log_operation(cmd, rc, pid, NULL, output);
 
     if(dev) {
-        dev->active_pid = 0;
+        dev->active_pids = g_list_remove(dev->active_pids, GINT_TO_POINTER(pid));
         mainloop_set_trigger(dev->work);
     } else {
         crm_trace("Device %s does not exist", cmd->device);
@@ -1816,7 +1840,7 @@ st_child_done(GPid pid, int rc, const char *output, gpointer user_data)
     /* The device is ready to do something else now */
     device = g_hash_table_lookup(device_list, cmd->device);
     if (device) {
-        device->active_pid = 0;
+        device->active_pids = g_list_remove(device->active_pids, GINT_TO_POINTER(pid));
         if (rc == pcmk_ok &&
             (safe_str_eq(cmd->action, "list") ||
              safe_str_eq(cmd->action, "monitor") || safe_str_eq(cmd->action, "status"))) {
diff --git a/fencing/internal.h b/fencing/internal.h
index 3fc75ec..d7140cb 100644
--- a/fencing/internal.h
+++ b/fencing/internal.h
@@ -34,7 +34,7 @@ typedef struct stonith_device_s {
     /* whether the cluster should automatically unfence nodes with the device */
     gboolean automatic_unfencing;
     guint priority;
-    guint active_pid;
+    GListPtr active_pids;
 
     enum st_device_flags flags;
 
diff --git a/fencing/main.c b/fencing/main.c
index 43d2cb3..86a36ef 100644
--- a/fencing/main.c
+++ b/fencing/main.c
@@ -1405,6 +1405,16 @@ main(int argc, char **argv)
         printf("    <content type=\"time\" default=\"0s\"/>\n");
         printf("  </parameter>\n");
 
+        printf("  <parameter name=\"%s\" unique=\"0\">\n", STONITH_ATTR_ACTION_LIMIT);
+        printf
+            ("    <shortdesc lang=\"en\">The maximum number of actions can be performed in parallel on this device</shortdesc>\n");
+        printf
+            ("    <longdesc lang=\"en\">Pengine property concurrent-fencing=true needs to be configured first.\n"
+             "Then use this to specify the maximum number of actions can be performed in parallel on this device. -1 is unlimited.</longdesc>\n");
+        printf("    <content type=\"integer\" default=\"1\"/>\n");
+        printf("  </parameter>\n");
+
+
         for (lpc = 0; lpc < DIMOF(actions); lpc++) {
             printf("  <parameter name=\"pcmk_%s_action\" unique=\"0\">\n", actions[lpc]);
             printf
diff --git a/include/crm/fencing/internal.h b/include/crm/fencing/internal.h
index a59151b..73169aa 100644
--- a/include/crm/fencing/internal.h
+++ b/include/crm/fencing/internal.h
@@ -110,6 +110,7 @@ xmlNode *create_device_registration_xml(const char *id, const char *namespace, c
 #  define STONITH_ATTR_HOSTLIST  "pcmk_host_list"
 #  define STONITH_ATTR_HOSTCHECK "pcmk_host_check"
 #  define STONITH_ATTR_DELAY_MAX "pcmk_delay_max"
+#  define STONITH_ATTR_ACTION_LIMIT "pcmk_action_limit"
 
 #  define STONITH_ATTR_ACTION_OP   "action"
 
