Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package crmsh for openSUSE:Factory checked in at 2021-12-16 02:00:55 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/crmsh (Old) and /work/SRC/openSUSE:Factory/.crmsh.new.2520 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "crmsh" Thu Dec 16 02:00:55 2021 rev:231 rq:940756 version:4.3.1+20211215.85834218 Changes: -------- --- /work/SRC/openSUSE:Factory/crmsh/crmsh.changes 2021-12-10 21:52:55.050906693 +0100 +++ /work/SRC/openSUSE:Factory/.crmsh.new.2520/crmsh.changes 2021-12-16 02:01:28.083652347 +0100 @@ -1,0 +2,7 @@ +Wed Dec 15 15:09:05 UTC 2021 - xli...@suse.com + +- Update to version 4.3.1+20211215.85834218: + * Dev: unittest: Adjust unit test based on previous changes + * Dev: xmlutil: Add class CrmMonXmlParser to parse xml output of crm_mon + +------------------------------------------------------------------- Old: ---- crmsh-4.3.1+20211210.a149de51.tar.bz2 New: ---- crmsh-4.3.1+20211215.85834218.tar.bz2 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ crmsh.spec ++++++ --- /var/tmp/diff_new_pack.aF3jY1/_old 2021-12-16 02:01:28.815652791 +0100 +++ /var/tmp/diff_new_pack.aF3jY1/_new 2021-12-16 02:01:28.831652801 +0100 @@ -36,7 +36,7 @@ Summary: High Availability cluster command-line interface License: GPL-2.0-or-later Group: %{pkg_group} -Version: 4.3.1+20211210.a149de51 +Version: 4.3.1+20211215.85834218 Release: 0 URL: http://crmsh.github.io Source0: %{name}-%{version}.tar.bz2 ++++++ _servicedata ++++++ --- /var/tmp/diff_new_pack.aF3jY1/_old 2021-12-16 02:01:28.903652844 +0100 +++ /var/tmp/diff_new_pack.aF3jY1/_new 2021-12-16 02:01:28.903652844 +0100 @@ -9,7 +9,7 @@ </service> <service name="tar_scm"> <param name="url">https://github.com/ClusterLabs/crmsh.git</param> - <param name="changesrevision">9cdc6cb5447970fa20263d9edcc1234567a55303</param> + <param name="changesrevision">99c28d735cde5888794c4ef218a527c3e65ff118</param> </service> </servicedata> (No newline at EOF) ++++++ crmsh-4.3.1+20211210.a149de51.tar.bz2 -> crmsh-4.3.1+20211215.85834218.tar.bz2 ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crmsh-4.3.1+20211210.a149de51/crmsh/bootstrap.py new/crmsh-4.3.1+20211215.85834218/crmsh/bootstrap.py --- old/crmsh-4.3.1+20211210.a149de51/crmsh/bootstrap.py 2021-12-10 03:35:17.000000000 +0100 +++ new/crmsh-4.3.1+20211215.85834218/crmsh/bootstrap.py 2021-12-15 15:55:49.000000000 +0100 @@ -353,12 +353,7 @@ """ with logger_utils.status_long(message): while True: - # -r here to display inactive resources - # -R here to display individual clone instances - _rc, out, err = utils.get_stdout_stderr("crm_mon -1rR") - # Make sure clone instances also started(no Stopped instance) - if re.search(r"{}\s+.*:\s+Started\s".format(resource), out) and \ - not re.search(r"{}\s+.*:\s+(Stopped|Starting)".format(resource), out): + if xmlutil.CrmMonXmlParser.is_resource_started(resource): break status_progress() sleep(1) @@ -367,8 +362,7 @@ def wait_for_cluster(): with logger_utils.status_long("Waiting for cluster"): while True: - _rc, out, _err = utils.get_stdout_stderr("crm_mon -1") - if is_online(out): + if is_online(): break status_progress() sleep(2) @@ -387,12 +381,12 @@ return peer_node -def is_online(crm_mon_txt): +def is_online(): """ Check whether local node is online Besides that, in join process, check whether init node is online """ - if not re.search("Online: .* {} ".format(utils.this_node()), crm_mon_txt): + if not xmlutil.CrmMonXmlParser.is_node_online(utils.this_node()): return False # if peer_node is None, this is in the init process @@ -402,7 +396,7 @@ # In join process # If the joining node is already online but can't find the init node # The communication IP maybe mis-configured - if not re.search("Online: .* {} ".format(peer_node), crm_mon_txt): + if not xmlutil.CrmMonXmlParser.is_node_online(peer_node): shutil.copy(COROSYNC_CONF_ORIG, corosync.conf()) csync2_update(corosync.conf()) utils.stop_service("corosync") @@ -1270,7 +1264,7 @@ if utils.calculate_quorate_status(expected_votes, actual_votes) and not diskless_sbd: # safe to use reload return QdevicePolicy.QDEVICE_RELOAD - elif utils.has_resource_running(): + elif xmlutil.CrmMonXmlParser.is_any_resource_running(): # will lose quorum, and with RA running # no reload, no restart cluster service # just leave a warning diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crmsh-4.3.1+20211210.a149de51/crmsh/cibconfig.py new/crmsh-4.3.1+20211215.85834218/crmsh/cibconfig.py --- old/crmsh-4.3.1+20211210.a149de51/crmsh/cibconfig.py 2021-12-10 03:35:17.000000000 +0100 +++ new/crmsh-4.3.1+20211215.85834218/crmsh/cibconfig.py 2021-12-15 15:55:49.000000000 +0100 @@ -41,7 +41,7 @@ from .xmlutil import merge_attributes, is_cib_element, sanity_check_meta from .xmlutil import is_simpleconstraint, is_template, rmnode, is_defaults, is_live_cib from .xmlutil import get_rsc_operations, delete_rscref, xml_equals, lookup_node, RscState -from .xmlutil import cibtext2elem, is_related, check_id_ref, xml_tostring +from .xmlutil import text2elem, is_related, check_id_ref, xml_tostring from .xmlutil import sanitize_cib_for_patching from .cliformat import get_score, nvpairs2list, abs_pos_score, cli_acl_roleref, nvpair_format from .cliformat import cli_nvpair, cli_acl_rule, rsc_set_constraint, get_kind, head_id_format @@ -623,7 +623,7 @@ try: cib_elem = etree.fromstring(s) except etree.ParseError as msg: - logger_utils.cib_parse_err(msg, s) + logger_utils.text_xml_parse_err(msg, s) return False sanitize_cib(cib_elem) if not show_unrecognized_elems(cib_elem): @@ -2749,7 +2749,7 @@ if cib is None: cib = read_cib(cibdump2elem) elif isinstance(cib, str): - cib = cibtext2elem(cib) + cib = text2elem(cib) if not self._import_cib(cib): return False if cibadmin_can_patch(): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crmsh-4.3.1+20211210.a149de51/crmsh/constants.py new/crmsh-4.3.1+20211215.85834218/crmsh/constants.py --- old/crmsh-4.3.1+20211210.a149de51/crmsh/constants.py 2021-12-10 03:35:17.000000000 +0100 +++ new/crmsh-4.3.1+20211215.85834218/crmsh/constants.py 2021-12-15 15:55:49.000000000 +0100 @@ -511,7 +511,9 @@ XML_NODE_QUERY_STANDBY_PATH = "//nodes/node[@id='{node_id}']/instance_attributes/nvpair[@name='standby']" XML_STATUS_QUERY_STANDBY_PATH = "//status/node_state[@id='{node_id}']/transient_attributes/instance_attributes/nvpair[@name='standby']" CRM_MON_ONE_SHOT = "crm_mon -1" +CRM_MON_XML_OUTPUT= "crm_mon --output-as=xml" STONITH_TIMEOUT_DEFAULT = 60 PCMK_DELAY_MAX = 30 DLM_CONTROLD_RA = "ocf::pacemaker:controld" +LVMLOCKD_RA = "ocf::heartbeat:lvmlockd" # vim:ts=4:sw=4:et: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crmsh-4.3.1+20211210.a149de51/crmsh/log.py new/crmsh-4.3.1+20211215.85834218/crmsh/log.py --- old/crmsh-4.3.1+20211210.a149de51/crmsh/log.py 2021-12-10 03:35:17.000000000 +0100 +++ new/crmsh-4.3.1+20211215.85834218/crmsh/log.py 2021-12-15 15:55:49.000000000 +0100 @@ -370,7 +370,7 @@ def empty_cib_err(self): self.logger.error("No CIB!") - def cib_parse_err(self, msg, s): + def text_xml_parse_err(self, msg, s): self.logger.error(msg) self.logger.info("offending string: %s", s) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crmsh-4.3.1+20211210.a149de51/crmsh/ocfs2.py new/crmsh-4.3.1+20211215.85834218/crmsh/ocfs2.py --- old/crmsh-4.3.1+20211210.a149de51/crmsh/ocfs2.py 2021-12-10 03:35:17.000000000 +0100 +++ new/crmsh-4.3.1+20211215.85834218/crmsh/ocfs2.py 2021-12-15 15:55:49.000000000 +0100 @@ -5,6 +5,8 @@ from . import ra from . import corosync from . import log +from . import xmlutil +from . import constants logger = log.setup_logger(__name__) @@ -330,7 +332,7 @@ if not target: return with logger_utils.status_long("Verify OCFS2 environment"): - use_cluster_lvm2 = utils.has_resource_configured("ocf::heartbeat:lvmlockd", peer) + use_cluster_lvm2 = xmlutil.CrmMonXmlParser.is_resource_configured(constants.LVMLOCKD_RA, peer) self._verify_packages(use_cluster_lvm2) if utils.is_dev_a_plain_raw_disk_or_partition(target, peer): utils.compare_uuid_with_peer_dev([target], peer) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crmsh-4.3.1+20211210.a149de51/crmsh/sbd.py new/crmsh-4.3.1+20211215.85834218/crmsh/sbd.py --- old/crmsh-4.3.1+20211210.a149de51/crmsh/sbd.py 2021-12-10 03:35:17.000000000 +0100 +++ new/crmsh-4.3.1+20211215.85834218/crmsh/sbd.py 2021-12-15 15:55:49.000000000 +0100 @@ -7,6 +7,7 @@ from . import log from . import constants from . import corosync +from . import xmlutil logger = log.setup_logger(__name__) @@ -220,7 +221,7 @@ """ # TODO this function should be outside of sbd.py, to adjust any fence device - if not utils.has_resource_configured(SBDManager.SBD_RA): + if not xmlutil.CrmMonXmlParser.is_resource_configured(SBDManager.SBD_RA): return if self.two_node_without_qdevice: @@ -433,7 +434,7 @@ """ Try to configure sbd resource, restart cluster on needed """ - if not utils.has_resource_running(): + if not xmlutil.CrmMonXmlParser.is_any_resource_running(): logger.info("Restarting cluster service") utils.cluster_run_cmd("crm cluster restart") bootstrap.wait_for_cluster() @@ -500,7 +501,7 @@ """ if not utils.package_is_installed("sbd") or \ not utils.service_is_enabled("sbd.service") or \ - utils.has_resource_configured(self.SBD_RA): + xmlutil.CrmMonXmlParser.is_resource_configured(self.SBD_RA): return # disk-based sbd diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crmsh-4.3.1+20211210.a149de51/crmsh/utils.py new/crmsh-4.3.1+20211215.85834218/crmsh/utils.py --- old/crmsh-4.3.1+20211210.a149de51/crmsh/utils.py 2021-12-10 03:35:17.000000000 +0100 +++ new/crmsh-4.3.1+20211215.85834218/crmsh/utils.py 2021-12-15 15:55:49.000000000 +0100 @@ -2676,14 +2676,14 @@ return int(actual_votes)/int(expected_votes) > 0.5 -def get_stdout_or_raise_error(cmd, remote=None, success_val_list=[0]): +def get_stdout_or_raise_error(cmd, remote=None, success_val_list=[0], no_raise=False): """ Common function to get stdout from cmd or raise exception """ if remote: cmd = "ssh {} root@{} \"{}\"".format(SSH_OPTION, remote, cmd) rc, out, err = get_stdout_stderr(cmd, no_reg=True) - if rc not in success_val_list: + if rc not in success_val_list and not no_raise: raise ValueError("Failed to run \"{}\": {}".format(cmd, err)) return out @@ -2696,28 +2696,6 @@ return dict(re.findall("(Expected|Total) votes:\s+(\d+)", out)) -def has_resource_running(ra_type=None): - """ - Check if any RA is running - """ - cmd = "crm_mon -1" - if ra_type: - cmd = "crm_mon -1rR" - out = get_stdout_or_raise_error(cmd) - if ra_type: - return re.search("{}.*Started".format(ra_type), out) is not None - else: - return re.search("No active resources", out) is None - - -def has_resource_configured(ra_type, peer=None): - """ - Check if the RA configured - """ - out = get_stdout_or_raise_error("crm_mon -1rR", remote=peer) - return re.search(ra_type, out) is not None - - def check_all_nodes_reachable(): """ Check if all cluster nodes are reachable @@ -2965,14 +2943,16 @@ """ Check if dlm ra controld is running """ - return has_resource_running(constants.DLM_CONTROLD_RA) + from . import xmlutil + return xmlutil.CrmMonXmlParser.is_resource_started(constants.DLM_CONTROLD_RA) def is_dlm_configured(): """ Check if dlm configured """ - return has_resource_configured(constants.DLM_CONTROLD_RA) + from . import xmlutil + return xmlutil.CrmMonXmlParser.is_resource_configured(constants.DLM_CONTROLD_RA) def is_quorate(): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crmsh-4.3.1+20211210.a149de51/crmsh/xmlutil.py new/crmsh-4.3.1+20211215.85834218/crmsh/xmlutil.py --- old/crmsh-4.3.1+20211210.a149de51/crmsh/xmlutil.py 2021-12-10 03:35:17.000000000 +0100 +++ new/crmsh-4.3.1+20211215.85834218/crmsh/xmlutil.py 2021-12-15 15:55:49.000000000 +0100 @@ -15,7 +15,7 @@ from . import constants from . import userdir from .utils import add_sudo, str2file, str2tmp, get_boolean -from .utils import get_stdout, stdout2list, crm_msec, crm_time_cmp +from .utils import get_stdout, get_stdout_or_raise_error, stdout2list, crm_msec, crm_time_cmp from .utils import olist, get_cib_in_use, get_tempdir, to_ascii from . import log @@ -101,15 +101,15 @@ return None -def cibtext2elem(cibtext): +def text2elem(text): """ Convert a text format CIB to an XML tree. """ try: - return etree.fromstring(cibtext) + return etree.fromstring(text) except Exception as err: - logger_utils.cib_parse_err(err, cibtext) + logger_utils.text_xml_parse_err(err, text) return None @@ -120,7 +120,7 @@ cmd = cib_dump rc, outp, errp = sudocall(cmd) if rc == 0: - return cibtext2elem(outp) + return text2elem(outp) elif rc != constants.cib_no_section_rc: logger.error("running %s: %s", cmd, errp) return None @@ -1482,4 +1482,68 @@ return e +class CrmMonXmlParser(object): + """ + Class to parse xml output of crm_mon + """ + def __init__(self, peer=None): + """ + Init function + when peer set, parse peer node's results + """ + self.xml_elem = None + self.peer = peer + + def _load(self): + """ + Load xml output of crm_mon + """ + output = get_stdout_or_raise_error(constants.CRM_MON_XML_OUTPUT, remote=self.peer, no_raise=True) + self.xml_elem = text2elem(output) + + @classmethod + def is_node_online(cls, node): + """ + Check if node online + """ + cls_inst = cls() + cls_inst._load() + elem_list = cls_inst.xml_elem.xpath('//node[@name="{}" and @online="true"]'.format(node)) + return len(elem_list) != 0 + + @classmethod + def is_resource_configured(cls, ra_type, peer=None): + """ + Check if the RA configured + """ + cls_inst = cls(peer=peer) + cls_inst._load() + elem_list = cls_inst.xml_elem.xpath('//resource[@resource_agent="{}"]'.format(ra_type)) + return len(elem_list) != 0 + + @classmethod + def is_any_resource_running(cls, peer=None): + """ + Check if any RA is running + """ + cls_inst = cls(peer=peer) + cls_inst._load() + elem_list = cls_inst.xml_elem.xpath('//resource[@active="true"]') + return len(elem_list) != 0 + + @classmethod + def is_resource_started(cls, ra, peer=None): + """ + Check if the RA started(in all clone instances if configured as clone) + + @ra could be resource id or resource type + """ + cls_inst = cls(peer=peer) + cls_inst._load() + elem_list = cls_inst.xml_elem.xpath('//resource[(@id="{ra}" or @resource_agent="{ra}") and @active="true"]'.format(ra=ra)) + # Stopped or not exist + if not elem_list: + return False + # Starting will return False + return all([True if elem.get('role') == 'Started' else False for elem in elem_list]) # vim:ts=4:sw=4:et: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crmsh-4.3.1+20211210.a149de51/test/unittests/test_bootstrap.py new/crmsh-4.3.1+20211215.85834218/test/unittests/test_bootstrap.py --- old/crmsh-4.3.1+20211210.a149de51/test/unittests/test_bootstrap.py 2021-12-10 03:35:17.000000000 +0100 +++ new/crmsh-4.3.1+20211215.85834218/test/unittests/test_bootstrap.py 2021-12-15 15:55:49.000000000 +0100 @@ -608,31 +608,31 @@ mock_error.assert_called_once_with("error") @mock.patch('crmsh.utils.this_node') - @mock.patch('re.search') @mock.patch('crmsh.bootstrap.get_cluster_node_hostname') - def test_is_online_local_offline(self, mock_get_peer, mock_search, mock_this_node): + @mock.patch('crmsh.xmlutil.CrmMonXmlParser.is_node_online') + def test_is_online_local_offline(self, mock_is_online, mock_get_peer, mock_this_node): mock_this_node.return_value = "node1" - mock_search.return_value = None + mock_is_online.return_value = False - assert bootstrap.is_online("text") is False + assert bootstrap.is_online() is False mock_this_node.assert_called_once_with() mock_get_peer.assert_not_called() - mock_search.assert_called_once_with("Online: .* node1 ", "text") + mock_is_online.assert_called_once_with("node1") @mock.patch('crmsh.utils.this_node') - @mock.patch('re.search') @mock.patch('crmsh.bootstrap.get_cluster_node_hostname') - def test_is_online_on_init_node(self, mock_get_peer, mock_search, mock_this_node): - mock_search.return_value = mock.Mock() + @mock.patch('crmsh.xmlutil.CrmMonXmlParser.is_node_online') + def test_is_online_on_init_node(self, mock_is_online, mock_get_peer, mock_this_node): mock_this_node.return_value = "node1" mock_get_peer.return_value = None + mock_is_online.return_value = True - assert bootstrap.is_online("text") is True + assert bootstrap.is_online() is True mock_this_node.assert_called_once_with() mock_get_peer.assert_called_once_with() - mock_search.assert_called_once_with("Online: .* node1 ", "text") + mock_is_online.assert_called_once_with("node1") @mock.patch('crmsh.utils.fatal') @mock.patch('crmsh.utils.stop_service') @@ -640,25 +640,21 @@ @mock.patch('crmsh.corosync.conf') @mock.patch('shutil.copy') @mock.patch('crmsh.utils.this_node') - @mock.patch('re.search') @mock.patch('crmsh.bootstrap.get_cluster_node_hostname') - def test_is_online_peer_offline(self, mock_get_peer, mock_search, mock_this_node, + @mock.patch('crmsh.xmlutil.CrmMonXmlParser.is_node_online') + def test_is_online_peer_offline(self, mock_is_online, mock_get_peer, mock_this_node, mock_copy, mock_corosync_conf, mock_csync2, mock_stop_service, mock_error): + mock_is_online.side_effect = [True, False] bootstrap.COROSYNC_CONF_ORIG = "/tmp/crmsh_tmpfile" - mock_search.side_effect = [ mock.Mock(), None ] mock_this_node.return_value = "node2" mock_get_peer.return_value = "node1" mock_corosync_conf.side_effect = [ "/etc/corosync/corosync.conf", "/etc/corosync/corosync.conf"] - bootstrap.is_online("text") + bootstrap.is_online() mock_this_node.assert_called_once_with() mock_get_peer.assert_called_once_with() - mock_search.assert_has_calls([ - mock.call("Online: .* node2 ", "text"), - mock.call("Online: .* node1 ", "text") - ]) mock_corosync_conf.assert_has_calls([ mock.call(), mock.call() @@ -674,22 +670,18 @@ @mock.patch('crmsh.corosync.conf') @mock.patch('shutil.copy') @mock.patch('crmsh.utils.this_node') - @mock.patch('re.search') @mock.patch('crmsh.bootstrap.get_cluster_node_hostname') - def test_is_online_both_online(self, mock_get_peer, mock_search, mock_this_node, + @mock.patch('crmsh.xmlutil.CrmMonXmlParser.is_node_online') + def test_is_online_both_online(self, mock_is_online, mock_get_peer, mock_this_node, mock_copy, mock_corosync_conf, mock_csync2, mock_stop_service, mock_error): - mock_search.side_effect = [ mock.Mock(), mock.Mock() ] + mock_is_online.side_effect = [True, True] mock_this_node.return_value = "node2" mock_get_peer.return_value = "node1" - assert bootstrap.is_online("text") is True + assert bootstrap.is_online() is True mock_this_node.assert_called_once_with() mock_get_peer.assert_called_once_with() - mock_search.assert_has_calls([ - mock.call("Online: .* node2 ", "text"), - mock.call("Online: .* node1 ", "text") - ]) mock_corosync_conf.assert_not_called() mock_copy.assert_not_called() mock_csync2.assert_not_called() @@ -1075,7 +1067,7 @@ mock_get_dict.assert_called_once_with() mock_quorate.assert_called_once_with(3, 2) - @mock.patch('crmsh.utils.has_resource_running') + @mock.patch('crmsh.xmlutil.CrmMonXmlParser.is_any_resource_running') @mock.patch('crmsh.utils.calculate_quorate_status') @mock.patch('crmsh.utils.get_quorum_votes_dict') def test_evaluate_qdevice_quorum_effect_reload(self, mock_get_dict, mock_quorate, mock_ra_running): @@ -1088,7 +1080,7 @@ mock_quorate.assert_called_once_with(2, 1) mock_ra_running.assert_called_once_with() - @mock.patch('crmsh.utils.has_resource_running') + @mock.patch('crmsh.xmlutil.CrmMonXmlParser.is_any_resource_running') @mock.patch('crmsh.utils.calculate_quorate_status') @mock.patch('crmsh.utils.get_quorum_votes_dict') def test_evaluate_qdevice_quorum_effect(self, mock_get_dict, mock_quorate, mock_ra_running): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crmsh-4.3.1+20211210.a149de51/test/unittests/test_ocfs2.py new/crmsh-4.3.1+20211215.85834218/test/unittests/test_ocfs2.py --- old/crmsh-4.3.1+20211210.a149de51/test/unittests/test_ocfs2.py 2021-12-10 03:35:17.000000000 +0100 +++ new/crmsh-4.3.1+20211215.85834218/test/unittests/test_ocfs2.py 2021-12-15 15:55:49.000000000 +0100 @@ -4,7 +4,7 @@ from unittest import mock except ImportError: import mock -from crmsh import ocfs2, utils, ra +from crmsh import ocfs2, utils, ra, constants logging.basicConfig(level=logging.INFO) @@ -450,7 +450,7 @@ @mock.patch('crmsh.utils.compare_uuid_with_peer_dev') @mock.patch('crmsh.utils.is_dev_a_plain_raw_disk_or_partition') @mock.patch('crmsh.ocfs2.OCFS2Manager._verify_packages') - @mock.patch('crmsh.utils.has_resource_configured') + @mock.patch('crmsh.xmlutil.CrmMonXmlParser.is_resource_configured') @mock.patch('crmsh.log.LoggerUtils.status_long') @mock.patch('crmsh.ocfs2.OCFS2Manager._find_target_on_join') def test_join_ocfs2(self, mock_find, mock_long, mock_configured, mock_verify_packages, mock_is_mapper, mock_compare): @@ -459,7 +459,7 @@ mock_is_mapper.return_value = True self.ocfs2_inst3.join_ocfs2("node1") mock_find.assert_called_once_with("node1") - mock_configured.assert_called_once_with("ocf::heartbeat:lvmlockd", "node1") + mock_configured.assert_called_once_with(constants.LVMLOCKD_RA, "node1") mock_verify_packages.assert_called_once_with(False) mock_is_mapper.assert_called_once_with("/dev/sda2", "node1") mock_compare.assert_called_once_with(["/dev/sda2"], "node1") diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crmsh-4.3.1+20211210.a149de51/test/unittests/test_sbd.py new/crmsh-4.3.1+20211215.85834218/test/unittests/test_sbd.py --- old/crmsh-4.3.1+20211210.a149de51/test/unittests/test_sbd.py 2021-12-10 03:35:17.000000000 +0100 +++ new/crmsh-4.3.1+20211215.85834218/test/unittests/test_sbd.py 2021-12-15 15:55:49.000000000 +0100 @@ -608,7 +608,7 @@ @mock.patch('crmsh.bootstrap.wait_for_cluster') @mock.patch('crmsh.utils.cluster_run_cmd') @mock.patch('logging.Logger.info') - @mock.patch('crmsh.utils.has_resource_running') + @mock.patch('crmsh.xmlutil.CrmMonXmlParser.is_any_resource_running') def test_restart_cluster_on_needed_no_ra_running(self, mock_ra_running, mock_status, mock_cluster_run, mock_wait, mock_config_sbd_ra): mock_ra_running.return_value = False self.sbd_inst._restart_cluster_and_configure_sbd_ra() @@ -619,7 +619,7 @@ @mock.patch('crmsh.sbd.SBDTimeout.get_stonith_timeout') @mock.patch('logging.Logger.warning') - @mock.patch('crmsh.utils.has_resource_running') + @mock.patch('crmsh.xmlutil.CrmMonXmlParser.is_any_resource_running') def test_restart_cluster_on_needed_diskless(self, mock_ra_running, mock_warn, mock_get_timeout): mock_ra_running.return_value = True mock_get_timeout.return_value = 60 @@ -632,7 +632,7 @@ @mock.patch('crmsh.sbd.SBDManager.configure_sbd_resource_and_properties') @mock.patch('logging.Logger.warning') - @mock.patch('crmsh.utils.has_resource_running') + @mock.patch('crmsh.xmlutil.CrmMonXmlParser.is_any_resource_running') def test_restart_cluster_on_needed(self, mock_ra_running, mock_warn, mock_config_sbd_ra): mock_ra_running.return_value = True self.sbd_inst._restart_cluster_and_configure_sbd_ra() @@ -666,7 +666,7 @@ @mock.patch('crmsh.sbd.SBDTimeout.adjust_sbd_timeout_related_cluster_configuration') @mock.patch('crmsh.utils.set_property') @mock.patch('crmsh.utils.get_stdout_or_raise_error') - @mock.patch('crmsh.utils.has_resource_configured') + @mock.patch('crmsh.xmlutil.CrmMonXmlParser.is_resource_configured') @mock.patch('crmsh.utils.service_is_enabled') @mock.patch('crmsh.utils.package_is_installed') def test_configure_sbd_resource_and_properties(self, mock_package, mock_enabled, mock_configured, mock_run, mock_set_property, sbd_adjust): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crmsh-4.3.1+20211210.a149de51/test/unittests/test_utils.py new/crmsh-4.3.1+20211215.85834218/test/unittests/test_utils.py --- old/crmsh-4.3.1+20211210.a149de51/test/unittests/test_utils.py 2021-12-10 03:35:17.000000000 +0100 +++ new/crmsh-4.3.1+20211215.85834218/test/unittests/test_utils.py 2021-12-15 15:55:49.000000000 +0100 @@ -1223,35 +1223,11 @@ mock_run.assert_called_once_with("corosync-quorumtool -s", remote=None, success_val_list=[0, 2]) -@mock.patch("crmsh.utils.get_stdout_or_raise_error") -def test_has_resource_running(mock_run): - mock_run.return_value = """ -Node List: - * Online: [ 15sp2-1 ] - -Active Resources: - * No active resources - """ - res = utils.has_resource_running() - assert res is False - mock_run.assert_called_once_with("crm_mon -1") - - def test_re_split_string(): assert utils.re_split_string('[; ]', "/dev/sda1; /dev/sdb1 ; ") == ["/dev/sda1", "/dev/sdb1"] assert utils.re_split_string('[; ]', "/dev/sda1 ") == ["/dev/sda1"] -@mock.patch("crmsh.utils.get_stdout_or_raise_error") -def test_has_resource_configured(mock_run): - mock_run.return_value = """ -primitive stonith-sbd stonith:external/sbd \ - params pcmk_delay_max=30s - """ - res = utils.has_resource_configured("stonith:external/sbd") - assert res is True - - @mock.patch('crmsh.utils.get_dev_info') def test_has_dev_partitioned(mock_get_dev_info): mock_get_dev_info.return_value = """ @@ -1525,11 +1501,11 @@ mock_run.assert_called_once_with('dlm_tool set_config "key2=test"') -@mock.patch('crmsh.utils.has_resource_configured') +@mock.patch('crmsh.xmlutil.CrmMonXmlParser.is_resource_configured') def test_is_dlm_configured(mock_configured): mock_configured.return_value = True assert utils.is_dlm_configured() is True - mock_configured.assert_called_once_with("ocf::pacemaker:controld") + mock_configured.assert_called_once_with(constants.DLM_CONTROLD_RA) @mock.patch('crmsh.utils.get_stdout_or_raise_error') diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crmsh-4.3.1+20211210.a149de51/test/unittests/test_xmlutil.py new/crmsh-4.3.1+20211215.85834218/test/unittests/test_xmlutil.py --- old/crmsh-4.3.1+20211210.a149de51/test/unittests/test_xmlutil.py 1970-01-01 01:00:00.000000000 +0100 +++ new/crmsh-4.3.1+20211215.85834218/test/unittests/test_xmlutil.py 2021-12-15 15:55:49.000000000 +0100 @@ -0,0 +1,86 @@ +import unittest + +try: + from unittest import mock +except ImportError: + import mock + +from crmsh import xmlutil, constants + + +class TestCrmMonXmlParser(unittest.TestCase): + """ + Unitary tests for crmsh.xmlutil.CrmMonXmlParser + """ + + @classmethod + def setUpClass(cls): + """ + Global setUp. + """ + + def setUp(self): + """ + Test setUp. + """ + self.parser_inst = xmlutil.CrmMonXmlParser() + self.nodes_xml = """ + <nodes> + <node name="tbw-1" id="1084783148" online="true" standby="false" standby_onfail="false" maintenance="false" pending="false" unclean="false" shutdown="false" expected_up="true" is_dc="true" resources_running="3" type="member"/> + <node name="tbw-2" id="1084783312" online="true" standby="false" standby_onfail="false" maintenance="false" pending="false" unclean="false" shutdown="false" expected_up="true" is_dc="false" resources_running="2" type="member"/> + </nodes> + """ + self.resources_xml = """ + <resources> + <resource id="ocfs2-dlm" resource_agent="ocf::pacemaker:controld" role="Started" active="true" orphaned="false" blocked="false" managed="true" failed="false" failure_ignored="false" nodes_running_on="1"> + <node name="tbw-2" id="1084783312" cached="true"/> + </resource> + <resource id="ocfs2-clusterfs" resource_agent="ocf::heartbeat:Filesystem" role="Started" active="true" orphaned="false" blocked="false" managed="true" failed="false" failure_ignored="false" nodes_running_on="1"> + <node name="tbw-2" id="1084783312" cached="true"/> + </resource> + </resources> + """ + + def tearDown(self): + """ + Test tearDown. + """ + + @classmethod + def tearDownClass(cls): + """ + Global tearDown. + """ + + @mock.patch('crmsh.xmlutil.text2elem') + @mock.patch('crmsh.xmlutil.get_stdout_or_raise_error') + def test_load(self, mock_run, mock_text2elem): + mock_run.return_value = "data" + mock_text2elem.return_value = mock.Mock() + self.parser_inst._load() + mock_run.assert_called_once_with(constants.CRM_MON_XML_OUTPUT, remote=None, no_raise=True) + mock_text2elem.assert_called_once_with("data") + + @mock.patch('crmsh.xmlutil.get_stdout_or_raise_error') + def test_is_node_online(self, mock_run): + mock_run.return_value = self.nodes_xml + assert xmlutil.CrmMonXmlParser.is_node_online("node1") is False + assert xmlutil.CrmMonXmlParser.is_node_online("tbw-2") is True + + @mock.patch('crmsh.xmlutil.get_stdout_or_raise_error') + def test_is_resource_configured(self, mock_run): + mock_run.return_value = self.resources_xml + assert xmlutil.CrmMonXmlParser.is_resource_configured("test") is False + assert xmlutil.CrmMonXmlParser.is_resource_configured("ocf::heartbeat:Filesystem") is True + + @mock.patch('crmsh.xmlutil.get_stdout_or_raise_error') + def test_is_any_resource_running(self, mock_run): + mock_run.return_value = self.resources_xml + assert xmlutil.CrmMonXmlParser.is_any_resource_running() is True + + @mock.patch('crmsh.xmlutil.get_stdout_or_raise_error') + def test_is_resource_started(self, mock_run): + mock_run.return_value = self.resources_xml + assert xmlutil.CrmMonXmlParser.is_resource_started("test") is False + assert xmlutil.CrmMonXmlParser.is_resource_started("ocfs2-clusterfs") is True + assert xmlutil.CrmMonXmlParser.is_resource_started("ocf::pacemaker:controld") is True