Add option to create volume from backup
Support for creating a volume from backup was added in microversio 3.47. This patch adds a --backup option to the volume create command to add that support. Change-Id: Ib26d2d335475d9aacbf77c0fd7b7cda2ba743943
This commit is contained in:
parent
56b0f6de0e
commit
bd0727c4f8
@ -16,6 +16,7 @@ import argparse
|
|||||||
from unittest import mock
|
from unittest import mock
|
||||||
from unittest.mock import call
|
from unittest.mock import call
|
||||||
|
|
||||||
|
from cinderclient import api_versions
|
||||||
from osc_lib.cli import format_columns
|
from osc_lib.cli import format_columns
|
||||||
from osc_lib import exceptions
|
from osc_lib import exceptions
|
||||||
from osc_lib import utils
|
from osc_lib import utils
|
||||||
@ -47,6 +48,9 @@ class TestVolume(volume_fakes.TestVolume):
|
|||||||
self.snapshots_mock = self.app.client_manager.volume.volume_snapshots
|
self.snapshots_mock = self.app.client_manager.volume.volume_snapshots
|
||||||
self.snapshots_mock.reset_mock()
|
self.snapshots_mock.reset_mock()
|
||||||
|
|
||||||
|
self.backups_mock = self.app.client_manager.volume.backups
|
||||||
|
self.backups_mock.reset_mock()
|
||||||
|
|
||||||
self.types_mock = self.app.client_manager.volume.volume_types
|
self.types_mock = self.app.client_manager.volume.volume_types
|
||||||
self.types_mock.reset_mock()
|
self.types_mock.reset_mock()
|
||||||
|
|
||||||
@ -129,6 +133,7 @@ class TestVolumeCreate(TestVolume):
|
|||||||
source_volid=None,
|
source_volid=None,
|
||||||
consistencygroup_id=None,
|
consistencygroup_id=None,
|
||||||
scheduler_hints=None,
|
scheduler_hints=None,
|
||||||
|
backup_id=None,
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertEqual(self.columns, columns)
|
self.assertEqual(self.columns, columns)
|
||||||
@ -174,6 +179,7 @@ class TestVolumeCreate(TestVolume):
|
|||||||
source_volid=None,
|
source_volid=None,
|
||||||
consistencygroup_id=consistency_group.id,
|
consistencygroup_id=consistency_group.id,
|
||||||
scheduler_hints={'k': 'v'},
|
scheduler_hints={'k': 'v'},
|
||||||
|
backup_id=None,
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertEqual(self.columns, columns)
|
self.assertEqual(self.columns, columns)
|
||||||
@ -210,6 +216,7 @@ class TestVolumeCreate(TestVolume):
|
|||||||
source_volid=None,
|
source_volid=None,
|
||||||
consistencygroup_id=None,
|
consistencygroup_id=None,
|
||||||
scheduler_hints=None,
|
scheduler_hints=None,
|
||||||
|
backup_id=None,
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertEqual(self.columns, columns)
|
self.assertEqual(self.columns, columns)
|
||||||
@ -248,6 +255,7 @@ class TestVolumeCreate(TestVolume):
|
|||||||
source_volid=None,
|
source_volid=None,
|
||||||
consistencygroup_id=None,
|
consistencygroup_id=None,
|
||||||
scheduler_hints=None,
|
scheduler_hints=None,
|
||||||
|
backup_id=None,
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertEqual(self.columns, columns)
|
self.assertEqual(self.columns, columns)
|
||||||
@ -286,6 +294,7 @@ class TestVolumeCreate(TestVolume):
|
|||||||
source_volid=None,
|
source_volid=None,
|
||||||
consistencygroup_id=None,
|
consistencygroup_id=None,
|
||||||
scheduler_hints=None,
|
scheduler_hints=None,
|
||||||
|
backup_id=None,
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertEqual(self.columns, columns)
|
self.assertEqual(self.columns, columns)
|
||||||
@ -323,11 +332,72 @@ class TestVolumeCreate(TestVolume):
|
|||||||
source_volid=None,
|
source_volid=None,
|
||||||
consistencygroup_id=None,
|
consistencygroup_id=None,
|
||||||
scheduler_hints=None,
|
scheduler_hints=None,
|
||||||
|
backup_id=None,
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertEqual(self.columns, columns)
|
self.assertEqual(self.columns, columns)
|
||||||
self.assertCountEqual(self.datalist, data)
|
self.assertCountEqual(self.datalist, data)
|
||||||
|
|
||||||
|
def test_volume_create_with_backup(self):
|
||||||
|
backup = volume_fakes.create_one_backup()
|
||||||
|
self.new_volume.backup_id = backup.id
|
||||||
|
arglist = [
|
||||||
|
'--backup', self.new_volume.backup_id,
|
||||||
|
self.new_volume.name,
|
||||||
|
]
|
||||||
|
verifylist = [
|
||||||
|
('backup', self.new_volume.backup_id),
|
||||||
|
('name', self.new_volume.name),
|
||||||
|
]
|
||||||
|
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||||
|
|
||||||
|
self.backups_mock.get.return_value = backup
|
||||||
|
|
||||||
|
self.app.client_manager.volume.api_version = \
|
||||||
|
api_versions.APIVersion('3.47')
|
||||||
|
|
||||||
|
# In base command class ShowOne in cliff, abstract method take_action()
|
||||||
|
# returns a two-part tuple with a tuple of column names and a tuple of
|
||||||
|
# data to be shown.
|
||||||
|
columns, data = self.cmd.take_action(parsed_args)
|
||||||
|
|
||||||
|
self.volumes_mock.create.assert_called_once_with(
|
||||||
|
size=backup.size,
|
||||||
|
snapshot_id=None,
|
||||||
|
name=self.new_volume.name,
|
||||||
|
description=None,
|
||||||
|
volume_type=None,
|
||||||
|
availability_zone=None,
|
||||||
|
metadata=None,
|
||||||
|
imageRef=None,
|
||||||
|
source_volid=None,
|
||||||
|
consistencygroup_id=None,
|
||||||
|
scheduler_hints=None,
|
||||||
|
backup_id=backup.id,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(self.columns, columns)
|
||||||
|
self.assertCountEqual(self.datalist, data)
|
||||||
|
|
||||||
|
def test_volume_create_with_backup_pre_347(self):
|
||||||
|
backup = volume_fakes.create_one_backup()
|
||||||
|
self.new_volume.backup_id = backup.id
|
||||||
|
arglist = [
|
||||||
|
'--backup', self.new_volume.backup_id,
|
||||||
|
self.new_volume.name,
|
||||||
|
]
|
||||||
|
verifylist = [
|
||||||
|
('backup', self.new_volume.backup_id),
|
||||||
|
('name', self.new_volume.name),
|
||||||
|
]
|
||||||
|
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||||
|
|
||||||
|
self.backups_mock.get.return_value = backup
|
||||||
|
|
||||||
|
exc = self.assertRaises(exceptions.CommandError, self.cmd.take_action,
|
||||||
|
parsed_args)
|
||||||
|
self.assertIn("--os-volume-api-version 3.47 or greater", str(exc))
|
||||||
|
|
||||||
def test_volume_create_with_bootable_and_readonly(self):
|
def test_volume_create_with_bootable_and_readonly(self):
|
||||||
arglist = [
|
arglist = [
|
||||||
'--bootable',
|
'--bootable',
|
||||||
@ -361,6 +431,7 @@ class TestVolumeCreate(TestVolume):
|
|||||||
source_volid=None,
|
source_volid=None,
|
||||||
consistencygroup_id=None,
|
consistencygroup_id=None,
|
||||||
scheduler_hints=None,
|
scheduler_hints=None,
|
||||||
|
backup_id=None,
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertEqual(self.columns, columns)
|
self.assertEqual(self.columns, columns)
|
||||||
@ -403,6 +474,7 @@ class TestVolumeCreate(TestVolume):
|
|||||||
source_volid=None,
|
source_volid=None,
|
||||||
consistencygroup_id=None,
|
consistencygroup_id=None,
|
||||||
scheduler_hints=None,
|
scheduler_hints=None,
|
||||||
|
backup_id=None,
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertEqual(self.columns, columns)
|
self.assertEqual(self.columns, columns)
|
||||||
@ -454,6 +526,7 @@ class TestVolumeCreate(TestVolume):
|
|||||||
source_volid=None,
|
source_volid=None,
|
||||||
consistencygroup_id=None,
|
consistencygroup_id=None,
|
||||||
scheduler_hints=None,
|
scheduler_hints=None,
|
||||||
|
backup_id=None,
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertEqual(2, mock_error.call_count)
|
self.assertEqual(2, mock_error.call_count)
|
||||||
|
@ -71,10 +71,10 @@ def _check_size_arg(args):
|
|||||||
volume is not specified.
|
volume is not specified.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if ((args.snapshot or args.source)
|
if ((args.snapshot or args.source or args.backup)
|
||||||
is None and args.size is None):
|
is None and args.size is None):
|
||||||
msg = _("--size is a required option if snapshot "
|
msg = _("--size is a required option if snapshot, backup "
|
||||||
"or source volume is not specified.")
|
"or source volume are not specified.")
|
||||||
raise exceptions.CommandError(msg)
|
raise exceptions.CommandError(msg)
|
||||||
|
|
||||||
|
|
||||||
@ -117,6 +117,12 @@ class CreateVolume(command.ShowOne):
|
|||||||
metavar="<volume>",
|
metavar="<volume>",
|
||||||
help=_("Volume to clone (name or ID)"),
|
help=_("Volume to clone (name or ID)"),
|
||||||
)
|
)
|
||||||
|
source_group.add_argument(
|
||||||
|
"--backup",
|
||||||
|
metavar="<backup>",
|
||||||
|
help=_("Restore backup to a volume (name or ID) "
|
||||||
|
"(supported by --os-volume-api-version 3.47 or later)"),
|
||||||
|
)
|
||||||
source_group.add_argument(
|
source_group.add_argument(
|
||||||
"--source-replicated",
|
"--source-replicated",
|
||||||
metavar="<replicated-volume>",
|
metavar="<replicated-volume>",
|
||||||
@ -177,9 +183,16 @@ class CreateVolume(command.ShowOne):
|
|||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
_check_size_arg(parsed_args)
|
_check_size_arg(parsed_args)
|
||||||
|
|
||||||
volume_client = self.app.client_manager.volume
|
volume_client = self.app.client_manager.volume
|
||||||
image_client = self.app.client_manager.image
|
image_client = self.app.client_manager.image
|
||||||
|
|
||||||
|
if parsed_args.backup and not (
|
||||||
|
volume_client.api_version.matches('3.47')):
|
||||||
|
msg = _("--os-volume-api-version 3.47 or greater is required "
|
||||||
|
"to create a volume from backup.")
|
||||||
|
raise exceptions.CommandError(msg)
|
||||||
|
|
||||||
source_volume = None
|
source_volume = None
|
||||||
if parsed_args.source:
|
if parsed_args.source:
|
||||||
source_volume = utils.find_resource(
|
source_volume = utils.find_resource(
|
||||||
@ -213,6 +226,15 @@ class CreateVolume(command.ShowOne):
|
|||||||
# snapshot size.
|
# snapshot size.
|
||||||
size = max(size or 0, snapshot_obj.size)
|
size = max(size or 0, snapshot_obj.size)
|
||||||
|
|
||||||
|
backup = None
|
||||||
|
if parsed_args.backup:
|
||||||
|
backup_obj = utils.find_resource(
|
||||||
|
volume_client.backups,
|
||||||
|
parsed_args.backup)
|
||||||
|
backup = backup_obj.id
|
||||||
|
# As above
|
||||||
|
size = max(size or 0, backup_obj.size)
|
||||||
|
|
||||||
volume = volume_client.volumes.create(
|
volume = volume_client.volumes.create(
|
||||||
size=size,
|
size=size,
|
||||||
snapshot_id=snapshot,
|
snapshot_id=snapshot,
|
||||||
@ -225,6 +247,7 @@ class CreateVolume(command.ShowOne):
|
|||||||
source_volid=source_volume,
|
source_volid=source_volume,
|
||||||
consistencygroup_id=consistency_group,
|
consistencygroup_id=consistency_group,
|
||||||
scheduler_hints=parsed_args.hint,
|
scheduler_hints=parsed_args.hint,
|
||||||
|
backup_id=backup,
|
||||||
)
|
)
|
||||||
|
|
||||||
if parsed_args.bootable or parsed_args.non_bootable:
|
if parsed_args.bootable or parsed_args.non_bootable:
|
||||||
|
@ -0,0 +1,4 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- |
|
||||||
|
Added ``--backup`` option to the ``volume create`` command.
|
Loading…
x
Reference in New Issue
Block a user