]> git.apps.os.sepia.ceph.com Git - ceph-ci.git/commitdiff
mgr/ssh: simplify ssh connection management
authorSage Weil <sage@redhat.com>
Tue, 1 Oct 2019 15:29:53 +0000 (10:29 -0500)
committerSage Weil <sage@redhat.com>
Sat, 5 Oct 2019 01:33:35 +0000 (20:33 -0500)
- Use a single instance of the config and identity files for the whole
module.  There's no need to create these for *every* connection--it just
pollutes /tmp.
- Drop the SSHConnection wrapper, since the temp files are tied to the
daemon lifecycle now.
- Prefix the tmp files so I can tell wtf is going on.
- Always connect to root@host, to avoid remoto's localhost detection
feature.  This ensures we have a consistent connection model and user.
(The daemon might be running as user ceph and try to connect to localhost,
but end up running the command as the wrong user and/or inside the
ceph-mgr container.)

Signed-off-by: Sage Weil <sage@redhat.com>
src/pybind/mgr/ssh/module.py

index bf957d81034120fb52198a3a16bb7e1311ee4e74..8daa28da4c4da982aefd106f09d4ab15427f98bc 100644 (file)
@@ -88,19 +88,6 @@ class SSHWriteCompletionReady(SSHWriteCompletion):
     def is_errored(self):
         return False
 
-class SSHConnection(object):
-    """
-    Tie tempfile lifetime (e.g. ssh_config) to a remoto connection.
-    """
-    def __init__(self):
-        self.conn = None
-        self.temp_files = []
-
-    # proxy to the remoto connection
-    def __getattr__(self, name):
-        return getattr(self.conn, name)
-
-
 def log_exceptions(f):
     if six.PY3:
         return f
@@ -145,6 +132,8 @@ class SSHOrchestrator(MgrModule, orchestrator.Orchestrator):
         self._cluster_fsid = None
         self._worker_pool = multiprocessing.pool.ThreadPool(1)
 
+        self._reconfig_ssh()
+
         # the keys in inventory_cache are authoritative.
         #   You must not call remove_outdated()
         # The values are cached by instance.
@@ -153,6 +142,52 @@ class SSHOrchestrator(MgrModule, orchestrator.Orchestrator):
         # 2. refresh parameter
         self.inventory_cache = orchestrator.OutdatablePersistentDict(self, self._STORE_HOST_PREFIX)
 
+    def _reconfig_ssh(self):
+        temp_files = []
+        ssh_options = []
+
+        # ssh_config
+        ssh_config_fname = self.get_localized_module_option("ssh_config_file")
+        ssh_config = self.get_store("ssh_config")
+        if ssh_config is not None or ssh_config_fname is None:
+            if not ssh_config:
+                ssh_config = DEFAULT_SSH_CONFIG
+            f = tempfile.NamedTemporaryFile(prefix='ceph-mgr-ssh-conf-')
+            os.fchmod(f.fileno(), 0o600);
+            f.write(ssh_config.encode('utf-8'))
+            f.flush() # make visible to other processes
+            temp_files += [f]
+            ssh_config_fname = f.name
+        if ssh_config_fname:
+            if not os.path.isfile(ssh_config_fname):
+                raise Exception("ssh_config \"{}\" does not exist".format(
+                    ssh_config_fname))
+            ssh_options += ['-F', ssh_config_fname]
+
+        # identity
+        ssh_key = self.get_store("ssh_identity_key")
+        ssh_pub = self.get_store("ssh_identity_pub")
+        tpub = None
+        tkey = None
+        if ssh_key and ssh_pub:
+            tkey = tempfile.NamedTemporaryFile(prefix='ceph-mgr-ssh-identity-')
+            tkey.write(ssh_key.encode('utf-8'))
+            os.fchmod(tkey.fileno(), 0o600);
+            tkey.flush() # make visible to other processes
+            tpub = open(tkey.name + '.pub', 'w')
+            os.fchmod(tpub.fileno(), 0o600);
+            tpub.write(ssh_pub)
+            tpub.flush() # make visible to other processes
+            temp_files += [tkey, tpub]
+            ssh_options += ['-i', tkey.name]
+
+        self._temp_files = temp_files
+        if ssh_options:
+            self._ssh_options = ' '.join(ssh_options)
+        else:
+            self._ssh_options = None
+        self.log.info('ssh_options %s' % ssh_options)
+
     def handle_command(self, inbuf, command):
         if command["prefix"] == "ssh set-ssh-config":
             return self._set_ssh_config(inbuf, command)
@@ -238,54 +273,15 @@ class SSHOrchestrator(MgrModule, orchestrator.Orchestrator):
         """
         Setup a connection for running commands on remote host.
         """
-        ssh_options = None
-
-        conn = SSHConnection()
-
-        # ssh_config
-        ssh_config_fname = self.get_localized_module_option("ssh_config_file")
-        ssh_config = self.get_store("ssh_config")
-        if ssh_config is not None or ssh_config_fname is None:
-            if not ssh_config:
-                ssh_config = DEFAULT_SSH_CONFIG
-            f = tempfile.NamedTemporaryFile()
-            os.fchmod(f.fileno(), 0o600);
-            f.write(ssh_config.encode('utf-8'))
-            f.flush() # make visible to other processes
-            conn.temp_files += [f]
-            ssh_config_fname = f.name
-        if ssh_config_fname:
-            if not os.path.isfile(ssh_config_fname):
-                raise Exception("ssh_config \"{}\" does not exist".format(
-                    ssh_config_fname))
-            ssh_options = "-F {}".format(ssh_config_fname)
-
-        # identity
-        ssh_key = self.get_store("ssh_identity_key")
-        ssh_pub = self.get_store("ssh_identity_pub")
-        if ssh_key and ssh_pub:
-            tkey = tempfile.NamedTemporaryFile()
-            tkey.write(ssh_key.encode('utf-8'))
-            os.fchmod(tkey.fileno(), 0o600);
-            tkey.flush() # make visible to other processes
-            tpub = tempfile.NamedTemporaryFile()
-            os.fchmod(tpub.fileno(), 0o600);
-            tpub.write(ssh_pub.encode('utf-8'))
-            tpub.flush() # make visible to other processes
-            conn.temp_files += [tkey, tpub]
-            if not ssh_options:
-                ssh_options = ''
-            ssh_options += '-i {}'.format(tkey.name)
-
         self.log.info("opening connection to host '{}' with ssh "
-                "options '{}'".format(host, ssh_options))
+                "options '{}'".format(host, self._ssh_options))
 
-        conn.conn = remoto.Connection(host,
-                logger=self.log,
-                detect_sudo=True,
-                ssh_options=ssh_options)
+        conn = remoto.Connection(
+            'root@' + host,
+            logger=self.log,
+            ssh_options=self._ssh_options)
 
-        conn.conn.import_module(remotes)
+        conn.import_module(remotes)
 
         return conn