VirtualBox

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

Last change on this file since 74438 was 74438, checked in by vboxsync, 6 years ago

ValidationKit: pylint adjustments/fixes.

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