Alessandro Pilotti před 10 roky
rodič
revize
79eefd1028

+ 3 - 0
coriolis/api/auth.py

@@ -32,6 +32,9 @@ class CoriolisKeystoneContext(wsgi.Middleware):
         project_name = req.headers.get('X_TENANT_NAME')
         project_name = req.headers.get('X_TENANT_NAME')
 
 
         req_id = req.environ.get(request_id.ENV_REQUEST_ID)
         req_id = req.environ.get(request_id.ENV_REQUEST_ID)
+        # TODO(alexpilotti): Check why it's not str
+        if isinstance(req_id, bytes):
+            req_id = req_id.decode()
 
 
         # Get the auth token
         # Get the auth token
         auth_token = req.headers.get('X_AUTH_TOKEN',
         auth_token = req.headers.get('X_AUTH_TOKEN',

+ 1 - 1
coriolis/api/v1/migrations.py

@@ -59,7 +59,7 @@ class MigrationController(object):
             req.environ['coriolis.context'], origin, destination, instances))
             req.environ['coriolis.context'], origin, destination, instances))
 
 
     def delete(self, req, id):
     def delete(self, req, id):
-        self._migration_api.stop(req.environ['coriolis.context'], id)
+        self._migration_api.delete(req.environ['coriolis.context'], id)
         raise exc.HTTPNoContent()
         raise exc.HTTPNoContent()
 
 
 
 

+ 4 - 0
coriolis/conductor/rpc/client.py

@@ -22,6 +22,10 @@ class ConductorClient(object):
             ctxt, 'migrate_instances', origin=origin, destination=destination,
             ctxt, 'migrate_instances', origin=origin, destination=destination,
             instances=instances)
             instances=instances)
 
 
+    def delete_migration(self, ctxt, migration_id):
+        self._client.call(
+            ctxt, 'delete_migration', migration_id=migration_id)
+
     def stop_instances_migration(self, ctxt, migration_id):
     def stop_instances_migration(self, ctxt, migration_id):
         self._client.call(
         self._client.call(
             ctxt, 'stop_instances_migration', migration_id=migration_id)
             ctxt, 'stop_instances_migration', migration_id=migration_id)

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

@@ -61,6 +61,14 @@ class ConductorServerEndpoint(object):
 
 
         return self.get_migration(ctxt, migration.id)
         return self.get_migration(ctxt, migration.id)
 
 
+    def delete_migration(self, ctxt, migration_id):
+        migration = db_api.get_migration(ctxt, migration_id)
+        for task in migration.tasks:
+            if task.status == constants.TASK_STATUS_STARTED:
+                raise exception.CoriolisException(
+                    "Cannot delete a running migration")
+        db_api.delete_migration(ctxt, migration_id)
+
     def stop_instances_migration(self, ctxt, migration_id):
     def stop_instances_migration(self, ctxt, migration_id):
         migration = db_api.get_migration(ctxt, migration_id)
         migration = db_api.get_migration(ctxt, migration_id)
         for task in migration.tasks:
         for task in migration.tasks:

+ 3 - 1
coriolis/context.py

@@ -9,7 +9,7 @@ class RequestContext(context.RequestContext):
                  roles=None, project_name=None, remote_address=None,
                  roles=None, project_name=None, remote_address=None,
                  timestamp=None, request_id=None, auth_token=None,
                  timestamp=None, request_id=None, auth_token=None,
                  overwrite=True, domain=None, user_domain=None,
                  overwrite=True, domain=None, user_domain=None,
-                 project_domain=None, **kwargs):
+                 project_domain=None, show_deleted=None, **kwargs):
 
 
         super(RequestContext, self).__init__(auth_token=auth_token,
         super(RequestContext, self).__init__(auth_token=auth_token,
                                              user=user,
                                              user=user,
@@ -18,6 +18,7 @@ class RequestContext(context.RequestContext):
                                              user_domain=user_domain,
                                              user_domain=user_domain,
                                              project_domain=project_domain,
                                              project_domain=project_domain,
                                              is_admin=is_admin,
                                              is_admin=is_admin,
+                                             show_deleted=show_deleted,
                                              request_id=request_id,
                                              request_id=request_id,
                                              overwrite=overwrite)
                                              overwrite=overwrite)
         self.roles = roles or []
         self.roles = roles or []
@@ -39,6 +40,7 @@ class RequestContext(context.RequestContext):
         result['remote_address'] = self.remote_address
         result['remote_address'] = self.remote_address
         result['timestamp'] = self.timestamp.isoformat()
         result['timestamp'] = self.timestamp.isoformat()
         result['request_id'] = self.request_id
         result['request_id'] = self.request_id
+        result['show_deleted'] = self.show_deleted
         return result
         return result
 
 
     @classmethod
     @classmethod

+ 36 - 7
coriolis/db/api.py

@@ -32,16 +32,37 @@ def db_version(engine):
     return IMPL.db_version(engine)
     return IMPL.db_version(engine)
 
 
 
 
+def _session(context):
+    return (context and context.session) or get_session()
+
+
+def _model_query(context, *args):
+    session = _session(context)
+    return session.query(*args)
+
+
+def _soft_delete_aware_query(context, *args, **kwargs):
+    """Query helper that accounts for context's `show_deleted` field.
+    :param show_deleted: if True, overrides context's show_deleted field.
+    """
+    query = _model_query(context, *args)
+    show_deleted = kwargs.get('show_deleted') or context.show_deleted
+
+    if not show_deleted:
+        query = query.filter_by(deleted_at=None)
+    return query
+
+
 @enginefacade.reader
 @enginefacade.reader
 def get_migrations(context):
 def get_migrations(context):
-    return context.session.query(models.Migration).options(
+    return _soft_delete_aware_query(context, models.Migration).options(
         orm.joinedload("tasks")).filter_by(
         orm.joinedload("tasks")).filter_by(
         project_id=context.tenant).all()
         project_id=context.tenant).all()
 
 
 
 
 @enginefacade.reader
 @enginefacade.reader
 def get_migration(context, migration_id):
 def get_migration(context, migration_id):
-    return context.session.query(models.Migration).options(
+    return _soft_delete_aware_query(context, models.Migration).options(
         orm.joinedload("tasks").joinedload("progress_updates")).filter_by(
         orm.joinedload("tasks").joinedload("progress_updates")).filter_by(
         project_id=context.tenant, id=migration_id).first()
         project_id=context.tenant, id=migration_id).first()
 
 
@@ -53,16 +74,24 @@ def add_migration(context, migration):
     context.session.add(migration)
     context.session.add(migration)
 
 
 
 
+@enginefacade.writer
+def delete_migration(context, migration_id):
+    count = _soft_delete_aware_query(context, models.Migration).filter_by(
+        project_id=context.tenant, id=migration_id).soft_delete()
+    if count == 0:
+        raise exception.CoriolisException("0 entries were soft deleted")
+
+
 @enginefacade.writer
 @enginefacade.writer
 def set_migration_status(context, migration_id, status):
 def set_migration_status(context, migration_id, status):
-    migration = context.session.query(models.Migration).filter_by(
+    migration = _soft_delete_aware_query(context, models.Migration).filter_by(
         project_id=context.tenant, id=migration_id).first()
         project_id=context.tenant, id=migration_id).first()
     migration.status = status
     migration.status = status
 
 
 
 
 @enginefacade.writer
 @enginefacade.writer
 def set_task_status(context, task_id, status, exception_details=None):
 def set_task_status(context, task_id, status, exception_details=None):
-    task = context.session.query(models.Task).filter_by(
+    task = _soft_delete_aware_query(context, models.Task).filter_by(
         id=task_id).first()
         id=task_id).first()
     task.status = status
     task.status = status
     task.exception_details = exception_details
     task.exception_details = exception_details
@@ -70,7 +99,7 @@ def set_task_status(context, task_id, status, exception_details=None):
 
 
 @enginefacade.writer
 @enginefacade.writer
 def set_task_host(context, task_id, host, process_id):
 def set_task_host(context, task_id, host, process_id):
-    task = context.session.query(models.Task).filter_by(
+    task = _soft_delete_aware_query(context, models.Task).filter_by(
         id=task_id).first()
         id=task_id).first()
     task.host = host
     task.host = host
     task.process_id = process_id
     task.process_id = process_id
@@ -82,8 +111,8 @@ def get_task(context, task_id, include_migration_tasks=False):
     if include_migration_tasks:
     if include_migration_tasks:
         join_options = join_options.joinedload("tasks")
         join_options = join_options.joinedload("tasks")
 
 
-    return context.session.query(models.Task).options(join_options).filter_by(
-        id=task_id).first()
+    return _soft_delete_aware_query(context, models.Task).options(
+        join_options).filter_by(id=task_id).first()
 
 
 
 
 @enginefacade.writer
 @enginefacade.writer

+ 6 - 0
coriolis/db/sqlalchemy/migrate_repo/versions/001_initial.py

@@ -13,6 +13,8 @@ def upgrade(migrate_engine):
                           default=lambda: str(uuid.uuid4())),
                           default=lambda: str(uuid.uuid4())),
         sqlalchemy.Column('created_at', sqlalchemy.DateTime),
         sqlalchemy.Column('created_at', sqlalchemy.DateTime),
         sqlalchemy.Column('updated_at', sqlalchemy.DateTime),
         sqlalchemy.Column('updated_at', sqlalchemy.DateTime),
+        sqlalchemy.Column('deleted_at', sqlalchemy.DateTime),
+        sqlalchemy.Column('deleted', sqlalchemy.String(36)),
         sqlalchemy.Column("user_id", sqlalchemy.String(255), nullable=False),
         sqlalchemy.Column("user_id", sqlalchemy.String(255), nullable=False),
         sqlalchemy.Column("project_id", sqlalchemy.String(255),
         sqlalchemy.Column("project_id", sqlalchemy.String(255),
                           nullable=False),
                           nullable=False),
@@ -30,6 +32,8 @@ def upgrade(migrate_engine):
                           default=lambda: str(uuid.uuid4())),
                           default=lambda: str(uuid.uuid4())),
         sqlalchemy.Column('created_at', sqlalchemy.DateTime),
         sqlalchemy.Column('created_at', sqlalchemy.DateTime),
         sqlalchemy.Column('updated_at', sqlalchemy.DateTime),
         sqlalchemy.Column('updated_at', sqlalchemy.DateTime),
+        sqlalchemy.Column('deleted_at', sqlalchemy.DateTime),
+        sqlalchemy.Column('deleted', sqlalchemy.String(36)),
         sqlalchemy.Column("migration_id", sqlalchemy.String(36),
         sqlalchemy.Column("migration_id", sqlalchemy.String(36),
                           sqlalchemy.ForeignKey('migration.id'),
                           sqlalchemy.ForeignKey('migration.id'),
                           nullable=False),
                           nullable=False),
@@ -51,6 +55,8 @@ def upgrade(migrate_engine):
                           default=lambda: str(uuid.uuid4())),
                           default=lambda: str(uuid.uuid4())),
         sqlalchemy.Column('created_at', sqlalchemy.DateTime),
         sqlalchemy.Column('created_at', sqlalchemy.DateTime),
         sqlalchemy.Column('updated_at', sqlalchemy.DateTime),
         sqlalchemy.Column('updated_at', sqlalchemy.DateTime),
+        sqlalchemy.Column('deleted_at', sqlalchemy.DateTime),
+        sqlalchemy.Column('deleted', sqlalchemy.String(36)),
         sqlalchemy.Column("task_id", sqlalchemy.String(36),
         sqlalchemy.Column("task_id", sqlalchemy.String(36),
                           sqlalchemy.ForeignKey('task.id'),
                           sqlalchemy.ForeignKey('task.id'),
                           nullable=False),
                           nullable=False),

+ 6 - 3
coriolis/db/sqlalchemy/models.py

@@ -10,7 +10,8 @@ from coriolis.db.sqlalchemy import types
 BASE = declarative.declarative_base()
 BASE = declarative.declarative_base()
 
 
 
 
-class TaskProgressUpdate(BASE, models.TimestampMixin, models.ModelBase):
+class TaskProgressUpdate(BASE, models.TimestampMixin, models.SoftDeleteMixin,
+                         models.ModelBase):
     __tablename__ = 'task_progress_update'
     __tablename__ = 'task_progress_update'
 
 
     id = sqlalchemy.Column(sqlalchemy.String(36),
     id = sqlalchemy.Column(sqlalchemy.String(36),
@@ -24,7 +25,8 @@ class TaskProgressUpdate(BASE, models.TimestampMixin, models.ModelBase):
     message = sqlalchemy.Column(sqlalchemy.String(1024), nullable=True)
     message = sqlalchemy.Column(sqlalchemy.String(1024), nullable=True)
 
 
 
 
-class Task(BASE, models.TimestampMixin, models.ModelBase):
+class Task(BASE, models.TimestampMixin, models.SoftDeleteMixin,
+           models.ModelBase):
     __tablename__ = 'task'
     __tablename__ = 'task'
 
 
     id = sqlalchemy.Column(sqlalchemy.String(36),
     id = sqlalchemy.Column(sqlalchemy.String(36),
@@ -45,7 +47,8 @@ class Task(BASE, models.TimestampMixin, models.ModelBase):
                                         backref=orm.backref('task'))
                                         backref=orm.backref('task'))
 
 
 
 
-class Migration(BASE, models.TimestampMixin, models.ModelBase):
+class Migration(BASE, models.TimestampMixin, models.ModelBase,
+                models.SoftDeleteMixin):
     __tablename__ = 'migration'
     __tablename__ = 'migration'
 
 
     id = sqlalchemy.Column(sqlalchemy.String(36),
     id = sqlalchemy.Column(sqlalchemy.String(36),

+ 4 - 1
coriolis/migrations/api.py

@@ -9,7 +9,10 @@ class API(object):
         return self._rpc_client.begin_migrate_instances(
         return self._rpc_client.begin_migrate_instances(
             ctxt, origin, destination, instances)
             ctxt, origin, destination, instances)
 
 
-    def stop(ctxt, self, migration_id):
+    def delete(self, ctxt, migration_id):
+        self._rpc_client.delete_migration(ctxt, migration_id)
+
+    def stop(self, ctxt, migration_id):
         self._rpc_client.stop_instances_migration(ctxt, migration_id)
         self._rpc_client.stop_instances_migration(ctxt, migration_id)
 
 
     def get_migrations(self, ctxt):
     def get_migrations(self, ctxt):