Преглед изворни кода

Merged in alexpilotti/coriolis (pull request #39)

OSMorphing refactoring
Alessandro Pilotti пре 9 година
родитељ
комит
f365d9cdbb
52 измењених фајлова са 455 додато и 5455 уклоњено
  1. 0 1
      coriolis/api/v1/views/migration_view.py
  2. 12 1
      coriolis/api/v1/views/replica_tasks_execution_view.py
  3. 0 4
      coriolis/api/wsgi.py
  4. 4 4
      coriolis/cmd/api.py
  5. 5 5
      coriolis/cmd/conductor.py
  6. 5 5
      coriolis/cmd/worker.py
  7. 40 8
      coriolis/conductor/rpc/server.py
  8. 8 0
      coriolis/constants.py
  9. 2 1
      coriolis/db/sqlalchemy/migrate_repo/manage.py
  10. 4 0
      coriolis/exception.py
  11. 22 13
      coriolis/osmorphing/base.py
  12. 26 0
      coriolis/osmorphing/coreos.py
  13. 3 9
      coriolis/osmorphing/debian.py
  14. 0 45
      coriolis/osmorphing/factory.py
  15. 60 22
      coriolis/osmorphing/manager.py
  16. 27 0
      coriolis/osmorphing/openwrt.py
  17. 1 44
      coriolis/osmorphing/oracle.py
  18. 0 3
      coriolis/osmorphing/osmount/windows.py
  19. 9 63
      coriolis/osmorphing/redhat.py
  20. 2 45
      coriolis/osmorphing/suse.py
  21. 1 11
      coriolis/osmorphing/ubuntu.py
  22. 1 210
      coriolis/osmorphing/windows.py
  23. 0 1013
      coriolis/providers/azure/__init__.py
  24. 0 20
      coriolis/providers/azure/exceptions.py
  25. 0 57
      coriolis/providers/azure/schemas/connection_info_schema.json
  26. 0 98
      coriolis/providers/azure/schemas/target_environment_schema.json
  27. 0 101
      coriolis/providers/azure/utils.py
  28. 1 1
      coriolis/providers/backup_writers.py
  29. 48 0
      coriolis/providers/base.py
  30. 10 7
      coriolis/providers/factory.py
  31. 0 5
      coriolis/providers/openstack/__init__.py
  32. 0 329
      coriolis/providers/openstack/common.py
  33. 0 479
      coriolis/providers/openstack/exp.py
  34. 0 1033
      coriolis/providers/openstack/imp.py
  35. 0 64
      coriolis/providers/openstack/schemas/connection_info_schema.json
  36. 0 57
      coriolis/providers/openstack/schemas/target_environment_schema.json
  37. 0 647
      coriolis/providers/vmware_vsphere/__init__.py
  38. 0 250
      coriolis/providers/vmware_vsphere/guestid.py
  39. 0 42
      coriolis/providers/vmware_vsphere/schemas/connection_info_schema.json
  40. 0 244
      coriolis/providers/vmware_vsphere/vixdisklib.py
  41. 1 2
      coriolis/schemas_exceptions.py
  42. 0 37
      coriolis/service.py
  43. 28 0
      coriolis/tasks/base.py
  44. 11 0
      coriolis/tasks/factory.py
  45. 39 1
      coriolis/tasks/migration_tasks.py
  46. 33 0
      coriolis/tasks/osmorphing_tasks.py
  47. 48 34
      coriolis/tasks/replica_tasks.py
  48. 0 0
      coriolis/tests/providers/azure/__init__.py
  49. 0 427
      coriolis/tests/providers/azure/test_azure_import.py
  50. 3 1
      coriolis/tests/providers/base.py
  51. 1 0
      coriolis/tests/testutils.py
  52. 0 12
      requirements.txt

+ 0 - 1
coriolis/api/v1/views/migration_view.py

@@ -15,7 +15,6 @@ def _format_migration(req, migration, keys=None):
     migration_dict = dict(itertools.chain.from_iterable(
     migration_dict = dict(itertools.chain.from_iterable(
         transform(k, v) for k, v in migration.items()))
         transform(k, v) for k, v in migration.items()))
 
 
-    # Migrations have a single tasks execution
     execution = replica_tasks_execution_view.format_replica_tasks_execution(
     execution = replica_tasks_execution_view.format_replica_tasks_execution(
         req, migration_dict["executions"][0])
         req, migration_dict["executions"][0])
 
 

+ 12 - 1
coriolis/api/v1/views/replica_tasks_execution_view.py

@@ -9,7 +9,18 @@ from coriolis import utils
 
 
 def _sort_tasks(tasks):
 def _sort_tasks(tasks):
     non_error_only_tasks = [t for t in tasks if
     non_error_only_tasks = [t for t in tasks if
-                            t["depends_on"] or not t["on_error"]]
+                            not t["depends_on"] and not t["on_error"]]
+
+    def _add_non_error_tasks(task_id):
+        for t in tasks:
+            if (t["depends_on"] and task_id in t["depends_on"] and
+                    t not in non_error_only_tasks):
+                non_error_only_tasks.append(t)
+                _add_non_error_tasks(t["id"])
+
+    for t in non_error_only_tasks:
+        _add_non_error_tasks(t["id"])
+
     # Include error only tasks only if executed
     # Include error only tasks only if executed
     error_only_tasks = [t for t in tasks if t["status"] !=
     error_only_tasks = [t for t in tasks if t["status"] !=
                         constants.TASK_STATUS_ON_ERROR_ONLY and
                         constants.TASK_STATUS_ON_ERROR_ONLY and

+ 0 - 4
coriolis/api/wsgi.py

@@ -1224,9 +1224,6 @@ class Fault(webob.exc.HTTPException):
             if retry:
             if retry:
                 fault_data[fault_name]['retryAfter'] = retry
                 fault_data[fault_name]['retryAfter'] = retry
 
 
-        # 'code' is an attribute on the fault tag itself
-        metadata = {'attributes': {fault_name: 'code'}}
-
         content_type = req.best_match_content_type()
         content_type = req.best_match_content_type()
         serializer = {
         serializer = {
             'application/json': JSONDictSerializer(),
             'application/json': JSONDictSerializer(),
@@ -1299,7 +1296,6 @@ class OverLimitFault(webob.exc.HTTPException):
     def __call__(self, request):
     def __call__(self, request):
         """Serializes the wrapped exception conforming to our error format."""
         """Serializes the wrapped exception conforming to our error format."""
         content_type = request.best_match_content_type()
         content_type = request.best_match_content_type()
-        metadata = {"attributes": {"overLimitFault": "code"}}
 
 
         def translate(msg):
         def translate(msg):
             locale = request.best_match_language()
             locale = request.best_match_language()

+ 4 - 4
coriolis/cmd/api.py

@@ -4,12 +4,12 @@
 import eventlet
 import eventlet
 eventlet.monkey_patch()
 eventlet.monkey_patch()
 
 
-import sys
+import sys # noqa
 
 
-from coriolis import service
-from coriolis import utils
+from coriolis import service # noqa
+from coriolis import utils # noqa
 
 
-from oslo_config import cfg
+from oslo_config import cfg # noqa
 
 
 CONF = cfg.CONF
 CONF = cfg.CONF
 
 

+ 5 - 5
coriolis/cmd/conductor.py

@@ -4,13 +4,13 @@
 import eventlet
 import eventlet
 eventlet.monkey_patch()
 eventlet.monkey_patch()
 
 
-import sys
+import sys # noqa
 
 
-from oslo_config import cfg
+from oslo_config import cfg # noqa
 
 
-from coriolis.conductor.rpc import server as rpc_server
-from coriolis import service
-from coriolis import utils
+from coriolis.conductor.rpc import server as rpc_server # noqa
+from coriolis import service # noqa
+from coriolis import utils # noqa
 
 
 CONF = cfg.CONF
 CONF = cfg.CONF
 
 

+ 5 - 5
coriolis/cmd/worker.py

@@ -4,13 +4,13 @@
 import eventlet
 import eventlet
 eventlet.monkey_patch()
 eventlet.monkey_patch()
 
 
-import sys
+import sys # noqa
 
 
-from oslo_config import cfg
+from oslo_config import cfg # noqa
 
 
-from coriolis.worker.rpc import server as rpc_server
-from coriolis import service
-from coriolis import utils
+from coriolis.worker.rpc import server as rpc_server # noqa
+from coriolis import service # noqa
+from coriolis import utils # noqa
 
 
 CONF = cfg.CONF
 CONF = cfg.CONF
 
 

+ 40 - 8
coriolis/conductor/rpc/server.py

@@ -75,11 +75,16 @@ class ConductorServerEndpoint(object):
         task.depends_on = depends_on
         task.depends_on = depends_on
         task.on_error = on_error
         task.on_error = on_error
 
 
-        if not depends_on and on_error:
-            task.status = constants.TASK_STATUS_ON_ERROR_ONLY
-        else:
+        if not on_error:
             task.status = constants.TASK_STATUS_PENDING
             task.status = constants.TASK_STATUS_PENDING
-
+        else:
+            task.status = constants.TASK_STATUS_ON_ERROR_ONLY
+            if depends_on:
+                for task_id in depends_on:
+                    if [t for t in task.execution.tasks if t.id == task_id and
+                            t.status != constants.TASK_STATUS_ON_ERROR_ONLY]:
+                        task.status = constants.TASK_STATUS_PENDING
+                        break
         return task
         return task
 
 
     def _begin_tasks(self, ctxt, execution, task_info={}):
     def _begin_tasks(self, ctxt, execution, task_info={}):
@@ -349,16 +354,31 @@ class ConductorServerEndpoint(object):
                 instance, constants.TASK_TYPE_DEPLOY_REPLICA_INSTANCE,
                 instance, constants.TASK_TYPE_DEPLOY_REPLICA_INSTANCE,
                 execution, [create_snapshot_task.id])
                 execution, [create_snapshot_task.id])
 
 
+            osmorphing_task = self._create_task(
+                instance, constants.TASK_TYPE_OS_MORPHING,
+                execution, depends_on=[deploy_replica_task.id])
+
+            finalize_deployment_task = self._create_task(
+                instance,
+                constants.TASK_TYPE_FINALIZE_REPLICA_INSTANCE_DEPLOYMENT,
+                execution, depends_on=[osmorphing_task.id])
+
             self._create_task(
             self._create_task(
                 instance, constants.TASK_TYPE_DELETE_REPLICA_DISK_SNAPSHOTS,
                 instance, constants.TASK_TYPE_DELETE_REPLICA_DISK_SNAPSHOTS,
-                execution, [deploy_replica_task.id],
+                execution, depends_on=[finalize_deployment_task.id],
                 on_error=clone_disks)
                 on_error=clone_disks)
 
 
+            cleanup_deployment_task = self._create_task(
+                instance,
+                constants.TASK_TYPE_CLEANUP_FAILED_REPLICA_INSTANCE_DEPLOYMENT,
+                execution, on_error=True)
+
             if not clone_disks:
             if not clone_disks:
                 self._create_task(
                 self._create_task(
                     instance,
                     instance,
                     constants.TASK_TYPE_RESTORE_REPLICA_DISK_SNAPSHOTS,
                     constants.TASK_TYPE_RESTORE_REPLICA_DISK_SNAPSHOTS,
-                    execution, on_error=True)
+                    execution, depends_on=[cleanup_deployment_task.id],
+                    on_error=True)
 
 
         db_api.add_migration(ctxt, migration)
         db_api.add_migration(ctxt, migration)
         LOG.info("Migration created: %s", migration.id)
         LOG.info("Migration created: %s", migration.id)
@@ -382,14 +402,26 @@ class ConductorServerEndpoint(object):
         migration.info = {}
         migration.info = {}
 
 
         for instance in instances:
         for instance in instances:
-
             task_export = self._create_task(
             task_export = self._create_task(
                 instance, constants.TASK_TYPE_EXPORT_INSTANCE, execution)
                 instance, constants.TASK_TYPE_EXPORT_INSTANCE, execution)
 
 
-            self._create_task(
+            task_import = self._create_task(
                 instance, constants.TASK_TYPE_IMPORT_INSTANCE,
                 instance, constants.TASK_TYPE_IMPORT_INSTANCE,
                 execution, depends_on=[task_export.id])
                 execution, depends_on=[task_export.id])
 
 
+            task_osmorphing = self._create_task(
+                instance, constants.TASK_TYPE_OS_MORPHING,
+                execution, depends_on=[task_import.id])
+
+            self._create_task(
+                instance, constants.TASK_TYPE_FINALIZE_IMPORT_INSTANCE,
+                execution, depends_on=[task_osmorphing.id])
+
+            self._create_task(
+                instance,
+                constants.TASK_TYPE_CLEANUP_FAILED_IMPORT_INSTANCE,
+                execution, on_error=True)
+
         db_api.add_migration(ctxt, migration)
         db_api.add_migration(ctxt, migration)
         LOG.info("Migration created: %s", migration.id)
         LOG.info("Migration created: %s", migration.id)
 
 

+ 8 - 0
coriolis/constants.py

@@ -14,6 +14,10 @@ TASK_STATUS_ON_ERROR_ONLY = "EXECUTE_ON_ERROR_ONLY"
 
 
 TASK_TYPE_EXPORT_INSTANCE = "EXPORT_INSTANCE"
 TASK_TYPE_EXPORT_INSTANCE = "EXPORT_INSTANCE"
 TASK_TYPE_IMPORT_INSTANCE = "IMPORT_INSTANCE"
 TASK_TYPE_IMPORT_INSTANCE = "IMPORT_INSTANCE"
+TASK_TYPE_FINALIZE_IMPORT_INSTANCE = "FINALIZE_IMPORT_INSTANCE"
+TASK_TYPE_CLEANUP_FAILED_IMPORT_INSTANCE = "CLEANUP_FAILED_IMPORT_INSTANCE"
+
+TASK_TYPE_OS_MORPHING = "OS_MORPHING"
 
 
 TASK_TYPE_GET_INSTANCE_INFO = "GET_INSTANCE_INFO"
 TASK_TYPE_GET_INSTANCE_INFO = "GET_INSTANCE_INFO"
 TASK_TYPE_DEPLOY_REPLICA_DISKS = "DEPLOY_REPLICA_DISKS"
 TASK_TYPE_DEPLOY_REPLICA_DISKS = "DEPLOY_REPLICA_DISKS"
@@ -25,6 +29,10 @@ TASK_TYPE_DEPLOY_REPLICA_TARGET_RESOURCES = "DEPLOY_REPLICA_TARGET_RESOURCES"
 TASK_TYPE_DELETE_REPLICA_TARGET_RESOURCES = "DELETE_REPLICA_TARGET_RESOURCES"
 TASK_TYPE_DELETE_REPLICA_TARGET_RESOURCES = "DELETE_REPLICA_TARGET_RESOURCES"
 TASK_TYPE_SHUTDOWN_INSTANCE = "SHUTDOWN_INSTANCE"
 TASK_TYPE_SHUTDOWN_INSTANCE = "SHUTDOWN_INSTANCE"
 TASK_TYPE_DEPLOY_REPLICA_INSTANCE = "DEPLOY_REPLICA_INSTANCE"
 TASK_TYPE_DEPLOY_REPLICA_INSTANCE = "DEPLOY_REPLICA_INSTANCE"
+TASK_TYPE_FINALIZE_REPLICA_INSTANCE_DEPLOYMENT = (
+    "FINALIZE_REPLICA_INSTANCE_DEPLOYMENT")
+TASK_TYPE_CLEANUP_FAILED_REPLICA_INSTANCE_DEPLOYMENT = (
+    "CLEANUP_FAILED_REPLICA_INSTANCE_DEPLOYMENT")
 TASK_TYPE_CREATE_REPLICA_DISK_SNAPSHOTS = "CREATE_REPLICA_DISK_SNAPSHOTS"
 TASK_TYPE_CREATE_REPLICA_DISK_SNAPSHOTS = "CREATE_REPLICA_DISK_SNAPSHOTS"
 TASK_TYPE_DELETE_REPLICA_DISK_SNAPSHOTS = "DELETE_REPLICA_DISK_SNAPSHOTS"
 TASK_TYPE_DELETE_REPLICA_DISK_SNAPSHOTS = "DELETE_REPLICA_DISK_SNAPSHOTS"
 TASK_TYPE_RESTORE_REPLICA_DISK_SNAPSHOTS = "RESTORE_REPLICA_DISK_SNAPSHOTS"
 TASK_TYPE_RESTORE_REPLICA_DISK_SNAPSHOTS = "RESTORE_REPLICA_DISK_SNAPSHOTS"

+ 2 - 1
coriolis/db/sqlalchemy/migrate_repo/manage.py

@@ -1,7 +1,8 @@
+#!/usr/bin/env python
+
 # Copyright 2016 Cloudbase Solutions Srl
 # Copyright 2016 Cloudbase Solutions Srl
 # All Rights Reserved.
 # All Rights Reserved.
 
 
-#!/usr/bin/env python
 from migrate.versioning.shell import main
 from migrate.versioning.shell import main
 
 
 if __name__ == '__main__':
 if __name__ == '__main__':

+ 4 - 0
coriolis/exception.py

@@ -205,6 +205,10 @@ class NotFound(CoriolisException):
     safe = True
     safe = True
 
 
 
 
+class OSMorphingToolsNotFound(NotFound):
+    message = _("Couldn't find any morphing tools for this OS.")
+
+
 class FileNotFound(NotFound):
 class FileNotFound(NotFound):
     message = _("File %(file_path)s could not be found.")
     message = _("File %(file_path)s could not be found.")
 
 

+ 22 - 13
coriolis/osmorphing/base.py

@@ -14,15 +14,14 @@ class BaseOSMorphingTools(object):
     __metaclass__ = abc.ABCMeta
     __metaclass__ = abc.ABCMeta
 
 
     def __init__(
     def __init__(
-            self, conn, os_root_dir, os_root_device,
-            hypervisor, platform, event_manager):
+            self, conn, os_root_dir, os_root_device, hypervisor,
+            event_manager):
         self._conn = conn
         self._conn = conn
         self._os_root_dir = os_root_dir
         self._os_root_dir = os_root_dir
         self._os_root_device = os_root_device
         self._os_root_device = os_root_device
-        self._hypervisor = hypervisor
-        self._platform = platform
         self._distro = None
         self._distro = None
         self._version = None
         self._version = None
+        self._hypervisor = hypervisor
         self._event_manager = event_manager
         self._event_manager = event_manager
 
 
     def check_os(self):
     def check_os(self):
@@ -50,10 +49,16 @@ class BaseOSMorphingTools(object):
     def install_packages(self, package_names):
     def install_packages(self, package_names):
         pass
         pass
 
 
+    def post_packages_install(self, package_names):
+        pass
+
+    def pre_packages_uninstall(self, package_names):
+        pass
+
     def uninstall_packages(self, package_names):
     def uninstall_packages(self, package_names):
         pass
         pass
 
 
-    def post_packages_install(self, package_names):
+    def post_packages_uninstall(self, package_names):
         pass
         pass
 
 
 
 
@@ -62,17 +67,15 @@ class BaseLinuxOSMorphingTools(BaseOSMorphingTools):
 
 
     _packages = {}
     _packages = {}
 
 
-    def __init__(self, conn, os_root_dir, os_root_dev,
-                 hypervisor, platform, event_manager):
+    def __init__(self, conn, os_root_dir, os_root_dev, hypervisor,
+                 event_manager):
         super(BaseLinuxOSMorphingTools, self).__init__(
         super(BaseLinuxOSMorphingTools, self).__init__(
-            conn, os_root_dir, os_root_dev,
-            hypervisor, platform, event_manager)
+            conn, os_root_dir, os_root_dev, hypervisor, event_manager)
         self._ssh = conn
         self._ssh = conn
 
 
     def get_packages(self):
     def get_packages(self):
-        k_add = [(h, p) for (h, p) in self._packages.keys() if
-                 (h is None or h == self._hypervisor) and
-                 (p is None or p == self._platform)]
+        k_add = [h for h in self._packages.keys() if
+                 h is None or h == self._hypervisor]
 
 
         add = [p[0] for p in itertools.chain.from_iterable(
         add = [p[0] for p in itertools.chain.from_iterable(
                [l for k, l in self._packages.items() if k in k_add])]
                [l for k, l in self._packages.items() if k in k_add])]
@@ -90,6 +93,12 @@ class BaseLinuxOSMorphingTools(BaseOSMorphingTools):
     def post_packages_install(self, package_names):
     def post_packages_install(self, package_names):
         self._restore_resolv_conf()
         self._restore_resolv_conf()
 
 
+    def pre_packages_uninstall(self, package_names):
+        self._copy_resolv_conf()
+
+    def post_packages_uninstall(self, package_names):
+        self._restore_resolv_conf()
+
     def _test_path(self, chroot_path):
     def _test_path(self, chroot_path):
         path = os.path.join(self._os_root_dir, chroot_path)
         path = os.path.join(self._os_root_dir, chroot_path)
         return utils.test_ssh_path(self._ssh, path)
         return utils.test_ssh_path(self._ssh, path)
@@ -142,7 +151,7 @@ class BaseLinuxOSMorphingTools(BaseOSMorphingTools):
     def _get_config(self, config_content):
     def _get_config(self, config_content):
         config = {}
         config = {}
         for config_line in config_content.split('\n'):
         for config_line in config_content.split('\n'):
-            m = re.match('(.*)="?([^"]*)"?', config_line)
+            m = re.match('(.*)=(?:"|\')?([^"\']*)(?:"|\')?', config_line)
             if m:
             if m:
                 name, value = m.groups()
                 name, value = m.groups()
                 config[name] = value
                 config[name] = value

+ 26 - 0
coriolis/osmorphing/coreos.py

@@ -0,0 +1,26 @@
+# Copyright 2017 Cloudbase Solutions Srl
+# All Rights Reserved.
+
+from coriolis.osmorphing import base
+
+
+class BaseCoreOSMorphingTools(base.BaseLinuxOSMorphingTools):
+    def _check_os(self):
+        os_release = self._get_os_release()
+        id = os_release.get("ID")
+        if id == "coreos":
+            name = os_release.get("NAME")
+            version = os_release.get("VERSION_ID")
+            return (name, version)
+
+    def pre_packages_install(self, package_names):
+        pass
+
+    def post_packages_install(self, package_names):
+        pass
+
+    def pre_packages_uninstall(self, package_names):
+        pass
+
+    def post_packages_uninstall(self, package_names):
+        pass

+ 3 - 9
coriolis/osmorphing/debian.py

@@ -3,17 +3,10 @@
 
 
 import os
 import os
 
 
-from coriolis import constants
 from coriolis.osmorphing import base
 from coriolis.osmorphing import base
 
 
 
 
-class DebianMorphingTools(base.BaseLinuxOSMorphingTools):
-    _packages = {
-        (constants.HYPERVISOR_VMWARE, None): [("open-vm-tools", True)],
-        # TODO: add cloud-initramfs-growroot
-        (None, constants.PLATFORM_OPENSTACK): [("cloud-init", True)],
-    }
-
+class BaseDebianMorphingTools(base.BaseLinuxOSMorphingTools):
     def _check_os(self):
     def _check_os(self):
         lsb_release_path = "etc/lsb-release"
         lsb_release_path = "etc/lsb-release"
         debian_version_path = "etc/debian_version"
         debian_version_path = "etc/debian_version"
@@ -37,7 +30,8 @@ class DebianMorphingTools(base.BaseLinuxOSMorphingTools):
                            interfaces_path)
                            interfaces_path)
 
 
     def pre_packages_install(self, package_names):
     def pre_packages_install(self, package_names):
-        super(DebianMorphingTools, self).pre_packages_install(package_names)
+        super(BaseDebianMorphingTools, self).pre_packages_install(
+            package_names)
 
 
         if package_names:
         if package_names:
             self._event_manager.progress_update("Updating packages list")
             self._event_manager.progress_update("Updating packages list")

+ 0 - 45
coriolis/osmorphing/factory.py

@@ -1,45 +0,0 @@
-# Copyright 2016 Cloudbase Solutions Srl
-# All Rights Reserved.
-
-import itertools
-
-from oslo_log import log as logging
-
-from coriolis import constants
-from coriolis import exception
-from coriolis.osmorphing import debian
-from coriolis.osmorphing import oracle
-from coriolis.osmorphing import redhat
-from coriolis.osmorphing import suse
-from coriolis.osmorphing import ubuntu
-from coriolis.osmorphing import windows
-
-LOG = logging.getLogger(__name__)
-
-
-def get_os_morphing_tools(
-        conn, os_type, os_root_dir, os_root_dev,
-        target_hypervisor, target_platform, event_manager):
-    os_morphing_tools_clss = {
-        constants.OS_TYPE_LINUX: [debian.DebianMorphingTools,
-                                  ubuntu.UbuntuMorphingTools,
-                                  oracle.OracleMorphingTools,
-                                  redhat.RedHatMorphingTools,
-                                  suse.SUSEMorphingTools],
-        constants.OS_TYPE_WINDOWS: [windows.WindowsMorphingTools],
-    }
-
-    if os_type and os_type not in os_morphing_tools_clss:
-        raise exception.CoriolisException("Unsupported OS type: %s" % os_type)
-
-    for cls in os_morphing_tools_clss.get(
-            os_type, itertools.chain(*os_morphing_tools_clss.values())):
-        tools = cls(
-            conn, os_root_dir, os_root_dev, target_hypervisor,
-            target_platform, event_manager)
-        LOG.debug("Testing OS morphing tools: %s", cls.__name__)
-        os_info = tools.check_os()
-        if os_info:
-            return (tools, os_info)
-    raise exception.CoriolisException(
-        "Cannot find the morphing tools for this OS image")

+ 60 - 22
coriolis/osmorphing/manager.py

@@ -3,46 +3,84 @@
 
 
 from oslo_log import log as logging
 from oslo_log import log as logging
 
 
-from coriolis.osmorphing import factory as osmorphing_factory
+from coriolis import exception
+from coriolis import events
 from coriolis.osmorphing.osmount import factory as osmount_factory
 from coriolis.osmorphing.osmount import factory as osmount_factory
 
 
 LOG = logging.getLogger(__name__)
 LOG = logging.getLogger(__name__)
 
 
 
 
-def morph_image(connection_info, os_type, target_hypervisor, target_platform,
-                nics_info, event_manager, ignore_devices=[]):
+def morph_image(origin_provider, destination_provider, connection_info,
+                osmorphing_info, event_handler):
+    event_manager = events.EventManager(event_handler)
+
+    event_manager.progress_update("Preparing instance for target platform")
+
+    os_type = osmorphing_info.get('os_type')
+    ignore_devices = osmorphing_info.get('ignore_devices', [])
+
     os_mount_tools = osmount_factory.get_os_mount_tools(
     os_mount_tools = osmount_factory.get_os_mount_tools(
         os_type, connection_info, event_manager, ignore_devices)
         os_type, connection_info, event_manager, ignore_devices)
 
 
     event_manager.progress_update("Discovering and mounting OS partitions")
     event_manager.progress_update("Discovering and mounting OS partitions")
     os_root_dir, other_mounted_dirs, os_root_dev = os_mount_tools.mount_os()
     os_root_dir, other_mounted_dirs, os_root_dev = os_mount_tools.mount_os()
 
 
+    osmorphing_info['os_root_dir'] = os_root_dir
+    osmorphing_info['os_root_dev'] = os_root_dev
     conn = os_mount_tools.get_connection()
     conn = os_mount_tools.get_connection()
-    os_morphing_tools, os_info = osmorphing_factory.get_os_morphing_tools(
-        conn, os_type, os_root_dir, os_root_dev, target_hypervisor,
-        target_platform, event_manager)
 
 
-    event_manager.progress_update('OS being migrated: %s' % str(os_info))
+    try:
+        (export_os_morphing_tools, _) = origin_provider.get_os_morphing_tools(
+            conn, osmorphing_info)
+    except exception.OSMorphingToolsNotFound:
+        export_os_morphing_tools = None
 
 
-    os_morphing_tools.set_net_config(nics_info, dhcp=True)
-    LOG.info("Pre packages")
-    (packages_add,
-     packages_remove) = os_morphing_tools.get_packages()
+    try:
+        (import_os_morphing_tools,
+         os_info) = destination_provider.get_os_morphing_tools(
+            conn, osmorphing_info)
+    except exception.OSMorphingToolsNotFound:
+        import_os_morphing_tools = None
+        os_info = None
 
 
-    os_morphing_tools.pre_packages_install(packages_add)
-
-    if packages_remove:
+    if not import_os_morphing_tools:
         event_manager.progress_update(
         event_manager.progress_update(
-            "Removing packages: %s" % str(packages_remove))
-        os_morphing_tools.uninstall_packages(packages_remove)
+            'No OS morphing tools found for this instance')
+    else:
+        event_manager.progress_update('OS being migrated: %s' % str(os_info))
 
 
-    if packages_add:
-        event_manager.progress_update(
-            "Adding packages: %s" % str(packages_add))
-        os_morphing_tools.install_packages(packages_add)
+        (packages_add, _) = import_os_morphing_tools.get_packages()
+
+        if export_os_morphing_tools:
+            (_, packages_remove) = export_os_morphing_tools.get_packages()
+            # Don't remove packages that need to be installed
+            packages_remove = list(set(packages_remove) - set(packages_add))
+
+            LOG.info("Pre packages uninstall")
+            export_os_morphing_tools.pre_packages_uninstall(packages_remove)
+
+            if packages_remove:
+                event_manager.progress_update(
+                    "Removing packages: %s" % str(packages_remove))
+                export_os_morphing_tools.uninstall_packages(packages_remove)
+
+            LOG.info("Post packages uninstall")
+            export_os_morphing_tools.post_packages_uninstall(packages_remove)
+
+        LOG.info("Pre packages install")
+        import_os_morphing_tools.pre_packages_install(packages_add)
+
+        nics_info = osmorphing_info.get('nics_info')
+        import_os_morphing_tools.set_net_config(nics_info, dhcp=True)
+        LOG.info("Pre packages")
+
+        if packages_add:
+            event_manager.progress_update(
+                "Adding packages: %s" % str(packages_add))
+            import_os_morphing_tools.install_packages(packages_add)
 
 
-    LOG.info("Post packages")
-    os_morphing_tools.post_packages_install(packages_add)
+        LOG.info("Post packages install")
+        import_os_morphing_tools.post_packages_install(packages_add)
 
 
     event_manager.progress_update("Dismounting OS partitions")
     event_manager.progress_update("Dismounting OS partitions")
     os_mount_tools.dismount_os(other_mounted_dirs + [os_root_dir])
     os_mount_tools.dismount_os(other_mounted_dirs + [os_root_dir])

+ 27 - 0
coriolis/osmorphing/openwrt.py

@@ -0,0 +1,27 @@
+# Copyright 2017 Cloudbase Solutions Srl
+# All Rights Reserved.
+
+from coriolis.osmorphing import base
+
+
+class BaseOpenWRTMorphingTools(base.BaseLinuxOSMorphingTools):
+    def _check_os(self):
+        openwrt_release = self._read_config_file(
+            "etc/openwrt_release", check_exists=True)
+        distrib_id = openwrt_release.get("DISTRIB_ID")
+        if distrib_id == "OpenWrt":
+            name = openwrt_release.get("DISTRIB_DESCRIPTION", distrib_id)
+            version = openwrt_release.get("DISTRIB_RELEASE")
+            return (name, version)
+
+    def pre_packages_install(self, package_names):
+        pass
+
+    def post_packages_install(self, package_names):
+        pass
+
+    def pre_packages_uninstall(self, package_names):
+        pass
+
+    def post_packages_uninstall(self, package_names):
+        pass

+ 1 - 44
coriolis/osmorphing/oracle.py

@@ -3,12 +3,10 @@
 
 
 import re
 import re
 
 
-from coriolis import constants
-from coriolis import exception
 from coriolis.osmorphing import redhat
 from coriolis.osmorphing import redhat
 
 
 
 
-class OracleMorphingTools(redhat.RedHatMorphingTools):
+class BaseOracleMorphingTools(redhat.BaseRedHatMorphingTools):
     def _check_os(self):
     def _check_os(self):
         oracle_release_path = "etc/oracle-release"
         oracle_release_path = "etc/oracle-release"
         if self._test_path(oracle_release_path):
         if self._test_path(oracle_release_path):
@@ -19,47 +17,6 @@ class OracleMorphingTools(redhat.RedHatMorphingTools):
                 distro, version = m.groups()
                 distro, version = m.groups()
                 return (distro, version)
                 return (distro, version)
 
 
-    def install_packages(self, package_names):
-        self._yum_install(package_names, self._enable_repos)
-
-    def pre_packages_install(self, package_names):
-        self._enable_repos = []
-        super(OracleMorphingTools, self).pre_packages_install(package_names)
-
-        if self._platform == constants.PLATFORM_OPENSTACK:
-            self._enable_cloud_init_repos()
-
-    def _enable_cloud_init_repos(self):
-        self._yum_install(['yum-utils'])
-
-        distro, version = self.check_os()
-        major_version = version.split(".")[0]
-
-        # TODO: for ULN users, use the corresponding repos
-        # e.g.: ol7_x86_64_addons
-        self._exec_cmd_chroot(
-            "yum-config-manager --add-repo "
-            "http://public-yum.oracle.com/public-yum-ol%s.repo" %
-            major_version)
-
-        self._enable_repos = ["ol%s_software_collections" % major_version,
-                              "ol%s_addons" % major_version]
-
-        if major_version == "7":
-            try:
-                self._yum_install(["python-cheetah"], self._enable_repos)
-            except exception.CoriolisException:
-                # The python-pygments RPM required by python-cheetah is called
-                # python27-python-pygments. Force the installation.
-                self._yum_install(
-                    ["python-markdown", "python27-python-pygments"],
-                    self._enable_repos)
-
-                self._exec_cmd_chroot(
-                    "rpm -Uvh %s --nodeps" %
-                    "http://public-yum.oracle.com/repo/OracleLinux/OL7/addons/"
-                    "x86_64/getPackage/python-cheetah-2.4.1-1.el7.x86_64.rpm")
-
     def _run_dracut(self):
     def _run_dracut(self):
         self._run_dracut_base('kernel')
         self._run_dracut_base('kernel')
         self._run_dracut_base('kernel-uek')
         self._run_dracut_base('kernel-uek')

+ 0 - 3
coriolis/osmorphing/osmount/windows.py

@@ -1,9 +1,6 @@
 # Copyright 2016 Cloudbase Solutions Srl
 # Copyright 2016 Cloudbase Solutions Srl
 # All Rights Reserved.
 # All Rights Reserved.
 
 
-import os
-import re
-
 from oslo_log import log as logging
 from oslo_log import log as logging
 
 
 from coriolis import exception
 from coriolis import exception

+ 9 - 63
coriolis/osmorphing/redhat.py

@@ -6,10 +6,7 @@ import re
 import uuid
 import uuid
 
 
 from oslo_log import log as logging
 from oslo_log import log as logging
-import yaml
 
 
-from coriolis import constants
-from coriolis import exception
 from coriolis.osmorphing import base
 from coriolis.osmorphing import base
 from coriolis import utils
 from coriolis import utils
 
 
@@ -22,20 +19,16 @@ RELEASE_FEDORA = "Fedora"
 DEFAULT_CLOUD_USER = "cloud-user"
 DEFAULT_CLOUD_USER = "cloud-user"
 
 
 
 
-class RedHatMorphingTools(base.BaseLinuxOSMorphingTools):
-    _packages = {
-        (None, None): [("dracut-config-generic", False)],
-        (constants.HYPERVISOR_VMWARE, None): [("open-vm-tools", True)],
-        (constants.HYPERVISOR_HYPERV, None): [("hyperv-daemons", True)],
-        (None, constants.PLATFORM_OPENSTACK): [
-            ("cloud-init", True),
-            ("cloud-utils", False),
-            ("parted", False),
-            ("git", False),
-            ("cloud-utils-growpart", False)],
-    }
+class BaseRedHatMorphingTools(base.BaseLinuxOSMorphingTools):
     _NETWORK_SCRIPTS_PATH = "etc/sysconfig/network-scripts"
     _NETWORK_SCRIPTS_PATH = "etc/sysconfig/network-scripts"
 
 
+    def __init__(self, conn, os_root_dir, os_root_dev,
+                 hypervisor, event_manager):
+        super(BaseRedHatMorphingTools, self).__init__(
+            conn, os_root_dir, os_root_dev,
+            hypervisor, event_manager)
+        self._enable_repos = []
+
     def _check_os(self):
     def _check_os(self):
         redhat_release_path = "etc/redhat-release"
         redhat_release_path = "etc/redhat-release"
         if self._test_path(redhat_release_path):
         if self._test_path(redhat_release_path):
@@ -137,7 +130,7 @@ class RedHatMorphingTools(base.BaseLinuxOSMorphingTools):
             self._exec_cmd_chroot(yum_cmd)
             self._exec_cmd_chroot(yum_cmd)
 
 
     def install_packages(self, package_names):
     def install_packages(self, package_names):
-        self._yum_install(package_names)
+        self._yum_install(package_names, self._enable_repos)
 
 
     def uninstall_packages(self, package_names):
     def uninstall_packages(self, package_names):
         self._yum_uninstall(package_names)
         self._yum_uninstall(package_names)
@@ -158,16 +151,6 @@ class RedHatMorphingTools(base.BaseLinuxOSMorphingTools):
                     "dracut -f /boot/initramfs-%(version)s.img %(version)s" %
                     "dracut -f /boot/initramfs-%(version)s.img %(version)s" %
                     {"version": kernel_version})
                     {"version": kernel_version})
 
 
-    def _get_default_cloud_user(self):
-        cloud_cfg_path = os.path.join(self._os_root_dir, 'etc/cloud/cloud.cfg')
-        if not self._test_path(cloud_cfg_path):
-            raise exception.CoriolisException(
-                "cloud-init config file not found: %s" % cloud_cfg_path)
-        cloud_cfg_content = self._read_file(cloud_cfg_path)
-        cloud_cfg = yaml.load(cloud_cfg_content)
-        return cloud_cfg.get('system_info', {}).get('default_user', {}).get(
-            'name', DEFAULT_CLOUD_USER)
-
     def _set_network_nozeroconf_config(self):
     def _set_network_nozeroconf_config(self):
         network_cfg_file = "etc/sysconfig/network"
         network_cfg_file = "etc/sysconfig/network"
         network_cfg = self._read_config_file(network_cfg_file,
         network_cfg = self._read_config_file(network_cfg_file,
@@ -175,23 +158,6 @@ class RedHatMorphingTools(base.BaseLinuxOSMorphingTools):
         network_cfg["NOZEROCONF"] = "yes"
         network_cfg["NOZEROCONF"] = "yes"
         self._write_config_file(network_cfg_file, network_cfg)
         self._write_config_file(network_cfg_file, network_cfg)
 
 
-    def _configure_cloud_init(self):
-        if "cloud-init" in self.get_packages()[0]:
-            cloud_user = self._get_default_cloud_user()
-            if not self._check_user_exists(cloud_user):
-                self._exec_cmd_chroot("useradd %s" % cloud_user)
-            self._set_network_nozeroconf_config()
-            if self._has_systemd():
-                self._enable_systemd_service("cloud-init")
-
-    def _add_hyperv_ballooning_udev_rules(self):
-        udev_file = "etc/udev/rules.d/100-balloon.rules"
-        content = 'SUBSYSTEM=="memory", ACTION=="add", ATTR{state}="online"\n'
-
-        if (self._hypervisor == constants.HYPERVISOR_HYPERV and
-                not self._test_path(udev_file)):
-            self._write_file_sudo(udev_file, content)
-
     def _write_config_file(self, chroot_path, config_data):
     def _write_config_file(self, chroot_path, config_data):
         content = self._get_config_file_content(config_data)
         content = self._get_config_file_content(config_data)
         self._write_file_sudo(chroot_path, content)
         self._write_file_sudo(chroot_path, content)
@@ -207,23 +173,3 @@ class RedHatMorphingTools(base.BaseLinuxOSMorphingTools):
 
 
     def _set_selinux_autorelabel(self):
     def _set_selinux_autorelabel(self):
         self._exec_cmd_chroot("touch /.autorelabel")
         self._exec_cmd_chroot("touch /.autorelabel")
-
-    def pre_packages_install(self, package_names):
-        super(RedHatMorphingTools, self).pre_packages_install(package_names)
-
-        distro, version = self.check_os()
-        if distro == RELEASE_RHEL and "cloud-init" in self.get_packages()[0]:
-            major_version = version.split(".")[0]
-            repo_name = "rhel-%s-server-rh-common-rpms" % major_version
-            # This is necessary for cloud-init
-            self._event_manager.progress_update(
-                "Enabling repository: %s" % repo_name)
-            self._exec_cmd_chroot(
-                "subscription-manager repos --enable %s" % repo_name)
-
-    def post_packages_install(self, package_names):
-        self._add_hyperv_ballooning_udev_rules()
-        self._run_dracut()
-        self._configure_cloud_init()
-        self._set_selinux_autorelabel()
-        super(RedHatMorphingTools, self).post_packages_install(package_names)

+ 2 - 45
coriolis/osmorphing/suse.py

@@ -1,23 +1,16 @@
 # Copyright 2016 Cloudbase Solutions Srl
 # Copyright 2016 Cloudbase Solutions Srl
 # All Rights Reserved.
 # All Rights Reserved.
 
 
-import os
 import re
 import re
 
 
-from coriolis import constants
 from coriolis.osmorphing import base
 from coriolis.osmorphing import base
 
 
 
 
-class SUSEMorphingTools(base.BaseLinuxOSMorphingTools):
-    _packages = {
-        (constants.HYPERVISOR_VMWARE, None): [("open-vm-tools", True)],
-        (None, constants.PLATFORM_OPENSTACK): [("cloud-init", True)],
-    }
-
+class BaseSUSEMorphingTools(base.BaseLinuxOSMorphingTools):
     def _check_os(self):
     def _check_os(self):
         os_release = self._get_os_release()
         os_release = self._get_os_release()
         name = os_release.get("NAME")
         name = os_release.get("NAME")
-        if name == "SLES" or name.startswith("openSUSE"):
+        if name and (name == "SLES" or name.startswith("openSUSE")):
             pretty_name = os_release.get("PRETTY_NAME")
             pretty_name = os_release.get("PRETTY_NAME")
             if name == "openSUSE Tumbleweed":
             if name == "openSUSE Tumbleweed":
                 self._version_id = None
                 self._version_id = None
@@ -40,32 +33,6 @@ class SUSEMorphingTools(base.BaseLinuxOSMorphingTools):
         # TODO: add networking support
         # TODO: add networking support
         pass
         pass
 
 
-    def pre_packages_install(self, package_names):
-        super(SUSEMorphingTools, self).pre_packages_install(package_names)
-
-        if self._platform == constants.PLATFORM_OPENSTACK:
-            # TODO: use OS version to choose the right repo
-            repo_version_map = {("SLES", "11.4"): "SLE_11_SP4",
-                                ("SLES", "11.3"): "SLE_11_SP3",
-                                ("SLES", "11.2"): "SLE_11_SP2",
-                                ("SLES", "12.1"): "SLE_12_SP1",
-                                ("SLES", "12"): "SLE_12",
-                                ("openSUSE", "13.2"): "openSUSE_13.2",
-                                ("openSUSE Leap", "42.1"):
-                                "openSUSE_Leap_42.1",
-                                ("openSUSE Tumbleweed", None):
-                                "openSUSE_Tumbleweed"}
-
-            repo_version = repo_version_map.get(
-                (self._distro, self._version_id), "SLE_12_SP1")
-
-            repo = "obs://Cloud:Tools/%s" % repo_version
-            self._event_manager.progress_update("Adding repository: %s" % repo)
-            self._exec_cmd_chroot("zypper --non-interactive addrepo -f %s "
-                                  "Cloud-Tools" % repo)
-        self._exec_cmd_chroot(
-            "zypper --non-interactive --no-gpg-checks refresh")
-
     def _run_dracut(self):
     def _run_dracut(self):
         package_names = self._exec_cmd_chroot(
         package_names = self._exec_cmd_chroot(
             'rpm -q kernel-default').decode().split('\n')[:-1]
             'rpm -q kernel-default').decode().split('\n')[:-1]
@@ -86,11 +53,6 @@ class SUSEMorphingTools(base.BaseLinuxOSMorphingTools):
         except:
         except:
             return False
             return False
 
 
-    def _configure_cloud_init(self):
-        if "cloud-init" in self.get_packages()[0]:
-            if self._has_systemd():
-                self._enable_systemd_service("cloud-init")
-
     def install_packages(self, package_names):
     def install_packages(self, package_names):
         self._exec_cmd_chroot(
         self._exec_cmd_chroot(
             'zypper --non-interactive install %s' % " ".join(package_names))
             'zypper --non-interactive install %s' % " ".join(package_names))
@@ -98,8 +60,3 @@ class SUSEMorphingTools(base.BaseLinuxOSMorphingTools):
     def uninstall_packages(self, package_names):
     def uninstall_packages(self, package_names):
         self._exec_cmd_chroot(
         self._exec_cmd_chroot(
             'zypper --non-interactive remove %s' % " ".join(package_names))
             'zypper --non-interactive remove %s' % " ".join(package_names))
-
-    def post_packages_install(self, packages_names):
-        self._run_dracut()
-        self._configure_cloud_init()
-        super(SUSEMorphingTools, self).post_packages_install(package_names)

+ 1 - 11
coriolis/osmorphing/ubuntu.py

@@ -1,20 +1,10 @@
 # Copyright 2016 Cloudbase Solutions Srl
 # Copyright 2016 Cloudbase Solutions Srl
 # All Rights Reserved.
 # All Rights Reserved.
 
 
-from coriolis import constants
 from coriolis.osmorphing import debian
 from coriolis.osmorphing import debian
 
 
 
 
-class UbuntuMorphingTools(debian.DebianMorphingTools):
-    _packages = {
-        (constants.HYPERVISOR_VMWARE, None): [("open-vm-tools", True)],
-        # TODO: sudo agt-get install linux-tool-<kernel release>
-        # linux-cloud-tools-<kernel release> -y
-        (constants.HYPERVISOR_HYPERV, None): [("hv-kvp-daemon-init", True)],
-        # TODO: add cloud-initramfs-growroot
-        (None, constants.PLATFORM_OPENSTACK): [("cloud-init", True)],
-    }
-
+class BaseUbuntuMorphingTools(debian.BaseDebianMorphingTools):
     def _check_os(self):
     def _check_os(self):
         config = self._read_config_file("etc/lsb-release", check_exists=True)
         config = self._read_config_file("etc/lsb-release", check_exists=True)
         dist_id = config.get('DISTRIB_ID')
         dist_id = config.get('DISTRIB_ID')

+ 1 - 210
coriolis/osmorphing/windows.py

@@ -6,31 +6,11 @@ import re
 import uuid
 import uuid
 
 
 from distutils import version
 from distutils import version
-from oslo_config import cfg
 from oslo_log import log as logging
 from oslo_log import log as logging
 
 
-from coriolis import constants
 from coriolis import exception
 from coriolis import exception
 from coriolis.osmorphing import base
 from coriolis.osmorphing import base
 
 
-opts = [
-    cfg.StrOpt('virtio_iso_url',
-               default='https://fedorapeople.org/groups/virt/virtio-win/'
-               'direct-downloads/stable-virtio/virtio-win.iso',
-               help="Location of the virtio-win ISO"),
-    cfg.StrOpt('cloudbaseinit_x64_url',
-               default="https://www.cloudbase.it/downloads/"
-               "CloudbaseInitSetup_x64.zip",
-               help="Location of the Cloudbase-Init ZIP for amd64 systems"),
-    cfg.StrOpt('cloudbaseinit_x86_url',
-               default="https://www.cloudbase.it/downloads/"
-               "CloudbaseInitSetup_x86.zip",
-               help="Location of the Cloudbase-Init ZIP for amd64 systems"),
-]
-
-CONF = cfg.CONF
-CONF.register_opts(opts, 'windows_images')
-
 LOG = logging.getLogger(__name__)
 LOG = logging.getLogger(__name__)
 
 
 SERVICE_START_AUTO = 2
 SERVICE_START_AUTO = 2
@@ -38,10 +18,9 @@ SERVICE_START_MANUAL = 3
 SERVICE_START_DISABLED = 4
 SERVICE_START_DISABLED = 4
 
 
 SERVICE_PATH_FORMAT = "HKLM:\\%s\\ControlSet001\\Services\\%s"
 SERVICE_PATH_FORMAT = "HKLM:\\%s\\ControlSet001\\Services\\%s"
-CLOUDBASEINIT_SERVICE_NAME = "cloudbase-init"
 
 
 
 
-class WindowsMorphingTools(base.BaseOSMorphingTools):
+class BaseWindowsMorphingTools(base.BaseOSMorphingTools):
     def _check_os(self):
     def _check_os(self):
         try:
         try:
             (self._version_number,
             (self._version_number,
@@ -52,16 +31,6 @@ class WindowsMorphingTools(base.BaseOSMorphingTools):
         except exception.CoriolisException as ex:
         except exception.CoriolisException as ex:
             LOG.debug("Exception during OS detection: %s", ex)
             LOG.debug("Exception during OS detection: %s", ex)
 
 
-    def pre_packages_install(self, packages_add):
-        if (not self._hypervisor or
-                self._hypervisor == constants.HYPERVISOR_KVM):
-            self._add_virtio_drivers()
-
-        if self._platform == constants.PLATFORM_OPENSTACK:
-            self._add_cloudbase_init()
-        else:
-            self._disable_cloudbase_init()
-
     def set_net_config(self, nics_info, dhcp):
     def set_net_config(self, nics_info, dhcp):
         # TODO: implement
         # TODO: implement
         pass
         pass
@@ -157,73 +126,6 @@ class WindowsMorphingTools(base.BaseOSMorphingTools):
         self._conn.exec_ps_command("Dismount-DiskImage '%s'" % path,
         self._conn.exec_ps_command("Dismount-DiskImage '%s'" % path,
                                    ignore_stdout=True)
                                    ignore_stdout=True)
 
 
-    def _add_virtio_drivers(self):
-        # TODO: add support for x86
-        arch = "amd64"
-
-        CLIENT = 1
-        SERVER = 2
-
-        # Ordered by version number
-        virtio_dirs = [
-            ("xp", version.LooseVersion("5.1"), CLIENT),
-            ("2k3", version.LooseVersion("5.2"), SERVER),
-            ("2k8", version.LooseVersion("6.0"), SERVER | CLIENT),
-            ("w7", version.LooseVersion("6.1"), CLIENT),
-            ("2k8R2", version.LooseVersion("6.1"), SERVER),
-            ("w8", version.LooseVersion("6.2"), CLIENT),
-            ("2k12", version.LooseVersion("6.2"), SERVER),
-            ("w8.1", version.LooseVersion("6.3"), CLIENT),
-            ("2k12R2", version.LooseVersion("6.3"), SERVER),
-            ("w10", version.LooseVersion("10.0"), SERVER | CLIENT),
-            ]
-
-        # The list of all possible editions is huge, this is a semplification
-        if "Server" in self._edition_id:
-            edition_type = SERVER
-        else:
-            edition_type = CLIENT
-
-        drivers = ["Balloon", "NetKVM", "qxl", "qxldod", "pvpanic", "viorng",
-                   "vioscsi", "vioserial", "viostor"]
-
-        self._event_manager.progress_update("Downloading virtio-win drivers")
-
-        virtio_iso_path = "c:\\virtio-win.iso"
-        self._conn.download_file(
-            CONF.windows_images.virtio_iso_url, virtio_iso_path)
-
-        self._event_manager.progress_update("Adding virtio-win drivers")
-
-        virtio_drive = self._mount_disk_image(virtio_iso_path)
-        try:
-            for virtio_dir, dir_version, dir_edition_type in reversed(
-                    virtio_dirs):
-                if self._version_number >= dir_version and (
-                        edition_type & dir_edition_type):
-                    path = "%s:\\Balloon\\%s\\%s" % (
-                        virtio_drive, virtio_dir, arch)
-                    if self._conn.test_path(path):
-                        break
-
-            driver_paths = ["%s:\\%s\\%s\\%s" % (
-                            virtio_drive, d, virtio_dir, arch)
-                            for d in drivers]
-
-            sid = self._get_sid()
-            # Fails on Nano Server without explicitly granting permissions
-            file_repo_path = ("%sWindows\System32\DriverStore\FileRepository" %
-                              self._os_root_dir)
-            self._grant_permissions(file_repo_path, "*%s" % sid)
-            try:
-                for driver_path in driver_paths:
-                    if self._conn.test_path(driver_path):
-                        self._add_dism_driver(driver_path)
-            finally:
-                self._revoke_permissions(file_repo_path, "*%s" % sid)
-        finally:
-            self._dismount_disk_image(virtio_iso_path)
-
     def _expand_archive(self, path, destination):
     def _expand_archive(self, path, destination):
         LOG.info("Expanding archive \"%(path)s\" in \"%(destination)s\"",
         LOG.info("Expanding archive \"%(path)s\" in \"%(destination)s\"",
                  {"path": path, "destination": destination})
                  {"path": path, "destination": destination})
@@ -286,114 +188,3 @@ class WindowsMorphingTools(base.BaseOSMorphingTools):
              "service_account": service_account,
              "service_account": service_account,
              "start_mode": start_mode},
              "start_mode": start_mode},
             ignore_stdout=True)
             ignore_stdout=True)
-
-    def _write_cloudbase_init_conf(self, cloudbaseinit_base_dir,
-                                   local_base_dir, com_port="COM1"):
-        LOG.info("Writing Cloudbase-Init configuration files")
-        conf_dir = "%s\\conf" % cloudbaseinit_base_dir
-        self._conn.exec_ps_command("mkdir '%s' -Force" % conf_dir,
-                                   ignore_stdout=True)
-
-        conf_file_path = "%s\\cloudbase-init.conf" % conf_dir
-
-        conf_content = (
-            "[DEFAULT]\n"
-            "username = Admin\n"
-            "groups = Administrators\n"
-            "inject_user_password = true\n"
-            "config_drive_raw_hhd = true\n"
-            "config_drive_cdrom = true\n"
-            "config_drive_vfat = true\n"
-            "bsdtar_path = %(bin_path)s\\bsdtar.exe\n"
-            "mtools_path = %(bin_path)s\n"
-            "logdir = %(log_path)s\n"
-            "logfile = cloudbase-init.log\n"
-            "default_log_levels = "
-            "comtypes=INFO,suds=INFO,iso8601=WARN,requests=WARN\n"
-            "mtu_use_dhcp_config = true\n"
-            "ntp_use_dhcp_config = true\n"
-            "allow_reboot = true\n"
-            "debug = true\n"
-            "logging_serial_port_settings = %(com_port)s,115200,N,8\n" %
-            {"bin_path": "%s\\Bin" % local_base_dir,
-             "log_path": "%s\\Log" % local_base_dir,
-             "com_port": com_port})
-
-        self._conn.write_file(conf_file_path, conf_content.encode())
-
-    def _check_cloudbase_init_exists(self, key_name):
-        reg_service_path = (SERVICE_PATH_FORMAT %
-                            (key_name, CLOUDBASEINIT_SERVICE_NAME))
-        return self._conn.exec_ps_command(
-            "Test-Path %s" % reg_service_path) == "True"
-
-    def _disable_cloudbase_init(self):
-        key_name = str(uuid.uuid4())
-        self._load_registry_hive(
-            "HKLM\%s" % key_name,
-            "%sWindows\\System32\\config\\SYSTEM" % self._os_root_dir)
-        try:
-            if self._check_cloudbase_init_exists(key_name):
-                self._event_manager.progress_update(
-                    "Disabling cloudbase-init")
-                self._set_service_start_mode(
-                    key_name, CLOUDBASEINIT_SERVICE_NAME,
-                    SERVICE_START_DISABLED)
-        finally:
-            self._unload_registry_hive("HKLM\%s" % key_name)
-
-    def _add_cloudbase_init(self):
-        # TODO: add support for x86
-        arch = "amd64"
-        arch_url_map = {"amd64": CONF.windows_images.cloudbaseinit_x64_url,
-                        "x86": CONF.windows_images.cloudbaseinit_x86_url}
-
-        self._event_manager.progress_update("Adding cloudbase-init")
-
-        key_name = str(uuid.uuid4())
-        self._load_registry_hive(
-            "HKLM\%s" % key_name,
-            "%sWindows\\System32\\config\\SYSTEM" % self._os_root_dir)
-        try:
-            if self._check_cloudbase_init_exists(key_name):
-                self._event_manager.progress_update(
-                    "Enabling cloudbase-init")
-                self._set_service_start_mode(
-                    key_name, CLOUDBASEINIT_SERVICE_NAME, SERVICE_START_AUTO)
-            else:
-                cloudbaseinit_zip_path = "c:\\cloudbaseinit.zip"
-                cloudbaseinit_base_dir = "%sCloudbase-Init" % self._os_root_dir
-
-                self._event_manager.progress_update(
-                    "Downloading cloudbase-init")
-                self._conn.download_file(arch_url_map[arch],
-                                         cloudbaseinit_zip_path)
-
-                self._event_manager.progress_update(
-                    "Installing cloudbase-init")
-                self._expand_archive(cloudbaseinit_zip_path,
-                                     cloudbaseinit_base_dir)
-
-                log_dir = "%s\\Log" % cloudbaseinit_base_dir
-                self._conn.exec_ps_command("mkdir '%s' -Force" % log_dir,
-                                           ignore_stdout=True)
-
-                local_base_dir = "C%s" % cloudbaseinit_base_dir[1:]
-                self._write_cloudbase_init_conf(
-                    cloudbaseinit_base_dir, local_base_dir)
-
-                image_path = (
-                    '""""%(path)s\\Bin\\OpenStackService.exe"""" '
-                    'cloudbase-init """"%(path)s\\Python\\Python.exe"""" -c '
-                    '""""from cloudbaseinit import shell;shell.main()"""" '
-                    '--config-file """"%(path)s\\conf\\cloudbase-init.conf""""'
-                    % {'path': local_base_dir})
-
-                self._create_service(
-                    key_name=key_name,
-                    service_name=CLOUDBASEINIT_SERVICE_NAME,
-                    image_path=image_path,
-                    display_name="Cloud Initialization Service",
-                    description="Service wrapper for cloudbase-init")
-        finally:
-            self._unload_registry_hive("HKLM\%s" % key_name)

+ 0 - 1013
coriolis/providers/azure/__init__.py

@@ -1,1013 +0,0 @@
-# Copyright 2016 Cloudbase Solutions Srl
-# All Rights Reserved.
-
-""" This module defines the Azure Resource Manager Importer and Exporter. """
-
-import collections
-import contextlib
-import math
-import os
-import os.path as path
-import re
-import tempfile
-import time
-import traceback
-
-import paramiko
-import requests
-from azure.mgmt import compute, network, storage
-from azure.mgmt.resource import resources
-from azure.storage import blob
-from msrestazure import azure_active_directory, azure_exceptions
-from oslo_config import cfg
-from oslo_log import log as logging
-from oslo_utils import units
-
-from coriolis import constants
-from coriolis import events
-from coriolis import exception
-from coriolis.osmorphing import manager as osmorpher
-from coriolis.providers import base
-from coriolis.providers.azure import utils as azutils
-from coriolis.providers.azure import exceptions as azexceptions
-from coriolis import schemas
-from coriolis import utils
-
-
-OPTIONS = [
-    # TODO: AzureStack soon: default for "auth_url", used in client creation.
-    cfg.StrOpt("default_auth_url",
-               default="https://management.core.windows.net/",
-               help="The API endpoint to authenticate and perform operations"
-                    "against."),
-    cfg.StrOpt("migr_location",
-               default="westus",
-               help="The default Azure location to migrate to "
-                    "when no location is specified through the "
-                    "target_environment."),
-    cfg.StrOpt("migr_subnet_name",
-               default="coriolis-worknet",
-               help="Name of the subnet to be used by worker instances."),
-    cfg.StrOpt("migr_container_name",
-               default="coriolis",
-               help="Name of the azure storage container which"
-                    "will support the migration."),
-    cfg.StrOpt("default_migration_hostname",
-               default="migratedinst",
-               help="Default hostname to be used in case the hostname of the "
-                    "instance being migrated does not conform with Azure's "
-                    "standards (no special characters of maximum length 15)."),
-    cfg.DictOpt("worker_volume_count_to_size_map",
-                default=collections.OrderedDict([
-                    (2, compute.models.VirtualMachineSizeTypes.standard_d1),
-                    (4, compute.models.VirtualMachineSizeTypes.standard_d2),
-                    (8, compute.models.VirtualMachineSizeTypes.standard_d3),
-                    (16, compute.models.VirtualMachineSizeTypes.standard_d4),
-                    (32, compute.models.VirtualMachineSizeTypes.standard_d5_v2)
-                ]),
-                help="Mapping between the number of volumes supported by "
-                     "an Azure instance size and its name (with correct "
-                     "capitalization and underscores) as show here: "
-                     "https://azure.microsoft.com/en-us/documentation/"
-                     "articles/virtual-machines-windows-sizes/"),
-    cfg.DictOpt("migr_image_sku_map",
-                default={
-                    constants.OS_TYPE_LINUX: "14.04.3-LTS",
-                    constants.OS_TYPE_WINDOWS: "2012-R2-Datacenter"
-                },
-                help="Mapping of image SKUs to be used for migration workers "
-                     "with respect to the type of OS being migrated.")
-]
-
-CONF = cfg.CONF
-CONF.register_opts(OPTIONS, "azure_migration_provider")
-
-LOG = logging.getLogger(__name__)
-
-MIGRATION_RESGROUP_NAME_FORMAT = "coriolis-migration-%s"
-MIGRATION_WORKER_NAME_FORMAT = "coriolis-worker-%s"
-AZURE_DISK_NAME_FORMAT = "%s.vhd"
-MIGRATION_WORKER_NIC_NAME_FORMAT = "coriolis-worker-nic-%s"
-MIGRATION_WORKER_PIP_NAME_FORMAT = "coriolis-worker-pip-%s"
-MIGRATION_NETWORK_NAME_FORMAT = "coriolis-migrnet-%s"
-
-BLOB_PATH_FORMAT = "https://%s.blob.core.windows.net/%s/%s"
-
-SSH_PUBKEY_FORMAT = "ssh-rsa %s tmp@migration"
-SSH_PUBKEY_FILEPATH_FORMAT = "/home/%s/.ssh/authorized_keys"
-
-PROVIDERS_NAME_MAP = {
-    x: "Microsoft." + x.capitalize()
-    for x in ["compute", "network"]
-}
-
-WORKER_VM_IMAGE_VERSION = "latest"
-
-WORKER_IMAGES_MAP = {
-    constants.OS_TYPE_LINUX: {
-        "version": WORKER_VM_IMAGE_VERSION,
-        "publisher": "canonical",
-        "offer": "UbuntuServer"
-    },
-    constants.OS_TYPE_WINDOWS: {
-        "version": WORKER_VM_IMAGE_VERSION,
-        "publisher": "MicrosoftWindowsServer",
-        "offer": "WindowsServer"
-    },
-}
-
-AZURE_OSTYPES_MAP = {
-    "linux":   compute.models.OperatingSystemTypes.linux,
-    "windows": compute.models.OperatingSystemTypes.windows
-}
-
-WINRM_EXTENSION_FILES_BASE_URI = (
-    # TODO: not fetch off github
-    "https://raw.githubusercontent.com/cloudbase/coriolis-resources/master/azure/")
-
-WINRM_EXTENSION_FILE_URIS = [
-    WINRM_EXTENSION_FILES_BASE_URI + f
-    for f in ["ConfigureWinRM.ps1", "makecert.exe", "winrmconf.cmd"]
-]
-
-WORKER_USERNAME = "coriolis"
-
-# NOTE: connection_info
-CREDENTIALS_CATEGORIES = ["user_credentials", "service_principal_credentials"]
-USER_CREDENTIALS_FIELDS = ["username", "password"]
-SP_CREDENTIALS_FIELDS = ["client_id", "client_secret", "tenant_id"]
-
-# An AzureStorageBlob is identified by its unique name and URI on Azure.
-AzureStorageBlob = collections.namedtuple(
-    "AzureStorageBlob", "name uri")
-
-# An AzureWorkerOSProfile is identified by the compute.models.OSProfile, the
-# authentication token to the machine (ssh key or cert thubprint), the port
-# awaiting the respective connection, as well as a list of
-# compute.models.VirtualMachineExtension's required by the worker.
-AzureWorkerOSProfile = collections.namedtuple(
-    "AzureWorkerOSProfile", "profile token port extensions")
-
-# An AzureWorkerInstance is identified by its unique name, ip address, port,
-# username, password, authentication token and its list of datadisks.
-AzureWorkerInstance = collections.namedtuple(
-    "AzureWorkerInstance", "name ip port username password pkey datadisks")
-
-
-class ImportProvider(base.BaseImportProvider):
-    """ Provides import capabilities. """
-    platform = constants.PLATFORM_AZURE_RM
-
-    connection_info_schema = schemas.get_schema(
-        __name__, schemas.PROVIDER_CONNECTION_INFO_SCHEMA_NAME)
-
-    target_environment_schema = schemas.get_schema(
-        __name__, schemas.PROVIDER_TARGET_ENVIRONMENT_SCHEMA_NAME)
-
-    def __init__(self, event_handler):
-        self._event_manager = events.EventManager(event_handler)
-
-    def validate_connection_info(self, connection_info):
-        """ Validates the provided connection information. """
-        LOG.info("Validating connection info: %s", connection_info)
-
-        super(ImportProvider, self).validate_connection_info(connection_info)
-
-        # NOTE: considering we cannot check the validity of the credentials per
-        # se if a secret href is provided here, we simply return on the spot:
-        if "secret_ref" in connection_info:
-            return
-
-        try:
-            # NOTE: attempt to register to a provider to ensure credentials
-            # are indeed actually valid.
-            resc = self._get_resource_client(connection_info)
-            utils.retry_on_error()(azutils.checked(resc.providers.register))(
-                PROVIDERS_NAME_MAP["compute"])
-        except (KeyError, azure_exceptions.CloudError,
-                azexceptions.AzureOperationException) as ex:
-
-            raise exception.InvalidInput(
-                "Invalid or incomplete Azure credentials provided")
-
-    def _get_cloud_credentials(self, connection_info):
-        """ returns the msrestazure.azure_active_directory.Credentials
-        implementation for the given connection_info. Should both user/pass and
-        service principal details be provided, the user/pass auth flow is
-        preffered.
-        """
-        user_creds, sp_creds = [connection_info.get(x, {}) for x in
-                                CREDENTIALS_CATEGORIES]
-
-        if user_creds and all([x in user_creds for x in
-                               USER_CREDENTIALS_FIELDS]):
-            return azure_active_directory.UserPassCredentials(
-                user_creds["username"],
-                user_creds["password"]
-            )
-
-        if sp_creds and all([x in sp_creds for x in
-                             SP_CREDENTIALS_FIELDS]):
-            return azure_active_directory.ServicePrincipalCredentials(
-                client_id=sp_creds["client_id"],
-                secret=sp_creds["client_secred"],
-                tenent=sp_creds["tenant_id"]
-            )
-
-        # NOTE: this ending raise is redundant considering the schema-based
-        # validation; but allows for another layer of validation considering
-        # a possible discrepancy between the schema and the code...
-        raise azexceptions.AzureOperationException(
-            msg="Either 'user_credentials' or 'service_principal_credentials' "
-            "must be specified in 'connection_info'.",
-            code=-1)
-
-    def _get_compute_client(self, connection_info):
-        """ Returns an azure.mgmt.compute.ComputeManagementClient.
-        """
-        return compute.ComputeManagementClient(
-            self._get_cloud_credentials(connection_info),
-            connection_info["subscription_id"])
-
-    def _get_network_client(self, connection_info):
-        """ Returns an azure.mgmt.network.NetworkResourceProviderClient. """
-        return network.NetworkManagementClient(
-            self._get_cloud_credentials(connection_info),
-            connection_info["subscription_id"])
-
-    def _get_storage_client(self, connection_info):
-        """ Returns an azure.mgmt.storage.StorageManagementClient. """
-        return storage.StorageManagementClient(
-            self._get_cloud_credentials(connection_info),
-            connection_info["subscription_id"])
-
-    def _get_resource_client(self, connection_info):
-        """ Returns an azure.mgmt.resource.ResourceManagementClient. """
-        return resources.ResourceManagementClient(
-            self._get_cloud_credentials(connection_info),
-            connection_info["subscription_id"])
-
-    def _get_page_blob_client(self, target_environment):
-        """ Returns an azure.storage.blob.PageBlobService client. """
-        return blob.PageBlobService(
-            account_name=target_environment["storage"]["account"],
-            account_key=target_environment["storage"]["key"]
-        )
-
-    def _get_block_blob_client(self, target_environment):
-        """ Returns an azure.storage.blob.PageBlobService client. """
-        return blob.BlockBlobService(
-            account_name=target_environment["storage"]["account"],
-            account_key=target_environment["storage"]["key"]
-        )
-
-    def _delete_recovery_disk(self, target_environment, vm_name):
-        """ Removes the recovery disk block blob for the given
-        instance name.
-        """
-        blobd = self._get_block_blob_client(target_environment)
-        cont_name = CONF.azure_migration_provider.migr_container_name
-
-        blob_names = [b.name
-                      for b in blobd.list_blobs(cont_name)
-                      if re.match(r"%s\..*\.status" % vm_name, b.name)]
-        if len(blob_names) > 1:
-            self._event_manager.progress_update(
-                "VM \"%s\" has more than one recovery disk. "
-                "Skipped deleting any to avoid adverse loss")
-
-        if len(blob_names) == 1:
-            blobd.delete_blob(cont_name, blob_names[0])
-
-    @utils.retry_on_error()
-    def _upload_disk(self, target_environment, disk_path, upload_name):
-        """ Uploads the disk from the provided path to an Azure blob
-        and returns the corresponding AzureStorageBlob.
-        """
-        self._event_manager.progress_update(
-            "Uploading disk from \"%s\" as \"%s\"" % (disk_path, upload_name))
-
-        def progressf(curr, total):
-            LOG.debug("Uploading '%s': %d/%d.", upload_name, curr, total)
-
-        blobd = self._get_page_blob_client(target_environment)
-
-        stor_name = target_environment["storage"]["account"]
-        cont_name = CONF.azure_migration_provider.migr_container_name
-        disk_uri = BLOB_PATH_FORMAT % (stor_name, cont_name, upload_name)
-
-        blobd.create_blob_from_path(
-            cont_name, upload_name, disk_path, progress_callback=progressf)
-
-        return AzureStorageBlob(
-            name=upload_name,
-            uri=disk_uri
-        )
-
-    @utils.retry_on_error()
-    def _create_migration_network(self, connection_info, target_environment,
-                                  migration_id):
-        """ Creates the virtual network to be used for the migration and
-        returns the response from its creation operation.
-        """
-        self._event_manager.progress_update(
-            "Creating migration network")
-
-        awaited = azutils.awaited(timeout=150)
-        netc = self._get_network_client(connection_info)
-        resgroup = MIGRATION_RESGROUP_NAME_FORMAT % migration_id
-        vn_name = MIGRATION_NETWORK_NAME_FORMAT % migration_id
-
-        awaited(netc.virtual_networks.create_or_update)(
-            resgroup,
-            vn_name,
-            network.models.VirtualNetwork(
-                location=target_environment.get(
-                    "location",
-                    CONF.azure_migration_provider.migr_location),
-                address_space=network.models.AddressSpace(
-                    # NOTE: can safely be completely arbitrary:
-                    address_prefixes=["10.0.0.0/16"]
-                ),
-                subnets=[
-                    network.models.Subnet(
-                        name=CONF.azure_migration_provider.migr_subnet_name,
-                        # NOTE: can safely be completely arbitrary:
-                        address_prefix='10.0.0.0/24'
-                    )
-                ]
-            ),
-        )
-
-        return azutils.checked(netc.virtual_networks.get)(resgroup, vn_name)
-
-    @utils.retry_on_error(
-        terminal_exceptions=[azexceptions.FatalAzureOperationException])
-    def _wait_for_vm(self, connection_info, resgroup, vm_name, period=30):
-        """ Blocks until the given VM has been started. """
-        vmclient = self._get_compute_client(connection_info).virtual_machines
-        state = azutils.checked(vmclient.get)(resgroup, vm_name)
-
-        while state.provisioning_state != "Succeeded":
-            if state.provisioning_state not in ("Creating", "Updating"):
-                raise azexceptions.FatalAzureOperationException(
-                    code=-1,
-                    msg="Awaited VM '%s' has reached an invalid state: %s" %
-                    (vm_name, state.provisioning_state)
-                )
-
-            time.sleep(period)
-            state = azutils.checked(vmclient.get)(resgroup, vm_name)
-
-    def _get_worker_osprofile(self, os_type, location, worker_name):
-        """ Returns the AzureWorkerOSProfile associated to the worker instance
-        on Azure. For Linux workers, the returned authentication token is a
-        paramiko.RSAKey the machine has been configured with.
-        For windows workers, the configured password is returned.
-        """
-        if os_type == constants.OS_TYPE_LINUX:
-            return self._get_linux_worker_osprofile(worker_name)
-        elif os_type == constants.OS_TYPE_WINDOWS:
-            return self._get_windows_worker_osprofile(
-                location, worker_name)
-
-        raise azexceptions.FatalAzureOperationException(
-            code=-1,
-            msg="Unsupported migration OS type '%s'." % os_type
-        )
-
-    def _get_linux_worker_osprofile(self, worker_name):
-        """ Returns the AzureWorkerOSProfile afferent to a Linux worker. """
-        LOG.info("Linux chosen as worker '%s' OS.", worker_name)
-
-        key = paramiko.RSAKey.generate(2048)
-
-        os_profile = compute.models.OSProfile(
-            admin_username=WORKER_USERNAME,
-            # NOTE: intentional lack of 'admin_password' provided so
-            # as to enable passwordless sudo on the target machine.
-            computer_name=worker_name,
-            linux_configuration=compute.models.LinuxConfiguration(
-                disable_password_authentication=True,
-                ssh=compute.models.SshConfiguration(
-                    public_keys=[compute.models.SshPublicKey(
-                        key_data=SSH_PUBKEY_FORMAT % key.get_base64(),
-                        path=SSH_PUBKEY_FILEPATH_FORMAT % WORKER_USERNAME
-                    )],
-                ),
-            )
-        )
-
-        return AzureWorkerOSProfile(
-            profile=os_profile,
-            token=key,
-            port=22,  # NOTE: default port used on Azure-provided images.
-            extensions=[]
-        )
-
-    def _get_windows_worker_osprofile(self, location, worker_name):
-        """ Returns the AzureWorkerOSProfile afferent to a Windows worker. """
-        LOG.info("Windows was chosen as worker '%s' OS.", worker_name)
-
-        password = azutils.get_random_password()
-        os_profile = compute.models.OSProfile(
-            computer_name=azutils.normalize_hostname(worker_name),
-            admin_username=WORKER_USERNAME,
-            admin_password=password,
-            windows_configuration=compute.models.WindowsConfiguration(
-                provision_vm_agent=True,
-                enable_automatic_updates=False,
-            )
-        )
-
-        winrm_extension = compute.models.VirtualMachineExtension(
-            location,
-            publisher=PROVIDERS_NAME_MAP["compute"],
-            virtual_machine_extension_type="CustomScriptExtension",
-            type_handler_version="1.4",
-            settings=dict(
-                fileUris=WINRM_EXTENSION_FILE_URIS,
-                commandToExecute="powershell -ExecutionPolicy Unrestricted "
-                                 "-file ConfigureWinRM.ps1 "
-                                 "*.%s.cloudapp.azure.com" % location
-            ),
-        )
-
-        return AzureWorkerOSProfile(
-            profile=os_profile,
-            token=password,
-            port=5986,  # NOTE: default port opened via the extension
-            extensions=[winrm_extension]
-        )
-
-    @utils.retry_on_error()
-    def _create_nic(self, connection_info, resgroup, nic_name,
-                    location, ip_configs):
-        """ Creates a network interface with the specified params.
-
-        NOTE: Azure does unfortunately not allow for the setting of MAC
-        addresses. A NIC's MAC is determined when the VM the NIC is attached
-        to is booted.
-        """
-
-        awaited = azutils.awaited(timeout=200)
-        net_client = self._get_network_client(connection_info)
-
-        nic = network.models.NetworkInterface(
-            location=location,
-            ip_configurations=ip_configs
-        )
-
-        awaited(net_client.network_interfaces.create_or_update)(
-            resgroup,
-            nic_name,
-            nic
-        )
-
-        return azutils.checked(net_client.network_interfaces.get)(
-            resgroup, nic_name)
-
-    @utils.retry_on_error()
-    def _create_public_ip(self, connection_info, resgroup, ip_name, location):
-        awaited = azutils.awaited(timeout=90)
-        net_client = self._get_network_client(connection_info)
-
-        awaited(net_client.public_ip_addresses.create_or_update)(
-            resgroup,
-            ip_name,
-            network.models.PublicIPAddress(
-                location=location,
-                public_ip_allocation_method=network.models.IPAllocationMethod.dynamic,
-            ),
-        )
-
-        return azutils.checked(net_client.public_ip_addresses.get)(
-            resgroup, ip_name)
-
-    def _convert_to_vhd(self, disk_path):
-        """ Converts the disk file given by its path to a fixed VHD and returns
-        the path of the resulting disk.
-
-        Does NOT check if it already is a VHD.
-        """
-        newpath = "%s.%s" % (
-            path.splitext(disk_path)[0],
-            constants.DISK_FORMAT_VHD
-        )
-
-        try:
-            utils.convert_disk_format(
-                disk_path, newpath,
-                constants.DISK_FORMAT_VHD,
-                preallocated=True
-            )
-        except Exception as ex:
-            raise azexceptions.FatalAzureOperationException(
-                code=-1,
-                msg="Unable to convert disk '%s' to fixed VHD." % disk_path
-            ) from ex
-
-        return newpath
-
-    def _migrate_disk(self, target_environment, lun,
-                      disk_path, upload_name):
-        """ Moves the given disk file to Azure storage as a page blob. """
-        disk_file_info = utils.get_disk_info(disk_path)
-
-        upload_path = disk_path
-
-        # convert disk if necessary:
-        if disk_file_info["format"] != constants.DISK_FORMAT_VHD:
-            self._event_manager.progress_update(
-                "Converting disk \"%s\" from \"%s\" to \"%s\"" %
-                (upload_name, disk_file_info["format"],
-                 constants.DISK_FORMAT_VHD))
-
-            upload_path = self._convert_to_vhd(disk_path)
-            os.remove(disk_path)
-
-        try:
-            blb = self._upload_disk(
-                target_environment, upload_path, upload_name)
-        except Exception as ex:
-            # NOTE: _upload_disk is set to retry, so it's game over here:
-            raise azexceptions.FatalAzureOperationException(
-                code=-1,
-                msg="Failed to upload disk '%s' to Azure." % upload_name
-            ) from ex
-        finally:
-            os.remove(upload_path)
-
-        return compute.models.DataDisk(
-            lun=lun,
-            name=blb.name,
-            caching=compute.models.CachingTypes.none,
-            vhd=compute.models.VirtualHardDisk(uri=blb.uri),
-            # NOTE: we must force Azure to resize the disk itself to ensure the
-            # 1MB internal size alignment without which the VM will not boot.
-            # Maximum granularity for specifying disk size on Azure is 1GB.
-            disk_size_gb=math.ceil(disk_file_info["virtual-size"] / units.Gi) + 2,
-            create_option=compute.models.DiskCreateOptionTypes.attach,
-        )
-
-    @utils.retry_on_error()
-    def _get_subnet(self, connection_info, resgroup, vn_name, sub_name):
-        """ Fetches information for the specified subnet. """
-        net_client = self._get_network_client(connection_info)
-
-        return azutils.checked(net_client.subnets.get)(
-            resgroup, vn_name, sub_name)
-
-    @utils.retry_on_error()
-    def _get_pub_ip(self, connection_info, resgroup, ip_name):
-        """ Fetches information for the given public IP address. """
-        net_client = self._get_network_client(connection_info)
-
-        return azutils.checked(net_client.public_ip_addresses.get)(
-            resgroup, ip_name)
-
-    def _get_worker_size(self, worker_name, ndisks):
-        """ Returns the required size of the worker needed to handle
-        the migration/transformation of the data disks. """
-        mapping = CONF.azure_migration_provider.worker_volume_count_to_size_map
-        volsmap = collections.OrderedDict(
-            [(n, mapping[n]) for n in sorted(mapping.keys())])
-
-        for maxvols, size in volsmap.items():
-            if ndisks <= maxvols:
-                self._event_manager.progress_update(
-                    "Migration worker size chosen as \"%s\"" %
-                    str(size).split(".")[-1])
-
-                return size
-
-        raise azexceptions.FatalAzureOperationException(
-            code=-1,
-            msg=("No Azure size suitable for migrating the instance "
-                 "as it has too many volumes (%d).", ndisks)
-        )
-
-    @utils.retry_on_error(
-        terminal_exceptions=[azexceptions.FatalAzureOperationException])
-    def _create_migration_worker(self, connection_info, target_environment,
-                                 export_info, migration_id, datadisks):
-        """ Creates and returns the connection information of the worker which
-        will perform the final operations on the migrating VM.
-        """
-        resgroup = MIGRATION_RESGROUP_NAME_FORMAT % migration_id
-        worker_name = MIGRATION_WORKER_NAME_FORMAT % migration_id
-
-        self._event_manager.progress_update("Creating migration worker")
-
-        awaited = azutils.awaited(timeout=600)
-        location = target_environment.get(
-            "location", CONF.azure_migration_provider.migr_location)
-        location = azutils.normalize_location(location)
-
-        # create the NIC and public IP:
-        self._event_manager.progress_update(
-            "Creating migration worker Public IP")
-
-        ip_name = MIGRATION_WORKER_PIP_NAME_FORMAT % migration_id
-        pub_ip = self._create_public_ip(connection_info, resgroup,
-                                        ip_name, location)
-
-        self._event_manager.progress_update(
-            "Creating migration worker NIC")
-
-        nic_name = MIGRATION_WORKER_NIC_NAME_FORMAT % migration_id
-        nic = self._create_nic(
-            connection_info, resgroup, nic_name, location,
-            ip_configs=[
-                network.models.NetworkInterfaceIPConfiguration(
-                    name=nic_name + "-ipconf",
-                    subnet=self._get_subnet(
-                        connection_info,
-                        resgroup,
-                        MIGRATION_NETWORK_NAME_FORMAT % migration_id,
-                        CONF.azure_migration_provider.migr_subnet_name
-                    ),
-                    private_ip_allocation_method=network.models.IPAllocationMethod.dynamic,
-                    public_ip_address=pub_ip,
-                )
-            ]
-        )
-
-        # create the worker:
-        worker_disk_name = AZURE_DISK_NAME_FORMAT % worker_name
-        computec = self._get_compute_client(connection_info)
-        worker_osprofile = self._get_worker_osprofile(
-            export_info["os_type"], location, worker_name)
-        worker_img = WORKER_IMAGES_MAP[export_info["os_type"]]
-        target_env_sku_map = target_environment.get("migr_image_sku_map", {})
-        conf_sku = CONF.azure_migration_provider.migr_image_sku_map.get(
-            export_info["os_type"])
-        worker_img_sku = target_environment.get(
-            "migr_image_sku",
-            target_env_sku_map.get(export_info["os_type"], conf_sku))
-
-
-        awaited(computec.virtual_machines.create_or_update)(
-            resgroup,
-            worker_name,
-            compute.models.VirtualMachine(
-                location=location,
-                os_profile=worker_osprofile.profile,
-                hardware_profile=compute.models.HardwareProfile(
-                    self._get_worker_size(worker_name, len(datadisks))
-                ),
-                network_profile=compute.models.NetworkProfile(
-                    network_interfaces=[
-                        compute.models.NetworkInterfaceReference(
-                            id=nic.id,
-                        ),
-                    ]
-                ),
-                storage_profile=compute.models.StorageProfile(
-                    os_disk=compute.models.OSDisk(
-                        caching=compute.models.CachingTypes.none,
-                        create_option=compute.models.DiskCreateOptionTypes.from_image,
-                        name=worker_disk_name,
-                        vhd=compute.models.VirtualHardDisk(
-                            BLOB_PATH_FORMAT % (
-                                target_environment["storage"]["account"],
-                                CONF.azure_migration_provider.migr_container_name,
-                                worker_disk_name
-                            )
-                        ),
-                    ),
-                    data_disks=datadisks,
-                    image_reference=compute.models.ImageReference(
-                        publisher=worker_img["publisher"],
-                        offer=worker_img["offer"],
-                        sku=worker_img_sku,
-                        version=worker_img["version"]
-                    )
-                )
-            )
-        )
-
-        self._event_manager.progress_update(
-            "Waiting for migration worker to start")
-        self._wait_for_vm(connection_info, resgroup, worker_name)
-        self._event_manager.progress_update(
-            "Migration worker has started succesfully")
-
-        # add any neccessary extensions to the Worker:
-        for ext in worker_osprofile.extensions:
-            self._event_manager.progress_update(
-                "Creating Worker VM extensions \"%s\"" % ext.name)
-
-            awaited(computec.virtual_machine_extensions.create_or_update)(
-                resgroup,
-                worker_name,
-                ext.name,
-                ext
-            )
-
-        # fetch the instance's allocated ip:
-        pub_ip = self._get_pub_ip(connection_info, resgroup, ip_name)
-
-        return AzureWorkerInstance(
-            name=worker_name,
-            ip=pub_ip.ip_address,
-            port=worker_osprofile.port,
-            datadisks=datadisks,
-            username=worker_osprofile.profile.admin_username,
-            password=worker_osprofile.profile.admin_password,
-            pkey=worker_osprofile.token
-        )
-
-    def _get_migration_os_profile(self, os_type, instance_name):
-        computer_name = instance_name
-        if os_type == constants.OS_TYPE_WINDOWS:
-            computer_name = azutils.normalize_hostname(instance_name)
-
-        profile = compute.models.OSProfile(
-            computer_name=computer_name,
-            # NOTE: here we are forced to provide a username and password to
-            # the API. These values will be ignored by the Azure agent as
-            # provisioning is turned off in the agent's configuration in
-            # the osmorphing stage.
-            admin_username="coriolis",
-            admin_password=azutils.get_random_password()
-        )
-
-        if os_type == constants.OS_TYPE_WINDOWS:
-            profile.windows_configuration = (
-                compute.models.WindowsConfiguration(
-                    provision_vm_agent=True,
-                    enable_automatic_updates=False,
-                )
-            )
-
-        return profile
-
-    def import_instance(self, ctxt, connection_info, target_environment,
-                        instance_name, export_info):
-        """ Runs the process of importing the instance to Azure. """
-        self._event_manager.progress_update(
-            "Importing instance \"%s\"" % instance_name)
-
-        awaited = azutils.awaited(300)
-        location = target_environment.get(
-            "location", CONF.azure_migration_provider.migr_location)
-        location = azutils.normalize_location(location)
-
-        migration_id = azutils.get_unique_id()[:10]
-        resgroup = MIGRATION_RESGROUP_NAME_FORMAT % migration_id
-
-        worker_info = None
-
-        try:
-            # setup storage container:
-            cont_name = CONF.azure_migration_provider.migr_container_name
-            self._event_manager.progress_update(
-                "Creating migration storage container \"%s\"" % cont_name)
-
-            blobd = self._get_page_blob_client(target_environment)
-            utils.retry_on_error()(blobd.create_container)(
-                cont_name, public_access=blob.PublicAccess.Container)
-
-            # migrate the instance's attached volumes:
-            datadisks = []
-            for lun, disk in enumerate(export_info["devices"]["disks"]):
-                LOG.info("Processing instance disk number %d", (lun + 1))
-
-                disk_name = AZURE_DISK_NAME_FORMAT % (
-                    instance_name + "_" + str(lun))
-                try:
-                    datadisk = self._migrate_disk(
-                        target_environment, lun, disk["path"], disk_name)
-                except:
-                    raise
-                else:
-                    datadisks.append(datadisk)
-
-            # setup migration resource group:
-            self._event_manager.progress_update(
-                "Creating migration resource group \"%s\"" % resgroup)
-
-            resc = self._get_resource_client(connection_info)
-            utils.retry_on_error()(
-                azutils.checked(resc.resource_groups.create_or_update))(
-                    resgroup,
-                    resources.models.ResourceGroup(location))
-
-            # create the migration network:
-            self._create_migration_network(
-                connection_info, target_environment, migration_id)
-
-            # create the worker:
-            worker_info = self._create_migration_worker(
-                connection_info, target_environment, export_info,
-                migration_id, datadisks)
-
-            # morph the images:
-            self._event_manager.progress_update(
-                "Preparing instance for new environment")
-
-            osmorpher.morph_image(
-                worker_info._asdict(),
-                export_info["os_type"],
-                constants.HYPERVISOR_HYPERV,
-                constants.PLATFORM_AZURE_RM,
-                None,
-                self._event_manager,
-                ignore_devices=["/dev/sdb"]
-            )
-
-            # delete the worker:
-            self._event_manager.progress_update("Deleting migration worker")
-            vmclient = self._get_compute_client(
-                connection_info).virtual_machines
-            utils.retry_on_error()(awaited(vmclient.delete))(
-                resgroup, worker_info.name)
-
-            # setup vm nics:
-            self._event_manager.progress_update(
-                "Setting up instance NICs")
-
-            nics = []
-            for i, nic in enumerate(export_info["devices"].get("nics", [])):
-                nic_name = nic.get("name", "%s-NIC-%d" % (instance_name, i + 1))
-                net_name = nic.get("network_name", None)
-                subnet_name = nic.get("subnet_name", None)
-                add_public_ip = False
-
-                if not net_name:
-                    net = target_environment.get("destination_network", {})
-                    net_name = net.get("name", None)
-
-                    if not net or not net_name:
-                        raise azexceptions.FatalAzureOperationException(
-                            code=-1,
-                            msg="A network to which to perform the migration "
-                                "must be specified within the target "
-                                "environment in order to add NIC '%s' to "
-                                "instance '%s'." % (
-                                    nic_name, instance_name)
-                        )
-
-                    LOG.info("'network_name' not provided for NIC '%s'. "
-                             "Attempting to attach to migration network '%s'.",
-                             nic_name, net_name)
-
-                    net_name = target_environment["destination_network"]["name"]
-                    subnet_name = target_environment[
-                        "destination_network"]["subnet"]
-                    add_public_ip = target_environment[
-                        "destination_network"]["is_public_network"]
-
-                else:
-                    network_map = target_environment.get("network_map", [])
-                    if not network_map:
-                        raise azexceptions.FatalAzureOperationException(
-                            code=-1,
-                            msg="A network map must be specified within the "
-                                "target environment in order to map the "
-                                "network of nic '%s', namely '%s'." % (
-                                    nic_name, net_name
-                                )
-                        )
-
-                    net = None
-                    for netm in network_map:
-                        if netm["source_network"] == net_name:
-                            net = netm
-                    if not net:
-                        raise azexceptions.FatalAzureOperationException(
-                            code=-1,
-                            msg="Cannot find suitable network name "
-                                "for attaching nic '%s' within the network "
-                                "map under the name '%s'" % (
-                                    nic_name, nic.get("network_name")
-                                )
-                        )
-
-                    net_name = net["destination_network"]
-                    subnet_name = net["destination_subnet"]
-                    add_public_ip = net["is_public_network"]
-
-                nic_name = azutils.normalize_resource_name(nic_name)
-
-                nic_ip_config = network.models.NetworkInterfaceIPConfiguration(
-                    name=nic_name + "-ipconf",
-                    subnet=self._get_subnet(
-                        connection_info,
-                        target_environment["resource_group"],
-                        net_name,
-                        subnet_name
-                    ),
-                    private_ip_allocation_method=network.models.IPAllocationMethod.dynamic,
-                )
-
-                if add_public_ip:
-                    ip_name = "%s-pip" % nic_name
-                    pub_ip = self._create_public_ip(
-                        connection_info,
-                        target_environment["resource_group"],
-                        ip_name, location
-                    )
-
-                    nic_ip_config.public_ip_address = pub_ip
-
-                nics.append(self._create_nic(
-                    connection_info,
-                    target_environment["resource_group"],
-                    nic_name,
-                    location,
-                    ip_configs=[nic_ip_config],
-                ))
-
-            # create the VM:
-            self._event_manager.progress_update(
-                "Starting migrated instance as \"%s\"" %
-                    azutils.normalize_resource_name(instance_name))
-
-            disk_name = AZURE_DISK_NAME_FORMAT % (
-                "".join(instance_name) + "_os_disk")
-            vm_profile = self._get_migration_os_profile(
-                export_info["os_type"], instance_name)
-
-            vmclient = self._get_compute_client(
-                connection_info).virtual_machines
-            utils.retry_on_error()(awaited(vmclient.create_or_update))(
-                target_environment["resource_group"],
-                azutils.normalize_resource_name(instance_name),
-                compute.models.VirtualMachine(
-                    location=location,
-                    os_profile=vm_profile,
-                    hardware_profile=compute.models.HardwareProfile(
-                        vm_size=compute.models.VirtualMachineSizeTypes(
-                            target_environment["size"]
-                        )
-                    ),
-                    network_profile=compute.models.NetworkProfile(
-                        network_interfaces=[compute.models.NetworkInterfaceReference(x.id)
-                                            for x in nics]
-                    ),
-                    storage_profile=compute.models.StorageProfile(
-                        os_disk=compute.models.OSDisk(
-                            name=disk_name,
-                            os_type=AZURE_OSTYPES_MAP[export_info["os_type"]],
-                            caching=compute.models.CachingTypes.none,
-                            create_option=compute.models.DiskCreateOptionTypes.from_image,
-                            # NOTE: we are guaranteed that the first disk is
-                            # the one with the OS inside it:
-                            image=worker_info.datadisks[0].vhd,
-                            vhd=compute.models.VirtualHardDisk(
-                                BLOB_PATH_FORMAT % (
-                                    target_environment["storage"]["account"],
-                                    CONF.azure_migration_provider.migr_container_name,
-                                    disk_name
-                                )
-                            ),
-                        ),
-                        # NOTE: we are guaranteed that the first disk in the
-                        # export_info is the one with the OS inside of it,
-                        # thus the rest are the datadisks:
-                        data_disks=worker_info.datadisks[1:],
-                    )
-                ),
-            )
-        except:
-            self._cleanup(connection_info, target_environment,
-                          resgroup, worker_info)
-            LOG.error(
-                "Exception occurred during import:\n" + traceback.format_exc())
-            raise
-        finally:
-            self._cleanup(connection_info, target_environment,
-                          resgroup, worker_info)
-
-    @utils.retry_on_error()
-    def _cleanup(self, connection_info, target_environment,
-                 migration_resgroup, worker_info):
-        """ Cleans up all resources for the migration. Is idempotent. """
-        awaited = azutils.awaited()
-
-        self._event_manager.progress_update("Cleaning up migration resource group")
-        resc = self._get_resource_client(connection_info)
-
-        # check if the migration resource group still exists:
-        if resc.resource_groups.check_existence(migration_resgroup):
-            # then, delete it:
-            awaited(resc.resource_groups.delete)(migration_resgroup)
-
-        if not worker_info:
-            # it means that the worker never got defined, and we may return:
-            return
-
-        worker_name = worker_info.name
-
-        # lastly, delete the worker's disks:
-        # delete worker disks:
-        blobd = self._get_page_blob_client(target_environment)
-        cont_name = CONF.azure_migration_provider.migr_container_name
-        blob_name = AZURE_DISK_NAME_FORMAT % worker_name
-
-        if blobd.exists(cont_name, blob_name):
-            blobd.delete_blob(cont_name, blob_name)
-
-        self._delete_recovery_disk(target_environment, worker_name)

+ 0 - 20
coriolis/providers/azure/exceptions.py

@@ -1,20 +0,0 @@
-# Copyright 2016 Cloudbase Solutions Srl
-# All Rights Reserved.
-
-"""
-Defines various exceptions which may arise during migrations to/from Azure.
-"""
-
-from coriolis import exception
-from coriolis.i18n import _
-
-
-class AzureOperationException(exception.CoriolisException):
-    """ Simply wraps a standard CoriolisException. """
-    message = _("Azure operation failed with code: %(code)d. "
-                "Error message: %(msg)s.")
-
-
-class FatalAzureOperationException(AzureOperationException):
-    """ Extends AzureOperationExceptions to indicate fatal errors. """
-    pass

+ 0 - 57
coriolis/providers/azure/schemas/connection_info_schema.json

@@ -1,57 +0,0 @@
-{
-  "$schema": "http://cloudbase.it/coriolis/schemas/azure_connection#",
-  "type": "object",
-  "properties": {
-    "secret_ref": {
-      "type": "string"
-    },
-    "subscription_id": {
-      "type": "string"
-    },
-    "user_credentials": {
-      "type": "object",
-      "properties": {
-        "username": {
-          "type": "string"
-        },
-        "password": {
-          "type": "string"
-        }
-      },
-      "required": [
-        "username",
-        "password"
-      ]
-    },
-    "service_principal_credentials": {
-      "type": "object",
-      "properties": {
-        "client_id": {
-          "type": "string"
-        },
-        "client_secret": {
-          "type": "string"
-        },
-        "tenant_id": {
-          "type": "string"
-        }
-      },
-      "required": [
-        "client_id",
-        "client_secret",
-        "tenant_id"
-      ]
-    }
-  },
-  "oneOf": [
-    {
-      "required": ["secret_ref"]
-    },
-    {
-      "required": ["subscription_id", "user_credentials"]
-    },
-    {
-      "required": ["subscription_id", "service_principal_credentials"]
-    }
-  ]
-}

+ 0 - 98
coriolis/providers/azure/schemas/target_environment_schema.json

@@ -1,98 +0,0 @@
-{
-  "$schema": "http://cloudbase.it/coriolis/schemas/azure_target_environment#",
-  "type": "object",
-  "properties": {
-    "secret_ref": {
-      "type": "string"
-    },
-    "size": {
-      "type": "string"
-    },
-    "location": {
-      "type": "string"
-    },
-    "resource_group": {
-      "type": "string"
-    },
-    "storage": {
-      "type": "object",
-      "properties": {
-        "account": {
-          "type": "string"
-        },
-        "key": {
-          "type": "string"
-        },
-        "container": {
-          "type": "string"
-        }
-      },
-      "required": [
-        "account",
-        "key"
-      ]
-    },
-    "migr_image_sku": {
-        "type": "string"
-    },
-    "migr_image_sku_map": {
-        "type": "object"
-    },
-    "network_map": {
-      "type": "array",
-      "items": {
-        "type": "object",
-        "properties": {
-          "source_network": {
-            "type": "string"
-          },
-          "destination_network": {
-            "type": "string"
-          },
-          "destination_subnet_name": {
-            "type": "string"
-          },
-          "is_public_network": {
-            "type": "boolean"
-          }
-        },
-        "required": [
-          "source_network",
-          "destination_network",
-          "destination_subnet",
-          "is_public_network"
-        ]
-      }
-    },
-    "destination_network": {
-      "type": "object",
-      "properties": {
-        "name": {
-          "type": "string"
-        },
-        "subnet": {
-          "type": "string"
-        },
-        "is_public_network": {
-          "type": "boolean"
-        }
-      },
-      "required": [
-        "name",
-        "subnet",
-        "is_public_network"
-      ]
-    }
-  },
-  "oneOf": [
-    {
-      "required": ["secret_ref"]
-    },
-    {
-      "required": ["size", "location", "resource_group", "storage", "destination_network"]
-    },
-    {
-      "required": ["size", "location", "resource_group", "storage", "network_map"]
-    }
-  ]
-}

+ 0 - 101
coriolis/providers/azure/utils.py

@@ -1,101 +0,0 @@
-# Copyright 2016 Cloudbase Solutions Srl
-# All Rights Reserved.
-
-"""
-General utility functions for performing Azure operations.
-"""
-
-import functools
-import random
-import string
-import uuid
-
-from oslo_config import cfg
-from oslo_log import log as logging
-
-
-CONF = cfg.CONF
-LOG = logging.getLogger(__name__)
-
-
-def get_random_password():
-    """ Returns a random password compatible with the minimal requirements of
-    Azure to be used for worker instances (namely, to contain 8+ characters
-    with any 3 of: a character (lower or uppoercase), digit or special symbol).
-    """
-    upper = random.choice(string.ascii_uppercase)
-    lower = random.choice(string.ascii_lowercase)
-    digit = random.choice(string.digits)
-
-    return "%s%s%s%s" % (
-        upper, digit, lower, get_unique_id()
-    )
-
-
-def get_unique_id():
-    """ Returns a generically Azure-friendly ID. """
-    return "".join([x for x in str(uuid.uuid4()) if x.isalnum()])
-
-
-def normalize_resource_name(name):
-    """ Normalizes a resource name.
-
-    Constraints are:
-        - alphanumeric characters or '.', '-', '_'
-        - maximum length is 80
-    """
-    return "".join([x for x in name if x.isalnum() or x in ['.', '-', '_']])
-
-
-def normalize_hostname(hostname):
-    """ Normalizes the provided hostname for Azure.
-
-    Constraints are:
-        - no special characters
-        - maximum length of 15
-    """
-    new = "".join([x for x in hostname if x.isalnum])
-    if not new:
-        new = CONF.azure_migration_provider.default_migration_hostname
-        LOG.info("Cannot normalize instance hostname '%s' for Azure. "
-                 "Defaulting to using '%s'.", hostname, new)
-
-    if len(new) > 15:
-        new = new[:15]
-
-    if new != hostname:
-        LOG.info("Normalized instance hostname '%s' to '%s' "
-                 "for booting instance on Azure.", new, hostname)
-
-    return new
-
-
-def normalize_location(location):
-    """ Normalizes a location for azure. """
-    return "".join([x.lower() for x in location if x.isalnum()])
-
-
-def checked(operation):
-    """ Forces raw status code check on an Azure operation. """
-    @functools.wraps(operation)
-    def _checked(*args, **kwargs):
-        resp = operation(*args, raw=True, **kwargs)
-
-        return resp.output
-
-    return _checked
-
-
-def awaited(timeout=90):
-    """ Awaits for the result of the given operation. """
-
-    def _awaited(operation):
-        @functools.wraps(operation)
-        def _await(*args, **kwargs):
-            resp = operation(
-                *args, long_running_operation_timeout=timeout, **kwargs)
-
-            return resp.result()
-        return _await
-
-    return _awaited

+ 1 - 1
coriolis/providers/backup_writers.py

@@ -141,7 +141,7 @@ class SSHBackupWriter(BaseBackupWriter):
         self._msg_id += 1
         self._msg_id += 1
         self._stdin.write(data)
         self._stdin.write(data)
         self._stdin.flush()
         self._stdin.flush()
-        out_msg_id = self._stdout.read(4)
+        self._stdout.read(4)
 
 
     def _open(self):
     def _open(self):
         self._connect_ssh()
         self._connect_ssh()

+ 48 - 0
coriolis/providers/base.py

@@ -2,9 +2,15 @@
 # All Rights Reserved.
 # All Rights Reserved.
 
 
 import abc
 import abc
+import itertools
 
 
+from oslo_log import log as logging
+
+from coriolis import exception
 from coriolis import schemas
 from coriolis import schemas
 
 
+LOG = logging.getLogger(__name__)
+
 
 
 class BaseProvider(object):
 class BaseProvider(object):
     __metaclass__ = abc.ABCMeta
     __metaclass__ = abc.ABCMeta
@@ -30,6 +36,9 @@ class BaseProvider(object):
                 "Error validating provider '%s' connection "
                 "Error validating provider '%s' connection "
                 "info: %s" % str(ex)) from ex
                 "info: %s" % str(ex)) from ex
 
 
+    def get_os_morphing_tools(self, conn, osmorphing_info):
+        raise exception.OSMorphingToolsNotFound()
+
 
 
 class BaseImportProvider(BaseProvider):
 class BaseImportProvider(BaseProvider):
     __metaclass__ = abc.ABCMeta
     __metaclass__ = abc.ABCMeta
@@ -60,6 +69,16 @@ class BaseImportProvider(BaseProvider):
         """
         """
         pass
         pass
 
 
+    @abc.abstractmethod
+    def finalize_import_instance(self, ctxt, connection_info,
+                                 instance_deployment_info):
+        pass
+
+    @abc.abstractmethod
+    def cleanup_failed_import_instance(self, ctxt, connection_info,
+                                       instance_deployment_info):
+        pass
+
 
 
 class BaseReplicaImportProvider(BaseProvider):
 class BaseReplicaImportProvider(BaseProvider):
     __metaclass__ = abc.ABCMeta
     __metaclass__ = abc.ABCMeta
@@ -70,6 +89,16 @@ class BaseReplicaImportProvider(BaseProvider):
                                 volumes_info, clone_disks):
                                 volumes_info, clone_disks):
         pass
         pass
 
 
+    @abc.abstractmethod
+    def finalize_replica_instance_deployment(self, ctxt, connection_info,
+                                             instance_deployment_info):
+        pass
+
+    @abc.abstractmethod
+    def cleanup_failed_replica_instance_deployment(self, ctxt, connection_info,
+                                                   instance_deployment_info):
+        pass
+
     @abc.abstractmethod
     @abc.abstractmethod
     def deploy_replica_disks(self, ctxt, connection_info, target_environment,
     def deploy_replica_disks(self, ctxt, connection_info, target_environment,
                              instance_name, export_info, volumes_info):
                              instance_name, export_info, volumes_info):
@@ -142,3 +171,22 @@ class BaseReplicaExportProvider(BaseProvider):
     @abc.abstractmethod
     @abc.abstractmethod
     def shutdown_instance(self, ctxt, connection_info, instance_name):
     def shutdown_instance(self, ctxt, connection_info, instance_name):
         pass
         pass
+
+
+def get_os_morphing_tools_helper(conn, os_morphing_tools_clss,
+                                 hypervisor_type, os_type, os_root_dir,
+                                 os_root_dev, event_manager):
+    if os_type and os_type not in os_morphing_tools_clss:
+        raise exception.OSMorphingToolsNotFound(
+            "Unsupported OS type: %s" % os_type)
+
+    for cls in os_morphing_tools_clss.get(
+            os_type, itertools.chain(*os_morphing_tools_clss.values())):
+        LOG.debug("Loading osmorphing instance: %s", cls)
+        tools = cls(
+            conn, os_root_dir, os_root_dev, hypervisor_type, event_manager)
+        LOG.debug("Testing OS morphing tools: %s", cls.__name__)
+        os_info = tools.check_os()
+        if os_info:
+            return (tools, os_info)
+    raise exception.OSMorphingToolsNotFound()

+ 10 - 7
coriolis/providers/factory.py

@@ -1,18 +1,21 @@
 # Copyright 2016 Cloudbase Solutions Srl
 # Copyright 2016 Cloudbase Solutions Srl
 # All Rights Reserved.
 # All Rights Reserved.
 
 
+from oslo_config import cfg
+
 from coriolis import constants
 from coriolis import constants
 from coriolis import exception
 from coriolis import exception
 from coriolis.providers import base
 from coriolis.providers import base
 from coriolis import utils
 from coriolis import utils
 
 
-PROVIDERS = {
-    "coriolis.providers.azure.ImportProvider",
-    "coriolis.providers.openstack.ExportProvider",
-    "coriolis.providers.openstack.ImportProvider",
-    "coriolis.providers.vmware_vsphere.ExportProvider",
-}
+serialization_opts = [
+    cfg.ListOpt('providers',
+                default=[],
+                help='List of provider class paths'),
+]
 
 
+CONF = cfg.CONF
+CONF.register_opts(serialization_opts)
 
 
 PROVIDER_TYPE_MAP = {
 PROVIDER_TYPE_MAP = {
     constants.PROVIDER_TYPE_EXPORT: base.BaseExportProvider,
     constants.PROVIDER_TYPE_EXPORT: base.BaseExportProvider,
@@ -23,7 +26,7 @@ PROVIDER_TYPE_MAP = {
 
 
 
 
 def get_provider(platform_name, provider_type, event_handler):
 def get_provider(platform_name, provider_type, event_handler):
-    for provider in PROVIDERS:
+    for provider in CONF.providers:
         cls = utils.load_class(provider)
         cls = utils.load_class(provider)
         if (cls.platform == platform_name and
         if (cls.platform == platform_name and
                 PROVIDER_TYPE_MAP[provider_type] in cls.__bases__):
                 PROVIDER_TYPE_MAP[provider_type] in cls.__bases__):

+ 0 - 5
coriolis/providers/openstack/__init__.py

@@ -1,5 +0,0 @@
-from coriolis.providers.openstack import exp
-from coriolis.providers.openstack import imp
-
-ExportProvider = exp.ExportProvider
-ImportProvider = imp.ImportProvider

+ 0 - 329
coriolis/providers/openstack/common.py

@@ -1,329 +0,0 @@
-# Copyright 2016 Cloudbase Solutions Srl
-# All Rights Reserved.
-
-import collections
-import math
-import time
-import uuid
-
-from oslo_log import log as logging
-from oslo_utils import units
-
-from coriolis import exception
-from coriolis import utils
-
-MIGRATION_TMP_FORMAT = "migration_tmp_%s"
-
-NOVA_API_VERSION = 2
-GLANCE_API_VERSION = 1
-NEUTRON_API_VERSION = '2.0'
-CINDER_API_VERSION = 2
-
-LOG = logging.getLogger(__name__)
-
-
-GlanceImage = collections.namedtuple(
-    "GlanceImage", "id format size path os_type")
-
-
-def get_unique_name():
-    return MIGRATION_TMP_FORMAT % str(uuid.uuid4())
-
-
-def create_image(glance, name, disk_path, disk_format, container_format,
-                 hypervisor_type):
-    properties = {}
-    if hypervisor_type:
-        properties["hypervisor_type"] = hypervisor_type
-
-    if glance.version == 1:
-        return _create_image_v1(glance, name, disk_path, disk_format,
-                                container_format, properties)
-    elif glance.version == 2:
-        return _create_image_v2(glance, name, disk_path, disk_format,
-                                container_format, properties)
-    else:
-        raise NotImplementedError("Unsupported Glance version")
-
-
-@utils.retry_on_error()
-def _create_image_v2(glance, name, disk_path, disk_format, container_format,
-                     properties):
-    image = glance.images.create(
-        name=name,
-        disk_format=disk_format,
-        container_format=container_format,
-        **properties)
-    try:
-        with open(disk_path, 'rb') as f:
-            glance.images.upload(image.id, f)
-        return image
-    except:
-        glance.images.delete(image.id)
-        raise
-
-
-@utils.retry_on_error()
-def _create_image_v1(glance, name, disk_path, disk_format, container_format,
-                     properties):
-    with open(disk_path, 'rb') as f:
-        return glance.images.create(
-            name=name,
-            disk_format=disk_format,
-            container_format=container_format,
-            properties=properties,
-            data=f)
-
-
-@utils.retry_on_error()
-def wait_for_image(nova, image_id, expected_status='ACTIVE'):
-    image = nova.images.get(image_id)
-    while image.status not in [expected_status, 'ERROR']:
-        LOG.debug('Image "%(id)s" in status: "%(status)s". '
-                  'Waiting for status: "%(expected_status)s".',
-                  {'id': image_id, 'status': image.status,
-                   'expected_status': expected_status})
-        time.sleep(2)
-        image = nova.images.get(image.id)
-    if image.status != expected_status:
-        raise exception.CoriolisException(
-            "Image is in status: %s" % image.status)
-
-
-@utils.retry_on_error()
-def wait_for_instance(nova, instance_id, expected_status='ACTIVE'):
-    instance = nova.servers.get(instance_id)
-    while instance.status not in [expected_status, 'ERROR']:
-        LOG.debug('Instance %(id)s status: %(status)s. '
-                  'Waiting for status: "%(expected_status)s".',
-                  {'id': instance_id, 'status': instance.status,
-                   'expected_status': expected_status})
-        time.sleep(2)
-        instance = nova.servers.get(instance.id)
-    if instance.status != expected_status:
-        raise exception.CoriolisException(
-            "VM is in status: %s" % instance.status)
-
-
-@utils.retry_on_error(terminal_exceptions=[exception.CoriolisException])
-def wait_for_instance_deletion(
-        nova, instance_id, max_retries=150, retry_period=2):
-    instances = nova.servers.findall(id=instance_id)
-    i = 0
-    while i < max_retries and instances:
-        i = i + 1
-        instance = utils.get_single_result(instances)
-        if instance.status == 'error':
-            raise exception.CoriolisException(
-                "Instance \"%s\" has reached invalid state \"%s\" while "
-                "deleting." % (instance_id, instance.status))
-
-        LOG.debug('Instance %(id)s status: %(status)s. '
-                  'Waiting %(period)s seconds for its deletion.',
-                  {'id': instance_id, 'status': instance.status,
-                   'period': retry_period})
-        time.sleep(retry_period)
-        instances = nova.servers.findall(id=instance_id)
-
-    if instances:
-        instance = utils.get_single_result(instances)
-        raise exception.CoriolisException(
-            "Max attempts of %(attempts)s reached while waiting for VM "
-            "\"%(instance_id)s\" deletion. Last known status: \"%(status)s\"" %
-            {'attempts': max_retries, 'status': instance.status,
-             'instance_id': instance_id})
-
-
-@utils.retry_on_error()
-def find_volume(cinder, volume_id):
-    volumes = cinder.volumes.findall(id=volume_id)
-    if volumes:
-        return utils.get_single_result(volumes)
-
-
-@utils.retry_on_error()
-def extend_volume(cinder, volume_id, new_size):
-    volume_size_gb = math.ceil(new_size / units.Gi)
-    cinder.volumes.extend(volume_id, volume_size_gb)
-
-
-@utils.retry_on_error()
-def get_volume_from_snapshot(cinder, snapshot_id):
-    snapshot = cinder.volume_snapshots.get(snapshot_id)
-    return cinder.volumes.get(snapshot.volume_id)
-
-
-@utils.retry_on_error()
-def create_volume(cinder, size, name, image_ref=None,
-                  snapshot_id=None, volume_type=None):
-    if snapshot_id:
-        volume_size_gb = None
-    else:
-        volume_size_gb = math.ceil(size / units.Gi)
-    return cinder.volumes.create(
-        size=volume_size_gb,
-        name=name,
-        volume_type=volume_type,
-        imageRef=image_ref,
-        snapshot_id=snapshot_id)
-
-
-@utils.retry_on_error(terminal_exceptions=[exception.NotFound])
-def get_flavor(nova, flavor_name):
-    flavors = nova.flavors.findall(name=flavor_name)
-    if not flavors:
-        raise exception.FlavorNotFound(flavor_name=flavor_name)
-    return flavors[0]
-
-
-@utils.retry_on_error(terminal_exceptions=[exception.NotFound])
-def get_image(glance, image_name):
-    images = glance.images.findall(name=image_name)
-    if not images:
-        raise exception.ImageNotFound(image_name=image_name)
-    return images[0]
-
-
-@utils.retry_on_error(terminal_exceptions=[exception.NotFound])
-def check_floating_ip_pool(nova, pool_name):
-    if not nova.floating_ip_pools.findall(name=pool_name):
-        raise exception.FloatingIPPoolNotFound(pool_name=pool_name)
-
-
-@utils.retry_on_error(terminal_exceptions=[exception.NotFound])
-def wait_for_volume(cinder, volume_id, expected_status='available'):
-    volumes = cinder.volumes.findall(id=volume_id)
-    if not volumes:
-        raise exception.VolumeNotFound(volume_id=volume_id)
-    volume = utils.get_single_result(volumes)
-
-    terminal_statuses = [expected_status, 'error']
-    if expected_status == 'in-use':
-        # if we're waiting for a volume to become attached, we are guaranteed
-        # that its status would no longer be 'available' the moment the
-        # attachment request is accepted.
-        terminal_statuses.append('available')
-
-    while volume.status not in terminal_statuses:
-        LOG.debug('Volume %(id)s status: %(status)s. '
-                  'Waiting for status: "%(expected_status)s".',
-                  {'id': volume_id, 'status': volume.status,
-                   'expected_status': expected_status})
-        time.sleep(2)
-        volume = cinder.volumes.get(volume.id)
-    if volume.status != expected_status:
-        raise exception.CoriolisException(
-            "Volume is in status: %s" % volume.status)
-
-
-@utils.retry_on_error()
-def delete_volume(cinder, volume_id):
-    volumes = cinder.volumes.findall(id=volume_id)
-    for volume in volumes:
-        volume.delete()
-
-
-@utils.retry_on_error()
-def create_volume_snapshot(cinder, volume_id, name, force=False):
-    return cinder.volume_snapshots.create(volume_id, name=name, force=force)
-
-
-@utils.retry_on_error(terminal_exceptions=[exception.NotFound])
-def wait_for_volume_snapshot(cinder, snapshot_id,
-                             expected_status='available'):
-    snapshots = cinder.volume_snapshots.findall(id=snapshot_id)
-
-    if not snapshots:
-        if expected_status == 'deleted':
-            return
-        raise exception.VolumeSnapshotNotFound(snapshot_id=snapshot_id)
-    snapshot = utils.get_single_result(snapshots)
-
-    while snapshot.status not in [expected_status, 'error']:
-        if expected_status == 'deleted' and snapshot.status == 'available':
-            LOG.debug("Cinder volume snapshot '%s' became 'available' while "
-                      "waiting for its deletion. This may be the behavior "
-                      "of the Cinder driver being used, so no further "
-                      "deletion attempts will be made.",
-                      snapshot_id)
-            return
-
-        LOG.debug('Volume snapshot %(id)s status: %(status)s. '
-                  'Waiting for status: "%(expected_status)s".',
-                  {'id': snapshot_id, 'status': snapshot.status,
-                   'expected_status': expected_status})
-        time.sleep(2)
-        if expected_status == 'deleted':
-            snapshots = cinder.volume_snapshots.findall(id=snapshot_id)
-            if not snapshots:
-                return
-            snapshot = utils.get_single_result(snapshots)
-        else:
-            snapshot = cinder.volume_snapshots.get(snapshot.id)
-    if snapshot.status != expected_status:
-        raise exception.CoriolisException(
-            "Volume snapshot is in status: %s" % snapshot.status)
-
-
-@utils.retry_on_error()
-def delete_volume_snapshot(cinder, snapshot_id):
-    snapshots = cinder.volume_snapshots.findall(id=snapshot_id)
-    for snapshot in snapshots:
-        return cinder.volume_snapshots.delete(snapshot.id)
-
-
-@utils.retry_on_error()
-def create_volume_backup(cinder, volume_id, snapshot_id, name, container,
-                         incremental, force=False):
-    return cinder.backups.create(
-        volume_id=volume_id,
-        snapshot_id=snapshot_id,
-        container=container,
-        name=name,
-        incremental=incremental,
-        force=force)
-
-
-@utils.retry_on_error(terminal_exceptions=[exception.NotFound])
-def wait_for_volume_backup(cinder, backup_id, expected_status='available'):
-    backups = cinder.backups.findall(id=backup_id)
-
-    if not backups:
-        if expected_status == 'deleted':
-            return
-        raise exception.VolumeBackupNotFound(backup_id=backup_id)
-    backup = utils.get_single_result(backups)
-
-    while backup.status not in [expected_status, 'error']:
-        LOG.debug('Volume backup %(id)s status: %(status)s. '
-                  'Waiting for status: "%(expected_status)s".',
-                  {'id': backup_id, 'status': backup.status,
-                   'expected_status': expected_status})
-        time.sleep(2)
-        if expected_status == 'deleted':
-            backups = cinder.backups.findall(id=backup_id)
-            if not backups:
-                return
-            backup = utils.get_single_result(backups)
-        else:
-            backup = cinder.backups.get(backup.id)
-    if backup.status != expected_status:
-        raise exception.CoriolisException(
-            "Volume backup is in status: %s" % backup.status)
-
-
-@utils.retry_on_error()
-def delete_volume_backup(cinder, backup_id):
-    cinder.backups.delete(backup_id)
-
-
-@utils.retry_on_error()
-def find_volume_backups(cinder, volume_id=None, container=None):
-    return cinder.backups.list(search_opts={
-        "volume_id": volume_id,
-        "container": container})
-
-
-@utils.retry_on_error()
-def get_instance_volumes(nova, instance_id):
-    return nova.volumes.get_server_volumes(instance_id)

+ 0 - 479
coriolis/providers/openstack/exp.py

@@ -1,479 +0,0 @@
-# Copyright 2016 Cloudbase Solutions Srl
-# All Rights Reserved.
-import gc
-import json
-import os
-import re
-import zlib
-
-from cinderclient import client as cinder_client
-from glanceclient import client as glance_client
-from novaclient import client as nova_client
-from oslo_config import cfg
-from oslo_log import log as logging
-from oslo_utils import units
-from swiftclient import client as swift_client
-
-from coriolis import constants
-from coriolis import events
-from coriolis import exception
-from coriolis import keystone
-from coriolis.providers import backup_writers
-from coriolis.providers import base
-from coriolis.providers.openstack import common
-from coriolis import schemas
-from coriolis import utils
-
-opts = [
-    cfg.StrOpt('volume_backups_container',
-               default="coriolis",
-               help='Cinder volume backups container name.'),
-]
-
-CONF = cfg.CONF
-CONF.register_opts(opts, 'openstack_migration_provider')
-
-LOG = logging.getLogger(__name__)
-
-VOLUME_BACKUP_VERSION = '1.0.0'
-
-
-class ExportProvider(base.BaseExportProvider, base.BaseReplicaExportProvider):
-
-    platform = constants.PLATFORM_OPENSTACK
-
-    _OS_DISTRO_MAP = {
-        'windows': constants.OS_TYPE_WINDOWS,
-        'freebsd': constants.OS_TYPE_BSD,
-        'netbsd': constants.OS_TYPE_BSD,
-        'openbsd': constants.OS_TYPE_BSD,
-        'opensolaris': constants.OS_TYPE_SOLARIS,
-        'arch': constants.OS_TYPE_LINUX,
-        'centos': constants.OS_TYPE_LINUX,
-        'debian': constants.OS_TYPE_LINUX,
-        'fedora': constants.OS_TYPE_LINUX,
-        'gentoo': constants.OS_TYPE_LINUX,
-        'mandrake': constants.OS_TYPE_LINUX,
-        'mandriva': constants.OS_TYPE_LINUX,
-        'mes': constants.OS_TYPE_LINUX,
-        'opensuse': constants.OS_TYPE_LINUX,
-        'rhel': constants.OS_TYPE_LINUX,
-        'sled': constants.OS_TYPE_LINUX,
-        'ubuntu': constants.OS_TYPE_LINUX,
-    }
-
-    connection_info_schema = schemas.get_schema(
-        __name__, schemas.PROVIDER_CONNECTION_INFO_SCHEMA_NAME)
-
-    def __init__(self, event_handler):
-        self._event_manager = events.EventManager(event_handler)
-
-    @utils.retry_on_error(terminal_exceptions=[exception.CoriolisException])
-    def _get_instance(self, nova, instance_name):
-        instances = nova.servers.list(
-            search_opts={'name': "^%s$" % instance_name})
-        if len(instances) > 1:
-            raise exception.CoriolisException(
-                'More than one instance exists with name: %s' % instance_name)
-        elif not instances:
-            raise exception.InstanceNotFound(instance_name=instance_name)
-        return instances[0]
-
-    def _get_os_type(self, image):
-        os_type = constants.DEFAULT_OS_TYPE
-        os_distro = image.properties.get('os_distro')
-        if not os_distro:
-            if 'os_type' in image.properties:
-                os_type = image.properties['os_type']
-            else:
-                self._event_manager.progress_update(
-                    "Image os_distro not set, defaulting to \"%s\"" % os_type)
-        elif os_distro not in self._OS_DISTRO_MAP:
-            self._event_manager.progress_update(
-                "Image os_distro \"%s\" not found, defaulting to \"%s\"" %
-                (os_distro, os_type))
-        else:
-            os_type = self._OS_DISTRO_MAP[os_distro]
-        return os_type
-
-    @utils.retry_on_error()
-    def _export_image(self, glance, export_path, image_id):
-        path = os.path.join(export_path, image_id)
-        LOG.debug('Saving snapshot to path: %s', export_path)
-        with open(path, 'wb') as f:
-            for chunk in glance.images.data(image_id):
-                f.write(chunk)
-
-        disk_info = utils.get_disk_info(path)
-        new_path = path + "." + disk_info['format']
-        os.rename(path, new_path)
-        LOG.debug('Renamed snapshot path: %s', new_path)
-        return new_path, disk_info['format']
-
-    @utils.retry_on_error()
-    def _create_snapshot(self, nova, glance, instance, export_path):
-        image_id = instance.create_image(common.get_unique_name())
-        try:
-            self._event_manager.progress_update(
-                "Waiting for instance snapshot to complete")
-            common.wait_for_image(nova, image_id)
-
-            image = glance.images.get(image_id)
-            image_size = image.size
-
-            if image.container_format != 'bare':
-                raise exception.CoriolisException(
-                    "Unsupported container format: %s" %
-                    image.container_format)
-
-            self._event_manager.progress_update(
-                "Exporting instance snapshot")
-
-            image_path, image_format = self._export_image(
-                glance, export_path, image_id)
-        finally:
-            self._event_manager.progress_update("Removing instance snapshot")
-
-            @utils.ignore_exceptions
-            @utils.retry_on_error()
-            def _del_image():
-                glance.images.delete(image_id)
-            _del_image()
-
-        os_type = self._get_os_type(image)
-        return common.GlanceImage(
-            id=image_id,
-            path=image_path,
-            format=image_format,
-            os_type=os_type,
-            size=image_size
-        )
-
-    def _get_instance_info(self, nova, cinder, instance_name):
-        self._event_manager.progress_update("Retrieving OpenStack instance")
-        instance = self._get_instance(nova, instance_name)
-
-        @utils.retry_on_error()
-        def _get_flavor_by_id():
-            return nova.flavors.get(instance.flavor["id"])
-        flavor = _get_flavor_by_id()
-
-        nics = []
-        for iface in instance.interface_list():
-            ips = set([ip['ip_address'] for ip in iface.fixed_ips])
-            net_name = [
-                n for n, v in instance.networks.items() if set(v) & ips][0]
-
-            nics.append({'name': iface.port_id,
-                         'id': iface.port_id,
-                         'mac_address': iface.mac_addr,
-                         'ip_addresses': list(ips),
-                         'network_id': iface.net_id,
-                         'network_name': net_name})
-
-        disks = []
-        vol_attachments = common.get_instance_volumes(nova, instance.id)
-        for vol_attachment in vol_attachments:
-            volume = cinder.volumes.get(vol_attachment.volumeId)
-            disks.append({
-                'format': constants.DISK_FORMAT_RAW,
-                'guest_device': vol_attachment.device,
-                'size_bytes': volume.size * units.Gi,
-                'storage_backend_identifier': volume.volume_type,
-                'path': '',
-                'id': volume.id
-            })
-
-        vm_info = {
-            'num_cpu': flavor.vcpus,
-            'num_cores_per_socket': 1,
-            'memory_mb': flavor.ram,
-            'nested_virtualization': False,
-            'name': instance_name,
-            'id': instance.id,
-            'flavor_name': flavor.name,
-            'devices': {
-                "nics": nics,
-                "disks": disks,
-                "cdroms": [],
-                "serial_ports": [],
-                "floppies": [],
-                "controllers": []
-            }
-        }
-
-        return instance, vm_info
-
-    def _check_shutdown_instance(self, nova, instance):
-        if instance.status != 'SHUTOFF':
-            self._event_manager.progress_update(
-                "Shutting down instance")
-
-            @utils.retry_on_error()
-            def _stop_instance():
-                instance.stop()
-
-            _stop_instance()
-            common.wait_for_instance(nova, instance.id, 'SHUTOFF')
-
-    def export_instance(self, ctxt, connection_info, instance_name,
-                        export_path):
-        session = keystone.create_keystone_session(ctxt, connection_info)
-        glance_api_version = connection_info.get("image_api_version",
-                                                 common.GLANCE_API_VERSION)
-        nova = nova_client.Client(common.NOVA_API_VERSION, session=session)
-        glance = glance_client.Client(glance_api_version, session=session)
-        cinder = cinder_client.Client(common.CINDER_API_VERSION,
-                                      session=session)
-
-        instance, vm_info = self._get_instance_info(
-            nova, cinder, instance_name)
-        self._check_shutdown_instance(nova, instance)
-
-        self._event_manager.progress_update("Creating instance snapshot")
-        image = self._create_snapshot(
-            nova, glance, instance, export_path)
-
-        disks = []
-        disks.append({
-            'format': image.format,
-            'path': image.path,
-            'size_bytes': image.size,
-            'id': str(image.id)
-        })
-
-        vm_info['os_type'] = image.os_type
-        vm_info['devices']['disks'] = disks
-
-        LOG.info("vm info: %s" % str(vm_info))
-        return vm_info
-
-    def get_replica_instance_info(self, ctxt, connection_info, instance_name):
-        session = keystone.create_keystone_session(ctxt, connection_info)
-        nova = nova_client.Client(common.NOVA_API_VERSION, session=session)
-        cinder = cinder_client.Client(common.CINDER_API_VERSION,
-                                      session=session)
-
-        instance, vm_info = self._get_instance_info(
-            nova, cinder, instance_name)
-
-        vm_info["os_type"] = constants.DEFAULT_OS_TYPE
-
-        return vm_info
-
-    def deploy_replica_source_resources(self, ctxt, connection_info):
-        return {"migr_resources": None, "connection_info": None}
-
-    def delete_replica_source_resources(self, ctxt, connection_info,
-                                        migr_resources_dict):
-        pass
-
-    @utils.retry_on_error()
-    def _get_backup_metadata(self, swift, volume_id, backup_id, container):
-        objects = swift.get_container(container)[1]
-
-        base_re = (r'volume_%(volume_id)s/\d+/az_nova_backup_%(backup_id)s' %
-                   {'volume_id': volume_id, 'backup_id': backup_id})
-
-        metadata_object_name = None
-        for obj in objects:
-            if re.match("%s_metadata" % base_re, obj["name"]):
-                metadata_object_name = obj["name"]
-                break
-
-        if not metadata_object_name:
-            raise exception.NotFound('Cinder backup metadata not found')
-
-        metadata_json = swift.get_object(container, metadata_object_name)[1]
-        metadata = json.loads(metadata_json.decode())
-
-        if metadata['version'] != VOLUME_BACKUP_VERSION:
-            raise exception.CoriolisException(
-                "Unsupported Cinder backup metadata version: %s" %
-                metadata['version'])
-
-        if metadata['backup_id'] != backup_id:
-            raise exception.CoriolisException(
-                "Metadata backup id does not match")
-
-        return metadata
-
-    @utils.retry_on_error()
-    def _read_backup_metadata_object(self, swift, metadata, container):
-        name, info = list(metadata.items())[0]
-        offset = info.get('offset', 0)
-        compression = info.get('compression')
-        md5 = info.get('md5')
-
-        data = swift.get_object(container, name)[1]
-
-        if compression == 'none':
-            pass
-        elif compression == 'zlib':
-            data = zlib.decompress(data)
-        else:
-            raise exception.CoriolisException(
-                "Unsupported compression format: %s" % compression)
-
-        if md5:
-            utils.check_md5(data, md5)
-
-        return data, offset
-
-    def replicate_disks(self, ctxt, connection_info, instance_name,
-                        source_conn_info, target_conn_info, volumes_info,
-                        incremental):
-        session = keystone.create_keystone_session(ctxt, connection_info)
-        cinder = cinder_client.Client(common.CINDER_API_VERSION,
-                                      session=session)
-        swift = swift_client.Connection(session=session)
-
-        ip = target_conn_info["ip"]
-        port = target_conn_info.get("port", 22)
-        username = target_conn_info["username"]
-        pkey = target_conn_info.get("pkey")
-        password = target_conn_info.get("password")
-
-        container = CONF.openstack_migration_provider.volume_backups_container
-
-        volumes_info, backups = self._create_volume_backups(
-            cinder, volumes_info, container)
-
-        LOG.info("Waiting for connectivity on host: %(ip)s:%(port)s",
-                 {"ip": ip, "port": port})
-        utils.wait_for_port_connectivity(ip, port)
-
-        for volume_info in volumes_info:
-            self._event_manager.progress_update(
-                "Retrieving backup metadata")
-
-            last_backup_id = volume_info.get("last_backup_id")
-            backup_metadata_list = []
-
-            volume_id = volume_info["disk_id"]
-            backup_id = backups[volume_id].id
-
-            # Other backups might have occurred since the last replica
-            # execution, make sure to replicate all data
-            while True:
-                backup_metadata = self._get_backup_metadata(
-                    swift, volume_id, backup_id, container)
-                backup_metadata_list.insert(0, backup_metadata)
-                previous_backup_id = backup_metadata.get("parent_id")
-                if (not previous_backup_id or
-                        previous_backup_id == last_backup_id):
-                    break
-                backup_id = previous_backup_id
-
-            backup_writer = backup_writers.SSHBackupWriter(
-                ip, port, username, pkey, password, volumes_info)
-
-            for backup_metadata in backup_metadata_list:
-                objects_metadata = backup_metadata["objects"]
-                backup_id = backup_metadata["backup_id"]
-
-                total_written_bytes = 0
-                total_bytes = sum([list(o.values())[0]['length']
-                                  for o in objects_metadata])
-
-                perc_step = self._event_manager.add_percentage_step(
-                    total_bytes,
-                    message_format="Volume backup %s replica progress: "
-                    "{:.0f}%%" % backup_id)
-
-                max_chunk_size = 10 * units.Mi
-
-                with backup_writer.open("", volume_id) as f:
-                    for object_metadata in objects_metadata:
-                        data, offset = self._read_backup_metadata_object(
-                            swift, object_metadata, container)
-
-                        f.seek(offset)
-
-                        i = 0
-                        while i < len(data):
-                            f.write(data[i:i + max_chunk_size])
-                            i += max_chunk_size
-
-                        total_written_bytes += len(data)
-                        self._event_manager.set_percentage_step(
-                            perc_step, total_written_bytes)
-
-                        data = None
-                        gc.collect()
-
-            volume_info["last_backup_id"] = backups[volume_id].id
-
-        return volumes_info
-
-    def _create_volume_backups(self, cinder, volumes_info, container):
-        snapshots = {}
-        backups = {}
-        try:
-            self._event_manager.progress_update("Creating volume snapshots")
-            for volume_info in volumes_info:
-                volume_id = volume_info["disk_id"]
-                snapshot = common.create_volume_snapshot(
-                    cinder, volume_id, common.get_unique_name(), force=True)
-                snapshots[volume_id] = snapshot
-
-            self._event_manager.progress_update(
-                "Waiting for volume snapshots to complete")
-            for snapshot in snapshots.values():
-                common.wait_for_volume_snapshot(cinder, snapshot.id)
-
-            self._event_manager.progress_update("Creating volume backups")
-            for volume_info in volumes_info:
-                volume_id = volume_info["disk_id"]
-
-                volume_backups = common.find_volume_backups(
-                    cinder, volume_id, container)
-                incremental = len(volume_backups) > 0
-
-                snapshot = snapshots[volume_id]
-                # TODO: add Cinder v1 check ('incremental' and 'force' are v2+)
-                volume_backup = common.create_volume_backup(
-                    cinder,
-                    volume_id=volume_id,
-                    snapshot_id=snapshot.id,
-                    container=container,
-                    name=common.get_unique_name(),
-                    incremental=incremental,
-                    # NOTE: 'force' is required by standard API definition for
-                    # any incremental backup while the VM is on, but some
-                    # Cinder backends will allow its absence.
-                    force=True
-                )
-                backups[volume_id] = volume_backup
-
-            self._event_manager.progress_update(
-                "Waiting for volume backups to complete")
-            for backup in backups.values():
-                common.wait_for_volume_backup(cinder, backup.id)
-
-            return volumes_info, backups
-        except Exception as ex:
-            LOG.exception(ex)
-            self._event_manager.progress_update(
-                "Deleting volume backups")
-            for backup in backups.values():
-                common.delete_volume_backup(cinder, backup.id)
-            self._event_manager.progress_update(
-                "Waiting for volume backups to be deleted")
-            for backup in backups.values():
-                common.wait_for_volume_backup(cinder, backup.id, 'deleted')
-            raise
-        finally:
-            self._event_manager.progress_update("Deleting volume snapshots")
-            for snapshot in snapshots.values():
-                common.delete_volume_snapshot(cinder, snapshot.id)
-            self._event_manager.progress_update(
-                "Waiting for volume snapshots to be deleted")
-            for snapshot in snapshots.values():
-                common.wait_for_volume_snapshot(cinder, snapshot.id, 'deleted')
-
-    def shutdown_instance(self, ctxt, connection_info, instance_name):
-        session = keystone.create_keystone_session(ctxt, connection_info)
-        nova = nova_client.Client(common.NOVA_API_VERSION, session=session)
-        instance = self._get_instance(nova, instance_name)
-        self._check_shutdown_instance(nova, instance)

+ 0 - 1033
coriolis/providers/openstack/imp.py

@@ -1,1033 +0,0 @@
-# Copyright 2016 Cloudbase Solutions Srl
-# All Rights Reserved.
-
-import collections
-import math
-import os
-import tempfile
-
-from cinderclient import client as cinder_client
-from glanceclient import client as glance_client
-from neutronclient.neutron import client as neutron_client
-from novaclient import client as nova_client
-from oslo_config import cfg
-from oslo_log import log as logging
-from oslo_utils import units
-import paramiko
-
-from coriolis import constants
-from coriolis import events
-from coriolis import exception
-from coriolis import keystone
-from coriolis.osmorphing import manager as osmorphing_manager
-from coriolis.providers import base
-from coriolis.providers.openstack import common
-from coriolis import schemas
-from coriolis import utils
-
-opts = [
-    cfg.StrOpt('disk_format',
-               default=constants.DISK_FORMAT_QCOW2,
-               help='Default image disk format.'),
-    cfg.StrOpt('container_format',
-               default='bare',
-               help='Default image container format.'),
-    cfg.StrOpt('hypervisor_type',
-               default=None,
-               help='Default hypervisor type.'),
-    cfg.StrOpt('boot_from_volume',
-               default=True,
-               help='Set to "True" to boot from volume by default instead of '
-               'using local storage.'),
-    cfg.StrOpt('delete_disks_on_vm_termination',
-               default=False,
-               help='Configure Cinder volumes to be deleted on an eventual '
-                    'termination of the migrated/replicated instance.'),
-    cfg.StrOpt('glance_upload',
-               default=True,
-               help='Set to "True" to use Glance to upload images.'),
-    cfg.StrOpt('default_cinder_volume_type',
-               default='',
-               help='Name of the Cinder volume type to be used for '
-                    'volumes with unspecified storage backing options.'),
-    cfg.DictOpt('migr_image_name_map',
-                default={},
-                help='Default image names used for worker instances during '
-                'migrations.'),
-    cfg.StrOpt('migr_flavor_name',
-               default='m1.small',
-               help='Default flavor name used for worker instances '
-               'during migrations.'),
-    cfg.StrOpt('migr_network_name',
-               default='private',
-               help='Default network name used for worker instances '
-               'during migrations.'),
-    cfg.StrOpt('fip_pool_name',
-               default='public',
-               help='Default floating ip pool name.'),
-]
-
-CONF = cfg.CONF
-CONF.register_opts(opts, 'openstack_migration_provider')
-
-DISK_HEADER_SIZE = 10 * units.Mi
-
-SSH_PORT = 22
-WINRM_HTTPS_PORT = 5986
-
-MIGR_USER_DATA = (
-    "#cloud-config\n"
-    "users:\n"
-    "  - name: %s\n"
-    "    ssh-authorized-keys:\n"
-    "      - %s\n"
-    "    sudo: ['ALL=(ALL) NOPASSWD:ALL']\n"
-    "    groups: sudo\n"
-    "    shell: /bin/bash\n"
-)
-MIGR_GUEST_USERNAME = 'cloudbase'
-MIGR_GUEST_USERNAME_WINDOWS = "admin"
-
-VOLUME_NAME_FORMAT = "%(instance_name)s %(num)s"
-REPLICA_VOLUME_NAME_FORMAT = "Coriolis Replica - %(instance_name)s %(num)s"
-
-LOG = logging.getLogger(__name__)
-
-
-class _MigrationResources(object):
-    def __init__(self, nova, neutron, keypair, instance, port,
-                 floating_ip, guest_port, sec_group, username, password, k):
-        self._nova = nova
-        self._neutron = neutron
-        self._instance = instance
-        self._port = port
-        self._floating_ip = floating_ip
-        self._guest_port = guest_port
-        self._sec_group = sec_group
-        self._keypair = keypair
-        self._k = k
-        self._username = username
-        self._password = password
-
-    def get_resources_dict(self):
-        return {
-            "instance_id": self._instance.id,
-            "keypair_name": self._keypair.name,
-            "port_id": self._port["id"],
-            "floating_ip_id": self._floating_ip.id,
-            "secgroup_id": self._sec_group.id,
-        }
-
-    @classmethod
-    @utils.retry_on_error()
-    def from_resources_dict(cls, nova, neutron, resources_dict):
-        instance_id = resources_dict["instance_id"]
-        keypair_name = resources_dict["keypair_name"]
-        floating_ip_id = resources_dict["floating_ip_id"]
-        secgroup_id = resources_dict["secgroup_id"]
-        port_id = resources_dict["port_id"]
-
-        instance = None
-        instances = nova.servers.findall(id=instance_id)
-        if instances:
-            instance = instances[0]
-
-        keypair = None
-        keypairs = nova.keypairs.findall(name=keypair_name)
-        if keypairs:
-            keypair = keypairs[0]
-
-        floating_ip = None
-        floating_ips = nova.floating_ips.findall(id=floating_ip_id)
-        if floating_ips:
-            floating_ip = floating_ips[0]
-
-        sec_group = None
-        sec_groups = nova.security_groups.findall(id=secgroup_id)
-        if sec_groups:
-            sec_group = sec_groups[0]
-
-        port = None
-        ports = neutron.list_ports(id=port_id)["ports"]
-        if ports:
-            port = ports[0]
-
-        return cls(
-            nova, neutron, keypair, instance, port, floating_ip, None,
-            sec_group, None, None, None)
-
-    def get_guest_connection_info(self):
-        return {
-            "ip": self._floating_ip.ip,
-            "port": self._guest_port,
-            "username": self._username,
-            "password": self._password,
-            "pkey": self._k,
-        }
-
-    def get_instance(self):
-        return self._instance
-
-    @utils.retry_on_error()
-    def delete(self):
-        if self._instance:
-            self._nova.servers.delete(self._instance)
-            common.wait_for_instance_deletion(self._nova, self._instance.id)
-            self._instance = None
-        if self._floating_ip:
-            self._nova.floating_ips.delete(self._floating_ip)
-            self._floating_ip = None
-        if self._port:
-            self._neutron.delete_port(self._port['id'])
-            self._port = None
-        if self._sec_group:
-            self._nova.security_groups.delete(self._sec_group.id)
-            self._sec_group = None
-        if self._keypair:
-            self._nova.keypairs.delete(self._keypair.name)
-            self._keypair = None
-
-
-class ImportProvider(base.BaseImportProvider, base.BaseReplicaImportProvider):
-
-    platform = constants.PLATFORM_OPENSTACK
-
-    connection_info_schema = schemas.get_schema(
-        __name__, schemas.PROVIDER_CONNECTION_INFO_SCHEMA_NAME)
-
-    target_environment_schema = schemas.get_schema(
-        __name__, schemas.PROVIDER_TARGET_ENVIRONMENT_SCHEMA_NAME)
-
-    def __init__(self, event_handler):
-        self._event_manager = events.EventManager(event_handler)
-
-    @utils.retry_on_error(terminal_exceptions=[exception.NotFound])
-    def _create_neutron_port(self, neutron, network_name, mac_address=None):
-        networks = neutron.list_networks(name=network_name)
-        if not networks['networks']:
-            raise exception.NetworkNotFound(network_name=network_name)
-        network_id = networks['networks'][0]['id']
-
-        # make sure that the port is not already existing from a previous
-        # migration attempt
-        if mac_address:
-            ports = neutron.list_ports(
-                mac_address=mac_address).get('ports', [])
-            if ports:
-                neutron.delete_port(ports[0]['id'])
-
-        body = {"port": {
-                "network_id": network_id,
-                }}
-        if mac_address:
-            body["port"]["mac_address"] = mac_address
-
-        return neutron.create_port(body=body)['port']
-
-    @utils.retry_on_error()
-    def _create_keypair(self, nova, name, public_key):
-        if nova.keypairs.findall(name=name):
-            nova.keypairs.delete(name)
-        return nova.keypairs.create(name=name, public_key=public_key)
-
-    @utils.retry_on_error(max_attempts=10, sleep_seconds=10)
-    def _get_instance_password(self, instance, k):
-        self._event_manager.progress_update("Getting instance password")
-        fd, key_path = tempfile.mkstemp()
-        try:
-            k.write_private_key_file(key_path)
-            return instance.get_password(private_key=key_path).decode()
-        finally:
-            os.close(fd)
-            os.remove(key_path)
-
-    @utils.retry_on_error(max_attempts=10, sleep_seconds=30,
-                          terminal_exceptions=[exception.NotFound])
-    def _deploy_migration_resources(self, nova, glance, neutron,
-                                    os_type, migr_image_name, migr_flavor_name,
-                                    migr_network_name, migr_fip_pool_name):
-        LOG.debug("Migration image name: %s", migr_image_name)
-        image = common.get_image(glance, migr_image_name)
-
-        LOG.debug("Migration flavor name: %s", migr_flavor_name)
-        flavor = common.get_flavor(nova, migr_flavor_name)
-
-        LOG.debug("Migration FIP pool name: %s", migr_fip_pool_name)
-        common.check_floating_ip_pool(nova, migr_fip_pool_name)
-
-        keypair = None
-        instance = None
-        floating_ip = None
-        sec_group = None
-        port = None
-
-        try:
-            migr_keypair_name = common.get_unique_name()
-
-            self._event_manager.progress_update(
-                "Creating migration worker instance keypair")
-
-            k = paramiko.RSAKey.generate(2048)
-            public_key = "ssh-rsa %s tmp@migration" % k.get_base64()
-            keypair = self._create_keypair(nova, migr_keypair_name, public_key)
-
-            self._event_manager.progress_update(
-                "Creating migration worker instance Neutron port")
-
-            port = self._create_neutron_port(neutron, migr_network_name)
-
-            # TODO(alexpilotti): use a single username
-            if os_type == constants.OS_TYPE_WINDOWS:
-                username = MIGR_GUEST_USERNAME_WINDOWS
-            else:
-                username = MIGR_GUEST_USERNAME
-
-            userdata = MIGR_USER_DATA % (username, public_key)
-            instance = nova.servers.create(
-                name=common.get_unique_name(),
-                image=image.id,
-                flavor=flavor,
-                key_name=migr_keypair_name,
-                userdata=userdata,
-                nics=[{'port-id': port['id']}])
-
-            self._event_manager.progress_update(
-                "Adding migration worker instance floating IP")
-
-            floating_ip = nova.floating_ips.create(pool=migr_fip_pool_name)
-            common.wait_for_instance(nova, instance.id, 'ACTIVE')
-
-            LOG.info("Floating IP: %s", floating_ip.ip)
-            instance.add_floating_ip(floating_ip)
-
-            self._event_manager.progress_update(
-                "Adding migration worker instance security group")
-
-            if os_type == constants.OS_TYPE_WINDOWS:
-                guest_port = WINRM_HTTPS_PORT
-            else:
-                guest_port = SSH_PORT
-
-            migr_sec_group_name = common.get_unique_name()
-            sec_group = nova.security_groups.create(
-                name=migr_sec_group_name, description=migr_sec_group_name)
-            nova.security_group_rules.create(
-                sec_group.id,
-                ip_protocol="tcp",
-                from_port=guest_port,
-                to_port=guest_port)
-            instance.add_security_group(sec_group.id)
-
-            self._event_manager.progress_update(
-                "Waiting for connectivity on host: %(ip)s:%(port)s" %
-                {"ip": floating_ip.ip, "port": guest_port})
-
-            utils.wait_for_port_connectivity(floating_ip.ip, guest_port)
-
-            if os_type == constants.OS_TYPE_WINDOWS:
-                password = self._get_instance_password(instance, k)
-            else:
-                password = None
-
-            return _MigrationResources(nova, neutron, keypair, instance, port,
-                                       floating_ip, guest_port, sec_group,
-                                       username, password, k)
-        except Exception as ex:
-            self._event_manager.progress_update(
-                "An error occurred, cleaning up worker resources: %s" %
-                str(ex))
-
-            if instance:
-                nova.servers.delete(instance)
-            if floating_ip:
-                nova.floating_ips.delete(floating_ip)
-            if port:
-                neutron.delete_port(port['id'])
-            if sec_group:
-                nova.security_groups.delete(sec_group.id)
-            if keypair:
-                nova.keypairs.delete(keypair.name)
-            raise
-
-    @utils.retry_on_error()
-    def _attach_volume(self, nova, cinder, instance, volume_id,
-                       volume_dev=None):
-        # volume can be either a Volume object or an id
-        volume = nova.volumes.create_server_volume(
-            instance.id, volume_id, volume_dev)
-        common.wait_for_volume(cinder, volume.id, 'in-use')
-        return volume
-
-    def _get_import_config(self, target_environment, os_type):
-        config = collections.namedtuple(
-            "ImportConfig",
-            ["glance_upload",
-             "target_disk_format",
-             "container_format",
-             "hypervisor_type",
-             "delete_disks_on_vm_termination",
-             "fip_pool_name",
-             "network_map",
-             "storage_map",
-             "keypair_name",
-             "migr_image_name",
-             "migr_flavor_name",
-             "migr_fip_pool_name",
-             "migr_network_name",
-             "flavor_name"])
-
-        config.glance_upload = target_environment.get(
-            "glance_upload", CONF.openstack_migration_provider.glance_upload)
-        config.target_disk_format = target_environment.get(
-            "disk_format", CONF.openstack_migration_provider.disk_format)
-        config.container_format = target_environment.get(
-            "container_format",
-            CONF.openstack_migration_provider.container_format)
-        config.hypervisor_type = target_environment.get(
-            "hypervisor_type",
-            CONF.openstack_migration_provider.hypervisor_type)
-        config.fip_pool_name = target_environment.get(
-            "fip_pool_name", CONF.openstack_migration_provider.fip_pool_name)
-        config.delete_disks_on_vm_termination = target_environment.get(
-            "delete_disks_on_vm_termination",
-            CONF.openstack_migration_provider.delete_disks_on_vm_termination)
-        config.network_map = target_environment.get("network_map", {})
-        config.storage_map = target_environment.get("storage_map", {})
-        config.keypair_name = target_environment.get("keypair_name")
-
-        config.migr_image_name = target_environment.get(
-            "migr_image_name",
-            target_environment.get("migr_image_name_map", {}).get(
-                os_type,
-                CONF.openstack_migration_provider.migr_image_name_map.get(
-                    os_type)))
-        config.migr_flavor_name = target_environment.get(
-            "migr_flavor_name",
-            CONF.openstack_migration_provider.migr_flavor_name)
-
-        config.migr_fip_pool_name = target_environment.get(
-            "migr_fip_pool_name",
-            config.fip_pool_name or
-            CONF.openstack_migration_provider.fip_pool_name)
-        config.migr_network_name = target_environment.get(
-            "migr_network_name",
-            CONF.openstack_migration_provider.migr_network_name)
-
-        config.flavor_name = target_environment.get(
-            "flavor_name", config.migr_flavor_name)
-
-        if not config.migr_image_name:
-            raise exception.CoriolisException(
-                "No matching migration image type found")
-
-        if not config.migr_network_name:
-            if len(config.network_map) != 1:
-                raise exception.CoriolisException(
-                    'If "migr_network_name" is not provided, "network_map" '
-                    'must contain exactly one entry')
-            config.migr_network_name = list(config.network_map.values())[0]
-
-        return config
-
-    @utils.retry_on_error()
-    def _convert_disk_format(
-            self, source_path, destination_path, destination_format):
-        """ Converts the  disk to the provided destination_format and
-        returns the new path to the converted disk. """
-        try:
-            LOG.info("Converting disk '%s' to %s" % (
-                     source_path,
-                     destination_format))
-            utils.convert_disk_format(
-                source_path, destination_path, destination_format)
-        except Exception as ex:
-            utils.ignore_exceptions(os.remove)(destination_path)
-            raise
-
-    def _create_images_and_volumes(self, glance, nova, cinder, config,
-                                   disks_info):
-        if not config.glance_upload:
-            raise exception.CoriolisException(
-                "Glance upload is currently required for migrations")
-
-        images = []
-        volumes = []
-
-        for disk_info in disks_info:
-            disk_path = disk_info["path"]
-            disk_file_info = utils.get_disk_info(disk_path)
-
-            target_disk_path = disk_path
-            if config.target_disk_format != disk_file_info["format"]:
-                target_disk_path = ("%s.%s" % (
-                    os.path.splitext(disk_path)[0], config.target_disk_format))
-                self._convert_disk_format(
-                    disk_path, target_disk_path, config.target_disk_format)
-                disk_file_info = utils.get_disk_info(target_disk_path)
-
-                LOG.info(
-                    "Succesfully converted '%s' to %s as '%s'. "
-                    "Removing original path." % (
-                        disk_path, config.target_disk_format,
-                        target_disk_path))
-                os.remove(disk_path)
-
-            self._event_manager.progress_update(
-                "Uploading Glance image")
-
-            disk_format = disk_file_info["format"]
-            if disk_format == "vhdx":
-                disk_format = "vhd"
-
-            image = common.create_image(
-                glance, common.get_unique_name(),
-                target_disk_path, disk_format,
-                config.container_format,
-                config.hypervisor_type)
-            images.append(image)
-
-            self._event_manager.progress_update(
-                "Waiting for Glance image to become active")
-            common.wait_for_image(nova, image.id)
-
-            virtual_disk_size = disk_file_info["virtual-size"]
-            if disk_format != constants.DISK_FORMAT_RAW:
-                virtual_disk_size += DISK_HEADER_SIZE
-
-            self._event_manager.progress_update(
-                "Creating Cinder volume")
-
-            volume_type = self._get_volume_type_for_disk(
-                cinder, disk_info, config.storage_map)
-
-            volume = common.create_volume(
-                cinder, virtual_disk_size, common.get_unique_name(),
-                image.id, volume_type=volume_type)
-            volumes.append(volume)
-
-        return images, volumes
-
-    def _get_volume_type_for_disk(self, cinder, disk_info, storage_map):
-        default = CONF.openstack_migration_provider.default_cinder_volume_type
-        if not default:
-            # NOTE: needed so as to explicitly use None instead of an
-            # empty string in case a default is not configured.
-            default = None
-
-        dest_stor = None
-        source_stor = None
-        if 'storage_backend_identifier' in disk_info:
-            # if 'storage_backend_identifier' was provided, fetch its
-            # correspondent from the 'storage_map' or use the default.
-            source_stor = disk_info['storage_backend_identifier']
-            dest_stor = storage_map.get(source_stor, None)
-            if not dest_stor:
-                LOG.debug(
-                    'Unable to find mapping for storage system "%s" in the '
-                    'storage_map for volume "%s". Setting volume type to the '
-                    'configured default of \"%s\"',
-                    source_stor, disk_info['path'], default)
-                dest_stor = default
-        else:
-            # else if unspecified, just use the default volume type:
-            LOG.debug("No 'storage_backend_identifier' provided for disk %s. "
-                     "Trying to use default volume type of '%s'", default)
-            dest_stor = default
-
-        # ensure the volume type exists:
-        if dest_stor and not utils.retry_on_error()(
-                cinder.volume_types.findall)(name=dest_stor):
-            raise exception.CoriolisException(
-                'Unable to find volume type "%s" (mapped from "%s" on the '
-                'source). Please ensure the storage_map is correct' % (
-                    dest_stor, source_stor))
-
-        LOG.info("Chosen volume_type for disk '%s' is '%s'",
-                 disk_info['path'], dest_stor)
-        return dest_stor
-
-    def _create_neutron_ports(self, neutron, config, nics_info):
-        ports = []
-
-        for nic_info in nics_info:
-            origin_network_name = nic_info.get("network_name")
-            if not origin_network_name:
-                self._event_manager.progress_update(
-                    "Origin network name not provided for nic: %s, skipping" % 
-                    nic_info.get("name"))
-                continue
-
-            network_name = config.network_map.get(origin_network_name)
-            if not network_name:
-                raise exception.CoriolisException(
-                    "Network not mapped in network_map: %s" %
-                    origin_network_name)
-
-            ports.append(self._create_neutron_port(
-                neutron, network_name, nic_info.get("mac_address")))
-
-        return ports
-
-    @utils.retry_on_error()
-    def _get_replica_volumes(self, cinder, volumes_info):
-        volumes = []
-        for volume_id in [v["volume_id"] for v in volumes_info]:
-            volumes.append(cinder.volumes.get(volume_id))
-        return volumes
-
-    @utils.retry_on_error()
-    def _rename_volumes(self, cinder, volumes, instance_name):
-        for i, volume in enumerate(volumes):
-            new_volume_name = VOLUME_NAME_FORMAT % {
-                "instance_name": instance_name, "num": i + 1}
-            cinder.volumes.update(volume.id, name=new_volume_name)
-
-    @utils.retry_on_error()
-    def _set_bootable_volumes(self, cinder, volumes):
-        # TODO: check if just setting the first volume as bootable is enough
-        for volume in volumes:
-            if not volume.bootable or volume.bootable == 'false':
-                cinder.volumes.set_bootable(volume, True)
-
-    def _create_volumes_from_replica_snapshots(self, cinder, volumes_info):
-        volumes = []
-        for volume_info in volumes_info:
-            snapshot_id = volume_info["volume_snapshot_id"]
-            volume_name = common.get_unique_name()
-
-            self._event_manager.progress_update(
-                "Creating Cinder volume from snapshot")
-
-            volume = common.create_volume(
-                cinder, None, volume_name, snapshot_id=snapshot_id)
-            volumes.append(volume)
-        return volumes
-
-    def _deploy_instance(self, ctxt, connection_info, target_environment,
-                         instance_name, export_info, volumes_info=None,
-                         clone_disks=False):
-        session = keystone.create_keystone_session(ctxt, connection_info)
-
-        glance_api_version = connection_info.get("image_api_version",
-                                                 common.GLANCE_API_VERSION)
-
-        nova = nova_client.Client(common.NOVA_API_VERSION, session=session)
-        glance = glance_client.Client(glance_api_version, session=session)
-        neutron = neutron_client.Client(common.NEUTRON_API_VERSION,
-                                        session=session)
-        cinder = cinder_client.Client(common.CINDER_API_VERSION,
-                                      session=session)
-
-        os_type = export_info.get('os_type')
-        LOG.info("os_type: %s", os_type)
-
-        config = self._get_import_config(target_environment, os_type)
-
-        images = []
-        volumes = []
-        ports = []
-
-        try:
-            if not volumes_info:
-                # Migration
-                disks_info = export_info["devices"]["disks"]
-                images, volumes = self._create_images_and_volumes(
-                    glance, nova, cinder, config, disks_info)
-            else:
-                # Migration from replica
-                if not clone_disks:
-                    volumes = self._get_replica_volumes(cinder, volumes_info)
-                else:
-                    volumes = self._create_volumes_from_replica_snapshots(
-                        cinder, volumes_info)
-
-            migr_resources = self._deploy_migration_resources(
-                nova, glance, neutron, os_type, config.migr_image_name,
-                config.migr_flavor_name, config.migr_network_name,
-                config.migr_fip_pool_name)
-
-            nics_info = export_info["devices"].get("nics", [])
-
-            try:
-                for i, volume in enumerate(volumes):
-                    common.wait_for_volume(cinder, volume.id)
-
-                    self._event_manager.progress_update(
-                        "Attaching volume to worker instance")
-
-                    self._attach_volume(
-                        nova, cinder, migr_resources.get_instance(), volume.id)
-
-                    conn_info = migr_resources.get_guest_connection_info()
-
-                osmorphing_hv_type = self._get_osmorphing_hypervisor_type(
-                    config.hypervisor_type)
-
-                self._event_manager.progress_update(
-                    "Preparing instance for target platform")
-                osmorphing_manager.morph_image(conn_info,
-                                               os_type,
-                                               osmorphing_hv_type,
-                                               constants.PLATFORM_OPENSTACK,
-                                               nics_info,
-                                               self._event_manager)
-            finally:
-                self._event_manager.progress_update(
-                    "Removing worker instance resources")
-                migr_resources.delete()
-
-            self._event_manager.progress_update("Renaming volumes")
-            self._rename_volumes(cinder, volumes, instance_name)
-
-            self._event_manager.progress_update(
-                "Ensuring volumes are bootable")
-            self._set_bootable_volumes(cinder, volumes)
-
-            self._event_manager.progress_update(
-                "Creating Neutron ports for migrated instance")
-            ports = self._create_neutron_ports(neutron, config, nics_info)
-
-            self._event_manager.progress_update(
-                "Creating migrated instance")
-            self._create_target_instance(
-                nova, config.flavor_name, instance_name,
-                config.keypair_name, ports, volumes,
-                ephemeral_volumes=config.delete_disks_on_vm_termination)
-        except:
-            if not volumes_info or clone_disks:
-                # Don't remove replica volumes
-                self._event_manager.progress_update("Deleting volumes")
-                for volume in volumes:
-                    @utils.ignore_exceptions
-                    @utils.retry_on_error()
-                    def _del_volume():
-                        volume.delete()
-                    _del_volume()
-            self._event_manager.progress_update("Deleting Neutron ports")
-            for port in ports:
-                @utils.ignore_exceptions
-                @utils.retry_on_error()
-                def _del_port():
-                    neutron.delete_port(port["id"])
-                _del_port()
-            raise
-        finally:
-            self._event_manager.progress_update("Deleting Glance images")
-            for image in images:
-                @utils.ignore_exceptions
-                @utils.retry_on_error()
-                def _del_image():
-                    image.delete()
-                _del_image()
-
-    def import_instance(self, ctxt, connection_info, target_environment,
-                        instance_name, export_info):
-        self._deploy_instance(ctxt, connection_info, target_environment,
-                              instance_name, export_info)
-
-    def _get_osmorphing_hypervisor_type(self, hypervisor_type):
-        if (hypervisor_type and
-                hypervisor_type.lower() == constants.HYPERVISOR_QEMU):
-            return constants.HYPERVISOR_KVM
-        elif hypervisor_type:
-            return hypervisor_type.lower()
-
-    @utils.retry_on_error(max_attempts=10, sleep_seconds=30,
-                          terminal_exceptions=[exception.NotFound])
-    def _create_target_instance(self, nova, flavor_name, instance_name,
-                                keypair_name, ports, volumes,
-                                ephemeral_volumes=False):
-        flavor = common.get_flavor(nova, flavor_name)
-
-        block_device_mapping = {}
-        for i, volume in enumerate(volumes):
-            # Delete volume on termination
-            block_device_mapping[
-                'vd%s' % chr(ord('a') + i)] = "%s:volume::%s" % (
-                    volume.id, ephemeral_volumes)
-
-        nics = [{'port-id': p['id']} for p in ports]
-
-        # Note: Nova requires an image even when booting from volume
-        LOG.info('Creating target instance...')
-        instance = nova.servers.create(
-            name=instance_name,
-            image='',
-            flavor=flavor,
-            key_name=keypair_name,
-            block_device_mapping=block_device_mapping,
-            nics=nics)
-
-        try:
-            common.wait_for_instance(nova, instance.id, 'ACTIVE')
-            return instance
-        except:
-            if instance:
-                nova.servers.delete(instance)
-            raise
-
-    def deploy_replica_instance(self, ctxt, connection_info,
-                                target_environment, instance_name, export_info,
-                                volumes_info, clone_disks):
-        self._deploy_instance(ctxt, connection_info, target_environment,
-                              instance_name, export_info, volumes_info,
-                              clone_disks)
-
-    def _update_existing_disk_volumes(self, cinder, disks_info, volumes_info):
-        for disk_info in disks_info:
-            disk_id = disk_info["id"]
-
-            vi = [v for v in volumes_info
-                  if v["disk_id"] == disk_id and v.get("volume_id")]
-            if vi:
-                volume_info = vi[0]
-                volume_id = volume_info["volume_id"]
-
-                volume = common.find_volume(cinder, volume_id)
-                if volume:
-                    virtual_disk_size_gb = math.ceil(
-                        disk_info["size_bytes"] / units.Gi)
-
-                    if virtual_disk_size_gb > volume.size:
-                        LOG.info(
-                            "Extending volume %(volume_id)s. "
-                            "Current size: %(curr_size)s GB, "
-                            "Requested size: %(requested_size)s GB",
-                            {"volume_id": volume_id,
-                             "curr_size": virtual_disk_size_gb,
-                             "requested_size": volume.size})
-                        self._event_manager.progress_update("Extending volume")
-                        common.extend_volume(
-                            cinder, volume_id, virtual_disk_size_gb * units.Gi)
-                    elif virtual_disk_size_gb < volume.size:
-                        LOG.warning(
-                            "Cannot shrink volume %(volume_id)s. "
-                            "Current size: %(curr_size)s GB, "
-                            "Requested size: %(requested_size)s GB",
-                            {"volume_id": volume_id,
-                             "curr_size": volume.size,
-                             "requested_size": virtual_disk_size_gb})
-                else:
-                    volumes_info.remove(volume_info)
-
-        return volumes_info
-
-    def _delete_removed_disk_volumes(self, cinder, disks_info, volumes_info):
-        for volume_info in volumes_info:
-            if volume_info["disk_id"] not in [
-                    d["id"] for d in disks_info if d["id"]]:
-
-                volume_id = volume_info["volume_id"]
-                volume = common.find_volume(cinder, volume_id)
-                if volume:
-                    self._event_manager.progress_update("Deleting volume")
-                    common.delete_volume(cinder, volume_id)
-                volumes_info.remove(volume_info)
-        return volumes_info
-
-    def _create_new_disk_volumes(
-            self, cinder, target_environment, disks_info,
-            volumes_info, instance_name):
-        try:
-            new_volumes = []
-            for i, disk_info in enumerate(disks_info):
-                disk_id = disk_info["id"]
-                virtual_disk_size = disk_info["size_bytes"]
-
-                if not [v for v in volumes_info if v["disk_id"] == disk_id]:
-                    self._event_manager.progress_update(
-                        "Creating volume")
-
-                    volume_name = REPLICA_VOLUME_NAME_FORMAT % {
-                        "instance_name": instance_name, "num": i + 1}
-
-                    storage_map = target_environment.get(
-                        'storage_map', {})
-                    volume_type = self._get_volume_type_for_disk(
-                        cinder, disk_info, storage_map)
-                    volume = common.create_volume(
-                        cinder, virtual_disk_size, volume_name,
-                        volume_type=volume_type)
-
-                    new_volumes.append(volume)
-                    volumes_info.append({
-                        "volume_id": volume.id,
-                        "disk_id": disk_id})
-                else:
-                    self._event_manager.progress_update(
-                        "Using previously deployed volume")
-
-            for volume in new_volumes:
-                common.wait_for_volume(cinder, volume.id)
-
-            return volumes_info
-        except:
-            for volume in new_volumes:
-                common.delete_volume(cinder, volume.id)
-            raise
-
-    def deploy_replica_disks(self, ctxt, connection_info, target_environment,
-                             instance_name, export_info, volumes_info):
-        session = keystone.create_keystone_session(ctxt, connection_info)
-
-        cinder = cinder_client.Client(common.CINDER_API_VERSION,
-                                      session=session)
-
-        disks_info = export_info["devices"]["disks"]
-
-        volumes_info = self._update_existing_disk_volumes(
-            cinder, disks_info, volumes_info)
-
-        volumes_info = self._delete_removed_disk_volumes(
-            cinder, disks_info, volumes_info)
-
-        volumes_info = self._create_new_disk_volumes(
-            cinder, target_environment, disks_info,
-            volumes_info, instance_name)
-
-        return volumes_info
-
-    def deploy_replica_target_resources(self, ctxt, connection_info,
-                                        target_environment, volumes_info):
-        session = keystone.create_keystone_session(ctxt, connection_info)
-
-        glance_api_version = connection_info.get("image_api_version",
-                                                 common.GLANCE_API_VERSION)
-        nova = nova_client.Client(common.NOVA_API_VERSION, session=session)
-        glance = glance_client.Client(glance_api_version, session=session)
-        neutron = neutron_client.Client(common.NEUTRON_API_VERSION,
-                                        session=session)
-        cinder = cinder_client.Client(common.CINDER_API_VERSION,
-                                      session=session)
-
-        # Data migration uses a Linux guest binary
-        os_type = constants.OS_TYPE_LINUX
-
-        config = self._get_import_config(target_environment, os_type)
-
-        migr_resources = self._deploy_migration_resources(
-            nova, glance, neutron, os_type, config.migr_image_name,
-            config.migr_flavor_name, config.migr_network_name,
-            config.migr_fip_pool_name)
-
-        try:
-            for i, volume_info in enumerate(volumes_info):
-                self._event_manager.progress_update(
-                    "Attaching volume to worker instance")
-
-                volume_id = volume_info["volume_id"]
-                ret_volume = self._attach_volume(
-                    nova, cinder, migr_resources.get_instance(), volume_id)
-                volume_info["volume_dev"] = ret_volume.device
-
-            return {
-                "migr_resources": migr_resources.get_resources_dict(),
-                "volumes_info": volumes_info,
-                "connection_info": migr_resources.get_guest_connection_info(),
-            }
-        except:
-            self._event_manager.progress_update(
-                "Removing worker instance resources")
-            migr_resources.delete()
-            raise
-
-    def delete_replica_target_resources(self, ctxt, connection_info,
-                                        migr_resources_dict):
-        session = keystone.create_keystone_session(ctxt, connection_info)
-
-        nova = nova_client.Client(common.NOVA_API_VERSION, session=session)
-        neutron = neutron_client.Client(common.NEUTRON_API_VERSION,
-                                        session=session)
-
-        migr_resources = _MigrationResources.from_resources_dict(
-            nova, neutron, migr_resources_dict)
-        self._event_manager.progress_update(
-            "Removing worker instance resources")
-        migr_resources.delete()
-
-    def delete_replica_disks(self, ctxt, connection_info, volumes_info):
-        session = keystone.create_keystone_session(ctxt, connection_info)
-
-        cinder = cinder_client.Client(common.CINDER_API_VERSION,
-                                      session=session)
-
-        self._event_manager.progress_update(
-            "Removing replica disk volumes")
-        for volume_info in volumes_info:
-            common.delete_volume(cinder, volume_info["volume_id"])
-
-    def create_replica_disk_snapshots(self, ctxt, connection_info,
-                                      volumes_info):
-        session = keystone.create_keystone_session(ctxt, connection_info)
-
-        cinder = cinder_client.Client(common.CINDER_API_VERSION,
-                                      session=session)
-
-        snapshots = []
-        self._event_manager.progress_update(
-            "Creating replica disk snapshots")
-        for volume_info in volumes_info:
-            snapshot = common.create_volume_snapshot(
-                cinder, volume_info["volume_id"], common.get_unique_name())
-            snapshots.append(snapshot)
-            volume_info["volume_snapshot_id"] = snapshot.id
-
-        for snapshot in snapshots:
-            common.wait_for_volume_snapshot(cinder, snapshot.id)
-
-        return volumes_info
-
-    def delete_replica_disk_snapshots(self, ctxt, connection_info,
-                                      volumes_info):
-        session = keystone.create_keystone_session(ctxt, connection_info)
-
-        cinder = cinder_client.Client(common.CINDER_API_VERSION,
-                                      session=session)
-
-        self._event_manager.progress_update(
-            "Removing replica disk snapshots")
-        for volume_info in volumes_info:
-            snapshot_id = volume_info.get("volume_snapshot_id")
-            if snapshot_id:
-                common.delete_volume_snapshot(cinder, snapshot_id)
-                common.wait_for_volume_snapshot(cinder, snapshot_id, 'deleted')
-                volume_info["volume_snapshot_id"] = None
-
-    def restore_replica_disk_snapshots(self, ctxt, connection_info,
-                                       volumes_info):
-        session = keystone.create_keystone_session(ctxt, connection_info)
-
-        cinder = cinder_client.Client(common.CINDER_API_VERSION,
-                                      session=session)
-
-        self._event_manager.progress_update(
-            "Restoring replica disk snapshots")
-
-        new_volumes = []
-        try:
-            for volume_info in volumes_info:
-                snapshot_id = volume_info.get("volume_snapshot_id")
-                if snapshot_id:
-                    original_volume = common.get_volume_from_snapshot(
-                        cinder, snapshot_id)
-
-                    volume_name = original_volume.name
-                    volume = common.create_volume(
-                        cinder, None, volume_name, snapshot_id=snapshot_id)
-                    new_volumes.append((volume_info, snapshot_id, volume))
-
-            for volume_info, snapshot_id, volume in new_volumes:
-                old_volume_id = volume_info["volume_id"]
-                common.wait_for_volume(cinder, volume.id)
-                common.delete_volume_snapshot(cinder, snapshot_id)
-                common.wait_for_volume_snapshot(cinder, snapshot_id, 'deleted')
-                common.delete_volume(cinder, old_volume_id)
-
-                volume_info["volume_id"] = volume.id
-                volume_info["volume_snapshot_id"] = None
-        except:
-            for _, _, volume in new_volumes:
-                common.delete_volume(cinder, volume.id)
-            raise
-
-        return volumes_info

+ 0 - 64
coriolis/providers/openstack/schemas/connection_info_schema.json

@@ -1,64 +0,0 @@
-{
-  "$schema": "http://cloudbase.it/coriolis/schemas/openstack_connection#",
-  "oneOf": [
-    {
-      "type": "object",
-      "properties": {
-        "identity_api_version": {
-          "type": "integer",
-          "minimum": 2,
-          "maximum": 3
-        },
-        "username": {
-          "type": "string"
-        },
-        "password": {
-          "type": "string"
-        },
-        "project_name": {
-          "type": "string"
-        },
-        "user_domain_name": {
-          "type": "string"
-        },
-        "project_domain_name": {
-          "type": "string"
-        },
-        "auth_url": {
-          "type": "string"
-        },
-        "allow_untrusted": {
-          "type": "boolean",
-          "default": false
-        }
-      },
-      "required": [
-        "username",
-        "password",
-        "project_name",
-        "auth_url"
-      ],
-      "additionalProperties": false
-    },
-    {
-      "type": "object",
-      "properties": {
-        "secret_ref": {
-          "type": "string",
-          "format": "uri"
-        }
-      },
-      "required": ["secret_ref"],
-      "additionalProperties": false
-    },
-    {
-      "type": "object",
-      "properties": {
-      },
-      "additionalProperties": false
-    },
-    {
-      "type": "null"
-    }
-  ]
-}

+ 0 - 57
coriolis/providers/openstack/schemas/target_environment_schema.json

@@ -1,57 +0,0 @@
-{
-  "$schema": "http://cloudbase.it/coriolis/schemas/openstack_target_environment#",
-  "oneOf": [
-    {
-      "type": "object",
-      "properties": {
-        "network_map": {
-          "type": "object"
-        },
-        "storage_map": {
-          "type": "object"
-        },
-        "glance_upload": {
-          "type": "boolean"
-        },
-        "disk_format": {
-          "type": "string"
-        },
-        "delete_disks_on_vm_termination": {
-            "type": "boolean"
-        },
-        "container_format": {
-          "type": "string"
-        },
-        "hypervisor_type": {
-          "type": "string"
-        },
-        "migr_image_name": {
-          "type": "string"
-        },
-        "migr_image_name_map": {
-          "type": "object"
-        },
-        "migr_flavor_name": {
-          "type": "string"
-        },
-        "flavor_name": {
-          "type": "string"
-        },
-        "fip_pool_name": {
-          "type": "string"
-        },
-        "migr_fip_pool_name": {
-          "type": "string"
-        },
-        "keypair_name": {
-          "type": "string"
-        }
-      },
-      "required": [
-        "network_map",
-        "flavor_name"
-      ],
-      "additionalProperties": false
-    }
-  ]
-}

+ 0 - 647
coriolis/providers/vmware_vsphere/__init__.py

@@ -1,647 +0,0 @@
-# Copyright 2016 Cloudbase Solutions Srl
-# All Rights Reserved.
-
-import contextlib
-import gc
-import os
-import re
-import sys
-import threading
-import time
-from urllib import request
-import uuid
-
-import eventlet
-from oslo_config import cfg
-from oslo_log import log as logging
-from oslo_utils import units
-from pyVim import connect
-from pyVmomi import vim
-
-from coriolis import constants
-from coriolis import events
-from coriolis import exception
-from coriolis.providers import backup_writers
-from coriolis.providers import base
-from coriolis.providers.vmware_vsphere import guestid
-from coriolis import schemas
-from coriolis.providers.vmware_vsphere import vixdisklib
-from coriolis import utils
-
-vmware_vsphere_opts = [
-    cfg.StrOpt('vdiskmanager_path',
-               default='vmware-vdiskmanager',
-               help='The vmware-vdiskmanager path.'),
-]
-
-CONF = cfg.CONF
-CONF.register_opts(vmware_vsphere_opts, 'vmware_vsphere')
-
-LOG = logging.getLogger(__name__)
-
-vixdisklib.init()
-
-
-class ExportProvider(base.BaseExportProvider, base.BaseReplicaExportProvider):
-
-    platform = constants.PLATFORM_VMWARE_VSPHERE
-
-    connection_info_schema = schemas.get_schema(
-        __name__, schemas.PROVIDER_CONNECTION_INFO_SCHEMA_NAME)
-
-    def __init__(self, event_handler):
-        self._event_manager = events.EventManager(event_handler)
-
-    @utils.retry_on_error()
-    def _convert_disk_type(self, disk_path, target_disk_path, target_type=0):
-        utils.exec_process([CONF.vmware_vsphere.vdiskmanager_path, "-r",
-                            disk_path, "-t", str(target_type),
-                            target_disk_path])
-
-    def _wait_for_task(self, task):
-        while task.info.state not in [vim.TaskInfo.State.success,
-                                      vim.TaskInfo.State.error]:
-            time.sleep(.1)
-        if task.info.state == vim.TaskInfo.State.error:
-            raise exception.CoriolisException(task.info.error.msg)
-
-    @staticmethod
-    def _keep_alive_vmware_conn(si, exit_event):
-        try:
-            while True:
-                LOG.debug("VMware connection keep alive")
-                si.CurrentTime()
-                if exit_event.wait(60):
-                    return
-        finally:
-            LOG.debug("Exiting VMware connection keep alive thread")
-
-    @utils.retry_on_error()
-    @contextlib.contextmanager
-    def _connect(self, connection_info):
-        host = connection_info["host"]
-        port = connection_info.get("port", 443)
-        username = connection_info["username"]
-        password = connection_info["password"]
-        allow_untrusted = connection_info.get("allow_untrusted", False)
-
-        # pyVmomi locks otherwise
-        sys.modules['socket'] = eventlet.patcher.original('socket')
-        ssl = eventlet.patcher.original('ssl')
-
-        context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
-        if allow_untrusted:
-            context.verify_mode = ssl.CERT_NONE
-
-        LOG.info("Connecting to: %s:%s" % (host, port))
-        si = connect.SmartConnect(
-            host=host,
-            user=username,
-            pwd=password,
-            port=port,
-            sslContext=context)
-
-        thread = None
-        try:
-            thread_exit_event = threading.Event()
-            thread = threading.Thread(
-                target=self._keep_alive_vmware_conn,
-                args=(si, thread_exit_event))
-            thread.start()
-
-            yield context, si
-        finally:
-            connect.Disconnect(si)
-            if thread:
-                thread_exit_event.set()
-                thread.join()
-
-    def _wait_for_vm_status(self, vm, status, max_wait=120):
-        i = 0
-        while i < max_wait and vm.runtime.powerState != status:
-            time.sleep(1)
-            i += 1
-        return i < max_wait
-
-    def _get_vm(self, si, instance_path):
-        vm = None
-        container = si.content.rootFolder
-        path_items = [p.replace('\\/', '/') for p in
-                      re.split(r'(?<!\\)/', instance_path)]
-        if len(path_items) == 1:
-            if len(container.childEntity) > 1:
-                raise exception.InvalidInput(
-                    "There's more than one container in the VMWare root "
-                    "folder, please specify the full path for the VM, e.g. "
-                    "\"Datacenter1/VM1\"")
-            else:
-                container = container.childEntity[0].vmFolder
-
-        LOG.debug("VM path items: %s", path_items)
-        for i, path_item in enumerate(path_items):
-            l = [o for o in container.childEntity if o.name == path_item]
-            if not l:
-                raise exception.InstanceNotFound(instance_name=instance_path)
-            item = l[0]
-            if (i + 1 == len(path_items) and
-                    isinstance(item, vim.VirtualMachine)):
-                vm = item
-            elif isinstance(item, vim.Datacenter):
-                container = item.vmFolder
-            else:
-                container = item
-
-        if vm is None:
-            raise exception.InstanceNotFound(instance_name=instance_path)
-
-        return vm
-
-    @utils.retry_on_error()
-    def _shutdown_vm(self, vm):
-        if vm.runtime.powerState != vim.VirtualMachinePowerState.poweredOff:
-            power_off = True
-            if (vm.guest.toolsRunningStatus !=
-                    vim.vm.GuestInfo.ToolsRunningStatus.guestToolsNotRunning):
-                self._event_manager.progress_update("Shutting down guest OS")
-                vm.ShutdownGuest()
-                if self._wait_for_vm_status(
-                        vm, vim.VirtualMachinePowerState.poweredOff):
-                    power_off = False
-
-            if power_off:
-                self._event_manager.progress_update(
-                    "Powering off the virtual machine")
-                task = vm.PowerOff()
-                self._wait_for_task(task)
-
-    @utils.retry_on_error()
-    def _get_vm_info(self, si, instance_path):
-
-        LOG.info("Retrieving data for VM: %s" % instance_path)
-        vm = self._get_vm(si, instance_path)
-
-        firmware_type_map = {
-            vim.vm.GuestOsDescriptor.FirmwareType.bios:
-                constants.FIRMWARE_TYPE_BIOS,
-            vim.vm.GuestOsDescriptor.FirmwareType.efi:
-                constants.FIRMWARE_TYPE_EFI
-        }
-
-        vm_info = {
-            'num_cpu': vm.config.hardware.numCPU,
-            'num_cores_per_socket': vm.config.hardware.numCoresPerSocket,
-            'memory_mb': vm.config.hardware.memoryMB,
-            'firmware_type': firmware_type_map[vm.config.firmware],
-            'nested_virtualization': vm.config.nestedHVEnabled,
-            'dynamic_memory_enabled':
-                not vm.config.memoryReservationLockedToMax,
-            'name': vm.config.name,
-            'guest_id': vm.config.guestId,
-            'os_type': guestid.GUEST_ID_OS_TYPE_MAP.get(vm.config.guestId),
-            'id': vm._moId,
-        }
-
-        LOG.info("vm info: %s" % str(vm_info))
-
-        disk_ctrls = []
-        devices = [d for d in vm.config.hardware.device if
-                   isinstance(d, vim.vm.device.VirtualController)]
-        for device in devices:
-            ctrl_type = None
-            if isinstance(device, vim.vm.device.VirtualPCIController):
-                ctrl_type = "PCI"
-            elif isinstance(device, vim.vm.device.VirtualSIOController):
-                ctrl_type = "SIO"
-            elif isinstance(device, vim.vm.device.VirtualIDEController):
-                ctrl_type = "IDE"
-            elif isinstance(device, vim.vm.device.VirtualSATAController):
-                ctrl_type = "SATA"
-            elif isinstance(device, vim.vm.device.VirtualSCSIController):
-                ctrl_type = "SCSI"
-            else:
-                continue
-            disk_ctrls.append({'id': device.key, 'type': ctrl_type,
-                               'bus_number': device.busNumber})
-
-        disks = []
-        devices = [d for d in vm.config.hardware.device if
-                   isinstance(d, vim.vm.device.VirtualDisk)]
-        for device in devices:
-            size = None
-            if hasattr(device, 'capacityInBytes'):
-                # vSphere >= 5.5
-                size = device.capacityInBytes
-            elif hasattr(device, 'capacityInKB'):
-                # vSphere <= 5.5, though left for backwards compatibility
-                size = device.capacityInKB * units.Ki
-            else:
-                raise exception.CoriolisException(
-                    "Cannot determine size of disk "
-                    "%s" % device.backing.fileName)
-
-            disks.append({'size_bytes': size,
-                          'unit_number': device.unitNumber,
-                          'id': device.key,
-                          'controller_id': device.controllerKey,
-                          'storage_backend_identifier':
-                              device.backing.datastore.name,
-                          'path': device.backing.fileName,
-                          'format': constants.DISK_FORMAT_VMDK})
-
-        cdroms = []
-        devices = [d for d in vm.config.hardware.device if
-                   isinstance(d, vim.vm.device.VirtualCdrom)]
-        for device in devices:
-            cdroms.append({'unit_number': device.unitNumber, 'id': device.key,
-                           'controller_id': device.controllerKey})
-
-        floppies = []
-        devices = [d for d in vm.config.hardware.device if
-                   isinstance(d, vim.vm.device.VirtualFloppy)]
-        for device in devices:
-            floppies.append({'unit_number': device.unitNumber,
-                             'id': device.key,
-                             'controller_id': device.controllerKey})
-
-        nics = []
-        devices = [d for d in vm.config.hardware.device if
-                   isinstance(d, vim.vm.device.VirtualEthernetCard)]
-        for device in devices:
-            nics.append({'mac_address': device.macAddress, 'id': device.key,
-                         'name': device.deviceInfo.label,
-                         'network_name': device.backing.network.name})
-
-        serial_ports = []
-        devices = [d for d in vm.config.hardware.device if
-                   isinstance(d, vim.vm.device.VirtualSerialPort)]
-        for device in devices:
-            serial_ports.append({'id': device.key})
-
-        boot_order = []
-        for boot_device in vm.config.bootOptions.bootOrder:
-            if isinstance(boot_device, vim.vm.BootOptions.BootableDiskDevice):
-                boot_order.append({"type": "disk",
-                                   "id": boot_device.deviceKey})
-            elif isinstance(boot_device,
-                            vim.vm.BootOptions.BootableCdromDevice):
-                boot_order.append({"type": "cdrom", "id": None})
-            elif isinstance(boot_device,
-                            vim.vm.BootOptions.BootableEthernetDevice):
-                boot_order.append({"type": "ethernet",
-                                   "id": boot_device.deviceKey})
-            elif isinstance(boot_device,
-                            vim.vm.BootOptions.BootableFloppyDevice):
-                boot_order.append({"type": "floppy", "id": None})
-
-        vm_info["devices"] = {
-            "nics": nics,
-            "controllers": disk_ctrls,
-            "disks": disks,
-            "cdroms": cdroms,
-            "floppies": floppies,
-            "serial_ports": serial_ports
-        }
-        vm_info["boot_order"] = boot_order
-
-        return vm_info, vm
-
-    @utils.retry_on_error()
-    def _export_disks(self, vm, export_path, context):
-        disk_paths = []
-
-        self._shutdown_vm(vm)
-        lease = vm.ExportVm()
-        while True:
-            if lease.state == vim.HttpNfcLease.State.ready:
-                try:
-                    tot_downloaded_bytes = 0
-                    for du in [du for du in lease.info.deviceUrl if du.disk]:
-                        # Key format: '/vm-70/VirtualLsiLogicController0:0'
-                        ctrl_str, address = du.key[
-                            du.key.rindex('/') + 1:].split(':')
-
-                        def _get_class_name(obj):
-                            return obj.__class__.__name__.split('.')[-1]
-
-                        for i, ctrl in enumerate(
-                            [d for d in vm.config.hardware.device if
-                             isinstance(
-                                d, vim.vm.device.VirtualController) and
-                                ctrl_str.startswith(_get_class_name(d))]):
-                            if int(ctrl_str[len(_get_class_name(ctrl)):]) == i:
-                                disk_key = [
-                                    d for d in vm.config.hardware.device if
-                                    isinstance(
-                                        d, vim.vm.device.VirtualDisk) and
-                                    d.controllerKey == ctrl.key and
-                                    d.unitNumber == int(address)][0].key
-                                break
-
-                        response = request.urlopen(du.url, context=context)
-                        path = os.path.join(export_path, du.targetId)
-                        disk_paths.append({
-                            'path': path,
-                            'id': disk_key,
-                            'format': constants.DISK_FORMAT_VMDK})
-
-                        LOG.info("Downloading: %s" % path)
-                        with open(path, 'wb') as f:
-                            while True:
-                                chunk = response.read(1024 * 1024)
-                                if not chunk:
-                                    break
-                                tot_downloaded_bytes += len(chunk)
-                                f.write(chunk)
-                                lease.HttpNfcLeaseProgress(
-                                    int(tot_downloaded_bytes * 100 /
-                                        (lease.info.totalDiskCapacityInKB *
-                                         1024)))
-
-                    lease.HttpNfcLeaseComplete()
-                    return disk_paths
-                except:
-                    lease.HttpNfcLeaseAbort()
-                    raise
-            elif lease.state == vim.HttpNfcLease.State.error:
-                raise exception.CoriolisException(lease.error.msg)
-            else:
-                time.sleep(.1)
-
-    def _connect_vixdisklib(self, connection_info, context,
-                            vmx_spec, snapshot_ref):
-        host = connection_info["host"]
-        port = connection_info.get("port", 443)
-        username = connection_info["username"]
-        password = connection_info["password"]
-
-        thumbprint = utils.get_ssl_cert_thumbprint(context, host, port)
-
-        return vixdisklib.connect(
-            server_name=host,
-            port=port,
-            thumbprint=thumbprint,
-            username=username,
-            password=password,
-            vmx_spec=vmx_spec,
-            snapshot_ref=snapshot_ref)
-
-    def _take_vm_snapshot(self, vm, snapshot_name, memory=False, quiesce=True):
-        task = vm.CreateSnapshot_Task(
-            name=snapshot_name, memory=False, quiesce=True)
-        self._wait_for_task(task)
-        return task.info.result
-
-    def _remove_vm_snapshot(self, snapshot, remove_children=False):
-        self._wait_for_task(snapshot.RemoveSnapshot_Task(remove_children))
-
-    @contextlib.contextmanager
-    def _take_temp_vm_snapshot(self, vm, snapshot_name, memory=False,
-                               quiesce=True):
-        self._event_manager.progress_update("Creating snapshot")
-        snapshot = self._take_vm_snapshot(vm, snapshot_name, memory, quiesce)
-        try:
-            yield snapshot
-        finally:
-            self._event_manager.progress_update("Removing snapshot")
-            self._remove_vm_snapshot(snapshot)
-
-    @utils.retry_on_error()
-    def _backup_snapshot_disks(self, snapshot, export_path, connection_info,
-                               context, disk_paths, backup_writer,
-                               incremental):
-        vm = snapshot.vm
-        vmx_spec = "moref=%s" % vm._GetMoId()
-        snapshot_ref = snapshot._GetMoId()
-        pos = 0
-        sector_size = vixdisklib.VIXDISKLIB_SECTOR_SIZE
-        max_sectors_per_read = 10 * 2048  # 10 MB
-
-        with self._connect_vixdisklib(connection_info, context,
-                                      vmx_spec, snapshot_ref) as conn:
-            for disk in [d for d in snapshot.config.hardware.device
-                         if isinstance(d, vim.vm.device.VirtualDisk)]:
-                change_id = '*'
-
-                l = [d for d in disk_paths if d['id'] == disk.key]
-                if l:
-                    disk_path = l[0]
-                    if incremental:
-                        change_id = disk_path.get("change_id", "*")
-                    path = disk_path["path"]
-                else:
-                    path = os.path.join(export_path, "disk-%s.raw" % disk.key)
-                    disk_path = {
-                        'path': path,
-                        'id': disk.key,
-                        'format': constants.DISK_FORMAT_RAW}
-                    disk_paths.append(disk_path)
-
-                LOG.info("CBT change id: %s", change_id)
-                changed_disk_areas = vm.QueryChangedDiskAreas(
-                    snapshot, disk.key, pos, change_id)
-
-                backup_disk_path = disk.backing.fileName
-
-                disk_size = changed_disk_areas.length
-                changed_area_size = sum(
-                    [x.length for x in changed_disk_areas.changedArea])
-
-                if not changed_area_size:
-                    self._event_manager.progress_update(
-                        "No blocks to replicate for disk: %s" %
-                        backup_disk_path)
-                    continue
-
-                if change_id == '*':
-                    self._event_manager.progress_update(
-                        "Performing full CBT replica for disk: {path}. "
-                        "Disk size: {disk_size:,}. Written blocks size: "
-                        "{changed_area_size:,}".format(
-                            path=backup_disk_path,
-                            disk_size=disk_size,
-                            changed_area_size=changed_area_size))
-                else:
-                    self._event_manager.progress_update(
-                        "Performing incremental CBT replica for disk: {path}. "
-                        "Disk size: {disk_size:,}. Changed blocks size: "
-                        "{changed_area_size:,}".format(
-                            path=backup_disk_path,
-                            disk_size=disk_size,
-                            changed_area_size=changed_area_size))
-
-                with vixdisklib.open(
-                        conn, backup_disk_path) as disk_handle:
-
-                    with backup_writer.open(path, disk.key) as f:
-                        # Create a sparse file
-                        f.truncate(disk.capacityInBytes)
-
-                        total_written_bytes = 0
-                        perc_step = self._event_manager.add_percentage_step(
-                            changed_area_size,
-                            message_format="Disk %s replica progress: "
-                            "{:.0f}%%" % backup_disk_path)
-
-                        for area in changed_disk_areas.changedArea:
-                            start_sector = area.start // sector_size
-                            num_sectors = area.length // sector_size
-
-                            f.seek(area.start)
-
-                            i = 0
-                            while i < num_sectors:
-                                curr_num_sectors = min(
-                                    num_sectors - i, max_sectors_per_read)
-
-                                buf_size = curr_num_sectors * sector_size
-                                buf = vixdisklib.get_buffer(buf_size)
-
-                                LOG.debug(
-                                    "Read start sector: %s, num sectors: %s" %
-                                    (start_sector + i, curr_num_sectors))
-
-                                vixdisklib.read(
-                                    disk_handle, start_sector + i,
-                                    curr_num_sectors, buf)
-                                i += curr_num_sectors
-
-                                f.write(buf.raw)
-
-                                buf = None
-                                gc.collect()
-
-                                total_written_bytes += buf_size
-                                self._event_manager.set_percentage_step(
-                                    perc_step, total_written_bytes)
-
-                disk_path['change_id'] = disk.backing.changeId
-
-    def _backup_disks(self, vm, export_path, connection_info, context):
-        if not vm.config.changeTrackingEnabled:
-            raise exception.CoriolisException("Change Tracking not enabled")
-
-        disk_paths = []
-
-        LOG.info("First backup pass")
-        snapshot_name = str(uuid.uuid4())
-        with self._take_temp_vm_snapshot(vm, snapshot_name) as snapshot:
-            self._backup_snapshot_disks(
-                snapshot, export_path, connection_info, context,
-                disk_paths, backup_writers.FileBackupWriter(),
-                incremental=False)
-
-        self._shutdown_vm(vm)
-
-        LOG.info("Second backup pass")
-        snapshot_name = str(uuid.uuid4())
-        with self._take_temp_vm_snapshot(vm, snapshot_name) as snapshot:
-            self._backup_snapshot_disks(
-                snapshot, export_path, connection_info, context,
-                disk_paths, backup_writers.FileBackupWriter(),
-                incremental=True)
-
-        return disk_paths
-
-    def export_instance(self, ctxt, connection_info, instance_name,
-                        export_path):
-        self._event_manager.progress_update("Connecting to vSphere host")
-        with self._connect(connection_info) as (context, si):
-            self._event_manager.progress_update(
-                "Retrieving virtual machine data")
-            vm_info, vm = self._get_vm_info(si, instance_name)
-
-            # Take advantage of CBT if available
-            backup_disks = vm.config.changeTrackingEnabled
-
-            self._event_manager.progress_update("Exporting disks")
-            if backup_disks:
-                disk_paths = self._backup_disks(
-                    vm, export_path, connection_info, context)
-            else:
-                disk_paths = self._export_disks(vm, export_path, context)
-
-        if not backup_disks:
-            self._event_manager.progress_update(
-                "Converting virtual disks format")
-            for disk_path in disk_paths:
-                path = disk_path["path"]
-                LOG.info("Converting VMDK type: %s" % path)
-                tmp_path = "%s.tmp" % path
-                self._convert_disk_type(path, tmp_path)
-                os.remove(path)
-                os.rename(tmp_path, path)
-
-        disks = vm_info["devices"]["disks"]
-
-        for disk_path in disk_paths:
-            disk_info = [d for d in disks if d["id"] == disk_path["id"]][0]
-            disk_info["format"] = disk_path["format"]
-            disk_info["path"] = os.path.abspath(disk_path["path"])
-
-        return vm_info
-
-    def get_replica_instance_info(self, ctxt, connection_info, instance_name):
-        self._event_manager.progress_update("Connecting to vSphere host")
-        with self._connect(connection_info) as (context, si):
-            self._event_manager.progress_update(
-                "Retrieving virtual machine data")
-            vm_info, vm = self._get_vm_info(si, instance_name)
-
-            if not vm.config.changeTrackingEnabled:
-                raise exception.CoriolisException(
-                    "Changed Block Tracking must be enabled in order to "
-                    "replicate a VM")
-
-        return vm_info
-
-    def shutdown_instance(self, ctxt, connection_info, instance_name):
-        self._event_manager.progress_update("Connecting to vSphere host")
-        with self._connect(connection_info) as (context, si):
-            vm = self._get_vm(si, instance_name)
-            self._shutdown_vm(vm)
-
-    def replicate_disks(self, ctxt, connection_info, instance_name,
-                        source_conn_info, target_conn_info, volumes_info,
-                        incremental):
-        ip = target_conn_info["ip"]
-        port = target_conn_info.get("port", 22)
-        username = target_conn_info["username"]
-        pkey = target_conn_info.get("pkey")
-        password = target_conn_info.get("password")
-
-        LOG.info("Waiting for connectivity on host: %(ip)s:%(port)s",
-                 {"ip": ip, "port": port})
-        utils.wait_for_port_connectivity(ip, port)
-
-        with self._connect(connection_info) as (context, si):
-            vm = self._get_vm(si, instance_name)
-
-            backup_writer = backup_writers.SSHBackupWriter(
-                ip, port, username, pkey, password, volumes_info)
-
-            snapshot_name = str(uuid.uuid4())
-            disk_paths = []
-            for volume_info in volumes_info:
-                disk_paths.append(
-                    {"id": volume_info["disk_id"],
-                     "change_id": volume_info.get("change_id", "*"),
-                     "path": ""})
-
-            with self._take_temp_vm_snapshot(vm, snapshot_name) as snapshot:
-                self._backup_snapshot_disks(
-                    snapshot, "", connection_info, context,
-                    disk_paths, backup_writer, incremental)
-
-            for volume_info in volumes_info:
-                change_id = [d["change_id"] for d in disk_paths
-                             if d["id"] == volume_info["disk_id"]][0]
-                volume_info["change_id"] = change_id
-
-        return volumes_info
-
-    def deploy_replica_source_resources(self, ctxt, connection_info):
-        return {"migr_resources": None, "connection_info": None}
-
-    def delete_replica_source_resources(self, ctxt, connection_info,
-                                        migr_resources_dict):
-        pass

+ 0 - 250
coriolis/providers/vmware_vsphere/guestid.py

@@ -1,250 +0,0 @@
-# Copyright 2016 Cloudbase Solutions Srl
-# All Rights Reserved.
-
-from pyVmomi import vim
-
-from coriolis import constants
-
-
-GUEST_ID_OS_TYPE_MAP = {
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.winNetStandard64Guest:
-    constants.OS_TYPE_WINDOWS,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.win95Guest:
-    constants.OS_TYPE_WINDOWS,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.centosGuest:
-    constants.OS_TYPE_LINUX,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.other24xLinux64Guest:
-    constants.OS_TYPE_LINUX,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.fedoraGuest:
-    constants.OS_TYPE_LINUX,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.asianux3Guest:
-    constants.OS_TYPE_LINUX,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.slesGuest:
-    constants.OS_TYPE_LINUX,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.darwin11Guest:
-    constants.OS_TYPE_OS_X,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.darwin13_64Guest:
-    constants.OS_TYPE_OS_X,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.win31Guest:
-    constants.OS_TYPE_WINDOWS,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.rhel3Guest:
-    constants.OS_TYPE_LINUX,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.openServer6Guest:
-    None,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.winVista64Guest:
-    constants.OS_TYPE_WINDOWS,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.windows7Server64Guest:
-    constants.OS_TYPE_WINDOWS,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.solaris6Guest:
-    constants.OS_TYPE_SOLARIS,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.rhel7Guest:
-    constants.OS_TYPE_LINUX,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.solaris7Guest:
-    constants.OS_TYPE_SOLARIS,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.debian6_64Guest:
-    constants.OS_TYPE_LINUX,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.debian7_64Guest:
-    constants.OS_TYPE_LINUX,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.winLonghorn64Guest:
-    constants.OS_TYPE_WINDOWS,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.darwin11_64Guest:
-    constants.OS_TYPE_OS_X,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.mandrakeGuest:
-    constants.OS_TYPE_LINUX,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.winVistaGuest:
-    constants.OS_TYPE_WINDOWS,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.windows7Guest:
-    constants.OS_TYPE_WINDOWS,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.sles10_64Guest:
-    constants.OS_TYPE_LINUX,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.windows7_64Guest:
-    constants.OS_TYPE_WINDOWS,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.solaris10_64Guest:
-    constants.OS_TYPE_SOLARIS,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.winNTGuest:
-    constants.OS_TYPE_WINDOWS,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.sles12_64Guest:
-    constants.OS_TYPE_LINUX,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.winNetDatacenter64Guest:
-    constants.OS_TYPE_WINDOWS,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.win98Guest:
-    constants.OS_TYPE_WINDOWS,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.winNetWebGuest:
-    constants.OS_TYPE_WINDOWS,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.genericLinuxGuest:
-    constants.OS_TYPE_LINUX,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.darwin10_64Guest:
-    constants.OS_TYPE_OS_X,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.netware6Guest:
-    None,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.debian7Guest:
-    constants.OS_TYPE_LINUX,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.freebsd64Guest:
-    constants.OS_TYPE_BSD,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.solaris10Guest:
-    constants.OS_TYPE_SOLARIS,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.other24xLinuxGuest:
-    constants.OS_TYPE_LINUX,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.windows8Server64Guest:
-    constants.OS_TYPE_WINDOWS,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.fedora64Guest:
-    constants.OS_TYPE_LINUX,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.debian6Guest:
-    constants.OS_TYPE_LINUX,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.win2000AdvServGuest:
-    constants.OS_TYPE_WINDOWS,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.nld9Guest:
-    None,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.asianux4_64Guest:
-    constants.OS_TYPE_LINUX,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.rhel7_64Guest:
-    constants.OS_TYPE_LINUX,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.darwin12_64Guest:
-    constants.OS_TYPE_OS_X,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.sles10Guest:
-    constants.OS_TYPE_LINUX,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.darwinGuest:
-    constants.OS_TYPE_OS_X,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.solaris8Guest:
-    constants.OS_TYPE_SOLARIS,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.win2000ProGuest:
-    constants.OS_TYPE_WINDOWS,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.winMeGuest:
-    constants.OS_TYPE_WINDOWS,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.eComStation2Guest:
-    None,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.winNetStandardGuest:
-    constants.OS_TYPE_WINDOWS,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.mandrivaGuest:
-    constants.OS_TYPE_LINUX,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.opensuse64Guest:
-    constants.OS_TYPE_LINUX,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.suseGuest:
-    constants.OS_TYPE_LINUX,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.windows8_64Guest:
-    constants.OS_TYPE_WINDOWS,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.windowsHyperVGuest:
-    constants.OS_TYPE_WINDOWS,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.rhel2Guest:
-    constants.OS_TYPE_LINUX,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.rhel5Guest:
-    constants.OS_TYPE_LINUX,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.other3xLinuxGuest:
-    constants.OS_TYPE_LINUX,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.netware4Guest:
-    None,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.other26xLinuxGuest:
-    constants.OS_TYPE_LINUX,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.opensuseGuest:
-    constants.OS_TYPE_LINUX,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.winXPHomeGuest:
-    constants.OS_TYPE_WINDOWS,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.openServer5Guest:
-    None,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.winLonghornGuest:
-    constants.OS_TYPE_WINDOWS,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.sles11_64Guest:
-    constants.OS_TYPE_LINUX,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.rhel4Guest:
-    constants.OS_TYPE_LINUX,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.rhel6_64Guest:
-    constants.OS_TYPE_LINUX,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.debian5_64Guest:
-    constants.OS_TYPE_LINUX,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.sles12Guest:
-    constants.OS_TYPE_LINUX,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.solaris11_64Guest:
-    constants.OS_TYPE_SOLARIS,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.oesGuest:
-    None,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.turboLinux64Guest:
-    constants.OS_TYPE_LINUX,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.centos64Guest:
-    constants.OS_TYPE_LINUX,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.oracleLinuxGuest:
-    constants.OS_TYPE_LINUX,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.oracleLinux64Guest:
-    constants.OS_TYPE_LINUX,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.os2Guest:
-    None,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.debian4Guest:
-    constants.OS_TYPE_LINUX,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.otherGuest:
-    None,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.sles11Guest:
-    constants.OS_TYPE_LINUX,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.windows8Guest:
-    constants.OS_TYPE_WINDOWS,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.netware5Guest:
-    None,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.other3xLinux64Guest:
-    constants.OS_TYPE_LINUX,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.mandriva64Guest:
-    constants.OS_TYPE_LINUX,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.other26xLinux64Guest:
-    constants.OS_TYPE_LINUX,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.ubuntuGuest:
-    constants.OS_TYPE_LINUX,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.winNetDatacenterGuest:
-    constants.OS_TYPE_WINDOWS,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.otherGuest64:
-    None,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.suse64Guest:
-    constants.OS_TYPE_LINUX,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.redhatGuest:
-    constants.OS_TYPE_LINUX,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.vmkernelGuest:
-    None,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.winXPPro64Guest:
-    constants.OS_TYPE_WINDOWS,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.unixWare7Guest:
-    None,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.otherLinux64Guest:
-    constants.OS_TYPE_LINUX,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.turboLinuxGuest:
-    constants.OS_TYPE_LINUX,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.dosGuest:
-    None,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.otherLinuxGuest:
-    constants.OS_TYPE_LINUX,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.darwin10Guest:
-    constants.OS_TYPE_OS_X,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.debian5Guest:
-    constants.OS_TYPE_LINUX,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.win2000ServGuest:
-    constants.OS_TYPE_WINDOWS,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.rhel4_64Guest:
-    constants.OS_TYPE_LINUX,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.rhel5_64Guest:
-    constants.OS_TYPE_LINUX,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.debian4_64Guest:
-    constants.OS_TYPE_LINUX,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.ubuntu64Guest:
-    constants.OS_TYPE_LINUX,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.asianux4Guest:
-    constants.OS_TYPE_LINUX,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.winNetEnterprise64Guest:
-    constants.OS_TYPE_WINDOWS,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.darwin64Guest:
-    constants.OS_TYPE_OS_X,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.sjdsGuest:
-    None,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.rhel6Guest:
-    constants.OS_TYPE_LINUX,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.freebsdGuest:
-    constants.OS_TYPE_BSD,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.eComStationGuest:
-    None,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.sles64Guest:
-    constants.OS_TYPE_LINUX,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.asianux3_64Guest:
-    constants.OS_TYPE_LINUX,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.winNetEnterpriseGuest:
-    constants.OS_TYPE_WINDOWS,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.winXPProGuest:
-    constants.OS_TYPE_WINDOWS,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.vmkernel5Guest:
-    None,
-    vim.vm.GuestOsDescriptor.GuestOsIdentifier.solaris9Guest:
-    constants.OS_TYPE_SOLARIS,
-}

+ 0 - 42
coriolis/providers/vmware_vsphere/schemas/connection_info_schema.json

@@ -1,42 +0,0 @@
-{
-  "$schema": "http://cloudbase.it/coriolis/schemas/vmware_vsphere_connection#",
-  "oneOf": [
-    {
-      "type": "object",
-      "properties": {
-        "host": {
-          "type": "string"
-        },
-        "port": {
-          "type": "integer"
-        },
-        "username": {
-          "type": "string"
-        },
-        "password": {
-          "type": "string"
-        },
-        "allow_untrusted": {
-          "type": "boolean"
-        }
-      },
-      "required": [
-        "host",
-        "port",
-        "username",
-        "password"
-      ],
-      "additionalProperties": false
-    },
-    {
-      "type": "object",
-      "properties": {
-        "secret_ref": {
-          "type": "string"
-        }
-      },
-      "required": ["secret_ref"],
-      "additionalProperties": false
-    }
-  ]
-}

+ 0 - 244
coriolis/providers/vmware_vsphere/vixdisklib.py

@@ -1,244 +0,0 @@
-# Copyright 2016 Cloudbase Solutions Srl
-# All Rights Reserved.
-
-import contextlib
-import ctypes
-import os
-
-from oslo_log import log as logging
-
-LOG = logging.getLogger(__name__)
-
-if os.name == 'nt':
-    vixDiskLibName = 'vixDiskLib.dll'
-else:
-    vixDiskLibName = 'libvixDiskLib.so'
-
-vixDiskLib = ctypes.cdll.LoadLibrary(vixDiskLibName)
-
-
-class VixDiskLibUidPasswdCreds(ctypes.Structure):
-    _fields_ = [
-        ("userName", ctypes.c_char_p),
-        ("password", ctypes.c_char_p),
-    ]
-
-
-class VixDiskLibSessionIdCreds(ctypes.Structure):
-    _fields_ = [
-        ("cookie", ctypes.c_char_p),
-        ("userName", ctypes.c_char_p),
-        ("key", ctypes.c_char_p),
-    ]
-
-
-class VixDiskLibCreds(ctypes.Union):
-    _fields_ = [
-        ("uid", VixDiskLibUidPasswdCreds),
-        ("sessionId", VixDiskLibSessionIdCreds),
-    ]
-
-
-class VixDiskLibConnectParams(ctypes.Structure):
-    _fields_ = [
-        ("vmxSpec", ctypes.c_char_p),
-        ("serverName", ctypes.c_char_p),
-        ("thumbPrint", ctypes.c_char_p),
-        # Note: this is 32bit on Windows
-        ("privateUse", ctypes.c_longlong),
-        ("credType", ctypes.c_uint32),
-        ("creds", VixDiskLibCreds),
-        ("port", ctypes.c_uint32),
-        ("nfcHostPort", ctypes.c_uint32),
-    ]
-
-
-class VixDiskLibConnection(ctypes.Structure):
-    _fields_ = []
-
-
-vixDiskLib.VixDiskLib_InitEx.argtypes = [
-    ctypes.c_uint32, ctypes.c_uint32, ctypes.c_void_p, ctypes.c_void_p,
-    ctypes.c_void_p, ctypes.c_char_p, ctypes.c_char_p]
-vixDiskLib.VixDiskLib_InitEx.restype = ctypes.c_uint64
-
-
-vixDiskLib.VixDiskLib_GetErrorText.argtypes = [
-    ctypes.c_uint64, ctypes.c_char_p]
-vixDiskLib.VixDiskLib_GetErrorText.restype = ctypes.c_void_p
-
-vixDiskLib.VixDiskLib_FreeErrorText.arg_types = [ctypes.c_char_p]
-vixDiskLib.VixDiskLib_FreeErrorText.restype = None
-
-vixDiskLib.VixDiskLib_ListTransportModes.argtypes = []
-vixDiskLib.VixDiskLib_ListTransportModes.restype = ctypes.c_char_p
-
-vixDiskLib.VixDiskLib_ConnectEx.argtypes = [
-    ctypes.POINTER(VixDiskLibConnectParams), ctypes.c_char, ctypes.c_char_p,
-    ctypes.c_char_p, ctypes.POINTER(ctypes.c_void_p)]
-vixDiskLib.VixDiskLib_ConnectEx.restype = ctypes.c_uint64
-
-vixDiskLib.VixDiskLib_Open.argtypes = [
-    ctypes.c_void_p, ctypes.c_char_p, ctypes.c_uint32,
-    ctypes.POINTER(ctypes.c_void_p)]
-vixDiskLib.VixDiskLib_Open.restype = ctypes.c_uint64
-
-vixDiskLib.VixDiskLib_Read.argtypes = [
-    ctypes.c_void_p, ctypes.c_uint64, ctypes.c_uint64, ctypes.c_char_p]
-vixDiskLib.VixDiskLib_Read.restype = ctypes.c_uint64
-
-vixDiskLib.VixDiskLib_GetMetadataKeys.argtypes = [
-    ctypes.c_void_p, ctypes.c_char_p, ctypes.c_uint64,
-    ctypes.POINTER(ctypes.c_uint64)]
-vixDiskLib.VixDiskLib_GetMetadataKeys.restype = ctypes.c_uint64
-
-vixDiskLib.VixDiskLib_ReadMetadata.argtypes = [
-    ctypes.c_void_p, ctypes.c_char_p, ctypes.c_char_p, ctypes.c_uint64,
-    ctypes.POINTER(ctypes.c_uint64)]
-vixDiskLib.VixDiskLib_ReadMetadata.restype = ctypes.c_uint64
-
-vixDiskLib.VixDiskLib_Close.argtypes = [ctypes.c_void_p]
-vixDiskLib.VixDiskLib_Close.restype = ctypes.c_uint64
-
-vixDiskLib.VixDiskLib_Disconnect.argtypes = [ctypes.c_void_p]
-vixDiskLib.VixDiskLib_Disconnect.restype = ctypes.c_uint64
-
-vixDiskLib.VixDiskLib_Exit.argtypes = []
-vixDiskLib.VixDiskLib_Exit.restype = None
-
-
-VIXDISKLIB_VERSION_MAJOR = 6
-VIXDISKLIB_VERSION_MINOR = 0
-
-VIXDISKLIB_SECTOR_SIZE = 512
-
-VIXDISKLIB_CRED_UID = 1
-
-VIXDISKLIB_FLAG_OPEN_UNBUFFERED = 1
-VIXDISKLIB_FLAG_OPEN_SINGLE_LINK = 2
-VIXDISKLIB_FLAG_OPEN_READ_ONLY = 4
-
-VIX_OK = 0
-VIX_E_BUFFER_TOOSMALL = 24
-
-
-def _check_err(err, allowed_values=[VIX_OK]):
-    if err not in allowed_values:
-        err_msg = vixDiskLib.VixDiskLib_GetErrorText(err, None)
-        err_msg_copy = str(ctypes.cast(
-            err_msg, ctypes.c_char_p).value.decode())
-        vixDiskLib.VixDiskLib_FreeErrorText(err_msg)
-        raise Exception(err_msg_copy)
-
-
-def init(config_path=None, major_ver=VIXDISKLIB_VERSION_MAJOR,
-         minor_ver=VIXDISKLIB_VERSION_MINOR):
-    if config_path:
-        config_path = config_path.encode()
-
-    _check_err(vixDiskLib.VixDiskLib_InitEx(
-        major_ver, minor_ver, None, None, None, None, config_path))
-
-
-def get_transport_modes():
-    transport_modes = vixDiskLib.VixDiskLib_ListTransportModes()
-    return transport_modes.decode().split(':')
-
-
-@contextlib.contextmanager
-def connect(server_name, thumbprint, username, password, vmx_spec=None,
-            snapshot_ref=None, read_only=True, transport_modes=None, port=443):
-    LOG.debug("Connecting VixDiskLib: %s", server_name)
-
-    connectParams = VixDiskLibConnectParams()
-
-    connectParams.serverName = server_name.encode()
-    if vmx_spec:
-        connectParams.vmxSpec = vmx_spec.encode()
-    if thumbprint:
-        connectParams.thumbPrint = thumbprint.encode()
-
-    connectParams.credType = VIXDISKLIB_CRED_UID
-    connectParams.creds.uid.userName = username.encode()
-    connectParams.creds.uid.password = password.encode()
-    connectParams.port = port
-
-    if transport_modes:
-        transport_modes = transport_modes.encode()
-
-    if snapshot_ref:
-        snapshot_ref = snapshot_ref.encode()
-
-    conn = ctypes.c_void_p()
-    _check_err(vixDiskLib.VixDiskLib_ConnectEx(
-        connectParams, read_only, snapshot_ref, transport_modes,
-        ctypes.byref(conn)))
-    try:
-        yield conn
-    finally:
-        disconnect(conn)
-
-
-@contextlib.contextmanager
-def open(conn, disk_path, flags=VIXDISKLIB_FLAG_OPEN_READ_ONLY):
-    LOG.debug("Openning VixDiskLib disk: %s", disk_path)
-
-    disk_handle = ctypes.c_void_p()
-    _check_err(vixDiskLib.VixDiskLib_Open(
-        conn, disk_path.encode(), flags, ctypes.byref(disk_handle)))
-    try:
-        yield disk_handle
-    finally:
-        close(disk_handle)
-
-
-def get_metadata_keys(disk_handle):
-    buf_len = ctypes.c_uint64()
-
-    _check_err(vixDiskLib.VixDiskLib_GetMetadataKeys(
-        disk_handle, None, 0, ctypes.byref(buf_len)),
-        [VIX_OK, VIX_E_BUFFER_TOOSMALL])
-
-    buf = ctypes.create_string_buffer(buf_len.value)
-    _check_err(vixDiskLib.VixDiskLib_GetMetadataKeys(
-        disk_handle, buf, buf_len, None))
-
-    return [k.decode() for k in buf.raw.split(b'\0') if len(k)]
-
-
-def read_metadata(disk_handle, key):
-    key = key.encode()
-    buf_len = ctypes.c_uint64()
-
-    _check_err(vixDiskLib.VixDiskLib_ReadMetadata(
-        disk_handle, key, None, 0, ctypes.byref(buf_len)),
-        [VIX_OK, VIX_E_BUFFER_TOOSMALL])
-
-    buf = ctypes.create_string_buffer(buf_len.value)
-    _check_err(vixDiskLib.VixDiskLib_ReadMetadata(
-        disk_handle, key, buf, buf_len, None))
-
-    return buf.value.decode()
-
-
-def get_buffer(size):
-    return ctypes.create_string_buffer(size)
-
-
-def read(disk_handle, start_sector, num_sectors, buf):
-    _check_err(vixDiskLib.VixDiskLib_Read(
-        disk_handle, start_sector, num_sectors, buf))
-
-
-def close(disk_handle):
-    LOG.debug("Closing VixDiskLib disk handle: %s", disk_handle)
-    _check_err(vixDiskLib.VixDiskLib_Close(disk_handle))
-
-
-def disconnect(conn):
-    LOG.debug("Disconnecting VixDiskLib")
-    _check_err(vixDiskLib.VixDiskLib_Disconnect(conn))
-
-
-def exit():
-    vixDiskLib.VixDiskLib_Exit()

+ 1 - 2
coriolis/schemas_exceptions.py

@@ -3,8 +3,6 @@
 
 
 """ Defines a set of exceptions possible during schema loading/validation. """
 """ Defines a set of exceptions possible during schema loading/validation. """
 
 
-import json
-
 import jinja2
 import jinja2
 import jsonschema
 import jsonschema
 
 
@@ -15,6 +13,7 @@ class CoriolisSchemaException(exception.CoriolisException):
     """ Base class for all coriolis schema handling exceptions. """
     """ Base class for all coriolis schema handling exceptions. """
     message = "Exception occured during schema validation: %(msg)s."
     message = "Exception occured during schema validation: %(msg)s."
 
 
+
 class CoriolisSchemaValidationError(
 class CoriolisSchemaValidationError(
         CoriolisSchemaException, jsonschema.ValidationError):
         CoriolisSchemaException, jsonschema.ValidationError):
     """ Raised when a schema validation has failed. """
     """ Raised when a schema validation has failed. """

+ 0 - 37
coriolis/service.py

@@ -1,7 +1,6 @@
 # Copyright 2016 Cloudbase Solutions Srl
 # Copyright 2016 Cloudbase Solutions Srl
 # All Rights Reserved.
 # All Rights Reserved.
 
 
-import os
 import platform
 import platform
 
 
 from oslo_concurrency import processutils
 from oslo_concurrency import processutils
@@ -95,39 +94,3 @@ class MessagingService(service.ServiceBase):
 
 
     def reset(self):
     def reset(self):
         self._server.reset()
         self._server.reset()
-
-
-'''
-_launcher = None
-
-def serve(server, workers=None):
-    global _launcher
-    if _launcher:
-        raise RuntimeError(_('serve() can only be called once'))
-
-    _launcher = service.launch(CONF, server, workers=workers)
-
-
-def wait():
-    try:
-        _launcher.wait()
-    except KeyboardInterrupt:
-        _launcher.stop()
-
-
-class Launcher(object):
-    def __init__(self):
-        self.launch_service = serve
-        self.wait = wait
-
-
-def get_process_launcher():
-    # Note(lpetrut): ProcessLauncher uses green pipes which fail on Windows
-    # due to missing support of non-blocking I/O pipes. For this reason, the
-    # service must be spawned differently on Windows, using the ServiceLauncher
-    # class instead.
-    if os.name == 'nt':
-        return Launcher()
-    else:
-        return service.ProcessLauncher(CONF)
-'''

+ 28 - 0
coriolis/tasks/base.py

@@ -4,9 +4,19 @@
 import abc
 import abc
 
 
 from coriolis import secrets
 from coriolis import secrets
+from coriolis import utils
 
 
+from oslo_config import cfg
 from oslo_log import log as logging
 from oslo_log import log as logging
 
 
+serialization_opts = [
+    cfg.StrOpt('temp_keypair_password',
+               default=None,
+               help='Password to be used when serializing temporary keys'),
+]
+
+CONF = cfg.CONF
+CONF.register_opts(serialization_opts, 'serialization')
 LOG = logging.getLogger(__name__)
 LOG = logging.getLogger(__name__)
 
 
 
 
@@ -24,3 +34,21 @@ def get_connection_info(ctxt, data):
         LOG.info("Retrieving connection info from secret: %s", secret_ref)
         LOG.info("Retrieving connection info from secret: %s", secret_ref)
         connection_info = secrets.get_secret(ctxt, secret_ref)
         connection_info = secrets.get_secret(ctxt, secret_ref)
     return connection_info
     return connection_info
+
+
+def marshal_migr_conn_info(migr_connection_info):
+    if migr_connection_info and "pkey" in migr_connection_info:
+        migr_connection_info = migr_connection_info.copy()
+        migr_connection_info["pkey"] = utils.serialize_key(
+            migr_connection_info["pkey"],
+            CONF.serialization.temp_keypair_password)
+    return migr_connection_info
+
+
+def unmarshal_migr_conn_info(migr_connection_info):
+    if migr_connection_info and "pkey" in migr_connection_info:
+        migr_connection_info = migr_connection_info.copy()
+        pkey_str = migr_connection_info["pkey"]
+        migr_connection_info["pkey"] = utils.deserialize_key(
+            pkey_str, CONF.serialization.temp_keypair_password)
+    return migr_connection_info

+ 11 - 0
coriolis/tasks/factory.py

@@ -4,6 +4,7 @@
 from coriolis import constants
 from coriolis import constants
 from coriolis import exception
 from coriolis import exception
 from coriolis.tasks import migration_tasks
 from coriolis.tasks import migration_tasks
+from coriolis.tasks import osmorphing_tasks
 from coriolis.tasks import replica_tasks
 from coriolis.tasks import replica_tasks
 
 
 _TASKS_MAP = {
 _TASKS_MAP = {
@@ -11,6 +12,12 @@ _TASKS_MAP = {
         migration_tasks.ExportInstanceTask,
         migration_tasks.ExportInstanceTask,
     constants.TASK_TYPE_IMPORT_INSTANCE:
     constants.TASK_TYPE_IMPORT_INSTANCE:
         migration_tasks.ImportInstanceTask,
         migration_tasks.ImportInstanceTask,
+    constants.TASK_TYPE_FINALIZE_IMPORT_INSTANCE:
+        migration_tasks.FinalizeImportInstanceTask,
+    constants.TASK_TYPE_CLEANUP_FAILED_IMPORT_INSTANCE:
+        migration_tasks.CleanupFailedImportInstanceTask,
+    constants.TASK_TYPE_OS_MORPHING:
+        osmorphing_tasks.OSMorphingTask,
     constants.TASK_TYPE_GET_INSTANCE_INFO:
     constants.TASK_TYPE_GET_INSTANCE_INFO:
         replica_tasks.GetInstanceInfoTask,
         replica_tasks.GetInstanceInfoTask,
     constants.TASK_TYPE_REPLICATE_DISKS:
     constants.TASK_TYPE_REPLICATE_DISKS:
@@ -31,6 +38,10 @@ _TASKS_MAP = {
         replica_tasks.DeleteReplicaSourceResourcesTask,
         replica_tasks.DeleteReplicaSourceResourcesTask,
     constants.TASK_TYPE_DEPLOY_REPLICA_INSTANCE:
     constants.TASK_TYPE_DEPLOY_REPLICA_INSTANCE:
         replica_tasks.DeployReplicaInstanceTask,
         replica_tasks.DeployReplicaInstanceTask,
+    constants.TASK_TYPE_FINALIZE_REPLICA_INSTANCE_DEPLOYMENT:
+        replica_tasks.FinalizeReplicaInstanceDeploymentTask,
+    constants.TASK_TYPE_CLEANUP_FAILED_REPLICA_INSTANCE_DEPLOYMENT:
+        replica_tasks.CleanupFailedReplicaInstanceDeploymentTask,
     constants.TASK_TYPE_CREATE_REPLICA_DISK_SNAPSHOTS:
     constants.TASK_TYPE_CREATE_REPLICA_DISK_SNAPSHOTS:
         replica_tasks.CreateReplicaDiskSnapshotsTask,
         replica_tasks.CreateReplicaDiskSnapshotsTask,
     constants.TASK_TYPE_DELETE_REPLICA_DISK_SNAPSHOTS:
     constants.TASK_TYPE_DELETE_REPLICA_DISK_SNAPSHOTS:

+ 39 - 1
coriolis/tasks/migration_tasks.py

@@ -41,7 +41,45 @@ class ImportInstanceTask(base.TaskRunner):
             destination["type"], constants.PROVIDER_TYPE_IMPORT, event_handler)
             destination["type"], constants.PROVIDER_TYPE_IMPORT, event_handler)
         connection_info = base.get_connection_info(ctxt, destination)
         connection_info = base.get_connection_info(ctxt, destination)
 
 
-        provider.import_instance(
+        import_info = provider.import_instance(
             ctxt, connection_info, target_environment, instance, export_info)
             ctxt, connection_info, target_environment, instance, export_info)
 
 
+        task_info["instance_deployment_info"] = import_info[
+            "instance_deployment_info"]
+        task_info["osmorphing_info"] = import_info.get("osmorphing_info", {})
+        task_info["osmorphing_connection_info"] = base.marshal_migr_conn_info(
+            import_info["osmorphing_connection_info"])
+
+        task_info["origin_provider_type"] = constants.PROVIDER_TYPE_EXPORT
+        task_info["destination_provider_type"] = constants.PROVIDER_TYPE_IMPORT
+
+        return task_info
+
+
+class FinalizeImportInstanceTask(base.TaskRunner):
+    def run(self, ctxt, instance, origin, destination, task_info,
+            event_handler):
+        provider = providers_factory.get_provider(
+            destination["type"], constants.PROVIDER_TYPE_IMPORT, event_handler)
+        connection_info = base.get_connection_info(ctxt, destination)
+        instance_deployment_info = task_info["instance_deployment_info"]
+
+        provider.finalize_import_instance(
+            ctxt, connection_info, instance_deployment_info)
+
+        return task_info
+
+
+class CleanupFailedImportInstanceTask(base.TaskRunner):
+    def run(self, ctxt, instance, origin, destination, task_info,
+            event_handler):
+        provider = providers_factory.get_provider(
+            destination["type"], constants.PROVIDER_TYPE_IMPORT, event_handler)
+        connection_info = base.get_connection_info(ctxt, destination)
+        instance_deployment_info = task_info.get(
+            "instance_deployment_info", {})
+
+        provider.cleanup_failed_import_instance(
+            ctxt, connection_info, instance_deployment_info)
+
         return task_info
         return task_info

+ 33 - 0
coriolis/tasks/osmorphing_tasks.py

@@ -0,0 +1,33 @@
+# Copyright 2016 Cloudbase Solutions Srl
+# All Rights Reserved.
+
+from coriolis.osmorphing import manager as osmorphing_manager
+from coriolis.providers import factory as providers_factory
+from coriolis.tasks import base
+
+
+class OSMorphingTask(base.TaskRunner):
+    def run(self, ctxt, instance, origin, destination, task_info,
+            event_handler):
+
+        origin_provider_type = task_info["origin_provider_type"]
+        destination_provider_type = task_info["destination_provider_type"]
+
+        origin_provider = providers_factory.get_provider(
+            origin["type"], origin_provider_type, event_handler)
+
+        destination_provider = providers_factory.get_provider(
+            destination["type"], destination_provider_type, event_handler)
+
+        osmorphing_connection_info = base.unmarshal_migr_conn_info(
+            task_info['osmorphing_connection_info'])
+        osmorphing_info = task_info.get('osmorphing_info', {})
+
+        osmorphing_manager.morph_image(
+            origin_provider,
+            destination_provider,
+            osmorphing_connection_info,
+            osmorphing_info,
+            event_handler)
+
+        return task_info

+ 48 - 34
coriolis/tasks/replica_tasks.py

@@ -6,41 +6,12 @@ from coriolis import exception
 from coriolis.providers import factory as providers_factory
 from coriolis.providers import factory as providers_factory
 from coriolis import schemas
 from coriolis import schemas
 from coriolis.tasks import base
 from coriolis.tasks import base
-from coriolis import utils
 
 
-from oslo_config import cfg
 from oslo_log import log as logging
 from oslo_log import log as logging
 
 
-serialization_opts = [
-    cfg.StrOpt('temp_keypair_password',
-               default=None,
-               help='Password to be used when serializing temporary keys'),
-]
-
-CONF = cfg.CONF
-CONF.register_opts(serialization_opts, 'serialization')
-
 LOG = logging.getLogger(__name__)
 LOG = logging.getLogger(__name__)
 
 
 
 
-def _marshal_migr_conn_info(migr_connection_info):
-    if migr_connection_info and "pkey" in migr_connection_info:
-        migr_connection_info = migr_connection_info.copy()
-        migr_connection_info["pkey"] = utils.serialize_key(
-            migr_connection_info["pkey"],
-            CONF.serialization.temp_keypair_password)
-    return migr_connection_info
-
-
-def _unmarshal_migr_conn_info(migr_connection_info):
-    if migr_connection_info and "pkey" in migr_connection_info:
-        migr_connection_info = migr_connection_info.copy()
-        pkey_str = migr_connection_info["pkey"]
-        migr_connection_info["pkey"] = utils.deserialize_key(
-            pkey_str, CONF.serialization.temp_keypair_password)
-    return migr_connection_info
-
-
 def _get_volumes_info(task_info):
 def _get_volumes_info(task_info):
     volumes_info = task_info.get("volumes_info")
     volumes_info = task_info.get("volumes_info")
     if not volumes_info:
     if not volumes_info:
@@ -91,10 +62,10 @@ class ReplicateDisksTask(base.TaskRunner):
 
 
         volumes_info = _get_volumes_info(task_info)
         volumes_info = _get_volumes_info(task_info)
 
 
-        migr_source_conn_info = _unmarshal_migr_conn_info(
+        migr_source_conn_info = base.unmarshal_migr_conn_info(
             task_info["migr_source_connection_info"])
             task_info["migr_source_connection_info"])
 
 
-        migr_target_conn_info = _unmarshal_migr_conn_info(
+        migr_target_conn_info = base.unmarshal_migr_conn_info(
             task_info["migr_target_connection_info"])
             task_info["migr_target_connection_info"])
 
 
         incremental = task_info.get("incremental", True)
         incremental = task_info.get("incremental", True)
@@ -161,7 +132,7 @@ class DeployReplicaSourceResourcesTask(base.TaskRunner):
 
 
         task_info["migr_source_resources"] = replica_resources_info[
         task_info["migr_source_resources"] = replica_resources_info[
             "migr_resources"]
             "migr_resources"]
-        migr_connection_info = _marshal_migr_conn_info(
+        migr_connection_info = base.marshal_migr_conn_info(
             replica_resources_info["connection_info"])
             replica_resources_info["connection_info"])
         task_info["migr_source_connection_info"] = migr_connection_info
         task_info["migr_source_connection_info"] = migr_connection_info
 
 
@@ -207,7 +178,7 @@ class DeployReplicaTargetResourcesTask(base.TaskRunner):
         task_info["migr_target_resources"] = replica_resources_info[
         task_info["migr_target_resources"] = replica_resources_info[
             "migr_resources"]
             "migr_resources"]
 
 
-        migr_connection_info = _marshal_migr_conn_info(
+        migr_connection_info = base.marshal_migr_conn_info(
             replica_resources_info["connection_info"])
             replica_resources_info["connection_info"])
         task_info["migr_target_connection_info"] = migr_connection_info
         task_info["migr_target_connection_info"] = migr_connection_info
 
 
@@ -249,10 +220,53 @@ class DeployReplicaInstanceTask(base.TaskRunner):
         clone_disks = task_info.get("clone_disks", True)
         clone_disks = task_info.get("clone_disks", True)
         LOG.debug("Clone disks: %s", clone_disks)
         LOG.debug("Clone disks: %s", clone_disks)
 
 
-        provider.deploy_replica_instance(
+        import_info = provider.deploy_replica_instance(
             ctxt, connection_info, target_environment, instance,
             ctxt, connection_info, target_environment, instance,
             export_info, volumes_info, clone_disks)
             export_info, volumes_info, clone_disks)
 
 
+        task_info["instance_deployment_info"] = import_info[
+            "instance_deployment_info"]
+        task_info["osmorphing_info"] = import_info.get("osmorphing_info", {})
+        task_info["osmorphing_connection_info"] = base.marshal_migr_conn_info(
+            import_info["osmorphing_connection_info"])
+
+        task_info[
+            "origin_provider_type"] = constants.PROVIDER_TYPE_REPLICA_EXPORT
+        task_info[
+            "destination_provider_type"
+        ] = constants.PROVIDER_TYPE_REPLICA_IMPORT
+
+        return task_info
+
+
+class FinalizeReplicaInstanceDeploymentTask(base.TaskRunner):
+    def run(self, ctxt, instance, origin, destination, task_info,
+            event_handler):
+        provider = providers_factory.get_provider(
+            destination["type"], constants.PROVIDER_TYPE_REPLICA_IMPORT,
+            event_handler)
+        connection_info = base.get_connection_info(ctxt, destination)
+        instance_deployment_info = task_info["instance_deployment_info"]
+
+        provider.finalize_replica_instance_deployment(
+            ctxt, connection_info, instance_deployment_info)
+
+        return task_info
+
+
+class CleanupFailedReplicaInstanceDeploymentTask(base.TaskRunner):
+    def run(self, ctxt, instance, origin, destination, task_info,
+            event_handler):
+        provider = providers_factory.get_provider(
+            destination["type"], constants.PROVIDER_TYPE_REPLICA_IMPORT,
+            event_handler)
+        connection_info = base.get_connection_info(ctxt, destination)
+        instance_deployment_info = task_info.get(
+            "instance_deployment_info", {})
+
+        provider.cleanup_failed_replica_instance_deployment(
+            ctxt, connection_info, instance_deployment_info)
+
         return task_info
         return task_info
 
 
 
 

+ 0 - 0
coriolis/tests/providers/azure/__init__.py


+ 0 - 427
coriolis/tests/providers/azure/test_azure_import.py

@@ -1,427 +0,0 @@
-import os
-import tempfile
-
-import mock
-from oslo_config import cfg
-from oslo_utils import units
-from azure.mgmt import compute, network
-
-from coriolis import constants
-from coriolis.providers import azure
-from coriolis.providers.azure import exceptions
-from coriolis.tests import testutils
-from coriolis.tests.providers import base
-
-
-class AzureImportProviderUnitTestsCase(base.ImportProviderTestCase):
-
-    _platform = constants.PLATFORM_AZURE_RM
-    _hypervisor = constants.HYPERVISOR_HYPERV
-
-    def setUp(self):
-        super(AzureImportProviderUnitTestsCase, self).setUp()
-
-        self._patch_utils_retry()
-
-        self._provider = azure.ImportProvider(self._mock_event_manager)
-
-        event_manager_patcher = mock.patch.object(
-            self._provider, '_event_manager',
-            new=self._mock_event_manager)
-        event_manager_patcher.start()
-
-        self._test_location = mock.sentinel.test_location
-        self._test_migration_id = mock.sentinel.test_migration_id
-        self._test_container_name = mock.sentinel.test_container
-        self._test_storage_name = mock.sentinel.test_storage_account
-        self._test_resource_group = mock.sentinel.test_resource_group
-        self._test_subnet_name = mock.sentinel.test_subnet_name
-
-        # various mocks for various helpers in azure.utils.
-        # to patch them '_patch_azure_utils' should be called.
-        self._test_random_password = mock.sentinel.test_azutils_random_password
-        self._mock_azutils_randpass = mock.MagicMock(
-            return_value=self._test_random_password)
-
-        self._test_unique_id = mock.sentinel.test_azutils_unique_id
-        self._mock_azutils_uniqueid = mock.MagicMock(
-            return_value=self._test_unique_id)
-
-        self._test_normalized_location = mock.sentinel.test_normalized_location
-        self._mock_azutils_normalize_location = mock.MagicMock(
-            return_value=self._test_normalized_location)
-
-        self._patch_azure_decorators()
-        self._setup_config_mock()
-
-    def _setup_config_mock(self):
-        azure_provider_conf = mock.MagicMock(
-            migr_container_name=self._test_container_name,
-            migr_subnet_name=self._test_subnet_name
-        )
-
-        conf_patcher = mock.patch.object(
-            cfg.CONF, 'azure_migration_provider', new=azure_provider_conf)
-        conf_patcher.start()
-
-    def _patch_azure_utils(self):
-        azutils_patcher = mock.patch.multiple(
-            'coriolis.providers.azure.azutils',
-            get_random_password=self._mock_azutils_randpass,
-            get_unique_id=self._mock_azutils_uniqueid,
-            normalize_location=self._mock_azutils_normalize_location
-        )
-        azutils_patcher.start()
-
-    def _patch_azure_decorators(self):
-        # NOTE: we patch the standard decorators found in azutils, whose core
-        # functionality lies in injecting the 'raw=True' kwarg to all ARM API
-        # calls which forces more checkable output/waitable long running
-        # operations (checked and awaited, respectively).
-        # Considering it's ideally a background thing; no tests focus on
-        # checking for that kwarg (except the tests for the decorators
-        # themselves...).
-        self._mock_checked = testutils.make_identity_decorator_mock()
-
-        self._mock_awaited_inner = testutils.make_identity_decorator_mock()
-        def awaited_dec(*args, **kwargs):
-            return self._mock_awaited_inner
-        self._mock_awaited = mock.MagicMock(side_effect=awaited_dec)
-
-        decs_patcher = mock.patch.multiple(
-            'coriolis.providers.azure.azutils',
-            checked=self._mock_checked,
-            awaited=self._mock_awaited
-        )
-        decs_patcher.start()
-
-    @mock.patch.object(azure.ImportProvider, '_get_block_blob_client')
-    def test_delete_recovery_disk(self, mock_get_blobc):
-        interesting_blob_name = ("%s.veryinteresting.status" %
-                                 self._test_instance_name)
-        blob_names = [
-            "uninteresting", interesting_blob_name, "boring"
-        ]
-        # NOTE: small workaround mocks already having a 'name' attribute:
-        mock_blobs = []
-        for name in blob_names:
-            m = mock.MagicMock()
-            m.name = name
-            mock_blobs.append(m)
-
-        mock_blob_client = mock.MagicMock()
-        mock_blob_client.list_blobs.return_value = mock_blobs
-
-        mock_get_blobc.return_value = mock_blob_client
-
-        self._provider._delete_recovery_disk(
-            self._test_target_env, self._test_instance_name)
-
-        mock_get_blobc.assert_called_once_with(self._test_target_env)
-
-        mock_blob_client.list_blobs.assert_called_once_with(
-            self._test_container_name)
-        mock_blob_client.delete_blob.assert_called_once_with(
-            self._test_container_name, interesting_blob_name)
-
-    @mock.patch.object(azure.ImportProvider, '_get_page_blob_client')
-    def test_upload_disk(self, mock_get_pagec):
-        test_env = {"storage": {"account": self._test_storage_name}}
-        test_disk_path = mock.sentinel.test_disk_path
-        test_upload_name = mock.sentinel.test_upload_name
-
-        mock_page_client = mock.MagicMock()
-        mock_get_pagec.return_value = mock_page_client
-
-        res = self._provider._upload_disk(
-            test_env, test_disk_path, test_upload_name)
-
-        disk_uri = azure.BLOB_PATH_FORMAT % (
-            self._test_storage_name,
-            self._test_container_name,
-            test_upload_name
-        )
-
-        mock_get_pagec.assert_called_once_with(test_env)
-        mock_page_client.create_blob_from_path.assert_called_once_with(
-            self._test_container_name, test_upload_name,
-            test_disk_path, progress_callback=mock.ANY)
-
-        self.assertEqual(res.name, test_upload_name)
-        self.assertEqual(res.uri, disk_uri)
-
-    @mock.patch.object(azure.ImportProvider, '_get_network_client')
-    def test_create_migration_network(self, mock_get_netc):
-        target_env = {"location": self._test_location}
-
-        # NOTE: this definition should remain rigid throughout as it has no
-        # real reason of being migration-specific.
-        test_vnet = network.models.VirtualNetwork(
-            location=self._test_location,
-            address_space=network.models.AddressSpace(
-                address_prefixes=["10.0.0.0/16"]
-            ),
-            subnets=[
-                network.models.Subnet(
-                    name=self._test_subnet_name,
-                    address_prefix='10.0.0.0/24'
-                )
-            ]
-        )
-
-        resgroup_name = azure.MIGRATION_RESGROUP_NAME_FORMAT % (
-            self._test_migration_id)
-        vn_name = azure.MIGRATION_NETWORK_NAME_FORMAT % (
-            self._test_migration_id)
-
-        mock_vnetc = mock.MagicMock()
-        mock_vnetc.create_or_update.return_value = test_vnet
-        mock_vnetc.get.return_value = test_vnet
-
-        mock_netc = mock.MagicMock()
-        mock_netc.virtual_networks = mock_vnetc
-
-        mock_get_netc.return_value = mock_netc
-
-        res = self._provider._create_migration_network(
-            self._test_conn_info, target_env, self._test_migration_id)
-
-        self.assertEqual(res, test_vnet)
-
-        mock_get_netc.assert_called_once_with(self._test_conn_info)
-
-        mock_vnetc.create_or_update.assert_called_once_with(
-            resgroup_name, vn_name, test_vnet)
-
-        mock_vnetc.get.assert_called_once_with(resgroup_name, vn_name)
-        self._mock_awaited_inner.assert_called_once_with(
-            mock_vnetc.create_or_update)
-        self._mock_checked.assert_called_once_with(mock_vnetc.get)
-
-    @mock.patch.object(azure.ImportProvider, '_get_compute_client')
-    def test_wait_for_vm_success(self, mock_get_computec):
-        vm_states = [
-            mock.Mock(provisioning_state=s) for s in
-            ["Creating", "Creating", "Updating", "Creating", "Succeeded"]
-        ]
-
-        mock_vmclient = mock.MagicMock()
-        mock_vmclient.get.side_effect = vm_states
-
-        mock_get_computec.return_value = mock.MagicMock(
-            virtual_machines=mock_vmclient)
-
-        self._provider._wait_for_vm(
-            self._test_conn_info,
-            self._test_resource_group,
-            self._test_instance_name,
-            period=0
-        )
-
-        mock_get_computec.assert_called_once_with(self._test_conn_info)
-
-        mock_vmclient.get.assert_has_calls([
-            mock.call(self._test_resource_group, self._test_instance_name)
-            for _ in vm_states
-        ])
-        self._mock_checked.assert_has_calls([
-            mock.call(mock_vmclient.get) for _ in vm_states
-        ])
-
-    @mock.patch.object(azure.ImportProvider, '_get_linux_worker_osprofile')
-    @mock.patch.object(azure.ImportProvider, '_get_windows_worker_osprofile')
-    def test_get_worker_osprofile(self, mock_get_windows_osprofile,
-                                  mock_get_linux_osprofile):
-        windows = constants.OS_TYPE_WINDOWS
-        linux = constants.OS_TYPE_LINUX
-        random_export_info = {"os_type": "some random OS"}
-
-        worker_name = mock.sentinel.worker_name
-        windows_profile = mock.sentinel.windows_osprofile
-        linux_profile = mock.sentinel.linux_osprofile
-
-        mock_get_windows_osprofile.return_value = windows_profile
-        mock_get_linux_osprofile.return_value = linux_profile
-
-        self._provider._get_worker_osprofile(
-            windows, self._test_location, worker_name)
-
-        mock_get_windows_osprofile.assert_called_once_with(
-            self._test_location, worker_name)
-        mock_get_linux_osprofile.assert_not_called()
-
-        mock_get_windows_osprofile.reset_mock()
-        mock_get_linux_osprofile.reset_mock()
-
-        self._provider._get_worker_osprofile(
-            linux, self._test_location, worker_name)
-
-        mock_get_linux_osprofile.assert_called_once_with(worker_name)
-        mock_get_windows_osprofile.assert_not_called()
-
-        mock_get_windows_osprofile.reset_mock()
-        mock_get_linux_osprofile.reset_mock()
-
-        self.assertRaises(
-            exceptions.FatalAzureOperationException,
-            self._provider._get_worker_osprofile,
-            random_export_info, self._test_location, worker_name)
-
-        mock_get_linux_osprofile.assert_not_called()
-        mock_get_windows_osprofile.assert_not_called()
-
-    @mock.patch.object(azure.ImportProvider, '_get_network_client')
-    def test_create_nic(self, mock_get_netc):
-
-        test_nic_name = mock.sentinel.test_nic_name
-        test_ip_configs = mock.sentinel.test_ip_configs
-
-        test_nic = network.models.NetworkInterface(
-            location=self._test_location,
-            ip_configurations=test_ip_configs
-        )
-
-        mock_nicc = mock.MagicMock()
-        mock_nicc.create_or_update.return_value = test_nic
-        mock_nicc.get.return_value = test_nic
-
-        mock_get_netc.return_value = mock.MagicMock()
-        mock_get_netc.return_value.network_interfaces = mock_nicc
-
-        res = self._provider._create_nic(
-            self._test_conn_info, self._test_resource_group, test_nic_name,
-            self._test_location, test_ip_configs)
-
-        mock_get_netc.assert_called_once_with(self._test_conn_info)
-
-        self._mock_awaited_inner.assert_called_once_with(
-            mock_nicc.create_or_update)
-        mock_nicc.create_or_update.assert_called_once_with(
-            self._test_resource_group, test_nic_name, test_nic)
-
-        self._mock_checked.assert_called_once_with(mock_nicc.get)
-        mock_nicc.get.assert_called_once_with(
-            self._test_resource_group, test_nic_name)
-
-        self.assertEqual(res, test_nic)
-
-    @mock.patch.object(azure.ImportProvider, '_get_network_client')
-    def test_create_public_ip(self, mock_get_netc):
-        test_ip_name = mock.sentinel.test_pulic_ip_name
-
-        test_pip = network.models.PublicIPAddress(
-            location=self._test_location,
-            public_ip_allocation_method=
-            network.models.IPAllocationMethod.dynamic
-        )
-
-        mock_pipc = mock.MagicMock()
-        mock_pipc.create_or_update.return_value = test_pip
-        mock_pipc.get.return_value = test_pip
-
-        mock_get_netc.return_value = mock.MagicMock()
-        mock_get_netc.return_value.public_ip_addresses = mock_pipc
-
-        res = self._provider._create_public_ip(
-            self._test_conn_info, self._test_resource_group,
-            test_ip_name, self._test_location)
-
-        mock_get_netc.assert_called_once_with(self._test_conn_info)
-
-        self._mock_awaited_inner.assert_called_once_with(
-            mock_pipc.create_or_update)
-        mock_pipc.create_or_update.assert_called_once_with(
-            self._test_resource_group, test_ip_name, test_pip)
-
-        self._mock_checked.assert_called_once_with(mock_pipc.get)
-        mock_pipc.get.assert_called_once_with(
-            self._test_resource_group, test_ip_name)
-
-        self.assertEqual(res, test_pip)
-
-    @mock.patch.object(os, 'remove')
-    @mock.patch.object(os.path, 'splitext')
-    def test_convert_to_vhd(self, mock_splitext, mock_osremove):
-        self._patch_utils_disk_functions()
-        self._patch_azure_utils()
-
-        test_disk_path = mock.sentinel.test_disk_path
-
-        test_newpath = mock.sentinel.test_new_diskpath
-        mock_splitext.return_value = ["testdiskname"]
-
-        test_newpath = "%s.%s" % (
-            mock_splitext.return_value[0],
-            constants.DISK_FORMAT_VHD
-        )
-
-        res = self._provider._convert_to_vhd(test_disk_path)
-
-        self._mock_utils_convert_disk.assert_called_once_with(
-            test_disk_path, test_newpath, constants.DISK_FORMAT_VHD,
-            preallocated=True)
-
-        self.assertEqual(res, test_newpath)
-
-    @mock.patch.object(os, 'remove')
-    @mock.patch.object(azure.ImportProvider, '_upload_disk')
-    @mock.patch.object(azure.ImportProvider, '_convert_to_vhd')
-    def _test_migrate_disk(self, mock_convert, mock_upload_disk, mock_osremove,
-            test_disk_format=None):
-        self._patch_utils_disk_functions()
-
-        test_disk_info = {
-            "virtual-size": 1 * units.Gi,
-            "format": test_disk_format
-        }
-        self._mock_utils_get_disk_info.return_value = test_disk_info
-
-        test_lun = mock.sentinel.test_lun
-        test_upload_name = mock.sentinel.test_upload_name
-        test_disk_path = mock.sentinel.test_disk_path
-
-        osremove_calls = [mock.call(test_disk_path)] # should get deleted anyhow...
-
-        test_blob_uri = mock.sentinel.test_blob_uri
-        test_blob_name = mock.sentinel.test_blob_name
-        test_blob = azure.AzureStorageBlob(
-            name=test_blob_name,
-            uri=test_blob_uri
-        )
-        mock_upload_disk.return_value = test_blob
-
-        expected_upload_path = test_disk_path
-        expected_data_disk = compute.models.DataDisk(
-            lun=test_lun,
-            disk_size_gb=2,
-            name=test_blob_name,
-            caching=compute.models.CachingTypes.none,
-            vhd=compute.models.VirtualHardDisk(uri=test_blob_uri),
-            create_option=compute.models.DiskCreateOptionTypes.attach
-        )
-
-        test_upload_path = mock.sentinel.test_upload_path
-        mock_convert.return_value = test_upload_path
-
-        res = self._provider._migrate_disk(
-            self._test_target_env, test_lun, test_disk_path, test_upload_name)
-
-        self._mock_utils_get_disk_info.assert_called_once_with(test_disk_path)
-
-        if test_disk_format != constants.DISK_FORMAT_VHD:
-            mock_convert.assert_called_once_with(test_disk_path)
-            osremove_calls.append(mock.call(test_upload_path))
-            expected_upload_path = test_upload_path
-
-        mock_upload_disk.assert_called_once_with(
-            self._test_target_env, expected_upload_path, test_upload_name)
-
-        mock_osremove.assert_has_calls(osremove_calls)
-
-        self.assertEqual(res, expected_data_disk)
-
-    def test_migrate_disk(self):
-        self._test_migrate_disk(test_disk_format=constants.DISK_FORMAT_VHD)
-
-    def test_migrate_disk_with_conversion(self):
-        self._test_migrate_disk(test_disk_format="somerandomformat")

+ 3 - 1
coriolis/tests/providers/base.py

@@ -24,6 +24,7 @@ class ProvidersBaseTestCase(test_base.CoriolisBaseTestCase):
         # NOTE: declare utils.retry_on_error mock; '_patch_utils_retry' should
         # NOTE: declare utils.retry_on_error mock; '_patch_utils_retry' should
         # be called to enable them.
         # be called to enable them.
         self._mock_utils_retry_inner = testutils.make_identity_decorator_mock()
         self._mock_utils_retry_inner = testutils.make_identity_decorator_mock()
+
         def retry_dec(*args, **kwargs):
         def retry_dec(*args, **kwargs):
             return self._mock_utils_retry_inner
             return self._mock_utils_retry_inner
         self._mock_utils_retry = mock.MagicMock(side_effect=retry_dec)
         self._mock_utils_retry = mock.MagicMock(side_effect=retry_dec)
@@ -64,7 +65,8 @@ class ImportProviderTestCase(ProvidersBaseTestCase):
         self._test_target_env = mock.sentinel.target_environment
         self._test_target_env = mock.sentinel.target_environment
         self._test_export_info = mock.sentinel.export_info
         self._test_export_info = mock.sentinel.export_info
 
 
-    def _test_morphing_called(self, os_type="", nics_info=None, ignore_devs=[]):
+    def _test_morphing_called(self, os_type="", nics_info=None,
+                              ignore_devs=[]):
         self._mock_morph.morph_image.assert_called_once_with(
         self._mock_morph.morph_image.assert_called_once_with(
             self._mock_conn_info, os_type,
             self._mock_conn_info, os_type,
             self._hypervisor, self._platform,
             self._hypervisor, self._platform,

+ 1 - 0
coriolis/tests/testutils.py

@@ -7,6 +7,7 @@ def identity_dec(item, *args, **kwargs):
     """ A decorator which adds nothing to the decorated item. """
     """ A decorator which adds nothing to the decorated item. """
     return item
     return item
 
 
+
 def make_identity_decorator_mock():
 def make_identity_decorator_mock():
     """ Returns a MagicMock with identity_dec as a side-effect. """
     """ Returns a MagicMock with identity_dec as a side-effect. """
     return mock.MagicMock(side_effect=identity_dec)
     return mock.MagicMock(side_effect=identity_dec)

+ 0 - 12
requirements.txt

@@ -19,23 +19,11 @@ paste
 pbr
 pbr
 psutil
 psutil
 pyOpenSSL
 pyOpenSSL
-python-cinderclient
-python-glanceclient>=2.0.0
 python-keystoneclient
 python-keystoneclient
-python-neutronclient
-python-novaclient
 python-barbicanclient
 python-barbicanclient
 python-swiftclient>=3.2
 python-swiftclient>=3.2
-pyVmomi
 pywinrm
 pywinrm
 PyYAML
 PyYAML
 requests
 requests
 sqlalchemy
 sqlalchemy
 webob
 webob
-msrest==0.4.0
-msrestazure==0.4.1
-azure-mgmt-compute==0.30.0rc5
-azure-mgmt-network==0.30.0rc5
-azure-mgmt-resource==0.30.0rc5
-azure-mgmt-storage==0.30.0rc5
-azure-storage==0.32.0