VirtualBox

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

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

vboxapi.py,vboxapisetup.py: Don't patch stuff in the bundle, breaks signing.

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