VirtualBox

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

Last change on this file since 54123 was 54123, checked in by vboxsync, 10 years ago

Main/glue/vboxapi.py: fix circular reference sabotaging garbage collection and a typo in the webservice part of the python glue.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 39.3 KB
Line 
1# -*- coding: utf-8 -*-
2# $Id: vboxapi.py 54123 2015-02-10 11:15:24Z vboxsync $
3"""
4VirtualBox Python API Glue.
5"""
6
7__copyright__ = \
8 """
9 Copyright (C) 2009-2015 Oracle Corporation
10
11 This file is part of VirtualBox Open Source Edition (OSE), as
12 available from http://www.virtualbox.org. This file is free software;
13 you can redistribute it and/or modify it under the terms of the GNU
14 General Public License (GPL) as published by the Free Software
15 Foundation, in version 2 as it comes in the "COPYING" file of the
16 VirtualBox OSE distribution. VirtualBox OSE is distributed in the
17 hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
18 """
19__version__ = "$Revision: 54123 $"
20
21
22# Note! To set Python bitness on OSX use 'export VERSIONER_PYTHON_PREFER_32_BIT=yes'
23
24
25# Standard Python imports.
26import os
27import sys
28import traceback
29
30
31if sys.version_info >= (3, 0):
32 xrange = range
33 long = int
34 import builtins
35 print_ = getattr(builtins, 'print', None)
36elif sys.version_info >= (2, 6):
37 import __builtin__
38 print_ = getattr(__builtin__, 'print', None)
39else:
40 def print_(*args, **kwargs):
41 """The new-style print function for Python 2.4 and 2.5."""
42 fp = kwargs.pop("file", sys.stdout)
43 if fp is None:
44 return
45
46 def write(data):
47 if not isinstance(data, basestring):
48 data = str(data)
49 # If the file has an encoding, encode unicode with it.
50 if isinstance(fp, file) and isinstance(data, unicode) and fp.encoding is not None:
51 errors = getattr(fp, "errors", None)
52 if errors is None:
53 errors = "strict"
54 data = data.encode(fp.encoding, errors)
55 fp.write(data)
56
57 want_unicode = False
58 sep = kwargs.pop("sep", None)
59 if sep is not None:
60 if isinstance(sep, unicode):
61 want_unicode = True
62 elif not isinstance(sep, str):
63 raise TypeError("sep must be None or a string")
64 end = kwargs.pop("end", None)
65 if end is not None:
66 if isinstance(end, unicode):
67 want_unicode = True
68 elif not isinstance(end, str):
69 raise TypeError("end must be None or a string")
70 if kwargs:
71 raise TypeError("invalid keyword arguments to print()")
72 if not want_unicode:
73 for arg in args:
74 if isinstance(arg, unicode):
75 want_unicode = True
76 break
77 if want_unicode:
78 newline = unicode("\n")
79 space = unicode(" ")
80 else:
81 newline = "\n"
82 space = " "
83 if sep is None:
84 sep = space
85 if end is None:
86 end = newline
87 for i, arg in enumerate(args):
88 if i:
89 write(sep)
90 write(arg)
91 write(end)
92
93#
94# Globals, environment and sys.path changes.
95#
96VBoxBinDir = os.environ.get("VBOX_PROGRAM_PATH", None)
97VBoxSdkDir = os.environ.get("VBOX_SDK_PATH", None)
98
99if VBoxBinDir is None:
100 # Will be set by the installer
101 VBoxBinDir = "%VBOX_INSTALL_PATH%"
102else:
103 VBoxBinDir = os.path.abspath(VBoxBinDir)
104
105if VBoxSdkDir is None:
106 # Will be set by the installer
107 VBoxSdkDir = "%VBOX_SDK_PATH%"
108else:
109 VBoxSdkDir = os.path.abspath(VBoxSdkDir)
110
111os.environ["VBOX_PROGRAM_PATH"] = VBoxBinDir
112os.environ["VBOX_SDK_PATH"] = VBoxSdkDir
113sys.path.append(VBoxBinDir)
114
115
116#
117# Import the generated VirtualBox constants.
118#
119from .VirtualBox_constants import VirtualBoxReflectionInfo
120
121
122class PerfCollector(object):
123 """ This class provides a wrapper over IPerformanceCollector in order to
124 get more 'pythonic' interface.
125
126 To begin collection of metrics use setup() method.
127
128 To get collected data use query() method.
129
130 It is possible to disable metric collection without changing collection
131 parameters with disable() method. The enable() method resumes metric
132 collection.
133 """
134
135 def __init__(self, mgr, vbox):
136 """ Initializes the instance.
137
138 """
139 self.mgr = mgr
140 self.isMscom = (mgr.type == 'MSCOM')
141 self.collector = vbox.performanceCollector
142
143 def setup(self, names, objects, period, nsamples):
144 """ Discards all previously collected values for the specified
145 metrics, sets the period of collection and the number of retained
146 samples, enables collection.
147 """
148 self.collector.setupMetrics(names, objects, period, nsamples)
149
150 def enable(self, names, objects):
151 """ Resumes metric collection for the specified metrics.
152 """
153 self.collector.enableMetrics(names, objects)
154
155 def disable(self, names, objects):
156 """ Suspends metric collection for the specified metrics.
157 """
158 self.collector.disableMetrics(names, objects)
159
160 def query(self, names, objects):
161 """ Retrieves collected metric values as well as some auxiliary
162 information. Returns an array of dictionaries, one dictionary per
163 metric. Each dictionary contains the following entries:
164 'name': metric name
165 'object': managed object this metric associated with
166 'unit': unit of measurement
167 'scale': divide 'values' by this number to get float numbers
168 'values': collected data
169 'values_as_string': pre-processed values ready for 'print' statement
170 """
171 # Get around the problem with input arrays returned in output
172 # parameters (see #3953) for MSCOM.
173 if self.isMscom:
174 (values, names, objects, names_out, objects_out, units, scales, sequence_numbers,
175 indices, lengths) = self.collector.queryMetricsData(names, objects)
176 else:
177 (values, names_out, objects_out, units, scales, sequence_numbers,
178 indices, lengths) = self.collector.queryMetricsData(names, objects)
179 out = []
180 for i in xrange(0, len(names_out)):
181 scale = int(scales[i])
182 if scale != 1:
183 fmt = '%.2f%s'
184 else:
185 fmt = '%d %s'
186 out.append({
187 'name': str(names_out[i]),
188 'object': str(objects_out[i]),
189 'unit': str(units[i]),
190 'scale': scale,
191 'values': [int(values[j]) for j in xrange(int(indices[i]), int(indices[i]) + int(lengths[i]))],
192 'values_as_string': '[' + ', '.join([fmt % (int(values[j]) / scale, units[i]) for j in
193 xrange(int(indices[i]), int(indices[i]) + int(lengths[i]))]) + ']'
194 })
195 return out
196
197
198#
199# Attribute hacks.
200#
201def ComifyName(name):
202 return name[0].capitalize() + name[1:]
203
204
205## This is for saving the original DispatchBaseClass __getattr__ and __setattr__
206# method references.
207_g_dCOMForward = {
208 'getattr': None,
209 'setattr': None,
210}
211
212
213def _CustomGetAttr(self, sAttr):
214 """ Our getattr replacement for DispatchBaseClass. """
215 # Fastpath.
216 oRet = self.__class__.__dict__.get(sAttr)
217 if oRet is not None:
218 return oRet
219
220 # Try case-insensitivity workaround for class attributes (COM methods).
221 sAttrLower = sAttr.lower()
222 for k in self.__class__.__dict__.keys():
223 if k.lower() == sAttrLower:
224 setattr(self.__class__, sAttr, self.__class__.__dict__[k])
225 return getattr(self, k)
226
227 # Slow path.
228 try:
229 return _g_dCOMForward['getattr'](self, ComifyName(sAttr))
230 except AttributeError:
231 return _g_dCOMForward['getattr'](self, sAttr)
232
233
234def _CustomSetAttr(self, sAttr, oValue):
235 """ Our setattr replacement for DispatchBaseClass. """
236 try:
237 return _g_dCOMForward['setattr'](self, ComifyName(sAttr), oValue)
238 except AttributeError:
239 return _g_dCOMForward['setattr'](self, sAttr, oValue)
240
241
242class PlatformBase(object):
243 """
244 Base class for the platform specific code.
245 """
246
247 def __init__(self, aoParams):
248 _ = aoParams
249
250 def getVirtualBox(self):
251 """
252 Gets a the IVirtualBox singleton.
253 """
254 return None
255
256 def getSessionObject(self, oIVBox):
257 """
258 Get a session object that can be used for opening machine sessions.
259
260 The oIVBox parameter is an getVirtualBox() return value, i.e. an
261 IVirtualBox reference.
262
263 See also openMachineSession.
264 """
265 _ = oIVBox
266 return None
267
268 def getType(self):
269 """ Returns the platform type (class name sans 'Platform'). """
270 return None
271
272 def isRemote(self):
273 """
274 Returns True if remote (web services) and False if local (COM/XPCOM).
275 """
276 return False
277
278 def getArray(self, oInterface, sAttrib):
279 """
280 Retrives the value of the array attribute 'sAttrib' from
281 interface 'oInterface'.
282
283 This is for hiding platform specific differences in attributes
284 returning arrays.
285 """
286 _ = oInterface
287 _ = sAttrib
288 return None
289
290 def initPerThread(self):
291 """
292 Does backend specific initialization for the calling thread.
293 """
294 return True
295
296 def deinitPerThread(self):
297 """
298 Does backend specific uninitialization for the calling thread.
299 """
300 return True
301
302 def createListener(self, oImplClass, dArgs):
303 """
304 Instantiates and wraps an active event listener class so it can be
305 passed to an event source for registration.
306
307 oImplClass is a class (type, not instance) which implements
308 IEventListener.
309
310 dArgs is a dictionary with string indexed variables. This may be
311 modified by the method to pass platform specific parameters. Can
312 be None.
313
314 This currently only works on XPCOM. COM support is not possible due to
315 shortcuts taken in the COM bridge code, which is not under our control.
316 Use passive listeners for COM and web services.
317 """
318 _ = oImplClass
319 _ = dArgs
320 raise Exception("No active listeners for this platform")
321
322 def waitForEvents(self, cMsTimeout):
323 """
324 Wait for events to arrive and process them.
325
326 The timeout (cMsTimeout) is in milliseconds for how long to wait for
327 events to arrive. A negative value means waiting for ever, while 0
328 does not wait at all.
329
330 Returns 0 if events was processed.
331 Returns 1 if timed out or interrupted in some way.
332 Returns 2 on error (like not supported for web services).
333
334 Raises an exception if the calling thread is not the main thread (the one
335 that initialized VirtualBoxManager) or if the time isn't an integer.
336 """
337 _ = cMsTimeout
338 return 2
339
340 def interruptWaitEvents(self):
341 """
342 Interrupt a waitForEvents call.
343 This is normally called from a worker thread to wake up the main thread.
344
345 Returns True on success, False on failure.
346 """
347 return False
348
349 def deinit(self):
350 """
351 Unitializes the platform specific backend.
352 """
353 return None
354
355 def queryInterface(self, oIUnknown, sClassName):
356 """
357 IUnknown::QueryInterface wrapper.
358
359 oIUnknown is who to ask.
360 sClassName is the name of the interface we're asking for.
361 """
362 return None
363
364 #
365 # Error (exception) access methods.
366 #
367
368 def xcptGetStatus(self, oXcpt):
369 """
370 Returns the COM status code from the VBox API given exception.
371 """
372 return None
373
374 def xcptIsDeadInterface(self, oXcpt):
375 """
376 Returns True if the exception indicates that the interface is dead, False if not.
377 """
378 return False
379
380 def xcptIsEqual(self, oXcpt, hrStatus):
381 """
382 Checks if the exception oXcpt is equal to the COM/XPCOM status code
383 hrStatus.
384
385 The oXcpt parameter can be any kind of object, we'll just return True
386 if it doesn't behave like a our exception class.
387
388 Will not raise any exception as long as hrStatus and self are not bad.
389 """
390 try:
391 hrXcpt = self.xcptGetStatus(oXcpt)
392 except AttributeError:
393 return False
394 if hrXcpt == hrStatus:
395 return True
396
397 # Fudge for 32-bit signed int conversion.
398 if 0x7fffffff < hrStatus <= 0xffffffff and hrXcpt < 0:
399 if (hrStatus - 0x100000000) == hrXcpt:
400 return True
401 return False
402
403 def xcptGetMessage(self, oXcpt):
404 """
405 Returns the best error message found in the COM-like exception.
406 Returns None to fall back on xcptToString.
407 Raises exception if oXcpt isn't our kind of exception object.
408 """
409 return None
410
411 def xcptGetBaseXcpt(self):
412 """
413 Returns the base exception class.
414 """
415 return None
416
417 def xcptSetupConstants(self, oDst):
418 """
419 Copy/whatever all error constants onto oDst.
420 """
421 return oDst
422
423 @staticmethod
424 def xcptCopyErrorConstants(oDst, oSrc):
425 """
426 Copy everything that looks like error constants from oDst to oSrc.
427 """
428 for sAttr in dir(oSrc):
429 if sAttr[0].isupper() and (sAttr[1].isupper() or sAttr[1] == '_'):
430 oAttr = getattr(oSrc, sAttr)
431 if type(oAttr) is int:
432 setattr(oDst, sAttr, oAttr)
433 return oDst
434
435
436class PlatformMSCOM(PlatformBase):
437 """
438 Platform specific code for MS COM.
439 """
440
441 ## @name VirtualBox COM Typelib definitions (should be generate)
442 #
443 # @remarks Must be updated when the corresponding VirtualBox.xidl bits
444 # are changed. Fortunately this isn't very often.
445 # @{
446 VBOX_TLB_GUID = '{D7569351-1750-46F0-936E-BD127D5BC264}'
447 VBOX_TLB_LCID = 0
448 VBOX_TLB_MAJOR = 1
449 VBOX_TLB_MINOR = 3
450 ## @}
451
452 def __init__(self, dParams):
453 PlatformBase.__init__(self, dParams)
454
455 #
456 # Since the code runs on all platforms, we have to do a lot of
457 # importing here instead of at the top of the file where it's normally located.
458 #
459 from win32com import universal
460 from win32com.client import gencache, DispatchBaseClass
461 from win32com.client import constants, getevents
462 import win32com
463 import pythoncom
464 import win32api
465 import winerror
466 from win32con import DUPLICATE_SAME_ACCESS
467 from win32api import GetCurrentThread, GetCurrentThreadId, DuplicateHandle, GetCurrentProcess
468 import threading
469
470 self.winerror = winerror
471
472 pid = GetCurrentProcess()
473 self.tid = GetCurrentThreadId()
474 handle = DuplicateHandle(pid, GetCurrentThread(), pid, 0, 0, DUPLICATE_SAME_ACCESS)
475 self.handles = []
476 self.handles.append(handle)
477
478 # Hack the COM dispatcher base class so we can modify method and
479 # attribute names to match those in xpcom.
480 if _g_dCOMForward['setattr'] is None:
481 _g_dCOMForward['getattr'] = DispatchBaseClass.__dict__['__getattr__']
482 _g_dCOMForward['setattr'] = DispatchBaseClass.__dict__['__setattr__']
483 setattr(DispatchBaseClass, '__getattr__', _CustomGetAttr)
484 setattr(DispatchBaseClass, '__setattr__', _CustomSetAttr)
485
486 # Hack the exception base class so the users doesn't need to check for
487 # XPCOM or COM and do different things.
488 ## @todo
489
490 #
491 # Make sure the gencache is correct (we don't quite follow the COM
492 # versioning rules).
493 #
494 self.flushGenPyCache(win32com.client.gencache)
495 win32com.client.gencache.EnsureDispatch('VirtualBox.Session')
496 win32com.client.gencache.EnsureDispatch('VirtualBox.VirtualBox')
497
498 self.oIntCv = threading.Condition()
499 self.fInterrupted = False
500
501 _ = dParams
502
503 def flushGenPyCache(self, oGenCache):
504 """
505 Flushes VBox related files in the win32com gen_py cache.
506
507 This is necessary since we don't follow the typelib versioning rules
508 that everyeone else seems to subscribe to.
509 """
510 #
511 # The EnsureModule method have broken validation code, it doesn't take
512 # typelib module directories into account. So we brute force them here.
513 # (It's possible the directory approach is from some older pywin
514 # version or the result of runnig makepy or gencache manually, but we
515 # need to cover it as well.)
516 #
517 sName = oGenCache.GetGeneratedFileName(self.VBOX_TLB_GUID, self.VBOX_TLB_LCID,
518 self.VBOX_TLB_MAJOR, self.VBOX_TLB_MINOR)
519 sGenPath = oGenCache.GetGeneratePath()
520 if len(sName) > 36 and len(sGenPath) > 5:
521 sTypelibPath = os.path.join(sGenPath, sName)
522 if os.path.isdir(sTypelibPath):
523 import shutil
524 shutil.rmtree(sTypelibPath, ignore_errors=True)
525
526 #
527 # Ensure that our typelib is valid.
528 #
529 return oGenCache.EnsureModule(self.VBOX_TLB_GUID, self.VBOX_TLB_LCID, self.VBOX_TLB_MAJOR, self.VBOX_TLB_MINOR)
530
531 def getSessionObject(self, oIVBox):
532 _ = oIVBox
533 import win32com
534 from win32com.client import Dispatch
535 return win32com.client.Dispatch("VirtualBox.Session")
536
537 def getVirtualBox(self):
538 import win32com
539 from win32com.client import Dispatch
540 return win32com.client.Dispatch("VirtualBox.VirtualBox")
541
542 def getType(self):
543 return 'MSCOM'
544
545 def getArray(self, oInterface, sAttrib):
546 return oInterface.__getattr__(sAttrib)
547
548 def initPerThread(self):
549 import pythoncom
550 pythoncom.CoInitializeEx(0)
551
552 def deinitPerThread(self):
553 import pythoncom
554 pythoncom.CoUninitialize()
555
556 def createListener(self, oImplClass, dArgs):
557 if True:
558 raise Exception('no active listeners on Windows as PyGatewayBase::QueryInterface() '
559 'returns new gateway objects all the time, thus breaking EventQueue '
560 'assumptions about the listener interface pointer being constants between calls ')
561 # Did this code ever really work?
562 d = {}
563 d['BaseClass'] = oImplClass
564 d['dArgs'] = dArgs
565 d['tlb_guid'] = PlatformMSCOM.VBOX_TLB_GUID
566 d['tlb_major'] = PlatformMSCOM.VBOX_TLB_MAJOR
567 d['tlb_minor'] = PlatformMSCOM.VBOX_TLB_MINOR
568 str_ = ""
569 str_ += "import win32com.server.util\n"
570 str_ += "import pythoncom\n"
571
572 str_ += "class ListenerImpl(BaseClass):\n"
573 str_ += " _com_interfaces_ = ['IEventListener']\n"
574 str_ += " _typelib_guid_ = tlb_guid\n"
575 str_ += " _typelib_version_ = tlb_major, tlb_minor\n"
576 str_ += " _reg_clsctx_ = pythoncom.CLSCTX_INPROC_SERVER\n"
577 # Maybe we'd better implement Dynamic invoke policy, to be more flexible here
578 str_ += " _reg_policy_spec_ = 'win32com.server.policy.EventHandlerPolicy'\n"
579
580 # capitalized version of listener method
581 str_ += " HandleEvent=BaseClass.handleEvent\n"
582 str_ += " def __init__(self): BaseClass.__init__(self, dArgs)\n"
583 str_ += "result = win32com.server.util.wrap(ListenerImpl())\n"
584 exec(str_, d, d)
585 return d['result']
586
587 def waitForEvents(self, timeout):
588 from win32api import GetCurrentThreadId
589 from win32event import INFINITE
590 from win32event import MsgWaitForMultipleObjects, \
591 QS_ALLINPUT, WAIT_TIMEOUT, WAIT_OBJECT_0
592 from pythoncom import PumpWaitingMessages
593 import types
594
595 if not isinstance(timeout, int):
596 raise TypeError("The timeout argument is not an integer")
597 if self.tid != GetCurrentThreadId():
598 raise Exception("wait for events from the same thread you inited!")
599
600 if timeout < 0:
601 cMsTimeout = INFINITE
602 else:
603 cMsTimeout = timeout
604 rc = MsgWaitForMultipleObjects(self.handles, 0, cMsTimeout, QS_ALLINPUT)
605 if WAIT_OBJECT_0 <= rc < WAIT_OBJECT_0 + len(self.handles):
606 # is it possible?
607 rc = 2
608 elif rc == WAIT_OBJECT_0 + len(self.handles):
609 # Waiting messages
610 PumpWaitingMessages()
611 rc = 0
612 else:
613 # Timeout
614 rc = 1
615
616 # check for interruption
617 self.oIntCv.acquire()
618 if self.fInterrupted:
619 self.fInterrupted = False
620 rc = 1
621 self.oIntCv.release()
622
623 return rc
624
625 def interruptWaitEvents(self):
626 """
627 Basically a python implementation of NativeEventQueue::postEvent().
628
629 The magic value must be in sync with the C++ implementation or this
630 won't work.
631
632 Note that because of this method we cannot easily make use of a
633 non-visible Window to handle the message like we would like to do.
634 """
635 from win32api import PostThreadMessage
636 from win32con import WM_USER
637
638 self.oIntCv.acquire()
639 self.fInterrupted = True
640 self.oIntCv.release()
641 try:
642 PostThreadMessage(self.tid, WM_USER, None, 0xf241b819)
643 except:
644 return False
645 return True
646
647 def deinit(self):
648 import pythoncom
649 from win32file import CloseHandle
650
651 for h in self.handles:
652 if h is not None:
653 CloseHandle(h)
654 self.handles = None
655 pythoncom.CoUninitialize()
656 pass
657
658 def queryInterface(self, oIUnknown, sClassName):
659 from win32com.client import CastTo
660 return CastTo(oIUnknown, sClassName)
661
662 def xcptGetStatus(self, oXcpt):
663 # The DISP_E_EXCEPTION + excptinfo fun needs checking up, only
664 # empirical info on it so far.
665 hrXcpt = oXcpt.hresult
666 if hrXcpt == self.winerror.DISP_E_EXCEPTION:
667 try:
668 hrXcpt = oXcpt.excepinfo[5]
669 except:
670 pass
671 return hrXcpt
672
673 def xcptIsDeadInterface(self, oXcpt):
674 return self.xcptGetStatus(oXcpt) in [
675 0x800706ba, -2147023174, # RPC_S_SERVER_UNAVAILABLE.
676 0x800706be, -2147023170, # RPC_S_CALL_FAILED.
677 0x800706bf, -2147023169, # RPC_S_CALL_FAILED_DNE.
678 0x80010108, -2147417848, # RPC_E_DISCONNECTED.
679 0x800706b5, -2147023179, # RPC_S_UNKNOWN_IF
680 ]
681
682 def xcptGetMessage(self, oXcpt):
683 if hasattr(oXcpt, 'excepinfo'):
684 try:
685 if len(oXcpt.excepinfo) >= 3:
686 sRet = oXcpt.excepinfo[2]
687 if len(sRet) > 0:
688 return sRet[0:]
689 except:
690 pass
691 if hasattr(oXcpt, 'strerror'):
692 try:
693 sRet = oXcpt.strerror
694 if len(sRet) > 0:
695 return sRet
696 except:
697 pass
698 return None
699
700 def xcptGetBaseXcpt(self):
701 import pythoncom
702
703 return pythoncom.com_error
704
705 def xcptSetupConstants(self, oDst):
706 import winerror
707
708 oDst = self.xcptCopyErrorConstants(oDst, winerror)
709
710 # XPCOM compatability constants.
711 oDst.NS_OK = oDst.S_OK
712 oDst.NS_ERROR_FAILURE = oDst.E_FAIL
713 oDst.NS_ERROR_ABORT = oDst.E_ABORT
714 oDst.NS_ERROR_NULL_POINTER = oDst.E_POINTER
715 oDst.NS_ERROR_NO_INTERFACE = oDst.E_NOINTERFACE
716 oDst.NS_ERROR_INVALID_ARG = oDst.E_INVALIDARG
717 oDst.NS_ERROR_OUT_OF_MEMORY = oDst.E_OUTOFMEMORY
718 oDst.NS_ERROR_NOT_IMPLEMENTED = oDst.E_NOTIMPL
719 oDst.NS_ERROR_UNEXPECTED = oDst.E_UNEXPECTED
720 return oDst
721
722
723class PlatformXPCOM(PlatformBase):
724 """
725 Platform specific code for XPCOM.
726 """
727
728 def __init__(self, dParams):
729 PlatformBase.__init__(self, dParams)
730 sys.path.append(VBoxSdkDir + '/bindings/xpcom/python/')
731 import xpcom.vboxxpcom
732 import xpcom
733 import xpcom.components
734 _ = dParams
735
736 def getSessionObject(self, oIVBox):
737 _ = oIVBox
738 import xpcom.components
739 return xpcom.components.classes["@virtualbox.org/Session;1"].createInstance()
740
741 def getVirtualBox(self):
742 import xpcom.components
743 return xpcom.components.classes["@virtualbox.org/VirtualBox;1"].createInstance()
744
745 def getType(self):
746 return 'XPCOM'
747
748 def getArray(self, oInterface, sAttrib):
749 return oInterface.__getattr__('get' + ComifyName(sAttrib))()
750
751 def initPerThread(self):
752 import xpcom
753 xpcom._xpcom.AttachThread()
754
755 def deinitPerThread(self):
756 import xpcom
757 xpcom._xpcom.DetachThread()
758
759 def createListener(self, oImplClass, dArgs):
760 d = {}
761 d['BaseClass'] = oImplClass
762 d['dArgs'] = dArgs
763 str = ""
764 str += "import xpcom.components\n"
765 str += "class ListenerImpl(BaseClass):\n"
766 str += " _com_interfaces_ = xpcom.components.interfaces.IEventListener\n"
767 str += " def __init__(self): BaseClass.__init__(self, dArgs)\n"
768 str += "result = ListenerImpl()\n"
769 exec(str, d, d)
770 return d['result']
771
772 def waitForEvents(self, timeout):
773 import xpcom
774 return xpcom._xpcom.WaitForEvents(timeout)
775
776 def interruptWaitEvents(self):
777 import xpcom
778 return xpcom._xpcom.InterruptWait()
779
780 def deinit(self):
781 import xpcom
782 xpcom._xpcom.DeinitCOM()
783
784 def queryInterface(self, oIUnknown, sClassName):
785 import xpcom.components
786 return oIUnknown.queryInterface(getattr(xpcom.components.interfaces, sClassName))
787
788 def xcptGetStatus(self, oXcpt):
789 return oXcpt.errno
790
791 def xcptIsDeadInterface(self, oXcpt):
792 return self.xcptGetStatus(oXcpt) in [
793 0x80004004, -2147467260, # NS_ERROR_ABORT
794 0x800706be, -2147023170, # NS_ERROR_CALL_FAILED (RPC_S_CALL_FAILED)
795 ]
796
797 def xcptGetMessage(self, oXcpt):
798 if hasattr(oXcpt, 'msg'):
799 try:
800 sRet = oXcpt.msg
801 if len(sRet) > 0:
802 return sRet
803 except:
804 pass
805 return None
806
807 def xcptGetBaseXcpt(self):
808 import xpcom
809 return xpcom.Exception
810
811 def xcptSetupConstants(self, oDst):
812 import xpcom
813 oDst = self.xcptCopyErrorConstants(oDst, xpcom.nsError)
814
815 # COM compatability constants.
816 oDst.E_ACCESSDENIED = -2147024891 # see VBox/com/defs.h
817 oDst.S_OK = oDst.NS_OK
818 oDst.E_FAIL = oDst.NS_ERROR_FAILURE
819 oDst.E_ABORT = oDst.NS_ERROR_ABORT
820 oDst.E_POINTER = oDst.NS_ERROR_NULL_POINTER
821 oDst.E_NOINTERFACE = oDst.NS_ERROR_NO_INTERFACE
822 oDst.E_INVALIDARG = oDst.NS_ERROR_INVALID_ARG
823 oDst.E_OUTOFMEMORY = oDst.NS_ERROR_OUT_OF_MEMORY
824 oDst.E_NOTIMPL = oDst.NS_ERROR_NOT_IMPLEMENTED
825 oDst.E_UNEXPECTED = oDst.NS_ERROR_UNEXPECTED
826 oDst.DISP_E_EXCEPTION = -2147352567 # For COM compatability only.
827 return oDst
828
829
830class PlatformWEBSERVICE(PlatformBase):
831 """
832 VirtualBox Web Services API specific code.
833 """
834
835 def __init__(self, dParams):
836 PlatformBase.__init__(self, dParams)
837 # Import web services stuff. Fix the sys.path the first time.
838 sWebServLib = os.path.join(VBoxSdkDir, 'bindings', 'webservice', 'python', 'lib')
839 if sWebServLib not in sys.path:
840 sys.path.append(sWebServLib)
841 import VirtualBox_wrappers
842 from VirtualBox_wrappers import IWebsessionManager2
843
844 # Initialize instance variables from parameters.
845 if dParams is not None:
846 self.user = dParams.get("user", "")
847 self.password = dParams.get("password", "")
848 self.url = dParams.get("url", "")
849 else:
850 self.user = ""
851 self.password = ""
852 self.url = None
853 self.vbox = None
854 self.wsmgr = None
855
856 #
857 # Base class overrides.
858 #
859
860 def getSessionObject(self, oIVBox):
861 return self.wsmgr.getSessionObject(oIVBox)
862
863 def getVirtualBox(self):
864 return self.connect(self.url, self.user, self.password)
865
866 def getType(self):
867 return 'WEBSERVICE'
868
869 def isRemote(self):
870 """ Returns True if remote VBox host, False if local. """
871 return True
872
873 def getArray(self, oInterface, sAttrib):
874 return oInterface.__getattr__(sAttrib)
875
876 def waitForEvents(self, timeout):
877 # Webservices cannot do that yet
878 return 2
879
880 def interruptWaitEvents(self, timeout):
881 # Webservices cannot do that yet
882 return False
883
884 def deinit(self):
885 try:
886 self.disconnect()
887 except:
888 pass
889
890 def queryInterface(self, oIUnknown, sClassName):
891 d = {}
892 d['oIUnknown'] = oIUnknown
893 str = ""
894 str += "from VirtualBox_wrappers import " + sClassName + "\n"
895 str += "result = " + sClassName + "(oIUnknown.mgr, oIUnknown.handle)\n"
896 # wrong, need to test if class indeed implements this interface
897 exec(str, d, d)
898 return d['result']
899
900 #
901 # Web service specific methods.
902 #
903
904 def connect(self, url, user, passwd):
905 if self.vbox is not None:
906 self.disconnect()
907 from VirtualBox_wrappers import IWebsessionManager2
908
909 if url is None:
910 url = ""
911 self.url = url
912 if user is None:
913 user = ""
914 self.user = user
915 if passwd is None:
916 passwd = ""
917 self.password = passwd
918 self.wsmgr = IWebsessionManager2(self.url)
919 self.vbox = self.wsmgr.logon(self.user, self.password)
920 if not self.vbox.handle:
921 raise Exception("cannot connect to '" + self.url + "' as '" + self.user + "'")
922 return self.vbox
923
924 def disconnect(self):
925 if self.vbox is not None and self.wsmgr is not None:
926 self.wsmgr.logoff(self.vbox)
927 self.vbox = None
928 self.wsmgr = None
929
930
931## The current (last) exception class.
932# This is reinitalized whenever VirtualBoxManager is called, so it will hold
933# the reference to the error exception class for the last platform/style that
934# was used. Most clients does talk to multiple VBox instance on different
935# platforms at the same time, so this should be sufficent for most uses and
936# be way simpler to use than VirtualBoxManager::oXcptClass.
937CurXctpClass = None
938
939
940class VirtualBoxManager(object):
941 """
942 VirtualBox API manager class.
943
944 The API users will have to instantiate this. If no parameters are given,
945 it will default to interface with the VirtualBox running on the local
946 machine. sStyle can be None (default), MSCOM, XPCOM or WEBSERVICES. Most
947 users will either be specifying None or WEBSERVICES.
948
949 The dPlatformParams is an optional dictionary for passing parameters to the
950 WEBSERVICE backend.
951 """
952
953 class Statuses(object):
954 def __init__(self):
955 pass
956
957 def __init__(self, sStyle=None, dPlatformParams=None):
958 if sStyle is None:
959 if sys.platform == 'win32':
960 sStyle = "MSCOM"
961 else:
962 sStyle = "XPCOM"
963 if sStyle == 'XPCOM':
964 self.platform = PlatformXPCOM(dPlatformParams)
965 elif sStyle == 'MSCOM':
966 self.platform = PlatformMSCOM(dPlatformParams)
967 elif sStyle == 'WEBSERVICE':
968 self.platform = PlatformWEBSERVICE(dPlatformParams)
969 else:
970 raise Exception('Unknown sStyle=%s' % (sStyle,))
971 self.style = sStyle
972 self.type = self.platform.getType()
973 self.remote = self.platform.isRemote()
974 ## VirtualBox API constants (for webservices, enums are symbolic).
975 self.constants = VirtualBoxReflectionInfo(sStyle == "WEBSERVICE")
976
977 ## Status constants.
978 self.statuses = self.platform.xcptSetupConstants(VirtualBoxManager.Statuses())
979 ## @todo Add VBOX_E_XXX to statuses? They're already in constants...
980 ## Dictionary for errToString, built on demand.
981 self._dErrorValToName = None
982
983 ## The exception class for the selected platform.
984 self.oXcptClass = self.platform.xcptGetBaseXcpt()
985 global CurXcptClass
986 CurXcptClass = self.oXcptClass
987
988 # Get the virtualbox singleton.
989 try:
990 self.vbox = self.platform.getVirtualBox()
991 except NameError:
992 print_("Installation problem: check that appropriate libs in place")
993 traceback.print_exc()
994 raise
995 except Exception:
996 _, e, _ = sys.exc_info()
997 print_("init exception: ", e)
998 traceback.print_exc()
999 if self.remote:
1000 self.vbox = None
1001 else:
1002 raise e
1003
1004 def __del__(self):
1005 self.deinit()
1006
1007 def getPythonApiRevision(self):
1008 """
1009 Returns a Python API revision number.
1010 This will be incremented when features are added to this file.
1011 """
1012 return 3
1013
1014 @deprecated
1015 @property
1016 def mgr(self):
1017 """
1018 This used to be an attribute referring to a session manager class with
1019 only one method called getSessionObject. It moved into this class.
1020 """
1021 return self;
1022
1023 #
1024 # Wrappers for self.platform methods.
1025 #
1026 def getVirtualBox(self):
1027 """ See PlatformBase::getVirtualBox(). """
1028 return self.platform.getVirtualBox()
1029
1030 def getSessionObject(self, oIVBox):
1031 """ See PlatformBase::getSessionObject(). """
1032 return self.platform.getSessionObject(oIVBox)
1033
1034 def getArray(self, oInterface, sAttrib):
1035 """ See PlatformBase::getArray(). """
1036 return self.platform.getArray(oInterface, sAttrib)
1037
1038 def createListener(self, oImplClass, dArgs=None):
1039 """ See PlatformBase::createListener(). """
1040 return self.platform.createListener(oImplClass, dArgs)
1041
1042 def waitForEvents(self, cMsTimeout):
1043 """ See PlatformBase::waitForEvents(). """
1044 return self.platform.waitForEvents(cMsTimeout)
1045
1046 def interruptWaitEvents(self):
1047 """ See PlatformBase::interruptWaitEvents(). """
1048 return self.platform.interruptWaitEvents()
1049
1050 def queryInterface(self, oIUnknown, sClassName):
1051 """ See PlatformBase::queryInterface(). """
1052 return self.platform.queryInterface(oIUnknown, sClassName)
1053
1054 #
1055 # Init and uninit.
1056 #
1057 def initPerThread(self):
1058 """ See PlatformBase::deinitPerThread(). """
1059 self.platform.initPerThread()
1060
1061 def deinitPerThread(self):
1062 """ See PlatformBase::deinitPerThread(). """
1063 return self.platform.deinitPerThread()
1064
1065 def deinit(self):
1066 """
1067 For unitializing the manager.
1068 Do not access it after calling this method.
1069 """
1070 if hasattr(self, "vbox"):
1071 del self.vbox
1072 self.vbox = None
1073 if hasattr(self, "platform"):
1074 self.platform.deinit()
1075 self.platform = None
1076 return True
1077
1078 #
1079 # Utility methods.
1080 #
1081 def openMachineSession(self, oIMachine, fPermitSharing=True):
1082 """
1083 Attempts to open the a session to the machine.
1084 Returns a session object on success.
1085 Raises exception on failure.
1086 """
1087 oSession = self.getSessionObject(self.vbox);
1088 if fPermitSharing:
1089 type_ = self.constants.LockType_Shared
1090 else:
1091 type_ = self.constants.LockType_Write
1092 oIMachine.lockMachine(oSession, type_)
1093 return oSession
1094
1095 def closeMachineSession(self, oSession):
1096 """
1097 Closes a session opened by openMachineSession.
1098 Ignores None parameters.
1099 """
1100 if oSession is not None:
1101 oSession.unlockMachine()
1102 return True
1103
1104 def getPerfCollector(self, oIVBox):
1105 """
1106 Returns a helper class (PerfCollector) for accessing performance
1107 collector goodies. See PerfCollector for details.
1108 """
1109 return PerfCollector(self, oIVBox)
1110
1111 def getBinDir(self):
1112 """
1113 Returns the VirtualBox binary directory.
1114 """
1115 global VBoxBinDir
1116 return VBoxBinDir
1117
1118 def getSdkDir(self):
1119 """
1120 Returns the VirtualBox SDK directory.
1121 """
1122 global VBoxSdkDir
1123 return VBoxSdkDir
1124
1125 #
1126 # Error code utilities.
1127 #
1128 ## @todo port to webservices!
1129 def xcptGetStatus(self, oXcpt=None):
1130 """
1131 Gets the status code from an exception. If the exception parameter
1132 isn't specified, the current exception is examined.
1133 """
1134 if oXcpt is None:
1135 oXcpt = sys.exc_info()[1]
1136 return self.platform.xcptGetStatus(oXcpt)
1137
1138 def xcptIsDeadInterface(self, oXcpt=None):
1139 """
1140 Returns True if the exception indicates that the interface is dead,
1141 False if not. If the exception parameter isn't specified, the current
1142 exception is examined.
1143 """
1144 if oXcpt is None:
1145 oXcpt = sys.exc_info()[1]
1146 return self.platform.xcptIsDeadInterface(oXcpt)
1147
1148 def xcptIsOurXcptKind(self, oXcpt=None):
1149 """
1150 Checks if the exception is one that could come from the VBox API. If
1151 the exception parameter isn't specified, the current exception is
1152 examined.
1153 """
1154 if self.oXcptClass is None: # @todo find the exception class for web services!
1155 return False
1156 if oXcpt is None:
1157 oXcpt = sys.exc_info()[1]
1158 return isinstance(oXcpt, self.oXcptClass)
1159
1160 def xcptIsEqual(self, oXcpt, hrStatus):
1161 """
1162 Checks if the exception oXcpt is equal to the COM/XPCOM status code
1163 hrStatus.
1164
1165 The oXcpt parameter can be any kind of object, we'll just return True
1166 if it doesn't behave like a our exception class. If it's None, we'll
1167 query the current exception and examine that.
1168
1169 Will not raise any exception as long as hrStatus and self are not bad.
1170 """
1171 if oXcpt is None:
1172 oXcpt = sys.exc_info()[1]
1173 return self.platform.xcptIsEqual(oXcpt, hrStatus)
1174
1175 def xcptIsNotEqual(self, oXcpt, hrStatus):
1176 """
1177 Negated xcptIsEqual.
1178 """
1179 return not self.xcptIsEqual(oXcpt, hrStatus)
1180
1181 def xcptToString(self, hrStatusOrXcpt=None):
1182 """
1183 Converts the specified COM status code, or the status code of the
1184 specified exception, to a C constant string. If the parameter isn't
1185 specified (is None), the current exception is examined.
1186 """
1187
1188 # Deal with exceptions.
1189 if hrStatusOrXcpt is None or self.xcptIsOurXcptKind(hrStatusOrXcpt):
1190 hrStatus = self.xcptGetStatus(hrStatusOrXcpt)
1191 else:
1192 hrStatus = hrStatusOrXcpt
1193
1194 # Build the dictionary on demand.
1195 if self._dErrorValToName is None:
1196 dErrorValToName = dict()
1197 for sKey in dir(self.statuses):
1198 if sKey[0].isupper():
1199 oValue = getattr(self.statuses, sKey)
1200 if type(oValue) is int:
1201 dErrorValToName[oValue] = sKey
1202 self._dErrorValToName = dErrorValToName
1203
1204 # Do the lookup, falling back on formatting the status number.
1205 try:
1206 sStr = self._dErrorValToName[int(hrStatus)]
1207 except KeyError:
1208 hrLong = long(hrStatus)
1209 sStr = '%#x (%d)' % (hrLong, hrLong)
1210 return sStr
1211
1212 def xcptGetMessage(self, oXcpt=None):
1213 """
1214 Returns the best error message found in the COM-like exception. If the
1215 exception parameter isn't specified, the current exception is examined.
1216 """
1217 if oXcpt is None:
1218 oXcpt = sys.exc_info()[1]
1219 sRet = self.platform.xcptGetMessage(oXcpt)
1220 if sRet is None:
1221 sRet = self.xcptToString(oXcpt)
1222 return sRet
1223
1224 # Legacy, remove in a day or two.
1225 errGetStatus = xcptGetStatus
1226 errIsDeadInterface = xcptIsDeadInterface
1227 errIsOurXcptKind = xcptIsOurXcptKind
1228 errGetMessage = xcptGetMessage
1229
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