Skip to content

Commit c46a559

Browse files
author
Huanxuan Ao
committed
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
1 parent 0ff7d6d commit c46a559

5 files changed

Lines changed: 165 additions & 6 deletions

File tree

doc/source/command-objects/flavor.rst

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,12 +128,23 @@ Set flavor properties
128128
129129
os flavor set
130130
[--property <key=value> [...] ]
131+
[--project <project>]
132+
[--project-domain <project-domain>]
131133
<flavor>
132134
133135
.. option:: --property <key=value>
134136

135137
Property to add or modify for this flavor (repeat option to set multiple properties)
136138

139+
.. option:: --project <project>
140+
141+
Set flavor access to project (name or ID) (admin only)
142+
143+
.. option:: --project-domain <project-domain>
144+
145+
Domain the project belongs to (name or ID).
146+
This can be used in case collisions between project names exist.
147+
137148
.. describe:: <flavor>
138149

139150
Flavor to modify (name or ID)

openstackclient/compute/v2/flavor.py

Lines changed: 48 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
from openstackclient.common import parseractions
2323
from openstackclient.common import utils
2424
from openstackclient.i18n import _
25+
from openstackclient.identity import common as identity_common
2526

2627

2728
def _find_flavor(compute_client, flavor):
@@ -245,6 +246,11 @@ class SetFlavor(command.Command):
245246

246247
def get_parser(self, prog_name):
247248
parser = super(SetFlavor, self).get_parser(prog_name)
249+
parser.add_argument(
250+
"flavor",
251+
metavar="<flavor>",
252+
help=_("Flavor to modify (name or ID)")
253+
)
248254
parser.add_argument(
249255
"--property",
250256
metavar="<key=value>",
@@ -253,16 +259,54 @@ def get_parser(self, prog_name):
253259
"(repeat option to set multiple properties)")
254260
)
255261
parser.add_argument(
256-
"flavor",
257-
metavar="<flavor>",
258-
help=_("Flavor to modify (name or ID)")
262+
'--project',
263+
metavar='<project>',
264+
help=_('Set flavor access to project (name or ID) '
265+
'(admin only)'),
259266
)
267+
identity_common.add_project_domain_option_to_parser(parser)
268+
260269
return parser
261270

262271
def take_action(self, parsed_args):
263272
compute_client = self.app.client_manager.compute
273+
identity_client = self.app.client_manager.identity
274+
264275
flavor = _find_flavor(compute_client, parsed_args.flavor)
265-
flavor.set_keys(parsed_args.property)
276+
277+
if not parsed_args.property and not parsed_args.project:
278+
raise exceptions.CommandError(_("Nothing specified to be set."))
279+
280+
result = 0
281+
if parsed_args.property:
282+
try:
283+
flavor.set_keys(parsed_args.property)
284+
except Exception as e:
285+
self.app.log.error(
286+
_("Failed to set flavor property: %s") % str(e))
287+
result += 1
288+
289+
if parsed_args.project:
290+
try:
291+
if flavor.is_public:
292+
msg = _("Cannot set access for a public flavor")
293+
raise exceptions.CommandError(msg)
294+
else:
295+
project_id = identity_common.find_project(
296+
identity_client,
297+
parsed_args.project,
298+
parsed_args.project_domain,
299+
).id
300+
compute_client.flavor_access.add_tenant_access(
301+
flavor.id, project_id)
302+
except Exception as e:
303+
self.app.log.error(_("Failed to set flavor access to"
304+
" project: %s") % str(e))
305+
result += 1
306+
307+
if result > 0:
308+
raise exceptions.CommandError(_("Command Failed: One or more of"
309+
" the operations failed"))
266310

267311

268312
class ShowFlavor(command.ShowOne):

openstackclient/tests/compute/v2/fakes.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,9 @@ def __init__(self, **kwargs):
132132
self.flavors = mock.Mock()
133133
self.flavors.resource_class = fakes.FakeResource(None, {})
134134

135+
self.flavor_access = mock.Mock()
136+
self.flavor_access.resource_class = fakes.FakeResource(None, {})
137+
135138
self.quotas = mock.Mock()
136139
self.quotas.resource_class = fakes.FakeResource(None, {})
137140

openstackclient/tests/compute/v2/test_flavor.py

Lines changed: 97 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,14 @@
1313
# under the License.
1414
#
1515

16+
import copy
17+
1618
from openstackclient.common import exceptions
1719
from openstackclient.common import utils
1820
from openstackclient.compute.v2 import flavor
1921
from openstackclient.tests.compute.v2 import fakes as compute_fakes
22+
from openstackclient.tests import fakes
23+
from openstackclient.tests.identity.v3 import fakes as identity_fakes
2024
from openstackclient.tests import utils as tests_utils
2125

2226

@@ -29,6 +33,13 @@ def setUp(self):
2933
self.flavors_mock = self.app.client_manager.compute.flavors
3034
self.flavors_mock.reset_mock()
3135

36+
# Get a shortcut to the FlavorAccessManager Mock
37+
self.flavor_access_mock = self.app.client_manager.compute.flavor_access
38+
self.flavor_access_mock.reset_mock()
39+
40+
self.projects_mock = self.app.client_manager.identity.projects
41+
self.projects_mock.reset_mock()
42+
3243

3344
class TestFlavorCreate(TestFlavor):
3445

@@ -427,16 +438,23 @@ def test_flavor_list_long(self):
427438
class TestFlavorSet(TestFlavor):
428439

429440
# Return value of self.flavors_mock.find().
430-
flavor = compute_fakes.FakeFlavor.create_one_flavor()
441+
flavor = compute_fakes.FakeFlavor.create_one_flavor(
442+
attrs={'os-flavor-access:is_public': False})
431443

432444
def setUp(self):
433445
super(TestFlavorSet, self).setUp()
434446

435447
self.flavors_mock.find.return_value = self.flavor
436448
self.flavors_mock.get.side_effect = exceptions.NotFound(None)
449+
# Return a project
450+
self.projects_mock.get.return_value = fakes.FakeResource(
451+
None,
452+
copy.deepcopy(identity_fakes.PROJECT),
453+
loaded=True,
454+
)
437455
self.cmd = flavor.SetFlavor(self.app, None)
438456

439-
def test_flavor_set(self):
457+
def test_flavor_set_property(self):
440458
arglist = [
441459
'--property', 'FOO="B A R"',
442460
'baremetal'
@@ -452,6 +470,83 @@ def test_flavor_set(self):
452470
is_public=None)
453471
self.assertIsNone(result)
454472

473+
def test_flavor_set_project(self):
474+
arglist = [
475+
'--project', identity_fakes.project_id,
476+
self.flavor.id,
477+
]
478+
verifylist = [
479+
('project', identity_fakes.project_id),
480+
('flavor', self.flavor.id),
481+
]
482+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
483+
484+
result = self.cmd.take_action(parsed_args)
485+
self.assertIsNone(result)
486+
487+
self.flavor_access_mock.add_tenant_access.assert_called_with(
488+
self.flavor.id,
489+
identity_fakes.project_id,
490+
)
491+
492+
def test_flavor_set_no_project(self):
493+
arglist = [
494+
'--project', '',
495+
self.flavor.id,
496+
]
497+
verifylist = [
498+
('project', ''),
499+
('flavor', self.flavor.id),
500+
]
501+
502+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
503+
self.assertRaises(exceptions.CommandError, self.cmd.take_action,
504+
parsed_args)
505+
506+
def test_flavor_set_no_flavor(self):
507+
arglist = [
508+
'--project', identity_fakes.project_id,
509+
]
510+
verifylist = [
511+
('project', identity_fakes.project_id),
512+
]
513+
514+
self.assertRaises(tests_utils.ParserException,
515+
self.check_parser,
516+
self.cmd,
517+
arglist,
518+
verifylist)
519+
520+
def test_flavor_set_with_unexist_flavor(self):
521+
self.flavors_mock.get.side_effect = exceptions.NotFound(None)
522+
self.flavors_mock.find.side_effect = exceptions.NotFound(None)
523+
524+
arglist = [
525+
'--project', identity_fakes.project_id,
526+
'unexist_flavor',
527+
]
528+
verifylist = [
529+
('project', identity_fakes.project_id),
530+
('flavor', 'unexist_flavor'),
531+
]
532+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
533+
534+
self.assertRaises(exceptions.CommandError,
535+
self.cmd.take_action,
536+
parsed_args)
537+
538+
def test_flavor_set_nothing(self):
539+
arglist = [
540+
self.flavor.id,
541+
]
542+
verifylist = [
543+
('flavor', self.flavor.id),
544+
]
545+
546+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
547+
self.assertRaises(exceptions.CommandError, self.cmd.take_action,
548+
parsed_args)
549+
455550

456551
class TestFlavorShow(TestFlavor):
457552

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
features:
3+
- |
4+
Added support for setting flavor access to project by using below command
5+
``flavor set <flavor> --project <project>``
6+
[Bug `1575461 <https://bugs.launchpad.net/python-openstackclient/+bug/1575461>`_]

0 commit comments

Comments
 (0)