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
@@ -42,6 +42,7 @@ def _format_namespace(namespace):
42
42
  'owner',
43
43
  'protected',
44
44
  'schema',
45
+ 'updated_at',
45
46
  'visibility',
46
47
  ]
47
48
 
@@ -61,7 +62,7 @@ def _format_namespace(namespace):
61
62
  return info
62
63
 
63
64
 
64
- class CreateMetadefNameSpace(command.ShowOne):
65
+ class CreateMetadefNamespace(command.ShowOne):
65
66
  _description = _("Create a metadef namespace")
66
67
 
67
68
  def get_parser(self, prog_name):
@@ -130,20 +131,21 @@ class CreateMetadefNameSpace(command.ShowOne):
130
131
  kwargs['visibility'] = parsed_args.visibility
131
132
 
132
133
  data = image_client.create_metadef_namespace(**kwargs)
134
+ info = _format_namespace(data)
133
135
 
134
- return zip(*sorted(data.items()))
136
+ return zip(*sorted(info.items()))
135
137
 
136
138
 
137
- class DeleteMetadefNameSpace(command.Command):
139
+ class DeleteMetadefNamespace(command.Command):
138
140
  _description = _("Delete metadef namespace")
139
141
 
140
142
  def get_parser(self, prog_name):
141
143
  parser = super().get_parser(prog_name)
142
144
  parser.add_argument(
143
- "namespace_name",
144
- metavar="<namespace_name>",
145
+ "namespace",
146
+ metavar="<namespace>",
145
147
  nargs="+",
146
- help=_("An identifier (a name) for the namespace"),
148
+ help=_("Metadef namespace(s) to delete (name)"),
147
149
  )
148
150
  return parser
149
151
 
@@ -151,9 +153,9 @@ class DeleteMetadefNameSpace(command.Command):
151
153
  image_client = self.app.client_manager.image
152
154
 
153
155
  result = 0
154
- for i in parsed_args.namespace_name:
156
+ for ns in parsed_args.namespace:
155
157
  try:
156
- namespace = image_client.get_metadef_namespace(i)
158
+ namespace = image_client.get_metadef_namespace(ns)
157
159
  image_client.delete_metadef_namespace(namespace.id)
158
160
  except Exception as e:
159
161
  result += 1
@@ -162,18 +164,18 @@ class DeleteMetadefNameSpace(command.Command):
162
164
  "Failed to delete namespace with name or "
163
165
  "ID '%(namespace)s': %(e)s"
164
166
  ),
165
- {'namespace': i, 'e': e},
167
+ {'namespace': ns, 'e': e},
166
168
  )
167
169
 
168
170
  if result > 0:
169
- total = len(parsed_args.namespace_name)
171
+ total = len(parsed_args.namespace)
170
172
  msg = _(
171
173
  "%(result)s of %(total)s namespace failed " "to delete."
172
174
  ) % {'result': result, 'total': total}
173
175
  raise exceptions.CommandError(msg)
174
176
 
175
177
 
176
- class ListMetadefNameSpaces(command.Lister):
178
+ class ListMetadefNamespace(command.Lister):
177
179
  _description = _("List metadef namespaces")
178
180
 
179
181
  def get_parser(self, prog_name):
@@ -215,7 +217,7 @@ class ListMetadefNameSpaces(command.Lister):
215
217
  )
216
218
 
217
219
 
218
- class SetMetadefNameSpace(command.Command):
220
+ class SetMetadefNamespace(command.Command):
219
221
  _description = _("Set metadef namespace properties")
220
222
 
221
223
  def get_parser(self, prog_name):
@@ -223,7 +225,7 @@ class SetMetadefNameSpace(command.Command):
223
225
  parser.add_argument(
224
226
  "namespace",
225
227
  metavar="<namespace>",
226
- help=_("Namespace (name) for the namespace"),
228
+ help=_("Metadef namespace to modify (name)"),
227
229
  )
228
230
  parser.add_argument(
229
231
  "--display-name",
@@ -241,14 +243,16 @@ class SetMetadefNameSpace(command.Command):
241
243
  action="store_const",
242
244
  const="public",
243
245
  dest="visibility",
244
- help=_("Set namespace visibility 'public'"),
246
+ help=_("Metadef namespace is accessible to the public"),
245
247
  )
246
248
  visibility_group.add_argument(
247
249
  "--private",
248
250
  action="store_const",
249
251
  const="private",
250
252
  dest="visibility",
251
- help=_("Set namespace visibility 'private'"),
253
+ help=_(
254
+ "Metadef namespace is inaccessible to the public (default)"
255
+ ),
252
256
  )
253
257
  protected_group = parser.add_mutually_exclusive_group()
254
258
  protected_group.add_argument(
@@ -289,24 +293,24 @@ class SetMetadefNameSpace(command.Command):
289
293
  image_client.update_metadef_namespace(namespace, **kwargs)
290
294
 
291
295
 
292
- class ShowMetadefNameSpace(command.ShowOne):
296
+ class ShowMetadefNamespace(command.ShowOne):
293
297
  _description = _("Show a metadef namespace")
294
298
 
295
299
  def get_parser(self, prog_name):
296
300
  parser = super().get_parser(prog_name)
297
301
  parser.add_argument(
298
- "namespace_name",
299
- metavar="<namespace_name>",
300
- help=_("Namespace (name) for the namespace"),
302
+ "namespace",
303
+ metavar="<namespace>",
304
+ help=_("Metadef namespace to show (name)"),
301
305
  )
302
306
  return parser
303
307
 
304
308
  def take_action(self, parsed_args):
305
309
  image_client = self.app.client_manager.image
306
310
 
307
- namespace_name = parsed_args.namespace_name
311
+ namespace = parsed_args.namespace
308
312
 
309
- data = image_client.get_metadef_namespace(namespace_name)
313
+ data = image_client.get_metadef_namespace(namespace)
310
314
  info = _format_namespace(data)
311
315
 
312
316
  return zip(*sorted(info.items()))
@@ -0,0 +1,189 @@
1
+ # Copyright 2023 Red Hat
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License"); you may
4
+ # not use this file except in compliance with the License. You may obtain
5
+ # a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11
+ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12
+ # License for the specific language governing permissions and limitations
13
+ # under the License.
14
+ #
15
+
16
+ """Image V2 Action Implementations"""
17
+
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_object(md_object):
31
+ fields_to_show = (
32
+ 'created_at',
33
+ 'description',
34
+ 'name',
35
+ 'namespace_name',
36
+ 'properties',
37
+ 'required',
38
+ 'updated_at',
39
+ )
40
+
41
+ return (
42
+ fields_to_show,
43
+ utils.get_item_properties(
44
+ md_object,
45
+ fields_to_show,
46
+ ),
47
+ )
48
+
49
+
50
+ class CreateMetadefObjects(command.ShowOne):
51
+ _description = _("Create a metadef object")
52
+
53
+ def get_parser(self, prog_name):
54
+ parser = super().get_parser(prog_name)
55
+ parser.add_argument(
56
+ "--namespace",
57
+ metavar="<namespace>",
58
+ help=_("Metadef namespace to create the object in (name)"),
59
+ )
60
+ parser.add_argument(
61
+ "name",
62
+ metavar='<metadef-object-name>',
63
+ help=_('New metadef object name'),
64
+ )
65
+ return parser
66
+
67
+ def take_action(self, parsed_args):
68
+ image_client = self.app.client_manager.image
69
+
70
+ namespace = image_client.get_metadef_namespace(
71
+ parsed_args.namespace,
72
+ )
73
+ data = image_client.create_metadef_object(
74
+ namespace=namespace.namespace,
75
+ name=parsed_args.name,
76
+ )
77
+
78
+ fields, value = _format_object(data)
79
+
80
+ return fields, value
81
+
82
+
83
+ class ShowMetadefObjects(command.ShowOne):
84
+ _description = _("Show a particular metadef object")
85
+
86
+ def get_parser(self, prog_name):
87
+ parser = super().get_parser(prog_name)
88
+ parser.add_argument(
89
+ "namespace",
90
+ metavar="<namespace>",
91
+ help=_("Metadef namespace of the object (name)"),
92
+ )
93
+ parser.add_argument(
94
+ "object",
95
+ metavar="<object>",
96
+ help=_("Metadef object to show"),
97
+ )
98
+ return parser
99
+
100
+ def take_action(self, parsed_args):
101
+ image_client = self.app.client_manager.image
102
+
103
+ namespace = parsed_args.namespace
104
+ object = parsed_args.object
105
+
106
+ data = image_client.get_metadef_object(object, namespace)
107
+
108
+ fields, value = _format_object(data)
109
+
110
+ return fields, value
111
+
112
+
113
+ class DeleteMetadefObject(command.Command):
114
+ _description = _("Delete metadata definitions object(s)")
115
+
116
+ def get_parser(self, prog_name):
117
+ parser = super().get_parser(prog_name)
118
+ parser.add_argument(
119
+ "namespace",
120
+ metavar="<namespace>",
121
+ help=_("Metadef namespace of the object (name)"),
122
+ )
123
+ parser.add_argument(
124
+ "objects",
125
+ metavar="<object>",
126
+ nargs="+",
127
+ help=_("Metadef object(s) to delete (name)"),
128
+ )
129
+ return parser
130
+
131
+ def take_action(self, parsed_args):
132
+ image_client = self.app.client_manager.image
133
+
134
+ namespace = parsed_args.namespace
135
+
136
+ result = 0
137
+ for obj in parsed_args.objects:
138
+ try:
139
+ object = image_client.get_metadef_object(obj, namespace)
140
+ image_client.delete_metadef_object(object, namespace)
141
+ except Exception as e:
142
+ result += 1
143
+ LOG.error(
144
+ _(
145
+ "Failed to delete object with name or "
146
+ "ID '%(object)s': %(e)s"
147
+ ),
148
+ {'object': obj, 'e': e},
149
+ )
150
+
151
+ if result > 0:
152
+ total = len(parsed_args.namespace)
153
+ msg = _("%(result)s of %(total)s object failed to delete.") % {
154
+ 'result': result,
155
+ 'total': total,
156
+ }
157
+ raise exceptions.CommandError(msg)
158
+
159
+
160
+ class ListMetadefObjects(command.Lister):
161
+ _description = _("List metadef objects inside a specific namespace.")
162
+
163
+ def get_parser(self, prog_name):
164
+ parser = super().get_parser(prog_name)
165
+ parser.add_argument(
166
+ "namespace",
167
+ metavar="<namespace>",
168
+ help=_("Namespace (name) for the namespace"),
169
+ )
170
+ return parser
171
+
172
+ def take_action(self, parsed_args):
173
+ image_client = self.app.client_manager.image
174
+
175
+ namespace = parsed_args.namespace
176
+ columns = ['name', 'description']
177
+
178
+ md_objects = list(image_client.metadef_objects(namespace))
179
+ column_headers = columns
180
+ return (
181
+ column_headers,
182
+ (
183
+ utils.get_item_properties(
184
+ md_object,
185
+ columns,
186
+ )
187
+ for md_object in md_objects
188
+ ),
189
+ )
@@ -0,0 +1,284 @@
1
+ # Copyright 2023 Red Hat, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License"); you may
4
+ # not use this file except in compliance with the License. You may obtain
5
+ # a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11
+ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12
+ # License for the specific language governing permissions and limitations
13
+ # under the License.
14
+
15
+ import json
16
+ import logging
17
+
18
+ from osc_lib.command import command
19
+ from osc_lib import exceptions
20
+ from osc_lib import utils
21
+
22
+ from openstackclient.i18n import _
23
+
24
+
25
+ LOG = logging.getLogger(__name__)
26
+
27
+
28
+ def _format_property(prop):
29
+ prop = prop.to_dict(ignore_none=True, original_names=True)
30
+ return {
31
+ key: prop[key]
32
+ for key in [
33
+ 'namespace_name',
34
+ 'name',
35
+ 'type',
36
+ 'title',
37
+ 'description',
38
+ 'operators',
39
+ 'default',
40
+ 'is_readonly',
41
+ 'minimum',
42
+ 'maximum',
43
+ 'enum',
44
+ 'pattern',
45
+ 'min_length',
46
+ 'max_length',
47
+ 'items',
48
+ 'require_unique_items',
49
+ 'min_items',
50
+ 'max_items',
51
+ 'allow_additional_items',
52
+ ]
53
+ if key in prop
54
+ }
55
+
56
+
57
+ class CreateMetadefProperty(command.ShowOne):
58
+ _description = _("Create a metadef property")
59
+
60
+ def get_parser(self, prog_name):
61
+ parser = super().get_parser(prog_name)
62
+ parser.add_argument(
63
+ "--name",
64
+ required=True,
65
+ help=_("Internal name of the property"),
66
+ )
67
+ parser.add_argument(
68
+ "--title",
69
+ required=True,
70
+ help=_("Property name displayed to the user"),
71
+ )
72
+ parser.add_argument(
73
+ "--type",
74
+ required=True,
75
+ help=_("Property type"),
76
+ )
77
+ parser.add_argument(
78
+ "--schema",
79
+ required=True,
80
+ help=_("Valid JSON schema of the property"),
81
+ )
82
+ parser.add_argument(
83
+ "namespace",
84
+ help=_("Name of namespace the property will belong."),
85
+ )
86
+ return parser
87
+
88
+ def take_action(self, parsed_args):
89
+ image_client = self.app.client_manager.image
90
+
91
+ kwargs = {
92
+ 'name': parsed_args.name,
93
+ 'title': parsed_args.title,
94
+ 'type': parsed_args.type,
95
+ }
96
+ try:
97
+ kwargs.update(json.loads(parsed_args.schema))
98
+ except json.JSONDecodeError as e:
99
+ raise exceptions.CommandError(
100
+ _("Failed to load JSON schema: %(e)s")
101
+ % {
102
+ 'e': e,
103
+ }
104
+ )
105
+
106
+ data = image_client.create_metadef_property(
107
+ parsed_args.namespace, **kwargs
108
+ )
109
+ info = _format_property(data)
110
+
111
+ return zip(*sorted(info.items()))
112
+
113
+
114
+ class DeleteMetadefProperty(command.Command):
115
+ _description = _("Delete metadef propert(ies)")
116
+
117
+ def get_parser(self, prog_name):
118
+ parser = super().get_parser(prog_name)
119
+ parser.add_argument(
120
+ "namespace",
121
+ metavar="<namespace>",
122
+ help=_("Metadef namespace of the property (name)"),
123
+ )
124
+ parser.add_argument(
125
+ "properties",
126
+ metavar="<property>",
127
+ nargs="+",
128
+ help=_("Metadef propert(ies) to delete (name)"),
129
+ )
130
+ return parser
131
+
132
+ def take_action(self, parsed_args):
133
+ image_client = self.app.client_manager.image
134
+
135
+ result = 0
136
+ for prop in parsed_args.properties:
137
+ try:
138
+ image_client.delete_metadef_property(
139
+ prop,
140
+ parsed_args.namespace,
141
+ ignore_missing=False,
142
+ )
143
+ except Exception as e:
144
+ result += 1
145
+ LOG.error(
146
+ _(
147
+ "Failed to delete property with name or ID "
148
+ "'%(property)s' from namespace '%(namespace)s': %(e)s"
149
+ ),
150
+ {
151
+ 'property': prop,
152
+ 'namespace': parsed_args.namespace,
153
+ 'e': e,
154
+ },
155
+ )
156
+
157
+ if result > 0:
158
+ total = len(parsed_args.namespace)
159
+ msg = _("%(result)s of %(total)s properties failed to delete.") % {
160
+ 'result': result,
161
+ 'total': total,
162
+ }
163
+ raise exceptions.CommandError(msg)
164
+
165
+
166
+ class ListMetadefProperties(command.Lister):
167
+ _description = _("List metadef properties")
168
+
169
+ def get_parser(self, prog_name):
170
+ parser = super().get_parser(prog_name)
171
+ parser.add_argument(
172
+ "namespace",
173
+ metavar="<namespace>",
174
+ help=_("An identifier (a name) for the namespace"),
175
+ )
176
+ return parser
177
+
178
+ def take_action(self, parsed_args):
179
+ image_client = self.app.client_manager.image
180
+ props = image_client.metadef_properties(parsed_args.namespace)
181
+ columns = ['name', 'title', 'type']
182
+ return (
183
+ columns,
184
+ (
185
+ utils.get_item_properties(
186
+ prop,
187
+ columns,
188
+ )
189
+ for prop in props
190
+ ),
191
+ )
192
+
193
+
194
+ class SetMetadefProperty(command.Command):
195
+ _description = _("Update metadef namespace property")
196
+
197
+ def get_parser(self, prog_name):
198
+ parser = super().get_parser(prog_name)
199
+ parser.add_argument(
200
+ "--name",
201
+ help=_("Internal name of the property"),
202
+ )
203
+ parser.add_argument(
204
+ "--title",
205
+ help=_("Property name displayed to the user"),
206
+ )
207
+ parser.add_argument(
208
+ "--type",
209
+ help=_("Property type"),
210
+ )
211
+ parser.add_argument(
212
+ "--schema",
213
+ help=_("Valid JSON schema of the property"),
214
+ )
215
+ parser.add_argument(
216
+ "namespace",
217
+ help=_("Namespace of the namespace to which the property belongs"),
218
+ )
219
+ parser.add_argument(
220
+ "property",
221
+ help=_("Property to update"),
222
+ )
223
+ return parser
224
+
225
+ def take_action(self, parsed_args):
226
+ image_client = self.app.client_manager.image
227
+
228
+ # We need to pass the values for *all* attributes as kwargs to
229
+ # update_metadef_property(), otherwise the attributes that are not
230
+ # listed will be reset.
231
+ data = image_client.get_metadef_property(
232
+ parsed_args.property,
233
+ parsed_args.namespace,
234
+ )
235
+ kwargs = _format_property(data)
236
+ for key in ['name', 'title', 'type']:
237
+ argument = getattr(parsed_args, key, None)
238
+ if argument is not None:
239
+ kwargs[key] = argument
240
+
241
+ if parsed_args.schema:
242
+ try:
243
+ kwargs.update(json.loads(parsed_args.schema))
244
+ except json.JSONDecodeError as e:
245
+ raise exceptions.CommandError(
246
+ _("Failed to load JSON schema: %(e)s")
247
+ % {
248
+ 'e': e,
249
+ }
250
+ )
251
+
252
+ image_client.update_metadef_property(
253
+ parsed_args.property,
254
+ parsed_args.namespace,
255
+ **kwargs,
256
+ )
257
+
258
+
259
+ class ShowMetadefProperty(command.ShowOne):
260
+ _description = _("Show a particular metadef property")
261
+
262
+ def get_parser(self, prog_name):
263
+ parser = super().get_parser(prog_name)
264
+ parser.add_argument(
265
+ "namespace",
266
+ metavar="<namespace>",
267
+ help=_("Metadef namespace of the property (name)"),
268
+ )
269
+ parser.add_argument(
270
+ "property",
271
+ metavar="<property>",
272
+ help=_("Property to show"),
273
+ )
274
+ return parser
275
+
276
+ def take_action(self, parsed_args):
277
+ image_client = self.app.client_manager.image
278
+ data = image_client.get_metadef_property(
279
+ parsed_args.property,
280
+ parsed_args.namespace,
281
+ )
282
+ info = _format_property(data)
283
+
284
+ return zip(*sorted(info.items()))