python-openstackclient 7.1.3__py3-none-any.whl → 7.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 (128) hide show
  1. openstackclient/api/api.py +2 -1
  2. openstackclient/api/image_v2.py +1 -1
  3. openstackclient/api/object_store_v1.py +12 -20
  4. openstackclient/common/clientmanager.py +1 -1
  5. openstackclient/common/module.py +2 -2
  6. openstackclient/common/quota.py +4 -4
  7. openstackclient/compute/v2/flavor.py +1 -1
  8. openstackclient/compute/v2/server.py +122 -59
  9. openstackclient/compute/v2/server_backup.py +1 -1
  10. openstackclient/compute/v2/server_image.py +1 -1
  11. openstackclient/compute/v2/server_migration.py +11 -2
  12. openstackclient/compute/v2/usage.py +3 -3
  13. openstackclient/identity/common.py +1 -1
  14. openstackclient/identity/v2_0/project.py +1 -1
  15. openstackclient/identity/v2_0/role_assignment.py +1 -1
  16. openstackclient/identity/v2_0/user.py +2 -2
  17. openstackclient/identity/v3/access_rule.py +26 -14
  18. openstackclient/identity/v3/identity_provider.py +1 -1
  19. openstackclient/identity/v3/project.py +1 -1
  20. openstackclient/image/v2/image.py +13 -13
  21. openstackclient/image/v2/metadef_objects.py +6 -4
  22. openstackclient/network/common.py +8 -7
  23. openstackclient/network/v2/floating_ip.py +6 -2
  24. openstackclient/network/v2/floating_ip_port_forwarding.py +2 -2
  25. openstackclient/network/v2/l3_conntrack_helper.py +1 -1
  26. openstackclient/network/v2/ndp_proxy.py +1 -0
  27. openstackclient/network/v2/network_agent.py +2 -6
  28. openstackclient/network/v2/network_qos_rule.py +2 -5
  29. openstackclient/network/v2/network_trunk.py +5 -4
  30. openstackclient/network/v2/port.py +18 -3
  31. openstackclient/network/v2/router.py +7 -4
  32. openstackclient/network/v2/subnet_pool.py +2 -2
  33. openstackclient/shell.py +3 -2
  34. openstackclient/tests/functional/common/test_help.py +3 -9
  35. openstackclient/tests/functional/common/test_module.py +1 -1
  36. openstackclient/tests/functional/common/test_quota.py +2 -4
  37. openstackclient/tests/functional/compute/v2/common.py +1 -3
  38. openstackclient/tests/functional/compute/v2/test_hypervisor.py +3 -3
  39. openstackclient/tests/functional/compute/v2/test_keypair.py +2 -2
  40. openstackclient/tests/functional/compute/v2/test_server.py +1 -1
  41. openstackclient/tests/functional/identity/v2/common.py +31 -48
  42. openstackclient/tests/functional/identity/v2/test_catalog.py +1 -1
  43. openstackclient/tests/functional/identity/v2/test_ec2_credentials.py +2 -2
  44. openstackclient/tests/functional/identity/v2/test_endpoint.py +2 -2
  45. openstackclient/tests/functional/identity/v2/test_project.py +8 -8
  46. openstackclient/tests/functional/identity/v2/test_role.py +14 -34
  47. openstackclient/tests/functional/identity/v2/test_service.py +2 -2
  48. openstackclient/tests/functional/identity/v2/test_token.py +1 -1
  49. openstackclient/tests/functional/identity/v2/test_user.py +7 -9
  50. openstackclient/tests/functional/identity/v3/common.py +69 -110
  51. openstackclient/tests/functional/identity/v3/test_access_rule.py +86 -0
  52. openstackclient/tests/functional/identity/v3/test_application_credential.py +18 -44
  53. openstackclient/tests/functional/identity/v3/test_catalog.py +1 -1
  54. openstackclient/tests/functional/identity/v3/test_domain.py +9 -11
  55. openstackclient/tests/functional/identity/v3/test_endpoint.py +15 -27
  56. openstackclient/tests/functional/identity/v3/test_group.py +32 -93
  57. openstackclient/tests/functional/identity/v3/test_idp.py +3 -3
  58. openstackclient/tests/functional/identity/v3/test_limit.py +32 -32
  59. openstackclient/tests/functional/identity/v3/test_project.py +17 -26
  60. openstackclient/tests/functional/identity/v3/test_region.py +6 -7
  61. openstackclient/tests/functional/identity/v3/test_registered_limit.py +27 -36
  62. openstackclient/tests/functional/identity/v3/test_role.py +30 -60
  63. openstackclient/tests/functional/identity/v3/test_role_assignment.py +33 -80
  64. openstackclient/tests/functional/identity/v3/test_service.py +7 -13
  65. openstackclient/tests/functional/identity/v3/test_service_provider.py +3 -3
  66. openstackclient/tests/functional/identity/v3/test_user.py +17 -34
  67. openstackclient/tests/functional/image/v2/test_image.py +1 -3
  68. openstackclient/tests/functional/network/v2/common.py +1 -3
  69. openstackclient/tests/functional/network/v2/test_default_security_group_rule.py +3 -8
  70. openstackclient/tests/functional/network/v2/test_l3_conntrack_helper.py +27 -31
  71. openstackclient/tests/functional/network/v2/test_network.py +9 -12
  72. openstackclient/tests/functional/network/v2/test_network_agent.py +15 -20
  73. openstackclient/tests/functional/network/v2/test_network_flavor.py +2 -2
  74. openstackclient/tests/functional/network/v2/test_network_ndp_proxy.py +17 -39
  75. openstackclient/tests/functional/network/v2/test_network_qos_rule.py +48 -63
  76. openstackclient/tests/functional/network/v2/test_network_qos_rule_type.py +1 -1
  77. openstackclient/tests/functional/network/v2/test_network_segment_range.py +2 -2
  78. openstackclient/tests/functional/network/v2/test_network_trunk.py +15 -25
  79. openstackclient/tests/functional/network/v2/test_port.py +28 -34
  80. openstackclient/tests/functional/network/v2/test_router.py +13 -19
  81. openstackclient/tests/functional/object/v1/test_object.py +4 -7
  82. openstackclient/tests/functional/volume/base.py +5 -17
  83. openstackclient/tests/functional/volume/v1/test_volume_type.py +11 -11
  84. openstackclient/tests/functional/volume/v2/test_volume_backup.py +1 -1
  85. openstackclient/tests/functional/volume/v2/test_volume_type.py +13 -15
  86. openstackclient/tests/functional/volume/v3/test_volume_type.py +13 -15
  87. openstackclient/tests/unit/api/test_compute_v2.py +0 -5
  88. openstackclient/tests/unit/api/test_object_store_v1.py +6 -4
  89. openstackclient/tests/unit/common/test_extension.py +24 -31
  90. openstackclient/tests/unit/compute/v2/test_host.py +0 -1
  91. openstackclient/tests/unit/compute/v2/test_server.py +123 -115
  92. openstackclient/tests/unit/identity/v3/test_access_rule.py +65 -64
  93. openstackclient/tests/unit/identity/v3/test_group.py +4 -10
  94. openstackclient/tests/unit/identity/v3/test_limit.py +2 -2
  95. openstackclient/tests/unit/image/v2/test_metadef_objects.py +1 -2
  96. openstackclient/tests/unit/image/v2/test_metadef_resource_type_association.py +2 -6
  97. openstackclient/tests/unit/integ/base.py +1 -1
  98. openstackclient/tests/unit/network/v2/test_default_security_group_rule.py +3 -3
  99. openstackclient/tests/unit/network/v2/test_floating_ip_port_forwarding.py +4 -4
  100. openstackclient/tests/unit/network/v2/test_local_ip_association.py +2 -2
  101. openstackclient/tests/unit/network/v2/test_network_qos_rule.py +12 -13
  102. openstackclient/tests/unit/network/v2/test_network_trunk.py +31 -35
  103. openstackclient/tests/unit/network/v2/test_port.py +40 -17
  104. openstackclient/tests/unit/network/v2/test_subnet_pool.py +1 -1
  105. openstackclient/tests/unit/object/v1/test_object.py +1 -1
  106. openstackclient/tests/unit/utils.py +2 -2
  107. openstackclient/volume/client.py +1 -1
  108. openstackclient/volume/v1/volume.py +2 -2
  109. openstackclient/volume/v1/volume_backup.py +2 -2
  110. openstackclient/volume/v1/volume_snapshot.py +2 -2
  111. openstackclient/volume/v2/volume.py +2 -2
  112. openstackclient/volume/v2/volume_backup.py +2 -2
  113. openstackclient/volume/v2/volume_snapshot.py +2 -2
  114. openstackclient/volume/v2/volume_type.py +4 -4
  115. openstackclient/volume/v3/service.py +0 -1
  116. openstackclient/volume/v3/volume.py +3 -3
  117. openstackclient/volume/v3/volume_backup.py +2 -2
  118. openstackclient/volume/v3/volume_group.py +3 -7
  119. openstackclient/volume/v3/volume_type.py +6 -6
  120. {python_openstackclient-7.1.3.dist-info → python_openstackclient-7.2.0.dist-info}/AUTHORS +3 -0
  121. {python_openstackclient-7.1.3.dist-info → python_openstackclient-7.2.0.dist-info}/METADATA +2 -3
  122. {python_openstackclient-7.1.3.dist-info → python_openstackclient-7.2.0.dist-info}/RECORD +127 -126
  123. python_openstackclient-7.2.0.dist-info/pbr.json +1 -0
  124. python_openstackclient-7.1.3.dist-info/pbr.json +0 -1
  125. {python_openstackclient-7.1.3.dist-info → python_openstackclient-7.2.0.dist-info}/LICENSE +0 -0
  126. {python_openstackclient-7.1.3.dist-info → python_openstackclient-7.2.0.dist-info}/WHEEL +0 -0
  127. {python_openstackclient-7.1.3.dist-info → python_openstackclient-7.2.0.dist-info}/entry_points.txt +0 -0
  128. {python_openstackclient-7.1.3.dist-info → python_openstackclient-7.2.0.dist-info}/top_level.txt +0 -0
@@ -42,15 +42,15 @@ class DeleteAccessRule(command.Command):
42
42
  return parser
43
43
 
44
44
  def take_action(self, parsed_args):
45
- identity_client = self.app.client_manager.identity
45
+ identity_client = self.app.client_manager.sdk_connection.identity
46
+ conn = self.app.client_manager.sdk_connection
47
+ user_id = conn.config.get_auth().get_user_id(conn.identity)
46
48
 
47
49
  errors = 0
48
50
  for ac in parsed_args.access_rule:
49
51
  try:
50
- access_rule = common.get_resource_by_id(
51
- identity_client.access_rules, ac
52
- )
53
- identity_client.access_rules.delete(access_rule.id)
52
+ access_rule = identity_client.get_access_rule(user_id, ac)
53
+ identity_client.delete_access_rule(user_id, access_rule.id)
54
54
  except Exception as e:
55
55
  errors += 1
56
56
  LOG.error(
@@ -83,16 +83,17 @@ class ListAccessRule(command.Lister):
83
83
  return parser
84
84
 
85
85
  def take_action(self, parsed_args):
86
- identity_client = self.app.client_manager.identity
86
+ identity_client = self.app.client_manager.sdk_connection.identity
87
87
  if parsed_args.user:
88
88
  user_id = common.find_user(
89
89
  identity_client, parsed_args.user, parsed_args.user_domain
90
90
  ).id
91
91
  else:
92
- user_id = None
92
+ conn = self.app.client_manager.sdk_connection
93
+ user_id = conn.config.get_auth().get_user_id(conn.identity)
93
94
 
94
95
  columns = ('ID', 'Service', 'Method', 'Path')
95
- data = identity_client.access_rules.list(user=user_id)
96
+ data = identity_client.access_rules(user=user_id)
96
97
  return (
97
98
  columns,
98
99
  (
@@ -119,11 +120,22 @@ class ShowAccessRule(command.ShowOne):
119
120
  return parser
120
121
 
121
122
  def take_action(self, parsed_args):
122
- identity_client = self.app.client_manager.identity
123
- access_rule = common.get_resource_by_id(
124
- identity_client.access_rules, parsed_args.access_rule
125
- )
123
+ identity_client = self.app.client_manager.sdk_connection.identity
124
+ conn = self.app.client_manager.sdk_connection
125
+ user_id = conn.config.get_auth().get_user_id(conn.identity)
126
126
 
127
- access_rule._info.pop('links', None)
127
+ access_rule = identity_client.get_access_rule(
128
+ user_id, parsed_args.access_rule
129
+ )
128
130
 
129
- return zip(*sorted(access_rule._info.items()))
131
+ columns = ('ID', 'Method', 'Path', 'Service')
132
+ return (
133
+ columns,
134
+ (
135
+ utils.get_item_properties(
136
+ access_rule,
137
+ columns,
138
+ formatters={},
139
+ )
140
+ ),
141
+ )
@@ -133,7 +133,7 @@ class CreateIdentityProvider(command.ShowOne):
133
133
  description=parsed_args.description,
134
134
  domain_id=domain_id,
135
135
  enabled=parsed_args.enabled,
136
- **kwargs
136
+ **kwargs,
137
137
  )
138
138
 
139
139
  idp._info.pop('links', None)
@@ -125,7 +125,7 @@ class CreateProject(command.ShowOne):
125
125
  description=parsed_args.description,
126
126
  enabled=enabled,
127
127
  options=options,
128
- **kwargs
128
+ **kwargs,
129
129
  )
130
130
  except ks_exc.Conflict:
131
131
  if parsed_args.or_show:
@@ -421,8 +421,8 @@ class CreateImage(command.ShowOne):
421
421
  identity_common.add_project_domain_option_to_parser(parser)
422
422
  for deadopt in self.deadopts:
423
423
  parser.add_argument(
424
- "--%s" % deadopt,
425
- metavar="<%s>" % deadopt,
424
+ f"--{deadopt}",
425
+ metavar=f"<{deadopt}>",
426
426
  dest=deadopt.replace('-', '_'),
427
427
  help=argparse.SUPPRESS,
428
428
  )
@@ -488,7 +488,7 @@ class CreateImage(command.ShowOne):
488
488
  fp = open(parsed_args.filename, 'rb')
489
489
  except FileNotFoundError:
490
490
  raise exceptions.CommandError(
491
- '%r is not a valid file' % parsed_args.filename,
491
+ f'{parsed_args.filename!r} is not a valid file',
492
492
  )
493
493
  else:
494
494
  fp = get_data_from_stdin()
@@ -1209,8 +1209,8 @@ class SetImage(command.Command):
1209
1209
  identity_common.add_project_domain_option_to_parser(parser)
1210
1210
  for deadopt in self.deadopts:
1211
1211
  parser.add_argument(
1212
- "--%s" % deadopt,
1213
- metavar="<%s>" % deadopt,
1212
+ f"--{deadopt}",
1213
+ metavar=f"<{deadopt}>",
1214
1214
  dest=f"dead_{deadopt.replace('-', '_')}",
1215
1215
  help=argparse.SUPPRESS,
1216
1216
  )
@@ -1575,7 +1575,7 @@ class StageImage(command.Command):
1575
1575
  fp = open(parsed_args.filename, 'rb')
1576
1576
  except FileNotFoundError:
1577
1577
  raise exceptions.CommandError(
1578
- '%r is not a valid file' % parsed_args.filename,
1578
+ f'{parsed_args.filename!r} is not a valid file',
1579
1579
  )
1580
1580
  else:
1581
1581
  fp = get_data_from_stdin()
@@ -1614,8 +1614,6 @@ class ImportImage(command.ShowOne):
1614
1614
  metavar='<image>',
1615
1615
  help=_('Image to initiate import process for (name or ID)'),
1616
1616
  )
1617
- # TODO(stephenfin): Uncomment help text when we have this command
1618
- # implemented
1619
1617
  parser.add_argument(
1620
1618
  '--method',
1621
1619
  metavar='<method>',
@@ -1630,8 +1628,6 @@ class ImportImage(command.ShowOne):
1630
1628
  help=_(
1631
1629
  "Import method used for image import process. "
1632
1630
  "Not all deployments will support all methods. "
1633
- # "Valid values can be retrieved with the 'image import "
1634
- # "methods' command. "
1635
1631
  "The 'glance-direct' method (default) requires images be "
1636
1632
  "first staged using the 'image-stage' command."
1637
1633
  ),
@@ -1734,11 +1730,15 @@ class ImportImage(command.ShowOne):
1734
1730
 
1735
1731
  if parsed_args.import_method not in import_methods:
1736
1732
  msg = _(
1737
- "The '%s' import method is not supported by this deployment. "
1738
- "Supported: %s"
1733
+ "The '%(method)s' import method is not supported by this "
1734
+ "deployment. Supported: %(supported)s"
1739
1735
  )
1740
1736
  raise exceptions.CommandError(
1741
- msg % (parsed_args.import_method, ', '.join(import_methods)),
1737
+ msg
1738
+ % {
1739
+ 'method': parsed_args.import_method,
1740
+ 'supported': ', '.join(import_methods),
1741
+ },
1742
1742
  )
1743
1743
 
1744
1744
  if parsed_args.import_method == 'web-download':
@@ -260,10 +260,12 @@ class ShowMetadefObjectProperty(command.ShowOne):
260
260
  prop['name'] = parsed_args.property
261
261
 
262
262
  except KeyError:
263
- msg = _('Property %s not found in object %s.') % (
264
- parsed_args.property,
265
- parsed_args.object,
266
- )
263
+ msg = _(
264
+ 'Property %(property)s not found in object %(object)s.'
265
+ ) % {
266
+ 'property': parsed_args.property,
267
+ 'object': parsed_args.object,
268
+ }
267
269
  raise exceptions.CommandError(msg)
268
270
 
269
271
  return zip(*sorted(prop.items()))
@@ -120,13 +120,14 @@ class NetDetectionMixin(metaclass=abc.ABCMeta):
120
120
  @staticmethod
121
121
  def split_help(network_help, compute_help):
122
122
  return (
123
- "*%(network_qualifier)s:*\n %(network_help)s\n\n"
124
- "*%(compute_qualifier)s:*\n %(compute_help)s"
125
- % dict(
126
- network_qualifier=_("Network version 2"),
127
- network_help=network_help,
128
- compute_qualifier=_("Compute version 2"),
129
- compute_help=compute_help,
123
+ "*{network_qualifier}:*\n {network_help}\n\n"
124
+ "*{compute_qualifier}:*\n {compute_help}".format(
125
+ **dict(
126
+ network_qualifier=_("Network version 2"),
127
+ network_help=network_help,
128
+ compute_qualifier=_("Compute version 2"),
129
+ compute_help=compute_help,
130
+ )
130
131
  )
131
132
  )
132
133
 
@@ -12,6 +12,7 @@
12
12
 
13
13
  """IP Floating action implementations"""
14
14
 
15
+ from openstack import exceptions as sdk_exceptions
15
16
  from osc_lib import utils
16
17
  from osc_lib.utils import tags as _tag
17
18
 
@@ -390,7 +391,10 @@ class ListFloatingIP(common.NetworkAndComputeLister):
390
391
 
391
392
  _tag.get_tag_filtering_args(parsed_args, query)
392
393
 
393
- data = client.ips(**query)
394
+ try:
395
+ data = list(client.ips(**query))
396
+ except sdk_exceptions.NotFoundException:
397
+ data = []
394
398
 
395
399
  return (
396
400
  headers,
@@ -448,7 +452,7 @@ class SetFloatingIP(common.NeutronCommandWithExtraArgs):
448
452
  '--port',
449
453
  metavar='<port>',
450
454
  help=_("Associate the floating IP with port (name or ID)"),
451
- ),
455
+ )
452
456
  parser.add_argument(
453
457
  '--fixed-ip-address',
454
458
  metavar='<ip-address>',
@@ -144,7 +144,7 @@ class CreateFloatingIPPortForwarding(
144
144
  "The protocol used in the floating IP "
145
145
  "port forwarding, for instance: TCP, UDP"
146
146
  ),
147
- ),
147
+ )
148
148
  parser.add_argument(
149
149
  '--description',
150
150
  metavar='<description>',
@@ -404,7 +404,7 @@ class SetFloatingIPPortForwarding(common.NeutronCommandWithExtraArgs):
404
404
  metavar='<protocol>',
405
405
  choices=['tcp', 'udp'],
406
406
  help=_("The IP protocol used in the floating IP port forwarding"),
407
- ),
407
+ )
408
408
  parser.add_argument(
409
409
  '--description',
410
410
  metavar='<description>',
@@ -245,7 +245,7 @@ class SetConntrackHelper(command.Command):
245
245
  client.update_conntrack_helper(
246
246
  parsed_args.conntrack_helper_id,
247
247
  attrs.pop('router_id'),
248
- **attrs
248
+ **attrs,
249
249
  )
250
250
 
251
251
 
@@ -14,6 +14,7 @@
14
14
  # under the License.
15
15
 
16
16
  """Router NDP proxy action implementations"""
17
+
17
18
  import logging
18
19
 
19
20
  from osc_lib.command import command
@@ -89,9 +89,7 @@ class AddNetworkToAgent(command.Command):
89
89
  try:
90
90
  client.add_dhcp_agent_to_network(agent, network)
91
91
  except Exception:
92
- msg = 'Failed to add {} to {}'.format(
93
- network.name, agent.agent_type
94
- )
92
+ msg = f'Failed to add {network.name} to {agent.agent_type}'
95
93
  exceptions.CommandError(msg)
96
94
 
97
95
 
@@ -321,9 +319,7 @@ class RemoveNetworkFromAgent(command.Command):
321
319
  try:
322
320
  client.remove_dhcp_agent_from_network(agent, network)
323
321
  except Exception:
324
- msg = 'Failed to remove {} to {}'.format(
325
- network.name, agent.agent_type
326
- )
322
+ msg = f'Failed to remove {network.name} to {agent.agent_type}'
327
323
  exceptions.CommandError(msg)
328
324
 
329
325
 
@@ -159,10 +159,7 @@ def _get_item_properties(item, fields):
159
159
 
160
160
  def _rule_action_call(client, action, rule_type):
161
161
  rule_type = rule_type.replace('-', '_')
162
- func_name = '{action}_qos_{rule_type}_rule'.format(
163
- action=action,
164
- rule_type=rule_type,
165
- )
162
+ func_name = f'{action}_qos_{rule_type}_rule'
166
163
  return getattr(client, func_name)
167
164
 
168
165
 
@@ -311,7 +308,7 @@ class DeleteNetworkQosRule(command.Command):
311
308
  )
312
309
  rule_type = _find_rule_type(qos, rule_id)
313
310
  if not rule_type:
314
- raise Exception('Rule %s not found' % rule_id)
311
+ raise Exception(f'Rule {rule_id} not found')
315
312
  _rule_action_call(network_client, ACTION_DELETE, rule_type)(
316
313
  rule_id, qos.id
317
314
  )
@@ -15,6 +15,7 @@
15
15
  #
16
16
 
17
17
  """Network trunk and subports action implementations"""
18
+
18
19
  import logging
19
20
 
20
21
  from cliff import columns as cliff_columns
@@ -67,8 +68,8 @@ class CreateNetworkTrunk(command.ShowOne):
67
68
  required_keys=['port'],
68
69
  help=_(
69
70
  "Subport to add. Subport is of form "
70
- "\'port=<name or ID>,segmentation-type=<segmentation-type>,"
71
- "segmentation-id=<segmentation-ID>\' (--subport) option "
71
+ "'port=<name or ID>,segmentation-type=<segmentation-type>,"
72
+ "segmentation-id=<segmentation-ID>' (--subport) option "
72
73
  "can be repeated"
73
74
  ),
74
75
  )
@@ -198,8 +199,8 @@ class SetNetworkTrunk(command.Command):
198
199
  required_keys=['port'],
199
200
  help=_(
200
201
  "Subport to add. Subport is of form "
201
- "\'port=<name or ID>,segmentation-type=<segmentation-type>"
202
- ",segmentation-id=<segmentation-ID>\' (--subport) option "
202
+ "'port=<name or ID>,segmentation-type=<segmentation-type>"
203
+ ",segmentation-id=<segmentation-ID>' (--subport) option "
203
204
  "can be repeated"
204
205
  ),
205
206
  )
@@ -274,13 +274,13 @@ def _prepare_filter_fixed_ips(client_manager, parsed_args):
274
274
  _subnet = client.find_subnet(
275
275
  subnet_name_id, ignore_missing=False
276
276
  )
277
- ips.append('subnet_id=%s' % _subnet.id)
277
+ ips.append(f'subnet_id={_subnet.id}')
278
278
 
279
279
  if 'ip-address' in ip_spec:
280
- ips.append('ip_address=%s' % ip_spec['ip-address'])
280
+ ips.append('ip_address={}'.format(ip_spec['ip-address']))
281
281
 
282
282
  if 'ip-substring' in ip_spec:
283
- ips.append('ip_address_substr=%s' % ip_spec['ip-substring'])
283
+ ips.append('ip_address_substr={}'.format(ip_spec['ip-substring']))
284
284
  return ips
285
285
 
286
286
 
@@ -792,6 +792,19 @@ class ListPort(command.Lister):
792
792
  metavar='<security-group>',
793
793
  help=_("List only ports associated with this security group"),
794
794
  )
795
+ # the API sadly reports these in upper case and while it would be
796
+ # wonderful to plaster over this ugliness client-side, there are
797
+ # already users in the wild doing this in upper case that we need to
798
+ # support
799
+ parser.add_argument(
800
+ '--status',
801
+ metavar='<status>',
802
+ choices=('ACTIVE', 'BUILD', 'DOWN', 'ERROR'),
803
+ help=_(
804
+ "List ports according to their status "
805
+ "('ACTIVE', 'BUILD', 'DOWN', 'ERROR')"
806
+ ),
807
+ )
795
808
  identity_common.add_project_domain_option_to_parser(parser)
796
809
  parser.add_argument(
797
810
  '--fixed-ip',
@@ -859,6 +872,8 @@ class ListPort(command.Lister):
859
872
  filters['network_id'] = network.id
860
873
  if parsed_args.mac_address:
861
874
  filters['mac_address'] = parsed_args.mac_address
875
+ if parsed_args.status:
876
+ filters['status'] = parsed_args.status
862
877
  if parsed_args.project:
863
878
  project_id = identity_common.find_project(
864
879
  identity_client,
@@ -164,10 +164,13 @@ def _get_external_gateway_attrs(client_manager, parsed_args):
164
164
  'subnet_id' in ip_spec
165
165
  and ip_net_id not in external_gateways
166
166
  ):
167
- msg = _(
168
- 'Subnet %s does not belong to any of the networks '
169
- 'provided for --external-gateway.'
170
- ) % (ip_spec['subnet_id'])
167
+ msg = (
168
+ _(
169
+ 'Subnet %s does not belong to any of the networks '
170
+ 'provided for --external-gateway.'
171
+ )
172
+ % (ip_spec['subnet_id'])
173
+ )
171
174
  raise exceptions.CommandError(msg)
172
175
  for gw_info in external_gateways[ip_net_id]:
173
176
  if 'external_fixed_ips' not in gw_info:
@@ -201,7 +201,7 @@ class CreateSubnetPool(command.ShowOne, common.NeutronCommandWithExtraArgs):
201
201
  "as the number of IP addresses that can be allocated "
202
202
  "from the subnet pool"
203
203
  ),
204
- ),
204
+ )
205
205
  _tag.add_tag_option_to_parser_for_create(parser, _('subnet pool'))
206
206
  return parser
207
207
 
@@ -433,7 +433,7 @@ class SetSubnetPool(common.NeutronCommandWithExtraArgs):
433
433
  "as the number of IP addresses that can be allocated "
434
434
  "from the subnet pool"
435
435
  ),
436
- ),
436
+ )
437
437
  _tag.add_tag_option_to_parser_for_set(parser, _('subnet pool'))
438
438
 
439
439
  return parser
openstackclient/shell.py CHANGED
@@ -96,8 +96,9 @@ class OpenStackShell(shell.OpenStackShell):
96
96
  key=lambda s: list(map(int, s.split('.'))),
97
97
  )
98
98
  self.log.warning(
99
- "%s version %s is not in supported versions: %s"
100
- % (api, version_opt, ', '.join(sorted_versions))
99
+ "{} version {} is not in supported versions: {}".format(
100
+ api, version_opt, ', '.join(sorted_versions)
101
+ )
101
102
  )
102
103
 
103
104
  # Command groups deal only with major versions
@@ -21,7 +21,7 @@ class HelpTests(base.TestCase):
21
21
  """Functional tests for openstackclient help output."""
22
22
 
23
23
  SERVER_COMMANDS = [
24
- ('server add security group', 'Add security group to server'),
24
+ ('server add security group', 'Add security group(s) to server'),
25
25
  ('server add volume', 'Add volume to server'),
26
26
  ('server backup create', 'Create a server backup image'),
27
27
  ('server create', 'Create a new server'),
@@ -60,15 +60,9 @@ class HelpTests(base.TestCase):
60
60
  """Check server commands in main help message."""
61
61
  raw_output = self.openstack('help')
62
62
  for command, description in self.SERVER_COMMANDS:
63
- msg = 'Command: {} not found in help output:\n{}'.format(
64
- command,
65
- raw_output,
66
- )
63
+ msg = f'Command: {command} not found in help output:\n{raw_output}'
67
64
  self.assertIn(command, raw_output, msg)
68
- msg = 'Description: {} not found in help output:\n{}'.format(
69
- description,
70
- raw_output,
71
- )
65
+ msg = f'Description: {description} not found in help output:\n{raw_output}'
72
66
  self.assertIn(description, raw_output, msg)
73
67
 
74
68
  def test_server_only_help(self):
@@ -59,7 +59,7 @@ class CommandTest(base.TestCase):
59
59
  input_groups = ['volume', 'network', 'image', 'identity', 'compute.v2']
60
60
  for each_input in input_groups:
61
61
  cmd_output = self.openstack(
62
- 'command list --group %s' % each_input,
62
+ f'command list --group {each_input}',
63
63
  parse_output=True,
64
64
  )
65
65
  group_names = [each.get('Command Group') for each in cmd_output]
@@ -165,8 +165,7 @@ class QuotaTests(base.TestCase):
165
165
  # That will ensure we have at least two networks in the system.
166
166
  for _ in range(2):
167
167
  self.openstack(
168
- 'network create --project %s %s'
169
- % (self.PROJECT_NAME, uuid.uuid4().hex)
168
+ f'network create --project {self.PROJECT_NAME} {uuid.uuid4().hex}'
170
169
  )
171
170
 
172
171
  self.assertRaises(
@@ -211,8 +210,7 @@ class QuotaTests(base.TestCase):
211
210
  # That will ensure we have at least two networks in the system.
212
211
  for _ in range(2):
213
212
  self.openstack(
214
- 'network create --project %s %s'
215
- % (self.PROJECT_NAME, uuid.uuid4().hex)
213
+ f'network create --project {self.PROJECT_NAME} {uuid.uuid4().hex}'
216
214
  )
217
215
 
218
216
  self.openstack('quota set --networks 1 --force ' + self.PROJECT_NAME)
@@ -132,9 +132,7 @@ class ComputeTestCase(base.TestCase):
132
132
  print(f'Server {name} now has status {status}')
133
133
  break
134
134
  print(
135
- 'Server {}: Waiting for {}, current status: {}'.format(
136
- name, expected_status, status
137
- )
135
+ f'Server {name}: Waiting for {expected_status}, current status: {status}'
138
136
  )
139
137
  self.assertNotIn(status, failures)
140
138
  time.sleep(interval)
@@ -37,8 +37,8 @@ class HypervisorTests(base.TestCase):
37
37
  for i in ids1:
38
38
  cmd_output = json.loads(
39
39
  self.openstack(
40
- "hypervisor show %s -f json "
41
- " --os-compute-api-version 2.1" % (i)
40
+ f"hypervisor show {i} -f json "
41
+ " --os-compute-api-version 2.1"
42
42
  )
43
43
  )
44
44
  self.assertIsNotNone(cmd_output)
@@ -47,6 +47,6 @@ class HypervisorTests(base.TestCase):
47
47
  # Show test - latest microversion
48
48
  for i in ids2:
49
49
  cmd_output = json.loads(
50
- self.openstack("hypervisor show %s -f json" % (i))
50
+ self.openstack(f"hypervisor show {i} -f json")
51
51
  )
52
52
  self.assertIsNotNone(cmd_output)
@@ -96,7 +96,7 @@ class KeypairTests(KeypairBase):
96
96
  f.flush()
97
97
 
98
98
  raw_output = self.openstack(
99
- 'keypair create --public-key %s tmpkey' % f.name,
99
+ f'keypair create --public-key {f.name} tmpkey',
100
100
  )
101
101
  self.addCleanup(
102
102
  self.openstack,
@@ -113,7 +113,7 @@ class KeypairTests(KeypairBase):
113
113
  """
114
114
  with tempfile.NamedTemporaryFile(mode='w+') as f:
115
115
  cmd_output = self.openstack(
116
- 'keypair create --private-key %s tmpkey' % f.name,
116
+ f'keypair create --private-key {f.name} tmpkey',
117
117
  parse_output=True,
118
118
  )
119
119
  self.addCleanup(self.openstack, 'keypair delete tmpkey')
@@ -111,7 +111,7 @@ class ServerTests(common.ComputeTestCase):
111
111
  )
112
112
  except exceptions.CommandFailed as e:
113
113
  self.assertIn(
114
- 'marker [%s] not found' % (name2), e.stderr.decode('utf-8')
114
+ f'marker [{name2}] not found', e.stderr.decode('utf-8')
115
115
  )
116
116
 
117
117
  def test_server_list_with_changes_before(self):