Merge "api: Introduce microverion 2.87 allowing boot from volume rescue"

This commit is contained in:
Zuul 2020-04-10 20:12:04 +00:00 committed by Gerrit Code Review
commit a40c694125
22 changed files with 556 additions and 10 deletions

View File

@ -0,0 +1,87 @@
{
"server": {
"OS-DCF:diskConfig": "AUTO",
"OS-EXT-AZ:availability_zone": "us-west",
"OS-EXT-SRV-ATTR:host": "compute",
"OS-EXT-SRV-ATTR:hostname": "new-server-test",
"OS-EXT-SRV-ATTR:hypervisor_hostname": "fake-mini",
"OS-EXT-SRV-ATTR:instance_name": "instance-00000001",
"OS-EXT-SRV-ATTR:kernel_id": "",
"OS-EXT-SRV-ATTR:launch_index": 0,
"OS-EXT-SRV-ATTR:ramdisk_id": "",
"OS-EXT-SRV-ATTR:reservation_id": "r-d0bls59j",
"OS-EXT-SRV-ATTR:root_device_name": "/dev/sda",
"OS-EXT-SRV-ATTR:user_data": "IyEvYmluL2Jhc2gKL2Jpbi9zdQplY2hvICJJIGFtIGluIHlvdSEiCg==",
"OS-EXT-STS:power_state": 4,
"OS-EXT-STS:task_state": null,
"OS-EXT-STS:vm_state": "rescued",
"OS-SRV-USG:launched_at": "2020-02-07T17:39:49.259481",
"OS-SRV-USG:terminated_at": null,
"accessIPv4": "1.2.3.4",
"accessIPv6": "80fe::",
"addresses": {
"private": [
{
"OS-EXT-IPS-MAC:mac_addr": "00:0c:29:0d:11:74",
"OS-EXT-IPS:type": "fixed",
"addr": "192.168.1.30",
"version": 4
}
]
},
"config_drive": "",
"created": "2020-02-07T17:39:48Z",
"description": null,
"flavor": {
"disk": 1,
"ephemeral": 0,
"extra_specs": {},
"original_name": "m1.tiny",
"ram": 512,
"swap": 0,
"vcpus": 1
},
"hostId": "2091634baaccdc4c5a1d57069c833e402921df696b7f970791b12ec6",
"host_status": "UP",
"id": "69bebe1c-3bdb-4feb-9b79-afa3d4782d95",
"image": {
"id": "70a599e0-31e7-49b7-b260-868f441e862b",
"links": [
{
"href": "http://openstack.example.com/6f70656e737461636b20342065766572/images/70a599e0-31e7-49b7-b260-868f441e862b",
"rel": "bookmark"
}
]
},
"key_name": null,
"links": [
{
"href": "http://openstack.example.com/v2.1/6f70656e737461636b20342065766572/servers/69bebe1c-3bdb-4feb-9b79-afa3d4782d95",
"rel": "self"
},
{
"href": "http://openstack.example.com/6f70656e737461636b20342065766572/servers/69bebe1c-3bdb-4feb-9b79-afa3d4782d95",
"rel": "bookmark"
}
],
"locked": false,
"locked_reason": null,
"metadata": {
"My Server Name": "Apache1"
},
"name": "new-server-test",
"os-extended-volumes:volumes_attached": [],
"security_groups": [
{
"name": "default"
}
],
"server_groups": [],
"status": "RESCUE",
"tags": [],
"tenant_id": "6f70656e737461636b20342065766572",
"trusted_image_certificates": null,
"updated": "2020-02-07T17:39:49Z",
"user_id": "fake"
}
}

View File

@ -0,0 +1,88 @@
{
"server": {
"OS-DCF:diskConfig": "AUTO",
"OS-EXT-AZ:availability_zone": "us-west",
"OS-EXT-SRV-ATTR:host": "compute",
"OS-EXT-SRV-ATTR:hostname": "new-server-test",
"OS-EXT-SRV-ATTR:hypervisor_hostname": "fake-mini",
"OS-EXT-SRV-ATTR:instance_name": "instance-00000001",
"OS-EXT-SRV-ATTR:kernel_id": "",
"OS-EXT-SRV-ATTR:launch_index": 0,
"OS-EXT-SRV-ATTR:ramdisk_id": "",
"OS-EXT-SRV-ATTR:reservation_id": "r-g20x6pwt",
"OS-EXT-SRV-ATTR:root_device_name": "/dev/sda",
"OS-EXT-SRV-ATTR:user_data": "IyEvYmluL2Jhc2gKL2Jpbi9zdQplY2hvICJJIGFtIGluIHlvdSEiCg==",
"OS-EXT-STS:power_state": 1,
"OS-EXT-STS:task_state": null,
"OS-EXT-STS:vm_state": "active",
"OS-SRV-USG:launched_at": "2020-02-07T17:39:55.632592",
"OS-SRV-USG:terminated_at": null,
"accessIPv4": "1.2.3.4",
"accessIPv6": "80fe::",
"addresses": {
"private": [
{
"OS-EXT-IPS-MAC:mac_addr": "00:0c:29:0d:11:74",
"OS-EXT-IPS:type": "fixed",
"addr": "192.168.1.30",
"version": 4
}
]
},
"config_drive": "",
"created": "2020-02-07T17:39:54Z",
"description": null,
"flavor": {
"disk": 1,
"ephemeral": 0,
"extra_specs": {},
"original_name": "m1.tiny",
"ram": 512,
"swap": 0,
"vcpus": 1
},
"hostId": "2091634baaccdc4c5a1d57069c833e402921df696b7f970791b12ec6",
"host_status": "UP",
"id": "5a0ffa96-ae59-4f82-b7a6-e0c9007cd576",
"image": {
"id": "70a599e0-31e7-49b7-b260-868f441e862b",
"links": [
{
"href": "http://openstack.example.com/6f70656e737461636b20342065766572/images/70a599e0-31e7-49b7-b260-868f441e862b",
"rel": "bookmark"
}
]
},
"key_name": null,
"links": [
{
"href": "http://openstack.example.com/v2.1/6f70656e737461636b20342065766572/servers/5a0ffa96-ae59-4f82-b7a6-e0c9007cd576",
"rel": "self"
},
{
"href": "http://openstack.example.com/6f70656e737461636b20342065766572/servers/5a0ffa96-ae59-4f82-b7a6-e0c9007cd576",
"rel": "bookmark"
}
],
"locked": false,
"locked_reason": null,
"metadata": {
"My Server Name": "Apache1"
},
"name": "new-server-test",
"os-extended-volumes:volumes_attached": [],
"progress": 0,
"security_groups": [
{
"name": "default"
}
],
"server_groups": [],
"status": "ACTIVE",
"tags": [],
"tenant_id": "6f70656e737461636b20342065766572",
"trusted_image_certificates": null,
"updated": "2020-02-07T17:39:56Z",
"user_id": "fake"
}
}

View File

@ -0,0 +1,6 @@
{
"rescue": {
"adminPass": "MySecretPass",
"rescue_image_ref": "70a599e0-31e7-49b7-b260-868f441e862b"
}
}

View File

@ -0,0 +1,5 @@
{
"rescue": {
"adminPass": "MySecretPass"
}
}

View File

@ -0,0 +1,3 @@
{
"adminPass": "MySecretPass"
}

View File

@ -0,0 +1,3 @@
{
"unrescue": null
}

View File

@ -19,7 +19,7 @@
} }
], ],
"status": "CURRENT", "status": "CURRENT",
"version": "2.86", "version": "2.87",
"min_version": "2.1", "min_version": "2.1",
"updated": "2013-07-23T11:33:21Z" "updated": "2013-07-23T11:33:21Z"
} }

View File

@ -22,7 +22,7 @@
} }
], ],
"status": "CURRENT", "status": "CURRENT",
"version": "2.86", "version": "2.87",
"min_version": "2.1", "min_version": "2.1",
"updated": "2013-07-23T11:33:21Z" "updated": "2013-07-23T11:33:21Z"
} }

View File

@ -234,6 +234,8 @@ REST_API_VERSION_HISTORY = """REST API Version History:
* 2.86 - Add support for validation of known extra specs to the * 2.86 - Add support for validation of known extra specs to the
``POST /flavors/{flavor_id}/os-extra_specs`` and ``POST /flavors/{flavor_id}/os-extra_specs`` and
``PUT /flavors/{flavor_id}/os-extra_specs/{id}`` APIs. ``PUT /flavors/{flavor_id}/os-extra_specs/{id}`` APIs.
* 2.87 - Adds support for rescuing boot from volume instances when the
compute host reports the COMPUTE_BFV_RESCUE capability trait.
""" """
# The minimum and maximum versions of the API supported # The minimum and maximum versions of the API supported
@ -242,7 +244,7 @@ REST_API_VERSION_HISTORY = """REST API Version History:
# Note(cyeoh): This only applies for the v2.1 API once microversions # Note(cyeoh): This only applies for the v2.1 API once microversions
# support is fully merged. It does not affect the V2 API. # support is fully merged. It does not affect the V2 API.
_MIN_API_VERSION = '2.1' _MIN_API_VERSION = '2.1'
_MAX_API_VERSION = '2.86' _MAX_API_VERSION = '2.87'
DEFAULT_API_VERSION = _MIN_API_VERSION DEFAULT_API_VERSION = _MIN_API_VERSION
# Almost all proxy APIs which are related to network, images and baremetal # Almost all proxy APIs which are related to network, images and baremetal

View File

@ -16,6 +16,7 @@
from webob import exc from webob import exc
from nova.api.openstack import api_version_request
from nova.api.openstack import common from nova.api.openstack import common
from nova.api.openstack.compute.schemas import rescue from nova.api.openstack.compute.schemas import rescue
from nova.api.openstack import wsgi from nova.api.openstack import wsgi
@ -56,11 +57,12 @@ class RescueController(wsgi.Controller):
rescue_image_ref = None rescue_image_ref = None
if body['rescue']: if body['rescue']:
rescue_image_ref = body['rescue'].get('rescue_image_ref') rescue_image_ref = body['rescue'].get('rescue_image_ref')
allow_bfv_rescue = api_version_request.is_supported(req, '2.87')
try: try:
self.compute_api.rescue(context, instance, self.compute_api.rescue(context, instance,
rescue_password=password, rescue_password=password,
rescue_image_ref=rescue_image_ref) rescue_image_ref=rescue_image_ref,
allow_bfv_rescue=allow_bfv_rescue)
except exception.InstanceIsLocked as e: except exception.InstanceIsLocked as e:
raise exc.HTTPConflict(explanation=e.format_message()) raise exc.HTTPConflict(explanation=e.format_message())
except exception.InstanceInvalidState as state_error: except exception.InstanceInvalidState as state_error:

View File

@ -1134,3 +1134,9 @@ Validation is only used for recognized extra spec namespaces, currently:
``accel``, ``aggregate_instance_extra_specs``, ``capabilities``, ``hw``, ``accel``, ``aggregate_instance_extra_specs``, ``capabilities``, ``hw``,
``hw_rng``, ``hw_video``, ``os``, ``pci_passthrough``, ``powervm``, ``quota``, ``hw_rng``, ``hw_video``, ``os``, ``pci_passthrough``, ``powervm``, ``quota``,
``resources``, ``trait``, and ``vmware``. ``resources``, ``trait``, and ``vmware``.
2.87
----
Adds support for rescuing boot from volume instances when the compute host
reports the ``COMPUTE_BFV_RESCUE`` capability trait.

View File

@ -0,0 +1,87 @@
{
"server": {
"OS-DCF:diskConfig": "AUTO",
"OS-EXT-AZ:availability_zone": "us-west",
"OS-EXT-SRV-ATTR:host": "compute",
"OS-EXT-SRV-ATTR:hostname": "new-server-test",
"OS-EXT-SRV-ATTR:hypervisor_hostname": "fake-mini",
"OS-EXT-SRV-ATTR:instance_name": "instance-00000001",
"OS-EXT-SRV-ATTR:kernel_id": "",
"OS-EXT-SRV-ATTR:launch_index": 0,
"OS-EXT-SRV-ATTR:ramdisk_id": "",
"OS-EXT-SRV-ATTR:reservation_id": "%(reservation_id)s",
"OS-EXT-SRV-ATTR:root_device_name": "/dev/sda",
"OS-EXT-SRV-ATTR:user_data": "IyEvYmluL2Jhc2gKL2Jpbi9zdQplY2hvICJJIGFtIGluIHlvdSEiCg==",
"OS-EXT-STS:power_state": 4,
"OS-EXT-STS:task_state": null,
"OS-EXT-STS:vm_state": "rescued",
"OS-SRV-USG:launched_at": "%(strtime)s",
"OS-SRV-USG:terminated_at": null,
"accessIPv4": "1.2.3.4",
"accessIPv6": "80fe::",
"addresses": {
"private": [
{
"OS-EXT-IPS-MAC:mac_addr": "00:0c:29:0d:11:74",
"OS-EXT-IPS:type": "fixed",
"addr": "192.168.1.30",
"version": 4
}
]
},
"config_drive": "",
"created": "%(isotime)s",
"description": null,
"flavor": {
"disk": 1,
"ephemeral": 0,
"extra_specs": {},
"original_name": "m1.tiny",
"ram": 512,
"swap": 0,
"vcpus": 1
},
"hostId": "2091634baaccdc4c5a1d57069c833e402921df696b7f970791b12ec6",
"host_status": "UP",
"id": "%(id)s",
"image": {
"id": "%(uuid)s",
"links": [
{
"href": "%(compute_endpoint)s/images/%(uuid)s",
"rel": "bookmark"
}
]
},
"key_name": null,
"links": [
{
"href": "%(versioned_compute_endpoint)s/servers/%(uuid)s",
"rel": "self"
},
{
"href": "%(compute_endpoint)s/servers/%(id)s",
"rel": "bookmark"
}
],
"locked": false,
"locked_reason": null,
"metadata": {
"My Server Name": "Apache1"
},
"name": "new-server-test",
"os-extended-volumes:volumes_attached": [],
"security_groups": [
{
"name": "default"
}
],
"server_groups": [],
"status": "RESCUE",
"tags": [],
"tenant_id": "6f70656e737461636b20342065766572",
"trusted_image_certificates": null,
"updated": "%(isotime)s",
"user_id": "fake"
}
}

View File

@ -0,0 +1,88 @@
{
"server": {
"OS-DCF:diskConfig": "AUTO",
"OS-EXT-AZ:availability_zone": "us-west",
"OS-EXT-SRV-ATTR:host": "compute",
"OS-EXT-SRV-ATTR:hostname": "new-server-test",
"OS-EXT-SRV-ATTR:hypervisor_hostname": "fake-mini",
"OS-EXT-SRV-ATTR:instance_name": "instance-00000001",
"OS-EXT-SRV-ATTR:kernel_id": "",
"OS-EXT-SRV-ATTR:launch_index": 0,
"OS-EXT-SRV-ATTR:ramdisk_id": "",
"OS-EXT-SRV-ATTR:reservation_id": "%(reservation_id)s",
"OS-EXT-SRV-ATTR:root_device_name": "/dev/sda",
"OS-EXT-SRV-ATTR:user_data": "IyEvYmluL2Jhc2gKL2Jpbi9zdQplY2hvICJJIGFtIGluIHlvdSEiCg==",
"OS-EXT-STS:power_state": 1,
"OS-EXT-STS:task_state": null,
"OS-EXT-STS:vm_state": "active",
"OS-SRV-USG:launched_at": "%(strtime)s",
"OS-SRV-USG:terminated_at": null,
"accessIPv4": "1.2.3.4",
"accessIPv6": "80fe::",
"addresses": {
"private": [
{
"OS-EXT-IPS-MAC:mac_addr": "00:0c:29:0d:11:74",
"OS-EXT-IPS:type": "fixed",
"addr": "192.168.1.30",
"version": 4
}
]
},
"config_drive": "",
"created": "%(isotime)s",
"description": null,
"flavor": {
"disk": 1,
"ephemeral": 0,
"extra_specs": {},
"original_name": "m1.tiny",
"ram": 512,
"swap": 0,
"vcpus": 1
},
"hostId": "2091634baaccdc4c5a1d57069c833e402921df696b7f970791b12ec6",
"host_status": "UP",
"id": "%(id)s",
"image": {
"id": "%(uuid)s",
"links": [
{
"href": "%(compute_endpoint)s/images/%(uuid)s",
"rel": "bookmark"
}
]
},
"key_name": null,
"links": [
{
"href": "%(versioned_compute_endpoint)s/servers/%(uuid)s",
"rel": "self"
},
{
"href": "%(compute_endpoint)s/servers/%(id)s",
"rel": "bookmark"
}
],
"locked": false,
"locked_reason": null,
"metadata": {
"My Server Name": "Apache1"
},
"name": "new-server-test",
"os-extended-volumes:volumes_attached": [],
"security_groups": [
{
"name": "default"
}
],
"server_groups": [],
"status": "ACTIVE",
"tags": [],
"tenant_id": "6f70656e737461636b20342065766572",
"trusted_image_certificates": null,
"updated": "%(isotime)s",
"user_id": "fake",
"progress": 0
}
}

View File

@ -0,0 +1,6 @@
{
"rescue": {
"adminPass": "MySecretPass",
"rescue_image_ref": "70a599e0-31e7-49b7-b260-868f441e862b"
}
}

View File

@ -0,0 +1,5 @@
{
"rescue": {
"adminPass": "%(password)s"
}
}

View File

@ -0,0 +1,3 @@
{
"adminPass": "%(password)s"
}

View File

@ -92,3 +92,9 @@ class RescueJsonTest(test_servers.ServersSampleBase):
subs['hypervisor_hostname'] = r'[\w\.\-]+' subs['hypervisor_hostname'] = r'[\w\.\-]+'
subs['cdrive'] = '.*' subs['cdrive'] = '.*'
self._verify_response('server-get-resp-unrescue', subs, response, 200) self._verify_response('server-get-resp-unrescue', subs, response, 200)
class Rescuev287JsonTest(RescueJsonTest):
"""2.87 adds support for rescuing boot from volume instances"""
microversion = '2.87'
scenarios = [('v2_87', {'api_major_version': 'v2.1'})]

View File

@ -0,0 +1,100 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from nova.tests import fixtures as nova_fixtures
from nova.tests.functional.api import client
from nova.tests.functional import integrated_helpers
class BFVRescue(integrated_helpers.ProviderUsageBaseTestCase):
"""Base class for various boot from volume rescue tests."""
def setUp(self):
super(BFVRescue, self).setUp()
self.useFixture(nova_fixtures.CinderFixture(self))
self._start_compute(host='host1')
def _create_bfv_server(self):
server_request = self._build_server(networks=[])
server_request.pop('imageRef')
server_request['block_device_mapping_v2'] = [{
'boot_index': 0,
'uuid': nova_fixtures.CinderFixture.IMAGE_BACKED_VOL,
'source_type': 'volume',
'destination_type': 'volume'}]
server = self.api.post_server({'server': server_request})
self._wait_for_state_change(server, 'ACTIVE')
return server
class DisallowBFVRescuev286(BFVRescue):
"""Asserts that BFV rescue requests fail prior to microversion 2.87.
"""
compute_driver = 'fake.MediumFakeDriver'
microversion = '2.86'
def test_bfv_rescue_not_supported(self):
server = self._create_bfv_server()
ex = self.assertRaises(client.OpenStackApiException,
self.api.post_server_action, server['id'], {'rescue': {
'rescue_image_ref': '155d900f-4e14-4e4c-a73d-069cbf4541e6'}})
self.assertEqual(400, ex.response.status_code)
self.assertIn('Cannot rescue a volume-backed instance',
ex.response.text)
class DisallowBFVRescuev286WithTrait(BFVRescue):
"""Asserts that BFV rescue requests fail prior to microversion 2.87 even
when the required COMPUTE_RESCUE_BFV trait is reported by the compute.
"""
compute_driver = 'fake.RescueBFVDriver'
microversion = '2.86'
def test_bfv_rescue_not_supported(self):
server = self._create_bfv_server()
ex = self.assertRaises(client.OpenStackApiException,
self.api.post_server_action, server['id'], {'rescue': {
'rescue_image_ref': '155d900f-4e14-4e4c-a73d-069cbf4541e6'}})
self.assertEqual(400, ex.response.status_code)
self.assertIn('Cannot rescue a volume-backed instance',
ex.response.text)
class DisallowBFVRescuev287WithoutTrait(BFVRescue):
"""Asserts that BFV rescue requests fail with microversion 2.87 (or later)
when the required COMPUTE_RESCUE_BFV trait is not reported by the compute.
"""
compute_driver = 'fake.MediumFakeDriver'
microversion = '2.87'
def test_bfv_rescue_not_supported(self):
server = self._create_bfv_server()
ex = self.assertRaises(client.OpenStackApiException,
self.api.post_server_action, server['id'], {'rescue': {
'rescue_image_ref': '155d900f-4e14-4e4c-a73d-069cbf4541e6'}})
self.assertEqual(400, ex.response.status_code)
self.assertIn('Host unable to rescue a volume-backed instance',
ex.response.text)
class AllowBFVRescuev287WithTrait(BFVRescue):
"""Asserts that BFV rescue requests pass with microversion 2.87 (or later)
when the required COMPUTE_RESCUE_BFV trait is reported by the compute.
"""
compute_driver = 'fake.RescueBFVDriver'
microversion = '2.87'
def test_bfv_rescue_supported(self):
server = self._create_bfv_server()
self.api.post_server_action(server['id'], {'rescue': {
'rescue_image_ref': '155d900f-4e14-4e4c-a73d-069cbf4541e6'}})
self._wait_for_state_change(server, 'RESCUE')

View File

@ -15,6 +15,9 @@
import mock import mock
import webob import webob
from oslo_utils.fixture import uuidsentinel as uuids
from nova.api.openstack import api_version_request
from nova.api.openstack.compute import rescue as rescue_v21 from nova.api.openstack.compute import rescue as rescue_v21
from nova import compute from nova import compute
import nova.conf import nova.conf
@ -28,7 +31,7 @@ UUID = '70f6db34-de8d-4fbd-aafb-4065bdfa6114'
def rescue(self, context, instance, rescue_password=None, def rescue(self, context, instance, rescue_password=None,
rescue_image_ref=None): rescue_image_ref=None, allow_bfv_rescue=False):
pass pass
@ -57,6 +60,9 @@ class RescueTestV21(test.NoDBTestCase):
def _set_up_controller(self): def _set_up_controller(self):
return rescue_v21.RescueController() return rescue_v21.RescueController()
def _allow_bfv_rescue(self):
return api_version_request.is_supported(self.fake_req, '2.87')
@mock.patch.object(compute.api.API, "rescue") @mock.patch.object(compute.api.API, "rescue")
def test_rescue_from_locked_server(self, mock_rescue): def test_rescue_from_locked_server(self, mock_rescue):
mock_rescue.side_effect = exception.InstanceIsLocked( mock_rescue.side_effect = exception.InstanceIsLocked(
@ -173,7 +179,8 @@ class RescueTestV21(test.NoDBTestCase):
mock.ANY, mock.ANY,
instance, instance,
rescue_password=u'ABC123', rescue_password=u'ABC123',
rescue_image_ref=self.image_uuid) rescue_image_ref=self.image_uuid,
allow_bfv_rescue=self._allow_bfv_rescue())
@mock.patch('nova.compute.api.API.rescue') @mock.patch('nova.compute.api.API.rescue')
@mock.patch('nova.api.openstack.common.get_instance') @mock.patch('nova.api.openstack.common.get_instance')
@ -187,9 +194,9 @@ class RescueTestV21(test.NoDBTestCase):
resp_json = self.controller._rescue(self.fake_req, UUID, body=body) resp_json = self.controller._rescue(self.fake_req, UUID, body=body)
self.assertEqual("ABC123", resp_json['adminPass']) self.assertEqual("ABC123", resp_json['adminPass'])
mock_compute_api_rescue.assert_called_with(mock.ANY, instance, mock_compute_api_rescue.assert_called_with(
rescue_password=u'ABC123', mock.ANY, instance, rescue_password=u'ABC123',
rescue_image_ref=None) rescue_image_ref=None, allow_bfv_rescue=self._allow_bfv_rescue())
def test_rescue_with_none(self): def test_rescue_with_none(self):
body = dict(rescue=None) body = dict(rescue=None)
@ -212,3 +219,28 @@ class RescueTestV21(test.NoDBTestCase):
self.assertRaises(exception.ValidationError, self.assertRaises(exception.ValidationError,
self.controller._rescue, self.controller._rescue,
self.fake_req, UUID, body=body) self.fake_req, UUID, body=body)
class RescueTestV287(RescueTestV21):
def setUp(self):
super(RescueTestV287, self).setUp()
v287_req = api_version_request.APIVersionRequest('2.87')
self.fake_req.api_version_request = v287_req
@mock.patch('nova.compute.api.API.rescue')
@mock.patch('nova.api.openstack.common.get_instance')
def test_allow_bfv_rescue(self, mock_get_instance, mock_compute_rescue):
instance = fake_instance.fake_instance_obj(
self.fake_req.environ['nova.context'])
mock_get_instance.return_value = instance
body = {"rescue": {"adminPass": "ABC123"}}
self.controller._rescue(self.fake_req, uuids.instance, body=body)
# Assert that allow_bfv_rescue is True for this 2.87 request
mock_get_instance.assert_called_once_with(
mock.ANY, mock.ANY, uuids.instance)
mock_compute_rescue.assert_called_with(
mock.ANY, instance, rescue_image_ref=None,
rescue_password=u'ABC123', allow_bfv_rescue=True)

View File

@ -711,6 +711,10 @@ class SameHostColdMigrateDriver(MediumFakeDriver):
supports_migrate_to_same_host=True) supports_migrate_to_same_host=True)
class RescueBFVDriver(MediumFakeDriver):
capabilities = dict(FakeDriver.capabilities, supports_bfv_rescue=True)
class PowerUpdateFakeDriver(SmallFakeDriver): class PowerUpdateFakeDriver(SmallFakeDriver):
# A specific fake driver for the power-update external event testing. # A specific fake driver for the power-update external event testing.

View File

@ -0,0 +1,10 @@
---
features:
- |
Stable device rescue for boot from volume instances is now supported
through the use of the 2.87 microversion when the compute hosting the
instance also reports the ``COMPUTE_RESCUE_BFV`` trait such as the libvirt
driver.
No changes have been made to the request or reponse parameters of the
rescue API itself.