VirtualBox

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

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

testdriver: More python 3 adjustments.

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

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