VirtualBox

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

Last change on this file since 65335 was 65335, checked in by vboxsync, 8 years ago

testmanager,testdriver: Adjustments of process info dumping. Don't do it if we're low on memory (gdb uses lots of memory!). Changed the kind from 'log/host/vmprocess' to 'process/report/vm' to better fit with the rest. processGetInfo needs a parameter indicating whether or not to try sudo the operations.

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