python-openstackclient 6.1.0__py3-none-any.whl → 6.2.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. openstackclient/common/project_cleanup.py +3 -5
  2. openstackclient/common/quota.py +4 -6
  3. openstackclient/compute/v2/host.py +44 -16
  4. openstackclient/compute/v2/server.py +18 -24
  5. openstackclient/compute/v2/server_migration.py +53 -29
  6. openstackclient/compute/v2/server_volume.py +35 -38
  7. openstackclient/compute/v2/service.py +5 -5
  8. openstackclient/identity/common.py +12 -0
  9. openstackclient/identity/v3/access_rule.py +8 -6
  10. openstackclient/image/v2/image.py +8 -2
  11. openstackclient/network/v2/floating_ip_port_forwarding.py +70 -32
  12. openstackclient/network/v2/network_service_provider.py +16 -10
  13. openstackclient/tests/functional/compute/v2/test_server.py +0 -3
  14. openstackclient/tests/unit/compute/v2/fakes.py +122 -207
  15. openstackclient/tests/unit/compute/v2/test_host.py +63 -42
  16. openstackclient/tests/unit/compute/v2/test_server.py +1 -2
  17. openstackclient/tests/unit/compute/v2/test_server_migration.py +92 -95
  18. openstackclient/tests/unit/compute/v2/test_server_volume.py +103 -83
  19. openstackclient/tests/unit/identity/v3/test_access_rule.py +11 -11
  20. openstackclient/tests/unit/network/v2/fakes.py +31 -7
  21. openstackclient/tests/unit/network/v2/test_floating_ip_port_forwarding.py +213 -18
  22. openstackclient/tests/unit/volume/v1/test_volume.py +48 -3
  23. openstackclient/tests/unit/volume/v2/test_consistency_group.py +2 -2
  24. openstackclient/tests/unit/volume/v2/test_volume.py +50 -3
  25. openstackclient/tests/unit/volume/v3/test_volume_group.py +47 -8
  26. openstackclient/volume/v1/volume.py +33 -4
  27. openstackclient/volume/v2/backup_record.py +8 -6
  28. openstackclient/volume/v2/consistency_group.py +32 -13
  29. openstackclient/volume/v2/volume.py +33 -4
  30. openstackclient/volume/v3/volume_group.py +91 -36
  31. {python_openstackclient-6.1.0.dist-info → python_openstackclient-6.2.1.dist-info}/AUTHORS +5 -0
  32. {python_openstackclient-6.1.0.dist-info → python_openstackclient-6.2.1.dist-info}/METADATA +1 -1
  33. {python_openstackclient-6.1.0.dist-info → python_openstackclient-6.2.1.dist-info}/RECORD +38 -38
  34. {python_openstackclient-6.1.0.dist-info → python_openstackclient-6.2.1.dist-info}/entry_points.txt +1 -0
  35. python_openstackclient-6.2.1.dist-info/pbr.json +1 -0
  36. python_openstackclient-6.1.0.dist-info/pbr.json +0 -1
  37. {python_openstackclient-6.1.0.dist-info → python_openstackclient-6.2.1.dist-info}/LICENSE +0 -0
  38. {python_openstackclient-6.1.0.dist-info → python_openstackclient-6.2.1.dist-info}/WHEEL +0 -0
  39. {python_openstackclient-6.1.0.dist-info → python_openstackclient-6.2.1.dist-info}/top_level.txt +0 -0
@@ -49,6 +49,18 @@ class TestCreateFloatingIPPortForwarding(TestFloatingIPPortForwarding):
49
49
  }
50
50
  )
51
51
  )
52
+
53
+ self.new_port_forwarding_with_ranges = (
54
+ network_fakes.FakeFloatingIPPortForwarding.
55
+ create_one_port_forwarding(
56
+ use_range=True,
57
+ attrs={
58
+ 'internal_port_id': self.port.id,
59
+ 'floatingip_id': self.floating_ip.id,
60
+ }
61
+ )
62
+ )
63
+
52
64
  self.network.create_floating_ip_port_forwarding = mock.Mock(
53
65
  return_value=self.new_port_forwarding)
54
66
 
@@ -63,22 +75,26 @@ class TestCreateFloatingIPPortForwarding(TestFloatingIPPortForwarding):
63
75
  self.columns = (
64
76
  'description',
65
77
  'external_port',
78
+ 'external_port_range',
66
79
  'floatingip_id',
67
80
  'id',
68
81
  'internal_ip_address',
69
82
  'internal_port',
70
83
  'internal_port_id',
84
+ 'internal_port_range',
71
85
  'protocol'
72
86
  )
73
87
 
74
88
  self.data = (
75
89
  self.new_port_forwarding.description,
76
90
  self.new_port_forwarding.external_port,
91
+ self.new_port_forwarding.external_port_range,
77
92
  self.new_port_forwarding.floatingip_id,
78
93
  self.new_port_forwarding.id,
79
94
  self.new_port_forwarding.internal_ip_address,
80
95
  self.new_port_forwarding.internal_port,
81
96
  self.new_port_forwarding.internal_port_id,
97
+ self.new_port_forwarding.internal_port_range,
82
98
  self.new_port_forwarding.protocol,
83
99
  )
84
100
 
@@ -90,6 +106,160 @@ class TestCreateFloatingIPPortForwarding(TestFloatingIPPortForwarding):
90
106
  self.assertRaises(tests_utils.ParserException, self.check_parser,
91
107
  self.cmd, arglist, verifylist)
92
108
 
109
+ def test_create_all_options_with_range(self):
110
+ arglist = [
111
+ '--port', self.new_port_forwarding_with_ranges.internal_port_id,
112
+ '--internal-protocol-port',
113
+ self.new_port_forwarding_with_ranges.internal_port_range,
114
+ '--external-protocol-port',
115
+ self.new_port_forwarding_with_ranges.external_port_range,
116
+ '--protocol', self.new_port_forwarding_with_ranges.protocol,
117
+ self.new_port_forwarding_with_ranges.floatingip_id,
118
+ '--internal-ip-address',
119
+ self.new_port_forwarding_with_ranges.internal_ip_address,
120
+ '--description',
121
+ self.new_port_forwarding_with_ranges.description,
122
+ ]
123
+ verifylist = [
124
+ ('port', self.new_port_forwarding_with_ranges.internal_port_id),
125
+ ('internal_protocol_port',
126
+ self.new_port_forwarding_with_ranges.internal_port_range),
127
+ ('external_protocol_port',
128
+ self.new_port_forwarding_with_ranges.external_port_range),
129
+ ('protocol', self.new_port_forwarding_with_ranges.protocol),
130
+ ('floating_ip',
131
+ self.new_port_forwarding_with_ranges.floatingip_id),
132
+ ('internal_ip_address', self.new_port_forwarding_with_ranges.
133
+ internal_ip_address),
134
+ ('description', self.new_port_forwarding_with_ranges.description),
135
+ ]
136
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
137
+ columns, data = self.cmd.take_action(parsed_args)
138
+
139
+ self.network.create_floating_ip_port_forwarding.\
140
+ assert_called_once_with(
141
+ self.new_port_forwarding.floatingip_id,
142
+ **{
143
+ 'external_port_range':
144
+ self.new_port_forwarding_with_ranges.
145
+ external_port_range,
146
+ 'internal_ip_address':
147
+ self.new_port_forwarding_with_ranges.
148
+ internal_ip_address,
149
+ 'internal_port_range':
150
+ self.new_port_forwarding_with_ranges.
151
+ internal_port_range,
152
+ 'internal_port_id':
153
+ self.new_port_forwarding_with_ranges.internal_port_id,
154
+ 'protocol': self.new_port_forwarding_with_ranges.protocol,
155
+ 'description':
156
+ self.new_port_forwarding_with_ranges.description,
157
+ })
158
+ self.assertEqual(self.columns, columns)
159
+ self.assertEqual(self.data, data)
160
+
161
+ def test_create_all_options_with_range_invalid_port_exception(self):
162
+ invalid_port_range = '999999:999999'
163
+ arglist = [
164
+ '--port', self.new_port_forwarding_with_ranges.internal_port_id,
165
+ '--internal-protocol-port', invalid_port_range,
166
+ '--external-protocol-port', invalid_port_range,
167
+ '--protocol', self.new_port_forwarding_with_ranges.protocol,
168
+ self.new_port_forwarding_with_ranges.floatingip_id,
169
+ '--internal-ip-address',
170
+ self.new_port_forwarding_with_ranges.internal_ip_address,
171
+ '--description',
172
+ self.new_port_forwarding_with_ranges.description,
173
+ ]
174
+ verifylist = [
175
+ ('port', self.new_port_forwarding_with_ranges.internal_port_id),
176
+ ('internal_protocol_port', invalid_port_range),
177
+ ('external_protocol_port', invalid_port_range),
178
+ ('protocol', self.new_port_forwarding_with_ranges.protocol),
179
+ ('floating_ip',
180
+ self.new_port_forwarding_with_ranges.floatingip_id),
181
+ ('internal_ip_address', self.new_port_forwarding_with_ranges.
182
+ internal_ip_address),
183
+ ('description', self.new_port_forwarding_with_ranges.description),
184
+ ]
185
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
186
+ msg = 'The port number range is <1-65535>'
187
+ try:
188
+ self.cmd.take_action(parsed_args)
189
+ self.fail('CommandError should be raised.')
190
+ except exceptions.CommandError as e:
191
+ self.assertEqual(msg, str(e))
192
+ self.network.create_floating_ip_port_forwarding.assert_not_called()
193
+
194
+ def test_create_all_options_with_invalid_range_exception(self):
195
+ invalid_port_range = '80:70'
196
+ arglist = [
197
+ '--port', self.new_port_forwarding_with_ranges.internal_port_id,
198
+ '--internal-protocol-port', invalid_port_range,
199
+ '--external-protocol-port', invalid_port_range,
200
+ '--protocol', self.new_port_forwarding_with_ranges.protocol,
201
+ self.new_port_forwarding_with_ranges.floatingip_id,
202
+ '--internal-ip-address',
203
+ self.new_port_forwarding_with_ranges.internal_ip_address,
204
+ '--description',
205
+ self.new_port_forwarding_with_ranges.description,
206
+ ]
207
+ verifylist = [
208
+ ('port', self.new_port_forwarding_with_ranges.internal_port_id),
209
+ ('internal_protocol_port', invalid_port_range),
210
+ ('external_protocol_port', invalid_port_range),
211
+ ('protocol', self.new_port_forwarding_with_ranges.protocol),
212
+ ('floating_ip',
213
+ self.new_port_forwarding_with_ranges.floatingip_id),
214
+ ('internal_ip_address', self.new_port_forwarding_with_ranges.
215
+ internal_ip_address),
216
+ ('description', self.new_port_forwarding_with_ranges.description),
217
+ ]
218
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
219
+ msg = 'The last number in port range must be greater or equal to ' \
220
+ 'the first'
221
+ try:
222
+ self.cmd.take_action(parsed_args)
223
+ self.fail('CommandError should be raised.')
224
+ except exceptions.CommandError as e:
225
+ self.assertEqual(msg, str(e))
226
+ self.network.create_floating_ip_port_forwarding.assert_not_called()
227
+
228
+ def test_create_all_options_with_unmatch_ranges_exception(self):
229
+ internal_range = '80:90'
230
+ external_range = '8080:8100'
231
+ arglist = [
232
+ '--port', self.new_port_forwarding_with_ranges.internal_port_id,
233
+ '--internal-protocol-port', internal_range,
234
+ '--external-protocol-port', external_range,
235
+ '--protocol', self.new_port_forwarding_with_ranges.protocol,
236
+ self.new_port_forwarding_with_ranges.floatingip_id,
237
+ '--internal-ip-address',
238
+ self.new_port_forwarding_with_ranges.internal_ip_address,
239
+ '--description',
240
+ self.new_port_forwarding_with_ranges.description,
241
+ ]
242
+ verifylist = [
243
+ ('port', self.new_port_forwarding_with_ranges.internal_port_id),
244
+ ('internal_protocol_port', internal_range),
245
+ ('external_protocol_port', external_range),
246
+ ('protocol', self.new_port_forwarding_with_ranges.protocol),
247
+ ('floating_ip',
248
+ self.new_port_forwarding_with_ranges.floatingip_id),
249
+ ('internal_ip_address', self.new_port_forwarding_with_ranges.
250
+ internal_ip_address),
251
+ ('description', self.new_port_forwarding_with_ranges.description),
252
+ ]
253
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
254
+ msg = "The relation between internal and external ports does not " \
255
+ "match the pattern 1:N and N:N"
256
+ try:
257
+ self.cmd.take_action(parsed_args)
258
+ self.fail('CommandError should be raised.')
259
+ except exceptions.CommandError as e:
260
+ self.assertEqual(msg, str(e))
261
+ self.network.create_floating_ip_port_forwarding.assert_not_called()
262
+
93
263
  def test_create_all_options(self):
94
264
  arglist = [
95
265
  '--port', self.new_port_forwarding.internal_port_id,
@@ -106,8 +276,10 @@ class TestCreateFloatingIPPortForwarding(TestFloatingIPPortForwarding):
106
276
  ]
107
277
  verifylist = [
108
278
  ('port', self.new_port_forwarding.internal_port_id),
109
- ('internal_protocol_port', self.new_port_forwarding.internal_port),
110
- ('external_protocol_port', self.new_port_forwarding.external_port),
279
+ ('internal_protocol_port',
280
+ str(self.new_port_forwarding.internal_port)),
281
+ ('external_protocol_port',
282
+ str(self.new_port_forwarding.external_port)),
111
283
  ('protocol', self.new_port_forwarding.protocol),
112
284
  ('floating_ip', self.new_port_forwarding.floatingip_id),
113
285
  ('internal_ip_address', self.new_port_forwarding.
@@ -253,7 +425,9 @@ class TestListFloatingIPPortForwarding(TestFloatingIPPortForwarding):
253
425
  'Internal Port ID',
254
426
  'Internal IP Address',
255
427
  'Internal Port',
428
+ 'Internal Port Range',
256
429
  'External Port',
430
+ 'External Port Range',
257
431
  'Protocol',
258
432
  'Description',
259
433
  )
@@ -275,7 +449,9 @@ class TestListFloatingIPPortForwarding(TestFloatingIPPortForwarding):
275
449
  port_forwarding.internal_port_id,
276
450
  port_forwarding.internal_ip_address,
277
451
  port_forwarding.internal_port,
452
+ port_forwarding.internal_port_range,
278
453
  port_forwarding.external_port,
454
+ port_forwarding.external_port_range,
279
455
  port_forwarding.protocol,
280
456
  port_forwarding.description,
281
457
  ))
@@ -330,7 +506,7 @@ class TestListFloatingIPPortForwarding(TestFloatingIPPortForwarding):
330
506
 
331
507
  query = {
332
508
  'internal_port_id': self.port_forwardings[0].internal_port_id,
333
- 'external_port': str(self.port_forwardings[0].external_port),
509
+ 'external_port': self.port_forwardings[0].external_port,
334
510
  'protocol': self.port_forwardings[0].protocol,
335
511
  }
336
512
 
@@ -392,7 +568,7 @@ class TestSetFloatingIPPortForwarding(TestFloatingIPPortForwarding):
392
568
  self.assertIsNone(result)
393
569
 
394
570
  def test_set_all_thing(self):
395
- arglist = [
571
+ arglist_single = [
396
572
  '--port', self.port.id,
397
573
  '--internal-ip-address', 'new_internal_ip_address',
398
574
  '--internal-protocol-port', '100',
@@ -402,21 +578,23 @@ class TestSetFloatingIPPortForwarding(TestFloatingIPPortForwarding):
402
578
  self._port_forwarding.floatingip_id,
403
579
  self._port_forwarding.id,
404
580
  ]
405
- verifylist = [
581
+ arglist_range = list(arglist_single)
582
+ arglist_range[5] = '100:110'
583
+ arglist_range[7] = '200:210'
584
+ verifylist_single = [
406
585
  ('port', self.port.id),
407
586
  ('internal_ip_address', 'new_internal_ip_address'),
408
- ('internal_protocol_port', 100),
409
- ('external_protocol_port', 200),
587
+ ('internal_protocol_port', '100'),
588
+ ('external_protocol_port', '200'),
410
589
  ('protocol', 'tcp'),
411
590
  ('description', 'some description'),
412
591
  ('floating_ip', self._port_forwarding.floatingip_id),
413
592
  ('port_forwarding_id', self._port_forwarding.id),
414
593
  ]
415
-
416
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
417
-
418
- result = self.cmd.take_action(parsed_args)
419
- attrs = {
594
+ verifylist_range = list(verifylist_single)
595
+ verifylist_range[2] = ('internal_protocol_port', '100:110')
596
+ verifylist_range[3] = ('external_protocol_port', '200:210')
597
+ attrs_single = {
420
598
  'internal_port_id': self.port.id,
421
599
  'internal_ip_address': 'new_internal_ip_address',
422
600
  'internal_port': 100,
@@ -424,12 +602,25 @@ class TestSetFloatingIPPortForwarding(TestFloatingIPPortForwarding):
424
602
  'protocol': 'tcp',
425
603
  'description': 'some description',
426
604
  }
427
- self.network.update_floating_ip_port_forwarding.assert_called_with(
428
- self._port_forwarding.floatingip_id,
429
- self._port_forwarding.id,
430
- **attrs
431
- )
432
- self.assertIsNone(result)
605
+ attrs_range = dict(attrs_single, internal_port_range='100:110',
606
+ external_port_range='200:210')
607
+ attrs_range.pop('internal_port')
608
+ attrs_range.pop('external_port')
609
+
610
+ def run_and_validate(arglist, verifylist, attrs):
611
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
612
+
613
+ result = self.cmd.take_action(parsed_args)
614
+
615
+ self.network.update_floating_ip_port_forwarding.assert_called_with(
616
+ self._port_forwarding.floatingip_id,
617
+ self._port_forwarding.id,
618
+ **attrs
619
+ )
620
+ self.assertIsNone(result)
621
+
622
+ run_and_validate(arglist_single, verifylist_single, attrs_single)
623
+ run_and_validate(arglist_range, verifylist_range, attrs_range)
433
624
 
434
625
 
435
626
  class TestShowFloatingIPPortForwarding(TestFloatingIPPortForwarding):
@@ -438,11 +629,13 @@ class TestShowFloatingIPPortForwarding(TestFloatingIPPortForwarding):
438
629
  columns = (
439
630
  'description',
440
631
  'external_port',
632
+ 'external_port_range',
441
633
  'floatingip_id',
442
634
  'id',
443
635
  'internal_ip_address',
444
636
  'internal_port',
445
637
  'internal_port_id',
638
+ 'internal_port_range',
446
639
  'protocol',
447
640
  )
448
641
 
@@ -459,11 +652,13 @@ class TestShowFloatingIPPortForwarding(TestFloatingIPPortForwarding):
459
652
  self.data = (
460
653
  self._port_forwarding.description,
461
654
  self._port_forwarding.external_port,
655
+ self._port_forwarding.external_port_range,
462
656
  self._port_forwarding.floatingip_id,
463
657
  self._port_forwarding.id,
464
658
  self._port_forwarding.internal_ip_address,
465
659
  self._port_forwarding.internal_port,
466
660
  self._port_forwarding.internal_port_id,
661
+ self._port_forwarding.internal_port_range,
467
662
  self._port_forwarding.protocol,
468
663
  )
469
664
  self.network.find_floating_ip_port_forwarding = mock.Mock(
@@ -430,7 +430,8 @@ class TestVolumeCreate(TestVolume):
430
430
  self.assertEqual(self.columns, columns)
431
431
  self.assertCountEqual(self.datalist, data)
432
432
 
433
- def test_volume_create_with_bootable_and_readonly(self):
433
+ @mock.patch.object(utils, 'wait_for_status', return_value=True)
434
+ def test_volume_create_with_bootable_and_readonly(self, mock_wait):
434
435
  arglist = [
435
436
  '--bootable',
436
437
  '--read-only',
@@ -472,7 +473,8 @@ class TestVolumeCreate(TestVolume):
472
473
  self.volumes_mock.update_readonly_flag.assert_called_with(
473
474
  self.new_volume.id, True)
474
475
 
475
- def test_volume_create_with_nonbootable_and_readwrite(self):
476
+ @mock.patch.object(utils, 'wait_for_status', return_value=True)
477
+ def test_volume_create_with_nonbootable_and_readwrite(self, mock_wait):
476
478
  arglist = [
477
479
  '--non-bootable',
478
480
  '--read-write',
@@ -515,8 +517,9 @@ class TestVolumeCreate(TestVolume):
515
517
  self.new_volume.id, False)
516
518
 
517
519
  @mock.patch.object(volume.LOG, 'error')
520
+ @mock.patch.object(utils, 'wait_for_status', return_value=True)
518
521
  def test_volume_create_with_bootable_and_readonly_fail(
519
- self, mock_error):
522
+ self, mock_wait, mock_error):
520
523
 
521
524
  self.volumes_mock.set_bootable.side_effect = (
522
525
  exceptions.CommandError())
@@ -566,6 +569,48 @@ class TestVolumeCreate(TestVolume):
566
569
  self.volumes_mock.update_readonly_flag.assert_called_with(
567
570
  self.new_volume.id, True)
568
571
 
572
+ @mock.patch.object(volume.LOG, 'error')
573
+ @mock.patch.object(utils, 'wait_for_status', return_value=False)
574
+ def test_volume_create_non_available_with_readonly(
575
+ self, mock_wait, mock_error):
576
+ arglist = [
577
+ '--non-bootable',
578
+ '--read-only',
579
+ '--size', str(self.new_volume.size),
580
+ self.new_volume.display_name,
581
+ ]
582
+ verifylist = [
583
+ ('bootable', False),
584
+ ('non_bootable', True),
585
+ ('read_only', True),
586
+ ('read_write', False),
587
+ ('size', self.new_volume.size),
588
+ ('name', self.new_volume.display_name),
589
+ ]
590
+
591
+ parsed_args = self.check_parser(
592
+ self.cmd, arglist, verifylist)
593
+
594
+ columns, data = self.cmd.take_action(parsed_args)
595
+
596
+ self.volumes_mock.create.assert_called_with(
597
+ self.new_volume.size,
598
+ None,
599
+ None,
600
+ self.new_volume.display_name,
601
+ None,
602
+ None,
603
+ None,
604
+ None,
605
+ None,
606
+ None,
607
+ None,
608
+ )
609
+
610
+ self.assertEqual(2, mock_error.call_count)
611
+ self.assertEqual(self.columns, columns)
612
+ self.assertCountEqual(self.datalist, data)
613
+
569
614
  def test_volume_create_without_size(self):
570
615
  arglist = [
571
616
  self.new_volume.display_name,
@@ -257,7 +257,7 @@ class TestConsistencyGroupCreate(TestConsistencyGroup):
257
257
  self.new_consistency_group.name,
258
258
  ]
259
259
  verifylist = [
260
- ('consistency_group_source', self.new_consistency_group.id),
260
+ ('source', self.new_consistency_group.id),
261
261
  ('description', self.new_consistency_group.description),
262
262
  ('name', self.new_consistency_group.name),
263
263
  ]
@@ -285,7 +285,7 @@ class TestConsistencyGroupCreate(TestConsistencyGroup):
285
285
  self.new_consistency_group.name,
286
286
  ]
287
287
  verifylist = [
288
- ('consistency_group_snapshot', self.consistency_group_snapshot.id),
288
+ ('snapshot', self.consistency_group_snapshot.id),
289
289
  ('description', self.new_consistency_group.description),
290
290
  ('name', self.new_consistency_group.name),
291
291
  ]
@@ -435,7 +435,8 @@ class TestVolumeCreate(TestVolume):
435
435
  self.assertEqual(self.columns, columns)
436
436
  self.assertCountEqual(self.datalist, data)
437
437
 
438
- def test_volume_create_with_bootable_and_readonly(self):
438
+ @mock.patch.object(utils, 'wait_for_status', return_value=True)
439
+ def test_volume_create_with_bootable_and_readonly(self, mock_wait):
439
440
  arglist = [
440
441
  '--bootable',
441
442
  '--read-only',
@@ -478,7 +479,8 @@ class TestVolumeCreate(TestVolume):
478
479
  self.volumes_mock.update_readonly_flag.assert_called_with(
479
480
  self.new_volume.id, True)
480
481
 
481
- def test_volume_create_with_nonbootable_and_readwrite(self):
482
+ @mock.patch.object(utils, 'wait_for_status', return_value=True)
483
+ def test_volume_create_with_nonbootable_and_readwrite(self, mock_wait):
482
484
  arglist = [
483
485
  '--non-bootable',
484
486
  '--read-write',
@@ -522,8 +524,9 @@ class TestVolumeCreate(TestVolume):
522
524
  self.new_volume.id, False)
523
525
 
524
526
  @mock.patch.object(volume.LOG, 'error')
527
+ @mock.patch.object(utils, 'wait_for_status', return_value=True)
525
528
  def test_volume_create_with_bootable_and_readonly_fail(
526
- self, mock_error):
529
+ self, mock_wait, mock_error):
527
530
 
528
531
  self.volumes_mock.set_bootable.side_effect = (
529
532
  exceptions.CommandError())
@@ -574,6 +577,50 @@ class TestVolumeCreate(TestVolume):
574
577
  self.volumes_mock.update_readonly_flag.assert_called_with(
575
578
  self.new_volume.id, True)
576
579
 
580
+ @mock.patch.object(volume.LOG, 'error')
581
+ @mock.patch.object(utils, 'wait_for_status', return_value=False)
582
+ def test_volume_create_non_available_with_readonly(
583
+ self, mock_wait, mock_error,
584
+ ):
585
+ arglist = [
586
+ '--non-bootable',
587
+ '--read-only',
588
+ '--size', str(self.new_volume.size),
589
+ self.new_volume.name,
590
+ ]
591
+ verifylist = [
592
+ ('bootable', False),
593
+ ('non_bootable', True),
594
+ ('read_only', True),
595
+ ('read_write', False),
596
+ ('size', self.new_volume.size),
597
+ ('name', self.new_volume.name),
598
+ ]
599
+
600
+ parsed_args = self.check_parser(
601
+ self.cmd, arglist, verifylist)
602
+
603
+ columns, data = self.cmd.take_action(parsed_args)
604
+
605
+ self.volumes_mock.create.assert_called_with(
606
+ size=self.new_volume.size,
607
+ snapshot_id=None,
608
+ name=self.new_volume.name,
609
+ description=None,
610
+ volume_type=None,
611
+ availability_zone=None,
612
+ metadata=None,
613
+ imageRef=None,
614
+ source_volid=None,
615
+ consistencygroup_id=None,
616
+ scheduler_hints=None,
617
+ backup_id=None,
618
+ )
619
+
620
+ self.assertEqual(2, mock_error.call_count)
621
+ self.assertEqual(self.columns, columns)
622
+ self.assertCountEqual(self.datalist, data)
623
+
577
624
  def test_volume_create_without_size(self):
578
625
  arglist = [
579
626
  self.new_volume.name,
@@ -100,8 +100,8 @@ class TestVolumeGroupCreate(TestVolumeGroup):
100
100
  api_versions.APIVersion('3.13')
101
101
 
102
102
  arglist = [
103
- self.fake_volume_group_type.id,
104
- self.fake_volume_type.id,
103
+ '--volume-group-type', self.fake_volume_group_type.id,
104
+ '--volume-type', self.fake_volume_type.id,
105
105
  ]
106
106
  verifylist = [
107
107
  ('volume_group_type', self.fake_volume_group_type.id),
@@ -128,12 +128,51 @@ class TestVolumeGroupCreate(TestVolumeGroup):
128
128
  self.assertEqual(self.columns, columns)
129
129
  self.assertCountEqual(self.data, data)
130
130
 
131
+ def test_volume_group_create__legacy(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
+ self.fake_volume_type.id,
138
+ ]
139
+ verifylist = [
140
+ ('volume_group_type_legacy', self.fake_volume_group_type.id),
141
+ ('volume_types_legacy', [self.fake_volume_type.id]),
142
+ ('name', None),
143
+ ('description', None),
144
+ ('availability_zone', None),
145
+ ]
146
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
147
+
148
+ with mock.patch.object(self.cmd.log, 'warning') as mock_warning:
149
+ columns, data = self.cmd.take_action(parsed_args)
150
+
151
+ self.volume_group_types_mock.get.assert_called_once_with(
152
+ self.fake_volume_group_type.id)
153
+ self.volume_types_mock.get.assert_called_once_with(
154
+ self.fake_volume_type.id)
155
+ self.volume_groups_mock.create.assert_called_once_with(
156
+ self.fake_volume_group_type.id,
157
+ self.fake_volume_type.id,
158
+ None,
159
+ None,
160
+ availability_zone=None,
161
+ )
162
+ self.assertEqual(self.columns, columns)
163
+ self.assertCountEqual(self.data, data)
164
+ mock_warning.assert_called_once()
165
+ self.assertIn(
166
+ 'Passing volume group type and volume types as positional ',
167
+ str(mock_warning.call_args[0][0]),
168
+ )
169
+
131
170
  def test_volume_group_create_no_volume_type(self):
132
171
  self.app.client_manager.volume.api_version = \
133
172
  api_versions.APIVersion('3.13')
134
173
 
135
174
  arglist = [
136
- self.fake_volume_group_type.id
175
+ '--volume-group-type', self.fake_volume_group_type.id,
137
176
  ]
138
177
  verifylist = [
139
178
  ('volume_group_type', self.fake_volume_group_type.id),
@@ -148,7 +187,7 @@ class TestVolumeGroupCreate(TestVolumeGroup):
148
187
  self.cmd.take_action,
149
188
  parsed_args)
150
189
  self.assertIn(
151
- '<volume_types> is a required argument',
190
+ '--volume-types is a required argument when creating ',
152
191
  str(exc))
153
192
 
154
193
  def test_volume_group_create_with_options(self):
@@ -156,8 +195,8 @@ class TestVolumeGroupCreate(TestVolumeGroup):
156
195
  api_versions.APIVersion('3.13')
157
196
 
158
197
  arglist = [
159
- self.fake_volume_group_type.id,
160
- self.fake_volume_type.id,
198
+ '--volume-group-type', self.fake_volume_group_type.id,
199
+ '--volume-type', self.fake_volume_type.id,
161
200
  '--name', 'foo',
162
201
  '--description', 'hello, world',
163
202
  '--availability-zone', 'bar',
@@ -192,8 +231,8 @@ class TestVolumeGroupCreate(TestVolumeGroup):
192
231
  api_versions.APIVersion('3.12')
193
232
 
194
233
  arglist = [
195
- self.fake_volume_group_type.id,
196
- self.fake_volume_type.id,
234
+ '--volume-group-type', self.fake_volume_group_type.id,
235
+ '--volume-type', self.fake_volume_type.id,
197
236
  ]
198
237
  verifylist = [
199
238
  ('volume_group_type', self.fake_volume_group_type.id),
@@ -224,15 +224,44 @@ class CreateVolume(command.ShowOne):
224
224
 
225
225
  if parsed_args.bootable or parsed_args.non_bootable:
226
226
  try:
227
- volume_client.volumes.set_bootable(
228
- volume.id, parsed_args.bootable)
227
+ if utils.wait_for_status(
228
+ volume_client.volumes.get,
229
+ volume.id,
230
+ success_status=['available'],
231
+ error_status=['error'],
232
+ sleep_time=1
233
+ ):
234
+ volume_client.volumes.set_bootable(
235
+ volume.id,
236
+ parsed_args.bootable
237
+ )
238
+ else:
239
+ msg = _(
240
+ "Volume status is not available for setting boot "
241
+ "state"
242
+ )
243
+ raise exceptions.CommandError(msg)
229
244
  except Exception as e:
230
245
  LOG.error(_("Failed to set volume bootable property: %s"), e)
231
246
  if parsed_args.read_only or parsed_args.read_write:
232
247
  try:
233
- volume_client.volumes.update_readonly_flag(
248
+ if utils.wait_for_status(
249
+ volume_client.volumes.get,
234
250
  volume.id,
235
- parsed_args.read_only)
251
+ success_status=['available'],
252
+ error_status=['error'],
253
+ sleep_time=1
254
+ ):
255
+ volume_client.volumes.update_readonly_flag(
256
+ volume.id,
257
+ parsed_args.read_only
258
+ )
259
+ else:
260
+ msg = _(
261
+ "Volume status is not available for setting it"
262
+ "read only."
263
+ )
264
+ raise exceptions.CommandError(msg)
236
265
  except Exception as e:
237
266
  LOG.error(_("Failed to set volume read-only access "
238
267
  "mode flag: %s"), e)