VirtualBox

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

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

ValidationKit/testdriver: Added apiclvt and apictimers to the info dumps.

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