ia64/xen-unstable

changeset 13159:c3d84afbbb47

Implement the major part of the new error handling for the Xen-API.

Signed-off-by: Ewan Mellor <ewan@xensource.com>
author Ewan Mellor <ewan@xensource.com>
date Thu Dec 21 15:16:25 2006 +0000 (2006-12-21)
parents 38213c2544d7
children 43f367b6c16c
files tools/python/xen/xend/XendAPI.py tools/python/xen/xend/XendAuthSessions.py
line diff
     1.1 --- a/tools/python/xen/xend/XendAPI.py	Thu Dec 21 13:12:09 2006 +0000
     1.2 +++ b/tools/python/xen/xend/XendAPI.py	Thu Dec 21 15:16:25 2006 +0000
     1.3 @@ -15,11 +15,12 @@
     1.4  # Copyright (C) 2006 XenSource Ltd.
     1.5  #============================================================================
     1.6  
     1.7 +import re
     1.8 +
     1.9  from xen.xend import XendDomain, XendDomainInfo, XendNode
    1.10  from xen.xend import XendLogging
    1.11  
    1.12  from xen.xend.XendAuthSessions import instance as auth_manager
    1.13 -from xen.xend.XendAuthSessions import session_required
    1.14  from xen.xend.XendError import *
    1.15  from xen.xend.XendClient import ERROR_INVALID_DOMAIN
    1.16  from xen.xend.XendLogging import log
    1.17 @@ -44,7 +45,16 @@ def xen_api_success_void():
    1.18  
    1.19  def xen_api_error(error):
    1.20      """Wraps an error value in XenAPI format."""
    1.21 -    return {"Status": "Error", "ErrorDescription": error}
    1.22 +    if type(error) == tuple:
    1.23 +        error = list(error)
    1.24 +    if type(error) != list:
    1.25 +        error = [error]
    1.26 +    if len(error) == 0:
    1.27 +        error = ['INTERNAL_ERROR', 'Empty list given to xen_api_error']
    1.28 +
    1.29 +    return { "Status": "Error",
    1.30 +             "ErrorDescription": [str(x) for x in error] }
    1.31 +
    1.32  
    1.33  def xen_api_todo():
    1.34      """Temporary method to make sure we track down all the TODOs"""
    1.35 @@ -68,6 +78,56 @@ def trace(func, api_name = ''):
    1.36      trace_func.api = api_name
    1.37      return trace_func
    1.38  
    1.39 +
    1.40 +takesRE = re.compile(r'^(.*)\(\) takes exactly ([0-9]*) argument')
    1.41 +def deconstruct_typeerror(exn):
    1.42 +    m = takesRE.search(exn[0])
    1.43 +    return m and m.groups() or None
    1.44 +
    1.45 +
    1.46 +def catch_typeerror(func):
    1.47 +    """Decorator to catch any TypeErrors and translate them into Xen-API
    1.48 +    errors.
    1.49 +
    1.50 +    @param func: function with params: (self, session, host_ref)
    1.51 +    @rtype: callable object
    1.52 +    """
    1.53 +    def f(self, *args, **kwargs):
    1.54 +        try:
    1.55 +            return func(self, *args, **kwargs)
    1.56 +        except TypeError, exn:
    1.57 +            if hasattr(func, 'api'):
    1.58 +                mt = deconstruct_typeerror(exn)
    1.59 +                if mt:
    1.60 +                    method, takes = mt
    1.61 +                    if method.endswith(func.api.split('.')[-1]):
    1.62 +                        return xen_api_error(
    1.63 +                            ['MESSAGE_PARAMETER_COUNT_MISMATCH',
    1.64 +                             func.api, int(takes) - 2,
    1.65 +                             len(args) + len(kwargs) - 1])
    1.66 +            raise
    1.67 +
    1.68 +    # make sure we keep the 'api' attribute
    1.69 +    if hasattr(func, 'api'):
    1.70 +        f.api = func.api
    1.71 +        
    1.72 +    return f
    1.73 +
    1.74 +
    1.75 +def session_required(func):
    1.76 +    def check_session(self, session, *args, **kwargs):
    1.77 +        if auth_manager().is_session_valid(session):
    1.78 +            return func(self, session, *args, **kwargs)
    1.79 +        else:
    1.80 +            return xen_api_error(['SESSION_INVALID', session])
    1.81 +
    1.82 +    # make sure we keep the 'api' attribute
    1.83 +    if hasattr(func, 'api'):
    1.84 +        check_session.api = func.api
    1.85 +
    1.86 +    return check_session
    1.87 +
    1.88 +
    1.89  def valid_host(func):
    1.90      """Decorator to verify if host_ref is valid before calling
    1.91      method.
    1.92 @@ -80,8 +140,7 @@ def valid_host(func):
    1.93          if type(host_ref) == type(str()) and xennode.is_valid_host(host_ref):
    1.94              return func(self, session, host_ref, *args, **kwargs)
    1.95          else:
    1.96 -            return {'Status': 'Failure',
    1.97 -                    'ErrorDescription': XEND_ERROR_HOST_INVALID}
    1.98 +            return xen_api_error(['HOST_HANDLE_INVALID', host_ref])
    1.99  
   1.100      # make sure we keep the 'api' attribute
   1.101      if hasattr(func, 'api'):
   1.102 @@ -102,8 +161,7 @@ def valid_host_cpu(func):
   1.103                 xennode.is_valid_cpu(host_cpu_ref):
   1.104              return func(self, session, host_cpu_ref, *args, **kwargs)
   1.105          else:
   1.106 -            return {'Status': 'Failure',
   1.107 -                    'ErrorDescription': XEND_ERROR_HOST_CPU_INVALID}
   1.108 +            return xen_api_error(['HOST_CPU_HANDLE_INVALID', host_cpu_ref])
   1.109          
   1.110      # make sure we keep the 'api' attribute
   1.111      if hasattr(func, 'api'):
   1.112 @@ -120,8 +178,10 @@ def valid_vm(func):
   1.113      """    
   1.114      def check_vm_ref(self, session, *args, **kwargs):
   1.115          if len(args) == 0:
   1.116 -            return {'Status': 'Failure',
   1.117 -                    'ErrorDescription': XEND_ERROR_VM_INVALID}
   1.118 +            # This will trigger a TypeError, because there aren't enough
   1.119 +            # arguments, which will be caught higher up and diagnosed.
   1.120 +            func(self, session)
   1.121 +            assert false
   1.122  
   1.123          vm_ref = args[0]
   1.124          xendom = XendDomain.instance()
   1.125 @@ -129,8 +189,7 @@ def valid_vm(func):
   1.126                 xendom.is_valid_vm(vm_ref):
   1.127              return func(self, session, *args, **kwargs)
   1.128          else:
   1.129 -            return {'Status': 'Failure',
   1.130 -                    'ErrorDescription': XEND_ERROR_VM_INVALID}
   1.131 +            return xen_api_error(['VM_HANDLE_INVALID', vm_ref])
   1.132  
   1.133      # make sure we keep the 'api' attribute
   1.134      if hasattr(func, 'api'):
   1.135 @@ -151,8 +210,7 @@ def valid_vbd(func):
   1.136                 xendom.is_valid_dev('vbd', vbd_ref):
   1.137              return func(self, session, vbd_ref, *args, **kwargs)
   1.138          else:
   1.139 -            return {'Status': 'Failure',
   1.140 -                    'ErrorDescription': XEND_ERROR_VBD_INVALID}
   1.141 +            return xen_api_error(['VBD_HANDLE_INVALID', vbd_ref])
   1.142  
   1.143      # make sure we keep the 'api' attribute
   1.144      if hasattr(func, 'api'):
   1.145 @@ -173,8 +231,7 @@ def valid_vif(func):
   1.146                 xendom.is_valid_dev('vif', vif_ref):
   1.147              return func(self, session, vif_ref, *args, **kwargs)
   1.148          else:
   1.149 -            return {'Status': 'Failure',
   1.150 -                    'ErrorDescription': XEND_ERROR_VIF_INVALID}
   1.151 +            return xen_api_error(['VIF_HANDLE_INVALID', vif_ref])
   1.152  
   1.153      # make sure we keep the 'api' attribute
   1.154      if hasattr(func, 'api'):
   1.155 @@ -196,8 +253,7 @@ def valid_vdi(func):
   1.156                 xennode.get_sr().is_valid_vdi(vdi_ref):
   1.157              return func(self, session, vdi_ref, *args, **kwargs)
   1.158          else:
   1.159 -            return {'Status': 'Failure',
   1.160 -                    'ErrorDescription': XEND_ERROR_VDI_INVALID}
   1.161 +            return xen_api_error(['VDI_HANDLE_INVALID', vdi_ref])
   1.162  
   1.163      # make sure we keep the 'api' attribute
   1.164      if hasattr(func, 'api'):
   1.165 @@ -218,8 +274,7 @@ def valid_vtpm(func):
   1.166                 xendom.is_valid_dev('vtpm', vtpm_ref):
   1.167              return func(self, session, vtpm_ref, *args, **kwargs)
   1.168          else:
   1.169 -            return {'Status': 'Failure',
   1.170 -                    'ErrorDescription': XEND_ERROR_VTPM_INVALID}
   1.171 +            return xen_api_error(['VTPM_HANDLE_INVALID', vtpm_ref])
   1.172  
   1.173      # make sure we keep the 'api' attribute
   1.174      if hasattr(func, 'api'):
   1.175 @@ -240,8 +295,7 @@ def valid_sr(func):
   1.176                 xennode.get_sr().uuid == sr_ref:
   1.177              return func(self, session, sr_ref, *args, **kwargs)
   1.178          else:
   1.179 -            return {'Status': 'Failure',
   1.180 -                    'ErrorDescription': XEND_ERROR_SR_INVALID}
   1.181 +            return xen_api_error(['SR_HANDLE_INVALID', sr_ref])
   1.182  
   1.183      # make sure we keep the 'api' attribute
   1.184      if hasattr(func, 'api'):
   1.185 @@ -274,7 +328,7 @@ class XendAPI:
   1.186      used via XMLRPCServer.
   1.187  
   1.188      All methods that need a valid session are marked with
   1.189 -    a L{XendAuthManager.session_required} decorator that will
   1.190 +    a L{session_required} decorator that will
   1.191      transparently perform the required session authentication.
   1.192  
   1.193      We need to support Python <2.4, so we use the old decorator syntax.
   1.194 @@ -291,15 +345,15 @@ class XendAPI:
   1.195          self.auth = auth
   1.196  
   1.197          classes = {
   1.198 -            'session': (session_required,),
   1.199 -            'host': (valid_host, session_required),
   1.200 -            'host_cpu': (valid_host_cpu, session_required),
   1.201 -            'VM': (valid_vm, session_required),
   1.202 -            'VBD': (valid_vbd, session_required),
   1.203 -            'VIF': (valid_vif, session_required),
   1.204 -            'VDI': (valid_vdi, session_required),
   1.205 -            'VTPM':(valid_vtpm, session_required),
   1.206 -            'SR':  (valid_sr, session_required)}
   1.207 +            'session': (session_required, catch_typeerror),
   1.208 +            'host': (valid_host, session_required, catch_typeerror),
   1.209 +            'host_cpu': (valid_host_cpu, session_required, catch_typeerror),
   1.210 +            'VM': (valid_vm, session_required, catch_typeerror),
   1.211 +            'VBD': (valid_vbd, session_required, catch_typeerror),
   1.212 +            'VIF': (valid_vif, session_required, catch_typeerror),
   1.213 +            'VDI': (valid_vdi, session_required, catch_typeerror),
   1.214 +            'VTPM':(valid_vtpm, session_required, catch_typeerror),
   1.215 +            'SR':  (valid_sr, session_required, catch_typeerror)}
   1.216          
   1.217          # Cheat methods
   1.218          # -------------
   1.219 @@ -375,7 +429,7 @@ class XendAPI:
   1.220                  func_full_name = '%s_%s' % (cls, func_name)
   1.221                  try:
   1.222                      method = getattr(XendAPI, func_full_name)
   1.223 -                    method = session_required(method)
   1.224 +                    method = catch_typeerror(session_required(method))
   1.225                      method.api = '%s.%s' % (cls, func_name)
   1.226                      setattr(XendAPI, func_full_name, method)
   1.227                  except AttributeError:
   1.228 @@ -396,14 +450,20 @@ class XendAPI:
   1.229      session_methods = ['logout']
   1.230      # session_funcs = ['login_with_password']    
   1.231  
   1.232 -    def session_login_with_password(self, username, password):
   1.233 +    def session_login_with_password(self, *args):
   1.234 +        if len(args) != 2:
   1.235 +            return xen_api_error(
   1.236 +                ['MESSAGE_PARAMETER_COUNT_MISMATCH',
   1.237 +                 'session.login_with_password', 2, len(args)])
   1.238 +        username = args[0]
   1.239 +        password = args[1]
   1.240          try:
   1.241              session = (self.auth == AUTH_NONE and
   1.242                         auth_manager().login_unconditionally(username) or
   1.243                         auth_manager().login_with_password(username, password))
   1.244              return xen_api_success(session)
   1.245          except XendError, e:
   1.246 -            return xen_api_error(XEND_ERROR_AUTHENTICATION_FAILED)
   1.247 +            return xen_api_error(['SESSION_AUTHENTICATION_FAILED'])
   1.248      session_login_with_password.api = 'session.login_with_password'
   1.249  
   1.250  
   1.251 @@ -425,7 +485,7 @@ class XendAPI:
   1.252          user = auth_manager().get_user(session)
   1.253          if user:
   1.254              return xen_api_success(user)
   1.255 -        return xen_api_error(XEND_ERROR_SESSION_INVALID)
   1.256 +        return xen_api_error(['SESSION_INVALID', session])
   1.257  
   1.258  
   1.259      # Xen API: Class User
   1.260 @@ -937,7 +997,7 @@ class XendAPI:
   1.261          dom = xendom.domain_lookup_nr(label)
   1.262          if dom:
   1.263              return xen_api_success([dom.get_uuid()])
   1.264 -        return xen_api_error(XEND_ERROR_VM_INVALID)
   1.265 +        return xen_api_success([])
   1.266      
   1.267      def VM_create(self, session, vm_struct):
   1.268          xendom = XendDomain.instance()
   1.269 @@ -949,7 +1009,7 @@ class XendAPI:
   1.270          xendom = XendDomain.instance()
   1.271          xeninfo = xendom.get_vm_by_uuid(vm_ref)
   1.272          if not xeninfo:
   1.273 -            return xen_api_error(XEND_ERROR_VM_INVALID)
   1.274 +            return xen_api_error(['VM_HANDLE_INVALID', vm_ref])
   1.275          
   1.276          record = {
   1.277              'uuid': xeninfo.get_uuid(),
   1.278 @@ -1051,10 +1111,10 @@ class XendAPI:
   1.279          xendom = XendDomain.instance()
   1.280          vm = xendom.get_vm_with_dev_uuid('vbd', vbd_ref)
   1.281          if not vm:
   1.282 -            return xen_api_error(XEND_ERROR_VBD_INVALID)
   1.283 +            return xen_api_error(['VBD_HANDLE_INVALID', vbd_ref])
   1.284          cfg = vm.get_dev_xenapi_config('vbd', vbd_ref)
   1.285          if not cfg:
   1.286 -            return xen_api_error(XEND_ERROR_VBD_INVALID)
   1.287 +            return xen_api_error(['VBD_HANDLE_INVALID', vbd_ref])
   1.288  
   1.289          valid_vbd_keys = self.VBD_attr_ro + self.VBD_attr_rw + \
   1.290                           self.Base_attr_ro + self.Base_attr_rw
   1.291 @@ -1073,7 +1133,7 @@ class XendAPI:
   1.292      def VBD_create(self, session, vbd_struct):
   1.293          xendom = XendDomain.instance()
   1.294          if not xendom.is_valid_vm(vbd_struct['VM']):
   1.295 -            return xen_api_error(XEND_ERROR_DOMAIN_INVALID)
   1.296 +            return xen_api_error(['VM_HANDLE_INVALID', vbd_struct['VM']])
   1.297          
   1.298          dom = xendom.get_vm_by_uuid(vbd_struct['VM'])
   1.299          vbd_ref = ''
   1.300 @@ -1087,7 +1147,7 @@ class XendAPI:
   1.301                  sr = XendNode.instance().get_sr()
   1.302                  vdi_image = sr.xen_api_get_by_uuid(vdi_ref)
   1.303                  if not vdi_image:
   1.304 -                    return xen_api_error(XEND_ERROR_VDI_INVALID)
   1.305 +                    return xen_api_error(['VDI_HANDLE_INVALID', vdi_ref])
   1.306                  vdi_image = vdi_image.qcow_path
   1.307                  vbd_ref = dom.create_vbd_with_vdi(vbd_struct, vdi_image)
   1.308          except XendError:
   1.309 @@ -1137,10 +1197,10 @@ class XendAPI:
   1.310          xendom = XendDomain.instance()
   1.311          vm = xendom.get_vm_with_dev_uuid('vif', vif_ref)
   1.312          if not vm:
   1.313 -            return xen_api_error(XEND_ERROR_VIF_INVALID)
   1.314 +            return xen_api_error(['VIF_HANDLE_INVALID', vif_ref])
   1.315          cfg = vm.get_dev_xenapi_config('vif', vif_ref)
   1.316          if not cfg:
   1.317 -            return xen_api_error(XEND_ERROR_VIF_INVALID)
   1.318 +            return xen_api_error(['VIF_HANDLE_INVALID', vif_ref])
   1.319          
   1.320          valid_vif_keys = self.VIF_attr_ro + self.VIF_attr_rw + \
   1.321                           self.Base_attr_ro + self.Base_attr_rw
   1.322 @@ -1164,7 +1224,7 @@ class XendAPI:
   1.323              except XendError:
   1.324                  return xen_api_error(XEND_ERROR_TODO)
   1.325          else:
   1.326 -            return xen_api_error(XEND_ERROR_DOMAIN_INVALID)
   1.327 +            return xen_api_error(['VM_HANDLE_INVALID', vif_struct['VM']])
   1.328  
   1.329  
   1.330      # Xen API: Class VDI
   1.331 @@ -1295,14 +1355,14 @@ class XendAPI:
   1.332                  'read_only': image.read_only,
   1.333                  })
   1.334  
   1.335 -        return xen_api_error(XEND_ERROR_VDI_INVALID)
   1.336 +        return xen_api_error(['VDI_HANDLE_INVALID', vdi_ref])
   1.337  
   1.338      # Class Functions    
   1.339      def VDI_create(self, session, vdi_struct):
   1.340          sr = XendNode.instance().get_sr()
   1.341          sr_ref = vdi_struct['SR']
   1.342          if sr.uuid != sr_ref:
   1.343 -            return xen_api_error(XEND_ERROR_SR_INVALID)
   1.344 +            return xen_api_error(['SR_HANDLE_INVALID', vdi_struct['SR']])
   1.345  
   1.346          vdi_uuid = sr.create_image(vdi_struct)
   1.347          return xen_api_success(vdi_uuid)
   1.348 @@ -1315,9 +1375,8 @@ class XendAPI:
   1.349          sr = XendNode.instance().get_sr()
   1.350          image_uuid = sr.xen_api_get_by_name_label(name)
   1.351          if image_uuid:
   1.352 -            return xen_api_success(image_uuid)
   1.353 -        
   1.354 -        return xen_api_error(XEND_ERROR_VDI_INVALID)
   1.355 +            return xen_api_success([image_uuid])
   1.356 +        return xen_api_success([])
   1.357  
   1.358  
   1.359      # Xen API: Class VTPM
   1.360 @@ -1336,10 +1395,10 @@ class XendAPI:
   1.361          xendom = XendDomain.instance()
   1.362          vm = xendom.get_vm_with_dev_uuid('vtpm', vtpm_ref)
   1.363          if not vm:
   1.364 -            return xen_api_error(XEND_ERROR_VTPM_INVALID)
   1.365 +            return xen_api_error(['VTPM_HANDLE_INVALID', vtpm_ref])
   1.366          cfg = vm.get_dev_xenapi_config('vtpm', vtpm_ref)
   1.367          if not cfg:
   1.368 -            return xen_api_error(XEND_ERROR_VTPM_INVALID)
   1.369 +            return xen_api_error(['VTPM_HANDLE_INVALID', vtpm_ref])
   1.370          valid_vtpm_keys = self.VTPM_attr_ro + self.VTPM_attr_rw + \
   1.371                            self.Base_attr_ro + self.Base_attr_rw
   1.372          for k in cfg.keys():
   1.373 @@ -1353,10 +1412,10 @@ class XendAPI:
   1.374          xendom = XendDomain.instance()
   1.375          vm = xendom.get_vm_with_dev_uuid('vtpm', vtpm_ref)
   1.376          if not vm:
   1.377 -            return xen_api_error(XEND_ERROR_VTPM_INVALID)
   1.378 +            return xen_api_error(['VTPM_HANDLE_INVALID', vtpm_ref])
   1.379          cfg = vm.get_dev_xenapi_config('vtpm', vtpm_ref)
   1.380          if not cfg:
   1.381 -            return xen_api_error(XEND_ERROR_VTPM_INVALID)
   1.382 +            return xen_api_error(['VTPM_HANDLE_INVALID', vtpm_ref])
   1.383          if cfg.has_key('instance'):
   1.384              instance = cfg['instance']
   1.385          else:
   1.386 @@ -1367,10 +1426,10 @@ class XendAPI:
   1.387          xendom = XendDomain.instance()
   1.388          vm = xendom.get_vm_with_dev_uuid('vtpm', vtpm_ref)
   1.389          if not vm:
   1.390 -            return xen_api_error(XEND_ERROR_VTPM_INVALID)
   1.391 +            return xen_api_error(['VTPM_HANDLE_INVALID', vtpm_ref])
   1.392          cfg = vm.get_dev_xenapi_config('vtpm', vtpm_ref)
   1.393          if not cfg:
   1.394 -            return xen_api_error(XEND_ERROR_VTPM_INVALID)
   1.395 +            return xen_api_error(['VTPM_HANDLE_INVALID', vtpm_ref])
   1.396          if cfg.has_key('type'):
   1.397              driver = cfg['type']
   1.398          else:
   1.399 @@ -1381,10 +1440,10 @@ class XendAPI:
   1.400          xendom = XendDomain.instance()
   1.401          vm = xendom.get_vm_with_dev_uuid('vtpm', vtpm_ref)
   1.402          if not vm:
   1.403 -            return xen_api_error(XEND_ERROR_VTPM_INVALID)
   1.404 +            return xen_api_error(['VTPM_HANDLE_INVALID', vtpm_ref])
   1.405          cfg = vm.get_dev_xenapi_config('vtpm', vtpm_ref)
   1.406          if not cfg:
   1.407 -            return xen_api_error(XEND_ERROR_VTPM_INVALID)
   1.408 +            return xen_api_error(['VTPM_HANDLE_INVALID', vtpm_ref])
   1.409          if cfg.has_key('backend'):
   1.410              backend = cfg['backend']
   1.411          else:
   1.412 @@ -1407,7 +1466,7 @@ class XendAPI:
   1.413              except XendError:
   1.414                  return xen_api_error(XEND_ERROR_TODO)
   1.415          else:
   1.416 -            return xen_api_error(XEND_ERROR_DOMAIN_INVALID)
   1.417 +            return xen_api_error(['VM_HANDLE_INVALID', vtpm_struct['VM']])
   1.418  
   1.419  
   1.420      # Xen API: Class SR
   1.421 @@ -1439,7 +1498,7 @@ class XendAPI:
   1.422      def SR_get_by_name_label(self, session, label):
   1.423          sr = XendNode.instance().get_sr()
   1.424          if sr.name_label != label:
   1.425 -            return xen_api_error(XEND_ERROR_SR_INVALID)
   1.426 +            return xen_api_success([])
   1.427          return xen_api_success([sr.uuid])
   1.428  
   1.429      def SR_create(self, session):
     2.1 --- a/tools/python/xen/xend/XendAuthSessions.py	Thu Dec 21 13:12:09 2006 +0000
     2.2 +++ b/tools/python/xen/xend/XendAuthSessions.py	Thu Dec 21 15:16:25 2006 +0000
     2.3 @@ -130,16 +130,3 @@ def instance():
     2.4          inst = XendAuthSessions()
     2.5          inst.init()
     2.6      return inst
     2.7 -
     2.8 -# Handy Authentication Decorators
     2.9 -# -------------------------------
    2.10 -def session_required(func):
    2.11 -    def check_session(self, session, *args, **kwargs):
    2.12 -        if instance().is_session_valid(session):
    2.13 -            return func(self, session, *args, **kwargs)
    2.14 -        else:
    2.15 -            return {'Status': 'Failure',
    2.16 -                    'ErrorDescription': XEND_ERROR_SESSION_INVALID}
    2.17 -    return check_session
    2.18 -
    2.19 -