VirtualBox

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

Last change on this file since 72864 was 72732, checked in by vboxsync, 7 years ago

Valkit: Resource handling regression fix.

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