summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--nova/api/openstack/servers.py43
-rw-r--r--nova/compute/api.py44
-rw-r--r--nova/tests/api/openstack/test_servers.py35
-rw-r--r--nova/tests/test_compute.py17
4 files changed, 71 insertions, 68 deletions
diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py
index 24fd5000c..497a04ae3 100644
--- a/nova/api/openstack/servers.py
+++ b/nova/api/openstack/servers.py
@@ -17,7 +17,6 @@
import logging
import traceback
-import functools
from webob import exc
@@ -36,41 +35,6 @@ LOG = logging.getLogger('server')
LOG.setLevel(logging.DEBUG)
-def checks_instance_lock(function):
- """
- decorator used for preventing action against locked instances
- unless, of course, you happen to be admin
-
- """
-
- @functools.wraps(function)
- def decorated_function(*args, **kwargs):
-
- # grab args to function
- try:
- if 'req' in kwargs:
- req = kwargs['req']
- else:
- req = args[1]
- if 'id' in kwargs:
- _id = kwargs['id']
- else:
- req = args[2]
- context = req.environ['nova.context']
- except:
- logging.error(_("check_lock: argument error: |%s|, |%s|"), args,
- kwargs)
- # if admin or unlocked call function, otherwise 404
- locked = compute_api.ComputeAPI().get_lock(context, _id)
- admin = req.environ['nova.context'].is_admin
- if admin or not locked:
- return function(*args, **kwargs)
-
- return faults.Fault(exc.HTTPMethodNotAllowed())
-
- return decorated_function
-
-
def _translate_detail_keys(inst):
""" Coerces into dictionary format, mapping everything to Rackspace-like
attributes for return"""
@@ -147,7 +111,6 @@ class Controller(wsgi.Controller):
except exception.NotFound:
return faults.Fault(exc.HTTPNotFound())
- @checks_instance_lock
def delete(self, req, id):
""" Destroys a server """
try:
@@ -175,7 +138,6 @@ class Controller(wsgi.Controller):
key_data=key_pair['public_key'])
return _translate_keys(instances[0])
- @checks_instance_lock
def update(self, req, id):
""" Updates the server name or password """
inst_dict = self._deserialize(req.body, req)
@@ -197,7 +159,6 @@ class Controller(wsgi.Controller):
return faults.Fault(exc.HTTPNotFound())
return exc.HTTPNoContent()
- @checks_instance_lock
def action(self, req, id):
""" Multi-purpose method used to reboot, rebuild, and
resize a server """
@@ -258,7 +219,6 @@ class Controller(wsgi.Controller):
return faults.Fault(exc.HTTPUnprocessableEntity())
return exc.HTTPAccepted()
- @checks_instance_lock
def pause(self, req, id):
""" Permit Admins to Pause the server. """
ctxt = req.environ['nova.context']
@@ -270,7 +230,6 @@ class Controller(wsgi.Controller):
return faults.Fault(exc.HTTPUnprocessableEntity())
return exc.HTTPAccepted()
- @checks_instance_lock
def unpause(self, req, id):
""" Permit Admins to Unpause the server. """
ctxt = req.environ['nova.context']
@@ -282,7 +241,6 @@ class Controller(wsgi.Controller):
return faults.Fault(exc.HTTPUnprocessableEntity())
return exc.HTTPAccepted()
- @checks_instance_lock
def suspend(self, req, id):
"""permit admins to suspend the server"""
context = req.environ['nova.context']
@@ -294,7 +252,6 @@ class Controller(wsgi.Controller):
return faults.Fault(exc.HTTPUnprocessableEntity())
return exc.HTTPAccepted()
- @checks_instance_lock
def resume(self, req, id):
"""permit admins to resume the server from suspend"""
context = req.environ['nova.context']
diff --git a/nova/compute/api.py b/nova/compute/api.py
index 361ab9914..d720a8f2c 100644
--- a/nova/compute/api.py
+++ b/nova/compute/api.py
@@ -23,6 +23,7 @@ Handles all API requests relating to instances (guest vms).
import datetime
import logging
import time
+import functools
from nova import db
from nova import exception
@@ -36,6 +37,40 @@ from nova.db import base
FLAGS = flags.FLAGS
+def checks_instance_lock(function):
+ """
+ decorator used for preventing action against locked instances
+ unless, of course, you happen to be admin
+
+ """
+
+ @functools.wraps(function)
+ def decorated_function(*args, **kwargs):
+
+ # grab args to function
+ try:
+ if 'context' in kwargs:
+ context = kwargs['context']
+ else:
+ context = args[1]
+ if 'instance_id' in kwargs:
+ instance_id = kwargs['instance_id']
+ else:
+ instance_id = args[2]
+ locked = ComputeAPI().get_lock(context, instance_id)
+ admin = context.is_admin
+ except:
+ logging.error(_("check_instance_lock: argument error: |%s|, |%s|"),
+ args,
+ kwargs)
+ # if admin or unlocked call function, otherwise 405
+ if admin or not locked:
+ return function(*args, **kwargs)
+ raise Exception(_("Instance is locked, cannot execute |%s|"), function)
+
+ return decorated_function
+
+
def generate_default_hostname(internal_id):
"""Default function to generate a hostname given an instance reference."""
return str(internal_id)
@@ -198,6 +233,7 @@ class ComputeAPI(base.Base):
'project_id': context.project_id}
db.security_group_create(context, values)
+ @checks_instance_lock
def update_instance(self, context, instance_id, **kwargs):
"""Updates the instance in the datastore.
@@ -212,6 +248,7 @@ class ComputeAPI(base.Base):
"""
return self.db.instance_update(context, instance_id, kwargs)
+ @checks_instance_lock
def delete_instance(self, context, instance_id):
logging.debug("Going to try and terminate %d" % instance_id)
try:
@@ -258,6 +295,7 @@ class ComputeAPI(base.Base):
def get_instance(self, context, instance_id):
return self.db.instance_get_by_internal_id(context, instance_id)
+ @checks_instance_lock
def reboot(self, context, instance_id):
"""Reboot the given instance."""
instance = self.db.instance_get_by_internal_id(context, instance_id)
@@ -267,6 +305,7 @@ class ComputeAPI(base.Base):
{"method": "reboot_instance",
"args": {"instance_id": instance['id']}})
+ @checks_instance_lock
def pause(self, context, instance_id):
"""Pause the given instance."""
instance = self.db.instance_get_by_internal_id(context, instance_id)
@@ -276,6 +315,7 @@ class ComputeAPI(base.Base):
{"method": "pause_instance",
"args": {"instance_id": instance['id']}})
+ @checks_instance_lock
def unpause(self, context, instance_id):
"""Unpause the given instance."""
instance = self.db.instance_get_by_internal_id(context, instance_id)
@@ -285,6 +325,7 @@ class ComputeAPI(base.Base):
{"method": "unpause_instance",
"args": {"instance_id": instance['id']}})
+ @checks_instance_lock
def suspend(self, context, instance_id):
"""suspend the instance with instance_id"""
instance = self.db.instance_get_by_internal_id(context, instance_id)
@@ -294,6 +335,7 @@ class ComputeAPI(base.Base):
{"method": "suspend_instance",
"args": {"instance_id": instance['id']}})
+ @checks_instance_lock
def resume(self, context, instance_id):
"""resume the instance with instance_id"""
instance = self.db.instance_get_by_internal_id(context, instance_id)
@@ -303,6 +345,7 @@ class ComputeAPI(base.Base):
{"method": "resume_instance",
"args": {"instance_id": instance['id']}})
+ @checks_instance_lock
def rescue(self, context, instance_id):
"""Rescue the given instance."""
instance = self.db.instance_get_by_internal_id(context, instance_id)
@@ -312,6 +355,7 @@ class ComputeAPI(base.Base):
{"method": "rescue_instance",
"args": {"instance_id": instance['id']}})
+ @checks_instance_lock
def unrescue(self, context, instance_id):
"""Unrescue the given instance."""
instance = self.db.instance_get_by_internal_id(context, instance_id)
diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py
index 56a5a9b27..05419fb70 100644
--- a/nova/tests/api/openstack/test_servers.py
+++ b/nova/tests/api/openstack/test_servers.py
@@ -321,14 +321,6 @@ class ServersTest(unittest.TestCase):
self.assertEqual(self.server_delete_called, True)
def test_lock(self):
- # part one: stubs it to be locked and test pause
- def get_locked(self, context, id):
- return True
-
- # set get to return locked
- self.stubs.Set(nova.compute.api.ComputeAPI, 'get_lock', get_locked)
-
- # attempt to pause
FLAGS.allow_admin_api = True
body = dict(server=dict(
name='server_test', imageId=2, flavorId=2, metadata={},
@@ -337,31 +329,24 @@ class ServersTest(unittest.TestCase):
req.method = 'POST'
req.content_type = 'application/json'
req.body = json.dumps(body)
- res = req.get_response(nova.api.API('os'))
- # expect a 404 since it was locked
- self.assertEqual(res.status_int, 405)
+ # part one: stubs it to be locked and attempt pause expecting exception
+ def get_locked(self, context, id):
+ return True
+ self.stubs.Set(nova.compute.api.ComputeAPI, 'get_lock', get_locked)
- # Part two: stubs it to be unlocked and test pause
+ # pause should raise exception on locked instance
+ self.assertRaises(Exception, req.get_response, nova.api.API('os'))
+
+ # Part two: stubs it to be unlocked and attempt pause expecting success
def get_unlocked(self, context, id):
return False
-
- # set get to return locked
self.stubs.Set(nova.compute.api.ComputeAPI, 'get_lock', get_unlocked)
- # attempt to pause
- FLAGS.allow_admin_api = True
- body = dict(server=dict(
- name='server_test', imageId=2, flavorId=2, metadata={},
- personality={}))
- req = webob.Request.blank('/v1.0/servers/1/pause')
- req.method = 'POST'
- req.content_type = 'application/json'
- req.body = json.dumps(body)
res = req.get_response(nova.api.API('os'))
- # expect a 202 since it was unlocked
- self.assertEqual(res.status_int, 202)
+ # expecting no exception, test will fail if exception is raised
+ res = req.get_response(nova.api.API('os'))
if __name__ == "__main__":
diff --git a/nova/tests/test_compute.py b/nova/tests/test_compute.py
index bcb8a1526..422d59da0 100644
--- a/nova/tests/test_compute.py
+++ b/nova/tests/test_compute.py
@@ -170,3 +170,20 @@ class ComputeTestCase(test.TestCase):
self.context,
instance_id)
self.compute.terminate_instance(self.context, instance_id)
+
+ def test_lock(self):
+ """ensure locked instance cannot be changed"""
+ instance_id = self._create_instance()
+ self.compute.run_instance(self.context, instance_id)
+ self.compute.pause_instance(self.context, instance_id)
+ self.compute.lock_instance(self.context, instance_id)
+
+ # pause should raise exception on locked instance
+ self.assertRaises(Exception, self.compute.unpause_instance,
+ self.context, instance_id)
+
+ # test will fail if exception is raised
+ self.compute.unlock_instance(self.context, instance_id)
+ self.compute.unpause_instance(self.context, instance_id)
+
+ self.compute.terminate_instance(self.context, instance_id)