Merge "compute: Report COMPUTE_RESCUE_BFV and check during rescue"
This commit is contained in:
commit
437f924843
|
@ -4247,7 +4247,8 @@ class API(base.Base):
|
|||
@check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.STOPPED,
|
||||
vm_states.ERROR])
|
||||
def rescue(self, context, instance, rescue_password=None,
|
||||
rescue_image_ref=None, clean_shutdown=True):
|
||||
rescue_image_ref=None, clean_shutdown=True,
|
||||
allow_bfv_rescue=False):
|
||||
"""Rescue the given instance."""
|
||||
|
||||
bdms = objects.BlockDeviceMappingList.get_by_instance_uuid(
|
||||
|
@ -4256,7 +4257,20 @@ class API(base.Base):
|
|||
if bdm.volume_id:
|
||||
vol = self.volume_api.get(context, bdm.volume_id)
|
||||
self.volume_api.check_attached(context, vol)
|
||||
if compute_utils.is_volume_backed_instance(context, instance, bdms):
|
||||
|
||||
volume_backed = compute_utils.is_volume_backed_instance(
|
||||
context, instance, bdms)
|
||||
|
||||
if volume_backed and allow_bfv_rescue:
|
||||
cn = objects.ComputeNode.get_by_host_and_nodename(
|
||||
context, instance.host, instance.node)
|
||||
traits = self.placementclient.get_provider_traits(
|
||||
context, cn.uuid).traits
|
||||
if os_traits.COMPUTE_RESCUE_BFV not in traits:
|
||||
reason = _("Host unable to rescue a volume-backed instance")
|
||||
raise exception.InstanceNotRescuable(instance_id=instance.uuid,
|
||||
reason=reason)
|
||||
elif volume_backed:
|
||||
reason = _("Cannot rescue a volume-backed instance")
|
||||
raise exception.InstanceNotRescuable(instance_id=instance.uuid,
|
||||
reason=reason)
|
||||
|
|
|
@ -20,6 +20,7 @@ import ddt
|
|||
import fixtures
|
||||
import iso8601
|
||||
import mock
|
||||
import os_traits as ot
|
||||
from oslo_messaging import exceptions as oslo_exceptions
|
||||
from oslo_serialization import jsonutils
|
||||
from oslo_utils import fixture as utils_fixture
|
||||
|
@ -5237,6 +5238,162 @@ class _ComputeAPIUnitTestMixIn(object):
|
|||
rpcapi_unrescue_instance.assert_called_once_with(
|
||||
self.context, instance=instance)
|
||||
|
||||
@mock.patch('nova.objects.compute_node.ComputeNode'
|
||||
'.get_by_host_and_nodename')
|
||||
@mock.patch('nova.compute.utils.is_volume_backed_instance',
|
||||
return_value=True)
|
||||
@mock.patch('nova.objects.block_device.BlockDeviceMappingList'
|
||||
'.get_by_instance_uuid')
|
||||
def test_rescue_bfv_with_required_trait(self, mock_get_bdms,
|
||||
mock_is_volume_backed,
|
||||
mock_get_cn):
|
||||
instance = self._create_instance_obj()
|
||||
bdms = objects.BlockDeviceMappingList(objects=[
|
||||
objects.BlockDeviceMapping(
|
||||
boot_index=0, image_id=uuids.image_id, source_type='image',
|
||||
destination_type='volume', volume_type=None,
|
||||
snapshot_id=None, volume_id=uuids.volume_id,
|
||||
volume_size=None)])
|
||||
with test.nested(
|
||||
mock.patch.object(self.compute_api.placementclient,
|
||||
'get_provider_traits'),
|
||||
mock.patch.object(self.compute_api.volume_api, 'get'),
|
||||
mock.patch.object(self.compute_api.volume_api, 'check_attached'),
|
||||
mock.patch.object(instance, 'save'),
|
||||
mock.patch.object(self.compute_api, '_record_action_start'),
|
||||
mock.patch.object(self.compute_api.compute_rpcapi,
|
||||
'rescue_instance')
|
||||
) as (
|
||||
mock_get_traits, mock_get_volume, mock_check_attached,
|
||||
mock_instance_save, mock_record_start, mock_rpcapi_rescue
|
||||
):
|
||||
# Mock out the returned compute node, bdms and volume
|
||||
mock_get_cn.return_value = mock.Mock(uuid=uuids.cn)
|
||||
mock_get_bdms.return_value = bdms
|
||||
mock_get_volume.return_value = mock.sentinel.volume
|
||||
|
||||
# Ensure the required trait is returned, allowing BFV rescue
|
||||
mock_trait_info = mock.Mock(traits=[ot.COMPUTE_RESCUE_BFV])
|
||||
mock_get_traits.return_value = mock_trait_info
|
||||
|
||||
# Try to rescue the instance
|
||||
self.compute_api.rescue(self.context, instance,
|
||||
rescue_image_ref=uuids.rescue_image_id,
|
||||
allow_bfv_rescue=True)
|
||||
|
||||
# Assert all of the calls made in the compute API
|
||||
mock_get_bdms.assert_called_once_with(self.context, instance.uuid)
|
||||
mock_get_volume.assert_called_once_with(
|
||||
self.context, uuids.volume_id)
|
||||
mock_check_attached.assert_called_once_with(
|
||||
self.context, mock.sentinel.volume)
|
||||
mock_is_volume_backed.assert_called_once_with(
|
||||
self.context, instance, bdms)
|
||||
mock_get_cn.assert_called_once_with(
|
||||
self.context, instance.host, instance.node)
|
||||
mock_get_traits.assert_called_once_with(self.context, uuids.cn)
|
||||
mock_instance_save.assert_called_once_with(
|
||||
expected_task_state=[None])
|
||||
mock_record_start.assert_called_once_with(
|
||||
self.context, instance, instance_actions.RESCUE)
|
||||
mock_rpcapi_rescue.assert_called_once_with(
|
||||
self.context, instance=instance, rescue_password=None,
|
||||
rescue_image_ref=uuids.rescue_image_id, clean_shutdown=True)
|
||||
|
||||
# Assert that the instance task state as set in the compute API
|
||||
self.assertEqual(task_states.RESCUING, instance.task_state)
|
||||
|
||||
@mock.patch('nova.objects.compute_node.ComputeNode'
|
||||
'.get_by_host_and_nodename')
|
||||
@mock.patch('nova.compute.utils.is_volume_backed_instance',
|
||||
return_value=True)
|
||||
@mock.patch('nova.objects.block_device.BlockDeviceMappingList'
|
||||
'.get_by_instance_uuid')
|
||||
def test_rescue_bfv_without_required_trait(self, mock_get_bdms,
|
||||
mock_is_volume_backed,
|
||||
mock_get_cn):
|
||||
instance = self._create_instance_obj()
|
||||
bdms = objects.BlockDeviceMappingList(objects=[
|
||||
objects.BlockDeviceMapping(
|
||||
boot_index=0, image_id=uuids.image_id, source_type='image',
|
||||
destination_type='volume', volume_type=None,
|
||||
snapshot_id=None, volume_id=uuids.volume_id,
|
||||
volume_size=None)])
|
||||
with test.nested(
|
||||
mock.patch.object(self.compute_api.placementclient,
|
||||
'get_provider_traits'),
|
||||
mock.patch.object(self.compute_api.volume_api, 'get'),
|
||||
mock.patch.object(self.compute_api.volume_api, 'check_attached'),
|
||||
) as (
|
||||
mock_get_traits, mock_get_volume, mock_check_attached
|
||||
):
|
||||
# Mock out the returned compute node, bdms and volume
|
||||
mock_get_bdms.return_value = bdms
|
||||
mock_get_volume.return_value = mock.sentinel.volume
|
||||
mock_get_cn.return_value = mock.Mock(uuid=uuids.cn)
|
||||
|
||||
# Ensure the required trait is not returned, denying BFV rescue
|
||||
mock_trait_info = mock.Mock(traits=[])
|
||||
mock_get_traits.return_value = mock_trait_info
|
||||
|
||||
# Assert that any attempt to rescue a bfv instance on a compute
|
||||
# node that does not report the COMPUTE_RESCUE_BFV trait fails and
|
||||
# raises InstanceNotRescuable
|
||||
self.assertRaises(exception.InstanceNotRescuable,
|
||||
self.compute_api.rescue, self.context, instance,
|
||||
rescue_image_ref=None, allow_bfv_rescue=True)
|
||||
|
||||
# Assert the calls made in the compute API prior to the failure
|
||||
mock_get_bdms.assert_called_once_with(self.context, instance.uuid)
|
||||
mock_get_volume.assert_called_once_with(
|
||||
self.context, uuids.volume_id)
|
||||
mock_check_attached.assert_called_once_with(
|
||||
self.context, mock.sentinel.volume)
|
||||
mock_is_volume_backed.assert_called_once_with(
|
||||
self.context, instance, bdms)
|
||||
mock_get_cn.assert_called_once_with(
|
||||
self.context, instance.host, instance.node)
|
||||
mock_get_traits.assert_called_once_with(
|
||||
self.context, uuids.cn)
|
||||
|
||||
@mock.patch('nova.compute.utils.is_volume_backed_instance',
|
||||
return_value=True)
|
||||
@mock.patch('nova.objects.block_device.BlockDeviceMappingList'
|
||||
'.get_by_instance_uuid')
|
||||
def test_rescue_bfv_without_allow_flag(self, mock_get_bdms,
|
||||
mock_is_volume_backed):
|
||||
instance = self._create_instance_obj()
|
||||
bdms = objects.BlockDeviceMappingList(objects=[
|
||||
objects.BlockDeviceMapping(
|
||||
boot_index=0, image_id=uuids.image_id, source_type='image',
|
||||
destination_type='volume', volume_type=None,
|
||||
snapshot_id=None, volume_id=uuids.volume_id,
|
||||
volume_size=None)])
|
||||
with test.nested(
|
||||
mock.patch.object(self.compute_api.volume_api, 'get'),
|
||||
mock.patch.object(self.compute_api.volume_api, 'check_attached'),
|
||||
) as (
|
||||
mock_get_volume, mock_check_attached
|
||||
):
|
||||
# Mock out the returned bdms and volume
|
||||
mock_get_bdms.return_value = bdms
|
||||
mock_get_volume.return_value = mock.sentinel.volume
|
||||
|
||||
# Assert that any attempt to rescue a bfv instance with
|
||||
# allow_bfv_rescue=False fails and raises InstanceNotRescuable
|
||||
self.assertRaises(exception.InstanceNotRescuable,
|
||||
self.compute_api.rescue, self.context, instance,
|
||||
rescue_image_ref=None, allow_bfv_rescue=False)
|
||||
|
||||
# Assert the calls made in the compute API prior to the failure
|
||||
mock_get_bdms.assert_called_once_with(self.context, instance.uuid)
|
||||
mock_get_volume.assert_called_once_with(
|
||||
self.context, uuids.volume_id)
|
||||
mock_check_attached.assert_called_once_with(
|
||||
self.context, mock.sentinel.volume)
|
||||
mock_is_volume_backed.assert_called_once_with(
|
||||
self.context, instance, bdms)
|
||||
|
||||
def test_set_admin_password_invalid_state(self):
|
||||
# Tests that InstanceInvalidState is raised when not ACTIVE.
|
||||
instance = self._create_instance_obj({'vm_state': vm_states.STOPPED})
|
||||
|
|
|
@ -125,9 +125,10 @@ CAPABILITY_TRAITS_MAP = {
|
|||
"supports_image_type_vmdk": os_traits.COMPUTE_IMAGE_TYPE_VMDK,
|
||||
# Added in os-traits 2.0.0
|
||||
"supports_image_type_ploop": os_traits.COMPUTE_IMAGE_TYPE_PLOOP,
|
||||
|
||||
# Added in os-traits 2.1.0.
|
||||
"supports_migrate_to_same_host": os_traits.COMPUTE_SAME_HOST_COLD_MIGRATE,
|
||||
# Added in os-traits 2.2.0.
|
||||
"supports_bfv_rescue": os_traits.COMPUTE_RESCUE_BFV,
|
||||
}
|
||||
|
||||
|
||||
|
@ -178,6 +179,7 @@ class ComputeDriver(object):
|
|||
"supports_trusted_certs": False,
|
||||
"supports_pcpus": False,
|
||||
"supports_accelerators": False,
|
||||
"supports_bfv_rescue": False,
|
||||
|
||||
# Image type support flags
|
||||
"supports_image_type_aki": False,
|
||||
|
|
|
@ -68,6 +68,7 @@ class PowerVMDriver(driver.ComputeDriver):
|
|||
# capabilities on the instance rather than on the class.
|
||||
self.capabilities = {
|
||||
'has_imagecache': False,
|
||||
'supports_bfv_rescue': False,
|
||||
'supports_evacuate': False,
|
||||
'supports_migrate_to_same_host': False,
|
||||
'supports_attach_interface': True,
|
||||
|
|
Loading…
Reference in New Issue