Using the VixCOM library

I recently had a requirement to automate testing of kernel mode drivers under various operating systems. We also needed the automated testing to be part of the MSBuild process, so we needed to be able to control the tests by either using a .NET component or a COM Control. Obviously we couldn’t use the build machine to run any of the tests because if a kernel mode driver fails, it usually kills the system with a BSOD(blue screen of death) and that would greatly interfere with the whole build process.

I took a look at Microsoft’s Virtual Server, but I have always thought it was not that intuitive. Since we already use the VMWare Workstation solution for various development tasks, the Free VMWare Server was the ideal choice. VMWare has a few SDK alternatives that allow you to automate VMWare Workstation and Server. The Vix API allows us to control VMWare Server and load virtual machines, revert to last snapshots, copy files onto the VM, and run programs in the virtual machines. The VixCOM COM component will allow us to easily use the API’s within our C# project.

Once you’ve installed the Vix API, you can add the VixCOM COM reference to your project by selecting the “VixCOM 1.0 Type Library” Component.

Adding VixCOM

The lack of documentation and practical samples seemed to be the biggest problem with the VIX API, so I created a wrapper class, VixWrapper.cs in C# to handle most of the APIs.

VixWrapper.cs listing

using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;

using VixCOM;

namespace Tranxition.BuildTasks
{
    class VixWrapper
    {
        VixCOM.IVixLib vixLib = null;

        ulong m_vixError;
        VixCOM.IHost m_hostHandle = null;
        VixCOM.IVM m_vmHandle = null;

        public ulong GetError()
        {
            return m_vixError;
        }

        public VixWrapper()
        {
            try
            {
                vixLib = new VixCOM.VixLibClass();
            }
            catch (COMException comExc)
            {
                System.Diagnostics.Trace.WriteLine(comExc.Message + “\n”);
                throw;
            }
        }

        /// <summary>
        /// Creates a host handle
        /// </summary>
        /// <returns>true if succeeded, otherwise false</returns>
        public bool Connect(string hostName, string userName, string password)
        {
            int hostType = string.IsNullOrEmpty(hostName) ?
                VixCOM.Constants.VIX_SERVICEPROVIDER_VMWARE_WORKSTATION :
                VixCOM.Constants.VIX_SERVICEPROVIDER_VMWARE_SERVER;

            int vixVersion = VixCOM.Constants.VIX_API_VERSION;

            vixVersion = 1; // Bugfix: http://communities.vmware.com/message/649925#649925

            VixCOM.IJob jobHandle = vixLib.Connect(vixVersion,
                hostType, hostName, 0, userName, password, 0, null,  null);

            int[] propertyIds = new int[1] { VixCOM.Constants.VIX_PROPERTY_JOB_RESULT_HANDLE };
            object results = new object();

            m_vixError = jobHandle.Wait(propertyIds, ref results);

            if (m_vixError == VixCOM.Constants.VIX_OK)
            {
                object[] objectArray = (object[])results;
                m_hostHandle = (VixCOM.IHost)objectArray[0];
                return true;
            }

            return false;
        }

        /// <summary>
        /// Opens the virtual machine specified in vmxFilePath
        /// </summary>
        /// <param name=”vmxFilePath”>The virtual machine vmx file to open</param>
        /// <returns>true if succeeded, otherwise false</returns>
        public bool Open(string vmxFilePath)
        {
            IJob jobHandle = m_hostHandle.OpenVM(vmxFilePath, null);

            int[] propertyIds = new int[1] { VixCOM.Constants.VIX_PROPERTY_JOB_RESULT_HANDLE };
            object results = new object();

            m_vixError = jobHandle.Wait(propertyIds, ref results);

            if (m_vixError == VixCOM.Constants.VIX_OK)
            {
                object[] objectArray = (object[])results;
                m_vmHandle = (VixCOM.IVM)objectArray[0];
                return true;
            }

            return false;
        }

        /// <summary>
        /// Power on the virtual machine
        /// </summary>
        /// <returns>true if succeeded, otherwise false</returns>
        public bool PowerOn()
        {
            IJob jobHandle = m_vmHandle.PowerOn(VixCOM.Constants.VIX_VMPOWEROP_LAUNCH_GUI,
                null, null);
            m_vixError = jobHandle.WaitWithoutResults();

            if (m_vixError == VixCOM.Constants.VIX_OK)
            {
                //
                // Wait until guest is completely booted.
                //
                jobHandle = m_vmHandle.WaitForToolsInGuest(300, null);

                m_vixError = jobHandle.WaitWithoutResults();
            }

            return (m_vixError == VixCOM.Constants.VIX_OK);
        }

        /// <summary>
        /// Starts a snapshot of a virtual machine
        /// </summary>
        /// <param name=”snapshot_name”>The name of the snapshot to start</param>
        /// <returns>true if succeeded, otherwise false</returns>
        public bool RevertToLastSnapshot()
        {
            ISnapshot snapshot = null;
            m_vixError = m_vmHandle.GetRootSnapshot(0, out snapshot);

            if (m_vixError == VixCOM.Constants.VIX_OK)
            {
                IJob jobHandle = m_vmHandle.RevertToSnapshot(snapshot, 0, null, null);

                m_vixError = jobHandle.WaitWithoutResults();
            }

            return (m_vixError == VixCOM.Constants.VIX_OK);
        }

        /// <summary>
        /// Login to the virtual machine
        /// </summary>
        /// <returns>true if succeeded, otherwise false</returns>
        public bool LogIn(string username, string password)
        {
            IJob jobHandle = m_vmHandle.LoginInGuest(username, password, 0, null);
            m_vixError = jobHandle.WaitWithoutResults();

            return (m_vixError == VixCOM.Constants.VIX_OK);
        }

        /// <summary>
        /// Creates the directory in the Virtual Machine
        /// </summary>
        /// <param name=”pathName”></param>
        /// <returns></returns>
        public bool CreateDirectoryInVm(string pathName)
        {
            IJob jobHandle = m_vmHandle.CreateDirectoryInGuest(pathName, null, null);
            m_vixError = jobHandle.WaitWithoutResults();

            return (m_vixError == VixCOM.Constants.VIX_OK);
        }

        /// <summary>
        /// Copies a file from the host machine to the virtual machine
        /// </summary>
        /// <param name=”sourceFile”>The source file on the host machine</param>
        /// <param name=”destinationFile”>The destination on the VM</param>
        /// <returns>true if succeeded, otherwise false</returns>
        public bool CopyFileToVm(string sourceFile, string destinationFile)
        {
            //
            // Copy files from host to guest
            //
            IJob jobHandle = m_vmHandle.CopyFileFromHostToGuest(sourceFile, destinationFile,
                0, null, null);
            m_vixError = jobHandle.WaitWithoutResults();

            return (m_vixError == VixCOM.Constants.VIX_OK);
        }

        /// <summary>
        /// Copies a file from the virtual machine to the host machine
        /// </summary>
        /// <param name=”sourceFile”>The source file on the virtual machine</param>
        /// <param name=”destinationFile”>The destination on the host machine</param>
        /// <returns>true if succeeded, otherwise false</returns>
        public bool CopyFileFromVm(string sourceFile, string destinationFile)
        {
            //
            // Copy files from host to guest
            //
            IJob jobHandle = m_vmHandle.CopyFileFromGuestToHost(sourceFile, destinationFile,
                0, null, null);
            m_vixError = jobHandle.WaitWithoutResults();

            return (m_vixError == VixCOM.Constants.VIX_OK);
        }

        /// <summary>
        /// Runs a program on the virtual machine
        /// </summary>
        /// <param name=”exePath”>The path of the program on the virtual machine</param>
        /// <param name=”parameters”>The parameters to pass to the executable</param>
        /// <param name=”resultCode”>The result code returned from the program that ran on the VM</param>
        /// <returns>true if succeeded, otherwise false</returns>
        public bool RunProgram(string exePath, string parameters, out int resultCode)
        {
            resultCode = -1;

            IJob jobHandle = m_vmHandle.RunProgramInGuest(exePath,
                parameters, VixCOM.Constants.VIX_RUNPROGRAM_ACTIVATE_WINDOW, null, null); // clientData

            int[] propertyIds = new int[1] { VixCOM.Constants.VIX_PROPERTY_JOB_RESULT_GUEST_PROGRAM_EXIT_CODE };
            object results = new object();
            m_vixError = jobHandle.Wait(propertyIds, ref results);

            if (m_vixError == VixCOM.Constants.VIX_OK)
            {
                object[] objectArray = (object[])results;
                resultCode = (int)objectArray[0];
                return true;
            }

            return false;
        }

        /// <summary>
        /// Power off the virtual machine
        /// </summary>
        /// <returns>true if succeeded, otherwise false</returns>
        public bool PowerOff()
        {
            IJob jobHandle = m_vmHandle.PowerOff(VixCOM.Constants.VIX_VMPOWEROP_NORMAL, null);
            m_vixError = jobHandle.WaitWithoutResults();

            return (m_vixError == VixCOM.Constants.VIX_OK);
        }
    }
}

Gotchas

After figuring everything out, I only ran into one glitch. You may have noticed the following lines in the code:

int vixVersion = VixCOM.Constants.VIX_API_VERSION;
vixVersion = 1; // Bugfix: http://communities.vmware.com/message/649925#649925

VixCOM.Constants.VIX_API_VERSION seems like the correct value to use for the Connect method, but it does cause an error. I did find the solution in the VMWare support message board.

The RunProgram function will return true or false, depending on if the program successfully executed. It has nothing to do with the return value of the program. The resultCode is the return value of the application.

Using the class

The only exception that should get thrown using this code is a COMException, so this is the only exception type that I catch. This will definitely occur if the VixCOM dll is not registered on the system. Here is a simple example of how to use the class:

try
{
    VixWrapper vix = new VixWrapper();

    //
    // Connect to the VMWare Server
    //
    if (vix.Connect(hostName, hostUser, hostPassword))
    {
        //
        // Opening the VMX File
        //
        if (vix.Open(vmxFilePath))
        {
            //
            // Reverting to the ‘only’ snapshot
            //
            if (vix.RevertToLastSnapshot())
            {
                //
                // Powering on the Virtual Machine
                //
                if (vix.PowerOn())
                {
                    //
                    // Logging in to the Virtual Machine
                    //
                    if (vix.LogIn(virtualMachineUsername, virtualMachinePassword))
                    {
                        //
                        // Copy the files to the virtual machine
                        //
                        bool copiedFilesSuccessfully = true;
                        foreach (ITaskItem fileToCopy in filesToCopy)
                        {
                            string sourceFilePath = fileToCopy.ItemSpec;

                            //
                            // The file must exist.
                            //
                            if (!File.Exists(sourceFilePath))
                            {
                                copiedFilesSuccessfully = false;
                                break;
                            }

                            string destinationFilePath = Path.Combine(virtualMachinePath, Path.GetFileName(sourceFilePath));

                            //
                            // Copying in files to the Virtual Machine
                            //
                            if (!vix.CopyFileToVm(sourceFilePath, destinationFilePath))
                            {
                                copiedFilesSuccessfully = false;
                                break;
                            }
                        }

                        //
                        // If we didn’t fail to copy any files over, then run the test
                        //
                        if (copiedFilesSuccessfully)
                        {
                            //
                            // Run the test program
                            //
                            int resultCode = 0;
                            if (vix.RunProgram(exePath, exeParameters, out resultCode))
                            {
                                if (resultCode == 0)
                                {
                                    //
                                    // The test PASSED!
                                    //
                                    returnValue = true;
                                }
                                else
                                {
                                    // The test FAILED!
                                   
returnValue = false;
                                }
                            }
                            else
                            {
                                //
                                // Unable to run test
                                //
                            }
                        }
                    }
                    else
                    {
                        // Unable to login to the virtual machine
                    }

                    vix.PowerOff();
                }
                else
                {
                    // Unable to power on the virtual machine
                }
            }
            else
            {
                // Unable to revert to the last snapshot
            }
        }
        else
        {
            // Unable to open the VMX file
        }
    }
    else
    {
        // Unable to connect to the host
    }

    return returnValue;
}
catch (COMException comExc)
{
    //
    // COM Exception
    //
    return false;
}

8 Responses to “Using the VixCOM library”

  1. Automating virtual machines « Noocyte’s Weblog Says:

    [...] Automating virtual machines In my last job (Synergi Solutions AS) we talked a bit about creating virtual machines as output of our nightly/release builds, but never got around to actually doing it. I just found a link to somebody who’s actually done it; http://tranxcoder.wordpress.com/2008/05/14/using-the-vixcom-library/ [...]

  2. Jakob Says:

    Hi,
    Great example code for us who are dealing with msbuild and want to set it up using vmware server for testing.
    I have just started looking into automating our test procedures using VIX for our vmware server 2.0 RC2. I’m having problem connecting to the server from my msbuild task I’ve created. The task tries to connect but fails, due to a timeout I think. The only error message I get back from VIX is error code 16 which don’t give any real hints to what the problem is. I’ve tried the vb script examples that comes with the VIX installation and they can at least connect to the host machine using the same settings as I’m using in my msbuild task. I’m running the vmware server on a ubuntu server version 8.04 and want to connect from a system running windows xp SP3.

    Tnx
    // Jakob

  3. ehaddan Says:

    The Vix error code 16 is VIX_E_VMSERVER_NOT_FOUND so the only thing I can think of is to double-check the parameters that you’re sending. I have also disabled firewalls on the vmware server, but since you are able to connect using other methods, this is probably not the reason.

  4. Jakob Says:

    Hi,

    I don’t think that I have any firewalls enabled on the server but I’ll look into that. The next problem I encounter when using a vbscript for example is that when I have connected to the server the virtual machine I want to start can’t be found. It says that it can’t allocate the vmx file. How should the parameter for the path to the vmx file be specified. Should it be according to the path from root on the server? E.g. On the server I have my VMs in /home/username/VirtualMachines is this the path to use or does vix require you to give a path from some place that vix think is root?

  5. Jakob Says:

    I’ve solved the problem with the vmPath so that it works from a vbscript. My problem with connecting from my C# MSBuild task remains. I’m wondering what the difference between running vix from the sample vbscript that comes along with the vix installation and running the vixcom lib from a msbuild task in C# is? Should be no difference since the vbscript is using the Set lib = CreateObject(”VixCOM.VixLib”) before connecting to the server.
    Let me know if anything comes to mind about this problem.

  6. Jakob Says:

    I got it to work. The problem was that I was using VIX_SERVICEPROVIDER_VMWARE_SERVER for hosttype instead of VIX_SERVICEPROVIDER_VMWARE_VI_SERVER and also according to the bug fix for API version used a 1 instead of the defined value for API version. That was the difference between the vbscript and the C# code.
    Must be something that has been fixed in the RC2 version of vmware server 2.0.

  7. Brent Digby Says:

    With the latest release of the API 1.6.2, the VixCOM.Constants.VIX_API_VERSION is fixed in the Connect().

    Also, if you have trouble with OpenVM, you could use vmrun, to get the list of registered VM.

  8. dB. Says:

    This might be of interest: http://code.dblock.org/ShowPost.aspx?id=29

Leave a Reply