VMware, VIX and ctypes

Steven F. Lott

slott56@gmail.com

http://slott-softwarearchitect.blogspot.com/

https://sourceforge.net/projects/pyvix2/

Topics

VMware and VIX

ctypes Basics

ctypes and VIX

Conclusion

VMware and VIX

Most VMware products are "service providers".

The service is running a guest OS. And making snapshots. Etc.

VIX allows you to control a service provider.

Issue: No Python bindings.

ctypes Basics

Get a Library.

Call C functions in that library.

Map C language data types to Python structures.

Getting A Library

>>> from ctypes import *
>>> from ctypes.util import find_library
>>> find_library("C")
'/usr/lib/libC.dylib'
>>> libc= CDLL( find_library("C") )

Or

>>> libc= cdll.LoadLibrary( find_library("C") )

Calling A Function

man page definition:

pid_t
getpid(void);
>>> libc.getpid()
17560

That's it? Yep.

>>> import os
>>> os.getpid()
17560

Non-Trivial Cases

String Buffers.

char *getcwd(char *buf, size_t size)

Returning a "Pointer".

char *getcwd(char *buf, size_t size)

Providing a "Pointer to a Structure".

DIR *opendir(const char *name)

dirent *readdir(DIR *dir)

String Buffers

char *getcwd(char *buf, size_t size)


>>> result= create_string_buffer(255)
>>> libc.getcwd( byref(result), 255 )
3751120
>>> result
<ctypes.c_char_Array_255 object at 0x100440b90>
>>> result.value
'/Users/slott/Documents'

Create a reference to a mutable string buffer.

Returning a Pointer

char *getcwd(char *buf, size_t size)


>>> result= create_string_buffer(255)
>>> libc.getcwd.restype=c_char_p
>>> libc.getcwd( byref(result), 255 )
'/Users/slott/Documents'

Define the result type to be a char *.

Structure in Mac OS Reference

http://developer.apple.com/library/mac/#documentation/Darwin/Reference/ManPages/man5/dirent.5.html

struct dirent { /* when _DARWIN_FEATURE_64_BIT_INODE is NOT defined */
        ino_t      d_ino;                /* file number of entry */
        __uint16_t d_reclen;             /* length of this record */
        __uint8_t  d_type;               /* file type, see below */
        __uint8_t  d_namlen;             /* length of string in d_name */
        char    d_name[255 + 1];   /* name must be no longer than this */
};

Structure in ctypes

from ctypes import *
from ctypes.util import find_library
libc= cdll.LoadLibrary( find_library("C") )
class DIRENT(Structure):
    _fields_ = [
        ("d_ino", c_int),
        ("d_reclen", c_ushort),
        ("d_type", c_char),
        ("d_namlen", c_ubyte),
        ("d_name", c_char*1024), # Array of chars
    ]

Pointer to Structure

DIR *opendir(const char *name)

dirent *readdir(DIR *dir)


libc.opendir.restype=c_void_p
d= libc.opendir( "." )

libc.readdir.restype=POINTER(DIRENT)
entry= libc.readdir( c_void_p(d) ) # "opaque DIR*"
entry.contents.d_name

Debugging Note

When you've got the type wrong,

Python crashes. You lose session state.

So.

Create a separate window with a script to define your structures.

Copy and paste code to restore your session and run another experiment.

Until you get the structure right.

ctypes and VIX

VIX Overview

The Good News.

VIX (like many non-kernel libraries) is "Object-Like C".

It uses "Handles" instead of actual pointers.

These are opaque structures. Treat them like ctypes.c_long.

https://sourceforge.net/projects/pyvix2/

VIX Handles

VIX provides a few variant handle types

Generic Handle Features

Amongst other things, we need to release handles we're given by VIX.

class Handle( object ):
    def __init__( self, handle=VIX_INVALID_HANDLE ):
        self.handle= handle
    def __del__( self ):
        self._vix.Vix_ReleaseHandle(self.handle)

Use Case:

x = Job( ... )

The Rarely-Used __del__ Method

Python uses reference-counting to collect unusable objects.

Each time an object is assigned to a variable, the reference count goes up.

Each time a variable's namespace goes out of scope, the reference count goes down.

When the reference count is zero, __del__ is called prior to removal from memory.

99.9% of the time, we don't care. This is the 0.1%.

Connecting to a Host

class Host( Handle ):
    def connect(self, hostname = None, hostport = 0, username = None, password = None):
        self.handle = VIX_INVALID_HANDLE

        # Get a Job Handle
        jobHandle = Job( _vix.VixHost_Connect(VIX_API_VERSION,
                self.service_provider, hostname, hostport,
                username, password, 0, VIX_INVALID_HANDLE, None, None, ) )
        # Wait until the Job is finished
        result= jobHandle.wait( )

        # Extract the Host handle
        self.handle= jobHandle.get_property(VIX_PROPERTY_JOB_RESULT_HANDLE)

Important Sequence

  1. Make the underlying _vix.something() call.
  2. Wrap the result as a Handle.
  3. Our Handle class will do the required Vix_ReleaseHandle.

Open a VM

def openVM( self, vmxFilePathName ):
    # Get a Job Handle
    jobHandle= Job( _vix.VixHost_OpenVM( self.handle,
        vmxFilePathName, VIX_VMOPEN_NORMAL,
        VIX_INVALID_HANDLE, None, None, ) )
    # Wait until the Job is finished
    result= jobHandle.wait( )

    # Extract the VM Handle
    vmHandle= VM( jobHandle.get_property( VIX_PROPERTY_JOB_RESULT_HANDLE ) )
    return vmHandle

Script Snippet 1

Lists running VM's on the host.

from __future__ import print_function
from pyvix2 import Host
host= Host()
host.connect() # local host
running= host.find_items()
print( running )

Script Snippet 2

Apache and MySQL running?

host= Host()
host.connect() # local host
vm= host.openVM("/path/to/guest.vmx")
vm.wait_for_tools_in_guest( 5 ) # Exception if no tools
vm.login( "user", "password" )
procs= vm.process_list()
for pid, owner, name, start_time, command in procs:
    if 'apache' in name or 'mysql' in name:
        print( pid, owner, command )

Conclusion

ctypes is not too difficult to use. If you know C. Well.

VIX has a clean, ctypes-friendly API.

It's pretty easy to create Python bindings for VIX.

https://sourceforge.net/projects/pyvix2/

Questions?