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?
@QuanT
The answer is to use EventEx instead of ExtendedEvent
@angelo
Hi angelo, I’m hitting the same issue. were you able to fix it?
Hi
We are working on a vCenter plugin which has a number of custom tasks/events associated with it. We have successfully managed to achieve this using your example.
The problem: Tasks/Events can be registered easily but when we unregister the plugin and then register it again with some new messages for Tasks/Events the old messages are shown instead of the new ones. I have noticed that on registration it creates .vmsg files in the following location:
“C:\Program Files\VMware\Infrastructure\VirtualCenter Server\extensions\PLUGIN-KEY\locale\en\”
Apparently these files also contain the updated strings each time the plugin is updated and registered with new strings. Inspecting the tasks from the mob somehow shows the old strings. When the tasks are run they still show those old strings that were supposed to have been updated. This is annoying.
It appears as if the only way to update those messages is to reinstall the vcenter and then register the extension. Even in that case the strings can only be updated once unless the vCenter is reinstalled. Can someone please give an explanation for this behavior of plugin or point out what could have gone wrong?
Good question. I haven’t gone that far to delete anything. What if you delete the .vmsf file after the unregistration? Please make sure to back it up though.
Steve
It appears as if that file has no effect on the string/messages shown inside the mob. So even when the plugin is registered and the file is deleted, the custom tasks continue to show the same old messages. If I delete these files after unregistering, they are created again when the plugin is registered but still this has no effect on the messages shown inside the mob.
Can you please explain why those old messages continue to appear even after I re-register the plugin with the new ones? I have a feeling that there is some sort of key-value cache or database that does not get updated if we try to input a message having a key already present in that database.
If that is the case, I think the information is also stored inside database, therefore recreated if the file is missing. You may want to open a SR with VMware support on this.
Steve
Hi Steve
Thanks for your response. I have noticed that the information is cached somewhere and is not updated when a plugin is registered. If the vCenter services are restarted then everything is read again (probably from the text files) and the information in custom tasks or any other custom strings gets updated.