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

Merged in aznashwan/coriolis-core/migration-result (pull request #125)

Save 'transfer_result' for migrations/replicas in the DB.
Nashwan Azhari 7 лет назад
Родитель
Сommit
13229fbb09

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

@@ -3,9 +3,21 @@
 
 import itertools
 
+from oslo_config import cfg as conf
+
 from coriolis.api.v1.views import replica_tasks_execution_view
 
 
+MIGRATIONS_API_OPTS = [
+    conf.BoolOpt("include_task_info_in_migrations_api",
+                 default=False,
+                 help="Whether or not to expose the internal 'info' field of "
+                      "a Migration as part of a `GET` request.")]
+
+CONF = conf.CONF
+CONF.register_opts(MIGRATIONS_API_OPTS)
+
+
 def _format_migration(req, migration, keys=None):
     def transform(key, value):
         if keys and key not in keys:
@@ -22,6 +34,11 @@ def _format_migration(req, migration, keys=None):
     tasks = execution.get("tasks")
     if tasks:
         migration_dict["tasks"] = tasks
+
+    if not CONF.include_task_info_in_migrations_api and (
+            "info" in migration_dict):
+        migration_dict.pop("info")
+
     del migration_dict["executions"]
     return migration_dict
 

+ 19 - 1
coriolis/api/v1/views/replica_view.py

@@ -3,6 +3,18 @@
 
 import itertools
 
+from oslo_config import cfg as conf
+
+
+REPLICA_API_OPTS = [
+    conf.BoolOpt("include_task_info_in_replicas_api",
+                 default=False,
+                 help="Whether or not to expose the internal 'info' field of "
+                      "a Replica as part of a `GET` request.")]
+
+CONF = conf.CONF
+CONF.register_opts(REPLICA_API_OPTS)
+
 
 def _format_replica(req, replica, keys=None):
     def transform(key, value):
@@ -10,9 +22,15 @@ def _format_replica(req, replica, keys=None):
             return
         yield (key, value)
 
-    return dict(itertools.chain.from_iterable(
+    replica_dict = dict(itertools.chain.from_iterable(
         transform(k, v) for k, v in replica.items()))
 
+    if not CONF.include_task_info_in_replicas_api and (
+            "info" in replica_dict):
+        replica_dict.pop("info")
+
+    return replica_dict
+
 
 def single(req, replica):
     return {"replica": _format_replica(req, replica)}

+ 28 - 0
coriolis/conductor/rpc/server.py

@@ -14,6 +14,7 @@ from coriolis.db.sqlalchemy import models
 from coriolis import exception
 from coriolis import keystone
 from coriolis.replica_cron.rpc import client as rpc_cron_client
+from coriolis import schemas
 from coriolis import utils
 from coriolis.worker.rpc import client as rpc_worker_client
 
@@ -784,6 +785,33 @@ class ConductorServerEndpoint(object):
                 # new volumes need to be deployed in place of the migrated
                 # ones.
                 updated_task_info = {"volumes_info": None}
+        elif task_type in (
+                constants.TASK_TYPE_FINALIZE_IMPORT_INSTANCE,
+                constants.TASK_TYPE_FINALIZE_REPLICA_INSTANCE_DEPLOYMENT):
+            # set 'transfer_result' in the 'base_transfer_action'
+            # table if the task returned a result.
+            if "transfer_result" in task_info:
+                transfer_result = task_info.get("transfer_result")
+                try:
+                    schemas.validate_value(
+                        transfer_result,
+                        schemas.CORIOLIS_VM_EXPORT_INFO_SCHEMA)
+                    LOG.debug(
+                        "Setting result for transfer action '%s': %s",
+                        execution.action_id, transfer_result)
+                    db_api.set_transfer_action_result(
+                        ctxt, execution.action_id, task.instance,
+                        transfer_result)
+                except exception.SchemaValidationException as ex:
+                    LOG.warn(
+                        "Could not validate transfer result '%s' against the "
+                        "VM export info schema. NOT saving value in Database. "
+                        "Exception details: %s",
+                        transfer_result, utils.get_exception_details())
+            else:
+                LOG.debug(
+                    "No 'transfer_result' was returned for task type '%s' "
+                    "for transfer action '%s'", task_type, execution.action_id)
 
         if updated_task_info:
             self._update_replica_volumes_info(

+ 16 - 0
coriolis/db/api.py

@@ -454,6 +454,22 @@ def set_transfer_action_info(context, action_id, instance, instance_info):
     return action_info[instance]
 
 
+@enginefacade.writer
+def set_transfer_action_result(context, action_id, instance, result):
+    """ Adds the result for the given 'instance' in the 'transfer_result'
+    JSON in the 'base_transfer_action' table.
+    """
+    action = get_action(context, action_id)
+
+    transfer_result = {}
+    if action.transfer_result:
+        transfer_result = action.transfer_result.copy()
+    transfer_result[instance] = result
+    action.transfer_result = transfer_result
+
+    return transfer_result[instance]
+
+
 @enginefacade.reader
 def get_tasks_execution(context, execution_id):
     q = _soft_delete_aware_query(context, models.TasksExecution)

+ 17 - 0
coriolis/db/sqlalchemy/migrate_repo/versions/005_adds_transfer_result.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 'transfer_result' column to 'base_transfer_action':
+    base_transfer_action = sqlalchemy.Table(
+        'base_transfer_action', meta, autoload=True)
+
+    transfer_result = sqlalchemy.Column(
+        "transfer_result", sqlalchemy.Text, nullable=True)
+    base_transfer_action.create_column(transfer_result)

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

@@ -111,6 +111,7 @@ class BaseTransferAction(BASE, models.TimestampMixin, models.ModelBase,
     destination_endpoint_id = sqlalchemy.Column(
         sqlalchemy.String(36),
         sqlalchemy.ForeignKey('endpoint.id'), nullable=False)
+    transfer_result = sqlalchemy.Column(types.Json, nullable=True)
 
     __mapper_args__ = {
         'polymorphic_identity': 'base_transfer_action',

+ 10 - 2
coriolis/providers/base.py

@@ -183,7 +183,11 @@ class BaseImportProvider(BaseImportInstanceProvider):
     @abc.abstractmethod
     def finalize_import_instance(self, ctxt, connection_info,
                                  instance_deployment_info):
-        pass
+        """ Should return a dict with the info of the migrated VM on the
+        destination platform in the same format as offered by
+        'BaseExportProvider.export_instance()'.
+        """
+        return {}
 
     @abc.abstractmethod
     def cleanup_failed_import_instance(self, ctxt, connection_info,
@@ -202,7 +206,11 @@ class BaseReplicaImportProvider(BaseImportInstanceProvider):
     @abc.abstractmethod
     def finalize_replica_instance_deployment(self, ctxt, connection_info,
                                              instance_deployment_info):
-        pass
+        """ Should return a dict with the info of the migrated VM on the
+        destination platform in the same format as offered by
+        'BaseExportProvider.export_instance()'.
+        """
+        return {}
 
     @abc.abstractmethod
     def cleanup_failed_replica_instance_deployment(self, ctxt, connection_info,

+ 7 - 1
coriolis/tasks/migration_tasks.py

@@ -138,8 +138,14 @@ class FinalizeImportInstanceTask(base.TaskRunner):
         connection_info = base.get_connection_info(ctxt, destination)
         instance_deployment_info = task_info["instance_deployment_info"]
 
-        provider.finalize_import_instance(
+        result = provider.finalize_import_instance(
             ctxt, connection_info, instance_deployment_info)
+        if result is not None:
+            task_info["transfer_result"] = result
+        else:
+            LOG.warn(
+                "'None' was returned as result for Finalize Import Instance "
+                "task '%s'.", task_info)
 
         return task_info
 

+ 7 - 1
coriolis/tasks/replica_tasks.py

@@ -247,8 +247,14 @@ class FinalizeReplicaInstanceDeploymentTask(base.TaskRunner):
         connection_info = base.get_connection_info(ctxt, destination)
         instance_deployment_info = task_info["instance_deployment_info"]
 
-        provider.finalize_replica_instance_deployment(
+        result = provider.finalize_replica_instance_deployment(
             ctxt, connection_info, instance_deployment_info)
+        if result is not None:
+            task_info["transfer_result"] = result
+        else:
+            LOG.warn(
+                "'None' was returned as result for Finalize Replica Instance "
+                "deployment task '%s'.", task_info)
 
         return task_info