diff options
author | Henry Nash <henryn@linux.vnet.ibm.com> | 2014-09-18 09:59:38 +0100 |
---|---|---|
committer | Henry Nash <henryn@linux.vnet.ibm.com> | 2014-09-18 22:27:15 +0100 |
commit | 98b240fe50a4c25f8baf67a9f1192d6637910631 (patch) | |
tree | fee85d970352da8353acc7977122bf1f50f149ea | |
parent | 118763cbe00e00e7b78b926e21e69ec479212b1b (diff) | |
download | python-keystoneclient-98b240fe50a4c25f8baf67a9f1192d6637910631.tar.gz |
Add support for endpoint policy.
This adds the client library class for the endpoint policy extension.
Implements: bp endpoint-policy
Change-Id: I7153d7a093f4299d7f912b0b4a9a02ffacdb9e69
-rw-r--r-- | keystoneclient/tests/v3/test_endpoint_filter.py | 28 | ||||
-rw-r--r-- | keystoneclient/tests/v3/test_endpoint_policy.py | 242 | ||||
-rw-r--r-- | keystoneclient/v3/client.py | 7 | ||||
-rw-r--r-- | keystoneclient/v3/contrib/endpoint_policy.py | 153 |
4 files changed, 418 insertions, 12 deletions
diff --git a/keystoneclient/tests/v3/test_endpoint_filter.py b/keystoneclient/tests/v3/test_endpoint_filter.py index f4be431..867193a 100644 --- a/keystoneclient/tests/v3/test_endpoint_filter.py +++ b/keystoneclient/tests/v3/test_endpoint_filter.py @@ -17,18 +17,8 @@ import uuid 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 +class EndpointTestUtils(object): + """Mixin class with shared methods between Endpoint Filter & Policy.""" def new_ref(self, **kwargs): # copied from CrudTests as we need to create endpoint and project @@ -46,6 +36,20 @@ class EndpointFilterTests(utils.TestCase): kwargs.setdefault('url', uuid.uuid4().hex) return kwargs + +class EndpointFilterTests(utils.TestCase, EndpointTestUtils): + """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_project_ref(self, **kwargs): # copied from ProjectTests as we need project refs for our tests kwargs = self.new_ref(**kwargs) diff --git a/keystoneclient/tests/v3/test_endpoint_policy.py b/keystoneclient/tests/v3/test_endpoint_policy.py new file mode 100644 index 0000000..59a9079 --- /dev/null +++ b/keystoneclient/tests/v3/test_endpoint_policy.py @@ -0,0 +1,242 @@ +# Copyright 2014 IBM Corp. +# +# 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 + +from keystoneclient.tests.v3 import test_endpoint_filter +from keystoneclient.tests.v3 import utils + + +class EndpointPolicyTests(utils.TestCase, + test_endpoint_filter.EndpointTestUtils): + """Test policy-endpoint associations (a.k.a. EndpointPolicy Extension).""" + + def setUp(self): + super(EndpointPolicyTests, self).setUp() + self.manager = self.client.endpoint_policy + + def new_policy_ref(self, **kwargs): + kwargs.setdefault('id', uuid.uuid4().hex) + kwargs.setdefault('type', uuid.uuid4().hex) + kwargs.setdefault('blob', uuid.uuid4().hex) + return kwargs + + def new_region_ref(self, **kwargs): + kwargs = self.new_ref(**kwargs) + return kwargs + + def new_service_ref(self, **kwargs): + kwargs = self.new_ref(**kwargs) + kwargs.setdefault('name', uuid.uuid4().hex) + kwargs.setdefault('type', uuid.uuid4().hex) + return kwargs + + def _crud_policy_association_for_endpoint_via_id( + self, http_action, manager_action): + policy_id = uuid.uuid4().hex + endpoint_id = uuid.uuid4().hex + + self.stub_url(http_action, + ['policies', policy_id, self.manager.OS_EP_POLICY_EXT, + 'endpoints', endpoint_id], + status_code=204) + manager_action(policy=policy_id, endpoint=endpoint_id) + + def _crud_policy_association_for_endpoint_via_obj( + self, http_action, manager_action): + policy_ref = self.new_policy_ref() + endpoint_ref = self.new_endpoint_ref() + policy = self.client.projects.resource_class( + self.client.policies, policy_ref, loaded=True) + endpoint = self.client.endpoints.resource_class( + self.client.endpoints, endpoint_ref, loaded=True) + + self.stub_url(http_action, + ['policies', policy_ref['id'], + self.manager.OS_EP_POLICY_EXT, + 'endpoints', endpoint_ref['id']], + status_code=204) + manager_action(policy=policy, endpoint=endpoint) + + def test_create_policy_association_for_endpoint_via_id(self): + self._crud_policy_association_for_endpoint_via_id( + 'PUT', self.manager.create_policy_association_for_endpoint) + + def test_create_policy_association_for_endpoint_via_obj(self): + self._crud_policy_association_for_endpoint_via_obj( + 'PUT', self.manager.create_policy_association_for_endpoint) + + def test_check_policy_association_for_endpoint_via_id(self): + self._crud_policy_association_for_endpoint_via_id( + 'HEAD', self.manager.check_policy_association_for_endpoint) + + def test_check_policy_association_for_endpoint_via_obj(self): + self._crud_policy_association_for_endpoint_via_obj( + 'HEAD', self.manager.check_policy_association_for_endpoint) + + def test_delete_policy_association_for_endpoint_via_id(self): + self._crud_policy_association_for_endpoint_via_id( + 'DELETE', self.manager.delete_policy_association_for_endpoint) + + def test_delete_policy_association_for_endpoint_via_obj(self): + self._crud_policy_association_for_endpoint_via_obj( + 'DELETE', self.manager.delete_policy_association_for_endpoint) + + def _crud_policy_association_for_service_via_id( + self, http_action, manager_action): + policy_id = uuid.uuid4().hex + service_id = uuid.uuid4().hex + + self.stub_url(http_action, + ['policies', policy_id, self.manager.OS_EP_POLICY_EXT, + 'services', service_id], + status_code=204) + manager_action(policy=policy_id, service=service_id) + + def _crud_policy_association_for_service_via_obj( + self, http_action, manager_action): + policy_ref = self.new_policy_ref() + service_ref = self.new_service_ref() + policy = self.client.projects.resource_class( + self.client.policies, policy_ref, loaded=True) + service = self.client.services.resource_class( + self.client.services, service_ref, loaded=True) + + self.stub_url(http_action, + ['policies', policy_ref['id'], + self.manager.OS_EP_POLICY_EXT, + 'services', service_ref['id']], + status_code=204) + manager_action(policy=policy, service=service) + + def test_create_policy_association_for_service_via_id(self): + self._crud_policy_association_for_service_via_id( + 'PUT', self.manager.create_policy_association_for_service) + + def test_create_policy_association_for_service_via_obj(self): + self._crud_policy_association_for_service_via_obj( + 'PUT', self.manager.create_policy_association_for_service) + + def test_check_policy_association_for_service_via_id(self): + self._crud_policy_association_for_service_via_id( + 'HEAD', self.manager.check_policy_association_for_service) + + def test_check_policy_association_for_service_via_obj(self): + self._crud_policy_association_for_service_via_obj( + 'HEAD', self.manager.check_policy_association_for_service) + + def test_delete_policy_association_for_service_via_id(self): + self._crud_policy_association_for_service_via_id( + 'DELETE', self.manager.delete_policy_association_for_service) + + def test_delete_policy_association_for_service_via_obj(self): + self._crud_policy_association_for_service_via_obj( + 'DELETE', self.manager.delete_policy_association_for_service) + + def _crud_policy_association_for_region_and_service_via_id( + self, http_action, manager_action): + policy_id = uuid.uuid4().hex + region_id = uuid.uuid4().hex + service_id = uuid.uuid4().hex + + self.stub_url(http_action, + ['policies', policy_id, self.manager.OS_EP_POLICY_EXT, + 'services', service_id, 'regions', region_id], + status_code=204) + manager_action(policy=policy_id, region=region_id, service=service_id) + + def _crud_policy_association_for_region_and_service_via_obj( + self, http_action, manager_action): + policy_ref = self.new_policy_ref() + region_ref = self.new_region_ref() + service_ref = self.new_service_ref() + policy = self.client.projects.resource_class( + self.client.policies, policy_ref, loaded=True) + region = self.client.regions.resource_class( + self.client.regions, region_ref, loaded=True) + service = self.client.services.resource_class( + self.client.services, service_ref, loaded=True) + + self.stub_url(http_action, + ['policies', policy_ref['id'], + self.manager.OS_EP_POLICY_EXT, + 'services', service_ref['id'], + 'regions', region_ref['id']], + status_code=204) + manager_action(policy=policy, region=region, service=service) + + def test_create_policy_association_for_region_and_service_via_id(self): + self._crud_policy_association_for_region_and_service_via_id( + 'PUT', + self.manager.create_policy_association_for_region_and_service) + + def test_create_policy_association_for_region_and_service_via_obj(self): + self._crud_policy_association_for_region_and_service_via_obj( + 'PUT', + self.manager.create_policy_association_for_region_and_service) + + def test_check_policy_association_for_region_and_service_via_id(self): + self._crud_policy_association_for_region_and_service_via_id( + 'HEAD', + self.manager.check_policy_association_for_region_and_service) + + def test_check_policy_association_for_region_and_service_via_obj(self): + self._crud_policy_association_for_region_and_service_via_obj( + 'HEAD', + self.manager.check_policy_association_for_region_and_service) + + def test_delete_policy_association_for_region_and_service_via_id(self): + self._crud_policy_association_for_region_and_service_via_id( + 'DELETE', + self.manager.delete_policy_association_for_region_and_service) + + def test_delete_policy_association_for_region_and_service_via_obj(self): + self._crud_policy_association_for_region_and_service_via_obj( + 'DELETE', + self.manager.delete_policy_association_for_region_and_service) + + def test_get_policy_for_endpoint(self): + endpoint_id = uuid.uuid4().hex + expected_policy = self.new_policy_ref() + + self.stub_url('GET', + ['endpoints', endpoint_id, self.manager.OS_EP_POLICY_EXT, + 'policy'], + json={'policy': expected_policy}, + status_code=200) + + policy_resp = self.manager.get_policy_for_endpoint( + endpoint=endpoint_id) + + self.assertEqual(expected_policy['id'], policy_resp.id) + self.assertEqual(expected_policy['blob'], policy_resp.blob) + self.assertEqual(expected_policy['type'], policy_resp.type) + + def test_list_endpoints_for_policy(self): + policy_id = uuid.uuid4().hex + endpoints = {'endpoints': [self.new_endpoint_ref(), + self.new_endpoint_ref()]} + self.stub_url('GET', + ['policies', policy_id, self.manager.OS_EP_POLICY_EXT, + 'endpoints'], + json=endpoints, + status_code=200) + + endpoints_resp = self.manager.list_endpoints_for_policy( + policy=policy_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) diff --git a/keystoneclient/v3/client.py b/keystoneclient/v3/client.py index 316588c..a271db3 100644 --- a/keystoneclient/v3/client.py +++ b/keystoneclient/v3/client.py @@ -20,6 +20,7 @@ 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 endpoint_policy from keystoneclient.v3.contrib import federation from keystoneclient.v3.contrib import oauth1 from keystoneclient.v3.contrib import trusts @@ -101,6 +102,11 @@ class Client(httpclient.HTTPClient): :py:class:`keystoneclient.v3.contrib.endpoint_filter.\ EndpointFilterManager` + .. py:attribute:: endpoint_policy + + :py:class:`keystoneclient.v3.contrib.endpoint_policy.\ +EndpointPolicyManager` + .. py:attribute:: endpoints :py:class:`keystoneclient.v3.endpoints.EndpointManager` @@ -163,6 +169,7 @@ EndpointFilterManager` self.credentials = credentials.CredentialManager(self) self.endpoint_filter = endpoint_filter.EndpointFilterManager(self) + self.endpoint_policy = endpoint_policy.EndpointPolicyManager(self) self.endpoints = endpoints.EndpointManager(self) self.domains = domains.DomainManager(self) self.federation = federation.FederationManager(self) diff --git a/keystoneclient/v3/contrib/endpoint_policy.py b/keystoneclient/v3/contrib/endpoint_policy.py new file mode 100644 index 0000000..9d4d997 --- /dev/null +++ b/keystoneclient/v3/contrib/endpoint_policy.py @@ -0,0 +1,153 @@ +# Copyright 2014 IBM Corp. +# +# 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.v3 import policies + + +class EndpointPolicyManager(base.Manager): + """Manager class for manipulating endpoint-policy associations.""" + + OS_EP_POLICY_EXT = 'OS-ENDPOINT-POLICY' + + def _act_on_policy_association_for_endpoint( + self, policy, endpoint, action): + if not (policy and endpoint): + raise ValueError('policy and endpoint are required') + + policy_id = base.getid(policy) + endpoint_id = base.getid(endpoint) + url = ('/policies/%(policy_id)s/%(ext_name)s' + '/endpoints/%(endpoint_id)s') % { + 'policy_id': policy_id, + 'ext_name': self.OS_EP_POLICY_EXT, + 'endpoint_id': endpoint_id} + return action(url=url) + + def create_policy_association_for_endpoint(self, policy, endpoint): + """Create an association between a policy and an endpoint.""" + self._act_on_policy_association_for_endpoint( + policy, endpoint, self._put) + + def check_policy_association_for_endpoint(self, policy, endpoint): + """Check an association between a policy and an endpoint.""" + self._act_on_policy_association_for_endpoint( + policy, endpoint, self._head) + + def delete_policy_association_for_endpoint(self, policy, endpoint): + """Delete an association between a policy and an endpoint.""" + self._act_on_policy_association_for_endpoint( + policy, endpoint, self._delete) + + def _act_on_policy_association_for_service(self, policy, service, action): + if not (policy and service): + raise ValueError('policy and service are required') + + policy_id = base.getid(policy) + service_id = base.getid(service) + url = ('/policies/%(policy_id)s/%(ext_name)s' + '/services/%(service_id)s') % { + 'policy_id': policy_id, + 'ext_name': self.OS_EP_POLICY_EXT, + 'service_id': service_id} + return action(url=url) + + def create_policy_association_for_service(self, policy, service): + """Create an association between a policy and a service.""" + self._act_on_policy_association_for_service( + policy, service, self._put) + + def check_policy_association_for_service(self, policy, service): + """Check an association between a policy and a service.""" + self._act_on_policy_association_for_service( + policy, service, self._head) + + def delete_policy_association_for_service(self, policy, service): + """Delete an association between a policy and a service.""" + self._act_on_policy_association_for_service( + policy, service, self._delete) + + def _act_on_policy_association_for_region_and_service( + self, policy, region, service, action): + if not (policy and region and service): + raise ValueError('policy, region and service are required') + + policy_id = base.getid(policy) + region_id = base.getid(region) + service_id = base.getid(service) + url = ('/policies/%(policy_id)s/%(ext_name)s' + '/services/%(service_id)s/regions/%(region_id)s') % { + 'policy_id': policy_id, + 'ext_name': self.OS_EP_POLICY_EXT, + 'service_id': service_id, + 'region_id': region_id} + return action(url=url) + + def create_policy_association_for_region_and_service( + self, policy, region, service): + """Create an association between a policy and a service in a region.""" + self._act_on_policy_association_for_region_and_service( + policy, region, service, self._put) + + def check_policy_association_for_region_and_service( + self, policy, region, service): + """Check an association between a policy and a service in a region.""" + self._act_on_policy_association_for_region_and_service( + policy, region, service, self._head) + + def delete_policy_association_for_region_and_service( + self, policy, region, service): + """Delete an association between a policy and a service in a region.""" + self._act_on_policy_association_for_region_and_service( + policy, region, service, self._delete) + + def get_policy_for_endpoint(self, endpoint): + """Get the effective policy for an endpoint. + + :param endpoint: endpoint object or ID + + :returns: policies.Policy object + + """ + if not endpoint: + raise ValueError('endpoint is required') + + endpoint_id = base.getid(endpoint) + url = ('/endpoints/%(endpoint_id)s/%(ext_name)s/policy') % { + 'endpoint_id': endpoint_id, + 'ext_name': self.OS_EP_POLICY_EXT} + + _resp, body = self.client.get(url) + return policies.Policy( + self, body[policies.PolicyManager.key], loaded=True) + + def list_endpoints_for_policy(self, policy): + """List endpoints with the effective association to a policy. + + :param policy: policy object or ID + + :returns: list of endpoints that are associated with the policy + + """ + if not policy: + raise ValueError('policy is required') + + policy_id = base.getid(policy) + url = ('/policies/%(policy_id)s/%(ext_name)s/endpoints') % { + 'policy_id': policy_id, + 'ext_name': self.OS_EP_POLICY_EXT} + return self._list( + url, + self.client.endpoints.collection_key, + obj_class=self.client.endpoints.resource_class) |