diff options
author | Jenkins <jenkins@review.openstack.org> | 2014-04-17 02:41:38 +0000 |
---|---|---|
committer | Gerrit Code Review <review@openstack.org> | 2014-04-17 02:41:38 +0000 |
commit | db8da0de2e982b4bdb622eee8aa04f05fc9cb646 (patch) | |
tree | b86f58b68f948a553c7195ed9c90cffef00db919 | |
parent | b333d66da8574096777711daf7229340cb9bdd5f (diff) | |
parent | eff1960daf341a0e23876c31300ee89378250e00 (diff) | |
download | python-keystoneclient-0.8.0.tar.gz |
Merge "Implement endpoint filtering functionality on the client side."0.8.0
-rw-r--r-- | keystoneclient/tests/v3/test_endpoint_filter.py | 153 | ||||
-rw-r--r-- | keystoneclient/v3/client.py | 2 | ||||
-rw-r--r-- | keystoneclient/v3/contrib/endpoint_filter.py | 86 |
3 files changed, 241 insertions, 0 deletions
diff --git a/keystoneclient/tests/v3/test_endpoint_filter.py b/keystoneclient/tests/v3/test_endpoint_filter.py new file mode 100644 index 0000000..f3e283c --- /dev/null +++ b/keystoneclient/tests/v3/test_endpoint_filter.py @@ -0,0 +1,153 @@ +# Copyright 2014 OpenStack Foundation +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import uuid + +import httpretty + +from keystoneclient.tests.v3 import utils + + +class EndpointFilterTests(utils.TestCase): + """Test project-endpoint associations (a.k.a. EndpointFilter Extension). + + Endpoint filter provides associations between service endpoints and + projects. These assciations are then used to create ad-hoc catalogs for + each project-scoped token request. + + """ + + def setUp(self): + super(EndpointFilterTests, self).setUp() + self.manager = self.client.endpoint_filter + + def new_ref(self, **kwargs): + # copied from CrudTests as we need to create endpoint and project + # refs for our tests. EndpointFilter is not exactly CRUD API. + kwargs.setdefault('id', uuid.uuid4().hex) + kwargs.setdefault('enabled', True) + return kwargs + + def new_endpoint_ref(self, **kwargs): + # copied from EndpointTests as we need endpoint refs for our tests + kwargs = self.new_ref(**kwargs) + kwargs.setdefault('interface', 'public') + kwargs.setdefault('region', uuid.uuid4().hex) + kwargs.setdefault('service_id', uuid.uuid4().hex) + kwargs.setdefault('url', uuid.uuid4().hex) + return kwargs + + def new_project_ref(self, **kwargs): + # copied from ProjectTests as we need project refs for our tests + kwargs = self.new_ref(**kwargs) + kwargs.setdefault('domain_id', uuid.uuid4().hex) + kwargs.setdefault('name', uuid.uuid4().hex) + return kwargs + + @httpretty.activate + def test_add_endpoint_to_project_via_id(self): + endpoint_id = uuid.uuid4().hex + project_id = uuid.uuid4().hex + + self.stub_url(httpretty.PUT, + [self.manager.OS_EP_FILTER_EXT, 'projects', project_id, + 'endpoints', endpoint_id], + status=201) + + self.manager.add_endpoint_to_project(project=project_id, + endpoint=endpoint_id) + + @httpretty.activate + def test_add_endpoint_to_project_via_obj(self): + project_ref = self.new_project_ref() + endpoint_ref = self.new_endpoint_ref() + project = self.client.projects.resource_class(self.client.projects, + project_ref, + loaded=True) + endpoint = self.client.endpoints.resource_class(self.client.endpoints, + endpoint_ref, + loaded=True) + + self.stub_url(httpretty.PUT, + [self.manager.OS_EP_FILTER_EXT, + 'projects', project_ref['id'], + 'endpoints', endpoint_ref['id']], + status=201) + + self.manager.add_endpoint_to_project(project=project, + endpoint=endpoint) + + @httpretty.activate + def test_delete_endpoint_from_project(self): + endpoint_id = uuid.uuid4().hex + project_id = uuid.uuid4().hex + + self.stub_url(httpretty.DELETE, + [self.manager.OS_EP_FILTER_EXT, 'projects', project_id, + 'endpoints', endpoint_id], + status=201) + + self.manager.delete_endpoint_from_project(project=project_id, + endpoint=endpoint_id) + + @httpretty.activate + def test_check_endpoint_in_project(self): + endpoint_id = uuid.uuid4().hex + project_id = uuid.uuid4().hex + + self.stub_url(httpretty.HEAD, + [self.manager.OS_EP_FILTER_EXT, 'projects', project_id, + 'endpoints', endpoint_id], + status=201) + + self.manager.check_endpoint_in_project(project=project_id, + endpoint=endpoint_id) + + @httpretty.activate + def test_list_endpoints_for_project(self): + project_id = uuid.uuid4().hex + endpoints = {'endpoints': [self.new_endpoint_ref(), + self.new_endpoint_ref()]} + self.stub_url(httpretty.GET, + [self.manager.OS_EP_FILTER_EXT, 'projects', project_id, + 'endpoints'], + json=endpoints, + status=200) + + endpoints_resp = self.manager.list_endpoints_for_project( + project=project_id) + + expected_endpoint_ids = [ + endpoint['id'] for endpoint in endpoints['endpoints']] + actual_endpoint_ids = [endpoint.id for endpoint in endpoints_resp] + self.assertEqual(expected_endpoint_ids, actual_endpoint_ids) + + @httpretty.activate + def test_list_projects_for_endpoint(self): + endpoint_id = uuid.uuid4().hex + projects = {'projects': [self.new_project_ref(), + self.new_project_ref()]} + self.stub_url(httpretty.GET, + [self.manager.OS_EP_FILTER_EXT, 'endpoints', endpoint_id, + 'projects'], + json=projects, + status=200) + + projects_resp = self.manager.list_projects_for_endpoint( + endpoint=endpoint_id) + + expected_project_ids = [ + project['id'] for project in projects['projects']] + actual_project_ids = [project.id for project in projects_resp] + self.assertEqual(expected_project_ids, actual_project_ids) diff --git a/keystoneclient/v3/client.py b/keystoneclient/v3/client.py index 65ba5d7..417fdb2 100644 --- a/keystoneclient/v3/client.py +++ b/keystoneclient/v3/client.py @@ -19,6 +19,7 @@ from keystoneclient.auth.identity import v3 as v3_auth from keystoneclient import exceptions from keystoneclient import httpclient from keystoneclient.openstack.common import jsonutils +from keystoneclient.v3.contrib import endpoint_filter from keystoneclient.v3.contrib import federation from keystoneclient.v3.contrib import trusts from keystoneclient.v3 import credentials @@ -104,6 +105,7 @@ class Client(httpclient.HTTPClient): self.services = services.ServiceManager(self) self.users = users.UserManager(self) self.trusts = trusts.TrustManager(self) + self.endpoint_filter = endpoint_filter.EndpointFilterManager(self) # DEPRECATED: if session is passed then we go to the new behaviour of # authenticating on the first required call. diff --git a/keystoneclient/v3/contrib/endpoint_filter.py b/keystoneclient/v3/contrib/endpoint_filter.py new file mode 100644 index 0000000..c0a1eef --- /dev/null +++ b/keystoneclient/v3/contrib/endpoint_filter.py @@ -0,0 +1,86 @@ +# Copyright 2014 OpenStack Foundation +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from keystoneclient import base +from keystoneclient import exceptions + + +class EndpointFilterManager(base.Manager): + """Manager class for manipulating project-endpoint associations.""" + OS_EP_FILTER_EXT = '/OS-EP-FILTER' + + def _build_base_url(self, project=None, endpoint=None): + project_id = base.getid(project) + endpoint_id = base.getid(endpoint) + + if project_id and endpoint_id: + api_path = '/projects/%s/endpoints/%s' % (project_id, endpoint_id) + elif project_id: + api_path = '/projects/%s/endpoints' % (project_id) + elif endpoint_id: + api_path = '/endpoints/%s/projects' % (endpoint_id) + else: + msg = 'Must specify a project, an endpoint, or both' + raise exceptions.ValidationError(msg) + + return self.OS_EP_FILTER_EXT + api_path + + def add_endpoint_to_project(self, project, endpoint): + """Create a project-endpoint association.""" + if not (project and endpoint): + raise ValueError('project and endpoint are required') + + base_url = self._build_base_url(project=project, + endpoint=endpoint) + return super(EndpointFilterManager, self)._put(url=base_url) + + def delete_endpoint_from_project(self, project, endpoint): + """Remove a project-endpoint association.""" + if not (project and endpoint): + raise ValueError('project and endpoint are required') + + base_url = self._build_base_url(project=project, + endpoint=endpoint) + return super(EndpointFilterManager, self)._delete(url=base_url) + + def check_endpoint_in_project(self, project, endpoint): + """Checks if project-endpoint association exist.""" + if not (project and endpoint): + raise ValueError('project and endpoint are required') + + base_url = self._build_base_url(project=project, + endpoint=endpoint) + return super(EndpointFilterManager, self)._head(url=base_url) + + def list_endpoints_for_project(self, project): + """List all endpoints for a given project.""" + if not project: + raise ValueError('project is required') + + base_url = self._build_base_url(project=project) + return super(EndpointFilterManager, self)._list( + base_url, + self.client.endpoints.collection_key, + obj_class=self.client.endpoints.resource_class) + + def list_projects_for_endpoint(self, endpoint): + """List all projects for a given endpoint.""" + if not endpoint: + raise ValueError('endpoint is required') + + base_url = self._build_base_url(endpoint=endpoint) + return super(EndpointFilterManager, self)._list( + base_url, + self.client.projects.collection_key, + obj_class=self.client.projects.resource_class) |