From 2af709ceb6cf20f37f79baed1d93a14874b49a5b Mon Sep 17 00:00:00 2001 From: Dolph Mathews Date: Tue, 11 Sep 2012 11:40:25 -0500 Subject: [PATCH] v3 Endpoint CRUD Change-Id: Iff60668a80f8a6679a691a8f256652d7814f2785 --- keystoneclient/v3/client.py | 2 + keystoneclient/v3/endpoints.py | 80 ++++++++++++++++++++++++++++++++++ tests/v3/test_endpoints.py | 77 ++++++++++++++++++++++++++++++++ tests/v3/utils.py | 24 +++++----- 4 files changed, 171 insertions(+), 12 deletions(-) create mode 100644 keystoneclient/v3/endpoints.py create mode 100644 tests/v3/test_endpoints.py diff --git a/keystoneclient/v3/client.py b/keystoneclient/v3/client.py index 46751574c..37774202b 100644 --- a/keystoneclient/v3/client.py +++ b/keystoneclient/v3/client.py @@ -16,6 +16,7 @@ import json import logging from keystoneclient.v2_0 import client +from keystoneclient.v3 import endpoints from keystoneclient.v3 import services @@ -58,6 +59,7 @@ class Client(client.Client): """ Initialize a new client for the Keystone v2.0 API. """ super(Client, self).__init__(endpoint=endpoint, **kwargs) + self.endpoints = endpoints.EndpointManager(self) self.services = services.ServiceManager(self) # NOTE(gabriel): If we have a pre-defined endpoint then we can diff --git a/keystoneclient/v3/endpoints.py b/keystoneclient/v3/endpoints.py new file mode 100644 index 000000000..ff4a678b0 --- /dev/null +++ b/keystoneclient/v3/endpoints.py @@ -0,0 +1,80 @@ +# Copyright 2011 OpenStack LLC. +# Copyright 2011 Nebula, Inc. +# All Rights Reserved. +# +# 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 + + +VALID_INTERFACES = ['public', 'admin', 'internal'] + + +class Endpoint(base.Resource): + """Represents an Identity endpoint. + + Attributes: + * id: a uuid that identifies the endpoint + * interface: 'public', 'admin' or 'internal' network interface + * region: geographic location of the endpoint + * service_id: service to which the endpoint belongs + * url: fully qualified service endpoint + + """ + pass + + +class EndpointManager(base.CrudManager): + """Manager class for manipulating Identity endpoints.""" + resource_class = Endpoint + collection_key = 'endpoints' + key = 'endpoint' + + def _validate_interface(self, interface): + if interface is not None and interface not in VALID_INTERFACES: + msg = '"interface" must be one of: %s' + msg = msg % ', '.join(VALID_INTERFACES) + raise Exception(msg) + + def create(self, service, url, name=None, interface=None, region=None): + self._validate_interface(interface) + return super(EndpointManager, self).create( + service_id=base.getid(service), + interface=interface, + url=url, + region=region) + + def get(self, endpoint): + return super(EndpointManager, self).get( + endpoint_id=base.getid(endpoint)) + + def list(self, service=None, name=None, interface=None, region=None): + self._validate_interface(interface) + return super(EndpointManager, self).list( + service_id=base.getid(service), + interface=interface, + region=region) + + def update(self, endpoint, service=None, url=None, name=None, + interface=None, region=None): + self._validate_interface(interface) + return super(EndpointManager, self).update( + endpoint_id=base.getid(endpoint), + service_id=base.getid(service), + interface=interface, + url=url, + region=region) + + def delete(self, endpoint): + return super(EndpointManager, self).delete( + endpoint_id=base.getid(endpoint)) diff --git a/tests/v3/test_endpoints.py b/tests/v3/test_endpoints.py new file mode 100644 index 000000000..f0a6e4266 --- /dev/null +++ b/tests/v3/test_endpoints.py @@ -0,0 +1,77 @@ +import uuid + +from keystoneclient.v3 import endpoints +from tests.v3 import utils + + +class EndpointTests(utils.TestCase, utils.CrudTests): + def setUp(self): + super(EndpointTests, self).setUp() + self.additionalSetUp() + self.key = 'endpoint' + self.collection_key = 'endpoints' + self.model = endpoints.Endpoint + self.manager = self.client.endpoints + + def new_ref(self, **kwargs): + kwargs = super(EndpointTests, 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 test_create_public_interface(self): + ref = self.new_ref(interface='public') + self.test_create(ref) + + def test_create_admin_interface(self): + ref = self.new_ref(interface='admin') + self.test_create(ref) + + def test_create_internal_interface(self): + ref = self.new_ref(interface='internal') + self.test_create(ref) + + def test_create_invalid_interface(self): + ref = self.new_ref(interface=uuid.uuid4().hex) + with self.assertRaises(Exception): + self.manager.create(**utils.parameterize(ref)) + + def test_update_public_interface(self): + ref = self.new_ref(interface='public') + self.test_update(ref) + + def test_update_admin_interface(self): + ref = self.new_ref(interface='admin') + self.test_update(ref) + + def test_update_internal_interface(self): + ref = self.new_ref(interface='internal') + self.test_update(ref) + + def test_update_invalid_interface(self): + ref = self.new_ref(interface=uuid.uuid4().hex) + with self.assertRaises(Exception): + self.manager.update(**utils.parameterize(ref)) + + def test_list_public_interface(self): + interface = 'public' + expected_path = 'v3/%s?interface=%s' % (self.collection_key, interface) + self.test_list(expected_path=expected_path, interface=interface) + + def test_list_admin_interface(self): + interface = 'admin' + expected_path = 'v3/%s?interface=%s' % (self.collection_key, interface) + self.test_list(expected_path=expected_path, interface=interface) + + def test_list_internal_interface(self): + interface = 'admin' + expected_path = 'v3/%s?interface=%s' % (self.collection_key, interface) + self.test_list(expected_path=expected_path, interface=interface) + + def test_list_invalid_interface(self): + interface = uuid.uuid4().hex + expected_path = 'v3/%s?interface=%s' % (self.collection_key, interface) + with self.assertRaises(Exception): + self.manager.list(expected_path=expected_path, interface=interface) diff --git a/tests/v3/utils.py b/tests/v3/utils.py index ee71c1115..af8d68bbe 100644 --- a/tests/v3/utils.py +++ b/tests/v3/utils.py @@ -103,8 +103,8 @@ class CrudTests(object): return json.dumps({self.collection_key: entity}, sort_keys=True) raise NotImplementedError('Are you sure you want to serialize that?') - def test_create(self): - ref = self.new_ref() + def test_create(self, ref=None): + ref = ref or self.new_ref() resp = httplib2.Response({ 'status': 201, 'body': self.serialize(ref), @@ -131,8 +131,8 @@ class CrudTests(object): ref[attr], 'Expected different %s' % attr) - def test_get(self): - ref = self.new_ref() + def test_get(self, ref=None): + ref = ref or self.new_ref() resp = httplib2.Response({ 'status': 200, 'body': self.serialize(ref), @@ -155,8 +155,8 @@ class CrudTests(object): ref[attr], 'Expected different %s' % attr) - def test_list(self): - ref_list = [self.new_ref(), self.new_ref()] + def test_list(self, ref_list=None, expected_path=None, **filter_kwargs): + ref_list = ref_list or [self.new_ref(), self.new_ref()] resp = httplib2.Response({ 'status': 200, @@ -167,18 +167,18 @@ class CrudTests(object): httplib2.Http.request( urlparse.urljoin( self.TEST_URL, - 'v3/%s' % self.collection_key), + expected_path or 'v3/%s' % self.collection_key), method, headers=self.headers[method]) \ .AndReturn((resp, resp['body'])) self.mox.ReplayAll() - returned_list = self.manager.list() + returned_list = self.manager.list(**filter_kwargs) self.assertTrue(len(returned_list)) [self.assertTrue(isinstance(r, self.model)) for r in returned_list] - def test_update(self): - ref = self.new_ref() + def test_update(self, ref=None): + ref = ref or self.new_ref() req_ref = ref.copy() del req_ref['id'] @@ -206,8 +206,8 @@ class CrudTests(object): ref[attr], 'Expected different %s' % attr) - def test_delete(self): - ref = self.new_ref() + def test_delete(self, ref=None): + ref = ref or self.new_ref() method = 'DELETE' resp = httplib2.Response({ 'status': 204,