Implement share backup
Add share backup feature. This will allow the user to create, restore and delete backups as well as listing backups and showing the details of a specific backup. Implement: blueprint share-backup Depends-On: Ice01ab7892b1eb52b3202f2c79957977f73f3aca Change-Id: I2c3848cbbeb921ede74756e25e58ef82277e0d2b
This commit is contained in:
parent
1734b45fa5
commit
5e24577904
@ -89,6 +89,19 @@ RESOURCE_LOCK_SORT_KEY_VALUES = (
|
||||
'lock_reason',
|
||||
)
|
||||
|
||||
BACKUP_SORT_KEY_VALUES = (
|
||||
'id',
|
||||
'status',
|
||||
'size',
|
||||
'share_id',
|
||||
'progress',
|
||||
'restore_progress',
|
||||
'name',
|
||||
'host',
|
||||
'topic',
|
||||
'project_id',
|
||||
)
|
||||
|
||||
TASK_STATE_MIGRATION_SUCCESS = 'migration_success'
|
||||
TASK_STATE_MIGRATION_ERROR = 'migration_error'
|
||||
TASK_STATE_MIGRATION_CANCELLED = 'migration_cancelled'
|
||||
|
416
manilaclient/osc/v2/share_backups.py
Normal file
416
manilaclient/osc/v2/share_backups.py
Normal file
@ -0,0 +1,416 @@
|
||||
# Copyright 2023 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.
|
||||
|
||||
import logging
|
||||
|
||||
from osc_lib.cli import parseractions
|
||||
from osc_lib.command import command
|
||||
from osc_lib import exceptions
|
||||
from osc_lib import utils as osc_utils
|
||||
|
||||
from manilaclient.common._i18n import _
|
||||
from manilaclient.common import constants
|
||||
from manilaclient.osc import utils
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class CreateShareBackup(command.ShowOne):
|
||||
"""Create a share backup."""
|
||||
_description = _("Create a backup of the given share")
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(CreateShareBackup, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
"share",
|
||||
metavar="<share>",
|
||||
help=_("Name or ID of the share to backup.")
|
||||
)
|
||||
parser.add_argument(
|
||||
'--name',
|
||||
metavar='<name>',
|
||||
default=None,
|
||||
help=_('Optional share backup name. (Default=None).')
|
||||
)
|
||||
parser.add_argument(
|
||||
'--description',
|
||||
metavar='<description>',
|
||||
default=None,
|
||||
help=_('Optional share backup description. (Default=None).')
|
||||
)
|
||||
parser.add_argument(
|
||||
"--backup-options",
|
||||
metavar="<key=value>",
|
||||
default={},
|
||||
action=parseractions.KeyValueAction,
|
||||
help=_("Backup driver option key=value pairs (Optional, "
|
||||
"Default=None)."),
|
||||
)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
share_client = self.app.client_manager.share
|
||||
share = osc_utils.find_resource(
|
||||
share_client.shares, parsed_args.share)
|
||||
|
||||
body = {}
|
||||
if parsed_args.backup_options:
|
||||
body['backup_options'] = utils.extract_key_value_options(
|
||||
parsed_args.backup_options)
|
||||
if parsed_args.description:
|
||||
body['description'] = parsed_args.description
|
||||
if parsed_args.name:
|
||||
body['name'] = parsed_args.name
|
||||
|
||||
share_backup = share_client.share_backups.create(share, **body)
|
||||
share_backup._info.pop('links', None)
|
||||
return self.dict2columns(share_backup._info)
|
||||
|
||||
|
||||
class DeleteShareBackup(command.Command):
|
||||
"""Delete one or more share backups."""
|
||||
_description = _("Delete one or more share backups")
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(DeleteShareBackup, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
"backup",
|
||||
metavar="<backup>",
|
||||
nargs="+",
|
||||
help=_("Name or ID of the backup(s) to delete")
|
||||
)
|
||||
parser.add_argument(
|
||||
"--wait",
|
||||
action='store_true',
|
||||
default=False,
|
||||
help=_("Wait for share backup deletion")
|
||||
)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
share_client = self.app.client_manager.share
|
||||
result = 0
|
||||
|
||||
for backup in parsed_args.backup:
|
||||
try:
|
||||
share_backup_obj = osc_utils.find_resource(
|
||||
share_client.share_backups, backup)
|
||||
share_client.share_backups.delete(share_backup_obj)
|
||||
|
||||
if parsed_args.wait:
|
||||
if not osc_utils.wait_for_delete(
|
||||
manager=share_client.share_backups,
|
||||
res_id=share_backup_obj.id):
|
||||
result += 1
|
||||
|
||||
except Exception as e:
|
||||
result += 1
|
||||
LOG.error(_(
|
||||
"Failed to delete a share backup with "
|
||||
"name or ID '%(backup)s': %(e)s"),
|
||||
{'backup': backup, 'e': e})
|
||||
|
||||
if result > 0:
|
||||
total = len(parsed_args.backup)
|
||||
msg = (_("%(result)s of %(total)s backups failed "
|
||||
"to delete.") % {'result': result, 'total': total})
|
||||
raise exceptions.CommandError(msg)
|
||||
|
||||
|
||||
class ListShareBackup(command.Lister):
|
||||
"""List share backups."""
|
||||
_description = _("List share backups")
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(ListShareBackup, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
"--share",
|
||||
metavar="<share>",
|
||||
default=None,
|
||||
help=_("Name or ID of the share to list backups for.")
|
||||
)
|
||||
parser.add_argument(
|
||||
"--name",
|
||||
metavar="<name>",
|
||||
default=None,
|
||||
help=_("Filter results by name. Default=None.")
|
||||
)
|
||||
parser.add_argument(
|
||||
'--description',
|
||||
metavar="<description>",
|
||||
default=None,
|
||||
help=_("Filter results by description. Default=None.")
|
||||
)
|
||||
parser.add_argument(
|
||||
"--name~",
|
||||
metavar="<name~>",
|
||||
default=None,
|
||||
help=_("Filter results matching a share backup name pattern. ")
|
||||
)
|
||||
parser.add_argument(
|
||||
'--description~',
|
||||
metavar="<description~>",
|
||||
default=None,
|
||||
help=_("Filter results matching a share backup description ")
|
||||
)
|
||||
parser.add_argument(
|
||||
'--status',
|
||||
metavar="<status>",
|
||||
default=None,
|
||||
help=_('Filter results by status. Default=None.')
|
||||
)
|
||||
parser.add_argument(
|
||||
"--limit",
|
||||
metavar="<num-backups>",
|
||||
type=int,
|
||||
default=None,
|
||||
action=parseractions.NonNegativeAction,
|
||||
help=_("Limit the number of backups returned. Default=None.")
|
||||
)
|
||||
parser.add_argument(
|
||||
'--offset',
|
||||
metavar="<offset>",
|
||||
default=None,
|
||||
help='Start position of backup records listing.')
|
||||
parser.add_argument(
|
||||
'--sort-key', '--sort_key',
|
||||
metavar='<sort_key>',
|
||||
type=str,
|
||||
default=None,
|
||||
help='Key to be sorted, available keys are %(keys)s. '
|
||||
'Default=None.'
|
||||
% {'keys': constants.BACKUP_SORT_KEY_VALUES})
|
||||
parser.add_argument(
|
||||
'--sort-dir', '--sort_dir',
|
||||
metavar='<sort_dir>',
|
||||
type=str,
|
||||
default=None,
|
||||
help='Sort direction, available values are %(values)s. '
|
||||
'OPTIONAL: Default=None.' % {
|
||||
'values': constants.SORT_DIR_VALUES})
|
||||
parser.add_argument(
|
||||
'--detail',
|
||||
dest='detail',
|
||||
metavar='<0|1>',
|
||||
nargs='?',
|
||||
type=int,
|
||||
const=1,
|
||||
default=0,
|
||||
help="Show detailed information about share backups.")
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
share_client = self.app.client_manager.share
|
||||
|
||||
share_id = None
|
||||
if parsed_args.share:
|
||||
share_id = osc_utils.find_resource(share_client.shares,
|
||||
parsed_args.share).id
|
||||
columns = [
|
||||
'ID',
|
||||
'Name',
|
||||
'Share ID',
|
||||
'Status'
|
||||
]
|
||||
|
||||
if parsed_args.detail:
|
||||
columns.extend(['Description', 'Size', 'Created At',
|
||||
'Updated At', 'Availability Zone', 'Progress',
|
||||
'Restore Progress', 'Host', 'Topic'])
|
||||
|
||||
search_opts = {
|
||||
'limit': parsed_args.limit,
|
||||
'offset': parsed_args.offset,
|
||||
'name': parsed_args.name,
|
||||
'description': parsed_args.description,
|
||||
'status': parsed_args.status,
|
||||
'share_id': share_id,
|
||||
}
|
||||
|
||||
search_opts['name~'] = getattr(parsed_args, 'name~')
|
||||
search_opts['description~'] = getattr(parsed_args, 'description~')
|
||||
|
||||
backups = share_client.share_backups.list(
|
||||
detailed=parsed_args.detail, search_opts=search_opts,
|
||||
sort_key=parsed_args.sort_key, sort_dir=parsed_args.sort_dir)
|
||||
|
||||
return (columns,
|
||||
(osc_utils.get_item_properties(b, columns) for b in backups))
|
||||
|
||||
|
||||
class ShowShareBackup(command.ShowOne):
|
||||
"""Show share backup."""
|
||||
_description = _("Show details of a backup")
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(ShowShareBackup, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
"backup",
|
||||
metavar="<backup>",
|
||||
help=_("ID of the share backup. ")
|
||||
)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
share_client = self.app.client_manager.share
|
||||
backup = osc_utils.find_resource(share_client.share_backups,
|
||||
parsed_args.backup)
|
||||
backup._info.pop('links', None)
|
||||
return self.dict2columns(backup._info)
|
||||
|
||||
|
||||
class RestoreShareBackup(command.Command):
|
||||
"""Restore share backup to share"""
|
||||
_description = _("Attempt to restore share backup")
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(RestoreShareBackup, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
"backup",
|
||||
metavar="<backup>",
|
||||
help=_('ID of backup to restore.')
|
||||
)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
share_client = self.app.client_manager.share
|
||||
share_backup = osc_utils.find_resource(
|
||||
share_client.share_backups,
|
||||
parsed_args.backup)
|
||||
share_client.share_backups.restore(share_backup.id)
|
||||
|
||||
|
||||
class SetShareBackup(command.Command):
|
||||
"""Set share backup properties."""
|
||||
_description = _("Set share backup properties")
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(SetShareBackup, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
"backup",
|
||||
metavar="<backup>",
|
||||
help=_('Name or ID of the backup to set a property for')
|
||||
)
|
||||
parser.add_argument(
|
||||
"--name",
|
||||
metavar="<name>",
|
||||
default=None,
|
||||
help=_("Set a name to the backup.")
|
||||
)
|
||||
parser.add_argument(
|
||||
"--description",
|
||||
metavar="<description>",
|
||||
default=None,
|
||||
help=_("Set a description to the backup.")
|
||||
)
|
||||
parser.add_argument(
|
||||
"--status",
|
||||
metavar="<status>",
|
||||
choices=['available', 'error', 'creating', 'deleting',
|
||||
'restoring'],
|
||||
help=_("Assign a status to the backup(Admin only). "
|
||||
"Options include : available, error, creating, "
|
||||
"deleting, restoring.")
|
||||
)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
share_client = self.app.client_manager.share
|
||||
result = 0
|
||||
|
||||
share_backup = osc_utils.find_resource(
|
||||
share_client.share_backups,
|
||||
parsed_args.backup)
|
||||
|
||||
kwargs = {}
|
||||
|
||||
if parsed_args.name is not None:
|
||||
kwargs['name'] = parsed_args.name
|
||||
if parsed_args.description is not None:
|
||||
kwargs['description'] = parsed_args.description
|
||||
|
||||
try:
|
||||
share_client.share_backups.update(share_backup, **kwargs)
|
||||
except Exception as e:
|
||||
result += 1
|
||||
LOG.error(_(
|
||||
"Failed to set share backup properties "
|
||||
"'%(properties)s': %(exception)s"),
|
||||
{'properties': kwargs,
|
||||
'exception': e})
|
||||
|
||||
if parsed_args.status:
|
||||
try:
|
||||
share_client.share_backups.reset_status(
|
||||
share_backup,
|
||||
parsed_args.status
|
||||
)
|
||||
except Exception as e:
|
||||
result += 1
|
||||
LOG.error(_(
|
||||
"Failed to update backup status to "
|
||||
"'%(status)s': %(e)s"),
|
||||
{'status': parsed_args.status, 'e': e})
|
||||
if result > 0:
|
||||
raise exceptions.CommandError(_("One or more of the "
|
||||
"set operations failed"))
|
||||
|
||||
|
||||
class UnsetShareBackup(command.Command):
|
||||
"""Unset share backup properties."""
|
||||
_description = _("Unset share backup properties")
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(UnsetShareBackup, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
"backup",
|
||||
metavar="<backup>",
|
||||
help=_('Name or ID of the backup to unset a property for')
|
||||
)
|
||||
parser.add_argument(
|
||||
"--name",
|
||||
action='store_true',
|
||||
help=_("Unset a name to the backup.")
|
||||
)
|
||||
parser.add_argument(
|
||||
"--description",
|
||||
action='store_true',
|
||||
help=_("Unset a description to the backup.")
|
||||
)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
share_client = self.app.client_manager.share
|
||||
share_backup = osc_utils.find_resource(
|
||||
share_client.share_backups,
|
||||
parsed_args.backup)
|
||||
|
||||
kwargs = {}
|
||||
if parsed_args.name:
|
||||
kwargs['name'] = None
|
||||
if parsed_args.description:
|
||||
kwargs['description'] = None
|
||||
if not kwargs:
|
||||
msg = "Either name or description must be provided."
|
||||
raise exceptions.CommandError(msg)
|
||||
|
||||
try:
|
||||
share_client.share_backups.update(share_backup, **kwargs)
|
||||
except Exception as e:
|
||||
LOG.error(_(
|
||||
"Failed to unset share backup properties "
|
||||
"'%(properties)s': %(exception)s"),
|
||||
{'properties': kwargs,
|
||||
'exception': e})
|
@ -434,3 +434,31 @@ class OSCClientTestBase(base.ClientTestBase):
|
||||
'share lock delete %s' % lock['id'],
|
||||
client=client)
|
||||
return lock
|
||||
|
||||
def create_backup(self, share_id, name=None, description=None,
|
||||
backup_options=None, add_cleanup=True):
|
||||
|
||||
name = name or data_utils.rand_name('autotest_backup_name')
|
||||
|
||||
cmd = (f'backup create {share_id} ')
|
||||
|
||||
if name:
|
||||
cmd += f' --name {name}'
|
||||
if description:
|
||||
cmd += f' --description {description}'
|
||||
if backup_options:
|
||||
options = ' --backup-options'
|
||||
for key, value in backup_options.items():
|
||||
options += f' {key}={value}'
|
||||
cmd += options
|
||||
|
||||
backup_object = self.dict_result('share', cmd)
|
||||
self._wait_for_object_status(
|
||||
'share backup', backup_object['id'], 'available')
|
||||
|
||||
if add_cleanup:
|
||||
self.addCleanup(
|
||||
self.openstack,
|
||||
f'share backup delete {backup_object["id"]} --wait')
|
||||
|
||||
return backup_object
|
||||
|
152
manilaclient/tests/functional/osc/test_share_backups.py
Normal file
152
manilaclient/tests/functional/osc/test_share_backups.py
Normal file
@ -0,0 +1,152 @@
|
||||
# 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.
|
||||
|
||||
import json
|
||||
|
||||
from manilaclient.tests.functional.osc import base
|
||||
|
||||
|
||||
class ShareBackupCLITest(base.OSCClientTestBase):
|
||||
"""Functional tests for share backup."""
|
||||
|
||||
def test_share_backup_create(self):
|
||||
share = self.create_share()
|
||||
|
||||
backup = self.create_backup(
|
||||
share_id=share['id'],
|
||||
name='test_backup_create',
|
||||
description='Description',
|
||||
backup_options={'dummy': True})
|
||||
|
||||
# fetch latest after periodic callback updates status
|
||||
backup = json.loads(self.openstack(
|
||||
f'share backup show -f json {backup["id"]}'))
|
||||
|
||||
self.assertEqual(share["id"], backup["share_id"])
|
||||
self.assertEqual('test_backup_create', backup["name"])
|
||||
self.assertEqual('Description', backup["description"])
|
||||
self.assertEqual('available', backup["status"])
|
||||
|
||||
backups_list = self.listing_result('share backup', 'list')
|
||||
self.assertIn(backup['id'],
|
||||
[item['ID'] for item in backups_list])
|
||||
|
||||
def test_share_backup_delete(self):
|
||||
share = self.create_share()
|
||||
|
||||
backup = self.create_backup(
|
||||
share_id=share['id'],
|
||||
backup_options={'dummy': True},
|
||||
add_cleanup=False)
|
||||
|
||||
self.openstack(
|
||||
f'share backup delete {backup["id"]} --wait')
|
||||
|
||||
self.check_object_deleted('share backup', backup["id"])
|
||||
|
||||
def test_share_backup_show(self):
|
||||
share = self.create_share()
|
||||
|
||||
backup = self.create_backup(
|
||||
share_id=share['id'],
|
||||
name='test_backup_show',
|
||||
description='Description',
|
||||
backup_options={'dummy': True})
|
||||
|
||||
show_result = self.dict_result(
|
||||
'share backup', f'show {backup["id"]}')
|
||||
|
||||
self.assertEqual(backup["id"], show_result["id"])
|
||||
self.assertEqual('test_backup_show', show_result["name"])
|
||||
self.assertEqual('Description', show_result["description"])
|
||||
|
||||
def test_share_backup_set(self):
|
||||
share = self.create_share()
|
||||
|
||||
backup = self.create_backup(share_id=share['id'],
|
||||
backup_options={'dummy': True})
|
||||
|
||||
self.openstack(
|
||||
f'share backup set {backup["id"]} '
|
||||
f'--name test_backup_set --description Description')
|
||||
|
||||
show_result = self.dict_result(
|
||||
'share backup ', f'show {backup["id"]}')
|
||||
|
||||
self.assertEqual(backup['id'], show_result["id"])
|
||||
self.assertEqual('test_backup_set', show_result["name"])
|
||||
self.assertEqual('Description', show_result["description"])
|
||||
|
||||
def test_share_backup_unset(self):
|
||||
share = self.create_share()
|
||||
|
||||
backup = self.create_backup(
|
||||
share_id=share['id'],
|
||||
name='test_backup_unset',
|
||||
description='Description',
|
||||
backup_options={'dummy': True})
|
||||
|
||||
self.openstack(
|
||||
f'share backup unset {backup["id"]} --name --description')
|
||||
|
||||
show_result = json.loads(self.openstack(
|
||||
f'share backup show -f json {backup["id"]}'))
|
||||
|
||||
self.assertEqual(backup['id'], show_result["id"])
|
||||
self.assertIsNone(show_result["name"])
|
||||
self.assertIsNone(show_result["description"])
|
||||
|
||||
def test_share_backup_list(self):
|
||||
share_1 = self.create_share()
|
||||
share_2 = self.create_share()
|
||||
|
||||
backup_1 = self.create_backup(share_id=share_1['id'],
|
||||
backup_options={'dummy': True})
|
||||
backup_2 = self.create_backup(share_id=share_2['id'],
|
||||
backup_options={'dummy': True})
|
||||
|
||||
backups_list = self.listing_result(
|
||||
'share backup', f'list --name {backup_2["name"]} '
|
||||
)
|
||||
|
||||
self.assertTableStruct(backups_list, [
|
||||
'ID',
|
||||
'Name',
|
||||
'Share ID',
|
||||
'Status'
|
||||
])
|
||||
self.assertEqual(1, len(backups_list))
|
||||
self.assertIn(backup_2['id'],
|
||||
[item['ID'] for item in backups_list])
|
||||
|
||||
backups_list = self.listing_result(
|
||||
'share backup', f'list --share {share_1["id"]} --detail'
|
||||
)
|
||||
|
||||
self.assertTableStruct(backups_list, [
|
||||
'ID',
|
||||
'Name',
|
||||
'Share ID',
|
||||
'Status',
|
||||
'Description',
|
||||
'Availability Zone',
|
||||
'Created At',
|
||||
'Updated At',
|
||||
'Size',
|
||||
'Progress',
|
||||
'Restore Progress',
|
||||
'Host',
|
||||
'Topic',
|
||||
])
|
||||
self.assertEqual(1, len(backups_list))
|
||||
self.assertIn(backup_1['id'],
|
||||
[item['ID'] for item in backups_list])
|
@ -37,6 +37,7 @@ class FakeShareClient(object):
|
||||
self.share_type_access = mock.Mock()
|
||||
self.quotas = mock.Mock()
|
||||
self.quota_classes = mock.Mock()
|
||||
self.share_backups = mock.Mock()
|
||||
self.share_snapshots = mock.Mock()
|
||||
self.share_group_snapshots = mock.Mock()
|
||||
self.share_snapshot_export_locations = mock.Mock()
|
||||
@ -1580,3 +1581,61 @@ class FakeResourceLock(object):
|
||||
FakeResourceLock.create_one_lock(attrs))
|
||||
|
||||
return resource_locks
|
||||
|
||||
|
||||
class FakeShareBackup(object):
|
||||
"""Fake a share Backup"""
|
||||
|
||||
@staticmethod
|
||||
def create_one_backup(attrs=None, methods=None):
|
||||
"""Create a fake share backup
|
||||
|
||||
:param Dictionary attrs:
|
||||
A dictionary with all attributes
|
||||
:return:
|
||||
A FakeResource object, with project_id, resource and so on
|
||||
"""
|
||||
|
||||
attrs = attrs or {}
|
||||
methods = methods or {}
|
||||
|
||||
share_backup = {
|
||||
'id': 'backup-id-' + uuid.uuid4().hex,
|
||||
'share_id': 'share-id-' + uuid.uuid4().hex,
|
||||
'status': None,
|
||||
'name': None,
|
||||
'description': None,
|
||||
'size': '0',
|
||||
'created_at': datetime.datetime.now().isoformat(),
|
||||
'updated_at': datetime.datetime.now().isoformat(),
|
||||
'availability_zone': None,
|
||||
'progress': None,
|
||||
'restore_progress': None,
|
||||
'host': None,
|
||||
'topic': None,
|
||||
}
|
||||
|
||||
share_backup.update(attrs)
|
||||
share_backup = osc_fakes.FakeResource(info=copy.deepcopy(
|
||||
share_backup),
|
||||
methods=methods,
|
||||
loaded=True)
|
||||
return share_backup
|
||||
|
||||
@staticmethod
|
||||
def create_share_backups(attrs=None, count=2):
|
||||
"""Create multiple fake backups.
|
||||
|
||||
:param Dictionary attrs:
|
||||
A dictionary with all attributes
|
||||
:param Integer count:
|
||||
The number of share backups to be faked
|
||||
:return:
|
||||
A list of FakeResource objects
|
||||
"""
|
||||
|
||||
share_backups = []
|
||||
for n in range(0, count):
|
||||
share_backups.append(
|
||||
FakeShareBackup.create_one_backup(attrs))
|
||||
return share_backups
|
||||
|
420
manilaclient/tests/unit/osc/v2/test_share_backups.py
Normal file
420
manilaclient/tests/unit/osc/v2/test_share_backups.py
Normal file
@ -0,0 +1,420 @@
|
||||
# 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 osc_lib import exceptions
|
||||
from osc_lib import utils as oscutils
|
||||
|
||||
from manilaclient import api_versions
|
||||
from manilaclient.api_versions import MAX_VERSION
|
||||
from manilaclient.osc.v2 import share_backups as osc_share_backups
|
||||
from manilaclient.tests.unit.osc import osc_utils
|
||||
from manilaclient.tests.unit.osc.v2 import fakes as manila_fakes
|
||||
|
||||
|
||||
class TestShareBackup(manila_fakes.TestShare):
|
||||
|
||||
def setUp(self):
|
||||
super(TestShareBackup, self).setUp()
|
||||
|
||||
self.shares_mock = self.app.client_manager.share.shares
|
||||
self.shares_mock.reset_mock()
|
||||
|
||||
self.backups_mock = self.app.client_manager.share.share_backups
|
||||
self.backups_mock.reset_mock()
|
||||
self.app.client_manager.share.api_version = api_versions.APIVersion(
|
||||
MAX_VERSION)
|
||||
|
||||
|
||||
class TestShareBackupCreate(TestShareBackup):
|
||||
|
||||
def setUp(self):
|
||||
super(TestShareBackupCreate, self).setUp()
|
||||
|
||||
self.share = manila_fakes.FakeShare.create_one_share()
|
||||
self.shares_mock.get.return_value = self.share
|
||||
|
||||
self.share_backup = (
|
||||
manila_fakes.FakeShareBackup.create_one_backup(
|
||||
attrs={'status': 'available'}
|
||||
))
|
||||
self.backups_mock.create.return_value = self.share_backup
|
||||
self.backups_mock.get.return_value = self.share_backup
|
||||
self.cmd = osc_share_backups.CreateShareBackup(self.app, None)
|
||||
self.data = tuple(self.share_backup._info.values())
|
||||
self.columns = tuple(self.share_backup._info.keys())
|
||||
|
||||
def test_share_backup_create_missing_args(self):
|
||||
arglist = []
|
||||
verifylist = []
|
||||
|
||||
self.assertRaises(
|
||||
osc_utils.ParserException,
|
||||
self.check_parser, self.cmd, arglist, verifylist)
|
||||
|
||||
def test_share_backup_create(self):
|
||||
arglist = [
|
||||
self.share.id
|
||||
]
|
||||
verifylist = [
|
||||
('share', self.share.id)
|
||||
]
|
||||
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
columns, data = self.cmd.take_action(parsed_args)
|
||||
self.backups_mock.create.assert_called_with(
|
||||
self.share,
|
||||
)
|
||||
|
||||
self.assertCountEqual(self.columns, columns)
|
||||
self.assertCountEqual(self.data, data)
|
||||
|
||||
def test_share_backup_create_name(self):
|
||||
arglist = [
|
||||
self.share.id,
|
||||
'--name', "FAKE_SHARE_BACKUP_NAME"
|
||||
]
|
||||
verifylist = [
|
||||
('share', self.share.id),
|
||||
('name', "FAKE_SHARE_BACKUP_NAME")
|
||||
]
|
||||
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
columns, data = self.cmd.take_action(parsed_args)
|
||||
self.backups_mock.create.assert_called_with(
|
||||
self.share,
|
||||
name="FAKE_SHARE_BACKUP_NAME",
|
||||
)
|
||||
|
||||
self.assertCountEqual(self.columns, columns)
|
||||
self.assertCountEqual(self.data, data)
|
||||
|
||||
|
||||
class TestShareBackupDelete(TestShareBackup):
|
||||
|
||||
def setUp(self):
|
||||
super(TestShareBackupDelete, self).setUp()
|
||||
|
||||
self.share_backup = (
|
||||
manila_fakes.FakeShareBackup.create_one_backup())
|
||||
self.backups_mock.get.return_value = self.share_backup
|
||||
|
||||
self.cmd = osc_share_backups.DeleteShareBackup(self.app, None)
|
||||
|
||||
def test_share_backup_delete_missing_args(self):
|
||||
arglist = []
|
||||
verifylist = []
|
||||
|
||||
self.assertRaises(osc_utils.ParserException,
|
||||
self.check_parser, self.cmd, arglist, verifylist)
|
||||
|
||||
def test_share_backup_delete(self):
|
||||
arglist = [
|
||||
self.share_backup.id
|
||||
]
|
||||
verifylist = [
|
||||
('backup', [self.share_backup.id])
|
||||
]
|
||||
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
result = self.cmd.take_action(parsed_args)
|
||||
self.backups_mock.delete.assert_called_with(self.share_backup)
|
||||
self.assertIsNone(result)
|
||||
|
||||
def test_share_backup_delete_multiple(self):
|
||||
share_backups = (
|
||||
manila_fakes.FakeShareBackup.create_share_backups(
|
||||
count=2))
|
||||
arglist = [
|
||||
share_backups[0].id,
|
||||
share_backups[1].id
|
||||
]
|
||||
verifylist = [
|
||||
('backup', [share_backups[0].id, share_backups[1].id])
|
||||
]
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
result = self.cmd.take_action(parsed_args)
|
||||
self.assertEqual(self.backups_mock.delete.call_count,
|
||||
len(share_backups))
|
||||
self.assertIsNone(result)
|
||||
|
||||
def test_share_backup_delete_exception(self):
|
||||
arglist = [
|
||||
self.share_backup.id
|
||||
]
|
||||
verifylist = [
|
||||
('backup', [self.share_backup.id])
|
||||
]
|
||||
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
self.backups_mock.delete.side_effect = exceptions.CommandError()
|
||||
self.assertRaises(exceptions.CommandError,
|
||||
self.cmd.take_action,
|
||||
parsed_args)
|
||||
|
||||
|
||||
class TestShareBackupList(TestShareBackup):
|
||||
|
||||
columns = [
|
||||
'ID',
|
||||
'Name',
|
||||
'Share ID',
|
||||
'Status',
|
||||
]
|
||||
detailed_columns = [
|
||||
'ID',
|
||||
'Name',
|
||||
'Share ID',
|
||||
'Status',
|
||||
'Description',
|
||||
'Size',
|
||||
'Created At',
|
||||
'Updated At',
|
||||
'Availability Zone',
|
||||
'Progress',
|
||||
'Restore Progress',
|
||||
'Host',
|
||||
'Topic',
|
||||
]
|
||||
|
||||
def setUp(self):
|
||||
super(TestShareBackupList, self).setUp()
|
||||
|
||||
self.share = manila_fakes.FakeShare.create_one_share()
|
||||
self.shares_mock.get.return_value = self.share
|
||||
self.backups_list = (
|
||||
manila_fakes.FakeShareBackup.create_share_backups(
|
||||
count=2))
|
||||
self.backups_mock.list.return_value = self.backups_list
|
||||
self.values = (oscutils.get_dict_properties(
|
||||
i._info, self.columns) for i in self.backups_list)
|
||||
self.detailed_values = (oscutils.get_dict_properties(
|
||||
i._info, self.detailed_columns) for i in self.backups_list)
|
||||
|
||||
self.cmd = osc_share_backups.ListShareBackup(self.app, None)
|
||||
|
||||
def test_share_backup_list(self):
|
||||
arglist = []
|
||||
verifylist = []
|
||||
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
columns, data = self.cmd.take_action(parsed_args)
|
||||
self.backups_mock.list.assert_called_with(
|
||||
detailed=0,
|
||||
search_opts={
|
||||
'offset': None, 'limit': None, 'name': None,
|
||||
'description': None, 'name~': None, 'description~': None,
|
||||
'status': None, 'share_id': None
|
||||
},
|
||||
sort_key=None, sort_dir=None
|
||||
)
|
||||
self.assertEqual(self.columns, columns)
|
||||
self.assertEqual(list(self.values), list(data))
|
||||
|
||||
def test_share_backup_list_detail(self):
|
||||
arglist = [
|
||||
'--detail'
|
||||
]
|
||||
verifylist = [
|
||||
('detail', True)
|
||||
]
|
||||
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
columns, data = self.cmd.take_action(parsed_args)
|
||||
self.backups_mock.list.assert_called_with(
|
||||
detailed=1,
|
||||
search_opts={
|
||||
'offset': None, 'limit': None, 'name': None,
|
||||
'description': None, 'name~': None, 'description~': None,
|
||||
'status': None, 'share_id': None
|
||||
},
|
||||
sort_key=None, sort_dir=None
|
||||
)
|
||||
self.assertEqual(self.detailed_columns, columns)
|
||||
self.assertEqual(list(self.detailed_values), list(data))
|
||||
|
||||
def test_share_backup_list_for_share(self):
|
||||
arglist = [
|
||||
'--share', self.share.id
|
||||
]
|
||||
verifylist = [
|
||||
('share', self.share.id)
|
||||
]
|
||||
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
columns, data = self.cmd.take_action(parsed_args)
|
||||
self.backups_mock.list.assert_called_with(
|
||||
detailed=0,
|
||||
search_opts={
|
||||
'offset': None, 'limit': None, 'name': None,
|
||||
'description': None, 'name~': None, 'description~': None,
|
||||
'status': None, 'share_id': self.share.id
|
||||
},
|
||||
sort_key=None, sort_dir=None
|
||||
)
|
||||
self.assertEqual(self.columns, columns)
|
||||
self.assertEqual(list(self.values), list(data))
|
||||
|
||||
|
||||
class TestShareBackupShow(TestShareBackup):
|
||||
|
||||
def setUp(self):
|
||||
super(TestShareBackupShow, self).setUp()
|
||||
self.share_backup = (
|
||||
manila_fakes.FakeShareBackup.create_one_backup()
|
||||
)
|
||||
self.backups_mock.get.return_value = self.share_backup
|
||||
self.cmd = osc_share_backups.ShowShareBackup(self.app, None)
|
||||
self.data = tuple(self.share_backup._info.values())
|
||||
self.columns = tuple(self.share_backup._info.keys())
|
||||
|
||||
def test_share_backup_show_missing_args(self):
|
||||
arglist = []
|
||||
verifylist = []
|
||||
|
||||
self.assertRaises(
|
||||
osc_utils.ParserException,
|
||||
self.check_parser, self.cmd, arglist, verifylist)
|
||||
|
||||
def test_share_backup_show(self):
|
||||
arglist = [
|
||||
self.share_backup.id
|
||||
]
|
||||
verifylist = [
|
||||
('backup', self.share_backup.id)
|
||||
]
|
||||
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
columns, data = self.cmd.take_action(parsed_args)
|
||||
self.backups_mock.get.assert_called_with(
|
||||
self.share_backup.id
|
||||
)
|
||||
|
||||
self.assertCountEqual(self.columns, columns)
|
||||
self.assertCountEqual(self.data, data)
|
||||
|
||||
|
||||
class TestShareBackupRestore(TestShareBackup):
|
||||
|
||||
def setUp(self):
|
||||
super(TestShareBackupRestore, self).setUp()
|
||||
self.share_backup = (
|
||||
manila_fakes.FakeShareBackup.create_one_backup()
|
||||
)
|
||||
self.backups_mock.get.return_value = self.share_backup
|
||||
self.cmd = osc_share_backups.RestoreShareBackup(
|
||||
self.app, None)
|
||||
|
||||
def test_share_backup_restore(self):
|
||||
arglist = [
|
||||
self.share_backup.id,
|
||||
]
|
||||
verifylist = [
|
||||
('backup', self.share_backup.id),
|
||||
]
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
result = self.cmd.take_action(parsed_args)
|
||||
self.backups_mock.restore.assert_called_with(self.share_backup.id)
|
||||
self.assertIsNone(result)
|
||||
|
||||
|
||||
class TestShareBackupSet(TestShareBackup):
|
||||
|
||||
def setUp(self):
|
||||
super(TestShareBackupSet, self).setUp()
|
||||
self.share_backup = (
|
||||
manila_fakes.FakeShareBackup.create_one_backup()
|
||||
)
|
||||
self.backups_mock.get.return_value = self.share_backup
|
||||
self.cmd = osc_share_backups.SetShareBackup(self.app, None)
|
||||
|
||||
def test_set_share_backup_name(self):
|
||||
arglist = [
|
||||
self.share_backup.id,
|
||||
'--name', "FAKE_SHARE_BACKUP_NAME"
|
||||
]
|
||||
verifylist = [
|
||||
('backup', self.share_backup.id),
|
||||
('name', "FAKE_SHARE_BACKUP_NAME")
|
||||
]
|
||||
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
result = self.cmd.take_action(parsed_args)
|
||||
self.backups_mock.update.assert_called_with(self.share_backup,
|
||||
name=parsed_args.name)
|
||||
self.assertIsNone(result)
|
||||
|
||||
def test_set_backup_status(self):
|
||||
arglist = [
|
||||
self.share_backup.id,
|
||||
'--status', 'available'
|
||||
]
|
||||
verifylist = [
|
||||
('backup', self.share_backup.id),
|
||||
('status', 'available')
|
||||
]
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
|
||||
result = self.cmd.take_action(parsed_args)
|
||||
|
||||
self.backups_mock.reset_status.assert_called_with(
|
||||
self.share_backup,
|
||||
parsed_args.status)
|
||||
self.assertIsNone(result)
|
||||
|
||||
|
||||
class TestShareBackupUnset(TestShareBackup):
|
||||
|
||||
def setUp(self):
|
||||
super(TestShareBackupUnset, self).setUp()
|
||||
|
||||
self.share_backup = (
|
||||
manila_fakes.FakeShareBackup.create_one_backup()
|
||||
)
|
||||
|
||||
self.backups_mock.get.return_value = self.share_backup
|
||||
self.cmd = osc_share_backups.UnsetShareBackup(self.app, None)
|
||||
|
||||
def test_unset_backup_name(self):
|
||||
arglist = [
|
||||
self.share_backup.id,
|
||||
'--name'
|
||||
]
|
||||
verifylist = [
|
||||
('backup', self.share_backup.id),
|
||||
('name', True)
|
||||
]
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
|
||||
result = self.cmd.take_action(parsed_args)
|
||||
|
||||
self.backups_mock.update.assert_called_with(
|
||||
self.share_backup,
|
||||
name=None)
|
||||
self.assertIsNone(result)
|
||||
|
||||
def test_unset_backup_description(self):
|
||||
arglist = [
|
||||
self.share_backup.id,
|
||||
'--description'
|
||||
]
|
||||
verifylist = [
|
||||
('backup', self.share_backup.id),
|
||||
('description', True)
|
||||
]
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
|
||||
result = self.cmd.take_action(parsed_args)
|
||||
|
||||
self.backups_mock.update.assert_called_with(
|
||||
self.share_backup,
|
||||
description=None)
|
||||
self.assertIsNone(result)
|
91
manilaclient/tests/unit/v2/test_share_backups.py
Normal file
91
manilaclient/tests/unit/v2/test_share_backups.py
Normal file
@ -0,0 +1,91 @@
|
||||
# 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 unittest import mock
|
||||
|
||||
import ddt
|
||||
|
||||
from manilaclient import api_versions
|
||||
from manilaclient.tests.unit import utils
|
||||
from manilaclient.tests.unit.v2 import fakes
|
||||
from manilaclient.v2 import share_backups
|
||||
|
||||
FAKE_BACKUP = 'fake_backup'
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
class ShareBackupsTest(utils.TestCase):
|
||||
|
||||
class _FakeShareBackup(object):
|
||||
id = 'fake_share_backup_id'
|
||||
|
||||
def setUp(self):
|
||||
super(ShareBackupsTest, self).setUp()
|
||||
microversion = api_versions.APIVersion("2.80")
|
||||
self.manager = share_backups.ShareBackupManager(
|
||||
fakes.FakeClient(api_version=microversion))
|
||||
|
||||
def test_delete_str(self):
|
||||
with mock.patch.object(self.manager, '_delete', mock.Mock()):
|
||||
self.manager.delete(FAKE_BACKUP)
|
||||
self.manager._delete.assert_called_once_with(
|
||||
share_backups.RESOURCE_PATH % FAKE_BACKUP)
|
||||
|
||||
def test_delete_obj(self):
|
||||
backup = self._FakeShareBackup
|
||||
with mock.patch.object(self.manager, '_delete', mock.Mock()):
|
||||
self.manager.delete(backup)
|
||||
self.manager._delete.assert_called_once_with(
|
||||
share_backups.RESOURCE_PATH % backup.id)
|
||||
|
||||
def test_get(self):
|
||||
with mock.patch.object(self.manager, '_get', mock.Mock()):
|
||||
self.manager.get(FAKE_BACKUP)
|
||||
self.manager._get.assert_called_once_with(
|
||||
share_backups.RESOURCE_PATH % FAKE_BACKUP,
|
||||
share_backups.RESOURCE_NAME)
|
||||
|
||||
def test_restore(self):
|
||||
with mock.patch.object(self.manager, '_action', mock.Mock()):
|
||||
self.manager.restore(FAKE_BACKUP)
|
||||
self.manager._action.assert_called_once_with(
|
||||
'restore', FAKE_BACKUP)
|
||||
|
||||
def test_list(self):
|
||||
with mock.patch.object(self.manager, '_list', mock.Mock()):
|
||||
self.manager.list()
|
||||
self.manager._list.assert_called_once_with(
|
||||
share_backups.RESOURCES_PATH + '/detail',
|
||||
share_backups.RESOURCES_NAME)
|
||||
|
||||
def test_list_with_share(self):
|
||||
with mock.patch.object(self.manager, '_list', mock.Mock()):
|
||||
self.manager.list(search_opts={'share_id': 'fake_share_id'})
|
||||
share_uri = '?share_id=fake_share_id'
|
||||
self.manager._list.assert_called_once_with(
|
||||
(share_backups.RESOURCES_PATH + '/detail' + share_uri),
|
||||
share_backups.RESOURCES_NAME)
|
||||
|
||||
def test_reset_state(self):
|
||||
with mock.patch.object(self.manager, '_action', mock.Mock()):
|
||||
self.manager.reset_status(FAKE_BACKUP, 'fake_status')
|
||||
self.manager._action.assert_called_once_with(
|
||||
'reset_status', FAKE_BACKUP, {'status': 'fake_status'})
|
||||
|
||||
def test_update(self):
|
||||
backup = self._FakeShareBackup
|
||||
with mock.patch.object(self.manager, '_update', mock.Mock()):
|
||||
data = dict(name='backup1')
|
||||
self.manager.update(backup, **data)
|
||||
self.manager._update.assert_called_once_with(
|
||||
share_backups.RESOURCE_PATH % backup.id,
|
||||
{'share_backup': data})
|
@ -28,6 +28,7 @@ from manilaclient.v2 import scheduler_stats
|
||||
from manilaclient.v2 import security_services
|
||||
from manilaclient.v2 import services
|
||||
from manilaclient.v2 import share_access_rules
|
||||
from manilaclient.v2 import share_backups
|
||||
from manilaclient.v2 import share_export_locations
|
||||
from manilaclient.v2 import share_group_snapshots
|
||||
from manilaclient.v2 import share_group_type_access
|
||||
@ -237,6 +238,7 @@ class Client(object):
|
||||
self.pools = scheduler_stats.PoolManager(self)
|
||||
self.share_access_rules = (
|
||||
share_access_rules.ShareAccessRuleManager(self))
|
||||
self.share_backups = share_backups.ShareBackupManager(self)
|
||||
|
||||
self._load_extensions(extensions)
|
||||
|
||||
|
137
manilaclient/v2/share_backups.py
Normal file
137
manilaclient/v2/share_backups.py
Normal file
@ -0,0 +1,137 @@
|
||||
# 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 manilaclient import api_versions
|
||||
from manilaclient import base
|
||||
from manilaclient.common import constants
|
||||
|
||||
RESOURCES_NAME = 'share_backups'
|
||||
RESOURCE_NAME = 'share_backup'
|
||||
RESOURCES_PATH = '/share-backups'
|
||||
RESOURCE_PATH = '/share-backups/%s'
|
||||
RESOURCE_PATH_ACTION = '/share-backups/%s/action'
|
||||
|
||||
|
||||
class ShareBackup(base.Resource):
|
||||
def __repr__(self):
|
||||
return "<Share Backup: %s>" % self.id
|
||||
|
||||
|
||||
class ShareBackupManager(base.ManagerWithFind):
|
||||
"""Manage :class:`ShareBackup` resources."""
|
||||
resource_class = ShareBackup
|
||||
|
||||
@api_versions.wraps("2.80")
|
||||
@api_versions.experimental_api
|
||||
def get(self, backup):
|
||||
"""Get a share backup.
|
||||
|
||||
:param backup: either backup object or its UUID.
|
||||
:rtype: :class:`ShareBackup`
|
||||
"""
|
||||
backup_id = base.getid(backup)
|
||||
return self._get(RESOURCE_PATH % backup_id, RESOURCE_NAME)
|
||||
|
||||
@api_versions.wraps("2.80")
|
||||
@api_versions.experimental_api
|
||||
def list(self, detailed=True, search_opts=None, sort_key=None,
|
||||
sort_dir=None):
|
||||
"""List all share backups or list backups belonging to a share.
|
||||
|
||||
:param detailed: list backups with detailed fields.
|
||||
:param search_opts: Search options to filter out shares.
|
||||
:param sort_key: Key to be sorted.
|
||||
:param sort_dir: Sort direction, should be 'desc' or 'asc'.
|
||||
:rtype: list of :class:`ShareBackup`
|
||||
"""
|
||||
|
||||
search_opts = search_opts or {}
|
||||
|
||||
if sort_key is not None:
|
||||
if sort_key in constants.BACKUP_SORT_KEY_VALUES:
|
||||
search_opts['sort_key'] = sort_key
|
||||
else:
|
||||
raise ValueError(
|
||||
'sort_key must be one of the following: %s.'
|
||||
% ', '.join(constants.BACKUP_SORT_KEY_VALUES))
|
||||
|
||||
if sort_dir is not None:
|
||||
if sort_dir in constants.SORT_DIR_VALUES:
|
||||
search_opts['sort_dir'] = sort_dir
|
||||
else:
|
||||
raise ValueError(
|
||||
'sort_dir must be one of the following: %s.'
|
||||
% ', '.join(constants.SORT_DIR_VALUES))
|
||||
|
||||
query_string = self._build_query_string(search_opts)
|
||||
if detailed:
|
||||
path = "/share-backups/detail%s" % (query_string,)
|
||||
else:
|
||||
path = "/share-backups%s" % (query_string,)
|
||||
|
||||
return self._list(path, 'share_backups')
|
||||
|
||||
@api_versions.wraps("2.80")
|
||||
@api_versions.experimental_api
|
||||
def create(self, share, backup_options=None, description=None, name=None):
|
||||
"""Create a backup for a share.
|
||||
|
||||
:param share: The share to create the backup of. Can be the share
|
||||
object or its UUID.
|
||||
:param backup_options: dict - custom set of key-values
|
||||
:param name: text - name of new share
|
||||
:param description: - description for new share
|
||||
"""
|
||||
share_id = base.getid(share)
|
||||
body = {
|
||||
'share_id': share_id,
|
||||
'backup_options': backup_options,
|
||||
'description': description,
|
||||
'name': name,
|
||||
}
|
||||
|
||||
return self._create(RESOURCES_PATH,
|
||||
{RESOURCE_NAME: body},
|
||||
RESOURCE_NAME)
|
||||
|
||||
@api_versions.wraps("2.80")
|
||||
@api_versions.experimental_api
|
||||
def delete(self, backup):
|
||||
backup_id = base.getid(backup)
|
||||
url = RESOURCE_PATH % backup_id
|
||||
self._delete(url)
|
||||
|
||||
@api_versions.wraps("2.80")
|
||||
@api_versions.experimental_api
|
||||
def restore(self, backup):
|
||||
return self._action('restore', backup)
|
||||
|
||||
@api_versions.wraps("2.80")
|
||||
@api_versions.experimental_api
|
||||
def reset_status(self, backup, state):
|
||||
return self._action('reset_status', backup, {"status": state})
|
||||
|
||||
@api_versions.wraps("2.80")
|
||||
@api_versions.experimental_api
|
||||
def update(self, backup, **kwargs):
|
||||
if not kwargs:
|
||||
return
|
||||
backup_id = base.getid(backup)
|
||||
body = {'share_backup': kwargs}
|
||||
return self._update(RESOURCE_PATH % backup_id, body)
|
||||
|
||||
def _action(self, action, backup, info=None, **kwargs):
|
||||
body = {action: info}
|
||||
self.run_hooks('modify_body_for_action', body, **kwargs)
|
||||
backup_id = base.getid(backup)
|
||||
url = RESOURCE_PATH_ACTION % backup_id
|
||||
return self.api.client.post(url, body=body)
|
7
releasenotes/notes/share-backup-98e11c6a28897e94.yaml
Normal file
7
releasenotes/notes/share-backup-98e11c6a28897e94.yaml
Normal file
@ -0,0 +1,7 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
Added support for share backup APIs in the SDK and the openstackclient
|
||||
plugin. You can use the openstack client to create a backup, restore a
|
||||
backup, delete a backup, list backups with filters, and update the name
|
||||
and description fields of a backup. Available from microversion 2.80.
|
@ -59,6 +59,13 @@ openstack.share.v2 =
|
||||
share_access_show = manilaclient.osc.v2.share_access_rules:ShowShareAccess
|
||||
share_access_set = manilaclient.osc.v2.share_access_rules:SetShareAccess
|
||||
share_access_unset = manilaclient.osc.v2.share_access_rules:UnsetShareAccess
|
||||
share_backup_create = manilaclient.osc.v2.share_backups:CreateShareBackup
|
||||
share_backup_delete = manilaclient.osc.v2.share_backups:DeleteShareBackup
|
||||
share_backup_list = manilaclient.osc.v2.share_backups:ListShareBackup
|
||||
share_backup_show = manilaclient.osc.v2.share_backups:ShowShareBackup
|
||||
share_backup_restore = manilaclient.osc.v2.share_backups:RestoreShareBackup
|
||||
share_backup_set = manilaclient.osc.v2.share_backups:SetShareBackup
|
||||
share_backup_unset = manilaclient.osc.v2.share_backups:UnsetShareBackup
|
||||
share_type_create = manilaclient.osc.v2.share_types:CreateShareType
|
||||
share_type_delete = manilaclient.osc.v2.share_types:DeleteShareType
|
||||
share_type_set = manilaclient.osc.v2.share_types:SetShareType
|
||||
|
Loading…
Reference in New Issue
Block a user