Add tests to ensure snapshots across replicas
Related-Bug: #1546303
Depends-On: Ia4cd2a36e31418e7a3d1c218080caa632755fe16
Depends-On: Id318a4adc0faf64a4bef57252aa2f0d9083b82b1
Change-Id: I269225b976efe13b2cbb9e0648d541a063df70e5
diff --git a/manila_tempest_tests/tests/api/base.py b/manila_tempest_tests/tests/api/base.py
index 0adfbc1..eab6b38 100644
--- a/manila_tempest_tests/tests/api/base.py
+++ b/manila_tempest_tests/tests/api/base.py
@@ -468,7 +468,7 @@
description=None, force=False,
client=None, cleanup_in_class=True):
if client is None:
- client = cls.shares_client
+ client = cls.shares_v2_client
if description is None:
description = "Tempest's snapshot"
snapshot = client.create_snapshot(share_id, name, description, force)
@@ -521,6 +521,16 @@
service['state'] == 'up']
return zones
+ def get_pools_for_replication_domain(self):
+ # Get the list of pools for the replication domain
+ pools = self.admin_client.list_pools(detail=True)['pools']
+ instance_host = self.shares[0]['host']
+ host_pool = [p for p in pools if p['name'] == instance_host][0]
+ rep_domain = host_pool['capabilities']['replication_domain']
+ pools_in_rep_domain = [p for p in pools if p['capabilities'][
+ 'replication_domain'] == rep_domain]
+ return rep_domain, pools_in_rep_domain
+
@classmethod
def create_share_replica(cls, share_id, availability_zone, client=None,
cleanup_in_class=False, cleanup=True):
@@ -545,8 +555,11 @@
@classmethod
def delete_share_replica(cls, replica_id, client=None):
client = client or cls.shares_v2_client
- client.delete_share_replica(replica_id)
- client.wait_for_resource_deletion(replica_id=replica_id)
+ try:
+ client.delete_share_replica(replica_id)
+ client.wait_for_resource_deletion(replica_id=replica_id)
+ except exceptions.NotFound:
+ pass
@classmethod
def promote_share_replica(cls, replica_id, client=None):
@@ -635,6 +648,19 @@
ic["deleted"] = True
@classmethod
+ def clear_share_replicas(cls, share_id, client=None):
+ client = client or cls.shares_v2_client
+ share_replicas = client.list_share_replicas(
+ share_id=share_id)
+
+ for replica in share_replicas:
+ try:
+ cls.delete_share_replica(replica['id'])
+ except exceptions.BadRequest:
+ # Ignore the exception due to deletion of last active replica
+ pass
+
+ @classmethod
def clear_resources(cls, resources=None):
"""Deletes resources, that were created in test suites.
@@ -658,6 +684,7 @@
client = res["client"]
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}
diff --git a/manila_tempest_tests/tests/api/test_replication.py b/manila_tempest_tests/tests/api/test_replication.py
index 4f4268c..f45a72e 100644
--- a/manila_tempest_tests/tests/api/test_replication.py
+++ b/manila_tempest_tests/tests/api/test_replication.py
@@ -108,16 +108,6 @@
return [replica for replica in replica_list
if replica['replica_state'] == r_state]
- def _get_pools_for_replication_domain(self):
- # Get the list of pools for the replication domain
- pools = self.admin_client.list_pools(detail=True)['pools']
- instance_host = self.shares[0]['host']
- host_pool = [p for p in pools if p['name'] == instance_host][0]
- rep_domain = host_pool['capabilities']['replication_domain']
- pools_in_rep_domain = [p for p in pools if p['capabilities'][
- 'replication_domain'] == rep_domain]
- return rep_domain, pools_in_rep_domain
-
def _verify_config_and_set_access_rule_data(self):
"""Verify the access rule configuration is enabled for NFS.
@@ -157,9 +147,10 @@
# Create the replica
self._verify_create_replica()
- # Verify access rule transitions to 'active' state.
- self.shares_v2_client.wait_for_access_rule_status(
- self.shares[0]["id"], rule["id"], constants.RULE_STATE_ACTIVE)
+ # Verify access_rules_status transitions to 'active' state.
+ self.shares_v2_client.wait_for_share_status(
+ self.shares[0]["id"], constants.RULE_STATE_ACTIVE,
+ status_attr='access_rules_status')
# Delete rule and wait for deletion
self.shares_v2_client.delete_access_rule(self.shares[0]["id"],
@@ -174,17 +165,19 @@
share_replica = self._verify_create_replica()
# Add access rule
- rule = self.shares_v2_client.create_access_rule(
+ self.shares_v2_client.create_access_rule(
self.shares[0]["id"], access_type, access_to, 'ro')
- self.shares_v2_client.wait_for_access_rule_status(
- self.shares[0]["id"], rule["id"], constants.RULE_STATE_ACTIVE)
+
+ self.shares_v2_client.wait_for_share_status(
+ self.shares[0]["id"], constants.RULE_STATE_ACTIVE,
+ status_attr='access_rules_status')
# Delete the replica
self.delete_share_replica(share_replica["id"])
@test.attr(type=["gate", ])
def test_add_multiple_share_replicas(self):
- rep_domain, pools = self._get_pools_for_replication_domain()
+ rep_domain, pools = self.get_pools_for_replication_domain()
if len(pools) < 3:
msg = ("Replication domain %(domain)s has only %(count)s pools. "
"Need at least 3 pools to run this test." %
diff --git a/manila_tempest_tests/tests/api/test_replication_snapshots.py b/manila_tempest_tests/tests/api/test_replication_snapshots.py
new file mode 100644
index 0000000..72121f1
--- /dev/null
+++ b/manila_tempest_tests/tests/api/test_replication_snapshots.py
@@ -0,0 +1,199 @@
+# Copyright 2016 Yogesh Kshirsagar
+# 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 import test
+import testtools
+
+from manila_tempest_tests import clients_share as clients
+from manila_tempest_tests.common import constants
+from manila_tempest_tests import share_exceptions
+from manila_tempest_tests.tests.api import base
+
+CONF = config.CONF
+_MIN_SUPPORTED_MICROVERSION = '2.11'
+
+
+@testtools.skipUnless(CONF.share.run_replication_tests,
+ 'Replication tests are disabled.')
+@testtools.skipUnless(CONF.share.run_snapshot_tests,
+ 'Snapshot tests disabled.')
+@base.skip_if_microversion_lt(_MIN_SUPPORTED_MICROVERSION)
+class ReplicationSnapshotTest(base.BaseSharesTest):
+
+ @classmethod
+ def resource_setup(cls):
+ super(ReplicationSnapshotTest, cls).resource_setup()
+ # Create share_type
+ name = data_utils.rand_name(constants.TEMPEST_MANILA_PREFIX)
+ cls.admin_client = clients.AdminManager().shares_v2_client
+ cls.replication_type = CONF.share.backend_replication_type
+
+ if cls.replication_type not in constants.REPLICATION_TYPE_CHOICES:
+ raise share_exceptions.ShareReplicationTypeException(
+ replication_type=cls.replication_type
+ )
+ cls.zones = cls.get_availability_zones(client=cls.admin_client)
+ cls.share_zone = cls.zones[0]
+ cls.replica_zone = cls.zones[-1]
+
+ cls.extra_specs = cls.add_required_extra_specs_to_dict(
+ {"replication_type": cls.replication_type})
+ share_type = cls.create_share_type(
+ name,
+ extra_specs=cls.extra_specs,
+ client=cls.admin_client)
+ cls.share_type = share_type["share_type"]
+ # Create share with above share_type
+ cls.creation_data = {'kwargs': {
+ 'share_type_id': cls.share_type['id'],
+ 'availability_zone': cls.share_zone,
+ }}
+
+ @test.attr(type=["gate", ])
+ def test_snapshot_after_share_replica(self):
+ """Test the snapshot for replicated share.
+
+ Create replica first and then create a snapshot.
+ Verify that the snapshot is properly created under replica by
+ creating a share from that snapshot.
+ """
+ share = self.create_share(share_type_id=self.share_type['id'],
+ availability_zone=self.share_zone)
+ original_replica = self.shares_v2_client.list_share_replicas(
+ share["id"])[0]
+
+ share_replica = self.create_share_replica(share["id"],
+ self.replica_zone,
+ cleanup=False)
+ self.addCleanup(self.delete_share_replica, original_replica['id'])
+ self.shares_v2_client.wait_for_share_replica_status(
+ share_replica['id'], constants.REPLICATION_STATE_IN_SYNC,
+ status_attr='replica_state')
+
+ snapshot = self.create_snapshot_wait_for_active(share["id"])
+ self.promote_share_replica(share_replica['id'])
+ self.delete_share_replica(original_replica['id'])
+ self.create_share(snapshot_id=snapshot['id'])
+
+ @test.attr(type=["gate", ])
+ def test_snapshot_before_share_replica(self):
+ """Test the snapshot for replicated share.
+
+ Create snapshot before creating share replica for the same
+ share.
+ Verify snapshot by creating share from the snapshot.
+ """
+ share = self.create_share(share_type_id=self.share_type['id'],
+ availability_zone=self.share_zone)
+ snapshot = self.create_snapshot_wait_for_active(share["id"])
+
+ original_replica = self.shares_v2_client.list_share_replicas(
+ share["id"])[0]
+ share_replica = self.create_share_replica(share["id"],
+ self.replica_zone,
+ cleanup=False)
+ self.addCleanup(self.delete_share_replica, original_replica['id'])
+ self.shares_v2_client.wait_for_share_replica_status(
+ share_replica['id'], constants.REPLICATION_STATE_IN_SYNC,
+ status_attr='replica_state')
+
+ # Wait for snapshot1 to become available
+ self.shares_v2_client.wait_for_snapshot_status(
+ snapshot['id'], "available")
+
+ self.promote_share_replica(share_replica['id'])
+ self.delete_share_replica(original_replica['id'])
+ self.create_share(snapshot_id=snapshot['id'])
+
+ @test.attr(type=["gate", ])
+ def test_snapshot_before_and_after_share_replica(self):
+ """Test the snapshot for replicated share.
+
+ Verify that snapshot can be created before and after share replica
+ being created.
+ Verify snapshots by creating share from the snapshots.
+ """
+ share = self.create_share(share_type_id=self.share_type['id'],
+ availability_zone=self.share_zone)
+ snapshot1 = self.create_snapshot_wait_for_active(share["id"])
+
+ original_replica = self.shares_v2_client.list_share_replicas(
+ share["id"])[0]
+
+ share_replica = self.create_share_replica(share["id"],
+ self.replica_zone,
+ cleanup=False)
+ self.addCleanup(self.delete_share_replica, original_replica['id'])
+ self.shares_v2_client.wait_for_share_replica_status(
+ share_replica['id'], constants.REPLICATION_STATE_IN_SYNC,
+ status_attr='replica_state')
+
+ snapshot2 = self.create_snapshot_wait_for_active(share["id"])
+
+ # Wait for snapshot1 to become available
+ self.shares_v2_client.wait_for_snapshot_status(
+ snapshot1['id'], "available")
+
+ self.promote_share_replica(share_replica['id'])
+ # Remove the original active replica to ensure that snapshot is
+ # still being created successfully.
+ self.delete_share_replica(original_replica['id'])
+
+ self.create_share(snapshot_id=snapshot1['id'])
+ self.create_share(snapshot_id=snapshot2['id'])
+
+ @test.attr(type=["gate", ])
+ def test_delete_snapshot_after_adding_replica(self):
+ """Verify the snapshot delete.
+
+ Ensure that deleting the original snapshot also deletes the
+ snapshot from replica.
+ """
+
+ share = self.create_share(share_type_id=self.share_type['id'],
+ availability_zone=self.share_zone)
+ share_replica = self.create_share_replica(share["id"],
+ self.replica_zone)
+ self.shares_v2_client.wait_for_share_replica_status(
+ share_replica['id'], constants.REPLICATION_STATE_IN_SYNC,
+ status_attr='replica_state')
+ snapshot = self.create_snapshot_wait_for_active(share["id"])
+ self.shares_v2_client.delete_snapshot(snapshot['id'])
+ self.shares_v2_client.wait_for_resource_deletion(
+ snapshot_id=snapshot["id"])
+
+ @test.attr(type=["gate", ])
+ def test_create_replica_from_snapshot_share(self):
+ """Test replica for a share that was created from snapshot."""
+
+ share = self.create_share(share_type_id=self.share_type['id'],
+ availability_zone=self.share_zone)
+ orig_snapshot = self.create_snapshot_wait_for_active(share["id"])
+ snap_share = self.create_share(snapshot_id=orig_snapshot['id'])
+ original_replica = self.shares_v2_client.list_share_replicas(
+ snap_share["id"])[0]
+ share_replica = self.create_share_replica(snap_share["id"],
+ self.replica_zone,
+ cleanup=False)
+ self.addCleanup(self.delete_share_replica, original_replica['id'])
+ self.shares_v2_client.wait_for_share_replica_status(
+ share_replica['id'], constants.REPLICATION_STATE_IN_SYNC,
+ status_attr='replica_state')
+ self.promote_share_replica(share_replica['id'])
+ # Delete the demoted replica so promoted replica can be cleaned
+ # during the cleanup
+ self.delete_share_replica(original_replica['id'])