VirtualBox

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

Last change on this file since 77922 was 76923, checked in by vboxsync, 6 years ago

Build fix.

  • Property svn:eol-style set to LF
  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
File size: 38.8 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: 76923 $"
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(True);
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, 0o755);
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(False);
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, fRestartSvcConfigD):
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
719 #
720 # Restart the svc.configd as it has a tendency to clog up with time and
721 # become unresponsive. It will handle SIGHUP by exiting the sigwait()
722 # look in the main function and shut down the service nicely (backend_fini).
723 # The restarter will then start a new instance of it.
724 #
725 if fRestartSvcConfigD:
726 time.sleep(1); # Give it a chance to flush pkgrm stuff.
727 self._sudoExecuteSync(['pkill', '-HUP', 'svc.configd']);
728 time.sleep(5); # Spare a few cpu cycles it to shutdown and restart.
729
730 return fRc;
731
732 #
733 # Windows
734 #
735
736 ## VBox windows services we can query the status of.
737 kasWindowsServices = [ 'vboxdrv', 'vboxusbmon', 'vboxnetadp', 'vboxnetflt', 'vboxnetlwf' ];
738
739 def _installVBoxOnWindows(self):
740 """ Installs VBox on Windows."""
741 sExe = self._findFile('^VirtualBox-.*-(MultiArch|Win).exe$');
742 if sExe is None:
743 return False;
744
745 # TEMPORARY HACK - START
746 # It seems that running the NDIS cleanup script upon uninstallation is not
747 # a good idea, so let's run it before installing VirtualBox.
748 #sHostName = socket.getfqdn();
749 #if not sHostName.startswith('testboxwin3') \
750 # and not sHostName.startswith('testboxharp2') \
751 # and not sHostName.startswith('wei01-b6ka-3') \
752 # and utils.getHostOsVersion() in ['8', '8.1', '9', '2008Server', '2008ServerR2', '2012Server']:
753 # reporter.log('Peforming extra NDIS cleanup...');
754 # sMagicScript = os.path.abspath(os.path.join(g_ksValidationKitDir, 'testdriver', 'win-vbox-net-uninstall.ps1'));
755 # fRc2, _ = self._sudoExecuteSync(['powershell.exe', '-Command', 'set-executionpolicy unrestricted']);
756 # if not fRc2:
757 # reporter.log('set-executionpolicy failed.');
758 # self._sudoExecuteSync(['powershell.exe', '-Command', 'get-executionpolicy']);
759 # fRc2, _ = self._sudoExecuteSync(['powershell.exe', '-File', sMagicScript]);
760 # if not fRc2:
761 # reporter.log('NDIS cleanup failed.');
762 # TEMPORARY HACK - END
763
764 # Uninstall any previous vbox version first.
765 fRc = self._uninstallVBoxOnWindows(True);
766 if fRc is not True:
767 return None; # There shouldn't be anything to uninstall, and if there is, it's not our fault.
768
769 # Install the new one.
770 asArgs = [sExe, '-vvvv', '--silent', '--logging'];
771 asArgs.extend(['--msiparams', 'REBOOT=ReallySuppress']);
772 sVBoxInstallPath = os.environ.get('VBOX_INSTALL_PATH', None);
773 if sVBoxInstallPath is not None:
774 asArgs.extend(['INSTALLDIR="%s"' % (sVBoxInstallPath,)]);
775 fRc2, iRc = self._sudoExecuteSync(asArgs);
776 if fRc2 is False:
777 if iRc == 3010: # ERROR_SUCCESS_REBOOT_REQUIRED
778 reporter.log('Note: Installer required a reboot to complete installation');
779 # Optional, don't fail.
780 else:
781 fRc = False;
782 sLogFile = os.path.join(tempfile.gettempdir(), 'VirtualBox', 'VBoxInstallLog.txt');
783 if sLogFile is not None \
784 and os.path.isfile(sLogFile):
785 reporter.addLogFile(sLogFile, 'log/installer', "Verbose MSI installation log file");
786 self._waitForTestManagerConnectivity(30);
787 return fRc;
788
789 def _isProcessPresent(self, sName):
790 """ Checks whether the named process is present or not. """
791 for oProcess in utils.processListAll():
792 sBase = oProcess.getBaseImageNameNoExeSuff();
793 if sBase is not None and sBase.lower() == sName:
794 return True;
795 return False;
796
797 def _killProcessesByName(self, sName, sDesc, fChildren = False):
798 """ Kills the named process, optionally including children. """
799 cKilled = 0;
800 aoProcesses = utils.processListAll();
801 for oProcess in aoProcesses:
802 sBase = oProcess.getBaseImageNameNoExeSuff();
803 if sBase is not None and sBase.lower() == sName:
804 reporter.log('Killing %s process: %s (%s)' % (sDesc, oProcess.iPid, sBase));
805 utils.processKill(oProcess.iPid);
806 cKilled += 1;
807
808 if fChildren:
809 for oChild in aoProcesses:
810 if oChild.iParentPid == oProcess.iPid and oChild.iParentPid is not None:
811 reporter.log('Killing %s child process: %s (%s)' % (sDesc, oChild.iPid, sBase));
812 utils.processKill(oChild.iPid);
813 cKilled += 1;
814 return cKilled;
815
816 def _terminateProcessesByNameAndArgSubstr(self, sName, sArg, sDesc):
817 """
818 Terminates the named process using taskkill.exe, if any of its args
819 contains the passed string.
820 """
821 cKilled = 0;
822 aoProcesses = utils.processListAll();
823 for oProcess in aoProcesses:
824 sBase = oProcess.getBaseImageNameNoExeSuff();
825 if sBase is not None and sBase.lower() == sName and any(sArg in s for s in oProcess.asArgs):
826
827 reporter.log('Killing %s process: %s (%s)' % (sDesc, oProcess.iPid, sBase));
828 self._executeSync(['taskkill.exe', '/pid', '%u' % (oProcess.iPid,)]);
829 cKilled += 1;
830 return cKilled;
831
832 def _uninstallVBoxOnWindows(self, fIgnoreServices = False):
833 """
834 Uninstalls VBox on Windows, all installations we find to be on the safe side...
835 """
836
837 import win32com.client; # pylint: disable=F0401
838 win32com.client.gencache.EnsureModule('{000C1092-0000-0000-C000-000000000046}', 1033, 1, 0);
839 oInstaller = win32com.client.Dispatch('WindowsInstaller.Installer',
840 resultCLSID = '{000C1090-0000-0000-C000-000000000046}')
841
842 # Search installed products for VirtualBox.
843 asProdCodes = [];
844 for sProdCode in oInstaller.Products:
845 try:
846 sProdName = oInstaller.ProductInfo(sProdCode, "ProductName");
847 except:
848 reporter.logXcpt();
849 continue;
850 #reporter.log('Info: %s=%s' % (sProdCode, sProdName));
851 if sProdName.startswith('Oracle VM VirtualBox') \
852 or sProdName.startswith('Sun VirtualBox'):
853 asProdCodes.append([sProdCode, sProdName]);
854
855 # Before we start uninstalling anything, just ruthlessly kill any cdb,
856 # msiexec, drvinst and some rundll process we might find hanging around.
857 if self._isProcessPresent('rundll32'):
858 cTimes = 0;
859 while cTimes < 3:
860 cTimes += 1;
861 cKilled = self._terminateProcessesByNameAndArgSubstr('rundll32', 'InstallSecurityPromptRunDllW',
862 'MSI driver installation');
863 if cKilled <= 0:
864 break;
865 time.sleep(10); # Give related drvinst process a chance to clean up after we killed the verification dialog.
866
867 if self._isProcessPresent('drvinst'):
868 time.sleep(15); # In the hope that it goes away.
869 cTimes = 0;
870 while cTimes < 4:
871 cTimes += 1;
872 cKilled = self._killProcessesByName('drvinst', 'MSI driver installation', True);
873 if cKilled <= 0:
874 break;
875 time.sleep(10); # Give related MSI process a chance to clean up after we killed the driver installer.
876
877 if self._isProcessPresent('msiexec'):
878 cTimes = 0;
879 while cTimes < 3:
880 reporter.log('found running msiexec process, waiting a bit...');
881 time.sleep(20) # In the hope that it goes away.
882 if not self._isProcessPresent('msiexec'):
883 break;
884 cTimes += 1;
885 ## @todo this could also be the msiexec system service, try to detect this case!
886 if cTimes >= 6:
887 cKilled = self._killProcessesByName('msiexec', 'MSI driver installation');
888 if cKilled > 0:
889 time.sleep(16); # fudge.
890
891 # cdb.exe sometimes stays running (from utils.getProcessInfo), blocking
892 # the scratch directory. No idea why.
893 if self._isProcessPresent('cdb'):
894 cTimes = 0;
895 while cTimes < 3:
896 cKilled = self._killProcessesByName('cdb', 'cdb.exe from getProcessInfo');
897 if cKilled <= 0:
898 break;
899 time.sleep(2); # fudge.
900
901 # Do the uninstalling.
902 fRc = True;
903 sLogFile = os.path.join(self.sScratchPath, 'VBoxUninstallLog.txt');
904 for sProdCode, sProdName in asProdCodes:
905 reporter.log('Uninstalling %s (%s)...' % (sProdName, sProdCode));
906 fRc2, iRc = self._sudoExecuteSync(['msiexec', '/uninstall', sProdCode, '/quiet', '/passive', '/norestart',
907 '/L*v', '%s' % (sLogFile), ]);
908 if fRc2 is False:
909 if iRc == 3010: # ERROR_SUCCESS_REBOOT_REQUIRED
910 reporter.log('Note: Uninstaller required a reboot to complete uninstallation');
911 reporter.addLogFile(sLogFile, 'log/uninstaller_reboot', \
912 "Verbose MSI uninstallation log file (reboot required)");
913 # Optional, don't fail.
914 else:
915 fRc = False;
916
917 self._waitForTestManagerConnectivity(30);
918 if fRc is False and os.path.isfile(sLogFile):
919 reporter.addLogFile(sLogFile, 'log/uninstaller', "Verbose MSI uninstallation log file");
920
921 # Log driver service states (should ls \Driver\VBox* and \Device\VBox*).
922 for sService in self.kasWindowsServices:
923 fRc2, _ = self._sudoExecuteSync(['sc.exe', 'query', sService]);
924 if fIgnoreServices is False and fRc2 is True:
925 fRc = False
926
927 return fRc;
928
929
930 #
931 # Extension pack.
932 #
933
934 def _getVBoxInstallPath(self, fFailIfNotFound):
935 """ Returns the default VBox installation path. """
936 sHost = utils.getHostOs();
937 if sHost == 'win':
938 sProgFiles = os.environ.get('ProgramFiles', 'C:\\Program Files');
939 asLocs = [
940 os.path.join(sProgFiles, 'Oracle', 'VirtualBox'),
941 os.path.join(sProgFiles, 'OracleVM', 'VirtualBox'),
942 os.path.join(sProgFiles, 'Sun', 'VirtualBox'),
943 ];
944 elif sHost == 'linux' or sHost == 'solaris':
945 asLocs = [ '/opt/VirtualBox', '/opt/VirtualBox-3.2', '/opt/VirtualBox-3.1', '/opt/VirtualBox-3.0'];
946 elif sHost == 'darwin':
947 asLocs = [ '/Applications/VirtualBox.app/Contents/MacOS' ];
948 else:
949 asLocs = [ '/opt/VirtualBox' ];
950 if 'VBOX_INSTALL_PATH' in os.environ:
951 asLocs.insert(0, os.environ.get('VBOX_INSTALL_PATH', None));
952
953 for sLoc in asLocs:
954 if os.path.isdir(sLoc):
955 return sLoc;
956 if fFailIfNotFound:
957 reporter.error('Failed to locate VirtualBox installation: %s' % (asLocs,));
958 else:
959 reporter.log2('Failed to locate VirtualBox installation: %s' % (asLocs,));
960 return None;
961
962 def _installExtPack(self):
963 """ Installs the extension pack. """
964 sVBox = self._getVBoxInstallPath(fFailIfNotFound = True);
965 if sVBox is None:
966 return False;
967 sExtPackDir = os.path.join(sVBox, 'ExtensionPacks');
968
969 if self._uninstallAllExtPacks() is not True:
970 return False;
971
972 sExtPack = self._findFile('Oracle_VM_VirtualBox_Extension_Pack.vbox-extpack');
973 if sExtPack is None:
974 sExtPack = self._findFile('Oracle_VM_VirtualBox_Extension_Pack.*.vbox-extpack');
975 if sExtPack is None:
976 return True;
977
978 sDstDir = os.path.join(sExtPackDir, 'Oracle_VM_VirtualBox_Extension_Pack');
979 reporter.log('Installing extension pack "%s" to "%s"...' % (sExtPack, sExtPackDir));
980 fRc, _ = self._sudoExecuteSync([ self.getBinTool('vts_tar'),
981 '--extract',
982 '--verbose',
983 '--gzip',
984 '--file', sExtPack,
985 '--directory', sDstDir,
986 '--file-mode-and-mask', '0644',
987 '--file-mode-or-mask', '0644',
988 '--dir-mode-and-mask', '0755',
989 '--dir-mode-or-mask', '0755',
990 '--owner', '0',
991 '--group', '0',
992 ]);
993 return fRc;
994
995 def _uninstallAllExtPacks(self):
996 """ Uninstalls all extension packs. """
997 sVBox = self._getVBoxInstallPath(fFailIfNotFound = False);
998 if sVBox is None:
999 return True;
1000
1001 sExtPackDir = os.path.join(sVBox, 'ExtensionPacks');
1002 if not os.path.exists(sExtPackDir):
1003 return True;
1004
1005 fRc, _ = self._sudoExecuteSync([self.getBinTool('vts_rm'), '-Rfv', '--', sExtPackDir]);
1006 return fRc;
1007
1008
1009
1010if __name__ == '__main__':
1011 sys.exit(VBoxInstallerTestDriver().main(sys.argv));
1012
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