VirtualBox

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

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

waitForEvents: Don't return None, another test.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 19.5 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.virtualbox.org. 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
220 def getSessionObject(self, vbox):
221 import win32com
222 from win32com.client import Dispatch
223 return win32com.client.Dispatch("VirtualBox.Session")
224
225 def getVirtualBox(self):
226 import win32com
227 from win32com.client import Dispatch
228 return win32com.client.Dispatch("VirtualBox.VirtualBox")
229
230 def getType(self):
231 return 'MSCOM'
232
233 def getRemote(self):
234 return False
235
236 def getArray(self, obj, field):
237 return obj.__getattr__(field)
238
239 def initPerThread(self):
240 import pythoncom
241 pythoncom.CoInitializeEx(0)
242
243 def deinitPerThread(self):
244 import pythoncom
245 pythoncom.CoUninitialize()
246
247 def createListener(self, impl, arg):
248 d = {}
249 d['BaseClass'] = impl
250 d['arg'] = arg
251 d['tlb_guid'] = PlatformMSCOM.VBOX_TLB_GUID
252 str = ""
253 str += "import win32com.server.util\n"
254 str += "import pythoncom\n"
255
256 str += "class ListenerImpl(BaseClass):\n"
257 str += " _com_interfaces_ = ['IEventListener']\n"
258 str += " _typelib_guid_ = tlb_guid\n"
259 str += " _typelib_version_ = 1, 0\n"
260 str += " _reg_clsctx_ = pythoncom.CLSCTX_INPROC_SERVER\n"
261 # Maybe we'd better implement Dynamic invoke policy, to be more flexible here
262 str += " _reg_policy_spec_ = 'win32com.server.policy.EventHandlerPolicy'\n"
263
264 # capitalized version of listener method
265 str += " HandleEvent=BaseClass.handleEvent\n"
266 str += " def __init__(self): BaseClass.__init__(self, arg)\n"
267 str += "result = win32com.server.util.wrap(ListenerImpl())\n"
268 exec (str,d,d)
269 return d['result']
270
271 def waitForEvents(self, timeout):
272 from win32api import GetCurrentThreadId
273 from win32con import INFINITE
274 from win32event import MsgWaitForMultipleObjects, \
275 QS_ALLINPUT, WAIT_TIMEOUT, WAIT_OBJECT_0
276 from pythoncom import PumpWaitingMessages
277 import types
278
279 if not isinstance(timeout, types.IntType):
280 raise TypeError("The timeout argument is not an integer")
281 if (self.tid != GetCurrentThreadId()):
282 raise Exception("wait for events from the same thread you inited!")
283
284 if timeout < 0: cMsTimeout = INFINITE
285 else: cMsTimeout = timeout
286 rc = MsgWaitForMultipleObjects(self.handles, 0, cMsTimeout, QS_ALLINPUT)
287 if rc >= WAIT_OBJECT_0 and rc < WAIT_OBJECT_0+len(self.handles):
288 # is it possible?
289 rc = 2;
290 elif rc==WAIT_OBJECT_0 + len(self.handles):
291 # Waiting messages
292 PumpWaitingMessages()
293 rc = 0;
294 else:
295 # Timeout
296 rc = 1;
297 return rc;
298
299 def interruptWaitEvents(self):
300 """
301 Basically a python implementation of EventQueue::postEvent().
302
303 The magic value must be in sync with the C++ implementation or this
304 won't work.
305
306 Note that because of this method we cannot easily make use of a
307 non-visible Window to handle the message like we would like to do.
308 """
309 from win32api import PostThreadMessage
310 from win32con import WM_USER
311 return PostThreadMessage(self.tid, WM_USER, None, 0xf241b819)
312
313 def deinit(self):
314 import pythoncom
315 from win32file import CloseHandle
316
317 for h in self.handles:
318 if h is not None:
319 CloseHandle(h)
320 self.handles = None
321 pythoncom.CoUninitialize()
322 pass
323
324 def queryInterface(self, obj, klazzName):
325 from win32com.client import CastTo
326 return CastTo(obj, klazzName)
327
328class PlatformXPCOM:
329 def __init__(self, params):
330 sys.path.append(VboxSdkDir+'/bindings/xpcom/python/')
331 import xpcom.vboxxpcom
332 import xpcom
333 import xpcom.components
334
335 def getSessionObject(self, vbox):
336 import xpcom.components
337 return xpcom.components.classes["@virtualbox.org/Session;1"].createInstance()
338
339 def getVirtualBox(self):
340 import xpcom.components
341 return xpcom.components.classes["@virtualbox.org/VirtualBox;1"].createInstance()
342
343 def getType(self):
344 return 'XPCOM'
345
346 def getRemote(self):
347 return False
348
349 def getArray(self, obj, field):
350 return obj.__getattr__('get'+ComifyName(field))()
351
352 def initPerThread(self):
353 import xpcom
354 xpcom._xpcom.AttachThread()
355
356 def deinitPerThread(self):
357 import xpcom
358 xpcom._xpcom.DetachThread()
359
360 def createListener(self, impl, arg):
361 d = {}
362 d['BaseClass'] = impl
363 d['arg'] = arg
364 str = ""
365 str += "import xpcom.components\n"
366 str += "class ListenerImpl(BaseClass):\n"
367 str += " _com_interfaces_ = xpcom.components.interfaces.IEventListener\n"
368 str += " def __init__(self): BaseClass.__init__(self, arg)\n"
369 str += "result = ListenerImpl()\n"
370 exec (str,d,d)
371 return d['result']
372
373 def waitForEvents(self, timeout):
374 import xpcom
375 return xpcom._xpcom.WaitForEvents(timeout)
376
377 def interruptWaitEvents(self):
378 import xpcom
379 return xpcom._xpcom.InterruptWait()
380
381 def deinit(self):
382 import xpcom
383 xpcom._xpcom.DeinitCOM()
384
385 def queryInterface(self, obj, klazzName):
386 import xpcom.components
387 return obj.queryInterface(getattr(xpcom.components.interfaces, klazzName))
388
389class PlatformWEBSERVICE:
390 def __init__(self, params):
391 sys.path.append(os.path.join(VboxSdkDir,'bindings', 'webservice', 'python', 'lib'))
392 # not really needed, but just fail early if misconfigured
393 import VirtualBox_services
394 import VirtualBox_wrappers
395 from VirtualBox_wrappers import IWebsessionManager2
396
397 if params is not None:
398 self.user = params.get("user", "")
399 self.password = params.get("password", "")
400 self.url = params.get("url", "")
401 else:
402 self.user = ""
403 self.password = ""
404 self.url = None
405 self.vbox = None
406
407 def getSessionObject(self, vbox):
408 return self.wsmgr.getSessionObject(vbox)
409
410 def getVirtualBox(self):
411 return self.connect(self.url, self.user, self.password)
412
413 def connect(self, url, user, passwd):
414 if self.vbox is not None:
415 self.disconnect()
416 from VirtualBox_wrappers import IWebsessionManager2
417 if url is None:
418 url = ""
419 self.url = url
420 if user is None:
421 user = ""
422 self.user = user
423 if passwd is None:
424 passwd = ""
425 self.password = passwd
426 self.wsmgr = IWebsessionManager2(self.url)
427 self.vbox = self.wsmgr.logon(self.user, self.password)
428 if not self.vbox.handle:
429 raise Exception("cannot connect to '"+self.url+"' as '"+self.user+"'")
430 return self.vbox
431
432 def disconnect(self):
433 if self.vbox is not None and self.wsmgr is not None:
434 self.wsmgr.logoff(self.vbox)
435 self.vbox = None
436 self.wsmgr = None
437
438 def getType(self):
439 return 'WEBSERVICE'
440
441 def getRemote(self):
442 return True
443
444 def getArray(self, obj, field):
445 return obj.__getattr__(field)
446
447 def initPerThread(self):
448 pass
449
450 def deinitPerThread(self):
451 pass
452
453 def createListener(self, impl, arg):
454 raise Exception("no active listeners for webservices")
455
456 def waitForEvents(self, timeout):
457 # Webservices cannot do that yet
458 return 2;
459
460 def interruptWaitEvents(self, timeout):
461 # Webservices cannot do that yet
462 return False;
463
464 def deinit(self):
465 try:
466 disconnect()
467 except:
468 pass
469
470 def queryInterface(self, obj, klazzName):
471 d = {}
472 d['obj'] = obj
473 str = ""
474 str += "from VirtualBox_wrappers import "+klazzName+"\n"
475 str += "result = "+klazzName+"(obj.mgr,obj.handle)\n"
476 # wrong, need to test if class indeed implements this interface
477 exec (str,d,d)
478 return d['result']
479
480class SessionManager:
481 def __init__(self, mgr):
482 self.mgr = mgr
483
484 def getSessionObject(self, vbox):
485 return self.mgr.platform.getSessionObject(vbox)
486
487class VirtualBoxManager:
488 def __init__(self, style, platparams):
489 if style is None:
490 if sys.platform == 'win32':
491 style = "MSCOM"
492 else:
493 style = "XPCOM"
494
495
496 exec "self.platform = Platform"+style+"(platparams)"
497 # for webservices, enums are symbolic
498 self.constants = VirtualBoxReflectionInfo(style == "WEBSERVICE")
499 self.type = self.platform.getType()
500 self.remote = self.platform.getRemote()
501 self.style = style
502 self.mgr = SessionManager(self)
503
504 try:
505 self.vbox = self.platform.getVirtualBox()
506 except NameError,ne:
507 print "Installation problem: check that appropriate libs in place"
508 traceback.print_exc()
509 raise ne
510 except Exception,e:
511 print "init exception: ",e
512 traceback.print_exc()
513 if self.remote:
514 self.vbox = None
515 else:
516 raise e
517
518 def getArray(self, obj, field):
519 return self.platform.getArray(obj, field)
520
521 def getVirtualBox(self):
522 return self.platform.getVirtualBox()
523
524 def __del__(self):
525 self.deinit()
526
527 def deinit(self):
528 if hasattr(self, "vbox"):
529 del self.vbox
530 self.vbox = None
531 if hasattr(self, "platform"):
532 self.platform.deinit()
533 self.platform = None
534
535 def initPerThread(self):
536 self.platform.initPerThread()
537
538 def openMachineSession(self, mach, permitSharing = True):
539 session = self.mgr.getSessionObject(self.vbox)
540 if permitSharing:
541 type = self.constants.LockType_Shared
542 else:
543 type = self.constants.LockType_Write
544 mach.lockMachine(session, type)
545 return session
546
547 def closeMachineSession(self, session):
548 if session is not None:
549 session.unlockMachine()
550
551 def deinitPerThread(self):
552 self.platform.deinitPerThread()
553
554 def createListener(self, impl, arg = None):
555 return self.platform.createListener(impl, arg)
556
557 def waitForEvents(self, timeout):
558 """
559 Wait for events to arrive and process them.
560
561 The timeout is in milliseconds. A negative value means waiting for
562 ever, while 0 does not wait at all.
563
564 Returns 0 if events was processed.
565 Returns 1 if timed out or interrupted in some way.
566 Returns 2 on error (like not supported for web services).
567
568 Raises an exception if the calling thread is not the main thread (the one
569 that initialized VirtualBoxManager) or if the time isn't an integer.
570 """
571 return self.platform.waitForEvents(timeout)
572
573 def interruptWaitEvents(self):
574 """
575 Interrupt a waitForEvents call.
576 This is normally called from a worker thread.
577
578 Returns True on success, False on failure.
579 """
580 return self.platform.interruptWaitEvents()
581
582 def getPerfCollector(self, vbox):
583 return PerfCollector(self, vbox)
584
585 def getBinDir(self):
586 global VboxBinDir
587 return VboxBinDir
588
589 def getSdkDir(self):
590 global VboxSdkDir
591 return VboxSdkDir
592
593 def queryInterface(self, obj, klazzName):
594 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