A while ago, I wrote a Knowledge Base article (CTX136426) documenting the Windows Management Instrumentation (WMI) available in XenServer to transfer data between the hypervisor and an operating system within a VM such as a XenDesktop or XenApp VD. I still frequently get enquiries on our SDK forum that are basically looking to see if such an interface exists so I’m republishing my article here in the hope that google finds it easier to index!
The WMI interface in XenServer uses the underlying Xen hypervisor XenStore datastore. It is of particular use to developers looking to build products for the XenServer platform such as anti-virus or backup products for XenDesktop or management/monitoring solutions. However it could also be exploited by an experienced System Administrator to develop their own scripts and tools. WMI offers a mechanism to access information about a Windows Virtual Machine from within the guest Virtual Machine (VM), such as the name of the VM or its IP address.
I had a lot of help from a very helpful XenServer developer (Ben Chalmers) in their Windows tools team to write this article, he even wrote several code examples below to get others started.
The information is fairly technical but we’ve had a lot of requests for more technical blogs recently, such as feedback from the UK User Group. We do try to listen and accommodate all tastes 😉
The History of This Functionality
The XenStore_Client.exe is a small executable program that was (in previous versions of XenServer) distributed with the XenServer Tools and enabled users to access the value of parameters contained in XenStore. This program was removed from XenServer 6.1.0 as there was no knowledge of dependencies or other consumers. However, after the program was removed, Citrix noticed that there were users relying on this unsupported tool. XenStore_Client.exe was never officially supported or tested.
XenServer 6.1.0 introduced an alternative mechanism for extracting XenStore information by using Windows Management Instrumentation (WMI), which offers a far richer interface. This article provides information about using WMI for extracting XenStore information.
Developers wishing to use XenServer 6.2.0 and above and the Windows guest WMI interface as for communicating with XenStore and other hypervisor interfaces should read CTX136422 outlining its usage and providing code examples. The WMI interface can be used in XenServer 6.1.0 as an alternative to XenStore_Client.exe which was available in earlier versions of XenServer.
Summary
The XenStore_Client.exe is a small executable program that was (in previous versions of XenServer) distributed with the XenServer Tools and enabled users to access the value of parameters contained in XenStore. This program was removed from XenServer 6.1.0 as there was no knowledge of dependencies or other consumers. However, after the program was removed, Citrix noticed that there were users relying on this unsupported tool. XenStore_Client.exe was never officially supported or tested.
XenServer 6.1.0 introduces an alternative mechanism for extracting XenStore information by using Windows Management Instrumentation (WMI), which offers a far richer interface. This article provides information about using WMI for extracting XenStore information.
Background
This article is for (XenServer 6.1.0 and above) customers who use the Windows guest WMI interface as for communicating with XenStore and other hypervisor interfaces. The WMI interface can be used in XenServer 6.1.0 as an alternative to XenStore_Client.exe which was available in earlier versions of XenServer.
WMI offers a mechanism to access information about a Windows Virtual Machine from within the guest Virtual Machine (VM), such as the name of the VM or its IP address.
The Windows Guest WMI Interface
The Windows guest WMI interface is provided by the device driver xeniface.sys, which forms part of the XenServer Tools (Windows paravirtualized drivers). This interface is available to users who have been granted access to the root\wmi namespace (that is, the device driver namespace). By default, this is available only for administrators.
The WMI Interface provides a base singleton object (CitrixXenStoreBase) that can be used to generate individual session objects (CitrixXenStoreSession). These session objects represent individual applications (or functions of an application) and are the objects used to interact with XenStore; to read, write, set, and unset watches.
Sessions might also take advantage of the transaction semantics to ensure atomicity of XenStore operations. The lifetime of watches and transactions are limited by the lifetime of the session object which creates them. On driver load, an object CitrixXenStoreBase, is made available through WMI, which is a singleton object representing xenstore Properties: Uint64 XenTime: LocalTime as set by Windows for the VM, and then adjusted by the hypervisor’s clock.
WMI Objects
CitrixXenStoreBase
A singleton object representing XenStore:
Properties | Description |
Uint64 XenTime | The Local Time set by windows for the VM, and then adjusted by the hypervisor’s clock. |
Methods | Description |
AddSession (String Id) |
Add a CitrixXenStoreSession object to the WMI namespace. The returned value (SessionId) is a unique value which can be used to identify the session which has been created (in case multiple sessions have the same string identifier).Returns SessionId. |
CitrixXenStoreSession
Each instance represents a single session of communication with XenStore:
Properties | Description |
String ID | A string to identify the use of the session and to determine if any sessions which have been left open can be closed |
Uint32 SessionID | An integer, unique amongst all sessions. This integer is the same as the integer value returned when AddSession creates the session. |
Methods | Description |
StartTransaction | Until the transaction is ended, all activities on this session occur within a transaction (hence activities will not affect XenStore until the transaction is committed). |
CommitTransaction | Attempt to commit the current session’s transaction. If this fails, the transaction has not succeeded, and may need to be retried. |
EndTransaction | Cancel the current transaction without committing |
GetValue (String Pathname) | Returns string value. Returns the value stored at the path Pathname in XenStore |
GetChildren (String Pathname) |
Returns children { UInt32 NoOfChildNodes, String[NoOfChildNodes]ChildNodes}: For a given node (at PathName), this returns an object containing the number of child nodes, and an array of strings, where each string is the full pathname of a child node. |
RemoveValue (String Pathname): |
Removes an entry from XenStore (and, if they exist, all descendent entries) |
SetValue(String Pathname, String value) | Set the value at a given pathname to Value |
Log(String): | Outputs a log message to the hypervisor |
SetWatch(String Pathname) | Watch a specified XenStore entry for changes. Note: Multiple changes of the same XenStore entry, which occur soon after one another, can be merged into one WMI event. The event is received after all the changes referred to by the merged event occur. |
RemoveWatch (String Pathname) | Remove the watch, set previously with SetWatch. |
EndSession() | Terminate this session. Remove the session object from the WMI namespace. All watches and transactions associated with the session will be ended |
Events
CitrixXenStoreWatchEvent
Event is raised when a particular XenStore entry changes. Multiple close changes of a XenStore event might be merged into one CitrixXenStoreWatchEvent. You can register your interest in receiving such an event by calling CitrixXenStoreSession.SetWatch(pathname)
Properties | Description |
String EventId | The pathname of the XenStore value which has changed |
CitrixXenStoreUnsuspendedEvent
Event is raised whenever a VM resumes from suspended state.
Power Shell Example:
The following example uses PowerShell to show how you can script the XenStore WMI interface:
# Locate the base object $base = gwmi -n root\wmi -cl CitrixXenStoreBase # Create a session $sid = $base.AddSession("MyNewSession") $session = gwmi -n root\wmi -q "select * from CitrixXenStoreSession where SessionId=$($sid.SessionId)" # Write a value $session.SetValue("data/TempValue","This is a string") # Read a value $session.GetValue("data/TempValue").value # Read the current VM's name $session.GetValue("name").value # Remove a value $session.RemoveValue("data/TempValue") #Examine Children $session.GetChildren("data").children # Set Watch $watch = Register-WMiEvent -n root\wmi -q "select * from CitrixXenStoreWatchEvent where EventId='data/TempValue'" -action {write $session.getvalue("data/TempValue") } $session.setvalue("data/TempValue","HELLO") $session.setvalue("data/TempValue","WORLD") $watch.action.output # Copyright (c) Citrix Systems, Inc. # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # 1) Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # 2) Redistributions in binary form must reproduce the above # copyright notice, this list of conditions and the following # disclaimer in the documentation and/or other materials # provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE # COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, # INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED # OF THE POSSIBILITY OF SUCH DAMAGE. #
Visual Basic Example:
rem testwmi.vbs rem a sanity check for the xeniface wmi interface rem can be called using cscript.exe rem will set %ERRORLEVEL% to 0 on success and 1 on failure rem will also output the text 'SUCCESS' or an error message rem rem This code was designed to showcase functionality and rem should not be regarded as production quality. Set objWMIService = GetObject("winmgmts:\\.\root\wmi") Set base = objWmiService.InstancesOf("CitrixXenStoreBase") Dim answer Dim answer2 Dim objItem rem Locate the base object if (base.Count) 1 then wscript.echo("Too many base objects found") wscript.quit(1) end if for each itementry in base rem is there a more trivial way of getting the only item from a collection in vbscript? set objItem = itementry next rem Add two sessions objitem.AddSession "VBSTestSession", answer objitem.AddSession "AnotherVBSTestSession", answer2 rem locate the first session query = "select * from CitrixXenStoreSession where SessionId = '" & answer & "'" Set sessions = objWMIService.ExecQuery(query) if (sessions.count) 1 then wscript.echo("Too many session-1 sessions found") wscript.quit(1) end if for each itementry in sessions rem is there a more trivial way of getting the only item from a collection in vbscript? set session = itementry next rem locate te second session query = "select * from CitrixXenStoreSession where SessionId = '" & answer2 & "'" Set sessions2 = objWMIService.ExecQuery(query) if (sessions2.count) 1 then wscript.echo("Too many session-2 sessions found") wscript.quit(1) end if dim session2 for each ses in sessions2 Set session2=ses next rem ensure we located the expected session if session.Id "VBSTestSession" then wscript.echo("incorrect session found") wscript.quit(1) end if rem blank a set of xenstore entries session.removevalue "data/wmitestrun" rem and put a known set of values there session.SetValue "data/wmitestrun/test1", "Testing" session.SetValue "data/wmitestrun/test2", "123 Testing" session.SetValue "data/wmitestrun/test3", "456 Testing" session.SetValue "data/wmitestrun/test4", "789 Testing" rem read back a value from xenstore, and check that it is right session.getvalue "data/wmitestrun/test1", res if res "Testing" then wscript.echo("failed writing or reading to data/wmitestrun/test1") wscript.echo("read = " & res) wscript.quit(1) end if rem read back a different value from xenstore, and check that it is right session.getvalue "data/wmitestrun/test2", res if res "123 Testing" then wscript.echo("failed writing or reading to data/wmitestrun/test2") wscript.echo("read = " & res) wscript.quit(1) end if rem transactions rem test that aborted transactions don't do anything session.starttransaction() session.SetValue "data/wmitestrun/test1", "WEIRD" session.getvalue "data/wmitestrun/test1", res if res "WEIRD" then wscript.echo("failed writing or reading within transaction to data/wmitestrun/test1") wscript.echo("read = " & res) wscript.quit(1) end if session.aborttransaction() session.getvalue "data/wmitestrun/test1", res if res "Testing" then wscript.echo("failed reading to data/wmitestrun/test1 after aborted transaction ") wscript.echo("read = " & res) wscript.quit(1) end if rem test that 2 overlapping transactions honour commits and aborts, and raise errors when needed session.starttransaction() session2.starttransaction() session.SetValue "data/wmitestrun/test1", "WEIRD" session2.SetValue "data/wmitestrun/test1", "Fish" session.getvalue "data/wmitestrun/test1", res session2.getvalue "data/wmitestrun/test1", res2 if res "WEIRD" then wscript.echo("failed writing or reading within transaction to data/wmitestrun/test1 session 1") wscript.echo("read = " & res) wscript.quit(1) end if if res2 "Fish" then wscript.echo("failed writing or reading within transaction to data/wmitestrun/test1 session 2") wscript.echo("read = " & res) wscript.quit(1) end if on error resume next session.committransaction() Err.clear() if Err.Number 0 then wscript.echo("Could not commit first transaction") wscript.quit(1) end if session2.committransaction() if Err.Number = 0 then wscript.echo("Both transactions comitted") wscript.quit(1) end if session2.aborttransaction() session2.getvalue "data/wmitestrun/test1", res2 if res2 "WEIRD" then wscript.echo("failed commiting the correct transaction") wscript.echo("read = " & res) wscript.quit(1) end if rem events rem set up an event sink dim refsink set refsink = CreateObject("WBemScripting.SWbemSink") wscript.ConnectObject refsink, "EVENTSINK_" stq = "Select * from CitrixXenStoreWatchEvent" objwmiservice.ExecNotificationQueryAsync refsink, stq evtcount = 0 rem watch a xenstore entry allevents=0 session.setwatch "data/wmitestrun/test1" session.setvalue "data/wmitestrun/test1","MAGIC" session.removevalue "data/wmitestrun/test1" session.setvalue "data/wmitestrun/test1","GOLD" wscript.sleep(5000) session.removewatch "data/wmitestrun/test1" rem check we received an event. Also, since events can be coalesced, check rem that when we receive our final event, the value we read from test1 is the rem final value we set it to rem (note the actual work of counting and checking events is done in the rem EVENTSINK_OnObjectready sub below) ) if evtcount <= 4 and allevents 1 then) wscript.echo("Failed to catch all the expected events") wscript.quit(1) end if session.removevalue "data/wmitestrun/test1" rem check that we can read the list of children an entry has strComputer = "." Set objWMIService = GetObject("winmgmts:" _ & "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2") Set colOperatingSystems = objWMIService.ExecQuery _ ("Select * from Win32_OperatingSystem") for each os in colOperatingSystems rem is there a more trivial way of getting the only item from a collection in vbscript? set myos = os next wscript.echo(myos.Version) if Mid(myos.Version, 1 , 3) "6.0" then dim children session.getchildren "data/wmitestrun", children if children.noofchildnodes 3 then wscript.echo("Failed to find all the expected child nodes") wscript.quit(1) end if end if session.getfirstchild "data/wmitestrun", res session.getnextsibling res, res rem end both sessions that we created. session2.EndSession() session.EndSession() Wscript.echo("Success") Sub EVENTSINK_OnObjectReady(re, rc) evtcount = evtcount + 1 session.getvalue "data/wmitestrun/test1", res if res = "GOLD" then allevents = 1 else allevents = 0 end if end sub rem Copyright (c) Citrix Systems, Inc. rem All rights reserved. rem rem Redistribution and use in source and binary forms, with or without rem modification, are permitted provided that the following conditions rem are met: rem rem 1) Redistributions of source code must retain the above copyright rem notice, this list of conditions and the following disclaimer. rem rem 2) Redistributions in binary form must reproduce the above rem copyright notice, this list of conditions and the following rem disclaimer in the documentation and/or other materials rem provided with the distribution. rem rem THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS rem "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT rem LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS rem FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE rem COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, rem INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES rem (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR rem SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) rem HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, rem STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) rem ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED rem OF THE POSSIBILITY OF SUCH DAMAGE. rem
More Information
The following command line tools which are shipped with Windows are particularly useful for exploring WMI:
• Wbemtext.exe provides a graphical user interface (GUI)
• Wmic.exe is a command line tool which can be used for accessing WMI
There are a number of external WMI explorers available.
• Windows Management Instrumentation
• Wikipedia: Windows Management Instrumentation.
Disclaimer
The sample code is provided to you AS IS with no representations, warranties or conditions of any kind. You may use, modify and distribute it at your own risk. CITRIX DISCLAIMS ALL WARRANTIES WHATSOEVER, EXPRESS, IMPLIED, WRITTEN, ORAL OR STATUTORY, INCLUDING WITHOUT LIMITATION WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NONINFRINGEMENT. Without limiting the generality of the foregoing, you acknowledge and agree that (a) the sample code may exhibit errors, design flaws or other problems, possibly resulting in loss of data or damage to property; (b) it may not be possible to make the sample code fully functional; and (c) Citrix may, without notice or liability to you, cease to make available the current version and/or any future versions of the sample code. In no event should the code be used to support of ultra-hazardous activities, including but not limited to life support or blasting activities. NEITHER CITRIX NOR ITS AFFILIATES OR AGENTS WILL BE LIABLE, UNDER BREACH OF CONTRACT OR ANY OTHER THEORY OF LIABILITY, FOR ANY DAMAGES WHATSOEVER ARISING FROM USE OF THE SAMPLE CODE, INCLUDING WITHOUT LIMITATION DIRECT, SPECIAL, INCIDENTAL, PUNITIVE, CONSEQUENTIAL OR OTHER DAMAGES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. Although the copyright in the code belongs to Citrix, any distribution of the code should include only your own standard copyright attribution, and not that of Citrix. You agree to indemnify and defend Citrix against any and all claims arising from your use, modification or distribution of the code.