Skip to content

Commit f5c09fe

Browse files
Jenkinsopenstack-gerrit
authored andcommitted
Merge "Support error handling for delete commands in volume v1"
2 parents 7d3d2b0 + af81a92 commit f5c09fe

7 files changed

Lines changed: 411 additions & 33 deletions

File tree

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

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,10 @@
1313
# under the License.
1414
#
1515

16+
import copy
1617
import mock
18+
import random
19+
import uuid
1720

1821
from openstackclient.tests.unit import fakes
1922
from openstackclient.tests.unit.identity.v2_0 import fakes as identity_fakes
@@ -234,6 +237,159 @@ def get_services(services=None, count=2):
234237
return mock.MagicMock(side_effect=services)
235238

236239

240+
class FakeQos(object):
241+
"""Fake one or more Qos specification."""
242+
243+
@staticmethod
244+
def create_one_qos(attrs=None):
245+
"""Create a fake Qos specification.
246+
247+
:param Dictionary attrs:
248+
A dictionary with all attributes
249+
:return:
250+
A FakeResource object with id, name, consumer, etc.
251+
"""
252+
attrs = attrs or {}
253+
254+
# Set default attributes.
255+
qos_info = {
256+
"id": 'qos-id-' + uuid.uuid4().hex,
257+
"name": 'qos-name-' + uuid.uuid4().hex,
258+
"consumer": 'front-end',
259+
"specs": {"foo": "bar", "iops": "9001"},
260+
}
261+
262+
# Overwrite default attributes.
263+
qos_info.update(attrs)
264+
265+
qos = fakes.FakeResource(
266+
info=copy.deepcopy(qos_info),
267+
loaded=True)
268+
return qos
269+
270+
@staticmethod
271+
def create_qoses(attrs=None, count=2):
272+
"""Create multiple fake Qos specifications.
273+
274+
:param Dictionary attrs:
275+
A dictionary with all attributes
276+
:param int count:
277+
The number of Qos specifications to fake
278+
:return:
279+
A list of FakeResource objects faking the Qos specifications
280+
"""
281+
qoses = []
282+
for i in range(0, count):
283+
qos = FakeQos.create_one_qos(attrs)
284+
qoses.append(qos)
285+
286+
return qoses
287+
288+
@staticmethod
289+
def get_qoses(qoses=None, count=2):
290+
"""Get an iterable MagicMock object with a list of faked qoses.
291+
292+
If qoses list is provided, then initialize the Mock object with the
293+
list. Otherwise create one.
294+
295+
:param List volumes:
296+
A list of FakeResource objects faking qoses
297+
:param Integer count:
298+
The number of qoses to be faked
299+
:return
300+
An iterable Mock object with side_effect set to a list of faked
301+
qoses
302+
"""
303+
if qoses is None:
304+
qoses = FakeQos.create_qoses(count)
305+
306+
return mock.MagicMock(side_effect=qoses)
307+
308+
309+
class FakeVolume(object):
310+
"""Fake one or more volumes."""
311+
312+
@staticmethod
313+
def create_one_volume(attrs=None):
314+
"""Create a fake volume.
315+
316+
:param Dictionary attrs:
317+
A dictionary with all attributes of volume
318+
:return:
319+
A FakeResource object with id, name, status, etc.
320+
"""
321+
attrs = attrs or {}
322+
323+
# Set default attribute
324+
volume_info = {
325+
'id': 'volume-id' + uuid.uuid4().hex,
326+
'name': 'volume-name' + uuid.uuid4().hex,
327+
'description': 'description' + uuid.uuid4().hex,
328+
'status': random.choice(['available', 'in_use']),
329+
'size': random.randint(1, 20),
330+
'volume_type':
331+
random.choice(['fake_lvmdriver-1', 'fake_lvmdriver-2']),
332+
'bootable':
333+
random.randint(0, 1),
334+
'metadata': {
335+
'key' + uuid.uuid4().hex: 'val' + uuid.uuid4().hex,
336+
'key' + uuid.uuid4().hex: 'val' + uuid.uuid4().hex,
337+
'key' + uuid.uuid4().hex: 'val' + uuid.uuid4().hex},
338+
'snapshot_id': random.randint(1, 5),
339+
'availability_zone': 'zone' + uuid.uuid4().hex,
340+
'attachments': [{
341+
'device': '/dev/' + uuid.uuid4().hex,
342+
'server_id': uuid.uuid4().hex,
343+
}, ],
344+
}
345+
346+
# Overwrite default attributes if there are some attributes set
347+
volume_info.update(attrs)
348+
349+
volume = fakes.FakeResource(
350+
None,
351+
volume_info,
352+
loaded=True)
353+
return volume
354+
355+
@staticmethod
356+
def create_volumes(attrs=None, count=2):
357+
"""Create multiple fake volumes.
358+
359+
:param Dictionary attrs:
360+
A dictionary with all attributes of volume
361+
:param Integer count:
362+
The number of volumes to be faked
363+
:return:
364+
A list of FakeResource objects
365+
"""
366+
volumes = []
367+
for n in range(0, count):
368+
volumes.append(FakeVolume.create_one_volume(attrs))
369+
370+
return volumes
371+
372+
@staticmethod
373+
def get_volumes(volumes=None, count=2):
374+
"""Get an iterable MagicMock object with a list of faked volumes.
375+
376+
If volumes list is provided, then initialize the Mock object with the
377+
list. Otherwise create one.
378+
379+
:param List volumes:
380+
A list of FakeResource objects faking volumes
381+
:param Integer count:
382+
The number of volumes to be faked
383+
:return
384+
An iterable Mock object with side_effect set to a list of faked
385+
volumes
386+
"""
387+
if volumes is None:
388+
volumes = FakeVolume.create_volumes(count)
389+
390+
return mock.MagicMock(side_effect=volumes)
391+
392+
237393
class FakeImagev1Client(object):
238394

239395
def __init__(self, **kwargs):

openstackclient/tests/unit/volume/v1/test_qos_specs.py

Lines changed: 62 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,10 @@
1414
#
1515

1616
import copy
17+
import mock
18+
from mock import call
1719

20+
from osc_lib import exceptions
1821
from osc_lib import utils
1922

2023
from openstackclient.tests.unit import fakes
@@ -188,62 +191,106 @@ def test_qos_create_with_properties(self):
188191

189192
class TestQosDelete(TestQos):
190193

194+
qos_specs = volume_fakes.FakeQos.create_qoses(count=2)
195+
191196
def setUp(self):
192197
super(TestQosDelete, self).setUp()
193198

194-
self.qos_mock.get.return_value = fakes.FakeResource(
195-
None,
196-
copy.deepcopy(volume_fakes.QOS),
197-
loaded=True,
198-
)
199-
199+
self.qos_mock.get = (
200+
volume_fakes.FakeQos.get_qoses(self.qos_specs))
200201
# Get the command object to test
201202
self.cmd = qos_specs.DeleteQos(self.app, None)
202203

203204
def test_qos_delete_with_id(self):
204205
arglist = [
205-
volume_fakes.qos_id
206+
self.qos_specs[0].id
206207
]
207208
verifylist = [
208-
('qos_specs', [volume_fakes.qos_id])
209+
('qos_specs', [self.qos_specs[0].id])
209210
]
210211
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
211212

212213
result = self.cmd.take_action(parsed_args)
213214

214-
self.qos_mock.delete.assert_called_with(volume_fakes.qos_id, False)
215+
self.qos_mock.delete.assert_called_with(self.qos_specs[0].id, False)
215216
self.assertIsNone(result)
216217

217218
def test_qos_delete_with_name(self):
218219
arglist = [
219-
volume_fakes.qos_name
220+
self.qos_specs[0].name
220221
]
221222
verifylist = [
222-
('qos_specs', [volume_fakes.qos_name])
223+
('qos_specs', [self.qos_specs[0].name])
223224
]
224225
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
225226

226227
result = self.cmd.take_action(parsed_args)
227228

228-
self.qos_mock.delete.assert_called_with(volume_fakes.qos_id, False)
229+
self.qos_mock.delete.assert_called_with(self.qos_specs[0].id, False)
229230
self.assertIsNone(result)
230231

231232
def test_qos_delete_with_force(self):
232233
arglist = [
233234
'--force',
234-
volume_fakes.qos_id
235+
self.qos_specs[0].id
235236
]
236237
verifylist = [
237238
('force', True),
238-
('qos_specs', [volume_fakes.qos_id])
239+
('qos_specs', [self.qos_specs[0].id])
239240
]
240241
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
241242

242243
result = self.cmd.take_action(parsed_args)
243244

244-
self.qos_mock.delete.assert_called_with(volume_fakes.qos_id, True)
245+
self.qos_mock.delete.assert_called_with(self.qos_specs[0].id, True)
245246
self.assertIsNone(result)
246247

248+
def test_delete_multiple_qoses(self):
249+
arglist = []
250+
for q in self.qos_specs:
251+
arglist.append(q.id)
252+
verifylist = [
253+
('qos_specs', arglist),
254+
]
255+
256+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
257+
result = self.cmd.take_action(parsed_args)
258+
259+
calls = []
260+
for q in self.qos_specs:
261+
calls.append(call(q.id, False))
262+
self.qos_mock.delete.assert_has_calls(calls)
263+
self.assertIsNone(result)
264+
265+
def test_delete_multiple_qoses_with_exception(self):
266+
arglist = [
267+
self.qos_specs[0].id,
268+
'unexist_qos',
269+
]
270+
verifylist = [
271+
('qos_specs', arglist),
272+
]
273+
274+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
275+
276+
find_mock_result = [self.qos_specs[0], exceptions.CommandError]
277+
with mock.patch.object(utils, 'find_resource',
278+
side_effect=find_mock_result) as find_mock:
279+
try:
280+
self.cmd.take_action(parsed_args)
281+
self.fail('CommandError should be raised.')
282+
except exceptions.CommandError as e:
283+
self.assertEqual(
284+
'1 of 2 QoS specifications failed to delete.', str(e))
285+
286+
find_mock.assert_any_call(self.qos_mock, self.qos_specs[0].id)
287+
find_mock.assert_any_call(self.qos_mock, 'unexist_qos')
288+
289+
self.assertEqual(2, find_mock.call_count)
290+
self.qos_mock.delete.assert_called_once_with(
291+
self.qos_specs[0].id, False
292+
)
293+
247294

248295
class TestQosDisassociate(TestQos):
249296

0 commit comments

Comments
 (0)