Merge "Add image metadef namespace command"
This commit is contained in:
commit
db6909bc63
@ -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()))
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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.
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user