From 24106290e665bfdc4aa3a2937f0a4b5462fb220e Mon Sep 17 00:00:00 2001 From: Lee Yarwood Date: Mon, 6 Jan 2020 19:29:26 +0000 Subject: [PATCH] api: Introduce microverion 2.87 allowing boot from volume rescue Building on Ic2ad1468d31b7707b7f8f2b845a9cf47d9d076d5, when requested this microversion will allow boot from volume requests to proceed when the COMPUTE_RESCUE_BFV trait is also reported by the compute the instance currently resides on. Implements: blueprint virt-bfv-instance-rescue Change-Id: I3242fec1547693078cf36c3637116f8c41f1d0bc --- .../v2.87/server-get-resp-rescue.json | 87 +++++++++++++++ .../v2.87/server-get-resp-unrescue.json | 88 +++++++++++++++ .../server-rescue-req-with-image-ref.json | 6 ++ .../os-rescue/v2.87/server-rescue-req.json | 5 + .../os-rescue/v2.87/server-rescue.json | 3 + .../os-rescue/v2.87/server-unrescue-req.json | 3 + .../versions/v21-version-get-resp.json | 2 +- .../versions/versions-get-resp.json | 2 +- nova/api/openstack/api_version_request.py | 4 +- nova/api/openstack/compute/rescue.py | 6 +- .../compute/rest_api_version_history.rst | 6 ++ .../v2.87/server-get-resp-rescue.json.tpl | 87 +++++++++++++++ .../v2.87/server-get-resp-unrescue.json.tpl | 88 +++++++++++++++ .../server-rescue-req-with-image-ref.json.tpl | 6 ++ .../v2.87/server-rescue-req.json.tpl | 5 + .../os-rescue/v2.87/server-rescue.json.tpl | 3 + .../v2.87/server-unrescue-req.json.tpl | 3 + .../api_sample_tests/test_rescue.py | 6 ++ nova/tests/functional/test_server_rescue.py | 100 ++++++++++++++++++ .../unit/api/openstack/compute/test_rescue.py | 42 +++++++- nova/virt/fake.py | 4 + .../stable_rescue_bfv-cd0e9f0f7e9eaa25.yaml | 10 ++ 22 files changed, 556 insertions(+), 10 deletions(-) create mode 100644 doc/api_samples/os-rescue/v2.87/server-get-resp-rescue.json create mode 100644 doc/api_samples/os-rescue/v2.87/server-get-resp-unrescue.json create mode 100644 doc/api_samples/os-rescue/v2.87/server-rescue-req-with-image-ref.json create mode 100644 doc/api_samples/os-rescue/v2.87/server-rescue-req.json create mode 100644 doc/api_samples/os-rescue/v2.87/server-rescue.json create mode 100644 doc/api_samples/os-rescue/v2.87/server-unrescue-req.json create mode 100644 nova/tests/functional/api_sample_tests/api_samples/os-rescue/v2.87/server-get-resp-rescue.json.tpl create mode 100644 nova/tests/functional/api_sample_tests/api_samples/os-rescue/v2.87/server-get-resp-unrescue.json.tpl create mode 100644 nova/tests/functional/api_sample_tests/api_samples/os-rescue/v2.87/server-rescue-req-with-image-ref.json.tpl create mode 100644 nova/tests/functional/api_sample_tests/api_samples/os-rescue/v2.87/server-rescue-req.json.tpl create mode 100644 nova/tests/functional/api_sample_tests/api_samples/os-rescue/v2.87/server-rescue.json.tpl create mode 100644 nova/tests/functional/api_sample_tests/api_samples/os-rescue/v2.87/server-unrescue-req.json.tpl create mode 100644 nova/tests/functional/test_server_rescue.py create mode 100644 releasenotes/notes/stable_rescue_bfv-cd0e9f0f7e9eaa25.yaml diff --git a/doc/api_samples/os-rescue/v2.87/server-get-resp-rescue.json b/doc/api_samples/os-rescue/v2.87/server-get-resp-rescue.json new file mode 100644 index 000000000000..3500b3ebd8d0 --- /dev/null +++ b/doc/api_samples/os-rescue/v2.87/server-get-resp-rescue.json @@ -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" + } +} \ No newline at end of file diff --git a/doc/api_samples/os-rescue/v2.87/server-get-resp-unrescue.json b/doc/api_samples/os-rescue/v2.87/server-get-resp-unrescue.json new file mode 100644 index 000000000000..3388fb55a5ba --- /dev/null +++ b/doc/api_samples/os-rescue/v2.87/server-get-resp-unrescue.json @@ -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" + } +} \ No newline at end of file diff --git a/doc/api_samples/os-rescue/v2.87/server-rescue-req-with-image-ref.json b/doc/api_samples/os-rescue/v2.87/server-rescue-req-with-image-ref.json new file mode 100644 index 000000000000..1cfab5287283 --- /dev/null +++ b/doc/api_samples/os-rescue/v2.87/server-rescue-req-with-image-ref.json @@ -0,0 +1,6 @@ +{ + "rescue": { + "adminPass": "MySecretPass", + "rescue_image_ref": "70a599e0-31e7-49b7-b260-868f441e862b" + } +} \ No newline at end of file diff --git a/doc/api_samples/os-rescue/v2.87/server-rescue-req.json b/doc/api_samples/os-rescue/v2.87/server-rescue-req.json new file mode 100644 index 000000000000..3796600282f1 --- /dev/null +++ b/doc/api_samples/os-rescue/v2.87/server-rescue-req.json @@ -0,0 +1,5 @@ +{ + "rescue": { + "adminPass": "MySecretPass" + } +} \ No newline at end of file diff --git a/doc/api_samples/os-rescue/v2.87/server-rescue.json b/doc/api_samples/os-rescue/v2.87/server-rescue.json new file mode 100644 index 000000000000..6cd942395fea --- /dev/null +++ b/doc/api_samples/os-rescue/v2.87/server-rescue.json @@ -0,0 +1,3 @@ +{ + "adminPass": "MySecretPass" +} \ No newline at end of file diff --git a/doc/api_samples/os-rescue/v2.87/server-unrescue-req.json b/doc/api_samples/os-rescue/v2.87/server-unrescue-req.json new file mode 100644 index 000000000000..cafc9b13a84d --- /dev/null +++ b/doc/api_samples/os-rescue/v2.87/server-unrescue-req.json @@ -0,0 +1,3 @@ +{ + "unrescue": null +} \ No newline at end of file diff --git a/doc/api_samples/versions/v21-version-get-resp.json b/doc/api_samples/versions/v21-version-get-resp.json index 7e05eed56d5e..abfbcf4f352e 100644 --- a/doc/api_samples/versions/v21-version-get-resp.json +++ b/doc/api_samples/versions/v21-version-get-resp.json @@ -19,7 +19,7 @@ } ], "status": "CURRENT", - "version": "2.86", + "version": "2.87", "min_version": "2.1", "updated": "2013-07-23T11:33:21Z" } diff --git a/doc/api_samples/versions/versions-get-resp.json b/doc/api_samples/versions/versions-get-resp.json index 3555b6b72073..0e870df3ae21 100644 --- a/doc/api_samples/versions/versions-get-resp.json +++ b/doc/api_samples/versions/versions-get-resp.json @@ -22,7 +22,7 @@ } ], "status": "CURRENT", - "version": "2.86", + "version": "2.87", "min_version": "2.1", "updated": "2013-07-23T11:33:21Z" } diff --git a/nova/api/openstack/api_version_request.py b/nova/api/openstack/api_version_request.py index ac187e6d5ffa..037b01124bcb 100644 --- a/nova/api/openstack/api_version_request.py +++ b/nova/api/openstack/api_version_request.py @@ -234,6 +234,8 @@ REST_API_VERSION_HISTORY = """REST API Version History: * 2.86 - Add support for validation of known extra specs to the ``POST /flavors/{flavor_id}/os-extra_specs`` and ``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 @@ -242,7 +244,7 @@ REST_API_VERSION_HISTORY = """REST API Version History: # Note(cyeoh): This only applies for the v2.1 API once microversions # support is fully merged. It does not affect the V2 API. _MIN_API_VERSION = '2.1' -_MAX_API_VERSION = '2.86' +_MAX_API_VERSION = '2.87' DEFAULT_API_VERSION = _MIN_API_VERSION # Almost all proxy APIs which are related to network, images and baremetal diff --git a/nova/api/openstack/compute/rescue.py b/nova/api/openstack/compute/rescue.py index 6afb62c008c2..ab2da84d63a0 100644 --- a/nova/api/openstack/compute/rescue.py +++ b/nova/api/openstack/compute/rescue.py @@ -16,6 +16,7 @@ from webob import exc +from nova.api.openstack import api_version_request from nova.api.openstack import common from nova.api.openstack.compute.schemas import rescue from nova.api.openstack import wsgi @@ -56,11 +57,12 @@ class RescueController(wsgi.Controller): rescue_image_ref = None if body['rescue']: rescue_image_ref = body['rescue'].get('rescue_image_ref') - + allow_bfv_rescue = api_version_request.is_supported(req, '2.87') try: self.compute_api.rescue(context, instance, 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: raise exc.HTTPConflict(explanation=e.format_message()) except exception.InstanceInvalidState as state_error: diff --git a/nova/api/openstack/compute/rest_api_version_history.rst b/nova/api/openstack/compute/rest_api_version_history.rst index 5168890b2346..c9206fffcc5d 100644 --- a/nova/api/openstack/compute/rest_api_version_history.rst +++ b/nova/api/openstack/compute/rest_api_version_history.rst @@ -1134,3 +1134,9 @@ Validation is only used for recognized extra spec namespaces, namely: ``accel``, ``aggregate_instance_extra_specs``, ``capabilities``, ``hw``, ``hw_rng``, ``hw_video``, ``os``, ``pci_passthrough``, ``powervm``, ``quota``, ``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. diff --git a/nova/tests/functional/api_sample_tests/api_samples/os-rescue/v2.87/server-get-resp-rescue.json.tpl b/nova/tests/functional/api_sample_tests/api_samples/os-rescue/v2.87/server-get-resp-rescue.json.tpl new file mode 100644 index 000000000000..34343fe8c246 --- /dev/null +++ b/nova/tests/functional/api_sample_tests/api_samples/os-rescue/v2.87/server-get-resp-rescue.json.tpl @@ -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" + } +} diff --git a/nova/tests/functional/api_sample_tests/api_samples/os-rescue/v2.87/server-get-resp-unrescue.json.tpl b/nova/tests/functional/api_sample_tests/api_samples/os-rescue/v2.87/server-get-resp-unrescue.json.tpl new file mode 100644 index 000000000000..02ac1caa9dfe --- /dev/null +++ b/nova/tests/functional/api_sample_tests/api_samples/os-rescue/v2.87/server-get-resp-unrescue.json.tpl @@ -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 + } +} diff --git a/nova/tests/functional/api_sample_tests/api_samples/os-rescue/v2.87/server-rescue-req-with-image-ref.json.tpl b/nova/tests/functional/api_sample_tests/api_samples/os-rescue/v2.87/server-rescue-req-with-image-ref.json.tpl new file mode 100644 index 000000000000..8a4ad0d52a9f --- /dev/null +++ b/nova/tests/functional/api_sample_tests/api_samples/os-rescue/v2.87/server-rescue-req-with-image-ref.json.tpl @@ -0,0 +1,6 @@ +{ + "rescue": { + "adminPass": "MySecretPass", + "rescue_image_ref": "70a599e0-31e7-49b7-b260-868f441e862b" + } +} diff --git a/nova/tests/functional/api_sample_tests/api_samples/os-rescue/v2.87/server-rescue-req.json.tpl b/nova/tests/functional/api_sample_tests/api_samples/os-rescue/v2.87/server-rescue-req.json.tpl new file mode 100644 index 000000000000..f946b74f53c5 --- /dev/null +++ b/nova/tests/functional/api_sample_tests/api_samples/os-rescue/v2.87/server-rescue-req.json.tpl @@ -0,0 +1,5 @@ +{ + "rescue": { + "adminPass": "%(password)s" + } +} diff --git a/nova/tests/functional/api_sample_tests/api_samples/os-rescue/v2.87/server-rescue.json.tpl b/nova/tests/functional/api_sample_tests/api_samples/os-rescue/v2.87/server-rescue.json.tpl new file mode 100644 index 000000000000..0da07da5b8f0 --- /dev/null +++ b/nova/tests/functional/api_sample_tests/api_samples/os-rescue/v2.87/server-rescue.json.tpl @@ -0,0 +1,3 @@ +{ + "adminPass": "%(password)s" +} diff --git a/nova/tests/functional/api_sample_tests/api_samples/os-rescue/v2.87/server-unrescue-req.json.tpl b/nova/tests/functional/api_sample_tests/api_samples/os-rescue/v2.87/server-unrescue-req.json.tpl new file mode 100644 index 000000000000..cafc9b13a84d --- /dev/null +++ b/nova/tests/functional/api_sample_tests/api_samples/os-rescue/v2.87/server-unrescue-req.json.tpl @@ -0,0 +1,3 @@ +{ + "unrescue": null +} \ No newline at end of file diff --git a/nova/tests/functional/api_sample_tests/test_rescue.py b/nova/tests/functional/api_sample_tests/test_rescue.py index 3c5545c297ff..06ed1d2c3271 100644 --- a/nova/tests/functional/api_sample_tests/test_rescue.py +++ b/nova/tests/functional/api_sample_tests/test_rescue.py @@ -92,3 +92,9 @@ class RescueJsonTest(test_servers.ServersSampleBase): subs['hypervisor_hostname'] = r'[\w\.\-]+' subs['cdrive'] = '.*' 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'})] diff --git a/nova/tests/functional/test_server_rescue.py b/nova/tests/functional/test_server_rescue.py new file mode 100644 index 000000000000..fa96c10344ab --- /dev/null +++ b/nova/tests/functional/test_server_rescue.py @@ -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') diff --git a/nova/tests/unit/api/openstack/compute/test_rescue.py b/nova/tests/unit/api/openstack/compute/test_rescue.py index 7f229ea3569e..fd2cdfa13b9d 100644 --- a/nova/tests/unit/api/openstack/compute/test_rescue.py +++ b/nova/tests/unit/api/openstack/compute/test_rescue.py @@ -15,6 +15,9 @@ import mock 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 import compute import nova.conf @@ -28,7 +31,7 @@ UUID = '70f6db34-de8d-4fbd-aafb-4065bdfa6114' def rescue(self, context, instance, rescue_password=None, - rescue_image_ref=None): + rescue_image_ref=None, allow_bfv_rescue=False): pass @@ -57,6 +60,9 @@ class RescueTestV21(test.NoDBTestCase): def _set_up_controller(self): 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") def test_rescue_from_locked_server(self, mock_rescue): mock_rescue.side_effect = exception.InstanceIsLocked( @@ -173,7 +179,8 @@ class RescueTestV21(test.NoDBTestCase): mock.ANY, instance, 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.api.openstack.common.get_instance') @@ -187,9 +194,9 @@ class RescueTestV21(test.NoDBTestCase): resp_json = self.controller._rescue(self.fake_req, UUID, body=body) self.assertEqual("ABC123", resp_json['adminPass']) - mock_compute_api_rescue.assert_called_with(mock.ANY, instance, - rescue_password=u'ABC123', - rescue_image_ref=None) + mock_compute_api_rescue.assert_called_with( + mock.ANY, instance, rescue_password=u'ABC123', + rescue_image_ref=None, allow_bfv_rescue=self._allow_bfv_rescue()) def test_rescue_with_none(self): body = dict(rescue=None) @@ -212,3 +219,28 @@ class RescueTestV21(test.NoDBTestCase): self.assertRaises(exception.ValidationError, self.controller._rescue, 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) diff --git a/nova/virt/fake.py b/nova/virt/fake.py index 2c668af8755e..358779e5f15e 100644 --- a/nova/virt/fake.py +++ b/nova/virt/fake.py @@ -711,6 +711,10 @@ class SameHostColdMigrateDriver(MediumFakeDriver): supports_migrate_to_same_host=True) +class RescueBFVDriver(MediumFakeDriver): + capabilities = dict(FakeDriver.capabilities, supports_bfv_rescue=True) + + class PowerUpdateFakeDriver(SmallFakeDriver): # A specific fake driver for the power-update external event testing. diff --git a/releasenotes/notes/stable_rescue_bfv-cd0e9f0f7e9eaa25.yaml b/releasenotes/notes/stable_rescue_bfv-cd0e9f0f7e9eaa25.yaml new file mode 100644 index 000000000000..80aefaa2c0db --- /dev/null +++ b/releasenotes/notes/stable_rescue_bfv-cd0e9f0f7e9eaa25.yaml @@ -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.