Merge "Add image metadef namespace command"

This commit is contained in:
Zuul 2022-12-15 17:45:31 +00:00 committed by Gerrit Code Review
commit db6909bc63
5 changed files with 418 additions and 3 deletions

View File

@ -15,8 +15,11 @@
"""Image V2 Action Implementations"""
import logging
from osc_lib.cli import format_columns
from osc_lib.command import command
from osc_lib import exceptions
from osc_lib import utils
from openstackclient.i18n import _
@ -25,6 +28,149 @@ _formatters = {
'tags': format_columns.ListColumn,
}
LOG = logging.getLogger(__name__)
def _format_namespace(namespace):
info = {}
fields_to_show = [
'created_at',
'description',
'display_name',
'namespace',
'owner',
'protected',
'schema',
'visibility',
]
namespace = namespace.to_dict(ignore_none=True, original_names=True)
# split out the usual key and the properties which are top-level
for key in namespace:
if key in fields_to_show:
info[key] = namespace.get(key)
elif key == "resource_type_associations":
info[key] = [resource_type['name']
for resource_type in namespace.get(key)]
elif key == 'properties':
info['properties'] = list(namespace.get(key).keys())
return info
class CreateMetadefNameSpace(command.ShowOne):
_description = _("Create a metadef namespace")
def get_parser(self, prog_name):
parser = super().get_parser(prog_name)
parser.add_argument(
"namespace",
metavar="<namespace>",
help=_("New metadef namespace name"),
)
parser.add_argument(
"--display-name",
metavar="<display_name>",
help=_("A user-friendly name for the namespace."),
)
parser.add_argument(
"--description",
metavar="<description>",
help=_("A description of the namespace"),
)
visibility_group = parser.add_mutually_exclusive_group()
visibility_group.add_argument(
"--public",
action="store_const",
const="public",
dest="visibility",
help=_("Set namespace visibility 'public'"),
)
visibility_group.add_argument(
"--private",
action="store_const",
const="private",
dest="visibility",
help=_("Set namespace visibility 'private'"),
)
protected_group = parser.add_mutually_exclusive_group()
protected_group.add_argument(
"--protected",
action="store_const",
const=True,
dest="is_protected",
help=_("Prevent metadef namespace from being deleted"),
)
protected_group.add_argument(
"--unprotected",
action="store_const",
const=False,
dest="is_protected",
help=_("Allow metadef namespace to be deleted (default)"),
)
return parser
def take_action(self, parsed_args):
image_client = self.app.client_manager.image
filter_keys = [
'namespace',
'display_name',
'description'
]
kwargs = {}
for key in filter_keys:
argument = getattr(parsed_args, key, None)
if argument is not None:
kwargs[key] = argument
if parsed_args.is_protected is not None:
kwargs['protected'] = parsed_args.is_protected
if parsed_args.visibility is not None:
kwargs['visibility'] = parsed_args.visibility
data = image_client.create_metadef_namespace(**kwargs)
return zip(*sorted(data.items()))
class DeleteMetadefNameSpace(command.Command):
_description = _("Delete metadef namespace")
def get_parser(self, prog_name):
parser = super().get_parser(prog_name)
parser.add_argument(
"namespace_name",
metavar="<namespace_name>",
nargs="+",
help=_("An identifier (a name) for the namespace"),
)
return parser
def take_action(self, parsed_args):
image_client = self.app.client_manager.image
result = 0
for i in parsed_args.namespace_name:
try:
namespace = image_client.get_metadef_namespace(i)
image_client.delete_metadef_namespace(namespace.id)
except Exception as e:
result += 1
LOG.error(_("Failed to delete namespace with name or "
"ID '%(namespace)s': %(e)s"),
{'namespace': i, 'e': e}
)
if result > 0:
total = len(parsed_args.namespace_name)
msg = (_("%(result)s of %(total)s namespace failed "
"to delete.") % {'result': result, 'total': total})
raise exceptions.CommandError(msg)
class ListMetadefNameSpaces(command.Lister):
_description = _("List metadef namespaces")
@ -63,3 +209,104 @@ class ListMetadefNameSpaces(command.Lister):
formatters=_formatters,
) for s in data)
)
class SetMetadefNameSpace(command.Command):
_description = _("Set metadef namespace properties")
def get_parser(self, prog_name):
parser = super().get_parser(prog_name)
parser.add_argument(
"namespace",
metavar="<namespace>",
help=_("Namespace (name) for the namespace"),
)
parser.add_argument(
"--display-name",
metavar="<display_name>",
help=_("Set a user-friendly name for the namespace."),
)
parser.add_argument(
"--description",
metavar="<description>",
help=_("Set the description of the namespace"),
)
visibility_group = parser.add_mutually_exclusive_group()
visibility_group.add_argument(
"--public",
action="store_const",
const="public",
dest="visibility",
help=_("Set namespace visibility 'public'"),
)
visibility_group.add_argument(
"--private",
action="store_const",
const="private",
dest="visibility",
help=_("Set namespace visibility 'private'"),
)
protected_group = parser.add_mutually_exclusive_group()
protected_group.add_argument(
"--protected",
action="store_const",
const=True,
dest="is_protected",
help=_("Prevent metadef namespace from being deleted"),
)
protected_group.add_argument(
"--unprotected",
action="store_const",
const=False,
dest="is_protected",
help=_("Allow metadef namespace to be deleted (default)"),
)
return parser
def take_action(self, parsed_args):
image_client = self.app.client_manager.image
namespace = parsed_args.namespace
filter_keys = [
'namespace',
'display_name',
'description'
]
kwargs = {}
for key in filter_keys:
argument = getattr(parsed_args, key, None)
if argument is not None:
kwargs[key] = argument
if parsed_args.is_protected is not None:
kwargs['protected'] = parsed_args.is_protected
if parsed_args.visibility is not None:
kwargs['visibility'] = parsed_args.visibility
image_client.update_metadef_namespace(namespace, **kwargs)
class ShowMetadefNameSpace(command.ShowOne):
_description = _("Show a metadef namespace")
def get_parser(self, prog_name):
parser = super().get_parser(prog_name)
parser.add_argument(
"namespace_name",
metavar="<namespace_name>",
help=_("Namespace (name) for the namespace"),
)
return parser
def take_action(self, parsed_args):
image_client = self.app.client_manager.image
namespace_name = parsed_args.namespace_name
data = image_client.get_metadef_namespace(namespace_name)
info = _format_namespace(data)
return zip(*sorted(info.items()))

View File

@ -239,7 +239,11 @@ def create_tasks(attrs=None, count=2):
class FakeMetadefNamespaceClient:
def __init__(self, **kwargs):
self.create_metadef_namespace = mock.Mock()
self.delete_metadef_namespace = mock.Mock()
self.metadef_namespaces = mock.Mock()
self.get_metadef_namespace = mock.Mock()
self.update_metadef_namespace = mock.Mock()
self.auth_token = kwargs['token']
self.management_url = kwargs['endpoint']
@ -277,10 +281,11 @@ def create_one_metadef_namespace(attrs=None):
'display_name': 'Flavor Quota',
'namespace': 'OS::Compute::Quota',
'owner': 'admin',
'resource_type_associations': ['OS::Nova::Flavor'],
# 'resource_type_associations': ['OS::Nova::Flavor'],
# The part that receives the list type factor is not implemented.
'visibility': 'public',
}
# Overwrite default attributes if there are some attributes set
metadef_namespace_list.update(attrs)
return metadef_namespace.MetadefNamespace(metadef_namespace_list)
return metadef_namespace.MetadefNamespace(**metadef_namespace_list)

View File

@ -30,8 +30,89 @@ class TestMetadefNamespaces(md_namespace_fakes.TestMetadefNamespaces):
self.domain_mock.reset_mock()
class TestMetadefNamespaceList(TestMetadefNamespaces):
class TestMetadefNamespaceCreate(TestMetadefNamespaces):
_metadef_namespace = md_namespace_fakes.create_one_metadef_namespace()
expected_columns = (
'created_at',
'description',
'display_name',
'id',
'is_protected',
'location',
'name',
'namespace',
'owner',
'resource_type_associations',
'updated_at',
'visibility'
)
expected_data = (
_metadef_namespace.created_at,
_metadef_namespace.description,
_metadef_namespace.display_name,
_metadef_namespace.id,
_metadef_namespace.is_protected,
_metadef_namespace.location,
_metadef_namespace.name,
_metadef_namespace.namespace,
_metadef_namespace.owner,
_metadef_namespace.resource_type_associations,
_metadef_namespace.updated_at,
_metadef_namespace.visibility
)
def setUp(self):
super().setUp()
self.client.create_metadef_namespace.return_value \
= self._metadef_namespace
self.cmd = metadef_namespaces.CreateMetadefNameSpace(self.app, None)
self.datalist = self._metadef_namespace
def test_namespace_create(self):
arglist = [
self._metadef_namespace.namespace
]
verifylist = [
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
self.assertEqual(self.expected_columns, columns)
self.assertEqual(self.expected_data, data)
class TestMetadefNamespaceDelete(TestMetadefNamespaces):
_metadef_namespace = md_namespace_fakes.create_one_metadef_namespace()
def setUp(self):
super().setUp()
self.client.delete_metadef_namespace.return_value \
= self._metadef_namespace
self.cmd = metadef_namespaces.DeleteMetadefNameSpace(self.app, None)
self.datalist = self._metadef_namespace
def test_namespace_create(self):
arglist = [
self._metadef_namespace.namespace
]
verifylist = [
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
result = self.cmd.take_action(parsed_args)
self.assertIsNone(result)
class TestMetadefNamespaceList(TestMetadefNamespaces):
_metadef_namespace = [md_namespace_fakes.create_one_metadef_namespace()]
columns = [
@ -65,3 +146,70 @@ class TestMetadefNamespaceList(TestMetadefNamespaces):
self.assertEqual(self.columns, columns)
self.assertEqual(getattr(self.datalist[0], 'namespace'),
next(data)[0])
class TestMetadefNamespaceSet(TestMetadefNamespaces):
_metadef_namespace = md_namespace_fakes.create_one_metadef_namespace()
def setUp(self):
super().setUp()
self.client.update_metadef_namespace.return_value \
= self._metadef_namespace
self.cmd = metadef_namespaces.SetMetadefNameSpace(self.app, None)
self.datalist = self._metadef_namespace
def test_namespace_set_no_options(self):
arglist = [
self._metadef_namespace.namespace
]
verifylist = [
('namespace', self._metadef_namespace.namespace),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
result = self.cmd.take_action(parsed_args)
self.assertIsNone(result)
class TestMetadefNamespaceShow(TestMetadefNamespaces):
_metadef_namespace = md_namespace_fakes.create_one_metadef_namespace()
expected_columns = (
'created_at',
'display_name',
'namespace',
'owner',
'visibility'
)
expected_data = (
_metadef_namespace.created_at,
_metadef_namespace.display_name,
_metadef_namespace.namespace,
_metadef_namespace.owner,
_metadef_namespace.visibility
)
def setUp(self):
super().setUp()
self.client.get_metadef_namespace.return_value \
= self._metadef_namespace
self.cmd = metadef_namespaces.ShowMetadefNameSpace(self.app, None)
def test_namespace_show_no_options(self):
arglist = [
self._metadef_namespace.namespace
]
verifylist = [
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
self.assertEqual(self.expected_columns, columns)
self.assertEqual(self.expected_data, data)

View File

@ -0,0 +1,10 @@
---
features:
- Add ``openstack image metadef namespace create`` command
to create metadef namespace for the image service.
- Add ``openstack image metadef namespace delete`` command
to delete image metadef namespace.
- Add ``openstack image metadef namespace set`` command
to update metadef namespace for the image service.
- Add ``openstack image metadef namespace show`` command
to show metadef namespace for the image service.

View File

@ -386,7 +386,12 @@ openstack.image.v2 =
image_stage = openstackclient.image.v2.image:StageImage
image_task_show = openstackclient.image.v2.task:ShowTask
image_task_list = openstackclient.image.v2.task:ListTask
image_metadef_namespace_create = openstackclient.image.v2.metadef_namespaces:CreateMetadefNameSpace
image_metadef_namespace_delete = openstackclient.image.v2.metadef_namespaces:DeleteMetadefNameSpace
image_metadef_namespace_list = openstackclient.image.v2.metadef_namespaces:ListMetadefNameSpaces
image_metadef_namespace_set = openstackclient.image.v2.metadef_namespaces:SetMetadefNameSpace
image_metadef_namespace_show = openstackclient.image.v2.metadef_namespaces:ShowMetadefNameSpace
openstack.network.v2 =
address_group_create = openstackclient.network.v2.address_group:CreateAddressGroup