Add share backup tests.
Share backup tests i.e. create/delete/get share_backups.
Change-Id: I366221702b3aa2e78bff82e6b2a543b6f6784f77
diff --git a/manila_tempest_tests/tests/api/base.py b/manila_tempest_tests/tests/api/base.py
index 18562e5..cb64ec8 100755
--- a/manila_tempest_tests/tests/api/base.py
+++ b/manila_tempest_tests/tests/api/base.py
@@ -705,6 +705,31 @@
return replica
@classmethod
+ def create_backup_wait_for_active(cls, share_id, client=None,
+ cleanup_in_class=False, cleanup=True,
+ version=CONF.share.max_api_microversion):
+ client = client or cls.shares_v2_client
+ backup_name = data_utils.rand_name('Backup')
+ backup_options = CONF.share.driver_assisted_backup_test_driver_options
+ backup = client.create_share_backup(
+ share_id,
+ name=backup_name,
+ backup_options=backup_options)['share_backup']
+ resource = {
+ "type": "share_backup",
+ "id": backup["id"],
+ "client": client,
+ }
+ if cleanup:
+ if cleanup_in_class:
+ cls.class_resources.insert(0, resource)
+ else:
+ cls.method_resources.insert(0, resource)
+ waiters.wait_for_resource_status(client, backup["id"], "available",
+ resource_name='share_backup')
+ return client.get_share_backup(backup['id'])['share_backup']
+
+ @classmethod
def delete_share_replica(cls, replica_id, client=None,
version=CONF.share.max_api_microversion):
client = client or cls.shares_v2_client
@@ -919,6 +944,9 @@
elif res["type"] == "share_replica":
client.delete_share_replica(res_id)
client.wait_for_resource_deletion(replica_id=res_id)
+ elif res["type"] == "share_backup":
+ client.delete_share_backup(res_id)
+ client.wait_for_resource_deletion(backup_id=res_id)
elif res["type"] == "share_network_subnet":
sn_id = res["extra_params"]["share_network_id"]
client.delete_subnet(sn_id, res_id)
diff --git a/manila_tempest_tests/tests/api/test_backup.py b/manila_tempest_tests/tests/api/test_backup.py
new file mode 100644
index 0000000..f443802
--- /dev/null
+++ b/manila_tempest_tests/tests/api/test_backup.py
@@ -0,0 +1,117 @@
+# Copyright 2024 Cloudification GmbH
+# 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 decorators
+from tempest.lib import exceptions as lib_exc
+from testtools import testcase as tc
+
+from manila_tempest_tests.common import waiters
+from manila_tempest_tests.tests.api import base
+from manila_tempest_tests import utils
+
+
+CONF = config.CONF
+_MIN_SUPPORTED_MICROVERSION = '2.80'
+
+
+class ShareBackupTest(base.BaseSharesMixedTest):
+
+ @classmethod
+ def skip_checks(cls):
+ super(ShareBackupTest, cls).skip_checks()
+ if not CONF.share.run_driver_assisted_backup_tests:
+ raise cls.skipException("Share backup tests are disabled.")
+ utils.check_skip_if_microversion_not_supported(
+ _MIN_SUPPORTED_MICROVERSION)
+
+ def setUp(self):
+ super(ShareBackupTest, self).setUp()
+ extra_specs = {
+ 'snapshot_support': True,
+ 'mount_snapshot_support': True,
+ }
+ share_type = self.create_share_type(extra_specs=extra_specs)
+ share = self.create_share(self.shares_v2_client.share_protocol,
+ share_type_id=share_type['id'])
+ self.share_id = share["id"]
+
+ @decorators.idempotent_id('12c36c97-faf4-4fec-9a9b-7cff0d2035cd')
+ @tc.attr(base.TAG_POSITIVE, base.TAG_BACKEND)
+ def test_create_share_backup(self):
+ backup = self.create_backup_wait_for_active(self.share_id)
+
+ # Verify backup create API response
+ expected_keys = ["id", "share_id", "status",
+ "availability_zone", "created_at", "updated_at",
+ "size", "progress", "restore_progress",
+ "name", "description"]
+
+ # Strict key check
+ actual_backup = self.shares_v2_client.get_share_backup(
+ backup['id'])['share_backup']
+ actual_keys = actual_backup.keys()
+ self.assertEqual(backup['id'], actual_backup['id'])
+ self.assertEqual(set(expected_keys), set(actual_keys))
+
+ @decorators.idempotent_id('34c36c97-faf4-4fec-9a9b-7cff0d2035cd')
+ @tc.attr(base.TAG_POSITIVE, base.TAG_BACKEND)
+ def test_delete_share_backup(self):
+ backup = self.create_backup_wait_for_active(
+ self.share_id, cleanup=False)
+
+ # Delete share backup
+ self.shares_v2_client.delete_share_backup(backup['id'])
+ self.shares_v2_client.wait_for_resource_deletion(
+ backup_id=backup['id'])
+ self.assertRaises(
+ lib_exc.NotFound,
+ self.shares_v2_client.get_share_backup,
+ backup['id'])
+
+ @decorators.idempotent_id('56c36c97-faf4-4fec-9a9b-7cff0d2035cd')
+ @tc.attr(base.TAG_POSITIVE, base.TAG_BACKEND)
+ def test_restore_share_backup(self):
+ backup = self.create_backup_wait_for_active(self.share_id)
+
+ # Restore share backup
+ restore = self.shares_v2_client.restore_share_backup(
+ backup['id'])['restore']
+ waiters.wait_for_resource_status(
+ self.shares_v2_client, backup['id'], 'available',
+ resource_name='share_backup')
+
+ self.assertEqual(restore['share_id'], self.share_id)
+
+ @decorators.idempotent_id('78c36c97-faf4-4fec-9a9b-7cff0d2035cd')
+ @tc.attr(base.TAG_POSITIVE, base.TAG_BACKEND)
+ def test_update_share_backup(self):
+ backup = self.create_backup_wait_for_active(self.share_id)
+
+ # Update share backup name
+ backup_name2 = data_utils.rand_name('Backup')
+ backup = self.shares_v2_client.update_share_backup(
+ backup['id'], name=backup_name2)['share_backup']
+ updated_backup = self.shares_v2_client.get_share_backup(
+ backup['id'])['share_backup']
+ self.assertEqual(backup_name2, updated_backup['name'])
+
+ @decorators.idempotent_id('19c36c97-faf4-4fec-9a9b-7cff0d2045af')
+ @tc.attr(base.TAG_POSITIVE, base.TAG_BACKEND)
+ def test_list_share_backups(self):
+ self.create_backup_wait_for_active(self.share_id)
+ backups = self.shares_v2_client.list_share_backups()
+ self.assertEqual(1, len(backups))
diff --git a/manila_tempest_tests/tests/api/test_backup_negative.py b/manila_tempest_tests/tests/api/test_backup_negative.py
new file mode 100644
index 0000000..743c195
--- /dev/null
+++ b/manila_tempest_tests/tests/api/test_backup_negative.py
@@ -0,0 +1,153 @@
+# Copyright 2024 Cloudification GmbH
+# 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 decorators
+from tempest.lib import exceptions as lib_exc
+from testtools import testcase as tc
+
+from manila_tempest_tests.common import waiters
+from manila_tempest_tests.tests.api import base
+from manila_tempest_tests import utils
+
+
+CONF = config.CONF
+_MIN_SUPPORTED_MICROVERSION = '2.80'
+
+
+class ShareBackupNegativeTest(base.BaseSharesMixedTest):
+
+ @classmethod
+ def skip_checks(cls):
+ super(ShareBackupNegativeTest, cls).skip_checks()
+ if not CONF.share.run_driver_assisted_backup_tests:
+ raise cls.skipException("Share backup tests are disabled.")
+ utils.check_skip_if_microversion_not_supported(
+ _MIN_SUPPORTED_MICROVERSION)
+
+ def setUp(self):
+ super(ShareBackupNegativeTest, self).setUp()
+ extra_specs = {
+ 'snapshot_support': True,
+ 'mount_snapshot_support': True,
+ }
+ share_type = self.create_share_type(extra_specs=extra_specs)
+ share = self.create_share(self.shares_v2_client.share_protocol,
+ share_type_id=share_type['id'])
+ self.share_id = share["id"]
+ self.backup_options = (
+ CONF.share.driver_assisted_backup_test_driver_options)
+
+ @decorators.idempotent_id('58c36c97-faf4-4fec-9a9b-7cff0d2035ab')
+ @tc.attr(base.TAG_NEGATIVE, base.TAG_BACKEND)
+ def test_create_backup_when_share_is_in_backup_creating_state(self):
+ backup_name1 = data_utils.rand_name('Backup')
+ backup1 = self.shares_v2_client.create_share_backup(
+ self.share_id,
+ name=backup_name1,
+ backup_options=self.backup_options)['share_backup']
+
+ # try create backup when share state is busy
+ backup_name2 = data_utils.rand_name('Backup')
+ self.assertRaises(lib_exc.BadRequest,
+ self.shares_v2_client.create_share_backup,
+ self.share_id,
+ name=backup_name2,
+ backup_options=self.backup_options)
+ waiters.wait_for_resource_status(
+ self.shares_v2_client, backup1['id'], "available",
+ resource_name='share_backup')
+
+ # delete the share backup
+ self.shares_v2_client.delete_share_backup(backup1['id'])
+ self.shares_v2_client.wait_for_resource_deletion(
+ backup_id=backup1['id'])
+
+ @decorators.idempotent_id('58c36c97-faf4-4fec-9a9b-7cff0d2012ab')
+ @tc.attr(base.TAG_NEGATIVE, base.TAG_BACKEND)
+ def test_create_backup_when_share_is_in_error_state(self):
+ self.admin_shares_v2_client.reset_state(self.share_id,
+ status='error')
+
+ # try create backup when share is not available
+ backup_name = data_utils.rand_name('Backup')
+ self.assertRaises(lib_exc.BadRequest,
+ self.shares_v2_client.create_share_backup,
+ self.share_id,
+ name=backup_name,
+ backup_options=self.backup_options)
+
+ self.admin_shares_v2_client.reset_state(self.share_id,
+ status='available')
+
+ @decorators.idempotent_id('58c36c97-faf4-4fec-9a9b-7cff0d2012de')
+ @tc.attr(base.TAG_NEGATIVE, base.TAG_BACKEND)
+ def test_create_backup_when_share_has_snapshots(self):
+ self.create_snapshot_wait_for_active(self.share_id,
+ cleanup_in_class=False)
+
+ # try create backup when share has snapshots
+ backup_name = data_utils.rand_name('Backup')
+ self.assertRaises(lib_exc.BadRequest,
+ self.shares_v2_client.create_share_backup,
+ self.share_id,
+ name=backup_name,
+ backup_options=self.backup_options)
+
+ @decorators.idempotent_id('58c12c97-faf4-4fec-9a9b-7cff0d2012de')
+ @tc.attr(base.TAG_NEGATIVE, base.TAG_BACKEND)
+ def test_delete_backup_when_backup_is_not_available(self):
+ backup = self.create_backup_wait_for_active(self.share_id)
+ self.admin_shares_v2_client.reset_state_share_backup(
+ backup['id'], status='creating')
+
+ # try delete backup when share backup is not available
+ self.assertRaises(lib_exc.BadRequest,
+ self.shares_v2_client.delete_share_backup,
+ backup['id'])
+
+ self.admin_shares_v2_client.reset_state_share_backup(
+ backup['id'], status='available')
+
+ @decorators.idempotent_id('58c56c97-faf4-4fec-9a9b-7cff0d2012de')
+ @tc.attr(base.TAG_NEGATIVE, base.TAG_BACKEND)
+ def test_restore_backup_when_share_is_not_available(self):
+ backup = self.create_backup_wait_for_active(self.share_id)
+ self.admin_shares_v2_client.reset_state(self.share_id,
+ status='error')
+
+ # try restore backup when share is not available
+ self.assertRaises(lib_exc.BadRequest,
+ self.shares_v2_client.restore_share_backup,
+ backup['id'])
+
+ self.admin_shares_v2_client.reset_state(self.share_id,
+ status='available')
+
+ @decorators.idempotent_id('58c12998-faf4-4fec-9a9b-7cff0d2012de')
+ @tc.attr(base.TAG_NEGATIVE, base.TAG_BACKEND)
+ def test_restore_backup_when_backup_is_not_available(self):
+ backup = self.create_backup_wait_for_active(self.share_id)
+ self.admin_shares_v2_client.reset_state_share_backup(
+ backup['id'], status='creating')
+
+ # try restore backup when backup is not available
+ self.assertRaises(lib_exc.BadRequest,
+ self.shares_v2_client.restore_share_backup,
+ backup['id'])
+
+ self.admin_shares_v2_client.reset_state_share_backup(
+ backup['id'], status='available')