Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package crmsh for openSUSE:Factory checked in at 2022-09-14 13:44:43 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/crmsh (Old) and /work/SRC/openSUSE:Factory/.crmsh.new.2083 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "crmsh" Wed Sep 14 13:44:43 2022 rev:257 rq:1003341 version:4.4.1+20220913.57fa9d96 Changes: -------- --- /work/SRC/openSUSE:Factory/crmsh/crmsh.changes 2022-09-08 14:24:07.814734857 +0200 +++ /work/SRC/openSUSE:Factory/.crmsh.new.2083/crmsh.changes 2022-09-14 13:44:51.905876575 +0200 @@ -1,0 +2,8 @@ +Tue Sep 13 07:53:14 UTC 2022 - zz...@suse.com + +- Update to version 4.4.1+20220913.57fa9d96: + * Dev: unittest: Adjust unit test based on previous changes + * Dev: utils: Refactor class ServiceManager, to show all nodes' status when running in parallel + * Dev: bootstrap: Add delay to start corosync when node list larger than 5 + +------------------------------------------------------------------- Old: ---- crmsh-4.4.1+20220908.d668787c.tar.bz2 New: ---- crmsh-4.4.1+20220913.57fa9d96.tar.bz2 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ crmsh.spec ++++++ --- /var/tmp/diff_new_pack.LkZws5/_old 2022-09-14 13:44:52.489878045 +0200 +++ /var/tmp/diff_new_pack.LkZws5/_new 2022-09-14 13:44:52.497878066 +0200 @@ -36,7 +36,7 @@ Summary: High Availability cluster command-line interface License: GPL-2.0-or-later Group: %{pkg_group} -Version: 4.4.1+20220908.d668787c +Version: 4.4.1+20220913.57fa9d96 Release: 0 URL: http://crmsh.github.io Source0: %{name}-%{version}.tar.bz2 ++++++ _servicedata ++++++ --- /var/tmp/diff_new_pack.LkZws5/_old 2022-09-14 13:44:52.549878196 +0200 +++ /var/tmp/diff_new_pack.LkZws5/_new 2022-09-14 13:44:52.553878206 +0200 @@ -9,7 +9,7 @@ </service> <service name="tar_scm"> <param name="url">https://github.com/ClusterLabs/crmsh.git</param> - <param name="changesrevision">d668787cd5b5d0c5339e6c993d2a68ee2d2e02fb</param> + <param name="changesrevision">57fa9d96091d03ead44b36d733ff41cd697f5284</param> </service> </servicedata> (No newline at EOF) ++++++ crmsh-4.4.1+20220908.d668787c.tar.bz2 -> crmsh-4.4.1+20220913.57fa9d96.tar.bz2 ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crmsh-4.4.1+20220908.d668787c/crmsh/bootstrap.py new/crmsh-4.4.1+20220913.57fa9d96/crmsh/bootstrap.py --- old/crmsh-4.4.1+20220908.d668787c/crmsh/bootstrap.py 2022-09-08 08:15:31.000000000 +0200 +++ new/crmsh-4.4.1+20220913.57fa9d96/crmsh/bootstrap.py 2022-09-13 09:37:22.000000000 +0200 @@ -685,6 +685,8 @@ """ Start pacemaker service with wait time for sbd When node_list set, start pacemaker service in parallel + + Return success node list """ from .sbd import SBDTimeout pacemaker_start_msg = "Starting pacemaker" @@ -694,8 +696,18 @@ utils.service_is_enabled("sbd.service") and \ SBDTimeout.is_sbd_delay_start(): pacemaker_start_msg += "(delaying start of sbd for {}s)".format(SBDTimeout.get_sbd_delay_start_sec_from_sysconfig()) + with logger_utils.status_long(pacemaker_start_msg): - utils.start_service("pacemaker.service", enable=enable_flag, node_list=node_list) + # To avoid possible JOIN flood in corosync + if len(node_list) > 5: + for node in node_list[:]: + time.sleep(0.25) + try: + utils.start_service("corosync.service", remote_addr=node) + except ValueError as err: + node_list.remove(node) + logger.error(err) + return utils.start_service("pacemaker.service", enable=enable_flag, node_list=node_list) def install_tmp(tmpfile, to): @@ -1342,6 +1354,9 @@ utils.disable_service("corosync-qdevice.service") return logger.info("""Configure Qdevice/Qnetd:""") + for node in utils.list_cluster_nodes(): + if not utils.service_is_available("corosync-qdevice.service", node): + utils.fatal("corosync-qdevice.service is not available on {}".format(node)) qdevice_inst = _context.qdevice_inst qnetd_addr = qdevice_inst.qnetd_addr # Configure ssh passwordless to qnetd if detect password is needed @@ -1680,6 +1695,10 @@ else: corosync.set_value("totem.nodeid", nodeid) + is_qdevice_configured = utils.is_qdevice_configured() + if is_qdevice_configured and not utils.service_is_available("corosync-qdevice.service"): + utils.fatal("corosync-qdevice.service is not available") + shutil.copy(corosync.conf(), COROSYNC_CONF_ORIG) # check if use IPv6 @@ -1756,7 +1775,6 @@ local_nodeid = get_local_nodeid() update_nodeid(local_nodeid) - is_qdevice_configured = utils.is_qdevice_configured() if is_qdevice_configured and not is_unicast: # expected_votes here maybe is "0", set to "3" to make sure cluster can start corosync.set_value("quorum.expected_votes", "3") diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crmsh-4.4.1+20220908.d668787c/crmsh/ui_cluster.py new/crmsh-4.4.1+20220913.57fa9d96/crmsh/ui_cluster.py --- old/crmsh-4.4.1+20220908.d668787c/crmsh/ui_cluster.py 2022-09-08 08:15:31.000000000 +0200 +++ new/crmsh-4.4.1+20220913.57fa9d96/crmsh/ui_cluster.py 2022-09-13 09:37:22.000000000 +0200 @@ -166,7 +166,7 @@ if start_qdevice: utils.start_service("corosync-qdevice", node_list=node_list) - bootstrap.start_pacemaker(node_list) + node_list = bootstrap.start_pacemaker(node_list) if start_qdevice: qdevice.QDevice.check_qdevice_vote() for node in node_list: @@ -200,12 +200,12 @@ utils.set_dlm_option(enable_quorum_fencing=0, enable_quorum_lockspace=0) # Stop pacemaker since it can make sure cluster has quorum until stop corosync - utils.stop_service("pacemaker", node_list=node_list) + node_list = utils.stop_service("pacemaker", node_list=node_list) # Then, stop qdevice if is active if utils.service_is_active("corosync-qdevice.service"): utils.stop_service("corosync-qdevice.service", node_list=node_list) # Last, stop corosync - utils.stop_service("corosync", node_list=node_list) + node_list = utils.stop_service("corosync", node_list=node_list) for node in node_list: logger.info("Cluster services stopped on {}".format(node)) @@ -219,31 +219,28 @@ self.do_stop(context, *args) self.do_start(context, *args) - def _enable_disable_common(self, context, *args): - ''' - Common part for enable and disable - ''' - node_list = parse_option_for_nodes(context, *args) - action = context.get_command_name() - utils.cluster_run_cmd("systemctl {} pacemaker.service".format(action), node_list) - if utils.service_is_available("corosync-qdevice.service") and (utils.is_qdevice_configured() or action == "disable"): - utils.cluster_run_cmd("systemctl {} corosync-qdevice.service".format(action), node_list) - for node in node_list: - logger.info("Cluster services %s on %s", action+'d', node) - @command.skill_level('administrator') def do_enable(self, context, *args): ''' Enable the cluster services on this node ''' - self._enable_disable_common(context, *args) + node_list = parse_option_for_nodes(context, *args) + node_list = utils.enable_service("pacemaker.service", node_list=node_list) + if utils.service_is_available("corosync-qdevice.service") and utils.is_qdevice_configured(): + utils.enable_service("corosync-qdevice.service", node_list=node_list) + for node in node_list: + logger.info("Cluster services enabled on %s", node) @command.skill_level('administrator') def do_disable(self, context, *args): ''' Disable the cluster services on this node ''' - self._enable_disable_common(context, *args) + node_list = parse_option_for_nodes(context, *args) + node_list = utils.disable_service("pacemaker.service", node_list=node_list) + utils.disable_service("corosync-qdevice.service", node_list=node_list) + for node in node_list: + logger.info("Cluster services disabled on %s", node) def _args_implicit(self, context, args, name): ''' @@ -399,6 +396,8 @@ parser.error("Invalid stage (%s)" % (stage)) if options.qnetd_addr: + if not utils.service_is_available("corosync-qdevice.service"): + utils.fatal("corosync-qdevice.service is not available") if options.qdevice_heuristics_mode and not options.qdevice_heuristics: parser.error("Option --qdevice-heuristics is required if want to configure heuristics mode") options.qdevice_heuristics_mode = options.qdevice_heuristics_mode or "sync" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crmsh-4.4.1+20220908.d668787c/crmsh/utils.py new/crmsh-4.4.1+20220913.57fa9d96/crmsh/utils.py --- old/crmsh-4.4.1+20220908.d668787c/crmsh/utils.py 2022-09-08 08:15:31.000000000 +0200 +++ new/crmsh-4.4.1+20220913.57fa9d96/crmsh/utils.py 2022-09-13 09:37:22.000000000 +0200 @@ -2515,6 +2515,7 @@ "is_active": "is-active", "is_available": "list-unit-files" } + IGNORE_ERRORS_IN_PARALLAX = ["is_enabled", "is_active", "is_available"] def __init__(self, service_name, remote_addr=None, node_list=[]): """ @@ -2522,8 +2523,15 @@ When node_list set, execute action between nodes in parallel """ self.service_name = service_name + if remote_addr and node_list: + raise ValueError("Cannot assign remote_addr and node_list at the same time") self.remote_addr = remote_addr + self.target_node = remote_addr or this_node() self.node_list = node_list + self.rc = False + self.parallax_res = None + self.nodes_dict = {} + self.success_nodes = [] def _do_action(self, action_type): """ @@ -2534,40 +2542,33 @@ cmd = "systemctl {} {}".format(action_type, self.service_name) if self.node_list: - cluster_run_cmd(cmd, self.node_list) - return True, None + self.parallax_res = parallax.parallax_call(self.node_list, cmd, strict=False) + return elif self.remote_addr and self.remote_addr != this_node(): prompt_msg = "Run \"{}\" on {}".format(cmd, self.remote_addr) - rc, output, err = run_cmd_on_remote(cmd, self.remote_addr, prompt_msg) + rc, _, err = run_cmd_on_remote(cmd, self.remote_addr, prompt_msg) else: - rc, output, err = get_stdout_stderr(cmd) + rc, _, err = get_stdout_stderr(cmd) if rc != 0 and err: raise ValueError("Run \"{}\" error: {}".format(cmd, err)) - return rc == 0, output + self.rc = rc == 0 - @property - def is_available(self): - return self.service_name in self._do_action(self.ACTION_MAP["is_available"])[1] - - @property - def is_enabled(self): - return self._do_action(self.ACTION_MAP["is_enabled"])[0] - - @property - def is_active(self): - return self._do_action(self.ACTION_MAP["is_active"])[0] - - def start(self): - self._do_action(self.ACTION_MAP["start"]) - - def stop(self): - self._do_action(self.ACTION_MAP["stop"]) - - def enable(self): - self._do_action(self.ACTION_MAP["enable"]) + def _handle_action_result(self, action): + if self.parallax_res: + for host, result in self.parallax_res: + if isinstance(result, parallax.Error): + if action not in self.IGNORE_ERRORS_IN_PARALLAX: + logger.error("Failed to %s %s on %s: %s", action, self.service_name, host, str(result)) + self.nodes_dict[host] = False + else: + self.nodes_dict[host] = True + else: + self.nodes_dict[self.target_node] = self.rc + self.success_nodes = [node for node in self.nodes_dict if self.nodes_dict[node]] - def disable(self): - self._do_action(self.ACTION_MAP["disable"]) + def action_and_handle_result(self, action): + self._do_action(self.ACTION_MAP[action]) + self._handle_action_result(action) @classmethod def service_is_available(cls, name, remote_addr=None): @@ -2575,7 +2576,8 @@ Check whether service is available """ inst = cls(name, remote_addr) - return inst.is_available + inst.action_and_handle_result("is_available") + return inst.nodes_dict[inst.target_node] @classmethod def service_is_enabled(cls, name, remote_addr=None): @@ -2583,7 +2585,8 @@ Check whether service is enabled """ inst = cls(name, remote_addr) - return inst.is_enabled + inst.action_and_handle_result("is_enabled") + return inst.nodes_dict[inst.target_node] @classmethod def service_is_active(cls, name, remote_addr=None): @@ -2591,45 +2594,56 @@ Check whether service is active """ inst = cls(name, remote_addr) - return inst.is_active + inst.action_and_handle_result("is_active") + return inst.nodes_dict[inst.target_node] @classmethod def start_service(cls, name, enable=False, remote_addr=None, node_list=[]): """ Start service + Return success node list """ inst = cls(name, remote_addr, node_list) if enable: - inst.enable() - inst.start() + inst.action_and_handle_result("enable") + inst.action_and_handle_result("start") + return inst.success_nodes @classmethod def stop_service(cls, name, disable=False, remote_addr=None, node_list=[]): """ Stop service + Return success node list """ inst = cls(name, remote_addr, node_list) if disable: - inst.disable() - inst.stop() + inst.action_and_handle_result("disable") + inst.action_and_handle_result("stop") + return inst.success_nodes @classmethod def enable_service(cls, name, remote_addr=None, node_list=[]): """ Enable service + Return success node list """ inst = cls(name, remote_addr, node_list) - if inst.is_available and not inst.is_enabled: - inst.enable() + inst.action_and_handle_result("enable") + return inst.success_nodes @classmethod def disable_service(cls, name, remote_addr=None, node_list=[]): """ Disable service + Return success node list """ inst = cls(name, remote_addr, node_list) - if inst.is_available and inst.is_enabled: - inst.disable() + inst.action_and_handle_result("is_available") + if not inst.success_nodes: + return [] + inst.node_list = inst.success_nodes + inst.action_and_handle_result("disable") + return inst.success_nodes service_is_available = ServiceManager.service_is_available diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crmsh-4.4.1+20220908.d668787c/test/unittests/test_bootstrap.py new/crmsh-4.4.1+20220913.57fa9d96/test/unittests/test_bootstrap.py --- old/crmsh-4.4.1+20220908.d668787c/test/unittests/test_bootstrap.py 2022-09-08 08:15:31.000000000 +0200 +++ new/crmsh-4.4.1+20220913.57fa9d96/test/unittests/test_bootstrap.py 2022-09-13 09:37:22.000000000 +0200 @@ -754,11 +754,13 @@ mock_status.assert_not_called() mock_disable.assert_called_once_with("corosync-qdevice.service") + @mock.patch('crmsh.utils.list_cluster_nodes') @mock.patch('crmsh.utils.fatal') @mock.patch('crmsh.bootstrap.invoke') @mock.patch('crmsh.utils.check_ssh_passwd_need') @mock.patch('logging.Logger.info') - def test_init_qdevice_copy_ssh_key_failed(self, mock_status, mock_ssh, mock_invoke, mock_error): + def test_init_qdevice_copy_ssh_key_failed(self, mock_status, mock_ssh, mock_invoke, mock_error, mock_list_nodes): + mock_list_nodes.return_value = [] bootstrap._context = mock.Mock(qdevice_inst=self.qdevice_with_ip) mock_ssh.return_value = True mock_invoke.return_value = (False, None, "error") @@ -775,11 +777,13 @@ mock_invoke.assert_called_once_with("ssh-copy-id -i /root/.ssh/id_rsa.pub root@10.10.10.123") mock_error.assert_called_once_with("Failed to copy ssh key: error") + @mock.patch('crmsh.utils.list_cluster_nodes') @mock.patch('crmsh.bootstrap.confirm') @mock.patch('crmsh.utils.is_qdevice_configured') @mock.patch('crmsh.utils.check_ssh_passwd_need') @mock.patch('logging.Logger.info') - def test_init_qdevice_already_configured(self, mock_status, mock_ssh, mock_qdevice_configured, mock_confirm): + def test_init_qdevice_already_configured(self, mock_status, mock_ssh, mock_qdevice_configured, mock_confirm, mock_list_nodes): + mock_list_nodes.return_value = [] bootstrap._context = mock.Mock(qdevice_inst=self.qdevice_with_ip) mock_ssh.return_value = False mock_qdevice_configured.return_value = True @@ -794,11 +798,13 @@ mock_confirm.assert_called_once_with("Qdevice is already configured - overwrite?") self.qdevice_with_ip.start_qdevice_service.assert_called_once_with() + @mock.patch('crmsh.utils.list_cluster_nodes') @mock.patch('crmsh.utils.is_qdevice_configured') @mock.patch('crmsh.utils.check_ssh_passwd_need') @mock.patch('logging.Logger.info') - def test_init_qdevice(self, mock_info, mock_ssh, mock_qdevice_configured): + def test_init_qdevice(self, mock_info, mock_ssh, mock_qdevice_configured, mock_list_nodes): bootstrap._context = mock.Mock(qdevice_inst=self.qdevice_with_ip) + mock_list_nodes.return_value = [] mock_ssh.return_value = False mock_qdevice_configured.return_value = False self.qdevice_with_ip.set_cluster_name = mock.Mock() @@ -814,6 +820,23 @@ self.qdevice_with_ip.valid_qnetd.assert_called_once_with() self.qdevice_with_ip.config_and_start_qdevice.assert_called_once_with() + @mock.patch('crmsh.utils.fatal') + @mock.patch('crmsh.utils.service_is_available') + @mock.patch('crmsh.utils.list_cluster_nodes') + @mock.patch('logging.Logger.info') + def test_init_qdevice_service_not_available(self, mock_info, mock_list_nodes, mock_available, mock_fatal): + bootstrap._context = mock.Mock(qdevice_inst=self.qdevice_with_ip) + mock_list_nodes.return_value = ["node1"] + mock_available.return_value = False + mock_fatal.side_effect = SystemExit + + with self.assertRaises(SystemExit): + bootstrap.init_qdevice() + + mock_fatal.assert_called_once_with("corosync-qdevice.service is not available on node1") + mock_available.assert_called_once_with("corosync-qdevice.service", "node1") + mock_info.assert_called_once_with("Configure Qdevice/Qnetd:") + @mock.patch('crmsh.bootstrap.prompt_for_string') def test_configure_qdevice_interactive_return(self, mock_prompt): bootstrap._context = mock.Mock(yes_to_all=True) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crmsh-4.4.1+20220908.d668787c/test/unittests/test_ui_cluster.py new/crmsh-4.4.1+20220913.57fa9d96/test/unittests/test_ui_cluster.py --- old/crmsh-4.4.1+20220908.d668787c/test/unittests/test_ui_cluster.py 2022-09-08 08:15:31.000000000 +0200 +++ new/crmsh-4.4.1+20220913.57fa9d96/test/unittests/test_ui_cluster.py 2022-09-13 09:37:22.000000000 +0200 @@ -65,6 +65,7 @@ @mock.patch('crmsh.ui_cluster.parse_option_for_nodes') def test_do_start(self, mock_parse_nodes, mock_active, mock_start, mock_qdevice_configured, mock_info, mock_start_pacemaker, mock_check_qdevice): context_inst = mock.Mock() + mock_start_pacemaker.return_value = ["node1"] mock_parse_nodes.return_value = ["node1"] mock_active.side_effect = [False, False] mock_qdevice_configured.return_value = True @@ -104,6 +105,7 @@ @mock.patch('crmsh.ui_cluster.parse_option_for_nodes') def test_do_stop(self, mock_parse_nodes, mock_active, mock_get_dc, mock_dlm_running, mock_is_quorate, mock_set_dlm, mock_stop, mock_info, mock_debug): context_inst = mock.Mock() + mock_stop.side_effect = [["node1"], ["ndoe1"], ["node1"]] mock_parse_nodes.return_value = ["node1"] mock_active.side_effect = [True, True] mock_dlm_running.return_value = True diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crmsh-4.4.1+20220908.d668787c/test/unittests/test_utils.py new/crmsh-4.4.1+20220913.57fa9d96/test/unittests/test_utils.py --- old/crmsh-4.4.1+20220908.d668787c/test/unittests/test_utils.py 2022-09-08 08:15:31.000000000 +0200 +++ new/crmsh-4.4.1+20220913.57fa9d96/test/unittests/test_utils.py 2022-09-13 09:37:22.000000000 +0200 @@ -1042,6 +1042,7 @@ """ self.service_local = utils.ServiceManager("service1") self.service_remote = utils.ServiceManager("service1", "node1") + self.service_node_list = utils.ServiceManager("service1", node_list=["node1", "node2"]) def tearDown(self): """ @@ -1067,110 +1068,105 @@ self.assertEqual("Run \"systemctl start service1\" error: this command failed", str(err.exception)) mock_run.assert_called_once_with("systemctl start service1") - @mock.patch("crmsh.utils.run_cmd_on_remote") - def test_do_action_remote(self, mock_run_remote): - mock_run_remote.return_value = (0, "data", None) - rc, out = self.service_remote._do_action("start") - assert rc == True - assert out == "data" - mock_run_remote.assert_called_once_with("systemctl start service1", - "node1", - "Run \"systemctl start service1\" on node1") - - def test_is_available(self): - self.service_local._do_action = mock.Mock() - self.service_local._do_action.return_value = (True, "service1 service2") - assert self.service_local.is_available == True - self.service_local._do_action.assert_called_once_with("list-unit-files") - - def test_is_enabled(self): - self.service_local._do_action = mock.Mock() - self.service_local._do_action.return_value = (True, None) - assert self.service_local.is_enabled == True - self.service_local._do_action.assert_called_once_with("is-enabled") - - def test_is_active(self): - self.service_local._do_action = mock.Mock() - self.service_local._do_action.return_value = (True, None) - assert self.service_local.is_active == True - self.service_local._do_action.assert_called_once_with("is-active") - - def test_start(self): - self.service_local._do_action = mock.Mock() - self.service_local._do_action.return_value = (True, None) - self.service_local.start() - self.service_local._do_action.assert_called_once_with("start") - - def test_stop(self): - self.service_local._do_action = mock.Mock() - self.service_local._do_action.return_value = (True, None) - self.service_local.stop() - self.service_local._do_action.assert_called_once_with("stop") - - def test_enable(self): - self.service_local._do_action = mock.Mock() - self.service_local._do_action.return_value = (True, None) - self.service_local.enable() - self.service_local._do_action.assert_called_once_with("enable") - - def test_disable(self): - self.service_local._do_action = mock.Mock() - self.service_local._do_action.return_value = (True, None) - self.service_local.disable() - self.service_local._do_action.assert_called_once_with("disable") - - @mock.patch("crmsh.utils.ServiceManager.is_available", new_callable=mock.PropertyMock) - def test_service_is_available(self, mock_available): - mock_available.return_value = True - res = utils.ServiceManager.service_is_available("service1") - self.assertEqual(res, True) - mock_available.assert_called_once_with() - - @mock.patch("crmsh.utils.ServiceManager.is_enabled", new_callable=mock.PropertyMock) - def test_service_is_enabled(self, mock_enabled): - mock_enabled.return_value = True - res = utils.ServiceManager.service_is_enabled("service1") - self.assertEqual(res, True) - mock_enabled.assert_called_once_with() - - @mock.patch("crmsh.utils.ServiceManager.is_active", new_callable=mock.PropertyMock) - def test_service_is_active(self, mock_active): - mock_active.return_value = True - res = utils.ServiceManager.service_is_active("service1") - self.assertEqual(res, True) - mock_active.assert_called_once_with() - - @mock.patch('crmsh.utils.ServiceManager.start') - @mock.patch('crmsh.utils.ServiceManager.enable') - def test_start_service(self, mock_enable, mock_start): - utils.ServiceManager.start_service("service1", enable=True) - mock_enable.assert_called_once_with() - mock_start.assert_called_once_with() - - @mock.patch('crmsh.utils.ServiceManager.stop') - @mock.patch('crmsh.utils.ServiceManager.disable') - def test_stop_service(self, mock_disable, mock_stop): - utils.ServiceManager.stop_service("service1", disable=True) - mock_disable.assert_called_once_with() - mock_stop.assert_called_once_with() - - @mock.patch('crmsh.utils.ServiceManager.enable') - @mock.patch('crmsh.utils.ServiceManager.is_enabled', new_callable=mock.PropertyMock) - @mock.patch('crmsh.utils.ServiceManager.is_available', new_callable=mock.PropertyMock) - def test_enable_service(self, mock_available, mock_enabled, mock_enable): - mock_available.return_value = True - mock_enabled.return_value = False - utils.ServiceManager.enable_service("service1") - mock_enable.assert_called_once_with() - - @mock.patch('crmsh.utils.ServiceManager.disable') - @mock.patch('crmsh.utils.ServiceManager.is_enabled', new_callable=mock.PropertyMock) - @mock.patch('crmsh.utils.ServiceManager.is_available', new_callable=mock.PropertyMock) - def test_disable_service(self, mock_available, mock_enabled, mock_disable): - mock_available.return_value = True - mock_enabled.return_value = True - utils.ServiceManager.disable_service("service1") - mock_disable.assert_called_once_with() + @mock.patch('crmsh.parallax.parallax_call') + def test_do_action_node_list(self, mock_call): + self.service_node_list._do_action("start") + mock_call.assert_called_once_with(["node1", "node2"], "systemctl start service1", strict=False) + + @mock.patch('crmsh.utils.this_node') + @mock.patch('crmsh.utils.run_cmd_on_remote') + def test_do_action_remote(self, mock_remote, mock_this_node): + mock_this_node.return_value = "node2" + mock_remote.return_value = (0, None, None) + self.service_remote._do_action("start") + mock_remote.assert_called_once_with("systemctl start service1", "node1", 'Run "systemctl start service1" on node1') + + @mock.patch('crmsh.utils.ServiceManager._handle_action_result') + @mock.patch('crmsh.utils.ServiceManager._do_action') + def test_action_and_handle_result(self, mock_action, mock_handle): + self.service_local.action_and_handle_result("start") + mock_action.assert_called_once_with("start") + mock_handle.assert_called_once_with("start") + + def test_service_is_available(self): + def _helper(self, val): + self.target_node = "node1" + self.nodes_dict = {"node1": True} + + with mock.patch.object(utils.ServiceManager, "action_and_handle_result", autospec=True) as mock_action: + mock_action.side_effect = _helper + assert utils.ServiceManager.service_is_available("service1") + assert mock_action.call_count == 1 + + def test_service_is_enabled(self): + def _helper(self, val): + self.target_node = "node1" + self.nodes_dict = {"node1": True} + + with mock.patch.object(utils.ServiceManager, "action_and_handle_result", autospec=True) as mock_action: + mock_action.side_effect = _helper + assert utils.ServiceManager.service_is_enabled("service1") + assert mock_action.call_count == 1 + + def test_service_is_active(self): + def _helper(self, val): + self.target_node = "node1" + self.nodes_dict = {"node1": True} + + with mock.patch.object(utils.ServiceManager, "action_and_handle_result", autospec=True) as mock_action: + mock_action.side_effect = _helper + assert utils.ServiceManager.service_is_active("service1") + assert mock_action.call_count == 1 + + def test_start_service(self): + def _helper(self, val): + self.success_nodes = expected_values + expected_values = ["node1", "node2"] + + with mock.patch.object(utils.ServiceManager, "action_and_handle_result", autospec=True) as mock_action: + mock_action.side_effect = _helper + assert utils.ServiceManager.start_service("service1", enable=True) == ["node1", "node2"] + assert mock_action.call_count == 2 + + def test_stop_service(self): + def _helper(self, val): + self.success_nodes = expected_values + expected_values = ["node1"] + + with mock.patch.object(utils.ServiceManager, "action_and_handle_result", autospec=True) as mock_action: + mock_action.side_effect = _helper + assert utils.ServiceManager.stop_service("service1", disable=True) == ["node1"] + assert mock_action.call_count == 2 + + def test_enable_service(self): + def _helper(self, val): + self.success_nodes = expected_values + expected_values = ["node1"] + + with mock.patch.object(utils.ServiceManager, "action_and_handle_result", autospec=True) as mock_action: + mock_action.side_effect = _helper + assert utils.ServiceManager.enable_service("service1") == ["node1"] + assert mock_action.called + + def test_disable_service_none(self): + def _helper(self, val): + self.success_nodes = expected_values + expected_values = [] + + with mock.patch.object(utils.ServiceManager, "action_and_handle_result", autospec=True) as mock_action: + mock_action.side_effect = _helper + assert utils.ServiceManager.disable_service("service1") == [] + assert mock_action.call_count == 1 + + def test_disable_service(self): + def _helper(self, val): + self.success_nodes = expected_values + expected_values = ["node1", "node2"] + + with mock.patch.object(utils.ServiceManager, "action_and_handle_result", autospec=True) as mock_action: + mock_action.side_effect = _helper + assert utils.ServiceManager.disable_service("service1") == ["node1", "node2"] + assert mock_action.call_count == 2 @mock.patch("crmsh.utils.get_nodeid_from_name")