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

Add `source_environment` column to `base_transfer_action`.

Arin Toaca 7 лет назад
Родитель
Сommit
757489bf34

+ 11 - 12
coriolis/api/v1/migrations.py

@@ -54,6 +54,10 @@ class MigrationController(api_wsgi.Controller):
             notes = migration.get("notes")
             notes = migration.get("notes")
             skip_os_morphing = migration.get("skip_os_morphing", False)
             skip_os_morphing = migration.get("skip_os_morphing", False)
 
 
+            source_environment = migration.get("source_environment", {})
+            self._endpoints_api.validate_source_environment(
+                context, origin_endpoint_id, source_environment)
+
             network_map = migration.get("network_map", {})
             network_map = migration.get("network_map", {})
             api_utils.validate_network_map(network_map)
             api_utils.validate_network_map(network_map)
             destination_environment['network_map'] = network_map
             destination_environment['network_map'] = network_map
@@ -62,13 +66,8 @@ class MigrationController(api_wsgi.Controller):
             # import provider before appending the 'storage_mappings' parameter
             # import provider before appending the 'storage_mappings' parameter
             # for plugins with strict property name checks which do not yet
             # for plugins with strict property name checks which do not yet
             # support storage mapping features:
             # support storage mapping features:
-            is_valid, message = (
-                self._endpoints_api.validate_target_environment(
-                    context, destination_endpoint_id, destination_environment))
-            if not is_valid:
-                raise exc.HTTPBadRequest(
-                    explanation="Invalid destination "
-                                "environment: %s" % message)
+            self._endpoints_api.validate_target_environment(
+                context, destination_endpoint_id, destination_environment)
 
 
             # TODO(aznashwan): until the provider plugin interface is updated
             # TODO(aznashwan): until the provider plugin interface is updated
             # to have separate 'network_map' and 'storage_mappings' fields,
             # to have separate 'network_map' and 'storage_mappings' fields,
@@ -78,8 +77,8 @@ class MigrationController(api_wsgi.Controller):
             destination_environment['storage_mappings'] = storage_mappings
             destination_environment['storage_mappings'] = storage_mappings
 
 
             return (origin_endpoint_id, destination_endpoint_id,
             return (origin_endpoint_id, destination_endpoint_id,
-                    destination_environment, instances, notes,
-                    skip_os_morphing, network_map, storage_mappings)
+                    source_environment, destination_environment, instances,
+                    notes, skip_os_morphing, network_map, storage_mappings)
         except Exception as ex:
         except Exception as ex:
             LOG.exception(ex)
             LOG.exception(ex)
             msg = getattr(ex, "message", str(ex))
             msg = getattr(ex, "message", str(ex))
@@ -104,17 +103,17 @@ class MigrationController(api_wsgi.Controller):
         else:
         else:
             (origin_endpoint_id,
             (origin_endpoint_id,
              destination_endpoint_id,
              destination_endpoint_id,
+             source_environment,
              destination_environment,
              destination_environment,
              instances,
              instances,
              notes,
              notes,
              skip_os_morphing, network_map,
              skip_os_morphing, network_map,
              storage_mappings) = self._validate_migration_input(
              storage_mappings) = self._validate_migration_input(
                 context, migration_body)
                 context, migration_body)
-
             migration = self._migration_api.migrate_instances(
             migration = self._migration_api.migrate_instances(
                 context, origin_endpoint_id, destination_endpoint_id,
                 context, origin_endpoint_id, destination_endpoint_id,
-                destination_environment, instances, network_map,
-                storage_mappings, notes, skip_os_morphing)
+                source_environment, destination_environment, instances,
+                network_map, storage_mappings, notes, skip_os_morphing)
 
 
         return migration_view.single(req, migration)
         return migration_view.single(req, migration)
 
 

+ 11 - 13
coriolis/api/v1/replicas.py

@@ -55,6 +55,10 @@ class ReplicaController(api_wsgi.Controller):
             instances = replica["instances"]
             instances = replica["instances"]
             notes = replica.get("notes")
             notes = replica.get("notes")
 
 
+            source_environment = replica.get("source_environment", {})
+            self._endpoints_api.validate_source_environment(
+                context, origin_endpoint_id, source_environment)
+
             network_map = replica.get("network_map", {})
             network_map = replica.get("network_map", {})
             api_utils.validate_network_map(network_map)
             api_utils.validate_network_map(network_map)
             destination_environment['network_map'] = network_map
             destination_environment['network_map'] = network_map
@@ -63,14 +67,8 @@ class ReplicaController(api_wsgi.Controller):
             # import provider before appending the 'storage_mappings' parameter
             # import provider before appending the 'storage_mappings' parameter
             # for plugins with strict property name checks which do not yet
             # for plugins with strict property name checks which do not yet
             # support storage mapping features:
             # support storage mapping features:
-            is_valid, message = (
-                self._endpoints_api.validate_target_environment(
-                    context, destination_endpoint_id,
-                    destination_environment))
-            if not is_valid:
-                raise exc.HTTPBadRequest(
-                    explanation="Invalid destination "
-                                "environment: %s" % message)
+            self._endpoints_api.validate_target_environment(
+                context, destination_endpoint_id, destination_environment)
 
 
             storage_mappings = replica.get("storage_mappings", {})
             storage_mappings = replica.get("storage_mappings", {})
             api_utils.validate_storage_mappings(storage_mappings)
             api_utils.validate_storage_mappings(storage_mappings)
@@ -81,8 +79,8 @@ class ReplicaController(api_wsgi.Controller):
             destination_environment['storage_mappings'] = storage_mappings
             destination_environment['storage_mappings'] = storage_mappings
 
 
             return (origin_endpoint_id, destination_endpoint_id,
             return (origin_endpoint_id, destination_endpoint_id,
-                    destination_environment, instances, network_map,
-                    storage_mappings, notes)
+                    source_environment, destination_environment, instances,
+                    network_map, storage_mappings, notes)
         except Exception as ex:
         except Exception as ex:
             LOG.exception(ex)
             LOG.exception(ex)
             msg = getattr(ex, "message", str(ex))
             msg = getattr(ex, "message", str(ex))
@@ -93,13 +91,13 @@ class ReplicaController(api_wsgi.Controller):
         context.can(replica_policies.get_replicas_policy_label("create"))
         context.can(replica_policies.get_replicas_policy_label("create"))
 
 
         (origin_endpoint_id, destination_endpoint_id,
         (origin_endpoint_id, destination_endpoint_id,
-         destination_environment, instances, network_map,
+         source_environment, destination_environment, instances, network_map,
          storage_mappings, notes) = self._validate_create_body(context, body)
          storage_mappings, notes) = self._validate_create_body(context, body)
 
 
         return replica_view.single(req, self._replica_api.create(
         return replica_view.single(req, self._replica_api.create(
             context, origin_endpoint_id, destination_endpoint_id,
             context, origin_endpoint_id, destination_endpoint_id,
-            destination_environment, instances, network_map,
-            storage_mappings, notes))
+            source_environment, destination_environment, instances,
+            network_map, storage_mappings, notes))
 
 
     def delete(self, req, id):
     def delete(self, req, id):
         context = req.environ["coriolis.context"]
         context = req.environ["coriolis.context"]

+ 12 - 0
coriolis/api/v1/utils.py

@@ -1,6 +1,7 @@
 # Copyright 2018 Cloudbase Solutions Srl
 # Copyright 2018 Cloudbase Solutions Srl
 # All Rights Reserved.
 # All Rights Reserved.
 
 
+
 from oslo_log import log as logging
 from oslo_log import log as logging
 from webob import exc
 from webob import exc
 
 
@@ -29,3 +30,14 @@ def validate_storage_mappings(storage_mappings):
     except exception.SchemaValidationException as ex:
     except exception.SchemaValidationException as ex:
         raise exc.HTTPBadRequest(
         raise exc.HTTPBadRequest(
             explanation="Invalid storage_mappings: %s" % str(ex))
             explanation="Invalid storage_mappings: %s" % str(ex))
+
+
+def bad_request_on_error(error_message):
+    def _bad_request_on_error(func):
+        def wrapper(*args, **kwargs):
+            (is_valid, message) = func(*args, **kwargs)
+            if not is_valid:
+                raise exc.HTTPBadRequest(explanation=(error_message % message))
+            return (is_valid, message)
+        return wrapper
+    return _bad_request_on_error

+ 15 - 6
coriolis/conductor/rpc/client.py

@@ -94,6 +94,12 @@ class ConductorClient(object):
             ctxt, 'validate_endpoint_target_environment',
             ctxt, 'validate_endpoint_target_environment',
             endpoint_id=endpoint_id, target_env=target_env)
             endpoint_id=endpoint_id, target_env=target_env)
 
 
+    def validate_endpoint_source_environment(
+            self, ctxt, endpoint_id, source_env):
+        return self._client.call(
+            ctxt, 'validate_endpoint_source_environment',
+            endpoint_id=endpoint_id, source_env=source_env)
+
     def get_available_providers(self, ctxt):
     def get_available_providers(self, ctxt):
         return self._client.call(
         return self._client.call(
             ctxt, 'get_available_providers')
             ctxt, 'get_available_providers')
@@ -135,8 +141,8 @@ class ConductorClient(object):
 
 
     def create_instances_replica(self, ctxt, origin_endpoint_id,
     def create_instances_replica(self, ctxt, origin_endpoint_id,
                                  destination_endpoint_id,
                                  destination_endpoint_id,
-                                 destination_environment, instances,
-                                 network_map, storage_mappings,
+                                 source_environment, destination_environment,
+                                 instances, network_map, storage_mappings,
                                  notes=None):
                                  notes=None):
         return self._client.call(
         return self._client.call(
             ctxt, 'create_instances_replica',
             ctxt, 'create_instances_replica',
@@ -146,7 +152,8 @@ class ConductorClient(object):
             instances=instances,
             instances=instances,
             notes=notes,
             notes=notes,
             network_map=network_map,
             network_map=network_map,
-            storage_mappings=storage_mappings)
+            storage_mappings=storage_mappings,
+            source_environment=source_environment)
 
 
     def get_replicas(self, ctxt, include_tasks_executions=False):
     def get_replicas(self, ctxt, include_tasks_executions=False):
         return self._client.call(
         return self._client.call(
@@ -174,8 +181,9 @@ class ConductorClient(object):
             ctxt, 'get_migration', migration_id=migration_id)
             ctxt, 'get_migration', migration_id=migration_id)
 
 
     def migrate_instances(self, ctxt, origin_endpoint_id,
     def migrate_instances(self, ctxt, origin_endpoint_id,
-                          destination_endpoint_id, destination_environment,
-                          instances, network_map, storage_mappings, notes=None,
+                          destination_endpoint_id, source_environment,
+                          destination_environment, instances, network_map,
+                          storage_mappings, notes=None,
                           skip_os_morphing=False):
                           skip_os_morphing=False):
         return self._client.call(
         return self._client.call(
             ctxt, 'migrate_instances',
             ctxt, 'migrate_instances',
@@ -186,7 +194,8 @@ class ConductorClient(object):
             notes=notes,
             notes=notes,
             skip_os_morphing=skip_os_morphing,
             skip_os_morphing=skip_os_morphing,
             network_map=network_map,
             network_map=network_map,
-            storage_mappings=storage_mappings)
+            storage_mappings=storage_mappings,
+            source_environment=source_environment)
 
 
     def deploy_replica_instances(self, ctxt, replica_id, clone_disks=False,
     def deploy_replica_instances(self, ctxt, replica_id, clone_disks=False,
                                  force=False, skip_os_morphing=False):
                                  force=False, skip_os_morphing=False):

+ 16 - 5
coriolis/conductor/rpc/server.py

@@ -168,6 +168,12 @@ class ConductorServerEndpoint(object):
         return self._rpc_worker_client.validate_endpoint_target_environment(
         return self._rpc_worker_client.validate_endpoint_target_environment(
             ctxt, endpoint.type, target_env)
             ctxt, endpoint.type, target_env)
 
 
+    def validate_endpoint_source_environment(
+            self, ctxt, endpoint_id, source_env):
+        endpoint = self.get_endpoint(ctxt, endpoint_id)
+        return self._rpc_worker_client.validate_endpoint_source_environment(
+            ctxt, endpoint.type, source_env)
+
     def get_available_providers(self, ctxt):
     def get_available_providers(self, ctxt):
         return self._rpc_worker_client.get_available_providers(ctxt)
         return self._rpc_worker_client.get_available_providers(ctxt)
 
 
@@ -202,7 +208,8 @@ class ConductorServerEndpoint(object):
         endpoint = self.get_endpoint(ctxt, action.origin_endpoint_id)
         endpoint = self.get_endpoint(ctxt, action.origin_endpoint_id)
         return {
         return {
             "connection_info": endpoint.connection_info,
             "connection_info": endpoint.connection_info,
-            "type": endpoint.type
+            "type": endpoint.type,
+            "source_environment": action.source_environment
         }
         }
 
 
     def _get_task_destination(self, ctxt, action):
     def _get_task_destination(self, ctxt, action):
@@ -385,7 +392,7 @@ class ConductorServerEndpoint(object):
             raise exception.SameDestination()
             raise exception.SameDestination()
 
 
     def create_instances_replica(self, ctxt, origin_endpoint_id,
     def create_instances_replica(self, ctxt, origin_endpoint_id,
-                                 destination_endpoint_id,
+                                 destination_endpoint_id, source_environment,
                                  destination_environment, instances,
                                  destination_environment, instances,
                                  network_map, storage_mappings, notes=None):
                                  network_map, storage_mappings, notes=None):
         origin_endpoint = self.get_endpoint(ctxt, origin_endpoint_id)
         origin_endpoint = self.get_endpoint(ctxt, origin_endpoint_id)
@@ -397,6 +404,7 @@ class ConductorServerEndpoint(object):
         replica.origin_endpoint = origin_endpoint
         replica.origin_endpoint = origin_endpoint
         replica.destination_endpoint = destination_endpoint
         replica.destination_endpoint = destination_endpoint
         replica.destination_environment = destination_environment
         replica.destination_environment = destination_environment
+        replica.source_environment = source_environment
         replica.instances = instances
         replica.instances = instances
         replica.executions = []
         replica.executions = []
         replica.info = {}
         replica.info = {}
@@ -495,6 +503,7 @@ class ConductorServerEndpoint(object):
         migration.origin_endpoint_id = replica.origin_endpoint_id
         migration.origin_endpoint_id = replica.origin_endpoint_id
         migration.destination_endpoint_id = replica.destination_endpoint_id
         migration.destination_endpoint_id = replica.destination_endpoint_id
         migration.destination_environment = replica.destination_environment
         migration.destination_environment = replica.destination_environment
+        migration.source_environment = replica.source_environment
         migration.network_map = replica.network_map
         migration.network_map = replica.network_map
         migration.storage_mappings = replica.storage_mappings
         migration.storage_mappings = replica.storage_mappings
         migration.instances = instances
         migration.instances = instances
@@ -584,9 +593,10 @@ class ConductorServerEndpoint(object):
         return self.get_migration(ctxt, migration.id)
         return self.get_migration(ctxt, migration.id)
 
 
     def migrate_instances(self, ctxt, origin_endpoint_id,
     def migrate_instances(self, ctxt, origin_endpoint_id,
-                          destination_endpoint_id, destination_environment,
-                          instances, network_map, storage_mappings,
-                          skip_os_morphing=False, notes=None):
+                          destination_endpoint_id, source_environment,
+                          destination_environment, instances, network_map,
+                          storage_mappings, skip_os_morphing=False,
+                          notes=None):
         origin_endpoint = self.get_endpoint(ctxt, origin_endpoint_id)
         origin_endpoint = self.get_endpoint(ctxt, origin_endpoint_id)
         destination_endpoint = self.get_endpoint(ctxt, destination_endpoint_id)
         destination_endpoint = self.get_endpoint(ctxt, destination_endpoint_id)
         self._check_endpoints(ctxt, origin_endpoint, destination_endpoint)
         self._check_endpoints(ctxt, origin_endpoint, destination_endpoint)
@@ -599,6 +609,7 @@ class ConductorServerEndpoint(object):
         migration.origin_endpoint = origin_endpoint
         migration.origin_endpoint = origin_endpoint
         migration.destination_endpoint = destination_endpoint
         migration.destination_endpoint = destination_endpoint
         migration.destination_environment = destination_environment
         migration.destination_environment = destination_environment
+        migration.source_environment = source_environment
         migration.network_map = network_map
         migration.network_map = network_map
         migration.storage_mappings = storage_mappings
         migration.storage_mappings = storage_mappings
         execution = models.TasksExecution()
         execution = models.TasksExecution()

+ 17 - 0
coriolis/db/sqlalchemy/migrate_repo/versions/008_adds_source_environment.py

@@ -0,0 +1,17 @@
+# Copyright 2018 Cloudbase Solutions Srl
+# All Rights Reserved.
+
+import sqlalchemy
+
+
+def upgrade(migrate_engine):
+    meta = sqlalchemy.MetaData()
+    meta.bind = migrate_engine
+
+    # add 'source_environment' column to 'base_transfer_action':
+    base_transfer_action = sqlalchemy.Table(
+        'base_transfer_action', meta, autoload=True)
+
+    source_environment = sqlalchemy.Column(
+        "source_environment", sqlalchemy.Text, nullable=True)
+    base_transfer_action.create_column(source_environment)

+ 1 - 0
coriolis/db/sqlalchemy/models.py

@@ -114,6 +114,7 @@ class BaseTransferAction(BASE, models.TimestampMixin, models.ModelBase,
     transfer_result = sqlalchemy.Column(types.Json, nullable=True)
     transfer_result = sqlalchemy.Column(types.Json, nullable=True)
     network_map = sqlalchemy.Column(types.Json, nullable=True)
     network_map = sqlalchemy.Column(types.Json, nullable=True)
     storage_mappings = sqlalchemy.Column(types.Json, nullable=True)
     storage_mappings = sqlalchemy.Column(types.Json, nullable=True)
+    source_environment = sqlalchemy.Column(types.Json, nullable=True)
 
 
     __mapper_args__ = {
     __mapper_args__ = {
         'polymorphic_identity': 'base_transfer_action',
         'polymorphic_identity': 'base_transfer_action',

+ 7 - 0
coriolis/endpoints/api.py

@@ -2,6 +2,7 @@
 # All Rights Reserved.
 # All Rights Reserved.
 
 
 from coriolis.conductor.rpc import client as rpc_client
 from coriolis.conductor.rpc import client as rpc_client
+from coriolis.api.v1 import utils as api_utils
 
 
 
 
 class API(object):
 class API(object):
@@ -33,6 +34,12 @@ class API(object):
         return self._rpc_client.validate_endpoint_connection(
         return self._rpc_client.validate_endpoint_connection(
             ctxt, endpoint_id)
             ctxt, endpoint_id)
 
 
+    @api_utils.bad_request_on_error("Invalid destination environment: %s")
     def validate_target_environment(self, ctxt, endpoint_id, target_env):
     def validate_target_environment(self, ctxt, endpoint_id, target_env):
         return self._rpc_client.validate_endpoint_target_environment(
         return self._rpc_client.validate_endpoint_target_environment(
             ctxt, endpoint_id, target_env)
             ctxt, endpoint_id, target_env)
+
+    @api_utils.bad_request_on_error("Invalid source environment: %s")
+    def validate_source_environment(self, ctxt, endpoint_id, source_env):
+        return self._rpc_client.validate_endpoint_source_environment(
+            ctxt, endpoint_id, source_env)

+ 5 - 4
coriolis/migrations/api.py

@@ -9,13 +9,14 @@ class API(object):
         self._rpc_client = rpc_client.ConductorClient()
         self._rpc_client = rpc_client.ConductorClient()
 
 
     def migrate_instances(self, ctxt, origin_endpoint_id,
     def migrate_instances(self, ctxt, origin_endpoint_id,
-                          destination_endpoint_id, destination_environment,
-                          instances, network_map, storage_mappings, notes=None,
+                          destination_endpoint_id, source_environment,
+                          destination_environment, instances, network_map,
+                          storage_mappings, notes=None,
                           skip_os_morphing=False):
                           skip_os_morphing=False):
         return self._rpc_client.migrate_instances(
         return self._rpc_client.migrate_instances(
             ctxt, origin_endpoint_id, destination_endpoint_id,
             ctxt, origin_endpoint_id, destination_endpoint_id,
-            destination_environment, instances, network_map, storage_mappings,
-            notes, skip_os_morphing)
+            source_environment, destination_environment, instances,
+            network_map, storage_mappings, notes, skip_os_morphing)
 
 
     def deploy_replica_instances(self, ctxt, replica_id, clone_disks=False,
     def deploy_replica_instances(self, ctxt, replica_id, clone_disks=False,
                                  force=False, skip_os_morphing=False):
                                  force=False, skip_os_morphing=False):

+ 14 - 6
coriolis/providers/base.py

@@ -309,8 +309,8 @@ class BaseReplicaImportProvider(BaseImportInstanceProvider):
 class BaseExportProvider(BaseInstanceProvider):
 class BaseExportProvider(BaseInstanceProvider):
 
 
     @abc.abstractmethod
     @abc.abstractmethod
-    def export_instance(self, ctxt, connection_info, instance_name,
-                        export_path):
+    def export_instance(self, ctxt, connection_info, source_environment,
+                        instance_name, export_path):
         """Exports the given instance.
         """Exports the given instance.
 
 
          Exports the instance given by its name from the given source cloud
          Exports the instance given by its name from the given source cloud
@@ -318,19 +318,26 @@ class BaseExportProvider(BaseInstanceProvider):
         """
         """
         pass
         pass
 
 
+    @abc.abstractmethod
+    def get_source_environment_schema(self):
+        pass
+
 
 
 class BaseReplicaExportProvider(BaseInstanceProvider):
 class BaseReplicaExportProvider(BaseInstanceProvider):
 
 
     @abc.abstractmethod
     @abc.abstractmethod
-    def get_replica_instance_info(self, ctxt, connection_info, instance_name):
+    def get_replica_instance_info(self, ctxt, connection_info,
+                                  source_environment, instance_name):
         pass
         pass
 
 
     @abc.abstractmethod
     @abc.abstractmethod
-    def deploy_replica_source_resources(self, ctxt, connection_info):
+    def deploy_replica_source_resources(self, ctxt, connection_info,
+                                        source_environment):
         pass
         pass
 
 
     @abc.abstractmethod
     @abc.abstractmethod
-    def delete_replica_source_resources(self, ctxt, connection_info,
+    def delete_replica_source_resources(self, ctxt, source_environment,
+                                        connection_info,
                                         migr_resources_dict):
                                         migr_resources_dict):
         pass
         pass
 
 
@@ -341,7 +348,8 @@ class BaseReplicaExportProvider(BaseInstanceProvider):
         pass
         pass
 
 
     @abc.abstractmethod
     @abc.abstractmethod
-    def shutdown_instance(self, ctxt, connection_info, instance_name):
+    def shutdown_instance(self, ctxt, connection_info, source_environment,
+                          instance_name):
         pass
         pass
 
 
 
 

+ 4 - 4
coriolis/replicas/api.py

@@ -9,12 +9,12 @@ class API(object):
         self._rpc_client = rpc_client.ConductorClient()
         self._rpc_client = rpc_client.ConductorClient()
 
 
     def create(self, ctxt, origin_endpoint_id, destination_endpoint_id,
     def create(self, ctxt, origin_endpoint_id, destination_endpoint_id,
-               destination_environment, instances, network_map,
-               storage_mappings, notes=None):
+               source_environment, destination_environment, instances,
+               network_map, storage_mappings, notes=None):
         return self._rpc_client.create_instances_replica(
         return self._rpc_client.create_instances_replica(
             ctxt, origin_endpoint_id, destination_endpoint_id,
             ctxt, origin_endpoint_id, destination_endpoint_id,
-            destination_environment, instances, network_map, storage_mappings,
-            notes)
+            source_environment, destination_environment, instances,
+            network_map, storage_mappings, notes)
 
 
     def delete(self, ctxt, replica_id):
     def delete(self, ctxt, replica_id):
         self._rpc_client.delete_replica(ctxt, replica_id)
         self._rpc_client.delete_replica(ctxt, replica_id)

+ 1 - 0
coriolis/schemas.py

@@ -19,6 +19,7 @@ DEFAULT_SCHEMAS_DIRECTORY = "schemas"
 PROVIDER_CONNECTION_INFO_SCHEMA_NAME = "connection_info_schema.json"
 PROVIDER_CONNECTION_INFO_SCHEMA_NAME = "connection_info_schema.json"
 
 
 PROVIDER_TARGET_ENVIRONMENT_SCHEMA_NAME = "target_environment_schema.json"
 PROVIDER_TARGET_ENVIRONMENT_SCHEMA_NAME = "target_environment_schema.json"
+PROVIDER_SOURCE_ENVIRONMENT_SCHEMA_NAME = "source_environment_schema.json"
 
 
 _CORIOLIS_VM_EXPORT_INFO_SCHEMA_NAME = "vm_export_info_schema.json"
 _CORIOLIS_VM_EXPORT_INFO_SCHEMA_NAME = "vm_export_info_schema.json"
 _CORIOLIS_VM_INSTANCE_INFO_SCHEMA_NAME = "vm_instance_info_schema.json"
 _CORIOLIS_VM_INSTANCE_INFO_SCHEMA_NAME = "vm_instance_info_schema.json"

+ 2 - 1
coriolis/tasks/migration_tasks.py

@@ -22,8 +22,9 @@ class ExportInstanceTask(base.TaskRunner):
         connection_info = base.get_connection_info(ctxt, origin)
         connection_info = base.get_connection_info(ctxt, origin)
         export_path = task_info["export_path"]
         export_path = task_info["export_path"]
 
 
+        source_environment = origin.get('source_environment') or {}
         export_info = provider.export_instance(
         export_info = provider.export_instance(
-            ctxt, connection_info, instance, export_path)
+            ctxt, connection_info, source_environment, instance, export_path)
 
 
         # Validate the output
         # Validate the output
         schemas.validate_value(
         schemas.validate_value(

+ 10 - 4
coriolis/tasks/replica_tasks.py

@@ -29,8 +29,9 @@ class GetInstanceInfoTask(base.TaskRunner):
             event_handler)
             event_handler)
         connection_info = base.get_connection_info(ctxt, origin)
         connection_info = base.get_connection_info(ctxt, origin)
 
 
+        source_environment = origin.get('source_environment') or {}
         export_info = provider.get_replica_instance_info(
         export_info = provider.get_replica_instance_info(
-            ctxt, connection_info, instance)
+            ctxt, connection_info, source_environment, instance)
 
 
         # Validate the output
         # Validate the output
         schemas.validate_value(
         schemas.validate_value(
@@ -48,7 +49,9 @@ class ShutdownInstanceTask(base.TaskRunner):
             event_handler)
             event_handler)
         connection_info = base.get_connection_info(ctxt, origin)
         connection_info = base.get_connection_info(ctxt, origin)
 
 
-        provider.shutdown_instance(ctxt, connection_info, instance)
+        source_environment = origin.get('source_environment') or {}
+        provider.shutdown_instance(ctxt, connection_info, source_environment,
+                                   instance)
 
 
         return task_info
         return task_info
 
 
@@ -128,8 +131,9 @@ class DeployReplicaSourceResourcesTask(base.TaskRunner):
             event_handler)
             event_handler)
         connection_info = base.get_connection_info(ctxt, origin)
         connection_info = base.get_connection_info(ctxt, origin)
 
 
+        source_environment = origin.get('source_environment') or {}
         replica_resources_info = provider.deploy_replica_source_resources(
         replica_resources_info = provider.deploy_replica_source_resources(
-            ctxt, connection_info)
+            ctxt, connection_info, source_environment)
 
 
         task_info["migr_source_resources"] = replica_resources_info[
         task_info["migr_source_resources"] = replica_resources_info[
             "migr_resources"]
             "migr_resources"]
@@ -150,9 +154,11 @@ class DeleteReplicaSourceResourcesTask(base.TaskRunner):
 
 
         migr_resources = task_info.get("migr_source_resources")
         migr_resources = task_info.get("migr_source_resources")
 
 
+        source_environment = origin.get("source_environment", {})
+
         if migr_resources:
         if migr_resources:
             provider.delete_replica_source_resources(
             provider.delete_replica_source_resources(
-                ctxt, connection_info, migr_resources)
+                ctxt, source_environment, connection_info, migr_resources)
 
 
         task_info["migr_source_resources"] = None
         task_info["migr_source_resources"] = None
         task_info["migr_source_connection_info"] = None
         task_info["migr_source_connection_info"] = None

+ 7 - 0
coriolis/worker/rpc/client.py

@@ -91,6 +91,13 @@ class WorkerClient(object):
             platform_name=platform_name,
             platform_name=platform_name,
             target_env=target_env)
             target_env=target_env)
 
 
+    def validate_endpoint_source_environment(
+            self, ctxt, platform_name, source_env):
+        return self._client.call(
+            ctxt, 'validate_endpoint_source_environment',
+            platform_name=platform_name,
+            source_env=source_env)
+
     def get_endpoint_storage(self, ctxt, platform_name, connection_info, env):
     def get_endpoint_storage(self, ctxt, platform_name, connection_info, env):
         return self._client.call(
         return self._client.call(
             ctxt, 'get_endpoint_storage',
             ctxt, 'get_endpoint_storage',

+ 24 - 0
coriolis/worker/rpc/server.py

@@ -21,6 +21,7 @@ from coriolis.providers import factory as providers_factory
 from coriolis import schemas
 from coriolis import schemas
 from coriolis.tasks import factory as task_runners_factory
 from coriolis.tasks import factory as task_runners_factory
 from coriolis import utils
 from coriolis import utils
+from coriolis.api.v1 import utils as api_utils
 
 
 
 
 worker_opts = [
 worker_opts = [
@@ -294,6 +295,7 @@ class WorkerServerEndpoint(object):
     def get_available_providers(self, ctxt):
     def get_available_providers(self, ctxt):
         return providers_factory.get_available_providers()
         return providers_factory.get_available_providers()
 
 
+    @api_utils.bad_request_on_error("Invalid destination environment: %s")
     def validate_endpoint_target_environment(
     def validate_endpoint_target_environment(
             self, ctxt, platform_name, target_env):
             self, ctxt, platform_name, target_env):
         provider = providers_factory.get_provider(
         provider = providers_factory.get_provider(
@@ -310,6 +312,23 @@ class WorkerServerEndpoint(object):
 
 
         return (is_valid, message)
         return (is_valid, message)
 
 
+    @api_utils.bad_request_on_error("Invalid source environment: %s")
+    def validate_endpoint_source_environment(
+            self, ctxt, platform_name, source_env):
+        provider = providers_factory.get_provider(
+            platform_name, constants.PROVIDER_TYPE_EXPORT, None)
+        source_env_schema = provider.get_source_environment_schema()
+
+        is_valid = True
+        message = None
+        try:
+            schemas.validate_value(source_env, source_env_schema)
+        except exception.SchemaValidationException as ex:
+            is_valid = False
+            message = str(ex)
+
+        return (is_valid, message)
+
     def validate_endpoint_connection(self, ctxt, platform_name,
     def validate_endpoint_connection(self, ctxt, platform_name,
                                      connection_info):
                                      connection_info):
         provider = providers_factory.get_provider(
         provider = providers_factory.get_provider(
@@ -354,6 +373,11 @@ class WorkerServerEndpoint(object):
             schema = provider.get_target_environment_schema()
             schema = provider.get_target_environment_schema()
             schemas["destination_environment_schema"] = schema
             schemas["destination_environment_schema"] = schema
 
 
+        if provider_type in [constants.PROVIDER_TYPE_EXPORT,
+                             constants.PROVIDER_TYPE_REPLICA_EXPORT]:
+            schema = provider.get_source_environment_schema()
+            schemas["source_environment_schema"] = schema
+
         return schemas
         return schemas