Add support for clean_shutdown to resize in compute api layer

Change Iec3dfd17725440958aac395ebc471e51afd6522e added
clean_shutdown support to the compute manager in Juno.
This change is part of a set to extend that to the compute
API layer, which will in turn allow us to extend the REST API
to provide a forced-shutdown option to those operations.

Change-Id: Ie93c3c4aa30b16e277628e1a741e46a37b794a75
Partially-Implements: blueprint user-defined-shutdown
This commit is contained in:
Phil Day 2014-12-19 23:06:07 +00:00
parent 410038a83f
commit a888996bac
15 changed files with 156 additions and 71 deletions

View File

@ -74,7 +74,7 @@ class CellsManager(manager.Manager):
Scheduling requests get passed to the scheduler class.
"""
target = oslo_messaging.Target(version='1.32')
target = oslo_messaging.Target(version='1.33')
def __init__(self, *args, **kwargs):
LOG.warning(_LW('The cells feature of Nova is considered experimental '
@ -523,10 +523,12 @@ class CellsManager(manager.Manager):
self.msg_runner.soft_delete_instance(ctxt, instance)
def resize_instance(self, ctxt, instance, flavor,
extra_instance_updates):
extra_instance_updates,
clean_shutdown=True):
"""Resize an instance in its cell."""
self.msg_runner.resize_instance(ctxt, instance,
flavor, extra_instance_updates)
flavor, extra_instance_updates,
clean_shutdown=clean_shutdown)
def live_migrate_instance(self, ctxt, instance, block_migration,
disk_over_commit, host_name):

View File

@ -894,10 +894,11 @@ class _TargetedMessageMethods(_BaseMessageMethods):
self._call_compute_api_with_obj(message.ctxt, instance, 'unpause')
def resize_instance(self, message, instance, flavor,
extra_instance_updates):
extra_instance_updates, clean_shutdown=True):
"""Resize an instance via compute_api.resize()."""
self._call_compute_api_with_obj(message.ctxt, instance, 'resize',
flavor_id=flavor['flavorid'],
clean_shutdown=clean_shutdown,
**extra_instance_updates)
def live_migrate_instance(self, message, instance, block_migration,
@ -1765,12 +1766,14 @@ class MessageRunner(object):
self._instance_action(ctxt, instance, 'unpause_instance')
def resize_instance(self, ctxt, instance, flavor,
extra_instance_updates):
extra_instance_updates,
clean_shutdown=True):
"""Resize an instance in its cell."""
extra_kwargs = dict(flavor=flavor,
extra_instance_updates=extra_instance_updates)
self._instance_action(ctxt, instance, 'resize_instance',
extra_kwargs=extra_kwargs)
extra_kwargs=extra_kwargs,
clean_shutdown=clean_shutdown)
def live_migrate_instance(self, ctxt, instance, block_migration,
disk_over_commit, host_name):

View File

@ -105,6 +105,7 @@ class CellsAPI(object):
* 1.30 - Make build_instances() use flavor object
* 1.31 - Add clean_shutdown to stop, resize, rescue, and shelve
* 1.32 - Send objects for instances in build_instances()
* 1.33 - Add clean_shutdown to resize_instance()
'''
VERSION_ALIASES = {
@ -551,14 +552,22 @@ class CellsAPI(object):
cctxt.cast(ctxt, 'soft_delete_instance', instance=instance)
def resize_instance(self, ctxt, instance, extra_instance_updates,
scheduler_hint, flavor, reservations):
scheduler_hint, flavor, reservations,
clean_shutdown=True):
if not CONF.cells.enable:
return
flavor_p = jsonutils.to_primitive(flavor)
cctxt = self.client.prepare(version='1.20')
cctxt.cast(ctxt, 'resize_instance',
instance=instance, flavor=flavor_p,
extra_instance_updates=extra_instance_updates)
version = '1.33'
msg_args = {'instance': instance,
'flavor': flavor_p,
'extra_instance_updates': extra_instance_updates,
'clean_shutdown': clean_shutdown}
if not self.client.can_send_version(version):
del msg_args['clean_shutdown']
version = '1.20'
cctxt = self.client.prepare(version=version)
cctxt.cast(ctxt, 'resize_instance', **msg_args)
def live_migrate_instance(self, ctxt, instance, host_name,
block_migration, disk_over_commit):

View File

@ -2537,7 +2537,7 @@ class API(base.Base):
@check_instance_lock
@check_instance_cell
@check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.STOPPED])
def resize(self, context, instance, flavor_id=None,
def resize(self, context, instance, flavor_id=None, clean_shutdown=True,
**extra_instance_updates):
"""Resize (ie, migrate) a running instance.
@ -2638,7 +2638,8 @@ class API(base.Base):
self.compute_task_api.resize_instance(context, instance,
extra_instance_updates, scheduler_hint=scheduler_hint,
flavor=new_instance_type,
reservations=quotas.reservations or [])
reservations=quotas.reservations or [],
clean_shutdown=clean_shutdown)
@wrap_check_policy
@check_instance_lock

View File

@ -588,7 +588,7 @@ class ComputeVirtAPI(virtapi.VirtAPI):
class ComputeManager(manager.Manager):
"""Manages the running instances from creation to destruction."""
target = messaging.Target(version='3.37')
target = messaging.Target(version='3.38')
# How long to wait in seconds before re-issuing a shutdown
# signal to a instance during power off. The overall
@ -3642,7 +3642,8 @@ class ComputeManager(manager.Manager):
quotas.commit()
def _prep_resize(self, context, image, instance, instance_type,
quotas, request_spec, filter_properties, node):
quotas, request_spec, filter_properties, node,
clean_shutdown=True):
if not filter_properties:
filter_properties = {}
@ -3675,14 +3676,16 @@ class ComputeManager(manager.Manager):
LOG.audit(_('Migrating'), context=context, instance=instance)
self.compute_rpcapi.resize_instance(
context, instance, claim.migration, image,
instance_type, quotas.reservations)
instance_type, quotas.reservations,
clean_shutdown)
@wrap_exception()
@reverts_task_state
@wrap_instance_event
@wrap_instance_fault
def prep_resize(self, context, image, instance, instance_type,
reservations, request_spec, filter_properties, node):
reservations, request_spec, filter_properties, node,
clean_shutdown=True):
"""Initiates the process of moving a running instance to another host.
Possibly changes the RAM and disk size in the process.
@ -3706,7 +3709,7 @@ class ComputeManager(manager.Manager):
self._prep_resize(context, image, instance,
instance_type, quotas,
request_spec, filter_properties,
node)
node, clean_shutdown)
# NOTE(dgenin): This is thrown in LibvirtDriver when the
# instance to be migrated is backed by LVM.
# Remove when LVM migration is implemented.

View File

@ -278,6 +278,7 @@ class ComputeAPI(object):
* 3.36 - Make build_and_run_instance() send a Flavor object
* 3.37 - Add clean_shutdown to stop, resize, rescue, shelve, and
shelve_offload
* 3.38 - Add clean_shutdown to prep_resize
'''
VERSION_ALIASES = {
@ -561,18 +562,24 @@ class ComputeAPI(object):
def prep_resize(self, ctxt, image, instance, instance_type, host,
reservations=None, request_spec=None,
filter_properties=None, node=None):
version = '3.0'
filter_properties=None, node=None,
clean_shutdown=True):
instance_type_p = jsonutils.to_primitive(instance_type)
image_p = jsonutils.to_primitive(image)
msg_args = {'instance': instance,
'instance_type': instance_type_p,
'image': image_p,
'reservations': reservations,
'request_spec': request_spec,
'filter_properties': filter_properties,
'node': node,
'clean_shutdown': clean_shutdown}
version = '3.38'
if not self.client.can_send_version(version):
del msg_args['clean_shutdown']
version = '3.0'
cctxt = self.client.prepare(server=host, version=version)
cctxt.cast(ctxt, 'prep_resize',
instance=instance,
instance_type=instance_type_p,
image=image_p, reservations=reservations,
request_spec=request_spec,
filter_properties=filter_properties,
node=node)
cctxt.cast(ctxt, 'prep_resize', **msg_args)
def reboot_instance(self, ctxt, instance, block_device_info,
reboot_type):

View File

@ -225,13 +225,15 @@ class LocalComputeTaskAPI(object):
manager.ComputeTaskManager())
def resize_instance(self, context, instance, extra_instance_updates,
scheduler_hint, flavor, reservations):
scheduler_hint, flavor, reservations,
clean_shutdown=True):
# NOTE(comstud): 'extra_instance_updates' is not used here but is
# needed for compatibility with the cells_rpcapi version of this
# method.
self._manager.migrate_server(
context, instance, scheduler_hint, False, False, flavor,
None, None, reservations)
context, instance, scheduler_hint, live=False, rebuild=False,
flavor=flavor, block_migration=None, disk_over_commit=None,
reservations=reservations, clean_shutdown=clean_shutdown)
def live_migrate_instance(self, context, instance, host_name,
block_migration, disk_over_commit):
@ -336,13 +338,15 @@ class ComputeTaskAPI(object):
self.conductor_compute_rpcapi = rpcapi.ComputeTaskAPI()
def resize_instance(self, context, instance, extra_instance_updates,
scheduler_hint, flavor, reservations):
scheduler_hint, flavor, reservations,
clean_shutdown=True):
# NOTE(comstud): 'extra_instance_updates' is not used here but is
# needed for compatibility with the cells_rpcapi version of this
# method.
self.conductor_compute_rpcapi.migrate_server(
context, instance, scheduler_hint, False, False, flavor,
None, None, reservations)
context, instance, scheduler_hint, live=False, rebuild=False,
flavor=flavor, block_migration=None, disk_over_commit=None,
reservations=reservations, clean_shutdown=clean_shutdown)
def live_migrate_instance(self, context, instance, host_name,
block_migration, disk_over_commit):

View File

@ -461,7 +461,7 @@ class ComputeTaskManager(base.Base):
may involve coordinating activities on multiple compute nodes.
"""
target = messaging.Target(namespace='compute_task', version='1.10')
target = messaging.Target(namespace='compute_task', version='1.11')
def __init__(self):
super(ComputeTaskManager, self).__init__()
@ -482,7 +482,8 @@ class ComputeTaskManager(base.Base):
exception.MigrationPreCheckError,
exception.LiveMigrationWithOldNovaNotSafe)
def migrate_server(self, context, instance, scheduler_hint, live, rebuild,
flavor, block_migration, disk_over_commit, reservations=None):
flavor, block_migration, disk_over_commit, reservations=None,
clean_shutdown=True):
if instance and not isinstance(instance, nova_object.NovaObject):
# NOTE(danms): Until v2 of the RPC API, we need to tolerate
# old-world instance objects here
@ -505,12 +506,12 @@ class ComputeTaskManager(base.Base):
instance_uuid):
self._cold_migrate(context, instance, flavor,
scheduler_hint['filter_properties'],
reservations)
reservations, clean_shutdown)
else:
raise NotImplementedError()
def _cold_migrate(self, context, instance, flavor, filter_properties,
reservations):
reservations, clean_shutdown):
image_ref = instance.image_ref
image = compute_utils.get_image_metadata(
context, self.image_api, image_ref, instance)
@ -555,7 +556,8 @@ class ComputeTaskManager(base.Base):
context, image, instance,
flavor, host,
reservations, request_spec=request_spec,
filter_properties=filter_properties, node=node)
filter_properties=filter_properties, node=node,
clean_shutdown=clean_shutdown)
except Exception as ex:
with excutils.save_and_reraise_exception():
updates = {'vm_state': instance['vm_state'],

View File

@ -391,6 +391,7 @@ class ComputeTaskAPI(object):
1.8 - Add rebuild_instance
1.9 - Converted requested_networks to NetworkRequestList object
1.10 - Made migrate_server() and build_instances() send flavor objects
1.11 - Added clean_shutdown to migrate_server()
"""
@ -404,22 +405,26 @@ class ComputeTaskAPI(object):
def migrate_server(self, context, instance, scheduler_hint, live, rebuild,
flavor, block_migration, disk_over_commit,
reservations=None):
version = '1.10'
reservations=None, clean_shutdown=True):
kw = {'instance': instance, 'scheduler_hint': scheduler_hint,
'live': live, 'rebuild': rebuild, 'flavor': flavor,
'block_migration': block_migration,
'disk_over_commit': disk_over_commit,
'reservations': reservations,
'clean_shutdown': clean_shutdown}
version = '1.11'
if not self.client.can_send_version(version):
flavor = objects_base.obj_to_primitive(flavor)
del kw['clean_shutdown']
version = '1.10'
if not self.client.can_send_version(version):
kw['flavor'] = objects_base.obj_to_primitive(flavor)
version = '1.6'
if not self.client.can_send_version(version):
instance = jsonutils.to_primitive(
kw['instance'] = jsonutils.to_primitive(
objects_base.obj_to_primitive(instance))
version = '1.4'
cctxt = self.client.prepare(version=version)
return cctxt.call(context, 'migrate_server',
instance=instance, scheduler_hint=scheduler_hint,
live=live, rebuild=rebuild, flavor=flavor,
block_migration=block_migration,
disk_over_commit=disk_over_commit,
reservations=reservations)
return cctxt.call(context, 'migrate_server', **kw)
def build_instances(self, context, instances, image, filter_properties,
admin_password, injected_files, requested_networks,

View File

@ -758,14 +758,22 @@ class CellsManagerClassTestCase(test.NoDBTestCase):
self.cells_manager.soft_delete_instance(self.ctxt,
instance='fake-instance')
def test_resize_instance(self):
def _test_resize_instance(self, clean_shutdown=True):
self.mox.StubOutWithMock(self.msg_runner, 'resize_instance')
self.msg_runner.resize_instance(self.ctxt, 'fake-instance',
'fake-flavor', 'fake-updates')
'fake-flavor', 'fake-updates',
clean_shutdown=clean_shutdown)
self.mox.ReplayAll()
self.cells_manager.resize_instance(
self.ctxt, instance='fake-instance', flavor='fake-flavor',
extra_instance_updates='fake-updates')
extra_instance_updates='fake-updates',
clean_shutdown=clean_shutdown)
def test_resize_instance(self):
self._test_resize_instance()
def test_resize_instance_forced_shutdown(self):
self._test_resize_instance(clean_shutdown=False)
def test_live_migrate_instance(self):
self.mox.StubOutWithMock(self.msg_runner, 'live_migrate_instance')

View File

@ -1309,14 +1309,22 @@ class CellsTargetedMethodsTestCase(test.TestCase):
def test_unpause_instance(self):
self._test_instance_action_method('unpause', (), {}, (), {}, False)
def test_resize_instance(self):
def _test_resize_instance(self, clean_shutdown=True):
kwargs = dict(flavor=dict(id=42, flavorid='orangemocchafrappuccino'),
extra_instance_updates=dict(cow='moo'))
expected_kwargs = dict(flavor_id='orangemocchafrappuccino', cow='moo')
extra_instance_updates=dict(cow='moo'),
clean_shutdown=clean_shutdown)
expected_kwargs = dict(flavor_id='orangemocchafrappuccino', cow='moo',
clean_shutdown=clean_shutdown)
self._test_instance_action_method('resize', (), kwargs,
(), expected_kwargs,
False)
def test_resize_instance(self):
self._test_resize_instance()
def test_resize_instance_forced_shutdown(self):
self._test_resize_instance(clean_shutdown=False)
def test_live_migrate_instance(self):
kwargs = dict(block_migration='fake-block-mig',
disk_over_commit='fake-commit',

View File

@ -666,12 +666,14 @@ class CellsAPITestCase(test.NoDBTestCase):
dict(cow='moo'),
'fake-hint',
'fake-flavor',
'fake-reservations')
'fake-reservations',
clean_shutdown=True)
expected_args = {'instance': 'fake-instance',
'flavor': 'fake-flavor',
'extra_instance_updates': dict(cow='moo')}
'extra_instance_updates': dict(cow='moo'),
'clean_shutdown': True}
self._check_result(call_info, 'resize_instance',
expected_args, version='1.20')
expected_args, version='1.33')
def test_live_migrate_instance(self):
call_info = self._stub_rpc_method('cast', None)

View File

@ -1379,7 +1379,8 @@ class _ComputeAPIUnitTestMixIn(object):
allow_mig_same_host=False,
project_id=None,
extra_kwargs=None,
same_flavor=False):
same_flavor=False,
clean_shutdown=True):
if extra_kwargs is None:
extra_kwargs = {}
@ -1482,16 +1483,20 @@ class _ComputeAPIUnitTestMixIn(object):
self.context, fake_inst, extra_kwargs,
scheduler_hint=scheduler_hint,
flavor=mox.IsA(objects.Flavor),
reservations=expected_reservations)
reservations=expected_reservations,
clean_shutdown=clean_shutdown)
self.mox.ReplayAll()
if flavor_id_passed:
self.compute_api.resize(self.context, fake_inst,
flavor_id='new-flavor-id',
clean_shutdown=clean_shutdown,
**extra_kwargs)
else:
self.compute_api.resize(self.context, fake_inst, **extra_kwargs)
self.compute_api.resize(self.context, fake_inst,
clean_shutdown=clean_shutdown,
**extra_kwargs)
def _test_migrate(self, *args, **kwargs):
self._test_resize(*args, flavor_id_passed=False, **kwargs)
@ -1511,6 +1516,9 @@ class _ComputeAPIUnitTestMixIn(object):
def test_resize_different_project_id(self):
self._test_resize(project_id='different')
def test_resize_forced_shutdown(self):
self._test_resize(clean_shutdown=False)
def test_migrate(self):
self._test_migrate()

View File

@ -281,13 +281,22 @@ class ComputeRpcAPITestCase(test.TestCase):
migrate_data=None, version='3.19')
def test_prep_resize(self):
self.flags(compute='3.0', group='upgrade_levels')
self._test_compute_api('prep_resize', 'cast',
instance=self.fake_instance_obj, instance_type='fake_type',
image='fake_image', host='host',
reservations=list('fake_res'),
request_spec='fake_spec',
filter_properties={'fakeprop': 'fakeval'},
node='node')
node='node', version='3.0')
self.flags(compute='3.38', group='upgrade_levels')
self._test_compute_api('prep_resize', 'cast',
instance=self.fake_instance_obj, instance_type='fake_type',
image='fake_image', host='host',
reservations=list('fake_res'),
request_spec='fake_spec',
filter_properties={'fakeprop': 'fakeval'},
node='node', clean_shutdown=True, version='3.38')
def test_reboot_instance(self):
self.maxDiff = None

View File

@ -1185,7 +1185,7 @@ class _BaseTaskTestCase(object):
{'host': 'destination'}, True, False, None,
'block_migration', 'disk_over_commit')
def test_cold_migrate(self):
def _test_cold_migrate(self, clean_shutdown=True):
self.mox.StubOutWithMock(compute_utils, 'get_image_metadata')
self.mox.StubOutWithMock(scheduler_utils, 'build_request_spec')
self.mox.StubOutWithMock(scheduler_utils, 'setup_instance_group')
@ -1223,7 +1223,8 @@ class _BaseTaskTestCase(object):
self.conductor_manager.compute_rpcapi.prep_resize(
self.context, 'image', mox.IsA(objects.Instance),
mox.IsA(objects.Flavor), 'host1', [], request_spec=request_spec,
filter_properties=filter_properties, node=None)
filter_properties=filter_properties, node=None,
clean_shutdown=clean_shutdown)
self.mox.ReplayAll()
@ -1234,11 +1235,19 @@ class _BaseTaskTestCase(object):
# The API method is actually 'resize_instance'. It gets
# converted into 'migrate_server' when doing RPC.
self.conductor.resize_instance(
self.context, inst_obj, {}, scheduler_hint, flavor, [])
self.context, inst_obj, {}, scheduler_hint, flavor, [],
clean_shutdown)
else:
self.conductor.migrate_server(
self.context, inst_obj, scheduler_hint,
False, False, flavor, None, None, [])
False, False, flavor, None, None, [],
clean_shutdown)
def test_cold_migrate(self):
self._test_cold_migrate()
def test_cold_migrate_forced_shutdown(self):
self._test_cold_migrate(clean_shutdown=False)
def test_build_instances(self):
system_metadata = flavors.save_flavor_info({},
@ -1831,7 +1840,8 @@ class ConductorTaskTestCase(_BaseTaskTestCase, test_compute.BaseTestCase):
self.assertRaises(exc.NoValidHost,
self.conductor._cold_migrate,
self.context, inst_obj,
flavor, filter_props, [resvs])
flavor, filter_props, [resvs],
clean_shutdown=True)
def test_cold_migrate_no_valid_host_back_in_stopped_state(self):
flavor = flavors.get_flavor_by_name('m1.tiny')
@ -1891,7 +1901,8 @@ class ConductorTaskTestCase(_BaseTaskTestCase, test_compute.BaseTestCase):
self.assertRaises(exc.NoValidHost,
self.conductor._cold_migrate, self.context,
inst_obj, flavor, filter_props, [resvs])
inst_obj, flavor, filter_props, [resvs],
clean_shutdown=True)
def test_cold_migrate_no_valid_host_error_msg(self):
flavor = flavors.get_flavor_by_name('m1.tiny')
@ -1920,7 +1931,8 @@ class ConductorTaskTestCase(_BaseTaskTestCase, test_compute.BaseTestCase):
) as (image_mock, brs_mock, sig_mock, select_dest_mock):
nvh = self.assertRaises(exc.NoValidHost,
self.conductor._cold_migrate, self.context,
inst_obj, flavor, filter_props, [resvs])
inst_obj, flavor, filter_props, [resvs],
clean_shutdown=True)
self.assertIn('cold migrate', nvh.message)
def test_cold_migrate_exception_host_in_error_state_and_raise(self):
@ -1979,7 +1991,8 @@ class ConductorTaskTestCase(_BaseTaskTestCase, test_compute.BaseTestCase):
'flavor', hosts[0]['host'], [resvs],
request_spec=request_spec,
filter_properties=expected_filter_props,
node=hosts[0]['nodename']).AndRaise(exc_info)
node=hosts[0]['nodename'],
clean_shutdown=True).AndRaise(exc_info)
updates = {'vm_state': vm_states.STOPPED,
'task_state': None}
@ -2000,7 +2013,8 @@ class ConductorTaskTestCase(_BaseTaskTestCase, test_compute.BaseTestCase):
self.assertRaises(test.TestingException,
self.conductor._cold_migrate,
self.context, inst_obj, 'flavor',
filter_props, [resvs])
filter_props, [resvs],
clean_shutdown=True)
def test_resize_no_valid_host_error_msg(self):
flavor = flavors.get_flavor_by_name('m1.tiny')
@ -2031,7 +2045,7 @@ class ConductorTaskTestCase(_BaseTaskTestCase, test_compute.BaseTestCase):
nvh = self.assertRaises(exc.NoValidHost,
self.conductor._cold_migrate, self.context,
inst_obj, flavor_new, filter_props,
[resvs])
[resvs], clean_shutdown=True)
self.assertIn('resize', nvh.message)
def test_build_instances_instance_not_found(self):