python-openstackclient 8.0.0__py3-none-any.whl → 8.1.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 (85) hide show
  1. openstackclient/compute/client.py +5 -0
  2. openstackclient/compute/v2/console.py +7 -0
  3. openstackclient/compute/v2/console_connection.py +48 -0
  4. openstackclient/compute/v2/keypair.py +10 -3
  5. openstackclient/compute/v2/server.py +75 -10
  6. openstackclient/compute/v2/server_event.py +1 -1
  7. openstackclient/identity/common.py +79 -0
  8. openstackclient/identity/v3/application_credential.py +2 -2
  9. openstackclient/identity/v3/domain.py +62 -43
  10. openstackclient/identity/v3/group.py +113 -68
  11. openstackclient/identity/v3/project.py +17 -0
  12. openstackclient/identity/v3/user.py +38 -5
  13. openstackclient/image/client.py +5 -0
  14. openstackclient/image/v2/image.py +11 -11
  15. openstackclient/network/client.py +0 -6
  16. openstackclient/network/v2/floating_ip.py +58 -29
  17. openstackclient/network/v2/network_qos_rule.py +3 -11
  18. openstackclient/network/v2/router.py +1 -1
  19. openstackclient/network/v2/security_group.py +5 -4
  20. openstackclient/network/v2/security_group_rule.py +1 -1
  21. openstackclient/shell.py +1 -1
  22. openstackclient/tests/functional/base.py +5 -1
  23. openstackclient/tests/functional/compute/v2/test_keypair.py +41 -5
  24. openstackclient/tests/unit/compute/v2/fakes.py +81 -305
  25. openstackclient/tests/unit/compute/v2/test_console.py +18 -1
  26. openstackclient/tests/unit/compute/v2/test_console_connection.py +72 -0
  27. openstackclient/tests/unit/compute/v2/test_flavor.py +1 -1
  28. openstackclient/tests/unit/compute/v2/test_keypair.py +12 -5
  29. openstackclient/tests/unit/compute/v2/test_server.py +169 -46
  30. openstackclient/tests/unit/compute/v2/test_server_backup.py +32 -71
  31. openstackclient/tests/unit/compute/v2/test_server_event.py +2 -2
  32. openstackclient/tests/unit/compute/v2/test_server_image.py +33 -72
  33. openstackclient/tests/unit/compute/v2/test_server_migration.py +4 -4
  34. openstackclient/tests/unit/identity/v3/test_application_credential.py +47 -25
  35. openstackclient/tests/unit/identity/v3/test_domain.py +115 -105
  36. openstackclient/tests/unit/identity/v3/test_group.py +353 -202
  37. openstackclient/tests/unit/identity/v3/test_project.py +16 -0
  38. openstackclient/tests/unit/identity/v3/test_user.py +86 -6
  39. openstackclient/tests/unit/image/v1/test_image.py +8 -9
  40. openstackclient/tests/unit/image/v2/test_image.py +49 -49
  41. openstackclient/tests/unit/network/v2/fakes.py +405 -485
  42. openstackclient/tests/unit/network/v2/test_floating_ip_network.py +13 -19
  43. openstackclient/tests/unit/network/v2/test_l3_conntrack_helper.py +2 -2
  44. openstackclient/tests/unit/network/v2/test_ndp_proxy.py +1 -3
  45. openstackclient/tests/unit/network/v2/test_network.py +4 -4
  46. openstackclient/tests/unit/network/v2/test_network_agent.py +15 -29
  47. openstackclient/tests/unit/network/v2/test_network_qos_policy.py +16 -19
  48. openstackclient/tests/unit/network/v2/test_network_qos_rule.py +79 -152
  49. openstackclient/tests/unit/network/v2/test_network_qos_rule_type.py +4 -6
  50. openstackclient/tests/unit/network/v2/test_network_rbac.py +2 -2
  51. openstackclient/tests/unit/network/v2/test_port.py +17 -17
  52. openstackclient/tests/unit/network/v2/test_router.py +73 -57
  53. openstackclient/tests/unit/network/v2/test_security_group_network.py +25 -27
  54. openstackclient/tests/unit/network/v2/test_security_group_rule_compute.py +1 -3
  55. openstackclient/tests/unit/network/v2/test_security_group_rule_network.py +33 -39
  56. openstackclient/tests/unit/volume/v2/fakes.py +1 -2
  57. openstackclient/tests/unit/volume/v2/test_service.py +57 -91
  58. openstackclient/tests/unit/volume/v2/test_volume.py +108 -105
  59. openstackclient/tests/unit/volume/v2/test_volume_backup.py +141 -148
  60. openstackclient/tests/unit/volume/v2/test_volume_snapshot.py +293 -283
  61. openstackclient/tests/unit/volume/v3/test_block_storage_log_level.py +61 -71
  62. openstackclient/tests/unit/volume/v3/test_service.py +221 -141
  63. openstackclient/tests/unit/volume/v3/test_volume.py +130 -119
  64. openstackclient/tests/unit/volume/v3/test_volume_attachment.py +1 -1
  65. openstackclient/tests/unit/volume/v3/test_volume_backup.py +198 -203
  66. openstackclient/tests/unit/volume/v3/test_volume_snapshot.py +682 -47
  67. openstackclient/volume/v2/service.py +41 -38
  68. openstackclient/volume/v2/volume.py +63 -37
  69. openstackclient/volume/v2/volume_backup.py +9 -3
  70. openstackclient/volume/v2/volume_snapshot.py +121 -84
  71. openstackclient/volume/v3/block_storage_log_level.py +22 -28
  72. openstackclient/volume/v3/service.py +105 -14
  73. openstackclient/volume/v3/volume.py +200 -39
  74. openstackclient/volume/v3/volume_backup.py +24 -19
  75. openstackclient/volume/v3/volume_snapshot.py +485 -10
  76. {python_openstackclient-8.0.0.dist-info → python_openstackclient-8.1.0.dist-info}/AUTHORS +8 -0
  77. python_openstackclient-8.1.0.dist-info/METADATA +264 -0
  78. {python_openstackclient-8.0.0.dist-info → python_openstackclient-8.1.0.dist-info}/RECORD +83 -81
  79. {python_openstackclient-8.0.0.dist-info → python_openstackclient-8.1.0.dist-info}/entry_points.txt +7 -6
  80. python_openstackclient-8.1.0.dist-info/pbr.json +1 -0
  81. python_openstackclient-8.0.0.dist-info/METADATA +0 -166
  82. python_openstackclient-8.0.0.dist-info/pbr.json +0 -1
  83. {python_openstackclient-8.0.0.dist-info → python_openstackclient-8.1.0.dist-info}/LICENSE +0 -0
  84. {python_openstackclient-8.0.0.dist-info → python_openstackclient-8.1.0.dist-info}/WHEEL +0 -0
  85. {python_openstackclient-8.0.0.dist-info → python_openstackclient-8.1.0.dist-info}/top_level.txt +0 -0
@@ -17,7 +17,7 @@
17
17
 
18
18
  import logging
19
19
 
20
- from keystoneauth1 import exceptions as ks_exc
20
+ from openstack import exceptions as sdk_exc
21
21
  from osc_lib.command import command
22
22
  from osc_lib import exceptions
23
23
  from osc_lib import utils
@@ -29,6 +29,25 @@ from openstackclient.identity import common
29
29
  LOG = logging.getLogger(__name__)
30
30
 
31
31
 
32
+ def _format_group(group):
33
+ columns = (
34
+ 'description',
35
+ 'domain_id',
36
+ 'id',
37
+ 'name',
38
+ )
39
+ column_headers = (
40
+ 'description',
41
+ 'domain_id',
42
+ 'id',
43
+ 'name',
44
+ )
45
+ return (
46
+ column_headers,
47
+ utils.get_item_properties(group, columns),
48
+ )
49
+
50
+
32
51
  class AddUserToGroup(command.Command):
33
52
  _description = _("Add user to group")
34
53
 
@@ -53,19 +72,19 @@ class AddUserToGroup(command.Command):
53
72
  return parser
54
73
 
55
74
  def take_action(self, parsed_args):
56
- identity_client = self.app.client_manager.identity
75
+ identity_client = self.app.client_manager.sdk_connection.identity
57
76
 
58
- group_id = common.find_group(
77
+ group_id = common.find_group_id_sdk(
59
78
  identity_client, parsed_args.group, parsed_args.group_domain
60
- ).id
79
+ )
61
80
 
62
81
  result = 0
63
82
  for i in parsed_args.user:
64
83
  try:
65
- user_id = common.find_user(
84
+ user_id = common.find_user_id_sdk(
66
85
  identity_client, i, parsed_args.user_domain
67
- ).id
68
- identity_client.users.add_to_group(user_id, group_id)
86
+ )
87
+ identity_client.add_user_to_group(user_id, group_id)
69
88
  except Exception as e:
70
89
  result += 1
71
90
  msg = _("%(user)s not added to group %(group)s: %(e)s") % {
@@ -109,32 +128,41 @@ class CheckUserInGroup(command.Command):
109
128
  return parser
110
129
 
111
130
  def take_action(self, parsed_args):
112
- identity_client = self.app.client_manager.identity
131
+ identity_client = self.app.client_manager.sdk_connection.identity
113
132
 
114
- user_id = common.find_user(
115
- identity_client, parsed_args.user, parsed_args.user_domain
116
- ).id
117
- group_id = common.find_group(
118
- identity_client, parsed_args.group, parsed_args.group_domain
119
- ).id
133
+ user_id = common.find_user_id_sdk(
134
+ identity_client,
135
+ parsed_args.user,
136
+ parsed_args.user_domain,
137
+ validate_actor_existence=False,
138
+ )
139
+ group_id = common.find_group_id_sdk(
140
+ identity_client,
141
+ parsed_args.group,
142
+ parsed_args.group_domain,
143
+ validate_actor_existence=False,
144
+ )
120
145
 
146
+ user_in_group = False
121
147
  try:
122
- identity_client.users.check_in_group(user_id, group_id)
123
- except ks_exc.http.HTTPClientError as e:
124
- if e.http_status == 403 or e.http_status == 404:
125
- msg = _("%(user)s not in group %(group)s\n") % {
126
- 'user': parsed_args.user,
127
- 'group': parsed_args.group,
128
- }
129
- self.app.stderr.write(msg)
130
- else:
131
- raise e
132
- else:
148
+ user_in_group = identity_client.check_user_in_group(
149
+ user_id, group_id
150
+ )
151
+ except sdk_exc.ForbiddenException:
152
+ # Assume False if forbidden
153
+ pass
154
+ if user_in_group:
133
155
  msg = _("%(user)s in group %(group)s\n") % {
134
156
  'user': parsed_args.user,
135
157
  'group': parsed_args.group,
136
158
  }
137
159
  self.app.stdout.write(msg)
160
+ else:
161
+ msg = _("%(user)s not in group %(group)s\n") % {
162
+ 'user': parsed_args.user,
163
+ 'group': parsed_args.group,
164
+ }
165
+ self.app.stderr.write(msg)
138
166
 
139
167
 
140
168
  class CreateGroup(command.ShowOne):
@@ -165,29 +193,33 @@ class CreateGroup(command.ShowOne):
165
193
  return parser
166
194
 
167
195
  def take_action(self, parsed_args):
168
- identity_client = self.app.client_manager.identity
196
+ identity_client = self.app.client_manager.sdk_connection.identity
169
197
 
170
- domain = None
198
+ kwargs = {}
199
+ if parsed_args.name:
200
+ kwargs['name'] = parsed_args.name
201
+ if parsed_args.description:
202
+ kwargs['description'] = parsed_args.description
171
203
  if parsed_args.domain:
172
- domain = common.find_domain(identity_client, parsed_args.domain).id
204
+ kwargs['domain_id'] = common.find_domain_id_sdk(
205
+ identity_client, parsed_args.domain
206
+ )
173
207
 
174
208
  try:
175
- group = identity_client.groups.create(
176
- name=parsed_args.name,
177
- domain=domain,
178
- description=parsed_args.description,
179
- )
180
- except ks_exc.Conflict:
209
+ group = identity_client.create_group(**kwargs)
210
+ except sdk_exc.ConflictException:
181
211
  if parsed_args.or_show:
182
- group = utils.find_resource(
183
- identity_client.groups, parsed_args.name, domain_id=domain
184
- )
212
+ if parsed_args.domain:
213
+ group = identity_client.find_group(
214
+ parsed_args.name, domain_id=parsed_args.domain
215
+ )
216
+ else:
217
+ group = identity_client.find_group(parsed_args.name)
185
218
  LOG.info(_('Returning existing group %s'), group.name)
186
219
  else:
187
220
  raise
188
221
 
189
- group._info.pop('links')
190
- return zip(*sorted(group._info.items()))
222
+ return _format_group(group)
191
223
 
192
224
 
193
225
  class DeleteGroup(command.Command):
@@ -209,15 +241,15 @@ class DeleteGroup(command.Command):
209
241
  return parser
210
242
 
211
243
  def take_action(self, parsed_args):
212
- identity_client = self.app.client_manager.identity
244
+ identity_client = self.app.client_manager.sdk_connection.identity
213
245
 
214
246
  errors = 0
215
247
  for group in parsed_args.groups:
216
248
  try:
217
- group_obj = common.find_group(
249
+ group_id = common.find_group_id_sdk(
218
250
  identity_client, group, parsed_args.domain
219
251
  )
220
- identity_client.groups.delete(group_obj.id)
252
+ identity_client.delete_group(group_id)
221
253
  except Exception as e:
222
254
  errors += 1
223
255
  LOG.error(
@@ -262,29 +294,37 @@ class ListGroup(command.Lister):
262
294
  return parser
263
295
 
264
296
  def take_action(self, parsed_args):
265
- identity_client = self.app.client_manager.identity
297
+ identity_client = self.app.client_manager.sdk_connection.identity
266
298
 
267
299
  domain = None
268
300
  if parsed_args.domain:
269
- domain = common.find_domain(identity_client, parsed_args.domain).id
301
+ domain = common.find_domain_id_sdk(
302
+ identity_client, parsed_args.domain
303
+ )
270
304
 
305
+ data = []
271
306
  if parsed_args.user:
272
- user = common.find_user(
307
+ user = common.find_user_id_sdk(
273
308
  identity_client,
274
309
  parsed_args.user,
275
310
  parsed_args.user_domain,
276
- ).id
311
+ )
312
+ if domain:
313
+ # NOTE(0weng): The API doesn't actually support filtering additionally by domain_id,
314
+ # so this doesn't really do anything.
315
+ data = identity_client.user_groups(user, domain_id=domain)
316
+ else:
317
+ data = identity_client.user_groups(user)
277
318
  else:
278
- user = None
319
+ if domain:
320
+ data = identity_client.groups(domain_id=domain)
321
+ else:
322
+ data = identity_client.groups()
279
323
 
280
324
  # List groups
281
325
  columns: tuple[str, ...] = ('ID', 'Name')
282
326
  if parsed_args.long:
283
327
  columns += ('Domain ID', 'Description')
284
- data = identity_client.groups.list(
285
- domain=domain,
286
- user=user,
287
- )
288
328
 
289
329
  return (
290
330
  columns,
@@ -323,19 +363,19 @@ class RemoveUserFromGroup(command.Command):
323
363
  return parser
324
364
 
325
365
  def take_action(self, parsed_args):
326
- identity_client = self.app.client_manager.identity
366
+ identity_client = self.app.client_manager.sdk_connection.identity
327
367
 
328
- group_id = common.find_group(
368
+ group_id = common.find_group_id_sdk(
329
369
  identity_client, parsed_args.group, parsed_args.group_domain
330
- ).id
370
+ )
331
371
 
332
372
  result = 0
333
373
  for i in parsed_args.user:
334
374
  try:
335
- user_id = common.find_user(
375
+ user_id = common.find_user_id_sdk(
336
376
  identity_client, i, parsed_args.user_domain
337
- ).id
338
- identity_client.users.remove_from_group(user_id, group_id)
377
+ )
378
+ identity_client.remove_user_from_group(user_id, group_id)
339
379
  except Exception as e:
340
380
  result += 1
341
381
  msg = _("%(user)s not removed from group %(group)s: %(e)s") % {
@@ -387,8 +427,8 @@ class SetGroup(command.Command):
387
427
  return parser
388
428
 
389
429
  def take_action(self, parsed_args):
390
- identity_client = self.app.client_manager.identity
391
- group = common.find_group(
430
+ identity_client = self.app.client_manager.sdk_connection.identity
431
+ group = common.find_group_id_sdk(
392
432
  identity_client, parsed_args.group, parsed_args.domain
393
433
  )
394
434
  kwargs = {}
@@ -397,7 +437,7 @@ class SetGroup(command.Command):
397
437
  if parsed_args.description:
398
438
  kwargs['description'] = parsed_args.description
399
439
 
400
- identity_client.groups.update(group.id, **kwargs)
440
+ identity_client.update_group(group, **kwargs)
401
441
 
402
442
 
403
443
  class ShowGroup(command.ShowOne):
@@ -418,13 +458,18 @@ class ShowGroup(command.ShowOne):
418
458
  return parser
419
459
 
420
460
  def take_action(self, parsed_args):
421
- identity_client = self.app.client_manager.identity
461
+ identity_client = self.app.client_manager.sdk_connection.identity
422
462
 
423
- group = common.find_group(
424
- identity_client,
425
- parsed_args.group,
426
- domain_name_or_id=parsed_args.domain,
427
- )
463
+ if parsed_args.domain:
464
+ domain = common.find_domain_id_sdk(
465
+ identity_client, parsed_args.domain
466
+ )
467
+ group = identity_client.find_group(
468
+ parsed_args.group, domain_id=domain, ignore_missing=False
469
+ )
470
+ else:
471
+ group = identity_client.find_group(
472
+ parsed_args.group, ignore_missing=False
473
+ )
428
474
 
429
- group._info.pop('links')
430
- return zip(*sorted(group._info.items()))
475
+ return _format_group(group)
@@ -240,6 +240,20 @@ class ListProject(command.Lister):
240
240
  'keys and directions.'
241
241
  ),
242
242
  )
243
+ parser.add_argument(
244
+ '--enabled',
245
+ action='store_true',
246
+ dest='is_enabled',
247
+ default=None,
248
+ help=_('List only enabled projects'),
249
+ )
250
+ parser.add_argument(
251
+ '--disabled',
252
+ action='store_false',
253
+ dest='is_enabled',
254
+ default=None,
255
+ help=_('List only disabled projects'),
256
+ )
243
257
  tag.add_tag_filtering_option_to_parser(parser, _('projects'))
244
258
  return parser
245
259
 
@@ -277,6 +291,9 @@ class ListProject(command.Lister):
277
291
 
278
292
  kwargs['user'] = user_id
279
293
 
294
+ if parsed_args.is_enabled is not None:
295
+ kwargs['is_enabled'] = parsed_args.is_enabled
296
+
280
297
  tag.get_tag_filtering_args(parsed_args, kwargs)
281
298
 
282
299
  if parsed_args.my_projects:
@@ -41,6 +41,7 @@ def _format_user(user):
41
41
  'name',
42
42
  'description',
43
43
  'password_expires_at',
44
+ 'options',
44
45
  )
45
46
  column_headers = (
46
47
  'default_project_id',
@@ -51,6 +52,7 @@ def _format_user(user):
51
52
  'name',
52
53
  'description',
53
54
  'password_expires_at',
55
+ 'options',
54
56
  )
55
57
  return (
56
58
  column_headers,
@@ -289,7 +291,7 @@ class CreateUser(command.ShowOne):
289
291
  elif parsed_args.password_prompt:
290
292
  password = utils.get_password(self.app.stdin)
291
293
 
292
- if not parsed_args.password:
294
+ if not password:
293
295
  LOG.warning(
294
296
  _(
295
297
  "No password was supplied, authentication will fail "
@@ -412,6 +414,24 @@ class ListUser(command.Lister):
412
414
  default=False,
413
415
  help=_('List additional fields in output'),
414
416
  )
417
+ parser.add_argument(
418
+ '--enabled',
419
+ action='store_true',
420
+ dest='is_enabled',
421
+ default=None,
422
+ help=_(
423
+ 'List only enabled users, does nothing with --project and --group'
424
+ ),
425
+ )
426
+ parser.add_argument(
427
+ '--disabled',
428
+ action='store_false',
429
+ dest='is_enabled',
430
+ default=None,
431
+ help=_(
432
+ 'List only disabled users, does nothing with --project and --group'
433
+ ),
434
+ )
415
435
  return parser
416
436
 
417
437
  def take_action(self, parsed_args):
@@ -431,6 +451,9 @@ class ListUser(command.Lister):
431
451
  ignore_missing=False,
432
452
  ).id
433
453
 
454
+ if parsed_args.is_enabled is not None:
455
+ enabled = parsed_args.is_enabled
456
+
434
457
  if parsed_args.project:
435
458
  if domain is not None:
436
459
  project = identity_client.find_project(
@@ -469,9 +492,15 @@ class ListUser(command.Lister):
469
492
  group=group,
470
493
  )
471
494
  else:
472
- data = identity_client.users(
473
- domain_id=domain,
474
- )
495
+ if parsed_args.is_enabled is not None:
496
+ data = identity_client.users(
497
+ domain_id=domain,
498
+ is_enabled=enabled,
499
+ )
500
+ else:
501
+ data = identity_client.users(
502
+ domain_id=domain,
503
+ )
475
504
 
476
505
  # Column handling
477
506
  if parsed_args.long:
@@ -659,6 +688,8 @@ class SetPasswordUser(command.Command):
659
688
 
660
689
  def take_action(self, parsed_args):
661
690
  identity_client = self.app.client_manager.sdk_connection.identity
691
+ conn = self.app.client_manager.sdk_connection
692
+ user_id = conn.config.get_auth().get_user_id(conn.identity)
662
693
 
663
694
  # FIXME(gyee): there are two scenarios:
664
695
  #
@@ -701,7 +732,9 @@ class SetPasswordUser(command.Command):
701
732
  )
702
733
 
703
734
  identity_client.update_user(
704
- current_password=current_password, password=password
735
+ user=user_id,
736
+ current_password=current_password,
737
+ password=password,
705
738
  )
706
739
 
707
740
 
@@ -46,3 +46,8 @@ def build_option_parser(parser):
46
46
  % DEFAULT_API_VERSION,
47
47
  )
48
48
  return parser
49
+
50
+
51
+ def check_api_version(check_version):
52
+ # SDK supports auto-negotiation for us: always return True
53
+ return True
@@ -17,14 +17,15 @@
17
17
 
18
18
  import argparse
19
19
  from base64 import b64encode
20
+ import copy
20
21
  import logging
21
22
  import os
22
23
  import sys
23
24
  import typing as ty
24
25
 
25
- from cinderclient import api_versions
26
26
  from openstack import exceptions as sdk_exceptions
27
27
  from openstack.image import image_signer
28
+ from openstack import utils as sdk_utils
28
29
  from osc_lib.api import utils as api_utils
29
30
  from osc_lib.cli import format_columns
30
31
  from osc_lib.cli import parseractions
@@ -576,7 +577,7 @@ class CreateImage(command.ShowOne):
576
577
  return _format_image(image)
577
578
 
578
579
  def _take_action_volume(self, parsed_args):
579
- volume_client = self.app.client_manager.volume
580
+ volume_client = self.app.client_manager.sdk_connection.volume
580
581
 
581
582
  unsupported_opts = {
582
583
  # 'name', # 'name' is a positional argument and will always exist
@@ -607,15 +608,14 @@ class CreateImage(command.ShowOne):
607
608
  # version
608
609
  LOG.warning(msg % opt_name)
609
610
 
610
- source_volume = utils.find_resource(
611
- volume_client.volumes,
612
- parsed_args.volume,
611
+ source_volume = volume_client.find_volume(
612
+ parsed_args.volume, ignore_missing=False
613
613
  )
614
614
  kwargs: dict[str, ty.Any] = {
615
615
  'visibility': None,
616
616
  'protected': None,
617
617
  }
618
- if volume_client.api_version < api_versions.APIVersion('3.1'):
618
+ if not sdk_utils.supports_microversion(volume_client, '3.1'):
619
619
  if parsed_args.visibility or parsed_args.is_protected is not None:
620
620
  msg = _(
621
621
  '--os-volume-api-version 3.1 or greater is required '
@@ -627,15 +627,15 @@ class CreateImage(command.ShowOne):
627
627
  kwargs['visibility'] = parsed_args.visibility or 'private'
628
628
  kwargs['protected'] = parsed_args.is_protected or False
629
629
 
630
- response, body = volume_client.volumes.upload_to_image(
630
+ response = volume_client.upload_volume_to_image(
631
631
  source_volume.id,
632
- parsed_args.force,
633
632
  parsed_args.name,
634
- parsed_args.container_format,
635
- parsed_args.disk_format,
633
+ force=parsed_args.force,
634
+ disk_format=parsed_args.disk_format,
635
+ container_format=parsed_args.container_format,
636
636
  **kwargs,
637
637
  )
638
- info = body['os-volume_upload_image']
638
+ info = copy.deepcopy(response)
639
639
  try:
640
640
  info['volume_type'] = info['volume_type']['name']
641
641
  except TypeError:
@@ -27,12 +27,6 @@ API_VERSIONS = ('2.0', '2')
27
27
 
28
28
  def make_client(instance):
29
29
  """Returns a network proxy"""
30
- # NOTE(dtroyer): As of osc-lib 1.8.0 and OpenStackSDK 0.10.0 the
31
- # old Profile interface and separate client creation
32
- # for each API that uses the SDK is unnecessary. This
33
- # callback remains as a remnant of the original plugin
34
- # interface and to avoid the code churn of changing all
35
- # of the existing references.
36
30
  LOG.debug(
37
31
  'Network client initialized using OpenStack SDK: %s',
38
32
  instance.sdk_connection.network,
@@ -243,18 +243,26 @@ class ListFloatingIP(common.NetworkAndComputeLister):
243
243
  parser.add_argument(
244
244
  '--network',
245
245
  metavar='<network>',
246
+ dest='networks',
247
+ action='append',
246
248
  help=self.enhance_help_neutron(
247
249
  _(
248
- "List floating IP(s) according to "
249
- "given network (name or ID)"
250
+ "List floating IP(s) according to given network "
251
+ "(name or ID) "
252
+ "(repeat option to fiter on multiple networks)"
250
253
  )
251
254
  ),
252
255
  )
253
256
  parser.add_argument(
254
257
  '--port',
255
258
  metavar='<port>',
259
+ dest='ports',
260
+ action='append',
256
261
  help=self.enhance_help_neutron(
257
- _("List floating IP(s) according to given port (name or ID)")
262
+ _(
263
+ "List floating IP(s) according to given port (name or ID) "
264
+ "(repeat option to fiter on multiple ports)"
265
+ )
258
266
  ),
259
267
  )
260
268
  parser.add_argument(
@@ -271,14 +279,6 @@ class ListFloatingIP(common.NetworkAndComputeLister):
271
279
  _("List floating IP(s) according to given floating IP address")
272
280
  ),
273
281
  )
274
- parser.add_argument(
275
- '--long',
276
- action='store_true',
277
- default=False,
278
- help=self.enhance_help_neutron(
279
- _("List additional fields in output")
280
- ),
281
- )
282
282
  parser.add_argument(
283
283
  '--status',
284
284
  metavar='<status>',
@@ -295,8 +295,8 @@ class ListFloatingIP(common.NetworkAndComputeLister):
295
295
  metavar='<project>',
296
296
  help=self.enhance_help_neutron(
297
297
  _(
298
- "List floating IP(s) according to given project (name or "
299
- "ID)"
298
+ "List floating IP(s) according to given project "
299
+ "(name or ID) "
300
300
  )
301
301
  ),
302
302
  )
@@ -304,13 +304,27 @@ class ListFloatingIP(common.NetworkAndComputeLister):
304
304
  parser.add_argument(
305
305
  '--router',
306
306
  metavar='<router>',
307
+ dest='routers',
308
+ action='append',
307
309
  help=self.enhance_help_neutron(
308
- _("List floating IP(s) according to given router (name or ID)")
310
+ _(
311
+ "List floating IP(s) according to given router "
312
+ "(name or ID) "
313
+ "(repeat option to fiter on multiple routers)"
314
+ )
309
315
  ),
310
316
  )
311
317
  _tag.add_tag_filtering_option_to_parser(
312
318
  parser, _('floating IP'), enhance_help=self.enhance_help_neutron
313
319
  )
320
+ parser.add_argument(
321
+ '--long',
322
+ action='store_true',
323
+ default=False,
324
+ help=self.enhance_help_neutron(
325
+ _("List additional fields in output")
326
+ ),
327
+ )
314
328
 
315
329
  return parser
316
330
 
@@ -354,22 +368,33 @@ class ListFloatingIP(common.NetworkAndComputeLister):
354
368
 
355
369
  query = {}
356
370
 
357
- if parsed_args.network is not None:
358
- network = network_client.find_network(
359
- parsed_args.network, ignore_missing=False
360
- )
361
- query['floating_network_id'] = network.id
362
- if parsed_args.port is not None:
363
- port = network_client.find_port(
364
- parsed_args.port, ignore_missing=False
365
- )
366
- query['port_id'] = port.id
371
+ if parsed_args.networks is not None:
372
+ network_ids = []
373
+ for network in parsed_args.networks:
374
+ network_id = network_client.find_network(
375
+ network, ignore_missing=False
376
+ ).id
377
+ network_ids.append(network_id)
378
+ query['floating_network_id'] = network_ids
379
+
380
+ if parsed_args.ports is not None:
381
+ port_ids = []
382
+ for port in parsed_args.ports:
383
+ port_id = network_client.find_port(
384
+ port, ignore_missing=False
385
+ ).id
386
+ port_ids.append(port_id)
387
+ query['port_id'] = port_ids
388
+
367
389
  if parsed_args.fixed_ip_address is not None:
368
390
  query['fixed_ip_address'] = parsed_args.fixed_ip_address
391
+
369
392
  if parsed_args.floating_ip_address is not None:
370
393
  query['floating_ip_address'] = parsed_args.floating_ip_address
394
+
371
395
  if parsed_args.status:
372
396
  query['status'] = parsed_args.status
397
+
373
398
  if parsed_args.project is not None:
374
399
  project = identity_common.find_project(
375
400
  identity_client,
@@ -377,11 +402,15 @@ class ListFloatingIP(common.NetworkAndComputeLister):
377
402
  parsed_args.project_domain,
378
403
  )
379
404
  query['project_id'] = project.id
380
- if parsed_args.router is not None:
381
- router = network_client.find_router(
382
- parsed_args.router, ignore_missing=False
383
- )
384
- query['router_id'] = router.id
405
+
406
+ if parsed_args.routers is not None:
407
+ router_ids = []
408
+ for router in parsed_args.routers:
409
+ router_id = network_client.find_router(
410
+ router, ignore_missing=False
411
+ ).id
412
+ router_ids.append(router_id)
413
+ query['router_id'] = router_ids
385
414
 
386
415
  _tag.get_tag_filtering_args(parsed_args, query)
387
416