Files
deb-python-pyvmomi/pyVmomi/VmomiSupport.py
Tianhao He b812a1d8f6 Support for build-time versions in pyVmomi
Since introduction of meta-versions and related
generated versions, we need facility, allowing
code to specify those versions during run-time.

(Note that although version classes are well-
defined, the particular versions that correspond
to a class change from build to build, depending
on meta-version state.)
2015-10-19 12:42:00 -07:00

1678 lines
58 KiB
Python

# VMware vSphere Python SDK
# Copyright (c) 2008-2015 VMware, Inc. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
## VMOMI support code
from __future__ import absolute_import
from __future__ import with_statement # 2.5 only
from six import iteritems
from six import iterkeys
from six import itervalues
from six import text_type
from six import PY3
from datetime import datetime
from pyVmomi import Iso8601
import base64
import threading
if PY3:
# python3 removed long, it's the same as int
long = int
# python3 removed basestring, use str instead.
basestring = str
NoneType = type(None)
try:
from pyVmomi.pyVmomiSettings import allowGetSet
_allowGetSet = allowGetSet
except:
_allowGetSet = True
try:
from pyVmomi.pyVmomiSettings import allowCapitalizedNames
_allowCapitalizedNames = allowCapitalizedNames
except:
_allowCapitalizedNames = True
(F_LINK,
F_LINKABLE,
F_OPTIONAL,
F_SECRET) = [ 1<<x for x in range(4) ]
BASE_VERSION = 'vmodl.version.version0'
VERSION1 = 'vmodl.version.version1'
XMLNS_XSD = "http://www.w3.org/2001/XMLSchema"
XMLNS_XSI = "http://www.w3.org/2001/XMLSchema-instance"
XMLNS_VMODL_BASE = "urn:vim25"
# The lock ensures that we serialize the lazy loading. In particular, we need
# to guard against multiple threads loading the same types on the same kind
# of objects at the same time
# The lock is protecting the following variables:
# _topLevelNames, _*DefMap, and _dependencyMap
_lazyLock = threading.RLock()
# Also referenced in __init__.py
_topLevelNames = set()
# Maps to store parameters to create the type for each vmodlName
_managedDefMap = {}
_dataDefMap = {}
_enumDefMap = {}
# Map to store parameters to create the type for each wsdlName
_wsdlDefMap = {}
# Map that stores the nested classes for a given class
# if a.b.c and a.b.d are the nested classes of a.b, then _dependencyMap[a.b] = {c,d}
_dependencyMap = {}
## Update the dependency map
# Note: Must be holding the _lazyLock
#
# @param names VmodlName of the type
def _AddToDependencyMap(names):
""" Note: Must be holding the _lazyLock """
curName = names[0]
_topLevelNames.add(curName)
for name in names[1:]:
_dependencyMap.setdefault(curName, set()).add(name)
curName = ".".join([curName, name])
## Check if a particular name is dependent on another
# Note: Must be holding the _lazyLock
#
# @param parent Parent Vmodl name
# @param name Vmodl name to be checked for dependency
# @return True, if name depends on parent, False otherwise
def _CheckForDependency(parent, name):
""" Note: Must be holding the _lazyLock """
if _allowCapitalizedNames:
# If the flag is set, check for both capitalized and
# uncapitalized form. This is a temporary fix for handling
# vim.EsxCLI namespace.
# Ex: If we add vim.EsxCLI.vdisk, then
# _dependencyMap['vim.EsxCLI'] will have value ['vdisk'].
# When we try to check dependency for vdisk, since the flag
# is set, we will uncapitalize EsxCLI and this will result
# in attribute error
dependents = _dependencyMap.get(parent)
if not dependents:
uncapitalizedParent = UncapitalizeVmodlName(parent)
dependents = _dependencyMap.get(uncapitalizedParent)
if dependents:
if name in dependents or Uncapitalize(name) in dependents:
return True
else:
dependents = _dependencyMap.get(parent)
if dependents:
if name in dependents:
return True
return False
## Checks for the type definition in all the maps
# and loads it if it finds the definition
#
# @param name vmodl name of the type
# @return vmodl type
def _LoadVmodlType(name):
isArray = name.endswith("[]")
if isArray:
name = name[:-2]
if _allowCapitalizedNames:
name = UncapitalizeVmodlName(name)
with _lazyLock:
for defMap, loadFn in [(_dataDefMap, LoadDataType),
(_managedDefMap, LoadManagedType),
(_enumDefMap, LoadEnumType)]:
dic = defMap.get(name)
if dic:
typ = loadFn(*dic)
return isArray and typ.Array or typ
return None
# In Python 2.4 and earlier, exceptions are old-style classes, so data objects
# must be old-style too. For 2.5 and newer, data objects must be new-style
# classes
if issubclass(Exception, object):
Base = object
SetAttr = object.__setattr__
else:
class Base: pass
def SetAttr(obj, key, val):
obj.__dict__[key] = val
## Simple class to store named attributes
class Object:
## Constructor
def __init__(self, **kwargs):
self.__dict__.update(kwargs)
# All properties and methods in vmodl types are created as LazyObject's
# in VmomiSupport. The attributes in these properties and methods that refer
# to vmodl types are "type", "result" and "methodResult". If a program tries
# to access any of these attributes, load the type. The vmodl name of the type
# can be retrieved by adding name to the attribute that is being accessed
# Creating a derived class of Object so that programs that want to use just
# Object are not affected
class LazyObject(Object):
def __getattr__(self, attr):
with _lazyLock:
# Check if another thread already initialized this
obj = self.__dict__.get(attr)
if obj:
return obj
if attr == "type" or attr == "result" or attr == "methodResult":
attrName = attr + "Name"
vmodlName = getattr(self, attrName)
vmodlType = GetVmodlType(vmodlName)
setattr(self, attr, vmodlType)
delattr(self, attrName)
return vmodlType
else:
raise AttributeError(attr)
class Link(text_type):
def __new__(cls, obj):
if isinstance(obj, basestring):
return text_type.__new__(cls, obj)
elif isinstance(obj, DataObject):
if obj.key:
return text_type.__new__(cls, obj.key)
raise AttributeError("DataObject does not have a key to link")
else:
raise ValueError
## LazyType to wrap around actual type
# This is used to intercept attribute accesses of a class
# and load the appropriate nested classes on-demand
class LazyType(type):
def __getattr__(self, attr):
if attr.endswith("[]"):
searchName = attr[:-2]
else:
searchName = attr
with _lazyLock:
nestedClasses = _dependencyMap.get(self.__name__, [])
if searchName in nestedClasses:
return GetVmodlType(self.__name__ + "." + attr)
else:
return super(LazyType, self).__getattribute__(attr)
## LazyModule class
# Used as a placeholder until the actual type is loaded
# If someone wants to use the type, then it is loaded on-demand
class LazyModule(object):
def __init__(self, name):
# name is used to save the current context of the object
# If it is created because of reference to a.b, name will
# be a.b
self.name = name
def __getattr__(self, attr):
# If someone tries to introspect the instance of this class
# using inspect.isclass(), the function will check if the object
# has __bases__ attr. So, throwing an AttributeError to make it work
if attr == "__bases__":
raise AttributeError
with _lazyLock:
# Check if we have already loaded the class or object
# corresponding to this attribute
obj = self.__dict__.get(attr)
if obj:
return obj
name = ".".join([self.name, attr])
# Get the actual vmodlName from the type dictionaries
actualName = _GetActualName(name)
if actualName:
typeObj = GetVmodlType(actualName)
else:
if _CheckForDependency(self.name, attr):
typeObj = LazyModule(name)
elif self.name == "vim":
try:
typeObj = GetWsdlType(XMLNS_VMODL_BASE, attr)
except:
raise AttributeError(attr)
else:
raise AttributeError(attr)
setattr(self, attr, typeObj)
return typeObj
# If the lazy module is representing a data object,
# this will get triggered when some code tries to initialize it
# Load the actual type and pass the arguments to it's init.
def __call__(self, **kwargs):
typ = _LoadVmodlType(self.name)
if typ:
return typ.__call__(**kwargs)
else:
raise AttributeError("'%s' does not exist" % self.name)
## Format a python VMOMI object
#
# @param val the object
# @param info the field
# @param indent the level of indentation
# @return the formatted string
def FormatObject(val, info=Object(name="", type=object, flags=0), indent=0):
start = indent * " " + (info.name and "%s = " % info.name or "")
if val == None:
result = "<unset>"
elif isinstance(val, DataObject):
if info.flags & F_LINK:
result = "<%s:%s>" % (val.__class__.__name__, val.key)
else:
result = "(%s) {\n%s\n%s}" % (val.__class__.__name__,
',\n'.join([FormatObject(getattr(val, prop.name), prop, indent+1)
for prop in val._GetPropertyList()]), indent * " ")
elif isinstance(val, ManagedObject):
if val._serverGuid is None:
result = "'%s:%s'" % (val.__class__.__name__, val._moId)
else:
result = "'%s:%s:%s'" % (val.__class__.__name__, val._serverGuid,
val._moId)
elif isinstance(val, list):
itemType = getattr(val, 'Item', getattr(info.type, 'Item', object))
if val:
item = Object(name="", type=itemType, flags=info.flags)
result = "(%s) [\n%s\n%s]" % (itemType.__name__,
',\n'.join([FormatObject(obj, item, indent+1) for obj in val]),
indent * " ")
else:
result = "(%s) []" % itemType.__name__
elif isinstance(val, type):
result = val.__name__
elif isinstance(val, ManagedMethod):
result = '%s.%s' % (val.info.type.__name__, val.info.name)
elif isinstance(val, bool):
result = val and "true" or "false"
elif isinstance(val, datetime):
result = Iso8601.ISO8601Format(val)
elif isinstance(val, binary):
result = base64.b64encode(val)
else:
result = repr(val)
return start + result
## Lookup a property for a given object type
#
# @param type the type
# @param name the name of the property
def GetPropertyInfo(type, name):
try:
while name not in type._propInfo:
type = type.__bases__[0]
else:
return type._propInfo[name]
except Exception:
raise AttributeError(name)
## VMOMI Managed Object class
class ManagedObject(object):
_wsdlName = "ManagedObject"
_propInfo = {}
_propList = []
_methodInfo = {}
_version = BASE_VERSION
## Constructor
#
# @param[in] self self
# @param[in] moId The ID of this managed object
# @param[in] stub The stub adapter, if this is a client stub object
def __init__(self, moId, stub=None, serverGuid=None):
object.__setattr__(self, "_moId", moId)
object.__setattr__(self, "_stub", stub)
object.__setattr__(self, "_serverGuid", serverGuid)
## Invoke a managed method
#
# @param info method info
# @param self self
# @param ... arguments
def _InvokeMethod(info, self, *posargs, **kwargs):
if len(posargs) > len(info.params):
s = "s"*(len(info.params)!=1)
raise TypeError("%s() takes at most %d argument%s (%d given)" %
(Capitalize(info.name), len(info.params), s, len(posargs)))
args = list(posargs) + [None] * (len(info.params) - len(posargs))
if len(kwargs) > 0:
paramNames = [param.name for param in info.params]
for (k, v) in list(kwargs.items()):
try:
idx = paramNames.index(k)
except ValueError:
raise TypeError("%s() got an unexpected keyword argument '%s'" %
(Capitalize(info.name), k))
if idx < len(posargs):
raise TypeError("%s() got multiple values for keyword argument '%s'" %
(Capitalize(info.name), k))
args[idx] = v
map(CheckField, info.params, args)
return self._stub.InvokeMethod(self, info, args)
_InvokeMethod = staticmethod(_InvokeMethod)
## Invoke a managed property accessor
#
# @param info property info
# @param self self
def _InvokeAccessor(info, self):
return self._stub.InvokeAccessor(self, info)
_InvokeAccessor = staticmethod(_InvokeAccessor)
## Get the ID of a managed object
def _GetMoId(self):
return self._moId
## Get the serverGuid of a managed object
def _GetServerGuid(self):
return self._serverGuid
## Get the stub of a managed object
def _GetStub(self):
return self._stub
## Get a list of all properties of this type and base types
#
# @param cls The managed object type
def _GetPropertyList(cls, includeBaseClassProps=True):
if not includeBaseClassProps:
return cls._propList
prop = {}
result = []
while cls != ManagedObject:
# Iterate through props, add info for prop not found in derived class
result = [info for info in cls._propList
if prop.setdefault(info.name, cls) == cls] + result
cls = cls.__bases__[0]
return result
_GetPropertyList = classmethod(_GetPropertyList)
## Get a list of all methods of this type and base types
#
# @param cls The managed object type
def _GetMethodList(cls):
meth = {}
result = []
while cls != ManagedObject:
# Iterate through methods, add info for method not found in derived class
result = [info for info in cls._methodInfo.values()
if meth.setdefault(info.name, cls) == cls] + result
cls = cls.__bases__[0]
return result
_GetMethodList = classmethod(_GetMethodList)
## Lookup a method for a given managed object type
#
# @param type the type
# @param name the name of the property
def _GetMethodInfo(type, name):
while hasattr(type, "_methodInfo"):
try:
return type._methodInfo[name]
except KeyError:
type = type.__bases__[0]
raise AttributeError(name)
_GetMethodInfo = classmethod(_GetMethodInfo)
def __setattr__(self,*args):
if self._stub is not None:
raise Exception("Managed object attributes are read-only")
else:
object.__setattr__(self,*args)
__delattr__ = __setattr__
if _allowGetSet == True:
def __getattr__(self, name):
if name.startswith("Get"):
return lambda : getattr(self, name[3].lower() + name[4:])
elif name.startswith("Set"):
return lambda val: setattr(self, name[3].lower() + name[4:], val)
raise AttributeError(name)
## The equality test of ManagedObject is for client side only and might
# not be appropriate for server side objects. The server side object has
# to override this function if it has a different equality logic
def __eq__(self, other):
if other is None:
return False
else:
return self._moId == other._moId and \
self.__class__ == other.__class__ and \
self._serverGuid == other._serverGuid
def __hash__(self):
return str(self).__hash__()
__str__ = __repr__ = FormatObject
_GetPropertyInfo = classmethod(GetPropertyInfo)
## VMOMI Data Object class
class DataObject(Base):
_wsdlName = "DataObject"
_propInfo = {}
_propList = []
_version = BASE_VERSION
## Constructor
#
# @param info property info
# @param ... keyword arguments indicate properties
def __init__(self, **kwargs):
for info in self._GetPropertyList():
if issubclass(info.type, list):
SetAttr(self, info.name, info.type())
elif info.flags & F_OPTIONAL:
SetAttr(self, info.name, None)
elif info.type is bool:
SetAttr(self, info.name, False)
elif issubclass(info.type, Enum):
SetAttr(self, info.name, None)
elif issubclass(info.type, str):
SetAttr(self, info.name, "")
elif info.type is long or \
issubclass(info.type, int) or \
issubclass(info.type, float):
# Take care of byte, short, int, long, float and double
SetAttr(self, info.name, info.type(0))
else:
SetAttr(self, info.name, None)
for (k, v) in kwargs.items():
setattr(self, k, v)
## Get a list of all properties of this type and base types
#
# @param cls the data object type
def _GetPropertyList(cls, includeBaseClassProps=True):
if not includeBaseClassProps:
return cls._propList
prop = {}
result = []
while cls != DataObject:
# Iterate through props, add info for prop not found in derived class
result = [info for info in cls._propList
if prop.setdefault(info.name, cls) == cls] + result
cls = cls.__bases__[0]
return result
_GetPropertyList = classmethod(_GetPropertyList)
def __setattr__(self, name, val):
CheckField(self._GetPropertyInfo(name), val)
SetAttr(self, name, val)
if _allowGetSet == True:
def __getattr__(self, name):
if name.startswith("Get"):
return lambda : getattr(self, name[3].lower() + name[4:])
elif name.startswith("Set"):
return lambda val: setattr(self, name[3].lower() + name[4:], val)
raise AttributeError(name)
_GetPropertyInfo = classmethod(GetPropertyInfo)
__str__ = __repr__ = FormatObject
## Base class for enum types
class Enum(str): pass
## Base class for array types
class Array(list):
__str__ = __repr__ = FormatObject
## Class for curried function objects
#
# Instances of this class are curried python function objects.
# If g = Curry(f, a1,...,an), then g(b1,...,bm) = f(a1,...,an, b1,...,bm)
class Curry(object):
## Constructor
#
# @param self self
# @param f the function object
# @param args arguments to fix
def __init__(self, f, *args):
self.f = f
self.args = args
def __call__(self, *args, **kwargs):
args = self.args + args
return self.f(*args, **kwargs)
def __get__(self, obj, type):
if obj:
# curried methods will receive 'self' *after* any fixed arguments
return lambda *args, **kwargs: \
self.f(*(self.args + (obj,) + args), **kwargs)
return self
## Class for managed object methods
class ManagedMethod(Curry):
## Constructor
#
# @param self self
# @param info method info
def __init__(self, info):
Curry.__init__(self, ManagedObject._InvokeMethod, info)
self.info = info
## Create the vmodl.MethodFault type
#
# This type must be generated dynamically because it inherits from
# vmodl.DynamicData, which is created dynamically by the emitted code.
#
# @return the new type
def CreateAndLoadMethodFaultType():
with _lazyLock:
props = [["msg", "string", BASE_VERSION, F_OPTIONAL],
["faultCause", "vmodl.MethodFault", "vmodl.version.version1", F_OPTIONAL],
["faultMessage", "vmodl.LocalizableMessage[]", "vmodl.version.version1", F_OPTIONAL]]
propInfo = {}
propList = [ LazyObject(name=p[0], typeName=p[1], version=p[2], flags=p[3])
for p in props ]
dic = {"_wsdlName" : "MethodFault", "_propInfo" : propInfo,
"_propList" : propList, "_version" : BASE_VERSION}
for info in propList:
propInfo[info.name] = info
name = "vmodl.MethodFault"
CreateDataType("vmodl.MethodFault", "MethodFault", "vmodl.DynamicData", BASE_VERSION, props)
return _AddType(type(Exception)(name,
(GetWsdlType(XMLNS_VMODL_BASE, "DynamicData"), Exception),
dic))
# If the name of nested class of vmodl type is same as one of the nested classes
# of its parent, then we have to replace it. Else it won't be possible to intercept
# it with LazyType class
# @param vmodl type
# @param parent of the vmodl type
# @return vmodl type
def _CheckNestedClasses(typ, parent):
with _lazyLock:
vmodlName = typ.__name__
nestedClasses = _dependencyMap.get(vmodlName, [])
for nestedClass in nestedClasses:
if hasattr(parent, nestedClass):
setattr(typ, nestedClass, GetVmodlType(vmodlName + "." + nestedClass))
return typ
## Create and Load a data object type at once
#
# @param vmodlName the VMODL name of the type
# @param wsdlName the WSDL name of the type
# @param parent the VMODL name of the parent type
# @param version the version of the type
# @param props properties of the type
# @return vmodl type
def CreateAndLoadDataType(vmodlName, wsdlName, parent, version, props):
CreateDataType(vmodlName, wsdlName, parent, version, props)
return LoadDataType(vmodlName, wsdlName, parent, version, props)
## Create a data object type
#
# @param vmodlName the VMODL name of the type
# @param wsdlName the WSDL name of the type
# @param parent the VMODL name of the parent type
# @param version the version of the type
# @param props properties of the type
def CreateDataType(vmodlName, wsdlName, parent, version, props):
with _lazyLock:
dic = [vmodlName, wsdlName, parent, version, props]
names = vmodlName.split(".")
if _allowCapitalizedNames:
vmodlName = ".".join(name[0].lower() + name[1:] for name in names)
_AddToDependencyMap(names)
typeNs = GetWsdlNamespace(version)
_dataDefMap[vmodlName] = dic
_wsdlDefMap[(typeNs, wsdlName)] = dic
_wsdlTypeMapNSs.add(typeNs)
## Load a data object type
# This function also loads the parent of the type if it's not loaded yet
#
# @param vmodlName the VMODL name of the type
# @param wsdlName the WSDL name of the type
# @param parent the VMODL name of the parent type
# @param version the version of the type
# @param props properties of the type
# @return the new data object type
def LoadDataType(vmodlName, wsdlName, parent, version, props):
with _lazyLock:
# Empty lists are saved as None in globals maps as it is much more memory
# efficient. PythonStubEmitter files emit empty lists as None.
if props is None:
props = []
propInfo = {}
propList = []
for p in props:
# DataObject Property does not contain the privilege for emitted types.
# However, DynamicTypeConstructor from DynamicTypeManagerHelper.py creates
# DataTypes with properties containing privilege id.
name, typeName, propVersion, flags = p[:4]
if flags & F_LINK:
if typeName.endswith("[]"):
linkType = "Link[]"
else:
linkType = "Link"
obj = LazyObject(name=name, typeName=linkType, version=propVersion,
flags=flags, expectedType=typeName)
else:
obj = LazyObject(name=name, typeName=typeName, version=propVersion,
flags=flags)
propList.append(obj)
dic = {"_wsdlName" : wsdlName, "_propInfo" : propInfo,
"_propList" : propList, "_version" : version}
for info in propList:
propInfo[info.name] = info
name = vmodlName
parent = GetVmodlType(parent)
result = _AddType(LazyType(name, (parent,), dic))
# MethodFault and RuntimeFault are builtin types, but MethodFault extends
# DynamicData, which is (erroneously?) an emitted type, so we can't create
# MethodFault and RuntimeFault until we have loaded DynamicData
if wsdlName == "DynamicData":
CreateAndLoadMethodFaultType()
CreateAndLoadDataType("vmodl.RuntimeFault", "RuntimeFault",
"vmodl.MethodFault", BASE_VERSION, [])
# Strictly speaking LocalizedMethodFault is not a data object type
# (it can't be used in VMODL) But it can be treated as a data object for
# (de)serialization purpose
CreateAndLoadDataType("vmodl.LocalizedMethodFault", "LocalizedMethodFault",
"vmodl.MethodFault", BASE_VERSION,
[("fault", "vmodl.MethodFault", BASE_VERSION, 0),
("localizedMessage", "string", BASE_VERSION, F_OPTIONAL),
])
return _CheckNestedClasses(result, parent)
## Create and Load a managed object type at once
#
# @param vmodlName the VMODL name of the type
# @param wsdlName the WSDL name of the type
# @param parent the VMODL name of the parent type
# @param version the version of the type
# @param props properties of the type
# @param methods methods of the type
# @return vmodl type
def CreateAndLoadManagedType(vmodlName, wsdlName, parent, version, props, methods):
CreateManagedType(vmodlName, wsdlName, parent, version, props, methods)
return LoadManagedType(vmodlName, wsdlName, parent, version, props, methods)
## Create a managed object type
#
# @param vmodlName the VMODL name of the type
# @param wsdlName the WSDL name of the type
# @param parent the VMODL name of the parent type
# @param version the version of the type
# @param props properties of the type
# @param methods methods of the type
def CreateManagedType(vmodlName, wsdlName, parent, version, props, methods):
with _lazyLock:
dic = [vmodlName, wsdlName, parent, version, props, methods]
names = vmodlName.split(".")
if _allowCapitalizedNames:
vmodlName = ".".join(name[0].lower() + name[1:] for name in names)
_AddToDependencyMap(names)
typeNs = GetWsdlNamespace(version)
if methods:
for meth in methods:
_SetWsdlMethod(typeNs, meth[1], dic)
_managedDefMap[vmodlName] = dic
_wsdlDefMap[(typeNs, wsdlName)] = dic
_wsdlTypeMapNSs.add(typeNs)
## Load a managed object type
# This function also loads the parent of the type if it's not loaded yet
#
# @param vmodlName the VMODL name of the type
# @param wsdlName the WSDL name of the type
# @param parent the VMODL name of the parent type
# @param version the version of the type
# @param props properties of the type
# @param methods methods of the type
# @return the new managed object type
def LoadManagedType(vmodlName, wsdlName, parent, version, props, methods):
with _lazyLock:
# Empty lists are saved as None in globals maps as it is much more memory
# efficient. PythonStubEmitter files emit empty lists as None.
if props is None:
props = []
if methods is None:
methods = []
parent = GetVmodlType(parent)
propInfo = {}
methodInfo = {}
propList = [LazyObject(name=p[0], typeName=p[1], version=p[2], flags=p[3],
privId=p[4]) for p in props]
dic = {"_wsdlName" : wsdlName, "_propInfo" : propInfo,
"_propList" : propList,
"_methodInfo" : methodInfo, "_version" : version}
for info in propList:
propInfo[info.name] = info
getter = Curry(ManagedObject._InvokeAccessor, info)
dic[info.name] = property(getter)
for (mVmodl, mWsdl, mVersion, mParams, mResult, mPrivilege, mFaults) in methods:
if mFaults is None:
mFaults = []
mName = Capitalize(mVmodl)
isTask = False
if mName.endswith("_Task"):
mName = mName[:-5]
isTask = True
params = tuple([LazyObject(name=p[0], typeName=p[1], version=p[2], flags=p[3],
privId=p[4]) for p in mParams])
info = LazyObject(name=mName, typeName=vmodlName, wsdlName=mWsdl,
version=mVersion, params=params, isTask=isTask,
resultFlags=mResult[0], resultName=mResult[1],
methodResultName=mResult[2], privId=mPrivilege, faults=mFaults)
methodInfo[mName] = info
mm = ManagedMethod(info)
ns = GetWsdlNamespace(info.version)
method = _SetWsdlMethod(ns, info.wsdlName, mm)
if method != mm:
raise RuntimeError(
"Duplicate wsdl method %s %s (new class %s vs existing %s)" % \
(ns, info.wsdlName, mm.info.type, method.info.type))
dic[mWsdl] = mm
dic[mName] = mm
name = vmodlName
result = _AddType(LazyType(name, (parent,) , dic))
return _CheckNestedClasses(result, parent)
## Create an enum type
#
# @param vmodlName the VMODL name of the type
# @param wsdlName the WSDL name of the type
# @param version the version of the type
# @param values enum values
# @return vmodl type
def CreateAndLoadEnumType(vmodlName, wsdlName, version, values):
CreateEnumType(vmodlName, wsdlName, version, values)
return LoadEnumType(vmodlName, wsdlName, version, values)
## Create an enum type
#
# @param vmodlName the VMODL name of the type
# @param wsdlName the WSDL name of the type
# @param version the version of the type
# @param values enum values
def CreateEnumType(vmodlName, wsdlName, version, values):
with _lazyLock:
dic = [vmodlName, wsdlName, version, values]
names = vmodlName.split(".")
if _allowCapitalizedNames:
vmodlName = ".".join(name[0].lower() + name[1:] for name in names)
_AddToDependencyMap(names)
typeNs = GetWsdlNamespace(version)
_enumDefMap[vmodlName] = dic
_wsdlDefMap[(typeNs, wsdlName)] = dic
_wsdlTypeMapNSs.add(typeNs)
## Load an enum type
#
# @param vmodlName the VMODL name of the type
# @param wsdlName the WSDL name of the type
# @param version the version of the type
# @param values enum values
# @return the new enum type
def LoadEnumType(vmodlName, wsdlName, version, values):
with _lazyLock:
name = vmodlName
# Enum type cannot have nested classes. So, creating normal type
# instead of LazyType
result = type(name, (Enum,),
{"_wsdlName" : wsdlName, "_version" : version})
result.values = map(result, values)
for value in result.values:
setattr(result, value, value)
return _AddType(result)
## Create an array type
#
# @param itemType the item type
# @return the new array type
def CreateArrayType(itemType):
return type("%s[]" % itemType.__name__, (Array,), {'Item' : itemType})
## Add a new type to the type maps, create array constructors
# Note: Must be holding the _lazyLock, or in main init path
#
# @param type the type object
# @return type
def _AddType(type):
""" Note: Must be holding the _lazyLock, or in main init path """
type.Array = CreateArrayType(type)
typeNS = GetWsdlNamespace(type._version)
newType = _SetWsdlType(typeNS, type._wsdlName, type)
if newType != type:
raise RuntimeError("Duplicate wsdl type %s (already in typemap)" % (type._wsdlName))
return type
## Check that a value matches a given type, and annotate if neccesary
#
# @param info object containing of expected type
# @param val object to check
# @throw TypeError if the value does not match the type
def CheckField(info, val):
with _lazyLock:
valType = Type(val)
if val is None or (isinstance(val, list) and len(val) == 0):
# If type of the property is an Any. We should allow this to have
# unset items
if not (info.flags & F_OPTIONAL) and info.type is not object:
raise TypeError('Required field "%s" not provided (not @optional)' % info.name)
return
elif info.type is object:
try:
GetQualifiedWsdlName(valType)
return
except KeyError:
raise TypeError('Unknown type for %s' % info.type.__name__)
elif isinstance(val, info.type):
return
elif issubclass(info.type, list):
# Checking the values of VMOMI array types is surprisingly complicated....
if isinstance(val, Array):
# 1. We've got a PyVmomi Array object, which is effectively a typed list;
# verify that the type of the Array is a subclass of the expected type.
if issubclass(valType.Item, info.type.Item):
return
elif info.flags & F_LINK:
# Allow objects of expected type to be assigned to links
if issubclass(valType, GetVmodlType(info.expectedType)):
return
elif val:
# 2. We've got a non-empty Python list object, which is untyped;
# walk the list and make sure that each element is a subclass
# of the expected type.
# Masking out F_OPTIONAL part of flags since we are checking for
# each element of the list
flags = info.flags & (F_LINKABLE | F_LINK)
if flags & F_LINK:
if info.expectedType.endswith('[]'):
expectedType = info.expectedType[:-2]
else:
expectedType = info.expectedType
itemInfo = Object(type=info.type.Item, name=info.name, flags=flags,
expectedType=expectedType)
else:
itemInfo = Object(type=info.type.Item, name=info.name, flags=flags)
for it in val:
CheckField(itemInfo, it)
return
else:
# 3. We've got None or an empty Python list object;
# no checking required, since the result will be an empty array.
return
elif info.type is type and valType is type(Exception) \
or issubclass(info.type, int) and issubclass(valType, int) \
or issubclass(info.type, long) and (issubclass(valType, int) or \
issubclass(valType, long)) \
or issubclass(info.type, float) and issubclass(valType, float) \
or issubclass(info.type, basestring) and issubclass(valType, basestring):
return
elif issubclass(info.type, Link):
# Allow object of expected type to be assigned to link
if issubclass(valType, GetVmodlType(info.expectedType)):
return
raise TypeError('For "%s" expected type %s, but got %s'
% (info.name, info.type.__name__, valType.__name__))
## Finalize a created type
#
# @param type a created type
def FinalizeType(type):
if issubclass(type, DataObject):
for info in type._propList:
info.type = GetVmodlType(info.type)
elif issubclass(type, ManagedObject):
for info in type._propInfo.values():
info.type = GetVmodlType(info.type)
for info in type._methodInfo.values():
info.result = GetVmodlType(info.result)
info.methodResult = GetVmodlType(info.methodResult)
info.type = GetVmodlType(info.type)
for param in info.params:
param.type = GetVmodlType(param.type)
## Get the type of an object, for both new and old-style classes
def Type(obj):
try:
return obj.__class__
except AttributeError:
return type(obj)
## Set a WSDL type with wsdl namespace and wsdl name
# Internal to VmomiSupport
#
# Note: Must be holding the _lazyLock, or in main init path
def _SetWsdlType(ns, wsdlName, typ):
"""
Set a WSDL type with wsdl namespace and wsdl name.
Returns added type / existing type if (ns, wsdlName) already in the map
Note: Must be holding the _lazyLock, or in main init path
"""
return _wsdlTypeMap.setdefault((ns, wsdlName), typ)
## Lookup a WSDL type from wsdl namespace and wsdl name
# @param ns XML namespace
# @param name wsdl name
# @return type if found else throws KeyError
def GetWsdlType(ns, name):
if ns is None or name is None:
raise KeyError("{0} {1}".format(ns, name))
with _lazyLock:
# Check if the type is loaded in the map
typ = _wsdlTypeMap.get( (ns, name) )
if typ:
return typ
# It is an array type, get the actual type and return the array
elif name.startswith("ArrayOf"):
try:
return GetWsdlType(ns, name[7:]).Array
except KeyError:
raise KeyError("{0} {1}".format(ns, name))
else:
# Type is not loaded yet, load it
typ = _LoadVmodlType(_wsdlDefMap[(ns, name)][0])
if typ:
return typ
raise KeyError("{0} {1}".format(ns, name))
class UnknownWsdlTypeError(KeyError):
# NOTE (hartsock): KeyError is extended here since most logic will be
# looking for the KeyError type. I do want to distinguish malformed WSDL
# errors as a separate classification of error for easier bug reports.
pass
## Guess the type from wsdlname with no ns
# WARNING! This should not be used in general, as there is no guarantee for
# the correctness of the guessing type
# @param name wsdl name
# @return type if found in any one of the name spaces else throws KeyError
def GuessWsdlType(name):
with _lazyLock:
# Because the types are lazily loaded, if some name is present
# in multiple namespaces, we will load the first type that we
# encounter and return it.
for ns in _wsdlTypeMapNSs:
try:
return GetWsdlType(ns, name)
except KeyError:
pass
raise UnknownWsdlTypeError(name)
## Return a map that contains all the wsdl types
# This function is rarely used
# By calling GetWsdlType on all wsdl names, we will
# make sure that the types are loaded before returning
# the iterator
# @return iterator to the wsdl type map
def GetWsdlTypes():
with _lazyLock:
for ns, name in _wsdlDefMap:
GetWsdlType(ns, name)
return itervalues(_wsdlTypeMap)
## Get the qualified XML schema name (ns, name) of a type
def GetQualifiedWsdlName(type):
with _lazyLock:
wsdlNSAndName = _wsdlNameMap.get(type)
if wsdlNSAndName:
return wsdlNSAndName
else:
if issubclass(type, list):
ns = GetWsdlNamespace(type.Item._version)
return (ns, "ArrayOf" + Capitalize(type.Item._wsdlName))
else:
ns = GetWsdlNamespace(type._version)
return (ns, type._wsdlName)
## Get the WSDL of a type
def GetWsdlName(type):
return GetQualifiedWsdlName(type)[-1]
## Capitalize a string
def Capitalize(str):
if str:
return str[0].upper() + str[1:]
return str
## Uncapitalize a string
def Uncapitalize(str):
if str:
return str[0].lower() + str[1:]
return str
## To uncapitalize the entire vmodl name
# pyVmomi used to map Java package names to capitalized Python module names,
# but now maps the Java package names unchanged to Python module names.
# This function is needed to support the legacy name mapping.
def UncapitalizeVmodlName(str):
if str:
return ".".join(name[0].lower() + name[1:] for name in str.split("."))
return str
## Add a parent version
def AddVersionParent(version, parent):
parentMap[version][parent] = True
## Get version namespace from version
def GetVersionNamespace(version):
""" Get version namespace from version """
ns = nsMap[version]
if not ns:
ns = serviceNsMap[version]
versionId = versionIdMap[version]
if not versionId:
namespace = ns
else:
namespace = '%s/%s' % (ns, versionId)
return namespace
## Get version from the version uri
def GetVersionFromVersionUri(version):
return versionMap[version.rsplit(":", 1)[-1]]
## Get wsdl namespace from version
def GetWsdlNamespace(version):
""" Get wsdl namespace from version """
return "urn:" + serviceNsMap[version]
## Get all the versions for the service with specified namespace (partially) ordered
## by compatibility (i.e. any version in the list that is compatible with some version
## v in the list will preceed v)
# @param namespace XML namespace identifying a service
# @return returns all the versions for the service with specified namespace (partially)
# ordered by compatibility
#
# NOTE: For this function, we use 'namespace' as a representation of 'service'. While
# this works for most services, for compatibility reasons, the core and query
# services share the 'vim25' namespace with the vim service. Fortunately, this
# shouldn't be an issue in practice, as the implementations of the vim
# service (vpxd and hostd) don't currently advertise that they support any
# versions of the core or query services, and we don't expect that they ever will.
# This function assumes that all other namespaces identify a unique service.
def GetServiceVersions(namespace):
"""
Get all the versions for the service with specified namespace (partially) ordered
by compatibility (i.e. any version in the list that is compatible with some version
v in the list will preceed v)
"""
versions = dict((v, True) for (v, n) in iteritems(serviceNsMap) if n == namespace)
mappings = {}
for v in iterkeys(versions):
mappings[v] = set(parent for parent in iterkeys(parentMap[v])
if parent != v and parent in versions.keys())
res = []
while True:
el = [ k for (k, v) in iteritems(mappings) if len(v) == 0 ]
if len(el) == 0:
return res
el.sort()
for k in el:
res.insert(0, k)
del mappings[k]
for values in itervalues(mappings):
values.discard(k)
## Set a WSDL method with wsdl namespace and wsdl name
# Internal to VmomiSupport
# Note: Must be holding the _lazyLock
#
# @param ns XML namespace
# @param wsdlName wsdl name
# @param inputMM managed method object or info to load it (it points to
# list object that points to the type info which holds
# this managed method's information)
# @return returns added method or exising method if (ns, wsdlName)
# is already in the map. It throws a runtime error if
# trying to set two type info list's to the same (ns, wsdlName)
def _SetWsdlMethod(ns, wsdlName, inputMM):
"""
Set a WSDL method with wsdl namespace and wsdl name
Returns added method / existing method if (ns, wsdlName) already in the map
Note: Must be holding the _lazyLock
"""
_wsdlMethodNSs.add(ns)
curMM = _wsdlMethodMap.get( (ns, wsdlName) )
# if inputMM is a list
if isinstance(inputMM, list):
if curMM is None:
_wsdlMethodMap[(ns, wsdlName)] = inputMM
return inputMM
elif isinstance(curMM, list):
raise RuntimeError(
"Duplicate wsdl method %s %s (new class %s vs existing %s)" % \
(ns, wsdlName, inputMM[0], curMM[0]))
else:
return curMM
# if inputMM is a ManagedMethod
else:
if curMM is None or isinstance(curMM, list):
_wsdlMethodMap[(ns, wsdlName)] = inputMM
return inputMM
else:
return curMM
## Get wsdl method from ns, wsdlName
# @param ns XML namespace
# @param wsdlName wsdl name
# @return managed method object or throws a KeyError
def GetWsdlMethod(ns, wsdlName):
""" Get wsdl method from ns, wsdlName """
with _lazyLock:
method = _wsdlMethodMap[(ns, wsdlName)]
if isinstance(method, ManagedMethod):
# The type corresponding to the method is loaded,
# just return the method object
return method
elif method:
# The type is not loaded, the map contains the info
# to load the type. Load the actual type and
# return the method object
LoadManagedType(*method)
return _wsdlMethodMap[(ns, wsdlName)]
else:
raise KeyError("{0} {1}".format(ns, name))
## Guess the method from wsdlname with no ns
# WARNING! This should not be used in general, as there is no guarantee for
# the correctness of the guessing method
# @param name wsdl name
# @return managed method object if found in any namespace else throws
# KeyError
def GuessWsdlMethod(name):
with _lazyLock:
for ns in _wsdlMethodNSs:
try:
return GetWsdlMethod(ns, name)
except KeyError:
pass
raise KeyError(name)
## Widen a type to one supported in a given version
def GetCompatibleType(type, version):
# Type can be widened if it has the attribute "_version" (which implies it
# is either a DataObject or ManagedObject)
if hasattr(type, "_version"):
while not IsChildVersion(version, type._version):
type = type.__bases__[0]
return type
## Invert an injective mapping
def InverseMap(map):
return dict([ (v, k) for (k, v) in iteritems(map) ])
## Support for build-time versions
class _BuildVersions:
def __init__(self):
self._verMap = {}
self._nsMap = {}
def Add(self, version):
vmodlNs = version.split(".",1)[0]
if not (vmodlNs in self._verMap):
self._verMap[vmodlNs] = version
if not (vmodlNs in self._nsMap):
self._nsMap[vmodlNs] = GetVersionNamespace(version)
def Get(self, vmodlNs):
return self._verMap[vmodlNs]
def GetNamespace(self, vmodlNs):
return self._nsMap[vmodlNs]
types = Object()
nsMap = {}
versionIdMap = {}
versionMap = {}
serviceNsMap = { BASE_VERSION : XMLNS_VMODL_BASE.split(":")[-1] }
parentMap = {}
newestVersions = _BuildVersions()
currentVersions = _BuildVersions()
stableVersions = _BuildVersions()
matureVersions = _BuildVersions()
publicVersions = _BuildVersions()
oldestVersions = _BuildVersions()
from pyVmomi.Version import AddVersion, IsChildVersion
if not isinstance(bool, type): # bool not a type in python <= 2.2
bool = type("bool", (int,),
{"__new__": lambda cls, val=0: int.__new__(cls, val and 1 or 0)})
byte = type("byte", (int,), {})
short = type("short", (int,), {})
double = type("double", (float,), {})
URI = type("URI", (str,), {})
binary = type("binary", (str,), {})
PropertyPath = type("PropertyPath", (text_type,), {})
# _wsdlTypeMapNSs store namespaces added to _wsdlTypeMap in _SetWsdlType
_wsdlTypeMapNSs = set()
_wsdlTypeMap = {
# Note: xsd has no void type. This is a hack from before. Can be removed?
(XMLNS_XSD, 'void') : NoneType,
(XMLNS_XSD, 'anyType') : object,
(XMLNS_XSD, 'boolean') : bool,
(XMLNS_XSD, 'byte') : byte,
(XMLNS_XSD, 'short') : short,
(XMLNS_XSD, 'int') : int,
(XMLNS_XSD, 'long') : long,
(XMLNS_XSD, 'float') : float,
(XMLNS_XSD, 'double') : double,
(XMLNS_XSD, 'string') : str,
(XMLNS_XSD, 'anyURI') : URI,
(XMLNS_XSD, 'base64Binary') : binary,
(XMLNS_XSD, 'dateTime') : datetime,
(XMLNS_XSD, 'Link') : Link,
(XMLNS_VMODL_BASE, 'TypeName') : type,
(XMLNS_VMODL_BASE, 'MethodName') : ManagedMethod,
(XMLNS_VMODL_BASE, 'PropertyPath') : PropertyPath
}
_wsdlNameMap = InverseMap(_wsdlTypeMap)
for ((ns, name), typ) in iteritems(dict(_wsdlTypeMap)):
if typ is not NoneType:
setattr(types, typ.__name__, typ)
_wsdlTypeMapNSs.add(ns)
arrayType = CreateArrayType(typ)
setattr(types, Capitalize(typ.__name__) + "Array", arrayType)
arrayName = "ArrayOf" + Capitalize(name)
arrayNS = XMLNS_VMODL_BASE
_SetWsdlType(arrayNS, arrayName, arrayType)
_wsdlNameMap[arrayType] = (arrayNS, arrayName)
del name, typ
# unicode is mapped to wsdl name 'string' (Cannot put in wsdlTypeMap or name
# collision with non-unicode string)
_wsdlNameMap[text_type] = (XMLNS_XSD, 'string')
_wsdlNameMap[CreateArrayType(text_type)] = (XMLNS_VMODL_BASE, 'ArrayOfString')
# _wsdlMethodNSs store namespaces added to _wsdlMethodMap in _SetWsdlMethod
_wsdlMethodNSs = set()
_wsdlMethodMap = {}
# Registering the classes defined in VmomiSupport in the definition maps
CreateManagedType(ManagedObject.__name__, ManagedObject._wsdlName, None,
ManagedObject._version, [], [])
_AddType(ManagedObject)
setattr(types, ManagedObject.__name__, ManagedObject)
CreateDataType(DataObject.__name__, DataObject._wsdlName, None,
DataObject._version, [])
_AddType(DataObject)
setattr(types, DataObject.__name__, DataObject)
## Vmodl types
vmodlTypes = {
# Note: xsd has no void type. This is a hack from before. Can be removed?
"void" : GetWsdlType(XMLNS_XSD, 'void'),
"anyType": GetWsdlType(XMLNS_XSD, 'anyType'),
"string" : GetWsdlType(XMLNS_XSD, 'string'),
"bool" : GetWsdlType(XMLNS_XSD, 'boolean'),
"boolean": GetWsdlType(XMLNS_XSD, 'boolean'),
"byte" : GetWsdlType(XMLNS_XSD, 'byte'),
"short" : GetWsdlType(XMLNS_XSD, 'short'),
"int" : GetWsdlType(XMLNS_XSD, 'int'),
"long" : GetWsdlType(XMLNS_XSD, 'long'),
"float" : GetWsdlType(XMLNS_XSD, 'float'),
"double" : GetWsdlType(XMLNS_XSD, 'double'),
"Link" : GetWsdlType(XMLNS_XSD, 'Link'),
"vmodl.URI" : GetWsdlType(XMLNS_XSD, 'anyURI'),
"vmodl.Binary" : GetWsdlType(XMLNS_XSD, 'base64Binary'),
"vmodl.DateTime" : GetWsdlType(XMLNS_XSD, 'dateTime'),
"vmodl.TypeName" : GetWsdlType(XMLNS_VMODL_BASE, 'TypeName'),
"vmodl.MethodName" : GetWsdlType(XMLNS_VMODL_BASE, 'MethodName'),
"vmodl.DataObject" : GetWsdlType(XMLNS_VMODL_BASE, 'DataObject'),
"vmodl.ManagedObject" : GetWsdlType(XMLNS_VMODL_BASE, 'ManagedObject'),
"vmodl.PropertyPath" : GetWsdlType(XMLNS_VMODL_BASE, 'PropertyPath'),
}
vmodlNames = {}
## Add array type into special names
for name, typ in vmodlTypes.copy().items():
if typ is not NoneType:
try:
arrayType = typ.Array
except AttributeError:
wsdlName = GetWsdlName(typ)
arrayNS = XMLNS_VMODL_BASE
arrayType = GetWsdlType(arrayNS, "ArrayOf" + Capitalize(wsdlName))
arrayName = name + "[]"
vmodlTypes[arrayName] = arrayType
# Set type to vmodl name map
vmodlNames[typ] = name
vmodlNames[arrayType] = arrayName
del name, typ
## Get type from vmodl name
#
# @param name vmodl name
# @return vmodl type
def GetVmodlType(name):
""" Get type from vmodl name """
# If the input is already a type, just return
if isinstance(name, type):
return name
# Try to get type from vmodl type names table
typ = vmodlTypes.get(name)
if typ:
return typ
# Else get the type from the _wsdlTypeMap
isArray = name.endswith("[]")
if isArray:
name = name[:-2]
ns, wsdlName = _GetWsdlInfo(name)
try:
typ = GetWsdlType(ns, wsdlName)
except KeyError:
raise KeyError(name)
if typ:
return isArray and typ.Array or typ
else:
raise KeyError(name)
## Get VMODL type name from type
#
# @param typ vmodl type
# @return vmodl name
def GetVmodlName(typ):
""" Get vmodl type name from type """
try:
return vmodlNames[typ]
except KeyError:
return typ.__name__
## Get Wsdl type name from Python type name
#
# @param pythonTypeName Python type name
# @return wsdl type name
def GetWsdlTypeName(pythonTypeName):
try:
typ = GetVmodlType(pythonTypeName)
except KeyError:
raise NameError('No type found with name ' + pythonTypeName)
return GetWsdlName(typ)
## Get Wsdl method name from Python method name
#
# @param pythonTypeName Python type name
# @param pythonMethodName Python method name
# @return wsdl method name
def GetWsdlMethodName(pythonTypeName, pythonMethodName):
try:
typ = GetVmodlType(pythonTypeName)
_, _, _, _, _, methods = _wsdlDefMap[GetQualifiedWsdlName(typ)]
except KeyError:
raise NameError('No type found with name ' + pythonTypeName)
uncapPythonMethodName = Uncapitalize(pythonMethodName)
for method in methods:
mVmodl, mWsdl, _, _, _, _, _ = method
if mVmodl == uncapPythonMethodName or mVmodl == pythonMethodName:
return mWsdl
raise NameError('No method found with name ' + pythonMethodName)
## Get Python type name from Wsdl type name
#
# @param ns wsdl namespace
# @param wsdlTypeName wsdl type name
# @return python type name
def GetPythonTypeName(wsdlTypeName, ns):
try:
typ = GetWsdlType(ns, wsdlTypeName)
except KeyError:
raise NameError('No type found with namespace %s and name %s' % (ns, wsdlTypeName))
return GetVmodlName(typ)
## Get Python method name from Wsdl method name
#
# @param ns wsdl namespace
# @param wsdlTypeName wsdl type name
# @param wsdlMethodName wsdl method name
# @return python method name
def GetPythonMethodName(wsdlTypeName, ns, wsdlMethodName):
try:
_, _, _, _, _, methods = _wsdlDefMap[(ns, wsdlTypeName)]
except KeyError:
raise NameError('No type found with namespace %s and name %s' % (ns, wsdlTypeName))
for method in methods:
mVmodl, mWsdl, _, _, _, _, _ = method
if mWsdl == wsdlMethodName:
return Capitalize(mVmodl)
raise NameError('No method found with name ' + wsdlMethodName)
## String only dictionary: same as dict, except it only accept string as value
#
class StringDict(dict):
"""
String only dictionary: same as dict, except it only accept string as value
dict in python is kind of strange. U cannot just override __setitem__, as
__init__, update, and setdefault all bypass __setitem__. When override,
we have to override all three together
"""
def __init__(self, *args, **kwargs):
dict.__init__(self)
self.update(*args, **kwargs)
# Same as dict setdefault, except this will call through our __setitem__
def update(self, *args, **kwargs):
for k, v in iteritems(dict(*args, **kwargs)):
self[k] = v
# Same as dict setdefault, except this will call through our __setitem__
def setdefault(self, key, val=None):
if key in self:
return self[key]
else:
self[key] = val
return val
def __setitem__(self, key, val):
"""x.__setitem__(i, y) <==> x[i]=y, where y must be a string"""
if not isinstance(val, basestring):
raise TypeError("key %s has non-string value %s of %s" %
(key, val, type(val)))
return dict.__setitem__(self, key, val)
## Retrieves the actual vmodl name from type dictionaries
#
# Note: Must be holding the _lazyLock
# @param name upcapitalized vmodl name
# @return vmodl name
def _GetActualName(name):
""" Note: Must be holding the _lazyLock """
if _allowCapitalizedNames:
name = UncapitalizeVmodlName(name)
for defMap in _dataDefMap, _managedDefMap, _enumDefMap:
dic = defMap.get(name)
if dic:
return dic[0]
return None
## Retrieves the actual wsdl name from type dictionaries
#
# @param name upcapitalized vmodl name
# @return (wsdl namespace, wsdl name)
def _GetWsdlInfo(name):
if _allowCapitalizedNames:
name = UncapitalizeVmodlName(name)
with _lazyLock:
# For data and managed objects, emitter puts version in field #3 and in
# enum objects, it is in field #2. So, have to handle them differently
for defMap in _dataDefMap, _managedDefMap:
dic = defMap.get(name)
if dic:
return GetWsdlNamespace(dic[3]), dic[1]
dic = _enumDefMap.get(name)
if dic:
return GetWsdlNamespace(dic[2]), dic[1]
return None, None
## Checks if the definition exists for a vmodl name
#
# @param name vmodl name
# @return True if name exists, False otherwise
def TypeDefExists(name):
# Check if is one of the primitive types
typ = vmodlTypes.get(name)
if typ:
return True
# Check if it's type definition is loaded in the dictionaries
if name.endswith("[]"):
name = name[:-2]
with _lazyLock:
actualName = _GetActualName(name)
return actualName is not None
# Thread local for req context
_threadLocalContext = threading.local()
# Get the RequestContext for the current thread
#
def GetRequestContext():
""" Get the RequestContext for the current thread """
global _threadLocalContext
return _threadLocalContext.__dict__.setdefault('reqCtx', StringDict())
# Get the Http context for the current thread
#
def GetHttpContext():
""" Get the Http context for the current thread """
global _threadLocalContext
return _threadLocalContext.__dict__.setdefault('httpCtx', dict())
## Class that resolves links
class LinkResolver:
## Constructor
#
# @param self self
# @param scope DataObject to be used against for resolving links
def __init__(self, scope):
self.linkables = {}
self._VisitDataObject(scope)
## Visit a DataObject and add it to linkable if it is one. Also
# visit its properties that are DataObjects
#
# @param self self
# @param obj DataObject to be visited
def _VisitDataObject(self, obj):
if isinstance(obj, DataObject):
for prop in obj._GetPropertyList():
if issubclass(prop.type, list):
for dataObj in getattr(obj, prop.name):
if (prop.flags & F_LINKABLE):
self._AddLinkable(dataObj)
self._VisitDataObject(dataObj)
else:
dataObj = getattr(obj, prop.name)
if (prop.flags & F_LINKABLE):
self._AddLinkable(dataObj)
self._VisitDataObject(dataObj)
elif isinstance(obj, list):
for dataObj in obj:
self._VisitDataObject(dataObj)
## Adds a DataObject to linkable dictionary using its key
#
# @param self self
# @param obj DataObject to be added to linkable
def _AddLinkable(self, obj):
key = getattr(obj, "key")
if key and key != '':
if self.linkables.has_key(key):
#duplicate key present
raise AttributeError(key)
else:
self.linkables[key] = obj
else:
#empty key
raise AttributeError(key)
## Resolves a key by looking up linkable dictionary
#
# @param self self
# @param key Key to be resolved
def ResolveLink(self, key):
val = self.linkables[key]
return val
## Resolves a list of keys by resolving each key
#
# @param self self
# @param keys keys to be resolved
def ResolveLinks(self, keys):
val = [self.linkables[k] for k in keys]
return val
## Resolves a link key using the object provided as its scope by creating a
# link resolver object
#
# @param key Key to be resolved
# @param obj DataObject to be used against for resolving links
def ResolveLink(key, obj):
if obj is None:
return None
linkResolver = LinkResolver(obj)
return linkResolver.ResolveLink(key)
## Resolves a list of link keys using the object provided as its scope by creating a
# link resolver object
#
# @param keys keys to be resolved
# @param obj DataObject to be used against for resolving links
def ResolveLinks(keys, obj):
if obj is None:
return None
linkResolver = LinkResolver(obj)
return linkResolver.ResolveLinks(keys)