[PATCH V2] Issue #737: Fix to remove twice calls of resource lookup on GET OPERATION

From: Archana Singh <archus@linux.vnet.ibm.com> Incase of GET lookup was called twice. Once lookup() before is_authorized() and then in self.get(). This added overhead to system when lookup() is called for each value in list from get_list() of Collection. So to avoid this overhead, lookup() should not be called before self.get(). 1) Added lookup() call from Resource's update(), delete(). 2) Removed lookup() call from Resource's index(). 2) As is_authorized() calls self.data which calls self.info. Added check to make sure that self.data only get called if self.info is not None. And intialized self.info as None in __init__. As its value is getting assigned in lookup(). 3) In _generate_action_handler_base(), lookup() was getting called before is_authorized(), move its call after is_authorized(). Archana Singh (1): Issue #737: Fix to remove twice calls of resource lookup on GET OPERATION src/wok/control/base.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) -- 2.1.4

From: Archana Singh <archus@linux.vnet.ibm.com> Incase of GET lookup was called twice. Once lookup() before is_authorized() and then in self.get(). This added overhead to system when lookup() is called for each value in list from get_list() of Collection. So to avoid this overhead, lookup() should not be called before self.get(). 1) Added lookup() call from Resource's update(), delete(). 2) Removed lookup() call from Resource's index(). 2) As is_authorized() calls self.data which calls self.info. Added check to make sure that self.data only get called if self.info is not None. And intialized self.info as None in __init__. As its value is getting assigned in lookup(). 3) In _generate_action_handler_base(), lookup() was getting called before is_authorized(), move its call after is_authorized(). --- src/wok/control/base.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/wok/control/base.py b/src/wok/control/base.py index 638e196..4f25ac9 100644 --- a/src/wok/control/base.py +++ b/src/wok/control/base.py @@ -58,6 +58,7 @@ class Resource(object): self.model_args = (ident,) self.role_key = None self.admin_methods = [] + self.info = None def _redirect(self, action_result, code=303): if isinstance(action_result, list): @@ -102,10 +103,9 @@ class Resource(object): def wrapper(*args, **kwargs): validate_method(('POST'), self.role_key, self.admin_methods) try: - self.lookup() if not self.is_authorized(): raise UnauthorizedError('WOKAPI0009E') - + self.lookup() model_args = list(self.model_args) if action_args is not None: request = parse_request() @@ -145,6 +145,7 @@ class Resource(object): def delete(self): try: + self.lookup() fn = getattr(self.model, model_fn(self, 'delete')) fn(*self.model_args) cherrypy.response.status = 204 @@ -163,10 +164,8 @@ class Resource(object): self.role_key, self.admin_methods) try: - self.lookup() if not self.is_authorized(): raise UnauthorizedError('WOKAPI0009E') - return {'GET': self.get, 'DELETE': self.delete, 'PUT': self.update}[method](*args, **kargs) @@ -188,8 +187,12 @@ class Resource(object): user_groups = cherrypy.session.get(USER_GROUPS, []) user_role = cherrypy.session.get(USER_ROLES, {}).get(self.role_key) - users = self.data.get("users", None) - groups = self.data.get("groups", None) + users = None + groups = None + + if self.info: + users = self.data.get("users", None) + groups = self.data.get("groups", None) if (users is None and groups is None) or user_role == 'admin': return True @@ -198,6 +201,7 @@ class Resource(object): def update(self, *args, **kargs): try: + self.lookup() update = getattr(self.model, model_fn(self, 'update')) except AttributeError: e = InvalidOperation('WOKAPI0003E', {'resource': -- 2.1.4

I think the only way to reduce the lookup() calls for a GET request is removing it from Resource.get() as the lookup() was already called in index() and everything keep as it is today. On 03/11/2015 16:54, archus@linux.vnet.ibm.com wrote:
From: Archana Singh <archus@linux.vnet.ibm.com>
Incase of GET lookup was called twice. Once lookup() before is_authorized() and then in self.get(). This added overhead to system when lookup() is called for each value in list from get_list() of Collection. So to avoid this overhead, lookup() should not be called before self.get().
1) Added lookup() call from Resource's update(), delete(). 2) Removed lookup() call from Resource's index(). 2) As is_authorized() calls self.data which calls self.info. Added check to make sure that self.data only get called if self.info is not None. And intialized self.info as None in __init__. As its value is getting assigned in lookup(). 3) In _generate_action_handler_base(), lookup() was getting called before is_authorized(), move its call after is_authorized(). --- src/wok/control/base.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-)
diff --git a/src/wok/control/base.py b/src/wok/control/base.py index 638e196..4f25ac9 100644 --- a/src/wok/control/base.py +++ b/src/wok/control/base.py @@ -58,6 +58,7 @@ class Resource(object): self.model_args = (ident,) self.role_key = None self.admin_methods = [] + self.info = None
def _redirect(self, action_result, code=303): if isinstance(action_result, list): @@ -102,10 +103,9 @@ class Resource(object): def wrapper(*args, **kwargs): validate_method(('POST'), self.role_key, self.admin_methods) try: - self.lookup() if not self.is_authorized(): raise UnauthorizedError('WOKAPI0009E') - + self.lookup() model_args = list(self.model_args) if action_args is not None: request = parse_request() @@ -145,6 +145,7 @@ class Resource(object):
def delete(self): try: + self.lookup() fn = getattr(self.model, model_fn(self, 'delete')) fn(*self.model_args) cherrypy.response.status = 204 @@ -163,10 +164,8 @@ class Resource(object): self.role_key, self.admin_methods)
try: - self.lookup() if not self.is_authorized(): raise UnauthorizedError('WOKAPI0009E') - return {'GET': self.get, 'DELETE': self.delete, 'PUT': self.update}[method](*args, **kargs) @@ -188,8 +187,12 @@ class Resource(object): user_groups = cherrypy.session.get(USER_GROUPS, []) user_role = cherrypy.session.get(USER_ROLES, {}).get(self.role_key)
- users = self.data.get("users", None) - groups = self.data.get("groups", None) + users = None + groups = None + + if self.info: + users = self.data.get("users", None) + groups = self.data.get("groups", None)
if (users is None and groups is None) or user_role == 'admin': return True @@ -198,6 +201,7 @@ class Resource(object):
def update(self, *args, **kargs): try: + self.lookup() update = getattr(self.model, model_fn(self, 'update')) except AttributeError: e = InvalidOperation('WOKAPI0003E', {'resource':

I have tried that option but it does not help as Resource.get() gets called from update place which needs lookup to be run again to get the updated value. On 11/5/2015 5:54 PM, Aline Manera wrote:
I think the only way to reduce the lookup() calls for a GET request is removing it from Resource.get() as the lookup() was already called in index() and everything keep as it is today.
On 03/11/2015 16:54, archus@linux.vnet.ibm.com wrote:
From: Archana Singh <archus@linux.vnet.ibm.com>
Incase of GET lookup was called twice. Once lookup() before is_authorized() and then in self.get(). This added overhead to system when lookup() is called for each value in list from get_list() of Collection. So to avoid this overhead, lookup() should not be called before self.get().
1) Added lookup() call from Resource's update(), delete(). 2) Removed lookup() call from Resource's index(). 2) As is_authorized() calls self.data which calls self.info. Added check to make sure that self.data only get called if self.info is not None. And intialized self.info as None in __init__. As its value is getting assigned in lookup(). 3) In _generate_action_handler_base(), lookup() was getting called before is_authorized(), move its call after is_authorized(). --- src/wok/control/base.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-)
diff --git a/src/wok/control/base.py b/src/wok/control/base.py index 638e196..4f25ac9 100644 --- a/src/wok/control/base.py +++ b/src/wok/control/base.py @@ -58,6 +58,7 @@ class Resource(object): self.model_args = (ident,) self.role_key = None self.admin_methods = [] + self.info = None
def _redirect(self, action_result, code=303): if isinstance(action_result, list): @@ -102,10 +103,9 @@ class Resource(object): def wrapper(*args, **kwargs): validate_method(('POST'), self.role_key, self.admin_methods) try: - self.lookup() if not self.is_authorized(): raise UnauthorizedError('WOKAPI0009E') - + self.lookup() model_args = list(self.model_args) if action_args is not None: request = parse_request() @@ -145,6 +145,7 @@ class Resource(object):
def delete(self): try: + self.lookup() fn = getattr(self.model, model_fn(self, 'delete')) fn(*self.model_args) cherrypy.response.status = 204 @@ -163,10 +164,8 @@ class Resource(object): self.role_key, self.admin_methods)
try: - self.lookup() if not self.is_authorized(): raise UnauthorizedError('WOKAPI0009E') - return {'GET': self.get, 'DELETE': self.delete, 'PUT': self.update}[method](*args, **kargs) @@ -188,8 +187,12 @@ class Resource(object): user_groups = cherrypy.session.get(USER_GROUPS, []) user_role = cherrypy.session.get(USER_ROLES, {}).get(self.role_key)
- users = self.data.get("users", None) - groups = self.data.get("groups", None) + users = None + groups = None + + if self.info: + users = self.data.get("users", None) + groups = self.data.get("groups", None)
if (users is None and groups is None) or user_role == 'admin': return True @@ -198,6 +201,7 @@ class Resource(object):
def update(self, *args, **kargs): try: + self.lookup() update = getattr(self.model, model_fn(self, 'update')) except AttributeError: e = InvalidOperation('WOKAPI0003E', {'resource':

I think as you mention remove lookup() from get() and in update() add call to lookup() and then get(). On 11/5/2015 5:56 PM, Archana Singh wrote:
I have tried that option but it does not help as Resource.get() gets called from update place which needs lookup to be run again to get the updated value.
On 11/5/2015 5:54 PM, Aline Manera wrote:
I think the only way to reduce the lookup() calls for a GET request is removing it from Resource.get() as the lookup() was already called in index() and everything keep as it is today.
On 03/11/2015 16:54, archus@linux.vnet.ibm.com wrote:
From: Archana Singh <archus@linux.vnet.ibm.com>
Incase of GET lookup was called twice. Once lookup() before is_authorized() and then in self.get(). This added overhead to system when lookup() is called for each value in list from get_list() of Collection. So to avoid this overhead, lookup() should not be called before self.get().
1) Added lookup() call from Resource's update(), delete(). 2) Removed lookup() call from Resource's index(). 2) As is_authorized() calls self.data which calls self.info. Added check to make sure that self.data only get called if self.info is not None. And intialized self.info as None in __init__. As its value is getting assigned in lookup(). 3) In _generate_action_handler_base(), lookup() was getting called before is_authorized(), move its call after is_authorized(). --- src/wok/control/base.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-)
diff --git a/src/wok/control/base.py b/src/wok/control/base.py index 638e196..4f25ac9 100644 --- a/src/wok/control/base.py +++ b/src/wok/control/base.py @@ -58,6 +58,7 @@ class Resource(object): self.model_args = (ident,) self.role_key = None self.admin_methods = [] + self.info = None
def _redirect(self, action_result, code=303): if isinstance(action_result, list): @@ -102,10 +103,9 @@ class Resource(object): def wrapper(*args, **kwargs): validate_method(('POST'), self.role_key, self.admin_methods) try: - self.lookup() if not self.is_authorized(): raise UnauthorizedError('WOKAPI0009E') - + self.lookup() model_args = list(self.model_args) if action_args is not None: request = parse_request() @@ -145,6 +145,7 @@ class Resource(object):
def delete(self): try: + self.lookup() fn = getattr(self.model, model_fn(self, 'delete')) fn(*self.model_args) cherrypy.response.status = 204 @@ -163,10 +164,8 @@ class Resource(object): self.role_key, self.admin_methods)
try: - self.lookup() if not self.is_authorized(): raise UnauthorizedError('WOKAPI0009E') - return {'GET': self.get, 'DELETE': self.delete, 'PUT': self.update}[method](*args, **kargs) @@ -188,8 +187,12 @@ class Resource(object): user_groups = cherrypy.session.get(USER_GROUPS, []) user_role = cherrypy.session.get(USER_ROLES, {}).get(self.role_key)
- users = self.data.get("users", None) - groups = self.data.get("groups", None) + users = None + groups = None + + if self.info: + users = self.data.get("users", None) + groups = self.data.get("groups", None)
if (users is None and groups is None) or user_role == 'admin': return True @@ -198,6 +201,7 @@ class Resource(object):
def update(self, *args, **kargs): try: + self.lookup() update = getattr(self.model, model_fn(self, 'update')) except AttributeError: e = InvalidOperation('WOKAPI0003E', {'resource':
_______________________________________________ Kimchi-devel mailing list Kimchi-devel@ovirt.org http://lists.ovirt.org/mailman/listinfo/kimchi-devel

On 05/11/2015 10:41, Archana Singh wrote:
I think as you mention remove lookup() from get() and in update() add call to lookup() and then get().
Yeap! I think that can work!
On 11/5/2015 5:56 PM, Archana Singh wrote:
I have tried that option but it does not help as Resource.get() gets called from update place which needs lookup to be run again to get the updated value.
On 11/5/2015 5:54 PM, Aline Manera wrote:
I think the only way to reduce the lookup() calls for a GET request is removing it from Resource.get() as the lookup() was already called in index() and everything keep as it is today.
On 03/11/2015 16:54, archus@linux.vnet.ibm.com wrote:
From: Archana Singh <archus@linux.vnet.ibm.com>
Incase of GET lookup was called twice. Once lookup() before is_authorized() and then in self.get(). This added overhead to system when lookup() is called for each value in list from get_list() of Collection. So to avoid this overhead, lookup() should not be called before self.get().
1) Added lookup() call from Resource's update(), delete(). 2) Removed lookup() call from Resource's index(). 2) As is_authorized() calls self.data which calls self.info. Added check to make sure that self.data only get called if self.info is not None. And intialized self.info as None in __init__. As its value is getting assigned in lookup(). 3) In _generate_action_handler_base(), lookup() was getting called before is_authorized(), move its call after is_authorized(). --- src/wok/control/base.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-)
diff --git a/src/wok/control/base.py b/src/wok/control/base.py index 638e196..4f25ac9 100644 --- a/src/wok/control/base.py +++ b/src/wok/control/base.py @@ -58,6 +58,7 @@ class Resource(object): self.model_args = (ident,) self.role_key = None self.admin_methods = [] + self.info = None
def _redirect(self, action_result, code=303): if isinstance(action_result, list): @@ -102,10 +103,9 @@ class Resource(object): def wrapper(*args, **kwargs): validate_method(('POST'), self.role_key, self.admin_methods) try: - self.lookup() if not self.is_authorized(): raise UnauthorizedError('WOKAPI0009E') - + self.lookup() model_args = list(self.model_args) if action_args is not None: request = parse_request() @@ -145,6 +145,7 @@ class Resource(object):
def delete(self): try: + self.lookup() fn = getattr(self.model, model_fn(self, 'delete')) fn(*self.model_args) cherrypy.response.status = 204 @@ -163,10 +164,8 @@ class Resource(object): self.role_key, self.admin_methods)
try: - self.lookup() if not self.is_authorized(): raise UnauthorizedError('WOKAPI0009E') - return {'GET': self.get, 'DELETE': self.delete, 'PUT': self.update}[method](*args, **kargs) @@ -188,8 +187,12 @@ class Resource(object): user_groups = cherrypy.session.get(USER_GROUPS, []) user_role = cherrypy.session.get(USER_ROLES, {}).get(self.role_key)
- users = self.data.get("users", None) - groups = self.data.get("groups", None) + users = None + groups = None + + if self.info: + users = self.data.get("users", None) + groups = self.data.get("groups", None)
if (users is None and groups is None) or user_role == 'admin': return True @@ -198,6 +201,7 @@ class Resource(object):
def update(self, *args, **kargs): try: + self.lookup() update = getattr(self.model, model_fn(self, 'update')) except AttributeError: e = InvalidOperation('WOKAPI0003E', {'resource':
_______________________________________________ Kimchi-devel mailing list Kimchi-devel@ovirt.org http://lists.ovirt.org/mailman/listinfo/kimchi-devel

Hi Archana, After some tests, I verified this patch set broke the guest authorization feature. The guest authorization feature allows user to specify which users and groups can access a specific resource. For example, in my system: GET /plugins/kimchi/vms/fedora22 { *"users":[** ** "guest"** ** ],* "screenshot":null, "cpus":2, "persistent":true, *"groups":[],* "graphics":{ "passwd":null, "passwdValidTo":null, "type":"vnc", "port":null, "listen":"127.0.0.1" }, "icon":null, "stats":{ "cpu_utilization":0, "io_throughput":0, "io_throughput_peak":100, "net_throughput":0, "mem_utilization":0, "net_throughput_peak":100 }, "name":"fedora22", "uuid":"7a07310c-6fd9-47e4-b8e1-ac5a7bd82c79", "access":"full", "state":"shutoff", "memory":4096.0 } That means, in addition to sysadmin the user 'guest' can also have access to the virtual machine "fedora22" After applying this patch, any user can has access to any virtual machine, ie, the guest authorization configuration is being ignored. See below: GET /plugins/kimchi/vms/ubuntu15.04 { * "users":[],* "screenshot":null, "cpus":1, "persistent":true, * "groups":[],* "graphics":{ "passwd":null, "passwdValidTo":null, "type":"vnc", "port":null, "listen":"127.0.0.1" }, "icon":null, "stats":{ "cpu_utilization":0, "io_throughput":0, "io_throughput_peak":100, "net_throughput":0, "mem_utilization":0, "net_throughput_peak":100 }, "name":"ubuntu15.04", "uuid":"aafc4eb0-9f8e-4f8e-b001-390b180c3675", "access":"full", "state":"shutoff", "memory":4096.0 } If I log into Kimchi as 'guest' user, I can also perform operation on 'ubuntu15.04' virtual machine but as you can see, only the sysadmin should be able to do that. On 03/11/2015 16:54, archus@linux.vnet.ibm.com wrote:
From: Archana Singh <archus@linux.vnet.ibm.com>
Incase of GET lookup was called twice. Once lookup() before is_authorized() and then in self.get(). This added overhead to system when lookup() is called for each value in list from get_list() of Collection. So to avoid this overhead, lookup() should not be called before self.get().
1) Added lookup() call from Resource's update(), delete(). 2) Removed lookup() call from Resource's index(). 2) As is_authorized() calls self.data which calls self.info. Added check to make sure that self.data only get called if self.info is not None. And intialized self.info as None in __init__. As its value is getting assigned in lookup(). 3) In _generate_action_handler_base(), lookup() was getting called before is_authorized(), move its call after is_authorized().
Archana Singh (1): Issue #737: Fix to remove twice calls of resource lookup on GET OPERATION
src/wok/control/base.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-)

On 05/11/2015 10:14, Aline Manera wrote:
Hi Archana,
After some tests, I verified this patch set broke the guest authorization feature.
The guest authorization feature allows user to specify which users and groups can access a specific resource.
For example, in my system:
GET /plugins/kimchi/vms/fedora22 { *"users":[** ** "guest"** ** ],* "screenshot":null, "cpus":2, "persistent":true, *"groups":[],* "graphics":{ "passwd":null, "passwdValidTo":null, "type":"vnc", "port":null, "listen":"127.0.0.1" }, "icon":null, "stats":{ "cpu_utilization":0, "io_throughput":0, "io_throughput_peak":100, "net_throughput":0, "mem_utilization":0, "net_throughput_peak":100 }, "name":"fedora22", "uuid":"7a07310c-6fd9-47e4-b8e1-ac5a7bd82c79", "access":"full", "state":"shutoff", "memory":4096.0 }
That means, in addition to sysadmin the user 'guest' can also have access to the virtual machine "fedora22"
After applying this patch, any user can has access to any virtual machine, ie, the guest authorization configuration is being ignored. See below:
GET /plugins/kimchi/vms/ubuntu15.04 { * "users":[],* "screenshot":null, "cpus":1, "persistent":true, * "groups":[],* "graphics":{ "passwd":null, "passwdValidTo":null, "type":"vnc", "port":null, "listen":"127.0.0.1" }, "icon":null, "stats":{ "cpu_utilization":0, "io_throughput":0, "io_throughput_peak":100, "net_throughput":0, "mem_utilization":0, "net_throughput_peak":100 }, "name":"ubuntu15.04", "uuid":"aafc4eb0-9f8e-4f8e-b001-390b180c3675", "access":"full", "state":"shutoff", "memory":4096.0 }
If I log into Kimchi as 'guest' user, I can also perform operation on 'ubuntu15.04' virtual machine but as you can see, only the sysadmin should be able to do that.
Just one more information. Without your patch, while trying to access the 'ubuntu15.04' virtual machine as 'guest' user I get the following: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"></meta> <title>403 Forbidden</title> <style type="text/css"> #powered_by { margin-top: 20px; border-top: 2px solid black; font-style: italic; } #traceback { color: red; } </style> </head> <body> <h2>403 Forbidden</h2> <p>WOKAPI0009E: WOKAPI0009E</p> <pre id="traceback">Traceback (most recent call last): File "/usr/lib/python2.7/dist-packages/cherrypy/_cprequest.py", line 670, in respond response.body = self.handler() File "/usr/lib/python2.7/dist-packages/cherrypy/lib/encoding.py", line 217, in __call__ self.body = self.oldhandler(*args, **kwargs) File "/usr/lib/python2.7/dist-packages/cherrypy/_cpdispatch.py", line 61, in __call__ return self.callable(*self.args, **self.kwargs) File "/home/alinefm/kimchi/src/wok/control/base.py", line 178, in index raise cherrypy.HTTPError(403, e.message) HTTPError: (403, u'WOKAPI0009E: WOKAPI0009E') </pre> <div id="powered_by"> <span> Powered by <a href="http://www.cherrypy.org">CherryPy 3.5.0</a> </span> </div> </body> </html>
On 03/11/2015 16:54, archus@linux.vnet.ibm.com wrote:
From: Archana Singh<archus@linux.vnet.ibm.com>
Incase of GET lookup was called twice. Once lookup() before is_authorized() and then in self.get(). This added overhead to system when lookup() is called for each value in list from get_list() of Collection. So to avoid this overhead, lookup() should not be called before self.get().
1) Added lookup() call from Resource's update(), delete(). 2) Removed lookup() call from Resource's index(). 2) As is_authorized() calls self.data which calls self.info. Added check to make sure that self.data only get called if self.info is not None. And intialized self.info as None in __init__. As its value is getting assigned in lookup(). 3) In _generate_action_handler_base(), lookup() was getting called before is_authorized(), move its call after is_authorized().
Archana Singh (1): Issue #737: Fix to remove twice calls of resource lookup on GET OPERATION
src/wok/control/base.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-)
_______________________________________________ Kimchi-devel mailing list Kimchi-devel@ovirt.org http://lists.ovirt.org/mailman/listinfo/kimchi-devel
participants (3)
-
Aline Manera
-
Archana Singh
-
archus@linux.vnet.ibm.com