python-openstackclient 8.1.0__py3-none-any.whl → 8.2.0__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 (56) hide show
  1. openstackclient/api/compute_v2.py +2 -2
  2. openstackclient/api/volume_v2.py +60 -0
  3. openstackclient/api/volume_v3.py +60 -0
  4. openstackclient/compute/v2/flavor.py +14 -1
  5. openstackclient/compute/v2/server.py +1 -3
  6. openstackclient/identity/common.py +8 -13
  7. openstackclient/identity/v3/application_credential.py +86 -85
  8. openstackclient/identity/v3/domain.py +5 -6
  9. openstackclient/identity/v3/project.py +25 -20
  10. openstackclient/identity/v3/role.py +7 -2
  11. openstackclient/image/v1/image.py +16 -1
  12. openstackclient/image/v2/cache.py +10 -6
  13. openstackclient/image/v2/image.py +48 -1
  14. openstackclient/image/v2/metadef_objects.py +8 -2
  15. openstackclient/image/v2/metadef_properties.py +9 -2
  16. openstackclient/network/v2/port.py +16 -0
  17. openstackclient/network/v2/security_group.py +44 -3
  18. openstackclient/network/v2/security_group_rule.py +17 -0
  19. openstackclient/tests/functional/identity/v3/test_access_rule.py +1 -1
  20. openstackclient/tests/functional/identity/v3/test_application_credential.py +7 -7
  21. openstackclient/tests/functional/image/v2/test_image.py +36 -14
  22. openstackclient/tests/functional/volume/v2/test_volume.py +1 -1
  23. openstackclient/tests/functional/volume/v3/test_volume.py +2 -2
  24. openstackclient/tests/unit/api/test_volume_v2.py +124 -0
  25. openstackclient/tests/unit/api/test_volume_v3.py +124 -0
  26. openstackclient/tests/unit/compute/v2/test_flavor.py +159 -174
  27. openstackclient/tests/unit/compute/v2/test_server.py +42 -51
  28. openstackclient/tests/unit/identity/v3/test_application_credential.py +47 -41
  29. openstackclient/tests/unit/identity/v3/test_domain.py +2 -2
  30. openstackclient/tests/unit/identity/v3/test_project.py +30 -53
  31. openstackclient/tests/unit/identity/v3/test_role.py +2 -8
  32. openstackclient/tests/unit/image/v1/test_image.py +47 -0
  33. openstackclient/tests/unit/image/v2/test_image.py +79 -9
  34. openstackclient/tests/unit/image/v2/test_metadef_objects.py +22 -0
  35. openstackclient/tests/unit/image/v2/test_metadef_properties.py +24 -10
  36. openstackclient/tests/unit/network/v2/fakes.py +1 -0
  37. openstackclient/tests/unit/network/v2/test_ndp_proxy.py +2 -2
  38. openstackclient/tests/unit/network/v2/test_port.py +40 -0
  39. openstackclient/tests/unit/network/v2/test_security_group_network.py +6 -0
  40. openstackclient/tests/unit/network/v2/test_security_group_rule_network.py +49 -0
  41. openstackclient/tests/unit/volume/v2/test_volume.py +358 -305
  42. openstackclient/tests/unit/volume/v3/test_volume.py +439 -415
  43. openstackclient/volume/v2/service.py +1 -1
  44. openstackclient/volume/v2/volume.py +78 -52
  45. openstackclient/volume/v3/service.py +1 -1
  46. openstackclient/volume/v3/volume.py +102 -75
  47. openstackclient/volume/v3/volume_group.py +1 -1
  48. {python_openstackclient-8.1.0.dist-info → python_openstackclient-8.2.0.dist-info}/AUTHORS +5 -0
  49. {python_openstackclient-8.1.0.dist-info → python_openstackclient-8.2.0.dist-info}/METADATA +7 -7
  50. {python_openstackclient-8.1.0.dist-info → python_openstackclient-8.2.0.dist-info}/RECORD +55 -51
  51. python_openstackclient-8.2.0.dist-info/pbr.json +1 -0
  52. python_openstackclient-8.1.0.dist-info/pbr.json +0 -1
  53. {python_openstackclient-8.1.0.dist-info → python_openstackclient-8.2.0.dist-info}/LICENSE +0 -0
  54. {python_openstackclient-8.1.0.dist-info → python_openstackclient-8.2.0.dist-info}/WHEEL +0 -0
  55. {python_openstackclient-8.1.0.dist-info → python_openstackclient-8.2.0.dist-info}/entry_points.txt +0 -0
  56. {python_openstackclient-8.1.0.dist-info → python_openstackclient-8.2.0.dist-info}/top_level.txt +0 -0
@@ -77,8 +77,7 @@ class TestProjectCreate(TestProject):
77
77
  ]
78
78
  verifylist = [
79
79
  ('parent', None),
80
- ('enable', False),
81
- ('disable', False),
80
+ ('enabled', True),
82
81
  ('name', self.project.name),
83
82
  ('tags', []),
84
83
  ]
@@ -134,8 +133,7 @@ class TestProjectCreate(TestProject):
134
133
  ]
135
134
  verifylist = [
136
135
  ('description', 'new desc'),
137
- ('enable', False),
138
- ('disable', False),
136
+ ('enabled', True),
139
137
  ('name', self.project.name),
140
138
  ('parent', None),
141
139
  ('tags', []),
@@ -172,8 +170,7 @@ class TestProjectCreate(TestProject):
172
170
  ]
173
171
  verifylist = [
174
172
  ('domain', self.project.domain_id),
175
- ('enable', False),
176
- ('disable', False),
173
+ ('enabled', True),
177
174
  ('name', self.project.name),
178
175
  ('parent', None),
179
176
  ('tags', []),
@@ -210,8 +207,7 @@ class TestProjectCreate(TestProject):
210
207
  ]
211
208
  verifylist = [
212
209
  ('domain', self.project.domain_id),
213
- ('enable', False),
214
- ('disable', False),
210
+ ('enabled', True),
215
211
  ('name', self.project.name),
216
212
  ('parent', None),
217
213
  ('tags', []),
@@ -243,8 +239,7 @@ class TestProjectCreate(TestProject):
243
239
  self.project.name,
244
240
  ]
245
241
  verifylist = [
246
- ('enable', True),
247
- ('disable', False),
242
+ ('enabled', True),
248
243
  ('name', self.project.name),
249
244
  ('parent', None),
250
245
  ('tags', []),
@@ -279,8 +274,7 @@ class TestProjectCreate(TestProject):
279
274
  self.project.name,
280
275
  ]
281
276
  verifylist = [
282
- ('enable', False),
283
- ('disable', True),
277
+ ('enabled', False),
284
278
  ('name', self.project.name),
285
279
  ('parent', None),
286
280
  ]
@@ -317,7 +311,7 @@ class TestProjectCreate(TestProject):
317
311
  self.project.name,
318
312
  ]
319
313
  verifylist = [
320
- ('property', {'fee': 'fi', 'fo': 'fum'}),
314
+ ('properties', {'fee': 'fi', 'fo': 'fum'}),
321
315
  ('name', self.project.name),
322
316
  ]
323
317
  parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -354,11 +348,10 @@ class TestProjectCreate(TestProject):
354
348
  ]
355
349
  verifylist = [
356
350
  ('parent', None),
357
- ('enable', False),
358
- ('disable', False),
351
+ ('enabled', True),
359
352
  ('name', self.project.name),
360
353
  ('tags', []),
361
- ('property', {'is_domain': 'false'}),
354
+ ('properties', {'is_domain': 'false'}),
362
355
  ('name', self.project.name),
363
356
  ]
364
357
 
@@ -393,11 +386,10 @@ class TestProjectCreate(TestProject):
393
386
  ]
394
387
  verifylist = [
395
388
  ('parent', None),
396
- ('enable', False),
397
- ('disable', False),
389
+ ('enabled', True),
398
390
  ('name', self.project.name),
399
391
  ('tags', []),
400
- ('property', {'is_domain': 'true'}),
392
+ ('properties', {'is_domain': 'true'}),
401
393
  ('name', self.project.name),
402
394
  ]
403
395
 
@@ -432,11 +424,10 @@ class TestProjectCreate(TestProject):
432
424
  ]
433
425
  verifylist = [
434
426
  ('parent', None),
435
- ('enable', False),
436
- ('disable', False),
427
+ ('enabled', True),
437
428
  ('name', self.project.name),
438
429
  ('tags', []),
439
- ('property', {'is_domain': 'none'}),
430
+ ('properties', {'is_domain': 'none'}),
440
431
  ('name', self.project.name),
441
432
  ]
442
433
 
@@ -481,8 +472,7 @@ class TestProjectCreate(TestProject):
481
472
  verifylist = [
482
473
  ('domain', self.project.domain_id),
483
474
  ('parent', self.parent.name),
484
- ('enable', False),
485
- ('disable', False),
475
+ ('enabled', True),
486
476
  ('name', self.project.name),
487
477
  ('tags', []),
488
478
  ]
@@ -544,8 +534,7 @@ class TestProjectCreate(TestProject):
544
534
  verifylist = [
545
535
  ('domain', self.project.domain_id),
546
536
  ('parent', 'invalid'),
547
- ('enable', False),
548
- ('disable', False),
537
+ ('enabled', True),
549
538
  ('name', self.project.name),
550
539
  ]
551
540
  parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -566,8 +555,7 @@ class TestProjectCreate(TestProject):
566
555
  ]
567
556
  verifylist = [
568
557
  ('domain', self.project.domain_id),
569
- ('enable', False),
570
- ('disable', False),
558
+ ('enabled', True),
571
559
  ('name', self.project.name),
572
560
  ('parent', None),
573
561
  ('tags', ['foo']),
@@ -602,8 +590,7 @@ class TestProjectCreate(TestProject):
602
590
  verifylist = [
603
591
  ('immutable', True),
604
592
  ('description', None),
605
- ('enable', False),
606
- ('disable', False),
593
+ ('enabled', True),
607
594
  ('name', self.project.name),
608
595
  ('parent', None),
609
596
  ('tags', []),
@@ -638,10 +625,9 @@ class TestProjectCreate(TestProject):
638
625
  self.project.name,
639
626
  ]
640
627
  verifylist = [
641
- ('no_immutable', True),
628
+ ('immutable', False),
642
629
  ('description', None),
643
- ('enable', False),
644
- ('disable', False),
630
+ ('enabled', True),
645
631
  ('name', self.project.name),
646
632
  ('parent', None),
647
633
  ('tags', []),
@@ -981,8 +967,7 @@ class TestProjectSet(TestProject):
981
967
  ]
982
968
  verifylist = [
983
969
  ('project', self.project.name),
984
- ('enable', False),
985
- ('disable', False),
970
+ ('enabled', None),
986
971
  ]
987
972
  parsed_args = self.check_parser(self.cmd, arglist, verifylist)
988
973
 
@@ -1001,8 +986,7 @@ class TestProjectSet(TestProject):
1001
986
  verifylist = [
1002
987
  ('name', 'qwerty'),
1003
988
  ('domain', self.project.domain_id),
1004
- ('enable', False),
1005
- ('disable', False),
989
+ ('enabled', None),
1006
990
  ('project', self.project.name),
1007
991
  ]
1008
992
  parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -1029,8 +1013,7 @@ class TestProjectSet(TestProject):
1029
1013
  verifylist = [
1030
1014
  ('domain', self.project.domain_id),
1031
1015
  ('description', 'new desc'),
1032
- ('enable', False),
1033
- ('disable', False),
1016
+ ('enabled', None),
1034
1017
  ('project', self.project.name),
1035
1018
  ]
1036
1019
  parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -1053,8 +1036,7 @@ class TestProjectSet(TestProject):
1053
1036
  ]
1054
1037
  verifylist = [
1055
1038
  ('domain', self.project.domain_id),
1056
- ('enable', True),
1057
- ('disable', False),
1039
+ ('enabled', True),
1058
1040
  ('project', self.project.name),
1059
1041
  ]
1060
1042
  parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -1077,8 +1059,7 @@ class TestProjectSet(TestProject):
1077
1059
  ]
1078
1060
  verifylist = [
1079
1061
  ('domain', self.project.domain_id),
1080
- ('enable', False),
1081
- ('disable', True),
1062
+ ('enabled', False),
1082
1063
  ('project', self.project.name),
1083
1064
  ]
1084
1065
  parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -1104,7 +1085,7 @@ class TestProjectSet(TestProject):
1104
1085
  ]
1105
1086
  verifylist = [
1106
1087
  ('domain', self.project.domain_id),
1107
- ('property', {'fee': 'fi', 'fo': 'fum'}),
1088
+ ('properties', {'fee': 'fi', 'fo': 'fum'}),
1108
1089
  ('project', self.project.name),
1109
1090
  ]
1110
1091
  parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -1132,8 +1113,7 @@ class TestProjectSet(TestProject):
1132
1113
  verifylist = [
1133
1114
  ('name', 'qwerty'),
1134
1115
  ('domain', self.project.domain_id),
1135
- ('enable', False),
1136
- ('disable', False),
1116
+ ('enabled', None),
1137
1117
  ('project', self.project.name),
1138
1118
  ('tags', ['foo']),
1139
1119
  ]
@@ -1160,8 +1140,7 @@ class TestProjectSet(TestProject):
1160
1140
  self.project.name,
1161
1141
  ]
1162
1142
  verifylist = [
1163
- ('enable', False),
1164
- ('disable', False),
1143
+ ('enabled', None),
1165
1144
  ('project', self.project.name),
1166
1145
  ('remove_tag', ['tag1', 'tag2']),
1167
1146
  ]
@@ -1183,8 +1162,7 @@ class TestProjectSet(TestProject):
1183
1162
  verifylist = [
1184
1163
  ('domain', self.project.domain_id),
1185
1164
  ('immutable', True),
1186
- ('enable', False),
1187
- ('disable', False),
1165
+ ('enabled', None),
1188
1166
  ('project', self.project.name),
1189
1167
  ]
1190
1168
  parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -1207,9 +1185,8 @@ class TestProjectSet(TestProject):
1207
1185
  ]
1208
1186
  verifylist = [
1209
1187
  ('domain', self.project.domain_id),
1210
- ('no_immutable', True),
1211
- ('enable', False),
1212
- ('disable', False),
1188
+ ('immutable', False),
1189
+ ('enabled', None),
1213
1190
  ('project', self.project.name),
1214
1191
  ]
1215
1192
  parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -494,7 +494,6 @@ class TestRoleCreate(identity_fakes.TestIdentityv3):
494
494
  # Set expected values
495
495
  kwargs = {
496
496
  'name': self.role.name,
497
- 'options': {},
498
497
  }
499
498
 
500
499
  self.identity_sdk_client.create_role.assert_called_with(**kwargs)
@@ -533,7 +532,6 @@ class TestRoleCreate(identity_fakes.TestIdentityv3):
533
532
  kwargs = {
534
533
  'domain_id': self.domain.id,
535
534
  'name': self.role_with_domain.name,
536
- 'options': {},
537
535
  }
538
536
 
539
537
  self.identity_sdk_client.create_role.assert_called_with(**kwargs)
@@ -572,7 +570,6 @@ class TestRoleCreate(identity_fakes.TestIdentityv3):
572
570
  kwargs = {
573
571
  'name': self.role_with_description.name,
574
572
  'description': self.role_with_description.description,
575
- 'options': {},
576
573
  }
577
574
 
578
575
  self.identity_sdk_client.create_role.assert_called_with(**kwargs)
@@ -629,7 +626,7 @@ class TestRoleCreate(identity_fakes.TestIdentityv3):
629
626
  self.role.name,
630
627
  ]
631
628
  verifylist = [
632
- ('no_immutable', True),
629
+ ('immutable', False),
633
630
  ('name', self.role.name),
634
631
  ]
635
632
  parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -1437,7 +1434,6 @@ class TestRoleSet(identity_fakes.TestIdentityv3):
1437
1434
  kwargs = {
1438
1435
  'name': 'over',
1439
1436
  'role': self.role.id,
1440
- 'options': {},
1441
1437
  }
1442
1438
  self.identity_sdk_client.update_role.assert_called_with(**kwargs)
1443
1439
  self.assertIsNone(result)
@@ -1472,7 +1468,6 @@ class TestRoleSet(identity_fakes.TestIdentityv3):
1472
1468
  'name': 'over',
1473
1469
  'role': self.role_with_domain.id,
1474
1470
  'domain_id': self.domain2.id,
1475
- 'options': {},
1476
1471
  }
1477
1472
  self.identity_sdk_client.update_role.assert_called_with(**kwargs)
1478
1473
  self.assertIsNone(result)
@@ -1501,7 +1496,6 @@ class TestRoleSet(identity_fakes.TestIdentityv3):
1501
1496
  'name': 'over',
1502
1497
  'description': 'role description',
1503
1498
  'role': self.role_with_domain.id,
1504
- 'options': {},
1505
1499
  }
1506
1500
  self.identity_sdk_client.update_role.assert_called_with(**kwargs)
1507
1501
  self.assertIsNone(result)
@@ -1544,7 +1538,7 @@ class TestRoleSet(identity_fakes.TestIdentityv3):
1544
1538
  ]
1545
1539
  verifylist = [
1546
1540
  ('name', 'over'),
1547
- ('no_immutable', True),
1541
+ ('immutable', False),
1548
1542
  ('role', self.role_with_domain.name),
1549
1543
  ]
1550
1544
  parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -757,3 +757,50 @@ class TestImageShow(image_fakes.TestImagev1):
757
757
 
758
758
  size_index = columns.index('size')
759
759
  self.assertEqual(data[size_index].human_readable(), '2K')
760
+
761
+
762
+ class TestImageSave(image_fakes.TestImagev1):
763
+ image = image_fakes.create_one_image({})
764
+
765
+ def setUp(self):
766
+ super().setUp()
767
+
768
+ self.image_client.find_image.return_value = self.image
769
+ self.image_client.download_image.return_value = self.image
770
+
771
+ # Get the command object to test
772
+ self.cmd = image.SaveImage(self.app, None)
773
+
774
+ def test_save_data(self):
775
+ arglist = ['--file', '/path/to/file', self.image.id]
776
+
777
+ verifylist = [('file', '/path/to/file'), ('image', self.image.id)]
778
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
779
+
780
+ self.cmd.take_action(parsed_args)
781
+
782
+ self.image_client.download_image.assert_called_once_with(
783
+ self.image.id, output='/path/to/file', stream=True, chunk_size=1024
784
+ )
785
+
786
+ def test_save_data_with_chunk_size(self):
787
+ arglist = [
788
+ '--file',
789
+ '/path/to/file',
790
+ '--chunk-size',
791
+ '2048',
792
+ self.image.id,
793
+ ]
794
+
795
+ verifylist = [
796
+ ('file', '/path/to/file'),
797
+ ('chunk_size', 2048),
798
+ ('image', self.image.id),
799
+ ]
800
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
801
+
802
+ self.cmd.take_action(parsed_args)
803
+
804
+ self.image_client.download_image.assert_called_once_with(
805
+ self.image.id, output='/path/to/file', stream=True, chunk_size=2048
806
+ )
@@ -822,6 +822,8 @@ class TestImageList(TestImage):
822
822
  'Visibility',
823
823
  'Protected',
824
824
  'Project',
825
+ 'Hash Algorithm',
826
+ 'Hash Value',
825
827
  'Tags',
826
828
  )
827
829
 
@@ -830,14 +832,16 @@ class TestImageList(TestImage):
830
832
  (
831
833
  self._image.id,
832
834
  self._image.name,
833
- None,
834
- None,
835
- None,
836
- None,
837
- None,
835
+ self._image.disk_format,
836
+ self._image.container_format,
837
+ self._image.size,
838
+ self._image.checksum,
839
+ self._image.status,
838
840
  self._image.visibility,
839
841
  self._image.is_protected,
840
842
  self._image.owner_id,
843
+ self._image.hash_algo,
844
+ self._image.hash_value,
841
845
  format_columns.ListColumn(self._image.tags),
842
846
  ),
843
847
  )
@@ -1356,15 +1360,17 @@ class TestImageSet(TestImage):
1356
1360
  exceptions.CommandError, self.cmd.take_action, parsed_args
1357
1361
  )
1358
1362
 
1359
- def test_image_set_bools1(self):
1363
+ def test_image_set_bools_true(self):
1360
1364
  arglist = [
1361
1365
  '--protected',
1362
1366
  '--private',
1367
+ '--hidden',
1363
1368
  'graven',
1364
1369
  ]
1365
1370
  verifylist = [
1366
1371
  ('is_protected', True),
1367
1372
  ('visibility', 'private'),
1373
+ ('is_hidden', True),
1368
1374
  ('image', 'graven'),
1369
1375
  ]
1370
1376
  parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -1374,6 +1380,7 @@ class TestImageSet(TestImage):
1374
1380
  kwargs = {
1375
1381
  'is_protected': True,
1376
1382
  'visibility': 'private',
1383
+ 'is_hidden': True,
1377
1384
  }
1378
1385
  # ImageManager.update(image, **kwargs)
1379
1386
  self.image_client.update_image.assert_called_with(
@@ -1381,15 +1388,17 @@ class TestImageSet(TestImage):
1381
1388
  )
1382
1389
  self.assertIsNone(result)
1383
1390
 
1384
- def test_image_set_bools2(self):
1391
+ def test_image_set_bools_false(self):
1385
1392
  arglist = [
1386
1393
  '--unprotected',
1387
1394
  '--public',
1395
+ '--unhidden',
1388
1396
  'graven',
1389
1397
  ]
1390
1398
  verifylist = [
1391
1399
  ('is_protected', False),
1392
1400
  ('visibility', 'public'),
1401
+ ('is_hidden', False),
1393
1402
  ('image', 'graven'),
1394
1403
  ]
1395
1404
  parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -1399,6 +1408,7 @@ class TestImageSet(TestImage):
1399
1408
  kwargs = {
1400
1409
  'is_protected': False,
1401
1410
  'visibility': 'public',
1411
+ 'is_hidden': False,
1402
1412
  }
1403
1413
  # ImageManager.update(image, **kwargs)
1404
1414
  self.image_client.update_image.assert_called_with(
@@ -1779,6 +1789,7 @@ class TestImageUnset(TestImage):
1779
1789
  attrs['hw_rng_model'] = 'virtio'
1780
1790
  attrs['prop'] = 'test'
1781
1791
  attrs['prop2'] = 'fake'
1792
+ attrs['os_secure_boot'] = 'required'
1782
1793
  self.image = image_fakes.create_one_image(attrs)
1783
1794
 
1784
1795
  self.image_client.find_image.return_value = self.image
@@ -1822,11 +1833,18 @@ class TestImageUnset(TestImage):
1822
1833
  'hw_rng_model',
1823
1834
  '--property',
1824
1835
  'prop',
1836
+ '--property',
1837
+ 'os_secure_boot',
1825
1838
  self.image.id,
1826
1839
  ]
1827
1840
 
1841
+ # openstacksdk translates 'os_secure_boot' property to
1842
+ # 'needs_secure_boot' Image attribute. This is true for
1843
+ # all IMAGE_ATTRIBUTES_CUSTOM_NAMES keys
1844
+ self.assertEqual(self.image.needs_secure_boot, 'required')
1845
+ self.assertFalse(hasattr(self.image, 'os_secure_boot'))
1828
1846
  verifylist = [
1829
- ('properties', ['hw_rng_model', 'prop']),
1847
+ ('properties', ['hw_rng_model', 'prop', 'os_secure_boot']),
1830
1848
  ('image', self.image.id),
1831
1849
  ]
1832
1850
  parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -2039,6 +2057,36 @@ class TestImageImport(TestImage):
2039
2057
 
2040
2058
  self.image_client.import_image.assert_not_called()
2041
2059
 
2060
+ def test_import_image__web_download_invalid_url(self):
2061
+ arglist = [
2062
+ self.image.name,
2063
+ '--method',
2064
+ 'web-download',
2065
+ '--uri',
2066
+ 'invalid:1234',
2067
+ ]
2068
+
2069
+ verifylist = [
2070
+ ('image', self.image.name),
2071
+ ('import_method', 'web-download'),
2072
+ ('uri', 'invalid:1234'),
2073
+ ]
2074
+
2075
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
2076
+
2077
+ exc = self.assertRaises(
2078
+ exceptions.CommandError,
2079
+ self.cmd.take_action,
2080
+ parsed_args,
2081
+ )
2082
+
2083
+ self.assertIn(
2084
+ "'invalid:1234' is not a valid url",
2085
+ str(exc),
2086
+ )
2087
+
2088
+ self.image_client.import_image.assert_not_called()
2089
+
2042
2090
  def test_import_image__web_download_invalid_image_state(self):
2043
2091
  self.image.status = 'uploading' # != 'queued'
2044
2092
  arglist = [
@@ -2190,7 +2238,29 @@ class TestImageSave(TestImage):
2190
2238
  self.cmd.take_action(parsed_args)
2191
2239
 
2192
2240
  self.image_client.download_image.assert_called_once_with(
2193
- self.image.id, stream=True, output='/path/to/file'
2241
+ self.image.id, output='/path/to/file', stream=True, chunk_size=1024
2242
+ )
2243
+
2244
+ def test_save_data_with_chunk_size(self):
2245
+ arglist = [
2246
+ '--file',
2247
+ '/path/to/file',
2248
+ '--chunk-size',
2249
+ '2048',
2250
+ self.image.id,
2251
+ ]
2252
+
2253
+ verifylist = [
2254
+ ('filename', '/path/to/file'),
2255
+ ('chunk_size', 2048),
2256
+ ('image', self.image.id),
2257
+ ]
2258
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
2259
+
2260
+ self.cmd.take_action(parsed_args)
2261
+
2262
+ self.image_client.download_image.assert_called_once_with(
2263
+ self.image.id, output='/path/to/file', stream=True, chunk_size=2048
2194
2264
  )
2195
2265
 
2196
2266
 
@@ -113,6 +113,7 @@ class TestMetadefObjectDelete(fakes.TestImagev2):
113
113
  super().setUp()
114
114
 
115
115
  self.image_client.delete_metadef_object.return_value = None
116
+ self.image_client.delete_all_metadef_objects.return_value = None
116
117
  self.cmd = metadef_objects.DeleteMetadefObject(self.app, None)
117
118
 
118
119
  def test_object_delete(self):
@@ -126,8 +127,29 @@ class TestMetadefObjectDelete(fakes.TestImagev2):
126
127
 
127
128
  result = self.cmd.take_action(parsed_args)
128
129
 
130
+ self.image_client.delete_metadef_object.assert_called_once_with(
131
+ self.image_client.get_metadef_object(),
132
+ self._metadef_namespace.namespace,
133
+ )
134
+ self.image_client.delete_all_metadef_objects.assert_not_called()
129
135
  self.assertIsNone(result)
130
136
 
137
+ def test_object_delete_all(self):
138
+ arglist = [
139
+ self._metadef_namespace.namespace,
140
+ ]
141
+
142
+ verifylist = []
143
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
144
+
145
+ result = self.cmd.take_action(parsed_args)
146
+
147
+ self.assertIsNone(result)
148
+ self.image_client.delete_all_metadef_objects.assert_called_with(
149
+ self._metadef_namespace.namespace,
150
+ )
151
+ self.image_client.delete_metadef_object.assert_not_called()
152
+
131
153
 
132
154
  class TestMetadefObjectList(fakes.TestImagev2):
133
155
  _metadef_namespace = fakes.create_one_metadef_namespace()
@@ -91,6 +91,7 @@ class TestMetadefPropertyDelete(image_fakes.TestImagev2):
91
91
  super().setUp()
92
92
 
93
93
  self.cmd = metadef_properties.DeleteMetadefProperty(self.app, None)
94
+ self.image_client.delete_all_metadef_properties.return_value = None
94
95
 
95
96
  def test_metadef_property_delete(self):
96
97
  arglist = ['namespace', 'property']
@@ -100,6 +101,10 @@ class TestMetadefPropertyDelete(image_fakes.TestImagev2):
100
101
  result = self.cmd.take_action(parsed_args)
101
102
 
102
103
  self.assertIsNone(result)
104
+ self.image_client.delete_metadef_property.assert_called_with(
105
+ 'property', 'namespace', ignore_missing=False
106
+ )
107
+ self.image_client.delete_all_metadef_properties.assert_not_called()
103
108
 
104
109
  def test_metadef_property_delete_missing_arguments(self):
105
110
  arglist = []
@@ -110,21 +115,13 @@ class TestMetadefPropertyDelete(image_fakes.TestImagev2):
110
115
  arglist,
111
116
  [],
112
117
  )
113
-
114
- arglist = ['namespace']
115
- self.assertRaises(
116
- tests_utils.ParserException,
117
- self.check_parser,
118
- self.cmd,
119
- arglist,
120
- [],
121
- )
118
+ self.image_client.delete_all_metadef_properties.assert_not_called()
119
+ self.image_client.delete_metadef_property.assert_not_called()
122
120
 
123
121
  def test_metadef_property_delete_exception(self):
124
122
  arglist = ['namespace', 'property']
125
123
  verifylist = []
126
124
  parsed_args = self.check_parser(self.cmd, arglist, verifylist)
127
-
128
125
  self.image_client.delete_metadef_property.side_effect = (
129
126
  sdk_exceptions.ResourceNotFound
130
127
  )
@@ -132,6 +129,23 @@ class TestMetadefPropertyDelete(image_fakes.TestImagev2):
132
129
  self.assertRaises(
133
130
  exceptions.CommandError, self.cmd.take_action, parsed_args
134
131
  )
132
+ self.image_client.delete_metadef_property.assert_called_with(
133
+ 'property', 'namespace', ignore_missing=False
134
+ )
135
+ self.image_client.delete_all_metadef_properties.assert_not_called()
136
+
137
+ def test_metadef_property_delete_all(self):
138
+ arglist = ['namespace']
139
+ verifylist = []
140
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
141
+
142
+ result = self.cmd.take_action(parsed_args)
143
+
144
+ self.assertIsNone(result)
145
+ self.image_client.delete_all_metadef_properties.assert_called_with(
146
+ 'namespace'
147
+ )
148
+ self.image_client.delete_metadef_property.assert_not_called()
135
149
 
136
150
 
137
151
  class TestMetadefPropertyList(image_fakes.TestImagev2):
@@ -1412,6 +1412,7 @@ def create_one_security_group(attrs=None):
1412
1412
  'security_group_rules': [],
1413
1413
  'tags': [],
1414
1414
  'location': 'MUNCHMUNCHMUNCH',
1415
+ 'is_shared': False,
1415
1416
  }
1416
1417
 
1417
1418
  # Overwrite default attributes.
@@ -311,7 +311,7 @@ class TestListNDPProxy(TestNDPProxy):
311
311
  **{'project_id': project.id}
312
312
  )
313
313
  self.assertEqual(self.columns, columns)
314
- self.assertItemsEqual(self.data, list(data))
314
+ self.assertCountEqual(self.data, list(data))
315
315
 
316
316
  def test_ndp_proxy_list_project_domain(self):
317
317
  project = identity_fakes_v3.FakeProject.create_one_project()
@@ -332,7 +332,7 @@ class TestListNDPProxy(TestNDPProxy):
332
332
 
333
333
  self.network_client.ndp_proxies.assert_called_once_with(**filters)
334
334
  self.assertEqual(self.columns, columns)
335
- self.assertItemsEqual(self.data, list(data))
335
+ self.assertCountEqual(self.data, list(data))
336
336
 
337
337
 
338
338
  class TestSetNDPProxy(TestNDPProxy):