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
@@ -126,7 +126,7 @@ class SetService(command.Command):
126
126
  volume_client = self.app.client_manager.sdk_connection.volume
127
127
 
128
128
  service = volume_client.find_service(
129
- host=parsed_args.host, service=parsed_args.service
129
+ parsed_args.service, ignore_missing=False, host=parsed_args.host
130
130
  )
131
131
 
132
132
  if parsed_args.enable:
@@ -18,8 +18,10 @@ import argparse
18
18
  import copy
19
19
  import functools
20
20
  import logging
21
+ import typing as ty
21
22
 
22
23
  from cliff import columns as cliff_columns
24
+ from openstack.block_storage.v2 import volume as _volume
23
25
  from openstack import exceptions as sdk_exceptions
24
26
  from osc_lib.cli import format_columns
25
27
  from osc_lib.cli import parseractions
@@ -27,6 +29,7 @@ from osc_lib.command import command
27
29
  from osc_lib import exceptions
28
30
  from osc_lib import utils
29
31
 
32
+ from openstackclient.api import volume_v2
30
33
  from openstackclient.common import pagination
31
34
  from openstackclient.i18n import _
32
35
  from openstackclient.identity import common as identity_common
@@ -89,6 +92,47 @@ class AttachmentsColumn(cliff_columns.FormattableColumn):
89
92
  return msg
90
93
 
91
94
 
95
+ def _format_volume(volume: _volume.Volume) -> dict[str, ty.Any]:
96
+ # Some columns returned by openstacksdk should not be shown because they're
97
+ # either irrelevant or duplicates
98
+ ignored_columns = {
99
+ # computed columns
100
+ 'location',
101
+ # create-only columns
102
+ 'OS-SCH-HNT:scheduler_hints',
103
+ 'imageRef',
104
+ # unnecessary columns
105
+ 'links',
106
+ }
107
+ optional_columns = {
108
+ # only present if part of a consistency group
109
+ 'consistencygroup_id',
110
+ # only present if there are image properties associated
111
+ 'volume_image_metadata',
112
+ }
113
+
114
+ info = volume.to_dict(original_names=True)
115
+ data = {}
116
+ for key, value in info.items():
117
+ if key in ignored_columns:
118
+ continue
119
+
120
+ if key in optional_columns:
121
+ if info[key] is None:
122
+ continue
123
+
124
+ data[key] = value
125
+
126
+ data.update(
127
+ {
128
+ 'properties': format_columns.DictColumn(data.pop('metadata')),
129
+ 'type': data.pop('volume_type'),
130
+ }
131
+ )
132
+
133
+ return data
134
+
135
+
92
136
  class CreateVolume(command.ShowOne):
93
137
  _description = _("Create new volume")
94
138
 
@@ -226,22 +270,22 @@ class CreateVolume(command.ShowOne):
226
270
  # volume from snapshot or source volume
227
271
  size = parsed_args.size
228
272
 
229
- volume_client = self.app.client_manager.volume
273
+ volume_client = self.app.client_manager.sdk_connection.volume
230
274
  image_client = self.app.client_manager.image
231
275
 
232
276
  source_volume = None
233
277
  if parsed_args.source:
234
- source_volume_obj = utils.find_resource(
235
- volume_client.volumes, parsed_args.source
278
+ source_volume_obj = volume_client.find_volume(
279
+ parsed_args.source, ignore_missing=False
236
280
  )
237
281
  source_volume = source_volume_obj.id
238
282
  size = max(size or 0, source_volume_obj.size)
239
283
 
240
284
  consistency_group = None
241
285
  if parsed_args.consistency_group:
242
- consistency_group = utils.find_resource(
243
- volume_client.consistencygroups, parsed_args.consistency_group
244
- ).id
286
+ consistency_group = volume_v2.find_consistency_group(
287
+ volume_client, parsed_args.consistency_group
288
+ )['id']
245
289
 
246
290
  image = None
247
291
  if parsed_args.image:
@@ -251,8 +295,8 @@ class CreateVolume(command.ShowOne):
251
295
 
252
296
  snapshot = None
253
297
  if parsed_args.snapshot:
254
- snapshot_obj = utils.find_resource(
255
- volume_client.volume_snapshots, parsed_args.snapshot
298
+ snapshot_obj = volume_client.find_snapshot(
299
+ parsed_args.snapshot, ignore_missing=False
256
300
  )
257
301
  snapshot = snapshot_obj.id
258
302
  # Cinder requires a value for size when creating a volume
@@ -263,7 +307,7 @@ class CreateVolume(command.ShowOne):
263
307
  # snapshot size.
264
308
  size = max(size or 0, snapshot_obj.size)
265
309
 
266
- volume = volume_client.volumes.create(
310
+ volume = volume_client.create_volume(
267
311
  size=size,
268
312
  snapshot_id=snapshot,
269
313
  name=parsed_args.name,
@@ -271,23 +315,23 @@ class CreateVolume(command.ShowOne):
271
315
  volume_type=parsed_args.type,
272
316
  availability_zone=parsed_args.availability_zone,
273
317
  metadata=parsed_args.properties,
274
- imageRef=image,
275
- source_volid=source_volume,
276
- consistencygroup_id=consistency_group,
318
+ image_id=image,
319
+ source_volume_id=source_volume,
320
+ consistency_group_id=consistency_group,
277
321
  scheduler_hints=parsed_args.hint,
278
322
  )
279
323
 
280
324
  if parsed_args.bootable is not None:
281
325
  try:
282
326
  if utils.wait_for_status(
283
- volume_client.volumes.get,
327
+ volume_client.get_volume,
284
328
  volume.id,
285
329
  success_status=['available'],
286
330
  error_status=['error'],
287
331
  sleep_time=1,
288
332
  ):
289
- volume_client.volumes.set_bootable(
290
- volume.id, parsed_args.bootable
333
+ volume_client.set_volume_bootable_status(
334
+ volume, parsed_args.bootable
291
335
  )
292
336
  else:
293
337
  msg = _(
@@ -300,14 +344,14 @@ class CreateVolume(command.ShowOne):
300
344
  if parsed_args.read_only is not None:
301
345
  try:
302
346
  if utils.wait_for_status(
303
- volume_client.volumes.get,
347
+ volume_client.get_volume,
304
348
  volume.id,
305
349
  success_status=['available'],
306
350
  error_status=['error'],
307
351
  sleep_time=1,
308
352
  ):
309
- volume_client.volumes.update_readonly_flag(
310
- volume.id, parsed_args.read_only
353
+ volume_client.set_volume_readonly(
354
+ volume, parsed_args.read_only
311
355
  )
312
356
  else:
313
357
  msg = _(
@@ -321,17 +365,8 @@ class CreateVolume(command.ShowOne):
321
365
  e,
322
366
  )
323
367
 
324
- # Remove key links from being displayed
325
- volume._info.update(
326
- {
327
- 'properties': format_columns.DictColumn(
328
- volume._info.pop('metadata')
329
- ),
330
- 'type': volume._info.pop('volume_type'),
331
- }
332
- )
333
- volume._info.pop("links", None)
334
- return zip(*sorted(volume._info.items()))
368
+ data = _format_volume(volume)
369
+ return zip(*sorted(data.items()))
335
370
 
336
371
 
337
372
  class DeleteVolume(command.Command):
@@ -578,13 +613,15 @@ class MigrateVolume(command.Command):
578
613
  return parser
579
614
 
580
615
  def take_action(self, parsed_args):
581
- volume_client = self.app.client_manager.volume
582
- volume = utils.find_resource(volume_client.volumes, parsed_args.volume)
583
- volume_client.volumes.migrate_volume(
616
+ volume_client = self.app.client_manager.sdk_connection.volume
617
+ volume = volume_client.find_volume(
618
+ parsed_args.volume, ignore_missing=False
619
+ )
620
+ volume_client.migrate_volume(
584
621
  volume.id,
585
- parsed_args.host,
586
- parsed_args.force_host_copy,
587
- parsed_args.lock_volume,
622
+ host=parsed_args.host,
623
+ force_host_copy=parsed_args.force_host_copy,
624
+ lock_volume=parsed_args.lock_volume,
588
625
  )
589
626
 
590
627
 
@@ -918,24 +955,13 @@ class ShowVolume(command.ShowOne):
918
955
  return parser
919
956
 
920
957
  def take_action(self, parsed_args):
921
- volume_client = self.app.client_manager.volume
922
- volume = utils.find_resource(volume_client.volumes, parsed_args.volume)
958
+ volume_client = self.app.client_manager.sdk_connection.volume
959
+ volume = volume_client.find_volume(
960
+ parsed_args.volume, ignore_missing=False
961
+ )
923
962
 
924
- # Special mapping for columns to make the output easier to read:
925
- # 'metadata' --> 'properties'
926
- # 'volume_type' --> 'type'
927
- volume._info.update(
928
- {
929
- 'properties': format_columns.DictColumn(
930
- volume._info.pop('metadata')
931
- ),
932
- 'type': volume._info.pop('volume_type'),
933
- },
934
- )
935
-
936
- # Remove key links from being displayed
937
- volume._info.pop("links", None)
938
- return zip(*sorted(volume._info.items()))
963
+ data = _format_volume(volume)
964
+ return zip(*sorted(data.items()))
939
965
 
940
966
 
941
967
  class UnsetVolume(command.Command):
@@ -133,7 +133,7 @@ class SetService(command.Command):
133
133
  volume_client = self.app.client_manager.sdk_connection.volume
134
134
 
135
135
  service = volume_client.find_service(
136
- host=parsed_args.host, service=parsed_args.service
136
+ parsed_args.service, ignore_missing=False, host=parsed_args.host
137
137
  )
138
138
 
139
139
  if parsed_args.enable:
@@ -18,8 +18,10 @@ import argparse
18
18
  import copy
19
19
  import functools
20
20
  import logging
21
+ import typing as ty
21
22
 
22
23
  from cliff import columns as cliff_columns
24
+ from openstack.block_storage.v3 import volume as _volume
23
25
  from openstack import exceptions as sdk_exceptions
24
26
  from openstack import utils as sdk_utils
25
27
  from osc_lib.cli import format_columns
@@ -28,6 +30,7 @@ from osc_lib.command import command
28
30
  from osc_lib import exceptions
29
31
  from osc_lib import utils
30
32
 
33
+ from openstackclient.api import volume_v3
31
34
  from openstackclient.common import pagination
32
35
  from openstackclient.i18n import _
33
36
  from openstackclient.identity import common as identity_common
@@ -90,6 +93,52 @@ class AttachmentsColumn(cliff_columns.FormattableColumn):
90
93
  return msg
91
94
 
92
95
 
96
+ def _format_volume(volume: _volume.Volume) -> dict[str, ty.Any]:
97
+ # Some columns returned by openstacksdk should not be shown because they're
98
+ # either irrelevant or duplicates
99
+ ignored_columns = {
100
+ # computed columns
101
+ 'location',
102
+ # create-only columns
103
+ 'OS-SCH-HNT:scheduler_hints',
104
+ 'imageRef',
105
+ # removed columns
106
+ 'os-volume-replication:driver_data',
107
+ 'os-volume-replication:extended_status',
108
+ # unnecessary columns
109
+ 'links',
110
+ }
111
+ optional_columns = {
112
+ # only present if part of a consistency group
113
+ 'consistencygroup_id',
114
+ # only present if the volume is encrypted
115
+ 'encryption_key_id',
116
+ # only present if there are image properties associated
117
+ 'volume_image_metadata',
118
+ }
119
+
120
+ info = volume.to_dict(original_names=True)
121
+ data = {}
122
+ for key, value in info.items():
123
+ if key in ignored_columns:
124
+ continue
125
+
126
+ if key in optional_columns:
127
+ if info[key] is None:
128
+ continue
129
+
130
+ data[key] = value
131
+
132
+ data.update(
133
+ {
134
+ 'properties': format_columns.DictColumn(data.pop('metadata')),
135
+ 'type': data.pop('volume_type'),
136
+ }
137
+ )
138
+
139
+ return data
140
+
141
+
93
142
  class CreateVolume(command.ShowOne):
94
143
  _description = _("Create new volume")
95
144
 
@@ -272,8 +321,7 @@ class CreateVolume(command.ShowOne):
272
321
  # volume from snapshot, backup or source volume
273
322
  size = parsed_args.size
274
323
 
275
- volume_client_sdk = self.app.client_manager.sdk_connection.volume
276
- volume_client = self.app.client_manager.volume
324
+ volume_client = self.app.client_manager.sdk_connection.volume
277
325
  image_client = self.app.client_manager.image
278
326
 
279
327
  if (
@@ -285,8 +333,8 @@ class CreateVolume(command.ShowOne):
285
333
  )
286
334
  raise exceptions.CommandError(msg)
287
335
 
288
- if parsed_args.backup and not (
289
- volume_client.api_version.matches('3.47')
336
+ if parsed_args.backup and not sdk_utils.supports_microversion(
337
+ volume_client, '3.47'
290
338
  ):
291
339
  msg = _(
292
340
  "--os-volume-api-version 3.47 or greater is required "
@@ -308,9 +356,7 @@ class CreateVolume(command.ShowOne):
308
356
  )
309
357
  raise exceptions.CommandError(msg)
310
358
  if parsed_args.cluster:
311
- if not sdk_utils.supports_microversion(
312
- volume_client_sdk, '3.16'
313
- ):
359
+ if not sdk_utils.supports_microversion(volume_client, '3.16'):
314
360
  msg = _(
315
361
  "--os-volume-api-version 3.16 or greater is required "
316
362
  "to support the cluster parameter."
@@ -328,7 +374,7 @@ class CreateVolume(command.ShowOne):
328
374
  "manage a volume."
329
375
  )
330
376
  raise exceptions.CommandError(msg)
331
- volume = volume_client_sdk.manage_volume(
377
+ volume = volume_client.manage_volume(
332
378
  host=parsed_args.host,
333
379
  cluster=parsed_args.cluster,
334
380
  ref=parsed_args.remote_source,
@@ -339,35 +385,22 @@ class CreateVolume(command.ShowOne):
339
385
  metadata=parsed_args.properties,
340
386
  bootable=parsed_args.bootable,
341
387
  )
342
- data = {}
343
- for key, value in volume.to_dict().items():
344
- # FIXME(stephenfin): Stop ignoring these once we bump SDK
345
- # https://review.opendev.org/c/openstack/openstacksdk/+/945836/
346
- if key in (
347
- 'cluster_name',
348
- 'consumes_quota',
349
- 'encryption_key_id',
350
- 'service_uuid',
351
- 'shared_targets',
352
- 'volume_type_id',
353
- ):
354
- continue
355
- data[key] = value
388
+ data = _format_volume(volume)
356
389
  return zip(*sorted(data.items()))
357
390
 
358
391
  source_volume = None
359
392
  if parsed_args.source:
360
- source_volume_obj = utils.find_resource(
361
- volume_client.volumes, parsed_args.source
393
+ source_volume_obj = volume_client.find_volume(
394
+ parsed_args.source, ignore_missing=False
362
395
  )
363
396
  source_volume = source_volume_obj.id
364
397
  size = max(size or 0, source_volume_obj.size)
365
398
 
366
399
  consistency_group = None
367
400
  if parsed_args.consistency_group:
368
- consistency_group = utils.find_resource(
369
- volume_client.consistencygroups, parsed_args.consistency_group
370
- ).id
401
+ consistency_group = volume_v3.find_consistency_group(
402
+ volume_client, parsed_args.consistency_group
403
+ )['id']
371
404
 
372
405
  image = None
373
406
  if parsed_args.image:
@@ -377,8 +410,8 @@ class CreateVolume(command.ShowOne):
377
410
 
378
411
  snapshot = None
379
412
  if parsed_args.snapshot:
380
- snapshot_obj = utils.find_resource(
381
- volume_client.volume_snapshots, parsed_args.snapshot
413
+ snapshot_obj = volume_client.find_snapshot(
414
+ parsed_args.snapshot, ignore_missing=False
382
415
  )
383
416
  snapshot = snapshot_obj.id
384
417
  # Cinder requires a value for size when creating a volume
@@ -391,14 +424,14 @@ class CreateVolume(command.ShowOne):
391
424
 
392
425
  backup = None
393
426
  if parsed_args.backup:
394
- backup_obj = utils.find_resource(
395
- volume_client.backups, parsed_args.backup
427
+ backup_obj = volume_client.find_backup(
428
+ parsed_args.backup, ignore_missing=False
396
429
  )
397
430
  backup = backup_obj.id
398
431
  # As above
399
432
  size = max(size or 0, backup_obj.size)
400
433
 
401
- volume = volume_client.volumes.create(
434
+ volume = volume_client.create_volume(
402
435
  size=size,
403
436
  snapshot_id=snapshot,
404
437
  name=parsed_args.name,
@@ -406,9 +439,9 @@ class CreateVolume(command.ShowOne):
406
439
  volume_type=parsed_args.type,
407
440
  availability_zone=parsed_args.availability_zone,
408
441
  metadata=parsed_args.properties,
409
- imageRef=image,
410
- source_volid=source_volume,
411
- consistencygroup_id=consistency_group,
442
+ image_id=image,
443
+ source_volume_id=source_volume,
444
+ consistency_group_id=consistency_group,
412
445
  scheduler_hints=parsed_args.hint,
413
446
  backup_id=backup,
414
447
  )
@@ -416,14 +449,14 @@ class CreateVolume(command.ShowOne):
416
449
  if parsed_args.bootable is not None:
417
450
  try:
418
451
  if utils.wait_for_status(
419
- volume_client.volumes.get,
452
+ volume_client.get_volume,
420
453
  volume.id,
421
454
  success_status=['available'],
422
455
  error_status=['error'],
423
456
  sleep_time=1,
424
457
  ):
425
- volume_client.volumes.set_bootable(
426
- volume.id, parsed_args.bootable
458
+ volume_client.set_volume_bootable_status(
459
+ volume, parsed_args.bootable
427
460
  )
428
461
  else:
429
462
  msg = _(
@@ -436,14 +469,14 @@ class CreateVolume(command.ShowOne):
436
469
  if parsed_args.read_only is not None:
437
470
  try:
438
471
  if utils.wait_for_status(
439
- volume_client.volumes.get,
472
+ volume_client.get_volume,
440
473
  volume.id,
441
474
  success_status=['available'],
442
475
  error_status=['error'],
443
476
  sleep_time=1,
444
477
  ):
445
- volume_client.volumes.update_readonly_flag(
446
- volume.id, parsed_args.read_only
478
+ volume_client.set_volume_readonly(
479
+ volume, parsed_args.read_only
447
480
  )
448
481
  else:
449
482
  msg = _(
@@ -457,17 +490,8 @@ class CreateVolume(command.ShowOne):
457
490
  e,
458
491
  )
459
492
 
460
- # Remove key links from being displayed
461
- volume._info.update(
462
- {
463
- 'properties': format_columns.DictColumn(
464
- volume._info.pop('metadata')
465
- ),
466
- 'type': volume._info.pop('volume_type'),
467
- }
468
- )
469
- volume._info.pop("links", None)
470
- return zip(*sorted(volume._info.items()))
493
+ data = _format_volume(volume)
494
+ return zip(*sorted(data.items()))
471
495
 
472
496
 
473
497
  class DeleteVolume(command.Command):
@@ -574,6 +598,16 @@ class ListVolume(command.Lister):
574
598
  metavar='<status>',
575
599
  help=_('Filter results by status'),
576
600
  )
601
+ parser.add_argument(
602
+ '--property',
603
+ metavar='<key=value>',
604
+ action=parseractions.KeyValueAction,
605
+ dest='properties',
606
+ help=_(
607
+ 'Filter by a property on the volume list '
608
+ '(repeat option to filter by multiple properties) '
609
+ ),
610
+ )
577
611
  parser.add_argument(
578
612
  '--all-projects',
579
613
  action='store_true',
@@ -642,6 +676,7 @@ class ListVolume(command.Lister):
642
676
  'user_id': user_id,
643
677
  'name': parsed_args.name,
644
678
  'status': parsed_args.status,
679
+ 'metadata': parsed_args.properties,
645
680
  }
646
681
 
647
682
  data = volume_client.volumes.list(
@@ -726,16 +761,19 @@ class MigrateVolume(command.Command):
726
761
  "(possibly by another operation)"
727
762
  ),
728
763
  )
764
+ # TODO(stephenfin): Add --cluster argument
729
765
  return parser
730
766
 
731
767
  def take_action(self, parsed_args):
732
- volume_client = self.app.client_manager.volume
733
- volume = utils.find_resource(volume_client.volumes, parsed_args.volume)
734
- volume_client.volumes.migrate_volume(
768
+ volume_client = self.app.client_manager.sdk_connection.volume
769
+ volume = volume_client.find_volume(
770
+ parsed_args.volume, ignore_missing=False
771
+ )
772
+ volume_client.migrate_volume(
735
773
  volume.id,
736
- parsed_args.host,
737
- parsed_args.force_host_copy,
738
- parsed_args.lock_volume,
774
+ host=parsed_args.host,
775
+ force_host_copy=parsed_args.force_host_copy,
776
+ lock_volume=parsed_args.lock_volume,
739
777
  )
740
778
 
741
779
 
@@ -1078,24 +1116,13 @@ class ShowVolume(command.ShowOne):
1078
1116
  return parser
1079
1117
 
1080
1118
  def take_action(self, parsed_args):
1081
- volume_client = self.app.client_manager.volume
1082
- volume = utils.find_resource(volume_client.volumes, parsed_args.volume)
1119
+ volume_client = self.app.client_manager.sdk_connection.volume
1120
+ volume = volume_client.find_volume(
1121
+ parsed_args.volume, ignore_missing=False
1122
+ )
1083
1123
 
1084
- # Special mapping for columns to make the output easier to read:
1085
- # 'metadata' --> 'properties'
1086
- # 'volume_type' --> 'type'
1087
- volume._info.update(
1088
- {
1089
- 'properties': format_columns.DictColumn(
1090
- volume._info.pop('metadata')
1091
- ),
1092
- 'type': volume._info.pop('volume_type'),
1093
- },
1094
- )
1095
-
1096
- # Remove key links from being displayed
1097
- volume._info.pop("links", None)
1098
- return zip(*sorted(volume._info.items()))
1124
+ data = _format_volume(volume)
1125
+ return zip(*sorted(data.items()))
1099
1126
 
1100
1127
 
1101
1128
  class UnsetVolume(command.Command):
@@ -552,7 +552,7 @@ class ShowVolumeGroup(command.ShowOne):
552
552
  parsed_args.group,
553
553
  )
554
554
 
555
- group = volume_client.groups.show(group.id, **kwargs)
555
+ group = volume_client.groups.get(group.id, **kwargs)
556
556
 
557
557
  if parsed_args.show_replication_targets:
558
558
  replication_targets = (
@@ -17,6 +17,7 @@ Alex Katz <akatz@akatz.tlv.csb>
17
17
  Alex Schultz <aschultz@mirantis.com>
18
18
  Alexander Gräb <alexander.graeb@secustack.com>
19
19
  Alexander Ignatov <aignatov@mirantis.com>
20
+ Alexey Stupnikov <aleksey.stupnikov@gmail.com>
20
21
  Alfredo Moralejo <amoralej@redhat.com>
21
22
  Allain Legacy <Allain.legacy@windriver.com>
22
23
  Alvaro Lopez Garcia <aloga@ifca.unican.es>
@@ -471,6 +472,7 @@ cw0306-lee <us0310306@gmail.com>
471
472
  daizhiyong <zhiyong.dai@easystack.cn>
472
473
  devMuscle <hongsbien@naver.com>
473
474
  djp <dimsss0607@gmail.com>
475
+ dna <la18byeol@gmail.com>
474
476
  dongwenshuai <dong.wenshuai@zte.com.cn>
475
477
  elajkat <lajos.katona@est.tech>
476
478
  gecong1973 <ge.cong@zte.com.cn>
@@ -484,6 +486,7 @@ hackertron <jayadityagupta11@gmail.com>
484
486
  heesom <heesom.hs@gmail.com>
485
487
  heha <zhanghanqun@unitedstack.com>
486
488
  henriquetruta <henrique@lsd.ufcg.edu.br>
489
+ hongp <inyong.hong@samsung.com>
487
490
  hoosa <janghoosa@gmail.com>
488
491
  huangshan <huangshan@fiberhome.com>
489
492
  huangtianhua <huangtianhua@huawei.com>
@@ -520,11 +523,13 @@ mpicea <jjh77745997@gmail.com>
520
523
  nidhimittalhada <nidhimittal19@gmail.com>
521
524
  niuke <niuke19970315@163.com>
522
525
  npraveen35 <npraveen35@gmail.com>
526
+ ohjiwoo <jiwooo.oh@samsung.com>
523
527
  okozachenko <okozachenko@vexxhost.com>
524
528
  pedh <hcn518@gmail.com>
525
529
  pedro <phpm13@gmail.com>
526
530
  pengyuesheng <pengyuesheng@gohighsec.com>
527
531
  phil-hopkins-a <phil.hopkins@rackspace.com>
532
+ psnew14 <psnew14@gmail.com>
528
533
  qinchunhua <qin.chunhua@zte.com.cn>
529
534
  qingszhao <zhao.daqing@99cloud.net>
530
535
  qtang <qtang@vmware.com>
@@ -1,10 +1,11 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: python-openstackclient
3
- Version: 8.1.0
3
+ Version: 8.2.0
4
4
  Summary: OpenStack Command-line Client
5
- Home-page: https://docs.openstack.org/python-openstackclient/latest/
6
- Author: OpenStack
7
- Author-email: openstack-discuss@lists.openstack.org
5
+ Author-email: OpenStack <openstack-discuss@lists.openstack.org>
6
+ License: Apache-2.0
7
+ Project-URL: Homepage, https://docs.openstack.org/python-openstackclient/
8
+ Project-URL: Repository, https://opendev.org/openstack/python-openstackclient/
8
9
  Classifier: Environment :: OpenStack
9
10
  Classifier: Intended Audience :: Information Technology
10
11
  Classifier: Intended Audience :: System Administrators
@@ -22,9 +23,9 @@ License-File: LICENSE
22
23
  License-File: AUTHORS
23
24
  Requires-Dist: pbr !=2.1.0,>=2.0.0
24
25
  Requires-Dist: cryptography >=2.7
25
- Requires-Dist: cliff >=3.5.0
26
+ Requires-Dist: cliff >=4.8.0
26
27
  Requires-Dist: iso8601 >=0.1.11
27
- Requires-Dist: openstacksdk >=4.5.0
28
+ Requires-Dist: openstacksdk >=4.6.0
28
29
  Requires-Dist: osc-lib >=2.3.0
29
30
  Requires-Dist: oslo.i18n >=3.15.3
30
31
  Requires-Dist: python-keystoneclient >=3.22.0
@@ -261,4 +262,3 @@ Links
261
262
  * `Mailing list <https://lists.openstack.org/mailman3/lists/openstack-discuss.lists.openstack.org/>`_
262
263
  * `Release Notes <https://docs.openstack.org/releasenotes/python-openstackclient>`_
263
264
  * `IRC (#openstack-sdks on OFTC (irc.oftc.net)) <irc://irc.oftc.net/openstack-sdks>`_
264
-