summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZuul <zuul@review.opendev.org>2022-12-15 17:45:31 +0000
committerGerrit Code Review <review@openstack.org>2022-12-15 17:45:31 +0000
commitdb6909bc63f19d5b39e3ab789d09ac10d2b920b5 (patch)
tree287d26e4782a6f1fe19e36965ca3c88fd8202249
parenta103b6ca34b6bda8278c36fe5915309a33d316f5 (diff)
parent38f972fa637871de8ab2ad4a70780faef6833198 (diff)
downloadpython-openstackclient-db6909bc63f19d5b39e3ab789d09ac10d2b920b5.tar.gz
Merge "Add image metadef namespace command"
-rw-r--r--openstackclient/image/v2/metadef_namespaces.py247
-rw-r--r--openstackclient/tests/unit/image/v2/fakes.py9
-rw-r--r--openstackclient/tests/unit/image/v2/test_metadef_namespaces.py150
-rw-r--r--releasenotes/notes/image-metadef-namespace-b940206bece64f97.yaml10
-rw-r--r--setup.cfg5
5 files changed, 418 insertions, 3 deletions
diff --git a/openstackclient/image/v2/metadef_namespaces.py b/openstackclient/image/v2/metadef_namespaces.py
index 158fd94e..f09f2002 100644
--- a/openstackclient/image/v2/metadef_namespaces.py
+++ b/openstackclient/image/v2/metadef_namespaces.py
@@ -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()))
diff --git a/openstackclient/tests/unit/image/v2/fakes.py b/openstackclient/tests/unit/image/v2/fakes.py
index ded9ff31..8ddd9a09 100644
--- a/openstackclient/tests/unit/image/v2/fakes.py
+++ b/openstackclient/tests/unit/image/v2/fakes.py
@@ -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)
diff --git a/openstackclient/tests/unit/image/v2/test_metadef_namespaces.py b/openstackclient/tests/unit/image/v2/test_metadef_namespaces.py
index 5eae289c..7ed11838 100644
--- a/openstackclient/tests/unit/image/v2/test_metadef_namespaces.py
+++ b/openstackclient/tests/unit/image/v2/test_metadef_namespaces.py
@@ -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)
diff --git a/releasenotes/notes/image-metadef-namespace-b940206bece64f97.yaml b/releasenotes/notes/image-metadef-namespace-b940206bece64f97.yaml
new file mode 100644
index 00000000..361e57fe
--- /dev/null
+++ b/releasenotes/notes/image-metadef-namespace-b940206bece64f97.yaml
@@ -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.
diff --git a/setup.cfg b/setup.cfg
index fa3d30fe..42ce970b 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -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