VirtualBox

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

Last change on this file since 90488 was 87468, checked in by vboxsync, 4 years ago

vboxinstaller.py: More LD_PRELOAD tweaking...

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