Allow unshelve to a specific host (Compute API part)
This patch introduce changes to the compute API that will allow PROJECT_ADMIN to unshelve an shelved offloaded server to a specific host. This patch also supports the ability to unpin the availability_zone of an instance that is bound to it. Implements: blueprint unshelve-to-host Change-Id: Ieb4766fdd88c469574fad823e05fe401537cdc30
This commit is contained in:
parent
bcb96f362a
commit
a263fa46f8
@ -95,14 +95,15 @@ class ShelveController(wsgi.Controller):
|
|||||||
context.can(shelve_policies.POLICY_ROOT % 'unshelve',
|
context.can(shelve_policies.POLICY_ROOT % 'unshelve',
|
||||||
target={'project_id': instance.project_id})
|
target={'project_id': instance.project_id})
|
||||||
|
|
||||||
new_az = None
|
unshelve_args = {}
|
||||||
|
|
||||||
unshelve_dict = body['unshelve']
|
unshelve_dict = body['unshelve']
|
||||||
support_az = api_version_request.is_supported(req, '2.77')
|
support_az = api_version_request.is_supported(req, '2.77')
|
||||||
if support_az and unshelve_dict:
|
if support_az and unshelve_dict:
|
||||||
new_az = unshelve_dict['availability_zone']
|
unshelve_args['new_az'] = unshelve_dict['availability_zone']
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.compute_api.unshelve(context, instance, new_az=new_az)
|
self.compute_api.unshelve(context, instance, **unshelve_args)
|
||||||
except (exception.InstanceIsLocked,
|
except (exception.InstanceIsLocked,
|
||||||
exception.UnshelveInstanceInvalidState,
|
exception.UnshelveInstanceInvalidState,
|
||||||
exception.MismatchVolumeAZException) as e:
|
exception.MismatchVolumeAZException) as e:
|
||||||
|
@ -380,6 +380,8 @@ def block_extended_resource_request(function):
|
|||||||
class API:
|
class API:
|
||||||
"""API for interacting with the compute manager."""
|
"""API for interacting with the compute manager."""
|
||||||
|
|
||||||
|
_sentinel = object()
|
||||||
|
|
||||||
def __init__(self, image_api=None, network_api=None, volume_api=None):
|
def __init__(self, image_api=None, network_api=None, volume_api=None):
|
||||||
self.image_api = image_api or glance.API()
|
self.image_api = image_api or glance.API()
|
||||||
self.network_api = network_api or neutron.API()
|
self.network_api = network_api or neutron.API()
|
||||||
@ -4391,31 +4393,45 @@ class API:
|
|||||||
context, instance=instance,
|
context, instance=instance,
|
||||||
clean_shutdown=clean_shutdown, accel_uuids=accel_uuids)
|
clean_shutdown=clean_shutdown, accel_uuids=accel_uuids)
|
||||||
|
|
||||||
def _validate_unshelve_az(self, context, instance, availability_zone):
|
def _check_offloaded(self, context, instance):
|
||||||
"""Verify the specified availability_zone during unshelve.
|
"""Check if the status of an instance is SHELVE_OFFLOADED,
|
||||||
|
if not raise an exception.
|
||||||
Verifies that the server is shelved offloaded, the AZ exists and
|
|
||||||
if [cinder]/cross_az_attach=False, that any attached volumes are in
|
|
||||||
the same AZ.
|
|
||||||
|
|
||||||
:param context: nova auth RequestContext for the unshelve action
|
|
||||||
:param instance: Instance object for the server being unshelved
|
|
||||||
:param availability_zone: The user-requested availability zone in
|
|
||||||
which to unshelve the server.
|
|
||||||
:raises: UnshelveInstanceInvalidState if the server is not shelved
|
|
||||||
offloaded
|
|
||||||
:raises: InvalidRequest if the requested AZ does not exist
|
|
||||||
:raises: MismatchVolumeAZException if [cinder]/cross_az_attach=False
|
|
||||||
and any attached volumes are not in the requested AZ
|
|
||||||
"""
|
"""
|
||||||
if instance.vm_state != vm_states.SHELVED_OFFLOADED:
|
if instance.vm_state != vm_states.SHELVED_OFFLOADED:
|
||||||
# NOTE(brinzhang): If the server status is 'SHELVED', it still
|
# NOTE(brinzhang): If the server status is 'SHELVED', it still
|
||||||
# belongs to a host, the availability_zone has not changed.
|
# belongs to a host, the availability_zone should not change.
|
||||||
# Unshelving a shelved offloaded server will go through the
|
# Unshelving a shelved offloaded server will go through the
|
||||||
# scheduler to find a new host.
|
# scheduler to find a new host.
|
||||||
raise exception.UnshelveInstanceInvalidState(
|
raise exception.UnshelveInstanceInvalidState(
|
||||||
state=instance.vm_state, instance_uuid=instance.uuid)
|
state=instance.vm_state, instance_uuid=instance.uuid)
|
||||||
|
|
||||||
|
def _ensure_host_in_az(self, context, host, availability_zone):
|
||||||
|
"""Ensure the host provided belongs to the availability zone,
|
||||||
|
if not raise an exception.
|
||||||
|
"""
|
||||||
|
if availability_zone is not None:
|
||||||
|
host_az = availability_zones.get_host_availability_zone(
|
||||||
|
context,
|
||||||
|
host
|
||||||
|
)
|
||||||
|
if host_az != availability_zone:
|
||||||
|
raise exception.UnshelveHostNotInAZ(
|
||||||
|
host=host, availability_zone=availability_zone)
|
||||||
|
|
||||||
|
def _validate_unshelve_az(self, context, instance, availability_zone):
|
||||||
|
"""Verify the specified availability_zone during unshelve.
|
||||||
|
|
||||||
|
Verifies the AZ exists and if [cinder]/cross_az_attach=False, that
|
||||||
|
any attached volumes are in the same AZ.
|
||||||
|
|
||||||
|
:param context: nova auth RequestContext for the unshelve action
|
||||||
|
:param instance: Instance object for the server being unshelved
|
||||||
|
:param availability_zone: The user-requested availability zone in
|
||||||
|
which to unshelve the server.
|
||||||
|
:raises: InvalidRequest if the requested AZ does not exist
|
||||||
|
:raises: MismatchVolumeAZException if [cinder]/cross_az_attach=False
|
||||||
|
and any attached volumes are not in the requested AZ
|
||||||
|
"""
|
||||||
available_zones = availability_zones.get_availability_zones(
|
available_zones = availability_zones.get_availability_zones(
|
||||||
context, self.host_api, get_only_available=True)
|
context, self.host_api, get_only_available=True)
|
||||||
if availability_zone not in available_zones:
|
if availability_zone not in available_zones:
|
||||||
@ -4443,30 +4459,87 @@ class API:
|
|||||||
|
|
||||||
@block_extended_resource_request
|
@block_extended_resource_request
|
||||||
@check_instance_lock
|
@check_instance_lock
|
||||||
@check_instance_state(vm_state=[vm_states.SHELVED,
|
@check_instance_state(
|
||||||
vm_states.SHELVED_OFFLOADED])
|
vm_state=[vm_states.SHELVED, vm_states.SHELVED_OFFLOADED])
|
||||||
def unshelve(self, context, instance, new_az=None):
|
def unshelve(
|
||||||
"""Restore a shelved instance."""
|
self, context, instance, new_az=_sentinel, host=None):
|
||||||
request_spec = objects.RequestSpec.get_by_instance_uuid(
|
"""Restore a shelved instance.
|
||||||
context, instance.uuid)
|
|
||||||
|
|
||||||
if new_az:
|
:param context: the nova request context
|
||||||
self._validate_unshelve_az(context, instance, new_az)
|
:param instance: nova.objects.instance.Instance object
|
||||||
LOG.debug("Replace the old AZ %(old_az)s in RequestSpec "
|
:param new_az: (optional) target AZ.
|
||||||
"with a new AZ %(new_az)s of the instance.",
|
If None is provided then the current AZ restriction
|
||||||
{"old_az": request_spec.availability_zone,
|
will be removed from the instance.
|
||||||
"new_az": new_az}, instance=instance)
|
If the parameter is not provided then the current
|
||||||
|
AZ restriction will not be changed.
|
||||||
|
:param host: (optional) a host to target
|
||||||
|
"""
|
||||||
# Unshelving a shelved offloaded server will go through the
|
# Unshelving a shelved offloaded server will go through the
|
||||||
# scheduler to pick a new host, so we update the
|
# scheduler to pick a new host, so we update the
|
||||||
# RequestSpec.availability_zone here. Note that if scheduling
|
# RequestSpec.availability_zone here. Note that if scheduling
|
||||||
# fails the RequestSpec will remain updated, which is not great,
|
# fails the RequestSpec will remain updated, which is not great.
|
||||||
# but if we want to change that we need to defer updating the
|
# Bug open to track this https://bugs.launchpad.net/nova/+bug/1978573
|
||||||
# RequestSpec until conductor which probably means RPC changes to
|
|
||||||
# pass the new_az variable to conductor. This is likely low
|
az_passed = new_az is not self._sentinel
|
||||||
# priority since the RequestSpec.availability_zone on a shelved
|
|
||||||
# offloaded server does not mean much anyway and clearly the user
|
request_spec = objects.RequestSpec.get_by_instance_uuid(
|
||||||
# is trying to put the server in the target AZ.
|
context, instance.uuid)
|
||||||
request_spec.availability_zone = new_az
|
|
||||||
|
# We need to check a list of preconditions and validate inputs first
|
||||||
|
|
||||||
|
# Ensure instance is shelve offloaded
|
||||||
|
if az_passed or host:
|
||||||
|
self._check_offloaded(context, instance)
|
||||||
|
|
||||||
|
if az_passed and new_az:
|
||||||
|
# we have to ensure that new AZ is valid
|
||||||
|
self._validate_unshelve_az(context, instance, new_az)
|
||||||
|
# This will be the AZ of the instance after the unshelve. It can be
|
||||||
|
# None indicating that the instance is not pinned to any AZ after the
|
||||||
|
# unshelve
|
||||||
|
expected_az_after_unshelve = (
|
||||||
|
request_spec.availability_zone
|
||||||
|
if not az_passed else new_az
|
||||||
|
)
|
||||||
|
# host is requested, so we have to see if it exists and does not
|
||||||
|
# contradict with the AZ of the instance
|
||||||
|
if host:
|
||||||
|
# Ensure that the requested host exists otherwise raise
|
||||||
|
# a ComputeHostNotFound exception
|
||||||
|
objects.ComputeNode.get_first_node_by_host_for_old_compat(
|
||||||
|
context, host, use_slave=True)
|
||||||
|
# A specific host is requested so we need to make sure that it is
|
||||||
|
# not contradicts with the AZ of the instance
|
||||||
|
self._ensure_host_in_az(
|
||||||
|
context, host, expected_az_after_unshelve)
|
||||||
|
|
||||||
|
if new_az is None:
|
||||||
|
LOG.debug(
|
||||||
|
'Unpin instance from AZ "%(old_az)s".',
|
||||||
|
{'old_az': request_spec.availability_zone},
|
||||||
|
instance=instance
|
||||||
|
)
|
||||||
|
|
||||||
|
LOG.debug(
|
||||||
|
'Unshelving instance with old availability_zone "%(old_az)s" to '
|
||||||
|
'new availability_zone "%(new_az)s" and host "%(host)s".',
|
||||||
|
{
|
||||||
|
'old_az': request_spec.availability_zone,
|
||||||
|
'new_az': '%s' %
|
||||||
|
new_az if az_passed
|
||||||
|
else 'not provided',
|
||||||
|
'host': host,
|
||||||
|
},
|
||||||
|
instance=instance,
|
||||||
|
)
|
||||||
|
# OK every precondition checks out, we just need to tell the scheduler
|
||||||
|
# where to put the instance
|
||||||
|
# We have the expected AZ already calculated. So we just need to
|
||||||
|
# set it in the request_spec to drive the scheduling
|
||||||
|
request_spec.availability_zone = expected_az_after_unshelve
|
||||||
|
# if host is requested we also need to tell the scheduler that
|
||||||
|
if host:
|
||||||
|
request_spec.requested_destination = objects.Destination(host=host)
|
||||||
request_spec.save()
|
request_spec.save()
|
||||||
|
|
||||||
instance.task_state = task_states.UNSHELVING
|
instance.task_state = task_states.UNSHELVING
|
||||||
|
@ -1022,6 +1022,12 @@ class ComputeTaskManager:
|
|||||||
scheduler_utils.populate_filter_properties(
|
scheduler_utils.populate_filter_properties(
|
||||||
filter_properties, selection)
|
filter_properties, selection)
|
||||||
(host, node) = (selection.service_host, selection.nodename)
|
(host, node) = (selection.service_host, selection.nodename)
|
||||||
|
LOG.debug(
|
||||||
|
"Scheduler selected host: %s, node:%s",
|
||||||
|
host,
|
||||||
|
node,
|
||||||
|
instance=instance
|
||||||
|
)
|
||||||
instance.availability_zone = (
|
instance.availability_zone = (
|
||||||
availability_zones.get_host_availability_zone(
|
availability_zones.get_host_availability_zone(
|
||||||
context, host))
|
context, host))
|
||||||
|
@ -1676,9 +1676,15 @@ class MismatchVolumeAZException(Invalid):
|
|||||||
|
|
||||||
|
|
||||||
class UnshelveInstanceInvalidState(InstanceInvalidState):
|
class UnshelveInstanceInvalidState(InstanceInvalidState):
|
||||||
msg_fmt = _('Specifying an availability zone when unshelving server '
|
msg_fmt = _('Specifying an availability zone or a host when unshelving '
|
||||||
'%(instance_uuid)s with status "%(state)s" is not supported. '
|
'server "%(instance_uuid)s" with status "%(state)s" is not '
|
||||||
'The server status must be SHELVED_OFFLOADED.')
|
'supported. The server status must be SHELVED_OFFLOADED.')
|
||||||
|
code = 409
|
||||||
|
|
||||||
|
|
||||||
|
class UnshelveHostNotInAZ(Invalid):
|
||||||
|
msg_fmt = _('Host "%(host)s" is not in the availability zone '
|
||||||
|
'"%(availability_zone)s".')
|
||||||
code = 409
|
code = 409
|
||||||
|
|
||||||
|
|
||||||
|
@ -140,7 +140,9 @@ class UnshelveServerControllerTestV277(test.NoDBTestCase):
|
|||||||
'unshelve') as mock_unshelve:
|
'unshelve') as mock_unshelve:
|
||||||
self.controller._unshelve(self.req, fakes.FAKE_UUID, body=body)
|
self.controller._unshelve(self.req, fakes.FAKE_UUID, body=body)
|
||||||
mock_unshelve.assert_called_once_with(
|
mock_unshelve.assert_called_once_with(
|
||||||
self.req.environ['nova.context'], instance, new_az=None)
|
self.req.environ['nova.context'],
|
||||||
|
instance,
|
||||||
|
)
|
||||||
|
|
||||||
@mock.patch('nova.compute.api.API.unshelve')
|
@mock.patch('nova.compute.api.API.unshelve')
|
||||||
@mock.patch('nova.api.openstack.common.get_instance')
|
@mock.patch('nova.api.openstack.common.get_instance')
|
||||||
@ -158,7 +160,9 @@ class UnshelveServerControllerTestV277(test.NoDBTestCase):
|
|||||||
APIVersionRequest('2.76'))
|
APIVersionRequest('2.76'))
|
||||||
self.controller._unshelve(self.req, fakes.FAKE_UUID, body=body)
|
self.controller._unshelve(self.req, fakes.FAKE_UUID, body=body)
|
||||||
mock_unshelve.assert_called_once_with(
|
mock_unshelve.assert_called_once_with(
|
||||||
self.req.environ['nova.context'], instance, new_az=None)
|
self.req.environ['nova.context'],
|
||||||
|
instance,
|
||||||
|
)
|
||||||
|
|
||||||
@mock.patch('nova.compute.api.API.unshelve')
|
@mock.patch('nova.compute.api.API.unshelve')
|
||||||
@mock.patch('nova.api.openstack.common.get_instance')
|
@mock.patch('nova.api.openstack.common.get_instance')
|
||||||
|
@ -11,10 +11,10 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
import eventlet
|
import eventlet
|
||||||
import mock
|
|
||||||
from oslo_utils import fixture as utils_fixture
|
from oslo_utils import fixture as utils_fixture
|
||||||
from oslo_utils.fixture import uuidsentinel as uuids
|
from oslo_utils.fixture import uuidsentinel as uuids
|
||||||
from oslo_utils import timeutils
|
from oslo_utils import timeutils
|
||||||
|
from unittest import mock
|
||||||
|
|
||||||
from nova.compute import api as compute_api
|
from nova.compute import api as compute_api
|
||||||
from nova.compute import claims
|
from nova.compute import claims
|
||||||
@ -24,6 +24,7 @@ from nova.compute import task_states
|
|||||||
from nova.compute import utils as compute_utils
|
from nova.compute import utils as compute_utils
|
||||||
from nova.compute import vm_states
|
from nova.compute import vm_states
|
||||||
import nova.conf
|
import nova.conf
|
||||||
|
from nova import context
|
||||||
from nova.db.main import api as db
|
from nova.db.main import api as db
|
||||||
from nova import exception
|
from nova import exception
|
||||||
from nova.network import neutron as neutron_api
|
from nova.network import neutron as neutron_api
|
||||||
@ -849,9 +850,67 @@ class ShelveComputeAPITestCase(test_compute.BaseTestCase):
|
|||||||
exclude_states = set()
|
exclude_states = set()
|
||||||
return vm_state - exclude_states
|
return vm_state - exclude_states
|
||||||
|
|
||||||
|
@mock.patch('nova.scheduler.client.report.SchedulerReportClient.'
|
||||||
|
'aggregate_add_host')
|
||||||
|
@mock.patch('nova.availability_zones.get_availability_zones')
|
||||||
|
def _create_host_inside_az(
|
||||||
|
self,
|
||||||
|
ctxt,
|
||||||
|
host,
|
||||||
|
az,
|
||||||
|
mock_az,
|
||||||
|
mock_aggregate,
|
||||||
|
):
|
||||||
|
|
||||||
|
self.api = compute_api.AggregateAPI()
|
||||||
|
mock_az.return_value = [az]
|
||||||
|
|
||||||
|
cells = objects.CellMappingList.get_all(ctxt)
|
||||||
|
cell = cells[0]
|
||||||
|
with context.target_cell(ctxt, cell) as cctxt:
|
||||||
|
s = objects.Service(context=cctxt,
|
||||||
|
host=host,
|
||||||
|
binary='nova-compute',
|
||||||
|
topic='compute',
|
||||||
|
report_count=0)
|
||||||
|
s.create()
|
||||||
|
|
||||||
|
hm = objects.HostMapping(context=ctxt,
|
||||||
|
cell_mapping=cell,
|
||||||
|
host=host)
|
||||||
|
hm.create()
|
||||||
|
|
||||||
|
self._init_aggregate_with_host(None, 'fake_aggregate1',
|
||||||
|
az, host)
|
||||||
|
|
||||||
|
def _create_request_spec_for_initial_az(self, az):
|
||||||
|
fake_spec = objects.RequestSpec()
|
||||||
|
fake_spec.availability_zone = az
|
||||||
|
return fake_spec
|
||||||
|
|
||||||
|
def _assert_unshelving_and_request_spec_az_and_host(
|
||||||
|
self,
|
||||||
|
context,
|
||||||
|
instance,
|
||||||
|
fake_spec,
|
||||||
|
fake_zone,
|
||||||
|
fake_host,
|
||||||
|
mock_get_by_instance_uuid,
|
||||||
|
mock_unshelve
|
||||||
|
):
|
||||||
|
mock_get_by_instance_uuid.assert_called_once_with(context,
|
||||||
|
instance.uuid)
|
||||||
|
|
||||||
|
mock_unshelve.assert_called_once_with(context, instance, fake_spec)
|
||||||
|
|
||||||
|
self.assertEqual(instance.task_state, task_states.UNSHELVING)
|
||||||
|
self.assertEqual(fake_spec.availability_zone, fake_zone)
|
||||||
|
if fake_host:
|
||||||
|
self.assertEqual(fake_spec.requested_destination.host, fake_host)
|
||||||
|
|
||||||
def _test_shelve(self, vm_state=vm_states.ACTIVE, boot_from_volume=False,
|
def _test_shelve(self, vm_state=vm_states.ACTIVE, boot_from_volume=False,
|
||||||
clean_shutdown=True):
|
clean_shutdown=True):
|
||||||
# Ensure instance can be shelved.
|
|
||||||
params = dict(task_state=None, vm_state=vm_state, display_name='vm01')
|
params = dict(task_state=None, vm_state=vm_state, display_name='vm01')
|
||||||
fake_instance = self._create_fake_instance_obj(params=params)
|
fake_instance = self._create_fake_instance_obj(params=params)
|
||||||
instance = fake_instance
|
instance = fake_instance
|
||||||
@ -988,12 +1047,14 @@ class ShelveComputeAPITestCase(test_compute.BaseTestCase):
|
|||||||
|
|
||||||
return instance
|
return instance
|
||||||
|
|
||||||
|
@mock.patch.object(objects.RequestSpec, 'save')
|
||||||
@mock.patch.object(objects.RequestSpec, 'get_by_instance_uuid')
|
@mock.patch.object(objects.RequestSpec, 'get_by_instance_uuid')
|
||||||
def test_unshelve(self, get_by_instance_uuid):
|
def test_unshelve(self, get_by_instance_uuid, fake_save):
|
||||||
# Ensure instance can be unshelved.
|
# Ensure instance can be unshelved.
|
||||||
instance = self._get_specify_state_instance(vm_states.SHELVED)
|
instance = self._get_specify_state_instance(vm_states.SHELVED)
|
||||||
|
|
||||||
fake_spec = objects.RequestSpec()
|
fake_spec = objects.RequestSpec()
|
||||||
|
fake_spec.availability_zone = None
|
||||||
get_by_instance_uuid.return_value = fake_spec
|
get_by_instance_uuid.return_value = fake_spec
|
||||||
with mock.patch.object(self.compute_api.compute_task_api,
|
with mock.patch.object(self.compute_api.compute_task_api,
|
||||||
'unshelve_instance') as unshelve:
|
'unshelve_instance') as unshelve:
|
||||||
@ -1116,24 +1177,558 @@ class ShelveComputeAPITestCase(test_compute.BaseTestCase):
|
|||||||
mock_get_bdms.assert_called_once_with(self.context, instance.uuid)
|
mock_get_bdms.assert_called_once_with(self.context, instance.uuid)
|
||||||
mock_get.assert_called_once_with(self.context, uuids.volume_id)
|
mock_get.assert_called_once_with(self.context, uuids.volume_id)
|
||||||
|
|
||||||
@mock.patch.object(compute_api.API, '_validate_unshelve_az')
|
# Next tests attempt to check the following behavior
|
||||||
|
# +----------+---------------------------+-------+----------------------------+
|
||||||
|
# | Boot | Unshelve after offload AZ | Host | Result |
|
||||||
|
# +==========+===========================+=======+============================+
|
||||||
|
# | No AZ | No AZ or AZ=null | No | Free scheduling, |
|
||||||
|
# | | | | reqspec.AZ=None |
|
||||||
|
# +----------+---------------------------+-------+----------------------------+
|
||||||
|
# | No AZ | No AZ or AZ=null | Host1 | Schedule to host1, |
|
||||||
|
# | | | | reqspec.AZ=None |
|
||||||
|
# +----------+---------------------------+-------+----------------------------+
|
||||||
|
# | No AZ | AZ="AZ1" | No | Schedule to AZ1, |
|
||||||
|
# | | | | reqspec.AZ="AZ1" |
|
||||||
|
# +----------+---------------------------+-------+----------------------------+
|
||||||
|
# | No AZ | AZ="AZ1" | Host1 | Verify that host1 in AZ1, |
|
||||||
|
# | | | | or (1). Schedule to |
|
||||||
|
# | | | | host1, reqspec.AZ="AZ1" |
|
||||||
|
# +----------+---------------------------+-------+----------------------------+
|
||||||
|
# | AZ1 | No AZ | No | Schedule to AZ1, |
|
||||||
|
# | | | | reqspec.AZ="AZ1" |
|
||||||
|
# +----------+---------------------------+-------+----------------------------+
|
||||||
|
# | AZ1 | AZ=null | No | Free scheduling, |
|
||||||
|
# | | | | reqspec.AZ=None |
|
||||||
|
# +----------+---------------------------+-------+----------------------------+
|
||||||
|
# | AZ1 | No AZ | Host1 | If host1 is in AZ1, |
|
||||||
|
# | | | | then schedule to host1, |
|
||||||
|
# | | | | reqspec.AZ="AZ1", otherwise|
|
||||||
|
# | | | | reject the request (1) |
|
||||||
|
# +----------+---------------------------+-------+----------------------------+
|
||||||
|
# | AZ1 | AZ=null | Host1 | Schedule to host1, |
|
||||||
|
# | | | | reqspec.AZ=None |
|
||||||
|
# +----------+---------------------------+-------+----------------------------+
|
||||||
|
# | AZ1 | AZ="AZ2" | No | Schedule to AZ2, |
|
||||||
|
# | | | | reqspec.AZ="AZ2" |
|
||||||
|
# +----------+---------------------------+-------+----------------------------+
|
||||||
|
# | AZ1 | AZ="AZ2" | Host1 | If host1 in AZ2 then |
|
||||||
|
# | | | | schedule to host1, |
|
||||||
|
# | | | | reqspec.AZ="AZ2", |
|
||||||
|
# | | | | otherwise reject (1) |
|
||||||
|
# +----------+---------------------------+-------+----------------------------+
|
||||||
|
#
|
||||||
|
# (1) Check at the api and return an error.
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# +----------+---------------------------+-------+----------------------------+
|
||||||
|
# | No AZ | No AZ or AZ=null | No | Free scheduling, |
|
||||||
|
# | | | | reqspec.AZ=None |
|
||||||
|
# +----------+---------------------------+-------+----------------------------+
|
||||||
|
@mock.patch.object(nova.conductor.ComputeTaskAPI, 'unshelve_instance')
|
||||||
@mock.patch.object(objects.RequestSpec, 'save')
|
@mock.patch.object(objects.RequestSpec, 'save')
|
||||||
|
@mock.patch.object(objects.ComputeNodeList, 'get_all_by_host')
|
||||||
@mock.patch.object(objects.RequestSpec, 'get_by_instance_uuid')
|
@mock.patch.object(objects.RequestSpec, 'get_by_instance_uuid')
|
||||||
def test_specified_az_unshelve(self, get_by_instance_uuid,
|
def test_unshelve_without_az(
|
||||||
mock_save, mock_validate_unshelve_az):
|
self,
|
||||||
# Ensure instance can be unshelved.
|
mock_get_by_instance_uuid,
|
||||||
|
mock_get_all_by_host,
|
||||||
|
mock_save,
|
||||||
|
mock_unshelve
|
||||||
|
):
|
||||||
|
|
||||||
|
context = self.context.elevated()
|
||||||
|
fake_host = 'fake_host1'
|
||||||
|
fake_zone = 'avail_zone1'
|
||||||
|
self._create_host_inside_az(self.context, fake_host, fake_zone)
|
||||||
|
|
||||||
instance = self._get_specify_state_instance(
|
instance = self._get_specify_state_instance(
|
||||||
vm_states.SHELVED_OFFLOADED)
|
vm_states.SHELVED_OFFLOADED)
|
||||||
|
|
||||||
new_az = "west_az"
|
fake_spec = self._create_request_spec_for_initial_az(None)
|
||||||
fake_spec = objects.RequestSpec()
|
mock_get_by_instance_uuid.return_value = fake_spec
|
||||||
fake_spec.availability_zone = "fake-old-az"
|
|
||||||
get_by_instance_uuid.return_value = fake_spec
|
|
||||||
|
|
||||||
self.compute_api.unshelve(self.context, instance, new_az=new_az)
|
self.compute_api.unshelve(context, instance)
|
||||||
|
|
||||||
mock_save.assert_called_once_with()
|
self._assert_unshelving_and_request_spec_az_and_host(
|
||||||
self.assertEqual(new_az, fake_spec.availability_zone)
|
context,
|
||||||
|
instance,
|
||||||
|
fake_spec,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
mock_get_by_instance_uuid,
|
||||||
|
mock_unshelve
|
||||||
|
)
|
||||||
|
|
||||||
mock_validate_unshelve_az.assert_called_once_with(
|
# +----------+---------------------------+-------+----------------------------+
|
||||||
self.context, instance, new_az)
|
# | No AZ | No AZ or AZ=null | Host1 | Schedule to host1, |
|
||||||
|
# | | | | reqspec.AZ=None |
|
||||||
|
# +----------+---------------------------+-------+----------------------------+
|
||||||
|
@mock.patch.object(nova.conductor.ComputeTaskAPI, 'unshelve_instance')
|
||||||
|
@mock.patch.object(objects.RequestSpec, 'save')
|
||||||
|
@mock.patch.object(objects.ComputeNodeList, 'get_all_by_host')
|
||||||
|
@mock.patch.object(objects.RequestSpec, 'get_by_instance_uuid')
|
||||||
|
def test_unshelve_without_az_to_host(
|
||||||
|
self,
|
||||||
|
mock_get_by_instance_uuid,
|
||||||
|
mock_get_all_by_host,
|
||||||
|
mock_save,
|
||||||
|
mock_unshelve
|
||||||
|
):
|
||||||
|
|
||||||
|
context = self.context.elevated()
|
||||||
|
fake_host = 'fake_host1'
|
||||||
|
fake_zone = 'avail_zone1'
|
||||||
|
self._create_host_inside_az(self.context, fake_host, fake_zone)
|
||||||
|
|
||||||
|
instance = self._get_specify_state_instance(
|
||||||
|
vm_states.SHELVED_OFFLOADED)
|
||||||
|
|
||||||
|
fake_spec = self._create_request_spec_for_initial_az(None)
|
||||||
|
mock_get_by_instance_uuid.return_value = fake_spec
|
||||||
|
|
||||||
|
self.compute_api.unshelve(context, instance, host=fake_host)
|
||||||
|
|
||||||
|
self._assert_unshelving_and_request_spec_az_and_host(
|
||||||
|
context,
|
||||||
|
instance,
|
||||||
|
fake_spec,
|
||||||
|
None,
|
||||||
|
fake_host,
|
||||||
|
mock_get_by_instance_uuid,
|
||||||
|
mock_unshelve
|
||||||
|
)
|
||||||
|
|
||||||
|
# +----------+---------------------------+-------+----------------------------+
|
||||||
|
# | No AZ | AZ="AZ1" | No | Schedule to AZ1, |
|
||||||
|
# | | | | reqspec.AZ="AZ1" |
|
||||||
|
# +----------+---------------------------+-------+----------------------------+
|
||||||
|
@mock.patch.object(nova.conductor.ComputeTaskAPI, 'unshelve_instance')
|
||||||
|
@mock.patch.object(objects.RequestSpec, 'save')
|
||||||
|
@mock.patch.object(objects.ComputeNodeList, 'get_all_by_host')
|
||||||
|
@mock.patch.object(objects.RequestSpec, 'get_by_instance_uuid')
|
||||||
|
def test_unshelve_without_az_to_newaz(
|
||||||
|
self,
|
||||||
|
mock_get_by_instance_uuid,
|
||||||
|
mock_get_all_by_host,
|
||||||
|
mock_save,
|
||||||
|
mock_unshelve
|
||||||
|
):
|
||||||
|
|
||||||
|
context = self.context.elevated()
|
||||||
|
fake_host = 'fake_host1'
|
||||||
|
fake_zone = 'avail_zone1'
|
||||||
|
self._create_host_inside_az(self.context, fake_host, fake_zone)
|
||||||
|
|
||||||
|
instance = self._get_specify_state_instance(
|
||||||
|
vm_states.SHELVED_OFFLOADED)
|
||||||
|
|
||||||
|
fake_spec = self._create_request_spec_for_initial_az(None)
|
||||||
|
mock_get_by_instance_uuid.return_value = fake_spec
|
||||||
|
|
||||||
|
self.compute_api.unshelve(context, instance, new_az=fake_zone)
|
||||||
|
|
||||||
|
self._assert_unshelving_and_request_spec_az_and_host(
|
||||||
|
context,
|
||||||
|
instance,
|
||||||
|
fake_spec,
|
||||||
|
fake_zone,
|
||||||
|
None,
|
||||||
|
mock_get_by_instance_uuid,
|
||||||
|
mock_unshelve
|
||||||
|
)
|
||||||
|
|
||||||
|
# +----------+---------------------------+-------+----------------------------+
|
||||||
|
# | No AZ | AZ="AZ1" | Host1 | Verify that host1 in AZ1, |
|
||||||
|
# | | | | or (1). Schedule to |
|
||||||
|
# | | | | host1, reqspec.AZ="AZ1" |
|
||||||
|
# +----------+---------------------------+-------+----------------------------+
|
||||||
|
@mock.patch.object(nova.conductor.ComputeTaskAPI, 'unshelve_instance')
|
||||||
|
@mock.patch.object(objects.RequestSpec, 'save')
|
||||||
|
@mock.patch.object(objects.ComputeNodeList, 'get_all_by_host')
|
||||||
|
@mock.patch.object(objects.RequestSpec, 'get_by_instance_uuid')
|
||||||
|
def test_unshelve_without_az_to_newaz_and_host(
|
||||||
|
self,
|
||||||
|
mock_get_by_instance_uuid,
|
||||||
|
mock_get_all_by_host,
|
||||||
|
mock_save,
|
||||||
|
mock_unshelve
|
||||||
|
):
|
||||||
|
|
||||||
|
context = self.context.elevated()
|
||||||
|
fake_host = 'fake_host1'
|
||||||
|
fake_zone = 'avail_zone1'
|
||||||
|
self._create_host_inside_az(self.context, fake_host, fake_zone)
|
||||||
|
|
||||||
|
instance = self._get_specify_state_instance(
|
||||||
|
vm_states.SHELVED_OFFLOADED)
|
||||||
|
|
||||||
|
fake_spec = self._create_request_spec_for_initial_az(None)
|
||||||
|
mock_get_by_instance_uuid.return_value = fake_spec
|
||||||
|
|
||||||
|
self.compute_api.unshelve(
|
||||||
|
context, instance, new_az=fake_zone, host=fake_host
|
||||||
|
)
|
||||||
|
|
||||||
|
self._assert_unshelving_and_request_spec_az_and_host(
|
||||||
|
context,
|
||||||
|
instance,
|
||||||
|
fake_spec,
|
||||||
|
fake_zone,
|
||||||
|
fake_host,
|
||||||
|
mock_get_by_instance_uuid,
|
||||||
|
mock_unshelve
|
||||||
|
)
|
||||||
|
|
||||||
|
@mock.patch.object(nova.conductor.ComputeTaskAPI, 'unshelve_instance')
|
||||||
|
@mock.patch.object(objects.RequestSpec, 'save')
|
||||||
|
@mock.patch.object(objects.ComputeNodeList, 'get_all_by_host')
|
||||||
|
@mock.patch.object(objects.RequestSpec, 'get_by_instance_uuid')
|
||||||
|
def test_unshelve_without_az_to_newaz_and_host_invalid(
|
||||||
|
self,
|
||||||
|
mock_get_by_instance_uuid,
|
||||||
|
mock_get_all_by_host,
|
||||||
|
mock_save,
|
||||||
|
mock_unshelve
|
||||||
|
):
|
||||||
|
|
||||||
|
context = self.context.elevated()
|
||||||
|
fake_host = 'fake_host1'
|
||||||
|
fake_zone = 'avail_zone1'
|
||||||
|
self._create_host_inside_az(self.context, fake_host, fake_zone)
|
||||||
|
|
||||||
|
instance = self._get_specify_state_instance(
|
||||||
|
vm_states.SHELVED_OFFLOADED)
|
||||||
|
|
||||||
|
fake_spec = self._create_request_spec_for_initial_az(None)
|
||||||
|
mock_get_by_instance_uuid.return_value = fake_spec
|
||||||
|
|
||||||
|
exc = self.assertRaises(
|
||||||
|
nova.exception.UnshelveHostNotInAZ,
|
||||||
|
self.compute_api.unshelve,
|
||||||
|
context,
|
||||||
|
instance,
|
||||||
|
new_az='avail_zone1',
|
||||||
|
host='fake_mini'
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertIn(
|
||||||
|
exc.message,
|
||||||
|
'Host "fake_mini" is not in the availability zone "avail_zone1".'
|
||||||
|
)
|
||||||
|
|
||||||
|
# +----------+---------------------------+-------+----------------------------+
|
||||||
|
# | AZ1 | No AZ | No | Schedule to AZ1, |
|
||||||
|
# | | | | reqspec.AZ="AZ1" |
|
||||||
|
# +----------+---------------------------+-------+----------------------------+
|
||||||
|
@mock.patch.object(nova.conductor.ComputeTaskAPI, 'unshelve_instance')
|
||||||
|
@mock.patch.object(objects.RequestSpec, 'save')
|
||||||
|
@mock.patch.object(objects.ComputeNodeList, 'get_all_by_host')
|
||||||
|
@mock.patch.object(objects.RequestSpec, 'get_by_instance_uuid')
|
||||||
|
def test_unshelve_with_az(
|
||||||
|
self,
|
||||||
|
mock_get_by_instance_uuid,
|
||||||
|
mock_get_all_by_host,
|
||||||
|
mock_save,
|
||||||
|
mock_unshelve
|
||||||
|
):
|
||||||
|
|
||||||
|
context = self.context.elevated()
|
||||||
|
fake_host = 'fake_host1'
|
||||||
|
fake_zone = 'avail_zone1'
|
||||||
|
self._create_host_inside_az(self.context, fake_host, fake_zone)
|
||||||
|
|
||||||
|
instance = self._get_specify_state_instance(
|
||||||
|
vm_states.SHELVED_OFFLOADED)
|
||||||
|
|
||||||
|
fake_spec = self._create_request_spec_for_initial_az(fake_zone)
|
||||||
|
mock_get_by_instance_uuid.return_value = fake_spec
|
||||||
|
|
||||||
|
self.compute_api.unshelve(context, instance)
|
||||||
|
|
||||||
|
self._assert_unshelving_and_request_spec_az_and_host(
|
||||||
|
context,
|
||||||
|
instance,
|
||||||
|
fake_spec,
|
||||||
|
fake_zone,
|
||||||
|
None,
|
||||||
|
mock_get_by_instance_uuid,
|
||||||
|
mock_unshelve
|
||||||
|
)
|
||||||
|
|
||||||
|
# +----------+---------------------------+-------+----------------------------+
|
||||||
|
# | AZ1 | AZ=null | No | Free scheduling, |
|
||||||
|
# | | | | reqspec.AZ=None |
|
||||||
|
# +----------+---------------------------+-------+----------------------------+
|
||||||
|
@mock.patch.object(nova.conductor.ComputeTaskAPI, 'unshelve_instance')
|
||||||
|
@mock.patch.object(objects.RequestSpec, 'save')
|
||||||
|
@mock.patch.object(objects.ComputeNodeList, 'get_all_by_host')
|
||||||
|
@mock.patch.object(objects.RequestSpec, 'get_by_instance_uuid')
|
||||||
|
def test_unshelve_with_az_to_unpin_az(
|
||||||
|
self,
|
||||||
|
mock_get_by_instance_uuid,
|
||||||
|
mock_get_all_by_host,
|
||||||
|
mock_save,
|
||||||
|
mock_unshelve
|
||||||
|
):
|
||||||
|
|
||||||
|
context = self.context.elevated()
|
||||||
|
fake_host = 'fake_host1'
|
||||||
|
fake_zone = 'avail_zone1'
|
||||||
|
self._create_host_inside_az(self.context, fake_host, fake_zone)
|
||||||
|
|
||||||
|
instance = self._get_specify_state_instance(
|
||||||
|
vm_states.SHELVED_OFFLOADED)
|
||||||
|
|
||||||
|
fake_spec = self._create_request_spec_for_initial_az(fake_zone)
|
||||||
|
mock_get_by_instance_uuid.return_value = fake_spec
|
||||||
|
|
||||||
|
self.compute_api.unshelve(context, instance, new_az=None)
|
||||||
|
|
||||||
|
self._assert_unshelving_and_request_spec_az_and_host(
|
||||||
|
context,
|
||||||
|
instance,
|
||||||
|
fake_spec,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
mock_get_by_instance_uuid,
|
||||||
|
mock_unshelve
|
||||||
|
)
|
||||||
|
|
||||||
|
# +----------+---------------------------+-------+----------------------------+
|
||||||
|
# | AZ1 | No AZ | Host1 | If host1 is in AZ1, |
|
||||||
|
# | | | | then schedule to host1, |
|
||||||
|
# | | | | reqspec.AZ="AZ1", otherwise|
|
||||||
|
# | | | | reject the request (1) |
|
||||||
|
# +----------+---------------------------+-------+----------------------------+
|
||||||
|
@mock.patch.object(nova.conductor.ComputeTaskAPI, 'unshelve_instance')
|
||||||
|
@mock.patch.object(objects.RequestSpec, 'save')
|
||||||
|
@mock.patch.object(objects.ComputeNodeList, 'get_all_by_host')
|
||||||
|
@mock.patch.object(objects.RequestSpec, 'get_by_instance_uuid')
|
||||||
|
def test_unshelve_with_az_to_host_in_az(
|
||||||
|
self,
|
||||||
|
mock_get_by_instance_uuid,
|
||||||
|
mock_get_all_by_host,
|
||||||
|
mock_save,
|
||||||
|
mock_unshelve
|
||||||
|
):
|
||||||
|
|
||||||
|
context = self.context.elevated()
|
||||||
|
fake_host = 'fake_host1'
|
||||||
|
fake_zone = 'avail_zone1'
|
||||||
|
self._create_host_inside_az(self.context, fake_host, fake_zone)
|
||||||
|
|
||||||
|
instance = self._get_specify_state_instance(
|
||||||
|
vm_states.SHELVED_OFFLOADED)
|
||||||
|
|
||||||
|
fake_spec = self._create_request_spec_for_initial_az(fake_zone)
|
||||||
|
mock_get_by_instance_uuid.return_value = fake_spec
|
||||||
|
|
||||||
|
self.compute_api.unshelve(context, instance, host=fake_host)
|
||||||
|
|
||||||
|
self._assert_unshelving_and_request_spec_az_and_host(
|
||||||
|
context,
|
||||||
|
instance,
|
||||||
|
fake_spec,
|
||||||
|
fake_zone,
|
||||||
|
fake_host,
|
||||||
|
mock_get_by_instance_uuid,
|
||||||
|
mock_unshelve
|
||||||
|
)
|
||||||
|
|
||||||
|
@mock.patch.object(nova.conductor.ComputeTaskAPI, 'unshelve_instance')
|
||||||
|
@mock.patch.object(objects.RequestSpec, 'save')
|
||||||
|
@mock.patch.object(objects.ComputeNodeList, 'get_all_by_host')
|
||||||
|
@mock.patch.object(objects.RequestSpec, 'get_by_instance_uuid')
|
||||||
|
def test_unshelve_with_az_to_invalid_host(
|
||||||
|
self,
|
||||||
|
mock_get_by_instance_uuid,
|
||||||
|
mock_get_all_by_host,
|
||||||
|
mock_save,
|
||||||
|
mock_unshelve
|
||||||
|
):
|
||||||
|
|
||||||
|
context = self.context.elevated()
|
||||||
|
fake_host = 'fake_host1'
|
||||||
|
fake_zone = 'avail_zone1'
|
||||||
|
self._create_host_inside_az(self.context, fake_host, fake_zone)
|
||||||
|
|
||||||
|
instance = self._get_specify_state_instance(
|
||||||
|
vm_states.SHELVED_OFFLOADED)
|
||||||
|
|
||||||
|
fake_spec = self._create_request_spec_for_initial_az(fake_zone)
|
||||||
|
mock_get_by_instance_uuid.return_value = fake_spec
|
||||||
|
|
||||||
|
exc = self.assertRaises(
|
||||||
|
nova.exception.UnshelveHostNotInAZ,
|
||||||
|
self.compute_api.unshelve,
|
||||||
|
context,
|
||||||
|
instance,
|
||||||
|
host='fake_mini'
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertIn(
|
||||||
|
exc.message,
|
||||||
|
'Host "fake_mini" is not in the availability zone "avail_zone1".'
|
||||||
|
)
|
||||||
|
|
||||||
|
# +----------+---------------------------+-------+----------------------------+
|
||||||
|
# | AZ1 | AZ=null | Host1 | Schedule to host1, |
|
||||||
|
# | | | | reqspec.AZ=None |
|
||||||
|
# +----------+---------------------------+-------+----------------------------+
|
||||||
|
@mock.patch.object(nova.conductor.ComputeTaskAPI, 'unshelve_instance')
|
||||||
|
@mock.patch.object(objects.RequestSpec, 'save')
|
||||||
|
@mock.patch.object(objects.ComputeNodeList, 'get_all_by_host')
|
||||||
|
@mock.patch.object(objects.RequestSpec, 'get_by_instance_uuid')
|
||||||
|
def test_unshelve_with_az_to_host_unpin_az(
|
||||||
|
self,
|
||||||
|
mock_get_by_instance_uuid,
|
||||||
|
mock_get_all_by_host,
|
||||||
|
mock_save,
|
||||||
|
mock_unshelve
|
||||||
|
):
|
||||||
|
|
||||||
|
context = self.context.elevated()
|
||||||
|
fake_host = 'fake_host1'
|
||||||
|
fake_zone = 'avail_zone1'
|
||||||
|
self._create_host_inside_az(self.context, fake_host, fake_zone)
|
||||||
|
|
||||||
|
instance = self._get_specify_state_instance(
|
||||||
|
vm_states.SHELVED_OFFLOADED)
|
||||||
|
|
||||||
|
fake_spec = self._create_request_spec_for_initial_az(fake_zone)
|
||||||
|
mock_get_by_instance_uuid.return_value = fake_spec
|
||||||
|
|
||||||
|
self.compute_api.unshelve(
|
||||||
|
context, instance, new_az=None, host=fake_host
|
||||||
|
)
|
||||||
|
|
||||||
|
self._assert_unshelving_and_request_spec_az_and_host(
|
||||||
|
context,
|
||||||
|
instance,
|
||||||
|
fake_spec,
|
||||||
|
None,
|
||||||
|
fake_host,
|
||||||
|
mock_get_by_instance_uuid,
|
||||||
|
mock_unshelve
|
||||||
|
)
|
||||||
|
|
||||||
|
# +----------+---------------------------+-------+----------------------------+
|
||||||
|
# | AZ1 | AZ="AZ2" | No | Schedule to AZ2, |
|
||||||
|
# | | | | reqspec.AZ="AZ2" |
|
||||||
|
# +----------+---------------------------+-------+----------------------------+
|
||||||
|
@mock.patch.object(nova.conductor.ComputeTaskAPI, 'unshelve_instance')
|
||||||
|
@mock.patch.object(objects.RequestSpec, 'save')
|
||||||
|
@mock.patch.object(objects.ComputeNodeList, 'get_all_by_host')
|
||||||
|
@mock.patch.object(objects.RequestSpec, 'get_by_instance_uuid')
|
||||||
|
def test_unshelve_with_az_to_newaz(
|
||||||
|
self,
|
||||||
|
mock_get_by_instance_uuid,
|
||||||
|
mock_get_all_by_host,
|
||||||
|
mock_save,
|
||||||
|
mock_unshelve
|
||||||
|
):
|
||||||
|
|
||||||
|
context = self.context.elevated()
|
||||||
|
fake_host = 'fake_host1'
|
||||||
|
fake_zone = 'avail_zone1'
|
||||||
|
self._create_host_inside_az(self.context, fake_host, fake_zone)
|
||||||
|
|
||||||
|
instance = self._get_specify_state_instance(
|
||||||
|
vm_states.SHELVED_OFFLOADED)
|
||||||
|
|
||||||
|
fake_spec = self._create_request_spec_for_initial_az('az1')
|
||||||
|
mock_get_by_instance_uuid.return_value = fake_spec
|
||||||
|
|
||||||
|
self.compute_api.unshelve(
|
||||||
|
context, instance, new_az=fake_zone
|
||||||
|
)
|
||||||
|
|
||||||
|
self._assert_unshelving_and_request_spec_az_and_host(
|
||||||
|
context,
|
||||||
|
instance,
|
||||||
|
fake_spec,
|
||||||
|
fake_zone,
|
||||||
|
None,
|
||||||
|
mock_get_by_instance_uuid,
|
||||||
|
mock_unshelve
|
||||||
|
)
|
||||||
|
|
||||||
|
# +----------+---------------------------+-------+----------------------------+
|
||||||
|
# | AZ1 | AZ="AZ2" | Host1 | If host1 in AZ2 then |
|
||||||
|
# | | | | schedule to host1, |
|
||||||
|
# | | | | reqspec.AZ="AZ2", |
|
||||||
|
# | | | | otherwise reject (1) |
|
||||||
|
# +----------+---------------------------+-------+----------------------------+
|
||||||
|
@mock.patch.object(nova.conductor.ComputeTaskAPI, 'unshelve_instance')
|
||||||
|
@mock.patch.object(objects.RequestSpec, 'save')
|
||||||
|
@mock.patch.object(objects.ComputeNodeList, 'get_all_by_host')
|
||||||
|
@mock.patch.object(objects.RequestSpec, 'get_by_instance_uuid')
|
||||||
|
def test_unshelve_with_az_to_newaz_and_host(
|
||||||
|
self,
|
||||||
|
mock_get_by_instance_uuid,
|
||||||
|
mock_get_all_by_host,
|
||||||
|
mock_save,
|
||||||
|
mock_unshelve
|
||||||
|
):
|
||||||
|
|
||||||
|
context = self.context.elevated()
|
||||||
|
fake_host = 'fake_host1'
|
||||||
|
fake_zone = 'avail_zone1'
|
||||||
|
self._create_host_inside_az(self.context, fake_host, fake_zone)
|
||||||
|
|
||||||
|
instance = self._get_specify_state_instance(
|
||||||
|
vm_states.SHELVED_OFFLOADED)
|
||||||
|
|
||||||
|
fake_spec = self._create_request_spec_for_initial_az('az1')
|
||||||
|
mock_get_by_instance_uuid.return_value = fake_spec
|
||||||
|
|
||||||
|
self.compute_api.unshelve(
|
||||||
|
context, instance, new_az=fake_zone, host=fake_host
|
||||||
|
)
|
||||||
|
|
||||||
|
self._assert_unshelving_and_request_spec_az_and_host(
|
||||||
|
context,
|
||||||
|
instance,
|
||||||
|
fake_spec,
|
||||||
|
fake_zone,
|
||||||
|
fake_host,
|
||||||
|
mock_get_by_instance_uuid,
|
||||||
|
mock_unshelve
|
||||||
|
)
|
||||||
|
|
||||||
|
@mock.patch.object(nova.conductor.ComputeTaskAPI, 'unshelve_instance')
|
||||||
|
@mock.patch.object(objects.RequestSpec, 'save')
|
||||||
|
@mock.patch.object(objects.ComputeNodeList, 'get_all_by_host')
|
||||||
|
@mock.patch.object(objects.RequestSpec, 'get_by_instance_uuid')
|
||||||
|
def test_unshelve_with_az_to_newaz_and_invalid_host(
|
||||||
|
self,
|
||||||
|
mock_get_by_instance_uuid,
|
||||||
|
mock_get_all_by_host,
|
||||||
|
mock_save,
|
||||||
|
mock_unshelve
|
||||||
|
):
|
||||||
|
|
||||||
|
context = self.context.elevated()
|
||||||
|
fake_host = 'fake_host1'
|
||||||
|
fake_zone = 'avail_zone1'
|
||||||
|
self._create_host_inside_az(self.context, fake_host, fake_zone)
|
||||||
|
|
||||||
|
instance = self._get_specify_state_instance(
|
||||||
|
vm_states.SHELVED_OFFLOADED)
|
||||||
|
|
||||||
|
fake_spec = self._create_request_spec_for_initial_az('az1')
|
||||||
|
mock_get_by_instance_uuid.return_value = fake_spec
|
||||||
|
|
||||||
|
exc = self.assertRaises(
|
||||||
|
nova.exception.UnshelveHostNotInAZ,
|
||||||
|
self.compute_api.unshelve,
|
||||||
|
context,
|
||||||
|
instance,
|
||||||
|
new_az=fake_zone,
|
||||||
|
host='fake_mini'
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertIn(
|
||||||
|
exc.message,
|
||||||
|
'Host "fake_mini" is not in the availability zone "avail_zone1".'
|
||||||
|
)
|
||||||
|
Loading…
Reference in New Issue
Block a user