commit 3e3cf385e0c9780fc95a4491703413b3dad54eb3
Author: Klaus Wenninger <klaus.wenninger@aon.at>
Date:   Wed Jun 21 11:55:57 2017 +0200

    Feature: stonith-ng: add pcmk_delay_base as static base-delay

diff --git a/fencing/commands.c b/fencing/commands.c
index 5c00e3a12..b336d3b82 100644
--- a/fencing/commands.c
+++ b/fencing/commands.c
@@ -147,6 +147,24 @@ get_action_delay_max(stonith_device_t * device, const char * action)
     return delay_max_ms;
 }
 
+static int
+get_action_delay_base(stonith_device_t * device, const char * action)
+{
+    const char *value = NULL;
+    int delay_base_ms = 0;
+
+    if (safe_str_neq(action, "off") && safe_str_neq(action, "reboot")) {
+        return 0;
+    }
+
+    value = g_hash_table_lookup(device->params, STONITH_ATTR_DELAY_BASE);
+    if (value) {
+       delay_base_ms = crm_get_msec(value);
+    }
+
+    return delay_base_ms;
+}
+
 /*!
  * \internal
  * \brief Override STONITH timeout with pcmk_*_timeout if available
@@ -424,6 +442,7 @@ static void
 schedule_stonith_command(async_command_t * cmd, stonith_device_t * device)
 {
     int delay_max = 0;
+    int delay_base = 0;
 
     CRM_CHECK(cmd != NULL, return);
     CRM_CHECK(device != NULL, return);
@@ -453,11 +472,26 @@ schedule_stonith_command(async_command_t * cmd, stonith_device_t * device)
     mainloop_set_trigger(device->work);
 
     delay_max = get_action_delay_max(device, cmd->action);
+    delay_base = get_action_delay_base(device, cmd->action);
+    if (delay_max == 0) {
+        delay_max = delay_base;
+    }
+    if (delay_max < delay_base) {
+        crm_warn("Base-delay (%dms) is larger than max-delay (%dms) "
+                 "for %s on %s - limiting to max-delay",
+                 delay_base, delay_max, cmd->action, device->id);
+        delay_base = delay_max;
+    }
     if (delay_max > 0) {
-        cmd->start_delay = rand() % delay_max;
-        crm_notice("Delaying %s on %s for %lldms (timeout=%ds)",
-                    cmd->action, device->id, cmd->start_delay, cmd->timeout);
-        cmd->delay_id = g_timeout_add(cmd->start_delay, start_delay_helper, cmd);
+        cmd->start_delay =
+            ((delay_max != delay_base)?(rand() % (delay_max - delay_base)):0)
+            + delay_base;
+        crm_notice("Delaying %s on %s for %lldms (timeout=%ds, base=%dms, "
+                   "max=%dms)",
+                    cmd->action, device->id, cmd->start_delay, cmd->timeout,
+                    delay_base, delay_max);
+        cmd->delay_id =
+            g_timeout_add(cmd->start_delay, start_delay_helper, cmd);
     }
 }
 
@@ -1728,6 +1762,7 @@ add_action_specific_attributes(xmlNode *xml, const char *action,
 {
     int action_specific_timeout;
     int delay_max;
+    int delay_base;
 
     CRM_CHECK(xml && action && device, return);
 
@@ -1749,6 +1784,23 @@ add_action_specific_attributes(xmlNode *xml, const char *action,
                   action, delay_max, device->id);
         crm_xml_add_int(xml, F_STONITH_DELAY_MAX, delay_max / 1000);
     }
+
+    delay_base = get_action_delay_base(device, action);
+    if (delay_base > 0) {
+        crm_xml_add_int(xml, F_STONITH_DELAY_BASE, delay_base / 1000);
+    }
+
+    if ((delay_max > 0) && (delay_base == 0)) {
+        crm_trace("Action %s has maximum random delay %dms on %s",
+                  action, delay_max, device->id);
+    } else if ((delay_max == 0) && (delay_base > 0)) {
+        crm_trace("Action %s has a static delay of %dms on %s",
+                  action, delay_base, device->id);
+    } else if ((delay_max > 0) && (delay_base > 0)) {
+        crm_trace("Action %s has a minimum delay of %dms and a randomly chosen "
+                  "maximum delay of %dms on %s",
+                  action, delay_base, delay_max, device->id);
+    }
 }
 
 /*!
diff --git a/fencing/main.c b/fencing/main.c
index b629d009a..1622362fa 100644
--- a/fencing/main.c
+++ b/fencing/main.c
@@ -1381,7 +1381,18 @@ main(int argc, char **argv)
             ("    <shortdesc lang=\"en\">Enable random delay for stonith actions and specify the maximum of random delay</shortdesc>\n");
         printf
             ("    <longdesc lang=\"en\">This prevents double fencing when using slow devices such as sbd.\n"
-             "Use this to enable random delay for stonith actions and specify the maximum of random delay.</longdesc>\n");
+             "Use this to enable random delay for stonith actions.\n"
+             "The overall delay is derived from a random delay value adding a static delay so that the sum is kept below the maximum delay.</longdesc>\n");
+        printf("    <content type=\"time\" default=\"0s\"/>\n");
+        printf("  </parameter>\n");
+
+        printf("  <parameter name=\"%s\" unique=\"0\">\n", STONITH_ATTR_DELAY_BASE);
+        printf
+            ("    <shortdesc lang=\"en\">Enable base delay for stonith actions and specify base delay value</shortdesc>\n");
+        printf
+            ("    <longdesc lang=\"en\">This prevents double fencing when different delays are configured on the nodes.\n"
+             "Use this to enable static delay for stonith actions.\n"
+             "The overall delay is derived from a random delay value adding a static delay so that the sum is kept below the maximum delay.</longdesc>\n");
         printf("    <content type=\"time\" default=\"0s\"/>\n");
         printf("  </parameter>\n");
 
diff --git a/fencing/remote.c b/fencing/remote.c
index 4a47d496c..6023ae84f 100644
--- a/fencing/remote.c
+++ b/fencing/remote.c
@@ -67,6 +67,8 @@ typedef struct device_properties_s {
     int custom_action_timeout[st_phase_max];
     /* Action-specific maximum random delay for each phase */
     int delay_max[st_phase_max];
+    /* Action-specific base delay for each phase */
+    int delay_base[st_phase_max];
 } device_properties_t;
 
 typedef struct st_query_result_s {
@@ -1676,6 +1678,13 @@ parse_action_specific(xmlNode *xml, const char *peer, const char *device,
                   peer, device, props->delay_max[phase], action);
     }
 
+    props->delay_base[phase] = 0;
+    crm_element_value_int(xml, F_STONITH_DELAY_BASE, &props->delay_base[phase]);
+    if (props->delay_base[phase]) {
+        crm_trace("Peer %s with device %s returned base delay %d for %s",
+                  peer, device, props->delay_base[phase], action);
+    }
+
     /* Handle devices with automatic unfencing */
     if (safe_str_eq(action, "on")) {
         int required = 0;
diff --git a/include/crm/fencing/internal.h b/include/crm/fencing/internal.h
index db4458a34..c0b27dbad 100644
--- a/include/crm/fencing/internal.h
+++ b/include/crm/fencing/internal.h
@@ -69,6 +69,8 @@ xmlNode *create_device_registration_xml(const char *id, const char *namespace, c
 #  define F_STONITH_ACTION_DISALLOWED     "st_action_disallowed"
 /*! Maximum of random fencing delay for a device */
 #  define F_STONITH_DELAY_MAX            "st_delay_max"
+/*! Base delay used for a fencing delay */
+#  define F_STONITH_DELAY_BASE           "st_delay_base"
 /*! Has this device been verified using a monitor type
  *  operation (monitor, list, status) */
 #  define F_STONITH_DEVICE_VERIFIED   "st_monitor_verified"
@@ -111,6 +113,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_DELAY_BASE   "pcmk_delay_base"
 #  define STONITH_ATTR_ACTION_LIMIT "pcmk_action_limit"
 
 #  define STONITH_ATTR_ACTION_OP   "action"
