VirtualBox

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

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

testdriver/vbox.py: Display COM stats after dropping and destroying VBoxMgr.

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

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