Add functional tests for Manila consistency groups
This commit adds tempest functional tests for Manila consistency
groups and cgsnapshot objects and actions. By default these tests are
enabled; 3rd party CI systems should disabled these tests through the
RUN_MANILA_CG_TESTS environment variable if their driver does not
support consistency groups.
Partially implements bp manila-consistency-groups
Change-Id: I3297e02ad53c328f0bfe5245fefdb6af80552b4a
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
new file mode 100644
index 0000000..c1fdb15
--- /dev/null
+++ b/manila_tempest_tests/tests/api/admin/test_consistency_group_actions.py
@@ -0,0 +1,118 @@
+# 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 import test
+from tempest_lib.common.utils import data_utils
+import testtools
+
+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_required_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']])
+
+ @test.attr(type=["gate", ])
+ def test_create_cg_from_cgsnapshot_with_multiple_share_types(self):
+ # Create cgsnapshot
+ cgsnapshot = self.create_cgsnapshot_wait_for_active(
+ self.consistency_group["id"], cleanup_in_class=False)
+
+ new_consistency_group = self.create_consistency_group(
+ cleanup_in_class=False, source_cgsnapshot_id=cgsnapshot['id'])
+
+ # 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))
+
+ @test.attr(type=["gate", ])
+ def test_create_cg_from_multi_typed_populated_cgsnapshot(self):
+ share_name = data_utils.rand_name("tempest-share-name")
+ share_desc = data_utils.rand_name("tempest-share-description")
+ share_size = 1
+ share = self.create_share(
+ cleanup_in_class=False,
+ name=share_name,
+ description=share_desc,
+ size=share_size,
+ consistency_group_id=self.consistency_group['id'],
+ share_type_id=self.share_type['id']
+ )
+
+ share_name2 = data_utils.rand_name("tempest-share-name")
+ share_desc2 = data_utils.rand_name("tempest-share-description")
+ share_size2 = 1
+ share2 = self.create_share(
+ cleanup_in_class=False,
+ name=share_name2,
+ description=share_desc2,
+ size=share_size2,
+ consistency_group_id=self.consistency_group['id'],
+ share_type_id=self.share_type2['id']
+ )
+
+ cg_shares = self.shares_client.list_shares(detailed=True, params={
+ 'consistency_group_id': self.consistency_group['id']})
+
+ cg_share_ids = [s['id'] for s in cg_shares]
+ for share_id in [share['id'], share2['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)
+
+ self.create_consistency_group(
+ cleanup_in_class=False, source_cgsnapshot_id=cgsnapshot['id'])
+
+ # 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)))
diff --git a/manila_tempest_tests/tests/api/admin/test_consistency_groups.py b/manila_tempest_tests/tests/api/admin/test_consistency_groups.py
new file mode 100644
index 0000000..4fbe7f9
--- /dev/null
+++ b/manila_tempest_tests/tests/api/admin/test_consistency_groups.py
@@ -0,0 +1,66 @@
+# 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 import test
+from tempest_lib.common.utils import data_utils
+import testtools
+
+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_required_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']
+
+ @test.attr(type=["gate", ])
+ def test_create_cg_with_multiple_share_types(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']])
+
+ 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))
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
new file mode 100644
index 0000000..a7a715f
--- /dev/null
+++ b/manila_tempest_tests/tests/api/admin/test_consistency_groups_negative.py
@@ -0,0 +1,270 @@
+# 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 import test
+from tempest_lib.common.utils import data_utils
+from tempest_lib import exceptions
+import testtools
+
+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_required_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_size = 1
+ cls.share = cls.create_share(
+ name=cls.share_name,
+ description=cls.share_desc,
+ size=cls.share_size,
+ 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)
+
+ @test.attr(type=["negative", "gate", ])
+ 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'])
+
+ @test.attr(type=["negative", "gate", ])
+ def test_create_share_of_unsupported_type_in_cg(self):
+ # Attempt to create share of default type in the cg
+ self.assertRaises(exceptions.BadRequest,
+ self.shares_client.create_share, size=1,
+ consistency_group_id=self.consistency_group['id'])
+
+ @test.attr(type=["negative", "gate", ])
+ def test_create_share_in_cg_that_is_not_available(self):
+ consistency_group = self.create_consistency_group(
+ cleanup_in_class=False)
+ self.addCleanup(self.shares_client.consistency_group_reset_state,
+ consistency_group['id'],
+ status='available')
+ # creating
+ self.shares_client.consistency_group_reset_state(
+ consistency_group['id'], status='creating')
+ self.shares_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,
+ size=self.share_size,
+ consistency_group_id=consistency_group['id'],
+ cleanup_in_class=False)
+ # deleting
+ self.shares_client.consistency_group_reset_state(
+ consistency_group['id'], status='deleting')
+ self.shares_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,
+ size=self.share_size,
+ consistency_group_id=consistency_group['id'],
+ cleanup_in_class=False)
+ # error
+ self.shares_client.consistency_group_reset_state(
+ consistency_group['id'], status='error')
+ self.shares_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,
+ size=self.share_size,
+ consistency_group_id=consistency_group['id'],
+ cleanup_in_class=False)
+
+ @test.attr(type=["negative", "gate", ])
+ def test_create_cgsnapshot_of_cg_that_is_not_available(self):
+ consistency_group = self.create_consistency_group(
+ cleanup_in_class=False)
+ self.addCleanup(self.shares_client.consistency_group_reset_state,
+ consistency_group['id'],
+ status='available')
+ # creating
+ self.shares_client.consistency_group_reset_state(
+ consistency_group['id'], status='creating')
+ self.shares_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)
+ # deleting
+ self.shares_client.consistency_group_reset_state(
+ consistency_group['id'], status='deleting')
+ self.shares_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)
+ # error
+ self.shares_client.consistency_group_reset_state(
+ consistency_group['id'], status='error')
+ self.shares_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)
+
+ @test.attr(type=["negative", "gate", ])
+ def test_create_cgsnapshot_of_cg_with_share_in_error_state(self):
+ consistency_group = self.create_consistency_group()
+ share_name = data_utils.rand_name("tempest-share-name")
+ share_desc = data_utils.rand_name("tempest-share-description")
+ share_size = 1
+ share = self.create_share(
+ name=share_name,
+ description=share_desc,
+ size=share_size,
+ consistency_group_id=consistency_group['id'],
+ cleanup_in_class=False,
+ )
+ 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)
+
+ @test.attr(type=["negative", "gate", ])
+ def test_delete_cgsnapshot_not_in_available_or_error(self):
+ cgsnapshot = self.create_cgsnapshot_wait_for_active(
+ self.consistency_group['id'], cleanup_in_class=False)
+ self.addCleanup(self.shares_client.cgsnapshot_reset_state,
+ cgsnapshot['id'],
+ status='available')
+
+ # creating
+ self.shares_client.cgsnapshot_reset_state(cgsnapshot['id'],
+ status='creating')
+ self.shares_client.wait_for_cgsnapshot_status(cgsnapshot['id'],
+ 'creating')
+ self.assertRaises(exceptions.Conflict,
+ self.shares_client.delete_cgsnapshot,
+ cgsnapshot['id'])
+ # deleting
+ self.shares_client.cgsnapshot_reset_state(cgsnapshot['id'],
+ status='deleting')
+ self.shares_client.wait_for_cgsnapshot_status(cgsnapshot['id'],
+ 'deleting')
+ self.assertRaises(exceptions.Conflict,
+ self.shares_client.delete_cgsnapshot,
+ cgsnapshot['id'])
+
+ @test.attr(type=["negative", "gate", ])
+ def test_delete_cg_not_in_available_or_error(self):
+ consistency_group = self.create_consistency_group(
+ cleanup_in_class=False)
+ self.addCleanup(self.shares_client.consistency_group_reset_state,
+ consistency_group['id'],
+ status='available')
+ # creating
+ self.shares_client.consistency_group_reset_state(
+ consistency_group['id'], status='creating')
+ self.shares_client.wait_for_consistency_group_status(
+ consistency_group['id'], 'creating')
+ self.assertRaises(exceptions.Conflict,
+ self.shares_client.delete_consistency_group,
+ consistency_group['id'])
+ # deleting
+ self.shares_client.consistency_group_reset_state(
+ consistency_group['id'], status='deleting')
+ self.shares_client.wait_for_consistency_group_status(
+ consistency_group['id'], 'deleting')
+ self.assertRaises(exceptions.Conflict,
+ self.shares_client.delete_consistency_group,
+ consistency_group['id'])
+
+ @test.attr(type=["negative", "gate", ])
+ def test_create_cg_with_conflicting_share_types(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)
+
+ @test.attr(type=["negative", "gate", ])
+ def test_create_cg_with_multi_tenant_share_type_and_no_share_network(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_client.create_consistency_group(
+ share_type_ids=[multi_tenant_share_type['id']])
+ 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)
+
+ @test.attr(type=["negative", "gate", ])
+ def test_update_cg_share_types(self):
+ consistency_group = self.create_consistency_group(
+ cleanup_in_class=False)
+
+ self.assertRaises(exceptions.BadRequest,
+ self.shares_client.update_consistency_group,
+ consistency_group['id'],
+ share_types=[self.share_type['id']])
diff --git a/manila_tempest_tests/tests/api/base.py b/manila_tempest_tests/tests/api/base.py
index 6444ccc..65e299d 100644
--- a/manila_tempest_tests/tests/api/base.py
+++ b/manila_tempest_tests/tests/api/base.py
@@ -280,7 +280,8 @@
def _create_share(cls, share_protocol=None, size=1, name=None,
snapshot_id=None, description=None, metadata=None,
share_network_id=None, share_type_id=None,
- client=None, cleanup_in_class=True, is_public=False):
+ consistency_group_id=None, client=None,
+ cleanup_in_class=True, is_public=False):
client = client or cls.shares_client
description = description or "Tempest's share"
share_network_id = share_network_id or client.share_network_id or None
@@ -296,8 +297,12 @@
'share_type_id': share_type_id,
'is_public': is_public,
}
+ if consistency_group_id:
+ kwargs['consistency_group_id'] = consistency_group_id
+
share = client.create_share(**kwargs)
- resource = {"type": "share", "id": share["id"], "client": client}
+ resource = {"type": "share", "id": share["id"], "client": client,
+ "consistency_group_id": consistency_group_id}
cleanup_list = (cls.class_resources if cleanup_in_class else
cls.method_resources)
cleanup_list.insert(0, resource)
@@ -376,6 +381,42 @@
return [d["share"] for d in data]
@classmethod
+ def create_consistency_group(cls, client=None, cleanup_in_class=True,
+ share_network_id=None, **kwargs):
+ client = client or cls.shares_client
+ kwargs['share_network_id'] = (share_network_id or
+ client.share_network_id or None)
+ consistency_group = client.create_consistency_group(**kwargs)
+ resource = {
+ "type": "consistency_group",
+ "id": consistency_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(
+ detailed=True,
+ params={'consistency_group_id': consistency_group['id']})
+
+ for share in new_cg_shares:
+ resource = {"type": "share",
+ "id": share["id"],
+ "client": client,
+ "consistency_group_id": share.get(
+ 'consistency_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
+
+ @classmethod
def create_snapshot_wait_for_active(cls, share_id, name=None,
description=None, force=False,
client=None, cleanup_in_class=True):
@@ -397,6 +438,27 @@
return snapshot
@classmethod
+ def create_cgsnapshot_wait_for_active(cls, consistency_group_id,
+ name=None, description=None,
+ client=None, cleanup_in_class=True):
+ client = client or cls.shares_client
+ if description is None:
+ description = "Tempest's cgsnapshot"
+ cgsnapshot = client.create_cgsnapshot(consistency_group_id, name=name,
+ description=description)
+ resource = {
+ "type": "cgsnapshot",
+ "id": cgsnapshot["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
+
+ @classmethod
def create_share_network(cls, client=None,
cleanup_in_class=False, **kwargs):
if client is None:
@@ -494,7 +556,11 @@
client = res["client"]
with handle_cleanup_exceptions():
if res["type"] is "share":
- client.delete_share(res_id)
+ params = None
+ cg_id = res.get('consistency_group_id')
+ if cg_id:
+ params = {'consistency_group_id': cg_id}
+ client.delete_share(res_id, params=params)
client.wait_for_resource_deletion(share_id=res_id)
elif res["type"] is "snapshot":
client.delete_snapshot(res_id)
@@ -508,6 +574,12 @@
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)
else:
LOG.warn("Provided unsupported resource type for "
"cleanup '%s'. Skipping." % res["type"])
diff --git a/manila_tempest_tests/tests/api/test_consistency_group_actions.py b/manila_tempest_tests/tests/api/test_consistency_group_actions.py
new file mode 100644
index 0000000..81bff8a
--- /dev/null
+++ b/manila_tempest_tests/tests/api/test_consistency_group_actions.py
@@ -0,0 +1,371 @@
+# -*- 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 import test
+from tempest_lib.common.utils import data_utils
+import testtools
+
+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 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 2 shares 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_size = 1
+ cls.share = cls.create_share(
+ name=cls.share_name,
+ description=cls.share_desc,
+ size=cls.share_size,
+ consistency_group_id=cls.consistency_group['id'],
+ metadata={'key': 'value'},
+ )
+
+ cls.share_name2 = data_utils.rand_name("tempest-share-name")
+ cls.share_desc2 = data_utils.rand_name("tempest-share-description")
+ cls.share_size2 = 2
+ cls.share2 = cls.create_share(
+ name=cls.share_name2,
+ description=cls.share_desc2,
+ size=cls.share_size2,
+ consistency_group_id=cls.consistency_group['id'],
+ )
+
+ 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)
+
+ # Create second consistency group for purposes of sorting and snapshot
+ # filtering
+ cls.cg_name2 = data_utils.rand_name("tempest-cg-name")
+ cls.cg_desc2 = data_utils.rand_name("tempest-cg-description")
+ cls.consistency_group2 = cls.create_consistency_group(
+ name=cls.cg_name2,
+ description=cls.cg_desc2,
+ )
+
+ # Create 1 share in second consistency group
+ cls.share_name3 = data_utils.rand_name("tempest-share-name")
+ cls.share_desc3 = data_utils.rand_name("tempest-share-description")
+ cls.share3 = cls.create_share(
+ name=cls.share_name3,
+ description=cls.share_desc3,
+ size=cls.share_size,
+ consistency_group_id=cls.consistency_group2['id'],
+ )
+
+ cls.cgsnap_name2 = data_utils.rand_name("tempest-cgsnap-name")
+ cls.cgsnap_desc2 = data_utils.rand_name("tempest-cgsnap-description")
+ cls.cgsnapshot2 = cls.create_cgsnapshot_wait_for_active(
+ cls.consistency_group2['id'],
+ name=cls.cgsnap_name2,
+ description=cls.cgsnap_desc2)
+
+ @test.attr(type=["gate", ])
+ def test_get_consistency_group(self):
+
+ # Get consistency group
+ consistency_group = self.shares_client.get_consistency_group(
+ self.consistency_group['id'])
+
+ # 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)
+
+ @test.attr(type=["gate", ])
+ def test_get_share(self):
+
+ # Get share
+ share = self.shares_client.get_share(self.share['id'])
+
+ # 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.consistency_group["id"], share["consistency_group_id"])
+ self.assertEqual(
+ self.consistency_group["id"], share["consistency_group_id"], msg)
+
+ @test.attr(type=["gate", ])
+ def test_list_consistency_groups(self):
+
+ # List consistency groups
+ consistency_groups = self.shares_client.list_consistency_groups()
+
+ # 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.consistency_group["id"],
+ self.consistency_group2["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)
+
+ @test.attr(type=["gate", ])
+ def test_list_consistency_groups_with_detail(self):
+
+ # List consistency groups
+ consistency_groups = self.shares_client.list_consistency_groups(
+ detailed=True)
+
+ # 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.consistency_group["id"],
+ self.consistency_group2["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)
+
+ @test.attr(type=["gate", ])
+ def test_filter_shares_by_consistency_group_id(self):
+
+ shares = self.shares_client.list_shares(detailed=True, params={
+ 'consistency_group_id': self.consistency_group['id']})
+
+ 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.share['id'], share_ids,
+ 'Share %s expected in returned list, but got %s'
+ % (self.share['id'], share_ids))
+ self.assertIn(self.share2['id'], share_ids,
+ 'Share %s expected in returned list, but got %s'
+ % (self.share['id'], share_ids))
+
+ @test.attr(type=["gate", ])
+ def test_get_cgsnapshot(self):
+ # Get consistency group
+ consistency_group = self.shares_client.get_consistency_group(
+ self.consistency_group['id'])
+
+ # 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)
+
+ @test.attr(type=["gate", ])
+ def test_get_cgsnapshot_members(self):
+
+ cgsnapshot_members = self.shares_client.list_cgsnapshot_members(
+ self.cgsnapshot['id'])
+ 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.share['id'], self.share2['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.share, self.share2]:
+ 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'])
+
+ @test.attr(type=["gate", "smoke", ])
+ def test_create_consistency_group_from_populated_cgsnapshot(self):
+
+ cgsnapshot_members = self.shares_client.list_cgsnapshot_members(
+ self.cgsnapshot['id'])
+
+ new_consistency_group = self.create_consistency_group(
+ cleanup_in_class=False, source_cgsnapshot_id=self.cgsnapshot['id'])
+
+ new_shares = self.shares_client.list_shares(
+ params={'consistency_group_id': new_consistency_group['id']},
+ detailed=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 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'])
+
+
+@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,
+ )
+
+ @test.attr(type=["gate", ])
+ def test_update_consistency_group(self):
+
+ # Get consistency_group
+ consistency_group = self.shares_client.get_consistency_group(
+ self.consistency_group['id'])
+ 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_client.update_consistency_group(
+ consistency_group["id"], name=new_name, description=new_desc)
+ self.assertEqual(new_name, updated["name"])
+ self.assertEqual(new_desc, updated["description"])
+
+ # Get consistency_group
+ consistency_group = self.shares_client.get_consistency_group(
+ self.consistency_group['id'])
+ self.assertEqual(new_name, consistency_group["name"])
+ self.assertEqual(new_desc, consistency_group["description"])
+
+ @test.attr(type=["gate", ])
+ def test_create_update_read_consistency_group_with_unicode(self):
+ value1 = u'ಠ_ಠ'
+ value2 = u'ಠ_ರೃ'
+ # Create consistency_group
+ consistency_group = self.create_consistency_group(
+ cleanup_in_class=False, name=value1, description=value1)
+ self.assertEqual(value1, consistency_group["name"])
+ self.assertEqual(value1, consistency_group["description"])
+
+ # Update consistency_group
+ updated = self.shares_client.update_consistency_group(
+ consistency_group["id"], name=value2, description=value2)
+ self.assertEqual(value2, updated["name"])
+ self.assertEqual(value2, updated["description"])
+
+ # Get consistency_group
+ consistency_group = self.shares_client.get_consistency_group(
+ consistency_group['id'])
+ 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
new file mode 100644
index 0000000..1ba3902
--- /dev/null
+++ b/manila_tempest_tests/tests/api/test_consistency_groups.py
@@ -0,0 +1,130 @@
+# 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 # noqa
+from tempest import test # noqa
+from tempest_lib import exceptions as lib_exc # noqa
+import testtools # noqa
+
+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."""
+
+ @test.attr(type=["gate", ])
+ def test_create_populate_delete_consistency_group(self):
+ # Create a consistency group
+ consistency_group = self.create_consistency_group(
+ cleanup_in_class=False)
+ 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)
+ # Delete
+ params = {"consistency_group_id": consistency_group['id']}
+ self.shares_client.delete_share(share['id'], params=params)
+ self.shares_client.wait_for_resource_deletion(share_id=share['id'])
+ self.shares_client.delete_consistency_group(consistency_group['id'])
+ self.shares_client.wait_for_resource_deletion(
+ cg_id=consistency_group['id'])
+
+ # Verify
+ self.assertRaises(lib_exc.NotFound,
+ self.shares_client.get_consistency_group,
+ consistency_group['id'])
+ self.assertRaises(lib_exc.NotFound,
+ self.shares_client.get_share,
+ share['id'])
+
+ @test.attr(type=["gate", ])
+ def test_create_delete_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)
+
+ 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_client.list_cgsnapshot_members(
+ cgsnapshot['id'])
+
+ self.assertEmpty(cgsnapshot_members,
+ 'Expected 0 cgsnapshot members, got %s' % len(
+ cgsnapshot_members))
+
+ # delete snapshot
+ self.shares_client.delete_cgsnapshot(cgsnapshot["id"])
+ self.shares_client.wait_for_resource_deletion(
+ cgsnapshot_id=cgsnapshot["id"])
+ self.assertRaises(lib_exc.NotFound,
+ self.shares_client.get_cgsnapshot, cgsnapshot['id'])
+
+ @test.attr(type=["gate", "smoke", ])
+ 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_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 share %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)
diff --git a/manila_tempest_tests/tests/api/test_consistency_groups_negative.py b/manila_tempest_tests/tests/api/test_consistency_groups_negative.py
new file mode 100644
index 0000000..7c813c0
--- /dev/null
+++ b/manila_tempest_tests/tests/api/test_consistency_groups_negative.py
@@ -0,0 +1,205 @@
+# 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 import test
+from tempest_lib.common.utils import data_utils
+from tempest_lib import exceptions as lib_exc
+import testtools
+
+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.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_size = 1
+ cls.share = cls.create_share(
+ name=cls.share_name,
+ description=cls.share_desc,
+ size=cls.share_size,
+ 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)
+
+ @test.attr(type=["negative", "smoke", "gate", ])
+ def test_create_cg_with_invalid_source_cgsnapshot_id_value(
+ self):
+ self.assertRaises(lib_exc.BadRequest,
+ self.create_consistency_group,
+ source_cgsnapshot_id='foobar',
+ cleanup_in_class=False)
+
+ @test.attr(type=["negative", "smoke", "gate", ])
+ def test_create_cg_with_nonexistent_source_cgsnapshot_id_value(self):
+ self.assertRaises(lib_exc.BadRequest,
+ self.create_consistency_group,
+ source_cgsnapshot_id=self.share['id'],
+ cleanup_in_class=False)
+
+ @test.attr(type=["negative", "smoke", "gate", ])
+ def test_create_cg_with_invalid_share_network_id_value(
+ self):
+ self.assertRaises(lib_exc.BadRequest,
+ self.create_consistency_group,
+ share_network_id='foobar',
+ cleanup_in_class=False)
+
+ @test.attr(type=["negative", "smoke", "gate", ])
+ def test_create_cg_with_nonexistent_share_network_id_value(self):
+ self.assertRaises(lib_exc.BadRequest,
+ self.create_consistency_group,
+ share_network_id=self.share['id'],
+ cleanup_in_class=False)
+
+ @test.attr(type=["negative", "smoke", "gate", ])
+ def test_create_cg_with_invalid_share_type_id_value(
+ self):
+ self.assertRaises(lib_exc.BadRequest,
+ self.create_consistency_group,
+ share_type_ids=['foobar'],
+ cleanup_in_class=False)
+
+ @test.attr(type=["negative", "smoke", "gate", ])
+ def test_create_cg_with_nonexistent_share_type_id_value(self):
+ self.assertRaises(lib_exc.BadRequest,
+ self.create_consistency_group,
+ share_type_ids=[self.share['id']],
+ cleanup_in_class=False)
+
+ @test.attr(type=["negative", "smoke", "gate", ])
+ def test_create_cgsnapshot_with_invalid_cg_id_value(
+ self):
+ self.assertRaises(lib_exc.BadRequest,
+ self.create_cgsnapshot_wait_for_active,
+ 'foobar',
+ cleanup_in_class=False)
+
+ @test.attr(type=["negative", "smoke", "gate", ])
+ def test_create_cgsnapshot_with_nonexistent_cg_id_value(self):
+ self.assertRaises(lib_exc.BadRequest,
+ self.create_cgsnapshot_wait_for_active,
+ self.share['id'],
+ cleanup_in_class=False)
+
+ @test.attr(type=["negative", "smoke", "gate", ])
+ def test_get_cg_with_wrong_id(self):
+ self.assertRaises(lib_exc.NotFound,
+ self.shares_client.get_consistency_group,
+ "wrong_consistency_group_id")
+
+ @test.attr(type=["negative", "smoke", "gate", ])
+ def test_get_cg_without_passing_cg_id(self):
+ self.assertRaises(lib_exc.NotFound,
+ self.shares_client.get_consistency_group, '')
+
+ @test.attr(type=["negative", "smoke", "gate", ])
+ def test_update_cg_with_wrong_id(self):
+ self.assertRaises(lib_exc.NotFound,
+ self.shares_client.update_consistency_group,
+ 'wrong_consistency_group_id',
+ name='new_name',
+ description='new_description')
+
+ @test.attr(type=["negative", "smoke", "gate", ])
+ def test_delete_cg_with_wrong_id(self):
+ self.assertRaises(lib_exc.NotFound,
+ self.shares_client.delete_consistency_group,
+ "wrong_consistency_group_id")
+
+ @test.attr(type=["negative", "smoke", "gate", ])
+ def test_delete_cg_without_passing_cg_id(self):
+ self.assertRaises(lib_exc.NotFound,
+ self.shares_client.delete_consistency_group, '')
+
+ @test.attr(type=["negative", "gate", ])
+ def test_delete_cg_in_use_by_cgsnapshot(self):
+ # Attempt delete of share type
+ self.assertRaises(lib_exc.Conflict,
+ self.shares_client.delete_consistency_group,
+ self.consistency_group['id'])
+
+ @test.attr(type=["negative", "gate", ])
+ def test_delete_share_in_use_by_cgsnapshot(self):
+ # Attempt delete of share type
+ params = {'consistency_group_id': self.share['consistency_group_id']}
+ self.assertRaises(lib_exc.Forbidden,
+ self.shares_client.delete_share,
+ self.share['id'],
+ params=params)
+
+ @test.attr(type=["negative", "smoke", "gate", ])
+ def test_delete_cg_containing_a_share(self):
+ self.assertRaises(lib_exc.Conflict,
+ self.shares_client.delete_consistency_group,
+ self.consistency_group['id'])
+ # Verify consistency group is not put into error state from conflict
+ cg = self.shares_client.get_consistency_group(
+ self.consistency_group['id'])
+ self.assertEqual('available', cg['status'])
+
+ @test.attr(type=["negative", "smoke", "gate", ])
+ def test_filter_shares_on_invalid_cg_id(self):
+ shares = self.shares_client.list_shares(detailed=True, params={
+ 'consistency_group_id': 'foobar'})
+
+ self.assertEqual(0, len(shares), 'Incorrect number of shares '
+ 'returned. Expected 0, got %s.' %
+ len(shares))
+
+ @test.attr(type=["negative", "smoke", "gate", ])
+ def test_filter_shares_on_nonexistent_cg_id(self):
+ shares = self.shares_client.list_shares(detailed=True, params={
+ 'consistency_group_id': self.share['id']})
+
+ self.assertEqual(0, len(shares), 'Incorrect number of shares '
+ 'returned. Expected 0, got %s.' %
+ len(shares))
+
+ @test.attr(type=["negative", "smoke", "gate", ])
+ def test_filter_shares_on_empty_cg_id(self):
+ consistency_group = self.create_consistency_group(
+ name='tempest_cg',
+ description='tempest_cg_desc',
+ cleanup_in_class=False,
+ )
+ shares = self.shares_client.list_shares(detailed=True, params={
+ 'consistency_group_id': consistency_group['id']})
+
+ self.assertEqual(0, len(shares), 'Incorrect number of shares '
+ 'returned. Expected 0, got %s.' %
+ len(shares))