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 assert_effect( self, alternative_cmds, expected_xml, output=None, output_start=None, output_regexp=None, ): alternative_list = (alternative_cmds if isinstance( alternative_cmds[0], list) else [alternative_cmds]) self.temp_cib.seek(0) cib_content = self.temp_cib.read() self.temp_cib.seek(0) for alternative in alternative_list[:-1]: self.assert_effect_single( alternative, expected_xml, output, output_start, output_regexp, ) write_data_to_tmpfile(cib_content, self.temp_cib) self.assert_effect_single( alternative_list[-1], expected_xml, output, output_start, output_regexp, )
def test_validation_errors(self): write_data_to_tmpfile(fixture_corosync_conf_minimal(), self.corosync_conf_file) self.assert_pcs_fail( ("cluster config update " "transport ip_version=ipvx link_mode=passive " "compression level=2 model=zlib threshold=NaN " "crypto hash= model=openssl " "totem consensus=0 down_check=1 token=12").split(), stdout_full= ("Error: invalid totem option 'down_check', allowed options " "are: 'block_unlisted_ips', 'consensus', 'downcheck', 'fail_recv_const', " "'heartbeat_failures_allowed', 'hold', 'join', 'max_messages', " "'max_network_delay', 'merge', 'miss_count_const', " "'send_join', 'seqno_unchanged_const', 'token', " "'token_coefficient', 'token_retransmit', " "'token_retransmits_before_loss_const', 'window_size'\n" "Error: 'ipvx' is not a valid ip_version value, use 'ipv4', " "'ipv4-6', 'ipv6', 'ipv6-4'\n" "Error: 'NaN' is not a valid threshold value, use a " "non-negative integer\n" "Error: If crypto option 'cipher' is enabled, crypto option " "'hash' must be enabled as well\n" "Error: Errors have occurred, therefore pcs is unable to " "continue\n"), ) self.assertEqual( self.corosync_conf_file.read(), fixture_corosync_conf_minimal(), )
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 test_success_rules_rsc_op(self): xml = """ <op_defaults> <meta_attributes id="X"> <rule id="X-rule" boolean-op="and" score="INFINITY"> <rsc_expression id="X-rule-rsc-Dummy" type="Dummy"/> <op_expression id="X-rule-op-monitor" name="monitor"/> </rule> <nvpair id="X-nam1" name="nam1" value="val1"/> </meta_attributes> </op_defaults> """ xml_manip = XmlManipulation.from_file(empty_cib_rules) xml_manip.append_to_first_tag_name("configuration", xml) write_data_to_tmpfile(str(xml_manip), self.temp_cib) self.assert_pcs_success( self.cli_command, stdout_full=dedent("""\ Meta Attrs: X nam1=val1 Rule: boolean-op=and score=INFINITY Expression: resource ::Dummy Expression: op monitor """), )
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" ), )
def test_uuid_not_present(self): write_data_to_tmpfile( fixture_corosync_conf_minimal(no_cluster_uuid=True), self.corosync_conf_file, ) self.assert_pcs_success(["cluster", "config", "uuid", "generate"]) corosync_json_after, _ = self.pcs_runner.run( ["cluster", "config", "show", "--output-format=json"]) self.assertIn("cluster_uuid", json.loads(corosync_json_after))
def setUp(self): self.corosync_conf_file = get_tmp_file( "tier1_cluster_config_show_corosync.conf") self.pcs_runner = PcsRunner( cib_file=None, corosync_conf_opt=self.corosync_conf_file.name, ) write_data_to_tmpfile(fixture_corosync_conf_minimal(), self.corosync_conf_file)
def test_multiple_options(self): write_data_to_tmpfile(fixture_corosync_conf_minimal(), self.corosync_conf_file) self.assert_pcs_success( ("cluster config update " "transport ip_version= link_mode=passive " "compression level=2 model=zlib threshold=10 " "crypto cipher=aes256 hash=sha512 model=openssl " "totem consensus=0 downcheck=1 token=12").split()) self.assertEqual( self.corosync_conf_file.read(), dedent("""\ totem { version: 2 cluster_name: cluster_name cluster_uuid: cluster_uuid transport: knet crypto_cipher: aes256 crypto_hash: sha512 consensus: 0 downcheck: 1 token: 12 link_mode: passive knet_compression_level: 2 knet_compression_model: zlib knet_compression_threshold: 10 crypto_model: openssl } nodelist { node { ring0_addr: node1_addr name: node1 nodeid: 1 } node { ring0_addr: node2_addr name: node2 nodeid: 2 } } quorum { provider: corosync_votequorum two_node: 1 } logging { to_logfile: yes logfile: /var/log/cluster/corosync.log to_syslog: yes timestamp: on } """), )
def setUp(self): self.temp_cib = get_tmp_file("tier1_cluster_cib_push") self.pcs_runner = PcsRunner(self.temp_cib.name) write_data_to_tmpfile(CIB_EPOCH, self.temp_cib) self.updated_cib = get_tmp_file("tier1_cluster_cib_push_updated") write_data_to_tmpfile("<cib/>", self.updated_cib) self.cib_push_cmd = f"cluster cib-push {self.updated_cib.name}".split() self.cib_push_diff_cmd = self.cib_push_cmd + [ f"diff-against={self.temp_cib.name}" ]
def test_file_parse_error(self): write_data_to_tmpfile("this is not\na valid corosync.conf file\n", self.corosync_conf_file) self.assert_pcs_fail( "cluster config update transport ip_version=ipv4 totem token=12". split(), stdout_full= ("Error: Unable to parse corosync config: a line is not opening " "or closing a section or key: value\n" "Error: Errors have occurred, therefore pcs is unable to continue\n" ), )
def test_uuid_present(self): write_data_to_tmpfile( fixture_corosync_conf_minimal(), self.corosync_conf_file, ) self.assert_pcs_fail( ["cluster", "config", "uuid", "generate"], stdout_full="Error: Cluster UUID has already been set, use --force " "to override\n" "Error: Errors have occurred, therefore pcs is unable " "to continue\n", )
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 test_minimal(self): write_data_to_tmpfile( fixture_corosync_conf_minimal(no_cluster_uuid=True), self.corosync_conf_file, ) self.assert_pcs_success( "cluster config update transport ip_version=ipv4 totem token=12". split()) self.assertEqual( self.corosync_conf_file.read(), dedent("""\ totem { version: 2 cluster_name: cluster_name transport: knet ip_version: ipv4 crypto_cipher: aes256 crypto_hash: sha256 token: 12 } nodelist { node { ring0_addr: node1_addr name: node1 nodeid: 1 } node { ring0_addr: node2_addr name: node2 nodeid: 2 } } quorum { provider: corosync_votequorum two_node: 1 } logging { to_logfile: yes logfile: /var/log/cluster/corosync.log to_syslog: yes timestamp: on } """), )
def test_uuid_present_with_force(self): write_data_to_tmpfile( fixture_corosync_conf_minimal(), self.corosync_conf_file, ) corosync_json_before, _ = self.pcs_runner.run( ["cluster", "config", "show", "--output-format=json"]) self.assert_pcs_success( ["cluster", "config", "uuid", "generate", "--force"], stdout_full="Warning: Cluster UUID has already been set\n", ) corosync_json_after, _ = self.pcs_runner.run( ["cluster", "config", "show", "--output-format=json"]) self.assertNotEqual( json.loads(corosync_json_before)["cluster_uuid"], json.loads(corosync_json_after)["cluster_uuid"], )
def test_success_rule_expired(self): xml_rsc = self.xml_expired_template.format(tag="rsc") xml_op = self.xml_expired_template.format(tag="op") xml_manip = XmlManipulation.from_file(empty_cib) xml_manip.append_to_first_tag_name("configuration", xml_rsc, xml_op) write_data_to_tmpfile(str(xml_manip), self.temp_cib) self.assert_pcs_success( self.cli_command, stdout_full=dedent(f"""\ Meta Attrs (not yet in effect): {self.prefix}-set1 name1=value1 Rule (not yet in effect): boolean-op=and score=INFINITY Expression: date gt 3000-01-01 Meta Attrs: {self.prefix}-set3 name3=value3 Rule: boolean-op=and score=INFINITY Expression: date in_range 1000-01-01 to 3000-01-01 """), )
def setUp(self): super().setUp() xml_rsc = """ <rsc_defaults> <meta_attributes id="rsc-set1" /> <meta_attributes id="rsc-set2" /> <meta_attributes id="rsc-set3" /> <meta_attributes id="rsc-set4" /> </rsc_defaults> """ xml_op = """ <op_defaults> <meta_attributes id="op-set1" /> <meta_attributes id="op-set2" /> <meta_attributes id="op-set3" /> <meta_attributes id="op-set4" /> </op_defaults> """ xml_manip = XmlManipulation.from_file(empty_cib) xml_manip.append_to_first_tag_name("configuration", xml_rsc, xml_op) write_data_to_tmpfile(str(xml_manip), self.temp_cib)
def test_success(self): xml = f""" <{self.cib_tag}> <meta_attributes id="my-set"> <nvpair id="my-set-name1" name="name1" value="value1" /> <nvpair id="my-set-name2" name="name2" value="value2" /> <nvpair id="my-set-name3" name="name3" value="value3" /> </meta_attributes> </{self.cib_tag}> """ xml_manip = XmlManipulation.from_file(empty_cib) xml_manip.append_to_first_tag_name("configuration", xml) write_data_to_tmpfile(str(xml_manip), self.temp_cib) warnings = ( "Warning: Defaults do not apply to resources which override " "them with their own defined values\n") self.assert_effect( self.cli_command + "set update my-set meta name2=value2A name3=".split(), dedent(f"""\ <{self.cib_tag}> <meta_attributes id="my-set"> <nvpair id="my-set-name1" name="name1" value="value1" /> <nvpair id="my-set-name2" name="name2" value="value2A" /> </meta_attributes> </{self.cib_tag}> """), output=warnings, ) self.assert_effect( self.cli_command + "set update my-set meta name1= name2=".split(), dedent(f"""\ <{self.cib_tag}> <meta_attributes id="my-set" /> </{self.cib_tag}> """), output=warnings, )
def test_success(self): xml_rsc = """ <rsc_defaults> <meta_attributes id="rsc-set1" score="10"> <nvpair id="rsc-set1-nv1" name="name1" value="rsc1"/> <nvpair id="rsc-set1-nv2" name="name2" value="rsc2"/> </meta_attributes> <meta_attributes id="rsc-setA"> <nvpair id="rsc-setA-nv1" name="name1" value="rscA"/> <nvpair id="rsc-setA-nv2" name="name2" value="rscB"/> </meta_attributes> </rsc_defaults> """ xml_op = """ <op_defaults> <meta_attributes id="op-set1" score="10"> <nvpair id="op-set1-nv1" name="name1" value="op1"/> <nvpair id="op-set1-nv2" name="name2" value="op2"/> </meta_attributes> <meta_attributes id="op-setA"> <nvpair id="op-setA-nv1" name="name1" value="opA"/> <nvpair id="op-setA-nv2" name="name2" value="opB"/> </meta_attributes> </op_defaults> """ xml_manip = XmlManipulation.from_file(empty_cib) xml_manip.append_to_first_tag_name("configuration", xml_rsc, xml_op) write_data_to_tmpfile(str(xml_manip), self.temp_cib) self.assert_pcs_success( self.cli_command, stdout_full=dedent(f"""\ Meta Attrs: {self.prefix}-set1 score=10 name1={self.prefix}1 name2={self.prefix}2 Meta Attrs: {self.prefix}-setA name1={self.prefix}A name2={self.prefix}B """), )
def fixture_multiple_remote_nodes(self): # bypass pcs validation mechanisms (including expected future # validation) write_data_to_tmpfile( """ <cib epoch="557" num_updates="122" admin_epoch="0" validate-with="pacemaker-1.2" crm_feature_set="3.0.6" 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> <primitive class="ocf" id="NODE-NAME" provider="pacemaker" type="remote" > <instance_attributes id="ia1"> <nvpair id="nvp1" name="server" value="HOST-A"/> </instance_attributes> </primitive> <primitive class="ocf" id="HOST-A" provider="pacemaker" type="remote" > <instance_attributes id="ia2"> <nvpair id="nvp2" name="server" value="HOST-B"/> </instance_attributes> </primitive> </resources> <constraints/> </configuration> <status/> </cib> """, self.temp_cib, )
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_success_rule(self): xml_template = """<{tag}_defaults> <meta_attributes id="{tag}-set1"> <rule id="{tag}-set1-rule" boolean-op="and" score="INFINITY"> <rule id="{tag}-set1-rule-rule" boolean-op="or" score="0"> <expression id="{tag}-set1-rule-rule-expr" operation="defined" attribute="attr1" /> <expression id="{tag}-set1-rule-rule-expr-1" attribute="attr2" operation="gte" type="number" value="12" /> <expression id="{tag}-set1-rule-rule-expr-2" attribute="attr3" operation="lt" type="version" value="3.2.1" /> <expression id="{tag}-set1-rule-rule-expr-3" attribute="attr4" operation="ne" type="string" value="test" /> <expression id="{tag}-set1-rule-rule-expr-4" attribute="attr5" operation="lt" value="3" /> </rule> <rule id="{tag}-set1-rule-rule-1" boolean-op="or" score="0"> <date_expression id="{tag}-set1-rule-rule-1-expr" operation="gt" start="2018-05-17T13:28:19" /> <date_expression id="{tag}-set1-rule-rule-1-expr-1" operation="in_range" start="2019-01-01" end="2019-03-15" /> <date_expression id="{tag}-set1-rule-rule-1-expr-2" operation="in_range" start="2019-05-01" > <duration id="{tag}-set1-rule-rule-1-expr-2-duration" months="2" /> </date_expression> <date_expression id="{tag}-set1-rule-rule-1-expr-3" operation="date_spec" > <date_spec id="{tag}-set1-rule-rule-1-expr-3-datespec" months="7-8" weekdays="6-7" years="2019" /> </date_expression> <date_expression id="{tag}-set1-rule-rule-1-expr-4" operation="in_range" end="2019-12-15" /> </rule> </rule> <nvpair id="{tag}-set1-nam1" name="nam1" value="val1"/> <nvpair id="{tag}-set1-nam2" name="nam2" value="val2"/> </meta_attributes> </{tag}_defaults>""" xml_rsc = xml_template.format(tag="rsc") xml_op = xml_template.format(tag="op") xml_manip = XmlManipulation.from_file(empty_cib) xml_manip.append_to_first_tag_name("configuration", xml_rsc, xml_op) write_data_to_tmpfile(str(xml_manip), self.temp_cib) self.assert_pcs_success( self.cli_command, stdout_full=dedent(f"""\ Meta Attrs: {self.prefix}-set1 nam1=val1 nam2=val2 Rule: boolean-op=and score=INFINITY Rule: boolean-op=or score=0 Expression: defined attr1 Expression: attr2 gte number 12 Expression: attr3 lt version 3.2.1 Expression: attr4 ne string test Expression: attr5 lt 3 Rule: boolean-op=or score=0 Expression: date gt 2018-05-17T13:28:19 Expression: date in_range 2019-01-01 to 2019-03-15 Expression: date in_range 2019-05-01 to duration Duration: months=2 Expression: Date Spec: months=7-8 weekdays=6-7 years=2019 Expression: date in_range to 2019-12-15 """), )
def test_unable_to_diff_invalid_crm_feature_set(self): write_data_to_tmpfile("<cib crm_feature_set='invalid'/>", self.temp_cib) self.assert_unable_to_diff( "the attribute 'crm_feature_set' of the element 'cib' has an " "invalid value: 'invalid'" )
def test_unable_to_parse_new_cib(self): write_data_to_tmpfile("", self.updated_cib) self.assert_pcs_fail(self.cib_push_cmd, stdout_start="Error: unable to parse new cib:")
def test_unable_to_diff_old_crm_feature_set_version(self): write_data_to_tmpfile("<cib crm_feature_set='3.0.8'/>", self.temp_cib) self.assert_unable_to_diff( "the 'crm_feature_set' version is '3.0.8' but at least version " "'3.0.9' is required" )
def test_diff_no_difference(self): write_data_to_tmpfile(CIB_EPOCH, self.updated_cib) self.assert_pcs_success( self.cib_push_diff_cmd, "The new CIB is the same as the original CIB, nothing to push.\n", )
def test_cib_updated(self): write_data_to_tmpfile(CIB_EPOCH_NEWER, self.updated_cib) self.assert_pcs_success(self.cib_push_cmd, "CIB updated\n")
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 test_unable_to_diff_missing_crm_feature_set(self): write_data_to_tmpfile("<cib/>", self.temp_cib) self.assert_unable_to_diff( "the 'cib' element is missing 'crm_feature_set' value" )
def test_cib_too_old(self): write_data_to_tmpfile(CIB_EPOCH_OLDER, self.updated_cib) self.assert_pcs_fail(self.cib_push_cmd, ERROR_CIB_OLD)