VirtualBox

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

Last change on this file since 79066 was 79056, checked in by vboxsync, 6 years ago

ValKit/tdGuestOsUnattendedInst1: Added option for installing guest additions and implemented waiting for them to come online. bugref:9151

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