VirtualBox

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

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

ValidationKit/testdriver: Added a @todo.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 166.5 KB
Line 
1# -*- coding: utf-8 -*-
2# $Id: vbox.py 71538 2018-03-28 13:07:27Z 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: 71538 $"
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 self.oVBoxSvcProcess = base.Process.spawnp(sTerm, sTerm, '-e', sGdbCmdLine); ## @todo -e is deprecated; use "-- <args>".
1041 os.environ['SHELL'] = self.sOurShell;
1042 if self.oVBoxSvcProcess is not None:
1043 reporter.log('Press enter or return after starting VBoxSVC in the debugger...');
1044 sys.stdin.read(1);
1045 fWritePidFile = False;
1046
1047 elif self.sHost == 'win':
1048 sWinDbg = 'c:\\Program Files\\Debugging Tools for Windows\\windbg.exe';
1049 if not os.path.isfile(sWinDbg): sWinDbg = 'c:\\Program Files\\Debugging Tools for Windows (x64)\\windbg.exe';
1050 if not os.path.isfile(sWinDbg): sWinDbg = 'c:\\Programme\\Debugging Tools for Windows\\windbg.exe'; # Localization rulez! pylint: disable=C0301
1051 if not os.path.isfile(sWinDbg): sWinDbg = 'c:\\Programme\\Debugging Tools for Windows (x64)\\windbg.exe';
1052 if not os.path.isfile(sWinDbg): sWinDbg = 'windbg'; # WinDbg must be in the path; better than nothing.
1053 # Assume that everything WinDbg needs is defined using the environment variables.
1054 # See WinDbg help for more information.
1055 reporter.log('windbg="%s"' % (sWinDbg));
1056 self.oVBoxSvcProcess = base.Process.spawn(sWinDbg, sWinDbg, sVBoxSVC + base.exeSuff());
1057 if self.oVBoxSvcProcess is not None:
1058 reporter.log('Press enter or return after starting VBoxSVC in the debugger...');
1059 sys.stdin.read(1);
1060 fWritePidFile = False;
1061 ## @todo add a pipe interface similar to xpcom if feasible, i.e. if
1062 # we can get actual handle values for pipes in python.
1063
1064 else:
1065 reporter.error('Port me!');
1066 else: # Run without a debugger attached.
1067 if self.sHost in ('darwin', 'freebsd', 'linux', 'solaris', ):
1068 #
1069 # XPCOM - We can use a pipe to let VBoxSVC notify us when it's ready.
1070 #
1071 iPipeR, iPipeW = os.pipe();
1072 if hasattr(os, 'set_inheritable'):
1073 os.set_inheritable(iPipeW, True); # pylint: disable=no-member
1074 os.environ['NSPR_INHERIT_FDS'] = 'vboxsvc:startup-pipe:5:0x%x' % (iPipeW,);
1075 reporter.log2("NSPR_INHERIT_FDS=%s" % (os.environ['NSPR_INHERIT_FDS']));
1076
1077 self.oVBoxSvcProcess = base.Process.spawn(sVBoxSVC, sVBoxSVC, '--auto-shutdown'); # SIGUSR1 requirement.
1078 try: # Try make sure we get the SIGINT and not VBoxSVC.
1079 os.setpgid(self.oVBoxSvcProcess.getPid(), 0); # pylint: disable=E1101
1080 os.setpgid(0, 0); # pylint: disable=E1101
1081 except:
1082 reporter.logXcpt();
1083
1084 os.close(iPipeW);
1085 try:
1086 sResponse = os.read(iPipeR, 32);
1087 except:
1088 reporter.logXcpt();
1089 sResponse = None;
1090 os.close(iPipeR);
1091
1092 if hasattr(sResponse, 'decode'):
1093 sResponse = sResponse.decode('utf-8', 'ignore');
1094
1095 if sResponse is None or sResponse.strip() != 'READY':
1096 reporter.error('VBoxSVC failed starting up... (sResponse=%s)' % (sResponse,));
1097 if not self.oVBoxSvcProcess.wait(5000):
1098 self.oVBoxSvcProcess.terminate(2500);
1099 self.oVBoxSvcProcess.wait(5000);
1100 self.oVBoxSvcProcess = None;
1101
1102 elif self.sHost == 'win':
1103 #
1104 # Windows - Just fudge it for now.
1105 #
1106 cMsFudge = 2000;
1107 self.oVBoxSvcProcess = base.Process.spawn(sVBoxSVC, sVBoxSVC);
1108
1109 else:
1110 reporter.error('Port me!');
1111
1112 #
1113 # Enable automatic crash reporting if we succeeded.
1114 #
1115 if self.oVBoxSvcProcess is not None:
1116 self.oVBoxSvcProcess.enableCrashReporting('crash/report/svc', 'crash/dump/svc');
1117
1118 #
1119 # Fudge and pid file.
1120 #
1121 if self.oVBoxSvcProcess != None and not self.oVBoxSvcProcess.wait(cMsFudge):
1122 if fWritePidFile:
1123 iPid = self.oVBoxSvcProcess.getPid();
1124 try:
1125 oFile = utils.openNoInherit(self.sVBoxSvcPidFile, "w+");
1126 oFile.write('%s' % (iPid,));
1127 oFile.close();
1128 except:
1129 reporter.logXcpt('sPidFile=%s' % (self.sVBoxSvcPidFile,));
1130 reporter.log('VBoxSVC PID=%u' % (iPid,));
1131
1132 #
1133 # Finally add the task so we'll notice when it dies in a relatively timely manner.
1134 #
1135 self.addTask(self.oVBoxSvcProcess);
1136 else:
1137 self.oVBoxSvcProcess = None;
1138 try: os.remove(self.sVBoxSvcPidFile);
1139 except: pass;
1140
1141 return self.oVBoxSvcProcess != None;
1142
1143
1144 def _killVBoxSVCByPidFile(self, sPidFile):
1145 """ Kill a VBoxSVC given the pid from it's pid file. """
1146
1147 # Read the pid file.
1148 if not os.path.isfile(sPidFile):
1149 return False;
1150 try:
1151 oFile = utils.openNoInherit(sPidFile, "r");
1152 sPid = oFile.readline().strip();
1153 oFile.close();
1154 except:
1155 reporter.logXcpt('sPidfile=%s' % (sPidFile,));
1156 return False;
1157
1158 # Convert the pid to an integer and validate the range a little bit.
1159 try:
1160 iPid = long(sPid);
1161 except:
1162 reporter.logXcpt('sPidfile=%s sPid="%s"' % (sPidFile, sPid));
1163 return False;
1164 if iPid <= 0:
1165 reporter.log('negative pid - sPidfile=%s sPid="%s" iPid=%d' % (sPidFile, sPid, iPid));
1166 return False;
1167
1168 # Take care checking that it's VBoxSVC we're about to inhume.
1169 if base.processCheckPidAndName(iPid, "VBoxSVC") is not True:
1170 reporter.log('Ignoring stale VBoxSVC pid file (pid=%s)' % (iPid,));
1171 return False;
1172
1173 # Loop thru our different ways of getting VBoxSVC to terminate.
1174 for aHow in [ [ base.sendUserSignal1, 5000, 'Dropping VBoxSVC a SIGUSR1 hint...'], \
1175 [ base.processInterrupt, 5000, 'Dropping VBoxSVC a SIGINT hint...'], \
1176 [ base.processTerminate, 7500, 'VBoxSVC is still around, killing it...'] ]:
1177 reporter.log(aHow[2]);
1178 if aHow[0](iPid) is True:
1179 msStart = base.timestampMilli();
1180 while base.timestampMilli() - msStart < 5000 \
1181 and base.processExists(iPid):
1182 time.sleep(0.2);
1183
1184 fRc = not base.processExists(iPid);
1185 if fRc is True:
1186 break;
1187 if fRc:
1188 reporter.log('Successfully killed VBoxSVC (pid=%s)' % (iPid,));
1189 else:
1190 reporter.log('Failed to kill VBoxSVC (pid=%s)' % (iPid,));
1191 return fRc;
1192
1193 def _stopVBoxSVC(self):
1194 """
1195 Stops VBoxSVC. Try the polite way first.
1196 """
1197
1198 if self.oVBoxSvcProcess:
1199 self.removeTask(self.oVBoxSvcProcess);
1200 self.oVBoxSvcProcess.enableCrashReporting(None, None); # Disables it.
1201
1202 fRc = False;
1203 if self.oVBoxSvcProcess is not None \
1204 and not self.fVBoxSvcInDebugger:
1205 # by process object.
1206 if self.oVBoxSvcProcess.isRunning():
1207 reporter.log('Dropping VBoxSVC a SIGUSR1 hint...');
1208 if not self.oVBoxSvcProcess.sendUserSignal1() \
1209 or not self.oVBoxSvcProcess.wait(5000):
1210 reporter.log('Dropping VBoxSVC a SIGINT hint...');
1211 if not self.oVBoxSvcProcess.interrupt() \
1212 or not self.oVBoxSvcProcess.wait(5000):
1213 reporter.log('VBoxSVC is still around, killing it...');
1214 self.oVBoxSvcProcess.terminate();
1215 self.oVBoxSvcProcess.wait(7500);
1216 else:
1217 reporter.log('VBoxSVC is no longer running...');
1218 if not self.oVBoxSvcProcess.isRunning():
1219 self.oVBoxSvcProcess = None;
1220 else:
1221 # by pid file.
1222 self._killVBoxSVCByPidFile('%s/VBoxSVC.pid' % (self.sScratchPath,));
1223 return fRc;
1224
1225 def _setupVBoxApi(self):
1226 """
1227 Import and set up the vboxapi.
1228 The caller saves and restores sys.path.
1229 """
1230
1231 # Setup vbox logging for self (the test driver).
1232 self.sSelfLogFile = '%s/VBoxTestDriver.log' % (self.sScratchPath,);
1233 try: os.remove(self.sSelfLogFile);
1234 except: pass;
1235 os.environ['VBOX_LOG'] = self.sLogSelfGroups;
1236 os.environ['VBOX_LOG_FLAGS'] = '%s append' % (self.sLogSelfFlags, );
1237 if self.sLogSelfDest:
1238 os.environ['VBOX_LOG_DEST'] = 'nodeny ' + self.sLogSelfDest;
1239 else:
1240 os.environ['VBOX_LOG_DEST'] = 'nodeny file=%s' % (self.sSelfLogFile,);
1241 os.environ['VBOX_RELEASE_LOG_FLAGS'] = 'time append';
1242
1243 # Hack the sys.path + environment so the vboxapi can be found.
1244 sys.path.insert(0, self.oBuild.sInstallPath);
1245 if self.oBuild.sSdkPath is not None:
1246 sys.path.insert(0, os.path.join(self.oBuild.sSdkPath, 'installer'))
1247 sys.path.insert(1, os.path.join(self.oBuild.sSdkPath, 'install')); # stupid stupid windows installer!
1248 sys.path.insert(2, os.path.join(self.oBuild.sSdkPath, 'bindings', 'xpcom', 'python'))
1249 os.environ['VBOX_PROGRAM_PATH'] = self.oBuild.sInstallPath;
1250 reporter.log("sys.path: %s" % (sys.path));
1251
1252 try:
1253 from vboxapi import VirtualBoxManager # pylint: disable=import-error
1254 except:
1255 reporter.logXcpt('Error importing vboxapi');
1256 return False;
1257
1258 # Exception and error hacks.
1259 try:
1260 # pylint: disable=import-error
1261 if self.sHost == 'win':
1262 from pythoncom import com_error as NativeComExceptionClass # pylint: disable=E0611
1263 import winerror as NativeComErrorClass
1264 else:
1265 from xpcom import Exception as NativeComExceptionClass
1266 from xpcom import nsError as NativeComErrorClass
1267 # pylint: enable=import-error
1268 except:
1269 reporter.logXcpt('Error importing (XP)COM related stuff for exception hacks and errors');
1270 return False;
1271 __deployExceptionHacks__(NativeComExceptionClass)
1272 ComError.copyErrors(NativeComErrorClass);
1273
1274 # Create the manager.
1275 try:
1276 self.oVBoxMgr = VirtualBoxManager(None, None)
1277 except:
1278 self.oVBoxMgr = None;
1279 reporter.logXcpt('VirtualBoxManager exception');
1280 return False;
1281
1282 # Figure the API version.
1283 try:
1284 oVBox = self.oVBoxMgr.getVirtualBox();
1285
1286 try:
1287 sVer = oVBox.version;
1288 except:
1289 reporter.logXcpt('Failed to get VirtualBox version, assuming 4.0.0');
1290 sVer = "4.0.0";
1291 reporter.log("IVirtualBox.version=%s" % (sVer,));
1292
1293 # Convert the string to three integer values and check ranges.
1294 asVerComponents = sVer.split('.');
1295 try:
1296 sLast = asVerComponents[2].split('_')[0].split('r')[0];
1297 aiVerComponents = (int(asVerComponents[0]), int(asVerComponents[1]), int(sLast));
1298 except:
1299 raise base.GenError('Malformed version "%s"' % (sVer,));
1300 if aiVerComponents[0] < 3 or aiVerComponents[0] > 19:
1301 raise base.GenError('Malformed version "%s" - 1st component is out of bounds 3..19: %u'
1302 % (sVer, aiVerComponents[0]));
1303 if aiVerComponents[1] < 0 or aiVerComponents[1] > 9:
1304 raise base.GenError('Malformed version "%s" - 2nd component is out of bounds 0..9: %u'
1305 % (sVer, aiVerComponents[1]));
1306 if aiVerComponents[2] < 0 or aiVerComponents[2] > 99:
1307 raise base.GenError('Malformed version "%s" - 3rd component is out of bounds 0..99: %u'
1308 % (sVer, aiVerComponents[2]));
1309
1310 # Convert the three integers into a floating point value. The API is table witin a
1311 # x.y release, so the third component only indicates whether it's a stable or
1312 # development build of the next release.
1313 self.fpApiVer = aiVerComponents[0] + 0.1 * aiVerComponents[1];
1314 if aiVerComponents[2] >= 51:
1315 if self.fpApiVer not in [4.3, 3.2,]:
1316 self.fpApiVer += 0.1;
1317 else:
1318 self.fpApiVer += 1.1;
1319
1320 # Patch VBox manage to gloss over portability issues (error constants, etc).
1321 self._patchVBoxMgr();
1322
1323 # Wrap oVBox.
1324 from testdriver.vboxwrappers import VirtualBoxWrapper;
1325 self.oVBox = VirtualBoxWrapper(oVBox, self.oVBoxMgr, self.fpApiVer, self);
1326
1327 # Install the constant wrapping hack.
1328 vboxcon.goHackModuleClass.oVBoxMgr = self.oVBoxMgr; # VBoxConstantWrappingHack.
1329 vboxcon.fpApiVer = self.fpApiVer;
1330
1331 except:
1332 self.oVBoxMgr = None;
1333 self.oVBox = None;
1334 reporter.logXcpt("getVirtualBox / API version exception");
1335 return False;
1336
1337 # Done
1338 self.fImportedVBoxApi = True;
1339 reporter.log('Found version %s (%s)' % (self.fpApiVer, sVer));
1340 return True;
1341
1342 def _patchVBoxMgr(self):
1343 """
1344 Glosses over missing self.oVBoxMgr methods on older VBox versions.
1345 """
1346
1347 def _xcptGetResult(oSelf, oXcpt = None):
1348 """ See vboxapi. """
1349 _ = oSelf;
1350 if oXcpt is None: oXcpt = sys.exc_info()[1];
1351 if sys.platform == 'win32':
1352 import winerror; # pylint: disable=import-error
1353 hrXcpt = oXcpt.hresult;
1354 if hrXcpt == winerror.DISP_E_EXCEPTION:
1355 hrXcpt = oXcpt.excepinfo[5];
1356 else:
1357 hrXcpt = oXcpt.error;
1358 return hrXcpt;
1359
1360 def _xcptIsDeadInterface(oSelf, oXcpt = None):
1361 """ See vboxapi. """
1362 return oSelf.xcptGetStatus(oXcpt) in [
1363 0x80004004, -2147467260, # NS_ERROR_ABORT
1364 0x800706be, -2147023170, # NS_ERROR_CALL_FAILED (RPC_S_CALL_FAILED)
1365 0x800706ba, -2147023174, # RPC_S_SERVER_UNAVAILABLE.
1366 0x800706be, -2147023170, # RPC_S_CALL_FAILED.
1367 0x800706bf, -2147023169, # RPC_S_CALL_FAILED_DNE.
1368 0x80010108, -2147417848, # RPC_E_DISCONNECTED.
1369 0x800706b5, -2147023179, # RPC_S_UNKNOWN_IF
1370 ];
1371
1372 def _xcptIsOurXcptKind(oSelf, oXcpt = None):
1373 """ See vboxapi. """
1374 _ = oSelf;
1375 if oXcpt is None: oXcpt = sys.exc_info()[1];
1376 if sys.platform == 'win32':
1377 from pythoncom import com_error as NativeComExceptionClass # pylint: disable=import-error,E0611
1378 else:
1379 from xpcom import Exception as NativeComExceptionClass # pylint: disable=import-error
1380 return isinstance(oXcpt, NativeComExceptionClass);
1381
1382 def _xcptIsEqual(oSelf, oXcpt, hrStatus):
1383 """ See vboxapi. """
1384 hrXcpt = oSelf.xcptGetResult(oXcpt);
1385 return hrXcpt == hrStatus or hrXcpt == hrStatus - 0x100000000;
1386
1387 def _xcptToString(oSelf, oXcpt):
1388 """ See vboxapi. """
1389 _ = oSelf;
1390 if oXcpt is None: oXcpt = sys.exc_info()[1];
1391 return str(oXcpt);
1392
1393 # Add utilities found in newer vboxapi revision.
1394 if not hasattr(self.oVBoxMgr, 'xcptIsDeadInterface'):
1395 import types;
1396 self.oVBoxMgr.xcptGetResult = types.MethodType(_xcptGetResult, self.oVBoxMgr);
1397 self.oVBoxMgr.xcptIsDeadInterface = types.MethodType(_xcptIsDeadInterface, self.oVBoxMgr);
1398 self.oVBoxMgr.xcptIsOurXcptKind = types.MethodType(_xcptIsOurXcptKind, self.oVBoxMgr);
1399 self.oVBoxMgr.xcptIsEqual = types.MethodType(_xcptIsEqual, self.oVBoxMgr);
1400 self.oVBoxMgr.xcptToString = types.MethodType(_xcptToString, self.oVBoxMgr);
1401
1402
1403 def _teardownVBoxApi(self): # pylint: disable=too-many-statements
1404 """
1405 Drop all VBox object references and shutdown com/xpcom.
1406 """
1407 if not self.fImportedVBoxApi:
1408 return True;
1409 import gc;
1410
1411 # Drop all references we've have to COM objects.
1412 self.aoRemoteSessions = [];
1413 self.aoVMs = [];
1414 self.oVBoxMgr = None;
1415 self.oVBox = None;
1416 vboxcon.goHackModuleClass.oVBoxMgr = None; # VBoxConstantWrappingHack.
1417
1418 # Do garbage collection to try get rid of those objects.
1419 try:
1420 gc.collect();
1421 except:
1422 reporter.logXcpt();
1423 self.fImportedVBoxApi = False;
1424
1425 # Check whether the python is still having any COM objects/interfaces around.
1426 cVBoxMgrs = 0;
1427 aoObjsLeftBehind = [];
1428 if self.sHost == 'win':
1429 import pythoncom; # pylint: disable=import-error
1430 try:
1431 cIfs = pythoncom._GetInterfaceCount(); # pylint: disable=no-member,protected-access
1432 cObjs = pythoncom._GetGatewayCount(); # pylint: disable=no-member,protected-access
1433 if cObjs == 0 and cIfs == 0:
1434 reporter.log('_teardownVBoxApi: no interfaces or objects left behind.');
1435 else:
1436 reporter.log('_teardownVBoxApi: Python COM still has %s objects and %s interfaces...' % ( cObjs, cIfs));
1437
1438 from win32com.client import DispatchBaseClass; # pylint: disable=import-error
1439 for oObj in gc.get_objects():
1440 if isinstance(oObj, DispatchBaseClass):
1441 reporter.log('_teardownVBoxApi: %s' % (oObj,));
1442 aoObjsLeftBehind.append(oObj);
1443 elif utils.getObjectTypeName(oObj) == 'VirtualBoxManager':
1444 reporter.log('_teardownVBoxApi: %s' % (oObj,));
1445 cVBoxMgrs += 1;
1446 aoObjsLeftBehind.append(oObj);
1447 oObj = None;
1448 except:
1449 reporter.logXcpt();
1450
1451 # If not being used, we can safely uninitialize COM.
1452 if cIfs == 0 and cObjs == 0 and cVBoxMgrs == 0 and not aoObjsLeftBehind:
1453 reporter.log('_teardownVBoxApi: Calling CoUninitialize...');
1454 try: pythoncom.CoUninitialize(); # pylint: disable=no-member
1455 except: reporter.logXcpt();
1456 else:
1457 reporter.log('_teardownVBoxApi: Returned from CoUninitialize.');
1458 else:
1459 try:
1460 # XPCOM doesn't crash and burn like COM if you shut it down with interfaces and objects around.
1461 # Also, it keeps a number of internal objects and interfaces around to do its job, so shutting
1462 # it down before we go looking for dangling interfaces is more or less required.
1463 from xpcom import _xpcom as _xpcom; # pylint: disable=import-error
1464 hrc = _xpcom.DeinitCOM();
1465 cIfs = _xpcom._GetInterfaceCount(); # pylint: disable=W0212
1466 cObjs = _xpcom._GetGatewayCount(); # pylint: disable=W0212
1467
1468 if cObjs == 0 and cIfs == 0:
1469 reporter.log('_teardownVBoxApi: No XPCOM interfaces or objects active. (hrc=%#x)' % (hrc,));
1470 else:
1471 reporter.log('_teardownVBoxApi: %s XPCOM objects and %s interfaces still around! (hrc=%#x)'
1472 % (cObjs, cIfs, hrc));
1473 if hasattr(_xpcom, '_DumpInterfaces'):
1474 try: _xpcom._DumpInterfaces(); # pylint: disable=W0212
1475 except: reporter.logXcpt('_teardownVBoxApi: _DumpInterfaces failed');
1476
1477 from xpcom.client import Component; # pylint: disable=import-error
1478 for oObj in gc.get_objects():
1479 if isinstance(oObj, Component):
1480 reporter.log('_teardownVBoxApi: %s' % (oObj,));
1481 aoObjsLeftBehind.append(oObj);
1482 if utils.getObjectTypeName(oObj) == 'VirtualBoxManager':
1483 reporter.log('_teardownVBoxApi: %s' % (oObj,));
1484 cVBoxMgrs += 1;
1485 aoObjsLeftBehind.append(oObj);
1486 oObj = None;
1487 except:
1488 reporter.logXcpt();
1489
1490 # Try get the referrers to (XP)COM interfaces and objects that was left behind.
1491 for iObj in range(len(aoObjsLeftBehind)): # pylint: disable=consider-using-enumerate
1492 try:
1493 aoReferrers = gc.get_referrers(aoObjsLeftBehind[iObj]);
1494 reporter.log('_teardownVBoxApi: Found %u referrers to %s:' % (len(aoReferrers), aoObjsLeftBehind[iObj],));
1495 for oReferrer in aoReferrers:
1496 oMyFrame = sys._getframe(0); # pylint: disable=protected-access
1497 if oReferrer is oMyFrame:
1498 reporter.log('_teardownVBoxApi: - frame of this function');
1499 elif oReferrer is aoObjsLeftBehind:
1500 reporter.log('_teardownVBoxApi: - aoObjsLeftBehind');
1501 else:
1502 fPrinted = False;
1503 if isinstance(oReferrer, (dict, list, tuple)):
1504 try:
1505 aoSubReferreres = gc.get_referrers(oReferrer);
1506 for oSubRef in aoSubReferreres:
1507 if not isinstance(oSubRef, list) \
1508 and not isinstance(oSubRef, dict) \
1509 and oSubRef is not oMyFrame \
1510 and oSubRef is not aoSubReferreres:
1511 reporter.log('_teardownVBoxApi: - %s :: %s:'
1512 % (utils.getObjectTypeName(oSubRef), utils.getObjectTypeName(oReferrer)));
1513 fPrinted = True;
1514 break;
1515 del aoSubReferreres;
1516 except:
1517 reporter.logXcpt('subref');
1518 if not fPrinted:
1519 reporter.log('_teardownVBoxApi: - %s:' % (utils.getObjectTypeName(oReferrer),));
1520 try:
1521 import pprint;
1522 for sLine in pprint.pformat(oReferrer, width = 130).split('\n'):
1523 reporter.log('_teardownVBoxApi: %s' % (sLine,));
1524 except:
1525 reporter.log('_teardownVBoxApi: %s' % (oReferrer,));
1526 except:
1527 reporter.logXcpt();
1528 del aoObjsLeftBehind;
1529
1530 # Force garbage collection again, just for good measure.
1531 try:
1532 gc.collect();
1533 time.sleep(0.5); # fudge factor
1534 except:
1535 reporter.logXcpt();
1536 return True;
1537
1538 def _powerOffAllVms(self):
1539 """
1540 Tries to power off all running VMs.
1541 """
1542 for oSession in self.aoRemoteSessions:
1543 uPid = oSession.getPid();
1544 if uPid is not None:
1545 reporter.log('_powerOffAllVms: PID is %s for %s, trying to kill it.' % (uPid, oSession.sName,));
1546 base.processKill(uPid);
1547 else:
1548 reporter.log('_powerOffAllVms: No PID for %s' % (oSession.sName,));
1549 oSession.close();
1550 return None;
1551
1552
1553
1554 #
1555 # Build type, OS and arch getters.
1556 #
1557
1558 def getBuildType(self):
1559 """
1560 Get the build type.
1561 """
1562 if not self._detectBuild():
1563 return 'release';
1564 return self.oBuild.sType;
1565
1566 def getBuildOs(self):
1567 """
1568 Get the build OS.
1569 """
1570 if not self._detectBuild():
1571 return self.sHost;
1572 return self.oBuild.sOs;
1573
1574 def getBuildArch(self):
1575 """
1576 Get the build arch.
1577 """
1578 if not self._detectBuild():
1579 return self.sHostArch;
1580 return self.oBuild.sArch;
1581
1582 def getGuestAdditionsIso(self):
1583 """
1584 Get the path to the guest addition iso.
1585 """
1586 if not self._detectBuild():
1587 return None;
1588 return self.oBuild.sGuestAdditionsIso;
1589
1590 #
1591 # Override everything from the base class so the testdrivers don't have to
1592 # check whether we have overridden a method or not.
1593 #
1594
1595 def showUsage(self):
1596 rc = base.TestDriver.showUsage(self);
1597 reporter.log('');
1598 reporter.log('Generic VirtualBox Options:');
1599 reporter.log(' --vbox-session-type <type>');
1600 reporter.log(' Sets the session type. Typical values are: gui, headless, sdl');
1601 reporter.log(' Default: %s' % (self.sSessionTypeDef));
1602 reporter.log(' --vrdp, --no-vrdp');
1603 reporter.log(' Enables VRDP, ports starting at 6000');
1604 reporter.log(' Default: --vrdp');
1605 reporter.log(' --vrdp-base-port <port>');
1606 reporter.log(' Sets the base for VRDP port assignments.');
1607 reporter.log(' Default: %s' % (self.uVrdpBasePortDef));
1608 reporter.log(' --vbox-default-bridged-nic <interface>');
1609 reporter.log(' Sets the default interface for bridged networking.');
1610 reporter.log(' Default: autodetect');
1611 reporter.log(' --vbox-use-svc-defaults');
1612 reporter.log(' Use default locations and files for VBoxSVC. This is useful');
1613 reporter.log(' for automatically configuring the test VMs for debugging.');
1614 reporter.log(' --vbox-self-log');
1615 reporter.log(' The VBox logger group settings for the testdriver.');
1616 reporter.log(' --vbox-self-log-flags');
1617 reporter.log(' The VBox logger flags settings for the testdriver.');
1618 reporter.log(' --vbox-self-log-dest');
1619 reporter.log(' The VBox logger destination settings for the testdriver.');
1620 reporter.log(' --vbox-session-log');
1621 reporter.log(' The VM session logger group settings.');
1622 reporter.log(' --vbox-session-log-flags');
1623 reporter.log(' The VM session logger flags.');
1624 reporter.log(' --vbox-session-log-dest');
1625 reporter.log(' The VM session logger destination settings.');
1626 reporter.log(' --vbox-svc-log');
1627 reporter.log(' The VBoxSVC logger group settings.');
1628 reporter.log(' --vbox-svc-log-flags');
1629 reporter.log(' The VBoxSVC logger flag settings.');
1630 reporter.log(' --vbox-svc-log-dest');
1631 reporter.log(' The VBoxSVC logger destination settings.');
1632 reporter.log(' --vbox-log');
1633 reporter.log(' The VBox logger group settings for everyone.');
1634 reporter.log(' --vbox-log-flags');
1635 reporter.log(' The VBox logger flags settings for everyone.');
1636 reporter.log(' --vbox-log-dest');
1637 reporter.log(' The VBox logger destination settings for everyone.');
1638 reporter.log(' --vbox-svc-debug');
1639 reporter.log(' Start VBoxSVC in a debugger');
1640 reporter.log(' --vbox-always-upload-logs');
1641 reporter.log(' Whether to always upload log files, or only do so on failure.');
1642 reporter.log(' --vbox-always-upload-screenshots');
1643 reporter.log(' Whether to always upload final screen shots, or only do so on failure.');
1644 reporter.log(' --vbox-debugger, --no-vbox-debugger');
1645 reporter.log(' Enables the VBox debugger, port at 5000');
1646 reporter.log(' Default: --vbox-debugger');
1647 if self.oTestVmSet is not None:
1648 self.oTestVmSet.showUsage();
1649 return rc;
1650
1651 def parseOption(self, asArgs, iArg): # pylint: disable=R0915
1652 if asArgs[iArg] == '--vbox-session-type':
1653 iArg += 1;
1654 if iArg >= len(asArgs):
1655 raise base.InvalidOption('The "--vbox-session-type" takes an argument');
1656 self.sSessionType = asArgs[iArg];
1657 elif asArgs[iArg] == '--vrdp':
1658 self.fEnableVrdp = True;
1659 elif asArgs[iArg] == '--no-vrdp':
1660 self.fEnableVrdp = False;
1661 elif asArgs[iArg] == '--vrdp-base-port':
1662 iArg += 1;
1663 if iArg >= len(asArgs):
1664 raise base.InvalidOption('The "--vrdp-base-port" takes an argument');
1665 try: self.uVrdpBasePort = int(asArgs[iArg]);
1666 except: raise base.InvalidOption('The "--vrdp-base-port" value "%s" is not a valid integer' % (asArgs[iArg],));
1667 if self.uVrdpBasePort <= 0 or self.uVrdpBasePort >= 65530:
1668 raise base.InvalidOption('The "--vrdp-base-port" value "%s" is not in the valid range (1..65530)'
1669 % (asArgs[iArg],));
1670 elif asArgs[iArg] == '--vbox-default-bridged-nic':
1671 iArg += 1;
1672 if iArg >= len(asArgs):
1673 raise base.InvalidOption('The "--vbox-default-bridged-nic" takes an argument');
1674 self.sDefBridgedNic = asArgs[iArg];
1675 elif asArgs[iArg] == '--vbox-use-svc-defaults':
1676 self.fUseDefaultSvc = True;
1677 elif asArgs[iArg] == '--vbox-self-log':
1678 iArg += 1;
1679 if iArg >= len(asArgs):
1680 raise base.InvalidOption('The "--vbox-self-log" takes an argument');
1681 self.sLogSelfGroups = asArgs[iArg];
1682 elif asArgs[iArg] == '--vbox-self-log-flags':
1683 iArg += 1;
1684 if iArg >= len(asArgs):
1685 raise base.InvalidOption('The "--vbox-self-log-flags" takes an argument');
1686 self.sLogSelfFlags = asArgs[iArg];
1687 elif asArgs[iArg] == '--vbox-self-log-dest':
1688 iArg += 1;
1689 if iArg >= len(asArgs):
1690 raise base.InvalidOption('The "--vbox-self-log-dest" takes an argument');
1691 self.sLogSelfDest = asArgs[iArg];
1692 elif asArgs[iArg] == '--vbox-session-log':
1693 iArg += 1;
1694 if iArg >= len(asArgs):
1695 raise base.InvalidOption('The "--vbox-session-log" takes an argument');
1696 self.sLogSessionGroups = asArgs[iArg];
1697 elif asArgs[iArg] == '--vbox-session-log-flags':
1698 iArg += 1;
1699 if iArg >= len(asArgs):
1700 raise base.InvalidOption('The "--vbox-session-log-flags" takes an argument');
1701 self.sLogSessionFlags = asArgs[iArg];
1702 elif asArgs[iArg] == '--vbox-session-log-dest':
1703 iArg += 1;
1704 if iArg >= len(asArgs):
1705 raise base.InvalidOption('The "--vbox-session-log-dest" takes an argument');
1706 self.sLogSessionDest = asArgs[iArg];
1707 elif asArgs[iArg] == '--vbox-svc-log':
1708 iArg += 1;
1709 if iArg >= len(asArgs):
1710 raise base.InvalidOption('The "--vbox-svc-log" takes an argument');
1711 self.sLogSvcGroups = asArgs[iArg];
1712 elif asArgs[iArg] == '--vbox-svc-log-flags':
1713 iArg += 1;
1714 if iArg >= len(asArgs):
1715 raise base.InvalidOption('The "--vbox-svc-log-flags" takes an argument');
1716 self.sLogSvcFlags = asArgs[iArg];
1717 elif asArgs[iArg] == '--vbox-svc-log-dest':
1718 iArg += 1;
1719 if iArg >= len(asArgs):
1720 raise base.InvalidOption('The "--vbox-svc-log-dest" takes an argument');
1721 self.sLogSvcDest = asArgs[iArg];
1722 elif asArgs[iArg] == '--vbox-log':
1723 iArg += 1;
1724 if iArg >= len(asArgs):
1725 raise base.InvalidOption('The "--vbox-log" takes an argument');
1726 self.sLogSelfGroups = asArgs[iArg];
1727 self.sLogSessionGroups = asArgs[iArg];
1728 self.sLogSvcGroups = asArgs[iArg];
1729 elif asArgs[iArg] == '--vbox-log-flags':
1730 iArg += 1;
1731 if iArg >= len(asArgs):
1732 raise base.InvalidOption('The "--vbox-svc-flags" takes an argument');
1733 self.sLogSelfFlags = asArgs[iArg];
1734 self.sLogSessionFlags = asArgs[iArg];
1735 self.sLogSvcFlags = asArgs[iArg];
1736 elif asArgs[iArg] == '--vbox-log-dest':
1737 iArg += 1;
1738 if iArg >= len(asArgs):
1739 raise base.InvalidOption('The "--vbox-log-dest" takes an argument');
1740 self.sLogSelfDest = asArgs[iArg];
1741 self.sLogSessionDest = asArgs[iArg];
1742 self.sLogSvcDest = asArgs[iArg];
1743 elif asArgs[iArg] == '--vbox-svc-debug':
1744 self.fVBoxSvcInDebugger = True;
1745 elif asArgs[iArg] == '--vbox-always-upload-logs':
1746 self.fAlwaysUploadLogs = True;
1747 elif asArgs[iArg] == '--vbox-always-upload-screenshots':
1748 self.fAlwaysUploadScreenshots = True;
1749 elif asArgs[iArg] == '--vbox-debugger':
1750 self.fEnableDebugger = True;
1751 elif asArgs[iArg] == '--no-vbox-debugger':
1752 self.fEnableDebugger = False;
1753 else:
1754 # Relevant for selecting VMs to test?
1755 if self.oTestVmSet is not None:
1756 iRc = self.oTestVmSet.parseOption(asArgs, iArg);
1757 if iRc != iArg:
1758 return iRc;
1759
1760 # Hand it to the base class.
1761 return base.TestDriver.parseOption(self, asArgs, iArg);
1762 return iArg + 1;
1763
1764 def completeOptions(self):
1765 return base.TestDriver.completeOptions(self);
1766
1767 def getResourceSet(self):
1768 if self.oTestVmSet is not None:
1769 return self.oTestVmSet.getResourceSet();
1770 return base.TestDriver.getResourceSet(self);
1771
1772 def actionExtract(self):
1773 return base.TestDriver.actionExtract(self);
1774
1775 def actionVerify(self):
1776 return base.TestDriver.actionVerify(self);
1777
1778 def actionConfig(self):
1779 return base.TestDriver.actionConfig(self);
1780
1781 def actionExecute(self):
1782 return base.TestDriver.actionExecute(self);
1783
1784 def actionCleanupBefore(self):
1785 """
1786 Kill any VBoxSVC left behind by a previous test run.
1787 """
1788 self._killVBoxSVCByPidFile('%s/VBoxSVC.pid' % (self.sScratchPath,));
1789 return base.TestDriver.actionCleanupBefore(self);
1790
1791 def actionCleanupAfter(self):
1792 """
1793 Clean up the VBox bits and then call the base driver.
1794
1795 If your test driver overrides this, it should normally call us at the
1796 end of the job.
1797 """
1798
1799 # Kill any left over VM processes.
1800 self._powerOffAllVms();
1801
1802 # Drop all VBox object references and shutdown xpcom then
1803 # terminating VBoxSVC, with extreme prejudice if need be.
1804 self._teardownVBoxApi();
1805 self._stopVBoxSVC();
1806
1807 # Add the VBoxSVC and testdriver debug+release log files.
1808 if self.fAlwaysUploadLogs or reporter.getErrorCount() > 0:
1809 if self.sVBoxSvcLogFile is not None and os.path.isfile(self.sVBoxSvcLogFile):
1810 reporter.addLogFile(self.sVBoxSvcLogFile, 'log/debug/svc', 'Debug log file for VBoxSVC');
1811 self.sVBoxSvcLogFile = None;
1812
1813 if self.sSelfLogFile is not None and os.path.isfile(self.sSelfLogFile):
1814 reporter.addLogFile(self.sSelfLogFile, 'log/debug/client', 'Debug log file for the test driver');
1815 self.sSelfLogFile = None;
1816
1817 sVBoxSvcRelLog = os.path.join(self.sScratchPath, 'VBoxUserHome', 'VBoxSVC.log');
1818 if os.path.isfile(sVBoxSvcRelLog):
1819 reporter.addLogFile(sVBoxSvcRelLog, 'log/release/svc', 'Release log file for VBoxSVC');
1820 for sSuff in [ '.1', '.2', '.3', '.4', '.5', '.6', '.7', '.8' ]:
1821 if os.path.isfile(sVBoxSvcRelLog + sSuff):
1822 reporter.addLogFile(sVBoxSvcRelLog + sSuff, 'log/release/svc', 'Release log file for VBoxSVC');
1823 # Testbox debugging - START - TEMPORARY, REMOVE ASAP.
1824 if self.sHost in ('darwin', 'freebsd', 'linux', 'solaris', ):
1825 try:
1826 reporter.log('> ls -la %s' % (os.path.join(self.sScratchPath, 'VBoxUserHome'),));
1827 utils.processCall(['ls', '-la', os.path.join(self.sScratchPath, 'VBoxUserHome')]);
1828 reporter.log('> ls -la %s' % (self.sScratchPath,));
1829 utils.processCall(['ls', '-la', self.sScratchPath]);
1830 except: pass;
1831 # Testbox debugging - END - TEMPORARY, REMOVE ASAP.
1832
1833 # Finally, call the base driver to wipe the scratch space.
1834 return base.TestDriver.actionCleanupAfter(self);
1835
1836 def actionAbort(self):
1837 """
1838 Terminate VBoxSVC if we've got a pid file.
1839 """
1840 #
1841 # Take default action first, then kill VBoxSVC. The other way around
1842 # is problematic since the testscript would continue running and possibly
1843 # trigger a new VBoxSVC to start.
1844 #
1845 fRc1 = base.TestDriver.actionAbort(self);
1846 fRc2 = self._killVBoxSVCByPidFile('%s/VBoxSVC.pid' % (self.sScratchPath,));
1847 return fRc1 is True and fRc2 is True;
1848
1849 def onExit(self, iRc):
1850 """
1851 Stop VBoxSVC if we've started it.
1852 """
1853 if self.oVBoxSvcProcess is not None:
1854 reporter.log('*** Shutting down the VBox API... (iRc=%s)' % (iRc,));
1855 self._powerOffAllVms();
1856 self._teardownVBoxApi();
1857 self._stopVBoxSVC();
1858 reporter.log('*** VBox API shutdown done.');
1859 return base.TestDriver.onExit(self, iRc);
1860
1861
1862 #
1863 # Task wait method override.
1864 #
1865
1866 def notifyAboutReadyTask(self, oTask):
1867 """
1868 Overriding base.TestDriver.notifyAboutReadyTask.
1869 """
1870 try:
1871 self.oVBoxMgr.interruptWaitEvents();
1872 reporter.log2('vbox.notifyAboutReadyTask: called interruptWaitEvents');
1873 except:
1874 reporter.logXcpt('vbox.notifyAboutReadyTask');
1875 return base.TestDriver.notifyAboutReadyTask(self, oTask);
1876
1877 def waitForTasksSleepWorker(self, cMsTimeout):
1878 """
1879 Overriding base.TestDriver.waitForTasksSleepWorker.
1880 """
1881 try:
1882 rc = self.oVBoxMgr.waitForEvents(int(cMsTimeout));
1883 _ = rc; #reporter.log2('vbox.waitForTasksSleepWorker(%u): true (waitForEvents -> %s)' % (cMsTimeout, rc));
1884 reporter.doPollWork('vbox.TestDriver.waitForTasksSleepWorker');
1885 return True;
1886 except KeyboardInterrupt:
1887 raise;
1888 except:
1889 reporter.logXcpt('vbox.waitForTasksSleepWorker');
1890 return False;
1891
1892 #
1893 # Utility methods.
1894 #
1895
1896 def processEvents(self, cMsTimeout = 0):
1897 """
1898 Processes events, returning after the first batch has been processed
1899 or the time limit has been reached.
1900
1901 Only Ctrl-C exception, no return.
1902 """
1903 try:
1904 self.oVBoxMgr.waitForEvents(cMsTimeout);
1905 except KeyboardInterrupt:
1906 raise;
1907 except:
1908 pass;
1909 return None;
1910
1911 def processPendingEvents(self):
1912 """ processEvents(0) - no waiting. """
1913 return self.processEvents(0);
1914
1915 def sleep(self, cSecs):
1916 """
1917 Sleep for a specified amount of time, processing XPCOM events all the while.
1918 """
1919 cMsTimeout = long(cSecs * 1000);
1920 msStart = base.timestampMilli();
1921 self.processEvents(0);
1922 while True:
1923 cMsElapsed = base.timestampMilli() - msStart;
1924 if cMsElapsed > cMsTimeout:
1925 break;
1926 #reporter.log2('cMsTimeout=%s - cMsElapsed=%d => %s' % (cMsTimeout, cMsElapsed, cMsTimeout - cMsElapsed));
1927 self.processEvents(cMsTimeout - cMsElapsed);
1928 return None;
1929
1930 def _logVmInfoUnsafe(self, oVM): # pylint: disable=R0915,R0912
1931 """
1932 Internal worker for logVmInfo that is wrapped in try/except.
1933
1934 This is copy, paste, search, replace and edit of infoCmd from vboxshell.py.
1935 """
1936 oOsType = self.oVBox.getGuestOSType(oVM.OSTypeId)
1937 reporter.log(" Name: %s" % (oVM.name));
1938 reporter.log(" ID: %s" % (oVM.id));
1939 reporter.log(" OS Type: %s - %s" % (oVM.OSTypeId, oOsType.description));
1940 reporter.log(" Machine state: %s" % (oVM.state));
1941 reporter.log(" Session state: %s" % (oVM.sessionState));
1942 if self.fpApiVer >= 4.2:
1943 reporter.log(" Session PID: %u (%#x)" % (oVM.sessionPID, oVM.sessionPID));
1944 else:
1945 reporter.log(" Session PID: %u (%#x)" % (oVM.sessionPid, oVM.sessionPid));
1946 if self.fpApiVer >= 5.0:
1947 reporter.log(" Session Name: %s" % (oVM.sessionName));
1948 else:
1949 reporter.log(" Session Name: %s" % (oVM.sessionType));
1950 reporter.log(" CPUs: %s" % (oVM.CPUCount));
1951 reporter.log(" RAM: %sMB" % (oVM.memorySize));
1952 reporter.log(" VRAM: %sMB" % (oVM.VRAMSize));
1953 reporter.log(" Monitors: %s" % (oVM.monitorCount));
1954 if oVM.chipsetType == vboxcon.ChipsetType_PIIX3: sType = "PIIX3";
1955 elif oVM.chipsetType == vboxcon.ChipsetType_ICH9: sType = "ICH9";
1956 else: sType = "unknown %s" % (oVM.chipsetType);
1957 reporter.log(" Chipset: %s" % (sType));
1958 if oVM.firmwareType == vboxcon.FirmwareType_BIOS: sType = "BIOS";
1959 elif oVM.firmwareType == vboxcon.FirmwareType_EFI: sType = "EFI";
1960 elif oVM.firmwareType == vboxcon.FirmwareType_EFI32: sType = "EFI32";
1961 elif oVM.firmwareType == vboxcon.FirmwareType_EFI64: sType = "EFI64";
1962 elif oVM.firmwareType == vboxcon.FirmwareType_EFIDUAL: sType = "EFIDUAL";
1963 else: sType = "unknown %s" % (oVM.firmwareType);
1964 reporter.log(" Firmware: %s" % (sType));
1965 reporter.log(" HwVirtEx: %s" % (oVM.getHWVirtExProperty(vboxcon.HWVirtExPropertyType_Enabled)));
1966 reporter.log(" VPID support: %s" % (oVM.getHWVirtExProperty(vboxcon.HWVirtExPropertyType_VPID)));
1967 reporter.log(" Nested paging: %s" % (oVM.getHWVirtExProperty(vboxcon.HWVirtExPropertyType_NestedPaging)));
1968 if self.fpApiVer >= 4.2 and hasattr(vboxcon, 'CPUPropertyType_LongMode'):
1969 reporter.log(" Long-mode: %s" % (oVM.getCPUProperty(vboxcon.CPUPropertyType_LongMode)));
1970 if self.fpApiVer >= 3.2:
1971 reporter.log(" PAE: %s" % (oVM.getCPUProperty(vboxcon.CPUPropertyType_PAE)));
1972 if self.fpApiVer < 5.0:
1973 reporter.log(" Synthetic CPU: %s" % (oVM.getCPUProperty(vboxcon.CPUPropertyType_Synthetic)));
1974 else:
1975 reporter.log(" PAE: %s" % (oVM.getCpuProperty(vboxcon.CpuPropertyType_PAE)));
1976 reporter.log(" Synthetic CPU: %s" % (oVM.getCpuProperty(vboxcon.CpuPropertyType_Synthetic)));
1977 reporter.log(" ACPI: %s" % (oVM.BIOSSettings.ACPIEnabled));
1978 reporter.log(" IO-APIC: %s" % (oVM.BIOSSettings.IOAPICEnabled));
1979 if self.fpApiVer >= 3.2:
1980 if self.fpApiVer >= 4.2:
1981 reporter.log(" HPET: %s" % (oVM.HPETEnabled));
1982 else:
1983 reporter.log(" HPET: %s" % (oVM.hpetEnabled));
1984 reporter.log(" 3D acceleration: %s" % (oVM.accelerate3DEnabled));
1985 reporter.log(" 2D acceleration: %s" % (oVM.accelerate2DVideoEnabled));
1986 reporter.log(" TeleporterEnabled: %s" % (oVM.teleporterEnabled));
1987 reporter.log(" TeleporterPort: %s" % (oVM.teleporterPort));
1988 reporter.log(" TeleporterAddress: %s" % (oVM.teleporterAddress));
1989 reporter.log(" TeleporterPassword: %s" % (oVM.teleporterPassword));
1990 reporter.log(" Clipboard mode: %s" % (oVM.clipboardMode));
1991 if self.fpApiVer >= 5.0:
1992 reporter.log(" Drag and drop mode: %s" % (oVM.dnDMode));
1993 elif self.fpApiVer >= 4.3:
1994 reporter.log(" Drag and drop mode: %s" % (oVM.dragAndDropMode));
1995 if self.fpApiVer >= 4.0:
1996 reporter.log(" VRDP server: %s" % (oVM.VRDEServer.enabled));
1997 try: sPorts = oVM.VRDEServer.getVRDEProperty("TCP/Ports");
1998 except: sPorts = "";
1999 reporter.log(" VRDP server ports: %s" % (sPorts));
2000 reporter.log(" VRDP auth: %s (%s)" % (oVM.VRDEServer.authType, oVM.VRDEServer.authLibrary));
2001 else:
2002 reporter.log(" VRDP server: %s" % (oVM.VRDPServer.enabled));
2003 reporter.log(" VRDP server ports: %s" % (oVM.VRDPServer.ports));
2004 reporter.log(" Last changed: %s" % (oVM.lastStateChange));
2005
2006 aoControllers = self.oVBoxMgr.getArray(oVM, 'storageControllers')
2007 if aoControllers:
2008 reporter.log(" Controllers:");
2009 for oCtrl in aoControllers:
2010 reporter.log(" %s %s bus: %s type: %s" % (oCtrl.name, oCtrl.controllerType, oCtrl.bus, oCtrl.controllerType));
2011 oAudioAdapter = oVM.audioAdapter;
2012 if oAudioAdapter.audioController == vboxcon.AudioControllerType_AC97: sType = "AC97";
2013 elif oAudioAdapter.audioController == vboxcon.AudioControllerType_SB16: sType = "SB16";
2014 elif oAudioAdapter.audioController == vboxcon.AudioControllerType_HDA: sType = "HDA";
2015 else: sType = "unknown %s" % (oAudioAdapter.audioController);
2016 reporter.log(" AudioController: %s" % (sType));
2017 reporter.log(" AudioEnabled: %s" % (oAudioAdapter.enabled));
2018 if oAudioAdapter.audioDriver == vboxcon.AudioDriverType_CoreAudio: sType = "CoreAudio";
2019 elif oAudioAdapter.audioDriver == vboxcon.AudioDriverType_DirectSound: sType = "DirectSound";
2020 elif oAudioAdapter.audioDriver == vboxcon.AudioDriverType_Pulse: sType = "PulseAudio";
2021 elif oAudioAdapter.audioDriver == vboxcon.AudioDriverType_OSS: sType = "OSS";
2022 elif oAudioAdapter.audioDriver == vboxcon.AudioDriverType_Null: sType = "NULL";
2023 else: sType = "unknown %s" % (oAudioAdapter.audioDriver);
2024 reporter.log(" Host AudioDriver: %s" % (sType));
2025
2026 self.processPendingEvents();
2027 aoAttachments = self.oVBoxMgr.getArray(oVM, 'mediumAttachments')
2028 if aoAttachments:
2029 reporter.log(" Attachments:");
2030 for oAtt in aoAttachments:
2031 sCtrl = "Controller: %s port: %s device: %s type: %s" % (oAtt.controller, oAtt.port, oAtt.device, oAtt.type);
2032 oMedium = oAtt.medium
2033 if oAtt.type == vboxcon.DeviceType_HardDisk:
2034 reporter.log(" %s: HDD" % sCtrl);
2035 reporter.log(" Id: %s" % (oMedium.id));
2036 reporter.log(" Name: %s" % (oMedium.name));
2037 reporter.log(" Format: %s" % (oMedium.format));
2038 reporter.log(" Location: %s" % (oMedium.location));
2039
2040 if oAtt.type == vboxcon.DeviceType_DVD:
2041 reporter.log(" %s: DVD" % sCtrl);
2042 if oMedium:
2043 reporter.log(" Id: %s" % (oMedium.id));
2044 reporter.log(" Name: %s" % (oMedium.name));
2045 if oMedium.hostDrive:
2046 reporter.log(" Host DVD %s" % (oMedium.location));
2047 if oAtt.passthrough:
2048 reporter.log(" [passthrough mode]");
2049 else:
2050 reporter.log(" Virtual image: %s" % (oMedium.location));
2051 reporter.log(" Size: %s" % (oMedium.size));
2052 else:
2053 reporter.log(" empty");
2054
2055 if oAtt.type == vboxcon.DeviceType_Floppy:
2056 reporter.log(" %s: Floppy" % sCtrl);
2057 if oMedium:
2058 reporter.log(" Id: %s" % (oMedium.id));
2059 reporter.log(" Name: %s" % (oMedium.name));
2060 if oMedium.hostDrive:
2061 reporter.log(" Host floppy: %s" % (oMedium.location));
2062 else:
2063 reporter.log(" Virtual image: %s" % (oMedium.location));
2064 reporter.log(" Size: %s" % (oMedium.size));
2065 else:
2066 reporter.log(" empty");
2067 self.processPendingEvents();
2068
2069 reporter.log(" Network Adapter:");
2070 for iSlot in range(0, 32):
2071 try: oNic = oVM.getNetworkAdapter(iSlot)
2072 except: break;
2073 if not oNic.enabled:
2074 reporter.log2(" slot #%d found but not enabled, skipping" % (iSlot,));
2075 continue;
2076 if oNic.adapterType == vboxcon.NetworkAdapterType_Am79C973: sType = "PCNet";
2077 elif oNic.adapterType == vboxcon.NetworkAdapterType_Am79C970A: sType = "PCNetOld";
2078 elif oNic.adapterType == vboxcon.NetworkAdapterType_I82545EM: sType = "E1000";
2079 elif oNic.adapterType == vboxcon.NetworkAdapterType_I82540EM: sType = "E1000Desk";
2080 elif oNic.adapterType == vboxcon.NetworkAdapterType_I82543GC: sType = "E1000Srv2";
2081 elif oNic.adapterType == vboxcon.NetworkAdapterType_Virtio: sType = "Virtio";
2082 else: sType = "unknown %s" % (oNic.adapterType);
2083 reporter.log(" slot #%d: type: %s (%s) MAC Address: %s lineSpeed: %s" % \
2084 (iSlot, sType, oNic.adapterType, oNic.MACAddress, oNic.lineSpeed) );
2085
2086 if oNic.attachmentType == vboxcon.NetworkAttachmentType_NAT:
2087 reporter.log(" attachmentType: NAT (%s)" % (oNic.attachmentType));
2088 if self.fpApiVer >= 4.1:
2089 reporter.log(" nat-network: %s" % (oNic.NATNetwork,));
2090 elif oNic.attachmentType == vboxcon.NetworkAttachmentType_Bridged:
2091 reporter.log(" attachmentType: Bridged (%s)" % (oNic.attachmentType));
2092 if self.fpApiVer >= 4.1:
2093 reporter.log(" hostInterface: %s" % (oNic.bridgedInterface));
2094 else:
2095 reporter.log(" hostInterface: %s" % (oNic.hostInterface));
2096 elif oNic.attachmentType == vboxcon.NetworkAttachmentType_Internal:
2097 reporter.log(" attachmentType: Internal (%s)" % (oNic.attachmentType));
2098 reporter.log(" intnet-name: %s" % (oNic.internalNetwork,));
2099 elif oNic.attachmentType == vboxcon.NetworkAttachmentType_HostOnly:
2100 reporter.log(" attachmentType: HostOnly (%s)" % (oNic.attachmentType));
2101 if self.fpApiVer >= 4.1:
2102 reporter.log(" hostInterface: %s" % (oNic.hostOnlyInterface));
2103 else:
2104 reporter.log(" hostInterface: %s" % (oNic.hostInterface));
2105 else:
2106 if self.fpApiVer >= 4.1:
2107 if oNic.attachmentType == vboxcon.NetworkAttachmentType_Generic:
2108 reporter.log(" attachmentType: Generic (%s)" % (oNic.attachmentType));
2109 reporter.log(" generic-driver: %s" % (oNic.GenericDriver));
2110 else:
2111 reporter.log(" attachmentType: unknown-%s" % (oNic.attachmentType));
2112 else:
2113 reporter.log(" attachmentType: unknown-%s" % (oNic.attachmentType));
2114 if oNic.traceEnabled:
2115 reporter.log(" traceFile: %s" % (oNic.traceFile));
2116 self.processPendingEvents();
2117
2118 reporter.log(" Serial ports:");
2119 for iSlot in range(0, 8):
2120 try: oPort = oVM.getSerialPort(iSlot)
2121 except: break;
2122 if oPort is not None and oPort.enabled:
2123 enmHostMode = oPort.hostMode;
2124 if enmHostMode == vboxcon.PortMode_Disconnected: sType = "Disconnected";
2125 elif enmHostMode == vboxcon.PortMode_HostPipe: sType = "HostPipe";
2126 elif enmHostMode == vboxcon.PortMode_HostDevice: sType = "HostDevice";
2127 elif enmHostMode == vboxcon.PortMode_RawFile: sType = "RawFile";
2128 elif enmHostMode == vboxcon.PortMode_TCP: sType = "TCP";
2129 else: sType = "unknown %s" % (enmHostMode);
2130 reporter.log(" slot #%d: hostMode: %s (%s) I/O port: %s IRQ: %s server: %s path: %s" %
2131 (iSlot, sType, enmHostMode, oPort.IOBase, oPort.IRQ, oPort.server, oPort.path,) );
2132 self.processPendingEvents();
2133
2134 return True;
2135
2136 def logVmInfo(self, oVM): # pylint: disable=R0915,R0912
2137 """
2138 Logs VM configuration details.
2139
2140 This is copy, past, search, replace and edit of infoCmd from vboxshell.py.
2141 """
2142 try:
2143 fRc = self._logVmInfoUnsafe(oVM);
2144 except:
2145 reporter.logXcpt();
2146 fRc = False;
2147 return fRc;
2148
2149 def logVmInfoByName(self, sName):
2150 """
2151 logVmInfo + getVmByName.
2152 """
2153 return self.logVmInfo(self.getVmByName(sName));
2154
2155 def tryFindGuestOsId(self, sIdOrDesc):
2156 """
2157 Takes a guest OS ID or Description and returns the ID.
2158 If nothing matching it is found, the input is returned unmodified.
2159 """
2160
2161 if self.fpApiVer >= 4.0:
2162 if sIdOrDesc == 'Solaris (64 bit)':
2163 sIdOrDesc = 'Oracle Solaris 10 5/09 and earlier (64 bit)';
2164
2165 try:
2166 aoGuestTypes = self.oVBoxMgr.getArray(self.oVBox, 'GuestOSTypes');
2167 except:
2168 reporter.logXcpt();
2169 else:
2170 for oGuestOS in aoGuestTypes:
2171 try:
2172 sId = oGuestOS.id;
2173 sDesc = oGuestOS.description;
2174 except:
2175 reporter.logXcpt();
2176 else:
2177 if sIdOrDesc == sId or sIdOrDesc == sDesc:
2178 sIdOrDesc = sId;
2179 break;
2180 self.processPendingEvents();
2181 return sIdOrDesc
2182
2183 def resourceFindVmHd(self, sVmName, sFlavor):
2184 """
2185 Search the test resources for the most recent VM HD.
2186
2187 Returns path relative to the test resource root.
2188 """
2189 ## @todo implement a proper search algo here.
2190 return '4.2/' + sFlavor + '/' + sVmName + '/t-' + sVmName + '.vdi';
2191
2192
2193 #
2194 # VM Api wrappers that logs errors, hides exceptions and other details.
2195 #
2196
2197 # pylint: disable=R0913,R0914,R0915
2198 def createTestVM(self,
2199 sName,
2200 iGroup,
2201 sHd = None,
2202 cMbRam = None,
2203 cCpus = 1,
2204 fVirtEx = None,
2205 fNestedPaging = None,
2206 sDvdImage = None,
2207 sKind = "Other",
2208 fIoApic = None,
2209 fPae = None,
2210 fFastBootLogo = True,
2211 eNic0Type = None,
2212 eNic0AttachType = None,
2213 sNic0NetName = 'default',
2214 sNic0MacAddr = 'grouped',
2215 sFloppy = None,
2216 fNatForwardingForTxs = None,
2217 sHddControllerType = 'IDE Controller',
2218 fVmmDevTestingPart = None,
2219 fVmmDevTestingMmio = False,
2220 sFirmwareType = 'bios',
2221 sChipsetType = 'piix3',
2222 sDvdControllerType = 'IDE Controller',
2223 sCom1RawFile = None):
2224 """
2225 Creates a test VM with a immutable HD from the test resources.
2226 """
2227 if not self.importVBoxApi():
2228 return None;
2229
2230 # create + register the VM
2231 try:
2232 if self.fpApiVer >= 4.2: # Introduces grouping (third parameter, empty for now).
2233 oVM = self.oVBox.createMachine("", sName, [], self.tryFindGuestOsId(sKind), "");
2234 elif self.fpApiVer >= 4.0:
2235 oVM = self.oVBox.createMachine("", sName, self.tryFindGuestOsId(sKind), "", False);
2236 elif self.fpApiVer >= 3.2:
2237 oVM = self.oVBox.createMachine(sName, self.tryFindGuestOsId(sKind), "", "", False);
2238 else:
2239 oVM = self.oVBox.createMachine(sName, self.tryFindGuestOsId(sKind), "", "");
2240 try:
2241 oVM.saveSettings();
2242 try:
2243 self.oVBox.registerMachine(oVM);
2244 except:
2245 raise;
2246 except:
2247 reporter.logXcpt();
2248 if self.fpApiVer >= 4.0:
2249 try:
2250 if self.fpApiVer >= 4.3:
2251 oProgress = oVM.deleteConfig([]);
2252 else:
2253 oProgress = oVM.delete(None);
2254 self.waitOnProgress(oProgress);
2255 except:
2256 reporter.logXcpt();
2257 else:
2258 try: oVM.deleteSettings();
2259 except: reporter.logXcpt();
2260 raise;
2261 except:
2262 reporter.errorXcpt('failed to create vm "%s"' % (sName));
2263 return None;
2264
2265 # Configure the VM.
2266 fRc = True;
2267 oSession = self.openSession(oVM);
2268 if oSession is not None:
2269 fRc = oSession.setupPreferredConfig();
2270
2271 if fRc and cMbRam is not None :
2272 fRc = oSession.setRamSize(cMbRam);
2273 if fRc and cCpus is not None:
2274 fRc = oSession.setCpuCount(cCpus);
2275 if fRc and fVirtEx is not None:
2276 fRc = oSession.enableVirtEx(fVirtEx);
2277 if fRc and fNestedPaging is not None:
2278 fRc = oSession.enableNestedPaging(fNestedPaging);
2279 if fRc and fIoApic is not None:
2280 fRc = oSession.enableIoApic(fIoApic);
2281 if fRc and fPae is not None:
2282 fRc = oSession.enablePae(fPae);
2283 if fRc and sDvdImage is not None:
2284 fRc = oSession.attachDvd(sDvdImage, sDvdControllerType);
2285 if fRc and sHd is not None:
2286 fRc = oSession.attachHd(sHd, sHddControllerType);
2287 if fRc and sFloppy is not None:
2288 fRc = oSession.attachFloppy(sFloppy);
2289 if fRc and eNic0Type is not None:
2290 fRc = oSession.setNicType(eNic0Type, 0);
2291 if fRc and (eNic0AttachType is not None or (sNic0NetName is not None and sNic0NetName != 'default')):
2292 fRc = oSession.setNicAttachment(eNic0AttachType, sNic0NetName, 0);
2293 if fRc and sNic0MacAddr is not None:
2294 if sNic0MacAddr == 'grouped':
2295 sNic0MacAddr = '%02u' % (iGroup);
2296 fRc = oSession.setNicMacAddress(sNic0MacAddr, 0);
2297 if fRc and fNatForwardingForTxs is True:
2298 fRc = oSession.setupNatForwardingForTxs();
2299 if fRc and fFastBootLogo is not None:
2300 fRc = oSession.setupBootLogo(fFastBootLogo);
2301 if fRc and self.fEnableVrdp:
2302 fRc = oSession.setupVrdp(True, self.uVrdpBasePort + iGroup);
2303 if fRc and fVmmDevTestingPart is not None:
2304 fRc = oSession.enableVmmDevTestingPart(fVmmDevTestingPart, fVmmDevTestingMmio);
2305 if fRc and sFirmwareType == 'bios':
2306 fRc = oSession.setFirmwareType(vboxcon.FirmwareType_BIOS);
2307 elif sFirmwareType == 'efi':
2308 fRc = oSession.setFirmwareType(vboxcon.FirmwareType_EFI);
2309 if fRc and self.fEnableDebugger:
2310 fRc = oSession.setExtraData('VBoxInternal/DBGC/Enabled', '1');
2311 if fRc and sChipsetType == 'piix3':
2312 fRc = oSession.setChipsetType(vboxcon.ChipsetType_PIIX3);
2313 elif sChipsetType == 'ich9':
2314 fRc = oSession.setChipsetType(vboxcon.ChipsetType_ICH9);
2315 if fRc and sCom1RawFile:
2316 fRc = oSession.setupSerialToRawFile(0, sCom1RawFile);
2317
2318 if fRc: fRc = oSession.saveSettings();
2319 if not fRc: oSession.discardSettings(True);
2320 oSession.close();
2321 if not fRc:
2322 try: self.oVBox.unregisterMachine(oVM.id);
2323 except: pass;
2324 if self.fpApiVer >= 4.0:
2325 try:
2326 if self.fpApiVer >= 4.3:
2327 oProgress = oVM.deleteConfig([]);
2328 else:
2329 oProgress = oVM.delete(None);
2330 self.waitOnProgress(oProgress);
2331 except:
2332 reporter.logXcpt();
2333 else:
2334 try: oVM.deleteSettings();
2335 except: reporter.logXcpt();
2336 return None;
2337
2338 # success.
2339 reporter.log('created "%s" with name "%s"' % (oVM.id, sName));
2340 self.aoVMs.append(oVM);
2341 self.logVmInfo(oVM); # testing...
2342 return oVM;
2343 # pylint: enable=R0913,R0914,R0915
2344
2345 def addTestMachine(self, sNameOrId, fQuiet = False):
2346 """
2347 Adds an already existing (that is, configured) test VM to the
2348 test VM list.
2349 """
2350 # find + add the VM to the list.
2351 try:
2352 if self.fpApiVer >= 4.0:
2353 oVM = self.oVBox.findMachine(sNameOrId);
2354 else:
2355 reporter.error('fpApiVer=%s - did you remember to initialize the API' % (self.fpApiVer,));
2356 except:
2357 reporter.errorXcpt('could not find vm "%s"' % (sNameOrId,));
2358 return None;
2359
2360 self.aoVMs.append(oVM);
2361 if not fQuiet:
2362 reporter.log('Added "%s" with name "%s"' % (oVM.id, sNameOrId));
2363 self.logVmInfo(oVM);
2364 return oVM;
2365
2366 def openSession(self, oVM):
2367 """
2368 Opens a session for the VM. Returns the a Session wrapper object that
2369 will automatically close the session when the wrapper goes out of scope.
2370
2371 On failure None is returned and an error is logged.
2372 """
2373 try:
2374 sUuid = oVM.id;
2375 except:
2376 reporter.errorXcpt('failed to get the UUID for VM "%s"' % (oVM,));
2377 return None;
2378
2379 # This loop is a kludge to deal with us racing the closing of the
2380 # direct session of a previous VM run. See waitOnDirectSessionClose.
2381 for i in range(10):
2382 try:
2383 if self.fpApiVer <= 3.2:
2384 oSession = self.oVBoxMgr.openMachineSession(sUuid);
2385 else:
2386 oSession = self.oVBoxMgr.openMachineSession(oVM);
2387 break;
2388 except:
2389 if i == 9:
2390 reporter.errorXcpt('failed to open session for "%s" ("%s")' % (sUuid, oVM));
2391 return None;
2392 if i > 0:
2393 reporter.logXcpt('warning: failed to open session for "%s" ("%s") - retrying in %u secs' % (sUuid, oVM, i));
2394 self.waitOnDirectSessionClose(oVM, 5000 + i * 1000);
2395 from testdriver.vboxwrappers import SessionWrapper;
2396 return SessionWrapper(oSession, oVM, self.oVBox, self.oVBoxMgr, self, False);
2397
2398 def getVmByName(self, sName):
2399 """
2400 Get a test VM by name. Returns None if not found, logged.
2401 """
2402 # Look it up in our 'cache'.
2403 for oVM in self.aoVMs:
2404 try:
2405 #reporter.log2('cur: %s / %s (oVM=%s)' % (oVM.name, oVM.id, oVM));
2406 if oVM.name == sName:
2407 return oVM;
2408 except:
2409 reporter.errorXcpt('failed to get the name from the VM "%s"' % (oVM));
2410
2411 # Look it up the standard way.
2412 return self.addTestMachine(sName, fQuiet = True);
2413
2414 def getVmByUuid(self, sUuid):
2415 """
2416 Get a test VM by uuid. Returns None if not found, logged.
2417 """
2418 # Look it up in our 'cache'.
2419 for oVM in self.aoVMs:
2420 try:
2421 if oVM.id == sUuid:
2422 return oVM;
2423 except:
2424 reporter.errorXcpt('failed to get the UUID from the VM "%s"' % (oVM));
2425
2426 # Look it up the standard way.
2427 return self.addTestMachine(sUuid, fQuiet = True);
2428
2429 def waitOnProgress(self, oProgress, cMsTimeout = 1000000, fErrorOnTimeout = True, cMsInterval = 1000):
2430 """
2431 Waits for a progress object to complete. Returns the status code.
2432 """
2433 # Wait for progress no longer than cMsTimeout time period.
2434 tsStart = datetime.datetime.now()
2435 while True:
2436 self.processPendingEvents();
2437 try:
2438 if oProgress.completed:
2439 break;
2440 except:
2441 return -1;
2442 self.processPendingEvents();
2443
2444 tsNow = datetime.datetime.now()
2445 tsDelta = tsNow - tsStart
2446 if ((tsDelta.microseconds + tsDelta.seconds * 1000000) // 1000) > cMsTimeout:
2447 if fErrorOnTimeout:
2448 reporter.errorTimeout('Timeout while waiting for progress.')
2449 return -1
2450
2451 reporter.doPollWork('vbox.TestDriver.waitOnProgress');
2452 try: oProgress.waitForCompletion(cMsInterval);
2453 except: return -2;
2454
2455 try: rc = oProgress.resultCode;
2456 except: rc = -2;
2457 self.processPendingEvents();
2458 return rc;
2459
2460 def waitOnDirectSessionClose(self, oVM, cMsTimeout):
2461 """
2462 Waits for the VM process to close it's current direct session.
2463
2464 Returns None.
2465 """
2466 # Get the original values so we're not subject to
2467 try:
2468 eCurState = oVM.sessionState;
2469 if self.fpApiVer >= 5.0:
2470 sCurName = sOrgName = oVM.sessionName;
2471 else:
2472 sCurName = sOrgName = oVM.sessionType;
2473 if self.fpApiVer >= 4.2:
2474 iCurPid = iOrgPid = oVM.sessionPID;
2475 else:
2476 iCurPid = iOrgPid = oVM.sessionPid;
2477 except Exception as oXcpt:
2478 if ComError.notEqual(oXcpt, ComError.E_ACCESSDENIED):
2479 reporter.logXcpt();
2480 self.processPendingEvents();
2481 return None;
2482 self.processPendingEvents();
2483
2484 msStart = base.timestampMilli();
2485 while iCurPid == iOrgPid \
2486 and sCurName == sOrgName \
2487 and sCurName != '' \
2488 and base.timestampMilli() - msStart < cMsTimeout \
2489 and ( eCurState == vboxcon.SessionState_Unlocking \
2490 or eCurState == vboxcon.SessionState_Spawning \
2491 or eCurState == vboxcon.SessionState_Locked):
2492 self.processEvents(1000);
2493 try:
2494 eCurState = oVM.sessionState;
2495 sCurName = oVM.sessionName if self.fpApiVer >= 5.0 else oVM.sessionType;
2496 iCurPid = oVM.sessionPID if self.fpApiVer >= 4.2 else oVM.sessionPid;
2497 except Exception as oXcpt:
2498 if ComError.notEqual(oXcpt, ComError.E_ACCESSDENIED):
2499 reporter.logXcpt();
2500 break;
2501 self.processPendingEvents();
2502 self.processPendingEvents();
2503 return None;
2504
2505 def uploadStartupLogFile(self, oVM, sVmName):
2506 """
2507 Uploads the VBoxStartup.log when present.
2508 """
2509 fRc = True;
2510 try:
2511 sLogFile = os.path.join(oVM.logFolder, 'VBoxHardening.log');
2512 except:
2513 reporter.logXcpt();
2514 fRc = False;
2515 else:
2516 if os.path.isfile(sLogFile):
2517 reporter.addLogFile(sLogFile, 'log/release/vm', '%s hardening log' % (sVmName, ),
2518 sAltName = '%s-%s' % (sVmName, os.path.basename(sLogFile),));
2519 return fRc;
2520
2521 def annotateAndUploadProcessReport(self, sProcessReport, sFilename, sKind, sDesc):
2522 """
2523 Annotates the given VM process report and uploads it if successfull.
2524 """
2525 fRc = False;
2526 if self.oBuild is not None and self.oBuild.sInstallPath is not None:
2527 oResolver = btresolver.BacktraceResolver(self.sScratchPath, self.oBuild.sInstallPath,
2528 self.getBuildOs(), self.getBuildArch(),
2529 fnLog = reporter.log);
2530 fRcTmp = oResolver.prepareEnv();
2531 if fRcTmp:
2532 reporter.log('Successfully prepared environment');
2533 sReportDbgSym = oResolver.annotateReport(sProcessReport);
2534 if sReportDbgSym is not None:
2535 reporter.addLogString(sReportDbgSym, sFilename, sKind, sDesc);
2536 fRc = True;
2537 else:
2538 reporter.log('Annotating report failed');
2539 oResolver.cleanupEnv();
2540 return fRc;
2541
2542 def startVmEx(self, oVM, fWait = True, sType = None, sName = None, asEnv = None): # pylint: disable=R0914,R0915
2543 """
2544 Start the VM, returning the VM session and progress object on success.
2545 The session is also added to the task list and to the aoRemoteSessions set.
2546
2547 asEnv is a list of string on the putenv() form.
2548
2549 On failure (None, None) is returned and an error is logged.
2550 """
2551 # Massage and check the input.
2552 if sType is None:
2553 sType = self.sSessionType;
2554 if sName is None:
2555 try: sName = oVM.name;
2556 except: sName = 'bad-vm-handle';
2557 reporter.log('startVmEx: sName=%s fWait=%s sType=%s' % (sName, fWait, sType));
2558 if oVM is None:
2559 return (None, None);
2560
2561 ## @todo Do this elsewhere.
2562 # Hack alert. Disables all annoying GUI popups.
2563 if sType == 'gui' and not self.aoRemoteSessions:
2564 try:
2565 self.oVBox.setExtraData('GUI/Input/AutoCapture', 'false');
2566 if self.fpApiVer >= 3.2:
2567 self.oVBox.setExtraData('GUI/LicenseAgreed', '8');
2568 else:
2569 self.oVBox.setExtraData('GUI/LicenseAgreed', '7');
2570 self.oVBox.setExtraData('GUI/RegistrationData', 'triesLeft=0');
2571 self.oVBox.setExtraData('GUI/SUNOnlineData', 'triesLeft=0');
2572 self.oVBox.setExtraData('GUI/SuppressMessages', 'confirmVMReset,remindAboutMouseIntegrationOn,'
2573 'remindAboutMouseIntegrationOff,remindAboutPausedVMInput,confirmInputCapture,'
2574 'confirmGoingFullscreen,remindAboutInaccessibleMedia,remindAboutWrongColorDepth,'
2575 'confirmRemoveMedium,allPopupPanes,allMessageBoxes,all');
2576 self.oVBox.setExtraData('GUI/UpdateDate', 'never');
2577 self.oVBox.setExtraData('GUI/PreventBetaWarning', self.oVBox.version);
2578 except:
2579 reporter.logXcpt();
2580
2581 # The UUID for the name.
2582 try:
2583 sUuid = oVM.id;
2584 except:
2585 reporter.errorXcpt('failed to get the UUID for VM "%s"' % (oVM));
2586 return (None, None);
2587 self.processPendingEvents();
2588
2589 # Construct the environment.
2590 sLogFile = '%s/VM-%s.log' % (self.sScratchPath, sUuid);
2591 try: os.remove(sLogFile);
2592 except: pass;
2593 if self.sLogSessionDest:
2594 sLogDest = self.sLogSessionDest;
2595 else:
2596 sLogDest = 'file=%s' % sLogFile;
2597 sEnv = 'VBOX_LOG=%s\nVBOX_LOG_FLAGS=%s\nVBOX_LOG_DEST=nodeny %s\nVBOX_RELEASE_LOG_FLAGS=append time' \
2598 % (self.sLogSessionGroups, self.sLogSessionFlags, sLogDest,);
2599 # Extra audio logging
2600 sEnv += '\nVBOX_RELEASE_LOG=drv_audio.e.l.l2+drv_host_audio.e.l.l2'
2601 if sType == 'gui':
2602 sEnv += '\nVBOX_GUI_DBG_ENABLED=1'
2603 if asEnv is not None and asEnv:
2604 sEnv += '\n' + ('\n'.join(asEnv));
2605
2606 # Shortcuts for local testing.
2607 oProgress = oWrapped = None;
2608 oTestVM = self.oTestVmSet.findTestVmByName(sName) if self.oTestVmSet is not None else None;
2609 try:
2610 if oTestVM is not None \
2611 and oTestVM.fSnapshotRestoreCurrent is True:
2612 if oVM.state is vboxcon.MachineState_Running:
2613 reporter.log2('Machine "%s" already running.' % (sName,));
2614 oProgress = None;
2615 oWrapped = self.openSession(oVM);
2616 else:
2617 reporter.log2('Checking if snapshot for machine "%s" exists.' % (sName,));
2618 oSessionWrapperRestore = self.openSession(oVM);
2619 if oSessionWrapperRestore is not None:
2620 oSnapshotCur = oVM.currentSnapshot;
2621 if oSnapshotCur is not None:
2622 reporter.log2('Restoring snapshot for machine "%s".' % (sName,));
2623 oSessionWrapperRestore.restoreSnapshot(oSnapshotCur);
2624 reporter.log2('Current snapshot for machine "%s" restored.' % (sName,));
2625 else:
2626 reporter.log('warning: no current snapshot for machine "%s" found.' % (sName,));
2627 oSessionWrapperRestore.close();
2628 except:
2629 reporter.errorXcpt();
2630 return (None, None);
2631
2632 # Open a remote session, wait for this operation to complete.
2633 # (The loop is a kludge to deal with us racing the closing of the
2634 # direct session of a previous VM run. See waitOnDirectSessionClose.)
2635 if oWrapped is None:
2636 for i in range(10):
2637 try:
2638 if self.fpApiVer < 4.3 \
2639 or (self.fpApiVer == 4.3 and not hasattr(self.oVBoxMgr, 'getSessionObject')):
2640 oSession = self.oVBoxMgr.mgr.getSessionObject(self.oVBox); # pylint: disable=E1101
2641 elif self.fpApiVer < 5.2 \
2642 or (self.fpApiVer == 5.2 and hasattr(self.oVBoxMgr, 'vbox')):
2643 oSession = self.oVBoxMgr.getSessionObject(self.oVBox); # pylint: disable=E1101
2644 else:
2645 oSession = self.oVBoxMgr.getSessionObject(); # pylint: disable=E1101,E1120
2646 if self.fpApiVer < 3.3:
2647 oProgress = self.oVBox.openRemoteSession(oSession, sUuid, sType, sEnv);
2648 else:
2649 oProgress = oVM.launchVMProcess(oSession, sType, sEnv);
2650 break;
2651 except:
2652 if i == 9:
2653 reporter.errorXcpt('failed to start VM "%s" ("%s"), aborting.' % (sUuid, sName));
2654 return (None, None);
2655 oSession = None;
2656 if i >= 0:
2657 reporter.logXcpt('warning: failed to start VM "%s" ("%s") - retrying in %u secs.' % (sUuid, oVM, i)); # pylint: disable=C0301
2658 self.waitOnDirectSessionClose(oVM, 5000 + i * 1000);
2659 if fWait and oProgress is not None:
2660 rc = self.waitOnProgress(oProgress);
2661 if rc < 0:
2662 self.waitOnDirectSessionClose(oVM, 5000);
2663
2664 # VM failed to power up, still collect VBox.log, need to wrap the session object
2665 # in order to use the helper for adding the log files to the report.
2666 from testdriver.vboxwrappers import SessionWrapper;
2667 oTmp = SessionWrapper(oSession, oVM, self.oVBox, self.oVBoxMgr, self, True, sName, sLogFile);
2668 oTmp.addLogsToReport();
2669 try:
2670 if oSession is not None:
2671 oSession.close();
2672 except: pass;
2673 reportError(oProgress, 'failed to open session for "%s"' % (sName));
2674 self.uploadStartupLogFile(oVM, sName);
2675 return (None, None);
2676 reporter.log2('waitOnProgress -> %s' % (rc,));
2677
2678 # Wrap up the session object and push on to the list before returning it.
2679 if oWrapped is None:
2680 from testdriver.vboxwrappers import SessionWrapper;
2681 oWrapped = SessionWrapper(oSession, oVM, self.oVBox, self.oVBoxMgr, self, True, sName, sLogFile);
2682
2683 oWrapped.registerEventHandlerForTask();
2684 self.aoRemoteSessions.append(oWrapped);
2685 if oWrapped is not self.aoRemoteSessions[len(self.aoRemoteSessions) - 1]:
2686 reporter.error('not by reference: oWrapped=%s aoRemoteSessions[%s]=%s'
2687 % (oWrapped, len(self.aoRemoteSessions) - 1,
2688 self.aoRemoteSessions[len(self.aoRemoteSessions) - 1]));
2689 self.addTask(oWrapped);
2690
2691 reporter.log2('startVmEx: oSession=%s, oSessionWrapper=%s, oProgress=%s' % (oSession, oWrapped, oProgress));
2692
2693 from testdriver.vboxwrappers import ProgressWrapper;
2694 return (oWrapped, ProgressWrapper(oProgress, self.oVBoxMgr, self,
2695 'starting %s' % (sName,)) if oProgress else None);
2696
2697 def startVm(self, oVM, sType=None, sName = None, asEnv = None):
2698 """ Simplified version of startVmEx. """
2699 oSession, _ = self.startVmEx(oVM, True, sType, sName, asEnv = asEnv);
2700 return oSession;
2701
2702 def startVmByNameEx(self, sName, fWait=True, sType=None, asEnv = None):
2703 """
2704 Start the VM, returning the VM session and progress object on success.
2705 The session is also added to the task list and to the aoRemoteSessions set.
2706
2707 On failure (None, None) is returned and an error is logged.
2708 """
2709 oVM = self.getVmByName(sName);
2710 if oVM is None:
2711 return (None, None);
2712 return self.startVmEx(oVM, fWait, sType, sName, asEnv = asEnv);
2713
2714 def startVmByName(self, sName, sType=None, asEnv = None):
2715 """
2716 Start the VM, returning the VM session on success. The session is
2717 also added to the task list and to the aoRemoteSessions set.
2718
2719 On failure None is returned and an error is logged.
2720 """
2721 oSession, _ = self.startVmByNameEx(sName, True, sType, asEnv = asEnv);
2722 return oSession;
2723
2724 def terminateVmBySession(self, oSession, oProgress = None, fTakeScreenshot = None): # pylint: disable=R0915
2725 """
2726 Terminates the VM specified by oSession and adds the release logs to
2727 the test report.
2728
2729 This will try achieve this by using powerOff, but will resort to
2730 tougher methods if that fails.
2731
2732 The session will always be removed from the task list.
2733 The session will be closed unless we fail to kill the process.
2734 The session will be removed from the remote session list if closed.
2735
2736 The progress object (a wrapper!) is for teleportation and similar VM
2737 operations, it will be attempted canceled before powering off the VM.
2738 Failures are logged but ignored.
2739 The progress object will always be removed from the task list.
2740
2741 Returns True if powerOff and session close both succeed.
2742 Returns False if on failure (logged), including when we successfully
2743 kill the VM process.
2744 """
2745 reporter.log2('terminateVmBySession: oSession=%s (pid=%s) oProgress=%s' % (oSession.sName, oSession.getPid(), oProgress));
2746
2747 # Call getPid first to make sure the PID is cached in the wrapper.
2748 oSession.getPid();
2749
2750 #
2751 # If the host is out of memory, just skip all the info collection as it
2752 # requires memory too and seems to wedge.
2753 #
2754 sHostProcessInfo = None;
2755 sHostProcessInfoHung = None;
2756 sLastScreenshotPath = None;
2757 sOsKernelLog = None;
2758 sVgaText = None;
2759 asMiscInfos = [];
2760
2761 if not oSession.fHostMemoryLow:
2762 # Try to fetch the VM process info before meddling with its state.
2763 if self.fAlwaysUploadLogs or reporter.testErrorCount() > 0:
2764 sHostProcessInfo = utils.processGetInfo(oSession.getPid(), fSudo = True);
2765
2766 #
2767 # Pause the VM if we're going to take any screenshots or dig into the
2768 # guest. Failures are quitely ignored.
2769 #
2770 if self.fAlwaysUploadLogs or reporter.testErrorCount() > 0:
2771 try:
2772 if oSession.oVM.state in [ vboxcon.MachineState_Running,
2773 vboxcon.MachineState_LiveSnapshotting,
2774 vboxcon.MachineState_Teleporting ]:
2775 oSession.o.console.pause();
2776 except:
2777 reporter.logXcpt();
2778
2779 #
2780 # Take Screenshot and upload it (see below) to Test Manager if appropriate/requested.
2781 #
2782 if fTakeScreenshot is True or self.fAlwaysUploadScreenshots or reporter.testErrorCount() > 0:
2783 sLastScreenshotPath = os.path.join(self.sScratchPath, "LastScreenshot-%s.png" % oSession.sName);
2784 fRc = oSession.takeScreenshot(sLastScreenshotPath);
2785 if fRc is not True:
2786 sLastScreenshotPath = None;
2787
2788 # Query the OS kernel log from the debugger if appropriate/requested.
2789 if self.fAlwaysUploadLogs or reporter.testErrorCount() > 0:
2790 sOsKernelLog = oSession.queryOsKernelLog();
2791
2792 # Do "info vgatext all" separately.
2793 if self.fAlwaysUploadLogs or reporter.testErrorCount() > 0:
2794 sVgaText = oSession.queryDbgInfoVgaText();
2795
2796 # Various infos (do after kernel because of symbols).
2797 if self.fAlwaysUploadLogs or reporter.testErrorCount() > 0:
2798 # Dump the guest stack for all CPUs.
2799 cCpus = oSession.getCpuCount();
2800 if cCpus > 0:
2801 for iCpu in xrange(0, cCpus):
2802 sThis = oSession.queryDbgGuestStack(iCpu);
2803 if sThis:
2804 asMiscInfos += [
2805 '================ start guest stack VCPU %s ================\n' % (iCpu,),
2806 sThis,
2807 '================ end guest stack VCPU %s ==================\n' % (iCpu,),
2808 ];
2809
2810 for sInfo, sArg in [ ('mode', 'all'),
2811 ('fflags', ''),
2812 ('cpumguest', 'verbose all'),
2813 ('cpumguestinstr', 'symbol all'),
2814 ('pic', ''),
2815 ('apic', ''),
2816 ('apiclvt', ''),
2817 ('apictimer', ''),
2818 ('ioapic', ''),
2819 ('pit', ''),
2820 ('phys', ''),
2821 ('clocks', ''),
2822 ('timers', ''),
2823 ('gdtguest', ''),
2824 ('ldtguest', ''),
2825 ]:
2826 if sInfo in ['apic',] and self.fpApiVer < 5.1: # asserts and burns
2827 continue;
2828 sThis = oSession.queryDbgInfo(sInfo, sArg);
2829 if sThis:
2830 if sThis[-1] != '\n':
2831 sThis += '\n';
2832 asMiscInfos += [
2833 '================ start %s %s ================\n' % (sInfo, sArg),
2834 sThis,
2835 '================ end %s %s ==================\n' % (sInfo, sArg),
2836 ];
2837
2838 #
2839 # Terminate the VM
2840 #
2841
2842 # Cancel the progress object if specified.
2843 if oProgress is not None:
2844 if not oProgress.isCompleted() and oProgress.isCancelable():
2845 reporter.log2('terminateVmBySession: canceling "%s"...' % (oProgress.sName));
2846 try:
2847 oProgress.o.cancel();
2848 except:
2849 reporter.logXcpt();
2850 else:
2851 oProgress.wait();
2852 self.removeTask(oProgress);
2853
2854 # Check if the VM has terminated by itself before powering it off.
2855 fClose = True;
2856 fRc = True;
2857 if oSession.needsPoweringOff():
2858 reporter.log('terminateVmBySession: powering off "%s"...' % (oSession.sName,));
2859 fRc = oSession.powerOff(fFudgeOnFailure = False);
2860 if fRc is not True:
2861 # power off failed, try terminate it in a nice manner.
2862 fRc = False;
2863 uPid = oSession.getPid();
2864 if uPid is not None:
2865 #
2866 # Collect some information about the VM process first to have
2867 # some state information for further investigation why powering off failed.
2868 #
2869 sHostProcessInfoHung = utils.processGetInfo(uPid, fSudo = True);
2870
2871 # Exterminate...
2872 reporter.error('terminateVmBySession: Terminating PID %u (VM %s)' % (uPid, oSession.sName));
2873 fClose = base.processTerminate(uPid);
2874 if fClose is True:
2875 self.waitOnDirectSessionClose(oSession.oVM, 5000);
2876 fClose = oSession.waitForTask(1000);
2877
2878 if fClose is not True:
2879 # Being nice failed...
2880 reporter.error('terminateVmBySession: Termination failed, trying to kill PID %u (VM %s) instead' \
2881 % (uPid, oSession.sName));
2882 fClose = base.processKill(uPid);
2883 if fClose is True:
2884 self.waitOnDirectSessionClose(oSession.oVM, 5000);
2885 fClose = oSession.waitForTask(1000);
2886 if fClose is not True:
2887 reporter.error('terminateVmBySession: Failed to kill PID %u (VM %s)' % (uPid, oSession.sName));
2888
2889 # The final steps.
2890 if fClose is True:
2891 reporter.log('terminateVmBySession: closing session "%s"...' % (oSession.sName,));
2892 oSession.close();
2893 self.waitOnDirectSessionClose(oSession.oVM, 10000);
2894 try:
2895 eState = oSession.oVM.state;
2896 except:
2897 reporter.logXcpt();
2898 else:
2899 if eState == vboxcon.MachineState_Aborted:
2900 reporter.error('terminateVmBySession: The VM "%s" aborted!' % (oSession.sName,));
2901 self.removeTask(oSession);
2902
2903 #
2904 # Add the release log, debug log and a screenshot of the VM to the test report.
2905 #
2906 if self.fAlwaysUploadLogs or reporter.testErrorCount() > 0:
2907 oSession.addLogsToReport();
2908
2909 # Add a screenshot if it has been requested and taken successfully.
2910 if sLastScreenshotPath is not None:
2911 if reporter.testErrorCount() > 0:
2912 reporter.addLogFile(sLastScreenshotPath, 'screenshot/failure', 'Last VM screenshot');
2913 else:
2914 reporter.addLogFile(sLastScreenshotPath, 'screenshot/success', 'Last VM screenshot');
2915
2916 # Add the guest OS log if it has been requested and taken successfully.
2917 if sOsKernelLog is not None:
2918 reporter.addLogString(sOsKernelLog, 'kernel.log', 'log/guest/kernel', 'Guest OS kernel log');
2919
2920 # Add "info vgatext all" if we've got it.
2921 if sVgaText is not None:
2922 reporter.addLogString(sVgaText, 'vgatext.txt', 'info/vgatext', 'info vgatext all');
2923
2924 # Add the "info xxxx" items if we've got any.
2925 if asMiscInfos:
2926 reporter.addLogString(u''.join(asMiscInfos), 'info.txt', 'info/collection', 'A bunch of info items.');
2927
2928 # Add the host process info if we were able to retrieve it.
2929 if sHostProcessInfo is not None:
2930 reporter.log('Trying to annotate the VM process report, please stand by...');
2931 fRcTmp = self.annotateAndUploadProcessReport(sHostProcessInfo, 'vmprocess.log',
2932 'process/report/vm', 'Annotated VM process state');
2933 # Upload the raw log for manual annotation in case resolving failed.
2934 if not fRcTmp:
2935 reporter.log('Failed to annotate VM process report, uploading raw report');
2936 reporter.addLogString(sHostProcessInfo, 'vmprocess.log', 'process/report/vm', 'VM process state');
2937
2938 # Add the host process info for failed power off attempts if we were able to retrieve it.
2939 if sHostProcessInfoHung is not None:
2940 reporter.log('Trying to annotate the hung VM process report, please stand by...');
2941 fRcTmp = self.annotateAndUploadProcessReport(sHostProcessInfoHung, 'vmprocess-hung.log',
2942 'process/report/vm', 'Annotated hung VM process state');
2943 # Upload the raw log for manual annotation in case resolving failed.
2944 if not fRcTmp:
2945 reporter.log('Failed to annotate hung VM process report, uploading raw report');
2946 reporter.addLogString(sHostProcessInfoHung, 'vmprocess-hung.log', 'process/report/vm',
2947 'Hung VM process state');
2948
2949 return fRc;
2950
2951
2952 #
2953 # Some information query functions (mix).
2954 #
2955 # Methods require the VBox API. If the information is provided by both
2956 # the testboxscript as well as VBox API, we'll check if it matches.
2957 #
2958
2959 def _hasHostCpuFeature(self, sEnvVar, sEnum, fpApiMinVer, fQuiet):
2960 """
2961 Common Worker for hasHostNestedPaging() and hasHostHwVirt().
2962
2963 Returns True / False.
2964 Raises exception on environment / host mismatch.
2965 """
2966 fEnv = os.environ.get(sEnvVar, None);
2967 if fEnv is not None:
2968 fEnv = fEnv.lower() not in [ 'false', 'f', 'not', 'no', 'n', '0', ];
2969
2970 fVBox = None;
2971 self.importVBoxApi();
2972 if self.fpApiVer >= fpApiMinVer and hasattr(vboxcon, sEnum):
2973 try:
2974 fVBox = self.oVBox.host.getProcessorFeature(getattr(vboxcon, sEnum));
2975 except:
2976 if not fQuiet:
2977 reporter.logXcpt();
2978
2979 if fVBox is not None:
2980 if fEnv is not None:
2981 if fEnv != fVBox and not fQuiet:
2982 reporter.log('TestBox configuration overwritten: fVBox=%s (%s) vs. fEnv=%s (%s)'
2983 % (fVBox, sEnum, fEnv, sEnvVar));
2984 return fEnv;
2985 return fVBox;
2986 if fEnv is not None:
2987 return fEnv;
2988 return False;
2989
2990 def hasHostHwVirt(self, fQuiet = False):
2991 """
2992 Checks if hardware assisted virtualization is supported by the host.
2993
2994 Returns True / False.
2995 Raises exception on environment / host mismatch.
2996 """
2997 return self._hasHostCpuFeature('TESTBOX_HAS_HW_VIRT', 'ProcessorFeature_HWVirtEx', 3.1, fQuiet);
2998
2999 def hasHostNestedPaging(self, fQuiet = False):
3000 """
3001 Checks if nested paging is supported by the host.
3002
3003 Returns True / False.
3004 Raises exception on environment / host mismatch.
3005 """
3006 return self._hasHostCpuFeature('TESTBOX_HAS_NESTED_PAGING', 'ProcessorFeature_NestedPaging', 4.2, fQuiet) \
3007 and self.hasHostHwVirt(fQuiet);
3008
3009 def hasHostLongMode(self, fQuiet = False):
3010 """
3011 Checks if the host supports 64-bit guests.
3012
3013 Returns True / False.
3014 Raises exception on environment / host mismatch.
3015 """
3016 # Note that the testboxscript doesn't export this variable atm.
3017 return self._hasHostCpuFeature('TESTBOX_HAS_LONG_MODE', 'ProcessorFeature_LongMode', 3.1, fQuiet);
3018
3019 def getHostCpuCount(self, fQuiet = False):
3020 """
3021 Returns the number of CPUs on the host.
3022
3023 Returns True / False.
3024 Raises exception on environment / host mismatch.
3025 """
3026 cEnv = os.environ.get('TESTBOX_CPU_COUNT', None);
3027 if cEnv is not None:
3028 cEnv = int(cEnv);
3029
3030 try:
3031 cVBox = self.oVBox.host.processorOnlineCount;
3032 except:
3033 if not fQuiet:
3034 reporter.logXcpt();
3035 cVBox = None;
3036
3037 if cVBox is not None:
3038 if cEnv is not None:
3039 assert cVBox == cEnv, 'Misconfigured TestBox: VBox: %u CPUs, testboxscript: %u CPUs' % (cVBox, cEnv);
3040 return cVBox;
3041 if cEnv is not None:
3042 return cEnv;
3043 return 1;
3044
3045 def _getHostCpuDesc(self, fQuiet = False):
3046 """
3047 Internal method used for getting the host CPU description from VBoxSVC.
3048 Returns description string, on failure an empty string is returned.
3049 """
3050 try:
3051 return self.oVBox.host.getProcessorDescription(0);
3052 except:
3053 if not fQuiet:
3054 reporter.logXcpt();
3055 return '';
3056
3057 def isHostCpuAmd(self, fQuiet = False):
3058 """
3059 Checks if the host CPU vendor is AMD.
3060
3061 Returns True / False.
3062 """
3063 sCpuDesc = self._getHostCpuDesc(fQuiet);
3064 return sCpuDesc.startswith("AMD") or sCpuDesc == 'AuthenticAMD';
3065
3066 def isHostCpuIntel(self, fQuiet = False):
3067 """
3068 Checks if the host CPU vendor is Intel.
3069
3070 Returns True / False.
3071 """
3072 sCpuDesc = self._getHostCpuDesc(fQuiet);
3073 return sCpuDesc.startswith("Intel") or sCpuDesc == 'GenuineIntel';
3074
3075 def isHostCpuVia(self, fQuiet = False):
3076 """
3077 Checks if the host CPU vendor is VIA (or Centaur).
3078
3079 Returns True / False.
3080 """
3081 sCpuDesc = self._getHostCpuDesc(fQuiet);
3082 return sCpuDesc.startswith("VIA") or sCpuDesc == 'CentaurHauls';
3083
3084 def isHostCpuP4(self, fQuiet = False):
3085 """
3086 Checks if the host CPU is a Pentium 4 / Pentium D.
3087
3088 Returns True / False.
3089 """
3090 if not self.isHostCpuIntel(fQuiet):
3091 return False;
3092
3093 (uFamilyModel, _, _, _) = self.oVBox.host.getProcessorCPUIDLeaf(0, 0x1, 0);
3094 return ((uFamilyModel >> 8) & 0xf) == 0xf;
3095
3096 def hasRawModeSupport(self, fQuiet = False):
3097 """
3098 Checks if raw-mode is supported by VirtualBox that the testbox is
3099 configured for it.
3100
3101 Returns True / False.
3102 Raises no exceptions.
3103
3104 Note! Differs from the rest in that we don't require the
3105 TESTBOX_WITH_RAW_MODE value to match the API. It is
3106 sometimes helpful to disable raw-mode on individual
3107 test boxes. (This probably goes for
3108 """
3109 # The environment variable can be used to disable raw-mode.
3110 fEnv = os.environ.get('TESTBOX_WITH_RAW_MODE', None);
3111 if fEnv is not None:
3112 fEnv = fEnv.lower() not in [ 'false', 'f', 'not', 'no', 'n', '0', ];
3113 if fEnv is False:
3114 return False;
3115
3116 # Starting with 5.0 GA / RC2 the API can tell us whether VBox was built
3117 # with raw-mode support or not.
3118 self.importVBoxApi();
3119 if self.fpApiVer >= 5.0:
3120 try:
3121 fVBox = self.oVBox.systemProperties.rawModeSupported;
3122 except:
3123 if not fQuiet:
3124 reporter.logXcpt();
3125 fVBox = True;
3126 if fVBox is False:
3127 return False;
3128
3129 return True;
3130
3131 #
3132 # Testdriver execution methods.
3133 #
3134
3135 def handleTask(self, oTask, sMethod):
3136 """
3137 Callback method for handling unknown tasks in the various run loops.
3138
3139 The testdriver should override this if it already tasks running when
3140 calling startVmAndConnectToTxsViaTcp, txsRunTest or similar methods.
3141 Call super to handle unknown tasks.
3142
3143 Returns True if handled, False if not.
3144 """
3145 reporter.error('%s: unknown task %s' % (sMethod, oTask));
3146 return False;
3147
3148 def txsDoTask(self, oSession, oTxsSession, fnAsync, aArgs):
3149 """
3150 Generic TXS task wrapper which waits both on the TXS and the session tasks.
3151
3152 Returns False on error, logged.
3153
3154 Returns task result on success.
3155 """
3156 # All async methods ends with the following to args.
3157 cMsTimeout = aArgs[-2];
3158 fIgnoreErrors = aArgs[-1];
3159
3160 fRemoveVm = self.addTask(oSession);
3161 fRemoveTxs = self.addTask(oTxsSession);
3162
3163 rc = fnAsync(*aArgs); # pylint: disable=W0142
3164 if rc is True:
3165 rc = False;
3166 oTask = self.waitForTasks(cMsTimeout + 1);
3167 if oTask is oTxsSession:
3168 if oTxsSession.isSuccess():
3169 rc = oTxsSession.getResult();
3170 elif fIgnoreErrors is True:
3171 reporter.log( 'txsDoTask: task failed (%s)' % (oTxsSession.getLastReply()[1],));
3172 else:
3173 reporter.error('txsDoTask: task failed (%s)' % (oTxsSession.getLastReply()[1],));
3174 else:
3175 oTxsSession.cancelTask();
3176 if oTask is None:
3177 if fIgnoreErrors is True:
3178 reporter.log( 'txsDoTask: The task timed out.');
3179 else:
3180 reporter.errorTimeout('txsDoTask: The task timed out.');
3181 elif oTask is oSession:
3182 reporter.error('txsDoTask: The VM terminated unexpectedly');
3183 else:
3184 if fIgnoreErrors is True:
3185 reporter.log( 'txsDoTask: An unknown task %s was returned' % (oTask,));
3186 else:
3187 reporter.error('txsDoTask: An unknown task %s was returned' % (oTask,));
3188 else:
3189 reporter.error('txsDoTask: fnAsync returned %s' % (rc,));
3190
3191 if fRemoveTxs:
3192 self.removeTask(oTxsSession);
3193 if fRemoveVm:
3194 self.removeTask(oSession);
3195 return rc;
3196
3197 # pylint: disable=C0111
3198
3199 def txsDisconnect(self, oSession, oTxsSession, cMsTimeout = 30000, fIgnoreErrors = False):
3200 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncDisconnect,
3201 (self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3202
3203 def txsUuid(self, oSession, oTxsSession, cMsTimeout = 30000, fIgnoreErrors = False):
3204 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncUuid,
3205 (self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3206
3207 def txsMkDir(self, oSession, oTxsSession, sRemoteDir, fMode = 0o700, cMsTimeout = 30000, fIgnoreErrors = False):
3208 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncMkDir,
3209 (sRemoteDir, fMode, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3210
3211 def txsMkDirPath(self, oSession, oTxsSession, sRemoteDir, fMode = 0o700, cMsTimeout = 30000, fIgnoreErrors = False):
3212 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncMkDirPath,
3213 (sRemoteDir, fMode, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3214
3215 def txsMkSymlink(self, oSession, oTxsSession, sLinkTarget, sLink, cMsTimeout = 30000, fIgnoreErrors = False):
3216 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncMkSymlink,
3217 (sLinkTarget, sLink, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3218
3219 def txsRmDir(self, oSession, oTxsSession, sRemoteDir, cMsTimeout = 30000, fIgnoreErrors = False):
3220 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncRmDir,
3221 (sRemoteDir, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3222
3223 def txsRmFile(self, oSession, oTxsSession, sRemoteFile, cMsTimeout = 30000, fIgnoreErrors = False):
3224 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncRmFile,
3225 (sRemoteFile, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3226
3227 def txsRmSymlink(self, oSession, oTxsSession, sRemoteSymlink, cMsTimeout = 30000, fIgnoreErrors = False):
3228 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncRmSymlink,
3229 (sRemoteSymlink, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3230
3231 def txsRmTree(self, oSession, oTxsSession, sRemoteTree, cMsTimeout = 30000, fIgnoreErrors = False):
3232 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncRmTree,
3233 (sRemoteTree, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3234
3235 def txsIsDir(self, oSession, oTxsSession, sRemoteDir, cMsTimeout = 30000, fIgnoreErrors = False):
3236 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncIsDir,
3237 (sRemoteDir, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3238
3239 def txsIsFile(self, oSession, oTxsSession, sRemoteFile, cMsTimeout = 30000, fIgnoreErrors = False):
3240 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncIsFile,
3241 (sRemoteFile, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3242
3243 def txsIsSymlink(self, oSession, oTxsSession, sRemoteSymlink, cMsTimeout = 30000, fIgnoreErrors = False):
3244 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncIsSymlink,
3245 (sRemoteSymlink, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3246
3247 def txsUploadFile(self, oSession, oTxsSession, sLocalFile, sRemoteFile, cMsTimeout = 30000, fIgnoreErrors = False):
3248 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncUploadFile, \
3249 (sLocalFile, sRemoteFile, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3250
3251 def txsUploadString(self, oSession, oTxsSession, sContent, sRemoteFile, cMsTimeout = 30000, fIgnoreErrors = False):
3252 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncUploadString, \
3253 (sContent, sRemoteFile, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3254
3255 def txsDownloadFile(self, oSession, oTxsSession, sRemoteFile, sLocalFile, cMsTimeout = 30000, fIgnoreErrors = False):
3256 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncDownloadFile, \
3257 (sRemoteFile, sLocalFile, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3258
3259 def txsDownloadFiles(self, oSession, oTxsSession, asFiles, fIgnoreErrors = False):
3260 """
3261 Convenience function to get files from the guest and stores it
3262 into the scratch directory for later (manual) review.
3263
3264 Returns True on success.
3265
3266 Returns False on failure, logged.
3267 """
3268 fRc = True;
3269 for sGstFile in asFiles:
3270 ## @todo Check for already existing files on the host and create a new
3271 # name for the current file to download.
3272 sTmpFile = os.path.join(self.sScratchPath, 'tmp-' + os.path.basename(sGstFile));
3273 reporter.log2('Downloading file "%s" to "%s" ...' % (sGstFile, sTmpFile));
3274 fRc = self.txsDownloadFile(oSession, oTxsSession, sGstFile, sTmpFile, 30 * 1000, fIgnoreErrors);
3275 try: os.unlink(sTmpFile);
3276 except: pass;
3277 if fRc:
3278 reporter.addLogFile(sTmpFile, 'misc/other', 'guest - ' + sGstFile);
3279 else:
3280 if fIgnoreErrors is not True:
3281 reporter.error('error downloading file "%s" to "%s"' % (sGstFile, sTmpFile));
3282 return fRc;
3283 reporter.log('warning: file "%s" was not downloaded, ignoring.' % (sGstFile,));
3284 return True;
3285
3286 def txsDownloadString(self, oSession, oTxsSession, sRemoteFile, sEncoding = 'utf-8', fIgnoreEncodingErrors = True,
3287 cMsTimeout = 30000, fIgnoreErrors = False):
3288 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncDownloadString,
3289 (sRemoteFile, sEncoding, fIgnoreEncodingErrors, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3290
3291 def txsUnpackFile(self, oSession, oTxsSession, sRemoteFile, sRemoteDir, cMsTimeout = 30000, fIgnoreErrors = False):
3292 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncUnpackFile, \
3293 (sRemoteFile, sRemoteDir, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3294
3295 # pylint: enable=C0111
3296
3297 def txsCdWait(self,
3298 oSession, # type: vboxwrappers.SessionWrapper
3299 oTxsSession, # type: txsclient.Session
3300 cMsTimeout = 30000, # type: int
3301 sFile = None # type: String
3302 ): # -> bool
3303 """
3304 Mostly an internal helper for txsRebootAndReconnectViaTcp and
3305 startVmAndConnectToTxsViaTcp that waits for the CDROM drive to become
3306 ready. It does this by polling for a file it knows to exist on the CD.
3307
3308 Returns True on success.
3309
3310 Returns False on failure, logged.
3311 """
3312
3313 if sFile is None:
3314 sFile = 'valkit.txt';
3315 fRemoveVm = self.addTask(oSession);
3316 fRemoveTxs = self.addTask(oTxsSession);
3317 cMsTimeout = self.adjustTimeoutMs(cMsTimeout);
3318 msStart = base.timestampMilli();
3319 cMsTimeout2 = cMsTimeout;
3320 fRc = oTxsSession.asyncIsFile('${CDROM}/%s' % (sFile,), cMsTimeout2);
3321 if fRc is True:
3322 while True:
3323 # wait for it to complete.
3324 oTask = self.waitForTasks(cMsTimeout2 + 1);
3325 if oTask is not oTxsSession:
3326 oTxsSession.cancelTask();
3327 if oTask is None:
3328 reporter.errorTimeout('txsCdWait: The task timed out (after %s ms).'
3329 % (base.timestampMilli() - msStart,));
3330 elif oTask is oSession:
3331 reporter.error('txsCdWait: The VM terminated unexpectedly');
3332 else:
3333 reporter.error('txsCdWait: An unknown task %s was returned' % (oTask,));
3334 fRc = False;
3335 break;
3336 if oTxsSession.isSuccess():
3337 break;
3338
3339 # Check for timeout.
3340 cMsElapsed = base.timestampMilli() - msStart;
3341 if cMsElapsed >= cMsTimeout:
3342 reporter.error('txsCdWait: timed out');
3343 fRc = False;
3344 break;
3345
3346 # delay.
3347 self.sleep(1);
3348
3349 # resubmitt the task.
3350 cMsTimeout2 = msStart + cMsTimeout - base.timestampMilli();
3351 if cMsTimeout2 < 500:
3352 cMsTimeout2 = 500;
3353 fRc = oTxsSession.asyncIsFile('${CDROM}/%s' % (sFile,), cMsTimeout2);
3354 if fRc is not True:
3355 reporter.error('txsCdWait: asyncIsFile failed');
3356 break;
3357 else:
3358 reporter.error('txsCdWait: asyncIsFile failed');
3359
3360 if fRemoveTxs:
3361 self.removeTask(oTxsSession);
3362 if fRemoveVm:
3363 self.removeTask(oSession);
3364 return fRc;
3365
3366 def txsDoConnectViaTcp(self, oSession, cMsTimeout, fNatForwardingForTxs = False):
3367 """
3368 Mostly an internal worker for connecting to TXS via TCP used by the
3369 *ViaTcp methods.
3370
3371 Returns a tuplet with True/False and TxsSession/None depending on the
3372 result. Errors are logged.
3373 """
3374
3375 reporter.log2('txsDoConnectViaTcp: oSession=%s, cMsTimeout=%s, fNatForwardingForTxs=%s'
3376 % (oSession, cMsTimeout, fNatForwardingForTxs));
3377
3378 cMsTimeout = self.adjustTimeoutMs(cMsTimeout);
3379 oTxsConnect = oSession.txsConnectViaTcp(cMsTimeout, fNatForwardingForTxs = fNatForwardingForTxs);
3380 if oTxsConnect is not None:
3381 self.addTask(oTxsConnect);
3382 fRemoveVm = self.addTask(oSession);
3383 oTask = self.waitForTasks(cMsTimeout + 1);
3384 reporter.log2('txsDoConnectViaTcp: waitForTasks returned %s' % (oTask,));
3385 self.removeTask(oTxsConnect);
3386 if oTask is oTxsConnect:
3387 oTxsSession = oTxsConnect.getResult();
3388 if oTxsSession is not None:
3389 reporter.log('txsDoConnectViaTcp: Connected to TXS on %s.' % (oTxsSession.oTransport.sHostname,));
3390 return (True, oTxsSession);
3391
3392 reporter.error('txsDoConnectViaTcp: failed to connect to TXS.');
3393 else:
3394 oTxsConnect.cancelTask();
3395 if oTask is None:
3396 reporter.errorTimeout('txsDoConnectViaTcp: connect stage 1 timed out');
3397 elif oTask is oSession:
3398 oSession.reportPrematureTermination('txsDoConnectViaTcp: ');
3399 else:
3400 reporter.error('txsDoConnectViaTcp: unknown/wrong task %s' % (oTask,));
3401 if fRemoveVm:
3402 self.removeTask(oSession);
3403 else:
3404 reporter.error('txsDoConnectViaTcp: txsConnectViaTcp failed');
3405 return (False, None);
3406
3407 def startVmAndConnectToTxsViaTcp(self, sVmName, fCdWait = False, cMsTimeout = 15*60000, \
3408 cMsCdWait = 30000, sFileCdWait = None, \
3409 fNatForwardingForTxs = False):
3410 """
3411 Starts the specified VM and tries to connect to its TXS via TCP.
3412 The VM will be powered off if TXS doesn't respond before the specified
3413 time has elapsed.
3414
3415 Returns a the VM and TXS sessions (a two tuple) on success. The VM
3416 session is in the task list, the TXS session is not.
3417 Returns (None, None) on failure, fully logged.
3418 """
3419
3420 # Zap the guest IP to make sure we're not getting a stale entry
3421 # (unless we're restoring the VM of course).
3422 oTestVM = self.oTestVmSet.findTestVmByName(sVmName) if self.oTestVmSet is not None else None;
3423 if oTestVM is None \
3424 or oTestVM.fSnapshotRestoreCurrent is False:
3425 try:
3426 oSession1 = self.openSession(self.getVmByName(sVmName));
3427 oSession1.delGuestPropertyValue('/VirtualBox/GuestInfo/Net/0/V4/IP');
3428 oSession1.saveSettings(True);
3429 del oSession1;
3430 except:
3431 reporter.logXcpt();
3432
3433 # Start the VM.
3434 reporter.log('startVmAndConnectToTxsViaTcp: Starting(/preparing) "%s" (timeout %s s)...' % (sVmName, cMsTimeout / 1000));
3435 reporter.flushall();
3436 oSession = self.startVmByName(sVmName);
3437 if oSession is not None:
3438 # Connect to TXS.
3439 reporter.log2('startVmAndConnectToTxsViaTcp: Started(/prepared) "%s", connecting to TXS ...' % (sVmName,));
3440 (fRc, oTxsSession) = self.txsDoConnectViaTcp(oSession, cMsTimeout, fNatForwardingForTxs);
3441 if fRc is True:
3442 if fCdWait:
3443 # Wait for CD?
3444 fRc = self.txsCdWait(oSession, oTxsSession, cMsCdWait, sFileCdWait);
3445 if fRc is not True:
3446 reporter.error('startVmAndConnectToTxsViaTcp: txsCdWait failed');
3447 if fRc is True:
3448 # Success!
3449 return (oSession, oTxsSession);
3450 else:
3451 reporter.error('startVmAndConnectToTxsViaTcp: txsDoConnectViaTcp failed');
3452 # If something went wrong while waiting for TXS to be started - take VM screenshot before terminate it
3453 self.terminateVmBySession(oSession);
3454 return (None, None);
3455
3456 def txsRebootAndReconnectViaTcp(self, oSession, oTxsSession, fCdWait = False, cMsTimeout = 15*60000, \
3457 cMsCdWait = 30000, sFileCdWait = None, fNatForwardingForTxs = False):
3458 """
3459 Executes the TXS reboot command
3460
3461 Returns A tuple of True and the new TXS session on success.
3462
3463 Returns A tuple of False and either the old TXS session or None on failure.
3464 """
3465 reporter.log2('txsRebootAndReconnect: cMsTimeout=%u' % (cMsTimeout,));
3466
3467 #
3468 # This stuff is a bit complicated because of rebooting being kind of
3469 # disruptive to the TXS and such... The protocol is that TXS will:
3470 # - ACK the reboot command.
3471 # - Shutdown the transport layer, implicitly disconnecting us.
3472 # - Execute the reboot operation.
3473 # - On failure, it will be re-init the transport layer and be
3474 # available pretty much immediately. UUID unchanged.
3475 # - On success, it will be respawed after the reboot (hopefully),
3476 # with a different UUID.
3477 #
3478 fRc = False;
3479 iStart = base.timestampMilli();
3480
3481 # Get UUID.
3482 cMsTimeout2 = min(60000, cMsTimeout);
3483 sUuidBefore = self.txsUuid(oSession, oTxsSession, self.adjustTimeoutMs(cMsTimeout2, 60000));
3484 if sUuidBefore is not False:
3485 # Reboot.
3486 cMsElapsed = base.timestampMilli() - iStart;
3487 cMsTimeout2 = cMsTimeout - cMsElapsed;
3488 fRc = self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncReboot,
3489 (self.adjustTimeoutMs(cMsTimeout2, 60000), False));
3490 if fRc is True:
3491 # Reconnect.
3492 if fNatForwardingForTxs is True:
3493 self.sleep(22); # NAT fudge - Two fixes are wanted: 1. TXS connect retries. 2. Main API reboot/reset hint.
3494 cMsElapsed = base.timestampMilli() - iStart;
3495 (fRc, oTxsSession) = self.txsDoConnectViaTcp(oSession, cMsTimeout - cMsElapsed, fNatForwardingForTxs);
3496 if fRc is True:
3497 # Check the UUID.
3498 cMsElapsed = base.timestampMilli() - iStart;
3499 cMsTimeout2 = min(60000, cMsTimeout - cMsElapsed);
3500 sUuidAfter = self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncUuid,
3501 (self.adjustTimeoutMs(cMsTimeout2, 60000), False));
3502 if sUuidBefore is not False:
3503 if sUuidAfter != sUuidBefore:
3504 reporter.log('The guest rebooted (UUID %s -> %s)' % (sUuidBefore, sUuidAfter))
3505
3506 # Do CD wait if specified.
3507 if fCdWait:
3508 fRc = self.txsCdWait(oSession, oTxsSession, cMsCdWait, sFileCdWait);
3509 if fRc is not True:
3510 reporter.error('txsRebootAndReconnectViaTcp: txsCdWait failed');
3511 else:
3512 reporter.error('txsRebootAndReconnectViaTcp: failed to get UUID (after)');
3513 else:
3514 reporter.error('txsRebootAndReconnectViaTcp: did not reboot (UUID %s)' % (sUuidBefore,));
3515 else:
3516 reporter.error('txsRebootAndReconnectViaTcp: txsDoConnectViaTcp failed');
3517 else:
3518 reporter.error('txsRebootAndReconnectViaTcp: reboot failed');
3519 else:
3520 reporter.error('txsRebootAndReconnectViaTcp: failed to get UUID (before)');
3521 return (fRc, oTxsSession);
3522
3523 # pylint: disable=R0914,R0913
3524
3525 def txsRunTest(self, oTxsSession, sTestName, cMsTimeout, sExecName, asArgs = (), asAddEnv = (), sAsUser = ""):
3526 """
3527 Executes the specified test task, waiting till it completes or times out.
3528
3529 The VM session (if any) must be in the task list.
3530
3531 Returns True if we executed the task and nothing abnormal happend.
3532 Query the process status from the TXS session.
3533
3534 Returns False if some unexpected task was signalled or we failed to
3535 submit the job.
3536 """
3537 reporter.testStart(sTestName);
3538 reporter.log2('txsRunTest: cMsTimeout=%u sExecName=%s asArgs=%s' % (cMsTimeout, sExecName, asArgs));
3539
3540 # Submit the job.
3541 fRc = False;
3542 if oTxsSession.asyncExec(sExecName, asArgs, asAddEnv, sAsUser, cMsTimeout = self.adjustTimeoutMs(cMsTimeout)):
3543 self.addTask(oTxsSession);
3544
3545 # Wait for the job to complete.
3546 while True:
3547 oTask = self.waitForTasks(cMsTimeout + 1);
3548 if oTask is None:
3549 reporter.log('txsRunTest: waitForTasks timed out');
3550 break;
3551 if oTask is oTxsSession:
3552 fRc = True;
3553 reporter.log('txsRunTest: isSuccess=%s getResult=%s' % (oTxsSession.isSuccess(), oTxsSession.getResult()));
3554 break;
3555 if not self.handleTask(oTask, 'txsRunTest'):
3556 break;
3557
3558 self.removeTask(oTxsSession);
3559 if not oTxsSession.pollTask():
3560 oTxsSession.cancelTask();
3561 else:
3562 reporter.error('txsRunTest: asyncExec failed');
3563
3564 reporter.testDone();
3565 return fRc;
3566
3567 def txsRunTestRedirectStd(self, oTxsSession, sTestName, cMsTimeout, sExecName, asArgs = (), asAddEnv = (), sAsUser = "",
3568 oStdIn = '/dev/null', oStdOut = '/dev/null', oStdErr = '/dev/null', oTestPipe = '/dev/null'):
3569 """
3570 Executes the specified test task, waiting till it completes or times out,
3571 redirecting stdin, stdout and stderr to the given objects.
3572
3573 The VM session (if any) must be in the task list.
3574
3575 Returns True if we executed the task and nothing abnormal happend.
3576 Query the process status from the TXS session.
3577
3578 Returns False if some unexpected task was signalled or we failed to
3579 submit the job.
3580 """
3581 reporter.testStart(sTestName);
3582 reporter.log2('txsRunTestRedirectStd: cMsTimeout=%u sExecName=%s asArgs=%s' % (cMsTimeout, sExecName, asArgs));
3583
3584 # Submit the job.
3585 fRc = False;
3586 if oTxsSession.asyncExecEx(sExecName, asArgs, asAddEnv, oStdIn, oStdOut, oStdErr,
3587 oTestPipe, sAsUser, cMsTimeout = self.adjustTimeoutMs(cMsTimeout)):
3588 self.addTask(oTxsSession);
3589
3590 # Wait for the job to complete.
3591 while True:
3592 oTask = self.waitForTasks(cMsTimeout + 1);
3593 if oTask is None:
3594 reporter.log('txsRunTestRedirectStd: waitForTasks timed out');
3595 break;
3596 if oTask is oTxsSession:
3597 fRc = True;
3598 reporter.log('txsRunTestRedirectStd: isSuccess=%s getResult=%s'
3599 % (oTxsSession.isSuccess(), oTxsSession.getResult()));
3600 break;
3601 if not self.handleTask(oTask, 'txsRunTestRedirectStd'):
3602 break;
3603
3604 self.removeTask(oTxsSession);
3605 if not oTxsSession.pollTask():
3606 oTxsSession.cancelTask();
3607 else:
3608 reporter.error('txsRunTestRedirectStd: asyncExec failed');
3609
3610 reporter.testDone();
3611 return fRc;
3612
3613 def txsRunTest2(self, oTxsSession1, oTxsSession2, sTestName, cMsTimeout,
3614 sExecName1, asArgs1,
3615 sExecName2, asArgs2,
3616 asAddEnv1 = (), sAsUser1 = '', fWithTestPipe1 = True,
3617 asAddEnv2 = (), sAsUser2 = '', fWithTestPipe2 = True):
3618 """
3619 Executes the specified test tasks, waiting till they complete or
3620 times out. The 1st task is started after the 2nd one.
3621
3622 The VM session (if any) must be in the task list.
3623
3624 Returns True if we executed the task and nothing abnormal happend.
3625 Query the process status from the TXS sessions.
3626
3627 Returns False if some unexpected task was signalled or we failed to
3628 submit the job.
3629 """
3630 reporter.testStart(sTestName);
3631
3632 # Submit the jobs.
3633 fRc = False;
3634 if oTxsSession1.asyncExec(sExecName1, asArgs1, asAddEnv1, sAsUser1, fWithTestPipe1, '1-',
3635 self.adjustTimeoutMs(cMsTimeout)):
3636 self.addTask(oTxsSession1);
3637
3638 self.sleep(2); # fudge! grr
3639
3640 if oTxsSession2.asyncExec(sExecName2, asArgs2, asAddEnv2, sAsUser2, fWithTestPipe2, '2-',
3641 self.adjustTimeoutMs(cMsTimeout)):
3642 self.addTask(oTxsSession2);
3643
3644 # Wait for the jobs to complete.
3645 cPendingJobs = 2;
3646 while True:
3647 oTask = self.waitForTasks(cMsTimeout + 1);
3648 if oTask is None:
3649 reporter.log('txsRunTest2: waitForTasks timed out');
3650 break;
3651
3652 if oTask is oTxsSession1 or oTask is oTxsSession2:
3653 if oTask is oTxsSession1: iTask = 1;
3654 else: iTask = 2;
3655 reporter.log('txsRunTest2: #%u - isSuccess=%s getResult=%s' \
3656 % (iTask, oTask.isSuccess(), oTask.getResult()));
3657 self.removeTask(oTask);
3658 cPendingJobs -= 1;
3659 if cPendingJobs <= 0:
3660 fRc = True;
3661 break;
3662
3663 elif not self.handleTask(oTask, 'txsRunTest'):
3664 break;
3665
3666 self.removeTask(oTxsSession2);
3667 if not oTxsSession2.pollTask():
3668 oTxsSession2.cancelTask();
3669 else:
3670 reporter.error('txsRunTest2: asyncExec #2 failed');
3671
3672 self.removeTask(oTxsSession1);
3673 if not oTxsSession1.pollTask():
3674 oTxsSession1.cancelTask();
3675 else:
3676 reporter.error('txsRunTest2: asyncExec #1 failed');
3677
3678 reporter.testDone();
3679 return fRc;
3680
3681 # pylint: enable=R0914,R0913
3682
3683
3684 #
3685 # Working with test results via serial port.
3686 #
3687
3688 class TxsMonitorComFile(base.TdTaskBase):
3689 """
3690 Class that monitors a COM output file.
3691 """
3692
3693 def __init__(self, sComRawFile, asStopWords = None):
3694 base.TdTaskBase.__init__(self, utils.getCallerName());
3695 self.sComRawFile = sComRawFile;
3696 self.oStopRegExp = re.compile('\\b(' + '|'.join(asStopWords if asStopWords else ('PASSED', 'FAILED',)) + ')\\b');
3697 self.sResult = None; ##< The result.
3698 self.cchDisplayed = 0; ##< Offset into the file string of what we've already fed to the logger.
3699
3700 def toString(self):
3701 return '<%s sComRawFile=%s oStopRegExp=%s sResult=%s cchDisplayed=%s>' \
3702 % (base.TdTaskBase.toString(self), self.sComRawFile, self.oStopRegExp, self.sResult, self.cchDisplayed,);
3703
3704 def pollTask(self, fLocked = False):
3705 """
3706 Overrides TdTaskBase.pollTask() for the purpose of polling the file.
3707 """
3708 if not fLocked:
3709 self.lockTask();
3710
3711 sFile = utils.noxcptReadFile(self.sComRawFile, '', 'rU');
3712 if len(sFile) > self.cchDisplayed:
3713 sNew = sFile[self.cchDisplayed:];
3714 oMatch = self.oStopRegExp.search(sNew);
3715 if oMatch:
3716 # Done! Get result, flush all the output and signal the task.
3717 self.sResult = oMatch.group(1);
3718 for sLine in sNew.split('\n'):
3719 reporter.log('COM OUTPUT: %s' % (sLine,));
3720 self.cchDisplayed = len(sFile);
3721 self.signalTaskLocked();
3722 else:
3723 # Output whole lines only.
3724 offNewline = sFile.find('\n', self.cchDisplayed);
3725 while offNewline >= 0:
3726 reporter.log('COM OUTPUT: %s' % (sFile[self.cchDisplayed:offNewline]))
3727 self.cchDisplayed = offNewline + 1;
3728 offNewline = sFile.find('\n', self.cchDisplayed);
3729
3730 fRet = self.fSignalled;
3731 if not fLocked:
3732 self.unlockTask();
3733 return fRet;
3734
3735 # Our stuff.
3736 def getResult(self):
3737 """
3738 Returns the connected TXS session object on success.
3739 Returns None on failure or if the task has not yet completed.
3740 """
3741 self.oCv.acquire();
3742 sResult = self.sResult;
3743 self.oCv.release();
3744 return sResult;
3745
3746 def cancelTask(self):
3747 """ Cancels the task. """
3748 self.signalTask();
3749 return True;
3750
3751
3752 def monitorComRawFile(self, oSession, sComRawFile, cMsTimeout = 15*60000, asStopWords = None):
3753 """
3754 Monitors the COM output file for stop words (PASSED and FAILED by default).
3755
3756 Returns the stop word.
3757 Returns None on VM error and timeout.
3758 """
3759
3760 reporter.log2('monitorComRawFile: oSession=%s, cMsTimeout=%s, sComRawFile=%s' % (oSession, cMsTimeout, sComRawFile));
3761
3762 oMonitorTask = self.TxsMonitorComFile(sComRawFile, asStopWords);
3763 self.addTask(oMonitorTask);
3764
3765 cMsTimeout = self.adjustTimeoutMs(cMsTimeout);
3766 oTask = self.waitForTasks(cMsTimeout + 1);
3767 reporter.log2('monitorComRawFile: waitForTasks returned %s' % (oTask,));
3768
3769 if oTask is not oMonitorTask:
3770 oMonitorTask.cancelTask();
3771 self.removeTask(oMonitorTask);
3772
3773 oMonitorTask.pollTask();
3774 return oMonitorTask.getResult();
3775
3776
3777 def runVmAndMonitorComRawFile(self, sVmName, sComRawFile, cMsTimeout = 15*60000, asStopWords = None):
3778 """
3779 Runs the specified VM and monitors the given COM output file for stop
3780 words (PASSED and FAILED by default).
3781
3782 The caller is assumed to have configured the VM to use the given
3783 file. The method will take no action to verify this.
3784
3785 Returns the stop word.
3786 Returns None on VM error and timeout.
3787 """
3788
3789 # Start the VM.
3790 reporter.log('runVmAndMonitorComRawFile: Starting(/preparing) "%s" (timeout %s s)...' % (sVmName, cMsTimeout / 1000));
3791 reporter.flushall();
3792 oSession = self.startVmByName(sVmName);
3793 if oSession is not None:
3794 # Let it run and then terminate it.
3795 sRet = self.monitorComRawFile(oSession, sComRawFile, cMsTimeout, asStopWords);
3796 self.terminateVmBySession(oSession);
3797 else:
3798 sRet = None;
3799 return sRet;
3800
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