VirtualBox

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

Last change on this file since 97544 was 97544, checked in by vboxsync, 2 years ago

Validation Kit/vbox.py: Also upload the VM session's debug log on failures.

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

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