Functional tests for create share from snapshot different pools/azs
This patch adds functional tests for the feature create share
from snapshot in different pools or availability zones.
Partially-implements: bp create-share-from-snapshot-in-another-pool-or-backend
Change-Id: Iece5bc2c1aa6485f3533711baf514bb852357820
diff --git a/manila_tempest_tests/config.py b/manila_tempest_tests/config.py
index 6973092..2205f6b 100644
--- a/manila_tempest_tests/config.py
+++ b/manila_tempest_tests/config.py
@@ -240,6 +240,11 @@
cfg.BoolOpt("run_mount_snapshot_tests",
default=False,
help="Enable or disable mountable snapshot tests."),
+ cfg.BoolOpt("run_create_share_from_snapshot_in_another_pool_or_az_tests",
+ default=False,
+ help="Defines whether to run tests that create share from "
+ "snapshots in another pool or az. Enable this "
+ "option if the used driver supports it."),
cfg.StrOpt("image_with_share_tools",
default="manila-service-image-master",
diff --git a/manila_tempest_tests/tests/api/admin/test_migration.py b/manila_tempest_tests/tests/api/admin/test_migration.py
index 6ceae58..1bc67fb 100644
--- a/manila_tempest_tests/tests/api/admin/test_migration.py
+++ b/manila_tempest_tests/tests/api/admin/test_migration.py
@@ -176,6 +176,11 @@
self.assertIn(r, filtered_rules)
self.assertEqual(len(expected_rules), len(filtered_rules))
+ # In v 2.54 and beyond, we expect key 'progress' in the destination
+ # share data
+ if utils.is_microversion_supported('2.54'):
+ self.assertEqual('100%', share['progress'])
+
# Share not migrated yet
else:
self.assertNotEqual(dest_pool, share['host'])
diff --git a/manila_tempest_tests/tests/api/admin/test_share_instances.py b/manila_tempest_tests/tests/api/admin/test_share_instances.py
index 89f0124..4474ffd 100644
--- a/manila_tempest_tests/tests/api/admin/test_share_instances.py
+++ b/manila_tempest_tests/tests/api/admin/test_share_instances.py
@@ -63,9 +63,11 @@
self.assertIn(self.share['id'], share_ids, msg)
@tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
- @ddt.data('2.3', '2.9', '2.10', '2.30')
+ @ddt.data('2.3', '2.9', '2.10', '2.30', '2.54')
def test_get_share_instance(self, version):
"""Test that we get the proper keys back for the instance."""
+ self.skip_if_microversion_not_supported(version)
+
share_instances = self.shares_v2_client.get_instances_of_share(
self.share['id'], version=version,
)
@@ -87,6 +89,8 @@
expected_keys.append("share_type_id")
if utils.is_microversion_ge(version, '2.30'):
expected_keys.append("cast_rules_to_readonly")
+ if utils.is_microversion_ge(version, '2.54'):
+ expected_keys.append("progress")
expected_keys = sorted(expected_keys)
actual_keys = sorted(si.keys())
self.assertEqual(expected_keys, actual_keys,
diff --git a/manila_tempest_tests/tests/api/base.py b/manila_tempest_tests/tests/api/base.py
index 8c18a4b..b5db8b3 100644
--- a/manila_tempest_tests/tests/api/base.py
+++ b/manila_tempest_tests/tests/api/base.py
@@ -718,6 +718,7 @@
client = client or cls.admin_shares_v2_client
if utils.is_microversion_supported('2.23'):
return client.list_pools(
+ detail=True,
search_opts={'share_type': share_type['id']})['pools']
pools = client.list_pools(detail=True)['pools']
diff --git a/manila_tempest_tests/tests/api/test_shares.py b/manila_tempest_tests/tests/api/test_shares.py
index 786648e..49af8e2 100644
--- a/manila_tempest_tests/tests/api/test_shares.py
+++ b/manila_tempest_tests/tests/api/test_shares.py
@@ -104,6 +104,12 @@
detailed_elements.add('create_share_from_snapshot_support')
self.assertTrue(detailed_elements.issubset(share.keys()), msg)
+ # In v 2.54 and beyond, we expect key 'progress' in the share data
+ # returned by the share create API.
+ if utils.is_microversion_supported('2.54'):
+ detailed_elements.add('progress')
+ self.assertTrue(detailed_elements.issubset(share.keys()), msg)
+
# Delete share
self.shares_v2_client.delete_share(share['id'])
self.shares_v2_client.wait_for_resource_deletion(share_id=share['id'])
diff --git a/manila_tempest_tests/tests/api/test_shares_from_snapshot_across_pools.py b/manila_tempest_tests/tests/api/test_shares_from_snapshot_across_pools.py
new file mode 100644
index 0000000..ce7f46d
--- /dev/null
+++ b/manila_tempest_tests/tests/api/test_shares_from_snapshot_across_pools.py
@@ -0,0 +1,167 @@
+# Copyright 2020 NetApp Inc.
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from collections import defaultdict
+
+from tempest import config
+from testtools import testcase as tc
+
+from manila_tempest_tests.tests.api import base
+from manila_tempest_tests import utils
+
+CONF = config.CONF
+
+
+class SharesFromSnapshotAcrossPools(base.BaseSharesMixedTest):
+ """Test class for share creation from a snapshot across pools."""
+
+ @classmethod
+ def resource_setup(cls):
+ super(SharesFromSnapshotAcrossPools, cls).resource_setup()
+ # create share_type
+ extra_specs = {"create_share_from_snapshot_support": True,
+ "snapshot_support": True}
+ cls.share_type = cls._create_share_type(extra_specs)
+ cls.share_type_id = cls.share_type['id']
+ cls.admin_client = cls.admin_shares_v2_client
+ cls.pools = cls.get_pools_matching_share_type(cls.share_type,
+ client=cls.admin_client)
+ if len(cls.pools) < 2:
+ msg = ("Could not find the necessary pools. At least two "
+ "compatibles pools are needed to run the tests to create "
+ "share from snapshot across pools.")
+ raise cls.skipException(msg)
+
+ # Availability zones grouped by 'replication_domain'
+ cls.rep_domain_azs = defaultdict(set)
+ for pool in cls.pools:
+ backend = pool['name'].split("#")[0]
+ rep_domain = pool['capabilities'].get('replication_domain')
+
+ if rep_domain is not None:
+ # Update pools with the availability zone
+ pool['availability_zone'] = (
+ cls.get_availability_zones(backends=[backend])[0])
+ cls.rep_domain_azs[rep_domain].add(pool['availability_zone'])
+
+ @classmethod
+ def skip_checks(cls):
+ super(SharesFromSnapshotAcrossPools, cls).skip_checks()
+ if not CONF.share.capability_create_share_from_snapshot_support:
+ raise cls.skipException(
+ 'Create share from snapshot tests are disabled.')
+ if (not CONF.share
+ .run_create_share_from_snapshot_in_another_pool_or_az_tests):
+ raise cls.skipException(
+ 'Create share from snapshot in another pool or az tests are '
+ 'disabled.')
+ utils.check_skip_if_microversion_lt("2.54")
+
+ @tc.attr(base.TAG_POSITIVE, base.TAG_BACKEND)
+ def test_create_share_from_snapshot_across_pools_within_backend(self):
+ backends = [pool['backend'] for pool in self.pools]
+ duplicated_backend_names = [x for n, x in enumerate(backends)
+ if x in backends[:n]]
+ if not duplicated_backend_names:
+ msg = ("Could not find the necessary pools. At least two pools in"
+ " the same backend are needed to run the tests to create"
+ " share from snapshot in another pool in the same backend.")
+ raise self.skipException(msg)
+
+ # This filter will return the pool_names of the first duplicated
+ # backend
+ pool_names = [x['pool'] for x in filter(
+ lambda x: x['backend'] == duplicated_backend_names[0], self.pools)]
+
+ # Creating share type setting up the pool_name and backend_name
+ extra_specs = {"pool_name": pool_names[0]}
+ self.admin_client.update_share_type_extra_specs(
+ self.share_type['id'], extra_specs)
+ share_type_a_get = self.admin_client.get_share_type(
+ self.share_type['id'])
+
+ self.addCleanup(
+ self.admin_shares_v2_client.delete_share_type_extra_spec,
+ self.share_type['id'], 'pool_name')
+
+ # Create source share
+ share_a = self.create_share(
+ share_type_id=share_type_a_get["share_type"]["id"])
+
+ # Retrieving the share using admin client because the shares's host
+ # field is necessary to do the assert
+ share_get_a = self.admin_client.get_share(share_a["id"])
+
+ # Create snapshot from source share
+ snap = self.create_snapshot_wait_for_active(share_get_a["id"])
+
+ # There's really no other way of deterministically ensuring a snapshot
+ # can be cloned in a different pool, because the scheduler will ensure
+ # it finds the best pool with knowledge that make senses at that point
+ # in time. Force the creation in another pool using the same share type
+ self.admin_client.update_share_type_extra_spec(
+ self.share_type['id'], "pool_name", pool_names[1])
+
+ # Create share from snapshot another pool
+ share_b = self.create_share(snapshot_id=snap["id"])
+
+ # Retrieving the share using admin client because the shares's host
+ # field is necessary to do the assert
+ share_get_b = self.admin_client.get_share(share_b['id'])
+
+ # Verify share created from snapshot
+ msg = ("Expected snapshot_id %s as "
+ "source of share %s" % (snap["id"], share_get_b["snapshot_id"]))
+ self.assertEqual(share_get_b["snapshot_id"], snap["id"], msg)
+
+ # Verify different pools
+ pool_name_a = share_get_a["host"].split("#")[1]
+ pool_name_b = share_get_b["host"].split("#")[1]
+ msg = ("The snapshot clone share was created on the same pool as the"
+ " source share %s" % pool_name_a)
+ self.assertNotEqual(pool_name_a, pool_name_b, msg)
+
+ @tc.attr(base.TAG_POSITIVE, base.TAG_BACKEND)
+ def test_share_from_snapshot_across_azs(self):
+ azs = next(self.rep_domain_azs[rep] for rep in self.rep_domain_azs if
+ len(self.rep_domain_azs[rep]) > 1)
+ if azs is None:
+ msg = ("Could not find the necessary azs. At least two azs "
+ "are needed to run the test to create share from snapshot "
+ "across azs.")
+ raise self.skipException(msg)
+ azs = list(azs)
+ share_a = self.create_share(share_type_id=self.share_type_id,
+ is_public=True,
+ availability_zone=azs[0])
+
+ # Create snapshot
+ snap = self.create_snapshot_wait_for_active(share_a["id"])
+
+ # Create share from snapshot
+ share_b = self.create_share(availability_zone=azs[1],
+ snapshot_id=snap["id"])
+
+ # Verify share created from snapshot
+ msg = ("Expected snapshot_id %s as "
+ "source of share: %s" % (snap["id"], share_b["snapshot_id"]))
+ self.assertEqual(share_b["snapshot_id"], snap["id"], msg)
+
+ # Verify different azs
+ msg = ("The snapshot clone share was created on the same AZ as the"
+ " source share %s" % share_a["availability_zone"])
+ self.assertNotEqual(share_b["availability_zone"],
+ share_a["availability_zone"],
+ msg)