A Quick VMWare Server Vix Primer

A Quick VMWare Server Vix Primer

Excerpted from Professional VMWare Server

by Eric Hammersley

Brand new with the VMware Server release comes the Programming API, also known as Vix. The Vix API offers a decent amount of additional functionality over its older counterparts. Limiting it is the fact that you can only develop against it in C; this will make it difficult to adopt in some shops. The Vix API currently supports only VMware Server. This means you will not be able to leverage any existing virtual machines not residing on a VMware Server host in your shop with this API. Later releases of Vix may lift this limitation and open up to supporting more VMware platforms.

Because this API is so new, I highly advise you to review the release notes for the Programming API if you have trouble. The release notes for version 1.0, which I use here, are available at the following page.

Unlike the Perl and COM APIs, Vix is rather multilayered. There are several things that you must be aware of before you start poking and prodding your way through the first script. Though I'm not implying that poking and prodding is your normal method of coding, I nevertheless, want to cover some basics to help you fully understand how Vix works.

Handles

Those of you familiar with C will undoubtedly have heard of a handle. A handle is little more than a pseudo-pointer, if you will. Without getting into when and why you might use a handle over a simple pointer, I'll just say the Vix API requires the use of handles of type VixHandle to reference almost all objects within the API.

The handles as they are used in Vix are more of what some might call a "magic cookie." This isn't some super secret programmer speak but a simple way of describing its use. The magic cookie gets passed around the code allowing you to uniquely identify and locate a specific object. In most cases this is done to mask operations performed against the handle, like any specific detail changes to the handle. This may be a difficult concept to grasp without specifics. The easiest generality to take away from this paragraph is that you need to use a handle to reference your objects in Vix. Take the following as a specific example:

VixHandle jobHandle = VIX_INVALID_HANDLE;
jobHandle = VixHost_Connect(
    VIX_API_VERSION,
    VIX_SERVICEPROVIDER_VMWARE_SERVER,
    NULL,                  // *hostName,
    0,                     // hostPort,
    NULL,                  // *userName,
    NULL,                  // *password,
    0,                     // options,
    VIX_INVALID_HANDLE,    // propertyListHandle,
    NULL,                  // *callbackProc,
    NULL);                 // *clientData);

The handle jobHandle in this case represents the object VixHost_Connect. Depending upon the type of object the handle represents, it will morph in a sense to certain properties and methods for that object. The actual content of the handle is nothing more than an integer. Internally Vix knows that the integer assigned to the handle refers to a specific object.

One last point on handles — every handle is reference counted. This means that when you created the VixHost_Connect object in the preceding code, the handle jobHandle received a reference count of 1. The importance of this is that because you have to manually release each and every handle you create, you must make sure the number of releases equals the reference count. A handle can be released by calling Vix_ReleaseHandle(<handle name>). Take the following as an example:

VixHandle handle1;
handle1 = Vix_Host_Connect( stuff goes here )
// The handle now represents the Vix_Host_Connect object
// The handle has a reference count of 1
// Implement code to utilize the handle
//Release the handle
Vix_ReleaseHandle(handle1);

With that in mind if you assign more than one handle to the same object, only the first handle maintains the reference count because the second one merely refers to the first one. If that seems confusing, try this as an example:

VixHandle handle1;
VixHandle handle2;
handle1 = Vix_Host_Connect( parameters to connect )
//handle1 has a reference count of 1
Handle2 = Vix_Host_Connect ( same parameters as above )  
	//same host is connected
//handle2 has the same value as handle1 (It represents the same object)
//handle1's reference count is now at 2
//since there are two references to the 
//object it represents.  
// Handle2 doesn't have a reference count since technically
//it's the same as handle1.
//Now, to release the handles you actually 
// have to do it twice, once for each //reference count
Vix_ReleaseHandle(handle1);
//At this point a valid handle still 
// exists to the object so you can still use
//handle1.  Its reference count is now 1 again.
Vix_ReleaseHandle(handle1);
//Now the handle is gone.

Although this concept may seem a bit strange, it is an important concept to grasp. Any function that returns a handle can increase its reference count by one. Tracking this is important, as you will see in later examples.

Handle Types

There are four different types of handles that you will see throughout this article. Each handle type has a unique set of properties relating to the object they reference. Here's a quick list of the different types of handles you will encounter.

VIX_HOST_HANDLE The host handle represents a host server running VMware Server. This kind of handle is commonly returned by the VixHost_Open() function.
VIX_JOB_HANDLE The job handle comes about during any asynchronous operation in Vix. Because almost all the functions in the API are asynchronous in nature, you will find the job handle in almost everything you do.
VIX_SNAPSHOT_HANDLE The snapshot handle is used to refer to a snapshot contained within a virtual machine. Currently the Vix API is very limited when it comes to snapshot handling.
VIX_VM_HANDLE The virtual machine handle is used to refer to a specific virtual machine. The VixVM_Open() function provides you with this type of handle.

Handle Properties

As you can probably deduce, a handle can refer to many different types of objects within Vix. For instance, when a virtual machine object is referenced by a handle, that handle contains specific properties relating to that type of object. These properties are typed name/value pairs that can be any of the following types:

  • 32-bit integer
  • 64-bit integer
  • String
  • Boolean
  • Handle

Handle properties are the way you modify and work with your object, regardless of their type. Their use ensures compatibility through any future version of the API. It's simply a layer of abstraction that you are most likely already familiar with.

The tables in the following short sections list for you the properties available for each type of handle. Unfortunately at this stage of release for Vix the descriptions of each property are not defined so I cannot provide you with more specific information on each one. You will see a few of these properties used throughout the rest of this article and Chapter 10, "Working with the API," of the book "Professional VMWare Server." (ISBN: 978-0-470-07988-1, Wrox, 2006) Several in the following lists, however, have yet to be defined.

Virtual Machine Properties

Property
VIX_PROPERTY_VM_NUM_VCPUS
VIX_PROPERTY_VM_VMX_PATHNAME
VIX_PROPERTY_VM_MEMORY_SIZE
VIX_PROPERTY_VM_POWER_STATE
VIX_PROPERTY_VM_TOOLS_STATE
VIX_PROPERTY_VM_IS_RUNNING

Event Info Property

This property is present only during the VixHost_FindItems() function call.

Property
VIX_PROPERTY_FOUND_ITEM_LOCATION

Job Properties

These properties are available with handles of type VIX_JOB_HANDLE.

Property
VIX_PROPERTY_JOB_RESULT_ERROR_CODE
VIX_PROPERTY_JOB_RESULT_VM_IN_GROUP
VIX_PROPERTY_JOB_RESULT_USER_MESSAGE
VIX_PROPERTY_JOB_RESULT_LINE_NUM
VIX_PROPERTY_JOB_RESULT_EXIT_CODE
VIX_PROPERTY_JOB_RESULT_COMMAND_OUTPUT
VIX_PROPERTY_JOB_RESULT_HANDLE

Snapshot Properties

These properties are available with handles of type VIX_SNAPSHOT_HANDLE.

Property
VIX_SNAPSHOT_DISPLAYNAME_PROPERTY
VIX_SNAPSHOT_DESCRIPTION_PROPERTY
VIX_SNAPSHOT_POWERSTATE_PROPERTY

The GetProperties() Function

Vix provides one function that allows you to retrieve properties from any handle. The GetProperties() function utilizes a varargs type signature that allows you to pass any number of arguments as long as you terminate the arguments with a special property, VIX_PROPERTY_NONE. Consider the following example:

VixError err;
VixHandle handle1;
int property1;
char* property2;
handle1 = <function that returns a handle>
err = Vix_GetProperties(handle1,
  FIRST_PROPERTY_NAME,
  &property1,
  SECOND_PROPERTY_NAME,
  &property2,
  VIX_PROPERTY_NONE);
Vix_ReleaseHandle(handle1);

Sure that's a pretty generic example but the signature of the Vix_GetProperties() function is the important point here. You provide the handle followed by sets of name/value pairs and terminate it with the special VIX_PROPERTY_NONE property.

Error Codes

All Vix functions return an error code. The time in which it is returned is dependent upon the type of operation performed, synchronous or asynchronous. The error codes returned are of type VixError and are made up of a 64-bit value. If a function completes successfully, it returns an error code of VIX_OK, which is what you will find yourself checking for most often.

Now, the 64-bit error value that is returned comes with a bit of a catch. VIX_OK is the only error that you can check against the entire 64-bit value because it simply returns all zeros. All other errors only correspond to the least significant 16 bits of the error value, which are defined in a global type. In other words, the following code is valid:

VixError err;
err = VixJob_GetError(jobHandle);
if (VIX_OK == err) {
  // Success
}

This works because success is always all zeros. The least significant 16 bits do not need to be masked off from the rest of the code. That being said, the following is not valid:

VixError err;
err = VixJob_GetError(jobHandle);
if (VIX_E_INVALID_ARG == err) {
  // This doesn't work
}

Only success can be determined against the entire unmasked error code. So, you ask, how can you determine the error code if it's not success? Well, you can either mask the error code yourself or use one of three built-in functions to do it for you:

  • First is VIX_ERROR_CODE(). This will mask the 64-bit value down to its least significant 16 bits for you. Using the preceding invalid bit of code let's make it valid:
VixError err;
err = VixJob_GetError(jobHandle);
if (VIX_E_INVALID_ARG == VIX_ERROR_CODE(err)) {
  // Works!  Handle the error code here
}
  • Now instead of evaluating against the entire 64-bit error value you use VIX_ERROR_CODE() to chop off everything but the 16 least significant bits. If you want to perform the masking yourself try the following:
VixError err;
err = VixJob_GetError(jobHandle);
if (VIX_E_INVALID_ARG == (err & 0xFFFF)) {
  // Works!  Performs the same masking that VIX_ERROR_CODE() does
  // for you
}

Personally I prefer the built-in function; however, you can perform the masking manually if you wish.

  • Next is the VIX_SUCCEEDED() function. This function performs the masking automatically and returns a Boolean of true if the error code is VIX_OK. Basically it says (VIX_OK == err) for you. Take the following example:
VixError err;
err = VixJob_GetError(jobHandle);
if (VIX_SUCCEEDED(err)) {
  // Handle successful return here
}

It's simple and perhaps a little cleaner and easier to read than the drawn out VIX_OK == err.

  • Finally there's VIX_FAILED(). This function performs the bit masking and returns true if there is in fact an error. It's basically (VIX_OK != err) when you get down to it. The following example shows its usage:
VixError err;
err = VixJob_GetError(jobHandle);
if (VIX_FAILED(err)) {
  // Handle failure here
  // If you need to test against a specific 
  // failure this will not help you much.
}
One could argue their actual usefulness but I tend to use the built-in functions for evaluation when at all possible. That way if the error code structure is changed later I can be sure VIX_SUCCESS() and VIX_FAILED() will always work. If you peek inside the vix.h header file you'll see that these are nothing more than built-in evaluations against VIX_OK. It's nothing more than a shortcut.

There are numerous error values defined within the global type. I cover them in Appendix C, "Vix Error Codes," in the book "Professional VMWare Server." (ISBN: 978-0-470-07988-1, Wrox, 2006).

A Quick VMWare Server Vix Primer

Asynchronous Operations and the Job Handle

The topic of asynchronous versus synchronous operations is much discussed in the API chapters of "Professional VMWare Server." As you no doubt know, a synchronous operation executes and completes at that moment, whereas an asynchronous operation may take some time to complete. This topic fits into the handle discussion quite nicely.

As I've indicated, there are several different types of handles in Vix; however, the one most often used is referred to as a job handle. All asynchronous operations in Vix return a job handle. This handle represents the pending operation taking place and will, upon completion, be signaled with the final job status. Once they have been signaled to either the successful completion of a pending job or, worse, the failure of it, the job handle will possess several different properties that contain specifics about the completed job. The most important property is usually a handle that represents the final completed object — more on that in a minute.

Several approaches are available to determine the results of a pending job handle. I list them as follows and then talk about some brief examples:

  • Polling for job completion (non-blocking)
  • Blocking until the job is complete (blocking)
  • Using callback functions (non-blocking)

Each has its place and depending upon your situation, you may end up using any combination of the three.

Polling for Job Completion

First I want to talk about polling for completion. When you register an asynchronous operation, as I have already explained, a job handle is created. Over time you will want to check, or poll, the resulting status of that operation. Vix provides a built-in function called VixJob_CheckCompletion(). This is a non-blocking function that sets a Boolean value representing the completion state of the asynchronous operation. You continue to poll the job handle with the function until you receive the completed value of true, at which time you can check the status and continue on with your code. Take the following as an example of polling an asynchronous operation.

All of the following examples are based upon a multithreaded environment. Vix is thread safe itself, and unless you are operating within a single-thread environment or require the use of single-threaded operations, the following will work. If you need more information about how to utilize Vix in a single-thread environment, please consult the VMware Programming API documentation.
Bool openVMPoll(const VixHandle hostHandle,
            const char *vmName,
            VixHandle *vmHandle)
{
  VixError err;
  Vixhandle jobHandle = VIX_INVALID_HANDLE;
  if (!vmHandle) {
    return FALSE;
  }
  // Start the virtual machine
  jobHandle = VixVM_Open(hostHandle,
                         vmName,
                         NULL,
                         NULL);
  // Loop until complete is true meaning the job is complete
  for (Bool complete = FALSE; complete !=TRUE; ) {
    sleep(1);
  
    err = VixJob_CheckCompletion(jobHandle, &complete);
    if (VIX_FAILED(err)) {
      Vix_ReleaseHandle(jobHandle);
      return FALSE;
    }
    err = Vix_GetProperties(jobHandle,
                            VIX_PROPERTY_JOB_RESULT_HANDLE,
                            vmHandle,
                            VIX_PROPERTY_NONE);
    if (VIX_FAILED(err)) {
      Vix_ReleaseHandle(jobHandle);
      *vmHandle = VIX_INVALID_HANDLE;
      return FALSE;
    }
  }
  Vix_ReleaseHandle(jobHandle);
  return TRUE;
}

This is a relatively simple example but rather powerful in terms of polling for job completion. First off the caller here must have already opened a connection to the host machine and must pass the corresponding handle in as hostHandle. Next the caller passes in the virtual machine's name, its configuration file path to be specific, and the handle he or she wants the resulting virtual machine object to reference once the job is complete.

Next the function makes sure the vmHandle provided is indeed valid and proceeds to try to connect to the virtual machine. This is an asynchronous operation, so the VixVM_Open() function is referenced by a jobHandle. The technique to poll for the job completion status is a simple loop. You loop as long as the complete Boolean value is false, which indicates the operation is not yet complete. You use the VixJob_CheckCompletion() function and sleep for one second between the end result, which you hope is that the VixJob_CheckCompletion() function finally returns a true and a successful result. Once this happens you use the Vix_GetProperties() function on the jobHandle to retrieve the virtual machine's handle via the property VIX_PROPERTY_JOB_RESULT_HANDLE. This sets the reference to the resulting virtual machine's handle to the vmHandle provided when the function was called.

That's your garden variety polling operation in Vix for a job handle. It will fit into most situations.

Blocking until the Job Is Complete

The next method utilizes a provided Vix function VixJob_Wait(). This function is a blocking function, meaning that all execution will halt on this command until it completes. All you have to provide to the function is the job handle itself, and then once it returns, you check its status and retrieve the resulting handle from its properties. Take a look at this method in the following example. I'm going to morph the polling example I just used into one using the blocking function instead:

Bool openVMBlock(const VixHandle hostHandle,
                 const char *vmName,
                 VixHandle *vmHandle)
{
  
  VixError err;
  Vixhandle jobHandle = VIX_INVALID_HANDLE;
  if (!vmHandle) {
    return FALSE;
  }
  // Start the virtual machine
  jobHandle = VixVM_Open(hostHandle,
                         vmName,
                         NULL,
                         NULL);
  //Now, block with VixJob_Wait until complete
  err = VixJob_Wait(jobHandle,
                    VIX_PROPERTY_NONE);
  if (VIX_FAILED(err)) {
    Vix_ReleaseHandle(jobHandle);
    return FALSE;
  } else {
    err = Vix_GetProperties(jobHandle,
                            VIX_PROPERTY_JOB_RESULT_HANDLE,
                            vmHandle,
                            VIX_PROPERTY_NONE);
    if (VIX_FAILED(err)) {
      Vix_ReleaseHandle(jobHandle);
      *vmHandle = VIX_INVALID_HANDLE;
      return FALSE;
    }
  Vix_ReleaseHandle(jobHandle);
  return TRUE;
}

This example is set up exactly the same as before, except where you polled for the completion status before, now the function waits until it comes back, thus blocking the rest of the execution. Once it returns and is obviously successful, you retrieve the handle for the virtual machine, set the passed-in vmHandle to reference it, and exit the function. In reality it performs the same operation as before but in this case with the block while the asynchronous operation completes.

The VixJob_Wait() function also allows you to retrieve the resulting handle as a property without asking the job handle itself. An example of this would be if you modified the preceding code for the VixJob_Wait() function to this:

err = VixJob_Wait(jobHandle,
                  VIX_PROPERTY_JOB_RESULT_HANDLE,
                  &vmHandle,
                  VIX_PROPERTY_NONE);

Using a Callback Function

Last you have the callback. This is probably what you will find yourself using most often because it offers better performance for your code. All asynchronous functions in Vix offer a parameter to assign it a callback function. The prototype for any callback function is as follows:

typedef void (VixEventProc)(VixHandle handle,
                            VixEventType eventType,
                            VixHandle moreEventInfo,
                            void *clientData);

The thing to understand here is that the callback function, if provided, is registered with the job handle itself. Once the job handle is signaled by the asynchronous operation, an event is fired that calls the callback function. This doesn't always have to be at completion. There are four event types that cause a callback function to be initiated.

Event Description
VIX_EVENTTYPE_CALLBACK_SIGNALED Indicates that the operation has completed.
VIX_EVENTTYPE_JOB_PROGRESS Reports progress on the operation. This may be fired several times during the operation.
VIX_EVENTTYPE_FIND_ITEM Used by VixHost_FindItems().
VIX_EVENTTYPE_HOST_INITALIZED Used by VixHost_Connect().

Most of the sample code provided in the "Functions" section in the remainder of Chapter 10," Using the Programming API," in Professional VMWare Server utilizes blocking functions to keep the examples simple. I do, however, use a callback to provide an example in the VixHost_FindItems function reference a little later in that chapter. If you explore this API to any great length, you will probably want to start leveraging the callback design for some tasks.

Although I continue to use Perl and COM as the APIs of choice for the remainder of Professional VMWare Server because they are time tested and work, there should be little doubt Vix is in fact the replacement for the Perl and COM APIs down the road.

This article is adapted from Professional VMWare Server by Eric Hammersley (Wrox, 2006, ISBN: 978-0-470-07988-1), from Chapter 10, "Using the Programming API."

Copyright 2006 by WROX. All rights reserved. Reproduced here by permission of the publisher.



Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Live Event Date: September 10, 2014 @ 11:00 a.m. ET / 8:00 a.m. PT Modern mobile applications connect systems-of-engagement (mobile apps) with systems-of-record (traditional IT) to deliver new and innovative business value. But the lifecycle for development of mobile apps is also new and different. Emerging trends in mobile development call for faster delivery of incremental features, coupled with feedback from the users of the app "in the wild". This loop of continuous delivery and continuous feedback is …

  • Packaged application development teams frequently operate with limited testing environments due to time and labor constraints. By virtualizing the entire application stack, packaged application development teams can deliver business results faster, at higher quality, and with lower risk.

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds