Prev | 1  2  3  4 Next
VMWare Tasks Library Draft Hidden
Read | Saturday, January 03, 2009 10:15 AM | Post Comment | 12 Clicks

VMWare

The VMWare Tasks Library is a managed C# wrapper on top of VMWare COM ViX API that makes it really easy to use.

Features:

  • a much more natural and easy programming model
  • supports virtual machine VI (ESX) and Workstation
  • enumerate and power Virtual Machines
  • enumerate, revert and power snapshots
  • execute commands in guest OS
  • list and delete files and directories with subdirectories in guest OS
  • manage shared folders
  • read and write guest and environment variables
Abstracting a cumbersome construct in VIX API: GetNumProperties + GetNthProperty Draft Hidden
Read | Saturday, December 27, 2008 6:15 PM | Post Comment | 13 Clicks

Did I tell you how much I love yield return?

One of the peculiar VIX COM API constructs is the combination that returns arrays of properties. This is done with two functions: GetNumProperties and GetNthProperties. The first returns the number of property arrays returned by the job and the second fetches a property array at a given index. The first obvious step is to wrap the functions within the job class.

public

T GetNthProperties<T>(int index, object[] properties)

{

   object result = null;

   VMWareInterop.Check(_handle.GetNthProperties(

      index, properties, ref result

));

   return (T) result;

}

 

public

int GetNumProperties(int property)

{

   return _handle.GetNumProperties(property);

}

We can now write such properties as RunningVirtualMachines.

public

IEnumerable<VMWareVirtualMachine> RunningVirtualMachines

{

   get

   {

      VMWareJob job =

new VMWareJob(_handle.FindItems(

         Constants.VIX_FIND_RUNNING_VMS, null, -1, null));

      job.Wait(VMWareInterop.Timeouts.FindItemsTimeout);

      object[] properties = { Constants.VIX_PROPERTY_FOUND_ITEM_LOCATION };

      for (int i = 0; i < job.GetNumProperties((int) properties[0]); i++)

      {

         yield return this.Open(

            (string) job.GetNthProperties<object[]>(

               i, properties)[0]);

      }

   }

}

This is still not good enough. Let's combine the number of results and the results themselves in a YieldWait method.

public IEnumerable<object[]> YieldWait(object[] properties, int timeoutInSeconds)

{

   Wait(timeoutInSeconds);

   for (int i = 0; i < GetNumProperties((int)properties[0]); i++)

   {

      yield return GetNthProperties<object[]>(i, properties);

   }

}

This results in a nice improvement over the previous implementation: we're interating over a resultset rather than calling methods for how many results are available and to fetch each result.

public IEnumerable<VMWareVirtualMachine> RunningVirtualMachines

{

   get

   {

      VMWareJob job = new VMWareJob(_handle.FindItems(

         Constants.VIX_FIND_RUNNING_VMS, null, -1, null));

      object[] properties = { Constants.VIX_PROPERTY_FOUND_ITEM_LOCATION };

      foreach (object[] runningVirtualMachine in job.YieldWait(

         properties, VMWareInterop.Timeouts.FindItemsTimeout))

      {

         yield return this.Open((string) runningVirtualMachine[0]);

      }

   }

}

Getting the display name of a VMWare snapshot Draft Hidden
Read | Friday, December 26, 2008 10:00 AM | Post Comment | 15 Clicks

Looking at the source code of another VMWare C# wrapper on CodePlex, turns out that most VMWare interfaces represent VMWare handles. This is a good decision from the design point of view, but the implementation in VMWare VIX API is wrong: it should be returning objects that implement multiple interfaces, instead it returns one of the interfaces that you can happily cast to another.

This does answer the question of how to get the display name of a VMWare snapshot.

Let's add a wrapper for a VIX handle.


/// <summary>
/// A wrapper for a VIX handle. Most VIX objects returned also implement IVixHandle.
/// </summary>
public class VMWareVixHandle
{
 
IVixHandle _handle = null;
 
public VMWareVixHandle(IVixHandle handle)
 {
  _handle = handle;
 }

 
/// <summary>
 
/// Get an array of properties.
 
/// </summary>
 
/// <param name="properties">properties to fetch</param>
 
/// <returns>an array of property values</returns>
 
public object[] GetProperties(object[] properties)
 {
 
object result = null;
 
VMWareInterop.Check(_handle.GetProperties(properties, ref result));
 
return (object[]) result;
 }
}

Then the snapshot's name is a cast to a IVixHandle which implements GetProperties.


public string DisplayName
{
 
get
 
{
 
object[] properties = { Constants.VIX_PROPERTY_SNAPSHOT_DISPLAYNAME };
 
return (string)new VMWareVixHandle((IVixHandle) _snapshot).GetProperties(properties)[0];
 }
}
Copying files to/from VMWare guest OS is slow: an alternate approach Draft Hidden
Read | Wednesday, December 24, 2008 1:45 AM | 2 Comments | Post Comment | 79 Clicks

I really like VMWare VIX API's CopyFileFromGuestToHost and CopyFileFromHostToGuest. These automatically copy a single file or an entire directory tree. Easy. Unfortunately I am copying large (150-300 Mb) files to/from a VMWare guest OS and it's taking an hour to copy a hundred megs. I bet VMWare API was developed by Russian developers, so it's probably sending UTF-8 encoded bytes in Russian via SOAP. You might remember that I am writing a tool that lets one test a cross-product of installers and virtual machine snapshots: three MSIs multipled by twenty snapshots equals days of file copying! It's not going to work.

I posted a question about this in VMWare Communities and got a satisfactory response: I don't think there is a workaround. We are aware of the issue (we run in to it internally) and hope to fix it in a future release.

In the meantime, I need a short term solution.

I decided to try and map the remote drive and use a simple File.Copy. The first challenge is to find out the remote server's IP address. VMWare exposes guest OS variables, including the ip, so I've extended VMWare Tasks to support those.


/// <summary>
/// Environment, guest and runtime variables
/// </summary>
/// <param name="name">name of the variable</param>
[IndexerName("Variables")]
public string this[string name]
{
 
get
 
{
 
VMWareJob job = new VMWareJob(_vm.ReadVariable(_variableType, name, 0, null));
 
object[] properties = { Constants.VIX_PROPERTY_JOB_RESULT_VM_VARIABLE_STRING };
 
return job.Wait<string>(properties, 0, VMWareInterop.Timeouts.ReadVariableTimeout);
 }
 
set
 
{
  
VMWareJob job = new VMWareJob(_vm.WriteVariable(_variableType, name, value, 0, null));
   job.Wait(
VMWareInterop.Timeouts.WriteVariableTimeout);
  }
}

Here's the IP address of a powered-on VM.


virtualMachine.GuestVariables["ip"];

We can make up a network path out of a local one.


public string PathToNetworkPath(string ip, string value)
{
 
return string.Format(@"\\{0}\{1}", ip, value.Replace(":", "$"));
}

Mapping a network drive is implemented in mpr.dll with WNetAddConnection2. A wrapper class will do the job.


public class NetworkDrive
{
 [
DllImport("mpr.dll")]
 
public static extern int WNetAddConnection2(
  
ref NETRESOURCE lpNetResource, string lpPassword, string UserName, int dwFlags);

 [StructLayout(LayoutKind.Sequential)]
 public struct NETRESOURCE
 {
  
public int dwScope;
  
public int dwType;
  
public int dwDisplayType;
  
public int dwUsage;
  
public string lpLocalName;
  
public string lpRemoteName;
  
public string lpComment;
  
public string lpProvider;
 }

 private const int RESOURCETYPE_DISK = 0x1;

 private string _localName;
 private string _remoteName;

 public NetworkDrive(string remoteName)
 {
  _remoteName = remoteName;
 }

 public NetworkDrive(string remoteName, string localName)
 {
  _remoteName = remoteName;
  _localName = localName;
 }

  public string LocalName
  {
  
get { return _localName; }
  
set { _localName = value; }
  }

  public string RemoteName
 
{
   
get { return _remoteName; }
   
set { _remoteName = value; }
  
}

 public void MapNetworkDrive(string username, string password)
 
{
  
NETRESOURCE netResource = new NETRESOURCE();
  
netResource.dwScope = 2;
  
netResource.dwType = RESOURCETYPE_DISK;
  
netResource.dwDisplayType = 3;
  
netResource.dwUsage = 1;
  
netResource.lpRemoteName = _remoteName;
  
netResource.lpLocalName = _localName;
  
int rc = WNetAddConnection2(ref netResource, password, username, 0);
  
if (rc != 0)
  
{
   
throw new Win32Exception(rc);
  
}
 
}
}

Let's put it all together.


public void CopyFileFromGuestToHost(string guestPath, string hostPath)
{
 
string guestNetworkPath = PathToNetworkPath(_ip, guestPath);
 
NetworkDrive guestNetworkDrive = new NetworkDrive(Path.GetPathRoot(guestNetworkPath));
 
guestNetworkDrive.MapNetworkDrive(_username, _password);
 
File.Copy(guestNetworkPath, hostPath, true);
}

The last thing that remains to be done to make the new copy compatible is to also copy directories and subdirectories. That's left as an exercise to the reader.

Announcing the VMWare Tasks Library Draft Hidden
Read | Saturday, December 20, 2008 1:30 AM | Post Comment | 89 Clicks

VMWare

Now that I wrote various tools that interop with VMWare, I started working on a VMWare Tasks Library, a managed C# wrapper on top of VMWare COM ViX API that makes it really easy to use.

I checked in basic functionality of connecting to a virtual machine host, restoring snapshots, powering them up, executing commands, getting lists of files in a directory and subdirectories, etc.

At my day job we developed a tool that uses VMWare to test installers (executes a cross-product of virtual machine snapshots and installers). I plan to take an alternate approach here and write an MSBuild Task to control VMWare, which could enable more flexible automation scenarios.

HowTo: remote control, copy and run programs on a VMware Workstation or VI server using VIX APIs Draft Hidden
Read | Wednesday, December 10, 2008 4:15 PM | Post Comment | 180 Clicks

I've been playing with VMware lately, both Workstation and VMware Infrastructure (VI). The company has really stepped up with the new SDKs and the level of programmable interfaces, making some excellent implementation decisions that enable us to drive virtual machines for primarily unit-testing purposes. The doc is a little light today, so this should help.

What do we use this for?

We (at my day job) developed a tool (in C#) that runs MSI installers to make sure the product installs on various knownly good or bad environments, sort of unit testing installers. We also have an ESX environment that we share.

VMWare Programming APIs

There're two types of APIs.

  • VMWare Virtual Infrastructure SDK: a set of tools and APIs to manage a VMWare Infrastructure environment. A toolkit has also been released that contains managed wrappers on top of the SOAP interface provided by a VMWare deployment. It's focused on VMWare ESX or VirtualCenter management and is beyond the scope of this post.
  • VMWare VIX API. The VIX API allows you to write programs and scripts that automate virtual machine operations, as well as the guests within virtual machines. It runs on both Windows and Linux and supports management of VMware Server, Workstation, and Virtual Infrastructure (both ESX and vCenter). Bindings are provided for C, Perl, and COM (Visual Basic, VBscript, C#). In this post I'll focus on the C# implementation.

What do you need?

Connecting to a VMWare Workstation or ESX Server

Connecting to a local VMWare Workstation or an ESX server is virtually identical. The ESX server requires an URL to the SOAP SDK (eg. https://esxserver/sdk) and a username and password.


private void ConnectToVMWareWorkstation()
{
 Connect(Constants.VIX_SERVICEPROVIDER_VMWARE_WORKSTATION, string.Empty, 0, string.Empty, string.Empty);
}

private void ConnectToVMWareVIServer(string hostName, int hostPort, string username, string password)
{
 Connect(Constants.VIX_SERVICEPROVIDER_VMWARE_VI_SERVER,  hostName, hostPort, username, password);
}

private void Connect(int hostType, string hostName, int hostPort, string username, string password)
{
 // Connect to a VMware Workstation
 IJob vmJob = VmwareVixInterop.vixLib.Connect(
  Constants.VIX_API_VERSION, hostType, hostName, hostPort, username, password, 0, null, null);
 // You need to get the IHost object that represents the host where your VM is located.
// Since COM allocates the pointer you need to use this funky mechanism to extract the IHosts array.
 object[] VIX_PROPERTY_JOB_RESULT_HANDLE =  new object[1];
 VIX_PROPERTY_JOB_RESULT_HANDLE[0] = Constants.VIX_PROPERTY_JOB_RESULT_HANDLE;
 // Wait for the operation to complete
 
object hosts = VmwareVixInterop.Wait(vmJob, VIX_PROPERTY_JOB_RESULT_HANDLE);
 
// Cast and retrieve the IHost object.
 
object[] hostArray = hosts as object[];
 _host = (IHost) hostArray[0];
}

Locating a Virtual Machine

A virtual machine on a VMWare Workstation is a file with a standard path (eg. C:\Virtual Machines\myvirtualmachine.vmx). Unsurprisingly it is the same thing on an ESX server, but with a relative storage path. The latter can be found in the VMWare Infrastructure Client: right click on a virtual machine, choose Edit Settings, click the Options tab and note the Virtual Machine Configuration File (eg. [storage] virtualmachine/virtualmachine.vmx).


public void OpenFile(string fileName)
{
 
// Open the VM from the path specified
 
IJob vmJob = _host.OpenVM(fileName, null);
 object[] resultHandles = new object[1];
 resultHandles[0] = Constants.VIX_PROPERTY_JOB_RESULT_HANDLE;
 
// Wait for the operation to complete
 
object vms = VmwareVixInterop.Wait(vmJob, resultHandles);
 
// Get the VM object returned
 
Object[] vmArray = vms as Object[];
 _vm = (IVM) vmArray[0];
}

Power-on, power-off, copy and execute

These are straightforward operations supported by the SDK via the IVM interface returned set by the above-mentioned OpenFile method. Here're some examples.

  • IJob PowerOn(int powerOnOptions, IVixHandle propertyList, ICallback jobDoneCallback);

  • IJob RevertToSnapshot(ISnapshot snapshot, int options, IVixHandle propertyList, ICallback jobDoneCallback);

  • IJob CopyFileFromHostToGuest(string hostPathName, string guestPathName, int options, IVixHandle propertyList, ICallback jobDoneCallback);

It is now possible to execute commands synchronously and asynchronously, without having to involve WMI or another similar method.


public