VirtualBox

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

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

ValKit/vboxinstaller.py: Made left behind drivers a cause for failure. Also fail if the installer requires a reboot.

  • Property svn:eol-style set to LF
  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
File size: 39.0 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: 80232 $"
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', 'vboxsds', 'virtualbox', 'virtualboxvm', 'vboxheadless', 'vboxmanage', 'vboxsdl',
309 'vboxwebsrv', 'vboxautostart', 'vboxballoonctrl', 'vboxbfe', 'vboxextpackhelperapp', 'vboxnetdhcp',
310 'vboxnetnat', 'vboxnetadpctl', 'vboxtestogl', 'vboxtunctl', 'vboxvmmpreload', 'vboxxpcomipcd', ]:
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 == 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 == 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(False);
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(False);
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
778 fRc2, iRc = self._sudoExecuteSync(asArgs);
779 if fRc2 is False:
780 if iRc == 3010: # ERROR_SUCCESS_REBOOT_REQUIRED
781 reporter.error('Installer required a reboot to complete installation (ERROR_SUCCESS_REBOOT_REQUIRED)');
782 else:
783 reporter.error('Installer failed, exit code: %s' % (iRc,));
784 fRc = False;
785
786 sLogFile = os.path.join(tempfile.gettempdir(), 'VirtualBox', 'VBoxInstallLog.txt');
787 if os.path.isfile(sLogFile):
788 reporter.addLogFile(sLogFile, 'log/installer', "Verbose MSI installation log file");
789 self._waitForTestManagerConnectivity(30);
790 return fRc;
791
792 def _isProcessPresent(self, sName):
793 """ Checks whether the named process is present or not. """
794 for oProcess in utils.processListAll():
795 sBase = oProcess.getBaseImageNameNoExeSuff();
796 if sBase is not None and sBase.lower() == sName:
797 return True;
798 return False;
799
800 def _killProcessesByName(self, sName, sDesc, fChildren = False):
801 """ Kills the named process, optionally including children. """
802 cKilled = 0;
803 aoProcesses = utils.processListAll();
804 for oProcess in aoProcesses:
805 sBase = oProcess.getBaseImageNameNoExeSuff();
806 if sBase is not None and sBase.lower() == sName:
807 reporter.log('Killing %s process: %s (%s)' % (sDesc, oProcess.iPid, sBase));
808 utils.processKill(oProcess.iPid);
809 cKilled += 1;
810
811 if fChildren:
812 for oChild in aoProcesses:
813 if oChild.iParentPid == oProcess.iPid and oChild.iParentPid is not None:
814 reporter.log('Killing %s child process: %s (%s)' % (sDesc, oChild.iPid, sBase));
815 utils.processKill(oChild.iPid);
816 cKilled += 1;
817 return cKilled;
818
819 def _terminateProcessesByNameAndArgSubstr(self, sName, sArg, sDesc):
820 """
821 Terminates the named process using taskkill.exe, if any of its args
822 contains the passed string.
823 """
824 cKilled = 0;
825 aoProcesses = utils.processListAll();
826 for oProcess in aoProcesses:
827 sBase = oProcess.getBaseImageNameNoExeSuff();
828 if sBase is not None and sBase.lower() == sName and any(sArg in s for s in oProcess.asArgs):
829
830 reporter.log('Killing %s process: %s (%s)' % (sDesc, oProcess.iPid, sBase));
831 self._executeSync(['taskkill.exe', '/pid', '%u' % (oProcess.iPid,)]);
832 cKilled += 1;
833 return cKilled;
834
835 def _uninstallVBoxOnWindows(self, fIgnoreServices = False):
836 """
837 Uninstalls VBox on Windows, all installations we find to be on the safe side...
838 """
839
840 import win32com.client; # pylint: disable=import-error
841 win32com.client.gencache.EnsureModule('{000C1092-0000-0000-C000-000000000046}', 1033, 1, 0);
842 oInstaller = win32com.client.Dispatch('WindowsInstaller.Installer',
843 resultCLSID = '{000C1090-0000-0000-C000-000000000046}')
844
845 # Search installed products for VirtualBox.
846 asProdCodes = [];
847 for sProdCode in oInstaller.Products:
848 try:
849 sProdName = oInstaller.ProductInfo(sProdCode, "ProductName");
850 except:
851 reporter.logXcpt();
852 continue;
853 #reporter.log('Info: %s=%s' % (sProdCode, sProdName));
854 if sProdName.startswith('Oracle VM VirtualBox') \
855 or sProdName.startswith('Sun VirtualBox'):
856 asProdCodes.append([sProdCode, sProdName]);
857
858 # Before we start uninstalling anything, just ruthlessly kill any cdb,
859 # msiexec, drvinst and some rundll process we might find hanging around.
860 if self._isProcessPresent('rundll32'):
861 cTimes = 0;
862 while cTimes < 3:
863 cTimes += 1;
864 cKilled = self._terminateProcessesByNameAndArgSubstr('rundll32', 'InstallSecurityPromptRunDllW',
865 'MSI driver installation');
866 if cKilled <= 0:
867 break;
868 time.sleep(10); # Give related drvinst process a chance to clean up after we killed the verification dialog.
869
870 if self._isProcessPresent('drvinst'):
871 time.sleep(15); # In the hope that it goes away.
872 cTimes = 0;
873 while cTimes < 4:
874 cTimes += 1;
875 cKilled = self._killProcessesByName('drvinst', 'MSI driver installation', True);
876 if cKilled <= 0:
877 break;
878 time.sleep(10); # Give related MSI process a chance to clean up after we killed the driver installer.
879
880 if self._isProcessPresent('msiexec'):
881 cTimes = 0;
882 while cTimes < 3:
883 reporter.log('found running msiexec process, waiting a bit...');
884 time.sleep(20) # In the hope that it goes away.
885 if not self._isProcessPresent('msiexec'):
886 break;
887 cTimes += 1;
888 ## @todo this could also be the msiexec system service, try to detect this case!
889 if cTimes >= 6:
890 cKilled = self._killProcessesByName('msiexec', 'MSI driver installation');
891 if cKilled > 0:
892 time.sleep(16); # fudge.
893
894 # cdb.exe sometimes stays running (from utils.getProcessInfo), blocking
895 # the scratch directory. No idea why.
896 if self._isProcessPresent('cdb'):
897 cTimes = 0;
898 while cTimes < 3:
899 cKilled = self._killProcessesByName('cdb', 'cdb.exe from getProcessInfo');
900 if cKilled <= 0:
901 break;
902 time.sleep(2); # fudge.
903
904 # Do the uninstalling.
905 fRc = True;
906 sLogFile = os.path.join(self.sScratchPath, 'VBoxUninstallLog.txt');
907 for sProdCode, sProdName in asProdCodes:
908 reporter.log('Uninstalling %s (%s)...' % (sProdName, sProdCode));
909 fRc2, iRc = self._sudoExecuteSync(['msiexec', '/uninstall', sProdCode, '/quiet', '/passive', '/norestart',
910 '/L*v', '%s' % (sLogFile), ]);
911 if fRc2 is False:
912 if iRc == 3010: # ERROR_SUCCESS_REBOOT_REQUIRED
913 reporter.error('Uninstaller required a reboot to complete uninstallation');
914 reporter.addLogFile(sLogFile, 'log/uninstaller_reboot',
915 "Verbose MSI uninstallation log file (reboot required)");
916 else:
917 reporter.error('Uninstaller failed, exit code: %s' % (iRc,));
918 fRc = False;
919
920 self._waitForTestManagerConnectivity(30);
921 if fRc is False and os.path.isfile(sLogFile):
922 reporter.addLogFile(sLogFile, 'log/uninstaller', "Verbose MSI uninstallation log file");
923
924 # Log driver service states (should ls \Driver\VBox* and \Device\VBox*).
925 for sService in self.kasWindowsServices:
926 fRc2, _ = self._sudoExecuteSync(['sc.exe', 'query', sService]);
927 if fIgnoreServices is False and fRc2 is True:
928 fRc = False
929
930 return fRc;
931
932
933 #
934 # Extension pack.
935 #
936
937 def _getVBoxInstallPath(self, fFailIfNotFound):
938 """ Returns the default VBox installation path. """
939 sHost = utils.getHostOs();
940 if sHost == 'win':
941 sProgFiles = os.environ.get('ProgramFiles', 'C:\\Program Files');
942 asLocs = [
943 os.path.join(sProgFiles, 'Oracle', 'VirtualBox'),
944 os.path.join(sProgFiles, 'OracleVM', 'VirtualBox'),
945 os.path.join(sProgFiles, 'Sun', 'VirtualBox'),
946 ];
947 elif sHost in ('linux', 'solaris',):
948 asLocs = [ '/opt/VirtualBox', '/opt/VirtualBox-3.2', '/opt/VirtualBox-3.1', '/opt/VirtualBox-3.0'];
949 elif sHost == 'darwin':
950 asLocs = [ '/Applications/VirtualBox.app/Contents/MacOS' ];
951 else:
952 asLocs = [ '/opt/VirtualBox' ];
953 if 'VBOX_INSTALL_PATH' in os.environ:
954 asLocs.insert(0, os.environ.get('VBOX_INSTALL_PATH', None));
955
956 for sLoc in asLocs:
957 if os.path.isdir(sLoc):
958 return sLoc;
959 if fFailIfNotFound:
960 reporter.error('Failed to locate VirtualBox installation: %s' % (asLocs,));
961 else:
962 reporter.log2('Failed to locate VirtualBox installation: %s' % (asLocs,));
963 return None;
964
965 def _installExtPack(self):
966 """ Installs the extension pack. """
967 sVBox = self._getVBoxInstallPath(fFailIfNotFound = True);
968 if sVBox is None:
969 return False;
970 sExtPackDir = os.path.join(sVBox, 'ExtensionPacks');
971
972 if self._uninstallAllExtPacks() is not True:
973 return False;
974
975 sExtPack = self._findFile('Oracle_VM_VirtualBox_Extension_Pack.vbox-extpack');
976 if sExtPack is None:
977 sExtPack = self._findFile('Oracle_VM_VirtualBox_Extension_Pack.*.vbox-extpack');
978 if sExtPack is None:
979 return True;
980
981 sDstDir = os.path.join(sExtPackDir, 'Oracle_VM_VirtualBox_Extension_Pack');
982 reporter.log('Installing extension pack "%s" to "%s"...' % (sExtPack, sExtPackDir));
983 fRc, _ = self._sudoExecuteSync([ self.getBinTool('vts_tar'),
984 '--extract',
985 '--verbose',
986 '--gzip',
987 '--file', sExtPack,
988 '--directory', sDstDir,
989 '--file-mode-and-mask', '0644',
990 '--file-mode-or-mask', '0644',
991 '--dir-mode-and-mask', '0755',
992 '--dir-mode-or-mask', '0755',
993 '--owner', '0',
994 '--group', '0',
995 ]);
996 return fRc;
997
998 def _uninstallAllExtPacks(self):
999 """ Uninstalls all extension packs. """
1000 sVBox = self._getVBoxInstallPath(fFailIfNotFound = False);
1001 if sVBox is None:
1002 return True;
1003
1004 sExtPackDir = os.path.join(sVBox, 'ExtensionPacks');
1005 if not os.path.exists(sExtPackDir):
1006 return True;
1007
1008 fRc, _ = self._sudoExecuteSync([self.getBinTool('vts_rm'), '-Rfv', '--', sExtPackDir]);
1009 return fRc;
1010
1011
1012
1013if __name__ == '__main__':
1014 sys.exit(VBoxInstallerTestDriver().main(sys.argv));
1015
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