# -*- coding: utf-8 -*- # $Id: vboxwrappers.py 79245 2019-06-19 15:11:15Z vboxsync $ # pylint: disable=too-many-lines """ VirtualBox Wrapper Classes """ __copyright__ = \ """ Copyright (C) 2010-2019 Oracle Corporation This file is part of VirtualBox Open Source Edition (OSE), as available from http://www.virtualbox.org. This file is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License (GPL) as published by the Free Software Foundation, in version 2 as it comes in the "COPYING" file of the VirtualBox OSE distribution. VirtualBox OSE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. The contents of this file may alternatively be used under the terms of the Common Development and Distribution License Version 1.0 (CDDL) only, as it comes in the "COPYING.CDDL" file of the VirtualBox OSE distribution, in which case the provisions of the CDDL are applicable instead of those of the GPL. You may elect to license modified versions of this file under the terms and conditions of either the GPL or the CDDL or both. """ __version__ = "$Revision: 79245 $" # Standard Python imports. import os; import socket; import sys; # Validation Kit imports. from common import utils; from common import netutils; from testdriver import base; from testdriver import reporter; from testdriver import txsclient; from testdriver import vboxcon; from testdriver import vbox; from testdriver.base import TdTaskBase; def _ControllerNameToBusAndType(sController): """ Translate a controller name to a storage bus. """ if sController == "IDE Controller": eBus = vboxcon.StorageBus_IDE; eType = vboxcon.StorageControllerType_PIIX4; elif sController == "SATA Controller": eBus = vboxcon.StorageBus_SATA; eType = vboxcon.StorageControllerType_IntelAhci; elif sController == "Floppy Controller": eType = vboxcon.StorageControllerType_I82078; eBus = vboxcon.StorageBus_Floppy; elif sController == "SAS Controller": eBus = vboxcon.StorageBus_SAS; eType = vboxcon.StorageControllerType_LsiLogicSas; elif sController == "SCSI Controller": eBus = vboxcon.StorageBus_SCSI; eType = vboxcon.StorageControllerType_LsiLogic; elif sController == "BusLogic SCSI Controller": eBus = vboxcon.StorageBus_SCSI; eType = vboxcon.StorageControllerType_BusLogic; elif sController == "NVMe Controller": eBus = vboxcon.StorageBus_PCIe; eType = vboxcon.StorageControllerType_NVMe; else: eBus = vboxcon.StorageBus_Null; eType = vboxcon.StorageControllerType_Null; return (eBus, eType); def _nameMachineState(eState): """ Gets the name (string) of a machine state.""" if eState == vboxcon.MachineState_PoweredOff: return 'PoweredOff'; if eState == vboxcon.MachineState_Saved: return 'Saved'; if eState == vboxcon.MachineState_Teleported: return 'Teleported'; if eState == vboxcon.MachineState_Aborted: return 'Aborted'; if eState == vboxcon.MachineState_Running: return 'Running'; if eState == vboxcon.MachineState_Paused: return 'Paused'; if eState == vboxcon.MachineState_Stuck: return 'GuruMeditation'; if eState == vboxcon.MachineState_Teleporting: return 'Teleporting'; if eState == vboxcon.MachineState_LiveSnapshotting: return 'LiveSnapshotting'; if eState == vboxcon.MachineState_Starting: return 'Starting'; if eState == vboxcon.MachineState_Stopping: return 'Stopping'; if eState == vboxcon.MachineState_Saving: return 'Saving'; if eState == vboxcon.MachineState_Restoring: return 'Restoring'; if eState == vboxcon.MachineState_TeleportingPausedVM: return 'TeleportingPausedVM'; if eState == vboxcon.MachineState_TeleportingIn: return 'TeleportingIn'; if eState == vboxcon.MachineState_FaultTolerantSyncing: return 'FaultTolerantSyncing'; if eState == vboxcon.MachineState_DeletingSnapshotOnline: return 'DeletingSnapshotOnline'; if eState == vboxcon.MachineState_DeletingSnapshotPaused: return 'DeletingSnapshotPaused'; if eState == vboxcon.MachineState_RestoringSnapshot: return 'RestoringSnapshot'; if eState == vboxcon.MachineState_DeletingSnapshot: return 'DeletingSnapshot'; if eState == vboxcon.MachineState_SettingUp: return 'SettingUp'; return 'Unknown-%s' % (eState,); class VirtualBoxWrapper(object): # pylint: disable=too-few-public-methods """ Wrapper around the IVirtualBox object that adds some (hopefully) useful utility methods The real object can be accessed thru the o member. That said, members can be accessed directly as well. """ def __init__(self, oVBox, oVBoxMgr, fpApiVer, oTstDrv): self.o = oVBox; self.oVBoxMgr = oVBoxMgr; self.fpApiVer = fpApiVer; self.oTstDrv = oTstDrv; def __getattr__(self, sName): # Try ourselves first. try: oAttr = self.__dict__[sName]; except: #try: # oAttr = dir(self)[sName]; #except AttributeError: oAttr = getattr(self.o, sName); return oAttr; # # Utilities. # def registerDerivedEventHandler(self, oSubClass, dArgs = None): """ Create an instance of the given VirtualBoxEventHandlerBase sub-class and register it. The new instance is returned on success. None is returned on error. """ dArgsCopy = dArgs.copy() if dArgs is not None else dict(); dArgsCopy['oVBox'] = self; return oSubClass.registerDerivedEventHandler(self.oVBoxMgr, self.fpApiVer, oSubClass, dArgsCopy, self.o, 'IVirtualBox', 'IVirtualBoxCallback'); def deleteHdByLocation(self, sHdLocation): """ Deletes a disk image from the host, given it's location. Returns True on success and False on failure. Error information is logged. """ try: oIMedium = self.o.findHardDisk(sHdLocation); except: try: if self.fpApiVer >= 4.1: oIMedium = self.o.openMedium(sHdLocation, vboxcon.DeviceType_HardDisk, vboxcon.AccessMode_ReadWrite, False); elif self.fpApiVer >= 4.0: oIMedium = self.o.openMedium(sHdLocation, vboxcon.DeviceType_HardDisk, vboxcon.AccessMode_ReadWrite); else: oIMedium = self.o.openHardDisk(sHdLocation, vboxcon.AccessMode_ReadOnly, False, "", False, ""); except: return reporter.errorXcpt('failed to open hd "%s"' % (sHdLocation)); return self.deleteHdByMedium(oIMedium) def deleteHdByMedium(self, oIMedium): """ Deletes a disk image from the host, given an IMedium reference. Returns True on success and False on failure. Error information is logged. """ try: oProgressCom = oIMedium.deleteStorage(); except: return reporter.errorXcpt('deleteStorage() for disk %s failed' % (oIMedium,)); try: oProgress = ProgressWrapper(oProgressCom, self.oVBoxMgr, self.oTstDrv, 'delete disk %s' % (oIMedium.location)); except: return reporter.errorXcpt(); oProgress.wait(); oProgress.logResult(); return oProgress.isSuccess(); class ProgressWrapper(TdTaskBase): """ Wrapper around a progress object for making it a task and providing useful utility methods. The real progress object can be accessed thru the o member. """ def __init__(self, oProgress, oVBoxMgr, oTstDrv, sName): TdTaskBase.__init__(self, utils.getCallerName()); self.o = oProgress; self.oVBoxMgr = oVBoxMgr; self.oTstDrv = oTstDrv; self.sName = sName; def toString(self): return '<%s sName=%s, oProgress=%s >' \ % (TdTaskBase.toString(self), self.sName, self.o); # # TdTaskBase overrides. # def pollTask(self, fLocked = False): """ Overrides TdTaskBase.pollTask(). This method returns False until the progress object has completed. """ self.doQuickApiTest(); try: try: if self.o.completed: return True; except: pass; finally: self.oTstDrv.processPendingEvents(); return False; def waitForTask(self, cMsTimeout = 0): """ Overrides TdTaskBase.waitForTask(). Process XPCOM/COM events while waiting. """ msStart = base.timestampMilli(); fState = self.pollTask(False); while not fState: cMsElapsed = base.timestampMilli() - msStart; if cMsElapsed > cMsTimeout: break; cMsToWait = cMsTimeout - cMsElapsed; if cMsToWait > 500: cMsToWait = 500; try: self.o.waitForCompletion(cMsToWait); except KeyboardInterrupt: raise; except: pass; if self.fnProcessEvents: self.fnProcessEvents(); reporter.doPollWork('ProgressWrapper.waitForTask'); fState = self.pollTask(False); return fState; # # Utility methods. # def isSuccess(self): """ Tests if the progress object completed successfully. Returns True on success, False on failure or incomplete. """ if not self.isCompleted(): return False; return self.getResult() >= 0; def isCompleted(self): """ Wrapper around IProgress.completed. """ return self.pollTask(); def isCancelable(self): """ Wrapper around IProgress.cancelable. """ try: fRc = self.o.cancelable; except: reporter.logXcpt(); fRc = False; return fRc; def wasCanceled(self): """ Wrapper around IProgress.canceled. """ try: fRc = self.o.canceled; except: reporter.logXcpt(self.sName); fRc = False; return fRc; def cancel(self): """ Wrapper around IProgress.cancel() Returns True on success, False on failure (logged as error). """ try: self.o.cancel(); except: reporter.errorXcpt(self.sName); return False; return True; def getResult(self): """ Wrapper around IProgress.resultCode. """ try: iRc = self.o.resultCode; except: reporter.logXcpt(self.sName); iRc = -1; return iRc; def getErrInfoResultCode(self): """ Wrapper around IProgress.errorInfo.resultCode. Returns the string on success, -1 on bad objects (logged as error), and -2 on missing errorInfo object. """ iRc = -1; try: oErrInfo = self.o.errorInfo; except: reporter.errorXcpt(self.sName); else: if oErrInfo is None: iRc = -2; else: try: iRc = oErrInfo.resultCode; except: reporter.errorXcpt(); return iRc; def getErrInfoText(self): """ Wrapper around IProgress.errorInfo.text. Returns the string on success, None on failure. Missing errorInfo is not logged as an error, all other failures are. """ sText = None; try: oErrInfo = self.o.errorInfo; except: reporter.log2Xcpt(self.sName); else: if oErrInfo is not None: try: sText = oErrInfo.text; except: reporter.errorXcpt(); return sText; def stringifyErrorInfo(self): """ Formats IProgress.errorInfo into a string. """ try: oErrInfo = self.o.errorInfo; except: reporter.logXcpt(self.sName); sErr = 'no error info'; else: sErr = vbox.stringifyErrorInfo(oErrInfo); return sErr; def stringifyResult(self): """ Stringify the result. """ if self.isCompleted(): if self.wasCanceled(): sRet = 'Progress %s: Canceled, hrc=%s' % (self.sName, vbox.ComError.toString(self.getResult())); elif self.getResult() == 0: sRet = 'Progress %s: Success' % (self.sName,); elif self.getResult() > 0: sRet = 'Progress %s: Success (hrc=%s)' % (self.sName, vbox.ComError.toString(self.getResult())); else: sRet = 'Progress %s: Failed! %s' % (self.sName, self.stringifyErrorInfo()); else: sRet = 'Progress %s: Not completed yet...' % (self.sName); return sRet; def logResult(self, fIgnoreErrors = False): """ Logs the result, failure logged as error unless fIgnoreErrors is True. Return True on success, False on failure (and fIgnoreErrors is false). """ sText = self.stringifyResult(); if self.isCompleted() and self.getResult() < 0 and fIgnoreErrors is False: return reporter.error(sText); reporter.log(sText); return True; def waitOnProgress(self, cMsInterval = 1000): """ See vbox.TestDriver.waitOnProgress. """ self.doQuickApiTest(); return self.oTstDrv.waitOnProgress(self.o, cMsInterval); def wait(self, cMsTimeout = 60000, fErrorOnTimeout = True, cMsInterval = 1000): """ Wait on the progress object for a while. Returns the resultCode of the progress object if completed. Returns -1 on timeout, logged as error if fErrorOnTimeout is set. Returns -2 is the progress object is invalid or waitForCompletion fails (logged as errors). """ msStart = base.timestampMilli(); while True: self.oTstDrv.processPendingEvents(); self.doQuickApiTest(); try: if self.o.completed: break; except: reporter.errorXcpt(self.sName); return -2; self.oTstDrv.processPendingEvents(); cMsElapsed = base.timestampMilli() - msStart; if cMsElapsed > cMsTimeout: if fErrorOnTimeout: reporter.error('Timing out after waiting for %u s on "%s"' % (cMsTimeout / 1000, self.sName)) return -1; try: self.o.waitForCompletion(cMsInterval); except: reporter.errorXcpt(self.sName); return -2; reporter.doPollWork('ProgressWrapper.wait'); try: rc = self.o.resultCode; except: rc = -2; reporter.errorXcpt(self.sName); self.oTstDrv.processPendingEvents(); return rc; def waitForOperation(self, iOperation, cMsTimeout = 60000, fErrorOnTimeout = True, cMsInterval = 1000, \ fIgnoreErrors = False): """ Wait for the completion of a operation. Negative iOperation values are relative to operationCount (this property may changed at runtime). Returns 0 if the operation completed normally. Returns -1 on timeout, logged as error if fErrorOnTimeout is set. Returns -2 is the progress object is invalid or waitForCompletion fails (logged as errors). Returns -3 if if the operation completed with an error, this is logged as an error. """ msStart = base.timestampMilli(); while True: self.oTstDrv.processPendingEvents(); self.doQuickApiTest(); try: iCurrentOperation = self.o.operation; cOperations = self.o.operationCount; if iOperation >= 0: iRealOperation = iOperation; else: iRealOperation = cOperations + iOperation; if iCurrentOperation > iRealOperation: return 0; if iCurrentOperation == iRealOperation \ and iRealOperation >= cOperations - 1 \ and self.o.completed: if self.o.resultCode < 0: self.logResult(fIgnoreErrors); return -3; return 0; except: if fIgnoreErrors: reporter.logXcpt(); else: reporter.errorXcpt(); return -2; self.oTstDrv.processPendingEvents(); cMsElapsed = base.timestampMilli() - msStart; if cMsElapsed > cMsTimeout: if fErrorOnTimeout: if fIgnoreErrors: reporter.log('Timing out after waiting for %s s on "%s" operation %d' \ % (cMsTimeout / 1000, self.sName, iOperation)) else: reporter.error('Timing out after waiting for %s s on "%s" operation %d' \ % (cMsTimeout / 1000, self.sName, iOperation)) return -1; try: self.o.waitForOperationCompletion(iRealOperation, cMsInterval); except: if fIgnoreErrors: reporter.logXcpt(self.sName); else: reporter.errorXcpt(self.sName); return -2; reporter.doPollWork('ProgressWrapper.waitForOperation'); # Not reached. return -3; # Make pylin happy (for now). def doQuickApiTest(self): """ Queries everything that is stable and easy to get at and checks that they don't throw errors. """ if True is True: # pylint: disable=comparison-with-itself try: iPct = self.o.operationPercent; sDesc = self.o.description; fCancelable = self.o.cancelable; cSecsRemain = self.o.timeRemaining; fCanceled = self.o.canceled; fCompleted = self.o.completed; iOp = self.o.operation; cOps = self.o.operationCount; iOpPct = self.o.operationPercent; sOpDesc = self.o.operationDescription; except: reporter.errorXcpt('%s: %s' % (self.sName, self.o,)); return False; try: # Very noisy -- only enable for debugging purposes. #reporter.log2('%s: op=%u/%u/%s: %u%%; total=%u%% cancel=%s/%s compl=%s rem=%us; desc=%s' \ # % (self.sName, iOp, cOps, sOpDesc, iOpPct, iPct, fCanceled, fCancelable, fCompleted, \ # cSecsRemain, sDesc)); _ = iPct; _ = sDesc; _ = fCancelable; _ = cSecsRemain; _ = fCanceled; _ = fCompleted; _ = iOp; _ = cOps; _ = iOpPct; _ = sOpDesc; except: reporter.errorXcpt(); return False; return True; class SessionWrapper(TdTaskBase): """ Wrapper around a machine session. The real session object can be accessed thru the o member (short is good, right :-). """ def __init__(self, oSession, oVM, oVBox, oVBoxMgr, oTstDrv, fRemoteSession, sFallbackName = None, sLogFile = None): """ Initializes the session wrapper. """ TdTaskBase.__init__(self, utils.getCallerName()); self.o = oSession; self.oVBox = oVBox; self.oVBoxMgr = oVBoxMgr; self.oVM = oVM; # Not the session machine. Useful backdoor... self.oTstDrv = oTstDrv; self.fpApiVer = oTstDrv.fpApiVer; self.fRemoteSession = fRemoteSession; self.sLogFile = sLogFile; self.oConsoleEventHandler = None; self.uPid = None; self.fPidFile = True; self.fHostMemoryLow = False; # see signalHostMemoryLow; read-only for outsiders. try: self.sName = oSession.machine.name; except: if sFallbackName is not None: self.sName = sFallbackName; else: try: self.sName = str(oSession.machine); except: self.sName = 'is-this-vm-already-off' try: self.sUuid = oSession.machine.id; except: self.sUuid = None; # Try cache the SessionPID. self.getPid(); def __del__(self): """ Destructor that makes sure the callbacks are deregistered and that the session is closed. """ self.deregisterEventHandlerForTask(); if self.o is not None: try: self.close(); reporter.log('close session %s' % (self.o)); except: pass; self.o = None; TdTaskBase.__del__(self); def toString(self): return '<%s: sUuid=%s, sName=%s, uPid=%s, sDbgCreated=%s, fRemoteSession=%s, oSession=%s,' \ ' oConsoleEventHandler=%s, oVM=%s >' \ % (type(self).__name__, self.sUuid, self.sName, self.uPid, self.sDbgCreated, self.fRemoteSession, self.o, self.oConsoleEventHandler, self.oVM,); def __str__(self): return self.toString(); # # TdTaskBase overrides. # def __pollTask(self): """ Internal poller """ # Poll for events after doing the remote GetState call, otherwise we # might end up sleepless because XPCOM queues a cleanup event. try: try: eState = self.o.machine.state; except Exception as oXcpt: if vbox.ComError.notEqual(oXcpt, vbox.ComError.E_UNEXPECTED): reporter.logXcpt(); return True; finally: self.oTstDrv.processPendingEvents(); # Switch if eState == vboxcon.MachineState_Running: return False; if eState == vboxcon.MachineState_Paused: return False; if eState == vboxcon.MachineState_Teleporting: return False; if eState == vboxcon.MachineState_LiveSnapshotting: return False; if eState == vboxcon.MachineState_Starting: return False; if eState == vboxcon.MachineState_Stopping: return False; if eState == vboxcon.MachineState_Saving: return False; if eState == vboxcon.MachineState_Restoring: return False; if eState == vboxcon.MachineState_TeleportingPausedVM: return False; if eState == vboxcon.MachineState_TeleportingIn: return False; # *Beeep* fudge! if self.fpApiVer < 3.2 \ and eState == vboxcon.MachineState_PoweredOff \ and self.getAgeAsMs() < 3000: return False; reporter.log('SessionWrapper::pollTask: eState=%s' % (eState)); return True; def pollTask(self, fLocked = False): """ Overrides TdTaskBase.pollTask(). This method returns False while the VM is online and running normally. """ # Call super to check if the task was signalled by runtime error or similar, # if not then check the VM state via __pollTask. fRc = super(SessionWrapper, self).pollTask(fLocked); if not fRc: fRc = self.__pollTask(); # HACK ALERT: Lazily try registering the console event handler if # we're not ready. if not fRc and self.oConsoleEventHandler is None: self.registerEventHandlerForTask(); # HACK ALERT: Lazily try get the PID and add it to the PID file. if not fRc and self.uPid is None: self.getPid(); return fRc; def waitForTask(self, cMsTimeout = 0): """ Overrides TdTaskBase.waitForTask(). Process XPCOM/COM events while waiting. """ msStart = base.timestampMilli(); fState = self.pollTask(False); while not fState: cMsElapsed = base.timestampMilli() - msStart; if cMsElapsed > cMsTimeout: break; cMsSleep = cMsTimeout - cMsElapsed; if cMsSleep > 10000: cMsSleep = 10000; try: self.oVBoxMgr.waitForEvents(cMsSleep); except KeyboardInterrupt: raise; except: pass; if self.fnProcessEvents: self.fnProcessEvents(); reporter.doPollWork('SessionWrapper.waitForTask'); fState = self.pollTask(False); return fState; def setTaskOwner(self, oOwner): """ HACK ALERT! Overrides TdTaskBase.setTaskOwner() so we can try call registerEventHandlerForTask() again when when the testdriver calls addTask() after VM has been spawned. Related to pollTask() above. The testdriver must not add the task too early for this to work! """ if oOwner is not None: self.registerEventHandlerForTask() return TdTaskBase.setTaskOwner(self, oOwner); # # Task helpers. # def registerEventHandlerForTask(self): """ Registers the console event handlers for working the task state. """ if self.oConsoleEventHandler is not None: return True; self.oConsoleEventHandler = self.registerDerivedEventHandler(vbox.SessionConsoleEventHandler, {}, False); return self.oConsoleEventHandler is not None; def deregisterEventHandlerForTask(self): """ Deregisters the console event handlers. """ if self.oConsoleEventHandler is not None: self.oConsoleEventHandler.unregister(); self.oConsoleEventHandler = None; def signalHostMemoryLow(self): """ Used by a runtime error event handler to indicate that we're low on memory. Signals the task. """ self.fHostMemoryLow = True; self.signalTask(); return True; def needsPoweringOff(self): """ Examins the machine state to see if the VM needs powering off. """ try: try: eState = self.o.machine.state; except Exception as oXcpt: if vbox.ComError.notEqual(oXcpt, vbox.ComError.E_UNEXPECTED): reporter.logXcpt(); return False; finally: self.oTstDrv.processPendingEvents(); # Switch if eState == vboxcon.MachineState_Running: return True; if eState == vboxcon.MachineState_Paused: return True; if eState == vboxcon.MachineState_Stuck: return True; if eState == vboxcon.MachineState_Teleporting: return True; if eState == vboxcon.MachineState_LiveSnapshotting: return True; if eState == vboxcon.MachineState_Starting: return True; if eState == vboxcon.MachineState_Saving: return True; if eState == vboxcon.MachineState_Restoring: return True; if eState == vboxcon.MachineState_TeleportingPausedVM: return True; if eState == vboxcon.MachineState_TeleportingIn: return True; if eState == vboxcon.MachineState_FaultTolerantSyncing: return True; return False; def assertPoweredOff(self): """ Asserts that the VM is powered off, reporting an error if not. Returns True if powered off, False + error msg if not. """ try: try: eState = self.oVM.state; except Exception: reporter.errorXcpt(); return True; finally: self.oTstDrv.processPendingEvents(); if eState == vboxcon.MachineState_PoweredOff: return True; reporter.error('Expected machine state "PoweredOff", machine is in the "%s" state instead.' % (_nameMachineState(eState),)); return False; def getMachineStateWithName(self): """ Gets the current machine state both as a constant number/whatever and as a human readable string. On error, the constants will be set to None and the string will be the error message. """ try: eState = self.oVM.state; except: return (None, '[error getting state: %s]' % (self.oVBoxMgr.xcptToString(),)); finally: self.oTstDrv.processPendingEvents(); return (eState, _nameMachineState(eState)); def reportPrematureTermination(self, sPrefix = ''): """ Reports a premature virtual machine termination. Returns False to facilitate simpler error paths. """ reporter.error(sPrefix + 'The virtual machine terminated prematurely!!'); (enmState, sStateNm) = self.getMachineStateWithName(); reporter.error(sPrefix + 'Machine state: %s' % (sStateNm,)); if enmState is not None \ and enmState == vboxcon.MachineState_Aborted \ and self.uPid is not None: # # Look for process crash info. # def addCrashFile(sLogFile, fBinary): """ processCollectCrashInfo callback. """ reporter.addLogFile(sLogFile, 'crash/dump/vm' if fBinary else 'crash/report/vm'); utils.processCollectCrashInfo(self.uPid, reporter.log, addCrashFile); return False; # # ISession / IMachine / ISomethingOrAnother wrappers. # def close(self): """ Closes the session if it's open and removes it from the vbox.TestDriver.aoRemoteSessions list. Returns success indicator. """ fRc = True; if self.o is not None: # Get the pid in case we need to kill the process later on. self.getPid(); # Try close it. try: if self.fpApiVer < 3.3: self.o.close(); else: self.o.unlockMachine(); self.o = None; except KeyboardInterrupt: raise; except: # Kludge to ignore VBoxSVC's closing of our session when the # direct session closes / VM process terminates. Fun! try: fIgnore = self.o.state == vboxcon.SessionState_Unlocked; except: fIgnore = False; if not fIgnore: reporter.errorXcpt('ISession::unlockMachine failed on %s' % (self.o)); fRc = False; # Remove it from the remote session list if applicable (not 100% clean). if fRc and self.fRemoteSession: try: if self in self.oTstDrv.aoRemoteSessions: reporter.log2('SessionWrapper::close: Removing myself from oTstDrv.aoRemoteSessions'); self.oTstDrv.aoRemoteSessions.remove(self) except: reporter.logXcpt(); if self.uPid is not None and self.fPidFile: self.oTstDrv.pidFileRemove(self.uPid); self.fPidFile = False; # It's only logical to deregister the event handler after the session # is closed. It also avoids circular references between the session # and the listener, which causes trouble with garbage collection. self.deregisterEventHandlerForTask(); self.oTstDrv.processPendingEvents(); return fRc; def saveSettings(self, fClose = False): """ Saves the settings and optionally closes the session. Returns success indicator. """ try: try: self.o.machine.saveSettings(); except: reporter.errorXcpt('saveSettings failed on %s' % (self.o)); return False; finally: self.oTstDrv.processPendingEvents(); if fClose: return self.close(); return True; def discardSettings(self, fClose = False): """ Discards the settings and optionally closes the session. """ try: try: self.o.machine.discardSettings(); except: reporter.errorXcpt('discardSettings failed on %s' % (self.o)); return False; finally: self.oTstDrv.processPendingEvents(); if fClose: return self.close(); return True; def enableVirtEx(self, fEnable): """ Enables or disables AMD-V/VT-x. Returns True on success and False on failure. Error information is logged. """ # Enable/disable it. fRc = True; try: self.o.machine.setHWVirtExProperty(vboxcon.HWVirtExPropertyType_Enabled, fEnable); except: reporter.errorXcpt('failed to set HWVirtExPropertyType_Enabled=%s for "%s"' % (fEnable, self.sName)); fRc = False; else: reporter.log('set HWVirtExPropertyType_Enabled=%s for "%s"' % (fEnable, self.sName)); # Force/unforce it. if fRc and hasattr(vboxcon, 'HWVirtExPropertyType_Force'): try: self.o.machine.setHWVirtExProperty(vboxcon.HWVirtExPropertyType_Force, fEnable); except: reporter.errorXcpt('failed to set HWVirtExPropertyType_Force=%s for "%s"' % (fEnable, self.sName)); fRc = False; else: reporter.log('set HWVirtExPropertyType_Force=%s for "%s"' % (fEnable, self.sName)); else: reporter.log('Warning! vboxcon has no HWVirtExPropertyType_Force attribute.'); ## @todo Modify CFGM to do the same for old VBox versions? self.oTstDrv.processPendingEvents(); return fRc; def enableNestedPaging(self, fEnable): """ Enables or disables nested paging.. Returns True on success and False on failure. Error information is logged. """ ## @todo Add/remove force CFGM thing, we don't want fallback logic when testing. fRc = True; try: self.o.machine.setHWVirtExProperty(vboxcon.HWVirtExPropertyType_NestedPaging, fEnable); except: reporter.errorXcpt('failed to set HWVirtExPropertyType_NestedPaging=%s for "%s"' % (fEnable, self.sName)); fRc = False; else: reporter.log('set HWVirtExPropertyType_NestedPaging=%s for "%s"' % (fEnable, self.sName)); self.oTstDrv.processPendingEvents(); return fRc; def enableLongMode(self, fEnable): """ Enables or disables LongMode. Returns True on success and False on failure. Error information is logged. """ # Supported. if self.fpApiVer < 4.2 or not hasattr(vboxcon, 'HWVirtExPropertyType_LongMode'): return True; # Enable/disable it. fRc = True; try: self.o.machine.setCPUProperty(vboxcon.CPUPropertyType_LongMode, fEnable); except: reporter.errorXcpt('failed to set CPUPropertyType_LongMode=%s for "%s"' % (fEnable, self.sName)); fRc = False; else: reporter.log('set CPUPropertyType_LongMode=%s for "%s"' % (fEnable, self.sName)); self.oTstDrv.processPendingEvents(); return fRc; def enableNestedHwVirt(self, fEnable): """ Enables or disables Nested Hardware-Virtualization. Returns True on success and False on failure. Error information is logged. """ # Supported. if self.fpApiVer < 5.3 or not hasattr(vboxcon, 'CPUPropertyType_HWVirt'): return True; # Enable/disable it. fRc = True; try: self.o.machine.setCPUProperty(vboxcon.CPUPropertyType_HWVirt, fEnable); except: reporter.errorXcpt('failed to set CPUPropertyType_HWVirt=%s for "%s"' % (fEnable, self.sName)); fRc = False; else: reporter.log('set CPUPropertyType_HWVirt=%s for "%s"' % (fEnable, self.sName)); self.oTstDrv.processPendingEvents(); return fRc; def enablePae(self, fEnable): """ Enables or disables PAE Returns True on success and False on failure. Error information is logged. """ fRc = True; try: if self.fpApiVer >= 3.2: # great, ain't it? self.o.machine.setCPUProperty(vboxcon.CPUPropertyType_PAE, fEnable); else: self.o.machine.setCpuProperty(vboxcon.CpuPropertyType_PAE, fEnable); except: reporter.errorXcpt('failed to set CPUPropertyType_PAE=%s for "%s"' % (fEnable, self.sName)); fRc = False; else: reporter.log('set CPUPropertyType_PAE=%s for "%s"' % (fEnable, self.sName)); self.oTstDrv.processPendingEvents(); return fRc; def enableIoApic(self, fEnable): """ Enables or disables the IO-APIC Returns True on success and False on failure. Error information is logged. """ fRc = True; try: self.o.machine.BIOSSettings.IOAPICEnabled = fEnable; except: reporter.errorXcpt('failed to set BIOSSettings.IOAPICEnabled=%s for "%s"' % (fEnable, self.sName)); fRc = False; else: reporter.log('set BIOSSettings.IOAPICEnabled=%s for "%s"' % (fEnable, self.sName)); self.oTstDrv.processPendingEvents(); return fRc; def enableHpet(self, fEnable): """ Enables or disables the HPET Returns True on success and False on failure. Error information is logged. """ fRc = True; try: if self.fpApiVer >= 4.2: self.o.machine.HPETEnabled = fEnable; else: self.o.machine.hpetEnabled = fEnable; except: reporter.errorXcpt('failed to set HpetEnabled=%s for "%s"' % (fEnable, self.sName)); fRc = False; else: reporter.log('set HpetEnabled=%s for "%s"' % (fEnable, self.sName)); self.oTstDrv.processPendingEvents(); return fRc; def enableUsbHid(self, fEnable): """ Enables or disables the USB HID Returns True on success and False on failure. Error information is logged. """ fRc = True; try: if fEnable: if self.fpApiVer >= 4.3: cOhciCtls = self.o.machine.getUSBControllerCountByType(vboxcon.USBControllerType_OHCI); if cOhciCtls == 0: self.o.machine.addUSBController('OHCI', vboxcon.USBControllerType_OHCI); else: self.o.machine.usbController.enabled = True; if self.fpApiVer >= 4.2: self.o.machine.pointingHIDType = vboxcon.PointingHIDType_ComboMouse; self.o.machine.keyboardHIDType = vboxcon.KeyboardHIDType_ComboKeyboard; else: self.o.machine.pointingHidType = vboxcon.PointingHidType_ComboMouse; self.o.machine.keyboardHidType = vboxcon.KeyboardHidType_ComboKeyboard; else: if self.fpApiVer >= 4.2: self.o.machine.pointingHIDType = vboxcon.PointingHIDType_PS2Mouse; self.o.machine.keyboardHIDType = vboxcon.KeyboardHIDType_PS2Keyboard; else: self.o.machine.pointingHidType = vboxcon.PointingHidType_PS2Mouse; self.o.machine.keyboardHidType = vboxcon.KeyboardHidType_PS2Keyboard; except: reporter.errorXcpt('failed to change UsbHid to %s for "%s"' % (fEnable, self.sName)); fRc = False; else: reporter.log('changed UsbHid to %s for "%s"' % (fEnable, self.sName)); self.oTstDrv.processPendingEvents(); return fRc; def enableUsbOhci(self, fEnable): """ Enables or disables the USB OHCI controller Returns True on success and False on failure. Error information is logged. """ fRc = True; try: if fEnable: if self.fpApiVer >= 4.3: cOhciCtls = self.o.machine.getUSBControllerCountByType(vboxcon.USBControllerType_OHCI); if cOhciCtls == 0: self.o.machine.addUSBController('OHCI', vboxcon.USBControllerType_OHCI); else: self.o.machine.usbController.enabled = True; else: if self.fpApiVer >= 4.3: cOhciCtls = self.o.machine.getUSBControllerCountByType(vboxcon.USBControllerType_OHCI); if cOhciCtls == 1: self.o.machine.removeUSBController('OHCI'); else: self.o.machine.usbController.enabled = False; except: reporter.errorXcpt('failed to change OHCI to %s for "%s"' % (fEnable, self.sName)); fRc = False; else: reporter.log('changed OHCI to %s for "%s"' % (fEnable, self.sName)); self.oTstDrv.processPendingEvents(); return fRc; def enableUsbEhci(self, fEnable): """ Enables or disables the USB EHCI controller, enables also OHCI if it is still disabled. Returns True on success and False on failure. Error information is logged. """ fRc = True; try: if fEnable: if self.fpApiVer >= 4.3: cOhciCtls = self.o.machine.getUSBControllerCountByType(vboxcon.USBControllerType_OHCI); if cOhciCtls == 0: self.o.machine.addUSBController('OHCI', vboxcon.USBControllerType_OHCI); cEhciCtls = self.o.machine.getUSBControllerCountByType(vboxcon.USBControllerType_EHCI); if cEhciCtls == 0: self.o.machine.addUSBController('EHCI', vboxcon.USBControllerType_EHCI); else: self.o.machine.usbController.enabled = True; self.o.machine.usbController.enabledEHCI = True; else: if self.fpApiVer >= 4.3: cEhciCtls = self.o.machine.getUSBControllerCountByType(vboxcon.USBControllerType_EHCI); if cEhciCtls == 1: self.o.machine.removeUSBController('EHCI'); else: self.o.machine.usbController.enabledEHCI = False; except: reporter.errorXcpt('failed to change EHCI to %s for "%s"' % (fEnable, self.sName)); fRc = False; else: reporter.log('changed EHCI to %s for "%s"' % (fEnable, self.sName)); self.oTstDrv.processPendingEvents(); return fRc; def enableUsbXhci(self, fEnable): """ Enables or disables the USB XHCI controller. Error information is logged. """ fRc = True; try: if fEnable: cXhciCtls = self.o.machine.getUSBControllerCountByType(vboxcon.USBControllerType_XHCI); if cXhciCtls == 0: self.o.machine.addUSBController('XHCI', vboxcon.USBControllerType_XHCI); else: cXhciCtls = self.o.machine.getUSBControllerCountByType(vboxcon.USBControllerType_XHCI); if cXhciCtls == 1: self.o.machine.removeUSBController('XHCI'); except: reporter.errorXcpt('failed to change XHCI to %s for "%s"' % (fEnable, self.sName)); fRc = False; else: reporter.log('changed XHCI to %s for "%s"' % (fEnable, self.sName)); self.oTstDrv.processPendingEvents(); return fRc; def setFirmwareType(self, eType): """ Sets the firmware type. Returns True on success and False on failure. Error information is logged. """ fRc = True; try: self.o.machine.firmwareType = eType; except: reporter.errorXcpt('failed to set firmwareType=%s for "%s"' % (eType, self.sName)); fRc = False; else: reporter.log('set firmwareType=%s for "%s"' % (eType, self.sName)); self.oTstDrv.processPendingEvents(); return fRc; def setChipsetType(self, eType): """ Sets the chipset type. Returns True on success and False on failure. Error information is logged. """ fRc = True; try: self.o.machine.chipsetType = eType; except: reporter.errorXcpt('failed to set chipsetType=%s for "%s"' % (eType, self.sName)); fRc = False; else: reporter.log('set chipsetType=%s for "%s"' % (eType, self.sName)); self.oTstDrv.processPendingEvents(); return fRc; def setupBootLogo(self, fEnable, cMsLogoDisplay = 0): """ Sets up the boot logo. fEnable toggles the fade and boot menu settings as well as the mode. """ fRc = True; try: self.o.machine.BIOSSettings.logoFadeIn = not fEnable; self.o.machine.BIOSSettings.logoFadeOut = not fEnable; self.o.machine.BIOSSettings.logoDisplayTime = cMsLogoDisplay; if fEnable: self.o.machine.BIOSSettings.bootMenuMode = vboxcon.BIOSBootMenuMode_Disabled; else: self.o.machine.BIOSSettings.bootMenuMode = vboxcon.BIOSBootMenuMode_MessageAndMenu; except: reporter.errorXcpt('failed to set logoFadeIn/logoFadeOut/bootMenuMode=%s for "%s"' % (fEnable, self.sName)); fRc = False; else: reporter.log('set logoFadeIn/logoFadeOut/bootMenuMode=%s for "%s"' % (fEnable, self.sName)); self.oTstDrv.processPendingEvents(); return fRc; def setupVrdp(self, fEnable, uPort = None): """ Configures VRDP. """ fRc = True; try: if self.fpApiVer >= 4.0: self.o.machine.VRDEServer.enabled = fEnable; else: self.o.machine.VRDPServer.enabled = fEnable; except: reporter.errorXcpt('failed to set VRDEServer::enabled=%s for "%s"' % (fEnable, self.sName)); fRc = False; if uPort is not None and fRc: try: if self.fpApiVer >= 4.0: self.o.machine.VRDEServer.setVRDEProperty("TCP/Ports", str(uPort)); else: self.o.machine.VRDPServer.ports = str(uPort); except: reporter.errorXcpt('failed to set VRDEServer::ports=%s for "%s"' % (uPort, self.sName)); fRc = False; if fRc: reporter.log('set VRDEServer.enabled/ports=%s/%s for "%s"' % (fEnable, uPort, self.sName)); self.oTstDrv.processPendingEvents(); return fRc; def getNicDriverNameFromType(self, eNicType): """ Helper that translate the adapter type into a driver name. """ if eNicType in (vboxcon.NetworkAdapterType_Am79C970A, vboxcon.NetworkAdapterType_Am79C973): sName = 'pcnet'; elif eNicType in (vboxcon.NetworkAdapterType_I82540EM, vboxcon.NetworkAdapterType_I82543GC, vboxcon.NetworkAdapterType_I82545EM): sName = 'e1000'; elif eNicType == vboxcon.NetworkAdapterType_Virtio: sName = 'virtio-net'; else: reporter.error('Unknown adapter type "%s" (VM: "%s")' % (eNicType, self.sName)); sName = 'pcnet'; return sName; def setupNatForwardingForTxs(self, iNic = 0, iHostPort = 5042): """ Sets up NAT forwarding for port 5042 if applicable, cleans up if not. """ try: oNic = self.o.machine.getNetworkAdapter(iNic); except: reporter.errorXcpt('getNetworkAdapter(%s) failed for "%s"' % (iNic, self.sName)); return False; # Nuke the old setup for all possible adapter types (in case we're # called after it changed). for sName in ('pcnet', 'e1000', 'virtio-net'): for sConfig in ('VBoxInternal/Devices/%s/%u/LUN#0/AttachedDriver/Config' % (sName, iNic), \ 'VBoxInternal/Devices/%s/%u/LUN#0/Config' % (sName, iNic)): try: self.o.machine.setExtraData('%s/txs/Protocol' % (sConfig), ''); self.o.machine.setExtraData('%s/txs/HostPort' % (sConfig), ''); self.o.machine.setExtraData('%s/txs/GuestPort' % (sConfig), ''); except: reporter.errorXcpt(); # Set up port forwarding if NAT attachment. try: eAttType = oNic.attachmentType; except: reporter.errorXcpt('attachmentType on %s failed for "%s"' % (iNic, self.sName)); return False; if eAttType != vboxcon.NetworkAttachmentType_NAT: return True; try: eNicType = oNic.adapterType; fTraceEnabled = oNic.traceEnabled; except: reporter.errorXcpt('attachmentType/traceEnabled on %s failed for "%s"' % (iNic, self.sName)); return False; if self.fpApiVer >= 4.1: try: if self.fpApiVer >= 4.2: oNatEngine = oNic.NATEngine; else: oNatEngine = oNic.natDriver; except: reporter.errorXcpt('Failed to get INATEngine data on "%s"' % (self.sName)); return False; try: oNatEngine.removeRedirect('txs'); except: pass; try: oNatEngine.addRedirect('txs', vboxcon.NATProtocol_TCP, '127.0.0.1', '%s' % (iHostPort), '', '5042'); except: reporter.errorXcpt('Failed to add a addRedirect redirect on "%s"' % (self.sName)); return False; else: sName = self.getNicDriverNameFromType(eNicType); if fTraceEnabled: sConfig = 'VBoxInternal/Devices/%s/%u/LUN#0/AttachedDriver/Config' % (sName, iNic) else: sConfig = 'VBoxInternal/Devices/%s/%u/LUN#0/Config' % (sName, iNic) try: self.o.machine.setExtraData('%s/txs/Protocol' % (sConfig), 'TCP'); self.o.machine.setExtraData('%s/txs/HostPort' % (sConfig), '%s' % (iHostPort)); self.o.machine.setExtraData('%s/txs/GuestPort' % (sConfig), '5042'); except: reporter.errorXcpt('Failed to set NAT extra data on "%s"' % (self.sName)); return False; return True; def setNicType(self, eType, iNic = 0): """ Sets the NIC type of the specified NIC. Returns True on success and False on failure. Error information is logged. """ try: try: oNic = self.o.machine.getNetworkAdapter(iNic); except: reporter.errorXcpt('getNetworkAdapter(%s) failed for "%s"' % (iNic, self.sName)); return False; try: oNic.adapterType = eType; except: reporter.errorXcpt('failed to set NIC type on slot %s to %s for VM "%s"' % (iNic, eType, self.sName)); return False; finally: self.oTstDrv.processPendingEvents(); if not self.setupNatForwardingForTxs(iNic): return False; reporter.log('set NIC type on slot %s to %s for VM "%s"' % (iNic, eType, self.sName)); return True; def setNicTraceEnabled(self, fTraceEnabled, sTraceFile, iNic = 0): """ Sets the NIC trace enabled flag and file path. Returns True on success and False on failure. Error information is logged. """ try: try: oNic = self.o.machine.getNetworkAdapter(iNic); except: reporter.errorXcpt('getNetworkAdapter(%s) failed for "%s"' % (iNic, self.sName)); return False; try: oNic.traceEnabled = fTraceEnabled; oNic.traceFile = sTraceFile; except: reporter.errorXcpt('failed to set NIC trace flag on slot %s to %s for VM "%s"' \ % (iNic, fTraceEnabled, self.sName)); return False; finally: self.oTstDrv.processPendingEvents(); if not self.setupNatForwardingForTxs(iNic): return False; reporter.log('set NIC trace on slot %s to "%s" (path "%s") for VM "%s"' % (iNic, fTraceEnabled, sTraceFile, self.sName)); return True; def getDefaultNicName(self, eAttachmentType): """ Return the default network / interface name for the NIC attachment type. """ sRetName = ''; if eAttachmentType == vboxcon.NetworkAttachmentType_Bridged: if self.oTstDrv.sDefBridgedNic is not None: sRetName = self.oTstDrv.sDefBridgedNic; else: sRetName = 'eth0'; try: aoHostNics = self.oVBoxMgr.getArray(self.oVBox.host, 'networkInterfaces'); for oHostNic in aoHostNics: if oHostNic.interfaceType == vboxcon.HostNetworkInterfaceType_Bridged \ and oHostNic.status == vboxcon.HostNetworkInterfaceStatus_Up: sRetName = oHostNic.name; break; except: reporter.errorXcpt(); elif eAttachmentType == vboxcon.NetworkAttachmentType_HostOnly: try: aoHostNics = self.oVBoxMgr.getArray(self.oVBox.host, 'networkInterfaces'); for oHostNic in aoHostNics: if oHostNic.interfaceType == vboxcon.HostNetworkInterfaceType_HostOnly: if oHostNic.status == vboxcon.HostNetworkInterfaceStatus_Up: sRetName = oHostNic.name; break; if sRetName == '': sRetName = oHostNic.name; except: reporter.errorXcpt(); if sRetName == '': sRetName = 'HostInterfaceNetwork-vboxnet0'; elif eAttachmentType == vboxcon.NetworkAttachmentType_Internal: sRetName = 'VBoxTest'; elif eAttachmentType == vboxcon.NetworkAttachmentType_NAT: sRetName = ''; else: reporter.error('eAttachmentType=%s is not known' % (eAttachmentType)); return sRetName; def setNicAttachment(self, eAttachmentType, sName = None, iNic = 0): """ Sets the attachment type of the specified NIC. Returns True on success and False on failure. Error information is logged. """ try: oNic = self.o.machine.getNetworkAdapter(iNic); except: reporter.errorXcpt('getNetworkAdapter(%s) failed for "%s"' % (iNic, self.sName)); return False; try: if eAttachmentType is not None: try: if self.fpApiVer >= 4.1: oNic.attachmentType = eAttachmentType; else: if eAttachmentType == vboxcon.NetworkAttachmentType_NAT: oNic.attachToNAT(); elif eAttachmentType == vboxcon.NetworkAttachmentType_Bridged: oNic.attachToBridgedInterface(); elif eAttachmentType == vboxcon.NetworkAttachmentType_Internal: oNic.attachToInternalNetwork(); elif eAttachmentType == vboxcon.NetworkAttachmentType_HostOnly: oNic.attachToHostOnlyInterface(); else: raise base.GenError("eAttachmentType=%s is invalid" % (eAttachmentType)); except: reporter.errorXcpt('failed to set the attachment type on slot %s to %s for VM "%s"' \ % (iNic, eAttachmentType, self.sName)); return False; else: try: eAttachmentType = oNic.attachmentType; except: reporter.errorXcpt('failed to get the attachment type on slot %s for VM "%s"' % (iNic, self.sName)); return False; finally: self.oTstDrv.processPendingEvents(); if sName is not None: # Resolve the special 'default' name. if sName == 'default': sName = self.getDefaultNicName(eAttachmentType); # The name translate to different attributes depending on the # attachment type. try: if eAttachmentType == vboxcon.NetworkAttachmentType_Bridged: ## @todo check this out on windows, may have to do a # translation of the name there or smth IIRC. try: if self.fpApiVer >= 4.1: oNic.bridgedInterface = sName; else: oNic.hostInterface = sName; except: reporter.errorXcpt('failed to set the hostInterface property on slot %s to "%s" for VM "%s"' % (iNic, sName, self.sName,)); return False; elif eAttachmentType == vboxcon.NetworkAttachmentType_HostOnly: try: if self.fpApiVer >= 4.1: oNic.hostOnlyInterface = sName; else: oNic.hostInterface = sName; except: reporter.errorXcpt('failed to set the internalNetwork property on slot %s to "%s" for VM "%s"' % (iNic, sName, self.sName,)); return False; elif eAttachmentType == vboxcon.NetworkAttachmentType_Internal: try: oNic.internalNetwork = sName; except: reporter.errorXcpt('failed to set the internalNetwork property on slot %s to "%s" for VM "%s"' % (iNic, sName, self.sName,)); return False; elif eAttachmentType == vboxcon.NetworkAttachmentType_NAT: try: oNic.NATNetwork = sName; except: reporter.errorXcpt('failed to set the NATNetwork property on slot %s to "%s" for VM "%s"' % (iNic, sName, self.sName,)); return False; finally: self.oTstDrv.processPendingEvents(); if not self.setupNatForwardingForTxs(iNic): return False; reporter.log('set NIC type on slot %s to %s for VM "%s"' % (iNic, eAttachmentType, self.sName)); return True; def setNicMacAddress(self, sMacAddr, iNic = 0): """ Sets the MAC address of the specified NIC. The sMacAddr parameter is a string supplying the tail end of the MAC address, missing quads are supplied from a constant byte (2), the IPv4 address of the host, and the NIC number. Returns True on success and False on failure. Error information is logged. """ # Resolve missing MAC address prefix by feeding in the host IP address bytes. cchMacAddr = len(sMacAddr); if 0 < cchMacAddr < 12: sHostIP = netutils.getPrimaryHostIp(); abHostIP = socket.inet_aton(sHostIP); if sys.version_info[0] < 3: abHostIP = (ord(abHostIP[0]), ord(abHostIP[1]), ord(abHostIP[2]), ord(abHostIP[3])); if abHostIP[0] == 127 \ or (abHostIP[0] == 169 and abHostIP[1] == 254) \ or (abHostIP[0] == 192 and abHostIP[1] == 168 and abHostIP[2] == 56): return reporter.error('host IP for "%s" is %s, most likely not unique.' % (netutils.getHostnameFqdn(), sHostIP,)); sDefaultMac = '%02X%02X%02X%02X%02X%02X' % (0x02, abHostIP[0], abHostIP[1], abHostIP[2], abHostIP[3], iNic); sMacAddr = sDefaultMac[0:(12 - cchMacAddr)] + sMacAddr; # Get the NIC object and try set it address. try: oNic = self.o.machine.getNetworkAdapter(iNic); except: return reporter.errorXcpt('getNetworkAdapter(%s) failed for "%s"' % (iNic, self.sName,)); try: oNic.MACAddress = sMacAddr; except: return reporter.errorXcpt('failed to set the MAC address on slot %s to "%s" for VM "%s"' % (iNic, sMacAddr, self.sName)); reporter.log('set MAC address on slot %s to %s for VM "%s"' % (iNic, sMacAddr, self.sName,)); return True; def setRamSize(self, cMB): """ Set the RAM size of the VM. Returns True on success and False on failure. Error information is logged. """ fRc = True; try: self.o.machine.memorySize = cMB; except: reporter.errorXcpt('failed to set the RAM size of "%s" to %s' % (self.sName, cMB)); fRc = False; else: reporter.log('set the RAM size of "%s" to %s' % (self.sName, cMB)); self.oTstDrv.processPendingEvents(); return fRc; def setVRamSize(self, cMB): """ Set the RAM size of the VM. Returns True on success and False on failure. Error information is logged. """ fRc = True; try: self.o.machine.VRAMSize = cMB; except: reporter.errorXcpt('failed to set the VRAM size of "%s" to %s' % (self.sName, cMB)); fRc = False; else: reporter.log('set the VRAM size of "%s" to %s' % (self.sName, cMB)); self.oTstDrv.processPendingEvents(); return fRc; def setCpuCount(self, cCpus): """ Set the number of CPUs. Returns True on success and False on failure. Error information is logged. """ fRc = True; try: self.o.machine.CPUCount = cCpus; except: reporter.errorXcpt('failed to set the CPU count of "%s" to %s' % (self.sName, cCpus)); fRc = False; else: reporter.log('set the CPU count of "%s" to %s' % (self.sName, cCpus)); self.oTstDrv.processPendingEvents(); return fRc; def getCpuCount(self): """ Returns the number of CPUs. Returns the number of CPUs on success and 0 on failure. Error information is logged. """ cCpus = 0; try: cCpus = self.o.machine.CPUCount; except: reporter.errorXcpt('failed to get the CPU count of "%s"' % (self.sName,)); self.oTstDrv.processPendingEvents(); return cCpus; def ensureControllerAttached(self, sController): """ Makes sure the specified controller is attached to the VM, attaching it if necessary. """ try: try: self.o.machine.getStorageControllerByName(sController); except: (eBus, eType) = _ControllerNameToBusAndType(sController); try: oCtl = self.o.machine.addStorageController(sController, eBus); except: reporter.errorXcpt('addStorageController("%s",%s) failed on "%s"' % (sController, eBus, self.sName) ); return False; else: try: oCtl.controllerType = eType; reporter.log('added storage controller "%s" (bus %s, type %s) to %s' % (sController, eBus, eType, self.sName)); except: reporter.errorXcpt('controllerType = %s on ("%s" / %s) failed on "%s"' % (eType, sController, eBus, self.sName) ); return False; finally: self.oTstDrv.processPendingEvents(); return True; def setStorageControllerPortCount(self, sController, iPortCount): """ Set maximum ports count for storage controller """ try: oCtl = self.o.machine.getStorageControllerByName(sController) oCtl.portCount = iPortCount self.oTstDrv.processPendingEvents() reporter.log('set controller "%s" port count to value %d' % (sController, iPortCount)) return True except: reporter.log('unable to set storage controller "%s" ports count to %d' % (sController, iPortCount)) return False def setStorageControllerHostIoCache(self, sController, fUseHostIoCache): """ Set maximum ports count for storage controller """ try: oCtl = self.o.machine.getStorageControllerByName(sController); oCtl.useHostIOCache = fUseHostIoCache; self.oTstDrv.processPendingEvents(); reporter.log('set controller "%s" host I/O cache setting to %r' % (sController, fUseHostIoCache)); return True; except: reporter.log('unable to set storage controller "%s" host I/O cache setting to %r' % (sController, fUseHostIoCache)); return False; def setBootOrder(self, iPosition, eType): """ Set guest boot order type @param iPosition boot order position @param eType device type (vboxcon.DeviceType_HardDisk, vboxcon.DeviceType_DVD, vboxcon.DeviceType_Floppy) """ try: self.o.machine.setBootOrder(iPosition, eType) except: return reporter.errorXcpt('Unable to set boot order.') reporter.log('Set boot order [%d] for device %s' % (iPosition, str(eType))) self.oTstDrv.processPendingEvents(); return True def setStorageControllerType(self, eType, sController = "IDE Controller"): """ Similar to ensureControllerAttached, except it will change the type. """ try: oCtl = self.o.machine.getStorageControllerByName(sController); except: (eBus, _) = _ControllerNameToBusAndType(sController); try: oCtl = self.o.machine.addStorageController(sController, eBus); reporter.log('added storage controller "%s" (bus %s) to %s' % (sController, eBus, self.sName)); except: reporter.errorXcpt('addStorageController("%s",%s) failed on "%s"' % (sController, eBus, self.sName) ); return False; try: oCtl.controllerType = eType; except: reporter.errorXcpt('failed to set controller type of "%s" on "%s" to %s' % (sController, self.sName, eType) ); return False; reporter.log('set controller type of "%s" on "%s" to %s' % (sController, self.sName, eType) ); self.oTstDrv.processPendingEvents(); return True; def attachDvd(self, sImage = None, sController = "IDE Controller", iPort = 1, iDevice = 0): """ Attaches a DVD drive to a VM, optionally with an ISO inserted. Returns True on success and False on failure. Error information is logged. """ # Input validation. if sImage is not None and not self.oTstDrv.isResourceFile(sImage)\ and not os.path.isabs(sImage): ## fixme - testsuite unzip ++ reporter.fatal('"%s" is not in the resource set' % (sImage)); return None; if not self.ensureControllerAttached(sController): return False; # Find/register the image if specified. oImage = None; sImageUuid = ""; if sImage is not None: sFullName = self.oTstDrv.getFullResourceName(sImage) try: oImage = self.oVBox.findDVDImage(sFullName); except: try: if self.fpApiVer >= 4.1: oImage = self.oVBox.openMedium(sFullName, vboxcon.DeviceType_DVD, vboxcon.AccessMode_ReadOnly, False); elif self.fpApiVer >= 4.0: oImage = self.oVBox.openMedium(sFullName, vboxcon.DeviceType_DVD, vboxcon.AccessMode_ReadOnly); else: oImage = self.oVBox.openDVDImage(sFullName, ""); except vbox.ComException as oXcpt: if oXcpt.errno != -1: reporter.errorXcpt('failed to open DVD image "%s" xxx' % (sFullName)); else: reporter.errorXcpt('failed to open DVD image "%s" yyy' % (sFullName)); return False; except: reporter.errorXcpt('failed to open DVD image "%s"' % (sFullName)); return False; try: sImageUuid = oImage.id; except: reporter.errorXcpt('failed to get the UUID of "%s"' % (sFullName)); return False; # Attach the DVD. fRc = True; try: if self.fpApiVer >= 4.0: self.o.machine.attachDevice(sController, iPort, iDevice, vboxcon.DeviceType_DVD, oImage); else: self.o.machine.attachDevice(sController, iPort, iDevice, vboxcon.DeviceType_DVD, sImageUuid); except: reporter.errorXcpt('attachDevice("%s",%s,%s,HardDisk,"%s") failed on "%s"' \ % (sController, iPort, iDevice, sImageUuid, self.sName) ); fRc = False; else: reporter.log('attached DVD to %s, image="%s"' % (self.sName, sImage)); self.oTstDrv.processPendingEvents(); return fRc; def attachHd(self, sHd, sController = "IDE Controller", iPort = 0, iDevice = 0, fImmutable = True, fForceResource = True): """ Attaches a HD to a VM. Returns True on success and False on failure. Error information is logged. """ # Input validation. if fForceResource and not self.oTstDrv.isResourceFile(sHd): reporter.fatal('"%s" is not in the resource set' % (sHd,)); return None; if not self.ensureControllerAttached(sController): return False; # Find the HD, registering it if necessary (as immutable). if fForceResource: sFullName = self.oTstDrv.getFullResourceName(sHd); else: sFullName = sHd; try: oHd = self.oVBox.findHardDisk(sFullName); except: try: if self.fpApiVer >= 4.1: oHd = self.oVBox.openMedium(sFullName, vboxcon.DeviceType_HardDisk, vboxcon.AccessMode_ReadOnly, False); elif self.fpApiVer >= 4.0: oHd = self.oVBox.openMedium(sFullName, vboxcon.DeviceType_HardDisk, vboxcon.AccessMode_ReadOnly); else: oHd = self.oVBox.openHardDisk(sFullName, vboxcon.AccessMode_ReadOnly, False, "", False, ""); except: reporter.errorXcpt('failed to open hd "%s"' % (sFullName)); return False; try: if fImmutable: oHd.type = vboxcon.MediumType_Immutable; else: oHd.type = vboxcon.MediumType_Normal; except: if fImmutable: reporter.errorXcpt('failed to set hd "%s" immutable' % (sHd)); else: reporter.errorXcpt('failed to set hd "%s" normal' % (sHd)); return False; # Attach it. fRc = True; try: if self.fpApiVer >= 4.0: self.o.machine.attachDevice(sController, iPort, iDevice, vboxcon.DeviceType_HardDisk, oHd); else: self.o.machine.attachDevice(sController, iPort, iDevice, vboxcon.DeviceType_HardDisk, oHd.id); except: reporter.errorXcpt('attachDevice("%s",%s,%s,HardDisk,"%s") failed on "%s"' \ % (sController, iPort, iDevice, oHd.id, self.sName) ); fRc = False; else: reporter.log('attached "%s" to %s' % (sHd, self.sName)); self.oTstDrv.processPendingEvents(); return fRc; def createBaseHd(self, sHd, sFmt = "VDI", cb = 10*1024*1024*1024, cMsTimeout = 60000, tMediumVariant = None): """ Creates a base HD. Returns Medium object on success and None on failure. Error information is logged. """ if tMediumVariant is None: tMediumVariant = (vboxcon.MediumVariant_Standard, ); try: if self.fpApiVer >= 5.0: oHd = self.oVBox.createMedium(sFmt, sHd, vboxcon.AccessMode_ReadWrite, vboxcon.DeviceType_HardDisk); else: oHd = self.oVBox.createHardDisk(sFmt, sHd); oProgressXpcom = oHd.createBaseStorage(cb, tMediumVariant); oProgress = ProgressWrapper(oProgressXpcom, self.oVBoxMgr, self.oTstDrv, 'create base disk %s' % (sHd)); oProgress.wait(cMsTimeout); oProgress.logResult(); except: reporter.errorXcpt('failed to create base hd "%s"' % (sHd)); oHd = None return oHd; def createDiffHd(self, oParentHd, sHd, sFmt = "VDI"): """ Creates a differencing HD. Returns Medium object on success and None on failure. Error information is logged. """ # Detect the proper format if requested if sFmt is None: try: oHdFmt = oParentHd.mediumFormat; lstCaps = self.oVBoxMgr.getArray(oHdFmt, 'capabilities'); if vboxcon.MediumFormatCapabilities_Differencing in lstCaps: sFmt = oHdFmt.id; else: sFmt = 'VDI'; except: reporter.errorXcpt('failed to get preferred diff format for "%s"' % (sHd)); return None; try: if self.fpApiVer >= 5.0: oHd = self.oVBox.createMedium(sFmt, sHd, vboxcon.AccessMode_ReadWrite, vboxcon.DeviceType_HardDisk); else: oHd = self.oVBox.createHardDisk(sFmt, sHd); oProgressXpcom = oParentHd.createDiffStorage(oHd, (vboxcon.MediumVariant_Standard, )) oProgress = ProgressWrapper(oProgressXpcom, self.oVBoxMgr, self.oTstDrv, 'create diff disk %s' % (sHd)); oProgress.wait(); oProgress.logResult(); except: reporter.errorXcpt('failed to create diff hd "%s"' % (sHd)); oHd = None return oHd; def createAndAttachHd(self, sHd, sFmt = "VDI", sController = "IDE Controller", cb = 10*1024*1024*1024, # pylint: disable=too-many-arguments iPort = 0, iDevice = 0, fImmutable = True, cMsTimeout = 60000, tMediumVariant = None): """ Creates and attaches a HD to a VM. Returns True on success and False on failure. Error information is logged. """ if not self.ensureControllerAttached(sController): return False; oHd = self.createBaseHd(sHd, sFmt, cb, cMsTimeout, tMediumVariant); if oHd is None: return False; fRc = True; try: if fImmutable: oHd.type = vboxcon.MediumType_Immutable; else: oHd.type = vboxcon.MediumType_Normal; except: if fImmutable: reporter.errorXcpt('failed to set hd "%s" immutable' % (sHd)); else: reporter.errorXcpt('failed to set hd "%s" normal' % (sHd)); fRc = False; # Attach it. if fRc is True: try: if self.fpApiVer >= 4.0: self.o.machine.attachDevice(sController, iPort, iDevice, vboxcon.DeviceType_HardDisk, oHd); else: self.o.machine.attachDevice(sController, iPort, iDevice, vboxcon.DeviceType_HardDisk, oHd.id); except: reporter.errorXcpt('attachDevice("%s",%s,%s,HardDisk,"%s") failed on "%s"' \ % (sController, iPort, iDevice, oHd.id, self.sName) ); fRc = False; else: reporter.log('attached "%s" to %s' % (sHd, self.sName)); # Delete disk in case of an error if fRc is False: try: oProgressCom = oHd.deleteStorage(); except: reporter.errorXcpt('deleteStorage() for disk %s failed' % (sHd,)); else: oProgress = ProgressWrapper(oProgressCom, self.oVBoxMgr, self.oTstDrv, 'delete disk %s' % (sHd)); oProgress.wait(); oProgress.logResult(); self.oTstDrv.processPendingEvents(); return fRc; def detachHd(self, sController = "IDE Controller", iPort = 0, iDevice = 0): """ Detaches a HD, if attached, and returns a reference to it (IMedium). In order to delete the detached medium, the caller must first save the changes made in this session. Returns (fRc, oHd), where oHd is None unless fRc is True, and fRc is your standard success indicator. Error information is logged. """ # What's attached? try: oHd = self.o.machine.getMedium(sController, iPort, iDevice); except: if self.oVBoxMgr.xcptIsOurXcptKind() \ and self.oVBoxMgr.xcptIsEqual(None, self.oVBoxMgr.constants.VBOX_E_OBJECT_NOT_FOUND): reporter.log('No HD attached (to %s %s:%s)' % (sController, iPort, iDevice)); return (True, None); return (reporter.errorXcpt('Error getting media at port %s, device %s, on %s.' % (iPort, iDevice, sController)), None); # Detach it. try: self.o.machine.detachDevice(sController, iPort, iDevice); except: return (reporter.errorXcpt('detachDevice("%s",%s,%s) failed on "%s"' \ % (sController, iPort, iDevice, self.sName) ), None); reporter.log('detached HD ("%s",%s,%s) from %s' % (sController, iPort, iDevice, self.sName)); return (True, oHd); def attachFloppy(self, sFloppy, sController = "Floppy Controller", iPort = 0, iDevice = 0): """ Attaches a floppy image to a VM. Returns True on success and False on failure. Error information is logged. """ # Input validation. ## @todo Fix this wrt to bootsector-xxx.img from the validationkit.zip. ##if not self.oTstDrv.isResourceFile(sFloppy): ## reporter.fatal('"%s" is not in the resource set' % (sFloppy)); ## return None; if not self.ensureControllerAttached(sController): return False; # Find the floppy image, registering it if necessary (as immutable). sFullName = self.oTstDrv.getFullResourceName(sFloppy); try: oFloppy = self.oVBox.findFloppyImage(sFullName); except: try: if self.fpApiVer >= 4.1: oFloppy = self.oVBox.openMedium(sFullName, vboxcon.DeviceType_Floppy, vboxcon.AccessMode_ReadOnly, False); elif self.fpApiVer >= 4.0: oFloppy = self.oVBox.openMedium(sFullName, vboxcon.DeviceType_Floppy, vboxcon.AccessMode_ReadOnly); else: oFloppy = self.oVBox.openFloppyImage(sFullName, ""); except: reporter.errorXcpt('failed to open floppy "%s"' % (sFullName)); return False; ## @todo the following works but causes trouble below (asserts in main). #try: # oFloppy.type = vboxcon.MediumType_Immutable; #except: # reporter.errorXcpt('failed to make floppy "%s" immutable' % (sFullName)); # return False; # Attach it. fRc = True; try: if self.fpApiVer >= 4.0: self.o.machine.attachDevice(sController, iPort, iDevice, vboxcon.DeviceType_Floppy, oFloppy); else: self.o.machine.attachDevice(sController, iPort, iDevice, vboxcon.DeviceType_Floppy, oFloppy.id); except: reporter.errorXcpt('attachDevice("%s",%s,%s,Floppy,"%s") failed on "%s"' \ % (sController, iPort, iDevice, oFloppy.id, self.sName) ); fRc = False; else: reporter.log('attached "%s" to %s' % (sFloppy, self.sName)); self.oTstDrv.processPendingEvents(); return fRc; def setupNic(self, sType, sXXX): """ Sets up a NIC to a VM. Returns True on success and False on failure. Error information is logged. """ if sType == "PCNet": enmType = vboxcon.NetworkAdapterType_Am79C973; elif sType == "PCNetOld": enmType = vboxcon.NetworkAdapterType_Am79C970A; elif sType == "E1000": enmType = vboxcon.NetworkAdapterType_I82545EM; # MT Server elif sType == "E1000Desk": enmType = vboxcon.NetworkAdapterType_I82540EM; # MT Desktop elif sType == "E1000Srv2": enmType = vboxcon.NetworkAdapterType_I82543GC; # T Server elif sType == "Virtio": enmType = vboxcon.NetworkAdapterType_Virtio; else: reporter.error('Invalid NIC type: "%s" (sXXX=%s)' % (sType, sXXX)); return False; ## @todo Implement me! if enmType is not None: pass return True; def setupAudio(self, eAudioControllerType, fEnable = True, eAudioDriverType = None): """ Sets up audio. :param eAudioControllerType: The audio controller type (vboxcon.AudioControllerType_XXX). :param fEnable: Whether to enable or disable the audio controller (default enable). :param eAudioDriverType: The audio driver type (vboxcon.AudioDriverType_XXX), picks something suitable if None is passed (default). """ try: oAudioAdapter = self.o.machine.audioAdapter; except: return reporter.errorXcpt('Failed to get the audio adapter.'); try: oAudioAdapter.audioController = eAudioControllerType; except: return reporter.errorXcpt('Failed to set the audio controller to %s.' % (eAudioControllerType,)); if eAudioDriverType is None: sHost = utils.getHostOs() if sHost == 'darwin': eAudioDriverType = vboxcon.AudioDriverType_CoreAudio; elif sHost == 'win': eAudioDriverType = vboxcon.AudioDriverType_DirectSound; elif sHost == 'linux': eAudioDriverType = vboxcon.AudioDriverType_Pulse; elif sHost == 'solaris': eAudioDriverType = vboxcon.AudioDriverType_OSS; else: reporter.error('PORTME: Do not know which audio driver to pick for: %s!' % (sHost,)); eAudioDriverType = vboxcon.AudioDriverType_Null; try: oAudioAdapter.audioDriver = eAudioDriverType; except: return reporter.errorXcpt('Failed to set the audio driver to %s.' % (eAudioDriverType,)) try: oAudioAdapter.enabled = fEnable; except: return reporter.errorXcpt('Failed to set the "enabled" property to %s.' % (fEnable,)); reporter.log('set audio adapter type to %d, driver to %d, and enabled to %s' % (eAudioControllerType, eAudioDriverType, fEnable,)); self.oTstDrv.processPendingEvents(); return True; def setupPreferredConfig(self): # pylint: disable=too-many-locals """ Configures the VM according to the preferences of the guest type. """ try: sOsTypeId = self.o.machine.OSTypeId; except: reporter.errorXcpt('failed to obtain the OSTypeId for "%s"' % (self.sName)); return False; try: oOsType = self.oVBox.getGuestOSType(sOsTypeId); except: reporter.errorXcpt('getGuestOSType("%s") failed for "%s"' % (sOsTypeId, self.sName)); return False; # get the attributes. try: #sFamilyId = oOsType.familyId; #f64Bit = oOsType.is64Bit; fIoApic = oOsType.recommendedIOAPIC; fVirtEx = oOsType.recommendedVirtEx; cMBRam = oOsType.recommendedRAM; cMBVRam = oOsType.recommendedVRAM; #cMBHdd = oOsType.recommendedHDD; eNicType = oOsType.adapterType; if self.fpApiVer >= 3.2: if self.fpApiVer >= 4.2: fPae = oOsType.recommendedPAE; fUsbHid = oOsType.recommendedUSBHID; fHpet = oOsType.recommendedHPET; eStorCtlType = oOsType.recommendedHDStorageController; else: fPae = oOsType.recommendedPae; fUsbHid = oOsType.recommendedUsbHid; fHpet = oOsType.recommendedHpet; eStorCtlType = oOsType.recommendedHdStorageController; eFirmwareType = oOsType.recommendedFirmware; else: fPae = False; fUsbHid = False; fHpet = False; eFirmwareType = -1; eStorCtlType = vboxcon.StorageControllerType_PIIX4; if self.fpApiVer >= 4.0: eAudioCtlType = oOsType.recommendedAudioController; except: reporter.errorXcpt('exception reading IGuestOSType(%s) attribute' % (sOsTypeId)); self.oTstDrv.processPendingEvents(); return False; self.oTstDrv.processPendingEvents(); # Do the setting. Continue applying settings on error in case the # caller ignores the return code fRc = True; if not self.enableIoApic(fIoApic): fRc = False; if not self.enableVirtEx(fVirtEx): fRc = False; if not self.enablePae(fPae): fRc = False; if not self.setRamSize(cMBRam): fRc = False; if not self.setVRamSize(cMBVRam): fRc = False; if not self.setNicType(eNicType, 0): fRc = False; if self.fpApiVer >= 3.2: if not self.setFirmwareType(eFirmwareType): fRc = False; if not self.enableUsbHid(fUsbHid): fRc = False; if not self.enableHpet(fHpet): fRc = False; if eStorCtlType in (vboxcon.StorageControllerType_PIIX3, vboxcon.StorageControllerType_PIIX4, vboxcon.StorageControllerType_ICH6,): if not self.setStorageControllerType(eStorCtlType, "IDE Controller"): fRc = False; if self.fpApiVer >= 4.0: if not self.setupAudio(eAudioCtlType): fRc = False; return fRc; def addUsbDeviceFilter(self, sName, sVendorId = None, sProductId = None, sRevision = None, # pylint: disable=too-many-arguments sManufacturer = None, sProduct = None, sSerialNumber = None, sPort = None, sRemote = None): """ Creates a USB device filter and inserts it into the VM. Returns True on success. Returns False on failure (logged). """ fRc = True; try: oUsbDevFilter = self.o.machine.USBDeviceFilters.createDeviceFilter(sName); oUsbDevFilter.active = True; if sVendorId is not None: oUsbDevFilter.vendorId = sVendorId; if sProductId is not None: oUsbDevFilter.productId = sProductId; if sRevision is not None: oUsbDevFilter.revision = sRevision; if sManufacturer is not None: oUsbDevFilter.manufacturer = sManufacturer; if sProduct is not None: oUsbDevFilter.product = sProduct; if sSerialNumber is not None: oUsbDevFilter.serialnumber = sSerialNumber; if sPort is not None: oUsbDevFilter.port = sPort; if sRemote is not None: oUsbDevFilter.remote = sRemote; try: self.o.machine.USBDeviceFilters.insertDeviceFilter(0, oUsbDevFilter); except: reporter.errorXcpt('insertDeviceFilter(%s) failed on "%s"' \ % (0, self.sName) ); fRc = False; else: reporter.log('inserted USB device filter "%s" to %s' % (sName, self.sName)); except: reporter.errorXcpt('createDeviceFilter("%s") failed on "%s"' \ % (sName, self.sName) ); fRc = False; return fRc; def getGuestPropertyValue(self, sName): """ Gets a guest property value. Returns the value on success, None on failure (logged). """ try: sValue = self.o.machine.getGuestPropertyValue(sName); except: reporter.errorXcpt('IMachine::getGuestPropertyValue("%s") failed' % (sName)); return None; return sValue; def setGuestPropertyValue(self, sName, sValue): """ Sets a guest property value. Returns the True on success, False on failure (logged). """ try: self.o.machine.setGuestPropertyValue(sName, sValue); except: reporter.errorXcpt('IMachine::setGuestPropertyValue("%s","%s") failed' % (sName, sValue)); return False; return True; def delGuestPropertyValue(self, sName): """ Deletes a guest property value. Returns the True on success, False on failure (logged). """ try: oMachine = self.o.machine; if self.fpApiVer >= 4.2: oMachine.deleteGuestProperty(sName); else: oMachine.setGuestPropertyValue(sName, ''); except: reporter.errorXcpt('Unable to delete guest property "%s"' % (sName,)); return False; return True; def setExtraData(self, sKey, sValue): """ Sets extra data. Returns the True on success, False on failure (logged). """ try: self.o.machine.setExtraData(sKey, sValue); except: reporter.errorXcpt('IMachine::setExtraData("%s","%s") failed' % (sKey, sValue)); return False; return True; def getExtraData(self, sKey): """ Gets extra data. Returns value on success, None on failure. """ try: sValue = self.o.machine.getExtraData(sKey) except: reporter.errorXcpt('IMachine::setExtraData("%s","%s") failed' % (sKey, sValue)) return None return sValue def setupTeleporter(self, fEnabled=True, uPort = 6500, sAddress = '', sPassword = ''): """ Sets up the teleporter for the VM. Returns True on success, False on failure (logged). """ try: self.o.machine.teleporterAddress = sAddress; self.o.machine.teleporterPort = uPort; self.o.machine.teleporterPassword = sPassword; self.o.machine.teleporterEnabled = fEnabled; except: reporter.errorXcpt('setupTeleporter(%s, %s, %s, %s)' % (fEnabled, sPassword, uPort, sAddress)); return False; return True; def enableTeleporter(self, fEnable=True): """ Enables or disables the teleporter of the VM. Returns True on success, False on failure (logged). """ try: self.o.machine.teleporterEnabled = fEnable; except: reporter.errorXcpt('IMachine::teleporterEnabled=%s failed' % (fEnable)); return False; return True; def teleport(self, sHostname = 'localhost', uPort = 6500, sPassword = 'password', cMsMaxDowntime = 250): """ Wrapper around the IConsole::teleport() method. Returns a progress object on success, None on failure (logged). """ reporter.log2('"%s"::teleport(%s,%s,%s,%s)...' % (self.sName, sHostname, uPort, sPassword, cMsMaxDowntime)); try: oProgress = self.o.console.teleport(sHostname, uPort, sPassword, cMsMaxDowntime) except: reporter.errorXcpt('IConsole::teleport(%s,%s,%s,%s) failed' % (sHostname, uPort, sPassword, cMsMaxDowntime)); return None; return ProgressWrapper(oProgress, self.oVBoxMgr, self.oTstDrv, 'teleport %s' % (self.sName,)); def getOsType(self): """ Gets the IGuestOSType interface for the machine. return IGuestOSType interface on success, None + errorXcpt on failure. No exceptions raised. """ try: sOsTypeId = self.o.machine.OSTypeId; except: reporter.errorXcpt('failed to obtain the OSTypeId for "%s"' % (self.sName)); return None; try: oOsType = self.oVBox.getGuestOSType(sOsTypeId); except: reporter.errorXcpt('getGuestOSType("%s") failed for "%s"' % (sOsTypeId, self.sName)); return None; return oOsType; def setOsType(self, sNewTypeId): """ Changes the OS type. returns True on success, False + errorXcpt on failure. No exceptions raised. """ try: self.o.machine.OSTypeId = sNewTypeId; except: reporter.errorXcpt('failed to set the OSTypeId for "%s" to "%s"' % (self.sName, sNewTypeId)); return False; return True; def setParavirtProvider(self, iProvider): """ Sets a paravirtualisation provider. Returns the True on success, False on failure (logged). """ try: self.o.machine.paravirtProvider = iProvider except: reporter.errorXcpt('Unable to set paravirtualisation provider "%s"' % (iProvider,)) return False; return True; def setupSerialToRawFile(self, iSerialPort, sRawFile): """ Enables the given serial port (zero based) and redirects it to sRawFile. Returns the True on success, False on failure (logged). """ try: oPort = self.o.machine.getSerialPort(iSerialPort); except: fRc = reporter.errorXcpt('failed to get serial port #%u' % (iSerialPort,)); else: try: oPort.path = sRawFile; except: fRc = reporter.errorXcpt('failed to set the "path" property on serial port #%u to "%s"' % (iSerialPort, sRawFile)); else: try: oPort.hostMode = vboxcon.PortMode_RawFile; except: fRc = reporter.errorXcpt('failed to set the "hostMode" property on serial port #%u to PortMode_RawFile' % (iSerialPort,)); else: try: oPort.enabled = True; except: fRc = reporter.errorXcpt('failed to set the "enable" property on serial port #%u to True' % (iSerialPort,)); else: reporter.log('set SerialPort[%s].enabled/hostMode/path=True/RawFile/%s' % (iSerialPort, sRawFile,)); fRc = True; self.oTstDrv.processPendingEvents(); return fRc; def enableSerialPort(self, iSerialPort): """ Enables the given serial port setting the initial port mode to disconnected. """ try: oPort = self.o.machine.getSerialPort(iSerialPort); except: fRc = reporter.errorXcpt('failed to get serial port #%u' % (iSerialPort,)); else: try: oPort.hostMode = vboxcon.PortMode_Disconnected; except: fRc = reporter.errorXcpt('failed to set the "hostMode" property on serial port #%u to PortMode_Disconnected' % (iSerialPort,)); else: try: oPort.enabled = True; except: fRc = reporter.errorXcpt('failed to set the "enable" property on serial port #%u to True' % (iSerialPort,)); else: reporter.log('set SerialPort[%s].enabled/hostMode/=True/Disconnected' % (iSerialPort,)); fRc = True; self.oTstDrv.processPendingEvents(); return fRc; def changeSerialPortAttachment(self, iSerialPort, ePortMode, sPath, fServer): """ Changes the attachment of the given serial port to the attachment config given. """ try: oPort = self.o.machine.getSerialPort(iSerialPort); except: fRc = reporter.errorXcpt('failed to get serial port #%u' % (iSerialPort,)); else: try: # Change port mode to disconnected first so changes get picked up by a potentially running VM. oPort.hostMode = vboxcon.PortMode_Disconnected; except: fRc = reporter.errorXcpt('failed to set the "hostMode" property on serial port #%u to PortMode_Disconnected' % (iSerialPort,)); else: try: oPort.path = sPath; oPort.server = fServer; oPort.hostMode = ePortMode; except: fRc = reporter.errorXcpt('failed to configure the serial port'); else: reporter.log('set SerialPort[%s].hostMode/path/server=%s/%s/%s' % (iSerialPort, ePortMode, sPath, fServer)); fRc = True; self.oTstDrv.processPendingEvents(); return fRc; # # IConsole wrappers. # def powerOff(self, fFudgeOnFailure = True): """ Powers off the VM. Returns True on success. Returns False on IConsole::powerDown() failure. Returns None if the progress object returns failure. """ # # Deregister event handler before we power off the VM, otherwise we're # racing for VM process termination and cause misleading spurious # error messages in the event handling code, because the event objects # disappear. # # Note! Doing this before powerDown to try prevent numerous smoketest # timeouts on XPCOM hosts. # self.deregisterEventHandlerForTask(); # Try power if off. try: oProgress = self.o.console.powerDown(); except: reporter.logXcpt('IConsole::powerDown failed on %s' % (self.sName)); if fFudgeOnFailure: self.oTstDrv.waitOnDirectSessionClose(self.oVM, 5000); # fudge self.waitForTask(1000); # fudge return False; # Wait on power off operation to complete. rc = self.oTstDrv.waitOnProgress(oProgress); if rc < 0: self.close(); if fFudgeOnFailure: vbox.reportError(oProgress, 'powerDown for "%s" failed' % (self.sName)); self.oTstDrv.waitOnDirectSessionClose(self.oVM, 5000); # fudge return None; # Wait for the VM to really power off or we'll fail to open a new session to it. self.oTstDrv.waitOnDirectSessionClose(self.oVM, 5000); # fudge return self.waitForTask(30 * 1000); # fudge def saveState(self, fPause = True): """ Saves state of the VM. Returns True on success. Returns False on IConsole::saveState() failure. Returns None if the progress object returns Failure. """ if fPause is True \ and self.oVM.state is vboxcon.MachineState_Running: self.o.console.pause(); if self.oVM.state is not vboxcon.MachineState_Paused: reporter.error('pause for "%s" failed' % (self.sName)); # Try saving state. try: if self.fpApiVer >= 5.0: oProgress = self.o.machine.saveState() else: oProgress = self.o.console.saveState() except: reporter.logXcpt('IMachine::saveState failed on %s' % (self.sName)); return False; # Wait for saving state operation to complete. rc = self.oTstDrv.waitOnProgress(oProgress); if rc < 0: self.close(); return None; # Wait for the VM to really terminate or we'll fail to open a new session to it. self.oTstDrv.waitOnDirectSessionClose(self.oVM, 5000); # fudge return self.waitForTask(30 * 1000); # fudge def discardSavedState(self, fRemove = True): """ Discards saved state of the VM. Returns True on success. Returns False on IConsole::discardSaveState() failure. """ try: if self.fpApiVer >= 5.0: self.o.machine.discardSavedState(fRemove) else: self.o.console.discardSavedState(fRemove) except: reporter.logXcpt('IMachine::discardSavedState failed on %s' % (self.sName)) return False return True def restoreSnapshot(self, oSnapshot, fFudgeOnFailure = True): """ Restores the given snapshot. Returns True on success. Returns False on IMachine::restoreSnapshot() failure. Returns None if the progress object returns failure. """ try: if self.fpApiVer >= 5.0: oProgress = self.o.machine.restoreSnapshot(oSnapshot); else: oProgress = self.o.console.restoreSnapshot(oSnapshot); except: reporter.logXcpt('IMachine::restoreSnapshot failed on %s' % (self.sName)); if fFudgeOnFailure: self.oTstDrv.waitOnDirectSessionClose(self.oVM, 5000); # fudge self.waitForTask(1000); # fudge return False; rc = self.oTstDrv.waitOnProgress(oProgress); if rc < 0: self.close(); if fFudgeOnFailure: vbox.reportError(oProgress, 'restoreSnapshot for "%s" failed' % (self.sName)); return None; return self.waitForTask(30 * 1000); def deleteSnapshot(self, oSnapshot, fFudgeOnFailure = True, cMsTimeout = 30 * 1000): """ Deletes the given snapshot merging the diff image into the base. Returns True on success. Returns False on IMachine::deleteSnapshot() failure. """ try: if self.fpApiVer >= 5.0: oProgressCom = self.o.machine.deleteSnapshot(oSnapshot); else: oProgressCom = self.o.console.deleteSnapshot(oSnapshot); oProgress = ProgressWrapper(oProgressCom, self.oVBoxMgr, self.oTstDrv, 'Delete Snapshot %s' % (oSnapshot)); oProgress.wait(cMsTimeout); oProgress.logResult(); except: reporter.logXcpt('IMachine::deleteSnapshot failed on %s' % (self.sName)); if fFudgeOnFailure: self.oTstDrv.waitOnDirectSessionClose(self.oVM, 5000); # fudge self.waitForTask(1000); # fudge return False; return True; def takeSnapshot(self, sName, sDescription = '', fPause = True, fFudgeOnFailure = True, cMsTimeout = 30 * 1000): """ Takes a snapshot with the given name Returns True on success. Returns False on IMachine::takeSnapshot() or VM state change failure. """ try: if fPause is True \ and self.oVM.state is vboxcon.MachineState_Running: self.o.console.pause(); if self.fpApiVer >= 5.0: (oProgressCom, _) = self.o.machine.takeSnapshot(sName, sDescription, True); else: oProgressCom = self.o.console.takeSnapshot(sName, sDescription); oProgress = ProgressWrapper(oProgressCom, self.oVBoxMgr, self.oTstDrv, 'Take Snapshot %s' % (sName)); oProgress.wait(cMsTimeout); oProgress.logResult(); except: reporter.logXcpt('IMachine::takeSnapshot failed on %s' % (self.sName)); if fFudgeOnFailure: self.oTstDrv.waitOnDirectSessionClose(self.oVM, 5000); # fudge self.waitForTask(1000); # fudge return False; if fPause is True \ and self.oVM.state is vboxcon.MachineState_Paused: self.o.console.resume(); return True; def findSnapshot(self, sName): """ Returns the snapshot object with the given name Returns snapshot object on success. Returns None if there is no snapshot with the given name. """ return self.oVM.findSnapshot(sName); def takeScreenshot(self, sFilename, iScreenId=0): """ Take screenshot from the given display and save it to specified file. Returns True on success Returns False on failure. """ try: if self.fpApiVer >= 5.0: iWidth, iHeight, _, _, _, _ = self.o.console.display.getScreenResolution(iScreenId) aPngData = self.o.console.display.takeScreenShotToArray(iScreenId, iWidth, iHeight, vboxcon.BitmapFormat_PNG) else: iWidth, iHeight, _, _, _ = self.o.console.display.getScreenResolution(iScreenId) aPngData = self.o.console.display.takeScreenShotPNGToArray(iScreenId, iWidth, iHeight) except: reporter.logXcpt("Unable to take screenshot") return False oFile = open(sFilename, 'wb') oFile.write(aPngData) oFile.close() return True def attachUsbDevice(self, sUuid, sCaptureFilename = None): """ Attach given USB device UUID to the VM. Returns True on success Returns False on failure. """ fRc = True; try: if sCaptureFilename is None: self.o.console.attachUSBDevice(sUuid, ''); else: self.o.console.attachUSBDevice(sUuid, sCaptureFilename); except: reporter.logXcpt('Unable to attach USB device %s' % (sUuid,)); fRc = False; return fRc; def detachUsbDevice(self, sUuid): """ Detach given USB device UUID from the VM. Returns True on success Returns False on failure. """ fRc = True; try: _ = self.o.console.detachUSBDevice(sUuid); except: reporter.logXcpt('Unable to detach USB device %s' % (sUuid,)); fRc = False; return fRc; # # IMachineDebugger wrappers. # def queryOsKernelLog(self): """ Tries to get the OS kernel log using the VM debugger interface. Returns string containing the kernel log on success. Returns None on failure. """ sOsKernelLog = None; try: self.o.console.debugger.loadPlugIn('all'); except: reporter.logXcpt('Unable to load debugger plugins'); else: try: sOsDetected = self.o.console.debugger.detectOS(); except: reporter.logXcpt('Failed to detect the guest OS'); else: try: sOsKernelLog = self.o.console.debugger.queryOSKernelLog(0); except: reporter.logXcpt('Unable to get the guest OS (%s) kernel log' % (sOsDetected,)); return sOsKernelLog; def queryDbgInfo(self, sItem, sArg = '', sDefault = None): """ Simple wrapper around IMachineDebugger::info. Returns string on success, sDefault on failure (logged). """ try: return self.o.console.debugger.info(sItem, sArg); except: reporter.logXcpt('Unable to query "%s" with arg "%s"' % (sItem, sArg,)); return sDefault; def queryDbgInfoVgaText(self, sArg = 'all'): """ Tries to get the 'info vgatext' output, provided we're in next mode. Returns string containing text on success. Returns None on failure or not text mode. """ sVgaText = None; try: sVgaText = self.o.console.debugger.info('vgatext', sArg); if sVgaText.startswith('Not in text mode!'): sVgaText = None; except: reporter.logXcpt('Unable to query vgatext with arg "%s"' % (sArg,)); return sVgaText; def queryDbgGuestStack(self, iCpu = 0): """ Returns the guest stack for the given VCPU. Returns string containing the guest stack for the selected VCPU on success. Returns None on failure. """ # # Load all plugins first and try to detect the OS so we can # get nicer stack traces. # try: self.o.console.debugger.loadPlugIn('all'); except: reporter.logXcpt('Unable to load debugger plugins'); else: try: sOsDetected = self.o.console.debugger.detectOS(); _ = sOsDetected; except: reporter.logXcpt('Failed to detect the guest OS'); sGuestStack = None; try: sGuestStack = self.o.console.debugger.dumpGuestStack(iCpu); except: reporter.logXcpt('Unable to query guest stack for CPU %s' % (iCpu, )); return sGuestStack; # # Other methods. # def getPrimaryIp(self): """ Tries to obtain the primary IP address of the guest via the guest properties. Returns IP address on success. Returns empty string on failure. """ sIpAddr = self.getGuestPropertyValue('/VirtualBox/GuestInfo/Net/0/V4/IP'); if vbox.isIpAddrValid(sIpAddr): return sIpAddr; return ''; def getPid(self): """ Gets the process ID for the direct session unless it's ourselves. """ if self.uPid is None and self.o is not None and self.fRemoteSession: try: if self.fpApiVer >= 4.2: uPid = self.o.machine.sessionPID; else: uPid = self.o.machine.sessionPid; if uPid != os.getpid() and uPid != 0xffffffff: self.uPid = uPid; except Exception as oXcpt: if vbox.ComError.equal(oXcpt, vbox.ComError.E_UNEXPECTED): try: if self.fpApiVer >= 4.2: uPid = self.oVM.sessionPID; else: uPid = self.oVM.sessionPid; if uPid != os.getpid() and uPid != 0xffffffff: self.uPid = uPid; except: reporter.log2Xcpt(); else: reporter.log2Xcpt(); if self.uPid is not None: reporter.log2('getPid: %u' % (self.uPid,)); self.fPidFile = self.oTstDrv.pidFileAdd(self.uPid, 'vm_%s' % (self.sName,), # Set-uid-to-root is similar to SUDO. fSudo = True); return self.uPid; def addLogsToReport(self, cReleaseLogs = 1): """ Retrieves and adds the release and debug logs to the test report. """ fRc = True; # Add each of the requested release logs to the report. for iLog in range(0, cReleaseLogs): try: if self.fpApiVer >= 3.2: sLogFile = self.oVM.queryLogFilename(iLog); elif iLog > 0: sLogFile = '%s/VBox.log' % (self.oVM.logFolder,); else: sLogFile = '%s/VBox.log.%u' % (self.oVM.logFolder, iLog); except: reporter.logXcpt('iLog=%s' % (iLog,)); fRc = False; else: if sLogFile is not None and sLogFile != '': # the None bit is for a 3.2.0 bug. reporter.addLogFile(sLogFile, 'log/release/vm', '%s #%u' % (self.sName, iLog), sAltName = '%s-%s' % (self.sName, os.path.basename(sLogFile),)); # Now for the hardened windows startup log. try: sLogFile = os.path.join(self.oVM.logFolder, 'VBoxHardening.log'); except: reporter.logXcpt(); fRc = False; else: if os.path.isfile(sLogFile): reporter.addLogFile(sLogFile, 'log/release/vm', '%s hardening log' % (self.sName, ), sAltName = '%s-%s' % (self.sName, os.path.basename(sLogFile),)); # Now for the debug log. if self.sLogFile is not None and os.path.isfile(self.sLogFile): reporter.addLogFile(self.sLogFile, 'log/debug/vm', '%s debug' % (self.sName, ), sAltName = '%s-%s' % (self.sName, os.path.basename(self.sLogFile),)); return fRc; def registerDerivedEventHandler(self, oSubClass, dArgs = None, fMustSucceed = True): """ Create an instance of the given ConsoleEventHandlerBase sub-class and register it. The new instance is returned on success. None is returned on error. """ # We need a console object. try: oConsole = self.o.console; except Exception as oXcpt: if fMustSucceed or vbox.ComError.notEqual(oXcpt, vbox.ComError.E_UNEXPECTED): reporter.errorXcpt('Failed to get ISession::console for "%s"' % (self.sName, )); return None; # Add the base class arguments. dArgsCopy = dArgs.copy() if dArgs is not None else dict(); dArgsCopy['oSession'] = self; dArgsCopy['oConsole'] = oConsole; sLogSuffix = 'on %s' % (self.sName,) return oSubClass.registerDerivedEventHandler(self.oVBoxMgr, self.fpApiVer, oSubClass, dArgsCopy, oConsole, 'IConsole', 'IConsoleCallback', fMustSucceed = fMustSucceed, sLogSuffix = sLogSuffix); def enableVmmDevTestingPart(self, fEnabled, fEnableMMIO = False): """ Enables the testing part of the VMMDev. Returns True on success and False on failure. Error information is logged. """ fRc = True; try: self.o.machine.setExtraData('VBoxInternal/Devices/VMMDev/0/Config/TestingEnabled', '1' if fEnabled else ''); self.o.machine.setExtraData('VBoxInternal/Devices/VMMDev/0/Config/TestingMMIO', '1' if fEnableMMIO and fEnabled else ''); except: reporter.errorXcpt('VM name "%s", fEnabled=%s' % (self.sName, fEnabled)); fRc = False; else: reporter.log('set VMMDevTesting=%s for "%s"' % (fEnabled, self.sName)); self.oTstDrv.processPendingEvents(); return fRc; # # Test eXecution Service methods. # def txsConnectViaTcp(self, cMsTimeout = 10*60000, sIpAddr = None, sMacAddr = None, fNatForwardingForTxs = False): """ Connects to the TXS using TCP/IP as transport. If no IP or MAC is addresses are specified, we'll get the IP from the guest additions. Returns a TxsConnectTask object on success, None + log on failure. """ # If the VM is configured with a NAT interface, connect to local host. fReversedSetup = False; fUseNatForTxs = False; if sIpAddr is None: try: oNic = self.oVM.getNetworkAdapter(0); if oNic.attachmentType == vboxcon.NetworkAttachmentType_NAT: fUseNatForTxs = True; except: reporter.errorXcpt(); return None; if fUseNatForTxs: fReversedSetup = not fNatForwardingForTxs; sIpAddr = '127.0.0.1'; # Kick off the task. try: oTask = TxsConnectTask(self, cMsTimeout, sIpAddr, sMacAddr, fReversedSetup, fnProcessEvents = self.oTstDrv.processPendingEvents); except: reporter.errorXcpt(); oTask = None; return oTask; def txsTryConnectViaTcp(self, cMsTimeout, sHostname, fReversed = False): """ Attempts to connect to a TXS instance. Returns True if a connection was established, False if not (only grave failures are logged as errors). Note! The timeout is more of a guideline... """ if sHostname is None or sHostname.strip() == '': raise base.GenError('Empty sHostname is not implemented yet'); oTxsSession = txsclient.tryOpenTcpSession(cMsTimeout, sHostname, fReversedSetup = fReversed, cMsIdleFudge = cMsTimeout // 2, fnProcessEvents = self.oTstDrv.processPendingEvents); if oTxsSession is None: return False; # Wait for the connect task to time out. self.oTstDrv.addTask(oTxsSession); self.oTstDrv.processPendingEvents(); oRc = self.oTstDrv.waitForTasks(cMsTimeout); self.oTstDrv.removeTask(oTxsSession); if oRc != oTxsSession: if oRc is not None: reporter.log('oRc=%s, expected %s' % (oRc, oTxsSession)); self.oTstDrv.processPendingEvents(); oTxsSession.cancelTask(); # this is synchronous return False; # Check the status. reporter.log2('TxsSession is ready, isSuccess() -> %s.' % (oTxsSession.isSuccess(),)); if not oTxsSession.isSuccess(): return False; reporter.log2('Disconnecting from TXS...'); return oTxsSession.syncDisconnect(); class TxsConnectTask(TdTaskBase): """ Class that takes care of connecting to a VM. """ class TxsConnectTaskVBoxCallback(vbox.VirtualBoxEventHandlerBase): """ Class for looking for IPv4 address changes on interface 0.""" def __init__(self, dArgs): vbox.VirtualBoxEventHandlerBase.__init__(self, dArgs); self.oParentTask = dArgs['oParentTask']; self.sMachineId = dArgs['sMachineId']; def onGuestPropertyChange(self, sMachineId, sName, sValue, sFlags): """Look for IP address.""" reporter.log2('onGuestPropertyChange(,%s,%s,%s,%s)' % (sMachineId, sName, sValue, sFlags)); if sMachineId == self.sMachineId \ and sName == '/VirtualBox/GuestInfo/Net/0/V4/IP': oParentTask = self.oParentTask; if oParentTask: oParentTask._setIp(sValue); # pylint: disable=protected-access def __init__(self, oSession, cMsTimeout, sIpAddr, sMacAddr, fReversedSetup, fnProcessEvents = None): TdTaskBase.__init__(self, utils.getCallerName()); self.cMsTimeout = cMsTimeout; self.fnProcessEvents = fnProcessEvents; self.sIpAddr = None; self.sNextIpAddr = None; self.sMacAddr = sMacAddr; self.fReversedSetup = fReversedSetup; self.oVBoxEventHandler = None; self.oTxsSession = None; # Skip things we don't implement. if sMacAddr is not None: reporter.error('TxsConnectTask does not implement sMacAddr yet'); raise base.GenError(); reporter.log2('TxsConnectTask: sIpAddr=%s fReversedSetup=%s' % (sIpAddr, fReversedSetup)) if fReversedSetup is True: self._openTcpSession(sIpAddr, fReversedSetup = True); elif sIpAddr is not None and sIpAddr.strip() != '': self._openTcpSession(sIpAddr, cMsIdleFudge = 5000); else: # # If we've got no IP address, register callbacks that listens for # the primary network adaptor of the VM to set a IPv4 guest prop. # Note! The order in which things are done here is kind of important. # # 0. The caller zaps the property before starting the VM. #try: # oSession.delGuestPropertyValue('/VirtualBox/GuestInfo/Net/0/V4/IP'); #except: # reporter.logXcpt(); # 1. Register the callback / event listener object. dArgs = {'oParentTask':self, 'sMachineId':oSession.o.machine.id}; self.oVBoxEventHandler = oSession.oVBox.registerDerivedEventHandler(self.TxsConnectTaskVBoxCallback, dArgs); # 2. Query the guest properties. try: sIpAddr = oSession.getGuestPropertyValue('/VirtualBox/GuestInfo/Net/0/V4/IP'); except: reporter.errorXcpt('IMachine::getGuestPropertyValue("/VirtualBox/GuestInfo/Net/0/V4/IP") failed'); self._deregisterEventHandler(); raise; else: if sIpAddr is not None: self._setIp(sIpAddr); # end __init__ def __del__(self): """ Make sure we deregister the callback. """ self._deregisterEventHandler(); return TdTaskBase.__del__(self); def toString(self): return '<%s cMsTimeout=%s, sIpAddr=%s, sNextIpAddr=%s, sMacAddr=%s, fReversedSetup=%s,' \ ' oTxsSession=%s oVBoxEventHandler=%s>' \ % (TdTaskBase.toString(self), self.cMsTimeout, self.sIpAddr, self.sNextIpAddr, self.sMacAddr, self.fReversedSetup, self.oTxsSession, self.oVBoxEventHandler); def _deregisterEventHandler(self): """Deregisters the event handler.""" fRc = True; oVBoxEventHandler = self.oVBoxEventHandler; if oVBoxEventHandler is not None: self.oVBoxEventHandler = None; fRc = oVBoxEventHandler.unregister(); oVBoxEventHandler.oParentTask = None; # Try avoid cylic deps. return fRc; def _setIp(self, sIpAddr, fInitCall = False): """Called when we get an IP. Will create a TXS session and signal the task.""" sIpAddr = sIpAddr.strip(); if sIpAddr is not None \ and sIpAddr != '': if vbox.isIpAddrValid(sIpAddr) or fInitCall: try: for s in sIpAddr.split('.'): i = int(s); if str(i) != s: raise Exception(); except: reporter.fatalXcpt(); else: reporter.log('TxsConnectTask: opening session to ip "%s"' % (sIpAddr)); self._openTcpSession(sIpAddr, cMsIdleFudge = 5000); return None; reporter.log('TxsConnectTask: Ignoring Bad ip "%s"' % (sIpAddr)); else: reporter.log2('TxsConnectTask: Ignoring empty ip "%s"' % (sIpAddr)); return None; def _openTcpSession(self, sIpAddr, uPort = None, fReversedSetup = False, cMsIdleFudge = 0): """ Calls txsclient.openTcpSession and switches our task to reflect the state of the subtask. """ self.oCv.acquire(); if self.oTxsSession is None: reporter.log2('_openTcpSession: sIpAddr=%s, uPort=%d, fReversedSetup=%s' % (sIpAddr, uPort if uPort is not None else 0, fReversedSetup)); self.sIpAddr = sIpAddr; self.oTxsSession = txsclient.openTcpSession(self.cMsTimeout, sIpAddr, uPort, fReversedSetup, cMsIdleFudge, fnProcessEvents = self.fnProcessEvents); self.oTxsSession.setTaskOwner(self); else: self.sNextIpAddr = sIpAddr; reporter.log2('_openTcpSession: sNextIpAddr=%s' % (sIpAddr,)); self.oCv.release(); return None; def notifyAboutReadyTask(self, oTxsSession): """ Called by the TXS session task when it's done. We'll signal the task completed or retry depending on the result. """ self.oCv.acquire(); # Disassociate ourselves with the session (avoid cyclic ref) oTxsSession.setTaskOwner(None); fSuccess = oTxsSession.isSuccess(); if self.oTxsSession is not None: if not fSuccess: self.oTxsSession = None; if fSuccess and self.fReversedSetup: self.sIpAddr = oTxsSession.oTransport.sHostname; else: fSuccess = False; # Signal done, or retry? fDeregister = False; if fSuccess \ or self.fReversedSetup \ or self.getAgeAsMs() >= self.cMsTimeout: self.signalTaskLocked(); fDeregister = True; else: sIpAddr = self.sNextIpAddr if self.sNextIpAddr is not None else self.sIpAddr; self._openTcpSession(sIpAddr, cMsIdleFudge = 5000); self.oCv.release(); # If we're done, deregister the callback (w/o owning lock). It will if fDeregister: self._deregisterEventHandler(); return True; # # Public methods # def getResult(self): """ Returns the connected TXS session object on success. Returns None on failure or if the task has not yet completed. """ self.oCv.acquire(); oTxsSession = self.oTxsSession; self.oCv.release(); if oTxsSession is not None and not oTxsSession.isSuccess(): oTxsSession = None; return oTxsSession; def cancelTask(self): """ Cancels the task. """ self._deregisterEventHandler(); # (make sure to avoid cyclic fun) self.oCv.acquire(); if not self.fSignalled: oTxsSession = self.oTxsSession; if oTxsSession is not None: self.oCv.release(); oTxsSession.setTaskOwner(None); oTxsSession.cancelTask(); oTxsSession.waitForTask(1000); self.oCv.acquire(); self.signalTaskLocked(); self.oCv.release(); return True; class AdditionsStatusTask(TdTaskBase): """ Class that takes care of waiting till the guest additions are in a given state. """ class AdditionsStatusTaskCallback(vbox.EventHandlerBase): """ Class for looking for IPv4 address changes on interface 0.""" def __init__(self, dArgs): self.oParentTask = dArgs['oParentTask']; vbox.EventHandlerBase.__init__(self, dArgs, self.oParentTask.oSession.fpApiVer, 'AdditionsStatusTaskCallback/%s' % (self.oParentTask.oSession.sName,)); def handleEvent(self, oEvt): try: enmType = oEvt.type; except: reporter.errorXcpt(); else: reporter.log2('AdditionsStatusTaskCallback:handleEvent: enmType=%s' % (enmType,)); if enmType == vboxcon.VBoxEventType_OnGuestAdditionsStatusChanged: oParentTask = self.oParentTask; if oParentTask: oParentTask.pollTask(); # end def __init__(self, oSession, oIGuest, cMsTimeout = 120000, aenmWaitForRunLevels = None, aenmWaitForActive = None, aenmWaitForInactive = None): """ aenmWaitForRunLevels - List of run level values to wait for (success if one matches). aenmWaitForActive - List facilities (type values) that must be active. aenmWaitForInactive - List facilities (type values) that must be inactive. The default is to wait for AdditionsRunLevelType_Userland if all three lists are unspecified or empty. """ TdTaskBase.__init__(self, utils.getCallerName()); self.oSession = oSession # type: vboxwrappers.SessionWrapper self.oIGuest = oIGuest; self.cMsTimeout = cMsTimeout; self.fSucceeded = False; self.oVBoxEventHandler = None; self.aenmWaitForRunLevels = aenmWaitForRunLevels if aenmWaitForRunLevels else []; self.aenmWaitForActive = aenmWaitForActive if aenmWaitForActive else []; self.aenmWaitForInactive = aenmWaitForInactive if aenmWaitForInactive else []; # Provide a sensible default if nothing is given. if not self.aenmWaitForRunLevels and not self.aenmWaitForActive and not self.aenmWaitForInactive: self.aenmWaitForRunLevels = [vboxcon.AdditionsRunLevelType_Userland,]; # Register the event handler on hosts which has it: if oSession.fpApiVer >= 6.1 or hasattr(vboxcon, 'VBoxEventType_OnGuestAdditionsStatusChanged'): aenmEvents = (vboxcon.VBoxEventType_OnGuestAdditionsStatusChanged,); dArgs = { 'oParentTask': self, }; self.oVBoxEventHandler = vbox.EventHandlerBase.registerDerivedEventHandler(oSession.oVBoxMgr, oSession.fpApiVer, self.AdditionsStatusTaskCallback, dArgs, oIGuest, 'IGuest', 'AdditionsStatusTaskCallback', aenmEvents = aenmEvents); reporter.log2('AdditionsStatusTask: %s' % (self.toString(), )); def __del__(self): """ Make sure we deregister the callback. """ self._deregisterEventHandler(); self.oIGuest = None; return TdTaskBase.__del__(self); def toString(self): return '<%s cMsTimeout=%s, fSucceeded=%s, aenmWaitForRunLevels=%s, aenmWaitForActive=%s, aenmWaitForInactive=%s, ' \ 'oVBoxEventHandler=%s>' \ % (TdTaskBase.toString(self), self.cMsTimeout, self.fSucceeded, self.aenmWaitForRunLevels, self.aenmWaitForActive, self.aenmWaitForInactive, self.oVBoxEventHandler,); def _deregisterEventHandler(self): """Deregisters the event handler.""" fRc = True; oVBoxEventHandler = self.oVBoxEventHandler; if oVBoxEventHandler is not None: self.oVBoxEventHandler = None; fRc = oVBoxEventHandler.unregister(); oVBoxEventHandler.oParentTask = None; # Try avoid cylic deps. return fRc; def _poll(self): """ Internal worker for pollTask() that returns the new signalled state. """ # # Check if any of the runlevels we wait for have been reached: # if self.aenmWaitForRunLevels: try: enmRunLevel = self.oIGuest.additionsRunLevel; except: reporter.errorXcpt(); return True; if enmRunLevel not in self.aenmWaitForRunLevels: reporter.log2('AdditionsStatusTask/poll: enmRunLevel=%s not in %s' % (enmRunLevel, self.aenmWaitForRunLevels,)); return False; reporter.log2('AdditionsStatusTask/poll: enmRunLevel=%s matched %s!' % (enmRunLevel, self.aenmWaitForRunLevels,)); # # Check for the facilities that must all be active. # for enmFacility in self.aenmWaitForActive: try: (enmStatus, _) = self.oIGuest.getFacilityStatus(enmFacility); except: reporter.errorXcpt('enmFacility=%s' % (enmFacility,)); return True; if enmStatus != vboxcon.AdditionsFacilityStatus_Active: reporter.log2('AdditionsStatusTask/poll: enmFacility=%s not active: %s' % (enmFacility, enmStatus,)); return False; # # Check for the facilities that must all be inactive or terminated. # for enmFacility in self.aenmWaitForInactive: try: (enmStatus, _) = self.oIGuest.getFacilityStatus(enmFacility); except: reporter.errorXcpt('enmFacility=%s' % (enmFacility,)); return True; if enmStatus not in (vboxcon.AdditionsFacilityStatus_Inactive, vboxcon.AdditionsFacilityStatus_Terminated): reporter.log2('AdditionsStatusTask/poll: enmFacility=%s not inactive: %s' % (enmFacility, enmStatus,)); return False; reporter.log('AdditionsStatusTask: Poll succeeded, signalling...'); self.fSucceeded = True; return True; # # Task methods # def pollTask(self, fLocked = False): """ Overridden pollTask method. """ if not fLocked: self.lockTask(); fDeregister = False; fRc = self.fSignalled; if not fRc: fRc = self._poll(); if fRc or self.getAgeAsMs() >= self.cMsTimeout: self.signalTaskLocked(); fDeregister = True; if not fLocked: self.unlockTask(); # If we're done, deregister the event callback (w/o owning lock). if fDeregister: self._deregisterEventHandler(); return fRc; def getResult(self): """ Returns true if the we succeeded. Returns false if not. If the task is signalled already, then we encountered a problem while polling. """ return self.fSucceeded; def cancelTask(self): """ Cancels the task. Just to actively disengage the event handler. """ self._deregisterEventHandler(); return True;