Add support for setting flavor-access

This patch adds "--project" option in "flavor set" command to support
for setting flavor access.

Change-Id: I75b473600080d8ab1dd6ad01561c4f989ed3c3bd
Partial-Bug: #1575461
This commit is contained in:
Huanxuan Ao 2016-05-26 20:00:12 +08:00
parent 0ff7d6dc69
commit c46a5597be
5 changed files with 165 additions and 6 deletions

View File

@ -128,12 +128,23 @@ Set flavor properties
os flavor set os flavor set
[--property <key=value> [...] ] [--property <key=value> [...] ]
[--project <project>]
[--project-domain <project-domain>]
<flavor> <flavor>
.. option:: --property <key=value> .. option:: --property <key=value>
Property to add or modify for this flavor (repeat option to set multiple properties) Property to add or modify for this flavor (repeat option to set multiple properties)
.. option:: --project <project>
Set flavor access to 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> .. describe:: <flavor>
Flavor to modify (name or ID) Flavor to modify (name or ID)

View File

@ -22,6 +22,7 @@ from openstackclient.common import exceptions
from openstackclient.common import parseractions from openstackclient.common import parseractions
from openstackclient.common import utils from openstackclient.common import utils
from openstackclient.i18n import _ from openstackclient.i18n import _
from openstackclient.identity import common as identity_common
def _find_flavor(compute_client, flavor): def _find_flavor(compute_client, flavor):
@ -245,6 +246,11 @@ class SetFlavor(command.Command):
def get_parser(self, prog_name): def get_parser(self, prog_name):
parser = super(SetFlavor, self).get_parser(prog_name) parser = super(SetFlavor, self).get_parser(prog_name)
parser.add_argument(
"flavor",
metavar="<flavor>",
help=_("Flavor to modify (name or ID)")
)
parser.add_argument( parser.add_argument(
"--property", "--property",
metavar="<key=value>", metavar="<key=value>",
@ -253,16 +259,54 @@ class SetFlavor(command.Command):
"(repeat option to set multiple properties)") "(repeat option to set multiple properties)")
) )
parser.add_argument( parser.add_argument(
"flavor", '--project',
metavar="<flavor>", metavar='<project>',
help=_("Flavor to modify (name or ID)") help=_('Set flavor access to project (name or ID) '
'(admin only)'),
) )
identity_common.add_project_domain_option_to_parser(parser)
return parser return parser
def take_action(self, parsed_args): def take_action(self, parsed_args):
compute_client = self.app.client_manager.compute compute_client = self.app.client_manager.compute
identity_client = self.app.client_manager.identity
flavor = _find_flavor(compute_client, parsed_args.flavor) flavor = _find_flavor(compute_client, parsed_args.flavor)
flavor.set_keys(parsed_args.property)
if not parsed_args.property and not parsed_args.project:
raise exceptions.CommandError(_("Nothing specified to be set."))
result = 0
if parsed_args.property:
try:
flavor.set_keys(parsed_args.property)
except Exception as e:
self.app.log.error(
_("Failed to set flavor property: %s") % str(e))
result += 1
if parsed_args.project:
try:
if flavor.is_public:
msg = _("Cannot set 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.add_tenant_access(
flavor.id, project_id)
except Exception as e:
self.app.log.error(_("Failed to set flavor access to"
" project: %s") % str(e))
result += 1
if result > 0:
raise exceptions.CommandError(_("Command Failed: One or more of"
" the operations failed"))
class ShowFlavor(command.ShowOne): class ShowFlavor(command.ShowOne):

View File

@ -132,6 +132,9 @@ class FakeComputev2Client(object):
self.flavors = mock.Mock() self.flavors = mock.Mock()
self.flavors.resource_class = fakes.FakeResource(None, {}) self.flavors.resource_class = fakes.FakeResource(None, {})
self.flavor_access = mock.Mock()
self.flavor_access.resource_class = fakes.FakeResource(None, {})
self.quotas = mock.Mock() self.quotas = mock.Mock()
self.quotas.resource_class = fakes.FakeResource(None, {}) self.quotas.resource_class = fakes.FakeResource(None, {})

View File

@ -13,10 +13,14 @@
# under the License. # under the License.
# #
import copy
from openstackclient.common import exceptions from openstackclient.common import exceptions
from openstackclient.common import utils from openstackclient.common import utils
from openstackclient.compute.v2 import flavor from openstackclient.compute.v2 import flavor
from openstackclient.tests.compute.v2 import fakes as compute_fakes from openstackclient.tests.compute.v2 import fakes as compute_fakes
from openstackclient.tests import fakes
from openstackclient.tests.identity.v3 import fakes as identity_fakes
from openstackclient.tests import utils as tests_utils from openstackclient.tests import utils as tests_utils
@ -29,6 +33,13 @@ class TestFlavor(compute_fakes.TestComputev2):
self.flavors_mock = self.app.client_manager.compute.flavors self.flavors_mock = self.app.client_manager.compute.flavors
self.flavors_mock.reset_mock() self.flavors_mock.reset_mock()
# Get a shortcut to the FlavorAccessManager Mock
self.flavor_access_mock = self.app.client_manager.compute.flavor_access
self.flavor_access_mock.reset_mock()
self.projects_mock = self.app.client_manager.identity.projects
self.projects_mock.reset_mock()
class TestFlavorCreate(TestFlavor): class TestFlavorCreate(TestFlavor):
@ -427,16 +438,23 @@ class TestFlavorList(TestFlavor):
class TestFlavorSet(TestFlavor): class TestFlavorSet(TestFlavor):
# Return value of self.flavors_mock.find(). # 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): def setUp(self):
super(TestFlavorSet, self).setUp() super(TestFlavorSet, self).setUp()
self.flavors_mock.find.return_value = self.flavor self.flavors_mock.find.return_value = self.flavor
self.flavors_mock.get.side_effect = exceptions.NotFound(None) 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.SetFlavor(self.app, None) self.cmd = flavor.SetFlavor(self.app, None)
def test_flavor_set(self): def test_flavor_set_property(self):
arglist = [ arglist = [
'--property', 'FOO="B A R"', '--property', 'FOO="B A R"',
'baremetal' 'baremetal'
@ -452,6 +470,83 @@ class TestFlavorSet(TestFlavor):
is_public=None) is_public=None)
self.assertIsNone(result) self.assertIsNone(result)
def test_flavor_set_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.add_tenant_access.assert_called_with(
self.flavor.id,
identity_fakes.project_id,
)
def test_flavor_set_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_set_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_set_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_set_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)
class TestFlavorShow(TestFlavor): class TestFlavorShow(TestFlavor):

View File

@ -0,0 +1,6 @@
---
features:
- |
Added support for setting flavor access to project by using below command
``flavor set <flavor> --project <project>``
[Bug `1575461 <https://bugs.launchpad.net/python-openstackclient/+bug/1575461>`_]