Merge "[RBAC] Add share manage/unmanage tests"
diff --git a/manila_tempest_tests/common/constants.py b/manila_tempest_tests/common/constants.py
index 2750760..3488bc5 100644
--- a/manila_tempest_tests/common/constants.py
+++ b/manila_tempest_tests/common/constants.py
@@ -42,6 +42,7 @@
REPLICATION_STATE_OUT_OF_SYNC = 'out_of_sync'
MIN_SHARE_REPLICATION_VERSION = '2.11'
SHARE_REPLICA_GRADUATION_VERSION = '2.56'
+SHARE_REPLICA_SHARE_NET_PARAM_VERSION = '2.72'
# Access Rules
RULE_STATE_ACTIVE = 'active'
diff --git a/manila_tempest_tests/config.py b/manila_tempest_tests/config.py
index 2c1b375..56a5b5a 100644
--- a/manila_tempest_tests/config.py
+++ b/manila_tempest_tests/config.py
@@ -40,7 +40,7 @@
"This value is only used to validate the versions "
"response from Manila."),
cfg.StrOpt("max_api_microversion",
- default="2.73",
+ default="2.74",
help="The maximum api microversion is configured to be the "
"value of the latest microversion supported by Manila."),
cfg.StrOpt("region",
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 87f7559..648b282 100644
--- a/manila_tempest_tests/services/share/v2/json/shares_client.py
+++ b/manila_tempest_tests/services/share/v2/json/shares_client.py
@@ -1299,7 +1299,7 @@
def get_share_group_type_spec(self, share_group_type_id, group_spec_key,
version=LATEST_MICROVERSION):
- uri = "group-types/%s/group_specs/%s" % (
+ uri = "share-group-types/%s/group-specs/%s" % (
share_group_type_id, group_spec_key)
headers, extra_headers = utils.get_extra_headers(
version, constants.SHARE_GROUPS_GRADUATION_VERSION)
@@ -1309,9 +1309,9 @@
body = json.loads(body)
return rest_client.ResponseBody(resp, body)
- def list_share_group_type_specs(self, share_group_type_id, params=None,
- version=LATEST_MICROVERSION):
- uri = "share-group-types/%s/group_specs" % share_group_type_id
+ def get_share_group_type_specs(self, share_group_type_id, params=None,
+ version=LATEST_MICROVERSION):
+ uri = "share-group-types/%s/group-specs" % share_group_type_id
headers, extra_headers = utils.get_extra_headers(
version, constants.SHARE_GROUPS_GRADUATION_VERSION)
if params is not None:
@@ -1343,10 +1343,10 @@
return self.create_share_group_type_specs(
share_group_type_id, group_specs_dict, version=version)
- def delete_share_group_type_spec(self, share_type_id, group_spec_key,
+ def delete_share_group_type_spec(self, share_group_type_id, group_spec_key,
version=LATEST_MICROVERSION):
uri = "share-group-types/%s/group-specs/%s" % (
- share_type_id, group_spec_key)
+ share_group_type_id, group_spec_key)
headers, extra_headers = utils.get_extra_headers(
version, constants.SHARE_GROUPS_GRADUATION_VERSION)
resp, body = self.delete(uri, headers=headers,
@@ -1563,8 +1563,8 @@
return rest_client.ResponseBody(resp, body)
def create_share_replica(self, share_id, availability_zone=None,
- version=LATEST_MICROVERSION,
- scheduler_hints=None):
+ scheduler_hints=None, share_network_id=None,
+ version=LATEST_MICROVERSION):
"""Add a share replica of an existing share."""
uri = "share-replicas"
post_body = {
@@ -1574,6 +1574,9 @@
if scheduler_hints:
post_body["scheduler_hints"] = scheduler_hints
+ if share_network_id:
+ post_body['share_network_id'] = share_network_id
+
headers, extra_headers = utils.get_extra_headers(
version, constants.SHARE_REPLICA_GRADUATION_VERSION)
body = json.dumps({'share_replica': post_body})
diff --git a/manila_tempest_tests/tests/api/admin/test_replication.py b/manila_tempest_tests/tests/api/admin/test_replication.py
index 242e786..3458023 100644
--- a/manila_tempest_tests/tests/api/admin/test_replication.py
+++ b/manila_tempest_tests/tests/api/admin/test_replication.py
@@ -105,9 +105,13 @@
# NOTE(Yogi1): Cleanup needs to be disabled for replica that is
# being promoted since it will become the 'primary'/'active' replica.
+ share_net_id = None
+ if utils.is_microversion_ge(version, (
+ constants.SHARE_REPLICA_SHARE_NET_PARAM_VERSION)):
+ share_net_id = self.sn_id
replica = self.create_share_replica(
- share["id"], self.replica_zone, cleanup=False,
- client=self.admin_client, version=version)
+ share["id"], self.replica_zone, share_network_id=share_net_id,
+ cleanup=False, client=self.admin_client, version=version)
# Wait for replica state to update after creation
waiters.wait_for_resource_status(
self.admin_client, replica['id'],
@@ -156,11 +160,15 @@
def test_force_delete_share_replica(self, version):
"""Test force deleting a replica that is in 'error_deleting' status."""
utils.check_skip_if_microversion_not_supported(version)
- replica = self.create_share_replica(self.share['id'],
- self.replica_zone,
- cleanup_in_class=False,
- client=self.admin_client,
- version=version)
+ share_net_id = None
+ if utils.is_microversion_ge(version, (
+ constants.SHARE_REPLICA_SHARE_NET_PARAM_VERSION)):
+ share_net_id = self.sn_id
+ replica = self.create_share_replica(
+ self.share['id'], self.replica_zone,
+ share_network_id=share_net_id,
+ cleanup_in_class=False,
+ client=self.admin_client, version=version)
self.admin_client.reset_share_replica_status(
replica['id'], constants.STATUS_ERROR_DELETING, version=version)
waiters.wait_for_resource_status(
@@ -179,11 +187,15 @@
def test_reset_share_replica_status(self, version):
"""Test resetting a replica's 'status' attribute."""
utils.check_skip_if_microversion_not_supported(version)
- replica = self.create_share_replica(self.share['id'],
- self.replica_zone,
- cleanup_in_class=False,
- client=self.admin_client,
- version=version)
+ share_net_id = None
+ if utils.is_microversion_ge(version, (
+ constants.SHARE_REPLICA_SHARE_NET_PARAM_VERSION)):
+ share_net_id = self.sn_id
+ replica = self.create_share_replica(
+ self.share['id'], self.replica_zone,
+ share_network_id=share_net_id,
+ cleanup_in_class=False, client=self.admin_client,
+ version=version)
self.admin_client.reset_share_replica_status(replica['id'],
constants.STATUS_ERROR,
version=version)
@@ -200,11 +212,15 @@
def test_reset_share_replica_state(self, version):
"""Test resetting a replica's 'replica_state' attribute."""
utils.check_skip_if_microversion_not_supported(version)
- replica = self.create_share_replica(self.share['id'],
- self.replica_zone,
- cleanup_in_class=False,
- client=self.admin_client,
- version=version)
+ share_net_id = None
+ if utils.is_microversion_ge(version, (
+ constants.SHARE_REPLICA_SHARE_NET_PARAM_VERSION)):
+ share_net_id = self.sn_id
+ replica = self.create_share_replica(
+ self.share['id'], self.replica_zone,
+ share_network_id=share_net_id,
+ cleanup_in_class=False, client=self.admin_client,
+ version=version)
self.admin_client.reset_share_replica_state(replica['id'],
constants.STATUS_ERROR,
version=version)
@@ -222,11 +238,12 @@
def test_resync_share_replica(self, version):
"""Test resyncing a replica."""
utils.check_skip_if_microversion_not_supported(version)
- replica = self.create_share_replica(self.share['id'],
- self.replica_zone,
- cleanup_in_class=False,
- client=self.admin_client,
- version=version)
+ share_net_id = None
+ if utils.is_microversion_ge(version, (
+ constants.SHARE_REPLICA_SHARE_NET_PARAM_VERSION)):
+ share_net_id = self.sn_id
+ replica = self.create_share_replica(
+ self.share['id'], share_network_id=share_net_id, version=version)
waiters.wait_for_resource_status(
self.admin_client, replica['id'],
constants.REPLICATION_STATE_IN_SYNC, resource_name='share_replica',
diff --git a/manila_tempest_tests/tests/api/admin/test_share_group_types.py b/manila_tempest_tests/tests/api/admin/test_share_group_types.py
index 1bc522a..771be4f 100644
--- a/manila_tempest_tests/tests/api/admin/test_share_group_types.py
+++ b/manila_tempest_tests/tests/api/admin/test_share_group_types.py
@@ -142,6 +142,42 @@
self.assertDictMatch(group_specs, sg_type['group_specs'])
+ @decorators.idempotent_id('dd620bfd-197b-4675-ace6-e26f809bb26e')
+ @tc.attr(base.TAG_POSITIVE, base.TAG_API)
+ def test_get_one_share_group_type_extra_spec(self):
+ name = data_utils.rand_name('share-group-type')
+ group_specs = {'key1': 'value1', 'key2': 'value2'}
+
+ sg_type = self.create_share_group_type(
+ name=name,
+ share_types=self.share_type['id'],
+ group_specs=group_specs,
+ cleanup_in_class=False,
+ version=constants.MIN_SHARE_GROUP_MICROVERSION)
+
+ extra_spec = self.shares_v2_client.get_share_group_type_spec(
+ sg_type['id'], 'key1')
+
+ self.assertEqual({'key1': group_specs['key1']}, extra_spec)
+
+ @decorators.idempotent_id('eef69171-9757-423c-8cd4-487cbd84ca24')
+ @tc.attr(base.TAG_POSITIVE, base.TAG_API)
+ def test_get_all_share_group_type_extra_specs(self):
+ name = data_utils.rand_name('share-group-type')
+ group_specs = {'key': 'value'}
+
+ sg_type = self.create_share_group_type(
+ name=name,
+ share_types=self.share_type['id'],
+ group_specs=group_specs,
+ cleanup_in_class=False,
+ version=constants.MIN_SHARE_GROUP_MICROVERSION)
+
+ extra_specs = self.shares_v2_client.get_share_group_type_specs(
+ sg_type['id'])
+
+ self.assertDictMatch(group_specs, extra_specs['group_specs'])
+
@decorators.idempotent_id('15b44580-a34d-4e0d-a77b-0e76b45d6199')
@tc.attr(base.TAG_POSITIVE, base.TAG_API)
@ddt.data(
diff --git a/manila_tempest_tests/tests/api/admin/test_share_group_types_negative.py b/manila_tempest_tests/tests/api/admin/test_share_group_types_negative.py
index 391a6b4..778d7a7 100644
--- a/manila_tempest_tests/tests/api/admin/test_share_group_types_negative.py
+++ b/manila_tempest_tests/tests/api/admin/test_share_group_types_negative.py
@@ -44,6 +44,7 @@
cls.share_group_type = cls.create_share_group_type(
data_utils.rand_name("unique_sgt_name"),
share_types=[cls.share_type['id']],
+ group_specs={"key": "value"},
client=cls.admin_shares_v2_client)
@decorators.idempotent_id('1f8e3f98-4df7-4383-94d6-4ad058ef79c1')
@@ -163,3 +164,27 @@
self.admin_shares_v2_client.remove_access_from_share_group_type,
data_utils.rand_name("fake"),
self.admin_shares_v2_client.tenant_id)
+
+ @decorators.idempotent_id('3e763f5b-6663-4620-9471-ed3050da6201')
+ @tc.attr(base.TAG_NEGATIVE, base.TAG_API)
+ def test_try_get_share_group_type_extra_specs_with_user(self):
+ self.assertRaises(
+ lib_exc.Forbidden,
+ self.shares_v2_client.get_share_group_type_specs,
+ self.share_group_type['id'])
+
+ @decorators.idempotent_id('2264f7eb-3ff0-47e9-8ab0-54694113db3d')
+ @tc.attr(base.TAG_NEGATIVE, base.TAG_API)
+ def test_try_get_extra_specs_from_nonexistent_share_group_type(self):
+ self.assertRaises(
+ lib_exc.NotFound,
+ self.admin_shares_v2_client.get_share_group_type_specs,
+ data_utils.rand_name('fake'))
+
+ @decorators.idempotent_id('2be79455-0ce7-4ca6-818f-40651ba79c6e')
+ @tc.attr(base.TAG_NEGATIVE, base.TAG_API)
+ def test_try_get_extra_spec_with_nonexistent_key(self):
+ self.assertRaises(
+ lib_exc.NotFound,
+ self.admin_shares_v2_client.get_share_group_type_spec,
+ self.share_group_type['id'], 'fake_key')
diff --git a/manila_tempest_tests/tests/api/admin/test_share_networks.py b/manila_tempest_tests/tests/api/admin/test_share_networks.py
index 82d8a8a..3eedb4e 100644
--- a/manila_tempest_tests/tests/api/admin/test_share_networks.py
+++ b/manila_tempest_tests/tests/api/admin/test_share_networks.py
@@ -18,6 +18,7 @@
from manila_tempest_tests.tests.api import base
from manila_tempest_tests.tests.api import test_share_networks
+from manila_tempest_tests import utils
class ShareNetworkAdminTest(base.BaseSharesMixedTest,
@@ -26,7 +27,7 @@
@classmethod
def resource_setup(cls):
super(ShareNetworkAdminTest, cls).resource_setup()
- ss_data = cls.generate_security_service_data()
+ ss_data = utils.generate_security_service_data()
cls.ss_ldap = cls.create_security_service(**ss_data)
cls.data_sn_with_ldap_ss = {
diff --git a/manila_tempest_tests/tests/api/base.py b/manila_tempest_tests/tests/api/base.py
index 42c2fab..226e767 100755
--- a/manila_tempest_tests/tests/api/base.py
+++ b/manila_tempest_tests/tests/api/base.py
@@ -676,14 +676,17 @@
@classmethod
def create_share_replica(cls, share_id, availability_zone=None,
+ scheduler_hints=None,
+ share_network_id=None,
client=None, cleanup_in_class=False,
cleanup=True,
- version=CONF.share.max_api_microversion,
- scheduler_hints=None):
+ version=CONF.share.max_api_microversion):
client = client or cls.shares_v2_client
replica = client.create_share_replica(
share_id, availability_zone=availability_zone,
- version=version, scheduler_hints=scheduler_hints)['share_replica']
+ scheduler_hints=scheduler_hints,
+ share_network_id=share_network_id,
+ version=version)['share_replica']
resource = {
"type": "share_replica",
"id": replica["id"],
@@ -723,35 +726,6 @@
return replica
@classmethod
- def _get_access_rule_data_from_config(cls):
- """Get the first available access type/to combination from config.
-
- This method opportunistically picks the first configured protocol
- to create the share. Do not use this method in tests where you need
- to test depth and breadth in the access types and access recipients.
- """
- protocol = cls.shares_v2_client.share_protocol
-
- if protocol in CONF.share.enable_ip_rules_for_protocols:
- access_type = "ip"
- access_to = utils.rand_ip()
- elif protocol in CONF.share.enable_user_rules_for_protocols:
- access_type = "user"
- access_to = CONF.share.username_for_user_rules
- elif protocol in CONF.share.enable_cert_rules_for_protocols:
- access_type = "cert"
- access_to = "client3.com"
- elif protocol in CONF.share.enable_cephx_rules_for_protocols:
- access_type = "cephx"
- access_to = data_utils.rand_name(
- cls.__class__.__name__ + '-cephx-id')
- else:
- message = "Unrecognized protocol and access rules configuration."
- raise cls.skipException(message)
-
- return access_type, access_to
-
- @classmethod
def create_share_network(cls, client=None,
cleanup_in_class=False,
add_security_services=True, **kwargs):
@@ -935,40 +909,6 @@
"cleanup '%s'. Skipping.", res["type"])
res["deleted"] = True
- @classmethod
- def generate_share_network_data(self):
- data = {
- "name": data_utils.rand_name("sn-name"),
- "description": data_utils.rand_name("sn-desc"),
- "neutron_net_id": data_utils.rand_name("net-id"),
- "neutron_subnet_id": data_utils.rand_name("subnet-id"),
- }
- return data
-
- @classmethod
- def generate_subnet_data(self):
- data = {
- "neutron_net_id": data_utils.rand_name("net-id"),
- "neutron_subnet_id": data_utils.rand_name("subnet-id"),
- }
- return data
-
- @classmethod
- def generate_security_service_data(self, set_ou=False):
- data = {
- "name": data_utils.rand_name("ss-name"),
- "description": data_utils.rand_name("ss-desc"),
- "dns_ip": utils.rand_ip(),
- "server": utils.rand_ip(),
- "domain": data_utils.rand_name("ss-domain"),
- "user": data_utils.rand_name("ss-user"),
- "password": data_utils.rand_name("ss-password"),
- }
- if set_ou:
- data["ou"] = data_utils.rand_name("ss-ou")
-
- return data
-
# Useful assertions
def assertDictMatch(self, d1, d2, approx_equal=False, tolerance=0.001):
"""Assert two dicts are equivalent.
@@ -1054,7 +994,8 @@
raise_rule_in_error_state=True, cleanup=True):
client = client or self.shares_v2_client
- a_type, a_to = self._get_access_rule_data_from_config()
+ a_type, a_to = utils.get_access_rule_data_from_config(
+ client.share_protocol)
access_type = access_type or a_type
access_to = access_to or a_to
diff --git a/manila_tempest_tests/tests/api/test_access_rules_metadata.py b/manila_tempest_tests/tests/api/test_access_rules_metadata.py
index ee541ac..6b0aa2d 100644
--- a/manila_tempest_tests/tests/api/test_access_rules_metadata.py
+++ b/manila_tempest_tests/tests/api/test_access_rules_metadata.py
@@ -55,8 +55,8 @@
@classmethod
def resource_setup(cls):
super(AccessRulesMetadataTest, cls).resource_setup()
- cls.protocol = cls.shares_v2_client.share_protocol
- cls.access_type, __ = cls._get_access_rule_data_from_config()
+ cls.access_type, __ = utils.get_access_rule_data_from_config(
+ cls.shares_v2_client.share_protocol)
int_range = range(20, 50)
cls.access_to = {
# list of unique values is required for ability to create lots
diff --git a/manila_tempest_tests/tests/api/test_access_rules_metadata_negative.py b/manila_tempest_tests/tests/api/test_access_rules_metadata_negative.py
index c848ed3..98a1361 100644
--- a/manila_tempest_tests/tests/api/test_access_rules_metadata_negative.py
+++ b/manila_tempest_tests/tests/api/test_access_rules_metadata_negative.py
@@ -55,9 +55,9 @@
@classmethod
def resource_setup(cls):
super(AccessesMetadataNegativeTest, cls).resource_setup()
- cls.protocol = cls.shares_v2_client.share_protocol
cls.access_type, cls.access_to = (
- cls._get_access_rule_data_from_config()
+ utils.get_access_rule_data_from_config(
+ cls.shares_v2_client.share_protocol)
)
# create share type
cls.share_type = cls.create_share_type()
diff --git a/manila_tempest_tests/tests/api/test_replication.py b/manila_tempest_tests/tests/api/test_replication.py
index 7873926..5865e69 100644
--- a/manila_tempest_tests/tests/api/test_replication.py
+++ b/manila_tempest_tests/tests/api/test_replication.py
@@ -13,6 +13,7 @@
# License for the specific language governing permissions and limitations
# under the License.
+import ddt
from tempest import config
from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
@@ -26,12 +27,14 @@
from manila_tempest_tests import utils
CONF = config.CONF
+LATEST_MICROVERSION = CONF.share.max_api_microversion
_MIN_SUPPORTED_MICROVERSION = '2.11'
SUMMARY_KEYS = ['share_id', 'id', 'replica_state', 'status']
DETAIL_KEYS = SUMMARY_KEYS + ['availability_zone', 'updated_at',
'share_network_id', 'created_at']
+@ddt.ddt
class ReplicationTest(base.BaseSharesMixedTest):
@classmethod
@@ -96,11 +99,15 @@
share["id"])['share_instances']
return share_instances[0]["id"]
- def _verify_create_replica(self):
+ def _verify_create_replica(self, version=LATEST_MICROVERSION):
# Create the replica
- share_replica = self.create_share_replica(self.shares[0]["id"],
- self.replica_zone,
- cleanup_in_class=False)
+ share_net_id = None
+ if utils.is_microversion_ge(version, (
+ constants.SHARE_REPLICA_SHARE_NET_PARAM_VERSION)):
+ share_net_id = self.sn_id
+ share_replica = self.create_share_replica(
+ self.shares[0]["id"], self.replica_zone,
+ share_network_id=share_net_id, cleanup_in_class=False)
share_replicas = self.shares_v2_client.list_share_replicas(
share_id=self.shares[0]["id"])['share_replicas']
# Ensure replica is created successfully.
@@ -155,6 +162,18 @@
raise self.skipException(
msg % ','.join(constants.REPLICATION_PROMOTION_CHOICES))
+ @decorators.idempotent_id('c59e3198-062b-4284-8a3b-189a62213573')
+ @tc.attr(base.TAG_POSITIVE, base.TAG_BACKEND)
+ @testtools.skipUnless(
+ CONF.share.multitenancy_enabled, "Only for multitenancy.")
+ @ddt.data(
+ *utils.deduplicate([constants.SHARE_REPLICA_SHARE_NET_PARAM_VERSION,
+ LATEST_MICROVERSION]))
+ def test_create_share_replica_with_provided_network(self, version):
+ utils.check_skip_if_microversion_not_supported(version)
+ share_replica = self._verify_create_replica(version)
+ self.assertIsNotNone(share_replica)
+
@decorators.idempotent_id('8858617f-292d-4e5c-9e15-794b7f1b2e3c')
@tc.attr(base.TAG_POSITIVE, base.TAG_BACKEND)
def test_add_delete_share_replica(self):
@@ -330,7 +349,8 @@
@tc.attr(base.TAG_POSITIVE, base.TAG_BACKEND)
def test_add_access_rule_create_replica_delete_rule(self):
# Add access rule to the share
- access_type, access_to = self._get_access_rule_data_from_config()
+ access_type, access_to = utils.get_access_rule_data_from_config(
+ self.shares_v2_client.share_protocol)
self.allow_access(
self.shares[0]["id"], access_type=access_type, access_to=access_to,
access_level='ro')
@@ -346,7 +366,8 @@
@decorators.idempotent_id('3af3f19a-1195-464e-870b-1a3918914f1b')
@tc.attr(base.TAG_POSITIVE, base.TAG_BACKEND)
def test_create_replica_add_access_rule_delete_replica(self):
- access_type, access_to = self._get_access_rule_data_from_config()
+ access_type, access_to = utils.get_access_rule_data_from_config(
+ self.shares_v2_client.share_protocol)
# Create the replica
share_replica = self._verify_create_replica()
@@ -358,6 +379,30 @@
# Delete the replica
self.delete_share_replica(share_replica["id"])
+ @decorators.idempotent_id('600a13d2-5cf0-482e-97af-9f598b55a406')
+ @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
+ @utils.skip_if_microversion_not_supported("2.74")
+ def test_add_access_rule_share_replica_error_status(self):
+ '''From 2.74, we can add rules even if replicas are in error state.'''
+ access_type, access_to = utils.get_access_rule_data_from_config(
+ self.shares_v2_client.share_protocol)
+ # Create the replica
+ share_replica = self._verify_create_replica()
+
+ # Reset the replica status to error
+ self.admin_client.reset_share_replica_status(
+ share_replica['id'], constants.STATUS_ERROR)
+
+ # Verify access rule will be added in error state
+ self.shares_v2_client.create_access_rule(
+ self.shares[0]["id"], access_type=access_type, access_to=access_to,
+ access_level='ro')
+
+ # Verify access_rules_status transitions to 'active' state.
+ waiters.wait_for_resource_status(
+ self.shares_v2_client, self.shares[0]["id"],
+ constants.RULE_STATE_ACTIVE, status_attr='access_rules_status')
+
@decorators.idempotent_id('a542c179-ea41-4bc0-bd80-e06eaddf5253')
@tc.attr(base.TAG_POSITIVE, base.TAG_BACKEND)
@testtools.skipUnless(CONF.share.run_multiple_share_replicas_tests,
@@ -408,7 +453,8 @@
share = self.create_shares([self.creation_data])[0]
# Add access rule
- access_type, access_to = self._get_access_rule_data_from_config()
+ access_type, access_to = utils.get_access_rule_data_from_config(
+ self.shares_v2_client.share_protocol)
self.allow_access(
share["id"], access_type=access_type, access_to=access_to,
access_level='ro')
diff --git a/manila_tempest_tests/tests/api/test_replication_negative.py b/manila_tempest_tests/tests/api/test_replication_negative.py
index e7d0e7d..9424a08 100644
--- a/manila_tempest_tests/tests/api/test_replication_negative.py
+++ b/manila_tempest_tests/tests/api/test_replication_negative.py
@@ -13,6 +13,7 @@
# License for the specific language governing permissions and limitations
# under the License.
+import ddt
from tempest import config
from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
@@ -83,6 +84,7 @@
return share, instance_id
+@ddt.ddt
class ReplicationNegativeTest(ReplicationNegativeBase):
def _is_replication_type_promotable(self):
@@ -189,8 +191,12 @@
@decorators.idempotent_id('600a13d2-5cf0-482e-97af-9f598b55a407')
@tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
- def test_add_access_rule_share_replica_error_status(self):
- access_type, access_to = self._get_access_rule_data_from_config()
+ @ddt.data('2.11', '2.73')
+ def test_add_access_rule_share_replica_error_status(self, version):
+ """From 2.74, we can add rules even if replicas are in error state."""
+ utils.check_skip_if_microversion_not_supported(version)
+ access_type, access_to = utils.get_access_rule_data_from_config(
+ self.shares_v2_client.share_protocol)
# Create the replica
share_replica = self.create_share_replica(self.share1["id"],
self.replica_zone,
@@ -199,10 +205,12 @@
self.admin_client.reset_share_replica_status(
share_replica['id'], constants.STATUS_ERROR)
- # Verify access rule cannot be added
- self.assertRaises(lib_exc.BadRequest,
- self.admin_client.create_access_rule,
- self.share1["id"], access_type, access_to, 'ro')
+ if utils.is_microversion_lt(version, '2.74'):
+ # Verify access rule cannot be added
+ self.assertRaises(lib_exc.BadRequest,
+ self.shares_v2_client.create_access_rule,
+ self.share1["id"], access_type, access_to, 'ro',
+ version)
@decorators.idempotent_id('91b93b71-4048-412b-bb42-0fe88edfb987')
@testtools.skipUnless(CONF.share.run_host_assisted_migration_tests or
@@ -248,7 +256,7 @@
@utils.skip_if_microversion_not_supported("2.51")
def test_try_add_replica_nonexistent_subnet(self):
# Create a new share network only for a specific az
- data = self.generate_share_network_data()
+ data = utils.generate_share_network_data()
subnet = utils.share_network_get_default_subnet(self.share_network)
data['neutron_net_id'] = subnet['neutron_net_id']
data['neutron_subnet_id'] = subnet['neutron_subnet_id']
diff --git a/manila_tempest_tests/tests/api/test_revert_to_snapshot.py b/manila_tempest_tests/tests/api/test_revert_to_snapshot.py
index 7609e69..dca394a 100644
--- a/manila_tempest_tests/tests/api/test_revert_to_snapshot.py
+++ b/manila_tempest_tests/tests/api/test_revert_to_snapshot.py
@@ -122,6 +122,9 @@
waiters.wait_for_resource_status(
self.shares_v2_client, self.share['id'],
constants.STATUS_AVAILABLE)
+ waiters.wait_for_resource_status(
+ self.shares_v2_client, snapshot['id'], constants.STATUS_AVAILABLE,
+ resource_name='snapshot')
@decorators.idempotent_id('09bd9942-7ef9-4d24-b2dd-f83bdda27b50')
@tc.attr(base.TAG_POSITIVE, base.TAG_BACKEND)
@@ -145,6 +148,9 @@
waiters.wait_for_resource_status(
self.shares_v2_client, self.share['id'],
constants.STATUS_AVAILABLE)
+ waiters.wait_for_resource_status(
+ self.shares_v2_client, snapshot1['id'], constants.STATUS_AVAILABLE,
+ resource_name='snapshot')
@decorators.idempotent_id('146de138-d351-49dc-a13a-5cdbed40b9ac')
@tc.attr(base.TAG_POSITIVE, base.TAG_BACKEND)
@@ -181,3 +187,6 @@
self.shares_v2_client, share_replica['id'],
constants.REPLICATION_STATE_IN_SYNC, resource_name='share_replica',
status_attr='replica_state')
+ waiters.wait_for_resource_status(
+ self.shares_v2_client, snapshot['id'], constants.STATUS_AVAILABLE,
+ resource_name='snapshot')
diff --git a/manila_tempest_tests/tests/api/test_rules.py b/manila_tempest_tests/tests/api/test_rules.py
index 979bf06..925b725 100644
--- a/manila_tempest_tests/tests/api/test_rules.py
+++ b/manila_tempest_tests/tests/api/test_rules.py
@@ -437,9 +437,9 @@
@classmethod
def resource_setup(cls):
super(ShareRulesTest, cls).resource_setup()
- cls.protocol = cls.shares_v2_client.share_protocol
cls.access_type, cls.access_to = (
- cls._get_access_rule_data_from_config()
+ utils.get_access_rule_data_from_config(
+ cls.shares_v2_client.share_protocol)
)
cls.share_type = cls.create_share_type()
cls.share_type_id = cls.share_type['id']
diff --git a/manila_tempest_tests/tests/api/test_rules_negative.py b/manila_tempest_tests/tests/api/test_rules_negative.py
index 1eb858d..a4b53f5 100644
--- a/manila_tempest_tests/tests/api/test_rules_negative.py
+++ b/manila_tempest_tests/tests/api/test_rules_negative.py
@@ -153,7 +153,8 @@
@decorators.idempotent_id('d2856c7d-9417-416d-8d08-e68376ee5b2e')
@tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
def test_add_access_rule_on_share_with_no_host(self):
- access_type, access_to = self._get_access_rule_data_from_config()
+ access_type, access_to = utils.get_access_rule_data_from_config(
+ self.protocol)
extra_specs = self.add_extra_specs_to_dict(
{"share_backend_name": 'invalid_backend'})
share_type = self.create_share_type('invalid_backend',
diff --git a/manila_tempest_tests/tests/api/test_security_services.py b/manila_tempest_tests/tests/api/test_security_services.py
index f938a91..85ed5fb 100644
--- a/manila_tempest_tests/tests/api/test_security_services.py
+++ b/manila_tempest_tests/tests/api/test_security_services.py
@@ -162,7 +162,7 @@
@decorators.idempotent_id('70927e29-4a6a-431a-bbc1-76bc419e0579')
@tc.attr(base.TAG_POSITIVE, base.TAG_API)
def test_create_delete_security_service(self):
- data = self.generate_security_service_data()
+ data = utils.generate_security_service_data()
self.service_names = ["ldap", "kerberos", "active_directory"]
for ss_name in self.service_names:
ss = self.create_security_service(ss_name, **data)
@@ -176,7 +176,7 @@
def test_get_security_service(self, version):
utils.check_skip_if_microversion_not_supported(version)
with_ou = True if utils.is_microversion_ge(version, '2.44') else False
- data = self.generate_security_service_data(set_ou=with_ou)
+ data = utils.generate_security_service_data(set_ou=with_ou)
if utils.is_microversion_ge(version, '2.0'):
ss = self.create_security_service(
@@ -196,11 +196,11 @@
@decorators.idempotent_id('84d47747-13c8-4ab9-9fc4-a43fbb29ad18')
@tc.attr(base.TAG_POSITIVE, base.TAG_API)
def test_update_security_service(self):
- data = self.generate_security_service_data()
+ data = utils.generate_security_service_data()
ss = self.create_security_service(**data)
self.assertDictContainsSubset(data, ss)
- upd_data = self.generate_security_service_data()
+ upd_data = utils.generate_security_service_data()
updated = self.shares_client.update_security_service(
ss["id"], **upd_data)['security_service']
@@ -211,7 +211,7 @@
if utils.is_microversion_ge(CONF.share.max_api_microversion, '2.44'):
# update again with ou
- upd_data_ou = self.generate_security_service_data(set_ou=True)
+ upd_data_ou = utils.generate_security_service_data(set_ou=True)
updated_ou = self.shares_v2_client.update_security_service(
ss["id"], **upd_data_ou)['security_service']
@@ -225,7 +225,7 @@
@testtools.skipIf(
not CONF.share.multitenancy_enabled, "Only for multitenancy.")
def test_try_update_valid_keys_sh_server_exists(self):
- ss_data = self.generate_security_service_data()
+ ss_data = utils.generate_security_service_data()
ss = self.create_security_service(**ss_data)
sn = self.shares_client.get_share_network(
@@ -274,7 +274,7 @@
@tc.attr(base.TAG_POSITIVE, base.TAG_API)
def test_try_list_security_services_all_tenants_ignored(self):
alt_security_service = self.create_security_service(
- **self.generate_security_service_data(),
+ **utils.generate_security_service_data(),
client=self.alt_shares_v2_client)
alt_security_service_id = alt_security_service['id']
sec_service_list = self.shares_client.list_security_services(
diff --git a/manila_tempest_tests/tests/api/test_security_services_mapping.py b/manila_tempest_tests/tests/api/test_security_services_mapping.py
index 2e7531d..6ce9a0a 100644
--- a/manila_tempest_tests/tests/api/test_security_services_mapping.py
+++ b/manila_tempest_tests/tests/api/test_security_services_mapping.py
@@ -17,6 +17,7 @@
from testtools import testcase as tc
from manila_tempest_tests.tests.api import base
+from manila_tempest_tests import utils
class SecurityServicesMappingTest(base.BaseSharesTest):
@@ -30,7 +31,7 @@
super(SecurityServicesMappingTest, self).setUp()
# create share network
- data = self.generate_share_network_data()
+ data = utils.generate_share_network_data()
self.sn = self.create_share_network(client=self.cl,
add_security_services=False,
@@ -38,7 +39,7 @@
self.assertDictContainsSubset(data, self.sn)
# create security service
- data = self.generate_security_service_data()
+ data = utils.generate_security_service_data()
self.ss = self.create_security_service(client=self.cl, **data)
self.assertDictContainsSubset(data, self.ss)
diff --git a/manila_tempest_tests/tests/api/test_security_services_mapping_negative.py b/manila_tempest_tests/tests/api/test_security_services_mapping_negative.py
index d0bb321..96506ce 100644
--- a/manila_tempest_tests/tests/api/test_security_services_mapping_negative.py
+++ b/manila_tempest_tests/tests/api/test_security_services_mapping_negative.py
@@ -146,7 +146,7 @@
@tc.attr(base.TAG_NEGATIVE, base.TAG_API)
def test_try_map_two_ss_with_same_type_to_sn(self):
# create share network
- data = self.generate_share_network_data()
+ data = utils.generate_share_network_data()
sn = self.create_share_network(client=self.cl,
add_security_services=False, **data)
@@ -155,7 +155,7 @@
# create security services with same type
security_services = []
for i in range(2):
- data = self.generate_security_service_data()
+ data = utils.generate_security_service_data()
ss = self.create_security_service(client=self.cl, **data)
self.assertDictContainsSubset(data, ss)
security_services.insert(i, ss)
@@ -173,14 +173,14 @@
@tc.attr(base.TAG_NEGATIVE, base.TAG_API)
def test_try_delete_ss_that_assigned_to_sn(self):
# create share network
- data = self.generate_share_network_data()
+ data = utils.generate_share_network_data()
sn = self.create_share_network(client=self.cl,
add_security_services=False, **data)
self.assertDictContainsSubset(data, sn)
# create security service
- data = self.generate_security_service_data()
+ data = utils.generate_security_service_data()
ss = self.create_security_service(client=self.cl, **data)
self.assertDictContainsSubset(data, ss)
diff --git a/manila_tempest_tests/tests/api/test_security_services_negative.py b/manila_tempest_tests/tests/api/test_security_services_negative.py
index 08e3c7b..23c7cc1 100644
--- a/manila_tempest_tests/tests/api/test_security_services_negative.py
+++ b/manila_tempest_tests/tests/api/test_security_services_negative.py
@@ -21,6 +21,7 @@
from testtools import testcase as tc
from manila_tempest_tests.tests.api import base
+from manila_tempest_tests import utils
CONF = config.CONF
LOG = log.getLogger(__name__)
@@ -93,7 +94,7 @@
@testtools.skipIf(
not CONF.share.multitenancy_enabled, "Only for multitenancy.")
def test_try_update_invalid_keys_sh_server_exists(self):
- ss_data = self.generate_security_service_data()
+ ss_data = utils.generate_security_service_data()
ss = self.create_security_service(**ss_data)
sn = self.shares_client.get_share_network(
@@ -129,7 +130,7 @@
@decorators.idempotent_id('288dbf42-ee22-4445-8363-7ebb1c3d89c9')
@tc.attr(base.TAG_NEGATIVE, base.TAG_API)
def test_get_deleted_security_service(self):
- data = self.generate_security_service_data()
+ data = utils.generate_security_service_data()
ss = self.create_security_service(**data)
self.assertDictContainsSubset(data, ss)
diff --git a/manila_tempest_tests/tests/api/test_share_network_subnets.py b/manila_tempest_tests/tests/api/test_share_network_subnets.py
index cdf2162..da3d5ee 100644
--- a/manila_tempest_tests/tests/api/test_share_network_subnets.py
+++ b/manila_tempest_tests/tests/api/test_share_network_subnets.py
@@ -61,7 +61,7 @@
az_name = az['name']
# Generate subnet data
- data = self.generate_subnet_data()
+ data = utils.generate_subnet_data()
data['share_network_id'] = share_network['id']
data['availability_zone'] = az_name
@@ -95,7 +95,7 @@
az_name = az['name']
# Generate subnet data
- data = self.generate_subnet_data()
+ data = utils.generate_subnet_data()
data['share_network_id'] = share_network['id']
data['availability_zone'] = az_name
diff --git a/manila_tempest_tests/tests/api/test_share_network_subnets_negative.py b/manila_tempest_tests/tests/api/test_share_network_subnets_negative.py
index 7b0ca55..52b297e 100644
--- a/manila_tempest_tests/tests/api/test_share_network_subnets_negative.py
+++ b/manila_tempest_tests/tests/api/test_share_network_subnets_negative.py
@@ -55,7 +55,7 @@
@decorators.idempotent_id('d20b6105-22d1-4fc0-8468-45dd019240c0')
@tc.attr(base.TAG_NEGATIVE, base.TAG_API)
def test_add_share_network_subnet_share_network_not_found(self):
- data = self.generate_subnet_data()
+ data = utils.generate_subnet_data()
self.assertRaises(lib_exc.NotFound,
self.shares_v2_client.create_subnet,
'fake_inexistent_id',
@@ -94,7 +94,7 @@
@tc.attr(base.TAG_NEGATIVE, base.TAG_API)
def test_add_share_network_subnet_missing_parameters(self):
# Generate subnet data
- data = self.generate_subnet_data()
+ data = utils.generate_subnet_data()
data['availability_zone'] = self.az_name
data.pop('neutron_net_id')
@@ -122,7 +122,7 @@
@tc.attr(base.TAG_NEGATIVE, base.TAG_API)
def test_get_deleted_subnet(self):
# Generate subnet data
- data = self.generate_subnet_data()
+ data = utils.generate_subnet_data()
data['share_network_id'] = self.share_network_id
az = self.shares_v2_client.list_availability_zones(
)['availability_zones'][0]
diff --git a/manila_tempest_tests/tests/api/test_share_networks.py b/manila_tempest_tests/tests/api/test_share_networks.py
index e3d5a78..f990566 100644
--- a/manila_tempest_tests/tests/api/test_share_networks.py
+++ b/manila_tempest_tests/tests/api/test_share_networks.py
@@ -165,7 +165,7 @@
cls.share_type = cls.create_share_type()
cls.share_type_id = cls.share_type['id']
- ss_data = cls.generate_security_service_data()
+ ss_data = utils.generate_security_service_data()
cls.ss_ldap = cls.create_security_service(**ss_data)
cls.data_sn_with_ldap_ss = {
@@ -219,7 +219,7 @@
@tc.attr(base.TAG_POSITIVE, base.TAG_API)
def test_create_delete_share_network(self):
# generate data for share network
- data = self.generate_share_network_data()
+ data = utils.generate_share_network_data()
# create share network
created = self.shares_client.create_share_network(
@@ -242,7 +242,7 @@
@decorators.idempotent_id('1837fdd3-8068-4e88-bc50-9224498f84c0')
@tc.attr(base.TAG_POSITIVE, base.TAG_API)
def test_update_share_network(self):
- update_data = self.generate_share_network_data()
+ update_data = utils.generate_share_network_data()
updated = self.shares_client.update_share_network(
self.sn_with_ldap_ss["id"],
**update_data)['share_network']
@@ -268,7 +268,7 @@
@tc.attr(base.TAG_POSITIVE, base.TAG_API)
def test_recreate_share_network(self):
# generate data for share network
- data = self.generate_share_network_data()
+ data = utils.generate_share_network_data()
# create share network
sn1 = self.shares_client.create_share_network(**data)['share_network']
@@ -288,7 +288,7 @@
@tc.attr(base.TAG_POSITIVE, base.TAG_API)
def test_create_two_share_networks_with_same_net_and_subnet(self):
# generate data for share network
- data = self.generate_share_network_data()
+ data = utils.generate_share_network_data()
# create first share network
sn1 = self.create_share_network(**data)
diff --git a/manila_tempest_tests/tests/api/test_share_networks_negative.py b/manila_tempest_tests/tests/api/test_share_networks_negative.py
index 3306068..94fa7b8 100644
--- a/manila_tempest_tests/tests/api/test_share_networks_negative.py
+++ b/manila_tempest_tests/tests/api/test_share_networks_negative.py
@@ -88,7 +88,7 @@
@decorators.idempotent_id('9166b81c-d6ab-4592-bcf7-9410250e30dd')
@tc.attr(base.TAG_NEGATIVE, base.TAG_API)
def test_try_get_deleted_share_network(self):
- data = self.generate_share_network_data()
+ data = utils.generate_share_network_data()
sn = self.create_share_network(**data)
self.assertDictContainsSubset(data, sn)
@@ -166,7 +166,7 @@
az_name = az['name']
# Generate subnet data
- data = self.generate_subnet_data()
+ data = utils.generate_subnet_data()
data['share_network_id'] = share_network['id']
data['availability_zone'] = az_name
@@ -200,7 +200,7 @@
@tc.attr(base.TAG_NEGATIVE, base.TAG_API)
@decorators.idempotent_id('f6f47c64-6821-4d4a-aa7d-3b0244158197')
def test_check_add_share_network_subnet_share_network_not_found(self):
- data = self.generate_subnet_data()
+ data = utils.generate_subnet_data()
self.assertRaises(lib_exc.NotFound,
self.shares_v2_client.subnet_create_check,
'fake_inexistent_id',
diff --git a/manila_tempest_tests/tests/rbac/base.py b/manila_tempest_tests/tests/rbac/base.py
index db53795..5aaf35e 100644
--- a/manila_tempest_tests/tests/rbac/base.py
+++ b/manila_tempest_tests/tests/rbac/base.py
@@ -102,6 +102,19 @@
return share_network
@classmethod
+ def create_share_type(cls):
+ name = data_utils.rand_name('share-type')
+ extra_specs = {
+ 'driver_handles_share_servers': CONF.share.multitenancy_enabled,
+ }
+ share_type = cls.admin_shares_v2_client.create_share_type(
+ name=name, extra_specs=extra_specs)['share_type']
+ cls.addClassResourceCleanup(
+ cls.delete_resource, cls.admin_shares_v2_client,
+ st_id=share_type['id'])
+ return share_type
+
+ @classmethod
def get_share_type(cls):
return cls.shares_v2_client.get_default_share_type()['share_type']
diff --git a/manila_tempest_tests/tests/rbac/test_availability_zones.py b/manila_tempest_tests/tests/rbac/test_availability_zones.py
new file mode 100644
index 0000000..1754726
--- /dev/null
+++ b/manila_tempest_tests/tests/rbac/test_availability_zones.py
@@ -0,0 +1,69 @@
+# Copyright 2022 Red Hat, Inc.
+# 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 abc
+
+from tempest.lib import decorators
+from testtools import testcase as tc
+
+from manila_tempest_tests.tests.api import base
+from manila_tempest_tests.tests.rbac import base as rbac_base
+
+
+class ShareRbacAvailabilityZonesTests(rbac_base.ShareRbacBaseTests,
+ metaclass=abc.ABCMeta):
+
+ @classmethod
+ def setup_clients(cls):
+ super(ShareRbacAvailabilityZonesTests, cls).setup_clients()
+ cls.persona = getattr(cls, 'os_%s' % cls.credentials[0])
+ cls.client = cls.persona.share_v2.SharesV2Client()
+
+ @abc.abstractmethod
+ def test_list_availability_zones(self):
+ pass
+
+
+class TestProjectAdminTestsNFS(ShareRbacAvailabilityZonesTests,
+ base.BaseSharesTest):
+
+ credentials = ['project_admin']
+
+ @decorators.idempotent_id('87d9bb1c-f4de-40e5-8f25-05a6e1055c0b')
+ @tc.attr(base.TAG_POSITIVE, base.TAG_API)
+ def test_list_availability_zones(self):
+ self.do_request('list_availability_zones', expected_status=200)
+
+
+class TestProjectMemberTestsNFS(ShareRbacAvailabilityZonesTests,
+ base.BaseSharesTest):
+
+ credentials = ['project_member']
+
+ @decorators.idempotent_id('ee2db349-176a-47bc-a20d-5ba9b5f8a813')
+ @tc.attr(base.TAG_POSITIVE, base.TAG_API)
+ def test_list_availability_zones(self):
+ self.do_request('list_availability_zones', expected_status=200)
+
+
+class TestProjectReaderTestsNFS(ShareRbacAvailabilityZonesTests,
+ base.BaseSharesTest):
+
+ credentials = ['project_reader']
+
+ @decorators.idempotent_id('a095fac8-ae62-4be7-8a3e-b0fc1bc71348')
+ @tc.attr(base.TAG_POSITIVE, base.TAG_API)
+ def test_list_availability_zones(self):
+ self.do_request('list_availability_zones', expected_status=200)
diff --git a/manila_tempest_tests/tests/rbac/test_export_locations.py b/manila_tempest_tests/tests/rbac/test_export_locations.py
index 168a6cb..4bd7074 100644
--- a/manila_tempest_tests/tests/rbac/test_export_locations.py
+++ b/manila_tempest_tests/tests/rbac/test_export_locations.py
@@ -77,13 +77,15 @@
@decorators.idempotent_id('c8d75c9f-104b-48a8-9817-17280ce516a8')
@tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
def test_list_export_locations(self):
- self.do_request(
+ locations = self.do_request(
'list_share_export_locations', expected_status=200,
- share_id=self.share['id'])
+ share_id=self.share['id'])['export_locations']
+ self.assertNotEmpty(locations)
- self.do_request(
+ alt_locations = self.do_request(
'list_share_export_locations', expected_status=200,
- share_id=self.alt_share['id'])
+ share_id=self.alt_share['id'])['export_locations']
+ self.assertNotEmpty(alt_locations)
@decorators.idempotent_id('01ac6355-fcad-4af8-b0ad-748fc111051d')
@tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
@@ -122,9 +124,10 @@
@decorators.idempotent_id('b25351bb-102f-437e-a0c1-01d926fa0a7d')
@tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
def test_list_export_locations(self):
- self.do_request(
+ locations = self.do_request(
'list_share_export_locations', expected_status=200,
- share_id=self.share['id'])
+ share_id=self.share['id'])['export_locations']
+ self.assertNotEmpty(locations)
self.do_request(
'list_share_export_locations', expected_status=lib_exc.Forbidden,
diff --git a/manila_tempest_tests/tests/rbac/test_services.py b/manila_tempest_tests/tests/rbac/test_services.py
new file mode 100644
index 0000000..5bb9bbe
--- /dev/null
+++ b/manila_tempest_tests/tests/rbac/test_services.py
@@ -0,0 +1,66 @@
+# Copyright 2022 Red Hat Inc.
+# 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 abc
+
+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.tests.rbac import base as rbac_base
+
+CONF = config.CONF
+
+
+class ShareRbacServicesTests(rbac_base.ShareRbacBaseTests,
+ metaclass=abc.ABCMeta):
+
+ @classmethod
+ def setup_clients(cls):
+ super(ShareRbacServicesTests, cls).setup_clients()
+ cls.persona = getattr(cls, 'os_%s' % cls.credentials[0])
+ cls.client = cls.persona.share_v2.SharesV2Client()
+
+ def test_list_services(self):
+ pass
+
+
+class TestProjectAdminTests(ShareRbacServicesTests, base.BaseSharesTest):
+ credentials = ['project_admin']
+
+ @decorators.idempotent_id('08ec3a0b-6e4a-4cbf-bd15-3f48f8ddf71f')
+ @tc.attr(base.TAG_POSITIVE, base.TAG_API)
+ def test_list_services(self):
+ self.do_request('list_services', expected_status=200)
+
+
+class TestProjectMemberTests(ShareRbacServicesTests, base.BaseSharesTest):
+ credentials = ['project_member']
+
+ @decorators.idempotent_id('7431dca6-9b03-48d3-b97c-41f72f7ed0a3')
+ @tc.attr(base.TAG_NEGATIVE, base.TAG_API)
+ def test_list_services(self):
+ self.do_request('list_services', expected_status=lib_exc.Forbidden)
+
+
+class TestProjectReaderTests(TestProjectMemberTests):
+ credentials = ['project_reader']
+
+ @decorators.idempotent_id('eca71619-d563-4d15-9e49-b661e6da46c0')
+ @tc.attr(base.TAG_NEGATIVE, base.TAG_API)
+ def test_list_services(self):
+ super(TestProjectReaderTests, self).test_list_services()
diff --git a/manila_tempest_tests/tests/rbac/test_share_type_extra_specs.py b/manila_tempest_tests/tests/rbac/test_share_type_extra_specs.py
new file mode 100644
index 0000000..0fae6f0
--- /dev/null
+++ b/manila_tempest_tests/tests/rbac/test_share_type_extra_specs.py
@@ -0,0 +1,170 @@
+# Copyright 2022 Red Hat, Inc.
+# 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 abc
+
+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.tests.rbac import base as rbac_base
+
+CONF = config.CONF
+
+
+class ShareRbacExtraSpecsTests(rbac_base.ShareRbacBaseTests,
+ metaclass=abc.ABCMeta):
+
+ @classmethod
+ def setup_clients(cls):
+ super(ShareRbacExtraSpecsTests, cls).setup_clients()
+ cls.persona = getattr(cls, 'os_%s' % cls.credentials[0])
+ cls.client = cls.persona.share_v2.SharesV2Client()
+ cls.admin_shares_v2_client = (
+ cls.os_project_admin.share_v2.SharesV2Client())
+
+ @classmethod
+ def resource_setup(cls):
+ super(ShareRbacExtraSpecsTests, cls).resource_setup()
+ cls.extra_specs = {u'key': u'value'}
+ cls.share_type = cls.create_share_type()
+
+ @abc.abstractmethod
+ def test_create_share_type_extra_specs(self):
+ pass
+
+ @abc.abstractmethod
+ def test_get_share_type_extra_specs(self):
+ pass
+
+ @abc.abstractmethod
+ def test_update_share_type_extra_spec(self):
+ pass
+
+ @abc.abstractmethod
+ def test_delete_share_type_extra_spec(self):
+ pass
+
+
+class ProjectAdminTests(ShareRbacExtraSpecsTests, base.BaseSharesTest):
+
+ credentials = ['project_admin']
+
+ @classmethod
+ def setup_clients(cls):
+ super(ProjectAdminTests, cls).setup_clients()
+ project_member = cls.setup_user_client(
+ cls.persona, project_id=cls.persona.credentials.project_id)
+ cls.share_member_client = project_member.share_v2.SharesV2Client()
+
+ @decorators.idempotent_id('d51817f4-b186-4eca-8779-f3c36dcd98e7')
+ @tc.attr(base.TAG_POSITIVE, base.TAG_API)
+ def test_create_share_type_extra_specs(self):
+ self.do_request(
+ 'create_share_type_extra_specs', expected_status=200,
+ share_type_id=self.share_type['id'], extra_specs=self.extra_specs)
+ self.addCleanup(
+ self.admin_shares_v2_client.delete_share_type_extra_spec,
+ self.share_type['id'], extra_spec_name='key')
+
+ @decorators.idempotent_id('d4deeb0b-8765-4487-9f1f-9f10088934dd')
+ @tc.attr(base.TAG_POSITIVE, base.TAG_API)
+ def test_get_share_type_extra_specs(self):
+ self.do_request(
+ 'get_share_type_extra_specs', expected_status=200,
+ share_type_id=self.share_type['id'])
+
+ @decorators.idempotent_id('bb27641b-5249-4343-bb08-5a123f31b9f1')
+ @tc.attr(base.TAG_POSITIVE, base.TAG_API)
+ def test_update_share_type_extra_spec(self):
+ self.do_request(
+ 'update_share_type_extra_spec', expected_status=200,
+ share_type_id=self.share_type['id'], spec_name='key',
+ spec_value='value_updated')
+
+ @decorators.idempotent_id('81d59322-8ec1-4f32-a50d-2fedd1cca655')
+ @tc.attr(base.TAG_POSITIVE, base.TAG_API)
+ def test_delete_share_type_extra_spec(self):
+ self.admin_shares_v2_client.create_share_type_extra_specs(
+ self.share_type['id'], self.extra_specs)
+ self.do_request(
+ 'delete_share_type_extra_spec', expected_status=202,
+ share_type_id=self.share_type['id'], extra_spec_name='key')
+
+
+class ProjectMemberTests(ShareRbacExtraSpecsTests, base.BaseSharesTest):
+
+ credentials = ['project_member', 'project_admin']
+
+ @decorators.idempotent_id('446c1e7c-5ca2-46f5-b2f3-6417152e5bf8')
+ @tc.attr(base.TAG_NEGATIVE, base.TAG_API)
+ def test_create_share_type_extra_specs(self):
+ self.do_request(
+ 'create_share_type_extra_specs', expected_status=lib_exc.Forbidden,
+ share_type_id=self.share_type['id'], extra_specs=self.extra_specs)
+
+ @decorators.idempotent_id('aabdf0c1-8b68-4c40-a542-cc1dbd039279')
+ @tc.attr(base.TAG_NEGATIVE, base.TAG_API)
+ def test_get_share_type_extra_specs(self):
+ self.do_request(
+ 'get_share_type_extra_specs', expected_status=lib_exc.Forbidden,
+ share_type_id=self.share_type['id'])
+
+ @decorators.idempotent_id('3629f91c-ad21-4321-acd9-7fca92a4721a')
+ @tc.attr(base.TAG_NEGATIVE, base.TAG_API)
+ def test_update_share_type_extra_spec(self):
+ self.do_request(
+ 'update_share_type_extra_spec', expected_status=lib_exc.Forbidden,
+ share_type_id=self.share_type['id'], spec_name='key',
+ spec_value='value_updated')
+
+ @decorators.idempotent_id('0bee97ab-b406-481c-9b8b-4813fb9f49dc')
+ @tc.attr(base.TAG_NEGATIVE, base.TAG_API)
+ def test_delete_share_type_extra_spec(self):
+ self.admin_shares_v2_client.create_share_type_extra_specs(
+ self.share_type['id'], self.extra_specs)
+ self.do_request(
+ 'delete_share_type_extra_spec', expected_status=lib_exc.Forbidden,
+ share_type_id=self.share_type['id'], extra_spec_name='key')
+ self.addCleanup(
+ self.admin_shares_v2_client.delete_share_type_extra_spec,
+ self.share_type['id'], extra_spec_name='key')
+
+
+class ProjectReaderTests(ProjectMemberTests):
+
+ credentials = ['project_reader', 'project_admin']
+
+ @decorators.idempotent_id('da80b823-9a96-45c3-8f86-e9f7fc5ad2c6')
+ @tc.attr(base.TAG_NEGATIVE, base.TAG_API)
+ def test_create_share_type_extra_specs(self):
+ super(ProjectReaderTests, self).test_create_share_type_extra_specs()
+
+ @decorators.idempotent_id('78989220-22dc-46d2-b83b-63f070988eed')
+ @tc.attr(base.TAG_NEGATIVE, base.TAG_API)
+ def test_get_share_type_extra_specs(self):
+ super(ProjectReaderTests, self).test_get_share_type_extra_specs()
+
+ @decorators.idempotent_id('c77173d4-b90c-4edf-a9b6-a26c81aaec42')
+ @tc.attr(base.TAG_NEGATIVE, base.TAG_API)
+ def test_update_share_type_extra_spec(self):
+ super(ProjectReaderTests, self).test_update_share_type_extra_spec()
+
+ @decorators.idempotent_id('46fc8e62-b987-444f-ab9f-cd86a8960156')
+ @tc.attr(base.TAG_NEGATIVE, base.TAG_API)
+ def test_delete_share_type_extra_spec(self):
+ super(ProjectReaderTests, self).test_delete_share_type_extra_spec()
diff --git a/manila_tempest_tests/tests/rbac/test_share_types.py b/manila_tempest_tests/tests/rbac/test_share_types.py
index 2263747..e4e6016 100644
--- a/manila_tempest_tests/tests/rbac/test_share_types.py
+++ b/manila_tempest_tests/tests/rbac/test_share_types.py
@@ -155,7 +155,7 @@
class ProjectReaderTests(ShareRbacShareTypesTests, base.BaseSharesTest):
- credentials = ['project_reader', 'project_member']
+ credentials = ['project_reader']
@decorators.idempotent_id('f4c352c4-c12b-4722-9fe7-9a2ec639ee63')
@tc.attr(base.TAG_NEGATIVE, base.TAG_API)
diff --git a/manila_tempest_tests/tests/rbac/test_user_messages.py b/manila_tempest_tests/tests/rbac/test_user_messages.py
new file mode 100644
index 0000000..b659f93
--- /dev/null
+++ b/manila_tempest_tests/tests/rbac/test_user_messages.py
@@ -0,0 +1,249 @@
+# Copyright 2022 Red Hat, Inc.
+# 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 abc
+
+from tempest import config
+from tempest.lib.common.utils import data_utils
+from tempest.lib import decorators
+from tempest.lib import exceptions as lib_exc
+from testtools import testcase as tc
+
+from manila_tempest_tests.common import waiters
+from manila_tempest_tests.tests.api import base
+from manila_tempest_tests.tests.rbac import base as rbac_base
+
+CONF = config.CONF
+
+
+class ShareRbacUserMessageTests(rbac_base.ShareRbacBaseTests,
+ metaclass=abc.ABCMeta):
+
+ @classmethod
+ def setup_clients(cls):
+ super(ShareRbacUserMessageTests, cls).setup_clients()
+ cls.persona = getattr(cls, 'os_%s' % cls.credentials[0])
+ cls.client = cls.persona.share_v2.SharesV2Client()
+ cls.share_admin_client = cls.os_project_admin.share_v2.SharesV2Client()
+ cls.alt_project_share_v2_client = (
+ cls.os_project_alt_member.share_v2.SharesV2Client())
+
+ @classmethod
+ def resource_setup(cls):
+ # One of the options for generating a user message is to create a share
+ # type with invalid extra_specs. Creating a manila share with this
+ # share type will fail because no valid host is found.
+ extra_specs = {
+ 'key': 'value',
+ 'driver_handles_share_servers': CONF.share.multitenancy_enabled,
+ }
+ share_type_name = data_utils.rand_name('share-type')
+ cls.share_type = cls.share_admin_client.create_share_type(
+ name=share_type_name, extra_specs=extra_specs)['share_type']
+ cls.addClassResourceCleanup(
+ cls.share_admin_client.delete_share_type, cls.share_type['id'])
+
+ def create_user_message(self, client, cleanup=True):
+ # Trigger a 'no valid host' situation to generate a message.
+ share = client.create_share(
+ share_type_id=self.share_type['id'])['share']
+ self.addCleanup(client.delete_share, share['id'])
+ waiters.wait_for_resource_status(client, share['id'], 'error')
+
+ message = waiters.wait_for_message(client, share['id'])
+ if cleanup:
+ self.addCleanup(client.delete_message, message['id'])
+ return message
+
+ @abc.abstractmethod
+ def test_list_messages(self):
+ pass
+
+ @abc.abstractmethod
+ def test_show_message(self):
+ pass
+
+ @abc.abstractmethod
+ def test_delete_message(self):
+ pass
+
+
+class ProjectAdminTests(ShareRbacUserMessageTests, base.BaseSharesTest):
+
+ credentials = ['project_admin', 'project_alt_member']
+
+ @classmethod
+ def setup_clients(cls):
+ super(ProjectAdminTests, cls).setup_clients()
+ project_member = cls.setup_user_client(
+ cls.persona, project_id=cls.persona.credentials.project_id)
+ cls.share_member_client = project_member.share_v2.SharesV2Client()
+
+ @decorators.idempotent_id('2067d8ba-953d-4035-b65d-6001b3d4ea8f')
+ @tc.attr(base.TAG_POSITIVE, base.TAG_API)
+ def test_list_messages(self):
+ message = self.create_user_message(
+ self.share_member_client, self.share_type)
+ message_alt = self.create_user_message(
+ self.alt_project_share_v2_client, self.share_type)
+
+ message_list = self.do_request(
+ 'list_messages', expected_status=200)['messages']
+ message_id_list = [
+ s['id'] for s in message_list
+ ]
+
+ self.assertIn(message['id'], message_id_list)
+ self.assertIn(message_alt['id'], message_id_list)
+
+ @decorators.idempotent_id('ec46f10e-c768-4df5-b75a-0ce3e22d8038')
+ @tc.attr(base.TAG_POSITIVE, base.TAG_API)
+ def test_show_message(self):
+ message = self.create_user_message(
+ self.share_member_client, self.share_type)
+ self.do_request(
+ 'get_message', message_id=message['id'], expected_status=200)
+
+ message_alt = self.create_user_message(
+ self.alt_project_share_v2_client, self.share_type)
+ self.do_request(
+ 'get_message', message_id=message_alt['id'], expected_status=200)
+
+ @decorators.idempotent_id('b91c355b-a5f8-47aa-8ab4-00a350f8ac7f')
+ @tc.attr(base.TAG_POSITIVE, base.TAG_API)
+ def test_delete_message(self):
+ message = self.create_user_message(
+ self.share_member_client, cleanup=False)
+ self.do_request(
+ 'delete_message', message_id=message['id'], expected_status=204)
+
+ message_alt = self.create_user_message(
+ self.alt_project_share_v2_client, cleanup=False)
+ self.do_request(
+ 'delete_message', message_id=message_alt['id'],
+ expected_status=204)
+
+
+class ProjectMemberTests(ShareRbacUserMessageTests, base.BaseSharesTest):
+ """Test suite for basic share use message operations by member user
+
+ In order to test share user message operations we need to preform an action
+ that generates a user message. One of the reasons for generating a user
+ message is share creation that fails because no valid host is found.
+ To achieve this goal we need to create a share type.
+ Since only user with admin credentials can create a share type, we have to
+ initialize these credentials within project member class.
+ """
+
+ credentials = ['project_member', 'project_admin', 'project_alt_member']
+
+ @decorators.idempotent_id('1fd0f86d-cb1e-4694-a54e-4b7774c7c652')
+ @tc.attr(base.TAG_POSITIVE, base.TAG_API)
+ def test_list_messages(self):
+ share_client = getattr(self, 'share_member_client', self.client)
+ message = self.create_user_message(share_client, self.share_type)
+
+ message_alt = self.create_user_message(
+ self.alt_project_share_v2_client, self.share_type)
+
+ message_list = self.do_request(
+ 'list_messages', expected_status=200)['messages']
+ message_id_list = [
+ s['id'] for s in message_list
+ ]
+
+ self.assertIn(message['id'], message_id_list)
+ self.assertNotIn(message_alt['id'], message_id_list)
+
+ @decorators.idempotent_id('283d33be-727b-4180-a503-95d31cc99a79')
+ @tc.attr(base.TAG_POSITIVE, base.TAG_API)
+ def test_show_message(self):
+ share_client = getattr(self, 'share_member_client', self.client)
+ message = self.create_user_message(share_client, self.share_type)
+ self.do_request(
+ 'get_message', message_id=message['id'], expected_status=200)
+
+ message_alt = self.create_user_message(
+ self.alt_project_share_v2_client, self.share_type)
+ self.do_request(
+ 'get_message', message_id=message_alt['id'],
+ expected_status=lib_exc.NotFound)
+
+ @decorators.idempotent_id('5821a3a9-6194-414a-9668-0d933a0d4fb0')
+ @tc.attr(base.TAG_POSITIVE, base.TAG_API)
+ def test_delete_message(self):
+ share_client = getattr(self, 'share_member_client', self.client)
+ message = self.create_user_message(
+ share_client, cleanup=False)
+ self.do_request(
+ 'delete_message', message_id=message['id'],
+ expected_status=204)
+
+ message_alt = self.create_user_message(
+ self.alt_project_share_v2_client, cleanup=False)
+ self.do_request(
+ 'delete_message', message_id=message_alt['id'],
+ expected_status=lib_exc.NotFound)
+ self.addCleanup(self.share_admin_client.delete_message,
+ message_alt['id'])
+
+
+class ProjectReaderTests(ProjectMemberTests):
+ """Test suite for basic share use message operations by reader user
+
+ In order to test certain share operations we must create a share resource
+ for this. Since reader user is limited in resources creation, we are forced
+ to use admin credentials, so we can test other share operations.
+ In this class we use admin user to create a member user within reader
+ project. That way we can perform a reader actions on this resource.
+ """
+
+ credentials = ['project_reader', 'project_admin', 'project_alt_member']
+
+ @classmethod
+ def setup_clients(cls):
+ super(ProjectReaderTests, cls).setup_clients()
+ project_member = cls.setup_user_client(
+ cls.os_project_admin,
+ project_id=cls.persona.credentials.project_id)
+ cls.share_member_client = project_member.share_v2.SharesV2Client()
+
+ @decorators.idempotent_id('ab3b8812-47df-4472-a410-7f84d52999f3')
+ @tc.attr(base.TAG_POSITIVE, base.TAG_API)
+ def test_list_messages(self):
+ super(ProjectReaderTests, self).test_list_messages()
+
+ @decorators.idempotent_id('f0603a61-b620-4f89-afc5-006d1195fa7f')
+ @tc.attr(base.TAG_POSITIVE, base.TAG_API)
+ def test_show_message(self):
+ super(ProjectReaderTests, self).test_show_message()
+
+ @decorators.idempotent_id('a03695c7-e05a-4c89-9a04-7d94a8dd2419')
+ @tc.attr(base.TAG_NEGATIVE, base.TAG_API)
+ def test_delete_message(self):
+ message = self.create_user_message(
+ self.share_member_client, cleanup=False)
+ self.do_request(
+ 'delete_message', message_id=message['id'],
+ expected_status=lib_exc.Forbidden)
+ self.addCleanup(self.share_admin_client.delete_message, message['id'])
+
+ message_alt = self.create_user_message(
+ self.alt_project_share_v2_client, cleanup=False)
+ self.do_request(
+ 'delete_message', message_id=message_alt['id'],
+ expected_status=lib_exc.Forbidden)
+ self.addCleanup(self.share_admin_client.delete_message,
+ message_alt['id'])
diff --git a/manila_tempest_tests/utils.py b/manila_tempest_tests/utils.py
index 5ecfb36..d7d86f1 100644
--- a/manila_tempest_tests/utils.py
+++ b/manila_tempest_tests/utils.py
@@ -19,8 +19,11 @@
from netaddr import ip
from tempest import config
+from tempest.lib.common.utils import data_utils
import testtools
+from manila_tempest_tests import utils
+
CONF = config.CONF
SHARE_NETWORK_SUBNETS_MICROVERSION = '2.51'
SHARE_REPLICA_QUOTAS_MICROVERSION = "2.53"
@@ -140,6 +143,40 @@
return address
+def generate_share_network_data():
+ data = {
+ "name": data_utils.rand_name("sn-name"),
+ "description": data_utils.rand_name("sn-desc"),
+ "neutron_net_id": data_utils.rand_name("net-id"),
+ "neutron_subnet_id": data_utils.rand_name("subnet-id"),
+ }
+ return data
+
+
+def generate_subnet_data():
+ data = {
+ "neutron_net_id": data_utils.rand_name("net-id"),
+ "neutron_subnet_id": data_utils.rand_name("subnet-id"),
+ }
+ return data
+
+
+def generate_security_service_data(set_ou=False):
+ data = {
+ "name": data_utils.rand_name("ss-name"),
+ "description": data_utils.rand_name("ss-desc"),
+ "dns_ip": utils.rand_ip(),
+ "server": utils.rand_ip(),
+ "domain": data_utils.rand_name("ss-domain"),
+ "user": data_utils.rand_name("ss-user"),
+ "password": data_utils.rand_name("ss-password"),
+ }
+ if set_ou:
+ data["ou"] = data_utils.rand_name("ss-ou")
+
+ return data
+
+
def choose_matching_backend(share, pools, share_type):
extra_specs = {}
# fix extra specs with string values instead of boolean
@@ -190,6 +227,33 @@
return extra_specs
+def get_access_rule_data_from_config(protocol):
+ """Get the first available access type/to combination from config.
+
+ This method opportunistically picks the first configured protocol
+ to create the share. Do not use this method in tests where you need
+ to test depth and breadth in the access types and access recipients.
+ """
+
+ if protocol in CONF.share.enable_ip_rules_for_protocols:
+ access_type = "ip"
+ access_to = rand_ip()
+ elif protocol in CONF.share.enable_user_rules_for_protocols:
+ access_type = "user"
+ access_to = CONF.share.username_for_user_rules
+ elif protocol in CONF.share.enable_cert_rules_for_protocols:
+ access_type = "cert"
+ access_to = "client3.com"
+ elif protocol in CONF.share.enable_cephx_rules_for_protocols:
+ access_type = "cephx"
+ access_to = data_utils.rand_name("cephx-id")
+ else:
+ message = "Unrecognized protocol and access rules configuration."
+ raise testtools.TestCase.skipException(message)
+
+ return access_type, access_to
+
+
def replication_with_multitenancy_support():
return (share_network_subnets_are_supported() and
CONF.share.multitenancy_enabled)
diff --git a/tox.ini b/tox.ini
index 153ebbf..907516f 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,7 +1,6 @@
[tox]
minversion = 3.1.1
-envlist = py3,pypy,pep8
-skipsdist = True
+envlist = py3,pep8
[testenv]
basepython = python3