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")

Reply via email to