Merge "Add support for out of place share backup restores"
This commit is contained in:
@@ -27,7 +27,7 @@ from manilaclient import utils
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
MAX_VERSION = '2.90'
|
||||
MAX_VERSION = '2.91'
|
||||
MIN_VERSION = '2.0'
|
||||
DEPRECATED_VERSION = '1.0'
|
||||
_VERSIONED_METHOD_MAP = {}
|
||||
|
@@ -20,10 +20,12 @@ 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__)
|
||||
|
||||
|
||||
@@ -282,14 +284,51 @@ class RestoreShareBackup(command.Command):
|
||||
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)
|
||||
share_client.share_backups.restore(share_backup.id)
|
||||
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):
|
||||
|
@@ -69,6 +69,24 @@ class ShareBackupCLITest(base.OSCClientTestBase):
|
||||
self.assertEqual('test_backup_show', show_result["name"])
|
||||
self.assertEqual('Description', show_result["description"])
|
||||
|
||||
def test_share_backup_restore(self):
|
||||
share = self.create_share()
|
||||
|
||||
backup = self.create_backup(
|
||||
share_id=share['id'],
|
||||
backup_options={'dummy': True})
|
||||
|
||||
self.openstack(
|
||||
f'share backup restore {backup["id"]} --wait')
|
||||
|
||||
backup = json.loads(self.openstack(
|
||||
f'share backup show -f json {backup["id"]}'))
|
||||
share = json.loads(self.openstack(
|
||||
f'share show -f json {share["id"]}'))
|
||||
|
||||
self.assertEqual('available', backup['status'])
|
||||
self.assertEqual('available', share['status'])
|
||||
|
||||
def test_share_backup_set(self):
|
||||
share = self.create_share()
|
||||
|
||||
|
@@ -309,7 +309,11 @@ class TestShareBackupRestore(TestShareBackup):
|
||||
self.share_backup = (
|
||||
manila_fakes.FakeShareBackup.create_one_backup()
|
||||
)
|
||||
self.target_share = (
|
||||
manila_fakes.FakeShare.create_one_share()
|
||||
)
|
||||
self.backups_mock.get.return_value = self.share_backup
|
||||
self.shares_mock.get.return_value = self.target_share
|
||||
self.cmd = osc_share_backups.RestoreShareBackup(
|
||||
self.app, None)
|
||||
|
||||
@@ -325,6 +329,24 @@ class TestShareBackupRestore(TestShareBackup):
|
||||
self.backups_mock.restore.assert_called_with(self.share_backup.id)
|
||||
self.assertIsNone(result)
|
||||
|
||||
def test_share_backup_restore_to_target(self):
|
||||
arglist = [
|
||||
self.share_backup.id,
|
||||
'--target-share', self.target_share.id
|
||||
]
|
||||
verifylist = [
|
||||
('backup', self.share_backup.id),
|
||||
('target_share', self.target_share.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,
|
||||
target_share_id=self.target_share.id
|
||||
)
|
||||
self.assertIsNone(result)
|
||||
|
||||
|
||||
class TestShareBackupSet(TestShareBackup):
|
||||
|
||||
|
@@ -20,6 +20,7 @@ from manilaclient.tests.unit.v2 import fakes
|
||||
from manilaclient.v2 import share_backups
|
||||
|
||||
FAKE_BACKUP = 'fake_backup'
|
||||
FAKE_TARGET_SHARE_ID = 'fake_target_share_id'
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
@@ -30,7 +31,7 @@ class ShareBackupsTest(utils.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(ShareBackupsTest, self).setUp()
|
||||
microversion = api_versions.APIVersion("2.80")
|
||||
microversion = api_versions.APIVersion("2.91")
|
||||
self.manager = share_backups.ShareBackupManager(
|
||||
fakes.FakeClient(api_version=microversion))
|
||||
|
||||
@@ -58,7 +59,16 @@ class ShareBackupsTest(utils.TestCase):
|
||||
with mock.patch.object(self.manager, '_action', mock.Mock()):
|
||||
self.manager.restore(FAKE_BACKUP)
|
||||
self.manager._action.assert_called_once_with(
|
||||
'restore', FAKE_BACKUP)
|
||||
'restore', FAKE_BACKUP, info=None)
|
||||
|
||||
def test_restore_to_target(self):
|
||||
with mock.patch.object(self.manager, '_action', mock.Mock()):
|
||||
self.manager.restore(
|
||||
FAKE_BACKUP,
|
||||
target_share_id=FAKE_TARGET_SHARE_ID
|
||||
)
|
||||
self.manager._action.assert_called_once_with(
|
||||
'restore', FAKE_BACKUP, info=FAKE_TARGET_SHARE_ID)
|
||||
|
||||
def test_list(self):
|
||||
with mock.patch.object(self.manager, '_list', mock.Mock()):
|
||||
|
@@ -110,10 +110,15 @@ class ShareBackupManager(base.ManagerWithFind):
|
||||
url = RESOURCE_PATH % backup_id
|
||||
self._delete(url)
|
||||
|
||||
@api_versions.wraps("2.80")
|
||||
@api_versions.wraps("2.80", "2.90")
|
||||
@api_versions.experimental_api
|
||||
def restore(self, backup):
|
||||
return self._action('restore', backup)
|
||||
def restore(self, backup_id):
|
||||
return self._action('restore', backup_id)
|
||||
|
||||
@api_versions.wraps("2.91")
|
||||
@api_versions.experimental_api
|
||||
def restore(self, backup_id, target_share_id=None): # noqa F811
|
||||
return self._action('restore', backup_id, info=target_share_id)
|
||||
|
||||
@api_versions.wraps("2.80")
|
||||
@api_versions.experimental_api
|
||||
|
@@ -0,0 +1,7 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
Added support for targeted share backup restores via the openstackclient
|
||||
plugin. You can use the openstack client to restore a share backup from
|
||||
one source share to another target share, given the backup or share driver
|
||||
provides support for the operation. Available from microversion 2.91.
|
Reference in New Issue
Block a user