diff --git a/pengine/allocate.c b/pengine/allocate.c
index f2987cc579..8f15282788 100644
--- a/pengine/allocate.c
+++ b/pengine/allocate.c
@@ -1,2455 +1,2457 @@
 /*
  * Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public
  * License as published by the Free Software Foundation; either
  * version 2 of the License, or (at your option) any later version.
  *
  * This software is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  * General Public License for more details.
  *
  * You should have received a copy of the GNU General Public
  * License along with this library; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  */
 
 #include <crm_internal.h>
 
 #include <sys/param.h>
 
 #include <crm/crm.h>
 #include <crm/cib.h>
 #include <crm/msg_xml.h>
 #include <crm/common/xml.h>
 
 #include <glib.h>
 
 #include <crm/pengine/status.h>
 #include <pengine.h>
 #include <allocate.h>
 #include <utils.h>
 
 CRM_TRACE_INIT_DATA(pe_allocate);
 
 void set_alloc_actions(pe_working_set_t * data_set);
 void migrate_reload_madness(pe_working_set_t * data_set);
 extern void ReloadRsc(resource_t * rsc, node_t *node, pe_working_set_t * data_set);
 extern gboolean DeleteRsc(resource_t * rsc, node_t * node, gboolean optional, pe_working_set_t * data_set);
 static void apply_remote_node_ordering(pe_working_set_t *data_set);
 
 resource_alloc_functions_t resource_class_alloc_functions[] = {
     {
      native_merge_weights,
      native_color,
      native_create_actions,
      native_create_probe,
      native_internal_constraints,
      native_rsc_colocation_lh,
      native_rsc_colocation_rh,
      native_rsc_location,
      native_action_flags,
      native_update_actions,
      native_expand,
      native_append_meta,
      },
     {
      group_merge_weights,
      group_color,
      group_create_actions,
      native_create_probe,
      group_internal_constraints,
      group_rsc_colocation_lh,
      group_rsc_colocation_rh,
      group_rsc_location,
      group_action_flags,
      group_update_actions,
      group_expand,
      group_append_meta,
      },
     {
      clone_merge_weights,
      clone_color,
      clone_create_actions,
      clone_create_probe,
      clone_internal_constraints,
      clone_rsc_colocation_lh,
      clone_rsc_colocation_rh,
      clone_rsc_location,
      clone_action_flags,
      clone_update_actions,
      clone_expand,
      clone_append_meta,
      },
     {
      master_merge_weights,
      master_color,
      master_create_actions,
      clone_create_probe,
      master_internal_constraints,
      clone_rsc_colocation_lh,
      master_rsc_colocation_rh,
      clone_rsc_location,
      clone_action_flags,
      clone_update_actions,
      clone_expand,
      master_append_meta,
      },
     {
      container_merge_weights,
      container_color,
      container_create_actions,
      container_create_probe,
      container_internal_constraints,
      container_rsc_colocation_lh,
      container_rsc_colocation_rh,
      container_rsc_location,
      container_action_flags,
      container_update_actions,
      container_expand,
      container_append_meta,
      }
 };
 
 gboolean
 update_action_flags(action_t * action, enum pe_action_flags flags, const char *source, int line)
 {
     static unsigned long calls = 0;
     gboolean changed = FALSE;
     gboolean clear = is_set(flags, pe_action_clear);
     enum pe_action_flags last = action->flags;
 
     if (clear) {
         action->flags = crm_clear_bit(source, line, action->uuid, action->flags, flags);
     } else {
         action->flags = crm_set_bit(source, line, action->uuid, action->flags, flags);
     }
 
     if (last != action->flags) {
         calls++;
         changed = TRUE;
         /* Useful for tracking down _who_ changed a specific flag */
         /* CRM_ASSERT(calls != 534); */
         clear_bit(flags, pe_action_clear);
         crm_trace("%s on %s: %sset flags 0x%.6x (was 0x%.6x, now 0x%.6x, %lu, %s)",
                   action->uuid, action->node ? action->node->details->uname : "[none]",
                   clear ? "un-" : "", flags, last, action->flags, calls, source);
     }
 
     return changed;
 }
 
 static gboolean
 check_rsc_parameters(resource_t * rsc, node_t * node, xmlNode * rsc_entry,
                      gboolean active_here, pe_working_set_t * data_set)
 {
     int attr_lpc = 0;
     gboolean force_restart = FALSE;
     gboolean delete_resource = FALSE;
     gboolean changed = FALSE;
 
     const char *value = NULL;
     const char *old_value = NULL;
 
     const char *attr_list[] = {
         XML_ATTR_TYPE,
         XML_AGENT_ATTR_CLASS,
         XML_AGENT_ATTR_PROVIDER
     };
 
     for (; attr_lpc < DIMOF(attr_list); attr_lpc++) {
         value = crm_element_value(rsc->xml, attr_list[attr_lpc]);
         old_value = crm_element_value(rsc_entry, attr_list[attr_lpc]);
         if (value == old_value  /* ie. NULL */
             || crm_str_eq(value, old_value, TRUE)) {
             continue;
         }
 
         changed = TRUE;
         trigger_unfencing(rsc, node, "Device definition changed", NULL, data_set);
         if (active_here) {
             force_restart = TRUE;
             crm_notice("Forcing restart of %s on %s, %s changed: %s -> %s",
                        rsc->id, node->details->uname, attr_list[attr_lpc],
                        crm_str(old_value), crm_str(value));
         }
     }
     if (force_restart) {
         /* make sure the restart happens */
         stop_action(rsc, node, FALSE);
         set_bit(rsc->flags, pe_rsc_start_pending);
         delete_resource = TRUE;
 
     } else if (changed) {
         delete_resource = TRUE;
     }
     return delete_resource;
 }
 
 static void
 CancelXmlOp(resource_t * rsc, xmlNode * xml_op, node_t * active_node,
             const char *reason, pe_working_set_t * data_set)
 {
     int interval = 0;
     action_t *cancel = NULL;
 
     char *key = NULL;
     const char *task = NULL;
     const char *call_id = NULL;
     const char *interval_s = NULL;
 
     CRM_CHECK(xml_op != NULL, return);
     CRM_CHECK(active_node != NULL, return);
 
     task = crm_element_value(xml_op, XML_LRM_ATTR_TASK);
     call_id = crm_element_value(xml_op, XML_LRM_ATTR_CALLID);
     interval_s = crm_element_value(xml_op, XML_LRM_ATTR_INTERVAL);
 
     interval = crm_parse_int(interval_s, "0");
 
     /* we need to reconstruct the key because of the way we used to construct resource IDs */
     key = generate_op_key(rsc->id, task, interval);
 
     crm_info("Action %s on %s will be stopped: %s",
              key, active_node->details->uname, reason ? reason : "unknown");
 
     /* TODO: This looks highly dangerous if we ever try to schedule 'key' too */
     cancel = custom_action(rsc, strdup(key), RSC_CANCEL, active_node, FALSE, TRUE, data_set);
 
     free(cancel->task);
     free(cancel->cancel_task);
     cancel->task = strdup(RSC_CANCEL);
     cancel->cancel_task = strdup(task);
 
     add_hash_param(cancel->meta, XML_LRM_ATTR_TASK, task);
     add_hash_param(cancel->meta, XML_LRM_ATTR_CALLID, call_id);
     add_hash_param(cancel->meta, XML_LRM_ATTR_INTERVAL, interval_s);
 
     custom_action_order(rsc, stop_key(rsc), NULL, rsc, NULL, cancel, pe_order_optional, data_set);
     free(key);
     key = NULL;
 }
 
 static gboolean
 check_action_definition(resource_t * rsc, node_t * active_node, xmlNode * xml_op,
                         pe_working_set_t * data_set)
 {
     char *key = NULL;
     int interval = 0;
     const char *interval_s = NULL;
     const op_digest_cache_t *digest_data = NULL;
     gboolean did_change = FALSE;
 
     const char *task = crm_element_value(xml_op, XML_LRM_ATTR_TASK);
     const char *op_version;
     const char *digest_secure = NULL;
 
     CRM_CHECK(active_node != NULL, return FALSE);
     if (safe_str_eq(task, RSC_STOP)) {
         return FALSE;
     }
 
     interval_s = crm_element_value(xml_op, XML_LRM_ATTR_INTERVAL);
     interval = crm_parse_int(interval_s, "0");
 
     if (interval > 0) {
         xmlNode *op_match = NULL;
 
         /* we need to reconstruct the key because of the way we used to construct resource IDs */
         key = generate_op_key(rsc->id, task, interval);
 
         pe_rsc_trace(rsc, "Checking parameters for %s", key);
         op_match = find_rsc_op_entry(rsc, key);
 
         if (op_match == NULL && is_set(data_set->flags, pe_flag_stop_action_orphans)) {
             CancelXmlOp(rsc, xml_op, active_node, "orphan", data_set);
             free(key);
             return TRUE;
 
         } else if (op_match == NULL) {
             pe_rsc_debug(rsc, "Orphan action detected: %s on %s", key, active_node->details->uname);
             free(key);
             return TRUE;
         }
         free(key);
         key = NULL;
     }
 
     crm_trace("Testing %s_%s_%d on %s",
               rsc->id, task, interval, active_node->details->uname);
     if (interval == 0 && safe_str_eq(task, RSC_STATUS)) {
         /* Reload based on the start action not a probe */
         task = RSC_START;
 
     } else if (interval == 0 && safe_str_eq(task, RSC_MIGRATED)) {
         /* Reload based on the start action not a migrate */
         task = RSC_START;
     } else if (interval == 0 && safe_str_eq(task, RSC_PROMOTE)) {
         /* Reload based on the start action not a promote */
         task = RSC_START;
     }
 
     op_version = crm_element_value(xml_op, XML_ATTR_CRM_VERSION);
     digest_data = rsc_action_digest_cmp(rsc, xml_op, active_node, data_set);
 
     if(is_set(data_set->flags, pe_flag_sanitized)) {
         digest_secure = crm_element_value(xml_op, XML_LRM_ATTR_SECURE_DIGEST);
     }
 
     if(digest_data->rc != RSC_DIGEST_MATCH
        && digest_secure
        && digest_data->digest_secure_calc
        && strcmp(digest_data->digest_secure_calc, digest_secure) == 0) {
         fprintf(stdout, "Only 'private' parameters to %s_%s_%d on %s changed: %s\n",
                 rsc->id, task, interval, active_node->details->uname,
                 crm_element_value(xml_op, XML_ATTR_TRANSITION_MAGIC));
 
     } else if (digest_data->rc == RSC_DIGEST_RESTART) {
         /* Changes that force a restart */
         const char *digest_restart = crm_element_value(xml_op, XML_LRM_ATTR_RESTART_DIGEST);
 
         did_change = TRUE;
         key = generate_op_key(rsc->id, task, interval);
         crm_log_xml_info(digest_data->params_restart, "params:restart");
         pe_rsc_info(rsc, "Parameters to %s on %s changed: was %s vs. now %s (restart:%s) %s",
                  key, active_node->details->uname,
                  crm_str(digest_restart), digest_data->digest_restart_calc,
                  op_version, crm_element_value(xml_op, XML_ATTR_TRANSITION_MAGIC));
 
         custom_action(rsc, key, task, NULL, FALSE, TRUE, data_set);
         trigger_unfencing(rsc, NULL, "Device parameters changed", NULL, data_set);
 
     } else if ((digest_data->rc == RSC_DIGEST_ALL) || (digest_data->rc == RSC_DIGEST_UNKNOWN)) {
         /* Changes that can potentially be handled by a reload */
         const char *digest_restart = crm_element_value(xml_op, XML_LRM_ATTR_RESTART_DIGEST);
         const char *digest_all = crm_element_value(xml_op, XML_LRM_ATTR_OP_DIGEST);
 
         did_change = TRUE;
         trigger_unfencing(rsc, NULL, "Device parameters changed (reload)", NULL, data_set);
         crm_log_xml_info(digest_data->params_all, "params:reload");
         key = generate_op_key(rsc->id, task, interval);
         pe_rsc_info(rsc, "Parameters to %s on %s changed: was %s vs. now %s (reload:%s) %s",
                  key, active_node->details->uname,
                  crm_str(digest_all), digest_data->digest_all_calc, op_version,
                  crm_element_value(xml_op, XML_ATTR_TRANSITION_MAGIC));
 
         if (interval > 0) {
             action_t *op = NULL;
 
 #if 0
             /* Always reload/restart the entire resource */
             ReloadRsc(rsc, active_node, data_set);
 #else
             /* Re-sending the recurring op is sufficient - the old one will be cancelled automatically */
             op = custom_action(rsc, key, task, active_node, TRUE, TRUE, data_set);
             set_bit(op->flags, pe_action_reschedule);
 #endif
 
         } else if (digest_restart && rsc->isolation_wrapper == NULL && (uber_parent(rsc))->isolation_wrapper == NULL) {
             pe_rsc_trace(rsc, "Reloading '%s' action for resource %s", task, rsc->id);
 
             /* Reload this resource */
             ReloadRsc(rsc, active_node, data_set);
             free(key);
 
         } else {
             pe_rsc_trace(rsc, "Resource %s doesn't know how to reload", rsc->id);
 
             /* Re-send the start/demote/promote op
              * Recurring ops will be detected independently
              */
             custom_action(rsc, key, task, NULL, FALSE, TRUE, data_set);
         }
     }
 
     return did_change;
 }
 
 
 static void
 check_actions_for(xmlNode * rsc_entry, resource_t * rsc, node_t * node, pe_working_set_t * data_set)
 {
     GListPtr gIter = NULL;
     int offset = -1;
     int interval = 0;
     int stop_index = 0;
     int start_index = 0;
 
     const char *task = NULL;
     const char *interval_s = NULL;
 
     xmlNode *rsc_op = NULL;
     GListPtr op_list = NULL;
     GListPtr sorted_op_list = NULL;
     gboolean is_probe = FALSE;
     gboolean did_change = FALSE;
 
     CRM_CHECK(node != NULL, return);
 
     if (is_set(rsc->flags, pe_rsc_orphan)) {
         resource_t *parent = uber_parent(rsc);
         if(parent == NULL
            || pe_rsc_is_clone(parent) == FALSE
            || is_set(parent->flags, pe_rsc_unique)) {
             pe_rsc_trace(rsc, "Skipping param check for %s and deleting: orphan", rsc->id);
             DeleteRsc(rsc, node, FALSE, data_set);
         } else {
             pe_rsc_trace(rsc, "Skipping param check for %s (orphan clone)", rsc->id);
         }
         return;
 
     } else if (pe_find_node_id(rsc->running_on, node->details->id) == NULL) {
         if (check_rsc_parameters(rsc, node, rsc_entry, FALSE, data_set)) {
             DeleteRsc(rsc, node, FALSE, data_set);
         }
         pe_rsc_trace(rsc, "Skipping param check for %s: no longer active on %s",
                      rsc->id, node->details->uname);
         return;
     }
 
     pe_rsc_trace(rsc, "Processing %s on %s", rsc->id, node->details->uname);
 
     if (check_rsc_parameters(rsc, node, rsc_entry, TRUE, data_set)) {
         DeleteRsc(rsc, node, FALSE, data_set);
     }
 
     for (rsc_op = __xml_first_child(rsc_entry); rsc_op != NULL; rsc_op = __xml_next_element(rsc_op)) {
         if (crm_str_eq((const char *)rsc_op->name, XML_LRM_TAG_RSC_OP, TRUE)) {
             op_list = g_list_prepend(op_list, rsc_op);
         }
     }
 
     sorted_op_list = g_list_sort(op_list, sort_op_by_callid);
     calculate_active_ops(sorted_op_list, &start_index, &stop_index);
 
     for (gIter = sorted_op_list; gIter != NULL; gIter = gIter->next) {
         xmlNode *rsc_op = (xmlNode *) gIter->data;
 
         offset++;
 
         if (start_index < stop_index) {
             /* stopped */
             continue;
         } else if (offset < start_index) {
             /* action occurred prior to a start */
             continue;
         }
 
         is_probe = FALSE;
         did_change = FALSE;
         task = crm_element_value(rsc_op, XML_LRM_ATTR_TASK);
 
         interval_s = crm_element_value(rsc_op, XML_LRM_ATTR_INTERVAL);
         interval = crm_parse_int(interval_s, "0");
 
         if (interval == 0 && safe_str_eq(task, RSC_STATUS)) {
             is_probe = TRUE;
         }
 
         if (interval > 0 &&
             (is_set(rsc->flags, pe_rsc_maintenance) || node->details->maintenance)) {
             CancelXmlOp(rsc, rsc_op, node, "maintenance mode", data_set);
 
         } else if (is_probe || safe_str_eq(task, RSC_START) || safe_str_eq(task, RSC_PROMOTE) || interval > 0
                    || safe_str_eq(task, RSC_MIGRATED)) {
             did_change = check_action_definition(rsc, node, rsc_op, data_set);
         }
 
         if (did_change && get_failcount(node, rsc, NULL, data_set)) {
             char *key = NULL;
             action_t *action_clear = NULL;
 
             key = generate_op_key(rsc->id, CRM_OP_CLEAR_FAILCOUNT, 0);
             action_clear =
                 custom_action(rsc, key, CRM_OP_CLEAR_FAILCOUNT, node, FALSE, TRUE, data_set);
             set_bit(action_clear->flags, pe_action_runnable);
 
             crm_notice("Clearing failure of %s on %s "
                        "because action definition changed " CRM_XS " %s",
                        rsc->id, node->details->uname, action_clear->uuid);
         }
     }
 
     g_list_free(sorted_op_list);
 
 }
 
 static GListPtr
 find_rsc_list(GListPtr result, resource_t * rsc, const char *id, gboolean renamed_clones,
               gboolean partial, pe_working_set_t * data_set)
 {
     GListPtr gIter = NULL;
     gboolean match = FALSE;
 
     if (id == NULL) {
         return NULL;
 
     } else if (rsc == NULL && data_set) {
 
         for (gIter = data_set->resources; gIter != NULL; gIter = gIter->next) {
             resource_t *child = (resource_t *) gIter->data;
 
             result = find_rsc_list(result, child, id, renamed_clones, partial, NULL);
         }
 
         return result;
 
     } else if (rsc == NULL) {
         return NULL;
     }
 
     if (partial) {
         if (strstr(rsc->id, id)) {
             match = TRUE;
 
         } else if (renamed_clones && rsc->clone_name && strstr(rsc->clone_name, id)) {
             match = TRUE;
         }
 
     } else {
         if (strcmp(rsc->id, id) == 0) {
             match = TRUE;
 
         } else if (renamed_clones && rsc->clone_name && strcmp(rsc->clone_name, id) == 0) {
             match = TRUE;
         }
     }
 
     if (match) {
         result = g_list_prepend(result, rsc);
     }
 
     if (rsc->children) {
         gIter = rsc->children;
         for (; gIter != NULL; gIter = gIter->next) {
             resource_t *child = (resource_t *) gIter->data;
 
             result = find_rsc_list(result, child, id, renamed_clones, partial, NULL);
         }
     }
 
     return result;
 }
 
 static void
 check_actions(pe_working_set_t * data_set)
 {
     const char *id = NULL;
     node_t *node = NULL;
     xmlNode *lrm_rscs = NULL;
     xmlNode *status = get_object_root(XML_CIB_TAG_STATUS, data_set->input);
 
     xmlNode *node_state = NULL;
 
     for (node_state = __xml_first_child(status); node_state != NULL;
          node_state = __xml_next_element(node_state)) {
         if (crm_str_eq((const char *)node_state->name, XML_CIB_TAG_STATE, TRUE)) {
             id = crm_element_value(node_state, XML_ATTR_ID);
             lrm_rscs = find_xml_node(node_state, XML_CIB_TAG_LRM, FALSE);
             lrm_rscs = find_xml_node(lrm_rscs, XML_LRM_TAG_RESOURCES, FALSE);
 
             node = pe_find_node_id(data_set->nodes, id);
 
             if (node == NULL) {
                 continue;
 
             /* Still need to check actions for a maintenance node to cancel existing monitor operations */
             } else if (can_run_resources(node) == FALSE && node->details->maintenance == FALSE) {
                 crm_trace("Skipping param check for %s: can't run resources",
                           node->details->uname);
                 continue;
             }
 
             crm_trace("Processing node %s", node->details->uname);
             if (node->details->online || is_set(data_set->flags, pe_flag_stonith_enabled)) {
                 xmlNode *rsc_entry = NULL;
 
                 for (rsc_entry = __xml_first_child(lrm_rscs); rsc_entry != NULL;
                      rsc_entry = __xml_next_element(rsc_entry)) {
                     if (crm_str_eq((const char *)rsc_entry->name, XML_LRM_TAG_RESOURCE, TRUE)) {
 
                         if (xml_has_children(rsc_entry)) {
                             GListPtr gIter = NULL;
                             GListPtr result = NULL;
                             const char *rsc_id = ID(rsc_entry);
 
                             CRM_CHECK(rsc_id != NULL, return);
 
                             result = find_rsc_list(NULL, NULL, rsc_id, TRUE, FALSE, data_set);
                             for (gIter = result; gIter != NULL; gIter = gIter->next) {
                                 resource_t *rsc = (resource_t *) gIter->data;
 
                                 if (rsc->variant != pe_native) {
                                     continue;
                                 }
                                 check_actions_for(rsc_entry, rsc, node, data_set);
                             }
                             g_list_free(result);
                         }
                     }
                 }
             }
         }
     }
 }
 
 static gboolean
 apply_placement_constraints(pe_working_set_t * data_set)
 {
     GListPtr gIter = NULL;
 
     crm_trace("Applying constraints...");
 
     for (gIter = data_set->placement_constraints; gIter != NULL; gIter = gIter->next) {
         rsc_to_node_t *cons = (rsc_to_node_t *) gIter->data;
 
         cons->rsc_lh->cmds->rsc_location(cons->rsc_lh, cons);
     }
 
     return TRUE;
 
 }
 
 static gboolean
 failcount_clear_action_exists(node_t * node, resource_t * rsc)
 {
     gboolean rc = FALSE;
     char *key = generate_op_key(rsc->id, CRM_OP_CLEAR_FAILCOUNT, 0);
     GListPtr list = find_actions_exact(rsc->actions, key, node);
 
     if (list) {
         rc = TRUE;
     }
     g_list_free(list);
     free(key);
 
     return rc;
 }
 
 /*!
  * \internal
  * \brief Force resource away if failures hit migration threshold
  *
  * \param[in,out] rsc       Resource to check for failures
  * \param[in,out] node      Node to check for failures
  * \param[in,out] data_set  Cluster working set to update
  */
 static void
 check_migration_threshold(resource_t *rsc, node_t *node,
                           pe_working_set_t *data_set)
 {
     int fail_count, countdown;
     resource_t *failed;
 
     /* Migration threshold of 0 means never force away */
     if (rsc->migration_threshold == 0) {
         return;
     }
 
     /* If there are no failures, there's no need to force away */
     fail_count = get_failcount_all(node, rsc, NULL, data_set);
     if (fail_count <= 0) {
         return;
     }
 
     /* How many more times recovery will be tried on this node */
     countdown = QB_MAX(rsc->migration_threshold - fail_count, 0);
 
     /* If failed resource has a parent, we'll force the parent away */
     failed = rsc;
     if (is_not_set(rsc->flags, pe_rsc_unique)) {
         failed = uber_parent(rsc);
     }
 
     if (countdown == 0) {
         resource_location(failed, node, -INFINITY, "__fail_limit__", data_set);
         crm_warn("Forcing %s away from %s after %d failures (max=%d)",
                  failed->id, node->details->uname, fail_count,
                  rsc->migration_threshold);
     } else {
         crm_info("%s can fail %d more times on %s before being forced off",
                  failed->id, countdown, node->details->uname);
     }
 }
 
 static void
 common_apply_stickiness(resource_t * rsc, node_t * node, pe_working_set_t * data_set)
 {
     if (rsc->children) {
         GListPtr gIter = rsc->children;
 
         for (; gIter != NULL; gIter = gIter->next) {
             resource_t *child_rsc = (resource_t *) gIter->data;
 
             common_apply_stickiness(child_rsc, node, data_set);
         }
         return;
     }
 
     if (is_set(rsc->flags, pe_rsc_managed)
         && rsc->stickiness != 0 && g_list_length(rsc->running_on) == 1) {
         node_t *current = pe_find_node_id(rsc->running_on, node->details->id);
         node_t *match = pe_hash_table_lookup(rsc->allowed_nodes, node->details->id);
 
         if (current == NULL) {
 
         } else if (match != NULL || is_set(data_set->flags, pe_flag_symmetric_cluster)) {
             resource_t *sticky_rsc = rsc;
 
             resource_location(sticky_rsc, node, rsc->stickiness, "stickiness", data_set);
             pe_rsc_debug(sticky_rsc, "Resource %s: preferring current location"
                          " (node=%s, weight=%d)", sticky_rsc->id,
                          node->details->uname, rsc->stickiness);
         } else {
             GHashTableIter iter;
             node_t *nIter = NULL;
 
             pe_rsc_debug(rsc, "Ignoring stickiness for %s: the cluster is asymmetric"
                          " and node %s is not explicitly allowed", rsc->id, node->details->uname);
             g_hash_table_iter_init(&iter, rsc->allowed_nodes);
             while (g_hash_table_iter_next(&iter, NULL, (void **)&nIter)) {
                 crm_err("%s[%s] = %d", rsc->id, nIter->details->uname, nIter->weight);
             }
         }
     }
 
     /* Check the migration threshold only if a failcount clear action
      * has not already been placed for this resource on the node.
      * There is no sense in potentially forcing the resource from this
      * node if the failcount is being reset anyway. */
     if (failcount_clear_action_exists(node, rsc) == FALSE) {
         check_migration_threshold(rsc, node, data_set);
     }
 }
 
 void
 complex_set_cmds(resource_t * rsc)
 {
     GListPtr gIter = rsc->children;
 
     rsc->cmds = &resource_class_alloc_functions[rsc->variant];
 
     for (; gIter != NULL; gIter = gIter->next) {
         resource_t *child_rsc = (resource_t *) gIter->data;
 
         complex_set_cmds(child_rsc);
     }
 }
 
 void
 set_alloc_actions(pe_working_set_t * data_set)
 {
 
     GListPtr gIter = data_set->resources;
 
     for (; gIter != NULL; gIter = gIter->next) {
         resource_t *rsc = (resource_t *) gIter->data;
 
         complex_set_cmds(rsc);
     }
 }
 
 static void
 calculate_system_health(gpointer gKey, gpointer gValue, gpointer user_data)
 {
     const char *key = (const char *)gKey;
     const char *value = (const char *)gValue;
     int *system_health = (int *)user_data;
 
     if (!gKey || !gValue || !user_data) {
         return;
     }
 
     /* Does it start with #health? */
     if (0 == strncmp(key, "#health", 7)) {
         int score;
 
         /* Convert the value into an integer */
         score = char2score(value);
 
         /* Add it to the running total */
         *system_health = merge_weights(score, *system_health);
     }
 }
 
 static gboolean
 apply_system_health(pe_working_set_t * data_set)
 {
     GListPtr gIter = NULL;
     const char *health_strategy = pe_pref(data_set->config_hash, "node-health-strategy");
     int base_health = 0;
 
     if (health_strategy == NULL || safe_str_eq(health_strategy, "none")) {
         /* Prevent any accidental health -> score translation */
         node_score_red = 0;
         node_score_yellow = 0;
         node_score_green = 0;
         return TRUE;
 
     } else if (safe_str_eq(health_strategy, "migrate-on-red")) {
 
         /* Resources on nodes which have health values of red are
          * weighted away from that node.
          */
         node_score_red = -INFINITY;
         node_score_yellow = 0;
         node_score_green = 0;
 
     } else if (safe_str_eq(health_strategy, "only-green")) {
 
         /* Resources on nodes which have health values of red or yellow
          * are forced away from that node.
          */
         node_score_red = -INFINITY;
         node_score_yellow = -INFINITY;
         node_score_green = 0;
 
     } else if (safe_str_eq(health_strategy, "progressive")) {
         /* Same as the above, but use the r/y/g scores provided by the user
          * Defaults are provided by the pe_prefs table
          * Also, custom health "base score" can be used
          */
         base_health = crm_parse_int(pe_pref(data_set->config_hash, "node-health-base"), "0");
 
     } else if (safe_str_eq(health_strategy, "custom")) {
 
         /* Requires the admin to configure the rsc_location constaints for
          * processing the stored health scores
          */
         /* TODO: Check for the existence of appropriate node health constraints */
         return TRUE;
 
     } else {
         crm_err("Unknown node health strategy: %s", health_strategy);
         return FALSE;
     }
 
     crm_info("Applying automated node health strategy: %s", health_strategy);
 
     for (gIter = data_set->nodes; gIter != NULL; gIter = gIter->next) {
         int system_health = base_health;
         node_t *node = (node_t *) gIter->data;
 
         /* Search through the node hash table for system health entries. */
         g_hash_table_foreach(node->details->attrs, calculate_system_health, &system_health);
 
         crm_info(" Node %s has an combined system health of %d",
                  node->details->uname, system_health);
 
         /* If the health is non-zero, then create a new rsc2node so that the
          * weight will be added later on.
          */
         if (system_health != 0) {
 
             GListPtr gIter2 = data_set->resources;
 
             for (; gIter2 != NULL; gIter2 = gIter2->next) {
                 resource_t *rsc = (resource_t *) gIter2->data;
 
                 rsc2node_new(health_strategy, rsc, system_health, NULL, node, data_set);
             }
         }
 
     }
 
     return TRUE;
 }
 
 gboolean
 stage0(pe_working_set_t * data_set)
 {
     xmlNode *cib_constraints = get_object_root(XML_CIB_TAG_CONSTRAINTS, data_set->input);
 
     if (data_set->input == NULL) {
         return FALSE;
     }
 
     if (is_set(data_set->flags, pe_flag_have_status) == FALSE) {
         crm_trace("Calculating status");
         cluster_status(data_set);
     }
 
     set_alloc_actions(data_set);
     apply_system_health(data_set);
     unpack_constraints(cib_constraints, data_set);
 
     return TRUE;
 }
 
 /*
  * Check nodes for resources started outside of the LRM
  */
 gboolean
 probe_resources(pe_working_set_t * data_set)
 {
     action_t *probe_node_complete = NULL;
 
     GListPtr gIter = NULL;
     GListPtr gIter2 = NULL;
 
     for (gIter = data_set->nodes; gIter != NULL; gIter = gIter->next) {
         node_t *node = (node_t *) gIter->data;
         const char *probed = g_hash_table_lookup(node->details->attrs, CRM_OP_PROBED);
 
         if (node->details->online == FALSE) {
             continue;
 
         } else if (node->details->unclean) {
             continue;
 
         } else if (is_container_remote_node(node)) {
             /* TODO enable guest node probes once ordered probing is implemented */
             continue;
 
         } else if (node->details->rsc_discovery_enabled == FALSE) {
             /* resource discovery is disabled for this node */
             continue;
         }
 
         if (probed != NULL && crm_is_true(probed) == FALSE) {
             action_t *probe_op = custom_action(NULL, crm_strdup_printf("%s-%s", CRM_OP_REPROBE, node->details->uname),
                                                CRM_OP_REPROBE, node, FALSE, TRUE, data_set);
 
             add_hash_param(probe_op->meta, XML_ATTR_TE_NOWAIT, XML_BOOLEAN_TRUE);
             continue;
         }
 
         for (gIter2 = data_set->resources; gIter2 != NULL; gIter2 = gIter2->next) {
             resource_t *rsc = (resource_t *) gIter2->data;
 
             rsc->cmds->create_probe(rsc, node, probe_node_complete, FALSE, data_set);
         }
     }
     return TRUE;
 }
 
 static void
 rsc_discover_filter(resource_t *rsc, node_t *node)
 {
     GListPtr gIter = rsc->children;
     resource_t *top = uber_parent(rsc);
     node_t *match;
 
     if (rsc->exclusive_discover == FALSE && top->exclusive_discover == FALSE) {
         return;
     }
 
     for (; gIter != NULL; gIter = gIter->next) {
         resource_t *child_rsc = (resource_t *) gIter->data;
         rsc_discover_filter(child_rsc, node);
     }
 
     match = g_hash_table_lookup(rsc->allowed_nodes, node->details->id);
     if (match && match->rsc_discover_mode != discover_exclusive) {
         match->weight = -INFINITY;
     }
 }
 
 /*
  * Count how many valid nodes we have (so we know the maximum number of
  *  colors we can resolve).
  *
  * Apply node constraints (ie. filter the "allowed_nodes" part of resources
  */
 gboolean
 stage2(pe_working_set_t * data_set)
 {
     GListPtr gIter = NULL;
 
     crm_trace("Applying placement constraints");
 
     gIter = data_set->nodes;
     for (; gIter != NULL; gIter = gIter->next) {
         node_t *node = (node_t *) gIter->data;
 
         if (node == NULL) {
             /* error */
 
         } else if (node->weight >= 0.0  /* global weight */
                    && node->details->online && node->details->type != node_ping) {
             data_set->max_valid_nodes++;
         }
     }
 
     apply_placement_constraints(data_set);
 
     gIter = data_set->nodes;
     for (; gIter != NULL; gIter = gIter->next) {
         GListPtr gIter2 = NULL;
         node_t *node = (node_t *) gIter->data;
 
         gIter2 = data_set->resources;
         for (; gIter2 != NULL; gIter2 = gIter2->next) {
             resource_t *rsc = (resource_t *) gIter2->data;
 
             common_apply_stickiness(rsc, node, data_set);
             rsc_discover_filter(rsc, node);
         }
     }
 
     return TRUE;
 }
 
 /*
  * Create internal resource constraints before allocation
  */
 gboolean
 stage3(pe_working_set_t * data_set)
 {
 
     GListPtr gIter = data_set->resources;
 
     for (; gIter != NULL; gIter = gIter->next) {
         resource_t *rsc = (resource_t *) gIter->data;
 
         rsc->cmds->internal_constraints(rsc, data_set);
     }
 
     return TRUE;
 }
 
 /*
  * Check for orphaned or redefined actions
  */
 gboolean
 stage4(pe_working_set_t * data_set)
 {
     check_actions(data_set);
     return TRUE;
 }
 
 static gint
 sort_rsc_process_order(gconstpointer a, gconstpointer b, gpointer data)
 {
     int rc = 0;
     int r1_weight = -INFINITY;
     int r2_weight = -INFINITY;
 
     const char *reason = "existence";
 
     const GListPtr nodes = (GListPtr) data;
     resource_t *resource1 = (resource_t *) convert_const_pointer(a);
     resource_t *resource2 = (resource_t *) convert_const_pointer(b);
 
     node_t *r1_node = NULL;
     node_t *r2_node = NULL;
     GListPtr gIter = NULL;
     GHashTable *r1_nodes = NULL;
     GHashTable *r2_nodes = NULL;
 
     if (a == NULL && b == NULL) {
         goto done;
     }
     if (a == NULL) {
         return 1;
     }
     if (b == NULL) {
         return -1;
     }
 
     reason = "priority";
     r1_weight = resource1->priority;
     r2_weight = resource2->priority;
 
     if (r1_weight > r2_weight) {
         rc = -1;
         goto done;
     }
 
     if (r1_weight < r2_weight) {
         rc = 1;
         goto done;
     }
 
     reason = "no node list";
     if (nodes == NULL) {
         goto done;
     }
 
     r1_nodes =
         rsc_merge_weights(resource1, resource1->id, NULL, NULL, 1,
                           pe_weights_forward | pe_weights_init);
     dump_node_scores(LOG_TRACE, NULL, resource1->id, r1_nodes);
     r2_nodes =
         rsc_merge_weights(resource2, resource2->id, NULL, NULL, 1,
                           pe_weights_forward | pe_weights_init);
     dump_node_scores(LOG_TRACE, NULL, resource2->id, r2_nodes);
 
     /* Current location score */
     reason = "current location";
     r1_weight = -INFINITY;
     r2_weight = -INFINITY;
 
     if (resource1->running_on) {
         r1_node = g_list_nth_data(resource1->running_on, 0);
         r1_node = g_hash_table_lookup(r1_nodes, r1_node->details->id);
         if (r1_node != NULL) {
             r1_weight = r1_node->weight;
         }
     }
     if (resource2->running_on) {
         r2_node = g_list_nth_data(resource2->running_on, 0);
         r2_node = g_hash_table_lookup(r2_nodes, r2_node->details->id);
         if (r2_node != NULL) {
             r2_weight = r2_node->weight;
         }
     }
 
     if (r1_weight > r2_weight) {
         rc = -1;
         goto done;
     }
 
     if (r1_weight < r2_weight) {
         rc = 1;
         goto done;
     }
 
     reason = "score";
     for (gIter = nodes; gIter != NULL; gIter = gIter->next) {
         node_t *node = (node_t *) gIter->data;
 
         r1_node = NULL;
         r2_node = NULL;
 
         r1_weight = -INFINITY;
         if (r1_nodes) {
             r1_node = g_hash_table_lookup(r1_nodes, node->details->id);
         }
         if (r1_node) {
             r1_weight = r1_node->weight;
         }
 
         r2_weight = -INFINITY;
         if (r2_nodes) {
             r2_node = g_hash_table_lookup(r2_nodes, node->details->id);
         }
         if (r2_node) {
             r2_weight = r2_node->weight;
         }
 
         if (r1_weight > r2_weight) {
             rc = -1;
             goto done;
         }
 
         if (r1_weight < r2_weight) {
             rc = 1;
             goto done;
         }
     }
 
   done:
     crm_trace("%s (%d) on %s %c %s (%d) on %s: %s",
               resource1->id, r1_weight, r1_node ? r1_node->details->id : "n/a",
               rc < 0 ? '>' : rc > 0 ? '<' : '=',
               resource2->id, r2_weight, r2_node ? r2_node->details->id : "n/a", reason);
 
     if (r1_nodes) {
         g_hash_table_destroy(r1_nodes);
     }
     if (r2_nodes) {
         g_hash_table_destroy(r2_nodes);
     }
 
     return rc;
 }
 
 static void
 allocate_resources(pe_working_set_t * data_set)
 {
     GListPtr gIter = NULL;
 
     if (is_set(data_set->flags, pe_flag_have_remote_nodes)) {
         /* Force remote connection resources to be allocated first. This
          * also forces any colocation dependencies to be allocated as well */
         for (gIter = data_set->resources; gIter != NULL; gIter = gIter->next) {
             resource_t *rsc = (resource_t *) gIter->data;
             if (rsc->is_remote_node == FALSE) {
                 continue;
             }
             pe_rsc_trace(rsc, "Allocating: %s", rsc->id);
             /* For remote node connection resources, always prefer the partial
              * migration target during resource allocation, if the rsc is in the
              * middle of a migration.
              */
             rsc->cmds->allocate(rsc, rsc->partial_migration_target, data_set);
         }
     }
 
     /* now do the rest of the resources */
     for (gIter = data_set->resources; gIter != NULL; gIter = gIter->next) {
         resource_t *rsc = (resource_t *) gIter->data;
         if (rsc->is_remote_node == TRUE) {
             continue;
         }
         pe_rsc_trace(rsc, "Allocating: %s", rsc->id);
         rsc->cmds->allocate(rsc, NULL, data_set);
     }
 }
 
 static void
 cleanup_orphans(resource_t * rsc, pe_working_set_t * data_set)
 {
     GListPtr gIter = NULL;
 
     if (is_set(data_set->flags, pe_flag_stop_rsc_orphans) == FALSE) {
         return;
     }
 
     /* Don't recurse into ->children, those are just unallocated clone instances */
     if(is_not_set(rsc->flags, pe_rsc_orphan)) {
         return;
     }
 
     for (gIter = data_set->nodes; gIter != NULL; gIter = gIter->next) {
         node_t *node = (node_t *) gIter->data;
 
         if (node->details->online && get_failcount(node, rsc, NULL, data_set)) {
             char *key = generate_op_key(rsc->id, CRM_OP_CLEAR_FAILCOUNT, 0);
             action_t *clear_op = custom_action(rsc, key, CRM_OP_CLEAR_FAILCOUNT,
                                                node, FALSE, TRUE, data_set);
 
             add_hash_param(clear_op->meta, XML_ATTR_TE_NOWAIT, XML_BOOLEAN_TRUE);
 
             pe_rsc_info(rsc,
                         "Clearing failure of %s on %s because it is orphaned "
                         CRM_XS " %s",
                         rsc->id, node->details->uname, clear_op->uuid);
 
             custom_action_order(rsc, NULL, clear_op,
                             rsc, generate_op_key(rsc->id, RSC_STOP, 0), NULL,
                             pe_order_optional, data_set);
         }
     }
 }
 
 gboolean
 stage5(pe_working_set_t * data_set)
 {
     GListPtr gIter = NULL;
 
     if (safe_str_neq(data_set->placement_strategy, "default")) {
         GListPtr nodes = g_list_copy(data_set->nodes);
 
         nodes = g_list_sort_with_data(nodes, sort_node_weight, NULL);
 
         data_set->resources =
             g_list_sort_with_data(data_set->resources, sort_rsc_process_order, nodes);
 
         g_list_free(nodes);
     }
 
     gIter = data_set->nodes;
     for (; gIter != NULL; gIter = gIter->next) {
         node_t *node = (node_t *) gIter->data;
 
         dump_node_capacity(show_utilization ? 0 : utilization_log_level, "Original", node);
     }
 
     crm_trace("Allocating services");
     /* Take (next) highest resource, assign it and create its actions */
 
     allocate_resources(data_set);
 
     gIter = data_set->nodes;
     for (; gIter != NULL; gIter = gIter->next) {
         node_t *node = (node_t *) gIter->data;
 
         dump_node_capacity(show_utilization ? 0 : utilization_log_level, "Remaining", node);
     }
 
     if (is_set(data_set->flags, pe_flag_startup_probes)) {
         crm_trace("Calculating needed probes");
         /* This code probably needs optimization
          * ptest -x with 100 nodes, 100 clones and clone-max=100:
 
          With probes:
 
          ptest[14781]: 2010/09/27_17:56:46 notice: TRACE: do_calculations: pengine.c:258 Calculate cluster status
          ptest[14781]: 2010/09/27_17:56:46 notice: TRACE: do_calculations: pengine.c:278 Applying placement constraints
          ptest[14781]: 2010/09/27_17:56:47 notice: TRACE: do_calculations: pengine.c:285 Create internal constraints
          ptest[14781]: 2010/09/27_17:56:47 notice: TRACE: do_calculations: pengine.c:292 Check actions
          ptest[14781]: 2010/09/27_17:56:48 notice: TRACE: do_calculations: pengine.c:299 Allocate resources
          ptest[14781]: 2010/09/27_17:56:48 notice: TRACE: stage5: allocate.c:881 Allocating services
          ptest[14781]: 2010/09/27_17:56:49 notice: TRACE: stage5: allocate.c:894 Calculating needed probes
          ptest[14781]: 2010/09/27_17:56:51 notice: TRACE: stage5: allocate.c:899 Creating actions
          ptest[14781]: 2010/09/27_17:56:52 notice: TRACE: stage5: allocate.c:905 Creating done
          ptest[14781]: 2010/09/27_17:56:52 notice: TRACE: do_calculations: pengine.c:306 Processing fencing and shutdown cases
          ptest[14781]: 2010/09/27_17:56:52 notice: TRACE: do_calculations: pengine.c:313 Applying ordering constraints
          36s
          ptest[14781]: 2010/09/27_17:57:28 notice: TRACE: do_calculations: pengine.c:320 Create transition graph
 
          Without probes:
 
          ptest[14637]: 2010/09/27_17:56:21 notice: TRACE: do_calculations: pengine.c:258 Calculate cluster status
          ptest[14637]: 2010/09/27_17:56:22 notice: TRACE: do_calculations: pengine.c:278 Applying placement constraints
          ptest[14637]: 2010/09/27_17:56:22 notice: TRACE: do_calculations: pengine.c:285 Create internal constraints
          ptest[14637]: 2010/09/27_17:56:22 notice: TRACE: do_calculations: pengine.c:292 Check actions
          ptest[14637]: 2010/09/27_17:56:23 notice: TRACE: do_calculations: pengine.c:299 Allocate resources
          ptest[14637]: 2010/09/27_17:56:23 notice: TRACE: stage5: allocate.c:881 Allocating services
          ptest[14637]: 2010/09/27_17:56:24 notice: TRACE: stage5: allocate.c:899 Creating actions
          ptest[14637]: 2010/09/27_17:56:25 notice: TRACE: stage5: allocate.c:905 Creating done
          ptest[14637]: 2010/09/27_17:56:25 notice: TRACE: do_calculations: pengine.c:306 Processing fencing and shutdown cases
          ptest[14637]: 2010/09/27_17:56:25 notice: TRACE: do_calculations: pengine.c:313 Applying ordering constraints
          ptest[14637]: 2010/09/27_17:56:25 notice: TRACE: do_calculations: pengine.c:320 Create transition graph
         */
 
         probe_resources(data_set);
     }
 
     crm_trace("Handle orphans");
 
     for (gIter = data_set->resources; gIter != NULL; gIter = gIter->next) {
         resource_t *rsc = (resource_t *) gIter->data;
         cleanup_orphans(rsc, data_set);
     }
 
     crm_trace("Creating actions");
 
     for (gIter = data_set->resources; gIter != NULL; gIter = gIter->next) {
         resource_t *rsc = (resource_t *) gIter->data;
 
         rsc->cmds->create_actions(rsc, data_set);
     }
 
     crm_trace("Creating done");
     return TRUE;
 }
 
 static gboolean
 is_managed(const resource_t * rsc)
 {
     GListPtr gIter = rsc->children;
 
     if (is_set(rsc->flags, pe_rsc_managed)) {
         return TRUE;
     }
 
     for (; gIter != NULL; gIter = gIter->next) {
         resource_t *child_rsc = (resource_t *) gIter->data;
 
         if (is_managed(child_rsc)) {
             return TRUE;
         }
     }
 
     return FALSE;
 }
 
 static gboolean
 any_managed_resources(pe_working_set_t * data_set)
 {
 
     GListPtr gIter = data_set->resources;
 
     for (; gIter != NULL; gIter = gIter->next) {
         resource_t *rsc = (resource_t *) gIter->data;
 
         if (is_managed(rsc)) {
             return TRUE;
         }
     }
     return FALSE;
 }
 
 /*!
  * \internal
  * \brief Create pseudo-op for guest node fence, and order relative to it
  *
  * \param[in] node      Guest node to fence
  * \param[in] done      STONITH_DONE operation
  * \param[in] data_set  Working set of CIB state
  */
 static void
 fence_guest(pe_node_t *node, pe_action_t *done, pe_working_set_t *data_set)
 {
     resource_t *container = node->details->remote_rsc->container;
     pe_action_t *stop = NULL;
     pe_action_t *stonith_op = NULL;
 
     /* The fence action is just a label; we don't do anything differently for
      * off vs. reboot. We specify it explicitly, rather than let it default to
      * cluster's default action, because we are not _initiating_ fencing -- we
      * are creating a pseudo-event to describe fencing that is already occurring
      * by other means (container recovery).
      */
     const char *fence_action = "off";
 
     /* Check whether guest's container resource is has any explicit stop or
      * start (the stop may be implied by fencing of the guest's host).
      */
     if (container) {
         stop = find_first_action(container->actions, NULL, CRMD_ACTION_STOP, NULL);
 
         if (find_first_action(container->actions, NULL, CRMD_ACTION_START, NULL)) {
             fence_action = "reboot";
         }
     }
 
     /* Create a fence pseudo-event, so we have an event to order actions
      * against, and crmd can always detect it.
      */
     stonith_op = pe_fence_op(node, fence_action, FALSE, data_set);
     update_action_flags(stonith_op, pe_action_pseudo | pe_action_runnable,
                         __FUNCTION__, __LINE__);
 
     /* We want to imply stops/demotes after the guest is stopped, not wait until
      * it is restarted, so we always order pseudo-fencing after stop, not start
      * (even though start might be closer to what is done for a real reboot).
      */
     if (stop) {
         order_actions(stop, stonith_op,
                       pe_order_runnable_left|pe_order_implies_then);
         crm_info("Implying guest node %s is down (action %d) "
                  "after container %s is stopped (action %d)",
                  node->details->uname, stonith_op->id,
                  container->id, stop->id);
     } else {
         crm_info("Implying guest node %s is down (action %d) ",
                  node->details->uname, stonith_op->id);
     }
 
     /* @TODO: Order pseudo-fence after any (optional) fence of guest's host */
 
     /* Order/imply other actions relative to pseudo-fence as with real fence */
     stonith_constraints(node, stonith_op, data_set);
     order_actions(stonith_op, done, pe_order_implies_then);
 }
 
 /*
  * Create dependencies for stonith and shutdown operations
  */
 gboolean
 stage6(pe_working_set_t * data_set)
 {
     action_t *dc_down = NULL;
     action_t *dc_fence = NULL;
     action_t *stonith_op = NULL;
     action_t *last_stonith = NULL;
     gboolean integrity_lost = FALSE;
     action_t *all_stopped = get_pseudo_op(ALL_STOPPED, data_set);
     action_t *done = get_pseudo_op(STONITH_DONE, data_set);
     gboolean need_stonith = TRUE;
     GListPtr gIter;
     GListPtr stonith_ops = NULL;
 
     /* Remote ordering constraints need to happen prior to calculate
      * fencing because it is one more place we will mark the node as
      * dirty.
      *
      * A nice side-effect of doing it first is that we can remove a
      * bunch of special logic from apply_*_ordering() because its
      * already part of pe_fence_node()
      */
     crm_trace("Creating remote ordering constraints");
     apply_remote_node_ordering(data_set);
 
     crm_trace("Processing fencing and shutdown cases");
     if (any_managed_resources(data_set) == FALSE) {
         crm_notice("Delaying fencing operations until there are resources to manage");
         need_stonith = FALSE;
     }
 
     /* Check each node for stonith/shutdown */
     for (gIter = data_set->nodes; gIter != NULL; gIter = gIter->next) {
         node_t *node = (node_t *) gIter->data;
 
         /* Guest nodes are "fenced" by recovering their container resource,
          * so handle them separately.
          */
         if (is_container_remote_node(node)) {
             if (node->details->remote_requires_reset && need_stonith) {
                 fence_guest(node, done, data_set);
             }
             continue;
         }
 
         stonith_op = NULL;
 
         if (node->details->unclean
             && need_stonith && pe_can_fence(data_set, node)) {
 
             pe_warn("Scheduling Node %s for STONITH", node->details->uname);
 
             stonith_op = pe_fence_op(node, NULL, FALSE, data_set);
 
             stonith_constraints(node, stonith_op, data_set);
 
             if (node->details->is_dc) {
                 dc_down = stonith_op;
                 dc_fence = stonith_op;
 
             } else if (is_set(data_set->flags, pe_flag_concurrent_fencing) == FALSE) {
                 if (last_stonith) {
                     order_actions(last_stonith, stonith_op, pe_order_optional);
                 }
                 last_stonith = stonith_op;
 
             } else {
                 order_actions(stonith_op, done, pe_order_implies_then);
                 stonith_ops = g_list_append(stonith_ops, stonith_op);
             }
 
         } else if (node->details->online && node->details->shutdown &&
                 /* TODO define what a shutdown op means for a remote node.
                  * For now we do not send shutdown operations for remote nodes, but
                  * if we can come up with a good use for this in the future, we will. */
                     is_remote_node(node) == FALSE) {
 
             action_t *down_op = NULL;
 
             crm_notice("Scheduling Node %s for shutdown", node->details->uname);
 
             down_op = custom_action(NULL, crm_strdup_printf("%s-%s", CRM_OP_SHUTDOWN, node->details->uname),
                                     CRM_OP_SHUTDOWN, node, FALSE, TRUE, data_set);
 
             shutdown_constraints(node, down_op, data_set);
             add_hash_param(down_op->meta, XML_ATTR_TE_NOWAIT, XML_BOOLEAN_TRUE);
 
             if (node->details->is_dc) {
                 dc_down = down_op;
             }
         }
 
         if (node->details->unclean && stonith_op == NULL) {
             integrity_lost = TRUE;
             pe_warn("Node %s is unclean!", node->details->uname);
         }
     }
 
     if (integrity_lost) {
         if (is_set(data_set->flags, pe_flag_stonith_enabled) == FALSE) {
             pe_warn("YOUR RESOURCES ARE NOW LIKELY COMPROMISED");
             pe_err("ENABLE STONITH TO KEEP YOUR RESOURCES SAFE");
 
         } else if (is_set(data_set->flags, pe_flag_have_quorum) == FALSE) {
             crm_notice("Cannot fence unclean nodes until quorum is"
                        " attained (or no-quorum-policy is set to ignore)");
         }
     }
 
     if (dc_down != NULL) {
         GListPtr gIter = NULL;
 
         crm_trace("Ordering shutdowns before %s on %s (DC)",
                   dc_down->task, dc_down->node->details->uname);
 
         add_hash_param(dc_down->meta, XML_ATTR_TE_NOWAIT, XML_BOOLEAN_TRUE);
 
         for (gIter = data_set->actions; gIter != NULL; gIter = gIter->next) {
             action_t *node_stop = (action_t *) gIter->data;
 
             if (safe_str_neq(CRM_OP_SHUTDOWN, node_stop->task)) {
                 continue;
             } else if (node_stop->node->details->is_dc) {
                 continue;
             }
 
             crm_debug("Ordering shutdown on %s before %s on %s",
                       node_stop->node->details->uname,
                       dc_down->task, dc_down->node->details->uname);
 
             order_actions(node_stop, dc_down, pe_order_optional);
         }
 
         if (last_stonith) {
             if (dc_down != last_stonith) {
                 order_actions(last_stonith, dc_down, pe_order_optional);
             }
 
         } else {
             GListPtr gIter2 = NULL;
 
             for (gIter2 = stonith_ops; gIter2 != NULL; gIter2 = gIter2->next) {
                 stonith_op = (action_t *) gIter2->data;
 
                 if (dc_down != stonith_op) {
                     order_actions(stonith_op, dc_down, pe_order_optional);
                 }
             }
         }
     }
 
 
     if (dc_fence) {
         order_actions(dc_down, done, pe_order_implies_then);
 
     } else if (last_stonith) {
         order_actions(last_stonith, done, pe_order_implies_then);
     }
 
     order_actions(done, all_stopped, pe_order_implies_then);
 
     g_list_free(stonith_ops);
     return TRUE;
 }
 
 /*
  * Determine the sets of independent actions and the correct order for the
  *  actions in each set.
  *
  * Mark dependencies of un-runnable actions un-runnable
  *
  */
 static GListPtr
 find_actions_by_task(GListPtr actions, resource_t * rsc, const char *original_key)
 {
     GListPtr list = NULL;
 
     list = find_actions(actions, original_key, NULL);
     if (list == NULL) {
         /* we're potentially searching a child of the original resource */
         char *key = NULL;
         char *tmp = NULL;
         char *task = NULL;
         int interval = 0;
 
         if (parse_op_key(original_key, &tmp, &task, &interval)) {
             key = generate_op_key(rsc->id, task, interval);
             /* crm_err("looking up %s instead of %s", key, original_key); */
             /* slist_iter(action, action_t, actions, lpc, */
             /*         crm_err("  - %s", action->uuid)); */
             list = find_actions(actions, key, NULL);
 
         } else {
             crm_err("search key: %s", original_key);
         }
 
         free(key);
         free(tmp);
         free(task);
     }
 
     return list;
 }
 
 static void
 rsc_order_then(action_t * lh_action, resource_t * rsc, order_constraint_t * order)
 {
     GListPtr gIter = NULL;
     GListPtr rh_actions = NULL;
     action_t *rh_action = NULL;
     enum pe_ordering type = order->type;
 
     CRM_CHECK(rsc != NULL, return);
     CRM_CHECK(order != NULL, return);
 
     rh_action = order->rh_action;
     crm_trace("Processing RH of ordering constraint %d", order->id);
 
     if (rh_action != NULL) {
         rh_actions = g_list_prepend(NULL, rh_action);
 
     } else if (rsc != NULL) {
         rh_actions = find_actions_by_task(rsc->actions, rsc, order->rh_action_task);
     }
 
     if (rh_actions == NULL) {
         pe_rsc_trace(rsc, "No RH-Side (%s/%s) found for constraint..."
                      " ignoring", rsc->id, order->rh_action_task);
         if (lh_action) {
             pe_rsc_trace(rsc, "LH-Side was: %s", lh_action->uuid);
         }
         return;
     }
 
     if (lh_action && lh_action->rsc == rsc && is_set(lh_action->flags, pe_action_dangle)) {
         pe_rsc_trace(rsc, "Detected dangling operation %s -> %s", lh_action->uuid,
                      order->rh_action_task);
         clear_bit(type, pe_order_implies_then);
     }
 
     gIter = rh_actions;
     for (; gIter != NULL; gIter = gIter->next) {
         action_t *rh_action_iter = (action_t *) gIter->data;
 
         if (lh_action) {
             order_actions(lh_action, rh_action_iter, type);
 
         } else if (type & pe_order_implies_then) {
             update_action_flags(rh_action_iter, pe_action_runnable | pe_action_clear, __FUNCTION__, __LINE__);
             crm_warn("Unrunnable %s 0x%.6x", rh_action_iter->uuid, type);
         } else {
             crm_warn("neither %s 0x%.6x", rh_action_iter->uuid, type);
         }
     }
 
     g_list_free(rh_actions);
 }
 
 static void
 rsc_order_first(resource_t * lh_rsc, order_constraint_t * order, pe_working_set_t * data_set)
 {
     GListPtr gIter = NULL;
     GListPtr lh_actions = NULL;
     action_t *lh_action = order->lh_action;
     resource_t *rh_rsc = order->rh_rsc;
 
     crm_trace("Processing LH of ordering constraint %d", order->id);
     CRM_ASSERT(lh_rsc != NULL);
 
     if (lh_action != NULL) {
         lh_actions = g_list_prepend(NULL, lh_action);
 
     } else if (lh_action == NULL) {
         lh_actions = find_actions_by_task(lh_rsc->actions, lh_rsc, order->lh_action_task);
     }
 
     if (lh_actions == NULL && lh_rsc != rh_rsc) {
         char *key = NULL;
         char *rsc_id = NULL;
         char *op_type = NULL;
         int interval = 0;
 
         parse_op_key(order->lh_action_task, &rsc_id, &op_type, &interval);
         key = generate_op_key(lh_rsc->id, op_type, interval);
 
         if (lh_rsc->fns->state(lh_rsc, TRUE) == RSC_ROLE_STOPPED && safe_str_eq(op_type, RSC_STOP)) {
             free(key);
             pe_rsc_trace(lh_rsc, "No LH-Side (%s/%s) found for constraint %d with %s - ignoring",
                          lh_rsc->id, order->lh_action_task, order->id, order->rh_action_task);
 
         } else if (lh_rsc->fns->state(lh_rsc, TRUE) == RSC_ROLE_SLAVE && safe_str_eq(op_type, RSC_DEMOTE)) {
             free(key);
             pe_rsc_trace(lh_rsc, "No LH-Side (%s/%s) found for constraint %d with %s - ignoring",
                          lh_rsc->id, order->lh_action_task, order->id, order->rh_action_task);
 
         } else {
             pe_rsc_trace(lh_rsc, "No LH-Side (%s/%s) found for constraint %d with %s - creating",
                          lh_rsc->id, order->lh_action_task, order->id, order->rh_action_task);
             lh_action = custom_action(lh_rsc, key, op_type, NULL, TRUE, TRUE, data_set);
             lh_actions = g_list_prepend(NULL, lh_action);
         }
 
         free(op_type);
         free(rsc_id);
     }
 
     gIter = lh_actions;
     for (; gIter != NULL; gIter = gIter->next) {
         action_t *lh_action_iter = (action_t *) gIter->data;
 
         if (rh_rsc == NULL && order->rh_action) {
             rh_rsc = order->rh_action->rsc;
         }
         if (rh_rsc) {
             rsc_order_then(lh_action_iter, rh_rsc, order);
 
         } else if (order->rh_action) {
             order_actions(lh_action_iter, order->rh_action, order->type);
         }
     }
 
     g_list_free(lh_actions);
 }
 
 extern gboolean update_action(action_t * action);
 extern void update_colo_start_chain(action_t * action);
 
 enum remote_connection_state 
 {
     remote_state_unknown = 0,
     remote_state_alive = 1,
     remote_state_resting = 2,
     remote_state_failed = 3,
     remote_state_stopped = 4
 };
 
 static int
 is_recurring_action(action_t *action) 
 {
     const char *interval_s = g_hash_table_lookup(action->meta, XML_LRM_ATTR_INTERVAL);
     int interval = crm_parse_int(interval_s, "0");
     if(interval > 0) {
         return TRUE;
     }
     return FALSE;
 }
 
 static void
 apply_container_ordering(action_t *action, pe_working_set_t *data_set)
 {
     /* VMs are also classified as containers for these purposes... in
      * that they both involve a 'thing' running on a real or remote
      * cluster node.
      *
      * This allows us to be smarter about the type and extent of
      * recovery actions required in various scenarios
      */
     resource_t *remote_rsc = NULL;
     resource_t *container = NULL;
     enum action_tasks task = text2task(action->task);
 
     if (action->rsc == NULL) {
         return;
     }
 
     CRM_ASSERT(action->node);
     CRM_ASSERT(is_remote_node(action->node));
 
     remote_rsc = action->node->details->remote_rsc;
     CRM_ASSERT(remote_rsc);
 
     container = remote_rsc->container;
     CRM_ASSERT(container);
 
     if(is_set(container->flags, pe_rsc_failed)) {
         pe_fence_node(data_set, action->node, "container failed");
     }
 
     crm_trace("Order %s action %s relative to %s%s for %s%s",
               action->task, action->uuid,
               is_set(remote_rsc->flags, pe_rsc_failed)? "failed " : "",
               remote_rsc->id,
               is_set(container->flags, pe_rsc_failed)? "failed " : "",
               container->id);
 
     switch (task) {
         case start_rsc:
         case action_promote:
             /* Force resource recovery if the container is recovered */
             custom_action_order(container, generate_op_key(container->id, RSC_START, 0), NULL,
                                 action->rsc, NULL, action,
                                 pe_order_preserve | pe_order_implies_then | pe_order_runnable_left, data_set);
 
             /* Wait for the connection resource to be up too */
             custom_action_order(remote_rsc, generate_op_key(remote_rsc->id, RSC_START, 0), NULL,
                                 action->rsc, NULL, action,
                                 pe_order_preserve | pe_order_runnable_left, data_set);
             break;
         case stop_rsc:
             if(is_set(container->flags, pe_rsc_failed)) {
                 /* When the container representing a guest node fails,
                  * the stop action for all the resources living in
                  * that container is implied by the container
                  * stopping. This is similar to how fencing operations
                  * work for cluster nodes.
                  */
             } else {
                 /* Otherwise, ensure the operation happens before the connection is brought down */
                 custom_action_order(action->rsc, NULL, action,
                                     remote_rsc, generate_op_key(remote_rsc->id, RSC_STOP, 0), NULL,
                                     pe_order_preserve, data_set);
             }
             break;
         case action_demote:
             if(is_set(container->flags, pe_rsc_failed)) {
                 /* Just like a stop, the demote is implied by the
                  * container having failed/stopped
                  *
                  * If we really wanted to we would order the demote
                  * after the stop, IFF the containers current role was
                  * stopped (otherwise we re-introduce an ordering
                  * loop)
                  */
 
             } else {
                 /* Otherwise, ensure the operation happens before the connection is brought down */
                 custom_action_order(action->rsc, NULL, action,
                                     remote_rsc, generate_op_key(remote_rsc->id, RSC_STOP, 0), NULL,
                                     pe_order_preserve, data_set);
             }
             break;
         default:
             /* Wait for the connection resource to be up */
             if (is_recurring_action(action)) {
                 /* In case we ever get the recovery logic wrong, force
                  * recurring monitors to be restarted, even if just
                  * the connection was re-established
                  */
-                custom_action_order(remote_rsc, generate_op_key(remote_rsc->id, RSC_START, 0), NULL,
-                                    action->rsc, NULL, action,
-                                    pe_order_preserve | pe_order_runnable_left | pe_order_implies_then, data_set);
+                if(task != no_action) {
+                    custom_action_order(remote_rsc, generate_op_key(remote_rsc->id, RSC_START, 0), NULL,
+                                        action->rsc, NULL, action,
+                                        pe_order_preserve | pe_order_runnable_left | pe_order_implies_then, data_set);
+                }
             } else {
                 custom_action_order(remote_rsc, generate_op_key(remote_rsc->id, RSC_START, 0), NULL,
                                     action->rsc, NULL, action,
                                     pe_order_preserve | pe_order_runnable_left, data_set);
             }
             break;
     }
 }
 
 static void
 apply_remote_ordering(action_t *action, pe_working_set_t *data_set)
 {
     resource_t *remote_rsc = NULL;
     node_t *cluster_node = NULL;
     enum action_tasks task = text2task(action->task);
     enum remote_connection_state state = remote_state_unknown;
     enum pe_ordering order_opts = pe_order_none;
 
     if (action->rsc == NULL) {
         return;
     }
 
     CRM_ASSERT(action->node);
     CRM_ASSERT(is_remote_node(action->node));
 
     remote_rsc = action->node->details->remote_rsc;
     CRM_ASSERT(remote_rsc);
 
     if(remote_rsc->running_on) {
         cluster_node = remote_rsc->running_on->data;
     }
 
     /* If the cluster node the remote connection resource resides on
      * is unclean or went offline, we can't process any operations
      * on that remote node until after it starts elsewhere.
      */
     if(remote_rsc->next_role == RSC_ROLE_STOPPED || remote_rsc->allocated_to == NULL) {
         /* There is nowhere left to run the connection resource,
          * and the resource is in a failed state (either directly
          * or because it is located on a failed node).
          *
          * If there are any resources known to be active on it (stop),
          * or if there are resources in an unknown state (probe), we
          * must assume the worst and fence it.
          */
         if (is_set(remote_rsc->flags, pe_rsc_failed)) {
             state = remote_state_failed;
         } else if(cluster_node && cluster_node->details->unclean) {
             state = remote_state_failed;
         } else {
             state = remote_state_stopped;
         }
 
     } else if (cluster_node == NULL) {
         /* Connection is recoverable but not currently running anywhere, see if we can recover it first */
         state = remote_state_unknown;
 
     } else if(cluster_node->details->unclean == TRUE
               || cluster_node->details->online == FALSE) {
         /* Connection is running on a dead node, see if we can recover it first */
         state = remote_state_resting;
 
     } else if (g_list_length(remote_rsc->running_on) > 1
                && remote_rsc->partial_migration_source
                && remote_rsc->partial_migration_target) {
         /* We're in the middle of migrating a connection resource,
          * wait until after the resource migrates before performing
          * any actions.
          */
         state = remote_state_resting;
 
     } else {
         state = remote_state_alive;
     }
 
     crm_trace("Order %s action %s relative to %s%s (state %d)",
               action->task, action->uuid,
               is_set(remote_rsc->flags, pe_rsc_failed)? "failed " : "",
               remote_rsc->id, state);
     switch (task) {
         case start_rsc:
         case action_promote:
             /* This as an internally generated constraint exempt from
              * user constraint prohibitions, and this action isn't runnable
              * if the connection start isn't runnable.
              */
             order_opts = pe_order_preserve | pe_order_runnable_left;
 
             if (state == remote_state_failed) {
                 /* Force recovery, by making this action required */
                 order_opts |= pe_order_implies_then;
             }
 
             /* Ensure connection is up before running this action */
             custom_action_order(remote_rsc,
                                 generate_op_key(remote_rsc->id, RSC_START, 0),
                                 NULL, action->rsc, NULL, action, order_opts,
                                 data_set);
             break;
 
         case stop_rsc:
             /* Handle special case with remote node where stop actions need to be
              * ordered after the connection resource starts somewhere else.
              */
             if(state == remote_state_resting) {
                 /* Wait for the connection resource to be up and assume everything is as we left it */
                 custom_action_order(remote_rsc, generate_op_key(remote_rsc->id, RSC_START, 0), NULL,
                                     action->rsc, NULL, action,
                                     pe_order_preserve | pe_order_runnable_left, data_set);
 
             } else {
                 if(state == remote_state_failed) {
                     /* We would only be here if the resource is
                      * running on the remote node.  Since we have no
                      * way to stop it, it is necessary to fence the
                      * node.
                      */
                     pe_fence_node(data_set, action->node, "resources are active and the connection is unrecoverable");
                 }
 
                 custom_action_order(action->rsc, NULL, action,
                                     remote_rsc, generate_op_key(remote_rsc->id, RSC_STOP, 0), NULL,
                                     pe_order_preserve | pe_order_implies_first, data_set);
             }
             break;
 
         case action_demote:
             /* Only order this demote relative to the connection start if the
              * connection isn't being torn down. Otherwise, the demote would be
              * blocked because the connection start would not be allowed.
              */
             if(state == remote_state_resting || state == remote_state_unknown) {
                 custom_action_order(remote_rsc, generate_op_key(remote_rsc->id, RSC_START, 0), NULL,
                                     action->rsc, NULL, action,
                                     pe_order_preserve, data_set);
             } /* Otherwise we can rely on the stop ordering */
             break;
 
         default:
             /* Wait for the connection resource to be up */
             if (is_recurring_action(action)) {
                 /* In case we ever get the recovery logic wrong, force
                  * recurring monitors to be restarted, even if just
                  * the connection was re-established
                  */
                 custom_action_order(remote_rsc, generate_op_key(remote_rsc->id, RSC_START, 0), NULL,
                                     action->rsc, NULL, action,
                                     pe_order_preserve | pe_order_runnable_left | pe_order_implies_then, data_set);
 
             } else {
                 if(task == monitor_rsc && state == remote_state_failed) {
                     /* We would only be here if we do not know the
                      * state of the resource on the remote node.
                      * Since we have no way to find out, it is
                      * necessary to fence the node.
                      */
                     pe_fence_node(data_set, action->node, "resources are in an unknown state and the connection is unrecoverable");
                 }
 
                 if(cluster_node && state == remote_state_stopped) {
                     /* The connection is currently up, but is going
                      * down permanently.
                      *
                      * Make sure we check services are actually
                      * stopped _before_ we let the connection get
                      * closed
                      */
                     custom_action_order(action->rsc, NULL, action,
                                         remote_rsc, generate_op_key(remote_rsc->id, RSC_STOP, 0), NULL,
                                         pe_order_preserve | pe_order_runnable_left, data_set);
 
                 } else {
                     custom_action_order(remote_rsc, generate_op_key(remote_rsc->id, RSC_START, 0), NULL,
                                         action->rsc, NULL, action,
                                         pe_order_preserve | pe_order_runnable_left, data_set);
                 }
             }
             break;
     }
 }
 
 static void
 apply_remote_node_ordering(pe_working_set_t *data_set)
 {
     GListPtr gIter = data_set->actions;
 
     if (is_set(data_set->flags, pe_flag_have_remote_nodes) == FALSE) {
         return;
     }
 
     for (; gIter != NULL; gIter = gIter->next) {
         action_t *action = (action_t *) gIter->data;
 
         if (action->rsc == NULL) {
             continue;
         }
 
         /* Special case. */
         if (action->rsc &&
             action->rsc->is_remote_node &&
             safe_str_eq(action->task, CRM_OP_CLEAR_FAILCOUNT)) {
 
             /* If we are clearing the failcount of an actual remote node
              * connection resource, then make sure this happens before allowing
              * the connection to start if we are planning on starting the
              * connection during this transition.
              */
             custom_action_order(action->rsc,
                 NULL,
                 action,
                 action->rsc,
                 generate_op_key(action->rsc->id, RSC_START, 0),
                 NULL,
                 pe_order_optional,
                 data_set);
 
             continue;
         }
 
         /* If the action occurs on a Pacemaker Remote node, create
          * ordering constraints that guarantee the action occurs while the node
          * is active (after start, before stop ... things like that).
          */
         if (action->node == NULL ||
             is_remote_node(action->node) == FALSE ||
             action->node->details->remote_rsc == NULL ||
             is_set(action->flags, pe_action_pseudo)) {
             crm_trace("Nothing required for %s", action->uuid);
 
         } else if(action->node->details->remote_rsc->container) {
             apply_container_ordering(action, data_set);
 
         } else {
             apply_remote_ordering(action, data_set);
         }
     }
 }
 
 static void
 order_probes(pe_working_set_t * data_set) 
 {
 #if 0
     GListPtr gIter = NULL;
 
     for (gIter = data_set->resources; gIter != NULL; gIter = gIter->next) {
         resource_t *rsc = (resource_t *) gIter->data;
 
         /* Given "A then B", we would prefer to wait for A to be
          * started before probing B.
          *
          * If A was a filesystem on which the binaries and data for B
          * lived, it would have been useful if the author of B's agent
          * could assume that A is running before B.monitor will be
          * called.
          *
          * However we can't _only_ probe once A is running, otherwise
          * we'd not detect the state of B if A could not be started
          * for some reason.
          *
          * In practice however, we cannot even do an opportunistic
          * version of this because B may be moving:
          *
          *   B.probe -> B.start
          *   B.probe -> B.stop
          *   B.stop -> B.start
          *   A.stop -> A.start
          *   A.start -> B.probe
          *
          * So far so good, but if we add the result of this code:
          *
          *   B.stop -> A.stop
          *
          * Then we get a loop:
          *
          *   B.probe -> B.stop -> A.stop -> A.start -> B.probe
          *
          * We could kill the 'B.probe -> B.stop' dependency, but that
          * could mean stopping B "too" soon, because B.start must wait
          * for the probes to complete.
          *
          * Another option is to allow it only if A is a non-unique
          * clone with clone-max == node-max (since we'll never be
          * moving it).  However, we could still be stopping one
          * instance at the same time as starting another.
 
          * The complexity of checking for allowed conditions combined
          * with the ever narrowing usecase suggests that this code
          * should remain disabled until someone gets smarter.
          */
         action_t *start = NULL;
         GListPtr actions = NULL;
         GListPtr probes = NULL;
         char *key = NULL;
 
         key = start_key(rsc);
         actions = find_actions(rsc->actions, key, NULL);
         free(key);
 
         if (actions) {
             start = actions->data;
             g_list_free(actions);
         }
 
         if(start == NULL) {
             crm_err("No start action for %s", rsc->id);
             continue;
         }
 
         key = generate_op_key(rsc->id, CRMD_ACTION_STATUS, 0);
         probes = find_actions(rsc->actions, key, NULL);
         free(key);
 
         for (actions = start->actions_before; actions != NULL; actions = actions->next) {
             action_wrapper_t *before = (action_wrapper_t *) actions->data;
 
             GListPtr pIter = NULL;
             action_t *first = before->action;
             resource_t *first_rsc = first->rsc;
 
             if(first->required_runnable_before) {
                 GListPtr clone_actions = NULL;
                 for (clone_actions = first->actions_before; clone_actions != NULL; clone_actions = clone_actions->next) {
                     before = (action_wrapper_t *) clone_actions->data;
 
                     crm_trace("Testing %s -> %s (%p) for %s", first->uuid, before->action->uuid, before->action->rsc, start->uuid);
 
                     CRM_ASSERT(before->action->rsc);
                     first_rsc = before->action->rsc;
                     break;
                 }
 
             } else if(safe_str_neq(first->task, RSC_START)) {
                 crm_trace("Not a start op %s for %s", first->uuid, start->uuid);
             }
 
             if(first_rsc == NULL) {
                 continue;
 
             } else if(uber_parent(first_rsc) == uber_parent(start->rsc)) {
                 crm_trace("Same parent %s for %s", first_rsc->id, start->uuid);
                 continue;
 
             } else if(FALSE && pe_rsc_is_clone(uber_parent(first_rsc)) == FALSE) {
                 crm_trace("Not a clone %s for %s", first_rsc->id, start->uuid);
                 continue;
             }
 
             crm_err("Appplying %s before %s %d", first->uuid, start->uuid, uber_parent(first_rsc)->variant);
 
             for (pIter = probes; pIter != NULL; pIter = pIter->next) {
                 action_t *probe = (action_t *) pIter->data;
 
                 crm_err("Ordering %s before %s", first->uuid, probe->uuid);
                 order_actions(first, probe, pe_order_optional);
             }
         }
     }
 #endif
 }
 
 gboolean
 stage7(pe_working_set_t * data_set)
 {
     GListPtr gIter = NULL;
 
     crm_trace("Applying ordering constraints");
 
     /* Don't ask me why, but apparently they need to be processed in
      * the order they were created in... go figure
      *
      * Also g_list_append() has horrendous performance characteristics
      * So we need to use g_list_prepend() and then reverse the list here
      */
     data_set->ordering_constraints = g_list_reverse(data_set->ordering_constraints);
 
     for (gIter = data_set->ordering_constraints; gIter != NULL; gIter = gIter->next) {
         order_constraint_t *order = (order_constraint_t *) gIter->data;
         resource_t *rsc = order->lh_rsc;
 
         crm_trace("Applying ordering constraint: %d", order->id);
 
         if (rsc != NULL) {
             crm_trace("rsc_action-to-*");
             rsc_order_first(rsc, order, data_set);
             continue;
         }
 
         rsc = order->rh_rsc;
         if (rsc != NULL) {
             crm_trace("action-to-rsc_action");
             rsc_order_then(order->lh_action, rsc, order);
 
         } else {
             crm_trace("action-to-action");
             order_actions(order->lh_action, order->rh_action, order->type);
         }
     }
 
     for (gIter = data_set->actions; gIter != NULL; gIter = gIter->next) {
         action_t *action = (action_t *) gIter->data;
 
         update_colo_start_chain(action);
     }
 
     crm_trace("Ordering probes");
     order_probes(data_set);
 
     crm_trace("Updating %d actions", g_list_length(data_set->actions));
     for (gIter = data_set->actions; gIter != NULL; gIter = gIter->next) {
         action_t *action = (action_t *) gIter->data;
 
         update_action(action);
     }
 
     LogNodeActions(data_set, FALSE);
     for (gIter = data_set->resources; gIter != NULL; gIter = gIter->next) {
         resource_t *rsc = (resource_t *) gIter->data;
 
         LogActions(rsc, data_set, FALSE);
     }
     return TRUE;
 }
 
 int transition_id = -1;
 
 /*
  * Create a dependency graph to send to the transitioner (via the CRMd)
  */
 gboolean
 stage8(pe_working_set_t * data_set)
 {
     GListPtr gIter = NULL;
     const char *value = NULL;
 
     transition_id++;
     crm_trace("Creating transition graph %d.", transition_id);
 
     data_set->graph = create_xml_node(NULL, XML_TAG_GRAPH);
 
     value = pe_pref(data_set->config_hash, "cluster-delay");
     crm_xml_add(data_set->graph, "cluster-delay", value);
 
     value = pe_pref(data_set->config_hash, "stonith-timeout");
     crm_xml_add(data_set->graph, "stonith-timeout", value);
 
     crm_xml_add(data_set->graph, "failed-stop-offset", "INFINITY");
 
     if (is_set(data_set->flags, pe_flag_start_failure_fatal)) {
         crm_xml_add(data_set->graph, "failed-start-offset", "INFINITY");
     } else {
         crm_xml_add(data_set->graph, "failed-start-offset", "1");
     }
 
     value = pe_pref(data_set->config_hash, "batch-limit");
     crm_xml_add(data_set->graph, "batch-limit", value);
 
     crm_xml_add_int(data_set->graph, "transition_id", transition_id);
 
     value = pe_pref(data_set->config_hash, "migration-limit");
     if (crm_int_helper(value, NULL) > 0) {
         crm_xml_add(data_set->graph, "migration-limit", value);
     }
 
 /* errors...
    slist_iter(action, action_t, action_list, lpc,
    if(action->optional == FALSE && action->runnable == FALSE) {
    print_action("Ignoring", action, TRUE);
    }
    );
 */
 
     gIter = data_set->resources;
     for (; gIter != NULL; gIter = gIter->next) {
         resource_t *rsc = (resource_t *) gIter->data;
 
         pe_rsc_trace(rsc, "processing actions for rsc=%s", rsc->id);
         rsc->cmds->expand(rsc, data_set);
     }
 
     crm_log_xml_trace(data_set->graph, "created resource-driven action list");
 
     /* pseudo action to distribute list of nodes with maintenance state update */
     add_maintenance_update(data_set);
 
     /* catch any non-resource specific actions */
     crm_trace("processing non-resource actions");
 
     gIter = data_set->actions;
     for (; gIter != NULL; gIter = gIter->next) {
         action_t *action = (action_t *) gIter->data;
 
         if (action->rsc
             && action->node
             && action->node->details->shutdown
             && is_not_set(action->rsc->flags, pe_rsc_maintenance)
             && is_not_set(action->flags, pe_action_optional)
             && is_not_set(action->flags, pe_action_runnable)
             && crm_str_eq(action->task, RSC_STOP, TRUE)
             ) {
             /* Eventually we should just ignore the 'fence' case
              * But for now it's the best way to detect (in CTS) when
              * CIB resource updates are being lost
              */
             if (is_set(data_set->flags, pe_flag_have_quorum)
                 || data_set->no_quorum_policy == no_quorum_ignore) {
                 crm_crit("Cannot %s node '%s' because of %s:%s%s (%s)",
                          action->node->details->unclean ? "fence" : "shut down",
                          action->node->details->uname, action->rsc->id,
                          is_not_set(action->rsc->flags, pe_rsc_managed) ? " unmanaged" : " blocked",
                          is_set(action->rsc->flags, pe_rsc_failed) ? " failed" : "",
                          action->uuid);
             }
         }
 
         graph_element_from_action(action, data_set);
     }
 
     crm_log_xml_trace(data_set->graph, "created generic action list");
     crm_trace("Created transition graph %d.", transition_id);
 
     return TRUE;
 }
 
 void
 LogNodeActions(pe_working_set_t * data_set, gboolean terminal)
 {
     GListPtr gIter = NULL;
 
     for (gIter = data_set->actions; gIter != NULL; gIter = gIter->next) {
         char *node_name = NULL;
         const char *task = NULL;
         action_t *action = (action_t *) gIter->data;
 
         if (action->rsc != NULL) {
             continue;
         }
 
         if (is_container_remote_node(action->node)) {
             node_name = crm_strdup_printf("%s (resource: %s)", action->node->details->uname, action->node->details->remote_rsc->container->id);
         } else if(action->node) {
             node_name = crm_strdup_printf("%s", action->node->details->uname);
         }
 
         if (safe_str_eq(action->task, CRM_OP_SHUTDOWN)) {
             task = "Shutdown";
         } else if (safe_str_eq(action->task, CRM_OP_FENCE)) {
             task = "Fence";
         }
 
         if(task == NULL) {
             /* Nothing to report */
         } else if(terminal) {
             printf(" * %s %s\n", task, node_name);
         } else {
             crm_notice(" * %s %s\n", task, node_name);
         }
 
         free(node_name);
     }
 }
 
 void
 cleanup_alloc_calculations(pe_working_set_t * data_set)
 {
     if (data_set == NULL) {
         return;
     }
 
     crm_trace("deleting %d order cons: %p",
               g_list_length(data_set->ordering_constraints), data_set->ordering_constraints);
     pe_free_ordering(data_set->ordering_constraints);
     data_set->ordering_constraints = NULL;
 
     crm_trace("deleting %d node cons: %p",
               g_list_length(data_set->placement_constraints), data_set->placement_constraints);
     pe_free_rsc_to_node(data_set->placement_constraints);
     data_set->placement_constraints = NULL;
 
     crm_trace("deleting %d inter-resource cons: %p",
               g_list_length(data_set->colocation_constraints), data_set->colocation_constraints);
     g_list_free_full(data_set->colocation_constraints, free);
     data_set->colocation_constraints = NULL;
 
     crm_trace("deleting %d ticket deps: %p",
               g_list_length(data_set->ticket_constraints), data_set->ticket_constraints);
     g_list_free_full(data_set->ticket_constraints, free);
     data_set->ticket_constraints = NULL;
 
     cleanup_calculations(data_set);
 }
diff --git a/pengine/allocate.h b/pengine/allocate.h
index d89943dd14..9a30b80ef4 100644
--- a/pengine/allocate.h
+++ b/pengine/allocate.h
@@ -1,184 +1,186 @@
 /* 
  * Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
  * 
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
  * License as published by the Free Software Foundation; either
  * version 2 of the License, or (at your option) any later version.
  * 
  * This software is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  * General Public License for more details.
  * 
  * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  */
 #ifndef CRM_PENGINE_COMPLEX_ALLOC__H
 #  define CRM_PENGINE_COMPLEX_ALLOC__H
 
 #  include <glib.h>
 #  include <crm/common/xml.h>
 #  include <crm/pengine/status.h>
 #  include <crm/pengine/complex.h>
 #  include <crm/pengine/internal.h>
 #  include <pengine.h>
 
 struct resource_alloc_functions_s {
     GHashTable *(*merge_weights) (resource_t *, const char *, GHashTable *, const char *, float,
                                   enum pe_weights);
     node_t *(*allocate) (resource_t *, node_t *, pe_working_set_t *);
     void (*create_actions) (resource_t *, pe_working_set_t *);
      gboolean(*create_probe) (resource_t *, node_t *, action_t *, gboolean, pe_working_set_t *);
     void (*internal_constraints) (resource_t *, pe_working_set_t *);
 
     void (*rsc_colocation_lh) (resource_t *, resource_t *, rsc_colocation_t *);
     void (*rsc_colocation_rh) (resource_t *, resource_t *, rsc_colocation_t *);
 
     void (*rsc_location) (resource_t *, rsc_to_node_t *);
 
     enum pe_action_flags (*action_flags) (action_t *, node_t *);
     enum pe_graph_flags (*update_actions) (action_t *, action_t *, node_t *, enum pe_action_flags,
                                            enum pe_action_flags, enum pe_ordering);
 
     void (*expand) (resource_t *, pe_working_set_t *);
     void (*append_meta) (resource_t * rsc, xmlNode * xml);
 };
 
 extern GHashTable *rsc_merge_weights(resource_t * rsc, const char *rhs, GHashTable * nodes,
                                      const char *attr, float factor, enum pe_weights flags);
 
 extern GHashTable *clone_merge_weights(resource_t * rsc, const char *rhs, GHashTable * nodes,
                                        const char *attr, float factor, enum pe_weights flags);
 
 extern GHashTable *container_merge_weights(resource_t * rsc, const char *rhs, GHashTable * nodes,
                                        const char *attr, float factor, enum pe_weights flags);
 
 extern GHashTable *master_merge_weights(resource_t * rsc, const char *rhs, GHashTable * nodes,
                                         const char *attr, float factor, enum pe_weights flags);
 
 extern GHashTable *native_merge_weights(resource_t * rsc, const char *rhs, GHashTable * nodes,
                                         const char *attr, float factor, enum pe_weights flags);
 
 extern GHashTable *group_merge_weights(resource_t * rsc, const char *rhs, GHashTable * nodes,
                                        const char *attr, float factor, enum pe_weights flags);
 
 extern node_t *native_color(resource_t * rsc, node_t * preferred, pe_working_set_t * data_set);
 extern void native_create_actions(resource_t * rsc, pe_working_set_t * data_set);
 extern void native_internal_constraints(resource_t * rsc, pe_working_set_t * data_set);
 extern void native_rsc_colocation_lh(resource_t * lh_rsc, resource_t * rh_rsc,
                                      rsc_colocation_t * constraint);
 extern void native_rsc_colocation_rh(resource_t * lh_rsc, resource_t * rh_rsc,
                                      rsc_colocation_t * constraint);
 extern void rsc_ticket_constraint(resource_t * lh_rsc, rsc_ticket_t * rsc_ticket,
                                   pe_working_set_t * data_set);
 extern enum pe_action_flags native_action_flags(action_t * action, node_t * node);
 
 extern void native_rsc_location(resource_t * rsc, rsc_to_node_t * constraint);
 extern void native_expand(resource_t * rsc, pe_working_set_t * data_set);
 extern void native_dump(resource_t * rsc, const char *pre_text, gboolean details);
 extern gboolean native_create_probe(resource_t * rsc, node_t * node, action_t * complete,
                                     gboolean force, pe_working_set_t * data_set);
 extern void native_append_meta(resource_t * rsc, xmlNode * xml);
 
 extern int group_num_allowed_nodes(resource_t * rsc);
 extern node_t *group_color(resource_t * rsc, node_t * preferred, pe_working_set_t * data_set);
 extern void group_create_actions(resource_t * rsc, pe_working_set_t * data_set);
 extern void group_internal_constraints(resource_t * rsc, pe_working_set_t * data_set);
 extern void group_rsc_colocation_lh(resource_t * lh_rsc, resource_t * rh_rsc,
                                     rsc_colocation_t * constraint);
 extern void group_rsc_colocation_rh(resource_t * lh_rsc, resource_t * rh_rsc,
                                     rsc_colocation_t * constraint);
 extern enum pe_action_flags group_action_flags(action_t * action, node_t * node);
 extern void group_rsc_location(resource_t * rsc, rsc_to_node_t * constraint);
 extern void group_expand(resource_t * rsc, pe_working_set_t * data_set);
 extern void group_append_meta(resource_t * rsc, xmlNode * xml);
 
 extern int container_num_allowed_nodes(resource_t * rsc);
 extern node_t *container_color(resource_t * rsc, node_t * preferred, pe_working_set_t * data_set);
 extern void container_create_actions(resource_t * rsc, pe_working_set_t * data_set);
 extern void container_internal_constraints(resource_t * rsc, pe_working_set_t * data_set);
 extern void container_rsc_colocation_lh(resource_t * lh_rsc, resource_t * rh_rsc,
                                     rsc_colocation_t * constraint);
 extern void container_rsc_colocation_rh(resource_t * lh_rsc, resource_t * rh_rsc,
                                     rsc_colocation_t * constraint);
 extern void container_rsc_location(resource_t * rsc, rsc_to_node_t * constraint);
 extern enum pe_action_flags container_action_flags(action_t * action, node_t * node);
 extern void container_expand(resource_t * rsc, pe_working_set_t * data_set);
 extern gboolean container_create_probe(resource_t * rsc, node_t * node, action_t * complete,
                                    gboolean force, pe_working_set_t * data_set);
 extern void container_append_meta(resource_t * rsc, xmlNode * xml);
 
 extern int clone_num_allowed_nodes(resource_t * rsc);
 extern node_t *clone_color(resource_t * rsc, node_t * preferred, pe_working_set_t * data_set);
 extern void clone_create_actions(resource_t * rsc, pe_working_set_t * data_set);
 extern void clone_internal_constraints(resource_t * rsc, pe_working_set_t * data_set);
 extern void clone_rsc_colocation_lh(resource_t * lh_rsc, resource_t * rh_rsc,
                                     rsc_colocation_t * constraint);
 extern void clone_rsc_colocation_rh(resource_t * lh_rsc, resource_t * rh_rsc,
                                     rsc_colocation_t * constraint);
 extern void clone_rsc_location(resource_t * rsc, rsc_to_node_t * constraint);
 extern enum pe_action_flags clone_action_flags(action_t * action, node_t * node);
 extern void clone_expand(resource_t * rsc, pe_working_set_t * data_set);
 extern gboolean clone_create_probe(resource_t * rsc, node_t * node, action_t * complete,
                                    gboolean force, pe_working_set_t * data_set);
 extern void clone_append_meta(resource_t * rsc, xmlNode * xml);
 
 extern gboolean master_unpack(resource_t * rsc, pe_working_set_t * data_set);
 extern node_t *master_color(resource_t * rsc, node_t * preferred, pe_working_set_t * data_set);
 extern void master_create_actions(resource_t * rsc, pe_working_set_t * data_set);
 extern void master_internal_constraints(resource_t * rsc, pe_working_set_t * data_set);
 extern void master_rsc_colocation_rh(resource_t * lh_rsc, resource_t * rh_rsc,
                                      rsc_colocation_t * constraint);
 extern void master_append_meta(resource_t * rsc, xmlNode * xml);
 
 /* extern resource_object_functions_t resource_variants[]; */
 extern resource_alloc_functions_t resource_class_alloc_functions[];
 extern gboolean is_active(rsc_to_node_t * cons);
 
 extern gboolean native_constraint_violated(resource_t * rsc_lh, resource_t * rsc_rh,
                                            rsc_colocation_t * constraint);
 
 extern gboolean unpack_rsc_to_attr(xmlNode * xml_obj, pe_working_set_t * data_set);
 
 extern gboolean unpack_rsc_to_node(xmlNode * xml_obj, pe_working_set_t * data_set);
 
 extern gboolean unpack_rsc_order(xmlNode * xml_obj, pe_working_set_t * data_set);
 
 extern gboolean unpack_rsc_colocation(xmlNode * xml_obj, pe_working_set_t * data_set);
 
 extern gboolean unpack_location(xmlNode * xml_obj, pe_working_set_t * data_set);
 
 extern gboolean unpack_rsc_ticket(xmlNode * xml_obj, pe_working_set_t * data_set);
 
 void LogNodeActions(pe_working_set_t * data_set, gboolean terminal);
 void LogActions(resource_t * rsc, pe_working_set_t * data_set, gboolean terminal);
 void container_LogActions(resource_t * rsc, pe_working_set_t * data_set, gboolean terminal);
 
 extern void cleanup_alloc_calculations(pe_working_set_t * data_set);
 
 extern void rsc_stonith_ordering(resource_t * rsc, action_t * stonith_op,
                                  pe_working_set_t * data_set);
 
 extern enum pe_graph_flags native_update_actions(action_t * first, action_t * then, node_t * node,
                                                  enum pe_action_flags flags,
                                                  enum pe_action_flags filter,
                                                  enum pe_ordering type);
 extern enum pe_graph_flags group_update_actions(action_t * first, action_t * then, node_t * node,
                                                 enum pe_action_flags flags,
                                                 enum pe_action_flags filter, enum pe_ordering type);
 extern enum pe_graph_flags clone_update_actions(action_t * first, action_t * then, node_t * node,
                                                 enum pe_action_flags flags,
                                                 enum pe_action_flags filter, enum pe_ordering type);
 extern enum pe_graph_flags container_update_actions(action_t * first, action_t * then, node_t * node,
                                                     enum pe_action_flags flags,
                                                     enum pe_action_flags filter, enum pe_ordering type);
 
 gboolean update_action_flags(action_t * action, enum pe_action_flags flags, const char *source, int line);
 gboolean update_action(action_t * action);
 void complex_set_cmds(resource_t * rsc);
 
-
+void master_promotion_constraints(resource_t * rsc, pe_working_set_t * data_set);
+void clone_create_pseudo_actions(
+    resource_t * rsc, GListPtr children, notify_data_t **start_notify, notify_data_t **stop_notify,  pe_working_set_t * data_set);
 
 #endif
diff --git a/pengine/clone.c b/pengine/clone.c
index 51338d6993..a51677ab60 100644
--- a/pengine/clone.c
+++ b/pengine/clone.c
@@ -1,1654 +1,1649 @@
 /* 
  * Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
  * 
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public
  * License as published by the Free Software Foundation; either
  * version 2 of the License, or (at your option) any later version.
  * 
  * This software is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  * General Public License for more details.
  * 
  * You should have received a copy of the GNU General Public
  * License along with this library; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  */
 
 #include <crm_internal.h>
 
 #include <crm/msg_xml.h>
 #include <allocate.h>
 #include <notif.h>
 #include <utils.h>
 #include <allocate.h>
 
 #define VARIANT_CLONE 1
 #include <lib/pengine/variant.h>
 
 gint sort_clone_instance(gconstpointer a, gconstpointer b, gpointer data_set);
 static void append_parent_colocation(resource_t * rsc, resource_t * child, gboolean all);
 
 static gint
 sort_rsc_id(gconstpointer a, gconstpointer b)
 {
     const resource_t *resource1 = (const resource_t *)a;
     const resource_t *resource2 = (const resource_t *)b;
 
     CRM_ASSERT(resource1 != NULL);
     CRM_ASSERT(resource2 != NULL);
 
     return strcmp(resource1->id, resource2->id);
 }
 
 static node_t *
 parent_node_instance(const resource_t * rsc, node_t * node)
 {
     node_t *ret = NULL;
 
     if (node != NULL && rsc->parent) {
         ret = pe_hash_table_lookup(rsc->parent->allowed_nodes, node->details->id);
     } else if(node != NULL) {
         ret = pe_hash_table_lookup(rsc->allowed_nodes, node->details->id);
     }
     return ret;
 }
 
 static gboolean
 did_fail(const resource_t * rsc)
 {
     GListPtr gIter = rsc->children;
 
     if (is_set(rsc->flags, pe_rsc_failed)) {
         return TRUE;
     }
 
     for (; gIter != NULL; gIter = gIter->next) {
         resource_t *child_rsc = (resource_t *) gIter->data;
 
         if (did_fail(child_rsc)) {
             return TRUE;
         }
     }
     return FALSE;
 }
 
 gint
 sort_clone_instance(gconstpointer a, gconstpointer b, gpointer data_set)
 {
     int rc = 0;
     node_t *node1 = NULL;
     node_t *node2 = NULL;
 
     gboolean can1 = TRUE;
     gboolean can2 = TRUE;
 
     const resource_t *resource1 = (const resource_t *)a;
     const resource_t *resource2 = (const resource_t *)b;
 
     CRM_ASSERT(resource1 != NULL);
     CRM_ASSERT(resource2 != NULL);
 
     /* allocation order:
      *  - active instances
      *  - instances running on nodes with the least copies
      *  - active instances on nodes that can't support them or are to be fenced
      *  - failed instances
      *  - inactive instances
      */
 
     if (resource1->running_on && resource2->running_on) {
         if (g_list_length(resource1->running_on) < g_list_length(resource2->running_on)) {
             crm_trace("%s < %s: running_on", resource1->id, resource2->id);
             return -1;
 
         } else if (g_list_length(resource1->running_on) > g_list_length(resource2->running_on)) {
             crm_trace("%s > %s: running_on", resource1->id, resource2->id);
             return 1;
         }
     }
 
     if (resource1->running_on) {
         node1 = resource1->running_on->data;
     }
     if (resource2->running_on) {
         node2 = resource2->running_on->data;
     }
 
     if (node1) {
         node_t *match = pe_hash_table_lookup(resource1->allowed_nodes, node1->details->id);
 
         if (match == NULL || match->weight < 0) {
             crm_trace("%s: current location is unavailable", resource1->id);
             node1 = NULL;
             can1 = FALSE;
         }
     }
 
     if (node2) {
         node_t *match = pe_hash_table_lookup(resource2->allowed_nodes, node2->details->id);
 
         if (match == NULL || match->weight < 0) {
             crm_trace("%s: current location is unavailable", resource2->id);
             node2 = NULL;
             can2 = FALSE;
         }
     }
 
     if (can1 != can2) {
         if (can1) {
             crm_trace("%s < %s: availability of current location", resource1->id, resource2->id);
             return -1;
         }
         crm_trace("%s > %s: availability of current location", resource1->id, resource2->id);
         return 1;
     }
 
     if (resource1->priority < resource2->priority) {
         crm_trace("%s < %s: priority", resource1->id, resource2->id);
         return 1;
 
     } else if (resource1->priority > resource2->priority) {
         crm_trace("%s > %s: priority", resource1->id, resource2->id);
         return -1;
     }
 
     if (node1 == NULL && node2 == NULL) {
         crm_trace("%s == %s: not active", resource1->id, resource2->id);
         return 0;
     }
 
     if (node1 != node2) {
         if (node1 == NULL) {
             crm_trace("%s > %s: active", resource1->id, resource2->id);
             return 1;
         } else if (node2 == NULL) {
             crm_trace("%s < %s: active", resource1->id, resource2->id);
             return -1;
         }
     }
 
     can1 = can_run_resources(node1);
     can2 = can_run_resources(node2);
     if (can1 != can2) {
         if (can1) {
             crm_trace("%s < %s: can", resource1->id, resource2->id);
             return -1;
         }
         crm_trace("%s > %s: can", resource1->id, resource2->id);
         return 1;
     }
 
     node1 = parent_node_instance(resource1, node1);
     node2 = parent_node_instance(resource2, node2);
     if (node1 != NULL && node2 == NULL) {
         crm_trace("%s < %s: not allowed", resource1->id, resource2->id);
         return -1;
     } else if (node1 == NULL && node2 != NULL) {
         crm_trace("%s > %s: not allowed", resource1->id, resource2->id);
         return 1;
     }
 
     if (node1 == NULL || node2 == NULL) {
         crm_trace("%s == %s: not allowed", resource1->id, resource2->id);
         return 0;
     }
 
     if (node1->count < node2->count) {
         crm_trace("%s < %s: count", resource1->id, resource2->id);
         return -1;
 
     } else if (node1->count > node2->count) {
         crm_trace("%s > %s: count", resource1->id, resource2->id);
         return 1;
     }
 
     can1 = did_fail(resource1);
     can2 = did_fail(resource2);
     if (can1 != can2) {
         if (can1) {
             crm_trace("%s > %s: failed", resource1->id, resource2->id);
             return 1;
         }
         crm_trace("%s < %s: failed", resource1->id, resource2->id);
         return -1;
     }
 
     if (node1 && node2) {
         int lpc = 0;
         int max = 0;
         node_t *n = NULL;
         GListPtr gIter = NULL;
         GListPtr list1 = NULL;
         GListPtr list2 = NULL;
         GHashTable *hash1 =
             g_hash_table_new_full(crm_str_hash, g_str_equal, NULL, g_hash_destroy_str);
         GHashTable *hash2 =
             g_hash_table_new_full(crm_str_hash, g_str_equal, NULL, g_hash_destroy_str);
 
         n = node_copy(resource1->running_on->data);
         g_hash_table_insert(hash1, (gpointer) n->details->id, n);
 
         n = node_copy(resource2->running_on->data);
         g_hash_table_insert(hash2, (gpointer) n->details->id, n);
 
         if(resource1->parent) {
             for (gIter = resource1->parent->rsc_cons; gIter; gIter = gIter->next) {
                 rsc_colocation_t *constraint = (rsc_colocation_t *) gIter->data;
 
                 crm_trace("Applying %s to %s", constraint->id, resource1->id);
 
                 hash1 = native_merge_weights(constraint->rsc_rh, resource1->id, hash1,
                                              constraint->node_attribute,
                                              (float)constraint->score / INFINITY, 0);
             }
 
             for (gIter = resource1->parent->rsc_cons_lhs; gIter; gIter = gIter->next) {
                 rsc_colocation_t *constraint = (rsc_colocation_t *) gIter->data;
 
                 crm_trace("Applying %s to %s", constraint->id, resource1->id);
 
                 hash1 = native_merge_weights(constraint->rsc_lh, resource1->id, hash1,
                                              constraint->node_attribute,
                                              (float)constraint->score / INFINITY, pe_weights_positive);
             }
         }
 
         if(resource2->parent) {
             for (gIter = resource2->parent->rsc_cons; gIter; gIter = gIter->next) {
                 rsc_colocation_t *constraint = (rsc_colocation_t *) gIter->data;
 
                 crm_trace("Applying %s to %s", constraint->id, resource2->id);
 
                 hash2 = native_merge_weights(constraint->rsc_rh, resource2->id, hash2,
                                              constraint->node_attribute,
                                              (float)constraint->score / INFINITY, 0);
             }
 
             for (gIter = resource2->parent->rsc_cons_lhs; gIter; gIter = gIter->next) {
                 rsc_colocation_t *constraint = (rsc_colocation_t *) gIter->data;
 
                 crm_trace("Applying %s to %s", constraint->id, resource2->id);
 
                 hash2 = native_merge_weights(constraint->rsc_lh, resource2->id, hash2,
                                              constraint->node_attribute,
                                              (float)constraint->score / INFINITY, pe_weights_positive);
             }
         }
 
         /* Current location score */
         node1 = g_list_nth_data(resource1->running_on, 0);
         node1 = g_hash_table_lookup(hash1, node1->details->id);
 
         node2 = g_list_nth_data(resource2->running_on, 0);
         node2 = g_hash_table_lookup(hash2, node2->details->id);
 
         if (node1->weight < node2->weight) {
             if (node1->weight < 0) {
                 crm_trace("%s > %s: current score: %d %d", resource1->id, resource2->id, node1->weight, node2->weight);
                 rc = -1;
                 goto out;
 
             } else {
                 crm_trace("%s < %s: current score: %d %d", resource1->id, resource2->id, node1->weight, node2->weight);
                 rc = 1;
                 goto out;
             }
 
         } else if (node1->weight > node2->weight) {
             crm_trace("%s > %s: current score: %d %d", resource1->id, resource2->id, node1->weight, node2->weight);
             rc = -1;
             goto out;
         }
 
         /* All location scores */
         list1 = g_hash_table_get_values(hash1);
         list2 = g_hash_table_get_values(hash2);
 
         list1 =
             g_list_sort_with_data(list1, sort_node_weight,
                                   g_list_nth_data(resource1->running_on, 0));
         list2 =
             g_list_sort_with_data(list2, sort_node_weight,
                                   g_list_nth_data(resource2->running_on, 0));
         max = g_list_length(list1);
         if (max < g_list_length(list2)) {
             max = g_list_length(list2);
         }
 
         for (; lpc < max; lpc++) {
             node1 = g_list_nth_data(list1, lpc);
             node2 = g_list_nth_data(list2, lpc);
             if (node1 == NULL) {
                 crm_trace("%s < %s: colocated score NULL", resource1->id, resource2->id);
                 rc = 1;
                 break;
 
             } else if (node2 == NULL) {
                 crm_trace("%s > %s: colocated score NULL", resource1->id, resource2->id);
                 rc = -1;
                 break;
             }
 
             if (node1->weight < node2->weight) {
                 crm_trace("%s < %s: colocated score", resource1->id, resource2->id);
                 rc = 1;
                 break;
 
             } else if (node1->weight > node2->weight) {
                 crm_trace("%s > %s: colocated score", resource1->id, resource2->id);
                 rc = -1;
                 break;
             }
         }
 
         /* Order by reverse uname - same as sort_node_weight() does? */
   out:
         g_hash_table_destroy(hash1);    /* Free mem */
         g_hash_table_destroy(hash2);    /* Free mem */
         g_list_free(list1);
         g_list_free(list2);
 
         if (rc != 0) {
             return rc;
         }
     }
 
     rc = strcmp(resource1->id, resource2->id);
     crm_trace("%s %c %s: default", resource1->id, rc < 0 ? '<' : '>', resource2->id);
     return rc;
 }
 
 static node_t *
 can_run_instance(resource_t * rsc, node_t * node, int limit)
 {
     node_t *local_node = NULL;
 
     if (node == NULL && rsc->allowed_nodes) {
         GHashTableIter iter;
         g_hash_table_iter_init(&iter, rsc->allowed_nodes);
         while (g_hash_table_iter_next(&iter, NULL, (void **)&local_node)) {
             can_run_instance(rsc, local_node, limit);
         }
         return NULL;
     }
 
     if (can_run_resources(node) == FALSE) {
         goto bail;
 
     } else if (is_set(rsc->flags, pe_rsc_orphan)) {
         goto bail;
     }
 
     local_node = parent_node_instance(rsc, node);
 
     if (local_node == NULL) {
         crm_warn("%s cannot run on %s: node not allowed", rsc->id, node->details->uname);
         goto bail;
 
     } else if (local_node->weight < 0) {
         common_update_score(rsc, node->details->id, local_node->weight);
         pe_rsc_trace(rsc, "%s cannot run on %s: Parent node weight doesn't allow it.",
                      rsc->id, node->details->uname);
 
     } else if (local_node->count < limit) {
         pe_rsc_trace(rsc, "%s can run on %s: %d", rsc->id, node->details->uname, local_node->count);
         return local_node;
 
     } else {
         pe_rsc_trace(rsc, "%s cannot run on %s: node full (%d >= %d)",
                      rsc->id, node->details->uname, local_node->count, limit);
     }
 
   bail:
     if (node) {
         common_update_score(rsc, node->details->id, -INFINITY);
     }
     return NULL;
 }
 
 static node_t *
 color_instance(resource_t * rsc, node_t * prefer, gboolean all_coloc, int limit, pe_working_set_t * data_set)
 {
     node_t *chosen = NULL;
     GHashTable *backup = NULL;
 
     CRM_ASSERT(rsc);
     pe_rsc_trace(rsc, "Processing %s %d %s", rsc->id, all_coloc, prefer?prefer->details->uname:"none");
 
     if (is_not_set(rsc->flags, pe_rsc_provisional)) {
         return rsc->fns->location(rsc, NULL, FALSE);
 
     } else if (is_set(rsc->flags, pe_rsc_allocating)) {
         pe_rsc_debug(rsc, "Dependency loop detected involving %s", rsc->id);
         return NULL;
     }
 
     /* Only include positive colocation preferences of dependent resources
      * if not every node will get a copy of the clone
      */
     append_parent_colocation(rsc->parent, rsc, all_coloc);
 
     if (prefer) {
         node_t *local_prefer = g_hash_table_lookup(rsc->allowed_nodes, prefer->details->id);
 
         if (local_prefer == NULL || local_prefer->weight < 0) {
             pe_rsc_trace(rsc, "Not pre-allocating %s to %s - unavailable", rsc->id,
                          prefer->details->uname);
             return NULL;
         }
     }
 
     can_run_instance(rsc, NULL, limit);
 
     backup = node_hash_dup(rsc->allowed_nodes);
     chosen = rsc->cmds->allocate(rsc, prefer, data_set);
     if (chosen) {
         node_t *local_node = parent_node_instance(rsc, chosen);
         if (prefer && chosen && chosen->details != prefer->details) {
             crm_notice("Pre-allocation failed: got %s instead of %s",
                        chosen->details->uname, prefer->details->uname);
             g_hash_table_destroy(rsc->allowed_nodes);
             rsc->allowed_nodes = backup;
             native_deallocate(rsc);
             chosen = NULL;
             backup = NULL;
 
         } else if (local_node) {
             local_node->count++;
 
         } else if (is_set(rsc->flags, pe_rsc_managed)) {
             /* what to do? we can't enforce per-node limits in this case */
             crm_config_err("%s not found in %s (list=%d)",
                            chosen->details->id, rsc->parent->id,
                            g_hash_table_size(rsc->parent->allowed_nodes));
         }
     }
 
     if(backup) {
         g_hash_table_destroy(backup);
     }
     return chosen;
 }
 
 static void
 append_parent_colocation(resource_t * rsc, resource_t * child, gboolean all)
 {
 
     GListPtr gIter = NULL;
 
     gIter = rsc->rsc_cons;
     for (; gIter != NULL; gIter = gIter->next) {
         rsc_colocation_t *cons = (rsc_colocation_t *) gIter->data;
 
         if (all || cons->score < 0 || cons->score == INFINITY) {
             child->rsc_cons = g_list_prepend(child->rsc_cons, cons);
         }
     }
 
     gIter = rsc->rsc_cons_lhs;
     for (; gIter != NULL; gIter = gIter->next) {
         rsc_colocation_t *cons = (rsc_colocation_t *) gIter->data;
 
         if (all || cons->score < 0) {
             child->rsc_cons_lhs = g_list_prepend(child->rsc_cons_lhs, cons);
         }
     }
 }
 
 
 void
 distribute_children(resource_t *rsc, GListPtr children, GListPtr nodes,
                     int max, int per_host_max, pe_working_set_t * data_set);
 
 void
 distribute_children(resource_t *rsc, GListPtr children, GListPtr nodes,
                     int max, int per_host_max, pe_working_set_t * data_set) 
 {
     int loop_max = 0;
     int allocated = 0;
     int available_nodes = 0;
 
     /* count now tracks the number of clones currently allocated */
     for(GListPtr nIter = nodes; nIter != NULL; nIter = nIter->next) {
         pe_node_t *node = nIter->data;
 
         node->count = 0;
         if (can_run_resources(node)) {
             available_nodes++;
         }
     }
 
     if(available_nodes) {
         loop_max = max / available_nodes;
     }
     if (loop_max < 1) {
         loop_max = 1;
     }
 
     pe_rsc_debug(rsc, "Allocating %d %s instances to a possible %d nodes (%d per host, %d optimal)",
                  max, rsc->id, available_nodes, per_host_max, loop_max);
 
     /* Pre-allocate as many instances as we can to their current location */
     for (GListPtr gIter = children; gIter != NULL && allocated < max; gIter = gIter->next) {
         resource_t *child = (resource_t *) gIter->data;
 
         if (child->running_on && is_set(child->flags, pe_rsc_provisional)
             && is_not_set(child->flags, pe_rsc_failed)) {
             node_t *child_node = child->running_on->data;
             node_t *local_node = parent_node_instance(child, child->running_on->data);
 
             pe_rsc_trace(rsc, "Pre-allocating %s (%d remaining)", child->id, max - allocated);
             pe_rsc_trace(rsc, "Foo %s to %s %d %d", child->id,
                          child_node->details->uname, max, available_nodes);
 
             if (can_run_resources(child_node) == FALSE || child_node->weight < 0) {
                 pe_rsc_trace(rsc, "Not Pre-allocating %s", child_node->details->uname);
 
             } else if(local_node && local_node->count >= loop_max) {
                 pe_rsc_trace(rsc, "Deferring allocation of %s", child_node->details->uname);
 
             } else if (color_instance(child, child_node, max < available_nodes, per_host_max, data_set)) {
                 pe_rsc_trace(rsc, "Pre-allocated %s to %s", child->id,
                              child_node->details->uname);
                 allocated++;
             }
         }
     }
 
     pe_rsc_trace(rsc, "Done pre-allocating (%d of %d)", allocated, max);
 
     for (GListPtr gIter = children; gIter != NULL; gIter = gIter->next) {
         resource_t *child = (resource_t *) gIter->data;
 
         if (g_list_length(child->running_on) > 0) {
             node_t *child_node = child->running_on->data;
             node_t *local_node = parent_node_instance(child, child->running_on->data);
 
             if (local_node == NULL) {
                 crm_err("%s is running on %s which isn't allowed",
                         child->id, child_node->details->uname);
             }
         }
 
         if (is_not_set(child->flags, pe_rsc_provisional)) {
         } else if (allocated >= max) {
             pe_rsc_debug(rsc, "Child %s not allocated - limit reached %d %d", child->id, allocated, max);
             resource_location(child, NULL, -INFINITY, "clone_color:limit_reached", data_set);
         } else {
             if (color_instance(child, NULL, max < available_nodes, per_host_max, data_set)) {
                 allocated++;
             }
         }
     }
 
     pe_rsc_debug(rsc, "Allocated %d %s instances of a possible %d",
                  allocated, rsc->id, max);
 }
 
 
 node_t *
 clone_color(resource_t * rsc, node_t * prefer, pe_working_set_t * data_set)
 {
     GListPtr nodes = NULL;
 
     clone_variant_data_t *clone_data = NULL;
 
     get_clone_variant_data(clone_data, rsc);
 
     if (is_not_set(rsc->flags, pe_rsc_provisional)) {
         return NULL;
 
     } else if (is_set(rsc->flags, pe_rsc_allocating)) {
         pe_rsc_debug(rsc, "Dependency loop detected involving %s", rsc->id);
         return NULL;
     }
 
     set_bit(rsc->flags, pe_rsc_allocating);
     pe_rsc_trace(rsc, "Processing %s", rsc->id);
 
     /* this information is used by sort_clone_instance() when deciding in which 
      * order to allocate clone instances
      */
     for (GListPtr gIter = rsc->rsc_cons; gIter != NULL; gIter = gIter->next) {
         rsc_colocation_t *constraint = (rsc_colocation_t *) gIter->data;
 
         pe_rsc_trace(rsc, "%s: Coloring %s first", rsc->id, constraint->rsc_rh->id);
         constraint->rsc_rh->cmds->allocate(constraint->rsc_rh, prefer, data_set);
     }
 
     for (GListPtr gIter = rsc->rsc_cons_lhs; gIter != NULL; gIter = gIter->next) {
         rsc_colocation_t *constraint = (rsc_colocation_t *) gIter->data;
 
         rsc->allowed_nodes =
             constraint->rsc_lh->cmds->merge_weights(constraint->rsc_lh, rsc->id, rsc->allowed_nodes,
                                                     constraint->node_attribute,
                                                     (float)constraint->score / INFINITY,
                                                     (pe_weights_rollback | pe_weights_positive));
     }
 
     dump_node_scores(show_scores ? 0 : scores_log_level, rsc, __FUNCTION__, rsc->allowed_nodes);
 
     nodes = g_hash_table_get_values(rsc->allowed_nodes);
     nodes = g_list_sort_with_data(nodes, sort_node_weight, NULL);
     rsc->children = g_list_sort_with_data(rsc->children, sort_clone_instance, data_set);
     distribute_children(rsc, rsc->children, nodes, clone_data->clone_max, clone_data->clone_node_max, data_set);
     g_list_free(nodes);
 
     clear_bit(rsc->flags, pe_rsc_provisional);
     clear_bit(rsc->flags, pe_rsc_allocating);
 
     pe_rsc_trace(rsc, "Done allocating %s", rsc->id);
     return NULL;
 }
 
 static void
 clone_update_pseudo_status(resource_t * rsc, gboolean * stopping, gboolean * starting,
                            gboolean * active)
 {
     GListPtr gIter = NULL;
 
     if (rsc->children) {
 
         gIter = rsc->children;
         for (; gIter != NULL; gIter = gIter->next) {
             resource_t *child = (resource_t *) gIter->data;
 
             clone_update_pseudo_status(child, stopping, starting, active);
         }
 
         return;
     }
 
     CRM_ASSERT(active != NULL);
     CRM_ASSERT(starting != NULL);
     CRM_ASSERT(stopping != NULL);
 
     if (rsc->running_on) {
         *active = TRUE;
     }
 
     gIter = rsc->actions;
     for (; gIter != NULL; gIter = gIter->next) {
         action_t *action = (action_t *) gIter->data;
 
         if (*starting && *stopping) {
             return;
 
         } else if (is_set(action->flags, pe_action_optional)) {
             pe_rsc_trace(rsc, "Skipping optional: %s", action->uuid);
             continue;
 
         } else if (is_set(action->flags, pe_action_pseudo) == FALSE
                    && is_set(action->flags, pe_action_runnable) == FALSE) {
             pe_rsc_trace(rsc, "Skipping unrunnable: %s", action->uuid);
             continue;
 
         } else if (safe_str_eq(RSC_STOP, action->task)) {
             pe_rsc_trace(rsc, "Stopping due to: %s", action->uuid);
             *stopping = TRUE;
 
         } else if (safe_str_eq(RSC_START, action->task)) {
             if (is_set(action->flags, pe_action_runnable) == FALSE) {
                 pe_rsc_trace(rsc, "Skipping pseudo-op: %s run=%d, pseudo=%d",
                              action->uuid, is_set(action->flags, pe_action_runnable),
                              is_set(action->flags, pe_action_pseudo));
             } else {
                 pe_rsc_trace(rsc, "Starting due to: %s", action->uuid);
                 pe_rsc_trace(rsc, "%s run=%d, pseudo=%d",
                              action->uuid, is_set(action->flags, pe_action_runnable),
                              is_set(action->flags, pe_action_pseudo));
                 *starting = TRUE;
             }
         }
     }
 }
 
 static action_t *
 find_rsc_action(resource_t * rsc, const char *key, gboolean active_only, GListPtr * list)
 {
     action_t *match = NULL;
     GListPtr possible = NULL;
     GListPtr active = NULL;
 
     possible = find_actions(rsc->actions, key, NULL);
 
     if (active_only) {
         GListPtr gIter = possible;
 
         for (; gIter != NULL; gIter = gIter->next) {
             action_t *op = (action_t *) gIter->data;
 
             if (is_set(op->flags, pe_action_optional) == FALSE) {
                 active = g_list_prepend(active, op);
             }
         }
 
         if (active && g_list_length(active) == 1) {
             match = g_list_nth_data(active, 0);
         }
 
         if (list) {
             *list = active;
             active = NULL;
         }
 
     } else if (possible && g_list_length(possible) == 1) {
         match = g_list_nth_data(possible, 0);
 
     }
     if (list) {
         *list = possible;
         possible = NULL;
     }
 
     if (possible) {
         g_list_free(possible);
     }
     if (active) {
         g_list_free(active);
     }
 
     return match;
 }
 
 static void
 child_ordering_constraints(resource_t * rsc, pe_working_set_t * data_set)
 {
     char *key = NULL;
     action_t *stop = NULL;
     action_t *start = NULL;
     action_t *last_stop = NULL;
     action_t *last_start = NULL;
     GListPtr gIter = NULL;
     gboolean active_only = TRUE;        /* change to false to get the old behavior */
     clone_variant_data_t *clone_data = NULL;
 
     get_clone_variant_data(clone_data, rsc);
 
     if (clone_data->ordered == FALSE) {
         return;
     }
     /* we have to maintain a consistent sorted child list when building order constraints */
     rsc->children = g_list_sort(rsc->children, sort_rsc_id);
 
     for (gIter = rsc->children; gIter != NULL; gIter = gIter->next) {
         resource_t *child = (resource_t *) gIter->data;
 
         key = stop_key(child);
         stop = find_rsc_action(child, key, active_only, NULL);
         free(key);
 
         key = start_key(child);
         start = find_rsc_action(child, key, active_only, NULL);
         free(key);
 
         if (stop) {
             if (last_stop) {
                 /* child/child relative stop */
                 order_actions(stop, last_stop, pe_order_optional);
             }
             last_stop = stop;
         }
 
         if (start) {
             if (last_start) {
                 /* child/child relative start */
                 order_actions(last_start, start, pe_order_optional);
             }
             last_start = start;
         }
     }
 }
 
 void
 clone_create_actions(resource_t * rsc, pe_working_set_t * data_set)
+{
+    clone_variant_data_t *clone_data = NULL;
+
+    get_clone_variant_data(clone_data, rsc);
+    clone_create_pseudo_actions(rsc, rsc->children, &clone_data->start_notify, &clone_data->stop_notify,data_set);
+    child_ordering_constraints(rsc, data_set);
+}
+
+void
+clone_create_pseudo_actions(
+    resource_t * rsc, GListPtr children, notify_data_t **start_notify, notify_data_t **stop_notify,  pe_working_set_t * data_set)
 {
     gboolean child_active = FALSE;
     gboolean child_starting = FALSE;
     gboolean child_stopping = FALSE;
     gboolean allow_dependent_migrations = TRUE;
 
     action_t *stop = NULL;
     action_t *stopped = NULL;
 
     action_t *start = NULL;
     action_t *started = NULL;
 
-    GListPtr gIter = rsc->children;
-    clone_variant_data_t *clone_data = NULL;
-
-    get_clone_variant_data(clone_data, rsc);
-
     pe_rsc_trace(rsc, "Creating actions for %s", rsc->id);
 
-    for (; gIter != NULL; gIter = gIter->next) {
+    for (GListPtr gIter = children; gIter != NULL; gIter = gIter->next) {
         resource_t *child_rsc = (resource_t *) gIter->data;
         gboolean starting = FALSE;
         gboolean stopping = FALSE;
 
         child_rsc->cmds->create_actions(child_rsc, data_set);
         clone_update_pseudo_status(child_rsc, &stopping, &starting, &child_active);
         if (stopping && starting) {
             allow_dependent_migrations = FALSE;
         }
 
         child_stopping |= stopping;
         child_starting |= starting;
     }
 
     /* start */
-    start = start_action(rsc, NULL, !child_starting);
-    started = custom_action(rsc, started_key(rsc),
-                            RSC_STARTED, NULL, !child_starting, TRUE, data_set);
-
-    update_action_flags(start, pe_action_pseudo | pe_action_runnable, __FUNCTION__, __LINE__);
-    update_action_flags(started, pe_action_pseudo, __FUNCTION__, __LINE__);
+    start = create_pseudo_resource_op(rsc, RSC_START, !child_starting, TRUE, data_set);
+    started = create_pseudo_resource_op(rsc, RSC_STARTED, !child_starting, FALSE, data_set);
     started->priority = INFINITY;
 
     if (child_active || child_starting) {
         update_action_flags(started, pe_action_runnable, __FUNCTION__, __LINE__);
     }
 
-    child_ordering_constraints(rsc, data_set);
-    if (clone_data->start_notify == NULL) {
-        clone_data->start_notify =
-            create_notification_boundaries(rsc, RSC_START, start, started, data_set);
+    if (start_notify != NULL && *start_notify == NULL) {
+        *start_notify = create_notification_boundaries(rsc, RSC_START, start, started, data_set);
     }
 
     /* stop */
-    stop = stop_action(rsc, NULL, !child_stopping);
-    stopped = custom_action(rsc, stopped_key(rsc),
-                            RSC_STOPPED, NULL, !child_stopping, TRUE, data_set);
-
+    stop = create_pseudo_resource_op(rsc, RSC_STOP, !child_stopping, TRUE, data_set);
+    stopped = create_pseudo_resource_op(rsc, RSC_STOPPED, !child_stopping, TRUE, data_set);
     stopped->priority = INFINITY;
-    update_action_flags(stop, pe_action_pseudo | pe_action_runnable, __FUNCTION__, __LINE__);
     if (allow_dependent_migrations) {
         update_action_flags(stop, pe_action_migrate_runnable, __FUNCTION__, __LINE__);
     }
-    update_action_flags(stopped, pe_action_pseudo | pe_action_runnable, __FUNCTION__, __LINE__);
-    if (clone_data->stop_notify == NULL) {
-        clone_data->stop_notify =
-            create_notification_boundaries(rsc, RSC_STOP, stop, stopped, data_set);
 
-        if (clone_data->stop_notify && clone_data->start_notify) {
-            order_actions(clone_data->stop_notify->post_done, clone_data->start_notify->pre,
-                          pe_order_optional);
+    if (stop_notify != NULL && *stop_notify == NULL) {
+        *stop_notify = create_notification_boundaries(rsc, RSC_STOP, stop, stopped, data_set);
+
+        if (*stop_notify && *start_notify) {
+            order_actions((*stop_notify)->post_done, (*start_notify)->pre, pe_order_optional);
         }
     }
 }
 
 void
 clone_internal_constraints(resource_t * rsc, pe_working_set_t * data_set)
 {
     resource_t *last_rsc = NULL;
     GListPtr gIter;
     clone_variant_data_t *clone_data = NULL;
 
     get_clone_variant_data(clone_data, rsc);
 
     pe_rsc_trace(rsc, "Internal constraints for %s", rsc->id);
     new_rsc_order(rsc, RSC_STOPPED, rsc, RSC_START, pe_order_optional, data_set);
     new_rsc_order(rsc, RSC_START, rsc, RSC_STARTED, pe_order_runnable_left, data_set);
     new_rsc_order(rsc, RSC_STOP, rsc, RSC_STOPPED, pe_order_runnable_left, data_set);
 
     if (rsc->variant == pe_master) {
         new_rsc_order(rsc, RSC_DEMOTED, rsc, RSC_STOP, pe_order_optional, data_set);
         new_rsc_order(rsc, RSC_STARTED, rsc, RSC_PROMOTE, pe_order_runnable_left, data_set);
     }
 
     if (clone_data->ordered) {
         /* we have to maintain a consistent sorted child list when building order constraints */
         rsc->children = g_list_sort(rsc->children, sort_rsc_id);
     }
     for (gIter = rsc->children; gIter != NULL; gIter = gIter->next) {
         resource_t *child_rsc = (resource_t *) gIter->data;
 
         child_rsc->cmds->internal_constraints(child_rsc, data_set);
 
         order_start_start(rsc, child_rsc, pe_order_runnable_left | pe_order_implies_first_printed);
         new_rsc_order(child_rsc, RSC_START, rsc, RSC_STARTED, pe_order_implies_then_printed,
                       data_set);
         if (clone_data->ordered && last_rsc) {
             order_start_start(last_rsc, child_rsc, pe_order_optional);
         }
 
         order_stop_stop(rsc, child_rsc, pe_order_implies_first_printed);
         new_rsc_order(child_rsc, RSC_STOP, rsc, RSC_STOPPED, pe_order_implies_then_printed,
                       data_set);
         if (clone_data->ordered && last_rsc) {
             order_stop_stop(child_rsc, last_rsc, pe_order_optional);
         }
 
         last_rsc = child_rsc;
     }
 }
 
 bool
 assign_node(resource_t * rsc, node_t * node, gboolean force)
 {
     bool changed = FALSE;
 
     if (rsc->children) {
 
         for (GListPtr gIter = rsc->children; gIter != NULL; gIter = gIter->next) {
             resource_t *child_rsc = (resource_t *) gIter->data;
 
             changed |= assign_node(child_rsc, node, force);
         }
 
         return changed;
     }
 
     if (rsc->allocated_to != NULL) {
         changed = true;
     }
 
     native_assign_node(rsc, NULL, node, force);
     return changed;
 }
 
 static resource_t *
 find_compatible_child_by_node(resource_t * local_child, node_t * local_node, resource_t * rsc,
                               enum rsc_role_e filter, gboolean current)
 {
     GListPtr gIter = NULL;
 
     if (local_node == NULL) {
         crm_err("Can't colocate unrunnable child %s with %s", local_child->id, rsc->id);
         return NULL;
     }
 
     crm_trace("Looking for compatible child from %s for %s on %s",
               local_child->id, rsc->id, local_node->details->uname);
 
     gIter = rsc->children;
     for (; gIter != NULL; gIter = gIter->next) {
         resource_t *child_rsc = (resource_t *) gIter->data;
 
         if(is_child_compatible(child_rsc, local_node, filter, current)) {
             crm_trace("Pairing %s with %s on %s",
                       local_child->id, child_rsc->id, local_node->details->uname);
             return child_rsc;
         }
     }
 
     crm_trace("Can't pair %s with %s", local_child->id, rsc->id);
     return NULL;
 }
 
 gboolean
 is_child_compatible(resource_t *child_rsc, node_t * local_node, enum rsc_role_e filter, gboolean current) 
 {
     node_t *node = NULL;
     enum rsc_role_e next_role = child_rsc->fns->state(child_rsc, current);
 
     if (is_set_recursive(child_rsc, pe_rsc_block, TRUE) == FALSE) {
         /* We only want instances that haven't failed */
         node = child_rsc->fns->location(child_rsc, NULL, current);
     }
 
     if (filter != RSC_ROLE_UNKNOWN && next_role != filter) {
         crm_trace("Filtered %s", child_rsc->id);
         return FALSE;
     }
 
     if (node && local_node && node->details == local_node->details) {
         return TRUE;
 
     } else if (node) {
         crm_trace("%s - %s vs %s", child_rsc->id, node->details->uname,
                   local_node->details->uname);
 
     } else {
         crm_trace("%s - not allocated %d", child_rsc->id, current);
     }
     return FALSE;
 }
 
 resource_t *
 find_compatible_child(resource_t * local_child, resource_t * rsc, enum rsc_role_e filter,
                       gboolean current)
 {
     resource_t *pair = NULL;
     GListPtr gIter = NULL;
     GListPtr scratch = NULL;
     node_t *local_node = NULL;
 
     local_node = local_child->fns->location(local_child, NULL, current);
     if (local_node) {
         return find_compatible_child_by_node(local_child, local_node, rsc, filter, current);
     }
 
     scratch = g_hash_table_get_values(local_child->allowed_nodes);
     scratch = g_list_sort_with_data(scratch, sort_node_weight, NULL);
 
     gIter = scratch;
     for (; gIter != NULL; gIter = gIter->next) {
         node_t *node = (node_t *) gIter->data;
 
         pair = find_compatible_child_by_node(local_child, node, rsc, filter, current);
         if (pair) {
             goto done;
         }
     }
 
     pe_rsc_debug(rsc, "Can't pair %s with %s", local_child->id, rsc->id);
   done:
     g_list_free(scratch);
     return pair;
 }
 
 void
 clone_rsc_colocation_lh(resource_t * rsc_lh, resource_t * rsc_rh, rsc_colocation_t * constraint)
 {
     /* -- Never called --
      *
      * Instead we add the colocation constraints to the child and call from there
      */
     CRM_ASSERT(FALSE);
 }
 
 void
 clone_rsc_colocation_rh(resource_t * rsc_lh, resource_t * rsc_rh, rsc_colocation_t * constraint)
 {
     GListPtr gIter = NULL;
     gboolean do_interleave = FALSE;
     clone_variant_data_t *clone_data = NULL;
     clone_variant_data_t *clone_data_lh = NULL;
 
     CRM_CHECK(constraint != NULL, return);
     CRM_CHECK(rsc_lh != NULL, pe_err("rsc_lh was NULL for %s", constraint->id); return);
     CRM_CHECK(rsc_rh != NULL, pe_err("rsc_rh was NULL for %s", constraint->id); return);
     CRM_CHECK(rsc_lh->variant == pe_native, return);
 
     get_clone_variant_data(clone_data, constraint->rsc_rh);
     pe_rsc_trace(rsc_rh, "Processing constraint %s: %s -> %s %d",
                  constraint->id, rsc_lh->id, rsc_rh->id, constraint->score);
 
     if (pe_rsc_is_clone(constraint->rsc_lh)) {
 
         get_clone_variant_data(clone_data_lh, constraint->rsc_lh);
         if (clone_data_lh->interleave
             && clone_data->clone_node_max != clone_data_lh->clone_node_max) {
             crm_config_err("Cannot interleave " XML_CIB_TAG_INCARNATION " %s and %s because"
                            " they do not support the same number of" " resources per node",
                            constraint->rsc_lh->id, constraint->rsc_rh->id);
 
             /* only the LHS side needs to be labeled as interleave */
         } else if (clone_data_lh->interleave) {
             do_interleave = TRUE;
         }
     }
 
     if (is_set(rsc_rh->flags, pe_rsc_provisional)) {
         pe_rsc_trace(rsc_rh, "%s is still provisional", rsc_rh->id);
         return;
 
     } else if (do_interleave) {
         resource_t *rh_child = NULL;
 
         rh_child = find_compatible_child(rsc_lh, rsc_rh, RSC_ROLE_UNKNOWN, FALSE);
 
         if (rh_child) {
             pe_rsc_debug(rsc_rh, "Pairing %s with %s", rsc_lh->id, rh_child->id);
             rsc_lh->cmds->rsc_colocation_lh(rsc_lh, rh_child, constraint);
 
         } else if (constraint->score >= INFINITY) {
             crm_notice("Cannot pair %s with instance of %s", rsc_lh->id, rsc_rh->id);
             assign_node(rsc_lh, NULL, TRUE);
 
         } else {
             pe_rsc_debug(rsc_rh, "Cannot pair %s with instance of %s", rsc_lh->id, rsc_rh->id);
         }
 
         return;
 
     } else if (constraint->score >= INFINITY) {
         GListPtr rhs = NULL;
 
         gIter = rsc_rh->children;
         for (; gIter != NULL; gIter = gIter->next) {
             resource_t *child_rsc = (resource_t *) gIter->data;
             node_t *chosen = child_rsc->fns->location(child_rsc, NULL, FALSE);
 
             if (chosen != NULL && is_set_recursive(child_rsc, pe_rsc_block, TRUE) == FALSE) {
                 pe_rsc_trace(rsc_rh, "Allowing %s: %s %d", constraint->id, chosen->details->uname, chosen->weight);
                 rhs = g_list_prepend(rhs, chosen);
             }
         }
 
         node_list_exclude(rsc_lh->allowed_nodes, rhs, FALSE);
         g_list_free(rhs);
         return;
     }
 
     gIter = rsc_rh->children;
     for (; gIter != NULL; gIter = gIter->next) {
         resource_t *child_rsc = (resource_t *) gIter->data;
 
         child_rsc->cmds->rsc_colocation_rh(rsc_lh, child_rsc, constraint);
     }
 }
 
 static enum action_tasks
 clone_child_action(action_t * action)
 {
     enum action_tasks result = no_action;
     resource_t *child = (resource_t *) action->rsc->children->data;
 
     if (safe_str_eq(action->task, "notify")
         || safe_str_eq(action->task, "notified")) {
 
         /* Find the action we're notifying about instead */
 
         int stop = 0;
         char *key = action->uuid;
         int lpc = strlen(key);
 
         for (; lpc > 0; lpc--) {
             if (key[lpc] == '_' && stop == 0) {
                 stop = lpc;
 
             } else if (key[lpc] == '_') {
                 char *task_mutable = NULL;
 
                 lpc++;
                 task_mutable = strdup(key + lpc);
                 task_mutable[stop - lpc] = 0;
 
                 crm_trace("Extracted action '%s' from '%s'", task_mutable, key);
                 result = get_complex_task(child, task_mutable, TRUE);
                 free(task_mutable);
                 break;
             }
         }
 
     } else {
         result = get_complex_task(child, action->task, TRUE);
     }
     return result;
 }
 
 enum pe_action_flags
 clone_action_flags(action_t * action, node_t * node)
 {
     GListPtr gIter = NULL;
     gboolean any_runnable = FALSE;
     gboolean check_runnable = TRUE;
     enum action_tasks task = clone_child_action(action);
     enum pe_action_flags flags = (pe_action_optional | pe_action_runnable | pe_action_pseudo);
     const char *task_s = task2text(task);
 
     gIter = action->rsc->children;
     for (; gIter != NULL; gIter = gIter->next) {
         action_t *child_action = NULL;
         resource_t *child = (resource_t *) gIter->data;
 
         child_action =
             find_first_action(child->actions, NULL, task_s, child->children ? NULL : node);
         pe_rsc_trace(action->rsc, "Checking for %s in %s on %s", task_s, child->id,
                      node ? node->details->uname : "none");
         if (child_action) {
             enum pe_action_flags child_flags = child->cmds->action_flags(child_action, node);
 
             if (is_set(flags, pe_action_optional)
                 && is_set(child_flags, pe_action_optional) == FALSE) {
                 pe_rsc_trace(child, "%s is mandatory because of %s", action->uuid,
                              child_action->uuid);
                 flags = crm_clear_bit(__FUNCTION__, __LINE__, action->rsc->id, flags, pe_action_optional);
                 pe_clear_action_bit(action, pe_action_optional);
             }
             if (is_set(child_flags, pe_action_runnable)) {
                 any_runnable = TRUE;
             }
 
         } else {
 
             GListPtr gIter2 = child->actions;
 
             for (; gIter2 != NULL; gIter2 = gIter2->next) {
                 action_t *op = (action_t *) gIter2->data;
 
                 pe_rsc_trace(child, "%s on %s (%s)", op->uuid,
                              op->node ? op->node->details->uname : "none", op->task);
             }
         }
     }
 
     if (check_runnable && any_runnable == FALSE) {
         pe_rsc_trace(action->rsc, "%s is not runnable because no children are", action->uuid);
         flags = crm_clear_bit(__FUNCTION__, __LINE__, action->rsc->id, flags, pe_action_runnable);
         if (node == NULL) {
             pe_clear_action_bit(action, pe_action_runnable);
         }
     }
 
     return flags;
 }
 
 static enum pe_graph_flags
 clone_update_actions_interleave(action_t * first, action_t * then, node_t * node,
                                 enum pe_action_flags flags, enum pe_action_flags filter,
                                 enum pe_ordering type)
 {
     gboolean current = FALSE;
     resource_t *first_child = NULL;
     GListPtr gIter = then->rsc->children;
     enum pe_graph_flags changed = pe_graph_none;        /*pe_graph_disable */
 
     enum action_tasks task = clone_child_action(first);
     const char *first_task = task2text(task);
 
     /* Fix this - lazy */
     if (crm_ends_with(first->uuid, "_stopped_0")
         || crm_ends_with(first->uuid, "_demoted_0")) {
         current = TRUE;
     }
 
     for (; gIter != NULL; gIter = gIter->next) {
         resource_t *then_child = (resource_t *) gIter->data;
 
         CRM_ASSERT(then_child != NULL);
         first_child = find_compatible_child(then_child, first->rsc, RSC_ROLE_UNKNOWN, current);
         if (first_child == NULL && current) {
             crm_trace("Ignore");
 
         } else if (first_child == NULL) {
             crm_debug("No match found for %s (%d / %s / %s)", then_child->id, current, first->uuid,
                       then->uuid);
 
             /* Me no like this hack - but what else can we do?
              *
              * If there is no-one active or about to be active
              *   on the same node as then_child, then they must
              *   not be allowed to start
              */
             if (type & (pe_order_runnable_left | pe_order_implies_then) /* Mandatory */ ) {
                 pe_rsc_info(then->rsc, "Inhibiting %s from being active", then_child->id);
                 if(assign_node(then_child, NULL, TRUE)) {
                     changed |= pe_graph_updated_then;
                 }
             }
 
         } else {
             action_t *first_action = NULL;
             action_t *then_action = NULL;
 
             pe_rsc_debug(then->rsc, "Pairing %s with %s", first_child->id, then_child->id);
 
             first_action = find_first_action(first_child->actions, NULL, first_task, node);
             then_action = find_first_action(then_child->actions, NULL, then->task, node);
 
             if (first_action == NULL) {
                 if (is_not_set(first_child->flags, pe_rsc_orphan)
                     && crm_str_eq(first_task, RSC_STOP, TRUE) == FALSE
                     && crm_str_eq(first_task, RSC_DEMOTE, TRUE) == FALSE) {
                     crm_err("Internal error: No action found for %s in %s (first)",
                             first_task, first_child->id);
 
                 } else {
                     crm_trace("No action found for %s in %s%s (first)",
                               first_task, first_child->id,
                               is_set(first_child->flags, pe_rsc_orphan) ? " (ORPHAN)" : "");
                 }
                 continue;
             }
 
             /* We're only interested if 'then' is neither stopping nor being demoted */ 
             if (then_action == NULL) {
                 if (is_not_set(then_child->flags, pe_rsc_orphan)
                     && crm_str_eq(then->task, RSC_STOP, TRUE) == FALSE
                     && crm_str_eq(then->task, RSC_DEMOTE, TRUE) == FALSE) {
                     crm_err("Internal error: No action found for %s in %s (then)",
                             then->task, then_child->id);
 
                 } else {
                     crm_trace("No action found for %s in %s%s (then)",
                               then->task, then_child->id,
                               is_set(then_child->flags, pe_rsc_orphan) ? " (ORPHAN)" : "");
                 }
                 continue;
             }
 
             if (order_actions(first_action, then_action, type)) {
                 crm_debug("Created constraint for %s -> %s", first_action->uuid, then_action->uuid);
                 changed |= (pe_graph_updated_first | pe_graph_updated_then);
             }
             changed |=
                 then_child->cmds->update_actions(first_action, then_action, node,
                                                  first_child->cmds->action_flags(first_action,
                                                                                  node), filter,
                                                  type);
         }
     }
     return changed;
 }
 
 enum pe_graph_flags
 clone_update_actions(action_t * first, action_t * then, node_t * node, enum pe_action_flags flags,
                      enum pe_action_flags filter, enum pe_ordering type)
 {
     const char *rsc = "none";
     gboolean interleave = FALSE;
     enum pe_graph_flags changed = pe_graph_none;
 
     if (first->rsc != then->rsc
         && pe_rsc_is_clone(first->rsc)
         && pe_rsc_is_clone(then->rsc)) {
         clone_variant_data_t *clone_data = NULL;
 
         if (crm_ends_with(then->uuid, "_stop_0")
             || crm_ends_with(then->uuid, "_demote_0")) {
             get_clone_variant_data(clone_data, first->rsc);
             rsc = first->rsc->id;
         } else {
             get_clone_variant_data(clone_data, then->rsc);
             rsc = then->rsc->id;
         }
         interleave = clone_data->interleave;
     }
 
     crm_trace("Interleave %s -> %s: %s (based on %s)",
               first->uuid, then->uuid, interleave ? "yes" : "no", rsc);
 
     if (interleave) {
         changed = clone_update_actions_interleave(first, then, node, flags, filter, type);
 
     } else if (then->rsc) {
         GListPtr gIter = then->rsc->children;
 
         changed |= native_update_actions(first, then, node, flags, filter, type);
 
         for (; gIter != NULL; gIter = gIter->next) {
             enum pe_graph_flags child_changed = pe_graph_none;
             GListPtr lpc = NULL;
             resource_t *child = (resource_t *) gIter->data;
             action_t *child_action = find_first_action(child->actions, NULL, then->task, node);
 
             if (child_action) {
                 enum pe_action_flags child_flags = child->cmds->action_flags(child_action, node);
 
                 if (is_set(child_flags, pe_action_runnable)) {
                                      
                     child_changed |=
                         child->cmds->update_actions(first, child_action, node, flags, filter, type);
                 }
                 changed |= child_changed;
                 if (child_changed & pe_graph_updated_then) {
                    for (lpc = child_action->actions_after; lpc != NULL; lpc = lpc->next) {
                         action_wrapper_t *other = (action_wrapper_t *) lpc->data;
                         update_action(other->action);
                     }
                 }
             }
         }
     }
 
     return changed;
 }
 
 void
 clone_rsc_location(resource_t * rsc, rsc_to_node_t * constraint)
 {
     GListPtr gIter = rsc->children;
 
     pe_rsc_trace(rsc, "Processing location constraint %s for %s", constraint->id, rsc->id);
 
     native_rsc_location(rsc, constraint);
 
     for (; gIter != NULL; gIter = gIter->next) {
         resource_t *child_rsc = (resource_t *) gIter->data;
 
         child_rsc->cmds->rsc_location(child_rsc, constraint);
     }
 }
 
 void
 clone_expand(resource_t * rsc, pe_working_set_t * data_set)
 {
     GListPtr gIter = NULL;
     clone_variant_data_t *clone_data = NULL;
 
     get_clone_variant_data(clone_data, rsc);
 
     gIter = rsc->actions;
     for (; gIter != NULL; gIter = gIter->next) {
         action_t *op = (action_t *) gIter->data;
 
         rsc->cmds->action_flags(op, NULL);
     }
 
     if (clone_data->start_notify) {
         collect_notification_data(rsc, TRUE, TRUE, clone_data->start_notify);
         expand_notification_data(clone_data->start_notify, data_set);
         create_notifications(rsc, clone_data->start_notify, data_set);
     }
 
     if (clone_data->stop_notify) {
         collect_notification_data(rsc, TRUE, TRUE, clone_data->stop_notify);
         expand_notification_data(clone_data->stop_notify, data_set);
         create_notifications(rsc, clone_data->stop_notify, data_set);
     }
 
     if (clone_data->promote_notify) {
         collect_notification_data(rsc, TRUE, TRUE, clone_data->promote_notify);
         expand_notification_data(clone_data->promote_notify, data_set);
         create_notifications(rsc, clone_data->promote_notify, data_set);
     }
 
     if (clone_data->demote_notify) {
         collect_notification_data(rsc, TRUE, TRUE, clone_data->demote_notify);
         expand_notification_data(clone_data->demote_notify, data_set);
         create_notifications(rsc, clone_data->demote_notify, data_set);
     }
 
     /* Now that the notifcations have been created we can expand the children */
 
     gIter = rsc->children;
     for (; gIter != NULL; gIter = gIter->next) {
         resource_t *child_rsc = (resource_t *) gIter->data;
 
         child_rsc->cmds->expand(child_rsc, data_set);
     }
 
     native_expand(rsc, data_set);
 
     /* The notifications are in the graph now, we can destroy the notify_data */
     free_notification_data(clone_data->demote_notify);
     clone_data->demote_notify = NULL;
     free_notification_data(clone_data->stop_notify);
     clone_data->stop_notify = NULL;
     free_notification_data(clone_data->start_notify);
     clone_data->start_notify = NULL;
     free_notification_data(clone_data->promote_notify);
     clone_data->promote_notify = NULL;
 }
 
 node_t *
 rsc_known_on(resource_t * rsc, GListPtr * list)
 {
     GListPtr gIter = NULL;
     node_t *one = NULL;
     GListPtr result = NULL;
 
     if (rsc->children) {
 
         gIter = rsc->children;
         for (; gIter != NULL; gIter = gIter->next) {
             resource_t *child = (resource_t *) gIter->data;
 
             rsc_known_on(child, &result);
         }
 
     } else if (rsc->known_on) {
         result = g_hash_table_get_values(rsc->known_on);
     }
 
     if (result && g_list_length(result) == 1) {
         one = g_list_nth_data(result, 0);
     }
 
     if (list) {
         GListPtr gIter = NULL;
 
         gIter = result;
         for (; gIter != NULL; gIter = gIter->next) {
             node_t *node = (node_t *) gIter->data;
 
             if (*list == NULL || pe_find_node_id(*list, node->details->id) == NULL) {
                 *list = g_list_prepend(*list, node);
             }
         }
     }
 
     g_list_free(result);
     return one;
 }
 
 static resource_t *
 find_instance_on(resource_t * rsc, node_t * node)
 {
     GListPtr gIter = NULL;
 
     gIter = rsc->children;
     for (; gIter != NULL; gIter = gIter->next) {
         GListPtr gIter2 = NULL;
         GListPtr known_list = NULL;
         resource_t *child = (resource_t *) gIter->data;
 
         rsc_known_on(child, &known_list);
 
         gIter2 = known_list;
         for (; gIter2 != NULL; gIter2 = gIter2->next) {
             node_t *known = (node_t *) gIter2->data;
 
             if (node->details == known->details) {
                 g_list_free(known_list);
                 return child;
             }
         }
         g_list_free(known_list);
     }
 
     return NULL;
 }
 
 gboolean
 clone_create_probe(resource_t * rsc, node_t * node, action_t * complete,
                    gboolean force, pe_working_set_t * data_set)
 {
     GListPtr gIter = NULL;
     gboolean any_created = FALSE;
     clone_variant_data_t *clone_data = NULL;
 
     CRM_ASSERT(rsc);
     get_clone_variant_data(clone_data, rsc);
 
     rsc->children = g_list_sort(rsc->children, sort_rsc_id);
     if (rsc->children == NULL) {
         pe_warn("Clone %s has no children", rsc->id);
         return FALSE;
     }
 
     if (rsc->exclusive_discover) {
         node_t *allowed = g_hash_table_lookup(rsc->allowed_nodes, node->details->id);
         if (allowed && allowed->rsc_discover_mode != discover_exclusive) {
             /* exclusive discover is enabled and this node is not marked
              * as a node this resource should be discovered on
              *
              * remove the node from allowed_nodes so that the
              * notification contains only nodes that we might ever run
              * on
              */
             g_hash_table_remove(rsc->allowed_nodes, node->details->id);
 
             /* Bit of a shortcut - might as well take it */
             return FALSE;
         }
     }
 
     if (is_not_set(rsc->flags, pe_rsc_unique)
         && clone_data->clone_node_max == 1) {
         /* only look for one copy */
         resource_t *child = NULL;
 
         /* Try whoever we probed last time */
         child = find_instance_on(rsc, node);
         if (child) {
             return child->cmds->create_probe(child, node, complete, force, data_set);
         }
 
         /* Try whoever we plan on starting there */
         gIter = rsc->children;
         for (; gIter != NULL; gIter = gIter->next) {
             node_t *local_node = NULL;
             resource_t *child_rsc = (resource_t *) gIter->data;
 
             CRM_ASSERT(child_rsc);
             local_node = child_rsc->fns->location(child_rsc, NULL, FALSE);
             if (local_node == NULL) {
                 continue;
             }
 
             if (local_node->details == node->details) {
                 return child_rsc->cmds->create_probe(child_rsc, node, complete, force, data_set);
             }
         }
 
         /* Fall back to the first clone instance */
         CRM_ASSERT(rsc->children);
         child = rsc->children->data;
         return child->cmds->create_probe(child, node, complete, force, data_set);
     }
 
     gIter = rsc->children;
     for (; gIter != NULL; gIter = gIter->next) {
         resource_t *child_rsc = (resource_t *) gIter->data;
 
         if (child_rsc->cmds->create_probe(child_rsc, node, complete, force, data_set)) {
             any_created = TRUE;
         }
 
         if (any_created && is_not_set(rsc->flags, pe_rsc_unique)
             && clone_data->clone_node_max == 1) {
             /* only look for one copy (clone :0) */
             break;
         }
     }
 
     return any_created;
 }
 
 void
 clone_append_meta(resource_t * rsc, xmlNode * xml)
 {
     char *name = NULL;
     clone_variant_data_t *clone_data = NULL;
 
     get_clone_variant_data(clone_data, rsc);
 
     name = crm_meta_name(XML_RSC_ATTR_UNIQUE);
     crm_xml_add(xml, name, is_set(rsc->flags, pe_rsc_unique) ? "true" : "false");
     free(name);
 
     name = crm_meta_name(XML_RSC_ATTR_NOTIFY);
     crm_xml_add(xml, name, is_set(rsc->flags, pe_rsc_notify) ? "true" : "false");
     free(name);
 
     name = crm_meta_name(XML_RSC_ATTR_INCARNATION_MAX);
     crm_xml_add_int(xml, name, clone_data->clone_max);
     free(name);
 
     name = crm_meta_name(XML_RSC_ATTR_INCARNATION_NODEMAX);
     crm_xml_add_int(xml, name, clone_data->clone_node_max);
     free(name);
 }
 
 GHashTable *
 clone_merge_weights(resource_t * rsc, const char *rhs, GHashTable * nodes, const char *attr,
                     float factor, enum pe_weights flags)
 {
     return rsc_merge_weights(rsc, rhs, nodes, attr, factor, flags);
 }
diff --git a/pengine/container.c b/pengine/container.c
index 58f6fca734..d648e0f0b3 100644
--- a/pengine/container.c
+++ b/pengine/container.c
@@ -1,497 +1,575 @@
 /* 
  * Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
  * 
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public
  * License as published by the Free Software Foundation; either
  * version 2 of the License, or (at your option) any later version.
  * 
  * This software is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  * General Public License for more details.
  * 
  * You should have received a copy of the GNU General Public
  * License along with this library; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  */
 
 #include <crm_internal.h>
 
 #include <crm/msg_xml.h>
 #include <allocate.h>
 #include <notif.h>
 #include <utils.h>
 
 #define VARIANT_CONTAINER 1
 #include <lib/pengine/variant.h>
 
 static bool
 is_child_container_node(container_variant_data_t *data, pe_node_t *node)
 {
     for (GListPtr gIter = data->tuples; gIter != NULL; gIter = gIter->next) {
         container_grouping_t *tuple = (container_grouping_t *)gIter->data;
         if(node->details == tuple->node->details) {
             return TRUE;
         }
     }
     return FALSE;
 }
 
 gint sort_clone_instance(gconstpointer a, gconstpointer b, gpointer data_set);
 void distribute_children(resource_t *rsc, GListPtr children, GListPtr nodes,
                          int max, int per_host_max, pe_working_set_t * data_set);
 
 node_t *
 container_color(resource_t * rsc, node_t * prefer, pe_working_set_t * data_set)
 {
     GListPtr containers = NULL;
     GListPtr nodes = NULL;
     container_variant_data_t *container_data = NULL;
 
     CRM_CHECK(rsc != NULL, return NULL);
 
     get_container_variant_data(container_data, rsc);
 
     set_bit(rsc->flags, pe_rsc_allocating);
 
     for (GListPtr gIter = container_data->tuples; gIter != NULL; gIter = gIter->next) {
         container_grouping_t *tuple = (container_grouping_t *)gIter->data;
         containers = g_list_append(containers, tuple->docker);
     }
 
     dump_node_scores(show_scores ? 0 : scores_log_level, rsc, __FUNCTION__, rsc->allowed_nodes);
 
     nodes = g_hash_table_get_values(rsc->allowed_nodes);
     nodes = g_list_sort_with_data(nodes, sort_node_weight, NULL);
     containers = g_list_sort_with_data(containers, sort_clone_instance, data_set);
     distribute_children(rsc, containers, nodes,
                         container_data->replicas, container_data->replicas_per_host, data_set);
     g_list_free(nodes);
     g_list_free(containers);
 
     for (GListPtr gIter = container_data->tuples; gIter != NULL; gIter = gIter->next) {
         container_grouping_t *tuple = (container_grouping_t *)gIter->data;
 
         CRM_ASSERT(tuple);
         if(tuple->ip) {
             tuple->ip->cmds->allocate(tuple->ip, prefer, data_set);
         }
         if(tuple->remote) {
             tuple->remote->cmds->allocate(tuple->remote, prefer, data_set);
         }
 
         // Explicitly allocate tuple->child before the container->child
         if(tuple->child) {
             pe_node_t *node = NULL;
             GHashTableIter iter;
             g_hash_table_iter_init(&iter, tuple->child->allowed_nodes);
             while (g_hash_table_iter_next(&iter, NULL, (gpointer *) & node)) {
                 if(node->details != tuple->node->details) {
                     node->weight = -INFINITY;
                 } else {
                     node->weight = INFINITY;
                 }
             }
 
             set_bit(tuple->child->parent->flags, pe_rsc_allocating);
             tuple->child->cmds->allocate(tuple->child, tuple->node, data_set);
             clear_bit(tuple->child->parent->flags, pe_rsc_allocating);
         }
     }
 
     if(container_data->child) {
         pe_node_t *node = NULL;
         GHashTableIter iter;
         g_hash_table_iter_init(&iter, container_data->child->allowed_nodes);
         while (g_hash_table_iter_next(&iter, NULL, (gpointer *) & node)) {
             if(is_child_container_node(container_data, node)) {
                 node->weight = 0;
             } else {
                 node->weight = -INFINITY;
             }
         }
         container_data->child->cmds->allocate(container_data->child, prefer, data_set);
     }
 
     clear_bit(rsc->flags, pe_rsc_allocating);
     clear_bit(rsc->flags, pe_rsc_provisional);
     return NULL;
 }
 
 void
 container_create_actions(resource_t * rsc, pe_working_set_t * data_set)
 {
+    pe_action_t *action = NULL;
+    GListPtr containers = NULL;
     container_variant_data_t *container_data = NULL;
 
     CRM_CHECK(rsc != NULL, return);
 
     get_container_variant_data(container_data, rsc);
     for (GListPtr gIter = container_data->tuples; gIter != NULL; gIter = gIter->next) {
         container_grouping_t *tuple = (container_grouping_t *)gIter->data;
 
         CRM_ASSERT(tuple);
         if(tuple->ip) {
             tuple->ip->cmds->create_actions(tuple->ip, data_set);
         }
         if(tuple->docker) {
             tuple->docker->cmds->create_actions(tuple->docker, data_set);
+            containers = g_list_append(containers, tuple->docker);
         }
         if(tuple->remote) {
             tuple->remote->cmds->create_actions(tuple->remote, data_set);
         }
     }
 
+    clone_create_pseudo_actions(rsc, containers, NULL, NULL,  data_set);
+
     if(container_data->child) {
         container_data->child->cmds->create_actions(container_data->child, data_set);
+
+        if(container_data->child->variant == pe_master) {
+            /* promote */
+            action = create_pseudo_resource_op(rsc, RSC_PROMOTE, TRUE, TRUE, data_set);
+            action = create_pseudo_resource_op(rsc, RSC_PROMOTED, TRUE, TRUE, data_set);
+            action->priority = INFINITY;
+
+            /* demote */
+            action = create_pseudo_resource_op(rsc, RSC_DEMOTE, TRUE, TRUE, data_set);
+            action = create_pseudo_resource_op(rsc, RSC_DEMOTED, TRUE, TRUE, data_set);
+            action->priority = INFINITY;
+        }
     }
+
+    g_list_free(containers);
 }
 
 void
 container_internal_constraints(resource_t * rsc, pe_working_set_t * data_set)
 {
     container_variant_data_t *container_data = NULL;
 
     CRM_CHECK(rsc != NULL, return);
 
     get_container_variant_data(container_data, rsc);
+
+    if(container_data->child) {
+        new_rsc_order(rsc, RSC_START, container_data->child, RSC_START, pe_order_implies_first_printed, data_set);
+        new_rsc_order(rsc, RSC_STOP, container_data->child, RSC_STOP, pe_order_implies_first_printed, data_set);
+
+        if(container_data->child->children) {
+            new_rsc_order(container_data->child, RSC_STARTED, rsc, RSC_STARTED, pe_order_implies_then_printed, data_set);
+            new_rsc_order(container_data->child, RSC_STOPPED, rsc, RSC_STOPPED, pe_order_implies_then_printed, data_set);
+        } else {
+            new_rsc_order(container_data->child, RSC_START, rsc, RSC_STARTED, pe_order_implies_then_printed, data_set);
+            new_rsc_order(container_data->child, RSC_STOP, rsc, RSC_STOPPED, pe_order_implies_then_printed, data_set);
+        }
+    }
+
     for (GListPtr gIter = container_data->tuples; gIter != NULL; gIter = gIter->next) {
         container_grouping_t *tuple = (container_grouping_t *)gIter->data;
 
         CRM_ASSERT(tuple);
-        if(tuple->docker) {
-            tuple->docker->cmds->internal_constraints(tuple->docker, data_set);
+        CRM_ASSERT(tuple->docker);
+
+        tuple->docker->cmds->internal_constraints(tuple->docker, data_set);
+
+        order_start_start(rsc, tuple->docker, pe_order_runnable_left | pe_order_implies_first_printed);
+
+        if(tuple->child) {
+            order_stop_stop(rsc, tuple->child, pe_order_implies_first_printed);
+
+        } else {
+            order_stop_stop(rsc, tuple->docker, pe_order_implies_first_printed);
+            new_rsc_order(tuple->docker, RSC_START, rsc, RSC_STARTED, pe_order_implies_then_printed, data_set);
+            new_rsc_order(tuple->docker, RSC_STOP, rsc, RSC_STOPPED, pe_order_implies_then_printed,
+                          data_set);
         }
 
         if(tuple->ip) {
             tuple->ip->cmds->internal_constraints(tuple->ip, data_set);
 
             // Start ip then docker
             new_rsc_order(tuple->ip, RSC_START, tuple->docker, RSC_START,
                           pe_order_runnable_left|pe_order_preserve, data_set);
             new_rsc_order(tuple->docker, RSC_STOP, tuple->ip, RSC_STOP,
                           pe_order_implies_first|pe_order_preserve, data_set);
 
             rsc_colocation_new("ip-with-docker", NULL, INFINITY, tuple->ip, tuple->docker, NULL, NULL, data_set);
         }
 
         if(tuple->remote) {
             /* This handles ordering and colocating remote relative to docker
              * (via "resource-with-container"). Since IP is also ordered and
              * colocated relative to docker, we don't need to do anything
              * explicit here with IP.
              */
             tuple->remote->cmds->internal_constraints(tuple->remote, data_set);
         }
 
         if(tuple->child) {
             CRM_ASSERT(tuple->remote);
 
             // Start of the remote then child is implicit in the PE's remote logic
         }
 
     }
 
     if(container_data->child) {
         container_data->child->cmds->internal_constraints(container_data->child, data_set);
+        if(container_data->child->variant == pe_master) {
+            master_promotion_constraints(rsc, data_set);
+
+            /* child demoted before global demoted */
+            new_rsc_order(container_data->child, RSC_DEMOTED, rsc, RSC_DEMOTED, pe_order_implies_then_printed, data_set);
+
+            /* global demote before child demote */
+            new_rsc_order(rsc, RSC_DEMOTE, container_data->child, RSC_DEMOTE, pe_order_implies_first_printed, data_set);
+
+            /* child promoted before global promoted */
+            new_rsc_order(container_data->child, RSC_PROMOTED, rsc, RSC_PROMOTED, pe_order_implies_then_printed, data_set);
+
+            /* global promote before child promote */
+            new_rsc_order(rsc, RSC_PROMOTE, container_data->child, RSC_PROMOTE, pe_order_implies_first_printed, data_set);
+        }
+
+    } else {
+//    int type = pe_order_optional | pe_order_implies_then | pe_order_restart;
+//        custom_action_order(rsc, generate_op_key(rsc->id, RSC_STOP, 0), NULL,
+//                            rsc, generate_op_key(rsc->id, RSC_START, 0), NULL, pe_order_optional, data_set);
     }
 }
 
 
+
 static resource_t *
 find_compatible_tuple_by_node(resource_t * rsc_lh, node_t * candidate, resource_t * rsc,
                               enum rsc_role_e filter, gboolean current)
 {
     container_variant_data_t *container_data = NULL;
 
     CRM_CHECK(candidate != NULL, return NULL);
     get_container_variant_data(container_data, rsc);
 
     crm_trace("Looking for compatible child from %s for %s on %s",
               rsc_lh->id, rsc->id, candidate->details->uname);
 
     for (GListPtr gIter = container_data->tuples; gIter != NULL; gIter = gIter->next) {
         container_grouping_t *tuple = (container_grouping_t *)gIter->data;
 
         if(is_child_compatible(tuple->docker, candidate, filter, current)) {
             crm_trace("Pairing %s with %s on %s",
                       rsc_lh->id, tuple->docker->id, candidate->details->uname);
             return tuple->docker;
         }
     }
 
     crm_trace("Can't pair %s with %s", rsc_lh->id, rsc->id);
     return NULL;
 }
 
 static resource_t *
 find_compatible_tuple(resource_t *rsc_lh, resource_t * rsc, enum rsc_role_e filter,
                       gboolean current)
 {
     GListPtr scratch = NULL;
     resource_t *pair = NULL;
     node_t *active_node_lh = NULL;
 
     active_node_lh = rsc_lh->fns->location(rsc_lh, NULL, current);
     if (active_node_lh) {
         return find_compatible_tuple_by_node(rsc_lh, active_node_lh, rsc, filter, current);
     }
 
     scratch = g_hash_table_get_values(rsc_lh->allowed_nodes);
     scratch = g_list_sort_with_data(scratch, sort_node_weight, NULL);
 
     for (GListPtr gIter = scratch; gIter != NULL; gIter = gIter->next) {
         node_t *node = (node_t *) gIter->data;
 
         pair = find_compatible_tuple_by_node(rsc_lh, node, rsc, filter, current);
         if (pair) {
             goto done;
         }
     }
 
     pe_rsc_debug(rsc, "Can't pair %s with %s", rsc_lh->id, rsc->id);
   done:
     g_list_free(scratch);
     return pair;
 }
 
 void
 container_rsc_colocation_lh(resource_t * rsc, resource_t * rsc_rh, rsc_colocation_t * constraint)
 {
     /* -- Never called --
      *
      * Instead we add the colocation constraints to the child and call from there
      */
     CRM_ASSERT(FALSE);
 }
 
 void
 container_rsc_colocation_rh(resource_t * rsc_lh, resource_t * rsc, rsc_colocation_t * constraint)
 {
     GListPtr allocated_rhs = NULL;
     container_variant_data_t *container_data = NULL;
 
     CRM_CHECK(constraint != NULL, return);
     CRM_CHECK(rsc_lh != NULL, pe_err("rsc_lh was NULL for %s", constraint->id); return);
     CRM_CHECK(rsc != NULL, pe_err("rsc was NULL for %s", constraint->id); return);
 
     if (is_set(rsc->flags, pe_rsc_provisional)) {
         pe_rsc_trace(rsc, "%s is still provisional", rsc->id);
         return;
 
     } else if(constraint->rsc_lh->variant > pe_group) {
         resource_t *rh_child = find_compatible_tuple(rsc_lh, rsc, RSC_ROLE_UNKNOWN, FALSE);
 
         if (rh_child) {
             pe_rsc_debug(rsc, "Pairing %s with %s", rsc_lh->id, rh_child->id);
             rsc_lh->cmds->rsc_colocation_lh(rsc_lh, rh_child, constraint);
 
         } else if (constraint->score >= INFINITY) {
             crm_notice("Cannot pair %s with instance of %s", rsc_lh->id, rsc->id);
             assign_node(rsc_lh, NULL, TRUE);
 
         } else {
             pe_rsc_debug(rsc, "Cannot pair %s with instance of %s", rsc_lh->id, rsc->id);
         }
 
         return;
     }
 
     get_container_variant_data(container_data, rsc);
     pe_rsc_trace(rsc, "Processing constraint %s: %s -> %s %d",
                  constraint->id, rsc_lh->id, rsc->id, constraint->score);
 
     for (GListPtr gIter = container_data->tuples; gIter != NULL; gIter = gIter->next) {
         container_grouping_t *tuple = (container_grouping_t *)gIter->data;
 
         if (constraint->score < INFINITY) {
             tuple->docker->cmds->rsc_colocation_rh(rsc_lh, tuple->docker, constraint);
 
         } else {
             node_t *chosen = tuple->docker->fns->location(tuple->docker, NULL, FALSE);
 
             if (chosen != NULL && is_set_recursive(tuple->docker, pe_rsc_block, TRUE) == FALSE) {
                 pe_rsc_trace(rsc, "Allowing %s: %s %d", constraint->id, chosen->details->uname, chosen->weight);
                 allocated_rhs = g_list_prepend(allocated_rhs, chosen);
             }
         }
     }
 
     if (constraint->score >= INFINITY) {
         node_list_exclude(rsc_lh->allowed_nodes, allocated_rhs, FALSE);
     }
     g_list_free(allocated_rhs);
 }
 
 enum pe_action_flags
 container_action_flags(action_t * action, node_t * node)
 {
     enum pe_action_flags flags = (pe_action_optional | pe_action_runnable | pe_action_pseudo);
     return flags;
 }
 
 
 enum pe_graph_flags
 container_update_actions(action_t * first, action_t * then, node_t * node, enum pe_action_flags flags,
                      enum pe_action_flags filter, enum pe_ordering type)
 {
     enum pe_graph_flags changed = pe_graph_none;
+
+    // At the point we need to force container X to stop because
+    // resource Y needs to stop, here is where we'd implement that
+
     return changed;
 }
 
 void
 container_rsc_location(resource_t * rsc, rsc_to_node_t * constraint)
 {
     container_variant_data_t *container_data = NULL;
     get_container_variant_data(container_data, rsc);
 
     pe_rsc_trace(rsc, "Processing location constraint %s for %s", constraint->id, rsc->id);
 
     native_rsc_location(rsc, constraint);
 
     for (GListPtr gIter = container_data->tuples; gIter != NULL; gIter = gIter->next) {
         container_grouping_t *tuple = (container_grouping_t *)gIter->data;
 
         if (tuple->docker) {
             tuple->docker->cmds->rsc_location(tuple->docker, constraint);
         }
         if(tuple->ip) {
             tuple->ip->cmds->rsc_location(tuple->ip, constraint);
         }
     }
+
+    if(container_data->child && (constraint->role_filter == RSC_ROLE_SLAVE || constraint->role_filter == RSC_ROLE_MASTER)) {
+        // Translate the node into container names running on that node
+        crm_err("Applying constraint %s", constraint->id);
+        container_data->child->cmds->rsc_location(container_data->child, constraint);
+        container_data->child->rsc_location = g_list_prepend(container_data->child->rsc_location, constraint);
+        crm_err("Added %d location constraints to %s", g_list_length(container_data->child->rsc_location), container_data->child->id);
+    }
 }
 
 void
 container_expand(resource_t * rsc, pe_working_set_t * data_set)
 {
     container_variant_data_t *container_data = NULL;
 
     CRM_CHECK(rsc != NULL, return);
 
     get_container_variant_data(container_data, rsc);
     for (GListPtr gIter = container_data->tuples; gIter != NULL; gIter = gIter->next) {
         container_grouping_t *tuple = (container_grouping_t *)gIter->data;
 
 
         CRM_ASSERT(tuple);
         if (tuple->docker && tuple->remote && tuple->docker->allocated_to
             && fix_remote_addr(tuple->remote)) {
 
             // REMOTE_CONTAINER_HACK: Allow remote nodes that start containers with pacemaker remote inside
             xmlNode *nvpair = get_xpath_object("//nvpair[@name='addr']", tuple->remote->xml, LOG_ERR);
 
             g_hash_table_replace(tuple->remote->parameters, strdup("addr"), strdup(tuple->docker->allocated_to->details->uname));
             crm_xml_add(nvpair, "value", tuple->docker->allocated_to->details->uname);
         }
         if(tuple->ip) {
             tuple->ip->cmds->expand(tuple->ip, data_set);
         }
         if(tuple->child) {
             tuple->child->cmds->expand(tuple->child, data_set);
         }
         if(tuple->docker) {
             tuple->docker->cmds->expand(tuple->docker, data_set);
         }
         if(tuple->remote) {
             tuple->remote->cmds->expand(tuple->remote, data_set);
         }
     }
 }
 
 gboolean
 container_create_probe(resource_t * rsc, node_t * node, action_t * complete,
                    gboolean force, pe_working_set_t * data_set)
 {
     bool any_created = FALSE;
     container_variant_data_t *container_data = NULL;
 
     CRM_CHECK(rsc != NULL, return FALSE);
 
     get_container_variant_data(container_data, rsc);
     for (GListPtr gIter = container_data->tuples; gIter != NULL; gIter = gIter->next) {
         container_grouping_t *tuple = (container_grouping_t *)gIter->data;
 
         CRM_ASSERT(tuple);
         if(tuple->ip) {
             any_created |= tuple->ip->cmds->create_probe(tuple->ip, node, complete, force, data_set);
         }
         if(tuple->child && node->details == tuple->node->details) {
             any_created |= tuple->child->cmds->create_probe(tuple->child, node, complete, force, data_set);
         }
         if(tuple->docker) {
             bool created = tuple->docker->cmds->create_probe(tuple->docker, node, complete, force, data_set);
 
             if(created) {
                 any_created = TRUE;
                 /* If we're limited to one replica per host (due to
                  * the lack of an IP range probably), then we don't
                  * want any of our peer containers starting until
                  * we've established that no other copies are already
                  * running.
                  *
                  * Partly this is to ensure that replicas_per_host is
                  * observed, but also to ensure that the containers
                  * don't fail to start because the necessary port
                  * mappings (which won't include an IP for uniqueness)
                  * are already taken
                  */
 
                 for (GListPtr tIter = container_data->tuples; tIter != NULL && container_data->replicas_per_host == 1; tIter = tIter->next) {
                     container_grouping_t *other = (container_grouping_t *)tIter->data;
 
                     if ((other != tuple) && (other != NULL)
                         && (other->docker != NULL)) {
 
                         custom_action_order(tuple->docker, generate_op_key(tuple->docker->id, RSC_STATUS, 0), NULL,
                                             other->docker, generate_op_key(other->docker->id, RSC_START, 0), NULL,
                                             pe_order_optional, data_set);
                     }
                 }
             }
         }
         if(FALSE && tuple->remote) {
             // TODO: Needed?
             any_created |= tuple->remote->cmds->create_probe(tuple->remote, node, complete, force, data_set);
         }
     }
     return any_created;
 }
 
 void
 container_append_meta(resource_t * rsc, xmlNode * xml)
 {
 }
 
 GHashTable *
 container_merge_weights(resource_t * rsc, const char *rhs, GHashTable * nodes, const char *attr,
                     float factor, enum pe_weights flags)
 {
     return rsc_merge_weights(rsc, rhs, nodes, attr, factor, flags);
 }
 
 void container_LogActions(
     resource_t * rsc, pe_working_set_t * data_set, gboolean terminal)
 {
     container_variant_data_t *container_data = NULL;
 
     CRM_CHECK(rsc != NULL, return);
 
     get_container_variant_data(container_data, rsc);
     for (GListPtr gIter = container_data->tuples; gIter != NULL; gIter = gIter->next) {
         container_grouping_t *tuple = (container_grouping_t *)gIter->data;
 
         CRM_ASSERT(tuple);
         if(tuple->ip) {
             LogActions(tuple->ip, data_set, terminal);
         }
         if(tuple->docker) {
             LogActions(tuple->docker, data_set, terminal);
         }
         if(tuple->remote) {
             LogActions(tuple->remote, data_set, terminal);
         }
         if(tuple->child) {
             LogActions(tuple->child, data_set, terminal);
         }
     }
 }
diff --git a/pengine/master.c b/pengine/master.c
index f6fcad3d35..93e5186da2 100644
--- a/pengine/master.c
+++ b/pengine/master.c
@@ -1,1079 +1,1063 @@
 /*
  * Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public
  * License as published by the Free Software Foundation; either
  * version 2 of the License, or (at your option) any later version.
  *
  * This software is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  * General Public License for more details.
  *
  * You should have received a copy of the GNU General Public
  * License along with this library; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  */
 
 #include <crm_internal.h>
 
 #include <crm/msg_xml.h>
 #include <allocate.h>
 #include <notif.h>
 #include <utils.h>
 
 #define VARIANT_CLONE 1
 #include <lib/pengine/variant.h>
 
 extern gint sort_clone_instance(gconstpointer a, gconstpointer b, gpointer data_set);
 
 static int master_score(resource_t * rsc, node_t * node, int not_set_value);
 
 static void
 child_promoting_constraints(clone_variant_data_t * clone_data, enum pe_ordering type,
                             resource_t * rsc, resource_t * child, resource_t * last,
                             pe_working_set_t * data_set)
 {
     if (child == NULL) {
         if (clone_data->ordered && last != NULL) {
             pe_rsc_trace(rsc, "Ordered version (last node)");
             /* last child promote before promoted started */
             new_rsc_order(last, RSC_PROMOTE, rsc, RSC_PROMOTED, type, data_set);
         }
         return;
     }
 
     /* child promote before global promoted */
     new_rsc_order(child, RSC_PROMOTE, rsc, RSC_PROMOTED, type, data_set);
 
     /* global promote before child promote */
     new_rsc_order(rsc, RSC_PROMOTE, child, RSC_PROMOTE, type, data_set);
 
     if (clone_data->ordered) {
         pe_rsc_trace(rsc, "Ordered version");
         if (last == NULL) {
             /* global promote before first child promote */
             last = rsc;
 
         }
         /* else: child/child relative promote */
         order_start_start(last, child, type);
         new_rsc_order(last, RSC_PROMOTE, child, RSC_PROMOTE, type, data_set);
 
     } else {
         pe_rsc_trace(rsc, "Un-ordered version");
     }
 }
 
 static void
 child_demoting_constraints(clone_variant_data_t * clone_data, enum pe_ordering type,
                            resource_t * rsc, resource_t * child, resource_t * last,
                            pe_working_set_t * data_set)
 {
     if (child == NULL) {
         if (clone_data->ordered && last != NULL) {
             pe_rsc_trace(rsc, "Ordered version (last node)");
             /* global demote before first child demote */
             new_rsc_order(rsc, RSC_DEMOTE, last, RSC_DEMOTE, pe_order_optional, data_set);
         }
         return;
     }
 
     /* child demote before global demoted */
     new_rsc_order(child, RSC_DEMOTE, rsc, RSC_DEMOTED, pe_order_implies_then_printed, data_set);
 
     /* global demote before child demote */
     new_rsc_order(rsc, RSC_DEMOTE, child, RSC_DEMOTE, pe_order_implies_first_printed, data_set);
 
     if (clone_data->ordered && last != NULL) {
         pe_rsc_trace(rsc, "Ordered version");
 
         /* child/child relative demote */
         new_rsc_order(child, RSC_DEMOTE, last, RSC_DEMOTE, type, data_set);
 
     } else if (clone_data->ordered) {
         pe_rsc_trace(rsc, "Ordered version (1st node)");
         /* first child stop before global stopped */
         new_rsc_order(child, RSC_DEMOTE, rsc, RSC_DEMOTED, type, data_set);
 
     } else {
         pe_rsc_trace(rsc, "Un-ordered version");
     }
 }
 
 static void
 master_update_pseudo_status(resource_t * rsc, gboolean * demoting, gboolean * promoting)
 {
     GListPtr gIter = NULL;
 
     if (rsc->children) {
         gIter = rsc->children;
         for (; gIter != NULL; gIter = gIter->next) {
             resource_t *child = (resource_t *) gIter->data;
 
             master_update_pseudo_status(child, demoting, promoting);
         }
         return;
     }
 
     CRM_ASSERT(demoting != NULL);
     CRM_ASSERT(promoting != NULL);
 
     gIter = rsc->actions;
     for (; gIter != NULL; gIter = gIter->next) {
         action_t *action = (action_t *) gIter->data;
 
         if (*promoting && *demoting) {
             return;
 
         } else if (is_set(action->flags, pe_action_optional)) {
             continue;
 
         } else if (safe_str_eq(RSC_DEMOTE, action->task)) {
             *demoting = TRUE;
 
         } else if (safe_str_eq(RSC_PROMOTE, action->task)) {
             *promoting = TRUE;
         }
     }
 }
 
-#define apply_master_location(list) do {				\
-    gIter2 = list;							\
-    for(; gIter2 != NULL; gIter2 = gIter2->next) {			\
-	rsc_to_node_t *cons = (rsc_to_node_t*)gIter2->data;		\
-									\
-	cons_node = NULL;						\
-	if(cons->role_filter == RSC_ROLE_MASTER) {			\
-	    pe_rsc_trace(rsc, "Applying %s to %s",				\
-			cons->id, child_rsc->id);			\
-	    cons_node = pe_find_node_id(				\
-		cons->node_list_rh, chosen->details->id);		\
-	}								\
-	if(cons_node != NULL) {						\
-	    int new_priority = merge_weights(				\
-		child_rsc->priority, cons_node->weight);		\
-	    pe_rsc_trace(rsc, "\t%s: %d->%d (%d)", child_rsc->id,		\
-			child_rsc->priority, new_priority, cons_node->weight); \
-	    child_rsc->priority = new_priority;				\
-	}								\
-    }									\
-    } while(0)
+static void apply_master_location(resource_t *child, GListPtr location_constraints, pe_node_t *chosen)
+{
+     for(GListPtr gIter = location_constraints; gIter != NULL; gIter = gIter->next) {
+	pe_node_t *cons_node = NULL;
+	rsc_to_node_t *cons = (rsc_to_node_t*)gIter->data;
+
+	if(cons->role_filter == RSC_ROLE_MASTER) {
+	    pe_rsc_trace(child, "Applying %s to %s", cons->id, child->id);
+	    cons_node = pe_find_node_id(cons->node_list_rh, chosen->details->id);
+	}
+	if(cons_node != NULL) {
+	    int new_priority = merge_weights(child->priority, cons_node->weight);
+	    pe_rsc_trace(child, "\t%s[%s]: %d -> %d (%d)", child->id,  cons_node->details->uname,
+			child->priority, new_priority, cons_node->weight);
+	    crm_err("\t%s[%s]: %d -> %d (%d)", child->id,  cons_node->details->uname,
+			child->priority, new_priority, cons_node->weight);
+	    child->priority = new_priority;
+        }
+    }
+}
 
 static node_t *
 can_be_master(resource_t * rsc)
 {
     node_t *node = NULL;
     node_t *local_node = NULL;
     resource_t *parent = uber_parent(rsc);
     clone_variant_data_t *clone_data = NULL;
 
 #if 0
     enum rsc_role_e role = RSC_ROLE_UNKNOWN;
 
     role = rsc->fns->state(rsc, FALSE);
     crm_info("%s role: %s", rsc->id, role2text(role));
 #endif
 
     if (rsc->children) {
         GListPtr gIter = rsc->children;
 
         for (; gIter != NULL; gIter = gIter->next) {
             resource_t *child = (resource_t *) gIter->data;
 
             if (can_be_master(child) == NULL) {
                 pe_rsc_trace(rsc, "Child %s of %s can't be promoted", child->id, rsc->id);
                 return NULL;
             }
         }
     }
 
     node = rsc->fns->location(rsc, NULL, FALSE);
     if (node == NULL) {
         pe_rsc_trace(rsc, "%s cannot be master: not allocated", rsc->id);
         return NULL;
 
     } else if (is_not_set(rsc->flags, pe_rsc_managed)) {
         if (rsc->fns->state(rsc, TRUE) == RSC_ROLE_MASTER) {
             crm_notice("Forcing unmanaged master %s to remain promoted on %s",
                        rsc->id, node->details->uname);
 
         } else {
             return NULL;
         }
 
     } else if (rsc->priority < 0) {
         pe_rsc_trace(rsc, "%s cannot be master: preference: %d", rsc->id, rsc->priority);
         return NULL;
 
     } else if (can_run_resources(node) == FALSE) {
         crm_trace("Node can't run any resources: %s", node->details->uname);
         return NULL;
     }
 
     get_clone_variant_data(clone_data, parent);
     local_node = pe_hash_table_lookup(parent->allowed_nodes, node->details->id);
 
     if (local_node == NULL) {
         crm_err("%s cannot run on %s: node not allowed", rsc->id, node->details->uname);
         return NULL;
 
     } else if (local_node->count < clone_data->master_node_max
                || is_not_set(rsc->flags, pe_rsc_managed)) {
         return local_node;
 
     } else {
         pe_rsc_trace(rsc, "%s cannot be master on %s: node full", rsc->id, node->details->uname);
     }
 
     return NULL;
 }
 
 static gint
 sort_master_instance(gconstpointer a, gconstpointer b, gpointer data_set)
 {
     int rc;
     enum rsc_role_e role1 = RSC_ROLE_UNKNOWN;
     enum rsc_role_e role2 = RSC_ROLE_UNKNOWN;
 
     const resource_t *resource1 = (const resource_t *)a;
     const resource_t *resource2 = (const resource_t *)b;
 
     CRM_ASSERT(resource1 != NULL);
     CRM_ASSERT(resource2 != NULL);
 
     role1 = resource1->fns->state(resource1, TRUE);
     role2 = resource2->fns->state(resource2, TRUE);
 
     rc = sort_rsc_index(a, b);
     if (rc != 0) {
         crm_trace("%s %c %s (index)", resource1->id, rc < 0 ? '<' : '>', resource2->id);
         return rc;
     }
 
     if (role1 > role2) {
         crm_trace("%s %c %s (role)", resource1->id, '<', resource2->id);
         return -1;
 
     } else if (role1 < role2) {
         crm_trace("%s %c %s (role)", resource1->id, '>', resource2->id);
         return 1;
     }
 
     return sort_clone_instance(a, b, data_set);
 }
 
 GHashTable *
 master_merge_weights(resource_t * rsc, const char *rhs, GHashTable * nodes, const char *attr,
                      float factor, enum pe_weights flags)
 {
     return rsc_merge_weights(rsc, rhs, nodes, attr, factor, flags);
 }
 
 static void
 master_promotion_order(resource_t * rsc, pe_working_set_t * data_set)
 {
     GListPtr gIter = NULL;
     node_t *node = NULL;
     node_t *chosen = NULL;
     clone_variant_data_t *clone_data = NULL;
     char score[33];
     size_t len = sizeof(score);
 
     get_clone_variant_data(clone_data, rsc);
 
     if (clone_data->merged_master_weights) {
         return;
     }
     clone_data->merged_master_weights = TRUE;
     pe_rsc_trace(rsc, "Merging weights for %s", rsc->id);
     set_bit(rsc->flags, pe_rsc_merging);
 
-    gIter = rsc->children;
-    for (; gIter != NULL; gIter = gIter->next) {
+    for (gIter = rsc->children; gIter != NULL; gIter = gIter->next) {
         resource_t *child = (resource_t *) gIter->data;
 
         pe_rsc_trace(rsc, "Sort index: %s = %d", child->id, child->sort_index);
     }
     dump_node_scores(LOG_DEBUG_3, rsc, "Before", rsc->allowed_nodes);
 
     gIter = rsc->children;
     for (; gIter != NULL; gIter = gIter->next) {
         resource_t *child = (resource_t *) gIter->data;
 
         chosen = child->fns->location(child, NULL, FALSE);
         if (chosen == NULL || child->sort_index < 0) {
             pe_rsc_trace(rsc, "Skipping %s", child->id);
             continue;
         }
 
         node = (node_t *) pe_hash_table_lookup(rsc->allowed_nodes, chosen->details->id);
         CRM_ASSERT(node != NULL);
         /* adds in master preferences and rsc_location.role=Master */
         score2char_stack(child->sort_index, score, len);
         pe_rsc_trace(rsc, "Adding %s to %s from %s", score,
                      node->details->uname, child->id);
         node->weight = merge_weights(child->sort_index, node->weight);
     }
 
     dump_node_scores(LOG_DEBUG_3, rsc, "Middle", rsc->allowed_nodes);
 
     gIter = rsc->rsc_cons;
     for (; gIter != NULL; gIter = gIter->next) {
         rsc_colocation_t *constraint = (rsc_colocation_t *) gIter->data;
 
         /* (re-)adds location preferences of resources that the
          * master instance should/must be colocated with
          */
         if (constraint->role_lh == RSC_ROLE_MASTER) {
             enum pe_weights flags = constraint->score == INFINITY ? 0 : pe_weights_rollback;
 
             pe_rsc_trace(rsc, "RHS: %s with %s: %d", constraint->rsc_lh->id, constraint->rsc_rh->id,
                          constraint->score);
             rsc->allowed_nodes =
                 constraint->rsc_rh->cmds->merge_weights(constraint->rsc_rh, rsc->id,
                                                         rsc->allowed_nodes,
                                                         constraint->node_attribute,
                                                         (float)constraint->score / INFINITY, flags);
         }
     }
 
     gIter = rsc->rsc_cons_lhs;
     for (; gIter != NULL; gIter = gIter->next) {
         rsc_colocation_t *constraint = (rsc_colocation_t *) gIter->data;
 
         /* (re-)adds location preferences of resource that wish to be
          * colocated with the master instance
          */
         if (constraint->role_rh == RSC_ROLE_MASTER) {
             pe_rsc_trace(rsc, "LHS: %s with %s: %d", constraint->rsc_lh->id, constraint->rsc_rh->id,
                          constraint->score);
             rsc->allowed_nodes =
                 constraint->rsc_lh->cmds->merge_weights(constraint->rsc_lh, rsc->id,
                                                         rsc->allowed_nodes,
                                                         constraint->node_attribute,
                                                         (float)constraint->score / INFINITY,
                                                         (pe_weights_rollback |
                                                          pe_weights_positive));
         }
     }
 
     gIter = rsc->rsc_tickets;
     for (; gIter != NULL; gIter = gIter->next) {
         rsc_ticket_t *rsc_ticket = (rsc_ticket_t *) gIter->data;
 
         if (rsc_ticket->role_lh == RSC_ROLE_MASTER
             && (rsc_ticket->ticket->granted == FALSE || rsc_ticket->ticket->standby)) {
             resource_location(rsc, NULL, -INFINITY, "__stateful_without_ticket__", data_set);
         }
     }
 
     dump_node_scores(LOG_DEBUG_3, rsc, "After", rsc->allowed_nodes);
 
     /* write them back and sort */
 
     gIter = rsc->children;
     for (; gIter != NULL; gIter = gIter->next) {
         resource_t *child = (resource_t *) gIter->data;
 
         chosen = child->fns->location(child, NULL, FALSE);
         if (is_not_set(child->flags, pe_rsc_managed) && child->next_role == RSC_ROLE_MASTER) {
             child->sort_index = INFINITY;
 
         } else if (chosen == NULL || child->sort_index < 0) {
             pe_rsc_trace(rsc, "%s: %d", child->id, child->sort_index);
 
         } else {
             node = (node_t *) pe_hash_table_lookup(rsc->allowed_nodes, chosen->details->id);
             CRM_ASSERT(node != NULL);
 
             child->sort_index = node->weight;
         }
         pe_rsc_trace(rsc, "Set sort index: %s = %d", child->id, child->sort_index);
     }
 
     rsc->children = g_list_sort_with_data(rsc->children, sort_master_instance, data_set);
     clear_bit(rsc->flags, pe_rsc_merging);
 }
 
 static gboolean
 filter_anonymous_instance(resource_t * rsc, node_t * node)
 {
     GListPtr rIter = NULL;
     char *key = clone_strip(rsc->id);
     resource_t *parent = uber_parent(rsc);
 
     for (rIter = parent->children; rIter; rIter = rIter->next) {
         resource_t *child = rIter->data;
         resource_t *active = parent->fns->find_rsc(child, key, node, pe_find_clone|pe_find_current);
 
         /*
          * Look for an active instance on $node, if there is one, only it receives the master score
          * Use ->find_rsc() because we might be a cloned group
          */
         if(rsc == active) {
             pe_rsc_trace(rsc, "Found %s for %s active on %s: done", active->id, key, node->details->uname);
             free(key);
             return TRUE;
         } else if(active) {
             pe_rsc_trace(rsc, "Found %s for %s on %s: not %s", active->id, key, node->details->uname, rsc->id);
             free(key);
             return FALSE;
         } else {
             pe_rsc_trace(rsc, "%s on %s: not active", key, node->details->uname);
         }
     }
 
     for (rIter = parent->children; rIter; rIter = rIter->next) {
         resource_t *child = rIter->data;
 
         /*
          * We know it's not running, but any score will still count if
          * the instance has been probed on $node
          *
          * Again use ->find_rsc() because we might be a cloned group
          * and knowing that other members of the group are known here
          * implies nothing
          */
         rsc = parent->fns->find_rsc(child, key, NULL, pe_find_clone);
         CRM_LOG_ASSERT(rsc);
         if(rsc) {
             pe_rsc_trace(rsc, "Checking %s for %s on %s", rsc->id, key, node->details->uname);
             if (g_hash_table_lookup(rsc->known_on, node->details->id)) {
                 free(key);
                 return TRUE;
             }
         }
     }
     free(key);
     return FALSE;
 }
 
 static int
 master_score(resource_t * rsc, node_t * node, int not_set_value)
 {
     char *attr_name;
     char *name = rsc->id;
     const char *attr_value = NULL;
     int score = not_set_value, len = 0;
 
     if (rsc->children) {
         GListPtr gIter = rsc->children;
 
         for (; gIter != NULL; gIter = gIter->next) {
             resource_t *child = (resource_t *) gIter->data;
             int c_score = master_score(child, node, not_set_value);
 
             if (score == not_set_value) {
                 score = c_score;
             } else {
                 score += c_score;
             }
         }
         return score;
     }
 
     if (node == NULL) {
         if (rsc->fns->state(rsc, TRUE) < RSC_ROLE_STARTED) {
             pe_rsc_trace(rsc, "Ignoring master score for %s: unknown state", rsc->id);
             return score;
         }
 
     } else {
         node_t *match = pe_find_node_id(rsc->running_on, node->details->id);
         node_t *known = pe_hash_table_lookup(rsc->known_on, node->details->id);
 
         if (is_not_set(rsc->flags, pe_rsc_unique) && filter_anonymous_instance(rsc, node)) {
             pe_rsc_trace(rsc, "Anonymous clone %s is allowed on %s", rsc->id, node->details->uname);
 
         } else if (match == NULL && known == NULL) {
             pe_rsc_trace(rsc, "%s (aka. %s) has been filtered on %s - ignoring", rsc->id,
                          rsc->clone_name, node->details->uname);
             return score;
         }
 
         match = pe_hash_table_lookup(rsc->allowed_nodes, node->details->id);
         if (match == NULL) {
             return score;
 
         } else if (match->weight < 0) {
             pe_rsc_trace(rsc, "%s on %s has score: %d - ignoring",
                          rsc->id, match->details->uname, match->weight);
             return score;
         }
     }
 
     if (rsc->clone_name) {
         /* Use the name the lrm knows this resource as,
          * since that's what crm_master would have used too
          */
         name = rsc->clone_name;
     }
 
     len = 8 + strlen(name);
     attr_name = calloc(1, len);
     sprintf(attr_name, "master-%s", name);
 
     if (node) {
         attr_value = g_hash_table_lookup(node->details->attrs, attr_name);
         pe_rsc_trace(rsc, "%s: %s[%s] = %s", rsc->id, attr_name, node->details->uname,
                      crm_str(attr_value));
     }
 
     if (attr_value != NULL) {
         score = char2score(attr_value);
     }
 
     free(attr_name);
     return score;
 }
 
 #define max(a, b) a<b?b:a
 
 static void
 apply_master_prefs(resource_t * rsc)
 {
     int score, new_score;
     GListPtr gIter = rsc->children;
     clone_variant_data_t *clone_data = NULL;
 
     get_clone_variant_data(clone_data, rsc);
 
     if (clone_data->applied_master_prefs) {
         /* Make sure we only do this once */
         return;
     }
 
     clone_data->applied_master_prefs = TRUE;
 
     for (; gIter != NULL; gIter = gIter->next) {
         GHashTableIter iter;
         node_t *node = NULL;
         resource_t *child_rsc = (resource_t *) gIter->data;
 
         g_hash_table_iter_init(&iter, child_rsc->allowed_nodes);
         while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) {
             if (can_run_resources(node) == FALSE) {
                 /* This node will never be promoted to master,
                  *  so don't apply the master score as that may
                  *  lead to clone shuffling
                  */
                 continue;
             }
 
             score = master_score(child_rsc, node, 0);
             if (score > 0) {
                 new_score = merge_weights(node->weight, score);
                 if (new_score != node->weight) {
                     pe_rsc_trace(rsc, "\t%s: Updating preference for %s (%d->%d)",
                                  child_rsc->id, node->details->uname, node->weight, new_score);
                     node->weight = new_score;
                 }
             }
 
             new_score = max(child_rsc->priority, score);
             if (new_score != child_rsc->priority) {
                 pe_rsc_trace(rsc, "\t%s: Updating priority (%d->%d)",
                              child_rsc->id, child_rsc->priority, new_score);
                 child_rsc->priority = new_score;
             }
         }
     }
 }
 
 static void
 set_role_slave(resource_t * rsc, gboolean current)
 {
     GListPtr gIter = rsc->children;
 
     if (current) {
         if (rsc->role == RSC_ROLE_STARTED) {
             rsc->role = RSC_ROLE_SLAVE;
         }
 
     } else {
         GListPtr allocated = NULL;
 
         rsc->fns->location(rsc, &allocated, FALSE);
 
         if (allocated) {
             rsc->next_role = RSC_ROLE_SLAVE;
 
         } else {
             rsc->next_role = RSC_ROLE_STOPPED;
         }
         g_list_free(allocated);
     }
 
     for (; gIter != NULL; gIter = gIter->next) {
         resource_t *child_rsc = (resource_t *) gIter->data;
 
         set_role_slave(child_rsc, current);
     }
 }
 
 static void
 set_role_master(resource_t * rsc)
 {
     GListPtr gIter = rsc->children;
 
     if (rsc->next_role == RSC_ROLE_UNKNOWN) {
         rsc->next_role = RSC_ROLE_MASTER;
     }
 
     for (; gIter != NULL; gIter = gIter->next) {
         resource_t *child_rsc = (resource_t *) gIter->data;
 
         set_role_master(child_rsc);
     }
 }
 
 node_t *
 master_color(resource_t * rsc, node_t * prefer, pe_working_set_t * data_set)
 {
     int promoted = 0;
     GListPtr gIter = NULL;
     GListPtr gIter2 = NULL;
 
     GHashTableIter iter;
     node_t *node = NULL;
     node_t *chosen = NULL;
-    node_t *cons_node = NULL;
     enum rsc_role_e next_role = RSC_ROLE_UNKNOWN;
 
     char score[33];
     size_t len = sizeof(score);
 
     clone_variant_data_t *clone_data = NULL;
 
     get_clone_variant_data(clone_data, rsc);
 
     if (is_not_set(rsc->flags, pe_rsc_provisional)) {
         return NULL;
 
     } else if (is_set(rsc->flags, pe_rsc_allocating)) {
         pe_rsc_debug(rsc, "Dependency loop detected involving %s", rsc->id);
         return NULL;
     }
 
     apply_master_prefs(rsc);
 
     clone_color(rsc, prefer, data_set);
 
     set_bit(rsc->flags, pe_rsc_allocating);
 
     /* count now tracks the number of masters allocated */
     g_hash_table_iter_init(&iter, rsc->allowed_nodes);
     while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) {
         node->count = 0;
     }
 
     /*
      * assign priority
      */
-    gIter = rsc->children;
-    for (; gIter != NULL; gIter = gIter->next) {
+    for (gIter = rsc->children; gIter != NULL; gIter = gIter->next) {
         GListPtr list = NULL;
         resource_t *child_rsc = (resource_t *) gIter->data;
 
         pe_rsc_trace(rsc, "Assigning priority for %s: %s", child_rsc->id,
                      role2text(child_rsc->next_role));
 
         if (child_rsc->fns->state(child_rsc, TRUE) == RSC_ROLE_STARTED) {
             set_role_slave(child_rsc, TRUE);
         }
 
         chosen = child_rsc->fns->location(child_rsc, &list, FALSE);
         if (g_list_length(list) > 1) {
             crm_config_err("Cannot promote non-colocated child %s", child_rsc->id);
         }
 
         g_list_free(list);
         if (chosen == NULL) {
             continue;
         }
 
         next_role = child_rsc->fns->state(child_rsc, FALSE);
         switch (next_role) {
             case RSC_ROLE_STARTED:
             case RSC_ROLE_UNKNOWN:
                 CRM_CHECK(chosen != NULL, break);
                 /*
                  * Default to -1 if no value is set
                  *
                  * This allows master locations to be specified
                  * based solely on rsc_location constraints,
                  * but prevents anyone from being promoted if
                  * neither a constraint nor a master-score is present
                  */
                 child_rsc->priority = master_score(child_rsc, chosen, -1);
                 break;
 
             case RSC_ROLE_SLAVE:
             case RSC_ROLE_STOPPED:
                 child_rsc->priority = -INFINITY;
                 break;
             case RSC_ROLE_MASTER:
                 /* We will arrive here if we're re-creating actions after a stonith
                  */
                 break;
             default:
                 CRM_CHECK(FALSE /* unhandled */ ,
                           crm_err("Unknown resource role: %d for %s", next_role, child_rsc->id));
         }
 
-        apply_master_location(child_rsc->rsc_location);
-        apply_master_location(rsc->rsc_location);
+        apply_master_location(child_rsc, child_rsc->rsc_location, chosen);
+        crm_err("Applying %d location constraints for %s", g_list_length(rsc->rsc_location), rsc->id);
+        apply_master_location(child_rsc, rsc->rsc_location, chosen);
 
-        gIter2 = child_rsc->rsc_cons;
-        for (; gIter2 != NULL; gIter2 = gIter2->next) {
+        for (gIter2 = child_rsc->rsc_cons; gIter2 != NULL; gIter2 = gIter2->next) {
             rsc_colocation_t *cons = (rsc_colocation_t *) gIter2->data;
 
             child_rsc->cmds->rsc_colocation_lh(child_rsc, cons->rsc_rh, cons);
         }
 
         child_rsc->sort_index = child_rsc->priority;
         pe_rsc_trace(rsc, "Assigning priority for %s: %d", child_rsc->id, child_rsc->priority);
 
         if (next_role == RSC_ROLE_MASTER) {
             child_rsc->sort_index = INFINITY;
         }
     }
 
     dump_node_scores(LOG_DEBUG_3, rsc, "Pre merge", rsc->allowed_nodes);
     master_promotion_order(rsc, data_set);
 
     /* mark the first N as masters */
 
-    gIter = rsc->children;
-    for (; gIter != NULL; gIter = gIter->next) {
+    for (gIter = rsc->children; gIter != NULL; gIter = gIter->next) {
         resource_t *child_rsc = (resource_t *) gIter->data;
         score2char_stack(child_rsc->sort_index, score, len);
 
         chosen = child_rsc->fns->location(child_rsc, NULL, FALSE);
         if (show_scores) {
             fprintf(stdout, "%s promotion score on %s: %s\n",
                     child_rsc->id, chosen ? chosen->details->uname : "none", score);
 
         } else {
             do_crm_log(scores_log_level, "%s promotion score on %s: %s",
                        child_rsc->id, chosen ? chosen->details->uname : "none", score);
         }
 
         chosen = NULL;          /* nuke 'chosen' so that we don't promote more than the
                                  * required number of instances
                                  */
 
         if (child_rsc->sort_index < 0) {
             pe_rsc_trace(rsc, "Not supposed to promote child: %s", child_rsc->id);
 
         } else if (promoted < clone_data->master_max || is_not_set(rsc->flags, pe_rsc_managed)) {
             chosen = can_be_master(child_rsc);
         }
 
         pe_rsc_debug(rsc, "%s master score: %d", child_rsc->id, child_rsc->priority);
 
         if (chosen == NULL) {
             set_role_slave(child_rsc, FALSE);
             continue;
 
         } else if(child_rsc->role < RSC_ROLE_MASTER
               && is_set(data_set->flags, pe_flag_have_quorum) == FALSE
               && data_set->no_quorum_policy == no_quorum_freeze) {
             crm_notice("Resource %s cannot be elevated from %s to %s: no-quorum-policy=freeze",
                        child_rsc->id, role2text(child_rsc->role), role2text(child_rsc->next_role));
             set_role_slave(child_rsc, FALSE);
             continue;
         }
 
         chosen->count++;
         pe_rsc_info(rsc, "Promoting %s (%s %s)",
                     child_rsc->id, role2text(child_rsc->role), chosen->details->uname);
         set_role_master(child_rsc);
         promoted++;
     }
 
     clone_data->masters_allocated = promoted;
     pe_rsc_info(rsc, "%s: Promoted %d instances of a possible %d to master",
                 rsc->id, promoted, clone_data->master_max);
 
     clear_bit(rsc->flags, pe_rsc_provisional);
     clear_bit(rsc->flags, pe_rsc_allocating);
 
     return NULL;
 }
 
 void
 master_create_actions(resource_t * rsc, pe_working_set_t * data_set)
 {
     action_t *action = NULL;
     GListPtr gIter = rsc->children;
     action_t *action_complete = NULL;
     gboolean any_promoting = FALSE;
     gboolean any_demoting = FALSE;
     resource_t *last_promote_rsc = NULL;
     resource_t *last_demote_rsc = NULL;
 
     clone_variant_data_t *clone_data = NULL;
 
     get_clone_variant_data(clone_data, rsc);
 
     pe_rsc_debug(rsc, "Creating actions for %s", rsc->id);
 
     /* create actions as normal */
     clone_create_actions(rsc, data_set);
 
     for (; gIter != NULL; gIter = gIter->next) {
         gboolean child_promoting = FALSE;
         gboolean child_demoting = FALSE;
         resource_t *child_rsc = (resource_t *) gIter->data;
 
         pe_rsc_trace(rsc, "Creating actions for %s", child_rsc->id);
         child_rsc->cmds->create_actions(child_rsc, data_set);
         master_update_pseudo_status(child_rsc, &child_demoting, &child_promoting);
 
         any_demoting = any_demoting || child_demoting;
         any_promoting = any_promoting || child_promoting;
         pe_rsc_trace(rsc, "Created actions for %s: %d %d", child_rsc->id, child_promoting,
                      child_demoting);
     }
 
     /* promote */
-    action = promote_action(rsc, NULL, !any_promoting);
-    action_complete = custom_action(rsc, promoted_key(rsc),
-                                    RSC_PROMOTED, NULL, !any_promoting, TRUE, data_set);
-
+    action = create_pseudo_resource_op(rsc, RSC_PROMOTE, !any_promoting, TRUE, data_set);
+    action_complete = create_pseudo_resource_op(rsc, RSC_PROMOTED, !any_promoting, TRUE, data_set);
     action_complete->priority = INFINITY;
-    update_action_flags(action, pe_action_pseudo, __FUNCTION__, __LINE__);
-    update_action_flags(action, pe_action_runnable, __FUNCTION__, __LINE__);
-    update_action_flags(action_complete, pe_action_pseudo, __FUNCTION__, __LINE__);
-    update_action_flags(action_complete, pe_action_runnable, __FUNCTION__, __LINE__);
-
-    if (clone_data->masters_allocated > 0) {
-        update_action_flags(action, pe_action_runnable, __FUNCTION__, __LINE__);
-        update_action_flags(action_complete, pe_action_runnable, __FUNCTION__, __LINE__);
-    }
 
     child_promoting_constraints(clone_data, pe_order_optional,
                                 rsc, NULL, last_promote_rsc, data_set);
 
     if (clone_data->promote_notify == NULL) {
         clone_data->promote_notify =
             create_notification_boundaries(rsc, RSC_PROMOTE, action, action_complete, data_set);
     }
 
     /* demote */
-    action = demote_action(rsc, NULL, !any_demoting);
-    action_complete = custom_action(rsc, demoted_key(rsc),
-                                    RSC_DEMOTED, NULL, !any_demoting, TRUE, data_set);
+    action = create_pseudo_resource_op(rsc, RSC_DEMOTE, !any_demoting, TRUE, data_set);
+    action_complete = create_pseudo_resource_op(rsc, RSC_DEMOTED, !any_demoting, TRUE, data_set);
     action_complete->priority = INFINITY;
 
-    update_action_flags(action, pe_action_pseudo, __FUNCTION__, __LINE__);
-    update_action_flags(action, pe_action_runnable, __FUNCTION__, __LINE__);
-    update_action_flags(action_complete, pe_action_pseudo, __FUNCTION__, __LINE__);
-    update_action_flags(action_complete, pe_action_runnable, __FUNCTION__, __LINE__);
-
     child_demoting_constraints(clone_data, pe_order_optional, rsc, NULL, last_demote_rsc, data_set);
 
     if (clone_data->demote_notify == NULL) {
         clone_data->demote_notify =
             create_notification_boundaries(rsc, RSC_DEMOTE, action, action_complete, data_set);
 
         if (clone_data->promote_notify) {
             /* If we ever wanted groups to have notifications we'd need to move this to native_internal_constraints() one day
              * Requires exposing *_notify
              */
             order_actions(clone_data->stop_notify->post_done, clone_data->promote_notify->pre,
                           pe_order_optional);
             order_actions(clone_data->start_notify->post_done, clone_data->promote_notify->pre,
                           pe_order_optional);
             order_actions(clone_data->demote_notify->post_done, clone_data->promote_notify->pre,
                           pe_order_optional);
             order_actions(clone_data->demote_notify->post_done, clone_data->start_notify->pre,
                           pe_order_optional);
             order_actions(clone_data->demote_notify->post_done, clone_data->stop_notify->pre,
                           pe_order_optional);
         }
     }
 
     /* restore the correct priority */
 
     gIter = rsc->children;
     for (; gIter != NULL; gIter = gIter->next) {
         resource_t *child_rsc = (resource_t *) gIter->data;
 
         child_rsc->priority = rsc->priority;
     }
 }
 
 void
-master_internal_constraints(resource_t * rsc, pe_working_set_t * data_set)
+master_promotion_constraints(resource_t * rsc, pe_working_set_t * data_set)
 {
-    GListPtr gIter = rsc->children;
-    resource_t *last_rsc = NULL;
-    clone_variant_data_t *clone_data = NULL;
-
-    get_clone_variant_data(clone_data, rsc);
-
-    clone_internal_constraints(rsc, data_set);
-
     /* global stopped before start */
     new_rsc_order(rsc, RSC_STOPPED, rsc, RSC_START, pe_order_optional, data_set);
 
     /* global stopped before promote */
     new_rsc_order(rsc, RSC_STOPPED, rsc, RSC_PROMOTE, pe_order_optional, data_set);
 
     /* global demoted before start */
     new_rsc_order(rsc, RSC_DEMOTED, rsc, RSC_START, pe_order_optional, data_set);
 
     /* global started before promote */
     new_rsc_order(rsc, RSC_STARTED, rsc, RSC_PROMOTE, pe_order_optional, data_set);
 
     /* global demoted before stop */
     new_rsc_order(rsc, RSC_DEMOTED, rsc, RSC_STOP, pe_order_optional, data_set);
 
     /* global demote before demoted */
     new_rsc_order(rsc, RSC_DEMOTE, rsc, RSC_DEMOTED, pe_order_optional, data_set);
 
     /* global demoted before promote */
     new_rsc_order(rsc, RSC_DEMOTED, rsc, RSC_PROMOTE, pe_order_optional, data_set);
+}
+
+
+void
+master_internal_constraints(resource_t * rsc, pe_working_set_t * data_set)
+{
+    GListPtr gIter = rsc->children;
+    resource_t *last_rsc = NULL;
+    clone_variant_data_t *clone_data = NULL;
+
+    get_clone_variant_data(clone_data, rsc);
+
+    clone_internal_constraints(rsc, data_set);
+    master_promotion_constraints(rsc, data_set);
 
     for (; gIter != NULL; gIter = gIter->next) {
         resource_t *child_rsc = (resource_t *) gIter->data;
 
         /* child demote before promote */
         new_rsc_order(child_rsc, RSC_DEMOTE, child_rsc, RSC_PROMOTE, pe_order_optional, data_set);
 
         child_promoting_constraints(clone_data, pe_order_optional,
                                     rsc, child_rsc, last_rsc, data_set);
 
         child_demoting_constraints(clone_data, pe_order_optional,
                                    rsc, child_rsc, last_rsc, data_set);
 
         last_rsc = child_rsc;
     }
 }
 
 static void
 node_hash_update_one(GHashTable * hash, node_t * other, const char *attr, int score)
 {
     GHashTableIter iter;
     node_t *node = NULL;
     const char *value = NULL;
 
     if (other == NULL) {
         return;
 
     } else if (attr == NULL) {
         attr = "#" XML_ATTR_UNAME;
     }
 
     value = g_hash_table_lookup(other->details->attrs, attr);
     g_hash_table_iter_init(&iter, hash);
     while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) {
         const char *tmp = g_hash_table_lookup(node->details->attrs, attr);
 
         if (safe_str_eq(value, tmp)) {
             crm_trace("%s: %d + %d", node->details->uname, node->weight, other->weight);
             node->weight = merge_weights(node->weight, score);
         }
     }
 }
 
 void
 master_rsc_colocation_rh(resource_t * rsc_lh, resource_t * rsc_rh, rsc_colocation_t * constraint)
 {
     GListPtr gIter = NULL;
 
     CRM_CHECK(rsc_rh != NULL, return);
     if (is_set(rsc_rh->flags, pe_rsc_provisional)) {
         return;
 
     } else if (constraint->role_rh == RSC_ROLE_UNKNOWN) {
         pe_rsc_trace(rsc_rh, "Handling %s as a clone colocation", constraint->id);
         clone_rsc_colocation_rh(rsc_lh, rsc_rh, constraint);
         return;
     }
 
     CRM_CHECK(rsc_lh != NULL, return);
     CRM_CHECK(rsc_lh->variant == pe_native, return);
     pe_rsc_trace(rsc_rh, "Processing constraint %s: %d", constraint->id, constraint->score);
 
     if (constraint->role_rh == RSC_ROLE_UNKNOWN) {
 
         gIter = rsc_rh->children;
         for (; gIter != NULL; gIter = gIter->next) {
             resource_t *child_rsc = (resource_t *) gIter->data;
 
             child_rsc->cmds->rsc_colocation_rh(rsc_lh, child_rsc, constraint);
         }
 
     } else if (is_set(rsc_lh->flags, pe_rsc_provisional)) {
         GListPtr rhs = NULL;
 
         gIter = rsc_rh->children;
         for (; gIter != NULL; gIter = gIter->next) {
             resource_t *child_rsc = (resource_t *) gIter->data;
             node_t *chosen = child_rsc->fns->location(child_rsc, NULL, FALSE);
             enum rsc_role_e next_role = child_rsc->fns->state(child_rsc, FALSE);
 
             pe_rsc_trace(rsc_rh, "Processing: %s", child_rsc->id);
             if (chosen != NULL && next_role == constraint->role_rh) {
                 pe_rsc_trace(rsc_rh, "Applying: %s %s %s %d", child_rsc->id,
                              role2text(next_role), chosen->details->uname, constraint->score);
                 if (constraint->score < INFINITY) {
                     node_hash_update_one(rsc_lh->allowed_nodes, chosen,
                                          constraint->node_attribute, constraint->score);
                 }
                 rhs = g_list_prepend(rhs, chosen);
             }
         }
 
         /* Only do this if it's not a master-master colocation
          * Doing this unconditionally would prevent the slaves from being started
          */
         if (constraint->role_lh != RSC_ROLE_MASTER || constraint->role_rh != RSC_ROLE_MASTER) {
             if (constraint->score >= INFINITY) {
                 node_list_exclude(rsc_lh->allowed_nodes, rhs, TRUE);
             }
         }
         g_list_free(rhs);
 
     } else if (constraint->role_lh == RSC_ROLE_MASTER) {
         resource_t *rh_child = find_compatible_child(rsc_lh, rsc_rh, constraint->role_rh, FALSE);
 
         if (rh_child == NULL && constraint->score >= INFINITY) {
             pe_rsc_trace(rsc_lh, "%s can't be promoted %s", rsc_lh->id, constraint->id);
             rsc_lh->priority = -INFINITY;
 
         } else if (rh_child != NULL) {
             int new_priority = merge_weights(rsc_lh->priority, constraint->score);
 
             pe_rsc_debug(rsc_lh, "Applying %s to %s", constraint->id, rsc_lh->id);
             pe_rsc_debug(rsc_lh, "\t%s: %d->%d", rsc_lh->id, rsc_lh->priority, new_priority);
             rsc_lh->priority = new_priority;
         }
     }
 
     return;
 }
 
 void
 master_append_meta(resource_t * rsc, xmlNode * xml)
 {
     char *name = NULL;
     clone_variant_data_t *clone_data = NULL;
 
     get_clone_variant_data(clone_data, rsc);
 
     clone_append_meta(rsc, xml);
 
     name = crm_meta_name(XML_RSC_ATTR_MASTER_MAX);
     crm_xml_add_int(xml, name, clone_data->master_max);
     free(name);
 
     name = crm_meta_name(XML_RSC_ATTR_MASTER_NODEMAX);
     crm_xml_add_int(xml, name, clone_data->master_node_max);
     free(name);
 }
diff --git a/pengine/utils.c b/pengine/utils.c
index a587e587d9..0cc6381c99 100644
--- a/pengine/utils.c
+++ b/pengine/utils.c
@@ -1,424 +1,436 @@
 /* 
  * Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
  * 
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public
  * License as published by the Free Software Foundation; either
  * version 2 of the License, or (at your option) any later version.
  * 
  * This software is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  * General Public License for more details.
  * 
  * You should have received a copy of the GNU General Public
  * License along with this library; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  */
 
 #include <crm_internal.h>
 #include <crm/msg_xml.h>
 #include <allocate.h>
 #include <utils.h>
 
 void
 pe_free_ordering(GListPtr constraints)
 {
     GListPtr iterator = constraints;
 
     while (iterator != NULL) {
         order_constraint_t *order = iterator->data;
 
         iterator = iterator->next;
 
         free(order->lh_action_task);
         free(order->rh_action_task);
         free(order);
     }
     if (constraints != NULL) {
         g_list_free(constraints);
     }
 }
 
 void
 pe_free_rsc_to_node(GListPtr constraints)
 {
     GListPtr iterator = constraints;
 
     while (iterator != NULL) {
         rsc_to_node_t *cons = iterator->data;
 
         iterator = iterator->next;
 
         g_list_free_full(cons->node_list_rh, free);
         free(cons->id);
         free(cons);
     }
     if (constraints != NULL) {
         g_list_free(constraints);
     }
 }
 
 rsc_to_node_t *
 rsc2node_new(const char *id, resource_t * rsc,
              int node_weight, const char *discover_mode,
              node_t * foo_node, pe_working_set_t * data_set)
 {
     rsc_to_node_t *new_con = NULL;
 
     if (rsc == NULL || id == NULL) {
         pe_err("Invalid constraint %s for rsc=%p", crm_str(id), rsc);
         return NULL;
 
     } else if (foo_node == NULL) {
         CRM_CHECK(node_weight == 0, return NULL);
     }
 
     new_con = calloc(1, sizeof(rsc_to_node_t));
     if (new_con != NULL) {
         new_con->id = strdup(id);
         new_con->rsc_lh = rsc;
         new_con->node_list_rh = NULL;
         new_con->role_filter = RSC_ROLE_UNKNOWN;
 
 
         if (discover_mode == NULL || safe_str_eq(discover_mode, "always")) {
             new_con->discover_mode = discover_always;
         } else if (safe_str_eq(discover_mode, "never")) {
             new_con->discover_mode = discover_never;
         } else if (safe_str_eq(discover_mode, "exclusive")) {
             new_con->discover_mode = discover_exclusive;
             rsc->exclusive_discover = TRUE;
         } else {
             pe_err("Invalid %s value %s in location constraint", XML_LOCATION_ATTR_DISCOVERY, discover_mode);
         }
 
         if (foo_node != NULL) {
             node_t *copy = node_copy(foo_node);
 
             copy->weight = node_weight;
             new_con->node_list_rh = g_list_prepend(NULL, copy);
         }
 
         data_set->placement_constraints = g_list_prepend(data_set->placement_constraints, new_con);
         rsc->rsc_location = g_list_prepend(rsc->rsc_location, new_con);
     }
 
     return new_con;
 }
 
 gboolean
 can_run_resources(const node_t * node)
 {
     if (node == NULL) {
         return FALSE;
     }
 #if 0
     if (node->weight < 0) {
         return FALSE;
     }
 #endif
 
     if (node->details->online == FALSE
         || node->details->shutdown || node->details->unclean
         || node->details->standby || node->details->maintenance) {
         crm_trace("%s: online=%d, unclean=%d, standby=%d, maintenance=%d",
                   node->details->uname, node->details->online,
                   node->details->unclean, node->details->standby, node->details->maintenance);
         return FALSE;
     }
     return TRUE;
 }
 
 /* return -1 if 'a' is more preferred
  * return  1 if 'b' is more preferred
  */
 
 gint
 sort_node_weight(gconstpointer a, gconstpointer b, gpointer data)
 {
     const node_t *node1 = (const node_t *)a;
     const node_t *node2 = (const node_t *)b;
     const node_t *active = (node_t *) data;
 
     int node1_weight = 0;
     int node2_weight = 0;
 
     int result = 0;
 
     if (a == NULL) {
         return 1;
     }
     if (b == NULL) {
         return -1;
     }
 
     node1_weight = node1->weight;
     node2_weight = node2->weight;
 
     if (can_run_resources(node1) == FALSE) {
         node1_weight = -INFINITY;
     }
     if (can_run_resources(node2) == FALSE) {
         node2_weight = -INFINITY;
     }
 
     if (node1_weight > node2_weight) {
         crm_trace("%s (%d) > %s (%d) : weight",
                   node1->details->uname, node1_weight, node2->details->uname, node2_weight);
         return -1;
     }
 
     if (node1_weight < node2_weight) {
         crm_trace("%s (%d) < %s (%d) : weight",
                   node1->details->uname, node1_weight, node2->details->uname, node2_weight);
         return 1;
     }
 
     crm_trace("%s (%d) == %s (%d) : weight",
               node1->details->uname, node1_weight, node2->details->uname, node2_weight);
 
     if (safe_str_eq(pe_dataset->placement_strategy, "minimal")) {
         goto equal;
     }
 
     if (safe_str_eq(pe_dataset->placement_strategy, "balanced")) {
         result = compare_capacity(node1, node2);
         if (result != 0) {
             return result;
         }
     }
 
     /* now try to balance resources across the cluster */
     if (node1->details->num_resources < node2->details->num_resources) {
         crm_trace("%s (%d) < %s (%d) : resources",
                   node1->details->uname, node1->details->num_resources,
                   node2->details->uname, node2->details->num_resources);
         return -1;
 
     } else if (node1->details->num_resources > node2->details->num_resources) {
         crm_trace("%s (%d) > %s (%d) : resources",
                   node1->details->uname, node1->details->num_resources,
                   node2->details->uname, node2->details->num_resources);
         return 1;
     }
 
     if (active && active->details == node1->details) {
         crm_trace("%s (%d) > %s (%d) : active",
                   node1->details->uname, node1->details->num_resources,
                   node2->details->uname, node2->details->num_resources);
         return -1;
     } else if (active && active->details == node2->details) {
         crm_trace("%s (%d) > %s (%d) : active",
                   node1->details->uname, node1->details->num_resources,
                   node2->details->uname, node2->details->num_resources);
         return 1;
     }
   equal:
     crm_trace("%s = %s", node1->details->uname, node2->details->uname);
     return strcmp(node1->details->uname, node2->details->uname);
 }
 
 void
 native_deallocate(resource_t * rsc)
 {
     if (rsc->allocated_to) {
         node_t *old = rsc->allocated_to;
 
         crm_info("Deallocating %s from %s", rsc->id, old->details->uname);
         set_bit(rsc->flags, pe_rsc_provisional);
         rsc->allocated_to = NULL;
 
         old->details->allocated_rsc = g_list_remove(old->details->allocated_rsc, rsc);
         old->details->num_resources--;
         /* old->count--; */
         calculate_utilization(old->details->utilization, rsc->utilization, TRUE);
         free(old);
     }
 }
 
 gboolean
 native_assign_node(resource_t * rsc, GListPtr nodes, node_t * chosen, gboolean force)
 {
     CRM_ASSERT(rsc->variant == pe_native);
 
     if (force == FALSE && chosen != NULL) {
         bool unset = FALSE;
 
         if(chosen->weight < 0) {
             unset = TRUE;
 
             // Allow the graph to assume that the remote resource will come up
         } else if(can_run_resources(chosen) == FALSE && !is_container_remote_node(chosen)) {
             unset = TRUE;
         }
 
         if(unset) {
             crm_debug("All nodes for resource %s are unavailable"
                       ", unclean or shutting down (%s: %d, %d)",
                       rsc->id, chosen->details->uname, can_run_resources(chosen), chosen->weight);
             rsc->next_role = RSC_ROLE_STOPPED;
             chosen = NULL;
         }
     }
 
     /* todo: update the old node for each resource to reflect its
      * new resource count
      */
 
     native_deallocate(rsc);
     clear_bit(rsc->flags, pe_rsc_provisional);
 
     if (chosen == NULL) {
         GListPtr gIter = NULL;
         char *rc_inactive = crm_itoa(PCMK_OCF_NOT_RUNNING);
 
         crm_debug("Could not allocate a node for %s", rsc->id);
         rsc->next_role = RSC_ROLE_STOPPED;
 
         for (gIter = rsc->actions; gIter != NULL; gIter = gIter->next) {
             action_t *op = (action_t *) gIter->data;
             const char *interval = g_hash_table_lookup(op->meta, XML_LRM_ATTR_INTERVAL);
 
             crm_debug("Processing %s", op->uuid);
             if(safe_str_eq(RSC_STOP, op->task)) {
                 update_action_flags(op, pe_action_optional | pe_action_clear, __FUNCTION__, __LINE__);
 
             } else if(safe_str_eq(RSC_START, op->task)) {
                 update_action_flags(op, pe_action_runnable | pe_action_clear, __FUNCTION__, __LINE__);
                 /* set_bit(rsc->flags, pe_rsc_block); */
 
             } else if(interval && safe_str_neq(interval, "0")) {
                 if(safe_str_eq(rc_inactive, g_hash_table_lookup(op->meta, XML_ATTR_TE_TARGET_RC))) {
                     /* This is a recurring monitor for the stopped state, leave it alone */
 
                 } else {
                     /* Normal monitor operation, cancel it */
                     update_action_flags(op, pe_action_runnable | pe_action_clear, __FUNCTION__, __LINE__);
                 }
             }
         }
 
         free(rc_inactive);
         return FALSE;
     }
 
     crm_debug("Assigning %s to %s", chosen->details->uname, rsc->id);
     rsc->allocated_to = node_copy(chosen);
 
     chosen->details->allocated_rsc = g_list_prepend(chosen->details->allocated_rsc, rsc);
     chosen->details->num_resources++;
     chosen->count++;
     calculate_utilization(chosen->details->utilization, rsc->utilization, FALSE);
     dump_rsc_utilization(show_utilization ? 0 : utilization_log_level, __FUNCTION__, rsc, chosen);
 
     return TRUE;
 }
 
 void
 log_action(unsigned int log_level, const char *pre_text, action_t * action, gboolean details)
 {
     const char *node_uname = NULL;
     const char *node_uuid = NULL;
 
     if (action == NULL) {
         crm_trace("%s%s: <NULL>", pre_text == NULL ? "" : pre_text, pre_text == NULL ? "" : ": ");
         return;
     }
 
     if (is_set(action->flags, pe_action_pseudo)) {
         node_uname = NULL;
         node_uuid = NULL;
 
     } else if (action->node != NULL) {
         node_uname = action->node->details->uname;
         node_uuid = action->node->details->id;
     } else {
         node_uname = "<none>";
         node_uuid = NULL;
     }
 
     switch (text2task(action->task)) {
         case stonith_node:
         case shutdown_crm:
             crm_trace("%s%s%sAction %d: %s%s%s%s%s%s",
                       pre_text == NULL ? "" : pre_text,
                       pre_text == NULL ? "" : ": ",
                       is_set(action->flags,
                              pe_action_pseudo) ? "Pseudo " : is_set(action->flags,
                                                                     pe_action_optional) ?
                       "Optional " : is_set(action->flags,
                                            pe_action_runnable) ? is_set(action->flags,
                                                                         pe_action_processed)
                       ? "" : "(Provisional) " : "!!Non-Startable!! ", action->id,
                       action->uuid, node_uname ? "\ton " : "",
                       node_uname ? node_uname : "", node_uuid ? "\t\t(" : "",
                       node_uuid ? node_uuid : "", node_uuid ? ")" : "");
             break;
         default:
             crm_trace("%s%s%sAction %d: %s %s%s%s%s%s%s",
                       pre_text == NULL ? "" : pre_text,
                       pre_text == NULL ? "" : ": ",
                       is_set(action->flags,
                              pe_action_optional) ? "Optional " : is_set(action->flags,
                                                                         pe_action_pseudo)
                       ? "Pseudo " : is_set(action->flags,
                                            pe_action_runnable) ? is_set(action->flags,
                                                                         pe_action_processed)
                       ? "" : "(Provisional) " : "!!Non-Startable!! ", action->id,
                       action->uuid, action->rsc ? action->rsc->id : "<none>",
                       node_uname ? "\ton " : "", node_uname ? node_uname : "",
                       node_uuid ? "\t\t(" : "", node_uuid ? node_uuid : "", node_uuid ? ")" : "");
 
             break;
     }
 
     if (details) {
         GListPtr gIter = NULL;
 
         crm_trace("\t\t====== Preceding Actions");
 
         gIter = action->actions_before;
         for (; gIter != NULL; gIter = gIter->next) {
             action_wrapper_t *other = (action_wrapper_t *) gIter->data;
 
             log_action(log_level + 1, "\t\t", other->action, FALSE);
         }
 
         crm_trace("\t\t====== Subsequent Actions");
 
         gIter = action->actions_after;
         for (; gIter != NULL; gIter = gIter->next) {
             action_wrapper_t *other = (action_wrapper_t *) gIter->data;
 
             log_action(log_level + 1, "\t\t", other->action, FALSE);
         }
 
         crm_trace("\t\t====== End");
 
     } else {
         crm_trace("\t\t(seen=%d, before=%d, after=%d)",
                   action->seen_count,
                   g_list_length(action->actions_before), g_list_length(action->actions_after));
     }
 }
 
 gboolean
 can_run_any(GHashTable * nodes)
 {
     GHashTableIter iter;
     node_t *node = NULL;
 
     if (nodes == NULL) {
         return FALSE;
     }
 
     g_hash_table_iter_init(&iter, nodes);
     while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) {
         if (can_run_resources(node) && node->weight >= 0) {
             return TRUE;
         }
     }
 
     return FALSE;
 }
+
+pe_action_t *
+create_pseudo_resource_op(resource_t * rsc, const char *task, bool optional, bool runnable, pe_working_set_t *data_set)
+{
+    pe_action_t *action = custom_action(rsc, generate_op_key(rsc->id, task, 0), task, NULL, optional, TRUE, data_set);
+    update_action_flags(action, pe_action_pseudo, __FUNCTION__, __LINE__);
+    update_action_flags(action, pe_action_runnable, __FUNCTION__, __LINE__);
+    if(runnable) {
+        update_action_flags(action, pe_action_runnable, __FUNCTION__, __LINE__);
+    }
+    return action;
+}
diff --git a/pengine/utils.h b/pengine/utils.h
index 79fd33d0e4..10e7201161 100644
--- a/pengine/utils.h
+++ b/pengine/utils.h
@@ -1,80 +1,81 @@
 /* 
  * Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
  * 
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
  * License as published by the Free Software Foundation; either
  * version 2 of the License, or (at your option) any later version.
  * 
  * This software is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  * General Public License for more details.
  * 
  * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  */
 #ifndef PENGINE_AUTILS__H
 #  define PENGINE_AUTILS__H
 
 /* Constraint helper functions */
 extern rsc_colocation_t *invert_constraint(rsc_colocation_t * constraint);
 
 extern rsc_to_node_t *copy_constraint(rsc_to_node_t * constraint);
 
 extern rsc_to_node_t *rsc2node_new(const char *id, resource_t * rsc, int weight,
                                    const char *discovery_mode, node_t * node,
                                    pe_working_set_t * data_set);
 
 extern void pe_free_rsc_to_node(GListPtr constraints);
 extern void pe_free_ordering(GListPtr constraints);
 
 extern gboolean rsc_colocation_new(const char *id, const char *node_attr, int score,
                                    resource_t * rsc_lh, resource_t * rsc_rh,
                                    const char *state_lh, const char *state_rh,
                                    pe_working_set_t * data_set);
 
 extern gboolean rsc_ticket_new(const char *id, resource_t * rsc_lh, ticket_t * ticket,
                                const char *state_lh, const char *loss_policy,
                                pe_working_set_t * data_set);
 
 extern gint sort_node_weight(gconstpointer a, gconstpointer b, gpointer data_set);
 
 extern gboolean can_run_resources(const node_t * node);
 extern gboolean native_assign_node(resource_t * rsc, GListPtr candidates, node_t * chosen,
                                    gboolean force);
 void native_deallocate(resource_t * rsc);
 
 extern void log_action(unsigned int log_level, const char *pre_text,
                        action_t * action, gboolean details);
 
 extern gboolean can_run_any(GHashTable * nodes);
 extern resource_t *find_compatible_child(resource_t * local_child, resource_t * rsc,
                                          enum rsc_role_e filter, gboolean current);
 gboolean is_child_compatible(resource_t *child_rsc, node_t * local_node, enum rsc_role_e filter, gboolean current);
 bool assign_node(resource_t * rsc, node_t * node, gboolean force);
 
 
 enum filter_colocation_res {
     influence_nothing = 0,
     influence_rsc_location,
     influence_rsc_priority,
 };
 
 extern enum filter_colocation_res
 filter_colocation_constraint(resource_t * rsc_lh, resource_t * rsc_rh,
                              rsc_colocation_t * constraint, gboolean preview);
 
 extern int compare_capacity(const node_t * node1, const node_t * node2);
 extern void calculate_utilization(GHashTable * current_utilization,
                                   GHashTable * utilization, gboolean plus);
 
 extern void process_utilization(resource_t * rsc, node_t ** prefer, pe_working_set_t * data_set);
+pe_action_t *create_pseudo_resource_op(resource_t * rsc, const char *task, bool optional, bool runnable, pe_working_set_t *data_set);
 
 #  define STONITH_UP "stonith_up"
 #  define STONITH_DONE "stonith_complete"
 #  define ALL_STOPPED "all_stopped"
 #  define LOAD_STOPPED "load_stopped"
 
 #endif