Leveraging the VI3 SDK with .NET, part two

The best tool to interact programmatically with VI3 is the SDK. The SDK is very powerful and its power can be harnessed with another equally powerful tool, .NET. In this tip, Andrew Kutz describes how to leverage the VI3 SDK with .NET using C#.

The best tool to interact programmatically with VI3 is the SDK. The SDK is very powerful and its power can be harnessed

with another equally powerful tool, .NET. In this tip, I will describe how to leverage the VI3 SDK with .NET using C#.

Before you read this article be sure to check out part one in this series.

Before you can use the VI3 SDK with C#, you need to generate C# class files from the VI3 Web Services Description Language (WSDL) files. Although Microsoft Visual Studio .NET will perform this task automatically when you add a Web reference, this crashed my IDE. I believe the reason for this is that the class file generated from vim.wsdl is almost 2 MB in size in .NET 2.0 and 820 KB in size in .NET 1.1.

Your best bet is to download the SDK's WSDL file and save the files to disk.

You can also download the SDK from VMware (you will need to register with VMware to download the SDK). Be sure to download the VI3 SDK package and not the CIM or Guest SDK, because the VI3 SDK contains the Getting Started and Programming guides as well as an extensive collection of HTML files on the SDK object model.

However you obtain the WSDL files, you should end up with two files, vim.wsdl and vimservice.wsdl. VMware is aware of a bug ( with some potential solutions) that causes up to two-minute load times for programs that reference the .NET 2.0 generated VI3 class files because of poor XML serialization. I highly recommend that you generate your VI3 class files using the .NET 1.1 WSDL tool.

Use the .NET tool wsdl.exe to generate C# classes from these files. The syntax for this is:

 %ProgramFiles%\Microsoft Visual Studio .NET
2003\SDK\v1.1\Bin\wsdl.exe
/o:VimApi.cs /n:VimApi vim.wsdl vimservice.wsdl

This command will produce the file VimApi.cs that contains the C# class representation of the WSDL files under the namespace "VimApi." You should replace the path to the WSDL binary with whatever is appropriate for your system, but you should make very sure that you are using the correct version of the binary.

If your machine has both .NET 1.1 and .NET 2.0 installed, then most likely the .NET 2.0 binary will be what gets executed if you simply type "wsdl" in a command prompt (if you have added the relevant paths to your system or user PATH environment variable).

Although the .NET 1.1 class file will work in .NET 2.0, the class file generated with the .NET 2.0 version of wsdl.exe will not work in .NET 1.1 because the .NET 2.0 generated files include code exclusive to .NET 2.0, such as partial classes. It is important to know which version of the WSDL binary you are using.

Congratulations, you are ready to start coding your first C# program with the VI3 SDK! And I already *have* your first C# program that uses the VI3 SDK right here beside me!

The name of the program is "hostshow." The program is a small, simple binary file that when executed, shows all of the hosts accessible by the VI3 SDK. We will look at the parts of the code that interact with the VI3 SDK line-by-line.

You can download the code and other files we will be looking at here from my Website. Decompress the zip file once it has been downloaded so that we can examine the included files:

- - hostshow.cs (This is the main source file.)

- - make_all.bat (This batch file builds a .NET 1.1 binary and a .NET 2.0 binary from the source.)

- - make_dotnet_1.1.bat (This batch file builds a .NET 1.1 binary from the source.)

- - make_dotnet_2.0.bat (This batch file builds a .NET 2.0 binary from the source.)

- - vim.wsdl (This is the same vim.wsdl that comes with the VI3 SDK.)

- - vimservice.wsdl (This is the same vimservice.wsdl that comes with the VI3 SDK.)

- - VimApi_dotnet_1.1.cs (This is the C# source file generated from vim.wsdl and vimservice.wsdl using the .NET 1.1 wsdl.exe tool.)

- - VimApi_dotnet_2.0.cs (This is the C# source file generated from vim.wsdl and vimservice.wsdl using the .NET 2.0 wsdl.exe tool.)

So right about now you may be thinking to yourself, "Why did he make me go through all of the trouble of building the class file VimApi.cs if he was going to give it to me?!?" The answer is practice, pure and simple. The more comfortable you are with VI3, C# and their related tools, the more adept you be at writing programs that leverage them.

Although the file "make_dotnet_2.0.bat" uses the .NET 2.0 version of the VimApi file, it is possible to use the .NET 1.1 version of the VimApi file with .NET 2.0 code. In fact, doing so will speed up .NET 2.0 applications that use the VI3 SDK to almost the same speeds as their .NET 1.1 counterparts.

I did not do it this way because I wanted to keep things as straightforward as possible when explaining how to build a C# program that uses the VI3 SDK, but I wanted you to know that it can be done.

There is more code in hostshow.cs than we are going to look at in this article, but the code we are not looking at does not interact with the VI3 SDK; rather it is the code necessary to provide a useful command line application.

I am sure most people reading this article have their own methods of writing command line apps, and my way is certainly no better than yours. So use mine, use yours. It does not matter. What does matter is how we make our programs talk to Virtual Infrastructure 3, so turn to line ~57 in hostshow.cs and let us begin.


  /*
  * We are declaring the variables we will use to talk to the
  * VI3 SDK. We declare them outside
  * the scope of the try block so that we can gracefully close 
  * an open connection to the server if an unhandled exception occurs.
  */
// This is the class for the very special managed object ServiceInstance, 
// the root of all things VI3 SDK
  VimService vim_svc = null;
  // This is the class that contains information relative to the 
  // current ServiceInstance, in this case, VimService
  ServiceContent vim_svc_content = null;
  // Even though the ServiceInstance is a special ManagedObject, 
  // it is still a ManagedObject and you pass
  // it to the Web service with a ManagedObjectReference (MoRef), in this case, 
  // vim_svc_ref
  ManagedObjectReference vim_svc_ref = null;
  // Be prepared to handle any errors that might occur connecting to the SDK 
  // and then accessing information from the SDK.
try
  {
   // Get the MoRef for the ServiceInstance
   vim_svc_ref = new
ManagedObjectReference();
   vim_svc_ref.type = "ServiceInstance";
   vim_svc_ref.Value = "ServiceInstance";
   /*
    * Prepare the VimService object to connect to the SDK by instantiating it,
    * building the URL to the SDK and creating a CookieContainer to store the
    * cookie that is returned upon a successful logon. The funny looking line where
    * we set the vim_svc.Url is parsing the command line options to build the URL and
    * to determine whether or not to use SSL to connect to the SDK. The ? followed by
    * the : is the ternary operator, a more elegant (albeit sometimes confusing)
    * way to perform a standard if/then operation.
    */
   vim_svc = new VimService();
   vim_svc.Url = string.Format(
CultureInfo.CurrentCulture,
    "http{0}://{1}/sdk",
    cmd_line_opts.Ssl ? "s" : "",
    cmd_line_opts.Server );
   vim_svc.CookieContainer = new
System.Net.CookieContainer();
   // The next line is self-explanatory.
   vim_svc_content =
vim_svc.RetrieveServiceContent( vim_svc_ref );
   /*
    * Log in to the SDK. This is where you will most likely see errors in your 
    * environment if your local computer does not trust the SSL certificate on
    * the SDK server or if you enter an invalid username and/or password. For
    * the former problem, you can see an 
article I wrote on VI3 and certificates.
    */
   vim_svc.Login(
vim_svc_content.sessionManager,
cmd_line_opts.Username, cmd_line_opts.Password, null );
   /*
    * Now that we are logged in, we have to search for all the hosts. Because 
    *  we do not know
    * where the hosts are in the SDK hierarchy, we have to search for them. As
    * we saw in part 1 of this series, it is possible to build a static path to find 
    * an object in the SDK's hierarchy, but if we do not know the location of said 
    * object, we have to account for all possible paths to that object. We must
    * build severalTraversalSpecifications. The name of the class bluntly gives 
    * away its purpose. A TraversalSpecification defines the possible search 
    * paths to an object, or to put it another way, it specifies how to traverse 
    * the SDK hierarchy. To read more about traversal specifications, please 
    * see page 128 of the VMware Infrastructure SDK Programming Guide and
    * page 81 of the VMware Infrastructure SDK Getting Started Guide
    */

   // Now let's look at this first tspec (what I'll call Traversal Specifications 
   // from here on out) in detail so that we can really understand what it's doing.

   // I name my tspecs so that they are self-explanatory. 'dc2hf' stands for 
   // 'Datacenter to Host Folder'
   TraversalSpec dc2hf = new
TraversalSpec();
   // The type property of a tspec defines what object the tspec applies to in 
   // the SDK hierarchy. In this case, it is the "Datacenter" object.
   dc2hf.type = "Datacenter";
   // The path property of a tspec defines what child object of the object 
   // defined by the type property the traversal looks. In this case the tspec
   // is configured thus far to look at the "Datacenter" object and any of its
   // child objects of type "hostFolder".
   dc2hf.path = "hostFolder";
   /*
    * The skip property defines whether or not to include the object defined
    * by the path property in the result set of the search or to use it only as part
    * of the traversal. This is useful for when we want to traverse an object 
    * without collecting its properties, such as perhaps, as in this case, a folder 
    * that contains hosts. We do not care about the properties of the
    * folder that contains the hosts, only the properties of the hosts themselves. 
    * So we want to set the skip property to TRUE to tell the property collector 
    * to not collect the properties of the object defined in the path property,    
    * the "hostFolder." But you will note that the line of code that does this 
    * is commented out, and by leaving this property undefined, it will 
    * default to false. In this particular case, whether the skip property is
    * TRUE or FALSE does not matter because of this searches' 
    * PropertySpecification. More on that when I get to it.
    */
   // dc2hf.skip = true;
   /*
    * The selectSet property is an array of SelectionSpecifications that can contain
    * tspecs. This attribute allows you to define the next step in
    * in the traversal. You may be asking yourself, "Isn't that what you said the
    * 'path' attribute is for?" You are correct; however, the selectSet property
    * exists for when you need to define additional traversal paths.
    * You can also reference named tspec objects with it so that you do not
    * have to redefine them. This is what we are doing here -- we are using the
    * named tspec object 'traverseChild'. This tspec is defined a little bit later;
    * I will point it out when we get there.
    */
   dc2hf.selectSet = new SelectionSpec[] { new SelectionSpec() };
   dc2hf.selectSet[ 0 ].name = "traverseChild";
   /*
    * This tspec defines the relationship ComputeResource-2-host. This is the the
    * tspec that will discover the hosts we are looking for, since all hosts are children
    * of some ComputeResource (even stand-a-lone hosts are children of a single
    * ComputeResource).
    */
   TraversalSpec cr2host = new TraversalSpec();
   cr2host.type = "ComputeResource";
   cr2host.path = "host";
   /*
    * This tspec is one you should get used to since it will almost always be used
    * in any program you write using the VI3 SDK. The Folder-2-childEntity tspec is
    * very important because it is the tspec that you use when dealing with the SDK
    * hierarchy's rootFolder object, the *only* object in the hierarchy you have a
    * reference to initially.
    */
   TraversalSpec f2c = new TraversalSpec();
   f2c.type = "Folder";
   /*
    * You'll notice that we set this tspec's name. This is so that other tspecs can
    * reference it in their selectSet property -- such as we do above with the
    * Datacenter-2-hostFolder tspec (dc2hf). This is *extremely* useful when we
    * need to do recursions on the hierarchy, because a tspec can reference itself,
    * as this one does. The name we choose is arbitrary, but it should not conflict
    * with existing names in the VI3 SDK.
    */
   f2c.name = "traverseChild";
   /*
    * Set the traversal path to 'childEntity', a special value for a folder. A folder
    * can contain many other folders AND a Datacenter, a VirtualMachine, or a
    * ComputeResource, but it can only contain many other folders and ONE 
    * of the other types -- that is a folder cannot contain Datacenter objects 
    * AND VirtualMachine objects.  Instead using the selectSet property of this 
    * tspec to define all of these possible children, we are using the special 
    * vault of 'childEntity' ,which can be ANY of these children.
    */
   f2c.path = "childEntity";
   // See my comments above the line of code that reads 'dc2hf.skip = true;'
   // f2c.skip = true;

   /*
    * We need to configure this tspec to look at other possible traversal
    * paths, such as Datacenters and ComputeResources. So we add the 
    * tspecs we defined for this object types and add them to this tspec's 
    * selectSet property. You may ask why are doing this if the path property 
    * of this tspec is set to childEntity, which I just said should grab Datacenter 
    * and ComputeResource objects? The answer to this
    * question is simply that we need to go beyond just those objects and 
    * traverse the tree further in order to find hosts, hence we have to define
    * the other possible paths to hosts, and these paths include 
    * Datacenter-2-hostFolder and ComputeResource-2-host
    * traversals. Since the path property does not allow us to define 
    * more than a single traversal, we have to use additional traversals 
    * to traverse to where the hosts are.
    */
   f2c.selectSet = new SelectionSpec[] {
new SelectionSpec(),
    dc2hf, cr2host };

   /*
    * A folder can contain another object of type Folder, and even though 
    * a Folder can be a child of a Folder, it is not captured by the childEntity 
    * value, so we have to allow for the possibility of a Folder in a Folder. 
    * To account for this, we employ recursion and set a SelectionSpec's 
    * name value to the name of this tspec, which means
    * that if this tspec encounters a child of type "Folder", it will use this 
    *  tspec to traverse it, starting the cycle over again until it finds an object
    * of another type defined in this tspec's selectSet property -- in this case 
    * a Datacenter of a ComputeResource.
    */
   f2c.selectSet[ 0 ].name = f2c.name;
   // This is the Object Specification used in this search.
   ObjectSpec ospec = new ObjectSpec();
   // We're starting this search with the service instance's rootFolder.
   ospec.obj = vim_svc_content.rootFolder;
   // See my comments above the line of code that reads 'dc2hf.skip = true;'
   // ospec.skip = true;
   // Add the top-level tspec (the Folder-2-childEntity) to the ospec.
   ospec.selectSet = new SelectionSpec[] { f2c };

   // This is the Property Specification use in this search. 
   PropertySpec pspec = new PropertySpec(); 
   /*
    * We're only interested in collecting properties on objects of type 
    * HostSystem.  This is why it does not matter whether we set the 
    * skip property on tspec and the ospec -- the skip property does
    * not apply if there the property specification used in a property 
    * collection does not apply to the object being traversed. Since
    * in this case we are only collecting the properties of the HostSystem
    * object, it does not matter if we skip collecting the properties of 
    * Folders, Datacenters, and ComputeResources because we don't 
    * care about them anyway. The VI3 SDK knows this too and actually 
    * ignores the skip property unless there is a Property Specification that
    * applies to the object being traversed.
    */
   pspec.type = "HostSystem";
   // Do not collect all properties about this object.
   pspec.all = false;
   // Collect only the name property from this object.
   pspec.pathSet = new string[] { "name" };
   // Build the PropertyFilterSpec and set its PropertySpecficiation (propSet)
   // and ObjectSpecification (objectset) attributes to pspec and ospec
   // respectively.
   PropertyFilterSpec pfspec = new
PropertyFilterSpec();
   pfspec.propSet = new PropertySpec[] {
pspec };
   pfspec.objectSet = new ObjectSpec[] {
ospec };
   // Retrieve the property values from the VI3 SDk web service.
   ObjectContent[] occoll =
vim_svc.RetrieveProperties( 
vim_svc_content.propertyCollector, new PropertyFilterSpec[] {
pfspec } );

   // Print out the results of the property retrieval if there were any.
   if ( occoll != null )
   {
    foreach ( ObjectContent oc in
occoll )
    {
   Console.WriteLine(
oc.propSet[ 0 ].val );
    }
   }
  }
finally
  {
   // We'll rethrow the exception, but let's make sure we release
   // the resources of the ServiceInstance as soon as we can.
if ( vim_svc != null )
   {
    vim_svc.Dispose();
   }
  }

I hope that this small application is simple enough and that I explained it with sufficient detail and clarity so that you can use it as a building block to designing your own applications with the VI3 SDK.

In this article, I demonstrated how to query the VI3 SDK for information, such as the available hosts. Stay tuned for part three of this series, where I will describe how to alter your VI3 configuration with the VI3 SDK and C#!

About the author: Andrew Kutz is deeply embedded in the dark, dangerous world of virtualization. Andrew is an avid fan of .NET, open source, Terminal Services, coding and comics. He is a Microsoft Certified Solutions Developer (MCSD) and a SANS/GIAC Certified Windows Security Administrator (GCWN). Andrew graduated from the University of Texas at Austin with a BA in Ancient History and Classical Civilization and currently lives in Austin, Tex., with his wife Mandy and their two puppies, Lucy and CJ.

This was first published in October 2007

Dig deeper on Scripting administrative tasks

0 comments

Oldest 

Forgot Password?

No problem! Submit your e-mail address below. We'll send you an email containing your password.

Your password has been sent to:

SearchServerVirtualization

SearchVirtualDesktop

SearchDataCenter

SearchCloudComputing

Close