summaryrefslogtreecommitdiffstats
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
parentef22c0054ccb846dd7e81ba35f7e9c2b533d5ff7 (diff)
Moving admin actions to extension
Begins to address LP bug 821145 Change-Id: I2799a8d70a167dda6d56f8fab2fc121fa2365a8a
-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
-rw-r--r--nova/exception.py15
-rw-r--r--nova/tests/api/openstack/contrib/test_admin_actions.py64
-rw-r--r--nova/tests/api/openstack/test_extensions.py1
-rw-r--r--nova/tests/api/openstack/test_server_actions.py17
-rw-r--r--nova/tests/api/openstack/test_servers.py113
12 files changed, 321 insertions, 367 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 """
diff --git a/nova/exception.py b/nova/exception.py
index e4d3b16f8..84c08dce8 100644
--- a/nova/exception.py
+++ b/nova/exception.py
@@ -27,11 +27,26 @@ SHOULD include dedicated exception logging.
from functools import wraps
import sys
+from novaclient import exceptions as novaclient_exceptions
+
from nova import log as logging
LOG = logging.getLogger('nova.exception')
+def novaclient_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 ProcessExecutionError(IOError):
def __init__(self, stdout=None, stderr=None, exit_code=None, cmd=None,
description=None):
diff --git a/nova/tests/api/openstack/contrib/test_admin_actions.py b/nova/tests/api/openstack/contrib/test_admin_actions.py
new file mode 100644
index 000000000..98178d111
--- /dev/null
+++ b/nova/tests/api/openstack/contrib/test_admin_actions.py
@@ -0,0 +1,64 @@
+# 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.
+
+import json
+import webob
+
+from nova import compute
+from nova import flags
+from nova import test
+from nova.tests.api.openstack import fakes
+
+
+FLAGS = flags.FLAGS
+
+
+def fake_compute_api(cls, req, id):
+ return True
+
+
+class AdminActionsTest(test.TestCase):
+
+ _actions = ('pause', 'unpause', 'suspend', 'resume', 'migrate',
+ 'resetNetwork', 'injectNetworkInfo', 'lock', 'unlock')
+
+ _methods = ('pause', 'unpause', 'suspend', 'resume', 'resize',
+ 'reset_network', 'inject_network_info', 'lock', 'unlock')
+
+ def setUp(self):
+ super(AdminActionsTest, self).setUp()
+ 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):
+ app = fakes.wsgi_app()
+ for _action in self._actions:
+ req = webob.Request.blank('/v1.1/fake/servers/1/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, 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('/v1.1/fake/servers/1/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/test_extensions.py b/nova/tests/api/openstack/test_extensions.py
index 4f66f5405..ce918933b 100644
--- a/nova/tests/api/openstack/test_extensions.py
+++ b/nova/tests/api/openstack/test_extensions.py
@@ -85,6 +85,7 @@ class ExtensionControllerTest(test.TestCase):
ext_path = os.path.join(os.path.dirname(__file__), "extensions")
self.flags(osapi_extensions_path=ext_path)
self.ext_list = [
+ "AdminActions",
"Createserverext",
"DeferredDelete",
"DiskConfig",
diff --git a/nova/tests/api/openstack/test_server_actions.py b/nova/tests/api/openstack/test_server_actions.py
index 04b603237..ae40b3c95 100644
--- a/nova/tests/api/openstack/test_server_actions.py
+++ b/nova/tests/api/openstack/test_server_actions.py
@@ -299,23 +299,6 @@ class ServerActionsTest(test.TestCase):
res = req.get_response(fakes.wsgi_app())
self.assertEqual(res.status_int, 400)
- def test_migrate_server(self):
- """This is basically the same as resize, only we provide the `migrate`
- attribute in the body's dict.
- """
- req = self.webreq('/1/migrate', 'POST')
-
- self.resize_called = False
-
- def resize_mock(*args):
- self.resize_called = True
-
- self.stubs.Set(nova.compute.api.API, 'resize', resize_mock)
-
- res = req.get_response(fakes.wsgi_app())
- self.assertEqual(res.status_int, 202)
- self.assertEqual(self.resize_called, True)
-
def test_create_backup(self):
"""The happy path for creating backups"""
self.flags(allow_admin_api=True)
diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py
index 97eb1da05..e4db6859a 100644
--- a/nova/tests/api/openstack/test_servers.py
+++ b/nova/tests/api/openstack/test_servers.py
@@ -261,10 +261,6 @@ class ServersTest(test.TestCase):
instance_addresses)
self.stubs.Set(nova.db.api, 'instance_get_floating_address',
instance_addresses)
- self.stubs.Set(nova.compute.API, 'pause', fake_compute_api)
- self.stubs.Set(nova.compute.API, 'unpause', fake_compute_api)
- self.stubs.Set(nova.compute.API, 'suspend', fake_compute_api)
- self.stubs.Set(nova.compute.API, 'resume', fake_compute_api)
self.stubs.Set(nova.compute.API, "get_diagnostics", fake_compute_api)
self.stubs.Set(nova.compute.API, "get_actions", fake_compute_api)
@@ -2700,55 +2696,6 @@ class ServersTest(test.TestCase):
self.assertEqual(s['imageId'], 10)
self.assertEqual(s['flavorId'], 1)
- def test_server_pause(self):
- self.flags(allow_admin_api=True)
- req = webob.Request.blank('/v1.0/servers/1/pause')
- req.method = 'POST'
- req.content_type = 'application/json'
- res = req.get_response(fakes.wsgi_app())
- self.assertEqual(res.status_int, 202)
-
- def test_server_unpause(self):
- self.flags(allow_admin_api=True)
- req = webob.Request.blank('/v1.0/servers/1/unpause')
- req.method = 'POST'
- req.content_type = 'application/json'
- res = req.get_response(fakes.wsgi_app())
- self.assertEqual(res.status_int, 202)
-
- def test_server_suspend(self):
- self.flags(allow_admin_api=True)
- req = webob.Request.blank('/v1.0/servers/1/suspend')
- req.method = 'POST'
- req.content_type = 'application/json'
- res = req.get_response(fakes.wsgi_app())
- self.assertEqual(res.status_int, 202)
-
- def test_server_resume(self):
- self.flags(allow_admin_api=True)
- req = webob.Request.blank('/v1.0/servers/1/resume')
- req.method = 'POST'
- req.content_type = 'application/json'
- res = req.get_response(fakes.wsgi_app())
- self.assertEqual(res.status_int, 202)
-
- def test_server_reset_network(self):
- self.flags(allow_admin_api=True)
- req = webob.Request.blank('/v1.0/servers/1/reset_network')
- req.method = 'POST'
- req.content_type = 'application/json'
- res = req.get_response(fakes.wsgi_app())
- self.assertEqual(res.status_int, 202)
-
- def test_server_inject_network_info(self):
- self.flags(allow_admin_api=True)
- req = webob.Request.blank(
- '/v1.0/servers/1/inject_network_info')
- req.method = 'POST'
- req.content_type = 'application/json'
- res = req.get_response(fakes.wsgi_app())
- self.assertEqual(res.status_int, 202)
-
def test_server_diagnostics(self):
self.flags(allow_admin_api=False)
req = webob.Request.blank("/v1.0/servers/1/diagnostics")
@@ -2779,66 +2726,6 @@ class ServersTest(test.TestCase):
self.assertEqual(res.status, '202 Accepted')
self.assertEqual(self.server_delete_called, True)
- def test_rescue_generates_password(self):
- self.flags(allow_admin_api=True)
-
- self.called = False
-
- def rescue_mock(*args, **kwargs):
- self.called = True
-
- self.stubs.Set(nova.compute.api.API, 'rescue', rescue_mock)
- req = webob.Request.blank('/v1.0/servers/1/rescue')
- req.method = 'POST'
- req.content_type = 'application/json'
-
- res = req.get_response(fakes.wsgi_app())
-
- self.assertEqual(self.called, True)
- self.assertEqual(res.status_int, 200)
- res_body = json.loads(res.body)
- self.assertTrue('adminPass' in res_body)
- self.assertEqual(FLAGS.password_length, len(res_body['adminPass']))
-
- def test_rescue_with_preset_password(self):
- self.flags(allow_admin_api=True)
-
- self.called = False
-
- def rescue_mock(*args, **kwargs):
- self.called = True
-
- self.stubs.Set(nova.compute.api.API, 'rescue', rescue_mock)
- req = webob.Request.blank('/v1.0/servers/1/rescue')
- req.method = 'POST'
- body = {"rescue": {"adminPass": "AABBCC112233"}}
- req.body = json.dumps(body)
- req.content_type = 'application/json'
-
- res = req.get_response(fakes.wsgi_app())
-
- self.assertEqual(self.called, True)
- self.assertEqual(res.status_int, 200)
- res_body = json.loads(res.body)
- self.assertTrue('adminPass' in res_body)
- self.assertEqual('AABBCC112233', res_body['adminPass'])
-
- def test_rescue_raises_handled(self):
- self.flags(allow_admin_api=True)
- body = {}
-
- def rescue_mock(*args, **kwargs):
- raise Exception('Who cares?')
-
- self.stubs.Set(nova.compute.api.API, 'rescue', rescue_mock)
- req = webob.Request.blank('/v1.0/servers/1/rescue')
- req.method = 'POST'
- req.content_type = 'application/json'
-
- res = req.get_response(fakes.wsgi_app())
-
- self.assertEqual(res.status_int, 422)
-
def test_delete_server_instance_v1_1(self):
req = webob.Request.blank('/v1.1/fake/servers/1')
req.method = 'DELETE'