python-openstackclient 8.0.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 (106) 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/client.py +5 -0
  5. openstackclient/compute/v2/console.py +7 -0
  6. openstackclient/compute/v2/console_connection.py +48 -0
  7. openstackclient/compute/v2/flavor.py +14 -1
  8. openstackclient/compute/v2/keypair.py +10 -3
  9. openstackclient/compute/v2/server.py +76 -13
  10. openstackclient/compute/v2/server_event.py +1 -1
  11. openstackclient/identity/common.py +85 -11
  12. openstackclient/identity/v3/application_credential.py +88 -87
  13. openstackclient/identity/v3/domain.py +67 -49
  14. openstackclient/identity/v3/group.py +113 -68
  15. openstackclient/identity/v3/project.py +42 -20
  16. openstackclient/identity/v3/role.py +7 -2
  17. openstackclient/identity/v3/user.py +38 -5
  18. openstackclient/image/client.py +5 -0
  19. openstackclient/image/v1/image.py +16 -1
  20. openstackclient/image/v2/cache.py +10 -6
  21. openstackclient/image/v2/image.py +59 -12
  22. openstackclient/image/v2/metadef_objects.py +8 -2
  23. openstackclient/image/v2/metadef_properties.py +9 -2
  24. openstackclient/network/client.py +0 -6
  25. openstackclient/network/v2/floating_ip.py +58 -29
  26. openstackclient/network/v2/network_qos_rule.py +3 -11
  27. openstackclient/network/v2/port.py +16 -0
  28. openstackclient/network/v2/router.py +1 -1
  29. openstackclient/network/v2/security_group.py +49 -7
  30. openstackclient/network/v2/security_group_rule.py +18 -1
  31. openstackclient/shell.py +1 -1
  32. openstackclient/tests/functional/base.py +5 -1
  33. openstackclient/tests/functional/compute/v2/test_keypair.py +41 -5
  34. openstackclient/tests/functional/identity/v3/test_access_rule.py +1 -1
  35. openstackclient/tests/functional/identity/v3/test_application_credential.py +7 -7
  36. openstackclient/tests/functional/image/v2/test_image.py +36 -14
  37. openstackclient/tests/functional/volume/v2/test_volume.py +1 -1
  38. openstackclient/tests/functional/volume/v3/test_volume.py +2 -2
  39. openstackclient/tests/unit/api/test_volume_v2.py +124 -0
  40. openstackclient/tests/unit/api/test_volume_v3.py +124 -0
  41. openstackclient/tests/unit/compute/v2/fakes.py +81 -305
  42. openstackclient/tests/unit/compute/v2/test_console.py +18 -1
  43. openstackclient/tests/unit/compute/v2/test_console_connection.py +72 -0
  44. openstackclient/tests/unit/compute/v2/test_flavor.py +160 -175
  45. openstackclient/tests/unit/compute/v2/test_keypair.py +12 -5
  46. openstackclient/tests/unit/compute/v2/test_server.py +211 -97
  47. openstackclient/tests/unit/compute/v2/test_server_backup.py +32 -71
  48. openstackclient/tests/unit/compute/v2/test_server_event.py +2 -2
  49. openstackclient/tests/unit/compute/v2/test_server_image.py +33 -72
  50. openstackclient/tests/unit/compute/v2/test_server_migration.py +4 -4
  51. openstackclient/tests/unit/identity/v3/test_application_credential.py +93 -65
  52. openstackclient/tests/unit/identity/v3/test_domain.py +117 -107
  53. openstackclient/tests/unit/identity/v3/test_group.py +353 -202
  54. openstackclient/tests/unit/identity/v3/test_project.py +46 -53
  55. openstackclient/tests/unit/identity/v3/test_role.py +2 -8
  56. openstackclient/tests/unit/identity/v3/test_user.py +86 -6
  57. openstackclient/tests/unit/image/v1/test_image.py +55 -9
  58. openstackclient/tests/unit/image/v2/test_image.py +128 -58
  59. openstackclient/tests/unit/image/v2/test_metadef_objects.py +22 -0
  60. openstackclient/tests/unit/image/v2/test_metadef_properties.py +24 -10
  61. openstackclient/tests/unit/network/v2/fakes.py +406 -485
  62. openstackclient/tests/unit/network/v2/test_floating_ip_network.py +13 -19
  63. openstackclient/tests/unit/network/v2/test_l3_conntrack_helper.py +2 -2
  64. openstackclient/tests/unit/network/v2/test_ndp_proxy.py +3 -5
  65. openstackclient/tests/unit/network/v2/test_network.py +4 -4
  66. openstackclient/tests/unit/network/v2/test_network_agent.py +15 -29
  67. openstackclient/tests/unit/network/v2/test_network_qos_policy.py +16 -19
  68. openstackclient/tests/unit/network/v2/test_network_qos_rule.py +79 -152
  69. openstackclient/tests/unit/network/v2/test_network_qos_rule_type.py +4 -6
  70. openstackclient/tests/unit/network/v2/test_network_rbac.py +2 -2
  71. openstackclient/tests/unit/network/v2/test_port.py +57 -17
  72. openstackclient/tests/unit/network/v2/test_router.py +73 -57
  73. openstackclient/tests/unit/network/v2/test_security_group_network.py +31 -27
  74. openstackclient/tests/unit/network/v2/test_security_group_rule_compute.py +1 -3
  75. openstackclient/tests/unit/network/v2/test_security_group_rule_network.py +82 -39
  76. openstackclient/tests/unit/volume/v2/fakes.py +1 -2
  77. openstackclient/tests/unit/volume/v2/test_service.py +57 -91
  78. openstackclient/tests/unit/volume/v2/test_volume.py +466 -410
  79. openstackclient/tests/unit/volume/v2/test_volume_backup.py +141 -148
  80. openstackclient/tests/unit/volume/v2/test_volume_snapshot.py +293 -283
  81. openstackclient/tests/unit/volume/v3/test_block_storage_log_level.py +61 -71
  82. openstackclient/tests/unit/volume/v3/test_service.py +221 -141
  83. openstackclient/tests/unit/volume/v3/test_volume.py +569 -534
  84. openstackclient/tests/unit/volume/v3/test_volume_attachment.py +1 -1
  85. openstackclient/tests/unit/volume/v3/test_volume_backup.py +198 -203
  86. openstackclient/tests/unit/volume/v3/test_volume_snapshot.py +682 -47
  87. openstackclient/volume/v2/service.py +41 -38
  88. openstackclient/volume/v2/volume.py +140 -88
  89. openstackclient/volume/v2/volume_backup.py +9 -3
  90. openstackclient/volume/v2/volume_snapshot.py +121 -84
  91. openstackclient/volume/v3/block_storage_log_level.py +22 -28
  92. openstackclient/volume/v3/service.py +105 -14
  93. openstackclient/volume/v3/volume.py +287 -99
  94. openstackclient/volume/v3/volume_backup.py +24 -19
  95. openstackclient/volume/v3/volume_group.py +1 -1
  96. openstackclient/volume/v3/volume_snapshot.py +485 -10
  97. {python_openstackclient-8.0.0.dist-info → python_openstackclient-8.2.0.dist-info}/AUTHORS +13 -0
  98. python_openstackclient-8.2.0.dist-info/METADATA +264 -0
  99. {python_openstackclient-8.0.0.dist-info → python_openstackclient-8.2.0.dist-info}/RECORD +104 -98
  100. {python_openstackclient-8.0.0.dist-info → python_openstackclient-8.2.0.dist-info}/entry_points.txt +7 -6
  101. python_openstackclient-8.2.0.dist-info/pbr.json +1 -0
  102. python_openstackclient-8.0.0.dist-info/METADATA +0 -166
  103. python_openstackclient-8.0.0.dist-info/pbr.json +0 -1
  104. {python_openstackclient-8.0.0.dist-info → python_openstackclient-8.2.0.dist-info}/LICENSE +0 -0
  105. {python_openstackclient-8.0.0.dist-info → python_openstackclient-8.2.0.dist-info}/WHEEL +0 -0
  106. {python_openstackclient-8.0.0.dist-info → python_openstackclient-8.2.0.dist-info}/top_level.txt +0 -0
@@ -45,33 +45,34 @@ class ListService(command.Lister):
45
45
  return parser
46
46
 
47
47
  def take_action(self, parsed_args):
48
- service_client = self.app.client_manager.volume
48
+ volume_client = self.app.client_manager.sdk_connection.volume
49
+
50
+ columns: tuple[str, ...] = (
51
+ "binary",
52
+ "host",
53
+ "availability_zone",
54
+ "status",
55
+ "state",
56
+ "updated_at",
57
+ )
58
+ column_names: tuple[str, ...] = (
59
+ "Binary",
60
+ "Host",
61
+ "Zone",
62
+ "Status",
63
+ "State",
64
+ "Updated At",
65
+ )
49
66
 
50
67
  if parsed_args.long:
51
- columns = [
52
- "Binary",
53
- "Host",
54
- "Zone",
55
- "Status",
56
- "State",
57
- "Updated At",
58
- "Disabled Reason",
59
- ]
60
- else:
61
- columns = [
62
- "Binary",
63
- "Host",
64
- "Zone",
65
- "Status",
66
- "State",
67
- "Updated At",
68
- ]
69
-
70
- data = service_client.services.list(
71
- parsed_args.host, parsed_args.service
68
+ columns += ("disabled_reason",)
69
+ column_names += ("Disabled Reason",)
70
+
71
+ data = volume_client.services(
72
+ host=parsed_args.host, binary=parsed_args.service
72
73
  )
73
74
  return (
74
- columns,
75
+ column_names,
75
76
  (
76
77
  utils.get_item_properties(
77
78
  s,
@@ -87,7 +88,11 @@ class SetService(command.Command):
87
88
 
88
89
  def get_parser(self, prog_name):
89
90
  parser = super().get_parser(prog_name)
90
- parser.add_argument("host", metavar="<host>", help=_("Name of host"))
91
+ parser.add_argument(
92
+ "host",
93
+ metavar="<host>",
94
+ help=_("Name of host"),
95
+ )
91
96
  parser.add_argument(
92
97
  "service",
93
98
  metavar="<service>",
@@ -118,19 +123,17 @@ class SetService(command.Command):
118
123
  )
119
124
  raise exceptions.CommandError(msg)
120
125
 
121
- service_client = self.app.client_manager.volume
126
+ volume_client = self.app.client_manager.sdk_connection.volume
127
+
128
+ service = volume_client.find_service(
129
+ parsed_args.service, ignore_missing=False, host=parsed_args.host
130
+ )
131
+
122
132
  if parsed_args.enable:
123
- service_client.services.enable(
124
- parsed_args.host, parsed_args.service
125
- )
133
+ service.enable(volume_client)
134
+
126
135
  if parsed_args.disable:
127
- if parsed_args.disable_reason:
128
- service_client.services.disable_log_reason(
129
- parsed_args.host,
130
- parsed_args.service,
131
- parsed_args.disable_reason,
132
- )
133
- else:
134
- service_client.services.disable(
135
- parsed_args.host, parsed_args.service
136
- )
136
+ service.disable(
137
+ volume_client,
138
+ reason=parsed_args.disable_reason,
139
+ )
@@ -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
 
@@ -107,7 +151,7 @@ class CreateVolume(command.ShowOne):
107
151
  )
108
152
  raise exceptions.CommandError(msg)
109
153
 
110
- def _get_parser(self, prog_name):
154
+ def get_parser(self, prog_name):
111
155
  parser = super().get_parser(prog_name)
112
156
  parser.add_argument(
113
157
  "name",
@@ -169,6 +213,7 @@ class CreateVolume(command.ShowOne):
169
213
  "--property",
170
214
  metavar="<key=value>",
171
215
  action=parseractions.KeyValueAction,
216
+ dest="properties",
172
217
  help=_(
173
218
  "Set a property to this volume "
174
219
  "(repeat option to set multiple properties)"
@@ -189,54 +234,58 @@ class CreateVolume(command.ShowOne):
189
234
  bootable_group.add_argument(
190
235
  "--bootable",
191
236
  action="store_true",
237
+ dest="bootable",
238
+ default=None,
192
239
  help=_("Mark volume as bootable"),
193
240
  )
194
241
  bootable_group.add_argument(
195
242
  "--non-bootable",
196
- action="store_true",
243
+ action="store_false",
244
+ dest="bootable",
245
+ default=None,
197
246
  help=_("Mark volume as non-bootable (default)"),
198
247
  )
199
248
  readonly_group = parser.add_mutually_exclusive_group()
200
249
  readonly_group.add_argument(
201
250
  "--read-only",
202
251
  action="store_true",
252
+ dest="read_only",
253
+ default=None,
203
254
  help=_("Set volume to read-only access mode"),
204
255
  )
205
256
  readonly_group.add_argument(
206
257
  "--read-write",
207
- action="store_true",
258
+ action="store_false",
259
+ dest="read_only",
260
+ default=None,
208
261
  help=_("Set volume to read-write access mode (default)"),
209
262
  )
210
- return parser, source_group
211
-
212
- def get_parser(self, prog_name):
213
- parser, _ = self._get_parser(prog_name)
214
263
  return parser
215
264
 
216
265
  def take_action(self, parsed_args):
217
- CreateVolume._check_size_arg(parsed_args)
266
+ self._check_size_arg(parsed_args)
218
267
  # size is validated in the above call to
219
268
  # _check_size_arg where we check that size
220
269
  # should be passed if we are not creating a
221
270
  # volume from snapshot or source volume
222
271
  size = parsed_args.size
223
272
 
224
- volume_client = self.app.client_manager.volume
273
+ volume_client = self.app.client_manager.sdk_connection.volume
225
274
  image_client = self.app.client_manager.image
226
275
 
227
276
  source_volume = None
228
277
  if parsed_args.source:
229
- source_volume_obj = utils.find_resource(
230
- volume_client.volumes, parsed_args.source
278
+ source_volume_obj = volume_client.find_volume(
279
+ parsed_args.source, ignore_missing=False
231
280
  )
232
281
  source_volume = source_volume_obj.id
233
282
  size = max(size or 0, source_volume_obj.size)
234
283
 
235
284
  consistency_group = None
236
285
  if parsed_args.consistency_group:
237
- consistency_group = utils.find_resource(
238
- volume_client.consistencygroups, parsed_args.consistency_group
239
- ).id
286
+ consistency_group = volume_v2.find_consistency_group(
287
+ volume_client, parsed_args.consistency_group
288
+ )['id']
240
289
 
241
290
  image = None
242
291
  if parsed_args.image:
@@ -246,8 +295,8 @@ class CreateVolume(command.ShowOne):
246
295
 
247
296
  snapshot = None
248
297
  if parsed_args.snapshot:
249
- snapshot_obj = utils.find_resource(
250
- volume_client.volume_snapshots, parsed_args.snapshot
298
+ snapshot_obj = volume_client.find_snapshot(
299
+ parsed_args.snapshot, ignore_missing=False
251
300
  )
252
301
  snapshot = snapshot_obj.id
253
302
  # Cinder requires a value for size when creating a volume
@@ -258,31 +307,31 @@ class CreateVolume(command.ShowOne):
258
307
  # snapshot size.
259
308
  size = max(size or 0, snapshot_obj.size)
260
309
 
261
- volume = volume_client.volumes.create(
310
+ volume = volume_client.create_volume(
262
311
  size=size,
263
312
  snapshot_id=snapshot,
264
313
  name=parsed_args.name,
265
314
  description=parsed_args.description,
266
315
  volume_type=parsed_args.type,
267
316
  availability_zone=parsed_args.availability_zone,
268
- metadata=parsed_args.property,
269
- imageRef=image,
270
- source_volid=source_volume,
271
- consistencygroup_id=consistency_group,
317
+ metadata=parsed_args.properties,
318
+ image_id=image,
319
+ source_volume_id=source_volume,
320
+ consistency_group_id=consistency_group,
272
321
  scheduler_hints=parsed_args.hint,
273
322
  )
274
323
 
275
- if parsed_args.bootable or parsed_args.non_bootable:
324
+ if parsed_args.bootable is not None:
276
325
  try:
277
326
  if utils.wait_for_status(
278
- volume_client.volumes.get,
327
+ volume_client.get_volume,
279
328
  volume.id,
280
329
  success_status=['available'],
281
330
  error_status=['error'],
282
331
  sleep_time=1,
283
332
  ):
284
- volume_client.volumes.set_bootable(
285
- volume.id, parsed_args.bootable
333
+ volume_client.set_volume_bootable_status(
334
+ volume, parsed_args.bootable
286
335
  )
287
336
  else:
288
337
  msg = _(
@@ -291,17 +340,18 @@ class CreateVolume(command.ShowOne):
291
340
  raise exceptions.CommandError(msg)
292
341
  except Exception as e:
293
342
  LOG.error(_("Failed to set volume bootable property: %s"), e)
294
- if parsed_args.read_only or parsed_args.read_write:
343
+
344
+ if parsed_args.read_only is not None:
295
345
  try:
296
346
  if utils.wait_for_status(
297
- volume_client.volumes.get,
347
+ volume_client.get_volume,
298
348
  volume.id,
299
349
  success_status=['available'],
300
350
  error_status=['error'],
301
351
  sleep_time=1,
302
352
  ):
303
- volume_client.volumes.update_readonly_flag(
304
- volume.id, parsed_args.read_only
353
+ volume_client.set_volume_readonly(
354
+ volume, parsed_args.read_only
305
355
  )
306
356
  else:
307
357
  msg = _(
@@ -315,17 +365,8 @@ class CreateVolume(command.ShowOne):
315
365
  e,
316
366
  )
317
367
 
318
- # Remove key links from being displayed
319
- volume._info.update(
320
- {
321
- 'properties': format_columns.DictColumn(
322
- volume._info.pop('metadata')
323
- ),
324
- 'type': volume._info.pop('volume_type'),
325
- }
326
- )
327
- volume._info.pop("links", None)
328
- return zip(*sorted(volume._info.items()))
368
+ data = _format_volume(volume)
369
+ return zip(*sorted(data.items()))
329
370
 
330
371
 
331
372
  class DeleteVolume(command.Command):
@@ -358,18 +399,19 @@ class DeleteVolume(command.Command):
358
399
  return parser
359
400
 
360
401
  def take_action(self, parsed_args):
361
- volume_client = self.app.client_manager.volume
402
+ volume_client = self.app.client_manager.sdk_connection.volume
362
403
  result = 0
363
404
 
364
- for i in parsed_args.volumes:
405
+ for volume in parsed_args.volumes:
365
406
  try:
366
- volume_obj = utils.find_resource(volume_client.volumes, i)
367
- if parsed_args.force:
368
- volume_client.volumes.force_delete(volume_obj.id)
369
- else:
370
- volume_client.volumes.delete(
371
- volume_obj.id, cascade=parsed_args.purge
372
- )
407
+ volume_obj = volume_client.find_volume(
408
+ volume, ignore_missing=False
409
+ )
410
+ volume_client.delete_volume(
411
+ volume_obj.id,
412
+ force=parsed_args.force,
413
+ cascade=parsed_args.purge,
414
+ )
373
415
  except Exception as e:
374
416
  result += 1
375
417
  LOG.error(
@@ -377,7 +419,7 @@ class DeleteVolume(command.Command):
377
419
  "Failed to delete volume with "
378
420
  "name or ID '%(volume)s': %(e)s"
379
421
  ),
380
- {'volume': i, 'e': e},
422
+ {'volume': volume, 'e': e},
381
423
  )
382
424
 
383
425
  if result > 0:
@@ -571,13 +613,15 @@ class MigrateVolume(command.Command):
571
613
  return parser
572
614
 
573
615
  def take_action(self, parsed_args):
574
- volume_client = self.app.client_manager.volume
575
- volume = utils.find_resource(volume_client.volumes, parsed_args.volume)
576
- 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(
577
621
  volume.id,
578
- parsed_args.host,
579
- parsed_args.force_host_copy,
580
- 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,
581
625
  )
582
626
 
583
627
 
@@ -622,6 +666,7 @@ class SetVolume(command.Command):
622
666
  '--property',
623
667
  metavar='<key=value>',
624
668
  action=parseractions.KeyValueAction,
669
+ dest="properties",
625
670
  help=_(
626
671
  'Set a property on this volume '
627
672
  '(repeat option to set multiple properties)'
@@ -631,6 +676,7 @@ class SetVolume(command.Command):
631
676
  '--image-property',
632
677
  metavar='<key=value>',
633
678
  action=parseractions.KeyValueAction,
679
+ dest="image_properties",
634
680
  help=_(
635
681
  'Set an image property on this volume '
636
682
  '(repeat option to set multiple image properties)'
@@ -707,22 +753,30 @@ class SetVolume(command.Command):
707
753
  bootable_group.add_argument(
708
754
  "--bootable",
709
755
  action="store_true",
756
+ dest="bootable",
757
+ default=None,
710
758
  help=_("Mark volume as bootable"),
711
759
  )
712
760
  bootable_group.add_argument(
713
761
  "--non-bootable",
714
- action="store_true",
762
+ action="store_false",
763
+ dest="bootable",
764
+ default=None,
715
765
  help=_("Mark volume as non-bootable"),
716
766
  )
717
767
  readonly_group = parser.add_mutually_exclusive_group()
718
768
  readonly_group.add_argument(
719
769
  "--read-only",
720
770
  action="store_true",
771
+ dest="read_only",
772
+ default=None,
721
773
  help=_("Set volume to read-only access mode"),
722
774
  )
723
775
  readonly_group.add_argument(
724
776
  "--read-write",
725
- action="store_true",
777
+ action="store_false",
778
+ dest="read_only",
779
+ default=None,
726
780
  help=_("Set volume to read-write access mode"),
727
781
  )
728
782
  return parser
@@ -771,28 +825,31 @@ class SetVolume(command.Command):
771
825
  LOG.error(_("Failed to clean volume properties: %s"), e)
772
826
  result += 1
773
827
 
774
- if parsed_args.property:
828
+ if parsed_args.properties:
775
829
  try:
776
830
  volume_client.volumes.set_metadata(
777
- volume.id, parsed_args.property
831
+ volume.id, parsed_args.properties
778
832
  )
779
833
  except Exception as e:
780
- LOG.error(_("Failed to set volume property: %s"), e)
834
+ LOG.error(_("Failed to set volume properties: %s"), e)
781
835
  result += 1
782
- if parsed_args.image_property:
836
+
837
+ if parsed_args.image_properties:
783
838
  try:
784
839
  volume_client.volumes.set_image_metadata(
785
- volume.id, parsed_args.image_property
840
+ volume.id, parsed_args.image_properties
786
841
  )
787
842
  except Exception as e:
788
- LOG.error(_("Failed to set image property: %s"), e)
843
+ LOG.error(_("Failed to set image properties: %s"), e)
789
844
  result += 1
845
+
790
846
  if parsed_args.state:
791
847
  try:
792
848
  volume_client.volumes.reset_state(volume.id, parsed_args.state)
793
849
  except Exception as e:
794
850
  LOG.error(_("Failed to set volume state: %s"), e)
795
851
  result += 1
852
+
796
853
  if parsed_args.attached:
797
854
  try:
798
855
  volume_client.volumes.reset_state(
@@ -801,6 +858,7 @@ class SetVolume(command.Command):
801
858
  except Exception as e:
802
859
  LOG.error(_("Failed to set volume attach-status: %s"), e)
803
860
  result += 1
861
+
804
862
  if parsed_args.detached:
805
863
  try:
806
864
  volume_client.volumes.reset_state(
@@ -809,7 +867,8 @@ class SetVolume(command.Command):
809
867
  except Exception as e:
810
868
  LOG.error(_("Failed to set volume attach-status: %s"), e)
811
869
  result += 1
812
- if parsed_args.bootable or parsed_args.non_bootable:
870
+
871
+ if parsed_args.bootable is not None:
813
872
  try:
814
873
  volume_client.volumes.set_bootable(
815
874
  volume.id, parsed_args.bootable
@@ -817,7 +876,8 @@ class SetVolume(command.Command):
817
876
  except Exception as e:
818
877
  LOG.error(_("Failed to set volume bootable property: %s"), e)
819
878
  result += 1
820
- if parsed_args.read_only or parsed_args.read_write:
879
+
880
+ if parsed_args.read_only is not None:
821
881
  try:
822
882
  volume_client.volumes.update_readonly_flag(
823
883
  volume.id, parsed_args.read_only
@@ -828,6 +888,7 @@ class SetVolume(command.Command):
828
888
  e,
829
889
  )
830
890
  result += 1
891
+
831
892
  policy = parsed_args.migration_policy or parsed_args.retype_policy
832
893
  if parsed_args.type:
833
894
  # get the migration policy
@@ -894,24 +955,13 @@ class ShowVolume(command.ShowOne):
894
955
  return parser
895
956
 
896
957
  def take_action(self, parsed_args):
897
- volume_client = self.app.client_manager.volume
898
- volume = utils.find_resource(volume_client.volumes, parsed_args.volume)
899
-
900
- # Special mapping for columns to make the output easier to read:
901
- # 'metadata' --> 'properties'
902
- # 'volume_type' --> 'type'
903
- volume._info.update(
904
- {
905
- 'properties': format_columns.DictColumn(
906
- volume._info.pop('metadata')
907
- ),
908
- 'type': volume._info.pop('volume_type'),
909
- },
958
+ volume_client = self.app.client_manager.sdk_connection.volume
959
+ volume = volume_client.find_volume(
960
+ parsed_args.volume, ignore_missing=False
910
961
  )
911
962
 
912
- # Remove key links from being displayed
913
- volume._info.pop("links", None)
914
- return zip(*sorted(volume._info.items()))
963
+ data = _format_volume(volume)
964
+ return zip(*sorted(data.items()))
915
965
 
916
966
 
917
967
  class UnsetVolume(command.Command):
@@ -928,6 +978,7 @@ class UnsetVolume(command.Command):
928
978
  '--property',
929
979
  metavar='<key>',
930
980
  action='append',
981
+ dest='properties',
931
982
  help=_(
932
983
  'Remove a property from volume '
933
984
  '(repeat option to remove multiple properties)'
@@ -937,6 +988,7 @@ class UnsetVolume(command.Command):
937
988
  '--image-property',
938
989
  metavar='<key>',
939
990
  action='append',
991
+ dest='image_properties',
940
992
  help=_(
941
993
  'Remove an image property from volume '
942
994
  '(repeat option to remove multiple image properties)'
@@ -949,22 +1001,22 @@ class UnsetVolume(command.Command):
949
1001
  volume = utils.find_resource(volume_client.volumes, parsed_args.volume)
950
1002
 
951
1003
  result = 0
952
- if parsed_args.property:
1004
+ if parsed_args.properties:
953
1005
  try:
954
1006
  volume_client.volumes.delete_metadata(
955
- volume.id, parsed_args.property
1007
+ volume.id, parsed_args.properties
956
1008
  )
957
1009
  except Exception as e:
958
- LOG.error(_("Failed to unset volume property: %s"), e)
1010
+ LOG.error(_("Failed to unset volume properties: %s"), e)
959
1011
  result += 1
960
1012
 
961
- if parsed_args.image_property:
1013
+ if parsed_args.image_properties:
962
1014
  try:
963
1015
  volume_client.volumes.delete_image_metadata(
964
- volume.id, parsed_args.image_property
1016
+ volume.id, parsed_args.image_properties
965
1017
  )
966
1018
  except Exception as e:
967
- LOG.error(_("Failed to unset image property: %s"), e)
1019
+ LOG.error(_("Failed to unset image properties: %s"), e)
968
1020
  result += 1
969
1021
 
970
1022
  if result > 0:
@@ -417,13 +417,19 @@ class SetVolumeBackup(command.Command):
417
417
  return parser
418
418
 
419
419
  def take_action(self, parsed_args):
420
- volume_client = self.app.client_manager.volume
421
- backup = utils.find_resource(volume_client.backups, parsed_args.backup)
420
+ volume_client = self.app.client_manager.sdk_connection.volume
421
+
422
+ backup = volume_client.find_backup(
423
+ parsed_args.backup,
424
+ ignore_missing=False,
425
+ )
422
426
 
423
427
  result = 0
424
428
  if parsed_args.state:
425
429
  try:
426
- volume_client.backups.reset_state(backup.id, parsed_args.state)
430
+ volume_client.reset_backup_status(
431
+ backup, status=parsed_args.state
432
+ )
427
433
  except Exception as e:
428
434
  LOG.error(_("Failed to set backup state: %s"), e)
429
435
  result += 1