Add user_id and project_id column to Migration

This adds two String columns, 'user_id' and 'project_id',
to the ``migrations`` table in the DB and corresponding
fields in the Migration versioned object.

Add optional 'user_id' and/or 'project_id' query parameters
for filtering migrations by user or project in the DB.

Co-Authored-By: Qiu Fossen <qiujunting>
Part of blueprint add-user-id-field-to-the-migrations-table
Change-Id: I4700cb00da66eaa84621e91649eaf93c4a6e6f01
This commit is contained in:
zhangbailin 2019-08-01 17:34:19 +08:00 committed by melanie witt
parent 7a18209a81
commit c65aabded2
12 changed files with 136 additions and 4 deletions

View File

@ -62,6 +62,10 @@ class MigrationsController(wsgi.Controller):
if 'memory_total' in obj:
for key in detail_keys:
del obj[key]
if 'user_id' in obj:
del obj['user_id']
if 'project_id' in obj:
del obj['project_id']
# NOTE(Shaohe Feng) above version 2.23, add migration_type for all
# kinds of migration, but we only add links just for in-progress
# live-migration.

View File

@ -4484,6 +4484,13 @@ def migration_get_all_by_filters(context, filters,
if "instance_uuid" in filters:
instance_uuid = filters["instance_uuid"]
query = query.filter(models.Migration.instance_uuid == instance_uuid)
if 'user_id' in filters:
user_id = filters['user_id']
query = query.filter(models.Migration.user_id == user_id)
if 'project_id' in filters:
project_id = filters['project_id']
query = query.filter(models.Migration.project_id == project_id)
if marker:
try:
marker = migration_get_by_uuid(context, marker)

View File

@ -0,0 +1,27 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from sqlalchemy import MetaData, Column, Table, String
NEW_COLUMNS_NAME = ['user_id', 'project_id']
BASE_TABLE_NAME = 'migrations'
def upgrade(migrate_engine):
meta = MetaData(bind=migrate_engine)
for prefix in ('', 'shadow_'):
table = Table(prefix + BASE_TABLE_NAME, meta, autoload=True)
for new_column_name in NEW_COLUMNS_NAME:
new_column = Column(new_column_name, String(255), nullable=True)
if not hasattr(table.c, new_column_name):
table.create_column(new_column)

View File

@ -797,6 +797,9 @@ class Migration(BASE, NovaBase, models.SoftDeleteMixin):
disk_remaining = Column(BigInteger, nullable=True)
cross_cell_move = Column(Boolean, default=False)
user_id = Column(String(255), nullable=True)
project_id = Column(String(255), nullable=True)
instance = orm.relationship("Instance", foreign_keys=instance_uuid,
primaryjoin='and_(Migration.instance_uuid == '
'Instance.uuid, Instance.deleted == '

View File

@ -42,7 +42,8 @@ class Migration(base.NovaPersistentObject, base.NovaObject,
# Version 1.4: Added migration progress detail
# Version 1.5: Added uuid
# Version 1.6: Added cross_cell_move and get_by_uuid().
VERSION = '1.6'
# Version 1.7: Added user_id and project_id
VERSION = '1.7'
fields = {
'id': fields.IntegerField(),
@ -67,6 +68,10 @@ class Migration(base.NovaPersistentObject, base.NovaObject,
'disk_processed': fields.IntegerField(nullable=True),
'disk_remaining': fields.IntegerField(nullable=True),
'cross_cell_move': fields.BooleanField(default=False),
# request context user id
'user_id': fields.StringField(nullable=True),
# request context project id
'project_id': fields.StringField(nullable=True),
}
@staticmethod
@ -104,6 +109,11 @@ class Migration(base.NovaPersistentObject, base.NovaObject,
del primitive['uuid']
if target_version < (1, 6) and 'cross_cell_move' in primitive:
del primitive['cross_cell_move']
if target_version < (1, 7):
if 'user_id' in primitive:
del primitive['user_id']
if 'project_id' in primitive:
del primitive['project_id']
def obj_load_attr(self, attrname):
if attrname == 'migration_type':

View File

@ -56,6 +56,8 @@ fake_migrations = [
'deleted': False,
'uuid': uuids.migration1,
'cross_cell_move': False,
'user_id': None,
'project_id': None
},
# non in-progress live migration
{
@ -83,6 +85,8 @@ fake_migrations = [
'deleted': False,
'uuid': uuids.migration2,
'cross_cell_move': False,
'user_id': None,
'project_id': None
},
# in-progress resize
{
@ -110,6 +114,8 @@ fake_migrations = [
'deleted': False,
'uuid': uuids.migration3,
'cross_cell_move': False,
'user_id': None,
'project_id': None
},
# non in-progress resize
{
@ -137,6 +143,8 @@ fake_migrations = [
'deleted': False,
'uuid': uuids.migration4,
'cross_cell_move': False,
'user_id': None,
'project_id': None
}
]

View File

@ -57,6 +57,8 @@ fake_migrations = [
'deleted': False,
'uuid': uuids.migration1,
'cross_cell_move': False,
'user_id': None,
'project_id': None
},
{
'id': 5678,
@ -83,6 +85,8 @@ fake_migrations = [
'deleted': False,
'uuid': uuids.migration2,
'cross_cell_move': False,
'user_id': None,
'project_id': None
}
]

View File

@ -4106,7 +4106,8 @@ class _ComputeAPIUnitTestMixIn(object):
'disk_remaining': None, 'deleted': False,
'hidden': False, 'created_at': None,
'updated_at': None, 'deleted_at': None,
'cross_cell_move': False}
'cross_cell_move': False, 'user_id': None,
'project_id': None}
def migration_get(context, id):
return migrations[id]

View File

@ -1123,7 +1123,8 @@ class MigrationTestCase(test.TestCase):
def _create(self, status='migrating', source_compute='host1',
source_node='a', dest_compute='host2', dest_node='b',
system_metadata=None, migration_type=None, uuid=None,
created_at=None, updated_at=None):
created_at=None, updated_at=None, user_id=None,
project_id=None):
values = {'host': source_compute}
instance = db.instance_create(self.ctxt, values)
@ -1139,6 +1140,10 @@ class MigrationTestCase(test.TestCase):
values['created_at'] = created_at
if updated_at:
values['updated_at'] = updated_at
if user_id:
values['user_id'] = user_id
if project_id:
values['project_id'] = project_id
db.migration_create(self.ctxt, values)
return values
@ -1292,6 +1297,54 @@ class MigrationTestCase(test.TestCase):
self.assertEqual(migration['instance_uuid'],
instance_migrations[0]['instance_uuid'])
def test_get_migrations_by_filters_user_id(self):
# Create two migrations with different user_id
user_id1 = "fake_user_id"
self._create(user_id=user_id1)
user_id2 = "other_fake_user_id"
self._create(user_id=user_id2)
# Filter on only the first user_id
filters = {"user_id": user_id1}
migrations = db.migration_get_all_by_filters(self.ctxt, filters)
# We should only get one migration back because we filtered on only
# one of the two different user_id
self.assertEqual(1, len(migrations))
for migration in migrations:
self.assertEqual(filters['user_id'], migration['user_id'])
def test_get_migrations_by_filters_project_id(self):
# Create two migrations with different project_id
project_id1 = "fake_project_id"
self._create(project_id=project_id1)
project_id2 = "other_fake_project_id"
self._create(project_id=project_id2)
# Filter on only the first project_id
filters = {"project_id": project_id1}
migrations = db.migration_get_all_by_filters(self.ctxt, filters)
# We should only get one migration back because we filtered on only
# one of the two different project_id
self.assertEqual(1, len(migrations))
for migration in migrations:
self.assertEqual(filters['project_id'], migration['project_id'])
def test_get_migrations_by_filters_user_id_and_project_id(self):
# Create two migrations with different user_id and project_id
user_id1 = "fake_user_id"
project_id1 = "fake_project_id"
self._create(user_id=user_id1, project_id=project_id1)
user_id2 = "other_fake_user_id"
project_id2 = "other_fake_project_id"
self._create(user_id=user_id2, project_id=project_id2)
# Filter on only the first user_id and project_id
filters = {"user_id": user_id1, "project_id": project_id1}
migrations = db.migration_get_all_by_filters(self.ctxt, filters)
# We should only get one migration back because we filtered on only
# one of the two different user_id and project_id
self.assertEqual(1, len(migrations))
for migration in migrations:
self.assertEqual(filters['user_id'], migration['user_id'])
self.assertEqual(filters['project_id'], migration['project_id'])
def test_migration_get_unconfirmed_by_dest_compute(self):
# Ensure no migrations are returned.
results = db.migration_get_unconfirmed_by_dest_compute(self.ctxt, 10,

View File

@ -1041,6 +1041,13 @@ class NovaMigrationsCheckers(test_migrations.ModelsMigrationsSync,
# check. The actual test for 400 is in TestServicesUUIDCheck.
pass
def _check_401(self, engine, data):
for prefix in ('', 'shadow_'):
self.assertColumnExists(
engine, '%smigrations' % prefix, 'user_id')
self.assertColumnExists(
engine, '%smigrations' % prefix, 'project_id')
class TestNovaMigrationsSQLite(NovaMigrationsCheckers,
test_fixtures.OpportunisticDBTestMixin,

View File

@ -54,6 +54,8 @@ def fake_db_migration(**updates):
'disk_processed': 23456,
'disk_remaining': 211111,
'cross_cell_move': False,
'user_id': None,
'project_id': None,
}
if updates:
@ -110,6 +112,8 @@ class _TestMigrationObject(object):
self.assertEqual(fake_migration['dest_compute'], mig.dest_compute)
self.assertIn('uuid', mig)
self.assertFalse(mig.cross_cell_move)
self.assertIn('user_id', mig)
self.assertIn('project_id', mig)
mock_create.assert_called_once_with(ctxt,
{'source_compute': 'foo',
'migration_type': 'resize',
@ -298,6 +302,10 @@ class _TestMigrationObject(object):
source_compute='fake-host' # added in 1.0
)
data = lambda x: x['nova_object.data']
primitive = data(mig.obj_to_primitive(target_version='1.6'))
self.assertIn('cross_cell_move', primitive)
self.assertNotIn('user_id', primitive)
self.assertNotIn('project_id', primitive)
primitive = data(mig.obj_to_primitive(target_version='1.5'))
self.assertIn('uuid', primitive)
self.assertNotIn('cross_cell_move', primitive)

View File

@ -1094,7 +1094,7 @@ object_data = {
'LibvirtLiveMigrateBDMInfo': '1.1-5f4a68873560b6f834b74e7861d71aaf',
'LibvirtLiveMigrateData': '1.9-7082cc7dd48ca49df71fe3846521b2f3',
'MemoryDiagnostics': '1.0-2c995ae0f2223bb0f8e523c5cc0b83da',
'Migration': '1.6-fd6b1abfd8e3ce945348e7b5f04baa28',
'Migration': '1.7-b77066a88d08bdb0b05d7bc18780c55a',
'MigrationContext': '1.1-9fb17b0b521370957a884636499df52d',
'MigrationList': '1.4-983a9c29d4f1e747ce719dc9063b729b',
'MonitorMetric': '1.1-53b1db7c4ae2c531db79761e7acc52ba',