VirtualBox

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

Last change on this file since 67954 was 66622, checked in by vboxsync, 7 years ago

ValidationKit: Kill cdb.exe on Windows during uninstall. No idea why sometimes it hangs around (it is started in utils.getProcessInfo), but its presence blocks scratch directory removal.

  • Property svn:eol-style set to LF
  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
File size: 38.1 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-2016 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: 66622 $"
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
443 #
444 # Install the extension pack.
445 #
446 if fRc is True and self._fAutoInstallPuelExtPack:
447 fRc = self._installExtPack();
448 if fRc is False:
449 reporter.testFailure('Extension pack installation error.');
450
451 # Some debugging...
452 try:
453 cMbFreeSpace = utils.getDiskUsage(self.sScratchPath);
454 reporter.log('Disk usage after VBox install: %d MB available at %s' % (cMbFreeSpace, self.sScratchPath,));
455 except:
456 reporter.logXcpt('Unable to get disk free space. Ignored. Continuing.');
457
458 reporter.testDone();
459 return fRc;
460
461 def _uninstallVBox(self, fIgnoreError = False):
462 """
463 Uninstall VirtualBox.
464 """
465 reporter.testStart('Uninstalling VirtualBox');
466
467 sHost = utils.getHostOs()
468 if sHost == 'darwin': fRc = self._uninstallVBoxOnDarwin();
469 elif sHost == 'linux': fRc = self._uninstallVBoxOnLinux();
470 elif sHost == 'solaris': fRc = self._uninstallVBoxOnSolaris();
471 elif sHost == 'win': fRc = self._uninstallVBoxOnWindows(True);
472 else:
473 reporter.error('Unsupported host "%s".' % (sHost,));
474 if fRc is False and not fIgnoreError:
475 reporter.testFailure('Uninstallation failed.');
476
477 fRc2 = self._uninstallAllExtPacks();
478 if not fRc2 and fRc:
479 fRc = fRc2;
480
481 reporter.testDone(fSkipped = (fRc is None));
482 return fRc;
483
484 def _findFile(self, sRegExp, fMandatory = False):
485 """
486 Returns the first build file that matches the given regular expression
487 (basename only).
488
489 Returns None if no match was found, logging it as an error if
490 fMandatory is set.
491 """
492 oRegExp = re.compile(sRegExp);
493
494 for sFile in self._asBuildFiles:
495 if oRegExp.match(os.path.basename(sFile)) and os.path.exists(sFile):
496 return sFile;
497
498 if fMandatory:
499 reporter.error('Failed to find a file matching "%s" in %s.' % (sRegExp, self._asBuildFiles,));
500 return None;
501
502 def _waitForTestManagerConnectivity(self, cSecTimeout):
503 """
504 Check and wait for network connectivity to the test manager.
505
506 This is used with the windows installation and uninstallation since
507 these usually disrupts network connectivity when installing the filter
508 driver. If we proceed to quickly, we might finish the test at a time
509 when we cannot report to the test manager and thus end up with an
510 abandonded test error.
511 """
512 cSecElapsed = 0;
513 secStart = utils.timestampSecond();
514 while reporter.checkTestManagerConnection() is False:
515 cSecElapsed = utils.timestampSecond() - secStart;
516 if cSecElapsed >= cSecTimeout:
517 reporter.log('_waitForTestManagerConnectivity: Giving up after %u secs.' % (cSecTimeout,));
518 return False;
519 time.sleep(2);
520
521 if cSecElapsed > 0:
522 reporter.log('_waitForTestManagerConnectivity: Waited %s secs.' % (cSecTimeout,));
523 return True;
524
525
526 #
527 # Darwin (Mac OS X).
528 #
529
530 def _darwinDmgPath(self):
531 """ Returns the path to the DMG mount."""
532 return os.path.join(self.sScratchPath, 'DmgMountPoint');
533
534 def _darwinUnmountDmg(self, fIgnoreError):
535 """
536 Umount any DMG on at the default mount point.
537 """
538 sMountPath = self._darwinDmgPath();
539 if not os.path.exists(sMountPath):
540 return True;
541
542 # Unmount.
543 fRc = self._executeSync(['hdiutil', 'detach', sMountPath ]);
544 if not fRc and not fIgnoreError:
545 reporter.error('Failed to unmount DMG at %s' % sMountPath);
546
547 # Remove dir.
548 try:
549 os.rmdir(sMountPath);
550 except:
551 if not fIgnoreError:
552 reporter.errorXcpt('Failed to remove directory %s' % sMountPath);
553 return fRc;
554
555 def _darwinMountDmg(self, sDmg):
556 """
557 Mount the DMG at the default mount point.
558 """
559 self._darwinUnmountDmg(fIgnoreError = True)
560
561 sMountPath = self._darwinDmgPath();
562 if not os.path.exists(sMountPath):
563 try:
564 os.mkdir(sMountPath, 0755);
565 except:
566 reporter.logXcpt();
567 return False;
568
569 return self._executeSync(['hdiutil', 'attach', '-readonly', '-mount', 'required', '-mountpoint', sMountPath, sDmg, ]);
570
571 def _installVBoxOnDarwin(self):
572 """ Installs VBox on Mac OS X."""
573 sDmg = self._findFile('^VirtualBox-.*\\.dmg$');
574 if sDmg is None:
575 return False;
576
577 # Mount the DMG.
578 fRc = self._darwinMountDmg(sDmg);
579 if fRc is not True:
580 return False;
581
582 # Uninstall any previous vbox version first.
583 sUninstaller = os.path.join(self._darwinDmgPath(), 'VirtualBox_Uninstall.tool');
584 fRc, _ = self._sudoExecuteSync([sUninstaller, '--unattended',]);
585 if fRc is True:
586
587 # Install the package.
588 sPkg = os.path.join(self._darwinDmgPath(), 'VirtualBox.pkg');
589 fRc, _ = self._sudoExecuteSync(['installer', '-verbose', '-dumplog', '-pkg', sPkg, '-target', '/']);
590
591 # Unmount the DMG and we're done.
592 if not self._darwinUnmountDmg(fIgnoreError = False):
593 fRc = False;
594 return fRc;
595
596 def _uninstallVBoxOnDarwin(self):
597 """ Uninstalls VBox on Mac OS X."""
598
599 # Is VirtualBox installed? If not, don't try uninstall it.
600 sVBox = self._getVBoxInstallPath(fFailIfNotFound = False);
601 if sVBox is None:
602 return True;
603
604 # Find the dmg.
605 sDmg = self._findFile('^VirtualBox-.*\\.dmg$');
606 if sDmg is None:
607 return False;
608 if not os.path.exists(sDmg):
609 return True;
610
611 # Mount the DMG.
612 fRc = self._darwinMountDmg(sDmg);
613 if fRc is not True:
614 return False;
615
616 # Execute the uninstaller.
617 sUninstaller = os.path.join(self._darwinDmgPath(), 'VirtualBox_Uninstall.tool');
618 fRc, _ = self._sudoExecuteSync([sUninstaller, '--unattended',]);
619
620 # Unmount the DMG and we're done.
621 if not self._darwinUnmountDmg(fIgnoreError = False):
622 fRc = False;
623 return fRc;
624
625 #
626 # GNU/Linux
627 #
628
629 def _installVBoxOnLinux(self):
630 """ Installs VBox on Linux."""
631 sRun = self._findFile('^VirtualBox-.*\\.run$');
632 if sRun is None:
633 return False;
634 utils.chmodPlusX(sRun);
635
636 # Install the new one.
637 fRc, _ = self._sudoExecuteSync([sRun,]);
638 return fRc;
639
640 def _uninstallVBoxOnLinux(self):
641 """ Uninstalls VBox on Linux."""
642
643 # Is VirtualBox installed? If not, don't try uninstall it.
644 sVBox = self._getVBoxInstallPath(fFailIfNotFound = False);
645 if sVBox is None:
646 return True;
647
648 # Find the .run file and use it.
649 sRun = self._findFile('^VirtualBox-.*\\.run$', fMandatory = False);
650 if sRun is not None:
651 utils.chmodPlusX(sRun);
652 fRc, _ = self._sudoExecuteSync([sRun, 'uninstall']);
653 return fRc;
654
655 # Try the installed uninstaller.
656 for sUninstaller in [os.path.join(sVBox, 'uninstall.sh'), '/opt/VirtualBox/uninstall.sh', ]:
657 if os.path.isfile(sUninstaller):
658 reporter.log('Invoking "%s"...' % (sUninstaller,));
659 fRc, _ = self._sudoExecuteSync([sUninstaller, 'uninstall']);
660 return fRc;
661
662 reporter.log('Did not find any VirtualBox install to uninstall.');
663 return True;
664
665
666 #
667 # Solaris
668 #
669
670 def _generateAutoResponseOnSolaris(self):
671 """
672 Generates an autoresponse file on solaris, returning the name.
673 None is return on failure.
674 """
675 sPath = os.path.join(self.sScratchPath, 'SolarisAutoResponse');
676 oFile = utils.openNoInherit(sPath, 'wt');
677 oFile.write('basedir=default\n'
678 'runlevel=nocheck\n'
679 'conflict=quit\n'
680 'setuid=nocheck\n'
681 'action=nocheck\n'
682 'partial=quit\n'
683 'instance=unique\n'
684 'idepend=quit\n'
685 'rdepend=quit\n'
686 'space=quit\n'
687 'mail=\n');
688 oFile.close();
689 return sPath;
690
691 def _installVBoxOnSolaris(self):
692 """ Installs VBox on Solaris."""
693 sPkg = self._findFile('^VirtualBox-.*\\.pkg$', fMandatory = False);
694 if sPkg is None:
695 sTar = self._findFile('^VirtualBox-.*-SunOS-.*\\.tar.gz$', fMandatory = False);
696 if sTar is not None:
697 if self._maybeUnpackArchive(sTar) is not True:
698 return False;
699 sPkg = self._findFile('^VirtualBox-.*\\.pkg$', fMandatory = True);
700 sRsp = self._findFile('^autoresponse$', fMandatory = True);
701 if sPkg is None or sRsp is None:
702 return False;
703
704 # Uninstall first (ignore result).
705 self._uninstallVBoxOnSolaris();
706
707 # Install the new one.
708 fRc, _ = self._sudoExecuteSync(['pkgadd', '-d', sPkg, '-n', '-a', sRsp, 'SUNWvbox']);
709 return fRc;
710
711 def _uninstallVBoxOnSolaris(self):
712 """ Uninstalls VBox on Solaris."""
713 reporter.flushall();
714 if utils.processCall(['pkginfo', '-q', 'SUNWvbox']) != 0:
715 return True;
716 sRsp = self._generateAutoResponseOnSolaris();
717 fRc, _ = self._sudoExecuteSync(['pkgrm', '-n', '-a', sRsp, 'SUNWvbox']);
718 return fRc;
719
720 #
721 # Windows
722 #
723
724 ## VBox windows services we can query the status of.
725 kasWindowsServices = [ 'vboxdrv', 'vboxusbmon', 'vboxnetadp', 'vboxnetflt', 'vboxnetlwf' ];
726
727 def _installVBoxOnWindows(self):
728 """ Installs VBox on Windows."""
729 sExe = self._findFile('^VirtualBox-.*-(MultiArch|Win).exe$');
730 if sExe is None:
731 return False;
732
733 # TEMPORARY HACK - START
734 # It seems that running the NDIS cleanup script upon uninstallation is not
735 # a good idea, so let's run it before installing VirtualBox.
736 #sHostName = socket.getfqdn();
737 #if not sHostName.startswith('testboxwin3') \
738 # and not sHostName.startswith('testboxharp2') \
739 # and not sHostName.startswith('wei01-b6ka-3') \
740 # and utils.getHostOsVersion() in ['8', '8.1', '9', '2008Server', '2008ServerR2', '2012Server']:
741 # reporter.log('Peforming extra NDIS cleanup...');
742 # sMagicScript = os.path.abspath(os.path.join(g_ksValidationKitDir, 'testdriver', 'win-vbox-net-uninstall.ps1'));
743 # fRc2, _ = self._sudoExecuteSync(['powershell.exe', '-Command', 'set-executionpolicy unrestricted']);
744 # if not fRc2:
745 # reporter.log('set-executionpolicy failed.');
746 # self._sudoExecuteSync(['powershell.exe', '-Command', 'get-executionpolicy']);
747 # fRc2, _ = self._sudoExecuteSync(['powershell.exe', '-File', sMagicScript]);
748 # if not fRc2:
749 # reporter.log('NDIS cleanup failed.');
750 # TEMPORARY HACK - END
751
752 # Uninstall any previous vbox version first.
753 fRc = self._uninstallVBoxOnWindows(True);
754 if fRc is not True:
755 return None; # There shouldn't be anything to uninstall, and if there is, it's not our fault.
756
757 # Install the new one.
758 asArgs = [sExe, '-vvvv', '--silent', '--logging'];
759 asArgs.extend(['--msiparams', 'REBOOT=ReallySuppress']);
760 sVBoxInstallPath = os.environ.get('VBOX_INSTALL_PATH', None);
761 if sVBoxInstallPath is not None:
762 asArgs.extend(['INSTALLDIR="%s"' % (sVBoxInstallPath,)]);
763 fRc2, iRc = self._sudoExecuteSync(asArgs);
764 if fRc2 is False:
765 if iRc == 3010: # ERROR_SUCCESS_REBOOT_REQUIRED
766 reporter.log('Note: Installer required a reboot to complete installation');
767 # Optional, don't fail.
768 else:
769 fRc = False;
770 sLogFile = os.path.join(tempfile.gettempdir(), 'VirtualBox', 'VBoxInstallLog.txt');
771 if sLogFile is not None \
772 and os.path.isfile(sLogFile):
773 reporter.addLogFile(sLogFile, 'log/installer', "Verbose MSI installation log file");
774 self._waitForTestManagerConnectivity(30);
775 return fRc;
776
777 def _isProcessPresent(self, sName):
778 """ Checks whether the named process is present or not. """
779 for oProcess in utils.processListAll():
780 sBase = oProcess.getBaseImageNameNoExeSuff();
781 if sBase is not None and sBase.lower() == sName:
782 return True;
783 return False;
784
785 def _killProcessesByName(self, sName, sDesc, fChildren = False):
786 """ Kills the named process, optionally including children. """
787 cKilled = 0;
788 aoProcesses = utils.processListAll();
789 for oProcess in aoProcesses:
790 sBase = oProcess.getBaseImageNameNoExeSuff();
791 if sBase is not None and sBase.lower() == sName:
792 reporter.log('Killing %s process: %s (%s)' % (sDesc, oProcess.iPid, sBase));
793 utils.processKill(oProcess.iPid);
794 cKilled += 1;
795
796 if fChildren:
797 for oChild in aoProcesses:
798 if oChild.iParentPid == oProcess.iPid and oChild.iParentPid is not None:
799 reporter.log('Killing %s child process: %s (%s)' % (sDesc, oChild.iPid, sBase));
800 utils.processKill(oChild.iPid);
801 cKilled += 1;
802 return cKilled;
803
804 def _terminateProcessesByNameAndArgSubstr(self, sName, sArg, sDesc):
805 """
806 Terminates the named process using taskkill.exe, if any of its args
807 contains the passed string.
808 """
809 cKilled = 0;
810 aoProcesses = utils.processListAll();
811 for oProcess in aoProcesses:
812 sBase = oProcess.getBaseImageNameNoExeSuff();
813 if sBase is not None and sBase.lower() == sName and any(sArg in s for s in oProcess.asArgs):
814
815 reporter.log('Killing %s process: %s (%s)' % (sDesc, oProcess.iPid, sBase));
816 self._executeSync(['taskkill.exe', '/pid', '%u' % (oProcess.iPid,)]);
817 cKilled += 1;
818 return cKilled;
819
820 def _uninstallVBoxOnWindows(self, fIgnoreServices = False):
821 """
822 Uninstalls VBox on Windows, all installations we find to be on the safe side...
823 """
824
825 import win32com.client; # pylint: disable=F0401
826 win32com.client.gencache.EnsureModule('{000C1092-0000-0000-C000-000000000046}', 1033, 1, 0);
827 oInstaller = win32com.client.Dispatch('WindowsInstaller.Installer',
828 resultCLSID = '{000C1090-0000-0000-C000-000000000046}')
829
830 # Search installed products for VirtualBox.
831 asProdCodes = [];
832 for sProdCode in oInstaller.Products:
833 try:
834 sProdName = oInstaller.ProductInfo(sProdCode, "ProductName");
835 except:
836 reporter.logXcpt();
837 continue;
838 #reporter.log('Info: %s=%s' % (sProdCode, sProdName));
839 if sProdName.startswith('Oracle VM VirtualBox') \
840 or sProdName.startswith('Sun VirtualBox'):
841 asProdCodes.append([sProdCode, sProdName]);
842
843 # Before we start uninstalling anything, just ruthlessly kill any cdb,
844 # msiexec, drvinst and some rundll process we might find hanging around.
845 if self._isProcessPresent('rundll32'):
846 cTimes = 0;
847 while cTimes < 3:
848 cTimes += 1;
849 cKilled = self._terminateProcessesByNameAndArgSubstr('rundll32', 'InstallSecurityPromptRunDllW',
850 'MSI driver installation');
851 if cKilled <= 0:
852 break;
853 time.sleep(10); # Give related drvinst process a chance to clean up after we killed the verification dialog.
854
855 if self._isProcessPresent('drvinst'):
856 time.sleep(15); # In the hope that it goes away.
857 cTimes = 0;
858 while cTimes < 4:
859 cTimes += 1;
860 cKilled = self._killProcessesByName('drvinst', 'MSI driver installation', True);
861 if cKilled <= 0:
862 break;
863 time.sleep(10); # Give related MSI process a chance to clean up after we killed the driver installer.
864
865 if self._isProcessPresent('msiexec'):
866 cTimes = 0;
867 while cTimes < 3:
868 reporter.log('found running msiexec process, waiting a bit...');
869 time.sleep(20) # In the hope that it goes away.
870 if not self._isProcessPresent('msiexec'):
871 break;
872 cTimes += 1;
873 ## @todo this could also be the msiexec system service, try to detect this case!
874 if cTimes >= 6:
875 cKilled = self._killProcessesByName('msiexec', 'MSI driver installation');
876 if cKilled > 0:
877 time.sleep(16); # fudge.
878
879 # cdb.exe sometimes stays running (from utils.getProcessInfo), blocking
880 # the scratch directory. No idea why.
881 if self._isProcessPresent('cdb'):
882 cTimes = 0;
883 while cTimes < 3:
884 cKilled = self._killProcessesByName('cdb', 'cdb.exe from getProcessInfo');
885 if cKilled <= 0:
886 break;
887 time.sleep(2); # fudge.
888
889 # Do the uninstalling.
890 fRc = True;
891 sLogFile = os.path.join(self.sScratchPath, 'VBoxUninstallLog.txt');
892 for sProdCode, sProdName in asProdCodes:
893 reporter.log('Uninstalling %s (%s)...' % (sProdName, sProdCode));
894 fRc2, iRc = self._sudoExecuteSync(['msiexec', '/uninstall', sProdCode, '/quiet', '/passive', '/norestart',
895 '/L*v', '%s' % (sLogFile), ]);
896 if fRc2 is False:
897 if iRc == 3010: # ERROR_SUCCESS_REBOOT_REQUIRED
898 reporter.log('Note: Uninstaller required a reboot to complete uninstallation');
899 # Optional, don't fail.
900 else:
901 fRc = False;
902 reporter.addLogFile(sLogFile, 'log/uninstaller', "Verbose MSI uninstallation log file");
903
904 self._waitForTestManagerConnectivity(30);
905 if fRc is False and os.path.isfile(sLogFile):
906 reporter.addLogFile(sLogFile, 'log/uninstaller');
907
908 # Log driver service states (should ls \Driver\VBox* and \Device\VBox*).
909 for sService in self.kasWindowsServices:
910 fRc2, _ = self._sudoExecuteSync(['sc.exe', 'query', sService]);
911 if fIgnoreServices is False and fRc2 is True:
912 fRc = False
913
914 return fRc;
915
916
917 #
918 # Extension pack.
919 #
920
921 def _getVBoxInstallPath(self, fFailIfNotFound):
922 """ Returns the default VBox installation path. """
923 sHost = utils.getHostOs();
924 if sHost == 'win':
925 sProgFiles = os.environ.get('ProgramFiles', 'C:\\Program Files');
926 asLocs = [
927 os.path.join(sProgFiles, 'Oracle', 'VirtualBox'),
928 os.path.join(sProgFiles, 'OracleVM', 'VirtualBox'),
929 os.path.join(sProgFiles, 'Sun', 'VirtualBox'),
930 ];
931 elif sHost == 'linux' or sHost == 'solaris':
932 asLocs = [ '/opt/VirtualBox', '/opt/VirtualBox-3.2', '/opt/VirtualBox-3.1', '/opt/VirtualBox-3.0'];
933 elif sHost == 'darwin':
934 asLocs = [ '/Applications/VirtualBox.app/Contents/MacOS' ];
935 else:
936 asLocs = [ '/opt/VirtualBox' ];
937 if 'VBOX_INSTALL_PATH' in os.environ:
938 asLocs.insert(0, os.environ.get('VBOX_INSTALL_PATH', None));
939
940 for sLoc in asLocs:
941 if os.path.isdir(sLoc):
942 return sLoc;
943 if fFailIfNotFound:
944 reporter.error('Failed to locate VirtualBox installation: %s' % (asLocs,));
945 else:
946 reporter.log2('Failed to locate VirtualBox installation: %s' % (asLocs,));
947 return None;
948
949 def _installExtPack(self):
950 """ Installs the extension pack. """
951 sVBox = self._getVBoxInstallPath(fFailIfNotFound = True);
952 if sVBox is None:
953 return False;
954 sExtPackDir = os.path.join(sVBox, 'ExtensionPacks');
955
956 if self._uninstallAllExtPacks() is not True:
957 return False;
958
959 sExtPack = self._findFile('Oracle_VM_VirtualBox_Extension_Pack.vbox-extpack');
960 if sExtPack is None:
961 sExtPack = self._findFile('Oracle_VM_VirtualBox_Extension_Pack.*.vbox-extpack');
962 if sExtPack is None:
963 return True;
964
965 sDstDir = os.path.join(sExtPackDir, 'Oracle_VM_VirtualBox_Extension_Pack');
966 reporter.log('Installing extension pack "%s" to "%s"...' % (sExtPack, sExtPackDir));
967 fRc, _ = self._sudoExecuteSync([ self.getBinTool('vts_tar'),
968 '--extract',
969 '--verbose',
970 '--gzip',
971 '--file', sExtPack,
972 '--directory', sDstDir,
973 '--file-mode-and-mask', '0644',
974 '--file-mode-or-mask', '0644',
975 '--dir-mode-and-mask', '0755',
976 '--dir-mode-or-mask', '0755',
977 '--owner', '0',
978 '--group', '0',
979 ]);
980 return fRc;
981
982 def _uninstallAllExtPacks(self):
983 """ Uninstalls all extension packs. """
984 sVBox = self._getVBoxInstallPath(fFailIfNotFound = False);
985 if sVBox is None:
986 return True;
987
988 sExtPackDir = os.path.join(sVBox, 'ExtensionPacks');
989 if not os.path.exists(sExtPackDir):
990 return True;
991
992 fRc, _ = self._sudoExecuteSync([self.getBinTool('vts_rm'), '-Rfv', '--', sExtPackDir]);
993 return fRc;
994
995
996
997if __name__ == '__main__':
998 sys.exit(VBoxInstallerTestDriver().main(sys.argv));
999
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