1# VMware vSphere Python SDK 2# Copyright (c) 2008-2018 VMware, Inc. All Rights Reserved. 3# 4# Licensed under the Apache License, Version 2.0 (the "License"); 5# you may not use this file except in compliance with the License. 6# You may obtain a copy of the License at 7# 8# http://www.apache.org/licenses/LICENSE-2.0 9# 10# Unless required by applicable law or agreed to in writing, software 11# distributed under the License is distributed on an "AS IS" BASIS, 12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13# See the License for the specific language governing permissions and 14# limitations under the License. 15 16## VMOMI support code 17from __future__ import absolute_import 18from __future__ import with_statement # 2.5 only 19 20from six import iteritems 21from six import iterkeys 22from six import itervalues 23from six import text_type 24from six import string_types 25from six import binary_type 26from six import PY3 27from datetime import datetime 28from pyVmomi import Iso8601 29import base64 30import json 31import threading 32if PY3: 33 from functools import cmp_to_key 34 35NoneType = type(None) 36try: 37 from pyVmomi.pyVmomiSettings import allowGetSet 38 _allowGetSet = allowGetSet 39except: 40 _allowGetSet = True 41 42try: 43 from pyVmomi.pyVmomiSettings import allowCapitalizedNames 44 _allowCapitalizedNames = allowCapitalizedNames 45except: 46 _allowCapitalizedNames = True 47 48(F_LINK, 49 F_LINKABLE, 50 F_OPTIONAL, 51 F_SECRET) = [ 1<<x for x in range(4) ] 52 53BASE_VERSION = 'vmodl.version.version0' 54VERSION1 = 'vmodl.version.version1' 55 56XMLNS_XSD = "http://www.w3.org/2001/XMLSchema" 57XMLNS_XSI = "http://www.w3.org/2001/XMLSchema-instance" 58XMLNS_VMODL_BASE = "urn:vim25" 59 60# The lock ensures that we serialize the lazy loading. In particular, we need 61# to guard against multiple threads loading the same types on the same kind 62# of objects at the same time 63# The lock is protecting the following variables: 64# _topLevelNames, _*DefMap, and _dependencyMap 65_lazyLock = threading.RLock() 66 67# Also referenced in __init__.py 68_topLevelNames = set() 69 70# Maps to store parameters to create the type for each vmodlName 71_managedDefMap = {} 72_dataDefMap = {} 73_enumDefMap = {} 74 75# Map to store parameters to create the type for each wsdlName 76_wsdlDefMap = {} 77 78# Map that stores the nested classes for a given class 79# if a.b.c and a.b.d are the nested classes of a.b, then _dependencyMap[a.b] = {c,d} 80_dependencyMap = {} 81 82# Map top level names to xml namespaces 83_urnMap = {"vim": XMLNS_VMODL_BASE, "sms": "urn:sms", "pbm": "urn:pbm"} 84 85## Update the dependency map 86# Note: Must be holding the _lazyLock 87# 88# @param names VmodlName of the type 89def _AddToDependencyMap(names): 90 """ Note: Must be holding the _lazyLock """ 91 curName = names[0] 92 _topLevelNames.add(curName) 93 for name in names[1:]: 94 _dependencyMap.setdefault(curName, set()).add(name) 95 curName = ".".join([curName, name]) 96 97## Check if a particular name is dependent on another 98# Note: Must be holding the _lazyLock 99# 100# @param parent Parent Vmodl name 101# @param name Vmodl name to be checked for dependency 102# @return True, if name depends on parent, False otherwise 103def _CheckForDependency(parent, name): 104 """ Note: Must be holding the _lazyLock """ 105 if _allowCapitalizedNames: 106 # If the flag is set, check for both capitalized and 107 # uncapitalized form. This is a temporary fix for handling 108 # vim.EsxCLI namespace. 109 # Ex: If we add vim.EsxCLI.vdisk, then 110 # _dependencyMap['vim.EsxCLI'] will have value ['vdisk']. 111 # When we try to check dependency for vdisk, since the flag 112 # is set, we will uncapitalize EsxCLI and this will result 113 # in attribute error 114 dependents = _dependencyMap.get(parent) 115 if not dependents: 116 uncapitalizedParent = UncapitalizeVmodlName(parent) 117 dependents = _dependencyMap.get(uncapitalizedParent) 118 119 if dependents: 120 if name in dependents or Uncapitalize(name) in dependents: 121 return True 122 else: 123 dependents = _dependencyMap.get(parent) 124 if dependents: 125 if name in dependents: 126 return True 127 return False 128 129## Checks for the type definition in all the maps 130# and loads it if it finds the definition 131# 132# @param name vmodl name of the type 133# @return vmodl type 134def _LoadVmodlType(name): 135 isArray = name.endswith("[]") 136 if isArray: 137 name = name[:-2] 138 if _allowCapitalizedNames: 139 name = UncapitalizeVmodlName(name) 140 141 with _lazyLock: 142 for defMap, loadFn in [(_dataDefMap, LoadDataType), 143 (_managedDefMap, LoadManagedType), 144 (_enumDefMap, LoadEnumType)]: 145 dic = defMap.get(name) 146 if dic: 147 typ = loadFn(*dic) 148 return isArray and typ.Array or typ 149 150 return None 151 152# In Python 2.4 and earlier, exceptions are old-style classes, so data objects 153# must be old-style too. For 2.5 and newer, data objects must be new-style 154# classes 155if issubclass(Exception, object): 156 Base = object 157 SetAttr = object.__setattr__ 158else: 159 class Base: pass 160 def SetAttr(obj, key, val): 161 obj.__dict__[key] = val 162 163## Simple class to store named attributes 164class Object: 165 ## Constructor 166 def __init__(self, **kwargs): 167 self.__dict__.update(kwargs) 168 169# All properties and methods in vmodl types are created as LazyObject's 170# in VmomiSupport. The attributes in these properties and methods that refer 171# to vmodl types are "type", "result" and "methodResult". If a program tries 172# to access any of these attributes, load the type. The vmodl name of the type 173# can be retrieved by adding name to the attribute that is being accessed 174# Creating a derived class of Object so that programs that want to use just 175# Object are not affected 176class LazyObject(Object): 177 178 def __getattr__(self, attr): 179 with _lazyLock: 180 # Check if another thread already initialized this 181 obj = self.__dict__.get(attr) 182 if obj: 183 return obj 184 185 if attr == "type" or attr == "result" or attr == "methodResult": 186 attrName = attr + "Name" 187 vmodlName = getattr(self, attrName) 188 vmodlType = GetVmodlType(vmodlName) 189 setattr(self, attr, vmodlType) 190 delattr(self, attrName) 191 return vmodlType 192 else: 193 raise AttributeError(attr) 194 195class Link(text_type): 196 def __new__(cls, obj): 197 if isinstance(obj, string_types): 198 return text_type.__new__(cls, obj) 199 elif isinstance(obj, DataObject): 200 if obj.key: 201 return text_type.__new__(cls, obj.key) 202 raise AttributeError("DataObject does not have a key to link") 203 else: 204 raise ValueError 205 206## LazyType to wrap around actual type 207# This is used to intercept attribute accesses of a class 208# and load the appropriate nested classes on-demand 209class LazyType(type): 210 211 def __getattr__(self, attr): 212 if attr.endswith("[]"): 213 searchName = attr[:-2] 214 else: 215 searchName = attr 216 217 with _lazyLock: 218 nestedClasses = _dependencyMap.get(self.__name__, []) 219 if searchName in nestedClasses: 220 return GetVmodlType(self.__name__ + "." + attr) 221 else: 222 return super(LazyType, self).__getattribute__(attr) 223 224## LazyModule class 225# Used as a placeholder until the actual type is loaded 226# If someone wants to use the type, then it is loaded on-demand 227class LazyModule(object): 228 229 def __init__(self, name): 230 # name is used to save the current context of the object 231 # If it is created because of reference to a.b, name will 232 # be a.b 233 self.name = name 234 235 def __getattr__(self, attr): 236 # If someone tries to introspect the instance of this class 237 # using inspect.isclass(), the function will check if the object 238 # has __bases__ attr. So, throwing an AttributeError to make it work 239 if attr == "__bases__": 240 raise AttributeError 241 242 with _lazyLock: 243 # Check if we have already loaded the class or object 244 # corresponding to this attribute 245 obj = self.__dict__.get(attr) 246 if obj: 247 return obj 248 249 name = ".".join([self.name, attr]) 250 # Get the actual vmodlName from the type dictionaries 251 actualName = _GetActualName(name) 252 if actualName: 253 typeObj = GetVmodlType(actualName) 254 else: 255 if _CheckForDependency(self.name, attr): 256 typeObj = LazyModule(name) 257 elif self.name in _urnMap: 258 try: 259 typeObj = GetWsdlType(_urnMap[self.name], attr) 260 except: 261 raise AttributeError(attr) 262 else: 263 raise AttributeError(attr) 264 setattr(self, attr, typeObj) 265 return typeObj 266 267 # If the lazy module is representing a data object, 268 # this will get triggered when some code tries to initialize it 269 # Load the actual type and pass the arguments to it's init. 270 def __call__(self, **kwargs): 271 typ = _LoadVmodlType(self.name) 272 if typ: 273 return typ.__call__(**kwargs) 274 else: 275 raise AttributeError("'%s' does not exist" % self.name) 276 277 278class VmomiJSONEncoder(json.JSONEncoder): 279 """ 280 Custom JSON encoder to encode vSphere objects. 281 282 When a ManagedObject is encoded, it gains three properties: 283 _vimid is the _moId (ex: 'vm-42') 284 _vimref is the moRef (ex: 'vim.VirtualMachine:vm-42') 285 _vimtype is the class name (ex: 'vim.VirtualMachine') 286 287 When a DataObject is encoded, it gains one property: 288 _vimtype is the class name (ex: 'vim.VirtualMachineQuestionInfo') 289 290 If the dynamicProperty and dynamicType are empty, they are optionally 291 omitted from the results of DataObjects and ManagedObjects 292 293 @example "Explode only the object passed in" 294 data = json.dumps(vm, cls=VmomiJSONEncoder) 295 296 @example "Explode specific objects" 297 data = json.dumps(vm, cls=VmomiJSONEncoder, 298 explode=[vm, vm.network[0]]) 299 300 @example "Explode all virtual machines in a list and their snapshots" 301 data = json.dumps([vm1, vm2], cls=VmomiJSONEncoder, 302 explode=[templateOf('VirtualMachine'), 303 templateOf('VirtualMachineSnapshot')]) 304 """ 305 def __init__(self, *args, **kwargs): 306 """ 307 Consumer must specify what types to explode into full content 308 and what types to leave as a ManagedObjectReference. If the 309 root object of the encoding is a ManagedObject, it will be 310 added to the explode list automatically. 311 312 A ManagedObject is only exploded once, the first time it is 313 encountered. All subsequent times it will be a moRef. 314 315 @param explode (list) A list of objects and/or types to 316 expand in the conversion process. Directly add any 317 objects to the list, or add the type of any object 318 using type(templateOf('VirtualMachine')) for example. 319 320 @param strip_dynamic (bool) Every object normally contains 321 a dynamicProperty list and a dynamicType field even if 322 the contents are [] and None respectively. These fields 323 clutter the output making it more difficult for a person 324 to read and bloat the size of the result unnecessarily. 325 This option removes them if they are the empty values above. 326 The default is False. 327 """ 328 self._done = set() 329 self._first = True 330 self._explode = kwargs.pop('explode', None) 331 self._strip_dynamic = kwargs.pop('strip_dynamic', False) 332 super(VmomiJSONEncoder, self).__init__(*args, **kwargs) 333 334 def _remove_empty_dynamic_if(self, result): 335 if self._strip_dynamic: 336 if 'dynamicProperty' in result and len(result['dynamicProperty']) == 0: 337 result.pop('dynamicProperty') 338 if 'dynamicType' in result and not result['dynamicType']: 339 result.pop('dynamicType') 340 return result 341 342 def default(self, obj): # pylint: disable=method-hidden 343 if self._first: 344 self._first = False 345 if not self._explode: 346 self._explode = [] 347 if isinstance(obj, ManagedObject): 348 self._explode.append(obj) 349 if isinstance(obj, ManagedObject): 350 if self.explode(obj): 351 result = {} 352 result['_vimid'] = obj._moId 353 result['_vimref'] = '{}:{}'.format(obj.__class__.__name__, 354 obj._moId) 355 result['_vimtype'] = obj.__class__.__name__ 356 for prop in obj._GetPropertyList(): 357 result[prop.name] = getattr(obj, prop.name) 358 return self._remove_empty_dynamic_if(result) 359 return str(obj).strip("'") # see FormatObject below 360 if isinstance(obj, DataObject): 361 result = {k:v for k,v in obj.__dict__.items()} 362 result['_vimtype'] = obj.__class__.__name__ 363 return self._remove_empty_dynamic_if(result) 364 if isinstance(obj, binary): 365 result = base64.b64encode(obj) 366 if PY3: # see FormatObject below 367 result = str(result, 'utf-8') 368 return result 369 if isinstance(obj, datetime): 370 return Iso8601.ISO8601Format(obj) 371 if isinstance(obj, UncallableManagedMethod): 372 return obj.name 373 if isinstance(obj, ManagedMethod): 374 return str(obj) # see FormatObject below 375 if isinstance(obj, type): 376 return obj.__name__ 377 super(VmomiJSONEncoder, self).default(obj) 378 379 def explode(self, obj): 380 """ Determine if the object should be exploded. """ 381 if obj in self._done: 382 return False 383 result = False 384 for item in self._explode: 385 if hasattr(item, '_moId'): 386 # If it has a _moId it is an instance 387 if obj._moId == item._moId: 388 result = True 389 else: 390 # If it does not have a _moId it is a template 391 if obj.__class__.__name__ == item.__name__: 392 result = True 393 if result: 394 self._done.add(obj) 395 return result 396 397 398def templateOf(typestr): 399 """ Returns a class template. """ 400 return GetWsdlType(XMLNS_VMODL_BASE, typestr) 401 402## Format a python VMOMI object 403# 404# @param val the object 405# @param info the field 406# @param indent the level of indentation 407# @return the formatted string 408def FormatObject(val, info=Object(name="", type=object, flags=0), indent=0): 409 start = indent * " " + (info.name and "%s = " % info.name or "") 410 if val == None: 411 result = "<unset>" 412 elif isinstance(val, DataObject): 413 if info.flags & F_LINK: 414 result = "<%s:%s>" % (val.__class__.__name__, val.key) 415 else: 416 result = "(%s) {\n%s\n%s}" % (val.__class__.__name__, 417 ',\n'.join([FormatObject(getattr(val, prop.name), prop, indent+1) 418 for prop in val._GetPropertyList()]), indent * " ") 419 elif isinstance(val, ManagedObject): 420 if val._serverGuid is None: 421 result = "'%s:%s'" % (val.__class__.__name__, val._moId) 422 else: 423 result = "'%s:%s:%s'" % (val.__class__.__name__, val._serverGuid, 424 val._moId) 425 elif isinstance(val, list): 426 itemType = getattr(val, 'Item', getattr(info.type, 'Item', object)) 427 if val: 428 item = Object(name="", type=itemType, flags=info.flags) 429 result = "(%s) [\n%s\n%s]" % (itemType.__name__, 430 ',\n'.join([FormatObject(obj, item, indent+1) for obj in val]), 431 indent * " ") 432 else: 433 result = "(%s) []" % itemType.__name__ 434 elif isinstance(val, type): 435 result = val.__name__ 436 elif isinstance(val, UncallableManagedMethod): 437 result = val.name 438 elif isinstance(val, ManagedMethod): 439 result = '%s.%s' % (val.info.type.__name__, val.info.name) 440 elif isinstance(val, bool): 441 result = val and "true" or "false" 442 elif isinstance(val, datetime): 443 result = Iso8601.ISO8601Format(val) 444 elif isinstance(val, binary): 445 result = base64.b64encode(val) 446 if PY3: 447 # In python3 the bytes result after the base64 encoding has a 448 # leading 'b' which causes error when we use it to construct the 449 # soap message. Workaround the issue by converting the result to 450 # string. Since the result of base64 encoding contains only subset 451 # of ASCII chars, converting to string will not change the value. 452 result = str(result, 'utf-8') 453 else: 454 result = repr(val) 455 return start + result 456 457## Lookup a property for a given object type 458# 459# @param type the type 460# @param name the name of the property 461def GetPropertyInfo(type, name): 462 try: 463 while name not in type._propInfo: 464 type = type.__bases__[0] 465 else: 466 return type._propInfo[name] 467 except Exception: 468 raise AttributeError(name) 469 470## VMOMI Managed Object class 471class ManagedObject(object): 472 _wsdlName = "ManagedObject" 473 _propInfo = {} 474 _propList = [] 475 _methodInfo = {} 476 _version = BASE_VERSION 477 478 ## Constructor 479 # 480 # @param[in] self self 481 # @param[in] moId The ID of this managed object 482 # @param[in] stub The stub adapter, if this is a client stub object 483 def __init__(self, moId, stub=None, serverGuid=None): 484 object.__setattr__(self, "_moId", moId) 485 object.__setattr__(self, "_stub", stub) 486 object.__setattr__(self, "_serverGuid", serverGuid) 487 488 ## Invoke a managed method 489 # 490 # @param info method info 491 # @param self self 492 # @param ... arguments 493 def _InvokeMethod(info, self, *posargs, **kwargs): 494 if len(posargs) > len(info.params): 495 s = "s"*(len(info.params)!=1) 496 raise TypeError("%s() takes at most %d argument%s (%d given)" % 497 (Capitalize(info.name), len(info.params), s, len(posargs))) 498 args = list(posargs) + [None] * (len(info.params) - len(posargs)) 499 if len(kwargs) > 0: 500 paramNames = [param.name for param in info.params] 501 for (k, v) in list(kwargs.items()): 502 try: 503 idx = paramNames.index(k) 504 except ValueError: 505 raise TypeError("%s() got an unexpected keyword argument '%s'" % 506 (Capitalize(info.name), k)) 507 if idx < len(posargs): 508 raise TypeError("%s() got multiple values for keyword argument '%s'" % 509 (Capitalize(info.name), k)) 510 args[idx] = v 511 list(map(CheckField, info.params, args)) 512 return self._stub.InvokeMethod(self, info, args) 513 _InvokeMethod = staticmethod(_InvokeMethod) 514 515 ## Invoke a managed property accessor 516 # 517 # @param info property info 518 # @param self self 519 def _InvokeAccessor(info, self): 520 return self._stub.InvokeAccessor(self, info) 521 _InvokeAccessor = staticmethod(_InvokeAccessor) 522 523 ## Get the ID of a managed object 524 def _GetMoId(self): 525 return self._moId 526 527 ## Get the serverGuid of a managed object 528 def _GetServerGuid(self): 529 return self._serverGuid 530 531 ## Get the stub of a managed object 532 def _GetStub(self): 533 return self._stub 534 535 ## Get a list of all properties of this type and base types 536 # 537 # @param cls The managed object type 538 def _GetPropertyList(cls, includeBaseClassProps=True): 539 if not includeBaseClassProps: 540 return cls._propList 541 prop = {} 542 result = [] 543 while cls != ManagedObject: 544 # Iterate through props, add info for prop not found in derived class 545 result = [info for info in cls._propList 546 if prop.setdefault(info.name, cls) == cls] + result 547 cls = cls.__bases__[0] 548 return result 549 _GetPropertyList = classmethod(_GetPropertyList) 550 551 ## Get a list of all methods of this type and base types 552 # 553 # @param cls The managed object type 554 def _GetMethodList(cls): 555 meth = {} 556 result = [] 557 while cls != ManagedObject: 558 # Iterate through methods, add info for method not found in derived class 559 result = [info for info in list(cls._methodInfo.values()) 560 if meth.setdefault(info.name, cls) == cls] + result 561 cls = cls.__bases__[0] 562 return result 563 _GetMethodList = classmethod(_GetMethodList) 564 565 ## Lookup a method for a given managed object type 566 # 567 # @param type the type 568 # @param name the name of the property 569 def _GetMethodInfo(type, name): 570 while hasattr(type, "_methodInfo"): 571 try: 572 return type._methodInfo[name] 573 except KeyError: 574 type = type.__bases__[0] 575 raise AttributeError(name) 576 _GetMethodInfo = classmethod(_GetMethodInfo) 577 578 def __setattr__(self,*args): 579 if self._stub is not None: 580 raise Exception("Managed object attributes are read-only") 581 else: 582 object.__setattr__(self,*args) 583 __delattr__ = __setattr__ 584 585 if _allowGetSet == True: 586 def __getattr__(self, name): 587 if name.startswith("Get"): 588 return lambda : getattr(self, name[3].lower() + name[4:]) 589 elif name.startswith("Set"): 590 return lambda val: setattr(self, name[3].lower() + name[4:], val) 591 raise AttributeError(name) 592 593 ## The equality test of ManagedObject is for client side only and might 594 # not be appropriate for server side objects. The server side object has 595 # to override this function if it has a different equality logic 596 def __eq__(self, other): 597 if other is None: 598 return False 599 else: 600 return self._moId == other._moId and \ 601 self.__class__ == other.__class__ and \ 602 self._serverGuid == other._serverGuid 603 604 def __ne__(self, other): 605 return not(self == other) 606 607 def __hash__(self): 608 return str(self).__hash__() 609 610 __str__ = __repr__ = FormatObject 611 _GetPropertyInfo = classmethod(GetPropertyInfo) 612 613## VMOMI Data Object class 614class DataObject(Base): 615 _wsdlName = "DataObject" 616 _propInfo = {} 617 _propList = [] 618 _version = BASE_VERSION 619 620 ## Constructor 621 # 622 # @param info property info 623 # @param ... keyword arguments indicate properties 624 def __init__(self, **kwargs): 625 for info in self._GetPropertyList(): 626 if issubclass(info.type, list): 627 SetAttr(self, info.name, info.type()) 628 elif info.flags & F_OPTIONAL: 629 SetAttr(self, info.name, None) 630 elif info.type is bool: 631 SetAttr(self, info.name, False) 632 elif issubclass(info.type, Enum): 633 SetAttr(self, info.name, None) 634 elif issubclass(info.type, str): 635 SetAttr(self, info.name, "") 636 elif info.type is long or \ 637 issubclass(info.type, int) or \ 638 issubclass(info.type, float): 639 # Take care of byte, short, int, long, float and double 640 SetAttr(self, info.name, info.type(0)) 641 else: 642 SetAttr(self, info.name, None) 643 for (k, v) in list(kwargs.items()): 644 setattr(self, k, v) 645 646 ## Get a list of all properties of this type and base types 647 # 648 # @param cls the data object type 649 def _GetPropertyList(cls, includeBaseClassProps=True): 650 if not includeBaseClassProps: 651 return cls._propList 652 prop = {} 653 result = [] 654 while cls != DataObject: 655 # Iterate through props, add info for prop not found in derived class 656 result = [info for info in cls._propList 657 if prop.setdefault(info.name, cls) == cls] + result 658 cls = cls.__bases__[0] 659 return result 660 _GetPropertyList = classmethod(_GetPropertyList) 661 662 def __setattr__(self, name, val): 663 CheckField(self._GetPropertyInfo(name), val) 664 SetAttr(self, name, val) 665 666 if _allowGetSet == True: 667 def __getattr__(self, name): 668 if name.startswith("Get"): 669 return lambda : getattr(self, name[3].lower() + name[4:]) 670 elif name.startswith("Set"): 671 return lambda val: setattr(self, name[3].lower() + name[4:], val) 672 raise AttributeError(name) 673 674 _GetPropertyInfo = classmethod(GetPropertyInfo) 675 __str__ = __repr__ = FormatObject 676 677## Base class for enum types 678class Enum(str): pass 679 680## Base class for array types 681class Array(list): 682 __str__ = __repr__ = FormatObject 683 684## Class for curried function objects 685# 686# Instances of this class are curried python function objects. 687# If g = Curry(f, a1,...,an), then g(b1,...,bm) = f(a1,...,an, b1,...,bm) 688class Curry(object): 689 ## Constructor 690 # 691 # @param self self 692 # @param f the function object 693 # @param args arguments to fix 694 def __init__(self, f, *args): 695 self.f = f 696 self.args = args 697 698 def __call__(self, *args, **kwargs): 699 args = self.args + args 700 return self.f(*args, **kwargs) 701 702 def __get__(self, obj, type): 703 if obj: 704 # curried methods will receive 'self' *after* any fixed arguments 705 return lambda *args, **kwargs: \ 706 self.f(*(self.args + (obj,) + args), **kwargs) 707 return self 708 709## Class for managed object methods 710class ManagedMethod(Curry): 711 ## Constructor 712 # 713 # @param self self 714 # @param info method info 715 def __init__(self, info): 716 Curry.__init__(self, ManagedObject._InvokeMethod, info) 717 self.info = info 718 719# Method used to represent any unknown wsdl method returned by server response. 720# Server may return unknown method name due to server defects or newer version. 721class UncallableManagedMethod(ManagedMethod): 722 def __init__(self, name): 723 self.name = name 724 725 def __call__(self, *args, **kwargs): 726 raise Exception("Managed method {} is not available".format(self.name)) 727 728## Create the vmodl.MethodFault type 729# 730# This type must be generated dynamically because it inherits from 731# vmodl.DynamicData, which is created dynamically by the emitted code. 732# 733# @return the new type 734def CreateAndLoadMethodFaultType(): 735 with _lazyLock: 736 props = [["msg", "string", BASE_VERSION, F_OPTIONAL], 737 ["faultCause", "vmodl.MethodFault", "vmodl.version.version1", F_OPTIONAL], 738 ["faultMessage", "vmodl.LocalizableMessage[]", "vmodl.version.version1", F_OPTIONAL]] 739 propInfo = {} 740 propList = [ LazyObject(name=p[0], typeName=p[1], version=p[2], flags=p[3]) 741 for p in props ] 742 dic = {"_wsdlName" : "MethodFault", "_propInfo" : propInfo, 743 "_propList" : propList, "_version" : BASE_VERSION} 744 for info in propList: 745 propInfo[info.name] = info 746 name = "vmodl.MethodFault" 747 CreateDataType("vmodl.MethodFault", "MethodFault", "vmodl.DynamicData", BASE_VERSION, props) 748 return _AddType(type(Exception)(name, 749 (GetWsdlType(XMLNS_VMODL_BASE, "DynamicData"), Exception), 750 dic)) 751 752# If the name of nested class of vmodl type is same as one of the nested classes 753# of its parent, then we have to replace it. Else it won't be possible to intercept 754# it with LazyType class 755# @param vmodl type 756# @param parent of the vmodl type 757# @return vmodl type 758def _CheckNestedClasses(typ, parent): 759 with _lazyLock: 760 vmodlName = typ.__name__ 761 nestedClasses = _dependencyMap.get(vmodlName, []) 762 for nestedClass in nestedClasses: 763 if hasattr(parent, nestedClass): 764 setattr(typ, nestedClass, GetVmodlType(vmodlName + "." + nestedClass)) 765 return typ 766 767## Create and Load a data object type at once 768# 769# @param vmodlName the VMODL name of the type 770# @param wsdlName the WSDL name of the type 771# @param parent the VMODL name of the parent type 772# @param version the version of the type 773# @param props properties of the type 774# @return vmodl type 775def CreateAndLoadDataType(vmodlName, wsdlName, parent, version, props): 776 CreateDataType(vmodlName, wsdlName, parent, version, props) 777 return LoadDataType(vmodlName, wsdlName, parent, version, props) 778 779## Create a data object type 780# 781# @param vmodlName the VMODL name of the type 782# @param wsdlName the WSDL name of the type 783# @param parent the VMODL name of the parent type 784# @param version the version of the type 785# @param props properties of the type 786def CreateDataType(vmodlName, wsdlName, parent, version, props): 787 with _lazyLock: 788 dic = [vmodlName, wsdlName, parent, version, props] 789 names = vmodlName.split(".") 790 if _allowCapitalizedNames: 791 vmodlName = ".".join(name[0].lower() + name[1:] for name in names) 792 _AddToDependencyMap(names) 793 typeNs = GetWsdlNamespace(version) 794 795 _dataDefMap[vmodlName] = dic 796 _wsdlDefMap[(typeNs, wsdlName)] = dic 797 _wsdlTypeMapNSs.add(typeNs) 798 799## Load a data object type 800# This function also loads the parent of the type if it's not loaded yet 801# 802# @param vmodlName the VMODL name of the type 803# @param wsdlName the WSDL name of the type 804# @param parent the VMODL name of the parent type 805# @param version the version of the type 806# @param props properties of the type 807# @return the new data object type 808def LoadDataType(vmodlName, wsdlName, parent, version, props): 809 with _lazyLock: 810 # Empty lists are saved as None in globals maps as it is much more memory 811 # efficient. PythonStubEmitter files emit empty lists as None. 812 if props is None: 813 props = [] 814 propInfo = {} 815 propList = [] 816 for p in props: 817 # DataObject Property does not contain the privilege for emitted types. 818 # However, DynamicTypeConstructor from DynamicTypeManagerHelper.py creates 819 # DataTypes with properties containing privilege id. 820 name, typeName, propVersion, flags = p[:4] 821 if flags & F_LINK: 822 if typeName.endswith("[]"): 823 linkType = "Link[]" 824 else: 825 linkType = "Link" 826 obj = LazyObject(name=name, typeName=linkType, version=propVersion, 827 flags=flags, expectedType=typeName) 828 else: 829 obj = LazyObject(name=name, typeName=typeName, version=propVersion, 830 flags=flags) 831 propList.append(obj) 832 dic = {"_wsdlName" : wsdlName, "_propInfo" : propInfo, 833 "_propList" : propList, "_version" : version} 834 for info in propList: 835 propInfo[info.name] = info 836 name = vmodlName 837 parent = GetVmodlType(parent) 838 result = _AddType(LazyType(name, (parent,), dic)) 839 840 # MethodFault and RuntimeFault are builtin types, but MethodFault extends 841 # DynamicData, which is (erroneously?) an emitted type, so we can't create 842 # MethodFault and RuntimeFault until we have loaded DynamicData 843 if wsdlName == "DynamicData": 844 CreateAndLoadMethodFaultType() 845 CreateAndLoadDataType("vmodl.RuntimeFault", "RuntimeFault", 846 "vmodl.MethodFault", BASE_VERSION, []) 847 # Strictly speaking LocalizedMethodFault is not a data object type 848 # (it can't be used in VMODL) But it can be treated as a data object for 849 # (de)serialization purpose 850 CreateAndLoadDataType("vmodl.LocalizedMethodFault", "LocalizedMethodFault", 851 "vmodl.MethodFault", BASE_VERSION, 852 [("fault", "vmodl.MethodFault", BASE_VERSION, 0), 853 ("localizedMessage", "string", BASE_VERSION, F_OPTIONAL), 854 ]) 855 856 return _CheckNestedClasses(result, parent) 857 858## Create and Load a managed object type at once 859# 860# @param vmodlName the VMODL name of the type 861# @param wsdlName the WSDL name of the type 862# @param parent the VMODL name of the parent type 863# @param version the version of the type 864# @param props properties of the type 865# @param methods methods of the type 866# @return vmodl type 867def CreateAndLoadManagedType(vmodlName, wsdlName, parent, version, props, methods): 868 CreateManagedType(vmodlName, wsdlName, parent, version, props, methods) 869 return LoadManagedType(vmodlName, wsdlName, parent, version, props, methods) 870 871## Create a managed object type 872# 873# @param vmodlName the VMODL name of the type 874# @param wsdlName the WSDL name of the type 875# @param parent the VMODL name of the parent type 876# @param version the version of the type 877# @param props properties of the type 878# @param methods methods of the type 879def CreateManagedType(vmodlName, wsdlName, parent, version, props, methods): 880 with _lazyLock: 881 dic = [vmodlName, wsdlName, parent, version, props, methods] 882 names = vmodlName.split(".") 883 if _allowCapitalizedNames: 884 vmodlName = ".".join(name[0].lower() + name[1:] for name in names) 885 886 _AddToDependencyMap(names) 887 typeNs = GetWsdlNamespace(version) 888 889 if methods: 890 for meth in methods: 891 _SetWsdlMethod(typeNs, meth[1], dic) 892 893 _managedDefMap[vmodlName] = dic 894 _wsdlDefMap[(typeNs, wsdlName)] = dic 895 _wsdlTypeMapNSs.add(typeNs) 896 897## Load a managed object type 898# This function also loads the parent of the type if it's not loaded yet 899# 900# @param vmodlName the VMODL name of the type 901# @param wsdlName the WSDL name of the type 902# @param parent the VMODL name of the parent type 903# @param version the version of the type 904# @param props properties of the type 905# @param methods methods of the type 906# @return the new managed object type 907def LoadManagedType(vmodlName, wsdlName, parent, version, props, methods): 908 with _lazyLock: 909 # Empty lists are saved as None in globals maps as it is much more memory 910 # efficient. PythonStubEmitter files emit empty lists as None. 911 if props is None: 912 props = [] 913 if methods is None: 914 methods = [] 915 parent = GetVmodlType(parent) 916 propInfo = {} 917 methodInfo = {} 918 propList = [LazyObject(name=p[0], typeName=p[1], version=p[2], flags=p[3], 919 privId=p[4]) for p in props] 920 dic = {"_wsdlName" : wsdlName, "_propInfo" : propInfo, 921 "_propList" : propList, 922 "_methodInfo" : methodInfo, "_version" : version} 923 for info in propList: 924 propInfo[info.name] = info 925 getter = Curry(ManagedObject._InvokeAccessor, info) 926 dic[info.name] = property(getter) 927 for (mVmodl, mWsdl, mVersion, mParams, mResult, mPrivilege, mFaults) in methods: 928 if mFaults is None: 929 mFaults = [] 930 mName = Capitalize(mVmodl) 931 isTask = False 932 if mName.endswith("_Task"): 933 mName = mName[:-5] 934 isTask = True 935 params = tuple([LazyObject(name=p[0], typeName=p[1], version=p[2], flags=p[3], 936 privId=p[4]) for p in mParams]) 937 info = LazyObject(name=mName, typeName=vmodlName, wsdlName=mWsdl, 938 version=mVersion, params=params, isTask=isTask, 939 resultFlags=mResult[0], resultName=mResult[1], 940 methodResultName=mResult[2], privId=mPrivilege, faults=mFaults) 941 methodInfo[mName] = info 942 mm = ManagedMethod(info) 943 ns = GetWsdlNamespace(info.version) 944 method = _SetWsdlMethod(ns, info.wsdlName, mm) 945 if method != mm: 946 raise RuntimeError( 947 "Duplicate wsdl method %s %s (new class %s vs existing %s)" % \ 948 (ns, info.wsdlName, mm.info.type, method.info.type)) 949 dic[mWsdl] = mm 950 dic[mName] = mm 951 name = vmodlName 952 result = _AddType(LazyType(name, (parent,) , dic)) 953 954 return _CheckNestedClasses(result, parent) 955 956## Create an enum type 957# 958# @param vmodlName the VMODL name of the type 959# @param wsdlName the WSDL name of the type 960# @param version the version of the type 961# @param values enum values 962# @return vmodl type 963def CreateAndLoadEnumType(vmodlName, wsdlName, version, values): 964 CreateEnumType(vmodlName, wsdlName, version, values) 965 return LoadEnumType(vmodlName, wsdlName, version, values) 966 967## Create an enum type 968# 969# @param vmodlName the VMODL name of the type 970# @param wsdlName the WSDL name of the type 971# @param version the version of the type 972# @param values enum values 973def CreateEnumType(vmodlName, wsdlName, version, values): 974 with _lazyLock: 975 dic = [vmodlName, wsdlName, version, values] 976 names = vmodlName.split(".") 977 if _allowCapitalizedNames: 978 vmodlName = ".".join(name[0].lower() + name[1:] for name in names) 979 980 _AddToDependencyMap(names) 981 typeNs = GetWsdlNamespace(version) 982 983 _enumDefMap[vmodlName] = dic 984 _wsdlDefMap[(typeNs, wsdlName)] = dic 985 _wsdlTypeMapNSs.add(typeNs) 986 987## Load an enum type 988# 989# @param vmodlName the VMODL name of the type 990# @param wsdlName the WSDL name of the type 991# @param version the version of the type 992# @param values enum values 993# @return the new enum type 994def LoadEnumType(vmodlName, wsdlName, version, values): 995 with _lazyLock: 996 name = vmodlName 997 # Enum type cannot have nested classes. So, creating normal type 998 # instead of LazyType 999 result = type(name, (Enum,), 1000 {"_wsdlName" : wsdlName, "_version" : version}) 1001 result.values = map(result, values) 1002 for value in result.values: 1003 setattr(result, value, value) 1004 return _AddType(result) 1005 1006## Create an array type 1007# 1008# @param itemType the item type 1009# @return the new array type 1010def CreateArrayType(itemType): 1011 return type("%s[]" % itemType.__name__, (Array,), {'Item' : itemType}) 1012 1013## Add a new type to the type maps, create array constructors 1014# Note: Must be holding the _lazyLock, or in main init path 1015# 1016# @param type the type object 1017# @return type 1018def _AddType(type): 1019 """ Note: Must be holding the _lazyLock, or in main init path """ 1020 type.Array = CreateArrayType(type) 1021 1022 typeNS = GetWsdlNamespace(type._version) 1023 newType = _SetWsdlType(typeNS, type._wsdlName, type) 1024 if newType != type: 1025 raise RuntimeError("Duplicate wsdl type %s (already in typemap)" % (type._wsdlName)) 1026 1027 return type 1028 1029## Check that a value matches a given type, and annotate if neccesary 1030# 1031# @param info object containing of expected type 1032# @param val object to check 1033# @throw TypeError if the value does not match the type 1034def CheckField(info, val): 1035 with _lazyLock: 1036 valType = Type(val) 1037 if val is None or (isinstance(val, list) and len(val) == 0): 1038 # If type of the property is an Any. We should allow this to have 1039 # unset items 1040 if not (info.flags & F_OPTIONAL) and info.type is not object: 1041 raise TypeError('Required field "%s" not provided (not @optional)' % info.name) 1042 return 1043 elif info.type is object: 1044 try: 1045 GetQualifiedWsdlName(valType) 1046 return 1047 except KeyError: 1048 raise TypeError('Unknown type for %s' % info.type.__name__) 1049 elif isinstance(val, info.type): 1050 return 1051 elif issubclass(info.type, list): 1052 # Checking the values of VMOMI array types is surprisingly complicated.... 1053 if isinstance(val, Array): 1054 # 1. We've got a PyVmomi Array object, which is effectively a typed list; 1055 # verify that the type of the Array is a subclass of the expected type. 1056 if issubclass(valType.Item, info.type.Item): 1057 return 1058 elif info.flags & F_LINK: 1059 # Allow objects of expected type to be assigned to links 1060 if issubclass(valType, GetVmodlType(info.expectedType)): 1061 return 1062 elif val: 1063 # 2. We've got a non-empty Python list object, which is untyped; 1064 # walk the list and make sure that each element is a subclass 1065 # of the expected type. 1066 1067 # Masking out F_OPTIONAL part of flags since we are checking for 1068 # each element of the list 1069 flags = info.flags & (F_LINKABLE | F_LINK) 1070 if flags & F_LINK: 1071 if info.expectedType.endswith('[]'): 1072 expectedType = info.expectedType[:-2] 1073 else: 1074 expectedType = info.expectedType 1075 itemInfo = Object(type=info.type.Item, name=info.name, flags=flags, 1076 expectedType=expectedType) 1077 else: 1078 itemInfo = Object(type=info.type.Item, name=info.name, flags=flags) 1079 for it in val: 1080 CheckField(itemInfo, it) 1081 return 1082 else: 1083 # 3. We've got None or an empty Python list object; 1084 # no checking required, since the result will be an empty array. 1085 return 1086 elif info.type is type and valType is type(Exception) \ 1087 or issubclass(info.type, int) and issubclass(valType, int) \ 1088 or issubclass(info.type, long) and (issubclass(valType, int) or \ 1089 issubclass(valType, long)) \ 1090 or issubclass(info.type, float) and issubclass(valType, float) \ 1091 or issubclass(info.type, string_types) and issubclass(valType, string_types): 1092 return 1093 elif issubclass(info.type, Link): 1094 # Allow object of expected type to be assigned to link 1095 if issubclass(valType, GetVmodlType(info.expectedType)): 1096 return 1097 raise TypeError('For "%s" expected type %s, but got %s' 1098 % (info.name, info.type.__name__, valType.__name__)) 1099 1100## Finalize a created type 1101# 1102# @param type a created type 1103def FinalizeType(type): 1104 if issubclass(type, DataObject): 1105 for info in type._propList: 1106 info.type = GetVmodlType(info.type) 1107 elif issubclass(type, ManagedObject): 1108 for info in list(type._propInfo.values()): 1109 info.type = GetVmodlType(info.type) 1110 for info in list(type._methodInfo.values()): 1111 info.result = GetVmodlType(info.result) 1112 info.methodResult = GetVmodlType(info.methodResult) 1113 info.type = GetVmodlType(info.type) 1114 for param in info.params: 1115 param.type = GetVmodlType(param.type) 1116 1117## Get the type of an object, for both new and old-style classes 1118def Type(obj): 1119 try: 1120 return obj.__class__ 1121 except AttributeError: 1122 return type(obj) 1123 1124## Set a WSDL type with wsdl namespace and wsdl name 1125# Internal to VmomiSupport 1126# 1127# Note: Must be holding the _lazyLock, or in main init path 1128def _SetWsdlType(ns, wsdlName, typ): 1129 """ 1130 Set a WSDL type with wsdl namespace and wsdl name. 1131 Returns added type / existing type if (ns, wsdlName) already in the map 1132 1133 Note: Must be holding the _lazyLock, or in main init path 1134 """ 1135 return _wsdlTypeMap.setdefault((ns, wsdlName), typ) 1136 1137## Lookup a WSDL type from wsdl namespace and wsdl name 1138# @param ns XML namespace 1139# @param name wsdl name 1140# @return type if found else throws KeyError 1141def GetWsdlType(ns, name): 1142 if ns is None or name is None: 1143 raise KeyError("{0} {1}".format(ns, name)) 1144 1145 with _lazyLock: 1146 # Check if the type is loaded in the map 1147 typ = _wsdlTypeMap.get( (ns, name) ) 1148 if typ: 1149 return typ 1150 # It is an array type, get the actual type and return the array 1151 elif name.startswith("ArrayOf"): 1152 try: 1153 return GetWsdlType(ns, name[7:]).Array 1154 except KeyError: 1155 raise KeyError("{0} {1}".format(ns, name)) 1156 else: 1157 # Type is not loaded yet, load it 1158 typ = _LoadVmodlType(_wsdlDefMap[(ns, name)][0]) 1159 if typ: 1160 return typ 1161 1162 raise KeyError("{0} {1}".format(ns, name)) 1163 1164 1165class UnknownWsdlTypeError(KeyError): 1166 # NOTE (hartsock): KeyError is extended here since most logic will be 1167 # looking for the KeyError type. I do want to distinguish malformed WSDL 1168 # errors as a separate classification of error for easier bug reports. 1169 pass 1170 1171## Guess the type from wsdlname with no ns 1172# WARNING! This should not be used in general, as there is no guarantee for 1173# the correctness of the guessing type 1174# @param name wsdl name 1175# @return type if found in any one of the name spaces else throws KeyError 1176def GuessWsdlType(name): 1177 with _lazyLock: 1178 # Some types may exist in multiple namespaces, and returning 1179 # the wrong one will cause a deserialization error. 1180 # Since in python3 the order of entries in set is not deterministic, 1181 # we will try to get the type from vim25 namespace first. 1182 try: 1183 return GetWsdlType(XMLNS_VMODL_BASE, name) 1184 except KeyError: 1185 pass 1186 1187 for ns in _wsdlTypeMapNSs: 1188 try: 1189 return GetWsdlType(ns, name) 1190 except KeyError: 1191 pass 1192 raise UnknownWsdlTypeError(name) 1193 1194## Return a map that contains all the wsdl types 1195# This function is rarely used 1196# By calling GetWsdlType on all wsdl names, we will 1197# make sure that the types are loaded before returning 1198# the iterator 1199# @return iterator to the wsdl type map 1200def GetWsdlTypes(): 1201 with _lazyLock: 1202 for ns, name in _wsdlDefMap: 1203 GetWsdlType(ns, name) 1204 return itervalues(_wsdlTypeMap) 1205 1206## Get the qualified XML schema name (ns, name) of a type 1207def GetQualifiedWsdlName(type): 1208 with _lazyLock: 1209 wsdlNSAndName = _wsdlNameMap.get(type) 1210 if wsdlNSAndName: 1211 return wsdlNSAndName 1212 else: 1213 if issubclass(type, list): 1214 ns = GetWsdlNamespace(type.Item._version) 1215 return (ns, "ArrayOf" + Capitalize(type.Item._wsdlName)) 1216 else: 1217 ns = GetWsdlNamespace(type._version) 1218 return (ns, type._wsdlName) 1219 1220## Get the WSDL of a type 1221def GetWsdlName(type): 1222 return GetQualifiedWsdlName(type)[-1] 1223 1224## Capitalize a string 1225def Capitalize(str): 1226 if str: 1227 return str[0].upper() + str[1:] 1228 return str 1229 1230## Uncapitalize a string 1231def Uncapitalize(str): 1232 if str: 1233 return str[0].lower() + str[1:] 1234 return str 1235 1236## To uncapitalize the entire vmodl name 1237# pyVmomi used to map Java package names to capitalized Python module names, 1238# but now maps the Java package names unchanged to Python module names. 1239# This function is needed to support the legacy name mapping. 1240def UncapitalizeVmodlName(str): 1241 if str: 1242 return ".".join(name[0].lower() + name[1:] for name in str.split(".")) 1243 return str 1244 1245## Add a parent version 1246def AddVersionParent(version, parent): 1247 parentMap[version].add(parent) 1248 1249def GetVersionProps(version): 1250 """Get version properties 1251 1252 This function is a fixed version of GetVersion(). 1253 """ 1254 1255 ns = nsMap[version] 1256 versionId = versionIdMap[version] 1257 isLegacy = versionMap.get(ns) == version 1258 serviceNs = serviceNsMap[version] 1259 return ns, versionId, isLegacy, serviceNs 1260 1261 1262## Get version namespace from version 1263def GetVersionNamespace(version): 1264 """ Get version namespace from version """ 1265 ns = nsMap[version] 1266 if not ns: 1267 ns = serviceNsMap[version] 1268 versionId = versionIdMap[version] 1269 if not versionId: 1270 namespace = ns 1271 else: 1272 namespace = '%s/%s' % (ns, versionId) 1273 return namespace 1274 1275## Get version from the version uri 1276def GetVersionFromVersionUri(version): 1277 return versionMap[version.rsplit(":", 1)[-1]] 1278 1279## Get wsdl namespace from version 1280def GetWsdlNamespace(version): 1281 """ Get wsdl namespace from version """ 1282 return "urn:" + serviceNsMap[version] 1283 1284## Get an iterable with all version parents 1285def GetVersionParents(version): 1286 return parentMap[version] 1287 1288## Get all the versions for the service with specified namespace (partially) ordered 1289## by compatibility (i.e. any version in the list that is compatible with some version 1290## v in the list will preceed v) 1291# @param namespace XML namespace identifying a service 1292# @return returns all the versions for the service with specified namespace (partially) 1293# ordered by compatibility 1294# 1295# NOTE: For this function, we use 'namespace' as a representation of 'service'. While 1296# this works for most services, for compatibility reasons, the core and query 1297# services share the 'vim25' namespace with the vim service. Fortunately, this 1298# shouldn't be an issue in practice, as the implementations of the vim 1299# service (vpxd and hostd) don't currently advertise that they support any 1300# versions of the core or query services, and we don't expect that they ever will. 1301# This function assumes that all other namespaces identify a unique service. 1302def GetServiceVersions(namespace): 1303 """ 1304 Get all the versions for the service with specified namespace (partially) ordered 1305 by compatibility (i.e. any version in the list that is compatible with some version 1306 v in the list will preceed v) 1307 """ 1308 def compare(a, b): 1309 if a == b: 1310 return 0 1311 if b in parentMap[a]: 1312 return -1 1313 if a in parentMap[b]: 1314 return 1 1315 return (a > b) - (a < b) 1316 1317 if PY3: 1318 return sorted([v for (v, n) in iteritems(serviceNsMap) if n == namespace], 1319 key=cmp_to_key(compare)) 1320 else: 1321 return sorted([v for (v, n) in iteritems(serviceNsMap) if n == namespace], 1322 compare) 1323 1324 1325## Set a WSDL method with wsdl namespace and wsdl name 1326# Internal to VmomiSupport 1327# Note: Must be holding the _lazyLock 1328# 1329# @param ns XML namespace 1330# @param wsdlName wsdl name 1331# @param inputMM managed method object or info to load it (it points to 1332# list object that points to the type info which holds 1333# this managed method's information) 1334# @return returns added method or exising method if (ns, wsdlName) 1335# is already in the map. It throws a runtime error if 1336# trying to set two type info list's to the same (ns, wsdlName) 1337def _SetWsdlMethod(ns, wsdlName, inputMM): 1338 """ 1339 Set a WSDL method with wsdl namespace and wsdl name 1340 Returns added method / existing method if (ns, wsdlName) already in the map 1341 1342 Note: Must be holding the _lazyLock 1343 """ 1344 _wsdlMethodNSs.add(ns) 1345 curMM = _wsdlMethodMap.get( (ns, wsdlName) ) 1346 # if inputMM is a list 1347 if isinstance(inputMM, list): 1348 if curMM is None: 1349 _wsdlMethodMap[(ns, wsdlName)] = inputMM 1350 return inputMM 1351 elif isinstance(curMM, list): 1352 raise RuntimeError( 1353 "Duplicate wsdl method %s %s (new class %s vs existing %s)" % \ 1354 (ns, wsdlName, inputMM[0], curMM[0])) 1355 else: 1356 return curMM 1357 # if inputMM is a ManagedMethod 1358 else: 1359 if curMM is None or isinstance(curMM, list): 1360 _wsdlMethodMap[(ns, wsdlName)] = inputMM 1361 return inputMM 1362 else: 1363 return curMM 1364 1365## Get wsdl method from ns, wsdlName 1366# @param ns XML namespace 1367# @param wsdlName wsdl name 1368# @return managed method object or throws a KeyError 1369def GetWsdlMethod(ns, wsdlName): 1370 """ Get wsdl method from ns, wsdlName """ 1371 with _lazyLock: 1372 method = _wsdlMethodMap[(ns, wsdlName)] 1373 if isinstance(method, ManagedMethod): 1374 # The type corresponding to the method is loaded, 1375 # just return the method object 1376 return method 1377 elif method: 1378 # The type is not loaded, the map contains the info 1379 # to load the type. Load the actual type and 1380 # return the method object 1381 LoadManagedType(*method) 1382 return _wsdlMethodMap[(ns, wsdlName)] 1383 else: 1384 raise KeyError("{0} {1}".format(ns, name)) 1385 1386## Guess the method from wsdlname with no ns 1387# WARNING! This should not be used in general, as there is no guarantee for 1388# the correctness of the guessing method 1389# @param name wsdl name 1390# @return managed method object if found in any namespace else throws 1391# KeyError 1392def GuessWsdlMethod(name): 1393 with _lazyLock: 1394 # Some methods may exist in multiple namespaces, and returning 1395 # the wrong one will cause a deserialization error. 1396 # Since in python3 the order of entries in set is not deterministic, 1397 # we will try to get the method from vim25 namespace first. 1398 try: 1399 return GetWsdlMethod(XMLNS_VMODL_BASE, name) 1400 except KeyError: 1401 pass 1402 1403 for ns in _wsdlMethodNSs: 1404 try: 1405 return GetWsdlMethod(ns, name) 1406 except KeyError: 1407 pass 1408 raise KeyError(name) 1409 1410## Widen a type to one supported in a given version 1411def GetCompatibleType(type, version): 1412 # Type can be widened if it has the attribute "_version" (which implies it 1413 # is either a DataObject or ManagedObject) 1414 if hasattr(type, "_version"): 1415 while not IsChildVersion(version, type._version): 1416 type = type.__bases__[0] 1417 return type 1418 1419## Invert an injective mapping 1420def InverseMap(map): 1421 return dict([ (v, k) for (k, v) in iteritems(map) ]) 1422 1423def GetVmodlNs(version): 1424 versionParts = version.split('.version.') 1425 assert len(versionParts) == 2, 'Unsupported version format: %s' % version 1426 return versionParts[0] 1427 1428types = Object() 1429nsMap = {} 1430versionIdMap = {} 1431versionMap = {} 1432serviceNsMap = { BASE_VERSION : XMLNS_VMODL_BASE.split(":")[-1] } 1433parentMap = {} 1434 1435class _MaturitySet: 1436 """ 1437 Registry for versions from all namespaces defining a given maturity. 1438 The registration is automatic (relevant code is generated by emitters), 1439 while for the query one may use either the VMODL namespace id (e.g. 'vim'), 1440 or the wire namespace id (e.g. 'vim25'). 1441 """ 1442 def __init__(self): 1443 self._verNameMap = {} # e.g. 'vim' -> 'vim.version.version12' 1444 self._verNameMapW = {} # e.g. 'vim25' -> 'vim.version.version12' 1445 self._wireIdMap = {} # e.g. 'vim' -> 'vim25/6.7' 1446 self._wireIdMapW = {} # e.g. 'vim25' -> 'vim25/6.7' 1447 1448 def Add(self, version): 1449 """ 1450 Register the version at corresponding maturity for a given VMODL 1451 namespace. The 'version' parameter is in the VMODL name format 1452 e.g. 'vim.version.version12'. This method is typically used by 1453 auto-generated code. 1454 """ 1455 vmodlNs = GetVmodlNs(version) 1456 1457 # TODO fix the VSAN-related part of vcenter-all to enable the assert 1458 # assert not (vmodlNs in self._verNameMap), 'Re-definition: %s' % vmodlNs 1459 1460 wireId = GetVersionNamespace(version) 1461 wireNs = wireId.split('/')[0] 1462 self._verNameMap[vmodlNs] = version 1463 self._verNameMapW[wireNs] = version 1464 self._wireIdMap[vmodlNs] = wireId 1465 self._wireIdMapW[wireNs] = wireId 1466 return wireId, wireNs 1467 1468 def GetName(self, vmodlNs): 1469 """ 1470 VMODL namespace to registered version name mapping, e.g. 1471 'vim' -> 'vim.version.version12' 1472 """ 1473 return self._verNameMap[vmodlNs] 1474 1475 def GetNameW(self, wireNs): 1476 """ 1477 Wire namespace to registered version name mapping, e.g. 1478 'vim25' -> 'vim.version.version12' 1479 """ 1480 return self._verNameMapW[wireNs] 1481 1482 def GetWireId(self, vmodlNs): 1483 """ 1484 VMODL namespace to registered version wire-id mapping, e.g. 1485 'vim' -> 'vim25/6.7' 1486 """ 1487 return self._wireIdMap[vmodlNs] 1488 1489 def GetWireIdW(self, wireNs): 1490 """ 1491 Wire namespace to registered version wire-id mapping, e.g. 1492 'vim25' -> 'vim25/6.7' 1493 """ 1494 return self._wireIdMapW[wireNs] 1495 1496 def EnumerateVmodlNs(self): 1497 """ 1498 Returns an iterable with registered VMODL namespace, e.g. 1499 ['vim', 'vpx', ... ] 1500 """ 1501 return self._verNameMap.keys() 1502 1503 def EnumerateWireNs(self): 1504 """ 1505 Returns an iterable with registered wire namespace, e.g. 1506 ['vim25', 'vpxd3', ... ] 1507 """ 1508 return self._verNameMapW.keys() 1509 1510 def EnumerateVersions(self): 1511 """ 1512 Returns an iterable with registered VMODL versions, e.g. 1513 ['vim.version.version12', 'vpx.version.version12', ... ] 1514 """ 1515 return self._verNameMap.values() 1516 1517 def EnumerateWireIds(self): 1518 """ 1519 Returns an iterable with registered versions wire-ids, e.g. 1520 e.g. ['vim25/6.7', 'vpxd3/6.7', ... ] 1521 """ 1522 return self._wireIdMap.values() 1523 1524# Backward compatibility aliases 1525_MaturitySet.Get = _MaturitySet.GetName 1526_MaturitySet.GetNamespace = _MaturitySet.GetWireId 1527 1528 1529newestVersions = _MaturitySet() 1530ltsVersions = _MaturitySet() 1531dottedVersions = _MaturitySet() 1532oldestVersions = _MaturitySet() 1533 1534# Alias for backward compatibility. 1535publicVersions = ltsVersions 1536 1537from .Version import AddVersion, IsChildVersion 1538 1539if not isinstance(bool, type): # bool not a type in python <= 2.2 1540 bool = type("bool", (int,), 1541 {"__new__": lambda cls, val=0: int.__new__(cls, val and 1 or 0)}) 1542byte = type("byte", (int,), {}) 1543short = type("short", (int,), {}) 1544double = type("double", (float,), {}) 1545if PY3: 1546 long = type("long", (int,), {}) 1547URI = type("URI", (str,), {}) 1548if not PY3: 1549 # six defines binary_type in python2 as a string; this means the 1550 # JSON encoder sees checksum properties as strings and attempts 1551 # to perform utf-8 decoding on them because they contain high-bit 1552 # characters. 1553 binary = type("binary", (bytearray,), {}) 1554else: 1555 binary = type("binary", (binary_type,), {}) 1556PropertyPath = type("PropertyPath", (text_type,), {}) 1557 1558# _wsdlTypeMapNSs store namespaces added to _wsdlTypeMap in _SetWsdlType 1559_wsdlTypeMapNSs = set() 1560_wsdlTypeMap = { 1561 # Note: xsd has no void type. This is a hack from before. Can be removed? 1562 (XMLNS_XSD, 'void') : NoneType, 1563 (XMLNS_XSD, 'anyType') : object, 1564 (XMLNS_XSD, 'boolean') : bool, 1565 (XMLNS_XSD, 'byte') : byte, 1566 (XMLNS_XSD, 'short') : short, 1567 (XMLNS_XSD, 'int') : int, 1568 (XMLNS_XSD, 'long') : long, 1569 (XMLNS_XSD, 'float') : float, 1570 (XMLNS_XSD, 'double') : double, 1571 (XMLNS_XSD, 'string') : str, 1572 (XMLNS_XSD, 'anyURI') : URI, 1573 (XMLNS_XSD, 'base64Binary') : binary, 1574 (XMLNS_XSD, 'dateTime') : datetime, 1575 (XMLNS_XSD, 'Link') : Link, 1576 (XMLNS_VMODL_BASE, 'TypeName') : type, 1577 (XMLNS_VMODL_BASE, 'MethodName') : ManagedMethod, 1578 (XMLNS_VMODL_BASE, 'PropertyPath') : PropertyPath 1579} 1580_wsdlNameMap = InverseMap(_wsdlTypeMap) 1581 1582for ((ns, name), typ) in list(_wsdlTypeMap.items()): 1583 if typ is not NoneType: 1584 setattr(types, typ.__name__, typ) 1585 _wsdlTypeMapNSs.add(ns) 1586 arrayType = CreateArrayType(typ) 1587 setattr(types, Capitalize(typ.__name__) + "Array", arrayType) 1588 arrayName = "ArrayOf" + Capitalize(name) 1589 arrayNS = XMLNS_VMODL_BASE 1590 _SetWsdlType(arrayNS, arrayName, arrayType) 1591 _wsdlNameMap[arrayType] = (arrayNS, arrayName) 1592del name, typ 1593 1594# unicode is mapped to wsdl name 'string' (Cannot put in wsdlTypeMap or name 1595# collision with non-unicode string) 1596_wsdlNameMap[text_type] = (XMLNS_XSD, 'string') 1597_wsdlNameMap[CreateArrayType(text_type)] = (XMLNS_VMODL_BASE, 'ArrayOfString') 1598 1599# _wsdlMethodNSs store namespaces added to _wsdlMethodMap in _SetWsdlMethod 1600_wsdlMethodNSs = set() 1601_wsdlMethodMap = {} 1602 1603# Registering the classes defined in VmomiSupport in the definition maps 1604CreateManagedType(ManagedObject.__name__, ManagedObject._wsdlName, None, 1605 ManagedObject._version, [], []) 1606_AddType(ManagedObject) 1607setattr(types, ManagedObject.__name__, ManagedObject) 1608 1609CreateDataType(DataObject.__name__, DataObject._wsdlName, None, 1610 DataObject._version, []) 1611_AddType(DataObject) 1612setattr(types, DataObject.__name__, DataObject) 1613 1614## Vmodl types 1615vmodlTypes = { 1616 # Note: xsd has no void type. This is a hack from before. Can be removed? 1617 "void" : GetWsdlType(XMLNS_XSD, 'void'), 1618 "anyType": GetWsdlType(XMLNS_XSD, 'anyType'), 1619 "string" : GetWsdlType(XMLNS_XSD, 'string'), 1620 "bool" : GetWsdlType(XMLNS_XSD, 'boolean'), 1621 "boolean": GetWsdlType(XMLNS_XSD, 'boolean'), 1622 "byte" : GetWsdlType(XMLNS_XSD, 'byte'), 1623 "short" : GetWsdlType(XMLNS_XSD, 'short'), 1624 "int" : GetWsdlType(XMLNS_XSD, 'int'), 1625 "long" : GetWsdlType(XMLNS_XSD, 'long'), 1626 "float" : GetWsdlType(XMLNS_XSD, 'float'), 1627 "double" : GetWsdlType(XMLNS_XSD, 'double'), 1628 "Link" : GetWsdlType(XMLNS_XSD, 'Link'), 1629 "vmodl.URI" : GetWsdlType(XMLNS_XSD, 'anyURI'), 1630 "vmodl.Binary" : GetWsdlType(XMLNS_XSD, 'base64Binary'), 1631 "vmodl.DateTime" : GetWsdlType(XMLNS_XSD, 'dateTime'), 1632 "vmodl.TypeName" : GetWsdlType(XMLNS_VMODL_BASE, 'TypeName'), 1633 "vmodl.MethodName" : GetWsdlType(XMLNS_VMODL_BASE, 'MethodName'), 1634 "vmodl.DataObject" : GetWsdlType(XMLNS_VMODL_BASE, 'DataObject'), 1635 "vmodl.ManagedObject" : GetWsdlType(XMLNS_VMODL_BASE, 'ManagedObject'), 1636 "vmodl.PropertyPath" : GetWsdlType(XMLNS_VMODL_BASE, 'PropertyPath'), 1637} 1638vmodlNames = {} 1639 1640## Add array type into special names 1641for name, typ in vmodlTypes.copy().items(): 1642 if typ is not NoneType: 1643 try: 1644 arrayType = typ.Array 1645 except AttributeError: 1646 wsdlName = GetWsdlName(typ) 1647 arrayNS = XMLNS_VMODL_BASE 1648 arrayType = GetWsdlType(arrayNS, "ArrayOf" + Capitalize(wsdlName)) 1649 arrayName = name + "[]" 1650 vmodlTypes[arrayName] = arrayType 1651 1652 # Set type to vmodl name map 1653 vmodlNames[typ] = name 1654 vmodlNames[arrayType] = arrayName 1655del name, typ 1656 1657 1658## Get type from vmodl name 1659# 1660# @param name vmodl name 1661# @return vmodl type 1662def GetVmodlType(name): 1663 """ Get type from vmodl name """ 1664 1665 # If the input is already a type, just return 1666 if isinstance(name, type): 1667 return name 1668 1669 # Try to get type from vmodl type names table 1670 typ = vmodlTypes.get(name) 1671 if typ: 1672 return typ 1673 1674 # Else get the type from the _wsdlTypeMap 1675 isArray = name.endswith("[]") 1676 if isArray: 1677 name = name[:-2] 1678 ns, wsdlName = _GetWsdlInfo(name) 1679 try: 1680 typ = GetWsdlType(ns, wsdlName) 1681 except KeyError: 1682 raise KeyError(name) 1683 if typ: 1684 return isArray and typ.Array or typ 1685 else: 1686 raise KeyError(name) 1687 1688## Get VMODL type name from type 1689# 1690# @param typ vmodl type 1691# @return vmodl name 1692def GetVmodlName(typ): 1693 """ Get vmodl type name from type """ 1694 try: 1695 return vmodlNames[typ] 1696 except KeyError: 1697 return typ.__name__ 1698 1699## Get Wsdl type name from Python type name 1700# 1701# @param pythonTypeName Python type name 1702# @return wsdl type name 1703def GetWsdlTypeName(pythonTypeName): 1704 try: 1705 typ = GetVmodlType(pythonTypeName) 1706 except KeyError: 1707 raise NameError('No type found with name ' + pythonTypeName) 1708 return GetWsdlName(typ) 1709 1710## Get Wsdl method name from Python method name 1711# 1712# @param pythonTypeName Python type name 1713# @param pythonMethodName Python method name 1714# @return wsdl method name 1715def GetWsdlMethodName(pythonTypeName, pythonMethodName): 1716 try: 1717 typ = GetVmodlType(pythonTypeName) 1718 _, _, _, _, _, methods = _wsdlDefMap[GetQualifiedWsdlName(typ)] 1719 except KeyError: 1720 raise NameError('No type found with name ' + pythonTypeName) 1721 uncapPythonMethodName = Uncapitalize(pythonMethodName) 1722 for method in methods: 1723 mVmodl, mWsdl, _, _, _, _, _ = method 1724 if mVmodl == uncapPythonMethodName or mVmodl == pythonMethodName: 1725 return mWsdl 1726 raise NameError('No method found with name ' + pythonMethodName) 1727 1728## Get Python type name from Wsdl type name 1729# 1730# @param ns wsdl namespace 1731# @param wsdlTypeName wsdl type name 1732# @return python type name 1733def GetPythonTypeName(wsdlTypeName, ns): 1734 try: 1735 typ = GetWsdlType(ns, wsdlTypeName) 1736 except KeyError: 1737 raise NameError('No type found with namespace %s and name %s' % (ns, wsdlTypeName)) 1738 return GetVmodlName(typ) 1739 1740## Get Python method name from Wsdl method name 1741# 1742# @param ns wsdl namespace 1743# @param wsdlTypeName wsdl type name 1744# @param wsdlMethodName wsdl method name 1745# @return python method name 1746def GetPythonMethodName(wsdlTypeName, ns, wsdlMethodName): 1747 try: 1748 _, _, _, _, _, methods = _wsdlDefMap[(ns, wsdlTypeName)] 1749 except KeyError: 1750 raise NameError('No type found with namespace %s and name %s' % (ns, wsdlTypeName)) 1751 for method in methods: 1752 mVmodl, mWsdl, _, _, _, _, _ = method 1753 if mWsdl == wsdlMethodName: 1754 return Capitalize(mVmodl) 1755 raise NameError('No method found with name ' + wsdlMethodName) 1756 1757## String only dictionary: same as dict, except it only accept string as value 1758# 1759class StringDict(dict): 1760 """ 1761 String only dictionary: same as dict, except it only accept string as value 1762 1763 dict in python is kind of strange. U cannot just override __setitem__, as 1764 __init__, update, and setdefault all bypass __setitem__. When override, 1765 we have to override all three together 1766 """ 1767 def __init__(self, *args, **kwargs): 1768 dict.__init__(self) 1769 self.update(*args, **kwargs) 1770 1771 # Same as dict setdefault, except this will call through our __setitem__ 1772 def update(self, *args, **kwargs): 1773 for k, v in iteritems(dict(*args, **kwargs)): 1774 self[k] = v 1775 1776 # Same as dict setdefault, except this will call through our __setitem__ 1777 def setdefault(self, key, val=None): 1778 if key in self: 1779 return self[key] 1780 else: 1781 self[key] = val 1782 return val 1783 1784 def __setitem__(self, key, val): 1785 """x.__setitem__(i, y) <==> x[i]=y, where y must be a string""" 1786 if not isinstance(val, string_types): 1787 raise TypeError("key %s has non-string value %s of %s" % 1788 (key, val, type(val))) 1789 return dict.__setitem__(self, key, val) 1790 1791## Retrieves the actual vmodl name from type dictionaries 1792# 1793# Note: Must be holding the _lazyLock 1794# @param name upcapitalized vmodl name 1795# @return vmodl name 1796def _GetActualName(name): 1797 """ Note: Must be holding the _lazyLock """ 1798 if _allowCapitalizedNames: 1799 name = UncapitalizeVmodlName(name) 1800 for defMap in _dataDefMap, _managedDefMap, _enumDefMap: 1801 dic = defMap.get(name) 1802 if dic: 1803 return dic[0] 1804 return None 1805 1806## Retrieves the actual wsdl name from type dictionaries 1807# 1808# @param name upcapitalized vmodl name 1809# @return (wsdl namespace, wsdl name) 1810def _GetWsdlInfo(name): 1811 if _allowCapitalizedNames: 1812 name = UncapitalizeVmodlName(name) 1813 1814 with _lazyLock: 1815 # For data and managed objects, emitter puts version in field #3 and in 1816 # enum objects, it is in field #2. So, have to handle them differently 1817 for defMap in _dataDefMap, _managedDefMap: 1818 dic = defMap.get(name) 1819 if dic: 1820 return GetWsdlNamespace(dic[3]), dic[1] 1821 1822 dic = _enumDefMap.get(name) 1823 if dic: 1824 return GetWsdlNamespace(dic[2]), dic[1] 1825 return None, None 1826 1827## Checks if the definition exists for a vmodl name 1828# 1829# @param name vmodl name 1830# @return True if name exists, False otherwise 1831def TypeDefExists(name): 1832 # Check if is one of the primitive types 1833 typ = vmodlTypes.get(name) 1834 if typ: 1835 return True 1836 1837 # Check if it's type definition is loaded in the dictionaries 1838 if name.endswith("[]"): 1839 name = name[:-2] 1840 1841 with _lazyLock: 1842 actualName = _GetActualName(name) 1843 return actualName is not None 1844 1845# Thread local for req context 1846_threadLocalContext = threading.local() 1847 1848# Get the RequestContext for the current thread 1849# 1850def GetRequestContext(): 1851 """ Get the RequestContext for the current thread """ 1852 global _threadLocalContext 1853 return _threadLocalContext.__dict__.setdefault('reqCtx', StringDict()) 1854 1855# Get the Http context for the current thread 1856# 1857def GetHttpContext(): 1858 """ Get the Http context for the current thread """ 1859 global _threadLocalContext 1860 return _threadLocalContext.__dict__.setdefault('httpCtx', dict()) 1861 1862## Class that resolves links 1863class LinkResolver: 1864 ## Constructor 1865 # 1866 # @param self self 1867 # @param scope DataObject to be used against for resolving links 1868 def __init__(self, scope): 1869 self.linkables = {} 1870 self._VisitDataObject(scope) 1871 1872 ## Visit a DataObject and add it to linkable if it is one. Also 1873 # visit its properties that are DataObjects 1874 # 1875 # @param self self 1876 # @param obj DataObject to be visited 1877 def _VisitDataObject(self, obj): 1878 if isinstance(obj, DataObject): 1879 for prop in obj._GetPropertyList(): 1880 if issubclass(prop.type, list): 1881 for dataObj in getattr(obj, prop.name): 1882 if (prop.flags & F_LINKABLE): 1883 self._AddLinkable(dataObj) 1884 self._VisitDataObject(dataObj) 1885 else: 1886 dataObj = getattr(obj, prop.name) 1887 if (prop.flags & F_LINKABLE): 1888 self._AddLinkable(dataObj) 1889 self._VisitDataObject(dataObj) 1890 elif isinstance(obj, list): 1891 for dataObj in obj: 1892 self._VisitDataObject(dataObj) 1893 1894 ## Adds a DataObject to linkable dictionary using its key 1895 # 1896 # @param self self 1897 # @param obj DataObject to be added to linkable 1898 def _AddLinkable(self, obj): 1899 key = getattr(obj, "key") 1900 if key and key != '': 1901 if key in self.linkables: 1902 #duplicate key present 1903 raise AttributeError(key) 1904 else: 1905 self.linkables[key] = obj 1906 else: 1907 #empty key 1908 raise AttributeError(key) 1909 1910 ## Resolves a key by looking up linkable dictionary 1911 # 1912 # @param self self 1913 # @param key Key to be resolved 1914 def ResolveLink(self, key): 1915 val = self.linkables[key] 1916 return val 1917 1918 ## Resolves a list of keys by resolving each key 1919 # 1920 # @param self self 1921 # @param keys keys to be resolved 1922 def ResolveLinks(self, keys): 1923 val = [self.linkables[k] for k in keys] 1924 return val 1925 1926## Resolves a link key using the object provided as its scope by creating a 1927# link resolver object 1928# 1929# @param key Key to be resolved 1930# @param obj DataObject to be used against for resolving links 1931def ResolveLink(key, obj): 1932 if obj is None: 1933 return None 1934 linkResolver = LinkResolver(obj) 1935 return linkResolver.ResolveLink(key) 1936 1937## Resolves a list of link keys using the object provided as its scope by creating a 1938# link resolver object 1939# 1940# @param keys keys to be resolved 1941# @param obj DataObject to be used against for resolving links 1942def ResolveLinks(keys, obj): 1943 if obj is None: 1944 return None 1945 linkResolver = LinkResolver(obj) 1946 return linkResolver.ResolveLinks(keys) 1947 1948 1949# Dictionary of type { 'branch' : { 'namespace' : count } } 1950# used by Vmodl Vcdb B2B code. 1951 1952_breakingChanges = {} 1953 1954 1955def AddBreakingChangesInfo(branchName, vmodlNamespace, count): 1956 _breakingChanges.setdefault(branchName, {})[vmodlNamespace] = count 1957 1958 1959def GetBreakingChanges(): 1960 return _breakingChanges 1961