Cold migrate using the RequestSpec object

Cold migrate and resize can now get the Spec object and pass it
down the wire to the conductor so that it could use it for the
move.

Partially-Implements: blueprint check-destination-on-migrations-newton

Co-Authored-By: Pawel Koniszewski <pawel.koniszewski@intel.com>
Change-Id: Ic3968721d257a167f3f946e5387cd227a7eeec6c
This commit is contained in:
Sylvain Bauza 2016-03-29 12:02:07 +02:00
parent 09f2d4d5ec
commit 76dfb4ba9f
10 changed files with 227 additions and 162 deletions

View File

@ -558,7 +558,12 @@ class CellsAPI(object):
def resize_instance(self, ctxt, instance, extra_instance_updates,
scheduler_hint, flavor, reservations,
clean_shutdown=True):
clean_shutdown=True,
request_spec=None):
# NOTE(sbauza): Since Cells v1 is quite feature-frozen, we don't want
# to pass down request_spec to the manager and rather keep the
# cell conductor providing a new RequestSpec like the original
# behaviour
flavor_p = jsonutils.to_primitive(flavor)
version = '1.33'
msg_args = {'instance': instance,

View File

@ -2729,12 +2729,26 @@ class API(base.Base):
self._record_action_start(context, instance,
instance_actions.RESIZE)
# NOTE(sbauza): The migration script we provided in Newton should make
# sure that all our instances are currently migrated to have an
# attached RequestSpec object but let's consider that the operator only
# half migrated all their instances in the meantime.
try:
request_spec = objects.RequestSpec.get_by_instance_uuid(
context, instance.uuid)
request_spec.ignore_hosts = filter_properties['ignore_hosts']
except exception.RequestSpecNotFound:
# Some old instances can still have no RequestSpec object attached
# to them, we need to support the old way
request_spec = None
scheduler_hint = {'filter_properties': filter_properties}
self.compute_task_api.resize_instance(context, instance,
extra_instance_updates, scheduler_hint=scheduler_hint,
flavor=new_instance_type,
reservations=quotas.reservations or [],
clean_shutdown=clean_shutdown)
clean_shutdown=clean_shutdown,
request_spec=request_spec)
@wrap_check_policy
@check_instance_lock

View File

@ -65,14 +65,15 @@ class LocalComputeTaskAPI(object):
def resize_instance(self, context, instance, extra_instance_updates,
scheduler_hint, flavor, reservations,
clean_shutdown=True):
clean_shutdown=True, request_spec=None):
# 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, live=False, rebuild=False,
flavor=flavor, block_migration=None, disk_over_commit=None,
reservations=reservations, clean_shutdown=clean_shutdown)
reservations=reservations, clean_shutdown=clean_shutdown,
request_spec=request_spec)
def live_migrate_instance(self, context, instance, host_name,
block_migration, disk_over_commit,
@ -176,14 +177,15 @@ class ComputeTaskAPI(object):
def resize_instance(self, context, instance, extra_instance_updates,
scheduler_hint, flavor, reservations,
clean_shutdown=True):
clean_shutdown=True, request_spec=None):
# 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, live=False, rebuild=False,
flavor=flavor, block_migration=None, disk_over_commit=None,
reservations=reservations, clean_shutdown=clean_shutdown)
reservations=reservations, clean_shutdown=clean_shutdown,
request_spec=request_spec)
def live_migrate_instance(self, context, instance, host_name,
block_migration, disk_over_commit,

View File

@ -200,20 +200,30 @@ class ComputeTaskManager(base.Base):
instance_uuid):
self._cold_migrate(context, instance, flavor,
scheduler_hint['filter_properties'],
reservations, clean_shutdown)
reservations, clean_shutdown, request_spec)
else:
raise NotImplementedError()
def _cold_migrate(self, context, instance, flavor, filter_properties,
reservations, clean_shutdown):
reservations, clean_shutdown, request_spec):
image = utils.get_image_from_system_metadata(
instance.system_metadata)
request_spec = scheduler_utils.build_request_spec(
context, image, [instance], instance_type=flavor)
# NOTE(sbauza): If a reschedule occurs when prep_resize(), then
# it only provides filter_properties legacy dict back to the
# conductor with no RequestSpec part of the payload.
if not request_spec:
request_spec = objects.RequestSpec.from_components(
context, instance.uuid, image,
instance.flavor, instance.numa_topology, instance.pci_requests,
filter_properties, None, instance.availability_zone)
task = self._build_cold_migrate_task(context, instance, flavor,
filter_properties, request_spec,
request_spec,
reservations, clean_shutdown)
# TODO(sbauza): Provide directly the RequestSpec object once
# _set_vm_state_and_notify() accepts it
legacy_spec = request_spec.to_legacy_request_spec_dict()
try:
task.execute()
except exception.NoValidHost as ex:
@ -223,7 +233,7 @@ class ComputeTaskManager(base.Base):
updates = {'vm_state': vm_state, 'task_state': None}
self._set_vm_state_and_notify(context, instance.uuid,
'migrate_server',
updates, ex, request_spec)
updates, ex, legacy_spec)
# if the flavor IDs match, it's migrate; otherwise resize
if flavor.id == instance.instance_type_id:
@ -239,14 +249,14 @@ class ComputeTaskManager(base.Base):
updates = {'vm_state': vm_state, 'task_state': None}
self._set_vm_state_and_notify(context, instance.uuid,
'migrate_server',
updates, ex, request_spec)
updates, ex, legacy_spec)
except Exception as ex:
with excutils.save_and_reraise_exception():
updates = {'vm_state': instance.vm_state,
'task_state': None}
self._set_vm_state_and_notify(context, instance.uuid,
'migrate_server',
updates, ex, request_spec)
updates, ex, legacy_spec)
def _set_vm_state_and_notify(self, context, instance_uuid, method, updates,
ex, request_spec):
@ -351,10 +361,10 @@ class ComputeTaskManager(base.Base):
request_spec)
def _build_cold_migrate_task(self, context, instance, flavor,
filter_properties, request_spec, reservations,
request_spec, reservations,
clean_shutdown):
return migrate.MigrationTask(context, instance, flavor,
filter_properties, request_spec,
request_spec,
reservations, clean_shutdown,
self.compute_rpcapi,
self.scheduler_client)

View File

@ -10,20 +10,21 @@
# License for the specific language governing permissions and limitations
# under the License.
from oslo_serialization import jsonutils
from nova.conductor.tasks import base
from nova import objects
from nova.scheduler import utils as scheduler_utils
class MigrationTask(base.TaskBase):
def __init__(self, context, instance, flavor, filter_properties,
def __init__(self, context, instance, flavor,
request_spec, reservations, clean_shutdown, compute_rpcapi,
scheduler_client):
super(MigrationTask, self).__init__(context, instance)
self.clean_shutdown = clean_shutdown
self.request_spec = request_spec
self.reservations = reservations
self.filter_properties = filter_properties
self.flavor = flavor
self.quotas = None
@ -31,33 +32,52 @@ class MigrationTask(base.TaskBase):
self.scheduler_client = scheduler_client
def _execute(self):
image = self.request_spec.get('image')
image = self.request_spec.image
self.quotas = objects.Quotas.from_reservations(self.context,
self.reservations,
instance=self.instance)
scheduler_utils.setup_instance_group(self.context, self.request_spec,
self.filter_properties)
scheduler_utils.populate_retry(self.filter_properties,
# TODO(sbauza): Remove that once prep_resize() accepts a RequestSpec
# object in the signature and all the scheduler.utils methods too
legacy_spec = self.request_spec.to_legacy_request_spec_dict()
legacy_props = self.request_spec.to_legacy_filter_properties_dict()
scheduler_utils.setup_instance_group(self.context, legacy_spec,
legacy_props)
scheduler_utils.populate_retry(legacy_props,
self.instance.uuid)
# TODO(sbauza): Hydrate here the object until we modify the
# scheduler.utils methods to directly use the RequestSpec object
spec_obj = objects.RequestSpec.from_primitives(
self.context, self.request_spec, self.filter_properties)
# TODO(sbauza): Remove that RequestSpec rehydratation once
# scheduler.utils methods use directly the NovaObject.
self.request_spec = objects.RequestSpec.from_components(
self.context, self.instance.uuid, image,
self.instance.flavor, self.instance.numa_topology,
self.instance.pci_requests, legacy_props, None,
self.instance.availability_zone)
# NOTE(sbauza): Force_hosts/nodes needs to be reset
# if we want to make sure that the next destination
# is not forced to be the original host
self.request_spec.reset_forced_destinations()
hosts = self.scheduler_client.select_destinations(
self.context, spec_obj)
self.context, self.request_spec)
host_state = hosts[0]
scheduler_utils.populate_filter_properties(self.filter_properties,
scheduler_utils.populate_filter_properties(legacy_props,
host_state)
# context is not serializable
self.filter_properties.pop('context', None)
legacy_props.pop('context', None)
(host, node) = (host_state['host'], host_state['nodename'])
# FIXME(sbauza): Serialize/Unserialize the legacy dict because of
# oslo.messaging #1529084 to transform datetime values into strings.
# tl;dr: datetimes in dicts are not accepted as correct values by the
# rpc fake driver.
legacy_spec = jsonutils.loads(jsonutils.dumps(legacy_spec))
self.compute_rpcapi.prep_resize(
self.context, image, self.instance, self.flavor, host,
self.reservations, request_spec=self.request_spec,
filter_properties=self.filter_properties, node=node,
clean_shutdown=self.clean_shutdown)
self.context, legacy_spec['image'], self.instance,
self.flavor, host, self.reservations,
request_spec=legacy_spec, filter_properties=legacy_props,
node=node, clean_shutdown=self.clean_shutdown)
def rollback(self):
if self.quotas:

View File

@ -668,6 +668,24 @@ class CellsAPITestCase(test.NoDBTestCase):
self._check_result(call_info, 'resize_instance',
expected_args, version='1.33')
def test_resize_instance_not_passing_request_spec(self):
call_info = self._stub_rpc_method('cast', None)
self.cells_rpcapi.resize_instance(self.fake_context,
'fake-instance',
dict(cow='moo'),
'fake-hint',
'fake-flavor',
'fake-reservations',
clean_shutdown=True,
request_spec='fake-spec')
expected_args = {'instance': 'fake-instance',
'flavor': 'fake-flavor',
'extra_instance_updates': dict(cow='moo'),
'clean_shutdown': True}
self._check_result(call_info, 'resize_instance',
expected_args, version='1.33')
def test_live_migrate_instance(self):
call_info = self._stub_rpc_method('cast', None)

View File

@ -1487,6 +1487,7 @@ class _ComputeAPIUnitTestMixIn(object):
self.mox.StubOutWithMock(fake_inst, 'save')
self.mox.StubOutWithMock(quota.QUOTAS, 'commit')
self.mox.StubOutWithMock(self.compute_api, '_record_action_start')
self.mox.StubOutWithMock(objects.RequestSpec, 'get_by_instance_uuid')
self.mox.StubOutWithMock(self.compute_api.compute_task_api,
'resize_instance')
@ -1571,6 +1572,10 @@ class _ComputeAPIUnitTestMixIn(object):
self.compute_api._record_action_start(self.context, fake_inst,
'migrate')
fake_spec = objects.RequestSpec()
objects.RequestSpec.get_by_instance_uuid(
self.context, fake_inst.uuid).AndReturn(fake_spec)
scheduler_hint = {'filter_properties': filter_properties}
self.compute_api.compute_task_api.resize_instance(
@ -1578,7 +1583,8 @@ class _ComputeAPIUnitTestMixIn(object):
scheduler_hint=scheduler_hint,
flavor=mox.IsA(objects.Flavor),
reservations=expected_reservations,
clean_shutdown=clean_shutdown)
clean_shutdown=clean_shutdown,
request_spec=fake_spec)
self.mox.ReplayAll()
@ -1592,6 +1598,11 @@ class _ComputeAPIUnitTestMixIn(object):
clean_shutdown=clean_shutdown,
**extra_kwargs)
if allow_same_host:
self.assertEqual([], fake_spec.ignore_hosts)
else:
self.assertEqual([fake_inst['host']], fake_spec.ignore_hosts)
def _test_migrate(self, *args, **kwargs):
self._test_resize(*args, flavor_id_passed=False, **kwargs)
@ -1690,6 +1701,7 @@ class _ComputeAPIUnitTestMixIn(object):
self.compute_api.resize, self.context,
fake_inst, flavor_id='flavor-id')
@mock.patch.object(objects.RequestSpec, 'get_by_instance_uuid')
@mock.patch('nova.compute.api.API._record_action_start')
@mock.patch('nova.compute.api.API._resize_cells_support')
@mock.patch('nova.conductor.conductor_api.ComputeTaskAPI.resize_instance')
@ -1698,7 +1710,8 @@ class _ComputeAPIUnitTestMixIn(object):
get_flavor_by_flavor_id,
resize_instance_mock,
cells_support_mock,
record_mock):
record_mock,
get_by_inst):
params = dict(image_ref='')
fake_inst = self._create_instance_obj(params=params)

View File

@ -352,6 +352,7 @@ class CellsConductorAPIRPCRedirect(test.NoDBTestCase):
# args since this is verified in compute test code.
self.assertTrue(self.cells_rpcapi.build_instances.called)
@mock.patch.object(objects.RequestSpec, 'get_by_instance_uuid')
@mock.patch.object(compute_api.API, '_record_action_start')
@mock.patch.object(compute_api.API, '_resize_cells_support')
@mock.patch.object(compute_utils, 'reserve_quota_delta')
@ -361,7 +362,7 @@ class CellsConductorAPIRPCRedirect(test.NoDBTestCase):
@mock.patch.object(compute_api.API, '_check_auto_disk_config')
@mock.patch.object(objects.BlockDeviceMappingList, 'get_by_instance_uuid')
def test_resize_instance(self, _bdms, _check, _extract, _save, _upsize,
_reserve, _cells, _record):
_reserve, _cells, _record, _spec_get_by_uuid):
flavor = objects.Flavor(**test_flavor.fake_flavor)
_extract.return_value = flavor
orig_system_metadata = {}

View File

@ -15,7 +15,6 @@ import mock
from nova.compute import rpcapi as compute_rpcapi
from nova.conductor.tasks import migrate
from nova import objects
from nova.objects import base as obj_base
from nova.scheduler import client as scheduler_client
from nova.scheduler import utils as scheduler_utils
from nova import test
@ -30,16 +29,18 @@ class MigrationTaskTestCase(test.NoDBTestCase):
self.user_id = 'fake'
self.project_id = 'fake'
self.context = FakeContext(self.user_id, self.project_id)
inst = fake_instance.fake_db_instance(image_ref='image_ref')
self.instance = objects.Instance._from_db_object(
self.context, objects.Instance(), inst, [])
self.instance.system_metadata = {'image_hw_disk_bus': 'scsi'}
self.flavor = fake_flavor.fake_flavor_obj(self.context)
self.flavor.extra_specs = {'extra_specs': 'fake'}
self.request_spec = {'instance_type':
obj_base.obj_to_primitive(self.flavor),
'instance_properties': {},
'image': 'image'}
inst = fake_instance.fake_db_instance(image_ref='image_ref',
instance_type=self.flavor)
inst_object = objects.Instance(
flavor=self.flavor,
numa_topology=None,
pci_requests=None,
system_metadata={'image_hw_disk_bus': 'scsi'})
self.instance = objects.Instance._from_db_object(
self.context, inst_object, inst, [])
self.request_spec = objects.RequestSpec(image=objects.ImageMeta())
self.hosts = [dict(host='host1', nodename=None, limits={})]
self.filter_properties = {'limits': {}, 'retry': {'num_attempts': 1,
'hosts': [['host1', None]]}}
@ -48,37 +49,35 @@ class MigrationTaskTestCase(test.NoDBTestCase):
def _generate_task(self):
return migrate.MigrationTask(self.context, self.instance, self.flavor,
self.filter_properties, self.request_spec,
self.reservations, self.clean_shutdown,
self.request_spec, self.reservations,
self.clean_shutdown,
compute_rpcapi.ComputeAPI(),
scheduler_client.SchedulerClient())
@mock.patch.object(scheduler_utils, 'build_request_spec')
@mock.patch.object(objects.RequestSpec, 'from_components')
@mock.patch.object(scheduler_utils, 'setup_instance_group')
@mock.patch.object(objects.RequestSpec, 'from_primitives')
@mock.patch.object(scheduler_client.SchedulerClient, 'select_destinations')
@mock.patch.object(compute_rpcapi.ComputeAPI, 'prep_resize')
@mock.patch.object(objects.Quotas, 'from_reservations')
def test_execute(self, quotas_mock, prep_resize_mock,
sel_dest_mock, spec_fp_mock, sig_mock, brs_mock):
brs_mock.return_value = self.request_spec
fake_spec = objects.RequestSpec()
spec_fp_mock.return_value = fake_spec
sel_dest_mock, sig_mock, request_spec_from_components):
sel_dest_mock.return_value = self.hosts
task = self._generate_task()
request_spec_from_components.return_value = self.request_spec
legacy_request_spec = self.request_spec.to_legacy_request_spec_dict()
task.execute()
quotas_mock.assert_called_once_with(self.context, self.reservations,
instance=self.instance)
sig_mock.assert_called_once_with(self.context, self.request_spec,
sig_mock.assert_called_once_with(self.context, legacy_request_spec,
self.filter_properties)
task.scheduler_client.select_destinations.assert_called_once_with(
self.context, fake_spec)
self.context, self.request_spec)
prep_resize_mock.assert_called_once_with(
self.context, 'image', self.instance, self.flavor,
self.hosts[0]['host'], self.reservations,
request_spec=self.request_spec,
self.context, legacy_request_spec['image'], self.instance,
self.flavor, self.hosts[0]['host'], self.reservations,
request_spec=legacy_request_spec,
filter_properties=self.filter_properties,
node=self.hosts[0]['nodename'], clean_shutdown=self.clean_shutdown)
self.assertFalse(quotas_mock.return_value.rollback.called)

View File

@ -355,33 +355,22 @@ class _BaseTaskTestCase(object):
self.assertEqual('destination', migration.dest_compute)
self.assertEqual(inst_obj.host, migration.source_compute)
def _test_cold_migrate(self, clean_shutdown=True):
self.mox.StubOutWithMock(utils, 'get_image_from_system_metadata')
self.mox.StubOutWithMock(scheduler_utils, 'build_request_spec')
self.mox.StubOutWithMock(migrate.MigrationTask, 'execute')
@mock.patch.object(migrate.MigrationTask, 'execute')
@mock.patch.object(utils, 'get_image_from_system_metadata')
@mock.patch.object(objects.RequestSpec, 'from_components')
def _test_cold_migrate(self, spec_from_components, get_image_from_metadata,
migration_task_execute, clean_shutdown=True):
get_image_from_metadata.return_value = 'image'
inst = fake_instance.fake_db_instance(image_ref='image_ref')
inst_obj = objects.Instance._from_db_object(
self.context, objects.Instance(), inst, [])
inst_obj.system_metadata = {'image_hw_disk_bus': 'scsi'}
flavor = flavors.get_default_flavor()
flavor.extra_specs = {'extra_specs': 'fake'}
filter_properties = {'limits': {},
'retry': {'num_attempts': 1,
'hosts': [['host1', None]]}}
request_spec = {'instance_type': obj_base.obj_to_primitive(flavor),
'instance_properties': {}}
utils.get_image_from_system_metadata(
inst_obj.system_metadata).AndReturn('image')
inst_obj.flavor = flavor
scheduler_utils.build_request_spec(
self.context, 'image',
[mox.IsA(objects.Instance)],
instance_type=mox.IsA(objects.Flavor)).AndReturn(request_spec)
task = self.conductor_manager._build_cold_migrate_task(
self.context, inst_obj, flavor, filter_properties,
request_spec, [], clean_shutdown=clean_shutdown)
task.execute()
self.mox.ReplayAll()
fake_spec = fake_request_spec.fake_spec_obj()
spec_from_components.return_value = fake_spec
scheduler_hint = {'filter_properties': {}}
@ -398,6 +387,10 @@ class _BaseTaskTestCase(object):
False, False, flavor, None, None, [],
clean_shutdown)
get_image_from_metadata.assert_called_once_with(
inst_obj.system_metadata)
migration_task_execute.assert_called_once_with()
def test_cold_migrate(self):
self._test_cold_migrate()
@ -1355,9 +1348,7 @@ class ConductorTaskTestCase(_BaseTaskTestCase, test_compute.BaseTestCase):
self.conductor._set_vm_state_and_notify(
self.context, 1, 'method', 'updates', 'ex', 'request_spec')
@mock.patch.object(scheduler_utils, 'build_request_spec')
@mock.patch.object(scheduler_utils, 'setup_instance_group')
@mock.patch.object(objects.RequestSpec, 'from_primitives')
@mock.patch.object(utils, 'get_image_from_system_metadata')
@mock.patch.object(objects.Quotas, 'from_reservations')
@mock.patch.object(scheduler_client.SchedulerClient, 'select_destinations')
@ -1366,7 +1357,7 @@ class ConductorTaskTestCase(_BaseTaskTestCase, test_compute.BaseTestCase):
@mock.patch.object(migrate.MigrationTask, 'rollback')
def test_cold_migrate_no_valid_host_back_in_active_state(
self, rollback_mock, notify_mock, select_dest_mock, quotas_mock,
metadata_mock, spec_fp_mock, sig_mock, brs_mock):
metadata_mock, sig_mock):
flavor = flavors.get_flavor_by_name('m1.tiny')
inst_obj = objects.Instance(
image_ref='fake-image_ref',
@ -1374,41 +1365,42 @@ class ConductorTaskTestCase(_BaseTaskTestCase, test_compute.BaseTestCase):
vm_state=vm_states.ACTIVE,
system_metadata={},
uuid=uuids.instance,
user_id=fakes.FAKE_USER_ID)
request_spec = dict(instance_type=dict(extra_specs=dict()),
instance_properties=dict())
filter_props = dict(context=None)
user_id=fakes.FAKE_USER_ID,
flavor=flavor,
availability_zone=None,
pci_requests=None,
numa_topology=None)
resvs = 'fake-resvs'
image = 'fake-image'
fake_spec = objects.RequestSpec()
fake_spec = objects.RequestSpec(image=objects.ImageMeta())
legacy_request_spec = fake_spec.to_legacy_request_spec_dict()
metadata_mock.return_value = image
brs_mock.return_value = request_spec
spec_fp_mock.return_value = fake_spec
exc_info = exc.NoValidHost(reason="")
select_dest_mock.side_effect = exc_info
updates = {'vm_state': vm_states.ACTIVE,
'task_state': None}
# Filter properties are populated during code execution
legacy_filter_props = {'retry': {'num_attempts': 1,
'hosts': []}}
self.assertRaises(exc.NoValidHost,
self.conductor._cold_migrate,
self.context, inst_obj,
flavor, filter_props, [resvs],
clean_shutdown=True)
flavor, {}, [resvs],
True, fake_spec)
metadata_mock.assert_called_with({})
brs_mock.assert_called_once_with(self.context, image,
[inst_obj],
instance_type=flavor)
quotas_mock.assert_called_once_with(self.context, [resvs],
instance=inst_obj)
sig_mock.assert_called_once_with(self.context, request_spec,
filter_props)
sig_mock.assert_called_once_with(self.context, legacy_request_spec,
legacy_filter_props)
notify_mock.assert_called_once_with(self.context, inst_obj.uuid,
'migrate_server', updates,
exc_info, request_spec)
exc_info, legacy_request_spec)
rollback_mock.assert_called_once_with()
@mock.patch.object(scheduler_utils, 'build_request_spec')
@mock.patch.object(scheduler_utils, 'setup_instance_group')
@mock.patch.object(objects.RequestSpec, 'from_primitives')
@mock.patch.object(objects.RequestSpec, 'from_components')
@mock.patch.object(utils, 'get_image_from_system_metadata')
@mock.patch.object(objects.Quotas, 'from_reservations')
@mock.patch.object(scheduler_client.SchedulerClient, 'select_destinations')
@ -1417,7 +1409,7 @@ class ConductorTaskTestCase(_BaseTaskTestCase, test_compute.BaseTestCase):
@mock.patch.object(migrate.MigrationTask, 'rollback')
def test_cold_migrate_no_valid_host_back_in_stopped_state(
self, rollback_mock, notify_mock, select_dest_mock, quotas_mock,
metadata_mock, spec_fp_mock, sig_mock, brs_mock):
metadata_mock, spec_fc_mock, sig_mock):
flavor = flavors.get_flavor_by_name('m1.tiny')
inst_obj = objects.Instance(
image_ref='fake-image_ref',
@ -1425,18 +1417,23 @@ class ConductorTaskTestCase(_BaseTaskTestCase, test_compute.BaseTestCase):
instance_type_id=flavor['id'],
system_metadata={},
uuid=uuids.instance,
user_id=fakes.FAKE_USER_ID)
user_id=fakes.FAKE_USER_ID,
flavor=flavor,
numa_topology=None,
pci_requests=None,
availability_zone=None)
image = 'fake-image'
request_spec = dict(instance_type=dict(extra_specs=dict()),
instance_properties=dict(),
image=image)
filter_props = dict(context=None)
resvs = 'fake-resvs'
fake_spec = objects.RequestSpec()
fake_spec = objects.RequestSpec(image=objects.ImageMeta())
spec_fc_mock.return_value = fake_spec
legacy_request_spec = fake_spec.to_legacy_request_spec_dict()
# Filter properties are populated during code execution
legacy_filter_props = {'retry': {'num_attempts': 1,
'hosts': []}}
metadata_mock.return_value = image
brs_mock.return_value = request_spec
spec_fp_mock.return_value = fake_spec
exc_info = exc.NoValidHost(reason="")
select_dest_mock.side_effect = exc_info
updates = {'vm_state': vm_states.STOPPED,
@ -1444,19 +1441,16 @@ class ConductorTaskTestCase(_BaseTaskTestCase, test_compute.BaseTestCase):
self.assertRaises(exc.NoValidHost,
self.conductor._cold_migrate,
self.context, inst_obj,
flavor, filter_props, [resvs],
clean_shutdown=True)
flavor, {}, [resvs],
True, fake_spec)
metadata_mock.assert_called_with({})
brs_mock.assert_called_once_with(self.context, image,
[inst_obj],
instance_type=flavor)
quotas_mock.assert_called_once_with(self.context, [resvs],
instance=inst_obj)
sig_mock.assert_called_once_with(self.context, request_spec,
filter_props)
sig_mock.assert_called_once_with(self.context, legacy_request_spec,
legacy_filter_props)
notify_mock.assert_called_once_with(self.context, inst_obj.uuid,
'migrate_server', updates,
exc_info, request_spec)
exc_info, legacy_request_spec)
rollback_mock.assert_called_once_with()
def test_cold_migrate_no_valid_host_error_msg(self):
@ -1468,32 +1462,27 @@ class ConductorTaskTestCase(_BaseTaskTestCase, test_compute.BaseTestCase):
system_metadata={},
uuid=uuids.instance,
user_id=fakes.FAKE_USER_ID)
request_spec = dict(instance_type=dict(extra_specs=dict()),
instance_properties=dict())
filter_props = dict(context=None)
fake_spec = fake_request_spec.fake_spec_obj()
resvs = 'fake-resvs'
image = 'fake-image'
with test.nested(
mock.patch.object(utils, 'get_image_from_system_metadata',
return_value=image),
mock.patch.object(scheduler_utils, 'build_request_spec',
return_value=request_spec),
mock.patch.object(self.conductor, '_set_vm_state_and_notify'),
mock.patch.object(migrate.MigrationTask,
'execute',
side_effect=exc.NoValidHost(reason="")),
mock.patch.object(migrate.MigrationTask, 'rollback')
) as (image_mock, brs_mock, set_vm_mock, task_execute_mock,
) as (image_mock, set_vm_mock, task_execute_mock,
task_rollback_mock):
nvh = self.assertRaises(exc.NoValidHost,
self.conductor._cold_migrate, self.context,
inst_obj, flavor, filter_props, [resvs],
clean_shutdown=True)
inst_obj, flavor, {}, [resvs],
True, fake_spec)
self.assertIn('cold migrate', nvh.message)
@mock.patch.object(utils, 'get_image_from_system_metadata')
@mock.patch('nova.scheduler.utils.build_request_spec')
@mock.patch.object(migrate.MigrationTask, 'execute')
@mock.patch.object(migrate.MigrationTask, 'rollback')
@mock.patch.object(conductor_manager.ComputeTaskManager,
@ -1502,7 +1491,6 @@ class ConductorTaskTestCase(_BaseTaskTestCase, test_compute.BaseTestCase):
set_vm_mock,
task_rollback_mock,
task_exec_mock,
brs_mock,
image_mock):
flavor = flavors.get_flavor_by_name('m1.tiny')
inst_obj = objects.Instance(
@ -1512,30 +1500,26 @@ class ConductorTaskTestCase(_BaseTaskTestCase, test_compute.BaseTestCase):
system_metadata={},
uuid=uuids.instance,
user_id=fakes.FAKE_USER_ID)
request_spec = dict(instance_type=dict(extra_specs=dict()),
instance_properties=dict())
filter_props = dict(context=None)
resvs = 'fake-resvs'
image = 'fake-image'
exception = exc.UnsupportedPolicyException(reason='')
fake_spec = fake_request_spec.fake_spec_obj()
legacy_request_spec = fake_spec.to_legacy_request_spec_dict()
image_mock.return_value = image
brs_mock.return_value = request_spec
task_exec_mock.side_effect = exception
self.assertRaises(exc.UnsupportedPolicyException,
self.conductor._cold_migrate, self.context,
inst_obj, flavor, filter_props, [resvs],
clean_shutdown=True)
inst_obj, flavor, {}, [resvs], True, fake_spec)
updates = {'vm_state': vm_states.STOPPED, 'task_state': None}
set_vm_mock.assert_called_once_with(self.context, inst_obj.uuid,
'migrate_server', updates,
exception, request_spec)
exception, legacy_request_spec)
@mock.patch.object(scheduler_utils, 'build_request_spec')
@mock.patch.object(scheduler_utils, 'setup_instance_group')
@mock.patch.object(objects.RequestSpec, 'from_primitives')
@mock.patch.object(objects.RequestSpec, 'from_components')
@mock.patch.object(utils, 'get_image_from_system_metadata')
@mock.patch.object(objects.Quotas, 'from_reservations')
@mock.patch.object(scheduler_client.SchedulerClient, 'select_destinations')
@ -1545,8 +1529,8 @@ class ConductorTaskTestCase(_BaseTaskTestCase, test_compute.BaseTestCase):
@mock.patch.object(compute_rpcapi.ComputeAPI, 'prep_resize')
def test_cold_migrate_exception_host_in_error_state_and_raise(
self, prep_resize_mock, rollback_mock, notify_mock,
select_dest_mock, quotas_mock, metadata_mock, spec_fp_mock,
sig_mock, brs_mock):
select_dest_mock, quotas_mock, metadata_mock, spec_fc_mock,
sig_mock):
flavor = flavors.get_flavor_by_name('m1.tiny')
inst_obj = objects.Instance(
image_ref='fake-image_ref',
@ -1554,19 +1538,19 @@ class ConductorTaskTestCase(_BaseTaskTestCase, test_compute.BaseTestCase):
instance_type_id=flavor['id'],
system_metadata={},
uuid=uuids.instance,
user_id=fakes.FAKE_USER_ID)
user_id=fakes.FAKE_USER_ID,
flavor=flavor,
availability_zone=None,
pci_requests=None,
numa_topology=None)
image = 'fake-image'
request_spec = dict(instance_type=dict(),
instance_properties=dict(),
image=image)
filter_props = dict(context=None)
resvs = 'fake-resvs'
fake_spec = objects.RequestSpec()
fake_spec = objects.RequestSpec(image=objects.ImageMeta())
legacy_request_spec = fake_spec.to_legacy_request_spec_dict()
spec_fc_mock.return_value = fake_spec
hosts = [dict(host='host1', nodename=None, limits={})]
metadata_mock.return_value = image
brs_mock.return_value = request_spec
spec_fp_mock.return_value = fake_spec
exc_info = test.TestingException('something happened')
select_dest_mock.return_value = hosts
@ -1576,28 +1560,29 @@ class ConductorTaskTestCase(_BaseTaskTestCase, test_compute.BaseTestCase):
self.assertRaises(test.TestingException,
self.conductor._cold_migrate,
self.context, inst_obj, flavor,
filter_props, [resvs],
clean_shutdown=True)
{}, [resvs], True, fake_spec)
# Filter properties are populated during code execution
legacy_filter_props = {'retry': {'num_attempts': 1,
'hosts': [['host1', None]]},
'limits': {}}
metadata_mock.assert_called_with({})
brs_mock.assert_called_once_with(self.context, image,
[inst_obj],
instance_type=flavor)
quotas_mock.assert_called_once_with(self.context, [resvs],
instance=inst_obj)
sig_mock.assert_called_once_with(self.context, request_spec,
filter_props)
sig_mock.assert_called_once_with(self.context, legacy_request_spec,
legacy_filter_props)
select_dest_mock.assert_called_once_with(
self.context, fake_spec)
prep_resize_mock.assert_called_once_with(
self.context, image, inst_obj, flavor,
hosts[0]['host'], [resvs],
request_spec=request_spec,
filter_properties=filter_props,
self.context, legacy_request_spec['image'],
inst_obj, flavor, hosts[0]['host'], [resvs],
request_spec=legacy_request_spec,
filter_properties=legacy_filter_props,
node=hosts[0]['nodename'], clean_shutdown=True)
notify_mock.assert_called_once_with(self.context, inst_obj.uuid,
'migrate_server', updates,
exc_info, request_spec)
exc_info, legacy_request_spec)
rollback_mock.assert_called_once_with()
def test_resize_no_valid_host_error_msg(self):
@ -1611,9 +1596,7 @@ class ConductorTaskTestCase(_BaseTaskTestCase, test_compute.BaseTestCase):
uuid=uuids.instance,
user_id=fakes.FAKE_USER_ID)
request_spec = dict(instance_type=dict(extra_specs=dict()),
instance_properties=dict())
filter_props = dict(context=None)
fake_spec = fake_request_spec.fake_spec_obj()
resvs = 'fake-resvs'
image = 'fake-image'
@ -1621,7 +1604,7 @@ class ConductorTaskTestCase(_BaseTaskTestCase, test_compute.BaseTestCase):
mock.patch.object(utils, 'get_image_from_system_metadata',
return_value=image),
mock.patch.object(scheduler_utils, 'build_request_spec',
return_value=request_spec),
return_value=fake_spec),
mock.patch.object(self.conductor, '_set_vm_state_and_notify'),
mock.patch.object(migrate.MigrationTask,
'execute',
@ -1631,8 +1614,8 @@ class ConductorTaskTestCase(_BaseTaskTestCase, test_compute.BaseTestCase):
task_rb_mock):
nvh = self.assertRaises(exc.NoValidHost,
self.conductor._cold_migrate, self.context,
inst_obj, flavor_new, filter_props,
[resvs], clean_shutdown=True)
inst_obj, flavor_new, {},
[resvs], True, fake_spec)
self.assertIn('resize', nvh.message)
def test_build_instances_instance_not_found(self):