From fe868063d757ee4d4303472c753e320679eaed23 Mon Sep 17 00:00:00 2001 From: Kiran Pawar Date: 2025年6月11日 13:33:02 +0000 Subject: [PATCH] Add tests for encryption_key_ref Positive test - 1. create barbican secret and create share using secret UUID Negative test - 1. invalid encryption key ref 2. invalid share type extra-spec 3. absent encryption key ref partially-implements: blueprint share-encryption Depends-On: https://review.opendev.org/c/openstack/requirements/+/963685 Change-Id: I3145f9cd6847464b2920f1b0a35e6c211e45b26e Signed-off-by: Kiran Pawar --- .../common/barbican_client_mgr.py | 73 ++++++++++++++ manila_tempest_tests/config.py | 10 +- .../services/share/v2/json/shares_client.py | 8 +- .../tests/api/admin/test_quotas.py | 26 +++++ .../tests/api/admin/test_quotas_negative.py | 1 + .../tests/api/test_share_encryption.py | 94 +++++++++++++++++++ .../api/test_share_encryption_negative.py | 74 +++++++++++++++ manila_tempest_tests/utils.py | 5 + requirements.txt | 3 + zuul.d/manila-tempest-jobs.yaml | 3 + 10 files changed, 294 insertions(+), 3 deletions(-) create mode 100644 manila_tempest_tests/common/barbican_client_mgr.py create mode 100644 manila_tempest_tests/tests/api/test_share_encryption.py create mode 100644 manila_tempest_tests/tests/api/test_share_encryption_negative.py diff --git a/manila_tempest_tests/common/barbican_client_mgr.py b/manila_tempest_tests/common/barbican_client_mgr.py new file mode 100644 index 00000000..ee8b53fc --- /dev/null +++ b/manila_tempest_tests/common/barbican_client_mgr.py @@ -0,0 +1,73 @@ +# Copyright 2025 Cloudifcation GmbH. All rights reserved. +# +# 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. + +import base64 +import secrets + +from oslo_log import log as logging +from tempest import config +from tempest.lib.services import clients +from tempest import test + +CONF = config.CONF +LOG = logging.getLogger(__name__) + + +class BarbicanClientManager(test.BaseTestCase): + """Class for interacting with the barbican service. + + This class is an abstraction for interacting with the barbican service. + """ + + credentials = ['primary',] + + @classmethod + def setup_clients(cls, tempest_client_mgr): + super(BarbicanClientManager, cls).setup_clients() + if CONF.identity.auth_version == 'v3': + auth_uri = CONF.identity.uri_v3 + else: + auth_uri = CONF.identity.uri + service_clients = clients.ServiceClients( + tempest_client_mgr.credentials, + auth_uri) + cls.secret_client = service_clients.secret_v1.SecretClient( + service='key-manager') + + @classmethod + def ref_to_uuid(cls, href): + return href.split('/')[-1] + + def store_secret(self): + """Store a secret in barbican. + + :returns: The barbican secret_ref. + """ + + key = secrets.token_bytes(32) + + manila_secret = self.secret_client.create_secret( + algorithm='AES', + bit_length=256, + secret_type='symmetric', + payload=base64.b64encode(key).decode(), + payload_content_type='application/octet-stream', + payload_content_encoding='base64', + mode='CBC' + ) + LOG.debug('Manila Secret has ref %s', manila_secret.get('secret_ref')) + return manila_secret.get('secret_ref') + + def delete_secret(self, secret_ref): + self.secret_client.delete_secret(secret_ref) diff --git a/manila_tempest_tests/config.py b/manila_tempest_tests/config.py index 60d4164b..5539bb51 100644 --- a/manila_tempest_tests/config.py +++ b/manila_tempest_tests/config.py @@ -40,7 +40,7 @@ ShareGroup = [ "This value is only used to validate the versions " "response from Manila."), cfg.StrOpt("max_api_microversion", - default="2.88", + default="2.90", help="The maximum api microversion is configured to be the " "value of the latest microversion supported by Manila."), cfg.StrOpt("region", @@ -220,6 +220,11 @@ ShareGroup = [ "ss_type:, " "ss_dns_ip:value, ss_user:value, ss_password=value, " "ss_domain:value, ss_server:value"), + cfg.ListOpt("capability_encryption_support", + default=[], + help="Encryption support capability. Possible values are " + "share_server, share etc. "), + # Switching ON/OFF test suites filtered by features cfg.BoolOpt("run_quota_tests", @@ -386,4 +391,7 @@ ShareGroup = [ cfg.DictOpt("driver_assisted_backup_test_driver_options", default={'dummy': True}, help="Share backup driver options specified as dict."), + cfg.BoolOpt("run_encryption_tests", + default=False, + help="Enable or disable share encryption tests."), ] diff --git a/manila_tempest_tests/services/share/v2/json/shares_client.py b/manila_tempest_tests/services/share/v2/json/shares_client.py index 4b35687a..7457fffd 100644 --- a/manila_tempest_tests/services/share/v2/json/shares_client.py +++ b/manila_tempest_tests/services/share/v2/json/shares_client.py @@ -248,7 +248,7 @@ class SharesV2Client(shares_client.SharesClient): share_type_id=None, is_public=False, share_group_id=None, availability_zone=None, version=LATEST_MICROVERSION, experimental=False, - scheduler_hints=None): + scheduler_hints=None, encryption_key_ref=None): headers = EXPERIMENTAL if experimental else None metadata = metadata or {} scheduler_hints = scheduler_hints or {} @@ -283,6 +283,8 @@ class SharesV2Client(shares_client.SharesClient): post_body["share"]["share_group_id"] = share_group_id if scheduler_hints: post_body["share"]["scheduler_hints"] = scheduler_hints + if encryption_key_ref: + post_body["share"]["encryption_key_ref"] = encryption_key_ref body = json.dumps(post_body) resp, body = self.post("shares", body, headers=headers, @@ -1102,7 +1104,7 @@ class SharesV2Client(shares_client.SharesClient): share_networks=None, share_groups=None, share_group_snapshots=None, force=True, share_type=None, share_replicas=None, - replica_gigabytes=None, url=None, + replica_gigabytes=None, encryption_keys=None, url=None, version=LATEST_MICROVERSION): if url is None: url = self._get_quotas_url(version) @@ -1130,6 +1132,8 @@ class SharesV2Client(shares_client.SharesClient): put_body["share_replicas"] = share_replicas if replica_gigabytes is not None: put_body["replica_gigabytes"] = replica_gigabytes + if encryption_keys is not None: + put_body["encryption_keys"] = encryption_keys put_body = json.dumps({"quota_set": put_body}) resp, body = self.put(url, put_body, version=version) diff --git a/manila_tempest_tests/tests/api/admin/test_quotas.py b/manila_tempest_tests/tests/api/admin/test_quotas.py index e8377e17..74c46223 100644 --- a/manila_tempest_tests/tests/api/admin/test_quotas.py +++ b/manila_tempest_tests/tests/api/admin/test_quotas.py @@ -66,6 +66,8 @@ class SharesAdminQuotasTest(base.BaseSharesAdminTest): if utils.share_replica_quotas_are_supported(): self.assertGreater(int(quotas["share_replicas"]), -2) self.assertGreater(int(quotas["replica_gigabytes"]), -2) + if utils.encryption_keys_quota_supported(): + self.assertGreater(int(quotas["encryption_keys"]), -2) @decorators.idempotent_id('1ff57cfa-cd8d-495f-86eb-9fead307428e') @tc.attr(base.TAG_POSITIVE, base.TAG_API) @@ -82,6 +84,8 @@ class SharesAdminQuotasTest(base.BaseSharesAdminTest): if utils.share_replica_quotas_are_supported(): self.assertGreater(int(quotas["share_replicas"]), -2) self.assertGreater(int(quotas["replica_gigabytes"]), -2) + if utils.encryption_keys_quota_supported(): + self.assertGreater(int(quotas["encryption_keys"]), -2) @decorators.idempotent_id('9b96dd45-7c0d-41ee-88e4-600185f61358') @tc.attr(base.TAG_POSITIVE, base.TAG_API) @@ -471,6 +475,19 @@ class SharesAdminQuotasUpdateTest(base.BaseSharesAdminTest): self.assertEqual(new_quota, int(updated["share_networks"])) + @decorators.idempotent_id('78957d97-afad-4371-a21e-79641fff83f7') + @tc.attr(base.TAG_POSITIVE, base.TAG_API) + @utils.skip_if_microversion_not_supported("2.90") + def test_update_tenant_quota_encryption_keys(self): + # get current quotas + quotas = self.client.show_quotas(self.tenant_id)['quota_set'] + new_quota = int(quotas["encryption_keys"]) + 2 + + # set new quota for encryption keys + updated = self.update_quotas(self.tenant_id, encryption_keys=new_quota) + + self.assertEqual(new_quota, int(updated["encryption_keys"])) + @decorators.idempotent_id('84e24c32-ee78-461e-ac1f-f9e4d99f88e2') @tc.attr(base.TAG_POSITIVE, base.TAG_API) def test_reset_tenant_quotas(self): @@ -496,6 +513,8 @@ class SharesAdminQuotasUpdateTest(base.BaseSharesAdminTest): if utils.share_replica_quotas_are_supported(): data["share_replicas"] = int(custom["share_replicas"]) + 2 data["replica_gigabytes"] = int(custom["replica_gigabytes"]) + 2 + if utils.encryption_keys_quota_supported(): + data["encryption_keys"] = int(custom["encryption_keys"]) + 2 # set new quota, turn off cleanup - we'll do it right below updated = self.update_quotas(self.tenant_id, cleanup=False, **data) @@ -518,6 +537,9 @@ class SharesAdminQuotasUpdateTest(base.BaseSharesAdminTest): data["share_replicas"], int(updated["share_replicas"])) self.assertEqual( data["replica_gigabytes"], int(updated["replica_gigabytes"])) + if utils.encryption_keys_quota_supported(): + self.assertEqual( + data["encryption_keys"], int(updated["encryption_keys"])) # Reset customized quotas self.client.reset_quotas(self.tenant_id) @@ -545,6 +567,10 @@ class SharesAdminQuotasUpdateTest(base.BaseSharesAdminTest): self.assertEqual( int(default["replica_gigabytes"]), int(reseted["replica_gigabytes"])) + if utils.encryption_keys_quota_supported(): + self.assertEqual( + int(default["encryption_keys"]), + int(reseted["encryption_keys"])) def _get_new_replica_quota_values(self, default_quotas, value_to_set): new_values = { diff --git a/manila_tempest_tests/tests/api/admin/test_quotas_negative.py b/manila_tempest_tests/tests/api/admin/test_quotas_negative.py index 5b4ff1d2..2534104e 100644 --- a/manila_tempest_tests/tests/api/admin/test_quotas_negative.py +++ b/manila_tempest_tests/tests/api/admin/test_quotas_negative.py @@ -80,6 +80,7 @@ class SharesAdminQuotasNegativeTest(base.BaseSharesAdminTest): {"gigabytes": -2}, {"snapshot_gigabytes": -2}, {"share_networks": -2}, + {"encryption_keys": -2}, ) @decorators.idempotent_id('07d3e69a-7cda-4ca7-9fea-c32f6830fdd3') @tc.attr(base.TAG_NEGATIVE, base.TAG_API) diff --git a/manila_tempest_tests/tests/api/test_share_encryption.py b/manila_tempest_tests/tests/api/test_share_encryption.py new file mode 100644 index 00000000..e199114a --- /dev/null +++ b/manila_tempest_tests/tests/api/test_share_encryption.py @@ -0,0 +1,94 @@ +# Copyright 2025 Cloudifcation GmbH. All rights reserved. +# +# 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. + +import ddt +from oslo_log import log +from tempest import config +from tempest.lib import decorators +from testtools import testcase as tc + +from manila_tempest_tests.common import barbican_client_mgr +from manila_tempest_tests.tests.api import base +from manila_tempest_tests import utils + +CONF = config.CONF +LOG = log.getLogger(__name__) + + +@ddt.ddt +class ShareEncryptionNFSTest(base.BaseSharesMixedTest): + """Covers share functionality, that is related to NFS share type.""" + protocol = "nfs" + + @classmethod + def skip_checks(cls): + super(ShareEncryptionNFSTest, cls).skip_checks() + if not CONF.share.run_encryption_tests: + raise cls.skipException('Encryption tests are disabled.') + utils.check_skip_if_microversion_not_supported("2.90") + + if cls.protocol not in CONF.share.enable_protocols: + message = "%s tests are disabled" % cls.protocol + raise cls.skipException(message) + + if ('share_server' not in CONF.share.capability_encryption_support and + 'share' not in CONF.share.capability_encryption_support): + message = "Unsupported value of encryption support capability" + raise cls.skipException(message) + + @classmethod + def resource_setup(cls): + super(ShareEncryptionNFSTest, cls).resource_setup() + + extra_specs = { + 'driver_handles_share_servers': CONF.share.multitenancy_enabled, + } + if 'share_server' in CONF.share.capability_encryption_support: + extra_specs.update({'encryption_support': 'share_server'}) + elif 'share' in CONF.share.capability_encryption_support: + extra_specs.update({'encryption_support': 'share'}) + + # create share_type + cls.share_type_enc = cls.create_share_type(extra_specs=extra_specs) + cls.share_type_enc_id = cls.share_type_enc['id'] + + # setup barbican client + cls.barbican_mgr = barbican_client_mgr.BarbicanClientManager() + cls.barbican_mgr.setup_clients(cls.os_primary) + + @decorators.idempotent_id('21ad41fb-04cf-493c-bc2f-66c80220898c') + @tc.attr(base.TAG_POSITIVE, base.TAG_BACKEND) + def test_create_share_with_share_server_encryption_key_ref(self): + + secret_href = self.barbican_mgr.store_secret() + secret_href_uuid = self.barbican_mgr.ref_to_uuid(secret_href) + + share = self.create_share( + share_protocol=self.protocol, + share_type_id=self.share_type_enc_id, + share_network_id=self.shares_v2_client.share_network_id, + size=1, + name="encrypted_share", + encryption_key_ref=secret_href_uuid, + cleanup_in_class=False) + + self.assertEqual(share['encryption_key_ref'], secret_href_uuid) + + # Delete Barbican secret + self.barbican_mgr.delete_secret(secret_href_uuid) + + +class ShareEncryptionCIFSTest(ShareEncryptionNFSTest): + """Covers share functionality, that is related to CIFS share type.""" + protocol = "cifs" diff --git a/manila_tempest_tests/tests/api/test_share_encryption_negative.py b/manila_tempest_tests/tests/api/test_share_encryption_negative.py new file mode 100644 index 00000000..3d374265 --- /dev/null +++ b/manila_tempest_tests/tests/api/test_share_encryption_negative.py @@ -0,0 +1,74 @@ +# Copyright 2025 Cloudifcation GmbH. All rights reserved. +# +# 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 tempest import config +from tempest.lib import decorators +from tempest.lib import exceptions as lib_exc +from testtools import testcase as tc + +from manila_tempest_tests.tests.api import base +from manila_tempest_tests import utils + +CONF = config.CONF + + +class SharesEncryptionNegativeTest(base.BaseSharesMixedTest): + + @classmethod + def skip_checks(cls): + super(SharesEncryptionNegativeTest, cls).skip_checks() + if not CONF.share.run_encryption_tests: + raise cls.skipException('Encryption tests are disabled.') + utils.check_skip_if_microversion_not_supported("2.90") + + @classmethod + def resource_setup(cls): + super(SharesEncryptionNegativeTest, cls).resource_setup() + # create share_type + cls.no_encryption_type = cls.create_share_type() + cls.no_encryption_type_id = cls.no_encryption_type['id'] + cls.encryption_type = cls.create_share_type( + extra_specs={ + 'encryption_support': 'share_server', + }) + cls.encryption_type_id = cls.encryption_type['id'] + + @decorators.idempotent_id('b8097d56-067e-4d7c-8401-31bc7021fe81') + @tc.attr(base.TAG_NEGATIVE, base.TAG_API) + def test_create_share_with_invalid_share_type(self): + # should not create share when encryption isn't supported by + # share type + self.assertRaises(lib_exc.BadRequest, + self.shares_v2_client.create_share, + share_type_id=self.no_encryption_type_id, + encryption_key_ref='fake_ref') + + @decorators.idempotent_id('b8097d56-067e-4d7c-8401-31bc7021fe88') + @tc.attr(base.TAG_NEGATIVE, base.TAG_API) + def test_create_share_with_invalid_encryption_key_ref(self): + # should not create share when key ref is invalid UUID + self.assertRaises(lib_exc.BadRequest, + self.shares_v2_client.create_share, + share_type_id=self.encryption_type_id, + encryption_key_ref='fake_ref') + + @decorators.idempotent_id('b8097d56-067e-4d7c-8401-31bc7021fe82') + @tc.attr(base.TAG_NEGATIVE, base.TAG_API) + def test_create_share_with_encryption_key_ref_absent_in_barbican(self): + # should not create share when key ref is not present in barbican + self.assertRaises( + lib_exc.BadRequest, + self.shares_v2_client.create_share, + share_type_id=self.encryption_type_id, + encryption_key_ref='cfbe8ae1-7932-43f2-bf82-3fd3ddba30c3') diff --git a/manila_tempest_tests/utils.py b/manila_tempest_tests/utils.py index d7d86f16..56a28e2b 100644 --- a/manila_tempest_tests/utils.py +++ b/manila_tempest_tests/utils.py @@ -27,6 +27,7 @@ from manila_tempest_tests import utils CONF = config.CONF SHARE_NETWORK_SUBNETS_MICROVERSION = '2.51' SHARE_REPLICA_QUOTAS_MICROVERSION = "2.53" +ENCRYPTION_KEYS_QUOTA_MICROVERSION = "2.90" EXPERIMENTAL = {'X-OpenStack-Manila-API-Experimental': 'True'} @@ -276,6 +277,10 @@ def share_replica_quotas_are_supported(): return is_microversion_supported(SHARE_REPLICA_QUOTAS_MICROVERSION) +def encryption_keys_quota_supported(): + return is_microversion_supported(ENCRYPTION_KEYS_QUOTA_MICROVERSION) + + def share_network_get_default_subnet(share_network): return next(( subnet for subnet in share_network.get('share_network_subnets', []) diff --git a/requirements.txt b/requirements.txt index ae5ea16c..d44cb740 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,3 +3,6 @@ pbr>=3.0.0 # Apache-2.0 ddt>=1.6.0 # MIT oslo.log>=3.36.0 # Apache-2.0 tempest>=31.1.0 # Apache-2.0 + +# the encryption tests require it +barbican-tempest-plugin>=1.6.0 # Apache-2.0 diff --git a/zuul.d/manila-tempest-jobs.yaml b/zuul.d/manila-tempest-jobs.yaml index b7e1949c..b377be1e 100644 --- a/zuul.d/manila-tempest-jobs.yaml +++ b/zuul.d/manila-tempest-jobs.yaml @@ -691,6 +691,7 @@ parent: manila-tempest-plugin-standalone-base required-projects: - openstack/barbican + - openstack/barbican-tempest-plugin vars: devstack_plugins: barbican: https://opendev.org/openstack/barbican @@ -761,6 +762,8 @@ run_share_server_migration_tests: true run_share_server_multiple_subnet_tests: true run_network_allocation_update_tests: true + run_encryption_tests: true + capability_encryption_support: share_server - job: name: manila-tempest-plugin-glusterfs-native

AltStyle によって変換されたページ (->オリジナル) /