VirtualBox

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

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

Main/glue/vboxapi.py: support for python3 (so far Windows only, PyXPCOM library adaptions missing), contributed by Ilya Kulakov. Thanks!

  • 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 54057 2015-02-02 12:35:10Z 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: 54057 $"
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 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 ## @deprecated
1005 # This used to refer to a session manager class with only one method
1006 # called getSessionObject. The method has moved into this call.
1007 self.mgr = self
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 #
1020 # Wrappers for self.platform methods.
1021 #
1022 def getVirtualBox(self):
1023 """ See PlatformBase::getVirtualBox(). """
1024 return self.platform.getVirtualBox()
1025
1026 def getSessionObject(self, oIVBox):
1027 """ See PlatformBase::getSessionObject(). """
1028 return self.platform.getSessionObject(oIVBox)
1029
1030 def getArray(self, oInterface, sAttrib):
1031 """ See PlatformBase::getArray(). """
1032 return self.platform.getArray(oInterface, sAttrib)
1033
1034 def createListener(self, oImplClass, dArgs=None):
1035 """ See PlatformBase::createListener(). """
1036 return self.platform.createListener(oImplClass, dArgs)
1037
1038 def waitForEvents(self, cMsTimeout):
1039 """ See PlatformBase::waitForEvents(). """
1040 return self.platform.waitForEvents(cMsTimeout)
1041
1042 def interruptWaitEvents(self):
1043 """ See PlatformBase::interruptWaitEvents(). """
1044 return self.platform.interruptWaitEvents()
1045
1046 def queryInterface(self, oIUnknown, sClassName):
1047 """ See PlatformBase::queryInterface(). """
1048 return self.platform.queryInterface(oIUnknown, sClassName)
1049
1050 #
1051 # Init and uninit.
1052 #
1053 def initPerThread(self):
1054 """ See PlatformBase::deinitPerThread(). """
1055 self.platform.initPerThread()
1056
1057 def deinitPerThread(self):
1058 """ See PlatformBase::deinitPerThread(). """
1059 return self.platform.deinitPerThread()
1060
1061 def deinit(self):
1062 """
1063 For unitializing the manager.
1064 Do not access it after calling this method.
1065 """
1066 if hasattr(self, "vbox"):
1067 del self.vbox
1068 self.vbox = None
1069 if hasattr(self, "platform"):
1070 self.platform.deinit()
1071 self.platform = None
1072 return True
1073
1074 #
1075 # Utility methods.
1076 #
1077 def openMachineSession(self, oIMachine, fPermitSharing=True):
1078 """
1079 Attemts to open the a session to the machine.
1080 Returns a session object on success.
1081 Raises exception on failure.
1082 """
1083 oSession = self.mgr.getSessionObject(self.vbox)
1084 if fPermitSharing:
1085 type_ = self.constants.LockType_Shared
1086 else:
1087 type_ = self.constants.LockType_Write
1088 oIMachine.lockMachine(oSession, type_)
1089 return oSession
1090
1091 def closeMachineSession(self, oSession):
1092 """
1093 Closes a session opened by openMachineSession.
1094 Ignores None parameters.
1095 """
1096 if oSession is not None:
1097 oSession.unlockMachine()
1098 return True
1099
1100 def getPerfCollector(self, oIVBox):
1101 """
1102 Returns a helper class (PerfCollector) for accessing performance
1103 collector goodies. See PerfCollector for details.
1104 """
1105 return PerfCollector(self, oIVBox)
1106
1107 def getBinDir(self):
1108 """
1109 Returns the VirtualBox binary directory.
1110 """
1111 global VBoxBinDir
1112 return VBoxBinDir
1113
1114 def getSdkDir(self):
1115 """
1116 Returns the VirtualBox SDK directory.
1117 """
1118 global VBoxSdkDir
1119 return VBoxSdkDir
1120
1121 #
1122 # Error code utilities.
1123 #
1124 ## @todo port to webservices!
1125 def xcptGetStatus(self, oXcpt=None):
1126 """
1127 Gets the status code from an exception. If the exception parameter
1128 isn't specified, the current exception is examined.
1129 """
1130 if oXcpt is None:
1131 oXcpt = sys.exc_info()[1]
1132 return self.platform.xcptGetStatus(oXcpt)
1133
1134 def xcptIsDeadInterface(self, oXcpt=None):
1135 """
1136 Returns True if the exception indicates that the interface is dead,
1137 False if not. If the exception parameter isn't specified, the current
1138 exception is examined.
1139 """
1140 if oXcpt is None:
1141 oXcpt = sys.exc_info()[1]
1142 return self.platform.xcptIsDeadInterface(oXcpt)
1143
1144 def xcptIsOurXcptKind(self, oXcpt=None):
1145 """
1146 Checks if the exception is one that could come from the VBox API. If
1147 the exception parameter isn't specified, the current exception is
1148 examined.
1149 """
1150 if self.oXcptClass is None: # @todo find the exception class for web services!
1151 return False
1152 if oXcpt is None:
1153 oXcpt = sys.exc_info()[1]
1154 return isinstance(oXcpt, self.oXcptClass)
1155
1156 def xcptIsEqual(self, oXcpt, hrStatus):
1157 """
1158 Checks if the exception oXcpt is equal to the COM/XPCOM status code
1159 hrStatus.
1160
1161 The oXcpt parameter can be any kind of object, we'll just return True
1162 if it doesn't behave like a our exception class. If it's None, we'll
1163 query the current exception and examine that.
1164
1165 Will not raise any exception as long as hrStatus and self are not bad.
1166 """
1167 if oXcpt is None:
1168 oXcpt = sys.exc_info()[1]
1169 return self.platform.xcptIsEqual(oXcpt, hrStatus)
1170
1171 def xcptIsNotEqual(self, oXcpt, hrStatus):
1172 """
1173 Negated xcptIsEqual.
1174 """
1175 return not self.xcptIsEqual(oXcpt, hrStatus)
1176
1177 def xcptToString(self, hrStatusOrXcpt=None):
1178 """
1179 Converts the specified COM status code, or the status code of the
1180 specified exception, to a C constant string. If the parameter isn't
1181 specified (is None), the current exception is examined.
1182 """
1183
1184 # Deal with exceptions.
1185 if hrStatusOrXcpt is None or self.xcptIsOurXcptKind(hrStatusOrXcpt):
1186 hrStatus = self.xcptGetStatus(hrStatusOrXcpt)
1187 else:
1188 hrStatus = hrStatusOrXcpt
1189
1190 # Build the dictionary on demand.
1191 if self._dErrorValToName is None:
1192 dErrorValToName = dict()
1193 for sKey in dir(self.statuses):
1194 if sKey[0].isupper():
1195 oValue = getattr(self.statuses, sKey)
1196 if type(oValue) is int:
1197 dErrorValToName[oValue] = sKey
1198 self._dErrorValToName = dErrorValToName
1199
1200 # Do the lookup, falling back on formatting the status number.
1201 try:
1202 sStr = self._dErrorValToName[int(hrStatus)]
1203 except KeyError:
1204 hrLong = long(hrStatus)
1205 sStr = '%#x (%d)' % (hrLong, hrLong)
1206 return sStr
1207
1208 def xcptGetMessage(self, oXcpt=None):
1209 """
1210 Returns the best error message found in the COM-like exception. If the
1211 exception parameter isn't specified, the current exception is examined.
1212 """
1213 if oXcpt is None:
1214 oXcpt = sys.exc_info()[1]
1215 sRet = self.platform.xcptGetMessage(oXcpt)
1216 if sRet is None:
1217 sRet = self.xcptToString(oXcpt)
1218 return sRet
1219
1220 # Legacy, remove in a day or two.
1221 errGetStatus = xcptGetStatus
1222 errIsDeadInterface = xcptIsDeadInterface
1223 errIsOurXcptKind = xcptIsOurXcptKind
1224 errGetMessage = xcptGetMessage
1225
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