software development, virtualization

The Guide to Writing Advanced VI3.x/vSphere4 Plug-Ins Part Deux

Welcome to part two of how to write advanced (a.k.a. “unofficial”) plug-ins for the VMware VI3.x and vSphere4 clients. You may be familiar with my first guide to writing VI3.x client plug-ins, the VMware Infrastructure 3.5 Plug-in and Extension Programming Guide. I first published this guide in early 2008 after successfully reverse-engineering the then VI3.x (now vSphere4) client, and this blog post is the follow up — a guide that shows how you can write advanced plug-ins that are compatible with both the VI3.x and vSphere4 clients using a single assembly.

A Little History

After I published my unofficial version VMware responded by releasing their official guide to writing plug-ins for the VI3.x client, but people were still interested in my method because it allowed for true integration, not just a hosted web application. Since that time VMware has released the vSphere4 suite of software including the vSphere4 client. At first VMware included the ability to extend the vSphere4 client at a more primitive level with C#, but now that method is deprecated in favor of their XML-configuration document (similar to how their original VI3.x plug-ins functioned. To me, favoring this type of plug-in development points to the deprecation of the vSphere client itself.

Even if you choose to ignore the notice of deprecation and make use of VMware’s C# extension points, your plug-ins will only work with vSphere4 as VMware’s vSphere4 client plug-ins are not backwards compatible with the VI3.x client. I guess VMware assumes that everyone is already running the latest version of their software (an assumption that is clearly not taking into account their own prices). This wouldn’t be a problem, except the architecture of the VI3.x/vSphere4 client(s) is flawed in that they both look in the same location for plug-ins (and it’s the only place the client(s) will look). You get to choose to use the old VMware.VIClient.Plugins.Plugin interface to create plug-ins that both VI3.x and vSphere4 understand, but that also means your plug-ins cannot take advantage of the new features of vSphere4, such as linked-mode awareness. Or you can write your plug-ins with the new VMware.VIClient.Plugins2.Plugin interface, however your plug-in will not work with VI3.x clients.

That brings us to the reasons for this blog. I aim to demonstrate how you can write advanced plug-ins that are compatible with both the VI3.x and vSphere4 clients by using my own custom PluginHarness that I developed in order to publish singular versions of the Hyper9 GuessMyOSToo and Search/Integration plug-ins.

The Problems

I’m not going to delve too deeply into the architecture of VI3.x plug-ins as I covered that in depth in my last guide. I will instead concentrate on some of the problems that arise for developers who wish to create advanced plug-ins that are compatible with both VI3.x and vSphere4 clients.

The Plug-in Directory

As I mentioned earlier, one of the glaring architectural problems I have observed with the way the vSphere4 client handles loading plug-ins is that it re-uses the same directory location for plug-ins that the VI3.x client used (C:\Program Files\VMware\Infrastructure\Virtual Infrastructure Client\Plugins by default). This presents a problem in that you cannot easily shield the vSphere4 client from detecting VI3.x plug-ins.

Yes, you can disable them, but it would be nice if they were not recognized at all. Especially since once the client detects a class that implements the VI3.x or vSphere4 plug-in interface it no longer scans for other classes that implement a plug-in interface. This means you can only have one class that implements a plug-in interface in a given directory in the plug-in folder — essentially limiting you to one plug-in (of the same name) per VI/vSphere client version. You could possible get tricky and have your plug-ins live in multiple folders and them set their assembly information to be identical, but then again you would see the same plug-in twice in the vSphere4 client’s plug-in configuration screen.

No Backwards Compatibility

The new interface for creating plug-ins does not implement the old interface and thus new plug-ins are not backwards compatible with VI3.x. This is perhaps the biggest problem when creating multi-version plug-ins because you have no way to create a single plug-in that can take advantage of the latest features of the vSphere4 client and at the same time work with the VI3.x client.

Compile Time Dependencies

Another problem that occurs when writing advanced plug-ins that span VI3.x/vSphere4 client versions is linking to VMware assemblies at compile time. For example, you might link to an assembly that contains some Windows Forms controls that you want to use in your plug-in. Heck, even one of VMware’s Updates (up to U5 for 3.x) can potentially break this as those updates frequently contain new versions of certain assemblies (ex. VMware.CustomControls.25.dll vs. VMware.CustomControls.25u2.dll).

This dependency means that developers will need to either a) not link to any of VMware’s own assemblies at compile time (other than their plug-in assembly) or b) release a new version of their plug-in every time VMware issues updates that affect the assemblies that have been linked at compile time.

The Solution

Faced with all of these problems I decided that the best bet was to create what I call a plug-in harness. The harness provides a single class that developers can use to create their plug-ins that does all of the heavy lifting when it comes to things like vSphere4 specific features and dependency management. Simply put, if you write your plug-ins using my harness, you do not need to worry about VMware’s underlying client version. As long as you are using the latest version of the harness code you will be fine.

In Practice

Of course, the best way to explain the plug-in harness is with a real-world example, in this case the recently open sourced GuessMyOSToo plug-in.

Again, because it bears repeating, I’m not going to start from the beginning on how to write plug-ins since I covered this previously in my last guide. If you are not familiar with writing plug-ins or have not read my original paper, I suggest you do so now.

For those of you who want to follow along in VisualStudio, the full solution for the GuessMyOSToo plug-in can be checked out via Subversion with the following command:

svn co https://guessmyostoo.svn.sourceforge.net/svnroot/guessmyostoo/trunk guessmyostoo

Because the project is so small anyway, I included the dependencies in the repository as well. So if you have VisualStudio 2008 or greater you should be able to open the solution file and follow along.

The GuessMyOSToo plug-in is made up of two primary files: the plug-in class, ThePlugin.cs and the plug-in harness, PluginHarness.cs. Let’s begin the tutorial by taking a look at the plug-in class.

The Implemented Plug-in Interface

You may notice that the plug-in class still implements the VI3.x plug-in interface VMware.VIClient.Plugins.Plugin:

public class ThePlugin : VMware.VIClient.Plugins.Plugin

This is because for a plug-in to be both VI3.x and vSphere4 compatible, the interface must be used that both client versions understand, and because the least common denominator is VI3.x, the old interface must be used. Of course, this requirement has its own consequences. The most important of which is that this plug-in is not immediately linked-mode aware.

Linked-Mode and Loading Plug-ins

One of the touted features of vSphere4 is vCenter’s ability to operate in Linked Mode. For an administrator using the vSphere4 client this means they can manage multiple instances of vCenter from the same client instance. For a developer creating plug-ins however, this means that the plug-ins must be aware of each vCenter instance the client is connected to as well as the credentials used to establish the connection.

vSphere4’s new plug-in framework allows developers to decorate their plug-in assembly with a special attribute called VMware.VIClient.Plugins2.MultiVCSupported which allows a plug-in to be informed when multiple vCenters are being managed via the vSphere4 client that the plug-in is loaded into. The net result of this is that a vSphere4 derived plug-in is only instantiated once and thus its Load method is also only called once. However, plug-ins designed to be both VI3.x and vSphere4 compatible cannot rely on this attribute as it only works with vSphere4-exclusive plug-ins and thus, as I’ve already mentioned, must derive from the VI3.x plug-in interface.

When the vSphere4 client detects and loads a VI3.x derived plug-in, the plug-in class is instantiated once for every instance of a vCenter that the vSphere4 client has access to. This may seem like an easy solution to the problem of making VI3.x plug-ins multi-instance aware, but it actually creates a problem. The plug-in’s Load method may contain code that should be executed once and only once. The solution is to create a static Load method that only gets executed once and let the instance Load methods simply keep track of the multiple vCenter instances and their credentials.

And that is exactly what the plug-in harness provides to the plug-in class. Let’s take a look at the body of the plug-in’s Load method:

public void Load(VIApp viApp)
{
	PluginHarness.Load(viApp, null, delegate
	{
		// ...
	});
}

Right away you will notice that the first line of the method body calls a static method of the PluginHarness class called Load:

public static void Load(
		VIApp viApp,
		MultiLoadDelegate multiLoadDelegate,
		OneTimeLoadDelegate oneTimeLoadDelegate)
{
	// Get this plug-in's service instance.
	var si = CreateServiceInstance(viApp);

	// Define the server's UUID.
	var suuid = PRE_VSPHERE4_SUUID;

	if (IsLessThanVSphere4())
	{
		Trace("adding <=vi3 viapp : " + suuid);
		viAppsRwl.AcquireWriterLock(Timeout.Infinite);
		viApps.Add(
			suuid,
			viApp);
		viAppsRwl.ReleaseWriterLock();
	}
	else
	{
		suuid = si.Content.about.instanceUuid;
		Trace("adding >=vi4 viapp : " + suuid);
		viAppsRwl.AcquireWriterLock(Timeout.Infinite);
		viApps.Add(
			suuid,
			viApp);
		viAppsRwl.ReleaseWriterLock();
	}

	// Get the server's IP addresses.
	var uri = new Uri(viApp.ServiceUrl);
	Trace("got server uri: " + uri);
	var safeHost = uri.DnsSafeHost;
	Trace("got dns safe host: " + safeHost);
	var ipAddresses = Dns.GetHostAddresses(safeHost);

	if (ipAddresses != null)
	{
		foreach (var ipa in ipAddresses)
		{
			var ips = ipa.ToString();

			Trace(
				String.Format(
					"server host {0} has ip of {1}",
					safeHost,
					ips));

			// Cache the server IP
			serverIPsRwl.AcquireWriterLock(Timeout.Infinite);
			serverIPs.Add(
				ips,
				si);
			serverIPsRwl.ReleaseWriterLock();

			// Cache the VI App IP
			viAppIPsRwl.AcquireWriterLock(Timeout.Infinite);
			viAppIPs.Add(
				ips,
				viApp);
			viAppIPsRwl.ReleaseWriterLock();
		}
	}

	// Cache the service instance.
	serviceInstancesRwl.AcquireWriterLock(Timeout.Infinite);
	serviceInstances.Add(
		suuid,
		si);
	serviceInstancesRwl.ReleaseWriterLock();

	// Cache the server UUID.
	serverUuidsRwl.AcquireWriterLock(Timeout.Infinite);
	serverUuids.Add(
		viApp,
		suuid);
	serverUuidsRwl.ReleaseWriterLock();

	// Execute the load delegate.
	if (multiLoadDelegate != null)
	{
		multiLoadDelegate.Invoke(viApp);
	}

	// Signal that this method has been invoked.
	Interlocked.Increment(ref oneTimeLoadMethodInvocationCount);

	// This method does not need to be invoked more than once because
	// everything else we do is static.
	if (oneTimeLoadMethodInvocationCount > 1)
	{
		Trace("one-time load method already invoked");
		return;
	}

	ThreadPool.QueueUserWorkItem(
		Load,
		oneTimeLoadDelegate);
}

The above code accomplishes several things.

  1. Create a Service Instance Reference – This step creates a reference to the service instance that the plug-in is currently being loaded for. This maps to a VirtualCenter/vCenter.
  2. Store a Reference to the VIApp – Depending on the version of the client a reference to the VIApp passed to the plug-in is stored in a synchronized list.
  3. Get the Server’s IP Address – The IP address of the targeted VirtualCenter/vCenter is indexed by both the associated ServiceInstance and VIApp objects.
  4. Caching – The ServiceInstance and server UUID are both cached.
  5. Execute the Multi-Load Delegate – This delegate is invoked per vCenter.
  6. Execute the One-Time-Load Delegate – This delegate is invoked only once, the first time the plug-in is loaded.

That is really the meat of how the plug-in harness works. It allows you to create plug-ins that work both in VI3, non-linked mode vSphere4, and linked-mode vSphere4 environments. Through the plug-in harness, your plug-in can access all ServiceInstances operating in linked mode in an authenticated fashion. The plug-in harness provides several other capabilities as well:

  • A Trace method for logging to the official VI client log file.
  • Access to many of the internal VI client UI components such as the main Windows Form, the Inventory list, etc.
  • Access to the internal VI client event notification system
  • Synchronizes access to all collections as well as the unloading of the plug-in.

Hope this helps!

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s