ia64/xen-unstable

changeset 13167:0d0e13ff1adf

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@xensource.com>
author Ewan Mellor <ewan@xensource.com>
date Fri Dec 22 11:38:05 2006 +0000 (2006-12-22)
parents f7ac2c963f77
children e45948c4dba4
files tools/python/xen/xend/XendAPI.py
line diff
     1.1 --- a/tools/python/xen/xend/XendAPI.py	Fri Dec 22 11:34:13 2006 +0000
     1.2 +++ b/tools/python/xen/xend/XendAPI.py	Fri Dec 22 11:38:05 2006 +0000
     1.3 @@ -15,7 +15,12 @@
     1.4  # Copyright (C) 2006 XenSource Ltd.
     1.5  #============================================================================
     1.6  
     1.7 +import inspect
     1.8 +import os
     1.9  import re
    1.10 +import string
    1.11 +import sys
    1.12 +import traceback
    1.13  
    1.14  from xen.xend import XendDomain, XendDomainInfo, XendNode
    1.15  from xen.xend import XendLogging
    1.16 @@ -52,7 +57,7 @@ def xen_api_error(error):
    1.17      if len(error) == 0:
    1.18          error = ['INTERNAL_ERROR', 'Empty list given to xen_api_error']
    1.19  
    1.20 -    return { "Status": "Error",
    1.21 +    return { "Status": "Failure",
    1.22               "ErrorDescription": [str(x) for x in error] }
    1.23  
    1.24  
    1.25 @@ -79,17 +84,17 @@ def trace(func, api_name = ''):
    1.26      return trace_func
    1.27  
    1.28  
    1.29 -takesRE = re.compile(r'^(.*)\(\) takes exactly ([0-9]*) argument')
    1.30 +takesRE = re.compile(r' ([0-9]*) argument')
    1.31  def deconstruct_typeerror(exn):
    1.32      m = takesRE.search(exn[0])
    1.33 -    return m and m.groups() or None
    1.34 +    return m and m.group(1) or None
    1.35  
    1.36  
    1.37  def catch_typeerror(func):
    1.38      """Decorator to catch any TypeErrors and translate them into Xen-API
    1.39      errors.
    1.40  
    1.41 -    @param func: function with params: (self, session, host_ref)
    1.42 +    @param func: function with params: (self, ...)
    1.43      @rtype: callable object
    1.44      """
    1.45      def f(self, *args, **kwargs):
    1.46 @@ -97,44 +102,48 @@ def catch_typeerror(func):
    1.47              return func(self, *args, **kwargs)
    1.48          except TypeError, exn:
    1.49              if hasattr(func, 'api'):
    1.50 -                mt = deconstruct_typeerror(exn)
    1.51 -                if mt:
    1.52 -                    method, takes = mt
    1.53 -                    if method.endswith(func.api.split('.')[-1]):
    1.54 -                        return xen_api_error(
    1.55 -                            ['MESSAGE_PARAMETER_COUNT_MISMATCH',
    1.56 -                             func.api, int(takes) - 2,
    1.57 -                             len(args) + len(kwargs) - 1])
    1.58 +                takes = deconstruct_typeerror(exn)
    1.59 +                if takes is not None:
    1.60 +                    # Assume that if the exception was thrown inside this
    1.61 +                    # file, then it is due to an invalid call from the client,
    1.62 +                    # but if it was thrown elsewhere, then it's an internal
    1.63 +                    # error (which will be handled further up).
    1.64 +                    tb = sys.exc_info()[2]
    1.65 +                    try:
    1.66 +                        sourcefile = traceback.extract_tb(tb)[-1][0]
    1.67 +                        if sourcefile == inspect.getsourcefile(XendAPI):
    1.68 +                            return xen_api_error(
    1.69 +                                ['MESSAGE_PARAMETER_COUNT_MISMATCH',
    1.70 +                                 func.api, int(takes) - 2,
    1.71 +                                 len(args) + len(kwargs) - 1])
    1.72 +                    finally:
    1.73 +                        del tb
    1.74              raise
    1.75  
    1.76 -    # make sure we keep the 'api' attribute
    1.77 -    if hasattr(func, 'api'):
    1.78 -        f.api = func.api
    1.79 -        
    1.80      return f
    1.81  
    1.82  
    1.83  def session_required(func):
    1.84 +    """Decorator to verify if session is valid before calling method.
    1.85 +
    1.86 +    @param func: function with params: (self, session, ...)
    1.87 +    @rtype: callable object
    1.88 +    """    
    1.89      def check_session(self, session, *args, **kwargs):
    1.90          if auth_manager().is_session_valid(session):
    1.91              return func(self, session, *args, **kwargs)
    1.92          else:
    1.93              return xen_api_error(['SESSION_INVALID', session])
    1.94  
    1.95 -    # make sure we keep the 'api' attribute
    1.96 -    if hasattr(func, 'api'):
    1.97 -        check_session.api = func.api
    1.98 -
    1.99      return check_session
   1.100  
   1.101  
   1.102  def valid_host(func):
   1.103 -    """Decorator to verify if host_ref is valid before calling
   1.104 -    method.
   1.105 +    """Decorator to verify if host_ref is valid before calling method.
   1.106  
   1.107 -    @param func: function with params: (self, session, host_ref)
   1.108 +    @param func: function with params: (self, session, host_ref, ...)
   1.109      @rtype: callable object
   1.110 -    """    
   1.111 +    """
   1.112      def check_host_ref(self, session, host_ref, *args, **kwargs):
   1.113          xennode = XendNode.instance()
   1.114          if type(host_ref) == type(str()) and xennode.is_valid_host(host_ref):
   1.115 @@ -142,17 +151,12 @@ def valid_host(func):
   1.116          else:
   1.117              return xen_api_error(['HOST_HANDLE_INVALID', host_ref])
   1.118  
   1.119 -    # make sure we keep the 'api' attribute
   1.120 -    if hasattr(func, 'api'):
   1.121 -        check_host_ref.api = func.api
   1.122 -        
   1.123      return check_host_ref
   1.124  
   1.125  def valid_host_cpu(func):
   1.126 -    """Decorator to verify if host_cpu_ref is valid before calling
   1.127 -    method.
   1.128 +    """Decorator to verify if host_cpu_ref is valid before calling method.
   1.129  
   1.130 -    @param func: function with params: (self, session, host_cpu_ref)
   1.131 +    @param func: function with params: (self, session, host_cpu_ref, ...)
   1.132      @rtype: callable object
   1.133      """    
   1.134      def check_host_cpu_ref(self, session, host_cpu_ref, *args, **kwargs):
   1.135 @@ -163,45 +167,28 @@ def valid_host_cpu(func):
   1.136          else:
   1.137              return xen_api_error(['HOST_CPU_HANDLE_INVALID', host_cpu_ref])
   1.138          
   1.139 -    # make sure we keep the 'api' attribute
   1.140 -    if hasattr(func, 'api'):
   1.141 -        check_host_cpu_ref.api = func.api
   1.142 -        
   1.143      return check_host_cpu_ref
   1.144  
   1.145  def valid_vm(func):
   1.146 -    """Decorator to verify if vm_ref is valid before calling
   1.147 -    method.
   1.148 +    """Decorator to verify if vm_ref is valid before calling method.
   1.149  
   1.150 -    @param func: function with params: (self, session, vm_ref)
   1.151 +    @param func: function with params: (self, session, vm_ref, ...)
   1.152      @rtype: callable object
   1.153      """    
   1.154 -    def check_vm_ref(self, session, *args, **kwargs):
   1.155 -        if len(args) == 0:
   1.156 -            # This will trigger a TypeError, because there aren't enough
   1.157 -            # arguments, which will be caught higher up and diagnosed.
   1.158 -            func(self, session)
   1.159 -            assert false
   1.160 -
   1.161 -        vm_ref = args[0]
   1.162 +    def check_vm_ref(self, session, vm_ref, *args, **kwargs):
   1.163          xendom = XendDomain.instance()
   1.164          if type(vm_ref) == type(str()) and \
   1.165                 xendom.is_valid_vm(vm_ref):
   1.166 -            return func(self, session, *args, **kwargs)
   1.167 +            return func(self, session, vm_ref, *args, **kwargs)
   1.168          else:
   1.169              return xen_api_error(['VM_HANDLE_INVALID', vm_ref])
   1.170  
   1.171 -    # make sure we keep the 'api' attribute
   1.172 -    if hasattr(func, 'api'):
   1.173 -        check_vm_ref.api = func.api
   1.174 -        
   1.175      return check_vm_ref
   1.176  
   1.177  def valid_vbd(func):
   1.178 -    """Decorator to verify if vbd_ref is valid before calling
   1.179 -    method.
   1.180 +    """Decorator to verify if vbd_ref is valid before calling method.
   1.181  
   1.182 -    @param func: function with params: (self, session, vbd_ref)
   1.183 +    @param func: function with params: (self, session, vbd_ref, ...)
   1.184      @rtype: callable object
   1.185      """    
   1.186      def check_vbd_ref(self, session, vbd_ref, *args, **kwargs):
   1.187 @@ -212,17 +199,12 @@ def valid_vbd(func):
   1.188          else:
   1.189              return xen_api_error(['VBD_HANDLE_INVALID', vbd_ref])
   1.190  
   1.191 -    # make sure we keep the 'api' attribute
   1.192 -    if hasattr(func, 'api'):
   1.193 -        check_vbd_ref.api = func.api
   1.194 -        
   1.195      return check_vbd_ref
   1.196  
   1.197  def valid_vif(func):
   1.198 -    """Decorator to verify if vif_ref is valid before calling
   1.199 -    method.
   1.200 +    """Decorator to verify if vif_ref is valid before calling method.
   1.201  
   1.202 -    @param func: function with params: (self, session, vif_ref)
   1.203 +    @param func: function with params: (self, session, vif_ref, ...)
   1.204      @rtype: callable object
   1.205      """
   1.206      def check_vif_ref(self, session, vif_ref, *args, **kwargs):
   1.207 @@ -233,18 +215,13 @@ def valid_vif(func):
   1.208          else:
   1.209              return xen_api_error(['VIF_HANDLE_INVALID', vif_ref])
   1.210  
   1.211 -    # make sure we keep the 'api' attribute
   1.212 -    if hasattr(func, 'api'):
   1.213 -        check_vif_ref.api = func.api
   1.214 -        
   1.215      return check_vif_ref
   1.216  
   1.217  
   1.218  def valid_vdi(func):
   1.219 -    """Decorator to verify if vdi_ref is valid before calling
   1.220 -    method.
   1.221 +    """Decorator to verify if vdi_ref is valid before calling method.
   1.222  
   1.223 -    @param func: function with params: (self, session, vdi_ref)
   1.224 +    @param func: function with params: (self, session, vdi_ref, ...)
   1.225      @rtype: callable object
   1.226      """
   1.227      def check_vdi_ref(self, session, vdi_ref, *args, **kwargs):
   1.228 @@ -255,17 +232,12 @@ def valid_vdi(func):
   1.229          else:
   1.230              return xen_api_error(['VDI_HANDLE_INVALID', vdi_ref])
   1.231  
   1.232 -    # make sure we keep the 'api' attribute
   1.233 -    if hasattr(func, 'api'):
   1.234 -        check_vdi_ref.api = func.api
   1.235 -        
   1.236      return check_vdi_ref
   1.237  
   1.238  def valid_vtpm(func):
   1.239 -    """Decorator to verify if vtpm_ref is valid before calling
   1.240 -    method.
   1.241 +    """Decorator to verify if vtpm_ref is valid before calling method.
   1.242  
   1.243 -    @param func: function with params: (self, session, vtpm_ref)
   1.244 +    @param func: function with params: (self, session, vtpm_ref, ...)
   1.245      @rtype: callable object
   1.246      """
   1.247      def check_vtpm_ref(self, session, vtpm_ref, *args, **kwargs):
   1.248 @@ -276,17 +248,12 @@ def valid_vtpm(func):
   1.249          else:
   1.250              return xen_api_error(['VTPM_HANDLE_INVALID', vtpm_ref])
   1.251  
   1.252 -    # make sure we keep the 'api' attribute
   1.253 -    if hasattr(func, 'api'):
   1.254 -        check_vtpm_ref.api = func.api
   1.255 -
   1.256      return check_vtpm_ref
   1.257  
   1.258  def valid_sr(func):
   1.259 -    """Decorator to verify if sr_ref is valid before calling
   1.260 -    method.
   1.261 +    """Decorator to verify if sr_ref is valid before calling method.
   1.262  
   1.263 -    @param func: function with params: (self, session, sr_ref)
   1.264 +    @param func: function with params: (self, session, sr_ref, ...)
   1.265      @rtype: callable object
   1.266      """
   1.267      def check_sr_ref(self, session, sr_ref, *args, **kwargs):
   1.268 @@ -297,10 +264,6 @@ def valid_sr(func):
   1.269          else:
   1.270              return xen_api_error(['SR_HANDLE_INVALID', sr_ref])
   1.271  
   1.272 -    # make sure we keep the 'api' attribute
   1.273 -    if hasattr(func, 'api'):
   1.274 -        check_sr_ref.api = func.api
   1.275 -        
   1.276      return check_sr_ref
   1.277  
   1.278  # -----------------------------
   1.279 @@ -338,109 +301,13 @@ class XendAPI:
   1.280      """
   1.281  
   1.282      def __init__(self, auth):
   1.283 -        """Initialised Xen API wrapper by making sure all functions
   1.284 -        have the correct validation decorators such as L{valid_host}
   1.285 -        and L{session_required}.
   1.286 -        """
   1.287          self.auth = auth
   1.288  
   1.289 -        classes = {
   1.290 -            'session': (session_required, catch_typeerror),
   1.291 -            'host': (valid_host, session_required, catch_typeerror),
   1.292 -            'host_cpu': (valid_host_cpu, session_required, catch_typeerror),
   1.293 -            'VM': (valid_vm, session_required, catch_typeerror),
   1.294 -            'VBD': (valid_vbd, session_required, catch_typeerror),
   1.295 -            'VIF': (valid_vif, session_required, catch_typeerror),
   1.296 -            'VDI': (valid_vdi, session_required, catch_typeerror),
   1.297 -            'VTPM':(valid_vtpm, session_required, catch_typeerror),
   1.298 -            'SR':  (valid_sr, session_required, catch_typeerror)}
   1.299 -        
   1.300 -        # Cheat methods
   1.301 -        # -------------
   1.302 -        # Methods that have a trivial implementation for all classes.
   1.303 -        # 1. get_by_uuid == getting by ref, so just return uuid for
   1.304 -        #    all get_by_uuid() methods.
   1.305 -        
   1.306 -        for cls in classes.keys():
   1.307 -            get_by_uuid = '%s_get_by_uuid' % cls
   1.308 -            get_uuid = '%s_get_uuid' % cls
   1.309 -            setattr(XendAPI, get_by_uuid,
   1.310 -                    lambda s, sess, obj_ref: xen_api_success(obj_ref))
   1.311 -            setattr(XendAPI, get_uuid,
   1.312 -                    lambda s, sess, obj_ref: xen_api_success(obj_ref))
   1.313 -
   1.314 -        # 2. get_record is just getting all the attributes, so provide
   1.315 -        #    a fake template implementation.
   1.316 -        # 
   1.317 -        # TODO: ...
   1.318 -
   1.319 -
   1.320 -        # Wrapping validators around XMLRPC calls
   1.321 -        # ---------------------------------------
   1.322 -        
   1.323 -        for cls, validators in classes.items():
   1.324 -            ro_attrs = getattr(self, '%s_attr_ro' % cls, [])
   1.325 -            rw_attrs = getattr(self, '%s_attr_rw' % cls, [])
   1.326 -            methods  = getattr(self, '%s_methods' % cls, [])
   1.327 -            funcs    = getattr(self, '%s_funcs' % cls, [])
   1.328 -
   1.329 -            # wrap validators around readable class attributes
   1.330 -            for attr_name in ro_attrs + rw_attrs + self.Base_attr_ro:
   1.331 -                getter_name = '%s_get_%s' % (cls, attr_name)
   1.332 -                try:
   1.333 -                    getter = getattr(XendAPI, getter_name)
   1.334 -                    for validator in validators:
   1.335 -                        getter = validator(getter)
   1.336 -                    getter.api = '%s.get_%s' % (cls, attr_name)
   1.337 -                    setattr(XendAPI, getter_name, getter)
   1.338 -                except AttributeError:
   1.339 -                    pass
   1.340 -                    #log.warn("API call: %s not found" % getter_name)
   1.341 -
   1.342 -            # wrap validators around writable class attrributes
   1.343 -            for attr_name in rw_attrs + self.Base_attr_rw:
   1.344 -                setter_name = '%s_set_%s' % (cls, attr_name)
   1.345 -                try:
   1.346 -                    setter = getattr(XendAPI, setter_name)
   1.347 -                    for validator in validators:
   1.348 -                        setter = validator(setter)
   1.349 -                    setter.api = '%s.set_%s' % (cls, attr_name)
   1.350 -                    setattr(XendAPI, setter_name, setter)
   1.351 -                except AttributeError:
   1.352 -                    pass
   1.353 -                    #log.warn("API call: %s not found" % setter_name)
   1.354 -
   1.355 -            # wrap validators around methods
   1.356 -            for method_name in methods + self.Base_methods:
   1.357 -                method_full_name = '%s_%s' % (cls, method_name)
   1.358 -
   1.359 -                try:
   1.360 -                    method = getattr(XendAPI, method_full_name)
   1.361 -                    for validator in validators:
   1.362 -                        method = validator(method)
   1.363 -                    method.api = '%s.%s' % (cls, method_name)
   1.364 -                    setattr(XendAPI, method_full_name, method)
   1.365 -                except AttributeError:
   1.366 -                    pass
   1.367 -                    #log.warn('API call: %s not found' % method_full_name)
   1.368 -
   1.369 -            # wrap validators around class functions
   1.370 -            for func_name in funcs + self.Base_funcs:
   1.371 -                func_full_name = '%s_%s' % (cls, func_name)
   1.372 -                try:
   1.373 -                    method = getattr(XendAPI, func_full_name)
   1.374 -                    method = catch_typeerror(session_required(method))
   1.375 -                    method.api = '%s.%s' % (cls, func_name)
   1.376 -                    setattr(XendAPI, func_full_name, method)
   1.377 -                except AttributeError:
   1.378 -                    pass
   1.379 -                    #log.warn('API call: %s not found' % func_full_name)
   1.380 -
   1.381  
   1.382      Base_attr_ro = ['uuid']
   1.383      Base_attr_rw = []
   1.384      Base_methods = ['destroy', 'get_record']
   1.385 -    Base_funcs   = ['create', 'get_by_uuid', 'get_all']
   1.386 +    Base_funcs   = ['create', 'get_all']
   1.387  
   1.388      # Xen API: Class Session
   1.389      # ----------------------------------------------------------------
   1.390 @@ -511,7 +378,7 @@ class XendAPI:
   1.391                      'reboot',
   1.392                      'shutdown']
   1.393      
   1.394 -    host_funcs = ['get_by_name_label']
   1.395 +    host_funcs = ['get_by_uuid', 'get_by_name_label']
   1.396  
   1.397      # attributes
   1.398      def host_get_name_label(self, session, host_ref):
   1.399 @@ -680,7 +547,7 @@ class XendAPI:
   1.400                    'suspend',
   1.401                    'resume']
   1.402      
   1.403 -    VM_funcs  = ['get_by_name_label']
   1.404 +    VM_funcs  = ['get_by_uuid', 'get_by_name_label']
   1.405  
   1.406      # parameters required for _create()
   1.407      VM_attr_inst = [
   1.408 @@ -1244,7 +1111,7 @@ class XendAPI:
   1.409      VDI_attr_inst = VDI_attr_ro + VDI_attr_rw
   1.410  
   1.411      VDI_methods = ['snapshot']
   1.412 -    VDI_funcs = ['get_by_name_label']
   1.413 +    VDI_funcs = ['get_by_uuid', 'get_by_name_label']
   1.414      
   1.415      def VDI_get_VBDs(self, session, vdi_ref):
   1.416          return xen_api_todo()
   1.417 @@ -1488,7 +1355,7 @@ class XendAPI:
   1.418                      'name_description']
   1.419      
   1.420      SR_methods = ['clone']
   1.421 -    SR_funcs = ['get_by_name_label']
   1.422 +    SR_funcs = ['get_by_uuid', 'get_by_name_label']
   1.423  
   1.424      # Class Functions
   1.425      def SR_get_all(self, session):
   1.426 @@ -1571,6 +1438,112 @@ class XendAPI:
   1.427          sr.name_description = value
   1.428          return xen_api_success_void()
   1.429  
   1.430 +
   1.431 +def _decorate():
   1.432 +    """Initialise Xen API wrapper by making sure all functions
   1.433 +    have the correct validation decorators such as L{valid_host}
   1.434 +    and L{session_required}.
   1.435 +    """
   1.436 +
   1.437 +    classes = {
   1.438 +        'session': (session_required, catch_typeerror),
   1.439 +        'host': (valid_host, session_required, catch_typeerror),
   1.440 +        'host_cpu': (valid_host_cpu, session_required, catch_typeerror),
   1.441 +        'VM': (valid_vm, session_required, catch_typeerror),
   1.442 +        'VBD': (valid_vbd, session_required, catch_typeerror),
   1.443 +        'VIF': (valid_vif, session_required, catch_typeerror),
   1.444 +        'VDI': (valid_vdi, session_required, catch_typeerror),
   1.445 +        'VTPM':(valid_vtpm, session_required, catch_typeerror),
   1.446 +        'SR':  (valid_sr, session_required, catch_typeerror)}
   1.447 +
   1.448 +    # Cheat methods
   1.449 +    # -------------
   1.450 +    # Methods that have a trivial implementation for all classes.
   1.451 +    # 1. get_by_uuid == getting by ref, so just return uuid for
   1.452 +    #    all get_by_uuid() methods.
   1.453 +
   1.454 +    for cls in classes.keys():
   1.455 +        get_by_uuid = '%s_get_by_uuid' % cls
   1.456 +        get_uuid = '%s_get_uuid' % cls
   1.457 +        def _get_by_uuid(_1, _2, ref):
   1.458 +            return xen_api_success(ref)
   1.459 +
   1.460 +        def _get_uuid(_1, _2, ref):
   1.461 +            return xen_api_success(ref)
   1.462 +
   1.463 +        setattr(XendAPI, get_by_uuid, _get_by_uuid)
   1.464 +        setattr(XendAPI, get_uuid,    _get_uuid)
   1.465 +
   1.466 +    # 2. get_record is just getting all the attributes, so provide
   1.467 +    #    a fake template implementation.
   1.468 +    # 
   1.469 +    # TODO: ...
   1.470 +
   1.471 +
   1.472 +    # Wrapping validators around XMLRPC calls
   1.473 +    # ---------------------------------------
   1.474 +
   1.475 +    for cls, validators in classes.items():
   1.476 +        ro_attrs = getattr(XendAPI, '%s_attr_ro' % cls, [])
   1.477 +        rw_attrs = getattr(XendAPI, '%s_attr_rw' % cls, [])
   1.478 +        methods  = getattr(XendAPI, '%s_methods' % cls, [])
   1.479 +        funcs    = getattr(XendAPI, '%s_funcs'   % cls, [])
   1.480 +
   1.481 +        # wrap validators around readable class attributes
   1.482 +        for attr_name in ro_attrs + rw_attrs + XendAPI.Base_attr_ro:
   1.483 +            getter_name = '%s_get_%s' % (cls, attr_name)
   1.484 +            try:
   1.485 +                getter = getattr(XendAPI, getter_name)
   1.486 +                for validator in validators:
   1.487 +                    getter = validator(getter)
   1.488 +                    getter.api = '%s.get_%s' % (cls, attr_name)
   1.489 +                setattr(XendAPI, getter_name, getter)
   1.490 +            except AttributeError:
   1.491 +                pass
   1.492 +                #log.warn("API call: %s not found" % getter_name)
   1.493 +
   1.494 +        # wrap validators around writable class attrributes
   1.495 +        for attr_name in rw_attrs + XendAPI.Base_attr_rw:
   1.496 +            setter_name = '%s_set_%s' % (cls, attr_name)
   1.497 +            try:
   1.498 +                setter = getattr(XendAPI, setter_name)
   1.499 +                for validator in validators:
   1.500 +                    setter = validator(setter)
   1.501 +                    setter.api = '%s.set_%s' % (cls, attr_name)
   1.502 +                setattr(XendAPI, setter_name, setter)
   1.503 +            except AttributeError:
   1.504 +                pass
   1.505 +                #log.warn("API call: %s not found" % setter_name)
   1.506 +
   1.507 +        # wrap validators around methods
   1.508 +        for method_name in methods + XendAPI.Base_methods:
   1.509 +            method_full_name = '%s_%s' % (cls, method_name)
   1.510 +            try:
   1.511 +                method = getattr(XendAPI, method_full_name)
   1.512 +                for validator in validators:
   1.513 +                    method = validator(method)
   1.514 +                    method.api = '%s.%s' % (cls, method_name)
   1.515 +                setattr(XendAPI, method_full_name, method)
   1.516 +            except AttributeError:
   1.517 +                pass
   1.518 +                #log.warn('API call: %s not found' % method_full_name)
   1.519 +
   1.520 +        # wrap validators around class functions
   1.521 +        for func_name in funcs + XendAPI.Base_funcs:
   1.522 +            func_full_name = '%s_%s' % (cls, func_name)
   1.523 +            try:
   1.524 +                method = getattr(XendAPI, func_full_name)
   1.525 +                method = session_required(method)
   1.526 +                method.api = '%s.%s' % (cls, func_name)
   1.527 +                method = catch_typeerror(method)
   1.528 +                method.api = '%s.%s' % (cls, func_name)
   1.529 +                setattr(XendAPI, func_full_name, method)
   1.530 +            except AttributeError:
   1.531 +                log.warn('API call: %s not found' % func_full_name)
   1.532 +
   1.533 +_decorate()
   1.534 +
   1.535 +
   1.536  #   
   1.537  # Auto generate some stubs based on XendAPI introspection
   1.538  #