v3 Catalog

- v3 catalog tests (bug 1023933)
- v3 catalog implementation (bug 1023938)

Change-Id: Ie118819d25afbff62327ffc8be5b5fda2ef7f4ed
This commit is contained in:
Dolph Mathews 2012-11-20 10:28:26 -06:00
parent 64452c6b55
commit ff669f0da9
8 changed files with 263 additions and 87 deletions

View File

@ -24,11 +24,7 @@ class Catalog(kvs.Base, catalog.Driver):
def get_catalog(self, user_id, tenant_id, metadata=None):
return self.db.get('catalog-%s-%s' % (tenant_id, user_id))
def get_service(self, service_id):
return self.db.get('service-%s' % service_id)
def list_services(self):
return self.db.get('service_list', [])
# service crud
def create_service(self, service_id, service):
self.db.set('service-%s' % service_id, service)
@ -37,16 +33,53 @@ class Catalog(kvs.Base, catalog.Driver):
self.db.set('service_list', list(service_list))
return service
def list_services(self):
return [self.get_service(x) for x in self.db.get('service_list', [])]
def get_service(self, service_id):
return self.db.get('service-%s' % service_id)
def update_service(self, service_id, service):
self.db.set('service-%s' % service_id, service)
return service
def delete_service(self, service_id):
# delete referencing endpoints
for endpoint_id in self.db.get('endpoint_list', []):
if self.get_endpoint(endpoint_id)['service_id'] == service_id:
self.delete_endpoint(endpoint_id)
self.db.delete('service-%s' % service_id)
service_list = set(self.db.get('service_list', []))
service_list.remove(service_id)
self.db.set('service_list', list(service_list))
# endpoint crud
def create_endpoint(self, endpoint_id, endpoint):
self.get_service(endpoint['service_id'])
self.db.set('endpoint-%s' % endpoint_id, endpoint)
endpoint_list = set(self.db.get('endpoint_list', []))
endpoint_list.add(endpoint_id)
self.db.set('endpoint_list', list(endpoint_list))
return endpoint
def list_endpoints(self):
return [self.get_endpoint(x) for x in self.db.get('endpoint_list', [])]
def get_endpoint(self, endpoint_id):
return self.db.get('endpoint-%s' % endpoint_id)
def update_endpoint(self, endpoint_id, endpoint):
self.db.set('endpoint-%s' % endpoint_id, endpoint)
return endpoint
def delete_endpoint(self, endpoint_id):
self.db.delete('endpoint-%s' % endpoint_id)
endpoint_list = set(self.db.get('endpoint_list', []))
endpoint_list.remove(endpoint_id)
self.db.set('endpoint_list', list(endpoint_list))
# Private interface
def _create_catalog(self, user_id, tenant_id, data):
self.db.set('catalog-%s-%s' % (tenant_id, user_id), data)

View File

@ -52,22 +52,25 @@ class Catalog(sql.Base, catalog.Driver):
# Services
def list_services(self):
session = self.get_session()
services = session.query(Service)
return [s['id'] for s in list(services)]
services = session.query(Service).all()
return [s.to_dict() for s in list(services)]
def _get_service(self, session, service_id):
try:
return session.query(Service).filter_by(id=service_id).one()
except sql.NotFound:
raise exception.ServiceNotFound(service_id=service_id)
def get_service(self, service_id):
session = self.get_session()
service_ref = session.query(Service).filter_by(id=service_id).first()
if not service_ref:
raise exception.ServiceNotFound(service_id=service_id)
return service_ref.to_dict()
return self._get_service(session, service_id).to_dict()
def delete_service(self, service_id):
session = self.get_session()
with session.begin():
ref = self._get_service(session, service_id)
session.query(Endpoint).filter_by(service_id=service_id).delete()
if not session.query(Service).filter_by(id=service_id).delete():
raise exception.ServiceNotFound(service_id=service_id)
session.delete(ref)
session.flush()
def create_service(self, service_id, service_ref):
@ -78,6 +81,18 @@ class Catalog(sql.Base, catalog.Driver):
session.flush()
return service.to_dict()
def update_service(self, service_id, service_ref):
session = self.get_session()
with session.begin():
ref = self._get_service(session, service_id)
old_dict = ref.to_dict()
old_dict.update(service_ref)
new_service = Service.from_dict(old_dict)
ref.type = new_service.type
ref.extra = new_service.extra
session.flush()
return ref.to_dict()
# Endpoints
def create_endpoint(self, endpoint_id, endpoint_ref):
session = self.get_session()
@ -95,18 +110,33 @@ class Catalog(sql.Base, catalog.Driver):
raise exception.EndpointNotFound(endpoint_id=endpoint_id)
session.flush()
def _get_endpoint(self, session, endpoint_id):
try:
return session.query(Endpoint).filter_by(id=endpoint_id).one()
except sql.NotFound:
raise exception.EndpointNotFound(endpoint_id=endpoint_id)
def get_endpoint(self, endpoint_id):
session = self.get_session()
endpoint_ref = session.query(Endpoint)
endpoint_ref = endpoint_ref.filter_by(id=endpoint_id).first()
if not endpoint_ref:
raise exception.EndpointNotFound(endpoint_id=endpoint_id)
return endpoint_ref.to_dict()
return self._get_endpoint(session, endpoint_id).to_dict()
def list_endpoints(self):
session = self.get_session()
endpoints = session.query(Endpoint)
return [e['id'] for e in list(endpoints)]
return [e.to_dict() for e in list(endpoints)]
def update_endpoint(self, endpoint_id, endpoint_ref):
session = self.get_session()
with session.begin():
ref = self._get_endpoint(session, endpoint_id)
old_dict = ref.to_dict()
old_dict.update(endpoint_ref)
new_endpoint = Endpoint.from_dict(old_dict)
ref.service_id = new_endpoint.service_id
ref.region = new_endpoint.region
ref.extra = new_endpoint.extra
session.flush()
return ref.to_dict()
def get_catalog(self, user_id, tenant_id, metadata=None):
d = dict(CONF.iteritems())
@ -114,8 +144,7 @@ class Catalog(sql.Base, catalog.Driver):
'user_id': user_id})
catalog = {}
endpoints = [self.get_endpoint(e)
for e in self.list_endpoints()]
endpoints = self.list_endpoints()
for ep in endpoints:
service = self.get_service(ep['service_id'])
srv_type = service['type']

View File

@ -116,14 +116,6 @@ class Driver(object):
raise exception.NotImplemented()
def list_services(self):
"""List all service ids in catalog.
:returns: list of service_ids or an empty list.
"""
raise exception.NotImplemented()
def get_all_services(self):
"""List all services.
:returns: list of service_refs or an empty list.
@ -176,14 +168,6 @@ class Driver(object):
raise exception.NotImplemented()
def list_endpoints(self):
"""List all endpoint ids in catalog.
:returns: list of endpoint_ids or an empty list.
"""
raise exception.NotImplemented()
def get_all_endpoints(self):
"""List all endpoints.
:returns: list of endpoint_refs or an empty list.
@ -242,14 +226,10 @@ class ServiceController(wsgi.Application):
self.token_api = token.Manager()
super(ServiceController, self).__init__()
# CRUD extensions
# NOTE(termie): this OS-KSADM stuff is not very consistent
def get_services(self, context):
self.assert_admin(context)
service_list = self.catalog_api.list_services(context)
service_refs = [self.catalog_api.get_service(context, x)
for x in service_list]
return {'OS-KSADM:services': service_refs}
return {'OS-KSADM:services': service_list}
def get_service(self, context, service_id):
self.assert_admin(context)
@ -281,9 +261,7 @@ class EndpointController(wsgi.Application):
def get_endpoints(self, context):
self.assert_admin(context)
endpoint_list = self.catalog_api.list_endpoints(context)
endpoint_refs = [self.catalog_api.get_endpoint(context, e)
for e in endpoint_list]
return {'endpoints': endpoint_refs}
return {'endpoints': endpoint_list}
def create_endpoint(self, context, endpoint):
self.assert_admin(context)
@ -312,7 +290,7 @@ class ServiceControllerV3(controller.V3Controller):
def list_services(self, context):
self.assert_admin(context)
refs = self.catalog_api.get_all_services(context)
refs = self.catalog_api.list_services(context)
refs = self._filter_by_attribute(context, refs, 'type')
return {'services': self._paginate(context, refs)}
@ -351,7 +329,7 @@ class EndpointControllerV3(controller.V3Controller):
def list_endpoints(self, context):
self.assert_admin(context)
refs = self.catalog_api.get_all_endpoints(context)
refs = self.catalog_api.list_endpoints(context)
refs = self._filter_by_attribute(context, refs, 'service_id')
refs = self._filter_by_attribute(context, refs, 'interface')
return {'endpoints': self._paginate(context, refs)}

View File

@ -863,7 +863,7 @@ class CatalogTests(object):
# list
services = self.catalog_api.list_services()
self.assertIn(service_id, services)
self.assertIn(service_id, [x['id'] for x in services])
# delete
self.catalog_api.delete_service(service_id)
@ -872,6 +872,30 @@ class CatalogTests(object):
self.assertRaises(exception.ServiceNotFound,
self.catalog_man.get_service, {}, service_id)
def test_delete_service_with_endpoint(self):
# create a service
service = {
'id': uuid.uuid4().hex,
'type': uuid.uuid4().hex,
'name': uuid.uuid4().hex,
'description': uuid.uuid4().hex,
}
self.catalog_api.create_service(service['id'], service)
# create an endpoint attached to the service
endpoint = {
'id': uuid.uuid4().hex,
'service_id': service['id'],
}
self.catalog_api.create_endpoint(endpoint['id'], endpoint)
# deleting the service should also delete the endpoint
self.catalog_api.delete_service(service['id'])
self.assertRaises(exception.EndpointNotFound,
self.catalog_man.get_endpoint, {}, endpoint['id'])
self.assertRaises(exception.EndpointNotFound,
self.catalog_man.delete_endpoint, {}, endpoint['id'])
def test_get_service_404(self):
self.assertRaises(exception.ServiceNotFound,
self.catalog_man.get_service,
@ -890,18 +914,21 @@ class CatalogTests(object):
'service_id': uuid.uuid4().hex,
}
self.assertRaises(exception.ServiceNotFound,
self.catalog_api.create_endpoint,
self.catalog_man.create_endpoint,
{},
endpoint['id'],
endpoint)
def test_get_endpoint_404(self):
self.assertRaises(exception.EndpointNotFound,
self.catalog_api.get_endpoint,
self.catalog_man.get_endpoint,
{},
uuid.uuid4().hex)
def test_delete_endpoint_404(self):
self.assertRaises(exception.EndpointNotFound,
self.catalog_api.delete_endpoint,
self.catalog_man.delete_endpoint,
{},
uuid.uuid4().hex)

View File

@ -68,19 +68,3 @@ class KvsCatalog(test.TestCase, test_backend.CatalogTests):
def test_get_catalog(self):
catalog_ref = self.catalog_api.get_catalog('foo', 'bar')
self.assertDictEqual(catalog_ref, self.catalog_foobar)
def test_create_endpoint_404(self):
self.assertRaises(exception.NotImplemented,
self.catalog_api.create_endpoint,
uuid.uuid4().hex,
{})
def test_get_endpoint_404(self):
self.assertRaises(exception.NotImplemented,
self.catalog_api.get_endpoint,
uuid.uuid4().hex)
def test_delete_endpoint_404(self):
self.assertRaises(exception.NotImplemented,
self.catalog_api.delete_endpoint,
uuid.uuid4().hex)

View File

@ -15,7 +15,6 @@
# under the License.
import os
import uuid
from keystone import catalog
from keystone.catalog.backends import templated as catalog_templated
@ -67,19 +66,3 @@ class TestTemplatedCatalog(test.TestCase, test_backend.CatalogTests):
'http://localhost:$(compute_port)s/v1.1/$(tenant)s'
with self.assertRaises(exception.MalformedEndpoint):
self.catalog_api.get_catalog('fake-user', 'fake-tenant')
def test_create_endpoint_404(self):
self.assertRaises(exception.NotImplemented,
self.catalog_api.create_endpoint,
uuid.uuid4().hex,
{})
def test_get_endpoint_404(self):
self.assertRaises(exception.NotImplemented,
self.catalog_api.get_endpoint,
uuid.uuid4().hex)
def test_delete_endpoint_404(self):
self.assertRaises(exception.NotImplemented,
self.catalog_api.delete_endpoint,
uuid.uuid4().hex)

View File

@ -711,7 +711,7 @@ class KeystoneClientTests(object):
self.assertEquals(service_type, service.type)
self.assertEquals(service_desc, service.description)
# update is not supported...
# update is not supported in API v2...
# delete & read
client.services.delete(id=service.id)
@ -736,10 +736,9 @@ class KeystoneClientTests(object):
id=uuid.uuid4().hex)
def test_endpoint_delete_404(self):
# the catalog backend is expected to return Not Implemented
from keystoneclient import exceptions as client_exceptions
client = self.get_client(admin=True)
self.assertRaises(client_exceptions.HTTPNotImplemented,
self.assertRaises(client_exceptions.NotFound,
client.endpoints.delete,
id=uuid.uuid4().hex)

143
tests/test_v3_catalog.py Normal file
View File

@ -0,0 +1,143 @@
import uuid
import test_v3
class CatalogTestCase(test_v3.RestfulTestCase):
"""Test service & endpoint CRUD"""
def setUp(self):
super(CatalogTestCase, self).setUp()
self.service_id = uuid.uuid4().hex
self.service = self.new_service_ref()
self.service['id'] = self.service_id
self.catalog_api.create_service(
self.service_id,
self.service.copy())
self.endpoint_id = uuid.uuid4().hex
self.endpoint = self.new_endpoint_ref(service_id=self.service_id)
self.endpoint['id'] = self.endpoint_id
self.catalog_api.create_endpoint(
self.endpoint_id,
self.endpoint.copy())
# service validation
def assertValidServiceListResponse(self, resp, ref):
return self.assertValidListResponse(
resp,
'services',
self.assertValidService,
ref)
def assertValidServiceResponse(self, resp, ref):
return self.assertValidResponse(
resp,
'service',
self.assertValidService,
ref)
def assertValidService(self, entity, ref=None):
self.assertIsNotNone(entity.get('type'))
if ref:
self.assertEqual(ref['type'], entity['type'])
return entity
# endpoint validation
def assertValidEndpointListResponse(self, resp, ref):
return self.assertValidListResponse(
resp,
'endpoints',
self.assertValidEndpoint,
ref)
def assertValidEndpointResponse(self, resp, ref):
return self.assertValidResponse(
resp,
'endpoint',
self.assertValidEndpoint,
ref)
def assertValidEndpoint(self, entity, ref=None):
self.assertIsNotNone(entity.get('interface'))
self.assertIsNotNone(entity.get('service_id'))
if ref:
self.assertEqual(ref['interface'], entity['interface'])
self.assertEqual(ref['service_id'], entity['service_id'])
return entity
# service crud tests
def test_create_service(self):
"""POST /services"""
ref = self.new_service_ref()
r = self.post(
'/services',
body={'service': ref})
return self.assertValidServiceResponse(r, ref)
def test_list_services(self):
"""GET /services"""
r = self.get('/services')
self.assertValidServiceListResponse(r, self.service)
def test_get_service(self):
"""GET /services/{service_id}"""
r = self.get('/services/%(service_id)s' % {
'service_id': self.service_id})
self.assertValidServiceResponse(r, self.service)
def test_update_service(self):
"""PATCH /services/{service_id}"""
service = self.new_service_ref()
del service['id']
r = self.patch('/services/%(service_id)s' % {
'service_id': self.service_id},
body={'service': service})
self.assertValidServiceResponse(r, service)
def test_delete_service(self):
"""DELETE /services/{service_id}"""
self.delete('/services/%(service_id)s' % {
'service_id': self.service_id})
# endpoint crud tests
def test_list_endpoints(self):
"""GET /endpoints"""
r = self.get('/endpoints')
self.assertValidEndpointListResponse(r, self.endpoint)
def test_create_endpoint(self):
"""POST /endpoints"""
ref = self.new_endpoint_ref(service_id=self.service_id)
r = self.post(
'/endpoints',
body={'endpoint': ref})
self.assertValidEndpointResponse(r, ref)
def test_get_endpoint(self):
"""GET /endpoints/{endpoint_id}"""
r = self.get(
'/endpoints/%(endpoint_id)s' % {
'endpoint_id': self.endpoint_id})
self.assertValidEndpointResponse(r, self.endpoint)
def test_update_endpoint(self):
"""PATCH /endpoints/{endpoint_id}"""
ref = self.new_endpoint_ref(service_id=self.service_id)
del ref['id']
r = self.patch(
'/endpoints/%(endpoint_id)s' % {
'endpoint_id': self.endpoint_id},
body={'endpoint': ref})
self.assertValidEndpointResponse(r, ref)
def test_delete_endpoint(self):
"""DELETE /endpoints/{endpoint_id}"""
self.delete(
'/endpoints/%(endpoint_id)s' % {
'endpoint_id': self.endpoint_id})