VirtualBox

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

Last change on this file was 106836, checked in by vboxsync, 5 weeks ago

Validation Kit/vboxinstaller: Use 'misc/other' for the setupapi logs, to make it compatible with the test manager.

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

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