VirtualBox

source: vbox/trunk/src/VBox/ValidationKit/testdriver/vboxinstaller.py@ 106438

Last change on this file since 106438 was 106061, checked in by vboxsync, 3 months ago

Copyright year updates by scm.

  • Property svn:eol-style set to LF
  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
File size: 51.2 KB
Line 
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3
4"""
5VirtualBox Installer Wrapper Driver.
6
7This installs VirtualBox, starts a sub driver which does the real testing,
8and then uninstall VirtualBox afterwards. This reduces the complexity of the
9other VBox test drivers.
10"""
11
12__copyright__ = \
13"""
14Copyright (C) 2010-2024 Oracle and/or its affiliates.
15
16This file is part of VirtualBox base platform packages, as
17available from https://www.virtualbox.org.
18
19This program is free software; you can redistribute it and/or
20modify it under the terms of the GNU General Public License
21as published by the Free Software Foundation, in version 3 of the
22License.
23
24This program is distributed in the hope that it will be useful, but
25WITHOUT ANY WARRANTY; without even the implied warranty of
26MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
27General Public License for more details.
28
29You should have received a copy of the GNU General Public License
30along with this program; if not, see <https://www.gnu.org/licenses>.
31
32The contents of this file may alternatively be used under the terms
33of the Common Development and Distribution License Version 1.0
34(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
35in the VirtualBox distribution, in which case the provisions of the
36CDDL are applicable instead of those of the GPL.
37
38You may elect to license modified versions of this file under the
39terms and conditions of either the GPL or the CDDL or both.
40
41SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
42"""
43__version__ = "$Revision: 106061 $"
44
45
46# Standard Python imports.
47import os
48import sys
49import re
50import socket
51import tempfile
52import time
53
54# Only the main script needs to modify the path.
55try: __file__ # pylint: disable=used-before-assignment
56except: __file__ = sys.argv[0];
57g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)));
58sys.path.append(g_ksValidationKitDir);
59
60# Validation Kit imports.
61from common import utils, webutils;
62from common.constants import rtexitcode;
63from testdriver import reporter;
64from testdriver.base import TestDriverBase;
65
66
67
68class VBoxInstallerTestDriver(TestDriverBase):
69 """
70 Implementation of a top level test driver.
71 """
72
73
74 ## State file indicating that we've skipped installation.
75 ksVar_Skipped = 'vboxinstaller-skipped';
76
77
78 def __init__(self):
79 TestDriverBase.__init__(self);
80 self._asSubDriver = []; # The sub driver and it's arguments.
81 self._asBuildUrls = []; # The URLs passed us on the command line.
82 self._asBuildFiles = []; # The downloaded file names.
83 self._fUnpackedBuildFiles = False;
84 self._fAutoInstallPuelExtPack = True;
85 self._fKernelDrivers = True;
86 self._fWinForcedInstallTimestampCA = True;
87 self._fInstallMsCrt = False; # By default we don't install the Microsoft CRT (only needed once).
88
89 #
90 # Base method we override
91 #
92
93 def showUsage(self):
94 rc = TestDriverBase.showUsage(self);
95 # 0 1 2 3 4 5 6 7 8
96 # 012345678901234567890123456789012345678901234567890123456789012345678901234567890
97 reporter.log('');
98 reporter.log('vboxinstaller Options:');
99 reporter.log(' --vbox-build <url[,url2[,...]]>');
100 reporter.log(' Comma separated list of URL to file to download and install or/and');
101 reporter.log(' unpack. URLs without a schema are assumed to be files on the');
102 reporter.log(' build share and will be copied off it.');
103 reporter.log(' --no-puel-extpack');
104 reporter.log(' Indicates that the PUEL extension pack should not be installed if found.');
105 reporter.log(' The default is to install it when found in the vbox-build.');
106 reporter.log(' --no-kernel-drivers');
107 reporter.log(' Indicates that the kernel drivers should not be installed on platforms');
108 reporter.log(' where this is optional. The default is to install them.');
109 reporter.log(' --forced-win-install-timestamp-ca, --no-forced-win-install-timestamp-ca');
110 reporter.log(' Whether to force installation of the legacy Windows timestamp CA.');
111 reporter.log(' If not forced, it will only installed on the hosts that needs it.');
112 reporter.log(' Default: --no-forced-win-install-timestamp-ca');
113 reporter.log(' --win-install-mscrt, --no-win-install-mscrt');
114 reporter.log(' Whether to install the MS Visual Studio Redistributable.');
115 reporter.log(' Default: --no-win-install-mscrt');
116 reporter.log(' --');
117 reporter.log(' Indicates the end of our parameters and the start of the sub');
118 reporter.log(' testdriver and its arguments.');
119 return rc;
120
121 def parseOption(self, asArgs, iArg):
122 """
123 Parse our arguments.
124 """
125 if asArgs[iArg] == '--':
126 # End of our parameters and start of the sub driver invocation.
127 iArg = self.requireMoreArgs(1, asArgs, iArg);
128 assert not self._asSubDriver;
129 self._asSubDriver = asArgs[iArg:];
130 self._asSubDriver[0] = self._asSubDriver[0].replace('/', os.path.sep);
131 iArg = len(asArgs) - 1;
132 elif asArgs[iArg] == '--vbox-build':
133 # List of files to copy/download and install.
134 iArg = self.requireMoreArgs(1, asArgs, iArg);
135 self._asBuildUrls = asArgs[iArg].split(',');
136 elif asArgs[iArg] == '--no-puel-extpack':
137 self._fAutoInstallPuelExtPack = False;
138 elif asArgs[iArg] == '--puel-extpack':
139 self._fAutoInstallPuelExtPack = True;
140 elif asArgs[iArg] == '--no-kernel-drivers':
141 self._fKernelDrivers = False;
142 elif asArgs[iArg] == '--kernel-drivers':
143 self._fKernelDrivers = True;
144 elif asArgs[iArg] == '--no-forced-win-install-timestamp-ca':
145 self._fWinForcedInstallTimestampCA = False;
146 elif asArgs[iArg] == '--forced-win-install-timestamp-ca':
147 self._fWinForcedInstallTimestampCA = True;
148 elif asArgs[iArg] == '--no-win-install-mscrt':
149 self._fInstallMsCrt = False;
150 elif asArgs[iArg] == '--win-install-mscrt':
151 self._fInstallMsCrt = True;
152 else:
153 return TestDriverBase.parseOption(self, asArgs, iArg);
154 return iArg + 1;
155
156 def completeOptions(self):
157 #
158 # Check that we've got what we need.
159 #
160 if not self._asBuildUrls:
161 reporter.error('No build files specified ("--vbox-build file1[,file2[...]]")');
162 return False;
163 if not self._asSubDriver:
164 reporter.error('No sub testdriver specified. (" -- test/stuff/tdStuff1.py args")');
165 return False;
166
167 #
168 # Construct _asBuildFiles as an array parallel to _asBuildUrls.
169 #
170 for sUrl in self._asBuildUrls:
171 sDstFile = os.path.join(self.sScratchPath, webutils.getFilename(sUrl));
172 self._asBuildFiles.append(sDstFile);
173
174 return TestDriverBase.completeOptions(self);
175
176 def actionExtract(self):
177 reporter.error('vboxinstall does not support extracting resources, you have to do that using the sub testdriver.');
178 return False;
179
180 def actionCleanupBefore(self):
181 """
182 Kills all VBox process we see.
183
184 This is only supposed to execute on a testbox so we don't need to go
185 all complicated wrt other users.
186 """
187 return self._killAllVBoxProcesses();
188
189 def actionConfig(self):
190 """
191 Install VBox and pass on the configure request to the sub testdriver.
192 """
193 fRc = self._installVBox();
194 if fRc is None:
195 self._persistentVarSet(self.ksVar_Skipped, 'true');
196 self.fBadTestbox = True;
197 else:
198 self._persistentVarUnset(self.ksVar_Skipped);
199
200 ## @todo vbox.py still has bugs preventing us from invoking it seperately with each action.
201 if fRc is True and 'execute' not in self.asActions and 'all' not in self.asActions:
202 fRc = self._executeSubDriver([ 'verify', ]);
203 if fRc is True and 'execute' not in self.asActions and 'all' not in self.asActions:
204 fRc = self._executeSubDriver([ 'config', ], fPreloadASan = True);
205 return fRc;
206
207 def actionExecute(self):
208 """
209 Execute the sub testdriver.
210 """
211 return self._executeSubDriver(self.asActions, fPreloadASan = True);
212
213 def actionCleanupAfter(self):
214 """
215 Forward this to the sub testdriver, then uninstall VBox.
216 """
217 fRc = True;
218 if 'execute' not in self.asActions and 'all' not in self.asActions:
219 fRc = self._executeSubDriver([ 'cleanup-after', ], fMaySkip = False);
220
221 if not self._killAllVBoxProcesses():
222 fRc = False;
223
224 if not self._uninstallVBox(self._persistentVarExists(self.ksVar_Skipped)):
225 fRc = False;
226
227 if utils.getHostOs() == 'darwin':
228 self._darwinUnmountDmg(fIgnoreError = True); # paranoia
229
230 if not TestDriverBase.actionCleanupAfter(self):
231 fRc = False;
232
233 return fRc;
234
235
236 def actionAbort(self):
237 """
238 Forward this to the sub testdriver first, then wipe all VBox like
239 processes, and finally do the pid file processing (again).
240 """
241 fRc1 = self._executeSubDriver([ 'abort', ], fMaySkip = False, fPreloadASan = True);
242 fRc2 = self._killAllVBoxProcesses();
243 fRc3 = TestDriverBase.actionAbort(self);
244 return fRc1 and fRc2 and fRc3;
245
246
247 #
248 # Persistent variables.
249 #
250 ## @todo integrate into the base driver. Persistent accross scratch wipes?
251
252 def __persistentVarCalcName(self, sVar):
253 """Returns the (full) filename for the given persistent variable."""
254 assert re.match(r'^[a-zA-Z0-9_-]*$', sVar) is not None;
255 return os.path.join(self.sScratchPath, 'persistent-%s.var' % (sVar,));
256
257 def _persistentVarSet(self, sVar, sValue = ''):
258 """
259 Sets a persistent variable.
260
261 Returns True on success, False + reporter.error on failure.
262
263 May raise exception if the variable name is invalid or something
264 unexpected happens.
265 """
266 sFull = self.__persistentVarCalcName(sVar);
267 try:
268 with open(sFull, 'w') as oFile: # pylint: disable=unspecified-encoding
269 if sValue:
270 oFile.write(sValue.encode('utf-8'));
271 except:
272 reporter.errorXcpt('Error creating "%s"' % (sFull,));
273 return False;
274 return True;
275
276 def _persistentVarUnset(self, sVar):
277 """
278 Unsets a persistent variable.
279
280 Returns True on success, False + reporter.error on failure.
281
282 May raise exception if the variable name is invalid or something
283 unexpected happens.
284 """
285 sFull = self.__persistentVarCalcName(sVar);
286 if os.path.exists(sFull):
287 try:
288 os.unlink(sFull);
289 except:
290 reporter.errorXcpt('Error unlinking "%s"' % (sFull,));
291 return False;
292 return True;
293
294 def _persistentVarExists(self, sVar):
295 """
296 Checks if a persistent variable exists.
297
298 Returns true/false.
299
300 May raise exception if the variable name is invalid or something
301 unexpected happens.
302 """
303 return os.path.exists(self.__persistentVarCalcName(sVar));
304
305 def _persistentVarGet(self, sVar):
306 """
307 Gets the value of a persistent variable.
308
309 Returns variable value on success.
310 Returns None if the variable doesn't exist or if an
311 error (reported) occured.
312
313 May raise exception if the variable name is invalid or something
314 unexpected happens.
315 """
316 sFull = self.__persistentVarCalcName(sVar);
317 if not os.path.exists(sFull):
318 return None;
319 try:
320 with open(sFull, 'r') as oFile: # pylint: disable=unspecified-encoding
321 sValue = oFile.read().decode('utf-8');
322 except:
323 reporter.errorXcpt('Error creating "%s"' % (sFull,));
324 return None;
325 return sValue;
326
327
328 #
329 # Helpers.
330 #
331
332 def _killAllVBoxProcesses(self):
333 """
334 Kills all virtual box related processes we find in the system.
335 """
336 sHostOs = utils.getHostOs();
337 asDebuggers = [ 'cdb', 'windbg', ] if sHostOs == 'windows' else [ 'gdb', 'gdb-i386-apple-darwin', 'lldb' ];
338
339 for iIteration in range(22):
340 # Gather processes to kill.
341 aoTodo = [];
342 aoDebuggers = [];
343 for oProcess in utils.processListAll():
344 sBase = oProcess.getBaseImageNameNoExeSuff();
345 if sBase is None:
346 continue;
347 sBase = sBase.lower();
348 if sBase in [ 'vboxsvc', 'vboxsds', 'virtualbox', 'virtualboxvm', 'vboxheadless', 'vboxmanage', 'vboxsdl',
349 'vboxwebsrv', 'vboxautostart', 'vboxballoonctrl', 'vboxbfe', 'vboxextpackhelperapp', 'vboxnetdhcp',
350 'vboxnetnat', 'vboxnetadpctl', 'vboxtestogl', 'vboxtunctl', 'vboxvmmpreload', 'vboxxpcomipcd', ]:
351 aoTodo.append(oProcess);
352 if sBase.startswith('virtualbox-') and sBase.endswith('-multiarch.exe'):
353 aoTodo.append(oProcess);
354 if sBase in asDebuggers:
355 aoDebuggers.append(oProcess);
356 if iIteration in [0, 21]:
357 reporter.log('Warning: debugger running: %s (%s %s)' % (oProcess.iPid, sBase, oProcess.asArgs));
358 if not aoTodo:
359 return True;
360
361 # Are any of the debugger processes hooked up to a VBox process?
362 if sHostOs == 'windows':
363 def isDebuggerDebuggingVBox(oDebugger, aoVBoxProcesses):
364 for oProcess in aoVBoxProcesses:
365 # The whole command line is asArgs[0] here. Fix if that changes.
366 if oDebugger.asArgs and oDebugger.asArgs[0].find('-p %s ' % (oProcess.iPid,)) >= 0:
367 return True;
368 return False;
369 else:
370 def isDebuggerDebuggingVBox(oDebugger, aoVBoxProcesses):
371 for oProcess in aoVBoxProcesses:
372 # Simplistic approach: Just check for argument equaling our pid.
373 if oDebugger.asArgs and ('%s' % oProcess.iPid) in oDebugger.asArgs:
374 return True;
375 return False;
376 for oDebugger in aoDebuggers:
377 if isDebuggerDebuggingVBox(oDebugger, aoTodo):
378 aoTodo.append(oDebugger);
379
380 # Kill.
381 for oProcess in aoTodo:
382 reporter.log('Loop #%d - Killing %s (%s, uid=%s)'
383 % ( iIteration, oProcess.iPid, oProcess.sImage if oProcess.sName is None else oProcess.sName,
384 oProcess.iUid, ));
385 if not utils.processKill(oProcess.iPid) \
386 and sHostOs != 'windows' \
387 and utils.processExists(oProcess.iPid):
388 # Many of the vbox processes are initially set-uid-to-root and associated debuggers are running
389 # via sudo, so we might not be able to kill them unless we sudo and use /bin/kill.
390 try: utils.sudoProcessCall(['/bin/kill', '-9', '%s' % (oProcess.iPid,)]);
391 except: reporter.logXcpt();
392
393 # Check if they're all dead like they should be.
394 time.sleep(0.1);
395 for oProcess in aoTodo:
396 if utils.processExists(oProcess.iPid):
397 time.sleep(2);
398 break;
399
400 return False;
401
402 def _executeSync(self, asArgs, fMaySkip = False):
403 """
404 Executes a child process synchronously.
405
406 Returns True if the process executed successfully and returned 0.
407 Returns None if fMaySkip is true and the child exits with RTEXITCODE_SKIPPED.
408 Returns False for all other cases.
409 """
410 reporter.log('Executing: %s' % (asArgs, ));
411 reporter.flushall();
412 try:
413 iRc = utils.processCall(asArgs, shell = False, close_fds = False);
414 except:
415 reporter.errorXcpt();
416 return False;
417 reporter.log('Exit code: %s (%s)' % (iRc, asArgs));
418 if fMaySkip and iRc == rtexitcode.RTEXITCODE_SKIPPED:
419 return None;
420 return iRc == 0;
421
422 def _sudoExecuteSync(self, asArgs):
423 """
424 Executes a sudo child process synchronously.
425 Returns a tuple [True, 0] if the process executed successfully
426 and returned 0, otherwise [False, rc] is returned.
427 """
428 reporter.log('Executing [sudo]: %s' % (asArgs, ));
429 reporter.flushall();
430 iRc = 0;
431 try:
432 iRc = utils.sudoProcessCall(asArgs, shell = False, close_fds = False);
433 except:
434 reporter.errorXcpt();
435 return (False, 0);
436 reporter.log('Exit code [sudo]: %s (%s)' % (iRc, asArgs));
437 return (iRc == 0, iRc);
438
439 def _findASanLibsForASanBuild(self):
440 """
441 Returns a list of (address) santizier related libraries to preload
442 when launching the sub driver.
443 Returns empty list for non-asan builds or on platforms where this isn't needed.
444 """
445 # Note! We include libasan.so.X in the VBoxAll tarball for asan builds, so we
446 # can use its presence both to detect an 'asan' build and to return it.
447 # Only the libasan.so.X library needs preloading at present.
448 if self.sHost in ('linux',):
449 sLibASan = self._findFile(r'libasan\.so\..*');
450 if sLibASan:
451 return [sLibASan,];
452 return [];
453
454 def _executeSubDriver(self, asActions, fMaySkip = True, fPreloadASan = True):
455 """
456 Execute the sub testdriver with the specified action.
457 """
458 asArgs = list(self._asSubDriver)
459 asArgs.append('--no-wipe-clean');
460 asArgs.extend(asActions);
461
462 asASanLibs = [];
463 if fPreloadASan:
464 asASanLibs = self._findASanLibsForASanBuild();
465 if asASanLibs:
466 os.environ['LD_PRELOAD'] = ':'.join(asASanLibs);
467 os.environ['LSAN_OPTIONS'] = 'detect_leaks=0'; # We don't want python leaks. vbox.py disables this.
468
469 # Because of https://github.com/google/sanitizers/issues/856 we must try use setarch to disable
470 # address space randomization.
471
472 reporter.log('LD_PRELOAD...')
473 if utils.getHostArch() == 'amd64':
474 sSetArch = utils.whichProgram('setarch');
475 reporter.log('sSetArch=%s' % (sSetArch,));
476 if sSetArch:
477 asArgs = [ sSetArch, 'x86_64', '-R', sys.executable ] + asArgs;
478 reporter.log('asArgs=%s' % (asArgs,));
479
480 rc = self._executeSync(asArgs, fMaySkip = fMaySkip);
481
482 del os.environ['LSAN_OPTIONS'];
483 del os.environ['LD_PRELOAD'];
484 return rc;
485
486 return self._executeSync(asArgs, fMaySkip = fMaySkip);
487
488 def _maybeUnpackArchive(self, sMaybeArchive, fNonFatal = False):
489 """
490 Attempts to unpack the given build file.
491 Updates _asBuildFiles.
492 Returns True/False. No exceptions.
493 """
494 def unpackFilter(sMember):
495 # type: (string) -> bool
496 """ Skips debug info. """
497 sLower = sMember.lower();
498 if sLower.endswith('.pdb'):
499 return False;
500 return True;
501
502 asMembers = utils.unpackFile(sMaybeArchive, self.sScratchPath, reporter.log,
503 reporter.log if fNonFatal else reporter.error,
504 fnFilter = unpackFilter);
505 if asMembers is None:
506 return False;
507 self._asBuildFiles.extend(asMembers);
508 return True;
509
510
511 def _installVBox(self):
512 """
513 Download / copy the build files into the scratch area and install them.
514 """
515 reporter.testStart('Installing VirtualBox');
516 reporter.log('CWD=%s' % (os.getcwd(),)); # curious
517
518 #
519 # Download the build files.
520 #
521 for i, sBuildUrl in enumerate(self._asBuildUrls):
522 if webutils.downloadFile(sBuildUrl, self._asBuildFiles[i], self.sBuildPath, reporter.log, reporter.log) is not True:
523 reporter.testDone(fSkipped = True);
524 return None; # Failed to get binaries, probably deleted. Skip the test run.
525
526 #
527 # Unpack anything we know what is and append it to the build files
528 # list. This allows us to use VBoxAll*.tar.gz files.
529 #
530 for sFile in list(self._asBuildFiles): # Note! We copy the list as _maybeUnpackArchive updates it.
531 if self._maybeUnpackArchive(sFile, fNonFatal = True) is not True:
532 reporter.testDone(fSkipped = True);
533 return None; # Failed to unpack. Probably local error, like busy
534 # DLLs on windows, no reason for failing the build.
535 self._fUnpackedBuildFiles = True;
536
537 #
538 # Go to system specific installation code.
539 #
540 sHost = utils.getHostOs()
541 if sHost == 'darwin': fRc = self._installVBoxOnDarwin();
542 elif sHost == 'linux': fRc = self._installVBoxOnLinux();
543 elif sHost == 'solaris': fRc = self._installVBoxOnSolaris();
544 elif sHost == 'win': fRc = self._installVBoxOnWindows();
545 else:
546 reporter.error('Unsupported host "%s".' % (sHost,));
547 if fRc is False:
548 reporter.testFailure('Installation error.');
549 elif fRc is not True:
550 reporter.log('Seems installation was skipped. Old version lurking behind? Not the fault of this build/test run!');
551
552 #
553 # Install the extension pack.
554 #
555 if fRc is True and self._fAutoInstallPuelExtPack:
556 fRc = self._installExtPack();
557 if fRc is False:
558 reporter.testFailure('Extension pack installation error.');
559
560 # Some debugging...
561 try:
562 cMbFreeSpace = utils.getDiskUsage(self.sScratchPath);
563 reporter.log('Disk usage after VBox install: %d MB available at %s' % (cMbFreeSpace, self.sScratchPath,));
564 except:
565 reporter.logXcpt('Unable to get disk free space. Ignored. Continuing.');
566
567 reporter.testDone(fRc is None);
568 return fRc;
569
570 def _uninstallVBox(self, fIgnoreError = False):
571 """
572 Uninstall VirtualBox.
573 """
574 reporter.testStart('Uninstalling VirtualBox');
575
576 sHost = utils.getHostOs()
577 if sHost == 'darwin': fRc = self._uninstallVBoxOnDarwin();
578 elif sHost == 'linux': fRc = self._uninstallVBoxOnLinux();
579 elif sHost == 'solaris': fRc = self._uninstallVBoxOnSolaris(True);
580 elif sHost == 'win': fRc = self._uninstallVBoxOnWindows('uninstall');
581 else:
582 reporter.error('Unsupported host "%s".' % (sHost,));
583 if fRc is False and not fIgnoreError:
584 reporter.testFailure('Uninstallation failed.');
585
586 fRc2 = self._uninstallAllExtPacks();
587 if not fRc2 and fRc:
588 fRc = fRc2;
589
590 reporter.testDone(fSkipped = fRc is None);
591 return fRc;
592
593 def _findFile(self, sRegExp, fMandatory = False):
594 """
595 Returns the first build file that matches the given regular expression
596 (basename only).
597
598 Returns None if no match was found, logging it as an error if
599 fMandatory is set.
600 """
601 oRegExp = re.compile(sRegExp);
602
603 reporter.log('_findFile: %s' % (sRegExp,));
604 for sFile in self._asBuildFiles:
605 if oRegExp.match(os.path.basename(sFile)) and os.path.exists(sFile):
606 return sFile;
607
608 # If we didn't unpack the build files, search all the files in the scratch area:
609 if not self._fUnpackedBuildFiles:
610 for sDir, _, asFiles in os.walk(self.sScratchPath):
611 for sFile in asFiles:
612 #reporter.log('_findFile: considering %s' % (sFile,));
613 if oRegExp.match(sFile):
614 return os.path.join(sDir, sFile);
615
616 if fMandatory:
617 reporter.error('Failed to find a file matching "%s" in %s.' % (sRegExp, self._asBuildFiles,));
618 return None;
619
620 def _waitForTestManagerConnectivity(self, cSecTimeout):
621 """
622 Check and wait for network connectivity to the test manager.
623
624 This is used with the windows installation and uninstallation since
625 these usually disrupts network connectivity when installing the filter
626 driver. If we proceed to quickly, we might finish the test at a time
627 when we cannot report to the test manager and thus end up with an
628 abandonded test error.
629 """
630 cSecElapsed = 0;
631 secStart = utils.timestampSecond();
632 while reporter.checkTestManagerConnection() is False:
633 cSecElapsed = utils.timestampSecond() - secStart;
634 if cSecElapsed >= cSecTimeout:
635 reporter.log('_waitForTestManagerConnectivity: Giving up after %u secs.' % (cSecTimeout,));
636 return False;
637 time.sleep(2);
638
639 if cSecElapsed > 0:
640 reporter.log('_waitForTestManagerConnectivity: Waited %s secs.' % (cSecTimeout,));
641 return True;
642
643
644 #
645 # Darwin (Mac OS X).
646 #
647
648 def _darwinDmgPath(self):
649 """ Returns the path to the DMG mount."""
650 return os.path.join(self.sScratchPath, 'DmgMountPoint');
651
652 def _darwinUnmountDmg(self, fIgnoreError):
653 """
654 Umount any DMG on at the default mount point.
655 """
656 sMountPath = self._darwinDmgPath();
657 if not os.path.exists(sMountPath):
658 return True;
659
660 # Unmount.
661 fRc = self._executeSync(['hdiutil', 'detach', sMountPath ]);
662 if not fRc and not fIgnoreError:
663 # In case it's busy for some reason or another, just retry after a little delay.
664 for iTry in range(6):
665 time.sleep(5);
666 reporter.log('Retry #%s unmount DMG at %s' % (iTry + 1, sMountPath,));
667 fRc = self._executeSync(['hdiutil', 'detach', sMountPath ]);
668 if fRc:
669 break;
670 if not fRc:
671 reporter.error('Failed to unmount DMG at %s' % (sMountPath,));
672
673 # Remove dir.
674 try:
675 os.rmdir(sMountPath);
676 except:
677 if not fIgnoreError:
678 reporter.errorXcpt('Failed to remove directory %s' % (sMountPath,));
679 return fRc;
680
681 def _darwinMountDmg(self, sDmg):
682 """
683 Mount the DMG at the default mount point.
684 """
685 self._darwinUnmountDmg(fIgnoreError = True)
686
687 sMountPath = self._darwinDmgPath();
688 if not os.path.exists(sMountPath):
689 try:
690 os.mkdir(sMountPath, 0o755);
691 except:
692 reporter.logXcpt();
693 return False;
694
695 return self._executeSync(['hdiutil', 'attach', '-readonly', '-mount', 'required', '-mountpoint', sMountPath, sDmg, ]);
696
697 def _generateWithoutKextsChoicesXmlOnDarwin(self):
698 """
699 Generates the choices XML when kernel drivers are disabled.
700 None is returned on failure.
701 """
702 sPath = os.path.join(self.sScratchPath, 'DarwinChoices.xml');
703 oFile = utils.openNoInherit(sPath, 'wt');
704 oFile.write('<?xml version="1.0" encoding="UTF-8"?>\n'
705 '<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">\n'
706 '<plist version="1.0">\n'
707 '<array>\n'
708 ' <dict>\n'
709 ' <key>attributeSetting</key>\n'
710 ' <integer>0</integer>\n'
711 ' <key>choiceAttribute</key>\n'
712 ' <string>selected</string>\n'
713 ' <key>choiceIdentifier</key>\n'
714 ' <string>choiceVBoxKEXTs</string>\n'
715 ' </dict>\n'
716 '</array>\n'
717 '</plist>\n');
718 oFile.close();
719 return sPath;
720
721 def _installVBoxOnDarwin(self):
722 """ Installs VBox on Mac OS X."""
723
724 # TEMPORARY HACK - START
725 # Don't install the kernel drivers on the testboxes with BigSur and later
726 # Needs a more generic approach but that one needs more effort.
727 sHostName = socket.getfqdn();
728 if sHostName.startswith('testboxmac10') \
729 or sHostName.startswith('testboxmac11'):
730 self._fKernelDrivers = False;
731 # TEMPORARY HACK - END
732
733 sDmg = self._findFile('^VirtualBox-.*\\.dmg$');
734 if sDmg is None:
735 return False;
736
737 # Mount the DMG.
738 fRc = self._darwinMountDmg(sDmg);
739 if fRc is not True:
740 return False;
741
742 # Uninstall any previous vbox version first.
743 sUninstaller = os.path.join(self._darwinDmgPath(), 'VirtualBox_Uninstall.tool');
744 fRc, _ = self._sudoExecuteSync([sUninstaller, '--unattended',]);
745 if fRc is True:
746
747 # Install the package.
748 sPkg = os.path.join(self._darwinDmgPath(), 'VirtualBox.pkg');
749 if self._fKernelDrivers:
750 fRc, _ = self._sudoExecuteSync(['installer', '-verbose', '-dumplog', '-pkg', sPkg, '-target', '/']);
751 else:
752 sChoicesXml = self._generateWithoutKextsChoicesXmlOnDarwin();
753 if sChoicesXml is not None:
754 fRc, _ = self._sudoExecuteSync(['installer', '-verbose', '-dumplog', '-pkg', sPkg, \
755 '-applyChoiceChangesXML', sChoicesXml, '-target', '/']);
756 else:
757 fRc = False;
758
759 # Unmount the DMG and we're done.
760 if not self._darwinUnmountDmg(fIgnoreError = False):
761 fRc = False;
762 return fRc;
763
764 def _uninstallVBoxOnDarwin(self):
765 """ Uninstalls VBox on Mac OS X."""
766
767 # Is VirtualBox installed? If not, don't try uninstall it.
768 sVBox = self._getVBoxInstallPath(fFailIfNotFound = False);
769 if sVBox is None:
770 return True;
771
772 # Find the dmg.
773 sDmg = self._findFile('^VirtualBox-.*\\.dmg$');
774 if sDmg is None:
775 return False;
776 if not os.path.exists(sDmg):
777 return True;
778
779 # Mount the DMG.
780 fRc = self._darwinMountDmg(sDmg);
781 if fRc is not True:
782 return False;
783
784 # Execute the uninstaller.
785 sUninstaller = os.path.join(self._darwinDmgPath(), 'VirtualBox_Uninstall.tool');
786 fRc, _ = self._sudoExecuteSync([sUninstaller, '--unattended',]);
787
788 # Unmount the DMG and we're done.
789 if not self._darwinUnmountDmg(fIgnoreError = False):
790 fRc = False;
791 return fRc;
792
793 #
794 # GNU/Linux
795 #
796
797 def _installVBoxOnLinux(self):
798 """ Installs VBox on Linux."""
799 sRun = self._findFile('^VirtualBox-.*\\.run$');
800 if sRun is None:
801 return False;
802 utils.chmodPlusX(sRun);
803
804 # Install the new one.
805 fRc, _ = self._sudoExecuteSync([sRun,]);
806 return fRc;
807
808 def _uninstallVBoxOnLinux(self):
809 """ Uninstalls VBox on Linux."""
810
811 # Is VirtualBox installed? If not, don't try uninstall it.
812 sVBox = self._getVBoxInstallPath(fFailIfNotFound = False);
813 if sVBox is None:
814 return True;
815
816 # Find the .run file and use it.
817 sRun = self._findFile('^VirtualBox-.*\\.run$', fMandatory = False);
818 if sRun is not None:
819 utils.chmodPlusX(sRun);
820 fRc, _ = self._sudoExecuteSync([sRun, 'uninstall']);
821 return fRc;
822
823 # Try the installed uninstaller.
824 for sUninstaller in [os.path.join(sVBox, 'uninstall.sh'), '/opt/VirtualBox/uninstall.sh', ]:
825 if os.path.isfile(sUninstaller):
826 reporter.log('Invoking "%s"...' % (sUninstaller,));
827 fRc, _ = self._sudoExecuteSync([sUninstaller, 'uninstall']);
828 return fRc;
829
830 reporter.log('Did not find any VirtualBox install to uninstall.');
831 return True;
832
833
834 #
835 # Solaris
836 #
837
838 def _generateAutoResponseOnSolaris(self):
839 """
840 Generates an autoresponse file on solaris, returning the name.
841 None is return on failure.
842 """
843 sPath = os.path.join(self.sScratchPath, 'SolarisAutoResponse');
844 oFile = utils.openNoInherit(sPath, 'wt');
845 oFile.write('basedir=default\n'
846 'runlevel=nocheck\n'
847 'conflict=quit\n'
848 'setuid=nocheck\n'
849 'action=nocheck\n'
850 'partial=quit\n'
851 'instance=unique\n'
852 'idepend=quit\n'
853 'rdepend=quit\n'
854 'space=quit\n'
855 'mail=\n');
856 oFile.close();
857 return sPath;
858
859 def _installVBoxOnSolaris(self):
860 """ Installs VBox on Solaris."""
861 sPkg = self._findFile('^VirtualBox-.*\\.pkg$', fMandatory = False);
862 if sPkg is None:
863 sTar = self._findFile('^VirtualBox-.*-SunOS-.*\\.tar.gz$', fMandatory = False);
864 if sTar is not None:
865 if self._maybeUnpackArchive(sTar) is not True:
866 return False;
867 sPkg = self._findFile('^VirtualBox-.*\\.pkg$', fMandatory = True);
868 sRsp = self._findFile('^autoresponse$', fMandatory = True);
869 if sPkg is None or sRsp is None:
870 return False;
871
872 # Uninstall first (ignore result).
873 self._uninstallVBoxOnSolaris(False);
874
875 # Install the new one.
876 fRc, _ = self._sudoExecuteSync(['pkgadd', '-d', sPkg, '-n', '-a', sRsp, 'SUNWvbox']);
877 return fRc;
878
879 def _uninstallVBoxOnSolaris(self, fRestartSvcConfigD):
880 """ Uninstalls VBox on Solaris."""
881 reporter.flushall();
882 if utils.processCall(['pkginfo', '-q', 'SUNWvbox']) != 0:
883 return True;
884 sRsp = self._generateAutoResponseOnSolaris();
885 fRc, _ = self._sudoExecuteSync(['pkgrm', '-n', '-a', sRsp, 'SUNWvbox']);
886
887 #
888 # Restart the svc.configd as it has a tendency to clog up with time and
889 # become unresponsive. It will handle SIGHUP by exiting the sigwait()
890 # look in the main function and shut down the service nicely (backend_fini).
891 # The restarter will then start a new instance of it.
892 #
893 if fRestartSvcConfigD:
894 time.sleep(1); # Give it a chance to flush pkgrm stuff.
895 self._sudoExecuteSync(['pkill', '-HUP', 'svc.configd']);
896 time.sleep(5); # Spare a few cpu cycles it to shutdown and restart.
897
898 return fRc;
899
900 #
901 # Windows
902 #
903
904 ## VBox windows services we can query the status of.
905 kasWindowsServices = [ 'vboxsup', 'vboxusbmon', 'vboxnetadp', 'vboxnetflt', 'vboxnetlwf' ];
906
907 def _installVBoxOnWindows(self):
908 """ Installs VBox on Windows."""
909 sExe = self._findFile('^VirtualBox-.*-(MultiArch|Win).exe$');
910 if sExe is None:
911 return False;
912
913 # TEMPORARY HACK - START
914 # It seems that running the NDIS cleanup script upon uninstallation is not
915 # a good idea, so let's run it before installing VirtualBox.
916 #sHostName = socket.getfqdn();
917 #if not sHostName.startswith('testboxwin3') \
918 # and not sHostName.startswith('testboxharp2') \
919 # and not sHostName.startswith('wei01-b6ka-3') \
920 # and utils.getHostOsVersion() in ['8', '8.1', '9', '2008Server', '2008ServerR2', '2012Server']:
921 # reporter.log('Peforming extra NDIS cleanup...');
922 # sMagicScript = os.path.abspath(os.path.join(g_ksValidationKitDir, 'testdriver', 'win-vbox-net-uninstall.ps1'));
923 # fRc2, _ = self._sudoExecuteSync(['powershell.exe', '-Command', 'set-executionpolicy unrestricted']);
924 # if not fRc2:
925 # reporter.log('set-executionpolicy failed.');
926 # self._sudoExecuteSync(['powershell.exe', '-Command', 'get-executionpolicy']);
927 # fRc2, _ = self._sudoExecuteSync(['powershell.exe', '-File', sMagicScript]);
928 # if not fRc2:
929 # reporter.log('NDIS cleanup failed.');
930 # TEMPORARY HACK - END
931
932 # Uninstall any previous vbox version first.
933 fRc = self._uninstallVBoxOnWindows('install');
934 if fRc is not True:
935 return None; # There shouldn't be anything to uninstall, and if there is, it's not our fault.
936
937 # Install the MS Visual Studio Redistributable, if requested. (VBox 7.0+ needs this installed once.)
938 if self._fInstallMsCrt:
939 reporter.log('Installing MS Visual Studio Redistributable (untested code)...');
940 ## @todo Test this.
941 ## @todo We could cache this on the testrsrc share.
942 sName = "vc_redist.x64.exe"
943 sUrl = "https://aka.ms/vs/17/release/" + sName # Permalink, according to MS.
944 sExe = os.path.join(self.sBuildPath, sName);
945 if webutils.downloadFile(sUrl, sExe, None, reporter.log, reporter.log):
946 asArgs = [ sExe, '/Q' ];
947 fRc2, iRc = self._sudoExecuteSync(asArgs);
948 if fRc2 is False:
949 return reporter.error('Installing MS Visual Studio Redistributable failed, exit code: %s' % (iRc,));
950 reporter.log('Installing MS Visual Studio Redistributable done');
951 else:
952 return False;
953
954 # We need the help text to detect supported options below.
955 reporter.log('Executing: %s' % ([sExe, '--silent', '--help'], ));
956 reporter.flushall();
957 (iExitCode, sHelp, _) = utils.processOutputUnchecked([sExe, '--silent', '--help'], fIgnoreEncoding = True);
958 reporter.log('Exit code: %d, %u chars of help text' % (iExitCode, len(sHelp),));
959
960 # Gather installer arguments.
961 asArgs = [sExe, '-vvvv', '--silent', '--logging'];
962 asArgs.extend(['--msiparams', 'REBOOT=ReallySuppress']);
963 sVBoxInstallPath = os.environ.get('VBOX_INSTALL_PATH', None);
964 if sVBoxInstallPath is not None:
965 asArgs.extend(['INSTALLDIR="%s"' % (sVBoxInstallPath,)]);
966
967 if sHelp.find("--msi-log-file") >= 0:
968 sLogFile = os.path.join(self.sScratchPath, 'VBoxInstallLog.txt'); # Specify location to prevent a random one.
969 asArgs.extend(['--msi-log-file', sLogFile]);
970 else:
971 sLogFile = os.path.join(tempfile.gettempdir(), 'VirtualBox', 'VBoxInstallLog.txt'); # Hardcoded TMP location.
972
973 if self._fWinForcedInstallTimestampCA and sHelp.find("--force-install-timestamp-ca") >= 0:
974 asArgs.extend(['--force-install-timestamp-ca']);
975
976 # Install it.
977 fRc2, iRc = self._sudoExecuteSync(asArgs);
978 if fRc2 is False:
979 if iRc == 3010: # ERROR_SUCCESS_REBOOT_REQUIRED
980 reporter.error('Installer required a reboot to complete installation (ERROR_SUCCESS_REBOOT_REQUIRED)');
981 else:
982 reporter.error('Installer failed, exit code: %s' % (iRc,));
983 fRc = False;
984
985 # Add the installer log if present and wait for the network connection to be restore after the filter driver upset.
986 if os.path.isfile(sLogFile):
987 reporter.addLogFile(sLogFile, 'log/installer', "Verbose MSI installation log file");
988 self._waitForTestManagerConnectivity(30);
989
990 return fRc;
991
992 def _isProcessPresent(self, sName):
993 """ Checks whether the named process is present or not. """
994 for oProcess in utils.processListAll():
995 sBase = oProcess.getBaseImageNameNoExeSuff();
996 if sBase is not None and sBase.lower() == sName:
997 return True;
998 return False;
999
1000 def _killProcessesByName(self, sName, sDesc, fChildren = False):
1001 """ Kills the named process, optionally including children. """
1002 cKilled = 0;
1003 aoProcesses = utils.processListAll();
1004 for oProcess in aoProcesses:
1005 sBase = oProcess.getBaseImageNameNoExeSuff();
1006 if sBase is not None and sBase.lower() == sName:
1007 reporter.log('Killing %s process: %s (%s)' % (sDesc, oProcess.iPid, sBase));
1008 utils.processKill(oProcess.iPid);
1009 cKilled += 1;
1010
1011 if fChildren:
1012 for oChild in aoProcesses:
1013 if oChild.iParentPid == oProcess.iPid and oChild.iParentPid is not None:
1014 reporter.log('Killing %s child process: %s (%s)' % (sDesc, oChild.iPid, sBase));
1015 utils.processKill(oChild.iPid);
1016 cKilled += 1;
1017 return cKilled;
1018
1019 def _terminateProcessesByNameAndArgSubstr(self, sName, sArg, sDesc):
1020 """
1021 Terminates the named process using taskkill.exe, if any of its args
1022 contains the passed string.
1023 """
1024 cKilled = 0;
1025 aoProcesses = utils.processListAll();
1026 for oProcess in aoProcesses:
1027 sBase = oProcess.getBaseImageNameNoExeSuff();
1028 if sBase is not None and sBase.lower() == sName and any(sArg in s for s in oProcess.asArgs):
1029
1030 reporter.log('Killing %s process: %s (%s)' % (sDesc, oProcess.iPid, sBase));
1031 self._executeSync(['taskkill.exe', '/pid', '%u' % (oProcess.iPid,)]);
1032 cKilled += 1;
1033 return cKilled;
1034
1035 def _uninstallVBoxOnWindows(self, sMode):
1036 """
1037 Uninstalls VBox on Windows, all installations we find to be on the safe side...
1038 """
1039 assert sMode in ['install', 'uninstall',];
1040
1041 import win32com.client; # pylint: disable=import-error
1042 win32com.client.gencache.EnsureModule('{000C1092-0000-0000-C000-000000000046}', 1033, 1, 0);
1043 oInstaller = win32com.client.Dispatch('WindowsInstaller.Installer',
1044 resultCLSID = '{000C1090-0000-0000-C000-000000000046}')
1045
1046 # Search installed products for VirtualBox.
1047 asProdCodes = [];
1048 for sProdCode in oInstaller.Products:
1049 try:
1050 sProdName = oInstaller.ProductInfo(sProdCode, "ProductName");
1051 except:
1052 reporter.logXcpt();
1053 continue;
1054 #reporter.log('Info: %s=%s' % (sProdCode, sProdName));
1055 if sProdName.startswith('Oracle VirtualBox') \
1056 or sProdName.startswith('Oracle VM VirtualBox') \
1057 or sProdName.startswith('Sun VirtualBox'):
1058 asProdCodes.append([sProdCode, sProdName]);
1059
1060 # Before we start uninstalling anything, just ruthlessly kill any cdb,
1061 # msiexec, drvinst and some rundll process we might find hanging around.
1062 if self._isProcessPresent('rundll32'):
1063 cTimes = 0;
1064 while cTimes < 3:
1065 cTimes += 1;
1066 cKilled = self._terminateProcessesByNameAndArgSubstr('rundll32', 'InstallSecurityPromptRunDllW',
1067 'MSI driver installation');
1068 if cKilled <= 0:
1069 break;
1070 time.sleep(10); # Give related drvinst process a chance to clean up after we killed the verification dialog.
1071
1072 if self._isProcessPresent('drvinst'):
1073 time.sleep(15); # In the hope that it goes away.
1074 cTimes = 0;
1075 while cTimes < 4:
1076 cTimes += 1;
1077 cKilled = self._killProcessesByName('drvinst', 'MSI driver installation', True);
1078 if cKilled <= 0:
1079 break;
1080 time.sleep(10); # Give related MSI process a chance to clean up after we killed the driver installer.
1081
1082 if self._isProcessPresent('msiexec'):
1083 cTimes = 0;
1084 while cTimes < 3:
1085 reporter.log('found running msiexec process, waiting a bit...');
1086 time.sleep(20) # In the hope that it goes away.
1087 if not self._isProcessPresent('msiexec'):
1088 break;
1089 cTimes += 1;
1090 ## @todo this could also be the msiexec system service, try to detect this case!
1091 if cTimes >= 6:
1092 cKilled = self._killProcessesByName('msiexec', 'MSI driver installation');
1093 if cKilled > 0:
1094 time.sleep(16); # fudge.
1095
1096 # cdb.exe sometimes stays running (from utils.getProcessInfo), blocking
1097 # the scratch directory. No idea why.
1098 if self._isProcessPresent('cdb'):
1099 cTimes = 0;
1100 while cTimes < 3:
1101 cKilled = self._killProcessesByName('cdb', 'cdb.exe from getProcessInfo');
1102 if cKilled <= 0:
1103 break;
1104 time.sleep(2); # fudge.
1105
1106 # Do the uninstalling.
1107 fRc = True;
1108 sLogFile = os.path.join(self.sScratchPath, 'VBoxUninstallLog.txt');
1109 for sProdCode, sProdName in asProdCodes:
1110 reporter.log('Uninstalling %s (%s)...' % (sProdName, sProdCode));
1111 fRc2, iRc = self._sudoExecuteSync(['msiexec', '/uninstall', sProdCode, '/quiet', '/passive', '/norestart',
1112 '/L*v', '%s' % (sLogFile), ]);
1113 if fRc2 is False:
1114 if iRc == 3010: # ERROR_SUCCESS_REBOOT_REQUIRED
1115 reporter.error('Uninstaller required a reboot to complete uninstallation');
1116 else:
1117 reporter.error('Uninstaller failed, exit code: %s' % (iRc,));
1118 fRc = False;
1119
1120 self._waitForTestManagerConnectivity(30);
1121
1122 # Upload the log on failure. Do it early if the extra cleanups below causes trouble.
1123 if fRc is False and os.path.isfile(sLogFile):
1124 reporter.addLogFile(sLogFile, 'log/uninstaller', "Verbose MSI uninstallation log file");
1125 sLogFile = None;
1126
1127 # Log driver service states (should ls \Driver\VBox* and \Device\VBox*).
1128 fHadLeftovers = False;
1129 asLeftovers = [];
1130 for sService in reversed(self.kasWindowsServices):
1131 cTries = 0;
1132 while True:
1133 fRc2, _ = self._sudoExecuteSync(['sc.exe', 'query', sService]);
1134 if not fRc2:
1135 break;
1136 fHadLeftovers = True;
1137
1138 cTries += 1;
1139 if cTries > 3:
1140 asLeftovers.append(sService,);
1141 break;
1142
1143 # Get the status output.
1144 try:
1145 sOutput = utils.sudoProcessOutputChecked(['sc.exe', 'query', sService]);
1146 except:
1147 reporter.logXcpt();
1148 else:
1149 if re.search(r'STATE\s+:\s*1\s*STOPPED', sOutput) is None:
1150 reporter.log('Trying to stop %s...' % (sService,));
1151 fRc2, _ = self._sudoExecuteSync(['sc.exe', 'stop', sService]);
1152 time.sleep(1); # fudge
1153
1154 reporter.log('Trying to delete %s...' % (sService,));
1155 self._sudoExecuteSync(['sc.exe', 'delete', sService]);
1156
1157 time.sleep(1); # fudge
1158
1159 if asLeftovers:
1160 reporter.log('Warning! Leftover VBox drivers: %s' % (', '.join(asLeftovers),));
1161 fRc = False;
1162
1163 if fHadLeftovers:
1164 self._waitForTestManagerConnectivity(30);
1165
1166 # Upload the log if we have any leftovers and didn't upload it already.
1167 if sLogFile is not None and (fRc is False or fHadLeftovers) and os.path.isfile(sLogFile):
1168 reporter.addLogFile(sLogFile, 'log/uninstaller', "Verbose MSI uninstallation log file");
1169
1170 return fRc;
1171
1172
1173 #
1174 # Extension pack.
1175 #
1176
1177 def _getVBoxInstallPath(self, fFailIfNotFound):
1178 """ Returns the default VBox installation path. """
1179 sHost = utils.getHostOs();
1180 if sHost == 'win':
1181 sProgFiles = os.environ.get('ProgramFiles', 'C:\\Program Files');
1182 asLocs = [
1183 os.path.join(sProgFiles, 'Oracle', 'VirtualBox'),
1184 os.path.join(sProgFiles, 'OracleVM', 'VirtualBox'),
1185 os.path.join(sProgFiles, 'Sun', 'VirtualBox'),
1186 ];
1187 elif sHost in ('linux', 'solaris',):
1188 asLocs = [ '/opt/VirtualBox', '/opt/VirtualBox-3.2', '/opt/VirtualBox-3.1', '/opt/VirtualBox-3.0'];
1189 elif sHost == 'darwin':
1190 asLocs = [ '/Applications/VirtualBox.app/Contents/MacOS' ];
1191 else:
1192 asLocs = [ '/opt/VirtualBox' ];
1193 if 'VBOX_INSTALL_PATH' in os.environ:
1194 asLocs.insert(0, os.environ.get('VBOX_INSTALL_PATH', None));
1195
1196 for sLoc in asLocs:
1197 if os.path.isdir(sLoc):
1198 return sLoc;
1199 if fFailIfNotFound:
1200 reporter.error('Failed to locate VirtualBox installation: %s' % (asLocs,));
1201 else:
1202 reporter.log2('Failed to locate VirtualBox installation: %s' % (asLocs,));
1203 return None;
1204
1205 ksExtPackBasenames = [ 'Oracle_VirtualBox_Extension_Pack', 'Oracle_VM_VirtualBox_Extension_Pack', ];
1206
1207 def _findExtPack(self):
1208 """ Locates the extension pack file. """
1209 for sExtPackBasename in self.ksExtPackBasenames:
1210 sExtPack = self._findFile('%s.vbox-extpack' % (sExtPackBasename,));
1211 if sExtPack is None:
1212 sExtPack = self._findFile('%s.*.vbox-extpack' % (sExtPackBasename,));
1213 if sExtPack is not None:
1214 return (sExtPack, sExtPackBasename);
1215 return (None, None);
1216
1217 def _installExtPack(self):
1218 """ Installs the extension pack. """
1219 sVBox = self._getVBoxInstallPath(fFailIfNotFound = True);
1220 if sVBox is None:
1221 return False;
1222 sExtPackDir = os.path.join(sVBox, 'ExtensionPacks');
1223
1224 if self._uninstallAllExtPacks() is not True:
1225 return False;
1226
1227 (sExtPack, sExtPackBasename) = self._findExtPack();
1228 if sExtPack is None:
1229 return True;
1230
1231 sDstDir = os.path.join(sExtPackDir, sExtPackBasename);
1232 reporter.log('Installing extension pack "%s" to "%s"...' % (sExtPack, sExtPackDir));
1233 fRc, _ = self._sudoExecuteSync([ self.getBinTool('vts_tar'),
1234 '--extract',
1235 '--verbose',
1236 '--gzip',
1237 '--file', sExtPack,
1238 '--directory', sDstDir,
1239 '--file-mode-and-mask', '0644',
1240 '--file-mode-or-mask', '0644',
1241 '--dir-mode-and-mask', '0755',
1242 '--dir-mode-or-mask', '0755',
1243 '--owner', '0',
1244 '--group', '0',
1245 ]);
1246 return fRc;
1247
1248 def _uninstallAllExtPacks(self):
1249 """ Uninstalls all extension packs. """
1250 sVBox = self._getVBoxInstallPath(fFailIfNotFound = False);
1251 if sVBox is None:
1252 return True;
1253
1254 sExtPackDir = os.path.join(sVBox, 'ExtensionPacks');
1255 if not os.path.exists(sExtPackDir):
1256 return True;
1257
1258 fRc, _ = self._sudoExecuteSync([self.getBinTool('vts_rm'), '-Rfv', '--', sExtPackDir]);
1259 return fRc;
1260
1261
1262
1263if __name__ == '__main__':
1264 sys.exit(VBoxInstallerTestDriver().main(sys.argv));
Note: See TracBrowser for help on using the repository browser.

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