vboxapi.py revision 7225c19c065cadcc51171f0ad664cc4a93303edd
c0829c6fca24bd3a9a687c93f9b64da2c54b767cvboxsync#
c0829c6fca24bd3a9a687c93f9b64da2c54b767cvboxsync# Copyright (C) 2009 Oracle Corporation
20bc99aa3ad84a338b7a9c1edc14682bce25d761vboxsync#
c0829c6fca24bd3a9a687c93f9b64da2c54b767cvboxsync# This file is part of VirtualBox Open Source Edition (OSE), as
c0829c6fca24bd3a9a687c93f9b64da2c54b767cvboxsync# available from http://www.virtualbox.org. This file is free software;
c0829c6fca24bd3a9a687c93f9b64da2c54b767cvboxsync# you can redistribute it and/or modify it under the terms of the GNU
e64031e20c39650a7bc902a3e1aba613b9415deevboxsync# General Public License (GPL) as published by the Free Software
c0829c6fca24bd3a9a687c93f9b64da2c54b767cvboxsync# Foundation, in version 2 as it comes in the "COPYING" file of the
c0829c6fca24bd3a9a687c93f9b64da2c54b767cvboxsync# VirtualBox OSE distribution. VirtualBox OSE is distributed in the
c0829c6fca24bd3a9a687c93f9b64da2c54b767cvboxsync# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
c0829c6fca24bd3a9a687c93f9b64da2c54b767cvboxsync#
c0829c6fca24bd3a9a687c93f9b64da2c54b767cvboxsyncimport sys,os
c0829c6fca24bd3a9a687c93f9b64da2c54b767cvboxsyncimport traceback
c0829c6fca24bd3a9a687c93f9b64da2c54b767cvboxsync
c0829c6fca24bd3a9a687c93f9b64da2c54b767cvboxsync# To set Python bitness on OSX use 'export VERSIONER_PYTHON_PREFER_32_BIT=yes'
c0829c6fca24bd3a9a687c93f9b64da2c54b767cvboxsync
c0829c6fca24bd3a9a687c93f9b64da2c54b767cvboxsyncVboxBinDir = os.environ.get("VBOX_PROGRAM_PATH", None)
c0829c6fca24bd3a9a687c93f9b64da2c54b767cvboxsyncVboxSdkDir = os.environ.get("VBOX_SDK_PATH", None)
fe0336dab2332556574fab2b79ab4210a7989127vboxsync
fe0336dab2332556574fab2b79ab4210a7989127vboxsyncif VboxBinDir is None:
fe0336dab2332556574fab2b79ab4210a7989127vboxsync # Will be set by the installer
fe0336dab2332556574fab2b79ab4210a7989127vboxsync VboxBinDir = "%VBOX_INSTALL_PATH%"
fe0336dab2332556574fab2b79ab4210a7989127vboxsync
fe0336dab2332556574fab2b79ab4210a7989127vboxsyncif VboxSdkDir is None:
fe0336dab2332556574fab2b79ab4210a7989127vboxsync # Will be set by the installer
c0829c6fca24bd3a9a687c93f9b64da2c54b767cvboxsync VboxSdkDir = "%VBOX_SDK_PATH%"
c0829c6fca24bd3a9a687c93f9b64da2c54b767cvboxsync
c0829c6fca24bd3a9a687c93f9b64da2c54b767cvboxsyncos.environ["VBOX_PROGRAM_PATH"] = VboxBinDir
c0829c6fca24bd3a9a687c93f9b64da2c54b767cvboxsyncos.environ["VBOX_SDK_PATH"] = VboxSdkDir
c0829c6fca24bd3a9a687c93f9b64da2c54b767cvboxsyncsys.path.append(VboxBinDir)
c0829c6fca24bd3a9a687c93f9b64da2c54b767cvboxsync
c0829c6fca24bd3a9a687c93f9b64da2c54b767cvboxsyncfrom VirtualBox_constants import VirtualBoxReflectionInfo
fe0336dab2332556574fab2b79ab4210a7989127vboxsync
fe0336dab2332556574fab2b79ab4210a7989127vboxsyncclass PerfCollector:
fe0336dab2332556574fab2b79ab4210a7989127vboxsync """ This class provides a wrapper over IPerformanceCollector in order to
fe0336dab2332556574fab2b79ab4210a7989127vboxsync get more 'pythonic' interface.
fe0336dab2332556574fab2b79ab4210a7989127vboxsync
fe0336dab2332556574fab2b79ab4210a7989127vboxsync To begin collection of metrics use setup() method.
fe0336dab2332556574fab2b79ab4210a7989127vboxsync
fe0336dab2332556574fab2b79ab4210a7989127vboxsync To get collected data use query() method.
c0829c6fca24bd3a9a687c93f9b64da2c54b767cvboxsync
fe0336dab2332556574fab2b79ab4210a7989127vboxsync It is possible to disable metric collection without changing collection
fe0336dab2332556574fab2b79ab4210a7989127vboxsync parameters with disable() method. The enable() method resumes metric
fe0336dab2332556574fab2b79ab4210a7989127vboxsync collection.
fe0336dab2332556574fab2b79ab4210a7989127vboxsync """
fe0336dab2332556574fab2b79ab4210a7989127vboxsync
fe0336dab2332556574fab2b79ab4210a7989127vboxsync def __init__(self, mgr, vbox):
fe0336dab2332556574fab2b79ab4210a7989127vboxsync """ Initializes the instance.
c0829c6fca24bd3a9a687c93f9b64da2c54b767cvboxsync
c0829c6fca24bd3a9a687c93f9b64da2c54b767cvboxsync """
c0829c6fca24bd3a9a687c93f9b64da2c54b767cvboxsync self.mgr = mgr
c0829c6fca24bd3a9a687c93f9b64da2c54b767cvboxsync self.isMscom = (mgr.type == 'MSCOM')
6de922ee8158732706074aacb20c2a5dc6d4d7a3vboxsync self.collector = vbox.performanceCollector
6de922ee8158732706074aacb20c2a5dc6d4d7a3vboxsync
c0829c6fca24bd3a9a687c93f9b64da2c54b767cvboxsync def setup(self, names, objects, period, nsamples):
c0829c6fca24bd3a9a687c93f9b64da2c54b767cvboxsync """ Discards all previously collected values for the specified
c0829c6fca24bd3a9a687c93f9b64da2c54b767cvboxsync metrics, sets the period of collection and the number of retained
c0829c6fca24bd3a9a687c93f9b64da2c54b767cvboxsync samples, enables collection.
c0829c6fca24bd3a9a687c93f9b64da2c54b767cvboxsync """
c0829c6fca24bd3a9a687c93f9b64da2c54b767cvboxsync self.collector.setupMetrics(names, objects, period, nsamples)
c0829c6fca24bd3a9a687c93f9b64da2c54b767cvboxsync
62a515eec8de1b7804ec6997c0f2013fef5c5a6bvboxsync def enable(self, names, objects):
c0829c6fca24bd3a9a687c93f9b64da2c54b767cvboxsync """ Resumes metric collection for the specified metrics.
c0829c6fca24bd3a9a687c93f9b64da2c54b767cvboxsync """
c0829c6fca24bd3a9a687c93f9b64da2c54b767cvboxsync self.collector.enableMetrics(names, objects)
c0829c6fca24bd3a9a687c93f9b64da2c54b767cvboxsync
c0829c6fca24bd3a9a687c93f9b64da2c54b767cvboxsync def disable(self, names, objects):
c0829c6fca24bd3a9a687c93f9b64da2c54b767cvboxsync """ Suspends metric collection for the specified metrics.
c0829c6fca24bd3a9a687c93f9b64da2c54b767cvboxsync """
c0829c6fca24bd3a9a687c93f9b64da2c54b767cvboxsync self.collector.disableMetrics(names, objects)
c0829c6fca24bd3a9a687c93f9b64da2c54b767cvboxsync
c0829c6fca24bd3a9a687c93f9b64da2c54b767cvboxsync def query(self, names, objects):
c0829c6fca24bd3a9a687c93f9b64da2c54b767cvboxsync """ Retrieves collected metric values as well as some auxiliary
c0829c6fca24bd3a9a687c93f9b64da2c54b767cvboxsync information. Returns an array of dictionaries, one dictionary per
c0829c6fca24bd3a9a687c93f9b64da2c54b767cvboxsync metric. Each dictionary contains the following entries:
c0829c6fca24bd3a9a687c93f9b64da2c54b767cvboxsync 'name': metric name
c0829c6fca24bd3a9a687c93f9b64da2c54b767cvboxsync 'object': managed object this metric associated with
c0829c6fca24bd3a9a687c93f9b64da2c54b767cvboxsync 'unit': unit of measurement
a8d4049da2285bc369273acbf613b8b7990cb4bfvboxsync 'scale': divide 'values' by this number to get float numbers
c0829c6fca24bd3a9a687c93f9b64da2c54b767cvboxsync 'values': collected data
21f41483611463008ef71e88d2c2b5ece30bc290vboxsync 'values_as_string': pre-processed values ready for 'print' statement
c0829c6fca24bd3a9a687c93f9b64da2c54b767cvboxsync """
c0829c6fca24bd3a9a687c93f9b64da2c54b767cvboxsync # Get around the problem with input arrays returned in output
c0829c6fca24bd3a9a687c93f9b64da2c54b767cvboxsync # parameters (see #3953) for MSCOM.
a8d4049da2285bc369273acbf613b8b7990cb4bfvboxsync if self.isMscom:
c0829c6fca24bd3a9a687c93f9b64da2c54b767cvboxsync (values, names, objects, names_out, objects_out, units, scales, sequence_numbers,
c0829c6fca24bd3a9a687c93f9b64da2c54b767cvboxsync indices, lengths) = self.collector.queryMetricsData(names, objects)
c0829c6fca24bd3a9a687c93f9b64da2c54b767cvboxsync else:
(values, names_out, objects_out, units, scales, sequence_numbers,
indices, lengths) = self.collector.queryMetricsData(names, objects)
out = []
for i in xrange(0, len(names_out)):
scale = int(scales[i])
if scale != 1:
fmt = '%.2f%s'
else:
fmt = '%d %s'
out.append({
'name':str(names_out[i]),
'object':str(objects_out[i]),
'unit':str(units[i]),
'scale':scale,
'values':[int(values[j]) for j in xrange(int(indices[i]), int(indices[i])+int(lengths[i]))],
'values_as_string':'['+', '.join([fmt % (int(values[j])/scale, units[i]) for j in xrange(int(indices[i]), int(indices[i])+int(lengths[i]))])+']'
})
return out
def ComifyName(name):
return name[0].capitalize()+name[1:]
_COMForward = { 'getattr' : None,
'setattr' : None}
def CustomGetAttr(self, attr):
# fastpath
if self.__class__.__dict__.get(attr) != None:
return self.__class__.__dict__.get(attr)
# try case-insensitivity workaround for class attributes (COM methods)
for k in self.__class__.__dict__.keys():
if k.lower() == attr.lower():
self.__class__.__dict__[attr] = self.__class__.__dict__[k]
return getattr(self, k)
try:
return _COMForward['getattr'](self,ComifyName(attr))
except AttributeError:
return _COMForward['getattr'](self,attr)
def CustomSetAttr(self, attr, value):
try:
return _COMForward['setattr'](self, ComifyName(attr), value)
except AttributeError:
return _COMForward['setattr'](self, attr, value)
class PlatformMSCOM:
# Class to fake access to constants in style of foo.bar.boo
class ConstantFake:
def __init__(self, parent, name):
self.__dict__['_parent'] = parent
self.__dict__['_name'] = name
self.__dict__['_consts'] = {}
try:
self.__dict__['_depth']=parent.__dict__['_depth']+1
except:
self.__dict__['_depth']=0
if self.__dict__['_depth'] > 4:
raise AttributeError
def __getattr__(self, attr):
import win32com
from win32com.client import constants
if attr.startswith("__"):
raise AttributeError
consts = self.__dict__['_consts']
fake = consts.get(attr, None)
if fake != None:
return fake
try:
name = self.__dict__['_name']
parent = self.__dict__['_parent']
while parent != None:
if parent._name is not None:
name = parent._name+'_'+name
parent = parent._parent
if name is not None:
name += "_" + attr
else:
name = attr
return win32com.client.constants.__getattr__(name)
except AttributeError,e:
fake = PlatformMSCOM.ConstantFake(self, attr)
consts[attr] = fake
return fake
class InterfacesWrapper:
def __init__(self):
self.__dict__['_rootFake'] = PlatformMSCOM.ConstantFake(None, None)
def __getattr__(self, a):
import win32com
from win32com.client import constants
if a.startswith("__"):
raise AttributeError
try:
return win32com.client.constants.__getattr__(a)
except AttributeError,e:
return self.__dict__['_rootFake'].__getattr__(a)
VBOX_TLB_GUID = '{46137EEC-703B-4FE5-AFD4-7C9BBBBA0259}'
VBOX_TLB_LCID = 0
VBOX_TLB_MAJOR = 1
VBOX_TLB_MINOR = 0
def __init__(self, params):
from win32com import universal
from win32com.client import gencache, DispatchBaseClass
from win32com.client import constants, getevents
import win32com
import pythoncom
import win32api
from win32con import DUPLICATE_SAME_ACCESS
from win32api import GetCurrentThread,GetCurrentThreadId,DuplicateHandle,GetCurrentProcess
import threading
pid = GetCurrentProcess()
self.tid = GetCurrentThreadId()
handle = DuplicateHandle(pid, GetCurrentThread(), pid, 0, 0, DUPLICATE_SAME_ACCESS)
self.handles = []
self.handles.append(handle)
_COMForward['getattr'] = DispatchBaseClass.__dict__['__getattr__']
DispatchBaseClass.__dict__['__getattr__'] = CustomGetAttr
_COMForward['setattr'] = DispatchBaseClass.__dict__['__setattr__']
DispatchBaseClass.__dict__['__setattr__'] = CustomSetAttr
win32com.client.gencache.EnsureDispatch('VirtualBox.Session')
win32com.client.gencache.EnsureDispatch('VirtualBox.VirtualBox')
self.oIntCv = threading.Condition()
self.fInterrupted = False;
def getSessionObject(self, vbox):
import win32com
from win32com.client import Dispatch
return win32com.client.Dispatch("VirtualBox.Session")
def getVirtualBox(self):
import win32com
from win32com.client import Dispatch
return win32com.client.Dispatch("VirtualBox.VirtualBox")
def getType(self):
return 'MSCOM'
def getRemote(self):
return False
def getArray(self, obj, field):
return obj.__getattr__(field)
def initPerThread(self):
import pythoncom
pythoncom.CoInitializeEx(0)
def deinitPerThread(self):
import pythoncom
pythoncom.CoUninitialize()
def createListener(self, impl, arg):
d = {}
d['BaseClass'] = impl
d['arg'] = arg
d['tlb_guid'] = PlatformMSCOM.VBOX_TLB_GUID
str = ""
str += "import win32com.server.util\n"
str += "import pythoncom\n"
str += "class ListenerImpl(BaseClass):\n"
str += " _com_interfaces_ = ['IEventListener']\n"
str += " _typelib_guid_ = tlb_guid\n"
str += " _typelib_version_ = 1, 0\n"
str += " _reg_clsctx_ = pythoncom.CLSCTX_INPROC_SERVER\n"
# Maybe we'd better implement Dynamic invoke policy, to be more flexible here
str += " _reg_policy_spec_ = 'win32com.server.policy.EventHandlerPolicy'\n"
# capitalized version of listener method
str += " HandleEvent=BaseClass.handleEvent\n"
str += " def __init__(self): BaseClass.__init__(self, arg)\n"
str += "result = win32com.server.util.wrap(ListenerImpl())\n"
exec (str,d,d)
return d['result']
def waitForEvents(self, timeout):
from win32api import GetCurrentThreadId
from win32event import INFINITE
from win32event import MsgWaitForMultipleObjects, \
QS_ALLINPUT, WAIT_TIMEOUT, WAIT_OBJECT_0
from pythoncom import PumpWaitingMessages
import types
if not isinstance(timeout, types.IntType):
raise TypeError("The timeout argument is not an integer")
if (self.tid != GetCurrentThreadId()):
raise Exception("wait for events from the same thread you inited!")
if timeout < 0: cMsTimeout = INFINITE
else: cMsTimeout = timeout
rc = MsgWaitForMultipleObjects(self.handles, 0, cMsTimeout, QS_ALLINPUT)
if rc >= WAIT_OBJECT_0 and rc < WAIT_OBJECT_0+len(self.handles):
# is it possible?
rc = 2;
elif rc==WAIT_OBJECT_0 + len(self.handles):
# Waiting messages
PumpWaitingMessages()
rc = 0;
else:
# Timeout
rc = 1;
# check for interruption
self.oIntCv.acquire()
if self.fInterrupted:
self.fInterrupted = False
rc = 1;
self.oIntCv.release()
return rc;
def interruptWaitEvents(self):
"""
Basically a python implementation of EventQueue::postEvent().
The magic value must be in sync with the C++ implementation or this
won't work.
Note that because of this method we cannot easily make use of a
non-visible Window to handle the message like we would like to do.
"""
from win32api import PostThreadMessage
from win32con import WM_USER
self.oIntCv.acquire()
self.fInterrupted = True
self.oIntCv.release()
try:
PostThreadMessage(self.tid, WM_USER, None, 0xf241b819)
except:
return False;
return True;
def deinit(self):
import pythoncom
from win32file import CloseHandle
for h in self.handles:
if h is not None:
CloseHandle(h)
self.handles = None
pythoncom.CoUninitialize()
pass
def queryInterface(self, obj, klazzName):
from win32com.client import CastTo
return CastTo(obj, klazzName)
class PlatformXPCOM:
def __init__(self, params):
sys.path.append(VboxSdkDir+'/bindings/xpcom/python/')
import xpcom.vboxxpcom
import xpcom
import xpcom.components
def getSessionObject(self, vbox):
import xpcom.components
return xpcom.components.classes["@virtualbox.org/Session;1"].createInstance()
def getVirtualBox(self):
import xpcom.components
return xpcom.components.classes["@virtualbox.org/VirtualBox;1"].createInstance()
def getType(self):
return 'XPCOM'
def getRemote(self):
return False
def getArray(self, obj, field):
return obj.__getattr__('get'+ComifyName(field))()
def initPerThread(self):
import xpcom
xpcom._xpcom.AttachThread()
def deinitPerThread(self):
import xpcom
xpcom._xpcom.DetachThread()
def createListener(self, impl, arg):
d = {}
d['BaseClass'] = impl
d['arg'] = arg
str = ""
str += "import xpcom.components\n"
str += "class ListenerImpl(BaseClass):\n"
str += " _com_interfaces_ = xpcom.components.interfaces.IEventListener\n"
str += " def __init__(self): BaseClass.__init__(self, arg)\n"
str += "result = ListenerImpl()\n"
exec (str,d,d)
return d['result']
def waitForEvents(self, timeout):
import xpcom
return xpcom._xpcom.WaitForEvents(timeout)
def interruptWaitEvents(self):
import xpcom
return xpcom._xpcom.InterruptWait()
def deinit(self):
import xpcom
xpcom._xpcom.DeinitCOM()
def queryInterface(self, obj, klazzName):
import xpcom.components
return obj.queryInterface(getattr(xpcom.components.interfaces, klazzName))
class PlatformWEBSERVICE:
def __init__(self, params):
sys.path.append(os.path.join(VboxSdkDir,'bindings', 'webservice', 'python', 'lib'))
#import VirtualBox_services
import VirtualBox_wrappers
from VirtualBox_wrappers import IWebsessionManager2
if params is not None:
self.user = params.get("user", "")
self.password = params.get("password", "")
self.url = params.get("url", "")
else:
self.user = ""
self.password = ""
self.url = None
self.vbox = None
def getSessionObject(self, vbox):
return self.wsmgr.getSessionObject(vbox)
def getVirtualBox(self):
return self.connect(self.url, self.user, self.password)
def connect(self, url, user, passwd):
if self.vbox is not None:
self.disconnect()
from VirtualBox_wrappers import IWebsessionManager2
if url is None:
url = ""
self.url = url
if user is None:
user = ""
self.user = user
if passwd is None:
passwd = ""
self.password = passwd
self.wsmgr = IWebsessionManager2(self.url)
self.vbox = self.wsmgr.logon(self.user, self.password)
if not self.vbox.handle:
raise Exception("cannot connect to '"+self.url+"' as '"+self.user+"'")
return self.vbox
def disconnect(self):
if self.vbox is not None and self.wsmgr is not None:
self.wsmgr.logoff(self.vbox)
self.vbox = None
self.wsmgr = None
def getType(self):
return 'WEBSERVICE'
def getRemote(self):
return True
def getArray(self, obj, field):
return obj.__getattr__(field)
def initPerThread(self):
pass
def deinitPerThread(self):
pass
def createListener(self, impl, arg):
raise Exception("no active listeners for webservices")
def waitForEvents(self, timeout):
# Webservices cannot do that yet
return 2;
def interruptWaitEvents(self, timeout):
# Webservices cannot do that yet
return False;
def deinit(self):
try:
disconnect()
except:
pass
def queryInterface(self, obj, klazzName):
d = {}
d['obj'] = obj
str = ""
str += "from VirtualBox_wrappers import "+klazzName+"\n"
str += "result = "+klazzName+"(obj.mgr,obj.handle)\n"
# wrong, need to test if class indeed implements this interface
exec (str,d,d)
return d['result']
class SessionManager:
def __init__(self, mgr):
self.mgr = mgr
def getSessionObject(self, vbox):
return self.mgr.platform.getSessionObject(vbox)
class VirtualBoxManager:
def __init__(self, style, platparams):
if style is None:
if sys.platform == 'win32':
style = "MSCOM"
else:
style = "XPCOM"
exec "self.platform = Platform"+style+"(platparams)"
# for webservices, enums are symbolic
self.constants = VirtualBoxReflectionInfo(style == "WEBSERVICE")
self.type = self.platform.getType()
self.remote = self.platform.getRemote()
self.style = style
self.mgr = SessionManager(self)
try:
self.vbox = self.platform.getVirtualBox()
except NameError,ne:
print "Installation problem: check that appropriate libs in place"
traceback.print_exc()
raise ne
except Exception,e:
print "init exception: ",e
traceback.print_exc()
if self.remote:
self.vbox = None
else:
raise e
def getArray(self, obj, field):
return self.platform.getArray(obj, field)
def getVirtualBox(self):
return self.platform.getVirtualBox()
def __del__(self):
self.deinit()
def deinit(self):
if hasattr(self, "vbox"):
del self.vbox
self.vbox = None
if hasattr(self, "platform"):
self.platform.deinit()
self.platform = None
def initPerThread(self):
self.platform.initPerThread()
def openMachineSession(self, mach, permitSharing = True):
session = self.mgr.getSessionObject(self.vbox)
if permitSharing:
type = self.constants.LockType_Shared
else:
type = self.constants.LockType_Write
mach.lockMachine(session, type)
return session
def closeMachineSession(self, session):
if session is not None:
session.unlockMachine()
def deinitPerThread(self):
self.platform.deinitPerThread()
def createListener(self, impl, arg = None):
return self.platform.createListener(impl, arg)
def waitForEvents(self, timeout):
"""
Wait for events to arrive and process them.
The timeout is in milliseconds. A negative value means waiting for
ever, while 0 does not wait at all.
Returns 0 if events was processed.
Returns 1 if timed out or interrupted in some way.
Returns 2 on error (like not supported for web services).
Raises an exception if the calling thread is not the main thread (the one
that initialized VirtualBoxManager) or if the time isn't an integer.
"""
return self.platform.waitForEvents(timeout)
def interruptWaitEvents(self):
"""
Interrupt a waitForEvents call.
This is normally called from a worker thread.
Returns True on success, False on failure.
"""
return self.platform.interruptWaitEvents()
def getPerfCollector(self, vbox):
return PerfCollector(self, vbox)
def getBinDir(self):
global VboxBinDir
return VboxBinDir
def getSdkDir(self):
global VboxSdkDir
return VboxSdkDir
def queryInterface(self, obj, klazzName):
return self.platform.queryInterface(obj, klazzName)