Skip to content

Commit bd0727c

Browse files
committed
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
1 parent 56b0f6d commit bd0727c

3 files changed

Lines changed: 103 additions & 3 deletions

File tree

openstackclient/tests/unit/volume/v2/test_volume.py

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
from unittest import mock
1717
from unittest.mock import call
1818

19+
from cinderclient import api_versions
1920
from osc_lib.cli import format_columns
2021
from osc_lib import exceptions
2122
from osc_lib import utils
@@ -47,6 +48,9 @@ def setUp(self):
4748
self.snapshots_mock = self.app.client_manager.volume.volume_snapshots
4849
self.snapshots_mock.reset_mock()
4950

51+
self.backups_mock = self.app.client_manager.volume.backups
52+
self.backups_mock.reset_mock()
53+
5054
self.types_mock = self.app.client_manager.volume.volume_types
5155
self.types_mock.reset_mock()
5256

@@ -129,6 +133,7 @@ def test_volume_create_min_options(self):
129133
source_volid=None,
130134
consistencygroup_id=None,
131135
scheduler_hints=None,
136+
backup_id=None,
132137
)
133138

134139
self.assertEqual(self.columns, columns)
@@ -174,6 +179,7 @@ def test_volume_create_options(self):
174179
source_volid=None,
175180
consistencygroup_id=consistency_group.id,
176181
scheduler_hints={'k': 'v'},
182+
backup_id=None,
177183
)
178184

179185
self.assertEqual(self.columns, columns)
@@ -210,6 +216,7 @@ def test_volume_create_properties(self):
210216
source_volid=None,
211217
consistencygroup_id=None,
212218
scheduler_hints=None,
219+
backup_id=None,
213220
)
214221

215222
self.assertEqual(self.columns, columns)
@@ -248,6 +255,7 @@ def test_volume_create_image_id(self):
248255
source_volid=None,
249256
consistencygroup_id=None,
250257
scheduler_hints=None,
258+
backup_id=None,
251259
)
252260

253261
self.assertEqual(self.columns, columns)
@@ -286,6 +294,7 @@ def test_volume_create_image_name(self):
286294
source_volid=None,
287295
consistencygroup_id=None,
288296
scheduler_hints=None,
297+
backup_id=None,
289298
)
290299

291300
self.assertEqual(self.columns, columns)
@@ -323,11 +332,72 @@ def test_volume_create_with_snapshot(self):
323332
source_volid=None,
324333
consistencygroup_id=None,
325334
scheduler_hints=None,
335+
backup_id=None,
336+
)
337+
338+
self.assertEqual(self.columns, columns)
339+
self.assertCountEqual(self.datalist, data)
340+
341+
def test_volume_create_with_backup(self):
342+
backup = volume_fakes.create_one_backup()
343+
self.new_volume.backup_id = backup.id
344+
arglist = [
345+
'--backup', self.new_volume.backup_id,
346+
self.new_volume.name,
347+
]
348+
verifylist = [
349+
('backup', self.new_volume.backup_id),
350+
('name', self.new_volume.name),
351+
]
352+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
353+
354+
self.backups_mock.get.return_value = backup
355+
356+
self.app.client_manager.volume.api_version = \
357+
api_versions.APIVersion('3.47')
358+
359+
# In base command class ShowOne in cliff, abstract method take_action()
360+
# returns a two-part tuple with a tuple of column names and a tuple of
361+
# data to be shown.
362+
columns, data = self.cmd.take_action(parsed_args)
363+
364+
self.volumes_mock.create.assert_called_once_with(
365+
size=backup.size,
366+
snapshot_id=None,
367+
name=self.new_volume.name,
368+
description=None,
369+
volume_type=None,
370+
availability_zone=None,
371+
metadata=None,
372+
imageRef=None,
373+
source_volid=None,
374+
consistencygroup_id=None,
375+
scheduler_hints=None,
376+
backup_id=backup.id,
326377
)
327378

328379
self.assertEqual(self.columns, columns)
329380
self.assertCountEqual(self.datalist, data)
330381

382+
def test_volume_create_with_backup_pre_347(self):
383+
backup = volume_fakes.create_one_backup()
384+
self.new_volume.backup_id = backup.id
385+
arglist = [
386+
'--backup', self.new_volume.backup_id,
387+
self.new_volume.name,
388+
]
389+
verifylist = [
390+
('backup', self.new_volume.backup_id),
391+
('name', self.new_volume.name),
392+
]
393+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
394+
395+
self.backups_mock.get.return_value = backup
396+
397+
exc = self.assertRaises(exceptions.CommandError, self.cmd.take_action,
398+
parsed_args)
399+
self.assertIn("--os-volume-api-version 3.47 or greater", str(exc))
400+
331401
def test_volume_create_with_bootable_and_readonly(self):
332402
arglist = [
333403
'--bootable',
@@ -361,6 +431,7 @@ def test_volume_create_with_bootable_and_readonly(self):
361431
source_volid=None,
362432
consistencygroup_id=None,
363433
scheduler_hints=None,
434+
backup_id=None,
364435
)
365436

366437
self.assertEqual(self.columns, columns)
@@ -403,6 +474,7 @@ def test_volume_create_with_nonbootable_and_readwrite(self):
403474
source_volid=None,
404475
consistencygroup_id=None,
405476
scheduler_hints=None,
477+
backup_id=None,
406478
)
407479

408480
self.assertEqual(self.columns, columns)
@@ -454,6 +526,7 @@ def test_volume_create_with_bootable_and_readonly_fail(
454526
source_volid=None,
455527
consistencygroup_id=None,
456528
scheduler_hints=None,
529+
backup_id=None,
457530
)
458531

459532
self.assertEqual(2, mock_error.call_count)

openstackclient/volume/v2/volume.py

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,10 +71,10 @@ def _check_size_arg(args):
7171
volume is not specified.
7272
"""
7373

74-
if ((args.snapshot or args.source)
74+
if ((args.snapshot or args.source or args.backup)
7575
is None and args.size is None):
76-
msg = _("--size is a required option if snapshot "
77-
"or source volume is not specified.")
76+
msg = _("--size is a required option if snapshot, backup "
77+
"or source volume are not specified.")
7878
raise exceptions.CommandError(msg)
7979

8080

@@ -117,6 +117,12 @@ def get_parser(self, prog_name):
117117
metavar="<volume>",
118118
help=_("Volume to clone (name or ID)"),
119119
)
120+
source_group.add_argument(
121+
"--backup",
122+
metavar="<backup>",
123+
help=_("Restore backup to a volume (name or ID) "
124+
"(supported by --os-volume-api-version 3.47 or later)"),
125+
)
120126
source_group.add_argument(
121127
"--source-replicated",
122128
metavar="<replicated-volume>",
@@ -177,9 +183,16 @@ def get_parser(self, prog_name):
177183

178184
def take_action(self, parsed_args):
179185
_check_size_arg(parsed_args)
186+
180187
volume_client = self.app.client_manager.volume
181188
image_client = self.app.client_manager.image
182189

190+
if parsed_args.backup and not (
191+
volume_client.api_version.matches('3.47')):
192+
msg = _("--os-volume-api-version 3.47 or greater is required "
193+
"to create a volume from backup.")
194+
raise exceptions.CommandError(msg)
195+
183196
source_volume = None
184197
if parsed_args.source:
185198
source_volume = utils.find_resource(
@@ -213,6 +226,15 @@ def take_action(self, parsed_args):
213226
# snapshot size.
214227
size = max(size or 0, snapshot_obj.size)
215228

229+
backup = None
230+
if parsed_args.backup:
231+
backup_obj = utils.find_resource(
232+
volume_client.backups,
233+
parsed_args.backup)
234+
backup = backup_obj.id
235+
# As above
236+
size = max(size or 0, backup_obj.size)
237+
216238
volume = volume_client.volumes.create(
217239
size=size,
218240
snapshot_id=snapshot,
@@ -225,6 +247,7 @@ def take_action(self, parsed_args):
225247
source_volid=source_volume,
226248
consistencygroup_id=consistency_group,
227249
scheduler_hints=parsed_args.hint,
250+
backup_id=backup,
228251
)
229252

230253
if parsed_args.bootable or parsed_args.non_bootable:
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
features:
3+
- |
4+
Added ``--backup`` option to the ``volume create`` command.

0 commit comments

Comments
 (0)