VirtualBox

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

Last change on this file since 96517 was 96517, checked in by vboxsync, 3 years ago

Validation Kit/vbox.py: Implemented support for recording video / audio to the test driver base. Disabled by default. See syntax help.

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette