VirtualBox

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

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

testdriver/vbox.py: stupid stupid windows installer putting vboxapi under sdk/install instead of sdk/install. sigh

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