VirtualBox

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

Last change on this file since 67022 was 67022, checked in by vboxsync, 8 years ago

comment typo

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