VirtualBox

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

Last change on this file since 103081 was 103076, checked in by vboxsync, 13 months ago

Validation Kit: Added the new path layout to sys.path for finding the vboxapi module, log the Python version if the module could not be found. bugref:10579

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 218.0 KB
Line 
1# -*- coding: utf-8 -*-
2# $Id: vbox.py 103076 2024-01-26 08:09:34Z vboxsync $
3# pylint: disable=too-many-lines
4
5"""
6VirtualBox Specific base testdriver.
7"""
8
9__copyright__ = \
10"""
11Copyright (C) 2010-2023 Oracle and/or its affiliates.
12
13This file is part of VirtualBox base platform packages, as
14available from https://www.virtualbox.org.
15
16This program is free software; you can redistribute it and/or
17modify it under the terms of the GNU General Public License
18as published by the Free Software Foundation, in version 3 of the
19License.
20
21This program is distributed in the hope that it will be useful, but
22WITHOUT ANY WARRANTY; without even the implied warranty of
23MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
24General Public License for more details.
25
26You should have received a copy of the GNU General Public License
27along with this program; if not, see <https://www.gnu.org/licenses>.
28
29The contents of this file may alternatively be used under the terms
30of the Common Development and Distribution License Version 1.0
31(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
32in the VirtualBox distribution, in which case the provisions of the
33CDDL are applicable instead of those of the GPL.
34
35You may elect to license modified versions of this file under the
36terms and conditions of either the GPL or the CDDL or both.
37
38SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
39"""
40__version__ = "$Revision: 103076 $"
41
42# pylint: disable=unnecessary-semicolon
43
44# Standard Python imports.
45import datetime
46import os
47import platform
48import re;
49import sys
50import threading
51import time
52import traceback
53
54# Figure out where the validation kit lives and make sure it's in the path.
55try: __file__ # pylint: disable=used-before-assignment
56except: __file__ = sys.argv[0];
57g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)));
58if g_ksValidationKitDir not in sys.path:
59 sys.path.append(g_ksValidationKitDir);
60
61# Validation Kit imports.
62from common import utils;
63from testdriver import base;
64from testdriver import btresolver;
65from testdriver import reporter;
66from testdriver import vboxcon;
67from testdriver import vboxtestvms;
68
69# Python 3 hacks:
70if sys.version_info[0] >= 3:
71 xrange = range; # pylint: disable=redefined-builtin,invalid-name
72 long = int; # pylint: disable=redefined-builtin,invalid-name
73
74#
75# Exception and Error Unification Hacks.
76# Note! This is pretty gross stuff. Be warned!
77# TODO: Find better ways of doing these things, preferrably in vboxapi.
78#
79
80ComException = None; # pylint: disable=invalid-name
81__fnComExceptionGetAttr__ = None; # pylint: disable=invalid-name
82
83def __MyDefaultGetAttr(oSelf, sName):
84 """ __getattribute__/__getattr__ default fake."""
85 try:
86 oAttr = oSelf.__dict__[sName];
87 except:
88 oAttr = dir(oSelf)[sName];
89 return oAttr;
90
91def __MyComExceptionGetAttr(oSelf, sName):
92 """ ComException.__getattr__ wrapper - both XPCOM and COM. """
93 try:
94 oAttr = __fnComExceptionGetAttr__(oSelf, sName);
95 except AttributeError:
96 if platform.system() == 'Windows':
97 if sName == 'errno':
98 oAttr = __fnComExceptionGetAttr__(oSelf, 'hresult');
99 elif sName == 'msg':
100 oAttr = __fnComExceptionGetAttr__(oSelf, 'strerror');
101 else:
102 raise;
103 else:
104 if sName == 'hresult':
105 oAttr = __fnComExceptionGetAttr__(oSelf, 'errno');
106 elif sName == 'strerror':
107 oAttr = __fnComExceptionGetAttr__(oSelf, 'msg');
108 elif sName == 'excepinfo':
109 oAttr = None;
110 elif sName == 'argerror':
111 oAttr = None;
112 else:
113 raise;
114 #print '__MyComExceptionGetAttr(,%s) -> "%s"' % (sName, oAttr);
115 return oAttr;
116
117def __deployExceptionHacks__(oNativeComExceptionClass):
118 """
119 Deploys the exception and error hacks that helps unifying COM and XPCOM
120 exceptions and errors.
121 """
122 global ComException # pylint: disable=invalid-name
123 global __fnComExceptionGetAttr__ # pylint: disable=invalid-name
124
125 # Hook up our attribute getter for the exception class (ASSUMES new-style).
126 if __fnComExceptionGetAttr__ is None:
127 try:
128 __fnComExceptionGetAttr__ = getattr(oNativeComExceptionClass, '__getattr__');
129 except:
130 try:
131 __fnComExceptionGetAttr__ = getattr(oNativeComExceptionClass, '__getattribute__');
132 except:
133 __fnComExceptionGetAttr__ = __MyDefaultGetAttr;
134 setattr(oNativeComExceptionClass, '__getattr__', __MyComExceptionGetAttr)
135
136 # Make the modified classes accessible (are there better ways to do this?)
137 ComException = oNativeComExceptionClass
138 return None;
139
140
141
142#
143# Utility functions.
144#
145
146def isIpAddrValid(sIpAddr):
147 """
148 Checks if a IPv4 address looks valid. This will return false for
149 localhost and similar.
150 Returns True / False.
151 """
152 if sIpAddr is None: return False;
153 if len(sIpAddr.split('.')) != 4: return False;
154 if sIpAddr.endswith('.0'): return False;
155 if sIpAddr.endswith('.255'): return False;
156 if sIpAddr.startswith('127.'): return False;
157 if sIpAddr.startswith('169.254.'): return False;
158 if sIpAddr.startswith('192.0.2.'): return False;
159 if sIpAddr.startswith('224.0.0.'): return False;
160 return True;
161
162def stringifyErrorInfo(oErrInfo):
163 """
164 Stringifies the error information in a IVirtualBoxErrorInfo object.
165
166 Returns string with error info.
167 """
168 try:
169 rc = oErrInfo.resultCode;
170 sText = oErrInfo.text;
171 sIid = oErrInfo.interfaceID;
172 sComponent = oErrInfo.component;
173 except:
174 sRet = 'bad error object (%s)?' % (oErrInfo,);
175 traceback.print_exc();
176 else:
177 sRet = 'rc=%s text="%s" IID=%s component=%s' % (ComError.toString(rc), sText, sIid, sComponent);
178 return sRet;
179
180def reportError(oErr, sText):
181 """
182 Report a VirtualBox error on oErr. oErr can be IVirtualBoxErrorInfo
183 or IProgress. Anything else is ignored.
184
185 Returns the same a reporter.error().
186 """
187 try:
188 oErrObj = oErr.errorInfo; # IProgress.
189 except:
190 oErrObj = oErr;
191 reporter.error(sText);
192 return reporter.error(stringifyErrorInfo(oErrObj));
193
194def formatComOrXpComException(oType, oXcpt):
195 """
196 Callback installed with the reporter to better format COM exceptions.
197 Similar to format_exception_only, only it returns None if not interested.
198 """
199 _ = oType;
200 oVBoxMgr = vboxcon.goHackModuleClass.oVBoxMgr;
201 if oVBoxMgr is None:
202 return None;
203 if not oVBoxMgr.xcptIsOurXcptKind(oXcpt): # pylint: disable=not-callable
204 return None;
205
206 if platform.system() == 'Windows':
207 hrc = oXcpt.hresult;
208 if hrc == ComError.DISP_E_EXCEPTION and oXcpt.excepinfo is not None and len(oXcpt.excepinfo) > 5:
209 hrc = oXcpt.excepinfo[5];
210 sWhere = oXcpt.excepinfo[1];
211 sMsg = oXcpt.excepinfo[2];
212 else:
213 sWhere = None;
214 sMsg = oXcpt.strerror;
215 else:
216 hrc = oXcpt.errno;
217 sWhere = None;
218 sMsg = oXcpt.msg;
219
220 sHrc = oVBoxMgr.xcptToString(hrc); # pylint: disable=not-callable
221 if sHrc.find('(') < 0:
222 sHrc = '%s (%#x)' % (sHrc, hrc & 0xffffffff,);
223
224 asRet = ['COM-Xcpt: %s' % (sHrc,)];
225 if sMsg and sWhere:
226 asRet.append('--------- %s: %s' % (sWhere, sMsg,));
227 elif sMsg:
228 asRet.append('--------- %s' % (sMsg,));
229 return asRet;
230 #if sMsg and sWhere:
231 # return ['COM-Xcpt: %s - %s: %s' % (sHrc, sWhere, sMsg,)];
232 #if sMsg:
233 # return ['COM-Xcpt: %s - %s' % (sHrc, sMsg,)];
234 #return ['COM-Xcpt: %s' % (sHrc,)];
235
236#
237# Classes
238#
239
240class ComError(object):
241 """
242 Unified COM and XPCOM status code repository.
243 This works more like a module than a class since it's replacing a module.
244 """
245
246 # The VBOX_E_XXX bits:
247 __VBOX_E_BASE = -2135228416;
248 VBOX_E_OBJECT_NOT_FOUND = __VBOX_E_BASE + 1;
249 VBOX_E_INVALID_VM_STATE = __VBOX_E_BASE + 2;
250 VBOX_E_VM_ERROR = __VBOX_E_BASE + 3;
251 VBOX_E_FILE_ERROR = __VBOX_E_BASE + 4;
252 VBOX_E_IPRT_ERROR = __VBOX_E_BASE + 5;
253 VBOX_E_PDM_ERROR = __VBOX_E_BASE + 6;
254 VBOX_E_INVALID_OBJECT_STATE = __VBOX_E_BASE + 7;
255 VBOX_E_HOST_ERROR = __VBOX_E_BASE + 8;
256 VBOX_E_NOT_SUPPORTED = __VBOX_E_BASE + 9;
257 VBOX_E_XML_ERROR = __VBOX_E_BASE + 10;
258 VBOX_E_INVALID_SESSION_STATE = __VBOX_E_BASE + 11;
259 VBOX_E_OBJECT_IN_USE = __VBOX_E_BASE + 12;
260 VBOX_E_DONT_CALL_AGAIN = __VBOX_E_BASE + 13;
261
262 # Reverse lookup table.
263 dDecimalToConst = {}; # pylint: disable=invalid-name
264
265 def __init__(self):
266 raise base.GenError('No instances, please');
267
268 @staticmethod
269 def copyErrors(oNativeComErrorClass):
270 """
271 Copy all error codes from oNativeComErrorClass to this class and
272 install compatability mappings.
273 """
274
275 # First, add the VBOX_E_XXX constants to dDecimalToConst.
276 for sAttr in dir(ComError):
277 if sAttr.startswith('VBOX_E'):
278 oAttr = getattr(ComError, sAttr);
279 ComError.dDecimalToConst[oAttr] = sAttr;
280
281 # Copy all error codes from oNativeComErrorClass to this class.
282 for sAttr in dir(oNativeComErrorClass):
283 if sAttr[0].isupper():
284 oAttr = getattr(oNativeComErrorClass, sAttr);
285 setattr(ComError, sAttr, oAttr);
286 if isinstance(oAttr, int):
287 ComError.dDecimalToConst[oAttr] = sAttr;
288
289 # Install mappings to the other platform.
290 if platform.system() == 'Windows':
291 ComError.NS_OK = ComError.S_OK;
292 ComError.NS_ERROR_FAILURE = ComError.E_FAIL;
293 ComError.NS_ERROR_ABORT = ComError.E_ABORT;
294 ComError.NS_ERROR_NULL_POINTER = ComError.E_POINTER;
295 ComError.NS_ERROR_NO_INTERFACE = ComError.E_NOINTERFACE;
296 ComError.NS_ERROR_INVALID_ARG = ComError.E_INVALIDARG;
297 ComError.NS_ERROR_OUT_OF_MEMORY = ComError.E_OUTOFMEMORY;
298 ComError.NS_ERROR_NOT_IMPLEMENTED = ComError.E_NOTIMPL;
299 ComError.NS_ERROR_UNEXPECTED = ComError.E_UNEXPECTED;
300 else:
301 ComError.E_ACCESSDENIED = -2147024891; # see VBox/com/defs.h
302 ComError.S_OK = ComError.NS_OK;
303 ComError.E_FAIL = ComError.NS_ERROR_FAILURE;
304 ComError.E_ABORT = ComError.NS_ERROR_ABORT;
305 ComError.E_POINTER = ComError.NS_ERROR_NULL_POINTER;
306 ComError.E_NOINTERFACE = ComError.NS_ERROR_NO_INTERFACE;
307 ComError.E_INVALIDARG = ComError.NS_ERROR_INVALID_ARG;
308 ComError.E_OUTOFMEMORY = ComError.NS_ERROR_OUT_OF_MEMORY;
309 ComError.E_NOTIMPL = ComError.NS_ERROR_NOT_IMPLEMENTED;
310 ComError.E_UNEXPECTED = ComError.NS_ERROR_UNEXPECTED;
311 ComError.DISP_E_EXCEPTION = -2147352567; # For COM compatability only.
312 return True;
313
314 @staticmethod
315 def getXcptResult(oXcpt):
316 """
317 Gets the result code for an exception.
318 Returns COM status code (or E_UNEXPECTED).
319 """
320 if platform.system() == 'Windows':
321 # The DISP_E_EXCEPTION + excptinfo fun needs checking up, only
322 # empirical info on it so far.
323 try:
324 hrXcpt = oXcpt.hresult;
325 except AttributeError:
326 hrXcpt = ComError.E_UNEXPECTED;
327 if hrXcpt == ComError.DISP_E_EXCEPTION and oXcpt.excepinfo is not None:
328 hrXcpt = oXcpt.excepinfo[5];
329 else:
330 try:
331 hrXcpt = oXcpt.errno;
332 except AttributeError:
333 hrXcpt = ComError.E_UNEXPECTED;
334 return hrXcpt;
335
336 @staticmethod
337 def equal(oXcpt, hr):
338 """
339 Checks if the ComException e is not equal to the COM status code hr.
340 This takes DISP_E_EXCEPTION & excepinfo into account.
341
342 This method can be used with any Exception derivate, however it will
343 only return True for classes similar to the two ComException variants.
344 """
345 if platform.system() == 'Windows':
346 # The DISP_E_EXCEPTION + excptinfo fun needs checking up, only
347 # empirical info on it so far.
348 try:
349 hrXcpt = oXcpt.hresult;
350 except AttributeError:
351 return False;
352 if hrXcpt == ComError.DISP_E_EXCEPTION and oXcpt.excepinfo is not None:
353 hrXcpt = oXcpt.excepinfo[5];
354 else:
355 try:
356 hrXcpt = oXcpt.errno;
357 except AttributeError:
358 return False;
359 return hrXcpt == hr;
360
361 @staticmethod
362 def notEqual(oXcpt, hr):
363 """
364 Checks if the ComException e is not equal to the COM status code hr.
365 See equal() for more details.
366 """
367 return not ComError.equal(oXcpt, hr)
368
369 @staticmethod
370 def toString(hr):
371 """
372 Converts the specified COM status code to a string.
373 """
374 try:
375 sStr = ComError.dDecimalToConst[int(hr)];
376 except KeyError:
377 hrLong = long(hr);
378 sStr = '%#x (%d)' % (hrLong, hrLong);
379 return sStr;
380
381
382class Build(object): # pylint: disable=too-few-public-methods
383 """
384 A VirtualBox build.
385
386 Note! After dropping the installation of VBox from this code and instead
387 realizing that with the vboxinstall.py wrapper driver, this class is
388 of much less importance and contains unnecessary bits and pieces.
389 """
390
391 def __init__(self, oDriver, strInstallPath):
392 """
393 Construct a build object from a build file name and/or install path.
394 """
395 # Initialize all members first.
396 self.oDriver = oDriver;
397 self.sInstallPath = strInstallPath;
398 self.sSdkPath = None;
399 self.sSrcRoot = None;
400 self.sKind = None;
401 self.sDesignation = None;
402 self.sType = None;
403 self.sOs = None;
404 self.sArch = None;
405 self.sGuestAdditionsIso = None;
406
407 # Figure out the values as best we can.
408 if strInstallPath is None:
409 #
410 # Both parameters are None, which means we're falling back on a
411 # build in the development tree.
412 #
413 self.sKind = "development";
414
415 if self.sType is None:
416 self.sType = os.environ.get("KBUILD_TYPE", "release");
417 if self.sOs is None:
418 self.sOs = os.environ.get("KBUILD_TARGET", oDriver.sHost);
419 if self.sArch is None:
420 self.sArch = os.environ.get("KBUILD_TARGET_ARCH", oDriver.sHostArch);
421
422 sOut = os.path.join('out', self.sOs + '.' + self.sArch, self.sType);
423 sSearch = os.environ.get('VBOX_TD_DEV_TREE', os.path.dirname(__file__)); # Env.var. for older trees or testboxscript.
424 sCandidat = None;
425 for i in range(0, 10): # pylint: disable=unused-variable
426 sBldDir = os.path.join(sSearch, sOut);
427 if os.path.isdir(sBldDir):
428 sCandidat = os.path.join(sBldDir, 'bin', 'VBoxSVC' + base.exeSuff());
429 if os.path.isfile(sCandidat):
430 self.sSdkPath = os.path.join(sBldDir, 'bin/sdk');
431 break;
432 sCandidat = os.path.join(sBldDir, 'dist/VirtualBox.app/Contents/MacOS/VBoxSVC');
433 if os.path.isfile(sCandidat):
434 self.sSdkPath = os.path.join(sBldDir, 'dist/sdk');
435 break;
436 sSearch = os.path.abspath(os.path.join(sSearch, '..'));
437 if sCandidat is None or not os.path.isfile(sCandidat):
438 raise base.GenError();
439 self.sInstallPath = os.path.abspath(os.path.dirname(sCandidat));
440 self.sSrcRoot = os.path.abspath(sSearch);
441
442 self.sDesignation = os.environ.get('TEST_BUILD_DESIGNATION', None);
443 if self.sDesignation is None:
444 try:
445 oFile = utils.openNoInherit(os.path.join(self.sSrcRoot, sOut, 'revision.kmk'), 'r');
446 except:
447 pass;
448 else:
449 s = oFile.readline();
450 oFile.close();
451 oMatch = re.search("VBOX_SVN_REV=(\\d+)", s);
452 if oMatch is not None:
453 self.sDesignation = oMatch.group(1);
454
455 if self.sDesignation is None:
456 self.sDesignation = 'XXXXX'
457 else:
458 #
459 # We've been pointed to an existing installation, this could be
460 # in the out dir of a svn checkout, untarred VBoxAll or a real
461 # installation directory.
462 #
463 self.sKind = "preinstalled";
464 self.sType = "release";
465 self.sOs = oDriver.sHost;
466 self.sArch = oDriver.sHostArch;
467 self.sInstallPath = os.path.abspath(strInstallPath);
468 self.sSdkPath = os.path.join(self.sInstallPath, 'sdk');
469 self.sSrcRoot = None;
470 self.sDesignation = os.environ.get('TEST_BUILD_DESIGNATION', 'XXXXX');
471 ## @todo Much more work is required here.
472
473 # Try Determine the build type.
474 sVBoxManage = os.path.join(self.sInstallPath, 'VBoxManage' + base.exeSuff());
475 if os.path.isfile(sVBoxManage):
476 try:
477 (iExit, sStdOut, _) = utils.processOutputUnchecked([sVBoxManage, '--dump-build-type']);
478 sStdOut = sStdOut.strip();
479 if iExit == 0 and sStdOut in ('release', 'debug', 'strict', 'dbgopt', 'asan'):
480 self.sType = sStdOut;
481 reporter.log('Build: Detected build type: %s' % (self.sType));
482 else:
483 reporter.log('Build: --dump-build-type -> iExit=%u sStdOut=%s' % (iExit, sStdOut,));
484 except:
485 reporter.logXcpt('Build: Running "%s --dump-build-type" failed!' % (sVBoxManage,));
486 else:
487 reporter.log3('Build: sVBoxManage=%s not found' % (sVBoxManage,));
488
489 # Do some checks.
490 if utils.getHostOs() != 'darwin': # On macOS VMMR0.r0 might not be present anymore, especially on arm64.
491 sVMMR0 = os.path.join(self.sInstallPath, 'VMMR0.r0');
492 if not os.path.isfile(sVMMR0) and utils.getHostOs() == 'solaris': # solaris is special.
493 sVMMR0 = os.path.join(self.sInstallPath, 'amd64' if utils.getHostArch() == 'amd64' else 'i386', 'VMMR0.r0');
494 if not os.path.isfile(sVMMR0):
495 raise base.GenError('%s is missing' % (sVMMR0,));
496
497 # Guest additions location is different on windows for some _stupid_ reason.
498 if self.sOs == 'win' and self.sKind != 'development':
499 self.sGuestAdditionsIso = '%s/VBoxGuestAdditions.iso' % (self.sInstallPath,);
500 elif self.sOs == 'darwin':
501 self.sGuestAdditionsIso = '%s/VBoxGuestAdditions.iso' % (self.sInstallPath,);
502 elif self.sOs == 'solaris':
503 self.sGuestAdditionsIso = '%s/VBoxGuestAdditions.iso' % (self.sInstallPath,);
504 else:
505 self.sGuestAdditionsIso = '%s/additions/VBoxGuestAdditions.iso' % (self.sInstallPath,);
506
507 # __init__ end;
508
509 def isDevBuild(self):
510 """ Returns True if it's development build (kind), otherwise False. """
511 return self.sKind == 'development';
512
513
514class EventHandlerBase(object):
515 """
516 Base class for both Console and VirtualBox event handlers.
517 """
518
519 def __init__(self, dArgs, fpApiVer, sName = None):
520 self.oVBoxMgr = dArgs['oVBoxMgr'];
521 self.oEventSrc = dArgs['oEventSrc']; # Console/VirtualBox for < 3.3
522 self.oListener = dArgs['oListener'];
523 self.fPassive = self.oListener is not None;
524 self.sName = sName
525 self.fShutdown = False;
526 self.oThread = None;
527 self.fpApiVer = fpApiVer;
528 self.dEventNo2Name = {};
529 for sKey, iValue in self.oVBoxMgr.constants.all_values('VBoxEventType').items():
530 self.dEventNo2Name[iValue] = sKey;
531
532 def threadForPassiveMode(self):
533 """
534 The thread procedure for the event processing thread.
535 """
536 assert self.fPassive is not None;
537 while not self.fShutdown:
538 try:
539 oEvt = self.oEventSrc.getEvent(self.oListener, 500);
540 except:
541 if not self.oVBoxMgr.xcptIsDeadInterface(): reporter.logXcpt();
542 else: reporter.log('threadForPassiveMode/%s: interface croaked (ignored)' % (self.sName,));
543 break;
544 if oEvt:
545 self.handleEvent(oEvt);
546 if not self.fShutdown:
547 try:
548 self.oEventSrc.eventProcessed(self.oListener, oEvt);
549 except:
550 reporter.logXcpt();
551 break;
552 self.unregister(fWaitForThread = False);
553 return None;
554
555 def startThreadForPassiveMode(self):
556 """
557 Called when working in passive mode.
558 """
559 self.oThread = threading.Thread(target = self.threadForPassiveMode, args=(), name='PAS-%s' % (self.sName,) );
560 self.oThread.setDaemon(True); # pylint: disable=deprecated-method
561 self.oThread.start();
562 return None;
563
564 def unregister(self, fWaitForThread = True):
565 """
566 Unregister the event handler.
567 """
568 fRc = False;
569 if not self.fShutdown:
570 self.fShutdown = True;
571
572 if self.oEventSrc is not None:
573 if self.fpApiVer < 3.3:
574 try:
575 self.oEventSrc.unregisterCallback(self.oListener);
576 fRc = True;
577 except:
578 reporter.errorXcpt('unregisterCallback failed on %s' % (self.oListener,));
579 else:
580 try:
581 self.oEventSrc.unregisterListener(self.oListener);
582 fRc = True;
583 except:
584 if self.oVBoxMgr.xcptIsDeadInterface():
585 reporter.log('unregisterListener failed on %s because of dead interface (%s)'
586 % (self.oListener, self.oVBoxMgr.xcptToString(),));
587 else:
588 reporter.errorXcpt('unregisterListener failed on %s' % (self.oListener,));
589
590 if self.oThread is not None \
591 and self.oThread != threading.current_thread():
592 self.oThread.join();
593 self.oThread = None;
594
595 _ = fWaitForThread;
596 return fRc;
597
598 def handleEvent(self, oEvt):
599 """
600 Compatibility wrapper that child classes implement.
601 """
602 _ = oEvt;
603 return None;
604
605 @staticmethod
606 def registerDerivedEventHandler(oVBoxMgr, fpApiVer, oSubClass, dArgsCopy, # pylint: disable=too-many-arguments
607 oSrcParent, sSrcParentNm, sICallbackNm,
608 fMustSucceed = True, sLogSuffix = '', aenmEvents = None):
609 """
610 Registers the callback / event listener.
611 """
612 dArgsCopy['oVBoxMgr'] = oVBoxMgr;
613 dArgsCopy['oListener'] = None;
614 if fpApiVer < 3.3:
615 dArgsCopy['oEventSrc'] = oSrcParent;
616 try:
617 oRet = oVBoxMgr.createCallback(sICallbackNm, oSubClass, dArgsCopy);
618 except:
619 reporter.errorXcpt('%s::createCallback(%s) failed%s' % (sSrcParentNm, sICallbackNm, sLogSuffix,));
620 else:
621 try:
622 oSrcParent.registerCallback(oRet);
623 return oRet;
624 except Exception as oXcpt:
625 if fMustSucceed or ComError.notEqual(oXcpt, ComError.E_UNEXPECTED):
626 reporter.errorXcpt('%s::registerCallback(%s)%s' % (sSrcParentNm, oRet, sLogSuffix,));
627 else:
628 #
629 # Scalable event handling introduced in VBox 4.0.
630 #
631 fPassive = sys.platform == 'win32'; # or webservices.
632
633 if not aenmEvents:
634 aenmEvents = (vboxcon.VBoxEventType_Any,);
635
636 try:
637 oEventSrc = oSrcParent.eventSource;
638 dArgsCopy['oEventSrc'] = oEventSrc;
639 if not fPassive:
640 oListener = oRet = oVBoxMgr.createListener(oSubClass, dArgsCopy);
641 else:
642 oListener = oEventSrc.createListener();
643 dArgsCopy['oListener'] = oListener;
644 oRet = oSubClass(dArgsCopy);
645 except:
646 reporter.errorXcpt('%s::eventSource.createListener(%s) failed%s' % (sSrcParentNm, oListener, sLogSuffix));
647 else:
648 try:
649 oEventSrc.registerListener(oListener, aenmEvents, not fPassive);
650 except Exception as oXcpt:
651 if fMustSucceed or ComError.notEqual(oXcpt, ComError.E_UNEXPECTED):
652 reporter.errorXcpt('%s::eventSource.registerListener(%s) failed%s'
653 % (sSrcParentNm, oListener, sLogSuffix));
654 else:
655 if not fPassive:
656 if sys.platform == 'win32':
657 from win32com.server.util import unwrap # pylint: disable=import-error
658 oRet = unwrap(oRet);
659 oRet.oListener = oListener;
660 else:
661 oRet.startThreadForPassiveMode();
662 return oRet;
663 return None;
664
665
666
667
668class ConsoleEventHandlerBase(EventHandlerBase):
669 """
670 Base class for handling IConsole events.
671
672 The class has IConsoleCallback (<=3.2) compatible callback methods which
673 the user can override as needed.
674
675 Note! This class must not inherit from object or we'll get type errors in VBoxPython.
676 """
677 def __init__(self, dArgs, sName = None):
678 self.oSession = dArgs['oSession'];
679 self.oConsole = dArgs['oConsole'];
680 if sName is None:
681 sName = self.oSession.sName;
682 EventHandlerBase.__init__(self, dArgs, self.oSession.fpApiVer, sName);
683
684
685 # pylint: disable=missing-docstring,too-many-arguments,unused-argument
686 def onMousePointerShapeChange(self, fVisible, fAlpha, xHot, yHot, cx, cy, abShape):
687 reporter.log2('onMousePointerShapeChange/%s' % (self.sName));
688 def onMouseCapabilityChange(self, fSupportsAbsolute, *aArgs): # Extra argument was added in 3.2.
689 reporter.log2('onMouseCapabilityChange/%s' % (self.sName));
690 def onKeyboardLedsChange(self, fNumLock, fCapsLock, fScrollLock):
691 reporter.log2('onKeyboardLedsChange/%s' % (self.sName));
692 def onStateChange(self, eState):
693 reporter.log2('onStateChange/%s' % (self.sName));
694 def onAdditionsStateChange(self):
695 reporter.log2('onAdditionsStateChange/%s' % (self.sName));
696 def onNetworkAdapterChange(self, oNic):
697 reporter.log2('onNetworkAdapterChange/%s' % (self.sName));
698 def onSerialPortChange(self, oPort):
699 reporter.log2('onSerialPortChange/%s' % (self.sName));
700 def onParallelPortChange(self, oPort):
701 reporter.log2('onParallelPortChange/%s' % (self.sName));
702 def onStorageControllerChange(self):
703 reporter.log2('onStorageControllerChange/%s' % (self.sName));
704 def onMediumChange(self, attachment):
705 reporter.log2('onMediumChange/%s' % (self.sName));
706 def onCPUChange(self, iCpu, fAdd):
707 reporter.log2('onCPUChange/%s' % (self.sName));
708 def onVRDPServerChange(self):
709 reporter.log2('onVRDPServerChange/%s' % (self.sName));
710 def onRemoteDisplayInfoChange(self):
711 reporter.log2('onRemoteDisplayInfoChange/%s' % (self.sName));
712 def onUSBControllerChange(self):
713 reporter.log2('onUSBControllerChange/%s' % (self.sName));
714 def onUSBDeviceStateChange(self, oDevice, fAttached, oError):
715 reporter.log2('onUSBDeviceStateChange/%s' % (self.sName));
716 def onSharedFolderChange(self, fGlobal):
717 reporter.log2('onSharedFolderChange/%s' % (self.sName));
718 def onRuntimeError(self, fFatal, sErrId, sMessage):
719 reporter.log2('onRuntimeError/%s' % (self.sName));
720 def onCanShowWindow(self):
721 reporter.log2('onCanShowWindow/%s' % (self.sName));
722 return True
723 def onShowWindow(self):
724 reporter.log2('onShowWindow/%s' % (self.sName));
725 return None;
726 # pylint: enable=missing-docstring,too-many-arguments,unused-argument
727
728 def handleEvent(self, oEvt):
729 """
730 Compatibility wrapper.
731 """
732 try:
733 oEvtBase = self.oVBoxMgr.queryInterface(oEvt, 'IEvent');
734 eType = oEvtBase.type;
735 except:
736 reporter.logXcpt();
737 return None;
738 if eType == vboxcon.VBoxEventType_OnRuntimeError:
739 try:
740 oEvtIt = self.oVBoxMgr.queryInterface(oEvtBase, 'IRuntimeErrorEvent');
741 return self.onRuntimeError(oEvtIt.fatal, oEvtIt.id, oEvtIt.message)
742 except:
743 reporter.logXcpt();
744 ## @todo implement the other events.
745 try:
746 if eType not in (vboxcon.VBoxEventType_OnMousePointerShapeChanged,
747 vboxcon.VBoxEventType_OnCursorPositionChanged):
748 if eType in self.dEventNo2Name:
749 reporter.log2('%s(%s)/%s' % (self.dEventNo2Name[eType], str(eType), self.sName));
750 else:
751 reporter.log2('%s/%s' % (str(eType), self.sName));
752 except AttributeError: # Handle older VBox versions which don't have a specific event.
753 pass;
754 return None;
755
756
757class VirtualBoxEventHandlerBase(EventHandlerBase):
758 """
759 Base class for handling IVirtualBox events.
760
761 The class has IConsoleCallback (<=3.2) compatible callback methods which
762 the user can override as needed.
763
764 Note! This class must not inherit from object or we'll get type errors in VBoxPython.
765 """
766 def __init__(self, dArgs, sName = "emanon"):
767 self.oVBoxMgr = dArgs['oVBoxMgr'];
768 self.oVBox = dArgs['oVBox'];
769 EventHandlerBase.__init__(self, dArgs, self.oVBox.fpApiVer, sName);
770
771 # pylint: disable=missing-docstring,unused-argument
772 def onMachineStateChange(self, sMachineId, eState):
773 pass;
774 def onMachineDataChange(self, sMachineId):
775 pass;
776 def onExtraDataCanChange(self, sMachineId, sKey, sValue):
777 # The COM bridge does tuples differently. Not very funny if you ask me... ;-)
778 if self.oVBoxMgr.type == 'MSCOM':
779 return '', 0, True;
780 return True, ''
781 def onExtraDataChange(self, sMachineId, sKey, sValue):
782 pass;
783 def onMediumRegistered(self, sMediumId, eMediumType, fRegistered):
784 pass;
785 def onMachineRegistered(self, sMachineId, fRegistered):
786 pass;
787 def onSessionStateChange(self, sMachineId, eState):
788 pass;
789 def onSnapshotTaken(self, sMachineId, sSnapshotId):
790 pass;
791 def onSnapshotDiscarded(self, sMachineId, sSnapshotId):
792 pass;
793 def onSnapshotChange(self, sMachineId, sSnapshotId):
794 pass;
795 def onGuestPropertyChange(self, sMachineId, sName, sValue, sFlags, fWasDeleted):
796 pass;
797 # pylint: enable=missing-docstring,unused-argument
798
799 def handleEvent(self, oEvt):
800 """
801 Compatibility wrapper.
802 """
803 try:
804 oEvtBase = self.oVBoxMgr.queryInterface(oEvt, 'IEvent');
805 eType = oEvtBase.type;
806 except:
807 reporter.logXcpt();
808 return None;
809 if eType == vboxcon.VBoxEventType_OnMachineStateChanged:
810 try:
811 oEvtIt = self.oVBoxMgr.queryInterface(oEvtBase, 'IMachineStateChangedEvent');
812 return self.onMachineStateChange(oEvtIt.machineId, oEvtIt.state)
813 except:
814 reporter.logXcpt();
815 elif eType == vboxcon.VBoxEventType_OnGuestPropertyChanged:
816 try:
817 oEvtIt = self.oVBoxMgr.queryInterface(oEvtBase, 'IGuestPropertyChangedEvent');
818 if hasattr(oEvtIt, 'fWasDeleted'): # Since 7.0 we have a dedicated flag
819 fWasDeleted = oEvtIt.fWasDeleted;
820 else:
821 fWasDeleted = False; # Don't indicate deletion here -- there can be empty guest properties.
822 return self.onGuestPropertyChange(oEvtIt.machineId, oEvtIt.name, oEvtIt.value, oEvtIt.flags, fWasDeleted);
823 except:
824 reporter.logXcpt();
825 ## @todo implement the other events.
826 if eType in self.dEventNo2Name:
827 reporter.log2('%s(%s)/%s' % (self.dEventNo2Name[eType], str(eType), self.sName));
828 else:
829 reporter.log2('%s/%s' % (str(eType), self.sName));
830 return None;
831
832
833class SessionConsoleEventHandler(ConsoleEventHandlerBase):
834 """
835 For catching machine state changes and waking up the task machinery at that point.
836 """
837 def __init__(self, dArgs):
838 ConsoleEventHandlerBase.__init__(self, dArgs);
839
840 def onMachineStateChange(self, sMachineId, eState): # pylint: disable=unused-argument
841 """ Just interrupt the wait loop here so it can check again. """
842 _ = sMachineId; _ = eState;
843 self.oVBoxMgr.interruptWaitEvents();
844
845 def onRuntimeError(self, fFatal, sErrId, sMessage):
846 reporter.log('onRuntimeError/%s: fFatal=%d sErrId=%s sMessage=%s' % (self.sName, fFatal, sErrId, sMessage));
847 oSession = self.oSession;
848 if oSession is not None: # paranoia
849 if sErrId == 'HostMemoryLow':
850 oSession.signalHostMemoryLow();
851 if sys.platform == 'win32':
852 from testdriver import winbase;
853 winbase.logMemoryStats();
854 oSession.signalTask();
855 self.oVBoxMgr.interruptWaitEvents();
856
857
858
859class TestDriver(base.TestDriver): # pylint: disable=too-many-instance-attributes
860 """
861 This is the VirtualBox test driver.
862 """
863
864 def __init__(self):
865 base.TestDriver.__init__(self);
866 self.fImportedVBoxApi = False;
867 self.fpApiVer = 3.2;
868 self.uRevision = 0;
869 self.uApiRevision = 0;
870 self.oBuild = None;
871 self.oVBoxMgr = None;
872 self.oVBox = None;
873 self.aoRemoteSessions = [];
874 self.aoVMs = []; ## @todo not sure if this list will be of any use.
875 self.oTestVmManager = vboxtestvms.TestVmManager(self.sResourcePath);
876 self.oTestVmSet = vboxtestvms.TestVmSet();
877 self.sSessionTypeDef = 'headless';
878 self.sSessionType = self.sSessionTypeDef;
879 self.fEnableVrdp = True;
880 self.uVrdpBasePortDef = 6000;
881 self.uVrdpBasePort = self.uVrdpBasePortDef;
882 self.sDefBridgedNic = None;
883 self.fUseDefaultSvc = False;
884 self.sLogSelfGroups = '';
885 self.sLogSelfFlags = 'time';
886 self.sLogSelfDest = '';
887 self.sLogSessionGroups = '';
888 self.sLogSessionFlags = 'time';
889 self.sLogSessionDest = '';
890 self.sLogSvcGroups = '';
891 self.sLogSvcFlags = 'time';
892 self.sLogSvcDest = '';
893 self.sSelfLogFile = None;
894 self.sSessionLogFile = None;
895 self.sVBoxSvcLogFile = None;
896 self.oVBoxSvcProcess = None;
897 self.sVBoxSvcPidFile = None;
898 self.fVBoxSvcInDebugger = False;
899 self.fVBoxSvcWaitForDebugger = False;
900 self.sVBoxValidationKit = None;
901 self.sVBoxValidationKitIso = None;
902 self.sVBoxBootSectors = None;
903 self.fAlwaysUploadLogs = False;
904 self.fAlwaysUploadScreenshots = False;
905 self.fAlwaysUploadRecordings = False; # Only upload recording files on failure by default.
906 self.fEnableDebugger = True;
907 self.fVmNoTerminate = False; # Whether to skip exit handling and tearing down the VMs.
908 self.adRecordingFiles = [];
909 self.fRecordingEnabled = False; # Don't record by default (yet).
910 self.fRecordingAudio = False; # Don't record audio by default.
911 self.cSecsRecordingMax = 0; # No recording time limit in seconds.
912 self.cMbRecordingMax = 195; # The test manager web server has a configured upload limit of 200 MiBs.
913 ## @todo Can we query the configured value here
914 # (via `from testmanager import config`)?
915
916 # Drop LD_PRELOAD and enable memory leak detection in LSAN_OPTIONS from vboxinstall.py
917 # before doing build detection. This is a little crude and inflexible...
918 if 'LD_PRELOAD' in os.environ:
919 del os.environ['LD_PRELOAD'];
920 if 'LSAN_OPTIONS' in os.environ:
921 asLSanOptions = os.environ['LSAN_OPTIONS'].split(':');
922 try: asLSanOptions.remove('detect_leaks=0');
923 except: pass;
924 if asLSanOptions: os.environ['LSAN_OPTIONS'] = ':'.join(asLSanOptions);
925 else: del os.environ['LSAN_OPTIONS'];
926
927 # Quietly detect build and validation kit.
928 self._detectBuild(False);
929 self._detectValidationKit(False);
930
931 # Make sure all debug logs goes to the scratch area unless
932 # specified otherwise (more of this later on).
933 if 'VBOX_LOG_DEST' not in os.environ:
934 os.environ['VBOX_LOG_DEST'] = 'nodeny dir=%s' % (self.sScratchPath);
935
936
937 def _detectBuild(self, fQuiet = False):
938 """
939 This is used internally to try figure a locally installed build when
940 running tests manually.
941 """
942 if self.oBuild is not None:
943 return True;
944
945 # Try dev build first since that's where I'll be using it first...
946 if True is True: # pylint: disable=comparison-with-itself,comparison-of-constants
947 try:
948 self.oBuild = Build(self, None);
949 reporter.log('VBox %s build at %s (%s).'
950 % (self.oBuild.sType, self.oBuild.sInstallPath, self.oBuild.sDesignation,));
951 return True;
952 except base.GenError:
953 pass;
954
955 # Try default installation locations.
956 if self.sHost == 'win':
957 sProgFiles = os.environ.get('ProgramFiles', 'C:\\Program Files');
958 asLocs = [
959 os.path.join(sProgFiles, 'Oracle', 'VirtualBox'),
960 os.path.join(sProgFiles, 'OracleVM', 'VirtualBox'),
961 os.path.join(sProgFiles, 'Sun', 'VirtualBox'),
962 ];
963 elif self.sHost == 'solaris':
964 asLocs = [ '/opt/VirtualBox-3.2', '/opt/VirtualBox-3.1', '/opt/VirtualBox-3.0', '/opt/VirtualBox' ];
965 elif self.sHost == 'darwin':
966 asLocs = [ '/Applications/VirtualBox.app/Contents/MacOS' ];
967 elif self.sHost == 'linux':
968 asLocs = [ '/opt/VirtualBox-3.2', '/opt/VirtualBox-3.1', '/opt/VirtualBox-3.0', '/opt/VirtualBox' ];
969 else:
970 asLocs = [ '/opt/VirtualBox' ];
971 if 'VBOX_INSTALL_PATH' in os.environ:
972 asLocs.insert(0, os.environ['VBOX_INSTALL_PATH']);
973
974 for sLoc in asLocs:
975 try:
976 self.oBuild = Build(self, sLoc);
977 reporter.log('VBox %s build at %s (%s).'
978 % (self.oBuild.sType, self.oBuild.sInstallPath, self.oBuild.sDesignation,));
979 return True;
980 except base.GenError:
981 pass;
982
983 if not fQuiet:
984 reporter.error('failed to find VirtualBox installation');
985 return False;
986
987 def _detectValidationKit(self, fQuiet = False):
988 """
989 This is used internally by the constructor to try locate an unzipped
990 VBox Validation Kit somewhere in the immediate proximity.
991 """
992 if self.sVBoxValidationKit is not None:
993 return True;
994
995 #
996 # Normally it's found where we're running from, which is the same as
997 # the script directly on the testboxes.
998 #
999 asCandidates = [self.sScriptPath, ];
1000 if g_ksValidationKitDir not in asCandidates:
1001 asCandidates.append(g_ksValidationKitDir);
1002 if os.getcwd() not in asCandidates:
1003 asCandidates.append(os.getcwd());
1004 if self.oBuild is not None and self.oBuild.sInstallPath not in asCandidates:
1005 asCandidates.append(self.oBuild.sInstallPath);
1006
1007 #
1008 # When working out of the tree, we'll search the current directory
1009 # as well as parent dirs.
1010 #
1011 for sDir in list(asCandidates):
1012 for i in range(10):
1013 sDir = os.path.dirname(sDir);
1014 if sDir not in asCandidates:
1015 asCandidates.append(sDir);
1016
1017 #
1018 # Do the searching.
1019 #
1020 sCandidate = None;
1021 for i, _ in enumerate(asCandidates):
1022 sCandidate = asCandidates[i];
1023 if os.path.isfile(os.path.join(sCandidate, 'VBoxValidationKit.iso')):
1024 break;
1025 sCandidate = os.path.join(sCandidate, 'validationkit');
1026 if os.path.isfile(os.path.join(sCandidate, 'VBoxValidationKit.iso')):
1027 break;
1028 sCandidate = None;
1029
1030 fRc = sCandidate is not None;
1031 if fRc is False:
1032 if not fQuiet:
1033 reporter.error('failed to find VBox Validation Kit installation (candidates: %s)' % (asCandidates,));
1034 sCandidate = os.path.join(self.sScriptPath, 'validationkit'); # Don't leave the values as None.
1035
1036 #
1037 # Set the member values.
1038 #
1039 self.sVBoxValidationKit = sCandidate;
1040 self.sVBoxValidationKitIso = os.path.join(sCandidate, 'VBoxValidationKit.iso');
1041 self.sVBoxBootSectors = os.path.join(sCandidate, 'bootsectors');
1042 return fRc;
1043
1044 def _makeEnvironmentChanges(self):
1045 """
1046 Make the necessary VBox related environment changes.
1047 Children not importing the VBox API should call this.
1048 """
1049 # Make sure we've got our own VirtualBox config and VBoxSVC (on XPCOM at least).
1050 if not self.fUseDefaultSvc:
1051 os.environ['VBOX_USER_HOME'] = os.path.join(self.sScratchPath, 'VBoxUserHome');
1052 sUser = os.environ.get('USERNAME', os.environ.get('USER', os.environ.get('LOGNAME', 'unknown')));
1053 os.environ['VBOX_IPC_SOCKETID'] = sUser + '-VBoxTest';
1054 return True;
1055
1056 @staticmethod
1057 def makeApiRevision(uMajor, uMinor, uBuild, uApiRevision):
1058 """ Calculates an API revision number. """
1059 return (long(uMajor) << 56) | (long(uMinor) << 48) | (long(uBuild) << 40) | uApiRevision;
1060
1061 def importVBoxApi(self):
1062 """
1063 Import the 'vboxapi' module from the VirtualBox build we're using and
1064 instantiate the two basic objects.
1065
1066 This will try detect an development or installed build if no build has
1067 been associated with the driver yet.
1068 """
1069 reporter.log2('importVBoxApi started\n')
1070 if self.fImportedVBoxApi:
1071 return True;
1072
1073 self._makeEnvironmentChanges();
1074
1075 # Do the detecting.
1076 self._detectBuild();
1077 if self.oBuild is None:
1078 return False;
1079
1080 # Avoid crashing when loading the 32-bit module (or whatever it is that goes bang).
1081 if self.oBuild.sArch == 'x86' \
1082 and self.sHost == 'darwin' \
1083 and platform.architecture()[0] == '64bit' \
1084 and self.oBuild.sKind == 'development' \
1085 and os.getenv('VERSIONER_PYTHON_PREFER_32_BIT') != 'yes':
1086 reporter.log("WARNING: 64-bit python on darwin, 32-bit VBox development build => crash");
1087 reporter.log("WARNING: bash-3.2$ /usr/bin/python2.5 ./testdriver");
1088 reporter.log("WARNING: or");
1089 reporter.log("WARNING: bash-3.2$ VERSIONER_PYTHON_PREFER_32_BIT=yes ./testdriver");
1090 return False;
1091
1092 # Start VBoxSVC and load the vboxapi bits.
1093 if self._startVBoxSVC() is True:
1094 assert(self.oVBoxSvcProcess is not None);
1095
1096 sSavedSysPath = sys.path;
1097 self._setupVBoxApi();
1098 sys.path = sSavedSysPath;
1099
1100 # Adjust the default machine folder.
1101 if self.fImportedVBoxApi and not self.fUseDefaultSvc and self.fpApiVer >= 4.0:
1102 sNewFolder = os.path.join(self.sScratchPath, 'VBoxUserHome', 'Machines');
1103 try:
1104 self.oVBox.systemProperties.defaultMachineFolder = sNewFolder;
1105 except:
1106 self.fImportedVBoxApi = False;
1107 self.oVBoxMgr = None;
1108 self.oVBox = None;
1109 reporter.logXcpt("defaultMachineFolder exception (sNewFolder=%s)" % (sNewFolder,));
1110
1111 # Kill VBoxSVC on failure.
1112 if self.oVBoxMgr is None:
1113 self._stopVBoxSVC();
1114 else:
1115 assert(self.oVBoxSvcProcess is None);
1116 reporter.log2('importVBoxApi finished\n')
1117 return self.fImportedVBoxApi;
1118
1119 def _startVBoxSVC(self): # pylint: disable=too-many-statements
1120 """ Starts VBoxSVC. """
1121 assert(self.oVBoxSvcProcess is None);
1122
1123 # Setup vbox logging for VBoxSVC now and start it manually. This way
1124 # we can control both logging and shutdown.
1125 self.sVBoxSvcLogFile = '%s/VBoxSVC-debug.log' % (self.sScratchPath,);
1126 try: os.remove(self.sVBoxSvcLogFile);
1127 except: pass;
1128 os.environ['VBOX_LOG'] = self.sLogSvcGroups;
1129 os.environ['VBOX_LOG_FLAGS'] = '%s append' % (self.sLogSvcFlags,); # Append becuse of VBoxXPCOMIPCD.
1130 if self.sLogSvcDest:
1131 os.environ['VBOX_LOG_DEST'] = 'nodeny ' + self.sLogSvcDest;
1132 else:
1133 os.environ['VBOX_LOG_DEST'] = 'nodeny file=%s' % (self.sVBoxSvcLogFile,);
1134 os.environ['VBOXSVC_RELEASE_LOG_FLAGS'] = 'time append';
1135
1136 reporter.log2('VBoxSVC environment:');
1137 for sKey, sVal in sorted(os.environ.items()):
1138 reporter.log2('%s=%s' % (sKey, sVal));
1139
1140 # Always leave a pid file behind so we can kill it during cleanup-before.
1141 self.sVBoxSvcPidFile = '%s/VBoxSVC.pid' % (self.sScratchPath,);
1142 fWritePidFile = True;
1143
1144 cMsFudge = 1;
1145 sVBoxSVC = '%s/VBoxSVC' % (self.oBuild.sInstallPath,); ## @todo .exe and stuff.
1146 if self.fVBoxSvcInDebugger:
1147 if self.sHost in ('darwin', 'freebsd', 'linux', 'solaris', ):
1148 # Start VBoxSVC in gdb in a new terminal.
1149 #sTerm = '/usr/bin/gnome-terminal'; - doesn't work, some fork+exec stuff confusing us.
1150 sTerm = '/usr/bin/xterm';
1151 if not os.path.isfile(sTerm): sTerm = '/usr/X11/bin/xterm';
1152 if not os.path.isfile(sTerm): sTerm = '/usr/X11R6/bin/xterm';
1153 if not os.path.isfile(sTerm): sTerm = '/usr/bin/xterm';
1154 if not os.path.isfile(sTerm): sTerm = 'xterm';
1155 sGdb = '/usr/bin/gdb';
1156 if not os.path.isfile(sGdb): sGdb = '/usr/local/bin/gdb';
1157 if not os.path.isfile(sGdb): sGdb = '/usr/sfw/bin/gdb';
1158 if not os.path.isfile(sGdb): sGdb = 'gdb';
1159 sGdbCmdLine = '%s --args %s --pidfile %s' % (sGdb, sVBoxSVC, self.sVBoxSvcPidFile);
1160 # Cool tweak to run performance analysis instead of gdb:
1161 #sGdb = '/usr/bin/valgrind';
1162 #sGdbCmdLine = '%s --tool=callgrind --collect-atstart=no -- %s --pidfile %s' \
1163 # % (sGdb, sVBoxSVC, self.sVBoxSvcPidFile);
1164 reporter.log('term="%s" gdb="%s"' % (sTerm, sGdbCmdLine));
1165 os.environ['SHELL'] = self.sOrgShell; # Non-working shell may cause gdb and/or the term problems.
1166 ## @todo -e is deprecated; use "-- <args>".
1167 self.oVBoxSvcProcess = base.Process.spawnp(sTerm, sTerm, '-e', sGdbCmdLine);
1168 os.environ['SHELL'] = self.sOurShell;
1169 if self.oVBoxSvcProcess is not None:
1170 reporter.log('Press enter or return after starting VBoxSVC in the debugger...');
1171 sys.stdin.read(1);
1172 fWritePidFile = False;
1173
1174 elif self.sHost == 'win':
1175 sWinDbg = 'c:\\Program Files\\Debugging Tools for Windows\\windbg.exe';
1176 if not os.path.isfile(sWinDbg): sWinDbg = 'c:\\Program Files\\Debugging Tools for Windows (x64)\\windbg.exe';
1177 if not os.path.isfile(sWinDbg): sWinDbg = 'c:\\Programme\\Debugging Tools for Windows\\windbg.exe'; # Localization rulez! pylint: disable=line-too-long
1178 if not os.path.isfile(sWinDbg): sWinDbg = 'c:\\Programme\\Debugging Tools for Windows (x64)\\windbg.exe';
1179 if not os.path.isfile(sWinDbg): sWinDbg = 'windbg'; # WinDbg must be in the path; better than nothing.
1180 # Assume that everything WinDbg needs is defined using the environment variables.
1181 # See WinDbg help for more information.
1182 reporter.log('windbg="%s"' % (sWinDbg));
1183 self.oVBoxSvcProcess = base.Process.spawn(sWinDbg, sWinDbg, sVBoxSVC + base.exeSuff());
1184 if self.oVBoxSvcProcess is not None:
1185 reporter.log('Press enter or return after starting VBoxSVC in the debugger...');
1186 sys.stdin.read(1);
1187 fWritePidFile = False;
1188 ## @todo add a pipe interface similar to xpcom if feasible, i.e. if
1189 # we can get actual handle values for pipes in python.
1190
1191 else:
1192 reporter.error('Port me!');
1193 else: # Run without a debugger attached.
1194 if self.sHost in ('darwin', 'freebsd', 'linux', 'solaris', ):
1195 #
1196 # XPCOM - We can use a pipe to let VBoxSVC notify us when it's ready.
1197 #
1198 iPipeR, iPipeW = os.pipe();
1199 if hasattr(os, 'set_inheritable'):
1200 os.set_inheritable(iPipeW, True); # pylint: disable=no-member
1201
1202 # For VBox < 7.1 this is required.
1203 os.environ['NSPR_INHERIT_FDS'] = 'vboxsvc:startup-pipe:5:0x%x' % (iPipeW,);
1204 reporter.log2("NSPR_INHERIT_FDS=%s" % (os.environ['NSPR_INHERIT_FDS']));
1205
1206 # New way since VBox 7.1
1207 os.environ['VBOX_STARTUP_PIPE_FD'] = '%u' % (iPipeW,);
1208 reporter.log2("VBOX_STARTUP_PIPE_FD=%s" % (os.environ['VBOX_STARTUP_PIPE_FD']));
1209
1210 self.oVBoxSvcProcess = base.Process.spawn(sVBoxSVC, sVBoxSVC, '--auto-shutdown'); # SIGUSR1 requirement.
1211 try: # Try make sure we get the SIGINT and not VBoxSVC.
1212 os.setpgid(self.oVBoxSvcProcess.getPid(), 0); # pylint: disable=no-member
1213 os.setpgid(0, 0); # pylint: disable=no-member
1214 except:
1215 reporter.logXcpt();
1216
1217 os.close(iPipeW);
1218 try:
1219 sResponse = os.read(iPipeR, 32);
1220 except:
1221 reporter.logXcpt();
1222 sResponse = None;
1223 os.close(iPipeR);
1224
1225 if hasattr(sResponse, 'decode'):
1226 sResponse = sResponse.decode('utf-8', 'ignore');
1227
1228 if sResponse is None or sResponse.strip() != 'READY':
1229 reporter.error('VBoxSVC failed starting up... (sResponse=%s)' % (sResponse,));
1230 if not self.oVBoxSvcProcess.wait(5000):
1231 self.oVBoxSvcProcess.terminate();
1232 self.oVBoxSvcProcess.wait(5000);
1233 self.oVBoxSvcProcess = None;
1234
1235 elif self.sHost == 'win':
1236 #
1237 # Windows - Just fudge it for now.
1238 #
1239 cMsFudge = 2000;
1240 self.oVBoxSvcProcess = base.Process.spawn(sVBoxSVC, sVBoxSVC);
1241
1242 else:
1243 reporter.error('Port me!');
1244
1245 #
1246 # Enable automatic crash reporting if we succeeded.
1247 #
1248 if self.oVBoxSvcProcess is not None:
1249 self.oVBoxSvcProcess.enableCrashReporting('crash/report/svc', 'crash/dump/svc');
1250
1251 #
1252 # Wait for debugger to attach.
1253 #
1254 if self.oVBoxSvcProcess is not None and self.fVBoxSvcWaitForDebugger:
1255 reporter.log('Press any key after attaching to VBoxSVC (pid %s) with a debugger...'
1256 % (self.oVBoxSvcProcess.getPid(),));
1257 sys.stdin.read(1);
1258
1259 #
1260 # Fudge and pid file.
1261 #
1262 if self.oVBoxSvcProcess is not None and not self.oVBoxSvcProcess.wait(cMsFudge):
1263 if fWritePidFile:
1264 iPid = self.oVBoxSvcProcess.getPid();
1265 try:
1266 oFile = utils.openNoInherit(self.sVBoxSvcPidFile, "w+");
1267 oFile.write('%s' % (iPid,));
1268 oFile.close();
1269 except:
1270 reporter.logXcpt('sPidFile=%s' % (self.sVBoxSvcPidFile,));
1271 reporter.log('VBoxSVC PID=%u' % (iPid,));
1272
1273 #
1274 # Finally add the task so we'll notice when it dies in a relatively timely manner.
1275 #
1276 self.addTask(self.oVBoxSvcProcess);
1277 else:
1278 self.oVBoxSvcProcess = None;
1279 try: os.remove(self.sVBoxSvcPidFile);
1280 except: pass;
1281
1282 return self.oVBoxSvcProcess is not None;
1283
1284
1285 def _killVBoxSVCByPidFile(self, sPidFile):
1286 """ Kill a VBoxSVC given the pid from it's pid file. """
1287
1288 # Read the pid file.
1289 if not os.path.isfile(sPidFile):
1290 return False;
1291 try:
1292 oFile = utils.openNoInherit(sPidFile, "r");
1293 sPid = oFile.readline().strip();
1294 oFile.close();
1295 except:
1296 reporter.logXcpt('sPidfile=%s' % (sPidFile,));
1297 return False;
1298
1299 # Convert the pid to an integer and validate the range a little bit.
1300 try:
1301 iPid = long(sPid);
1302 except:
1303 reporter.logXcpt('sPidfile=%s sPid="%s"' % (sPidFile, sPid));
1304 return False;
1305 if iPid <= 0:
1306 reporter.log('negative pid - sPidfile=%s sPid="%s" iPid=%d' % (sPidFile, sPid, iPid));
1307 return False;
1308
1309 # Take care checking that it's VBoxSVC we're about to inhume.
1310 if base.processCheckPidAndName(iPid, "VBoxSVC") is not True:
1311 reporter.log('Ignoring stale VBoxSVC pid file (pid=%s)' % (iPid,));
1312 return False;
1313
1314 # Loop thru our different ways of getting VBoxSVC to terminate.
1315 for aHow in [ [ base.sendUserSignal1, 5000, 'Dropping VBoxSVC a SIGUSR1 hint...'], \
1316 [ base.processInterrupt, 5000, 'Dropping VBoxSVC a SIGINT hint...'], \
1317 [ base.processTerminate, 7500, 'VBoxSVC is still around, killing it...'] ]:
1318 reporter.log(aHow[2]);
1319 if aHow[0](iPid) is True:
1320 msStart = base.timestampMilli();
1321 while base.timestampMilli() - msStart < 5000 \
1322 and base.processExists(iPid):
1323 time.sleep(0.2);
1324
1325 fRc = not base.processExists(iPid);
1326 if fRc is True:
1327 break;
1328 if fRc:
1329 reporter.log('Successfully killed VBoxSVC (pid=%s)' % (iPid,));
1330 else:
1331 reporter.log('Failed to kill VBoxSVC (pid=%s)' % (iPid,));
1332 return fRc;
1333
1334 def _stopVBoxSVC(self):
1335 """
1336 Stops VBoxSVC. Try the polite way first.
1337 """
1338
1339 if self.oVBoxSvcProcess:
1340 self.removeTask(self.oVBoxSvcProcess);
1341 self.oVBoxSvcProcess.enableCrashReporting(None, None); # Disables it.
1342
1343 fRc = False;
1344 if self.oVBoxSvcProcess is not None \
1345 and not self.fVBoxSvcInDebugger:
1346 # by process object.
1347 if self.oVBoxSvcProcess.isRunning():
1348 reporter.log('Dropping VBoxSVC a SIGUSR1 hint...');
1349 if not self.oVBoxSvcProcess.sendUserSignal1() \
1350 or not self.oVBoxSvcProcess.wait(5000):
1351 reporter.log('Dropping VBoxSVC a SIGINT hint...');
1352 if not self.oVBoxSvcProcess.interrupt() \
1353 or not self.oVBoxSvcProcess.wait(5000):
1354 reporter.log('VBoxSVC is still around, killing it...');
1355 self.oVBoxSvcProcess.terminate();
1356 self.oVBoxSvcProcess.wait(7500);
1357 else:
1358 reporter.log('VBoxSVC is no longer running...');
1359
1360 if not self.oVBoxSvcProcess.isRunning():
1361 iExit = self.oVBoxSvcProcess.getExitCode();
1362 if iExit != 0 or not self.oVBoxSvcProcess.isNormalExit():
1363 reporter.error("VBoxSVC exited with status %d (%#x)" % (iExit, self.oVBoxSvcProcess.uExitCode));
1364 self.oVBoxSvcProcess = None;
1365 else:
1366 # by pid file.
1367 self._killVBoxSVCByPidFile('%s/VBoxSVC.pid' % (self.sScratchPath,));
1368 return fRc;
1369
1370 def _setupVBoxApi(self):
1371 """
1372 Import and set up the vboxapi.
1373 The caller saves and restores sys.path.
1374 """
1375
1376 # Setup vbox logging for self (the test driver).
1377 self.sSelfLogFile = '%s/VBoxTestDriver.log' % (self.sScratchPath,);
1378 try: os.remove(self.sSelfLogFile);
1379 except: pass;
1380 os.environ['VBOX_LOG'] = self.sLogSelfGroups;
1381 os.environ['VBOX_LOG_FLAGS'] = '%s append' % (self.sLogSelfFlags, );
1382 if self.sLogSelfDest:
1383 os.environ['VBOX_LOG_DEST'] = 'nodeny ' + self.sLogSelfDest;
1384 else:
1385 os.environ['VBOX_LOG_DEST'] = 'nodeny file=%s' % (self.sSelfLogFile,);
1386 os.environ['VBOX_RELEASE_LOG_FLAGS'] = 'time append';
1387
1388 reporter.log2('Self environment:');
1389 for sKey, sVal in sorted(os.environ.items()):
1390 reporter.log2('%s=%s' % (sKey, sVal));
1391
1392 # Hack the sys.path + environment so the vboxapi can be found.
1393 sys.path.insert(0, self.oBuild.sInstallPath);
1394 if self.oBuild.sSdkPath is not None:
1395 sys.path.insert(0, os.path.join(self.oBuild.sSdkPath, 'installer'))
1396 sys.path.insert(0, os.path.join(self.oBuild.sSdkPath, 'installer', 'python', 'vboxapi', 'src')) # For >= VBox 7.1
1397 sys.path.insert(1, os.path.join(self.oBuild.sSdkPath, 'install')); # stupid stupid windows installer (VBox < 7.1)!
1398 sys.path.insert(2, os.path.join(self.oBuild.sSdkPath, 'bindings', 'xpcom', 'python'))
1399 os.environ['VBOX_PROGRAM_PATH'] = self.oBuild.sInstallPath;
1400 reporter.log("sys.path: %s" % (sys.path));
1401
1402 try:
1403 from vboxapi import VirtualBoxManager; # pylint: disable=import-error
1404 except:
1405 reporter.logXcpt('Error importing vboxapi (Python %s)' % (sys.version,));
1406 return False;
1407
1408 # Exception and error hacks.
1409 try:
1410 # pylint: disable=import-error
1411 if self.sHost == 'win':
1412 from pythoncom import com_error as NativeComExceptionClass # pylint: disable=no-name-in-module
1413 import winerror as NativeComErrorClass
1414 else:
1415 from xpcom import Exception as NativeComExceptionClass
1416 from xpcom import nsError as NativeComErrorClass
1417 # pylint: enable=import-error
1418 except:
1419 reporter.logXcpt('Error importing (XP)COM related stuff for exception hacks and errors');
1420 return False;
1421 __deployExceptionHacks__(NativeComExceptionClass)
1422 ComError.copyErrors(NativeComErrorClass);
1423
1424 # Create the manager.
1425 try:
1426 self.oVBoxMgr = VirtualBoxManager(None, None)
1427 except:
1428 self.oVBoxMgr = None;
1429 reporter.logXcpt('VirtualBoxManager exception');
1430 return False;
1431
1432 # Figure the API version.
1433 try:
1434 oVBox = self.oVBoxMgr.getVirtualBox();
1435
1436 try:
1437 sVer = oVBox.version;
1438 except:
1439 reporter.logXcpt('Failed to get VirtualBox version, assuming 4.0.0');
1440 sVer = "4.0.0";
1441 reporter.log("IVirtualBox.version=%s" % (sVer,));
1442
1443 # Convert the string to three integer values and check ranges.
1444 asVerComponents = sVer.split('.');
1445 try:
1446 sLast = asVerComponents[2].split('_')[0].split('r')[0];
1447 aiVerComponents = (int(asVerComponents[0]), int(asVerComponents[1]), int(sLast));
1448 except:
1449 raise base.GenError('Malformed version "%s"' % (sVer,));
1450 if aiVerComponents[0] < 3 or aiVerComponents[0] > 19:
1451 raise base.GenError('Malformed version "%s" - 1st component is out of bounds 3..19: %u'
1452 % (sVer, aiVerComponents[0]));
1453 if aiVerComponents[1] < 0 or aiVerComponents[1] > 9:
1454 raise base.GenError('Malformed version "%s" - 2nd component is out of bounds 0..9: %u'
1455 % (sVer, aiVerComponents[1]));
1456 if aiVerComponents[2] < 0 or aiVerComponents[2] > 99:
1457 raise base.GenError('Malformed version "%s" - 3rd component is out of bounds 0..99: %u'
1458 % (sVer, aiVerComponents[2]));
1459
1460 # Convert the three integers into a floating point value. The API is stable within a
1461 # x.y release, so the third component only indicates whether it's a stable or
1462 # development build of the next release.
1463 self.fpApiVer = aiVerComponents[0] + 0.1 * aiVerComponents[1];
1464 if aiVerComponents[2] >= 71:
1465 if self.fpApiVer not in [6.1, 5.2, 4.3, 3.2,]:
1466 self.fpApiVer += 0.1;
1467 else:
1468 self.fpApiVer = int(self.fpApiVer) + 1.0;
1469 # fudge value to be always bigger than the nominal value (0.1 gets rounded down)
1470 if round(self.fpApiVer, 1) > self.fpApiVer:
1471 self.fpApiVer += sys.float_info.epsilon * self.fpApiVer / 2.0;
1472
1473 try:
1474 self.uRevision = oVBox.revision;
1475 except:
1476 reporter.logXcpt('Failed to get VirtualBox revision, assuming 0');
1477 self.uRevision = 0;
1478 reporter.log("IVirtualBox.revision=%u" % (self.uRevision,));
1479
1480 try:
1481 self.uApiRevision = oVBox.APIRevision;
1482 except:
1483 reporter.logXcpt('Failed to get VirtualBox APIRevision, faking it.');
1484 self.uApiRevision = self.makeApiRevision(aiVerComponents[0], aiVerComponents[1], aiVerComponents[2], 0);
1485 reporter.log("IVirtualBox.APIRevision=%#x" % (self.uApiRevision,));
1486
1487 # Patch VBox manage to gloss over portability issues (error constants, etc).
1488 self._patchVBoxMgr();
1489
1490 # Wrap oVBox.
1491 from testdriver.vboxwrappers import VirtualBoxWrapper;
1492 self.oVBox = VirtualBoxWrapper(oVBox, self.oVBoxMgr, self.fpApiVer, self);
1493
1494 # Install the constant wrapping hack.
1495 vboxcon.goHackModuleClass.oVBoxMgr = self.oVBoxMgr; # VBoxConstantWrappingHack.
1496 vboxcon.fpApiVer = self.fpApiVer;
1497 reporter.setComXcptFormatter(formatComOrXpComException);
1498
1499 except:
1500 self.oVBoxMgr = None;
1501 self.oVBox = None;
1502 reporter.logXcpt("getVirtualBox / API version exception");
1503 return False;
1504
1505 # Done
1506 self.fImportedVBoxApi = True;
1507 reporter.log('Found version %s (%s)' % (self.fpApiVer, sVer));
1508 return True;
1509
1510 def _patchVBoxMgr(self):
1511 """
1512 Glosses over missing self.oVBoxMgr methods on older VBox versions.
1513 """
1514
1515 def _xcptGetResult(oSelf, oXcpt = None):
1516 """ See vboxapi. """
1517 _ = oSelf;
1518 if oXcpt is None: oXcpt = sys.exc_info()[1];
1519 if sys.platform == 'win32':
1520 import winerror; # pylint: disable=import-error
1521 hrXcpt = oXcpt.hresult;
1522 if hrXcpt == winerror.DISP_E_EXCEPTION:
1523 hrXcpt = oXcpt.excepinfo[5];
1524 else:
1525 hrXcpt = oXcpt.error;
1526 return hrXcpt;
1527
1528 def _xcptIsDeadInterface(oSelf, oXcpt = None):
1529 """ See vboxapi. """
1530 return oSelf.xcptGetStatus(oXcpt) in [
1531 0x80004004, -2147467260, # NS_ERROR_ABORT
1532 0x800706be, -2147023170, # NS_ERROR_CALL_FAILED (RPC_S_CALL_FAILED)
1533 0x800706ba, -2147023174, # RPC_S_SERVER_UNAVAILABLE.
1534 0x800706be, -2147023170, # RPC_S_CALL_FAILED.
1535 0x800706bf, -2147023169, # RPC_S_CALL_FAILED_DNE.
1536 0x80010108, -2147417848, # RPC_E_DISCONNECTED.
1537 0x800706b5, -2147023179, # RPC_S_UNKNOWN_IF
1538 ];
1539
1540 def _xcptIsOurXcptKind(oSelf, oXcpt = None):
1541 """ See vboxapi. """
1542 _ = oSelf;
1543 if oXcpt is None: oXcpt = sys.exc_info()[1];
1544 if sys.platform == 'win32':
1545 from pythoncom import com_error as NativeComExceptionClass # pylint: disable=import-error,no-name-in-module
1546 else:
1547 from xpcom import Exception as NativeComExceptionClass # pylint: disable=import-error
1548 return isinstance(oXcpt, NativeComExceptionClass);
1549
1550 def _xcptIsEqual(oSelf, oXcpt, hrStatus):
1551 """ See vboxapi. """
1552 hrXcpt = oSelf.xcptGetResult(oXcpt);
1553 return hrXcpt == hrStatus or hrXcpt == hrStatus - 0x100000000; # pylint: disable=consider-using-in
1554
1555 def _xcptToString(oSelf, oXcpt):
1556 """ See vboxapi. """
1557 _ = oSelf;
1558 if oXcpt is None: oXcpt = sys.exc_info()[1];
1559 return str(oXcpt);
1560
1561 def _getEnumValueName(oSelf, sEnumTypeNm, oEnumValue, fTypePrefix = False):
1562 """ See vboxapi. """
1563 _ = oSelf; _ = fTypePrefix;
1564 return '%s::%s' % (sEnumTypeNm, oEnumValue);
1565
1566 # Add utilities found in newer vboxapi revision.
1567 if not hasattr(self.oVBoxMgr, 'xcptIsDeadInterface'):
1568 import types;
1569 self.oVBoxMgr.xcptGetResult = types.MethodType(_xcptGetResult, self.oVBoxMgr);
1570 self.oVBoxMgr.xcptIsDeadInterface = types.MethodType(_xcptIsDeadInterface, self.oVBoxMgr);
1571 self.oVBoxMgr.xcptIsOurXcptKind = types.MethodType(_xcptIsOurXcptKind, self.oVBoxMgr);
1572 self.oVBoxMgr.xcptIsEqual = types.MethodType(_xcptIsEqual, self.oVBoxMgr);
1573 self.oVBoxMgr.xcptToString = types.MethodType(_xcptToString, self.oVBoxMgr);
1574 if not hasattr(self.oVBoxMgr, 'getEnumValueName'):
1575 import types;
1576 self.oVBoxMgr.getEnumValueName = types.MethodType(_getEnumValueName, self.oVBoxMgr);
1577
1578
1579 def _teardownVBoxApi(self): # pylint: disable=too-many-statements
1580 """
1581 Drop all VBox object references and shutdown com/xpcom.
1582 """
1583 if not self.fImportedVBoxApi:
1584 return True;
1585 import gc;
1586
1587 # Drop all references we've have to COM objects.
1588 self.aoRemoteSessions = [];
1589 self.aoVMs = [];
1590 self.oVBoxMgr = None;
1591 self.oVBox = None;
1592 vboxcon.goHackModuleClass.oVBoxMgr = None; # VBoxConstantWrappingHack.
1593 reporter.setComXcptFormatter(None);
1594
1595 # Do garbage collection to try get rid of those objects.
1596 try:
1597 gc.collect();
1598 except:
1599 reporter.logXcpt();
1600 self.fImportedVBoxApi = False;
1601
1602 # Check whether the python is still having any COM objects/interfaces around.
1603 cVBoxMgrs = 0;
1604 aoObjsLeftBehind = [];
1605 if self.sHost == 'win':
1606 import pythoncom; # pylint: disable=import-error
1607 try:
1608 cIfs = pythoncom._GetInterfaceCount(); # pylint: disable=no-member,protected-access
1609 cObjs = pythoncom._GetGatewayCount(); # pylint: disable=no-member,protected-access
1610 if cObjs == 0 and cIfs == 0:
1611 reporter.log('_teardownVBoxApi: no interfaces or objects left behind.');
1612 else:
1613 reporter.log('_teardownVBoxApi: Python COM still has %s objects and %s interfaces...' % ( cObjs, cIfs));
1614
1615 from win32com.client import DispatchBaseClass; # pylint: disable=import-error
1616 for oObj in gc.get_objects():
1617 if isinstance(oObj, DispatchBaseClass):
1618 reporter.log('_teardownVBoxApi: %s' % (oObj,));
1619 aoObjsLeftBehind.append(oObj);
1620 elif utils.getObjectTypeName(oObj) == 'VirtualBoxManager':
1621 reporter.log('_teardownVBoxApi: %s' % (oObj,));
1622 cVBoxMgrs += 1;
1623 aoObjsLeftBehind.append(oObj);
1624 oObj = None;
1625 except:
1626 reporter.logXcpt();
1627
1628 # If not being used, we can safely uninitialize COM.
1629 if cIfs == 0 and cObjs == 0 and cVBoxMgrs == 0 and not aoObjsLeftBehind:
1630 reporter.log('_teardownVBoxApi: Calling CoUninitialize...');
1631 try: pythoncom.CoUninitialize(); # pylint: disable=no-member
1632 except: reporter.logXcpt();
1633 else:
1634 reporter.log('_teardownVBoxApi: Returned from CoUninitialize.');
1635 else:
1636 try:
1637 # XPCOM doesn't crash and burn like COM if you shut it down with interfaces and objects around.
1638 # Also, it keeps a number of internal objects and interfaces around to do its job, so shutting
1639 # it down before we go looking for dangling interfaces is more or less required.
1640 from xpcom import _xpcom as _xpcom; # pylint: disable=import-error,useless-import-alias
1641 hrc = _xpcom.DeinitCOM();
1642 cIfs = _xpcom._GetInterfaceCount(); # pylint: disable=protected-access
1643 cObjs = _xpcom._GetGatewayCount(); # pylint: disable=protected-access
1644
1645 if cObjs == 0 and cIfs == 0:
1646 reporter.log('_teardownVBoxApi: No XPCOM interfaces or objects active. (hrc=%#x)' % (hrc,));
1647 else:
1648 reporter.log('_teardownVBoxApi: %s XPCOM objects and %s interfaces still around! (hrc=%#x)'
1649 % (cObjs, cIfs, hrc));
1650 if hasattr(_xpcom, '_DumpInterfaces'):
1651 try: _xpcom._DumpInterfaces(); # pylint: disable=protected-access
1652 except: reporter.logXcpt('_teardownVBoxApi: _DumpInterfaces failed');
1653
1654 from xpcom.client import Component; # pylint: disable=import-error
1655 for oObj in gc.get_objects():
1656 if isinstance(oObj, Component):
1657 reporter.log('_teardownVBoxApi: %s' % (oObj,));
1658 aoObjsLeftBehind.append(oObj);
1659 if utils.getObjectTypeName(oObj) == 'VirtualBoxManager':
1660 reporter.log('_teardownVBoxApi: %s' % (oObj,));
1661 cVBoxMgrs += 1;
1662 aoObjsLeftBehind.append(oObj);
1663 oObj = None;
1664 except:
1665 reporter.logXcpt();
1666
1667 # Try get the referrers to (XP)COM interfaces and objects that was left behind.
1668 for iObj in range(len(aoObjsLeftBehind)): # pylint: disable=consider-using-enumerate
1669 try:
1670 aoReferrers = gc.get_referrers(aoObjsLeftBehind[iObj]);
1671 reporter.log('_teardownVBoxApi: Found %u referrers to %s:' % (len(aoReferrers), aoObjsLeftBehind[iObj],));
1672 for oReferrer in aoReferrers:
1673 oMyFrame = sys._getframe(0); # pylint: disable=protected-access
1674 if oReferrer is oMyFrame:
1675 reporter.log('_teardownVBoxApi: - frame of this function');
1676 elif oReferrer is aoObjsLeftBehind:
1677 reporter.log('_teardownVBoxApi: - aoObjsLeftBehind');
1678 else:
1679 fPrinted = False;
1680 if isinstance(oReferrer, (dict, list, tuple)):
1681 try:
1682 aoSubReferreres = gc.get_referrers(oReferrer);
1683 for oSubRef in aoSubReferreres:
1684 if not isinstance(oSubRef, list) \
1685 and not isinstance(oSubRef, dict) \
1686 and oSubRef is not oMyFrame \
1687 and oSubRef is not aoSubReferreres:
1688 reporter.log('_teardownVBoxApi: - %s :: %s:'
1689 % (utils.getObjectTypeName(oSubRef), utils.getObjectTypeName(oReferrer)));
1690 fPrinted = True;
1691 break;
1692 del aoSubReferreres;
1693 except:
1694 reporter.logXcpt('subref');
1695 if not fPrinted:
1696 reporter.log('_teardownVBoxApi: - %s:' % (utils.getObjectTypeName(oReferrer),));
1697 try:
1698 import pprint;
1699 for sLine in pprint.pformat(oReferrer, width = 130).split('\n'):
1700 reporter.log('_teardownVBoxApi: %s' % (sLine,));
1701 except:
1702 reporter.log('_teardownVBoxApi: %s' % (oReferrer,));
1703 except:
1704 reporter.logXcpt();
1705 del aoObjsLeftBehind;
1706
1707 # Force garbage collection again, just for good measure.
1708 try:
1709 gc.collect();
1710 time.sleep(0.5); # fudge factor
1711 except:
1712 reporter.logXcpt();
1713 return True;
1714
1715 def _powerOffAllVms(self):
1716 """
1717 Tries to power off all running VMs.
1718 """
1719 for oSession in self.aoRemoteSessions:
1720 uPid = oSession.getPid();
1721 if uPid is not None:
1722 reporter.log('_powerOffAllVms: PID is %s for %s, trying to kill it.' % (uPid, oSession.sName,));
1723 base.processKill(uPid);
1724 else:
1725 reporter.log('_powerOffAllVms: No PID for %s' % (oSession.sName,));
1726 oSession.close();
1727 return None;
1728
1729
1730
1731 #
1732 # Build type, OS and arch getters.
1733 #
1734
1735 def getBuildType(self):
1736 """
1737 Get the build type.
1738 """
1739 if not self._detectBuild():
1740 return 'release';
1741 return self.oBuild.sType;
1742
1743 def getBuildOs(self):
1744 """
1745 Get the build OS.
1746 """
1747 if not self._detectBuild():
1748 return self.sHost;
1749 return self.oBuild.sOs;
1750
1751 def getBuildArch(self):
1752 """
1753 Get the build arch.
1754 """
1755 if not self._detectBuild():
1756 return self.sHostArch;
1757 return self.oBuild.sArch;
1758
1759 def getGuestAdditionsIso(self):
1760 """
1761 Get the path to the guest addition iso.
1762 """
1763 if not self._detectBuild():
1764 return None;
1765 return self.oBuild.sGuestAdditionsIso;
1766
1767 @staticmethod
1768 def versionToTuple(sVer, fIgnoreErrors = False):
1769 """
1770 Returns a semantic versioning string as a tuple.
1771 """
1772 try:
1773 # Regular expression taken from semver.org (recommended regular expression for semantic version strings).
1774 # Creative Commons ― CC BY 3.0
1775 oRegEx = re.compile(r'^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$'); # pylint: disable=line-too-long
1776 oMatch = oRegEx.search(sVer);
1777 return oMatch.groups();
1778 except:
1779 if not fIgnoreErrors:
1780 reporter.logXcpt('Handling regex for "%s" failed' % (sVer,));
1781 return None;
1782
1783 @staticmethod
1784 def compareVersion(sVer1, sVer2, fIgnoreErrors = False):
1785 """
1786 Compares two version numbers and returns the result.
1787
1788 Takes either strings or version tuples as input.
1789
1790 Returns 0 if both versions match.
1791 Return -1 if version 1 is bigger than version 2.
1792 Return 1 if version 2 is bigger than version 1.
1793 Returns None on error.
1794 """
1795 assert sVer1 is not None;
1796 assert sVer2 is not None;
1797 try:
1798 tpVer1 = TestDriver.versionToTuple(sVer1, fIgnoreErrors);
1799 if tpVer1 is None:
1800 return None;
1801 tpVer2 = TestDriver.versionToTuple(sVer2, fIgnoreErrors);
1802 if tpVer2 is None:
1803 return None;
1804 if tpVer1 == tpVer2:
1805 return 0;
1806 return 1 if tuple(map(str, tpVer2)) > tuple(map(str, tpVer1)) else -1;
1807 except:
1808 if not fIgnoreErrors:
1809 reporter.logXcpt();
1810 return None;
1811
1812 @staticmethod
1813 def isVersionEqualOrBigger(sVer1, sVer2, fIgnoreErrors = False):
1814 """
1815 Checks whether version 1 is equal or bigger than version 2.
1816
1817 Returns True if version 1 is equal or bigger than version 2, False if not.
1818 """
1819 return not TestDriver.compareVersion(sVer1, sVer2, fIgnoreErrors);
1820
1821 def getGuestAdditionsVersion(self, oSession, fIgnoreErrors = False):
1822 """
1823 Returns the installed Guest Additions version.
1824
1825 Returns version as a string (e.g. "7.0.6-beta2-whatever"), or None if not found / on error.
1826 """
1827 assert oSession is not None;
1828 try:
1829 return oSession.o.console.guest.additionsVersion;
1830 except:
1831 if not fIgnoreErrors:
1832 reporter.errorXcpt('Getting the Guest Additions version failed');
1833 return None;
1834
1835 #
1836 # Override everything from the base class so the testdrivers don't have to
1837 # check whether we have overridden a method or not.
1838 #
1839
1840 def showUsage(self):
1841 rc = base.TestDriver.showUsage(self);
1842 reporter.log('');
1843 reporter.log('Generic VirtualBox Options:');
1844 reporter.log(' --vbox-session-type <type>');
1845 reporter.log(' Sets the session type. Typical values are: gui, headless, sdl');
1846 reporter.log(' Default: %s' % (self.sSessionTypeDef));
1847 reporter.log(' --vrdp, --no-vrdp');
1848 reporter.log(' Enables VRDP, ports starting at 6000');
1849 reporter.log(' Default: --vrdp');
1850 reporter.log(' --vrdp-base-port <port>');
1851 reporter.log(' Sets the base for VRDP port assignments.');
1852 reporter.log(' Default: %s' % (self.uVrdpBasePortDef));
1853 reporter.log(' --vbox-default-bridged-nic <interface>');
1854 reporter.log(' Sets the default interface for bridged networking.');
1855 reporter.log(' Default: autodetect');
1856 reporter.log(' --vbox-use-svc-defaults');
1857 reporter.log(' Use default locations and files for VBoxSVC. This is useful');
1858 reporter.log(' for automatically configuring the test VMs for debugging.');
1859 reporter.log(' --vbox-log');
1860 reporter.log(' The VBox logger group settings for everyone.');
1861 reporter.log(' --vbox-log-flags');
1862 reporter.log(' The VBox logger flags settings for everyone.');
1863 reporter.log(' --vbox-log-dest');
1864 reporter.log(' The VBox logger destination settings for everyone.');
1865 reporter.log(' --vbox-self-log');
1866 reporter.log(' The VBox logger group settings for the testdriver.');
1867 reporter.log(' --vbox-self-log-flags');
1868 reporter.log(' The VBox logger flags settings for the testdriver.');
1869 reporter.log(' --vbox-self-log-dest');
1870 reporter.log(' The VBox logger destination settings for the testdriver.');
1871 reporter.log(' --vbox-session-log');
1872 reporter.log(' The VM session logger group settings.');
1873 reporter.log(' --vbox-session-log-flags');
1874 reporter.log(' The VM session logger flags.');
1875 reporter.log(' --vbox-session-log-dest');
1876 reporter.log(' The VM session logger destination settings.');
1877 reporter.log(' --vbox-svc-log');
1878 reporter.log(' The VBoxSVC logger group settings.');
1879 reporter.log(' --vbox-svc-log-flags');
1880 reporter.log(' The VBoxSVC logger flag settings.');
1881 reporter.log(' --vbox-svc-log-dest');
1882 reporter.log(' The VBoxSVC logger destination settings.');
1883 reporter.log(' --vbox-svc-debug');
1884 reporter.log(' Start VBoxSVC in a debugger.');
1885 reporter.log(' --vbox-svc-wait-debug');
1886 reporter.log(' Start VBoxSVC and wait for debugger to attach to it.');
1887 reporter.log(' --vbox-always-upload-logs');
1888 reporter.log(' Whether to always upload log files, or only do so on failure.');
1889 reporter.log(' --vbox-always-upload-screenshots');
1890 reporter.log(' Whether to always upload final screen shots, or only do so on failure.');
1891 reporter.log(' --vbox-always-upload-recordings, --no-vbox-always-upload-recordings');
1892 reporter.log(' Whether to always upload recordings, or only do so on failure.');
1893 reporter.log(' Default: --no-vbox-always-upload-recordings');
1894 reporter.log(' --vbox-debugger, --no-vbox-debugger');
1895 reporter.log(' Enables the VBox debugger, port at 5000');
1896 reporter.log(' Default: --vbox-debugger');
1897 reporter.log(' --vbox-recording, --no-vbox-recording');
1898 reporter.log(' Enables/disables recording.');
1899 reporter.log(' Default: --no-vbox-recording');
1900 reporter.log(' --vbox-recording-audio, --no-vbox-recording-audio');
1901 reporter.log(' Enables/disables audio recording.');
1902 reporter.log(' Default: --no-vbox-recording-audio');
1903 reporter.log(' --vbox-recording-max-time <seconds>');
1904 reporter.log(' Limits the maximum recording time in seconds.');
1905 reporter.log(' Default: Unlimited.');
1906 reporter.log(' --vbox-recording-max-file-size <MiB>');
1907 reporter.log(' Limits the maximum per-file size in MiB.');
1908 reporter.log(' Explicitly specify 0 for unlimited size.');
1909 reporter.log(' Default: 195 MB.');
1910 reporter.log(' --vbox-vm-no-terminate');
1911 reporter.log(' Does not terminate the test VM after running the test driver.');
1912 if self.oTestVmSet is not None:
1913 self.oTestVmSet.showUsage();
1914 return rc;
1915
1916 def parseOption(self, asArgs, iArg): # pylint: disable=too-many-branches,too-many-statements
1917 if asArgs[iArg] == '--vbox-session-type':
1918 iArg += 1;
1919 if iArg >= len(asArgs):
1920 raise base.InvalidOption('The "--vbox-session-type" takes an argument');
1921 self.sSessionType = asArgs[iArg];
1922 elif asArgs[iArg] == '--vrdp':
1923 self.fEnableVrdp = True;
1924 elif asArgs[iArg] == '--no-vrdp':
1925 self.fEnableVrdp = False;
1926 elif asArgs[iArg] == '--vrdp-base-port':
1927 iArg += 1;
1928 if iArg >= len(asArgs):
1929 raise base.InvalidOption('The "--vrdp-base-port" takes an argument');
1930 try: self.uVrdpBasePort = int(asArgs[iArg]);
1931 except: raise base.InvalidOption('The "--vrdp-base-port" value "%s" is not a valid integer' % (asArgs[iArg],));
1932 if self.uVrdpBasePort <= 0 or self.uVrdpBasePort >= 65530:
1933 raise base.InvalidOption('The "--vrdp-base-port" value "%s" is not in the valid range (1..65530)'
1934 % (asArgs[iArg],));
1935 elif asArgs[iArg] == '--vbox-default-bridged-nic':
1936 iArg += 1;
1937 if iArg >= len(asArgs):
1938 raise base.InvalidOption('The "--vbox-default-bridged-nic" takes an argument');
1939 self.sDefBridgedNic = asArgs[iArg];
1940 elif asArgs[iArg] == '--vbox-use-svc-defaults':
1941 self.fUseDefaultSvc = True;
1942 elif asArgs[iArg] == '--vbox-self-log':
1943 iArg += 1;
1944 if iArg >= len(asArgs):
1945 raise base.InvalidOption('The "--vbox-self-log" takes an argument');
1946 self.sLogSelfGroups = asArgs[iArg];
1947 elif asArgs[iArg] == '--vbox-self-log-flags':
1948 iArg += 1;
1949 if iArg >= len(asArgs):
1950 raise base.InvalidOption('The "--vbox-self-log-flags" takes an argument');
1951 self.sLogSelfFlags = asArgs[iArg];
1952 elif asArgs[iArg] == '--vbox-self-log-dest':
1953 iArg += 1;
1954 if iArg >= len(asArgs):
1955 raise base.InvalidOption('The "--vbox-self-log-dest" takes an argument');
1956 self.sLogSelfDest = asArgs[iArg];
1957 elif asArgs[iArg] == '--vbox-session-log':
1958 iArg += 1;
1959 if iArg >= len(asArgs):
1960 raise base.InvalidOption('The "--vbox-session-log" takes an argument');
1961 self.sLogSessionGroups = asArgs[iArg];
1962 elif asArgs[iArg] == '--vbox-session-log-flags':
1963 iArg += 1;
1964 if iArg >= len(asArgs):
1965 raise base.InvalidOption('The "--vbox-session-log-flags" takes an argument');
1966 self.sLogSessionFlags = asArgs[iArg];
1967 elif asArgs[iArg] == '--vbox-session-log-dest':
1968 iArg += 1;
1969 if iArg >= len(asArgs):
1970 raise base.InvalidOption('The "--vbox-session-log-dest" takes an argument');
1971 self.sLogSessionDest = asArgs[iArg];
1972 elif asArgs[iArg] == '--vbox-svc-log':
1973 iArg += 1;
1974 if iArg >= len(asArgs):
1975 raise base.InvalidOption('The "--vbox-svc-log" takes an argument');
1976 self.sLogSvcGroups = asArgs[iArg];
1977 elif asArgs[iArg] == '--vbox-svc-log-flags':
1978 iArg += 1;
1979 if iArg >= len(asArgs):
1980 raise base.InvalidOption('The "--vbox-svc-log-flags" takes an argument');
1981 self.sLogSvcFlags = asArgs[iArg];
1982 elif asArgs[iArg] == '--vbox-svc-log-dest':
1983 iArg += 1;
1984 if iArg >= len(asArgs):
1985 raise base.InvalidOption('The "--vbox-svc-log-dest" takes an argument');
1986 self.sLogSvcDest = asArgs[iArg];
1987 elif asArgs[iArg] == '--vbox-log':
1988 iArg += 1;
1989 if iArg >= len(asArgs):
1990 raise base.InvalidOption('The "--vbox-log" takes an argument');
1991 self.sLogSelfGroups = asArgs[iArg];
1992 self.sLogSessionGroups = asArgs[iArg];
1993 self.sLogSvcGroups = asArgs[iArg];
1994 elif asArgs[iArg] == '--vbox-log-flags':
1995 iArg += 1;
1996 if iArg >= len(asArgs):
1997 raise base.InvalidOption('The "--vbox-svc-flags" takes an argument');
1998 self.sLogSelfFlags = asArgs[iArg];
1999 self.sLogSessionFlags = asArgs[iArg];
2000 self.sLogSvcFlags = asArgs[iArg];
2001 elif asArgs[iArg] == '--vbox-log-dest':
2002 iArg += 1;
2003 if iArg >= len(asArgs):
2004 raise base.InvalidOption('The "--vbox-log-dest" takes an argument');
2005 self.sLogSelfDest = asArgs[iArg];
2006 self.sLogSessionDest = asArgs[iArg];
2007 self.sLogSvcDest = asArgs[iArg];
2008 elif asArgs[iArg] == '--vbox-svc-debug':
2009 self.fVBoxSvcInDebugger = True;
2010 elif asArgs[iArg] == '--vbox-svc-wait-debug':
2011 self.fVBoxSvcWaitForDebugger = True;
2012 elif asArgs[iArg] == '--vbox-always-upload-logs':
2013 self.fAlwaysUploadLogs = True;
2014 elif asArgs[iArg] == '--vbox-always-upload-screenshots':
2015 self.fAlwaysUploadScreenshots = True;
2016 elif asArgs[iArg] == '--no-vbox-always-upload-recordings':
2017 self.fAlwaysUploadRecordings = False;
2018 elif asArgs[iArg] == '--vbox-always-upload-recordings':
2019 self.fAlwaysUploadRecordings = True;
2020 elif asArgs[iArg] == '--vbox-debugger':
2021 self.fEnableDebugger = True;
2022 elif asArgs[iArg] == '--no-vbox-debugger':
2023 self.fEnableDebugger = False;
2024 elif asArgs[iArg] == '--vbox-recording':
2025 self.fRecordingEnabled = True;
2026 elif asArgs[iArg] == '--vbox-no-recording':
2027 self.fRecordingEnabled = False;
2028 elif asArgs[iArg] == '--no-vbox-recording-audio':
2029 self.fRecordingAudio = False;
2030 elif asArgs[iArg] == '--vbox-recording-audio':
2031 self.fRecordingAudio = True;
2032 elif asArgs[iArg] == '--vbox-recording-max-time':
2033 iArg += 1;
2034 if iArg >= len(asArgs):
2035 raise base.InvalidOption('The "--vbox-recording-max-time" takes an argument');
2036 self.cSecsRecordingMax = int(asArgs[iArg]);
2037 elif asArgs[iArg] == '--vbox-recording-max-file-size':
2038 iArg += 1;
2039 if iArg >= len(asArgs):
2040 raise base.InvalidOption('The "--vbox-recording-max-file-size" takes an argument');
2041 self.cMbRecordingMax = int(asArgs[iArg]);
2042 elif asArgs[iArg] == '--vbox-vm-no-terminate':
2043 self.fVmNoTerminate = True;
2044 else:
2045 # Relevant for selecting VMs to test?
2046 if self.oTestVmSet is not None:
2047 iRc = self.oTestVmSet.parseOption(asArgs, iArg);
2048 if iRc != iArg:
2049 return iRc;
2050
2051 # Hand it to the base class.
2052 return base.TestDriver.parseOption(self, asArgs, iArg);
2053 return iArg + 1;
2054
2055 def completeOptions(self):
2056 return base.TestDriver.completeOptions(self);
2057
2058 def getNetworkAdapterNameFromType(self, oNic):
2059 """
2060 Returns the network adapter name from a given adapter type.
2061
2062 Returns an empty string if not found / invalid.
2063 """
2064 sAdpName = '';
2065 if oNic.adapterType in (vboxcon.NetworkAdapterType_Am79C970A, \
2066 vboxcon.NetworkAdapterType_Am79C973, \
2067 vboxcon.NetworkAdapterType_Am79C960):
2068 sAdpName = 'pcnet';
2069 elif oNic.adapterType in (vboxcon.NetworkAdapterType_I82540EM, \
2070 vboxcon.NetworkAdapterType_I82543GC, \
2071 vboxcon.NetworkAdapterType_I82545EM):
2072 sAdpName = 'e1000';
2073 elif oNic.adapterType == vboxcon.NetworkAdapterType_Virtio:
2074 sAdpName = 'virtio-net';
2075 return sAdpName;
2076
2077 def getResourceSet(self):
2078 asRsrcs = [];
2079 if self.oTestVmSet is not None:
2080 asRsrcs.extend(self.oTestVmSet.getResourceSet());
2081 asRsrcs.extend(base.TestDriver.getResourceSet(self));
2082 return asRsrcs;
2083
2084 def actionExtract(self):
2085 return base.TestDriver.actionExtract(self);
2086
2087 def actionVerify(self):
2088 return base.TestDriver.actionVerify(self);
2089
2090 def actionConfig(self):
2091 return base.TestDriver.actionConfig(self);
2092
2093 def actionExecute(self):
2094 return base.TestDriver.actionExecute(self);
2095
2096 def actionCleanupBefore(self):
2097 """
2098 Kill any VBoxSVC left behind by a previous test run.
2099 """
2100 self._killVBoxSVCByPidFile('%s/VBoxSVC.pid' % (self.sScratchPath,));
2101 return base.TestDriver.actionCleanupBefore(self);
2102
2103 def actionCleanupAfter(self):
2104 """
2105 Clean up the VBox bits and then call the base driver.
2106
2107 If your test driver overrides this, it should normally call us at the
2108 end of the job.
2109 """
2110 cErrorsEntry = reporter.getErrorCount();
2111
2112 # Kill any left over VM processes.
2113 self._powerOffAllVms();
2114
2115 # Drop all VBox object references and shutdown xpcom then
2116 # terminating VBoxSVC, with extreme prejudice if need be.
2117 self._teardownVBoxApi();
2118 self._stopVBoxSVC();
2119
2120 # Add the VBoxSVC and testdriver debug+release log files.
2121 if self.fAlwaysUploadLogs or reporter.getErrorCount() > 0:
2122 if self.sVBoxSvcLogFile is not None and os.path.isfile(self.sVBoxSvcLogFile):
2123 reporter.addLogFile(self.sVBoxSvcLogFile, 'log/debug/svc', 'Debug log file for VBoxSVC');
2124 self.sVBoxSvcLogFile = None;
2125
2126 if self.sSelfLogFile is not None and os.path.isfile(self.sSelfLogFile):
2127 reporter.addLogFile(self.sSelfLogFile, 'log/debug/client', 'Debug log file for the test driver');
2128 self.sSelfLogFile = None;
2129
2130 if self.sSessionLogFile is not None and os.path.isfile(self.sSessionLogFile):
2131 reporter.addLogFile(self.sSessionLogFile, 'log/debug/session', 'Debug log file for the VM session');
2132 self.sSessionLogFile = None;
2133
2134 sVBoxSvcRelLog = os.path.join(self.sScratchPath, 'VBoxUserHome', 'VBoxSVC.log');
2135 if os.path.isfile(sVBoxSvcRelLog):
2136 reporter.addLogFile(sVBoxSvcRelLog, 'log/release/svc', 'Release log file for VBoxSVC');
2137 for sSuff in [ '.1', '.2', '.3', '.4', '.5', '.6', '.7', '.8' ]:
2138 if os.path.isfile(sVBoxSvcRelLog + sSuff):
2139 reporter.addLogFile(sVBoxSvcRelLog + sSuff, 'log/release/svc', 'Release log file for VBoxSVC');
2140
2141 # Finally, call the base driver to wipe the scratch space.
2142 fRc = base.TestDriver.actionCleanupAfter(self);
2143
2144 # Flag failure if the error count increased.
2145 if reporter.getErrorCount() > cErrorsEntry:
2146 fRc = False;
2147 return fRc;
2148
2149
2150 def actionAbort(self):
2151 """
2152 Terminate VBoxSVC if we've got a pid file.
2153 """
2154 #
2155 # Take default action first, then kill VBoxSVC. The other way around
2156 # is problematic since the testscript would continue running and possibly
2157 # trigger a new VBoxSVC to start.
2158 #
2159 fRc1 = base.TestDriver.actionAbort(self);
2160 fRc2 = self._killVBoxSVCByPidFile('%s/VBoxSVC.pid' % (self.sScratchPath,));
2161 return fRc1 is True and fRc2 is True;
2162
2163 def onExit(self, iRc):
2164 """
2165 Stop VBoxSVC if we've started it.
2166 """
2167 if not self.fVmNoTerminate \
2168 and self.oVBoxSvcProcess is not None:
2169 reporter.log('*** Shutting down the VBox API... (iRc=%s)' % (iRc,));
2170 self._powerOffAllVms();
2171 self._teardownVBoxApi();
2172 self._stopVBoxSVC();
2173 reporter.log('*** VBox API shutdown done.');
2174 return base.TestDriver.onExit(self, iRc);
2175
2176
2177 #
2178 # Task wait method override.
2179 #
2180
2181 def notifyAboutReadyTask(self, oTask):
2182 """
2183 Overriding base.TestDriver.notifyAboutReadyTask.
2184 """
2185 try:
2186 self.oVBoxMgr.interruptWaitEvents();
2187 reporter.log2('vbox.notifyAboutReadyTask: called interruptWaitEvents');
2188 except:
2189 reporter.logXcpt('vbox.notifyAboutReadyTask');
2190 return base.TestDriver.notifyAboutReadyTask(self, oTask);
2191
2192 def waitForTasksSleepWorker(self, cMsTimeout):
2193 """
2194 Overriding base.TestDriver.waitForTasksSleepWorker.
2195 """
2196 try:
2197 rc = self.oVBoxMgr.waitForEvents(int(cMsTimeout));
2198 _ = rc; #reporter.log2('vbox.waitForTasksSleepWorker(%u): true (waitForEvents -> %s)' % (cMsTimeout, rc));
2199 reporter.doPollWork('vbox.TestDriver.waitForTasksSleepWorker');
2200 return True;
2201 except KeyboardInterrupt:
2202 raise;
2203 except:
2204 reporter.logXcpt('vbox.waitForTasksSleepWorker');
2205 return False;
2206
2207 #
2208 # Utility methods.
2209 #
2210
2211 def processEvents(self, cMsTimeout = 0):
2212 """
2213 Processes events, returning after the first batch has been processed
2214 or the time limit has been reached.
2215
2216 Only Ctrl-C exception, no return.
2217 """
2218 try:
2219 self.oVBoxMgr.waitForEvents(cMsTimeout);
2220 except KeyboardInterrupt:
2221 raise;
2222 except:
2223 pass;
2224 return None;
2225
2226 def processPendingEvents(self):
2227 """ processEvents(0) - no waiting. """
2228 return self.processEvents(0);
2229
2230 def sleep(self, cSecs):
2231 """
2232 Sleep for a specified amount of time, processing XPCOM events all the while.
2233 """
2234 cMsTimeout = long(cSecs * 1000);
2235 msStart = base.timestampMilli();
2236 self.processEvents(0);
2237 while True:
2238 cMsElapsed = base.timestampMilli() - msStart;
2239 if cMsElapsed > cMsTimeout:
2240 break;
2241 #reporter.log2('cMsTimeout=%s - cMsElapsed=%d => %s' % (cMsTimeout, cMsElapsed, cMsTimeout - cMsElapsed));
2242 self.processEvents(cMsTimeout - cMsElapsed);
2243 return None;
2244
2245 def _logVmInfoUnsafe(self, oVM): # pylint: disable=too-many-statements,too-many-branches
2246 """
2247 Internal worker for logVmInfo that is wrapped in try/except.
2248 """
2249 reporter.log(" Name: %s" % (oVM.name,));
2250 reporter.log(" ID: %s" % (oVM.id,));
2251 oOsType = self.oVBox.getGuestOSType(oVM.OSTypeId);
2252 if self.fpApiVer >= 7.1:
2253 if oVM.platform.architecture == vboxcon.PlatformArchitecture_ARM:
2254 sArch = 'ARM';
2255 else:
2256 sArch = 'x86';
2257 reporter.log(" Architecture: %s" % (sArch,));
2258 else: # x86 only.
2259 reporter.log(" Architecture: x86");
2260 reporter.log(" OS Type: %s - %s" % (oVM.OSTypeId, oOsType.description,));
2261 reporter.log(" Machine state: %s" % (oVM.state,));
2262 reporter.log(" Session state: %s" % (oVM.sessionState,));
2263 if self.fpApiVer >= 4.2:
2264 reporter.log(" Session PID: %u (%#x)" % (oVM.sessionPID, oVM.sessionPID,));
2265 else:
2266 reporter.log(" Session PID: %u (%#x)" % (oVM.sessionPid, oVM.sessionPid,));
2267 if self.fpApiVer >= 5.0:
2268 reporter.log(" Session Name: %s" % (oVM.sessionName,));
2269 else:
2270 reporter.log(" Session Name: %s" % (oVM.sessionType,));
2271 reporter.log(" CPUs: %s" % (oVM.CPUCount,));
2272 reporter.log(" RAM: %sMB" % (oVM.memorySize,));
2273 if self.fpApiVer >= 6.1 and hasattr(oVM, 'graphicsAdapter'):
2274 reporter.log(" VRAM: %sMB" % (oVM.graphicsAdapter.VRAMSize,));
2275 reporter.log(" Monitors: %s" % (oVM.graphicsAdapter.monitorCount,));
2276 reporter.log(" GraphicsController: %s"
2277 % (self.oVBoxMgr.getEnumValueName('GraphicsControllerType', # pylint: disable=not-callable
2278 oVM.graphicsAdapter.graphicsControllerType),));
2279 else:
2280 reporter.log(" VRAM: %sMB" % (oVM.VRAMSize,));
2281 reporter.log(" Monitors: %s" % (oVM.monitorCount,));
2282 reporter.log(" GraphicsController: %s"
2283 % (self.oVBoxMgr.getEnumValueName('GraphicsControllerType', oVM.graphicsControllerType),)); # pylint: disable=not-callable
2284 if self.fpApiVer >= 7.1:
2285 reporter.log(" Chipset: %s" % (self.oVBoxMgr.getEnumValueName('ChipsetType', oVM.platform.chipsetType),));# pylint: disable=not-callable
2286 if hasattr(vboxcon, 'IommuType_None'):
2287 reporter.log(" IOMMU: %s" % (self.oVBoxMgr.getEnumValueName('IommuType', oVM.platform.iommuType),));# pylint: disable=not-callable
2288 reporter.log(" Firmware: %s"
2289 % (self.oVBoxMgr.getEnumValueName('FirmwareType', oVM.firmwareSettings.firmwareType),)); # pylint: disable=not-callable
2290 if oVM.platform.architecture == vboxcon.PlatformArchitecture_x86:
2291 reporter.log(" HwVirtEx: %s"
2292 % (oVM.platform.x86.getHWVirtExProperty(vboxcon.HWVirtExPropertyType_Enabled),));
2293 reporter.log(" VPID support: %s"
2294 % (oVM.platform.x86.getHWVirtExProperty(vboxcon.HWVirtExPropertyType_VPID),));
2295 reporter.log(" Nested paging: %s"
2296 % (oVM.platform.x86.getHWVirtExProperty(vboxcon.HWVirtExPropertyType_NestedPaging),));
2297 else:
2298 reporter.log(" Chipset: %s" % (self.oVBoxMgr.getEnumValueName('ChipsetType', oVM.chipsetType),)); # pylint: disable=not-callable
2299 if self.fpApiVer >= 6.2 and hasattr(vboxcon, 'IommuType_None'):
2300 reporter.log(" IOMMU: %s" % (self.oVBoxMgr.getEnumValueName('IommuType', oVM.iommuType),)); # pylint: disable=not-callable
2301 reporter.log(" Firmware: %s" % (self.oVBoxMgr.getEnumValueName('FirmwareType', oVM.firmwareType),)); # pylint: disable=not-callable
2302 reporter.log(" HwVirtEx: %s" % (oVM.getHWVirtExProperty(vboxcon.HWVirtExPropertyType_Enabled),));
2303 reporter.log(" VPID support: %s" % (oVM.getHWVirtExProperty(vboxcon.HWVirtExPropertyType_VPID),));
2304 reporter.log(" Nested paging: %s" % (oVM.getHWVirtExProperty(vboxcon.HWVirtExPropertyType_NestedPaging),));
2305 atCpuPropertyTypesX86 = [
2306 ( 'PAE', 'PAE: '),
2307 ( 'LongMode', 'Long-mode: '),
2308 ( 'HWVirt', 'Nested VT-x/AMD-V: '),
2309 ( 'APIC', 'APIC: '),
2310 ( 'X2APIC', 'X2APIC: '),
2311 ( 'TripleFaultReset', 'TripleFaultReset: '),
2312 ( 'IBPBOnVMExit', 'IBPBOnVMExit: '),
2313 ( 'SpecCtrl', 'SpecCtrl: '),
2314 ( 'SpecCtrlByHost', 'SpecCtrlByHost: '),
2315 ## @todo r=andy Add missing properties.
2316 ];
2317 fnGetCpuPropertyX86 = None;
2318 if self.fpApiVer >= 7.1:
2319 sCpuPropertyTypeNamePrefix = 'CpuPropertyTypeX86_';
2320 if oVM.platform.architecture == vboxcon.PlatformArchitecture_x86:
2321 fnGetCpuPropertyX86 = oVM.platform.x86.getCPUProperty;
2322 else:
2323 sCpuPropertyTypeNamePrefix = 'CpuPropertyType_';
2324 fnGetCpuPropertyX86 = oVM.getCPUProperty;
2325 if fnGetCpuPropertyX86:
2326 for sEnumValue, sDesc in atCpuPropertyTypesX86:
2327 if hasattr(vboxcon, sCpuPropertyTypeNamePrefix + sEnumValue):
2328 reporter.log(" %s%s" % (sDesc, fnGetCpuPropertyX86(getattr(vboxcon, sEnumValue)),));
2329 if self.fpApiVer >= 7.1:
2330 reporter.log(" ACPI: %s" % (oVM.firmwareSettings.ACPIEnabled,));
2331 reporter.log(" IO-APIC: %s" % (oVM.firmwareSettings.IOAPICEnabled,));
2332 if oVM.platform.architecture == vboxcon.PlatformArchitecture_x86:
2333 reporter.log(" HPET: %s" % (oVM.platform.x86.HPETEnabled,));
2334 else:
2335 reporter.log(" ACPI: %s" % (oVM.BIOSSettings.ACPIEnabled,));
2336 reporter.log(" IO-APIC: %s" % (oVM.BIOSSettings.IOAPICEnabled,));
2337 if self.fpApiVer >= 3.2:
2338 if self.fpApiVer >= 4.2:
2339 reporter.log(" HPET: %s" % (oVM.HPETEnabled,));
2340 else:
2341 reporter.log(" HPET: %s" % (oVM.hpetEnabled,));
2342 if self.fpApiVer >= 6.1 and hasattr(oVM, 'graphicsAdapter'):
2343 reporter.log(" 3D acceleration: %s" % (oVM.graphicsAdapter.accelerate3DEnabled,));
2344 reporter.log(" 2D acceleration: %s" % (oVM.graphicsAdapter.accelerate2DVideoEnabled,));
2345 else:
2346 reporter.log(" 3D acceleration: %s" % (oVM.accelerate3DEnabled,));
2347 reporter.log(" 2D acceleration: %s" % (oVM.accelerate2DVideoEnabled,));
2348 reporter.log(" TeleporterEnabled: %s" % (oVM.teleporterEnabled,));
2349 reporter.log(" TeleporterPort: %s" % (oVM.teleporterPort,));
2350 reporter.log(" TeleporterAddress: %s" % (oVM.teleporterAddress,));
2351 reporter.log(" TeleporterPassword: %s" % (oVM.teleporterPassword,));
2352 reporter.log(" Clipboard mode: %s" % (oVM.clipboardMode,));
2353 if self.fpApiVer >= 5.0:
2354 reporter.log(" Drag and drop mode: %s" % (oVM.dnDMode,));
2355 elif self.fpApiVer >= 4.3:
2356 reporter.log(" Drag and drop mode: %s" % (oVM.dragAndDropMode,));
2357 if self.fpApiVer >= 4.0:
2358 reporter.log(" VRDP server: %s" % (oVM.VRDEServer.enabled,));
2359 try: sPorts = oVM.VRDEServer.getVRDEProperty("TCP/Ports");
2360 except: sPorts = "";
2361 reporter.log(" VRDP server ports: %s" % (sPorts,));
2362 reporter.log(" VRDP auth: %s (%s)" % (oVM.VRDEServer.authType, oVM.VRDEServer.authLibrary,));
2363 else:
2364 reporter.log(" VRDP server: %s" % (oVM.VRDPServer.enabled,));
2365 reporter.log(" VRDP server ports: %s" % (oVM.VRDPServer.ports,));
2366 reporter.log(" Last changed: %s" % (oVM.lastStateChange,));
2367
2368 aoControllers = self.oVBoxMgr.getArray(oVM, 'storageControllers')
2369 if aoControllers:
2370 reporter.log(" Controllers:");
2371 for oCtrl in aoControllers:
2372 reporter.log(" %s %s bus: %s type: %s" % (oCtrl.name, oCtrl.controllerType, oCtrl.bus, oCtrl.controllerType,));
2373 if self.fpApiVer >= 7.0:
2374 oAdapter = oVM.audioSettings.adapter;
2375 else:
2376 oAdapter = oVM.audioAdapter;
2377 reporter.log(" AudioController: %s"
2378 % (self.oVBoxMgr.getEnumValueName('AudioControllerType', oAdapter.audioController),)); # pylint: disable=not-callable
2379 reporter.log(" AudioEnabled: %s" % (oAdapter.enabled,));
2380 reporter.log(" Host AudioDriver: %s"
2381 % (self.oVBoxMgr.getEnumValueName('AudioDriverType', oAdapter.audioDriver),)); # pylint: disable=not-callable
2382
2383 self.processPendingEvents();
2384 aoAttachments = self.oVBoxMgr.getArray(oVM, 'mediumAttachments')
2385 if aoAttachments:
2386 reporter.log(" Attachments:");
2387 for oAtt in aoAttachments:
2388 sCtrl = "Controller: %s port: %s device: %s type: %s" % (oAtt.controller, oAtt.port, oAtt.device, oAtt.type);
2389 oMedium = oAtt.medium
2390 if oAtt.type == vboxcon.DeviceType_HardDisk:
2391 reporter.log(" %s: HDD" % sCtrl);
2392 reporter.log(" Id: %s" % (oMedium.id,));
2393 reporter.log(" Name: %s" % (oMedium.name,));
2394 reporter.log(" Format: %s" % (oMedium.format,));
2395 reporter.log(" Location: %s" % (oMedium.location,));
2396
2397 if oAtt.type == vboxcon.DeviceType_DVD:
2398 reporter.log(" %s: DVD" % sCtrl);
2399 if oMedium:
2400 reporter.log(" Id: %s" % (oMedium.id,));
2401 reporter.log(" Name: %s" % (oMedium.name,));
2402 if oMedium.hostDrive:
2403 reporter.log(" Host DVD %s" % (oMedium.location,));
2404 if oAtt.passthrough:
2405 reporter.log(" [passthrough mode]");
2406 else:
2407 reporter.log(" Virtual image: %s" % (oMedium.location,));
2408 reporter.log(" Size: %s" % (oMedium.size,));
2409 else:
2410 reporter.log(" empty");
2411
2412 if oAtt.type == vboxcon.DeviceType_Floppy:
2413 reporter.log(" %s: Floppy" % sCtrl);
2414 if oMedium:
2415 reporter.log(" Id: %s" % (oMedium.id,));
2416 reporter.log(" Name: %s" % (oMedium.name,));
2417 if oMedium.hostDrive:
2418 reporter.log(" Host floppy: %s" % (oMedium.location,));
2419 else:
2420 reporter.log(" Virtual image: %s" % (oMedium.location,));
2421 reporter.log(" Size: %s" % (oMedium.size,));
2422 else:
2423 reporter.log(" empty");
2424 self.processPendingEvents();
2425
2426 reporter.log(" Network Adapter:");
2427 for iSlot in range(0, 32):
2428 try: oNic = oVM.getNetworkAdapter(iSlot)
2429 except: break;
2430 if not oNic.enabled:
2431 reporter.log2(" slot #%d found but not enabled, skipping" % (iSlot,));
2432 continue;
2433 reporter.log(" slot #%d: type: %s (%s) MAC Address: %s lineSpeed: %s"
2434 % (iSlot, self.oVBoxMgr.getEnumValueName('NetworkAdapterType', oNic.adapterType), # pylint: disable=not-callable
2435 oNic.adapterType, oNic.MACAddress, oNic.lineSpeed) );
2436
2437 if oNic.attachmentType == vboxcon.NetworkAttachmentType_NAT:
2438 reporter.log(" attachmentType: NAT (%s)" % (oNic.attachmentType,));
2439 if self.fpApiVer >= 4.1:
2440 reporter.log(" nat-network: %s" % (oNic.NATNetwork,));
2441 if self.fpApiVer >= 7.0 and hasattr(oNic.NATEngine, 'localhostReachable'):
2442 reporter.log(" localhostReachable: %s" % (oNic.NATEngine.localhostReachable,));
2443
2444 elif oNic.attachmentType == vboxcon.NetworkAttachmentType_Bridged:
2445 reporter.log(" attachmentType: Bridged (%s)" % (oNic.attachmentType,));
2446 if self.fpApiVer >= 4.1:
2447 reporter.log(" hostInterface: %s" % (oNic.bridgedInterface,));
2448 else:
2449 reporter.log(" hostInterface: %s" % (oNic.hostInterface,));
2450 elif oNic.attachmentType == vboxcon.NetworkAttachmentType_Internal:
2451 reporter.log(" attachmentType: Internal (%s)" % (oNic.attachmentType,));
2452 reporter.log(" intnet-name: %s" % (oNic.internalNetwork,));
2453 elif oNic.attachmentType == vboxcon.NetworkAttachmentType_HostOnly:
2454 reporter.log(" attachmentType: HostOnly (%s)" % (oNic.attachmentType,));
2455 if self.fpApiVer >= 4.1:
2456 reporter.log(" hostInterface: %s" % (oNic.hostOnlyInterface,));
2457 else:
2458 reporter.log(" hostInterface: %s" % (oNic.hostInterface,));
2459 else:
2460 if self.fpApiVer >= 7.0:
2461 if oNic.attachmentType == vboxcon.NetworkAttachmentType_HostOnlyNetwork:
2462 reporter.log(" attachmentType: HostOnlyNetwork (%s)" % (oNic.attachmentType,));
2463 reporter.log(" hostonly-net: %s" % (oNic.hostOnlyNetwork,));
2464 elif self.fpApiVer >= 4.1:
2465 if oNic.attachmentType == vboxcon.NetworkAttachmentType_Generic:
2466 reporter.log(" attachmentType: Generic (%s)" % (oNic.attachmentType,));
2467 reporter.log(" generic-driver: %s" % (oNic.GenericDriver,));
2468 else:
2469 reporter.log(" attachmentType: unknown-%s" % (oNic.attachmentType,));
2470 else:
2471 reporter.log(" attachmentType: unknown-%s" % (oNic.attachmentType,));
2472 if oNic.traceEnabled:
2473 reporter.log(" traceFile: %s" % (oNic.traceFile,));
2474 self.processPendingEvents();
2475
2476 reporter.log(" Serial ports:");
2477 for iSlot in range(0, 8):
2478 try: oPort = oVM.getSerialPort(iSlot)
2479 except: break;
2480 if oPort is not None and oPort.enabled:
2481 enmHostMode = oPort.hostMode;
2482 reporter.log(" slot #%d: hostMode: %s (%s) I/O port: %s IRQ: %s server: %s path: %s" %
2483 (iSlot, self.oVBoxMgr.getEnumValueName('PortMode', enmHostMode), # pylint: disable=not-callable
2484 enmHostMode, oPort.IOBase, oPort.IRQ, oPort.server, oPort.path,) );
2485 self.processPendingEvents();
2486
2487 return True;
2488
2489 def logVmInfo(self, oVM): # pylint: disable=too-many-statements,too-many-branches
2490 """
2491 Logs VM configuration details.
2492
2493 This is copy, past, search, replace and edit of infoCmd from vboxshell.py.
2494 """
2495 try:
2496 fRc = self._logVmInfoUnsafe(oVM);
2497 except:
2498 reporter.logXcpt();
2499 fRc = False;
2500 return fRc;
2501
2502 def logVmInfoByName(self, sName):
2503 """
2504 logVmInfo + getVmByName.
2505 """
2506 return self.logVmInfo(self.getVmByName(sName));
2507
2508 def tryFindGuestOsId(self, sIdOrDesc):
2509 """
2510 Takes a guest OS ID or Description and returns the ID.
2511 If nothing matching it is found, the input is returned unmodified.
2512 """
2513
2514 if self.fpApiVer >= 4.0:
2515 if sIdOrDesc == 'Solaris (64 bit)':
2516 sIdOrDesc = 'Oracle Solaris 10 5/09 and earlier (64 bit)';
2517
2518 try:
2519 aoGuestTypes = self.oVBoxMgr.getArray(self.oVBox, 'GuestOSTypes');
2520 except:
2521 reporter.logXcpt();
2522 else:
2523 for oGuestOS in aoGuestTypes:
2524 try:
2525 sId = oGuestOS.id;
2526 sDesc = oGuestOS.description;
2527 except:
2528 reporter.logXcpt();
2529 else:
2530 if sIdOrDesc in (sId, sDesc,):
2531 sIdOrDesc = sId;
2532 break;
2533 self.processPendingEvents();
2534 return sIdOrDesc
2535
2536 def resourceFindVmHd(self, sVmName, sFlavor):
2537 """
2538 Search the test resources for the most recent VM HD.
2539
2540 Returns path relative to the test resource root.
2541 """
2542 ## @todo implement a proper search algo here.
2543 return '4.2/' + sFlavor + '/' + sVmName + '/t-' + sVmName + '.vdi';
2544
2545
2546 #
2547 # VM Api wrappers that logs errors, hides exceptions and other details.
2548 #
2549
2550 def createTestVMOnly(self, sName, sKind, sPlatformArchitecture = 'x86'):
2551 """
2552 Creates and registers a test VM without doing any kind of configuration.
2553 Uses x86 as a platform architecture by default (only >= 7.1).
2554
2555 Returns VM object (IMachine) on success, None on failure.
2556 """
2557 if not self.importVBoxApi():
2558 return None;
2559
2560 # Default to x86 if not explicitly specified.
2561 if self.fpApiVer >= 7.1:
2562 if not sPlatformArchitecture \
2563 or sPlatformArchitecture == 'x86':
2564 enmPlatformArchitecture = vboxcon.PlatformArchitecture_x86;
2565 elif sPlatformArchitecture == 'ARM':
2566 enmPlatformArchitecture = vboxcon.PlatformArchitecture_ARM;
2567 else:
2568 reporter.error('Unkown platform architecture "%s"' % (sPlatformArchitecture,));
2569 return None;
2570 elif sPlatformArchitecture != 'x86': # < 7.1 only has x86 support.
2571 reporter.errorXcpt('This host version of VirtualBox only supports x86 as platform architecture');
2572 return None;
2573
2574 # create + register the VM
2575 try:
2576 if self.fpApiVer >= 7.1: # Introduces platform support (x86 + ARM).
2577 oVM = self.oVBox.createMachine("", sName, enmPlatformArchitecture, [], self.tryFindGuestOsId(sKind), \
2578 "", "", "", "");
2579 elif self.fpApiVer >= 7.0: # Introduces VM encryption (three new parameters, empty for now).
2580 oVM = self.oVBox.createMachine("", sName, [], self.tryFindGuestOsId(sKind), "", "", "", "");
2581 elif self.fpApiVer >= 4.2: # Introduces grouping (third parameter, empty for now).
2582 oVM = self.oVBox.createMachine("", sName, [], self.tryFindGuestOsId(sKind), "");
2583 elif self.fpApiVer >= 4.0:
2584 oVM = self.oVBox.createMachine("", sName, self.tryFindGuestOsId(sKind), "", False);
2585 elif self.fpApiVer >= 3.2:
2586 oVM = self.oVBox.createMachine(sName, self.tryFindGuestOsId(sKind), "", "", False);
2587 else:
2588 oVM = self.oVBox.createMachine(sName, self.tryFindGuestOsId(sKind), "", "");
2589 try:
2590 oVM.saveSettings();
2591 try:
2592 self.oVBox.registerMachine(oVM);
2593 return oVM;
2594 except:
2595 reporter.logXcpt();
2596 raise;
2597 except:
2598 reporter.logXcpt();
2599 if self.fpApiVer >= 4.0:
2600 try:
2601 if self.fpApiVer >= 4.3:
2602 oProgress = oVM.deleteConfig([]);
2603 else:
2604 oProgress = oVM.delete(None);
2605 self.waitOnProgress(oProgress);
2606 except:
2607 reporter.logXcpt();
2608 else:
2609 try: oVM.deleteSettings();
2610 except: reporter.logXcpt();
2611 raise;
2612 except:
2613 reporter.errorXcpt('failed to create vm "%s"' % (sName));
2614 return None;
2615
2616 # pylint: disable=too-many-arguments,too-many-locals,too-many-statements,too-many-branches
2617 def createTestVM(self,
2618 sName,
2619 iGroup,
2620 sHd = None,
2621 cMbRam = None,
2622 cCpus = 1,
2623 fVirtEx = None,
2624 fNestedPaging = None,
2625 sDvdImage = None,
2626 sKind = "Other",
2627 fIoApic = None,
2628 fNstHwVirt = None,
2629 fPae = None,
2630 fFastBootLogo = True,
2631 eNic0Type = None,
2632 eNic0AttachType = None,
2633 sNic0NetName = 'default',
2634 sNic0MacAddr = 'grouped',
2635 sFloppy = None,
2636 fNatForwardingForTxs = None,
2637 sHddControllerType = 'IDE Controller',
2638 fVmmDevTestingPart = None,
2639 fVmmDevTestingMmio = False,
2640 sFirmwareType = 'bios',
2641 sChipsetType = 'piix3',
2642 sIommuType = 'none',
2643 sDvdControllerType = 'IDE Controller',
2644 sCom1RawFile = None,
2645 fSecureBoot = False,
2646 sUefiMokPathPrefix = None,
2647 sGraphicsControllerType = None,
2648 sPlatformArchitecture = 'x86'):
2649 """
2650 Creates a test VM with a immutable HD from the test resources.
2651 """
2652 # create + register the VM
2653 oVM = self.createTestVMOnly(sName, sKind, sPlatformArchitecture);
2654 if not oVM:
2655 return None;
2656
2657 # Configure the VM.
2658 fRc = True;
2659 oSession = self.openSession(oVM);
2660 if oSession is not None:
2661 fRc = oSession.setupPreferredConfig();
2662
2663 if fRc and cMbRam is not None :
2664 fRc = oSession.setRamSize(cMbRam);
2665 if fRc and cCpus is not None:
2666 fRc = oSession.setCpuCount(cCpus);
2667 if fRc and sDvdImage is not None:
2668 fRc = oSession.attachDvd(sDvdImage, sDvdControllerType);
2669 if fRc and sHd is not None:
2670 fRc = oSession.attachHd(sHd, sHddControllerType);
2671 if fRc and sFloppy is not None:
2672 fRc = oSession.attachFloppy(sFloppy);
2673 if fRc and eNic0Type is not None:
2674 fRc = oSession.setNicType(eNic0Type, 0);
2675 if fRc and (eNic0AttachType is not None or (sNic0NetName is not None and sNic0NetName != 'default')):
2676 fRc = oSession.setNicAttachment(eNic0AttachType, sNic0NetName, 0);
2677 if fRc and sNic0MacAddr is not None:
2678 if sNic0MacAddr == 'grouped':
2679 sNic0MacAddr = '%02X' % (iGroup);
2680 fRc = oSession.setNicMacAddress(sNic0MacAddr, 0);
2681 # Needed to reach the host (localhost) from the guest. See xTracker #9896.
2682 if fRc and self.fpApiVer >= 7.0:
2683 fRc = oSession.setNicLocalhostReachable(True, 0);
2684 if fRc and fNatForwardingForTxs is True:
2685 fRc = oSession.setupNatForwardingForTxs();
2686 if fRc and fFastBootLogo is not None:
2687 fRc = oSession.setupBootLogo(fFastBootLogo);
2688 if fRc and self.fEnableVrdp:
2689 fRc = oSession.setupVrdp(True, self.uVrdpBasePort + iGroup);
2690 if fRc and fVmmDevTestingPart is not None:
2691 fRc = oSession.enableVmmDevTestingPart(fVmmDevTestingPart, fVmmDevTestingMmio);
2692 if fRc and sFirmwareType == 'bios':
2693 fRc = oSession.setFirmwareType(vboxcon.FirmwareType_BIOS);
2694 elif fRc and sFirmwareType == 'efi':
2695 fRc = oSession.setFirmwareType(vboxcon.FirmwareType_EFI);
2696 if fRc and self.fpApiVer >= 7.0 and fSecureBoot:
2697 fRc = oSession.enableSecureBoot(fSecureBoot, sUefiMokPathPrefix);
2698 if fRc and self.fEnableDebugger:
2699 fRc = oSession.setExtraData('VBoxInternal/DBGC/Enabled', '1');
2700 if fRc and self.fRecordingEnabled:
2701 try:
2702 if self.fpApiVer >= 6.1: # Only for VBox 6.1 and up now.
2703 reporter.log('Recording enabled');
2704 if self.cSecsRecordingMax > 0:
2705 reporter.log('Recording time limit is set to %d seconds' % (self.cSecsRecordingMax));
2706 if self.cMbRecordingMax > 0:
2707 reporter.log('Recording file limit is set to %d MB' % (self.cMbRecordingMax));
2708 oRecSettings = oSession.o.machine.recordingSettings;
2709 oRecSettings.enabled = True;
2710 aoScreens = self.oVBoxMgr.getArray(oRecSettings, 'screens');
2711 for oScreen in aoScreens:
2712 try:
2713 oScreen.enabled = True;
2714 sRecFile = os.path.join(self.sScratchPath, "recording-%s.webm" % (sName));
2715 oScreen.filename = sRecFile;
2716 sRecFile = oScreen.filename; # Get back the file from Main, in case it was modified somehow.
2717 dRecFile = { 'id' : oScreen.id, 'file' : sRecFile };
2718 self.adRecordingFiles.append(dRecFile);
2719 if self.fpApiVer >= 7.0:
2720 aFeatures = [ vboxcon.RecordingFeature_Video, ];
2721 if self.fRecordingAudio:
2722 aFeatures.append(vboxcon.RecordingFeature_Audio);
2723 try:
2724 oScreen.setFeatures(aFeatures);
2725 except: ## @todo Figure out why this is needed on Windows.
2726 oScreen.features = aFeatures;
2727 else: # <= VBox 6.1 the feature were kept as a ULONG.
2728 uFeatures = vboxcon.RecordingFeature_Video;
2729 if self.fRecordingAudio:
2730 uFeatures = uFeatures | vboxcon.RecordingFeature_Audio;
2731 oScreen.features = uFeatures;
2732 reporter.log2('Recording screen %d to "%s"' % (dRecFile['id'], dRecFile['file'],));
2733 oScreen.maxTime = self.cSecsRecordingMax;
2734 oScreen.maxFileSize = self.cMbRecordingMax;
2735 except:
2736 reporter.errorXcpt('failed to configure recording for "%s" (screen %d)' % (sName, oScreen.id));
2737 else:
2738 # Not fatal.
2739 reporter.log('Recording only available for VBox >= 6.1, sorry!')
2740 except:
2741 reporter.errorXcpt('failed to configure recording for "%s"' % (sName));
2742 if fRc:
2743 if sChipsetType == 'piix3':
2744 fRc = oSession.setChipsetType(vboxcon.ChipsetType_PIIX3);
2745 elif sChipsetType == 'ich9':
2746 fRc = oSession.setChipsetType(vboxcon.ChipsetType_ICH9);
2747 elif self.fpApiVer >= 7.1 \
2748 and sChipsetType == 'armv8virtual': # ARM only is for VBox >= 7.1.
2749 fRc = oSession.setChipsetType(vboxcon.ChipsetType_ARMv8Virtual);
2750 else: # This is fatal.
2751 reporter.log('Unknown chipset type "%s" specified' % (sChipsetType,));
2752 if fRc and sCom1RawFile:
2753 fRc = oSession.setupSerialToRawFile(0, sCom1RawFile);
2754 if fRc and self.fpApiVer >= 6.2 and hasattr(vboxcon, 'IommuType_AMD') and sIommuType == 'amd':
2755 fRc = oSession.setIommuType(vboxcon.IommuType_AMD);
2756 elif fRc and self.fpApiVer >= 6.2 and hasattr(vboxcon, 'IommuType_Intel') and sIommuType == 'intel':
2757 fRc = oSession.setIommuType(vboxcon.IommuType_Intel);
2758 if fRc and sGraphicsControllerType is not None:
2759 if sGraphicsControllerType == 'VBoxSVGA':
2760 fRc = oSession.setVideoControllerType(vboxcon.GraphicsControllerType_VBoxSVGA);
2761 elif sGraphicsControllerType == 'VMSVGA':
2762 fRc = oSession.setVideoControllerType(vboxcon.GraphicsControllerType_VMSVGA);
2763 elif sGraphicsControllerType == 'VBoxVGA':
2764 fRc = oSession.setVideoControllerType(vboxcon.GraphicsControllerType_VBoxVGA);
2765 elif sGraphicsControllerType == 'QemuRamFb':
2766 fRc = oSession.setVideoControllerType(vboxcon.GraphicsControllerType_QemuRamFB);
2767
2768 #
2769 # x86-specifics.
2770 #
2771 if sPlatformArchitecture == 'x86':
2772 if fRc and fVirtEx is not None:
2773 fRc = oSession.enableVirtExX86(fVirtEx);
2774 if fRc and fNestedPaging is not None:
2775 fRc = oSession.enableNestedPagingX86(fNestedPaging);
2776 if fRc and fIoApic is not None:
2777 fRc = oSession.enableIoApic(fIoApic);
2778 if fRc and fNstHwVirt is not None:
2779 fRc = oSession.enableNestedHwVirtX86(fNstHwVirt);
2780 if fRc and fPae is not None:
2781 fRc = oSession.enablePaeX86(fPae);
2782
2783 #
2784 # ARM-specifics.
2785 #
2786 elif sPlatformArchitecture == 'ARM':
2787 pass; ## @todo BUGBUG Add stuff for ARM here.
2788
2789 if fRc: fRc = oSession.saveSettings();
2790 if not fRc: oSession.discardSettings(True);
2791 oSession.close();
2792 if not fRc:
2793 if self.fpApiVer >= 4.0:
2794 try: oVM.unregister(vboxcon.CleanupMode_Full);
2795 except: reporter.logXcpt();
2796 try:
2797 if self.fpApiVer >= 4.3:
2798 oProgress = oVM.deleteConfig([]);
2799 else:
2800 oProgress = oVM.delete([]);
2801 self.waitOnProgress(oProgress);
2802 except:
2803 reporter.logXcpt();
2804 else:
2805 try: self.oVBox.unregisterMachine(oVM.id);
2806 except: reporter.logXcpt();
2807 try: oVM.deleteSettings();
2808 except: reporter.logXcpt();
2809 return None;
2810
2811 # success.
2812 reporter.log('created "%s" with name "%s"' % (oVM.id, sName));
2813 self.aoVMs.append(oVM);
2814 self.logVmInfo(oVM); # testing...
2815 return oVM;
2816 # pylint: enable=too-many-arguments,too-many-locals,too-many-statements
2817
2818 def createTestVmWithDefaults(self, # pylint: disable=too-many-arguments
2819 sName,
2820 iGroup,
2821 sKind,
2822 sPlatformArchitecture = 'x86',
2823 sDvdImage = None,
2824 fFastBootLogo = True,
2825 eNic0AttachType = None,
2826 sNic0NetName = 'default',
2827 sNic0MacAddr = 'grouped',
2828 fVmmDevTestingPart = None,
2829 fVmmDevTestingMmio = False,
2830 sCom1RawFile = None):
2831 """
2832 Creates a test VM with all defaults and no HDs.
2833
2834 Defaults to the x86 platform architecture if not explicitly specified otherwise.
2835 """
2836 # create + register the VM
2837 oVM = self.createTestVMOnly(sName, sKind, sPlatformArchitecture);
2838 if oVM is not None:
2839 # Configure the VM with defaults according to sKind.
2840 fRc = True;
2841 oSession = self.openSession(oVM);
2842 if oSession is not None:
2843 if self.fpApiVer >= 6.0:
2844 try:
2845 oSession.o.machine.applyDefaults('');
2846 except:
2847 reporter.errorXcpt('failed to apply defaults to vm "%s"' % (sName,));
2848 fRc = False;
2849 else:
2850 reporter.error("Implement applyDefaults for vbox version %s" % (self.fpApiVer,));
2851 #fRc = oSession.setupPreferredConfig();
2852 fRc = False;
2853
2854 # Apply the specified configuration:
2855 if fRc and sDvdImage is not None:
2856 #fRc = oSession.insertDvd(sDvdImage); # attachDvd
2857 reporter.error('Implement: oSession.insertDvd(%s)' % (sDvdImage,));
2858 fRc = False;
2859
2860 if fRc and fFastBootLogo is not None:
2861 fRc = oSession.setupBootLogo(fFastBootLogo);
2862
2863 if fRc and (eNic0AttachType is not None or (sNic0NetName is not None and sNic0NetName != 'default')):
2864 fRc = oSession.setNicAttachment(eNic0AttachType, sNic0NetName, 0);
2865 if fRc and sNic0MacAddr is not None:
2866 if sNic0MacAddr == 'grouped':
2867 sNic0MacAddr = '%02X' % (iGroup,);
2868 fRc = oSession.setNicMacAddress(sNic0MacAddr, 0);
2869 # Needed to reach the host (localhost) from the guest. See xTracker #9896.
2870 if fRc and self.fpApiVer >= 7.0:
2871 fRc = oSession.setNicLocalhostReachable(True, 0);
2872
2873 if fRc and self.fEnableVrdp:
2874 fRc = oSession.setupVrdp(True, self.uVrdpBasePort + iGroup);
2875
2876 if fRc and fVmmDevTestingPart is not None:
2877 fRc = oSession.enableVmmDevTestingPart(fVmmDevTestingPart, fVmmDevTestingMmio);
2878
2879 if fRc and sCom1RawFile:
2880 fRc = oSession.setupSerialToRawFile(0, sCom1RawFile);
2881
2882 # Save the settings if we were successfull, otherwise discard them.
2883 if fRc:
2884 fRc = oSession.saveSettings();
2885 if not fRc:
2886 oSession.discardSettings(True);
2887 oSession.close();
2888
2889 if fRc is True:
2890 # If we've been successful, add the VM to the list and return it.
2891 # success.
2892 reporter.log('created "%s" with name "%s"' % (oVM.id, sName, ));
2893 self.aoVMs.append(oVM);
2894 self.logVmInfo(oVM); # testing...
2895 return oVM;
2896
2897 # Failed. Unregister the machine and delete it.
2898 if self.fpApiVer >= 4.0:
2899 try: oVM.unregister(vboxcon.CleanupMode_Full);
2900 except: reporter.logXcpt();
2901 try:
2902 if self.fpApiVer >= 4.3:
2903 oProgress = oVM.deleteConfig([]);
2904 else:
2905 oProgress = oVM.delete([]);
2906 self.waitOnProgress(oProgress);
2907 except:
2908 reporter.logXcpt();
2909 else:
2910 try: self.oVBox.unregisterMachine(oVM.id);
2911 except: reporter.logXcpt();
2912 try: oVM.deleteSettings();
2913 except: reporter.logXcpt();
2914 return None;
2915
2916 def addTestMachine(self, sNameOrId, fQuiet = False):
2917 """
2918 Adds an already existing (that is, configured) test VM to the
2919 test VM list.
2920
2921 Returns the VM object on success, None if failed.
2922 """
2923 # find + add the VM to the list.
2924 oVM = None;
2925 try:
2926 if self.fpApiVer >= 4.0:
2927 oVM = self.oVBox.findMachine(sNameOrId);
2928 else:
2929 reporter.error('fpApiVer=%s - did you remember to initialize the API' % (self.fpApiVer,));
2930 except:
2931 reporter.errorXcpt('could not find vm "%s"' % (sNameOrId,));
2932
2933 if oVM:
2934 self.aoVMs.append(oVM);
2935 if not fQuiet:
2936 reporter.log('Added "%s" with name "%s"' % (oVM.id, sNameOrId));
2937 self.logVmInfo(oVM);
2938 return oVM;
2939
2940 def forgetTestMachine(self, oVM, fQuiet = False):
2941 """
2942 Forget about an already known test VM in the test VM list.
2943
2944 Returns True on success, False if failed.
2945 """
2946 try:
2947 sUuid = oVM.id;
2948 sName = oVM.name;
2949 except:
2950 reporter.errorXcpt('failed to get the UUID for VM "%s"' % (oVM,));
2951 return False;
2952 try:
2953 self.aoVMs.remove(oVM);
2954 if not fQuiet:
2955 reporter.log('Removed "%s" with name "%s"' % (sUuid, sName));
2956 except:
2957 reporter.errorXcpt('could not find vm "%s"' % (sName,));
2958 return False;
2959 return True;
2960
2961 def openSession(self, oVM):
2962 """
2963 Opens a session for the VM. Returns the a Session wrapper object that
2964 will automatically close the session when the wrapper goes out of scope.
2965
2966 On failure None is returned and an error is logged.
2967 """
2968 try:
2969 sUuid = oVM.id;
2970 except:
2971 reporter.errorXcpt('failed to get the UUID for VM "%s"' % (oVM,));
2972 return None;
2973
2974 # This loop is a kludge to deal with us racing the closing of the
2975 # direct session of a previous VM run. See waitOnDirectSessionClose.
2976 for i in range(10):
2977 try:
2978 if self.fpApiVer <= 3.2:
2979 oSession = self.oVBoxMgr.openMachineSession(sUuid);
2980 else:
2981 oSession = self.oVBoxMgr.openMachineSession(oVM);
2982 break;
2983 except:
2984 if i == 9:
2985 reporter.errorXcpt('failed to open session for "%s" ("%s")' % (sUuid, oVM));
2986 return None;
2987 if i > 0:
2988 reporter.logXcpt('warning: failed to open session for "%s" ("%s") - retrying in %u secs' % (sUuid, oVM, i));
2989 self.waitOnDirectSessionClose(oVM, 5000 + i * 1000);
2990 from testdriver.vboxwrappers import SessionWrapper;
2991 return SessionWrapper(oSession, oVM, self.oVBox, self.oVBoxMgr, self, False);
2992
2993 #
2994 # Guest locations.
2995 #
2996
2997 @staticmethod
2998 def getGuestTempDir(oTestVm):
2999 """
3000 Helper for finding a temporary directory in the test VM.
3001
3002 Note! It may be necessary to create it!
3003 """
3004 if oTestVm.isWindows():
3005 return "C:\\Temp";
3006 if oTestVm.isOS2():
3007 return "C:\\Temp";
3008 return '/var/tmp';
3009
3010 @staticmethod
3011 def getGuestSystemDir(oTestVm, sPathPrefix = ''):
3012 """
3013 Helper for finding a system directory in the test VM that we can play around with.
3014 sPathPrefix can be used to specify other directories, such as /usr/local/bin/ or /usr/bin, for instance.
3015
3016 On Windows this is always the System32 directory, so this function can be used as
3017 basis for locating other files in or under that directory.
3018 """
3019 if oTestVm.isWindows():
3020 return oTestVm.pathJoin(TestDriver.getGuestWinDir(oTestVm), 'System32');
3021 if oTestVm.isOS2():
3022 return 'C:\\OS2\\DLL';
3023
3024 # OL / RHEL symlinks "/bin"/ to "/usr/bin". To avoid (unexpectedly) following symlinks, use "/usr/bin" then instead.
3025 if not sPathPrefix \
3026 and oTestVm.sKind in ('Oracle_64', 'Oracle'): ## @todo Does this apply for "RedHat" as well?
3027 return "/usr/bin";
3028
3029 return sPathPrefix + "/bin";
3030
3031 @staticmethod
3032 def getGuestSystemAdminDir(oTestVm, sPathPrefix = ''):
3033 """
3034 Helper for finding a system admin directory ("sbin") in the test VM that we can play around with.
3035 sPathPrefix can be used to specify other directories, such as /usr/local/sbin/ or /usr/sbin, for instance.
3036
3037 On Windows this is always the System32 directory, so this function can be used as
3038 basis for locating other files in or under that directory.
3039 On UNIX-y systems this always is the "sh" shell to guarantee a common shell syntax.
3040 """
3041 if oTestVm.isWindows():
3042 return oTestVm.pathJoin(TestDriver.getGuestWinDir(oTestVm), 'System32');
3043 if oTestVm.isOS2():
3044 return 'C:\\OS2\\DLL'; ## @todo r=andy Not sure here.
3045
3046 # OL / RHEL symlinks "/sbin"/ to "/usr/sbin". To avoid (unexpectedly) following symlinks, use "/usr/sbin" then instead.
3047 if not sPathPrefix \
3048 and oTestVm.sKind in ('Oracle_64', 'Oracle'): ## @todo Does this apply for "RedHat" as well?
3049 return "/usr/sbin";
3050
3051 return sPathPrefix + "/sbin";
3052
3053 @staticmethod
3054 def getGuestWinDir(oTestVm):
3055 """
3056 Helper for finding the Windows directory in the test VM that we can play around with.
3057 ASSUMES that we always install Windows on drive C.
3058
3059 Returns the Windows directory, or an empty string when executed on a non-Windows guest (asserts).
3060 """
3061 sWinDir = '';
3062 if oTestVm.isWindows():
3063 if oTestVm.sKind in ['WindowsNT4', 'WindowsNT3x', 'Windows2000',]:
3064 sWinDir = 'C:\\WinNT\\';
3065 else:
3066 sWinDir = 'C:\\Windows\\';
3067 assert sWinDir != '', 'Retrieving Windows directory for non-Windows OS';
3068 return sWinDir;
3069
3070 @staticmethod
3071 def getGuestSystemShell(oTestVm):
3072 """
3073 Helper for finding the default system shell in the test VM.
3074 """
3075 if oTestVm.isWindows():
3076 return TestDriver.getGuestSystemDir(oTestVm) + '\\cmd.exe';
3077 if oTestVm.isOS2():
3078 return TestDriver.getGuestSystemDir(oTestVm) + '\\..\\CMD.EXE';
3079 return "/bin/sh";
3080
3081 @staticmethod
3082 def getGuestSystemFileForReading(oTestVm):
3083 """
3084 Helper for finding a file in the test VM that we can read.
3085 """
3086 if oTestVm.isWindows():
3087 return TestDriver.getGuestSystemDir(oTestVm) + '\\ntdll.dll';
3088 if oTestVm.isOS2():
3089 return TestDriver.getGuestSystemDir(oTestVm) + '\\DOSCALL1.DLL';
3090 return "/bin/sh";
3091
3092 def getVmByName(self, sName):
3093 """
3094 Get a test VM by name. Returns None if not found, logged.
3095 """
3096 # Look it up in our 'cache'.
3097 for oVM in self.aoVMs:
3098 try:
3099 #reporter.log2('cur: %s / %s (oVM=%s)' % (oVM.name, oVM.id, oVM));
3100 if oVM.name == sName:
3101 return oVM;
3102 except:
3103 reporter.errorXcpt('failed to get the name from the VM "%s"' % (oVM));
3104
3105 # Look it up the standard way.
3106 return self.addTestMachine(sName, fQuiet = True);
3107
3108 def getVmByUuid(self, sUuid):
3109 """
3110 Get a test VM by uuid. Returns None if not found, logged.
3111 """
3112 # Look it up in our 'cache'.
3113 for oVM in self.aoVMs:
3114 try:
3115 if oVM.id == sUuid:
3116 return oVM;
3117 except:
3118 reporter.errorXcpt('failed to get the UUID from the VM "%s"' % (oVM));
3119
3120 # Look it up the standard way.
3121 return self.addTestMachine(sUuid, fQuiet = True);
3122
3123 def waitOnProgress(self, oProgress, cMsTimeout = 1000000, fErrorOnTimeout = True, cMsInterval = 1000):
3124 """
3125 Waits for a progress object to complete. Returns the status code.
3126 """
3127 # Wait for progress no longer than cMsTimeout time period.
3128 tsStart = datetime.datetime.now()
3129 while True:
3130 self.processPendingEvents();
3131 try:
3132 if oProgress.completed:
3133 break;
3134 except:
3135 return -1;
3136 self.processPendingEvents();
3137
3138 tsNow = datetime.datetime.now()
3139 tsDelta = tsNow - tsStart
3140 if ((tsDelta.microseconds + tsDelta.seconds * 1000000) // 1000) > cMsTimeout:
3141 if fErrorOnTimeout:
3142 reporter.errorTimeout('Timeout while waiting for progress.')
3143 return -1
3144
3145 reporter.doPollWork('vbox.TestDriver.waitOnProgress');
3146 try: oProgress.waitForCompletion(cMsInterval);
3147 except: return -2;
3148
3149 try: rc = oProgress.resultCode;
3150 except: rc = -2;
3151 self.processPendingEvents();
3152 return rc;
3153
3154 def waitOnDirectSessionClose(self, oVM, cMsTimeout):
3155 """
3156 Waits for the VM process to close it's current direct session.
3157
3158 Returns None.
3159 """
3160 # Get the original values so we're not subject to
3161 try:
3162 eCurState = oVM.sessionState;
3163 if self.fpApiVer >= 5.0:
3164 sCurName = sOrgName = oVM.sessionName;
3165 else:
3166 sCurName = sOrgName = oVM.sessionType;
3167 if self.fpApiVer >= 4.2:
3168 iCurPid = iOrgPid = oVM.sessionPID;
3169 else:
3170 iCurPid = iOrgPid = oVM.sessionPid;
3171 except Exception as oXcpt:
3172 if ComError.notEqual(oXcpt, ComError.E_ACCESSDENIED):
3173 reporter.logXcpt();
3174 self.processPendingEvents();
3175 return None;
3176 self.processPendingEvents();
3177
3178 msStart = base.timestampMilli();
3179 while iCurPid == iOrgPid \
3180 and sCurName == sOrgName \
3181 and sCurName != '' \
3182 and base.timestampMilli() - msStart < cMsTimeout \
3183 and eCurState in (vboxcon.SessionState_Unlocking, vboxcon.SessionState_Spawning, vboxcon.SessionState_Locked,):
3184 self.processEvents(1000);
3185 try:
3186 eCurState = oVM.sessionState;
3187 sCurName = oVM.sessionName if self.fpApiVer >= 5.0 else oVM.sessionType;
3188 iCurPid = oVM.sessionPID if self.fpApiVer >= 4.2 else oVM.sessionPid;
3189 except Exception as oXcpt:
3190 if ComError.notEqual(oXcpt, ComError.E_ACCESSDENIED):
3191 reporter.logXcpt();
3192 break;
3193 self.processPendingEvents();
3194 self.processPendingEvents();
3195 return None;
3196
3197 def uploadStartupLogFile(self, oVM, sVmName):
3198 """
3199 Uploads the VBoxStartup.log when present.
3200 """
3201 fRc = True;
3202 try:
3203 sLogFile = os.path.join(oVM.logFolder, 'VBoxHardening.log');
3204 except:
3205 reporter.logXcpt();
3206 fRc = False;
3207 else:
3208 if os.path.isfile(sLogFile):
3209 reporter.addLogFile(sLogFile, 'log/release/vm', '%s hardening log' % (sVmName, ),
3210 sAltName = '%s-%s' % (sVmName, os.path.basename(sLogFile),));
3211 return fRc;
3212
3213 def annotateAndUploadProcessReport(self, sProcessReport, sFilename, sKind, sDesc):
3214 """
3215 Annotates the given VM process report and uploads it if successfull.
3216 """
3217 fRc = False;
3218 if self.oBuild is not None and self.oBuild.sInstallPath is not None:
3219 oResolver = btresolver.BacktraceResolver(self.sScratchPath, self.oBuild.sInstallPath,
3220 self.getBuildOs(), self.getBuildArch(),
3221 fnLog = reporter.log);
3222 fRcTmp = oResolver.prepareEnv();
3223 if fRcTmp:
3224 reporter.log('Successfully prepared environment');
3225 sReportDbgSym = oResolver.annotateReport(sProcessReport);
3226 if sReportDbgSym and len(sReportDbgSym) > 8:
3227 reporter.addLogString(sReportDbgSym, sFilename, sKind, sDesc);
3228 fRc = True;
3229 else:
3230 reporter.log('Annotating report failed');
3231 oResolver.cleanupEnv();
3232 return fRc;
3233
3234 def startVmEx(self, oVM, fWait = True, sType = None, sName = None, asEnv = None): # pylint: disable=too-many-locals,too-many-statements
3235 """
3236 Start the VM, returning the VM session and progress object on success.
3237 The session is also added to the task list and to the aoRemoteSessions set.
3238
3239 asEnv is a list of string on the putenv() form.
3240
3241 On failure (None, None) is returned and an error is logged.
3242 """
3243 # Massage and check the input.
3244 if sType is None:
3245 sType = self.sSessionType;
3246 if sName is None:
3247 try: sName = oVM.name;
3248 except: sName = 'bad-vm-handle';
3249 reporter.log('startVmEx: sName=%s fWait=%s sType=%s' % (sName, fWait, sType));
3250 if oVM is None:
3251 return (None, None);
3252
3253 ## @todo Do this elsewhere.
3254 # Hack alert. Disables all annoying GUI popups.
3255 if sType == 'gui' and not self.aoRemoteSessions:
3256 try:
3257 self.oVBox.setExtraData('GUI/Input/AutoCapture', 'false');
3258 if self.fpApiVer >= 3.2:
3259 self.oVBox.setExtraData('GUI/LicenseAgreed', '8');
3260 else:
3261 self.oVBox.setExtraData('GUI/LicenseAgreed', '7');
3262 self.oVBox.setExtraData('GUI/RegistrationData', 'triesLeft=0');
3263 self.oVBox.setExtraData('GUI/SUNOnlineData', 'triesLeft=0');
3264 self.oVBox.setExtraData('GUI/SuppressMessages', 'confirmVMReset,remindAboutMouseIntegrationOn,'
3265 'remindAboutMouseIntegrationOff,remindAboutPausedVMInput,confirmInputCapture,'
3266 'confirmGoingFullscreen,remindAboutInaccessibleMedia,remindAboutWrongColorDepth,'
3267 'confirmRemoveMedium,allPopupPanes,allMessageBoxes,all');
3268 self.oVBox.setExtraData('GUI/UpdateDate', 'never');
3269 self.oVBox.setExtraData('GUI/PreventBetaWarning', self.oVBox.version);
3270 except:
3271 reporter.logXcpt();
3272
3273 # The UUID for the name.
3274 try:
3275 sUuid = oVM.id;
3276 except:
3277 reporter.errorXcpt('failed to get the UUID for VM "%s"' % (oVM));
3278 return (None, None);
3279 self.processPendingEvents();
3280
3281 # Construct the environment.
3282 self.sSessionLogFile = '%s/VM-%s.log' % (self.sScratchPath, sUuid);
3283 try: os.remove(self.sSessionLogFile);
3284 except: pass;
3285 if self.sLogSessionDest:
3286 sLogDest = self.sLogSessionDest;
3287 else:
3288 sLogDest = 'file=%s' % (self.sSessionLogFile,);
3289 asEnvFinal = [
3290 'VBOX_LOG=%s' % (self.sLogSessionGroups,),
3291 'VBOX_LOG_FLAGS=%s' % (self.sLogSessionFlags,),
3292 'VBOX_LOG_DEST=nodeny %s' % (sLogDest,),
3293 'VBOX_RELEASE_LOG_FLAGS=append time',
3294 ];
3295 if sType == 'gui':
3296 asEnvFinal.append('VBOX_GUI_DBG_ENABLED=1');
3297 if asEnv is not None and asEnv:
3298 asEnvFinal += asEnv;
3299
3300 reporter.log2('Session environment:\n%s' % (asEnvFinal,));
3301
3302 # Shortcuts for local testing.
3303 oProgress = oWrapped = None;
3304 oTestVM = self.oTestVmSet.findTestVmByName(sName) if self.oTestVmSet is not None else None;
3305 try:
3306 if oTestVM is not None \
3307 and oTestVM.fSnapshotRestoreCurrent is True:
3308 if oVM.state is vboxcon.MachineState_Running:
3309 reporter.log2('Machine "%s" already running.' % (sName,));
3310 oProgress = None;
3311 oWrapped = self.openSession(oVM);
3312 else:
3313 reporter.log2('Checking if snapshot for machine "%s" exists.' % (sName,));
3314 oSessionWrapperRestore = self.openSession(oVM);
3315 if oSessionWrapperRestore is not None:
3316 oSnapshotCur = oVM.currentSnapshot;
3317 if oSnapshotCur is not None:
3318 reporter.log2('Restoring snapshot for machine "%s".' % (sName,));
3319 oSessionWrapperRestore.restoreSnapshot(oSnapshotCur);
3320 reporter.log2('Current snapshot for machine "%s" restored.' % (sName,));
3321 else:
3322 reporter.log('warning: no current snapshot for machine "%s" found.' % (sName,));
3323 oSessionWrapperRestore.close();
3324 except:
3325 reporter.errorXcpt();
3326 return (None, None);
3327
3328 oSession = None; # Must be initialized, otherwise the log statement at the end of the function can fail.
3329
3330 # Open a remote session, wait for this operation to complete.
3331 # (The loop is a kludge to deal with us racing the closing of the
3332 # direct session of a previous VM run. See waitOnDirectSessionClose.)
3333 if oWrapped is None:
3334 for i in range(10):
3335 try:
3336 if self.fpApiVer < 4.3 \
3337 or (self.fpApiVer == 4.3 and not hasattr(self.oVBoxMgr, 'getSessionObject')):
3338 oSession = self.oVBoxMgr.mgr.getSessionObject(self.oVBox); # pylint: disable=no-member
3339 elif self.fpApiVer < 5.2 \
3340 or (self.fpApiVer == 5.2 and hasattr(self.oVBoxMgr, 'vbox')):
3341 oSession = self.oVBoxMgr.getSessionObject(self.oVBox); # pylint: disable=no-member
3342 else:
3343 oSession = self.oVBoxMgr.getSessionObject(); # pylint: disable=no-member,no-value-for-parameter
3344 if self.fpApiVer < 3.3:
3345 oProgress = self.oVBox.openRemoteSession(oSession, sUuid, sType, '\n'.join(asEnvFinal));
3346 else:
3347 if self.uApiRevision >= self.makeApiRevision(6, 1, 0, 1):
3348 oProgress = oVM.launchVMProcess(oSession, sType, asEnvFinal);
3349 else:
3350 oProgress = oVM.launchVMProcess(oSession, sType, '\n'.join(asEnvFinal));
3351 break;
3352 except:
3353 if i == 9:
3354 reporter.errorXcpt('failed to start VM "%s" ("%s"), aborting.' % (sUuid, sName));
3355 return (None, None);
3356 oSession = None;
3357 if i >= 0:
3358 reporter.logXcpt('warning: failed to start VM "%s" ("%s") - retrying in %u secs.' % (sUuid, oVM, i)); # pylint: disable=line-too-long
3359 self.waitOnDirectSessionClose(oVM, 5000 + i * 1000);
3360 if fWait and oProgress is not None:
3361 rc = self.waitOnProgress(oProgress);
3362 if rc < 0:
3363 self.waitOnDirectSessionClose(oVM, 5000);
3364
3365 # VM failed to power up, still collect VBox.log, need to wrap the session object
3366 # in order to use the helper for adding the log files to the report.
3367 from testdriver.vboxwrappers import SessionWrapper;
3368 oTmp = SessionWrapper(oSession, oVM, self.oVBox, self.oVBoxMgr, self, True, sName, self.sSessionLogFile);
3369 oTmp.addLogsToReport();
3370
3371 # Try to collect a stack trace of the process for further investigation of any startup hangs.
3372 uPid = oTmp.getPid();
3373 if uPid is not None:
3374 sHostProcessInfoHung = utils.processGetInfo(uPid, fSudo = True);
3375 if sHostProcessInfoHung is not None:
3376 reporter.log('Trying to annotate the hung VM startup process report, please stand by...');
3377 fRcTmp = self.annotateAndUploadProcessReport(sHostProcessInfoHung, 'vmprocess-startup-hung.log',
3378 'process/report/vm', 'Annotated hung VM process state during startup'); # pylint: disable=line-too-long
3379 # Upload the raw log for manual annotation in case resolving failed.
3380 if not fRcTmp:
3381 reporter.log('Failed to annotate hung VM process report, uploading raw report');
3382 reporter.addLogString(sHostProcessInfoHung, 'vmprocess-startup-hung.log', 'process/report/vm',
3383 'Hung VM process state during startup');
3384
3385 try:
3386 if oSession is not None:
3387 oSession.close();
3388 except: pass;
3389 reportError(oProgress, 'failed to open session for "%s"' % (sName));
3390 self.uploadStartupLogFile(oVM, sName);
3391 return (None, None);
3392 reporter.log2('waitOnProgress -> %s' % (rc,));
3393
3394 # Wrap up the session object and push on to the list before returning it.
3395 if oWrapped is None:
3396 from testdriver.vboxwrappers import SessionWrapper;
3397 oWrapped = SessionWrapper(oSession, oVM, self.oVBox, self.oVBoxMgr, self, True, sName, self.sSessionLogFile);
3398
3399 oWrapped.registerEventHandlerForTask();
3400 self.aoRemoteSessions.append(oWrapped);
3401 if oWrapped is not self.aoRemoteSessions[len(self.aoRemoteSessions) - 1]:
3402 reporter.error('not by reference: oWrapped=%s aoRemoteSessions[%s]=%s'
3403 % (oWrapped, len(self.aoRemoteSessions) - 1,
3404 self.aoRemoteSessions[len(self.aoRemoteSessions) - 1]));
3405 self.addTask(oWrapped);
3406
3407 reporter.log2('startVmEx: oSession=%s, oSessionWrapper=%s, oProgress=%s' % (oSession, oWrapped, oProgress));
3408
3409 from testdriver.vboxwrappers import ProgressWrapper;
3410 return (oWrapped, ProgressWrapper(oProgress, self.oVBoxMgr, self,
3411 'starting %s' % (sName,)) if oProgress else None);
3412
3413 def startVm(self, oVM, sType=None, sName = None, asEnv = None):
3414 """ Simplified version of startVmEx. """
3415 oSession, _ = self.startVmEx(oVM, True, sType, sName, asEnv = asEnv);
3416 return oSession;
3417
3418 def startVmByNameEx(self, sName, fWait=True, sType=None, asEnv = None):
3419 """
3420 Start the VM, returning the VM session and progress object on success.
3421 The session is also added to the task list and to the aoRemoteSessions set.
3422
3423 On failure (None, None) is returned and an error is logged.
3424 """
3425 oVM = self.getVmByName(sName);
3426 if oVM is None:
3427 return (None, None);
3428 return self.startVmEx(oVM, fWait, sType, sName, asEnv = asEnv);
3429
3430 def startVmByName(self, sName, sType=None, asEnv = None):
3431 """
3432 Start the VM, returning the VM session on success. The session is
3433 also added to the task list and to the aoRemoteSessions set.
3434
3435 On failure None is returned and an error is logged.
3436 """
3437 oSession, _ = self.startVmByNameEx(sName, True, sType, asEnv = asEnv);
3438 return oSession;
3439
3440 def terminateVmBySession(self, oSession, oProgress = None, fTakeScreenshot = None): # pylint: disable=too-many-statements
3441 """
3442 Terminates the VM specified by oSession and adds the release logs to
3443 the test report.
3444
3445 This will try achieve this by using powerOff, but will resort to
3446 tougher methods if that fails.
3447
3448 The session will always be removed from the task list.
3449 The session will be closed unless we fail to kill the process.
3450 The session will be removed from the remote session list if closed.
3451
3452 The progress object (a wrapper!) is for teleportation and similar VM
3453 operations, it will be attempted canceled before powering off the VM.
3454 Failures are logged but ignored.
3455 The progress object will always be removed from the task list.
3456
3457 Returns True if powerOff and session close both succeed.
3458 Returns False if on failure (logged), including when we successfully
3459 kill the VM process.
3460 """
3461
3462 reporter.log2('terminateVmBySession: oSession=%s (pid=%s) oProgress=%s' % (oSession.sName, oSession.getPid(), oProgress));
3463
3464 if self.fVmNoTerminate:
3465 reporter.log('terminateVmBySession: Skipping, as --vbox-vm-no-terminate was specified');
3466 # Make sure that we still process the events the VM needs.
3467 self.sleep(24 * 60 * 60 * 1000);
3468
3469 # Call getPid first to make sure the PID is cached in the wrapper.
3470 oSession.getPid();
3471
3472 #
3473 # If the host is out of memory, just skip all the info collection as it
3474 # requires memory too and seems to wedge.
3475 #
3476 sHostProcessInfo = None;
3477 sHostProcessInfoHung = None;
3478 sLastScreenshotPath = None;
3479 sOsKernelLog = None;
3480 sVgaText = None;
3481 asMiscInfos = [];
3482
3483 if not oSession.fHostMemoryLow:
3484 # Try to fetch the VM process info before meddling with its state.
3485 if self.fAlwaysUploadLogs or reporter.testErrorCount() > 0:
3486 sHostProcessInfo = utils.processGetInfo(oSession.getPid(), fSudo = True);
3487
3488 #
3489 # Pause the VM if we're going to take any screenshots or dig into the
3490 # guest. Failures are quitely ignored.
3491 #
3492 if self.fAlwaysUploadLogs or reporter.testErrorCount() > 0:
3493 try:
3494 if oSession.oVM.state in [ vboxcon.MachineState_Running,
3495 vboxcon.MachineState_LiveSnapshotting,
3496 vboxcon.MachineState_Teleporting ]:
3497 oSession.o.console.pause();
3498 except:
3499 reporter.logXcpt();
3500
3501 #
3502 # Take Screenshot and upload it (see below) to Test Manager if appropriate/requested.
3503 #
3504 if fTakeScreenshot is True or self.fAlwaysUploadScreenshots or reporter.testErrorCount() > 0:
3505 sLastScreenshotPath = os.path.join(self.sScratchPath, "LastScreenshot-%s.png" % oSession.sName);
3506 fRc = oSession.takeScreenshot(sLastScreenshotPath);
3507 if fRc is not True:
3508 sLastScreenshotPath = None;
3509
3510 # Query the OS kernel log from the debugger if appropriate/requested.
3511 if self.fAlwaysUploadLogs or reporter.testErrorCount() > 0:
3512 sOsKernelLog = oSession.queryOsKernelLog();
3513
3514 # Do "info vgatext all" separately.
3515 if self.fAlwaysUploadLogs or reporter.testErrorCount() > 0:
3516 sVgaText = oSession.queryDbgInfoVgaText();
3517
3518 # Various infos (do after kernel because of symbols).
3519 if self.fAlwaysUploadLogs or reporter.testErrorCount() > 0:
3520 # Dump the guest stack for all CPUs.
3521 cCpus = oSession.getCpuCount();
3522 if cCpus > 0:
3523 for iCpu in xrange(0, cCpus):
3524 sThis = oSession.queryDbgGuestStack(iCpu);
3525 if sThis:
3526 asMiscInfos += [
3527 '================ start guest stack VCPU %s ================\n' % (iCpu,),
3528 sThis,
3529 '================ end guest stack VCPU %s ==================\n' % (iCpu,),
3530 ];
3531
3532 for sInfo, sArg in [ ('mode', 'all'),
3533 ('fflags', ''),
3534 ('cpumguest', 'verbose all'),
3535 ('cpumguestinstr', 'symbol all'),
3536 ('exits', ''),
3537 ('pic', ''),
3538 ('apic', ''),
3539 ('apiclvt', ''),
3540 ('apictimer', ''),
3541 ('ioapic', ''),
3542 ('pit', ''),
3543 ('phys', ''),
3544 ('clocks', ''),
3545 ('timers', ''),
3546 ('gdt', ''),
3547 ('ldt', ''),
3548 ]:
3549 if sInfo in ['apic',] and self.fpApiVer < 5.1: # asserts and burns
3550 continue;
3551 sThis = oSession.queryDbgInfo(sInfo, sArg);
3552 if sThis:
3553 if sThis[-1] != '\n':
3554 sThis += '\n';
3555 asMiscInfos += [
3556 '================ start %s %s ================\n' % (sInfo, sArg),
3557 sThis,
3558 '================ end %s %s ==================\n' % (sInfo, sArg),
3559 ];
3560
3561 #
3562 # Terminate the VM
3563 #
3564
3565 # Cancel the progress object if specified.
3566 if oProgress is not None:
3567 if not oProgress.isCompleted() and oProgress.isCancelable():
3568 reporter.log2('terminateVmBySession: canceling "%s"...' % (oProgress.sName));
3569 try:
3570 oProgress.o.cancel();
3571 except:
3572 reporter.logXcpt();
3573 else:
3574 oProgress.wait();
3575 self.removeTask(oProgress);
3576
3577 # Check if the VM has terminated by itself before powering it off.
3578 fClose = True;
3579 fRc = True;
3580 if oSession.needsPoweringOff():
3581 reporter.log('terminateVmBySession: powering off "%s"...' % (oSession.sName,));
3582 fRc = oSession.powerOff(fFudgeOnFailure = False);
3583 if fRc is not True:
3584 # power off failed, try terminate it in a nice manner.
3585 fRc = False;
3586 uPid = oSession.getPid();
3587 if uPid is not None:
3588 #
3589 # Collect some information about the VM process first to have
3590 # some state information for further investigation why powering off failed.
3591 #
3592 sHostProcessInfoHung = utils.processGetInfo(uPid, fSudo = True);
3593
3594 # Exterminate...
3595 reporter.error('terminateVmBySession: Terminating PID %u (VM %s)' % (uPid, oSession.sName));
3596 fClose = base.processTerminate(uPid);
3597 if fClose is True:
3598 self.waitOnDirectSessionClose(oSession.oVM, 5000);
3599 fClose = oSession.waitForTask(1000);
3600
3601 if fClose is not True:
3602 # Being nice failed...
3603 reporter.error('terminateVmBySession: Termination failed, trying to kill PID %u (VM %s) instead' \
3604 % (uPid, oSession.sName));
3605 fClose = base.processKill(uPid);
3606 if fClose is True:
3607 self.waitOnDirectSessionClose(oSession.oVM, 5000);
3608 fClose = oSession.waitForTask(1000);
3609 if fClose is not True:
3610 reporter.error('terminateVmBySession: Failed to kill PID %u (VM %s)' % (uPid, oSession.sName));
3611
3612 # The final steps.
3613 if fClose is True:
3614 reporter.log('terminateVmBySession: closing session "%s"...' % (oSession.sName,));
3615 oSession.close();
3616 self.waitOnDirectSessionClose(oSession.oVM, 10000);
3617 try:
3618 eState = oSession.oVM.state;
3619 except:
3620 reporter.logXcpt();
3621 else:
3622 if eState == vboxcon.MachineState_Aborted:
3623 reporter.error('terminateVmBySession: The VM "%s" aborted!' % (oSession.sName,));
3624 self.removeTask(oSession);
3625
3626 #
3627 # Add the release log, debug log and a screenshot of the VM to the test report.
3628 #
3629 if self.fAlwaysUploadLogs or reporter.testErrorCount() > 0:
3630 oSession.addLogsToReport();
3631
3632 # Add a screenshot if it has been requested and taken successfully.
3633 if sLastScreenshotPath is not None:
3634 if reporter.testErrorCount() > 0:
3635 reporter.addLogFile(sLastScreenshotPath, 'screenshot/failure', 'Last VM screenshot');
3636 else:
3637 reporter.addLogFile(sLastScreenshotPath, 'screenshot/success', 'Last VM screenshot');
3638
3639 # Add the guest OS log if it has been requested and taken successfully.
3640 if sOsKernelLog is not None:
3641 reporter.addLogString(sOsKernelLog, 'kernel.log', 'log/guest/kernel', 'Guest OS kernel log');
3642
3643 # Add "info vgatext all" if we've got it.
3644 if sVgaText is not None:
3645 reporter.addLogString(sVgaText, 'vgatext.txt', 'info/vgatext', 'info vgatext all');
3646
3647 # Add the "info xxxx" items if we've got any.
3648 if asMiscInfos:
3649 reporter.addLogString(u''.join(asMiscInfos), 'info.txt', 'info/collection', 'A bunch of info items.');
3650
3651 # Add the host process info if we were able to retrieve it.
3652 if sHostProcessInfo is not None:
3653 reporter.log('Trying to annotate the VM process report, please stand by...');
3654 fRcTmp = self.annotateAndUploadProcessReport(sHostProcessInfo, 'vmprocess.log',
3655 'process/report/vm', 'Annotated VM process state');
3656 # Upload the raw log for manual annotation in case resolving failed.
3657 if not fRcTmp:
3658 reporter.log('Failed to annotate VM process report, uploading raw report');
3659 reporter.addLogString(sHostProcessInfo, 'vmprocess.log', 'process/report/vm', 'VM process state');
3660
3661 # Add the host process info for failed power off attempts if we were able to retrieve it.
3662 if sHostProcessInfoHung is not None:
3663 reporter.log('Trying to annotate the hung VM process report, please stand by...');
3664 fRcTmp = self.annotateAndUploadProcessReport(sHostProcessInfoHung, 'vmprocess-hung.log',
3665 'process/report/vm', 'Annotated hung VM process state');
3666 # Upload the raw log for manual annotation in case resolving failed.
3667 if not fRcTmp:
3668 reporter.log('Failed to annotate hung VM process report, uploading raw report');
3669 fRcTmp = reporter.addLogString(sHostProcessInfoHung, 'vmprocess-hung.log', 'process/report/vm',
3670 'Hung VM process state');
3671 if not fRcTmp:
3672 try: reporter.log('******* START vmprocess-hung.log *******\n%s\n******* END vmprocess-hung.log *******\n'
3673 % (sHostProcessInfoHung,));
3674 except: pass; # paranoia
3675
3676 # Upload the screen video recordings if appropriate.
3677 if self.fAlwaysUploadRecordings or reporter.testErrorCount() > 0:
3678 reporter.log2('Uploading %d screen recordings ...' % (len(self.adRecordingFiles),));
3679 for dRecFile in self.adRecordingFiles:
3680 reporter.log2('Uploading screen recording "%s" (screen %d)' % (dRecFile['file'], dRecFile['id']));
3681 reporter.addLogFile(dRecFile['file'],
3682 'screenrecording/failure' if reporter.testErrorCount() > 0 else 'screenrecording/success',
3683 'Recording of screen #%d' % (dRecFile['id'],));
3684
3685 return fRc;
3686
3687
3688 #
3689 # Some information query functions (mix).
3690 #
3691 # Methods require the VBox API. If the information is provided by both
3692 # the testboxscript as well as VBox API, we'll check if it matches.
3693 #
3694
3695 def _hasHostCpuFeature(self, sEnvVar, sEnum, fpApiMinVer, fQuiet):
3696 """
3697 Common Worker for hasHostNestedPaging() and hasHostHwVirt().
3698
3699 Returns True / False.
3700 Raises exception on environment / host mismatch.
3701 """
3702 fEnv = os.environ.get(sEnvVar, None);
3703 if fEnv is not None:
3704 fEnv = fEnv.lower() not in [ 'false', 'f', 'not', 'no', 'n', '0', ];
3705
3706 fVBox = None;
3707 self.importVBoxApi();
3708 if self.fpApiVer >= fpApiMinVer and hasattr(vboxcon, sEnum):
3709 try:
3710 fVBox = self.oVBox.host.getProcessorFeature(getattr(vboxcon, sEnum));
3711 except:
3712 if not fQuiet:
3713 reporter.logXcpt();
3714
3715 if fVBox is not None:
3716 if fEnv is not None:
3717 if fEnv != fVBox and not fQuiet:
3718 reporter.log('TestBox configuration overwritten: fVBox=%s (%s) vs. fEnv=%s (%s)'
3719 % (fVBox, sEnum, fEnv, sEnvVar));
3720 return fEnv;
3721 return fVBox;
3722 if fEnv is not None:
3723 return fEnv;
3724 return False;
3725
3726 def hasHostHwVirt(self, fQuiet = False):
3727 """
3728 Checks if hardware assisted virtualization is supported by the host.
3729
3730 Returns True / False.
3731 Raises exception on environment / host mismatch.
3732 """
3733 return self._hasHostCpuFeature('TESTBOX_HAS_HW_VIRT', 'ProcessorFeature_HWVirtEx', 3.1, fQuiet);
3734
3735 def hasHostNestedPaging(self, fQuiet = False):
3736 """
3737 Checks if nested paging is supported by the host.
3738
3739 Returns True / False.
3740 Raises exception on environment / host mismatch.
3741 """
3742 return self._hasHostCpuFeature('TESTBOX_HAS_NESTED_PAGING', 'ProcessorFeature_NestedPaging', 4.2, fQuiet) \
3743 and self.hasHostHwVirt(fQuiet);
3744
3745 def hasHostNestedHwVirt(self, fQuiet = False):
3746 """
3747 Checks if nested hardware-assisted virtualization is supported by the host.
3748
3749 Returns True / False.
3750 Raises exception on environment / host mismatch.
3751 """
3752 return self._hasHostCpuFeature('TESTBOX_HAS_NESTED_HWVIRT', 'ProcessorFeature_NestedHWVirt', 6.0, fQuiet) \
3753 and self.hasHostHwVirt(fQuiet);
3754
3755 def hasHostLongMode(self, fQuiet = False):
3756 """
3757 Checks if the host supports 64-bit guests.
3758
3759 Returns True / False.
3760 Raises exception on environment / host mismatch.
3761 """
3762 # Note that the testboxscript doesn't export this variable atm.
3763 return self._hasHostCpuFeature('TESTBOX_HAS_LONG_MODE', 'ProcessorFeature_LongMode', 3.1, fQuiet);
3764
3765 def getHostCpuCount(self, fQuiet = False):
3766 """
3767 Returns the number of CPUs on the host.
3768
3769 Returns True / False.
3770 Raises exception on environment / host mismatch.
3771 """
3772 cEnv = os.environ.get('TESTBOX_CPU_COUNT', None);
3773 if cEnv is not None:
3774 cEnv = int(cEnv);
3775
3776 try:
3777 cVBox = self.oVBox.host.processorOnlineCount;
3778 except:
3779 if not fQuiet:
3780 reporter.logXcpt();
3781 cVBox = None;
3782
3783 if cVBox is not None:
3784 if cEnv is not None:
3785 assert cVBox == cEnv, 'Misconfigured TestBox: VBox: %u CPUs, testboxscript: %u CPUs' % (cVBox, cEnv);
3786 return cVBox;
3787 if cEnv is not None:
3788 return cEnv;
3789 return 1;
3790
3791 def _getHostCpuDesc(self, fQuiet = False):
3792 """
3793 Internal method used for getting the host CPU description from VBoxSVC.
3794 Returns description string, on failure an empty string is returned.
3795 """
3796 try:
3797 return self.oVBox.host.getProcessorDescription(0);
3798 except:
3799 if not fQuiet:
3800 reporter.logXcpt();
3801 return '';
3802
3803 def isHostCpuAmd(self, fQuiet = False):
3804 """
3805 Checks if the host CPU vendor is AMD.
3806
3807 Returns True / False.
3808 """
3809 sCpuDesc = self._getHostCpuDesc(fQuiet);
3810 return 'AMD' in sCpuDesc or sCpuDesc == 'AuthenticAMD';
3811
3812 def isHostCpuIntel(self, fQuiet = False):
3813 """
3814 Checks if the host CPU vendor is Intel.
3815
3816 Returns True / False.
3817 """
3818 sCpuDesc = self._getHostCpuDesc(fQuiet);
3819 return sCpuDesc.startswith("Intel") or sCpuDesc == 'GenuineIntel';
3820
3821 def isHostCpuVia(self, fQuiet = False):
3822 """
3823 Checks if the host CPU vendor is VIA (or Centaur).
3824
3825 Returns True / False.
3826 """
3827 sCpuDesc = self._getHostCpuDesc(fQuiet);
3828 return sCpuDesc.startswith("VIA") or sCpuDesc == 'CentaurHauls';
3829
3830 def isHostCpuShanghai(self, fQuiet = False):
3831 """
3832 Checks if the host CPU vendor is Shanghai (or Zhaoxin).
3833
3834 Returns True / False.
3835 """
3836 sCpuDesc = self._getHostCpuDesc(fQuiet);
3837 return sCpuDesc.startswith("ZHAOXIN") or sCpuDesc.strip(' ') == 'Shanghai';
3838
3839 def isHostCpuP4(self, fQuiet = False):
3840 """
3841 Checks if the host CPU is a Pentium 4 / Pentium D.
3842
3843 Returns True / False.
3844 """
3845 if not self.isHostCpuIntel(fQuiet):
3846 return False;
3847
3848 if self.fpApiVer >= 7.1:
3849 (uFamilyModel, _, _, _) = self.oVBox.host.x86.getProcessorCPUIDLeaf(0, 0x1, 0);
3850 else:
3851 (uFamilyModel, _, _, _) = self.oVBox.host.getProcessorCPUIDLeaf(0, 0x1, 0);
3852 return ((uFamilyModel >> 8) & 0xf) == 0xf;
3853
3854 def hasRawModeSupport(self, fQuiet = False):
3855 """
3856 Checks if raw-mode is supported by VirtualBox that the testbox is
3857 configured for it.
3858
3859 Returns True / False.
3860 Raises no exceptions.
3861
3862 Note! Differs from the rest in that we don't require the
3863 TESTBOX_WITH_RAW_MODE value to match the API. It is
3864 sometimes helpful to disable raw-mode on individual
3865 test boxes. (This probably goes for
3866 """
3867 # The environment variable can be used to disable raw-mode.
3868 fEnv = os.environ.get('TESTBOX_WITH_RAW_MODE', None);
3869 if fEnv is not None:
3870 fEnv = fEnv.lower() not in [ 'false', 'f', 'not', 'no', 'n', '0', ];
3871 if fEnv is False:
3872 return False;
3873
3874 # Starting with 5.0 GA / RC2 the API can tell us whether VBox was built
3875 # with raw-mode support or not.
3876 self.importVBoxApi();
3877 if self.fpApiVer >= 5.0:
3878 try:
3879 if self.fpApiVer >= 7.1:
3880 # @todo r=aeichner Not entirely correct as we can support multiple platforms on a single host
3881 # each having an individual raw-mode status. Not relevant right now because
3882 # there is no raw-mode at all currently.
3883 oPlatformProperties = self.oVBox.getPlatformProperties(self.oVBox.host.architecture);
3884 fVBox = oPlatformProperties.rawModeSupported;
3885 else:
3886 fVBox = self.oVBox.systemProperties.rawModeSupported;
3887 except:
3888 if not fQuiet:
3889 reporter.logXcpt();
3890 fVBox = True;
3891 if fVBox is False:
3892 return False;
3893
3894 return True;
3895
3896 #
3897 # Testdriver execution methods.
3898 #
3899
3900 def handleTask(self, oTask, sMethod):
3901 """
3902 Callback method for handling unknown tasks in the various run loops.
3903
3904 The testdriver should override this if it already tasks running when
3905 calling startVmAndConnectToTxsViaTcp, txsRunTest or similar methods.
3906 Call super to handle unknown tasks.
3907
3908 Returns True if handled, False if not.
3909 """
3910 reporter.error('%s: unknown task %s' % (sMethod, oTask));
3911 return False;
3912
3913 def txsDoTask(self, oSession, oTxsSession, fnAsync, aArgs):
3914 """
3915 Generic TXS task wrapper which waits both on the TXS and the session tasks.
3916
3917 Returns False on error, logged.
3918 Returns task result on success.
3919 """
3920 # All async methods ends with the following two args.
3921 cMsTimeout = aArgs[-2];
3922 fIgnoreErrors = aArgs[-1];
3923
3924 fRemoveVm = self.addTask(oSession);
3925 fRemoveTxs = self.addTask(oTxsSession);
3926
3927 reporter.log2('txsDoTask(%s): Running' % (str(fnAsync)));
3928 rc = fnAsync(*aArgs); # pylint: disable=star-args
3929 if rc is True:
3930 rc = False;
3931 oTask = self.waitForTasks(cMsTimeout + 1);
3932 if oTask is oTxsSession:
3933 if oTxsSession.isSuccess():
3934 rc = oTxsSession.getResult();
3935 elif fIgnoreErrors is True:
3936 reporter.log( 'txsDoTask(%s): task failed (%s)' % (str(fnAsync), oTxsSession.getLastReply()[1],));
3937 else:
3938 reporter.error('txsDoTask(%s): task failed (%s)' % (str(fnAsync), oTxsSession.getLastReply()[1],));
3939 else:
3940 oTxsSession.cancelTask();
3941 if oTask is None:
3942 if fIgnoreErrors is True:
3943 reporter.log( 'txsDoTask(%s): The task timed out.' % (str(fnAsync)));
3944 else:
3945 reporter.errorTimeout('txsDoTask(%s): The task timed out.' % (str(fnAsync)));
3946 elif oTask is oSession:
3947 reporter.error('txsDoTask(%s): The VM terminated unexpectedly' % (str(fnAsync)));
3948 else:
3949 if fIgnoreErrors is True:
3950 reporter.log( 'txsDoTask(%s): An unknown task %s was returned' % (str(fnAsync), oTask,));
3951 else:
3952 reporter.error('txsDoTask(%s): An unknown task %s was returned' % (str(fnAsync), oTask,));
3953 else:
3954 reporter.error('txsDoTask(%s) returned %s' % (str(fnAsync), rc,));
3955
3956 if fRemoveTxs:
3957 self.removeTask(oTxsSession);
3958 if fRemoveVm:
3959 self.removeTask(oSession);
3960 return rc;
3961
3962 # pylint: disable=missing-docstring
3963
3964 def txsDisconnect(self, oSession, oTxsSession, cMsTimeout = 30000, fIgnoreErrors = False):
3965 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncDisconnect,
3966 (self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3967
3968 def txsVer(self, oSession, oTxsSession, cMsTimeout = 30000, fIgnoreErrors = False):
3969 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncVer,
3970 (self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3971
3972 def txsUuid(self, oSession, oTxsSession, cMsTimeout = 30000, fIgnoreErrors = False):
3973 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncUuid,
3974 (self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3975
3976 def txsMkDir(self, oSession, oTxsSession, sRemoteDir, fMode = 0o700, cMsTimeout = 30000, fIgnoreErrors = False):
3977 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncMkDir,
3978 (sRemoteDir, fMode, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3979
3980 def txsMkDirPath(self, oSession, oTxsSession, sRemoteDir, fMode = 0o700, cMsTimeout = 30000, fIgnoreErrors = False):
3981 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncMkDirPath,
3982 (sRemoteDir, fMode, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3983
3984 def txsMkSymlink(self, oSession, oTxsSession, sLinkTarget, sLink, cMsTimeout = 30000, fIgnoreErrors = False):
3985 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncMkSymlink,
3986 (sLinkTarget, sLink, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3987
3988 def txsRmDir(self, oSession, oTxsSession, sRemoteDir, cMsTimeout = 30000, fIgnoreErrors = False):
3989 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncRmDir,
3990 (sRemoteDir, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3991
3992 def txsRmFile(self, oSession, oTxsSession, sRemoteFile, cMsTimeout = 30000, fIgnoreErrors = False):
3993 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncRmFile,
3994 (sRemoteFile, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3995
3996 def txsRmSymlink(self, oSession, oTxsSession, sRemoteSymlink, cMsTimeout = 30000, fIgnoreErrors = False):
3997 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncRmSymlink,
3998 (sRemoteSymlink, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3999
4000 def txsRmTree(self, oSession, oTxsSession, sRemoteTree, cMsTimeout = 30000, fIgnoreErrors = False):
4001 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncRmTree,
4002 (sRemoteTree, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
4003
4004 def txsIsDir(self, oSession, oTxsSession, sRemoteDir, cMsTimeout = 30000, fIgnoreErrors = False):
4005 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncIsDir,
4006 (sRemoteDir, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
4007
4008 def txsIsFile(self, oSession, oTxsSession, sRemoteFile, cMsTimeout = 30000, fIgnoreErrors = False):
4009 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncIsFile,
4010 (sRemoteFile, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
4011
4012 def txsIsSymlink(self, oSession, oTxsSession, sRemoteSymlink, cMsTimeout = 30000, fIgnoreErrors = False):
4013 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncIsSymlink,
4014 (sRemoteSymlink, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
4015
4016 def txsCopyFile(self, oSession, oTxsSession, sSrcFile, sDstFile, fMode = 0, cMsTimeout = 30000, fIgnoreErrors = False):
4017 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncCopyFile, \
4018 (sSrcFile, sDstFile, fMode, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
4019
4020 def txsUploadFile(self, oSession, oTxsSession, sLocalFile, sRemoteFile, cMsTimeout = 30000, fIgnoreErrors = False):
4021 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncUploadFile, \
4022 (sLocalFile, sRemoteFile, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
4023
4024 def txsUploadString(self, oSession, oTxsSession, sContent, sRemoteFile, cMsTimeout = 30000, fIgnoreErrors = False):
4025 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncUploadString, \
4026 (sContent, sRemoteFile, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
4027
4028 def txsDownloadFile(self, oSession, oTxsSession, sRemoteFile, sLocalFile, cMsTimeout = 30000, fIgnoreErrors = False):
4029 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncDownloadFile, \
4030 (sRemoteFile, sLocalFile, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
4031
4032 def txsDownloadFiles(self, oSession, oTxsSession, aasFiles, fAddToLog = True, fIgnoreErrors = False):
4033 """
4034 Convenience function to get files from the guest, storing them in the
4035 scratch and adding them to the test result set (optional, but default).
4036
4037 The aasFiles parameter contains an array of with guest-path + host-path
4038 pairs, optionally a file 'kind', description and an alternative upload
4039 filename can also be specified.
4040
4041 Host paths are relative to the scratch directory or they must be given
4042 in absolute form. The guest path should be using guest path style.
4043
4044 Returns True on success.
4045 Returns False on failure (unless fIgnoreErrors is set), logged.
4046 """
4047 for asEntry in aasFiles:
4048 # Unpack:
4049 sGstFile = asEntry[0];
4050 sHstFile = asEntry[1];
4051 sKind = asEntry[2] if len(asEntry) > 2 and asEntry[2] else 'misc/other';
4052 sDescription = asEntry[3] if len(asEntry) > 3 and asEntry[3] else '';
4053 sAltName = asEntry[4] if len(asEntry) > 4 and asEntry[4] else None;
4054 assert len(asEntry) <= 5 and sGstFile and sHstFile;
4055 if not os.path.isabs(sHstFile):
4056 sHstFile = os.path.join(self.sScratchPath, sHstFile);
4057
4058 reporter.log2('Downloading file "%s" to "%s" ...' % (sGstFile, sHstFile,));
4059
4060 try: os.unlink(sHstFile); ## @todo txsDownloadFile doesn't truncate the output file.
4061 except: pass;
4062
4063 fRc = self.txsDownloadFile(oSession, oTxsSession, sGstFile, sHstFile, 30 * 1000, fIgnoreErrors);
4064 if fRc:
4065 if fAddToLog:
4066 reporter.addLogFile(sHstFile, sKind, sDescription, sAltName);
4067 else:
4068 if fIgnoreErrors is not True:
4069 return reporter.error('error downloading file "%s" to "%s"' % (sGstFile, sHstFile));
4070 reporter.log('warning: file "%s" was not downloaded, ignoring.' % (sGstFile,));
4071 return True;
4072
4073 def txsDownloadString(self, oSession, oTxsSession, sRemoteFile, sEncoding = 'utf-8', fIgnoreEncodingErrors = True,
4074 cMsTimeout = 30000, fIgnoreErrors = False):
4075 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncDownloadString,
4076 (sRemoteFile, sEncoding, fIgnoreEncodingErrors, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
4077
4078 def txsPackFile(self, oSession, oTxsSession, sRemoteFile, sRemoteSource, cMsTimeout = 30000, fIgnoreErrors = False):
4079 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncPackFile, \
4080 (sRemoteFile, sRemoteSource, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
4081
4082 def txsUnpackFile(self, oSession, oTxsSession, sRemoteFile, sRemoteDir, cMsTimeout = 30000, fIgnoreErrors = False):
4083 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncUnpackFile, \
4084 (sRemoteFile, sRemoteDir, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
4085
4086 def txsExpandString(self, oSession, oTxsSession, sString, cMsTimeout = 30000, fIgnoreErrors = False):
4087 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncExpandString, \
4088 (sString, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
4089
4090 # pylint: enable=missing-docstring
4091
4092 def txsCdWait(self,
4093 oSession, # type: vboxwrappers.SessionWrapper
4094 oTxsSession, # type: txsclient.Session
4095 cMsTimeout = 30000, # type: int
4096 sFile = None # type: String
4097 ): # -> bool
4098 """
4099 Mostly an internal helper for txsRebootAndReconnectViaTcp and
4100 startVmAndConnectToTxsViaTcp that waits for the CDROM drive to become
4101 ready. It does this by polling for a file it knows to exist on the CD.
4102
4103 Returns True on success.
4104
4105 Returns False on failure, logged.
4106 """
4107
4108 if sFile is None:
4109 sFile = 'valkit.txt';
4110
4111 reporter.log('txsCdWait: Waiting for file "%s" to become available ...' % (sFile,));
4112
4113 fRemoveVm = self.addTask(oSession);
4114 fRemoveTxs = self.addTask(oTxsSession);
4115 cMsTimeout = self.adjustTimeoutMs(cMsTimeout);
4116 msStart = base.timestampMilli();
4117 cMsTimeout2 = cMsTimeout;
4118 fRc = oTxsSession.asyncIsFile('${CDROM}/%s' % (sFile,), cMsTimeout2);
4119 if fRc is True:
4120 while True:
4121 # wait for it to complete.
4122 oTask = self.waitForTasks(cMsTimeout2 + 1);
4123 if oTask is not oTxsSession:
4124 oTxsSession.cancelTask();
4125 if oTask is None:
4126 reporter.errorTimeout('txsCdWait: The task timed out (after %s ms).'
4127 % (base.timestampMilli() - msStart,));
4128 elif oTask is oSession:
4129 reporter.error('txsCdWait: The VM terminated unexpectedly');
4130 else:
4131 reporter.error('txsCdWait: An unknown task %s was returned' % (oTask,));
4132 fRc = False;
4133 break;
4134 if oTxsSession.isSuccess():
4135 break;
4136
4137 # Check for timeout.
4138 cMsElapsed = base.timestampMilli() - msStart;
4139 if cMsElapsed >= cMsTimeout:
4140 reporter.error('txsCdWait: timed out');
4141 fRc = False;
4142 break;
4143 # delay.
4144 self.sleep(1);
4145
4146 # resubmit the task.
4147 cMsTimeout2 = msStart + cMsTimeout - base.timestampMilli();
4148 cMsTimeout2 = max(cMsTimeout2, 500);
4149 fRc = oTxsSession.asyncIsFile('${CDROM}/%s' % (sFile,), cMsTimeout2);
4150 if fRc is not True:
4151 reporter.error('txsCdWait: asyncIsFile failed');
4152 break;
4153 else:
4154 reporter.error('txsCdWait: asyncIsFile failed');
4155
4156 if not fRc:
4157 # Do some diagnosis to find out why this failed.
4158 ## @todo Identify guest OS type and only run one of the following commands.
4159 fIsNotWindows = True;
4160 reporter.log('txsCdWait: Listing root contents of ${CDROM}:');
4161 if fIsNotWindows:
4162 reporter.log('txsCdWait: Tiggering udevadm ...');
4163 oTxsSession.syncExec("/sbin/udevadm", ("/sbin/udevadm", "trigger", "--verbose"), fIgnoreErrors = True);
4164 time.sleep(15);
4165 oTxsSession.syncExec("/bin/ls", ("/bin/ls", "-al", "${CDROM}"), fIgnoreErrors = True);
4166 reporter.log('txsCdWait: Listing media directory:');
4167 oTxsSession.syncExec('/bin/ls', ('/bin/ls', '-l', '-a', '-R', '/media'), fIgnoreErrors = True);
4168 reporter.log('txsCdWait: Listing mount points / drives:');
4169 oTxsSession.syncExec('/bin/mount', ('/bin/mount',), fIgnoreErrors = True);
4170 oTxsSession.syncExec('/bin/cat', ('/bin/cat', '/etc/fstab'), fIgnoreErrors = True);
4171 oTxsSession.syncExec('/bin/dmesg', ('/bin/dmesg',), fIgnoreErrors = True);
4172 oTxsSession.syncExec('/usr/bin/lshw', ('/usr/bin/lshw', '-c', 'disk'), fIgnoreErrors = True);
4173 oTxsSession.syncExec('/bin/journalctl',
4174 ('/bin/journalctl', '-x', '-b'), fIgnoreErrors = True);
4175 oTxsSession.syncExec('/bin/journalctl',
4176 ('/bin/journalctl', '-x', '-b', '/usr/lib/udisks2/udisksd'), fIgnoreErrors = True);
4177 oTxsSession.syncExec('/usr/bin/udisksctl',
4178 ('/usr/bin/udisksctl', 'info', '-b', '/dev/sr0'), fIgnoreErrors = True);
4179 oTxsSession.syncExec('/bin/systemctl',
4180 ('/bin/systemctl', 'status', 'udisks2'), fIgnoreErrors = True);
4181 oTxsSession.syncExec('/bin/ps',
4182 ('/bin/ps', '-a', '-u', '-x'), fIgnoreErrors = True);
4183 reporter.log('txsCdWait: Mounting manually ...');
4184 for _ in range(3):
4185 oTxsSession.syncExec('/bin/mount', ('/bin/mount', '/dev/sr0', '${CDROM}'), fIgnoreErrors = True);
4186 time.sleep(5);
4187 reporter.log('txsCdWait: Re-Listing media directory:');
4188 oTxsSession.syncExec('/bin/ls', ('/bin/ls', '-l', '-a', '-R', '/media'), fIgnoreErrors = True);
4189 else:
4190 # ASSUMES that we always install Windows on drive C right now.
4191 sWinDir = "C:\\Windows\\System32\\";
4192 # Should work since WinXP Pro.
4193 oTxsSession.syncExec(sWinDir + "wbem\\WMIC.exe",
4194 ("WMIC.exe", "logicaldisk", "get",
4195 "deviceid, volumename, description"),
4196 fIgnoreErrors = True);
4197 oTxsSession.syncExec(sWinDir + " cmd.exe",
4198 ('cmd.exe', '/C', 'dir', '${CDROM}'),
4199 fIgnoreErrors = True);
4200
4201 if fRemoveTxs:
4202 self.removeTask(oTxsSession);
4203 if fRemoveVm:
4204 self.removeTask(oSession);
4205 return fRc;
4206
4207 def txsDoConnectViaTcp(self, oSession, cMsTimeout, fNatForwardingForTxs = False):
4208 """
4209 Mostly an internal worker for connecting to TXS via TCP used by the
4210 *ViaTcp methods.
4211
4212 Returns a tuplet with True/False and TxsSession/None depending on the
4213 result. Errors are logged.
4214 """
4215
4216 reporter.log2('txsDoConnectViaTcp: oSession=%s, cMsTimeout=%s, fNatForwardingForTxs=%s'
4217 % (oSession, cMsTimeout, fNatForwardingForTxs));
4218
4219 cMsTimeout = self.adjustTimeoutMs(cMsTimeout);
4220 oTxsConnect = oSession.txsConnectViaTcp(cMsTimeout, fNatForwardingForTxs = fNatForwardingForTxs);
4221 if oTxsConnect is not None:
4222 self.addTask(oTxsConnect);
4223 fRemoveVm = self.addTask(oSession);
4224 oTask = self.waitForTasks(cMsTimeout + 1);
4225 reporter.log2('txsDoConnectViaTcp: waitForTasks returned %s' % (oTask,));
4226 self.removeTask(oTxsConnect);
4227 if oTask is oTxsConnect:
4228 oTxsSession = oTxsConnect.getResult();
4229 if oTxsSession is not None:
4230 reporter.log('txsDoConnectViaTcp: Connected to TXS on %s.' % (oTxsSession.oTransport.sHostname,));
4231 return (True, oTxsSession);
4232
4233 reporter.error('txsDoConnectViaTcp: failed to connect to TXS.');
4234 else:
4235 oTxsConnect.cancelTask();
4236 if oTask is None:
4237 reporter.errorTimeout('txsDoConnectViaTcp: connect stage 1 timed out');
4238 elif oTask is oSession:
4239 oSession.reportPrematureTermination('txsDoConnectViaTcp: ');
4240 else:
4241 reporter.error('txsDoConnectViaTcp: unknown/wrong task %s' % (oTask,));
4242 if fRemoveVm:
4243 self.removeTask(oSession);
4244 else:
4245 reporter.error('txsDoConnectViaTcp: txsConnectViaTcp failed');
4246 return (False, None);
4247
4248 def startVmAndConnectToTxsViaTcp(self, sVmName, fCdWait = False, cMsTimeout = 15*60000, \
4249 cMsCdWait = 30000, sFileCdWait = None, \
4250 fNatForwardingForTxs = False):
4251 """
4252 Starts the specified VM and tries to connect to its TXS via TCP.
4253 The VM will be powered off if TXS doesn't respond before the specified
4254 time has elapsed.
4255
4256 Returns a the VM and TXS sessions (a two tuple) on success. The VM
4257 session is in the task list, the TXS session is not.
4258 Returns (None, None) on failure, fully logged.
4259 """
4260
4261 # Zap the guest IP to make sure we're not getting a stale entry
4262 # (unless we're restoring the VM of course).
4263 oTestVM = self.oTestVmSet.findTestVmByName(sVmName) if self.oTestVmSet is not None else None;
4264 if oTestVM is None \
4265 or oTestVM.fSnapshotRestoreCurrent is False:
4266 try:
4267 oSession1 = self.openSession(self.getVmByName(sVmName));
4268 oSession1.delGuestPropertyValue('/VirtualBox/GuestInfo/Net/0/V4/IP');
4269 oSession1.saveSettings(True);
4270 del oSession1;
4271 except:
4272 reporter.logXcpt();
4273
4274 # Start the VM.
4275 reporter.log('startVmAndConnectToTxsViaTcp: Starting(/preparing) "%s" (timeout %s s)...' % (sVmName, cMsTimeout / 1000));
4276 reporter.flushall();
4277 oSession = self.startVmByName(sVmName);
4278 if oSession is not None:
4279 # Connect to TXS.
4280 reporter.log2('startVmAndConnectToTxsViaTcp: Started(/prepared) "%s", connecting to TXS ...' % (sVmName,));
4281 (fRc, oTxsSession) = self.txsDoConnectViaTcp(oSession, cMsTimeout, fNatForwardingForTxs);
4282 if fRc is True:
4283 if fCdWait:
4284 # Wait for CD?
4285 reporter.log2('startVmAndConnectToTxsViaTcp: Waiting for file "%s" to become available ...' % (sFileCdWait,));
4286 fRc = self.txsCdWait(oSession, oTxsSession, cMsCdWait, sFileCdWait);
4287 if fRc is not True:
4288 reporter.error('startVmAndConnectToTxsViaTcp: txsCdWait failed');
4289
4290 sVer = self.txsVer(oSession, oTxsSession, cMsTimeout, fIgnoreErrors = True);
4291 if sVer is not False:
4292 reporter.log('startVmAndConnectToTxsViaTcp: TestExecService version %s' % (sVer,));
4293 else:
4294 reporter.log('startVmAndConnectToTxsViaTcp: Unable to retrieve TestExecService version');
4295
4296 if fRc is True:
4297 # Success!
4298 return (oSession, oTxsSession);
4299 else:
4300 reporter.error('startVmAndConnectToTxsViaTcp: txsDoConnectViaTcp failed');
4301 # If something went wrong while waiting for TXS to be started - take VM screenshot before terminate it
4302 self.terminateVmBySession(oSession);
4303 return (None, None);
4304
4305 def txsRebootAndReconnectViaTcp(self, oSession, oTxsSession, fCdWait = False, cMsTimeout = 15*60000, \
4306 cMsCdWait = 30000, sFileCdWait = None, fNatForwardingForTxs = False):
4307 """
4308 Executes the TXS reboot command
4309
4310 Returns A tuple of True and the new TXS session on success.
4311
4312 Returns A tuple of False and either the old TXS session or None on failure.
4313 """
4314 reporter.log2('txsRebootAndReconnect: cMsTimeout=%u' % (cMsTimeout,));
4315
4316 #
4317 # This stuff is a bit complicated because of rebooting being kind of
4318 # disruptive to the TXS and such... The protocol is that TXS will:
4319 # - ACK the reboot command.
4320 # - Shutdown the transport layer, implicitly disconnecting us.
4321 # - Execute the reboot operation.
4322 # - On failure, it will be re-init the transport layer and be
4323 # available pretty much immediately. UUID unchanged.
4324 # - On success, it will be respawed after the reboot (hopefully),
4325 # with a different UUID.
4326 #
4327 fRc = False;
4328 iStart = base.timestampMilli();
4329
4330 # Get UUID.
4331 cMsTimeout2 = min(60000, cMsTimeout);
4332 sUuidBefore = self.txsUuid(oSession, oTxsSession, self.adjustTimeoutMs(cMsTimeout2, 60000));
4333 if sUuidBefore is not False:
4334 # Reboot.
4335 cMsElapsed = base.timestampMilli() - iStart;
4336 cMsTimeout2 = cMsTimeout - cMsElapsed;
4337 fRc = self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncReboot,
4338 (self.adjustTimeoutMs(cMsTimeout2, 60000), False));
4339 if fRc is True:
4340 # Reconnect.
4341 if fNatForwardingForTxs is True:
4342 self.sleep(22); # NAT fudge - Two fixes are wanted: 1. TXS connect retries. 2. Main API reboot/reset hint.
4343 cMsElapsed = base.timestampMilli() - iStart;
4344 (fRc, oTxsSession) = self.txsDoConnectViaTcp(oSession, cMsTimeout - cMsElapsed, fNatForwardingForTxs);
4345 if fRc is True:
4346 # Check the UUID.
4347 cMsElapsed = base.timestampMilli() - iStart;
4348 cMsTimeout2 = min(60000, cMsTimeout - cMsElapsed);
4349 sUuidAfter = self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncUuid,
4350 (self.adjustTimeoutMs(cMsTimeout2, 60000), False));
4351 if sUuidBefore is not False:
4352 if sUuidAfter != sUuidBefore:
4353 reporter.log('The guest rebooted (UUID %s -> %s)' % (sUuidBefore, sUuidAfter))
4354
4355 # Do CD wait if specified.
4356 if fCdWait:
4357 fRc = self.txsCdWait(oSession, oTxsSession, cMsCdWait, sFileCdWait);
4358 if fRc is not True:
4359 reporter.error('txsRebootAndReconnectViaTcp: txsCdWait failed');
4360
4361 sVer = self.txsVer(oSession, oTxsSession, cMsTimeout, fIgnoreErrors = True);
4362 if sVer is not False:
4363 reporter.log('txsRebootAndReconnectViaTcp: TestExecService version %s' % (sVer,));
4364 else:
4365 reporter.log('txsRebootAndReconnectViaTcp: Unable to retrieve TestExecService version');
4366 else:
4367 reporter.error('txsRebootAndReconnectViaTcp: failed to get UUID (after)');
4368 else:
4369 reporter.error('txsRebootAndReconnectViaTcp: did not reboot (UUID %s)' % (sUuidBefore,));
4370 else:
4371 reporter.error('txsRebootAndReconnectViaTcp: txsDoConnectViaTcp failed');
4372 else:
4373 reporter.error('txsRebootAndReconnectViaTcp: reboot failed');
4374 else:
4375 reporter.error('txsRebootAndReconnectViaTcp: failed to get UUID (before)');
4376 return (fRc, oTxsSession);
4377
4378 # pylint: disable=too-many-locals,too-many-arguments
4379
4380 def txsRunTest(self, oTxsSession, sTestName, cMsTimeout, sExecName, asArgs = (), asAddEnv = (), sAsUser = "",
4381 fCheckSessionStatus = False):
4382 """
4383 Executes the specified test task, waiting till it completes or times out.
4384
4385 The VM session (if any) must be in the task list.
4386
4387 Returns True if we executed the task and nothing abnormal happend.
4388 Query the process status from the TXS session.
4389
4390 Returns False if some unexpected task was signalled or we failed to
4391 submit the job.
4392
4393 If fCheckSessionStatus is set to True, the overall session status will be
4394 taken into account and logged as an error on failure.
4395 """
4396 reporter.testStart(sTestName);
4397 reporter.log2('txsRunTest: cMsTimeout=%u sExecName=%s asArgs=%s' % (cMsTimeout, sExecName, asArgs));
4398
4399 # Submit the job.
4400 fRc = False;
4401 if oTxsSession.asyncExec(sExecName, asArgs, asAddEnv, sAsUser, cMsTimeout = self.adjustTimeoutMs(cMsTimeout)):
4402 self.addTask(oTxsSession);
4403
4404 # Wait for the job to complete.
4405 while True:
4406 oTask = self.waitForTasks(cMsTimeout + 1);
4407 if oTask is None:
4408 if fCheckSessionStatus:
4409 reporter.error('txsRunTest: waitForTasks for test "%s" timed out' % (sTestName,));
4410 else:
4411 reporter.log('txsRunTest: waitForTasks for test "%s" timed out' % (sTestName,));
4412 break;
4413 if oTask is oTxsSession:
4414 if fCheckSessionStatus \
4415 and not oTxsSession.isSuccess():
4416 reporter.error('txsRunTest: Test "%s" failed' % (sTestName,));
4417 else:
4418 fRc = True;
4419 reporter.log('txsRunTest: isSuccess=%s getResult=%s' \
4420 % (oTxsSession.isSuccess(), oTxsSession.getResult()));
4421 break;
4422 if not self.handleTask(oTask, 'txsRunTest'):
4423 break;
4424
4425 self.removeTask(oTxsSession);
4426 if not oTxsSession.pollTask():
4427 oTxsSession.cancelTask();
4428 else:
4429 reporter.error('txsRunTest: asyncExec failed');
4430
4431 reporter.testDone();
4432 return fRc;
4433
4434 def txsRunTestRedirectStd(self, oTxsSession, sTestName, cMsTimeout, sExecName, asArgs = (), asAddEnv = (), sAsUser = "",
4435 oStdIn = '/dev/null', oStdOut = '/dev/null', oStdErr = '/dev/null', oTestPipe = '/dev/null',
4436 fIgnoreErrors = False):
4437 """
4438 Executes the specified test task, waiting till it completes or times out,
4439 redirecting stdin, stdout and stderr to the given objects.
4440
4441 The VM session (if any) must be in the task list.
4442
4443 Returns True if we executed the task and nothing abnormal happend.
4444 Query the process status from the TXS session.
4445
4446 Returns False if some unexpected task was signalled or we failed to
4447 submit the job.
4448 """
4449 reporter.testStart(sTestName);
4450 reporter.log2('txsRunTestRedirectStd: cMsTimeout=%u sExecName=%s asArgs=%s' % (cMsTimeout, sExecName, asArgs));
4451
4452 # Submit the job.
4453 fRc = False;
4454 if oTxsSession.asyncExecEx(sExecName, asArgs, asAddEnv, oStdIn, oStdOut, oStdErr,
4455 oTestPipe, sAsUser, cMsTimeout = self.adjustTimeoutMs(cMsTimeout),
4456 fIgnoreErrors = fIgnoreErrors):
4457 self.addTask(oTxsSession);
4458
4459 # Wait for the job to complete.
4460 while True:
4461 oTask = self.waitForTasks(cMsTimeout + 1);
4462 if oTask is None:
4463 if fIgnoreErrors:
4464 reporter.log('txsRunTestRedirectStd: waitForTasks timed out');
4465 else:
4466 reporter.error('txsRunTestRedirectStd: waitForTasks timed out');
4467 break;
4468 if oTask is oTxsSession:
4469 fRc = True;
4470 if not oTxsSession.isSuccess() \
4471 and not fIgnoreErrors:
4472 reporter.error('txsRunTestRedirectStd: failed; result is "%s"' % (oTxsSession.getResult()));
4473 else:
4474 reporter.log('txsRunTestRedirectStd: isSuccess=%s getResult=%s'
4475 % (oTxsSession.isSuccess(), oTxsSession.getResult()));
4476 break;
4477
4478 if not self.handleTask(oTask, 'txsRunTestRedirectStd'):
4479 break;
4480
4481 self.removeTask(oTxsSession);
4482 if not oTxsSession.pollTask():
4483 oTxsSession.cancelTask();
4484 else:
4485 reporter.error('txsRunTestRedirectStd: asyncExec failed');
4486
4487 reporter.testDone();
4488 return fRc;
4489
4490 def txsRunTestStdIn(self, oTxsSession, sTestName, cMsTimeout, sExecName, asArgs = (), asAddEnv = (), sAsUser = "",
4491 sStdIn = None, fIgnoreErrors = False):
4492 """
4493 Executes the specified test task, waiting till it completes or times out.
4494 Redirecting simple string input into stdin, redirecting text stdout / stderr output to verbose logging.
4495
4496 The VM session (if any) must be in the task list.
4497
4498 Returns True if we executed the task and nothing abnormal happend.
4499 Query the process status from the TXS session.
4500
4501 Returns False if some unexpected task was signalled or we failed to
4502 submit the job.
4503 """
4504 assert sStdIn is not None;
4505 class StdInWrapper(object): # pylint: disable=too-few-public-methods
4506 """
4507 Wraps sStdIn in a file like class.
4508 """
4509 def __init__(self, sStdIn):
4510 self.sContent = sStdIn;
4511 self.off = 0;
4512
4513 def read(self, cbMax):
4514 """
4515 Returns next stdin input (up to cbMax), or an empty string if all input has been supplied already.
4516 """
4517 cbLeft = len(self.sContent) - self.off;
4518 if cbLeft == 0:
4519 return "";
4520 if cbLeft <= cbMax:
4521 sRet = self.sContent[self.off:(self.off + cbLeft)];
4522 else:
4523 sRet = self.sContent[self.off:(self.off + cbMax)];
4524 self.off = self.off + len(sRet);
4525 reporter.log2('Reading from stdin: "%s"' % (sRet,));
4526 return sRet;
4527
4528 return self.txsRunTestRedirectStd(oTxsSession, sTestName, cMsTimeout, sExecName, asArgs, asAddEnv, sAsUser,
4529 oStdIn = StdInWrapper(sStdIn),
4530 oStdErr = reporter.FileWrapper('stderr'), oStdOut = reporter.FileWrapper('stdout'),
4531 fIgnoreErrors = fIgnoreErrors);
4532
4533 def txsRunTest2(self, oTxsSession1, oTxsSession2, sTestName, cMsTimeout,
4534 sExecName1, asArgs1,
4535 sExecName2, asArgs2,
4536 asAddEnv1 = (), sAsUser1 = '', fWithTestPipe1 = True,
4537 asAddEnv2 = (), sAsUser2 = '', fWithTestPipe2 = True):
4538 """
4539 Executes the specified test tasks, waiting till they complete or
4540 times out. The 1st task is started after the 2nd one.
4541
4542 The VM session (if any) must be in the task list.
4543
4544 Returns True if we executed the task and nothing abnormal happend.
4545 Query the process status from the TXS sessions.
4546
4547 Returns False if some unexpected task was signalled or we failed to
4548 submit the job.
4549 """
4550 reporter.testStart(sTestName);
4551
4552 # Submit the jobs.
4553 fRc = False;
4554 if oTxsSession1.asyncExec(sExecName1, asArgs1, asAddEnv1, sAsUser1, fWithTestPipe1, '1-',
4555 self.adjustTimeoutMs(cMsTimeout)):
4556 self.addTask(oTxsSession1);
4557
4558 self.sleep(2); # fudge! grr
4559
4560 if oTxsSession2.asyncExec(sExecName2, asArgs2, asAddEnv2, sAsUser2, fWithTestPipe2, '2-',
4561 self.adjustTimeoutMs(cMsTimeout)):
4562 self.addTask(oTxsSession2);
4563
4564 # Wait for the jobs to complete.
4565 cPendingJobs = 2;
4566 while True:
4567 oTask = self.waitForTasks(cMsTimeout + 1);
4568 if oTask is None:
4569 reporter.log('txsRunTest2: waitForTasks timed out');
4570 break;
4571
4572 if oTask is oTxsSession1 or oTask is oTxsSession2:
4573 if oTask is oTxsSession1: iTask = 1;
4574 else: iTask = 2;
4575 reporter.log('txsRunTest2: #%u - isSuccess=%s getResult=%s' \
4576 % (iTask, oTask.isSuccess(), oTask.getResult()));
4577 self.removeTask(oTask);
4578 cPendingJobs -= 1;
4579 if cPendingJobs <= 0:
4580 fRc = True;
4581 break;
4582
4583 elif not self.handleTask(oTask, 'txsRunTest'):
4584 break;
4585
4586 self.removeTask(oTxsSession2);
4587 if not oTxsSession2.pollTask():
4588 oTxsSession2.cancelTask();
4589 else:
4590 reporter.error('txsRunTest2: asyncExec #2 failed');
4591
4592 self.removeTask(oTxsSession1);
4593 if not oTxsSession1.pollTask():
4594 oTxsSession1.cancelTask();
4595 else:
4596 reporter.error('txsRunTest2: asyncExec #1 failed');
4597
4598 reporter.testDone();
4599 return fRc;
4600
4601 # pylint: enable=too-many-locals,too-many-arguments
4602
4603
4604 #
4605 # Working with test results via serial port.
4606 #
4607
4608 class TxsMonitorComFile(base.TdTaskBase):
4609 """
4610 Class that monitors a COM output file.
4611 """
4612
4613 def __init__(self, sComRawFile, asStopWords = None):
4614 base.TdTaskBase.__init__(self, utils.getCallerName());
4615 self.sComRawFile = sComRawFile;
4616 self.oStopRegExp = re.compile('\\b(' + '|'.join(asStopWords if asStopWords else ('PASSED', 'FAILED',)) + ')\\b');
4617 self.sResult = None; ##< The result.
4618 self.cchDisplayed = 0; ##< Offset into the file string of what we've already fed to the logger.
4619
4620 def toString(self):
4621 return '<%s sComRawFile=%s oStopRegExp=%s sResult=%s cchDisplayed=%s>' \
4622 % (base.TdTaskBase.toString(self), self.sComRawFile, self.oStopRegExp, self.sResult, self.cchDisplayed,);
4623
4624 def pollTask(self, fLocked = False):
4625 """
4626 Overrides TdTaskBase.pollTask() for the purpose of polling the file.
4627 """
4628 if not fLocked:
4629 self.lockTask();
4630
4631 sFile = utils.noxcptReadFile(self.sComRawFile, '', 'rU');
4632 if len(sFile) > self.cchDisplayed:
4633 sNew = sFile[self.cchDisplayed:];
4634 oMatch = self.oStopRegExp.search(sNew);
4635 if oMatch:
4636 # Done! Get result, flush all the output and signal the task.
4637 self.sResult = oMatch.group(1);
4638 for sLine in sNew.split('\n'):
4639 reporter.log('COM OUTPUT: %s' % (sLine,));
4640 self.cchDisplayed = len(sFile);
4641 self.signalTaskLocked();
4642 else:
4643 # Output whole lines only.
4644 offNewline = sFile.find('\n', self.cchDisplayed);
4645 while offNewline >= 0:
4646 reporter.log('COM OUTPUT: %s' % (sFile[self.cchDisplayed:offNewline]))
4647 self.cchDisplayed = offNewline + 1;
4648 offNewline = sFile.find('\n', self.cchDisplayed);
4649
4650 fRet = self.fSignalled;
4651 if not fLocked:
4652 self.unlockTask();
4653 return fRet;
4654
4655 # Our stuff.
4656 def getResult(self):
4657 """
4658 Returns the connected TXS session object on success.
4659 Returns None on failure or if the task has not yet completed.
4660 """
4661 self.oCv.acquire();
4662 sResult = self.sResult;
4663 self.oCv.release();
4664 return sResult;
4665
4666 def cancelTask(self):
4667 """ Cancels the task. """
4668 self.signalTask();
4669 return True;
4670
4671
4672 def monitorComRawFile(self, oSession, sComRawFile, cMsTimeout = 15*60000, asStopWords = None):
4673 """
4674 Monitors the COM output file for stop words (PASSED and FAILED by default).
4675
4676 Returns the stop word.
4677 Returns None on VM error and timeout.
4678 """
4679
4680 reporter.log2('monitorComRawFile: oSession=%s, cMsTimeout=%s, sComRawFile=%s' % (oSession, cMsTimeout, sComRawFile));
4681
4682 oMonitorTask = self.TxsMonitorComFile(sComRawFile, asStopWords);
4683 self.addTask(oMonitorTask);
4684
4685 cMsTimeout = self.adjustTimeoutMs(cMsTimeout);
4686 oTask = self.waitForTasks(cMsTimeout + 1);
4687 reporter.log2('monitorComRawFile: waitForTasks returned %s' % (oTask,));
4688
4689 if oTask is not oMonitorTask:
4690 oMonitorTask.cancelTask();
4691 self.removeTask(oMonitorTask);
4692
4693 oMonitorTask.pollTask();
4694 return oMonitorTask.getResult();
4695
4696
4697 def runVmAndMonitorComRawFile(self, sVmName, sComRawFile, cMsTimeout = 15*60000, asStopWords = None):
4698 """
4699 Runs the specified VM and monitors the given COM output file for stop
4700 words (PASSED and FAILED by default).
4701
4702 The caller is assumed to have configured the VM to use the given
4703 file. The method will take no action to verify this.
4704
4705 Returns the stop word.
4706 Returns None on VM error and timeout.
4707 """
4708
4709 # Start the VM.
4710 reporter.log('runVmAndMonitorComRawFile: Starting(/preparing) "%s" (timeout %s s)...' % (sVmName, cMsTimeout / 1000));
4711 reporter.flushall();
4712 oSession = self.startVmByName(sVmName);
4713 if oSession is not None:
4714 # Let it run and then terminate it.
4715 sRet = self.monitorComRawFile(oSession, sComRawFile, cMsTimeout, asStopWords);
4716 self.terminateVmBySession(oSession);
4717 else:
4718 sRet = None;
4719 return sRet;
4720
4721 #
4722 # Other stuff
4723 #
4724
4725 def waitForGAs(self,
4726 oSession, # type: vboxwrappers.SessionWrapper
4727 cMsTimeout = 120000, aenmWaitForRunLevels = None, aenmWaitForActive = None, aenmWaitForInactive = None):
4728 """
4729 Waits for the guest additions to enter a certain state.
4730
4731 aenmWaitForRunLevels - List of run level values to wait for (success if one matches).
4732 aenmWaitForActive - List facilities (type values) that must be active.
4733 aenmWaitForInactive - List facilities (type values) that must be inactive.
4734
4735 Defaults to wait for AdditionsRunLevelType_Userland if nothing else is given.
4736
4737 Returns True on success, False w/ error logging on timeout or failure.
4738 """
4739 reporter.log2('waitForGAs: oSession=%s, cMsTimeout=%s' % (oSession, cMsTimeout,));
4740
4741 #
4742 # Get IGuest:
4743 #
4744 try:
4745 oIGuest = oSession.o.console.guest;
4746 except:
4747 return reporter.errorXcpt();
4748
4749 #
4750 # Create a wait task:
4751 #
4752 from testdriver.vboxwrappers import AdditionsStatusTask;
4753 try:
4754 oGaStatusTask = AdditionsStatusTask(oSession = oSession,
4755 oIGuest = oIGuest,
4756 cMsTimeout = cMsTimeout,
4757 aenmWaitForRunLevels = aenmWaitForRunLevels,
4758 aenmWaitForActive = aenmWaitForActive,
4759 aenmWaitForInactive = aenmWaitForInactive);
4760 except:
4761 return reporter.errorXcpt();
4762
4763 #
4764 # Add the task and make sure the VM session is also present.
4765 #
4766 self.addTask(oGaStatusTask);
4767 fRemoveSession = self.addTask(oSession);
4768 oTask = self.waitForTasks(cMsTimeout + 1);
4769 reporter.log2('waitForGAs: returned %s (oGaStatusTask=%s, oSession=%s)' % (oTask, oGaStatusTask, oSession,));
4770 self.removeTask(oGaStatusTask);
4771 if fRemoveSession:
4772 self.removeTask(oSession);
4773
4774 #
4775 # Digest the result.
4776 #
4777 if oTask is oGaStatusTask:
4778 fSucceeded = oGaStatusTask.getResult();
4779 if fSucceeded is True:
4780 reporter.log('waitForGAs: Succeeded.');
4781 else:
4782 reporter.error('waitForGAs: Failed.');
4783 else:
4784 oGaStatusTask.cancelTask();
4785 if oTask is None:
4786 reporter.error('waitForGAs: Timed out.');
4787 elif oTask is oSession:
4788 oSession.reportPrematureTermination('waitForGAs: ');
4789 else:
4790 reporter.error('waitForGAs: unknown/wrong task %s' % (oTask,));
4791 fSucceeded = False;
4792 return fSucceeded;
4793
4794 @staticmethod
4795 def controllerTypeToName(eControllerType):
4796 """
4797 Translate a controller type to a standard controller name.
4798 """
4799 if eControllerType in (vboxcon.StorageControllerType_PIIX3, vboxcon.StorageControllerType_PIIX4,):
4800 sName = "IDE Controller";
4801 elif eControllerType == vboxcon.StorageControllerType_IntelAhci:
4802 sName = "SATA Controller";
4803 elif eControllerType == vboxcon.StorageControllerType_LsiLogicSas:
4804 sName = "SAS Controller";
4805 elif eControllerType in (vboxcon.StorageControllerType_LsiLogic, vboxcon.StorageControllerType_BusLogic,):
4806 sName = "SCSI Controller";
4807 elif eControllerType == vboxcon.StorageControllerType_NVMe:
4808 sName = "NVMe Controller";
4809 elif eControllerType == vboxcon.StorageControllerType_VirtioSCSI:
4810 sName = "VirtIO SCSI Controller";
4811 else:
4812 sName = "Storage Controller";
4813 return sName;
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