VimWrap

From IronPython Cookbook

The code for VMware convenience wrapper. see description in my blog for more details.

The code works with API versions 2.0 and 2.5 (ESX 3.0 and 3.5 respectively) - just make sure you use the right version of the VimService2005 assembly (part of the SDK).

import clr
clr.AddReference('VimService2005')
import VimApi

clr.AddReference('System.Web.Services')
import System.Web.Services
clr.AddReference('System.Xml')
import System.Xml

from System.Net import CookieContainer

#########################################################
#                     VimException
#########################################################
class VimException(Exception):
    """Extracts the relevant information from the soap exception thrown natively by VimApi"""
    def __init__(self,e):
        try:
            self.msg = e.Message
            self.name = 'GenericError'
            self.element = None
            for c in e.Detail.ChildNodes:
                if isinstance(c,System.Xml.XmlElement):
                    self.name = c.Name
                    if self.name.endswith('Fault'):
                        self.name = self.name[:-5]
                    self.element = c
                    break
        except Exception,err:
            self.name = 'UnexpectedExceptionError'

    def __str__(self):
        return 'VimException(%s: %s)' % (self.name,self.msg)

#########################################################
#                    ServiceWrapper
#########################################################
class ServiceWrapper(object):
    """Convenience wrapper over VMware's VimService and ServiceContent classes.
       Also designed to work naturally with the ManagedObject class.
    """
    def __init__(self,url,user,pswd):
        self.url = url
        self.user = user
        self.pswd = pswd
        
        self.b_connected = False

        self.service = VimApi.VimService(
            Url = self.url,
            CookieContainer = CookieContainer(),
        )

        self.service_instance = self.wrap(VimApi.ManagedObjectReference(
            type="ServiceInstance",
            Value="ServiceInstance",
        ))        

    def wrap(self,x):
        if isinstance(x,VimApi.ManagedObjectReference):
            return ManagedObject(x,self)
        if isinstance(x,System.Array): # XXX - seems this is the only sequence type we need to handle
            return [self.wrap(i) for i in x]
        return x

    def unwrap(self,x):
        if isinstance(x,ManagedObject):
            return x.moref
        if isinstance(x,tuple): # XXX - seems this is the only sequence type we need to handle
            return tuple(self.unwrap(i) for i in x)
        return x
               
    def connect(self):
        content = self.service_instance.RetrieveServiceContent()
        for propname in [p.Name for p in clr.GetClrType(VimApi.ServiceContent).GetProperties()]:
            if propname in ['dynamicProperty','dynamicType']:
                continue # these are boilerplate fields for SOAP protocol. ignore them
            prop = getattr(content,propname)
            obj = self.wrap(prop)
            setattr(self,propname,obj)
        self.b_connected = True            

    def login(self):
        if not self.b_connected:
            self.connect()
        self.sessionManager.Login(self.user,self.pswd,None)
        
    def invoke(self,moref,method_name,*a):
        mth = getattr(self.service,method_name)
        a = [self.unwrap(x) for x in a]
        try:
            res = mth(moref,*a)
        except System.Web.Services.Protocols.SoapException, e:
            raise VimException(e)
        return self.wrap(res)
        
    def get_property(self,moref,name):
        # Create a filter spec with the specified object and property spec
        fs = VimApi.PropertyFilterSpec()

        # create a property spec (describes what we want retrieved)
        ps = VimApi.PropertySpec()
        ps.type = moref.type
        ps.all = False
        ps.pathSet = (name,)
        fs.propSet = (ps,)
        
        # the search starting point
        os = VimApi.ObjectSpec()
        os.obj = moref
        fs.objectSet = (os,)
                
        # run the query
        raw_res = self.propertyCollector.RetrieveProperties((fs,))
        
        # translate the result
        if raw_res is None:
            return None
        propSet = raw_res[0].propSet
        if propSet is None:
            return None            
        res = propSet[0].val
        return self.wrap(res)

#########################################################
#                    ManagedObject
#########################################################
class ManagedObject(object):
    """Object oriented wrapper over VMware's ManagedObjectReference (an opaque handle)"""
    def __init__(self,moref,service):
        self.moref = moref
        self.service = service # service wrapper
        
    def __repr__(self):
        return '%s(%s)' % (self.moref.type,self.moref.Value)

    def __getattr__(self,name):        
        if hasattr(self.service.service,name): # it's a method            
            def wrapped(*a):
                return self.service.invoke(self.moref,name,*a)
            wrapped.__name__ = name
            return wrapped
        else: # not a method - assume it's a property
            try:
                return self.service.get_property(self.moref,name)
            except VimException, e:
                if e.name == 'InvalidProperty':
                    raise AttributeError(name)
                else:
                    raise


Here's an example usage:

from vimwrap import ServiceWrapper
svc = ServiceWrapper(url,user,pswd)
svc.login()

f = svc.searchIndex.FindByInventoryPath('.../my_folder')
print 'Folder: %s' % f
print 'Children:'
for child in f.childEntity:
    print '\t%s: %s' % (child.name,child)


Here's another example - powering on a machine, and checking task progress:

vm =  svc.searchIndex.FindByInventoryPath('.../test_machine')
task = vm.PowerOnVM_Task(None)
ti = task.info # call VMware to get updated task info
print 'powering on... state=%s, progress=%s' % (ti.stat

note of caution:

This is great for exploration and basic tasks. However, once you need to go over larger configurations, you will need to use methods like RetrieveProperties directly to get only the data you need and get all of it in one call. I suggest writing a wrapper for that API too - it's still much more complex and boilerplate than it needs to be.

Back to Contents.

TOOLBOX
LANGUAGES