diff --git a/doc/source/command-objects/flavor.rst b/doc/source/command-objects/flavor.rst index a886b9ff1a..9ea504c1be 100644 --- a/doc/source/command-objects/flavor.rst +++ b/doc/source/command-objects/flavor.rst @@ -21,6 +21,7 @@ Create new flavor [--vcpus ] [--rxtx-factor ] [--public | --private] + [--property [...] ] [--project ] [--project-domain ] @@ -61,6 +62,10 @@ Create new flavor Flavor is not available to other projects +.. option:: --property + + Property to add for this flavor (repeat option to set multiple properties) + .. option:: --project Allow to access private flavor (name or ID) diff --git a/functional/tests/compute/v2/test_flavor.py b/functional/tests/compute/v2/test_flavor.py index 2bb075bd59..6edb1fdfec 100644 --- a/functional/tests/compute/v2/test_flavor.py +++ b/functional/tests/compute/v2/test_flavor.py @@ -25,7 +25,8 @@ class FlavorTests(test.TestCase): @classmethod def setUpClass(cls): opts = cls.get_show_opts(cls.FIELDS) - raw_output = cls.openstack('flavor create ' + cls.NAME + opts) + raw_output = cls.openstack( + 'flavor create --property a=b --property c=d ' + cls.NAME + opts) expected = cls.NAME + '\n' cls.assertOutput(expected, raw_output) @@ -47,19 +48,22 @@ class FlavorTests(test.TestCase): def test_flavor_properties(self): opts = self.get_show_opts(['properties']) - - raw_output = self.openstack( - 'flavor set --property a=b --property c=d ' + self.NAME - ) - self.assertEqual('', raw_output) - + # check the properties we added in create command. raw_output = self.openstack('flavor show ' + self.NAME + opts) self.assertEqual("a='b', c='d'\n", raw_output) raw_output = self.openstack( - 'flavor unset --property a ' + self.NAME + 'flavor set --property e=f --property g=h ' + self.NAME ) self.assertEqual('', raw_output) raw_output = self.openstack('flavor show ' + self.NAME + opts) - self.assertEqual("c='d'\n", raw_output) + self.assertEqual("a='b', c='d', e='f', g='h'\n", raw_output) + + raw_output = self.openstack( + 'flavor unset --property a --property c ' + self.NAME + ) + self.assertEqual('', raw_output) + + raw_output = self.openstack('flavor show ' + self.NAME + opts) + self.assertEqual("e='f', g='h'\n", raw_output) diff --git a/openstackclient/compute/v2/flavor.py b/openstackclient/compute/v2/flavor.py index 01d7da75ff..000df59899 100644 --- a/openstackclient/compute/v2/flavor.py +++ b/openstackclient/compute/v2/flavor.py @@ -121,6 +121,13 @@ class CreateFlavor(command.ShowOne): action="store_false", help=_("Flavor is not available to other projects") ) + parser.add_argument( + "--property", + metavar="", + action=parseractions.KeyValueAction, + help=_("Property to add for this flavor " + "(repeat option to set multiple properties)") + ) parser.add_argument( '--project', metavar='', @@ -150,8 +157,7 @@ class CreateFlavor(command.ShowOne): parsed_args.public ) - flavor = compute_client.flavors.create(*args)._info.copy() - flavor.pop("links") + flavor = compute_client.flavors.create(*args) if parsed_args.project: try: @@ -166,8 +172,17 @@ class CreateFlavor(command.ShowOne): msg = _("Failed to add project %(project)s access to " "flavor: %(e)s") LOG.error(msg % {'project': parsed_args.project, 'e': e}) + if parsed_args.property: + try: + flavor.set_keys(parsed_args.property) + except Exception as e: + LOG.error(_("Failed to set flavor property: %s"), e) - return zip(*sorted(six.iteritems(flavor))) + flavor_info = flavor._info.copy() + flavor_info.pop("links") + flavor_info['properties'] = utils.format_dict(flavor.get_keys()) + + return zip(*sorted(six.iteritems(flavor_info))) class DeleteFlavor(command.Command): diff --git a/openstackclient/tests/compute/v2/fakes.py b/openstackclient/tests/compute/v2/fakes.py index b7f17fbc93..a7a66d5e1f 100644 --- a/openstackclient/tests/compute/v2/fakes.py +++ b/openstackclient/tests/compute/v2/fakes.py @@ -716,6 +716,7 @@ class FakeFlavor(object): 'OS-FLV-DISABLED:disabled': False, 'os-flavor-access:is_public': True, 'OS-FLV-EXT-DATA:ephemeral': 0, + 'properties': {'property': 'value'}, } # Overwrite default attributes. diff --git a/openstackclient/tests/compute/v2/test_flavor.py b/openstackclient/tests/compute/v2/test_flavor.py index da76b6d706..20ae8706f6 100644 --- a/openstackclient/tests/compute/v2/test_flavor.py +++ b/openstackclient/tests/compute/v2/test_flavor.py @@ -56,6 +56,7 @@ class TestFlavorCreate(TestFlavor): 'id', 'name', 'os-flavor-access:is_public', + 'properties', 'ram', 'rxtx_factor', 'swap', @@ -68,6 +69,7 @@ class TestFlavorCreate(TestFlavor): flavor.id, flavor.name, flavor.is_public, + utils.format_dict(flavor.properties), flavor.ram, flavor.rxtx_factor, flavor.swap, @@ -116,7 +118,6 @@ class TestFlavorCreate(TestFlavor): def test_flavor_create_all_options(self): arglist = [ - self.flavor.name, '--id', self.flavor.id, '--ram', str(self.flavor.ram), '--disk', str(self.flavor.disk), @@ -125,9 +126,10 @@ class TestFlavorCreate(TestFlavor): '--vcpus', str(self.flavor.vcpus), '--rxtx-factor', str(self.flavor.rxtx_factor), '--public', + '--property', 'property=value', + self.flavor.name, ] verifylist = [ - ('name', self.flavor.name), ('id', self.flavor.id), ('ram', self.flavor.ram), ('disk', self.flavor.disk), @@ -136,6 +138,8 @@ class TestFlavorCreate(TestFlavor): ('vcpus', self.flavor.vcpus), ('rxtx_factor', self.flavor.rxtx_factor), ('public', True), + ('property', {'property': 'value'}), + ('name', self.flavor.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -152,6 +156,8 @@ class TestFlavorCreate(TestFlavor): ) columns, data = self.cmd.take_action(parsed_args) self.flavors_mock.create.assert_called_once_with(*args) + self.flavor.set_keys.assert_called_once_with({'property': 'value'}) + self.flavor.get_keys.assert_called_once_with() self.assertEqual(self.columns, columns) self.assertEqual(self.data, data) @@ -160,7 +166,6 @@ class TestFlavorCreate(TestFlavor): self.flavor.is_public = False arglist = [ - self.flavor.name, '--id', self.flavor.id, '--ram', str(self.flavor.ram), '--disk', str(self.flavor.disk), @@ -170,9 +175,11 @@ class TestFlavorCreate(TestFlavor): '--rxtx-factor', str(self.flavor.rxtx_factor), '--private', '--project', identity_fakes.project_id, + '--property', 'key1=value1', + '--property', 'key2=value2', + self.flavor.name, ] verifylist = [ - ('name', self.flavor.name), ('id', self.flavor.id), ('ram', self.flavor.ram), ('disk', self.flavor.disk), @@ -182,6 +189,8 @@ class TestFlavorCreate(TestFlavor): ('rxtx_factor', self.flavor.rxtx_factor), ('public', False), ('project', identity_fakes.project_id), + ('property', {'key1': 'value1', 'key2': 'value2'}), + ('name', self.flavor.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -202,6 +211,9 @@ class TestFlavorCreate(TestFlavor): self.flavor.id, identity_fakes.project_id, ) + self.flavor.set_keys.assert_called_with( + {'key1': 'value1', 'key2': 'value2'}) + self.flavor.get_keys.assert_called_with() self.assertEqual(self.columns, columns) self.assertEqual(self.data, data) diff --git a/releasenotes/notes/bug-1596798-b22fd587bdca8b36.yaml b/releasenotes/notes/bug-1596798-b22fd587bdca8b36.yaml new file mode 100644 index 0000000000..6ab833da16 --- /dev/null +++ b/releasenotes/notes/bug-1596798-b22fd587bdca8b36.yaml @@ -0,0 +1,4 @@ +--- +features: + - Add ``--property`` option to ``flavor create`` command. + [Bug `1596798 `_]