Merge "[CI] Disable glance RBAC enforcement in generic jobs"
diff --git a/manila_tempest_tests/config.py b/manila_tempest_tests/config.py
index 56a5b5a..0cd07d3 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.74",
+ default="2.81",
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 bf944d4..fe3e31c 100644
--- a/manila_tempest_tests/services/share/v2/json/shares_client.py
+++ b/manila_tempest_tests/services/share/v2/json/shares_client.py
@@ -2149,3 +2149,68 @@
self.expected_success(200, resp.status)
body = json.loads(body)
return rest_client.ResponseBody(resp, body)
+
+#################
+
+ def create_resource_lock(self, resource_id, resource_type,
+ resource_action='delete', lock_reason=None,
+ version=LATEST_MICROVERSION):
+ body = {
+ "resource_lock": {
+ 'resource_id': resource_id,
+ 'resource_type': resource_type,
+ 'resource_action': resource_action,
+ 'lock_reason': lock_reason,
+ },
+ }
+ body = json.dumps(body)
+ resp, body = self.post("resource-locks", body, version=version)
+ self.expected_success(200, resp.status)
+ body = json.loads(body)
+ return rest_client.ResponseBody(resp, body)
+
+ def get_resource_lock(self, lock_id, version=LATEST_MICROVERSION):
+ resp, body = self.get("resource-locks/%s" % lock_id, version=version)
+
+ self.expected_success(200, resp.status)
+ body = json.loads(body)
+ return rest_client.ResponseBody(resp, body)
+
+ def list_resource_locks(self, filters=None, version=LATEST_MICROVERSION):
+ uri = (
+ "resource-locks?%s" % parse.urlencode(filters)
+ if filters else "resource-locks"
+ )
+
+ resp, body = self.get(uri, version=version)
+
+ self.expected_success(200, resp.status)
+ body = json.loads(body)
+ return rest_client.ResponseBody(resp, body)
+
+ def update_resource_lock(self,
+ lock_id,
+ resource_action=None,
+ lock_reason=None,
+ version=LATEST_MICROVERSION):
+ uri = 'resource-locks/%s' % lock_id
+ post_body = {}
+ if resource_action:
+ post_body['resource_action'] = resource_action
+ if lock_reason:
+ post_body['lock_reason'] = lock_reason
+ body = json.dumps({'resource_lock': post_body})
+
+ resp, body = self.put(uri, body, version=version)
+
+ self.expected_success(200, resp.status)
+ body = json.loads(body)
+ return rest_client.ResponseBody(resp, body)
+
+ def delete_resource_lock(self, lock_id, version=LATEST_MICROVERSION):
+ uri = "resource-locks/%s" % lock_id
+
+ resp, body = self.delete(uri, version=version)
+
+ self.expected_success(204, resp.status)
+ return rest_client.ResponseBody(resp, body)
diff --git a/manila_tempest_tests/tests/api/base.py b/manila_tempest_tests/tests/api/base.py
index c5282d9..18562e5 100755
--- a/manila_tempest_tests/tests/api/base.py
+++ b/manila_tempest_tests/tests/api/base.py
@@ -792,6 +792,30 @@
return security_service
@classmethod
+ def create_resource_lock(cls, resource_id, resource_type='share',
+ resource_action='delete', lock_reason=None,
+ client=None, version=LATEST_MICROVERSION,
+ cleanup_in_class=True):
+ lock_reason = lock_reason or "locked by tempest tests"
+ client = client or cls.shares_v2_client
+
+ lock = client.create_resource_lock(resource_id,
+ resource_type,
+ resource_action=resource_action,
+ lock_reason=lock_reason,
+ version=version)['resource_lock']
+ resource = {
+ "type": "resource_lock",
+ "id": lock["id"],
+ "client": client,
+ }
+ if cleanup_in_class:
+ cls.class_resources.insert(0, resource)
+ else:
+ cls.method_resources.insert(0, resource)
+ return lock
+
+ @classmethod
def update_share_type(cls, share_type_id, name=None,
is_public=None, description=None,
client=None):
@@ -904,6 +928,8 @@
elif res["type"] == "quotas":
user_id = res.get('user_id')
client.reset_quotas(res_id, user_id=user_id)
+ elif res["type"] == "resource_lock":
+ client.delete_resource_lock(res_id)
else:
LOG.warning("Provided unsupported resource type for "
"cleanup '%s'. Skipping.", res["type"])
diff --git a/manila_tempest_tests/tests/api/test_resource_locks.py b/manila_tempest_tests/tests/api/test_resource_locks.py
new file mode 100644
index 0000000..4f88d72
--- /dev/null
+++ b/manila_tempest_tests/tests/api/test_resource_locks.py
@@ -0,0 +1,291 @@
+# 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 datetime
+
+from oslo_utils import timeutils
+from oslo_utils import uuidutils
+from tempest import config
+from tempest.lib import decorators
+from tempest.lib import exceptions as lib_exc
+
+from manila_tempest_tests.tests.api import base
+from manila_tempest_tests import utils
+
+CONF = config.CONF
+
+LOCKS_MIN_API_VERSION = '2.81'
+
+RESOURCE_LOCK_FIELDS = {
+ 'id',
+ 'resource_id',
+ 'resource_action',
+ 'resource_type',
+ 'user_id',
+ 'project_id',
+ 'lock_context',
+ 'created_at',
+ 'updated_at',
+ 'lock_reason',
+ 'links',
+}
+
+
+class ResourceLockCRUTest(base.BaseSharesMixedTest):
+
+ @classmethod
+ def skip_checks(cls):
+ super(ResourceLockCRUTest, cls).skip_checks()
+ utils.check_skip_if_microversion_not_supported(LOCKS_MIN_API_VERSION)
+
+ @classmethod
+ def resource_setup(cls):
+ super(ResourceLockCRUTest, cls).resource_setup()
+ # create share type
+ share_type = cls.create_share_type()
+ cls.share_type_id = share_type['id']
+
+ # create share and place a "delete" lock on it
+ cls.share = cls.create_share(share_type_id=cls.share_type_id)
+ cls.lock = cls.create_resource_lock(cls.share['id'])
+
+ @decorators.attr(type=[base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND])
+ @decorators.idempotent_id('f3d162a6-2ab4-433b-b8e7-6bf4f0bb6b0e')
+ def test_list_resource_locks(self):
+ locks = self.shares_v2_client.list_resource_locks()['resource_locks']
+ self.assertIsInstance(locks, list)
+ self.assertIn(self.lock['id'], [x['id'] for x in locks])
+ lock = locks[0]
+ self.assertEqual(RESOURCE_LOCK_FIELDS, set(lock.keys()))
+
+ @decorators.attr(type=[base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND])
+ @decorators.idempotent_id('72cc0d43-f676-4dd8-8a93-faa71608de98')
+ def test_list_resource_locks_sorted_and_paginated(self):
+ lock_2 = self.create_resource_lock(self.share['id'],
+ cleanup_in_class=False)
+ lock_3 = self.create_resource_lock(self.share['id'],
+ cleanup_in_class=False)
+
+ expected_order = [self.lock['id'], lock_2['id']]
+
+ filters = {'sort_key': 'created_at', 'sort_dir': 'asc', 'limit': 2}
+ body = self.shares_v2_client.list_resource_locks(filters=filters)
+ # tempest/lib/common/rest_client.py's _parse_resp checks
+ # for number of keys in response's dict, if there is only single
+ # key, it returns directly this key, otherwise it returns
+ # parsed body. If limit param is used, then API returns
+ # multiple keys in response ('resource_locks' and
+ # 'resource_lock_links')
+ locks = body['resource_locks']
+ self.assertIsInstance(locks, list)
+ actual_order = [x['id'] for x in locks]
+ self.assertEqual(2, len(actual_order))
+ self.assertNotIn(lock_3['id'], actual_order)
+ self.assertEqual(expected_order, actual_order)
+
+ @decorators.attr(type=[base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND])
+ @decorators.idempotent_id('22831edc-9d99-432d-a0b6-85af8853db98')
+ def test_list_resource_locks_filtered(self):
+ # Filter by resource_id, resource_action, lock_reason_like,
+ # created_since, created_before
+ share_2 = self.create_share(share_type_id=self.share_type_id)
+ share_1_lock_2 = self.create_resource_lock(
+ self.share['id'],
+ lock_reason="clemson tigers rule",
+ cleanup_in_class=False)
+ share_2_lock = self.create_resource_lock(share_2['id'],
+ cleanup_in_class=False)
+
+ # filter by resource_type
+ expected_locks = sorted([
+ self.lock['id'],
+ share_1_lock_2['id'],
+ share_2_lock['id']
+ ])
+ actual_locks = self.shares_v2_client.list_resource_locks(
+ filters={'resource_type': 'share'})['resource_locks']
+ self.assertEqual(expected_locks,
+ sorted([lock['id'] for lock in actual_locks]))
+
+ # filter by resource_id
+ expected_locks = sorted([self.lock['id'], share_1_lock_2['id']])
+ actual_locks = self.shares_v2_client.list_resource_locks(
+ filters={'resource_id': self.share['id']})['resource_locks']
+ self.assertEqual(expected_locks,
+ sorted([lock['id'] for lock in actual_locks]))
+
+ # filter by inexact lock reason
+ actual_locks = self.shares_v2_client.list_resource_locks(
+ filters={'lock_reason~': "clemson"})['resource_locks']
+ self.assertEqual([share_1_lock_2['id']],
+ [lock['id'] for lock in actual_locks])
+
+ # timestamp filters
+ created_at_1 = timeutils.parse_strtime(self.lock['created_at'])
+ created_at_2 = timeutils.parse_strtime(share_2_lock['created_at'])
+ time_1 = created_at_1 - datetime.timedelta(seconds=1)
+ time_2 = created_at_2 - datetime.timedelta(microseconds=1)
+ filters_1 = {'created_since': str(time_1)}
+
+ # should return all resource locks created by this test including
+ # self.lock
+ actual_locks = self.shares_v2_client.list_resource_locks(
+ filters=filters_1)['resource_locks']
+ actual_lock_ids = [lock['id'] for lock in actual_locks]
+ self.assertGreaterEqual(len(actual_lock_ids), 3)
+ self.assertIn(self.lock['id'], actual_lock_ids)
+ self.assertIn(share_1_lock_2['id'], actual_lock_ids)
+
+ for lock in actual_locks:
+ time_diff_with_created_since = timeutils.delta_seconds(
+ time_1, timeutils.parse_strtime(lock['created_at']))
+ self.assertGreaterEqual(time_diff_with_created_since, 0)
+
+ filters_2 = {
+ 'created_since': str(time_1),
+ 'created_before': str(time_2),
+ }
+
+ actual_locks = self.shares_v2_client.list_resource_locks(
+ filters=filters_2)['resource_locks']
+ self.assertIsInstance(actual_locks, list)
+ actual_lock_ids = [lock['id'] for lock in actual_locks]
+ self.assertGreaterEqual(len(actual_lock_ids), 2)
+ self.assertIn(self.lock['id'], actual_lock_ids)
+ self.assertIn(share_1_lock_2['id'], actual_lock_ids)
+ self.assertNotIn(share_2_lock['id'], actual_lock_ids)
+
+ @decorators.attr(type=[base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND])
+ @decorators.idempotent_id('8cbf7331-f3a1-4c7b-ab1e-f8b938bf135e')
+ def test_get_resource_lock(self):
+ lock = self.shares_v2_client.get_resource_lock(
+ self.lock['id'])['resource_lock']
+
+ self.assertEqual(set(RESOURCE_LOCK_FIELDS), set(lock.keys()))
+ self.assertTrue(uuidutils.is_uuid_like(lock['id']))
+ self.assertEqual('share', lock['resource_type'])
+ self.assertEqual(self.share['id'], lock['resource_id'])
+ self.assertEqual('delete', lock['resource_action'])
+ self.assertEqual('user', lock['lock_context'])
+ self.assertEqual(self.shares_v2_client.user_id, lock['user_id'])
+ self.assertEqual(self.shares_v2_client.project_id, lock['project_id'])
+
+ @decorators.attr(type=[base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND])
+ @decorators.idempotent_id('a7f0fb6a-05ac-4afa-b8d9-04d20549bbd1')
+ def test_create_resource_lock(self):
+ # testing lock creation by a different user in the same project
+ project = self.os_admin.projects_client.show_project(
+ self.shares_v2_client.project_id)['project']
+ new_user_client = self.create_user_and_get_client(project)
+
+ lock = self.create_resource_lock(
+ self.share['id'],
+ client=new_user_client.shares_v2_client,
+ cleanup_in_class=False)
+
+ self.assertEqual(set(RESOURCE_LOCK_FIELDS), set(lock.keys()))
+ self.assertTrue(uuidutils.is_uuid_like(lock['id']))
+ self.assertEqual('share', lock['resource_type'])
+ self.assertEqual(self.share['id'], lock['resource_id'])
+ self.assertEqual('delete', lock['resource_action'])
+ self.assertEqual('user', lock['lock_context'])
+ self.assertEqual(new_user_client.shares_v2_client.user_id,
+ lock['user_id'])
+ self.assertEqual(self.shares_v2_client.project_id, lock['project_id'])
+
+ # testing lock creation by admin
+ lock = self.create_resource_lock(
+ self.share['id'],
+ client=self.admin_shares_v2_client,
+ cleanup_in_class=False)
+ self.assertEqual('admin', lock['lock_context'])
+
+ @decorators.attr(type=[base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND])
+ @decorators.idempotent_id('d7b51cde-ff4f-45ce-a237-401e8be5b4e5')
+ def test_update_resource_lock(self):
+ lock = self.shares_v2_client.update_resource_lock(
+ self.lock['id'], lock_reason="new lock reason")['resource_lock']
+
+ # update is synchronous
+ self.assertEqual("new lock reason", lock['lock_reason'])
+
+ # verify get
+ lock = self.shares_v2_client.get_resource_lock(lock['id'])
+ self.assertEqual("new lock reason",
+ lock['resource_lock']['lock_reason'])
+
+
+class ResourceLockDeleteTest(base.BaseSharesMixedTest):
+
+ @classmethod
+ def skip_checks(cls):
+ super(ResourceLockDeleteTest, cls).skip_checks()
+ utils.check_skip_if_microversion_not_supported(LOCKS_MIN_API_VERSION)
+
+ @classmethod
+ def resource_setup(cls):
+ super(ResourceLockDeleteTest, cls).resource_setup()
+ cls.share_type_id = cls.create_share_type()['id']
+
+ @decorators.idempotent_id('835fd617-4600-40a0-9ba1-40e5e0097b01')
+ @decorators.attr(type=[base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND])
+ def test_delete_lock(self):
+ share = self.create_share(share_type_id=self.share_type_id)
+ lock_1 = self.create_resource_lock(share['id'], cleanup_in_class=False)
+ lock_2 = self.create_resource_lock(share['id'], cleanup_in_class=False)
+
+ locks = self.shares_v2_client.list_resource_locks(
+ filters={'resource_id': share['id']})['resource_locks']
+ self.assertEqual(sorted([lock_1['id'], lock_2['id']]),
+ sorted([lock['id'] for lock in locks]))
+
+ self.shares_v2_client.delete_resource_lock(lock_1['id'])
+ locks = self.shares_v2_client.list_resource_locks(
+ filters={'resource_id': share['id']})['resource_locks']
+ self.assertEqual(1, len(locks))
+ self.assertIn(lock_2['id'], [lock['id'] for lock in locks])
+
+ @decorators.idempotent_id('a96e70c7-0afe-4335-9abc-4b45ef778bd7')
+ @decorators.attr(type=[base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND])
+ def test_delete_locked_resource(self):
+ share = self.create_share(share_type_id=self.share_type_id)
+ lock_1 = self.create_resource_lock(share['id'], cleanup_in_class=False)
+ lock_2 = self.create_resource_lock(share['id'], cleanup_in_class=False)
+
+ # share can't be deleted when a lock exists
+ self.assertRaises(lib_exc.Forbidden,
+ self.shares_v2_client.delete_share,
+ share['id'])
+
+ # admin can't do this either
+ self.assertRaises(lib_exc.Forbidden,
+ self.admin_shares_v2_client.delete_share,
+ share['id'])
+ # "the force" shouldn't work either
+ self.assertRaises(lib_exc.Forbidden,
+ self.admin_shares_v2_client.delete_share,
+ share['id'],
+ params={'force': True})
+
+ self.shares_v2_client.delete_resource_lock(lock_1['id'])
+
+ # there's at least one lock, share deletion should still fail
+ self.assertRaises(lib_exc.Forbidden,
+ self.shares_v2_client.delete_share,
+ share['id'])
+
+ self.shares_v2_client.delete_resource_lock(lock_2['id'])
+
+ # locks are gone, share deletion should be possible
+ self.shares_v2_client.delete_share(share['id'])
+ self.shares_v2_client.wait_for_resource_deletion(
+ share_id=share["id"])
diff --git a/manila_tempest_tests/tests/api/test_resource_locks_negative.py b/manila_tempest_tests/tests/api/test_resource_locks_negative.py
new file mode 100644
index 0000000..9501d6c
--- /dev/null
+++ b/manila_tempest_tests/tests/api/test_resource_locks_negative.py
@@ -0,0 +1,127 @@
+# 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 manila_tempest_tests.tests.api import base
+from manila_tempest_tests import utils
+
+CONF = config.CONF
+
+LOCKS_MIN_API_VERSION = '2.81'
+
+
+class ResourceLockNegativeTestAPIOnly(base.BaseSharesMixedTest):
+
+ @classmethod
+ def skip_checks(cls):
+ super(ResourceLockNegativeTestAPIOnly, cls).skip_checks()
+ utils.check_skip_if_microversion_not_supported(LOCKS_MIN_API_VERSION)
+
+ @decorators.attr(type=[base.TAG_NEGATIVE, base.TAG_API])
+ @decorators.idempotent_id('dd978cf7-1622-49e8-a6c8-3da4ac6c6f86')
+ def test_create_resource_lock_invalid_resource(self):
+ self.assertRaises(
+ lib_exc.BadRequest,
+ self.shares_v2_client.create_resource_lock,
+ 'invalid-share-id',
+ 'share'
+ )
+
+ @decorators.attr(type=[base.TAG_NEGATIVE, base.TAG_API])
+ @decorators.idempotent_id('d5600bdc-72c8-43fd-9900-c112aa6c87fa')
+ def test_delete_resource_lock_invalid(self):
+ self.assertRaises(
+ lib_exc.NotFound,
+ self.shares_v2_client.delete_resource_lock,
+ 'invalid-lock-id'
+ )
+
+
+class ResourceLockNegativeTestWithShares(base.BaseSharesMixedTest):
+ @classmethod
+ def skip_checks(cls):
+ super(ResourceLockNegativeTestWithShares, cls).skip_checks()
+ utils.check_skip_if_microversion_not_supported(LOCKS_MIN_API_VERSION)
+
+ @classmethod
+ def resource_setup(cls):
+ super(ResourceLockNegativeTestWithShares, cls).resource_setup()
+ share_type = cls.create_share_type()
+ cls.share = cls.create_share(share_type_id=share_type['id'])
+ cls.user_project = cls.os_admin.projects_client.show_project(
+ cls.shares_v2_client.project_id)['project']
+
+ @decorators.attr(type=[base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND])
+ @decorators.idempotent_id('658297a8-d675-471d-8a19-3d9e9af3a352')
+ def test_create_resource_lock_invalid_resource_action(self):
+ self.assertRaises(
+ lib_exc.BadRequest,
+ self.shares_v2_client.create_resource_lock,
+ self.share['id'],
+ 'share',
+ resource_action='invalid-action'
+ )
+
+ @decorators.attr(type=[base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND])
+ @decorators.idempotent_id('0057b3e7-c250-492d-805b-e355dff954ed')
+ def test_create_resource_lock_invalid_lock_reason_too_long(self):
+ self.assertRaises(
+ lib_exc.BadRequest,
+ self.shares_v2_client.create_resource_lock,
+ self.share['id'],
+ 'share',
+ resource_action='delete',
+ lock_reason='invalid' * 150,
+ )
+
+ @decorators.attr(type=[base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND])
+ @decorators.idempotent_id('a2db3d29-b42f-4c0b-b484-afd32f91f747')
+ def test_update_resource_lock_invalid_param(self):
+ lock = self.create_resource_lock(self.share['id'])
+ self.assertRaises(
+ lib_exc.BadRequest,
+ self.shares_v2_client.update_resource_lock,
+ lock['id'],
+ resource_action='invalid-action'
+ )
+ self.assertRaises(
+ lib_exc.BadRequest,
+ self.shares_v2_client.update_resource_lock,
+ lock['id'],
+ lock_reason='invalid' * 150,
+ )
+
+ @decorators.attr(type=[base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND])
+ @decorators.idempotent_id('45b12120-0fc3-461f-8776-fdb92e599394')
+ def test_update_resource_lock_created_by_different_user(self):
+ lock = self.create_resource_lock(self.share['id'])
+ new_user = self.create_user_and_get_client(project=self.user_project)
+ self.assertRaises(
+ lib_exc.Forbidden,
+ new_user.shares_v2_client.update_resource_lock,
+ lock['id'],
+ lock_reason="I shouldn't be able to do this",
+ )
+
+ @decorators.attr(type=[base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND])
+ @decorators.idempotent_id('00a8ef2b-8769-4aad-aefc-43fc579492f7')
+ def test_delete_resource_lock_created_by_different_user(self):
+ lock = self.create_resource_lock(self.share['id'])
+ new_user = self.create_user_and_get_client(project=self.user_project)
+ self.assertRaises(
+ lib_exc.Forbidden,
+ new_user.shares_v2_client.delete_resource_lock,
+ lock['id'],
+ )
diff --git a/manila_tempest_tests/tests/rbac/base.py b/manila_tempest_tests/tests/rbac/base.py
index 06759ff..d3c63de 100644
--- a/manila_tempest_tests/tests/rbac/base.py
+++ b/manila_tempest_tests/tests/rbac/base.py
@@ -129,6 +129,20 @@
return share_group_type
@classmethod
+ def create_share_group(cls, client, share_group_type_id, share_type_ids):
+ name = data_utils.rand_name('share-group')
+ share_group = client.create_share_group(
+ name=name, share_group_type_id=share_group_type_id,
+ share_type_ids=share_type_ids)['share_group']
+ waiters.wait_for_resource_status(
+ client, share_group['id'], 'available',
+ resource_name='share_group')
+ cls.addClassResourceCleanup(
+ cls.delete_resource, client,
+ share_group_id=share_group['id'])
+ return share_group
+
+ @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_share_groups.py b/manila_tempest_tests/tests/rbac/test_share_groups.py
new file mode 100644
index 0000000..b09a5e4
--- /dev/null
+++ b/manila_tempest_tests/tests/rbac/test_share_groups.py
@@ -0,0 +1,474 @@
+# 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 ShareRbacShareGroupsTests(rbac_base.ShareRbacBaseTests,
+ metaclass=abc.ABCMeta):
+
+ @classmethod
+ def skip_checks(cls):
+ super(ShareRbacShareGroupsTests, cls).skip_checks()
+ if cls.protocol not in CONF.share.enable_protocols:
+ message = "%s tests are disabled" % cls.protocol
+ raise cls.skipException(message)
+
+ @classmethod
+ def setup_clients(cls):
+ super(ShareRbacShareGroupsTests, 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())
+ cls.alt_project_share_v2_client = (
+ cls.os_project_alt_member.share_v2.SharesV2Client())
+
+ @classmethod
+ def resource_setup(cls):
+ super(ShareRbacShareGroupsTests, cls).resource_setup()
+ cls.share_type = cls.create_share_type()
+ cls.share_group_type = cls.create_share_group_type(
+ cls.share_type['id'])
+
+ def share_group(self, share_group_type_id, share_type_ids):
+ share_group = {}
+ share_group['name'] = data_utils.rand_name('share_group')
+ share_group['share_group_type_id'] = share_group_type_id
+ share_group['share_type_ids'] = [share_type_ids]
+ return share_group
+
+ @abc.abstractmethod
+ def test_get_share_group(self):
+ pass
+
+ @abc.abstractmethod
+ def test_list_share_groups(self):
+ pass
+
+ @abc.abstractmethod
+ def test_create_share_group(self):
+ pass
+
+ @abc.abstractmethod
+ def test_delete_share_group(self):
+ pass
+
+ @abc.abstractmethod
+ def test_force_delete_share_group(self):
+ pass
+
+ @abc.abstractmethod
+ def test_update_share_group(self):
+ pass
+
+ @abc.abstractmethod
+ def test_reset_share_group(self):
+ pass
+
+
+class TestProjectAdminTestsNFS(ShareRbacShareGroupsTests, base.BaseSharesTest):
+
+ credentials = ['project_admin', 'project_alt_member']
+ protocol = 'nfs'
+
+ @classmethod
+ def setup_clients(cls):
+ super(TestProjectAdminTestsNFS, 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()
+
+ @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
+ @decorators.idempotent_id('0de993c5-8389-4997-8f7f-345e27f563f1')
+ def test_get_share_group(self):
+ share_group = self.create_share_group(
+ self.share_member_client, self.share_group_type['id'],
+ [self.share_type['id']])
+ self.do_request(
+ 'get_share_group', expected_status=200,
+ share_group_id=share_group['id'])
+
+ alt_share_group = self.create_share_group(
+ self.alt_project_share_v2_client, self.share_group_type['id'],
+ [self.share_type['id']])
+ self.do_request(
+ 'get_share_group', expected_status=200,
+ share_group_id=alt_share_group['id'])
+
+ @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
+ @decorators.idempotent_id('3b277a44-dcae-46da-a58c-f5281d8abc84')
+ def test_list_share_groups(self):
+ share_group = self.create_share_group(
+ self.share_member_client, self.share_group_type['id'],
+ [self.share_type['id']])
+ alt_share_group = self.create_share_group(
+ self.alt_project_share_v2_client, self.share_group_type['id'],
+ [self.share_type['id']])
+
+ params = {"all_tenants": 1}
+ share_group_list = self.do_request(
+ 'list_share_groups', expected_status=200,
+ params=params)['share_groups']
+ share_group_id_list = [
+ s['id'] for s in share_group_list
+ ]
+
+ self.assertIn(share_group['id'], share_group_id_list)
+ self.assertIn(alt_share_group['id'], share_group_id_list)
+
+ @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
+ @decorators.idempotent_id('d060996e-c5f2-4dff-820b-6892a096a425')
+ def test_create_share_group(self):
+ share_group = self.do_request(
+ 'create_share_group', expected_status=202,
+ **self.share_group(self.share_group_type['id'],
+ self.share_type['id']))['share_group']
+ waiters.wait_for_resource_status(
+ self.client, share_group['id'], 'available',
+ resource_name='share_group')
+ self.addCleanup(self.client.wait_for_resource_deletion,
+ share_group_id=share_group['id'])
+ self.addCleanup(self.client.delete_share_group, share_group['id'])
+
+ @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
+ @decorators.idempotent_id('ea6cbb78-057e-4fbc-86bf-125b033cb76f')
+ def test_delete_share_group(self):
+ share_group = self.create_share_group(
+ self.share_member_client, self.share_group_type['id'],
+ [self.share_type['id']])
+ self.do_request(
+ 'delete_share_group', expected_status=202,
+ share_group_id=share_group['id'])
+ self.client.wait_for_resource_deletion(
+ share_group_id=share_group['id'])
+
+ alt_share_group = self.create_share_group(
+ self.alt_project_share_v2_client, self.share_group_type['id'],
+ [self.share_type['id']])
+ self.do_request(
+ 'delete_share_group', expected_status=202,
+ share_group_id=alt_share_group['id'])
+ self.client.wait_for_resource_deletion(
+ share_group_id=alt_share_group['id'])
+
+ @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
+ @decorators.idempotent_id('2cb00ffb-47e3-495e-853c-007752c9e679')
+ def test_force_delete_share_group(self):
+ share_group = self.create_share_group(
+ self.share_member_client, self.share_group_type['id'],
+ [self.share_type['id']])
+ self.do_request(
+ 'share_group_force_delete', expected_status=202,
+ share_group_id=share_group['id'])
+ self.client.wait_for_resource_deletion(
+ share_group_id=share_group['id'])
+
+ alt_share_group = self.create_share_group(
+ self.alt_project_share_v2_client, self.share_group_type['id'],
+ [self.share_type['id']])
+ self.do_request(
+ 'share_group_force_delete', expected_status=202,
+ share_group_id=alt_share_group['id'])
+ self.client.wait_for_resource_deletion(
+ share_group_id=alt_share_group['id'])
+
+ @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
+ @decorators.idempotent_id('1bab40d5-bdba-4a23-9300-807fe513bf15')
+ def test_update_share_group(self):
+ share_group = self.create_share_group(
+ self.share_member_client, self.share_group_type['id'],
+ [self.share_type['id']])
+ name = data_utils.rand_name('rename_share')
+ self.do_request(
+ 'update_share_group', expected_status=200,
+ share_group_id=share_group['id'], name=name)
+
+ alt_share_group = self.create_share_group(
+ self.share_member_client, self.share_group_type['id'],
+ [self.share_type['id']])
+ name = data_utils.rand_name('rename_share')
+ self.do_request(
+ 'update_share_group', expected_status=200,
+ share_group_id=alt_share_group['id'], name=name)
+
+ @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
+ @decorators.idempotent_id('069bc68e-6411-44b8-abe9-399885f0eee5')
+ def test_reset_share_group(self):
+ share_group = self.create_share_group(
+ self.share_member_client, self.share_group_type['id'],
+ [self.share_type['id']])
+ self.do_request(
+ 'share_group_reset_state', expected_status=202,
+ share_group_id=share_group['id'], status='error')
+
+ alt_share_group = self.create_share_group(
+ self.share_member_client, self.share_group_type['id'],
+ [self.share_type['id']])
+ self.do_request(
+ 'share_group_reset_state', expected_status=202,
+ share_group_id=alt_share_group['id'], status='error')
+
+
+class TestProjectMemberTestsNFS(ShareRbacShareGroupsTests,
+ base.BaseSharesTest):
+
+ credentials = ['project_member', 'project_admin', 'project_alt_member']
+ protocol = 'nfs'
+
+ @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
+ @decorators.idempotent_id('a29e1a68-220e-40fc-98ea-9092fd256d07')
+ def test_get_share_group(self):
+ share_client = getattr(self, 'share_member_client', self.client)
+ share_group = self.create_share_group(
+ share_client, self.share_group_type['id'], [self.share_type['id']])
+ self.do_request(
+ 'get_share_group', expected_status=200,
+ share_group_id=share_group['id'])
+
+ alt_share_group = self.create_share_group(
+ self.alt_project_share_v2_client, self.share_group_type['id'],
+ [self.share_type['id']])
+ self.do_request(
+ 'get_share_group', expected_status=lib_exc.NotFound,
+ share_group_id=alt_share_group['id'])
+
+ @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
+ @decorators.idempotent_id('d9c04932-c47e-46e0-bfcf-79c2af32c4c7')
+ def test_list_share_groups(self):
+ share_client = getattr(self, 'share_member_client', self.client)
+ share_group = self.create_share_group(
+ share_client, self.share_group_type['id'], [self.share_type['id']])
+ alt_share_group = self.create_share_group(
+ self.alt_project_share_v2_client, self.share_group_type['id'],
+ [self.share_type['id']])
+
+ params = {"all_tenants": 1}
+ share_group_list = self.do_request(
+ 'list_share_groups', expected_status=200,
+ params=params)['share_groups']
+ share_group_id_list = [
+ s['id'] for s in share_group_list
+ ]
+
+ self.assertIn(share_group['id'], share_group_id_list)
+ self.assertNotIn(alt_share_group['id'], share_group_id_list)
+
+ @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
+ @decorators.idempotent_id('ebad2242-1fb5-4d99-9a5a-281c1944e03d')
+ def test_create_share_group(self):
+ share_group = self.do_request(
+ 'create_share_group', expected_status=202,
+ **self.share_group(self.share_group_type['id'],
+ self.share_type['id']))['share_group']
+ waiters.wait_for_resource_status(
+ self.client, share_group['id'], 'available',
+ resource_name='share_group')
+ self.addCleanup(self.client.wait_for_resource_deletion,
+ share_group_id=share_group['id'])
+ self.addCleanup(self.client.delete_share_group, share_group['id'])
+
+ @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
+ @decorators.idempotent_id('f5c243e4-5128-4a1c-9a15-8c9f0a44437e')
+ def test_delete_share_group(self):
+ share_group = self.create_share_group(
+ self.client, self.share_group_type['id'], [self.share_type['id']])
+ self.do_request(
+ 'delete_share_group', expected_status=202,
+ share_group_id=share_group['id'])
+ self.client.wait_for_resource_deletion(
+ share_group_id=share_group['id'])
+
+ alt_share_group = self.create_share_group(
+ self.alt_project_share_v2_client, self.share_group_type['id'],
+ [self.share_type['id']])
+ self.do_request(
+ 'delete_share_group', expected_status=lib_exc.NotFound,
+ share_group_id=alt_share_group['id'])
+
+ @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
+ @decorators.idempotent_id('36a58d50-1257-479f-80a2-f9b7a00814e2')
+ def test_force_delete_share_group(self):
+ share_client = getattr(self, 'share_member_client', self.client)
+ share_group = self.create_share_group(
+ share_client, self.share_group_type['id'],
+ [self.share_type['id']])
+ self.do_request(
+ 'share_group_force_delete', expected_status=lib_exc.Forbidden,
+ share_group_id=share_group['id'])
+
+ alt_share_group = self.create_share_group(
+ self.alt_project_share_v2_client, self.share_group_type['id'],
+ [self.share_type['id']])
+ self.do_request(
+ 'share_group_force_delete', expected_status=lib_exc.Forbidden,
+ share_group_id=alt_share_group['id'])
+
+ @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
+ @decorators.idempotent_id('cf9e34b6-6c04-4920-a811-2dbcf07ba14e')
+ def test_update_share_group(self):
+ share_group = self.create_share_group(
+ self.client, self.share_group_type['id'], [self.share_type['id']])
+ name = data_utils.rand_name('rename_share')
+ self.do_request(
+ 'update_share_group', expected_status=200,
+ share_group_id=share_group['id'], name=name)
+
+ alt_share_group = self.create_share_group(
+ self.alt_project_share_v2_client, self.share_group_type['id'],
+ [self.share_type['id']])
+ name = data_utils.rand_name('rename_share')
+ self.do_request(
+ 'update_share_group', expected_status=lib_exc.NotFound,
+ share_group_id=alt_share_group['id'], name=name)
+
+ @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
+ @decorators.idempotent_id('2108c4cd-74e0-467f-823a-e44cf8686afa')
+ def test_reset_share_group(self):
+ share_client = getattr(self, 'share_member_client', self.client)
+ share_group = self.create_share_group(
+ share_client, self.share_group_type['id'],
+ [self.share_type['id']])
+ self.do_request(
+ 'share_group_reset_state', expected_status=lib_exc.Forbidden,
+ share_group_id=share_group['id'], status='error')
+
+ alt_share_group = self.create_share_group(
+ self.alt_project_share_v2_client, self.share_group_type['id'],
+ [self.share_type['id']])
+ self.do_request(
+ 'share_group_reset_state', expected_status=lib_exc.Forbidden,
+ share_group_id=alt_share_group['id'], status='error')
+
+
+class TestProjectReaderTestsNFS(TestProjectMemberTestsNFS):
+ """Test suite for basic share group operations by reader user
+
+ In order to test certain share operations we must create a share group
+ 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(TestProjectReaderTestsNFS, 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()
+
+ @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
+ @decorators.idempotent_id('ec0ecbb0-5d45-4624-bb26-8b2e140e2ea9')
+ def test_get_share_group(self):
+ super(TestProjectReaderTestsNFS, self).test_get_share_group()
+
+ @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
+ @decorators.idempotent_id('4ac87837-5bdf-4253-ab50-dd6efdcea285')
+ def test_list_share_groups(self):
+ super(TestProjectReaderTestsNFS, self).test_list_share_groups()
+
+ @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
+ @decorators.idempotent_id('526dcd91-e789-48f8-b209-c384d77e5803')
+ def test_create_share_group(self):
+ self.do_request(
+ 'create_share_group', expected_status=lib_exc.Forbidden,
+ **self.share_group(self.share_group_type['id'],
+ self.share_type['id']))
+
+ @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
+ @decorators.idempotent_id('fdf4d49e-a576-441f-9a3c-e2d58c0d8679')
+ def test_delete_share_group(self):
+ share_group = self.create_share_group(
+ self.share_member_client, self.share_group_type['id'],
+ [self.share_type['id']])
+ self.do_request(
+ 'delete_share_group', expected_status=lib_exc.Forbidden,
+ share_group_id=share_group['id'])
+
+ alt_share_group = self.create_share_group(
+ self.alt_project_share_v2_client, self.share_group_type['id'],
+ [self.share_type['id']])
+ self.do_request(
+ 'delete_share_group', expected_status=lib_exc.Forbidden,
+ share_group_id=alt_share_group['id'])
+
+ @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
+ @decorators.idempotent_id('eddca093-e3a1-4a79-a8c7-8fd04c77b02f')
+ def test_force_delete_share_group(self):
+ super(TestProjectReaderTestsNFS, self).test_force_delete_share_group()
+
+ @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
+ @decorators.idempotent_id('4530c19d-0aa5-402e-ac83-a3f2333f6c71')
+ def test_update_share_group(self):
+ share_group = self.create_share_group(
+ self.share_member_client, self.share_group_type['id'],
+ [self.share_type['id']])
+ name = data_utils.rand_name('rename_share')
+ self.do_request(
+ 'update_share_group', expected_status=lib_exc.Forbidden,
+ share_group_id=share_group['id'], name=name)
+
+ alt_share_group = self.create_share_group(
+ self.alt_project_share_v2_client, self.share_group_type['id'],
+ [self.share_type['id']])
+ name = data_utils.rand_name('rename_share')
+ self.do_request(
+ 'update_share_group', expected_status=lib_exc.Forbidden,
+ share_group_id=alt_share_group['id'], name=name)
+
+ @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
+ @decorators.idempotent_id('37f23531-69b5-418d-bd91-7913341586ec')
+ def test_reset_share_group(self):
+ super(TestProjectReaderTestsNFS, self).test_reset_share_group()
+
+
+class TestProjectAdminTestsCEPHFS(TestProjectAdminTestsNFS):
+ protocol = 'cephfs'
+
+
+class TestProjectMemberTestsCEPHFS(TestProjectMemberTestsNFS):
+ protocol = 'cephfs'
+
+
+class TestProjectReaderTestsCEPHFS(TestProjectReaderTestsNFS):
+ protocol = 'cephfs'
+
+
+class TestProjectAdminTestsCIFS(TestProjectAdminTestsNFS):
+ protocol = 'cifs'
+
+
+class TestProjectMemberTestsCIFS(TestProjectMemberTestsNFS):
+ protocol = 'cifs'
+
+
+class TestProjectReaderTestsCIFS(TestProjectReaderTestsNFS):
+ protocol = 'cifs'
diff --git a/manila_tempest_tests/tests/scenario/manager.py b/manila_tempest_tests/tests/scenario/manager.py
index a73ab44..fe2833a 100644
--- a/manila_tempest_tests/tests/scenario/manager.py
+++ b/manila_tempest_tests/tests/scenario/manager.py
@@ -16,7 +16,6 @@
from oslo_log import log
from oslo_utils import uuidutils
-from tempest.common import image as common_image
from tempest import config
from tempest.lib.common.utils import data_utils
from tempest.lib.common.utils import test_utils
@@ -45,24 +44,16 @@
'name': name,
'container_format': fmt,
'disk_format': disk_format or fmt,
+ 'visibility': 'private'
}
- if CONF.image_feature_enabled.api_v1:
- params['is_public'] = 'False'
- params['properties'] = properties
- params = {'headers': common_image.image_meta_to_headers(**params)}
- else:
- params['visibility'] = 'private'
- # Additional properties are flattened out in the v2 API.
- params.update(properties)
+ # Additional properties are flattened out in the v2 API.
+ params.update(properties)
body = self.image_client.create_image(**params)
image = body['image'] if 'image' in body else body
self.addCleanup(self.image_client.delete_image, image['id'])
self.assertEqual("queued", image['status'])
with open(path, 'rb') as image_file:
- if CONF.image_feature_enabled.api_v1:
- self.image_client.update_image(image['id'], data=image_file)
- else:
- self.image_client.store_image_file(image['id'], image_file)
+ self.image_client.store_image_file(image['id'], image_file)
return image['id']
def glance_image_create(self):
diff --git a/playbooks/manila-tempest-plugin-standalone/run.yaml b/playbooks/manila-tempest-plugin-standalone/run.yaml
index 8df9205..26ad69a 100644
--- a/playbooks/manila-tempest-plugin-standalone/run.yaml
+++ b/playbooks/manila-tempest-plugin-standalone/run.yaml
@@ -5,7 +5,6 @@
- hosts: tempest
roles:
- setup-tempest-run-dir
- - set-tempest-config
- setup-tempest-data-dir
- acl-devstack-files
- run-tempest
diff --git a/roles/set-tempest-config/README.rst b/roles/set-tempest-config/README.rst
deleted file mode 100644
index 9402d3c..0000000
--- a/roles/set-tempest-config/README.rst
+++ /dev/null
@@ -1,19 +0,0 @@
-set-tempest-config
-==================
-
-This is a workaround for the `merge_config_file <https://opendev
-.org/openstack/devstack/src/commit/76d7d7c90c3979c72404fddd31ee884c8bfdb1ec
-/inc/meta-config#L82>`_ routine that doesn't working correctly on jobs based on
-the "devstack-minimal" profile.
-
-**Role Variables**
-
-.. zuul:rolevar:: devstack_base_dir
- :default: /opt/stack
-
- The devstack base directory.
-
-.. zuul:rolevar:: devstack_local_conf_path
- :default: "{{ devstack_base_dir }}/devstack/local.conf"
-
- Where to find the local.conf file
diff --git a/roles/set-tempest-config/defaults/main.yml b/roles/set-tempest-config/defaults/main.yml
deleted file mode 100644
index 5cc7ca6..0000000
--- a/roles/set-tempest-config/defaults/main.yml
+++ /dev/null
@@ -1,3 +0,0 @@
----
-devstack_base_dir: /opt/stack
-devstack_local_conf_path: "{{ devstack_base_dir }}/devstack/local.conf"
diff --git a/roles/set-tempest-config/tasks/main.yml b/roles/set-tempest-config/tasks/main.yml
deleted file mode 100644
index 3572ff5..0000000
--- a/roles/set-tempest-config/tasks/main.yml
+++ /dev/null
@@ -1,14 +0,0 @@
-- name: Install required pip packages
- pip:
- name: devstack-tools
- state: "latest"
- virtualenv: /var/tmp/venv
-
-- name: Copy tempest config
- shell: >-
- . /var/tmp/venv/bin/activate && \
- dsconf extract {{ devstack_local_conf_path }} \
- test-config \
- '$TEMPEST_CONFIG' \
- {{ devstack_base_dir }}/tempest/etc/tempest.conf
- become: yes
diff --git a/zuul.d/manila-tempest-jobs.yaml b/zuul.d/manila-tempest-jobs.yaml
index 20b0804..202e0f0 100644
--- a/zuul.d/manila-tempest-jobs.yaml
+++ b/zuul.d/manila-tempest-jobs.yaml
@@ -162,11 +162,12 @@
name: manila-tempest-plugin-zfsonlinux
description: Test ZFSOnLinux multibackend (DHSS=False) with postgresql db
parent: manila-tempest-plugin-zfsonlinux-base
- branches: &ubuntu_jammy_test_image_branches ^(?!stable/(yoga|xena|wallaby|victoria|ussuri)).*$
+ branches: &ubuntu_jammy_test_image_branches
+ regex: ^stable/(yoga|xena|wallaby|victoria|ussuri)$
+ negate: true
- job:
name: manila-tempest-plugin-lvm-base
- nodeset: openstack-single-node-focal
description: |
Test LVM multibackend (DHSS=False) in a 4+6 (dual-stack) devstack
environment with IPv6 control plane endpoints.
@@ -176,6 +177,7 @@
- openstack/neutron-dynamic-routing
vars:
tempest_test_regex: '(^manila_tempest_tests.tests)(?=.*\[.*\bbackend\b.*\])'
+ tempest_exclude_regex: "(^manila_tempest_tests.tests.scenario.*)"
devstack_services: &devstack-with-ovs
# NOTE(gouthamr): LP#1940324 prevents bgp usage with OVN, disable OVN
br-ex-tcpdump: false
@@ -230,13 +232,25 @@
multi_backend: true
image_password: manila
+# NOTE(carloss): Nova bumped libvirt to a version available only on Ubuntu
+# Jammy. We are then forced to migrate this job to use Jammy. When LP Bug
+#1998489 is fixed, we will be able to unify the job above with this.
- job:
name: manila-tempest-plugin-lvm
description: |
- Test LVM multibackend (DHSS=False) in a 4+6 (dual-stack) devstack
- environment with IPv6 control plane endpoints.
- branches: *ubuntu_jammy_test_image_branches
+ Test LVM multibackend (DHSS=False) in a IPv4 environment.
+ branches:
+ regex: ^stable/(2023.1|zed|yoga|xena|wallaby|victoria|ussuri)$
+ negate: true
parent: manila-tempest-plugin-lvm-base
+ vars:
+ devstack_localrc:
+ MANILA_SETUP_IPV6: false
+ devstack_local_conf:
+ test-config:
+ $TEMPEST_CONFIG:
+ share:
+ run_ipv6_tests: false
- job:
name: manila-tempest-plugin-container
@@ -394,7 +408,6 @@
- job:
name: manila-tempest-plugin-cephfs-native
- nodeset: openstack-single-node-focal
description: Test CephFS Native (DHSS=False)
parent: manila-tempest-plugin-cephfs-native-base
branches: *ubuntu_jammy_test_image_branches
@@ -620,7 +633,6 @@
description: |
Test the GlusterFS driver (DHSS=False) with the native GlusterFS protocol
parent: manila-tempest-plugin-standalone-base
- nodeset: openstack-single-node-focal
required-projects:
- x/devstack-plugin-glusterfs
vars:
@@ -653,7 +665,6 @@
description: |
Test the GlusterFS driver (DHSS=False) with the native NFS protocol
parent: manila-tempest-plugin-standalone-base
- nodeset: openstack-single-node-focal
required-projects:
- x/devstack-plugin-glusterfs
vars:
@@ -724,7 +735,9 @@
- job:
name: manila-tempest-plugin-lvm-fips
parent: manila-tempest-plugin-lvm-fips-base
- branches: ^(?!stable/(yoga|xena|wallaby|victoria|ussuri)).*$
+ branches:
+ regex: ^stable/(yoga|xena|wallaby|victoria|ussuri)$
+ negate: true
- project-template:
name: manila-tempest-plugin-jobs-using-service-image
diff --git a/zuul.d/manila-tempest-stable-jobs.yaml b/zuul.d/manila-tempest-stable-jobs.yaml
index 28db229..0fb38ab 100644
--- a/zuul.d/manila-tempest-stable-jobs.yaml
+++ b/zuul.d/manila-tempest-stable-jobs.yaml
@@ -7,7 +7,7 @@
Test the scenario test cases on the generic driver multibackend
(DHSS=True) with NFS and CIFS
parent: manila-tempest-plugin-generic-scenario-base
- branches: &manila_tempest_image_pinned_branches ^(stable/(zed|yoga|xena)).*$
+ branches: &manila_tempest_image_pinned_branches ^stable/(2023.1|zed|yoga|xena)$
vars: &manila_tempest_image_pinned_vars
devstack_localrc:
# NOTE(carloss): Pinning manila service image to a Focal version,
@@ -27,15 +27,12 @@
- job:
name: manila-tempest-plugin-lvm-stable
- # NOTE(carloss): we are aware that focal is the current default, but
- # in order to avoid breakages when devstack-minimal switches to a newer
- # branch, we are pinning focal here.
- nodeset: openstack-single-node-focal
description: |
Test LVM multibackend (DHSS=False) in a 4+6 (dual-stack) devstack
environment with IPv6 control plane endpoints.
branches: *manila_tempest_image_pinned_branches
parent: manila-tempest-plugin-lvm-base
+ nodeset: openstack-single-node-focal
vars: *manila_tempest_image_pinned_vars
- job:
@@ -68,20 +65,18 @@
- job:
name: manila-tempest-plugin-lvm-fips-stable
parent: manila-tempest-plugin-lvm-fips-base
- branches: ^(stable/(yoga|xena)).*$
+ branches: ^stable/(yoga|xena)$
vars: *manila_tempest_image_pinned_vars
- job:
name: manila-tempest-plugin-lvm-yoga
parent: manila-tempest-plugin-lvm-base
- nodeset: openstack-single-node-focal
override-checkout: stable/yoga
vars: *manila_tempest_image_pinned_vars
- job:
name: manila-tempest-plugin-lvm-xena
parent: manila-tempest-plugin-lvm-base
- nodeset: openstack-single-node-focal
override-checkout: stable/xena
vars: *manila_tempest_image_pinned_vars