Merge "Add image metadef namespace command"
This commit is contained in:
commit
db6909bc63
@ -15,8 +15,11 @@
|
|||||||
|
|
||||||
"""Image V2 Action Implementations"""
|
"""Image V2 Action Implementations"""
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
from osc_lib.cli import format_columns
|
from osc_lib.cli import format_columns
|
||||||
from osc_lib.command import command
|
from osc_lib.command import command
|
||||||
|
from osc_lib import exceptions
|
||||||
from osc_lib import utils
|
from osc_lib import utils
|
||||||
|
|
||||||
from openstackclient.i18n import _
|
from openstackclient.i18n import _
|
||||||
@ -25,6 +28,149 @@ _formatters = {
|
|||||||
'tags': format_columns.ListColumn,
|
'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):
|
class ListMetadefNameSpaces(command.Lister):
|
||||||
_description = _("List metadef namespaces")
|
_description = _("List metadef namespaces")
|
||||||
@ -63,3 +209,104 @@ class ListMetadefNameSpaces(command.Lister):
|
|||||||
formatters=_formatters,
|
formatters=_formatters,
|
||||||
) for s in data)
|
) 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:
|
class FakeMetadefNamespaceClient:
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
|
self.create_metadef_namespace = mock.Mock()
|
||||||
|
self.delete_metadef_namespace = mock.Mock()
|
||||||
self.metadef_namespaces = 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.auth_token = kwargs['token']
|
||||||
self.management_url = kwargs['endpoint']
|
self.management_url = kwargs['endpoint']
|
||||||
@ -277,10 +281,11 @@ def create_one_metadef_namespace(attrs=None):
|
|||||||
'display_name': 'Flavor Quota',
|
'display_name': 'Flavor Quota',
|
||||||
'namespace': 'OS::Compute::Quota',
|
'namespace': 'OS::Compute::Quota',
|
||||||
'owner': 'admin',
|
'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',
|
'visibility': 'public',
|
||||||
}
|
}
|
||||||
|
|
||||||
# Overwrite default attributes if there are some attributes set
|
# Overwrite default attributes if there are some attributes set
|
||||||
metadef_namespace_list.update(attrs)
|
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()
|
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()]
|
_metadef_namespace = [md_namespace_fakes.create_one_metadef_namespace()]
|
||||||
|
|
||||||
columns = [
|
columns = [
|
||||||
@ -65,3 +146,70 @@ class TestMetadefNamespaceList(TestMetadefNamespaces):
|
|||||||
self.assertEqual(self.columns, columns)
|
self.assertEqual(self.columns, columns)
|
||||||
self.assertEqual(getattr(self.datalist[0], 'namespace'),
|
self.assertEqual(getattr(self.datalist[0], 'namespace'),
|
||||||
next(data)[0])
|
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_stage = openstackclient.image.v2.image:StageImage
|
||||||
image_task_show = openstackclient.image.v2.task:ShowTask
|
image_task_show = openstackclient.image.v2.task:ShowTask
|
||||||
image_task_list = openstackclient.image.v2.task:ListTask
|
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_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 =
|
openstack.network.v2 =
|
||||||
address_group_create = openstackclient.network.v2.address_group:CreateAddressGroup
|
address_group_create = openstackclient.network.v2.address_group:CreateAddressGroup
|
||||||
|
Loading…
Reference in New Issue
Block a user