diff options
| author | Brian Waldon <brian.waldon@rackspace.com> | 2011-09-20 01:59:05 -0400 |
|---|---|---|
| committer | Brian Waldon <brian.waldon@rackspace.com> | 2011-09-29 17:04:09 -0400 |
| commit | 5f7356be106cbe1116708d2e5d30d5a2f4c479a4 (patch) | |
| tree | ea593522ee9971c12d3ceb3450adbb13e65c0371 | |
| parent | ef22c0054ccb846dd7e81ba35f7e9c2b533d5ff7 (diff) | |
Moving admin actions to extension
Begins to address LP bug 821145
Change-Id: I2799a8d70a167dda6d56f8fab2fc121fa2365a8a
| -rw-r--r-- | nova/api/openstack/__init__.py | 9 | ||||
| -rw-r--r-- | nova/api/openstack/contrib/admin_actions.py | 204 | ||||
| -rw-r--r-- | nova/api/openstack/contrib/admin_only.py | 30 | ||||
| -rw-r--r-- | nova/api/openstack/contrib/hosts.py | 5 | ||||
| -rw-r--r-- | nova/api/openstack/contrib/rescue.py | 14 | ||||
| -rw-r--r-- | nova/api/openstack/extensions.py | 26 | ||||
| -rw-r--r-- | nova/api/openstack/servers.py | 190 | ||||
| -rw-r--r-- | nova/exception.py | 15 | ||||
| -rw-r--r-- | nova/tests/api/openstack/contrib/test_admin_actions.py | 64 | ||||
| -rw-r--r-- | nova/tests/api/openstack/test_extensions.py | 1 | ||||
| -rw-r--r-- | nova/tests/api/openstack/test_server_actions.py | 17 | ||||
| -rw-r--r-- | nova/tests/api/openstack/test_servers.py | 113 |
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' |
