diff --git a/openstackclient/image/v2/metadef_namespaces.py b/openstackclient/image/v2/metadef_namespaces.py new file mode 100644 index 0000000000..158fd94e3b --- /dev/null +++ b/openstackclient/image/v2/metadef_namespaces.py @@ -0,0 +1,65 @@ +# Copyright 2012-2013 OpenStack Foundation +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# + +"""Image V2 Action Implementations""" + +from osc_lib.cli import format_columns +from osc_lib.command import command +from osc_lib import utils + +from openstackclient.i18n import _ + +_formatters = { + 'tags': format_columns.ListColumn, +} + + +class ListMetadefNameSpaces(command.Lister): + _description = _("List metadef namespaces") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + parser.add_argument( + "--resource-types", + metavar="", + help=_("filter resource types"), + ) + parser.add_argument( + "--visibility", + metavar="", + help=_("filter on visibility"), + ) + return parser + + def take_action(self, parsed_args): + image_client = self.app.client_manager.image + filter_keys = ['resource_types', 'visibility'] + kwargs = {} + for key in filter_keys: + argument = getattr(parsed_args, key, None) + if argument is not None: + kwargs[key] = argument + # List of namespace data received + data = list(image_client.metadef_namespaces(**kwargs)) + columns = ['namespace'] + column_headers = columns + return ( + column_headers, + (utils.get_item_properties( + s, + columns, + formatters=_formatters, + ) for s in data) + ) diff --git a/openstackclient/tests/unit/image/v2/fakes.py b/openstackclient/tests/unit/image/v2/fakes.py index f20154502e..cf09df778a 100644 --- a/openstackclient/tests/unit/image/v2/fakes.py +++ b/openstackclient/tests/unit/image/v2/fakes.py @@ -18,6 +18,7 @@ import uuid from openstack.image.v2 import image from openstack.image.v2 import member +from openstack.image.v2 import metadef_namespace from openstack.image.v2 import task from openstackclient.tests.unit import fakes @@ -44,6 +45,7 @@ class FakeImagev2Client: self.update_member = mock.Mock() self.remove_tag = mock.Mock() + self.metadef_namespaces = mock.Mock() self.tasks = mock.Mock() self.get_task = mock.Mock() @@ -55,6 +57,8 @@ class FakeImagev2Client: self.tasks = mock.Mock() self.tasks.resource_class = fakes.FakeResource(None, {}) + self.metadef_namespaces = mock.Mock() + class TestImagev2(utils.TestCommand): @@ -202,3 +206,53 @@ def create_tasks(attrs=None, count=2): tasks.append(create_one_task(attrs)) return tasks + + +class FakeMetadefNamespaceClient: + + def __init__(self, **kwargs): + self.metadef_namespaces = mock.Mock() + + self.auth_token = kwargs['token'] + self.management_url = kwargs['endpoint'] + self.version = 2.0 + + +class TestMetadefNamespaces(utils.TestCommand): + + def setUp(self): + super().setUp() + + self.app.client_manager.image = FakeMetadefNamespaceClient( + endpoint=fakes.AUTH_URL, + token=fakes.AUTH_TOKEN, + ) + + self.app.client_manager.identity = identity_fakes.FakeIdentityv3Client( + endpoint=fakes.AUTH_URL, + token=fakes.AUTH_TOKEN, + ) + + +def create_one_metadef_namespace(attrs=None): + """Create a fake MetadefNamespace member. + + :param attrs: A dictionary with all attributes of metadef_namespace member + :type attrs: dict + :return: a list of MetadefNamespace objects + :rtype: list of `metadef_namespace.MetadefNamespace` + """ + attrs = attrs or {} + + metadef_namespace_list = { + 'created_at': '2022-08-17T11:30:22Z', + 'display_name': 'Flavor Quota', + 'namespace': 'OS::Compute::Quota', + 'owner': 'admin', + 'resource_type_associations': ['OS::Nova::Flavor'], + 'visibility': 'public', + } + + # Overwrite default attributes if there are some attributes set + metadef_namespace_list.update(attrs) + 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 new file mode 100644 index 0000000000..5eae289cdc --- /dev/null +++ b/openstackclient/tests/unit/image/v2/test_metadef_namespaces.py @@ -0,0 +1,67 @@ +# Copyright 2013 Nebula Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from openstackclient.image.v2 import metadef_namespaces +from openstackclient.tests.unit.image.v2 import fakes as md_namespace_fakes + + +class TestMetadefNamespaces(md_namespace_fakes.TestMetadefNamespaces): + def setUp(self): + super().setUp() + + # Get shortcuts to mocked image client + self.client = self.app.client_manager.image + + # Get shortcut to the Mocks in identity client + self.project_mock = self.app.client_manager.identity.projects + self.project_mock.reset_mock() + self.domain_mock = self.app.client_manager.identity.domains + self.domain_mock.reset_mock() + + +class TestMetadefNamespaceList(TestMetadefNamespaces): + + _metadef_namespace = [md_namespace_fakes.create_one_metadef_namespace()] + + columns = [ + 'namespace' + ] + + datalist = [] + + def setUp(self): + super().setUp() + + self.client.metadef_namespaces.side_effect = [ + self._metadef_namespace, []] + + # Get the command object to test + self.client.metadef_namespaces.return_value = iter( + self._metadef_namespace + ) + self.cmd = metadef_namespaces.ListMetadefNameSpaces(self.app, None) + self.datalist = self._metadef_namespace + + def test_namespace_list_no_options(self): + arglist = [] + parsed_args = self.check_parser(self.cmd, arglist, []) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + self.assertEqual(self.columns, columns) + self.assertEqual(getattr(self.datalist[0], 'namespace'), + next(data)[0]) diff --git a/releasenotes/notes/add-image-metadef-namespace-support-4ba37ec3a1a72185.yaml b/releasenotes/notes/add-image-metadef-namespace-support-4ba37ec3a1a72185.yaml new file mode 100644 index 0000000000..c8a53ad038 --- /dev/null +++ b/releasenotes/notes/add-image-metadef-namespace-support-4ba37ec3a1a72185.yaml @@ -0,0 +1,4 @@ +--- +features: + - Adds ``openstack image metadef namespace list``. + The output is equivalent to glance md-namespace-list. diff --git a/requirements.txt b/requirements.txt index d14c324610..c787ef7627 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,7 +6,7 @@ pbr!=2.1.0,>=2.0.0 # Apache-2.0 cliff>=3.5.0 # Apache-2.0 iso8601>=0.1.11 # MIT -openstacksdk>=0.61.0 # Apache-2.0 +openstacksdk>=0.102.0 # Apache-2.0 osc-lib>=2.3.0 # Apache-2.0 oslo.i18n>=3.15.3 # Apache-2.0 oslo.utils>=3.33.0 # Apache-2.0 diff --git a/setup.cfg b/setup.cfg index bd153d6ee3..f8d0dffce5 100644 --- a/setup.cfg +++ b/setup.cfg @@ -385,6 +385,7 @@ openstack.image.v2 = image_unset = openstackclient.image.v2.image:UnsetImage image_task_show = openstackclient.image.v2.task:ShowTask image_task_list = openstackclient.image.v2.task:ListTask + image_metadef_namespace_list = openstackclient.image.v2.metadef_namespaces:ListMetadefNameSpaces openstack.network.v2 = address_group_create = openstackclient.network.v2.address_group:CreateAddressGroup