class TestTagMixin( get_assert_pcs_effect_mixin(lambda cib: etree.tostring( # pylint:disable=undefined-variable etree.parse(cib).findall(".//tags")[0]))): def setUp(self): # pylint: disable=invalid-name self.temp_cib = get_tmp_file("tier1_tag") write_file_to_tmpfile(tags_cib, self.temp_cib) self.pcs_runner = PcsRunner(self.temp_cib.name) def tearDown(self): # pylint: disable=invalid-name self.temp_cib.close() @staticmethod def fixture_tags_xml(tag1=None, append=""): tag1_default = """ <tag id="tag1"> <obj_ref id="x1"/> <obj_ref id="x2"/> <obj_ref id="x3"/> </tag> """ if tag1 is None: tag1 = tag1_default return f"""
class RscDefaultsSetCreate( get_assert_pcs_effect_mixin(lambda cib: etree.tostring( # pylint:disable=undefined-variable etree.parse(cib).findall(".//rsc_defaults")[0])), DefaultsSetCreateMixin, TestCase, ): cli_command = ["resource", "defaults"] cib_tag = "rsc_defaults" @skip_unless_pacemaker_supports_rsc_and_op_rules() def test_success_rules_rsc_op(self): self.assert_effect( self.cli_command + "set create id=X meta nam1=val1 rule resource ::Dummy".split(), f"""\ <{self.cib_tag}> <meta_attributes id="X"> <rule id="X-rule" boolean-op="and" score="INFINITY"> <rsc_expression id="X-rule-rsc-Dummy" type="Dummy"/> </rule> <nvpair id="X-nam1" name="nam1" value="val1"/> </meta_attributes> </{self.cib_tag}> """, output=( "CIB has been upgraded to the latest schema version.\n" "Warning: Defaults do not apply to resources which override " "them with their own defined values\n"), )
class PropertyUnset( TestCase, get_assert_pcs_effect_mixin(lambda cib: etree.tostring( # pylint:disable=undefined-variable etree.parse(cib).findall(".//crm_config")[0])), ): def setUp(self): self.temp_cib = get_tmp_file("tier1_properties_unset") write_file_to_tmpfile(empty_cib, self.temp_cib) self.pcs_runner = PcsRunner(self.temp_cib.name) def tearDown(self): self.temp_cib.close() @staticmethod def fixture_xml_no_props(): # must match empty_cib return """ <crm_config /> """ @staticmethod def fixture_xml_empty_props(): # must match empty_cib return """ <crm_config> <cluster_property_set id="cib-bootstrap-options" /> </crm_config> """ @staticmethod def fixture_xml_with_props(): # must match empty_cib return """ <crm_config> <cluster_property_set id="cib-bootstrap-options"> <nvpair id="cib-bootstrap-options-batch-limit" name="batch-limit" value="100" /> </cluster_property_set> </crm_config> """ def test_keep_empty_nvset(self): self.assert_effect( "property set batch-limit=100".split(), self.fixture_xml_with_props(), ) self.assert_effect("property unset batch-limit".split(), self.fixture_xml_empty_props()) def test_dont_create_nvset_on_removal(self): # pcs mimics crm_attribute. So this behaves differently than the rest # of pcs - instead of doing nothing it returns an error. # Should be changed to be consistent with the rest of pcs. self.assert_pcs_fail( "property unset batch-limit".split(), "Error: can't remove property: 'batch-limit' that doesn't exist\n", )
class OpDefaultsUpdate( get_assert_pcs_effect_mixin(lambda cib: etree.tostring( # pylint:disable=undefined-variable etree.parse(cib).findall(".//op_defaults")[0])), DefaultsUpdateMixin, TestCase, ): cli_command = ["resource", "op", "defaults"] prefix = "op" cib_tag = "op_defaults"
class ResourceTest( TestCase, get_assert_pcs_effect_mixin(get_cib_resources) ): empty_cib = rc("cib-empty.xml") temp_cib = rc("temp-cib.xml") def setUp(self): shutil.copy(self.empty_cib, self.temp_cib) self.pcs_runner = PcsRunner(self.temp_cib)
class ResourceTest(TestCase, get_assert_pcs_effect_mixin(get_cib_resources)): empty_cib = rc("cib-empty.xml") def setUp(self): self.temp_cib = get_tmp_file("tier1_test_resource_common") write_file_to_tmpfile(self.empty_cib, self.temp_cib) self.pcs_runner = PcsRunner(self.temp_cib.name) def tearDown(self): self.temp_cib.close()
class RscDefaultsSetUpdate( get_assert_pcs_effect_mixin(lambda cib: etree.tostring( # pylint:disable=undefined-variable etree.parse(cib).findall(".//rsc_defaults")[0])), DefaultsSetUpdateMixin, TestCase, ): cli_command = "resource defaults" prefix = "rsc" cib_tag = "rsc_defaults"
class BundleCreateCommon( TestCase, get_assert_pcs_effect_mixin(lambda cib: etree.tostring( # pylint:disable=undefined-variable etree.parse(cib).findall(".//resources")[0]))): temp_cib = rc("temp-cib.xml") empty_cib = None def setUp(self): shutil.copy(self.empty_cib, self.temp_cib) self.pcs_runner = PcsRunner(self.temp_cib) self.pcs_runner.mock_settings = get_mock_settings( "crm_resource_binary")
class BundleCreateCommon( TestCase, get_assert_pcs_effect_mixin(lambda cib: etree.tostring( # pylint:disable=undefined-variable etree.parse(cib).findall(".//resources")[0])), ): empty_cib = rc("cib-empty.xml") def setUp(self): self.temp_cib = get_tmp_file("tier1_bundle_create") write_file_to_tmpfile(self.empty_cib, self.temp_cib) self.pcs_runner = PcsRunner(self.temp_cib.name) self.pcs_runner.mock_settings = get_mock_settings( "crm_resource_binary") def tearDown(self): self.temp_cib.close()
class TestGroupMixin( get_assert_pcs_effect_mixin(lambda cib: etree.tostring( # pylint:disable=undefined-variable etree.parse(cib).findall(".//resources")[0])), ): empty_cib = rc("cib-empty.xml") def setUp(self): # pylint: disable=invalid-name self.temp_cib = get_tmp_file("tier1_cib_resource_group_ungroup") self.pcs_runner = PcsRunner(self.temp_cib.name) xml_manip = XmlManipulation.from_file(self.empty_cib) xml_manip.append_to_first_tag_name("resources", FIXTURE_AGROUP_XML) xml_manip.append_to_first_tag_name( "configuration", """ <tags> <tag id="T1"> <obj_ref id="AGroup"/> </tag> <tag id="T2"> <obj_ref id="AGroup"/> </tag> </tags> """, ) xml_manip.append_to_first_tag_name( "constraints", """ <rsc_location id="location-AGroup-rh7-1-INFINITY" node="rh7-1" rsc="AGroup" score="INFINITY"/> """, """ <rsc_location id="location-T1-rh7-1-INFINITY" node="rh7-1" rsc="T1" score="INFINITY"/> """, ) write_data_to_tmpfile(str(xml_manip), self.temp_cib) def tearDown(self): # pylint: disable=invalid-name self.temp_cib.close()
class RscDefaultsSetCreate( get_assert_pcs_effect_mixin(lambda cib: etree.tostring( # pylint:disable=undefined-variable etree.parse(cib).findall(".//rsc_defaults")[0])), DefaultsSetCreateMixin, TestCase, ): cli_command = ["resource", "defaults"] cib_tag = "rsc_defaults" @skip_unless_pacemaker_supports_rsc_and_op_rules() def test_success_rules_rsc_op(self): self.assert_effect( self.cli_command + "set create id=X meta nam1=val1 rule resource ::Dummy".split(), f"""\ <{self.cib_tag}> <meta_attributes id="X"> <rule id="X-rule" boolean-op="and" score="INFINITY"> <rsc_expression id="X-rule-rsc-Dummy" type="Dummy"/> </rule> <nvpair id="X-nam1" name="nam1" value="val1"/> </meta_attributes> </{self.cib_tag}> """, output=( "CIB has been upgraded to the latest schema version.\n" "Warning: Defaults do not apply to resources which override " "them with their own defined values\n"), ) def test_node_attr_expressions(self): self.assert_pcs_fail( self.cli_command + ("set create rule defined attr").split(), ("Error: Keywords 'defined', 'not_defined', 'eq', 'ne', 'gte', " "'gt', 'lte' and 'lt' cannot be used in a rule in this command\n" "Error: Errors have occurred, therefore pcs is unable to continue\n" ), )
class NodeAttributeTest( TestCase, get_assert_pcs_effect_mixin(lambda cib: etree.tostring( # pylint:disable=undefined-variable etree.parse(cib).findall(".//nodes")[0])), ): # pylint: disable=too-many-public-methods def setUp(self): self.temp_cib = get_tmp_file("tier1_node_attribute") write_file_to_tmpfile(empty_cib, self.temp_cib) self.pcs_runner = PcsRunner(self.temp_cib.name) def tearDown(self): self.temp_cib.close() def fixture_attrs(self, nodes, attrs=None): attrs = {} if attrs is None else attrs xml_lines = ["<nodes>"] for node_id, node_name in enumerate(nodes, 1): xml_lines.extend([ '<node id="{0}" uname="{1}">'.format(node_id, node_name), '<instance_attributes id="nodes-{0}">'.format(node_id), ]) nv = '<nvpair id="nodes-{id}-{name}" name="{name}" value="{val}"/>' for name, value in attrs.get(node_name, {}).items(): xml_lines.append(nv.format(id=node_id, name=name, val=value)) xml_lines.extend(["</instance_attributes>", "</node>"]) xml_lines.append("</nodes>") utils_usefile_original = utils.usefile utils_filename_original = utils.filename utils.usefile = True utils.filename = self.temp_cib.name output, retval = utils.run( ["cibadmin", "--modify", "--xml-text", "\n".join(xml_lines)]) utils.usefile = utils_usefile_original utils.filename = utils_filename_original assert output == "" assert retval == 0 @staticmethod def fixture_xml_no_attrs(): # must match empty_cib return """ <nodes> <node id="1" uname="rh7-1" /> <node id="2" uname="rh7-2" /> </nodes> """ @staticmethod def fixture_xml_empty_attrs(): # must match empty_cib return """ <nodes> <node id="1" uname="rh7-1"> <instance_attributes id="nodes-1" /> </node> <node id="2" uname="rh7-2" /> </nodes> """ @staticmethod def fixture_xml_with_attrs(): # must match empty_cib return """ <nodes> <node id="1" uname="rh7-1"> <instance_attributes id="nodes-1"> <nvpair id="nodes-1-test" name="test" value="100" /> </instance_attributes> </node> <node id="2" uname="rh7-2" /> </nodes> """ def test_show_empty(self): self.fixture_attrs(["rh7-1", "rh7-2"]) self.assert_pcs_success("node attribute".split(), "Node Attributes:\n") def test_show_nonempty(self): self.fixture_attrs( ["rh7-1", "rh7-2"], { "rh7-1": { "IP": "192.168.1.1", }, "rh7-2": { "IP": "192.168.1.2", }, }, ) self.assert_pcs_success( "node attribute".split(), """\ Node Attributes: rh7-1: IP=192.168.1.1 rh7-2: IP=192.168.1.2 """, ) def test_show_multiple_per_node(self): self.fixture_attrs( ["rh7-1", "rh7-2"], { "rh7-1": { "IP": "192.168.1.1", "alias": "node1", }, "rh7-2": { "IP": "192.168.1.2", "alias": "node2", }, }, ) self.assert_pcs_success( "node attribute".split(), """\ Node Attributes: rh7-1: IP=192.168.1.1 alias=node1 rh7-2: IP=192.168.1.2 alias=node2 """, ) def test_show_one_node(self): self.fixture_attrs( ["rh7-1", "rh7-2"], { "rh7-1": { "IP": "192.168.1.1", "alias": "node1", }, "rh7-2": { "IP": "192.168.1.2", "alias": "node2", }, }, ) self.assert_pcs_success( "node attribute rh7-1".split(), """\ Node Attributes: rh7-1: IP=192.168.1.1 alias=node1 """, ) def test_show_missing_node(self): self.fixture_attrs( ["rh7-1", "rh7-2"], { "rh7-1": { "IP": "192.168.1.1", "alias": "node1", }, "rh7-2": { "IP": "192.168.1.2", "alias": "node2", }, }, ) self.assert_pcs_success( "node attribute rh7-3".split(), """\ Node Attributes: """, ) def test_show_name(self): self.fixture_attrs( ["rh7-1", "rh7-2"], { "rh7-1": { "IP": "192.168.1.1", "alias": "node1", }, "rh7-2": { "IP": "192.168.1.2", "alias": "node2", }, }, ) self.assert_pcs_success( "node attribute --name alias".split(), """\ Node Attributes: rh7-1: alias=node1 rh7-2: alias=node2 """, ) def test_show_missing_name(self): self.fixture_attrs( ["rh7-1", "rh7-2"], { "rh7-1": { "IP": "192.168.1.1", "alias": "node1", }, "rh7-2": { "IP": "192.168.1.2", "alias": "node2", }, }, ) self.assert_pcs_success( "node attribute --name missing".split(), """\ Node Attributes: """, ) def test_show_node_and_name(self): self.fixture_attrs( ["rh7-1", "rh7-2"], { "rh7-1": { "IP": "192.168.1.1", "alias": "node1", }, "rh7-2": { "IP": "192.168.1.2", "alias": "node2", }, }, ) self.assert_pcs_success( "node attribute --name alias rh7-1".split(), """\ Node Attributes: rh7-1: alias=node1 """, ) def test_set_new(self): self.fixture_attrs(["rh7-1", "rh7-2"]) self.assert_pcs_success("node attribute rh7-1 IP=192.168.1.1".split()) self.assert_pcs_success( "node attribute".split(), """\ Node Attributes: rh7-1: IP=192.168.1.1 """, ) self.assert_pcs_success("node attribute rh7-2 IP=192.168.1.2".split()) self.assert_pcs_success( "node attribute".split(), """\ Node Attributes: rh7-1: IP=192.168.1.1 rh7-2: IP=192.168.1.2 """, ) def test_set_existing(self): self.fixture_attrs( ["rh7-1", "rh7-2"], { "rh7-1": { "IP": "192.168.1.1", }, "rh7-2": { "IP": "192.168.1.2", }, }, ) self.assert_pcs_success("node attribute rh7-2 IP=192.168.2.2".split()) self.assert_pcs_success( "node attribute".split(), """\ Node Attributes: rh7-1: IP=192.168.1.1 rh7-2: IP=192.168.2.2 """, ) def test_unset(self): self.fixture_attrs( ["rh7-1", "rh7-2"], { "rh7-1": { "IP": "192.168.1.1", }, "rh7-2": { "IP": "192.168.1.2", }, }, ) self.assert_pcs_success("node attribute rh7-2 IP=".split()) self.assert_pcs_success( "node attribute".split(), """\ Node Attributes: rh7-1: IP=192.168.1.1 """, ) def test_unset_nonexisting(self): self.fixture_attrs( ["rh7-1", "rh7-2"], { "rh7-1": { "IP": "192.168.1.1", }, "rh7-2": { "IP": "192.168.1.2", }, }, ) self.assert_pcs_result( "node attribute rh7-1 missing=".split(), "Error: attribute: 'missing' doesn't exist for node: 'rh7-1'\n", returncode=2, ) def test_unset_nonexisting_forced(self): self.fixture_attrs( ["rh7-1", "rh7-2"], { "rh7-1": { "IP": "192.168.1.1", }, "rh7-2": { "IP": "192.168.1.2", }, }, ) self.assert_pcs_success( "node attribute rh7-1 missing= --force".split(), "") def test_keep_empty_nvset(self): self.assert_effect( "node attribute rh7-1 test=100".split(), self.fixture_xml_with_attrs(), ) self.assert_effect( "node attribute rh7-1 test=".split(), self.fixture_xml_empty_attrs(), ) def test_dont_create_nvset_on_removal(self): # pcs does not actually do cib editing, it passes it to crm_node. So # this behaves differently than the rest of pcs - instead of doing # nothing it returns an error. # Should be changed to be consistent with the rest of pcs. output, retval = pcs(self.temp_cib.name, "node attribute rh7-1 test=".split()) self.assertEqual( output, "Error: attribute: 'test' doesn't exist for node: 'rh7-1'\n") self.assertEqual(retval, 2)
class OperationAdd(TestCase, get_assert_pcs_effect_mixin(get_cib_resources)): empty_cib = rc("cib-empty.xml") def setUp(self): self.temp_cib = get_tmp_file("tier1_cib_resource_operation_add") self.pcs_runner = PcsRunner(self.temp_cib.name) write_data_to_tmpfile(self.fixture_cib_cache(), self.temp_cib) def tearDown(self): self.temp_cib.close() def fixture_cib_cache(self): if not hasattr(self.__class__, "cib_cache"): self.__class__.cib_cache = self.fixture_cib() return self.__class__.cib_cache def fixture_cib(self): write_file_to_tmpfile(self.empty_cib, self.temp_cib) self.assert_pcs_success( "resource create --no-default-ops R ocf:heartbeat:Dummy".split()) # add to cib: # <primitive class="ocf" id="R" provider="heartbeat" type="Dummy"> # <operations> # <op id="R-monitor-interval-60s" interval="60s" # name="monitor" # /> # </operations> # </primitive> self.temp_cib.seek(0) return self.temp_cib.read() def test_base_add(self): self.assert_effect( "resource op add R start interval=20s".split(), """<resources> <primitive class="ocf" id="R" provider="heartbeat" type="Dummy"> <operations> <op id="R-monitor-interval-10s" interval="10s" name="monitor" timeout="20s" /> <op id="R-start-interval-20s" interval="20s" name="start" /> </operations> </primitive> </resources>""", ) def test_add_with_OCF_CHECK_LEVEL(self): # pylint: disable=invalid-name self.assert_effect( ("resource op add R start interval=20s OCF_CHECK_LEVEL=1 " "description=test-description").split(), """<resources> <primitive class="ocf" id="R" provider="heartbeat" type="Dummy"> <operations> <op id="R-monitor-interval-10s" interval="10s" name="monitor" timeout="20s" /> <op description="test-description" name="start" id="R-start-interval-20s" interval="20s" > <instance_attributes id="params-R-start-interval-20s" > <nvpair id="R-start-interval-20s-OCF_CHECK_LEVEL-1" name="OCF_CHECK_LEVEL" value="1" /> </instance_attributes> </op> </operations> </primitive> </resources>""", ) def test_can_multiple_operation_add(self): self.assert_effect( "resource op add R start interval=20s".split(), """<resources> <primitive class="ocf" id="R" provider="heartbeat" type="Dummy"> <operations> <op id="R-monitor-interval-10s" interval="10s" name="monitor" timeout="20s" /> <op id="R-start-interval-20s" interval="20s" name="start" /> </operations> </primitive> </resources>""", ) self.assert_effect( "resource op add R stop interval=30s".split(), """<resources> <primitive class="ocf" id="R" provider="heartbeat" type="Dummy"> <operations> <op id="R-monitor-interval-10s" interval="10s" name="monitor" timeout="20s" /> <op id="R-start-interval-20s" interval="20s" name="start" /> <op id="R-stop-interval-30s" interval="30s" name="stop" /> </operations> </primitive> </resources>""", ) def test_id_specified(self): self.assert_effect( "resource op add R start timeout=30 id=abcd".split(), """<resources> <primitive class="ocf" id="R" provider="heartbeat" type="Dummy"> <operations> <op id="R-monitor-interval-10s" interval="10s" name="monitor" timeout="20s" /> <op id="abcd" interval="0s" name="start" timeout="30" /> </operations> </primitive> </resources>""", ) def test_invalid_id(self): self.assert_pcs_fail_regardless_of_force( "resource op add R start timeout=30 id=ab#cd".split(), "Error: invalid operation id 'ab#cd', '#' is not a valid" " character for a operation id\n", ) def test_duplicate_id(self): self.assert_pcs_fail_regardless_of_force( "resource op add R start timeout=30 id=R".split(), "Error: id 'R' is already in use, please specify another one\n", ) def test_unknown_option(self): self.assert_pcs_fail( "resource op add R start timeout=30 requires=quorum".split(), ("Error: requires is not a valid op option (use --force to " "override)\n"), )
class ManageUnmanage( TestCase, get_assert_pcs_effect_mixin(lambda cib: etree.tostring( # pylint:disable=undefined-variable etree.parse(cib).findall(".//resources")[0])), ): empty_cib = rc("cib-empty.xml") @staticmethod def fixture_cib_unmanaged_a(add_empty_meta_b=False): empty_meta_b = '<meta_attributes id="B-meta_attributes" />' return """ <resources> <primitive class="ocf" id="A" provider="heartbeat" type="Dummy"> <meta_attributes id="A-meta_attributes"> <nvpair id="A-meta_attributes-is-managed" name="is-managed" value="false" /> </meta_attributes> <operations> <op id="A-monitor-interval-10s" interval="10s" name="monitor" timeout="20s" /> </operations> </primitive> <primitive class="ocf" id="B" provider="heartbeat" type="Dummy"> {empty_meta_b}<operations> <op id="B-monitor-interval-10s" interval="10s" name="monitor" timeout="20s" /> </operations> </primitive> </resources> """.format(empty_meta_b=(empty_meta_b if add_empty_meta_b else "")) def setUp(self): self.temp_cib = get_tmp_file("tier1_cib_resource_manage_unmanage") write_file_to_tmpfile(self.empty_cib, self.temp_cib) self.pcs_runner = PcsRunner(self.temp_cib.name) def tearDown(self): self.temp_cib.close() def fixture_resource(self, name, managed=True, with_monitors=False): self.assert_pcs_success([ "resource", "create", name, "ocf:heartbeat:Dummy", "--no-default-ops", ]) if not managed: cmd = ["resource", "unmanage", name] if with_monitors: cmd.append("--monitor") self.assert_pcs_success(cmd) def fixture_tag(self, name, ids): self.assert_pcs_success(["tag", "create", name] + ids) def test_unmanage_none(self): self.assert_pcs_fail( "resource unmanage".split(), "Error: You must specify resource(s) to unmanage\n", ) def test_manage_none(self): self.assert_pcs_fail( "resource manage".split(), "Error: You must specify resource(s) to manage\n", ) def test_unmanage_one(self): self.fixture_resource("A") self.fixture_resource("B") self.assert_effect("resource unmanage A".split(), self.fixture_cib_unmanaged_a()) def test_manage_one(self): self.fixture_resource("A", managed=False) self.fixture_resource("B", managed=False) self.assert_effect( "resource manage B".split(), self.fixture_cib_unmanaged_a(add_empty_meta_b=True), ) def test_unmanage_monitor(self): self.fixture_resource("A") self.assert_effect( "resource unmanage A --monitor".split(), """ <resources> <primitive class="ocf" id="A" provider="heartbeat" type="Dummy"> <meta_attributes id="A-meta_attributes"> <nvpair id="A-meta_attributes-is-managed" name="is-managed" value="false" /> </meta_attributes> <operations> <op id="A-monitor-interval-10s" interval="10s" name="monitor" timeout="20s" enabled="false" /> </operations> </primitive> </resources> """, ) def test_unmanage_monitor_enabled(self): self.fixture_resource("A") self.assert_effect( "resource unmanage A".split(), """ <resources> <primitive class="ocf" id="A" provider="heartbeat" type="Dummy"> <meta_attributes id="A-meta_attributes"> <nvpair id="A-meta_attributes-is-managed" name="is-managed" value="false" /> </meta_attributes> <operations> <op id="A-monitor-interval-10s" interval="10s" name="monitor" timeout="20s" /> </operations> </primitive> </resources> """, ) def test_manage_monitor(self): self.fixture_resource("A", managed=True, with_monitors=True) self.assert_effect( "resource manage A --monitor".split(), """ <resources> <primitive class="ocf" id="A" provider="heartbeat" type="Dummy"> <operations> <op id="A-monitor-interval-10s" interval="10s" name="monitor" timeout="20s" /> </operations> </primitive> </resources> """, ) def test_manage_monitor_disabled(self): self.fixture_resource("A", managed=False, with_monitors=True) self.assert_effect( "resource manage A".split(), """ <resources> <primitive class="ocf" id="A" provider="heartbeat" type="Dummy"> <meta_attributes id="A-meta_attributes" /> <operations> <op id="A-monitor-interval-10s" interval="10s" name="monitor" timeout="20s" enabled="false" /> </operations> </primitive> </resources> """, "Warning: Resource 'A' has no enabled monitor operations." " Re-run with '--monitor' to enable them.\n", ) def test_unmanage_more(self): self.fixture_resource("A") self.fixture_resource("B") self.fixture_tag("TA", ["A"]) self.assert_effect( "resource unmanage TA B".split(), """ <resources> <primitive class="ocf" id="A" provider="heartbeat" type="Dummy"> <meta_attributes id="A-meta_attributes"> <nvpair id="A-meta_attributes-is-managed" name="is-managed" value="false" /> </meta_attributes> <operations> <op id="A-monitor-interval-10s" interval="10s" name="monitor" timeout="20s" /> </operations> </primitive> <primitive class="ocf" id="B" provider="heartbeat" type="Dummy"> <meta_attributes id="B-meta_attributes"> <nvpair id="B-meta_attributes-is-managed" name="is-managed" value="false" /> </meta_attributes> <operations> <op id="B-monitor-interval-10s" interval="10s" name="monitor" timeout="20s" /> </operations> </primitive> </resources> """, ) def test_manage_more(self): self.fixture_resource("A", managed=False) self.fixture_resource("B", managed=False) self.fixture_tag("TA", ["A"]) self.assert_effect( "resource manage TA B".split(), """ <resources> <primitive class="ocf" id="A" provider="heartbeat" type="Dummy"> <meta_attributes id="A-meta_attributes" /> <operations> <op id="A-monitor-interval-10s" interval="10s" name="monitor" timeout="20s" /> </operations> </primitive> <primitive class="ocf" id="B" provider="heartbeat" type="Dummy"> <meta_attributes id="B-meta_attributes" /> <operations> <op id="B-monitor-interval-10s" interval="10s" name="monitor" timeout="20s" /> </operations> </primitive> </resources> """, ) def test_unmanage_nonexistent(self): self.fixture_resource("A") self.assert_pcs_fail( "resource unmanage A B".split(), ("Error: bundle/clone/group/resource/tag 'B' does not exist\n" "Error: Errors have occurred, therefore pcs is unable to continue\n" ), ) self.assert_resources_xml_in_cib(""" <resources> <primitive class="ocf" id="A" provider="heartbeat" type="Dummy"> <operations> <op id="A-monitor-interval-10s" interval="10s" name="monitor" timeout="20s" /> </operations> </primitive> </resources> """) def test_manage_nonexistent(self): self.fixture_resource("A", managed=False) self.assert_pcs_fail( "resource manage A B".split(), ("Error: bundle/clone/group/resource/tag 'B' does not exist\n" "Error: Errors have occurred, therefore pcs is unable to continue\n" ), ) self.assert_resources_xml_in_cib(""" <resources> <primitive class="ocf" id="A" provider="heartbeat" type="Dummy"> <meta_attributes id="A-meta_attributes"> <nvpair id="A-meta_attributes-is-managed" name="is-managed" value="false" /> </meta_attributes> <operations> <op id="A-monitor-interval-10s" interval="10s" name="monitor" timeout="20s" /> </operations> </primitive> </resources> """)
class Clone( TestCase, get_assert_pcs_effect_mixin( lambda cib: etree.tostring( # pylint:disable=undefined-variable etree.parse(cib).findall(".//resources")[0] ) ), ): # pylint: disable=too-many-public-methods empty_cib = rc("cib-empty.xml") def setUp(self): self.temp_cib = get_tmp_file("tier1_cib_resource_clone_unclone_clone") self.pcs_runner = PcsRunner(self.temp_cib.name) self.set_cib_file(FIXTURE_PRIMITIVE_FOR_CLONE) self.stonith_deprecation_warning = ( "Deprecation Warning: Ability of this command to accept stonith " "resources is deprecated and will be removed in a future release.\n" ) def tearDown(self): self.temp_cib.close() def set_cib_file(self, *xml_string_list): xml_manip = XmlManipulation.from_file(self.empty_cib) xml_manip.append_to_first_tag_name("resources", *xml_string_list) write_data_to_tmpfile(str(xml_manip), self.temp_cib) def test_clone(self): self.assert_effect( "resource clone C".split(), fixture_resources_xml(fixture_clone("C-clone", "C")), ) def test_clone_custom_id(self): self.assert_effect( "resource clone C CustomCloneId".split(), fixture_resources_xml(fixture_clone("CustomCloneId", "C")), ) def test_clone_id_increment(self): self.set_cib_file( fixture_clone("C-clone", "Dummy"), FIXTURE_PRIMITIVE_FOR_CLONE, ) self.assert_effect( "resource clone C".split(), fixture_resources_xml( fixture_clone("C-clone", "Dummy"), fixture_clone("C-clone-1", "C"), ), ) def test_clone_id_is_stonith(self): self.set_cib_file(FIXTURE_STONITH_FOR_CLONE) self.assert_pcs_fail( "resource clone fence-device".split(), self.stonith_deprecation_warning + fixture_clone_stonith_msg(), ) self.assert_resources_xml_in_cib( fixture_resources_xml(FIXTURE_STONITH_FOR_CLONE) ) def test_clone_id_is_stonith_forced(self): self.set_cib_file(FIXTURE_STONITH_FOR_CLONE) self.assert_effect( "resource clone fence-device --force".split(), fixture_resources_xml(FIXTURE_STONITH_CLONE), output=self.stonith_deprecation_warning + fixture_clone_stonith_msg(forced=True), ) def test_clone_group_with_stonith(self): self.set_cib_file(FIXTURE_GROUP_WITH_STONITH) self.assert_effect( "resource clone Group".split(), fixture_resources_xml(FIXTURE_CLONED_GROUP_WITH_STONITH), ) def test_clone_group_with_stonith_forced(self): self.set_cib_file(FIXTURE_GROUP_WITH_STONITH) self.assert_effect( "resource clone Group --force".split(), fixture_resources_xml(FIXTURE_CLONED_GROUP_WITH_STONITH), ) def test_promotable_clone(self): self.assert_effect( "resource promotable C".split(), fixture_resources_xml( fixture_clone("C-clone", "C", promotable=True) ), ) def test_promotable_clone_custom_id(self): self.assert_effect( "resource promotable C CustomPromotableId".split(), fixture_resources_xml( fixture_clone("CustomPromotableId", "C", promotable=True) ), ) def test_promotable_clone_id_is_stonith(self): self.set_cib_file(FIXTURE_STONITH_FOR_CLONE) self.assert_pcs_fail( "resource promotable fence-device".split(), self.stonith_deprecation_warning + fixture_clone_stonith_msg(), ) self.assert_resources_xml_in_cib( fixture_resources_xml(FIXTURE_STONITH_FOR_CLONE) ) def test_promotable_clone_id_is_stonith_forced(self): self.set_cib_file(FIXTURE_STONITH_FOR_CLONE) self.assert_effect( "resource promotable fence-device --force".split(), fixture_resources_xml(FIXTURE_STONITH_PROMOTABLE), output=self.stonith_deprecation_warning + fixture_clone_stonith_msg(forced=True), ) def test_promotable_keyword_and_option(self): self.assert_pcs_fail( "resource promotable C CustomCloneId promotable=false".split(), ( "Error: you cannot specify both promotable option and " "promotable keyword\n" ), ) self.assert_resources_xml_in_cib( fixture_resources_xml(FIXTURE_PRIMITIVE_FOR_CLONE) ) def test_clone_with_options(self): self.assert_effect( ( "resource clone C CustomCloneId globally-unique=true meta a=b " "c=d" ).split(), fixture_resources_xml(FIXTURE_CLONE_WITH_OPTIONS), ) def test_group_last_member(self): self.set_cib_file(FIXTURE_GROUP_LAST_MEMBER) self.assert_effect( "resource clone C".split(), fixture_resources_xml(fixture_clone("C-clone", "C")), ) def test_nonexistent_resource(self): self.assert_pcs_fail( "resource clone NonExistentClone".split(), "Error: unable to find group or resource: NonExistentClone\n", ) self.assert_resources_xml_in_cib( fixture_resources_xml(FIXTURE_PRIMITIVE_FOR_CLONE) ) def test_invalid_clone_id(self): self.assert_pcs_fail( "resource clone C 1invalid".split(), "Error: invalid id '1invalid'\n", ) self.assert_resources_xml_in_cib( fixture_resources_xml(FIXTURE_PRIMITIVE_FOR_CLONE) ) def test_clone_id_already_exist(self): self.assert_pcs_fail( "resource clone C C".split(), "Error: id 'C' already exists\n", ) self.assert_resources_xml_in_cib( fixture_resources_xml(FIXTURE_PRIMITIVE_FOR_CLONE) ) def test_group_already_cloned(self): self.set_cib_file(FIXTURE_CLONED_GROUP) self.assert_pcs_fail( "resource clone Group".split(), "Error: cannot clone a group that has already been cloned\n", ) self.assert_resources_xml_in_cib( fixture_resources_xml(FIXTURE_CLONED_GROUP) ) def test_already_a_clone_resource(self): self.set_cib_file(FIXTURE_CLONED_GROUP) self.assert_pcs_fail( "resource clone G1".split(), "Error: G1 is already a clone resource\n", ) self.assert_resources_xml_in_cib( fixture_resources_xml(FIXTURE_CLONED_GROUP) ) def test_bundle_resource(self): self.set_cib_file(FIXTURE_BUNDLE_RESOURCE) self.assert_pcs_fail( "resource clone Dummy".split(), "Error: cannot clone bundle resource\n", ) self.assert_resources_xml_in_cib( fixture_resources_xml(FIXTURE_BUNDLE_RESOURCE) )
class Unclone( TestCase, get_assert_pcs_effect_mixin( lambda cib: etree.tostring( # pylint:disable=undefined-variable etree.parse(cib).findall(".//resources")[0] ) ), ): empty_cib = rc("cib-empty.xml") def assert_tags_xml(self, expected_xml): self.assert_resources_xml_in_cib( expected_xml, get_cib_part_func=lambda cib: etree.tostring( etree.parse(cib).findall(".//tags")[0], ), ) def assert_constraint_xml(self, expected_xml): self.assert_resources_xml_in_cib( expected_xml, get_cib_part_func=lambda cib: etree.tostring( etree.parse(cib).findall(".//constraints")[0], ), ) def setUp(self): # pylint: disable=invalid-name self.temp_cib = get_tmp_file("tier1_cib_resource_group_ungroup") self.pcs_runner = PcsRunner(self.temp_cib.name) xml_manip = XmlManipulation.from_file(self.empty_cib) xml_manip.append_to_first_tag_name( "resources", FIXTURE_CLONE, FIXTURE_DUMMY, ) xml_manip.append_to_first_tag_name( "configuration", FIXTURE_TAGS_CONFIG_XML, ) xml_manip.append_to_first_tag_name( "constraints", """ <rsc_location id="location-C-clone-rh7-1-INFINITY" node="rh7-1" rsc="C-clone" score="INFINITY"/> """, """ <rsc_location id="location-TagCloneOnly-rh7-1-INFINITY" node="rh7-1" rsc="TagCloneOnly" score="INFINITY"/> """, ) write_data_to_tmpfile(str(xml_manip), self.temp_cib) def tearDown(self): # pylint: disable=invalid-name self.temp_cib.close() def test_nonexistent_clone(self): self.assert_pcs_fail( "resource unclone NonExistentClone".split(), "Error: could not find resource: NonExistentClone\n", ) self.assert_resources_xml_in_cib(FIXTURE_CLONE_AND_RESOURCE) self.assert_tags_xml(FIXTURE_TAGS_CONFIG_XML) self.assert_constraint_xml(FIXTURE_CONSTRAINTS_CONFIG_XML) def test_not_clone_resource(self): self.assert_pcs_fail( "resource unclone Dummy".split(), "Error: 'Dummy' is not a clone resource\n", ) self.assert_resources_xml_in_cib(FIXTURE_CLONE_AND_RESOURCE) self.assert_tags_xml(FIXTURE_TAGS_CONFIG_XML) self.assert_constraint_xml(FIXTURE_CONSTRAINTS_CONFIG_XML) def test_unclone_clone_id(self): self.assert_effect( "resource unclone C-clone".split(), FIXTURE_RESOURCES ) self.assert_tags_xml(FIXTURE_TAGS_RESULT_XML) self.assert_constraint_xml("<constraints/>") def test_unclone_resoruce_id(self): self.assert_effect("resource unclone C".split(), FIXTURE_RESOURCES) self.assert_tags_xml(FIXTURE_TAGS_RESULT_XML) self.assert_constraint_xml("<constraints/>")
class OpDefaultsSetCreate( get_assert_pcs_effect_mixin(lambda cib: etree.tostring( # pylint:disable=undefined-variable etree.parse(cib).findall(".//op_defaults")[0])), DefaultsSetCreateMixin, TestCase, ): cli_command = ["resource", "op", "defaults"] cib_tag = "op_defaults" def test_rule_error_messages(self): self.assert_pcs_fail( self.cli_command + ("set create rule defined attr1 or attr2 gte number 12a or " "attr3 lt version 3.2.1a or attr4 ne string test or attr5 lt 3 " ).split(), ("Error: '12a' is not a valid number attribute value, use a " "floating-point number\n" "Error: '3.2.1a' is not a valid version attribute value, use " "a version number (e.g. 1, 1.2, 1.23.45, ...)\n" "Error: Errors have occurred, therefore pcs is unable to continue\n" ), ) @skip_unless_pacemaker_supports_rsc_and_op_rules() def test_success_rules_rsc_op(self): self.assert_effect( self.cli_command + ("-- set create id=X meta nam1=val1 " "rule resource ::Dummy and (op start or op stop) and " "(defined attr1 or attr2 gte number -1.2 or " "attr3 lt version 3.2.1 or attr4 ne string test or attr5 lt 3) " ).split(), f"""\ <{self.cib_tag}> <meta_attributes id="X"> <rule id="X-rule" boolean-op="and" score="INFINITY"> <rsc_expression id="X-rule-rsc-Dummy" type="Dummy"/> <rule id="X-rule-rule" boolean-op="or" score="0"> <op_expression id="X-rule-rule-op-start" name="start" /> <op_expression id="X-rule-rule-op-stop" name="stop" /> </rule> <rule id="X-rule-rule-1" boolean-op="or" score="0"> <expression id="X-rule-rule-1-expr" operation="defined" attribute="attr1" /> <expression id="X-rule-rule-1-expr-1" attribute="attr2" operation="gte" type="number" value="-1.2" /> <expression id="X-rule-rule-1-expr-2" attribute="attr3" operation="lt" type="version" value="3.2.1" /> <expression id="X-rule-rule-1-expr-3" attribute="attr4" operation="ne" type="string" value="test" /> <expression id="X-rule-rule-1-expr-4" attribute="attr5" operation="lt" value="3" /> </rule> </rule> <nvpair id="X-nam1" name="nam1" value="val1"/> </meta_attributes> </{self.cib_tag}> """, output=( "CIB has been upgraded to the latest schema version.\n" "Warning: Defaults do not apply to resources which override " "them with their own defined values\n"), )
class EnableDisable( TestCase, get_assert_pcs_effect_mixin(lambda cib: etree.tostring( # pylint:disable=undefined-variable etree.parse(cib).findall(".//resources")[0])), ): empty_cib = rc("cib-empty.xml") def setUp(self): self.temp_cib = get_tmp_file("tier1_cib_resource_enable_disable") write_file_to_tmpfile(self.empty_cib, self.temp_cib) self.pcs_runner = PcsRunner(self.temp_cib.name) def tearDown(self): self.temp_cib.close() def fixture_resource(self, name, disabled=False): cmd = [ "resource", "create", name, "ocf:heartbeat:Dummy", "--no-default-ops", ] if disabled: cmd.append("--disabled") self.assert_pcs_success(cmd) def fixture_tag(self, name, ids): self.assert_pcs_success(["tag", "create", name] + ids) def test_enable_none(self): self.assert_pcs_fail( "resource enable".split(), "Error: You must specify resource(s) to enable\n", ) def test_disable_none(self): self.assert_pcs_fail( "resource disable".split(), "Error: You must specify resource(s) to disable\n", ) def test_enable(self): self.fixture_resource("A", disabled=True) self.fixture_resource("B", disabled=True) self.fixture_tag("TA", ["A"]) self.assert_effect( "resource enable TA B".split(), """ <resources> <primitive class="ocf" id="A" provider="heartbeat" type="Dummy"> <meta_attributes id="A-meta_attributes"/> <operations> <op id="A-monitor-interval-10s" interval="10s" name="monitor" timeout="20s" /> </operations> </primitive> <primitive class="ocf" id="B" provider="heartbeat" type="Dummy"> <meta_attributes id="B-meta_attributes"/> <operations> <op id="B-monitor-interval-10s" interval="10s" name="monitor" timeout="20s" /> </operations> </primitive> </resources> """, ) def test_disable(self): self.fixture_resource("A", disabled=False) self.fixture_resource("B", disabled=False) self.fixture_tag("TA", ["A"]) self.assert_effect( "resource disable B TA".split(), """ <resources> <primitive class="ocf" id="A" provider="heartbeat" type="Dummy"> <meta_attributes id="A-meta_attributes"> <nvpair id="A-meta_attributes-target-role" name="target-role" value="Stopped" /> </meta_attributes> <operations> <op id="A-monitor-interval-10s" interval="10s" name="monitor" timeout="20s" /> </operations> </primitive> <primitive class="ocf" id="B" provider="heartbeat" type="Dummy"> <meta_attributes id="B-meta_attributes"> <nvpair id="B-meta_attributes-target-role" name="target-role" value="Stopped" /> </meta_attributes> <operations> <op id="B-monitor-interval-10s" interval="10s" name="monitor" timeout="20s" /> </operations> </primitive> </resources> """, ) def test_enable_nonexistent(self): self.fixture_resource("A", disabled=True) self.assert_pcs_fail( "resource enable A B".split(), ("Error: bundle/clone/group/resource/tag 'B' does not exist\n" "Error: Errors have occurred, therefore pcs is unable to continue\n" ), ) self.assert_resources_xml_in_cib(""" <resources> <primitive class="ocf" id="A" provider="heartbeat" type="Dummy"> <meta_attributes id="A-meta_attributes"> <nvpair id="A-meta_attributes-target-role" name="target-role" value="Stopped" /> </meta_attributes> <operations> <op id="A-monitor-interval-10s" interval="10s" name="monitor" timeout="20s" /> </operations> </primitive> </resources> """) def test_disable_nonexistent(self): self.fixture_resource("A", disabled=False) self.assert_pcs_fail( "resource disable A B".split(), ("Error: bundle/clone/group/resource/tag 'B' does not exist\n" "Error: Errors have occurred, therefore pcs is unable to continue\n" ), ) self.assert_resources_xml_in_cib(""" <resources> <primitive class="ocf" id="A" provider="heartbeat" type="Dummy"> <operations> <op id="A-monitor-interval-10s" interval="10s" name="monitor" timeout="20s" /> </operations> </primitive> </resources> """) def test_resource_disable_non_existing_with_failed_action(self): xml = """ <cib epoch="557" num_updates="122" admin_epoch="0" crm_feature_set="3.0.14" validate-with="pacemaker-2.10" update-origin="rh7-3" update-client="crmd" cib-last-written="Thu Aug 23 16:49:17 2012" have-quorum="0" dc-uuid="2"> <configuration> <crm_config/> <nodes> </nodes> <resources/> <constraints/> </configuration> <status> <node_state id="1" uname="rh7-1" in_ccm="true" crmd="online" crm-debug-origin="do_update_resource" join="member" expected="member"> <lrm id="1"> <lrm_resources> <lrm_resource id="A" type="apache" class="ocf" provider="heartbeat"> <lrm_rsc_op id="A_last_0" operation_key="A_monitor_0" operation="monitor" crm-debug-origin="do_update_resource" crm_feature_set="3.0.14" transition-key="1:1104:7:817ee250-d179-483a-819e-9be9cb0e06df" transition-magic="0:7;1:1104:7:817ee250-d179-483a-819e-9be9cb0e06df" exit-reason="" on_node="rh7-1" call-id="1079" rc-code="7" op-status="0" interval="0" last-run="1591791198" last-rc-change="1591791198" exec-time="275" queue-time="0" op-digest="f2317cad3d54cec5d7d7aa7d0bf35cf8"/> </lrm_resource> </lrm_resources> </lrm> </node_state> </status> </cib> """ write_data_to_tmpfile(xml, self.temp_cib) self.assert_pcs_fail( "resource disable A".split(), ("Error: bundle/clone/group/resource/tag 'A' does not exist\n" "Error: Errors have occurred, therefore pcs is unable to continue\n" ), )
class GroupDeleteRemoveUngroupBase( get_assert_pcs_effect_mixin(lambda cib: etree.tostring( # pylint:disable=undefined-variable etree.parse(cib).findall(".//resources")[0])), TestGroupMixin, ): command = None def assert_tags_xml(self, expected_xml): self.assert_resources_xml_in_cib( expected_xml, get_cib_part_func=lambda cib: etree.tostring( etree.parse(cib).findall(".//tags")[0], ), ) def assert_constraint_xml(self, expected_xml): self.assert_resources_xml_in_cib( expected_xml, get_cib_part_func=lambda cib: etree.tostring( etree.parse(cib).findall(".//constraints")[0], ), ) def test_nonexistent_group(self): self.assert_pcs_fail( ["resource"] + self.command + ["NonExistentGroup"], "Error: Group 'NonExistentGroup' does not exist\n", ) self.assert_resources_xml_in_cib( fixture_resources_xml([FIXTURE_AGROUP_XML]), ) self.assert_tags_xml(FIXTURE_TAGS_CONFIG_XML) self.assert_constraint_xml(FIXTURE_CONSTRAINTS_CONFIG_XML) def test_not_a_group_id(self): self.assert_pcs_fail( ["resource"] + self.command + ["A1"], "Error: Group 'A1' does not exist\n", ) self.assert_resources_xml_in_cib( fixture_resources_xml([FIXTURE_AGROUP_XML]), ) self.assert_tags_xml(FIXTURE_TAGS_CONFIG_XML) self.assert_constraint_xml(FIXTURE_CONSTRAINTS_CONFIG_XML) def test_whole_group(self): self.assert_effect( ["resource"] + self.command + ["AGroup"], fixture_resources_xml([ fixture_primitive_xml("A1"), fixture_primitive_xml("A2"), fixture_primitive_xml("A3"), ], ), output=( "Removing Constraint - location-TagGroupOnly-rh7-1-INFINITY\n" "Removing Constraint - location-AGroup-rh7-1-INFINITY\n"), ) self.assert_tags_xml(FIXTURE_TAGS_RESULT_XML) self.assert_constraint_xml("<constraints/>") def test_specified_resources(self): self.assert_effect( ["resource"] + self.command + ["AGroup", "A1", "A3"], fixture_resources_xml([ fixture_group_xml( "AGroup", [ fixture_primitive_xml("A2"), ], ), fixture_primitive_xml("A1"), fixture_primitive_xml("A3"), ], ), ) self.assert_tags_xml(FIXTURE_TAGS_CONFIG_XML) self.assert_constraint_xml(FIXTURE_CONSTRAINTS_CONFIG_XML) def test_all_resources(self): self.assert_effect( ["resource"] + self.command + ["AGroup", "A1", "A2", "A3"], fixture_resources_xml([ fixture_primitive_xml("A1"), fixture_primitive_xml("A2"), fixture_primitive_xml("A3"), ], ), output=( "Removing Constraint - location-TagGroupOnly-rh7-1-INFINITY\n" "Removing Constraint - location-AGroup-rh7-1-INFINITY\n"), ) self.assert_tags_xml(FIXTURE_TAGS_RESULT_XML) self.assert_constraint_xml("<constraints/>") def test_cloned_group(self): self.assert_pcs_success("resource clone AGroup".split()) self.assert_pcs_fail( ["resource"] + self.command + ["AGroup"], "Error: Cannot remove all resources from a cloned group\n", ) self.assert_resources_xml_in_cib( fixture_resources_xml( [fixture_clone_xml("AGroup-clone", FIXTURE_AGROUP_XML)], )) self.assert_tags_xml(FIXTURE_TAGS_CONFIG_XML) self.assert_constraint_xml(FIXTURE_CLONE_TAG_CONSTRAINTS) def test_cloned_group_all_resorces_specified(self): self.assert_pcs_success("resource clone AGroup".split()) self.assert_pcs_fail( ["resource"] + self.command + ["AGroup", "A1", "A2", "A3"], "Error: Cannot remove all resources from a cloned group\n", ) self.assert_resources_xml_in_cib( fixture_resources_xml( [fixture_clone_xml("AGroup-clone", FIXTURE_AGROUP_XML)], )) self.assert_tags_xml(FIXTURE_TAGS_CONFIG_XML) self.assert_constraint_xml(FIXTURE_CLONE_TAG_CONSTRAINTS) def test_cloned_group_with_one_resource(self): self.assert_pcs_success("resource clone AGroup".split()) self.assert_pcs_success("resource ungroup AGroup A1 A2".split()) self.assert_effect( ["resource"] + self.command + ["AGroup"], fixture_resources_xml([ fixture_clone_xml( "AGroup-clone", fixture_primitive_xml("A3"), ), fixture_primitive_xml("A1"), fixture_primitive_xml("A2"), ], ), output= "Removing Constraint - location-TagGroupOnly-rh7-1-INFINITY\n", ) self.assert_tags_xml(FIXTURE_TAGS_RESULT_XML) self.assert_constraint_xml(FIXTURE_CLONE_CONSTRAINT)
class Unclone( TestCase, get_assert_pcs_effect_mixin(lambda cib: etree.tostring( # pylint:disable=undefined-variable etree.parse(cib).findall(".//resources")[0])), ): empty_cib = rc("cib-empty.xml") def setUp(self): # pylint: disable=invalid-name self.temp_cib = get_tmp_file("tier1_cib_resource_group_ungroup") self.pcs_runner = PcsRunner(self.temp_cib.name) xml_manip = XmlManipulation.from_file(self.empty_cib) xml_manip.append_to_first_tag_name( "resources", FIXTURE_CLONE, FIXTURE_DUMMY, ) xml_manip.append_to_first_tag_name( "configuration", """ <tags> <tag id="T1"> <obj_ref id="C-clone"/> <obj_ref id="Dummy"/> </tag> <tag id="T2"> <obj_ref id="C-clone"/> </tag> </tags> """, ) xml_manip.append_to_first_tag_name( "constraints", """ <rsc_location id="location-C-clone-rh7-1-INFINITY" node="rh7-1" rsc="C-clone" score="INFINITY"/> """, """ <rsc_location id="location-T1-rh7-1-INFINITY" node="rh7-1" rsc="T1" score="INFINITY"/> """, ) write_data_to_tmpfile(str(xml_manip), self.temp_cib) def tearDown(self): # pylint: disable=invalid-name self.temp_cib.close() def test_nonexistent_clone(self): self.assert_pcs_fail( "resource unclone NonExistentClone", "Error: could not find resource: NonExistentClone\n", ) self.assert_resources_xml_in_cib(FIXTURE_CLONE_AND_RESOURCE) def test_not_clone_resource(self): self.assert_pcs_fail( "resource unclone Dummy", "Error: 'Dummy' is not a clone resource\n", ) self.assert_resources_xml_in_cib(FIXTURE_CLONE_AND_RESOURCE) def test_unclone_clone_id(self): self.assert_effect("resource unclone C-clone", FIXTURE_RESOURCES) def test_unclone_resoruce_id(self): self.assert_effect("resource unclone C", FIXTURE_RESOURCES)
class Move( TestCase, get_assert_pcs_effect_mixin(lambda cib: etree.tostring( # pylint:disable=undefined-variable etree.parse(cib).findall(".//resources")[0])), ): empty_cib = rc("cib-empty.xml") def setUp(self): # pylint: disable=invalid-name self.temp_cib = get_tmp_file("tier1_cib_resource_move") self.pcs_runner = PcsRunner(self.temp_cib.name) xml_manip = XmlManipulation.from_file(self.empty_cib) xml_manip.append_to_first_tag_name("resources", fixture_primitive) xml_manip.append_to_first_tag_name( "constraints", *fixture_constraints, ) xml_manip.append_to_first_tag_name( "nodes", *fixture_nodes, ) write_data_to_tmpfile(str(xml_manip), self.temp_cib) def tearDown(self): # pylint: disable=invalid-name self.temp_cib.close() def test_move_to_node_with_existing_ban_constraints(self): self.assert_effect( "resource move-with-constraint A node1".split(), f"<resources>{fixture_primitive}</resources>", output=( "Warning: A move constraint has been created and the resource " "'A' may or may not move depending on other configuration\n"), ) self.assert_resources_xml_in_cib( """ <constraints> <rsc_location id="location-A-node1--INFINITY" rsc="A" node="node1" score="-INFINITY" /> <rsc_location id="cli-prefer-A" rsc="A" role="Started" node="node1" score="INFINITY" /> </constraints> """, get_cib_part_func=lambda cib: etree.tostring( etree.parse(cib).findall(".//constraints")[0]), ) def test_move_stopped_with_existing_constraints(self): self.assert_pcs_fail( "resource move-with-constraint A".split(), ("Error: You must specify a node when moving/banning a stopped " "resource\n"), ) def test_nonexistent_resource(self): self.assert_pcs_fail( "resource move-with-constraint NonExistent".split(), ("Error: bundle/clone/group/resource 'NonExistent' does not " "exist\n" "Error: Errors have occurred, therefore pcs is unable to " "continue\n"), ) def test_wait_not_supported_with_file(self): self.assert_pcs_fail( "resource move-with-constraint A --wait".split(), "Error: Cannot use '-f' together with '--wait'\n", ) def test_move_autoclean_not_supported_with_file(self): self.assert_pcs_fail( "resource move A".split(), "Error: Specified option '-f' is not supported in this command\n", )
class NodeUtilizationSet( TestCase, get_assert_pcs_effect_mixin(lambda cib: etree.tostring( # pylint:disable=undefined-variable etree.parse(cib).findall(".//nodes")[0])), ): def setUp(self): self.temp_cib = get_tmp_file("tier1_node_utilization_set") write_file_to_tmpfile(empty_cib, self.temp_cib) self.pcs_runner = PcsRunner(self.temp_cib.name) def tearDown(self): self.temp_cib.close() @staticmethod def fixture_xml_no_utilization(): # must match empty_cib return """ <nodes> <node id="1" uname="rh7-1" /> <node id="2" uname="rh7-2" /> </nodes> """ @staticmethod def fixture_xml_empty_utilization(): # must match empty_cib return """ <nodes> <node id="1" uname="rh7-1"> <utilization id="nodes-1-utilization" /> </node> <node id="2" uname="rh7-2" /> </nodes> """ @staticmethod def fixture_xml_with_utilization(): # must match empty_cib return """ <nodes> <node id="1" uname="rh7-1"> <utilization id="nodes-1-utilization"> <nvpair id="nodes-1-utilization-test" name="test" value="100" /> </utilization> </node> <node id="2" uname="rh7-2" /> </nodes> """ def test_node_utilization_set(self): output, returnVal = pcs(self.temp_cib.name, "node utilization rh7-1 test1=10".split()) ac("", output) self.assertEqual(0, returnVal) output, returnVal = pcs(self.temp_cib.name, "node utilization rh7-2".split()) expected_out = """\ Node Utilization: """ ac(expected_out, output) self.assertEqual(0, returnVal) output, returnVal = pcs(self.temp_cib.name, "node utilization rh7-1".split()) expected_out = """\ Node Utilization: rh7-1: test1=10 """ ac(expected_out, output) self.assertEqual(0, returnVal) output, returnVal = pcs( self.temp_cib.name, "node utilization rh7-1 test1=-10 test4=1234".split(), ) ac("", output) self.assertEqual(0, returnVal) output, returnVal = pcs(self.temp_cib.name, "node utilization rh7-1".split()) expected_out = """\ Node Utilization: rh7-1: test1=-10 test4=1234 """ ac(expected_out, output) self.assertEqual(0, returnVal) output, returnVal = pcs( self.temp_cib.name, "node utilization rh7-2 test2=321 empty=".split(), ) ac("", output) self.assertEqual(0, returnVal) output, returnVal = pcs(self.temp_cib.name, "node utilization rh7-2".split()) expected_out = """\ Node Utilization: rh7-2: test2=321 """ ac(expected_out, output) self.assertEqual(0, returnVal) output, returnVal = pcs(self.temp_cib.name, "node utilization".split()) expected_out = """\ Node Utilization: rh7-1: test1=-10 test4=1234 rh7-2: test2=321 """ ac(expected_out, output) self.assertEqual(0, returnVal) output, returnVal = pcs(self.temp_cib.name, "node utilization rh7-2 test1=-20".split()) ac("", output) self.assertEqual(0, returnVal) output, returnVal = pcs(self.temp_cib.name, "node utilization --name test1".split()) expected_out = """\ Node Utilization: rh7-1: test1=-10 rh7-2: test1=-20 """ ac(expected_out, output) self.assertEqual(0, returnVal) output, returnVal = pcs( self.temp_cib.name, "node utilization --name test1 rh7-2".split(), ) expected_out = """\ Node Utilization: rh7-2: test1=-20 """ ac(expected_out, output) self.assertEqual(0, returnVal) def test_refuse_non_option_attribute_parameter_among_options(self): self.assert_pcs_fail( "node utilization rh7-1 net".split(), "Error: missing value of 'net' option\n", ) def test_refuse_option_without_key(self): self.assert_pcs_fail( "node utilization rh7-1 =1".split(), "Error: missing key in '=1' option\n", ) def test_refuse_unknown_node(self): self.assert_pcs_fail( "node utilization rh7-0 test=10".split(), "Error: Unable to find a node: rh7-0\n", ) def test_refuse_value_not_int(self): self.assert_pcs_fail( "node utilization rh7-1 test1=10 test=int".split(), "Error: Value of utilization attribute must be integer: " "'test=int'\n", ) def test_keep_empty_nvset(self): self.assert_effect( "node utilization rh7-1 test=100".split(), self.fixture_xml_with_utilization(), ) self.assert_effect( "node utilization rh7-1 test=".split(), self.fixture_xml_empty_utilization(), ) def test_dont_create_nvset_on_removal(self): self.assert_effect( "node utilization rh7-1 test=".split(), self.fixture_xml_no_utilization(), )
class ManageUnmanage( TestCase, get_assert_pcs_effect_mixin(lambda cib: etree.tostring( # pylint:disable=undefined-variable etree.parse(cib).findall(".//resources")[0]))): empty_cib = rc("cib-empty.xml") temp_cib = rc("temp-cib.xml") @staticmethod def fixture_cib_unmanaged_a(add_empty_meta_b=False): empty_meta_b = '<meta_attributes id="B-meta_attributes" />' return """ <resources> <primitive class="ocf" id="A" provider="heartbeat" type="Dummy"> <meta_attributes id="A-meta_attributes"> <nvpair id="A-meta_attributes-is-managed" name="is-managed" value="false" /> </meta_attributes> <operations> <op id="A-monitor-interval-10s" interval="10s" name="monitor" timeout="20s" /> </operations> </primitive> <primitive class="ocf" id="B" provider="heartbeat" type="Dummy"> {empty_meta_b}<operations> <op id="B-monitor-interval-10s" interval="10s" name="monitor" timeout="20s" /> </operations> </primitive> </resources> """.format(empty_meta_b=(empty_meta_b if add_empty_meta_b else "")) def setUp(self): shutil.copy(self.empty_cib, self.temp_cib) self.pcs_runner = PcsRunner(self.temp_cib) def fixture_resource(self, name, managed=True, with_monitors=False): self.assert_pcs_success( "resource create {0} ocf:heartbeat:Dummy --no-default-ops".format( name)) if not managed: self.assert_pcs_success("resource unmanage {0} {1}".format( name, "--monitor" if with_monitors else "")) def test_unmanage_none(self): self.assert_pcs_fail( "resource unmanage", "Error: You must specify resource(s) to unmanage\n") def test_manage_none(self): self.assert_pcs_fail( "resource manage", "Error: You must specify resource(s) to manage\n") def test_unmanage_one(self): self.fixture_resource("A") self.fixture_resource("B") self.assert_effect("resource unmanage A", self.fixture_cib_unmanaged_a()) def test_manage_one(self): self.fixture_resource("A", managed=False) self.fixture_resource("B", managed=False) self.assert_effect("resource manage B", self.fixture_cib_unmanaged_a(add_empty_meta_b=True)) def test_unmanage_monitor(self): self.fixture_resource("A") self.assert_effect( "resource unmanage A --monitor", """ <resources> <primitive class="ocf" id="A" provider="heartbeat" type="Dummy"> <meta_attributes id="A-meta_attributes"> <nvpair id="A-meta_attributes-is-managed" name="is-managed" value="false" /> </meta_attributes> <operations> <op id="A-monitor-interval-10s" interval="10s" name="monitor" timeout="20s" enabled="false" /> </operations> </primitive> </resources> """) def test_unmanage_monitor_enabled(self): self.fixture_resource("A") self.assert_effect( "resource unmanage A", """ <resources> <primitive class="ocf" id="A" provider="heartbeat" type="Dummy"> <meta_attributes id="A-meta_attributes"> <nvpair id="A-meta_attributes-is-managed" name="is-managed" value="false" /> </meta_attributes> <operations> <op id="A-monitor-interval-10s" interval="10s" name="monitor" timeout="20s" /> </operations> </primitive> </resources> """) def test_manage_monitor(self): self.fixture_resource("A", managed=True, with_monitors=True) self.assert_effect( "resource manage A --monitor", """ <resources> <primitive class="ocf" id="A" provider="heartbeat" type="Dummy"> <operations> <op id="A-monitor-interval-10s" interval="10s" name="monitor" timeout="20s" /> </operations> </primitive> </resources> """) def test_manage_monitor_disabled(self): self.fixture_resource("A", managed=False, with_monitors=True) self.assert_effect( "resource manage A", """ <resources> <primitive class="ocf" id="A" provider="heartbeat" type="Dummy"> <meta_attributes id="A-meta_attributes" /> <operations> <op id="A-monitor-interval-10s" interval="10s" name="monitor" timeout="20s" enabled="false" /> </operations> </primitive> </resources> """, "Warning: Resource 'A' has no enabled monitor operations." " Re-run with '--monitor' to enable them.\n") def test_unmanage_more(self): self.fixture_resource("A") self.fixture_resource("B") self.assert_effect( "resource unmanage A B", """ <resources> <primitive class="ocf" id="A" provider="heartbeat" type="Dummy"> <meta_attributes id="A-meta_attributes"> <nvpair id="A-meta_attributes-is-managed" name="is-managed" value="false" /> </meta_attributes> <operations> <op id="A-monitor-interval-10s" interval="10s" name="monitor" timeout="20s" /> </operations> </primitive> <primitive class="ocf" id="B" provider="heartbeat" type="Dummy"> <meta_attributes id="B-meta_attributes"> <nvpair id="B-meta_attributes-is-managed" name="is-managed" value="false" /> </meta_attributes> <operations> <op id="B-monitor-interval-10s" interval="10s" name="monitor" timeout="20s" /> </operations> </primitive> </resources> """) def test_manage_more(self): self.fixture_resource("A", managed=False) self.fixture_resource("B", managed=False) self.assert_effect( "resource manage A B", """ <resources> <primitive class="ocf" id="A" provider="heartbeat" type="Dummy"> <meta_attributes id="A-meta_attributes" /> <operations> <op id="A-monitor-interval-10s" interval="10s" name="monitor" timeout="20s" /> </operations> </primitive> <primitive class="ocf" id="B" provider="heartbeat" type="Dummy"> <meta_attributes id="B-meta_attributes" /> <operations> <op id="B-monitor-interval-10s" interval="10s" name="monitor" timeout="20s" /> </operations> </primitive> </resources> """) def test_unmanage_nonexistent(self): self.fixture_resource("A") self.assert_pcs_fail( "resource unmanage A B", "Error: bundle/clone/group/resource 'B' does not exist\n") self.assert_resources_xml_in_cib(""" <resources> <primitive class="ocf" id="A" provider="heartbeat" type="Dummy"> <operations> <op id="A-monitor-interval-10s" interval="10s" name="monitor" timeout="20s" /> </operations> </primitive> </resources> """) def test_manage_nonexistent(self): self.fixture_resource("A", managed=False) self.assert_pcs_fail( "resource manage A B", "Error: bundle/clone/group/resource 'B' does not exist\n") self.assert_resources_xml_in_cib(""" <resources> <primitive class="ocf" id="A" provider="heartbeat" type="Dummy"> <meta_attributes id="A-meta_attributes"> <nvpair id="A-meta_attributes-is-managed" name="is-managed" value="false" /> </meta_attributes> <operations> <op id="A-monitor-interval-10s" interval="10s" name="monitor" timeout="20s" /> </operations> </primitive> </resources> """)