summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorYogeshwar Srikrishnan <yoga80@yahoo.com>2011-11-27 14:04:35 -0600
committerZiad Sawalha <github@highbridgellc.com>2011-11-30 17:38:23 -0600
commit2eab4b317bf4db8e8107a003cc0df194f65b50da (patch)
tree23eb3f942245cceb06f51c0d6a1fe11091f521ea
parentd9f9501f36912f3e26aaafb55e5dc19c9bcea909 (diff)
Bug #890801 Changes to support /extensions call.
- Introduced a new extension reader to read static extension content. - Added additional rst files explaining extensions. - Removed functionality from additional middleware that used to support /extensions call.ie RAX-KEY-extension - Removed service extension test as it was no more relavent. - Added unit test that checks toggling of extensions. - Additional notes on the conf file. Change-Id: Ia7b47b123e94704ca5d88dcce0db4ee1ac5eb3ba
-rw-r--r--doc/source/extensions.rst125
-rw-r--r--doc/source/index.rst2
-rw-r--r--doc/source/keystone.conf.rst4
-rw-r--r--etc/keystone.conf3
-rw-r--r--keystone/contrib/extensions/__init__.py8
-rw-r--r--keystone/contrib/extensions/admin/__init__.py16
-rw-r--r--keystone/contrib/extensions/extensions.json1
-rw-r--r--keystone/contrib/extensions/extensions.xml5
-rw-r--r--keystone/contrib/extensions/service/__init__.py1
-rwxr-xr-xkeystone/contrib/extensions/service/raxgrp/api.py5
-rw-r--r--keystone/contrib/extensions/service/raxkey/frontend.py58
-rw-r--r--keystone/controllers/extensions.py30
-rw-r--r--keystone/logic/extension_reader.py109
-rw-r--r--keystone/logic/types/extension.py12
-rwxr-xr-xkeystone/routers/admin.py1
-rw-r--r--keystone/routers/service.py3
-rw-r--r--keystone/test/functional/test_ext_raxkskey.py30
-rw-r--r--keystone/test/functional/test_extensions.py31
-rw-r--r--keystone/test/unit/test_extensions.py65
19 files changed, 392 insertions, 117 deletions
diff --git a/doc/source/extensions.rst b/doc/source/extensions.rst
new file mode 100644
index 00000000..d377ba24
--- /dev/null
+++ b/doc/source/extensions.rst
@@ -0,0 +1,125 @@
+================
+Extensions
+================
+
+Extensions support adding features and functions to OpenStack APIs at any time, without prior
+approval or waiting for a new API and release cycles.
+
+The extension mechanism is in development and documented in extensions_ and extensionspresentation_.
+
+This document describes the extensions included with Keystone, how to enable and disable them,
+and briefly touches on how to write your own extensions.
+
+.. _extensions: http://docs.openstack.org/trunk/openstack-compute/developer/openstack-api-extensions/content/ch02s01.html
+.. _extensionspresentation: http://www.slideshare.net/RackerWilliams/openstack-extensions
+
+
+Built-in Extensions
+-------------------
+
+Keystone ships with a number of extensions found under the
+keystone/contib/extensions folder.
+
+The following built-in extensions are included:
+
+OS-KSADM
+
+ This is an extensions that supports managing users, tenants, and roles
+ through the API. Without this extensions, the ony way to manage those
+ objects is through keystone-manage or directly in the underlying database.
+
+ This is an Admin API extension only.
+
+OS-KSCATALOG
+
+ This extensions supports managing Endpoints and prrovides the Endpoint
+ Template mechanism for managing bulk endpoints.
+
+ This is an Admin API extension only.
+
+OS-EC2
+
+ This extension adds support for EC2 credentials.
+
+ This is an Admin and Service API extension.
+
+RAX-GRP
+
+ This extension adds functionality the enables groups.
+
+ This is an Admin and Service API extension.
+
+RAX-KEY
+
+ This extensions adds support for authentication with an API Key (the core
+ Keystone API only supports username/password credentials)
+
+ This is an Admin and Service API extension.
+
+
+.. note::
+
+ The included extensions are in the process of being rewritten. Currently
+ only osksadm and oskscatalog work with this new extensions design.
+
+
+Enabling/Disabling extensions.
+------------------------------
+ The Keystone conf file has a property called extensions. This property holds
+ the list of supported extensions that you want enabled. If you want to
+ add/remove an extension from being supported, add/remove the extension key
+ from this property. The key is the name of the folder of the extension
+ under the keystone/contrib/extensions folder.
+ **If you want to load different extensions in the service API than the Admin API
+ you need to use different config files.
+
+Adding additional extensions.
+------------------------------
+
+To add a new extension, these are the steps involved.
+
+1. Register your identifier (this process is not ready. For now, find a short
+identifier that you know won't conflict with other extension writers).
+
+ Example: OS for OpenStack, RAX for Rackspace
+
+2. Decide a short hand name for extension.
+
+ Example: OS-KSADM (for OpenStack's Keystone Admin extensions)
+
+3. Decide whether the extension enhances Admin API, Service API or both.
+
+4. Add a folder with the name we have already decided @
+/contrib/extensions/{admin or service} based on which API you are enhancing.
+
+5. Add static extension files for json (name it as extension.json) and xml
+(name it as extension.xml) to the new extension folder. Refer to `Service Guide <https://github.com/openstack/keystone/blob/master/keystone/content/admin/identityadminguide.pdf?raw=true>`_
+`Sample extension XML <https://github.com/openstack/keystone/blob/master/keystone/content/common/samples/extension.json>`_
+`Sample extension JSON <https://github.com/openstack/keystone/blob/master/keystone/content/common/samples/extension.xml>`_ for the the content and structure.
+
+6. If your extension is adding additional methods override the base class
+'BaseExtensionHandler', call it 'ExtensionHandler', and add your methods.
+
+7. If your extension modifies existing calls yu need to modify existing code to support the extension.
+
+8. Modify this documentation to refelect the availability of your extension.
+
+9. Add your extensions documentation, WADL, and XSD files in the keystone/content
+folder
+
+10. Add your extension name to the list of supported extensions in the keystone conf file.
+
+
+Finding out which extensions are running
+----------------------------------------
+
+A quick and simple test is::
+
+ curl http://localhost:35357/v2.0/extensions
+
+ or
+
+ curl http://localhost:5000/v2.0/extensions
+
+The response will list the extensions available.
+ \ No newline at end of file
diff --git a/doc/source/index.rst b/doc/source/index.rst
index c338e57a..3611d8a2 100644
--- a/doc/source/index.rst
+++ b/doc/source/index.rst
@@ -43,6 +43,8 @@ Getting Started
setup
testing
+ migration
+ extensions
configuration
controllingservers
configuringservices
diff --git a/doc/source/keystone.conf.rst b/doc/source/keystone.conf.rst
index 39482ee3..64798902 100644
--- a/doc/source/keystone.conf.rst
+++ b/doc/source/keystone.conf.rst
@@ -45,6 +45,10 @@ keystone.conf example
'swift' : 'X-Storage-Url',
'cdn' : 'X-CDN-Management-Url'}
+ #List of extensions currently loaded.
+ #Refer docs for list of supported extensions.
+ extensions= osksadm,oskscatalog
+
# Address to bind the API server
# TODO Properties defined within app not available via pipeline.
service_host = 0.0.0.0
diff --git a/etc/keystone.conf b/etc/keystone.conf
index 459c09e5..075f2ffd 100644
--- a/etc/keystone.conf
+++ b/etc/keystone.conf
@@ -26,7 +26,8 @@ service-header-mappings = {
'swift' : 'X-Storage-Url',
'cdn' : 'X-CDN-Management-Url'}
-#List of extensions currently supported
+#List of extensions currently loaded.
+#Refer docs for list of supported extensions.
extensions= osksadm,oskscatalog
# Address to bind the API server
diff --git a/keystone/contrib/extensions/__init__.py b/keystone/contrib/extensions/__init__.py
index 35bba8c3..865efcb2 100644
--- a/keystone/contrib/extensions/__init__.py
+++ b/keystone/contrib/extensions/__init__.py
@@ -20,13 +20,15 @@ import ast
import logging
from keystone import utils
EXTENSION_PREFIX = 'keystone.contrib.extensions.'
+DEFAULT_EXTENSIONS = 'osksadm,oskscatalog'
+CONFIG_EXTENSION_PROPERTY = 'extensions'
class BaseExtensionConfigurer(object):
- def configure_extensions(self, extension_property, extension_type,
- extension_defaults, mapper, options):
+ def configure_extensions(self, extension_type,
+ mapper, options):
supported_extensions = options.get(
- extension_property, extension_defaults)
+ CONFIG_EXTENSION_PROPERTY, DEFAULT_EXTENSIONS)
for supported_extension in supported_extensions.split(','):
self.extension_handlers = []
supported_extension = EXTENSION_PREFIX\
diff --git a/keystone/contrib/extensions/admin/__init__.py b/keystone/contrib/extensions/admin/__init__.py
index 9530970c..16be5a64 100644
--- a/keystone/contrib/extensions/admin/__init__.py
+++ b/keystone/contrib/extensions/admin/__init__.py
@@ -20,21 +20,19 @@ import ast
import logging
from keystone import utils
from keystone.contrib.extensions import BaseExtensionConfigurer
-DEFAULT_EXTENSIONS = 'osksadm,oskscatalog'
-CONFIGURER = None
-CONFIG_EXTENSION_PROPERTY = 'extensions'
+ADMIN_CONFIGURER = None
EXTENSION_ADMIN_PREFIX = 'admin'
class AdminExtensionConfigurer(BaseExtensionConfigurer):
def configure(self, mapper, options):
- self.configure_extensions(CONFIG_EXTENSION_PROPERTY,
+ self.configure_extensions(
EXTENSION_ADMIN_PREFIX,
- DEFAULT_EXTENSIONS, mapper, options)
+ mapper, options)
def get_extension_configurer():
- global CONFIGURER
- if not CONFIGURER:
- CONFIGURER = AdminExtensionConfigurer()
- return CONFIGURER
+ global ADMIN_CONFIGURER
+ if not ADMIN_CONFIGURER:
+ ADMIN_CONFIGURER = AdminExtensionConfigurer()
+ return ADMIN_CONFIGURER
diff --git a/keystone/contrib/extensions/extensions.json b/keystone/contrib/extensions/extensions.json
new file mode 100644
index 00000000..9e1c96d0
--- /dev/null
+++ b/keystone/contrib/extensions/extensions.json
@@ -0,0 +1 @@
+{ "extensions" : { "values" : []}} \ No newline at end of file
diff --git a/keystone/contrib/extensions/extensions.xml b/keystone/contrib/extensions/extensions.xml
new file mode 100644
index 00000000..ed5ee9c6
--- /dev/null
+++ b/keystone/contrib/extensions/extensions.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<extensions xmlns="http://docs.openstack.org/common/api/v1.0"
+ xmlns:atom="http://www.w3.org/2005/Atom">
+</extensions>
diff --git a/keystone/contrib/extensions/service/__init__.py b/keystone/contrib/extensions/service/__init__.py
index e69de29b..e0eeb867 100644
--- a/keystone/contrib/extensions/service/__init__.py
+++ b/keystone/contrib/extensions/service/__init__.py
@@ -0,0 +1 @@
+EXTENSION_SERVICE_PREFIX = 'service'
diff --git a/keystone/contrib/extensions/service/raxgrp/api.py b/keystone/contrib/extensions/service/raxgrp/api.py
index ef9e7fd5..1882a13f 100755
--- a/keystone/contrib/extensions/service/raxgrp/api.py
+++ b/keystone/contrib/extensions/service/raxgrp/api.py
@@ -61,10 +61,11 @@ class RAXEXTBaseUserAPI(BaseUserAPI):
def users_get_page_markers(self, marker, limit):
raise NotImplementedError
- def users_get_by_tenant_get_page(self, tenant_id, marker, limit):
+ def users_get_by_tenant_get_page(self, tenant_id, role_id, marker, limit):
raise NotImplementedError
- def users_get_by_tenant_get_page_markers(self, tenant_id, marker, limit):
+ def users_get_by_tenant_get_page_markers(self, tenant_id,
+ role_id, marker, limit):
raise NotImplementedError
def check_password(self, user, password):
diff --git a/keystone/contrib/extensions/service/raxkey/frontend.py b/keystone/contrib/extensions/service/raxkey/frontend.py
index c461466a..bd203b78 100644
--- a/keystone/contrib/extensions/service/raxkey/frontend.py
+++ b/keystone/contrib/extensions/service/raxkey/frontend.py
@@ -19,18 +19,14 @@
"""
RACKSPACE API KEY EXTENSION
-This WSGI component
-- detects calls with extensions in them.
-- processes the necessary components
+Soon to be deprecated middleware.
"""
-
-import os
-import json
-from lxml import etree
-from webob.exc import Request, Response
+import logging
EXTENSION_ALIAS = "RAX-KEY"
+LOG = logging.getLogger('keystone.contrib.extensions')
+
class FrontEndFilter(object):
"""API Key Middleware that handles authentication with API Key"""
@@ -42,49 +38,9 @@ class FrontEndFilter(object):
self.app = app
def __call__(self, env, start_response):
- """ Handle incoming request. Transform. And send downstream. """
- request = Request(env)
- if request.path == "/extensions":
- if env['KEYSTONE_API_VERSION'] == '2.0':
- request = Request(env)
- response = request.get_response(self.app)
- if response.status_int == 200:
- if response.content_type == 'application/json':
- #load json for this extension from file
- thisextension = open(os.path.join(
- os.path.dirname(__file__),
- "extension.json")).read()
- thisextensionjson = json.loads(thisextension)
-
- #load json in response
- body = json.loads(response.body)
- extensionsarray = body["extensions"]["values"]
-
- #add this extension and return the response
- extensionsarray.append(thisextensionjson)
- newresp = Response(
- content_type='application/json',
- body=json.dumps(body))
- return newresp(env, start_response)
- elif response.content_type == 'application/xml':
- #load xml for this extension from file
- thisextensionxml = etree.parse(os.path.join(
- os.path.dirname(__file__),
- "extension.xml")).getroot()
- #load xml being returned in response
- body = etree.fromstring(response.body)
-
- #add this extension and return the response
- body.append(thisextensionxml)
- newresp = Response(
- content_type='application/xml',
- body=etree.tostring(body))
- return newresp(env, start_response)
-
- # return the response
- return response(env, start_response)
-
- #default action, bypass
+ LOG.warn('This middleware is soon to be deprecated." +\
+ "Please remove related entries from conf files.')
+ #Kept for backward compatibility.Does nothing as of now.
return self.app(env, start_response)
diff --git a/keystone/controllers/extensions.py b/keystone/controllers/extensions.py
index 159c2ebe..96032839 100644
--- a/keystone/controllers/extensions.py
+++ b/keystone/controllers/extensions.py
@@ -1,26 +1,24 @@
-from webob import Response
-
from keystone import utils
-from keystone.common import template, wsgi
+from keystone.common import wsgi
+from keystone.logic.extension_reader import ExtensionsReader
+from keystone.contrib.extensions.admin import EXTENSION_ADMIN_PREFIX
+from keystone.contrib.extensions.service import EXTENSION_SERVICE_PREFIX
class ExtensionsController(wsgi.Controller):
"""Controller for extensions related methods"""
- def __init__(self, options):
+ def __init__(self, options, is_service_operation=None):
super(ExtensionsController, self).__init__()
self.options = options
-
- @utils.wrap_error
- def get_extensions_info(self, req, path):
- resp = Response()
-
- if utils.is_xml_response(req):
- resp_file = "%s.xml" % path
- mime_type = "application/xml"
+ if is_service_operation:
+ self.extension_prefix = EXTENSION_SERVICE_PREFIX
else:
- resp_file = "%s.json" % path
- mime_type = "application/json"
+ self.extension_prefix = EXTENSION_ADMIN_PREFIX
+ self.extension_reader = ExtensionsReader(options,
+ self.extension_prefix)
- return template.static_file(resp, req, resp_file,
- root=utils.get_app_root(), mimetype=mime_type)
+ @utils.wrap_error
+ def get_extensions_info(self, req):
+ return utils.send_result(200, req,
+ self.extension_reader.get_extensions())
diff --git a/keystone/logic/extension_reader.py b/keystone/logic/extension_reader.py
new file mode 100644
index 00000000..1c7d4914
--- /dev/null
+++ b/keystone/logic/extension_reader.py
@@ -0,0 +1,109 @@
+import os
+import json
+from lxml import etree
+
+from keystone import utils
+from keystone.contrib.extensions import CONFIG_EXTENSION_PROPERTY
+from keystone.contrib.extensions import DEFAULT_EXTENSIONS
+from keystone.logic.types.extension import Extensions
+
+EXTENSIONS_PATH = 'contrib/extensions'
+
+
+class ExtensionsReader(object):
+ """Reader to read static extensions content"""
+
+ def __init__(self, options, extension_prefix):
+ self.extensions = None
+ self.options = options
+ self.extension_prefix = extension_prefix
+ self.root = None
+ self.supported_extensions = None
+ self.__init_extensions()
+
+ def __init_extensions(self):
+ self.extensions = Extensions(self.__get_json_extensions(),
+ self.__get_xml_extensions())
+
+ def __get_json_extensions(self):
+ """ Initializes and returns all json static extension content."""
+ body = self.__get_all_json_extensions()
+ extensionsarray = body["extensions"]["values"]
+ for supported_extension in self.__get_supported_extensions():
+ thisextensionjson = self.__get_extension_json(
+ supported_extension)
+ if thisextensionjson is not None:
+ extensionsarray.append(thisextensionjson)
+ return json.dumps(body)
+
+ def __get_xml_extensions(self):
+ """ Initializes and returns all xml static extension content."""
+ body = self.__get_all_xml_extensions()
+ for supported_extension in self.__get_supported_extensions():
+ thisextensionxml = self.__get_extension_xml(supported_extension)
+ if thisextensionxml is not None:
+ body.append(thisextensionxml)
+ return etree.tostring(body)
+
+ def __get_root(self):
+ """ Returns application root.Has a local reference for reuse."""
+ if self.root is None:
+ self.root = utils.get_app_root()
+ self.root = os.path.abspath(self.root) + os.sep
+ return self.root
+
+ def __get_file(self, resp_file):
+ """ Helper get file method."""
+ root = self.__get_root()
+ filename = os.path.abspath(os.path.join(root, resp_file.strip('/\\')))
+ return open(filename).read()
+
+ def __get_all_json_extensions(self):
+ """ Gets empty json extensions content to which specific
+ extensions are added."""
+ resp_file = "%s/%s.json" % (EXTENSIONS_PATH, 'extensions')
+ allextensions = self.__get_file(resp_file)
+ return json.loads(allextensions)
+
+ def __get_all_xml_extensions(self):
+ """ Gets empty xml extensions content
+ to which specific extensions are added."""
+ resp_file = "%s/%s.xml" % (EXTENSIONS_PATH, 'extensions')
+ allextensions = self.__get_file(resp_file)
+ return etree.fromstring(allextensions)
+
+ def __get_supported_extensions(self):
+ """ Returns list of supported extensions."""
+ if self.supported_extensions is None:
+ self.supported_extensions = self.options.get(
+ CONFIG_EXTENSION_PROPERTY, DEFAULT_EXTENSIONS).split(',')
+ return self.supported_extensions
+
+ def __get_extension_json(self, extension_name):
+ """Returns specific extension's json content."""
+ thisextension = self.__get_extension_file(extension_name, 'json')
+ return thisextension if not thisextension\
+ else json.loads(thisextension.read())
+
+ def __get_extension_xml(self, extension_name):
+ """Returns specific extension's xml content."""
+ thisextension = self.__get_extension_file(extension_name, 'xml')
+ return thisextension if not thisextension\
+ else etree.parse(thisextension).getroot()
+
+ def __get_extension_file(self, extension_name, request_type):
+ """Returns specific static extension file."""
+ try:
+ extension_dir = "%s/%s/%s" % (EXTENSIONS_PATH,
+ self.extension_prefix, extension_name)
+ extension_dir = os.path.abspath(os.path.join(self.__get_root(),
+ extension_dir.strip('/\\')))
+ extension_file = open(os.path.join(extension_dir,
+ "extension." + request_type))
+ return extension_file
+ except IOError:
+ return None
+
+ def get_extensions(self):
+ """Return Extensions result."""
+ return self.extensions
diff --git a/keystone/logic/types/extension.py b/keystone/logic/types/extension.py
new file mode 100644
index 00000000..bc8bab59
--- /dev/null
+++ b/keystone/logic/types/extension.py
@@ -0,0 +1,12 @@
+class Extensions(object):
+ """An extensions type to hold static extensions content."""
+
+ def __init__(self, json_content, xml_content):
+ self.xml_content = xml_content
+ self.json_content = json_content
+
+ def to_json(self):
+ return self.json_content
+
+ def to_xml(self):
+ return self.xml_content
diff --git a/keystone/routers/admin.py b/keystone/routers/admin.py
index 8e1d5748..26d0d5cb 100755
--- a/keystone/routers/admin.py
+++ b/keystone/routers/admin.py
@@ -87,7 +87,6 @@ class AdminApi(wsgi.Router):
mapper.connect("/extensions",
controller=extensions_controller,
action="get_extensions_info",
- path="content/admin/extensions",
conditions=dict(method=["GET"]))
# Static Files Controller
diff --git a/keystone/routers/service.py b/keystone/routers/service.py
index f8d9dc5d..96063130 100644
--- a/keystone/routers/service.py
+++ b/keystone/routers/service.py
@@ -56,11 +56,10 @@ class ServiceApi(wsgi.Router):
action="get_version_info", file="service/version",
conditions=dict(method=["GET"]))
- extensions_controller = ExtensionsController(options)
+ extensions_controller = ExtensionsController(options, True)
mapper.connect("/extensions",
controller=extensions_controller,
action="get_extensions_info",
- path="content/service/extensions",
conditions=dict(method=["GET"]))
# Static Files Controller
diff --git a/keystone/test/functional/test_ext_raxkskey.py b/keystone/test/functional/test_ext_raxkskey.py
deleted file mode 100644
index 81b3b341..00000000
--- a/keystone/test/functional/test_ext_raxkskey.py
+++ /dev/null
@@ -1,30 +0,0 @@
-import unittest2 as unittest
-from keystone.test.functional import common
-
-
-class TestExtensions(common.ApiTestCase):
- def test_extensions_json(self):
- r = self.service_request(path='/extensions.json',
- assert_status=200)
- self.assertTrue('json' in r.getheader('Content-Type'))
- content = r.json
- self.assertIsNotNone(content['extensions'])
- self.assertIsNotNone(content['extensions']['values'])
- found = False
- for value in content['extensions']['values']:
- if value['extension']['alias'] == 'RAX-KSKEY':
- found = True
- break
- self.assertTrue(found)
-
- def test_extensions_xml(self):
- r = self.service_request(path='/extensions.xml')
- self.assertTrue('xml' in r.getheader('Content-Type'))
- content = r.xml
- extension = content.find(
- "{http://docs.openstack.org/common/api/v1.0}extension")
- self.assertEqual(extension.get("alias"), "RAX-KSKEY")
-
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/keystone/test/functional/test_extensions.py b/keystone/test/functional/test_extensions.py
index 8dbbf97d..de0fe841 100644
--- a/keystone/test/functional/test_extensions.py
+++ b/keystone/test/functional/test_extensions.py
@@ -6,6 +6,9 @@ class TestExtensions(common.FunctionalTestCase):
def test_extensions_json(self):
r = self.service_request(path='/extensions.json')
self.assertTrue('json' in r.getheader('Content-Type'))
+ content = r.json
+ self.assertIsNotNone(content['extensions'])
+ self.assertIsNotNone(content['extensions']['values'])
def test_extensions_xml(self):
r = self.service_request(path='/extensions.xml')
@@ -14,12 +17,36 @@ class TestExtensions(common.FunctionalTestCase):
class TestAdminExtensions(common.ApiTestCase):
def test_extensions_json(self):
- r = self.service_request(path='/extensions.json')
+ r = self.admin_request(path='/extensions.json')
self.assertTrue('json' in r.getheader('Content-Type'))
+ content = r.json
+ self.assertIsNotNone(content['extensions'])
+ self.assertIsNotNone(content['extensions']['values'])
+ found_osksadm = False
+ found_oskscatalog = False
+ for value in content['extensions']['values']:
+ if value['extension']['alias'] == 'OS-KSADM':
+ found_osksadm = True
+ if value['extension']['alias'] == 'OS-KSCATALOG':
+ found_oskscatalog = True
+ self.assertTrue(found_osksadm, "Missing OS-KSADM extension.")
+ self.assertTrue(found_oskscatalog, "Missing OS-KSCATALOG extension.")
def test_extensions_xml(self):
- r = self.service_request(path='/extensions.xml')
+ r = self.admin_request(path='/extensions.xml')
self.assertTrue('xml' in r.getheader('Content-Type'))
+ content = r.xml
+ extensions = content.findall(
+ "{http://docs.openstack.org/common/api/v1.0}extension")
+ found_osksadm = False
+ found_oskscatalog = False
+ for extension in extensions:
+ if extension.get("alias") == 'OS-KSADM':
+ found_osksadm = True
+ if extension.get("alias") == 'OS-KSCATALOG':
+ found_oskscatalog = True
+ self.assertTrue(found_osksadm, "Missing OS-KSADM extension.")
+ self.assertTrue(found_oskscatalog, "Missing OS-KSCATALOG extension.")
if __name__ == '__main__':
diff --git a/keystone/test/unit/test_extensions.py b/keystone/test/unit/test_extensions.py
new file mode 100644
index 00000000..5e161ca5
--- /dev/null
+++ b/keystone/test/unit/test_extensions.py
@@ -0,0 +1,65 @@
+import unittest2 as unittest
+from keystone.logic.extension_reader import ExtensionsReader
+from keystone.contrib.extensions import CONFIG_EXTENSION_PROPERTY
+from keystone.contrib.extensions.admin import EXTENSION_ADMIN_PREFIX
+from xml.etree import ElementTree
+import json
+
+
+class MockOptions(object):
+ """ Mock object that mimics options."""
+ def __init__(self, loaded_extensions):
+ self.loaded_extensions = loaded_extensions
+
+ def get(self, prop_name, default):
+ if prop_name == CONFIG_EXTENSION_PROPERTY:
+ return self.loaded_extensions
+
+
+class TestExtensionReader(unittest.TestCase):
+ """Unit tests for ExtensionsReader.These
+ tests check whether the returned extensions vary
+ when they are configured differently."""
+
+ def setUp(self):
+ self.options = MockOptions("osksadm")
+ self.extensions_reader = ExtensionsReader(self.options,
+ EXTENSION_ADMIN_PREFIX)
+
+ def test_extensions_with_only_osksadm_json(self):
+ r = self.extensions_reader.get_extensions().to_json()
+ content = json.loads(r)
+ self.assertIsNotNone(content['extensions'])
+ self.assertIsNotNone(content['extensions']['values'])
+ found_osksadm = False
+ found_oskscatalog = False
+ for value in content['extensions']['values']:
+ if value['extension']['alias'] == 'OS-KSADM':
+ found_osksadm = True
+ if value['extension']['alias'] == 'OS-KSCATALOG':
+ found_oskscatalog = True
+ self.assertTrue(found_osksadm,
+ "Missing OS-KSADM extension.")
+ self.assertFalse(found_oskscatalog,
+ "Non configured OS-KSCATALOG extension returned.")
+
+ def test_extensions_with_only_osksadm_xml(self):
+ r = self.extensions_reader.get_extensions().to_xml()
+ content = ElementTree.XML(r)
+ extensions = content.findall(
+ "{http://docs.openstack.org/common/api/v1.0}extension")
+ found_osksadm = False
+ found_oskscatalog = False
+ for extension in extensions:
+ if extension.get("alias") == 'OS-KSADM':
+ found_osksadm = True
+ if extension.get("alias") == 'OS-KSCATALOG':
+ found_oskscatalog = True
+ self.assertTrue(found_osksadm,
+ "Missing OS-KSADM extension.")
+ self.assertFalse(found_oskscatalog,
+ "Non configured OS-KSCATALOG extension returned.")
+
+
+if __name__ == '__main__':
+ unittest.main()