You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

183 lines
6.4 KiB

# Copyright (C) 2005-2023 Splunk Inc. All Rights Reserved.
import vim25.suds_resolver
import suds
import os
import inspect
from hydra import six
from suds.plugin import MessagePlugin
from vim25.mo import ManagedObject, ServiceInstance, ServerConnection
class VimFault(Exception):
def __init__(self, fault):
self.fault = fault
self.fault_type = fault.__class__.__name__
self._fault_dict = {}
if type(fault) == suds.WebFault:
for attr in fault:
self._fault_dict[attr[0]] = attr[1]
Exception.__init__(self, "%s: %s" % (self.fault_type, self._fault_dict))
else:
Exception.__init__(self, "%s" % fault)
class Vim25Client(object):
def __init__(self, server_url=None, debugmode=False, **kwargs):
self.soapClient = suds.client.Client(url="https://"+server_url+"/sdk/vimService?wsdl", **kwargs)
self.soapClient.set_options(location="https://"+server_url+"/sdk")
#For Future use in cache control
#self.soapCache = self.soapClient.options.cache
self.moCache = None
if debugmode:
import logging
logging.basicConfig(filename="suds.log", level=logging.DEBUG)
logging.getLogger('suds.transport').setLevel(logging.DEBUG)
def handle_exception(self, e):
if type(e).__name__ == "WebFault":
if len(e.fault.faultstring) > 0:
raise
detail = e.document.childAtPath("/Envelope/Body/Fault/detail")
fault_type = detail.getChildren()[0].name
fault = self.soapClient.factory.create('ns0:' + fault_type)
if isinstance(e.fault.detail[0], list):
for attr in e.fault.detail[0]:
setattr(fault, attr[0], attr[1])
else:
fault["text"] = e.fault.detail[0]
raise VimFault(fault)
elif isinstance(e, Exception):
raise VimFault(e)
def __getattr__(self, t):
"""
This is a catch-all method for invoking web services methods through WSClient (a suds client).
See the definition of _invoke in WSClient and of service attribute in suds.client.CLient.
Note that the kwargs are transformed to convert managed entity objects (and arrays) into
MORs and MOR arrays.
"""
def vimSvcWrapper(_this, **kwargs):
for arg in kwargs:
if isinstance(kwargs[arg], ManagedObject):
kwargs[arg] = kwargs[arg].getMOR()
elif (isinstance(kwargs[arg], list) and len(kwargs[arg]) > 0
and isinstance(kwargs[arg][0], ManagedObject)):
kwargs[arg] = [mor.getMOR() for mor in kwargs[arg]]
return self.invoke(t, _this, **kwargs)
return vimSvcWrapper
def new (self, t, **kwargs):
obj = self.soapClient.factory.create("ns0:%s" % t)
for n, v in six.iteritems(kwargs):
setattr(obj, n, v)
return obj
def isinstance(self, obj, _type):
return type(obj) == type(_type())
def invoke(self, method, _this, **kwargs):
try:
targetFunc = getattr(self.soapClient.service, method)
result = targetFunc(_this, **kwargs)
except Exception as e:
self.handle_exception(e)
return result
def vim_wrap(self, obj):
if isinstance(obj, list):
return [self.vim_wrap(i) for i in obj]
try:
if isinstance(obj, suds.sudsobject.Object):
members = dict(filter(lambda obj: not obj[0].startswith('__') and not inspect.isroutine(obj[1]), inspect.getmembers(obj)))
return self.new(obj.__class__.__name__, **members)
except:
return obj
return obj
def createServiceInstance(self, server_url=None, username=None, password=None, sessioncookie=None):
# ServiceInstance creates a ServerConnection object during its initialization
# ServerConnection has a reference to this client (in the vimService variable)
# This client also stores a reference to the ServerConnection (self.sc),
# assigned by ServiceInstance during the initialization process
self.serviceInstance = ServiceInstance(self, server_url, username, password, sessioncookie)
def setServerConnection(self, sc):
self.sc = sc
def createMORs(self, mors=[]):
return [mor.getMOR() for mor in mors]
def createExactManagedObject(self, mor):
if not self.moCache:
import vim25.mo
self.moCache = dict(filter(lambda obj: inspect.isclass(obj[1]), inspect.getmembers(vim25.mo)))
return self.moCache.get(mor._type)(self.sc, mor) if mor._type in self.moCache else None
def createExactManagedEntity(self, mor):
return self.createExactManagedObject(mor)
def createExactManagedEntities(self, mors=[]):
return [self.createExactManagedEntity(mor) for mor in mors]
def createManagedEntities(self, mors):
return [self.createExactManagedEntity(mor) for mor in mors] if mors else []
def convertProperty(self, dynaPropVal):
propertyValue = dynaPropVal
# if dynaPropVal is an array type, need to convert into []
return propertyValue
def createObjectSpec(self, mor, skip, selSet):
return self.new('ObjectSpec', obj=mor, skip=skip, selectSet=selSet)
def createPropertySpec(self, tp, allProp, pathSet):
return self.new('PropertySpec', type=tp, all=allProp, pathSet=pathSet)
def createSelectionSpec(self, names):
return [self.new('SelectionSpec', name=name) for name in names]
def createTraversalSpec(self, name, tp, path, selSpec):
sp = selSpec
if len(sp)>0 and isinstance(sp[0], str):
sp = self.createSelectionSpec(sp)
return self.new('TraversalSpec', name=name, type=tp, path=path, skip=False,selectSet=sp)
def buildPropertySpecArray(self, typeProplists):
pSpecs = []
for tp in typeProplists:
tpe = tp[0]
props = tp[1:]
al = len(props)==0
pSpecs.append(self.createPropertySpec(tpe, al, props))
return pSpecs
class SoapFixer(MessagePlugin):
"""
We need this because suds doesn't play well with elements declared as anyType in the WSDL schema.
If we are working with any field labeled as xsd:anyType, we must intercept that SOAP envelope
and set the type to xsd:int or xsd:string, whichever is appropriate.
"""
def marshalled(self, context):
def check_int(s):
if s is None or s == "": return False
if s[0] in ('-', '+'): return s[1:].isdigit()
return s.isdigit()
# TODO: figure out if this is an optimal way to check this path.
# Should we also check that Body/UpdateOptions/_this has type "OptionManager"? E.g.:
# context.envelope.childAtPath("Body/UpdateOptions/_this") is not None and
# context.envelope.childAtPath("Body/UpdateOptions/_this").get('type') == "OptionManager" and
# ......
if context.envelope.childAtPath("Body/UpdateOptions/changedValue") is not None:
for elt in context.envelope.childrenAtPath("Body/UpdateOptions/changedValue"):
val = elt.getChild('value')
xsd_type = 'xsd:int' if val is not None and check_int(val.getText()) else 'xsd:string'
val.set('xsi:type', xsd_type)