summaryrefslogtreecommitdiffstats
path: root/src/VBox/ValidationKit/testdriver/vboxinstaller.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/ValidationKit/testdriver/vboxinstaller.py')
-rwxr-xr-xsrc/VBox/ValidationKit/testdriver/vboxinstaller.py1011
1 files changed, 1011 insertions, 0 deletions
diff --git a/src/VBox/ValidationKit/testdriver/vboxinstaller.py b/src/VBox/ValidationKit/testdriver/vboxinstaller.py
new file mode 100755
index 00000000..5ddca0f4
--- /dev/null
+++ b/src/VBox/ValidationKit/testdriver/vboxinstaller.py
@@ -0,0 +1,1011 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+VirtualBox Installer Wrapper Driver.
+
+This installs VirtualBox, starts a sub driver which does the real testing,
+and then uninstall VirtualBox afterwards. This reduces the complexity of the
+other VBox test drivers.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2010-2019 Oracle Corporation
+
+This file is part of VirtualBox Open Source Edition (OSE), as
+available from http://www.virtualbox.org. This file is free software;
+you can redistribute it and/or modify it under the terms of the GNU
+General Public License (GPL) as published by the Free Software
+Foundation, in version 2 as it comes in the "COPYING" file of the
+VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL) only, as it comes in the "COPYING.CDDL" file of the
+VirtualBox OSE distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+"""
+__version__ = "$Revision: 128281 $"
+
+
+# Standard Python imports.
+import os
+import sys
+import re
+#import socket
+import tempfile
+import time
+
+# Only the main script needs to modify the path.
+try: __file__
+except: __file__ = sys.argv[0];
+g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)));
+sys.path.append(g_ksValidationKitDir);
+
+# Validation Kit imports.
+from common import utils, webutils;
+from common.constants import rtexitcode;
+from testdriver import reporter;
+from testdriver.base import TestDriverBase;
+
+
+
+class VBoxInstallerTestDriver(TestDriverBase):
+ """
+ Implementation of a top level test driver.
+ """
+
+
+ ## State file indicating that we've skipped installation.
+ ksVar_Skipped = 'vboxinstaller-skipped';
+
+
+ def __init__(self):
+ TestDriverBase.__init__(self);
+ self._asSubDriver = []; # The sub driver and it's arguments.
+ self._asBuildUrls = []; # The URLs passed us on the command line.
+ self._asBuildFiles = []; # The downloaded file names.
+ self._fAutoInstallPuelExtPack = True;
+
+ #
+ # Base method we override
+ #
+
+ def showUsage(self):
+ rc = TestDriverBase.showUsage(self);
+ # 0 1 2 3 4 5 6 7 8
+ # 012345678901234567890123456789012345678901234567890123456789012345678901234567890
+ reporter.log('');
+ reporter.log('vboxinstaller Options:');
+ reporter.log(' --vbox-build <url[,url2[,..]]>');
+ reporter.log(' Comma separated list of URL to file to download and install or/and');
+ reporter.log(' unpack. URLs without a schema are assumed to be files on the');
+ reporter.log(' build share and will be copied off it.');
+ reporter.log(' --no-puel-extpack');
+ reporter.log(' Indicates that the PUEL extension pack should not be installed if found.');
+ reporter.log(' The default is to install it if found in the vbox-build.');
+ reporter.log(' --');
+ reporter.log(' Indicates the end of our parameters and the start of the sub');
+ reporter.log(' testdriver and its arguments.');
+ return rc;
+
+ def parseOption(self, asArgs, iArg):
+ """
+ Parse our arguments.
+ """
+ if asArgs[iArg] == '--':
+ # End of our parameters and start of the sub driver invocation.
+ iArg = self.requireMoreArgs(1, asArgs, iArg);
+ assert not self._asSubDriver;
+ self._asSubDriver = asArgs[iArg:];
+ self._asSubDriver[0] = self._asSubDriver[0].replace('/', os.path.sep);
+ iArg = len(asArgs) - 1;
+ elif asArgs[iArg] == '--vbox-build':
+ # List of files to copy/download and install.
+ iArg = self.requireMoreArgs(1, asArgs, iArg);
+ self._asBuildUrls = asArgs[iArg].split(',');
+ elif asArgs[iArg] == '--no-puel-extpack':
+ self._fAutoInstallPuelExtPack = False;
+ elif asArgs[iArg] == '--puel-extpack':
+ self._fAutoInstallPuelExtPack = True;
+ else:
+ return TestDriverBase.parseOption(self, asArgs, iArg);
+ return iArg + 1;
+
+ def completeOptions(self):
+ #
+ # Check that we've got what we need.
+ #
+ if not self._asBuildUrls:
+ reporter.error('No build files specfiied ("--vbox-build file1[,file2[...]]")');
+ return False;
+ if not self._asSubDriver:
+ reporter.error('No sub testdriver specified. (" -- test/stuff/tdStuff1.py args")');
+ return False;
+
+ #
+ # Construct _asBuildFiles as an array parallel to _asBuildUrls.
+ #
+ for sUrl in self._asBuildUrls:
+ sDstFile = os.path.join(self.sScratchPath, webutils.getFilename(sUrl));
+ self._asBuildFiles.append(sDstFile);
+
+ return TestDriverBase.completeOptions(self);
+
+ def actionExtract(self):
+ reporter.error('vboxinstall does not support extracting resources, you have to do that using the sub testdriver.');
+ return False;
+
+ def actionCleanupBefore(self):
+ """
+ Kills all VBox process we see.
+
+ This is only supposed to execute on a testbox so we don't need to go
+ all complicated wrt other users.
+ """
+ return self._killAllVBoxProcesses();
+
+ def actionConfig(self):
+ """
+ Install VBox and pass on the configure request to the sub testdriver.
+ """
+ fRc = self._installVBox();
+ if fRc is None: self._persistentVarSet(self.ksVar_Skipped, 'true');
+ else: self._persistentVarUnset(self.ksVar_Skipped);
+
+ ## @todo vbox.py still has bugs preventing us from invoking it seperately with each action.
+ if fRc is True and 'execute' not in self.asActions and 'all' not in self.asActions:
+ fRc = self._executeSubDriver([ 'verify', ]);
+ if fRc is True and 'execute' not in self.asActions and 'all' not in self.asActions:
+ fRc = self._executeSubDriver([ 'config', ]);
+ return fRc;
+
+ def actionExecute(self):
+ """
+ Execute the sub testdriver.
+ """
+ return self._executeSubDriver(self.asActions);
+
+ def actionCleanupAfter(self):
+ """
+ Forward this to the sub testdriver, then uninstall VBox.
+ """
+ fRc = True;
+ if 'execute' not in self.asActions and 'all' not in self.asActions:
+ fRc = self._executeSubDriver([ 'cleanup-after', ], fMaySkip = False);
+
+ if not self._killAllVBoxProcesses():
+ fRc = False;
+
+ if not self._uninstallVBox(self._persistentVarExists(self.ksVar_Skipped)):
+ fRc = False;
+
+ if utils.getHostOs() == 'darwin':
+ self._darwinUnmountDmg(fIgnoreError = True); # paranoia
+
+ if not TestDriverBase.actionCleanupAfter(self):
+ fRc = False;
+
+ return fRc;
+
+
+ def actionAbort(self):
+ """
+ Forward this to the sub testdriver first, then wipe all VBox like
+ processes, and finally do the pid file processing (again).
+ """
+ fRc1 = self._executeSubDriver([ 'abort', ], fMaySkip = False);
+ fRc2 = self._killAllVBoxProcesses();
+ fRc3 = TestDriverBase.actionAbort(self);
+ return fRc1 and fRc2 and fRc3;
+
+
+ #
+ # Persistent variables.
+ #
+ ## @todo integrate into the base driver. Persisten accross scratch wipes?
+
+ def __persistentVarCalcName(self, sVar):
+ """Returns the (full) filename for the given persistent variable."""
+ assert re.match(r'^[a-zA-Z0-9_-]*$', sVar) is not None;
+ return os.path.join(self.sScratchPath, 'persistent-%s.var' % (sVar,));
+
+ def _persistentVarSet(self, sVar, sValue = ''):
+ """
+ Sets a persistent variable.
+
+ Returns True on success, False + reporter.error on failure.
+
+ May raise exception if the variable name is invalid or something
+ unexpected happens.
+ """
+ sFull = self.__persistentVarCalcName(sVar);
+ try:
+ oFile = open(sFull, 'w');
+ if sValue:
+ oFile.write(sValue.encode('utf-8'));
+ oFile.close();
+ except:
+ reporter.errorXcpt('Error creating "%s"' % (sFull,));
+ return False;
+ return True;
+
+ def _persistentVarUnset(self, sVar):
+ """
+ Unsets a persistent variable.
+
+ Returns True on success, False + reporter.error on failure.
+
+ May raise exception if the variable name is invalid or something
+ unexpected happens.
+ """
+ sFull = self.__persistentVarCalcName(sVar);
+ if os.path.exists(sFull):
+ try:
+ os.unlink(sFull);
+ except:
+ reporter.errorXcpt('Error unlinking "%s"' % (sFull,));
+ return False;
+ return True;
+
+ def _persistentVarExists(self, sVar):
+ """
+ Checks if a persistent variable exists.
+
+ Returns true/false.
+
+ May raise exception if the variable name is invalid or something
+ unexpected happens.
+ """
+ return os.path.exists(self.__persistentVarCalcName(sVar));
+
+ def _persistentVarGet(self, sVar):
+ """
+ Gets the value of a persistent variable.
+
+ Returns variable value on success.
+ Returns None if the variable doesn't exist or if an
+ error (reported) occured.
+
+ May raise exception if the variable name is invalid or something
+ unexpected happens.
+ """
+ sFull = self.__persistentVarCalcName(sVar);
+ if not os.path.exists(sFull):
+ return None;
+ try:
+ oFile = open(sFull, 'r');
+ sValue = oFile.read().decode('utf-8');
+ oFile.close();
+ except:
+ reporter.errorXcpt('Error creating "%s"' % (sFull,));
+ return None;
+ return sValue;
+
+
+ #
+ # Helpers.
+ #
+
+ def _killAllVBoxProcesses(self):
+ """
+ Kills all virtual box related processes we find in the system.
+ """
+
+ for iIteration in range(22):
+ # Gather processes to kill.
+ aoTodo = [];
+ for oProcess in utils.processListAll():
+ sBase = oProcess.getBaseImageNameNoExeSuff();
+ if sBase is None:
+ continue;
+ sBase = sBase.lower();
+ if sBase in [ 'vboxsvc', 'virtualbox', 'virtualboxvm', 'vboxheadless', 'vboxmanage', 'vboxsdl', 'vboxwebsrv',
+ 'vboxautostart', 'vboxballoonctrl', 'vboxbfe', 'vboxextpackhelperapp', 'vboxnetdhcp',
+ 'vboxnetadpctl', 'vboxtestogl', 'vboxtunctl', 'vboxvmmpreload', 'vboxxpcomipcd', 'vmCreator', ]:
+ aoTodo.append(oProcess);
+ if sBase.startswith('virtualbox-') and sBase.endswith('-multiarch.exe'):
+ aoTodo.append(oProcess);
+ if iIteration in [0, 21] and sBase in [ 'windbg', 'gdb', 'gdb-i386-apple-darwin', ]:
+ reporter.log('Warning: debugger running: %s (%s)' % (oProcess.iPid, sBase,));
+ if not aoTodo:
+ return True;
+
+ # Kill.
+ for oProcess in aoTodo:
+ reporter.log('Loop #%d - Killing %s (%s, uid=%s)'
+ % ( iIteration, oProcess.iPid, oProcess.sImage if oProcess.sName is None else oProcess.sName,
+ oProcess.iUid, ));
+ utils.processKill(oProcess.iPid); # No mercy.
+
+ # Check if they're all dead like they should be.
+ time.sleep(0.1);
+ for oProcess in aoTodo:
+ if utils.processExists(oProcess.iPid):
+ time.sleep(2);
+ break;
+
+ return False;
+
+ def _executeSync(self, asArgs, fMaySkip = False):
+ """
+ Executes a child process synchronously.
+
+ Returns True if the process executed successfully and returned 0.
+ Returns None if fMaySkip is true and the child exits with RTEXITCODE_SKIPPED.
+ Returns False for all other cases.
+ """
+ reporter.log('Executing: %s' % (asArgs, ));
+ reporter.flushall();
+ try:
+ iRc = utils.processCall(asArgs, shell = False, close_fds = False);
+ except:
+ reporter.errorXcpt();
+ return False;
+ reporter.log('Exit code: %s (%s)' % (iRc, asArgs));
+ if fMaySkip and iRc == rtexitcode.RTEXITCODE_SKIPPED:
+ return None;
+ return iRc is 0;
+
+ def _sudoExecuteSync(self, asArgs):
+ """
+ Executes a sudo child process synchronously.
+ Returns a tuple [True, 0] if the process executed successfully
+ and returned 0, otherwise [False, rc] is returned.
+ """
+ reporter.log('Executing [sudo]: %s' % (asArgs, ));
+ reporter.flushall();
+ iRc = 0;
+ try:
+ iRc = utils.sudoProcessCall(asArgs, shell = False, close_fds = False);
+ except:
+ reporter.errorXcpt();
+ return (False, 0);
+ reporter.log('Exit code [sudo]: %s (%s)' % (iRc, asArgs));
+ return (iRc is 0, iRc);
+
+ def _executeSubDriver(self, asActions, fMaySkip = True):
+ """
+ Execute the sub testdriver with the specified action.
+ """
+ asArgs = list(self._asSubDriver)
+ asArgs.append('--no-wipe-clean');
+ asArgs.extend(asActions);
+ return self._executeSync(asArgs, fMaySkip = fMaySkip);
+
+ def _maybeUnpackArchive(self, sMaybeArchive, fNonFatal = False):
+ """
+ Attempts to unpack the given build file.
+ Updates _asBuildFiles.
+ Returns True/False. No exceptions.
+ """
+ def unpackFilter(sMember):
+ # type: (string) -> bool
+ """ Skips debug info. """
+ sLower = sMember.lower();
+ if sLower.endswith('.pdb'):
+ return False;
+ return True;
+
+ asMembers = utils.unpackFile(sMaybeArchive, self.sScratchPath, reporter.log,
+ reporter.log if fNonFatal else reporter.error,
+ fnFilter = unpackFilter);
+ if asMembers is None:
+ return False;
+ self._asBuildFiles.extend(asMembers);
+ return True;
+
+
+ def _installVBox(self):
+ """
+ Download / copy the build files into the scratch area and install them.
+ """
+ reporter.testStart('Installing VirtualBox');
+ reporter.log('CWD=%s' % (os.getcwd(),)); # curious
+
+ #
+ # Download the build files.
+ #
+ for i in range(len(self._asBuildUrls)):
+ if webutils.downloadFile(self._asBuildUrls[i], self._asBuildFiles[i],
+ self.sBuildPath, reporter.log, reporter.log) is not True:
+ reporter.testDone(fSkipped = True);
+ return None; # Failed to get binaries, probably deleted. Skip the test run.
+
+ #
+ # Unpack anything we know what is and append it to the build files
+ # list. This allows us to use VBoxAll*.tar.gz files.
+ #
+ for sFile in list(self._asBuildFiles):
+ if self._maybeUnpackArchive(sFile, fNonFatal = True) is not True:
+ reporter.testDone(fSkipped = True);
+ return None; # Failed to unpack. Probably local error, like busy
+ # DLLs on windows, no reason for failing the build.
+
+ #
+ # Go to system specific installation code.
+ #
+ sHost = utils.getHostOs()
+ if sHost == 'darwin': fRc = self._installVBoxOnDarwin();
+ elif sHost == 'linux': fRc = self._installVBoxOnLinux();
+ elif sHost == 'solaris': fRc = self._installVBoxOnSolaris();
+ elif sHost == 'win': fRc = self._installVBoxOnWindows();
+ else:
+ reporter.error('Unsupported host "%s".' % (sHost,));
+ if fRc is False:
+ reporter.testFailure('Installation error.');
+
+ #
+ # Install the extension pack.
+ #
+ if fRc is True and self._fAutoInstallPuelExtPack:
+ fRc = self._installExtPack();
+ if fRc is False:
+ reporter.testFailure('Extension pack installation error.');
+
+ # Some debugging...
+ try:
+ cMbFreeSpace = utils.getDiskUsage(self.sScratchPath);
+ reporter.log('Disk usage after VBox install: %d MB available at %s' % (cMbFreeSpace, self.sScratchPath,));
+ except:
+ reporter.logXcpt('Unable to get disk free space. Ignored. Continuing.');
+
+ reporter.testDone();
+ return fRc;
+
+ def _uninstallVBox(self, fIgnoreError = False):
+ """
+ Uninstall VirtualBox.
+ """
+ reporter.testStart('Uninstalling VirtualBox');
+
+ sHost = utils.getHostOs()
+ if sHost == 'darwin': fRc = self._uninstallVBoxOnDarwin();
+ elif sHost == 'linux': fRc = self._uninstallVBoxOnLinux();
+ elif sHost == 'solaris': fRc = self._uninstallVBoxOnSolaris(True);
+ elif sHost == 'win': fRc = self._uninstallVBoxOnWindows(True);
+ else:
+ reporter.error('Unsupported host "%s".' % (sHost,));
+ if fRc is False and not fIgnoreError:
+ reporter.testFailure('Uninstallation failed.');
+
+ fRc2 = self._uninstallAllExtPacks();
+ if not fRc2 and fRc:
+ fRc = fRc2;
+
+ reporter.testDone(fSkipped = (fRc is None));
+ return fRc;
+
+ def _findFile(self, sRegExp, fMandatory = False):
+ """
+ Returns the first build file that matches the given regular expression
+ (basename only).
+
+ Returns None if no match was found, logging it as an error if
+ fMandatory is set.
+ """
+ oRegExp = re.compile(sRegExp);
+
+ for sFile in self._asBuildFiles:
+ if oRegExp.match(os.path.basename(sFile)) and os.path.exists(sFile):
+ return sFile;
+
+ if fMandatory:
+ reporter.error('Failed to find a file matching "%s" in %s.' % (sRegExp, self._asBuildFiles,));
+ return None;
+
+ def _waitForTestManagerConnectivity(self, cSecTimeout):
+ """
+ Check and wait for network connectivity to the test manager.
+
+ This is used with the windows installation and uninstallation since
+ these usually disrupts network connectivity when installing the filter
+ driver. If we proceed to quickly, we might finish the test at a time
+ when we cannot report to the test manager and thus end up with an
+ abandonded test error.
+ """
+ cSecElapsed = 0;
+ secStart = utils.timestampSecond();
+ while reporter.checkTestManagerConnection() is False:
+ cSecElapsed = utils.timestampSecond() - secStart;
+ if cSecElapsed >= cSecTimeout:
+ reporter.log('_waitForTestManagerConnectivity: Giving up after %u secs.' % (cSecTimeout,));
+ return False;
+ time.sleep(2);
+
+ if cSecElapsed > 0:
+ reporter.log('_waitForTestManagerConnectivity: Waited %s secs.' % (cSecTimeout,));
+ return True;
+
+
+ #
+ # Darwin (Mac OS X).
+ #
+
+ def _darwinDmgPath(self):
+ """ Returns the path to the DMG mount."""
+ return os.path.join(self.sScratchPath, 'DmgMountPoint');
+
+ def _darwinUnmountDmg(self, fIgnoreError):
+ """
+ Umount any DMG on at the default mount point.
+ """
+ sMountPath = self._darwinDmgPath();
+ if not os.path.exists(sMountPath):
+ return True;
+
+ # Unmount.
+ fRc = self._executeSync(['hdiutil', 'detach', sMountPath ]);
+ if not fRc and not fIgnoreError:
+ reporter.error('Failed to unmount DMG at %s' % sMountPath);
+
+ # Remove dir.
+ try:
+ os.rmdir(sMountPath);
+ except:
+ if not fIgnoreError:
+ reporter.errorXcpt('Failed to remove directory %s' % sMountPath);
+ return fRc;
+
+ def _darwinMountDmg(self, sDmg):
+ """
+ Mount the DMG at the default mount point.
+ """
+ self._darwinUnmountDmg(fIgnoreError = True)
+
+ sMountPath = self._darwinDmgPath();
+ if not os.path.exists(sMountPath):
+ try:
+ os.mkdir(sMountPath, 0o755);
+ except:
+ reporter.logXcpt();
+ return False;
+
+ return self._executeSync(['hdiutil', 'attach', '-readonly', '-mount', 'required', '-mountpoint', sMountPath, sDmg, ]);
+
+ def _installVBoxOnDarwin(self):
+ """ Installs VBox on Mac OS X."""
+ sDmg = self._findFile('^VirtualBox-.*\\.dmg$');
+ if sDmg is None:
+ return False;
+
+ # Mount the DMG.
+ fRc = self._darwinMountDmg(sDmg);
+ if fRc is not True:
+ return False;
+
+ # Uninstall any previous vbox version first.
+ sUninstaller = os.path.join(self._darwinDmgPath(), 'VirtualBox_Uninstall.tool');
+ fRc, _ = self._sudoExecuteSync([sUninstaller, '--unattended',]);
+ if fRc is True:
+
+ # Install the package.
+ sPkg = os.path.join(self._darwinDmgPath(), 'VirtualBox.pkg');
+ fRc, _ = self._sudoExecuteSync(['installer', '-verbose', '-dumplog', '-pkg', sPkg, '-target', '/']);
+
+ # Unmount the DMG and we're done.
+ if not self._darwinUnmountDmg(fIgnoreError = False):
+ fRc = False;
+ return fRc;
+
+ def _uninstallVBoxOnDarwin(self):
+ """ Uninstalls VBox on Mac OS X."""
+
+ # Is VirtualBox installed? If not, don't try uninstall it.
+ sVBox = self._getVBoxInstallPath(fFailIfNotFound = False);
+ if sVBox is None:
+ return True;
+
+ # Find the dmg.
+ sDmg = self._findFile('^VirtualBox-.*\\.dmg$');
+ if sDmg is None:
+ return False;
+ if not os.path.exists(sDmg):
+ return True;
+
+ # Mount the DMG.
+ fRc = self._darwinMountDmg(sDmg);
+ if fRc is not True:
+ return False;
+
+ # Execute the uninstaller.
+ sUninstaller = os.path.join(self._darwinDmgPath(), 'VirtualBox_Uninstall.tool');
+ fRc, _ = self._sudoExecuteSync([sUninstaller, '--unattended',]);
+
+ # Unmount the DMG and we're done.
+ if not self._darwinUnmountDmg(fIgnoreError = False):
+ fRc = False;
+ return fRc;
+
+ #
+ # GNU/Linux
+ #
+
+ def _installVBoxOnLinux(self):
+ """ Installs VBox on Linux."""
+ sRun = self._findFile('^VirtualBox-.*\\.run$');
+ if sRun is None:
+ return False;
+ utils.chmodPlusX(sRun);
+
+ # Install the new one.
+ fRc, _ = self._sudoExecuteSync([sRun,]);
+ return fRc;
+
+ def _uninstallVBoxOnLinux(self):
+ """ Uninstalls VBox on Linux."""
+
+ # Is VirtualBox installed? If not, don't try uninstall it.
+ sVBox = self._getVBoxInstallPath(fFailIfNotFound = False);
+ if sVBox is None:
+ return True;
+
+ # Find the .run file and use it.
+ sRun = self._findFile('^VirtualBox-.*\\.run$', fMandatory = False);
+ if sRun is not None:
+ utils.chmodPlusX(sRun);
+ fRc, _ = self._sudoExecuteSync([sRun, 'uninstall']);
+ return fRc;
+
+ # Try the installed uninstaller.
+ for sUninstaller in [os.path.join(sVBox, 'uninstall.sh'), '/opt/VirtualBox/uninstall.sh', ]:
+ if os.path.isfile(sUninstaller):
+ reporter.log('Invoking "%s"...' % (sUninstaller,));
+ fRc, _ = self._sudoExecuteSync([sUninstaller, 'uninstall']);
+ return fRc;
+
+ reporter.log('Did not find any VirtualBox install to uninstall.');
+ return True;
+
+
+ #
+ # Solaris
+ #
+
+ def _generateAutoResponseOnSolaris(self):
+ """
+ Generates an autoresponse file on solaris, returning the name.
+ None is return on failure.
+ """
+ sPath = os.path.join(self.sScratchPath, 'SolarisAutoResponse');
+ oFile = utils.openNoInherit(sPath, 'wt');
+ oFile.write('basedir=default\n'
+ 'runlevel=nocheck\n'
+ 'conflict=quit\n'
+ 'setuid=nocheck\n'
+ 'action=nocheck\n'
+ 'partial=quit\n'
+ 'instance=unique\n'
+ 'idepend=quit\n'
+ 'rdepend=quit\n'
+ 'space=quit\n'
+ 'mail=\n');
+ oFile.close();
+ return sPath;
+
+ def _installVBoxOnSolaris(self):
+ """ Installs VBox on Solaris."""
+ sPkg = self._findFile('^VirtualBox-.*\\.pkg$', fMandatory = False);
+ if sPkg is None:
+ sTar = self._findFile('^VirtualBox-.*-SunOS-.*\\.tar.gz$', fMandatory = False);
+ if sTar is not None:
+ if self._maybeUnpackArchive(sTar) is not True:
+ return False;
+ sPkg = self._findFile('^VirtualBox-.*\\.pkg$', fMandatory = True);
+ sRsp = self._findFile('^autoresponse$', fMandatory = True);
+ if sPkg is None or sRsp is None:
+ return False;
+
+ # Uninstall first (ignore result).
+ self._uninstallVBoxOnSolaris(False);
+
+ # Install the new one.
+ fRc, _ = self._sudoExecuteSync(['pkgadd', '-d', sPkg, '-n', '-a', sRsp, 'SUNWvbox']);
+ return fRc;
+
+ def _uninstallVBoxOnSolaris(self, fRestartSvcConfigD):
+ """ Uninstalls VBox on Solaris."""
+ reporter.flushall();
+ if utils.processCall(['pkginfo', '-q', 'SUNWvbox']) != 0:
+ return True;
+ sRsp = self._generateAutoResponseOnSolaris();
+ fRc, _ = self._sudoExecuteSync(['pkgrm', '-n', '-a', sRsp, 'SUNWvbox']);
+
+ #
+ # Restart the svc.configd as it has a tendency to clog up with time and
+ # become unresponsive. It will handle SIGHUP by exiting the sigwait()
+ # look in the main function and shut down the service nicely (backend_fini).
+ # The restarter will then start a new instance of it.
+ #
+ if fRestartSvcConfigD:
+ time.sleep(1); # Give it a chance to flush pkgrm stuff.
+ self._sudoExecuteSync(['pkill', '-HUP', 'svc.configd']);
+ time.sleep(5); # Spare a few cpu cycles it to shutdown and restart.
+
+ return fRc;
+
+ #
+ # Windows
+ #
+
+ ## VBox windows services we can query the status of.
+ kasWindowsServices = [ 'vboxdrv', 'vboxusbmon', 'vboxnetadp', 'vboxnetflt', 'vboxnetlwf' ];
+
+ def _installVBoxOnWindows(self):
+ """ Installs VBox on Windows."""
+ sExe = self._findFile('^VirtualBox-.*-(MultiArch|Win).exe$');
+ if sExe is None:
+ return False;
+
+ # TEMPORARY HACK - START
+ # It seems that running the NDIS cleanup script upon uninstallation is not
+ # a good idea, so let's run it before installing VirtualBox.
+ #sHostName = socket.getfqdn();
+ #if not sHostName.startswith('testboxwin3') \
+ # and not sHostName.startswith('testboxharp2') \
+ # and not sHostName.startswith('wei01-b6ka-3') \
+ # and utils.getHostOsVersion() in ['8', '8.1', '9', '2008Server', '2008ServerR2', '2012Server']:
+ # reporter.log('Peforming extra NDIS cleanup...');
+ # sMagicScript = os.path.abspath(os.path.join(g_ksValidationKitDir, 'testdriver', 'win-vbox-net-uninstall.ps1'));
+ # fRc2, _ = self._sudoExecuteSync(['powershell.exe', '-Command', 'set-executionpolicy unrestricted']);
+ # if not fRc2:
+ # reporter.log('set-executionpolicy failed.');
+ # self._sudoExecuteSync(['powershell.exe', '-Command', 'get-executionpolicy']);
+ # fRc2, _ = self._sudoExecuteSync(['powershell.exe', '-File', sMagicScript]);
+ # if not fRc2:
+ # reporter.log('NDIS cleanup failed.');
+ # TEMPORARY HACK - END
+
+ # Uninstall any previous vbox version first.
+ fRc = self._uninstallVBoxOnWindows(True);
+ if fRc is not True:
+ return None; # There shouldn't be anything to uninstall, and if there is, it's not our fault.
+
+ # Install the new one.
+ asArgs = [sExe, '-vvvv', '--silent', '--logging'];
+ asArgs.extend(['--msiparams', 'REBOOT=ReallySuppress']);
+ sVBoxInstallPath = os.environ.get('VBOX_INSTALL_PATH', None);
+ if sVBoxInstallPath is not None:
+ asArgs.extend(['INSTALLDIR="%s"' % (sVBoxInstallPath,)]);
+ fRc2, iRc = self._sudoExecuteSync(asArgs);
+ if fRc2 is False:
+ if iRc == 3010: # ERROR_SUCCESS_REBOOT_REQUIRED
+ reporter.log('Note: Installer required a reboot to complete installation');
+ # Optional, don't fail.
+ else:
+ fRc = False;
+ sLogFile = os.path.join(tempfile.gettempdir(), 'VirtualBox', 'VBoxInstallLog.txt');
+ if sLogFile is not None \
+ and os.path.isfile(sLogFile):
+ reporter.addLogFile(sLogFile, 'log/installer', "Verbose MSI installation log file");
+ self._waitForTestManagerConnectivity(30);
+ return fRc;
+
+ def _isProcessPresent(self, sName):
+ """ Checks whether the named process is present or not. """
+ for oProcess in utils.processListAll():
+ sBase = oProcess.getBaseImageNameNoExeSuff();
+ if sBase is not None and sBase.lower() == sName:
+ return True;
+ return False;
+
+ def _killProcessesByName(self, sName, sDesc, fChildren = False):
+ """ Kills the named process, optionally including children. """
+ cKilled = 0;
+ aoProcesses = utils.processListAll();
+ for oProcess in aoProcesses:
+ sBase = oProcess.getBaseImageNameNoExeSuff();
+ if sBase is not None and sBase.lower() == sName:
+ reporter.log('Killing %s process: %s (%s)' % (sDesc, oProcess.iPid, sBase));
+ utils.processKill(oProcess.iPid);
+ cKilled += 1;
+
+ if fChildren:
+ for oChild in aoProcesses:
+ if oChild.iParentPid == oProcess.iPid and oChild.iParentPid is not None:
+ reporter.log('Killing %s child process: %s (%s)' % (sDesc, oChild.iPid, sBase));
+ utils.processKill(oChild.iPid);
+ cKilled += 1;
+ return cKilled;
+
+ def _terminateProcessesByNameAndArgSubstr(self, sName, sArg, sDesc):
+ """
+ Terminates the named process using taskkill.exe, if any of its args
+ contains the passed string.
+ """
+ cKilled = 0;
+ aoProcesses = utils.processListAll();
+ for oProcess in aoProcesses:
+ sBase = oProcess.getBaseImageNameNoExeSuff();
+ if sBase is not None and sBase.lower() == sName and any(sArg in s for s in oProcess.asArgs):
+
+ reporter.log('Killing %s process: %s (%s)' % (sDesc, oProcess.iPid, sBase));
+ self._executeSync(['taskkill.exe', '/pid', '%u' % (oProcess.iPid,)]);
+ cKilled += 1;
+ return cKilled;
+
+ def _uninstallVBoxOnWindows(self, fIgnoreServices = False):
+ """
+ Uninstalls VBox on Windows, all installations we find to be on the safe side...
+ """
+
+ import win32com.client; # pylint: disable=F0401
+ win32com.client.gencache.EnsureModule('{000C1092-0000-0000-C000-000000000046}', 1033, 1, 0);
+ oInstaller = win32com.client.Dispatch('WindowsInstaller.Installer',
+ resultCLSID = '{000C1090-0000-0000-C000-000000000046}')
+
+ # Search installed products for VirtualBox.
+ asProdCodes = [];
+ for sProdCode in oInstaller.Products:
+ try:
+ sProdName = oInstaller.ProductInfo(sProdCode, "ProductName");
+ except:
+ reporter.logXcpt();
+ continue;
+ #reporter.log('Info: %s=%s' % (sProdCode, sProdName));
+ if sProdName.startswith('Oracle VM VirtualBox') \
+ or sProdName.startswith('Sun VirtualBox'):
+ asProdCodes.append([sProdCode, sProdName]);
+
+ # Before we start uninstalling anything, just ruthlessly kill any cdb,
+ # msiexec, drvinst and some rundll process we might find hanging around.
+ if self._isProcessPresent('rundll32'):
+ cTimes = 0;
+ while cTimes < 3:
+ cTimes += 1;
+ cKilled = self._terminateProcessesByNameAndArgSubstr('rundll32', 'InstallSecurityPromptRunDllW',
+ 'MSI driver installation');
+ if cKilled <= 0:
+ break;
+ time.sleep(10); # Give related drvinst process a chance to clean up after we killed the verification dialog.
+
+ if self._isProcessPresent('drvinst'):
+ time.sleep(15); # In the hope that it goes away.
+ cTimes = 0;
+ while cTimes < 4:
+ cTimes += 1;
+ cKilled = self._killProcessesByName('drvinst', 'MSI driver installation', True);
+ if cKilled <= 0:
+ break;
+ time.sleep(10); # Give related MSI process a chance to clean up after we killed the driver installer.
+
+ if self._isProcessPresent('msiexec'):
+ cTimes = 0;
+ while cTimes < 3:
+ reporter.log('found running msiexec process, waiting a bit...');
+ time.sleep(20) # In the hope that it goes away.
+ if not self._isProcessPresent('msiexec'):
+ break;
+ cTimes += 1;
+ ## @todo this could also be the msiexec system service, try to detect this case!
+ if cTimes >= 6:
+ cKilled = self._killProcessesByName('msiexec', 'MSI driver installation');
+ if cKilled > 0:
+ time.sleep(16); # fudge.
+
+ # cdb.exe sometimes stays running (from utils.getProcessInfo), blocking
+ # the scratch directory. No idea why.
+ if self._isProcessPresent('cdb'):
+ cTimes = 0;
+ while cTimes < 3:
+ cKilled = self._killProcessesByName('cdb', 'cdb.exe from getProcessInfo');
+ if cKilled <= 0:
+ break;
+ time.sleep(2); # fudge.
+
+ # Do the uninstalling.
+ fRc = True;
+ sLogFile = os.path.join(self.sScratchPath, 'VBoxUninstallLog.txt');
+ for sProdCode, sProdName in asProdCodes:
+ reporter.log('Uninstalling %s (%s)...' % (sProdName, sProdCode));
+ fRc2, iRc = self._sudoExecuteSync(['msiexec', '/uninstall', sProdCode, '/quiet', '/passive', '/norestart',
+ '/L*v', '%s' % (sLogFile), ]);
+ if fRc2 is False:
+ if iRc == 3010: # ERROR_SUCCESS_REBOOT_REQUIRED
+ reporter.log('Note: Uninstaller required a reboot to complete uninstallation');
+ reporter.addLogFile(sLogFile, 'log/uninstaller_reboot', "Verbose MSI uninstallation log file (reboot required)");
+ # Optional, don't fail.
+ else:
+ fRc = False;
+
+ self._waitForTestManagerConnectivity(30);
+ if fRc is False and os.path.isfile(sLogFile):
+ reporter.addLogFile(sLogFile, 'log/uninstaller', "Verbose MSI uninstallation log file");
+
+ # Log driver service states (should ls \Driver\VBox* and \Device\VBox*).
+ for sService in self.kasWindowsServices:
+ fRc2, _ = self._sudoExecuteSync(['sc.exe', 'query', sService]);
+ if fIgnoreServices is False and fRc2 is True:
+ fRc = False
+
+ return fRc;
+
+
+ #
+ # Extension pack.
+ #
+
+ def _getVBoxInstallPath(self, fFailIfNotFound):
+ """ Returns the default VBox installation path. """
+ sHost = utils.getHostOs();
+ if sHost == 'win':
+ sProgFiles = os.environ.get('ProgramFiles', 'C:\\Program Files');
+ asLocs = [
+ os.path.join(sProgFiles, 'Oracle', 'VirtualBox'),
+ os.path.join(sProgFiles, 'OracleVM', 'VirtualBox'),
+ os.path.join(sProgFiles, 'Sun', 'VirtualBox'),
+ ];
+ elif sHost == 'linux' or sHost == 'solaris':
+ asLocs = [ '/opt/VirtualBox', '/opt/VirtualBox-3.2', '/opt/VirtualBox-3.1', '/opt/VirtualBox-3.0'];
+ elif sHost == 'darwin':
+ asLocs = [ '/Applications/VirtualBox.app/Contents/MacOS' ];
+ else:
+ asLocs = [ '/opt/VirtualBox' ];
+ if 'VBOX_INSTALL_PATH' in os.environ:
+ asLocs.insert(0, os.environ.get('VBOX_INSTALL_PATH', None));
+
+ for sLoc in asLocs:
+ if os.path.isdir(sLoc):
+ return sLoc;
+ if fFailIfNotFound:
+ reporter.error('Failed to locate VirtualBox installation: %s' % (asLocs,));
+ else:
+ reporter.log2('Failed to locate VirtualBox installation: %s' % (asLocs,));
+ return None;
+
+ def _installExtPack(self):
+ """ Installs the extension pack. """
+ sVBox = self._getVBoxInstallPath(fFailIfNotFound = True);
+ if sVBox is None:
+ return False;
+ sExtPackDir = os.path.join(sVBox, 'ExtensionPacks');
+
+ if self._uninstallAllExtPacks() is not True:
+ return False;
+
+ sExtPack = self._findFile('Oracle_VM_VirtualBox_Extension_Pack.vbox-extpack');
+ if sExtPack is None:
+ sExtPack = self._findFile('Oracle_VM_VirtualBox_Extension_Pack.*.vbox-extpack');
+ if sExtPack is None:
+ return True;
+
+ sDstDir = os.path.join(sExtPackDir, 'Oracle_VM_VirtualBox_Extension_Pack');
+ reporter.log('Installing extension pack "%s" to "%s"...' % (sExtPack, sExtPackDir));
+ fRc, _ = self._sudoExecuteSync([ self.getBinTool('vts_tar'),
+ '--extract',
+ '--verbose',
+ '--gzip',
+ '--file', sExtPack,
+ '--directory', sDstDir,
+ '--file-mode-and-mask', '0644',
+ '--file-mode-or-mask', '0644',
+ '--dir-mode-and-mask', '0755',
+ '--dir-mode-or-mask', '0755',
+ '--owner', '0',
+ '--group', '0',
+ ]);
+ return fRc;
+
+ def _uninstallAllExtPacks(self):
+ """ Uninstalls all extension packs. """
+ sVBox = self._getVBoxInstallPath(fFailIfNotFound = False);
+ if sVBox is None:
+ return True;
+
+ sExtPackDir = os.path.join(sVBox, 'ExtensionPacks');
+ if not os.path.exists(sExtPackDir):
+ return True;
+
+ fRc, _ = self._sudoExecuteSync([self.getBinTool('vts_rm'), '-Rfv', '--', sExtPackDir]);
+ return fRc;
+
+
+
+if __name__ == '__main__':
+ sys.exit(VBoxInstallerTestDriver().main(sys.argv));
+