diff --git a/api-ref/source/v3/ext-backups.inc b/api-ref/source/v3/ext-backups.inc index 0a6986ee476..01949258a15 100644 --- a/api-ref/source/v3/ext-backups.inc +++ b/api-ref/source/v3/ext-backups.inc @@ -98,6 +98,7 @@ Response Parameters - count: count - metadata: metadata_backup - user_id: user_id_backup + - encryption_key_id: encryption_key_id Response Example ---------------- @@ -157,6 +158,7 @@ Response Parameters - os-backup-project-attr:project_id: os-backup-project-attr:project_id - metadata: metadata_backup - user_id: user_id_backup + - encryption_key_id: encryption_key_id Response Example ---------------- diff --git a/api-ref/source/v3/parameters.yaml b/api-ref/source/v3/parameters.yaml index c521e0b0630..1187ae7b291 100644 --- a/api-ref/source/v3/parameters.yaml +++ b/api-ref/source/v3/parameters.yaml @@ -1140,6 +1140,12 @@ encryption_id_body: in: body required: true type: string +encryption_key_id: + description: | + The UUID of the encryption key. + in: body + required: false + type: string event_id: description: | The id of the event to this message, this id could diff --git a/api-ref/source/v3/samples/versions/version-show-response.json b/api-ref/source/v3/samples/versions/version-show-response.json index 1cd449cbbbe..df31cb2f2b5 100644 --- a/api-ref/source/v3/samples/versions/version-show-response.json +++ b/api-ref/source/v3/samples/versions/version-show-response.json @@ -21,8 +21,8 @@ ], "min_version": "3.0", "status": "CURRENT", - "updated": "2020-11-19T08:56:00Z", - "version": "3.63" + "updated": "2021-02-03T00:00:00Z", + "version": "3.64" } ] } diff --git a/api-ref/source/v3/samples/versions/versions-response.json b/api-ref/source/v3/samples/versions/versions-response.json index 795b50572e9..b1c96e54281 100644 --- a/api-ref/source/v3/samples/versions/versions-response.json +++ b/api-ref/source/v3/samples/versions/versions-response.json @@ -45,8 +45,8 @@ ], "min_version": "3.0", "status": "CURRENT", - "updated": "2020-11-19T08:56:00Z", - "version": "3.63" + "updated": "2021-02-03T00:00:00Z", + "version": "3.64" } ] } diff --git a/api-ref/source/v3/volumes-v3-volumes.inc b/api-ref/source/v3/volumes-v3-volumes.inc index dadfbab9267..49cbc4b0d7f 100644 --- a/api-ref/source/v3/volumes-v3-volumes.inc +++ b/api-ref/source/v3/volumes-v3-volumes.inc @@ -109,6 +109,7 @@ Response Parameters - availability_zone: availability_zone - os-vol-host-attr:host: os-vol-host-attr:host - encrypted: encrypted + - encryption_key_id: encryption_key_id - updated_at: updated_at - replication_status: replication_status - snapshot_id: snapshot_id @@ -355,6 +356,7 @@ Response Parameters - availability_zone: availability_zone - os-vol-host-attr:host: os-vol-host-attr:host - encrypted: encrypted + - encryption_key_id: encryption_key_id - updated_at: updated_at - replication_status: replication_status - snapshot_id: snapshot_id diff --git a/cinder/api/microversions.py b/cinder/api/microversions.py index 8c4268e36e1..e8605c691b5 100644 --- a/cinder/api/microversions.py +++ b/cinder/api/microversions.py @@ -167,6 +167,8 @@ DEFAULT_TYPE_OVERRIDES = '3.62' VOLUME_TYPE_ID_IN_VOLUME_DETAIL = '3.63' +ENCRYPTION_KEY_ID_IN_DETAILS = '3.64' + def get_mv_header(version): """Gets a formatted HTTP microversion header. diff --git a/cinder/api/openstack/api_version_request.py b/cinder/api/openstack/api_version_request.py index 3513d5fe7e1..bde0205d45e 100644 --- a/cinder/api/openstack/api_version_request.py +++ b/cinder/api/openstack/api_version_request.py @@ -147,6 +147,7 @@ REST_API_VERSION_HISTORY = """ in the volume details. This MV affects the volume detail list ("GET /v3/{project_id}/volumes/detail") and volume-show ("GET /v3/{project_id}/volumes/{volume_id}") calls. + * 3.64 - Include 'encryption_key_id' in volume and backup details """ # The minimum and maximum versions of the API supported @@ -154,9 +155,9 @@ REST_API_VERSION_HISTORY = """ # minimum version of the API supported. # Explicitly using /v2 endpoints will still work _MIN_API_VERSION = "3.0" -_MAX_API_VERSION = "3.63" +_MAX_API_VERSION = "3.64" _LEGACY_API_VERSION2 = "2.0" -UPDATED = "2020-11-19T08:56:00Z" +UPDATED = "2021-02-03T00:00:00Z" # NOTE(cyeoh): min and max versions declared as functions so we can diff --git a/cinder/api/openstack/rest_api_version_history.rst b/cinder/api/openstack/rest_api_version_history.rst index 4f6e78ac4ec..c4d1c4130ed 100644 --- a/cinder/api/openstack/rest_api_version_history.rst +++ b/cinder/api/openstack/rest_api_version_history.rst @@ -485,3 +485,8 @@ value. Includes volume type ID in the volume-show and volume-detail-list JSON responses. Before this microversion, Cinder returns only the volume type name in the volume details. + +3.64 +---- +Include the ``encryption_key_id`` in volume and backup details when the +associated volume is encrypted. diff --git a/cinder/api/v3/views/backups.py b/cinder/api/v3/views/backups.py index 2a428ec64c7..07038236e31 100644 --- a/cinder/api/v3/views/backups.py +++ b/cinder/api/v3/views/backups.py @@ -15,6 +15,7 @@ from cinder.api import microversions as mv from cinder.api.views import backups as views_v2 +from cinder.common import constants as cinder_constants class ViewBuilder(views_v2.ViewBuilder): @@ -29,4 +30,11 @@ class ViewBuilder(views_v2.ViewBuilder): req_version = request.api_version_request if req_version.matches(mv.BACKUP_METADATA): backup_ref['backup']['metadata'] = backup.metadata + + if req_version.matches(mv.ENCRYPTION_KEY_ID_IN_DETAILS, None): + encryption_key_id = backup.get('encryption_key_id', None) + if (encryption_key_id and + encryption_key_id != cinder_constants.FIXED_KEY_ID): + backup_ref['backup']['encryption_key_id'] = encryption_key_id + return backup_ref diff --git a/cinder/api/v3/views/volumes.py b/cinder/api/v3/views/volumes.py index 6e61146337c..ed171c7bde1 100644 --- a/cinder/api/v3/views/volumes.py +++ b/cinder/api/v3/views/volumes.py @@ -15,6 +15,7 @@ from cinder.api import microversions as mv from cinder.api.v2.views import volumes as views_v2 +from cinder.common import constants as cinder_constants class ViewBuilder(views_v2.ViewBuilder): @@ -70,6 +71,12 @@ class ViewBuilder(views_v2.ViewBuilder): volume_ref[ 'volume']["volume_type_id"] = volume['volume_type'].get('id') + if req_version.matches(mv.ENCRYPTION_KEY_ID_IN_DETAILS, None): + encryption_key_id = volume.get('encryption_key_id', None) + if (encryption_key_id and + encryption_key_id != cinder_constants.FIXED_KEY_ID): + volume_ref['volume']['encryption_key_id'] = encryption_key_id + return volume_ref def _list_view(self, func, request, volumes, volume_count, diff --git a/cinder/common/constants.py b/cinder/common/constants.py index 46f36f0124d..61c84132d74 100644 --- a/cinder/common/constants.py +++ b/cinder/common/constants.py @@ -26,3 +26,6 @@ SCHEDULER_TOPIC = SCHEDULER_BINARY VOLUME_TOPIC = VOLUME_BINARY BACKUP_TOPIC = BACKUP_BINARY LOG_BINARIES = (SCHEDULER_BINARY, VOLUME_BINARY, BACKUP_BINARY, API_BINARY) + +# The encryption key ID used by the legacy fixed-key ConfKeyMgr +FIXED_KEY_ID = '00000000-0000-0000-0000-000000000000' diff --git a/cinder/tests/unit/api/v3/test_backups.py b/cinder/tests/unit/api/v3/test_backups.py index 6597fc5afb9..f2c9d81f649 100644 --- a/cinder/tests/unit/api/v3/test_backups.py +++ b/cinder/tests/unit/api/v3/test_backups.py @@ -32,6 +32,7 @@ from cinder import context from cinder import exception from cinder.objects import fields from cinder.tests.unit.api import fakes +from cinder.tests.unit.api.v3.test_volumes import ENCRYPTION_KEY_ID_IN_DETAILS from cinder.tests.unit import fake_constants as fake from cinder.tests.unit import test from cinder.tests.unit import utils as test_utils @@ -286,6 +287,25 @@ class BackupsControllerAPITestCase(test.TestCase): else: self.assertIn('metadata', backup_get) + @ddt.data(*ENCRYPTION_KEY_ID_IN_DETAILS) + @ddt.unpack + def test_backup_show_with_encryption_key_id(self, + expected_in_details, + encryption_key_id, + version): + backup = test_utils.create_backup(self.ctxt, + encryption_key_id=encryption_key_id) + self.addCleanup(backup.destroy) + + url = '/v3/%s/backups/%s' % (fake.PROJECT_ID, backup.id) + req = fakes.HTTPRequest.blank(url, version=version) + backup_details = self.controller.show(req, backup.id)['backup'] + + if expected_in_details: + self.assertIn('encryption_key_id', backup_details) + else: + self.assertNotIn('encryption_key_id', backup_details) + def test_backup_update_with_null_validate(self): backup = test_utils.create_backup( self.ctxt, diff --git a/cinder/tests/unit/api/v3/test_volumes.py b/cinder/tests/unit/api/v3/test_volumes.py index 06b936a4914..c2f787021ac 100644 --- a/cinder/tests/unit/api/v3/test_volumes.py +++ b/cinder/tests/unit/api/v3/test_volumes.py @@ -31,6 +31,7 @@ from cinder.api import microversions as mv from cinder.api.v2.views.volumes import ViewBuilder from cinder.api.v3 import volumes from cinder.backup import api as backup_api +from cinder.common import constants as cinder_constants from cinder import context from cinder import db from cinder import exception @@ -50,6 +51,29 @@ from cinder.volume import api as vol_get DEFAULT_AZ = "zone1:host1" +# DDT data for testing whether an 'encryption_key_id' should appear in a +# volume's or backup's details (also used by test_backups.py). +ENCRYPTION_KEY_ID_IN_DETAILS = { + 'expected_in_details': True, + 'encryption_key_id': fake.ENCRYPTION_KEY_ID, + 'version': mv.ENCRYPTION_KEY_ID_IN_DETAILS, +}, { + # No encryption ID to display + 'expected_in_details': False, + 'encryption_key_id': None, + 'version': mv.ENCRYPTION_KEY_ID_IN_DETAILS, +}, { + # Fixed key ID should not be displayed + 'expected_in_details': False, + 'encryption_key_id': cinder_constants.FIXED_KEY_ID, + 'version': mv.ENCRYPTION_KEY_ID_IN_DETAILS, +}, { + # Unsupported microversion + 'expected_in_details': False, + 'encryption_key_id': fake.ENCRYPTION_KEY_ID, + 'version': mv.get_prior_version(mv.ENCRYPTION_KEY_ID_IN_DETAILS), +} + @ddt.ddt class VolumeApiTest(test.TestCase): @@ -856,6 +880,26 @@ class VolumeApiTest(test.TestCase): else: self.assertNotIn('provider_id', res_dict['volume']) + @ddt.data(*ENCRYPTION_KEY_ID_IN_DETAILS) + @ddt.unpack + def test_volume_show_with_encryption_key_id(self, + expected_in_details, + encryption_key_id, + version): + volume = test_utils.create_volume(self.ctxt, + testcase_instance=self, + volume_type_id=None, + encryption_key_id=encryption_key_id) + + req = fakes.HTTPRequest.blank('/v3/volumes/%s' % volume.id, + version=version) + volume_details = self.controller.show(req, volume.id)['volume'] + + if expected_in_details: + self.assertIn('encryption_key_id', volume_details) + else: + self.assertNotIn('encryption_key_id', volume_details) + def _fake_create_volume(self, size=1): vol = { 'display_name': 'fake_volume1', diff --git a/releasenotes/notes/add-encryption-key-id-to-details-e721977fba0f2b51.yaml b/releasenotes/notes/add-encryption-key-id-to-details-e721977fba0f2b51.yaml new file mode 100644 index 00000000000..f7fd87fc9ae --- /dev/null +++ b/releasenotes/notes/add-encryption-key-id-to-details-e721977fba0f2b51.yaml @@ -0,0 +1,6 @@ +--- +features: + - | + Starting with API microversion 3.64, an ``encryption_key_id`` attribute + is included in the response body of volume and backup details when the + associated volume is encrypted.