VirtualBox

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

Last change on this file since 78396 was 78385, checked in by vboxsync, 6 years ago

vboxinstaller.py: Don't let the installation report success when it was skipped. Also added log message about why we usually skip stuff.

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