Skip to content

Commit ec01268

Browse files
committed
Add options to create volume group from source
This patch adds ``--source-group`` and ``--group-snapshot`` options to the ``volume group create`` command to allow creating group from a source group or a group snapshot. Change-Id: I87482a5dd43c519dfdcf981635aa879914a70a5c
1 parent 73b4ce8 commit ec01268

File tree

4 files changed

+226
-30
lines changed

4 files changed

+226
-30
lines changed

doc/source/cli/data/cinder.csv

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ freeze-host,volume host set --disable,Freeze and disable the specified cinder-vo
4545
get-capabilities,volume backend capability show,Show capabilities of a volume backend. Admin only.
4646
get-pools,volume backend pool list,Show pool information for backends. Admin only.
4747
group-create,volume group create,Creates a group. (Supported by API versions 3.13 - 3.latest)
48-
group-create-from-src,,Creates a group from a group snapshot or a source group. (Supported by API versions 3.14 - 3.latest)
48+
group-create-from-src,volume group create [--source-group|--group-snapshot],Creates a group from a group snapshot or a source group. (Supported by API versions 3.14 - 3.latest)
4949
group-delete,volume group delete,Removes one or more groups. (Supported by API versions 3.13 - 3.latest)
5050
group-disable-replication,volume group set --disable-replication,Disables replication for group. (Supported by API versions 3.38 - 3.latest)
5151
group-enable-replication,volume group set --enable-replication,Enables replication for group. (Supported by API versions 3.38 - 3.latest)

openstackclient/tests/unit/volume/v3/test_volume_group.py

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,12 @@
1010
# License for the specific language governing permissions and limitations
1111
# under the License.
1212

13+
from unittest import mock
14+
1315
from cinderclient import api_versions
1416
from osc_lib import exceptions
1517

18+
from openstackclient.tests.unit import utils as tests_utils
1619
from openstackclient.tests.unit.volume.v3 import fakes as volume_fakes
1720
from openstackclient.volume.v3 import volume_group
1821

@@ -32,6 +35,10 @@ def setUp(self):
3235
self.volume_types_mock = self.app.client_manager.volume.volume_types
3336
self.volume_types_mock.reset_mock()
3437

38+
self.volume_group_snapshots_mock = \
39+
self.app.client_manager.volume.group_snapshots
40+
self.volume_group_snapshots_mock.reset_mock()
41+
3542

3643
class TestVolumeGroupCreate(TestVolumeGroup):
3744

@@ -43,6 +50,8 @@ class TestVolumeGroupCreate(TestVolumeGroup):
4350
'volume_types': [fake_volume_type.id],
4451
},
4552
)
53+
fake_volume_group_snapshot = \
54+
volume_fakes.create_one_volume_group_snapshot()
4655

4756
columns = (
4857
'ID',
@@ -79,6 +88,10 @@ def setUp(self):
7988
self.fake_volume_group_type
8089
self.volume_groups_mock.create.return_value = self.fake_volume_group
8190
self.volume_groups_mock.get.return_value = self.fake_volume_group
91+
self.volume_groups_mock.create_from_src.return_value = \
92+
self.fake_volume_group
93+
self.volume_group_snapshots_mock.get.return_value = \
94+
self.fake_volume_group_snapshot
8295

8396
self.cmd = volume_group.CreateVolumeGroup(self.app, None)
8497

@@ -115,6 +128,29 @@ def test_volume_group_create(self):
115128
self.assertEqual(self.columns, columns)
116129
self.assertCountEqual(self.data, data)
117130

131+
def test_volume_group_create_no_volume_type(self):
132+
self.app.client_manager.volume.api_version = \
133+
api_versions.APIVersion('3.13')
134+
135+
arglist = [
136+
self.fake_volume_group_type.id
137+
]
138+
verifylist = [
139+
('volume_group_type', self.fake_volume_group_type.id),
140+
('name', None),
141+
('description', None),
142+
('availability_zone', None),
143+
]
144+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
145+
146+
exc = self.assertRaises(
147+
exceptions.CommandError,
148+
self.cmd.take_action,
149+
parsed_args)
150+
self.assertIn(
151+
'<volume_types> is a required argument',
152+
str(exc))
153+
118154
def test_volume_group_create_with_options(self):
119155
self.app.client_manager.volume.api_version = \
120156
api_versions.APIVersion('3.13')
@@ -176,6 +212,101 @@ def test_volume_group_create_pre_v313(self):
176212
'--os-volume-api-version 3.13 or greater is required',
177213
str(exc))
178214

215+
def test_volume_group_create_from_source_group(self):
216+
self.app.client_manager.volume.api_version = \
217+
api_versions.APIVersion('3.14')
218+
219+
arglist = [
220+
'--source-group', self.fake_volume_group.id,
221+
]
222+
verifylist = [
223+
('source_group', self.fake_volume_group.id),
224+
]
225+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
226+
227+
columns, data = self.cmd.take_action(parsed_args)
228+
229+
self.volume_groups_mock.get.assert_has_calls(
230+
[mock.call(self.fake_volume_group.id),
231+
mock.call(self.fake_volume_group.id)])
232+
self.volume_groups_mock.create_from_src.assert_called_once_with(
233+
None,
234+
self.fake_volume_group.id,
235+
None,
236+
None,
237+
)
238+
self.assertEqual(self.columns, columns)
239+
self.assertCountEqual(self.data, data)
240+
241+
def test_volume_group_create_from_group_snapshot(self):
242+
self.app.client_manager.volume.api_version = \
243+
api_versions.APIVersion('3.14')
244+
245+
arglist = [
246+
'--group-snapshot', self.fake_volume_group_snapshot.id,
247+
]
248+
verifylist = [
249+
('group_snapshot', self.fake_volume_group_snapshot.id),
250+
]
251+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
252+
253+
columns, data = self.cmd.take_action(parsed_args)
254+
255+
self.volume_group_snapshots_mock.get.assert_called_once_with(
256+
self.fake_volume_group_snapshot.id)
257+
self.volume_groups_mock.get.assert_called_once_with(
258+
self.fake_volume_group.id)
259+
self.volume_groups_mock.create_from_src.assert_called_once_with(
260+
self.fake_volume_group_snapshot.id,
261+
None,
262+
None,
263+
None,
264+
)
265+
self.assertEqual(self.columns, columns)
266+
self.assertCountEqual(self.data, data)
267+
268+
def test_volume_group_create_from_src_pre_v314(self):
269+
self.app.client_manager.volume.api_version = \
270+
api_versions.APIVersion('3.13')
271+
272+
arglist = [
273+
'--source-group', self.fake_volume_group.id,
274+
]
275+
verifylist = [
276+
('source_group', self.fake_volume_group.id),
277+
]
278+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
279+
280+
exc = self.assertRaises(
281+
exceptions.CommandError,
282+
self.cmd.take_action,
283+
parsed_args)
284+
self.assertIn(
285+
'--os-volume-api-version 3.14 or greater is required',
286+
str(exc))
287+
288+
def test_volume_group_create_from_src_source_group_group_snapshot(self):
289+
self.app.client_manager.volume.api_version = \
290+
api_versions.APIVersion('3.14')
291+
292+
arglist = [
293+
'--source-group', self.fake_volume_group.id,
294+
'--group-snapshot', self.fake_volume_group_snapshot.id,
295+
]
296+
verifylist = [
297+
('source_group', self.fake_volume_group.id),
298+
('group_snapshot', self.fake_volume_group_snapshot.id),
299+
]
300+
301+
exc = self.assertRaises(tests_utils.ParserException,
302+
self.check_parser,
303+
self.cmd,
304+
arglist,
305+
verifylist)
306+
self.assertIn(
307+
'--group-snapshot: not allowed with argument --source-group',
308+
str(exc))
309+
179310

180311
class TestVolumeGroupDelete(TestVolumeGroup):
181312

openstackclient/volume/v3/volume_group.py

Lines changed: 88 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -82,15 +82,17 @@ class CreateVolumeGroup(command.ShowOne):
8282

8383
def get_parser(self, prog_name):
8484
parser = super().get_parser(prog_name)
85-
parser.add_argument(
85+
source_parser = parser.add_mutually_exclusive_group()
86+
source_parser.add_argument(
8687
'volume_group_type',
8788
metavar='<volume_group_type>',
89+
nargs='?',
8890
help=_('Name or ID of volume group type to use.'),
8991
)
9092
parser.add_argument(
9193
'volume_types',
9294
metavar='<volume_type>',
93-
nargs='+',
95+
nargs='*',
9496
default=[],
9597
help=_('Name or ID of volume type(s) to use.'),
9698
)
@@ -107,44 +109,101 @@ def get_parser(self, prog_name):
107109
parser.add_argument(
108110
'--availability-zone',
109111
metavar='<availability-zone>',
110-
help=_('Availability zone for volume group.'),
112+
help=_('Availability zone for volume group. '
113+
'(not available if creating group from source)'),
114+
)
115+
source_parser.add_argument(
116+
'--source-group',
117+
metavar='<source-group>',
118+
help=_('Existing volume group (name or ID) '
119+
'(supported by --os-volume-api-version 3.14 or later)'),
120+
)
121+
source_parser.add_argument(
122+
'--group-snapshot',
123+
metavar='<group-snapshot>',
124+
help=_('Existing group snapshot (name or ID) '
125+
'(supported by --os-volume-api-version 3.14 or later)'),
111126
)
112127
return parser
113128

114129
def take_action(self, parsed_args):
115130
volume_client = self.app.client_manager.volume
116131

117-
if volume_client.api_version < api_versions.APIVersion('3.13'):
118-
msg = _(
119-
"--os-volume-api-version 3.13 or greater is required to "
120-
"support the 'volume group create' command"
121-
)
122-
raise exceptions.CommandError(msg)
123-
124-
volume_group_type = utils.find_resource(
125-
volume_client.group_types,
126-
parsed_args.volume_group_type,
127-
)
128-
129-
volume_types = []
130-
for volume_type in parsed_args.volume_types:
131-
volume_types.append(
132-
utils.find_resource(
133-
volume_client.volume_types,
134-
volume_type,
132+
if parsed_args.volume_group_type:
133+
if volume_client.api_version < api_versions.APIVersion('3.13'):
134+
msg = _(
135+
"--os-volume-api-version 3.13 or greater is required to "
136+
"support the 'volume group create' command"
135137
)
138+
raise exceptions.CommandError(msg)
139+
if not parsed_args.volume_types:
140+
msg = _(
141+
"<volume_types> is a required argument when creating a "
142+
"group from group type."
143+
)
144+
raise exceptions.CommandError(msg)
145+
146+
volume_group_type = utils.find_resource(
147+
volume_client.group_types,
148+
parsed_args.volume_group_type,
136149
)
150+
volume_types = []
151+
for volume_type in parsed_args.volume_types:
152+
volume_types.append(
153+
utils.find_resource(
154+
volume_client.volume_types,
155+
volume_type,
156+
)
157+
)
137158

138-
group = volume_client.groups.create(
139-
volume_group_type.id,
140-
','.join(x.id for x in volume_types),
141-
parsed_args.name,
142-
parsed_args.description,
143-
availability_zone=parsed_args.availability_zone)
159+
group = volume_client.groups.create(
160+
volume_group_type.id,
161+
','.join(x.id for x in volume_types),
162+
parsed_args.name,
163+
parsed_args.description,
164+
availability_zone=parsed_args.availability_zone)
144165

145-
group = volume_client.groups.get(group.id)
166+
group = volume_client.groups.get(group.id)
167+
return _format_group(group)
146168

147-
return _format_group(group)
169+
else:
170+
if volume_client.api_version < api_versions.APIVersion('3.14'):
171+
msg = _(
172+
"--os-volume-api-version 3.14 or greater is required to "
173+
"support the 'volume group create "
174+
"[--source-group|--group-snapshot]' command"
175+
)
176+
raise exceptions.CommandError(msg)
177+
if (parsed_args.source_group is None and
178+
parsed_args.group_snapshot is None):
179+
msg = _(
180+
"Either --source-group <source_group> or "
181+
"'--group-snapshot <group_snapshot>' needs to be "
182+
"provided to run the 'volume group create "
183+
"[--source-group|--group-snapshot]' command"
184+
)
185+
raise exceptions.CommandError(msg)
186+
if parsed_args.availability_zone:
187+
msg = _("'--availability-zone' option will not work "
188+
"if creating group from source.")
189+
LOG.warning(msg)
190+
191+
source_group = None
192+
if parsed_args.source_group:
193+
source_group = utils.find_resource(volume_client.groups,
194+
parsed_args.source_group)
195+
group_snapshot = None
196+
if parsed_args.group_snapshot:
197+
group_snapshot = utils.find_resource(
198+
volume_client.group_snapshots,
199+
parsed_args.group_snapshot)
200+
group = volume_client.groups.create_from_src(
201+
group_snapshot.id if group_snapshot else None,
202+
source_group.id if source_group else None,
203+
parsed_args.name,
204+
parsed_args.description)
205+
group = volume_client.groups.get(group.id)
206+
return _format_group(group)
148207

149208

150209
class DeleteVolumeGroup(command.Command):
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
features:
3+
- |
4+
Added ``--source-group`` and ``--group-snapshot`` options to the
5+
``volume group create`` command to allow creating group from
6+
a source group or a group snapshot.

0 commit comments

Comments
 (0)