python-openstackclient 7.2.1__py3-none-any.whl → 7.3.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 (66) hide show
  1. openstackclient/common/limits.py +1 -1
  2. openstackclient/common/quota.py +7 -2
  3. openstackclient/compute/v2/server.py +38 -22
  4. openstackclient/compute/v2/usage.py +2 -2
  5. openstackclient/identity/common.py +22 -34
  6. openstackclient/identity/v3/credential.py +45 -28
  7. openstackclient/identity/v3/limit.py +15 -0
  8. openstackclient/identity/v3/region.py +23 -22
  9. openstackclient/identity/v3/registered_limit.py +18 -0
  10. openstackclient/identity/v3/role.py +287 -117
  11. openstackclient/identity/v3/role_assignment.py +1 -1
  12. openstackclient/identity/v3/service_provider.py +95 -45
  13. openstackclient/identity/v3/trust.py +114 -75
  14. openstackclient/image/v2/image.py +3 -0
  15. openstackclient/network/v2/network.py +33 -0
  16. openstackclient/network/v2/network_flavor_profile.py +1 -17
  17. openstackclient/network/v2/port.py +73 -19
  18. openstackclient/tests/functional/compute/v2/test_server.py +87 -1
  19. openstackclient/tests/functional/identity/v3/common.py +1 -1
  20. openstackclient/tests/functional/identity/v3/test_application_credential.py +2 -1
  21. openstackclient/tests/functional/identity/v3/test_role.py +24 -0
  22. openstackclient/tests/functional/identity/v3/test_role_assignment.py +8 -0
  23. openstackclient/tests/functional/identity/v3/test_service_provider.py +1 -5
  24. openstackclient/tests/functional/network/v2/test_port.py +28 -1
  25. openstackclient/tests/unit/compute/v2/fakes.py +0 -304
  26. openstackclient/tests/unit/compute/v2/test_aggregate.py +40 -31
  27. openstackclient/tests/unit/compute/v2/test_console.py +7 -3
  28. openstackclient/tests/unit/compute/v2/test_hypervisor.py +60 -53
  29. openstackclient/tests/unit/compute/v2/test_keypair.py +57 -69
  30. openstackclient/tests/unit/compute/v2/test_server.py +63 -5
  31. openstackclient/tests/unit/compute/v2/test_server_group.py +99 -105
  32. openstackclient/tests/unit/compute/v2/test_server_volume.py +12 -5
  33. openstackclient/tests/unit/compute/v2/test_service.py +83 -37
  34. openstackclient/tests/unit/compute/v2/test_usage.py +12 -7
  35. openstackclient/tests/unit/identity/v2_0/test_catalog.py +3 -6
  36. openstackclient/tests/unit/identity/v2_0/test_role.py +1 -2
  37. openstackclient/tests/unit/identity/v2_0/test_role_assignment.py +2 -1
  38. openstackclient/tests/unit/identity/v2_0/test_token.py +6 -20
  39. openstackclient/tests/unit/identity/v3/test_catalog.py +2 -5
  40. openstackclient/tests/unit/identity/v3/test_credential.py +74 -63
  41. openstackclient/tests/unit/identity/v3/test_project.py +1 -3
  42. openstackclient/tests/unit/identity/v3/test_region.py +74 -96
  43. openstackclient/tests/unit/identity/v3/test_role.py +679 -603
  44. openstackclient/tests/unit/identity/v3/test_role_assignment.py +263 -1
  45. openstackclient/tests/unit/identity/v3/test_service_provider.py +159 -209
  46. openstackclient/tests/unit/identity/v3/test_token.py +5 -20
  47. openstackclient/tests/unit/identity/v3/test_trust.py +137 -155
  48. openstackclient/tests/unit/image/v2/test_image.py +6 -0
  49. openstackclient/tests/unit/network/v2/fakes.py +3 -0
  50. openstackclient/tests/unit/network/v2/test_network.py +25 -0
  51. openstackclient/tests/unit/network/v2/test_network_flavor_profile.py +0 -35
  52. openstackclient/tests/unit/network/v2/test_port.py +129 -16
  53. openstackclient/tests/unit/utils.py +8 -2
  54. openstackclient/tests/unit/volume/v2/test_volume_backup.py +31 -13
  55. openstackclient/tests/unit/volume/v3/test_volume_backup.py +34 -13
  56. openstackclient/volume/v2/volume_backup.py +11 -2
  57. openstackclient/volume/v3/volume_backup.py +13 -2
  58. {python_openstackclient-7.2.1.dist-info → python_openstackclient-7.3.0.dist-info}/AUTHORS +2 -0
  59. {python_openstackclient-7.2.1.dist-info → python_openstackclient-7.3.0.dist-info}/METADATA +14 -16
  60. {python_openstackclient-7.2.1.dist-info → python_openstackclient-7.3.0.dist-info}/RECORD +65 -65
  61. {python_openstackclient-7.2.1.dist-info → python_openstackclient-7.3.0.dist-info}/WHEEL +1 -1
  62. {python_openstackclient-7.2.1.dist-info → python_openstackclient-7.3.0.dist-info}/entry_points.txt +0 -1
  63. python_openstackclient-7.3.0.dist-info/pbr.json +1 -0
  64. python_openstackclient-7.2.1.dist-info/pbr.json +0 -1
  65. {python_openstackclient-7.2.1.dist-info → python_openstackclient-7.3.0.dist-info}/LICENSE +0 -0
  66. {python_openstackclient-7.2.1.dist-info → python_openstackclient-7.3.0.dist-info}/top_level.txt +0 -0
@@ -38,6 +38,20 @@ class AdminStateColumn(cliff_columns.FormattableColumn):
38
38
  return 'UP' if self._value else 'DOWN'
39
39
 
40
40
 
41
+ class SubPortColumn(format_columns.ListDictColumn):
42
+ def _retrieve_subports(self):
43
+ if isinstance(self._value, dict):
44
+ self._value = self._value['sub_ports']
45
+
46
+ def human_readable(self):
47
+ self._retrieve_subports()
48
+ return super().human_readable()
49
+
50
+ def machine_readable(self):
51
+ self._retrieve_subports()
52
+ return super().machine_readable()
53
+
54
+
41
55
  _formatters = {
42
56
  'admin_state_up': AdminStateColumn,
43
57
  'is_admin_state_up': AdminStateColumn,
@@ -51,6 +65,7 @@ _formatters = {
51
65
  'fixed_ips': format_columns.ListDictColumn,
52
66
  'security_group_ids': format_columns.ListColumn,
53
67
  'tags': format_columns.ListColumn,
68
+ 'trunk_details': SubPortColumn,
54
69
  }
55
70
 
56
71
 
@@ -93,6 +108,7 @@ def _get_columns(item):
93
108
  'status': 'status',
94
109
  'tags': 'tags',
95
110
  'trunk_details': 'trunk_details',
111
+ 'trusted': 'trusted',
96
112
  'updated_at': 'updated_at',
97
113
  }
98
114
  return (
@@ -222,6 +238,10 @@ def _get_attrs(client_manager, parsed_args):
222
238
  and parsed_args.hardware_offload_type
223
239
  ):
224
240
  attrs['hardware_offload_type'] = parsed_args.hardware_offload_type
241
+ if parsed_args.not_trusted:
242
+ attrs['trusted'] = False
243
+ if parsed_args.trusted:
244
+ attrs['trusted'] = True
225
245
 
226
246
  return attrs
227
247
 
@@ -388,6 +408,25 @@ def _add_updatable_args(parser, create=False):
388
408
  '(repeat option to set multiple hints)'
389
409
  ),
390
410
  )
411
+ port_trusted = parser.add_mutually_exclusive_group()
412
+ port_trusted.add_argument(
413
+ '--trusted',
414
+ action='store_true',
415
+ help=_(
416
+ "Set port to be trusted. This will be populated into the "
417
+ "'binding:profile' dictionary and passed to the services "
418
+ "which expect it in this dictionary (for example, Nova)"
419
+ ),
420
+ )
421
+ port_trusted.add_argument(
422
+ '--not-trusted',
423
+ action='store_true',
424
+ help=_(
425
+ "Set port to be not trusted. This will be populated into the "
426
+ "'binding:profile' dictionary and passed to the services "
427
+ "which expect it in this dictionary (for example, Nova)"
428
+ ),
429
+ )
391
430
 
392
431
 
393
432
  # TODO(abhiraut): Use the SDK resource mapped attribute names once the
@@ -534,7 +573,7 @@ class CreatePort(command.ShowOne, common.NeutronCommandWithExtraArgs):
534
573
  '--security-group',
535
574
  metavar='<security-group>',
536
575
  action='append',
537
- dest='security_group',
576
+ dest='security_groups',
538
577
  help=_(
539
578
  "Security group to associate with this port (name or ID) "
540
579
  "(repeat option to set multiple security groups)"
@@ -542,8 +581,9 @@ class CreatePort(command.ShowOne, common.NeutronCommandWithExtraArgs):
542
581
  )
543
582
  secgroups.add_argument(
544
583
  '--no-security-group',
545
- dest='no_security_group',
546
- action='store_true',
584
+ action='store_const',
585
+ const=[],
586
+ dest='security_groups',
547
587
  help=_("Associate no security groups with this port"),
548
588
  )
549
589
  parser.add_argument(
@@ -609,13 +649,11 @@ class CreatePort(command.ShowOne, common.NeutronCommandWithExtraArgs):
609
649
  elif parsed_args.no_fixed_ip:
610
650
  attrs['fixed_ips'] = []
611
651
 
612
- if parsed_args.security_group:
652
+ if parsed_args.security_groups is not None:
613
653
  attrs['security_group_ids'] = [
614
654
  client.find_security_group(sg, ignore_missing=False).id
615
- for sg in parsed_args.security_group
655
+ for sg in parsed_args.security_groups
616
656
  ]
617
- elif parsed_args.no_security_group:
618
- attrs['security_group_ids'] = []
619
657
 
620
658
  if parsed_args.allowed_address_pairs:
621
659
  attrs['allowed_address_pairs'] = _convert_address_pairs(
@@ -828,25 +866,30 @@ class ListPort(command.Lister):
828
866
  network_client = self.app.client_manager.network
829
867
  identity_client = self.app.client_manager.identity
830
868
 
831
- columns = (
869
+ columns = [
832
870
  'id',
833
871
  'name',
834
872
  'mac_address',
835
873
  'fixed_ips',
836
874
  'status',
837
- )
838
- column_headers = (
875
+ ]
876
+ column_headers = [
839
877
  'ID',
840
878
  'Name',
841
879
  'MAC Address',
842
880
  'Fixed IP Addresses',
843
881
  'Status',
844
- )
882
+ ]
845
883
 
846
884
  filters = {}
847
885
  if parsed_args.long:
848
- columns += ('security_group_ids', 'device_owner', 'tags')
849
- column_headers += ('Security Groups', 'Device Owner', 'Tags')
886
+ columns.extend(
887
+ ['security_groups', 'device_owner', 'tags', 'trunk_details']
888
+ )
889
+ column_headers.extend(
890
+ ['Security Groups', 'Device Owner', 'Tags', 'Trunk subports']
891
+ )
892
+
850
893
  if parsed_args.device_owner is not None:
851
894
  filters['device_owner'] = parsed_args.device_owner
852
895
  if parsed_args.device_id is not None:
@@ -894,6 +937,12 @@ class ListPort(command.Lister):
894
937
 
895
938
  data = network_client.ports(fields=columns, **filters)
896
939
 
940
+ if parsed_args.long:
941
+ columns = [
942
+ 'security_group_ids' if item == 'security_groups' else item
943
+ for item in columns
944
+ ]
945
+
897
946
  headers, attrs = utils.calculate_header_and_attrs(
898
947
  column_headers, columns, parsed_args
899
948
  )
@@ -982,7 +1031,7 @@ class SetPort(common.NeutronCommandWithExtraArgs):
982
1031
  '--security-group',
983
1032
  metavar='<security-group>',
984
1033
  action='append',
985
- dest='security_group',
1034
+ dest='security_groups',
986
1035
  help=_(
987
1036
  "Security group to associate with this port (name or ID) "
988
1037
  "(repeat option to set multiple security groups)"
@@ -1083,7 +1132,7 @@ class SetPort(common.NeutronCommandWithExtraArgs):
1083
1132
 
1084
1133
  if parsed_args.no_security_group:
1085
1134
  attrs['security_group_ids'] = []
1086
- if parsed_args.security_group:
1135
+ if parsed_args.security_groups:
1087
1136
  if 'security_group_ids' not in attrs:
1088
1137
  # NOTE(dtroyer): Get existing security groups, iterate the
1089
1138
  # list to force a new list object to be
@@ -1094,7 +1143,7 @@ class SetPort(common.NeutronCommandWithExtraArgs):
1094
1143
  ]
1095
1144
  attrs['security_group_ids'].extend(
1096
1145
  client.find_security_group(sg, ignore_missing=False).id
1097
- for sg in parsed_args.security_group
1146
+ for sg in parsed_args.security_groups
1098
1147
  )
1099
1148
 
1100
1149
  if parsed_args.no_allowed_address_pair:
@@ -1137,6 +1186,11 @@ class SetPort(common.NeutronCommandWithExtraArgs):
1137
1186
  raise exceptions.CommandError(msg)
1138
1187
  attrs['hints'] = expanded_hints
1139
1188
 
1189
+ if parsed_args.not_trusted:
1190
+ attrs['trusted'] = False
1191
+ if parsed_args.trusted:
1192
+ attrs['trusted'] = True
1193
+
1140
1194
  attrs.update(
1141
1195
  self._parse_extra_properties(parsed_args.extra_properties)
1142
1196
  )
@@ -1202,7 +1256,7 @@ class UnsetPort(common.NeutronUnsetCommandWithExtraArgs):
1202
1256
  '--security-group',
1203
1257
  metavar='<security-group>',
1204
1258
  action='append',
1205
- dest='security_group_ids',
1259
+ dest='security_groups',
1206
1260
  help=_(
1207
1261
  "Security group which should be removed this port (name "
1208
1262
  "or ID) (repeat option to unset multiple security groups)"
@@ -1287,9 +1341,9 @@ class UnsetPort(common.NeutronUnsetCommandWithExtraArgs):
1287
1341
  msg = _("Port does not contain binding-profile %s") % key
1288
1342
  raise exceptions.CommandError(msg)
1289
1343
  attrs['binding:profile'] = tmp_binding_profile
1290
- if parsed_args.security_group_ids:
1344
+ if parsed_args.security_groups:
1291
1345
  try:
1292
- for sg in parsed_args.security_group_ids:
1346
+ for sg in parsed_args.security_groups:
1293
1347
  sg_id = client.find_security_group(
1294
1348
  sg, ignore_missing=False
1295
1349
  ).id
@@ -1295,7 +1295,7 @@ class ServerTests(common.ComputeTestCase):
1295
1295
  )
1296
1296
  if ip_address in cmd_output['addresses']['private']:
1297
1297
  # Hang out for a bit and try again
1298
- print('retrying add port check')
1298
+ print('retrying remove port check')
1299
1299
  wait_time += 10
1300
1300
  time.sleep(10)
1301
1301
  else:
@@ -1367,6 +1367,92 @@ class ServerTests(common.ComputeTestCase):
1367
1367
  addresses = cmd_output['addresses']['private']
1368
1368
  self.assertIn(ip_address, addresses)
1369
1369
 
1370
+ def test_server_add_remove_security_group(self):
1371
+ name = uuid.uuid4().hex
1372
+ cmd_output = self.openstack(
1373
+ 'server create '
1374
+ + '--network private '
1375
+ + '--flavor '
1376
+ + self.flavor_name
1377
+ + ' '
1378
+ + '--image '
1379
+ + self.image_name
1380
+ + ' '
1381
+ + '--wait '
1382
+ + name,
1383
+ parse_output=True,
1384
+ )
1385
+
1386
+ self.assertIsNotNone(cmd_output['id'])
1387
+ self.assertEqual(name, cmd_output['name'])
1388
+ self.addCleanup(self.openstack, 'server delete --wait ' + name)
1389
+
1390
+ # create security group
1391
+ security_group_name = uuid.uuid4().hex
1392
+
1393
+ cmd_output = self.openstack(
1394
+ 'security group list',
1395
+ parse_output=True,
1396
+ )
1397
+ self.assertNotIn(security_group_name, cmd_output)
1398
+
1399
+ cmd_output = self.openstack(
1400
+ 'security group create ' + security_group_name,
1401
+ parse_output=True,
1402
+ )
1403
+ self.assertIsNotNone(cmd_output['id'])
1404
+ self.addCleanup(
1405
+ self.openstack, 'security group delete ' + security_group_name
1406
+ )
1407
+
1408
+ # add security group to server, assert the name of the security group
1409
+ # appears
1410
+ self.openstack(
1411
+ 'server add security group ' + name + ' ' + security_group_name
1412
+ )
1413
+
1414
+ wait_time = 0
1415
+ while wait_time < 60:
1416
+ cmd_output = self.openstack(
1417
+ 'server show ' + name,
1418
+ parse_output=True,
1419
+ )
1420
+ if security_group_name not in [
1421
+ x['name'] for x in cmd_output['security_groups']
1422
+ ]:
1423
+ # Hang out for a bit and try again
1424
+ print('retrying add security group check')
1425
+ wait_time += 10
1426
+ time.sleep(10)
1427
+ else:
1428
+ break
1429
+ security_groups = [x['name'] for x in cmd_output['security_groups']]
1430
+ self.assertIn(security_group_name, security_groups)
1431
+
1432
+ # remove security group, assert the name of the security group doesn't
1433
+ # appear
1434
+ self.openstack(
1435
+ 'server remove security group ' + name + ' ' + security_group_name
1436
+ )
1437
+
1438
+ wait_time = 0
1439
+ while wait_time < 60:
1440
+ cmd_output = self.openstack(
1441
+ 'server show ' + name,
1442
+ parse_output=True,
1443
+ )
1444
+ if security_group_name not in [
1445
+ x['name'] for x in cmd_output['security_groups']
1446
+ ]:
1447
+ # Hang out for a bit and try again
1448
+ print('retrying remove security group check')
1449
+ wait_time += 10
1450
+ time.sleep(10)
1451
+ else:
1452
+ break
1453
+ security_groups = [x['name'] for x in cmd_output['security_groups']]
1454
+ self.assertNotIn(security_group_name, security_groups)
1455
+
1370
1456
  def test_server_add_remove_volume(self):
1371
1457
  volume_wait_for = volume_common.BaseVolumeTests.wait_for_status
1372
1458
 
@@ -49,7 +49,7 @@ class IdentityTests(base.TestCase):
49
49
  ]
50
50
  ROLE_FIELDS = ['id', 'name', 'domain_id', 'description']
51
51
  SERVICE_FIELDS = ['id', 'enabled', 'name', 'type', 'description']
52
- REGION_FIELDS = ['description', 'enabled', 'parent_region', 'region']
52
+ REGION_FIELDS = ['description', 'parent_region', 'region']
53
53
  ENDPOINT_FIELDS = [
54
54
  'id',
55
55
  'region',
@@ -107,7 +107,8 @@ class ApplicationCredentialTests(common.IdentityTests):
107
107
  secret = data_utils.rand_name('secret')
108
108
  description = data_utils.rand_name('description')
109
109
  tomorrow = (
110
- datetime.datetime.utcnow() + datetime.timedelta(days=1)
110
+ datetime.datetime.now(datetime.timezone.utc).replace(tzinfo=None)
111
+ + datetime.timedelta(days=1)
111
112
  ).strftime('%Y-%m-%dT%H:%M:%S%z')
112
113
  role1, role2 = self._create_role_assignments()
113
114
  raw_output = self.openstack(
@@ -93,6 +93,30 @@ class RoleTests(common.IdentityTests):
93
93
  )
94
94
  self.assertEqual(0, len(raw_output))
95
95
 
96
+ def test_role_add_inherited(self):
97
+ role_name = self._create_dummy_role()
98
+ username = self._create_dummy_user()
99
+ raw_output = self.openstack(
100
+ 'role add '
101
+ f'--project {self.project_name} '
102
+ f'--project-domain {self.domain_name} '
103
+ f'--user {username} '
104
+ f'--user-domain {self.domain_name} '
105
+ '--inherited '
106
+ f'{role_name}'
107
+ )
108
+ self.addCleanup(
109
+ self.openstack,
110
+ 'role remove '
111
+ f'--project {self.project_name} '
112
+ f'--project-domain {self.domain_name} '
113
+ f'--user {username} '
114
+ f'--user-domain {self.domain_name} '
115
+ '--inherited '
116
+ f'{role_name}',
117
+ )
118
+ self.assertEqual(0, len(raw_output))
119
+
96
120
  def test_role_remove(self):
97
121
  role_name = self._create_dummy_role()
98
122
  username = self._create_dummy_user()
@@ -151,6 +151,14 @@ class RoleAssignmentTests(common.IdentityTests):
151
151
  '--inherited '
152
152
  f'{role_name}'
153
153
  )
154
+ self.addCleanup(
155
+ self.openstack,
156
+ 'role remove '
157
+ f'--project {self.project_name} '
158
+ f'--user {username} '
159
+ '--inherited '
160
+ f'{role_name}',
161
+ )
154
162
  self.assertEqual(0, len(raw_output))
155
163
 
156
164
  raw_output = self.openstack('role assignment list --inherited')
@@ -60,9 +60,5 @@ class ServiceProviderTests(common.IdentityTests):
60
60
  'description': new_description,
61
61
  }
62
62
  )
63
- self.assertEqual(0, len(raw_output))
64
- raw_output = self.openstack(
65
- f'service provider show {service_provider}'
66
- )
67
63
  updated_value = self.parse_show_as_object(raw_output)
68
- self.assertIn(new_description, updated_value['description'])
64
+ self.assertEqual(new_description, updated_value.get('description'))
@@ -81,10 +81,20 @@ class PortTests(common.NetworkTagTests):
81
81
  self.addCleanup(self.openstack, f'port delete {id1}')
82
82
  self.assertEqual(self.NAME, json_output.get('name'))
83
83
 
84
+ # sg for port2
85
+ sg_name1 = uuid.uuid4().hex
84
86
  json_output = self.openstack(
85
- f'port create --network {self.NETWORK_NAME} {self.NAME}x',
87
+ f'security group create {sg_name1}',
86
88
  parse_output=True,
87
89
  )
90
+ sg_id1 = json_output.get('id')
91
+ self.addCleanup(self.openstack, f'security group delete {sg_id1}')
92
+ json_output = self.openstack(
93
+ f'port create --network {self.NETWORK_NAME} '
94
+ f'--security-group {sg_name1} {self.NAME}x',
95
+ parse_output=True,
96
+ )
97
+
88
98
  id2 = json_output.get('id')
89
99
  self.assertIsNotNone(id2)
90
100
  mac2 = json_output.get('mac_address')
@@ -113,6 +123,12 @@ class PortTests(common.NetworkTagTests):
113
123
  id_list = [item.get('ID') for item in json_output]
114
124
  self.assertIn(id1, id_list)
115
125
  self.assertIn(id2, id_list)
126
+ item_sg_map = {
127
+ item.get('ID'): item.get('Security Groups') for item in json_output
128
+ }
129
+ self.assertIn(id1, item_sg_map.keys())
130
+ self.assertIn(id2, item_sg_map.keys())
131
+ self.assertIn([sg_id1], item_sg_map.values())
116
132
 
117
133
  # Test list --mac-address
118
134
  json_output = self.openstack(
@@ -127,6 +143,17 @@ class PortTests(common.NetworkTagTests):
127
143
  self.assertNotIn(mac1, item_map.values())
128
144
  self.assertIn(mac2, item_map.values())
129
145
 
146
+ # Test list --security-group
147
+ json_output = self.openstack(
148
+ f'port list --security-group {sg_id1}',
149
+ parse_output=True,
150
+ )
151
+ item_map = {
152
+ item.get('ID'): item.get('Security Groups') for item in json_output
153
+ }
154
+ self.assertNotIn(id1, item_map.keys())
155
+ self.assertIn(id2, item_map.keys())
156
+
130
157
  # Test list with unknown fields
131
158
  json_output = self.openstack(
132
159
  'port list -c ID -c Name -c device_id',