summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJenkins <jenkins@review.openstack.org>2011-12-20 17:10:55 +0000
committerGerrit Code Review <review@openstack.org>2011-12-20 17:10:55 +0000
commit3828b89182a77a25cf42658d91c49e787dfea05c (patch)
treed958a585682689435d85dd7ca2c70fcde7e240ce
parente662da0c634c89ef65150540b9ebbde353d3450e (diff)
parentc6ea206bf81830ce949f33bde928226435c99f4b (diff)
downloadnova-3828b89182a77a25cf42658d91c49e787dfea05c.tar.gz
nova-3828b89182a77a25cf42658d91c49e787dfea05c.tar.xz
nova-3828b89182a77a25cf42658d91c49e787dfea05c.zip
Merge "Creating mechanism that loads Admin API extensions"
-rw-r--r--nova/api/openstack/v2/contrib/admin_actions.py13
-rw-r--r--nova/api/openstack/v2/contrib/extended_status.py15
-rw-r--r--nova/api/openstack/v2/contrib/hosts.py7
-rw-r--r--nova/api/openstack/v2/contrib/simple_tenant_usage.py8
-rw-r--r--nova/api/openstack/v2/extensions.py33
-rw-r--r--nova/tests/api/openstack/v2/contrib/test_admin_actions.py20
-rw-r--r--nova/tests/api/openstack/v2/contrib/test_extendedstatus.py16
-rw-r--r--nova/tests/api/openstack/v2/contrib/test_hosts.py (renamed from nova/tests/test_hosts.py)0
-rw-r--r--nova/tests/api/openstack/v2/extensions/foxinsocks.py2
-rw-r--r--nova/tests/api/openstack/v2/test_extensions.py32
10 files changed, 69 insertions, 77 deletions
diff --git a/nova/api/openstack/v2/contrib/admin_actions.py b/nova/api/openstack/v2/contrib/admin_actions.py
index 632a8dc0e..46a4ec6f0 100644
--- a/nova/api/openstack/v2/contrib/admin_actions.py
+++ b/nova/api/openstack/v2/contrib/admin_actions.py
@@ -12,8 +12,6 @@
# License for the specific language governing permissions and limitations
# under the License.
-"""The rescue mode extension."""
-
import traceback
import webob
@@ -40,12 +38,12 @@ class Admin_actions(extensions.ExtensionDescriptor):
alias = "os-admin-actions"
namespace = "http://docs.openstack.org/ext/admin-actions/api/v1.1"
updated = "2011-09-20T00:00:00+00:00"
+ admin_only = True
def __init__(self, ext_mgr):
super(Admin_actions, self).__init__(ext_mgr)
self.compute_api = compute.API()
- @extensions.admin_only
@exception.novaclient_converter
@scheduler_api.redirect_handler
def _pause(self, input_dict, req, id):
@@ -60,7 +58,6 @@ class Admin_actions(extensions.ExtensionDescriptor):
raise exc.HTTPUnprocessableEntity()
return webob.Response(status_int=202)
- @extensions.admin_only
@exception.novaclient_converter
@scheduler_api.redirect_handler
def _unpause(self, input_dict, req, id):
@@ -75,7 +72,6 @@ class Admin_actions(extensions.ExtensionDescriptor):
raise exc.HTTPUnprocessableEntity()
return webob.Response(status_int=202)
- @extensions.admin_only
@exception.novaclient_converter
@scheduler_api.redirect_handler
def _suspend(self, input_dict, req, id):
@@ -90,7 +86,6 @@ class Admin_actions(extensions.ExtensionDescriptor):
raise exc.HTTPUnprocessableEntity()
return webob.Response(status_int=202)
- @extensions.admin_only
@exception.novaclient_converter
@scheduler_api.redirect_handler
def _resume(self, input_dict, req, id):
@@ -105,7 +100,6 @@ class Admin_actions(extensions.ExtensionDescriptor):
raise exc.HTTPUnprocessableEntity()
return webob.Response(status_int=202)
- @extensions.admin_only
@exception.novaclient_converter
@scheduler_api.redirect_handler
def _migrate(self, input_dict, req, id):
@@ -117,7 +111,6 @@ class Admin_actions(extensions.ExtensionDescriptor):
raise exc.HTTPBadRequest()
return webob.Response(status_int=202)
- @extensions.admin_only
@exception.novaclient_converter
@scheduler_api.redirect_handler
def _reset_network(self, input_dict, req, id):
@@ -132,7 +125,6 @@ class Admin_actions(extensions.ExtensionDescriptor):
raise exc.HTTPUnprocessableEntity()
return webob.Response(status_int=202)
- @extensions.admin_only
@exception.novaclient_converter
@scheduler_api.redirect_handler
def _inject_network_info(self, input_dict, req, id):
@@ -149,7 +141,6 @@ class Admin_actions(extensions.ExtensionDescriptor):
raise exc.HTTPUnprocessableEntity()
return webob.Response(status_int=202)
- @extensions.admin_only
@exception.novaclient_converter
@scheduler_api.redirect_handler
def _lock(self, input_dict, req, id):
@@ -166,7 +157,6 @@ class Admin_actions(extensions.ExtensionDescriptor):
raise exc.HTTPUnprocessableEntity()
return webob.Response(status_int=202)
- @extensions.admin_only
@exception.novaclient_converter
@scheduler_api.redirect_handler
def _unlock(self, input_dict, req, id):
@@ -185,6 +175,7 @@ class Admin_actions(extensions.ExtensionDescriptor):
def get_actions(self):
actions = [
+ #TODO(bcwaldon): These actions should be prefixed with 'os-'
extensions.ActionExtension("servers", "pause", self._pause),
extensions.ActionExtension("servers", "unpause", self._unpause),
extensions.ActionExtension("servers", "suspend", self._suspend),
diff --git a/nova/api/openstack/v2/contrib/extended_status.py b/nova/api/openstack/v2/contrib/extended_status.py
index 5e0a69780..d2e2fe914 100644
--- a/nova/api/openstack/v2/contrib/extended_status.py
+++ b/nova/api/openstack/v2/contrib/extended_status.py
@@ -14,9 +14,6 @@
"""The Extended Status Admin API extension."""
-import traceback
-
-import webob
from webob import exc
from nova.api.openstack.v2 import extensions
@@ -38,6 +35,7 @@ class Extended_status(extensions.ExtensionDescriptor):
alias = "OS-EXT-STS"
namespace = "http://docs.openstack.org/ext/extended_status/api/v1.1"
updated = "2011-11-03T00:00:00+00:00"
+ admin_only = True
def get_request_extensions(self):
request_extensions = []
@@ -69,6 +67,8 @@ class Extended_status(extensions.ExtensionDescriptor):
explanation = _("Server not found.")
raise exc.HTTPNotFound(explanation=explanation)
+ #TODO(bcwaldon): these attributes should be prefixed with
+ # something specific to this extension
for state in ['task_state', 'vm_state', 'power_state']:
key = "%s:%s" % (Extended_status.alias, state)
server[key] = inst_ref[state]
@@ -87,11 +87,10 @@ class Extended_status(extensions.ExtensionDescriptor):
_get_and_extend_all(context, body)
return res
- if FLAGS.allow_admin_api:
- req_ext = extensions.RequestExtension('GET',
- '/:(project_id)/servers/:(id)',
- _extended_status_handler)
- request_extensions.append(req_ext)
+ req_ext = extensions.RequestExtension('GET',
+ '/:(project_id)/servers/:(id)',
+ _extended_status_handler)
+ request_extensions.append(req_ext)
return request_extensions
diff --git a/nova/api/openstack/v2/contrib/hosts.py b/nova/api/openstack/v2/contrib/hosts.py
index b422a80ab..bf1dd10da 100644
--- a/nova/api/openstack/v2/contrib/hosts.py
+++ b/nova/api/openstack/v2/contrib/hosts.py
@@ -19,7 +19,6 @@ import webob.exc
from xml.dom import minidom
from xml.parsers import expat
-from nova.api.openstack import common
from nova.api.openstack import wsgi
from nova.api.openstack import xmlutil
from nova.api.openstack.v2 import extensions
@@ -107,15 +106,12 @@ class HostController(object):
raise webob.exc.HTTPBadRequest(explanation=e.msg)
return {"host": host, "power_action": result}
- @extensions.admin_only
def startup(self, req, id):
return self._host_power_action(req, host=id, action="startup")
- @extensions.admin_only
def shutdown(self, req, id):
return self._host_power_action(req, host=id, action="shutdown")
- @extensions.admin_only
def reboot(self, req, id):
return self._host_power_action(req, host=id, action="reboot")
@@ -179,12 +175,13 @@ class HostDeserializer(wsgi.XMLDeserializer):
class Hosts(extensions.ExtensionDescriptor):
- """Host administration"""
+ """Admin-only host administration"""
name = "Hosts"
alias = "os-hosts"
namespace = "http://docs.openstack.org/ext/hosts/api/v1.1"
updated = "2011-06-29T00:00:00+00:00"
+ admin_only = True
def get_resources(self):
body_serializers = {
diff --git a/nova/api/openstack/v2/contrib/simple_tenant_usage.py b/nova/api/openstack/v2/contrib/simple_tenant_usage.py
index 4204c53bf..1d3053420 100644
--- a/nova/api/openstack/v2/contrib/simple_tenant_usage.py
+++ b/nova/api/openstack/v2/contrib/simple_tenant_usage.py
@@ -19,14 +19,11 @@ from datetime import datetime
import urlparse
import webob
-from webob import exc
from nova.api.openstack.v2 import extensions
-from nova.api.openstack.v2 import views
from nova.api.openstack import wsgi
from nova.api.openstack import xmlutil
from nova.compute import api
-from nova.db.sqlalchemy.session import get_session
from nova import exception
from nova import flags
@@ -181,7 +178,7 @@ class SimpleTenantUsageController(object):
"""Retrive tenant_usage for all tenants"""
context = req.environ['nova.context']
- if not context.is_admin and FLAGS.allow_admin_api:
+ if not context.is_admin:
return webob.Response(status_int=403)
(period_start, period_stop, detailed) = self._get_datetime_range(req)
@@ -196,7 +193,7 @@ class SimpleTenantUsageController(object):
tenant_id = id
context = req.environ['nova.context']
- if not context.is_admin and FLAGS.allow_admin_api:
+ if not context.is_admin:
if tenant_id != context.project_id:
return webob.Response(status_int=403)
@@ -261,6 +258,7 @@ class Simple_tenant_usage(extensions.ExtensionDescriptor):
alias = "os-simple-tenant-usage"
namespace = "http://docs.openstack.org/ext/os-simple-tenant-usage/api/v1.1"
updated = "2011-08-19T00:00:00+00:00"
+ admin_only = True
def get_resources(self):
resources = []
diff --git a/nova/api/openstack/v2/extensions.py b/nova/api/openstack/v2/extensions.py
index b3fbfc923..b6ed897cb 100644
--- a/nova/api/openstack/v2/extensions.py
+++ b/nova/api/openstack/v2/extensions.py
@@ -16,19 +16,11 @@
# License for the specific language governing permissions and limitations
# under the License.
-import functools
-import imp
-import inspect
-import os
-import sys
-
-from lxml import etree
import routes
import webob.dec
import webob.exc
import nova.api.openstack.v2
-from nova.api.openstack import common
from nova.api.openstack import wsgi
from nova.api.openstack import xmlutil
from nova import exception
@@ -68,6 +60,10 @@ class ExtensionDescriptor(object):
# '2011-01-22T13:25:27-06:00'
updated = None
+ # This attribute causes the extension to load only when
+ # the admin api is enabled
+ admin_only = False
+
def __init__(self, ext_mgr):
"""Register extension with the extension manager."""
@@ -426,9 +422,15 @@ class ExtensionManager(object):
' '.join(extension.__doc__.strip().split()))
LOG.debug(_('Ext namespace: %s'), extension.namespace)
LOG.debug(_('Ext updated: %s'), extension.updated)
+ LOG.debug(_('Ext admin_only: %s'), extension.admin_only)
except AttributeError as ex:
LOG.exception(_("Exception loading extension: %s"), unicode(ex))
return False
+
+ # Don't load admin api extensions if the admin api isn't enabled
+ if not FLAGS.allow_admin_api and extension.admin_only:
+ return False
+
return True
def load_extension(self, ext_factory):
@@ -443,6 +445,7 @@ class ExtensionManager(object):
LOG.debug(_("Loading extension %s"), ext_factory)
# Load the factory
+
factory = utils.import_class(ext_factory)
# Call it
@@ -452,7 +455,9 @@ class ExtensionManager(object):
def _load_extensions(self):
"""Load extensions specified on the command line."""
- for ext_factory in FLAGS.osapi_extension:
+ extensions = list(FLAGS.osapi_extension)
+
+ for ext_factory in extensions:
try:
self.load_extension(ext_factory)
except Exception as exc:
@@ -542,16 +547,6 @@ class ExtensionsXMLSerializer(xmlutil.XMLTemplateSerializer):
return ExtensionTemplate()
-def admin_only(fnc):
- @functools.wraps(fnc)
- def _wrapped(self, *args, **kwargs):
- if FLAGS.allow_admin_api:
- return fnc(self, *args, **kwargs)
- raise webob.exc.HTTPNotFound()
- _wrapped.func_name = fnc.func_name
- return _wrapped
-
-
def wrap_errors(fn):
"""Ensure errors are not passed along."""
def wrapped(*args):
diff --git a/nova/tests/api/openstack/v2/contrib/test_admin_actions.py b/nova/tests/api/openstack/v2/contrib/test_admin_actions.py
index 159314ed9..a7237ae58 100644
--- a/nova/tests/api/openstack/v2/contrib/test_admin_actions.py
+++ b/nova/tests/api/openstack/v2/contrib/test_admin_actions.py
@@ -20,6 +20,7 @@ import webob
from nova import compute
from nova import flags
from nova import test
+from nova import utils
from nova.tests.api.openstack import fakes
@@ -60,28 +61,19 @@ class AdminActionsTest(test.TestCase):
def setUp(self):
super(AdminActionsTest, self).setUp()
- self.flags(allow_admin_api=True)
self.stubs.Set(compute.API, 'get', fake_compute_api_get)
+ self.UUID = utils.gen_uuid()
+ self.flags(allow_admin_api=True)
for _method in self._methods:
self.stubs.Set(compute.API, _method, fake_compute_api)
- def test_admin_api_enabled(self):
+ def test_admin_api_actions(self):
+ self.maxDiff = None
app = fakes.wsgi_app()
for _action in self._actions:
- req = webob.Request.blank('/v2/fake/servers/abcd/action')
+ req = webob.Request.blank('/v2/fake/servers/%s/action' % self.UUID)
req.method = 'POST'
req.body = json.dumps({_action: None})
req.content_type = 'application/json'
res = req.get_response(app)
self.assertEqual(res.status_int, 202)
-
- def test_admin_api_disabled(self):
- FLAGS.allow_admin_api = False
- app = fakes.wsgi_app()
- for _action in self._actions:
- req = webob.Request.blank('/v2/fake/servers/abcd/action')
- req.method = 'POST'
- req.body = json.dumps({_action: None})
- req.content_type = 'application/json'
- res = req.get_response(app)
- self.assertEqual(res.status_int, 404)
diff --git a/nova/tests/api/openstack/v2/contrib/test_extendedstatus.py b/nova/tests/api/openstack/v2/contrib/test_extendedstatus.py
index 59d44191a..66565650e 100644
--- a/nova/tests/api/openstack/v2/contrib/test_extendedstatus.py
+++ b/nova/tests/api/openstack/v2/contrib/test_extendedstatus.py
@@ -41,6 +41,7 @@ class ExtendedStatusTest(test.TestCase):
self.uuid = '70f6db34-de8d-4fbd-aafb-4065bdfa6114'
self.url = '/v2/openstack/servers/%s' % self.uuid
fakes.stub_out_nw_api(self.stubs)
+ self.flags(allow_admin_api=True)
self.stubs.Set(compute.api.API, 'routing_get', fake_compute_get)
def _make_request(self):
@@ -54,8 +55,7 @@ class ExtendedStatusTest(test.TestCase):
self.assertEqual(server.get('OS-EXT-STS:power_state'), power_state)
self.assertEqual(server.get('OS-EXT-STS:task_state'), task_state)
- def test_extended_status_with_admin(self):
- self.flags(allow_admin_api=True)
+ def test_extended_status(self):
res = self._make_request()
body = json.loads(res.body)
@@ -65,19 +65,7 @@ class ExtendedStatusTest(test.TestCase):
power_state='empowered',
task_state='kayaking')
- def test_extended_status_no_admin(self):
- self.flags(allow_admin_api=False)
- res = self._make_request()
- body = json.loads(res.body)
-
- self.assertEqual(res.status_int, 200)
- self.assertServerStates(body['server'],
- vm_state=None,
- power_state=None,
- task_state=None)
-
def test_extended_status_no_instance_fails(self):
- self.flags(allow_admin_api=True)
def fake_compute_get(*args, **kwargs):
raise exception.InstanceNotFound()
diff --git a/nova/tests/test_hosts.py b/nova/tests/api/openstack/v2/contrib/test_hosts.py
index a537ff2f3..a537ff2f3 100644
--- a/nova/tests/test_hosts.py
+++ b/nova/tests/api/openstack/v2/contrib/test_hosts.py
diff --git a/nova/tests/api/openstack/v2/extensions/foxinsocks.py b/nova/tests/api/openstack/v2/extensions/foxinsocks.py
index d2995953a..ba1508668 100644
--- a/nova/tests/api/openstack/v2/extensions/foxinsocks.py
+++ b/nova/tests/api/openstack/v2/extensions/foxinsocks.py
@@ -26,7 +26,7 @@ class FoxInSocksController(object):
return "Try to say this Mr. Knox, sir..."
-class Foxinsocks(object):
+class Foxinsocks(extensions.ExtensionDescriptor):
"""The Fox In Socks Extension"""
name = "Fox In Socks"
diff --git a/nova/tests/api/openstack/v2/test_extensions.py b/nova/tests/api/openstack/v2/test_extensions.py
index 2c29f6eed..a3027194b 100644
--- a/nova/tests/api/openstack/v2/test_extensions.py
+++ b/nova/tests/api/openstack/v2/test_extensions.py
@@ -96,6 +96,7 @@ class ExtensionControllerTest(ExtensionTestCase):
def setUp(self):
super(ExtensionControllerTest, self).setUp()
+ self.flags(allow_admin_api=True)
self.ext_list = [
"AdminActions",
"Console_output",
@@ -307,6 +308,19 @@ class InvalidExtension(object):
alias = "THIRD"
+class AdminExtension(extensions.ExtensionDescriptor):
+ """Admin-only extension"""
+
+ name = "Admin Ext"
+ alias = "ADMIN"
+ namespace = "http://www.example.com/"
+ updated = "2011-01-22T13:25:27-06:00"
+ admin_only = True
+
+ def __init__(self, *args, **kwargs):
+ pass
+
+
class ExtensionManagerTest(ExtensionTestCase):
response_body = "Try to say this Mr. Knox, sir..."
@@ -330,6 +344,24 @@ class ExtensionManagerTest(ExtensionTestCase):
self.assertTrue('FOXNSOX' in ext_mgr.extensions)
self.assertTrue('THIRD' not in ext_mgr.extensions)
+ def test_admin_extensions(self):
+ self.flags(allow_admin_api=True)
+ app = v2.APIRouter()
+ ext_midware = extensions.ExtensionMiddleware(app)
+ ext_mgr = ext_midware.ext_mgr
+ ext_mgr.register(AdminExtension())
+ self.assertTrue('FOXNSOX' in ext_mgr.extensions)
+ self.assertTrue('ADMIN' in ext_mgr.extensions)
+
+ def test_admin_extensions_no_admin_api(self):
+ self.flags(allow_admin_api=False)
+ app = v2.APIRouter()
+ ext_midware = extensions.ExtensionMiddleware(app)
+ ext_mgr = ext_midware.ext_mgr
+ ext_mgr.register(AdminExtension())
+ self.assertTrue('FOXNSOX' in ext_mgr.extensions)
+ self.assertTrue('ADMIN' not in ext_mgr.extensions)
+
class ActionExtensionTest(ExtensionTestCase):