From 22c60f3ac7a482609ba393a96db3989dca4c280c Mon Sep 17 00:00:00 2001 From: Huanxuan Ao Date: Wed, 1 Jun 2016 09:42:40 +0800 Subject: [PATCH] Add support for removing flavor-access Add "--project" and "--project-domain" options in "flavor unset" command to remove flavor access. Change-Id: Ia5c5a80d9890d5af066b75b4e202647c18c7d915 Partial-Bug: #1575461 --- doc/source/command-objects/flavor.rst | 11 +++ openstackclient/compute/v2/flavor.py | 60 +++++++++++-- .../tests/compute/v2/test_flavor.py | 88 ++++++++++++++++++- .../notes/bug-1575461-54a23327081cbec7.yaml | 6 ++ 4 files changed, 154 insertions(+), 11 deletions(-) create mode 100644 releasenotes/notes/bug-1575461-54a23327081cbec7.yaml diff --git a/doc/source/command-objects/flavor.rst b/doc/source/command-objects/flavor.rst index c6bde8828..a30bedecb 100644 --- a/doc/source/command-objects/flavor.rst +++ b/doc/source/command-objects/flavor.rst @@ -175,12 +175,23 @@ Unset flavor properties os flavor unset [--property [...] ] + [--project ] + [--project-domain ] .. option:: --property Property to remove from flavor (repeat option to remove multiple properties) +.. option:: --project + + Remove flavor access from project (name or ID) (admin only) + +.. option:: --project-domain + + Domain the project belongs to (name or ID). + This can be used in case collisions between project names exist. + .. describe:: Flavor to modify (name or ID) diff --git a/openstackclient/compute/v2/flavor.py b/openstackclient/compute/v2/flavor.py index 48d0e27e1..87909a182 100644 --- a/openstackclient/compute/v2/flavor.py +++ b/openstackclient/compute/v2/flavor.py @@ -337,22 +337,64 @@ class UnsetFlavor(command.Command): def get_parser(self, prog_name): parser = super(UnsetFlavor, self).get_parser(prog_name) - parser.add_argument( - "--property", - metavar="", - action='append', - required=True, - help=_("Property to remove from flavor " - "(repeat option to unset multiple properties)") - ) parser.add_argument( "flavor", metavar="", help=_("Flavor to modify (name or ID)") ) + parser.add_argument( + "--property", + metavar="", + action='append', + help=_("Property to remove from flavor " + "(repeat option to unset multiple properties)") + ) + parser.add_argument( + '--project', + metavar='', + help=_('Remove flavor access from project (name or ID) ' + '(admin only)'), + ) + identity_common.add_project_domain_option_to_parser(parser) + return parser def take_action(self, parsed_args): compute_client = self.app.client_manager.compute + identity_client = self.app.client_manager.identity + flavor = _find_flavor(compute_client, parsed_args.flavor) - flavor.unset_keys(parsed_args.property) + + if not parsed_args.property and not parsed_args.project: + raise exceptions.CommandError(_("Nothing specified to be unset.")) + + result = 0 + if parsed_args.property: + try: + flavor.unset_keys(parsed_args.property) + except Exception as e: + self.app.log.error( + _("Failed to unset flavor property: %s") % str(e)) + result += 1 + + if parsed_args.project: + try: + if flavor.is_public: + msg = _("Cannot remove access for a public flavor") + raise exceptions.CommandError(msg) + else: + project_id = identity_common.find_project( + identity_client, + parsed_args.project, + parsed_args.project_domain, + ).id + compute_client.flavor_access.remove_tenant_access( + flavor.id, project_id) + except Exception as e: + self.app.log.error(_("Failed to remove flavor access from" + " project: %s") % str(e)) + result += 1 + + if result > 0: + raise exceptions.CommandError(_("Command Failed: One or more of" + " the operations failed")) diff --git a/openstackclient/tests/compute/v2/test_flavor.py b/openstackclient/tests/compute/v2/test_flavor.py index e5bdffe4e..9d424890e 100644 --- a/openstackclient/tests/compute/v2/test_flavor.py +++ b/openstackclient/tests/compute/v2/test_flavor.py @@ -616,16 +616,23 @@ class TestFlavorShow(TestFlavor): class TestFlavorUnset(TestFlavor): # Return value of self.flavors_mock.find(). - flavor = compute_fakes.FakeFlavor.create_one_flavor() + flavor = compute_fakes.FakeFlavor.create_one_flavor( + attrs={'os-flavor-access:is_public': False}) def setUp(self): super(TestFlavorUnset, self).setUp() self.flavors_mock.find.return_value = self.flavor self.flavors_mock.get.side_effect = exceptions.NotFound(None) + # Return a project + self.projects_mock.get.return_value = fakes.FakeResource( + None, + copy.deepcopy(identity_fakes.PROJECT), + loaded=True, + ) self.cmd = flavor.UnsetFlavor(self.app, None) - def test_flavor_unset(self): + def test_flavor_unset_property(self): arglist = [ '--property', 'property', 'baremetal' @@ -640,3 +647,80 @@ class TestFlavorUnset(TestFlavor): self.flavors_mock.find.assert_called_with(name=parsed_args.flavor, is_public=None) self.assertIsNone(result) + + def test_flavor_unset_project(self): + arglist = [ + '--project', identity_fakes.project_id, + self.flavor.id, + ] + verifylist = [ + ('project', identity_fakes.project_id), + ('flavor', self.flavor.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + self.assertIsNone(result) + + self.flavor_access_mock.remove_tenant_access.assert_called_with( + self.flavor.id, + identity_fakes.project_id, + ) + + def test_flavor_unset_no_project(self): + arglist = [ + '--project', '', + self.flavor.id, + ] + verifylist = [ + ('project', ''), + ('flavor', self.flavor.id), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + self.assertRaises(exceptions.CommandError, self.cmd.take_action, + parsed_args) + + def test_flavor_unset_no_flavor(self): + arglist = [ + '--project', identity_fakes.project_id, + ] + verifylist = [ + ('project', identity_fakes.project_id), + ] + + self.assertRaises(tests_utils.ParserException, + self.check_parser, + self.cmd, + arglist, + verifylist) + + def test_flavor_unset_with_unexist_flavor(self): + self.flavors_mock.get.side_effect = exceptions.NotFound(None) + self.flavors_mock.find.side_effect = exceptions.NotFound(None) + + arglist = [ + '--project', identity_fakes.project_id, + 'unexist_flavor', + ] + verifylist = [ + ('project', identity_fakes.project_id), + ('flavor', 'unexist_flavor'), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.assertRaises(exceptions.CommandError, + self.cmd.take_action, + parsed_args) + + def test_flavor_unset_nothing(self): + arglist = [ + self.flavor.id, + ] + verifylist = [ + ('flavor', self.flavor.id), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + self.assertRaises(exceptions.CommandError, self.cmd.take_action, + parsed_args) diff --git a/releasenotes/notes/bug-1575461-54a23327081cbec7.yaml b/releasenotes/notes/bug-1575461-54a23327081cbec7.yaml new file mode 100644 index 000000000..a498bfbda --- /dev/null +++ b/releasenotes/notes/bug-1575461-54a23327081cbec7.yaml @@ -0,0 +1,6 @@ +--- +features: + - | + Added support for removing flavor access from project by using below command + ``flavor unset --project --project-domain `` + [Bug `1575461 `_]