Add share backup tests.
Share backup tests i.e. create/delete/get share_backups. Change-Id: I366221702b3aa2e78bff82e6b2a543b6f6784f77
This commit is contained in:
parent
7604da8054
commit
edd82c7385
@ -60,6 +60,7 @@ def wait_for_resource_status(client, resource_id, status,
|
||||
'share_group': 'get_share_group',
|
||||
'share_group_snapshot': 'get_share_group_snapshot',
|
||||
'share_replica': 'get_share_replica',
|
||||
'share_backup': 'get_share_backup'
|
||||
}
|
||||
|
||||
action_name = get_resource_action[resource_name]
|
||||
|
@ -265,6 +265,9 @@ ShareGroup = [
|
||||
default=False,
|
||||
help="Enable or disable migration with "
|
||||
"preserve_snapshots tests set to True."),
|
||||
cfg.BoolOpt("run_driver_assisted_backup_tests",
|
||||
default=False,
|
||||
help="Enable or disable share backup tests."),
|
||||
cfg.BoolOpt("run_manage_unmanage_tests",
|
||||
default=False,
|
||||
help="Defines whether to run manage/unmanage tests or not. "
|
||||
@ -314,6 +317,10 @@ ShareGroup = [
|
||||
default=1500,
|
||||
help="Time to wait for share migration before "
|
||||
"timing out (seconds)."),
|
||||
cfg.IntOpt("share_backup_timeout",
|
||||
default=1500,
|
||||
help="Time to wait for share backup before "
|
||||
"timing out (seconds)."),
|
||||
cfg.IntOpt("share_server_migration_timeout",
|
||||
default="1500",
|
||||
help="Time to wait for share server migration before "
|
||||
@ -349,4 +356,7 @@ ShareGroup = [
|
||||
"writing data from /dev/zero might not yield significant "
|
||||
"space savings as these systems are already optimized for "
|
||||
"efficient compression."),
|
||||
cfg.DictOpt("driver_assisted_backup_test_driver_options",
|
||||
default={'dummy': True},
|
||||
help="Share backup driver options specified as dict."),
|
||||
]
|
||||
|
@ -323,6 +323,9 @@ class SharesClient(rest_client.RestClient):
|
||||
elif "server_id" in kwargs:
|
||||
return self._is_resource_deleted(
|
||||
self.show_share_server, kwargs.get("server_id"))
|
||||
elif "backup_id" in kwargs:
|
||||
return self._is_resource_deleted(
|
||||
self.get_share_backup, kwargs.get("backup_id"))
|
||||
else:
|
||||
raise share_exceptions.InvalidResource(
|
||||
message=str(kwargs))
|
||||
|
@ -1836,6 +1836,109 @@ class SharesV2Client(shares_client.SharesClient):
|
||||
body = json.loads(body)
|
||||
return rest_client.ResponseBody(resp, body)
|
||||
|
||||
###############
|
||||
|
||||
def get_share_backup(self, backup_id, version=LATEST_MICROVERSION):
|
||||
"""Returns the details of a single backup."""
|
||||
resp, body = self.get("share-backups/%s" % backup_id,
|
||||
headers=EXPERIMENTAL,
|
||||
extra_headers=True,
|
||||
version=version)
|
||||
self.expected_success(200, resp.status)
|
||||
body = json.loads(body)
|
||||
return rest_client.ResponseBody(resp, body)
|
||||
|
||||
def list_share_backups(self, share_id=None, version=LATEST_MICROVERSION):
|
||||
"""Get list of backups."""
|
||||
uri = "share-backups/detail"
|
||||
if share_id:
|
||||
uri += (f'?share_id={share_id}')
|
||||
resp, body = self.get(uri, headers=EXPERIMENTAL,
|
||||
extra_headers=True, version=version)
|
||||
self.expected_success(200, resp.status)
|
||||
body = json.loads(body)
|
||||
return rest_client.ResponseBody(resp, body)
|
||||
|
||||
def create_share_backup(self, share_id, name=None, description=None,
|
||||
backup_options=None, version=LATEST_MICROVERSION):
|
||||
"""Create a share backup."""
|
||||
if name is None:
|
||||
name = data_utils.rand_name("tempest-created-share-backup")
|
||||
if description is None:
|
||||
description = data_utils.rand_name(
|
||||
"tempest-created-share-backup-desc")
|
||||
post_body = {
|
||||
'share_backup': {
|
||||
'name': name,
|
||||
'description': description,
|
||||
'share_id': share_id,
|
||||
'backup_options': backup_options,
|
||||
}
|
||||
}
|
||||
body = json.dumps(post_body)
|
||||
resp, body = self.post('share-backups', body,
|
||||
headers=EXPERIMENTAL,
|
||||
extra_headers=True,
|
||||
version=version)
|
||||
|
||||
self.expected_success(202, resp.status)
|
||||
body = json.loads(body)
|
||||
return rest_client.ResponseBody(resp, body)
|
||||
|
||||
def delete_share_backup(self, backup_id, version=LATEST_MICROVERSION):
|
||||
"""Delete share backup."""
|
||||
uri = "share-backups/%s" % backup_id
|
||||
resp, body = self.delete(uri,
|
||||
headers=EXPERIMENTAL,
|
||||
extra_headers=True,
|
||||
version=version)
|
||||
self.expected_success(202, resp.status)
|
||||
return rest_client.ResponseBody(resp, body)
|
||||
|
||||
def restore_share_backup(self, backup_id, version=LATEST_MICROVERSION):
|
||||
"""Restore share backup."""
|
||||
uri = "share-backups/%s/action" % backup_id
|
||||
body = {'restore': None}
|
||||
resp, body = self.post(uri, json.dumps(body),
|
||||
headers=EXPERIMENTAL,
|
||||
extra_headers=True,
|
||||
version=version)
|
||||
self.expected_success(202, resp.status)
|
||||
body = json.loads(body)
|
||||
return rest_client.ResponseBody(resp, body)
|
||||
|
||||
def update_share_backup(self, backup_id, name=None, description=None,
|
||||
version=LATEST_MICROVERSION):
|
||||
"""Update share backup."""
|
||||
uri = "share-backups/%s" % backup_id
|
||||
post_body = {}
|
||||
if name:
|
||||
post_body['name'] = name
|
||||
if description:
|
||||
post_body['description'] = description
|
||||
|
||||
body = json.dumps({'share_backup': post_body})
|
||||
resp, body = self.put(uri, body,
|
||||
headers=EXPERIMENTAL,
|
||||
extra_headers=True,
|
||||
version=version)
|
||||
self.expected_success(200, resp.status)
|
||||
body = json.loads(body)
|
||||
return rest_client.ResponseBody(resp, body)
|
||||
|
||||
def reset_state_share_backup(self, backup_id,
|
||||
status=constants.STATUS_AVAILABLE,
|
||||
version=LATEST_MICROVERSION):
|
||||
|
||||
uri = "share-backups/%s/action" % backup_id
|
||||
body = {'reset_status': {'status': status}}
|
||||
resp, body = self.post(uri, json.dumps(body),
|
||||
headers=EXPERIMENTAL,
|
||||
extra_headers=True,
|
||||
version=LATEST_MICROVERSION)
|
||||
self.expected_success(202, resp.status)
|
||||
return rest_client.ResponseBody(resp, body)
|
||||
|
||||
################
|
||||
|
||||
def create_snapshot_access_rule(self, snapshot_id, access_type="ip",
|
||||
|
@ -86,3 +86,7 @@ class ShareServerBuildErrorException(exceptions.TempestException):
|
||||
class ShareServerMigrationException(exceptions.TempestException):
|
||||
message = ("Share server %(server_id)s failed to migrate and is in ERROR "
|
||||
"status")
|
||||
|
||||
|
||||
class ShareBackupException(exceptions.TempestException):
|
||||
message = ("Share backup %(backup_id)s failed and is in ERROR status")
|
||||
|
@ -704,6 +704,31 @@ class BaseSharesTest(test.BaseTestCase):
|
||||
resource_name='share_replica')
|
||||
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):
|
||||
@ -919,6 +944,9 @@ class BaseSharesTest(test.BaseTestCase):
|
||||
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)
|
||||
|
117
manila_tempest_tests/tests/api/test_backup.py
Normal file
117
manila_tempest_tests/tests/api/test_backup.py
Normal file
@ -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))
|
153
manila_tempest_tests/tests/api/test_backup_negative.py
Normal file
153
manila_tempest_tests/tests/api/test_backup_negative.py
Normal file
@ -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')
|
@ -626,6 +626,8 @@
|
||||
MANILA_OPTGROUP_membernet_standalone_network_plugin_mask: 24
|
||||
MANILA_OPTGROUP_membernet_standalone_network_plugin_network_type: vlan
|
||||
MANILA_OPTGROUP_membernet_standalone_network_plugin_segmentation_id: 1010
|
||||
MANILA_CREATE_BACKUP_CONTINUE_TASK_INTERVAL: 30
|
||||
MANILA_RESTORE_BACKUP_CONTINUE_TASK_INTERVAL: 30
|
||||
devstack_local_conf:
|
||||
test-config:
|
||||
"$TEMPEST_CONFIG":
|
||||
@ -639,6 +641,7 @@
|
||||
enable_user_rules_for_protocols: cifs
|
||||
multi_backend: true
|
||||
multitenancy_enabled: false
|
||||
run_driver_assisted_backup_tests: true
|
||||
run_driver_assisted_migration_tests: true
|
||||
run_manage_unmanage_snapshot_tests: true
|
||||
run_manage_unmanage_tests: true
|
||||
|
Loading…
Reference in New Issue
Block a user