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 <kinpaa@gmail.com>
This commit is contained in:
Kiran Pawar
2025年06月11日 13:33:02 +00:00
parent 496237a646
commit fe868063d7

View File

@@ -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)

View File

@@ -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:<ldap, kerberos or active_directory>, "
"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."),
]

View File

@@ -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)

View File

@@ -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 = {

View File

@@ -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)

View File

@@ -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"

View File

@@ -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')

View File

@@ -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', [])

View File

@@ -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

View File

@@ -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
Reference in New Issue
openstack/manila-tempest-plugin
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.

The note is not visible to the blocked user.