VirtualBox

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

最後變更 在這個檔案從103029是 103029,由 vboxsync 提交於 14 月 前

Main/Python: SCM fix. bugref:10579

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

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