VirtualBox

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

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

ValidationKit: More python 3 adjustments.

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

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