Blame SOURCES/bz1078078-pcmk-pe_automatically_re_unfence_a_node_if_the_fencing_device_definition_changes.patch

7ebc05
commit 5efd173dac6b22eee48f1f9372b2e86cb6d27282
7ebc05
Author: Andrew Beekhof <andrew@beekhof.net>
7ebc05
Date:   Thu Apr 3 16:02:23 2014 +1100
7ebc05
7ebc05
    Feature: PE: Automatically re-unfence a node if the fencing device definition changes
7ebc05
    
7ebc05
    Ensures all resources that require unfencing are stopped first
7ebc05
    If unfencing is enabled, this is the new default for resource.requires
7ebc05
    
7ebc05
    (cherry picked from commit 26fe2e39b9e060597cddeeb69d8b846784dfac55)
7ebc05
    
7ebc05
    Conflicts:
7ebc05
    	include/crm/pengine/internal.h
7ebc05
    	include/crm/pengine/status.h
7ebc05
    	lib/pengine/complex.c
7ebc05
7ebc05
diff --git a/fencing/main.c b/fencing/main.c
7ebc05
index c40610e..e002125 100644
7ebc05
--- a/fencing/main.c
7ebc05
+++ b/fencing/main.c
7ebc05
@@ -573,7 +573,6 @@ fencing_topology_init(xmlNode * msg)
7ebc05
 }
7ebc05
 
7ebc05
 #define rsc_name(x) x->clone_name?x->clone_name:x->id
7ebc05
-static bool have_fence_scsi = FALSE;
7ebc05
 
7ebc05
 static void cib_device_update(resource_t *rsc, pe_working_set_t *data_set)
7ebc05
 {
7ebc05
@@ -662,34 +661,6 @@ static void cib_device_update(resource_t *rsc, pe_working_set_t *data_set)
7ebc05
         data = create_device_registration_xml(rsc_name(rsc), provider, agent, params);
7ebc05
         stonith_device_register(data, NULL, TRUE);
7ebc05
 
7ebc05
-        /* If required, unfence ourselves on cluster startup
7ebc05
-         *
7ebc05
-         * Make this generic/smarter if/when more than a single agent needs this
7ebc05
-         */
7ebc05
-        if(have_fence_scsi == FALSE && safe_str_eq(agent, "fence_scsi")) {
7ebc05
-            stonith_device_t *device = g_hash_table_lookup(device_list, rsc->id);
7ebc05
-
7ebc05
-            if(stonith_our_uname == NULL) {
7ebc05
-                crm_trace("Cannot unfence ourselves: no local host name");
7ebc05
-
7ebc05
-            } else if(device == NULL) {
7ebc05
-                crm_err("Cannot unfence ourselves: no such device '%s'", rsc->id);
7ebc05
-
7ebc05
-            } else {
7ebc05
-                const char *alias = g_hash_table_lookup(device->aliases, stonith_our_uname);
7ebc05
-
7ebc05
-                if (!alias) {
7ebc05
-                    alias = stonith_our_uname;
7ebc05
-                }
7ebc05
-
7ebc05
-                if (device->targets && string_in_list(device->targets, alias)) {
7ebc05
-                    have_fence_scsi = TRUE;
7ebc05
-                    crm_notice("Unfencing ourselves with %s (%s)", agent, device->id);
7ebc05
-                    schedule_internal_command(__FUNCTION__, device, "on", stonith_our_uname, 0, NULL, unfence_cb);
7ebc05
-                }
7ebc05
-            }
7ebc05
-        }
7ebc05
-
7ebc05
         stonith_key_value_freeall(params, 1, 1);
7ebc05
         free_xml(data);
7ebc05
     }
7ebc05
diff --git a/include/crm/msg_xml.h b/include/crm/msg_xml.h
7ebc05
index 8575163..055f476 100644
7ebc05
--- a/include/crm/msg_xml.h
7ebc05
+++ b/include/crm/msg_xml.h
7ebc05
@@ -200,6 +200,7 @@
7ebc05
 #  define XML_RSC_ATTR_MULTIPLE		"multiple-active"
7ebc05
 #  define XML_RSC_ATTR_PRIORITY		"priority"
7ebc05
 #  define XML_RSC_ATTR_REQUIRES		"requires"
7ebc05
+#  define XML_RSC_ATTR_PROVIDES		"provides"
7ebc05
 #  define XML_RSC_ATTR_CONTAINER	"container"
7ebc05
 #  define XML_RSC_ATTR_INTERNAL_RSC	"internal_rsc"
7ebc05
 #  define XML_OP_ATTR_ON_FAIL		"on-fail"
7ebc05
diff --git a/include/crm/pengine/internal.h b/include/crm/pengine/internal.h
7ebc05
index dd52e7f..657c647 100644
7ebc05
--- a/include/crm/pengine/internal.h
7ebc05
+++ b/include/crm/pengine/internal.h
7ebc05
@@ -261,6 +261,7 @@ op_digest_cache_t *rsc_action_digest_cmp(resource_t * rsc, xmlNode * xml_op, nod
7ebc05
                                          pe_working_set_t * data_set);
7ebc05
 
7ebc05
 gboolean is_remote_node(xmlNode *xml);
7ebc05
+void clear_bit_recursive(resource_t * rsc, unsigned long long flag);
7ebc05
 
7ebc05
 resource_t * rsc_contains_remote_node(pe_working_set_t * data_set, resource_t *rsc);
7ebc05
 #endif
7ebc05
diff --git a/include/crm/pengine/status.h b/include/crm/pengine/status.h
7ebc05
index b74bf83..ddf9dd7 100644
7ebc05
--- a/include/crm/pengine/status.h
7ebc05
+++ b/include/crm/pengine/status.h
7ebc05
@@ -60,6 +60,7 @@ enum pe_find {
7ebc05
 
7ebc05
 #  define pe_flag_stonith_enabled	0x00000010ULL
7ebc05
 #  define pe_flag_have_stonith_resource	0x00000020ULL
7ebc05
+#  define pe_flag_enable_unfencing	0x00000040ULL
7ebc05
 
7ebc05
 #  define pe_flag_stop_rsc_orphans	0x00000100ULL
7ebc05
 #  define pe_flag_stop_action_orphans	0x00000200ULL
7ebc05
@@ -165,6 +166,7 @@ struct node_s {
7ebc05
 
7ebc05
 #  define pe_rsc_notify		0x00000010ULL
7ebc05
 #  define pe_rsc_unique		0x00000020ULL
7ebc05
+#  define pe_rsc_fence_device   0x00000040ULL
7ebc05
 
7ebc05
 #  define pe_rsc_provisional	0x00000100ULL
7ebc05
 #  define pe_rsc_allocating	0x00000200ULL
7ebc05
@@ -329,6 +331,12 @@ enum pe_ordering {
7ebc05
 
7ebc05
     pe_order_runnable_left         = 0x100,     /* 'then' requires 'first' to be runnable */
7ebc05
 
7ebc05
+    pe_order_pseudo_left           = 0x200,     /* 'then' can only be pseudo if 'first' is runnable */
7ebc05
+    pe_order_implies_then_on_node  = 0x400,     /* If 'first' is required on 'nodeX',
7ebc05
+                                                 * ensure instances of 'then' on 'nodeX' are too.
7ebc05
+                                                 * Only really useful if 'then' is a clone and 'first' is not
7ebc05
+                                                 */
7ebc05
+
7ebc05
     pe_order_restart               = 0x1000,    /* 'then' is runnable if 'first' is optional or runnable */
7ebc05
     pe_order_stonith_stop          = 0x2000,    /* only applies if the action is non-pseudo */
7ebc05
     pe_order_serialize_only        = 0x4000,    /* serialize */
7ebc05
diff --git a/lib/pengine/clone.c b/lib/pengine/clone.c
7ebc05
index c6c69e6..b13cceb 100644
7ebc05
--- a/lib/pengine/clone.c
7ebc05
+++ b/lib/pengine/clone.c
7ebc05
@@ -46,7 +46,7 @@ mark_as_orphan(resource_t * rsc)
7ebc05
     }
7ebc05
 }
7ebc05
 
7ebc05
-static void
7ebc05
+void
7ebc05
 clear_bit_recursive(resource_t * rsc, unsigned long long flag)
7ebc05
 {
7ebc05
     GListPtr gIter = rsc->children;
7ebc05
diff --git a/lib/pengine/complex.c b/lib/pengine/complex.c
7ebc05
index 8a6049d..8b774c0 100644
7ebc05
--- a/lib/pengine/complex.c
7ebc05
+++ b/lib/pengine/complex.c
7ebc05
@@ -340,12 +340,13 @@ gboolean
7ebc05
 common_unpack(xmlNode * xml_obj, resource_t ** rsc,
7ebc05
               resource_t * parent, pe_working_set_t * data_set)
7ebc05
 {
7ebc05
+    bool isdefault = FALSE;
7ebc05
     xmlNode *expanded_xml = NULL;
7ebc05
     xmlNode *ops = NULL;
7ebc05
     resource_t *top = NULL;
7ebc05
     const char *value = NULL;
7ebc05
+    const char *rclass = NULL; /* Look for this after any templates have been expanded */
7ebc05
     const char *id = crm_element_value(xml_obj, XML_ATTR_ID);
7ebc05
-    const char *class = crm_element_value(xml_obj, XML_AGENT_ATTR_CLASS);
7ebc05
 
7ebc05
     crm_log_xml_trace(xml_obj, "Processing resource input...");
7ebc05
 
7ebc05
@@ -375,6 +376,8 @@ common_unpack(xmlNode * xml_obj, resource_t ** rsc,
7ebc05
         (*rsc)->orig_xml = NULL;
7ebc05
     }
7ebc05
 
7ebc05
+    /* Do not use xml_obj from here on, use (*rsc)->xml in case templates are involved */
7ebc05
+    rclass = crm_element_value((*rsc)->xml, XML_AGENT_ATTR_CLASS);
7ebc05
     (*rsc)->parent = parent;
7ebc05
 
7ebc05
     ops = find_xml_node((*rsc)->xml, "operations", FALSE);
7ebc05
@@ -538,47 +541,67 @@ common_unpack(xmlNode * xml_obj, resource_t ** rsc,
7ebc05
         }
7ebc05
     }
7ebc05
 
7ebc05
+    if (safe_str_eq(rclass, "stonith")) {
7ebc05
+        set_bit(data_set->flags, pe_flag_have_stonith_resource);
7ebc05
+        set_bit((*rsc)->flags, pe_rsc_fence_device);
7ebc05
+    }
7ebc05
+
7ebc05
     value = g_hash_table_lookup((*rsc)->meta, XML_RSC_ATTR_REQUIRES);
7ebc05
+
7ebc05
+  handle_requires_pref:
7ebc05
     if (safe_str_eq(value, "nothing")) {
7ebc05
 
7ebc05
     } else if (safe_str_eq(value, "quorum")) {
7ebc05
         set_bit((*rsc)->flags, pe_rsc_needs_quorum);
7ebc05
 
7ebc05
     } else if (safe_str_eq(value, "unfencing")) {
7ebc05
-        set_bit((*rsc)->flags, pe_rsc_needs_fencing);
7ebc05
-        set_bit((*rsc)->flags, pe_rsc_needs_unfencing);
7ebc05
-        if (is_set(data_set->flags, pe_flag_stonith_enabled)) {
7ebc05
-            crm_notice("%s requires (un)fencing but fencing is disabled", (*rsc)->id);
7ebc05
+        if (is_set((*rsc)->flags, pe_rsc_fence_device)) {
7ebc05
+            crm_config_warn("%s is a fencing device but requires (un)fencing", (*rsc)->id);
7ebc05
+            value = "quorum";
7ebc05
+            isdefault = TRUE;
7ebc05
+            goto handle_requires_pref;
7ebc05
+
7ebc05
+        } else if (is_not_set(data_set->flags, pe_flag_stonith_enabled)) {
7ebc05
+            crm_config_warn("%s requires (un)fencing but fencing is disabled", (*rsc)->id);
7ebc05
+            value = "quorum";
7ebc05
+            isdefault = TRUE;
7ebc05
+            goto handle_requires_pref;
7ebc05
+
7ebc05
+        } else {
7ebc05
+            set_bit((*rsc)->flags, pe_rsc_needs_fencing);
7ebc05
+            set_bit((*rsc)->flags, pe_rsc_needs_unfencing);
7ebc05
         }
7ebc05
 
7ebc05
     } else if (safe_str_eq(value, "fencing")) {
7ebc05
         set_bit((*rsc)->flags, pe_rsc_needs_fencing);
7ebc05
         if (is_set(data_set->flags, pe_flag_stonith_enabled)) {
7ebc05
-            crm_notice("%s requires fencing but fencing is disabled", (*rsc)->id);
7ebc05
+            crm_config_warn("%s requires (un)fencing but fencing is disabled", (*rsc)->id);
7ebc05
         }
7ebc05
 
7ebc05
     } else {
7ebc05
         if (value) {
7ebc05
             crm_config_err("Invalid value for %s->requires: %s%s",
7ebc05
                            (*rsc)->id, value,
7ebc05
-                           is_set(data_set->flags,
7ebc05
-                                  pe_flag_stonith_enabled) ? "" : " (stonith-enabled=false)");
7ebc05
+                           is_set(data_set->flags, pe_flag_stonith_enabled) ? "" : " (stonith-enabled=false)");
7ebc05
         }
7ebc05
 
7ebc05
-        if (is_set(data_set->flags, pe_flag_stonith_enabled)) {
7ebc05
-            set_bit((*rsc)->flags, pe_rsc_needs_fencing);
7ebc05
-            value = "fencing (default)";
7ebc05
+        isdefault = TRUE;
7ebc05
+        if (is_set(data_set->flags, pe_flag_enable_unfencing)) {
7ebc05
+            value = "unfencing";
7ebc05
+
7ebc05
+        } else if (is_set(data_set->flags, pe_flag_stonith_enabled)) {
7ebc05
+            value = "fencing";
7ebc05
 
7ebc05
         } else if (data_set->no_quorum_policy == no_quorum_ignore) {
7ebc05
-            value = "nothing (default)";
7ebc05
+            value = "nothing";
7ebc05
 
7ebc05
         } else {
7ebc05
-            set_bit((*rsc)->flags, pe_rsc_needs_quorum);
7ebc05
-            value = "quorum (default)";
7ebc05
+            value = "quorum";
7ebc05
         }
7ebc05
+        goto handle_requires_pref;
7ebc05
     }
7ebc05
 
7ebc05
-    pe_rsc_trace((*rsc), "\tRequired to start: %s", value);
7ebc05
+    pe_rsc_trace((*rsc), "\tRequired to start: %s%s", value, isdefault?" (default)":"");
7ebc05
 
7ebc05
     value = g_hash_table_lookup((*rsc)->meta, XML_RSC_ATTR_FAIL_TIMEOUT);
7ebc05
     if (value != NULL) {
7ebc05
@@ -606,10 +629,6 @@ common_unpack(xmlNode * xml_obj, resource_t ** rsc,
7ebc05
     pe_rsc_trace((*rsc), "\tAction notification: %s",
7ebc05
                  is_set((*rsc)->flags, pe_rsc_notify) ? "required" : "not required");
7ebc05
 
7ebc05
-    if (safe_str_eq(class, "stonith")) {
7ebc05
-        set_bit(data_set->flags, pe_flag_have_stonith_resource);
7ebc05
-    }
7ebc05
-
7ebc05
     (*rsc)->utilization =
7ebc05
         g_hash_table_new_full(crm_str_hash, g_str_equal, g_hash_destroy_str, g_hash_destroy_str);
7ebc05
 
7ebc05
diff --git a/lib/pengine/unpack.c b/lib/pengine/unpack.c
7ebc05
index 56ed291..39ded96 100644
7ebc05
--- a/lib/pengine/unpack.c
7ebc05
+++ b/lib/pengine/unpack.c
7ebc05
@@ -77,6 +77,35 @@ unpack_config(xmlNode * config, pe_working_set_t * data_set)
7ebc05
     GHashTable *config_hash =
7ebc05
         g_hash_table_new_full(crm_str_hash, g_str_equal, g_hash_destroy_str, g_hash_destroy_str);
7ebc05
 
7ebc05
+    xmlXPathObjectPtr xpathObj = NULL;
7ebc05
+
7ebc05
+    if(is_not_set(data_set->flags, pe_flag_enable_unfencing)) {
7ebc05
+        xpathObj = xpath_search(data_set->input, "//nvpair[@name='provides' and @value='unfencing']");
7ebc05
+        if(xpathObj && numXpathResults(xpathObj) > 0) {
7ebc05
+            set_bit(data_set->flags, pe_flag_enable_unfencing);
7ebc05
+        }
7ebc05
+        freeXpathObject(xpathObj);
7ebc05
+    }
7ebc05
+
7ebc05
+    if(is_not_set(data_set->flags, pe_flag_enable_unfencing)) {
7ebc05
+        xpathObj = xpath_search(data_set->input, "//nvpair[@name='requires' and @value='unfencing']");
7ebc05
+        if(xpathObj && numXpathResults(xpathObj) > 0) {
7ebc05
+            set_bit(data_set->flags, pe_flag_enable_unfencing);
7ebc05
+        }
7ebc05
+        freeXpathObject(xpathObj);
7ebc05
+    }
7ebc05
+
7ebc05
+
7ebc05
+#ifdef REDHAT_COMPAT_6
7ebc05
+    if(is_not_set(data_set->flags, pe_flag_enable_unfencing)) {
7ebc05
+        xpathObj = xpath_search(data_set->input, "//primitive[@type='fence_scsi']");
7ebc05
+        if(xpathObj && numXpathResults(xpathObj) > 0) {
7ebc05
+            set_bit(data_set->flags, pe_flag_enable_unfencing);
7ebc05
+        }
7ebc05
+        freeXpathObject(xpathObj);
7ebc05
+    }
7ebc05
+#endif
7ebc05
+
7ebc05
     data_set->config_hash = config_hash;
7ebc05
 
7ebc05
     unpack_instance_attributes(data_set->input, config, XML_CIB_TAG_PROPSET, NULL, config_hash,
7ebc05
diff --git a/pengine/allocate.c b/pengine/allocate.c
7ebc05
index b9ce069..88258f4 100644
7ebc05
--- a/pengine/allocate.c
7ebc05
+++ b/pengine/allocate.c
7ebc05
@@ -123,6 +123,7 @@ check_rsc_parameters(resource_t * rsc, node_t * node, xmlNode * rsc_entry,
7ebc05
         }
7ebc05
 
7ebc05
         changed = TRUE;
7ebc05
+        trigger_unfencing(rsc, node, "Device definition changed", NULL, data_set);
7ebc05
         if (active_here) {
7ebc05
             force_restart = TRUE;
7ebc05
             crm_notice("Forcing restart of %s on %s, %s changed: %s -> %s",
7ebc05
@@ -169,6 +170,7 @@ CancelXmlOp(resource_t * rsc, xmlNode * xml_op, node_t * active_node,
7ebc05
     crm_info("Action %s on %s will be stopped: %s",
7ebc05
              key, active_node->details->uname, reason ? reason : "unknown");
7ebc05
 
7ebc05
+    /* TODO: This looks highly dangerous if we ever try to schedule 'key' too */
7ebc05
     cancel = custom_action(rsc, strdup(key), RSC_CANCEL, active_node, FALSE, TRUE, data_set);
7ebc05
 
7ebc05
     free(cancel->task);
7ebc05
@@ -227,6 +229,7 @@ check_action_definition(resource_t * rsc, node_t * active_node, xmlNode * xml_op
7ebc05
         key = NULL;
7ebc05
     }
7ebc05
 
7ebc05
+    crm_trace("Testing %s_%s_%d on %s", rsc->id, task, interval, active_node?active_node->details->uname:"N/A");
7ebc05
     if (interval == 0 && safe_str_eq(task, RSC_STATUS)) {
7ebc05
         /* Reload based on the start action not a probe */
7ebc05
         task = RSC_START;
7ebc05
@@ -252,6 +255,7 @@ check_action_definition(resource_t * rsc, node_t * active_node, xmlNode * xml_op
7ebc05
                  op_version, crm_element_value(xml_op, XML_ATTR_TRANSITION_MAGIC));
7ebc05
 
7ebc05
         custom_action(rsc, key, task, NULL, FALSE, TRUE, data_set);
7ebc05
+        trigger_unfencing(rsc, NULL, "Device parameters changed", NULL, data_set);
7ebc05
 
7ebc05
     } else if ((digest_data->rc == RSC_DIGEST_ALL) || (digest_data->rc == RSC_DIGEST_UNKNOWN)) {
7ebc05
         /* Changes that can potentially be handled by a reload */
7ebc05
@@ -259,6 +263,7 @@ check_action_definition(resource_t * rsc, node_t * active_node, xmlNode * xml_op
7ebc05
         const char *digest_all = crm_element_value(xml_op, XML_LRM_ATTR_OP_DIGEST);
7ebc05
 
7ebc05
         did_change = TRUE;
7ebc05
+        trigger_unfencing(rsc, NULL, "Device parameters changed (reload)", NULL, data_set);
7ebc05
         crm_log_xml_info(digest_data->params_all, "params:reload");
7ebc05
         key = generate_op_key(rsc->id, task, interval);
7ebc05
         pe_rsc_info(rsc, "Parameters to %s on %s changed: was %s vs. now %s (reload:%s) %s",
7ebc05
@@ -1262,8 +1267,40 @@ any_managed_resources(pe_working_set_t * data_set)
7ebc05
     return FALSE;
7ebc05
 }
7ebc05
 
7ebc05
+void
7ebc05
+trigger_unfencing(
7ebc05
+    resource_t * rsc, node_t *node, const char *reason, action_t *dependancy, pe_working_set_t * data_set) 
7ebc05
+{
7ebc05
+    if(is_not_set(data_set->flags, pe_flag_enable_unfencing)) {
7ebc05
+        /* No resources require it */
7ebc05
+        return;
7ebc05
+
7ebc05
+    } else if (rsc != NULL && is_not_set(rsc->flags, pe_rsc_fence_device)) {
7ebc05
+        /* Wasnt a stonith device */
7ebc05
+        return;
7ebc05
+
7ebc05
+    } else if(node) {
7ebc05
+        action_t *unfence = pe_fence_op(node, "on", FALSE, data_set);
7ebc05
+
7ebc05
+        crm_notice("Unfencing %s: %s", node->details->uname, reason);
7ebc05
+        if(FALSE && dependancy) {
7ebc05
+            order_actions(dependancy, unfence, pe_order_optional);
7ebc05
+        }
7ebc05
+
7ebc05
+    } else if(rsc) {
7ebc05
+        GHashTableIter iter;
7ebc05
+
7ebc05
+        g_hash_table_iter_init(&iter, rsc->allowed_nodes);
7ebc05
+        while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) {
7ebc05
+            if(node->details->online && node->details->unclean == FALSE && node->details->shutdown == FALSE) {
7ebc05
+                trigger_unfencing(rsc, node, reason, dependancy, data_set);
7ebc05
+            }
7ebc05
+        }
7ebc05
+    }
7ebc05
+}
7ebc05
+
7ebc05
 action_t *
7ebc05
-pe_fence_op(node_t * node, const char *op, pe_working_set_t * data_set)
7ebc05
+pe_fence_op(node_t * node, const char *op, bool optional, pe_working_set_t * data_set)
7ebc05
 {
7ebc05
     char *key = NULL;
7ebc05
     action_t *stonith_op = NULL;
7ebc05
@@ -1279,13 +1316,18 @@ pe_fence_op(node_t * node, const char *op, pe_working_set_t * data_set)
7ebc05
     }
7ebc05
 
7ebc05
     if(stonith_op == NULL) {
7ebc05
-        stonith_op = custom_action(NULL, key, CRM_OP_FENCE, node, FALSE, TRUE, data_set);
7ebc05
+        stonith_op = custom_action(NULL, key, CRM_OP_FENCE, node, optional, TRUE, data_set);
7ebc05
 
7ebc05
         add_hash_param(stonith_op->meta, XML_LRM_ATTR_TARGET, node->details->uname);
7ebc05
         add_hash_param(stonith_op->meta, XML_LRM_ATTR_TARGET_UUID, node->details->id);
7ebc05
         add_hash_param(stonith_op->meta, "stonith_action", op);
7ebc05
     }
7ebc05
 
7ebc05
+    if(optional == FALSE) {
7ebc05
+        crm_trace("%s is no longer optional", stonith_op->uuid);
7ebc05
+        pe_clear_action_bit(stonith_op, pe_action_optional);
7ebc05
+    }
7ebc05
+
7ebc05
     return stonith_op;
7ebc05
 }
7ebc05
 
7ebc05
@@ -1323,7 +1365,7 @@ stage6(pe_working_set_t * data_set)
7ebc05
         if (need_stonith && node->details->unclean && pe_can_fence(data_set, node)) {
7ebc05
             pe_warn("Scheduling Node %s for STONITH", node->details->uname);
7ebc05
 
7ebc05
-            stonith_op = pe_fence_op(node, NULL, data_set);
7ebc05
+            stonith_op = pe_fence_op(node, NULL, FALSE, data_set);
7ebc05
 
7ebc05
             stonith_constraints(node, stonith_op, data_set);
7ebc05
 
7ebc05
diff --git a/pengine/allocate.h b/pengine/allocate.h
7ebc05
index 042d3aa..712de60 100644
7ebc05
--- a/pengine/allocate.h
7ebc05
+++ b/pengine/allocate.h
7ebc05
@@ -51,7 +51,7 @@ struct resource_alloc_functions_s {
7ebc05
     void (*append_meta) (resource_t * rsc, xmlNode * xml);
7ebc05
 };
7ebc05
 
7ebc05
-action_t *pe_fence_op(node_t * node, const char *op, pe_working_set_t * data_set);
7ebc05
+action_t *pe_fence_op(node_t * node, const char *op, bool optional, pe_working_set_t * data_set);
7ebc05
 
7ebc05
 extern GHashTable *rsc_merge_weights(resource_t * rsc, const char *rhs, GHashTable * nodes,
7ebc05
                                      const char *attr, float factor, enum pe_weights flags);
7ebc05
@@ -161,6 +161,8 @@ extern void rsc_migrate_reload(resource_t * rsc, pe_working_set_t * data_set);
7ebc05
 extern void rsc_stonith_ordering(resource_t * rsc, action_t * stonith_op,
7ebc05
                                  pe_working_set_t * data_set);
7ebc05
 
7ebc05
+void trigger_unfencing(resource_t * rsc, node_t *node, const char *reason, action_t *dependancy, pe_working_set_t * data_set);
7ebc05
+
7ebc05
 extern enum pe_graph_flags native_update_actions(action_t * first, action_t * then, node_t * node,
7ebc05
                                                  enum pe_action_flags flags,
7ebc05
                                                  enum pe_action_flags filter,
7ebc05
diff --git a/pengine/graph.c b/pengine/graph.c
7ebc05
index 779f0e0..9c82d23 100644
7ebc05
--- a/pengine/graph.c
7ebc05
+++ b/pengine/graph.c
7ebc05
@@ -430,11 +430,49 @@ update_action(action_t * then)
7ebc05
                   uname : "", other->type);
7ebc05
 
7ebc05
         if (first == other->action) {
7ebc05
+            /*
7ebc05
+             * 'first' was not expanded (ie. from 'start' to 'running'), which could mean it:
7ebc05
+             * - has no associated resource,
7ebc05
+             * - was a primitive,
7ebc05
+             * - was pre-expanded (ie. 'running' instead of 'start')
7ebc05
+             *
7ebc05
+             * The third argument here to graph_update_action() is a node which is used under two conditions:
7ebc05
+             * - Interleaving, in which case first->node and
7ebc05
+             *   then->node are equal (and NULL)
7ebc05
+             * - If 'then' is a clone, to limit the scope of the
7ebc05
+             *   constraint to instances on the supplied node
7ebc05
+             *
7ebc05
+             */
7ebc05
+            int otype = other->type;
7ebc05
+            node_t *node = then->node;
7ebc05
+
7ebc05
+            if(is_set(otype, pe_order_implies_then_on_node)) {
7ebc05
+                /* Normally we want the _whole_ 'then' clone to
7ebc05
+                 * restart if 'first' is restarted, so then->node is
7ebc05
+                 * needed.
7ebc05
+                 *
7ebc05
+                 * However for unfencing, we want to limit this to
7ebc05
+                 * instances on the same node as 'first' (the
7ebc05
+                 * unfencing operation), so first->node is supplied.
7ebc05
+                 *
7ebc05
+                 * Swap the node, from then on we can can treat it
7ebc05
+                 * like any other 'pe_order_implies_then'
7ebc05
+                 */
7ebc05
+
7ebc05
+                clear_bit(otype, pe_order_implies_then_on_node);
7ebc05
+                set_bit(otype, pe_order_implies_then);
7ebc05
+                node = first->node;
7ebc05
+            }
7ebc05
             clear_bit(first_flags, pe_action_pseudo);
7ebc05
-            changed |= graph_update_action(first, then, then->node, first_flags, other->type);
7ebc05
+            changed |= graph_update_action(first, then, node, first_flags, otype);
7ebc05
 
7ebc05
+            /* 'first' was for a complex resource (clone, group, etc),
7ebc05
+             * create a new dependancy if necessary
7ebc05
+             */
7ebc05
         } else if (order_actions(first, then, other->type)) {
7ebc05
-            /* Start again to get the new actions_before list */
7ebc05
+            /* This was the first time 'first' and 'then' were associated,
7ebc05
+             * start again to get the new actions_before list
7ebc05
+             */
7ebc05
             changed |= (pe_graph_updated_then | pe_graph_disable);
7ebc05
         }
7ebc05
 
7ebc05
diff --git a/pengine/native.c b/pengine/native.c
7ebc05
index 4016a3c..d0eb950 100644
7ebc05
--- a/pengine/native.c
7ebc05
+++ b/pengine/native.c
7ebc05
@@ -1236,9 +1236,7 @@ native_internal_constraints(resource_t * rsc, pe_working_set_t * data_set)
7ebc05
 
7ebc05
     resource_t *top = uber_parent(rsc);
7ebc05
     int type = pe_order_optional | pe_order_implies_then | pe_order_restart;
7ebc05
-    gboolean is_stonith =
7ebc05
-        (rsc->xml && safe_str_eq(crm_element_value(rsc->xml, XML_AGENT_ATTR_CLASS), "stonith")) ?
7ebc05
-        TRUE : FALSE;
7ebc05
+    gboolean is_stonith = is_set(rsc->flags, pe_rsc_fence_device);
7ebc05
 
7ebc05
     custom_action_order(rsc, generate_op_key(rsc->id, RSC_STOP, 0), NULL,
7ebc05
                         rsc, generate_op_key(rsc->id, RSC_START, 0), NULL, type, data_set);
7ebc05
@@ -1253,6 +1251,34 @@ native_internal_constraints(resource_t * rsc, pe_working_set_t * data_set)
7ebc05
                             pe_order_runnable_left, data_set);
7ebc05
     }
7ebc05
 
7ebc05
+    if (is_stonith == FALSE
7ebc05
+        && is_set(data_set->flags, pe_flag_enable_unfencing)
7ebc05
+        && is_set(rsc->flags, pe_rsc_needs_unfencing)) {
7ebc05
+        /* Check if the node needs to be unfenced first */
7ebc05
+        node_t *node = NULL;
7ebc05
+        GHashTableIter iter;
7ebc05
+
7ebc05
+        if(rsc != top) {
7ebc05
+            /* Only create these constraints once, rsc is almost certainly cloned */
7ebc05
+            clear_bit_recursive(top, pe_rsc_needs_unfencing);
7ebc05
+        }
7ebc05
+
7ebc05
+        g_hash_table_iter_init(&iter, rsc->allowed_nodes);
7ebc05
+        while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) {
7ebc05
+            action_t *unfence = pe_fence_op(node, "on", TRUE, data_set);
7ebc05
+
7ebc05
+            custom_action_order(top, generate_op_key(top->id, top == rsc?RSC_STOP:RSC_STOPPED, 0), NULL,
7ebc05
+                                NULL, strdup(unfence->uuid), unfence,
7ebc05
+                                pe_order_optional, data_set);
7ebc05
+
7ebc05
+            crm_debug("Stopping %s prior to unfencing %s", top->id, unfence->uuid);
7ebc05
+
7ebc05
+            custom_action_order(NULL, strdup(unfence->uuid), unfence,
7ebc05
+                                top, generate_op_key(top->id, RSC_START, 0), NULL,
7ebc05
+                                pe_order_implies_then_on_node, data_set);
7ebc05
+        }
7ebc05
+    }
7ebc05
+
7ebc05
     if (is_not_set(rsc->flags, pe_rsc_managed)) {
7ebc05
         pe_rsc_trace(rsc, "Skipping fencing constraints for unmanaged resource: %s", rsc->id);
7ebc05
         return;
7ebc05
@@ -2456,20 +2482,9 @@ native_create_probe(resource_t * rsc, node_t * node, action_t * complete,
7ebc05
     probe = custom_action(rsc, key, RSC_STATUS, node, FALSE, TRUE, data_set);
7ebc05
     update_action_flags(probe, pe_action_optional | pe_action_clear);
7ebc05
 
7ebc05
-    /* Check if the node needs to be unfenced first */
7ebc05
-    if (is_set(rsc->flags, pe_rsc_needs_unfencing)) {
7ebc05
-        action_t *unfence = pe_fence_op(node, "on", data_set);
7ebc05
-
7ebc05
-        crm_notice("Unfencing %s for %s", node->details->uname, rsc->id);
7ebc05
-        order_actions(unfence, probe, pe_order_implies_then);
7ebc05
-
7ebc05
-        /* The lack of ordering constraints on STONITH_UP would
7ebc05
-         * traditionally mean unfencing is initiated /before/ the
7ebc05
-         * devices are started.
7ebc05
-         *
7ebc05
-         * However this is a non-issue as stonithd is now smart
7ebc05
-         * enough to be able to use devices directly from the cib
7ebc05
-         */
7ebc05
+    if(is_set(rsc->flags, pe_rsc_fence_device)) {
7ebc05
+        crm_crit("TODO: %s: Trigger when probing the device or the resource that needs unfencing?", rsc->id);
7ebc05
+        trigger_unfencing(NULL, node, "node discovery", probe, data_set);
7ebc05
     }
7ebc05
 
7ebc05
     /*
7ebc05
diff --git a/pengine/regression.sh b/pengine/regression.sh
7ebc05
index aa50854..9cb6c18 100755
7ebc05
--- a/pengine/regression.sh
7ebc05
+++ b/pengine/regression.sh
7ebc05
@@ -317,6 +317,11 @@ do_test bug-cl-5170 "Prevent clone from starting with on-fail=block"
7ebc05
 do_test clone-fail-block-colocation "Move colocated group when failed clone has on-fail=block"
7ebc05
 
7ebc05
 echo ""
7ebc05
+do_test unfence-startup "Clean unfencing"
7ebc05
+do_test unfence-definition "Unfencing when the agent changes"
7ebc05
+do_test unfence-parameters "Unfencing when the agent parameters changes"
7ebc05
+
7ebc05
+echo ""
7ebc05
 do_test master-0 "Stopped -> Slave"
7ebc05
 do_test master-1 "Stopped -> Promote"
7ebc05
 do_test master-2 "Stopped -> Promote : notify"