VirtualBox

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

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

Validation Kit/testdriver: Minor comment typo.

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