diff --git a/include/crm/msg_xml.h b/include/crm/msg_xml.h
index df5f42d55a..c75d72410d 100644
--- a/include/crm/msg_xml.h
+++ b/include/crm/msg_xml.h
@@ -1,274 +1,278 @@
 /* 
  * 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.1 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 #ifndef XML_TAGS__H
 #define XML_TAGS__H
 
 #define CIB_OPTIONS_FIRST "cib-bootstrap-options"
 
 #define F_CRM_DATA			"crm_xml"
 #define F_CRM_TASK			"crm_task"
 #define F_CRM_HOST_TO			"crm_host_to"
 #define F_CRM_MSG_TYPE			F_SUBTYPE
 #define F_CRM_SYS_TO			"crm_sys_to"
 #define F_CRM_SYS_FROM			"crm_sys_from"
 #define F_CRM_HOST_FROM			F_ORIG
 #define F_CRM_REFERENCE			XML_ATTR_REFERENCE
 #define F_CRM_VERSION			XML_ATTR_VERSION
 #define F_CRM_ORIGIN			"origin"
 #define F_CRM_JOIN_ID			"join_id"
 #define F_CRM_ELECTION_ID		"election-id"
 #define F_CRM_ELECTION_OWNER		"election-owner"
 #define F_CRM_TGRAPH			"crm-tgraph"
 #define F_CRM_TGRAPH_INPUT		"crm-tgraph-in"
 
 /*---- Common tags/attrs */
 #define XML_DIFF_MARKER			"__crm_diff_marker__"
 #define XML_ATTR_TAGNAME		F_XML_TAGNAME
 #define XML_TAG_CIB			"cib"
 #define XML_TAG_FAILED			"failed"
 
 #define XML_ATTR_CRM_VERSION		"crm_feature_set"
 #define XML_ATTR_DIGEST			"digest"
 #define XML_ATTR_VALIDATION		"validate-with"
 
 #define XML_ATTR_QUORUM_PANIC		"no-quorum-panic"
 #define XML_ATTR_HAVE_QUORUM		"have-quorum"
 #define XML_ATTR_EXPECTED_VOTES		"expected-quorum-votes"
 #define XML_ATTR_GENERATION		"epoch"
 #define XML_ATTR_GENERATION_ADMIN	"admin_epoch"
 #define XML_ATTR_NUMUPDATES		"num_updates"
 #define XML_ATTR_TIMEOUT		"timeout"
 #define XML_ATTR_ORIGIN			"crm-debug-origin"
 #define XML_ATTR_TSTAMP			"crm-timestamp"
 #define XML_CIB_ATTR_WRITTEN		"cib-last-written"
 #define XML_ATTR_VERSION		"version"
 #define XML_ATTR_DESC			"description"
 #define XML_ATTR_ID			"id"
 #define XML_ATTR_IDREF			"id-ref"
 #define XML_ATTR_ID_LONG		"long-id"
 #define XML_ATTR_TYPE			"type"
 #define XML_ATTR_FILTER_TYPE		"type-filter"
 #define XML_ATTR_FILTER_ID		"id-filter"
 #define XML_ATTR_FILTER_PRIORITY	"priority-filter"
 #define XML_ATTR_VERBOSE		"verbose"
 #define XML_ATTR_OP			"op"
 #define XML_ATTR_DC			"is_dc"
 #define XML_ATTR_DC_UUID		"dc-uuid"
 
 #define XML_BOOLEAN_TRUE		"true"
 #define XML_BOOLEAN_FALSE		"false"
 #define XML_BOOLEAN_YES			XML_BOOLEAN_TRUE
 #define XML_BOOLEAN_NO			XML_BOOLEAN_FALSE
 
 #define XML_TAG_OPTIONS			"options"
 
 /*---- top level tags/attrs */
 #define XML_MSG_TAG			"crm_message"
 #define XML_MSG_TAG_DATA		"msg_data"
 #define XML_ATTR_REQUEST		"request"
 #define XML_ATTR_RESPONSE		"response"
 
 #define XML_ATTR_UNAME			"uname"
 #define XML_ATTR_UUID			"id"
 #define XML_ATTR_REFERENCE		"reference"
 
 #define XML_FAIL_TAG_RESOURCE		"failed_resource"
 
 #define XML_FAILRES_ATTR_RESID		"resource_id"
 #define XML_FAILRES_ATTR_REASON		"reason"
 #define XML_FAILRES_ATTR_RESSTATUS	"resource_status"
 
 #define XML_CRM_TAG_PING		"ping_response"
 #define XML_PING_ATTR_STATUS		"result"
 #define XML_PING_ATTR_SYSFROM		"crm_subsystem"
 
 #define XML_TAG_FRAGMENT		"cib_fragment"
 #define XML_ATTR_RESULT			"result"
 #define XML_ATTR_SECTION		"section"
 
 #define XML_FAIL_TAG_CIB		"failed_update"
 
 #define XML_FAILCIB_ATTR_ID		"id"
 #define XML_FAILCIB_ATTR_OBJTYPE	"object_type"
 #define XML_FAILCIB_ATTR_OP		"operation"
 #define XML_FAILCIB_ATTR_REASON		"reason"
 
 /*---- CIB specific tags/attrs */
 #define XML_CIB_TAG_SECTION_ALL		"all"
 #define XML_CIB_TAG_CONFIGURATION	"configuration"
 #define XML_CIB_TAG_STATUS       	"status"
 #define XML_CIB_TAG_RESOURCES		"resources"
 #define XML_CIB_TAG_NODES         	"nodes"
 #define XML_CIB_TAG_CONSTRAINTS   	"constraints"
 #define XML_CIB_TAG_CRMCONFIG   	"crm_config"
 #define XML_CIB_TAG_OPCONFIG		"op_defaults"
 #define XML_CIB_TAG_RSCCONFIG   	"rsc_defaults"
 
 #define XML_CIB_TAG_STATE         	"node_state"
 #define XML_CIB_TAG_NODE          	"node"
 #define XML_CIB_TAG_CONSTRAINT    	"constraint"
 #define XML_CIB_TAG_NVPAIR        	"nvpair"
 
 #define XML_CIB_TAG_PROPSET	   	"cluster_property_set"
 #define XML_TAG_ATTR_SETS	   	"instance_attributes"
 #define XML_TAG_META_SETS	   	"meta_attributes"
 #define XML_TAG_ATTRS			"attributes"
 #define XML_TAG_PARAMS			"parameters"
 #define XML_TAG_PARAM			"param"
 #define XML_TAG_UTILIZATION		"utilization"
 
 #define XML_TAG_RESOURCE_REF		"resource_ref"
 #define XML_CIB_TAG_RESOURCE	  	"primitive"
 #define XML_CIB_TAG_GROUP	  	"group"
 #define XML_CIB_TAG_INCARNATION		"clone"
 #define XML_CIB_TAG_MASTER		"master"
 
 #define XML_RSC_ATTR_RESTART	  	"restart-type"
 #define XML_RSC_ATTR_ORDERED		"ordered"
 #define XML_RSC_ATTR_INTERLEAVE		"interleave"
 #define XML_RSC_ATTR_INCARNATION	"clone"
 #define XML_RSC_ATTR_INCARNATION_MAX	"clone-max"
 #define XML_RSC_ATTR_INCARNATION_NODEMAX	"clone-node-max"
 #define XML_RSC_ATTR_MASTER_MAX		"master-max"
 #define XML_RSC_ATTR_MASTER_NODEMAX	"master-node-max"
 #define XML_RSC_ATTR_STATE		"clone-state"
 #define XML_RSC_ATTR_MANAGED		"is-managed"
 #define XML_RSC_ATTR_TARGET_ROLE	"target-role"
 #define XML_RSC_ATTR_UNIQUE		"globally-unique"
 #define XML_RSC_ATTR_NOTIFY		"notify"
 #define XML_RSC_ATTR_STICKINESS		"resource-stickiness"
 #define XML_RSC_ATTR_FAIL_STICKINESS	"migration-threshold"
 #define XML_RSC_ATTR_FAIL_TIMEOUT	"failure-timeout"
 #define XML_RSC_ATTR_MULTIPLE		"multiple-active"
 #define XML_RSC_ATTR_PRIORITY		"priority"
 #define XML_OP_ATTR_ON_FAIL		"on-fail"
 #define XML_OP_ATTR_START_DELAY		"start-delay"
 #define XML_OP_ATTR_ALLOW_MIGRATE	"allow-migrate"
 #define XML_OP_ATTR_ORIGIN		"interval-origin"
 #define XML_OP_ATTR_PENDING		"record-pending"
 
 #define XML_CIB_TAG_LRM		  	"lrm"
 #define XML_LRM_TAG_RESOURCES     	"lrm_resources"
 #define XML_LRM_TAG_RESOURCE     	"lrm_resource"
 #define XML_LRM_TAG_AGENTS	     	"lrm_agents"
 #define XML_LRM_TAG_AGENT		"lrm_agent"
 #define XML_LRM_TAG_RSC_OP		"lrm_rsc_op"
 #define XML_AGENT_ATTR_CLASS		"class"
 #define XML_AGENT_ATTR_PROVIDER		"provider"
 #define XML_LRM_TAG_ATTRIBUTES		"attributes"
 
 #define XML_CIB_ATTR_REPLACE       	"replace"
 #define XML_CIB_ATTR_SOURCE       	"source"
 
 #define XML_CIB_ATTR_HEALTH       	"health"
 #define XML_CIB_ATTR_WEIGHT       	"weight"
 #define XML_CIB_ATTR_PRIORITY     	"priority"
 #define XML_CIB_ATTR_CLEAR        	"clear_on"
 #define XML_CIB_ATTR_SOURCE       	"source"
 
 #define XML_CIB_ATTR_JOINSTATE    	"join"
 #define XML_CIB_ATTR_EXPSTATE     	"expected"
 #define XML_CIB_ATTR_INCCM        	"in_ccm"
 #define XML_CIB_ATTR_CRMDSTATE    	"crmd"
 #define XML_CIB_ATTR_HASTATE    	"ha"
 
 #define XML_CIB_ATTR_SHUTDOWN       	"shutdown"
 #define XML_CIB_ATTR_STONITH	    	"stonith"
 
 #define XML_LRM_ATTR_INTERVAL		"interval"
 #define XML_LRM_ATTR_TASK		"operation"
 #define XML_LRM_ATTR_TASK_KEY		"operation_key"
 #define XML_LRM_ATTR_TARGET		"on_node"
 #define XML_LRM_ATTR_TARGET_UUID	"on_node_uuid"
 #define XML_LRM_ATTR_RSCID		"rsc-id"
 #define XML_LRM_ATTR_OPSTATUS		"op-status"
 #define XML_LRM_ATTR_RC			"rc-code"
 #define XML_LRM_ATTR_CALLID		"call-id"
 #define XML_LRM_ATTR_OP_DIGEST		"op-digest"
 #define XML_LRM_ATTR_OP_RESTART		"op-force-restart"
 #define XML_LRM_ATTR_RESTART_DIGEST	"op-restart-digest"
 
 #define XML_TAG_GRAPH			"transition_graph"
 #define XML_GRAPH_TAG_RSC_OP		"rsc_op"
 #define XML_GRAPH_TAG_PSEUDO_EVENT	"pseudo_event"
 #define XML_GRAPH_TAG_CRM_EVENT		"crm_event"
 
 #define XML_TAG_RULE			"rule"
 #define XML_RULE_ATTR_SCORE		"score"
 #define XML_RULE_ATTR_SCORE_ATTRIBUTE	"score-attribute"
 #define XML_RULE_ATTR_SCORE_MANGLED	"score-attribute-mangled"
 #define XML_RULE_ATTR_ROLE		"role"
 #define XML_RULE_ATTR_RESULT		"result"
 #define XML_RULE_ATTR_BOOLEAN_OP	"boolean-op"
 
 #define XML_TAG_EXPRESSION		"expression"
 #define XML_EXPR_ATTR_ATTRIBUTE		"attribute"
 #define XML_EXPR_ATTR_OPERATION		"operation"
 #define XML_EXPR_ATTR_VALUE		"value"
 #define XML_EXPR_ATTR_TYPE		"type"
 
 #define XML_CONS_TAG_RSC_DEPEND		"rsc_colocation"
 #define XML_CONS_TAG_RSC_ORDER		"rsc_order"
 #define XML_CONS_TAG_RSC_LOCATION	"rsc_location"
 #define XML_CONS_TAG_RSC_SET		"resource_set"
 #define XML_CONS_ATTR_SYMMETRICAL	"symmetrical"
 
 #define XML_COLOC_ATTR_SOURCE		"rsc"
 #define XML_COLOC_ATTR_SOURCE_ROLE	"rsc-role"
 #define XML_COLOC_ATTR_TARGET		"with-rsc"
 #define XML_COLOC_ATTR_TARGET_ROLE	"with-rsc-role"
 #define XML_COLOC_ATTR_NODE_ATTR	"node-attribute"
+#define XML_COLOC_ATTR_SOURCE_INSTANCE	"rsc-instance"
+#define XML_COLOC_ATTR_TARGET_INSTANCE	"with-rsc-instance"
 
 #define XML_ORDER_ATTR_FIRST		"first"
 #define XML_ORDER_ATTR_THEN		"then"
 #define XML_ORDER_ATTR_FIRST_ACTION	"first-action"
 #define XML_ORDER_ATTR_THEN_ACTION	"then-action"
+#define XML_ORDER_ATTR_FIRST_INSTANCE	"first-instance"
+#define XML_ORDER_ATTR_THEN_INSTANCE	"then-instance"
 #define XML_ORDER_ATTR_KIND		"kind"
 
 #define XML_NVPAIR_ATTR_NAME        	"name"
 #define XML_NVPAIR_ATTR_VALUE        	"value"
 
 #define XML_NODE_ATTR_STATE		"state"
 
 #define XML_CONFIG_ATTR_DC_DEADTIME	"dc-deadtime"
 #define XML_CONFIG_ATTR_ELECTION_FAIL	"election-timeout"
 #define XML_CONFIG_ATTR_FORCE_QUIT	"shutdown-escalation"
 #define XML_CONFIG_ATTR_RECHECK		"cluster-recheck-interval"
 
 #define XML_CIB_TAG_GENERATION_TUPPLE	"generation_tuple"
 
 #define XML_ATTR_TRANSITION_MAGIC	"transition-magic"
 #define XML_ATTR_TRANSITION_KEY		"transition-key"
 
 #define XML_ATTR_TE_NOWAIT		"op_no_wait"
 #define XML_ATTR_TE_TARGET_RC		"op_target_rc"
 #define XML_ATTR_TE_ALLOWFAIL		"op_allow_fail"
 #define XML_ATTR_LRM_PROBE		"lrm-is-probe"
 #define XML_TAG_TRANSIENT_NODEATTRS	"transient_attributes"
 
 #define XML_TAG_DIFF_ADDED		"diff-added"
 #define XML_TAG_DIFF_REMOVED		"diff-removed"
 
 #include <crm/common/xml.h> 
 
 #define ID(x) crm_element_value(x, XML_ATTR_ID)
 #define INSTANCE(x) crm_element_value(x, XML_CIB_ATTR_INSTANCE)
 #define TSTAMP(x) crm_element_value(x, XML_ATTR_TSTAMP)
 #define TYPE(x) crm_element_name(x) 
 
 #endif
diff --git a/lib/pengine/clone.c b/lib/pengine/clone.c
index 51d798ed6e..296970e481 100644
--- a/lib/pengine/clone.c
+++ b/lib/pengine/clone.c
@@ -1,480 +1,497 @@
 /* 
  * 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.1 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 
 #include <crm_internal.h>
 
 #include <crm/pengine/rules.h>
 #include <crm/pengine/status.h>
 #include <unpack.h>
 #include <utils.h>
 #include <crm/msg_xml.h>
 
 #define VARIANT_CLONE 1
 #include "./variant.h"
 
 void clone_create_notifications(
 	resource_t *rsc, action_t *action, action_t *action_complete,
 	pe_working_set_t *data_set);
 void force_non_unique_clone(resource_t *rsc, const char *rid, pe_working_set_t *data_set);
 resource_t *create_child_clone(resource_t *rsc, int sub_id, pe_working_set_t *data_set);
 
 static void mark_as_orphan(resource_t *rsc) 
 {
     set_bit(rsc->flags, pe_rsc_orphan);
     slist_iter(
 	child, resource_t, rsc->children, lpc,
 	mark_as_orphan(child);
 	);
 }
 
 static void clear_bit_recursive(resource_t *rsc, unsigned long long flag) 
 {
     clear_bit_inplace(rsc->flags, flag);
     if(rsc->children) {
 	slist_iter(
 	    child_rsc, resource_t, rsc->children, lpc,
 	    clear_bit_recursive(child_rsc, flag);
 	    );
     }
 }
 
 void force_non_unique_clone(resource_t *rsc, const char *rid, pe_working_set_t *data_set) 
 {
     if(rsc->variant == pe_clone || rsc->variant == pe_master) {
 	clone_variant_data_t *clone_data = NULL;
 	get_clone_variant_data(clone_data, rsc);
 	
 	crm_config_warn("Clones %s contains non-OCF resource %s and so "
 			"can only be used as an anonymous clone. "
 			"Set the "XML_RSC_ATTR_UNIQUE" meta attribute to false",
 			rsc->id, rid);
 	
 	clone_data->clone_node_max = 1;
 	clone_data->clone_max = g_list_length(data_set->nodes);
 	clear_bit_recursive(rsc, pe_rsc_unique);
     }
 }
 
+resource_t *
+find_clone_instance(resource_t *rsc, const char *sub_id, pe_working_set_t *data_set) 
+{
+    char *child_id = NULL;
+    resource_t *child = NULL;
+    const char *child_base = NULL;
+    clone_variant_data_t *clone_data = NULL;
+    get_clone_variant_data(clone_data, rsc);
+
+    child_base = ID(clone_data->xml_obj_child);
+    child_id = crm_concat(child_base, sub_id, ':');
+    child = pe_find_resource(rsc->children, child_id);
+
+    crm_free(child_id);
+    return child;
+}
+
 resource_t *
 create_child_clone(resource_t *rsc, int sub_id, pe_working_set_t *data_set) 
 {
 	gboolean as_orphan = FALSE;
 	char *inc_num = NULL;
 	char *inc_max = NULL;
 	resource_t *child_rsc = NULL;
 	xmlNode * child_copy = NULL;
 	clone_variant_data_t *clone_data = NULL;
 	get_clone_variant_data(clone_data, rsc);
 
 	CRM_CHECK(clone_data->xml_obj_child != NULL, return FALSE);
 
 	if(sub_id < 0) {
 	    as_orphan = TRUE;
 	    sub_id = clone_data->total_clones;
 	}
 	inc_num = crm_itoa(sub_id);
 	inc_max = crm_itoa(clone_data->clone_max);	
 
 	child_copy = copy_xml(clone_data->xml_obj_child);
 
 	crm_xml_add(child_copy, XML_RSC_ATTR_INCARNATION, inc_num);
 
 	if(common_unpack(child_copy, &child_rsc,
 			 rsc, data_set) == FALSE) {
 		pe_err("Failed unpacking resource %s",
 		       crm_element_value(child_copy, XML_ATTR_ID));
 		child_rsc = NULL;
 		goto bail;
 	}
 /* 	child_rsc->globally_unique = rsc->globally_unique; */
 
 	clone_data->total_clones += 1;
 	crm_debug_2("Setting clone attributes for: %s", child_rsc->id);
 	rsc->children = g_list_append(rsc->children, child_rsc);
 	if(as_orphan) {
 	    mark_as_orphan(child_rsc);
 	}
 	
 	add_hash_param(child_rsc->meta, XML_RSC_ATTR_INCARNATION_MAX, inc_max);
 	
 	print_resource(LOG_DEBUG_3, "Added", child_rsc, FALSE);
 
   bail:
 	crm_free(inc_num);
 	crm_free(inc_max);
 	
 	return child_rsc;
 }
 
 gboolean master_unpack(resource_t *rsc, pe_working_set_t *data_set)
 {
 	const char *master_max = g_hash_table_lookup(
 		rsc->meta, XML_RSC_ATTR_MASTER_MAX);
 	const char *master_node_max = g_hash_table_lookup(
 		rsc->meta, XML_RSC_ATTR_MASTER_NODEMAX);
 
 	g_hash_table_replace(rsc->meta, crm_strdup("stateful"), crm_strdup(XML_BOOLEAN_TRUE));
 	if(clone_unpack(rsc, data_set)) {
 		clone_variant_data_t *clone_data = NULL;
 		get_clone_variant_data(clone_data, rsc);
 		clone_data->master_max = crm_parse_int(master_max, "1");
 		clone_data->master_node_max = crm_parse_int(master_node_max, "1");
 		return TRUE;
 	}
 	return FALSE;
 }
 
 gboolean clone_unpack(resource_t *rsc, pe_working_set_t *data_set)
 {
 	int lpc = 0;
 	const char *type = NULL;
 	resource_t *self = NULL;
 	int num_xml_children = 0;	
 	xmlNode *xml_tmp = NULL;
 	xmlNode *xml_self = NULL;
 	xmlNode *xml_obj = rsc->xml;
 	clone_variant_data_t *clone_data = NULL;
 
 	const char *ordered = g_hash_table_lookup(
 		rsc->meta, XML_RSC_ATTR_ORDERED);
 	const char *interleave = g_hash_table_lookup(
 		rsc->meta, XML_RSC_ATTR_INTERLEAVE);
 	const char *max_clones = g_hash_table_lookup(
 		rsc->meta, XML_RSC_ATTR_INCARNATION_MAX);
 	const char *max_clones_node = g_hash_table_lookup(
 		rsc->meta, XML_RSC_ATTR_INCARNATION_NODEMAX);
 
 	crm_debug_3("Processing resource %s...", rsc->id);
 	
 	crm_malloc0(clone_data, sizeof(clone_variant_data_t));
 	rsc->variant_opaque = clone_data;
 	clone_data->interleave  = FALSE;
 	clone_data->ordered     = FALSE;
 	
 	clone_data->active_clones  = 0;
 	clone_data->xml_obj_child  = NULL;
 	clone_data->clone_node_max = crm_parse_int(max_clones_node, "1");
 
 	if(max_clones) {
 	    clone_data->clone_max = crm_parse_int(max_clones, "1");
 
 	} else if(g_list_length(data_set->nodes) > 0) {
 	    clone_data->clone_max = g_list_length(data_set->nodes);
 
 	} else {
 	    clone_data->clone_max = 1; /* Handy during crm_verify */
 	}
 	
 	if(crm_is_true(interleave)) {
 		clone_data->interleave = TRUE;
 	}
 	if(crm_is_true(ordered)) {
 		clone_data->ordered = TRUE;
 	}
 	if((rsc->flags & pe_rsc_unique) == 0 && clone_data->clone_node_max > 1) {
 	    crm_config_err("Anonymous clones (%s) may only support one copy"
 			   " per node", rsc->id);
 	    clone_data->clone_node_max = 1;
 	}
 
 	crm_debug_2("Options for %s", rsc->id);
 	crm_debug_2("\tClone max: %d", clone_data->clone_max);
 	crm_debug_2("\tClone node max: %d", clone_data->clone_node_max);
 	crm_debug_2("\tClone is unique: %s", is_set(rsc->flags, pe_rsc_unique)?"true":"false");
 
 	clone_data->xml_obj_child = find_xml_node(
 		xml_obj, XML_CIB_TAG_GROUP, FALSE);
 
 	if(clone_data->xml_obj_child == NULL) {
 	    clone_data->xml_obj_child = find_xml_node(
 		xml_obj, XML_CIB_TAG_RESOURCE, TRUE);
 	} else {
 	    xml_child_iter_filter(xml_obj, a_child, XML_CIB_TAG_RESOURCE, num_xml_children++);
 	}
 
 	if(clone_data->xml_obj_child == NULL) {
 		crm_config_err("%s has nothing to clone", rsc->id);
 		return FALSE;
 	}
 
 	type = crm_element_name(clone_data->xml_obj_child);
 	xml_child_iter_filter(xml_obj, a_child, type, num_xml_children++);
 
 	if(num_xml_children > 1) {
 	    crm_config_err("%s has too many children.  Only the first (%s) will be cloned.",
 			   rsc->id, ID(clone_data->xml_obj_child));
 	}
 	
 	xml_self = copy_xml(rsc->xml);
 	/* this is a bit of a hack - but simplifies everything else */
 	xmlNodeSetName(xml_self, ((const xmlChar*)XML_CIB_TAG_RESOURCE));
 /* 	set_id(xml_self, "self", -1); */
 	xml_tmp = find_xml_node(xml_obj, "operations", FALSE);
 	if(xml_tmp != NULL) {
 		add_node_copy(xml_self, xml_tmp);
 	}
 
 	/* Make clones ever so slightly sticky by default
 	 * This is the only way to ensure clone instances are not
 	 *  shuffled around the cluster for no benefit
 	 */
   	add_hash_param(rsc->meta, XML_RSC_ATTR_STICKINESS, "1");
 	
 	if(common_unpack(xml_self, &self, rsc, data_set)) {
 		clone_data->self = self;
 
 	} else {
 		crm_log_xml_err(xml_self, "Couldnt unpack dummy child");
 		clone_data->self = self;
 		return FALSE;
 	}
 	
 	crm_debug_2("\tClone is unique (fixed): %s", is_set(rsc->flags, pe_rsc_unique)?"true":"false");
 	clone_data->notify_confirm = is_set(rsc->flags, pe_rsc_notify);
 	add_hash_param(rsc->meta, XML_RSC_ATTR_UNIQUE,
 		       is_set(rsc->flags, pe_rsc_unique)?XML_BOOLEAN_TRUE:XML_BOOLEAN_FALSE);
 
 	for(lpc = 0; lpc < clone_data->clone_max; lpc++) {
 		create_child_clone(rsc, lpc, data_set);
 	}
 
 	if(clone_data->clone_max == 0) {
 	    /* create one so that unpack_find_resource() will hook up
 	     * any orphans up to the parent correctly
 	     */
 	    create_child_clone(rsc, -1, data_set);
 	}
 	
 	crm_debug_3("Added %d children to resource %s...",
 		    clone_data->clone_max, rsc->id);
 	return TRUE;
 }
 
 gboolean clone_active(resource_t *rsc, gboolean all)
 {
 	clone_variant_data_t *clone_data = NULL;
 	get_clone_variant_data(clone_data, rsc);
 
 	slist_iter(
 		child_rsc, resource_t, rsc->children, lpc,
 		gboolean child_active = child_rsc->fns->active(child_rsc, all);
 		if(all == FALSE && child_active) {
 			return TRUE;
 		} else if(all && child_active == FALSE) {
 			return FALSE;
 		}
 		);
 	if(all) {
 		return TRUE;
 	} else {
 		return FALSE;
 	}
 }
 
 static char *
 add_list_element(char *list, const char *value) 
 {
     int len = 0;
     int last = 0;
 
     if(value == NULL) {
 	return list;
     }
     if(list) {
 	last = strlen(list);
     }
     len = last + 2;  /* +1 space, +1 EOS */
     len += strlen(value);
     crm_realloc(list, len);
     sprintf(list + last, " %s", value);
     return list;
 }
 
 static void
 short_print(char *list, const char *prefix, const char *type, long options, void *print_data) 
 {
     if(list) {
 	if(options & pe_print_html) {
 	    status_print("<li>");
 	}
 	status_print("%s%s: [%s ]", prefix, type, list);
 
 	if(options & pe_print_html) {
 	    status_print("</li>\n");
 
 	} else if(options & pe_print_suppres_nl) {
 	    /* nothing */
 	} else if((options & pe_print_printf) || (options & pe_print_ncurses)) {
 		status_print("\n");
 	}
 
     }
 }
 
 void clone_print(
 	resource_t *rsc, const char *pre_text, long options, void *print_data)
 {
     char *child_text = NULL;
     char *master_list = NULL;
     char *started_list = NULL;
     char *stopped_list = NULL;
     const char *type = "Clone";
     clone_variant_data_t *clone_data = NULL;
     get_clone_variant_data(clone_data, rsc);
 
     if(pre_text == NULL) { pre_text = " "; }
     child_text = crm_concat(pre_text, "   ", ' ');
 
     if(rsc->variant == pe_master) {
 	type = "Master/Slave";
     }
 
     status_print("%s%s Set: %s%s%s",
 		 pre_text?pre_text:"", type, rsc->id,
 		 is_set(rsc->flags, pe_rsc_unique)?" (unique)":"",
 		 is_set(rsc->flags, pe_rsc_managed)?"":" (unmanaged)");
 	
     if(options & pe_print_html) {
 	status_print("\n<ul>\n");
 
     } else if((options & pe_print_log) == 0) {
 	status_print("\n");
     }
 
     slist_iter(
 	child_rsc, resource_t, rsc->children, lpc,
 
 	gboolean print_full = FALSE;
 	if(child_rsc->fns->active(child_rsc, FALSE) == FALSE) {
 	    /* Inactive clone */
 	    if(is_set(child_rsc->flags, pe_rsc_orphan)) {
 		continue;
 
 	    } else if(is_set(rsc->flags, pe_rsc_unique)) {
 		print_full = TRUE;
 	    } else {
 		stopped_list = add_list_element(stopped_list, child_rsc->id);
 	    }
 		
 	} else if(is_set(child_rsc->flags, pe_rsc_unique)
 		  || is_set(child_rsc->flags, pe_rsc_orphan)
 		  || is_set(child_rsc->flags, pe_rsc_managed) == FALSE
 		  || is_set(child_rsc->flags, pe_rsc_failed)) {
 
 	    /* Unique, unmanaged or failed clone */
 	    print_full = TRUE;
 		
 	} else if(child_rsc->fns->active(child_rsc, TRUE)) {
 	    /* Fully active anonymous clone */
 	    node_t *location = child_rsc->fns->location(child_rsc, NULL, TRUE);
 
 	    if(location) {
 		const char *host = location->details->uname;
 		enum rsc_role_e a_role = child_rsc->fns->state(child_rsc, TRUE);
 
 		if(a_role > RSC_ROLE_SLAVE) {
 		    /* And active on a single node as master */
 		    master_list = add_list_element(master_list, host);
 		    
 		} else {
 		    /* And active on a single node as started/slave */
 		    started_list = add_list_element(started_list, host);
 		}
 	    
 	    } else {
 		/* uncolocated group - bleh */
 		print_full = TRUE;
 	    }
 		
 	} else {
 	    /* Partially active anonymous clone */
 	    print_full = TRUE;
 	}
 	    
 	if(print_full) {
 	    if(options & pe_print_html) {
 		status_print("<li>\n");
 	    }
 	    child_rsc->fns->print(
 		child_rsc, child_text, options, print_data);
 	    if(options & pe_print_html) {
 		status_print("</li>\n");
 	    }
 	}
 	    
 	);
 	
     short_print(master_list, child_text, "Masters", options, print_data);
     short_print(started_list, child_text, rsc->variant==pe_master?"Slaves":"Started", options, print_data);
     short_print(stopped_list, child_text, "Stopped", options, print_data);
 
     crm_free(master_list);
     crm_free(started_list);
     crm_free(stopped_list);    
     
     if(options & pe_print_html) {
 	status_print("</ul>\n");
     }
 
     crm_free(child_text);
 }
 
 void clone_free(resource_t *rsc)
 {
 	clone_variant_data_t *clone_data = NULL;
 	get_clone_variant_data(clone_data, rsc);
 
 	crm_debug_3("Freeing %s", rsc->id);
 
 	slist_iter(
 		child_rsc, resource_t, rsc->children, lpc,
 
 		crm_debug_3("Freeing child %s", child_rsc->id);
 		free_xml(child_rsc->xml);
 		child_rsc->fns->free(child_rsc);
 		);
 
 	crm_debug_3("Freeing child list");
 	pe_free_shallow_adv(rsc->children, FALSE);
 
 	if(clone_data->self) {
 		free_xml(clone_data->self->xml);
 		clone_data->self->fns->free(clone_data->self);
 	}
 	common_free(rsc);
 }
 
 enum rsc_role_e
 clone_resource_state(const resource_t *rsc, gboolean current)
 {
 	enum rsc_role_e clone_role = RSC_ROLE_UNKNOWN;
 
 	clone_variant_data_t *clone_data = NULL;
 	get_clone_variant_data(clone_data, rsc);
 
 	slist_iter(
 		child_rsc, resource_t, rsc->children, lpc,
 		enum rsc_role_e a_role = child_rsc->fns->state(child_rsc, current);
 		if(a_role > clone_role) {
 			clone_role = a_role;
 		}
 		);
 
 	crm_debug_3("%s role: %s", rsc->id, role2text(clone_role));
 	return clone_role;
 }
diff --git a/lib/pengine/utils.h b/lib/pengine/utils.h
index 0dc38b05b4..60e3060a92 100644
--- a/lib/pengine/utils.h
+++ b/lib/pengine/utils.h
@@ -1,132 +1,134 @@
 /* 
  * 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.1 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 #ifndef PE_UTILS__H
 #define PE_UTILS__H
 #include <crm/pengine/common.h>
 #include <crm/pengine/status.h>
 
 
 extern node_t *node_copy(node_t *this_node) ;
 extern time_t get_timet_now(pe_working_set_t *data_set);
 extern int get_failcount(node_t *node, resource_t *rsc, int *last_failure, pe_working_set_t *data_set);
 
 /* Binary like operators for lists of nodes */
 extern GListPtr node_list_exclude(GListPtr list1, GListPtr list2, gboolean merge_scores);
 extern GListPtr node_list_dup(GListPtr list1, gboolean reset, gboolean filter);
 
 extern GListPtr node_list_and(GListPtr list1, GListPtr list2, gboolean filter);
 
 extern GListPtr node_list_xor(GListPtr list1, GListPtr list2, gboolean filter);
 
 extern GListPtr node_list_minus(GListPtr list1,GListPtr list2,gboolean filter);
 
 extern gboolean node_list_eq(GListPtr list1, GListPtr list2, gboolean filter);
 
 extern GListPtr node_list_or(GListPtr list1, GListPtr list2, gboolean filter);
 
 extern void pe_free_shallow(GListPtr alist);
 extern void pe_free_shallow_adv(GListPtr alist, gboolean with_data);
 
 /* For creating the transition graph */
 extern xmlNode *action2xml(action_t *action, gboolean as_input);
 
 /* Printing functions for debug */
 extern void print_node(
 	const char *pre_text, node_t *node, gboolean details);
 
 extern void print_resource(
 	int log_level, const char *pre_text, resource_t *rsc, gboolean details);
 
 extern void dump_node_scores(int level, resource_t *rsc, const char *comment, GListPtr nodes);
 
 extern void dump_node_capacity(int level, const char *comment, node_t *node);
 extern void dump_rsc_utilization(int level, const char *comment, resource_t *rsc, node_t *node);
 
 /* Sorting functions */
 extern gint sort_rsc_priority(gconstpointer a, gconstpointer b);
 extern gint sort_rsc_index(gconstpointer a, gconstpointer b);
 
 extern xmlNode *find_rsc_op_entry(resource_t *rsc, const char *key);
 
 extern action_t *custom_action(
 	resource_t *rsc, char *key, const char *task, node_t *on_node,
 	gboolean optional, gboolean foo, pe_working_set_t *data_set);
 
 #define delete_key(rsc) generate_op_key(rsc->id, CRMD_ACTION_DELETE, 0)
 #define delete_action(rsc, node, optional) custom_action(		\
 		rsc, delete_key(rsc), CRMD_ACTION_DELETE, node,		\
 		optional, TRUE, data_set);
 
 #define stopped_key(rsc) generate_op_key(rsc->id, CRMD_ACTION_STOPPED, 0)
 #define stopped_action(rsc, node, optional) custom_action(		\
 		rsc, stopped_key(rsc), CRMD_ACTION_STOPPED, node,	\
 		optional, TRUE, data_set);
 
 #define stop_key(rsc) generate_op_key(rsc->id, CRMD_ACTION_STOP, 0)
 #define stop_action(rsc, node, optional) custom_action(			\
 		rsc, stop_key(rsc), CRMD_ACTION_STOP, node,		\
 		optional, TRUE, data_set);
 
 #define start_key(rsc) generate_op_key(rsc->id, CRMD_ACTION_START, 0)
 #define start_action(rsc, node, optional) custom_action(		\
 		rsc, start_key(rsc), CRMD_ACTION_START, node,		\
 		optional, TRUE, data_set)
 
 #define started_key(rsc) generate_op_key(rsc->id, CRMD_ACTION_STARTED, 0)
 #define started_action(rsc, node, optional) custom_action(		\
 		rsc, started_key(rsc), CRMD_ACTION_STARTED, node,	\
 		optional, TRUE, data_set)
 
 #define promote_key(rsc) generate_op_key(rsc->id, CRMD_ACTION_PROMOTE, 0)
 #define promote_action(rsc, node, optional) custom_action(		\
 		rsc, promote_key(rsc), CRMD_ACTION_PROMOTE, node,	\
 		optional, TRUE, data_set)
 
 #define promoted_key(rsc) generate_op_key(rsc->id, CRMD_ACTION_PROMOTED, 0)
 #define promoted_action(rsc, node, optional) custom_action(		\
 		rsc, promoted_key(rsc), CRMD_ACTION_PROMOTED, node,	\
 		optional, TRUE, data_set)
 
 #define demote_key(rsc) generate_op_key(rsc->id, CRMD_ACTION_DEMOTE, 0)
 #define demote_action(rsc, node, optional) custom_action(		\
 		rsc, demote_key(rsc), CRMD_ACTION_DEMOTE, node,		\
 		optional, TRUE, data_set)
 
 #define demoted_key(rsc) generate_op_key(rsc->id, CRMD_ACTION_DEMOTED, 0)
 #define demoted_action(rsc, node, optional) custom_action(		\
 		rsc, demoted_key(rsc), CRMD_ACTION_DEMOTED, node,	\
 		optional, TRUE, data_set)
 
 extern action_t *find_first_action(GListPtr input, const char *uuid, const char *task, node_t *on_node);
 
 extern GListPtr find_actions(GListPtr input, const char *key, node_t *on_node);
 extern GListPtr find_actions_exact(
 	GListPtr input, const char *key, node_t *on_node);
 extern GListPtr find_recurring_actions(GListPtr input, node_t *not_on_node);
 
 extern void set_id(xmlNode *xml_obj, const char *prefix, int child);
 extern void pe_free_action(action_t *action);
 
 extern void
 resource_location(resource_t *rsc, node_t *node, int score, const char *tag,
 		  pe_working_set_t *data_set);
 
 extern gint sort_op_by_callid(gconstpointer a, gconstpointer b);
 extern gboolean get_target_role(resource_t *rsc, enum rsc_role_e *role);
 
+extern resource_t *find_clone_instance(resource_t *rsc, const char *sub_id, pe_working_set_t *data_set);
+
 #endif
diff --git a/pengine/constraints.c b/pengine/constraints.c
index 339e91766f..5bd78058ac 100644
--- a/pengine/constraints.c
+++ b/pengine/constraints.c
@@ -1,1188 +1,1214 @@
 /* 
  * 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.1 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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 <crm/common/msg.h>
 
 
 #include <glib.h>
 
 #include <crm/pengine/status.h>
 #include <pengine.h>
 #include <allocate.h>
 #include <utils.h>
 #include <crm/pengine/rules.h>
 #include <lib/pengine/utils.h>
 
 enum pe_order_kind 
 {
     pe_order_kind_optional,
     pe_order_kind_mandatory,
     pe_order_kind_serialize,
 };
 
 enum pe_ordering get_flags(
     const char *id, enum pe_order_kind kind,
     const char *action_first, const char *action_then, gboolean invert);
 
 gboolean 
 unpack_constraints(xmlNode * xml_constraints, pe_working_set_t *data_set)
 {
 	xmlNode *lifetime = NULL;
 	xml_child_iter(
 		xml_constraints, xml_obj, 
 
 		const char *id = crm_element_value(xml_obj, XML_ATTR_ID);
 		if(id == NULL) {
 			crm_config_err("Constraint <%s...> must have an id",
 				crm_element_name(xml_obj));
 			continue;
 		}
 
 		crm_debug_3("Processing constraint %s %s",
 			    crm_element_name(xml_obj),id);
 
 		lifetime = first_named_child(xml_obj, "lifetime");
 		if(lifetime) {
 		    crm_config_warn("Support for the lifetime tag, used by %s, is deprecated."
 				    " The rules it contains should instead be direct decendants of the constraint object", id);
 		}
 		
 		if(test_ruleset(lifetime, NULL, data_set->now) == FALSE) {
 			crm_info("Constraint %s %s is not active",
 				 crm_element_name(xml_obj), id);
 
 		} else if(safe_str_eq(XML_CONS_TAG_RSC_ORDER,
 				      crm_element_name(xml_obj))) {
 			unpack_rsc_order(xml_obj, data_set);
 
 		} else if(safe_str_eq(XML_CONS_TAG_RSC_DEPEND,
 				      crm_element_name(xml_obj))) {
 			unpack_rsc_colocation(xml_obj, data_set);
 
 		} else if(safe_str_eq(XML_CONS_TAG_RSC_LOCATION,
 				      crm_element_name(xml_obj))) {
 			unpack_rsc_location(xml_obj, data_set);
 
 		} else {
 			pe_err("Unsupported constraint type: %s",
 				crm_element_name(xml_obj));
 		}
 		);
 
 	return TRUE;
 }
 
 static const char *
 invert_action(const char *action) 
 {
 	if(safe_str_eq(action, RSC_START)) {
 		return RSC_STOP;
 
 	} else if(safe_str_eq(action, RSC_STOP)) {
 		return RSC_START;
 		
 	} else if(safe_str_eq(action, RSC_PROMOTE)) {
 		return RSC_DEMOTE;
 		
  	} else if(safe_str_eq(action, RSC_DEMOTE)) {
 		return RSC_PROMOTE;
 
 	} else if(safe_str_eq(action, RSC_PROMOTED)) {
 		return RSC_DEMOTED;
 		
 	} else if(safe_str_eq(action, RSC_DEMOTED)) {
 		return RSC_PROMOTED;
 
 	} else if(safe_str_eq(action, RSC_STARTED)) {
 		return RSC_STOPPED;
 		
 	} else if(safe_str_eq(action, RSC_STOPPED)) {
 		return RSC_STARTED;
 	}
 	crm_config_warn("Unknown action: %s", action);
 	return NULL;
 }
 
 static enum pe_order_kind get_ordering_type(xmlNode *xml_obj)
 {
     enum pe_order_kind kind_e = pe_order_kind_mandatory;
     const char *kind = crm_element_value(xml_obj, XML_ORDER_ATTR_KIND);
 
     if(kind == NULL) {
 	const char *score = crm_element_value(xml_obj, XML_RULE_ATTR_SCORE);
 	kind_e = pe_order_kind_mandatory;
 
 	if(score) {
 	    int score_i = char2score(score);
 	    if(score_i == 0) {
 		kind_e = pe_order_kind_optional;
 	    }
 
 	/* } else if(rsc_then->variant == pe_native && rsc_first->variant > pe_group) { */
 	/*     kind_e = pe_order_kind_optional; */
 	}
 	    
     } else if(safe_str_eq(kind, "Mandatory")) {
 	kind_e = pe_order_kind_mandatory;
 
     } else if(safe_str_eq(kind, "Optional")) {
 	kind_e = pe_order_kind_optional;
 
     } else if(safe_str_eq(kind, "Serialize")) {
 	kind_e = pe_order_kind_serialize;
 
     } else {
 	const char *id = crm_element_value(xml_obj, XML_ATTR_ID);
 	crm_config_err("Constraint %s: Unknown type '%s'", id, kind);
     }
     return kind_e;
 }
 
 static gboolean
 unpack_simple_rsc_order(xmlNode * xml_obj, pe_working_set_t *data_set)
 {
 	int order_id = 0;
 	resource_t *rsc_then = NULL;
 	resource_t *rsc_first = NULL;
 	gboolean invert_bool = TRUE;
 	enum pe_order_kind kind = pe_order_kind_mandatory;
 	enum pe_ordering cons_weight = pe_order_optional;
 
 	const char *id_first  = NULL;
 	const char *id_then  = NULL;
 	const char *action_then = NULL;
 	const char *action_first = NULL;
 	
 	const char *id     = crm_element_value(xml_obj, XML_ATTR_ID);
 	const char *invert = crm_element_value(xml_obj, XML_CONS_ATTR_SYMMETRICAL);
 
 	crm_str_to_boolean(invert, &invert_bool);
 	
 	if(xml_obj == NULL) {
 		crm_config_err("No constraint object to process.");
 		return FALSE;
 
 	} else if(id == NULL) {
 		crm_config_err("%s constraint must have an id",
 			crm_element_name(xml_obj));
 		return FALSE;		
 	}
 
 	id_then  = crm_element_value(xml_obj, XML_ORDER_ATTR_THEN);
 	id_first = crm_element_value(xml_obj, XML_ORDER_ATTR_FIRST);
 
 	action_then  = crm_element_value(xml_obj, XML_ORDER_ATTR_THEN_ACTION);
 	action_first = crm_element_value(xml_obj, XML_ORDER_ATTR_FIRST_ACTION);
 
 	if(action_first == NULL) {
 	    action_first = RSC_START;
 	}
 	if(action_then == NULL) {
 	    action_then = action_first;
 	}
 
 	if(id_then == NULL || id_first == NULL) {
 		crm_config_err("Constraint %s needs two sides lh: %s rh: %s",
 			      id, crm_str(id_then), crm_str(id_first));
 		return FALSE;
 	}	
 
 	rsc_then = pe_find_resource(data_set->resources, id_then);
 	rsc_first = pe_find_resource(data_set->resources, id_first);
 
 	if(rsc_then == NULL) {
 		crm_config_err("Constraint %s: no resource found for name '%s'", id, id_then);
 		return FALSE;
 	
 	} else if(rsc_first == NULL) {
 		crm_config_err("Constraint %s: no resource found for name '%s'", id, id_first);
 		return FALSE;
 	}
 
 	cons_weight = pe_order_optional;
 	kind = get_ordering_type(xml_obj);
 	
 	if(kind == pe_order_kind_optional && rsc_then->restart_type == pe_restart_restart) {
 	    crm_debug_2("Upgrade : recovery - implies right");
 	    cons_weight |= pe_order_implies_right;
 	}
 
 	cons_weight |= get_flags(id, kind, action_first, action_then, FALSE);
 	order_id = new_rsc_order(
 	    rsc_first, action_first, rsc_then, action_then, cons_weight, data_set);
 
 	crm_debug_2("order-%d (%s): %s_%s before %s_%s flags=0x%.6x",
 		    order_id, id, rsc_first->id, action_first, rsc_then->id, action_then,
 		    cons_weight);
 	
 	
 	if(invert_bool == FALSE) {
 		return TRUE;
 
 	} else if(invert && kind == pe_order_kind_serialize) {
 	    crm_config_warn("Cannot invert serialized constraint set %s", id);
 	    return TRUE;
 
 	} else if(kind == pe_order_kind_serialize) {
 	    return TRUE;
 	}
 	
 	action_then = invert_action(action_then);
 	action_first = invert_action(action_first);
 	if(action_then == NULL || action_first == NULL) {
 		crm_config_err("Cannot invert rsc_order constraint %s."
 			       " Please specify the inverse manually.", id);
 		return TRUE;
 	}
 
 	cons_weight = pe_order_optional;
 	if(kind == pe_order_kind_optional && rsc_then->restart_type == pe_restart_restart) {
 	    crm_debug_2("Upgrade : recovery - implies left");
 	    cons_weight |= pe_order_implies_left;
 	}
 
 	cons_weight |= get_flags(id, kind, action_first, action_then, TRUE);
 	order_id = new_rsc_order(
 	    rsc_then, action_then, rsc_first, action_first, cons_weight, data_set);
 
 	crm_debug_2("order-%d (%s): %s_%s before %s_%s flags=0x%.6x",
 		    order_id, id, rsc_then->id, action_then, rsc_first->id, action_first,
 		    cons_weight);
 	
 	return TRUE;
 }
 
 gboolean
 unpack_rsc_location(xmlNode * xml_obj, pe_working_set_t *data_set)
 {
 	gboolean empty = TRUE;
 	const char *id_lh   = crm_element_value(xml_obj, "rsc");
 	const char *id      = crm_element_value(xml_obj, XML_ATTR_ID);
 	resource_t *rsc_lh  = pe_find_resource(data_set->resources, id_lh);
 	const char *node    = crm_element_value(xml_obj, "node");
 	const char *score   = crm_element_value(xml_obj, XML_RULE_ATTR_SCORE);
 	
 	if(rsc_lh == NULL) {
 		/* only a warn as BSC adds the constraint then the resource */
 		crm_config_warn("No resource (con=%s, rsc=%s)", id, id_lh);
 		return FALSE;
 	}
 
 	if(node != NULL && score != NULL) {
 	    int score_i = char2score(score);
 	    node_t *match = pe_find_node(data_set->nodes, node);
 
 	    if(match) {
 		rsc2node_new(id, rsc_lh, score_i, match, data_set);
 		return TRUE;
 	    } else {
 		return FALSE;
 	    }
 	}
 	
 	xml_child_iter_filter(
 		xml_obj, rule_xml, XML_TAG_RULE,
 		empty = FALSE;
 		crm_debug_2("Unpacking %s/%s", id, ID(rule_xml));
 		generate_location_rule(rsc_lh, rule_xml, data_set);
 		);
 
 	if(empty) {
 		crm_config_err("Invalid location constraint %s:"
 			      " rsc_location must contain at least one rule",
 			      ID(xml_obj));
 	}
 	return TRUE;
 }
 
 static int
 get_node_score(const char *rule, const char *score, gboolean raw, node_t *node)
 {
 	int score_f = 0;
 	if(score == NULL) {
 		pe_err("Rule %s: no score specified.  Assuming 0.", rule);
 	
 	} else if(raw) {
 		score_f = char2score(score);
 	
 	} else {
 		const char *attr_score = g_hash_table_lookup(
 			node->details->attrs, score);
 		if(attr_score == NULL) {
 			crm_debug("Rule %s: node %s did not have a value for %s",
 				  rule, node->details->uname, score);
 			score_f = -INFINITY;
 			
 		} else {
 			crm_debug("Rule %s: node %s had value %s for %s",
 				  rule, node->details->uname, attr_score, score);
 			score_f = char2score(attr_score);
 		}
 	}
 	return score_f;
 }
 
 
 rsc_to_node_t *
 generate_location_rule(
 	resource_t *rsc, xmlNode *rule_xml, pe_working_set_t *data_set)
 {	
 	const char *rule_id = NULL;
 	const char *score   = NULL;
 	const char *boolean = NULL;
 	const char *role    = NULL;
 
 	GListPtr match_L  = NULL;
 	
 	int score_f   = 0;
 	gboolean do_and = TRUE;
 	gboolean accept = TRUE;
 	gboolean raw_score = TRUE;
 	
 	rsc_to_node_t *location_rule = NULL;
 	
 	rule_xml = expand_idref(rule_xml, data_set->input);
 	rule_id = crm_element_value(rule_xml, XML_ATTR_ID);
 	boolean = crm_element_value(rule_xml, XML_RULE_ATTR_BOOLEAN_OP);
 	role = crm_element_value(rule_xml, XML_RULE_ATTR_ROLE);
 
 	crm_debug_2("Processing rule: %s", rule_id);
 
 	if(role != NULL && text2role(role) == RSC_ROLE_UNKNOWN) {
 		pe_err("Bad role specified for %s: %s", rule_id, role);
 		return NULL;
 	}
 	
 	score = crm_element_value(rule_xml, XML_RULE_ATTR_SCORE);
 	if(score != NULL) {
 		score_f = char2score(score);
 		
 	} else {
 		score = crm_element_value(
 			rule_xml, XML_RULE_ATTR_SCORE_ATTRIBUTE);
 		if(score == NULL) {
 			score = crm_element_value(
 				rule_xml, XML_RULE_ATTR_SCORE_MANGLED);
 		}
 		if(score != NULL) {
 			raw_score = FALSE;
 		}
 	}
 	if(safe_str_eq(boolean, "or")) {
 		do_and = FALSE;
 	}
 	
 	location_rule = rsc2node_new(rule_id, rsc, 0, NULL, data_set);
 	
 	if(location_rule == NULL) {
 		return NULL;
 	}
 	if(role != NULL) {
 		crm_debug_2("Setting role filter: %s", role);
 		location_rule->role_filter = text2role(role);
 		if(location_rule->role_filter == RSC_ROLE_SLAVE) {
 		    /* Fold slave back into Started for simplicity
 		     * At the point Slave location constraints are evaluated,
 		     * all resources are still either stopped or started
 		     */  
 		    location_rule->role_filter = RSC_ROLE_STARTED;
 		}
 	}
 	if(do_and) {
 		match_L = node_list_dup(data_set->nodes, TRUE, FALSE);
 		slist_iter(
 			node, node_t, match_L, lpc,
 			node->weight = get_node_score(rule_id, score, raw_score, node);
 			);
 	}
 
 	slist_iter(
 	    node, node_t, data_set->nodes, lpc,
 
 			accept = test_rule(
 			    rule_xml, node->details->attrs, RSC_ROLE_UNKNOWN, data_set->now);
 
 			crm_debug_2("Rule %s %s on %s", ID(rule_xml), accept?"passed":"failed", node->details->uname);
 
 			score_f = get_node_score(rule_id, score, raw_score, node);
 /* 			if(accept && score_f == -INFINITY) { */
 /* 				accept = FALSE; */
 /* 			} */
 			
 			if(accept) {
 				node_t *local = pe_find_node_id(
 					match_L, node->details->id);
 				if(local == NULL && do_and) {
 					continue;
 					
 				} else if(local == NULL) {
 					local = node_copy(node);
 					match_L = g_list_append(match_L, local);
 				}
 
 				if(do_and == FALSE) {
 					local->weight = merge_weights(
 						local->weight, score_f);
 				}
 				crm_debug_2("node %s now has weight %d",
 					    node->details->uname, local->weight);
 				
 			} else if(do_and && !accept) {
 				/* remove it */
 				node_t *delete = pe_find_node_id(
 					match_L, node->details->id);
 				if(delete != NULL) {
 					match_L = g_list_remove(match_L,delete);
 					crm_debug_5("node %s did not match",
 						    node->details->uname);
 				}
 				crm_free(delete);
 			}
 		);
 	
 	location_rule->node_list_rh = match_L;
 	if(location_rule->node_list_rh == NULL) {
 		crm_debug_2("No matching nodes for rule %s", rule_id);
 		return NULL;
 	} 
 
 	crm_debug_3("%s: %d nodes matched",
 		    rule_id, g_list_length(location_rule->node_list_rh));
 	return location_rule;
 }
 
 static gint sort_cons_priority_lh(gconstpointer a, gconstpointer b)
 {
 	const rsc_colocation_t *rsc_constraint1 = (const rsc_colocation_t*)a;
 	const rsc_colocation_t *rsc_constraint2 = (const rsc_colocation_t*)b;
 
 	if(a == NULL) { return 1; }
 	if(b == NULL) { return -1; }
 
 	CRM_ASSERT(rsc_constraint1->rsc_lh != NULL);
 	CRM_ASSERT(rsc_constraint1->rsc_rh != NULL);
 	
 	if(rsc_constraint1->rsc_lh->priority > rsc_constraint2->rsc_lh->priority) {
 	    return -1;
 	}
 	
 	if(rsc_constraint1->rsc_lh->priority < rsc_constraint2->rsc_lh->priority) {
 	    return 1;
 	}
 
 	return strcmp(rsc_constraint1->rsc_lh->id, rsc_constraint2->rsc_lh->id);
 }
 
 static gint sort_cons_priority_rh(gconstpointer a, gconstpointer b)
 {
 	const rsc_colocation_t *rsc_constraint1 = (const rsc_colocation_t*)a;
 	const rsc_colocation_t *rsc_constraint2 = (const rsc_colocation_t*)b;
 
 	if(a == NULL) { return 1; }
 	if(b == NULL) { return -1; }
 
 	CRM_ASSERT(rsc_constraint1->rsc_lh != NULL);
 	CRM_ASSERT(rsc_constraint1->rsc_rh != NULL);
 	
 	if(rsc_constraint1->rsc_rh->priority > rsc_constraint2->rsc_rh->priority) {
 	    return -1;
 	}
 	
 	if(rsc_constraint1->rsc_rh->priority < rsc_constraint2->rsc_rh->priority) {
 	    return 1;
 	}
 	return strcmp(rsc_constraint1->rsc_rh->id, rsc_constraint2->rsc_rh->id);
 }
 
 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)
 {
 	rsc_colocation_t *new_con      = NULL;
 	if(rsc_lh == NULL){
 		crm_config_err("No resource found for LHS %s", id);
 		return FALSE;
 
 	} else if(rsc_rh == NULL){
 		crm_config_err("No resource found for RHS of %s", id);
 		return FALSE;
 	}
 
 	crm_malloc0(new_con, sizeof(rsc_colocation_t));
 	if(new_con == NULL) {
 		return FALSE;
 	}
 
 	if(state_lh == NULL
 	   || safe_str_eq(state_lh, RSC_ROLE_STARTED_S)) {
 		state_lh = RSC_ROLE_UNKNOWN_S;
 	}
 
 	if(state_rh == NULL
 	   || safe_str_eq(state_rh, RSC_ROLE_STARTED_S)) {
 		state_rh = RSC_ROLE_UNKNOWN_S;
 	} 
 
 	new_con->id       = id;
 	new_con->rsc_lh   = rsc_lh;
 	new_con->rsc_rh   = rsc_rh;
 	new_con->score   = score;
 	new_con->role_lh = text2role(state_lh);
 	new_con->role_rh = text2role(state_rh);
 	new_con->node_attribute = node_attr;
 
 	if(node_attr == NULL) {
 	    node_attr = "#"XML_ATTR_UNAME;
 	}
 	
 	crm_debug_3("%s ==> %s (%s %d)", rsc_lh->id, rsc_rh->id, node_attr, score);
 	
 	rsc_lh->rsc_cons = g_list_insert_sorted(
 		rsc_lh->rsc_cons, new_con, sort_cons_priority_rh);
 
 	rsc_rh->rsc_cons_lhs = g_list_insert_sorted(
 		rsc_rh->rsc_cons_lhs, new_con, sort_cons_priority_lh);
 
 	data_set->colocation_constraints = g_list_append(
 		data_set->colocation_constraints, new_con);
 	
 	return TRUE;
 }
 
 /* LHS before RHS */
 int
 new_rsc_order(resource_t *lh_rsc, const char *lh_task, 
 	      resource_t *rh_rsc, const char *rh_task, 
 	      enum pe_ordering type, pe_working_set_t *data_set)
 {
     char *lh_key = NULL;
     char *rh_key = NULL;
 
     CRM_CHECK(lh_rsc != NULL,  return -1);
     CRM_CHECK(lh_task != NULL, return -1);
     CRM_CHECK(rh_rsc != NULL,  return -1);
     CRM_CHECK(rh_task != NULL, return -1);
     
     lh_key = generate_op_key(lh_rsc->id, lh_task, 0);
     rh_key = generate_op_key(rh_rsc->id, rh_task, 0);
     
     return custom_action_order(lh_rsc, lh_key, NULL,
 			       rh_rsc, rh_key, NULL, type, data_set);
 }
 
 /* LHS before RHS */
 int
 custom_action_order(
 	resource_t *lh_rsc, char *lh_action_task, action_t *lh_action,
 	resource_t *rh_rsc, char *rh_action_task, action_t *rh_action,
 	enum pe_ordering type, pe_working_set_t *data_set)
 {
 	order_constraint_t *order = NULL;
 	if(lh_rsc == NULL && lh_action) {
 		lh_rsc = lh_action->rsc;
 	}
 	if(rh_rsc == NULL && rh_action) {
 		rh_rsc = rh_action->rsc;
 	}
 
 	if((lh_action == NULL && lh_rsc == NULL)
 	   || (rh_action == NULL && rh_rsc == NULL)){
 		crm_config_err("Invalid inputs %p.%p %p.%p",
 			      lh_rsc, lh_action, rh_rsc, rh_action);
 		crm_free(lh_action_task);
 		crm_free(rh_action_task);
 		return -1;
 	}
 	
 	crm_malloc0(order, sizeof(order_constraint_t));
 
 	crm_debug_3("Creating ordering constraint %d",
 		    data_set->order_id);
 	
 	order->id             = data_set->order_id++;
 	order->type           = type;
 	order->lh_rsc         = lh_rsc;
 	order->rh_rsc         = rh_rsc;
 	order->lh_action      = lh_action;
 	order->rh_action      = rh_action;
 	order->lh_action_task = lh_action_task;
 	order->rh_action_task = rh_action_task;
 	
 	data_set->ordering_constraints = g_list_append(
 		data_set->ordering_constraints, order);
 	
 	if(lh_rsc != NULL && rh_rsc != NULL) {
 		crm_debug_4("Created ordering constraint %d (%s):"
 			 " %s/%s before %s/%s",
 			 order->id, ordering_type2text(order->type),
 			 lh_rsc->id, lh_action_task,
 			 rh_rsc->id, rh_action_task);
 		
 	} else if(lh_rsc != NULL) {
 		crm_debug_4("Created ordering constraint %d (%s):"
 			 " %s/%s before action %d (%s)",
 			 order->id, ordering_type2text(order->type),
 			 lh_rsc->id, lh_action_task,
 			 rh_action->id, rh_action_task);
 		
 	} else if(rh_rsc != NULL) {
 		crm_debug_4("Created ordering constraint %d (%s):"
 			 " action %d (%s) before %s/%s",
 			 order->id, ordering_type2text(order->type),
 			 lh_action->id, lh_action_task,
 			 rh_rsc->id, rh_action_task);
 		
 	} else {
 		crm_debug_4("Created ordering constraint %d (%s):"
 			 " action %d (%s) before action %d (%s)",
 			 order->id, ordering_type2text(order->type),
 			 lh_action->id, lh_action_task,
 			 rh_action->id, rh_action_task);
 	}
 	
 	return order->id;
 }
 
 enum pe_ordering get_flags(
     const char *id, enum pe_order_kind kind,
     const char *action_first, const char *action_then, gboolean invert) {
     enum pe_ordering flags = pe_order_optional;
 
     if(invert && kind == pe_order_kind_mandatory) {
 	crm_debug_2("Upgrade %s: implies left", id);
 	flags |= pe_order_implies_left;
 	if(safe_str_eq(action_then, RSC_DEMOTE)) {
 	    crm_debug_2("Upgrade %s: demote", id);
 	    flags |= pe_order_demote;
 	}
 	
     } else if(kind == pe_order_kind_mandatory) {
 	crm_debug_2("Upgrade %s: implies right", id);
 	flags |= pe_order_implies_right;
 	if(safe_str_eq(action_first, RSC_START)
 	   || safe_str_eq(action_first, RSC_PROMOTE)) {
 	    crm_debug_2("Upgrade %s: runnable", id);
 	    flags |= pe_order_runnable_left;
 	}
 
     } else if(kind == pe_order_kind_serialize) {
 	flags |= pe_order_serialize_only;
     }
     
     return flags;
 }
 
 
 static gboolean
 unpack_order_set(xmlNode *set, enum pe_order_kind kind, resource_t **rsc,
 		 action_t **begin, action_t **end, action_t **inv_begin, action_t **inv_end,
 		 const char *symmetrical, pe_working_set_t *data_set) 
 {
     GListPtr set_iter = NULL;
     GListPtr resources = NULL;
     
     resource_t *last = NULL;
     resource_t *resource = NULL;
 
     int local_kind = kind;
     gboolean sequential = FALSE;
     enum pe_ordering flags = pe_order_optional;
     
     char *key = NULL;
     const char *id = ID(set);
     const char *action = crm_element_value(set, "action");
     const char *sequential_s = crm_element_value(set, "sequential");
     const char *kind_s = crm_element_value(set, XML_ORDER_ATTR_KIND);
 
     char *pseudo_id = NULL;
     char *end_id    = NULL;
     char *begin_id  = NULL;
 
     if(action == NULL) {
 	action = RSC_START;
     }
 
     if(kind_s) {
 	local_kind = get_ordering_type(set);
     }
     if(sequential_s == NULL) {
 	sequential_s = "1";
     }
     
     sequential = crm_is_true(sequential_s);
     flags = get_flags(id, local_kind, action, action, FALSE);
 
     xml_child_iter_filter(
 	set, xml_rsc, XML_TAG_RESOURCE_REF,
 	
 	resource = pe_find_resource(data_set->resources, ID(xml_rsc));
 	resources = g_list_append(resources, resource);
 	);
 
     if(g_list_length(resources) == 1) {
 	crm_err("Single set: %s", id);
 	*rsc = resource;
 	*end = NULL;
 	*begin = NULL;
 	*inv_end = NULL;
 	*inv_begin = NULL;
 	return TRUE;
     }
 
     pseudo_id = crm_concat(id, action, '-');
     end_id    = crm_concat(pseudo_id, "end", '-');
     begin_id  = crm_concat(pseudo_id, "begin", '-');
 
     *rsc = NULL;
     *end = get_pseudo_op(end_id, data_set);
     *begin = get_pseudo_op(begin_id, data_set);    
     
     set_iter = resources;
     while(set_iter != NULL) {
 	resource = (resource_t *) set_iter->data;
 	set_iter = set_iter->next;
 
 	key = generate_op_key(resource->id, action, 0);
 
 	custom_action_order(NULL, NULL, *begin, resource, crm_strdup(key), NULL,
 			    flags|pe_order_implies_left_printed, data_set);
 	
 	custom_action_order(resource, crm_strdup(key), NULL, NULL, NULL, *end,
 			    flags|pe_order_implies_right_printed, data_set);
 	
 	if(local_kind == pe_order_kind_serialize) {
 	    /* Serialize before everything that comes after */
 	    slist_iter(
 		then_rsc, resource_t, set_iter, lpc,
 
 		char *then_key = generate_op_key(then_rsc->id, action, 0);
 		custom_action_order(resource, crm_strdup(key), NULL, then_rsc, then_key, NULL,
 				    flags, data_set);
 		);
 		
 	} else if(sequential) {
 	    if(last != NULL) {
 		new_rsc_order(last, action, resource, action, flags, data_set);
 	    }
 	    last = resource;
 	}
     }
     
     if(crm_is_true(symmetrical) == FALSE) {
 	goto done;
 
     } else if(symmetrical && local_kind == pe_order_kind_serialize) {
 	crm_config_warn("Cannot invert serialized constraint set %s", id);
 	goto done;
 
     } else if(local_kind == pe_order_kind_serialize) {
 	goto done;
     }
     
 
     last = NULL;
     action = invert_action(action);
     
     pseudo_id = crm_concat(id, action, '-');
     end_id    = crm_concat(pseudo_id, "end", '-');
     begin_id  = crm_concat(pseudo_id, "begin", '-');
 
     *inv_end = get_pseudo_op(end_id, data_set);
     *inv_begin = get_pseudo_op(begin_id, data_set);
 
     flags = get_flags(id, local_kind, action, action, TRUE);
 
     set_iter = resources;
     while(set_iter != NULL) {
 	resource = (resource_t *) set_iter->data;
 	set_iter = set_iter->next;
 
 	key = generate_op_key(resource->id, action, 0);
 
 	custom_action_order(NULL, NULL, *inv_begin, resource, crm_strdup(key), NULL,
 			    flags|pe_order_implies_left_printed, data_set);
 
 	custom_action_order(resource, crm_strdup(key), NULL, NULL, NULL, *inv_end,
 			    flags|pe_order_implies_right_printed, data_set);
 	
 	if(sequential) {
 	    if(last != NULL) {
 		new_rsc_order(resource, action, last, action, flags, data_set);
 	    }
 	    last = resource;
 	}
     }
 
   done:
     g_list_free(resources);
     crm_free(pseudo_id);
     return TRUE;
 }
 
 static gboolean order_rsc_sets(
     const char *id, xmlNode *set1, xmlNode *set2, enum pe_order_kind kind, pe_working_set_t *data_set) {
 		
     resource_t *rsc_1 = NULL;
     resource_t *rsc_2 = NULL;
 	    
     const char *action_1 = crm_element_value(set1, "action");
     const char *action_2 = crm_element_value(set2, "action");
 
     const char *sequential_1 = crm_element_value(set1, "sequential");
     const char *sequential_2 = crm_element_value(set2, "sequential");
 
     enum pe_ordering flags = get_flags(id, kind, action_1, action_2, FALSE);
 	    
     if(crm_is_true(sequential_1)) {
 	/* get the first one */
 	xml_child_iter_filter(
 	    set1, xml_rsc, XML_TAG_RESOURCE_REF,
 	    rsc_1 = pe_find_resource(data_set->resources, ID(xml_rsc));
 	    break;
 	    );
     }
 
     if(crm_is_true(sequential_2)) {
 	/* get the last one */
 	const char *rid = NULL;
 	xml_child_iter_filter(
 	    set2, xml_rsc, XML_TAG_RESOURCE_REF,
 	    rid = ID(xml_rsc);
 	    );
 	rsc_2 = pe_find_resource(data_set->resources, rid);
     }
 
     if(rsc_1 != NULL && rsc_2 != NULL) {
 	new_rsc_order(rsc_1, action_1, rsc_2, action_2, flags, data_set);
 
     } else if(rsc_1 != NULL) {
 	xml_child_iter_filter(
 	    set2, xml_rsc, XML_TAG_RESOURCE_REF,
 	    rsc_2 = pe_find_resource(data_set->resources, ID(xml_rsc));
 	    new_rsc_order(rsc_1, action_1, rsc_2, action_2, flags, data_set);
 	    );
 
     } else if(rsc_2 != NULL) {
 	xml_child_iter_filter(
 	    set1, xml_rsc, XML_TAG_RESOURCE_REF,
 	    rsc_1 = pe_find_resource(data_set->resources, ID(xml_rsc));
 	    new_rsc_order(rsc_1, action_1, rsc_2, action_2, flags, data_set);
 	    );
 
     } else {
 	xml_child_iter_filter(
 	    set1, xml_rsc, XML_TAG_RESOURCE_REF,
 	    rsc_1 = pe_find_resource(data_set->resources, ID(xml_rsc));
 
 	    xml_child_iter_filter(
 		set2, xml_rsc_2, XML_TAG_RESOURCE_REF,
 		rsc_2 = pe_find_resource(data_set->resources, ID(xml_rsc_2));
 		new_rsc_order(rsc_1, action_1, rsc_2, action_2, flags, data_set);
 		);
 	    );
     }
     return TRUE;
 }
 
 static char *null_or_opkey(resource_t *rsc, const char *action) 
 {
     if(rsc == NULL) {
 	return NULL;
     }
     return generate_op_key(rsc->id, action, 0);
 }
 
 gboolean
 unpack_rsc_order(xmlNode *xml_obj, pe_working_set_t *data_set)
 {
 	gboolean any_sets = FALSE;
 
 	resource_t *rsc = NULL;
 	resource_t *last_rsc = NULL;
 
 	action_t *set_end = NULL;
 	action_t *set_begin = NULL;
 
 	action_t *set_inv_end = NULL;
 	action_t *set_inv_begin = NULL;
 	
 	xmlNode *last = NULL;
 	action_t *last_end = NULL;
 	action_t *last_begin = NULL;
 	action_t *last_inv_end = NULL;
 	action_t *last_inv_begin = NULL;
 	
 	const char *id    = crm_element_value(xml_obj, XML_ATTR_ID);
 	const char *invert = crm_element_value(xml_obj, XML_CONS_ATTR_SYMMETRICAL);
 	enum pe_order_kind kind = get_ordering_type(xml_obj);
 
 	if(invert == NULL) {
 	    invert = "true";
 	}
 
 	xml_child_iter_filter(
 	    xml_obj, set, XML_CONS_TAG_RSC_SET,
 
 	    any_sets = TRUE;
 	    set = expand_idref(set, data_set->input);
 	    if(unpack_order_set(set, kind, &rsc, &set_begin, &set_end,
 				&set_inv_begin, &set_inv_end, invert, data_set) == FALSE) {
 		return FALSE;
 
 	    } else if(last) {
 		const char *set_action = crm_element_value(set, "action");
 		const char *last_action = crm_element_value(last, "action");
 		enum pe_ordering flags = get_flags(id, kind, last_action, set_action, FALSE);
 
 
 		if(!set_action) { set_action = RSC_START; }
 		if(!last_action) { last_action = RSC_START; }
 		
 		if(rsc == NULL && last_rsc == NULL) {
 		    order_actions(last_end, set_begin, flags);
 		} else {
 		    custom_action_order(
 			last_rsc, null_or_opkey(last_rsc, last_action), last_end,
 			rsc, null_or_opkey(rsc, set_action), set_begin,
 			flags, data_set);
 		}
 		
 		if(crm_is_true(invert)) {
 		    set_action = invert_action(set_action);
 		    last_action = invert_action(last_action);
 		    
 		    flags = get_flags(id, kind, last_action, set_action, TRUE);
 		    if(rsc == NULL && last_rsc == NULL) {
 			order_actions(last_inv_begin, set_inv_end, flags);
 
 		    } else {
 			custom_action_order(
 			    last_rsc, null_or_opkey(last_rsc, last_action), last_inv_begin,
 			    rsc, null_or_opkey(rsc, set_action), set_inv_end,
 			    flags, data_set);
 		    }
 		}
 		    
 	    } else if(/* never called */last && order_rsc_sets(id, last, set, kind, data_set) == FALSE) {
 		return FALSE;
 
 	    }
 	    last = set;
 	    last_rsc = rsc;
 	    last_end = set_end;
 	    last_begin = set_begin;
 	    last_inv_end = set_inv_end;
 	    last_inv_begin = set_inv_begin;
 	    );
 
 	if(any_sets == FALSE) {
 	    return unpack_simple_rsc_order(xml_obj, data_set);
 	}
 
 	return TRUE;
 }
 
 static gboolean
 unpack_colocation_set(xmlNode *set, int score, pe_working_set_t *data_set) 
 {
     resource_t *with = NULL;
     resource_t *resource = NULL;
     const char *set_id = ID(set);
     const char *role = crm_element_value(set, "role");
     const char *sequential = crm_element_value(set, "sequential");
     int local_score = score;
 
     const char *score_s = crm_element_value(set, XML_RULE_ATTR_SCORE);
     if(score_s) {
 	local_score = char2score(score_s);
     }
     
     if(sequential == NULL || crm_is_true(sequential)) {
 	xml_child_iter_filter(
 	    set, xml_rsc, XML_TAG_RESOURCE_REF,
 	    
 	    resource = pe_find_resource(data_set->resources, ID(xml_rsc));
 	    if(with != NULL) {
 		crm_debug_2("Colocating %s with %s", resource->id, with->id);
 		rsc_colocation_new(set_id, NULL, local_score, resource, with, role, role, data_set);
 	    }
 
 	    with = resource;
 	    );
     }
     
     return TRUE;
 }
 
 	    
 
 static gboolean colocate_rsc_sets(
     const char *id, xmlNode *set1, xmlNode *set2, int score, pe_working_set_t *data_set)
 {
     resource_t *rsc_1 = NULL;
     resource_t *rsc_2 = NULL;
 	    
     const char *role_1 = crm_element_value(set1, "role");
     const char *role_2 = crm_element_value(set2, "role");
 
     const char *sequential_1 = crm_element_value(set1, "sequential");
     const char *sequential_2 = crm_element_value(set2, "sequential");
 
     if(crm_is_true(sequential_1)) {
 	/* get the first one */
 	xml_child_iter_filter(
 	    set1, xml_rsc, XML_TAG_RESOURCE_REF,
 	    rsc_1 = pe_find_resource(data_set->resources, ID(xml_rsc));
 	    break;
 	    );
     }
 
     if(crm_is_true(sequential_2)) {
 	/* get the last one */
 	const char *rid = NULL;
 	xml_child_iter_filter(
 	    set2, xml_rsc, XML_TAG_RESOURCE_REF,
 	    rid = ID(xml_rsc);
 	    );
 	rsc_2 = pe_find_resource(data_set->resources, rid);
     }
 
     if(rsc_1 != NULL && rsc_2 != NULL) {
 	rsc_colocation_new(id, NULL, score, rsc_1, rsc_2, role_1, role_2, data_set);
 
     } else if(rsc_1 != NULL) {
 	xml_child_iter_filter(
 	    set2, xml_rsc, XML_TAG_RESOURCE_REF,
 	    rsc_2 = pe_find_resource(data_set->resources, ID(xml_rsc));
 	    rsc_colocation_new(id, NULL, score, rsc_1, rsc_2, role_1, role_2, data_set);
 	    );
 
     } else if(rsc_2 != NULL) {
 	xml_child_iter_filter(
 	    set1, xml_rsc, XML_TAG_RESOURCE_REF,
 	    rsc_1 = pe_find_resource(data_set->resources, ID(xml_rsc));
 	    rsc_colocation_new(id, NULL, score, rsc_1, rsc_2, role_1, role_2, data_set);
 	    );
 
     } else {
 	xml_child_iter_filter(
 	    set1, xml_rsc, XML_TAG_RESOURCE_REF,
 	    rsc_1 = pe_find_resource(data_set->resources, ID(xml_rsc));
 
 	    xml_child_iter_filter(
 		set2, xml_rsc_2, XML_TAG_RESOURCE_REF,
 		rsc_2 = pe_find_resource(data_set->resources, ID(xml_rsc_2));
 		rsc_colocation_new(id, NULL, score, rsc_1, rsc_2, role_1, role_2, data_set);
 		);
 	    );
     }
 
     return TRUE;
 }
 
 static gboolean unpack_simple_colocation(xmlNode *xml_obj, pe_working_set_t *data_set)
 {
     int score_i = 0;
 
     const char *id       = crm_element_value(xml_obj, XML_ATTR_ID);
     const char *score    = crm_element_value(xml_obj, XML_RULE_ATTR_SCORE);
     
     const char *id_lh    = crm_element_value(xml_obj, XML_COLOC_ATTR_SOURCE);
     const char *id_rh    = crm_element_value(xml_obj, XML_COLOC_ATTR_TARGET);
     const char *state_lh = crm_element_value(xml_obj, XML_COLOC_ATTR_SOURCE_ROLE);
     const char *state_rh = crm_element_value(xml_obj, XML_COLOC_ATTR_TARGET_ROLE);
+    const char *instance_lh = crm_element_value(xml_obj, XML_COLOC_ATTR_SOURCE_INSTANCE);
+    const char *instance_rh = crm_element_value(xml_obj, XML_COLOC_ATTR_TARGET_INSTANCE);
     const char *attr     = crm_element_value(xml_obj, XML_COLOC_ATTR_NODE_ATTR);    
 
     const char *symmetrical = crm_element_value(xml_obj, XML_CONS_ATTR_SYMMETRICAL);
     
     resource_t *rsc_lh = pe_find_resource(data_set->resources, id_lh);
     resource_t *rsc_rh = pe_find_resource(data_set->resources, id_rh);
     
     if(rsc_lh == NULL) {
-	crm_config_err("No resource (con=%s, rsc=%s)", id, id_lh);
+	crm_config_err("Invalid constraint '%s': No resource named '%s'", id, id_lh);
 	return FALSE;
 	
     } else if(rsc_rh == NULL) {
-	crm_config_err("No resource (con=%s, rsc=%s)", id, id_rh);
+	crm_config_err("Invalid constraint '%s': No resource named '%s'", id, id_rh);
 	return FALSE;
+
+    } else if(instance_lh && rsc_lh->variant < pe_clone) {
+	crm_config_err("Invalid constraint '%s': Resource '%s' is not a clone but instance %s was requested", id, id_lh, instance_lh);
+	return FALSE;
+
+    } else if(instance_rh && rsc_rh->variant < pe_clone) {
+	crm_config_err("Invalid constraint '%s': Resource '%s' is not a clone but instance %s was requested", id, id_rh, instance_rh);
+	return FALSE;
+    }
+
+    if(instance_lh) {
+	rsc_lh = find_clone_instance(rsc_lh, instance_lh, data_set);
+	if(rsc_lh == NULL) {
+	    crm_config_warn("Invalid constraint '%s': No instance '%s' of '%s'", id, instance_lh, id_lh);
+	    return FALSE;
+	}
+    }
+
+    if(instance_rh) {
+	rsc_rh = find_clone_instance(rsc_rh, instance_rh, data_set);
+	if(rsc_rh == NULL) {
+	    crm_config_warn("Invalid constraint '%s': No instance '%s' of '%s'", id, instance_rh, id_rh);
+	    return FALSE;
+	}
     }
 
     if(crm_is_true(symmetrical)) {
 	crm_config_warn("The %s colocation constraint attribute has been removed."
 			"  It didn't do what you think it did anyway.",
 			XML_CONS_ATTR_SYMMETRICAL);
     }
 
     if(score) {
 	score_i = char2score(score);
     }
     
     rsc_colocation_new(id, attr, score_i, rsc_lh, rsc_rh, state_lh, state_rh, data_set);
     return TRUE;
 }
 
 gboolean
 unpack_rsc_colocation(xmlNode *xml_obj, pe_working_set_t *data_set)
 {
 	int score_i = 0;
 	xmlNode *last = NULL;
 	gboolean any_sets = FALSE;
 
 	const char *id    = crm_element_value(xml_obj, XML_ATTR_ID);
 	const char *score = crm_element_value(xml_obj, XML_RULE_ATTR_SCORE);
 
 	if(score) {
 	    score_i = char2score(score);
 	}
 	
 	xml_child_iter_filter(
 	    xml_obj, set, XML_CONS_TAG_RSC_SET,
 
 	    any_sets = TRUE;
 	    set = expand_idref(set, data_set->input);
 	    if(unpack_colocation_set(set, score_i, data_set) == FALSE) {
 		return FALSE;
 
 	    } else if(last && colocate_rsc_sets(id, last, set, score_i, data_set) == FALSE) {
 		return FALSE;
 	    }
 	    last = set;
 	    );
 
 	
 	if(any_sets == FALSE) {
 	    return unpack_simple_colocation(xml_obj, data_set);
 	}
 
 	return TRUE;
 }
 
 gboolean is_active(rsc_to_node_t *cons)
 {
 	return TRUE;
 }
diff --git a/xml/constraints-1.1.rng b/xml/constraints-1.1.rng
index 84708aac3c..f493cf4951 100644
--- a/xml/constraints-1.1.rng
+++ b/xml/constraints-1.1.rng
@@ -1,180 +1,192 @@
 <?xml version="1.0" encoding="utf-8"?>
 <grammar xmlns="http://relaxng.org/ns/structure/1.0" 
          datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
   <start>
       <ref name="element-constraints"/>
   </start>
 
   <define name="element-constraints">
       <zeroOrMore>
 	<choice>
 	  <ref name="element-location"/>
 	  <ref name="element-colocation"/>
 	  <ref name="element-order"/>
 	</choice>
       </zeroOrMore>
   </define>
 
   <define name="element-location">
     <element name="rsc_location">
       <attribute name="id"><data type="ID"/></attribute>
       <attribute name="rsc"><data type="IDREF"/></attribute>
       <choice>
 	<group>
 	  <externalRef href="score.rng"/>
 	  <attribute name="node"><text/></attribute>
 	</group>
 	<oneOrMore>
 	  <externalRef href="rule.rng"/>
 	</oneOrMore>
       </choice>
       <optional>
 	<ref name="element-lifetime"/>
       </optional>
     </element>
   </define>
 
   <define name="element-resource-set">
     <element name="resource_set">
       <choice>
 	<attribute name="id-ref"><data type="IDREF"/></attribute>
 	<group>
 	  <attribute name="id"><data type="ID"/></attribute>
 	  <optional>
 	    <attribute name="sequential"><data type="boolean"/></attribute>
 	  </optional>
 	  <optional>
 	    <attribute name="action">
 	      <ref name="attribute-actions"/>
 	    </attribute>
 	  </optional>
 	  <optional>
 	    <attribute name="role">
 	      <ref name="attribute-roles"/>
 	    </attribute>
 	  </optional>
 	  <optional>
 	    <externalRef href="score.rng"/>
 	  </optional>
 	  <oneOrMore>
 	    <element name="resource_ref">
 	      <attribute name="id"><data type="IDREF"/></attribute>
 	    </element>
 	  </oneOrMore>
 	</group>
       </choice>
     </element>
   </define>
 
   <define name="element-colocation">
     <element name="rsc_colocation">
       <attribute name="id"><data type="ID"/></attribute>
       <optional>
 	<choice>
 	  <externalRef href="score.rng"/>
 	  <attribute name="score-attribute"><text/></attribute>
 	  <attribute name="score-attribute-mangle"><text/></attribute>
 	</choice>
       </optional>
       <optional>
 	<ref name="element-lifetime"/>
       </optional>
       <choice>
 	<oneOrMore>
 	  <ref name="element-resource-set"/>
 	</oneOrMore>
 	<group>
 	  <attribute name="rsc"><data type="IDREF"/></attribute>
 	  <attribute name="with-rsc"><data type="IDREF"/></attribute>
 	  <optional>
 	    <attribute name="node-attribute"><text/></attribute>
 	  </optional>
 	  <optional>
 	    <attribute name="rsc-role">
 	      <ref name="attribute-roles"/>
 	    </attribute>
 	  </optional>
 	  <optional>
 	    <attribute name="with-rsc-role">
 	      <ref name="attribute-roles"/>
 	    </attribute>
 	  </optional>
+	  <optional>
+	    <attribute name="rsc-instance"><data type="integer"/></attribute>
+	  </optional>
+	  <optional>
+	    <attribute name="with-rsc-instance"><data type="integer"/></attribute>
+	  </optional>
 	</group>
       </choice>
     </element>
   </define>
 
   <define name="element-order">
     <element name="rsc_order">
       <attribute name="id"><data type="ID"/></attribute>
       <optional>
 	<ref name="element-lifetime"/>
       </optional>
       <optional>
 	<attribute name="symmetrical"><data type="boolean"/></attribute>
       </optional>
       <optional>
 	<choice>
 	  <externalRef href="score.rng"/>
 	  <attribute name="kind">
 	    <ref name="order-types"/>
 	  </attribute>
 	</choice>
       </optional>
       <choice>
 	<oneOrMore>
 	  <ref name="element-resource-set"/>
 	</oneOrMore>
 	<group>
 	  <attribute name="first"><data type="IDREF"/></attribute>
 	  <attribute name="then"><data type="IDREF"/></attribute>
 	  <optional>
 	    <attribute name="first-action">
 	      <ref name="attribute-actions"/>
 	    </attribute>
 	  </optional>
 	  <optional>
 	    <attribute name="then-action">
 	      <ref name="attribute-actions"/>
 	    </attribute>
 	  </optional>
+	  <optional>
+	    <attribute name="first-instance"><data type="integer"/></attribute>
+	  </optional>
+	  <optional>
+	    <attribute name="then-instance"><data type="integer"/></attribute>
+	  </optional>
 	</group>
       </choice>
     </element>
   </define>
  
   <define name="attribute-actions">
     <choice>
       <value>start</value>
       <value>promote</value>
       <value>demote</value>
       <value>stop</value>
     </choice>
   </define>
       
   <define name="attribute-roles">
     <choice>
       <value>Stopped</value>
       <value>Started</value>
       <value>Master</value>
       <value>Slave</value>
     </choice>
   </define>
 
   <define name="order-types">
     <choice>
       <value>Optional</value>
       <value>Mandatory</value>
       <value>Serialize</value>
     </choice>
   </define>
 
   <define name="element-lifetime">
     <element name="lifetime">
       <oneOrMore>
 	<externalRef href="rule.rng"/>
       </oneOrMore>
     </element>
   </define>
   
 </grammar>