Merge "Add support for removing flavor-access"
This commit is contained in:
		| @@ -175,12 +175,23 @@ Unset flavor properties | ||||
|  | ||||
|     os flavor unset | ||||
|         [--property <key> [...] ] | ||||
|         [--project <project>] | ||||
|         [--project-domain <project-domain>] | ||||
|         <flavor> | ||||
|  | ||||
| .. option:: --property <key> | ||||
|  | ||||
|     Property to remove from flavor (repeat option to remove multiple properties) | ||||
|  | ||||
| .. option:: --project <project> | ||||
|  | ||||
|     Remove flavor access from project (name or ID) (admin only) | ||||
|  | ||||
| .. option:: --project-domain <project-domain> | ||||
|  | ||||
|     Domain the project belongs to (name or ID). | ||||
|     This can be used in case collisions between project names exist. | ||||
|  | ||||
| .. describe:: <flavor> | ||||
|  | ||||
|     Flavor to modify (name or ID) | ||||
|   | ||||
| @@ -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="<key>", | ||||
|             action='append', | ||||
|             required=True, | ||||
|             help=_("Property to remove from flavor " | ||||
|                    "(repeat option to unset multiple properties)") | ||||
|         ) | ||||
|         parser.add_argument( | ||||
|             "flavor", | ||||
|             metavar="<flavor>", | ||||
|             help=_("Flavor to modify (name or ID)") | ||||
|         ) | ||||
|         parser.add_argument( | ||||
|             "--property", | ||||
|             metavar="<key>", | ||||
|             action='append', | ||||
|             help=_("Property to remove from flavor " | ||||
|                    "(repeat option to unset multiple properties)") | ||||
|         ) | ||||
|         parser.add_argument( | ||||
|             '--project', | ||||
|             metavar='<project>', | ||||
|             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")) | ||||
|   | ||||
| @@ -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) | ||||
|   | ||||
							
								
								
									
										6
									
								
								releasenotes/notes/bug-1575461-54a23327081cbec7.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								releasenotes/notes/bug-1575461-54a23327081cbec7.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | ||||
| --- | ||||
| features: | ||||
|   - | | ||||
|     Added support for removing flavor access from project by using below command | ||||
|     ``flavor unset <flavor> --project <project> --project-domain <project-domain>`` | ||||
|     [Bug `1575461 <https://bugs.launchpad.net/python-openstackclient/+bug/1575461>`_] | ||||
		Reference in New Issue
	
	Block a user
	 Jenkins
					Jenkins