[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Xen-changelog] [xen-unstable] Move the decoration of all the Xen-API methods out of XendAPI.__init__ and



# HG changeset patch
# User Ewan Mellor <ewan@xxxxxxxxxxxxx>
# Date 1166787485 0
# Node ID 0d0e13ff1adfd55862f6d79438549f5f0d6b2c03
# Parent  f7ac2c963f772e905f56c3c8ad0af57893628774
Move the decoration of all the Xen-API methods out of XendAPI.__init__ and
into the module scope.  This avoids the decorators being added multiple times,
once for each server running.

Fix handling of MESSAGE_PARAMETER_COUNT_MISMATCH in many cases.  Move the
get_by_uuid declarations, so that those functions get the per-class validator
as well, to check for existence of the object requested.

Signed-off-by: Ewan Mellor <ewan@xxxxxxxxxxxxx>
---
 tools/python/xen/xend/XendAPI.py |  363 ++++++++++++++++++---------------------
 1 files changed, 168 insertions(+), 195 deletions(-)

diff -r f7ac2c963f77 -r 0d0e13ff1adf tools/python/xen/xend/XendAPI.py
--- a/tools/python/xen/xend/XendAPI.py  Fri Dec 22 11:34:13 2006 +0000
+++ b/tools/python/xen/xend/XendAPI.py  Fri Dec 22 11:38:05 2006 +0000
@@ -15,7 +15,12 @@
 # Copyright (C) 2006 XenSource Ltd.
 #============================================================================
 
+import inspect
+import os
 import re
+import string
+import sys
+import traceback
 
 from xen.xend import XendDomain, XendDomainInfo, XendNode
 from xen.xend import XendLogging
@@ -52,7 +57,7 @@ def xen_api_error(error):
     if len(error) == 0:
         error = ['INTERNAL_ERROR', 'Empty list given to xen_api_error']
 
-    return { "Status": "Error",
+    return { "Status": "Failure",
              "ErrorDescription": [str(x) for x in error] }
 
 
@@ -79,17 +84,17 @@ def trace(func, api_name = ''):
     return trace_func
 
 
-takesRE = re.compile(r'^(.*)\(\) takes exactly ([0-9]*) argument')
+takesRE = re.compile(r' ([0-9]*) argument')
 def deconstruct_typeerror(exn):
     m = takesRE.search(exn[0])
-    return m and m.groups() or None
+    return m and m.group(1) or None
 
 
 def catch_typeerror(func):
     """Decorator to catch any TypeErrors and translate them into Xen-API
     errors.
 
-    @param func: function with params: (self, session, host_ref)
+    @param func: function with params: (self, ...)
     @rtype: callable object
     """
     def f(self, *args, **kwargs):
@@ -97,44 +102,48 @@ def catch_typeerror(func):
             return func(self, *args, **kwargs)
         except TypeError, exn:
             if hasattr(func, 'api'):
-                mt = deconstruct_typeerror(exn)
-                if mt:
-                    method, takes = mt
-                    if method.endswith(func.api.split('.')[-1]):
-                        return xen_api_error(
-                            ['MESSAGE_PARAMETER_COUNT_MISMATCH',
-                             func.api, int(takes) - 2,
-                             len(args) + len(kwargs) - 1])
+                takes = deconstruct_typeerror(exn)
+                if takes is not None:
+                    # Assume that if the exception was thrown inside this
+                    # file, then it is due to an invalid call from the client,
+                    # but if it was thrown elsewhere, then it's an internal
+                    # error (which will be handled further up).
+                    tb = sys.exc_info()[2]
+                    try:
+                        sourcefile = traceback.extract_tb(tb)[-1][0]
+                        if sourcefile == inspect.getsourcefile(XendAPI):
+                            return xen_api_error(
+                                ['MESSAGE_PARAMETER_COUNT_MISMATCH',
+                                 func.api, int(takes) - 2,
+                                 len(args) + len(kwargs) - 1])
+                    finally:
+                        del tb
             raise
 
-    # make sure we keep the 'api' attribute
-    if hasattr(func, 'api'):
-        f.api = func.api
-        
     return f
 
 
 def session_required(func):
+    """Decorator to verify if session is valid before calling method.
+
+    @param func: function with params: (self, session, ...)
+    @rtype: callable object
+    """    
     def check_session(self, session, *args, **kwargs):
         if auth_manager().is_session_valid(session):
             return func(self, session, *args, **kwargs)
         else:
             return xen_api_error(['SESSION_INVALID', session])
 
-    # make sure we keep the 'api' attribute
-    if hasattr(func, 'api'):
-        check_session.api = func.api
-
     return check_session
 
 
 def valid_host(func):
-    """Decorator to verify if host_ref is valid before calling
-    method.
-
-    @param func: function with params: (self, session, host_ref)
+    """Decorator to verify if host_ref is valid before calling method.
+
+    @param func: function with params: (self, session, host_ref, ...)
     @rtype: callable object
-    """    
+    """
     def check_host_ref(self, session, host_ref, *args, **kwargs):
         xennode = XendNode.instance()
         if type(host_ref) == type(str()) and xennode.is_valid_host(host_ref):
@@ -142,17 +151,12 @@ def valid_host(func):
         else:
             return xen_api_error(['HOST_HANDLE_INVALID', host_ref])
 
-    # make sure we keep the 'api' attribute
-    if hasattr(func, 'api'):
-        check_host_ref.api = func.api
-        
     return check_host_ref
 
 def valid_host_cpu(func):
-    """Decorator to verify if host_cpu_ref is valid before calling
-    method.
-
-    @param func: function with params: (self, session, host_cpu_ref)
+    """Decorator to verify if host_cpu_ref is valid before calling method.
+
+    @param func: function with params: (self, session, host_cpu_ref, ...)
     @rtype: callable object
     """    
     def check_host_cpu_ref(self, session, host_cpu_ref, *args, **kwargs):
@@ -163,45 +167,28 @@ def valid_host_cpu(func):
         else:
             return xen_api_error(['HOST_CPU_HANDLE_INVALID', host_cpu_ref])
         
-    # make sure we keep the 'api' attribute
-    if hasattr(func, 'api'):
-        check_host_cpu_ref.api = func.api
-        
     return check_host_cpu_ref
 
 def valid_vm(func):
-    """Decorator to verify if vm_ref is valid before calling
-    method.
-
-    @param func: function with params: (self, session, vm_ref)
+    """Decorator to verify if vm_ref is valid before calling method.
+
+    @param func: function with params: (self, session, vm_ref, ...)
     @rtype: callable object
     """    
-    def check_vm_ref(self, session, *args, **kwargs):
-        if len(args) == 0:
-            # This will trigger a TypeError, because there aren't enough
-            # arguments, which will be caught higher up and diagnosed.
-            func(self, session)
-            assert false
-
-        vm_ref = args[0]
+    def check_vm_ref(self, session, vm_ref, *args, **kwargs):
         xendom = XendDomain.instance()
         if type(vm_ref) == type(str()) and \
                xendom.is_valid_vm(vm_ref):
-            return func(self, session, *args, **kwargs)
+            return func(self, session, vm_ref, *args, **kwargs)
         else:
             return xen_api_error(['VM_HANDLE_INVALID', vm_ref])
 
-    # make sure we keep the 'api' attribute
-    if hasattr(func, 'api'):
-        check_vm_ref.api = func.api
-        
     return check_vm_ref
 
 def valid_vbd(func):
-    """Decorator to verify if vbd_ref is valid before calling
-    method.
-
-    @param func: function with params: (self, session, vbd_ref)
+    """Decorator to verify if vbd_ref is valid before calling method.
+
+    @param func: function with params: (self, session, vbd_ref, ...)
     @rtype: callable object
     """    
     def check_vbd_ref(self, session, vbd_ref, *args, **kwargs):
@@ -212,17 +199,12 @@ def valid_vbd(func):
         else:
             return xen_api_error(['VBD_HANDLE_INVALID', vbd_ref])
 
-    # make sure we keep the 'api' attribute
-    if hasattr(func, 'api'):
-        check_vbd_ref.api = func.api
-        
     return check_vbd_ref
 
 def valid_vif(func):
-    """Decorator to verify if vif_ref is valid before calling
-    method.
-
-    @param func: function with params: (self, session, vif_ref)
+    """Decorator to verify if vif_ref is valid before calling method.
+
+    @param func: function with params: (self, session, vif_ref, ...)
     @rtype: callable object
     """
     def check_vif_ref(self, session, vif_ref, *args, **kwargs):
@@ -233,18 +215,13 @@ def valid_vif(func):
         else:
             return xen_api_error(['VIF_HANDLE_INVALID', vif_ref])
 
-    # make sure we keep the 'api' attribute
-    if hasattr(func, 'api'):
-        check_vif_ref.api = func.api
-        
     return check_vif_ref
 
 
 def valid_vdi(func):
-    """Decorator to verify if vdi_ref is valid before calling
-    method.
-
-    @param func: function with params: (self, session, vdi_ref)
+    """Decorator to verify if vdi_ref is valid before calling method.
+
+    @param func: function with params: (self, session, vdi_ref, ...)
     @rtype: callable object
     """
     def check_vdi_ref(self, session, vdi_ref, *args, **kwargs):
@@ -255,17 +232,12 @@ def valid_vdi(func):
         else:
             return xen_api_error(['VDI_HANDLE_INVALID', vdi_ref])
 
-    # make sure we keep the 'api' attribute
-    if hasattr(func, 'api'):
-        check_vdi_ref.api = func.api
-        
     return check_vdi_ref
 
 def valid_vtpm(func):
-    """Decorator to verify if vtpm_ref is valid before calling
-    method.
-
-    @param func: function with params: (self, session, vtpm_ref)
+    """Decorator to verify if vtpm_ref is valid before calling method.
+
+    @param func: function with params: (self, session, vtpm_ref, ...)
     @rtype: callable object
     """
     def check_vtpm_ref(self, session, vtpm_ref, *args, **kwargs):
@@ -276,17 +248,12 @@ def valid_vtpm(func):
         else:
             return xen_api_error(['VTPM_HANDLE_INVALID', vtpm_ref])
 
-    # make sure we keep the 'api' attribute
-    if hasattr(func, 'api'):
-        check_vtpm_ref.api = func.api
-
     return check_vtpm_ref
 
 def valid_sr(func):
-    """Decorator to verify if sr_ref is valid before calling
-    method.
-
-    @param func: function with params: (self, session, sr_ref)
+    """Decorator to verify if sr_ref is valid before calling method.
+
+    @param func: function with params: (self, session, sr_ref, ...)
     @rtype: callable object
     """
     def check_sr_ref(self, session, sr_ref, *args, **kwargs):
@@ -297,10 +264,6 @@ def valid_sr(func):
         else:
             return xen_api_error(['SR_HANDLE_INVALID', sr_ref])
 
-    # make sure we keep the 'api' attribute
-    if hasattr(func, 'api'):
-        check_sr_ref.api = func.api
-        
     return check_sr_ref
 
 # -----------------------------
@@ -338,109 +301,13 @@ class XendAPI:
     """
 
     def __init__(self, auth):
-        """Initialised Xen API wrapper by making sure all functions
-        have the correct validation decorators such as L{valid_host}
-        and L{session_required}.
-        """
         self.auth = auth
-
-        classes = {
-            'session': (session_required, catch_typeerror),
-            'host': (valid_host, session_required, catch_typeerror),
-            'host_cpu': (valid_host_cpu, session_required, catch_typeerror),
-            'VM': (valid_vm, session_required, catch_typeerror),
-            'VBD': (valid_vbd, session_required, catch_typeerror),
-            'VIF': (valid_vif, session_required, catch_typeerror),
-            'VDI': (valid_vdi, session_required, catch_typeerror),
-            'VTPM':(valid_vtpm, session_required, catch_typeerror),
-            'SR':  (valid_sr, session_required, catch_typeerror)}
-        
-        # Cheat methods
-        # -------------
-        # Methods that have a trivial implementation for all classes.
-        # 1. get_by_uuid == getting by ref, so just return uuid for
-        #    all get_by_uuid() methods.
-        
-        for cls in classes.keys():
-            get_by_uuid = '%s_get_by_uuid' % cls
-            get_uuid = '%s_get_uuid' % cls
-            setattr(XendAPI, get_by_uuid,
-                    lambda s, sess, obj_ref: xen_api_success(obj_ref))
-            setattr(XendAPI, get_uuid,
-                    lambda s, sess, obj_ref: xen_api_success(obj_ref))
-
-        # 2. get_record is just getting all the attributes, so provide
-        #    a fake template implementation.
-        # 
-        # TODO: ...
-
-
-        # Wrapping validators around XMLRPC calls
-        # ---------------------------------------
-        
-        for cls, validators in classes.items():
-            ro_attrs = getattr(self, '%s_attr_ro' % cls, [])
-            rw_attrs = getattr(self, '%s_attr_rw' % cls, [])
-            methods  = getattr(self, '%s_methods' % cls, [])
-            funcs    = getattr(self, '%s_funcs' % cls, [])
-
-            # wrap validators around readable class attributes
-            for attr_name in ro_attrs + rw_attrs + self.Base_attr_ro:
-                getter_name = '%s_get_%s' % (cls, attr_name)
-                try:
-                    getter = getattr(XendAPI, getter_name)
-                    for validator in validators:
-                        getter = validator(getter)
-                    getter.api = '%s.get_%s' % (cls, attr_name)
-                    setattr(XendAPI, getter_name, getter)
-                except AttributeError:
-                    pass
-                    #log.warn("API call: %s not found" % getter_name)
-
-            # wrap validators around writable class attrributes
-            for attr_name in rw_attrs + self.Base_attr_rw:
-                setter_name = '%s_set_%s' % (cls, attr_name)
-                try:
-                    setter = getattr(XendAPI, setter_name)
-                    for validator in validators:
-                        setter = validator(setter)
-                    setter.api = '%s.set_%s' % (cls, attr_name)
-                    setattr(XendAPI, setter_name, setter)
-                except AttributeError:
-                    pass
-                    #log.warn("API call: %s not found" % setter_name)
-
-            # wrap validators around methods
-            for method_name in methods + self.Base_methods:
-                method_full_name = '%s_%s' % (cls, method_name)
-
-                try:
-                    method = getattr(XendAPI, method_full_name)
-                    for validator in validators:
-                        method = validator(method)
-                    method.api = '%s.%s' % (cls, method_name)
-                    setattr(XendAPI, method_full_name, method)
-                except AttributeError:
-                    pass
-                    #log.warn('API call: %s not found' % method_full_name)
-
-            # wrap validators around class functions
-            for func_name in funcs + self.Base_funcs:
-                func_full_name = '%s_%s' % (cls, func_name)
-                try:
-                    method = getattr(XendAPI, func_full_name)
-                    method = catch_typeerror(session_required(method))
-                    method.api = '%s.%s' % (cls, func_name)
-                    setattr(XendAPI, func_full_name, method)
-                except AttributeError:
-                    pass
-                    #log.warn('API call: %s not found' % func_full_name)
 
 
     Base_attr_ro = ['uuid']
     Base_attr_rw = []
     Base_methods = ['destroy', 'get_record']
-    Base_funcs   = ['create', 'get_by_uuid', 'get_all']
+    Base_funcs   = ['create', 'get_all']
 
     # Xen API: Class Session
     # ----------------------------------------------------------------
@@ -511,7 +378,7 @@ class XendAPI:
                     'reboot',
                     'shutdown']
     
-    host_funcs = ['get_by_name_label']
+    host_funcs = ['get_by_uuid', 'get_by_name_label']
 
     # attributes
     def host_get_name_label(self, session, host_ref):
@@ -680,7 +547,7 @@ class XendAPI:
                   'suspend',
                   'resume']
     
-    VM_funcs  = ['get_by_name_label']
+    VM_funcs  = ['get_by_uuid', 'get_by_name_label']
 
     # parameters required for _create()
     VM_attr_inst = [
@@ -1244,7 +1111,7 @@ class XendAPI:
     VDI_attr_inst = VDI_attr_ro + VDI_attr_rw
 
     VDI_methods = ['snapshot']
-    VDI_funcs = ['get_by_name_label']
+    VDI_funcs = ['get_by_uuid', 'get_by_name_label']
     
     def VDI_get_VBDs(self, session, vdi_ref):
         return xen_api_todo()
@@ -1488,7 +1355,7 @@ class XendAPI:
                     'name_description']
     
     SR_methods = ['clone']
-    SR_funcs = ['get_by_name_label']
+    SR_funcs = ['get_by_uuid', 'get_by_name_label']
 
     # Class Functions
     def SR_get_all(self, session):
@@ -1571,6 +1438,112 @@ class XendAPI:
         sr.name_description = value
         return xen_api_success_void()
 
+
+def _decorate():
+    """Initialise Xen API wrapper by making sure all functions
+    have the correct validation decorators such as L{valid_host}
+    and L{session_required}.
+    """
+
+    classes = {
+        'session': (session_required, catch_typeerror),
+        'host': (valid_host, session_required, catch_typeerror),
+        'host_cpu': (valid_host_cpu, session_required, catch_typeerror),
+        'VM': (valid_vm, session_required, catch_typeerror),
+        'VBD': (valid_vbd, session_required, catch_typeerror),
+        'VIF': (valid_vif, session_required, catch_typeerror),
+        'VDI': (valid_vdi, session_required, catch_typeerror),
+        'VTPM':(valid_vtpm, session_required, catch_typeerror),
+        'SR':  (valid_sr, session_required, catch_typeerror)}
+
+    # Cheat methods
+    # -------------
+    # Methods that have a trivial implementation for all classes.
+    # 1. get_by_uuid == getting by ref, so just return uuid for
+    #    all get_by_uuid() methods.
+
+    for cls in classes.keys():
+        get_by_uuid = '%s_get_by_uuid' % cls
+        get_uuid = '%s_get_uuid' % cls
+        def _get_by_uuid(_1, _2, ref):
+            return xen_api_success(ref)
+
+        def _get_uuid(_1, _2, ref):
+            return xen_api_success(ref)
+
+        setattr(XendAPI, get_by_uuid, _get_by_uuid)
+        setattr(XendAPI, get_uuid,    _get_uuid)
+
+    # 2. get_record is just getting all the attributes, so provide
+    #    a fake template implementation.
+    # 
+    # TODO: ...
+
+
+    # Wrapping validators around XMLRPC calls
+    # ---------------------------------------
+
+    for cls, validators in classes.items():
+        ro_attrs = getattr(XendAPI, '%s_attr_ro' % cls, [])
+        rw_attrs = getattr(XendAPI, '%s_attr_rw' % cls, [])
+        methods  = getattr(XendAPI, '%s_methods' % cls, [])
+        funcs    = getattr(XendAPI, '%s_funcs'   % cls, [])
+
+        # wrap validators around readable class attributes
+        for attr_name in ro_attrs + rw_attrs + XendAPI.Base_attr_ro:
+            getter_name = '%s_get_%s' % (cls, attr_name)
+            try:
+                getter = getattr(XendAPI, getter_name)
+                for validator in validators:
+                    getter = validator(getter)
+                    getter.api = '%s.get_%s' % (cls, attr_name)
+                setattr(XendAPI, getter_name, getter)
+            except AttributeError:
+                pass
+                #log.warn("API call: %s not found" % getter_name)
+
+        # wrap validators around writable class attrributes
+        for attr_name in rw_attrs + XendAPI.Base_attr_rw:
+            setter_name = '%s_set_%s' % (cls, attr_name)
+            try:
+                setter = getattr(XendAPI, setter_name)
+                for validator in validators:
+                    setter = validator(setter)
+                    setter.api = '%s.set_%s' % (cls, attr_name)
+                setattr(XendAPI, setter_name, setter)
+            except AttributeError:
+                pass
+                #log.warn("API call: %s not found" % setter_name)
+
+        # wrap validators around methods
+        for method_name in methods + XendAPI.Base_methods:
+            method_full_name = '%s_%s' % (cls, method_name)
+            try:
+                method = getattr(XendAPI, method_full_name)
+                for validator in validators:
+                    method = validator(method)
+                    method.api = '%s.%s' % (cls, method_name)
+                setattr(XendAPI, method_full_name, method)
+            except AttributeError:
+                pass
+                #log.warn('API call: %s not found' % method_full_name)
+
+        # wrap validators around class functions
+        for func_name in funcs + XendAPI.Base_funcs:
+            func_full_name = '%s_%s' % (cls, func_name)
+            try:
+                method = getattr(XendAPI, func_full_name)
+                method = session_required(method)
+                method.api = '%s.%s' % (cls, func_name)
+                method = catch_typeerror(method)
+                method.api = '%s.%s' % (cls, func_name)
+                setattr(XendAPI, func_full_name, method)
+            except AttributeError:
+                log.warn('API call: %s not found' % func_full_name)
+
+_decorate()
+
+
 #   
 # Auto generate some stubs based on XendAPI introspection
 #

_______________________________________________
Xen-changelog mailing list
Xen-changelog@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-changelog


 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.