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>
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 #