Skip to content

Commit 13bc379

Browse files
author
Huanxuan Ao
committed
Implement network rbac create and delete commands
Add "network rbac create" and "network rbac delete" commands and also add unit tests, functional tests, docs and release note for them. Change-Id: I5fd58342f2deaa9bae7717412a942a21bbd7d045 Partially-Implements: blueprint neutron-client-rbac
1 parent a8880e8 commit 13bc379

8 files changed

Lines changed: 528 additions & 5 deletions

File tree

doc/source/command-objects/network-rbac.rst

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,68 @@ to network resources for specific projects.
88

99
Network v2
1010

11+
network rbac create
12+
-------------------
13+
14+
Create network RBAC policy
15+
16+
.. program:: network rbac create
17+
.. code:: bash
18+
19+
os network rbac create
20+
--type <type>
21+
--action <action>
22+
--target-project <target-project> [--target-project-domain <target-project-domain>]
23+
[--project <project> [--project-domain <project-domain>]]
24+
<rbac-policy>
25+
26+
.. option:: --type <type>
27+
28+
Type of the object that RBAC policy affects ("qos_policy" or "network") (required)
29+
30+
.. option:: --action <action>
31+
32+
Action for the RBAC policy ("access_as_external" or "access_as_shared") (required)
33+
34+
.. option:: --target-project <target-project>
35+
36+
The project to which the RBAC policy will be enforced (name or ID) (required)
37+
38+
.. option:: --target-project-domain <target-project-domain>
39+
40+
Domain the target project belongs to (name or ID).
41+
This can be used in case collisions between project names exist.
42+
43+
.. option:: --project <project>
44+
45+
The owner project (name or ID)
46+
47+
.. option:: --project-domain <project-domain>
48+
49+
Domain the project belongs to (name or ID).
50+
This can be used in case collisions between project names exist.
51+
52+
.. _network_rbac_create-rbac-policy:
53+
.. describe:: <rbac-object>
54+
55+
The object to which this RBAC policy affects (name or ID for network objects, ID only for QoS policy objects)
56+
57+
network rbac delete
58+
-------------------
59+
60+
Delete network RBAC policy(s)
61+
62+
.. program:: network rbac delete
63+
.. code:: bash
64+
65+
os network rbac delete
66+
<rbac-policy> [<rbac-policy> ...]
67+
68+
.. _network_rbac_delete-rbac-policy:
69+
.. describe:: <rbac-policy>
70+
71+
RBAC policy(s) to delete (ID only)
72+
1173
network rbac list
1274
-----------------
1375

doc/source/specs/command-objects/example.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ Delete example(s)
3838
3939
.. describe:: <example>
4040

41-
Example to delete (name or ID)
41+
Example(s) to delete (name or ID)
4242

4343
example list
4444
------------
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# Licensed under the Apache License, Version 2.0 (the "License"); you may
2+
# not use this file except in compliance with the License. You may obtain
3+
# a copy of the License at
4+
#
5+
# http://www.apache.org/licenses/LICENSE-2.0
6+
#
7+
# Unless required by applicable law or agreed to in writing, software
8+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
9+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
10+
# License for the specific language governing permissions and limitations
11+
# under the License.
12+
13+
import uuid
14+
15+
from functional.common import test
16+
17+
18+
class NetworkRBACTests(test.TestCase):
19+
"""Functional tests for network rbac. """
20+
NET_NAME = uuid.uuid4().hex
21+
OBJECT_ID = None
22+
ID = None
23+
HEADERS = ['ID']
24+
FIELDS = ['id']
25+
26+
@classmethod
27+
def setUpClass(cls):
28+
opts = cls.get_opts(cls.FIELDS)
29+
raw_output = cls.openstack('network create ' + cls.NET_NAME + opts)
30+
cls.OBJECT_ID = raw_output.strip('\n')
31+
opts = cls.get_opts(['id', 'object_id'])
32+
raw_output = cls.openstack('network rbac create ' +
33+
cls.OBJECT_ID +
34+
' --action access_as_shared' +
35+
' --target-project admin' +
36+
' --type network' + opts)
37+
cls.ID, object_id, rol = tuple(raw_output.split('\n'))
38+
cls.assertOutput(cls.OBJECT_ID, object_id)
39+
40+
@classmethod
41+
def tearDownClass(cls):
42+
raw_output = cls.openstack('network rbac delete ' + cls.ID)
43+
cls.assertOutput('', raw_output)
44+
raw_output = cls.openstack('network delete ' + cls.OBJECT_ID)
45+
cls.assertOutput('', raw_output)
46+
47+
def test_network_rbac_list(self):
48+
opts = self.get_opts(self.HEADERS)
49+
raw_output = self.openstack('network rbac list' + opts)
50+
self.assertIn(self.ID, raw_output)
51+
52+
def test_network_rbac_show(self):
53+
opts = self.get_opts(self.FIELDS)
54+
raw_output = self.openstack('network rbac show ' + self.ID + opts)
55+
self.assertEqual(self.ID + "\n", raw_output)

openstackclient/network/v2/network_rbac.py

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,17 @@
1313

1414
"""RBAC action implementations"""
1515

16+
import logging
17+
1618
from osc_lib.command import command
19+
from osc_lib import exceptions
1720
from osc_lib import utils
1821

1922
from openstackclient.i18n import _
23+
from openstackclient.identity import common as identity_common
24+
25+
26+
LOG = logging.getLogger(__name__)
2027

2128

2229
def _get_columns(item):
@@ -30,6 +37,131 @@ def _get_columns(item):
3037
return tuple(sorted(columns))
3138

3239

40+
def _get_attrs(client_manager, parsed_args):
41+
attrs = {}
42+
attrs['object_type'] = parsed_args.type
43+
attrs['action'] = parsed_args.action
44+
45+
network_client = client_manager.network
46+
if parsed_args.type == 'network':
47+
object_id = network_client.find_network(
48+
parsed_args.rbac_object, ignore_missing=False).id
49+
if parsed_args.type == 'qos_policy':
50+
# TODO(Huanxuan Ao): Support finding a object ID by obejct name
51+
# after qos policy finding supported in SDK.
52+
object_id = parsed_args.rbac_object
53+
attrs['object_id'] = object_id
54+
55+
identity_client = client_manager.identity
56+
project_id = identity_common.find_project(
57+
identity_client,
58+
parsed_args.target_project,
59+
parsed_args.target_project_domain,
60+
).id
61+
attrs['target_tenant'] = project_id
62+
if parsed_args.project is not None:
63+
project_id = identity_common.find_project(
64+
identity_client,
65+
parsed_args.project,
66+
parsed_args.project_domain,
67+
).id
68+
attrs['tenant_id'] = project_id
69+
70+
return attrs
71+
72+
73+
class CreateNetworkRBAC(command.ShowOne):
74+
"""Create network RBAC policy"""
75+
76+
def get_parser(self, prog_name):
77+
parser = super(CreateNetworkRBAC, self).get_parser(prog_name)
78+
parser.add_argument(
79+
'rbac_object',
80+
metavar="<rbac-object>",
81+
help=_("The object to which this RBAC policy affects (name or "
82+
"ID for network objects, ID only for QoS policy objects)")
83+
)
84+
parser.add_argument(
85+
'--type',
86+
metavar="<type>",
87+
required=True,
88+
choices=['qos_policy', 'network'],
89+
help=_('Type of the object that RBAC policy '
90+
'affects ("qos_policy" or "network")')
91+
)
92+
parser.add_argument(
93+
'--action',
94+
metavar="<action>",
95+
required=True,
96+
choices=['access_as_external', 'access_as_shared'],
97+
help=_('Action for the RBAC policy '
98+
'("access_as_external" or "access_as_shared")')
99+
)
100+
parser.add_argument(
101+
'--target-project',
102+
required=True,
103+
metavar="<target-project>",
104+
help=_('The project to which the RBAC policy '
105+
'will be enforced (name or ID)')
106+
)
107+
parser.add_argument(
108+
'--target-project-domain',
109+
metavar='<target-project-domain>',
110+
help=_('Domain the target project belongs to (name or ID). '
111+
'This can be used in case collisions between project names '
112+
'exist.'),
113+
)
114+
parser.add_argument(
115+
'--project',
116+
metavar="<project>",
117+
help=_('The owner project (name or ID)')
118+
)
119+
identity_common.add_project_domain_option_to_parser(parser)
120+
return parser
121+
122+
def take_action(self, parsed_args):
123+
client = self.app.client_manager.network
124+
attrs = _get_attrs(self.app.client_manager, parsed_args)
125+
obj = client.create_rbac_policy(**attrs)
126+
columns = _get_columns(obj)
127+
data = utils.get_item_properties(obj, columns)
128+
return columns, data
129+
130+
131+
class DeleteNetworkRBAC(command.Command):
132+
"""Delete network RBAC policy(s)"""
133+
134+
def get_parser(self, prog_name):
135+
parser = super(DeleteNetworkRBAC, self).get_parser(prog_name)
136+
parser.add_argument(
137+
'rbac_policy',
138+
metavar="<rbac-policy>",
139+
nargs='+',
140+
help=_("RBAC policy(s) to delete (ID only)")
141+
)
142+
return parser
143+
144+
def take_action(self, parsed_args):
145+
client = self.app.client_manager.network
146+
result = 0
147+
148+
for rbac in parsed_args.rbac_policy:
149+
try:
150+
obj = client.find_rbac_policy(rbac, ignore_missing=False)
151+
client.delete_rbac_policy(obj)
152+
except Exception as e:
153+
result += 1
154+
LOG.error(_("Failed to delete RBAC policy with "
155+
"ID '%(rbac)s': %(e)s"),
156+
{'rbac': rbac, 'e': e})
157+
158+
if result > 0:
159+
total = len(parsed_args.rbac_policy)
160+
msg = (_("%(result)s of %(total)s RBAC policies failed "
161+
"to delete.") % {'result': result, 'total': total})
162+
raise exceptions.CommandError(msg)
163+
164+
33165
class ListNetworkRBAC(command.Lister):
34166
"""List network RBAC policies"""
35167

openstackclient/tests/network/v2/fakes.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -541,6 +541,25 @@ def create_network_rbacs(attrs=None, count=2):
541541

542542
return rbac_policies
543543

544+
@staticmethod
545+
def get_network_rbacs(rbac_policies=None, count=2):
546+
"""Get an iterable MagicMock object with a list of faked rbac policies.
547+
548+
If rbac policies list is provided, then initialize the Mock object
549+
with the list. Otherwise create one.
550+
551+
:param List rbac_policies:
552+
A list of FakeResource objects faking rbac policies
553+
:param int count:
554+
The number of rbac policies to fake
555+
:return:
556+
An iterable Mock object with side_effect set to a list of faked
557+
rbac policies
558+
"""
559+
if rbac_policies is None:
560+
rbac_policies = FakeNetworkRBAC.create_network_rbacs(count)
561+
return mock.MagicMock(side_effect=rbac_policies)
562+
544563

545564
class FakeRouter(object):
546565
"""Fake one or more routers."""

0 commit comments

Comments
 (0)