From d9c4c43a409977ed593169011b103b618302cc08 Mon Sep 17 00:00:00 2001 From: Cyril Roelandt Date: Sun, 15 Oct 2023 20:21:38 +0000 Subject: [PATCH] Add "image metadef property create" command Change-Id: Icb4fab0aef13b28212771da3a3b7c4a0775bb38e --- doc/source/cli/data/glance.csv | 2 +- .../image/v2/metadef_properties.py | 116 ++++++++++++++---- .../unit/image/v2/test_metadef_properties.py | 69 +++++++++++ ...adef-property-create-c9a4ec2bced892af.yaml | 5 + setup.cfg | 1 + 5 files changed, 166 insertions(+), 27 deletions(-) create mode 100644 releasenotes/notes/add-metadef-property-create-c9a4ec2bced892af.yaml diff --git a/doc/source/cli/data/glance.csv b/doc/source/cli/data/glance.csv index 5cd78e5a5e..90f7aeb15f 100644 --- a/doc/source/cli/data/glance.csv +++ b/doc/source/cli/data/glance.csv @@ -38,7 +38,7 @@ md-object-list,,List metadata definitions objects inside a specific namespace. md-object-property-show,,Describe a specific metadata definitions property inside an object. md-object-show,,Describe a specific metadata definitions object inside a namespace. md-object-update,,Update metadata definitions object inside a namespace. -md-property-create,,Create a new metadata definitions property inside a namespace. +md-property-create,image metadef property create,Create a new metadata definitions property inside a namespace. md-property-delete,,Delete a specific metadata definitions property inside a namespace. md-property-list,image metadef property list,List metadata definitions properties inside a specific namespace. md-property-show,image metadef property show,Describe a specific metadata definitions property inside a namespace. diff --git a/openstackclient/image/v2/metadef_properties.py b/openstackclient/image/v2/metadef_properties.py index bf34964bbe..2de7ac00ba 100644 --- a/openstackclient/image/v2/metadef_properties.py +++ b/openstackclient/image/v2/metadef_properties.py @@ -12,12 +12,101 @@ # License for the specific language governing permissions and limitations # under the License. +import json + from osc_lib.command import command +from osc_lib import exceptions from osc_lib import utils from openstackclient.i18n import _ +def _format_property(prop): + prop = prop.to_dict(ignore_none=True, original_names=True) + return { + key: prop[key] + for key in [ + 'namespace_name', + 'name', + 'type', + 'title', + 'description', + 'operators', + 'default', + 'is_readonly', + 'minimum', + 'maximum', + 'enum', + 'pattern', + 'min_length', + 'max_length', + 'items', + 'require_unique_items', + 'min_items', + 'max_items', + 'allow_additional_items', + ] + if key in prop + } + + +class CreateMetadefProperty(command.ShowOne): + _description = _("Create a metadef property") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + parser.add_argument( + "--name", + required=True, + help=_("Internal name of the property"), + ) + parser.add_argument( + "--title", + required=True, + help=_("Property name displayed to the user"), + ) + parser.add_argument( + "--type", + required=True, + help=_("Property type"), + ) + parser.add_argument( + "--schema", + required=True, + help=_("Valid JSON schema of the property"), + ) + parser.add_argument( + "namespace_name", + help=_("Name of namespace the property will belong."), + ) + return parser + + def take_action(self, parsed_args): + image_client = self.app.client_manager.image + + kwargs = { + 'name': parsed_args.name, + 'title': parsed_args.title, + 'type': parsed_args.type, + } + try: + kwargs.update(json.loads(parsed_args.schema)) + except json.JSONDecodeError as e: + raise exceptions.CommandError( + _("Failed to load JSON schema: %(e)s") + % { + 'e': e, + } + ) + + data = image_client.create_metadef_property( + parsed_args.namespace_name, **kwargs + ) + info = _format_property(data) + + return zip(*sorted(info.items())) + + class ListMetadefProperties(command.Lister): _description = _("List metadef properties") @@ -68,31 +157,6 @@ class ShowMetadefProperty(command.ShowOne): data = image_client.get_metadef_property( parsed_args.property_name, parsed_args.namespace_name ) - data = data.to_dict(ignore_none=True, original_names=True) - info = { - key: data[key] - for key in [ - 'namespace_name', - 'name', - 'type', - 'title', - 'description', - 'operators', - 'default', - 'is_readonly', - 'minimum', - 'maximum', - 'enum', - 'pattern', - 'min_length', - 'max_length', - 'items', - 'require_unique_items', - 'min_items', - 'max_items', - 'allow_additional_items', - ] - if key in data - } + info = _format_property(data) return zip(*sorted(info.items())) diff --git a/openstackclient/tests/unit/image/v2/test_metadef_properties.py b/openstackclient/tests/unit/image/v2/test_metadef_properties.py index c3c8de9615..b1fad7f6cd 100644 --- a/openstackclient/tests/unit/image/v2/test_metadef_properties.py +++ b/openstackclient/tests/unit/image/v2/test_metadef_properties.py @@ -12,8 +12,77 @@ # License for the specific language governing permissions and limitations # under the License. +from osc_lib import exceptions + from openstackclient.image.v2 import metadef_properties from openstackclient.tests.unit.image.v2 import fakes as image_fakes +from openstackclient.tests.unit import utils as tests_utils + + +class TestMetadefPropertyCreate(image_fakes.TestImagev2): + _metadef_namespace = image_fakes.create_one_metadef_namespace() + _metadef_property = image_fakes.create_one_metadef_property() + expected_columns = ( + 'name', + 'title', + 'type', + ) + expected_data = ( + _metadef_property.name, + _metadef_property.title, + _metadef_property.type, + ) + + def setUp(self): + super().setUp() + self.image_client.create_metadef_property.return_value = ( + self._metadef_property + ) + self.cmd = metadef_properties.CreateMetadefProperty(self.app, None) + + def test_metadef_property_create_missing_arguments(self): + self.assertRaises( + tests_utils.ParserException, self.check_parser, self.cmd, [], [] + ) + + def test_metadef_property_create(self): + arglist = [ + '--name', + 'cpu_cores', + '--schema', + '{}', + '--title', + 'vCPU Cores', + '--type', + 'integer', + 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) + + def test_metadef_property_create_invalid_schema(self): + arglist = [ + '--name', + 'cpu_cores', + '--schema', + '{invalid}', + '--title', + 'vCPU Cores', + '--type', + 'integer', + self._metadef_namespace.namespace, + ] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.assertRaises( + exceptions.CommandError, self.cmd.take_action, parsed_args + ) class TestMetadefPropertyList(image_fakes.TestImagev2): diff --git a/releasenotes/notes/add-metadef-property-create-c9a4ec2bced892af.yaml b/releasenotes/notes/add-metadef-property-create-c9a4ec2bced892af.yaml new file mode 100644 index 0000000000..11862e1671 --- /dev/null +++ b/releasenotes/notes/add-metadef-property-create-c9a4ec2bced892af.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + Add ``image metadef property create`` command to create a new + metadef property inside a specific namespace. diff --git a/setup.cfg b/setup.cfg index 48781ffd3b..0a579a7967 100644 --- a/setup.cfg +++ b/setup.cfg @@ -397,6 +397,7 @@ openstack.image.v2 = image_metadef_namespace_set = openstackclient.image.v2.metadef_namespaces:SetMetadefNameSpace image_metadef_namespace_show = openstackclient.image.v2.metadef_namespaces:ShowMetadefNameSpace + image_metadef_property_create = openstackclient.image.v2.metadef_properties:CreateMetadefProperty image_metadef_property_list = openstackclient.image.v2.metadef_properties:ListMetadefProperties image_metadef_property_show = openstackclient.image.v2.metadef_properties:ShowMetadefProperty