Ok, lately I have been very busy with IIS7 related milestones, so I am a little drained mentally. Thus, I will talk about a "simpler" topic that should still be useful useful... just not as much prep work for me... Virtual Server. :-)

This will be the first of many articles about the Virtual Server 2005 Administration API and how to programmatically take advantage of it to do a variety of things.

Motivation

I have been an avid Virtual Server user/developer from the very beginning of the product, so I have a huge code investment in automating a variety of tasks within Virtual Server... and after reading the code samples from the blog of Virtual PC Guy (Ben), I have decided that I should share some of the more non-obvious tidbits of how to use Virtual Server. After all, I had to figure out several of these things after being informed by various members of the Virtual Server product team how X or Y was not possible for reason Z... but I still needed to do them anyways, so I figured it out.

For example, Ben's Guest keyboard typer does not work if the Guest and Host OS have different keyboard layout. Try setting the Host with the Dvorak layout and the Guest with QWERTY (or vice versa) and see what happens. Complete gibberish. I was the first to file that bug... probably because I was the first non-QWERTY typist to use Virtual Server. Now, I can type both QWERTY and Dvorak, but I favor Dvorak because it is SO much more comfortable than QWERTY.

The First Task... Waiting

Let's start simple. A simple function which generically "waits" for any asynchronous Virtual Server task to complete while displaying live "progress" to the console window. Yes, all of my code requires the CSCRIPT Windows Scripting Host - run the following as administrator to set up Windows Scripting host:

CSCRIPT //h:cscript //NoLogo //s

Hey, I believe in commandline automation tools. None of that pansy WSCRIPT stuff or mushy VB.Net/C# UI stuff... because we all know that real work gets done with commandline tools. Thems fighting words... ;-)

Now, why is such a function useful? Well, various VS API calls change state within the Virtual Machine, and sometimes, you want to access useful state that is present IMMEDIATELY AFTER the task is complete. Some examples and why it is useful:

  • IVirtualMachine.Save() - the task ends when the Virtual Machine is completely saved to disk. You need this when you want to automate saving Virtual Machine state prior to making a copy of the various saved VM files from the host.
  • IVirtualMachine.MergeUndoDisks() - the task ends when the undo drive is completely committed. You need this to commit an undo drive and then possible re-start the Virtual Machine or make a copy of the merged drive file from the host.
  • IVirtualServer.CreateFixedVirtualHardDisk() - the task ends when the fixed hard disk is completely created (if you create a multi-gigabyte fixed VHD, it can take a while). You need this if you automate creation of virtual hard disks and be assured that the virtual hard disk is "valid for use" when attached to the Virtual Machine so that you can immediately power on the VM.
  • IVirtualHardDisk.Compact() - the task ends when the "compact" function finishes. This is useful in conjunction with other functions that automate the defrag, pre-compact, compact, and restore of a VHD to trim its size back down.

Now, I realize that I just mentioned a whole bunch of related functionality that one would love use in conjunction with this simple function. Yes, I will give code samples and explanation of them in future blog entries.

Enjoy.

//David

Code to Wait for VS Task

The WaitForTask() function expects an asynchronous Virtual Server Task as input, and it will output simple "percentage complete" progress text as the task is waited on, and when the task finishes, the function returns and deletes its progress text so that the rest of your automation still gets a clean console output without the transient state of waiting for a task to complete (for logging purposes, for example).

var GUEST_OS_SLEEP_RESOLUTION = 250;
var CLEAR_LINE = String.fromCharCode( 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8 );
var CRLF = "\r\n";

function WaitForTask( task )
{
    var complete;
    var strLine = "";
    var cchLine = 0;

    while ( (complete = task.PercentCompleted) <= 100 )
    {
        strLine = CLEAR_LINE.substring( 0, cchLine ) + complete + "%   ";
        cchLine = strLine.length;     //this should not exceed CLEAR_LINE

        WScript.Stdout.Write( strLine );

        if ( complete >= 100 )
        {
            // Delete the % display so that next line is clean.
            WScript.Stdout.Write( CLEAR_LINE );
            break;
        }

        WScript.Sleep( GUEST_OS_SLEEP_RESOLUTION );
    }
}