python-openstackclient 6.1.0__py3-none-any.whl → 6.2.1__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 (39) hide show
  1. openstackclient/common/project_cleanup.py +3 -5
  2. openstackclient/common/quota.py +4 -6
  3. openstackclient/compute/v2/host.py +44 -16
  4. openstackclient/compute/v2/server.py +18 -24
  5. openstackclient/compute/v2/server_migration.py +53 -29
  6. openstackclient/compute/v2/server_volume.py +35 -38
  7. openstackclient/compute/v2/service.py +5 -5
  8. openstackclient/identity/common.py +12 -0
  9. openstackclient/identity/v3/access_rule.py +8 -6
  10. openstackclient/image/v2/image.py +8 -2
  11. openstackclient/network/v2/floating_ip_port_forwarding.py +70 -32
  12. openstackclient/network/v2/network_service_provider.py +16 -10
  13. openstackclient/tests/functional/compute/v2/test_server.py +0 -3
  14. openstackclient/tests/unit/compute/v2/fakes.py +122 -207
  15. openstackclient/tests/unit/compute/v2/test_host.py +63 -42
  16. openstackclient/tests/unit/compute/v2/test_server.py +1 -2
  17. openstackclient/tests/unit/compute/v2/test_server_migration.py +92 -95
  18. openstackclient/tests/unit/compute/v2/test_server_volume.py +103 -83
  19. openstackclient/tests/unit/identity/v3/test_access_rule.py +11 -11
  20. openstackclient/tests/unit/network/v2/fakes.py +31 -7
  21. openstackclient/tests/unit/network/v2/test_floating_ip_port_forwarding.py +213 -18
  22. openstackclient/tests/unit/volume/v1/test_volume.py +48 -3
  23. openstackclient/tests/unit/volume/v2/test_consistency_group.py +2 -2
  24. openstackclient/tests/unit/volume/v2/test_volume.py +50 -3
  25. openstackclient/tests/unit/volume/v3/test_volume_group.py +47 -8
  26. openstackclient/volume/v1/volume.py +33 -4
  27. openstackclient/volume/v2/backup_record.py +8 -6
  28. openstackclient/volume/v2/consistency_group.py +32 -13
  29. openstackclient/volume/v2/volume.py +33 -4
  30. openstackclient/volume/v3/volume_group.py +91 -36
  31. {python_openstackclient-6.1.0.dist-info → python_openstackclient-6.2.1.dist-info}/AUTHORS +5 -0
  32. {python_openstackclient-6.1.0.dist-info → python_openstackclient-6.2.1.dist-info}/METADATA +1 -1
  33. {python_openstackclient-6.1.0.dist-info → python_openstackclient-6.2.1.dist-info}/RECORD +38 -38
  34. {python_openstackclient-6.1.0.dist-info → python_openstackclient-6.2.1.dist-info}/entry_points.txt +1 -0
  35. python_openstackclient-6.2.1.dist-info/pbr.json +1 -0
  36. python_openstackclient-6.1.0.dist-info/pbr.json +0 -1
  37. {python_openstackclient-6.1.0.dist-info → python_openstackclient-6.2.1.dist-info}/LICENSE +0 -0
  38. {python_openstackclient-6.1.0.dist-info → python_openstackclient-6.2.1.dist-info}/WHEEL +0 -0
  39. {python_openstackclient-6.1.0.dist-info → python_openstackclient-6.2.1.dist-info}/top_level.txt +0 -0
@@ -28,16 +28,15 @@ from openstackclient.identity import common as identity_common
28
28
  LOG = logging.getLogger(__name__)
29
29
 
30
30
 
31
- def ask_user_yesno(msg, default=True):
31
+ def ask_user_yesno(msg):
32
32
  """Ask user Y/N question
33
33
 
34
34
  :param str msg: question text
35
- :param bool default: default value
36
35
  :return bool: User choice
37
36
  """
38
37
  while True:
39
38
  answer = getpass._raw_input(
40
- '{} [{}]: '.format(msg, 'y/N' if not default else 'Y/n'))
39
+ '{} [{}]: '.format(msg, 'y/n'))
41
40
  if answer in ('y', 'Y', 'yes'):
42
41
  return True
43
42
  elif answer in ('n', 'N', 'no'):
@@ -129,8 +128,7 @@ class ProjectCleanup(command.Command):
129
128
  return
130
129
 
131
130
  confirm = ask_user_yesno(
132
- _("These resources will be deleted. Are you sure"),
133
- default=False)
131
+ _("These resources will be deleted. Are you sure"))
134
132
 
135
133
  if confirm:
136
134
  self.log.warning(_('Deleting resources'))
@@ -749,12 +749,10 @@ class SetQuota(common.NetDetectionMixin, command.Command):
749
749
 
750
750
 
751
751
  class ShowQuota(command.Lister):
752
- _description = _(
753
- "Show quotas for project or class. "
754
- "Specify ``--os-compute-api-version 2.50`` or higher to see "
755
- "``server-groups`` and ``server-group-members`` output for a given "
756
- "quota class."
757
- )
752
+ _description = _("""Show quotas for project or class.
753
+
754
+ Specify ``--os-compute-api-version 2.50`` or higher to see ``server-groups``
755
+ and ``server-group-members`` output for a given quota class.""")
758
756
 
759
757
  def get_parser(self, prog_name):
760
758
  parser = super().get_parser(prog_name)
@@ -22,10 +22,10 @@ from openstackclient.i18n import _
22
22
 
23
23
 
24
24
  class ListHost(command.Lister):
25
- _description = _("List hosts")
25
+ _description = _("DEPRECATED: List hosts")
26
26
 
27
27
  def get_parser(self, prog_name):
28
- parser = super(ListHost, self).get_parser(prog_name)
28
+ parser = super().get_parser(prog_name)
29
29
  parser.add_argument(
30
30
  "--zone",
31
31
  metavar="<zone>",
@@ -34,17 +34,33 @@ class ListHost(command.Lister):
34
34
  return parser
35
35
 
36
36
  def take_action(self, parsed_args):
37
- compute_client = self.app.client_manager.compute
37
+ compute_client = self.app.client_manager.sdk_connection.compute
38
38
  columns = (
39
39
  "Host Name",
40
40
  "Service",
41
41
  "Zone"
42
42
  )
43
- data = compute_client.api.host_list(parsed_args.zone)
44
- return (columns,
45
- (utils.get_dict_properties(
46
- s, columns,
47
- ) for s in data))
43
+
44
+ self.log.warning(
45
+ "API has been deprecated. "
46
+ "Please consider using 'hypervisor list' instead."
47
+ )
48
+
49
+ # doing this since openstacksdk has decided not to support this
50
+ # deprecated command
51
+ hosts = compute_client.get(
52
+ '/os-hosts', microversion='2.1'
53
+ ).json().get('hosts')
54
+
55
+ if parsed_args.zone is not None:
56
+ filtered_hosts = []
57
+ for host in hosts:
58
+ if host['zone'] == parsed_args.zone:
59
+ filtered_hosts.append(host)
60
+
61
+ hosts = filtered_hosts
62
+
63
+ return columns, (utils.get_dict_properties(s, columns) for s in hosts)
48
64
 
49
65
 
50
66
  class SetHost(command.Command):
@@ -102,10 +118,10 @@ class SetHost(command.Command):
102
118
 
103
119
 
104
120
  class ShowHost(command.Lister):
105
- _description = _("Display host details")
121
+ _description = _("DEPRECATED: Display host details")
106
122
 
107
123
  def get_parser(self, prog_name):
108
- parser = super(ShowHost, self).get_parser(prog_name)
124
+ parser = super().get_parser(prog_name)
109
125
  parser.add_argument(
110
126
  "host",
111
127
  metavar="<host>",
@@ -114,7 +130,7 @@ class ShowHost(command.Lister):
114
130
  return parser
115
131
 
116
132
  def take_action(self, parsed_args):
117
- compute_client = self.app.client_manager.compute
133
+ compute_client = self.app.client_manager.sdk_connection.compute
118
134
  columns = (
119
135
  "Host",
120
136
  "Project",
@@ -123,9 +139,21 @@ class ShowHost(command.Lister):
123
139
  "Disk GB"
124
140
  )
125
141
 
126
- data = compute_client.api.host_show(parsed_args.host)
142
+ self.log.warning(
143
+ "API has been deprecated. "
144
+ "Please consider using 'hypervisor show' instead."
145
+ )
146
+
147
+ # doing this since openstacksdk has decided not to support this
148
+ # deprecated command
149
+ resources = compute_client.get(
150
+ '/os-hosts/' + parsed_args.host,
151
+ microversion='2.1'
152
+ ).json().get('host')
153
+
154
+ data = []
155
+ if resources is not None:
156
+ for resource in resources:
157
+ data.append(resource['resource'])
127
158
 
128
- return (columns,
129
- (utils.get_dict_properties(
130
- s, columns,
131
- ) for s in data))
159
+ return columns, (utils.get_dict_properties(s, columns) for s in data)
@@ -608,10 +608,10 @@ class AddServerSecurityGroup(command.Command):
608
608
 
609
609
 
610
610
  class AddServerVolume(command.ShowOne):
611
- _description = _(
612
- "Add volume to server. "
613
- "Specify ``--os-compute-api-version 2.20`` or higher to add a volume "
614
- "to a server with status ``SHELVED`` or ``SHELVED_OFFLOADED``.")
611
+ _description = _("""Add volume to server.
612
+
613
+ Specify ``--os-compute-api-version 2.20`` or higher to add a volume to a server
614
+ with status ``SHELVED`` or ``SHELVED_OFFLOADED``.""")
615
615
 
616
616
  def get_parser(self, prog_name):
617
617
  parser = super(AddServerVolume, self).get_parser(prog_name)
@@ -3757,11 +3757,10 @@ class RemoveServerSecurityGroup(command.Command):
3757
3757
 
3758
3758
 
3759
3759
  class RemoveServerVolume(command.Command):
3760
- _description = _(
3761
- "Remove volume from server. "
3762
- "Specify ``--os-compute-api-version 2.20`` or higher to remove a "
3763
- "volume from a server with status ``SHELVED`` or "
3764
- "``SHELVED_OFFLOADED``.")
3760
+ _description = _("""Remove volume from server.
3761
+
3762
+ Specify ``--os-compute-api-version 2.20`` or higher to remove a
3763
+ volume from a server with status ``SHELVED`` or ``SHELVED_OFFLOADED``.""")
3765
3764
 
3766
3765
  def get_parser(self, prog_name):
3767
3766
  parser = super(RemoveServerVolume, self).get_parser(prog_name)
@@ -3798,11 +3797,10 @@ class RemoveServerVolume(command.Command):
3798
3797
 
3799
3798
 
3800
3799
  class RescueServer(command.Command):
3801
- _description = _(
3802
- "Put server in rescue mode. "
3803
- "Specify ``--os-compute-api-version 2.87`` or higher to rescue a "
3804
- "server booted from a volume."
3805
- )
3800
+ _description = _("""Put server in rescue mode.
3801
+
3802
+ Specify ``--os-compute-api-version 2.87`` or higher to rescue a
3803
+ server booted from a volume.""")
3806
3804
 
3807
3805
  def get_parser(self, prog_name):
3808
3806
  parser = super(RescueServer, self).get_parser(prog_name)
@@ -3958,9 +3956,7 @@ Confirm (verify) success of resize operation and release the old server.""")
3958
3956
 
3959
3957
  # TODO(stephenfin): Remove in OSC 7.0
3960
3958
  class MigrateConfirm(ResizeConfirm):
3961
- _description = _("""DEPRECATED: Confirm server migration.
3962
-
3963
- Use 'server migration confirm' instead.""")
3959
+ _description = _("DEPRECATED: Use 'server migration confirm' instead.")
3964
3960
 
3965
3961
  def take_action(self, parsed_args):
3966
3962
  msg = _(
@@ -4006,9 +4002,7 @@ one.""")
4006
4002
 
4007
4003
  # TODO(stephenfin): Remove in OSC 7.0
4008
4004
  class MigrateRevert(ResizeRevert):
4009
- _description = _("""Revert server migration.
4010
-
4011
- Use 'server migration revert' instead.""")
4005
+ _description = _("DEPRECATED: Use 'server migration revert' instead.")
4012
4006
 
4013
4007
  def take_action(self, parsed_args):
4014
4008
  msg = _(
@@ -4346,10 +4340,10 @@ class ShelveServer(command.Command):
4346
4340
 
4347
4341
 
4348
4342
  class ShowServer(command.ShowOne):
4349
- _description = _(
4350
- "Show server details. Specify ``--os-compute-api-version 2.47`` "
4351
- "or higher to see the embedded flavor information for the server."
4352
- )
4343
+ _description = _("""Show server details.
4344
+
4345
+ Specify ``--os-compute-api-version 2.47`` or higher to see the embedded flavor
4346
+ information for the server.""")
4353
4347
 
4354
4348
  def get_parser(self, prog_name):
4355
4349
  parser = super(ShowServer, self).get_parser(prog_name)
@@ -14,7 +14,6 @@
14
14
 
15
15
  import uuid
16
16
 
17
- from novaclient import api_versions
18
17
  from openstack import utils as sdk_utils
19
18
  from osc_lib.command import command
20
19
  from osc_lib import exceptions
@@ -256,7 +255,7 @@ class ListMigration(command.Lister):
256
255
 
257
256
 
258
257
  def _get_migration_by_uuid(compute_client, server_id, migration_uuid):
259
- for migration in compute_client.server_migrations.list(server_id):
258
+ for migration in compute_client.server_migrations(server_id):
260
259
  if migration.uuid == migration_uuid:
261
260
  return migration
262
261
  break
@@ -290,9 +289,9 @@ class ShowMigration(command.ShowOne):
290
289
  return parser
291
290
 
292
291
  def take_action(self, parsed_args):
293
- compute_client = self.app.client_manager.compute
292
+ compute_client = self.app.client_manager.sdk_connection.compute
294
293
 
295
- if compute_client.api_version < api_versions.APIVersion('2.24'):
294
+ if not sdk_utils.supports_microversion(compute_client, '2.24'):
296
295
  msg = _(
297
296
  '--os-compute-api-version 2.24 or greater is required to '
298
297
  'support the server migration show command'
@@ -308,16 +307,16 @@ class ShowMigration(command.ShowOne):
308
307
  )
309
308
  raise exceptions.CommandError(msg)
310
309
 
311
- if compute_client.api_version < api_versions.APIVersion('2.59'):
310
+ if not sdk_utils.supports_microversion(compute_client, '2.59'):
312
311
  msg = _(
313
312
  '--os-compute-api-version 2.59 or greater is required to '
314
313
  'retrieve server migrations by UUID'
315
314
  )
316
315
  raise exceptions.CommandError(msg)
317
316
 
318
- server = utils.find_resource(
319
- compute_client.servers,
317
+ server = compute_client.find_server(
320
318
  parsed_args.server,
319
+ ignore_missing=False,
321
320
  )
322
321
 
323
322
  # the nova API doesn't currently allow retrieval by UUID but it's a
@@ -328,11 +327,13 @@ class ShowMigration(command.ShowOne):
328
327
  compute_client, server.id, parsed_args.migration,
329
328
  )
330
329
  else:
331
- server_migration = compute_client.server_migrations.get(
332
- server.id, parsed_args.migration,
330
+ server_migration = compute_client.get_server_migration(
331
+ server.id,
332
+ parsed_args.migration,
333
+ ignore_missing=False,
333
334
  )
334
335
 
335
- columns = (
336
+ column_headers = (
336
337
  'ID',
337
338
  'Server UUID',
338
339
  'Status',
@@ -351,14 +352,35 @@ class ShowMigration(command.ShowOne):
351
352
  'Updated At',
352
353
  )
353
354
 
354
- if compute_client.api_version >= api_versions.APIVersion('2.59'):
355
- columns += ('UUID',)
355
+ columns = (
356
+ 'id',
357
+ 'server_id',
358
+ 'status',
359
+ 'source_compute',
360
+ 'source_node',
361
+ 'dest_compute',
362
+ 'dest_host',
363
+ 'dest_node',
364
+ 'memory_total_bytes',
365
+ 'memory_processed_bytes',
366
+ 'memory_remaining_bytes',
367
+ 'disk_total_bytes',
368
+ 'disk_processed_bytes',
369
+ 'disk_remaining_bytes',
370
+ 'created_at',
371
+ 'updated_at',
372
+ )
356
373
 
357
- if compute_client.api_version >= api_versions.APIVersion('2.80'):
358
- columns += ('User ID', 'Project ID')
374
+ if sdk_utils.supports_microversion(compute_client, '2.59'):
375
+ column_headers += ('UUID',)
376
+ columns += ('uuid',)
377
+
378
+ if sdk_utils.supports_microversion(compute_client, '2.80'):
379
+ column_headers += ('User ID', 'Project ID')
380
+ columns += ('user_id', 'project_id')
359
381
 
360
382
  data = utils.get_item_properties(server_migration, columns)
361
- return columns, data
383
+ return column_headers, data
362
384
 
363
385
 
364
386
  class AbortMigration(command.Command):
@@ -382,9 +404,9 @@ class AbortMigration(command.Command):
382
404
  return parser
383
405
 
384
406
  def take_action(self, parsed_args):
385
- compute_client = self.app.client_manager.compute
407
+ compute_client = self.app.client_manager.sdk_connection.compute
386
408
 
387
- if compute_client.api_version < api_versions.APIVersion('2.24'):
409
+ if not sdk_utils.supports_microversion(compute_client, '2.24'):
388
410
  msg = _(
389
411
  '--os-compute-api-version 2.24 or greater is required to '
390
412
  'support the server migration abort command'
@@ -400,16 +422,16 @@ class AbortMigration(command.Command):
400
422
  )
401
423
  raise exceptions.CommandError(msg)
402
424
 
403
- if compute_client.api_version < api_versions.APIVersion('2.59'):
425
+ if not sdk_utils.supports_microversion(compute_client, '2.59'):
404
426
  msg = _(
405
427
  '--os-compute-api-version 2.59 or greater is required to '
406
428
  'abort server migrations by UUID'
407
429
  )
408
430
  raise exceptions.CommandError(msg)
409
431
 
410
- server = utils.find_resource(
411
- compute_client.servers,
432
+ server = compute_client.find_server(
412
433
  parsed_args.server,
434
+ ignore_missing=False,
413
435
  )
414
436
 
415
437
  # the nova API doesn't currently allow retrieval by UUID but it's a
@@ -421,8 +443,10 @@ class AbortMigration(command.Command):
421
443
  compute_client, server.id, parsed_args.migration,
422
444
  ).id
423
445
 
424
- compute_client.server_migrations.live_migration_abort(
425
- server.id, migration_id,
446
+ compute_client.abort_server_migration(
447
+ migration_id,
448
+ server.id,
449
+ ignore_missing=False,
426
450
  )
427
451
 
428
452
 
@@ -447,9 +471,9 @@ class ForceCompleteMigration(command.Command):
447
471
  return parser
448
472
 
449
473
  def take_action(self, parsed_args):
450
- compute_client = self.app.client_manager.compute
474
+ compute_client = self.app.client_manager.sdk_connection.compute
451
475
 
452
- if compute_client.api_version < api_versions.APIVersion('2.22'):
476
+ if not sdk_utils.supports_microversion(compute_client, '2.22'):
453
477
  msg = _(
454
478
  '--os-compute-api-version 2.22 or greater is required to '
455
479
  'support the server migration force complete command'
@@ -465,16 +489,16 @@ class ForceCompleteMigration(command.Command):
465
489
  )
466
490
  raise exceptions.CommandError(msg)
467
491
 
468
- if compute_client.api_version < api_versions.APIVersion('2.59'):
492
+ if not sdk_utils.supports_microversion(compute_client, '2.59'):
469
493
  msg = _(
470
494
  '--os-compute-api-version 2.59 or greater is required to '
471
495
  'abort server migrations by UUID'
472
496
  )
473
497
  raise exceptions.CommandError(msg)
474
498
 
475
- server = utils.find_resource(
476
- compute_client.servers,
499
+ server = compute_client.find_server(
477
500
  parsed_args.server,
501
+ ignore_missing=False,
478
502
  )
479
503
 
480
504
  # the nova API doesn't currently allow retrieval by UUID but it's a
@@ -486,6 +510,6 @@ class ForceCompleteMigration(command.Command):
486
510
  compute_client, server.id, parsed_args.migration,
487
511
  ).id
488
512
 
489
- compute_client.server_migrations.live_migrate_force_complete(
490
- server.id, migration_id,
513
+ compute_client.force_complete_server_migration(
514
+ migration_id, server.id
491
515
  )
@@ -14,7 +14,7 @@
14
14
 
15
15
  """Compute v2 Server action implementations"""
16
16
 
17
- from novaclient import api_versions
17
+ from openstack import utils as sdk_utils
18
18
  from osc_lib.command import command
19
19
  from osc_lib import exceptions
20
20
  from osc_lib import utils
@@ -34,27 +34,25 @@ class ListServerVolume(command.Lister):
34
34
  return parser
35
35
 
36
36
  def take_action(self, parsed_args):
37
+ compute_client = self.app.client_manager.sdk_connection.compute
37
38
 
38
- compute_client = self.app.client_manager.compute
39
-
40
- server = utils.find_resource(
41
- compute_client.servers,
39
+ server = compute_client.find_server(
42
40
  parsed_args.server,
41
+ ignore_missing=False,
43
42
  )
44
-
45
- volumes = compute_client.volumes.get_server_volumes(server.id)
43
+ volumes = compute_client.volume_attachments(server)
46
44
 
47
45
  columns = ()
48
46
  column_headers = ()
49
47
 
50
- if compute_client.api_version < api_versions.APIVersion('2.89'):
48
+ if not sdk_utils.supports_microversion(compute_client, '2.89'):
51
49
  columns += ('id',)
52
50
  column_headers += ('ID',)
53
51
 
54
52
  columns += (
55
53
  'device',
56
- 'serverId',
57
- 'volumeId',
54
+ 'server_id',
55
+ 'volume_id',
58
56
  )
59
57
  column_headers += (
60
58
  'Device',
@@ -62,40 +60,36 @@ class ListServerVolume(command.Lister):
62
60
  'Volume ID',
63
61
  )
64
62
 
65
- if compute_client.api_version >= api_versions.APIVersion('2.70'):
63
+ if sdk_utils.supports_microversion(compute_client, '2.70'):
66
64
  columns += ('tag',)
67
65
  column_headers += ('Tag',)
68
66
 
69
- if compute_client.api_version >= api_versions.APIVersion('2.79'):
67
+ if sdk_utils.supports_microversion(compute_client, '2.79'):
70
68
  columns += ('delete_on_termination',)
71
69
  column_headers += ('Delete On Termination?',)
72
70
 
73
- if compute_client.api_version >= api_versions.APIVersion('2.89'):
74
- columns += ('attachment_id', 'bdm_uuid')
71
+ if sdk_utils.supports_microversion(compute_client, '2.89'):
72
+ columns += ('attachment_id', 'bdm_id')
75
73
  column_headers += ('Attachment ID', 'BlockDeviceMapping UUID')
76
74
 
77
75
  return (
78
76
  column_headers,
79
- (
80
- utils.get_item_properties(
81
- s, columns, mixed_case_fields=('serverId', 'volumeId')
82
- ) for s in volumes
83
- ),
77
+ (utils.get_item_properties(s, columns) for s in volumes),
84
78
  )
85
79
 
86
80
 
87
- class UpdateServerVolume(command.Command):
81
+ class SetServerVolume(command.Command):
88
82
  """Update a volume attachment on the server."""
89
83
 
90
84
  def get_parser(self, prog_name):
91
- parser = super(UpdateServerVolume, self).get_parser(prog_name)
85
+ parser = super().get_parser(prog_name)
92
86
  parser.add_argument(
93
87
  'server',
94
88
  help=_('Server to update volume for (name or ID)'),
95
89
  )
96
90
  parser.add_argument(
97
91
  'volume',
98
- help=_('Volume (ID)'),
92
+ help=_('Volume to update attachment for (name or ID)'),
99
93
  )
100
94
  termination_group = parser.add_mutually_exclusive_group()
101
95
  termination_group.add_argument(
@@ -120,31 +114,34 @@ class UpdateServerVolume(command.Command):
120
114
  return parser
121
115
 
122
116
  def take_action(self, parsed_args):
123
-
124
- compute_client = self.app.client_manager.compute
117
+ compute_client = self.app.client_manager.sdk_connection.compute
118
+ volume_client = self.app.client_manager.sdk_connection.volume
125
119
 
126
120
  if parsed_args.delete_on_termination is not None:
127
- if compute_client.api_version < api_versions.APIVersion('2.85'):
121
+ if not sdk_utils.supports_microversion(compute_client, '2.85'):
128
122
  msg = _(
129
123
  '--os-compute-api-version 2.85 or greater is required to '
130
- 'support the --(no-)delete-on-termination option'
124
+ 'support the -delete-on-termination or '
125
+ '--preserve-on-termination option'
131
126
  )
132
127
  raise exceptions.CommandError(msg)
133
128
 
134
- server = utils.find_resource(
135
- compute_client.servers,
129
+ server = compute_client.find_server(
136
130
  parsed_args.server,
131
+ ignore_missing=False,
137
132
  )
138
-
139
- # NOTE(stephenfin): This may look silly, and that's because it is.
140
- # This API was originally used only for the swapping volumes, which
141
- # is an internal operation that should only be done by
142
- # orchestration software rather than a human. We're not going to
143
- # expose that, but we are going to expose the ability to change the
144
- # delete on termination behavior.
145
- compute_client.volumes.update_server_volume(
146
- server.id,
147
- parsed_args.volume,
133
+ volume = volume_client.find_volume(
148
134
  parsed_args.volume,
135
+ ignore_missing=False,
136
+ )
137
+
138
+ compute_client.update_volume_attachment(
139
+ server,
140
+ volume,
149
141
  delete_on_termination=parsed_args.delete_on_termination,
150
142
  )
143
+
144
+
145
+ # Legacy alias
146
+ class UpdateServerVolume(SetServerVolume):
147
+ """DEPRECATED: Use 'server volume set' instead."""
@@ -71,11 +71,11 @@ class DeleteService(command.Command):
71
71
 
72
72
 
73
73
  class ListService(command.Lister):
74
- _description = _("List compute services. Using "
75
- "``--os-compute-api-version`` 2.53 or greater will "
76
- "return the ID as a UUID value which can be used to "
77
- "uniquely identify the service in a multi-cell "
78
- "deployment.")
74
+ _description = _("""List compute services.
75
+
76
+ Using ``--os-compute-api-version`` 2.53 or greater will return the ID as a UUID
77
+ value which can be used to uniquely identify the service in a multi-cell
78
+ deployment.""")
79
79
 
80
80
  def get_parser(self, prog_name):
81
81
  parser = super(ListService, self).get_parser(prog_name)
@@ -87,6 +87,18 @@ def get_resource(manager, name_type_or_id):
87
87
  raise exceptions.CommandError(msg % name_type_or_id)
88
88
 
89
89
 
90
+ def get_resource_by_id(manager, resource_id):
91
+ """Get resource by ID
92
+
93
+ Raises CommandError if the resource is not found
94
+ """
95
+ try:
96
+ return manager.get(resource_id)
97
+ except identity_exc.NotFound:
98
+ msg = _("Resource with id {} not found")
99
+ raise exceptions.CommandError(msg.format(resource_id))
100
+
101
+
90
102
  def _get_token_resource(client, resource, parsed_name, parsed_domain=None):
91
103
  """Peek into the user's auth token to get resource IDs
92
104
 
@@ -37,7 +37,7 @@ class DeleteAccessRule(command.Command):
37
37
  'access_rule',
38
38
  metavar='<access-rule>',
39
39
  nargs="+",
40
- help=_('Access rule(s) to delete (name or ID)'),
40
+ help=_('Access rule ID(s) to delete'),
41
41
  )
42
42
  return parser
43
43
 
@@ -47,8 +47,9 @@ class DeleteAccessRule(command.Command):
47
47
  errors = 0
48
48
  for ac in parsed_args.access_rule:
49
49
  try:
50
- access_rule = utils.find_resource(
51
- identity_client.access_rules, ac)
50
+ access_rule = common.get_resource_by_id(
51
+ identity_client.access_rules, ac
52
+ )
52
53
  identity_client.access_rules.delete(access_rule.id)
53
54
  except Exception as e:
54
55
  errors += 1
@@ -103,14 +104,15 @@ class ShowAccessRule(command.ShowOne):
103
104
  parser.add_argument(
104
105
  'access_rule',
105
106
  metavar='<access-rule>',
106
- help=_('Access rule to display (name or ID)'),
107
+ help=_('Access rule ID to display'),
107
108
  )
108
109
  return parser
109
110
 
110
111
  def take_action(self, parsed_args):
111
112
  identity_client = self.app.client_manager.identity
112
- access_rule = utils.find_resource(identity_client.access_rules,
113
- parsed_args.access_rule)
113
+ access_rule = common.get_resource_by_id(
114
+ identity_client.access_rules, parsed_args.access_rule
115
+ )
114
116
 
115
117
  access_rule._info.pop('links', None)
116
118
 
@@ -1206,7 +1206,10 @@ class SetImage(command.Command):
1206
1206
  const="accepted",
1207
1207
  dest="membership",
1208
1208
  default=None,
1209
- help=_("Accept the image membership"),
1209
+ help=_(
1210
+ "Accept the image membership for either the project indicated "
1211
+ "by '--project', if provided, or the current user's project"
1212
+ ),
1210
1213
  )
1211
1214
  membership_group.add_argument(
1212
1215
  "--reject",
@@ -1214,7 +1217,10 @@ class SetImage(command.Command):
1214
1217
  const="rejected",
1215
1218
  dest="membership",
1216
1219
  default=None,
1217
- help=_("Reject the image membership"),
1220
+ help=_(
1221
+ "Reject the image membership for either the project indicated "
1222
+ "by '--project', if provided, or the current user's project"
1223
+ ),
1218
1224
  )
1219
1225
  membership_group.add_argument(
1220
1226
  "--pending",