Hello community,

here is the log from the commit of package crmsh for openSUSE:Factory checked 
in at 2019-11-06 13:56:46
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/crmsh (Old)
 and      /work/SRC/openSUSE:Factory/.crmsh.new.2990 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "crmsh"

Wed Nov  6 13:56:46 2019 rev:165 rq:745170 version:4.1.0+git.1572504697.472361c5

Changes:
--------
--- /work/SRC/openSUSE:Factory/crmsh/crmsh.changes      2019-10-30 
14:46:46.358115561 +0100
+++ /work/SRC/openSUSE:Factory/.crmsh.new.2990/crmsh.changes    2019-11-06 
13:56:49.444203017 +0100
@@ -1,0 +2,17 @@
+Thu Oct 31 06:57:08 UTC 2019 - xli...@suse.com
+
+- Update to version 4.1.0+git.1572504697.472361c5:
+  * Doc: ui_configure: do_property: ask to remove maintenance from resources 
and nodes
+  * Test: ui_configure: do_property: ask to remove maintenance from resources 
and nodes
+  * Dev: ui_configure: do_property: ask to remove maintenance from resources 
and nodes
+
+-------------------------------------------------------------------
+Tue Oct 29 21:57:47 UTC 2019 - xli...@suse.com
+
+- Update to version 4.1.0+git.1572385946.69f4f51b:
+  * Low: unittest: test init_ssh and init_ssh_remote in bootstrap.py
+  * Low: bootstrap: create authorized_keys file if not exists
+  * Low: bootstrap: add "--no-overwrite-sshkey" option to avoid SSH key be 
overwritten
+  * Low: bootstrap: don't overwrite ssh key if already exists
+
+-------------------------------------------------------------------

Old:
----
  crmsh-4.1.0+git.1572337494.6f2c8ea9.tar.bz2

New:
----
  crmsh-4.1.0+git.1572504697.472361c5.tar.bz2

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

Other differences:
------------------
++++++ crmsh.spec ++++++
--- /var/tmp/diff_new_pack.WURMsp/_old  2019-11-06 13:56:50.712204346 +0100
+++ /var/tmp/diff_new_pack.WURMsp/_new  2019-11-06 13:56:50.720204354 +0100
@@ -36,7 +36,7 @@
 Summary:        High Availability cluster command-line interface
 License:        GPL-2.0-or-later
 Group:          %{pkg_group}
-Version:        4.1.0+git.1572337494.6f2c8ea9
+Version:        4.1.0+git.1572504697.472361c5
 Release:        0
 Url:            http://crmsh.github.io
 Source0:        %{name}-%{version}.tar.bz2

++++++ _servicedata ++++++
--- /var/tmp/diff_new_pack.WURMsp/_old  2019-11-06 13:56:50.824204464 +0100
+++ /var/tmp/diff_new_pack.WURMsp/_new  2019-11-06 13:56:50.824204464 +0100
@@ -1,4 +1,4 @@
 <servicedata>
 <service name="tar_scm">
             <param name="url">git://github.com/ClusterLabs/crmsh.git</param>
-          <param 
name="changesrevision">6f2c8ea926f61f2ce99fcfbefa401d51feaddbf1</param></service></servicedata>
\ No newline at end of file
+          <param 
name="changesrevision">c8d41bd637dd03b4d60a9f35ca099e41ac32eec4</param></service></servicedata>
\ No newline at end of file

++++++ crmsh-4.1.0+git.1572337494.6f2c8ea9.tar.bz2 -> 
crmsh-4.1.0+git.1572504697.472361c5.tar.bz2 ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/crmsh-4.1.0+git.1572337494.6f2c8ea9/crmsh/bootstrap.py 
new/crmsh-4.1.0+git.1572504697.472361c5/crmsh/bootstrap.py
--- old/crmsh-4.1.0+git.1572337494.6f2c8ea9/crmsh/bootstrap.py  2019-10-29 
09:24:54.000000000 +0100
+++ new/crmsh-4.1.0+git.1572504697.472361c5/crmsh/bootstrap.py  2019-10-31 
07:51:37.000000000 +0100
@@ -670,7 +670,8 @@
     start_service("sshd.service")
     invoke("mkdir -m 700 -p /root/.ssh")
     if os.path.exists("/root/.ssh/id_rsa"):
-        if not confirm("/root/.ssh/id_rsa already exists - overwrite?"):
+        if _context.yes_to_all and _context.no_overwrite_sshkey or \
+                not confirm("/root/.ssh/id_rsa already exists - overwrite?"):
             return
         rmfile("/root/.ssh/id_rsa")
     status("Generating SSH key")
@@ -682,7 +683,10 @@
     """
     Called by ha-cluster-join
     """
-    authkeys = open("/root/.ssh/authorized_keys", "r+")
+    authorized_keys_file = "/root/.ssh/authorized_keys"
+    if not os.path.exists(authorized_keys_file):
+        open(authorized_keys_file, 'w').close()
+    authkeys = open(authorized_keys_file, "r+")
     authkeys_data = authkeys.read()
     for key in ("id_rsa", "id_dsa", "id_ecdsa", "id_ed25519"):
         fn = os.path.join("/root/.ssh", key)
@@ -690,7 +694,7 @@
             continue
         keydata = open(fn + ".pub").read()
         if keydata not in authkeys_data:
-            append(fn + ".pub", "/root/.ssh/authorized_keys")
+            append(fn + ".pub", authorized_keys_file)
 
 
 def init_csync2():
@@ -2056,7 +2060,7 @@
 
 def bootstrap_init(cluster_name="hacluster", ui_context=None, nic=None, 
ocfs2_device=None,
                    shared_device=None, sbd_device=None, diskless_sbd=False, 
quiet=False,
-                   template=None, admin_ip=None, yes_to_all=False,
+                   template=None, admin_ip=None, yes_to_all=False, 
no_overwrite_sshkey=False,
                    unicast=False, second_hb=False, ipv6=False, watchdog=None, 
qdevice=None, stage=None, args=None):
     """
     -i <nic>
@@ -2097,6 +2101,7 @@
     _context.watchdog = watchdog
     _context.ui_context = ui_context
     _context.qdevice = qdevice
+    _context.no_overwrite_sshkey = no_overwrite_sshkey
 
     def check_option():
         if _context.admin_ip and not valid_adminIP(_context.admin_ip):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/crmsh-4.1.0+git.1572337494.6f2c8ea9/crmsh/ui_cluster.py 
new/crmsh-4.1.0+git.1572504697.472361c5/crmsh/ui_cluster.py
--- old/crmsh-4.1.0+git.1572337494.6f2c8ea9/crmsh/ui_cluster.py 2019-10-29 
09:24:54.000000000 +0100
+++ new/crmsh-4.1.0+git.1572504697.472361c5/crmsh/ui_cluster.py 2019-10-31 
07:51:37.000000000 +0100
@@ -187,7 +187,7 @@
         parser.add_option("-q", "--quiet", action="store_true", dest="quiet",
                           help="Be quiet (don't describe what's happening, 
just do it)")
         parser.add_option("-y", "--yes", action="store_true", 
dest="yes_to_all",
-                          help='Answer "yes" to all prompts (use with caution, 
this is destructive, especially during the "storage" stage)')
+                          help='Answer "yes" to all prompts (use with caution, 
this is destructive, especially during the "storage" stage. The 
/root/.ssh/id_rsa key will be overwritten unless the option 
"--no-overwrite-sshkey" is used)')
         parser.add_option("-t", "--template", dest="template",
                           help='Optionally configure cluster with template 
"name" (currently only "ocfs2" is valid here)')
         parser.add_option("-n", "--name", metavar="NAME", dest="name", 
default="hacluster",
@@ -200,6 +200,8 @@
                           help="Enable SBD even if no SBD device is configured 
(diskless mode)")
         parser.add_option("-w", "--watchdog", dest="watchdog", 
metavar="WATCHDOG",
                           help="Use the given watchdog device")
+        parser.add_option("--no-overwrite-sshkey", action="store_true", 
dest="no_overwrite_sshkey",
+                          help='Avoid "/root/.ssh/id_rsa" overwrite if "-y" 
option is used (False by default)')
 
         network_group = optparse.OptionGroup(parser, "Network configuration", 
"Options for configuring the network and messaging layer.")
         network_group.add_option("-i", "--interface", dest="nic", metavar="IF",
@@ -278,6 +280,7 @@
             template=options.template,
             admin_ip=options.admin_ip,
             yes_to_all=options.yes_to_all,
+            no_overwrite_sshkey=options.no_overwrite_sshkey,
             unicast=options.unicast,
             second_hb=options.second_hb,
             ipv6=options.ipv6,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/crmsh-4.1.0+git.1572337494.6f2c8ea9/crmsh/ui_configure.py 
new/crmsh-4.1.0+git.1572504697.472361c5/crmsh/ui_configure.py
--- old/crmsh-4.1.0+git.1572337494.6f2c8ea9/crmsh/ui_configure.py       
2019-10-29 09:24:54.000000000 +0100
+++ new/crmsh-4.1.0+git.1572504697.472361c5/crmsh/ui_configure.py       
2019-10-31 07:51:37.000000000 +0100
@@ -27,6 +27,7 @@
 from . import ui_utils
 from . import ui_assist
 from .crm_gv import gv_types
+from .ui_node import get_resources_on_nodes, remove_redundant_attrs
 
 
 def _type_completions():
@@ -884,6 +885,34 @@
             return True
         return cib_factory.change_schema(schema_st)
 
+    def __override_lower_level_attrs(self, *args):
+        """
+        When setting up an attribute of a cluster, the same
+        attribute may already exist in one of the nodes an/or
+        any resource.
+        The user should be informed about it and, if he wants,
+        he will have an option to delete the already existing
+        attribute.
+        """
+        if not args:
+            return
+
+        nvpair = args[0].split('=', 1)
+        if 2 != len(nvpair):
+            return
+
+        attr_name, attr_value = nvpair
+
+        if "maintenance-mode" == attr_name:
+            attr = "maintenance"
+            conflicting_lower_level_attr = 'is-managed'
+            # FIXME! the first argument is hardcoded
+            objs = get_resources_on_nodes(cib_factory.node_id_list(), [ 
"primitive", "group", "clone"])
+            remove_redundant_attrs(objs, "meta_attributes", attr, 
conflicting_lower_level_attr)
+
+            objs = get_resources_on_nodes(cib_factory.node_id_list(), [ "node" 
])
+            remove_redundant_attrs(objs, "instance_attributes", attr, 
conflicting_lower_level_attr)
+
     def __conf_object(self, cmd, *args):
         "The configure object command."
         if cmd in list(constants.cib_cli_map.values()) and \
@@ -1024,6 +1053,7 @@
     @command.completers_repeating(_property_completer)
     def do_property(self, context, *args):
         "usage: property [$id=<set_id>] <option>=<value>"
+        self.__override_lower_level_attrs(*args)
         return self.__conf_object(context.get_command_name(), *args)
 
     @command.skill_level('administrator')
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/crmsh-4.1.0+git.1572337494.6f2c8ea9/crmsh/ui_node.py 
new/crmsh-4.1.0+git.1572504697.472361c5/crmsh/ui_node.py
--- old/crmsh-4.1.0+git.1572337494.6f2c8ea9/crmsh/ui_node.py    2019-10-29 
09:24:54.000000000 +0100
+++ new/crmsh-4.1.0+git.1572504697.472361c5/crmsh/ui_node.py    2019-10-31 
07:51:37.000000000 +0100
@@ -16,21 +16,26 @@
 from .cibconfig import cib_factory
 from .ui_resource import rm_meta_attribute
 
-def remove_redundant_attrs(objs, attr, conflicting_attr = None):
+def remove_redundant_attrs(objs, attributes_tag, attr, conflicting_attr = 
None):
     """
     Remove attr from all resources_tags in the cib.xml
     """
+    field2show = "id" # if attributes_tag == "meta_attributes"
+    # By default the id of the object should be shown
+    # The id of nodes is simply an integer number => show its uname field
+    if "instance_attributes" == attributes_tag:
+        field2show = "uname"
     # Override the resources on the node
     for r in objs:
-        for meta_set in xmlutil.get_set_nodes(r, "meta_attributes", 
create=False):
+        for meta_set in xmlutil.get_set_nodes(r, attributes_tag, create=False):
             a = xmlutil.get_attr_in_set(meta_set, attr)
             if a is not None and \
                 (config.core.manage_children == "always" or \
                 (config.core.manage_children == "ask" and
                 utils.ask("'%s' attribute already exists in %s. Remove it?" %
-                        (attr, r.get("id"))))):
+                        (attr, r.get(field2show))))):
                 common_debug("force remove meta attr %s from %s" %
-                        (attr, r.get("id")))
+                        (attr, r.get(field2show)))
                 xmlutil.rmnode(a)
                 xmlutil.xml_processnodes(r, xmlutil.is_emptynvpairs, 
xmlutil.rmnodes)
             if conflicting_attr is not None:
@@ -39,9 +44,9 @@
                     (config.core.manage_children == "always" or \
                     (config.core.manage_children == "ask" and
                     utils.ask("'%s' conflicts with '%s' in %s. Remove it?" %
-                            (conflicting_attr, attr, r.get("id"))))):
+                            (conflicting_attr, attr, r.get(field2show))))):
                     common_debug("force remove meta attr %s from %s" %
-                            (conflicting_attr, r.get("id")))
+                            (conflicting_attr, r.get(field2show)))
                     xmlutil.rmnode(a)
                     xmlutil.xml_processnodes(r, xmlutil.is_emptynvpairs, 
xmlutil.rmnodes)
 
@@ -84,7 +89,7 @@
     objs = get_resources_on_nodes([cluster_node_name], [ "primitive", "group", 
"clone"])
 
     # Ask the user to remove the 'attr' attributes on those primitives, groups 
and clones
-    remove_redundant_attrs(objs, attr, conflicting_attr)
+    remove_redundant_attrs(objs, "meta_attributes", attr, conflicting_attr)
 
     # Remove the node conflicting attribute
     nvpairs = xml_node.xpath("./instance_attributes/nvpair[@name='%s']" % 
(conflicting_attr))
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/crmsh-4.1.0+git.1572337494.6f2c8ea9/doc/crm.8.adoc 
new/crmsh-4.1.0+git.1572504697.472361c5/doc/crm.8.adoc
--- old/crmsh-4.1.0+git.1572337494.6f2c8ea9/doc/crm.8.adoc      2019-10-29 
09:24:54.000000000 +0100
+++ new/crmsh-4.1.0+git.1572504697.472361c5/doc/crm.8.adoc      2019-10-31 
07:51:37.000000000 +0100
@@ -3841,6 +3841,9 @@
 available cluster configuration properties, use the
 <<cmdhelp_ra_info,`ra info`>> command with +pengine+, +crmd+,
 +cib+ and +stonithd+ as arguments.
+When setting the +maintenance-mode+ property, it will
+inform the user if there are nodes or resources that
+have the +maintenance+ property.
 
 For more information on rule expressions, see
 <<topics_Syntax_RuleExpressions,Syntax: Rule expressions>>.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/crmsh-4.1.0+git.1572337494.6f2c8ea9/doc/website-v1/man-2.0.adoc 
new/crmsh-4.1.0+git.1572504697.472361c5/doc/website-v1/man-2.0.adoc
--- old/crmsh-4.1.0+git.1572337494.6f2c8ea9/doc/website-v1/man-2.0.adoc 
2019-10-29 09:24:54.000000000 +0100
+++ new/crmsh-4.1.0+git.1572504697.472361c5/doc/website-v1/man-2.0.adoc 
2019-10-31 07:51:37.000000000 +0100
@@ -3471,6 +3471,9 @@
 available cluster configuration properties, use the
 <<cmdhelp_ra_info,`ra info`>> command with +pengine+, +crmd+,
 +cib+ and +stonithd+ as arguments.
+When setting the +maintenance-mode+ property, it will
+inform the user if there are nodes or resources that
+have the +maintenance+ property.
 
 For more information on rule expressions, see
 <<topics_Syntax_RuleExpressions,Syntax: Rule expressions>>.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/crmsh-4.1.0+git.1572337494.6f2c8ea9/doc/website-v1/man-3.adoc 
new/crmsh-4.1.0+git.1572504697.472361c5/doc/website-v1/man-3.adoc
--- old/crmsh-4.1.0+git.1572337494.6f2c8ea9/doc/website-v1/man-3.adoc   
2019-10-29 09:24:54.000000000 +0100
+++ new/crmsh-4.1.0+git.1572504697.472361c5/doc/website-v1/man-3.adoc   
2019-10-31 07:51:37.000000000 +0100
@@ -3732,6 +3732,9 @@
 available cluster configuration properties, use the
 <<cmdhelp_ra_info,`ra info`>> command with +pengine+, +crmd+,
 +cib+ and +stonithd+ as arguments.
+When setting the +maintenance-mode+ property, it will
+inform the user if there are nodes or resources that
+have the +maintenance+ property.
 
 For more information on rule expressions, see
 <<topics_Syntax_RuleExpressions,Syntax: Rule expressions>>.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/crmsh-4.1.0+git.1572337494.6f2c8ea9/test/testcases/confbasic 
new/crmsh-4.1.0+git.1572504697.472361c5/test/testcases/confbasic
--- old/crmsh-4.1.0+git.1572337494.6f2c8ea9/test/testcases/confbasic    
2019-10-29 09:24:54.000000000 +0100
+++ new/crmsh-4.1.0+git.1572504697.472361c5/test/testcases/confbasic    
2019-10-31 07:51:37.000000000 +0100
@@ -85,3 +85,7 @@
 _test
 verify
 .
+-F node maintenance node1
+-F resource maintenance g1 off
+-F resource maintenance d1
+-F configure property maintenance-mode=true
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/crmsh-4.1.0+git.1572337494.6f2c8ea9/test/testcases/confbasic.exp 
new/crmsh-4.1.0+git.1572504697.472361c5/test/testcases/confbasic.exp
--- old/crmsh-4.1.0+git.1572337494.6f2c8ea9/test/testcases/confbasic.exp        
2019-10-29 09:24:54.000000000 +0100
+++ new/crmsh-4.1.0+git.1572504697.472361c5/test/testcases/confbasic.exp        
2019-10-31 07:51:37.000000000 +0100
@@ -147,3 +147,13 @@
        record-pending=true
 .INP: commit
 WARNING: 55: c2: resource d1 is grouped, constraints should apply to the group
+.TRY -F node maintenance node1
+.TRY -F resource maintenance g1 off
+.TRY -F resource maintenance d1
+.TRY -F configure property maintenance-mode=true
+INFO: 'maintenance' attribute already exists in d1. Remove it? [YES]
+INFO: 'maintenance' attribute already exists in g1. Remove it? [YES]
+INFO: 'maintenance' attribute already exists in node1. Remove it? [YES]
+.EXT crmd metadata
+.EXT pengine metadata
+.EXT cib metadata
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/crmsh-4.1.0+git.1572337494.6f2c8ea9/test/unittests/test_bootstrap.py 
new/crmsh-4.1.0+git.1572504697.472361c5/test/unittests/test_bootstrap.py
--- old/crmsh-4.1.0+git.1572337494.6f2c8ea9/test/unittests/test_bootstrap.py    
1970-01-01 01:00:00.000000000 +0100
+++ new/crmsh-4.1.0+git.1572504697.472361c5/test/unittests/test_bootstrap.py    
2019-10-31 07:51:37.000000000 +0100
@@ -0,0 +1,146 @@
+"""
+Unitary tests for crmsh/bootstrap.py
+
+:author: xinliang
+:organization: SUSE Linux GmbH
+:contact: xli...@suse.de
+
+:since: 2019-10-21
+"""
+
+# pylint:disable=C0103,C0111,W0212,W0611
+
+import os
+import unittest
+
+try:
+    from unittest import mock
+except ImportError:
+    import mock
+
+from crmsh import bootstrap
+
+
+class TestBootstrap(unittest.TestCase):
+    """
+    Unitary tests for crmsh/bootstrap.py
+    """
+
+    @classmethod
+    def setUpClass(cls):
+        """
+        Global setUp.
+        """
+
+    #@mock.patch('crmsh.bootstrap.Context')
+    def setUp(self):
+        """
+        Test setUp.
+        """
+
+    def tearDown(self):
+        """
+        Test tearDown.
+        """
+
+    @classmethod
+    def tearDownClass(cls):
+        """
+        Global tearDown.
+        """
+
+    @mock.patch('crmsh.bootstrap.append')
+    @mock.patch('crmsh.bootstrap.status')
+    @mock.patch('os.path.exists')
+    @mock.patch('crmsh.bootstrap.start_service')
+    @mock.patch('crmsh.bootstrap.invoke')
+    def test_init_ssh_no_exist_keys(self, mock_invoke, mock_start_service,
+                                    mock_exists, mock_status, mock_append):
+        mock_exists.return_value = False
+
+        bootstrap.init_ssh()
+
+        mock_start_service.assert_called_once_with("sshd.service")
+        mock_invoke.assert_has_calls([
+            mock.call("mkdir -m 700 -p /root/.ssh"),
+            mock.call("ssh-keygen -q -f /root/.ssh/id_rsa -C 'Cluster 
Internal' -N ''")
+        ])
+        mock_exists.assert_called_once_with("/root/.ssh/id_rsa")
+        mock_status.assert_called_once_with("Generating SSH key")
+        mock_append.assert_called_once_with("/root/.ssh/id_rsa.pub", 
"/root/.ssh/authorized_keys")
+
+    @mock.patch('crmsh.bootstrap.append')
+    @mock.patch('crmsh.bootstrap.status')
+    @mock.patch('crmsh.bootstrap.rmfile')
+    @mock.patch('crmsh.bootstrap.confirm')
+    @mock.patch('os.path.exists')
+    @mock.patch('crmsh.bootstrap.start_service')
+    @mock.patch('crmsh.bootstrap.invoke')
+    def test_init_ssh_exits_keys_yes_to_all_confirm(self, mock_invoke, 
mock_start_service,
+                         mock_exists, mock_confirm, mock_rmfile, mock_status, 
mock_append):
+        mock_exists.return_value = True
+        bootstrap._context = mock.Mock(yes_to_all=True, 
no_overwrite_sshkey=False)
+        mock_confirm.return_value = True
+
+        bootstrap.init_ssh()
+
+        mock_start_service.assert_called_once_with("sshd.service")
+        mock_invoke.assert_has_calls([
+            mock.call("mkdir -m 700 -p /root/.ssh"),
+            mock.call("ssh-keygen -q -f /root/.ssh/id_rsa -C 'Cluster 
Internal' -N ''")
+        ])
+        mock_exists.assert_called_once_with("/root/.ssh/id_rsa")
+        mock_confirm.assert_called_once_with("/root/.ssh/id_rsa already exists 
- overwrite?")
+        mock_rmfile.assert_called_once_with("/root/.ssh/id_rsa")
+        mock_status.assert_called_once_with("Generating SSH key")
+        mock_append.assert_called_once_with("/root/.ssh/id_rsa.pub", 
"/root/.ssh/authorized_keys")
+
+    @mock.patch('crmsh.bootstrap.rmfile')
+    @mock.patch('crmsh.bootstrap.confirm')
+    @mock.patch('os.path.exists')
+    @mock.patch('crmsh.bootstrap.start_service')
+    @mock.patch('crmsh.bootstrap.invoke')
+    def test_init_ssh_exits_keys_no_overwrite(self, mock_invoke, 
mock_start_service,
+                                              mock_exists, mock_confirm, 
mock_rmfile):
+        mock_exists.return_value = True
+        bootstrap._context = mock.Mock(yes_to_all=True, 
no_overwrite_sshkey=True)
+
+        bootstrap.init_ssh()
+
+        mock_start_service.assert_called_once_with("sshd.service")
+        mock_invoke.assert_called_once_with("mkdir -m 700 -p /root/.ssh")
+        mock_exists.assert_called_once_with("/root/.ssh/id_rsa")
+        mock_confirm.assert_not_called()
+        mock_rmfile.assert_not_called()
+
+    @mock.patch('builtins.open')
+    @mock.patch('crmsh.bootstrap.append')
+    @mock.patch('os.path.join')
+    @mock.patch('os.path.exists')
+    def test_init_ssh_remote_no_sshkey(self, mock_exists, mock_join, 
mock_append, mock_open_file):
+        mock_exists.side_effect = [False, True, False, False, False]
+        mock_join.side_effect = ["/root/.ssh/id_rsa",
+                                 "/root/.ssh/id_dsa",
+                                 "/root/.ssh/id_ecdsa",
+                                 "/root/.ssh/id_ed25519"]
+        mock_open_file.side_effect = [
+            mock.mock_open().return_value,
+            mock.mock_open(read_data="data1 data2").return_value,
+            mock.mock_open(read_data="data1111").return_value
+        ]
+
+        bootstrap.init_ssh_remote()
+
+        mock_open_file.assert_has_calls([
+            mock.call("/root/.ssh/authorized_keys", 'w'),
+            mock.call("/root/.ssh/authorized_keys", "r+"),
+            mock.call("/root/.ssh/id_rsa.pub")
+        ])
+        mock_exists.assert_has_calls([
+            mock.call("/root/.ssh/authorized_keys"),
+            mock.call("/root/.ssh/id_rsa"),
+            mock.call("/root/.ssh/id_dsa"),
+            mock.call("/root/.ssh/id_ecdsa"),
+            mock.call("/root/.ssh/id_ed25519"),
+        ])
+        mock_append.assert_called_once_with("/root/.ssh/id_rsa.pub", 
"/root/.ssh/authorized_keys")


Reply via email to