summaryrefslogtreecommitdiffstats
path: root/nova/api
diff options
context:
space:
mode:
authorBrian Waldon <brian.waldon@rackspace.com>2011-09-20 01:59:05 -0400
committerBrian Waldon <brian.waldon@rackspace.com>2011-09-29 17:04:09 -0400
commit5f7356be106cbe1116708d2e5d30d5a2f4c479a4 (patch)
treeea593522ee9971c12d3ceb3450adbb13e65c0371 /nova/api
parentef22c0054ccb846dd7e81ba35f7e9c2b533d5ff7 (diff)
Moving admin actions to extension
Begins to address LP bug 821145 Change-Id: I2799a8d70a167dda6d56f8fab2fc121fa2365a8a
Diffstat (limited to 'nova/api')
-rw-r--r--nova/api/openstack/__init__.py9
-rw-r--r--nova/api/openstack/contrib/admin_actions.py204
-rw-r--r--nova/api/openstack/contrib/admin_only.py30
-rw-r--r--nova/api/openstack/contrib/hosts.py5
-rw-r--r--nova/api/openstack/contrib/rescue.py14
-rw-r--r--nova/api/openstack/extensions.py26
-rw-r--r--nova/api/openstack/servers.py190
7 files changed, 241 insertions, 237 deletions
diff --git a/nova/api/openstack/__init__.py b/nova/api/openstack/__init__.py
index b517eae2c..a49510816 100644
--- a/nova/api/openstack/__init__.py
+++ b/nova/api/openstack/__init__.py
@@ -115,17 +115,8 @@ class APIRouter(base_wsgi.Router):
if FLAGS.allow_admin_api:
LOG.debug(_("Including admin operations in API."))
- server_members['pause'] = 'POST'
- server_members['unpause'] = 'POST'
server_members['diagnostics'] = 'GET'
server_members['actions'] = 'GET'
- server_members['suspend'] = 'POST'
- server_members['resume'] = 'POST'
- server_members['rescue'] = 'POST'
- server_members['migrate'] = 'POST'
- server_members['unrescue'] = 'POST'
- server_members['reset_network'] = 'POST'
- server_members['inject_network_info'] = 'POST'
mapper.resource("user", "users",
controller=users.create_resource(),
diff --git a/nova/api/openstack/contrib/admin_actions.py b/nova/api/openstack/contrib/admin_actions.py
new file mode 100644
index 000000000..be00eaf29
--- /dev/null
+++ b/nova/api/openstack/contrib/admin_actions.py
@@ -0,0 +1,204 @@
+# Copyright 2011 Openstack, LLC.
+#
+# 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.
+
+"""The rescue mode extension."""
+
+import traceback
+
+import webob
+from webob import exc
+
+from nova import compute
+from nova import exception
+from nova import flags
+from nova import log as logging
+from nova import utils
+from nova.api.openstack import extensions
+from nova.api.openstack import faults
+from nova.scheduler import api as scheduler_api
+
+
+FLAGS = flags.FLAGS
+LOG = logging.getLogger("nova.api.openstack.contrib.admin_actions")
+
+
+class Admin_actions(extensions.ExtensionDescriptor):
+ """Adds a set of admin-only actions to servers"""
+
+ def __init__(self):
+ super(Admin_actions, self).__init__()
+ self.compute_api = compute.API()
+
+ @extensions.admin_only
+ @exception.novaclient_converter
+ @scheduler_api.redirect_handler
+ def _pause(self, input_dict, req, id):
+ """Permit Admins to pause the server"""
+ ctxt = req.environ['nova.context']
+ try:
+ self.compute_api.pause(ctxt, id)
+ except Exception:
+ readable = traceback.format_exc()
+ LOG.exception(_("Compute.api::pause %s"), readable)
+ 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):
+ """Permit Admins to unpause the server"""
+ ctxt = req.environ['nova.context']
+ try:
+ self.compute_api.unpause(ctxt, id)
+ except Exception:
+ readable = traceback.format_exc()
+ LOG.exception(_("Compute.api::unpause %s"), readable)
+ 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):
+ """Permit admins to suspend the server"""
+ context = req.environ['nova.context']
+ try:
+ self.compute_api.suspend(context, id)
+ except Exception:
+ readable = traceback.format_exc()
+ LOG.exception(_("compute.api::suspend %s"), readable)
+ 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):
+ """Permit admins to resume the server from suspend"""
+ context = req.environ['nova.context']
+ try:
+ self.compute_api.resume(context, id)
+ except Exception:
+ readable = traceback.format_exc()
+ LOG.exception(_("compute.api::resume %s"), readable)
+ 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):
+ """Permit admins to migrate a server to a new host"""
+ try:
+ self.compute_api.resize(req.environ['nova.context'], id)
+ except Exception, e:
+ LOG.exception(_("Error in migrate %s"), e)
+ 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):
+ """Permit admins to reset networking on an server"""
+ context = req.environ['nova.context']
+ try:
+ self.compute_api.reset_network(context, id)
+ except Exception:
+ readable = traceback.format_exc()
+ LOG.exception(_("Compute.api::reset_network %s"), readable)
+ 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):
+ """Permit admins to inject network info into a server"""
+ context = req.environ['nova.context']
+ try:
+ self.compute_api.inject_network_info(context, id)
+ except Exception:
+ readable = traceback.format_exc()
+ LOG.exception(_("Compute.api::inject_network_info %s"), readable)
+ 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):
+ """Permit admins to lock a server"""
+ context = req.environ['nova.context']
+ try:
+ self.compute_api.lock(context, id)
+ except Exception:
+ readable = traceback.format_exc()
+ LOG.exception(_("Compute.api::lock %s"), readable)
+ 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):
+ """Permit admins to lock a server"""
+ context = req.environ['nova.context']
+ try:
+ self.compute_api.unlock(context, id)
+ except Exception:
+ readable = traceback.format_exc()
+ LOG.exception(_("Compute.api::unlock %s"), readable)
+ raise exc.HTTPUnprocessableEntity()
+ return webob.Response(status_int=202)
+
+ def get_name(self):
+ return "AdminActions"
+
+ def get_alias(self):
+ return "os-admin-actions"
+
+ def get_description(self):
+ return "Adds admin-only server actions: pause, unpause, " + \
+ "suspend, resume, migrate, resetNetwork, " +\
+ "injectNetworkInfo, lock and unlock"
+
+ def get_namespace(self):
+ return "http://docs.openstack.org/ext/admin-actions/api/v1.1"
+
+ def get_updated(self):
+ return "2011-09-20T00:00:00+00:00"
+
+ def get_actions(self):
+ actions = [
+ extensions.ActionExtension("servers", "pause", self._pause),
+ extensions.ActionExtension("servers", "unpause", self._unpause),
+ extensions.ActionExtension("servers", "suspend", self._suspend),
+ extensions.ActionExtension("servers", "resume", self._resume),
+ extensions.ActionExtension("servers", "migrate", self._migrate),
+
+ extensions.ActionExtension("servers",
+ "resetNetwork",
+ self._reset_network),
+
+ extensions.ActionExtension("servers",
+ "injectNetworkInfo",
+ self._inject_network_info),
+
+ extensions.ActionExtension("servers", "lock", self._lock),
+ extensions.ActionExtension("servers", "unlock", self._unlock),
+ ]
+
+ return actions
diff --git a/nova/api/openstack/contrib/admin_only.py b/nova/api/openstack/contrib/admin_only.py
deleted file mode 100644
index e821c9e1f..000000000
--- a/nova/api/openstack/contrib/admin_only.py
+++ /dev/null
@@ -1,30 +0,0 @@
-# Copyright (c) 2011 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.
-
-"""Decorator for limiting extensions that should be admin-only."""
-
-from functools import wraps
-from nova import flags
-FLAGS = flags.FLAGS
-
-
-def admin_only(fnc):
- @wraps(fnc)
- def _wrapped(self, *args, **kwargs):
- if FLAGS.allow_admin_api:
- return fnc(self, *args, **kwargs)
- return []
- _wrapped.func_name = fnc.func_name
- return _wrapped
diff --git a/nova/api/openstack/contrib/hosts.py b/nova/api/openstack/contrib/hosts.py
index ecaa365b7..4cd908370 100644
--- a/nova/api/openstack/contrib/hosts.py
+++ b/nova/api/openstack/contrib/hosts.py
@@ -24,7 +24,6 @@ from nova import log as logging
from nova.api.openstack import common
from nova.api.openstack import extensions
from nova.api.openstack import faults
-from nova.api.openstack.contrib import admin_only
from nova.scheduler import api as scheduler_api
@@ -105,12 +104,15 @@ 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")
@@ -131,7 +133,6 @@ class Hosts(extensions.ExtensionDescriptor):
def get_updated(self):
return "2011-06-29T00:00:00+00:00"
- @admin_only.admin_only
def get_resources(self):
resources = [extensions.ResourceExtension('os-hosts',
HostController(), collection_actions={'update': 'PUT'},
diff --git a/nova/api/openstack/contrib/rescue.py b/nova/api/openstack/contrib/rescue.py
index 2e5dbab73..3e3459bfe 100644
--- a/nova/api/openstack/contrib/rescue.py
+++ b/nova/api/openstack/contrib/rescue.py
@@ -29,23 +29,13 @@ FLAGS = flags.FLAGS
LOG = logging.getLogger("nova.api.contrib.rescue")
-def wrap_errors(fn):
- """"Ensure errors are not passed along."""
- def wrapped(*args):
- try:
- return fn(*args)
- except Exception, e:
- return faults.Fault(exc.HTTPInternalServerError())
- return wrapped
-
-
class Rescue(exts.ExtensionDescriptor):
"""The Rescue controller for the OpenStack API."""
def __init__(self):
super(Rescue, self).__init__()
self.compute_api = compute.API()
- @wrap_errors
+ @exts.wrap_errors
def _rescue(self, input_dict, req, instance_id):
"""Rescue an instance."""
context = req.environ["nova.context"]
@@ -57,7 +47,7 @@ class Rescue(exts.ExtensionDescriptor):
return {'adminPass': password}
- @wrap_errors
+ @exts.wrap_errors
def _unrescue(self, input_dict, req, instance_id):
"""Unrescue an instance."""
context = req.environ["nova.context"]
diff --git a/nova/api/openstack/extensions.py b/nova/api/openstack/extensions.py
index efede945f..fae2bb1b1 100644
--- a/nova/api/openstack/extensions.py
+++ b/nova/api/openstack/extensions.py
@@ -16,14 +16,16 @@
# 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
-from lxml import etree
from nova import exception
from nova import flags
@@ -36,7 +38,7 @@ from nova.api.openstack import wsgi
from nova.api.openstack import xmlutil
-LOG = logging.getLogger('extensions')
+LOG = logging.getLogger('nova.api.openstack.extensions')
FLAGS = flags.FLAGS
@@ -523,3 +525,23 @@ class ExtensionsXMLSerializer(wsgi.XMLDictSerializer):
"""Convert the xml object to an xml string."""
return etree.tostring(root, encoding='UTF-8')
+
+
+def admin_only(fnc):
+ @functools.wraps(fnc)
+ def _wrapped(self, *args, **kwargs):
+ if FLAGS.allow_admin_api:
+ return fnc(self, *args, **kwargs)
+ return faults.Fault(webob.exc.HTTPNotFound())
+ _wrapped.func_name = fnc.func_name
+ return _wrapped
+
+
+def wrap_errors(fn):
+ """"Ensure errors are not passed along."""
+ def wrapped(*args):
+ try:
+ return fn(*args)
+ except Exception, e:
+ return faults.Fault(webob.exc.HTTPInternalServerError())
+ return wrapped
diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py
index de081343e..814fcb8b5 100644
--- a/nova/api/openstack/servers.py
+++ b/nova/api/openstack/servers.py
@@ -18,7 +18,6 @@ import base64
import os
import traceback
-from novaclient import exceptions as novaclient_exceptions
from lxml import etree
from webob import exc
import webob
@@ -59,19 +58,6 @@ class ConvertedException(exc.WSGIHTTPException):
super(ConvertedException, self).__init__()
-def novaclient_exception_converter(f):
- """Convert novaclient ClientException HTTP codes to webob exceptions.
- Has to be the outer-most decorator.
- """
- def new_f(*args, **kwargs):
- try:
- ret = f(*args, **kwargs)
- return ret
- except novaclient_exceptions.ClientException, e:
- raise ConvertedException(e.code, e.message, e.details)
- return new_f
-
-
class Controller(object):
""" The Server API base controller class for the OpenStack API """
@@ -354,7 +340,7 @@ class Controller(object):
expl = _('Userdata content cannot be decoded')
raise exc.HTTPBadRequest(explanation=expl)
- @novaclient_exception_converter
+ @exception.novaclient_converter
@scheduler_api.redirect_handler
def show(self, req, id):
""" Returns server details by server id """
@@ -580,7 +566,7 @@ class Controller(object):
def _update(self, context, req, id, inst_dict):
return exc.HTTPNotImplemented()
- @novaclient_exception_converter
+ @exception.novaclient_converter
@scheduler_api.redirect_handler
def action(self, req, id, body):
"""Multi-purpose method used to take actions on a server"""
@@ -719,41 +705,7 @@ class Controller(object):
raise exc.HTTPUnprocessableEntity()
return webob.Response(status_int=202)
- @novaclient_exception_converter
- @scheduler_api.redirect_handler
- def lock(self, req, id):
- """
- lock the instance with id
- admin only operation
-
- """
- context = req.environ['nova.context']
- try:
- self.compute_api.lock(context, id)
- except Exception:
- readable = traceback.format_exc()
- LOG.exception(_("Compute.api::lock %s"), readable)
- raise exc.HTTPUnprocessableEntity()
- return webob.Response(status_int=202)
-
- @novaclient_exception_converter
- @scheduler_api.redirect_handler
- def unlock(self, req, id):
- """
- unlock the instance with id
- admin only operation
-
- """
- context = req.environ['nova.context']
- try:
- self.compute_api.unlock(context, id)
- except Exception:
- readable = traceback.format_exc()
- LOG.exception(_("Compute.api::unlock %s"), readable)
- raise exc.HTTPUnprocessableEntity()
- return webob.Response(status_int=202)
-
- @novaclient_exception_converter
+ @exception.novaclient_converter
@scheduler_api.redirect_handler
def get_lock(self, req, id):
"""
@@ -769,133 +721,7 @@ class Controller(object):
raise exc.HTTPUnprocessableEntity()
return webob.Response(status_int=202)
- @novaclient_exception_converter
- @scheduler_api.redirect_handler
- def reset_network(self, req, id):
- """
- Reset networking on an instance (admin only).
-
- """
- context = req.environ['nova.context']
- try:
- self.compute_api.reset_network(context, id)
- except Exception:
- readable = traceback.format_exc()
- LOG.exception(_("Compute.api::reset_network %s"), readable)
- raise exc.HTTPUnprocessableEntity()
- return webob.Response(status_int=202)
-
- @novaclient_exception_converter
- @scheduler_api.redirect_handler
- def inject_network_info(self, req, id):
- """
- Inject network info for an instance (admin only).
-
- """
- context = req.environ['nova.context']
- try:
- self.compute_api.inject_network_info(context, id)
- except Exception:
- readable = traceback.format_exc()
- LOG.exception(_("Compute.api::inject_network_info %s"), readable)
- raise exc.HTTPUnprocessableEntity()
- return webob.Response(status_int=202)
-
- @novaclient_exception_converter
- @scheduler_api.redirect_handler
- def pause(self, req, id):
- """ Permit Admins to Pause the server. """
- ctxt = req.environ['nova.context']
- try:
- self.compute_api.pause(ctxt, id)
- except Exception:
- readable = traceback.format_exc()
- LOG.exception(_("Compute.api::pause %s"), readable)
- raise exc.HTTPUnprocessableEntity()
- return webob.Response(status_int=202)
-
- @novaclient_exception_converter
- @scheduler_api.redirect_handler
- def unpause(self, req, id):
- """ Permit Admins to Unpause the server. """
- ctxt = req.environ['nova.context']
- try:
- self.compute_api.unpause(ctxt, id)
- except Exception:
- readable = traceback.format_exc()
- LOG.exception(_("Compute.api::unpause %s"), readable)
- raise exc.HTTPUnprocessableEntity()
- return webob.Response(status_int=202)
-
- @novaclient_exception_converter
- @scheduler_api.redirect_handler
- def suspend(self, req, id):
- """permit admins to suspend the server"""
- context = req.environ['nova.context']
- try:
- self.compute_api.suspend(context, id)
- except Exception:
- readable = traceback.format_exc()
- LOG.exception(_("compute.api::suspend %s"), readable)
- raise exc.HTTPUnprocessableEntity()
- return webob.Response(status_int=202)
-
- @novaclient_exception_converter
- @scheduler_api.redirect_handler
- def resume(self, req, id):
- """permit admins to resume the server from suspend"""
- context = req.environ['nova.context']
- try:
- self.compute_api.resume(context, id)
- except Exception:
- readable = traceback.format_exc()
- LOG.exception(_("compute.api::resume %s"), readable)
- raise exc.HTTPUnprocessableEntity()
- return webob.Response(status_int=202)
-
- @novaclient_exception_converter
- @scheduler_api.redirect_handler
- def migrate(self, req, id):
- try:
- self.compute_api.resize(req.environ['nova.context'], id)
- except Exception, e:
- LOG.exception(_("Error in migrate %s"), e)
- raise exc.HTTPBadRequest()
- return webob.Response(status_int=202)
-
- @novaclient_exception_converter
- @scheduler_api.redirect_handler
- def rescue(self, req, id, body={}):
- """Permit users to rescue the server."""
- context = req.environ["nova.context"]
- try:
- if 'rescue' in body and body['rescue'] and \
- 'adminPass' in body['rescue']:
- password = body['rescue']['adminPass']
- else:
- password = utils.generate_password(FLAGS.password_length)
- self.compute_api.rescue(context, id, rescue_password=password)
- except Exception:
- readable = traceback.format_exc()
- LOG.exception(_("compute.api::rescue %s"), readable)
- raise exc.HTTPUnprocessableEntity()
-
- return {'adminPass': password}
-
- @novaclient_exception_converter
- @scheduler_api.redirect_handler
- def unrescue(self, req, id):
- """Permit users to unrescue the server."""
- context = req.environ["nova.context"]
- try:
- self.compute_api.unrescue(context, id)
- except Exception:
- readable = traceback.format_exc()
- LOG.exception(_("compute.api::unrescue %s"), readable)
- raise exc.HTTPUnprocessableEntity()
- return webob.Response(status_int=202)
-
- @novaclient_exception_converter
+ @exception.novaclient_converter
@scheduler_api.redirect_handler
def get_ajax_console(self, req, id):
"""Returns a url to an instance's ajaxterm console."""
@@ -906,7 +732,7 @@ class Controller(object):
raise exc.HTTPNotFound()
return webob.Response(status_int=202)
- @novaclient_exception_converter
+ @exception.novaclient_converter
@scheduler_api.redirect_handler
def get_vnc_console(self, req, id):
"""Returns a url to an instance's ajaxterm console."""
@@ -917,7 +743,7 @@ class Controller(object):
raise exc.HTTPNotFound()
return webob.Response(status_int=202)
- @novaclient_exception_converter
+ @exception.novaclient_converter
@scheduler_api.redirect_handler
def diagnostics(self, req, id):
"""Permit Admins to retrieve server diagnostics."""
@@ -960,7 +786,7 @@ class Controller(object):
class ControllerV10(Controller):
"""v1.0 OpenStack API controller"""
- @novaclient_exception_converter
+ @exception.novaclient_converter
@scheduler_api.redirect_handler
def delete(self, req, id):
""" Destroys a server """
@@ -1047,7 +873,7 @@ class ControllerV10(Controller):
class ControllerV11(Controller):
"""v1.1 OpenStack API controller"""
- @novaclient_exception_converter
+ @exception.novaclient_converter
@scheduler_api.redirect_handler
def delete(self, req, id):
""" Destroys a server """