VirtualBox

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

Last change on this file since 47721 was 47546, checked in by vboxsync, 11 years ago

vboxapi.py: No active listeners on windows due to pywin32 shortcuts in PyGatewayBase::QueryInterface().

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

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