Add support for forcing migrate_flavor_data
In some cases it might be helpful to force the migration of flavor
data. For example, when operating on a snapshot of a production
database there may be instances in an operating state that would
stop the migration from finishing. Blocking on this will make it
impossible to finish the migrations (once the migrations require
all the data has been migrated).
Change-Id: I4f6c0679b144897f2bfc2fa06bc7c72b25a85fd3
(cherry picked from commit 5f515dda64
)
This commit is contained in:
parent
ccd002b269
commit
e56aed8670
|
@ -960,7 +960,10 @@ class DbCommands(object):
|
|||
|
||||
@args('--max-number', metavar='<number>', dest='max_number',
|
||||
help='Maximum number of instances to consider')
|
||||
def migrate_flavor_data(self, max_number=None):
|
||||
@args('--force', action='store_true', dest='force',
|
||||
help='Force instances to migrate (even if they may be performing '
|
||||
'another operation). Warning, this is potentially dangerous.')
|
||||
def migrate_flavor_data(self, max_number=None, force=False):
|
||||
if max_number is not None:
|
||||
max_number = int(max_number)
|
||||
if max_number < 0:
|
||||
|
@ -969,7 +972,7 @@ class DbCommands(object):
|
|||
admin_context = context.get_admin_context()
|
||||
flavor_cache = {}
|
||||
match, done = db.migrate_flavor_data(admin_context, max_number,
|
||||
flavor_cache)
|
||||
flavor_cache, force)
|
||||
print(_('%(total)i instances matched query, %(done)i completed') %
|
||||
{'total': match, 'done': done})
|
||||
|
||||
|
|
|
@ -1952,17 +1952,19 @@ def archive_deleted_rows_for_table(context, tablename, max_rows=None):
|
|||
max_rows=max_rows)
|
||||
|
||||
|
||||
def migrate_flavor_data(context, max_count, flavor_cache):
|
||||
def migrate_flavor_data(context, max_count, flavor_cache, force=False):
|
||||
"""Migrate instance flavor data from system_metadata to instance_extra.
|
||||
|
||||
:param max_count: The maximum number of instances to consider in this
|
||||
run.
|
||||
:param flavor_cache: A dict to persist flavor information in across
|
||||
calls (just pass an empty dict here)
|
||||
:param force: Boolean whether or not to force migration of instances that
|
||||
are performing another operation.
|
||||
:returns: number of instances needing migration, number of instances
|
||||
migrated (both will always be less than max_count)
|
||||
"""
|
||||
return IMPL.migrate_flavor_data(context, max_count, flavor_cache)
|
||||
return IMPL.migrate_flavor_data(context, max_count, flavor_cache, force)
|
||||
|
||||
|
||||
####################
|
||||
|
|
|
@ -6066,7 +6066,7 @@ def _load_missing_flavor(instance, flavor_cache):
|
|||
|
||||
|
||||
@require_admin_context
|
||||
def migrate_flavor_data(context, max_count, flavor_cache):
|
||||
def migrate_flavor_data(context, max_count, flavor_cache, force=False):
|
||||
# NOTE(danms): This is only ever run in nova-manage, and we need to avoid
|
||||
# a circular import
|
||||
from nova import objects
|
||||
|
@ -6093,10 +6093,11 @@ def migrate_flavor_data(context, max_count, flavor_cache):
|
|||
# middle of some other operation. This is just a guess and not
|
||||
# a lock. There is still a race here, although it's the same
|
||||
# race as the normal code, since we use expected_task_state below.
|
||||
if instance.task_state is not None:
|
||||
continue
|
||||
if instance.vm_state in [vm_states.RESCUED, vm_states.RESIZED]:
|
||||
continue
|
||||
if not force:
|
||||
if instance.task_state is not None:
|
||||
continue
|
||||
if instance.vm_state in [vm_states.RESCUED, vm_states.RESIZED]:
|
||||
continue
|
||||
|
||||
# NOTE(danms): If we have a really old instance with no flavor
|
||||
# information at all, flavor will not have been set during load.
|
||||
|
@ -6120,7 +6121,8 @@ def migrate_flavor_data(context, max_count, flavor_cache):
|
|||
{'instance_uuid': db_instance['uuid']})
|
||||
LOG.debug(
|
||||
'Created instance_extra for %s' % db_instance['uuid'])
|
||||
instance.save(expected_task_state=[None])
|
||||
instance.save(expected_task_state=[instance.task_state],
|
||||
expected_vm_state=[instance.vm_state])
|
||||
count_hit += 1
|
||||
|
||||
return count_all, count_hit
|
||||
|
|
|
@ -7620,7 +7620,7 @@ class FlavorMigrationTestCase(test.TestCase):
|
|||
self.assertEqual(0, match)
|
||||
self.assertEqual(0, done)
|
||||
|
||||
def test_migrate_flavor_honors_states(self):
|
||||
def _test_migrate_flavor_states(self, force=False):
|
||||
ctxt = context.get_admin_context()
|
||||
flavor = flavors.get_default_flavor()
|
||||
sysmeta = flavors.save_flavor_info({}, flavor)
|
||||
|
@ -7648,13 +7648,28 @@ class FlavorMigrationTestCase(test.TestCase):
|
|||
}
|
||||
db.instance_create(ctxt, values)
|
||||
|
||||
match, done = db.migrate_flavor_data(ctxt, None, {})
|
||||
self.assertEqual(4, match)
|
||||
self.assertEqual(1, done)
|
||||
match, done = db.migrate_flavor_data(ctxt, None, {}, force)
|
||||
|
||||
match, done = db.migrate_flavor_data(ctxt, None, {})
|
||||
self.assertEqual(3, match)
|
||||
self.assertEqual(0, done)
|
||||
if not force:
|
||||
self.assertEqual(4, match)
|
||||
self.assertEqual(1, done)
|
||||
|
||||
match, done = db.migrate_flavor_data(ctxt, None, {}, force)
|
||||
self.assertEqual(3, match)
|
||||
self.assertEqual(0, done)
|
||||
else:
|
||||
self.assertEqual(4, match)
|
||||
self.assertEqual(4, done)
|
||||
|
||||
match, done = db.migrate_flavor_data(ctxt, None, {}, force)
|
||||
self.assertEqual(0, match)
|
||||
self.assertEqual(0, done)
|
||||
|
||||
def test_migrate_flavor_honors_states(self):
|
||||
self._test_migrate_flavor_states(force=False)
|
||||
|
||||
def test_migrate_flavor_force_states(self):
|
||||
self._test_migrate_flavor_states(force=True)
|
||||
|
||||
def test_migrate_flavor_gets_missing_extra_rows(self):
|
||||
ctxt = context.get_admin_context()
|
||||
|
|
Loading…
Reference in New Issue