VirtualBox

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

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

Validation Kit/testdriver: Fixed downloading guest files via txsService -- do the temporary file truncation before the download, not after.

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette