Initial Changes to move service operations to extensions.

Change-Id: I96e9927d47212ecaf013e82aeff96d98115e9840
This commit is contained in:
Yogeshwar Srikrishnan 2011-09-19 12:07:50 -05:00
parent a913210194
commit 0425fba560
20 changed files with 180 additions and 76 deletions

View File

@ -113,6 +113,7 @@ class Service(Base, KeystoneBase):
__tablename__ = 'services'
__api__ = 'service'
id = Column(String(255), primary_key=True, unique=True)
type = Column(String(255))
desc = Column(String(255))

View File

@ -555,7 +555,7 @@
&getFaults;
</method>
<method name="POST" id="deleteUserCredential">
<method name="DELETE" id="deleteUserCredential">
<doc xml:lang="EN" title="Delete user credential">
<p xmlns="http://www.w3.org/1999/xhtml">Delete User credentials.</p>
<examples xmlns="http://docs.rackspace.com/api" title="Delete User Credentials">
@ -606,7 +606,7 @@
&getFaults;
</method>
<method name="PUT" id="addRole">
<method name="POST" id="addRole">
<doc xml:lang="EN" title="Add Role">
<p xmlns="http://www.w3.org/1999/xhtml">Add a Role.</p>
@ -676,7 +676,6 @@
</examples>
</doc>
<request>
<param name="serviceId" style="query" required="false" type="xsd:string"/>
<param name="marker" style="query" required="false" type="xsd:string"/>
<param name="limit" style="query" required="false" type="xsd:int"/>
</request>
@ -702,7 +701,7 @@
&commonFaults;
&getFaults;
</method>
<method name="PUT" id="addService">
<method name="POST" id="addService">
<doc xml:lang="EN" title="Add Service">
<p xmlns="http://www.w3.org/1999/xhtml">Add a service.</p>

View File

@ -154,7 +154,7 @@
&getFaults;
</method>
<method name="POST" id="deleteUserCredential">
<method name="DELETE" id="deleteUserCredential">
<doc xml:lang="EN" title="Delete user credential">
<p xmlns="http://www.w3.org/1999/xhtml">Delete User credentials.</p>
<examples xmlns="http://docs.rackspace.com/api" title="Delete User Credentials">

View File

@ -154,7 +154,7 @@
&getFaults;
</method>
<method name="POST" id="deleteUserCredential">
<method name="DELETE" id="deleteUserCredential">
<doc xml:lang="EN" title="Delete user credential">
<p xmlns="http://www.w3.org/1999/xhtml">Delete User credentials.</p>
<examples xmlns="http://docs.rackspace.com/api" title="Delete User Credentials">

View File

@ -0,0 +1,26 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2010 OpenStack LLC.
# 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 keystone.contrib.extensions.admin.osksadm.extensionHandler\
import ExtensionHandler as KSADMExtensionHandler
def configure_extensions(mapper, options):
ksadm_extenion_handler = KSADMExtensionHandler()
ksadm_extenion_handler.map_extension_methods(mapper, options)

View File

@ -0,0 +1,20 @@
# Copyright 2010 OpenStack LLC.
# 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
class BaseExtensionHandler(object):
def map_extension_methods(self, mapper, options):
raise NotImplementedError

View File

@ -0,0 +1,42 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2010 OpenStack LLC.
# 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 keystone.contrib.extensions.admin.extension import BaseExtensionHandler
from keystone.controllers.services import ServicesController
class ExtensionHandler(BaseExtensionHandler):
def map_extension_methods(self, mapper, options):
# Services Controller
services_controller = ServicesController(options)
mapper.connect("/OS-KSADM/services",
controller=services_controller,
action="get_services",
conditions=dict(method=["GET"]))
mapper.connect("/OS-KSADM/services",
controller=services_controller,
action="create_service",
conditions=dict(method=["POST"]))
mapper.connect("/OS-KSADM/services/{service_id}",
controller=services_controller,
action="delete_service",
conditions=dict(method=["DELETE"]))
mapper.connect("/OS-KSADM/services/{service_id}",
controller=services_controller,
action="get_service",
conditions=dict(method=["GET"]))

View File

@ -11,7 +11,6 @@ class ServicesController(wsgi.Controller):
def __init__(self, options):
self.options = options
# Not exposed yet.
@utils.wrap_error
def create_service(self, req):
service = utils.get_normalized_request_content(Service, req)

View File

@ -828,6 +828,7 @@ class IdentityService(object):
"A service with that id already exists")
dservice = models.Service()
dservice.id = service.service_id
dservice.type = service.type
dservice.desc = service.desc
api.SERVICE.create(dservice)
return service
@ -839,7 +840,7 @@ class IdentityService(object):
dservices = api.SERVICE.get_page(marker, limit)
for dservice in dservices:
ts.append(Service(dservice.id,
dservice.desc))
dservice.type, dservice.desc))
prev, next = api.SERVICE.get_page_markers(marker, limit)
links = []
if prev:
@ -856,7 +857,7 @@ class IdentityService(object):
dservice = api.SERVICE.get(service_id)
if not dservice:
raise fault.ItemNotFoundFault("The service could not be found")
return Service(dservice.id, dservice.desc)
return Service(dservice.id, dservice.type, dservice.desc)
def delete_service(self, admin_token, service_id):
self.__validate_service_or_keystone_admin_token(admin_token)

View File

@ -21,8 +21,9 @@ from keystone.logic.types import fault
class Service(object):
def __init__(self, service_id, desc):
def __init__(self, service_id, type, desc):
self.service_id = service_id
self.type = type
self.desc = desc
@staticmethod
@ -30,15 +31,19 @@ class Service(object):
try:
dom = etree.Element("root")
dom.append(etree.fromstring(xml_str))
root = dom.find("{http://docs.openstack.org/identity/api/v2.0}" \
"service")
root = dom.find(
"{http://docs.openstack.org/identity/api/ext/OS-KSADM/v1.0}"\
"service")
if root == None:
raise fault.BadRequestFault("Expecting Service")
service_id = root.get("id")
desc = root.get("description")
type = root.get("type")
if service_id == None:
raise fault.BadRequestFault("Expecting Service")
return Service(service_id, desc)
raise fault.BadRequestFault("Expecting Service ID")
if type == None:
raise fault.BadRequestFault("Expecting Service Type")
return Service(service_id, type, desc)
except etree.LxmlError as e:
raise fault.BadRequestFault("Cannot parse service", str(e))
@ -46,25 +51,35 @@ class Service(object):
def from_json(json_str):
try:
obj = json.loads(json_str)
if not "service" in obj:
raise fault.BadRequestFault("Expecting service")
service = obj["service"]
if not "OS-KSADM:service" in obj:
raise fault.BadRequestFault("Expecting Service")
service = obj["OS-KSADM:service"]
if not "id" in service:
service_id = None
else:
service_id = service["id"]
if service_id == None:
raise fault.BadRequestFault("Expecting service")
desc = service["description"]
return Service(service_id, desc)
raise fault.BadRequestFault("Expecting Service ID")
if not "description" in service:
desc = None
else:
desc = service["description"]
if not "type" in service:
raise fault.BadRequestFault("Expecting Service Type")
else:
type = service["type"]
return Service(service_id, type, desc)
except (ValueError, TypeError) as e:
raise fault.BadRequestFault("Cannot parse service", str(e))
def to_dom(self):
dom = etree.Element("service",
xmlns="http://docs.openstack.org/identity/api/v2.0")
xmlns="http://docs.openstack.org/identity/api/ext/OS-KSADM/v1.0")
if self.service_id:
dom.set("id", self.service_id)
if self.type:
dom.set("type", self.type)
if self.desc:
dom.set("description", string.lower(str(self.desc)))
return dom
@ -76,9 +91,11 @@ class Service(object):
service = {}
if self.service_id:
service["id"] = self.service_id
if self.type:
service["type"] = self.type
if self.desc:
service["description"] = self.desc
return {'service': service}
return {'OS-KSADM:service': service}
def to_json(self):
return json.dumps(self.to_dict())
@ -93,7 +110,8 @@ class Services(object):
def to_xml(self):
dom = etree.Element("services")
dom.set(u"xmlns", "http://docs.openstack.org/identity/api/v2.0")
dom.set(u"xmlns",
"http://docs.openstack.org/identity/api/ext/OS-KSADM/v1.0")
for t in self.values:
dom.append(t.to_dom())
@ -104,6 +122,7 @@ class Services(object):
return etree.tostring(dom)
def to_json(self):
values = [t.to_dict()["service"] for t in self.values]
links = [t.to_dict()["links"] for t in self.links]
return json.dumps({"services": {"values": values, "links": links}})
services = [t.to_dict()["OS-KSADM:service"] for t in self.values]
services_links = [t.to_dict()["links"] for t in self.links]
return json.dumps({"OS-KSADM:services": services,
"OS-KSADM:services_links": services_links})

View File

@ -209,7 +209,9 @@ def process(*args):
print 'SUCCESS: Token %s deleted.' % (object_id,)
elif (object_type, command) == ('service', 'add'):
if api.add_service(service=object_id):
type = optional_arg(args, 3)
desc = optional_arg(args, 4)
if api.add_service(service=object_id, type=type, desc=desc):
print "SUCCESS: Service %s created successfully." % (object_id,)
elif (object_type, command) == ('service', 'list'):

View File

@ -160,9 +160,11 @@ def delete_token(token):
return db_api.TOKEN.delete(token)
def add_service(service):
def add_service(service, type, desc):
obj = db_models.Service()
obj.id = service
obj.type = type
obj.desc = desc
return db_api.SERVICE.create(obj)

View File

@ -22,12 +22,12 @@ import keystone.backends as db
from keystone.controllers.auth import AuthController
from keystone.controllers.endpointtemplates import EndpointTemplatesController
from keystone.controllers.roles import RolesController
from keystone.controllers.services import ServicesController
from keystone.controllers.staticfiles import StaticFilesController
from keystone.controllers.tenant import TenantController
from keystone.controllers.user import UserController
from keystone.controllers.version import VersionController
from keystone.controllers.extensions import ExtensionsController
import keystone.contrib.extensions.admin as extension
class AdminApi(wsgi.Router):
@ -226,23 +226,5 @@ class AdminApi(wsgi.Router):
action="get_static_file",
root="content/common/", path="samples/",
conditions=dict(method=["GET"]))
# Services Controller
services_controller = ServicesController(options)
mapper.connect("/services",
controller=services_controller,
action="get_services",
conditions=dict(method=["GET"]))
mapper.connect("/services",
controller=services_controller,
action="create_service",
conditions=dict(method=["POST"]))
mapper.connect("/services/{service_id}",
controller=services_controller,
action="delete_service",
conditions=dict(method=["DELETE"]))
mapper.connect("/services/{service_id}",
controller=services_controller,
action="get_service",
conditions=dict(method=["GET"]))
extension.configure_extensions(mapper, options)
super(AdminApi, self).__init__(mapper)

View File

@ -342,21 +342,23 @@ class ApiTestCase(RestfulTestCase):
def post_service(self, **kwargs):
"""POST /services"""
return self.admin_request(method='POST', path='/services', **kwargs)
return self.admin_request(method='POST',
path='/OS-KSADM/services', **kwargs)
def get_services(self, **kwargs):
"""GET /services"""
return self.admin_request(method='GET', path='/services', **kwargs)
return self.admin_request(method='GET',
path='/OS-KSADM/services', **kwargs)
def get_service(self, service_id, **kwargs):
"""GET /services/{service_id}"""
return self.admin_request(method='GET',
path='/services/%s' % (service_id,), **kwargs)
path='/OS-KSADM/services/%s' % (service_id,), **kwargs)
def delete_service(self, service_id, **kwargs):
"""DELETE /services/{service_id}"""
return self.admin_request(method='DELETE',
path='/services/%s' % (service_id,), **kwargs)
path='/OS-KSADM/services/%s' % (service_id,), **kwargs)
def get_root(self, **kwargs):
"""GET /"""
@ -445,6 +447,7 @@ class FunctionalTestCase(ApiTestCase):
service_admin_token = '111222333444'
xmlns = '{http://docs.openstack.org/identity/api/v2.0}'
xmlns_ksadm = '{http://docs.openstack.org/identity/api/ext/OS-KSADM/v1.0}'
def setUp(self):
"""Prepare keystone for system tests"""
@ -650,14 +653,16 @@ class FunctionalTestCase(ApiTestCase):
role_id = optional_str(role_id)
return self.delete_role(role_id, **kwargs)
def create_service(self, service_id=None, service_description=None,
def create_service(self, service_id=None,
service_type=None, service_description=None,
**kwargs):
service_id = optional_str(service_id)
service_type = optional_str(service_type)
service_description = optional_str(service_description)
data = {
"service": {
"OS-KSADM:service": {
"id": service_id,
"type": service_type,
"description": service_description}}
return self.post_service(as_json=data, **kwargs)

View File

@ -33,7 +33,7 @@ class AuthenticationTest(common.FunctionalTestCase):
self.services = {}
self.endpoint_templates = {}
for x in range(0, 5):
self.services[x] = self.create_service().json['service']
self.services[x] = self.create_service().json['OS-KSADM:service']
self.endpoint_templates[x] = self.create_endpoint_template(
service_id=self.services[x]['id']).json['endpointTemplate']
self.create_endpoint_for_tenant(self.tenant['id'],

View File

@ -22,7 +22,7 @@ class EndpointTemplatesTest(common.FunctionalTestCase):
def setUp(self, *args, **kwargs):
super(EndpointTemplatesTest, self).setUp(*args, **kwargs)
self.service = self.create_service().json['service']
self.service = self.create_service().json['OS-KSADM:service']
self.endpoint_template = self.create_endpoint_template(
service_id=self.service['id']).json['endpointTemplate']

View File

@ -81,14 +81,14 @@ class CreateRolesTest(RolesTest):
self.remove_role(role['id'], assert_status=204)
def test_create_role_mapped_to_a_service(self):
service = self.create_service().json['service']
service = self.create_service().json['OS-KSADM:service']
role_id = service['id'] + ':' + common.unique_str()
role = self.create_role(role_id=role_id, service_id=service['id']).\
json['role']
self.assertEqual(service['id'], role['serviceId'])
def test_create_role_mapped_to_a_service_xml(self):
service = self.create_service().json['service']
service = self.create_service().json['OS-KSADM:service']
role_id = service['id'] + ':' + common.unique_str()
data = ('<?xml version="1.0" encoding="UTF-8"?> '

View File

@ -29,7 +29,7 @@ class ServicesTest(common.FunctionalTestCase):
class GetServicesTest(ServicesTest):
def test_get_services_using_keystone_admin_token_json(self):
services = self.list_services(assert_status=200).\
json['services']['values']
json['OS-KSADM:services']
self.assertTrue(len(services))
@ -37,14 +37,14 @@ class GetServicesTest(ServicesTest):
r = self.list_services(assert_status=200, headers={
'Accept': 'application/xml'})
self.assertEqual(r.xml.tag, self.xmlns + "services")
services = r.xml.findall(self.xmlns + "service")
self.assertEqual(r.xml.tag, self.xmlns_ksadm + "services")
services = r.xml.findall(self.xmlns_ksadm + "service")
self.assertTrue(len(services))
def test_get_services_using_service_admin_token(self):
self.admin_token = self.service_admin_token
services = self.list_services(assert_status=200).\
json['services']['values']
json['OS-KSADM:services']
self.assertTrue(len(services))
@ -53,8 +53,8 @@ class GetServicesTest(ServicesTest):
r = self.get_services(assert_status=200, headers={
'Accept': 'application/xml'})
self.assertEqual(r.xml.tag, self.xmlns + "services")
services = r.xml.findall(self.xmlns + "service")
self.assertEqual(r.xml.tag, self.xmlns_ksadm + "services")
services = r.xml.findall(self.xmlns_ksadm + "service")
self.assertTrue(len(services))
def test_get_services_using_disabled_token(self):
@ -78,11 +78,11 @@ class GetServiceTest(ServicesTest):
def setUp(self, *args, **kwargs):
super(ServicesTest, self).setUp(*args, **kwargs)
self.service = self.create_service().json['service']
self.service = self.create_service().json['OS-KSADM:service']
def test_service_get_json(self):
service = self.fetch_service(service_id=self.service['id'],
assert_status=200).json['service']
assert_status=200).json['OS-KSADM:service']
self.assertIsNotNone(service['id'])
self.assertIsNotNone(service['description'])
@ -91,7 +91,7 @@ class GetServiceTest(ServicesTest):
service = self.fetch_service(service_id=self.service['id'],
assert_status=200, headers={'Accept': 'application/xml'}).xml
self.assertEqual(service.tag, self.xmlns + 'service')
self.assertEqual(service.tag, self.xmlns_ksadm + 'service')
self.assertIsNotNone(service.get('id'))
self.assertIsNotNone(service.get('description'))
@ -114,20 +114,25 @@ class GetServiceTest(ServicesTest):
class CreateServiceTest(ServicesTest):
def test_service_create_json(self):
self.service = self.create_service(assert_status=201).json['service']
self.service = self.create_service(
assert_status=201).json['OS-KSADM:service']
def test_service_create_xml(self):
service_id = common.unique_str()
data = '<?xml version="1.0" encoding="UTF-8"?>\
<service xmlns="http://docs.openstack.org/identity/api/v2.0" \
id="%s" description="A Description of the service"/>\
' % (service_id,)
<service xmlns=\
"http://docs.openstack.org/identity/api/ext/OS-KSADM/v1.0" \
id="%s" type="example type" \
description="A Description of the service"/>' % (service_id,)
self.post_service(as_xml=data, assert_status=201)
def test_service_create_duplicate_json(self):
service_id = common.unique_str()
self.create_service(service_id=service_id, assert_status=201)
self.create_service(service_id=service_id, assert_status=409)
service_type = "compute"
self.create_service(service_id=service_id,
service_type=service_type, assert_status=201)
self.create_service(service_id=service_id,
service_type=service_type, assert_status=409)
def test_service_create_using_expired_token(self):
self.admin_token = self.expired_admin_token
@ -150,7 +155,7 @@ class DeleteServiceTest(ServicesTest):
def setUp(self, *args, **kwargs):
super(DeleteServiceTest, self).setUp(*args, **kwargs)
self.service = self.create_service().json['service']
self.service = self.create_service().json['OS-KSADM:service']
def test_service_delete(self):
self.remove_service(self.service['id'], assert_status=204)

View File

@ -78,7 +78,8 @@ DEFAULT_FIXTURE = [
('endpoint', 'add', '1234', '4'),
('endpoint', 'add', '1234', '5'),
# Add Services
('service', 'add', 'exampleservice'),
('service', 'add', 'exampleservice',
'example type', 'example description'),
# Add Credentials
('credentials', 'add', 'admin', 'EC2', 'admin:admin', 'admin', 'admin'),
]