VirtualBox

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

Last change on this file since 99235 was 99170, checked in by vboxsync, 23 months ago

Validation Kit/testdriver: Log task function callback names for easier debugging.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 206.6 KB
Line 
1# -*- coding: utf-8 -*-
2# $Id: vbox.py 99170 2023-03-24 09:57:55Z 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: 99170 $"
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 if self.fImportedVBoxApi:
1070 return True;
1071
1072 self._makeEnvironmentChanges();
1073
1074 # Do the detecting.
1075 self._detectBuild();
1076 if self.oBuild is None:
1077 return False;
1078
1079 # Avoid crashing when loading the 32-bit module (or whatever it is that goes bang).
1080 if self.oBuild.sArch == 'x86' \
1081 and self.sHost == 'darwin' \
1082 and platform.architecture()[0] == '64bit' \
1083 and self.oBuild.sKind == 'development' \
1084 and os.getenv('VERSIONER_PYTHON_PREFER_32_BIT') != 'yes':
1085 reporter.log("WARNING: 64-bit python on darwin, 32-bit VBox development build => crash");
1086 reporter.log("WARNING: bash-3.2$ /usr/bin/python2.5 ./testdriver");
1087 reporter.log("WARNING: or");
1088 reporter.log("WARNING: bash-3.2$ VERSIONER_PYTHON_PREFER_32_BIT=yes ./testdriver");
1089 return False;
1090
1091 # Start VBoxSVC and load the vboxapi bits.
1092 if self._startVBoxSVC() is True:
1093 assert(self.oVBoxSvcProcess is not None);
1094
1095 sSavedSysPath = sys.path;
1096 self._setupVBoxApi();
1097 sys.path = sSavedSysPath;
1098
1099 # Adjust the default machine folder.
1100 if self.fImportedVBoxApi and not self.fUseDefaultSvc and self.fpApiVer >= 4.0:
1101 sNewFolder = os.path.join(self.sScratchPath, 'VBoxUserHome', 'Machines');
1102 try:
1103 self.oVBox.systemProperties.defaultMachineFolder = sNewFolder;
1104 except:
1105 self.fImportedVBoxApi = False;
1106 self.oVBoxMgr = None;
1107 self.oVBox = None;
1108 reporter.logXcpt("defaultMachineFolder exception (sNewFolder=%s)" % (sNewFolder,));
1109
1110 # Kill VBoxSVC on failure.
1111 if self.oVBoxMgr is None:
1112 self._stopVBoxSVC();
1113 else:
1114 assert(self.oVBoxSvcProcess is None);
1115 return self.fImportedVBoxApi;
1116
1117 def _startVBoxSVC(self): # pylint: disable=too-many-statements
1118 """ Starts VBoxSVC. """
1119 assert(self.oVBoxSvcProcess is None);
1120
1121 # Setup vbox logging for VBoxSVC now and start it manually. This way
1122 # we can control both logging and shutdown.
1123 self.sVBoxSvcLogFile = '%s/VBoxSVC-debug.log' % (self.sScratchPath,);
1124 try: os.remove(self.sVBoxSvcLogFile);
1125 except: pass;
1126 os.environ['VBOX_LOG'] = self.sLogSvcGroups;
1127 os.environ['VBOX_LOG_FLAGS'] = '%s append' % (self.sLogSvcFlags,); # Append becuse of VBoxXPCOMIPCD.
1128 if self.sLogSvcDest:
1129 os.environ['VBOX_LOG_DEST'] = 'nodeny ' + self.sLogSvcDest;
1130 else:
1131 os.environ['VBOX_LOG_DEST'] = 'nodeny file=%s' % (self.sVBoxSvcLogFile,);
1132 os.environ['VBOXSVC_RELEASE_LOG_FLAGS'] = 'time append';
1133
1134 reporter.log2('VBoxSVC environment:');
1135 for sKey, sVal in sorted(os.environ.items()):
1136 reporter.log2('%s=%s' % (sKey, sVal));
1137
1138 # Always leave a pid file behind so we can kill it during cleanup-before.
1139 self.sVBoxSvcPidFile = '%s/VBoxSVC.pid' % (self.sScratchPath,);
1140 fWritePidFile = True;
1141
1142 cMsFudge = 1;
1143 sVBoxSVC = '%s/VBoxSVC' % (self.oBuild.sInstallPath,); ## @todo .exe and stuff.
1144 if self.fVBoxSvcInDebugger:
1145 if self.sHost in ('darwin', 'freebsd', 'linux', 'solaris', ):
1146 # Start VBoxSVC in gdb in a new terminal.
1147 #sTerm = '/usr/bin/gnome-terminal'; - doesn't work, some fork+exec stuff confusing us.
1148 sTerm = '/usr/bin/xterm';
1149 if not os.path.isfile(sTerm): sTerm = '/usr/X11/bin/xterm';
1150 if not os.path.isfile(sTerm): sTerm = '/usr/X11R6/bin/xterm';
1151 if not os.path.isfile(sTerm): sTerm = '/usr/bin/xterm';
1152 if not os.path.isfile(sTerm): sTerm = 'xterm';
1153 sGdb = '/usr/bin/gdb';
1154 if not os.path.isfile(sGdb): sGdb = '/usr/local/bin/gdb';
1155 if not os.path.isfile(sGdb): sGdb = '/usr/sfw/bin/gdb';
1156 if not os.path.isfile(sGdb): sGdb = 'gdb';
1157 sGdbCmdLine = '%s --args %s --pidfile %s' % (sGdb, sVBoxSVC, self.sVBoxSvcPidFile);
1158 # Cool tweak to run performance analysis instead of gdb:
1159 #sGdb = '/usr/bin/valgrind';
1160 #sGdbCmdLine = '%s --tool=callgrind --collect-atstart=no -- %s --pidfile %s' \
1161 # % (sGdb, sVBoxSVC, self.sVBoxSvcPidFile);
1162 reporter.log('term="%s" gdb="%s"' % (sTerm, sGdbCmdLine));
1163 os.environ['SHELL'] = self.sOrgShell; # Non-working shell may cause gdb and/or the term problems.
1164 ## @todo -e is deprecated; use "-- <args>".
1165 self.oVBoxSvcProcess = base.Process.spawnp(sTerm, sTerm, '-e', sGdbCmdLine);
1166 os.environ['SHELL'] = self.sOurShell;
1167 if self.oVBoxSvcProcess is not None:
1168 reporter.log('Press enter or return after starting VBoxSVC in the debugger...');
1169 sys.stdin.read(1);
1170 fWritePidFile = False;
1171
1172 elif self.sHost == 'win':
1173 sWinDbg = 'c:\\Program Files\\Debugging Tools for Windows\\windbg.exe';
1174 if not os.path.isfile(sWinDbg): sWinDbg = 'c:\\Program Files\\Debugging Tools for Windows (x64)\\windbg.exe';
1175 if not os.path.isfile(sWinDbg): sWinDbg = 'c:\\Programme\\Debugging Tools for Windows\\windbg.exe'; # Localization rulez! pylint: disable=line-too-long
1176 if not os.path.isfile(sWinDbg): sWinDbg = 'c:\\Programme\\Debugging Tools for Windows (x64)\\windbg.exe';
1177 if not os.path.isfile(sWinDbg): sWinDbg = 'windbg'; # WinDbg must be in the path; better than nothing.
1178 # Assume that everything WinDbg needs is defined using the environment variables.
1179 # See WinDbg help for more information.
1180 reporter.log('windbg="%s"' % (sWinDbg));
1181 self.oVBoxSvcProcess = base.Process.spawn(sWinDbg, sWinDbg, sVBoxSVC + base.exeSuff());
1182 if self.oVBoxSvcProcess is not None:
1183 reporter.log('Press enter or return after starting VBoxSVC in the debugger...');
1184 sys.stdin.read(1);
1185 fWritePidFile = False;
1186 ## @todo add a pipe interface similar to xpcom if feasible, i.e. if
1187 # we can get actual handle values for pipes in python.
1188
1189 else:
1190 reporter.error('Port me!');
1191 else: # Run without a debugger attached.
1192 if self.sHost in ('darwin', 'freebsd', 'linux', 'solaris', ):
1193 #
1194 # XPCOM - We can use a pipe to let VBoxSVC notify us when it's ready.
1195 #
1196 iPipeR, iPipeW = os.pipe();
1197 if hasattr(os, 'set_inheritable'):
1198 os.set_inheritable(iPipeW, True); # pylint: disable=no-member
1199 os.environ['NSPR_INHERIT_FDS'] = 'vboxsvc:startup-pipe:5:0x%x' % (iPipeW,);
1200 reporter.log2("NSPR_INHERIT_FDS=%s" % (os.environ['NSPR_INHERIT_FDS']));
1201
1202 self.oVBoxSvcProcess = base.Process.spawn(sVBoxSVC, sVBoxSVC, '--auto-shutdown'); # SIGUSR1 requirement.
1203 try: # Try make sure we get the SIGINT and not VBoxSVC.
1204 os.setpgid(self.oVBoxSvcProcess.getPid(), 0); # pylint: disable=no-member
1205 os.setpgid(0, 0); # pylint: disable=no-member
1206 except:
1207 reporter.logXcpt();
1208
1209 os.close(iPipeW);
1210 try:
1211 sResponse = os.read(iPipeR, 32);
1212 except:
1213 reporter.logXcpt();
1214 sResponse = None;
1215 os.close(iPipeR);
1216
1217 if hasattr(sResponse, 'decode'):
1218 sResponse = sResponse.decode('utf-8', 'ignore');
1219
1220 if sResponse is None or sResponse.strip() != 'READY':
1221 reporter.error('VBoxSVC failed starting up... (sResponse=%s)' % (sResponse,));
1222 if not self.oVBoxSvcProcess.wait(5000):
1223 self.oVBoxSvcProcess.terminate();
1224 self.oVBoxSvcProcess.wait(5000);
1225 self.oVBoxSvcProcess = None;
1226
1227 elif self.sHost == 'win':
1228 #
1229 # Windows - Just fudge it for now.
1230 #
1231 cMsFudge = 2000;
1232 self.oVBoxSvcProcess = base.Process.spawn(sVBoxSVC, sVBoxSVC);
1233
1234 else:
1235 reporter.error('Port me!');
1236
1237 #
1238 # Enable automatic crash reporting if we succeeded.
1239 #
1240 if self.oVBoxSvcProcess is not None:
1241 self.oVBoxSvcProcess.enableCrashReporting('crash/report/svc', 'crash/dump/svc');
1242
1243 #
1244 # Wait for debugger to attach.
1245 #
1246 if self.oVBoxSvcProcess is not None and self.fVBoxSvcWaitForDebugger:
1247 reporter.log('Press any key after attaching to VBoxSVC (pid %s) with a debugger...'
1248 % (self.oVBoxSvcProcess.getPid(),));
1249 sys.stdin.read(1);
1250
1251 #
1252 # Fudge and pid file.
1253 #
1254 if self.oVBoxSvcProcess is not None and not self.oVBoxSvcProcess.wait(cMsFudge):
1255 if fWritePidFile:
1256 iPid = self.oVBoxSvcProcess.getPid();
1257 try:
1258 oFile = utils.openNoInherit(self.sVBoxSvcPidFile, "w+");
1259 oFile.write('%s' % (iPid,));
1260 oFile.close();
1261 except:
1262 reporter.logXcpt('sPidFile=%s' % (self.sVBoxSvcPidFile,));
1263 reporter.log('VBoxSVC PID=%u' % (iPid,));
1264
1265 #
1266 # Finally add the task so we'll notice when it dies in a relatively timely manner.
1267 #
1268 self.addTask(self.oVBoxSvcProcess);
1269 else:
1270 self.oVBoxSvcProcess = None;
1271 try: os.remove(self.sVBoxSvcPidFile);
1272 except: pass;
1273
1274 return self.oVBoxSvcProcess is not None;
1275
1276
1277 def _killVBoxSVCByPidFile(self, sPidFile):
1278 """ Kill a VBoxSVC given the pid from it's pid file. """
1279
1280 # Read the pid file.
1281 if not os.path.isfile(sPidFile):
1282 return False;
1283 try:
1284 oFile = utils.openNoInherit(sPidFile, "r");
1285 sPid = oFile.readline().strip();
1286 oFile.close();
1287 except:
1288 reporter.logXcpt('sPidfile=%s' % (sPidFile,));
1289 return False;
1290
1291 # Convert the pid to an integer and validate the range a little bit.
1292 try:
1293 iPid = long(sPid);
1294 except:
1295 reporter.logXcpt('sPidfile=%s sPid="%s"' % (sPidFile, sPid));
1296 return False;
1297 if iPid <= 0:
1298 reporter.log('negative pid - sPidfile=%s sPid="%s" iPid=%d' % (sPidFile, sPid, iPid));
1299 return False;
1300
1301 # Take care checking that it's VBoxSVC we're about to inhume.
1302 if base.processCheckPidAndName(iPid, "VBoxSVC") is not True:
1303 reporter.log('Ignoring stale VBoxSVC pid file (pid=%s)' % (iPid,));
1304 return False;
1305
1306 # Loop thru our different ways of getting VBoxSVC to terminate.
1307 for aHow in [ [ base.sendUserSignal1, 5000, 'Dropping VBoxSVC a SIGUSR1 hint...'], \
1308 [ base.processInterrupt, 5000, 'Dropping VBoxSVC a SIGINT hint...'], \
1309 [ base.processTerminate, 7500, 'VBoxSVC is still around, killing it...'] ]:
1310 reporter.log(aHow[2]);
1311 if aHow[0](iPid) is True:
1312 msStart = base.timestampMilli();
1313 while base.timestampMilli() - msStart < 5000 \
1314 and base.processExists(iPid):
1315 time.sleep(0.2);
1316
1317 fRc = not base.processExists(iPid);
1318 if fRc is True:
1319 break;
1320 if fRc:
1321 reporter.log('Successfully killed VBoxSVC (pid=%s)' % (iPid,));
1322 else:
1323 reporter.log('Failed to kill VBoxSVC (pid=%s)' % (iPid,));
1324 return fRc;
1325
1326 def _stopVBoxSVC(self):
1327 """
1328 Stops VBoxSVC. Try the polite way first.
1329 """
1330
1331 if self.oVBoxSvcProcess:
1332 self.removeTask(self.oVBoxSvcProcess);
1333 self.oVBoxSvcProcess.enableCrashReporting(None, None); # Disables it.
1334
1335 fRc = False;
1336 if self.oVBoxSvcProcess is not None \
1337 and not self.fVBoxSvcInDebugger:
1338 # by process object.
1339 if self.oVBoxSvcProcess.isRunning():
1340 reporter.log('Dropping VBoxSVC a SIGUSR1 hint...');
1341 if not self.oVBoxSvcProcess.sendUserSignal1() \
1342 or not self.oVBoxSvcProcess.wait(5000):
1343 reporter.log('Dropping VBoxSVC a SIGINT hint...');
1344 if not self.oVBoxSvcProcess.interrupt() \
1345 or not self.oVBoxSvcProcess.wait(5000):
1346 reporter.log('VBoxSVC is still around, killing it...');
1347 self.oVBoxSvcProcess.terminate();
1348 self.oVBoxSvcProcess.wait(7500);
1349 else:
1350 reporter.log('VBoxSVC is no longer running...');
1351
1352 if not self.oVBoxSvcProcess.isRunning():
1353 iExit = self.oVBoxSvcProcess.getExitCode();
1354 if iExit != 0 or not self.oVBoxSvcProcess.isNormalExit():
1355 reporter.error("VBoxSVC exited with status %d (%#x)" % (iExit, self.oVBoxSvcProcess.uExitCode));
1356 self.oVBoxSvcProcess = None;
1357 else:
1358 # by pid file.
1359 self._killVBoxSVCByPidFile('%s/VBoxSVC.pid' % (self.sScratchPath,));
1360 return fRc;
1361
1362 def _setupVBoxApi(self):
1363 """
1364 Import and set up the vboxapi.
1365 The caller saves and restores sys.path.
1366 """
1367
1368 # Setup vbox logging for self (the test driver).
1369 self.sSelfLogFile = '%s/VBoxTestDriver.log' % (self.sScratchPath,);
1370 try: os.remove(self.sSelfLogFile);
1371 except: pass;
1372 os.environ['VBOX_LOG'] = self.sLogSelfGroups;
1373 os.environ['VBOX_LOG_FLAGS'] = '%s append' % (self.sLogSelfFlags, );
1374 if self.sLogSelfDest:
1375 os.environ['VBOX_LOG_DEST'] = 'nodeny ' + self.sLogSelfDest;
1376 else:
1377 os.environ['VBOX_LOG_DEST'] = 'nodeny file=%s' % (self.sSelfLogFile,);
1378 os.environ['VBOX_RELEASE_LOG_FLAGS'] = 'time append';
1379
1380 reporter.log2('Self environment:');
1381 for sKey, sVal in sorted(os.environ.items()):
1382 reporter.log2('%s=%s' % (sKey, sVal));
1383
1384 # Hack the sys.path + environment so the vboxapi can be found.
1385 sys.path.insert(0, self.oBuild.sInstallPath);
1386 if self.oBuild.sSdkPath is not None:
1387 sys.path.insert(0, os.path.join(self.oBuild.sSdkPath, 'installer'))
1388 sys.path.insert(1, os.path.join(self.oBuild.sSdkPath, 'install')); # stupid stupid windows installer!
1389 sys.path.insert(2, os.path.join(self.oBuild.sSdkPath, 'bindings', 'xpcom', 'python'))
1390 os.environ['VBOX_PROGRAM_PATH'] = self.oBuild.sInstallPath;
1391 reporter.log("sys.path: %s" % (sys.path));
1392
1393 try:
1394 from vboxapi import VirtualBoxManager; # pylint: disable=import-error
1395 except:
1396 reporter.logXcpt('Error importing vboxapi');
1397 return False;
1398
1399 # Exception and error hacks.
1400 try:
1401 # pylint: disable=import-error
1402 if self.sHost == 'win':
1403 from pythoncom import com_error as NativeComExceptionClass # pylint: disable=no-name-in-module
1404 import winerror as NativeComErrorClass
1405 else:
1406 from xpcom import Exception as NativeComExceptionClass
1407 from xpcom import nsError as NativeComErrorClass
1408 # pylint: enable=import-error
1409 except:
1410 reporter.logXcpt('Error importing (XP)COM related stuff for exception hacks and errors');
1411 return False;
1412 __deployExceptionHacks__(NativeComExceptionClass)
1413 ComError.copyErrors(NativeComErrorClass);
1414
1415 # Create the manager.
1416 try:
1417 self.oVBoxMgr = VirtualBoxManager(None, None)
1418 except:
1419 self.oVBoxMgr = None;
1420 reporter.logXcpt('VirtualBoxManager exception');
1421 return False;
1422
1423 # Figure the API version.
1424 try:
1425 oVBox = self.oVBoxMgr.getVirtualBox();
1426
1427 try:
1428 sVer = oVBox.version;
1429 except:
1430 reporter.logXcpt('Failed to get VirtualBox version, assuming 4.0.0');
1431 sVer = "4.0.0";
1432 reporter.log("IVirtualBox.version=%s" % (sVer,));
1433
1434 # Convert the string to three integer values and check ranges.
1435 asVerComponents = sVer.split('.');
1436 try:
1437 sLast = asVerComponents[2].split('_')[0].split('r')[0];
1438 aiVerComponents = (int(asVerComponents[0]), int(asVerComponents[1]), int(sLast));
1439 except:
1440 raise base.GenError('Malformed version "%s"' % (sVer,));
1441 if aiVerComponents[0] < 3 or aiVerComponents[0] > 19:
1442 raise base.GenError('Malformed version "%s" - 1st component is out of bounds 3..19: %u'
1443 % (sVer, aiVerComponents[0]));
1444 if aiVerComponents[1] < 0 or aiVerComponents[1] > 9:
1445 raise base.GenError('Malformed version "%s" - 2nd component is out of bounds 0..9: %u'
1446 % (sVer, aiVerComponents[1]));
1447 if aiVerComponents[2] < 0 or aiVerComponents[2] > 99:
1448 raise base.GenError('Malformed version "%s" - 3rd component is out of bounds 0..99: %u'
1449 % (sVer, aiVerComponents[2]));
1450
1451 # Convert the three integers into a floating point value. The API is stable within a
1452 # x.y release, so the third component only indicates whether it's a stable or
1453 # development build of the next release.
1454 self.fpApiVer = aiVerComponents[0] + 0.1 * aiVerComponents[1];
1455 if aiVerComponents[2] >= 51:
1456 if self.fpApiVer not in [6.1, 5.2, 4.3, 3.2,]:
1457 self.fpApiVer += 0.1;
1458 else:
1459 self.fpApiVer = int(self.fpApiVer) + 1.0;
1460 # fudge value to be always bigger than the nominal value (0.1 gets rounded down)
1461 if round(self.fpApiVer, 1) > self.fpApiVer:
1462 self.fpApiVer += sys.float_info.epsilon * self.fpApiVer / 2.0;
1463
1464 try:
1465 self.uRevision = oVBox.revision;
1466 except:
1467 reporter.logXcpt('Failed to get VirtualBox revision, assuming 0');
1468 self.uRevision = 0;
1469 reporter.log("IVirtualBox.revision=%u" % (self.uRevision,));
1470
1471 try:
1472 self.uApiRevision = oVBox.APIRevision;
1473 except:
1474 reporter.logXcpt('Failed to get VirtualBox APIRevision, faking it.');
1475 self.uApiRevision = self.makeApiRevision(aiVerComponents[0], aiVerComponents[1], aiVerComponents[2], 0);
1476 reporter.log("IVirtualBox.APIRevision=%#x" % (self.uApiRevision,));
1477
1478 # Patch VBox manage to gloss over portability issues (error constants, etc).
1479 self._patchVBoxMgr();
1480
1481 # Wrap oVBox.
1482 from testdriver.vboxwrappers import VirtualBoxWrapper;
1483 self.oVBox = VirtualBoxWrapper(oVBox, self.oVBoxMgr, self.fpApiVer, self);
1484
1485 # Install the constant wrapping hack.
1486 vboxcon.goHackModuleClass.oVBoxMgr = self.oVBoxMgr; # VBoxConstantWrappingHack.
1487 vboxcon.fpApiVer = self.fpApiVer;
1488 reporter.setComXcptFormatter(formatComOrXpComException);
1489
1490 except:
1491 self.oVBoxMgr = None;
1492 self.oVBox = None;
1493 reporter.logXcpt("getVirtualBox / API version exception");
1494 return False;
1495
1496 # Done
1497 self.fImportedVBoxApi = True;
1498 reporter.log('Found version %s (%s)' % (self.fpApiVer, sVer));
1499 return True;
1500
1501 def _patchVBoxMgr(self):
1502 """
1503 Glosses over missing self.oVBoxMgr methods on older VBox versions.
1504 """
1505
1506 def _xcptGetResult(oSelf, oXcpt = None):
1507 """ See vboxapi. """
1508 _ = oSelf;
1509 if oXcpt is None: oXcpt = sys.exc_info()[1];
1510 if sys.platform == 'win32':
1511 import winerror; # pylint: disable=import-error
1512 hrXcpt = oXcpt.hresult;
1513 if hrXcpt == winerror.DISP_E_EXCEPTION:
1514 hrXcpt = oXcpt.excepinfo[5];
1515 else:
1516 hrXcpt = oXcpt.error;
1517 return hrXcpt;
1518
1519 def _xcptIsDeadInterface(oSelf, oXcpt = None):
1520 """ See vboxapi. """
1521 return oSelf.xcptGetStatus(oXcpt) in [
1522 0x80004004, -2147467260, # NS_ERROR_ABORT
1523 0x800706be, -2147023170, # NS_ERROR_CALL_FAILED (RPC_S_CALL_FAILED)
1524 0x800706ba, -2147023174, # RPC_S_SERVER_UNAVAILABLE.
1525 0x800706be, -2147023170, # RPC_S_CALL_FAILED.
1526 0x800706bf, -2147023169, # RPC_S_CALL_FAILED_DNE.
1527 0x80010108, -2147417848, # RPC_E_DISCONNECTED.
1528 0x800706b5, -2147023179, # RPC_S_UNKNOWN_IF
1529 ];
1530
1531 def _xcptIsOurXcptKind(oSelf, oXcpt = None):
1532 """ See vboxapi. """
1533 _ = oSelf;
1534 if oXcpt is None: oXcpt = sys.exc_info()[1];
1535 if sys.platform == 'win32':
1536 from pythoncom import com_error as NativeComExceptionClass # pylint: disable=import-error,no-name-in-module
1537 else:
1538 from xpcom import Exception as NativeComExceptionClass # pylint: disable=import-error
1539 return isinstance(oXcpt, NativeComExceptionClass);
1540
1541 def _xcptIsEqual(oSelf, oXcpt, hrStatus):
1542 """ See vboxapi. """
1543 hrXcpt = oSelf.xcptGetResult(oXcpt);
1544 return hrXcpt == hrStatus or hrXcpt == hrStatus - 0x100000000; # pylint: disable=consider-using-in
1545
1546 def _xcptToString(oSelf, oXcpt):
1547 """ See vboxapi. """
1548 _ = oSelf;
1549 if oXcpt is None: oXcpt = sys.exc_info()[1];
1550 return str(oXcpt);
1551
1552 def _getEnumValueName(oSelf, sEnumTypeNm, oEnumValue, fTypePrefix = False):
1553 """ See vboxapi. """
1554 _ = oSelf; _ = fTypePrefix;
1555 return '%s::%s' % (sEnumTypeNm, oEnumValue);
1556
1557 # Add utilities found in newer vboxapi revision.
1558 if not hasattr(self.oVBoxMgr, 'xcptIsDeadInterface'):
1559 import types;
1560 self.oVBoxMgr.xcptGetResult = types.MethodType(_xcptGetResult, self.oVBoxMgr);
1561 self.oVBoxMgr.xcptIsDeadInterface = types.MethodType(_xcptIsDeadInterface, self.oVBoxMgr);
1562 self.oVBoxMgr.xcptIsOurXcptKind = types.MethodType(_xcptIsOurXcptKind, self.oVBoxMgr);
1563 self.oVBoxMgr.xcptIsEqual = types.MethodType(_xcptIsEqual, self.oVBoxMgr);
1564 self.oVBoxMgr.xcptToString = types.MethodType(_xcptToString, self.oVBoxMgr);
1565 if not hasattr(self.oVBoxMgr, 'getEnumValueName'):
1566 import types;
1567 self.oVBoxMgr.getEnumValueName = types.MethodType(_getEnumValueName, self.oVBoxMgr);
1568
1569
1570 def _teardownVBoxApi(self): # pylint: disable=too-many-statements
1571 """
1572 Drop all VBox object references and shutdown com/xpcom.
1573 """
1574 if not self.fImportedVBoxApi:
1575 return True;
1576 import gc;
1577
1578 # Drop all references we've have to COM objects.
1579 self.aoRemoteSessions = [];
1580 self.aoVMs = [];
1581 self.oVBoxMgr = None;
1582 self.oVBox = None;
1583 vboxcon.goHackModuleClass.oVBoxMgr = None; # VBoxConstantWrappingHack.
1584 reporter.setComXcptFormatter(None);
1585
1586 # Do garbage collection to try get rid of those objects.
1587 try:
1588 gc.collect();
1589 except:
1590 reporter.logXcpt();
1591 self.fImportedVBoxApi = False;
1592
1593 # Check whether the python is still having any COM objects/interfaces around.
1594 cVBoxMgrs = 0;
1595 aoObjsLeftBehind = [];
1596 if self.sHost == 'win':
1597 import pythoncom; # pylint: disable=import-error
1598 try:
1599 cIfs = pythoncom._GetInterfaceCount(); # pylint: disable=no-member,protected-access
1600 cObjs = pythoncom._GetGatewayCount(); # pylint: disable=no-member,protected-access
1601 if cObjs == 0 and cIfs == 0:
1602 reporter.log('_teardownVBoxApi: no interfaces or objects left behind.');
1603 else:
1604 reporter.log('_teardownVBoxApi: Python COM still has %s objects and %s interfaces...' % ( cObjs, cIfs));
1605
1606 from win32com.client import DispatchBaseClass; # pylint: disable=import-error
1607 for oObj in gc.get_objects():
1608 if isinstance(oObj, DispatchBaseClass):
1609 reporter.log('_teardownVBoxApi: %s' % (oObj,));
1610 aoObjsLeftBehind.append(oObj);
1611 elif utils.getObjectTypeName(oObj) == 'VirtualBoxManager':
1612 reporter.log('_teardownVBoxApi: %s' % (oObj,));
1613 cVBoxMgrs += 1;
1614 aoObjsLeftBehind.append(oObj);
1615 oObj = None;
1616 except:
1617 reporter.logXcpt();
1618
1619 # If not being used, we can safely uninitialize COM.
1620 if cIfs == 0 and cObjs == 0 and cVBoxMgrs == 0 and not aoObjsLeftBehind:
1621 reporter.log('_teardownVBoxApi: Calling CoUninitialize...');
1622 try: pythoncom.CoUninitialize(); # pylint: disable=no-member
1623 except: reporter.logXcpt();
1624 else:
1625 reporter.log('_teardownVBoxApi: Returned from CoUninitialize.');
1626 else:
1627 try:
1628 # XPCOM doesn't crash and burn like COM if you shut it down with interfaces and objects around.
1629 # Also, it keeps a number of internal objects and interfaces around to do its job, so shutting
1630 # it down before we go looking for dangling interfaces is more or less required.
1631 from xpcom import _xpcom as _xpcom; # pylint: disable=import-error,useless-import-alias
1632 hrc = _xpcom.DeinitCOM();
1633 cIfs = _xpcom._GetInterfaceCount(); # pylint: disable=protected-access
1634 cObjs = _xpcom._GetGatewayCount(); # pylint: disable=protected-access
1635
1636 if cObjs == 0 and cIfs == 0:
1637 reporter.log('_teardownVBoxApi: No XPCOM interfaces or objects active. (hrc=%#x)' % (hrc,));
1638 else:
1639 reporter.log('_teardownVBoxApi: %s XPCOM objects and %s interfaces still around! (hrc=%#x)'
1640 % (cObjs, cIfs, hrc));
1641 if hasattr(_xpcom, '_DumpInterfaces'):
1642 try: _xpcom._DumpInterfaces(); # pylint: disable=protected-access
1643 except: reporter.logXcpt('_teardownVBoxApi: _DumpInterfaces failed');
1644
1645 from xpcom.client import Component; # pylint: disable=import-error
1646 for oObj in gc.get_objects():
1647 if isinstance(oObj, Component):
1648 reporter.log('_teardownVBoxApi: %s' % (oObj,));
1649 aoObjsLeftBehind.append(oObj);
1650 if utils.getObjectTypeName(oObj) == 'VirtualBoxManager':
1651 reporter.log('_teardownVBoxApi: %s' % (oObj,));
1652 cVBoxMgrs += 1;
1653 aoObjsLeftBehind.append(oObj);
1654 oObj = None;
1655 except:
1656 reporter.logXcpt();
1657
1658 # Try get the referrers to (XP)COM interfaces and objects that was left behind.
1659 for iObj in range(len(aoObjsLeftBehind)): # pylint: disable=consider-using-enumerate
1660 try:
1661 aoReferrers = gc.get_referrers(aoObjsLeftBehind[iObj]);
1662 reporter.log('_teardownVBoxApi: Found %u referrers to %s:' % (len(aoReferrers), aoObjsLeftBehind[iObj],));
1663 for oReferrer in aoReferrers:
1664 oMyFrame = sys._getframe(0); # pylint: disable=protected-access
1665 if oReferrer is oMyFrame:
1666 reporter.log('_teardownVBoxApi: - frame of this function');
1667 elif oReferrer is aoObjsLeftBehind:
1668 reporter.log('_teardownVBoxApi: - aoObjsLeftBehind');
1669 else:
1670 fPrinted = False;
1671 if isinstance(oReferrer, (dict, list, tuple)):
1672 try:
1673 aoSubReferreres = gc.get_referrers(oReferrer);
1674 for oSubRef in aoSubReferreres:
1675 if not isinstance(oSubRef, list) \
1676 and not isinstance(oSubRef, dict) \
1677 and oSubRef is not oMyFrame \
1678 and oSubRef is not aoSubReferreres:
1679 reporter.log('_teardownVBoxApi: - %s :: %s:'
1680 % (utils.getObjectTypeName(oSubRef), utils.getObjectTypeName(oReferrer)));
1681 fPrinted = True;
1682 break;
1683 del aoSubReferreres;
1684 except:
1685 reporter.logXcpt('subref');
1686 if not fPrinted:
1687 reporter.log('_teardownVBoxApi: - %s:' % (utils.getObjectTypeName(oReferrer),));
1688 try:
1689 import pprint;
1690 for sLine in pprint.pformat(oReferrer, width = 130).split('\n'):
1691 reporter.log('_teardownVBoxApi: %s' % (sLine,));
1692 except:
1693 reporter.log('_teardownVBoxApi: %s' % (oReferrer,));
1694 except:
1695 reporter.logXcpt();
1696 del aoObjsLeftBehind;
1697
1698 # Force garbage collection again, just for good measure.
1699 try:
1700 gc.collect();
1701 time.sleep(0.5); # fudge factor
1702 except:
1703 reporter.logXcpt();
1704 return True;
1705
1706 def _powerOffAllVms(self):
1707 """
1708 Tries to power off all running VMs.
1709 """
1710 for oSession in self.aoRemoteSessions:
1711 uPid = oSession.getPid();
1712 if uPid is not None:
1713 reporter.log('_powerOffAllVms: PID is %s for %s, trying to kill it.' % (uPid, oSession.sName,));
1714 base.processKill(uPid);
1715 else:
1716 reporter.log('_powerOffAllVms: No PID for %s' % (oSession.sName,));
1717 oSession.close();
1718 return None;
1719
1720
1721
1722 #
1723 # Build type, OS and arch getters.
1724 #
1725
1726 def getBuildType(self):
1727 """
1728 Get the build type.
1729 """
1730 if not self._detectBuild():
1731 return 'release';
1732 return self.oBuild.sType;
1733
1734 def getBuildOs(self):
1735 """
1736 Get the build OS.
1737 """
1738 if not self._detectBuild():
1739 return self.sHost;
1740 return self.oBuild.sOs;
1741
1742 def getBuildArch(self):
1743 """
1744 Get the build arch.
1745 """
1746 if not self._detectBuild():
1747 return self.sHostArch;
1748 return self.oBuild.sArch;
1749
1750 def getGuestAdditionsIso(self):
1751 """
1752 Get the path to the guest addition iso.
1753 """
1754 if not self._detectBuild():
1755 return None;
1756 return self.oBuild.sGuestAdditionsIso;
1757
1758 #
1759 # Override everything from the base class so the testdrivers don't have to
1760 # check whether we have overridden a method or not.
1761 #
1762
1763 def showUsage(self):
1764 rc = base.TestDriver.showUsage(self);
1765 reporter.log('');
1766 reporter.log('Generic VirtualBox Options:');
1767 reporter.log(' --vbox-session-type <type>');
1768 reporter.log(' Sets the session type. Typical values are: gui, headless, sdl');
1769 reporter.log(' Default: %s' % (self.sSessionTypeDef));
1770 reporter.log(' --vrdp, --no-vrdp');
1771 reporter.log(' Enables VRDP, ports starting at 6000');
1772 reporter.log(' Default: --vrdp');
1773 reporter.log(' --vrdp-base-port <port>');
1774 reporter.log(' Sets the base for VRDP port assignments.');
1775 reporter.log(' Default: %s' % (self.uVrdpBasePortDef));
1776 reporter.log(' --vbox-default-bridged-nic <interface>');
1777 reporter.log(' Sets the default interface for bridged networking.');
1778 reporter.log(' Default: autodetect');
1779 reporter.log(' --vbox-use-svc-defaults');
1780 reporter.log(' Use default locations and files for VBoxSVC. This is useful');
1781 reporter.log(' for automatically configuring the test VMs for debugging.');
1782 reporter.log(' --vbox-log');
1783 reporter.log(' The VBox logger group settings for everyone.');
1784 reporter.log(' --vbox-log-flags');
1785 reporter.log(' The VBox logger flags settings for everyone.');
1786 reporter.log(' --vbox-log-dest');
1787 reporter.log(' The VBox logger destination settings for everyone.');
1788 reporter.log(' --vbox-self-log');
1789 reporter.log(' The VBox logger group settings for the testdriver.');
1790 reporter.log(' --vbox-self-log-flags');
1791 reporter.log(' The VBox logger flags settings for the testdriver.');
1792 reporter.log(' --vbox-self-log-dest');
1793 reporter.log(' The VBox logger destination settings for the testdriver.');
1794 reporter.log(' --vbox-session-log');
1795 reporter.log(' The VM session logger group settings.');
1796 reporter.log(' --vbox-session-log-flags');
1797 reporter.log(' The VM session logger flags.');
1798 reporter.log(' --vbox-session-log-dest');
1799 reporter.log(' The VM session logger destination settings.');
1800 reporter.log(' --vbox-svc-log');
1801 reporter.log(' The VBoxSVC logger group settings.');
1802 reporter.log(' --vbox-svc-log-flags');
1803 reporter.log(' The VBoxSVC logger flag settings.');
1804 reporter.log(' --vbox-svc-log-dest');
1805 reporter.log(' The VBoxSVC logger destination settings.');
1806 reporter.log(' --vbox-svc-debug');
1807 reporter.log(' Start VBoxSVC in a debugger.');
1808 reporter.log(' --vbox-svc-wait-debug');
1809 reporter.log(' Start VBoxSVC and wait for debugger to attach to it.');
1810 reporter.log(' --vbox-always-upload-logs');
1811 reporter.log(' Whether to always upload log files, or only do so on failure.');
1812 reporter.log(' --vbox-always-upload-screenshots');
1813 reporter.log(' Whether to always upload final screen shots, or only do so on failure.');
1814 reporter.log(' --vbox-always-upload-recordings, --no-vbox-always-upload-recordings');
1815 reporter.log(' Whether to always upload recordings, or only do so on failure.');
1816 reporter.log(' Default: --no-vbox-always-upload-recordings');
1817 reporter.log(' --vbox-debugger, --no-vbox-debugger');
1818 reporter.log(' Enables the VBox debugger, port at 5000');
1819 reporter.log(' Default: --vbox-debugger');
1820 reporter.log(' --vbox-recording, --no-vbox-recording');
1821 reporter.log(' Enables/disables recording.');
1822 reporter.log(' Default: --no-vbox-recording');
1823 reporter.log(' --vbox-recording-audio, --no-vbox-recording-audio');
1824 reporter.log(' Enables/disables audio recording.');
1825 reporter.log(' Default: --no-vbox-recording-audio');
1826 reporter.log(' --vbox-recording-max-time <seconds>');
1827 reporter.log(' Limits the maximum recording time in seconds.');
1828 reporter.log(' Default: Unlimited.');
1829 reporter.log(' --vbox-recording-max-file-size <MiB>');
1830 reporter.log(' Limits the maximum per-file size in MiB.');
1831 reporter.log(' Explicitly specify 0 for unlimited size.');
1832 reporter.log(' Default: 195 MB.');
1833 reporter.log(' --vbox-vm-no-terminate');
1834 reporter.log(' Does not terminate the test VM after running the test driver.');
1835 if self.oTestVmSet is not None:
1836 self.oTestVmSet.showUsage();
1837 return rc;
1838
1839 def parseOption(self, asArgs, iArg): # pylint: disable=too-many-branches,too-many-statements
1840 if asArgs[iArg] == '--vbox-session-type':
1841 iArg += 1;
1842 if iArg >= len(asArgs):
1843 raise base.InvalidOption('The "--vbox-session-type" takes an argument');
1844 self.sSessionType = asArgs[iArg];
1845 elif asArgs[iArg] == '--vrdp':
1846 self.fEnableVrdp = True;
1847 elif asArgs[iArg] == '--no-vrdp':
1848 self.fEnableVrdp = False;
1849 elif asArgs[iArg] == '--vrdp-base-port':
1850 iArg += 1;
1851 if iArg >= len(asArgs):
1852 raise base.InvalidOption('The "--vrdp-base-port" takes an argument');
1853 try: self.uVrdpBasePort = int(asArgs[iArg]);
1854 except: raise base.InvalidOption('The "--vrdp-base-port" value "%s" is not a valid integer' % (asArgs[iArg],));
1855 if self.uVrdpBasePort <= 0 or self.uVrdpBasePort >= 65530:
1856 raise base.InvalidOption('The "--vrdp-base-port" value "%s" is not in the valid range (1..65530)'
1857 % (asArgs[iArg],));
1858 elif asArgs[iArg] == '--vbox-default-bridged-nic':
1859 iArg += 1;
1860 if iArg >= len(asArgs):
1861 raise base.InvalidOption('The "--vbox-default-bridged-nic" takes an argument');
1862 self.sDefBridgedNic = asArgs[iArg];
1863 elif asArgs[iArg] == '--vbox-use-svc-defaults':
1864 self.fUseDefaultSvc = True;
1865 elif asArgs[iArg] == '--vbox-self-log':
1866 iArg += 1;
1867 if iArg >= len(asArgs):
1868 raise base.InvalidOption('The "--vbox-self-log" takes an argument');
1869 self.sLogSelfGroups = asArgs[iArg];
1870 elif asArgs[iArg] == '--vbox-self-log-flags':
1871 iArg += 1;
1872 if iArg >= len(asArgs):
1873 raise base.InvalidOption('The "--vbox-self-log-flags" takes an argument');
1874 self.sLogSelfFlags = asArgs[iArg];
1875 elif asArgs[iArg] == '--vbox-self-log-dest':
1876 iArg += 1;
1877 if iArg >= len(asArgs):
1878 raise base.InvalidOption('The "--vbox-self-log-dest" takes an argument');
1879 self.sLogSelfDest = asArgs[iArg];
1880 elif asArgs[iArg] == '--vbox-session-log':
1881 iArg += 1;
1882 if iArg >= len(asArgs):
1883 raise base.InvalidOption('The "--vbox-session-log" takes an argument');
1884 self.sLogSessionGroups = asArgs[iArg];
1885 elif asArgs[iArg] == '--vbox-session-log-flags':
1886 iArg += 1;
1887 if iArg >= len(asArgs):
1888 raise base.InvalidOption('The "--vbox-session-log-flags" takes an argument');
1889 self.sLogSessionFlags = asArgs[iArg];
1890 elif asArgs[iArg] == '--vbox-session-log-dest':
1891 iArg += 1;
1892 if iArg >= len(asArgs):
1893 raise base.InvalidOption('The "--vbox-session-log-dest" takes an argument');
1894 self.sLogSessionDest = asArgs[iArg];
1895 elif asArgs[iArg] == '--vbox-svc-log':
1896 iArg += 1;
1897 if iArg >= len(asArgs):
1898 raise base.InvalidOption('The "--vbox-svc-log" takes an argument');
1899 self.sLogSvcGroups = asArgs[iArg];
1900 elif asArgs[iArg] == '--vbox-svc-log-flags':
1901 iArg += 1;
1902 if iArg >= len(asArgs):
1903 raise base.InvalidOption('The "--vbox-svc-log-flags" takes an argument');
1904 self.sLogSvcFlags = asArgs[iArg];
1905 elif asArgs[iArg] == '--vbox-svc-log-dest':
1906 iArg += 1;
1907 if iArg >= len(asArgs):
1908 raise base.InvalidOption('The "--vbox-svc-log-dest" takes an argument');
1909 self.sLogSvcDest = asArgs[iArg];
1910 elif asArgs[iArg] == '--vbox-log':
1911 iArg += 1;
1912 if iArg >= len(asArgs):
1913 raise base.InvalidOption('The "--vbox-log" takes an argument');
1914 self.sLogSelfGroups = asArgs[iArg];
1915 self.sLogSessionGroups = asArgs[iArg];
1916 self.sLogSvcGroups = asArgs[iArg];
1917 elif asArgs[iArg] == '--vbox-log-flags':
1918 iArg += 1;
1919 if iArg >= len(asArgs):
1920 raise base.InvalidOption('The "--vbox-svc-flags" takes an argument');
1921 self.sLogSelfFlags = asArgs[iArg];
1922 self.sLogSessionFlags = asArgs[iArg];
1923 self.sLogSvcFlags = asArgs[iArg];
1924 elif asArgs[iArg] == '--vbox-log-dest':
1925 iArg += 1;
1926 if iArg >= len(asArgs):
1927 raise base.InvalidOption('The "--vbox-log-dest" takes an argument');
1928 self.sLogSelfDest = asArgs[iArg];
1929 self.sLogSessionDest = asArgs[iArg];
1930 self.sLogSvcDest = asArgs[iArg];
1931 elif asArgs[iArg] == '--vbox-svc-debug':
1932 self.fVBoxSvcInDebugger = True;
1933 elif asArgs[iArg] == '--vbox-svc-wait-debug':
1934 self.fVBoxSvcWaitForDebugger = True;
1935 elif asArgs[iArg] == '--vbox-always-upload-logs':
1936 self.fAlwaysUploadLogs = True;
1937 elif asArgs[iArg] == '--vbox-always-upload-screenshots':
1938 self.fAlwaysUploadScreenshots = True;
1939 elif asArgs[iArg] == '--no-vbox-always-upload-recordings':
1940 self.fAlwaysUploadRecordings = False;
1941 elif asArgs[iArg] == '--vbox-always-upload-recordings':
1942 self.fAlwaysUploadRecordings = True;
1943 elif asArgs[iArg] == '--vbox-debugger':
1944 self.fEnableDebugger = True;
1945 elif asArgs[iArg] == '--no-vbox-debugger':
1946 self.fEnableDebugger = False;
1947 elif asArgs[iArg] == '--vbox-recording':
1948 self.fRecordingEnabled = True;
1949 elif asArgs[iArg] == '--vbox-no-recording':
1950 self.fRecordingEnabled = False;
1951 elif asArgs[iArg] == '--no-vbox-recording-audio':
1952 self.fRecordingAudio = False;
1953 elif asArgs[iArg] == '--vbox-recording-audio':
1954 self.fRecordingAudio = True;
1955 elif asArgs[iArg] == '--vbox-recording-max-time':
1956 iArg += 1;
1957 if iArg >= len(asArgs):
1958 raise base.InvalidOption('The "--vbox-recording-max-time" takes an argument');
1959 self.cSecsRecordingMax = int(asArgs[iArg]);
1960 elif asArgs[iArg] == '--vbox-recording-max-file-size':
1961 iArg += 1;
1962 if iArg >= len(asArgs):
1963 raise base.InvalidOption('The "--vbox-recording-max-file-size" takes an argument');
1964 self.cMbRecordingMax = int(asArgs[iArg]);
1965 elif asArgs[iArg] == '--vbox-vm-no-terminate':
1966 self.fVmNoTerminate = True;
1967 else:
1968 # Relevant for selecting VMs to test?
1969 if self.oTestVmSet is not None:
1970 iRc = self.oTestVmSet.parseOption(asArgs, iArg);
1971 if iRc != iArg:
1972 return iRc;
1973
1974 # Hand it to the base class.
1975 return base.TestDriver.parseOption(self, asArgs, iArg);
1976 return iArg + 1;
1977
1978 def completeOptions(self):
1979 return base.TestDriver.completeOptions(self);
1980
1981 def getNetworkAdapterNameFromType(self, oNic):
1982 """
1983 Returns the network adapter name from a given adapter type.
1984
1985 Returns an empty string if not found / invalid.
1986 """
1987 sAdpName = '';
1988 if oNic.adapterType in (vboxcon.NetworkAdapterType_Am79C970A, \
1989 vboxcon.NetworkAdapterType_Am79C973, \
1990 vboxcon.NetworkAdapterType_Am79C960):
1991 sAdpName = 'pcnet';
1992 elif oNic.adapterType in (vboxcon.NetworkAdapterType_I82540EM, \
1993 vboxcon.NetworkAdapterType_I82543GC, \
1994 vboxcon.NetworkAdapterType_I82545EM):
1995 sAdpName = 'e1000';
1996 elif oNic.adapterType == vboxcon.NetworkAdapterType_Virtio:
1997 sAdpName = 'virtio-net';
1998 return sAdpName;
1999
2000 def getResourceSet(self):
2001 asRsrcs = [];
2002 if self.oTestVmSet is not None:
2003 asRsrcs.extend(self.oTestVmSet.getResourceSet());
2004 asRsrcs.extend(base.TestDriver.getResourceSet(self));
2005 return asRsrcs;
2006
2007 def actionExtract(self):
2008 return base.TestDriver.actionExtract(self);
2009
2010 def actionVerify(self):
2011 return base.TestDriver.actionVerify(self);
2012
2013 def actionConfig(self):
2014 return base.TestDriver.actionConfig(self);
2015
2016 def actionExecute(self):
2017 return base.TestDriver.actionExecute(self);
2018
2019 def actionCleanupBefore(self):
2020 """
2021 Kill any VBoxSVC left behind by a previous test run.
2022 """
2023 self._killVBoxSVCByPidFile('%s/VBoxSVC.pid' % (self.sScratchPath,));
2024 return base.TestDriver.actionCleanupBefore(self);
2025
2026 def actionCleanupAfter(self):
2027 """
2028 Clean up the VBox bits and then call the base driver.
2029
2030 If your test driver overrides this, it should normally call us at the
2031 end of the job.
2032 """
2033 cErrorsEntry = reporter.getErrorCount();
2034
2035 # Kill any left over VM processes.
2036 self._powerOffAllVms();
2037
2038 # Drop all VBox object references and shutdown xpcom then
2039 # terminating VBoxSVC, with extreme prejudice if need be.
2040 self._teardownVBoxApi();
2041 self._stopVBoxSVC();
2042
2043 # Add the VBoxSVC and testdriver debug+release log files.
2044 if self.fAlwaysUploadLogs or reporter.getErrorCount() > 0:
2045 if self.sVBoxSvcLogFile is not None and os.path.isfile(self.sVBoxSvcLogFile):
2046 reporter.addLogFile(self.sVBoxSvcLogFile, 'log/debug/svc', 'Debug log file for VBoxSVC');
2047 self.sVBoxSvcLogFile = None;
2048
2049 if self.sSelfLogFile is not None and os.path.isfile(self.sSelfLogFile):
2050 reporter.addLogFile(self.sSelfLogFile, 'log/debug/client', 'Debug log file for the test driver');
2051 self.sSelfLogFile = None;
2052
2053 if self.sSessionLogFile is not None and os.path.isfile(self.sSessionLogFile):
2054 reporter.addLogFile(self.sSessionLogFile, 'log/debug/session', 'Debug log file for the VM session');
2055 self.sSessionLogFile = None;
2056
2057 sVBoxSvcRelLog = os.path.join(self.sScratchPath, 'VBoxUserHome', 'VBoxSVC.log');
2058 if os.path.isfile(sVBoxSvcRelLog):
2059 reporter.addLogFile(sVBoxSvcRelLog, 'log/release/svc', 'Release log file for VBoxSVC');
2060 for sSuff in [ '.1', '.2', '.3', '.4', '.5', '.6', '.7', '.8' ]:
2061 if os.path.isfile(sVBoxSvcRelLog + sSuff):
2062 reporter.addLogFile(sVBoxSvcRelLog + sSuff, 'log/release/svc', 'Release log file for VBoxSVC');
2063
2064 # Finally, call the base driver to wipe the scratch space.
2065 fRc = base.TestDriver.actionCleanupAfter(self);
2066
2067 # Flag failure if the error count increased.
2068 if reporter.getErrorCount() > cErrorsEntry:
2069 fRc = False;
2070 return fRc;
2071
2072
2073 def actionAbort(self):
2074 """
2075 Terminate VBoxSVC if we've got a pid file.
2076 """
2077 #
2078 # Take default action first, then kill VBoxSVC. The other way around
2079 # is problematic since the testscript would continue running and possibly
2080 # trigger a new VBoxSVC to start.
2081 #
2082 fRc1 = base.TestDriver.actionAbort(self);
2083 fRc2 = self._killVBoxSVCByPidFile('%s/VBoxSVC.pid' % (self.sScratchPath,));
2084 return fRc1 is True and fRc2 is True;
2085
2086 def onExit(self, iRc):
2087 """
2088 Stop VBoxSVC if we've started it.
2089 """
2090 if not self.fVmNoTerminate \
2091 and self.oVBoxSvcProcess is not None:
2092 reporter.log('*** Shutting down the VBox API... (iRc=%s)' % (iRc,));
2093 self._powerOffAllVms();
2094 self._teardownVBoxApi();
2095 self._stopVBoxSVC();
2096 reporter.log('*** VBox API shutdown done.');
2097 return base.TestDriver.onExit(self, iRc);
2098
2099
2100 #
2101 # Task wait method override.
2102 #
2103
2104 def notifyAboutReadyTask(self, oTask):
2105 """
2106 Overriding base.TestDriver.notifyAboutReadyTask.
2107 """
2108 try:
2109 self.oVBoxMgr.interruptWaitEvents();
2110 reporter.log2('vbox.notifyAboutReadyTask: called interruptWaitEvents');
2111 except:
2112 reporter.logXcpt('vbox.notifyAboutReadyTask');
2113 return base.TestDriver.notifyAboutReadyTask(self, oTask);
2114
2115 def waitForTasksSleepWorker(self, cMsTimeout):
2116 """
2117 Overriding base.TestDriver.waitForTasksSleepWorker.
2118 """
2119 try:
2120 rc = self.oVBoxMgr.waitForEvents(int(cMsTimeout));
2121 _ = rc; #reporter.log2('vbox.waitForTasksSleepWorker(%u): true (waitForEvents -> %s)' % (cMsTimeout, rc));
2122 reporter.doPollWork('vbox.TestDriver.waitForTasksSleepWorker');
2123 return True;
2124 except KeyboardInterrupt:
2125 raise;
2126 except:
2127 reporter.logXcpt('vbox.waitForTasksSleepWorker');
2128 return False;
2129
2130 #
2131 # Utility methods.
2132 #
2133
2134 def processEvents(self, cMsTimeout = 0):
2135 """
2136 Processes events, returning after the first batch has been processed
2137 or the time limit has been reached.
2138
2139 Only Ctrl-C exception, no return.
2140 """
2141 try:
2142 self.oVBoxMgr.waitForEvents(cMsTimeout);
2143 except KeyboardInterrupt:
2144 raise;
2145 except:
2146 pass;
2147 return None;
2148
2149 def processPendingEvents(self):
2150 """ processEvents(0) - no waiting. """
2151 return self.processEvents(0);
2152
2153 def sleep(self, cSecs):
2154 """
2155 Sleep for a specified amount of time, processing XPCOM events all the while.
2156 """
2157 cMsTimeout = long(cSecs * 1000);
2158 msStart = base.timestampMilli();
2159 self.processEvents(0);
2160 while True:
2161 cMsElapsed = base.timestampMilli() - msStart;
2162 if cMsElapsed > cMsTimeout:
2163 break;
2164 #reporter.log2('cMsTimeout=%s - cMsElapsed=%d => %s' % (cMsTimeout, cMsElapsed, cMsTimeout - cMsElapsed));
2165 self.processEvents(cMsTimeout - cMsElapsed);
2166 return None;
2167
2168 def _logVmInfoUnsafe(self, oVM): # pylint: disable=too-many-statements,too-many-branches
2169 """
2170 Internal worker for logVmInfo that is wrapped in try/except.
2171 """
2172 reporter.log(" Name: %s" % (oVM.name,));
2173 reporter.log(" ID: %s" % (oVM.id,));
2174 oOsType = self.oVBox.getGuestOSType(oVM.OSTypeId);
2175 reporter.log(" OS Type: %s - %s" % (oVM.OSTypeId, oOsType.description,));
2176 reporter.log(" Machine state: %s" % (oVM.state,));
2177 reporter.log(" Session state: %s" % (oVM.sessionState,));
2178 if self.fpApiVer >= 4.2:
2179 reporter.log(" Session PID: %u (%#x)" % (oVM.sessionPID, oVM.sessionPID,));
2180 else:
2181 reporter.log(" Session PID: %u (%#x)" % (oVM.sessionPid, oVM.sessionPid,));
2182 if self.fpApiVer >= 5.0:
2183 reporter.log(" Session Name: %s" % (oVM.sessionName,));
2184 else:
2185 reporter.log(" Session Name: %s" % (oVM.sessionType,));
2186 reporter.log(" CPUs: %s" % (oVM.CPUCount,));
2187 reporter.log(" RAM: %sMB" % (oVM.memorySize,));
2188 if self.fpApiVer >= 6.1 and hasattr(oVM, 'graphicsAdapter'):
2189 reporter.log(" VRAM: %sMB" % (oVM.graphicsAdapter.VRAMSize,));
2190 reporter.log(" Monitors: %s" % (oVM.graphicsAdapter.monitorCount,));
2191 reporter.log(" GraphicsController: %s"
2192 % (self.oVBoxMgr.getEnumValueName('GraphicsControllerType', # pylint: disable=not-callable
2193 oVM.graphicsAdapter.graphicsControllerType),));
2194 else:
2195 reporter.log(" VRAM: %sMB" % (oVM.VRAMSize,));
2196 reporter.log(" Monitors: %s" % (oVM.monitorCount,));
2197 reporter.log(" GraphicsController: %s"
2198 % (self.oVBoxMgr.getEnumValueName('GraphicsControllerType', oVM.graphicsControllerType),)); # pylint: disable=not-callable
2199 reporter.log(" Chipset: %s" % (self.oVBoxMgr.getEnumValueName('ChipsetType', oVM.chipsetType),)); # pylint: disable=not-callable
2200 if self.fpApiVer >= 6.2 and hasattr(vboxcon, 'IommuType_None'):
2201 reporter.log(" IOMMU: %s" % (self.oVBoxMgr.getEnumValueName('IommuType', oVM.iommuType),)); # pylint: disable=not-callable
2202 reporter.log(" Firmware: %s" % (self.oVBoxMgr.getEnumValueName('FirmwareType', oVM.firmwareType),)); # pylint: disable=not-callable
2203 reporter.log(" HwVirtEx: %s" % (oVM.getHWVirtExProperty(vboxcon.HWVirtExPropertyType_Enabled),));
2204 reporter.log(" VPID support: %s" % (oVM.getHWVirtExProperty(vboxcon.HWVirtExPropertyType_VPID),));
2205 reporter.log(" Nested paging: %s" % (oVM.getHWVirtExProperty(vboxcon.HWVirtExPropertyType_NestedPaging),));
2206 atTypes = [
2207 ( 'CPUPropertyType_PAE', 'PAE: '),
2208 ( 'CPUPropertyType_LongMode', 'Long-mode: '),
2209 ( 'CPUPropertyType_HWVirt', 'Nested VT-x/AMD-V: '),
2210 ( 'CPUPropertyType_APIC', 'APIC: '),
2211 ( 'CPUPropertyType_X2APIC', 'X2APIC: '),
2212 ( 'CPUPropertyType_TripleFaultReset', 'TripleFaultReset: '),
2213 ( 'CPUPropertyType_IBPBOnVMExit', 'IBPBOnVMExit: '),
2214 ( 'CPUPropertyType_SpecCtrl', 'SpecCtrl: '),
2215 ( 'CPUPropertyType_SpecCtrlByHost', 'SpecCtrlByHost: '),
2216 ];
2217 for sEnumValue, sDesc in atTypes:
2218 if hasattr(vboxcon, sEnumValue):
2219 reporter.log(" %s%s" % (sDesc, oVM.getCPUProperty(getattr(vboxcon, sEnumValue)),));
2220 reporter.log(" ACPI: %s" % (oVM.BIOSSettings.ACPIEnabled,));
2221 reporter.log(" IO-APIC: %s" % (oVM.BIOSSettings.IOAPICEnabled,));
2222 if self.fpApiVer >= 3.2:
2223 if self.fpApiVer >= 4.2:
2224 reporter.log(" HPET: %s" % (oVM.HPETEnabled,));
2225 else:
2226 reporter.log(" HPET: %s" % (oVM.hpetEnabled,));
2227 if self.fpApiVer >= 6.1 and hasattr(oVM, 'graphicsAdapter'):
2228 reporter.log(" 3D acceleration: %s" % (oVM.graphicsAdapter.accelerate3DEnabled,));
2229 reporter.log(" 2D acceleration: %s" % (oVM.graphicsAdapter.accelerate2DVideoEnabled,));
2230 else:
2231 reporter.log(" 3D acceleration: %s" % (oVM.accelerate3DEnabled,));
2232 reporter.log(" 2D acceleration: %s" % (oVM.accelerate2DVideoEnabled,));
2233 reporter.log(" TeleporterEnabled: %s" % (oVM.teleporterEnabled,));
2234 reporter.log(" TeleporterPort: %s" % (oVM.teleporterPort,));
2235 reporter.log(" TeleporterAddress: %s" % (oVM.teleporterAddress,));
2236 reporter.log(" TeleporterPassword: %s" % (oVM.teleporterPassword,));
2237 reporter.log(" Clipboard mode: %s" % (oVM.clipboardMode,));
2238 if self.fpApiVer >= 5.0:
2239 reporter.log(" Drag and drop mode: %s" % (oVM.dnDMode,));
2240 elif self.fpApiVer >= 4.3:
2241 reporter.log(" Drag and drop mode: %s" % (oVM.dragAndDropMode,));
2242 if self.fpApiVer >= 4.0:
2243 reporter.log(" VRDP server: %s" % (oVM.VRDEServer.enabled,));
2244 try: sPorts = oVM.VRDEServer.getVRDEProperty("TCP/Ports");
2245 except: sPorts = "";
2246 reporter.log(" VRDP server ports: %s" % (sPorts,));
2247 reporter.log(" VRDP auth: %s (%s)" % (oVM.VRDEServer.authType, oVM.VRDEServer.authLibrary,));
2248 else:
2249 reporter.log(" VRDP server: %s" % (oVM.VRDPServer.enabled,));
2250 reporter.log(" VRDP server ports: %s" % (oVM.VRDPServer.ports,));
2251 reporter.log(" Last changed: %s" % (oVM.lastStateChange,));
2252
2253 aoControllers = self.oVBoxMgr.getArray(oVM, 'storageControllers')
2254 if aoControllers:
2255 reporter.log(" Controllers:");
2256 for oCtrl in aoControllers:
2257 reporter.log(" %s %s bus: %s type: %s" % (oCtrl.name, oCtrl.controllerType, oCtrl.bus, oCtrl.controllerType,));
2258 if self.fpApiVer >= 7.0:
2259 oAdapter = oVM.audioSettings.adapter;
2260 else:
2261 oAdapter = oVM.audioAdapter;
2262 reporter.log(" AudioController: %s"
2263 % (self.oVBoxMgr.getEnumValueName('AudioControllerType', oAdapter.audioController),)); # pylint: disable=not-callable
2264 reporter.log(" AudioEnabled: %s" % (oAdapter.enabled,));
2265 reporter.log(" Host AudioDriver: %s"
2266 % (self.oVBoxMgr.getEnumValueName('AudioDriverType', oAdapter.audioDriver),)); # pylint: disable=not-callable
2267
2268 self.processPendingEvents();
2269 aoAttachments = self.oVBoxMgr.getArray(oVM, 'mediumAttachments')
2270 if aoAttachments:
2271 reporter.log(" Attachments:");
2272 for oAtt in aoAttachments:
2273 sCtrl = "Controller: %s port: %s device: %s type: %s" % (oAtt.controller, oAtt.port, oAtt.device, oAtt.type);
2274 oMedium = oAtt.medium
2275 if oAtt.type == vboxcon.DeviceType_HardDisk:
2276 reporter.log(" %s: HDD" % sCtrl);
2277 reporter.log(" Id: %s" % (oMedium.id,));
2278 reporter.log(" Name: %s" % (oMedium.name,));
2279 reporter.log(" Format: %s" % (oMedium.format,));
2280 reporter.log(" Location: %s" % (oMedium.location,));
2281
2282 if oAtt.type == vboxcon.DeviceType_DVD:
2283 reporter.log(" %s: DVD" % sCtrl);
2284 if oMedium:
2285 reporter.log(" Id: %s" % (oMedium.id,));
2286 reporter.log(" Name: %s" % (oMedium.name,));
2287 if oMedium.hostDrive:
2288 reporter.log(" Host DVD %s" % (oMedium.location,));
2289 if oAtt.passthrough:
2290 reporter.log(" [passthrough mode]");
2291 else:
2292 reporter.log(" Virtual image: %s" % (oMedium.location,));
2293 reporter.log(" Size: %s" % (oMedium.size,));
2294 else:
2295 reporter.log(" empty");
2296
2297 if oAtt.type == vboxcon.DeviceType_Floppy:
2298 reporter.log(" %s: Floppy" % sCtrl);
2299 if oMedium:
2300 reporter.log(" Id: %s" % (oMedium.id,));
2301 reporter.log(" Name: %s" % (oMedium.name,));
2302 if oMedium.hostDrive:
2303 reporter.log(" Host floppy: %s" % (oMedium.location,));
2304 else:
2305 reporter.log(" Virtual image: %s" % (oMedium.location,));
2306 reporter.log(" Size: %s" % (oMedium.size,));
2307 else:
2308 reporter.log(" empty");
2309 self.processPendingEvents();
2310
2311 reporter.log(" Network Adapter:");
2312 for iSlot in range(0, 32):
2313 try: oNic = oVM.getNetworkAdapter(iSlot)
2314 except: break;
2315 if not oNic.enabled:
2316 reporter.log2(" slot #%d found but not enabled, skipping" % (iSlot,));
2317 continue;
2318 reporter.log(" slot #%d: type: %s (%s) MAC Address: %s lineSpeed: %s"
2319 % (iSlot, self.oVBoxMgr.getEnumValueName('NetworkAdapterType', oNic.adapterType), # pylint: disable=not-callable
2320 oNic.adapterType, oNic.MACAddress, oNic.lineSpeed) );
2321
2322 if oNic.attachmentType == vboxcon.NetworkAttachmentType_NAT:
2323 reporter.log(" attachmentType: NAT (%s)" % (oNic.attachmentType,));
2324 if self.fpApiVer >= 4.1:
2325 reporter.log(" nat-network: %s" % (oNic.NATNetwork,));
2326 if self.fpApiVer >= 7.0 and hasattr(oNic.NATEngine, 'localhostReachable'):
2327 reporter.log(" localhostReachable: %s" % (oNic.NATEngine.localhostReachable,));
2328
2329 elif oNic.attachmentType == vboxcon.NetworkAttachmentType_Bridged:
2330 reporter.log(" attachmentType: Bridged (%s)" % (oNic.attachmentType,));
2331 if self.fpApiVer >= 4.1:
2332 reporter.log(" hostInterface: %s" % (oNic.bridgedInterface,));
2333 else:
2334 reporter.log(" hostInterface: %s" % (oNic.hostInterface,));
2335 elif oNic.attachmentType == vboxcon.NetworkAttachmentType_Internal:
2336 reporter.log(" attachmentType: Internal (%s)" % (oNic.attachmentType,));
2337 reporter.log(" intnet-name: %s" % (oNic.internalNetwork,));
2338 elif oNic.attachmentType == vboxcon.NetworkAttachmentType_HostOnly:
2339 reporter.log(" attachmentType: HostOnly (%s)" % (oNic.attachmentType,));
2340 if self.fpApiVer >= 4.1:
2341 reporter.log(" hostInterface: %s" % (oNic.hostOnlyInterface,));
2342 else:
2343 reporter.log(" hostInterface: %s" % (oNic.hostInterface,));
2344 else:
2345 if self.fpApiVer >= 7.0:
2346 if oNic.attachmentType == vboxcon.NetworkAttachmentType_HostOnlyNetwork:
2347 reporter.log(" attachmentType: HostOnlyNetwork (%s)" % (oNic.attachmentType,));
2348 reporter.log(" hostonly-net: %s" % (oNic.hostOnlyNetwork,));
2349 elif self.fpApiVer >= 4.1:
2350 if oNic.attachmentType == vboxcon.NetworkAttachmentType_Generic:
2351 reporter.log(" attachmentType: Generic (%s)" % (oNic.attachmentType,));
2352 reporter.log(" generic-driver: %s" % (oNic.GenericDriver,));
2353 else:
2354 reporter.log(" attachmentType: unknown-%s" % (oNic.attachmentType,));
2355 else:
2356 reporter.log(" attachmentType: unknown-%s" % (oNic.attachmentType,));
2357 if oNic.traceEnabled:
2358 reporter.log(" traceFile: %s" % (oNic.traceFile,));
2359 self.processPendingEvents();
2360
2361 reporter.log(" Serial ports:");
2362 for iSlot in range(0, 8):
2363 try: oPort = oVM.getSerialPort(iSlot)
2364 except: break;
2365 if oPort is not None and oPort.enabled:
2366 enmHostMode = oPort.hostMode;
2367 reporter.log(" slot #%d: hostMode: %s (%s) I/O port: %s IRQ: %s server: %s path: %s" %
2368 (iSlot, self.oVBoxMgr.getEnumValueName('PortMode', enmHostMode), # pylint: disable=not-callable
2369 enmHostMode, oPort.IOBase, oPort.IRQ, oPort.server, oPort.path,) );
2370 self.processPendingEvents();
2371
2372 return True;
2373
2374 def logVmInfo(self, oVM): # pylint: disable=too-many-statements,too-many-branches
2375 """
2376 Logs VM configuration details.
2377
2378 This is copy, past, search, replace and edit of infoCmd from vboxshell.py.
2379 """
2380 try:
2381 fRc = self._logVmInfoUnsafe(oVM);
2382 except:
2383 reporter.logXcpt();
2384 fRc = False;
2385 return fRc;
2386
2387 def logVmInfoByName(self, sName):
2388 """
2389 logVmInfo + getVmByName.
2390 """
2391 return self.logVmInfo(self.getVmByName(sName));
2392
2393 def tryFindGuestOsId(self, sIdOrDesc):
2394 """
2395 Takes a guest OS ID or Description and returns the ID.
2396 If nothing matching it is found, the input is returned unmodified.
2397 """
2398
2399 if self.fpApiVer >= 4.0:
2400 if sIdOrDesc == 'Solaris (64 bit)':
2401 sIdOrDesc = 'Oracle Solaris 10 5/09 and earlier (64 bit)';
2402
2403 try:
2404 aoGuestTypes = self.oVBoxMgr.getArray(self.oVBox, 'GuestOSTypes');
2405 except:
2406 reporter.logXcpt();
2407 else:
2408 for oGuestOS in aoGuestTypes:
2409 try:
2410 sId = oGuestOS.id;
2411 sDesc = oGuestOS.description;
2412 except:
2413 reporter.logXcpt();
2414 else:
2415 if sIdOrDesc in (sId, sDesc,):
2416 sIdOrDesc = sId;
2417 break;
2418 self.processPendingEvents();
2419 return sIdOrDesc
2420
2421 def resourceFindVmHd(self, sVmName, sFlavor):
2422 """
2423 Search the test resources for the most recent VM HD.
2424
2425 Returns path relative to the test resource root.
2426 """
2427 ## @todo implement a proper search algo here.
2428 return '4.2/' + sFlavor + '/' + sVmName + '/t-' + sVmName + '.vdi';
2429
2430
2431 #
2432 # VM Api wrappers that logs errors, hides exceptions and other details.
2433 #
2434
2435 def createTestVMOnly(self, sName, sKind):
2436 """
2437 Creates and register a test VM without doing any kind of configuration.
2438
2439 Returns VM object (IMachine) on success, None on failure.
2440 """
2441 if not self.importVBoxApi():
2442 return None;
2443
2444 # create + register the VM
2445 try:
2446 if self.fpApiVer >= 7.0: # Introduces VM encryption (three new parameters, empty for now).
2447 oVM = self.oVBox.createMachine("", sName, [], self.tryFindGuestOsId(sKind), "", "", "", "");
2448 elif self.fpApiVer >= 4.2: # Introduces grouping (third parameter, empty for now).
2449 oVM = self.oVBox.createMachine("", sName, [], self.tryFindGuestOsId(sKind), "");
2450 elif self.fpApiVer >= 4.0:
2451 oVM = self.oVBox.createMachine("", sName, self.tryFindGuestOsId(sKind), "", False);
2452 elif self.fpApiVer >= 3.2:
2453 oVM = self.oVBox.createMachine(sName, self.tryFindGuestOsId(sKind), "", "", False);
2454 else:
2455 oVM = self.oVBox.createMachine(sName, self.tryFindGuestOsId(sKind), "", "");
2456 try:
2457 oVM.saveSettings();
2458 try:
2459 self.oVBox.registerMachine(oVM);
2460 return oVM;
2461 except:
2462 reporter.logXcpt();
2463 raise;
2464 except:
2465 reporter.logXcpt();
2466 if self.fpApiVer >= 4.0:
2467 try:
2468 if self.fpApiVer >= 4.3:
2469 oProgress = oVM.deleteConfig([]);
2470 else:
2471 oProgress = oVM.delete(None);
2472 self.waitOnProgress(oProgress);
2473 except:
2474 reporter.logXcpt();
2475 else:
2476 try: oVM.deleteSettings();
2477 except: reporter.logXcpt();
2478 raise;
2479 except:
2480 reporter.errorXcpt('failed to create vm "%s"' % (sName));
2481 return None;
2482
2483 # pylint: disable=too-many-arguments,too-many-locals,too-many-statements,too-many-branches
2484 def createTestVM(self,
2485 sName,
2486 iGroup,
2487 sHd = None,
2488 cMbRam = None,
2489 cCpus = 1,
2490 fVirtEx = None,
2491 fNestedPaging = None,
2492 sDvdImage = None,
2493 sKind = "Other",
2494 fIoApic = None,
2495 fNstHwVirt = None,
2496 fPae = None,
2497 fFastBootLogo = True,
2498 eNic0Type = None,
2499 eNic0AttachType = None,
2500 sNic0NetName = 'default',
2501 sNic0MacAddr = 'grouped',
2502 sFloppy = None,
2503 fNatForwardingForTxs = None,
2504 sHddControllerType = 'IDE Controller',
2505 fVmmDevTestingPart = None,
2506 fVmmDevTestingMmio = False,
2507 sFirmwareType = 'bios',
2508 sChipsetType = 'piix3',
2509 sIommuType = 'none',
2510 sDvdControllerType = 'IDE Controller',
2511 sCom1RawFile = None,
2512 fSecureBoot = False,
2513 sUefiMokPathPrefix = None):
2514 """
2515 Creates a test VM with a immutable HD from the test resources.
2516 """
2517 # create + register the VM
2518 oVM = self.createTestVMOnly(sName, sKind);
2519 if not oVM:
2520 return None;
2521
2522 # Configure the VM.
2523 fRc = True;
2524 oSession = self.openSession(oVM);
2525 if oSession is not None:
2526 fRc = oSession.setupPreferredConfig();
2527
2528 if fRc and cMbRam is not None :
2529 fRc = oSession.setRamSize(cMbRam);
2530 if fRc and cCpus is not None:
2531 fRc = oSession.setCpuCount(cCpus);
2532 if fRc and fVirtEx is not None:
2533 fRc = oSession.enableVirtEx(fVirtEx);
2534 if fRc and fNestedPaging is not None:
2535 fRc = oSession.enableNestedPaging(fNestedPaging);
2536 if fRc and fIoApic is not None:
2537 fRc = oSession.enableIoApic(fIoApic);
2538 if fRc and fNstHwVirt is not None:
2539 fRc = oSession.enableNestedHwVirt(fNstHwVirt);
2540 if fRc and fPae is not None:
2541 fRc = oSession.enablePae(fPae);
2542 if fRc and sDvdImage is not None:
2543 fRc = oSession.attachDvd(sDvdImage, sDvdControllerType);
2544 if fRc and sHd is not None:
2545 fRc = oSession.attachHd(sHd, sHddControllerType);
2546 if fRc and sFloppy is not None:
2547 fRc = oSession.attachFloppy(sFloppy);
2548 if fRc and eNic0Type is not None:
2549 fRc = oSession.setNicType(eNic0Type, 0);
2550 if fRc and (eNic0AttachType is not None or (sNic0NetName is not None and sNic0NetName != 'default')):
2551 fRc = oSession.setNicAttachment(eNic0AttachType, sNic0NetName, 0);
2552 if fRc and sNic0MacAddr is not None:
2553 if sNic0MacAddr == 'grouped':
2554 sNic0MacAddr = '%02X' % (iGroup);
2555 fRc = oSession.setNicMacAddress(sNic0MacAddr, 0);
2556 # Needed to reach the host (localhost) from the guest. See xTracker #9896.
2557 if fRc and self.fpApiVer >= 7.0:
2558 fRc = oSession.setNicLocalhostReachable(True, 0);
2559 if fRc and fNatForwardingForTxs is True:
2560 fRc = oSession.setupNatForwardingForTxs();
2561 if fRc and fFastBootLogo is not None:
2562 fRc = oSession.setupBootLogo(fFastBootLogo);
2563 if fRc and self.fEnableVrdp:
2564 fRc = oSession.setupVrdp(True, self.uVrdpBasePort + iGroup);
2565 if fRc and fVmmDevTestingPart is not None:
2566 fRc = oSession.enableVmmDevTestingPart(fVmmDevTestingPart, fVmmDevTestingMmio);
2567 if fRc and sFirmwareType == 'bios':
2568 fRc = oSession.setFirmwareType(vboxcon.FirmwareType_BIOS);
2569 elif fRc and sFirmwareType == 'efi':
2570 fRc = oSession.setFirmwareType(vboxcon.FirmwareType_EFI);
2571 if fRc and self.fpApiVer >= 7.0 and fSecureBoot:
2572 fRc = oSession.enableSecureBoot(fSecureBoot, sUefiMokPathPrefix);
2573 if fRc and self.fEnableDebugger:
2574 fRc = oSession.setExtraData('VBoxInternal/DBGC/Enabled', '1');
2575 if fRc and self.fRecordingEnabled:
2576 try:
2577 if self.fpApiVer >= 6.1: # Only for VBox 6.1 and up now.
2578 reporter.log('Recording enabled');
2579 if self.cSecsRecordingMax > 0:
2580 reporter.log('Recording time limit is set to %d seconds' % (self.cSecsRecordingMax));
2581 if self.cMbRecordingMax > 0:
2582 reporter.log('Recording file limit is set to %d MB' % (self.cMbRecordingMax));
2583 oRecSettings = oSession.o.machine.recordingSettings;
2584 oRecSettings.enabled = True;
2585 aoScreens = self.oVBoxMgr.getArray(oRecSettings, 'screens');
2586 for oScreen in aoScreens:
2587 try:
2588 oScreen.enabled = True;
2589 sRecFile = os.path.join(self.sScratchPath, "recording-%s.webm" % (sName));
2590 oScreen.filename = sRecFile;
2591 sRecFile = oScreen.filename; # Get back the file from Main, in case it was modified somehow.
2592 dRecFile = { 'id' : oScreen.id, 'file' : sRecFile };
2593 self.adRecordingFiles.append(dRecFile);
2594 if self.fpApiVer >= 7.0:
2595 aFeatures = [ vboxcon.RecordingFeature_Video, ];
2596 if self.fRecordingAudio:
2597 aFeatures.append(vboxcon.RecordingFeature_Audio);
2598 try:
2599 oScreen.setFeatures(aFeatures);
2600 except: ## @todo Figure out why this is needed on Windows.
2601 oScreen.features = aFeatures;
2602 else: # <= VBox 6.1 the feature were kept as a ULONG.
2603 uFeatures = vboxcon.RecordingFeature_Video;
2604 if self.fRecordingAudio:
2605 uFeatures = uFeatures | vboxcon.RecordingFeature_Audio;
2606 oScreen.features = uFeatures;
2607 reporter.log2('Recording screen %d to "%s"' % (dRecFile['id'], dRecFile['file'],));
2608 oScreen.maxTime = self.cSecsRecordingMax;
2609 oScreen.maxFileSize = self.cMbRecordingMax;
2610 except:
2611 reporter.errorXcpt('failed to configure recording for "%s" (screen %d)' % (sName, oScreen.id));
2612 else:
2613 # Not fatal.
2614 reporter.log('Recording only available for VBox >= 6.1, sorry!')
2615 except:
2616 reporter.errorXcpt('failed to configure recording for "%s"' % (sName));
2617 if fRc and sChipsetType == 'piix3':
2618 fRc = oSession.setChipsetType(vboxcon.ChipsetType_PIIX3);
2619 elif fRc and sChipsetType == 'ich9':
2620 fRc = oSession.setChipsetType(vboxcon.ChipsetType_ICH9);
2621 if fRc and sCom1RawFile:
2622 fRc = oSession.setupSerialToRawFile(0, sCom1RawFile);
2623 if fRc and self.fpApiVer >= 6.2 and hasattr(vboxcon, 'IommuType_AMD') and sIommuType == 'amd':
2624 fRc = oSession.setIommuType(vboxcon.IommuType_AMD);
2625 elif fRc and self.fpApiVer >= 6.2 and hasattr(vboxcon, 'IommuType_Intel') and sIommuType == 'intel':
2626 fRc = oSession.setIommuType(vboxcon.IommuType_Intel);
2627
2628 if fRc: fRc = oSession.saveSettings();
2629 if not fRc: oSession.discardSettings(True);
2630 oSession.close();
2631 if not fRc:
2632 if self.fpApiVer >= 4.0:
2633 try: oVM.unregister(vboxcon.CleanupMode_Full);
2634 except: reporter.logXcpt();
2635 try:
2636 if self.fpApiVer >= 4.3:
2637 oProgress = oVM.deleteConfig([]);
2638 else:
2639 oProgress = oVM.delete([]);
2640 self.waitOnProgress(oProgress);
2641 except:
2642 reporter.logXcpt();
2643 else:
2644 try: self.oVBox.unregisterMachine(oVM.id);
2645 except: reporter.logXcpt();
2646 try: oVM.deleteSettings();
2647 except: reporter.logXcpt();
2648 return None;
2649
2650 # success.
2651 reporter.log('created "%s" with name "%s"' % (oVM.id, sName));
2652 self.aoVMs.append(oVM);
2653 self.logVmInfo(oVM); # testing...
2654 return oVM;
2655 # pylint: enable=too-many-arguments,too-many-locals,too-many-statements
2656
2657 def createTestVmWithDefaults(self, # pylint: disable=too-many-arguments
2658 sName,
2659 iGroup,
2660 sKind,
2661 sDvdImage = None,
2662 fFastBootLogo = True,
2663 eNic0AttachType = None,
2664 sNic0NetName = 'default',
2665 sNic0MacAddr = 'grouped',
2666 fVmmDevTestingPart = None,
2667 fVmmDevTestingMmio = False,
2668 sCom1RawFile = None):
2669 """
2670 Creates a test VM with all defaults and no HDs.
2671 """
2672 # create + register the VM
2673 oVM = self.createTestVMOnly(sName, sKind);
2674 if oVM is not None:
2675 # Configure the VM with defaults according to sKind.
2676 fRc = True;
2677 oSession = self.openSession(oVM);
2678 if oSession is not None:
2679 if self.fpApiVer >= 6.0:
2680 try:
2681 oSession.o.machine.applyDefaults('');
2682 except:
2683 reporter.errorXcpt('failed to apply defaults to vm "%s"' % (sName,));
2684 fRc = False;
2685 else:
2686 reporter.error("Implement applyDefaults for vbox version %s" % (self.fpApiVer,));
2687 #fRc = oSession.setupPreferredConfig();
2688 fRc = False;
2689
2690 # Apply the specified configuration:
2691 if fRc and sDvdImage is not None:
2692 #fRc = oSession.insertDvd(sDvdImage); # attachDvd
2693 reporter.error('Implement: oSession.insertDvd(%s)' % (sDvdImage,));
2694 fRc = False;
2695
2696 if fRc and fFastBootLogo is not None:
2697 fRc = oSession.setupBootLogo(fFastBootLogo);
2698
2699 if fRc and (eNic0AttachType is not None or (sNic0NetName is not None and sNic0NetName != 'default')):
2700 fRc = oSession.setNicAttachment(eNic0AttachType, sNic0NetName, 0);
2701 if fRc and sNic0MacAddr is not None:
2702 if sNic0MacAddr == 'grouped':
2703 sNic0MacAddr = '%02X' % (iGroup,);
2704 fRc = oSession.setNicMacAddress(sNic0MacAddr, 0);
2705 # Needed to reach the host (localhost) from the guest. See xTracker #9896.
2706 if fRc and self.fpApiVer >= 7.0:
2707 fRc = oSession.setNicLocalhostReachable(True, 0);
2708
2709 if fRc and self.fEnableVrdp:
2710 fRc = oSession.setupVrdp(True, self.uVrdpBasePort + iGroup);
2711
2712 if fRc and fVmmDevTestingPart is not None:
2713 fRc = oSession.enableVmmDevTestingPart(fVmmDevTestingPart, fVmmDevTestingMmio);
2714
2715 if fRc and sCom1RawFile:
2716 fRc = oSession.setupSerialToRawFile(0, sCom1RawFile);
2717
2718 # Save the settings if we were successfull, otherwise discard them.
2719 if fRc:
2720 fRc = oSession.saveSettings();
2721 if not fRc:
2722 oSession.discardSettings(True);
2723 oSession.close();
2724
2725 if fRc is True:
2726 # If we've been successful, add the VM to the list and return it.
2727 # success.
2728 reporter.log('created "%s" with name "%s"' % (oVM.id, sName, ));
2729 self.aoVMs.append(oVM);
2730 self.logVmInfo(oVM); # testing...
2731 return oVM;
2732
2733 # Failed. Unregister the machine and delete it.
2734 if self.fpApiVer >= 4.0:
2735 try: oVM.unregister(vboxcon.CleanupMode_Full);
2736 except: reporter.logXcpt();
2737 try:
2738 if self.fpApiVer >= 4.3:
2739 oProgress = oVM.deleteConfig([]);
2740 else:
2741 oProgress = oVM.delete([]);
2742 self.waitOnProgress(oProgress);
2743 except:
2744 reporter.logXcpt();
2745 else:
2746 try: self.oVBox.unregisterMachine(oVM.id);
2747 except: reporter.logXcpt();
2748 try: oVM.deleteSettings();
2749 except: reporter.logXcpt();
2750 return None;
2751
2752 def addTestMachine(self, sNameOrId, fQuiet = False):
2753 """
2754 Adds an already existing (that is, configured) test VM to the
2755 test VM list.
2756
2757 Returns the VM object on success, None if failed.
2758 """
2759 # find + add the VM to the list.
2760 oVM = None;
2761 try:
2762 if self.fpApiVer >= 4.0:
2763 oVM = self.oVBox.findMachine(sNameOrId);
2764 else:
2765 reporter.error('fpApiVer=%s - did you remember to initialize the API' % (self.fpApiVer,));
2766 except:
2767 reporter.errorXcpt('could not find vm "%s"' % (sNameOrId,));
2768
2769 if oVM:
2770 self.aoVMs.append(oVM);
2771 if not fQuiet:
2772 reporter.log('Added "%s" with name "%s"' % (oVM.id, sNameOrId));
2773 self.logVmInfo(oVM);
2774 return oVM;
2775
2776 def forgetTestMachine(self, oVM, fQuiet = False):
2777 """
2778 Forget about an already known test VM in the test VM list.
2779
2780 Returns True on success, False if failed.
2781 """
2782 try:
2783 sUuid = oVM.id;
2784 sName = oVM.name;
2785 except:
2786 reporter.errorXcpt('failed to get the UUID for VM "%s"' % (oVM,));
2787 return False;
2788 try:
2789 self.aoVMs.remove(oVM);
2790 if not fQuiet:
2791 reporter.log('Removed "%s" with name "%s"' % (sUuid, sName));
2792 except:
2793 reporter.errorXcpt('could not find vm "%s"' % (sName,));
2794 return False;
2795 return True;
2796
2797 def openSession(self, oVM):
2798 """
2799 Opens a session for the VM. Returns the a Session wrapper object that
2800 will automatically close the session when the wrapper goes out of scope.
2801
2802 On failure None is returned and an error is logged.
2803 """
2804 try:
2805 sUuid = oVM.id;
2806 except:
2807 reporter.errorXcpt('failed to get the UUID for VM "%s"' % (oVM,));
2808 return None;
2809
2810 # This loop is a kludge to deal with us racing the closing of the
2811 # direct session of a previous VM run. See waitOnDirectSessionClose.
2812 for i in range(10):
2813 try:
2814 if self.fpApiVer <= 3.2:
2815 oSession = self.oVBoxMgr.openMachineSession(sUuid);
2816 else:
2817 oSession = self.oVBoxMgr.openMachineSession(oVM);
2818 break;
2819 except:
2820 if i == 9:
2821 reporter.errorXcpt('failed to open session for "%s" ("%s")' % (sUuid, oVM));
2822 return None;
2823 if i > 0:
2824 reporter.logXcpt('warning: failed to open session for "%s" ("%s") - retrying in %u secs' % (sUuid, oVM, i));
2825 self.waitOnDirectSessionClose(oVM, 5000 + i * 1000);
2826 from testdriver.vboxwrappers import SessionWrapper;
2827 return SessionWrapper(oSession, oVM, self.oVBox, self.oVBoxMgr, self, False);
2828
2829 #
2830 # Guest locations.
2831 #
2832
2833 @staticmethod
2834 def getGuestTempDir(oTestVm):
2835 """
2836 Helper for finding a temporary directory in the test VM.
2837
2838 Note! It may be necessary to create it!
2839 """
2840 if oTestVm.isWindows():
2841 return "C:\\Temp";
2842 if oTestVm.isOS2():
2843 return "C:\\Temp";
2844 return '/var/tmp';
2845
2846 @staticmethod
2847 def getGuestSystemDir(oTestVm, sPathPrefix = ''):
2848 """
2849 Helper for finding a system directory in the test VM that we can play around with.
2850 sPathPrefix can be used to specify other directories, such as /usr/local/bin/ or /usr/bin, for instance.
2851
2852 On Windows this is always the System32 directory, so this function can be used as
2853 basis for locating other files in or under that directory.
2854 """
2855 if oTestVm.isWindows():
2856 return oTestVm.pathJoin(TestDriver.getGuestWinDir(oTestVm), 'System32');
2857 if oTestVm.isOS2():
2858 return 'C:\\OS2\\DLL';
2859
2860 # OL / RHEL symlinks "/bin"/ to "/usr/bin". To avoid (unexpectedly) following symlinks, use "/usr/bin" then instead.
2861 if not sPathPrefix \
2862 and oTestVm.sKind in ('Oracle_64', 'Oracle'): ## @todo Does this apply for "RedHat" as well?
2863 return "/usr/bin";
2864
2865 return sPathPrefix + "/bin";
2866
2867 @staticmethod
2868 def getGuestSystemAdminDir(oTestVm, sPathPrefix = ''):
2869 """
2870 Helper for finding a system admin directory ("sbin") in the test VM that we can play around with.
2871 sPathPrefix can be used to specify other directories, such as /usr/local/sbin/ or /usr/sbin, for instance.
2872
2873 On Windows this is always the System32 directory, so this function can be used as
2874 basis for locating other files in or under that directory.
2875 On UNIX-y systems this always is the "sh" shell to guarantee a common shell syntax.
2876 """
2877 if oTestVm.isWindows():
2878 return oTestVm.pathJoin(TestDriver.getGuestWinDir(oTestVm), 'System32');
2879 if oTestVm.isOS2():
2880 return 'C:\\OS2\\DLL'; ## @todo r=andy Not sure here.
2881
2882 # OL / RHEL symlinks "/sbin"/ to "/usr/sbin". To avoid (unexpectedly) following symlinks, use "/usr/sbin" then instead.
2883 if not sPathPrefix \
2884 and oTestVm.sKind in ('Oracle_64', 'Oracle'): ## @todo Does this apply for "RedHat" as well?
2885 return "/usr/sbin";
2886
2887 return sPathPrefix + "/sbin";
2888
2889 @staticmethod
2890 def getGuestWinDir(oTestVm):
2891 """
2892 Helper for finding the Windows directory in the test VM that we can play around with.
2893 ASSUMES that we always install Windows on drive C.
2894
2895 Returns the Windows directory, or an empty string when executed on a non-Windows guest (asserts).
2896 """
2897 sWinDir = '';
2898 if oTestVm.isWindows():
2899 if oTestVm.sKind in ['WindowsNT4', 'WindowsNT3x',]:
2900 sWinDir = 'C:\\WinNT\\';
2901 else:
2902 sWinDir = 'C:\\Windows\\';
2903 assert sWinDir != '', 'Retrieving Windows directory for non-Windows OS';
2904 return sWinDir;
2905
2906 @staticmethod
2907 def getGuestSystemShell(oTestVm):
2908 """
2909 Helper for finding the default system shell in the test VM.
2910 """
2911 if oTestVm.isWindows():
2912 return TestDriver.getGuestSystemDir(oTestVm) + '\\cmd.exe';
2913 if oTestVm.isOS2():
2914 return TestDriver.getGuestSystemDir(oTestVm) + '\\..\\CMD.EXE';
2915 return "/bin/sh";
2916
2917 @staticmethod
2918 def getGuestSystemFileForReading(oTestVm):
2919 """
2920 Helper for finding a file in the test VM that we can read.
2921 """
2922 if oTestVm.isWindows():
2923 return TestDriver.getGuestSystemDir(oTestVm) + '\\ntdll.dll';
2924 if oTestVm.isOS2():
2925 return TestDriver.getGuestSystemDir(oTestVm) + '\\DOSCALL1.DLL';
2926 return "/bin/sh";
2927
2928 def getVmByName(self, sName):
2929 """
2930 Get a test VM by name. Returns None if not found, logged.
2931 """
2932 # Look it up in our 'cache'.
2933 for oVM in self.aoVMs:
2934 try:
2935 #reporter.log2('cur: %s / %s (oVM=%s)' % (oVM.name, oVM.id, oVM));
2936 if oVM.name == sName:
2937 return oVM;
2938 except:
2939 reporter.errorXcpt('failed to get the name from the VM "%s"' % (oVM));
2940
2941 # Look it up the standard way.
2942 return self.addTestMachine(sName, fQuiet = True);
2943
2944 def getVmByUuid(self, sUuid):
2945 """
2946 Get a test VM by uuid. Returns None if not found, logged.
2947 """
2948 # Look it up in our 'cache'.
2949 for oVM in self.aoVMs:
2950 try:
2951 if oVM.id == sUuid:
2952 return oVM;
2953 except:
2954 reporter.errorXcpt('failed to get the UUID from the VM "%s"' % (oVM));
2955
2956 # Look it up the standard way.
2957 return self.addTestMachine(sUuid, fQuiet = True);
2958
2959 def waitOnProgress(self, oProgress, cMsTimeout = 1000000, fErrorOnTimeout = True, cMsInterval = 1000):
2960 """
2961 Waits for a progress object to complete. Returns the status code.
2962 """
2963 # Wait for progress no longer than cMsTimeout time period.
2964 tsStart = datetime.datetime.now()
2965 while True:
2966 self.processPendingEvents();
2967 try:
2968 if oProgress.completed:
2969 break;
2970 except:
2971 return -1;
2972 self.processPendingEvents();
2973
2974 tsNow = datetime.datetime.now()
2975 tsDelta = tsNow - tsStart
2976 if ((tsDelta.microseconds + tsDelta.seconds * 1000000) // 1000) > cMsTimeout:
2977 if fErrorOnTimeout:
2978 reporter.errorTimeout('Timeout while waiting for progress.')
2979 return -1
2980
2981 reporter.doPollWork('vbox.TestDriver.waitOnProgress');
2982 try: oProgress.waitForCompletion(cMsInterval);
2983 except: return -2;
2984
2985 try: rc = oProgress.resultCode;
2986 except: rc = -2;
2987 self.processPendingEvents();
2988 return rc;
2989
2990 def waitOnDirectSessionClose(self, oVM, cMsTimeout):
2991 """
2992 Waits for the VM process to close it's current direct session.
2993
2994 Returns None.
2995 """
2996 # Get the original values so we're not subject to
2997 try:
2998 eCurState = oVM.sessionState;
2999 if self.fpApiVer >= 5.0:
3000 sCurName = sOrgName = oVM.sessionName;
3001 else:
3002 sCurName = sOrgName = oVM.sessionType;
3003 if self.fpApiVer >= 4.2:
3004 iCurPid = iOrgPid = oVM.sessionPID;
3005 else:
3006 iCurPid = iOrgPid = oVM.sessionPid;
3007 except Exception as oXcpt:
3008 if ComError.notEqual(oXcpt, ComError.E_ACCESSDENIED):
3009 reporter.logXcpt();
3010 self.processPendingEvents();
3011 return None;
3012 self.processPendingEvents();
3013
3014 msStart = base.timestampMilli();
3015 while iCurPid == iOrgPid \
3016 and sCurName == sOrgName \
3017 and sCurName != '' \
3018 and base.timestampMilli() - msStart < cMsTimeout \
3019 and eCurState in (vboxcon.SessionState_Unlocking, vboxcon.SessionState_Spawning, vboxcon.SessionState_Locked,):
3020 self.processEvents(1000);
3021 try:
3022 eCurState = oVM.sessionState;
3023 sCurName = oVM.sessionName if self.fpApiVer >= 5.0 else oVM.sessionType;
3024 iCurPid = oVM.sessionPID if self.fpApiVer >= 4.2 else oVM.sessionPid;
3025 except Exception as oXcpt:
3026 if ComError.notEqual(oXcpt, ComError.E_ACCESSDENIED):
3027 reporter.logXcpt();
3028 break;
3029 self.processPendingEvents();
3030 self.processPendingEvents();
3031 return None;
3032
3033 def uploadStartupLogFile(self, oVM, sVmName):
3034 """
3035 Uploads the VBoxStartup.log when present.
3036 """
3037 fRc = True;
3038 try:
3039 sLogFile = os.path.join(oVM.logFolder, 'VBoxHardening.log');
3040 except:
3041 reporter.logXcpt();
3042 fRc = False;
3043 else:
3044 if os.path.isfile(sLogFile):
3045 reporter.addLogFile(sLogFile, 'log/release/vm', '%s hardening log' % (sVmName, ),
3046 sAltName = '%s-%s' % (sVmName, os.path.basename(sLogFile),));
3047 return fRc;
3048
3049 def annotateAndUploadProcessReport(self, sProcessReport, sFilename, sKind, sDesc):
3050 """
3051 Annotates the given VM process report and uploads it if successfull.
3052 """
3053 fRc = False;
3054 if self.oBuild is not None and self.oBuild.sInstallPath is not None:
3055 oResolver = btresolver.BacktraceResolver(self.sScratchPath, self.oBuild.sInstallPath,
3056 self.getBuildOs(), self.getBuildArch(),
3057 fnLog = reporter.log);
3058 fRcTmp = oResolver.prepareEnv();
3059 if fRcTmp:
3060 reporter.log('Successfully prepared environment');
3061 sReportDbgSym = oResolver.annotateReport(sProcessReport);
3062 if sReportDbgSym and len(sReportDbgSym) > 8:
3063 reporter.addLogString(sReportDbgSym, sFilename, sKind, sDesc);
3064 fRc = True;
3065 else:
3066 reporter.log('Annotating report failed');
3067 oResolver.cleanupEnv();
3068 return fRc;
3069
3070 def startVmEx(self, oVM, fWait = True, sType = None, sName = None, asEnv = None): # pylint: disable=too-many-locals,too-many-statements
3071 """
3072 Start the VM, returning the VM session and progress object on success.
3073 The session is also added to the task list and to the aoRemoteSessions set.
3074
3075 asEnv is a list of string on the putenv() form.
3076
3077 On failure (None, None) is returned and an error is logged.
3078 """
3079 # Massage and check the input.
3080 if sType is None:
3081 sType = self.sSessionType;
3082 if sName is None:
3083 try: sName = oVM.name;
3084 except: sName = 'bad-vm-handle';
3085 reporter.log('startVmEx: sName=%s fWait=%s sType=%s' % (sName, fWait, sType));
3086 if oVM is None:
3087 return (None, None);
3088
3089 ## @todo Do this elsewhere.
3090 # Hack alert. Disables all annoying GUI popups.
3091 if sType == 'gui' and not self.aoRemoteSessions:
3092 try:
3093 self.oVBox.setExtraData('GUI/Input/AutoCapture', 'false');
3094 if self.fpApiVer >= 3.2:
3095 self.oVBox.setExtraData('GUI/LicenseAgreed', '8');
3096 else:
3097 self.oVBox.setExtraData('GUI/LicenseAgreed', '7');
3098 self.oVBox.setExtraData('GUI/RegistrationData', 'triesLeft=0');
3099 self.oVBox.setExtraData('GUI/SUNOnlineData', 'triesLeft=0');
3100 self.oVBox.setExtraData('GUI/SuppressMessages', 'confirmVMReset,remindAboutMouseIntegrationOn,'
3101 'remindAboutMouseIntegrationOff,remindAboutPausedVMInput,confirmInputCapture,'
3102 'confirmGoingFullscreen,remindAboutInaccessibleMedia,remindAboutWrongColorDepth,'
3103 'confirmRemoveMedium,allPopupPanes,allMessageBoxes,all');
3104 self.oVBox.setExtraData('GUI/UpdateDate', 'never');
3105 self.oVBox.setExtraData('GUI/PreventBetaWarning', self.oVBox.version);
3106 except:
3107 reporter.logXcpt();
3108
3109 # The UUID for the name.
3110 try:
3111 sUuid = oVM.id;
3112 except:
3113 reporter.errorXcpt('failed to get the UUID for VM "%s"' % (oVM));
3114 return (None, None);
3115 self.processPendingEvents();
3116
3117 # Construct the environment.
3118 self.sSessionLogFile = '%s/VM-%s.log' % (self.sScratchPath, sUuid);
3119 try: os.remove(self.sSessionLogFile);
3120 except: pass;
3121 if self.sLogSessionDest:
3122 sLogDest = self.sLogSessionDest;
3123 else:
3124 sLogDest = 'file=%s' % (self.sSessionLogFile,);
3125 asEnvFinal = [
3126 'VBOX_LOG=%s' % (self.sLogSessionGroups,),
3127 'VBOX_LOG_FLAGS=%s' % (self.sLogSessionFlags,),
3128 'VBOX_LOG_DEST=nodeny %s' % (sLogDest,),
3129 'VBOX_RELEASE_LOG_FLAGS=append time',
3130 ];
3131 if sType == 'gui':
3132 asEnvFinal.append('VBOX_GUI_DBG_ENABLED=1');
3133 if asEnv is not None and asEnv:
3134 asEnvFinal += asEnv;
3135
3136 reporter.log2('Session environment:\n%s' % (asEnvFinal,));
3137
3138 # Shortcuts for local testing.
3139 oProgress = oWrapped = None;
3140 oTestVM = self.oTestVmSet.findTestVmByName(sName) if self.oTestVmSet is not None else None;
3141 try:
3142 if oTestVM is not None \
3143 and oTestVM.fSnapshotRestoreCurrent is True:
3144 if oVM.state is vboxcon.MachineState_Running:
3145 reporter.log2('Machine "%s" already running.' % (sName,));
3146 oProgress = None;
3147 oWrapped = self.openSession(oVM);
3148 else:
3149 reporter.log2('Checking if snapshot for machine "%s" exists.' % (sName,));
3150 oSessionWrapperRestore = self.openSession(oVM);
3151 if oSessionWrapperRestore is not None:
3152 oSnapshotCur = oVM.currentSnapshot;
3153 if oSnapshotCur is not None:
3154 reporter.log2('Restoring snapshot for machine "%s".' % (sName,));
3155 oSessionWrapperRestore.restoreSnapshot(oSnapshotCur);
3156 reporter.log2('Current snapshot for machine "%s" restored.' % (sName,));
3157 else:
3158 reporter.log('warning: no current snapshot for machine "%s" found.' % (sName,));
3159 oSessionWrapperRestore.close();
3160 except:
3161 reporter.errorXcpt();
3162 return (None, None);
3163
3164 oSession = None; # Must be initialized, otherwise the log statement at the end of the function can fail.
3165
3166 # Open a remote session, wait for this operation to complete.
3167 # (The loop is a kludge to deal with us racing the closing of the
3168 # direct session of a previous VM run. See waitOnDirectSessionClose.)
3169 if oWrapped is None:
3170 for i in range(10):
3171 try:
3172 if self.fpApiVer < 4.3 \
3173 or (self.fpApiVer == 4.3 and not hasattr(self.oVBoxMgr, 'getSessionObject')):
3174 oSession = self.oVBoxMgr.mgr.getSessionObject(self.oVBox); # pylint: disable=no-member
3175 elif self.fpApiVer < 5.2 \
3176 or (self.fpApiVer == 5.2 and hasattr(self.oVBoxMgr, 'vbox')):
3177 oSession = self.oVBoxMgr.getSessionObject(self.oVBox); # pylint: disable=no-member
3178 else:
3179 oSession = self.oVBoxMgr.getSessionObject(); # pylint: disable=no-member,no-value-for-parameter
3180 if self.fpApiVer < 3.3:
3181 oProgress = self.oVBox.openRemoteSession(oSession, sUuid, sType, '\n'.join(asEnvFinal));
3182 else:
3183 if self.uApiRevision >= self.makeApiRevision(6, 1, 0, 1):
3184 oProgress = oVM.launchVMProcess(oSession, sType, asEnvFinal);
3185 else:
3186 oProgress = oVM.launchVMProcess(oSession, sType, '\n'.join(asEnvFinal));
3187 break;
3188 except:
3189 if i == 9:
3190 reporter.errorXcpt('failed to start VM "%s" ("%s"), aborting.' % (sUuid, sName));
3191 return (None, None);
3192 oSession = None;
3193 if i >= 0:
3194 reporter.logXcpt('warning: failed to start VM "%s" ("%s") - retrying in %u secs.' % (sUuid, oVM, i)); # pylint: disable=line-too-long
3195 self.waitOnDirectSessionClose(oVM, 5000 + i * 1000);
3196 if fWait and oProgress is not None:
3197 rc = self.waitOnProgress(oProgress);
3198 if rc < 0:
3199 self.waitOnDirectSessionClose(oVM, 5000);
3200
3201 # VM failed to power up, still collect VBox.log, need to wrap the session object
3202 # in order to use the helper for adding the log files to the report.
3203 from testdriver.vboxwrappers import SessionWrapper;
3204 oTmp = SessionWrapper(oSession, oVM, self.oVBox, self.oVBoxMgr, self, True, sName, self.sSessionLogFile);
3205 oTmp.addLogsToReport();
3206
3207 # Try to collect a stack trace of the process for further investigation of any startup hangs.
3208 uPid = oTmp.getPid();
3209 if uPid is not None:
3210 sHostProcessInfoHung = utils.processGetInfo(uPid, fSudo = True);
3211 if sHostProcessInfoHung is not None:
3212 reporter.log('Trying to annotate the hung VM startup process report, please stand by...');
3213 fRcTmp = self.annotateAndUploadProcessReport(sHostProcessInfoHung, 'vmprocess-startup-hung.log',
3214 'process/report/vm', 'Annotated hung VM process state during startup'); # pylint: disable=line-too-long
3215 # Upload the raw log for manual annotation in case resolving failed.
3216 if not fRcTmp:
3217 reporter.log('Failed to annotate hung VM process report, uploading raw report');
3218 reporter.addLogString(sHostProcessInfoHung, 'vmprocess-startup-hung.log', 'process/report/vm',
3219 'Hung VM process state during startup');
3220
3221 try:
3222 if oSession is not None:
3223 oSession.close();
3224 except: pass;
3225 reportError(oProgress, 'failed to open session for "%s"' % (sName));
3226 self.uploadStartupLogFile(oVM, sName);
3227 return (None, None);
3228 reporter.log2('waitOnProgress -> %s' % (rc,));
3229
3230 # Wrap up the session object and push on to the list before returning it.
3231 if oWrapped is None:
3232 from testdriver.vboxwrappers import SessionWrapper;
3233 oWrapped = SessionWrapper(oSession, oVM, self.oVBox, self.oVBoxMgr, self, True, sName, self.sSessionLogFile);
3234
3235 oWrapped.registerEventHandlerForTask();
3236 self.aoRemoteSessions.append(oWrapped);
3237 if oWrapped is not self.aoRemoteSessions[len(self.aoRemoteSessions) - 1]:
3238 reporter.error('not by reference: oWrapped=%s aoRemoteSessions[%s]=%s'
3239 % (oWrapped, len(self.aoRemoteSessions) - 1,
3240 self.aoRemoteSessions[len(self.aoRemoteSessions) - 1]));
3241 self.addTask(oWrapped);
3242
3243 reporter.log2('startVmEx: oSession=%s, oSessionWrapper=%s, oProgress=%s' % (oSession, oWrapped, oProgress));
3244
3245 from testdriver.vboxwrappers import ProgressWrapper;
3246 return (oWrapped, ProgressWrapper(oProgress, self.oVBoxMgr, self,
3247 'starting %s' % (sName,)) if oProgress else None);
3248
3249 def startVm(self, oVM, sType=None, sName = None, asEnv = None):
3250 """ Simplified version of startVmEx. """
3251 oSession, _ = self.startVmEx(oVM, True, sType, sName, asEnv = asEnv);
3252 return oSession;
3253
3254 def startVmByNameEx(self, sName, fWait=True, sType=None, asEnv = None):
3255 """
3256 Start the VM, returning the VM session and progress object on success.
3257 The session is also added to the task list and to the aoRemoteSessions set.
3258
3259 On failure (None, None) is returned and an error is logged.
3260 """
3261 oVM = self.getVmByName(sName);
3262 if oVM is None:
3263 return (None, None);
3264 return self.startVmEx(oVM, fWait, sType, sName, asEnv = asEnv);
3265
3266 def startVmByName(self, sName, sType=None, asEnv = None):
3267 """
3268 Start the VM, returning the VM session on success. The session is
3269 also added to the task list and to the aoRemoteSessions set.
3270
3271 On failure None is returned and an error is logged.
3272 """
3273 oSession, _ = self.startVmByNameEx(sName, True, sType, asEnv = asEnv);
3274 return oSession;
3275
3276 def terminateVmBySession(self, oSession, oProgress = None, fTakeScreenshot = None): # pylint: disable=too-many-statements
3277 """
3278 Terminates the VM specified by oSession and adds the release logs to
3279 the test report.
3280
3281 This will try achieve this by using powerOff, but will resort to
3282 tougher methods if that fails.
3283
3284 The session will always be removed from the task list.
3285 The session will be closed unless we fail to kill the process.
3286 The session will be removed from the remote session list if closed.
3287
3288 The progress object (a wrapper!) is for teleportation and similar VM
3289 operations, it will be attempted canceled before powering off the VM.
3290 Failures are logged but ignored.
3291 The progress object will always be removed from the task list.
3292
3293 Returns True if powerOff and session close both succeed.
3294 Returns False if on failure (logged), including when we successfully
3295 kill the VM process.
3296 """
3297
3298 reporter.log2('terminateVmBySession: oSession=%s (pid=%s) oProgress=%s' % (oSession.sName, oSession.getPid(), oProgress));
3299
3300 if self.fVmNoTerminate:
3301 reporter.log('terminateVmBySession: Skipping, as --vbox-vm-no-terminate was specified');
3302 # Make sure that we still process the events the VM needs.
3303 self.sleep(24 * 60 * 60 * 1000);
3304
3305 # Call getPid first to make sure the PID is cached in the wrapper.
3306 oSession.getPid();
3307
3308 #
3309 # If the host is out of memory, just skip all the info collection as it
3310 # requires memory too and seems to wedge.
3311 #
3312 sHostProcessInfo = None;
3313 sHostProcessInfoHung = None;
3314 sLastScreenshotPath = None;
3315 sOsKernelLog = None;
3316 sVgaText = None;
3317 asMiscInfos = [];
3318
3319 if not oSession.fHostMemoryLow:
3320 # Try to fetch the VM process info before meddling with its state.
3321 if self.fAlwaysUploadLogs or reporter.testErrorCount() > 0:
3322 sHostProcessInfo = utils.processGetInfo(oSession.getPid(), fSudo = True);
3323
3324 #
3325 # Pause the VM if we're going to take any screenshots or dig into the
3326 # guest. Failures are quitely ignored.
3327 #
3328 if self.fAlwaysUploadLogs or reporter.testErrorCount() > 0:
3329 try:
3330 if oSession.oVM.state in [ vboxcon.MachineState_Running,
3331 vboxcon.MachineState_LiveSnapshotting,
3332 vboxcon.MachineState_Teleporting ]:
3333 oSession.o.console.pause();
3334 except:
3335 reporter.logXcpt();
3336
3337 #
3338 # Take Screenshot and upload it (see below) to Test Manager if appropriate/requested.
3339 #
3340 if fTakeScreenshot is True or self.fAlwaysUploadScreenshots or reporter.testErrorCount() > 0:
3341 sLastScreenshotPath = os.path.join(self.sScratchPath, "LastScreenshot-%s.png" % oSession.sName);
3342 fRc = oSession.takeScreenshot(sLastScreenshotPath);
3343 if fRc is not True:
3344 sLastScreenshotPath = None;
3345
3346 # Query the OS kernel log from the debugger if appropriate/requested.
3347 if self.fAlwaysUploadLogs or reporter.testErrorCount() > 0:
3348 sOsKernelLog = oSession.queryOsKernelLog();
3349
3350 # Do "info vgatext all" separately.
3351 if self.fAlwaysUploadLogs or reporter.testErrorCount() > 0:
3352 sVgaText = oSession.queryDbgInfoVgaText();
3353
3354 # Various infos (do after kernel because of symbols).
3355 if self.fAlwaysUploadLogs or reporter.testErrorCount() > 0:
3356 # Dump the guest stack for all CPUs.
3357 cCpus = oSession.getCpuCount();
3358 if cCpus > 0:
3359 for iCpu in xrange(0, cCpus):
3360 sThis = oSession.queryDbgGuestStack(iCpu);
3361 if sThis:
3362 asMiscInfos += [
3363 '================ start guest stack VCPU %s ================\n' % (iCpu,),
3364 sThis,
3365 '================ end guest stack VCPU %s ==================\n' % (iCpu,),
3366 ];
3367
3368 for sInfo, sArg in [ ('mode', 'all'),
3369 ('fflags', ''),
3370 ('cpumguest', 'verbose all'),
3371 ('cpumguestinstr', 'symbol all'),
3372 ('exits', ''),
3373 ('pic', ''),
3374 ('apic', ''),
3375 ('apiclvt', ''),
3376 ('apictimer', ''),
3377 ('ioapic', ''),
3378 ('pit', ''),
3379 ('phys', ''),
3380 ('clocks', ''),
3381 ('timers', ''),
3382 ('gdt', ''),
3383 ('ldt', ''),
3384 ]:
3385 if sInfo in ['apic',] and self.fpApiVer < 5.1: # asserts and burns
3386 continue;
3387 sThis = oSession.queryDbgInfo(sInfo, sArg);
3388 if sThis:
3389 if sThis[-1] != '\n':
3390 sThis += '\n';
3391 asMiscInfos += [
3392 '================ start %s %s ================\n' % (sInfo, sArg),
3393 sThis,
3394 '================ end %s %s ==================\n' % (sInfo, sArg),
3395 ];
3396
3397 #
3398 # Terminate the VM
3399 #
3400
3401 # Cancel the progress object if specified.
3402 if oProgress is not None:
3403 if not oProgress.isCompleted() and oProgress.isCancelable():
3404 reporter.log2('terminateVmBySession: canceling "%s"...' % (oProgress.sName));
3405 try:
3406 oProgress.o.cancel();
3407 except:
3408 reporter.logXcpt();
3409 else:
3410 oProgress.wait();
3411 self.removeTask(oProgress);
3412
3413 # Check if the VM has terminated by itself before powering it off.
3414 fClose = True;
3415 fRc = True;
3416 if oSession.needsPoweringOff():
3417 reporter.log('terminateVmBySession: powering off "%s"...' % (oSession.sName,));
3418 fRc = oSession.powerOff(fFudgeOnFailure = False);
3419 if fRc is not True:
3420 # power off failed, try terminate it in a nice manner.
3421 fRc = False;
3422 uPid = oSession.getPid();
3423 if uPid is not None:
3424 #
3425 # Collect some information about the VM process first to have
3426 # some state information for further investigation why powering off failed.
3427 #
3428 sHostProcessInfoHung = utils.processGetInfo(uPid, fSudo = True);
3429
3430 # Exterminate...
3431 reporter.error('terminateVmBySession: Terminating PID %u (VM %s)' % (uPid, oSession.sName));
3432 fClose = base.processTerminate(uPid);
3433 if fClose is True:
3434 self.waitOnDirectSessionClose(oSession.oVM, 5000);
3435 fClose = oSession.waitForTask(1000);
3436
3437 if fClose is not True:
3438 # Being nice failed...
3439 reporter.error('terminateVmBySession: Termination failed, trying to kill PID %u (VM %s) instead' \
3440 % (uPid, oSession.sName));
3441 fClose = base.processKill(uPid);
3442 if fClose is True:
3443 self.waitOnDirectSessionClose(oSession.oVM, 5000);
3444 fClose = oSession.waitForTask(1000);
3445 if fClose is not True:
3446 reporter.error('terminateVmBySession: Failed to kill PID %u (VM %s)' % (uPid, oSession.sName));
3447
3448 # The final steps.
3449 if fClose is True:
3450 reporter.log('terminateVmBySession: closing session "%s"...' % (oSession.sName,));
3451 oSession.close();
3452 self.waitOnDirectSessionClose(oSession.oVM, 10000);
3453 try:
3454 eState = oSession.oVM.state;
3455 except:
3456 reporter.logXcpt();
3457 else:
3458 if eState == vboxcon.MachineState_Aborted:
3459 reporter.error('terminateVmBySession: The VM "%s" aborted!' % (oSession.sName,));
3460 self.removeTask(oSession);
3461
3462 #
3463 # Add the release log, debug log and a screenshot of the VM to the test report.
3464 #
3465 if self.fAlwaysUploadLogs or reporter.testErrorCount() > 0:
3466 oSession.addLogsToReport();
3467
3468 # Add a screenshot if it has been requested and taken successfully.
3469 if sLastScreenshotPath is not None:
3470 if reporter.testErrorCount() > 0:
3471 reporter.addLogFile(sLastScreenshotPath, 'screenshot/failure', 'Last VM screenshot');
3472 else:
3473 reporter.addLogFile(sLastScreenshotPath, 'screenshot/success', 'Last VM screenshot');
3474
3475 # Add the guest OS log if it has been requested and taken successfully.
3476 if sOsKernelLog is not None:
3477 reporter.addLogString(sOsKernelLog, 'kernel.log', 'log/guest/kernel', 'Guest OS kernel log');
3478
3479 # Add "info vgatext all" if we've got it.
3480 if sVgaText is not None:
3481 reporter.addLogString(sVgaText, 'vgatext.txt', 'info/vgatext', 'info vgatext all');
3482
3483 # Add the "info xxxx" items if we've got any.
3484 if asMiscInfos:
3485 reporter.addLogString(u''.join(asMiscInfos), 'info.txt', 'info/collection', 'A bunch of info items.');
3486
3487 # Add the host process info if we were able to retrieve it.
3488 if sHostProcessInfo is not None:
3489 reporter.log('Trying to annotate the VM process report, please stand by...');
3490 fRcTmp = self.annotateAndUploadProcessReport(sHostProcessInfo, 'vmprocess.log',
3491 'process/report/vm', 'Annotated VM process state');
3492 # Upload the raw log for manual annotation in case resolving failed.
3493 if not fRcTmp:
3494 reporter.log('Failed to annotate VM process report, uploading raw report');
3495 reporter.addLogString(sHostProcessInfo, 'vmprocess.log', 'process/report/vm', 'VM process state');
3496
3497 # Add the host process info for failed power off attempts if we were able to retrieve it.
3498 if sHostProcessInfoHung is not None:
3499 reporter.log('Trying to annotate the hung VM process report, please stand by...');
3500 fRcTmp = self.annotateAndUploadProcessReport(sHostProcessInfoHung, 'vmprocess-hung.log',
3501 'process/report/vm', 'Annotated hung VM process state');
3502 # Upload the raw log for manual annotation in case resolving failed.
3503 if not fRcTmp:
3504 reporter.log('Failed to annotate hung VM process report, uploading raw report');
3505 fRcTmp = reporter.addLogString(sHostProcessInfoHung, 'vmprocess-hung.log', 'process/report/vm',
3506 'Hung VM process state');
3507 if not fRcTmp:
3508 try: reporter.log('******* START vmprocess-hung.log *******\n%s\n******* END vmprocess-hung.log *******\n'
3509 % (sHostProcessInfoHung,));
3510 except: pass; # paranoia
3511
3512 # Upload the screen video recordings if appropriate.
3513 if self.fAlwaysUploadRecordings or reporter.testErrorCount() > 0:
3514 reporter.log2('Uploading %d screen recordings ...' % (len(self.adRecordingFiles),));
3515 for dRecFile in self.adRecordingFiles:
3516 reporter.log2('Uploading screen recording "%s" (screen %d)' % (dRecFile['file'], dRecFile['id']));
3517 reporter.addLogFile(dRecFile['file'],
3518 'screenrecording/failure' if reporter.testErrorCount() > 0 else 'screenrecording/success',
3519 'Recording of screen #%d' % (dRecFile['id'],));
3520
3521 return fRc;
3522
3523
3524 #
3525 # Some information query functions (mix).
3526 #
3527 # Methods require the VBox API. If the information is provided by both
3528 # the testboxscript as well as VBox API, we'll check if it matches.
3529 #
3530
3531 def _hasHostCpuFeature(self, sEnvVar, sEnum, fpApiMinVer, fQuiet):
3532 """
3533 Common Worker for hasHostNestedPaging() and hasHostHwVirt().
3534
3535 Returns True / False.
3536 Raises exception on environment / host mismatch.
3537 """
3538 fEnv = os.environ.get(sEnvVar, None);
3539 if fEnv is not None:
3540 fEnv = fEnv.lower() not in [ 'false', 'f', 'not', 'no', 'n', '0', ];
3541
3542 fVBox = None;
3543 self.importVBoxApi();
3544 if self.fpApiVer >= fpApiMinVer and hasattr(vboxcon, sEnum):
3545 try:
3546 fVBox = self.oVBox.host.getProcessorFeature(getattr(vboxcon, sEnum));
3547 except:
3548 if not fQuiet:
3549 reporter.logXcpt();
3550
3551 if fVBox is not None:
3552 if fEnv is not None:
3553 if fEnv != fVBox and not fQuiet:
3554 reporter.log('TestBox configuration overwritten: fVBox=%s (%s) vs. fEnv=%s (%s)'
3555 % (fVBox, sEnum, fEnv, sEnvVar));
3556 return fEnv;
3557 return fVBox;
3558 if fEnv is not None:
3559 return fEnv;
3560 return False;
3561
3562 def hasHostHwVirt(self, fQuiet = False):
3563 """
3564 Checks if hardware assisted virtualization is supported by the host.
3565
3566 Returns True / False.
3567 Raises exception on environment / host mismatch.
3568 """
3569 return self._hasHostCpuFeature('TESTBOX_HAS_HW_VIRT', 'ProcessorFeature_HWVirtEx', 3.1, fQuiet);
3570
3571 def hasHostNestedPaging(self, fQuiet = False):
3572 """
3573 Checks if nested paging is supported by the host.
3574
3575 Returns True / False.
3576 Raises exception on environment / host mismatch.
3577 """
3578 return self._hasHostCpuFeature('TESTBOX_HAS_NESTED_PAGING', 'ProcessorFeature_NestedPaging', 4.2, fQuiet) \
3579 and self.hasHostHwVirt(fQuiet);
3580
3581 def hasHostNestedHwVirt(self, fQuiet = False):
3582 """
3583 Checks if nested hardware-assisted virtualization is supported by the host.
3584
3585 Returns True / False.
3586 Raises exception on environment / host mismatch.
3587 """
3588 return self._hasHostCpuFeature('TESTBOX_HAS_NESTED_HWVIRT', 'ProcessorFeature_NestedHWVirt', 6.0, fQuiet) \
3589 and self.hasHostHwVirt(fQuiet);
3590
3591 def hasHostLongMode(self, fQuiet = False):
3592 """
3593 Checks if the host supports 64-bit guests.
3594
3595 Returns True / False.
3596 Raises exception on environment / host mismatch.
3597 """
3598 # Note that the testboxscript doesn't export this variable atm.
3599 return self._hasHostCpuFeature('TESTBOX_HAS_LONG_MODE', 'ProcessorFeature_LongMode', 3.1, fQuiet);
3600
3601 def getHostCpuCount(self, fQuiet = False):
3602 """
3603 Returns the number of CPUs on the host.
3604
3605 Returns True / False.
3606 Raises exception on environment / host mismatch.
3607 """
3608 cEnv = os.environ.get('TESTBOX_CPU_COUNT', None);
3609 if cEnv is not None:
3610 cEnv = int(cEnv);
3611
3612 try:
3613 cVBox = self.oVBox.host.processorOnlineCount;
3614 except:
3615 if not fQuiet:
3616 reporter.logXcpt();
3617 cVBox = None;
3618
3619 if cVBox is not None:
3620 if cEnv is not None:
3621 assert cVBox == cEnv, 'Misconfigured TestBox: VBox: %u CPUs, testboxscript: %u CPUs' % (cVBox, cEnv);
3622 return cVBox;
3623 if cEnv is not None:
3624 return cEnv;
3625 return 1;
3626
3627 def _getHostCpuDesc(self, fQuiet = False):
3628 """
3629 Internal method used for getting the host CPU description from VBoxSVC.
3630 Returns description string, on failure an empty string is returned.
3631 """
3632 try:
3633 return self.oVBox.host.getProcessorDescription(0);
3634 except:
3635 if not fQuiet:
3636 reporter.logXcpt();
3637 return '';
3638
3639 def isHostCpuAmd(self, fQuiet = False):
3640 """
3641 Checks if the host CPU vendor is AMD.
3642
3643 Returns True / False.
3644 """
3645 sCpuDesc = self._getHostCpuDesc(fQuiet);
3646 return 'AMD' in sCpuDesc or sCpuDesc == 'AuthenticAMD';
3647
3648 def isHostCpuIntel(self, fQuiet = False):
3649 """
3650 Checks if the host CPU vendor is Intel.
3651
3652 Returns True / False.
3653 """
3654 sCpuDesc = self._getHostCpuDesc(fQuiet);
3655 return sCpuDesc.startswith("Intel") or sCpuDesc == 'GenuineIntel';
3656
3657 def isHostCpuVia(self, fQuiet = False):
3658 """
3659 Checks if the host CPU vendor is VIA (or Centaur).
3660
3661 Returns True / False.
3662 """
3663 sCpuDesc = self._getHostCpuDesc(fQuiet);
3664 return sCpuDesc.startswith("VIA") or sCpuDesc == 'CentaurHauls';
3665
3666 def isHostCpuShanghai(self, fQuiet = False):
3667 """
3668 Checks if the host CPU vendor is Shanghai (or Zhaoxin).
3669
3670 Returns True / False.
3671 """
3672 sCpuDesc = self._getHostCpuDesc(fQuiet);
3673 return sCpuDesc.startswith("ZHAOXIN") or sCpuDesc.strip(' ') == 'Shanghai';
3674
3675 def isHostCpuP4(self, fQuiet = False):
3676 """
3677 Checks if the host CPU is a Pentium 4 / Pentium D.
3678
3679 Returns True / False.
3680 """
3681 if not self.isHostCpuIntel(fQuiet):
3682 return False;
3683
3684 (uFamilyModel, _, _, _) = self.oVBox.host.getProcessorCPUIDLeaf(0, 0x1, 0);
3685 return ((uFamilyModel >> 8) & 0xf) == 0xf;
3686
3687 def hasRawModeSupport(self, fQuiet = False):
3688 """
3689 Checks if raw-mode is supported by VirtualBox that the testbox is
3690 configured for it.
3691
3692 Returns True / False.
3693 Raises no exceptions.
3694
3695 Note! Differs from the rest in that we don't require the
3696 TESTBOX_WITH_RAW_MODE value to match the API. It is
3697 sometimes helpful to disable raw-mode on individual
3698 test boxes. (This probably goes for
3699 """
3700 # The environment variable can be used to disable raw-mode.
3701 fEnv = os.environ.get('TESTBOX_WITH_RAW_MODE', None);
3702 if fEnv is not None:
3703 fEnv = fEnv.lower() not in [ 'false', 'f', 'not', 'no', 'n', '0', ];
3704 if fEnv is False:
3705 return False;
3706
3707 # Starting with 5.0 GA / RC2 the API can tell us whether VBox was built
3708 # with raw-mode support or not.
3709 self.importVBoxApi();
3710 if self.fpApiVer >= 5.0:
3711 try:
3712 fVBox = self.oVBox.systemProperties.rawModeSupported;
3713 except:
3714 if not fQuiet:
3715 reporter.logXcpt();
3716 fVBox = True;
3717 if fVBox is False:
3718 return False;
3719
3720 return True;
3721
3722 #
3723 # Testdriver execution methods.
3724 #
3725
3726 def handleTask(self, oTask, sMethod):
3727 """
3728 Callback method for handling unknown tasks in the various run loops.
3729
3730 The testdriver should override this if it already tasks running when
3731 calling startVmAndConnectToTxsViaTcp, txsRunTest or similar methods.
3732 Call super to handle unknown tasks.
3733
3734 Returns True if handled, False if not.
3735 """
3736 reporter.error('%s: unknown task %s' % (sMethod, oTask));
3737 return False;
3738
3739 def txsDoTask(self, oSession, oTxsSession, fnAsync, aArgs):
3740 """
3741 Generic TXS task wrapper which waits both on the TXS and the session tasks.
3742
3743 Returns False on error, logged.
3744 Returns task result on success.
3745 """
3746 # All async methods ends with the following two args.
3747 cMsTimeout = aArgs[-2];
3748 fIgnoreErrors = aArgs[-1];
3749
3750 fRemoveVm = self.addTask(oSession);
3751 fRemoveTxs = self.addTask(oTxsSession);
3752
3753 reporter.log2('txsDoTask(%s): Running' % (str(fnAsync)));
3754 rc = fnAsync(*aArgs); # pylint: disable=star-args
3755 if rc is True:
3756 rc = False;
3757 oTask = self.waitForTasks(cMsTimeout + 1);
3758 if oTask is oTxsSession:
3759 if oTxsSession.isSuccess():
3760 rc = oTxsSession.getResult();
3761 elif fIgnoreErrors is True:
3762 reporter.log( 'txsDoTask(%s): task failed (%s)' % (str(fnAsync), oTxsSession.getLastReply()[1],));
3763 else:
3764 reporter.error('txsDoTask(%s): task failed (%s)' % (str(fnAsync), oTxsSession.getLastReply()[1],));
3765 else:
3766 oTxsSession.cancelTask();
3767 if oTask is None:
3768 if fIgnoreErrors is True:
3769 reporter.log( 'txsDoTask(%s): The task timed out.' % (str(fnAsync)));
3770 else:
3771 reporter.errorTimeout('txsDoTask(%s): The task timed out.' % (str(fnAsync)));
3772 elif oTask is oSession:
3773 reporter.error('txsDoTask(%s): The VM terminated unexpectedly' % (str(fnAsync)));
3774 else:
3775 if fIgnoreErrors is True:
3776 reporter.log( 'txsDoTask(%s): An unknown task %s was returned' % (str(fnAsync), oTask,));
3777 else:
3778 reporter.error('txsDoTask(%s): An unknown task %s was returned' % (str(fnAsync), oTask,));
3779 else:
3780 reporter.error('txsDoTask(%s) returned %s' % (str(fnAsync), rc,));
3781
3782 if fRemoveTxs:
3783 self.removeTask(oTxsSession);
3784 if fRemoveVm:
3785 self.removeTask(oSession);
3786 return rc;
3787
3788 # pylint: disable=missing-docstring
3789
3790 def txsDisconnect(self, oSession, oTxsSession, cMsTimeout = 30000, fIgnoreErrors = False):
3791 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncDisconnect,
3792 (self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3793
3794 def txsVer(self, oSession, oTxsSession, cMsTimeout = 30000, fIgnoreErrors = False):
3795 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncVer,
3796 (self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3797
3798 def txsUuid(self, oSession, oTxsSession, cMsTimeout = 30000, fIgnoreErrors = False):
3799 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncUuid,
3800 (self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3801
3802 def txsMkDir(self, oSession, oTxsSession, sRemoteDir, fMode = 0o700, cMsTimeout = 30000, fIgnoreErrors = False):
3803 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncMkDir,
3804 (sRemoteDir, fMode, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3805
3806 def txsMkDirPath(self, oSession, oTxsSession, sRemoteDir, fMode = 0o700, cMsTimeout = 30000, fIgnoreErrors = False):
3807 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncMkDirPath,
3808 (sRemoteDir, fMode, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3809
3810 def txsMkSymlink(self, oSession, oTxsSession, sLinkTarget, sLink, cMsTimeout = 30000, fIgnoreErrors = False):
3811 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncMkSymlink,
3812 (sLinkTarget, sLink, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3813
3814 def txsRmDir(self, oSession, oTxsSession, sRemoteDir, cMsTimeout = 30000, fIgnoreErrors = False):
3815 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncRmDir,
3816 (sRemoteDir, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3817
3818 def txsRmFile(self, oSession, oTxsSession, sRemoteFile, cMsTimeout = 30000, fIgnoreErrors = False):
3819 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncRmFile,
3820 (sRemoteFile, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3821
3822 def txsRmSymlink(self, oSession, oTxsSession, sRemoteSymlink, cMsTimeout = 30000, fIgnoreErrors = False):
3823 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncRmSymlink,
3824 (sRemoteSymlink, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3825
3826 def txsRmTree(self, oSession, oTxsSession, sRemoteTree, cMsTimeout = 30000, fIgnoreErrors = False):
3827 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncRmTree,
3828 (sRemoteTree, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3829
3830 def txsIsDir(self, oSession, oTxsSession, sRemoteDir, cMsTimeout = 30000, fIgnoreErrors = False):
3831 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncIsDir,
3832 (sRemoteDir, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3833
3834 def txsIsFile(self, oSession, oTxsSession, sRemoteFile, cMsTimeout = 30000, fIgnoreErrors = False):
3835 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncIsFile,
3836 (sRemoteFile, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3837
3838 def txsIsSymlink(self, oSession, oTxsSession, sRemoteSymlink, cMsTimeout = 30000, fIgnoreErrors = False):
3839 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncIsSymlink,
3840 (sRemoteSymlink, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3841
3842 def txsCopyFile(self, oSession, oTxsSession, sSrcFile, sDstFile, fMode = 0, cMsTimeout = 30000, fIgnoreErrors = False):
3843 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncCopyFile, \
3844 (sSrcFile, sDstFile, fMode, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3845
3846 def txsUploadFile(self, oSession, oTxsSession, sLocalFile, sRemoteFile, cMsTimeout = 30000, fIgnoreErrors = False):
3847 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncUploadFile, \
3848 (sLocalFile, sRemoteFile, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3849
3850 def txsUploadString(self, oSession, oTxsSession, sContent, sRemoteFile, cMsTimeout = 30000, fIgnoreErrors = False):
3851 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncUploadString, \
3852 (sContent, sRemoteFile, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3853
3854 def txsDownloadFile(self, oSession, oTxsSession, sRemoteFile, sLocalFile, cMsTimeout = 30000, fIgnoreErrors = False):
3855 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncDownloadFile, \
3856 (sRemoteFile, sLocalFile, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3857
3858 def txsDownloadFiles(self, oSession, oTxsSession, aasFiles, fAddToLog = True, fIgnoreErrors = False):
3859 """
3860 Convenience function to get files from the guest, storing them in the
3861 scratch and adding them to the test result set (optional, but default).
3862
3863 The aasFiles parameter contains an array of with guest-path + host-path
3864 pairs, optionally a file 'kind', description and an alternative upload
3865 filename can also be specified.
3866
3867 Host paths are relative to the scratch directory or they must be given
3868 in absolute form. The guest path should be using guest path style.
3869
3870 Returns True on success.
3871 Returns False on failure (unless fIgnoreErrors is set), logged.
3872 """
3873 for asEntry in aasFiles:
3874 # Unpack:
3875 sGstFile = asEntry[0];
3876 sHstFile = asEntry[1];
3877 sKind = asEntry[2] if len(asEntry) > 2 and asEntry[2] else 'misc/other';
3878 sDescription = asEntry[3] if len(asEntry) > 3 and asEntry[3] else '';
3879 sAltName = asEntry[4] if len(asEntry) > 4 and asEntry[4] else None;
3880 assert len(asEntry) <= 5 and sGstFile and sHstFile;
3881 if not os.path.isabs(sHstFile):
3882 sHstFile = os.path.join(self.sScratchPath, sHstFile);
3883
3884 reporter.log2('Downloading file "%s" to "%s" ...' % (sGstFile, sHstFile,));
3885
3886 try: os.unlink(sHstFile); ## @todo txsDownloadFile doesn't truncate the output file.
3887 except: pass;
3888
3889 fRc = self.txsDownloadFile(oSession, oTxsSession, sGstFile, sHstFile, 30 * 1000, fIgnoreErrors);
3890 if fRc:
3891 if fAddToLog:
3892 reporter.addLogFile(sHstFile, sKind, sDescription, sAltName);
3893 else:
3894 if fIgnoreErrors is not True:
3895 return reporter.error('error downloading file "%s" to "%s"' % (sGstFile, sHstFile));
3896 reporter.log('warning: file "%s" was not downloaded, ignoring.' % (sGstFile,));
3897 return True;
3898
3899 def txsDownloadString(self, oSession, oTxsSession, sRemoteFile, sEncoding = 'utf-8', fIgnoreEncodingErrors = True,
3900 cMsTimeout = 30000, fIgnoreErrors = False):
3901 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncDownloadString,
3902 (sRemoteFile, sEncoding, fIgnoreEncodingErrors, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3903
3904 def txsPackFile(self, oSession, oTxsSession, sRemoteFile, sRemoteSource, cMsTimeout = 30000, fIgnoreErrors = False):
3905 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncPackFile, \
3906 (sRemoteFile, sRemoteSource, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3907
3908 def txsUnpackFile(self, oSession, oTxsSession, sRemoteFile, sRemoteDir, cMsTimeout = 30000, fIgnoreErrors = False):
3909 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncUnpackFile, \
3910 (sRemoteFile, sRemoteDir, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3911
3912 def txsExpandString(self, oSession, oTxsSession, sString, cMsTimeout = 30000, fIgnoreErrors = False):
3913 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncExpandString, \
3914 (sString, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3915
3916 # pylint: enable=missing-docstring
3917
3918 def txsCdWait(self,
3919 oSession, # type: vboxwrappers.SessionWrapper
3920 oTxsSession, # type: txsclient.Session
3921 cMsTimeout = 30000, # type: int
3922 sFile = None # type: String
3923 ): # -> bool
3924 """
3925 Mostly an internal helper for txsRebootAndReconnectViaTcp and
3926 startVmAndConnectToTxsViaTcp that waits for the CDROM drive to become
3927 ready. It does this by polling for a file it knows to exist on the CD.
3928
3929 Returns True on success.
3930
3931 Returns False on failure, logged.
3932 """
3933
3934 if sFile is None:
3935 sFile = 'valkit.txt';
3936
3937 reporter.log('txsCdWait: Waiting for file "%s" to become available ...' % (sFile,));
3938
3939 fRemoveVm = self.addTask(oSession);
3940 fRemoveTxs = self.addTask(oTxsSession);
3941 cMsTimeout = self.adjustTimeoutMs(cMsTimeout);
3942 msStart = base.timestampMilli();
3943 cMsTimeout2 = cMsTimeout;
3944 fRc = oTxsSession.asyncIsFile('${CDROM}/%s' % (sFile,), cMsTimeout2);
3945 if fRc is True:
3946 while True:
3947 # wait for it to complete.
3948 oTask = self.waitForTasks(cMsTimeout2 + 1);
3949 if oTask is not oTxsSession:
3950 oTxsSession.cancelTask();
3951 if oTask is None:
3952 reporter.errorTimeout('txsCdWait: The task timed out (after %s ms).'
3953 % (base.timestampMilli() - msStart,));
3954 elif oTask is oSession:
3955 reporter.error('txsCdWait: The VM terminated unexpectedly');
3956 else:
3957 reporter.error('txsCdWait: An unknown task %s was returned' % (oTask,));
3958 fRc = False;
3959 break;
3960 if oTxsSession.isSuccess():
3961 break;
3962
3963 # Check for timeout.
3964 cMsElapsed = base.timestampMilli() - msStart;
3965 if cMsElapsed >= cMsTimeout:
3966 reporter.error('txsCdWait: timed out');
3967 fRc = False;
3968 break;
3969 # delay.
3970 self.sleep(1);
3971
3972 # resubmit the task.
3973 cMsTimeout2 = msStart + cMsTimeout - base.timestampMilli();
3974 cMsTimeout2 = max(cMsTimeout2, 500);
3975 fRc = oTxsSession.asyncIsFile('${CDROM}/%s' % (sFile,), cMsTimeout2);
3976 if fRc is not True:
3977 reporter.error('txsCdWait: asyncIsFile failed');
3978 break;
3979 else:
3980 reporter.error('txsCdWait: asyncIsFile failed');
3981
3982 if not fRc:
3983 # Do some diagnosis to find out why this failed.
3984 ## @todo Identify guest OS type and only run one of the following commands.
3985 fIsNotWindows = True;
3986 reporter.log('txsCdWait: Listing root contents of ${CDROM}:');
3987 if fIsNotWindows:
3988 reporter.log('txsCdWait: Tiggering udevadm ...');
3989 oTxsSession.syncExec("/sbin/udevadm", ("/sbin/udevadm", "trigger", "--verbose"), fIgnoreErrors = True);
3990 time.sleep(15);
3991 oTxsSession.syncExec("/bin/ls", ("/bin/ls", "-al", "${CDROM}"), fIgnoreErrors = True);
3992 reporter.log('txsCdWait: Listing media directory:');
3993 oTxsSession.syncExec('/bin/ls', ('/bin/ls', '-l', '-a', '-R', '/media'), fIgnoreErrors = True);
3994 reporter.log('txsCdWait: Listing mount points / drives:');
3995 oTxsSession.syncExec('/bin/mount', ('/bin/mount',), fIgnoreErrors = True);
3996 oTxsSession.syncExec('/bin/cat', ('/bin/cat', '/etc/fstab'), fIgnoreErrors = True);
3997 oTxsSession.syncExec('/bin/dmesg', ('/bin/dmesg',), fIgnoreErrors = True);
3998 oTxsSession.syncExec('/usr/bin/lshw', ('/usr/bin/lshw', '-c', 'disk'), fIgnoreErrors = True);
3999 oTxsSession.syncExec('/bin/journalctl',
4000 ('/bin/journalctl', '-x', '-b'), fIgnoreErrors = True);
4001 oTxsSession.syncExec('/bin/journalctl',
4002 ('/bin/journalctl', '-x', '-b', '/usr/lib/udisks2/udisksd'), fIgnoreErrors = True);
4003 oTxsSession.syncExec('/usr/bin/udisksctl',
4004 ('/usr/bin/udisksctl', 'info', '-b', '/dev/sr0'), fIgnoreErrors = True);
4005 oTxsSession.syncExec('/bin/systemctl',
4006 ('/bin/systemctl', 'status', 'udisks2'), fIgnoreErrors = True);
4007 oTxsSession.syncExec('/bin/ps',
4008 ('/bin/ps', '-a', '-u', '-x'), fIgnoreErrors = True);
4009 reporter.log('txsCdWait: Mounting manually ...');
4010 for _ in range(3):
4011 oTxsSession.syncExec('/bin/mount', ('/bin/mount', '/dev/sr0', '${CDROM}'), fIgnoreErrors = True);
4012 time.sleep(5);
4013 reporter.log('txsCdWait: Re-Listing media directory:');
4014 oTxsSession.syncExec('/bin/ls', ('/bin/ls', '-l', '-a', '-R', '/media'), fIgnoreErrors = True);
4015 else:
4016 # ASSUMES that we always install Windows on drive C right now.
4017 sWinDir = "C:\\Windows\\System32\\";
4018 # Should work since WinXP Pro.
4019 oTxsSession.syncExec(sWinDir + "wbem\\WMIC.exe",
4020 ("WMIC.exe", "logicaldisk", "get",
4021 "deviceid, volumename, description"),
4022 fIgnoreErrors = True);
4023 oTxsSession.syncExec(sWinDir + " cmd.exe",
4024 ('cmd.exe', '/C', 'dir', '${CDROM}'),
4025 fIgnoreErrors = True);
4026
4027 if fRemoveTxs:
4028 self.removeTask(oTxsSession);
4029 if fRemoveVm:
4030 self.removeTask(oSession);
4031 return fRc;
4032
4033 def txsDoConnectViaTcp(self, oSession, cMsTimeout, fNatForwardingForTxs = False):
4034 """
4035 Mostly an internal worker for connecting to TXS via TCP used by the
4036 *ViaTcp methods.
4037
4038 Returns a tuplet with True/False and TxsSession/None depending on the
4039 result. Errors are logged.
4040 """
4041
4042 reporter.log2('txsDoConnectViaTcp: oSession=%s, cMsTimeout=%s, fNatForwardingForTxs=%s'
4043 % (oSession, cMsTimeout, fNatForwardingForTxs));
4044
4045 cMsTimeout = self.adjustTimeoutMs(cMsTimeout);
4046 oTxsConnect = oSession.txsConnectViaTcp(cMsTimeout, fNatForwardingForTxs = fNatForwardingForTxs);
4047 if oTxsConnect is not None:
4048 self.addTask(oTxsConnect);
4049 fRemoveVm = self.addTask(oSession);
4050 oTask = self.waitForTasks(cMsTimeout + 1);
4051 reporter.log2('txsDoConnectViaTcp: waitForTasks returned %s' % (oTask,));
4052 self.removeTask(oTxsConnect);
4053 if oTask is oTxsConnect:
4054 oTxsSession = oTxsConnect.getResult();
4055 if oTxsSession is not None:
4056 reporter.log('txsDoConnectViaTcp: Connected to TXS on %s.' % (oTxsSession.oTransport.sHostname,));
4057 return (True, oTxsSession);
4058
4059 reporter.error('txsDoConnectViaTcp: failed to connect to TXS.');
4060 else:
4061 oTxsConnect.cancelTask();
4062 if oTask is None:
4063 reporter.errorTimeout('txsDoConnectViaTcp: connect stage 1 timed out');
4064 elif oTask is oSession:
4065 oSession.reportPrematureTermination('txsDoConnectViaTcp: ');
4066 else:
4067 reporter.error('txsDoConnectViaTcp: unknown/wrong task %s' % (oTask,));
4068 if fRemoveVm:
4069 self.removeTask(oSession);
4070 else:
4071 reporter.error('txsDoConnectViaTcp: txsConnectViaTcp failed');
4072 return (False, None);
4073
4074 def startVmAndConnectToTxsViaTcp(self, sVmName, fCdWait = False, cMsTimeout = 15*60000, \
4075 cMsCdWait = 30000, sFileCdWait = None, \
4076 fNatForwardingForTxs = False):
4077 """
4078 Starts the specified VM and tries to connect to its TXS via TCP.
4079 The VM will be powered off if TXS doesn't respond before the specified
4080 time has elapsed.
4081
4082 Returns a the VM and TXS sessions (a two tuple) on success. The VM
4083 session is in the task list, the TXS session is not.
4084 Returns (None, None) on failure, fully logged.
4085 """
4086
4087 # Zap the guest IP to make sure we're not getting a stale entry
4088 # (unless we're restoring the VM of course).
4089 oTestVM = self.oTestVmSet.findTestVmByName(sVmName) if self.oTestVmSet is not None else None;
4090 if oTestVM is None \
4091 or oTestVM.fSnapshotRestoreCurrent is False:
4092 try:
4093 oSession1 = self.openSession(self.getVmByName(sVmName));
4094 oSession1.delGuestPropertyValue('/VirtualBox/GuestInfo/Net/0/V4/IP');
4095 oSession1.saveSettings(True);
4096 del oSession1;
4097 except:
4098 reporter.logXcpt();
4099
4100 # Start the VM.
4101 reporter.log('startVmAndConnectToTxsViaTcp: Starting(/preparing) "%s" (timeout %s s)...' % (sVmName, cMsTimeout / 1000));
4102 reporter.flushall();
4103 oSession = self.startVmByName(sVmName);
4104 if oSession is not None:
4105 # Connect to TXS.
4106 reporter.log2('startVmAndConnectToTxsViaTcp: Started(/prepared) "%s", connecting to TXS ...' % (sVmName,));
4107 (fRc, oTxsSession) = self.txsDoConnectViaTcp(oSession, cMsTimeout, fNatForwardingForTxs);
4108 if fRc is True:
4109 if fCdWait:
4110 # Wait for CD?
4111 reporter.log2('startVmAndConnectToTxsViaTcp: Waiting for file "%s" to become available ...' % (sFileCdWait,));
4112 fRc = self.txsCdWait(oSession, oTxsSession, cMsCdWait, sFileCdWait);
4113 if fRc is not True:
4114 reporter.error('startVmAndConnectToTxsViaTcp: txsCdWait failed');
4115
4116 sVer = self.txsVer(oSession, oTxsSession, cMsTimeout, fIgnoreErrors = True);
4117 if sVer is not False:
4118 reporter.log('startVmAndConnectToTxsViaTcp: TestExecService version %s' % (sVer,));
4119 else:
4120 reporter.log('startVmAndConnectToTxsViaTcp: Unable to retrieve TestExecService version');
4121
4122 if fRc is True:
4123 # Success!
4124 return (oSession, oTxsSession);
4125 else:
4126 reporter.error('startVmAndConnectToTxsViaTcp: txsDoConnectViaTcp failed');
4127 # If something went wrong while waiting for TXS to be started - take VM screenshot before terminate it
4128 self.terminateVmBySession(oSession);
4129 return (None, None);
4130
4131 def txsRebootAndReconnectViaTcp(self, oSession, oTxsSession, fCdWait = False, cMsTimeout = 15*60000, \
4132 cMsCdWait = 30000, sFileCdWait = None, fNatForwardingForTxs = False):
4133 """
4134 Executes the TXS reboot command
4135
4136 Returns A tuple of True and the new TXS session on success.
4137
4138 Returns A tuple of False and either the old TXS session or None on failure.
4139 """
4140 reporter.log2('txsRebootAndReconnect: cMsTimeout=%u' % (cMsTimeout,));
4141
4142 #
4143 # This stuff is a bit complicated because of rebooting being kind of
4144 # disruptive to the TXS and such... The protocol is that TXS will:
4145 # - ACK the reboot command.
4146 # - Shutdown the transport layer, implicitly disconnecting us.
4147 # - Execute the reboot operation.
4148 # - On failure, it will be re-init the transport layer and be
4149 # available pretty much immediately. UUID unchanged.
4150 # - On success, it will be respawed after the reboot (hopefully),
4151 # with a different UUID.
4152 #
4153 fRc = False;
4154 iStart = base.timestampMilli();
4155
4156 # Get UUID.
4157 cMsTimeout2 = min(60000, cMsTimeout);
4158 sUuidBefore = self.txsUuid(oSession, oTxsSession, self.adjustTimeoutMs(cMsTimeout2, 60000));
4159 if sUuidBefore is not False:
4160 # Reboot.
4161 cMsElapsed = base.timestampMilli() - iStart;
4162 cMsTimeout2 = cMsTimeout - cMsElapsed;
4163 fRc = self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncReboot,
4164 (self.adjustTimeoutMs(cMsTimeout2, 60000), False));
4165 if fRc is True:
4166 # Reconnect.
4167 if fNatForwardingForTxs is True:
4168 self.sleep(22); # NAT fudge - Two fixes are wanted: 1. TXS connect retries. 2. Main API reboot/reset hint.
4169 cMsElapsed = base.timestampMilli() - iStart;
4170 (fRc, oTxsSession) = self.txsDoConnectViaTcp(oSession, cMsTimeout - cMsElapsed, fNatForwardingForTxs);
4171 if fRc is True:
4172 # Check the UUID.
4173 cMsElapsed = base.timestampMilli() - iStart;
4174 cMsTimeout2 = min(60000, cMsTimeout - cMsElapsed);
4175 sUuidAfter = self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncUuid,
4176 (self.adjustTimeoutMs(cMsTimeout2, 60000), False));
4177 if sUuidBefore is not False:
4178 if sUuidAfter != sUuidBefore:
4179 reporter.log('The guest rebooted (UUID %s -> %s)' % (sUuidBefore, sUuidAfter))
4180
4181 # Do CD wait if specified.
4182 if fCdWait:
4183 fRc = self.txsCdWait(oSession, oTxsSession, cMsCdWait, sFileCdWait);
4184 if fRc is not True:
4185 reporter.error('txsRebootAndReconnectViaTcp: txsCdWait failed');
4186
4187 sVer = self.txsVer(oSession, oTxsSession, cMsTimeout, fIgnoreErrors = True);
4188 if sVer is not False:
4189 reporter.log('txsRebootAndReconnectViaTcp: TestExecService version %s' % (sVer,));
4190 else:
4191 reporter.log('txsRebootAndReconnectViaTcp: Unable to retrieve TestExecService version');
4192 else:
4193 reporter.error('txsRebootAndReconnectViaTcp: failed to get UUID (after)');
4194 else:
4195 reporter.error('txsRebootAndReconnectViaTcp: did not reboot (UUID %s)' % (sUuidBefore,));
4196 else:
4197 reporter.error('txsRebootAndReconnectViaTcp: txsDoConnectViaTcp failed');
4198 else:
4199 reporter.error('txsRebootAndReconnectViaTcp: reboot failed');
4200 else:
4201 reporter.error('txsRebootAndReconnectViaTcp: failed to get UUID (before)');
4202 return (fRc, oTxsSession);
4203
4204 # pylint: disable=too-many-locals,too-many-arguments
4205
4206 def txsRunTest(self, oTxsSession, sTestName, cMsTimeout, sExecName, asArgs = (), asAddEnv = (), sAsUser = "",
4207 fCheckSessionStatus = False):
4208 """
4209 Executes the specified test task, waiting till it completes or times out.
4210
4211 The VM session (if any) must be in the task list.
4212
4213 Returns True if we executed the task and nothing abnormal happend.
4214 Query the process status from the TXS session.
4215
4216 Returns False if some unexpected task was signalled or we failed to
4217 submit the job.
4218
4219 If fCheckSessionStatus is set to True, the overall session status will be
4220 taken into account and logged as an error on failure.
4221 """
4222 reporter.testStart(sTestName);
4223 reporter.log2('txsRunTest: cMsTimeout=%u sExecName=%s asArgs=%s' % (cMsTimeout, sExecName, asArgs));
4224
4225 # Submit the job.
4226 fRc = False;
4227 if oTxsSession.asyncExec(sExecName, asArgs, asAddEnv, sAsUser, cMsTimeout = self.adjustTimeoutMs(cMsTimeout)):
4228 self.addTask(oTxsSession);
4229
4230 # Wait for the job to complete.
4231 while True:
4232 oTask = self.waitForTasks(cMsTimeout + 1);
4233 if oTask is None:
4234 if fCheckSessionStatus:
4235 reporter.error('txsRunTest: waitForTasks for test "%s" timed out' % (sTestName,));
4236 else:
4237 reporter.log('txsRunTest: waitForTasks for test "%s" timed out' % (sTestName,));
4238 break;
4239 if oTask is oTxsSession:
4240 if fCheckSessionStatus \
4241 and not oTxsSession.isSuccess():
4242 reporter.error('txsRunTest: Test "%s" failed' % (sTestName,));
4243 else:
4244 fRc = True;
4245 reporter.log('txsRunTest: isSuccess=%s getResult=%s' \
4246 % (oTxsSession.isSuccess(), oTxsSession.getResult()));
4247 break;
4248 if not self.handleTask(oTask, 'txsRunTest'):
4249 break;
4250
4251 self.removeTask(oTxsSession);
4252 if not oTxsSession.pollTask():
4253 oTxsSession.cancelTask();
4254 else:
4255 reporter.error('txsRunTest: asyncExec failed');
4256
4257 reporter.testDone();
4258 return fRc;
4259
4260 def txsRunTestRedirectStd(self, oTxsSession, sTestName, cMsTimeout, sExecName, asArgs = (), asAddEnv = (), sAsUser = "",
4261 oStdIn = '/dev/null', oStdOut = '/dev/null', oStdErr = '/dev/null', oTestPipe = '/dev/null'):
4262 """
4263 Executes the specified test task, waiting till it completes or times out,
4264 redirecting stdin, stdout and stderr to the given objects.
4265
4266 The VM session (if any) must be in the task list.
4267
4268 Returns True if we executed the task and nothing abnormal happend.
4269 Query the process status from the TXS session.
4270
4271 Returns False if some unexpected task was signalled or we failed to
4272 submit the job.
4273 """
4274 reporter.testStart(sTestName);
4275 reporter.log2('txsRunTestRedirectStd: cMsTimeout=%u sExecName=%s asArgs=%s' % (cMsTimeout, sExecName, asArgs));
4276
4277 # Submit the job.
4278 fRc = False;
4279 if oTxsSession.asyncExecEx(sExecName, asArgs, asAddEnv, oStdIn, oStdOut, oStdErr,
4280 oTestPipe, sAsUser, cMsTimeout = self.adjustTimeoutMs(cMsTimeout)):
4281 self.addTask(oTxsSession);
4282
4283 # Wait for the job to complete.
4284 while True:
4285 oTask = self.waitForTasks(cMsTimeout + 1);
4286 if oTask is None:
4287 reporter.log('txsRunTestRedirectStd: waitForTasks timed out');
4288 break;
4289 if oTask is oTxsSession:
4290 fRc = True;
4291 reporter.log('txsRunTestRedirectStd: isSuccess=%s getResult=%s'
4292 % (oTxsSession.isSuccess(), oTxsSession.getResult()));
4293 break;
4294 if not self.handleTask(oTask, 'txsRunTestRedirectStd'):
4295 break;
4296
4297 self.removeTask(oTxsSession);
4298 if not oTxsSession.pollTask():
4299 oTxsSession.cancelTask();
4300 else:
4301 reporter.error('txsRunTestRedirectStd: asyncExec failed');
4302
4303 reporter.testDone();
4304 return fRc;
4305
4306 def txsRunTest2(self, oTxsSession1, oTxsSession2, sTestName, cMsTimeout,
4307 sExecName1, asArgs1,
4308 sExecName2, asArgs2,
4309 asAddEnv1 = (), sAsUser1 = '', fWithTestPipe1 = True,
4310 asAddEnv2 = (), sAsUser2 = '', fWithTestPipe2 = True):
4311 """
4312 Executes the specified test tasks, waiting till they complete or
4313 times out. The 1st task is started after the 2nd one.
4314
4315 The VM session (if any) must be in the task list.
4316
4317 Returns True if we executed the task and nothing abnormal happend.
4318 Query the process status from the TXS sessions.
4319
4320 Returns False if some unexpected task was signalled or we failed to
4321 submit the job.
4322 """
4323 reporter.testStart(sTestName);
4324
4325 # Submit the jobs.
4326 fRc = False;
4327 if oTxsSession1.asyncExec(sExecName1, asArgs1, asAddEnv1, sAsUser1, fWithTestPipe1, '1-',
4328 self.adjustTimeoutMs(cMsTimeout)):
4329 self.addTask(oTxsSession1);
4330
4331 self.sleep(2); # fudge! grr
4332
4333 if oTxsSession2.asyncExec(sExecName2, asArgs2, asAddEnv2, sAsUser2, fWithTestPipe2, '2-',
4334 self.adjustTimeoutMs(cMsTimeout)):
4335 self.addTask(oTxsSession2);
4336
4337 # Wait for the jobs to complete.
4338 cPendingJobs = 2;
4339 while True:
4340 oTask = self.waitForTasks(cMsTimeout + 1);
4341 if oTask is None:
4342 reporter.log('txsRunTest2: waitForTasks timed out');
4343 break;
4344
4345 if oTask is oTxsSession1 or oTask is oTxsSession2:
4346 if oTask is oTxsSession1: iTask = 1;
4347 else: iTask = 2;
4348 reporter.log('txsRunTest2: #%u - isSuccess=%s getResult=%s' \
4349 % (iTask, oTask.isSuccess(), oTask.getResult()));
4350 self.removeTask(oTask);
4351 cPendingJobs -= 1;
4352 if cPendingJobs <= 0:
4353 fRc = True;
4354 break;
4355
4356 elif not self.handleTask(oTask, 'txsRunTest'):
4357 break;
4358
4359 self.removeTask(oTxsSession2);
4360 if not oTxsSession2.pollTask():
4361 oTxsSession2.cancelTask();
4362 else:
4363 reporter.error('txsRunTest2: asyncExec #2 failed');
4364
4365 self.removeTask(oTxsSession1);
4366 if not oTxsSession1.pollTask():
4367 oTxsSession1.cancelTask();
4368 else:
4369 reporter.error('txsRunTest2: asyncExec #1 failed');
4370
4371 reporter.testDone();
4372 return fRc;
4373
4374 # pylint: enable=too-many-locals,too-many-arguments
4375
4376
4377 #
4378 # Working with test results via serial port.
4379 #
4380
4381 class TxsMonitorComFile(base.TdTaskBase):
4382 """
4383 Class that monitors a COM output file.
4384 """
4385
4386 def __init__(self, sComRawFile, asStopWords = None):
4387 base.TdTaskBase.__init__(self, utils.getCallerName());
4388 self.sComRawFile = sComRawFile;
4389 self.oStopRegExp = re.compile('\\b(' + '|'.join(asStopWords if asStopWords else ('PASSED', 'FAILED',)) + ')\\b');
4390 self.sResult = None; ##< The result.
4391 self.cchDisplayed = 0; ##< Offset into the file string of what we've already fed to the logger.
4392
4393 def toString(self):
4394 return '<%s sComRawFile=%s oStopRegExp=%s sResult=%s cchDisplayed=%s>' \
4395 % (base.TdTaskBase.toString(self), self.sComRawFile, self.oStopRegExp, self.sResult, self.cchDisplayed,);
4396
4397 def pollTask(self, fLocked = False):
4398 """
4399 Overrides TdTaskBase.pollTask() for the purpose of polling the file.
4400 """
4401 if not fLocked:
4402 self.lockTask();
4403
4404 sFile = utils.noxcptReadFile(self.sComRawFile, '', 'rU');
4405 if len(sFile) > self.cchDisplayed:
4406 sNew = sFile[self.cchDisplayed:];
4407 oMatch = self.oStopRegExp.search(sNew);
4408 if oMatch:
4409 # Done! Get result, flush all the output and signal the task.
4410 self.sResult = oMatch.group(1);
4411 for sLine in sNew.split('\n'):
4412 reporter.log('COM OUTPUT: %s' % (sLine,));
4413 self.cchDisplayed = len(sFile);
4414 self.signalTaskLocked();
4415 else:
4416 # Output whole lines only.
4417 offNewline = sFile.find('\n', self.cchDisplayed);
4418 while offNewline >= 0:
4419 reporter.log('COM OUTPUT: %s' % (sFile[self.cchDisplayed:offNewline]))
4420 self.cchDisplayed = offNewline + 1;
4421 offNewline = sFile.find('\n', self.cchDisplayed);
4422
4423 fRet = self.fSignalled;
4424 if not fLocked:
4425 self.unlockTask();
4426 return fRet;
4427
4428 # Our stuff.
4429 def getResult(self):
4430 """
4431 Returns the connected TXS session object on success.
4432 Returns None on failure or if the task has not yet completed.
4433 """
4434 self.oCv.acquire();
4435 sResult = self.sResult;
4436 self.oCv.release();
4437 return sResult;
4438
4439 def cancelTask(self):
4440 """ Cancels the task. """
4441 self.signalTask();
4442 return True;
4443
4444
4445 def monitorComRawFile(self, oSession, sComRawFile, cMsTimeout = 15*60000, asStopWords = None):
4446 """
4447 Monitors the COM output file for stop words (PASSED and FAILED by default).
4448
4449 Returns the stop word.
4450 Returns None on VM error and timeout.
4451 """
4452
4453 reporter.log2('monitorComRawFile: oSession=%s, cMsTimeout=%s, sComRawFile=%s' % (oSession, cMsTimeout, sComRawFile));
4454
4455 oMonitorTask = self.TxsMonitorComFile(sComRawFile, asStopWords);
4456 self.addTask(oMonitorTask);
4457
4458 cMsTimeout = self.adjustTimeoutMs(cMsTimeout);
4459 oTask = self.waitForTasks(cMsTimeout + 1);
4460 reporter.log2('monitorComRawFile: waitForTasks returned %s' % (oTask,));
4461
4462 if oTask is not oMonitorTask:
4463 oMonitorTask.cancelTask();
4464 self.removeTask(oMonitorTask);
4465
4466 oMonitorTask.pollTask();
4467 return oMonitorTask.getResult();
4468
4469
4470 def runVmAndMonitorComRawFile(self, sVmName, sComRawFile, cMsTimeout = 15*60000, asStopWords = None):
4471 """
4472 Runs the specified VM and monitors the given COM output file for stop
4473 words (PASSED and FAILED by default).
4474
4475 The caller is assumed to have configured the VM to use the given
4476 file. The method will take no action to verify this.
4477
4478 Returns the stop word.
4479 Returns None on VM error and timeout.
4480 """
4481
4482 # Start the VM.
4483 reporter.log('runVmAndMonitorComRawFile: Starting(/preparing) "%s" (timeout %s s)...' % (sVmName, cMsTimeout / 1000));
4484 reporter.flushall();
4485 oSession = self.startVmByName(sVmName);
4486 if oSession is not None:
4487 # Let it run and then terminate it.
4488 sRet = self.monitorComRawFile(oSession, sComRawFile, cMsTimeout, asStopWords);
4489 self.terminateVmBySession(oSession);
4490 else:
4491 sRet = None;
4492 return sRet;
4493
4494 #
4495 # Other stuff
4496 #
4497
4498 def waitForGAs(self,
4499 oSession, # type: vboxwrappers.SessionWrapper
4500 cMsTimeout = 120000, aenmWaitForRunLevels = None, aenmWaitForActive = None, aenmWaitForInactive = None):
4501 """
4502 Waits for the guest additions to enter a certain state.
4503
4504 aenmWaitForRunLevels - List of run level values to wait for (success if one matches).
4505 aenmWaitForActive - List facilities (type values) that must be active.
4506 aenmWaitForInactive - List facilities (type values) that must be inactive.
4507
4508 Defaults to wait for AdditionsRunLevelType_Userland if nothing else is given.
4509
4510 Returns True on success, False w/ error logging on timeout or failure.
4511 """
4512 reporter.log2('waitForGAs: oSession=%s, cMsTimeout=%s' % (oSession, cMsTimeout,));
4513
4514 #
4515 # Get IGuest:
4516 #
4517 try:
4518 oIGuest = oSession.o.console.guest;
4519 except:
4520 return reporter.errorXcpt();
4521
4522 #
4523 # Create a wait task:
4524 #
4525 from testdriver.vboxwrappers import AdditionsStatusTask;
4526 try:
4527 oGaStatusTask = AdditionsStatusTask(oSession = oSession,
4528 oIGuest = oIGuest,
4529 cMsTimeout = cMsTimeout,
4530 aenmWaitForRunLevels = aenmWaitForRunLevels,
4531 aenmWaitForActive = aenmWaitForActive,
4532 aenmWaitForInactive = aenmWaitForInactive);
4533 except:
4534 return reporter.errorXcpt();
4535
4536 #
4537 # Add the task and make sure the VM session is also present.
4538 #
4539 self.addTask(oGaStatusTask);
4540 fRemoveSession = self.addTask(oSession);
4541 oTask = self.waitForTasks(cMsTimeout + 1);
4542 reporter.log2('waitForGAs: returned %s (oGaStatusTask=%s, oSession=%s)' % (oTask, oGaStatusTask, oSession,));
4543 self.removeTask(oGaStatusTask);
4544 if fRemoveSession:
4545 self.removeTask(oSession);
4546
4547 #
4548 # Digest the result.
4549 #
4550 if oTask is oGaStatusTask:
4551 fSucceeded = oGaStatusTask.getResult();
4552 if fSucceeded is True:
4553 reporter.log('waitForGAs: Succeeded.');
4554 else:
4555 reporter.error('waitForGAs: Failed.');
4556 else:
4557 oGaStatusTask.cancelTask();
4558 if oTask is None:
4559 reporter.error('waitForGAs: Timed out.');
4560 elif oTask is oSession:
4561 oSession.reportPrematureTermination('waitForGAs: ');
4562 else:
4563 reporter.error('waitForGAs: unknown/wrong task %s' % (oTask,));
4564 fSucceeded = False;
4565 return fSucceeded;
4566
4567 @staticmethod
4568 def controllerTypeToName(eControllerType):
4569 """
4570 Translate a controller type to a standard controller name.
4571 """
4572 if eControllerType in (vboxcon.StorageControllerType_PIIX3, vboxcon.StorageControllerType_PIIX4,):
4573 sName = "IDE Controller";
4574 elif eControllerType == vboxcon.StorageControllerType_IntelAhci:
4575 sName = "SATA Controller";
4576 elif eControllerType == vboxcon.StorageControllerType_LsiLogicSas:
4577 sName = "SAS Controller";
4578 elif eControllerType in (vboxcon.StorageControllerType_LsiLogic, vboxcon.StorageControllerType_BusLogic,):
4579 sName = "SCSI Controller";
4580 elif eControllerType == vboxcon.StorageControllerType_NVMe:
4581 sName = "NVMe Controller";
4582 elif eControllerType == vboxcon.StorageControllerType_VirtioSCSI:
4583 sName = "VirtIO SCSI Controller";
4584 else:
4585 sName = "Storage Controller";
4586 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