python-openstackclient 6.3.0__py3-none-any.whl → 6.5.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 (162) hide show
  1. openstackclient/common/availability_zone.py +4 -4
  2. openstackclient/common/pagination.py +82 -0
  3. openstackclient/compute/v2/flavor.py +2 -16
  4. openstackclient/compute/v2/hypervisor.py +2 -21
  5. openstackclient/compute/v2/keypair.py +2 -9
  6. openstackclient/compute/v2/server.py +220 -131
  7. openstackclient/compute/v2/server_event.py +30 -19
  8. openstackclient/compute/v2/server_group.py +2 -23
  9. openstackclient/compute/v2/server_migration.py +2 -22
  10. openstackclient/compute/v2/usage.py +4 -6
  11. openstackclient/identity/v3/mapping.py +25 -3
  12. openstackclient/identity/v3/policy.py +3 -1
  13. openstackclient/image/v2/cache.py +218 -0
  14. openstackclient/image/v2/image.py +40 -17
  15. openstackclient/image/v2/metadef_namespaces.py +25 -21
  16. openstackclient/image/v2/metadef_objects.py +189 -0
  17. openstackclient/image/v2/metadef_properties.py +284 -0
  18. openstackclient/network/utils.py +100 -0
  19. openstackclient/network/v2/default_security_group_rule.py +418 -0
  20. openstackclient/network/v2/local_ip_association.py +1 -1
  21. openstackclient/network/v2/ndp_proxy.py +7 -3
  22. openstackclient/network/v2/network.py +2 -2
  23. openstackclient/network/v2/port.py +65 -19
  24. openstackclient/network/v2/security_group_rule.py +18 -111
  25. openstackclient/network/v2/subnet.py +1 -0
  26. openstackclient/object/v1/container.py +2 -12
  27. openstackclient/object/v1/object.py +2 -11
  28. openstackclient/tests/functional/base.py +13 -6
  29. openstackclient/tests/functional/identity/v3/test_role.py +11 -3
  30. openstackclient/tests/functional/network/v2/common.py +7 -1
  31. openstackclient/tests/functional/network/v2/test_address_group.py +2 -4
  32. openstackclient/tests/functional/network/v2/test_address_scope.py +0 -6
  33. openstackclient/tests/functional/network/v2/test_default_security_group_rule.py +67 -0
  34. openstackclient/tests/functional/network/v2/test_floating_ip.py +3 -6
  35. openstackclient/tests/functional/network/v2/test_ip_availability.py +3 -8
  36. openstackclient/tests/functional/network/v2/test_l3_conntrack_helper.py +3 -4
  37. openstackclient/tests/functional/network/v2/test_local_ip.py +2 -4
  38. openstackclient/tests/functional/network/v2/test_network.py +18 -17
  39. openstackclient/tests/functional/network/v2/test_network_agent.py +24 -21
  40. openstackclient/tests/functional/network/v2/test_network_flavor.py +0 -6
  41. openstackclient/tests/functional/network/v2/test_network_flavor_profile.py +0 -6
  42. openstackclient/tests/functional/network/v2/test_network_meter.py +6 -6
  43. openstackclient/tests/functional/network/v2/test_network_meter_rule.py +7 -8
  44. openstackclient/tests/functional/network/v2/test_network_ndp_proxy.py +1 -3
  45. openstackclient/tests/functional/network/v2/test_network_qos_policy.py +4 -4
  46. openstackclient/tests/functional/network/v2/test_network_qos_rule.py +16 -20
  47. openstackclient/tests/functional/network/v2/test_network_qos_rule_type.py +4 -4
  48. openstackclient/tests/functional/network/v2/test_network_rbac.py +1 -4
  49. openstackclient/tests/functional/network/v2/test_network_segment.py +7 -12
  50. openstackclient/tests/functional/network/v2/test_network_segment_range.py +3 -4
  51. openstackclient/tests/functional/network/v2/test_network_service_provider.py +2 -4
  52. openstackclient/tests/functional/network/v2/test_network_trunk.py +3 -3
  53. openstackclient/tests/functional/network/v2/test_port.py +2 -8
  54. openstackclient/tests/functional/network/v2/test_router.py +0 -6
  55. openstackclient/tests/functional/network/v2/test_security_group.py +1 -4
  56. openstackclient/tests/functional/network/v2/test_security_group_rule.py +1 -4
  57. openstackclient/tests/functional/network/v2/test_subnet.py +4 -22
  58. openstackclient/tests/functional/network/v2/test_subnet_pool.py +0 -6
  59. openstackclient/tests/unit/common/test_availability_zone.py +28 -30
  60. openstackclient/tests/unit/common/test_extension.py +1 -4
  61. openstackclient/tests/unit/common/test_limits.py +2 -4
  62. openstackclient/tests/unit/common/test_project_cleanup.py +3 -10
  63. openstackclient/tests/unit/common/test_quota.py +18 -24
  64. openstackclient/tests/unit/compute/v2/fakes.py +24 -11
  65. openstackclient/tests/unit/compute/v2/test_agent.py +1 -1
  66. openstackclient/tests/unit/compute/v2/test_aggregate.py +62 -72
  67. openstackclient/tests/unit/compute/v2/test_console.py +18 -30
  68. openstackclient/tests/unit/compute/v2/test_flavor.py +85 -89
  69. openstackclient/tests/unit/compute/v2/test_host.py +12 -19
  70. openstackclient/tests/unit/compute/v2/test_hypervisor.py +23 -25
  71. openstackclient/tests/unit/compute/v2/test_hypervisor_stats.py +2 -6
  72. openstackclient/tests/unit/compute/v2/test_keypair.py +25 -39
  73. openstackclient/tests/unit/compute/v2/test_server.py +316 -365
  74. openstackclient/tests/unit/compute/v2/test_server_backup.py +5 -17
  75. openstackclient/tests/unit/compute/v2/test_server_event.py +23 -25
  76. openstackclient/tests/unit/compute/v2/test_server_group.py +41 -33
  77. openstackclient/tests/unit/compute/v2/test_server_image.py +6 -18
  78. openstackclient/tests/unit/compute/v2/test_server_migration.py +45 -45
  79. openstackclient/tests/unit/compute/v2/test_server_volume.py +15 -31
  80. openstackclient/tests/unit/compute/v2/test_service.py +51 -56
  81. openstackclient/tests/unit/compute/v2/test_usage.py +10 -13
  82. openstackclient/tests/unit/fakes.py +4 -0
  83. openstackclient/tests/unit/identity/v3/test_mappings.py +9 -4
  84. openstackclient/tests/unit/identity/v3/test_trust.py +0 -2
  85. openstackclient/tests/unit/image/v1/fakes.py +2 -1
  86. openstackclient/tests/unit/image/v1/test_image.py +1 -1
  87. openstackclient/tests/unit/image/v2/fakes.py +82 -0
  88. openstackclient/tests/unit/image/v2/test_cache.py +214 -0
  89. openstackclient/tests/unit/image/v2/test_image.py +62 -4
  90. openstackclient/tests/unit/image/v2/test_metadef_namespaces.py +5 -19
  91. openstackclient/tests/unit/image/v2/test_metadef_objects.py +162 -0
  92. openstackclient/tests/unit/image/v2/test_metadef_properties.py +227 -0
  93. openstackclient/tests/unit/integ/cli/test_shell.py +0 -2
  94. openstackclient/tests/unit/network/test_common.py +3 -3
  95. openstackclient/tests/unit/network/v2/fakes.py +1 -0
  96. openstackclient/tests/unit/network/v2/test_default_security_group_rule.py +1133 -0
  97. openstackclient/tests/unit/network/v2/test_floating_ip_compute.py +5 -13
  98. openstackclient/tests/unit/network/v2/test_floating_ip_pool_compute.py +1 -9
  99. openstackclient/tests/unit/network/v2/test_network.py +33 -0
  100. openstackclient/tests/unit/network/v2/test_network_compute.py +5 -11
  101. openstackclient/tests/unit/network/v2/test_network_trunk.py +6 -8
  102. openstackclient/tests/unit/network/v2/test_port.py +83 -38
  103. openstackclient/tests/unit/network/v2/test_security_group_compute.py +7 -15
  104. openstackclient/tests/unit/network/v2/test_security_group_rule_compute.py +19 -27
  105. openstackclient/tests/unit/network/v2/test_security_group_rule_network.py +3 -6
  106. openstackclient/tests/unit/network/v2/test_subnet.py +92 -0
  107. openstackclient/tests/unit/network/v2/test_subnet_pool.py +11 -13
  108. openstackclient/tests/unit/test_shell.py +1 -7
  109. openstackclient/tests/unit/utils.py +10 -4
  110. openstackclient/tests/unit/volume/v1/fakes.py +7 -1
  111. openstackclient/tests/unit/volume/v1/test_qos_specs.py +2 -2
  112. openstackclient/tests/unit/volume/v1/test_service.py +1 -1
  113. openstackclient/tests/unit/volume/v1/test_transfer_request.py +2 -2
  114. openstackclient/tests/unit/volume/v1/test_type.py +2 -4
  115. openstackclient/tests/unit/volume/v1/test_volume.py +5 -7
  116. openstackclient/tests/unit/volume/v1/test_volume_backup.py +4 -4
  117. openstackclient/tests/unit/volume/v2/fakes.py +32 -12
  118. openstackclient/tests/unit/volume/v2/test_backup_record.py +1 -1
  119. openstackclient/tests/unit/volume/v2/test_consistency_group.py +4 -6
  120. openstackclient/tests/unit/volume/v2/test_consistency_group_snapshot.py +2 -4
  121. openstackclient/tests/unit/volume/v2/test_qos_specs.py +2 -2
  122. openstackclient/tests/unit/volume/v2/test_service.py +1 -1
  123. openstackclient/tests/unit/volume/v2/test_volume.py +78 -16
  124. openstackclient/tests/unit/volume/v2/test_volume_backend.py +10 -22
  125. openstackclient/tests/unit/volume/v2/test_volume_backup.py +76 -89
  126. openstackclient/tests/unit/volume/v2/test_volume_host.py +1 -1
  127. openstackclient/tests/unit/volume/v2/test_volume_snapshot.py +5 -7
  128. openstackclient/tests/unit/volume/v2/test_volume_transfer_request.py +4 -8
  129. openstackclient/tests/unit/volume/v2/test_volume_type.py +164 -24
  130. openstackclient/tests/unit/volume/v3/fakes.py +91 -15
  131. openstackclient/tests/unit/volume/v3/test_block_storage_cleanup.py +3 -7
  132. openstackclient/tests/unit/volume/v3/test_block_storage_cluster.py +11 -31
  133. openstackclient/tests/unit/volume/v3/test_block_storage_log_level.py +6 -16
  134. openstackclient/tests/unit/volume/v3/test_block_storage_manage.py +219 -157
  135. openstackclient/tests/unit/volume/v3/test_block_storage_resource_filter.py +32 -23
  136. openstackclient/tests/unit/volume/v3/test_volume.py +50 -48
  137. openstackclient/tests/unit/volume/v3/test_volume_attachment.py +17 -47
  138. openstackclient/tests/unit/volume/v3/test_volume_group.py +23 -65
  139. openstackclient/tests/unit/volume/v3/test_volume_group_snapshot.py +88 -77
  140. openstackclient/tests/unit/volume/v3/test_volume_group_type.py +14 -42
  141. openstackclient/tests/unit/volume/v3/test_volume_message.py +10 -28
  142. openstackclient/volume/v1/volume.py +2 -14
  143. openstackclient/volume/v2/volume.py +30 -15
  144. openstackclient/volume/v2/volume_backend.py +10 -18
  145. openstackclient/volume/v2/volume_backup.py +18 -15
  146. openstackclient/volume/v2/volume_snapshot.py +2 -12
  147. openstackclient/volume/v2/volume_type.py +211 -14
  148. openstackclient/volume/v3/block_storage_manage.py +72 -11
  149. openstackclient/volume/v3/block_storage_resource_filter.py +33 -11
  150. openstackclient/volume/v3/volume_attachment.py +2 -14
  151. openstackclient/volume/v3/volume_group_snapshot.py +27 -27
  152. openstackclient/volume/v3/volume_message.py +2 -13
  153. {python_openstackclient-6.3.0.dist-info → python_openstackclient-6.5.0.dist-info}/AUTHORS +11 -0
  154. {python_openstackclient-6.3.0.dist-info → python_openstackclient-6.5.0.dist-info}/METADATA +6 -5
  155. {python_openstackclient-6.3.0.dist-info → python_openstackclient-6.5.0.dist-info}/RECORD +160 -151
  156. {python_openstackclient-6.3.0.dist-info → python_openstackclient-6.5.0.dist-info}/entry_points.txt +23 -5
  157. python_openstackclient-6.5.0.dist-info/pbr.json +1 -0
  158. openstackclient/tests/unit/common/test_parseractions.py +0 -233
  159. python_openstackclient-6.3.0.dist-info/pbr.json +0 -1
  160. {python_openstackclient-6.3.0.dist-info → python_openstackclient-6.5.0.dist-info}/LICENSE +0 -0
  161. {python_openstackclient-6.3.0.dist-info → python_openstackclient-6.5.0.dist-info}/WHEEL +0 -0
  162. {python_openstackclient-6.3.0.dist-info → python_openstackclient-6.5.0.dist-info}/top_level.txt +0 -0
@@ -16,6 +16,7 @@
16
16
  """Compute v2 Server operation event implementations"""
17
17
 
18
18
  import logging
19
+ import uuid
19
20
 
20
21
  from cliff import columns
21
22
  import iso8601
@@ -24,14 +25,38 @@ from openstack import utils as sdk_utils
24
25
  from osc_lib.command import command
25
26
  from osc_lib import exceptions
26
27
  from osc_lib import utils
27
- from oslo_utils import uuidutils
28
28
 
29
+ from openstackclient.common import pagination
29
30
  from openstackclient.i18n import _
30
31
 
31
-
32
32
  LOG = logging.getLogger(__name__)
33
33
 
34
34
 
35
+ # TODO(stephenfin): Move this to osc_lib since it's useful elsewhere (e.g.
36
+ # glance)
37
+ def is_uuid_like(value) -> bool:
38
+ """Returns validation of a value as a UUID.
39
+
40
+ :param val: Value to verify
41
+ :type val: string
42
+ :returns: bool
43
+
44
+ .. versionchanged:: 1.1.1
45
+ Support non-lowercase UUIDs.
46
+ """
47
+ try:
48
+ formatted_value = (
49
+ value.replace('urn:', '')
50
+ .replace('uuid:', '')
51
+ .strip('{}')
52
+ .replace('-', '')
53
+ .lower()
54
+ )
55
+ return str(uuid.UUID(value)).replace('-', '') == formatted_value
56
+ except (TypeError, ValueError, AttributeError):
57
+ return False
58
+
59
+
35
60
  class ServerActionEventColumn(columns.FormattableColumn):
36
61
  """Custom formatter for server action events.
37
62
 
@@ -119,21 +144,7 @@ class ListServerEvent(command.Lister):
119
144
  "(supported with --os-compute-api-version 2.66 or above)"
120
145
  ),
121
146
  )
122
- parser.add_argument(
123
- '--marker',
124
- help=_(
125
- 'The last server event ID of the previous page '
126
- '(supported by --os-compute-api-version 2.58 or above)'
127
- ),
128
- )
129
- parser.add_argument(
130
- '--limit',
131
- type=int,
132
- help=_(
133
- 'Maximum number of server events to display '
134
- '(supported by --os-compute-api-version 2.58 or above)'
135
- ),
136
- )
147
+ pagination.add_marker_pagination_option_to_parser(parser)
137
148
  return parser
138
149
 
139
150
  def take_action(self, parsed_args):
@@ -202,7 +213,7 @@ class ListServerEvent(command.Lister):
202
213
  # If we fail to find the resource, it is possible the server is
203
214
  # deleted. Try once more using the <server> arg directly if it is a
204
215
  # UUID.
205
- if uuidutils.is_uuid_like(parsed_args.server):
216
+ if is_uuid_like(parsed_args.server):
206
217
  server_id = parsed_args.server
207
218
  else:
208
219
  raise
@@ -275,7 +286,7 @@ class ShowServerEvent(command.ShowOne):
275
286
  # If we fail to find the resource, it is possible the server is
276
287
  # deleted. Try once more using the <server> arg directly if it is a
277
288
  # UUID.
278
- if uuidutils.is_uuid_like(parsed_args.server):
289
+ if is_uuid_like(parsed_args.server):
279
290
  server_id = parsed_args.server
280
291
  else:
281
292
  raise
@@ -24,9 +24,9 @@ from osc_lib.command import command
24
24
  from osc_lib import exceptions
25
25
  from osc_lib import utils
26
26
 
27
+ from openstackclient.common import pagination
27
28
  from openstackclient.i18n import _
28
29
 
29
-
30
30
  LOG = logging.getLogger(__name__)
31
31
 
32
32
 
@@ -191,28 +191,7 @@ class ListServerGroup(command.Lister):
191
191
  )
192
192
  # TODO(stephenfin): This should really be a --marker option, but alas
193
193
  # the API doesn't support that for some reason
194
- parser.add_argument(
195
- '--offset',
196
- metavar='<offset>',
197
- type=int,
198
- default=None,
199
- help=_(
200
- 'Index from which to start listing servers. This should '
201
- 'typically be a factor of --limit. Display all servers groups '
202
- 'if not specified.'
203
- ),
204
- )
205
- parser.add_argument(
206
- '--limit',
207
- metavar='<limit>',
208
- type=int,
209
- default=None,
210
- help=_(
211
- "Maximum number of server groups to display. "
212
- "If limit is greater than 'osapi_max_limit' option of Nova "
213
- "API, 'osapi_max_limit' will be used instead."
214
- ),
215
- )
194
+ pagination.add_offset_pagination_option_to_parser(parser)
216
195
  return parser
217
196
 
218
197
  def take_action(self, parsed_args):
@@ -19,6 +19,7 @@ from osc_lib.command import command
19
19
  from osc_lib import exceptions
20
20
  from osc_lib import utils
21
21
 
22
+ from openstackclient.common import pagination
22
23
  from openstackclient.i18n import _
23
24
  from openstackclient.identity import common as identity_common
24
25
 
@@ -54,28 +55,7 @@ class ListMigration(command.Lister):
54
55
  ],
55
56
  help=_('Filter migrations by type'),
56
57
  )
57
- parser.add_argument(
58
- '--marker',
59
- metavar='<marker>',
60
- help=_(
61
- "The last migration of the previous page; displays list "
62
- "of migrations after 'marker'. Note that the marker is "
63
- "the migration UUID. "
64
- "(supported with --os-compute-api-version 2.59 or above)"
65
- ),
66
- )
67
- parser.add_argument(
68
- '--limit',
69
- metavar='<limit>',
70
- type=int,
71
- help=_(
72
- "Maximum number of migrations to display. Note that there "
73
- "is a configurable max limit on the server, and the limit "
74
- "that is used will be the minimum of what is requested "
75
- "here and what is configured in the server. "
76
- "(supported with --os-compute-api-version 2.59 or above)"
77
- ),
78
- )
58
+ pagination.add_marker_pagination_option_to_parser(parser)
79
59
  parser.add_argument(
80
60
  '--changes-since',
81
61
  dest='changes_since',
@@ -153,7 +153,6 @@ class ListUsage(command.Lister):
153
153
  )
154
154
 
155
155
  date_cli_format = "%Y-%m-%d"
156
- date_api_format = "%Y-%m-%dT%H:%M:%S"
157
156
  now = datetime.datetime.utcnow()
158
157
 
159
158
  if parsed_args.start:
@@ -170,8 +169,8 @@ class ListUsage(command.Lister):
170
169
 
171
170
  usage_list = list(
172
171
  compute_client.usages(
173
- start=start.strftime(date_api_format),
174
- end=end.strftime(date_api_format),
172
+ start=start,
173
+ end=end,
175
174
  detailed=True,
176
175
  )
177
176
  )
@@ -239,7 +238,6 @@ class ShowUsage(command.ShowOne):
239
238
  identity_client = self.app.client_manager.identity
240
239
  compute_client = self.app.client_manager.sdk_connection.compute
241
240
  date_cli_format = "%Y-%m-%d"
242
- date_api_format = "%Y-%m-%dT%H:%M:%S"
243
241
  now = datetime.datetime.utcnow()
244
242
 
245
243
  if parsed_args.start:
@@ -265,8 +263,8 @@ class ShowUsage(command.ShowOne):
265
263
 
266
264
  usage = compute_client.get_usage(
267
265
  project=project,
268
- start=start.strftime(date_api_format),
269
- end=end.strftime(date_api_format),
266
+ start=start,
267
+ end=end,
270
268
  )
271
269
 
272
270
  if parsed_args.formatter == 'table':
@@ -81,6 +81,21 @@ class _RulesReader(object):
81
81
  else:
82
82
  return rules
83
83
 
84
+ @staticmethod
85
+ def add_federated_schema_version_option(parser):
86
+ parser.add_argument(
87
+ '--schema-version',
88
+ metavar='<schema_version>',
89
+ required=False,
90
+ default=None,
91
+ help=_(
92
+ "The federated attribute mapping schema version. The "
93
+ "default value on the client side is 'None'; however, that "
94
+ "will lead the backend to set the default according to "
95
+ "'attribute_mapping_default_schema_version' option."
96
+ ),
97
+ )
98
+
84
99
 
85
100
  class CreateMapping(command.ShowOne, _RulesReader):
86
101
  _description = _("Create new mapping")
@@ -98,6 +113,7 @@ class CreateMapping(command.ShowOne, _RulesReader):
98
113
  required=True,
99
114
  help=_('Filename that contains a set of mapping rules (required)'),
100
115
  )
116
+ _RulesReader.add_federated_schema_version_option(parser)
101
117
  return parser
102
118
 
103
119
  def take_action(self, parsed_args):
@@ -105,7 +121,9 @@ class CreateMapping(command.ShowOne, _RulesReader):
105
121
 
106
122
  rules = self._read_rules(parsed_args.rules)
107
123
  mapping = identity_client.federation.mappings.create(
108
- mapping_id=parsed_args.mapping, rules=rules
124
+ mapping_id=parsed_args.mapping,
125
+ rules=rules,
126
+ schema_version=parsed_args.schema_version,
109
127
  )
110
128
 
111
129
  mapping._info.pop('links', None)
@@ -158,7 +176,7 @@ class ListMapping(command.Lister):
158
176
  # rules, (s)he should show specific ones.
159
177
  identity_client = self.app.client_manager.identity
160
178
  data = identity_client.federation.mappings.list()
161
- columns = ('ID',)
179
+ columns = ('ID', 'schema_version')
162
180
  items = [utils.get_item_properties(s, columns) for s in data]
163
181
  return (columns, items)
164
182
 
@@ -178,6 +196,8 @@ class SetMapping(command.Command, _RulesReader):
178
196
  metavar='<filename>',
179
197
  help=_('Filename that contains a new set of mapping rules'),
180
198
  )
199
+
200
+ _RulesReader.add_federated_schema_version_option(parser)
181
201
  return parser
182
202
 
183
203
  def take_action(self, parsed_args):
@@ -186,7 +206,9 @@ class SetMapping(command.Command, _RulesReader):
186
206
  rules = self._read_rules(parsed_args.rules)
187
207
 
188
208
  mapping = identity_client.federation.mappings.update(
189
- mapping=parsed_args.mapping, rules=rules
209
+ mapping=parsed_args.mapping,
210
+ rules=rules,
211
+ schema_version=parsed_args.schema_version,
190
212
  )
191
213
 
192
214
  mapping._info.pop('links', None)
@@ -92,7 +92,9 @@ class DeletePolicy(command.Command):
92
92
 
93
93
  if result > 0:
94
94
  total = len(parsed_args.policy)
95
- msg = _("%(result)s of %(total)s policys failed " "to delete.") % {
95
+ msg = _(
96
+ "%(result)s of %(total)s policies failed " "to delete."
97
+ ) % {
96
98
  'result': result,
97
99
  'total': total,
98
100
  }
@@ -0,0 +1,218 @@
1
+ # Copyright 2023 Red Hat.
2
+ # All Rights Reserved.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License"); you may
5
+ # not use this file except in compliance with the License. You may obtain
6
+ # a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12
+ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13
+ # License for the specific language governing permissions and limitations
14
+ # under the License.
15
+
16
+ import copy
17
+ import datetime
18
+ import logging
19
+
20
+ from osc_lib.command import command
21
+ from osc_lib import exceptions
22
+ from osc_lib import utils
23
+
24
+ from openstackclient.i18n import _
25
+
26
+
27
+ LOG = logging.getLogger(__name__)
28
+
29
+
30
+ def _format_image_cache(cached_images):
31
+ """Format image cache to make it more consistent with OSC operations."""
32
+
33
+ image_list = []
34
+ for item in cached_images:
35
+ if item == "cached_images":
36
+ for image in cached_images[item]:
37
+ image_obj = copy.deepcopy(image)
38
+ image_obj['state'] = 'cached'
39
+ image_obj[
40
+ 'last_accessed'
41
+ ] = datetime.datetime.utcfromtimestamp(
42
+ image['last_accessed']
43
+ ).isoformat()
44
+ image_obj[
45
+ 'last_modified'
46
+ ] = datetime.datetime.utcfromtimestamp(
47
+ image['last_modified']
48
+ ).isoformat()
49
+ image_list.append(image_obj)
50
+ elif item == "queued_images":
51
+ for image in cached_images[item]:
52
+ image = {'image_id': image}
53
+ image.update(
54
+ {
55
+ 'state': 'queued',
56
+ 'last_accessed': 'N/A',
57
+ 'last_modified': 'N/A',
58
+ 'size': 'N/A',
59
+ 'hits': 'N/A',
60
+ }
61
+ )
62
+ image_list.append(image)
63
+ return image_list
64
+
65
+
66
+ class ListCachedImage(command.Lister):
67
+ _description = _("Get Cache State")
68
+
69
+ def get_parser(self, prog_name):
70
+ parser = super().get_parser(prog_name)
71
+ return parser
72
+
73
+ def take_action(self, parsed_args):
74
+ image_client = self.app.client_manager.image
75
+
76
+ # List of Cache data received
77
+ data = _format_image_cache(dict(image_client.get_image_cache()))
78
+ columns = [
79
+ 'image_id',
80
+ 'state',
81
+ 'last_accessed',
82
+ 'last_modified',
83
+ 'size',
84
+ 'hits',
85
+ ]
86
+ column_headers = [
87
+ "ID",
88
+ "State",
89
+ "Last Accessed (UTC)",
90
+ "Last Modified (UTC)",
91
+ "Size",
92
+ "Hits",
93
+ ]
94
+
95
+ return (
96
+ column_headers,
97
+ (
98
+ utils.get_dict_properties(
99
+ image,
100
+ columns,
101
+ )
102
+ for image in data
103
+ ),
104
+ )
105
+
106
+
107
+ class QueueCachedImage(command.Command):
108
+ _description = _("Queue image(s) for caching.")
109
+
110
+ def get_parser(self, prog_name):
111
+ parser = super().get_parser(prog_name)
112
+ parser.add_argument(
113
+ "images",
114
+ metavar="<image>",
115
+ nargs="+",
116
+ help=_("Image to display (name or ID)"),
117
+ )
118
+ return parser
119
+
120
+ def take_action(self, parsed_args):
121
+ image_client = self.app.client_manager.image
122
+
123
+ failures = 0
124
+ for image in parsed_args.images:
125
+ try:
126
+ image_obj = image_client.find_image(
127
+ image,
128
+ ignore_missing=False,
129
+ )
130
+ image_client.queue_image(image_obj.id)
131
+ except Exception as e:
132
+ failures += 1
133
+ msg = _(
134
+ "Failed to queue image with name or "
135
+ "ID '%(image)s': %(e)s"
136
+ )
137
+ LOG.error(msg, {'image': image, 'e': e})
138
+
139
+ if failures > 0:
140
+ total = len(parsed_args.images)
141
+ msg = _("Failed to queue %(failures)s of %(total)s images") % {
142
+ 'failures': failures,
143
+ 'total': total,
144
+ }
145
+ raise exceptions.CommandError(msg)
146
+
147
+
148
+ class DeleteCachedImage(command.Command):
149
+ _description = _("Delete image(s) from cache")
150
+
151
+ def get_parser(self, prog_name):
152
+ parser = super().get_parser(prog_name)
153
+ parser.add_argument(
154
+ "images",
155
+ metavar="<image>",
156
+ nargs="+",
157
+ help=_("Image(s) to delete (name or ID)"),
158
+ )
159
+ return parser
160
+
161
+ def take_action(self, parsed_args):
162
+ failures = 0
163
+ image_client = self.app.client_manager.image
164
+ for image in parsed_args.images:
165
+ try:
166
+ image_obj = image_client.find_image(
167
+ image,
168
+ ignore_missing=False,
169
+ )
170
+ image_client.cache_delete_image(image_obj.id)
171
+ except Exception as e:
172
+ failures += 1
173
+ msg = _(
174
+ "Failed to delete image with name or "
175
+ "ID '%(image)s': %(e)s"
176
+ )
177
+ LOG.error(msg, {'image': image, 'e': e})
178
+
179
+ if failures > 0:
180
+ total = len(parsed_args.images)
181
+ msg = _("Failed to delete %(failures)s of %(total)s images.") % {
182
+ 'failures': failures,
183
+ 'total': total,
184
+ }
185
+ raise exceptions.CommandError(msg)
186
+
187
+
188
+ class ClearCachedImage(command.Command):
189
+ _description = _("Clear all images from cache, queue or both")
190
+
191
+ def get_parser(self, prog_name):
192
+ parser = super().get_parser(prog_name)
193
+ parser.add_argument(
194
+ "--cache",
195
+ action="store_const",
196
+ const="cache",
197
+ dest="target",
198
+ help=_("Clears all the cached images"),
199
+ )
200
+ parser.add_argument(
201
+ "--queue",
202
+ action="store_const",
203
+ const="queue",
204
+ dest="target",
205
+ help=_("Clears all the queued images"),
206
+ )
207
+ return parser
208
+
209
+ def take_action(self, parsed_args):
210
+ image_client = self.app.client_manager.image
211
+
212
+ target = parsed_args.target
213
+ try:
214
+ image_client.clear_cache(target)
215
+ except Exception:
216
+ msg = _("Failed to clear image cache")
217
+ LOG.error(msg)
218
+ raise exceptions.CommandError(msg)
@@ -31,6 +31,7 @@ from osc_lib.command import command
31
31
  from osc_lib import exceptions
32
32
  from osc_lib import utils
33
33
 
34
+ from openstackclient.common import pagination
34
35
  from openstackclient.common import progressbar
35
36
  from openstackclient.i18n import _
36
37
  from openstackclient.identity import common as identity_common
@@ -805,9 +806,9 @@ class ListImage(command.Lister):
805
806
  default=False,
806
807
  help=_('List additional fields in output'),
807
808
  )
808
-
809
809
  # --page-size has never worked, leave here for silent compatibility
810
810
  # We'll implement limit/marker differently later
811
+ # TODO(stephenfin): Remove this in the next major version bump
811
812
  parser.add_argument(
812
813
  "--page-size",
813
814
  metavar="<size>",
@@ -823,22 +824,7 @@ class ListImage(command.Lister):
823
824
  "specified separated by comma"
824
825
  ),
825
826
  )
826
- parser.add_argument(
827
- "--limit",
828
- metavar="<num-images>",
829
- type=int,
830
- help=_("Maximum number of images to display."),
831
- )
832
- parser.add_argument(
833
- '--marker',
834
- metavar='<image>',
835
- default=None,
836
- help=_(
837
- "The last image of the previous page. Display "
838
- "list of images after marker. Display all images if not "
839
- "specified. (name or ID)"
840
- ),
841
- )
827
+ pagination.add_marker_pagination_option_to_parser(parser)
842
828
  return parser
843
829
 
844
830
  def take_action(self, parsed_args):
@@ -1006,6 +992,43 @@ class RemoveProjectImage(command.Command):
1006
992
  image_client.remove_member(member=project_id, image=image.id)
1007
993
 
1008
994
 
995
+ class ShowProjectImage(command.ShowOne):
996
+ _description = _("Show a particular project associated with image")
997
+
998
+ def get_parser(self, prog_name):
999
+ parser = super().get_parser(prog_name)
1000
+ parser.add_argument(
1001
+ "image",
1002
+ metavar="<image>",
1003
+ help=_("Image (name or ID)"),
1004
+ )
1005
+ parser.add_argument(
1006
+ "member",
1007
+ metavar="<project>",
1008
+ help=_("Project to show (name or ID)"),
1009
+ )
1010
+ identity_common.add_project_domain_option_to_parser(parser)
1011
+ return parser
1012
+
1013
+ def take_action(self, parsed_args):
1014
+ image_client = self.app.client_manager.image
1015
+
1016
+ image = image_client.find_image(
1017
+ parsed_args.image,
1018
+ ignore_missing=False,
1019
+ )
1020
+
1021
+ obj = image_client.get_member(
1022
+ image=image.id,
1023
+ member=parsed_args.member,
1024
+ )
1025
+
1026
+ display_columns, columns = _get_member_columns(obj)
1027
+ data = utils.get_item_properties(obj, columns, formatters={})
1028
+
1029
+ return (display_columns, data)
1030
+
1031
+
1009
1032
  class SaveImage(command.Command):
1010
1033
  _description = _("Save an image locally")
1011
1034