VirtualBox

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

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

vbox.py: Removed the two extra CoInitialize calls since 5.1 vboxapi has been updated to not uninitialize now.

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