From 8aea573bd2e44e152fb4ef1627640bab1818dede Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Tue, 28 Dec 2010 23:55:58 -0600 Subject: initial lock functionality commit --- nova/api/openstack/__init__.py | 73 +++++++++++++++++++++++++++++++++++ nova/api/openstack/servers.py | 86 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 159 insertions(+) (limited to 'nova/api') diff --git a/nova/api/openstack/__init__.py b/nova/api/openstack/__init__.py index bebcdc18c..b3bb65550 100644 --- a/nova/api/openstack/__init__.py +++ b/nova/api/openstack/__init__.py @@ -22,6 +22,7 @@ WSGI middleware for OpenStack API controllers. import json import time +import functools import logging import routes @@ -113,3 +114,75 @@ class APIRouter(wsgi.Router): controller=sharedipgroups.Controller()) super(APIRouter, self).__init__(mapper) + + +#class CheckLock(object): +# """ +# decorator used for preventing action against locked instances +# unless, of course, you happen to be admin +# +# """ +# def __init__(self, function): +# self.function = function +# +# def __getattribute__(self, attr): +# if attr == "function": +# return super(CheckLock, self).__getattribute__(attr) +# return self.function.__getattribute__(attr) +# +# def __call__(self, *args, **kwargs): +# logging.info(_("Calling %s. Checking locks and privileges"), +# self.function.__name__) +# +# # get req +# if 'req' is in kwargs: +# req = kwargs['req'] +# else: +# req = args[1] +# +# # check table for lock +# locked = True +# if(locked): +# # check context for admin +# if(req.environ['nova.context'].is_admin): +# self.function(*args, **kwargs) +# else: +# pass +# # return 404 +# +# def __get__(self, obj, objtype): +# f = functools.partial(self.__call__, obj) +# f.__doc__ = self.function.__doc__ +# return f + + + + +#def checks_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): +# +# # check table for lock +# locked = True +# if(locked): +# try: +# # get context from req and check for admin +# if 'req' is in kwargs: +# req = kwargs['req'] +# else: +# req = args[1] +# if(req.environ['nova.context'].is_admin): +# function(*args, **kwargs) +# else: +# pass +# # return 404 +# except: +# logging.error(_("CheckLock: error getting context")) +# +# return decorated_function diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index 10c397384..46e65ca83 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -35,6 +35,40 @@ LOG = logging.getLogger('server') LOG.setLevel(logging.DEBUG) +def checks_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' is in kwargs: + req = kwargs['req'] + else: + req = args[1] + if 'id' is in kwargs: + _id = kwargs['id'] + else: + req = args[2] + context = req.environ['nova.context'] + except: + logging.error(_("CheckLock: argument error")) + + # if locked and admin call function, otherwise 404 + if(compute_api.ComputeAPI().get_lock(context, _id)): + if(req.environ['nova.context'].is_admin): + function(*args, **kwargs) + # return 404 + return faults.Fault(exc.HTTPUnprocessableEntity()) + + return decorated_function + + def _entity_list(entities): """ Coerces a list of servers into proper dictionary format """ return dict(servers=entities) @@ -104,6 +138,7 @@ class Controller(wsgi.Controller): res = [entity_maker(inst)['server'] for inst in limited_list] return _entity_list(res) + @checks_lock def show(self, req, id): """ Returns server details by server id """ try: @@ -113,6 +148,7 @@ class Controller(wsgi.Controller): except exception.NotFound: return faults.Fault(exc.HTTPNotFound()) + @checks_lock def delete(self, req, id): """ Destroys a server """ try: @@ -140,6 +176,7 @@ class Controller(wsgi.Controller): key_data=key_pair['public_key']) return _entity_inst(instances[0]) + @checks_lock def update(self, req, id): """ Updates the server name or password """ inst_dict = self._deserialize(req.body, req) @@ -160,6 +197,7 @@ class Controller(wsgi.Controller): return faults.Fault(exc.HTTPNotFound()) return exc.HTTPNoContent() + @checks_lock def action(self, req, id): """ Multi-purpose method used to reboot, rebuild, and resize a server """ @@ -176,6 +214,51 @@ class Controller(wsgi.Controller): return faults.Fault(exc.HTTPUnprocessableEntity()) return exc.HTTPAccepted() + 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: + readable = traceback.format_exc() + logging.error(_("Compute.api::lock %s"), readable) + return faults.Fault(exc.HTTPUnprocessableEntity()) + return exc.HTTPAccepted() + + 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: + readable = traceback.format_exc() + logging.error(_("Compute.api::unlock %s"), readable) + return faults.Fault(exc.HTTPUnprocessableEntity()) + return exc.HTTPAccepted() + + def get_lock(self, req, id): + """ + return the boolean state of (instance with id)'s lock + + """ + context = req.environ['nova.context'] + try: + self.compute_api.get_lock(context, id) + except: + readable = traceback.format_exc() + logging.error(_("Compute.api::get_lock %s"), readable) + return faults.Fault(exc.HTTPUnprocessableEntity()) + return exc.HTTPAccepted() + + @checks_lock def pause(self, req, id): """ Permit Admins to Pause the server. """ ctxt = req.environ['nova.context'] @@ -187,6 +270,7 @@ class Controller(wsgi.Controller): return faults.Fault(exc.HTTPUnprocessableEntity()) return exc.HTTPAccepted() + @checks_lock def unpause(self, req, id): """ Permit Admins to Unpause the server. """ ctxt = req.environ['nova.context'] @@ -198,6 +282,7 @@ class Controller(wsgi.Controller): return faults.Fault(exc.HTTPUnprocessableEntity()) return exc.HTTPAccepted() + @checks_lock def suspend(self, req, id): """permit admins to suspend the server""" context = req.environ['nova.context'] @@ -209,6 +294,7 @@ class Controller(wsgi.Controller): return faults.Fault(exc.HTTPUnprocessableEntity()) return exc.HTTPAccepted() + @checks_lock def resume(self, req, id): """permit admins to resume the server from suspend""" context = req.environ['nova.context'] -- cgit From 3a85ba4fa4215737731b2e755abfa350c509e46f Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Wed, 29 Dec 2010 13:04:41 -0600 Subject: syntax error --- nova/api/openstack/servers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'nova/api') diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index 46e65ca83..7744815fc 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -47,11 +47,11 @@ def checks_lock(function): # grab args to function try: - if 'req' is in kwargs: + if 'req' in kwargs: req = kwargs['req'] else: req = args[1] - if 'id' is in kwargs: + if 'id' in kwargs: _id = kwargs['id'] else: req = args[2] -- cgit From b6e5c68d65701b840006cea49367879ee88c9b80 Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Wed, 29 Dec 2010 13:09:49 -0600 Subject: forgot import --- nova/api/openstack/__init__.py | 1 - nova/api/openstack/servers.py | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) (limited to 'nova/api') diff --git a/nova/api/openstack/__init__.py b/nova/api/openstack/__init__.py index b3bb65550..c0bd37fef 100644 --- a/nova/api/openstack/__init__.py +++ b/nova/api/openstack/__init__.py @@ -22,7 +22,6 @@ WSGI middleware for OpenStack API controllers. import json import time -import functools import logging import routes diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index 7744815fc..8b837e6fc 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -17,6 +17,7 @@ import logging import traceback +import functools from webob import exc -- cgit From 0afb4a06dcb94ae41d04b3d78304746b0cc5b26f Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Wed, 29 Dec 2010 13:33:51 -0600 Subject: refactor --- nova/api/openstack/servers.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) (limited to 'nova/api') diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index 8b837e6fc..292a664b7 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -58,14 +58,15 @@ def checks_lock(function): req = args[2] context = req.environ['nova.context'] except: - logging.error(_("CheckLock: argument error")) - - # if locked and admin call function, otherwise 404 - if(compute_api.ComputeAPI().get_lock(context, _id)): - if(req.environ['nova.context'].is_admin): - function(*args, **kwargs) - # return 404 - return faults.Fault(exc.HTTPUnprocessableEntity()) + logging.error(_("CheckLock: 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.HTTPNotFound()) return decorated_function -- cgit From 6202b21b42615cf15b0dd60089026472e6836c69 Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Wed, 29 Dec 2010 15:31:53 -0600 Subject: removed some code i didn't end up using --- nova/api/openstack/__init__.py | 72 ------------------------------------------ 1 file changed, 72 deletions(-) (limited to 'nova/api') diff --git a/nova/api/openstack/__init__.py b/nova/api/openstack/__init__.py index 7cceb7733..66aceee2d 100644 --- a/nova/api/openstack/__init__.py +++ b/nova/api/openstack/__init__.py @@ -113,75 +113,3 @@ class APIRouter(wsgi.Router): controller=sharedipgroups.Controller()) super(APIRouter, self).__init__(mapper) - - -#class CheckLock(object): -# """ -# decorator used for preventing action against locked instances -# unless, of course, you happen to be admin -# -# """ -# def __init__(self, function): -# self.function = function -# -# def __getattribute__(self, attr): -# if attr == "function": -# return super(CheckLock, self).__getattribute__(attr) -# return self.function.__getattribute__(attr) -# -# def __call__(self, *args, **kwargs): -# logging.info(_("Calling %s. Checking locks and privileges"), -# self.function.__name__) -# -# # get req -# if 'req' is in kwargs: -# req = kwargs['req'] -# else: -# req = args[1] -# -# # check table for lock -# locked = True -# if(locked): -# # check context for admin -# if(req.environ['nova.context'].is_admin): -# self.function(*args, **kwargs) -# else: -# pass -# # return 404 -# -# def __get__(self, obj, objtype): -# f = functools.partial(self.__call__, obj) -# f.__doc__ = self.function.__doc__ -# return f - - - - -#def checks_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): -# -# # check table for lock -# locked = True -# if(locked): -# try: -# # get context from req and check for admin -# if 'req' is in kwargs: -# req = kwargs['req'] -# else: -# req = args[1] -# if(req.environ['nova.context'].is_admin): -# function(*args, **kwargs) -# else: -# pass -# # return 404 -# except: -# logging.error(_("CheckLock: error getting context")) -# -# return decorated_function -- cgit From aac25e8cc6e75d5d0abc41a8cf979300e58bcc3b Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Wed, 29 Dec 2010 17:04:40 -0600 Subject: removed () from if (can't believe i did that) and renamed checks_lock decorator --- nova/api/openstack/servers.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) (limited to 'nova/api') diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index c7263273c..74b4f55b5 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -36,7 +36,7 @@ LOG = logging.getLogger('server') LOG.setLevel(logging.DEBUG) -def checks_lock(function): +def checks_instance_lock(function): """ decorator used for preventing action against locked instances unless, of course, you happen to be admin @@ -58,12 +58,12 @@ def checks_lock(function): req = args[2] context = req.environ['nova.context'] except: - logging.error(_("CheckLock: argument error: |%s|, |%s|"), args, + 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): + if admin or not locked: return function(*args, **kwargs) return faults.Fault(exc.HTTPNotFound()) @@ -138,7 +138,7 @@ class Controller(wsgi.Controller): res = [entity_maker(inst)['server'] for inst in limited_list] return dict(servers=res) - @checks_lock + @checks_instance_lock def show(self, req, id): """ Returns server details by server id """ try: @@ -148,7 +148,7 @@ class Controller(wsgi.Controller): except exception.NotFound: return faults.Fault(exc.HTTPNotFound()) - @checks_lock + @checks_instance_lock def delete(self, req, id): """ Destroys a server """ try: @@ -176,7 +176,7 @@ class Controller(wsgi.Controller): key_data=key_pair['public_key']) return _translate_keys(instances[0]) - @checks_lock + @checks_instance_lock def update(self, req, id): """ Updates the server name or password """ inst_dict = self._deserialize(req.body, req) @@ -198,7 +198,7 @@ class Controller(wsgi.Controller): return faults.Fault(exc.HTTPNotFound()) return exc.HTTPNoContent() - @checks_lock + @checks_instance_lock def action(self, req, id): """ Multi-purpose method used to reboot, rebuild, and resize a server """ @@ -259,7 +259,7 @@ class Controller(wsgi.Controller): return faults.Fault(exc.HTTPUnprocessableEntity()) return exc.HTTPAccepted() - @checks_lock + @checks_instance_lock def pause(self, req, id): """ Permit Admins to Pause the server. """ ctxt = req.environ['nova.context'] @@ -271,7 +271,7 @@ class Controller(wsgi.Controller): return faults.Fault(exc.HTTPUnprocessableEntity()) return exc.HTTPAccepted() - @checks_lock + @checks_instance_lock def unpause(self, req, id): """ Permit Admins to Unpause the server. """ ctxt = req.environ['nova.context'] @@ -283,7 +283,7 @@ class Controller(wsgi.Controller): return faults.Fault(exc.HTTPUnprocessableEntity()) return exc.HTTPAccepted() - @checks_lock + @checks_instance_lock def suspend(self, req, id): """permit admins to suspend the server""" context = req.environ['nova.context'] @@ -295,7 +295,7 @@ class Controller(wsgi.Controller): return faults.Fault(exc.HTTPUnprocessableEntity()) return exc.HTTPAccepted() - @checks_lock + @checks_instance_lock def resume(self, req, id): """permit admins to resume the server from suspend""" context = req.environ['nova.context'] -- cgit From be6750a77e5121fe8f0d95016da4e96c9de3b5aa Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Wed, 29 Dec 2010 17:40:18 -0600 Subject: removed lock check from show and changed returning 404 to 405 --- nova/api/openstack/servers.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'nova/api') diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index 74b4f55b5..24fd5000c 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -66,7 +66,7 @@ def checks_instance_lock(function): if admin or not locked: return function(*args, **kwargs) - return faults.Fault(exc.HTTPNotFound()) + return faults.Fault(exc.HTTPMethodNotAllowed()) return decorated_function @@ -138,7 +138,6 @@ class Controller(wsgi.Controller): res = [entity_maker(inst)['server'] for inst in limited_list] return dict(servers=res) - @checks_instance_lock def show(self, req, id): """ Returns server details by server id """ try: -- cgit From 24e253a1feaa0a39e4095f447f62f7ea9b43c8bb Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Wed, 29 Dec 2010 18:30:01 -0600 Subject: moved check lock decorator to compute api level. altered openstack.test_servers according and wrote test for lock in tests.test_compute --- nova/api/openstack/servers.py | 43 ------------------------------------------- 1 file changed, 43 deletions(-) (limited to 'nova/api') 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'] -- cgit