Hello community, here is the log from the commit of package crmsh for openSUSE:Factory checked in at 2015-10-12 10:02:35 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/crmsh (Old) and /work/SRC/openSUSE:Factory/.crmsh.new (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "crmsh" Changes: -------- --- /work/SRC/openSUSE:Factory/crmsh/crmsh.changes 2015-10-02 09:23:51.000000000 +0200 +++ /work/SRC/openSUSE:Factory/.crmsh.new/crmsh.changes 2015-10-12 10:02:44.000000000 +0200 @@ -1,0 +2,25 @@ +Thu Oct 08 21:44:06 UTC 2015 - kgronl...@suse.com + +- Update to version 2.2.0~rc3+git.1444340345.59850ca: + + high: utils: Fix cluster_copy_file error when nodes provided (bsc#949603) + + low: xmlutil: More informative message when updating resource references after rename + + doc: fix some command syntax grammar in the man page + + doc: resource-discovery for location constraints + +------------------------------------------------------------------- +Tue Oct 06 12:22:30 UTC 2015 - kgronl...@suse.com + +- Update to version 2.2.0~rc3+git.1444133917.3f7f79f: + + high: cibconfig: Fix bug in is_edit_valid (bsc#948547) + + high: cibconfig: Delete constraints before resources + +------------------------------------------------------------------- +Tue Oct 06 09:14:24 UTC 2015 - kgronl...@suse.com + +- Update to version 2.2.0~rc3+git.1444122392.193bf69: + + high: cibconfig: Allow nodes and resources with the same ID (bsc#948547) + + high: cibconfig: Allow node/rsc id collision in _set_update (bsc#948547) + + medium: hb_report: Don't cat binary logs + + low: report: Silence tar warning on early stream close + +------------------------------------------------------------------- Old: ---- crmsh-2.2.0~rc3+git.1443544100.aa2abda.tar.bz2 New: ---- crmsh-2.2.0~rc3+git.1444340345.59850ca.tar.bz2 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ crmsh.spec ++++++ --- /var/tmp/diff_new_pack.lYKxtr/_old 2015-10-12 10:02:44.000000000 +0200 +++ /var/tmp/diff_new_pack.lYKxtr/_new 2015-10-12 10:02:44.000000000 +0200 @@ -36,7 +36,7 @@ Summary: High Availability cluster command-line interface License: GPL-2.0+ Group: %{pkg_group} -Version: 2.2.0~rc3+git.1443544100.aa2abda +Version: 2.2.0~rc3+git.1444340345.59850ca Release: 0 Url: http://crmsh.github.io Source0: %{name}-%{version}.tar.bz2 ++++++ _servicedata ++++++ --- /var/tmp/diff_new_pack.lYKxtr/_old 2015-10-12 10:02:44.000000000 +0200 +++ /var/tmp/diff_new_pack.lYKxtr/_new 2015-10-12 10:02:44.000000000 +0200 @@ -1,4 +1,4 @@ <servicedata> <service name="tar_scm"> <param name="url">git://github.com/ClusterLabs/crmsh.git</param> - <param name="changesrevision">aa2abdad0c401d1cebf43e1a20da26b3de2992c5</param></service></servicedata> \ No newline at end of file + <param name="changesrevision">59850ca9ed07b3e965170b1fb50712ea1bfa502f</param></service></servicedata> \ No newline at end of file ++++++ crmsh-2.2.0~rc3+git.1443544100.aa2abda.tar.bz2 -> crmsh-2.2.0~rc3+git.1444340345.59850ca.tar.bz2 ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crmsh-2.2.0~rc3+git.1443544100.aa2abda/doc/crm.8.adoc new/crmsh-2.2.0~rc3+git.1444340345.59850ca/doc/crm.8.adoc --- old/crmsh-2.2.0~rc3+git.1443544100.aa2abda/doc/crm.8.adoc 2015-09-30 17:21:36.000000000 +0200 +++ new/crmsh-2.2.0~rc3+git.1444340345.59850ca/doc/crm.8.adoc 2015-10-08 23:44:05.000000000 +0200 @@ -797,12 +797,12 @@ id_spec :: $id=<id> | $id-ref=<id> score :: <number> | <attribute> | [-]inf -expression :: <simple_exp> [bool_op <simple_exp> ...] +expression :: <simple_exp> [<bool_op> <simple_exp> ...] bool_op :: or | and simple_exp :: <attribute> [type:]<binary_op> <value> | <unary_op> <attribute> | date <date_expr> -type :: string | version | number +type :: <string> | <version> | <number> binary_op :: lt | gt | lte | gte | eq | ne unary_op :: defined | not_defined @@ -2574,8 +2574,8 @@ ............... clone <name> <rsc> [description=<description>] - [meta attr_list] - [params attr_list] + [meta <attr_list>] + [params <attr_list>] attr_list :: [$id=<id>] <attr>=<val> [<attr>=<val>...] | $id-ref=<id> ............... @@ -2622,13 +2622,13 @@ colocation <id> <score>: <rsc>[:<role>] <with-rsc>[:<role>] [node-attribute=<node_attr>] -colocation <id> <score>: resource_sets +colocation <id> <score>: <resource_sets> [node-attribute=<node_attr>] -resource_sets :: resource_set [resource_set ...] +resource_sets :: <resource_set> [<resource_set> ...] resource_set :: ["("|"["] <rsc>[:<role>] [<rsc>[:<role>] ...] \ - [attributes] [")"|"]"] + [<attributes>] [")"|"]"] attributes :: [require-all=(true|false)] [sequential=(true|false)] @@ -2924,6 +2924,9 @@ * Tag containing resource ids: +location loc1 tag1 100: node1+ * Resource pattern: +location loc1 /web.*/ 100: node1+ +The +resource-discovery+ attribute allows probes to be selectively +enabled or disabled per resource and node. + The syntax for resource sets is described in detail for <<cmdhelp_configure_colocation,`colocation`>>. @@ -2935,12 +2938,14 @@ Usage: ............... -location <id> rsc [role=<role>] {node_pref|rules} +location <id> <rsc> [<attributes>] {<node_pref>|<rules>} rsc :: /<rsc-pattern>/ | { resource_sets } | <rsc> +attributes :: role=<role> | resource-discovery=always|never|exclusive + node_pref :: <score>: <node> rules :: @@ -2949,7 +2954,7 @@ id_spec :: $id=<id> | $id-ref=<id> score :: <number> | <attribute> | [-]inf -expression :: <simple_exp> [bool_op <simple_exp> ...] +expression :: <simple_exp> [<bool_op> <simple_exp> ...] bool_op :: or | and simple_exp :: <attribute> [type:]<binary_op> <value> | <unary_op> <attribute> @@ -2984,6 +2989,9 @@ location conn_2 dummy_float \ rule -inf: not_defined pingd or pingd number:lte 0 + +# never probe for rsc1 on node1 +location no-probe rsc1 resource-discovery=never -inf: node1 ............... [[cmdhelp_configure_modgroup,modify group]] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crmsh-2.2.0~rc3+git.1443544100.aa2abda/hb_report/hb_report.in new/crmsh-2.2.0~rc3+git.1444340345.59850ca/hb_report/hb_report.in --- old/crmsh-2.2.0~rc3+git.1443544100.aa2abda/hb_report/hb_report.in 2015-09-30 17:21:36.000000000 +0200 +++ new/crmsh-2.2.0~rc3+git.1444340345.59850ca/hb_report/hb_report.in 2015-10-08 23:44:05.000000000 +0200 @@ -330,8 +330,10 @@ echo "gzip -dc" elif echo $1 | grep -qs 'xz$'; then echo "xz -dc" - else + elif file $1 | grep -qs 'text'; then echo "cat" + else + echo "echo" fi } # diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crmsh-2.2.0~rc3+git.1443544100.aa2abda/modules/cibconfig.py new/crmsh-2.2.0~rc3+git.1444340345.59850ca/modules/cibconfig.py --- old/crmsh-2.2.0~rc3+git.1443544100.aa2abda/modules/cibconfig.py 2015-09-30 17:21:36.000000000 +0200 +++ new/crmsh-2.2.0~rc3+git.1444340345.59850ca/modules/cibconfig.py 2015-10-08 23:44:05.000000000 +0200 @@ -494,28 +494,6 @@ rc |= obj.check_sanity() return rc - def is_edit_valid(self, id_set): - ''' - 1. Cannot name any elements as those which exist but - were not picked for editing. - 2. Cannot remove running resources. - ''' - rc = True - not_allowed = id_set & self.locked_ids - rscstat = RscState() - if not_allowed: - common_err("Elements %s already exist" % - ', '.join(list(not_allowed))) - rc = False - delete_set = self.obj_ids - id_set - cannot_delete = [x for x in delete_set - if not rscstat.can_delete(x)] - if cannot_delete: - common_err("Cannot delete running resources: %s" % - ', '.join(cannot_delete)) - rc = False - return rc - class CibObjectSetCli(CibObjectSet): ''' @@ -570,9 +548,7 @@ coming from edit). The original CIB is preserved and no changes are made. ''' - edit_d = {} - id_set = oset() - del_set = oset() + diff = CibDiff(self) rc = True err_buf.start_tmp_lineno() cp = CliParser() @@ -580,32 +556,17 @@ err_buf.incr_lineno() node = cp.parse(cli_text) if node not in (False, None): - obj_id = id_for_node(node) - if obj_id is None: - common_err("element %s has no id!" % - etree.tostring(node, pretty_print=True)) - rc = False - elif obj_id in id_set: - common_err("duplicate element %s" % obj_id) - rc = False - else: - id_set.add(obj_id) - edit_d[obj_id] = node + rc = diff.add(node) elif node is False: rc = False err_buf.stop_tmp_lineno() + # we can't proceed if there was a syntax error, but we # can ask the user to fix problems - if not no_remove: - rc &= self.is_edit_valid(id_set) - del_set = self.obj_ids - id_set if not rc: return rc - mk_set = id_set - self.obj_ids - upd_set = id_set & self.obj_ids - rc = cib_factory.set_update(edit_d, mk_set, upd_set, del_set, - upd_type="cli", method=method) + rc = diff.apply(cib_factory, mode='cli', no_remove=no_remove, method=method) if not rc: self._initialize() return rc @@ -639,29 +600,12 @@ if not show_unrecognized_elems(cib_elem): return False rc = True - id_set = oset() - del_set = oset() - edit_d = {} + diff = CibDiff(self) for node in get_top_cib_nodes(cib_elem, []): - id = self._get_id(node) - if id is None: - common_err("element %s has no id!" % - etree.tostring(node, pretty_print=True)) - rc = False - elif id in id_set: - common_err("duplicate element %s" % id) - rc = False - else: - id_set.add(id) - edit_d[id] = node - if not no_remove: - rc &= self.is_edit_valid(id_set) - del_set = self.obj_ids - id_set + rc = diff.add(node) if not rc: return rc - mk_set = id_set - self.obj_ids - upd_set = id_set & self.obj_ids - rc = cib_factory.set_update(edit_d, mk_set, upd_set, del_set, "xml", method) + rc = diff.apply(cib_factory, mode='xml', no_remove=no_remove, method=method) if not rc: self._initialize() return rc @@ -1145,7 +1089,7 @@ Check if all operation attributes are supported by the schema. ''' - rc = True + rc = 0 op_id = op_node.get("name") for name in op_node.keys(): vals = schema.rng_attr_values(op_node.tag, name) @@ -1155,14 +1099,14 @@ if v not in vals: common_warn("%s: op '%s' attribute '%s' value '%s' not recognized" % (self.obj_id, op_id, name, v)) - rc = False + rc = 1 return rc def _check_ops_attributes(self): ''' Check if operation attributes settings are valid. ''' - rc = True + rc = 0 if self.node is None: return rc for op_node in self.node.xpath("operations/op"): @@ -2139,6 +2083,112 @@ cib_upgrade = "cibadmin --upgrade --force" +class CibDiff(object): + ''' + Represents a cib edit order. + Is complicated by the fact that + nodes and resources can have + colliding ids. + + Can carry changes either as CLI objects + or as XML statements. + ''' + def __init__(self, objset): + self.objset = objset + self._node_set = oset() + self._nodes = {} + self._rsc_set = oset() + self._resources = {} + + def add(self, item): + obj_id = id_for_node(item) + is_node = item.tag == 'node' + if obj_id is None: + common_err("element %s has no id!" % + etree.tostring(item, pretty_print=True)) + return False + elif is_node and obj_id in self._node_set: + common_err("Duplicate node: %s" % (obj_id)) + return False + elif not is_node and obj_id in self._rsc_set: + common_err("Duplicate resource: %s" % (obj_id)) + return False + elif is_node: + self._node_set.add(obj_id) + self._nodes[obj_id] = item + else: + self._rsc_set.add(obj_id) + self._resources[obj_id] = item + return True + + def _obj_type(self, nid): + for obj in self.objset.all_set: + if obj.obj_id == nid: + return obj.obj_type + return None + + def _obj_nodes(self): + return oset([n for n in self.objset.obj_ids + if self._obj_type(n) == 'node']) + + def _obj_resources(self): + return oset([n for n in self.objset.obj_ids + if self._obj_type(n) != 'node']) + + def _is_edit_valid(self, id_set, existing): + ''' + 1. Cannot name any elements as those which exist but + were not picked for editing. + 2. Cannot remove running resources. + ''' + rc = True + not_allowed = id_set & self.objset.locked_ids + rscstat = RscState() + if not_allowed: + common_err("Elements %s already exist" % + ', '.join(list(not_allowed))) + rc = False + delete_set = existing - id_set + cannot_delete = [x for x in delete_set + if not rscstat.can_delete(x)] + if cannot_delete: + common_err("Cannot delete running resources: %s" % + ', '.join(cannot_delete)) + rc = False + return rc + + + def apply(self, factory, mode='cli', no_remove=False, method='replace'): + rc = True + + edited_nodes = self._nodes.copy() + edited_resources = self._resources.copy() + + def calc_sets(input_set, existing): + rc = True + if not no_remove: + rc = self._is_edit_valid(input_set, existing) + del_set = existing - (input_set) + else: + del_set = oset() + mk_set = (input_set) - existing + upd_set = (input_set) & existing + return rc, mk_set, upd_set, del_set + + if not rc: + return rc + + for e, s, existing in ((edited_nodes, self._node_set, self._obj_nodes()), + (edited_resources, self._rsc_set, self._obj_resources())): + rc, mk, upd, rm = calc_sets(s, existing) + if not rc: + return rc + rc = cib_factory.set_update(e, mk, upd, rm, upd_type=mode, method=method) + if not rc: + return rc + return rc + + class CibFactory(object): ''' Juggle with CIB objects. @@ -2602,7 +2652,7 @@ # need to get addresses of all new objects created by # deepcopy for obj in self.cib_objects: - obj.node = self.find_node(obj.xml_obj_type, obj.obj_id) + obj.node = self.find_xml_node(obj.xml_obj_type, obj.obj_id) self._update_links(obj) idmgmt.pop_state() return self.check_structure() @@ -2649,9 +2699,34 @@ if objs is None: return None if len(objs) > 0: + for obj in objs: + if obj.obj_type != 'node': + return obj return objs[0] return None + def find_resource(self, obj_id): + if not self.is_cib_sane(): + return None + objs = self.find_objects(obj_id) + if objs is None: + return None + for obj in objs: + if obj.obj_type != 'node': + return obj + return None + + def find_node(self, obj_id): + if not self.is_cib_sane(): + return None + objs = self.find_objects(obj_id) + if objs is None: + return None + for obj in objs: + if obj.obj_type == 'node': + return obj + return None + # # tab completion functions # @@ -2687,8 +2762,8 @@ def node_id_list(self): "List of node ids." - return [x.node.get("uname") for x in self.cib_objects - if x.obj_type == "node"] + return sorted([x.node.get("uname") for x in self.cib_objects + if x.obj_type == "node"]) def f_prim_free_id_list(self): "List of possible primitives ids (for group completion)." @@ -2723,8 +2798,8 @@ return obj return None - def find_node(self, tag, id, strict=True): - "Find a node of this type with this id." + def find_xml_node(self, tag, id, strict=True): + "Find a xml node of this type with this id." try: if tag in constants.defaults_tags: expr = '//%s/meta_attributes[@id="%s"]' % (tag, id) @@ -2754,7 +2829,7 @@ return False rc = True for obj_id in args: - obj = self.find_object(obj_id) + obj = self.find_resource(obj_id) if not obj: no_object_err(obj_id) rc = False @@ -2834,7 +2909,7 @@ id to reference. ''' self.id_refs[id_ref] = attr_list_type - obj = self.find_object(id_ref) + obj = self.find_resource(id_ref) if obj: nodes = obj.node.xpath(".//%s" % attr_list_type) if len(nodes) > 1: @@ -2904,7 +2979,7 @@ matching_tags = [x for x in self.cib_objects if x.obj_type == 'tag' and x.obj_id == t] ret = [] for mt in matching_tags: - matches = [cib_factory.find_object(o) for o in mt.node.xpath('./obj_ref/@id')] + matches = [cib_factory.find_resource(o) for o in mt.node.xpath('./obj_ref/@id')] ret += [m for m in matches if m is not None] return ret @@ -2955,7 +3030,7 @@ rc = True constraint_id = node.get("id") for obj_id in referenced_resources(node): - if not self.find_object(obj_id): + if not self.find_resource(obj_id): constraint_norefobj_err(constraint_id, obj_id) rc = False return rc @@ -2993,7 +3068,7 @@ def _verify_child(self, child_id, parent_tag, obj_id): 'Check if child exists and obj_id is (or may become) its parent.' - child = self.find_object(child_id) + child = self.find_resource(child_id) if not child: no_object_err(child_id) return False @@ -3062,7 +3137,7 @@ '''Add an op to a primitive.''' # does the referenced primitive exist rsc_id = node.get('rsc') - rsc_obj = self.find_object(rsc_id) + rsc_obj = self.find_resource(rsc_id) if not rsc_obj: no_object_err(rsc_id) return None @@ -3095,7 +3170,7 @@ if obj_type == "op": return self.add_op(elem) if obj_type == "node": - obj = self.find_object(obj_id) + obj = self.find_node(obj_id) # make an exception and allow updating nodes if obj: self.merge_from_cli(obj, elem) @@ -3166,17 +3241,32 @@ del_set is a set to be removed. method is either replace or update. ''' - common_debug("_cli_set_update: %s, %s, %s" % (mk_set, upd_set, del_set)) + common_debug("_cli_set_update: mk=%s, upd=%s, del=%s" % (mk_set, upd_set, del_set)) test_l = [] def obj_is_container(x): - obj = self.find_object(x) + obj = self.find_resource(x) return obj and is_container(obj.node) - del_containers = [x for x in del_set if obj_is_container(x)] - del_objs = [x for x in del_set if not obj_is_container(x)] + def obj_is_constraint(x): + obj = self.find_resource(x) + return obj and is_constraint(obj.node) + + del_constraints = [] + del_containers = [] + del_objs = [] + for x in del_set: + if obj_is_constraint(x): + del_constraints.append(x) + elif obj_is_container(x): + del_containers.append(x) + else: + del_objs.append(x) - # delete containers first in case objects are moved elsewhere + # delete constraints and containers first in case objects are moved elsewhere + if not self.delete(*del_constraints): + common_debug("delete %s failed" % (list(del_set))) + return False if not self.delete(*del_containers): common_debug("delete %s failed" % (list(del_set))) return False @@ -3190,7 +3280,10 @@ test_l.append(obj) for id in upd_set: - obj = self.find_object(id) + if edit_d[id].tag == 'node': + obj = self.find_node(id) + else: + obj = self.find_resource(id) if not obj: common_debug("%s not found!" % (id)) return False @@ -3203,7 +3296,8 @@ (obj, etree.tostring(node), method)) return False test_l.append(obj) - if not self.delete(*del_objs): + + if not self.delete(*reversed(del_objs)): common_debug("delete %s failed" % (list(del_set))) return False rc = True @@ -3229,7 +3323,10 @@ return False test_l.append(obj) for id in upd_set: - obj = self.find_object(id) + if edit_d[id].tag == 'node': + obj = self.find_node(id) + else: + obj = self.find_resource(id) if not obj: return False if not self.update_from_node(obj, edit_d[id]): @@ -3272,7 +3369,7 @@ if not new_children_ids: return True old_children = [x for x in obj.children if x.parent == obj] - new_children = [self.find_object(x) for x in new_children_ids] + new_children = [self.find_resource(x) for x in new_children_ids] new_children = [c for c in new_children if c is not None] obj.children = new_children # relink orphans to top @@ -3380,7 +3477,7 @@ for c in node.iterchildren('primitive'): pid = c.get('id') - child_obj = self.find_object(pid) + child_obj = self.find_resource(pid) if child_obj is None: child_obj = self.create_from_node(copy.deepcopy(c)) if not child_obj: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crmsh-2.2.0~rc3+git.1443544100.aa2abda/modules/parse.py new/crmsh-2.2.0~rc3+git.1444340345.59850ca/modules/parse.py --- old/crmsh-2.2.0~rc3+git.1443544100.aa2abda/modules/parse.py 2015-09-30 17:21:36.000000000 +0200 +++ new/crmsh-2.2.0~rc3+git.1444340345.59850ca/modules/parse.py 2015-10-08 23:44:05.000000000 +0200 @@ -742,8 +742,8 @@ def parse_location(self): """ - location <id> rsc [[$]<attribute>=<value>] <score>: <node> - location <id> rsc [[$]<attribute>=<value>] <rule> [<rule> ...] + location <id> <rsc> [[$]<attribute>=<value>] <score>: <node> + location <id> <rsc> [[$]<attribute>=<value>] <rule> [<rule> ...] rsc :: /<rsc-pattern>/ | { <rsc-set> } | <rsc> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crmsh-2.2.0~rc3+git.1443544100.aa2abda/modules/report.py new/crmsh-2.2.0~rc3+git.1444340345.59850ca/modules/report.py --- old/crmsh-2.2.0~rc3+git.1443544100.aa2abda/modules/report.py 2015-09-30 17:21:36.000000000 +0200 +++ new/crmsh-2.2.0~rc3+git.1444340345.59850ca/modules/report.py 2015-10-08 23:44:05.000000000 +0200 @@ -751,7 +751,7 @@ self.error(msg) return None try: - rc, tf_loc = get_stdout("tar -t%s < %s | head -1" % (tar_unpack_option, quote(bfname))) + rc, tf_loc = get_stdout("tar -t%s < %s 2> /dev/null | head -1" % (tar_unpack_option, quote(bfname))) if os.path.abspath(tf_loc) != os.path.abspath(loc): common_debug("top directory in tarball: %s, doesn't match the tarball name: %s" % (tf_loc, loc)) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crmsh-2.2.0~rc3+git.1443544100.aa2abda/modules/ui_assist.py new/crmsh-2.2.0~rc3+git.1444340345.59850ca/modules/ui_assist.py --- old/crmsh-2.2.0~rc3+git.1443544100.aa2abda/modules/ui_assist.py 2015-09-30 17:21:36.000000000 +0200 +++ new/crmsh-2.2.0~rc3+git.1444340345.59850ca/modules/ui_assist.py 2015-10-08 23:44:05.000000000 +0200 @@ -39,7 +39,7 @@ ''' if len(primitives) < 1: context.fatal_error("Expected at least one primitive argument") - objs = [cib_factory.find_object(p) for p in primitives] + objs = [cib_factory.find_resource(p) for p in primitives] for prim, obj in zip(primitives, objs): if obj is None: context.fatal_error("Primitive %s not found" % (prim)) @@ -100,7 +100,7 @@ context.fatal_error("Need at least two arguments") for node in nodes: - obj = cib_factory.find_object(node) + obj = cib_factory.find_resource(node) if not obj: context.fatal_error("Object not found: %s" % (node)) if not xmlutil.is_primitive(obj.node): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crmsh-2.2.0~rc3+git.1443544100.aa2abda/modules/ui_configure.py new/crmsh-2.2.0~rc3+git.1444340345.59850ca/modules/ui_configure.py --- old/crmsh-2.2.0~rc3+git.1443544100.aa2abda/modules/ui_configure.py 2015-09-30 17:21:36.000000000 +0200 +++ new/crmsh-2.2.0~rc3+git.1444340345.59850ca/modules/ui_configure.py 2015-10-08 23:44:05.000000000 +0200 @@ -105,7 +105,7 @@ def ra_agent_for_template(tmpl): '''@template -> ra.agent''' - obj = cib_factory.find_object(tmpl[1:]) + obj = cib_factory.find_resource(tmpl[1:]) if obj is None: return None return ra.get_ra(obj.node) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crmsh-2.2.0~rc3+git.1443544100.aa2abda/modules/utils.py new/crmsh-2.2.0~rc3+git.1444340345.59850ca/modules/utils.py --- old/crmsh-2.2.0~rc3+git.1443544100.aa2abda/modules/utils.py 2015-09-30 17:21:36.000000000 +0200 +++ new/crmsh-2.2.0~rc3+git.1444340345.59850ca/modules/utils.py 2015-10-08 23:44:05.000000000 +0200 @@ -1438,7 +1438,7 @@ if not nodes: nodes = list_cluster_nodes() nodes.remove(this_node()) - opts = parallax.Options() + opts = parallax.Options() opts.timeout = 60 opts.ssh_options += ['ControlPersist=no'] ok = True diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crmsh-2.2.0~rc3+git.1443544100.aa2abda/modules/xmlutil.py new/crmsh-2.2.0~rc3+git.1444340345.59850ca/modules/xmlutil.py --- old/crmsh-2.2.0~rc3+git.1443544100.aa2abda/modules/xmlutil.py 2015-09-30 17:21:36.000000000 +0200 +++ new/crmsh-2.2.0~rc3+git.1444340345.59850ca/modules/xmlutil.py 2015-10-08 23:44:05.000000000 +0200 @@ -1021,7 +1021,7 @@ def rename_rscref(c_obj, old_id, new_id): if rename_rscref_simple(c_obj, old_id, new_id) or \ rename_rscref_rset(c_obj, old_id, new_id): - err_buf.info("resource references in %s updated" % str(c_obj)) + err_buf.info("modified %s from %s to %s" % (str(c_obj), old_id, new_id)) def delete_rscref(c_obj, rsc_id): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crmsh-2.2.0~rc3+git.1443544100.aa2abda/test/testcases/edit new/crmsh-2.2.0~rc3+git.1444340345.59850ca/test/testcases/edit --- old/crmsh-2.2.0~rc3+git.1443544100.aa2abda/test/testcases/edit 2015-09-30 17:21:36.000000000 +0200 +++ new/crmsh-2.2.0~rc3+git.1444340345.59850ca/test/testcases/edit 2015-10-08 23:44:05.000000000 +0200 @@ -12,7 +12,9 @@ op monitor interval=120m OCF_CHECK_LEVEL=10 filter "sed '$aprimitive p2 ocf:heartbeat:Dummy'" filter "sed '$agroup g1 p1 p2'" +show filter "sed 's/p2/p3/;$aprimitive p3 ocf:heartbeat:Dummy'" g1 +show filter "sed '$aclone c1 p2'" filter "sed 's/p2/g1/'" c1 filter "sed '/clone/s/g1/p2/'" c1 g1 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crmsh-2.2.0~rc3+git.1443544100.aa2abda/test/testcases/edit.exp new/crmsh-2.2.0~rc3+git.1444340345.59850ca/test/testcases/edit.exp --- old/crmsh-2.2.0~rc3+git.1443544100.aa2abda/test/testcases/edit.exp 2015-09-30 17:21:36.000000000 +0200 +++ new/crmsh-2.2.0~rc3+git.1444340345.59850ca/test/testcases/edit.exp 2015-10-08 23:44:05.000000000 +0200 @@ -9,7 +9,38 @@ .INP: primitive p1 ocf:heartbeat:Dummy op monitor interval=60m op monitor interval=120m OCF_CHECK_LEVEL=10 .INP: filter "sed '$aprimitive p2 ocf:heartbeat:Dummy'" .INP: filter "sed '$agroup g1 p1 p2'" +.INP: show +node node1 \ + attributes mem=16G +primitive p1 Dummy \ + op monitor interval=60m \ + op monitor interval=120m OCF_CHECK_LEVEL=10 +primitive p2 Dummy +primitive st stonith:null \ + params hostlist=node1 \ + meta description="some description here" \ + op start requires=nothing interval=0 \ + op monitor interval=60m +group g1 p1 p2 +property cib-bootstrap-options: \ + default-action-timeout=2m .INP: filter "sed 's/p2/p3/;$aprimitive p3 ocf:heartbeat:Dummy'" g1 +.INP: show +node node1 \ + attributes mem=16G +primitive p1 Dummy \ + op monitor interval=60m \ + op monitor interval=120m OCF_CHECK_LEVEL=10 +primitive p2 Dummy +primitive p3 Dummy +primitive st stonith:null \ + params hostlist=node1 \ + meta description="some description here" \ + op start requires=nothing interval=0 \ + op monitor interval=60m +group g1 p1 p3 +property cib-bootstrap-options: \ + default-action-timeout=2m .INP: filter "sed '$aclone c1 p2'" .INP: filter "sed 's/p2/g1/'" c1 .INP: filter "sed '/clone/s/g1/p2/'" c1 g1 @@ -26,7 +57,7 @@ .INP: primitive d3 ocf:heartbeat:Dummy .INP: group g2 d1 d2 .INP: filter "sed '/g2/s/d1/p1/;/g1/s/p1/d1/'" -ERROR: 27: Cannot create group:g1: Child primitive:d1 already in group:g2 +ERROR: 29: Cannot create group:g1: Child primitive:d1 already in group:g2 .INP: filter "sed '/g1/s/d1/p1/;/g2/s/p1/d1/'" .INP: filter "sed '$alocation loc-d1 d1 rule $id=r1 -inf: not_defined webserver rule $id=r2 webserver: defined webserver'" .INP: filter "sed 's/not_defined webserver/& or mem number:lte 0/'" loc-d1 @@ -41,15 +72,15 @@ .INP: modgroup g1 add p1 ERROR: 1: syntax in group: child p1 listed more than once in group g1 parsing 'group g1 p1 p2 d3 p1' .INP: modgroup g1 remove st -ERROR: 40: configure.modgroup: st is not member of g1 +ERROR: 42: configure.modgroup: st is not member of g1 .INP: modgroup g1 remove c1 -ERROR: 41: configure.modgroup: c1 is not member of g1 +ERROR: 43: configure.modgroup: c1 is not member of g1 .INP: modgroup g1 remove nosuch -ERROR: 42: configure.modgroup: nosuch is not member of g1 +ERROR: 44: configure.modgroup: nosuch is not member of g1 .INP: modgroup g1 add c1 -ERROR: 43: a group may contain only primitives; c1 is clone +ERROR: 45: a group may contain only primitives; c1 is clone .INP: modgroup g1 add nosuch -ERROR: 44: g1 refers to missing object nosuch +ERROR: 46: g1 refers to missing object nosuch .INP: filter "sed 's/^/# this is a comment\n/'" loc-d1 .INP: rsc_defaults $id="rsc_options" failure-timeout=10m .INP: filter "sed 's/2m/60s/'" cib-bootstrap-options diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crmsh-2.2.0~rc3+git.1443544100.aa2abda/test/testcases/history.exp new/crmsh-2.2.0~rc3+git.1444340345.59850ca/test/testcases/history.exp --- old/crmsh-2.2.0~rc3+git.1443544100.aa2abda/test/testcases/history.exp 2015-09-30 17:21:36.000000000 +0200 +++ new/crmsh-2.2.0~rc3+git.1444340345.59850ca/test/testcases/history.exp 2015-10-08 23:44:05.000000000 +0200 @@ -2,6 +2,7 @@ .INP: history .INP: source history-test.tar.bz2 .INP: info +.EXT tar -tj < history-test.tar.bz2 2> /dev/null | head -1 .EXT tar -xj < history-test.tar.bz2 Source: history-test.tar.bz2 Created on: Fri 14 Dec 19:08:38 UTC 2012 @@ -278,6 +279,7 @@ .TRY History 2 .INP: history .INP: session load _crmsh_regtest +.EXT tar -tj < history-test.tar.bz2 2> /dev/null | head -1 .EXT tar -xj < history-test.tar.bz2 .INP: exclude corosync|crmd|pengine|stonith-ng|cib|attrd|mgmtd|sshd diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crmsh-2.2.0~rc3+git.1443544100.aa2abda/test/testcases/newfeatures new/crmsh-2.2.0~rc3+git.1444340345.59850ca/test/testcases/newfeatures --- old/crmsh-2.2.0~rc3+git.1443544100.aa2abda/test/testcases/newfeatures 2015-09-30 17:21:36.000000000 +0200 +++ new/crmsh-2.2.0~rc3+git.1444340345.59850ca/test/testcases/newfeatures 2015-10-08 23:44:05.000000000 +0200 @@ -19,6 +19,7 @@ tag tag1: p0 p1 p2 tag tag2 p0 p1 p2 location l1 { p0 p1 p2 } inf: node1 +primitive node1 Dummy show _test verify diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crmsh-2.2.0~rc3+git.1443544100.aa2abda/test/testcases/newfeatures.exp new/crmsh-2.2.0~rc3+git.1444340345.59850ca/test/testcases/newfeatures.exp --- old/crmsh-2.2.0~rc3+git.1443544100.aa2abda/test/testcases/newfeatures.exp 2015-09-30 17:21:36.000000000 +0200 +++ new/crmsh-2.2.0~rc3+git.1444340345.59850ca/test/testcases/newfeatures.exp 2015-10-08 23:44:05.000000000 +0200 @@ -13,8 +13,10 @@ .INP: tag tag1: p0 p1 p2 .INP: tag tag2 p0 p1 p2 .INP: location l1 { p0 p1 p2 } inf: node1 +.INP: primitive node1 Dummy .INP: show node node1 +primitive node1 Dummy primitive p0 Dummy \ params state=1 primitive p1 Dummy \ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crmsh-2.2.0~rc3+git.1443544100.aa2abda/test/testcases/resource.exp new/crmsh-2.2.0~rc3+git.1444340345.59850ca/test/testcases/resource.exp --- old/crmsh-2.2.0~rc3+git.1443544100.aa2abda/test/testcases/resource.exp 2015-09-30 17:21:36.000000000 +0200 +++ new/crmsh-2.2.0~rc3+git.1444340345.59850ca/test/testcases/resource.exp 2015-10-08 23:44:05.000000000 +0200 @@ -87,7 +87,7 @@ .SETENV showobj=cli-prefer-p3 .TRY resource migrate p3 node1 -.EXT crm_resource --move -r 'p3' --node='node1' +.EXT crm_resource --quiet --move -r 'p3' --node='node1' .INP: configure .INP: _regtest on .INP: show xml cli-prefer-p3 @@ -105,10 +105,10 @@ .SETENV showobj= .TRY resource unmigrate p3 -.EXT crm_resource --clear -r 'p3' +.EXT crm_resource --quiet --clear -r 'p3' .SETENV showobj=cli-prefer-p3 .TRY resource migrate p3 node1 force -.EXT crm_resource --move -r 'p3' --node='node1' --force +.EXT crm_resource --quiet --move -r 'p3' --node='node1' --force .INP: configure .INP: _regtest on .INP: show xml cli-prefer-p3 @@ -126,7 +126,7 @@ .SETENV showobj= .TRY resource unmigrate p3 -.EXT crm_resource --clear -r 'p3' +.EXT crm_resource --quiet --clear -r 'p3' .SETENV showobj=p0 .TRY resource param p0 set a0 "1 2 3" .EXT crm_resource -r 'p0' -p 'a0' -v '1 2 3' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crmsh-2.2.0~rc3+git.1443544100.aa2abda/test/unittests/__init__.py new/crmsh-2.2.0~rc3+git.1444340345.59850ca/test/unittests/__init__.py --- old/crmsh-2.2.0~rc3+git.1443544100.aa2abda/test/unittests/__init__.py 2015-09-30 17:21:36.000000000 +0200 +++ new/crmsh-2.2.0~rc3+git.1444340345.59850ca/test/unittests/__init__.py 2015-10-08 23:44:05.000000000 +0200 @@ -17,6 +17,8 @@ config.path.sharedir = os.path.join(_here, "../../doc") config.path.crm_dtd_dir = os.path.join(_here, "schemas") +os.environ["CIB_file"] = "test" + # install a basic CIB from crmsh import cibconfig @@ -35,9 +37,9 @@ </cluster_property_set> </crm_config> <nodes> - <node id="1" uname="ha-one"/> - <node id="2" uname="ha-two"/> - <node id="3" uname="ha-three"/> + <node id="ha-one" uname="ha-one"/> + <node id="ha-two" uname="ha-two"/> + <node id="ha-three" uname="ha-three"/> </nodes> <resources> </resources> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crmsh-2.2.0~rc3+git.1443544100.aa2abda/test/unittests/test_bugs.py new/crmsh-2.2.0~rc3+git.1444340345.59850ca/test/unittests/test_bugs.py --- old/crmsh-2.2.0~rc3+git.1443544100.aa2abda/test/unittests/test_bugs.py 2015-09-30 17:21:36.000000000 +0200 +++ new/crmsh-2.2.0~rc3+git.1444340345.59850ca/test/unittests/test_bugs.py 2015-10-08 23:44:05.000000000 +0200 @@ -531,3 +531,219 @@ factory.create_object('colocation', 'coloc-p1-p2-p3', 'inf:', 'p1', 'p2', 'p3') c = factory.find_object('coloc-p1-p2-p3') assert c and c.check_sanity() > 0 + + +@with_setup(setup_func, teardown_func) +def test_existing_node_resource(): + factory.create_object('primitive', 'ha-one', 'Dummy') + + n = factory.find_node('ha-one') + assert factory.test_element(n) + + r = factory.find_resource('ha-one') + assert factory.test_element(r) + + assert n != r + + assert factory.check_structure() + factory.cli_use_validate_all() + + ok, s = factory.mkobj_set('ha-one') + assert ok + + +@with_setup(setup_func, teardown_func) +def test_existing_node_resource_2(): + obj = cibconfig.mkset_obj() + assert obj is not None + + from crmsh import clidisplay + with clidisplay.nopretty(): + text = obj.repr() + text += "\nprimitive ha-one Dummy" + ok = obj.save(text) + assert ok + + obj = cibconfig.mkset_obj() + assert obj is not None + with clidisplay.nopretty(): + text2 = obj.repr() + + assert sorted(text.split('\n')) == sorted(text2.split('\n')) + + +@with_setup(setup_func, teardown_func) +def test_id_collision_breakage_1(): + from crmsh import clidisplay + + obj = cibconfig.mkset_obj() + assert obj is not None + with clidisplay.nopretty(): + original_cib = obj.repr() + print original_cib + + obj = cibconfig.mkset_obj() + assert obj is not None + + ok = obj.save("""node node1 +primitive p0 ocf:pacemaker:Dummy +primitive p1 ocf:pacemaker:Dummy +primitive p2 ocf:heartbeat:Delay \ + params startdelay=2 mondelay=2 stopdelay=2 +primitive p3 ocf:pacemaker:Dummy +primitive st stonith:null params hostlist=node1 +clone c1 p1 +ms m1 p2 +property default-action-timeout=60s +""") + assert ok + + obj = cibconfig.mkset_obj() + assert obj is not None + ok = obj.save("""property default-action-timeout=2m +node node1 \ + attributes mem=16G +primitive st stonith:null \ + params hostlist='node1' \ + meta description="some description here" \ + op start requires=nothing \ + op monitor interval=60m +primitive p1 ocf:heartbeat:Dummy \ + op monitor interval=60m \ + op monitor interval=120m OCF_CHECK_LEVEL=10 +""") + assert ok + + obj = cibconfig.mkset_obj() + with clidisplay.nopretty(): + text = obj.repr() + text = text + "\nprimitive p2 ocf:heartbeat:Dummy" + ok = obj.save(text) + assert ok + + obj = cibconfig.mkset_obj() + with clidisplay.nopretty(): + text = obj.repr() + text = text + "\ngroup g1 p1 p2" + ok = obj.save(text) + assert ok + + obj = cibconfig.mkset_obj("g1") + with clidisplay.nopretty(): + text = obj.repr() + text = text.replace("group g1 p1 p2", "group g1 p1 p3") + text = text + "\nprimitive p3 ocf:heartbeat:Dummy" + ok = obj.save(text) + assert ok + + obj = cibconfig.mkset_obj("g1") + with clidisplay.nopretty(): + print obj.repr().strip() + assert obj.repr().strip() == "group g1 p1 p3" + + obj = cibconfig.mkset_obj() + assert obj is not None + ok = obj.save(original_cib) + assert ok + obj = cibconfig.mkset_obj() + with clidisplay.nopretty(): + print "*** ORIGINAL" + print original_cib + print "*** NOW" + print obj.repr() + assert original_cib == obj.repr() + + +@with_setup(setup_func, teardown_func) +def test_id_collision_breakage_2(): + from crmsh import clidisplay + + obj = cibconfig.mkset_obj() + assert obj is not None + with clidisplay.nopretty(): + original_cib = obj.repr() + print original_cib + + obj = cibconfig.mkset_obj() + assert obj is not None + + ok = obj.save("""node 168633610: webui +node 168633611: node1 +rsc_template web-server apache \ + params port=8000 \ + op monitor interval=10s +primitive d0 Dummy \ + meta target-role=Started +primitive d1 Dummy +primitive d2 Dummy +# Never use this STONITH agent in production! +primitive development-stonith stonith:null \ + params hostlist="webui node1 node2 node3" +primitive proxy systemd:haproxy \ + op monitor interval=10s +primitive proxy-vip IPaddr2 \ + params ip=10.13.37.20 +primitive srv1 @web-server +primitive srv2 @web-server +primitive vip1 IPaddr2 \ + params ip=10.13.37.21 \ + op monitor interval=20s +primitive vip2 IPaddr2 \ + params ip=10.13.37.22 \ + op monitor interval=20s +primitive virtual-ip IPaddr2 \ + params ip=10.13.37.77 lvs_support=false \ + op start timeout=20 interval=0 \ + op stop timeout=20 interval=0 \ + op monitor interval=10 timeout=20 +primitive yet-another-virtual-ip IPaddr2 \ + params ip=10.13.37.72 cidr_netmask=24 \ + op start interval=0 timeout=20 \ + op stop interval=0 timeout=20 \ + op monitor interval=10 timeout=20 \ + meta target-role=Started +group dovip d0 virtual-ip \ + meta target-role=Stopped +group g-proxy proxy-vip proxy +group g-serv1 vip1 srv1 +group g-serv2 vip2 srv2 +clone d2-clone d2 \ + meta target-role=Started +tag dummytag d0 d1 d1-on-node1 d2 d2-clone +# Never put the two web servers on the same node +colocation co-serv -inf: g-serv1 g-serv2 +location d1-on-node1 d1 inf: node1 +# Never put any web server or haproxy on webui +location l-avoid-webui { g-proxy g-serv1 g-serv2 } -inf: webui +# Prever to spread groups across nodes +location l-proxy g-proxy 200: node1 +location l-serv1 g-serv1 200: node2 +location l-serv2 g-serv2 200: node3 +property cib-bootstrap-options: \ + have-watchdog=false \ + dc-version="1.1.13+git20150917.20c2178-224.2-1.1.13+git20150917.20c2178" \ + cluster-infrastructure=corosync \ + cluster-name=hacluster \ + stonith-enabled=true \ + no-quorum-policy=ignore \ + placement-strategy=balanced +rsc_defaults rsc-options: \ + resource-stickiness=1 \ + migration-threshold=3 +op_defaults op-options: \ + timeout=600 \ + record-pending=true +""") + assert ok + + obj = cibconfig.mkset_obj() + assert obj is not None + ok = obj.save(original_cib) + assert ok + obj = cibconfig.mkset_obj() + with clidisplay.nopretty(): + print "*** ORIGINAL" + print original_cib + print "*** NOW" + print obj.repr() + assert original_cib == obj.repr() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crmsh-2.2.0~rc3+git.1443544100.aa2abda/test/unittests/test_objset.py new/crmsh-2.2.0~rc3+git.1444340345.59850ca/test/unittests/test_objset.py --- old/crmsh-2.2.0~rc3+git.1443544100.aa2abda/test/unittests/test_objset.py 2015-09-30 17:21:36.000000000 +0200 +++ new/crmsh-2.2.0~rc3+git.1444340345.59850ca/test/unittests/test_objset.py 2015-10-08 23:44:05.000000000 +0200 @@ -39,4 +39,4 @@ setobj = cibconfig.mkset_obj() s = setobj.repr_nopretty() sp = s.splitlines() - assert_in("node 1: ha-one", sp[0:3]) + assert_in("node ha-one", sp[0:3]) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crmsh-2.2.0~rc3+git.1443544100.aa2abda/test/unittests/test_parse.py new/crmsh-2.2.0~rc3+git.1444340345.59850ca/test/unittests/test_parse.py --- old/crmsh-2.2.0~rc3+git.1443544100.aa2abda/test/unittests/test_parse.py 2015-09-30 17:21:36.000000000 +0200 +++ new/crmsh-2.2.0~rc3+git.1444340345.59850ca/test/unittests/test_parse.py 2015-10-08 23:44:05.000000000 +0200 @@ -434,7 +434,8 @@ # num test nodes are 3 out = self.parser.parse('fencing_topology poison-pill power') - self.assertEqual("""<fencing-topology><fencing-level devices="poison-pill" index="1" target="ha-one"/><fencing-level devices="power" index="2" target="ha-one"/><fencing-level devices="poison-pill" index="1" target="ha-two"/><fencing-level devices="power" index="2" target="ha-two"/><fencing-level devices="poison-pill" index="1" target="ha-three"/><fencing-level devices="power" index="2" target="ha-three"/></fencing-topology>""", etree.tostring(out)) + expect = '<fencing-topology><fencing-level devices="poison-pill" index="1" target="ha-one"/><fencing-level devices="power" index="2" target="ha-one"/><fencing-level devices="poison-pill" index="1" target="ha-three"/><fencing-level devices="power" index="2" target="ha-three"/><fencing-level devices="poison-pill" index="1" target="ha-two"/><fencing-level devices="power" index="2" target="ha-two"/></fencing-topology>' + self.assertEqual(expect, etree.tostring(out)) out = self.parser.parse('fencing_topology node-a: poison-pill power node-b: ipmi serial') self.assertEqual(4, len(out)) @@ -661,7 +662,7 @@ '<rsc_ticket id="ticket-A_m6" ticket="ticket-A" rsc="m6"/>', '<rsc_ticket id="ticket-B_m6_m5" ticket="ticket-B" loss-policy="fence"><resource_set><resource_ref id="m6"/><resource_ref id="m5"/></resource_set></rsc_ticket>', '<rsc_ticket id="ticket-C_master" ticket="ticket-C" loss-policy="fence"><resource_set><resource_ref id="m6"/></resource_set><resource_set role="Master"><resource_ref id="m5"/></resource_set></rsc_ticket>', - '<fencing-topology><fencing-level devices="st" index="1" target="ha-one"/><fencing-level devices="st2" index="2" target="ha-one"/><fencing-level devices="st" index="1" target="ha-two"/><fencing-level devices="st2" index="2" target="ha-two"/><fencing-level devices="st" index="1" target="ha-three"/><fencing-level devices="st2" index="2" target="ha-three"/></fencing-topology>', + '<fencing-topology><fencing-level devices="st" index="1" target="ha-one"/><fencing-level devices="st2" index="2" target="ha-one"/><fencing-level devices="st" index="1" target="ha-three"/><fencing-level devices="st2" index="2" target="ha-three"/><fencing-level devices="st" index="1" target="ha-two"/><fencing-level devices="st2" index="2" target="ha-two"/></fencing-topology>', '<cluster_property_set><nvpair name="stonith-enabled" value="true"/></cluster_property_set>', '<cluster_property_set id="cpset2"><nvpair name="maintenance-mode" value="true"/></cluster_property_set>', '<rsc_defaults><meta_attributes><nvpair name="failure-timeout" value="10m"/></meta_attributes></rsc_defaults>',