VirtualBox

source: vbox/trunk/src/VBox/Main/glue/vboxapi.py@ 30750

Last change on this file since 30750 was 30533, checked in by vboxsync, 15 years ago

Python: active callbacks with Windows Python

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 20.2 KB
Line 
1#
2# Copyright (C) 2009 Oracle Corporation
3#
4# This file is part of VirtualBox Open Source Edition (OSE), as
5# available from http://www.215389.xyz. This file is free software;
6# you can redistribute it and/or modify it under the terms of the GNU
7# General Public License (GPL) as published by the Free Software
8# Foundation, in version 2 as it comes in the "COPYING" file of the
9# VirtualBox OSE distribution. VirtualBox OSE is distributed in the
10# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
11#
12import sys,os
13import traceback
14
15# To set Python bitness on OSX use 'export VERSIONER_PYTHON_PREFER_32_BIT=yes'
16
17VboxBinDir = os.environ.get("VBOX_PROGRAM_PATH", None)
18VboxSdkDir = os.environ.get("VBOX_SDK_PATH", None)
19
20if VboxBinDir is None:
21 # Will be set by the installer
22 VboxBinDir = "%VBOX_INSTALL_PATH%"
23
24if VboxSdkDir is None:
25 # Will be set by the installer
26 VboxSdkDir = "%VBOX_SDK_PATH%"
27
28os.environ["VBOX_PROGRAM_PATH"] = VboxBinDir
29os.environ["VBOX_SDK_PATH"] = VboxSdkDir
30sys.path.append(VboxBinDir)
31
32from VirtualBox_constants import VirtualBoxReflectionInfo
33
34class PerfCollector:
35 """ This class provides a wrapper over IPerformanceCollector in order to
36 get more 'pythonic' interface.
37
38 To begin collection of metrics use setup() method.
39
40 To get collected data use query() method.
41
42 It is possible to disable metric collection without changing collection
43 parameters with disable() method. The enable() method resumes metric
44 collection.
45 """
46
47 def __init__(self, mgr, vbox):
48 """ Initializes the instance.
49
50 """
51 self.mgr = mgr
52 self.isMscom = (mgr.type == 'MSCOM')
53 self.collector = vbox.performanceCollector
54
55 def setup(self, names, objects, period, nsamples):
56 """ Discards all previously collected values for the specified
57 metrics, sets the period of collection and the number of retained
58 samples, enables collection.
59 """
60 self.collector.setupMetrics(names, objects, period, nsamples)
61
62 def enable(self, names, objects):
63 """ Resumes metric collection for the specified metrics.
64 """
65 self.collector.enableMetrics(names, objects)
66
67 def disable(self, names, objects):
68 """ Suspends metric collection for the specified metrics.
69 """
70 self.collector.disableMetrics(names, objects)
71
72 def query(self, names, objects):
73 """ Retrieves collected metric values as well as some auxiliary
74 information. Returns an array of dictionaries, one dictionary per
75 metric. Each dictionary contains the following entries:
76 'name': metric name
77 'object': managed object this metric associated with
78 'unit': unit of measurement
79 'scale': divide 'values' by this number to get float numbers
80 'values': collected data
81 'values_as_string': pre-processed values ready for 'print' statement
82 """
83 # Get around the problem with input arrays returned in output
84 # parameters (see #3953) for MSCOM.
85 if self.isMscom:
86 (values, names, objects, names_out, objects_out, units, scales, sequence_numbers,
87 indices, lengths) = self.collector.queryMetricsData(names, objects)
88 else:
89 (values, names_out, objects_out, units, scales, sequence_numbers,
90 indices, lengths) = self.collector.queryMetricsData(names, objects)
91 out = []
92 for i in xrange(0, len(names_out)):
93 scale = int(scales[i])
94 if scale != 1:
95 fmt = '%.2f%s'
96 else:
97 fmt = '%d %s'
98 out.append({
99 'name':str(names_out[i]),
100 'object':str(objects_out[i]),
101 'unit':str(units[i]),
102 'scale':scale,
103 'values':[int(values[j]) for j in xrange(int(indices[i]), int(indices[i])+int(lengths[i]))],
104 'values_as_string':'['+', '.join([fmt % (int(values[j])/scale, units[i]) for j in xrange(int(indices[i]), int(indices[i])+int(lengths[i]))])+']'
105 })
106 return out
107
108def ComifyName(name):
109 return name[0].capitalize()+name[1:]
110
111_COMForward = { 'getattr' : None,
112 'setattr' : None}
113
114def CustomGetAttr(self, attr):
115 # fastpath
116 if self.__class__.__dict__.get(attr) != None:
117 return self.__class__.__dict__.get(attr)
118
119 # try case-insensitivity workaround for class attributes (COM methods)
120 for k in self.__class__.__dict__.keys():
121 if k.lower() == attr.lower():
122 self.__class__.__dict__[attr] = self.__class__.__dict__[k]
123 return getattr(self, k)
124 try:
125 return _COMForward['getattr'](self,ComifyName(attr))
126 except AttributeError:
127 return _COMForward['getattr'](self,attr)
128
129def CustomSetAttr(self, attr, value):
130 try:
131 return _COMForward['setattr'](self, ComifyName(attr), value)
132 except AttributeError:
133 return _COMForward['setattr'](self, attr, value)
134
135class PlatformMSCOM:
136 # Class to fake access to constants in style of foo.bar.boo
137 class ConstantFake:
138 def __init__(self, parent, name):
139 self.__dict__['_parent'] = parent
140 self.__dict__['_name'] = name
141 self.__dict__['_consts'] = {}
142 try:
143 self.__dict__['_depth']=parent.__dict__['_depth']+1
144 except:
145 self.__dict__['_depth']=0
146 if self.__dict__['_depth'] > 4:
147 raise AttributeError
148
149 def __getattr__(self, attr):
150 import win32com
151 from win32com.client import constants
152
153 if attr.startswith("__"):
154 raise AttributeError
155
156 consts = self.__dict__['_consts']
157
158 fake = consts.get(attr, None)
159 if fake != None:
160 return fake
161 try:
162 name = self.__dict__['_name']
163 parent = self.__dict__['_parent']
164 while parent != None:
165 if parent._name is not None:
166 name = parent._name+'_'+name
167 parent = parent._parent
168
169 if name is not None:
170 name += "_" + attr
171 else:
172 name = attr
173 return win32com.client.constants.__getattr__(name)
174 except AttributeError,e:
175 fake = PlatformMSCOM.ConstantFake(self, attr)
176 consts[attr] = fake
177 return fake
178
179
180 class InterfacesWrapper:
181 def __init__(self):
182 self.__dict__['_rootFake'] = PlatformMSCOM.ConstantFake(None, None)
183
184 def __getattr__(self, a):
185 import win32com
186 from win32com.client import constants
187 if a.startswith("__"):
188 raise AttributeError
189 try:
190 return win32com.client.constants.__getattr__(a)
191 except AttributeError,e:
192 return self.__dict__['_rootFake'].__getattr__(a)
193
194 VBOX_TLB_GUID = '{46137EEC-703B-4FE5-AFD4-7C9BBBBA0259}'
195 VBOX_TLB_LCID = 0
196 VBOX_TLB_MAJOR = 1
197 VBOX_TLB_MINOR = 0
198
199 def __init__(self, params):
200 from win32com import universal
201 from win32com.client import gencache, DispatchBaseClass
202 from win32com.client import constants, getevents
203 import win32com
204 import pythoncom
205 import win32api
206 from win32con import DUPLICATE_SAME_ACCESS
207 from win32api import GetCurrentThread,GetCurrentThreadId,DuplicateHandle,GetCurrentProcess
208 pid = GetCurrentProcess()
209 self.tid = GetCurrentThreadId()
210 handle = DuplicateHandle(pid, GetCurrentThread(), pid, 0, 0, DUPLICATE_SAME_ACCESS)
211 self.handles = []
212 self.handles.append(handle)
213 _COMForward['getattr'] = DispatchBaseClass.__dict__['__getattr__']
214 DispatchBaseClass.__dict__['__getattr__'] = CustomGetAttr
215 _COMForward['setattr'] = DispatchBaseClass.__dict__['__setattr__']
216 DispatchBaseClass.__dict__['__setattr__'] = CustomSetAttr
217 win32com.client.gencache.EnsureDispatch('VirtualBox.Session')
218 win32com.client.gencache.EnsureDispatch('VirtualBox.VirtualBox')
219 win32com.client.gencache.EnsureDispatch('VirtualBox.CallbackWrapper')
220
221 def getSessionObject(self, vbox):
222 import win32com
223 from win32com.client import Dispatch
224 return win32com.client.Dispatch("VirtualBox.Session")
225
226 def getVirtualBox(self):
227 import win32com
228 from win32com.client import Dispatch
229 return win32com.client.Dispatch("VirtualBox.VirtualBox")
230
231 def getType(self):
232 return 'MSCOM'
233
234 def getRemote(self):
235 return False
236
237 def getArray(self, obj, field):
238 return obj.__getattr__(field)
239
240 def initPerThread(self):
241 import pythoncom
242 pythoncom.CoInitializeEx(0)
243
244 def deinitPerThread(self):
245 import pythoncom
246 pythoncom.CoUninitialize()
247
248 def createCallback(self, iface, impl, arg):
249 d = {}
250 d['BaseClass'] = impl
251 d['arg'] = arg
252 d['tlb_guid'] = PlatformMSCOM.VBOX_TLB_GUID
253 str = ""
254 str += "import win32com.server.util\n"
255 str += "import pythoncom\n"
256
257 str += "class "+iface+"Impl(BaseClass):\n"
258 str += " _com_interfaces_ = ['"+iface+"']\n"
259 str += " _typelib_guid_ = tlb_guid\n"
260 str += " _typelib_version_ = 1, 0\n"
261 str += " _reg_clsctx_ = pythoncom.CLSCTX_INPROC_SERVER\n"
262 # Maybe we'd better implement Dynamic invoke policy, to be more flexible here
263 str += " _reg_policy_spec_ = 'win32com.server.policy.EventHandlerPolicy'\n"
264
265 # generate capitalized version of callback methods -
266 # that's how Python COM looks them up
267 for m in dir(impl):
268 if m.startswith("on"):
269 str += " "+ComifyName(m)+"=BaseClass."+m+"\n"
270
271 str += " def __init__(self): BaseClass.__init__(self, arg)\n"
272 str += "result = win32com.client.Dispatch('VirtualBox.CallbackWrapper')\n"
273 str += "result.SetLocalObject(win32com.server.util.wrap("+iface+"Impl()))\n"
274 exec (str,d,d)
275 return d['result']
276
277 def createListener(self, impl, arg):
278 d = {}
279 d['BaseClass'] = impl
280 d['arg'] = arg
281 d['tlb_guid'] = PlatformMSCOM.VBOX_TLB_GUID
282 str = ""
283 str += "import win32com.server.util\n"
284 str += "import pythoncom\n"
285
286 str += "class ListenerImpl(BaseClass):\n"
287 str += " _com_interfaces_ = ['IEventListener']\n"
288 str += " _typelib_guid_ = tlb_guid\n"
289 str += " _typelib_version_ = 1, 0\n"
290 str += " _reg_clsctx_ = pythoncom.CLSCTX_INPROC_SERVER\n"
291 # Maybe we'd better implement Dynamic invoke policy, to be more flexible here
292 str += " _reg_policy_spec_ = 'win32com.server.policy.EventHandlerPolicy'\n"
293
294 # capitalized version of callback method
295 str += " HandleEvent=BaseClass.handleEvent\n"
296 str += " def __init__(self): BaseClass.__init__(self, arg)\n"
297 str += "result = win32com.server.util.wrap(ListenerImpl())\n"
298 exec (str,d,d)
299 return d['result']
300
301 def waitForEvents(self, timeout):
302 from win32api import GetCurrentThreadId
303 from win32event import MsgWaitForMultipleObjects, \
304 QS_ALLINPUT, WAIT_TIMEOUT, WAIT_OBJECT_0
305 from pythoncom import PumpWaitingMessages
306
307 if (self.tid != GetCurrentThreadId()):
308 raise Exception("wait for events from the same thread you inited!")
309
310 rc = MsgWaitForMultipleObjects(self.handles, 0, timeout, QS_ALLINPUT)
311 if rc >= WAIT_OBJECT_0 and rc < WAIT_OBJECT_0+len(self.handles):
312 # is it possible?
313 pass
314 elif rc==WAIT_OBJECT_0 + len(self.handles):
315 # Waiting messages
316 PumpWaitingMessages()
317 else:
318 # Timeout
319 pass
320
321 def interruptWaitEvents(self):
322 from win32api import PostThreadMessage
323 from win32con import WM_USER
324 PostThreadMessage(self.tid, WM_USER, None, None)
325
326 def deinit(self):
327 import pythoncom
328 from win32file import CloseHandle
329
330 for h in self.handles:
331 if h is not None:
332 CloseHandle(h)
333 self.handles = None
334 pythoncom.CoUninitialize()
335 pass
336
337 def queryInterface(self, obj, klazzName):
338 from win32com.client import CastTo
339 return CastTo(obj, klazzName)
340
341class PlatformXPCOM:
342 def __init__(self, params):
343 sys.path.append(VboxSdkDir+'/bindings/xpcom/python/')
344 import xpcom.vboxxpcom
345 import xpcom
346 import xpcom.components
347
348 def getSessionObject(self, vbox):
349 import xpcom.components
350 return xpcom.components.classes["@virtualbox.org/Session;1"].createInstance()
351
352 def getVirtualBox(self):
353 import xpcom.components
354 return xpcom.components.classes["@virtualbox.org/VirtualBox;1"].createInstance()
355
356 def getType(self):
357 return 'XPCOM'
358
359 def getRemote(self):
360 return False
361
362 def getArray(self, obj, field):
363 return obj.__getattr__('get'+ComifyName(field))()
364
365 def initPerThread(self):
366 import xpcom
367 xpcom._xpcom.AttachThread()
368
369 def deinitPerThread(self):
370 import xpcom
371 xpcom._xpcom.DetachThread()
372
373 def createCallback(self, iface, impl, arg):
374 d = {}
375 d['BaseClass'] = impl
376 d['arg'] = arg
377 str = ""
378 str += "import xpcom.components\n"
379 str += "class "+iface+"Impl(BaseClass):\n"
380 str += " _com_interfaces_ = xpcom.components.interfaces."+iface+"\n"
381 str += " def __init__(self): BaseClass.__init__(self, arg)\n"
382 str += "result = xpcom.components.classes['@virtualbox.org/CallbackWrapper;1'].createInstance()\n"
383 str += "result.setLocalObject("+iface+"Impl())\n"
384 exec (str,d,d)
385 return d['result']
386
387 def createListener(self, impl, arg):
388 d = {}
389 d['BaseClass'] = impl
390 d['arg'] = arg
391 str = ""
392 str += "import xpcom.components\n"
393 str += "class ListenerImpl(BaseClass):\n"
394 str += " _com_interfaces_ = xpcom.components.interfaces.IEventListener\n"
395 str += " def __init__(self): BaseClass.__init__(self, arg)\n"
396 str += "result = ListenerImpl()\n"
397 exec (str,d,d)
398 return d['result']
399
400 def waitForEvents(self, timeout):
401 import xpcom
402 xpcom._xpcom.WaitForEvents(timeout)
403
404 def interruptWaitEvents(self):
405 import xpcom
406 xpcom._xpcom.InterruptWait()
407
408 def deinit(self):
409 import xpcom
410 xpcom._xpcom.DeinitCOM()
411
412 def queryInterface(self, obj, klazzName):
413 import xpcom.components
414 return obj.queryInterface(getattr(xpcom.components.interfaces, klazzName))
415
416class PlatformWEBSERVICE:
417 def __init__(self, params):
418 sys.path.append(os.path.join(VboxSdkDir,'bindings', 'webservice', 'python', 'lib'))
419 # not really needed, but just fail early if misconfigured
420 import VirtualBox_services
421 import VirtualBox_wrappers
422 from VirtualBox_wrappers import IWebsessionManager2
423
424 if params is not None:
425 self.user = params.get("user", "")
426 self.password = params.get("password", "")
427 self.url = params.get("url", "")
428 else:
429 self.user = ""
430 self.password = ""
431 self.url = None
432 self.vbox = None
433
434 def getSessionObject(self, vbox):
435 return self.wsmgr.getSessionObject(vbox)
436
437 def getVirtualBox(self):
438 return self.connect(self.url, self.user, self.password)
439
440 def connect(self, url, user, passwd):
441 if self.vbox is not None:
442 self.disconnect()
443 from VirtualBox_wrappers import IWebsessionManager2
444 if url is None:
445 url = ""
446 self.url = url
447 if user is None:
448 user = ""
449 self.user = user
450 if passwd is None:
451 passwd = ""
452 self.password = passwd
453 self.wsmgr = IWebsessionManager2(self.url)
454 self.vbox = self.wsmgr.logon(self.user, self.password)
455 if not self.vbox.handle:
456 raise Exception("cannot connect to '"+self.url+"' as '"+self.user+"'")
457 return self.vbox
458
459 def disconnect(self):
460 if self.vbox is not None and self.wsmgr is not None:
461 self.wsmgr.logoff(self.vbox)
462 self.vbox = None
463 self.wsmgr = None
464
465 def getType(self):
466 return 'WEBSERVICE'
467
468 def getRemote(self):
469 return True
470
471 def getArray(self, obj, field):
472 return obj.__getattr__(field)
473
474 def initPerThread(self):
475 pass
476
477 def deinitPerThread(self):
478 pass
479
480 def createCallback(self, iface, impl, arg):
481 raise Exception("no callbacks for webservices")
482
483 def createListener(self, impl, arg):
484 raise Exception("no active listeners for webservices")
485
486 def waitForEvents(self, timeout):
487 # Webservices cannot do that yet
488 pass
489
490 def interruptWaitEvents(self, timeout):
491 # Webservices cannot do that yet
492 pass
493
494 def deinit(self):
495 try:
496 disconnect()
497 except:
498 pass
499
500 def queryInterface(self, obj, klazzName):
501 d = {}
502 d['obj'] = obj
503 str = ""
504 str += "from VirtualBox_wrappers import "+klazzName+"\n"
505 str += "result = "+klazzName+"(obj.mgr,obj.handle)\n"
506 # wrong, need to test if class indeed implements this interface
507 exec (str,d,d)
508 return d['result']
509
510class SessionManager:
511 def __init__(self, mgr):
512 self.mgr = mgr
513
514 def getSessionObject(self, vbox):
515 return self.mgr.platform.getSessionObject(vbox)
516
517class VirtualBoxManager:
518 def __init__(self, style, platparams):
519 if style is None:
520 if sys.platform == 'win32':
521 style = "MSCOM"
522 else:
523 style = "XPCOM"
524
525
526 exec "self.platform = Platform"+style+"(platparams)"
527 # for webservices, enums are symbolic
528 self.constants = VirtualBoxReflectionInfo(style == "WEBSERVICE")
529 self.type = self.platform.getType()
530 self.remote = self.platform.getRemote()
531 self.style = style
532 self.mgr = SessionManager(self)
533
534 try:
535 self.vbox = self.platform.getVirtualBox()
536 except NameError,ne:
537 print "Installation problem: check that appropriate libs in place"
538 traceback.print_exc()
539 raise ne
540 except Exception,e:
541 print "init exception: ",e
542 traceback.print_exc()
543 if self.remote:
544 self.vbox = None
545 else:
546 raise e
547
548 def getArray(self, obj, field):
549 return self.platform.getArray(obj, field)
550
551 def getVirtualBox(self):
552 return self.platform.getVirtualBox()
553
554 def __del__(self):
555 self.deinit()
556
557 def deinit(self):
558 if hasattr(self, "vbox"):
559 del self.vbox
560 self.vbox = None
561 if hasattr(self, "platform"):
562 self.platform.deinit()
563 self.platform = None
564
565 def initPerThread(self):
566 self.platform.initPerThread()
567
568 def openMachineSession(self, machineId):
569 session = self.mgr.getSessionObject(self.vbox)
570 try:
571 self.vbox.openExistingSession(session, machineId)
572 except:
573 self.vbox.openSession(session, machineId)
574 return session
575
576 def closeMachineSession(self, session):
577 if session is not None:
578 session.close()
579
580 def deinitPerThread(self):
581 self.platform.deinitPerThread()
582
583 def createCallback(self, iface, impl, arg):
584 return self.platform.createCallback(iface, impl, arg)
585
586 def createListener(self, impl, arg = None):
587 return self.platform.createListener(impl, arg)
588
589 def waitForEvents(self, timeout):
590 return self.platform.waitForEvents(timeout)
591
592 def interruptWaitEvents(self):
593 return self.platform.interruptWaitEvents()
594
595 def getPerfCollector(self, vbox):
596 return PerfCollector(self, vbox)
597
598 def getBinDir(self):
599 global VboxBinDir
600 return VboxBinDir
601
602 def getSdkDir(self):
603 global VboxSdkDir
604 return VboxSdkDir
605
606 def queryInterface(self, obj, klazzName):
607 return self.platform.queryInterface(obj, klazzName)
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette