Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package crmsh for openSUSE:Factory checked 
in at 2023-03-09 17:46:51
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/crmsh (Old)
 and      /work/SRC/openSUSE:Factory/.crmsh.new.31432 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "crmsh"

Thu Mar  9 17:46:51 2023 rev:285 rq:1070389 version:4.5.0+20230309.a4c4192d

Changes:
--------
--- /work/SRC/openSUSE:Factory/crmsh/crmsh.changes      2023-03-07 
16:50:53.389759978 +0100
+++ /work/SRC/openSUSE:Factory/.crmsh.new.31432/crmsh.changes   2023-03-09 
17:46:52.759271772 +0100
@@ -1,0 +2,16 @@
+Thu Mar 09 10:28:49 UTC 2023 - xli...@suse.com
+
+- Update to version 4.5.0+20230309.a4c4192d:
+  * Dev: ChangeLog: update ChangeLog for release 4.5.0-rc2
+  * Dev: version: Bump crmsh version to 4.5.0
+
+-------------------------------------------------------------------
+Thu Mar 09 08:27:30 UTC 2023 - xli...@suse.com
+
+- Update to version 4.4.1+20230309.e15745c6:
+  * Dev: bootstrap: remove unused codes
+  * Dev: unittest: Adjust unit test for previous change
+  * Dev: bootstrap: Swap hacluster ssh key with other nodes
+  * Dev: behave: Check passwordless for hacluster between cluster nodes
+
+-------------------------------------------------------------------

Old:
----
  crmsh-4.4.1+20230307.daea9d13.tar.bz2

New:
----
  crmsh-4.5.0+20230309.a4c4192d.tar.bz2

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ crmsh.spec ++++++
--- /var/tmp/diff_new_pack.Fx3W5a/_old  2023-03-09 17:46:53.423275306 +0100
+++ /var/tmp/diff_new_pack.Fx3W5a/_new  2023-03-09 17:46:53.427275328 +0100
@@ -36,7 +36,7 @@
 Summary:        High Availability cluster command-line interface
 License:        GPL-2.0-or-later
 Group:          %{pkg_group}
-Version:        4.4.1+20230307.daea9d13
+Version:        4.5.0+20230309.a4c4192d
 Release:        0
 URL:            http://crmsh.github.io
 Source0:        %{name}-%{version}.tar.bz2

++++++ _service ++++++
--- /var/tmp/diff_new_pack.Fx3W5a/_old  2023-03-09 17:46:53.459275498 +0100
+++ /var/tmp/diff_new_pack.Fx3W5a/_new  2023-03-09 17:46:53.463275520 +0100
@@ -3,7 +3,7 @@
     <param name="url">https://github.com/ClusterLabs/crmsh.git</param>
     <param name="scm">git</param>
     <param name="filename">crmsh</param>
-    <param name="versionformat">4.4.1+%cd.%h</param>
+    <param name="versionformat">4.5.0+%cd.%h</param>
     <param name="revision">master</param>
     <param name="changesgenerate">enable</param>
   </service>

++++++ _servicedata ++++++
--- /var/tmp/diff_new_pack.Fx3W5a/_old  2023-03-09 17:46:53.483275626 +0100
+++ /var/tmp/diff_new_pack.Fx3W5a/_new  2023-03-09 17:46:53.487275648 +0100
@@ -9,7 +9,7 @@
 </service>
 <service name="tar_scm">
   <param name="url">https://github.com/ClusterLabs/crmsh.git</param>
-  <param 
name="changesrevision">3b0a582ed4f2b9de2e74d86bfdd6bf7e2ca44511</param>
+  <param 
name="changesrevision">1615c26abe860045bf4e15442899e992cc29455c</param>
 </service>
 </servicedata>
 (No newline at EOF)

++++++ crmsh-4.4.1+20230307.daea9d13.tar.bz2 -> 
crmsh-4.5.0+20230309.a4c4192d.tar.bz2 ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/crmsh-4.4.1+20230307.daea9d13/ChangeLog 
new/crmsh-4.5.0+20230309.a4c4192d/ChangeLog
--- old/crmsh-4.4.1+20230307.daea9d13/ChangeLog 2023-03-07 02:41:16.000000000 
+0100
+++ new/crmsh-4.5.0+20230309.a4c4192d/ChangeLog 2023-03-09 10:55:42.000000000 
+0100
@@ -1,3 +1,11 @@
+* Thu Mar 9 2023 Xin Liang <xli...@suse.com>
+- Release 4.5.0 rc2
+- Dev: version: Bump crmsh version to 4.5.0
+- Fix: bootstrap: Swap hacluster ssh key with other nodes
+- Fix: report: Fix crm report issue under non-root user
+- Fix: bootstrap: Don't save core.debug when saving core.hosts (bsc#1208991)
+- Dev: log: Redirect debug messages into stderr
+
 * Fri Mar 3 2023 Xin Liang <xli...@suse.com>
 - Release 4.5.0 rc1
 - Fix: qdevice: Unable to setup qdevice under non-root user (bsc#1208770)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/crmsh-4.4.1+20230307.daea9d13/configure.ac 
new/crmsh-4.5.0+20230309.a4c4192d/configure.ac
--- old/crmsh-4.4.1+20230307.daea9d13/configure.ac      2023-03-07 
02:41:16.000000000 +0100
+++ new/crmsh-4.5.0+20230309.a4c4192d/configure.ac      2023-03-09 
10:55:42.000000000 +0100
@@ -8,7 +8,7 @@
 
 AC_PREREQ([2.53])
 
-AC_INIT([crmsh],[4.4.0],[us...@clusterlabs.org])
+AC_INIT([crmsh],[4.5.0],[us...@clusterlabs.org])
 
 AC_ARG_WITH(version,
     [  --with-version=version   Override package version (if you're a packager 
needing to pretend) ],
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/crmsh-4.4.1+20230307.daea9d13/crmsh/bootstrap.py 
new/crmsh-4.5.0+20230309.a4c4192d/crmsh/bootstrap.py
--- old/crmsh-4.4.1+20230307.daea9d13/crmsh/bootstrap.py        2023-03-07 
02:41:16.000000000 +0100
+++ new/crmsh-4.5.0+20230309.a4c4192d/crmsh/bootstrap.py        2023-03-09 
10:55:42.000000000 +0100
@@ -864,7 +864,6 @@
         for node in _context.node_list:
             if utils.service_is_active("pacemaker.service", remote_addr=node):
                 utils.fatal("Cluster is currently active on {} - can't 
run".format(node))
-    print()
 
 
 def init_ssh_impl(local_user: str, node_list: typing.List[str], user_list: 
typing.List[str]):
@@ -970,27 +969,35 @@
     return keyfile_dict
 
 
-def is_nologin(user):
+def is_nologin(user, remote=None):
     """
-    Check if user's shell is /sbin/nologin
+    Check if user's shell is nologin
     """
-    with open("/etc/passwd") as f:
-        return re.search("{}:.*:/sbin/nologin".format(user), f.read())
+    passwd_file = "/etc/passwd"
+    pattern = f"{user}:.*:/.*/nologin"
+    if remote:
+        cmd = f"cat {passwd_file}|grep {pattern}"
+        rc, _, _ = utils.run_cmd_on_remote(cmd, remote)
+        return rc == 0
+    else:
+        with open(passwd_file) as f:
+            return re.search(pattern, f.read())
 
 
-# FIXME! What is this all about?
-def change_user_shell(user):
+def change_user_shell(user, remote=None):
     """
     To change user's login shell
     """
-    message = "The user '{}' will have the login shell configuration changed 
to /bin/bash"
-    if user != "root" and is_nologin(user):
+    user_msg = f"'{user}' on {remote}" if remote else f"'{user}'"
+    message = f"The user {user_msg} will have the login shell configuration 
changed to /bin/bash"
+    if user != "root" and is_nologin(user, remote):
         if _context is not None and not _context.yes_to_all:
-            logger.info(message.format(user))
+            logger.info(message)
             if not confirm("Continue?"):
                 _context.with_other_user = False
                 return
-        invoke("usermod -s /bin/bash {}".format(user))
+        cmd = f"usermod -s /bin/bash {user}"
+        utils.get_stdout_or_raise_error(cmd, remote)
 
 
 def configure_ssh_key(user):
@@ -1672,6 +1679,7 @@
     # After this, login to remote_node is passwordless
     swap_public_ssh_key(seed_host, local_user, seed_user, local_user, 
seed_user, add=True)
     configure_ssh_key('hacluster')
+    change_user_shell('hacluster', seed_host)
     swap_public_ssh_key(seed_host, 'hacluster', 'hacluster', local_user, 
seed_user, add=True)
 
     # This makes sure the seed host has its own SSH keys in its own
@@ -1735,29 +1743,6 @@
         ))
     return result.stdout.decode('utf-8')
 
-def fetch_public_key_from_remote_node(node, user="root"):
-    """
-    Fetch public key file from remote node
-    Return a temp file contains public key
-    Return None if no key exist
-    """
-
-    # For dsa, might need to add PubkeyAcceptedKeyTypes=+ssh-dss to config 
file, see
-    # 
https://superuser.com/questions/1016989/ssh-dsa-keys-no-longer-work-for-password-less-authentication
-    home_dir = userdir.gethomedir(user)
-    for key in ("id_rsa", "id_ecdsa", "id_ed25519", "id_dsa"):
-        public_key_file = "{}/.ssh/{}.pub".format(home_dir, key)
-        cmd = "ssh {} {}@{} 'test -f {}'".format(SSH_OPTION, user, node, 
public_key_file)
-        if not invokerc(cmd):
-            continue
-        _, temp_public_key_file = tmpfiles.create()
-        cmd = "scp {} {}@{}:{} {}".format(SSH_OPTION, user, node, 
public_key_file, temp_public_key_file)
-        rc, _, err = invoke(cmd)
-        if not rc:
-            utils.fatal("Failed to run \"{}\": {}".format(cmd, err))
-        return temp_public_key_file
-    raise ValueError("No ssh key exist on {}".format(node))
-
 
 def join_csync2(seed_host):
     """
@@ -1970,9 +1955,32 @@
         remote_privileged_user = remote_user_to_swap
         utils.ssh_copy_id(local_user, remote_privileged_user, node)
         swap_public_ssh_key(node, local_user, remote_user_to_swap, local_user, 
remote_privileged_user)
+        if local_user != 'hacluster':
+            change_user_shell('hacluster', node)
+            swap_public_ssh_key(node, 'hacluster', 'hacluster', local_user, 
remote_privileged_user, add=True)
+    if local_user != 'hacluster':
+        swap_key_for_hacluster(cluster_nodes_list)
+
     user_by_host.save_remote(cluster_nodes_list)
 
 
+def swap_key_for_hacluster(other_node_list):
+    """
+    In some cases, old cluster may not be configured passwordless for 
hacluster.
+    The new join node should check and swap the public key between the old 
cluster nodes.
+    """
+    for node in other_node_list:
+        for n in other_node_list:
+            if node == n:
+                continue
+            logger.info("Checking passwordless for hacluster between %s and 
%s", node, n)
+            _, public_key, authorized_file = key_files('hacluster').values()
+            public_key_content = utils.get_stdout_or_raise_error(f'cat 
{public_key}', remote=n)
+            if not utils.check_text_included(public_key_content, 
authorized_file, remote=node):
+                cmd = "echo '{}' >> {}".format(public_key_content, 
authorized_file)
+                utils.get_stdout_or_raise_error(cmd, remote=node)
+
+
 def sync_files_to_disk():
     """
     Sync file content to disk between cluster nodes
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/crmsh-4.4.1+20230307.daea9d13/setup.py 
new/crmsh-4.5.0+20230309.a4c4192d/setup.py
--- old/crmsh-4.4.1+20230307.daea9d13/setup.py  2023-03-07 02:41:16.000000000 
+0100
+++ new/crmsh-4.5.0+20230309.a4c4192d/setup.py  2023-03-09 10:55:42.000000000 
+0100
@@ -4,7 +4,7 @@
 from setuptools import setup
 
 setup(name='crmsh',
-      version='4.4.0',
+      version='4.5.0',
       description='Command-line interface for High-Availability cluster 
management',
       author='Kristoffer Gronlund, Xin Liang',
       author_email='xli...@suse.com',
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/crmsh-4.4.1+20230307.daea9d13/test/features/bootstrap_init_join_remove.feature
 
new/crmsh-4.5.0+20230309.a4c4192d/test/features/bootstrap_init_join_remove.feature
--- 
old/crmsh-4.4.1+20230307.daea9d13/test/features/bootstrap_init_join_remove.feature
  2023-03-07 02:41:16.000000000 +0100
+++ 
new/crmsh-4.5.0+20230309.a4c4192d/test/features/bootstrap_init_join_remove.feature
  2023-03-09 10:55:42.000000000 +0100
@@ -2,10 +2,10 @@
 Feature: crmsh bootstrap process - init, join and remove
 
   Test crmsh bootstrap init/join/remove process
-  Need nodes: hanode1 hanode2
+  Need nodes: hanode1 hanode2 hanode3
 
   Background: Setup a two nodes cluster
-    Given   Nodes ["hanode1", "hanode2"] are cleaned up
+    Given   Nodes ["hanode1", "hanode2", "hanode3"] are cleaned up
     And     Cluster service is "stopped" on "hanode1"
     And     Cluster service is "stopped" on "hanode2"
     When    Run "crm cluster init -y" on "hanode1"
@@ -150,3 +150,34 @@
     Then    Directory "/var/lib/pacemaker/cib/" is empty on "hanode1"
     Then    Directory "/var/lib/pacemaker/pengine/" is empty on "hanode1"
     Then    Directory "/var/lib/corosync/" is empty on "hanode1"
+
+  Scenario: Check hacluster's passwordless configuration on 2 nodes
+    Then    Check passwordless for hacluster between "hanode1 hanode2"
+
+  Scenario: Check hacluster's passwordless configuration in old cluster, 2 
nodes
+    When    Run "crm cluster stop --all" on "hanode1"
+    Then    Cluster service is "stopped" on "hanode1"
+    And     Cluster service is "stopped" on "hanode2"
+    When    Run "crm cluster init -y" on "hanode1"
+    Then    Cluster service is "started" on "hanode1"
+    When    Run "rm -rf /var/lib/heartbeat/cores/hacluster/.ssh" on "hanode1"
+    When    Run "crm cluster join -c hanode1 -y" on "hanode2"
+    Then    Cluster service is "started" on "hanode2"
+    And     Online nodes are "hanode1 hanode2"
+    And     Check passwordless for hacluster between "hanode1 hanode2"
+
+  Scenario: Check hacluster's passwordless configuration on 3 nodes
+    Given   Cluster service is "stopped" on "hanode3"
+    When    Run "crm cluster join -c hanode1 -y" on "hanode3"
+    Then    Cluster service is "started" on "hanode3"
+    And     Online nodes are "hanode1 hanode2 hanode3"
+    And     Check passwordless for hacluster between "hanode1 hanode2 hanode3"
+
+  Scenario: Check hacluster's passwordless configuration in old cluster, 3 
nodes
+    Given   Cluster service is "stopped" on "hanode3"
+    When    Run "rm -rf /var/lib/heartbeat/cores/hacluster/.ssh" on "hanode1"
+    And     Run "rm -rf /var/lib/heartbeat/cores/hacluster/.ssh" on "hanode2"
+    When    Run "crm cluster join -c hanode1 -y" on "hanode3"
+    Then    Cluster service is "started" on "hanode3"
+    And     Online nodes are "hanode1 hanode2 hanode3"
+    And     Check passwordless for hacluster between "hanode1 hanode2 hanode3"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/crmsh-4.4.1+20230307.daea9d13/test/features/steps/step_implementation.py 
new/crmsh-4.5.0+20230309.a4c4192d/test/features/steps/step_implementation.py
--- 
old/crmsh-4.4.1+20230307.daea9d13/test/features/steps/step_implementation.py    
    2023-03-07 02:41:16.000000000 +0100
+++ 
new/crmsh-4.5.0+20230309.a4c4192d/test/features/steps/step_implementation.py    
    2023-03-09 10:55:42.000000000 +0100
@@ -6,7 +6,7 @@
 
 import behave
 from behave import given, when, then
-from crmsh import corosync, parallax, sbd
+from crmsh import corosync, parallax, sbd, userdir
 from crmsh import utils as crmutils
 from utils import check_cluster_state, check_service_state, online, 
run_command, me, \
                   run_command_local_or_remote, file_in_archive, \
@@ -479,3 +479,18 @@
         index += 1
     return False
 
+@then('Check passwordless for hacluster between "{nodelist}"')
+def step_impl(context, nodelist):
+    if userdir.getuser() != 'root':
+        return True
+    ssh_option = "-o StrictHostKeyChecking=no -T -o Batchmode=yes"
+    node_list = nodelist.split()
+    for node in node_list:
+        for n in node_list:
+            if node == n:
+                continue
+            cmd = f"su - hacluster -c 'ssh {ssh_option} hacluster@{n} true'"
+            if node != me():
+                cmd = f"ssh {node} \"{cmd}\""
+            context.logger.info(f"\nRun cmd: {cmd}")
+            run_command(context, cmd)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/crmsh-4.4.1+20230307.daea9d13/test/unittests/test_bootstrap.py 
new/crmsh-4.5.0+20230309.a4c4192d/test/unittests/test_bootstrap.py
--- old/crmsh-4.4.1+20230307.daea9d13/test/unittests/test_bootstrap.py  
2023-03-07 02:41:16.000000000 +0100
+++ new/crmsh-4.5.0+20230309.a4c4192d/test/unittests/test_bootstrap.py  
2023-03-09 10:55:42.000000000 +0100
@@ -361,19 +361,19 @@
 
         bootstrap.change_user_shell("hacluster")
 
-        mock_nologin.assert_called_once_with("hacluster")
+        mock_nologin.assert_called_once_with("hacluster", None)
         mock_confirm.assert_called_once_with("Continue?")
 
-    @mock.patch('crmsh.bootstrap.invoke')
+    @mock.patch('crmsh.utils.get_stdout_or_raise_error')
     @mock.patch('crmsh.bootstrap.is_nologin')
-    def test_change_user_shell_return(self, mock_nologin, mock_invoke):
+    def test_change_user_shell(self, mock_nologin, mock_invoke):
         bootstrap._context = mock.Mock(yes_to_all=True)
         mock_nologin.return_value = True
 
         bootstrap.change_user_shell("hacluster")
 
-        mock_nologin.assert_called_once_with("hacluster")
-        mock_invoke.assert_called_once_with("usermod -s /bin/bash hacluster")
+        mock_nologin.assert_called_once_with("hacluster", None)
+        mock_invoke.assert_called_once_with("usermod -s /bin/bash hacluster", 
None)
 
     @mock.patch('crmsh.utils.su_subprocess_run')
     def test_generate_ssh_key_pair_on_remote(self, mock_su: mock.MagicMock):
@@ -441,36 +441,6 @@
         cmd = "cat fromfile | ssh {} root@node1 'cat >> 
tofile'".format(constants.SSH_OPTION)
         mock_run.assert_called_once_with(cmd)
 
-    @mock.patch('crmsh.bootstrap.invokerc')
-    def test_fetch_public_key_from_remote_node_exception(self, mock_invoke):
-        mock_invoke.side_effect = [False, False, False, False]
-
-        with self.assertRaises(ValueError) as err:
-            bootstrap.fetch_public_key_from_remote_node("node1")
-        self.assertEqual("No ssh key exist on node1", str(err.exception))
-
-        mock_invoke.assert_has_calls([
-            mock.call("ssh {} root@node1 'test -f 
/root/.ssh/id_rsa.pub'".format(constants.SSH_OPTION)),
-            mock.call("ssh {} root@node1 'test -f 
/root/.ssh/id_ecdsa.pub'".format(constants.SSH_OPTION)),
-            mock.call("ssh {} root@node1 'test -f 
/root/.ssh/id_ed25519.pub'".format(constants.SSH_OPTION)),
-            mock.call("ssh {} root@node1 'test -f 
/root/.ssh/id_dsa.pub'".format(constants.SSH_OPTION))
-            ])
-
-    @mock.patch('crmsh.tmpfiles.create')
-    @mock.patch('crmsh.bootstrap.invokerc')
-    @mock.patch('crmsh.bootstrap.invoke')
-    def test_fetch_public_key_from_remote_node(self, mock_invoke, 
mock_invokerc, mock_tmpfile):
-        mock_invokerc.return_value = True
-        mock_invoke.return_value = (True, None, None)
-        mock_tmpfile.return_value = (0, "temp_file_name")
-
-        res = bootstrap.fetch_public_key_from_remote_node("node1")
-        self.assertEqual(res, "temp_file_name")
-
-        mock_invokerc.assert_called_once_with("ssh {} root@node1 'test -f 
/root/.ssh/id_rsa.pub'".format(constants.SSH_OPTION))
-        mock_invoke.assert_called_once_with("scp -o StrictHostKeyChecking=no 
root@node1:/root/.ssh/id_rsa.pub temp_file_name")
-        mock_tmpfile.assert_called_once_with()
-
     @mock.patch('crmsh.utils.fatal')
     def test_join_ssh_no_seed_host(self, mock_error):
         mock_error.side_effect = ValueError
@@ -478,12 +448,13 @@
             bootstrap.join_ssh_impl(None, None)
         mock_error.assert_called_once_with("No existing IP/hostname specified 
(use -c option)")
 
+    @mock.patch('crmsh.bootstrap.change_user_shell')
     @mock.patch('crmsh.utils.su_get_stdout_or_raise_error')
     @mock.patch('crmsh.bootstrap.swap_public_ssh_key')
     @mock.patch('crmsh.utils.ssh_copy_id')
     @mock.patch('crmsh.bootstrap.configure_ssh_key')
     @mock.patch('crmsh.utils.start_service')
-    def test_join_ssh(self, mock_start_service, mock_config_ssh, 
mock_ssh_copy_id, mock_swap, mock_invoke):
+    def test_join_ssh(self, mock_start_service, mock_config_ssh, 
mock_ssh_copy_id, mock_swap, mock_invoke, mock_change):
         bootstrap._context = mock.Mock(current_user="bob", 
user_list=["alice"], node_list=['node1'], default_nic_list=["eth1"])
         mock_invoke.return_value = ''
         mock_swap.return_value = None
@@ -594,6 +565,8 @@
         ])
         mock_error.assert_called_once_with("Can't fetch hostname of node1: 
None")
 
+    @mock.patch('crmsh.bootstrap.swap_key_for_hacluster')
+    @mock.patch('crmsh.bootstrap.change_user_shell')
     @mock.patch('crmsh.utils.HostUserConfig')
     @mock.patch('crmsh.bootstrap._fetch_core_hosts')
     @mock.patch('crmsh.utils.ssh_copy_id')
@@ -608,7 +581,8 @@
             mock_ssh_copy_id: mock.MagicMock,
             mock_fetch_core_hosts,
             mock_host_user_config_class,
-
+            mock_change_shell,
+            mock_swap_hacluster
     ):
         bootstrap._context = mock.Mock(current_user="carol", 
user_list=["alice", "bob"])
         mock_fetch_core_hosts.return_value = (["alice", "bob"], ["node1", 
"node2"])
@@ -630,7 +604,10 @@
         mock_ssh_copy_id.assert_has_calls([
             mock.call('carol', 'bob', 'node2')
         ])
-        mock_swap.assert_called_once_with('node2', "carol", "bob", "carol", 
"bob")
+        mock_swap.assert_has_calls([
+            mock.call('node2', "carol", "bob", "carol", "bob"),
+            mock.call('node2', 'hacluster', 'hacluster', 'carol', 'bob', 
add=True)
+            ])
 
     @mock.patch('crmsh.userdir.getuser')
     @mock.patch('crmsh.bootstrap.key_files')

Reply via email to