VirtualBox

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

最後變更 在這個檔案從92782是 92330,由 vboxsync 提交於 3 年 前

ValKit/vboxinstaller.py: Retry 'hdituil detach' for 6 time with a 5 seconds delay between each to try fix the occational 'hdiutil: couldn't unmount disk2 - Resource busy' problem.

  • 屬性 svn:eol-style 設為 LF
  • 屬性 svn:executable 設為 *
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 43.2 KB
 
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.alldomusa.eu.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: 92330 $"
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 # In case it's busy for some reason or another, just retry after a little delay.
602 for iTry in range(6):
603 time.sleep(5);
604 reporter.error('Retry #%s unmount DMT at %s' % (iTry + 1, sMountPath,));
605 fRc = self._executeSync(['hdiutil', 'detach', sMountPath ]);
606 if fRc:
607 break;
608 if not fRc:
609 reporter.error('Failed to unmount DMG at %s' % (sMountPath,));
610
611 # Remove dir.
612 try:
613 os.rmdir(sMountPath);
614 except:
615 if not fIgnoreError:
616 reporter.errorXcpt('Failed to remove directory %s' % (sMountPath,));
617 return fRc;
618
619 def _darwinMountDmg(self, sDmg):
620 """
621 Mount the DMG at the default mount point.
622 """
623 self._darwinUnmountDmg(fIgnoreError = True)
624
625 sMountPath = self._darwinDmgPath();
626 if not os.path.exists(sMountPath):
627 try:
628 os.mkdir(sMountPath, 0o755);
629 except:
630 reporter.logXcpt();
631 return False;
632
633 return self._executeSync(['hdiutil', 'attach', '-readonly', '-mount', 'required', '-mountpoint', sMountPath, sDmg, ]);
634
635 def _installVBoxOnDarwin(self):
636 """ Installs VBox on Mac OS X."""
637 sDmg = self._findFile('^VirtualBox-.*\\.dmg$');
638 if sDmg is None:
639 return False;
640
641 # Mount the DMG.
642 fRc = self._darwinMountDmg(sDmg);
643 if fRc is not True:
644 return False;
645
646 # Uninstall any previous vbox version first.
647 sUninstaller = os.path.join(self._darwinDmgPath(), 'VirtualBox_Uninstall.tool');
648 fRc, _ = self._sudoExecuteSync([sUninstaller, '--unattended',]);
649 if fRc is True:
650
651 # Install the package.
652 sPkg = os.path.join(self._darwinDmgPath(), 'VirtualBox.pkg');
653 fRc, _ = self._sudoExecuteSync(['installer', '-verbose', '-dumplog', '-pkg', sPkg, '-target', '/']);
654
655 # Unmount the DMG and we're done.
656 if not self._darwinUnmountDmg(fIgnoreError = False):
657 fRc = False;
658 return fRc;
659
660 def _uninstallVBoxOnDarwin(self):
661 """ Uninstalls VBox on Mac OS X."""
662
663 # Is VirtualBox installed? If not, don't try uninstall it.
664 sVBox = self._getVBoxInstallPath(fFailIfNotFound = False);
665 if sVBox is None:
666 return True;
667
668 # Find the dmg.
669 sDmg = self._findFile('^VirtualBox-.*\\.dmg$');
670 if sDmg is None:
671 return False;
672 if not os.path.exists(sDmg):
673 return True;
674
675 # Mount the DMG.
676 fRc = self._darwinMountDmg(sDmg);
677 if fRc is not True:
678 return False;
679
680 # Execute the uninstaller.
681 sUninstaller = os.path.join(self._darwinDmgPath(), 'VirtualBox_Uninstall.tool');
682 fRc, _ = self._sudoExecuteSync([sUninstaller, '--unattended',]);
683
684 # Unmount the DMG and we're done.
685 if not self._darwinUnmountDmg(fIgnoreError = False):
686 fRc = False;
687 return fRc;
688
689 #
690 # GNU/Linux
691 #
692
693 def _installVBoxOnLinux(self):
694 """ Installs VBox on Linux."""
695 sRun = self._findFile('^VirtualBox-.*\\.run$');
696 if sRun is None:
697 return False;
698 utils.chmodPlusX(sRun);
699
700 # Install the new one.
701 fRc, _ = self._sudoExecuteSync([sRun,]);
702 return fRc;
703
704 def _uninstallVBoxOnLinux(self):
705 """ Uninstalls VBox on Linux."""
706
707 # Is VirtualBox installed? If not, don't try uninstall it.
708 sVBox = self._getVBoxInstallPath(fFailIfNotFound = False);
709 if sVBox is None:
710 return True;
711
712 # Find the .run file and use it.
713 sRun = self._findFile('^VirtualBox-.*\\.run$', fMandatory = False);
714 if sRun is not None:
715 utils.chmodPlusX(sRun);
716 fRc, _ = self._sudoExecuteSync([sRun, 'uninstall']);
717 return fRc;
718
719 # Try the installed uninstaller.
720 for sUninstaller in [os.path.join(sVBox, 'uninstall.sh'), '/opt/VirtualBox/uninstall.sh', ]:
721 if os.path.isfile(sUninstaller):
722 reporter.log('Invoking "%s"...' % (sUninstaller,));
723 fRc, _ = self._sudoExecuteSync([sUninstaller, 'uninstall']);
724 return fRc;
725
726 reporter.log('Did not find any VirtualBox install to uninstall.');
727 return True;
728
729
730 #
731 # Solaris
732 #
733
734 def _generateAutoResponseOnSolaris(self):
735 """
736 Generates an autoresponse file on solaris, returning the name.
737 None is return on failure.
738 """
739 sPath = os.path.join(self.sScratchPath, 'SolarisAutoResponse');
740 oFile = utils.openNoInherit(sPath, 'wt');
741 oFile.write('basedir=default\n'
742 'runlevel=nocheck\n'
743 'conflict=quit\n'
744 'setuid=nocheck\n'
745 'action=nocheck\n'
746 'partial=quit\n'
747 'instance=unique\n'
748 'idepend=quit\n'
749 'rdepend=quit\n'
750 'space=quit\n'
751 'mail=\n');
752 oFile.close();
753 return sPath;
754
755 def _installVBoxOnSolaris(self):
756 """ Installs VBox on Solaris."""
757 sPkg = self._findFile('^VirtualBox-.*\\.pkg$', fMandatory = False);
758 if sPkg is None:
759 sTar = self._findFile('^VirtualBox-.*-SunOS-.*\\.tar.gz$', fMandatory = False);
760 if sTar is not None:
761 if self._maybeUnpackArchive(sTar) is not True:
762 return False;
763 sPkg = self._findFile('^VirtualBox-.*\\.pkg$', fMandatory = True);
764 sRsp = self._findFile('^autoresponse$', fMandatory = True);
765 if sPkg is None or sRsp is None:
766 return False;
767
768 # Uninstall first (ignore result).
769 self._uninstallVBoxOnSolaris(False);
770
771 # Install the new one.
772 fRc, _ = self._sudoExecuteSync(['pkgadd', '-d', sPkg, '-n', '-a', sRsp, 'SUNWvbox']);
773 return fRc;
774
775 def _uninstallVBoxOnSolaris(self, fRestartSvcConfigD):
776 """ Uninstalls VBox on Solaris."""
777 reporter.flushall();
778 if utils.processCall(['pkginfo', '-q', 'SUNWvbox']) != 0:
779 return True;
780 sRsp = self._generateAutoResponseOnSolaris();
781 fRc, _ = self._sudoExecuteSync(['pkgrm', '-n', '-a', sRsp, 'SUNWvbox']);
782
783 #
784 # Restart the svc.configd as it has a tendency to clog up with time and
785 # become unresponsive. It will handle SIGHUP by exiting the sigwait()
786 # look in the main function and shut down the service nicely (backend_fini).
787 # The restarter will then start a new instance of it.
788 #
789 if fRestartSvcConfigD:
790 time.sleep(1); # Give it a chance to flush pkgrm stuff.
791 self._sudoExecuteSync(['pkill', '-HUP', 'svc.configd']);
792 time.sleep(5); # Spare a few cpu cycles it to shutdown and restart.
793
794 return fRc;
795
796 #
797 # Windows
798 #
799
800 ## VBox windows services we can query the status of.
801 kasWindowsServices = [ 'vboxdrv', 'vboxusbmon', 'vboxnetadp', 'vboxnetflt', 'vboxnetlwf' ];
802
803 def _installVBoxOnWindows(self):
804 """ Installs VBox on Windows."""
805 sExe = self._findFile('^VirtualBox-.*-(MultiArch|Win).exe$');
806 if sExe is None:
807 return False;
808
809 # TEMPORARY HACK - START
810 # It seems that running the NDIS cleanup script upon uninstallation is not
811 # a good idea, so let's run it before installing VirtualBox.
812 #sHostName = socket.getfqdn();
813 #if not sHostName.startswith('testboxwin3') \
814 # and not sHostName.startswith('testboxharp2') \
815 # and not sHostName.startswith('wei01-b6ka-3') \
816 # and utils.getHostOsVersion() in ['8', '8.1', '9', '2008Server', '2008ServerR2', '2012Server']:
817 # reporter.log('Peforming extra NDIS cleanup...');
818 # sMagicScript = os.path.abspath(os.path.join(g_ksValidationKitDir, 'testdriver', 'win-vbox-net-uninstall.ps1'));
819 # fRc2, _ = self._sudoExecuteSync(['powershell.exe', '-Command', 'set-executionpolicy unrestricted']);
820 # if not fRc2:
821 # reporter.log('set-executionpolicy failed.');
822 # self._sudoExecuteSync(['powershell.exe', '-Command', 'get-executionpolicy']);
823 # fRc2, _ = self._sudoExecuteSync(['powershell.exe', '-File', sMagicScript]);
824 # if not fRc2:
825 # reporter.log('NDIS cleanup failed.');
826 # TEMPORARY HACK - END
827
828 # Uninstall any previous vbox version first.
829 fRc = self._uninstallVBoxOnWindows('install');
830 if fRc is not True:
831 return None; # There shouldn't be anything to uninstall, and if there is, it's not our fault.
832
833 # Install the new one.
834 asArgs = [sExe, '-vvvv', '--silent', '--logging'];
835 asArgs.extend(['--msiparams', 'REBOOT=ReallySuppress']);
836 sVBoxInstallPath = os.environ.get('VBOX_INSTALL_PATH', None);
837 if sVBoxInstallPath is not None:
838 asArgs.extend(['INSTALLDIR="%s"' % (sVBoxInstallPath,)]);
839
840 fRc2, iRc = self._sudoExecuteSync(asArgs);
841 if fRc2 is False:
842 if iRc == 3010: # ERROR_SUCCESS_REBOOT_REQUIRED
843 reporter.error('Installer required a reboot to complete installation (ERROR_SUCCESS_REBOOT_REQUIRED)');
844 else:
845 reporter.error('Installer failed, exit code: %s' % (iRc,));
846 fRc = False;
847
848 sLogFile = os.path.join(tempfile.gettempdir(), 'VirtualBox', 'VBoxInstallLog.txt');
849 if os.path.isfile(sLogFile):
850 reporter.addLogFile(sLogFile, 'log/installer', "Verbose MSI installation log file");
851 self._waitForTestManagerConnectivity(30);
852 return fRc;
853
854 def _isProcessPresent(self, sName):
855 """ Checks whether the named process is present or not. """
856 for oProcess in utils.processListAll():
857 sBase = oProcess.getBaseImageNameNoExeSuff();
858 if sBase is not None and sBase.lower() == sName:
859 return True;
860 return False;
861
862 def _killProcessesByName(self, sName, sDesc, fChildren = False):
863 """ Kills the named process, optionally including children. """
864 cKilled = 0;
865 aoProcesses = utils.processListAll();
866 for oProcess in aoProcesses:
867 sBase = oProcess.getBaseImageNameNoExeSuff();
868 if sBase is not None and sBase.lower() == sName:
869 reporter.log('Killing %s process: %s (%s)' % (sDesc, oProcess.iPid, sBase));
870 utils.processKill(oProcess.iPid);
871 cKilled += 1;
872
873 if fChildren:
874 for oChild in aoProcesses:
875 if oChild.iParentPid == oProcess.iPid and oChild.iParentPid is not None:
876 reporter.log('Killing %s child process: %s (%s)' % (sDesc, oChild.iPid, sBase));
877 utils.processKill(oChild.iPid);
878 cKilled += 1;
879 return cKilled;
880
881 def _terminateProcessesByNameAndArgSubstr(self, sName, sArg, sDesc):
882 """
883 Terminates the named process using taskkill.exe, if any of its args
884 contains the passed string.
885 """
886 cKilled = 0;
887 aoProcesses = utils.processListAll();
888 for oProcess in aoProcesses:
889 sBase = oProcess.getBaseImageNameNoExeSuff();
890 if sBase is not None and sBase.lower() == sName and any(sArg in s for s in oProcess.asArgs):
891
892 reporter.log('Killing %s process: %s (%s)' % (sDesc, oProcess.iPid, sBase));
893 self._executeSync(['taskkill.exe', '/pid', '%u' % (oProcess.iPid,)]);
894 cKilled += 1;
895 return cKilled;
896
897 def _uninstallVBoxOnWindows(self, sMode):
898 """
899 Uninstalls VBox on Windows, all installations we find to be on the safe side...
900 """
901 assert sMode in ['install', 'uninstall',];
902
903 import win32com.client; # pylint: disable=import-error
904 win32com.client.gencache.EnsureModule('{000C1092-0000-0000-C000-000000000046}', 1033, 1, 0);
905 oInstaller = win32com.client.Dispatch('WindowsInstaller.Installer',
906 resultCLSID = '{000C1090-0000-0000-C000-000000000046}')
907
908 # Search installed products for VirtualBox.
909 asProdCodes = [];
910 for sProdCode in oInstaller.Products:
911 try:
912 sProdName = oInstaller.ProductInfo(sProdCode, "ProductName");
913 except:
914 reporter.logXcpt();
915 continue;
916 #reporter.log('Info: %s=%s' % (sProdCode, sProdName));
917 if sProdName.startswith('Oracle VM VirtualBox') \
918 or sProdName.startswith('Sun VirtualBox'):
919 asProdCodes.append([sProdCode, sProdName]);
920
921 # Before we start uninstalling anything, just ruthlessly kill any cdb,
922 # msiexec, drvinst and some rundll process we might find hanging around.
923 if self._isProcessPresent('rundll32'):
924 cTimes = 0;
925 while cTimes < 3:
926 cTimes += 1;
927 cKilled = self._terminateProcessesByNameAndArgSubstr('rundll32', 'InstallSecurityPromptRunDllW',
928 'MSI driver installation');
929 if cKilled <= 0:
930 break;
931 time.sleep(10); # Give related drvinst process a chance to clean up after we killed the verification dialog.
932
933 if self._isProcessPresent('drvinst'):
934 time.sleep(15); # In the hope that it goes away.
935 cTimes = 0;
936 while cTimes < 4:
937 cTimes += 1;
938 cKilled = self._killProcessesByName('drvinst', 'MSI driver installation', True);
939 if cKilled <= 0:
940 break;
941 time.sleep(10); # Give related MSI process a chance to clean up after we killed the driver installer.
942
943 if self._isProcessPresent('msiexec'):
944 cTimes = 0;
945 while cTimes < 3:
946 reporter.log('found running msiexec process, waiting a bit...');
947 time.sleep(20) # In the hope that it goes away.
948 if not self._isProcessPresent('msiexec'):
949 break;
950 cTimes += 1;
951 ## @todo this could also be the msiexec system service, try to detect this case!
952 if cTimes >= 6:
953 cKilled = self._killProcessesByName('msiexec', 'MSI driver installation');
954 if cKilled > 0:
955 time.sleep(16); # fudge.
956
957 # cdb.exe sometimes stays running (from utils.getProcessInfo), blocking
958 # the scratch directory. No idea why.
959 if self._isProcessPresent('cdb'):
960 cTimes = 0;
961 while cTimes < 3:
962 cKilled = self._killProcessesByName('cdb', 'cdb.exe from getProcessInfo');
963 if cKilled <= 0:
964 break;
965 time.sleep(2); # fudge.
966
967 # Do the uninstalling.
968 fRc = True;
969 sLogFile = os.path.join(self.sScratchPath, 'VBoxUninstallLog.txt');
970 for sProdCode, sProdName in asProdCodes:
971 reporter.log('Uninstalling %s (%s)...' % (sProdName, sProdCode));
972 fRc2, iRc = self._sudoExecuteSync(['msiexec', '/uninstall', sProdCode, '/quiet', '/passive', '/norestart',
973 '/L*v', '%s' % (sLogFile), ]);
974 if fRc2 is False:
975 if iRc == 3010: # ERROR_SUCCESS_REBOOT_REQUIRED
976 reporter.error('Uninstaller required a reboot to complete uninstallation');
977 else:
978 reporter.error('Uninstaller failed, exit code: %s' % (iRc,));
979 fRc = False;
980
981 self._waitForTestManagerConnectivity(30);
982
983 # Upload the log on failure. Do it early if the extra cleanups below causes trouble.
984 if fRc is False and os.path.isfile(sLogFile):
985 reporter.addLogFile(sLogFile, 'log/uninstaller', "Verbose MSI uninstallation log file");
986 sLogFile = None;
987
988 # Log driver service states (should ls \Driver\VBox* and \Device\VBox*).
989 fHadLeftovers = False;
990 asLeftovers = [];
991 for sService in reversed(self.kasWindowsServices):
992 cTries = 0;
993 while True:
994 fRc2, _ = self._sudoExecuteSync(['sc.exe', 'query', sService]);
995 if not fRc2:
996 break;
997 fHadLeftovers = True;
998
999 cTries += 1;
1000 if cTries > 3:
1001 asLeftovers.append(sService,);
1002 break;
1003
1004 # Get the status output.
1005 try:
1006 sOutput = utils.sudoProcessOutputChecked(['sc.exe', 'query', sService]);
1007 except:
1008 reporter.logXcpt();
1009 else:
1010 if re.search(r'STATE\s+:\s*1\s*STOPPED', sOutput) is None:
1011 reporter.log('Trying to stop %s...' % (sService,));
1012 fRc2, _ = self._sudoExecuteSync(['sc.exe', 'stop', sService]);
1013 time.sleep(1); # fudge
1014
1015 reporter.log('Trying to delete %s...' % (sService,));
1016 self._sudoExecuteSync(['sc.exe', 'delete', sService]);
1017
1018 time.sleep(1); # fudge
1019
1020 if asLeftovers:
1021 reporter.log('Warning! Leftover VBox drivers: %s' % (', '.join(asLeftovers),));
1022 fRc = False;
1023
1024 if fHadLeftovers:
1025 self._waitForTestManagerConnectivity(30);
1026
1027 # Upload the log if we have any leftovers and didn't upload it already.
1028 if sLogFile is not None and (fRc is False or fHadLeftovers) and os.path.isfile(sLogFile):
1029 reporter.addLogFile(sLogFile, 'log/uninstaller', "Verbose MSI uninstallation log file");
1030
1031 return fRc;
1032
1033
1034 #
1035 # Extension pack.
1036 #
1037
1038 def _getVBoxInstallPath(self, fFailIfNotFound):
1039 """ Returns the default VBox installation path. """
1040 sHost = utils.getHostOs();
1041 if sHost == 'win':
1042 sProgFiles = os.environ.get('ProgramFiles', 'C:\\Program Files');
1043 asLocs = [
1044 os.path.join(sProgFiles, 'Oracle', 'VirtualBox'),
1045 os.path.join(sProgFiles, 'OracleVM', 'VirtualBox'),
1046 os.path.join(sProgFiles, 'Sun', 'VirtualBox'),
1047 ];
1048 elif sHost in ('linux', 'solaris',):
1049 asLocs = [ '/opt/VirtualBox', '/opt/VirtualBox-3.2', '/opt/VirtualBox-3.1', '/opt/VirtualBox-3.0'];
1050 elif sHost == 'darwin':
1051 asLocs = [ '/Applications/VirtualBox.app/Contents/MacOS' ];
1052 else:
1053 asLocs = [ '/opt/VirtualBox' ];
1054 if 'VBOX_INSTALL_PATH' in os.environ:
1055 asLocs.insert(0, os.environ.get('VBOX_INSTALL_PATH', None));
1056
1057 for sLoc in asLocs:
1058 if os.path.isdir(sLoc):
1059 return sLoc;
1060 if fFailIfNotFound:
1061 reporter.error('Failed to locate VirtualBox installation: %s' % (asLocs,));
1062 else:
1063 reporter.log2('Failed to locate VirtualBox installation: %s' % (asLocs,));
1064 return None;
1065
1066 def _installExtPack(self):
1067 """ Installs the extension pack. """
1068 sVBox = self._getVBoxInstallPath(fFailIfNotFound = True);
1069 if sVBox is None:
1070 return False;
1071 sExtPackDir = os.path.join(sVBox, 'ExtensionPacks');
1072
1073 if self._uninstallAllExtPacks() is not True:
1074 return False;
1075
1076 sExtPack = self._findFile('Oracle_VM_VirtualBox_Extension_Pack.vbox-extpack');
1077 if sExtPack is None:
1078 sExtPack = self._findFile('Oracle_VM_VirtualBox_Extension_Pack.*.vbox-extpack');
1079 if sExtPack is None:
1080 return True;
1081
1082 sDstDir = os.path.join(sExtPackDir, 'Oracle_VM_VirtualBox_Extension_Pack');
1083 reporter.log('Installing extension pack "%s" to "%s"...' % (sExtPack, sExtPackDir));
1084 fRc, _ = self._sudoExecuteSync([ self.getBinTool('vts_tar'),
1085 '--extract',
1086 '--verbose',
1087 '--gzip',
1088 '--file', sExtPack,
1089 '--directory', sDstDir,
1090 '--file-mode-and-mask', '0644',
1091 '--file-mode-or-mask', '0644',
1092 '--dir-mode-and-mask', '0755',
1093 '--dir-mode-or-mask', '0755',
1094 '--owner', '0',
1095 '--group', '0',
1096 ]);
1097 return fRc;
1098
1099 def _uninstallAllExtPacks(self):
1100 """ Uninstalls all extension packs. """
1101 sVBox = self._getVBoxInstallPath(fFailIfNotFound = False);
1102 if sVBox is None:
1103 return True;
1104
1105 sExtPackDir = os.path.join(sVBox, 'ExtensionPacks');
1106 if not os.path.exists(sExtPackDir):
1107 return True;
1108
1109 fRc, _ = self._sudoExecuteSync([self.getBinTool('vts_rm'), '-Rfv', '--', sExtPackDir]);
1110 return fRc;
1111
1112
1113
1114if __name__ == '__main__':
1115 sys.exit(VBoxInstallerTestDriver().main(sys.argv));
1116
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

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