Change-Id: Id6ca5075bd11765f00b62b44f91635ef68205175 Signed-off-by: Stephen Finucane <sfinucan@redhat.com>
500 lines
15 KiB
Python
500 lines
15 KiB
Python
# 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 import api_versions
|
|
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().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().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().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=(
|
|
f'Key to be sorted, available keys are '
|
|
f'{constants.BACKUP_SORT_KEY_VALUES}. Default=None.'
|
|
),
|
|
)
|
|
parser.add_argument(
|
|
'--sort-dir',
|
|
'--sort_dir',
|
|
metavar='<sort_dir>',
|
|
type=str,
|
|
default=None,
|
|
help=(
|
|
f'Sort direction, available values are '
|
|
f'{constants.SORT_DIR_VALUES}. OPTIONAL: Default=None.'
|
|
),
|
|
)
|
|
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().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().get_parser(prog_name)
|
|
parser.add_argument(
|
|
"backup", metavar="<backup>", help=_('ID of backup to restore.')
|
|
)
|
|
parser.add_argument(
|
|
"--target-share",
|
|
metavar="<target-share>",
|
|
default=None,
|
|
help=_(
|
|
'share to restore backup to. Source share if none supplied'
|
|
),
|
|
)
|
|
parser.add_argument(
|
|
'--wait',
|
|
action='store_true',
|
|
default=False,
|
|
help=_('Wait for restore conclusion'),
|
|
)
|
|
return parser
|
|
|
|
def take_action(self, parsed_args):
|
|
share_client = self.app.client_manager.share
|
|
kwargs = {}
|
|
|
|
share_backup = osc_utils.find_resource(
|
|
share_client.share_backups, parsed_args.backup
|
|
)
|
|
target_share_id = None
|
|
if parsed_args.target_share is not None:
|
|
if share_client.api_version < api_versions.APIVersion('2.91'):
|
|
raise exceptions.CommandError(
|
|
'performing targeted restores is only available '
|
|
'for API microversion >= 2.91'
|
|
)
|
|
else:
|
|
target_share_id = osc_utils.find_resource(
|
|
share_client.shares, parsed_args.target_share
|
|
).id
|
|
kwargs['target_share_id'] = target_share_id
|
|
|
|
share_client.share_backups.restore(share_backup.id, **kwargs)
|
|
|
|
if parsed_args.wait:
|
|
if not osc_utils.wait_for_status(
|
|
status_f=share_client.shares.get,
|
|
res_id=(target_share_id or share_backup.share_id),
|
|
success_status=['available'],
|
|
error_status=['error', 'backup_restoring_error'],
|
|
):
|
|
LOG.error(_("ERROR: share is in error state."))
|
|
|
|
|
|
class SetShareBackup(command.Command):
|
|
"""Set share backup properties."""
|
|
|
|
_description = _("Set share backup properties")
|
|
|
|
def get_parser(self, prog_name):
|
|
parser = super().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().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},
|
|
)
|