summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJenkins <jenkins@review.openstack.org>2014-04-17 02:41:38 +0000
committerGerrit Code Review <review@openstack.org>2014-04-17 02:41:38 +0000
commitdb8da0de2e982b4bdb622eee8aa04f05fc9cb646 (patch)
treeb86f58b68f948a553c7195ed9c90cffef00db919
parentb333d66da8574096777711daf7229340cb9bdd5f (diff)
parenteff1960daf341a0e23876c31300ee89378250e00 (diff)
downloadpython-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.py153
-rw-r--r--keystoneclient/v3/client.py2
-rw-r--r--keystoneclient/v3/contrib/endpoint_filter.py86
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)