Skip to content

Commit 3f3d882

Browse files
Zuulopenstack-gerrit
authored andcommitted
Merge "volume: Add 'volume group snapshot *' commands"
2 parents 6abfb01 + 34de2d3 commit 3f3d882

8 files changed

Lines changed: 573 additions & 4 deletions

File tree

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
=====================
2+
volume group snapshot
3+
=====================
4+
5+
Block Storage v3
6+
7+
.. autoprogram-cliff:: openstack.volume.v3
8+
:command: volume group snapshot *

doc/source/cli/commands.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,7 @@ referring to both Compute and Volume quotas.
160160
* ``volume backup record``: (**Volume**) volume record that can be imported or exported
161161
* ``volume backend``: (**Volume**) volume backend storage
162162
* ``volume group``: (**Volume**) group of volumes
163+
* ``volume group snapshot``: (**Volume**) a point-in-time copy of a volume group
163164
* ``volume group type``: (**Volume**) deployment-specific types of volumes groups available
164165
* ``volume host``: (**Volume**) the physical computer for volumes
165166
* ``volume message``: (**Volume**) volume API internal messages detailing volume failure messages

doc/source/cli/data/cinder.csv

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,10 @@ group-failover-replication,volume group failover,Fails over replication for grou
5353
group-list,volume group list,Lists all groups. (Supported by API versions 3.13 - 3.latest)
5454
group-list-replication-targets,volume group list --replication-targets,Lists replication targets for group. (Supported by API versions 3.38 - 3.latest)
5555
group-show,volume group show,Shows details of a group. (Supported by API versions 3.13 - 3.latest)
56-
group-snapshot-create,,Creates a group snapshot. (Supported by API versions 3.14 - 3.latest)
57-
group-snapshot-delete,,Removes one or more group snapshots. (Supported by API versions 3.14 - 3.latest)
58-
group-snapshot-list,,Lists all group snapshots. (Supported by API versions 3.14 - 3.latest)
59-
group-snapshot-show,,Shows group snapshot details. (Supported by API versions 3.14 - 3.latest)
56+
group-snapshot-create,volume group snapshot create,Creates a group snapshot. (Supported by API versions 3.14 - 3.latest)
57+
group-snapshot-delete,volume group snapshot delete,Removes one or more group snapshots. (Supported by API versions 3.14 - 3.latest)
58+
group-snapshot-list,volume group snapshot list,Lists all group snapshots. (Supported by API versions 3.14 - 3.latest)
59+
group-snapshot-show,volume group snapshot show,Shows group snapshot details. (Supported by API versions 3.14 - 3.latest)
6060
group-specs-list,volume group type list,Lists current group types and specs. (Supported by API versions 3.11 - 3.latest)
6161
group-type-create,volume group type create,Creates a group type. (Supported by API versions 3.11 - 3.latest)
6262
group-type-default,volume group type list --default,List the default group type. (Supported by API versions 3.11 - 3.latest)

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

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ def __init__(self, **kwargs):
3434
self.attachments.resource_class = fakes.FakeResource(None, {})
3535
self.groups = mock.Mock()
3636
self.groups.resource_class = fakes.FakeResource(None, {})
37+
self.group_snapshots = mock.Mock()
38+
self.group_snapshots.resource_class = fakes.FakeResource(None, {})
3739
self.group_types = mock.Mock()
3840
self.group_types.resource_class = fakes.FakeResource(None, {})
3941
self.messages = mock.Mock()
@@ -125,6 +127,57 @@ def create_volume_groups(attrs=None, count=2):
125127
return groups
126128

127129

130+
class FakeVolumeGroupSnapshot:
131+
"""Fake one or more volume group snapshots."""
132+
133+
@staticmethod
134+
def create_one_volume_group_snapshot(attrs=None, methods=None):
135+
"""Create a fake group snapshot.
136+
137+
:param attrs: A dictionary with all attributes
138+
:param methods: A dictionary with all methods
139+
:return: A FakeResource object with id, name, description, etc.
140+
"""
141+
attrs = attrs or {}
142+
143+
# Set default attribute
144+
group_snapshot_info = {
145+
'id': uuid.uuid4().hex,
146+
'name': f'group-snapshot-{uuid.uuid4().hex}',
147+
'description': f'description-{uuid.uuid4().hex}',
148+
'status': random.choice(['available']),
149+
'group_id': uuid.uuid4().hex,
150+
'group_type_id': uuid.uuid4().hex,
151+
'project_id': uuid.uuid4().hex,
152+
}
153+
154+
# Overwrite default attributes if there are some attributes set
155+
group_snapshot_info.update(attrs)
156+
157+
group_snapshot = fakes.FakeResource(
158+
None,
159+
group_snapshot_info,
160+
methods=methods,
161+
loaded=True)
162+
return group_snapshot
163+
164+
@staticmethod
165+
def create_volume_group_snapshots(attrs=None, count=2):
166+
"""Create multiple fake group snapshots.
167+
168+
:param attrs: A dictionary with all attributes of group snapshot
169+
:param count: The number of group snapshots to be faked
170+
:return: A list of FakeResource objects
171+
"""
172+
group_snapshots = []
173+
for n in range(0, count):
174+
group_snapshots.append(
175+
FakeVolumeGroupSnapshot.create_one_volume_group_snapshot(attrs)
176+
)
177+
178+
return group_snapshots
179+
180+
128181
class FakeVolumeGroupType:
129182
"""Fake one or more volume group types."""
130183

Lines changed: 262 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,262 @@
1+
# Licensed under the Apache License, Version 2.0 (the "License"); you may
2+
# not use this file except in compliance with the License. You may obtain
3+
# a copy of the License at
4+
#
5+
# http://www.apache.org/licenses/LICENSE-2.0
6+
#
7+
# Unless required by applicable law or agreed to in writing, software
8+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
9+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
10+
# License for the specific language governing permissions and limitations
11+
# under the License.
12+
13+
from cinderclient import api_versions
14+
from osc_lib import exceptions
15+
16+
from openstackclient.tests.unit.volume.v3 import fakes as volume_fakes
17+
from openstackclient.volume.v3 import volume_group_snapshot
18+
19+
20+
class TestVolumeGroupSnapshot(volume_fakes.TestVolume):
21+
22+
def setUp(self):
23+
super().setUp()
24+
25+
self.volume_groups_mock = self.app.client_manager.volume.groups
26+
self.volume_groups_mock.reset_mock()
27+
28+
self.volume_group_snapshots_mock = \
29+
self.app.client_manager.volume.group_snapshots
30+
self.volume_group_snapshots_mock.reset_mock()
31+
32+
33+
class TestVolumeGroupSnapshotCreate(TestVolumeGroupSnapshot):
34+
35+
fake_volume_group = volume_fakes.FakeVolumeGroup.create_one_volume_group()
36+
fake_volume_group_snapshot = \
37+
volume_fakes.FakeVolumeGroupSnapshot.create_one_volume_group_snapshot()
38+
39+
columns = (
40+
'ID',
41+
'Status',
42+
'Name',
43+
'Description',
44+
'Group',
45+
'Group Type',
46+
)
47+
data = (
48+
fake_volume_group_snapshot.id,
49+
fake_volume_group_snapshot.status,
50+
fake_volume_group_snapshot.name,
51+
fake_volume_group_snapshot.description,
52+
fake_volume_group_snapshot.group_id,
53+
fake_volume_group_snapshot.group_type_id,
54+
)
55+
56+
def setUp(self):
57+
super().setUp()
58+
59+
self.volume_groups_mock.get.return_value = self.fake_volume_group
60+
self.volume_group_snapshots_mock.create.return_value = \
61+
self.fake_volume_group_snapshot
62+
self.volume_group_snapshots_mock.get.return_value = \
63+
self.fake_volume_group_snapshot
64+
65+
self.cmd = volume_group_snapshot.CreateVolumeGroupSnapshot(
66+
self.app, None)
67+
68+
def test_volume_group_snapshot_create(self):
69+
self.app.client_manager.volume.api_version = \
70+
api_versions.APIVersion('3.14')
71+
72+
arglist = [
73+
self.fake_volume_group.id,
74+
]
75+
verifylist = [
76+
('volume_group', self.fake_volume_group.id),
77+
('name', None),
78+
('description', None),
79+
]
80+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
81+
82+
columns, data = self.cmd.take_action(parsed_args)
83+
84+
self.volume_groups_mock.get.assert_called_once_with(
85+
self.fake_volume_group.id)
86+
self.volume_group_snapshots_mock.create.assert_called_once_with(
87+
self.fake_volume_group.id, None, None,
88+
)
89+
self.assertEqual(self.columns, columns)
90+
self.assertCountEqual(self.data, data)
91+
92+
def test_volume_group_snapshot_create_with_options(self):
93+
self.app.client_manager.volume.api_version = \
94+
api_versions.APIVersion('3.14')
95+
96+
arglist = [
97+
self.fake_volume_group.id,
98+
'--name', 'foo',
99+
'--description', 'hello, world',
100+
]
101+
verifylist = [
102+
('volume_group', self.fake_volume_group.id),
103+
('name', 'foo'),
104+
('description', 'hello, world'),
105+
]
106+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
107+
108+
columns, data = self.cmd.take_action(parsed_args)
109+
110+
self.volume_groups_mock.get.assert_called_once_with(
111+
self.fake_volume_group.id)
112+
self.volume_group_snapshots_mock.create.assert_called_once_with(
113+
self.fake_volume_group.id, 'foo', 'hello, world',
114+
)
115+
self.assertEqual(self.columns, columns)
116+
self.assertCountEqual(self.data, data)
117+
118+
def test_volume_group_snapshot_create_pre_v314(self):
119+
self.app.client_manager.volume.api_version = \
120+
api_versions.APIVersion('3.13')
121+
122+
arglist = [
123+
self.fake_volume_group.id,
124+
]
125+
verifylist = [
126+
('volume_group', self.fake_volume_group.id),
127+
('name', None),
128+
('description', None),
129+
]
130+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
131+
132+
exc = self.assertRaises(
133+
exceptions.CommandError,
134+
self.cmd.take_action,
135+
parsed_args)
136+
self.assertIn(
137+
'--os-volume-api-version 3.14 or greater is required',
138+
str(exc))
139+
140+
141+
class TestVolumeGroupSnapshotDelete(TestVolumeGroupSnapshot):
142+
143+
fake_volume_group_snapshot = \
144+
volume_fakes.FakeVolumeGroupSnapshot.create_one_volume_group_snapshot()
145+
146+
def setUp(self):
147+
super().setUp()
148+
149+
self.volume_group_snapshots_mock.get.return_value = \
150+
self.fake_volume_group_snapshot
151+
self.volume_group_snapshots_mock.delete.return_value = None
152+
153+
self.cmd = volume_group_snapshot.DeleteVolumeGroupSnapshot(
154+
self.app, None)
155+
156+
def test_volume_group_snapshot_delete(self):
157+
self.app.client_manager.volume.api_version = \
158+
api_versions.APIVersion('3.14')
159+
160+
arglist = [
161+
self.fake_volume_group_snapshot.id,
162+
]
163+
verifylist = [
164+
('snapshot', self.fake_volume_group_snapshot.id),
165+
]
166+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
167+
168+
result = self.cmd.take_action(parsed_args)
169+
170+
self.volume_group_snapshots_mock.delete.assert_called_once_with(
171+
self.fake_volume_group_snapshot.id,
172+
)
173+
self.assertIsNone(result)
174+
175+
def test_volume_group_snapshot_delete_pre_v314(self):
176+
self.app.client_manager.volume.api_version = \
177+
api_versions.APIVersion('3.13')
178+
179+
arglist = [
180+
self.fake_volume_group_snapshot.id,
181+
]
182+
verifylist = [
183+
('snapshot', self.fake_volume_group_snapshot.id),
184+
]
185+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
186+
187+
exc = self.assertRaises(
188+
exceptions.CommandError,
189+
self.cmd.take_action,
190+
parsed_args)
191+
self.assertIn(
192+
'--os-volume-api-version 3.14 or greater is required',
193+
str(exc))
194+
195+
196+
class TestVolumeGroupSnapshotList(TestVolumeGroupSnapshot):
197+
198+
fake_volume_group_snapshots = \
199+
volume_fakes.FakeVolumeGroupSnapshot.create_volume_group_snapshots()
200+
201+
columns = (
202+
'ID',
203+
'Status',
204+
'Name',
205+
)
206+
data = [
207+
(
208+
fake_volume_group_snapshot.id,
209+
fake_volume_group_snapshot.status,
210+
fake_volume_group_snapshot.name,
211+
) for fake_volume_group_snapshot in fake_volume_group_snapshots
212+
]
213+
214+
def setUp(self):
215+
super().setUp()
216+
217+
self.volume_group_snapshots_mock.list.return_value = \
218+
self.fake_volume_group_snapshots
219+
220+
self.cmd = volume_group_snapshot.ListVolumeGroupSnapshot(
221+
self.app, None)
222+
223+
def test_volume_group_snapshot_list(self):
224+
self.app.client_manager.volume.api_version = \
225+
api_versions.APIVersion('3.14')
226+
227+
arglist = [
228+
'--all-projects',
229+
]
230+
verifylist = [
231+
('all_projects', True),
232+
]
233+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
234+
235+
columns, data = self.cmd.take_action(parsed_args)
236+
237+
self.volume_group_snapshots_mock.list.assert_called_once_with(
238+
search_opts={
239+
'all_tenants': True,
240+
},
241+
)
242+
self.assertEqual(self.columns, columns)
243+
self.assertCountEqual(tuple(self.data), data)
244+
245+
def test_volume_group_snapshot_list_pre_v314(self):
246+
self.app.client_manager.volume.api_version = \
247+
api_versions.APIVersion('3.13')
248+
249+
arglist = [
250+
]
251+
verifylist = [
252+
('all_projects', False),
253+
]
254+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
255+
256+
exc = self.assertRaises(
257+
exceptions.CommandError,
258+
self.cmd.take_action,
259+
parsed_args)
260+
self.assertIn(
261+
'--os-volume-api-version 3.14 or greater is required',
262+
str(exc))

0 commit comments

Comments
 (0)