[Tempest] Add functional tests for share groups feature
Add functional tempest tests to new 'share group' feature [1].
[1] I79a80a62ae4e0015d6161edc2b93fd1f9ba69537
Co-Authored-By: Andrew Kerr <andrew.kerr@netapp.com>
Co-Authored-By: Valeriy Ponomaryov <vponomaryov@mirantis.com>
Partially-implements-blueprint: manila-share-groups
Depends-On: I8e29baed62355fc31caeec9c7a66eaebfcbdf184
Change-Id: I820eb959082995d961b1be992e4b2d1d1a985c1c
diff --git a/manila_tempest_tests/tests/api/admin/test_consistency_group_actions.py b/manila_tempest_tests/tests/api/admin/test_consistency_group_actions.py
deleted file mode 100644
index 8b2a21c..0000000
--- a/manila_tempest_tests/tests/api/admin/test_consistency_group_actions.py
+++ /dev/null
@@ -1,151 +0,0 @@
-# Copyright 2015 Andrew Kerr
-# All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from tempest import config
-from tempest.lib.common.utils import data_utils
-import testtools
-from testtools import testcase as tc
-
-from manila_tempest_tests.tests.api import base
-
-CONF = config.CONF
-
-
-@testtools.skipUnless(CONF.share.run_consistency_group_tests,
- 'Consistency Group tests disabled.')
-class ConsistencyGroupActionsTest(base.BaseSharesAdminTest):
-
- @classmethod
- def resource_setup(cls):
- super(ConsistencyGroupActionsTest, cls).resource_setup()
- # Create 2 share_types
- name = data_utils.rand_name("tempest-manila")
- extra_specs = cls.add_extra_specs_to_dict()
- share_type = cls.create_share_type(name, extra_specs=extra_specs)
- cls.share_type = share_type['share_type']
-
- name = data_utils.rand_name("tempest-manila")
- share_type = cls.create_share_type(name, extra_specs=extra_specs)
- cls.share_type2 = share_type['share_type']
-
- # Create a consistency group
- cls.consistency_group = cls.create_consistency_group(
- share_type_ids=[cls.share_type['id'], cls.share_type2['id']])
- cls.consistency_group = cls.shares_v2_client.get_consistency_group(
- cls.consistency_group['id'])
-
- @tc.attr(base.TAG_POSITIVE, base.TAG_BACKEND)
- def test_create_cg_from_cgsnapshot_with_multiple_share_types_v2_4(self):
- # Create cgsnapshot
- cgsnapshot = self.create_cgsnapshot_wait_for_active(
- self.consistency_group["id"],
- cleanup_in_class=False,
- version='2.4',
- )
-
- new_consistency_group = self.create_consistency_group(
- cleanup_in_class=False,
- source_cgsnapshot_id=cgsnapshot['id'],
- version='2.4',
- )
-
- # Verify share_types are the same
- expected_types = sorted(self.consistency_group['share_types'])
- actual_types = sorted(new_consistency_group['share_types'])
- self.assertEqual(expected_types, actual_types,
- 'Expected share types of %s, but got %s.' % (
- expected_types, actual_types))
-
- @tc.attr(base.TAG_POSITIVE, base.TAG_BACKEND)
- def test_create_cg_from_multi_typed_populated_cgsnapshot_v2_4(self):
- share_name = data_utils.rand_name("tempest-share-name")
- share_desc = data_utils.rand_name("tempest-share-description")
-
- shares = self.create_shares([
- {'kwargs': {
- 'cleanup_in_class': False,
- 'name': share_name,
- 'description': share_desc,
- 'consistency_group_id': self.consistency_group['id'],
- 'share_type_id': st_id,
- }} for st_id in (self.share_type['id'], self.share_type2['id'])
- ])
-
- cg_shares = self.shares_v2_client.list_shares(
- detailed=True,
- params={'consistency_group_id': self.consistency_group['id']},
- version='2.4',
- )
-
- cg_share_ids = [s['id'] for s in cg_shares]
- for share_id in (shares[0]['id'], shares[1]['id']):
- self.assertIn(share_id, cg_share_ids, 'Share %s not in '
- 'consistency group %s.' %
- (share_id, self.consistency_group['id']))
-
- cgsnap_name = data_utils.rand_name("tempest-cgsnap-name")
- cgsnap_desc = data_utils.rand_name("tempest-cgsnap-description")
- cgsnapshot = self.create_cgsnapshot_wait_for_active(
- self.consistency_group["id"],
- name=cgsnap_name,
- description=cgsnap_desc,
- cleanup_in_class=False,
- version='2.4',
- )
-
- new_cg = self.create_consistency_group(
- cleanup_in_class=False, source_cgsnapshot_id=cgsnapshot['id'],
- version='2.4')
- new_cg_shares = self.shares_v2_client.list_shares(
- detailed=True,
- params={'consistency_group_id': new_cg['id']},
- version='2.4')
-
- # TODO(akerr): Skip until bug 1483886 is resolved
- # Verify that the new shares correspond to correct share types
- # expected_share_types = [self.share_type['id'], self.share_type2[
- # 'id']]
- # actual_share_types = [s['share_type'] for s in new_cg_shares]
- # self.assertEqual(sorted(expected_share_types),
- # sorted(actual_share_types),
- # 'Expected shares of types %s, got %s.' % (
- # sorted(expected_share_types),
- # sorted(actual_share_types)))
-
- # Ensure that share_server information of the child CG and associated
- # shares match with that of the parent CG
- self.assertEqual(self.consistency_group['share_network_id'],
- new_cg['share_network_id'])
- self.assertEqual(self.consistency_group['share_server_id'],
- new_cg['share_server_id'])
-
- for share in new_cg_shares:
- msg = ('Share %(share)s has %(attr)s=%(value)s and it does not '
- 'match that of the parent CG where %(attr)s=%(orig)s.')
- payload = {
- 'share': share['id'],
- 'attr': 'share_network_id',
- 'value': share['share_network_id'],
- 'orig': self.consistency_group['share_network_id'],
- }
- self.assertEqual(self.consistency_group['share_network_id'],
- share['share_network_id'], msg % payload)
-
- payload.update({'attr': 'share_server_id',
- 'value': share['share_server_id'],
- 'orig': self.consistency_group['share_server_id'],
- })
- self.assertEqual(self.consistency_group['share_server_id'],
- share['share_server_id'], msg % payload)
diff --git a/manila_tempest_tests/tests/api/admin/test_consistency_groups.py b/manila_tempest_tests/tests/api/admin/test_consistency_groups.py
deleted file mode 100644
index 22678e5..0000000
--- a/manila_tempest_tests/tests/api/admin/test_consistency_groups.py
+++ /dev/null
@@ -1,99 +0,0 @@
-# Copyright 2015 Andrew Kerr
-# All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from tempest import config
-from tempest.lib.common.utils import data_utils
-import testtools
-from testtools import testcase as tc
-
-from manila_tempest_tests.tests.api import base
-
-CONF = config.CONF
-CG_REQUIRED_ELEMENTS = {"id", "name", "description", "created_at", "status",
- "share_types", "project_id", "host", "links"}
-
-
-@testtools.skipUnless(CONF.share.run_consistency_group_tests,
- 'Consistency Group tests disabled.')
-class ConsistencyGroupsTest(base.BaseSharesAdminTest):
-
- @classmethod
- def resource_setup(cls):
- super(ConsistencyGroupsTest, cls).resource_setup()
- # Create 2 share_types
- name = data_utils.rand_name("tempest-manila")
- extra_specs = cls.add_extra_specs_to_dict()
- share_type = cls.create_share_type(name, extra_specs=extra_specs)
- cls.share_type = share_type['share_type']
-
- name = data_utils.rand_name("tempest-manila")
- share_type = cls.create_share_type(name, extra_specs=extra_specs)
- cls.share_type2 = share_type['share_type']
-
- @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
- def test_create_cg_with_multiple_share_types_v2_4(self):
- # Create a consistency group
- consistency_group = self.create_consistency_group(
- cleanup_in_class=False,
- share_type_ids=[self.share_type['id'], self.share_type2['id']],
- version='2.4',
- )
-
- self.assertTrue(CG_REQUIRED_ELEMENTS.issubset(
- consistency_group.keys()),
- 'At least one expected element missing from consistency group '
- 'response. Expected %(expected)s, got %(actual)s.' % {
- "expected": CG_REQUIRED_ELEMENTS,
- "actual": consistency_group.keys()})
-
- actual_share_types = consistency_group['share_types']
- expected_share_types = [self.share_type['id'], self.share_type2['id']]
- self.assertEqual(sorted(expected_share_types),
- sorted(actual_share_types),
- 'Incorrect share types applied to consistency group '
- '%s. Expected %s, got %s' % (consistency_group['id'],
- expected_share_types,
- actual_share_types))
-
- @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
- @testtools.skipIf(
- not CONF.share.multitenancy_enabled, "Only for multitenancy.")
- def test_create_cg_from_cgsnapshot_verify_share_server_information(self):
- # Create a consistency group
- orig_consistency_group = self.create_consistency_group(
- cleanup_in_class=False,
- share_type_ids=[self.share_type['id']],
- version='2.4')
-
- # Get latest CG information
- orig_consistency_group = self.shares_v2_client.get_consistency_group(
- orig_consistency_group['id'], version='2.4')
-
- # Assert share server information
- self.assertIsNotNone(orig_consistency_group['share_network_id'])
- self.assertIsNotNone(orig_consistency_group['share_server_id'])
-
- cg_snapshot = self.create_cgsnapshot_wait_for_active(
- orig_consistency_group['id'], cleanup_in_class=False,
- version='2.4')
- new_consistency_group = self.create_consistency_group(
- cleanup_in_class=False, version='2.4',
- source_cgsnapshot_id=cg_snapshot['id'])
-
- # Assert share server information
- self.assertEqual(orig_consistency_group['share_network_id'],
- new_consistency_group['share_network_id'])
- self.assertEqual(orig_consistency_group['share_server_id'],
- new_consistency_group['share_server_id'])
diff --git a/manila_tempest_tests/tests/api/admin/test_consistency_groups_negative.py b/manila_tempest_tests/tests/api/admin/test_consistency_groups_negative.py
deleted file mode 100644
index a850379..0000000
--- a/manila_tempest_tests/tests/api/admin/test_consistency_groups_negative.py
+++ /dev/null
@@ -1,292 +0,0 @@
-# Copyright 2015 Andrew Kerr
-# All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from tempest import config
-from tempest.lib.common.utils import data_utils
-from tempest.lib import exceptions
-import testtools
-from testtools import testcase as tc
-
-from manila_tempest_tests.tests.api import base
-
-CONF = config.CONF
-
-
-@testtools.skipUnless(CONF.share.run_consistency_group_tests,
- 'Consistency Group tests disabled.')
-class ConsistencyGroupsNegativeTest(base.BaseSharesAdminTest):
-
- @classmethod
- def resource_setup(cls):
- super(ConsistencyGroupsNegativeTest, cls).resource_setup()
- # Create share_type
- name = data_utils.rand_name("tempest-manila")
- extra_specs = cls.add_extra_specs_to_dict()
- share_type = cls.create_share_type(name, extra_specs=extra_specs)
- cls.share_type = share_type['share_type']
-
- # Create a consistency group
- cls.consistency_group = cls.create_consistency_group(
- share_type_ids=[cls.share_type['id']])
-
- # Create share inside consistency group
- cls.share_name = data_utils.rand_name("tempest-share-name")
- cls.share_desc = data_utils.rand_name("tempest-share-description")
- cls.share = cls.create_share(
- name=cls.share_name,
- description=cls.share_desc,
- consistency_group_id=cls.consistency_group['id'],
- share_type_id=cls.share_type['id'],
- )
-
- # Create a cgsnapshot of the consistency group
- cls.cgsnap_name = data_utils.rand_name("tempest-cgsnap-name")
- cls.cgsnap_desc = data_utils.rand_name("tempest-cgsnap-description")
- cls.cgsnapshot = cls.create_cgsnapshot_wait_for_active(
- cls.consistency_group["id"],
- name=cls.cgsnap_name,
- description=cls.cgsnap_desc)
-
- @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
- def test_delete_share_type_in_use_by_cg(self):
- # Attempt delete of share type
- self.assertRaises(exceptions.BadRequest,
- self.shares_client.delete_share_type,
- self.share_type['id'])
-
- @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
- def test_create_share_of_unsupported_type_in_cg_v2_4(self):
- # Attempt to create share of default type in the cg
- self.assertRaises(exceptions.BadRequest,
- self.create_share,
- size=1,
- consistency_group_id=self.consistency_group['id'],
- version='2.4')
-
- @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
- def test_create_share_in_cg_that_is_not_available_v2_4(self):
- consistency_group = self.create_consistency_group(
- cleanup_in_class=False, version='2.4')
- self.addCleanup(self.shares_v2_client.consistency_group_reset_state,
- consistency_group['id'],
- status='available',
- version='2.4')
- # creating
- self.shares_v2_client.consistency_group_reset_state(
- consistency_group['id'], status='creating', version='2.4')
- self.shares_v2_client.wait_for_consistency_group_status(
- consistency_group['id'], 'creating')
- self.assertRaises(exceptions.BadRequest, self.create_share,
- name=self.share_name,
- description=self.share_desc,
- consistency_group_id=consistency_group['id'],
- cleanup_in_class=False,
- version='2.4')
- # deleting
- self.shares_v2_client.consistency_group_reset_state(
- consistency_group['id'], status='deleting', version='2.4')
- self.shares_v2_client.wait_for_consistency_group_status(
- consistency_group['id'], 'deleting')
- self.assertRaises(exceptions.BadRequest, self.create_share,
- name=self.share_name,
- description=self.share_desc,
- consistency_group_id=consistency_group['id'],
- cleanup_in_class=False,
- version='2.4')
- # error
- self.shares_v2_client.consistency_group_reset_state(
- consistency_group['id'], status='error', version='2.4')
- self.shares_v2_client.wait_for_consistency_group_status(
- consistency_group['id'], 'error')
- self.assertRaises(exceptions.BadRequest, self.create_share,
- name=self.share_name,
- description=self.share_desc,
- consistency_group_id=consistency_group['id'],
- cleanup_in_class=False,
- version='2.4')
-
- @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
- def test_create_cgsnapshot_of_cg_that_is_not_available_v2_4(self):
- consistency_group = self.create_consistency_group(
- cleanup_in_class=False, version='2.4')
- self.addCleanup(self.shares_v2_client.consistency_group_reset_state,
- consistency_group['id'],
- status='available',
- version='2.4')
- # creating
- self.shares_v2_client.consistency_group_reset_state(
- consistency_group['id'], status='creating', version='2.4')
- self.shares_v2_client.wait_for_consistency_group_status(
- consistency_group['id'], 'creating')
- self.assertRaises(exceptions.Conflict,
- self.create_cgsnapshot_wait_for_active,
- consistency_group['id'],
- cleanup_in_class=False,
- version='2.4')
- # deleting
- self.shares_v2_client.consistency_group_reset_state(
- consistency_group['id'], status='deleting', version='2.4')
- self.shares_v2_client.wait_for_consistency_group_status(
- consistency_group['id'], 'deleting')
- self.assertRaises(exceptions.Conflict,
- self.create_cgsnapshot_wait_for_active,
- consistency_group['id'],
- cleanup_in_class=False,
- version='2.4')
- # error
- self.shares_v2_client.consistency_group_reset_state(
- consistency_group['id'], status='error', version='2.4')
- self.shares_v2_client.wait_for_consistency_group_status(
- consistency_group['id'], 'error')
- self.assertRaises(exceptions.Conflict,
- self.create_cgsnapshot_wait_for_active,
- consistency_group['id'],
- cleanup_in_class=False,
- version='2.4')
-
- @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
- def test_create_cgsnapshot_of_cg_with_share_in_error_state_v2_4(self):
- consistency_group = self.create_consistency_group(version='2.4')
- share_name = data_utils.rand_name("tempest-share-name")
- share_desc = data_utils.rand_name("tempest-share-description")
- share = self.create_share(
- name=share_name,
- description=share_desc,
- consistency_group_id=consistency_group['id'],
- cleanup_in_class=False,
- version='2.4',
- )
- self.shares_client.reset_state(s_id=share['id'])
- self.shares_client.wait_for_share_status(share['id'], 'error')
- self.assertRaises(exceptions.Conflict,
- self.create_cgsnapshot_wait_for_active,
- consistency_group['id'],
- cleanup_in_class=False,
- version='2.4')
-
- @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
- def test_delete_cgsnapshot_not_in_available_or_error_v2_4(self):
- cgsnapshot = self.create_cgsnapshot_wait_for_active(
- self.consistency_group['id'],
- cleanup_in_class=False,
- version='2.4',
- )
- self.addCleanup(self.shares_v2_client.cgsnapshot_reset_state,
- cgsnapshot['id'],
- status='available',
- version='2.4')
-
- # creating
- self.shares_v2_client.cgsnapshot_reset_state(cgsnapshot['id'],
- status='creating',
- version='2.4')
- self.shares_v2_client.wait_for_cgsnapshot_status(cgsnapshot['id'],
- 'creating')
- self.assertRaises(exceptions.Conflict,
- self.shares_v2_client.delete_cgsnapshot,
- cgsnapshot['id'],
- version='2.4')
- # deleting
- self.shares_v2_client.cgsnapshot_reset_state(cgsnapshot['id'],
- status='deleting',
- version='2.4')
- self.shares_v2_client.wait_for_cgsnapshot_status(cgsnapshot['id'],
- 'deleting')
- self.assertRaises(exceptions.Conflict,
- self.shares_v2_client.delete_cgsnapshot,
- cgsnapshot['id'],
- version='2.4')
-
- @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
- def test_delete_cg_not_in_available_or_error_v2_4(self):
- consistency_group = self.create_consistency_group(
- cleanup_in_class=False, version='2.4')
- self.addCleanup(self.shares_v2_client.consistency_group_reset_state,
- consistency_group['id'],
- status='available',
- version='2.4')
- # creating
- self.shares_v2_client.consistency_group_reset_state(
- consistency_group['id'], status='creating', version='2.4')
- self.shares_v2_client.wait_for_consistency_group_status(
- consistency_group['id'], 'creating')
- self.assertRaises(exceptions.Conflict,
- self.shares_v2_client.delete_consistency_group,
- consistency_group['id'],
- version='2.4')
- # deleting
- self.shares_v2_client.consistency_group_reset_state(
- consistency_group['id'], status='deleting', version='2.4')
- self.shares_v2_client.wait_for_consistency_group_status(
- consistency_group['id'], 'deleting')
- self.assertRaises(exceptions.Conflict,
- self.shares_v2_client.delete_consistency_group,
- consistency_group['id'],
- version='2.4')
-
- @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
- def test_create_cg_with_conflicting_share_types_v2_4(self):
- # Create conflicting share types
- name = data_utils.rand_name("tempest-manila")
- extra_specs = {"driver_handles_share_servers": False}
- share_type = self.create_share_type(name, extra_specs=extra_specs)
- single_tenant_share_type = share_type['share_type']
-
- name = data_utils.rand_name("tempest-manila")
- extra_specs = {"driver_handles_share_servers": True}
- share_type = self.create_share_type(name, extra_specs=extra_specs)
- multi_tenant_share_type = share_type['share_type']
-
- self.assertRaises(exceptions.BadRequest,
- self.create_consistency_group,
- share_type_ids=[single_tenant_share_type['id'],
- multi_tenant_share_type['id']],
- cleanup_in_class=False,
- version='2.4')
-
- @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
- def test_create_cg_with_multi_tenant_share_type_and_no_share_network_v2_4(
- self):
- # Create multi tenant share type
- name = data_utils.rand_name("tempest-manila")
- extra_specs = {"driver_handles_share_servers": True}
- share_type = self.create_share_type(name, extra_specs=extra_specs)
- multi_tenant_share_type = share_type['share_type']
-
- def create_cg():
- cg = self.shares_v2_client.create_consistency_group(
- share_type_ids=[multi_tenant_share_type['id']],
- version='2.4'
- )
- resource = {
- "type": "consistency_group",
- "id": cg["id"],
- "client": self.shares_client
- }
- self.method_resources.insert(0, resource)
- return cg
-
- self.assertRaises(exceptions.BadRequest, create_cg)
-
- @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
- def test_update_cg_share_types(self):
- consistency_group = self.create_consistency_group(
- cleanup_in_class=False, version='2.4')
-
- self.assertRaises(exceptions.BadRequest,
- self.shares_v2_client.update_consistency_group,
- consistency_group['id'],
- share_types=[self.share_type['id']],
- version='2.4')
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
new file mode 100644
index 0000000..a6bda73
--- /dev/null
+++ b/manila_tempest_tests/tests/api/admin/test_share_group_types.py
@@ -0,0 +1,236 @@
+# Copyright 2016 Andrew Kerr
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from tempest import config
+from tempest.lib.common.utils import data_utils
+import testtools
+from testtools import testcase as tc
+
+from manila_tempest_tests.common import constants
+from manila_tempest_tests.tests.api import base
+
+CONF = config.CONF
+
+
+@testtools.skipUnless(
+ CONF.share.run_share_group_tests, 'Share Group tests disabled.')
+@base.skip_if_microversion_lt(constants.MIN_SHARE_GROUP_MICROVERSION)
+class ShareGroupTypesTest(base.BaseSharesAdminTest):
+
+ @classmethod
+ def resource_setup(cls):
+ super(ShareGroupTypesTest, cls).resource_setup()
+
+ # Create 2 share_types
+ name = data_utils.rand_name("tempest-manila")
+ extra_specs = cls.add_extra_specs_to_dict()
+ share_type = cls.create_share_type(name, extra_specs=extra_specs)
+ cls.share_type = share_type['share_type']
+
+ name = data_utils.rand_name("tempest-manila")
+ share_type = cls.create_share_type(name, extra_specs=extra_specs)
+ cls.share_type2 = share_type['share_type']
+
+ @tc.attr(base.TAG_POSITIVE, base.TAG_API)
+ def test_create_get_delete_share_group_type_min(self):
+ name = data_utils.rand_name("tempest-manila")
+
+ # Create share group type
+ sg_type_c = self.create_share_group_type(
+ name=name,
+ share_types=self.share_type['id'],
+ cleanup_in_class=False,
+ version=constants.MIN_SHARE_GROUP_MICROVERSION)
+
+ self.assertEqual(
+ [self.share_type['id']],
+ sg_type_c['share_types'],
+ 'Share type not applied correctly.')
+
+ # Read share group type
+ sg_type_r = self.shares_v2_client.get_share_group_type(sg_type_c['id'])
+ keys = set(sg_type_r.keys())
+ self.assertTrue(
+ constants.SHARE_GROUP_TYPE_REQUIRED_KEYS.issubset(keys),
+ 'At least one expected key missing from share group type '
+ 'response. Expected %s, got %s.' % (
+ constants.SHARE_GROUP_TYPE_REQUIRED_KEYS, keys))
+ self.assertEqual(sg_type_c['name'], sg_type_r['name'])
+
+ # Delete share group type
+ self.shares_v2_client.delete_share_group_type(
+ sg_type_r['id'], version=constants.MIN_SHARE_GROUP_MICROVERSION)
+ self.shares_v2_client.wait_for_resource_deletion(
+ share_group_type_id=sg_type_r['id'])
+
+ @tc.attr(base.TAG_POSITIVE, base.TAG_API)
+ def test_create_share_group_type_multiple_share_types_min(self):
+ name = data_utils.rand_name("tempest-manila")
+
+ sg_type = self.create_share_group_type(
+ name=name,
+ share_types=[self.share_type['id'], self.share_type2['id']],
+ cleanup_in_class=False,
+ version=constants.MIN_SHARE_GROUP_MICROVERSION)
+
+ self.assertEqual(
+ {self.share_type['id'], self.share_type2['id']},
+ set(sg_type['share_types']),
+ 'Share types not applied correctly.')
+
+ @tc.attr(base.TAG_POSITIVE, base.TAG_API)
+ def test_create_share_group_type_with_one_spec_min(self):
+ name = data_utils.rand_name("tempest-manila")
+ 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)
+
+ self.assertDictMatch(group_specs, sg_type['group_specs'])
+
+ @tc.attr(base.TAG_POSITIVE, base.TAG_API)
+ def test_create_share_group_type_with_multiple_specs_min(self):
+ name = data_utils.rand_name("tempest-manila")
+ 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)
+
+ self.assertDictMatch(group_specs, sg_type['group_specs'])
+
+ @tc.attr(base.TAG_POSITIVE, base.TAG_API)
+ def test_update_single_share_group_type_spec_min(self):
+ name = data_utils.rand_name("tempest-manila")
+ 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)
+
+ self.assertDictMatch(group_specs, sg_type['group_specs'])
+
+ group_specs = {'key1': 'value3', 'key2': 'value2'}
+
+ self.shares_v2_client.update_share_group_type_spec(
+ sg_type['id'], 'key1', 'value3')
+ sg_type = self.shares_v2_client.get_share_group_type(sg_type['id'])
+
+ self.assertDictMatch(group_specs, sg_type['group_specs'])
+
+ @tc.attr(base.TAG_POSITIVE, base.TAG_API)
+ def test_update_all_share_group_type_specs_min(self):
+ name = data_utils.rand_name("tempest-manila")
+ 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)
+
+ self.assertDictMatch(group_specs, sg_type['group_specs'])
+
+ group_specs = {'key1': 'value3', 'key2': 'value4'}
+
+ self.shares_v2_client.update_share_group_type_specs(
+ sg_type['id'], group_specs)
+ sg_type = self.shares_v2_client.get_share_group_type(sg_type['id'])
+
+ self.assertDictMatch(group_specs, sg_type['group_specs'])
+
+ @tc.attr(base.TAG_POSITIVE, base.TAG_API)
+ def test_delete_single_share_group_type_spec_min(self):
+ name = data_utils.rand_name("tempest-manila")
+ 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)
+
+ self.assertDictMatch(group_specs, sg_type['group_specs'])
+
+ key_to_delete = 'key1'
+ group_specs.pop(key_to_delete)
+
+ self.shares_v2_client.delete_share_group_type_spec(
+ sg_type['id'], key_to_delete)
+ sg_type = self.shares_v2_client.get_share_group_type(
+ sg_type['id'])
+
+ self.assertDictMatch(group_specs, sg_type['group_specs'])
+
+ @tc.attr(base.TAG_POSITIVE, base.TAG_API)
+ def test_private_share_group_type_access(self):
+ name = data_utils.rand_name("tempest-manila")
+ group_specs = {"key1": "value1", "key2": "value2"}
+ project_id = self.shares_v2_client.tenant_id
+
+ # Create private share group type
+ sgt_create = self.create_share_group_type(
+ name=name,
+ share_types=[self.share_type['id']],
+ is_public=False,
+ group_specs=group_specs,
+ )
+ self.assertEqual(name, sgt_create['name'])
+ sgt_id = sgt_create["id"]
+
+ # It should not be listed without access
+ sgt_list = self.shares_v2_client.list_share_group_types()
+ self.assertFalse(any(sgt_id == sgt["id"] for sgt in sgt_list))
+
+ # List projects that have access for share group type - none expected
+ access = self.shares_v2_client.list_access_to_share_group_type(sgt_id)
+ self.assertEqual([], access)
+
+ # Add project access to share group type
+ access = self.shares_v2_client.add_access_to_share_group_type(
+ sgt_id, project_id)
+
+ # Now it should be listed
+ sgt_list = self.shares_v2_client.list_share_group_types()
+ self.assertTrue(any(sgt_id == sgt["id"] for sgt in sgt_list))
+
+ # List projects that have access for share group type - one expected
+ access = self.shares_v2_client.list_access_to_share_group_type(sgt_id)
+ expected = [{'share_group_type_id': sgt_id, 'project_id': project_id}]
+ self.assertEqual(expected, access)
+
+ # Remove project access from share group type
+ access = self.shares_v2_client.remove_access_from_share_group_type(
+ sgt_id, project_id)
+
+ # It should not be listed without access
+ sgt_list = self.shares_v2_client.list_share_group_types()
+ self.assertFalse(any(sgt_id == sgt["id"] for sgt in sgt_list))
+
+ # List projects that have access for share group type - none expected
+ access = self.shares_v2_client.list_access_to_share_group_type(sgt_id)
+ self.assertEqual([], access)
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
new file mode 100644
index 0000000..e7f6824
--- /dev/null
+++ b/manila_tempest_tests/tests/api/admin/test_share_group_types_negative.py
@@ -0,0 +1,128 @@
+# 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.common.utils import data_utils
+from tempest.lib import exceptions as lib_exc
+import testtools
+from testtools import testcase as tc
+
+from manila_tempest_tests.common import constants
+from manila_tempest_tests.tests.api import base
+
+CONF = config.CONF
+
+
+@testtools.skipUnless(
+ CONF.share.run_share_group_tests, 'Share Group tests disabled.')
+@base.skip_if_microversion_lt(constants.MIN_SHARE_GROUP_MICROVERSION)
+class ShareGroupTypesAdminNegativeTest(base.BaseSharesMixedTest):
+
+ @classmethod
+ def resource_setup(cls):
+ super(ShareGroupTypesAdminNegativeTest, cls).resource_setup()
+ cls.share_type = cls.create_share_type(
+ data_utils.rand_name("unique_st_name"),
+ extra_specs=cls.add_extra_specs_to_dict({"key": "value"}),
+ client=cls.admin_shares_v2_client)
+ cls.share_group_type = cls.create_share_group_type(
+ data_utils.rand_name("unique_sgt_name"),
+ share_types=[cls.share_type['share_type']['id']],
+ client=cls.admin_shares_v2_client)
+
+ @tc.attr(base.TAG_NEGATIVE, base.TAG_API)
+ def test_create_share_ggroup_with_nonexistent_share_type(self):
+ self.assertRaises(
+ lib_exc.BadRequest,
+ self.admin_shares_v2_client.create_share_group_type,
+ share_types=data_utils.rand_name("fake"))
+
+ @tc.attr(base.TAG_NEGATIVE, base.TAG_API)
+ def test_create_share_group_type_with_empty_name(self):
+ self.assertRaises(
+ lib_exc.BadRequest,
+ self.create_share_group_type, '',
+ client=self.admin_shares_v2_client)
+
+ @tc.attr(base.TAG_NEGATIVE, base.TAG_API)
+ def test_create_share_group_type_with_too_big_name(self):
+ self.assertRaises(
+ lib_exc.BadRequest,
+ self.create_share_group_type,
+ "x" * 256, client=self.admin_shares_v2_client)
+
+ @tc.attr(base.TAG_NEGATIVE, base.TAG_API)
+ def test_get_share_group_type_using_nonexistent_id(self):
+ self.assertRaises(
+ lib_exc.NotFound,
+ self.admin_shares_v2_client.get_share_group_type,
+ data_utils.rand_name("fake"))
+
+ @tc.attr(base.TAG_NEGATIVE, base.TAG_API)
+ def test_try_delete_share_group_type_using_nonexistent_id(self):
+ self.assertRaises(
+ lib_exc.NotFound,
+ self.admin_shares_v2_client.delete_share_group_type,
+ data_utils.rand_name("fake"))
+
+ @tc.attr(base.TAG_NEGATIVE, base.TAG_API)
+ def test_try_create_duplicate_of_share_group_type(self):
+ unique_name = data_utils.rand_name("unique_sgt_name")
+ list_of_ids = set()
+ for step in (1, 2):
+ sg_type = self.create_share_group_type(
+ unique_name,
+ share_types=[self.share_type['share_type']['id']],
+ client=self.admin_shares_v2_client,
+ cleanup_in_class=False)
+ self.assertRaises(
+ lib_exc.Conflict,
+ self.create_share_group_type,
+ unique_name,
+ share_types=[self.share_type['share_type']['id']],
+ client=self.admin_shares_v2_client)
+ list_of_ids.add(sg_type['id'])
+ self.assertEqual(unique_name, sg_type['name'])
+ self.admin_shares_v2_client.delete_share_group_type(sg_type['id'])
+ self.assertEqual(2, len(list_of_ids))
+
+ @tc.attr(base.TAG_NEGATIVE, base.TAG_API)
+ def test_add_project_access_to_public_share_group_type(self):
+ self.assertRaises(
+ lib_exc.Conflict,
+ self.admin_shares_v2_client.add_access_to_share_group_type,
+ self.share_group_type["id"],
+ self.admin_shares_v2_client.tenant_id)
+
+ @tc.attr(base.TAG_NEGATIVE, base.TAG_API)
+ def test_remove_project_access_from_public_share_group_type(self):
+ self.assertRaises(
+ lib_exc.Conflict,
+ self.admin_shares_v2_client.remove_access_from_share_group_type,
+ self.share_group_type["id"],
+ self.admin_shares_v2_client.tenant_id)
+
+ @tc.attr(base.TAG_NEGATIVE, base.TAG_API)
+ def test_add_project_access_to_nonexistent_share_group_type(self):
+ self.assertRaises(
+ lib_exc.NotFound,
+ self.admin_shares_v2_client.add_access_to_share_group_type,
+ data_utils.rand_name("fake"),
+ self.admin_shares_v2_client.tenant_id)
+
+ @tc.attr(base.TAG_NEGATIVE, base.TAG_API)
+ def test_remove_project_access_from_nonexistent_share_group_type(self):
+ self.assertRaises(
+ lib_exc.NotFound,
+ self.admin_shares_v2_client.remove_access_from_share_group_type,
+ data_utils.rand_name("fake"),
+ self.admin_shares_v2_client.tenant_id)
diff --git a/manila_tempest_tests/tests/api/admin/test_share_groups.py b/manila_tempest_tests/tests/api/admin/test_share_groups.py
new file mode 100644
index 0000000..e289005
--- /dev/null
+++ b/manila_tempest_tests/tests/api/admin/test_share_groups.py
@@ -0,0 +1,175 @@
+# Copyright 2016 Andrew Kerr
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from tempest import config
+from tempest.lib.common.utils import data_utils
+import testtools
+from testtools import testcase as tc
+
+from manila_tempest_tests.common import constants
+from manila_tempest_tests.tests.api import base
+
+CONF = config.CONF
+
+
+@testtools.skipUnless(
+ CONF.share.run_share_group_tests, 'Share Group tests disabled.')
+@base.skip_if_microversion_lt(constants.MIN_SHARE_GROUP_MICROVERSION)
+class ShareGroupsTest(base.BaseSharesAdminTest):
+
+ @classmethod
+ def resource_setup(cls):
+ super(ShareGroupsTest, cls).resource_setup()
+ # Create 2 share_types
+ name = data_utils.rand_name("tempest-manila")
+ extra_specs = cls.add_extra_specs_to_dict()
+ share_type = cls.create_share_type(name, extra_specs=extra_specs)
+ cls.share_type = share_type['share_type']
+
+ name = data_utils.rand_name("tempest-manila")
+ share_type = cls.create_share_type(name, extra_specs=extra_specs)
+ cls.share_type2 = share_type['share_type']
+
+ cls.sg_type = cls.create_share_group_type(
+ name=name,
+ share_types=[cls.share_type['id'], cls.share_type2['id']],
+ cleanup_in_class=True,
+ version=constants.MIN_SHARE_GROUP_MICROVERSION)
+
+ @tc.attr(base.TAG_POSITIVE, base.TAG_API)
+ def test_create_share_group_with_single_share_type_min(self):
+ share_group = self.create_share_group(
+ share_group_type_id=self.sg_type['id'],
+ cleanup_in_class=False,
+ share_type_ids=[self.share_type['id']],
+ version=constants.MIN_SHARE_GROUP_MICROVERSION)
+
+ keys = set(share_group.keys())
+ self.assertTrue(
+ constants.SHARE_GROUP_DETAIL_REQUIRED_KEYS.issubset(keys),
+ 'At least one expected element missing from share group '
+ 'response. Expected %(expected)s, got %(actual)s.' % {
+ "expected": constants.SHARE_GROUP_DETAIL_REQUIRED_KEYS,
+ "actual": keys})
+
+ actual_sg_type = share_group['share_group_type_id']
+ expected_sg_type = self.sg_type['id']
+ self.assertEqual(
+ expected_sg_type, actual_sg_type,
+ 'Incorrect share group type applied to share group '
+ '%s. Expected %s, got %s' % (
+ share_group['id'], expected_sg_type, actual_sg_type))
+
+ actual_share_types = share_group['share_types']
+ expected_share_types = [self.share_type['id']]
+ self.assertEqual(
+ sorted(expected_share_types),
+ sorted(actual_share_types),
+ 'Incorrect share types applied to share group %s. '
+ 'Expected %s, got %s' % (
+ share_group['id'], expected_share_types, actual_share_types))
+
+ @tc.attr(base.TAG_POSITIVE, base.TAG_API)
+ def test_create_share_group_with_multiple_share_types_min(self):
+ share_group = self.create_share_group(
+ share_group_type_id=self.sg_type['id'],
+ cleanup_in_class=False,
+ share_type_ids=[self.share_type['id'], self.share_type2['id']],
+ version=constants.MIN_SHARE_GROUP_MICROVERSION)
+
+ keys = set(share_group.keys())
+ self.assertTrue(
+ constants.SHARE_GROUP_DETAIL_REQUIRED_KEYS.issubset(keys),
+ 'At least one expected element missing from share group '
+ 'response. Expected %(expected)s, got %(actual)s.' % {
+ "expected": constants.SHARE_GROUP_DETAIL_REQUIRED_KEYS,
+ "actual": keys})
+
+ actual_sg_type = share_group['share_group_type_id']
+ expected_sg_type = self.sg_type['id']
+ self.assertEqual(
+ expected_sg_type, actual_sg_type,
+ 'Incorrect share group type applied to share group %s. '
+ 'Expected %s, got %s' % (
+ share_group['id'], expected_sg_type, actual_sg_type))
+
+ actual_share_types = share_group['share_types']
+ expected_share_types = [self.share_type['id'], self.share_type2['id']]
+ self.assertEqual(
+ sorted(expected_share_types),
+ sorted(actual_share_types),
+ 'Incorrect share types applied to share group %s. '
+ 'Expected %s, got %s' % (
+ share_group['id'], expected_share_types, actual_share_types))
+
+ @tc.attr(base.TAG_POSITIVE, base.TAG_API)
+ def test_default_share_group_type_applied(self):
+ default_type = self.shares_v2_client.get_default_share_group_type()
+ default_share_types = default_type['share_types']
+
+ share_group = self.create_share_group(
+ cleanup_in_class=False,
+ share_type_ids=default_share_types,
+ version=constants.MIN_SHARE_GROUP_MICROVERSION)
+
+ keys = set(share_group.keys())
+ self.assertTrue(
+ constants.SHARE_GROUP_DETAIL_REQUIRED_KEYS.issubset(keys),
+ 'At least one expected element missing from share group '
+ 'response. Expected %(expected)s, got %(actual)s.' % {
+ "expected": constants.SHARE_GROUP_DETAIL_REQUIRED_KEYS,
+ "actual": keys})
+
+ actual_sg_type = share_group['share_group_type_id']
+ expected_sg_type = default_type['id']
+ self.assertEqual(
+ expected_sg_type, actual_sg_type,
+ 'Incorrect share group type applied to share group %s. '
+ 'Expected %s, got %s' % (
+ share_group['id'], expected_sg_type, actual_sg_type))
+
+ @testtools.skipUnless(
+ CONF.share.multitenancy_enabled, "Only for multitenancy.")
+ @tc.attr(base.TAG_POSITIVE, base.TAG_API)
+ def test_create_sg_from_snapshot_verify_share_server_information_min(self):
+ # Create a share group
+ orig_sg = self.create_share_group(
+ share_group_type_id=self.sg_type['id'],
+ cleanup_in_class=False,
+ share_type_ids=[self.share_type['id']],
+ version=constants.MIN_SHARE_GROUP_MICROVERSION)
+
+ # Get latest share group information
+ orig_sg = self.shares_v2_client.get_share_group(
+ orig_sg['id'], version=constants.MIN_SHARE_GROUP_MICROVERSION)
+
+ # Assert share server information
+ self.assertIsNotNone(orig_sg['share_network_id'])
+ self.assertIsNotNone(orig_sg['share_server_id'])
+
+ sg_snapshot = self.create_share_group_snapshot_wait_for_active(
+ orig_sg['id'], cleanup_in_class=False,
+ version=constants.MIN_SHARE_GROUP_MICROVERSION)
+ new_sg = self.create_share_group(
+ share_group_type_id=self.sg_type['id'],
+ cleanup_in_class=False,
+ version=constants.MIN_SHARE_GROUP_MICROVERSION,
+ source_share_group_snapshot_id=sg_snapshot['id'])
+
+ # Assert share server information
+ self.assertEqual(
+ orig_sg['share_network_id'], new_sg['share_network_id'])
+ self.assertEqual(
+ orig_sg['share_server_id'], new_sg['share_server_id'])
diff --git a/manila_tempest_tests/tests/api/base.py b/manila_tempest_tests/tests/api/base.py
index 48cd3ed..7837195 100644
--- a/manila_tempest_tests/tests/api/base.py
+++ b/manila_tempest_tests/tests/api/base.py
@@ -392,7 +392,7 @@
def _create_share(cls, share_protocol=None, size=None, name=None,
snapshot_id=None, description=None, metadata=None,
share_network_id=None, share_type_id=None,
- consistency_group_id=None, client=None,
+ share_group_id=None, client=None,
cleanup_in_class=True, is_public=False, **kwargs):
client = client or cls.shares_v2_client
description = description or "Tempest's share"
@@ -410,12 +410,12 @@
'share_type_id': share_type_id,
'is_public': is_public,
})
- if consistency_group_id:
- kwargs['consistency_group_id'] = consistency_group_id
+ if share_group_id:
+ kwargs['share_group_id'] = share_group_id
share = client.create_share(**kwargs)
resource = {"type": "share", "id": share["id"], "client": client,
- "consistency_group_id": consistency_group_id}
+ "share_group_id": share_group_id}
cleanup_list = (cls.class_resources if cleanup_in_class else
cls.method_resources)
cleanup_list.insert(0, resource)
@@ -540,41 +540,63 @@
return [d["share"] for d in data]
@classmethod
- def create_consistency_group(cls, client=None, cleanup_in_class=True,
- share_network_id=None, **kwargs):
+ def create_share_group(cls, client=None, cleanup_in_class=True,
+ share_network_id=None, **kwargs):
client = client or cls.shares_v2_client
- if kwargs.get('source_cgsnapshot_id') is None:
+ if kwargs.get('source_share_group_snapshot_id') is None:
kwargs['share_network_id'] = (share_network_id or
client.share_network_id or None)
- consistency_group = client.create_consistency_group(**kwargs)
+ share_group = client.create_share_group(**kwargs)
resource = {
- "type": "consistency_group",
- "id": consistency_group["id"],
- "client": client}
+ "type": "share_group",
+ "id": share_group["id"],
+ "client": client,
+ }
if cleanup_in_class:
cls.class_resources.insert(0, resource)
else:
cls.method_resources.insert(0, resource)
- if kwargs.get('source_cgsnapshot_id'):
- new_cg_shares = client.list_shares(
+ if kwargs.get('source_share_group_snapshot_id'):
+ new_share_group_shares = client.list_shares(
detailed=True,
- params={'consistency_group_id': consistency_group['id']})
+ params={'share_group_id': share_group['id']},
+ experimental=True)
- for share in new_cg_shares:
+ for share in new_share_group_shares:
resource = {"type": "share",
"id": share["id"],
"client": client,
- "consistency_group_id": share.get(
- 'consistency_group_id')}
+ "share_group_id": share.get("share_group_id")}
if cleanup_in_class:
cls.class_resources.insert(0, resource)
else:
cls.method_resources.insert(0, resource)
- client.wait_for_consistency_group_status(consistency_group['id'],
- 'available')
- return consistency_group
+ client.wait_for_share_group_status(share_group['id'], 'available')
+ return share_group
+
+ @classmethod
+ def create_share_group_type(cls, name=None, share_types=(), is_public=None,
+ group_specs=None, client=None,
+ cleanup_in_class=True, **kwargs):
+ client = client or cls.shares_v2_client
+ share_group_type = client.create_share_group_type(
+ name=name,
+ share_types=share_types,
+ is_public=is_public,
+ group_specs=group_specs,
+ **kwargs)
+ resource = {
+ "type": "share_group_type",
+ "id": share_group_type["id"],
+ "client": client,
+ }
+ if cleanup_in_class:
+ cls.class_resources.insert(0, resource)
+ else:
+ cls.method_resources.insert(0, resource)
+ return share_group_type
@classmethod
def create_snapshot_wait_for_active(cls, share_id, name=None,
@@ -598,28 +620,26 @@
return snapshot
@classmethod
- def create_cgsnapshot_wait_for_active(cls, consistency_group_id,
- name=None, description=None,
- client=None, cleanup_in_class=True,
- **kwargs):
+ def create_share_group_snapshot_wait_for_active(
+ cls, share_group_id, name=None, description=None, client=None,
+ cleanup_in_class=True, **kwargs):
client = client or cls.shares_v2_client
if description is None:
- description = "Tempest's cgsnapshot"
- cgsnapshot = client.create_cgsnapshot(consistency_group_id,
- name=name,
- description=description,
- **kwargs)
+ description = "Tempest's share group snapshot"
+ sg_snapshot = client.create_share_group_snapshot(
+ share_group_id, name=name, description=description, **kwargs)
resource = {
- "type": "cgsnapshot",
- "id": cgsnapshot["id"],
+ "type": "share_group_snapshot",
+ "id": sg_snapshot["id"],
"client": client,
}
if cleanup_in_class:
cls.class_resources.insert(0, resource)
else:
cls.method_resources.insert(0, resource)
- client.wait_for_cgsnapshot_status(cgsnapshot["id"], "available")
- return cgsnapshot
+ client.wait_for_share_group_snapshot_status(
+ sg_snapshot["id"], "available")
+ return sg_snapshot
@classmethod
def get_availability_zones(cls, client=None):
@@ -800,7 +820,6 @@
:param resources: dict with keys 'type','id','client' and 'deleted'
"""
-
if resources is None:
resources = cls.method_resources
for res in resources:
@@ -814,9 +833,9 @@
with handle_cleanup_exceptions():
if res["type"] is "share":
cls.clear_share_replicas(res_id)
- cg_id = res.get('consistency_group_id')
- if cg_id:
- params = {'consistency_group_id': cg_id}
+ share_group_id = res.get('share_group_id')
+ if share_group_id:
+ params = {'share_group_id': share_group_id}
client.delete_share(res_id, params=params)
else:
client.delete_share(res_id)
@@ -833,12 +852,18 @@
elif res["type"] is "share_type":
client.delete_share_type(res_id)
client.wait_for_resource_deletion(st_id=res_id)
- elif res["type"] is "consistency_group":
- client.delete_consistency_group(res_id)
- client.wait_for_resource_deletion(cg_id=res_id)
- elif res["type"] is "cgsnapshot":
- client.delete_cgsnapshot(res_id)
- client.wait_for_resource_deletion(cgsnapshot_id=res_id)
+ elif res["type"] is "share_group":
+ client.delete_share_group(res_id)
+ client.wait_for_resource_deletion(
+ share_group_id=res_id)
+ elif res["type"] is "share_group_type":
+ client.delete_share_group_type(res_id)
+ client.wait_for_resource_deletion(
+ share_group_type_id=res_id)
+ elif res["type"] is "share_group_snapshot":
+ client.delete_share_group_snapshot(res_id)
+ client.wait_for_resource_deletion(
+ share_group_snapshot_id=res_id)
elif res["type"] is "share_replica":
client.delete_share_replica(res_id)
client.wait_for_resource_deletion(replica_id=res_id)
diff --git a/manila_tempest_tests/tests/api/test_consistency_group_actions.py b/manila_tempest_tests/tests/api/test_consistency_group_actions.py
deleted file mode 100644
index 7a7c54d..0000000
--- a/manila_tempest_tests/tests/api/test_consistency_group_actions.py
+++ /dev/null
@@ -1,377 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright 2015 Andrew Kerr
-# All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from tempest import config
-from tempest.lib.common.utils import data_utils
-import testtools
-from testtools import testcase as tc
-
-from manila_tempest_tests.tests.api import base
-
-CONF = config.CONF
-
-CG_SIMPLE_KEYS = {"id", "name", "links"}
-CG_DETAIL_REQUIRED_KEYS = {"id", "name", "description", "created_at", "status",
- "project_id", "host", "links"}
-CGSNAPSHOT_SIMPLE_KEYS = {"id", "name", "links"}
-CGSNAPSHOT_DETAIL_REQUIRED_KEYS = {"id", "name", "description", "created_at",
- "status", "project_id", "links"}
-
-
-@testtools.skipUnless(CONF.share.run_consistency_group_tests,
- 'Consistency Group tests disabled.')
-class ConsistencyGroupActionsTest(base.BaseSharesTest):
- """Covers consistency group functionality."""
-
- @classmethod
- def resource_setup(cls):
- super(ConsistencyGroupActionsTest, cls).resource_setup()
-
- # Create first consistency group
- cls.cg_name = data_utils.rand_name("tempest-cg-name")
- cls.cg_desc = data_utils.rand_name("tempest-cg-description")
- cls.cg = cls.create_consistency_group(
- name=cls.cg_name, description=cls.cg_desc)
-
- # Create second consistency group for purposes of sorting and snapshot
- # filtering
- cls.cg2 = cls.create_consistency_group(
- name=cls.cg_name, description=cls.cg_desc)
-
- # Create 2 shares inside first CG and 1 inside second CG
- cls.share_name = data_utils.rand_name("tempest-share-name")
- cls.share_desc = data_utils.rand_name("tempest-share-description")
- cls.share_size = CONF.share.share_size
- cls.share_size2 = cls.share_size + 1
- cls.shares = cls.create_shares([
- {'kwargs': {
- 'name': cls.share_name,
- 'description': cls.share_desc,
- 'size': size,
- 'consistency_group_id': cg_id,
- }} for size, cg_id in ((cls.share_size, cls.cg['id']),
- (cls.share_size2, cls.cg['id']),
- (cls.share_size, cls.cg2['id']))
- ])
-
- # Create CG snapshots
- cls.cgsnap_name = data_utils.rand_name("tempest-cgsnap-name")
- cls.cgsnap_desc = data_utils.rand_name("tempest-cgsnap-description")
-
- cls.cgsnapshot = cls.create_cgsnapshot_wait_for_active(
- cls.cg["id"],
- name=cls.cgsnap_name,
- description=cls.cgsnap_desc)
-
- cls.cgsnapshot2 = cls.create_cgsnapshot_wait_for_active(
- cls.cg2['id'], name=cls.cgsnap_name, description=cls.cgsnap_desc)
-
- @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
- def test_get_consistency_group_v2_4(self):
-
- # Get consistency group
- consistency_group = self.shares_v2_client.get_consistency_group(
- self.cg['id'], version='2.4')
-
- # Verify keys
- actual_keys = set(consistency_group.keys())
- self.assertTrue(CG_DETAIL_REQUIRED_KEYS.issubset(actual_keys),
- 'Not all required keys returned for consistency '
- 'group %s. Expected at least: %s, found %s' % (
- consistency_group['id'],
- CG_DETAIL_REQUIRED_KEYS,
- actual_keys))
-
- # Verify values
- msg = "Expected name: '%s', actual name: '%s'" % (
- self.cg_name, consistency_group["name"])
- self.assertEqual(self.cg_name, str(consistency_group["name"]), msg)
-
- msg = "Expected description: '%s', actual description: '%s'" % (
- self.cg_desc, consistency_group["description"])
- self.assertEqual(self.cg_desc, str(consistency_group["description"]),
- msg)
-
- @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
- def test_get_share_v2_4(self):
-
- # Get share
- share = self.shares_v2_client.get_share(self.shares[0]['id'],
- version='2.4')
-
- # Verify keys
- expected_keys = {"status", "description", "links", "availability_zone",
- "created_at", "export_location", "share_proto",
- "name", "snapshot_id", "id", "size",
- "consistency_group_id"}
- actual_keys = set(share.keys())
- self.assertTrue(expected_keys.issubset(actual_keys),
- 'Not all required keys returned for share %s. '
- 'Expected at least: %s, found %s' % (share['id'],
- expected_keys,
- actual_keys))
-
- # Verify values
- msg = "Expected name: '%s', actual name: '%s'" % (self.share_name,
- share["name"])
- self.assertEqual(self.share_name, str(share["name"]), msg)
-
- msg = "Expected description: '%s', actual description: '%s'" % (
- self.share_desc, share["description"])
- self.assertEqual(self.share_desc, str(share["description"]), msg)
-
- msg = "Expected size: '%s', actual size: '%s'" % (self.share_size,
- share["size"])
- self.assertEqual(self.share_size, int(share["size"]), msg)
-
- msg = "Expected consistency_group_id: '%s', actual value: '%s'" % (
- self.cg["id"], share["consistency_group_id"])
- self.assertEqual(self.cg["id"], share["consistency_group_id"], msg)
-
- @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
- def test_list_consistency_groups_v2_4(self):
-
- # List consistency groups
- consistency_groups = self.shares_v2_client.list_consistency_groups(
- version='2.4')
-
- # Verify keys
- [self.assertEqual(CG_SIMPLE_KEYS, set(cg.keys())) for cg in
- consistency_groups]
-
- # Consistency group ids are in list exactly once
- for cg_id in (self.cg["id"], self.cg2["id"]):
- gen = [cgid["id"] for cgid in consistency_groups
- if cgid["id"] == cg_id]
- msg = ("Expected id %s exactly once in consistency group list" %
- cg_id)
- self.assertEqual(1, len(gen), msg)
-
- @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
- def test_list_consistency_groups_with_detail_v2_4(self):
-
- # List consistency groups
- consistency_groups = self.shares_v2_client.list_consistency_groups(
- detailed=True, version='2.4')
-
- # Verify keys
- [self.assertTrue(CG_DETAIL_REQUIRED_KEYS.issubset(set(cg.keys())))
- for cg in consistency_groups]
-
- # Consistency group ids are in list exactly once
- for cg_id in (self.cg["id"], self.cg2["id"]):
- gen = [cgid["id"] for cgid in consistency_groups
- if cgid["id"] == cg_id]
- msg = ("Expected id %s exactly once in consistency group list" %
- cg_id)
- self.assertEqual(1, len(gen), msg)
-
- @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
- def test_filter_shares_by_consistency_group_id_v2_4(self):
-
- shares = self.shares_v2_client.list_shares(
- detailed=True,
- params={'consistency_group_id': self.cg['id']},
- version='2.4'
- )
-
- share_ids = [share['id'] for share in shares]
-
- self.assertEqual(2, len(shares),
- 'Incorrect number of shares returned. Expected 2, '
- 'got %s' % len(shares))
- self.assertIn(self.shares[0]['id'], share_ids,
- 'Share %s expected in returned list, but got %s'
- % (self.shares[0]['id'], share_ids))
- self.assertIn(self.shares[1]['id'], share_ids,
- 'Share %s expected in returned list, but got %s'
- % (self.shares[0]['id'], share_ids))
-
- @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
- def test_get_cgsnapshot_v2_4(self):
-
- # Get consistency group
- consistency_group = self.shares_v2_client.get_consistency_group(
- self.cg['id'], version='2.4')
-
- # Verify keys
- actual_keys = set(consistency_group.keys())
- self.assertTrue(CG_DETAIL_REQUIRED_KEYS.issubset(actual_keys),
- 'Not all required keys returned for consistency '
- 'group %s. Expected at least: %s, found %s' % (
- consistency_group['id'],
- CG_DETAIL_REQUIRED_KEYS,
- actual_keys))
-
- # Verify values
- msg = "Expected name: '%s', actual name: '%s'" % (
- self.cg_name, consistency_group["name"])
- self.assertEqual(self.cg_name, str(consistency_group["name"]), msg)
-
- msg = "Expected description: '%s', actual description: '%s'" % (
- self.cg_desc, consistency_group["description"])
- self.assertEqual(self.cg_desc, str(consistency_group["description"]),
- msg)
-
- @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
- def test_get_cgsnapshot_members_v2_4(self):
-
- cgsnapshot_members = self.shares_v2_client.list_cgsnapshot_members(
- self.cgsnapshot['id'], version='2.4')
- member_share_ids = [member['share_id'] for member in
- cgsnapshot_members]
- self.assertEqual(2, len(cgsnapshot_members),
- 'Unexpected number of cgsnapshot members. Expected '
- '2, got %s.' % len(cgsnapshot_members))
- # Verify each share is represented in the cgsnapshot appropriately
- for share_id in (self.shares[0]['id'], self.shares[1]['id']):
- self.assertIn(share_id, member_share_ids,
- 'Share missing %s missing from cgsnapshot. Found %s.'
- % (share_id, member_share_ids))
- for share in (self.shares[0], self.shares[1]):
- for member in cgsnapshot_members:
- if share['id'] == member['share_id']:
- self.assertEqual(share['size'], member['size'])
- self.assertEqual(share['share_proto'],
- member['share_protocol'])
- # TODO(akerr): Add back assert when bug 1483886 is fixed
- # self.assertEqual(share['share_type'],
- # member['share_type_id'])
-
- @tc.attr(base.TAG_POSITIVE, base.TAG_BACKEND)
- def test_create_consistency_group_from_populated_cgsnapshot_v2_4(self):
-
- cgsnapshot_members = self.shares_v2_client.list_cgsnapshot_members(
- self.cgsnapshot['id'], version='2.4')
-
- new_consistency_group = self.create_consistency_group(
- cleanup_in_class=False,
- source_cgsnapshot_id=self.cgsnapshot['id'],
- version='2.4'
- )
-
- new_consistency_group = self.shares_v2_client.get_consistency_group(
- new_consistency_group['id'], version='2.4')
-
- # Verify that share_network information matches source CG
- self.assertEqual(self.cg['share_network_id'],
- new_consistency_group['share_network_id'])
-
- new_shares = self.shares_v2_client.list_shares(
- params={'consistency_group_id': new_consistency_group['id']},
- detailed=True,
- version='2.4'
- )
-
- # Verify each new share is available
- for share in new_shares:
- self.assertEqual('available', share['status'],
- 'Share %s is not in available status.'
- % share['id'])
-
- # Verify each cgsnapshot member is represented in the new cg
- # appropriately
- share_source_member_ids = [share['source_cgsnapshot_member_id'] for
- share in new_shares]
- for member in cgsnapshot_members:
- self.assertIn(member['id'], share_source_member_ids,
- 'cgsnapshot member %s not represented by '
- 'consistency group %s.' % (
- member['id'], new_consistency_group['id']))
- for share in new_shares:
- if share['source_cgsnapshot_member_id'] == member['id']:
- self.assertEqual(member['size'], share['size'])
- self.assertEqual(member['share_protocol'],
- share['share_proto'])
- # TODO(akerr): Add back assert when bug 1483886 is fixed
- # self.assertEqual(member['share_type_id'],
- # share['share_type'])
- self.assertEqual(self.cg['share_network_id'],
- share['share_network_id'])
-
-
-@testtools.skipUnless(CONF.share.run_consistency_group_tests,
- 'Consistency Group tests disabled.')
-class ConsistencyGroupRenameTest(base.BaseSharesTest):
-
- @classmethod
- def resource_setup(cls):
- super(ConsistencyGroupRenameTest, cls).resource_setup()
-
- # Create consistency group
- cls.cg_name = data_utils.rand_name("tempest-cg-name")
- cls.cg_desc = data_utils.rand_name("tempest-cg-description")
- cls.consistency_group = cls.create_consistency_group(
- name=cls.cg_name,
- description=cls.cg_desc,
- )
-
- @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
- def test_update_consistency_group_v2_4(self):
-
- # Get consistency_group
- consistency_group = self.shares_v2_client.get_consistency_group(
- self.consistency_group['id'], version='2.4')
- self.assertEqual(self.cg_name, consistency_group["name"])
- self.assertEqual(self.cg_desc, consistency_group["description"])
-
- # Update consistency_group
- new_name = data_utils.rand_name("tempest-new-name")
- new_desc = data_utils.rand_name("tempest-new-description")
- updated = self.shares_v2_client.update_consistency_group(
- consistency_group["id"],
- name=new_name,
- description=new_desc,
- version='2.4'
- )
- self.assertEqual(new_name, updated["name"])
- self.assertEqual(new_desc, updated["description"])
-
- # Get consistency_group
- consistency_group = self.shares_v2_client.get_consistency_group(
- self.consistency_group['id'], version='2.4')
- self.assertEqual(new_name, consistency_group["name"])
- self.assertEqual(new_desc, consistency_group["description"])
-
- @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
- def test_create_update_read_consistency_group_with_unicode_v2_4(self):
- value1 = u'ಠ_ಠ'
- value2 = u'ಠ_ರೃ'
- # Create consistency_group
- consistency_group = self.create_consistency_group(
- cleanup_in_class=False,
- name=value1,
- description=value1,
- version='2.4'
- )
- self.assertEqual(value1, consistency_group["name"])
- self.assertEqual(value1, consistency_group["description"])
-
- # Update consistency_group
- updated = self.shares_v2_client.update_consistency_group(
- consistency_group["id"],
- name=value2,
- description=value2,
- version='2.4'
- )
- self.assertEqual(value2, updated["name"])
- self.assertEqual(value2, updated["description"])
-
- # Get consistency_group
- consistency_group = self.shares_v2_client.get_consistency_group(
- consistency_group['id'], version='2.4')
- self.assertEqual(value2, consistency_group["name"])
- self.assertEqual(value2, consistency_group["description"])
diff --git a/manila_tempest_tests/tests/api/test_consistency_groups.py b/manila_tempest_tests/tests/api/test_consistency_groups.py
deleted file mode 100644
index 8f141da..0000000
--- a/manila_tempest_tests/tests/api/test_consistency_groups.py
+++ /dev/null
@@ -1,144 +0,0 @@
-# Copyright 2015 Andrew Kerr
-# All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from tempest import config
-from tempest.lib import exceptions as lib_exc
-import testtools
-from testtools import testcase as tc
-
-from manila_tempest_tests.tests.api import base
-
-CONF = config.CONF
-CG_REQUIRED_ELEMENTS = {"id", "name", "description", "created_at", "status",
- "share_types", "project_id", "host", "links"}
-CGSNAPSHOT_REQUIRED_ELEMENTS = {"id", "name", "description", "created_at",
- "status", "project_id", "links"}
-
-
-@testtools.skipUnless(CONF.share.run_consistency_group_tests,
- 'Consistency Group tests disabled.')
-class ConsistencyGroupsTest(base.BaseSharesTest):
- """Covers consistency group functionality."""
-
- @tc.attr(base.TAG_POSITIVE, base.TAG_BACKEND)
- def test_create_populate_delete_consistency_group_v2_4(self):
- # Create a consistency group
- consistency_group = self.create_consistency_group(
- cleanup_in_class=False, version='2.4')
- self.assertTrue(CG_REQUIRED_ELEMENTS.issubset(
- consistency_group.keys()),
- 'At least one expected element missing from consistency group '
- 'response. Expected %(expected)s, got %(actual)s.' % {
- "expected": CG_REQUIRED_ELEMENTS,
- "actual": consistency_group.keys()})
- # Populate
- share = self.create_share(consistency_group_id=consistency_group['id'],
- cleanup_in_class=False,
- version='2.4')
- # Delete
- params = {"consistency_group_id": consistency_group['id']}
- self.shares_v2_client.delete_share(share['id'], params=params,
- version='2.4')
- self.shares_client.wait_for_resource_deletion(share_id=share['id'])
- self.shares_v2_client.delete_consistency_group(consistency_group['id'],
- version='2.4')
- self.shares_v2_client.wait_for_resource_deletion(
- cg_id=consistency_group['id'])
-
- # Verify
- self.assertRaises(lib_exc.NotFound,
- self.shares_v2_client.get_consistency_group,
- consistency_group['id'])
- self.assertRaises(lib_exc.NotFound,
- self.shares_client.get_share,
- share['id'])
-
- @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
- def test_create_delete_empty_cgsnapshot_v2_4(self):
- # Create base consistency group
- consistency_group = self.create_consistency_group(
- cleanup_in_class=False, version='2.4')
- # Create cgsnapshot
- cgsnapshot = self.create_cgsnapshot_wait_for_active(
- consistency_group["id"], cleanup_in_class=False, version='2.4')
-
- self.assertTrue(CGSNAPSHOT_REQUIRED_ELEMENTS.issubset(
- cgsnapshot.keys()),
- 'At least one expected element missing from cgsnapshot response. '
- 'Expected %(expected)s, got %(actual)s.' % {
- "expected": CGSNAPSHOT_REQUIRED_ELEMENTS,
- "actual": cgsnapshot.keys()})
-
- cgsnapshot_members = self.shares_v2_client.list_cgsnapshot_members(
- cgsnapshot['id'], version='2.4')
-
- self.assertEmpty(cgsnapshot_members,
- 'Expected 0 cgsnapshot members, got %s' % len(
- cgsnapshot_members))
-
- # delete snapshot
- self.shares_v2_client.delete_cgsnapshot(cgsnapshot["id"],
- version='2.4')
- self.shares_v2_client.wait_for_resource_deletion(
- cgsnapshot_id=cgsnapshot["id"])
- self.assertRaises(lib_exc.NotFound,
- self.shares_v2_client.get_cgsnapshot,
- cgsnapshot['id'],
- version='2.4')
-
- @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
- def test_create_consistency_group_from_empty_cgsnapshot(self):
- # Create base consistency group
- consistency_group = self.create_consistency_group(
- cleanup_in_class=False)
-
- # Create cgsnapshot
- cgsnapshot = self.create_cgsnapshot_wait_for_active(
- consistency_group["id"], cleanup_in_class=False)
-
- cgsnapshot_members = self.shares_v2_client.list_cgsnapshot_members(
- cgsnapshot['id'])
-
- self.assertEmpty(cgsnapshot_members,
- 'Expected 0 cgsnapshot members, got %s' % len(
- cgsnapshot_members))
-
- new_consistency_group = self.create_consistency_group(
- cleanup_in_class=False, source_cgsnapshot_id=cgsnapshot['id'])
-
- new_shares = self.shares_client.list_shares(
- params={'consistency_group_id': new_consistency_group['id']})
-
- self.assertEmpty(new_shares,
- 'Expected 0 new shares, got %s' % len(new_shares))
-
- msg = 'Expected cgsnapshot_id %s as source of consistency group %s' % (
- cgsnapshot['id'], new_consistency_group['source_cgsnapshot_id'])
- self.assertEqual(new_consistency_group['source_cgsnapshot_id'],
- cgsnapshot['id'], msg)
-
- msg = ('Unexpected share_types on new consistency group. Expected '
- '%s, got %s.' % (consistency_group['share_types'],
- new_consistency_group['share_types']))
- self.assertEqual(sorted(consistency_group['share_types']),
- sorted(new_consistency_group['share_types']), msg)
-
- # Assert the share_network information is the same
- msg = 'Expected share_network %s as share_network of cg %s' % (
- consistency_group['share_network_id'],
- new_consistency_group['share_network_id'])
- self.assertEqual(consistency_group['share_network_id'],
- new_consistency_group['share_network_id'],
- msg)
diff --git a/manila_tempest_tests/tests/api/test_consistency_groups_negative.py b/manila_tempest_tests/tests/api/test_consistency_groups_negative.py
deleted file mode 100644
index 9c38a2b..0000000
--- a/manila_tempest_tests/tests/api/test_consistency_groups_negative.py
+++ /dev/null
@@ -1,226 +0,0 @@
-# Copyright 2015 Andrew Kerr
-# All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from tempest import config
-from tempest.lib.common.utils import data_utils
-from tempest.lib import exceptions as lib_exc
-import testtools
-from testtools import testcase as tc
-
-from manila_tempest_tests.tests.api import base
-
-CONF = config.CONF
-
-
-@testtools.skipUnless(CONF.share.run_consistency_group_tests,
- 'Consistency Group tests disabled.')
-class ConsistencyGroupsAPIOnlyNegativeTest(base.BaseSharesTest):
-
- @tc.attr(base.TAG_NEGATIVE, base.TAG_API)
- def test_delete_cg_without_passing_cg_id_v2_4(self):
- self.assertRaises(lib_exc.NotFound,
- self.shares_v2_client.delete_consistency_group,
- '',
- version='2.4')
-
- @tc.attr(base.TAG_NEGATIVE, base.TAG_API)
- def test_delete_cg_with_wrong_id_v2_4(self):
- self.assertRaises(lib_exc.NotFound,
- self.shares_v2_client.delete_consistency_group,
- "wrong_consistency_group_id",
- version='2.4')
-
- @tc.attr(base.TAG_NEGATIVE, base.TAG_API)
- def test_update_cg_with_wrong_id_v2_4(self):
- self.assertRaises(lib_exc.NotFound,
- self.shares_v2_client.update_consistency_group,
- 'wrong_consistency_group_id',
- name='new_name',
- description='new_description',
- version='2.4')
-
- @tc.attr(base.TAG_NEGATIVE, base.TAG_API)
- def test_get_cg_without_passing_cg_id_v2_4(self):
- self.assertRaises(lib_exc.NotFound,
- self.shares_v2_client.get_consistency_group,
- '',
- version='2.4')
-
- @tc.attr(base.TAG_NEGATIVE, base.TAG_API)
- def test_filter_shares_on_invalid_cg_id_v2_4(self):
- shares = self.shares_v2_client.list_shares(
- detailed=True,
- params={'consistency_group_id': 'foobar'},
- version='2.4',
- )
-
- self.assertEqual(0, len(shares),
- 'Incorrect number of shares returned. Expected 0, '
- 'got %s.' % len(shares))
-
- @tc.attr(base.TAG_NEGATIVE, base.TAG_API)
- def test_create_cgsnapshot_with_invalid_cg_id_value_v2_4(self):
- self.assertRaises(lib_exc.BadRequest,
- self.create_cgsnapshot_wait_for_active,
- 'foobar',
- cleanup_in_class=False,
- version='2.4')
-
- @tc.attr(base.TAG_NEGATIVE, base.TAG_API)
- def test_create_cg_with_invalid_share_type_id_value_v2_4(self):
- self.assertRaises(lib_exc.BadRequest,
- self.create_consistency_group,
- share_type_ids=['foobar'],
- cleanup_in_class=False,
- version='2.4')
-
- @tc.attr(base.TAG_NEGATIVE, base.TAG_API)
- def test_create_cg_with_invalid_share_network_id_value_v2_4(self):
- self.assertRaises(lib_exc.BadRequest,
- self.create_consistency_group,
- share_network_id='foobar',
- cleanup_in_class=False,
- version='2.4')
-
- @tc.attr(base.TAG_NEGATIVE, base.TAG_API)
- def test_create_cg_with_invalid_source_cgsnapshot_id_value_v2_4(
- self):
- self.assertRaises(lib_exc.BadRequest,
- self.create_consistency_group,
- source_cgsnapshot_id='foobar',
- cleanup_in_class=False,
- version='2.4')
-
-
-@testtools.skipUnless(CONF.share.run_consistency_group_tests,
- 'Consistency Group tests disabled.')
-class ConsistencyGroupsNegativeTest(base.BaseSharesTest):
-
- @classmethod
- def resource_setup(cls):
- super(ConsistencyGroupsNegativeTest, cls).resource_setup()
- # Create a consistency group
- cls.cg_name = data_utils.rand_name("tempest-cg-name")
- cls.cg_desc = data_utils.rand_name("tempest-cg-description")
- cls.consistency_group = cls.create_consistency_group(
- name=cls.cg_name,
- description=cls.cg_desc
- )
- # Create a share in the consistency group
- cls.share_name = data_utils.rand_name("tempest-share-name")
- cls.share_desc = data_utils.rand_name("tempest-share-description")
- cls.share = cls.create_share(
- name=cls.share_name,
- description=cls.share_desc,
- consistency_group_id=cls.consistency_group['id'],
- )
- # Create a cgsnapshot of the consistency group
- cls.cgsnap_name = data_utils.rand_name("tempest-cgsnap-name")
- cls.cgsnap_desc = data_utils.rand_name("tempest-cgsnap-description")
- cls.cgsnapshot = cls.create_cgsnapshot_wait_for_active(
- cls.consistency_group["id"],
- name=cls.cgsnap_name,
- description=cls.cgsnap_desc)
-
- @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
- def test_create_cg_with_nonexistent_source_cgsnapshot_id_value_v2_4(self):
- self.assertRaises(lib_exc.BadRequest,
- self.create_consistency_group,
- source_cgsnapshot_id=self.share['id'],
- cleanup_in_class=False,
- version='2.4')
-
- @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
- def test_create_cg_with_nonexistent_share_network_id_value_v2_4(self):
- self.assertRaises(lib_exc.BadRequest,
- self.create_consistency_group,
- share_network_id=self.share['id'],
- cleanup_in_class=False,
- version='2.4')
-
- @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
- def test_create_cg_with_nonexistent_share_type_id_value_v2_4(self):
- self.assertRaises(lib_exc.BadRequest,
- self.create_consistency_group,
- share_type_ids=[self.share['id']],
- cleanup_in_class=False,
- version='2.4')
-
- @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
- def test_create_cgsnapshot_with_nonexistent_cg_id_value_v2_4(self):
- self.assertRaises(lib_exc.BadRequest,
- self.create_cgsnapshot_wait_for_active,
- self.share['id'],
- cleanup_in_class=False,
- version='2.4')
-
- @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
- def test_delete_cg_in_use_by_cgsnapshot_v2_4(self):
- # Attempt delete of share type
- self.assertRaises(lib_exc.Conflict,
- self.shares_v2_client.delete_consistency_group,
- self.consistency_group['id'],
- version='2.4')
-
- @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
- def test_delete_share_in_use_by_cgsnapshot_v2_4(self):
- # Attempt delete of share type
- params = {'consistency_group_id': self.share['consistency_group_id']}
- self.assertRaises(lib_exc.Forbidden,
- self.shares_v2_client.delete_share,
- self.share['id'],
- params=params,
- version='2.4')
-
- @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
- def test_delete_cg_containing_a_share_v2_4(self):
- self.assertRaises(lib_exc.Conflict,
- self.shares_v2_client.delete_consistency_group,
- self.consistency_group['id'],
- version='2.4')
- # Verify consistency group is not put into error state from conflict
- cg = self.shares_v2_client.get_consistency_group(
- self.consistency_group['id'], version='2.4')
- self.assertEqual('available', cg['status'])
-
- @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
- def test_filter_shares_on_nonexistent_cg_id_v2_4(self):
- shares = self.shares_v2_client.list_shares(
- detailed=True,
- params={'consistency_group_id': self.share['id']},
- version='2.4'
- )
-
- self.assertEqual(0, len(shares),
- 'Incorrect number of shares returned. Expected 0, '
- 'got %s.' % len(shares))
-
- @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
- def test_filter_shares_on_empty_cg_id_v2_4(self):
- consistency_group = self.create_consistency_group(
- name='tempest_cg',
- description='tempest_cg_desc',
- cleanup_in_class=False,
- version='2.4',
- )
- shares = self.shares_v2_client.list_shares(
- detailed=True,
- params={'consistency_group_id': consistency_group['id']},
- version='2.4',
- )
-
- self.assertEqual(0, len(shares),
- 'Incorrect number of shares returned. Expected 0, '
- 'got %s.' % len(shares))
diff --git a/manila_tempest_tests/tests/api/test_share_group_actions.py b/manila_tempest_tests/tests/api/test_share_group_actions.py
new file mode 100644
index 0000000..aa582bf
--- /dev/null
+++ b/manila_tempest_tests/tests/api/test_share_group_actions.py
@@ -0,0 +1,395 @@
+# -*- coding: utf-8 -*-
+# Copyright 2016 Andrew Kerr
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from tempest import config
+from tempest.lib.common.utils import data_utils
+import testtools
+from testtools import testcase as tc
+
+from manila_tempest_tests.common import constants
+from manila_tempest_tests.tests.api import base
+
+CONF = config.CONF
+
+
+@testtools.skipUnless(
+ CONF.share.run_share_group_tests, 'Share Group tests disabled.')
+@base.skip_if_microversion_lt(constants.MIN_SHARE_GROUP_MICROVERSION)
+class ShareGroupActionsTest(base.BaseSharesTest):
+ """Covers share group functionality."""
+
+ @classmethod
+ def resource_setup(cls):
+ super(ShareGroupActionsTest, cls).resource_setup()
+
+ # Create first share group
+ cls.share_group_name = data_utils.rand_name("tempest-sg-name")
+ cls.share_group_desc = data_utils.rand_name("tempest-sg-description")
+ cls.share_group = cls.create_share_group(
+ name=cls.share_group_name, description=cls.share_group_desc)
+
+ # Create second share group for purposes of sorting and snapshot
+ # filtering
+ cls.share_group2 = cls.create_share_group(
+ name=cls.share_group_name, description=cls.share_group_desc)
+
+ # Create 2 shares - inside first and second share groups
+ cls.share_name = data_utils.rand_name("tempest-share-name")
+ cls.share_desc = data_utils.rand_name("tempest-share-description")
+ cls.share_size = 1
+ cls.share_size2 = 2
+ cls.shares = cls.create_shares([
+ {'kwargs': {
+ 'name': cls.share_name,
+ 'description': cls.share_desc,
+ 'size': size,
+ 'share_group_id': sg_id,
+ 'experimental': True,
+ }} for size, sg_id in ((cls.share_size, cls.share_group['id']),
+ (cls.share_size2, cls.share_group['id']),
+ (cls.share_size, cls.share_group2['id']))
+ ])
+
+ # Create share group snapshots
+ cls.sg_snap_name = data_utils.rand_name("tempest-sg-snap-name")
+ cls.sg_snap_desc = data_utils.rand_name("tempest-sg-snap-desc")
+
+ cls.sg_snapshot = cls.create_share_group_snapshot_wait_for_active(
+ cls.share_group["id"],
+ name=cls.sg_snap_name,
+ description=cls.sg_snap_desc,
+ )
+
+ cls.sg_snapshot2 = cls.create_share_group_snapshot_wait_for_active(
+ cls.share_group2['id'],
+ name=cls.sg_snap_name,
+ description=cls.sg_snap_desc,
+ )
+
+ @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
+ def test_get_share_group_min_supported_sg_microversion(self):
+
+ # Get share group
+ share_group = self.shares_v2_client.get_share_group(
+ self.share_group['id'],
+ version=constants.MIN_SHARE_GROUP_MICROVERSION,
+ )
+
+ # Verify keys
+ actual_keys = set(share_group.keys())
+ self.assertTrue(
+ constants.SHARE_GROUP_DETAIL_REQUIRED_KEYS.issubset(actual_keys),
+ 'Not all required keys returned for share group %s. '
+ 'Expected at least: %s, found %s' % (
+ share_group['id'],
+ constants.SHARE_GROUP_DETAIL_REQUIRED_KEYS,
+ actual_keys))
+
+ # Verify values
+ self.assertEqual(self.share_group_name, share_group["name"])
+ self.assertEqual(self.share_group_desc, share_group["description"])
+
+ @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
+ def test_get_share_min_supported_sg_microversion(self):
+
+ # Get share
+ share = self.shares_v2_client.get_share(
+ self.shares[0]['id'],
+ version=constants.MIN_SHARE_GROUP_MICROVERSION,
+ experimental=True)
+
+ # Verify keys
+ expected_keys = {
+ "status", "description", "links", "availability_zone",
+ "created_at", "share_proto", "name", "snapshot_id",
+ "id", "size", "share_group_id",
+ }
+ actual_keys = set(share.keys())
+ self.assertTrue(
+ expected_keys.issubset(actual_keys),
+ 'Not all required keys returned for share %s. '
+ 'Expected at least: %s, found %s' % (
+ share['id'], expected_keys, actual_keys))
+
+ # Verify values
+ self.assertEqual(self.share_name, share["name"])
+ self.assertEqual(self.share_desc, share["description"])
+ self.assertEqual(self.share_size, int(share["size"]))
+ self.assertEqual(self.share_group["id"], share["share_group_id"])
+
+ @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
+ def test_list_share_groups_min(self):
+
+ # List share groups
+ share_groups = self.shares_v2_client.list_share_groups(
+ version=constants.MIN_SHARE_GROUP_MICROVERSION)
+
+ # Verify keys
+ self.assertGreater(len(share_groups), 0)
+ for sg in share_groups:
+ keys = set(sg.keys())
+ self.assertEqual(
+ constants.SHARE_GROUP_SIMPLE_KEYS,
+ keys,
+ 'Incorrect keys returned for share group %s. '
+ 'Expected: %s, found %s' % (
+ sg['id'],
+ constants.SHARE_GROUP_SIMPLE_KEYS,
+ ','.join(keys)))
+
+ # Share group ids are in list exactly once
+ for sg_id in (self.share_group["id"], self.share_group2["id"]):
+ gen = [sg["id"] for sg in share_groups if sg["id"] == sg_id]
+ msg = ("Expected id %s exactly once in share group list" % sg_id)
+ self.assertEqual(1, len(gen), msg)
+
+ @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
+ def test_list_share_groups_with_detail_min(self):
+
+ # List share groups
+ share_groups = self.shares_v2_client.list_share_groups(
+ detailed=True, version=constants.MIN_SHARE_GROUP_MICROVERSION)
+
+ # Verify keys
+ for sg in share_groups:
+ keys = set(sg.keys())
+ self.assertTrue(
+ constants.SHARE_GROUP_DETAIL_REQUIRED_KEYS.issubset(
+ keys),
+ 'Not all required keys returned for share group %s. '
+ 'Expected at least: %s, found %s' % (
+ sg['id'],
+ constants.SHARE_GROUP_DETAIL_REQUIRED_KEYS,
+ ','.join(keys),
+ )
+ )
+
+ # Share group ids are in list exactly once
+ for group_id in (self.share_group["id"], self.share_group2["id"]):
+ gen = [share_group["id"] for share_group in share_groups
+ if share_group["id"] == group_id]
+ msg = ("Expected id %s exactly once in share group list" %
+ group_id)
+ self.assertEqual(1, len(gen), msg)
+
+ @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
+ def test_filter_shares_by_share_group_id_min(self):
+ shares = self.shares_v2_client.list_shares(
+ detailed=True,
+ params={'share_group_id': self.share_group['id']},
+ version=constants.MIN_SHARE_GROUP_MICROVERSION,
+ experimental=True,
+ )
+
+ share_ids = [share['id'] for share in shares]
+
+ self.assertEqual(
+ 2, len(shares),
+ 'Incorrect number of shares returned. '
+ 'Expected 2, got %s' % len(shares))
+ self.assertIn(
+ self.shares[0]['id'], share_ids,
+ 'Share %s expected in returned list, but got %s' % (
+ self.shares[0]['id'], share_ids))
+ self.assertIn(
+ self.shares[1]['id'], share_ids,
+ 'Share %s expected in returned list, but got %s' % (
+ self.shares[0]['id'], share_ids))
+
+ @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
+ def test_get_share_group_snapshot_min(self):
+ # Get share group snapshot
+ sg_snapshot = self.shares_v2_client.get_share_group_snapshot(
+ self.sg_snapshot['id'],
+ version=constants.MIN_SHARE_GROUP_MICROVERSION,
+ )
+
+ # Verify keys
+ actual_keys = set(sg_snapshot.keys())
+ self.assertTrue(
+ constants.SHARE_GROUP_SNAPSHOT_DETAIL_REQUIRED_KEYS.issubset(
+ actual_keys),
+ 'Not all required keys returned for share group %s. '
+ 'Expected at least: %s, found %s' % (
+ sg_snapshot['id'],
+ constants.SHARE_GROUP_DETAIL_REQUIRED_KEYS,
+ actual_keys,
+ )
+ )
+
+ # Verify values
+ self.assertEqual(self.sg_snap_name, sg_snapshot["name"])
+ self.assertEqual(self.sg_snap_desc, sg_snapshot["description"])
+
+ @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
+ def test_get_share_group_snapshot_members_min(self):
+ sg_snapshot = self.shares_v2_client.get_share_group_snapshot(
+ self.sg_snapshot['id'],
+ version=constants.MIN_SHARE_GROUP_MICROVERSION,
+ )
+ sg_snapshot_members = sg_snapshot['members']
+ member_share_ids = [m['share_id'] for m in sg_snapshot_members]
+ self.assertEqual(
+ 2, len(sg_snapshot_members),
+ 'Unexpected number of share group snapshot members. '
+ 'Expected 2, got %s.' % len(sg_snapshot_members))
+ # Verify each share is represented in the share group snapshot
+ # appropriately
+ for share_id in (self.shares[0]['id'], self.shares[1]['id']):
+ self.assertIn(
+ share_id, member_share_ids,
+ 'Share missing %s missing from share group '
+ 'snapshot. Found %s.' % (share_id, member_share_ids))
+ for share in (self.shares[0], self.shares[1]):
+ for member in sg_snapshot_members:
+ if share['id'] == member['share_id']:
+ self.assertEqual(share['size'], member['size'])
+
+ @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
+ def test_create_share_group_from_populated_share_group_snapshot_min(self):
+
+ sg_snapshot = self.shares_v2_client.get_share_group_snapshot(
+ self.sg_snapshot['id'],
+ version=constants.MIN_SHARE_GROUP_MICROVERSION,
+ )
+ snapshot_members = sg_snapshot['members']
+
+ new_share_group = self.create_share_group(
+ cleanup_in_class=False,
+ source_share_group_snapshot_id=self.sg_snapshot['id'],
+ version=constants.MIN_SHARE_GROUP_MICROVERSION,
+ )
+
+ new_share_group = self.shares_v2_client.get_share_group(
+ new_share_group['id'],
+ version=constants.MIN_SHARE_GROUP_MICROVERSION,
+ )
+
+ # Verify that share_network information matches source share group
+ self.assertEqual(
+ self.share_group['share_network_id'],
+ new_share_group['share_network_id'])
+
+ new_shares = self.shares_v2_client.list_shares(
+ params={'share_group_id': new_share_group['id']},
+ detailed=True,
+ version=constants.MIN_SHARE_GROUP_MICROVERSION,
+ experimental=True,
+ )
+
+ # Verify each new share is available
+ for share in new_shares:
+ self.assertEqual(
+ 'available', share['status'],
+ 'Share %s is not in available status.' % share['id'])
+
+ # Verify each sgsnapshot member is represented in the new sg
+ # appropriately
+ share_source_member_ids = [
+ share['source_share_group_snapshot_member_id']
+ for share in new_shares]
+ for member in snapshot_members:
+ self.assertIn(
+ member['id'], share_source_member_ids,
+ 'Share group snapshot member %s not represented by '
+ 'share group %s.' % (member['id'], new_share_group['id']))
+ for share in new_shares:
+ if (share['source_share_group_snapshot_member_id'] == (
+ member['id'])):
+ self.assertEqual(member['size'], share['size'])
+ self.assertEqual(
+ self.share_group['share_network_id'],
+ share['share_network_id'])
+
+
+@testtools.skipUnless(
+ CONF.share.run_share_group_tests, 'Share Group tests disabled.')
+@base.skip_if_microversion_lt(constants.MIN_SHARE_GROUP_MICROVERSION)
+class ShareGroupRenameTest(base.BaseSharesTest):
+
+ @classmethod
+ def resource_setup(cls):
+ super(ShareGroupRenameTest, cls).resource_setup()
+
+ # Create share group
+ cls.share_group_name = data_utils.rand_name("tempest-sg-name")
+ cls.share_group_desc = data_utils.rand_name("tempest-sg-description")
+ cls.share_group = cls.create_share_group(
+ name=cls.share_group_name,
+ description=cls.share_group_desc,
+ )
+
+ @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
+ def test_update_share_group_min(self):
+
+ # Get share_group
+ share_group = self.shares_v2_client.get_share_group(
+ self.share_group['id'],
+ version=constants.MIN_SHARE_GROUP_MICROVERSION
+ )
+ self.assertEqual(self.share_group_name, share_group["name"])
+ self.assertEqual(self.share_group_desc, share_group["description"])
+
+ # Update share_group
+ new_name = data_utils.rand_name("tempest-new-name")
+ new_desc = data_utils.rand_name("tempest-new-description")
+ updated = self.shares_v2_client.update_share_group(
+ share_group["id"],
+ name=new_name,
+ description=new_desc,
+ version=constants.MIN_SHARE_GROUP_MICROVERSION,
+ )
+ self.assertEqual(new_name, updated["name"])
+ self.assertEqual(new_desc, updated["description"])
+
+ # Get share_group
+ share_group = self.shares_v2_client.get_share_group(
+ self.share_group['id'],
+ version=constants.MIN_SHARE_GROUP_MICROVERSION,
+ )
+ self.assertEqual(new_name, share_group["name"])
+ self.assertEqual(new_desc, share_group["description"])
+
+ @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
+ def test_create_update_read_share_group_with_unicode_min(self):
+ value1 = u'ಠ_ಠ'
+ value2 = u'ಠ_ರೃ'
+
+ # Create share_group
+ share_group = self.create_share_group(
+ cleanup_in_class=False,
+ name=value1,
+ description=value1,
+ version=constants.MIN_SHARE_GROUP_MICROVERSION,
+ )
+ self.assertEqual(value1, share_group["name"])
+ self.assertEqual(value1, share_group["description"])
+
+ # Update share group
+ updated = self.shares_v2_client.update_share_group(
+ share_group["id"],
+ name=value2,
+ description=value2,
+ version=constants.MIN_SHARE_GROUP_MICROVERSION,
+ )
+ self.assertEqual(value2, updated["name"])
+ self.assertEqual(value2, updated["description"])
+
+ # Get share group
+ share_group = self.shares_v2_client.get_share_group(
+ share_group['id'], version=constants.MIN_SHARE_GROUP_MICROVERSION)
+ self.assertEqual(value2, share_group["name"])
+ self.assertEqual(value2, share_group["description"])
diff --git a/manila_tempest_tests/tests/api/test_share_groups.py b/manila_tempest_tests/tests/api/test_share_groups.py
new file mode 100644
index 0000000..98d29e4
--- /dev/null
+++ b/manila_tempest_tests/tests/api/test_share_groups.py
@@ -0,0 +1,165 @@
+# Copyright 2016 Andrew Kerr
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from tempest import config
+from tempest.lib import exceptions as lib_exc
+import testtools
+from testtools import testcase as tc
+
+from manila_tempest_tests.common import constants
+from manila_tempest_tests.tests.api import base
+
+CONF = config.CONF
+
+
+@testtools.skipUnless(
+ CONF.share.run_share_group_tests, 'Share Group tests disabled.')
+@base.skip_if_microversion_lt(constants.MIN_SHARE_GROUP_MICROVERSION)
+class ShareGroupsTest(base.BaseSharesTest):
+ """Covers share group functionality."""
+
+ @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
+ def test_create_populate_delete_share_group_min(self):
+ # Create a share group
+ share_group = self.create_share_group(
+ cleanup_in_class=False,
+ version=constants.MIN_SHARE_GROUP_MICROVERSION)
+ keys = set(share_group.keys())
+ self.assertTrue(
+ constants.SHARE_GROUP_DETAIL_REQUIRED_KEYS.issubset(keys),
+ 'At least one expected element missing from share group '
+ 'response. Expected %(expected)s, got %(actual)s.' % {
+ "expected": constants.SHARE_GROUP_DETAIL_REQUIRED_KEYS,
+ "actual": keys}
+ )
+ # Populate
+ share = self.create_share(
+ share_group_id=share_group['id'],
+ cleanup_in_class=False,
+ version=constants.MIN_SHARE_GROUP_MICROVERSION,
+ experimental=True)
+
+ # Delete
+ params = {"share_group_id": share_group['id']}
+ self.shares_v2_client.delete_share(
+ share['id'],
+ params=params,
+ version=constants.MIN_SHARE_GROUP_MICROVERSION)
+ self.shares_client.wait_for_resource_deletion(share_id=share['id'])
+ self.shares_v2_client.delete_share_group(
+ share_group['id'], version=constants.MIN_SHARE_GROUP_MICROVERSION)
+ self.shares_v2_client.wait_for_resource_deletion(
+ share_group_id=share_group['id'])
+
+ # Verify
+ self.assertRaises(
+ lib_exc.NotFound,
+ self.shares_v2_client.get_share_group, share_group['id'])
+ self.assertRaises(
+ lib_exc.NotFound, self.shares_client.get_share, share['id'])
+
+ @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
+ def test_create_delete_empty_share_group_snapshot_min(self):
+ # Create base share group
+ share_group = self.create_share_group(
+ cleanup_in_class=False,
+ version=constants.MIN_SHARE_GROUP_MICROVERSION)
+
+ # Create share group snapshot
+ sg_snapshot = self.create_share_group_snapshot_wait_for_active(
+ share_group["id"],
+ cleanup_in_class=False,
+ version=constants.MIN_SHARE_GROUP_MICROVERSION)
+
+ keys = set(sg_snapshot.keys())
+ self.assertTrue(
+ constants.SHARE_GROUP_SNAPSHOT_DETAIL_REQUIRED_KEYS.issubset(keys),
+ 'At least one expected element missing from share group snapshot '
+ 'response. Expected %(e)s, got %(a)s.' % {
+ "e": constants.SHARE_GROUP_SNAPSHOT_DETAIL_REQUIRED_KEYS,
+ "a": keys})
+
+ sg_snapshot_members = sg_snapshot['members']
+ self.assertEmpty(
+ sg_snapshot_members,
+ 'Expected 0 share_group_snapshot members, got %s' % len(
+ sg_snapshot_members))
+
+ # Delete snapshot
+ self.shares_v2_client.delete_share_group_snapshot(
+ sg_snapshot["id"], version=constants.MIN_SHARE_GROUP_MICROVERSION)
+ self.shares_v2_client.wait_for_resource_deletion(
+ share_group_snapshot_id=sg_snapshot["id"])
+ self.assertRaises(
+ lib_exc.NotFound,
+ self.shares_v2_client.get_share_group_snapshot,
+ sg_snapshot['id'],
+ version=constants.MIN_SHARE_GROUP_MICROVERSION)
+
+ @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
+ def test_create_share_group_from_empty_share_group_snapshot_min(self):
+ # Create base share group
+ share_group = self.create_share_group(
+ cleanup_in_class=False,
+ version=constants.MIN_SHARE_GROUP_MICROVERSION)
+
+ # Create share group snapshot
+ sg_snapshot = self.create_share_group_snapshot_wait_for_active(
+ share_group["id"], cleanup_in_class=False,
+ version=constants.MIN_SHARE_GROUP_MICROVERSION)
+
+ snapshot_members = sg_snapshot['members']
+
+ self.assertEmpty(
+ snapshot_members,
+ 'Expected 0 share group snapshot members, got %s' %
+ len(snapshot_members))
+
+ new_share_group = self.create_share_group(
+ cleanup_in_class=False,
+ source_share_group_snapshot_id=sg_snapshot['id'],
+ version=constants.MIN_SHARE_GROUP_MICROVERSION)
+
+ new_shares = self.shares_v2_client.list_shares(
+ params={'share_group_id': new_share_group['id']},
+ version=constants.MIN_SHARE_GROUP_MICROVERSION, experimental=True)
+
+ self.assertEmpty(
+ new_shares, 'Expected 0 new shares, got %s' % len(new_shares))
+
+ msg = ('Expected source_ishare_group_snapshot_id %s '
+ 'as source of share group %s' % (
+ sg_snapshot['id'],
+ new_share_group['source_share_group_snapshot_id']))
+ self.assertEqual(
+ new_share_group['source_share_group_snapshot_id'],
+ sg_snapshot['id'],
+ msg)
+
+ msg = ('Unexpected share_types on new share group. Expected '
+ '%s, got %s.' % (share_group['share_types'],
+ new_share_group['share_types']))
+ self.assertEqual(
+ sorted(share_group['share_types']),
+ sorted(new_share_group['share_types']), msg)
+
+ # Assert the share_network information is the same
+ msg = 'Expected share_network %s as share_network of cg %s' % (
+ share_group['share_network_id'],
+ new_share_group['share_network_id'])
+ self.assertEqual(
+ share_group['share_network_id'],
+ new_share_group['share_network_id'],
+ msg)
diff --git a/manila_tempest_tests/tests/api/test_share_groups_negative.py b/manila_tempest_tests/tests/api/test_share_groups_negative.py
new file mode 100644
index 0000000..b33eca9
--- /dev/null
+++ b/manila_tempest_tests/tests/api/test_share_groups_negative.py
@@ -0,0 +1,239 @@
+# Copyright 2016 Andrew Kerr
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from tempest import config
+from tempest.lib.common.utils import data_utils
+from tempest.lib import exceptions as lib_exc
+import testtools
+from testtools import testcase as tc
+
+from manila_tempest_tests.common import constants
+from manila_tempest_tests.tests.api import base
+
+CONF = config.CONF
+
+
+@testtools.skipUnless(
+ CONF.share.run_share_group_tests, 'Share Group tests disabled.')
+@base.skip_if_microversion_lt(constants.MIN_SHARE_GROUP_MICROVERSION)
+class ShareGroupsNegativeTest(base.BaseSharesTest):
+
+ @classmethod
+ def resource_setup(cls):
+ super(ShareGroupsNegativeTest, cls).resource_setup()
+ # Create a share group
+ cls.share_group_name = data_utils.rand_name("tempest-sg-name")
+ cls.share_group_desc = data_utils.rand_name("tempest-sg-description")
+ cls.share_group = cls.create_share_group(
+ name=cls.share_group_name,
+ description=cls.share_group_desc
+ )
+ # Create a share in the share group
+ cls.share_name = data_utils.rand_name("tempest-share-name")
+ cls.share_desc = data_utils.rand_name("tempest-share-description")
+ cls.share_size = 1
+ cls.share = cls.create_share(
+ name=cls.share_name,
+ description=cls.share_desc,
+ size=cls.share_size,
+ share_group_id=cls.share_group['id'],
+ experimental=True,
+ )
+ # Create a share group snapshot of the share group
+ cls.sg_snap_name = data_utils.rand_name("tempest-sg-snap-name")
+ cls.sg_snap_desc = data_utils.rand_name(
+ "tempest-group-snap-description")
+ cls.sg_snapshot = cls.create_share_group_snapshot_wait_for_active(
+ cls.share_group["id"],
+ name=cls.sg_snap_name,
+ description=cls.sg_snap_desc
+ )
+
+ @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
+ def test_create_sg_with_invalid_source_sg_snapshot_id_value_min(self):
+ self.assertRaises(
+ lib_exc.BadRequest,
+ self.create_share_group,
+ source_share_group_snapshot_id='foobar',
+ cleanup_in_class=False,
+ version=constants.MIN_SHARE_GROUP_MICROVERSION)
+
+ @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
+ def test_create_sg_with_nonexistent_source_sg_snapshot_id_value_min(self):
+ self.assertRaises(
+ lib_exc.BadRequest,
+ self.create_share_group,
+ source_share_group_snapshot_id=self.share['id'],
+ cleanup_in_class=False,
+ version=constants.MIN_SHARE_GROUP_MICROVERSION)
+
+ @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
+ def test_create_sg_with_invalid_share_network_id_value_min(self):
+ self.assertRaises(
+ lib_exc.BadRequest,
+ self.create_share_group,
+ share_network_id='foobar',
+ cleanup_in_class=False,
+ version=constants.MIN_SHARE_GROUP_MICROVERSION)
+
+ @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
+ def test_create_group_with_nonexistent_share_network_id_value_min(self):
+ self.assertRaises(
+ lib_exc.BadRequest,
+ self.create_share_group,
+ share_network_id=self.share['id'],
+ cleanup_in_class=False,
+ version=constants.MIN_SHARE_GROUP_MICROVERSION)
+
+ @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
+ def test_create_sg_with_invalid_share_type_id_value_min(self):
+ self.assertRaises(
+ lib_exc.BadRequest,
+ self.create_share_group,
+ share_type_ids=['foobar'],
+ cleanup_in_class=False,
+ version=constants.MIN_SHARE_GROUP_MICROVERSION)
+
+ @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
+ def test_create_sg_with_nonexistent_share_type_id_value_min(self):
+ self.assertRaises(
+ lib_exc.BadRequest,
+ self.create_share_group,
+ share_type_ids=[self.share['id']],
+ cleanup_in_class=False,
+ version=constants.MIN_SHARE_GROUP_MICROVERSION)
+
+ @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
+ def test_create_sg_snapshot_with_invalid_sg_id_value_min(self):
+ self.assertRaises(
+ lib_exc.BadRequest,
+ self.create_share_group_snapshot_wait_for_active,
+ 'foobar',
+ cleanup_in_class=False,
+ version=constants.MIN_SHARE_GROUP_MICROVERSION)
+
+ @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
+ def test_create_sg_snapshot_with_nonexistent_sg_id_value_min(self):
+ self.assertRaises(
+ lib_exc.BadRequest,
+ self.create_share_group_snapshot_wait_for_active,
+ self.share['id'],
+ cleanup_in_class=False,
+ version=constants.MIN_SHARE_GROUP_MICROVERSION)
+
+ @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
+ def test_get_sg_with_invalid_id_min(self):
+ self.assertRaises(
+ lib_exc.NotFound,
+ self.shares_v2_client.get_share_group,
+ "invalid_share_group_id",
+ version=constants.MIN_SHARE_GROUP_MICROVERSION)
+
+ @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
+ def test_get_sg_without_passing_group_id_min(self):
+ self.assertRaises(
+ lib_exc.NotFound,
+ self.shares_v2_client.get_share_group,
+ '', version=constants.MIN_SHARE_GROUP_MICROVERSION)
+
+ @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
+ def test_update_sg_with_invalid_id_min(self):
+ self.assertRaises(
+ lib_exc.NotFound,
+ self.shares_v2_client.update_share_group,
+ 'invalid_share_group_id',
+ name='new_name',
+ description='new_description',
+ version=constants.MIN_SHARE_GROUP_MICROVERSION)
+
+ @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
+ def test_delete_sg_with_invalid_id_min(self):
+ self.assertRaises(
+ lib_exc.NotFound,
+ self.shares_v2_client.delete_share_group,
+ "invalid_share_group_id",
+ version=constants.MIN_SHARE_GROUP_MICROVERSION)
+
+ @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
+ def test_delete_sg_without_passing_sg_id_min(self):
+ self.assertRaises(
+ lib_exc.NotFound,
+ self.shares_v2_client.delete_share_group,
+ '', version=constants.MIN_SHARE_GROUP_MICROVERSION)
+
+ @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
+ def test_delete_sg_in_use_by_sg_snapshot_min(self):
+ self.assertRaises(
+ lib_exc.Conflict,
+ self.shares_v2_client.delete_share_group,
+ self.share_group['id'],
+ version=constants.MIN_SHARE_GROUP_MICROVERSION)
+
+ @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
+ def test_delete_share_in_use_by_sg_snapshot_min(self):
+ params = {'share_group_id': self.share['share_group_id']}
+ self.assertRaises(
+ lib_exc.Forbidden,
+ self.shares_v2_client.delete_share,
+ self.share['id'],
+ params=params,
+ version=constants.MIN_SHARE_GROUP_MICROVERSION)
+
+ @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
+ def test_delete_sg_containing_a_share_min(self):
+ self.assertRaises(
+ lib_exc.Conflict,
+ self.shares_v2_client.delete_share_group,
+ self.share_group['id'],
+ version=constants.MIN_SHARE_GROUP_MICROVERSION)
+
+ # Verify share group is not put into error state from conflict
+ sg = self.shares_v2_client.get_share_group(
+ self.share_group['id'],
+ version=constants.MIN_SHARE_GROUP_MICROVERSION)
+ self.assertEqual('available', sg['status'])
+
+ @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
+ def test_filter_shares_on_invalid_group_id_min(self):
+ shares = self.shares_v2_client.list_shares(
+ detailed=True,
+ params={'share_group_id': 'foobar'},
+ version=constants.MIN_SHARE_GROUP_MICROVERSION,
+ )
+ self.assertEqual(0, len(shares), 'Incorrect number of shares returned')
+
+ @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
+ def test_filter_shares_on_nonexistent_group_id_min(self):
+ shares = self.shares_v2_client.list_shares(
+ detailed=True,
+ params={'share_group_id': self.share['id']},
+ version=constants.MIN_SHARE_GROUP_MICROVERSION,
+ )
+ self.assertEqual(0, len(shares), 'Incorrect number of shares returned')
+
+ @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
+ def test_filter_shares_on_empty_share_group_id_min(self):
+ share_group = self.create_share_group(
+ name='tempest_sg',
+ description='tempest_sg_desc',
+ cleanup_in_class=False,
+ version=constants.MIN_SHARE_GROUP_MICROVERSION,
+ )
+ shares = self.shares_v2_client.list_shares(
+ detailed=True,
+ params={'share_group_id': share_group['id']},
+ version=constants.MIN_SHARE_GROUP_MICROVERSION,
+ )
+ self.assertEqual(0, len(shares), 'Incorrect number of shares returned')