Add migration db and object pagination support.

This will be used by migration pagination API.

Added sort_keys, sort_dirs, limit, marker kwargs to get_by_filters
method of MigrationList object, also added them and change-since to
db.migration_get_all_by_filters for migrations pagination support.

We need ensure it will maintain a default sort order with
older microversions.

Part of blueprint add-pagination-and-change-since-for-migration-list

Change-Id: I3bbd5b95ecf9d096e662592c9b2e6ad37382fb49
This commit is contained in:
Yikun Jiang 2017-10-25 11:44:56 +08:00 committed by Matt Riedemann
parent 74deea4d8f
commit 971cbaee02
6 changed files with 121 additions and 12 deletions

View File

@ -568,9 +568,13 @@ def migration_get_in_progress_by_host_and_node(context, host, node):
return IMPL.migration_get_in_progress_by_host_and_node(context, host, node)
def migration_get_all_by_filters(context, filters):
def migration_get_all_by_filters(context, filters, sort_keys=None,
sort_dirs=None, limit=None, marker=None):
"""Finds all migrations using the provided filters."""
return IMPL.migration_get_all_by_filters(context, filters)
return IMPL.migration_get_all_by_filters(context, filters,
sort_keys=sort_keys,
sort_dirs=sort_dirs,
limit=limit, marker=marker)
def migration_get_in_progress_by_instance(context, instance_uuid,

View File

@ -4391,8 +4391,17 @@ def migration_get_in_progress_by_instance(context, instance_uuid,
@pick_context_manager_reader
def migration_get_all_by_filters(context, filters):
def migration_get_all_by_filters(context, filters,
sort_keys=None, sort_dirs=None,
limit=None, marker=None):
if limit == 0:
return []
query = model_query(context, models.Migration)
if 'changes-since' in filters:
changes_since = timeutils.normalize_time(filters['changes-since'])
query = query. \
filter(models.Migration.updated_at >= changes_since)
if "status" in filters:
status = filters["status"]
status = [status] if isinstance(status, six.string_types) else status
@ -4411,9 +4420,25 @@ def migration_get_all_by_filters(context, filters):
hidden = filters["hidden"]
query = query.filter(models.Migration.hidden == hidden)
if "instance_uuid" in filters:
uuid = filters["instance_uuid"]
query = query.filter(models.Migration.instance_uuid == uuid)
return query.all()
instance_uuid = filters["instance_uuid"]
query = query.filter(models.Migration.instance_uuid == instance_uuid)
if marker:
try:
marker = migration_get_by_uuid(context, marker)
except exception.MigrationNotFound:
raise exception.MarkerNotFound(marker=marker)
if limit or marker or sort_keys or sort_dirs:
# Default sort by desc(['created_at', 'id'])
sort_keys, sort_dirs = process_sort_params(sort_keys, sort_dirs,
default_dir='desc')
return sqlalchemyutils.paginate_query(query,
models.Migration,
limit=limit,
sort_keys=sort_keys,
marker=marker,
sort_dirs=sort_dirs).all()
else:
return query.all()
@pick_context_manager_writer

View File

@ -185,7 +185,9 @@ class MigrationList(base.ObjectListBase, base.NovaObject):
# Version 1.2: Migration version 1.2
# Version 1.3: Added a new function to get in progress migrations
# for an instance.
VERSION = '1.3'
# Version 1.4: Added sort_keys, sort_dirs, limit, marker kwargs to
# get_by_filters for migrations pagination support.
VERSION = '1.4'
fields = {
'objects': fields.ListOfObjectsField('Migration'),
@ -214,8 +216,11 @@ class MigrationList(base.ObjectListBase, base.NovaObject):
db_migrations)
@base.remotable_classmethod
def get_by_filters(cls, context, filters):
db_migrations = db.migration_get_all_by_filters(context, filters)
def get_by_filters(cls, context, filters, sort_keys=None, sort_dirs=None,
limit=None, marker=None):
db_migrations = db.migration_get_all_by_filters(
context, filters, sort_keys=sort_keys, sort_dirs=sort_dirs,
limit=limit, marker=marker)
return base.obj_make_list(context, cls(context), objects.Migration,
db_migrations)

View File

@ -1454,7 +1454,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):
system_metadata=None, migration_type=None, uuid=None,
created_at=None, updated_at=None):
values = {'host': source_compute}
instance = db.instance_create(self.ctxt, values)
@ -1466,6 +1467,10 @@ class MigrationTestCase(test.TestCase):
'source_node': source_node, 'dest_compute': dest_compute,
'dest_node': dest_node, 'instance_uuid': instance['uuid'],
'migration_type': migration_type, 'uuid': uuid}
if created_at:
values['created_at'] = created_at
if updated_at:
values['updated_at'] = updated_at
db.migration_create(self.ctxt, values)
return values
@ -1699,6 +1704,74 @@ class MigrationTestCase(test.TestCase):
db.migration_get_by_id_and_instance, self.ctxt,
'500', '501')
def _create_3_migration_after_time(self, time=None):
time = time or timeutils.utcnow()
tmp_time = time + datetime.timedelta(days=1)
after_1hour = datetime.timedelta(hours=1)
self._create(uuid=uuidsentinel.uuid_time1, created_at=tmp_time,
updated_at=tmp_time + after_1hour)
tmp_time = time + datetime.timedelta(days=2)
self._create(uuid=uuidsentinel.uuid_time2, created_at=tmp_time,
updated_at=tmp_time + after_1hour)
tmp_time = time + datetime.timedelta(days=3)
self._create(uuid=uuidsentinel.uuid_time3, created_at=tmp_time,
updated_at=tmp_time + after_1hour)
def test_get_migrations_by_filters_with_limit(self):
migrations = db.migration_get_all_by_filters(self.ctxt, {}, limit=3)
self.assertEqual(3, len(migrations))
def test_get_migrations_by_filters_with_limit_marker(self):
self._create_3_migration_after_time()
# order by created_at, desc: time3, time2, time1
migrations = db.migration_get_all_by_filters(
self.ctxt, {}, limit=2, marker=uuidsentinel.uuid_time3)
# time3 as marker: time2, time1
self.assertEqual(2, len(migrations))
self.assertEqual(migrations[0]['uuid'], uuidsentinel.uuid_time2)
self.assertEqual(migrations[1]['uuid'], uuidsentinel.uuid_time1)
# time3 as marker, limit 2: time3, time2
migrations = db.migration_get_all_by_filters(
self.ctxt, {}, limit=1, marker=uuidsentinel.uuid_time3)
self.assertEqual(1, len(migrations))
self.assertEqual(migrations[0]['uuid'], uuidsentinel.uuid_time2)
def test_get_migrations_by_filters_with_limit_marker_sort(self):
self._create_3_migration_after_time()
# order by created_at, desc: time3, time2, time1
migrations = db.migration_get_all_by_filters(
self.ctxt, {}, limit=2, marker=uuidsentinel.uuid_time3)
# time2, time1
self.assertEqual(2, len(migrations))
self.assertEqual(migrations[0]['uuid'], uuidsentinel.uuid_time2)
self.assertEqual(migrations[1]['uuid'], uuidsentinel.uuid_time1)
# order by updated_at, desc: time1, time2, time3
migrations = db.migration_get_all_by_filters(
self.ctxt, {}, sort_keys=['updated_at'], sort_dirs=['asc'],
limit=2, marker=uuidsentinel.uuid_time1)
# time2, time3
self.assertEqual(2, len(migrations))
self.assertEqual(migrations[0]['uuid'], uuidsentinel.uuid_time2)
self.assertEqual(migrations[1]['uuid'], uuidsentinel.uuid_time3)
def test_get_migrations_by_filters_with_not_found_marker(self):
self.assertRaises(exception.MarkerNotFound,
db.migration_get_all_by_filters, self.ctxt, {},
marker=uuidsentinel.not_found_marker)
def test_get_migrations_by_filters_with_changes_since(self):
changes_time = timeutils.utcnow(with_timezone=True)
self._create_3_migration_after_time(changes_time)
after_1day_2hours = datetime.timedelta(days=1, hours=2)
filters = {"changes-since": changes_time + after_1day_2hours}
migrations = db.migration_get_all_by_filters(
self.ctxt, filters,
sort_keys=['updated_at'], sort_dirs=['asc'])
self.assertEqual(2, len(migrations))
self.assertEqual(migrations[0]['uuid'], uuidsentinel.uuid_time2)
self.assertEqual(migrations[1]['uuid'], uuidsentinel.uuid_time3)
class ModelsObjectComparatorMixin(object):
def _dict_from_object(self, obj, ignored_keys):

View File

@ -215,7 +215,9 @@ class _TestMigrationObject(object):
self.assertEqual(2, len(migrations))
for index, db_migration in enumerate(db_migrations):
self.compare_obj(migrations[index], db_migration)
mock_get.assert_called_once_with(ctxt, filters)
mock_get.assert_called_once_with(ctxt, filters,
sort_dirs=None, sort_keys=None,
limit=None, marker=None)
def test_migrate_old_resize_record(self):
db_migration = dict(fake_db_migration(), migration_type=None)

View File

@ -1125,7 +1125,7 @@ object_data = {
'MemoryDiagnostics': '1.0-2c995ae0f2223bb0f8e523c5cc0b83da',
'Migration': '1.5-48bebaada664ee15bc23b35b2b814d75',
'MigrationContext': '1.1-9fb17b0b521370957a884636499df52d',
'MigrationList': '1.3-55595bfc1a299a5962614d0821a3567e',
'MigrationList': '1.4-983a9c29d4f1e747ce719dc9063b729b',
'MonitorMetric': '1.1-53b1db7c4ae2c531db79761e7acc52ba',
'MonitorMetricList': '1.1-15ecf022a68ddbb8c2a6739cfc9f8f5e',
'NicDiagnostics': '1.0-895e9ad50e0f56d5258585e3e066aea5',