summaryrefslogtreecommitdiffstats
path: root/src/VBox/ValidationKit/testdriver/vboxwrappers.py
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-11 08:17:27 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-11 08:17:27 +0000
commitf215e02bf85f68d3a6106c2a1f4f7f063f819064 (patch)
tree6bb5b92c046312c4e95ac2620b10ddf482d3fa8b /src/VBox/ValidationKit/testdriver/vboxwrappers.py
parentInitial commit. (diff)
downloadvirtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.tar.xz
virtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.zip
Adding upstream version 7.0.14-dfsg.upstream/7.0.14-dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/VBox/ValidationKit/testdriver/vboxwrappers.py')
-rwxr-xr-xsrc/VBox/ValidationKit/testdriver/vboxwrappers.py3666
1 files changed, 3666 insertions, 0 deletions
diff --git a/src/VBox/ValidationKit/testdriver/vboxwrappers.py b/src/VBox/ValidationKit/testdriver/vboxwrappers.py
new file mode 100755
index 00000000..179e9cdc
--- /dev/null
+++ b/src/VBox/ValidationKit/testdriver/vboxwrappers.py
@@ -0,0 +1,3666 @@
+# -*- coding: utf-8 -*-
+# $Id: vboxwrappers.py $
+# pylint: disable=too-many-lines
+
+"""
+VirtualBox Wrapper Classes
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2010-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox 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.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+# 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;
+ elif sController == "VirtIO SCSI Controller":
+ eBus = vboxcon.StorageBus_VirtioSCSI;
+ eType = vboxcon.StorageControllerType_VirtioSCSI;
+ 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_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';
+ if hasattr(vboxcon, 'MachineState_FaultTolerantSyncing'):
+ if eState == vboxcon.MachineState_FaultTolerantSyncing: return 'FaultTolerantSyncing';
+ if hasattr(vboxcon, 'MachineState_AbortedSaved'): # since r147033 / 7.0
+ if eState == vboxcon.MachineState_AbortedSaved: return 'Aborted-Saved';
+ 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 {};
+ 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;
+ cMsToWait = min(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;
+ cMsSleep = min(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 hasattr(vboxcon, 'MachineState_FaultTolerantSyncing'):
+ 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 fIgnore:
+ self.o = None; # Must prevent a retry during GC.
+ else:
+ 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 setIommuType(self, eType):
+ """
+ Sets the IOMMU type.
+ Returns True on success and False on failure. Error information is logged.
+ """
+ # Supported.
+ if self.fpApiVer < 6.2 or not hasattr(vboxcon, 'IommuType_Intel') or not hasattr(vboxcon, 'IommuType_AMD'):
+ return True;
+ fRc = True;
+ try:
+ self.o.machine.iommuType = eType;
+ except:
+ reporter.errorXcpt('failed to set iommuType=%s for "%s"' % (eType, self.sName));
+ fRc = False;
+ else:
+ reporter.log('set iommuType=%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 == '':
+ # Create a new host-only interface.
+ reporter.log("Creating host only NIC ...");
+ try:
+ (oIProgress, oIHostOnly) = self.oVBox.host.createHostOnlyNetworkInterface();
+ oProgress = ProgressWrapper(oIProgress, self.oVBoxMgr, self.oTstDrv, 'Create host only NIC');
+ oProgress.wait();
+ if oProgress.logResult() is False:
+ return '';
+ sRetName = oIHostOnly.name;
+ except:
+ reporter.errorXcpt();
+ return '';
+ reporter.log("Created host only NIC: '%s'" % (sRetName,));
+
+ elif self.fpApiVer >= 7.0 and eAttachmentType == vboxcon.NetworkAttachmentType_HostOnlyNetwork:
+ aoHostNetworks = self.oVBoxMgr.getArray(self.oVBox, 'hostOnlyNetworks');
+ if aoHostNetworks:
+ sRetName = aoHostNetworks[0].networkName;
+ else:
+ try:
+ oHostOnlyNet = self.oVBox.createHostOnlyNetwork('Host-only Test Network');
+ oHostOnlyNet.lowerIP = '192.168.56.1';
+ oHostOnlyNet.upperIP = '192.168.56.199';
+ oHostOnlyNet.networkMask = '255.255.255.0';
+ sRetName = oHostOnlyNet.networkName;
+ except:
+ reporter.errorXcpt();
+ return '';
+
+ elif eAttachmentType == vboxcon.NetworkAttachmentType_Internal:
+ sRetName = 'VBoxTest';
+
+ elif eAttachmentType == vboxcon.NetworkAttachmentType_NAT:
+ sRetName = '';
+
+ else: ## @todo Support NetworkAttachmentType_NATNetwork
+ 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 self.fpApiVer >= 7.0 and eAttachmentType == vboxcon.NetworkAttachmentType_HostOnlyNetwork:
+ try:
+ oNic.hostOnlyNetwork = sName;
+ except:
+ reporter.errorXcpt('failed to set the hostOnlyNetwork 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 attachment type on slot %s to %s for VM "%s"' % (iNic, eAttachmentType, self.sName));
+ return True;
+
+ def setNicLocalhostReachable(self, fReachable, iNic = 0):
+ """
+ Sets whether the specified NIC can reach the host or not.
+ Only affects (enabled) NICs configured to NAT at the moment.
+
+ Returns True on success and False on failure. Error information is logged.
+ """
+ try:
+ oNic = self.o.machine.getNetworkAdapter(iNic);
+ except:
+ return reporter.errorXcpt('getNetworkAdapter(%s) failed for "%s"' % (iNic, self.sName,));
+
+ try:
+ if not oNic.enabled: # NIC not enabled? Nothing to do here.
+ return True;
+ except:
+ return reporter.errorXcpt('NIC enabled status (%s) failed for "%s"' % (iNic, self.sName,));
+
+ reporter.log('Setting "LocalhostReachable" for network adapter in slot %d to %s' % (iNic, fReachable));
+
+ try:
+ oNatEngine = oNic.NATEngine;
+ except:
+ return reporter.errorXcpt('Getting NIC NAT engine (%s) failed for "%s"' % (iNic, self.sName,));
+
+ try:
+ if hasattr(oNatEngine, "localhostReachable"):
+ oNatEngine.localhostReachable = fReachable;
+ else:
+ oNatEngine.LocalhostReachable = fReachable;
+ except:
+ return reporter.errorXcpt('LocalhostReachable (%s) failed for "%s"' % (iNic, 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 setLargePages(self, fUseLargePages):
+ """
+ Configures whether the VM should use large pages or not.
+ Returns True on success and False on failure. Error information is logged.
+ """
+ fRc = True;
+ try:
+ self.o.machine.setHWVirtExProperty(vboxcon.HWVirtExPropertyType_LargePages, fUseLargePages);
+ except:
+ reporter.errorXcpt('failed to set large pages of "%s" to %s' % (self.sName, fUseLargePages));
+ fRc = False;
+ else:
+ reporter.log('set the large pages of "%s" to %s' % (self.sName, fUseLargePages));
+ 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:
+ if self.fpApiVer >= 6.1 and hasattr(self.o.machine, 'graphicsAdapter'):
+ self.o.machine.graphicsAdapter.VRAMSize = cMB;
+ else:
+ 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 setVideoControllerType(self, eControllerType):
+ """
+ Set the video controller type of the VM.
+ Returns True on success and False on failure. Error information is logged.
+ """
+ fRc = True;
+ try:
+ if self.fpApiVer >= 6.1 and hasattr(self.o.machine, 'graphicsAdapter'):
+ self.o.machine.graphicsAdapter.graphicsControllerType = eControllerType;
+ else:
+ self.o.machine.graphicsControllerType = eControllerType;
+ except:
+ reporter.errorXcpt('failed to set the video controller type of "%s" to %s' % (self.sName, eControllerType));
+ fRc = False;
+ else:
+ reporter.log('set the video controller type of "%s" to %s' % (self.sName, eControllerType));
+ self.oTstDrv.processPendingEvents();
+ return fRc;
+
+ def setAccelerate3DEnabled(self, fEnabled):
+ """
+ Set the video controller type of the VM.
+ Returns True on success and False on failure. Error information is logged.
+ """
+ fRc = True;
+ try:
+ if self.fpApiVer >= 6.1 and hasattr(self.o.machine, 'graphicsAdapter'):
+ self.o.machine.graphicsAdapter.accelerate3DEnabled = fEnabled;
+ else:
+ self.o.machine.accelerate3DEnabled = fEnabled;
+ except:
+ reporter.errorXcpt('failed to set the accelerate3DEnabled of "%s" to %s' % (self.sName, fEnabled));
+ fRc = False;
+ else:
+ reporter.log('set the accelerate3DEnabled of "%s" to %s' % (self.sName, fEnabled));
+ 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, fEnableIn = False, fEnableOut = 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 fEnableIn: Whether to enable or disable audio input (default disable).
+ :param fEnableOut: Whether to enable or disable audio output (default enable).
+ :param eAudioDriverType: The audio driver type (vboxcon.AudioDriverType_XXX), picks something suitable
+ if None is passed (default).
+ """
+ try:
+ if self.fpApiVer >= 7.0:
+ oAdapter = self.o.machine.audioSettings.adapter;
+ else:
+ oAdapter = self.o.machine.audioAdapter;
+ except: return reporter.errorXcpt('Failed to get the audio adapter.');
+
+ try: oAdapter.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: oAdapter.audioDriver = eAudioDriverType;
+ except: return reporter.errorXcpt('Failed to set the audio driver to %s.' % (eAudioDriverType,))
+
+ try: oAdapter.enabled = fEnable;
+ except: return reporter.errorXcpt('Failed to set the "enabled" property to %s.' % (fEnable,));
+
+ try: oAdapter.enabledIn = fEnableIn;
+ except: return reporter.errorXcpt('Failed to set the "enabledIn" property to %s.' % (fEnable,));
+
+ try: oAdapter.enabledOut = fEnableOut;
+ except: return reporter.errorXcpt('Failed to set the "enabledOut" property to %s.' % (fEnable,));
+
+ reporter.log('set audio adapter type to %d, driver to %d, and enabled to %s (input is %s, output is %s)'
+ % (eAudioControllerType, eAudioDriverType, fEnable, fEnableIn, fEnableOut,));
+ 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
+
+ with open(sFilename, 'wb') as oFile: # pylint: disable=unspecified-encoding
+ oFile.write(aPngData)
+
+ 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 {};
+ 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, 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;
+ sMacAddr = None;
+ oIDhcpServer = None;
+ if sIpAddr is None:
+ try:
+ oNic = self.oVM.getNetworkAdapter(0);
+ enmAttachmentType = oNic.attachmentType;
+ if enmAttachmentType == vboxcon.NetworkAttachmentType_NAT:
+ fUseNatForTxs = True;
+ elif enmAttachmentType == vboxcon.NetworkAttachmentType_HostOnly and not sIpAddr:
+ # Get the MAC address and find the DHCP server.
+ sMacAddr = oNic.MACAddress;
+ sHostOnlyNIC = oNic.hostOnlyInterface;
+ oIHostOnlyIf = self.oVBox.host.findHostNetworkInterfaceByName(sHostOnlyNIC);
+ sHostOnlyNet = oIHostOnlyIf.networkName;
+ oIDhcpServer = self.oVBox.findDHCPServerByNetworkName(sHostOnlyNet);
+ 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, oIDhcpServer, 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, fWasDeleted):
+ """Look for IP address."""
+ reporter.log2('onGuestPropertyChange(,%s,%s,%s,%s,%s)' % (sMachineId, sName, sValue, sFlags, fWasDeleted));
+ 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, oIDhcpServer, fReversedSetup, fnProcessEvents = None):
+ TdTaskBase.__init__(self, utils.getCallerName(), fnProcessEvents = fnProcessEvents);
+ self.cMsTimeout = cMsTimeout;
+ self.fnProcessEvents = fnProcessEvents;
+ self.sIpAddr = None;
+ self.sNextIpAddr = None;
+ self.sMacAddr = sMacAddr;
+ self.oIDhcpServer = oIDhcpServer;
+ self.fReversedSetup = fReversedSetup;
+ self.oVBoxEventHandler = None;
+ self.oTxsSession = None;
+
+ # Check that the input makes sense:
+ if (sMacAddr is None) != (oIDhcpServer is None) \
+ or (sMacAddr and fReversedSetup) \
+ or (sMacAddr and sIpAddr):
+ reporter.error('TxsConnectTask sMacAddr=%s oIDhcpServer=%s sIpAddr=%s fReversedSetup=%s'
+ % (sMacAddr, oIDhcpServer, sIpAddr, fReversedSetup,));
+ 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);
+
+ #
+ # If the network adapter of the VM is host-only we can talk poll IDHCPServer
+ # for the guest IP, allowing us to detect it for VMs without guest additions.
+ # This will when we're polled.
+ #
+ if sMacAddr is not None:
+ assert self.oIDhcpServer is not None;
+
+
+ # 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;
+
+ def _pollDhcpServer(self):
+ """
+ Polls the DHCP server by MAC address in host-only setups.
+ """
+
+ if self.sIpAddr:
+ return False;
+
+ if self.oIDhcpServer is None or not self.sMacAddr:
+ return False;
+
+ try:
+ (sIpAddr, sState, secIssued, secExpire) = self.oIDhcpServer.findLeaseByMAC(self.sMacAddr, 0);
+ except:
+ reporter.log4Xcpt('sMacAddr=%s' % (self.sMacAddr,));
+ return False;
+
+ secNow = utils.secondsSinceUnixEpoch();
+ reporter.log2('dhcp poll: secNow=%s secExpire=%s secIssued=%s sState=%s sIpAddr=%s'
+ % (secNow, secExpire, secIssued, sState, sIpAddr,));
+ if secNow > secExpire or sState != 'acked' or not sIpAddr:
+ return False;
+
+ reporter.log('dhcp poll: sIpAddr=%s secExpire=%s (%s TTL) secIssued=%s (%s ago)'
+ % (sIpAddr, secExpire, secExpire - secNow, secIssued, secNow - secIssued,));
+ self._setIp(sIpAddr);
+ return True;
+
+ #
+ # Task methods
+ #
+
+ def pollTask(self, fLocked = False):
+ """
+ Overridden pollTask method.
+ """
+ self._pollDhcpServer();
+ return TdTaskBase.pollTask(self, fLocked);
+
+ #
+ # 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.log6('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;
+