VirtualBox

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

Last change on this file since 86714 was 86437, checked in by vboxsync, 4 years ago

testdriver/vboxinstaller.py: Preload libasan.so.X when present in VBoxAll on linux and disable memory leak detection. bugref:9841

  • Property svn:eol-style set to LF
  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
File size: 42.3 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: 86437 $"
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 rc = self._executeSync(asArgs, fMaySkip = fMaySkip);
407
408 del os.environ['LSAN_OPTIONS'];
409 del os.environ['LD_PRELOAD'];
410 return rc;
411
412 return self._executeSync(asArgs, fMaySkip = fMaySkip);
413
414 def _maybeUnpackArchive(self, sMaybeArchive, fNonFatal = False):
415 """
416 Attempts to unpack the given build file.
417 Updates _asBuildFiles.
418 Returns True/False. No exceptions.
419 """
420 def unpackFilter(sMember):
421 # type: (string) -> bool
422 """ Skips debug info. """
423 sLower = sMember.lower();
424 if sLower.endswith('.pdb'):
425 return False;
426 return True;
427
428 asMembers = utils.unpackFile(sMaybeArchive, self.sScratchPath, reporter.log,
429 reporter.log if fNonFatal else reporter.error,
430 fnFilter = unpackFilter);
431 if asMembers is None:
432 return False;
433 self._asBuildFiles.extend(asMembers);
434 return True;
435
436
437 def _installVBox(self):
438 """
439 Download / copy the build files into the scratch area and install them.
440 """
441 reporter.testStart('Installing VirtualBox');
442 reporter.log('CWD=%s' % (os.getcwd(),)); # curious
443
444 #
445 # Download the build files.
446 #
447 for i in range(len(self._asBuildUrls)):
448 if webutils.downloadFile(self._asBuildUrls[i], self._asBuildFiles[i],
449 self.sBuildPath, reporter.log, reporter.log) is not True:
450 reporter.testDone(fSkipped = True);
451 return None; # Failed to get binaries, probably deleted. Skip the test run.
452
453 #
454 # Unpack anything we know what is and append it to the build files
455 # list. This allows us to use VBoxAll*.tar.gz files.
456 #
457 for sFile in list(self._asBuildFiles): # Note! We copy the list as _maybeUnpackArchive updates it.
458 if self._maybeUnpackArchive(sFile, fNonFatal = True) is not True:
459 reporter.testDone(fSkipped = True);
460 return None; # Failed to unpack. Probably local error, like busy
461 # DLLs on windows, no reason for failing the build.
462 self._fUnpackedBuildFiles = True;
463
464 #
465 # Go to system specific installation code.
466 #
467 sHost = utils.getHostOs()
468 if sHost == 'darwin': fRc = self._installVBoxOnDarwin();
469 elif sHost == 'linux': fRc = self._installVBoxOnLinux();
470 elif sHost == 'solaris': fRc = self._installVBoxOnSolaris();
471 elif sHost == 'win': fRc = self._installVBoxOnWindows();
472 else:
473 reporter.error('Unsupported host "%s".' % (sHost,));
474 if fRc is False:
475 reporter.testFailure('Installation error.');
476 elif fRc is not True:
477 reporter.log('Seems installation was skipped. Old version lurking behind? Not the fault of this build/test run!');
478
479 #
480 # Install the extension pack.
481 #
482 if fRc is True and self._fAutoInstallPuelExtPack:
483 fRc = self._installExtPack();
484 if fRc is False:
485 reporter.testFailure('Extension pack installation error.');
486
487 # Some debugging...
488 try:
489 cMbFreeSpace = utils.getDiskUsage(self.sScratchPath);
490 reporter.log('Disk usage after VBox install: %d MB available at %s' % (cMbFreeSpace, self.sScratchPath,));
491 except:
492 reporter.logXcpt('Unable to get disk free space. Ignored. Continuing.');
493
494 reporter.testDone(fRc is None);
495 return fRc;
496
497 def _uninstallVBox(self, fIgnoreError = False):
498 """
499 Uninstall VirtualBox.
500 """
501 reporter.testStart('Uninstalling VirtualBox');
502
503 sHost = utils.getHostOs()
504 if sHost == 'darwin': fRc = self._uninstallVBoxOnDarwin();
505 elif sHost == 'linux': fRc = self._uninstallVBoxOnLinux();
506 elif sHost == 'solaris': fRc = self._uninstallVBoxOnSolaris(True);
507 elif sHost == 'win': fRc = self._uninstallVBoxOnWindows('uninstall');
508 else:
509 reporter.error('Unsupported host "%s".' % (sHost,));
510 if fRc is False and not fIgnoreError:
511 reporter.testFailure('Uninstallation failed.');
512
513 fRc2 = self._uninstallAllExtPacks();
514 if not fRc2 and fRc:
515 fRc = fRc2;
516
517 reporter.testDone(fSkipped = (fRc is None));
518 return fRc;
519
520 def _findFile(self, sRegExp, fMandatory = False):
521 """
522 Returns the first build file that matches the given regular expression
523 (basename only).
524
525 Returns None if no match was found, logging it as an error if
526 fMandatory is set.
527 """
528 oRegExp = re.compile(sRegExp);
529
530 reporter.log('_findFile: %s' % (sRegExp,));
531 for sFile in self._asBuildFiles:
532 if oRegExp.match(os.path.basename(sFile)) and os.path.exists(sFile):
533 return sFile;
534
535 # If we didn't unpack the build files, search all the files in the scratch area:
536 if not self._fUnpackedBuildFiles:
537 for sDir, _, asFiles in os.walk(self.sScratchPath):
538 for sFile in asFiles:
539 #reporter.log('_findFile: considering %s' % (sFile,));
540 if oRegExp.match(sFile):
541 return os.path.join(sDir, sFile);
542
543 if fMandatory:
544 reporter.error('Failed to find a file matching "%s" in %s.' % (sRegExp, self._asBuildFiles,));
545 return None;
546
547 def _waitForTestManagerConnectivity(self, cSecTimeout):
548 """
549 Check and wait for network connectivity to the test manager.
550
551 This is used with the windows installation and uninstallation since
552 these usually disrupts network connectivity when installing the filter
553 driver. If we proceed to quickly, we might finish the test at a time
554 when we cannot report to the test manager and thus end up with an
555 abandonded test error.
556 """
557 cSecElapsed = 0;
558 secStart = utils.timestampSecond();
559 while reporter.checkTestManagerConnection() is False:
560 cSecElapsed = utils.timestampSecond() - secStart;
561 if cSecElapsed >= cSecTimeout:
562 reporter.log('_waitForTestManagerConnectivity: Giving up after %u secs.' % (cSecTimeout,));
563 return False;
564 time.sleep(2);
565
566 if cSecElapsed > 0:
567 reporter.log('_waitForTestManagerConnectivity: Waited %s secs.' % (cSecTimeout,));
568 return True;
569
570
571 #
572 # Darwin (Mac OS X).
573 #
574
575 def _darwinDmgPath(self):
576 """ Returns the path to the DMG mount."""
577 return os.path.join(self.sScratchPath, 'DmgMountPoint');
578
579 def _darwinUnmountDmg(self, fIgnoreError):
580 """
581 Umount any DMG on at the default mount point.
582 """
583 sMountPath = self._darwinDmgPath();
584 if not os.path.exists(sMountPath):
585 return True;
586
587 # Unmount.
588 fRc = self._executeSync(['hdiutil', 'detach', sMountPath ]);
589 if not fRc and not fIgnoreError:
590 reporter.error('Failed to unmount DMG at %s' % sMountPath);
591
592 # Remove dir.
593 try:
594 os.rmdir(sMountPath);
595 except:
596 if not fIgnoreError:
597 reporter.errorXcpt('Failed to remove directory %s' % sMountPath);
598 return fRc;
599
600 def _darwinMountDmg(self, sDmg):
601 """
602 Mount the DMG at the default mount point.
603 """
604 self._darwinUnmountDmg(fIgnoreError = True)
605
606 sMountPath = self._darwinDmgPath();
607 if not os.path.exists(sMountPath):
608 try:
609 os.mkdir(sMountPath, 0o755);
610 except:
611 reporter.logXcpt();
612 return False;
613
614 return self._executeSync(['hdiutil', 'attach', '-readonly', '-mount', 'required', '-mountpoint', sMountPath, sDmg, ]);
615
616 def _installVBoxOnDarwin(self):
617 """ Installs VBox on Mac OS X."""
618 sDmg = self._findFile('^VirtualBox-.*\\.dmg$');
619 if sDmg is None:
620 return False;
621
622 # Mount the DMG.
623 fRc = self._darwinMountDmg(sDmg);
624 if fRc is not True:
625 return False;
626
627 # Uninstall any previous vbox version first.
628 sUninstaller = os.path.join(self._darwinDmgPath(), 'VirtualBox_Uninstall.tool');
629 fRc, _ = self._sudoExecuteSync([sUninstaller, '--unattended',]);
630 if fRc is True:
631
632 # Install the package.
633 sPkg = os.path.join(self._darwinDmgPath(), 'VirtualBox.pkg');
634 fRc, _ = self._sudoExecuteSync(['installer', '-verbose', '-dumplog', '-pkg', sPkg, '-target', '/']);
635
636 # Unmount the DMG and we're done.
637 if not self._darwinUnmountDmg(fIgnoreError = False):
638 fRc = False;
639 return fRc;
640
641 def _uninstallVBoxOnDarwin(self):
642 """ Uninstalls VBox on Mac OS X."""
643
644 # Is VirtualBox installed? If not, don't try uninstall it.
645 sVBox = self._getVBoxInstallPath(fFailIfNotFound = False);
646 if sVBox is None:
647 return True;
648
649 # Find the dmg.
650 sDmg = self._findFile('^VirtualBox-.*\\.dmg$');
651 if sDmg is None:
652 return False;
653 if not os.path.exists(sDmg):
654 return True;
655
656 # Mount the DMG.
657 fRc = self._darwinMountDmg(sDmg);
658 if fRc is not True:
659 return False;
660
661 # Execute the uninstaller.
662 sUninstaller = os.path.join(self._darwinDmgPath(), 'VirtualBox_Uninstall.tool');
663 fRc, _ = self._sudoExecuteSync([sUninstaller, '--unattended',]);
664
665 # Unmount the DMG and we're done.
666 if not self._darwinUnmountDmg(fIgnoreError = False):
667 fRc = False;
668 return fRc;
669
670 #
671 # GNU/Linux
672 #
673
674 def _installVBoxOnLinux(self):
675 """ Installs VBox on Linux."""
676 sRun = self._findFile('^VirtualBox-.*\\.run$');
677 if sRun is None:
678 return False;
679 utils.chmodPlusX(sRun);
680
681 # Install the new one.
682 fRc, _ = self._sudoExecuteSync([sRun,]);
683 return fRc;
684
685 def _uninstallVBoxOnLinux(self):
686 """ Uninstalls VBox on Linux."""
687
688 # Is VirtualBox installed? If not, don't try uninstall it.
689 sVBox = self._getVBoxInstallPath(fFailIfNotFound = False);
690 if sVBox is None:
691 return True;
692
693 # Find the .run file and use it.
694 sRun = self._findFile('^VirtualBox-.*\\.run$', fMandatory = False);
695 if sRun is not None:
696 utils.chmodPlusX(sRun);
697 fRc, _ = self._sudoExecuteSync([sRun, 'uninstall']);
698 return fRc;
699
700 # Try the installed uninstaller.
701 for sUninstaller in [os.path.join(sVBox, 'uninstall.sh'), '/opt/VirtualBox/uninstall.sh', ]:
702 if os.path.isfile(sUninstaller):
703 reporter.log('Invoking "%s"...' % (sUninstaller,));
704 fRc, _ = self._sudoExecuteSync([sUninstaller, 'uninstall']);
705 return fRc;
706
707 reporter.log('Did not find any VirtualBox install to uninstall.');
708 return True;
709
710
711 #
712 # Solaris
713 #
714
715 def _generateAutoResponseOnSolaris(self):
716 """
717 Generates an autoresponse file on solaris, returning the name.
718 None is return on failure.
719 """
720 sPath = os.path.join(self.sScratchPath, 'SolarisAutoResponse');
721 oFile = utils.openNoInherit(sPath, 'wt');
722 oFile.write('basedir=default\n'
723 'runlevel=nocheck\n'
724 'conflict=quit\n'
725 'setuid=nocheck\n'
726 'action=nocheck\n'
727 'partial=quit\n'
728 'instance=unique\n'
729 'idepend=quit\n'
730 'rdepend=quit\n'
731 'space=quit\n'
732 'mail=\n');
733 oFile.close();
734 return sPath;
735
736 def _installVBoxOnSolaris(self):
737 """ Installs VBox on Solaris."""
738 sPkg = self._findFile('^VirtualBox-.*\\.pkg$', fMandatory = False);
739 if sPkg is None:
740 sTar = self._findFile('^VirtualBox-.*-SunOS-.*\\.tar.gz$', fMandatory = False);
741 if sTar is not None:
742 if self._maybeUnpackArchive(sTar) is not True:
743 return False;
744 sPkg = self._findFile('^VirtualBox-.*\\.pkg$', fMandatory = True);
745 sRsp = self._findFile('^autoresponse$', fMandatory = True);
746 if sPkg is None or sRsp is None:
747 return False;
748
749 # Uninstall first (ignore result).
750 self._uninstallVBoxOnSolaris(False);
751
752 # Install the new one.
753 fRc, _ = self._sudoExecuteSync(['pkgadd', '-d', sPkg, '-n', '-a', sRsp, 'SUNWvbox']);
754 return fRc;
755
756 def _uninstallVBoxOnSolaris(self, fRestartSvcConfigD):
757 """ Uninstalls VBox on Solaris."""
758 reporter.flushall();
759 if utils.processCall(['pkginfo', '-q', 'SUNWvbox']) != 0:
760 return True;
761 sRsp = self._generateAutoResponseOnSolaris();
762 fRc, _ = self._sudoExecuteSync(['pkgrm', '-n', '-a', sRsp, 'SUNWvbox']);
763
764 #
765 # Restart the svc.configd as it has a tendency to clog up with time and
766 # become unresponsive. It will handle SIGHUP by exiting the sigwait()
767 # look in the main function and shut down the service nicely (backend_fini).
768 # The restarter will then start a new instance of it.
769 #
770 if fRestartSvcConfigD:
771 time.sleep(1); # Give it a chance to flush pkgrm stuff.
772 self._sudoExecuteSync(['pkill', '-HUP', 'svc.configd']);
773 time.sleep(5); # Spare a few cpu cycles it to shutdown and restart.
774
775 return fRc;
776
777 #
778 # Windows
779 #
780
781 ## VBox windows services we can query the status of.
782 kasWindowsServices = [ 'vboxdrv', 'vboxusbmon', 'vboxnetadp', 'vboxnetflt', 'vboxnetlwf' ];
783
784 def _installVBoxOnWindows(self):
785 """ Installs VBox on Windows."""
786 sExe = self._findFile('^VirtualBox-.*-(MultiArch|Win).exe$');
787 if sExe is None:
788 return False;
789
790 # TEMPORARY HACK - START
791 # It seems that running the NDIS cleanup script upon uninstallation is not
792 # a good idea, so let's run it before installing VirtualBox.
793 #sHostName = socket.getfqdn();
794 #if not sHostName.startswith('testboxwin3') \
795 # and not sHostName.startswith('testboxharp2') \
796 # and not sHostName.startswith('wei01-b6ka-3') \
797 # and utils.getHostOsVersion() in ['8', '8.1', '9', '2008Server', '2008ServerR2', '2012Server']:
798 # reporter.log('Peforming extra NDIS cleanup...');
799 # sMagicScript = os.path.abspath(os.path.join(g_ksValidationKitDir, 'testdriver', 'win-vbox-net-uninstall.ps1'));
800 # fRc2, _ = self._sudoExecuteSync(['powershell.exe', '-Command', 'set-executionpolicy unrestricted']);
801 # if not fRc2:
802 # reporter.log('set-executionpolicy failed.');
803 # self._sudoExecuteSync(['powershell.exe', '-Command', 'get-executionpolicy']);
804 # fRc2, _ = self._sudoExecuteSync(['powershell.exe', '-File', sMagicScript]);
805 # if not fRc2:
806 # reporter.log('NDIS cleanup failed.');
807 # TEMPORARY HACK - END
808
809 # Uninstall any previous vbox version first.
810 fRc = self._uninstallVBoxOnWindows('install');
811 if fRc is not True:
812 return None; # There shouldn't be anything to uninstall, and if there is, it's not our fault.
813
814 # Install the new one.
815 asArgs = [sExe, '-vvvv', '--silent', '--logging'];
816 asArgs.extend(['--msiparams', 'REBOOT=ReallySuppress']);
817 sVBoxInstallPath = os.environ.get('VBOX_INSTALL_PATH', None);
818 if sVBoxInstallPath is not None:
819 asArgs.extend(['INSTALLDIR="%s"' % (sVBoxInstallPath,)]);
820
821 fRc2, iRc = self._sudoExecuteSync(asArgs);
822 if fRc2 is False:
823 if iRc == 3010: # ERROR_SUCCESS_REBOOT_REQUIRED
824 reporter.error('Installer required a reboot to complete installation (ERROR_SUCCESS_REBOOT_REQUIRED)');
825 else:
826 reporter.error('Installer failed, exit code: %s' % (iRc,));
827 fRc = False;
828
829 sLogFile = os.path.join(tempfile.gettempdir(), 'VirtualBox', 'VBoxInstallLog.txt');
830 if os.path.isfile(sLogFile):
831 reporter.addLogFile(sLogFile, 'log/installer', "Verbose MSI installation log file");
832 self._waitForTestManagerConnectivity(30);
833 return fRc;
834
835 def _isProcessPresent(self, sName):
836 """ Checks whether the named process is present or not. """
837 for oProcess in utils.processListAll():
838 sBase = oProcess.getBaseImageNameNoExeSuff();
839 if sBase is not None and sBase.lower() == sName:
840 return True;
841 return False;
842
843 def _killProcessesByName(self, sName, sDesc, fChildren = False):
844 """ Kills the named process, optionally including children. """
845 cKilled = 0;
846 aoProcesses = utils.processListAll();
847 for oProcess in aoProcesses:
848 sBase = oProcess.getBaseImageNameNoExeSuff();
849 if sBase is not None and sBase.lower() == sName:
850 reporter.log('Killing %s process: %s (%s)' % (sDesc, oProcess.iPid, sBase));
851 utils.processKill(oProcess.iPid);
852 cKilled += 1;
853
854 if fChildren:
855 for oChild in aoProcesses:
856 if oChild.iParentPid == oProcess.iPid and oChild.iParentPid is not None:
857 reporter.log('Killing %s child process: %s (%s)' % (sDesc, oChild.iPid, sBase));
858 utils.processKill(oChild.iPid);
859 cKilled += 1;
860 return cKilled;
861
862 def _terminateProcessesByNameAndArgSubstr(self, sName, sArg, sDesc):
863 """
864 Terminates the named process using taskkill.exe, if any of its args
865 contains the passed string.
866 """
867 cKilled = 0;
868 aoProcesses = utils.processListAll();
869 for oProcess in aoProcesses:
870 sBase = oProcess.getBaseImageNameNoExeSuff();
871 if sBase is not None and sBase.lower() == sName and any(sArg in s for s in oProcess.asArgs):
872
873 reporter.log('Killing %s process: %s (%s)' % (sDesc, oProcess.iPid, sBase));
874 self._executeSync(['taskkill.exe', '/pid', '%u' % (oProcess.iPid,)]);
875 cKilled += 1;
876 return cKilled;
877
878 def _uninstallVBoxOnWindows(self, sMode):
879 """
880 Uninstalls VBox on Windows, all installations we find to be on the safe side...
881 """
882 assert sMode in ['install', 'uninstall',];
883
884 import win32com.client; # pylint: disable=import-error
885 win32com.client.gencache.EnsureModule('{000C1092-0000-0000-C000-000000000046}', 1033, 1, 0);
886 oInstaller = win32com.client.Dispatch('WindowsInstaller.Installer',
887 resultCLSID = '{000C1090-0000-0000-C000-000000000046}')
888
889 # Search installed products for VirtualBox.
890 asProdCodes = [];
891 for sProdCode in oInstaller.Products:
892 try:
893 sProdName = oInstaller.ProductInfo(sProdCode, "ProductName");
894 except:
895 reporter.logXcpt();
896 continue;
897 #reporter.log('Info: %s=%s' % (sProdCode, sProdName));
898 if sProdName.startswith('Oracle VM VirtualBox') \
899 or sProdName.startswith('Sun VirtualBox'):
900 asProdCodes.append([sProdCode, sProdName]);
901
902 # Before we start uninstalling anything, just ruthlessly kill any cdb,
903 # msiexec, drvinst and some rundll process we might find hanging around.
904 if self._isProcessPresent('rundll32'):
905 cTimes = 0;
906 while cTimes < 3:
907 cTimes += 1;
908 cKilled = self._terminateProcessesByNameAndArgSubstr('rundll32', 'InstallSecurityPromptRunDllW',
909 'MSI driver installation');
910 if cKilled <= 0:
911 break;
912 time.sleep(10); # Give related drvinst process a chance to clean up after we killed the verification dialog.
913
914 if self._isProcessPresent('drvinst'):
915 time.sleep(15); # In the hope that it goes away.
916 cTimes = 0;
917 while cTimes < 4:
918 cTimes += 1;
919 cKilled = self._killProcessesByName('drvinst', 'MSI driver installation', True);
920 if cKilled <= 0:
921 break;
922 time.sleep(10); # Give related MSI process a chance to clean up after we killed the driver installer.
923
924 if self._isProcessPresent('msiexec'):
925 cTimes = 0;
926 while cTimes < 3:
927 reporter.log('found running msiexec process, waiting a bit...');
928 time.sleep(20) # In the hope that it goes away.
929 if not self._isProcessPresent('msiexec'):
930 break;
931 cTimes += 1;
932 ## @todo this could also be the msiexec system service, try to detect this case!
933 if cTimes >= 6:
934 cKilled = self._killProcessesByName('msiexec', 'MSI driver installation');
935 if cKilled > 0:
936 time.sleep(16); # fudge.
937
938 # cdb.exe sometimes stays running (from utils.getProcessInfo), blocking
939 # the scratch directory. No idea why.
940 if self._isProcessPresent('cdb'):
941 cTimes = 0;
942 while cTimes < 3:
943 cKilled = self._killProcessesByName('cdb', 'cdb.exe from getProcessInfo');
944 if cKilled <= 0:
945 break;
946 time.sleep(2); # fudge.
947
948 # Do the uninstalling.
949 fRc = True;
950 sLogFile = os.path.join(self.sScratchPath, 'VBoxUninstallLog.txt');
951 for sProdCode, sProdName in asProdCodes:
952 reporter.log('Uninstalling %s (%s)...' % (sProdName, sProdCode));
953 fRc2, iRc = self._sudoExecuteSync(['msiexec', '/uninstall', sProdCode, '/quiet', '/passive', '/norestart',
954 '/L*v', '%s' % (sLogFile), ]);
955 if fRc2 is False:
956 if iRc == 3010: # ERROR_SUCCESS_REBOOT_REQUIRED
957 reporter.error('Uninstaller required a reboot to complete uninstallation');
958 else:
959 reporter.error('Uninstaller failed, exit code: %s' % (iRc,));
960 fRc = False;
961
962 self._waitForTestManagerConnectivity(30);
963
964 # Upload the log on failure. Do it early if the extra cleanups below causes trouble.
965 if fRc is False and os.path.isfile(sLogFile):
966 reporter.addLogFile(sLogFile, 'log/uninstaller', "Verbose MSI uninstallation log file");
967 sLogFile = None;
968
969 # Log driver service states (should ls \Driver\VBox* and \Device\VBox*).
970 fHadLeftovers = False;
971 asLeftovers = [];
972 for sService in reversed(self.kasWindowsServices):
973 cTries = 0;
974 while True:
975 fRc2, _ = self._sudoExecuteSync(['sc.exe', 'query', sService]);
976 if not fRc2:
977 break;
978 fHadLeftovers = True;
979
980 cTries += 1;
981 if cTries > 3:
982 asLeftovers.append(sService,);
983 break;
984
985 # Get the status output.
986 try:
987 sOutput = utils.sudoProcessOutputChecked(['sc.exe', 'query', sService]);
988 except:
989 reporter.logXcpt();
990 else:
991 if re.search(r'STATE\s+:\s*1\s*STOPPED', sOutput) is None:
992 reporter.log('Trying to stop %s...' % (sService,));
993 fRc2, _ = self._sudoExecuteSync(['sc.exe', 'stop', sService]);
994 time.sleep(1); # fudge
995
996 reporter.log('Trying to delete %s...' % (sService,));
997 self._sudoExecuteSync(['sc.exe', 'delete', sService]);
998
999 time.sleep(1); # fudge
1000
1001 if asLeftovers:
1002 reporter.log('Warning! Leftover VBox drivers: %s' % (', '.join(asLeftovers),));
1003 fRc = False;
1004
1005 if fHadLeftovers:
1006 self._waitForTestManagerConnectivity(30);
1007
1008 # Upload the log if we have any leftovers and didn't upload it already.
1009 if sLogFile is not None and (fRc is False or fHadLeftovers) and os.path.isfile(sLogFile):
1010 reporter.addLogFile(sLogFile, 'log/uninstaller', "Verbose MSI uninstallation log file");
1011
1012 return fRc;
1013
1014
1015 #
1016 # Extension pack.
1017 #
1018
1019 def _getVBoxInstallPath(self, fFailIfNotFound):
1020 """ Returns the default VBox installation path. """
1021 sHost = utils.getHostOs();
1022 if sHost == 'win':
1023 sProgFiles = os.environ.get('ProgramFiles', 'C:\\Program Files');
1024 asLocs = [
1025 os.path.join(sProgFiles, 'Oracle', 'VirtualBox'),
1026 os.path.join(sProgFiles, 'OracleVM', 'VirtualBox'),
1027 os.path.join(sProgFiles, 'Sun', 'VirtualBox'),
1028 ];
1029 elif sHost in ('linux', 'solaris',):
1030 asLocs = [ '/opt/VirtualBox', '/opt/VirtualBox-3.2', '/opt/VirtualBox-3.1', '/opt/VirtualBox-3.0'];
1031 elif sHost == 'darwin':
1032 asLocs = [ '/Applications/VirtualBox.app/Contents/MacOS' ];
1033 else:
1034 asLocs = [ '/opt/VirtualBox' ];
1035 if 'VBOX_INSTALL_PATH' in os.environ:
1036 asLocs.insert(0, os.environ.get('VBOX_INSTALL_PATH', None));
1037
1038 for sLoc in asLocs:
1039 if os.path.isdir(sLoc):
1040 return sLoc;
1041 if fFailIfNotFound:
1042 reporter.error('Failed to locate VirtualBox installation: %s' % (asLocs,));
1043 else:
1044 reporter.log2('Failed to locate VirtualBox installation: %s' % (asLocs,));
1045 return None;
1046
1047 def _installExtPack(self):
1048 """ Installs the extension pack. """
1049 sVBox = self._getVBoxInstallPath(fFailIfNotFound = True);
1050 if sVBox is None:
1051 return False;
1052 sExtPackDir = os.path.join(sVBox, 'ExtensionPacks');
1053
1054 if self._uninstallAllExtPacks() is not True:
1055 return False;
1056
1057 sExtPack = self._findFile('Oracle_VM_VirtualBox_Extension_Pack.vbox-extpack');
1058 if sExtPack is None:
1059 sExtPack = self._findFile('Oracle_VM_VirtualBox_Extension_Pack.*.vbox-extpack');
1060 if sExtPack is None:
1061 return True;
1062
1063 sDstDir = os.path.join(sExtPackDir, 'Oracle_VM_VirtualBox_Extension_Pack');
1064 reporter.log('Installing extension pack "%s" to "%s"...' % (sExtPack, sExtPackDir));
1065 fRc, _ = self._sudoExecuteSync([ self.getBinTool('vts_tar'),
1066 '--extract',
1067 '--verbose',
1068 '--gzip',
1069 '--file', sExtPack,
1070 '--directory', sDstDir,
1071 '--file-mode-and-mask', '0644',
1072 '--file-mode-or-mask', '0644',
1073 '--dir-mode-and-mask', '0755',
1074 '--dir-mode-or-mask', '0755',
1075 '--owner', '0',
1076 '--group', '0',
1077 ]);
1078 return fRc;
1079
1080 def _uninstallAllExtPacks(self):
1081 """ Uninstalls all extension packs. """
1082 sVBox = self._getVBoxInstallPath(fFailIfNotFound = False);
1083 if sVBox is None:
1084 return True;
1085
1086 sExtPackDir = os.path.join(sVBox, 'ExtensionPacks');
1087 if not os.path.exists(sExtPackDir):
1088 return True;
1089
1090 fRc, _ = self._sudoExecuteSync([self.getBinTool('vts_rm'), '-Rfv', '--', sExtPackDir]);
1091 return fRc;
1092
1093
1094
1095if __name__ == '__main__':
1096 sys.exit(VBoxInstallerTestDriver().main(sys.argv));
1097
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