From cc7369c74317a65adb5334c63eb0cc6ce3796e38 Mon Sep 17 00:00:00 2001 From: Mark Chappell Date: Thu, 8 Oct 2020 10:22:10 +0200 Subject: [PATCH] Add support for Block Storage (v3) VolumeType Encyption resources Change-Id: I146bdf8e80d4383f61de4b4305bdaaa2e8c80a23 --- openstack/block_storage/v3/_proxy.py | 93 +++++++++++++++++++ openstack/block_storage/v3/type.py | 35 +++++++ .../tests/unit/block_storage/v3/test_proxy.py | 28 ++++++ .../block_storage/v3/test_type_encryption.py | 61 ++++++++++++ ...rage-type_encryption-121f8a222c822fb5.yaml | 3 + 5 files changed, 220 insertions(+) create mode 100644 openstack/tests/unit/block_storage/v3/test_type_encryption.py create mode 100644 releasenotes/notes/block_storage-type_encryption-121f8a222c822fb5.yaml diff --git a/openstack/block_storage/v3/_proxy.py b/openstack/block_storage/v3/_proxy.py index 6314329a7..9b30d8b0c 100644 --- a/openstack/block_storage/v3/_proxy.py +++ b/openstack/block_storage/v3/_proxy.py @@ -175,6 +175,99 @@ class Proxy(_base_proxy.BaseBlockStorageProxy): """ return self._update(_type.Type, type, **attrs) + def get_type_encryption(self, volume_type_id): + """Get the encryption details of a volume type + + :param volume_type_id: The value can be the ID of a type or a + :class:`~openstack.volume.v3.type.Type` + instance. + + :returns: One :class:`~openstack.volume.v3.type.TypeEncryption` + :raises: :class:`~openstack.exceptions.ResourceNotFound` + when no resource can be found. + """ + volume_type = self._get_resource(_type.Type, volume_type_id) + + return self._get(_type.TypeEncryption, + volume_type_id=volume_type.id, + requires_id=False) + + def create_type_encryption(self, volume_type, **attrs): + """Create new type encryption from attributes + + :param volume_type: The value can be the ID of a type or a + :class:`~openstack.volume.v3.type.Type` + instance. + + :param dict attrs: Keyword arguments which will be used to create + a :class:`~openstack.volume.v3.type.TypeEncryption`, + comprised of the properties on the TypeEncryption + class. + + :returns: The results of type encryption creation + :rtype: :class:`~openstack.volume.v3.type.TypeEncryption` + """ + volume_type = self._get_resource(_type.Type, volume_type) + + return self._create(_type.TypeEncryption, + volume_type_id=volume_type.id, **attrs) + + def delete_type_encryption(self, encryption=None, + volume_type=None, ignore_missing=True): + """Delete type encryption attributes + + :param encryption: The value can be None or a + :class:`~openstack.volume.v3.type.TypeEncryption` + instance. If encryption_id is None then + volume_type_id must be specified. + + :param volume_type: The value can be the ID of a type or a + :class:`~openstack.volume.v3.type.Type` + instance. Required if encryption_id is None. + + :param bool ignore_missing: When set to ``False`` + :class:`~openstack.exceptions.ResourceNotFound` will be + raised when the type does not exist. + When set to ``True``, no exception will be set when + attempting to delete a nonexistent type. + + :returns: ``None`` + """ + + if volume_type: + volume_type = self._get_resource(_type.Type, volume_type) + encryption = self._get(_type.TypeEncryption, + volume_type=volume_type.id, + requires_id=False) + + self._delete(_type.TypeEncryption, encryption, + ignore_missing=ignore_missing) + + def update_type_encryption(self, encryption=None, + volume_type=None, **attrs): + """Update a type + :param encryption: The value can be None or a + :class:`~openstack.volume.v3.type.TypeEncryption` + instance. If encryption_id is None then + volume_type_id must be specified. + + :param volume_type: The value can be the ID of a type or a + :class:`~openstack.volume.v3.type.Type` + instance. Required if encryption_id is None. + :param dict attrs: The attributes to update on the type encryption. + + :returns: The updated type encryption + :rtype: :class:`~openstack.volume.v3.type.TypeEncryption` + """ + + if volume_type: + volume_type = self._get_resource(_type.Type, volume_type) + encryption = self._get(_type.TypeEncryption, + volume_type=volume_type.id, + requires_id=False) + + return self._update(_type.TypeEncryption, encryption, **attrs) + def get_volume(self, volume): """Get a single volume diff --git a/openstack/block_storage/v3/type.py b/openstack/block_storage/v3/type.py index b8fe44f85..b3b9326c0 100644 --- a/openstack/block_storage/v3/type.py +++ b/openstack/block_storage/v3/type.py @@ -38,3 +38,38 @@ class Type(resource.Resource): extra_specs = resource.Body("extra_specs", type=dict) #: a private volume-type. *Type: bool* is_public = resource.Body('os-volume-type-access:is_public', type=bool) + + +class TypeEncryption(resource.Resource): + resource_key = "encryption" + resources_key = "encryption" + base_path = "/types/%(volume_type_id)s/encryption" + + # capabilities + allow_fetch = True + allow_create = True + allow_delete = True + allow_list = False + allow_commit = True + + # Properties + #: A ID representing this type. + encryption_id = resource.Body("encryption_id", alternate_id=True) + #: The ID of the Volume Type. + volume_type_id = resource.URI("volume_type_id") + #: The Size of encryption key. + key_size = resource.Body("key_size") + #: The class that provides encryption support. + provider = resource.Body("provider") + #: Notional service where encryption is performed. + control_location = resource.Body("control_location") + #: The encryption algorithm or mode. + cipher = resource.Body("cipher") + #: The resource is deleted or not. + deleted = resource.Body("deleted") + #: The date and time when the resource was created. + created_at = resource.Body("created_at") + #: The date and time when the resource was updated. + updated_at = resource.Body("updated_at") + #: The date and time when the resource was deleted. + deleted_at = resource.Body("deleted_at") diff --git a/openstack/tests/unit/block_storage/v3/test_proxy.py b/openstack/tests/unit/block_storage/v3/test_proxy.py index c3e9cbc53..e9ffdb1aa 100644 --- a/openstack/tests/unit/block_storage/v3/test_proxy.py +++ b/openstack/tests/unit/block_storage/v3/test_proxy.py @@ -75,6 +75,34 @@ class TestVolumeProxy(test_proxy_base.TestProxyBase): def test_type_update(self): self.verify_update(self.proxy.update_type, type.Type) + def test_type_encryption_get(self): + self.verify_get(self.proxy.get_type_encryption, + type.TypeEncryption, + expected_args=[type.TypeEncryption], + expected_kwargs={ + 'volume_type_id': 'value', + 'requires_id': False + }) + + def test_type_encryption_create(self): + self.verify_create(self.proxy.create_type_encryption, + type.TypeEncryption, + method_kwargs={'volume_type': 'id'}, + expected_kwargs={'volume_type_id': 'id'} + ) + + def test_type_encryption_update(self): + self.verify_update(self.proxy.update_type_encryption, + type.TypeEncryption) + + def test_type_encryption_delete(self): + self.verify_delete(self.proxy.delete_type_encryption, + type.TypeEncryption, False) + + def test_type_encryption_delete_ignore(self): + self.verify_delete(self.proxy.delete_type_encryption, + type.TypeEncryption, True) + def test_volume_get(self): self.verify_get(self.proxy.get_volume, volume.Volume) diff --git a/openstack/tests/unit/block_storage/v3/test_type_encryption.py b/openstack/tests/unit/block_storage/v3/test_type_encryption.py new file mode 100644 index 000000000..98733ddb6 --- /dev/null +++ b/openstack/tests/unit/block_storage/v3/test_type_encryption.py @@ -0,0 +1,61 @@ +# 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 openstack.tests.unit import base + +from openstack.block_storage.v3 import type + +FAKE_ID = "479394ab-2f25-416e-8f58-721d8e5e29de" +TYPE_ID = "22373aed-c4a8-4072-b66c-bf0a90dc9a12" +TYPE_ENC = { + "key_size": 256, + "volume_type_id": TYPE_ID, + "encryption_id": FAKE_ID, + "provider": "nova.volume.encryptors.luks.LuksEncryptor", + "control_location": "front-end", + "cipher": "aes-xts-plain64", + "deleted": False, + "created_at": "2020-10-07T07:52:30.000000", + "updated_at": "2020-10-08T07:42:45.000000", + "deleted_at": None, +} + + +class TestTypeEncryption(base.TestCase): + + def test_basic(self): + sot = type.TypeEncryption(**TYPE_ENC) + self.assertEqual("encryption", sot.resource_key) + self.assertEqual("encryption", sot.resources_key) + self.assertEqual("/types/%(volume_type_id)s/encryption", sot.base_path) + self.assertTrue(sot.allow_create) + self.assertTrue(sot.allow_fetch) + self.assertTrue(sot.allow_delete) + self.assertFalse(sot.allow_list) + self.assertTrue(sot.allow_commit) + + def test_new(self): + sot = type.TypeEncryption.new(encryption_id=FAKE_ID) + self.assertEqual(FAKE_ID, sot.encryption_id) + + def test_create(self): + sot = type.TypeEncryption(**TYPE_ENC) + self.assertEqual(TYPE_ENC["volume_type_id"], sot.volume_type_id) + self.assertEqual(TYPE_ENC["encryption_id"], sot.encryption_id) + self.assertEqual(TYPE_ENC["key_size"], sot.key_size) + self.assertEqual(TYPE_ENC["provider"], sot.provider) + self.assertEqual(TYPE_ENC["control_location"], sot.control_location) + self.assertEqual(TYPE_ENC["cipher"], sot.cipher) + self.assertEqual(TYPE_ENC["deleted"], sot.deleted) + self.assertEqual(TYPE_ENC["created_at"], sot.created_at) + self.assertEqual(TYPE_ENC["updated_at"], sot.updated_at) + self.assertEqual(TYPE_ENC["deleted_at"], sot.deleted_at) diff --git a/releasenotes/notes/block_storage-type_encryption-121f8a222c822fb5.yaml b/releasenotes/notes/block_storage-type_encryption-121f8a222c822fb5.yaml new file mode 100644 index 000000000..deed2290d --- /dev/null +++ b/releasenotes/notes/block_storage-type_encryption-121f8a222c822fb5.yaml @@ -0,0 +1,3 @@ +--- +features: + - Add support for block storage type encryption parameters.