Remove legacy request spec compat code from API
For operations which require a request spec, we now require that the request spec exists when we look it up in the API or we fail. All instances created since Newton should have a request spec, and instances created before Newton should have been migrated to have a request spec as part of an online data migration. Failing to find a request spec will now result in a 500 internal server error. As a result, the RequestSpec.get_by_instance_uuid query is moved to before updating the instance.task_state in the various API methods since we want to do all we can before updating the instance in case we fail. Related to blueprint request-spec-use-by-compute Change-Id: I34ffaf285718059b55f90e812b57f1e11d566c6f
This commit is contained in:
parent
ed4fe3ead6
commit
52d515ba7d
@ -3304,6 +3304,9 @@ class API(base.Base):
|
|||||||
else:
|
else:
|
||||||
orig_image_ref = instance.image_ref
|
orig_image_ref = instance.image_ref
|
||||||
|
|
||||||
|
request_spec = objects.RequestSpec.get_by_instance_uuid(
|
||||||
|
context, instance.uuid)
|
||||||
|
|
||||||
self._checks_for_create_and_rebuild(context, image_id, image,
|
self._checks_for_create_and_rebuild(context, image_id, image,
|
||||||
flavor, metadata, files_to_inject, root_bdm)
|
flavor, metadata, files_to_inject, root_bdm)
|
||||||
|
|
||||||
@ -3365,38 +3368,31 @@ class API(base.Base):
|
|||||||
# attached RequestSpec object but let's consider that the operator only
|
# attached RequestSpec object but let's consider that the operator only
|
||||||
# half migrated all their instances in the meantime.
|
# half migrated all their instances in the meantime.
|
||||||
host = instance.host
|
host = instance.host
|
||||||
try:
|
# If a new image is provided on rebuild, we will need to run
|
||||||
request_spec = objects.RequestSpec.get_by_instance_uuid(
|
# through the scheduler again, but we want the instance to be
|
||||||
context, instance.uuid)
|
# rebuilt on the same host it's already on.
|
||||||
# If a new image is provided on rebuild, we will need to run
|
if orig_image_ref != image_href:
|
||||||
# through the scheduler again, but we want the instance to be
|
# We have to modify the request spec that goes to the scheduler
|
||||||
# rebuilt on the same host it's already on.
|
# to contain the new image. We persist this since we've already
|
||||||
if orig_image_ref != image_href:
|
# changed the instance.image_ref above so we're being
|
||||||
# We have to modify the request spec that goes to the scheduler
|
# consistent.
|
||||||
# to contain the new image. We persist this since we've already
|
request_spec.image = objects.ImageMeta.from_dict(image)
|
||||||
# changed the instance.image_ref above so we're being
|
request_spec.save()
|
||||||
# consistent.
|
if 'scheduler_hints' not in request_spec:
|
||||||
request_spec.image = objects.ImageMeta.from_dict(image)
|
request_spec.scheduler_hints = {}
|
||||||
request_spec.save()
|
# Nuke the id on this so we can't accidentally save
|
||||||
if 'scheduler_hints' not in request_spec:
|
# this hint hack later
|
||||||
request_spec.scheduler_hints = {}
|
del request_spec.id
|
||||||
# Nuke the id on this so we can't accidentally save
|
|
||||||
# this hint hack later
|
|
||||||
del request_spec.id
|
|
||||||
|
|
||||||
# NOTE(danms): Passing host=None tells conductor to
|
# NOTE(danms): Passing host=None tells conductor to
|
||||||
# call the scheduler. The _nova_check_type hint
|
# call the scheduler. The _nova_check_type hint
|
||||||
# requires that the scheduler returns only the same
|
# requires that the scheduler returns only the same
|
||||||
# host that we are currently on and only checks
|
# host that we are currently on and only checks
|
||||||
# rebuild-related filters.
|
# rebuild-related filters.
|
||||||
request_spec.scheduler_hints['_nova_check_type'] = ['rebuild']
|
request_spec.scheduler_hints['_nova_check_type'] = ['rebuild']
|
||||||
request_spec.force_hosts = [instance.host]
|
request_spec.force_hosts = [instance.host]
|
||||||
request_spec.force_nodes = [instance.node]
|
request_spec.force_nodes = [instance.node]
|
||||||
host = None
|
host = None
|
||||||
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
|
|
||||||
|
|
||||||
self.compute_task_api.rebuild_instance(context, instance=instance,
|
self.compute_task_api.rebuild_instance(context, instance=instance,
|
||||||
new_pass=admin_password, injected_files=files_to_inject,
|
new_pass=admin_password, injected_files=files_to_inject,
|
||||||
@ -3453,6 +3449,16 @@ class API(base.Base):
|
|||||||
self._check_quota_for_upsize(context, instance, instance.flavor,
|
self._check_quota_for_upsize(context, instance, instance.flavor,
|
||||||
instance.old_flavor)
|
instance.old_flavor)
|
||||||
|
|
||||||
|
# Conductor updated the RequestSpec.flavor during the initial resize
|
||||||
|
# operation to point at the new flavor, so we need to update the
|
||||||
|
# RequestSpec to point back at the original flavor, otherwise
|
||||||
|
# subsequent move operations through the scheduler will be using the
|
||||||
|
# wrong flavor.
|
||||||
|
reqspec = objects.RequestSpec.get_by_instance_uuid(
|
||||||
|
context, instance.uuid)
|
||||||
|
reqspec.flavor = instance.old_flavor
|
||||||
|
reqspec.save()
|
||||||
|
|
||||||
instance.task_state = task_states.RESIZE_REVERTING
|
instance.task_state = task_states.RESIZE_REVERTING
|
||||||
instance.save(expected_task_state=[None])
|
instance.save(expected_task_state=[None])
|
||||||
|
|
||||||
@ -3462,21 +3468,6 @@ class API(base.Base):
|
|||||||
self._record_action_start(context, instance,
|
self._record_action_start(context, instance,
|
||||||
instance_actions.REVERT_RESIZE)
|
instance_actions.REVERT_RESIZE)
|
||||||
|
|
||||||
# Conductor updated the RequestSpec.flavor during the initial resize
|
|
||||||
# operation to point at the new flavor, so we need to update the
|
|
||||||
# RequestSpec to point back at the original flavor, otherwise
|
|
||||||
# subsequent move operations through the scheduler will be using the
|
|
||||||
# wrong flavor.
|
|
||||||
try:
|
|
||||||
reqspec = objects.RequestSpec.get_by_instance_uuid(
|
|
||||||
context, instance.uuid)
|
|
||||||
reqspec.flavor = instance.old_flavor
|
|
||||||
reqspec.save()
|
|
||||||
except exception.RequestSpecNotFound:
|
|
||||||
# TODO(mriedem): Make this a failure in Stein when we drop
|
|
||||||
# compatibility for missing request specs.
|
|
||||||
pass
|
|
||||||
|
|
||||||
# TODO(melwitt): We're not rechecking for strict quota here to guard
|
# TODO(melwitt): We're not rechecking for strict quota here to guard
|
||||||
# against going over quota during a race at this time because the
|
# against going over quota during a race at this time because the
|
||||||
# resource consumption for this operation is written to the database
|
# resource consumption for this operation is written to the database
|
||||||
@ -3601,16 +3592,20 @@ class API(base.Base):
|
|||||||
current_instance_type,
|
current_instance_type,
|
||||||
new_instance_type)
|
new_instance_type)
|
||||||
|
|
||||||
instance.task_state = task_states.RESIZE_PREP
|
|
||||||
instance.progress = 0
|
|
||||||
instance.update(extra_instance_updates)
|
|
||||||
instance.save(expected_task_state=[None])
|
|
||||||
|
|
||||||
filter_properties = {'ignore_hosts': []}
|
filter_properties = {'ignore_hosts': []}
|
||||||
|
|
||||||
if not CONF.allow_resize_to_same_host:
|
if not CONF.allow_resize_to_same_host:
|
||||||
filter_properties['ignore_hosts'].append(instance.host)
|
filter_properties['ignore_hosts'].append(instance.host)
|
||||||
|
|
||||||
|
request_spec = objects.RequestSpec.get_by_instance_uuid(
|
||||||
|
context, instance.uuid)
|
||||||
|
request_spec.ignore_hosts = filter_properties['ignore_hosts']
|
||||||
|
|
||||||
|
instance.task_state = task_states.RESIZE_PREP
|
||||||
|
instance.progress = 0
|
||||||
|
instance.update(extra_instance_updates)
|
||||||
|
instance.save(expected_task_state=[None])
|
||||||
|
|
||||||
if self.cell_type == 'api':
|
if self.cell_type == 'api':
|
||||||
# Create migration record.
|
# Create migration record.
|
||||||
self._resize_cells_support(context, instance,
|
self._resize_cells_support(context, instance,
|
||||||
@ -3624,39 +3619,21 @@ class API(base.Base):
|
|||||||
self._record_action_start(context, instance,
|
self._record_action_start(context, instance,
|
||||||
instance_actions.RESIZE)
|
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
|
|
||||||
if host_name is not None:
|
|
||||||
# If there is no request spec we cannot honor the request
|
|
||||||
# and we need to fail.
|
|
||||||
raise exception.CannotMigrateWithTargetHost()
|
|
||||||
request_spec = None
|
|
||||||
|
|
||||||
# TODO(melwitt): We're not rechecking for strict quota here to guard
|
# TODO(melwitt): We're not rechecking for strict quota here to guard
|
||||||
# against going over quota during a race at this time because the
|
# against going over quota during a race at this time because the
|
||||||
# resource consumption for this operation is written to the database
|
# resource consumption for this operation is written to the database
|
||||||
# by compute.
|
# by compute.
|
||||||
scheduler_hint = {'filter_properties': filter_properties}
|
scheduler_hint = {'filter_properties': filter_properties}
|
||||||
|
|
||||||
if request_spec:
|
if host_name is None:
|
||||||
if host_name is None:
|
# If 'host_name' is not specified,
|
||||||
# If 'host_name' is not specified,
|
# clear the 'requested_destination' field of the RequestSpec.
|
||||||
# clear the 'requested_destination' field of the RequestSpec.
|
request_spec.requested_destination = None
|
||||||
request_spec.requested_destination = None
|
else:
|
||||||
else:
|
# Set the host and the node so that the scheduler will
|
||||||
# Set the host and the node so that the scheduler will
|
# validate them.
|
||||||
# validate them.
|
request_spec.requested_destination = objects.Destination(
|
||||||
request_spec.requested_destination = objects.Destination(
|
host=node.host, node=node.hypervisor_hostname)
|
||||||
host=node.host, node=node.hypervisor_hostname)
|
|
||||||
|
|
||||||
self.compute_task_api.resize_instance(context, instance,
|
self.compute_task_api.resize_instance(context, instance,
|
||||||
extra_instance_updates, scheduler_hint=scheduler_hint,
|
extra_instance_updates, scheduler_hint=scheduler_hint,
|
||||||
@ -3707,18 +3684,14 @@ class API(base.Base):
|
|||||||
vm_states.SHELVED_OFFLOADED])
|
vm_states.SHELVED_OFFLOADED])
|
||||||
def unshelve(self, context, instance):
|
def unshelve(self, context, instance):
|
||||||
"""Restore a shelved instance."""
|
"""Restore a shelved instance."""
|
||||||
|
request_spec = objects.RequestSpec.get_by_instance_uuid(
|
||||||
|
context, instance.uuid)
|
||||||
|
|
||||||
instance.task_state = task_states.UNSHELVING
|
instance.task_state = task_states.UNSHELVING
|
||||||
instance.save(expected_task_state=[None])
|
instance.save(expected_task_state=[None])
|
||||||
|
|
||||||
self._record_action_start(context, instance, instance_actions.UNSHELVE)
|
self._record_action_start(context, instance, instance_actions.UNSHELVE)
|
||||||
|
|
||||||
try:
|
|
||||||
request_spec = objects.RequestSpec.get_by_instance_uuid(
|
|
||||||
context, instance.uuid)
|
|
||||||
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
|
|
||||||
self.compute_task_api.unshelve_instance(context, instance,
|
self.compute_task_api.unshelve_instance(context, instance,
|
||||||
request_spec)
|
request_spec)
|
||||||
|
|
||||||
@ -4487,6 +4460,9 @@ class API(base.Base):
|
|||||||
# state.
|
# state.
|
||||||
nodes = objects.ComputeNodeList.get_all_by_host(context, host_name)
|
nodes = objects.ComputeNodeList.get_all_by_host(context, host_name)
|
||||||
|
|
||||||
|
request_spec = objects.RequestSpec.get_by_instance_uuid(
|
||||||
|
context, instance.uuid)
|
||||||
|
|
||||||
instance.task_state = task_states.MIGRATING
|
instance.task_state = task_states.MIGRATING
|
||||||
instance.save(expected_task_state=[None])
|
instance.save(expected_task_state=[None])
|
||||||
|
|
||||||
@ -4502,14 +4478,6 @@ class API(base.Base):
|
|||||||
self.consoleauth_rpcapi.delete_tokens_for_instance(
|
self.consoleauth_rpcapi.delete_tokens_for_instance(
|
||||||
context, instance.uuid)
|
context, instance.uuid)
|
||||||
|
|
||||||
try:
|
|
||||||
request_spec = objects.RequestSpec.get_by_instance_uuid(
|
|
||||||
context, instance.uuid)
|
|
||||||
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
|
|
||||||
|
|
||||||
# NOTE(sbauza): Force is a boolean by the new related API version
|
# NOTE(sbauza): Force is a boolean by the new related API version
|
||||||
if force is False and host_name:
|
if force is False and host_name:
|
||||||
# Unset the host to make sure we call the scheduler
|
# Unset the host to make sure we call the scheduler
|
||||||
@ -4520,19 +4488,13 @@ class API(base.Base):
|
|||||||
# compute per service but doesn't support live migrations,
|
# compute per service but doesn't support live migrations,
|
||||||
# let's provide the first one.
|
# let's provide the first one.
|
||||||
target = nodes[0]
|
target = nodes[0]
|
||||||
if request_spec:
|
destination = objects.Destination(
|
||||||
# TODO(sbauza): Hydrate a fake spec for old instances not yet
|
host=target.host,
|
||||||
# having a request spec attached to them (particularly true for
|
node=target.hypervisor_hostname
|
||||||
# cells v1). For the moment, let's keep the same behaviour for
|
)
|
||||||
# all the instances but provide the destination only if a spec
|
# This is essentially a hint to the scheduler to only consider
|
||||||
# is found.
|
# the specified host but still run it through the filters.
|
||||||
destination = objects.Destination(
|
request_spec.requested_destination = destination
|
||||||
host=target.host,
|
|
||||||
node=target.hypervisor_hostname
|
|
||||||
)
|
|
||||||
# This is essentially a hint to the scheduler to only consider
|
|
||||||
# the specified host but still run it through the filters.
|
|
||||||
request_spec.requested_destination = destination
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.compute_task_api.live_migrate_instance(context, instance,
|
self.compute_task_api.live_migrate_instance(context, instance,
|
||||||
@ -4657,6 +4619,9 @@ class API(base.Base):
|
|||||||
'expected to be down, but it was up.', inst_host)
|
'expected to be down, but it was up.', inst_host)
|
||||||
raise exception.ComputeServiceInUse(host=inst_host)
|
raise exception.ComputeServiceInUse(host=inst_host)
|
||||||
|
|
||||||
|
request_spec = objects.RequestSpec.get_by_instance_uuid(
|
||||||
|
context, instance.uuid)
|
||||||
|
|
||||||
instance.task_state = task_states.REBUILDING
|
instance.task_state = task_states.REBUILDING
|
||||||
instance.save(expected_task_state=[None])
|
instance.save(expected_task_state=[None])
|
||||||
self._record_action_start(context, instance, instance_actions.EVACUATE)
|
self._record_action_start(context, instance, instance_actions.EVACUATE)
|
||||||
@ -4680,14 +4645,6 @@ class API(base.Base):
|
|||||||
action=fields_obj.NotificationAction.EVACUATE,
|
action=fields_obj.NotificationAction.EVACUATE,
|
||||||
source=fields_obj.NotificationSource.API)
|
source=fields_obj.NotificationSource.API)
|
||||||
|
|
||||||
try:
|
|
||||||
request_spec = objects.RequestSpec.get_by_instance_uuid(
|
|
||||||
context, instance.uuid)
|
|
||||||
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
|
|
||||||
|
|
||||||
# NOTE(sbauza): Force is a boolean by the new related API version
|
# NOTE(sbauza): Force is a boolean by the new related API version
|
||||||
if force is False and host:
|
if force is False and host:
|
||||||
nodes = objects.ComputeNodeList.get_all_by_host(context, host)
|
nodes = objects.ComputeNodeList.get_all_by_host(context, host)
|
||||||
@ -4697,17 +4654,11 @@ class API(base.Base):
|
|||||||
# compute per service but doesn't support evacuations,
|
# compute per service but doesn't support evacuations,
|
||||||
# let's provide the first one.
|
# let's provide the first one.
|
||||||
target = nodes[0]
|
target = nodes[0]
|
||||||
if request_spec:
|
destination = objects.Destination(
|
||||||
# TODO(sbauza): Hydrate a fake spec for old instances not yet
|
host=target.host,
|
||||||
# having a request spec attached to them (particularly true for
|
node=target.hypervisor_hostname
|
||||||
# cells v1). For the moment, let's keep the same behaviour for
|
)
|
||||||
# all the instances but provide the destination only if a spec
|
request_spec.requested_destination = destination
|
||||||
# is found.
|
|
||||||
destination = objects.Destination(
|
|
||||||
host=target.host,
|
|
||||||
node=target.hypervisor_hostname
|
|
||||||
)
|
|
||||||
request_spec.requested_destination = destination
|
|
||||||
|
|
||||||
return self.compute_task_api.rebuild_instance(context,
|
return self.compute_task_api.rebuild_instance(context,
|
||||||
instance=instance,
|
instance=instance,
|
||||||
|
@ -83,6 +83,18 @@ class ServerActionsControllerTestV21(test.TestCase):
|
|||||||
self.context = self.req.environ['nova.context']
|
self.context = self.req.environ['nova.context']
|
||||||
|
|
||||||
self.image_api = image.API()
|
self.image_api = image.API()
|
||||||
|
# Assume that anything that hits the compute API and looks for a
|
||||||
|
# RequestSpec doesn't care about it, since testing logic that deep
|
||||||
|
# should be done in nova.tests.unit.compute.test_compute_api.
|
||||||
|
mock_reqspec = mock.patch('nova.objects.RequestSpec')
|
||||||
|
mock_reqspec.start()
|
||||||
|
self.addCleanup(mock_reqspec.stop)
|
||||||
|
# Similarly we shouldn't care about anything hitting conductor from
|
||||||
|
# these tests.
|
||||||
|
mock_conductor = mock.patch.object(
|
||||||
|
self.controller.compute_api, 'compute_task_api')
|
||||||
|
mock_conductor.start()
|
||||||
|
self.addCleanup(mock_conductor.stop)
|
||||||
|
|
||||||
def _get_controller(self):
|
def _get_controller(self):
|
||||||
return self.servers.ServersController()
|
return self.servers.ServersController()
|
||||||
@ -565,11 +577,20 @@ class ServerActionsControllerTestV21(test.TestCase):
|
|||||||
|
|
||||||
def return_image_meta(*args, **kwargs):
|
def return_image_meta(*args, **kwargs):
|
||||||
image_meta_table = {
|
image_meta_table = {
|
||||||
'1': {'id': 1, 'status': 'active', 'container_format': 'aki'},
|
uuids.kernel_image_id: {
|
||||||
'2': {'id': 2, 'status': 'active', 'container_format': 'ari'},
|
'id': uuids.kernel_image_id,
|
||||||
|
'status': 'active',
|
||||||
|
'container_format': 'aki'},
|
||||||
|
uuids.ramdisk_image_id: {
|
||||||
|
'id': uuids.ramdisk_image_id,
|
||||||
|
'status': 'active',
|
||||||
|
'container_format': 'ari'},
|
||||||
'155d900f-4e14-4e4c-a73d-069cbf4541e6':
|
'155d900f-4e14-4e4c-a73d-069cbf4541e6':
|
||||||
{'id': 3, 'status': 'active', 'container_format': 'raw',
|
{'id': '155d900f-4e14-4e4c-a73d-069cbf4541e6',
|
||||||
'properties': {'kernel_id': 1, 'ramdisk_id': 2}},
|
'status': 'active',
|
||||||
|
'container_format': 'raw',
|
||||||
|
'properties': {'kernel_id': uuids.kernel_image_id,
|
||||||
|
'ramdisk_id': uuids.ramdisk_image_id}},
|
||||||
}
|
}
|
||||||
image_id = args[2]
|
image_id = args[2]
|
||||||
try:
|
try:
|
||||||
@ -589,8 +610,8 @@ class ServerActionsControllerTestV21(test.TestCase):
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
self.controller._action_rebuild(self.req, FAKE_UUID, body=body).obj
|
self.controller._action_rebuild(self.req, FAKE_UUID, body=body).obj
|
||||||
self.assertEqual(instance_meta['kernel_id'], '1')
|
self.assertEqual(instance_meta['kernel_id'], uuids.kernel_image_id)
|
||||||
self.assertEqual(instance_meta['ramdisk_id'], '2')
|
self.assertEqual(instance_meta['ramdisk_id'], uuids.ramdisk_image_id)
|
||||||
|
|
||||||
@mock.patch.object(compute_api.API, 'rebuild')
|
@mock.patch.object(compute_api.API, 'rebuild')
|
||||||
def test_rebuild_instance_raise_auto_disk_config_exc(self, mock_rebuild):
|
def test_rebuild_instance_raise_auto_disk_config_exc(self, mock_rebuild):
|
||||||
|
@ -239,6 +239,18 @@ class ControllerTest(test.TestCase):
|
|||||||
policy.init()
|
policy.init()
|
||||||
self.addCleanup(policy.reset)
|
self.addCleanup(policy.reset)
|
||||||
fake_network.stub_out_nw_api_get_instance_nw_info(self)
|
fake_network.stub_out_nw_api_get_instance_nw_info(self)
|
||||||
|
# Assume that anything that hits the compute API and looks for a
|
||||||
|
# RequestSpec doesn't care about it, since testing logic that deep
|
||||||
|
# should be done in nova.tests.unit.compute.test_compute_api.
|
||||||
|
mock_reqspec = mock.patch('nova.objects.RequestSpec')
|
||||||
|
mock_reqspec.start()
|
||||||
|
self.addCleanup(mock_reqspec.stop)
|
||||||
|
# Similarly we shouldn't care about anything hitting conductor from
|
||||||
|
# these tests.
|
||||||
|
mock_conductor = mock.patch.object(
|
||||||
|
self.controller.compute_api, 'compute_task_api')
|
||||||
|
mock_conductor.start()
|
||||||
|
self.addCleanup(mock_conductor.stop)
|
||||||
|
|
||||||
|
|
||||||
class ServersControllerTest(ControllerTest):
|
class ServersControllerTest(ControllerTest):
|
||||||
|
@ -8882,7 +8882,8 @@ class ComputeAPITestCase(BaseTestCase):
|
|||||||
scheduler_hints={'group':
|
scheduler_hints={'group':
|
||||||
'5b674f73-c8cf-40ef-9965-3b6fe4b304b1'})
|
'5b674f73-c8cf-40ef-9965-3b6fe4b304b1'})
|
||||||
|
|
||||||
def _test_rebuild(self, vm_state):
|
@mock.patch('nova.objects.RequestSpec')
|
||||||
|
def _test_rebuild(self, mock_reqspec, vm_state=None):
|
||||||
instance = self._create_fake_instance_obj()
|
instance = self._create_fake_instance_obj()
|
||||||
instance_uuid = instance['uuid']
|
instance_uuid = instance['uuid']
|
||||||
self.compute.build_and_run_instance(self.context, instance, {}, {}, {},
|
self.compute.build_and_run_instance(self.context, instance, {}, {}, {},
|
||||||
@ -8913,7 +8914,7 @@ class ComputeAPITestCase(BaseTestCase):
|
|||||||
|
|
||||||
with mock.patch.object(self.compute_api.compute_task_api,
|
with mock.patch.object(self.compute_api.compute_task_api,
|
||||||
'rebuild_instance', fake_rpc_rebuild):
|
'rebuild_instance', fake_rpc_rebuild):
|
||||||
image_ref = instance["image_ref"] + '-new_image_ref'
|
image_ref = uuids.new_image_ref
|
||||||
password = "new_password"
|
password = "new_password"
|
||||||
|
|
||||||
instance.vm_state = vm_state
|
instance.vm_state = vm_state
|
||||||
@ -8978,7 +8979,8 @@ class ComputeAPITestCase(BaseTestCase):
|
|||||||
self.assertIn('Unable to find root block device mapping for '
|
self.assertIn('Unable to find root block device mapping for '
|
||||||
'volume-backed instance', six.text_type(ex))
|
'volume-backed instance', six.text_type(ex))
|
||||||
|
|
||||||
def test_rebuild_with_deleted_image(self):
|
@mock.patch('nova.objects.RequestSpec')
|
||||||
|
def test_rebuild_with_deleted_image(self, mock_reqspec):
|
||||||
# If we're given a deleted image by glance, we should not be able to
|
# If we're given a deleted image by glance, we should not be able to
|
||||||
# rebuild from it
|
# rebuild from it
|
||||||
instance = self._create_fake_instance_obj(
|
instance = self._create_fake_instance_obj(
|
||||||
@ -8996,7 +8998,8 @@ class ComputeAPITestCase(BaseTestCase):
|
|||||||
self.compute_api.rebuild(self.context, instance,
|
self.compute_api.rebuild(self.context, instance,
|
||||||
self.fake_image['id'], 'new_password')
|
self.fake_image['id'], 'new_password')
|
||||||
|
|
||||||
def test_rebuild_with_too_little_ram(self):
|
@mock.patch('nova.objects.RequestSpec')
|
||||||
|
def test_rebuild_with_too_little_ram(self, mock_reqspec):
|
||||||
instance = self._create_fake_instance_obj(
|
instance = self._create_fake_instance_obj(
|
||||||
params={'image_ref': FAKE_IMAGE_REF})
|
params={'image_ref': FAKE_IMAGE_REF})
|
||||||
instance.flavor.memory_mb = 64
|
instance.flavor.memory_mb = 64
|
||||||
@ -9016,7 +9019,8 @@ class ComputeAPITestCase(BaseTestCase):
|
|||||||
self.compute_api.rebuild(self.context,
|
self.compute_api.rebuild(self.context,
|
||||||
instance, self.fake_image['id'], 'new_password')
|
instance, self.fake_image['id'], 'new_password')
|
||||||
|
|
||||||
def test_rebuild_with_too_little_disk(self):
|
@mock.patch('nova.objects.RequestSpec')
|
||||||
|
def test_rebuild_with_too_little_disk(self, mock_reqspec):
|
||||||
instance = self._create_fake_instance_obj(
|
instance = self._create_fake_instance_obj(
|
||||||
params={'image_ref': FAKE_IMAGE_REF})
|
params={'image_ref': FAKE_IMAGE_REF})
|
||||||
|
|
||||||
@ -9046,7 +9050,8 @@ class ComputeAPITestCase(BaseTestCase):
|
|||||||
self.compute_api.rebuild(self.context,
|
self.compute_api.rebuild(self.context,
|
||||||
instance, self.fake_image['id'], 'new_password')
|
instance, self.fake_image['id'], 'new_password')
|
||||||
|
|
||||||
def test_rebuild_with_just_enough_ram_and_disk(self):
|
@mock.patch('nova.objects.RequestSpec')
|
||||||
|
def test_rebuild_with_just_enough_ram_and_disk(self, mock_reqspec):
|
||||||
instance = self._create_fake_instance_obj(
|
instance = self._create_fake_instance_obj(
|
||||||
params={'image_ref': FAKE_IMAGE_REF})
|
params={'image_ref': FAKE_IMAGE_REF})
|
||||||
|
|
||||||
@ -9070,7 +9075,8 @@ class ComputeAPITestCase(BaseTestCase):
|
|||||||
self.compute_api.rebuild(self.context,
|
self.compute_api.rebuild(self.context,
|
||||||
instance, self.fake_image['id'], 'new_password')
|
instance, self.fake_image['id'], 'new_password')
|
||||||
|
|
||||||
def test_rebuild_with_no_ram_and_disk_reqs(self):
|
@mock.patch('nova.objects.RequestSpec')
|
||||||
|
def test_rebuild_with_no_ram_and_disk_reqs(self, mock_reqspec):
|
||||||
instance = self._create_fake_instance_obj(
|
instance = self._create_fake_instance_obj(
|
||||||
params={'image_ref': FAKE_IMAGE_REF})
|
params={'image_ref': FAKE_IMAGE_REF})
|
||||||
|
|
||||||
@ -9091,7 +9097,8 @@ class ComputeAPITestCase(BaseTestCase):
|
|||||||
self.compute_api.rebuild(self.context,
|
self.compute_api.rebuild(self.context,
|
||||||
instance, self.fake_image['id'], 'new_password')
|
instance, self.fake_image['id'], 'new_password')
|
||||||
|
|
||||||
def test_rebuild_with_too_large_image(self):
|
@mock.patch('nova.objects.RequestSpec')
|
||||||
|
def test_rebuild_with_too_large_image(self, mock_reqspec):
|
||||||
instance = self._create_fake_instance_obj(
|
instance = self._create_fake_instance_obj(
|
||||||
params={'image_ref': FAKE_IMAGE_REF})
|
params={'image_ref': FAKE_IMAGE_REF})
|
||||||
|
|
||||||
@ -12501,7 +12508,8 @@ class DisabledInstanceTypesTestCase(BaseTestCase):
|
|||||||
self.assertRaises(exception.FlavorNotFound,
|
self.assertRaises(exception.FlavorNotFound,
|
||||||
self.compute_api.create, self.context, self.inst_type, None)
|
self.compute_api.create, self.context, self.inst_type, None)
|
||||||
|
|
||||||
def test_can_resize_to_visible_instance_type(self):
|
@mock.patch('nova.objects.RequestSpec')
|
||||||
|
def test_can_resize_to_visible_instance_type(self, mock_reqspec):
|
||||||
instance = self._create_fake_instance_obj()
|
instance = self._create_fake_instance_obj()
|
||||||
orig_get_flavor_by_flavor_id =\
|
orig_get_flavor_by_flavor_id =\
|
||||||
flavors.get_flavor_by_flavor_id
|
flavors.get_flavor_by_flavor_id
|
||||||
|
@ -1908,7 +1908,8 @@ class _ComputeAPIUnitTestMixIn(object):
|
|||||||
@mock.patch('nova.objects.Quotas.check_deltas')
|
@mock.patch('nova.objects.Quotas.check_deltas')
|
||||||
@mock.patch('nova.objects.Migration.get_by_instance_and_status')
|
@mock.patch('nova.objects.Migration.get_by_instance_and_status')
|
||||||
@mock.patch('nova.context.RequestContext.elevated')
|
@mock.patch('nova.context.RequestContext.elevated')
|
||||||
def test_revert_resize_concurrent_fail(self, mock_elevated,
|
@mock.patch('nova.objects.RequestSpec')
|
||||||
|
def test_revert_resize_concurrent_fail(self, mock_reqspec, mock_elevated,
|
||||||
mock_get_migration, mock_check):
|
mock_get_migration, mock_check):
|
||||||
params = dict(vm_state=vm_states.RESIZED)
|
params = dict(vm_state=vm_states.RESIZED)
|
||||||
fake_inst = self._create_instance_obj(params=params)
|
fake_inst = self._create_instance_obj(params=params)
|
||||||
@ -2056,10 +2057,17 @@ class _ComputeAPIUnitTestMixIn(object):
|
|||||||
host_name=host_name,
|
host_name=host_name,
|
||||||
**extra_kwargs)
|
**extra_kwargs)
|
||||||
else:
|
else:
|
||||||
self.compute_api.resize(self.context, fake_inst,
|
if request_spec:
|
||||||
clean_shutdown=clean_shutdown,
|
self.compute_api.resize(self.context, fake_inst,
|
||||||
host_name=host_name,
|
clean_shutdown=clean_shutdown,
|
||||||
**extra_kwargs)
|
host_name=host_name,
|
||||||
|
**extra_kwargs)
|
||||||
|
else:
|
||||||
|
self.assertRaises(exception.RequestSpecNotFound,
|
||||||
|
self.compute_api.resize,
|
||||||
|
self.context, fake_inst,
|
||||||
|
clean_shutdown=clean_shutdown,
|
||||||
|
host_name=host_name, **extra_kwargs)
|
||||||
|
|
||||||
if request_spec:
|
if request_spec:
|
||||||
if allow_same_host:
|
if allow_same_host:
|
||||||
@ -2106,10 +2114,12 @@ class _ComputeAPIUnitTestMixIn(object):
|
|||||||
mock_inst_save.assert_called_once_with(
|
mock_inst_save.assert_called_once_with(
|
||||||
expected_task_state=[None])
|
expected_task_state=[None])
|
||||||
|
|
||||||
if self.cell_type == 'api':
|
if self.cell_type == 'api' and request_spec:
|
||||||
mock_migration.assert_called_once_with(context=self.context)
|
mock_migration.assert_called_once_with(context=self.context)
|
||||||
mock_elevated.assert_called_once_with()
|
mock_elevated.assert_called_once_with()
|
||||||
mig.create.assert_called_once_with()
|
mig.create.assert_called_once_with()
|
||||||
|
else:
|
||||||
|
mock_migration.assert_not_called()
|
||||||
|
|
||||||
mock_get_by_instance_uuid.assert_called_once_with(self.context,
|
mock_get_by_instance_uuid.assert_called_once_with(self.context,
|
||||||
fake_inst.uuid)
|
fake_inst.uuid)
|
||||||
@ -2118,15 +2128,21 @@ class _ComputeAPIUnitTestMixIn(object):
|
|||||||
mock_record.assert_called_once_with(self.context, fake_inst,
|
mock_record.assert_called_once_with(self.context, fake_inst,
|
||||||
'resize')
|
'resize')
|
||||||
else:
|
else:
|
||||||
mock_record.assert_called_once_with(self.context, fake_inst,
|
if request_spec:
|
||||||
'migrate')
|
mock_record.assert_called_once_with(
|
||||||
|
self.context, fake_inst, 'migrate')
|
||||||
|
else:
|
||||||
|
mock_record.assert_not_called()
|
||||||
|
|
||||||
mock_resize.assert_called_once_with(
|
if request_spec:
|
||||||
self.context, fake_inst, extra_kwargs,
|
mock_resize.assert_called_once_with(
|
||||||
scheduler_hint=scheduler_hint,
|
self.context, fake_inst, extra_kwargs,
|
||||||
flavor=test.MatchType(objects.Flavor),
|
scheduler_hint=scheduler_hint,
|
||||||
clean_shutdown=clean_shutdown,
|
flavor=test.MatchType(objects.Flavor),
|
||||||
request_spec=fake_spec)
|
clean_shutdown=clean_shutdown,
|
||||||
|
request_spec=fake_spec)
|
||||||
|
else:
|
||||||
|
mock_resize.assert_not_called()
|
||||||
|
|
||||||
def _test_migrate(self, *args, **kwargs):
|
def _test_migrate(self, *args, **kwargs):
|
||||||
self._test_resize(*args, flavor_id_passed=False, **kwargs)
|
self._test_resize(*args, flavor_id_passed=False, **kwargs)
|
||||||
@ -2198,34 +2214,6 @@ class _ComputeAPIUnitTestMixIn(object):
|
|||||||
def test_migrate_request_spec_not_found(self):
|
def test_migrate_request_spec_not_found(self):
|
||||||
self._test_migrate(request_spec=False)
|
self._test_migrate(request_spec=False)
|
||||||
|
|
||||||
@mock.patch.object(objects.Migration, 'create')
|
|
||||||
@mock.patch.object(objects.InstanceAction, 'action_start')
|
|
||||||
@mock.patch.object(objects.Instance, 'save')
|
|
||||||
@mock.patch.object(objects.RequestSpec, 'get_by_instance_uuid')
|
|
||||||
@mock.patch.object(objects.ComputeNodeList, 'get_all_by_host',
|
|
||||||
return_value=[objects.ComputeNode(
|
|
||||||
host='target_host',
|
|
||||||
hypervisor_hostname='hypervisor_host')])
|
|
||||||
def test_migrate_request_spec_not_found_with_target_host(
|
|
||||||
self, mock_get_all_by_host, mock_get_by_instance_uuid, mock_save,
|
|
||||||
mock_action_start, mock_migration_create):
|
|
||||||
fake_inst = self._create_instance_obj()
|
|
||||||
mock_get_by_instance_uuid.side_effect = (
|
|
||||||
exception.RequestSpecNotFound(instance_uuid=fake_inst.uuid))
|
|
||||||
self.assertRaises(exception.CannotMigrateWithTargetHost,
|
|
||||||
self.compute_api.resize, self.context,
|
|
||||||
fake_inst, host_name='target_host')
|
|
||||||
mock_get_all_by_host.assert_called_once_with(
|
|
||||||
self.context, 'target_host', True)
|
|
||||||
mock_get_by_instance_uuid.assert_called_once_with(self.context,
|
|
||||||
fake_inst.uuid)
|
|
||||||
mock_save.assert_called_once_with(expected_task_state=[None])
|
|
||||||
mock_action_start.assert_called_once_with(
|
|
||||||
self.context, fake_inst.uuid, instance_actions.MIGRATE,
|
|
||||||
want_result=False)
|
|
||||||
if self.cell_type == 'api':
|
|
||||||
mock_migration_create.assert_called_once_with()
|
|
||||||
|
|
||||||
def test_migrate_with_requested_destination(self):
|
def test_migrate_with_requested_destination(self):
|
||||||
# RequestSpec has requested_destination
|
# RequestSpec has requested_destination
|
||||||
self._test_migrate(requested_destination=True)
|
self._test_migrate(requested_destination=True)
|
||||||
|
Loading…
Reference in New Issue
Block a user