Explorar o código

Merge pull request #95 from Dany9966/ssh-pty

Send sudo SSH commands through a pseudo-terminal.
Nashwan Azhari %!s(int64=6) %!d(string=hai) anos
pai
achega
efdc1f31aa

+ 5 - 4
coriolis/osmorphing/base.py

@@ -125,11 +125,12 @@ class BaseLinuxOSMorphingTools(BaseOSMorphingTools):
         return utils.list_ssh_dir(self._ssh, path)
 
     def _exec_cmd(self, cmd):
-        return utils.exec_ssh_cmd(self._ssh, cmd, self._environment)
+        return utils.exec_ssh_cmd(self._ssh, cmd, self._environment,
+                                  get_pty=True)
 
     def _exec_cmd_chroot(self, cmd):
         return utils.exec_ssh_cmd_chroot(
-            self._ssh, self._os_root_dir, cmd, self._environment)
+            self._ssh, self._os_root_dir, cmd, self._environment, get_pty=True)
 
     def _check_user_exists(self, username):
         try:
@@ -163,7 +164,7 @@ class BaseLinuxOSMorphingTools(BaseOSMorphingTools):
     def _get_config(self, config_content):
         config = {}
         regex_expr = '(.*[^-\\s])\\s*=\\s*(?:"|\')?([^"\']*)(?:"|\')?\\s*'
-        for config_line in config_content.split('\n'):
+        for config_line in config_content.splitlines():
             m = re.match(regex_expr, config_line)
             if m:
                 name, value = m.groups()
@@ -193,7 +194,7 @@ class BaseLinuxOSMorphingTools(BaseOSMorphingTools):
         fstab_chroot_path = "etc/fstab"
         fstab_contents = self._read_file(fstab_chroot_path).decode()
         LOG.debug("Contents of /%s: %s", fstab_chroot_path, fstab_contents)
-        fstab_contents_lines = fstab_contents.split('\n')
+        fstab_contents_lines = fstab_contents.splitlines()
 
         found = False
         regex = "^(%s)" % current_prefix

+ 3 - 2
coriolis/osmorphing/debian.py

@@ -32,8 +32,9 @@ class BaseDebianMorphingTools(base.BaseLinuxOSMorphingTools):
                 return (dist_id, release)
         elif self._test_path(debian_version_path):
             release = self._read_file(
-                debian_version_path).decode().split('\n')[0]
-            return ('Debian', release)
+                debian_version_path).decode().splitlines()
+            if release:
+                return ('Debian', release[0])
 
     def disable_predictable_nic_names(self):
         grub_cfg = os.path.join(

+ 7 - 5
coriolis/osmorphing/oracle.py

@@ -11,11 +11,13 @@ class BaseOracleMorphingTools(redhat.BaseRedHatMorphingTools):
         oracle_release_path = "etc/oracle-release"
         if self._test_path(oracle_release_path):
             release_info = self._read_file(
-                oracle_release_path).decode().split('\n')[0].strip()
-            m = re.match(r"^(.*) release ([0-9].*)$", release_info)
-            if m:
-                distro, version = m.groups()
-                return (distro, version)
+                oracle_release_path).decode().splitlines()
+            if release_info:
+                m = re.match(r"^(.*) release ([0-9].*)$",
+                             release_info[0].strip())
+                if m:
+                    distro, version = m.groups()
+                    return (distro, version)
 
     def _run_dracut(self):
         self._run_dracut_base('kernel')

+ 22 - 20
coriolis/osmorphing/osmount/base.py

@@ -89,7 +89,8 @@ class BaseSSHOSMountTools(BaseOSMountTools):
         pass
 
     def _exec_cmd(self, cmd):
-        return utils.exec_ssh_cmd(self._ssh, cmd, self._environment)
+        return utils.exec_ssh_cmd(self._ssh, cmd, self._environment,
+                                  get_pty=True)
 
     def get_connection(self):
         return self._ssh
@@ -97,7 +98,7 @@ class BaseSSHOSMountTools(BaseOSMountTools):
 
 class BaseLinuxOSMountTools(BaseSSHOSMountTools):
     def _get_pvs(self):
-        out = self._exec_cmd("sudo pvdisplay -c").decode().split("\n")
+        out = self._exec_cmd("sudo pvdisplay -c").decode().splitlines()
         pvs = {}
         for line in out:
             if line == "":
@@ -112,13 +113,14 @@ class BaseLinuxOSMountTools(BaseSSHOSMountTools):
     def _get_vgnames(self):
         vg_names = []
         vgscan_out_lines = self._exec_cmd(
-            "sudo vgscan").decode().split('\n')[1:-1]
-        for vgscan_out_line in vgscan_out_lines:
-            m = re.match(
-                r'\s*Found volume group "(.*)" using metadata type lvm2',
-                vgscan_out_line)
-            if m:
-                vg_names.append(m.groups()[0])
+            "sudo vgscan").decode().splitlines()
+        if len(vgscan_out_lines) > 1:
+            for vgscan_out_line in vgscan_out_lines[1:]:
+                m = re.match(
+                    r'\s*Found volume group "(.*)" using metadata type lvm2',
+                    vgscan_out_line)
+                if m:
+                    vg_names.append(m.groups()[0])
         return vg_names
 
     def _get_lv_paths(self):
@@ -127,7 +129,7 @@ class BaseLinuxOSMountTools(BaseSSHOSMountTools):
         out = self._exec_cmd("sudo lvdisplay -c").decode().strip()
         if out:
             LOG.debug("Decoded `lvdisplay` output data: %s", out)
-            out_lines = out.split('\n')
+            out_lines = out.splitlines()
             for line in out_lines:
                 lvm_vol = line.strip().split(':')
                 if lvm_vol:
@@ -274,7 +276,7 @@ class BaseLinuxOSMountTools(BaseSSHOSMountTools):
 
     def _get_mounted_devices(self):
         mounts = self._exec_cmd(
-            "cat /proc/mounts").decode().split('\n')[:-1]
+            "cat /proc/mounts").decode().splitlines()
         ret = []
         for line in mounts:
             colls = line.split()
@@ -285,7 +287,7 @@ class BaseLinuxOSMountTools(BaseSSHOSMountTools):
 
     def _get_mount_destinations(self):
         mounts = self._exec_cmd(
-            "cat /proc/mounts").decode().split('\n')[:-1]
+            "cat /proc/mounts").decode().splitlines()
         ret = set()
         for line in mounts:
             colls = line.split()
@@ -300,7 +302,7 @@ class BaseLinuxOSMountTools(BaseSSHOSMountTools):
         # Querying for the kernel device name (KNAME) should ensure we get the
         # device names we desire both for physical and logical volumes.
         volume_devs = self._exec_cmd(
-            "lsblk -lnao KNAME").decode().split('\n')[:-1]
+            "lsblk -lnao KNAME").decode().splitlines()
         LOG.debug("All block devices: %s", str(volume_devs))
 
         volume_devs = ["/dev/%s" % d for d in volume_devs if
@@ -321,12 +323,12 @@ class BaseLinuxOSMountTools(BaseSSHOSMountTools):
         dev_name = None
         for dev_path in devices:
             dirs = None
-            tmp_dir = self._exec_cmd('mktemp -d').decode().split('\n')[0]
+            tmp_dir = self._exec_cmd('mktemp -d').decode().splitlines()[0]
             try:
                 self._exec_cmd('sudo mount %s %s' % (dev_path, tmp_dir))
                 # NOTE: it's possible that the device was mounted successfully
                 # but an I/O error occurs later along the line:
-                dirs = self._exec_cmd('ls %s' % tmp_dir).decode().split('\n')
+                dirs = utils.list_ssh_dir(self._ssh, tmp_dir)
             except Exception:
                 self._event_manager.progress_update(
                     "Failed to mount and scan device '%s'" % dev_path)
@@ -374,7 +376,7 @@ class BaseLinuxOSMountTools(BaseSSHOSMountTools):
                 "root partition not found")
 
         try:
-            tmp_dir = self._exec_cmd('mktemp -d').decode().split('\n')[0]
+            tmp_dir = self._exec_cmd('mktemp -d').decode().splitlines()[0]
             self._exec_cmd('sudo mount %s %s' % (os_root_device, tmp_dir))
             os_root_dir = tmp_dir
         except Exception:
@@ -415,7 +417,7 @@ class BaseLinuxOSMountTools(BaseSSHOSMountTools):
         for volume_dev in volume_devs:
             self._exec_cmd("sudo partx -v -a %s || true" % volume_dev)
             dev_paths += self._exec_cmd(
-                "sudo ls %s*" % volume_dev).decode().split('\n')[:-1]
+                "sudo ls -1 %s*" % volume_dev).decode().splitlines()
 
         pvs = self._get_pvs()
         for vg_name in self._get_vgnames():
@@ -428,7 +430,7 @@ class BaseLinuxOSMountTools(BaseSSHOSMountTools):
                 continue
             self._exec_cmd("sudo vgchange -ay %s" % vg_name)
             lvm_dev_paths = self._exec_cmd(
-                "sudo ls /dev/%s/*" % vg_name).decode().split('\n')[:-1]
+                "sudo ls -1 /dev/%s/*" % vg_name).decode().splitlines()
             dev_paths += lvm_dev_paths
 
         valid_filesystems = ['ext2', 'ext3', 'ext4', 'xfs', 'btrfs']
@@ -441,8 +443,8 @@ class BaseLinuxOSMountTools(BaseSSHOSMountTools):
                 continue
             fs_type = self._exec_cmd(
                 "sudo blkid -o value -s TYPE %s || true" %
-                dev_path).decode().split('\n')[0]
-            if fs_type in valid_filesystems:
+                dev_path).decode().splitlines()
+            if fs_type and fs_type[0] in valid_filesystems:
                 if fs_type == "xfs":
                     utils.run_xfs_repair(self._ssh, dev_path)
                 else:

+ 8 - 6
coriolis/osmorphing/redhat.py

@@ -46,11 +46,13 @@ class BaseRedHatMorphingTools(base.BaseLinuxOSMorphingTools):
         redhat_release_path = "etc/redhat-release"
         if self._test_path(redhat_release_path):
             release_info = self._read_file(
-                redhat_release_path).decode().split('\n')[0].strip()
-            m = re.match(r"^(.*) release ([0-9].*) \((.*)\).*$", release_info)
-            if m:
-                distro, version, _ = m.groups()
-                return (distro, version)
+                redhat_release_path).decode().splitlines()
+            if release_info:
+                m = re.match(r"^(.*) release ([0-9].*) \((.*)\).*$",
+                             release_info[0].strip())
+                if m:
+                    distro, version, _ = m.groups()
+                    return (distro, version)
 
     def disable_predictable_nic_names(self):
         kernel_versions = self._list_dir("lib/modules")
@@ -185,7 +187,7 @@ class BaseRedHatMorphingTools(base.BaseLinuxOSMorphingTools):
 
     def _run_dracut_base(self, rpm_base_name):
         package_names = self._exec_cmd_chroot(
-            'rpm -q %s' % rpm_base_name).decode().split('\n')[:-1]
+            'rpm -q %s' % rpm_base_name).decode().splitlines()
         for package_name in package_names:
             m = re.match('^%s-(.*)$' % rpm_base_name, package_name)
             if m:

+ 4 - 3
coriolis/osmorphing/suse.py

@@ -27,14 +27,15 @@ class BaseSUSEMorphingTools(base.BaseLinuxOSMorphingTools):
         suse_release_path = "etc/SuSE-release"
         if self._test_path(suse_release_path):
             out = self._read_file(suse_release_path).decode()
-            release = out.split('\n')[0]
+            release = out.splitlines()
             release_info = self._get_config(out)
             version_id = release_info['VERSION']
             patch_level = release_info.get('PATCHLEVEL', None)
             if patch_level:
                 version_id = "%s.%s" % (version_id, patch_level)
             self._version_id = version_id
-            return ('SUSE', release)
+            if release:
+                return ('SUSE', release[0])
 
     def disable_predictable_nic_names(self):
         # TODO(gsamfira): implement once we have networking support
@@ -46,7 +47,7 @@ class BaseSUSEMorphingTools(base.BaseLinuxOSMorphingTools):
 
     def _run_dracut(self):
         package_names = self._exec_cmd_chroot(
-            'rpm -q kernel-default').decode().split('\n')[:-1]
+            'rpm -q kernel-default').decode().splitlines()
         for package_name in package_names:
             m = re.match(r'^kernel-default-(.*)\.\d\..*$', package_name)
             if m:

+ 1 - 1
coriolis/providers/backup_writers.py

@@ -580,7 +580,7 @@ class HTTPBackupWriter(BaseBackupWriter):
         utils.exec_ssh_cmd(
             ssh,
             "sudo /sbin/iptables -I INPUT -p tcp --dport %s "
-            "-j ACCEPT" % self._writer_port)
+            "-j ACCEPT" % self._writer_port, get_pty=True)
 
     def _get_impl(self, path, disk_id):
         ssh = self._connect_ssh()

+ 9 - 7
coriolis/utils.py

@@ -277,15 +277,16 @@ def exec_ssh_cmd(ssh, cmd, environment=None, get_pty=False):
     return std_out
 
 
-def exec_ssh_cmd_chroot(ssh, chroot_dir, cmd, environment=None):
+def exec_ssh_cmd_chroot(ssh, chroot_dir, cmd, environment=None, get_pty=False):
     return exec_ssh_cmd(ssh, "sudo -E chroot %s %s" % (chroot_dir, cmd),
-                        environment=environment)
+                        environment=environment, get_pty=get_pty)
 
 
 def check_fs(ssh, fs_type, dev_path):
     try:
         out = exec_ssh_cmd(
-            ssh, "sudo fsck -p -t %s %s" % (fs_type, dev_path)).decode()
+            ssh, "sudo fsck -p -t %s %s" % (fs_type, dev_path),
+            get_pty=True).decode()
         LOG.debug("File system checked:\n%s", out)
     except Exception as ex:
         LOG.warn("Checking file system returned an error:\n%s", str(ex))
@@ -297,14 +298,15 @@ def run_xfs_repair(ssh, dev_path):
             ssh, "mktemp -d").decode().rstrip("\n")
         LOG.debug("mounting %s on %s" % (dev_path, tmp_dir))
         mount_out = exec_ssh_cmd(
-            ssh, "sudo mount %s %s" % (dev_path, tmp_dir)).decode()
+            ssh, "sudo mount %s %s" % (dev_path, tmp_dir),
+            get_pty=True).decode()
         LOG.debug("mount returned: %s" % mount_out)
         LOG.debug("Umounting %s" % tmp_dir)
         umount_out = exec_ssh_cmd(
-            ssh, "sudo umount %s" % tmp_dir).decode()
+            ssh, "sudo umount %s" % tmp_dir, get_pty=True).decode()
         LOG.debug("umounting returned: %s" % umount_out)
         out = exec_ssh_cmd(
-            ssh, "sudo xfs_repair %s" % dev_path).decode()
+            ssh, "sudo xfs_repair %s" % dev_path, get_pty=True).decode()
         LOG.debug("File system repaired:\n%s", out)
     except Exception as ex:
         LOG.warn("xfs_repair returned an error:\n%s", str(ex))
@@ -646,7 +648,7 @@ class Grub2ConfigEditor(object):
 
     def _parse_cfg(self, cfg):
         ret = []
-        for line in cfg.split("\n")[:-1]:
+        for line in cfg.splitlines():
             if line.startswith("#") or len(line.strip()) == 0:
                 ret.append(
                     {