VirtualBox

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

Last change on this file since 84187 was 82968, checked in by vboxsync, 5 years ago

Copyright year updates by scm.

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