Creating Your Own Task and Event in vSphere
vSphere has a powerful extension mechanism that allows you to add new features as integral part of the platform. Many vendors have already leveraged this by providing plug-ins so that users can manage their components seamlessly within same vSphere Client.
You can actually do more than that with the extension. The following sample shows how to create your own task and event with vSphere API. The code should be self explanatory therefore I don’t elaborate much here. Note that you must run the sample with a vCenter server as extensibility is implemented only in vCenter.
When running the code, you can see a new task created and progresses with 10% every second in the “Recent Tasks” pane of vSphere Client. When the task is done, you will also see a new event posted in the “Tasks & Events” tab of the host you associate the task with.
What can you do with this capability? Here are two typical use cases:
- Use it in your vSphere Client plug-in so that progress of a chosen operation is visible to all logged in users. In that case, you don’t need to create additional extension;
- Use it in your server extension applications such as VMware Site Recovery Manager.
You can also use it in a utility program as this sample, but may be an over use of this feature.
package com.vmware.vim25.mo.samples;
import java.net.MalformedURLException;
import java.net.URL;
import java.rmi.RemoteException;
import java.util.Calendar;
import com.vmware.vim25.Description;
import com.vmware.vim25.Extension;
import com.vmware.vim25.ExtensionEventTypeInfo;
import com.vmware.vim25.ExtensionResourceInfo;
import com.vmware.vim25.ExtensionTaskTypeInfo;
import com.vmware.vim25.KeyValue;
import com.vmware.vim25.TaskInfo;
import com.vmware.vim25.TaskInfoState;
import com.vmware.vim25.mo.ExtensionManager;
import com.vmware.vim25.mo.HostSystem;
import com.vmware.vim25.mo.InventoryNavigator;
import com.vmware.vim25.mo.ManagedEntity;
import com.vmware.vim25.mo.ServiceInstance;
import com.vmware.vim25.mo.Task;
import com.vmware.vim25.mo.TaskManager;
public class CreateCustomTask
{
final static String EXTENSION_KEY = "DoubleCloud";
public static void main(String[] args) throws RemoteException, MalformedURLException, InterruptedException
{
if (args.length != 3)
{
System.out.println("java CreateCustomTask <SdkUrl> <username> <password>");
System.out.println("java CreateCustomTask https://192.168.8.8/sdk administrator password");
return;
}
ServiceInstance si = new ServiceInstance(new URL(args[0]), args[1], args[2], true);
ManagedEntity[] mes = new InventoryNavigator(si.getRootFolder()).searchManagedEntities("HostSystem");
HostSystem host = (HostSystem) mes[0];
ExtensionManager em = si.getExtensionManager();
if(em.findExtension(EXTENSION_KEY)==null)
{
em.registerExtension(createExtension());
}
//allow a little time for vCenter to persist the extension data
Thread.sleep(3000);
TaskManager tm = si.getTaskManager();
TaskInfo ti = tm.createTask(host, "AcmeBackupTask", "Administrator", true);
Task task = new Task(si.getServerConnection(), ti.getTask());
//change the state to running before updating the progress
task.setTaskState(TaskInfoState.running, null, null);
for(int i=1; i<=10; i++)
{
System.out.println("percentage done: " + i*10);
task.updateProgress(i*10);
Thread.sleep(1000);
}
task.setTaskState(TaskInfoState.success, null, null);
//remove extension. you can leave it there and no need to register next time
em.unregisterExtension(EXTENSION_KEY);
si.getServerConnection().logout();
}
private static Extension createExtension()
{
Extension extension = new Extension();
Description desc = new Description();
desc.setLabel("DoubleCloud");
desc.setSummary("A sample extension with a new type of task.");
ExtensionTaskTypeInfo etti = new ExtensionTaskTypeInfo();
etti.setTaskID("AcmeBackupTask");
ExtensionEventTypeInfo eeti = new ExtensionEventTypeInfo();
eeti.setEventID("AcmeBackupEvent");
KeyValue kv1 = new KeyValue();
kv1.setKey("AcmeBackupTask.category");
kv1.setValue("info");
KeyValue kv2 = new KeyValue();
kv2.setKey("AcmeBackupTask.label");
kv2.setValue("Acme backup task");
KeyValue kv3 = new KeyValue();
kv3.setKey("AcmeBackupTask.fullFormat");
kv3.setValue("Acme backup virtual machines on host {hostname}");
ExtensionResourceInfo eri = new ExtensionResourceInfo();
eri.setLocale("en");
eri.setModule("task");
eri.setData(new KeyValue[] {kv1, kv2, kv3});
extension.setDescription(desc);
extension.setKey(EXTENSION_KEY);
extension.setVersion("1.0.0");
extension.setLastHeartbeatTime(Calendar.getInstance());
extension.setCompany("Acme Inc.");
extension.setTaskList(new ExtensionTaskTypeInfo[] { etti });
extension.setEventList(new ExtensionEventTypeInfo[] { eeti });
extension.setResourceList(new ExtensionResourceInfo[] { eri });
return extension;
}
}
import java.net.MalformedURLException;
import java.net.URL;
import java.rmi.RemoteException;
import java.util.Calendar;
import com.vmware.vim25.Description;
import com.vmware.vim25.Extension;
import com.vmware.vim25.ExtensionEventTypeInfo;
import com.vmware.vim25.ExtensionResourceInfo;
import com.vmware.vim25.ExtensionTaskTypeInfo;
import com.vmware.vim25.KeyValue;
import com.vmware.vim25.TaskInfo;
import com.vmware.vim25.TaskInfoState;
import com.vmware.vim25.mo.ExtensionManager;
import com.vmware.vim25.mo.HostSystem;
import com.vmware.vim25.mo.InventoryNavigator;
import com.vmware.vim25.mo.ManagedEntity;
import com.vmware.vim25.mo.ServiceInstance;
import com.vmware.vim25.mo.Task;
import com.vmware.vim25.mo.TaskManager;
public class CreateCustomTask
{
final static String EXTENSION_KEY = “DoubleCloud”;
public static void main(String[] args) throws RemoteException, MalformedURLException, InterruptedException
{
if (args.length != 3)
{
System.out.println(“java CreateCustomTask <SdkUrl> <username> <password>”);
System.out.println(“java CreateCustomTask https://192.168.8.8/sdk administrator password”);
return;
}
ServiceInstance si = new ServiceInstance(new URL(args[0]), args[1], args[2], true);
ManagedEntity[] mes = new InventoryNavigator(si.getRootFolder()).searchManagedEntities(“HostSystem”);
HostSystem host = (HostSystem) mes[0];
ExtensionManager em = si.getExtensionManager();
if(em.findExtension(EXTENSION_KEY)==null)
{
em.registerExtension(createExtension());
}
//allow a little time for vCenter to persist the extension data
Thread.sleep(3000);
TaskManager tm = si.getTaskManager();
TaskInfo ti = tm.createTask(host, “AcmeBackupTask”, “Administrator”, true);
Task task = new Task(si.getServerConnection(), ti.getTask());
//change the state to running before updating the progress
task.setTaskState(TaskInfoState.running, null, null);
for(int i=1; i<=10; i++)
{
System.out.println(“percentage done: ” + i*10);
task.updateProgress(i*10);
Thread.sleep(1000);
}
task.setTaskState(TaskInfoState.success, null, null);
//remove extension. you can leave it there and no need to register next time
em.unregisterExtension(EXTENSION_KEY);
si.getServerConnection().logout();
}
private static Extension createExtension()
{
Extension extension = new Extension();
Description desc = new Description();
desc.setLabel(“DoubleCloud”);
desc.setSummary(“A sample extension with a new type of task.”);
ExtensionTaskTypeInfo etti = new ExtensionTaskTypeInfo();
etti.setTaskID(“AcmeBackupTask”);
ExtensionEventTypeInfo eeti = new ExtensionEventTypeInfo();
eeti.setEventID(“AcmeBackupEvent”);
KeyValue kv1 = new KeyValue();
kv1.setKey(“AcmeBackupTask.category”);
kv1.setValue(“info”);
KeyValue kv2 = new KeyValue();
kv2.setKey(“AcmeBackupTask.label”);
kv2.setValue(“Acme backup task”);
KeyValue kv3 = new KeyValue();
kv3.setKey(“AcmeBackupTask.fullFormat”);
kv3.setValue(“Acme backup virtual machines on host {hostname}”);
ExtensionResourceInfo eri = new ExtensionResourceInfo();
eri.setLocale(“en”);
eri.setModule(“task”);
eri.setData(new KeyValue[] {kv1, kv2, kv3});
extension.setDescription(desc);
extension.setKey(EXTENSION_KEY);
extension.setVersion(“1.0.0″);
extension.setLastHeartbeatTime(Calendar.getInstance());
extension.setCompany(“Acme Inc.”);
extension.setTaskList(new ExtensionTaskTypeInfo[] { etti });
extension.setEventList(new ExtensionEventTypeInfo[] { eeti });
extension.setResourceList(new ExtensionResourceInfo[] { eri });
return extension;
}
}
Wow! It’s really awesome feture of vCenter.
Steve is there any ability to set IP address (ip,netmask, gateway) to concrete ethernet adapater of VM by means of vCenter???
I’ve tried a lot of things but unfortunatelly still there is no results.
Do you mean you want to change the networking setting like IP address by connecting to vCenter server? What API have you tried?
Steve
Hi,
I have used this in our product, but when I call findExtention on ExtensionManager it returns me a extension object with empty ResourceList. Do you know why this must be happening. I wanted to updateExtension due to some reasons and so I added some more events and resoure info list for new event keys and next updateExtension. But it overrides old values and the description in vSphere for old events is seen as XXX.
Does any one know about this ? Please revert back if any solution / information.
Regards,
Yogesh G
This is the most comprehensive description I could find of how to do this- thanks!
But there’s always one more question…
In my case I was wondering how to specify some elements of the formatted message when an event is posted, rather than when the event type is specified in an extension.
If I leave the formatted message empty in the definition and attempt to set it at creation-time, it is filled in with what looks like an identifier for this property, e.g. “event.com.my.event.blah”
So, next, I assume that the {} syntax can be used for argument replacement at event-creation time. I assume that the use of {} in the formatted message will correspond to the arguments in the eventTypeSchema.
However, I find that if I try to specify an XML event type schema using VI Java, the SOAP parser seems to be confused by the unexpected XML, (somewhat like the story of the boy called “Robert Drop Table” in xkcd!).
The error trace is:
—-
Unexpected element tag “EventType” seen
while parsing serialized value of type string
at line 1, column 737
while parsing property “eventTypeSchema” of static type string
while parsing serialized DataObject of type vim.Extension.EventTypeInfo
at line 1, column 678
while parsing property “eventList” of static type ArrayOfExtensionEventTypeInfo
while parsing serialized DataObject of type vim.Extension
at line 1, column 377
while parsing call information for method RegisterExtension
at line 1, column 285
while parsing SOAP body
at line 1, column 271
while parsing SOAP envelope
at line 1, column 38
while parsing HTTP request for method registerExtension
on object of type vim.ExtensionManager
at line 1, column 0
—————————–
Is there some way of “escaping” the XML, or is there another way of defining arguments for replacement at event creation time?
Thanks!
@Bob Fletcher
And of course, immediately after posting, I had a d’oh moment. Simply turn the of the eventTypeSchema into < and > and it works a treat. Hope this is of use to someone else…
Hi Bob,
Glad you find it useful. If you want to include XML data in any string using vijava, you want to escape the 5 special characters like & etc.
-Steve
Great, thanks!
I’m afraid I have a follow-up question…
I find that when creating custom tasks as described, I get an error in the “Task” field of the corresponding event listing which looks like: “**NO DESCRIPTION FOR: AcmeBackup Task”.
Where should this description be defined?
I assumed the the “.fullFormat” key-value pair defined this, and have also tried using a “.summary” key as suggested elsewhere.
I’ve also tried setting Task.setTaskDescription at creation time, but this resulted in an invalid request error…
I’m using vSphere 5.0.0 beta if that’s relevant.
Thanks!
@Yogesh
Your obervation is correct. The resource list is always Unset in MOB and you won’t find it in returned extension object either. It is by design according to VMware. On the same token, updating resource list is actually creating a new one. So you will have to include previously defined events, in addition to new ones, in your call. BTW, the resources are stored in files not database: C:\Program Files\VMware\Infrastructure\VirtualCenter Server\extensions. However I will still use Web Service calls to alter these files to be safe.
@Bob Fletcher
You have noticed the column head reads “Task”. So the key is “.label”, whose value shows up in othe two places as you may already see, and you should see “Acme backup event” after running Steve’s sample. I have tried my other apps with 5.0 and nothing is broken yet. So I doubt the version is the cause. It behaves as expected in both my C# and Java codes. I did observe this once while migrating my codes from C# to Java. Somehow it disappeared after I restarted my Java dev environment for a different reason.
This is about task notification. How can we post event which is not task?
In my server extension, I define my event with ExtensionEventTypeInfo(), and in there I define the eventTypeSchema property with an XML that contains 1 argument (snip) chassisstring.
I also register a message resource for use with this event type, which contains the .fullformat string as “Show {chassis} info”.
Question: when I post an event, how should I pass in a value for this argument so that it replaces the {chassis} argument when displayed?
To post an event of the above type, I first create an ExtendedEvent object and do a PostEvent(), but can’t find a way to get the event .fullformat string to show the {chassis} value. I played with the ExtendedEvent.data as well as ExtendedEvent.dynamicProperty but none worked.
Hi I have successfully registered a task and can update its using task.setTaskState(state, null, null), where state is either TaskInfoState.error or TaskInfoState.success. The problem I am having is setting the status message when I have an error. The documentation suggests
LocalizedMethodFault msg = new LocalizedMethodFault();
msg.localizedMessage = “My error message”;
msg.fault = null;
vmTask.setTaskState(TaskInfoState.error, null, msg);
But if I do this I get an exception thrown com.vmware.vim25.InvalidRequest
This is driving me nuts. Does anyone have any idea? Should the localizedMessage be a key value lookup into a message catalogue somewhere?