Просмотр исходного кода

Add user-friendly messages for some OSMorphing errors.

Daniel Vincze 5 лет назад
Родитель
Сommit
c9728aeaba

+ 26 - 0
coriolis/exception.py

@@ -440,3 +440,29 @@ class NoSuitableWorkerServiceError(NoServiceError):
     message = _(
         "No suitable Coriolis Worker service was found which fits the "
         "criteria for the required operation.")
+
+
+class OSMorphingException(CoriolisException):
+    pass
+
+
+class PackageManagerOperationException(OSMorphingException):
+    pass
+
+
+class FailedPackageInstallationException(PackageManagerOperationException):
+    message = (
+        "Failed to install required packages %(package_names)s through "
+        "%(package_manager)s. Please ensure that the required packages are "
+        "available within the %(package_manager)s repositories configured "
+        "within the source machine. If not, please either add or enable "
+        "additional repositories within the source machine which contain the "
+        "packages Coriolis requires, or attempt to manually install the "
+        "packages on the source machine and then migrate the VM using Coriolis"
+        " with the OSMorphing process disabled. Error was: %(error)s")
+
+
+class FailedPackageUninstallationException(PackageManagerOperationException):
+    message = (
+        "Failed to remove unwanted packages (%(package_names)s) through "
+        "%(package_manager)s. Error was: %(error)s")

+ 35 - 10
coriolis/osmorphing/debian.py

@@ -6,6 +6,7 @@ from io import StringIO
 
 import yaml
 
+from coriolis import exception
 from coriolis import utils
 from coriolis.osmorphing import base
 from coriolis.osmorphing.osdetect import debian as debian_osdetect
@@ -121,17 +122,41 @@ class BaseDebianMorphingTools(base.BaseLinuxOSMorphingTools):
     def pre_packages_install(self, package_names):
         super(BaseDebianMorphingTools, self).pre_packages_install(
             package_names)
-
-        if package_names:
-            self._event_manager.progress_update("Updating packages list")
-            self._exec_cmd_chroot('apt-get clean')
-            self._exec_cmd_chroot('apt-get update -y')
+        try:
+            if package_names:
+                self._event_manager.progress_update("Updating packages list")
+                self._exec_cmd_chroot('apt-get clean')
+                self._exec_cmd_chroot('apt-get update -y')
+        except Exception as err:
+            raise exception.PackageManagerOperationException(
+                "Failed to refresh apt repositories. Please ensure that *all* "
+                "of the apt repositories configured within the source machine "
+                "exist, are properly configured, and are reachable from the "
+                "virtual network on the target platform which the OSMorphing "
+                "minion machine is running on. If there are repositories "
+                "configured within the source machine which are local, "
+                "private, or otherwise unreachable from the target platform, "
+                "please either try disabling the repositories within the "
+                "source machine, or try to set up a mirror of said "
+                "repositories which will be reachable from the temporary "
+                "OSMorphing minion machine on the target platform. Original "
+                "error was: %s" % str(err)) from err
 
     def install_packages(self, package_names):
-        apt_get_cmd = 'apt-get install %s -y' % " ".join(package_names)
-        self._exec_cmd_chroot(apt_get_cmd)
+        try:
+            apt_get_cmd = 'apt-get install %s -y' % " ".join(package_names)
+            self._exec_cmd_chroot(apt_get_cmd)
+        except Exception as err:
+            raise exception.FailedPackageInstallationException(
+                package_names=package_names, package_manager='apt',
+                error=str(err)) from err
 
     def uninstall_packages(self, package_names):
-        for package_name in package_names:
-            apt_get_cmd = 'apt-get remove %s -y || true' % package_name
-            self._exec_cmd_chroot(apt_get_cmd)
+        try:
+            for package_name in package_names:
+                apt_get_cmd = 'apt-get remove %s -y || true' % package_name
+                self._exec_cmd_chroot(apt_get_cmd)
+        except exception.CoriolisException as err:
+            raise exception.FailedPackageUninstallationException(
+                package_names=package_names, package_manager='apt',
+                error=str(err)) from err

+ 22 - 10
coriolis/osmorphing/manager.py

@@ -131,7 +131,14 @@ def morph_image(origin_provider, destination_provider, connection_info,
     os_mount_tools.set_proxy(proxy_settings)
 
     LOG.info("Preparing for OS partitions discovery")
-    os_mount_tools.setup()
+    try:
+        os_mount_tools.setup()
+    except Exception as err:
+        raise exception.CoriolisException(
+            "Failed to set up the minion machine for OSMorphing. This may be "
+            "due to an incompatibility between the OS image used for the "
+            "OSMorphing minion machine and the VM undergoing OSMorphing. "
+            "Error was: %s" % str(err)) from err
 
     event_manager.progress_update("Discovering and mounting OS partitions")
     os_root_dir, os_root_dev = os_mount_tools.mount_os()
@@ -230,17 +237,22 @@ def morph_image(origin_provider, destination_provider, connection_info,
     if packages_add:
         event_manager.progress_update(
             "Adding packages: %s" % str(packages_add))
-        try:
-            import_os_morphing_tools.install_packages(
-                packages_add)
-        except Exception as err:
-            raise exception.CoriolisException(
-                "Failed to install packages: %s. Please review logs"
-                " for more details." % ", ".join(
-                    packages_add)) from err
+
+        import_os_morphing_tools.install_packages(
+            packages_add)
 
     LOG.info("Post packages install")
     import_os_morphing_tools.post_packages_install(packages_add)
 
     event_manager.progress_update("Dismounting OS partitions")
-    os_mount_tools.dismount_os(os_root_dir)
+    try:
+        os_mount_tools.dismount_os(os_root_dir)
+    except Exception as err:
+        raise exception.CoriolisException(
+            "Failed to dismount the OS undergoing OSMorphing. This could have "
+            "been caused by minor FS corruption during the last disk sync. "
+            "Please ensure that any source-side FS integrity mechanisms (e.g. "
+            "filesystem quiescing, crash-consistent backups, etc.) are "
+            "enabled and available for the source machine. If none are "
+            "available, please try migrating/replicating the source machine "
+            "while it is powered off. Error was: %s" % str(err)) from err

+ 39 - 4
coriolis/osmorphing/osmount/base.py

@@ -391,15 +391,36 @@ class BaseLinuxOSMountTools(BaseSSHOSMountTools):
 
         if os_root_device is None:
             raise exception.OperatingSystemNotFound(
-                "root partition not found")
+                "Coriolis was unable to identify the root partition of the OS "
+                "being migrated for mounting during OSMorphing. Please ensure "
+                "that the source VM's root partition(s) are not encrypted, "
+                "and that they are using a filesystem type and version which "
+                "is supported by the OS images used for the OSMorphing minion "
+                "machine. Also ensure that the source VM's mountpoint "
+                "declarations in '/etc/fstab' are all correct, and are "
+                "declared using '/dev/disk/by-uuid/' or 'UUID=' notation. "
+                "If all else fails, please retry while using an OSMorphing "
+                "minion machine image which is the same OS release as the VM "
+                "being migrated.")
 
         try:
             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:
+        except Exception as ex:
             self._event_manager.progress_update(
-                "Failed to mount root device '%s'" % os_root_device)
+                "Exception occurred while Coriolis was attempting to mount the"
+                " root device (%s) of the OS being migrated for OSMorphing. "
+                "Please ensure that the source VM's root partition(s) are "
+                "using a filesystem type and version which is supported by the"
+                " OS images used for the OSMorphing minion machine. Also "
+                "ensure that the source VM's mountpoint declarations in "
+                "'/etc/fstab' are all correct, and are declared using "
+                "'/dev/disk/by-uuid/' or 'UUID=' notation. If all else fails, "
+                "please retry while using an OSMorphing minion machine image "
+                "which is the same OS release as the VM being migrated. Error "
+                "was: %s" % (os_root_device, str(ex)))
+            LOG.error(ex)
             LOG.warn(
                 "Failed to mount root device '%s':\n%s",
                 os_root_device, utils.get_exception_details())
@@ -476,7 +497,21 @@ class BaseLinuxOSMountTools(BaseSSHOSMountTools):
                 if fs_type == "xfs":
                     utils.run_xfs_repair(self._ssh, dev_path)
                 else:
-                    utils.check_fs(self._ssh, fs_type, dev_path)
+                    try:
+                        utils.check_fs(self._ssh, fs_type, dev_path)
+                    except Exception as err:
+                        self._event_manager.progress_update(
+                            "Coriolis failed to check one of the migrated VM's"
+                            " filesystems. This could have been caused by "
+                            "FS corruption during the last disk sync. If "
+                            "the migration fails, please ensure that any "
+                            "source-side FS integrity mechanisms (e.g. "
+                            "filesystem quiescing, crash-consistent backups, "
+                            "etc.) are enabled and available for the source "
+                            "machine. If none are available, please try "
+                            "migrating/replicating the source machine while it"
+                            " is powered off. Error was: %s" % str(err))
+                        LOG.error(err)
                 dev_paths_to_mount.append(dev_path)
 
         os_root_dir, os_root_device = self._find_and_mount_root(

+ 18 - 7
coriolis/osmorphing/redhat.py

@@ -7,6 +7,7 @@ import uuid
 
 from oslo_log import log as logging
 
+from coriolis import exception
 from coriolis import utils
 from coriolis.osmorphing import base
 from coriolis.osmorphing.osdetect import centos as centos_detect
@@ -156,15 +157,25 @@ class BaseRedHatMorphingTools(base.BaseLinuxOSMorphingTools):
         self._add_net_udev_rules(net_ifaces_info)
 
     def _yum_install(self, package_names, enable_repos=[]):
-        yum_cmd = 'yum install %s -y%s' % (
-            " ".join(package_names),
-            "".join([" --enablerepo=%s" % r for r in enable_repos]))
-        self._exec_cmd_chroot(yum_cmd)
+        try:
+            yum_cmd = 'yum install %s -y%s' % (
+                " ".join(package_names),
+                "".join([" --enablerepo=%s" % r for r in enable_repos]))
+            self._exec_cmd_chroot(yum_cmd)
+        except exception.CoriolisException as err:
+            raise exception.FailedPackageInstallationException(
+                package_names=package_names, package_manager='yum',
+                error=str(err)) from err
 
     def _yum_uninstall(self, package_names):
-        for package_name in package_names:
-            yum_cmd = 'yum remove %s -y' % package_name
-            self._exec_cmd_chroot(yum_cmd)
+        try:
+            for package_name in package_names:
+                yum_cmd = 'yum remove %s -y' % package_name
+                self._exec_cmd_chroot(yum_cmd)
+        except exception.CoriolisException as err:
+            raise exception.FailedPackageUninstallationException(
+                package_names=package_names, package_manager='yum',
+                error=str(err)) from err
 
     def _yum_clean_all(self):
         self._exec_cmd_chroot("yum clean all")

+ 12 - 4
coriolis/osmorphing/suse.py

@@ -123,8 +123,10 @@ class BaseSUSEMorphingTools(base.BaseLinuxOSMorphingTools):
                 "zypper --non-interactive --no-gpg-checks refresh")
         except Exception as err:
             raise exception.CoriolisException(
-                "Failed to activate SLES module: %s. Please review logs"
-                " for more details." % module) from err
+                "Failed to activate SLES module: %s. Please check whether the "
+                "SUSE system registration is still valid on the source VM "
+                "and retry. Review logs for more details. Error was: %s" % (
+                    module, str(err))) from err
 
     def _add_cloud_tools_repo(self):
         repo_suffix = ""
@@ -148,8 +150,14 @@ class BaseSUSEMorphingTools(base.BaseLinuxOSMorphingTools):
                 " for more details." % repo) from err
 
     def install_packages(self, package_names):
-        self._exec_cmd_chroot(
-            'zypper --non-interactive install %s' % " ".join(package_names))
+        try:
+            self._exec_cmd_chroot(
+                'zypper --non-interactive install %s' % " ".join(package_names)
+            )
+        except exception.CoriolisException as err:
+            raise exception.FailedPackageInstallationException(
+                package_names=package_names, package_manager='zypper',
+                error=str(err)) from err
 
     def uninstall_packages(self, package_names):
         try:

+ 4 - 2
coriolis/utils.py

@@ -334,8 +334,10 @@ def check_fs(ssh, fs_type, dev_path):
             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))
+    except Exception:
+        LOG.warn("Checking file system returned an error:\n%s" % (
+            get_exception_details()))
+        raise
 
 
 def run_xfs_repair(ssh, dev_path):