The simple javascript example I wrote about last time is actually a great deal more interesting when it's combined with Firefox and Firebug. Using Firebug it's possible to both view the xmlrpc requests and also to browse the fields in the objects returned by the API calls. This becomes particularly enlightening when we have a up-to-date local cache of API objects, enabling the browsing of all of the fields available to API users. This is really useful for everyone developing against the XenServer API, not just web application developers. However, first of all we need to know how to get and maintain this cache, so I'll describe that first.
Using the methods in my last post, to find out about changes in the fields of objects we'd have to poll the XenServer every so often. This would involve large amounts of data transfer, especially when the objects of interest are VMs, which are by far the largest objects in our datamodel. There is a much nicer method of keeping track of the changes on the XenServer, and that is by using two API calls: 'event.register' and 'event.next'.
The basic idea is that you register for xapi to keep a note of changes in classes in which you're interested, e.g. VMs, hosts, or just everything, and when you call 'Event.next' it will give you back all of the objects in those classes that have changed since last time. When there are no events ready to send back, the call will block until one happens, which makes it perfect for an asychronous XHR request. Using the modified jquery.rpc library, making an asynchronous call just involves putting the callback as the last argument to the API call - for example:
rpc.VM.get_all_records(session)
is synchronous, and returns the VM references and records, whereas
rpc.VM.get_all_records(session,callback_fn)
is asynchronous, returns nothing, and the callback function is called with the result when the RPC completes.
In order that we don't miss out any events, we register for events before populating the local cache. So the sequence of calls is:
rpc.event.register(session,["vm"]); // case insensitive cache['vm']=rpc.VM.get_all_records(session); rpc.event.next(session,eventcallback);
Here we've registered for events on VMs - you can specify multiple classes to listen out for or use "*" to mean all classes (except those we don't generate events for, like sessions!) The event callback then updates the cache whenever it receives data, and then in turn again calls event.next:
function eventcallback(evts) {
for(var i=0; i<evts.length; i++) {
evt=evts[i];
if(cache[evt['class']]) {
cache[evt['class']][evt.ref]=evt.snapshot;
}
}
rpc.event.next(session,eventcallback);
}
For the demo application, we register for all classes, but only pay attention to the modification events to VM objects - in this case we simply print out the names of the VMs in <div>s styled according to the power state. To see the event system working, just start or stop a VM, and the page will be updated accordingly. The demo is available here. Once again, to install it, it's easiest to have it served up by the XenServer itself, so ssh in and mkdir /opt/xensource/www - then copy the files from the demo zip in. Edit the 'demo.js' file to put in the correct username and password, then just point your web browser at the server.
As I mentioned at the beginning though, it's much more interesting to look at the demo with Firebug installed, where you can see the xmlrpc requests (note that to take this screenshot I changed the 'use_json' param to 'false'):
Using the DOM browser, you can see the contents of the local cache, including the names and values of all of the fields available to XenAPI clients:
To start off my blog here, I've put together a small example of how to use javascript to interact with an unmodified XenServer using the XMLRPC API. Mostly of the work is done by the awesome jquery library and a slightly modified jquery.rpc plugin. To illustrate the basics this demo will simply list the names of the VMs and templates that XenServer knows about - in subsequent posts I'll put together some more interesting examples. A zip file containing the code can be found here.
The demo consists of an index html page which more-or-less just includes the two jquery files and the demo.js file. The simplest way of trying it out is to is to put the files onto the XenServer to be served by the integrated web server. This is done by creating the webserver root directory '/opt/xensource/www' and copying the files in. Alternatively, and more usefully, the files can be loaded locally from a file:// URI. However, this is more tricky because there may well be cross-site scripting protection built into the browser - certainly Firefox has this feature and there are commented-out lines in the demo files explaining how to deal with this.
I'll just describe here the demo.js file. To talk to the XenServer, we must first create the jquery rpc object. In versions of XenServer prior to 5.0 the system.listMethods method was not implemented, so we have to explicitly list the methods that we'll be using when creating the rpc object.
rpc = new $.rpc( actual_uri, "xml", nextfn, null, ["session.login_with_password","VM.get_all_records"] );
In this case, we're only going to use two API calls. Of the other parameters, 'actual_uri' is the URI of the XenServer to connect to, and could be e.g. "http://<ip>/" if the files are not being hosted by XenServer, or simply "/" if they are. 'nextfn' is the function to call on successful creation of the rpc object. Once the object is initialised, we can call API functions:
var session_result = rpc.session.login_with_password(username,password); if(session_result.Status=="Failure") { raise("Failed to log in"); } mysession = session_result.result.Value;
When we first started investigating the javascript route to the API, we found that the parsing of the XMLRPC results was often very slow indeed. We tried a couple of methods for dealing with this - one interesting idea was to use an XSLT transform to turn the xmlrpc response into json, which worked well and significantly speeded up the processing time of calls that produced large results (e.g. VM.get_all_records). However, it was decided as a tradeoff between complexity and effectiveness that inside xapi itself we would put a little converter that translated the XMLRPC 'result' field into json, leaving it embedded in a small amount of xmlrpc to avoid adding an entirely new layer of logic into xapi. This had a huge impact on the speed of processing for very little code change. In order to access this semi-json API it's a simple case of appending '/json' to the URI used for the standard XMLRPC calls. I've added a 'use_json' field to the demo javascript as an example of how it's used.
Next time I'll explain how the event mechanism works, and how it can be used to keep an up-to-date cache of the entire XenServer database.