VirtualBox

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

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

ValKit/testdriver: Screen recording corrections. We do those upload last because they will usually be bigger and therefore more likely to cause trouble, so lets get the other logs up first. Noticed we have two --vbox-always-upload-xxxx options, so changed --vbox-recording-force-upload to --vbox-always-upload-recordings to fit better in. bugref:8733

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