Skip to content

Commit 38f972f

Browse files
committed
Add image metadef namespace command
add image metadef namespace create, delete, set, show command Change-Id: I35b1cb8ff2b1735ae3a1b405d8f2071fe449ea6e
1 parent 96162c2 commit 38f972f

5 files changed

Lines changed: 418 additions & 3 deletions

File tree

openstackclient/image/v2/metadef_namespaces.py

Lines changed: 247 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,11 @@
1515

1616
"""Image V2 Action Implementations"""
1717

18+
import logging
19+
1820
from osc_lib.cli import format_columns
1921
from osc_lib.command import command
22+
from osc_lib import exceptions
2023
from osc_lib import utils
2124

2225
from openstackclient.i18n import _
@@ -25,6 +28,149 @@
2528
'tags': format_columns.ListColumn,
2629
}
2730

31+
LOG = logging.getLogger(__name__)
32+
33+
34+
def _format_namespace(namespace):
35+
info = {}
36+
37+
fields_to_show = [
38+
'created_at',
39+
'description',
40+
'display_name',
41+
'namespace',
42+
'owner',
43+
'protected',
44+
'schema',
45+
'visibility',
46+
]
47+
48+
namespace = namespace.to_dict(ignore_none=True, original_names=True)
49+
50+
# split out the usual key and the properties which are top-level
51+
for key in namespace:
52+
if key in fields_to_show:
53+
info[key] = namespace.get(key)
54+
elif key == "resource_type_associations":
55+
info[key] = [resource_type['name']
56+
for resource_type in namespace.get(key)]
57+
elif key == 'properties':
58+
info['properties'] = list(namespace.get(key).keys())
59+
60+
return info
61+
62+
63+
class CreateMetadefNameSpace(command.ShowOne):
64+
_description = _("Create a metadef namespace")
65+
66+
def get_parser(self, prog_name):
67+
parser = super().get_parser(prog_name)
68+
parser.add_argument(
69+
"namespace",
70+
metavar="<namespace>",
71+
help=_("New metadef namespace name"),
72+
)
73+
parser.add_argument(
74+
"--display-name",
75+
metavar="<display_name>",
76+
help=_("A user-friendly name for the namespace."),
77+
)
78+
parser.add_argument(
79+
"--description",
80+
metavar="<description>",
81+
help=_("A description of the namespace"),
82+
)
83+
visibility_group = parser.add_mutually_exclusive_group()
84+
visibility_group.add_argument(
85+
"--public",
86+
action="store_const",
87+
const="public",
88+
dest="visibility",
89+
help=_("Set namespace visibility 'public'"),
90+
)
91+
visibility_group.add_argument(
92+
"--private",
93+
action="store_const",
94+
const="private",
95+
dest="visibility",
96+
help=_("Set namespace visibility 'private'"),
97+
)
98+
protected_group = parser.add_mutually_exclusive_group()
99+
protected_group.add_argument(
100+
"--protected",
101+
action="store_const",
102+
const=True,
103+
dest="is_protected",
104+
help=_("Prevent metadef namespace from being deleted"),
105+
)
106+
protected_group.add_argument(
107+
"--unprotected",
108+
action="store_const",
109+
const=False,
110+
dest="is_protected",
111+
help=_("Allow metadef namespace to be deleted (default)"),
112+
)
113+
return parser
114+
115+
def take_action(self, parsed_args):
116+
image_client = self.app.client_manager.image
117+
filter_keys = [
118+
'namespace',
119+
'display_name',
120+
'description'
121+
]
122+
kwargs = {}
123+
124+
for key in filter_keys:
125+
argument = getattr(parsed_args, key, None)
126+
if argument is not None:
127+
kwargs[key] = argument
128+
129+
if parsed_args.is_protected is not None:
130+
kwargs['protected'] = parsed_args.is_protected
131+
132+
if parsed_args.visibility is not None:
133+
kwargs['visibility'] = parsed_args.visibility
134+
135+
data = image_client.create_metadef_namespace(**kwargs)
136+
137+
return zip(*sorted(data.items()))
138+
139+
140+
class DeleteMetadefNameSpace(command.Command):
141+
_description = _("Delete metadef namespace")
142+
143+
def get_parser(self, prog_name):
144+
parser = super().get_parser(prog_name)
145+
parser.add_argument(
146+
"namespace_name",
147+
metavar="<namespace_name>",
148+
nargs="+",
149+
help=_("An identifier (a name) for the namespace"),
150+
)
151+
return parser
152+
153+
def take_action(self, parsed_args):
154+
image_client = self.app.client_manager.image
155+
156+
result = 0
157+
for i in parsed_args.namespace_name:
158+
try:
159+
namespace = image_client.get_metadef_namespace(i)
160+
image_client.delete_metadef_namespace(namespace.id)
161+
except Exception as e:
162+
result += 1
163+
LOG.error(_("Failed to delete namespace with name or "
164+
"ID '%(namespace)s': %(e)s"),
165+
{'namespace': i, 'e': e}
166+
)
167+
168+
if result > 0:
169+
total = len(parsed_args.namespace_name)
170+
msg = (_("%(result)s of %(total)s namespace failed "
171+
"to delete.") % {'result': result, 'total': total})
172+
raise exceptions.CommandError(msg)
173+
28174

29175
class ListMetadefNameSpaces(command.Lister):
30176
_description = _("List metadef namespaces")
@@ -63,3 +209,104 @@ def take_action(self, parsed_args):
63209
formatters=_formatters,
64210
) for s in data)
65211
)
212+
213+
214+
class SetMetadefNameSpace(command.Command):
215+
_description = _("Set metadef namespace properties")
216+
217+
def get_parser(self, prog_name):
218+
parser = super().get_parser(prog_name)
219+
parser.add_argument(
220+
"namespace",
221+
metavar="<namespace>",
222+
help=_("Namespace (name) for the namespace"),
223+
)
224+
parser.add_argument(
225+
"--display-name",
226+
metavar="<display_name>",
227+
help=_("Set a user-friendly name for the namespace."),
228+
)
229+
parser.add_argument(
230+
"--description",
231+
metavar="<description>",
232+
help=_("Set the description of the namespace"),
233+
)
234+
visibility_group = parser.add_mutually_exclusive_group()
235+
visibility_group.add_argument(
236+
"--public",
237+
action="store_const",
238+
const="public",
239+
dest="visibility",
240+
help=_("Set namespace visibility 'public'"),
241+
)
242+
visibility_group.add_argument(
243+
"--private",
244+
action="store_const",
245+
const="private",
246+
dest="visibility",
247+
help=_("Set namespace visibility 'private'"),
248+
)
249+
protected_group = parser.add_mutually_exclusive_group()
250+
protected_group.add_argument(
251+
"--protected",
252+
action="store_const",
253+
const=True,
254+
dest="is_protected",
255+
help=_("Prevent metadef namespace from being deleted"),
256+
)
257+
protected_group.add_argument(
258+
"--unprotected",
259+
action="store_const",
260+
const=False,
261+
dest="is_protected",
262+
help=_("Allow metadef namespace to be deleted (default)"),
263+
)
264+
return parser
265+
266+
def take_action(self, parsed_args):
267+
image_client = self.app.client_manager.image
268+
269+
namespace = parsed_args.namespace
270+
271+
filter_keys = [
272+
'namespace',
273+
'display_name',
274+
'description'
275+
]
276+
kwargs = {}
277+
278+
for key in filter_keys:
279+
argument = getattr(parsed_args, key, None)
280+
if argument is not None:
281+
kwargs[key] = argument
282+
283+
if parsed_args.is_protected is not None:
284+
kwargs['protected'] = parsed_args.is_protected
285+
286+
if parsed_args.visibility is not None:
287+
kwargs['visibility'] = parsed_args.visibility
288+
289+
image_client.update_metadef_namespace(namespace, **kwargs)
290+
291+
292+
class ShowMetadefNameSpace(command.ShowOne):
293+
_description = _("Show a metadef namespace")
294+
295+
def get_parser(self, prog_name):
296+
parser = super().get_parser(prog_name)
297+
parser.add_argument(
298+
"namespace_name",
299+
metavar="<namespace_name>",
300+
help=_("Namespace (name) for the namespace"),
301+
)
302+
return parser
303+
304+
def take_action(self, parsed_args):
305+
image_client = self.app.client_manager.image
306+
307+
namespace_name = parsed_args.namespace_name
308+
309+
data = image_client.get_metadef_namespace(namespace_name)
310+
info = _format_namespace(data)
311+
312+
return zip(*sorted(info.items()))

openstackclient/tests/unit/image/v2/fakes.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,11 @@ def create_tasks(attrs=None, count=2):
212212
class FakeMetadefNamespaceClient:
213213

214214
def __init__(self, **kwargs):
215+
self.create_metadef_namespace = mock.Mock()
216+
self.delete_metadef_namespace = mock.Mock()
215217
self.metadef_namespaces = mock.Mock()
218+
self.get_metadef_namespace = mock.Mock()
219+
self.update_metadef_namespace = mock.Mock()
216220

217221
self.auth_token = kwargs['token']
218222
self.management_url = kwargs['endpoint']
@@ -250,10 +254,11 @@ def create_one_metadef_namespace(attrs=None):
250254
'display_name': 'Flavor Quota',
251255
'namespace': 'OS::Compute::Quota',
252256
'owner': 'admin',
253-
'resource_type_associations': ['OS::Nova::Flavor'],
257+
# 'resource_type_associations': ['OS::Nova::Flavor'],
258+
# The part that receives the list type factor is not implemented.
254259
'visibility': 'public',
255260
}
256261

257262
# Overwrite default attributes if there are some attributes set
258263
metadef_namespace_list.update(attrs)
259-
return metadef_namespace.MetadefNamespace(metadef_namespace_list)
264+
return metadef_namespace.MetadefNamespace(**metadef_namespace_list)

0 commit comments

Comments
 (0)