Skip to content

Commit 21b656c

Browse files
Zuulopenstack-gerrit
authored andcommitted
Merge "Add support for endpoing filter commands"
2 parents 58e5abf + 12ee186 commit 21b656c

8 files changed

Lines changed: 426 additions & 18 deletions

File tree

doc/source/cli/command-objects/endpoint.rst

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,34 @@ endpoint
44

55
Identity v2, v3
66

7+
endpoint add project
8+
--------------------
9+
10+
Associate a project to and endpoint for endpoint filtering
11+
12+
.. program:: endpoint add project
13+
.. code:: bash
14+
15+
openstack endpoint add project
16+
[--project-domain <project-domain>]
17+
<endpoint>
18+
<project>
19+
20+
.. option:: --project-domain <project-domain>
21+
22+
Domain the project belongs to (name or ID).
23+
This can be used in case collisions between project names exist.
24+
25+
.. _endpoint_add_project-endpoint:
26+
.. describe:: <endpoint>
27+
28+
Endpoint to associate with specified project (name or ID)
29+
30+
.. _endpoint_add_project-project:
31+
.. describe:: <project>
32+
33+
Project to associate with specified endpoint (name or ID)
34+
735
endpoint create
836
---------------
937

@@ -107,6 +135,8 @@ List endpoints
107135
[--interface <interface>]
108136
[--region <region-id>]
109137
[--long]
138+
[--endpoint <endpoint> |
139+
--project <project> [--project-domain <project-domain>]]
110140
111141
.. option:: --service <service>
112142
@@ -132,6 +162,55 @@ List endpoints
132162
133163
*Identity version 2 only*
134164
165+
.. option:: --endpoint
166+
167+
List projects that have access to that endpoint using
168+
endpoint filtering
169+
170+
*Identity version 3 only*
171+
172+
.. option:: --project
173+
174+
List endpoints available for the project using
175+
endpoint filtering
176+
177+
*Identity version 3 only*
178+
179+
.. option:: --project-domain
180+
181+
Domain the project belongs to (name or ID).
182+
This can be used in case collisions between project names exist.
183+
184+
*Identity version 3 only*
185+
186+
endpoint remove project
187+
-----------------------
188+
189+
Dissociate a project from an endpoint.
190+
191+
.. program:: endpoint remove project
192+
.. code:: bash
193+
194+
openstack endpoint remove project
195+
[--project-domain <project-domain>]
196+
<endpoint>
197+
<project>
198+
199+
.. option:: --project-domain <project-domain>
200+
201+
Domain the project belongs to (name or ID).
202+
This can be used in case collisions between project names exist.
203+
204+
.. _endpoint_remove_project-endpoint:
205+
.. describe:: <endpoint>
206+
207+
Endpoint to dissociate with specified project (name or ID)
208+
209+
.. _endpoint_remove_project-project:
210+
.. describe:: <project>
211+
212+
Project to dissociate with specified endpoint (name or ID)
213+
135214
endpoint set
136215
------------
137216

openstackclient/identity/v3/endpoint.py

Lines changed: 130 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,42 @@ def get_service_name(service):
3636
return ''
3737

3838

39+
class AddProjectToEndpoint(command.Command):
40+
_description = _("Associate a project to an endpoint")
41+
42+
def get_parser(self, prog_name):
43+
parser = super(
44+
AddProjectToEndpoint, self).get_parser(prog_name)
45+
parser.add_argument(
46+
'endpoint',
47+
metavar='<endpoint>',
48+
help=_('Endpoint to associate with '
49+
'specified project (name or ID)'),
50+
)
51+
parser.add_argument(
52+
'project',
53+
metavar='<project>',
54+
help=_('Project to associate with '
55+
'specified endpoint name or ID)'),
56+
)
57+
common.add_project_domain_option_to_parser(parser)
58+
return parser
59+
60+
def take_action(self, parsed_args):
61+
client = self.app.client_manager.identity
62+
63+
endpoint = utils.find_resource(client.endpoints,
64+
parsed_args.endpoint)
65+
66+
project = common.find_project(client,
67+
parsed_args.project,
68+
parsed_args.project_domain)
69+
70+
client.endpoint_filter.add_endpoint_to_project(
71+
project=project.id,
72+
endpoint=endpoint.id)
73+
74+
3975
class CreateEndpoint(command.ShowOne):
4076
_description = _("Create new endpoint")
4177

@@ -152,34 +188,111 @@ def get_parser(self, prog_name):
152188
metavar='<region-id>',
153189
help=_('Filter by region ID'),
154190
)
191+
list_group = parser.add_mutually_exclusive_group()
192+
list_group.add_argument(
193+
'--endpoint',
194+
metavar='<endpoint-group>',
195+
help=_('Endpoint to list filters'),
196+
)
197+
list_group.add_argument(
198+
'--project',
199+
metavar='<project>',
200+
help=_('Project to list filters (name or ID)'),
201+
)
202+
common.add_project_domain_option_to_parser(list_group)
155203
return parser
156204

157205
def take_action(self, parsed_args):
158206
identity_client = self.app.client_manager.identity
159-
columns = ('ID', 'Region', 'Service Name', 'Service Type',
160-
'Enabled', 'Interface', 'URL')
161-
kwargs = {}
162-
if parsed_args.service:
163-
service = common.find_service(identity_client, parsed_args.service)
164-
kwargs['service'] = service.id
165-
if parsed_args.interface:
166-
kwargs['interface'] = parsed_args.interface
167-
if parsed_args.region:
168-
kwargs['region'] = parsed_args.region
169-
data = identity_client.endpoints.list(**kwargs)
170-
service_list = identity_client.services.list()
171-
172-
for ep in data:
173-
service = common.find_service_in_list(service_list, ep.service_id)
174-
ep.service_name = get_service_name(service)
175-
ep.service_type = service.type
207+
208+
endpoint = None
209+
if parsed_args.endpoint:
210+
endpoint = utils.find_resource(identity_client.endpoints,
211+
parsed_args.endpoint)
212+
project = None
213+
if parsed_args.project:
214+
project = common.find_project(identity_client,
215+
parsed_args.project,
216+
parsed_args.project_domain)
217+
218+
if endpoint:
219+
columns = ('ID', 'Name')
220+
data = (
221+
identity_client.endpoint_filter
222+
.list_projects_for_endpoint(endpoint=endpoint.id)
223+
)
224+
else:
225+
columns = ('ID', 'Region', 'Service Name', 'Service Type',
226+
'Enabled', 'Interface', 'URL')
227+
kwargs = {}
228+
if parsed_args.service:
229+
service = common.find_service(identity_client,
230+
parsed_args.service)
231+
kwargs['service'] = service.id
232+
if parsed_args.interface:
233+
kwargs['interface'] = parsed_args.interface
234+
if parsed_args.region:
235+
kwargs['region'] = parsed_args.region
236+
237+
if project:
238+
data = (
239+
identity_client.endpoint_filter
240+
.list_endpoints_for_project(project=project.id)
241+
)
242+
else:
243+
data = identity_client.endpoints.list(**kwargs)
244+
245+
service_list = identity_client.services.list()
246+
247+
for ep in data:
248+
service = common.find_service_in_list(service_list,
249+
ep.service_id)
250+
ep.service_name = get_service_name(service)
251+
ep.service_type = service.type
252+
176253
return (columns,
177254
(utils.get_item_properties(
178255
s, columns,
179256
formatters={},
180257
) for s in data))
181258

182259

260+
class RemoveProjectFromEndpoint(command.Command):
261+
_description = _("Dissociate a project from an endpoint")
262+
263+
def get_parser(self, prog_name):
264+
parser = super(
265+
RemoveProjectFromEndpoint, self).get_parser(prog_name)
266+
parser.add_argument(
267+
'endpoint',
268+
metavar='<endpoint>',
269+
help=_('Endpoint to dissociate from '
270+
'specified project (name or ID)'),
271+
)
272+
parser.add_argument(
273+
'project',
274+
metavar='<project>',
275+
help=_('Project to dissociate from '
276+
'specified endpoint name or ID)'),
277+
)
278+
common.add_project_domain_option_to_parser(parser)
279+
return parser
280+
281+
def take_action(self, parsed_args):
282+
client = self.app.client_manager.identity
283+
284+
endpoint = utils.find_resource(client.endpoints,
285+
parsed_args.endpoint)
286+
287+
project = common.find_project(client,
288+
parsed_args.project,
289+
parsed_args.project_domain)
290+
291+
client.endpoint_filter.delete_endpoint_from_project(
292+
project=project.id,
293+
endpoint=endpoint.id)
294+
295+
183296
class SetEndpoint(command.Command):
184297
_description = _("Set endpoint properties")
185298

openstackclient/tests/functional/identity/v3/common.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ class IdentityTests(base.TestCase):
4242
REGION_LIST_HEADERS = ['Region', 'Parent Region', 'Description']
4343
ENDPOINT_LIST_HEADERS = ['ID', 'Region', 'Service Name', 'Service Type',
4444
'Enabled', 'Interface', 'URL']
45+
ENDPOINT_LIST_PROJECT_HEADERS = ['ID', 'Name']
4546

4647
IDENTITY_PROVIDER_FIELDS = ['description', 'enabled', 'id', 'remote_ids',
4748
'domain_id']

openstackclient/tests/functional/identity/v3/test_endpoint.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,29 @@ def test_endpoint_list(self):
4242
items = self.parse_listing(raw_output)
4343
self.assert_table_structure(items, self.ENDPOINT_LIST_HEADERS)
4444

45+
def test_endpoint_list_filter(self):
46+
endpoint_id = self._create_dummy_endpoint(add_clean_up=False)
47+
project_id = self._create_dummy_project(add_clean_up=False)
48+
raw_output = self.openstack(
49+
'endpoint add project '
50+
'%(endpoint_id)s '
51+
'%(project_id)s' % {
52+
'project_id': project_id,
53+
'endpoint_id': endpoint_id})
54+
self.assertEqual(0, len(raw_output))
55+
raw_output = self.openstack(
56+
'endpoint list --endpoint %s' % endpoint_id)
57+
self.assertIn(project_id, raw_output)
58+
items = self.parse_listing(raw_output)
59+
self.assert_table_structure(items,
60+
self.ENDPOINT_LIST_PROJECT_HEADERS)
61+
62+
raw_output = self.openstack(
63+
'endpoint list --project %s' % project_id)
64+
self.assertIn(endpoint_id, raw_output)
65+
items = self.parse_listing(raw_output)
66+
self.assert_table_structure(items, self.ENDPOINT_LIST_HEADERS)
67+
4568
def test_endpoint_set(self):
4669
endpoint_id = self._create_dummy_endpoint()
4770
new_endpoint_url = data_utils.rand_url()
@@ -65,3 +88,22 @@ def test_endpoint_show(self):
6588
raw_output = self.openstack('endpoint show %s' % endpoint_id)
6689
items = self.parse_show(raw_output)
6790
self.assert_show_fields(items, self.ENDPOINT_FIELDS)
91+
92+
def test_endpoint_add_remove_project(self):
93+
endpoint_id = self._create_dummy_endpoint(add_clean_up=False)
94+
project_id = self._create_dummy_project(add_clean_up=False)
95+
raw_output = self.openstack(
96+
'endpoint add project '
97+
'%(endpoint_id)s '
98+
'%(project_id)s' % {
99+
'project_id': project_id,
100+
'endpoint_id': endpoint_id})
101+
self.assertEqual(0, len(raw_output))
102+
103+
raw_output = self.openstack(
104+
'endpoint remove project '
105+
'%(endpoint_id)s '
106+
'%(project_id)s' % {
107+
'project_id': project_id,
108+
'endpoint_id': endpoint_id})
109+
self.assertEqual(0, len(raw_output))

openstackclient/tests/unit/identity/v3/fakes.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -493,6 +493,8 @@ def __init__(self, **kwargs):
493493
self.credentials.resource_class = fakes.FakeResource(None, {})
494494
self.endpoints = mock.Mock()
495495
self.endpoints.resource_class = fakes.FakeResource(None, {})
496+
self.endpoint_filter = mock.Mock()
497+
self.endpoint_filter.resource_class = fakes.FakeResource(None, {})
496498
self.groups = mock.Mock()
497499
self.groups.resource_class = fakes.FakeResource(None, {})
498500
self.oauth1 = mock.Mock()
@@ -911,6 +913,31 @@ def create_one_endpoint(attrs=None):
911913
loaded=True)
912914
return endpoint
913915

916+
@staticmethod
917+
def create_one_endpoint_filter(attrs=None):
918+
"""Create a fake endpoint project relationship.
919+
920+
:param Dictionary attrs:
921+
A dictionary with all attributes of endpoint filter
922+
:return:
923+
A FakeResource object with project, endpoint and so on
924+
"""
925+
attrs = attrs or {}
926+
927+
# Set default attribute
928+
endpoint_filter_info = {
929+
'project': 'project-id-' + uuid.uuid4().hex,
930+
'endpoint': 'endpoint-id-' + uuid.uuid4().hex,
931+
}
932+
933+
# Overwrite default attributes if there are some attributes set
934+
endpoint_filter_info.update(attrs)
935+
936+
endpoint_filter = fakes.FakeModel(
937+
copy.deepcopy(endpoint_filter_info))
938+
939+
return endpoint_filter
940+
914941

915942
class FakeService(object):
916943
"""Fake one or more service."""

0 commit comments

Comments
 (0)