VirtualBox

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

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

5.2 Beta 1

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 155.4 KB
Line 
1# -*- coding: utf-8 -*-
2# $Id: vbox.py 68290 2017-08-03 16:24:55Z 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.virtualbox.org. This file is free software;
15you can redistribute it and/or modify it under the terms of the GNU
16General Public License (GPL) as published by the Free Software
17Foundation, in version 2 as it comes in the "COPYING" file of the
18VirtualBox OSE distribution. VirtualBox OSE is distributed in the
19hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
20
21The contents of this file may alternatively be used under the terms
22of the Common Development and Distribution License Version 1.0
23(CDDL) only, as it comes in the "COPYING.CDDL" file of the
24VirtualBox OSE distribution, in which case the provisions of the
25CDDL are applicable instead of those of the GPL.
26
27You may elect to license modified versions of this file under the
28terms and conditions of either the GPL or the CDDL or both.
29"""
30__version__ = "$Revision: 68290 $"
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.2"):
1298 self.fpApiVer = 5.2;
1299 elif sVer.startswith("5.1"):
1300 self.fpApiVer = 5.1;
1301 elif sVer.startswith("5.0") or (sVer.startswith("4.3.5") and len(sVer) == 6):
1302 self.fpApiVer = 5.0;
1303 elif sVer.startswith("4.3") or (sVer.startswith("4.2.5") and len(sVer) == 6):
1304 self.fpApiVer = 4.3;
1305 elif sVer.startswith("4.2."):
1306 self.fpApiVer = 4.2; ## @todo Fudge: Add (proper) 4.2 API support. Unmount medium etc?
1307 elif sVer.startswith("4.1.") or (sVer.startswith("4.0.5") and len(sVer) == 6):
1308 self.fpApiVer = 4.1;
1309 elif sVer.startswith("4.0."):
1310 self.fpApiVer = 4.0;
1311 elif sVer.startswith("3.2."):
1312 self.fpApiVer = 3.2;
1313 elif sVer.startswith("3.1."):
1314 self.fpApiVer = 3.1;
1315 elif sVer.startswith("3.0."):
1316 self.fpApiVer = 3.0;
1317 else:
1318 raise base.GenError('Unknown version "%s"' % (sVer,));
1319
1320 self._patchVBoxMgr();
1321
1322 from testdriver.vboxwrappers import VirtualBoxWrapper;
1323 self.oVBox = VirtualBoxWrapper(oVBox, self.oVBoxMgr, self.fpApiVer, self);
1324 vboxcon.goHackModuleClass.oVBoxMgr = self.oVBoxMgr; # VBoxConstantWrappingHack.
1325 vboxcon.fpApiVer = self.fpApiVer
1326 self.fImportedVBoxApi = True;
1327 reporter.log('Found version %s (%s)' % (self.fpApiVer, sVer));
1328 except:
1329 self.oVBoxMgr = None;
1330 self.oVBox = None;
1331 reporter.logXcpt("getVirtualBox exception");
1332 return False;
1333 return True;
1334
1335 def _patchVBoxMgr(self):
1336 """
1337 Glosses over missing self.oVBoxMgr methods on older VBox versions.
1338 """
1339
1340 def _xcptGetResult(oSelf, oXcpt = None):
1341 """ See vboxapi. """
1342 _ = oSelf;
1343 if oXcpt is None: oXcpt = sys.exc_info()[1];
1344 if sys.platform == 'win32':
1345 import winerror; # pylint: disable=F0401
1346 hrXcpt = oXcpt.hresult;
1347 if hrXcpt == winerror.DISP_E_EXCEPTION:
1348 hrXcpt = oXcpt.excepinfo[5];
1349 else:
1350 hrXcpt = oXcpt.error;
1351 return hrXcpt;
1352
1353 def _xcptIsDeadInterface(oSelf, oXcpt = None):
1354 """ See vboxapi. """
1355 return oSelf.xcptGetStatus(oXcpt) in [
1356 0x80004004, -2147467260, # NS_ERROR_ABORT
1357 0x800706be, -2147023170, # NS_ERROR_CALL_FAILED (RPC_S_CALL_FAILED)
1358 0x800706ba, -2147023174, # RPC_S_SERVER_UNAVAILABLE.
1359 0x800706be, -2147023170, # RPC_S_CALL_FAILED.
1360 0x800706bf, -2147023169, # RPC_S_CALL_FAILED_DNE.
1361 0x80010108, -2147417848, # RPC_E_DISCONNECTED.
1362 0x800706b5, -2147023179, # RPC_S_UNKNOWN_IF
1363 ];
1364
1365 def _xcptIsOurXcptKind(oSelf, oXcpt = None):
1366 """ See vboxapi. """
1367 _ = oSelf;
1368 if oXcpt is None: oXcpt = sys.exc_info()[1];
1369 if sys.platform == 'win32':
1370 from pythoncom import com_error as NativeComExceptionClass # pylint: disable=F0401,E0611
1371 else:
1372 from xpcom import Exception as NativeComExceptionClass # pylint: disable=F0401
1373 return isinstance(oXcpt, NativeComExceptionClass);
1374
1375 def _xcptIsEqual(oSelf, oXcpt, hrStatus):
1376 """ See vboxapi. """
1377 hrXcpt = oSelf.xcptGetResult(oXcpt);
1378 return hrXcpt == hrStatus or hrXcpt == hrStatus - 0x100000000;
1379
1380 def _xcptToString(oSelf, oXcpt):
1381 """ See vboxapi. """
1382 _ = oSelf;
1383 if oXcpt is None: oXcpt = sys.exc_info()[1];
1384 return str(oXcpt);
1385
1386 # Add utilities found in newer vboxapi revision.
1387 if not hasattr(self.oVBoxMgr, 'xcptIsDeadInterface'):
1388 import types;
1389 self.oVBoxMgr.xcptGetResult = types.MethodType(_xcptGetResult, self.oVBoxMgr);
1390 self.oVBoxMgr.xcptIsDeadInterface = types.MethodType(_xcptIsDeadInterface, self.oVBoxMgr);
1391 self.oVBoxMgr.xcptIsOurXcptKind = types.MethodType(_xcptIsOurXcptKind, self.oVBoxMgr);
1392 self.oVBoxMgr.xcptIsEqual = types.MethodType(_xcptIsEqual, self.oVBoxMgr);
1393 self.oVBoxMgr.xcptToString = types.MethodType(_xcptToString, self.oVBoxMgr);
1394
1395
1396 def _teardownVBoxApi(self):
1397 """
1398 Drop all VBox object references and shutdown com/xpcom.
1399 """
1400 if not self.fImportedVBoxApi:
1401 return True;
1402
1403 self.aoRemoteSessions = [];
1404 self.aoVMs = [];
1405 self.oVBoxMgr = None;
1406 self.oVBox = None;
1407 vboxcon.goHackModuleClass.oVBoxMgr = None; # VBoxConstantWrappingHack.
1408
1409 try:
1410 import gc
1411 gc.collect();
1412 objects = gc.get_objects()
1413 try:
1414 try:
1415 from types import InstanceType
1416 except ImportError:
1417 InstanceType = None # Python 3.x compatibility
1418 for o in objects:
1419 objtype = type(o)
1420 if objtype == InstanceType: # Python 2.x codepath
1421 objtype = o.__class__
1422 if objtype.__name__ == 'VirtualBoxManager':
1423 reporter.log('actionCleanupAfter: CAUTION, there is still a VirtualBoxManager object, GC trouble')
1424 break
1425 finally:
1426 del objects
1427 except:
1428 reporter.logXcpt();
1429 self.fImportedVBoxApi = False;
1430
1431 if self.sHost == 'win':
1432 pass; ## TODO shutdown COM if possible/necessary?
1433 else:
1434 try:
1435 from xpcom import _xpcom as _xpcom; # pylint: disable=F0401
1436 hrc = _xpcom.NS_ShutdownXPCOM();
1437 cIfs = _xpcom._GetInterfaceCount(); # pylint: disable=W0212
1438 cObjs = _xpcom._GetGatewayCount(); # pylint: disable=W0212
1439 if cObjs == 0 and cIfs == 0:
1440 reporter.log('actionCleanupAfter: NS_ShutdownXPCOM -> %s, nothing left behind.' % (hrc, ));
1441 else:
1442 reporter.log('actionCleanupAfter: NS_ShutdownXPCOM -> %s, leaving %s objects and %s interfaces behind...' \
1443 % (hrc, cObjs, cIfs));
1444 if hasattr(_xpcom, '_DumpInterfaces'):
1445 try:
1446 _xpcom._DumpInterfaces(); # pylint: disable=W0212
1447 except:
1448 reporter.logXcpt('actionCleanupAfter: _DumpInterfaces failed');
1449 except:
1450 reporter.logXcpt();
1451
1452 try:
1453 gc.collect();
1454 time.sleep(0.5); # fudge factory
1455 except:
1456 reporter.logXcpt();
1457 return True;
1458
1459 def _powerOffAllVms(self):
1460 """
1461 Tries to power off all running VMs.
1462 """
1463 for oSession in self.aoRemoteSessions:
1464 uPid = oSession.getPid();
1465 if uPid is not None:
1466 reporter.log('_powerOffAllVms: PID is %s for %s, trying to kill it.' % (uPid, oSession.sName,));
1467 base.processKill(uPid);
1468 else:
1469 reporter.log('_powerOffAllVms: No PID for %s' % (oSession.sName,));
1470 oSession.close();
1471 return None;
1472
1473
1474
1475 #
1476 # Build type, OS and arch getters.
1477 #
1478
1479 def getBuildType(self):
1480 """
1481 Get the build type.
1482 """
1483 if not self._detectBuild():
1484 return 'release';
1485 return self.oBuild.sType;
1486
1487 def getBuildOs(self):
1488 """
1489 Get the build OS.
1490 """
1491 if not self._detectBuild():
1492 return self.sHost;
1493 return self.oBuild.sOs;
1494
1495 def getBuildArch(self):
1496 """
1497 Get the build arch.
1498 """
1499 if not self._detectBuild():
1500 return self.sHostArch;
1501 return self.oBuild.sArch;
1502
1503 def getGuestAdditionsIso(self):
1504 """
1505 Get the path to the guest addition iso.
1506 """
1507 if not self._detectBuild():
1508 return None;
1509 return self.oBuild.sGuestAdditionsIso;
1510
1511 #
1512 # Override everything from the base class so the testdrivers don't have to
1513 # check whether we have overridden a method or not.
1514 #
1515
1516 def showUsage(self):
1517 rc = base.TestDriver.showUsage(self);
1518 reporter.log('');
1519 reporter.log('Generic VirtualBox Options:');
1520 reporter.log(' --vbox-session-type <type>');
1521 reporter.log(' Sets the session type. Typical values are: gui, headless, sdl');
1522 reporter.log(' Default: %s' % (self.sSessionTypeDef));
1523 reporter.log(' --vrdp, --no-vrdp');
1524 reporter.log(' Enables VRDP, ports starting at 6000');
1525 reporter.log(' Default: --vrdp');
1526 reporter.log(' --vrdp-base-port <port>');
1527 reporter.log(' Sets the base for VRDP port assignments.');
1528 reporter.log(' Default: %s' % (self.uVrdpBasePortDef));
1529 reporter.log(' --vbox-default-bridged-nic <interface>');
1530 reporter.log(' Sets the default interface for bridged networking.');
1531 reporter.log(' Default: autodetect');
1532 reporter.log(' --vbox-use-svc-defaults');
1533 reporter.log(' Use default locations and files for VBoxSVC. This is useful');
1534 reporter.log(' for automatically configuring the test VMs for debugging.');
1535 reporter.log(' --vbox-self-log');
1536 reporter.log(' The VBox logger group settings for the testdriver.');
1537 reporter.log(' --vbox-self-log-flags');
1538 reporter.log(' The VBox logger flags settings for the testdriver.');
1539 reporter.log(' --vbox-self-log-dest');
1540 reporter.log(' The VBox logger destination settings for the testdriver.');
1541 reporter.log(' --vbox-session-log');
1542 reporter.log(' The VM session logger group settings.');
1543 reporter.log(' --vbox-session-log-flags');
1544 reporter.log(' The VM session logger flags.');
1545 reporter.log(' --vbox-session-log-dest');
1546 reporter.log(' The VM session logger destination settings.');
1547 reporter.log(' --vbox-svc-log');
1548 reporter.log(' The VBoxSVC logger group settings.');
1549 reporter.log(' --vbox-svc-log-flags');
1550 reporter.log(' The VBoxSVC logger flag settings.');
1551 reporter.log(' --vbox-svc-log-dest');
1552 reporter.log(' The VBoxSVC logger destination settings.');
1553 reporter.log(' --vbox-log');
1554 reporter.log(' The VBox logger group settings for everyone.');
1555 reporter.log(' --vbox-log-flags');
1556 reporter.log(' The VBox logger flags settings for everyone.');
1557 reporter.log(' --vbox-log-dest');
1558 reporter.log(' The VBox logger destination settings for everyone.');
1559 reporter.log(' --vbox-svc-debug');
1560 reporter.log(' Start VBoxSVC in a debugger');
1561 reporter.log(' --vbox-always-upload-logs');
1562 reporter.log(' Whether to always upload log files, or only do so on failure.');
1563 reporter.log(' --vbox-always-upload-screenshots');
1564 reporter.log(' Whether to always upload final screen shots, or only do so on failure.');
1565 reporter.log(' --vbox-debugger, --no-vbox-debugger');
1566 reporter.log(' Enables the VBox debugger, port at 5000');
1567 reporter.log(' Default: --vbox-debugger');
1568 if self.oTestVmSet is not None:
1569 self.oTestVmSet.showUsage();
1570 return rc;
1571
1572 def parseOption(self, asArgs, iArg): # pylint: disable=R0915
1573 if asArgs[iArg] == '--vbox-session-type':
1574 iArg += 1;
1575 if iArg >= len(asArgs):
1576 raise base.InvalidOption('The "--vbox-session-type" takes an argument');
1577 self.sSessionType = asArgs[iArg];
1578 elif asArgs[iArg] == '--vrdp':
1579 self.fEnableVrdp = True;
1580 elif asArgs[iArg] == '--no-vrdp':
1581 self.fEnableVrdp = False;
1582 elif asArgs[iArg] == '--vrdp-base-port':
1583 iArg += 1;
1584 if iArg >= len(asArgs):
1585 raise base.InvalidOption('The "--vrdp-base-port" takes an argument');
1586 try: self.uVrdpBasePort = int(asArgs[iArg]);
1587 except: raise base.InvalidOption('The "--vrdp-base-port" value "%s" is not a valid integer' % (asArgs[iArg],));
1588 if self.uVrdpBasePort <= 0 or self.uVrdpBasePort >= 65530:
1589 raise base.InvalidOption('The "--vrdp-base-port" value "%s" is not in the valid range (1..65530)'
1590 % (asArgs[iArg],));
1591 elif asArgs[iArg] == '--vbox-default-bridged-nic':
1592 iArg += 1;
1593 if iArg >= len(asArgs):
1594 raise base.InvalidOption('The "--vbox-default-bridged-nic" takes an argument');
1595 self.sDefBridgedNic = asArgs[iArg];
1596 elif asArgs[iArg] == '--vbox-use-svc-defaults':
1597 self.fUseDefaultSvc = True;
1598 elif asArgs[iArg] == '--vbox-self-log':
1599 iArg += 1;
1600 if iArg >= len(asArgs):
1601 raise base.InvalidOption('The "--vbox-self-log" takes an argument');
1602 self.sLogSelfGroups = asArgs[iArg];
1603 elif asArgs[iArg] == '--vbox-self-log-flags':
1604 iArg += 1;
1605 if iArg >= len(asArgs):
1606 raise base.InvalidOption('The "--vbox-self-log-flags" takes an argument');
1607 self.sLogSelfFlags = asArgs[iArg];
1608 elif asArgs[iArg] == '--vbox-self-log-dest':
1609 iArg += 1;
1610 if iArg >= len(asArgs):
1611 raise base.InvalidOption('The "--vbox-self-log-dest" takes an argument');
1612 self.sLogSelfDest = asArgs[iArg];
1613 elif asArgs[iArg] == '--vbox-session-log':
1614 iArg += 1;
1615 if iArg >= len(asArgs):
1616 raise base.InvalidOption('The "--vbox-session-log" takes an argument');
1617 self.sLogSessionGroups = asArgs[iArg];
1618 elif asArgs[iArg] == '--vbox-session-log-flags':
1619 iArg += 1;
1620 if iArg >= len(asArgs):
1621 raise base.InvalidOption('The "--vbox-session-log-flags" takes an argument');
1622 self.sLogSessionFlags = asArgs[iArg];
1623 elif asArgs[iArg] == '--vbox-session-log-dest':
1624 iArg += 1;
1625 if iArg >= len(asArgs):
1626 raise base.InvalidOption('The "--vbox-session-log-dest" takes an argument');
1627 self.sLogSessionDest = asArgs[iArg];
1628 elif asArgs[iArg] == '--vbox-svc-log':
1629 iArg += 1;
1630 if iArg >= len(asArgs):
1631 raise base.InvalidOption('The "--vbox-svc-log" takes an argument');
1632 self.sLogSvcGroups = asArgs[iArg];
1633 elif asArgs[iArg] == '--vbox-svc-log-flags':
1634 iArg += 1;
1635 if iArg >= len(asArgs):
1636 raise base.InvalidOption('The "--vbox-svc-log-flags" takes an argument');
1637 self.sLogSvcFlags = asArgs[iArg];
1638 elif asArgs[iArg] == '--vbox-svc-log-dest':
1639 iArg += 1;
1640 if iArg >= len(asArgs):
1641 raise base.InvalidOption('The "--vbox-svc-log-dest" takes an argument');
1642 self.sLogSvcDest = asArgs[iArg];
1643 elif asArgs[iArg] == '--vbox-log':
1644 iArg += 1;
1645 if iArg >= len(asArgs):
1646 raise base.InvalidOption('The "--vbox-log" takes an argument');
1647 self.sLogSelfGroups = asArgs[iArg];
1648 self.sLogSessionGroups = asArgs[iArg];
1649 self.sLogSvcGroups = asArgs[iArg];
1650 elif asArgs[iArg] == '--vbox-log-flags':
1651 iArg += 1;
1652 if iArg >= len(asArgs):
1653 raise base.InvalidOption('The "--vbox-svc-flags" takes an argument');
1654 self.sLogSelfFlags = asArgs[iArg];
1655 self.sLogSessionFlags = asArgs[iArg];
1656 self.sLogSvcFlags = asArgs[iArg];
1657 elif asArgs[iArg] == '--vbox-log-dest':
1658 iArg += 1;
1659 if iArg >= len(asArgs):
1660 raise base.InvalidOption('The "--vbox-log-dest" takes an argument');
1661 self.sLogSelfDest = asArgs[iArg];
1662 self.sLogSessionDest = asArgs[iArg];
1663 self.sLogSvcDest = asArgs[iArg];
1664 elif asArgs[iArg] == '--vbox-svc-debug':
1665 self.fVBoxSvcInDebugger = True;
1666 elif asArgs[iArg] == '--vbox-always-upload-logs':
1667 self.fAlwaysUploadLogs = True;
1668 elif asArgs[iArg] == '--vbox-always-upload-screenshots':
1669 self.fAlwaysUploadScreenshots = True;
1670 elif asArgs[iArg] == '--vbox-debugger':
1671 self.fEnableDebugger = True;
1672 elif asArgs[iArg] == '--no-vbox-debugger':
1673 self.fEnableDebugger = False;
1674 else:
1675 # Relevant for selecting VMs to test?
1676 if self.oTestVmSet is not None:
1677 iRc = self.oTestVmSet.parseOption(asArgs, iArg);
1678 if iRc != iArg:
1679 return iRc;
1680
1681 # Hand it to the base class.
1682 return base.TestDriver.parseOption(self, asArgs, iArg);
1683 return iArg + 1;
1684
1685 def completeOptions(self):
1686 return base.TestDriver.completeOptions(self);
1687
1688 def getResourceSet(self):
1689 if self.oTestVmSet is not None:
1690 return self.oTestVmSet.getResourceSet();
1691 return base.TestDriver.getResourceSet(self);
1692
1693 def actionExtract(self):
1694 return base.TestDriver.actionExtract(self);
1695
1696 def actionVerify(self):
1697 return base.TestDriver.actionVerify(self);
1698
1699 def actionConfig(self):
1700 return base.TestDriver.actionConfig(self);
1701
1702 def actionExecute(self):
1703 return base.TestDriver.actionExecute(self);
1704
1705 def actionCleanupBefore(self):
1706 """
1707 Kill any VBoxSVC left behind by a previous test run.
1708 """
1709 self._killVBoxSVCByPidFile('%s/VBoxSVC.pid' % (self.sScratchPath,));
1710 return base.TestDriver.actionCleanupBefore(self);
1711
1712 def actionCleanupAfter(self):
1713 """
1714 Clean up the VBox bits and then call the base driver.
1715
1716 If your test driver overrides this, it should normally call us at the
1717 end of the job.
1718 """
1719
1720 # Kill any left over VM processes.
1721 self._powerOffAllVms();
1722
1723 # Drop all VBox object references and shutdown xpcom then
1724 # terminating VBoxSVC, with extreme prejudice if need be.
1725 self._teardownVBoxApi();
1726 self._stopVBoxSVC();
1727
1728 # Add the VBoxSVC and testdriver debug+release log files.
1729 if self.fAlwaysUploadLogs or reporter.getErrorCount() > 0:
1730 if self.sVBoxSvcLogFile is not None and os.path.isfile(self.sVBoxSvcLogFile):
1731 reporter.addLogFile(self.sVBoxSvcLogFile, 'log/debug/svc', 'Debug log file for VBoxSVC');
1732 self.sVBoxSvcLogFile = None;
1733
1734 if self.sSelfLogFile is not None and os.path.isfile(self.sSelfLogFile):
1735 reporter.addLogFile(self.sSelfLogFile, 'log/debug/client', 'Debug log file for the test driver');
1736 self.sSelfLogFile = None;
1737
1738 sVBoxSvcRelLog = os.path.join(self.sScratchPath, 'VBoxUserHome', 'VBoxSVC.log');
1739 if os.path.isfile(sVBoxSvcRelLog):
1740 reporter.addLogFile(sVBoxSvcRelLog, 'log/release/svc', 'Release log file for VBoxSVC');
1741 for sSuff in [ '.1', '.2', '.3', '.4', '.5', '.6', '.7', '.8' ]:
1742 if os.path.isfile(sVBoxSvcRelLog + sSuff):
1743 reporter.addLogFile(sVBoxSvcRelLog + sSuff, 'log/release/svc', 'Release log file for VBoxSVC');
1744 # Testbox debugging - START - TEMPORARY, REMOVE ASAP.
1745 if self.sHost in ('darwin', 'freebsd', 'linux', 'solaris', ):
1746 try:
1747 print '> ls -la %s' % (os.path.join(self.sScratchPath, 'VBoxUserHome'),);
1748 utils.processCall(['ls', '-la', os.path.join(self.sScratchPath, 'VBoxUserHome')]);
1749 print '> ls -la %s' % (self.sScratchPath,);
1750 utils.processCall(['ls', '-la', self.sScratchPath]);
1751 except: pass;
1752 # Testbox debugging - END - TEMPORARY, REMOVE ASAP.
1753
1754 # Finally, call the base driver to wipe the scratch space.
1755 return base.TestDriver.actionCleanupAfter(self);
1756
1757 def actionAbort(self):
1758 """
1759 Terminate VBoxSVC if we've got a pid file.
1760 """
1761 #
1762 # Take default action first, then kill VBoxSVC. The other way around
1763 # is problematic since the testscript would continue running and possibly
1764 # trigger a new VBoxSVC to start.
1765 #
1766 fRc1 = base.TestDriver.actionAbort(self);
1767 fRc2 = self._killVBoxSVCByPidFile('%s/VBoxSVC.pid' % (self.sScratchPath,));
1768 return fRc1 is True and fRc2 is True;
1769
1770 def onExit(self, iRc):
1771 """
1772 Stop VBoxSVC if we've started it.
1773 """
1774 if self.oVBoxSvcProcess is not None:
1775 reporter.log('*** Shutting down the VBox API... (iRc=%s)' % (iRc,));
1776 self._powerOffAllVms();
1777 self._teardownVBoxApi();
1778 self._stopVBoxSVC();
1779 reporter.log('*** VBox API shutdown done.');
1780 return base.TestDriver.onExit(self, iRc);
1781
1782
1783 #
1784 # Task wait method override.
1785 #
1786
1787 def notifyAboutReadyTask(self, oTask):
1788 """
1789 Overriding base.TestDriver.notifyAboutReadyTask.
1790 """
1791 try:
1792 self.oVBoxMgr.interruptWaitEvents();
1793 reporter.log2('vbox.notifyAboutReadyTask: called interruptWaitEvents');
1794 except:
1795 reporter.logXcpt('vbox.notifyAboutReadyTask');
1796 return base.TestDriver.notifyAboutReadyTask(self, oTask);
1797
1798 def waitForTasksSleepWorker(self, cMsTimeout):
1799 """
1800 Overriding base.TestDriver.waitForTasksSleepWorker.
1801 """
1802 try:
1803 rc = self.oVBoxMgr.waitForEvents(int(cMsTimeout));
1804 _ = rc; #reporter.log2('vbox.waitForTasksSleepWorker(%u): true (waitForEvents -> %s)' % (cMsTimeout, rc));
1805 reporter.doPollWork('vbox.TestDriver.waitForTasksSleepWorker');
1806 return True;
1807 except KeyboardInterrupt:
1808 raise;
1809 except:
1810 reporter.logXcpt('vbox.waitForTasksSleepWorker');
1811 return False;
1812
1813 #
1814 # Utility methods.
1815 #
1816
1817 def processEvents(self, cMsTimeout = 0):
1818 """
1819 Processes events, returning after the first batch has been processed
1820 or the time limit has been reached.
1821
1822 Only Ctrl-C exception, no return.
1823 """
1824 try:
1825 self.oVBoxMgr.waitForEvents(cMsTimeout);
1826 except KeyboardInterrupt:
1827 raise;
1828 except:
1829 pass;
1830 return None;
1831
1832 def processPendingEvents(self):
1833 """ processEvents(0) - no waiting. """
1834 return self.processEvents(0);
1835
1836 def sleep(self, cSecs):
1837 """
1838 Sleep for a specified amount of time, processing XPCOM events all the while.
1839 """
1840 cMsTimeout = long(cSecs * 1000);
1841 msStart = base.timestampMilli();
1842 self.processEvents(0);
1843 while True:
1844 cMsElapsed = base.timestampMilli() - msStart;
1845 if cMsElapsed > cMsTimeout:
1846 break;
1847 #reporter.log2('cMsTimeout=%s - cMsElapsed=%d => %s' % (cMsTimeout, cMsElapsed, cMsTimeout - cMsElapsed));
1848 self.processEvents(cMsTimeout - cMsElapsed);
1849 return None;
1850
1851 def _logVmInfoUnsafe(self, oVM): # pylint: disable=R0915,R0912
1852 """
1853 Internal worker for logVmInfo that is wrapped in try/except.
1854
1855 This is copy, paste, search, replace and edit of infoCmd from vboxshell.py.
1856 """
1857 oOsType = self.oVBox.getGuestOSType(oVM.OSTypeId)
1858 reporter.log(" Name: %s" % (oVM.name));
1859 reporter.log(" ID: %s" % (oVM.id));
1860 reporter.log(" OS Type: %s - %s" % (oVM.OSTypeId, oOsType.description));
1861 reporter.log(" Machine state: %s" % (oVM.state));
1862 reporter.log(" Session state: %s" % (oVM.sessionState));
1863 if self.fpApiVer >= 4.2:
1864 reporter.log(" Session PID: %u (%#x)" % (oVM.sessionPID, oVM.sessionPID));
1865 else:
1866 reporter.log(" Session PID: %u (%#x)" % (oVM.sessionPid, oVM.sessionPid));
1867 if self.fpApiVer >= 5.0:
1868 reporter.log(" Session Name: %s" % (oVM.sessionName));
1869 else:
1870 reporter.log(" Session Name: %s" % (oVM.sessionType));
1871 reporter.log(" CPUs: %s" % (oVM.CPUCount));
1872 reporter.log(" RAM: %sMB" % (oVM.memorySize));
1873 reporter.log(" VRAM: %sMB" % (oVM.VRAMSize));
1874 reporter.log(" Monitors: %s" % (oVM.monitorCount));
1875 if oVM.chipsetType == vboxcon.ChipsetType_PIIX3: sType = "PIIX3";
1876 elif oVM.chipsetType == vboxcon.ChipsetType_ICH9: sType = "ICH9";
1877 else: sType = "unknown %s" % (oVM.chipsetType);
1878 reporter.log(" Chipset: %s" % (sType));
1879 if oVM.firmwareType == vboxcon.FirmwareType_BIOS: sType = "BIOS";
1880 elif oVM.firmwareType == vboxcon.FirmwareType_EFI: sType = "EFI";
1881 elif oVM.firmwareType == vboxcon.FirmwareType_EFI32: sType = "EFI32";
1882 elif oVM.firmwareType == vboxcon.FirmwareType_EFI64: sType = "EFI64";
1883 elif oVM.firmwareType == vboxcon.FirmwareType_EFIDUAL: sType = "EFIDUAL";
1884 else: sType = "unknown %s" % (oVM.firmwareType);
1885 reporter.log(" Firmware: %s" % (sType));
1886 reporter.log(" HwVirtEx: %s" % (oVM.getHWVirtExProperty(vboxcon.HWVirtExPropertyType_Enabled)));
1887 reporter.log(" VPID support: %s" % (oVM.getHWVirtExProperty(vboxcon.HWVirtExPropertyType_VPID)));
1888 reporter.log(" Nested paging: %s" % (oVM.getHWVirtExProperty(vboxcon.HWVirtExPropertyType_NestedPaging)));
1889 if self.fpApiVer >= 4.2 and hasattr(vboxcon, 'CPUPropertyType_LongMode'):
1890 reporter.log(" Long-mode: %s" % (oVM.getCPUProperty(vboxcon.CPUPropertyType_LongMode)));
1891 if self.fpApiVer >= 3.2:
1892 reporter.log(" PAE: %s" % (oVM.getCPUProperty(vboxcon.CPUPropertyType_PAE)));
1893 if self.fpApiVer < 5.0:
1894 reporter.log(" Synthetic CPU: %s" % (oVM.getCPUProperty(vboxcon.CPUPropertyType_Synthetic)));
1895 else:
1896 reporter.log(" PAE: %s" % (oVM.getCpuProperty(vboxcon.CpuPropertyType_PAE)));
1897 reporter.log(" Synthetic CPU: %s" % (oVM.getCpuProperty(vboxcon.CpuPropertyType_Synthetic)));
1898 reporter.log(" ACPI: %s" % (oVM.BIOSSettings.ACPIEnabled));
1899 reporter.log(" IO-APIC: %s" % (oVM.BIOSSettings.IOAPICEnabled));
1900 if self.fpApiVer >= 3.2:
1901 if self.fpApiVer >= 4.2:
1902 reporter.log(" HPET: %s" % (oVM.HPETEnabled));
1903 else:
1904 reporter.log(" HPET: %s" % (oVM.hpetEnabled));
1905 reporter.log(" 3D acceleration: %s" % (oVM.accelerate3DEnabled));
1906 reporter.log(" 2D acceleration: %s" % (oVM.accelerate2DVideoEnabled));
1907 reporter.log(" TeleporterEnabled: %s" % (oVM.teleporterEnabled));
1908 reporter.log(" TeleporterPort: %s" % (oVM.teleporterPort));
1909 reporter.log(" TeleporterAddress: %s" % (oVM.teleporterAddress));
1910 reporter.log(" TeleporterPassword: %s" % (oVM.teleporterPassword));
1911 reporter.log(" Clipboard mode: %s" % (oVM.clipboardMode));
1912 if self.fpApiVer >= 5.0:
1913 reporter.log(" Drag and drop mode: %s" % (oVM.dnDMode));
1914 elif self.fpApiVer >= 4.3:
1915 reporter.log(" Drag and drop mode: %s" % (oVM.dragAndDropMode));
1916 if self.fpApiVer >= 4.0:
1917 reporter.log(" VRDP server: %s" % (oVM.VRDEServer.enabled));
1918 try: sPorts = oVM.VRDEServer.getVRDEProperty("TCP/Ports");
1919 except: sPorts = "";
1920 reporter.log(" VRDP server ports: %s" % (sPorts));
1921 reporter.log(" VRDP auth: %s (%s)" % (oVM.VRDEServer.authType, oVM.VRDEServer.authLibrary));
1922 else:
1923 reporter.log(" VRDP server: %s" % (oVM.VRDPServer.enabled));
1924 reporter.log(" VRDP server ports: %s" % (oVM.VRDPServer.ports));
1925 reporter.log(" Last changed: %s" % (oVM.lastStateChange));
1926
1927 aoControllers = self.oVBoxMgr.getArray(oVM, 'storageControllers')
1928 if aoControllers:
1929 reporter.log(" Controllers:");
1930 for oCtrl in aoControllers:
1931 reporter.log(" %s %s bus: %s type: %s" % (oCtrl.name, oCtrl.controllerType, oCtrl.bus, oCtrl.controllerType));
1932 oAudioAdapter = oVM.audioAdapter;
1933 if oAudioAdapter.audioController == vboxcon.AudioControllerType_AC97: sType = "AC97";
1934 elif oAudioAdapter.audioController == vboxcon.AudioControllerType_SB16: sType = "SB16";
1935 elif oAudioAdapter.audioController == vboxcon.AudioControllerType_HDA: sType = "HDA";
1936 else: sType = "unknown %s" % (oAudioAdapter.audioController);
1937 reporter.log(" AudioController: %s" % (sType));
1938 reporter.log(" AudioEnabled: %s" % (oAudioAdapter.enabled));
1939 if oAudioAdapter.audioDriver == vboxcon.AudioDriverType_CoreAudio: sType = "CoreAudio";
1940 elif oAudioAdapter.audioDriver == vboxcon.AudioDriverType_DirectSound: sType = "DirectSound";
1941 elif oAudioAdapter.audioDriver == vboxcon.AudioDriverType_Pulse: sType = "PulseAudio";
1942 elif oAudioAdapter.audioDriver == vboxcon.AudioDriverType_OSS: sType = "OSS";
1943 elif oAudioAdapter.audioDriver == vboxcon.AudioDriverType_Null: sType = "NULL";
1944 else: sType = "unknown %s" % (oAudioAdapter.audioDriver);
1945 reporter.log(" Host AudioDriver: %s" % (sType));
1946
1947 self.processPendingEvents();
1948 aoAttachments = self.oVBoxMgr.getArray(oVM, 'mediumAttachments')
1949 if aoAttachments:
1950 reporter.log(" Attachments:");
1951 for oAtt in aoAttachments:
1952 sCtrl = "Controller: %s port: %s device: %s type: %s" % (oAtt.controller, oAtt.port, oAtt.device, oAtt.type);
1953 oMedium = oAtt.medium
1954 if oAtt.type == vboxcon.DeviceType_HardDisk:
1955 reporter.log(" %s: HDD" % sCtrl);
1956 reporter.log(" Id: %s" % (oMedium.id));
1957 reporter.log(" Name: %s" % (oMedium.name));
1958 reporter.log(" Format: %s" % (oMedium.format));
1959 reporter.log(" Location: %s" % (oMedium.location));
1960
1961 if oAtt.type == vboxcon.DeviceType_DVD:
1962 reporter.log(" %s: DVD" % sCtrl);
1963 if oMedium:
1964 reporter.log(" Id: %s" % (oMedium.id));
1965 reporter.log(" Name: %s" % (oMedium.name));
1966 if oMedium.hostDrive:
1967 reporter.log(" Host DVD %s" % (oMedium.location));
1968 if oAtt.passthrough:
1969 reporter.log(" [passthrough mode]");
1970 else:
1971 reporter.log(" Virtual image: %s" % (oMedium.location));
1972 reporter.log(" Size: %s" % (oMedium.size));
1973 else:
1974 reporter.log(" empty");
1975
1976 if oAtt.type == vboxcon.DeviceType_Floppy:
1977 reporter.log(" %s: Floppy" % sCtrl);
1978 if oMedium:
1979 reporter.log(" Id: %s" % (oMedium.id));
1980 reporter.log(" Name: %s" % (oMedium.name));
1981 if oMedium.hostDrive:
1982 reporter.log(" Host floppy: %s" % (oMedium.location));
1983 else:
1984 reporter.log(" Virtual image: %s" % (oMedium.location));
1985 reporter.log(" Size: %s" % (oMedium.size));
1986 else:
1987 reporter.log(" empty");
1988 self.processPendingEvents();
1989
1990 reporter.log(" Network Adapter:");
1991 for iSlot in range(0, 32):
1992 try: oNic = oVM.getNetworkAdapter(iSlot)
1993 except: break;
1994 if not oNic.enabled:
1995 reporter.log2(" slot #%d found but not enabled, skipping" % (iSlot,));
1996 continue;
1997 if oNic.adapterType == vboxcon.NetworkAdapterType_Am79C973: sType = "PCNet";
1998 elif oNic.adapterType == vboxcon.NetworkAdapterType_Am79C970A: sType = "PCNetOld";
1999 elif oNic.adapterType == vboxcon.NetworkAdapterType_I82545EM: sType = "E1000";
2000 elif oNic.adapterType == vboxcon.NetworkAdapterType_I82540EM: sType = "E1000Desk";
2001 elif oNic.adapterType == vboxcon.NetworkAdapterType_I82543GC: sType = "E1000Srv2";
2002 elif oNic.adapterType == vboxcon.NetworkAdapterType_Virtio: sType = "Virtio";
2003 else: sType = "unknown %s" % (oNic.adapterType);
2004 reporter.log(" slot #%d: type: %s (%s) MAC Address: %s lineSpeed: %s" % \
2005 (iSlot, sType, oNic.adapterType, oNic.MACAddress, oNic.lineSpeed) );
2006
2007 if oNic.attachmentType == vboxcon.NetworkAttachmentType_NAT:
2008 reporter.log(" attachmentType: NAT (%s)" % (oNic.attachmentType));
2009 if self.fpApiVer >= 4.1:
2010 reporter.log(" nat-network: %s" % (oNic.NATNetwork,));
2011 elif oNic.attachmentType == vboxcon.NetworkAttachmentType_Bridged:
2012 reporter.log(" attachmentType: Bridged (%s)" % (oNic.attachmentType));
2013 if self.fpApiVer >= 4.1:
2014 reporter.log(" hostInterface: %s" % (oNic.bridgedInterface));
2015 else:
2016 reporter.log(" hostInterface: %s" % (oNic.hostInterface));
2017 elif oNic.attachmentType == vboxcon.NetworkAttachmentType_Internal:
2018 reporter.log(" attachmentType: Internal (%s)" % (oNic.attachmentType));
2019 reporter.log(" intnet-name: %s" % (oNic.internalNetwork,));
2020 elif oNic.attachmentType == vboxcon.NetworkAttachmentType_HostOnly:
2021 reporter.log(" attachmentType: HostOnly (%s)" % (oNic.attachmentType));
2022 if self.fpApiVer >= 4.1:
2023 reporter.log(" hostInterface: %s" % (oNic.hostOnlyInterface));
2024 else:
2025 reporter.log(" hostInterface: %s" % (oNic.hostInterface));
2026 else:
2027 if self.fpApiVer >= 4.1:
2028 if oNic.attachmentType == vboxcon.NetworkAttachmentType_Generic:
2029 reporter.log(" attachmentType: Generic (%s)" % (oNic.attachmentType));
2030 reporter.log(" generic-driver: %s" % (oNic.GenericDriver));
2031 else:
2032 reporter.log(" attachmentType: unknown-%s" % (oNic.attachmentType));
2033 else:
2034 reporter.log(" attachmentType: unknown-%s" % (oNic.attachmentType));
2035 if oNic.traceEnabled:
2036 reporter.log(" traceFile: %s" % (oNic.traceFile));
2037 self.processPendingEvents();
2038 return True;
2039
2040 def logVmInfo(self, oVM): # pylint: disable=R0915,R0912
2041 """
2042 Logs VM configuration details.
2043
2044 This is copy, past, search, replace and edit of infoCmd from vboxshell.py.
2045 """
2046 try:
2047 fRc = self._logVmInfoUnsafe(oVM);
2048 except:
2049 reporter.logXcpt();
2050 fRc = False;
2051 return fRc;
2052
2053 def logVmInfoByName(self, sName):
2054 """
2055 logVmInfo + getVmByName.
2056 """
2057 return self.logVmInfo(self.getVmByName(sName));
2058
2059 def tryFindGuestOsId(self, sIdOrDesc):
2060 """
2061 Takes a guest OS ID or Description and returns the ID.
2062 If nothing matching it is found, the input is returned unmodified.
2063 """
2064
2065 if self.fpApiVer >= 4.0:
2066 if sIdOrDesc == 'Solaris (64 bit)':
2067 sIdOrDesc = 'Oracle Solaris 10 5/09 and earlier (64 bit)';
2068
2069 try:
2070 aoGuestTypes = self.oVBoxMgr.getArray(self.oVBox, 'GuestOSTypes');
2071 except:
2072 reporter.logXcpt();
2073 else:
2074 for oGuestOS in aoGuestTypes:
2075 try:
2076 sId = oGuestOS.id;
2077 sDesc = oGuestOS.description;
2078 except:
2079 reporter.logXcpt();
2080 else:
2081 if sIdOrDesc == sId or sIdOrDesc == sDesc:
2082 sIdOrDesc = sId;
2083 break;
2084 self.processPendingEvents();
2085 return sIdOrDesc
2086
2087 def resourceFindVmHd(self, sVmName, sFlavor):
2088 """
2089 Search the test resources for the most recent VM HD.
2090
2091 Returns path relative to the test resource root.
2092 """
2093 ## @todo implement a proper search algo here.
2094 return '4.2/' + sFlavor + '/' + sVmName + '/t-' + sVmName + '.vdi';
2095
2096
2097 #
2098 # VM Api wrappers that logs errors, hides exceptions and other details.
2099 #
2100
2101 # pylint: disable=R0913,R0914,R0915
2102 def createTestVM(self, sName, iGroup, sHd = None, cMbRam = None, cCpus = 1, fVirtEx = None, fNestedPaging = None, \
2103 sDvdImage = None, sKind = "Other", fIoApic = None, fPae = None, fFastBootLogo = True, \
2104 eNic0Type = None, eNic0AttachType = None, sNic0NetName = 'default', sNic0MacAddr = 'grouped', \
2105 sFloppy = None, fNatForwardingForTxs = None, sHddControllerType = 'IDE Controller', \
2106 fVmmDevTestingPart = None, fVmmDevTestingMmio = False, sFirmwareType = 'bios', sChipsetType = 'piix3'):
2107 """
2108 Creates a test VM with a immutable HD from the test resources.
2109 """
2110 if not self.importVBoxApi():
2111 return None;
2112
2113 # create + register the VM
2114 try:
2115 if self.fpApiVer >= 4.2: # Introduces grouping (third parameter, empty for now).
2116 oVM = self.oVBox.createMachine("", sName, [], self.tryFindGuestOsId(sKind), "");
2117 elif self.fpApiVer >= 4.0:
2118 oVM = self.oVBox.createMachine("", sName, self.tryFindGuestOsId(sKind), "", False);
2119 elif self.fpApiVer >= 3.2:
2120 oVM = self.oVBox.createMachine(sName, self.tryFindGuestOsId(sKind), "", "", False);
2121 else:
2122 oVM = self.oVBox.createMachine(sName, self.tryFindGuestOsId(sKind), "", "");
2123 try:
2124 oVM.saveSettings();
2125 try:
2126 self.oVBox.registerMachine(oVM);
2127 except:
2128 raise;
2129 except:
2130 reporter.logXcpt();
2131 if self.fpApiVer >= 4.0:
2132 try:
2133 if self.fpApiVer >= 4.3:
2134 oProgress = oVM.deleteConfig([]);
2135 else:
2136 oProgress = oVM.delete(None);
2137 self.waitOnProgress(oProgress);
2138 except:
2139 reporter.logXcpt();
2140 else:
2141 try: oVM.deleteSettings();
2142 except: reporter.logXcpt();
2143 raise;
2144 except:
2145 reporter.errorXcpt('failed to create vm "%s"' % (sName));
2146 return None;
2147
2148 # Configure the VM.
2149 fRc = True;
2150 oSession = self.openSession(oVM);
2151 if oSession is not None:
2152 fRc = oSession.setupPreferredConfig();
2153
2154 if fRc and cMbRam is not None :
2155 fRc = oSession.setRamSize(cMbRam);
2156 if fRc and cCpus is not None:
2157 fRc = oSession.setCpuCount(cCpus);
2158 if fRc and fVirtEx is not None:
2159 fRc = oSession.enableVirtEx(fVirtEx);
2160 if fRc and fNestedPaging is not None:
2161 fRc = oSession.enableNestedPaging(fNestedPaging);
2162 if fRc and fIoApic is not None:
2163 fRc = oSession.enableIoApic(fIoApic);
2164 if fRc and fPae is not None:
2165 fRc = oSession.enablePae(fPae);
2166 if fRc and sDvdImage is not None:
2167 fRc = oSession.attachDvd(sDvdImage);
2168 if fRc and sHd is not None:
2169 fRc = oSession.attachHd(sHd, sHddControllerType);
2170 if fRc and sFloppy is not None:
2171 fRc = oSession.attachFloppy(sFloppy);
2172 if fRc and eNic0Type is not None:
2173 fRc = oSession.setNicType(eNic0Type, 0);
2174 if fRc and (eNic0AttachType is not None or (sNic0NetName is not None and sNic0NetName != 'default')):
2175 fRc = oSession.setNicAttachment(eNic0AttachType, sNic0NetName, 0);
2176 if fRc and sNic0MacAddr is not None:
2177 if sNic0MacAddr == 'grouped':
2178 sNic0MacAddr = '%02u' % (iGroup);
2179 fRc = oSession.setNicMacAddress(sNic0MacAddr, 0);
2180 if fRc and fNatForwardingForTxs is True:
2181 fRc = oSession.setupNatForwardingForTxs();
2182 if fRc and fFastBootLogo is not None:
2183 fRc = oSession.setupBootLogo(fFastBootLogo);
2184 if fRc and self.fEnableVrdp:
2185 fRc = oSession.setupVrdp(True, self.uVrdpBasePort + iGroup);
2186 if fRc and fVmmDevTestingPart is not None:
2187 fRc = oSession.enableVmmDevTestingPart(fVmmDevTestingPart, fVmmDevTestingMmio);
2188 if fRc and sFirmwareType == 'bios':
2189 fRc = oSession.setFirmwareType(vboxcon.FirmwareType_BIOS);
2190 elif sFirmwareType == 'efi':
2191 fRc = oSession.setFirmwareType(vboxcon.FirmwareType_EFI);
2192 if fRc and self.fEnableDebugger:
2193 fRc = oSession.setExtraData('VBoxInternal/DBGC/Enabled', '1');
2194 if fRc and sChipsetType == 'piix3':
2195 fRc = oSession.setChipsetType(vboxcon.ChipsetType_PIIX3);
2196 elif sChipsetType == 'ich9':
2197 fRc = oSession.setChipsetType(vboxcon.ChipsetType_ICH9);
2198
2199 if fRc: fRc = oSession.saveSettings();
2200 if not fRc: oSession.discardSettings(True);
2201 oSession.close();
2202 if not fRc:
2203 try: self.oVBox.unregisterMachine(oVM.id);
2204 except: pass;
2205 if self.fpApiVer >= 4.0:
2206 try:
2207 if self.fpApiVer >= 4.3:
2208 oProgress = oVM.deleteConfig([]);
2209 else:
2210 oProgress = oVM.delete(None);
2211 self.waitOnProgress(oProgress);
2212 except:
2213 reporter.logXcpt();
2214 else:
2215 try: oVM.deleteSettings();
2216 except: reporter.logXcpt();
2217 return None;
2218
2219 # success.
2220 reporter.log('created "%s" with name "%s"' % (oVM.id, sName));
2221 self.aoVMs.append(oVM);
2222 self.logVmInfo(oVM); # testing...
2223 return oVM;
2224 # pylint: enable=R0913,R0914,R0915
2225
2226 def addTestMachine(self, sNameOrId, fQuiet = False):
2227 """
2228 Adds an already existing (that is, configured) test VM to the
2229 test VM list.
2230 """
2231 # find + add the VM to the list.
2232 try:
2233 if self.fpApiVer >= 4.0:
2234 oVM = self.oVBox.findMachine(sNameOrId);
2235 else:
2236 reporter.error('Port me!'); ## @todo Add support for older version < 4.0.
2237 except:
2238 reporter.errorXcpt('could not find vm "%s"' % (sNameOrId,));
2239 return None;
2240
2241 self.aoVMs.append(oVM);
2242 if not fQuiet:
2243 reporter.log('Added "%s" with name "%s"' % (oVM.id, sNameOrId));
2244 self.logVmInfo(oVM);
2245 return oVM;
2246
2247 def openSession(self, oVM):
2248 """
2249 Opens a session for the VM. Returns the a Session wrapper object that
2250 will automatically close the session when the wrapper goes out of scope.
2251
2252 On failure None is returned and an error is logged.
2253 """
2254 try:
2255 sUuid = oVM.id;
2256 except:
2257 reporter.errorXcpt('failed to get the UUID for VM "%s"' % (oVM,));
2258 return None;
2259
2260 # This loop is a kludge to deal with us racing the closing of the
2261 # direct session of a previous VM run. See waitOnDirectSessionClose.
2262 for i in range(10):
2263 try:
2264 if self.fpApiVer <= 3.2:
2265 oSession = self.oVBoxMgr.openMachineSession(sUuid);
2266 else:
2267 oSession = self.oVBoxMgr.openMachineSession(oVM);
2268 break;
2269 except:
2270 if i == 9:
2271 reporter.errorXcpt('failed to open session for "%s" ("%s")' % (sUuid, oVM));
2272 return None;
2273 if i > 0:
2274 reporter.logXcpt('warning: failed to open session for "%s" ("%s") - retrying in %u secs' % (sUuid, oVM, i));
2275 self.waitOnDirectSessionClose(oVM, 5000 + i * 1000);
2276 from testdriver.vboxwrappers import SessionWrapper;
2277 return SessionWrapper(oSession, oVM, self.oVBox, self.oVBoxMgr, self, False);
2278
2279 def getVmByName(self, sName):
2280 """
2281 Get a test VM by name. Returns None if not found, logged.
2282 """
2283 # Look it up in our 'cache'.
2284 for oVM in self.aoVMs:
2285 try:
2286 #reporter.log2('cur: %s / %s (oVM=%s)' % (oVM.name, oVM.id, oVM));
2287 if oVM.name == sName:
2288 return oVM;
2289 except:
2290 reporter.errorXcpt('failed to get the name from the VM "%s"' % (oVM));
2291
2292 # Look it up the standard way.
2293 return self.addTestMachine(sName, fQuiet = True);
2294
2295 def getVmByUuid(self, sUuid):
2296 """
2297 Get a test VM by uuid. Returns None if not found, logged.
2298 """
2299 # Look it up in our 'cache'.
2300 for oVM in self.aoVMs:
2301 try:
2302 if oVM.id == sUuid:
2303 return oVM;
2304 except:
2305 reporter.errorXcpt('failed to get the UUID from the VM "%s"' % (oVM));
2306
2307 # Look it up the standard way.
2308 return self.addTestMachine(sUuid, fQuiet = True);
2309
2310 def waitOnProgress(self, oProgress, cMsTimeout = 1000000, fErrorOnTimeout = True, cMsInterval = 1000):
2311 """
2312 Waits for a progress object to complete. Returns the status code.
2313 """
2314 # Wait for progress no longer than cMsTimeout time period.
2315 tsStart = datetime.datetime.now()
2316 while True:
2317 self.processPendingEvents();
2318 try:
2319 if oProgress.completed:
2320 break;
2321 except:
2322 return -1;
2323 self.processPendingEvents();
2324
2325 tsNow = datetime.datetime.now()
2326 tsDelta = tsNow - tsStart
2327 if ((tsDelta.microseconds + tsDelta.seconds * 1000000) / 1000) > cMsTimeout:
2328 if fErrorOnTimeout:
2329 reporter.errorTimeout('Timeout while waiting for progress.')
2330 return -1
2331
2332 reporter.doPollWork('vbox.TestDriver.waitOnProgress');
2333 try: oProgress.waitForCompletion(cMsInterval);
2334 except: return -2;
2335
2336 try: rc = oProgress.resultCode;
2337 except: rc = -2;
2338 self.processPendingEvents();
2339 return rc;
2340
2341 def waitOnDirectSessionClose(self, oVM, cMsTimeout):
2342 """
2343 Waits for the VM process to close it's current direct session.
2344
2345 Returns None.
2346 """
2347 # Get the original values so we're not subject to
2348 try:
2349 eCurState = oVM.sessionState;
2350 if self.fpApiVer >= 5.0:
2351 sCurName = sOrgName = oVM.sessionName;
2352 else:
2353 sCurName = sOrgName = oVM.sessionType;
2354 if self.fpApiVer >= 4.2:
2355 iCurPid = iOrgPid = oVM.sessionPID;
2356 else:
2357 iCurPid = iOrgPid = oVM.sessionPid;
2358 except Exception, oXcpt:
2359 if ComError.notEqual(oXcpt, ComError.E_ACCESSDENIED):
2360 reporter.logXcpt();
2361 self.processPendingEvents();
2362 return None;
2363 self.processPendingEvents();
2364
2365 msStart = base.timestampMilli();
2366 while iCurPid == iOrgPid \
2367 and sCurName == sOrgName \
2368 and sCurName != '' \
2369 and base.timestampMilli() - msStart < cMsTimeout \
2370 and ( eCurState == vboxcon.SessionState_Unlocking \
2371 or eCurState == vboxcon.SessionState_Spawning \
2372 or eCurState == vboxcon.SessionState_Locked):
2373 self.processEvents(1000);
2374 try:
2375 eCurState = oVM.sessionState;
2376 sCurName = oVM.sessionName if self.fpApiVer >= 5.0 else oVM.sessionType;
2377 iCurPid = oVM.sessionPID if self.fpApiVer >= 4.2 else oVM.sessionPid;
2378 except Exception, oXcpt:
2379 if ComError.notEqual(oXcpt, ComError.E_ACCESSDENIED):
2380 reporter.logXcpt();
2381 break;
2382 self.processPendingEvents();
2383 self.processPendingEvents();
2384 return None;
2385
2386 def uploadStartupLogFile(self, oVM, sVmName):
2387 """
2388 Uploads the VBoxStartup.log when present.
2389 """
2390 fRc = True;
2391 try:
2392 sLogFile = os.path.join(oVM.logFolder, 'VBoxHardening.log');
2393 except:
2394 reporter.logXcpt();
2395 fRc = False;
2396 else:
2397 if os.path.isfile(sLogFile):
2398 reporter.addLogFile(sLogFile, 'log/release/vm', '%s hardening log' % (sVmName, ),
2399 sAltName = '%s-%s' % (sVmName, os.path.basename(sLogFile),));
2400 return fRc;
2401
2402 def annotateAndUploadProcessReport(self, sProcessReport, sFilename, sKind, sDesc):
2403 """
2404 Annotates the given VM process report and uploads it if successfull.
2405 """
2406 fRc = False;
2407 if self.oBuild is not None and self.oBuild.sInstallPath is not None:
2408 oResolver = btresolver.BacktraceResolver(self.sScratchPath, self.oBuild.sInstallPath,
2409 self.getBuildOs(), self.getBuildArch(),
2410 fnLog = reporter.log);
2411 fRcTmp = oResolver.prepareEnv();
2412 if fRcTmp:
2413 reporter.log('Successfully prepared environment');
2414 sReportDbgSym = oResolver.annotateReport(sProcessReport);
2415 if sReportDbgSym is not None:
2416 reporter.addLogString(sReportDbgSym, sFilename, sKind, sDesc);
2417 fRc = True;
2418 else:
2419 reporter.log('Annotating report failed');
2420 oResolver.cleanupEnv();
2421 return fRc;
2422
2423 def startVmEx(self, oVM, fWait = True, sType = None, sName = None, asEnv = None): # pylint: disable=R0914,R0915
2424 """
2425 Start the VM, returning the VM session and progress object on success.
2426 The session is also added to the task list and to the aoRemoteSessions set.
2427
2428 asEnv is a list of string on the putenv() form.
2429
2430 On failure (None, None) is returned and an error is logged.
2431 """
2432 # Massage and check the input.
2433 if sType is None:
2434 sType = self.sSessionType;
2435 if sName is None:
2436 try: sName = oVM.name;
2437 except: sName = 'bad-vm-handle';
2438 reporter.log('startVmEx: sName=%s fWait=%s sType=%s' % (sName, fWait, sType));
2439 if oVM is None:
2440 return (None, None);
2441
2442 ## @todo Do this elsewhere.
2443 # Hack alert. Disables all annoying GUI popups.
2444 if sType == 'gui' and not self.aoRemoteSessions:
2445 try:
2446 self.oVBox.setExtraData('GUI/Input/AutoCapture', 'false');
2447 if self.fpApiVer >= 3.2:
2448 self.oVBox.setExtraData('GUI/LicenseAgreed', '8');
2449 else:
2450 self.oVBox.setExtraData('GUI/LicenseAgreed', '7');
2451 self.oVBox.setExtraData('GUI/RegistrationData', 'triesLeft=0');
2452 self.oVBox.setExtraData('GUI/SUNOnlineData', 'triesLeft=0');
2453 self.oVBox.setExtraData('GUI/SuppressMessages', 'confirmVMReset,remindAboutMouseIntegrationOn,'
2454 'remindAboutMouseIntegrationOff,remindAboutPausedVMInput,confirmInputCapture,'
2455 'confirmGoingFullscreen,remindAboutInaccessibleMedia,remindAboutWrongColorDepth,'
2456 'confirmRemoveMedium,allPopupPanes,allMessageBoxes,all');
2457 self.oVBox.setExtraData('GUI/UpdateDate', 'never');
2458 self.oVBox.setExtraData('GUI/PreventBetaWarning', self.oVBox.version);
2459 except:
2460 reporter.logXcpt();
2461
2462 # The UUID for the name.
2463 try:
2464 sUuid = oVM.id;
2465 except:
2466 reporter.errorXcpt('failed to get the UUID for VM "%s"' % (oVM));
2467 return (None, None);
2468 self.processPendingEvents();
2469
2470 # Construct the environment.
2471 sLogFile = '%s/VM-%s.log' % (self.sScratchPath, sUuid);
2472 try: os.remove(sLogFile);
2473 except: pass;
2474 if self.sLogSessionDest:
2475 sLogDest = self.sLogSessionDest;
2476 else:
2477 sLogDest = 'file=%s' % sLogFile;
2478 sEnv = 'VBOX_LOG=%s\nVBOX_LOG_FLAGS=%s\nVBOX_LOG_DEST=%s\nVBOX_RELEASE_LOG_FLAGS=append time' \
2479 % (self.sLogSessionGroups, self.sLogSessionFlags, sLogDest,);
2480 # Extra audio logging
2481 sEnv += '\nVBOX_RELEASE_LOG=drv_audio.e.l.l2+drv_host_audio.e.l.l2'
2482 if sType == 'gui':
2483 sEnv += '\nVBOX_GUI_DBG_ENABLED=1'
2484 if asEnv is not None and asEnv:
2485 sEnv += '\n' + ('\n'.join(asEnv));
2486
2487 # Shortcuts for local testing.
2488 oProgress = oWrapped = None;
2489 oTestVM = self.oTestVmSet.findTestVmByName(sName) if self.oTestVmSet is not None else None;
2490 try:
2491 if oTestVM is not None \
2492 and oTestVM.fSnapshotRestoreCurrent is True:
2493 if oVM.state is vboxcon.MachineState_Running:
2494 reporter.log2('Machine "%s" already running.' % (sName,));
2495 oProgress = None;
2496 oWrapped = self.openSession(oVM);
2497 else:
2498 reporter.log2('Checking if snapshot for machine "%s" exists.' % (sName,));
2499 oSessionWrapperRestore = self.openSession(oVM);
2500 if oSessionWrapperRestore is not None:
2501 oSnapshotCur = oVM.currentSnapshot;
2502 if oSnapshotCur is not None:
2503 reporter.log2('Restoring snapshot for machine "%s".' % (sName,));
2504 oSessionWrapperRestore.restoreSnapshot(oSnapshotCur);
2505 reporter.log2('Current snapshot for machine "%s" restored.' % (sName,));
2506 else:
2507 reporter.log('warning: no current snapshot for machine "%s" found.' % (sName,));
2508 oSessionWrapperRestore.close();
2509 except:
2510 reporter.errorXcpt();
2511 return (None, None);
2512
2513 # Open a remote session, wait for this operation to complete.
2514 # (The loop is a kludge to deal with us racing the closing of the
2515 # direct session of a previous VM run. See waitOnDirectSessionClose.)
2516 if oWrapped is None:
2517 for i in range(10):
2518 try:
2519 if self.fpApiVer < 4.3 \
2520 or (self.fpApiVer == 4.3 and not hasattr(self.oVBoxMgr, 'getSessionObject')):
2521 oSession = self.oVBoxMgr.mgr.getSessionObject(self.oVBox); # pylint: disable=E1101
2522 elif self.fpApiVer < 5.2 \
2523 or (self.fpApiVer == 5.2 and hasattr(self.oVBoxMgr, 'vbox')):
2524 oSession = self.oVBoxMgr.getSessionObject(self.oVBox); # pylint: disable=E1101
2525 else:
2526 oSession = self.oVBoxMgr.getSessionObject(); # pylint: disable=E1101
2527 if self.fpApiVer < 3.3:
2528 oProgress = self.oVBox.openRemoteSession(oSession, sUuid, sType, sEnv);
2529 else:
2530 oProgress = oVM.launchVMProcess(oSession, sType, sEnv);
2531 break;
2532 except:
2533 if i == 9:
2534 reporter.errorXcpt('failed to start VM "%s" ("%s"), aborting.' % (sUuid, sName));
2535 return (None, None);
2536 oSession = None;
2537 if i >= 0:
2538 reporter.logXcpt('warning: failed to start VM "%s" ("%s") - retrying in %u secs.' % (sUuid, oVM, i)); # pylint: disable=C0301
2539 self.waitOnDirectSessionClose(oVM, 5000 + i * 1000);
2540 if fWait and oProgress is not None:
2541 rc = self.waitOnProgress(oProgress);
2542 if rc < 0:
2543 self.waitOnDirectSessionClose(oVM, 5000);
2544
2545 # VM failed to power up, still collect VBox.log, need to wrap the session object
2546 # in order to use the helper for adding the log files to the report.
2547 from testdriver.vboxwrappers import SessionWrapper;
2548 oTmp = SessionWrapper(oSession, oVM, self.oVBox, self.oVBoxMgr, self, True, sName, sLogFile);
2549 oTmp.addLogsToReport();
2550 try:
2551 if oSession is not None:
2552 oSession.close();
2553 except: pass;
2554 reportError(oProgress, 'failed to open session for "%s"' % (sName));
2555 self.uploadStartupLogFile(oVM, sName);
2556 return (None, None);
2557 reporter.log2('waitOnProgress -> %s' % (rc,));
2558
2559 # Wrap up the session object and push on to the list before returning it.
2560 if oWrapped is None:
2561 from testdriver.vboxwrappers import SessionWrapper;
2562 oWrapped = SessionWrapper(oSession, oVM, self.oVBox, self.oVBoxMgr, self, True, sName, sLogFile);
2563
2564 oWrapped.registerEventHandlerForTask();
2565 self.aoRemoteSessions.append(oWrapped);
2566 if oWrapped is not self.aoRemoteSessions[len(self.aoRemoteSessions) - 1]:
2567 reporter.error('not by reference: oWrapped=%s aoRemoteSessions[%s]=%s'
2568 % (oWrapped, len(self.aoRemoteSessions) - 1,
2569 self.aoRemoteSessions[len(self.aoRemoteSessions) - 1]));
2570 self.addTask(oWrapped);
2571
2572 reporter.log2('startVmEx: oSession=%s, oSessionWrapper=%s, oProgress=%s' % (oSession, oWrapped, oProgress));
2573
2574 from testdriver.vboxwrappers import ProgressWrapper;
2575 return (oWrapped, ProgressWrapper(oProgress, self.oVBoxMgr, self,
2576 'starting %s' % (sName,)) if oProgress else None);
2577
2578 def startVm(self, oVM, sType=None, sName = None, asEnv = None):
2579 """ Simplified version of startVmEx. """
2580 oSession, _ = self.startVmEx(oVM, True, sType, sName, asEnv = asEnv);
2581 return oSession;
2582
2583 def startVmByNameEx(self, sName, fWait=True, sType=None, asEnv = None):
2584 """
2585 Start the VM, returning the VM session and progress object on success.
2586 The session is also added to the task list and to the aoRemoteSessions set.
2587
2588 On failure (None, None) is returned and an error is logged.
2589 """
2590 oVM = self.getVmByName(sName);
2591 if oVM is None:
2592 return (None, None);
2593 return self.startVmEx(oVM, fWait, sType, sName, asEnv = asEnv);
2594
2595 def startVmByName(self, sName, sType=None, asEnv = None):
2596 """
2597 Start the VM, returning the VM session on success. The session is
2598 also added to the task list and to the aoRemoteSessions set.
2599
2600 On failure None is returned and an error is logged.
2601 """
2602 oSession, _ = self.startVmByNameEx(sName, True, sType, asEnv = asEnv);
2603 return oSession;
2604
2605 def terminateVmBySession(self, oSession, oProgress = None, fTakeScreenshot = None): # pylint: disable=R0915
2606 """
2607 Terminates the VM specified by oSession and adds the release logs to
2608 the test report.
2609
2610 This will try achieve this by using powerOff, but will resort to
2611 tougher methods if that fails.
2612
2613 The session will always be removed from the task list.
2614 The session will be closed unless we fail to kill the process.
2615 The session will be removed from the remote session list if closed.
2616
2617 The progress object (a wrapper!) is for teleportation and similar VM
2618 operations, it will be attempted canceled before powering off the VM.
2619 Failures are logged but ignored.
2620 The progress object will always be removed from the task list.
2621
2622 Returns True if powerOff and session close both succeed.
2623 Returns False if on failure (logged), including when we successfully
2624 kill the VM process.
2625 """
2626 reporter.log2('terminateVmBySession: oSession=%s (pid=%s) oProgress=%s' % (oSession.sName, oSession.getPid(), oProgress));
2627
2628 # Call getPid first to make sure the PID is cached in the wrapper.
2629 oSession.getPid();
2630
2631 #
2632 # If the host is out of memory, just skip all the info collection as it
2633 # requires memory too and seems to wedge.
2634 #
2635 sHostProcessInfo = None;
2636 sHostProcessInfoHung = None;
2637 sLastScreenshotPath = None;
2638 sOsKernelLog = None;
2639 sVgaText = None;
2640 asMiscInfos = [];
2641
2642 if not oSession.fHostMemoryLow:
2643 # Try to fetch the VM process info before meddling with its state.
2644 if self.fAlwaysUploadLogs or reporter.testErrorCount() > 0:
2645 sHostProcessInfo = utils.processGetInfo(oSession.getPid(), fSudo = True);
2646
2647 #
2648 # Pause the VM if we're going to take any screenshots or dig into the
2649 # guest. Failures are quitely ignored.
2650 #
2651 if self.fAlwaysUploadLogs or reporter.testErrorCount() > 0:
2652 try:
2653 if oSession.oVM.state in [ vboxcon.MachineState_Running,
2654 vboxcon.MachineState_LiveSnapshotting,
2655 vboxcon.MachineState_Teleporting ]:
2656 oSession.o.console.pause();
2657 except:
2658 reporter.logXcpt();
2659
2660 #
2661 # Take Screenshot and upload it (see below) to Test Manager if appropriate/requested.
2662 #
2663 if fTakeScreenshot is True or self.fAlwaysUploadScreenshots or reporter.testErrorCount() > 0:
2664 sLastScreenshotPath = os.path.join(self.sScratchPath, "LastScreenshot-%s.png" % oSession.sName);
2665 fRc = oSession.takeScreenshot(sLastScreenshotPath);
2666 if fRc is not True:
2667 sLastScreenshotPath = None;
2668
2669 # Query the OS kernel log from the debugger if appropriate/requested.
2670 if self.fAlwaysUploadLogs or reporter.testErrorCount() > 0:
2671 sOsKernelLog = oSession.queryOsKernelLog();
2672
2673 # Do "info vgatext all" separately.
2674 if self.fAlwaysUploadLogs or reporter.testErrorCount() > 0:
2675 sVgaText = oSession.queryDbgInfoVgaText();
2676
2677 # Various infos (do after kernel because of symbols).
2678 if self.fAlwaysUploadLogs or reporter.testErrorCount() > 0:
2679 # Dump the guest stack for all CPUs.
2680 cCpus = oSession.getCpuCount();
2681 if cCpus > 0:
2682 for iCpu in xrange(0, cCpus):
2683 sThis = oSession.queryDbgGuestStack(iCpu);
2684 if sThis:
2685 asMiscInfos += [
2686 '================ start guest stack VCPU %s ================\n' % (iCpu,),
2687 sThis,
2688 '================ end guest stack VCPU %s ==================\n' % (iCpu,),
2689 ];
2690
2691 for sInfo, sArg in [ ('mode', 'all'),
2692 ('fflags', ''),
2693 ('cpumguest', 'verbose all'),
2694 ('cpumguestinstr', 'symbol all'),
2695 ('pic', ''),
2696 ('apic', ''),
2697 ('apiclvt', ''),
2698 ('apictimer', ''),
2699 ('ioapic', ''),
2700 ('pit', ''),
2701 ('phys', ''),
2702 ('clocks', ''),
2703 ('timers', ''),
2704 ('gdtguest', ''),
2705 ('ldtguest', ''),
2706 ]:
2707 if sInfo in ['apic',] and self.fpApiVer < 5.1: # asserts and burns
2708 continue;
2709 sThis = oSession.queryDbgInfo(sInfo, sArg);
2710 if sThis:
2711 if sThis[-1] != '\n':
2712 sThis += '\n';
2713 asMiscInfos += [
2714 '================ start %s %s ================\n' % (sInfo, sArg),
2715 sThis,
2716 '================ end %s %s ==================\n' % (sInfo, sArg),
2717 ];
2718
2719 #
2720 # Terminate the VM
2721 #
2722
2723 # Cancel the progress object if specified.
2724 if oProgress is not None:
2725 if not oProgress.isCompleted() and oProgress.isCancelable():
2726 reporter.log2('terminateVmBySession: canceling "%s"...' % (oProgress.sName));
2727 try:
2728 oProgress.o.cancel();
2729 except:
2730 reporter.logXcpt();
2731 else:
2732 oProgress.wait();
2733 self.removeTask(oProgress);
2734
2735 # Check if the VM has terminated by itself before powering it off.
2736 fClose = True;
2737 fRc = True;
2738 if oSession.needsPoweringOff():
2739 reporter.log('terminateVmBySession: powering off "%s"...' % (oSession.sName,));
2740 fRc = oSession.powerOff(fFudgeOnFailure = False);
2741 if fRc is not True:
2742 # power off failed, try terminate it in a nice manner.
2743 fRc = False;
2744 uPid = oSession.getPid();
2745 if uPid is not None:
2746 #
2747 # Collect some information about the VM process first to have
2748 # some state information for further investigation why powering off failed.
2749 #
2750 sHostProcessInfoHung = utils.processGetInfo(uPid, fSudo = True);
2751
2752 # Exterminate...
2753 reporter.error('terminateVmBySession: Terminating PID %u (VM %s)' % (uPid, oSession.sName));
2754 fClose = base.processTerminate(uPid);
2755 if fClose is True:
2756 self.waitOnDirectSessionClose(oSession.oVM, 5000);
2757 fClose = oSession.waitForTask(1000);
2758
2759 if fClose is not True:
2760 # Being nice failed...
2761 reporter.error('terminateVmBySession: Termination failed, trying to kill PID %u (VM %s) instead' \
2762 % (uPid, oSession.sName));
2763 fClose = base.processKill(uPid);
2764 if fClose is True:
2765 self.waitOnDirectSessionClose(oSession.oVM, 5000);
2766 fClose = oSession.waitForTask(1000);
2767 if fClose is not True:
2768 reporter.error('terminateVmBySession: Failed to kill PID %u (VM %s)' % (uPid, oSession.sName));
2769
2770 # The final steps.
2771 if fClose is True:
2772 reporter.log('terminateVmBySession: closing session "%s"...' % (oSession.sName,));
2773 oSession.close();
2774 self.waitOnDirectSessionClose(oSession.oVM, 10000);
2775 try:
2776 eState = oSession.oVM.state;
2777 except:
2778 reporter.logXcpt();
2779 else:
2780 if eState == vboxcon.MachineState_Aborted:
2781 reporter.error('terminateVmBySession: The VM "%s" aborted!' % (oSession.sName,));
2782 self.removeTask(oSession);
2783
2784 #
2785 # Add the release log, debug log and a screenshot of the VM to the test report.
2786 #
2787 if self.fAlwaysUploadLogs or reporter.testErrorCount() > 0:
2788 oSession.addLogsToReport();
2789
2790 # Add a screenshot if it has been requested and taken successfully.
2791 if sLastScreenshotPath is not None:
2792 if reporter.testErrorCount() > 0:
2793 reporter.addLogFile(sLastScreenshotPath, 'screenshot/failure', 'Last VM screenshot');
2794 else:
2795 reporter.addLogFile(sLastScreenshotPath, 'screenshot/success', 'Last VM screenshot');
2796
2797 # Add the guest OS log if it has been requested and taken successfully.
2798 if sOsKernelLog is not None:
2799 reporter.addLogString(sOsKernelLog, 'kernel.log', 'log/guest/kernel', 'Guest OS kernel log');
2800
2801 # Add "info vgatext all" if we've got it.
2802 if sVgaText is not None:
2803 reporter.addLogString(sVgaText, 'vgatext.txt', 'info/vgatext', 'info vgatext all');
2804
2805 # Add the "info xxxx" items if we've got any.
2806 if asMiscInfos:
2807 reporter.addLogString(u''.join(asMiscInfos), 'info.txt', 'info/collection', 'A bunch of info items.');
2808
2809 # Add the host process info if we were able to retrieve it.
2810 if sHostProcessInfo is not None:
2811 reporter.log('Trying to annotate the VM process report, please stand by...');
2812 fRcTmp = self.annotateAndUploadProcessReport(sHostProcessInfo, 'vmprocess.log',
2813 'process/report/vm', 'Annotated VM process state');
2814 # Upload the raw log for manual annotation in case resolving failed.
2815 if not fRcTmp:
2816 reporter.log('Failed to annotate VM process report, uploading raw report');
2817 reporter.addLogString(sHostProcessInfo, 'vmprocess.log', 'process/report/vm', 'VM process state');
2818
2819 # Add the host process info for failed power off attempts if we were able to retrieve it.
2820 if sHostProcessInfoHung is not None:
2821 reporter.log('Trying to annotate the hung VM process report, please stand by...');
2822 fRcTmp = self.annotateAndUploadProcessReport(sHostProcessInfoHung, 'vmprocess-hung.log',
2823 'process/report/vm', 'Annotated hung VM process state');
2824 # Upload the raw log for manual annotation in case resolving failed.
2825 if not fRcTmp:
2826 reporter.log('Failed to annotate hung VM process report, uploading raw report');
2827 reporter.addLogString(sHostProcessInfoHung, 'vmprocess-hung.log', 'process/report/vm',
2828 'Hung VM process state');
2829
2830 return fRc;
2831
2832
2833 #
2834 # Some information query functions (mix).
2835 #
2836 # Methods require the VBox API. If the information is provided by both
2837 # the testboxscript as well as VBox API, we'll check if it matches.
2838 #
2839
2840 def _hasHostCpuFeature(self, sEnvVar, sEnum, fpApiMinVer, fQuiet):
2841 """
2842 Common Worker for hasHostNestedPaging() and hasHostHwVirt().
2843
2844 Returns True / False.
2845 Raises exception on environment / host mismatch.
2846 """
2847 fEnv = os.environ.get(sEnvVar, None);
2848 if fEnv is not None:
2849 fEnv = fEnv.lower() not in [ 'false', 'f', 'not', 'no', 'n', '0', ];
2850
2851 fVBox = None;
2852 self.importVBoxApi();
2853 if self.fpApiVer >= fpApiMinVer and hasattr(vboxcon, sEnum):
2854 try:
2855 fVBox = self.oVBox.host.getProcessorFeature(getattr(vboxcon, sEnum));
2856 except:
2857 if not fQuiet:
2858 reporter.logXcpt();
2859
2860 if fVBox is not None:
2861 if fEnv is not None:
2862 if fEnv != fVBox and not fQuiet:
2863 reporter.log('TestBox configuration overwritten: fVBox=%s (%s) vs. fEnv=%s (%s)'
2864 % (fVBox, sEnum, fEnv, sEnvVar));
2865 return fEnv;
2866 return fVBox;
2867 if fEnv is not None:
2868 return fEnv;
2869 return False;
2870
2871 def hasHostHwVirt(self, fQuiet = False):
2872 """
2873 Checks if hardware assisted virtualization is supported by the host.
2874
2875 Returns True / False.
2876 Raises exception on environment / host mismatch.
2877 """
2878 return self._hasHostCpuFeature('TESTBOX_HAS_HW_VIRT', 'ProcessorFeature_HWVirtEx', 3.1, fQuiet);
2879
2880 def hasHostNestedPaging(self, fQuiet = False):
2881 """
2882 Checks if nested paging is supported by the host.
2883
2884 Returns True / False.
2885 Raises exception on environment / host mismatch.
2886 """
2887 return self._hasHostCpuFeature('TESTBOX_HAS_NESTED_PAGING', 'ProcessorFeature_NestedPaging', 4.2, fQuiet) \
2888 and self.hasHostHwVirt(fQuiet);
2889
2890 def hasHostLongMode(self, fQuiet = False):
2891 """
2892 Checks if the host supports 64-bit guests.
2893
2894 Returns True / False.
2895 Raises exception on environment / host mismatch.
2896 """
2897 # Note that the testboxscript doesn't export this variable atm.
2898 return self._hasHostCpuFeature('TESTBOX_HAS_LONG_MODE', 'ProcessorFeature_LongMode', 3.1, fQuiet);
2899
2900 def getHostCpuCount(self, fQuiet = False):
2901 """
2902 Returns the number of CPUs on the host.
2903
2904 Returns True / False.
2905 Raises exception on environment / host mismatch.
2906 """
2907 cEnv = os.environ.get('TESTBOX_CPU_COUNT', None);
2908 if cEnv is not None:
2909 cEnv = int(cEnv);
2910
2911 try:
2912 cVBox = self.oVBox.host.processorOnlineCount;
2913 except:
2914 if not fQuiet:
2915 reporter.logXcpt();
2916 cVBox = None;
2917
2918 if cVBox is not None:
2919 if cEnv is not None:
2920 assert cVBox == cEnv, 'Misconfigured TestBox: VBox: %u CPUs, testboxscript: %u CPUs' % (cVBox, cEnv);
2921 return cVBox;
2922 if cEnv is not None:
2923 return cEnv;
2924 return 1;
2925
2926 def _getHostCpuDesc(self, fQuiet = False):
2927 """
2928 Internal method used for getting the host CPU description from VBoxSVC.
2929 Returns description string, on failure an empty string is returned.
2930 """
2931 try:
2932 return self.oVBox.host.getProcessorDescription(0);
2933 except:
2934 if not fQuiet:
2935 reporter.logXcpt();
2936 return '';
2937
2938 def isHostCpuAmd(self, fQuiet = False):
2939 """
2940 Checks if the host CPU vendor is AMD.
2941
2942 Returns True / False.
2943 """
2944 sCpuDesc = self._getHostCpuDesc(fQuiet);
2945 return sCpuDesc.startswith("AMD") or sCpuDesc == 'AuthenticAMD';
2946
2947 def isHostCpuIntel(self, fQuiet = False):
2948 """
2949 Checks if the host CPU vendor is Intel.
2950
2951 Returns True / False.
2952 """
2953 sCpuDesc = self._getHostCpuDesc(fQuiet);
2954 return sCpuDesc.startswith("Intel") or sCpuDesc == 'GenuineIntel';
2955
2956 def isHostCpuVia(self, fQuiet = False):
2957 """
2958 Checks if the host CPU vendor is VIA (or Centaur).
2959
2960 Returns True / False.
2961 """
2962 sCpuDesc = self._getHostCpuDesc(fQuiet);
2963 return sCpuDesc.startswith("VIA") or sCpuDesc == 'CentaurHauls';
2964
2965 def isHostCpuP4(self, fQuiet = False):
2966 """
2967 Checks if the host CPU is a Pentium 4 / Pentium D.
2968
2969 Returns True / False.
2970 """
2971 if not self.isHostCpuIntel(fQuiet):
2972 return False;
2973
2974 (uFamilyModel, _, _, _) = self.oVBox.host.getProcessorCPUIDLeaf(0, 0x1, 0);
2975 return ((uFamilyModel >> 8) & 0xf) == 0xf;
2976
2977 def hasRawModeSupport(self, fQuiet = False):
2978 """
2979 Checks if raw-mode is supported by VirtualBox that the testbox is
2980 configured for it.
2981
2982 Returns True / False.
2983 Raises no exceptions.
2984
2985 Note! Differs from the rest in that we don't require the
2986 TESTBOX_WITH_RAW_MODE value to match the API. It is
2987 sometimes helpful to disable raw-mode on individual
2988 test boxes. (This probably goes for
2989 """
2990 # The environment variable can be used to disable raw-mode.
2991 fEnv = os.environ.get('TESTBOX_WITH_RAW_MODE', None);
2992 if fEnv is not None:
2993 fEnv = fEnv.lower() not in [ 'false', 'f', 'not', 'no', 'n', '0', ];
2994 if fEnv is False:
2995 return False;
2996
2997 # Starting with 5.0 GA / RC2 the API can tell us whether VBox was built
2998 # with raw-mode support or not.
2999 self.importVBoxApi();
3000 if self.fpApiVer >= 5.0:
3001 try:
3002 fVBox = self.oVBox.systemProperties.rawModeSupported;
3003 except:
3004 if not fQuiet:
3005 reporter.logXcpt();
3006 fVBox = True;
3007 if fVBox is False:
3008 return False;
3009
3010 return True;
3011
3012 #
3013 # Testdriver execution methods.
3014 #
3015
3016 def handleTask(self, oTask, sMethod):
3017 """
3018 Callback method for handling unknown tasks in the various run loops.
3019
3020 The testdriver should override this if it already tasks running when
3021 calling startVmAndConnectToTxsViaTcp, txsRunTest or similar methods.
3022 Call super to handle unknown tasks.
3023
3024 Returns True if handled, False if not.
3025 """
3026 reporter.error('%s: unknown task %s' % (sMethod, oTask));
3027 return False;
3028
3029 def txsDoTask(self, oSession, oTxsSession, fnAsync, aArgs):
3030 """
3031 Generic TXS task wrapper which waits both on the TXS and the session tasks.
3032
3033 Returns False on error, logged.
3034
3035 Returns task result on success.
3036 """
3037 # All async methods ends with the following to args.
3038 cMsTimeout = aArgs[-2];
3039 fIgnoreErrors = aArgs[-1];
3040
3041 fRemoveVm = self.addTask(oSession);
3042 fRemoveTxs = self.addTask(oTxsSession);
3043
3044 rc = fnAsync(*aArgs); # pylint: disable=W0142
3045 if rc is True:
3046 rc = False;
3047 oTask = self.waitForTasks(cMsTimeout + 1);
3048 if oTask is oTxsSession:
3049 if oTxsSession.isSuccess():
3050 rc = oTxsSession.getResult();
3051 elif fIgnoreErrors is True:
3052 reporter.log( 'txsDoTask: task failed (%s)' % (oTxsSession.getLastReply()[1],));
3053 else:
3054 reporter.error('txsDoTask: task failed (%s)' % (oTxsSession.getLastReply()[1],));
3055 else:
3056 oTxsSession.cancelTask();
3057 if oTask is None:
3058 if fIgnoreErrors is True:
3059 reporter.log( 'txsDoTask: The task timed out.');
3060 else:
3061 reporter.errorTimeout('txsDoTask: The task timed out.');
3062 elif oTask is oSession:
3063 reporter.error('txsDoTask: The VM terminated unexpectedly');
3064 else:
3065 if fIgnoreErrors is True:
3066 reporter.log( 'txsDoTask: An unknown task %s was returned' % (oTask,));
3067 else:
3068 reporter.error('txsDoTask: An unknown task %s was returned' % (oTask,));
3069 else:
3070 reporter.error('txsDoTask: fnAsync returned %s' % (rc,));
3071
3072 if fRemoveTxs:
3073 self.removeTask(oTxsSession);
3074 if fRemoveVm:
3075 self.removeTask(oSession);
3076 return rc;
3077
3078 # pylint: disable=C0111
3079
3080 def txsDisconnect(self, oSession, oTxsSession, cMsTimeout = 30000, fIgnoreErrors = False):
3081 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncDisconnect,
3082 (self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3083
3084 def txsUuid(self, oSession, oTxsSession, cMsTimeout = 30000, fIgnoreErrors = False):
3085 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncUuid,
3086 (self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3087
3088 def txsMkDir(self, oSession, oTxsSession, sRemoteDir, fMode = 0700, cMsTimeout = 30000, fIgnoreErrors = False):
3089 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncMkDir,
3090 (sRemoteDir, fMode, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3091
3092 def txsMkDirPath(self, oSession, oTxsSession, sRemoteDir, fMode = 0700, cMsTimeout = 30000, fIgnoreErrors = False):
3093 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncMkDirPath,
3094 (sRemoteDir, fMode, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3095
3096 def txsMkSymlink(self, oSession, oTxsSession, sLinkTarget, sLink, cMsTimeout = 30000, fIgnoreErrors = False):
3097 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncMkSymlink,
3098 (sLinkTarget, sLink, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3099
3100 def txsRmDir(self, oSession, oTxsSession, sRemoteDir, cMsTimeout = 30000, fIgnoreErrors = False):
3101 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncRmDir,
3102 (sRemoteDir, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3103
3104 def txsRmFile(self, oSession, oTxsSession, sRemoteFile, cMsTimeout = 30000, fIgnoreErrors = False):
3105 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncRmFile,
3106 (sRemoteFile, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3107
3108 def txsRmSymlink(self, oSession, oTxsSession, sRemoteSymlink, cMsTimeout = 30000, fIgnoreErrors = False):
3109 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncRmSymlink,
3110 (sRemoteSymlink, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3111
3112 def txsRmTree(self, oSession, oTxsSession, sRemoteTree, cMsTimeout = 30000, fIgnoreErrors = False):
3113 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncRmTree,
3114 (sRemoteTree, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3115
3116 def txsIsDir(self, oSession, oTxsSession, sRemoteDir, cMsTimeout = 30000, fIgnoreErrors = False):
3117 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncIsDir,
3118 (sRemoteDir, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3119
3120 def txsIsFile(self, oSession, oTxsSession, sRemoteFile, cMsTimeout = 30000, fIgnoreErrors = False):
3121 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncIsFile,
3122 (sRemoteFile, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3123
3124 def txsIsSymlink(self, oSession, oTxsSession, sRemoteSymlink, cMsTimeout = 30000, fIgnoreErrors = False):
3125 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncIsSymlink,
3126 (sRemoteSymlink, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3127
3128 def txsUploadFile(self, oSession, oTxsSession, sLocalFile, sRemoteFile, cMsTimeout = 30000, fIgnoreErrors = False):
3129 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncUploadFile, \
3130 (sLocalFile, sRemoteFile, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3131
3132 def txsUploadString(self, oSession, oTxsSession, sContent, sRemoteFile, cMsTimeout = 30000, fIgnoreErrors = False):
3133 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncUploadString, \
3134 (sContent, sRemoteFile, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3135
3136 def txsDownloadFile(self, oSession, oTxsSession, sRemoteFile, sLocalFile, cMsTimeout = 30000, fIgnoreErrors = False):
3137 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncDownloadFile, \
3138 (sRemoteFile, sLocalFile, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3139
3140 def txsDownloadFiles(self, oSession, oTxsSession, asFiles, fIgnoreErrors = False):
3141 """
3142 Convenience function to get files from the guest and stores it
3143 into the scratch directory for later (manual) review.
3144
3145 Returns True on success.
3146
3147 Returns False on failure, logged.
3148 """
3149 fRc = True;
3150 for sGstFile in asFiles:
3151 ## @todo Check for already existing files on the host and create a new
3152 # name for the current file to download.
3153 sTmpFile = os.path.join(self.sScratchPath, 'tmp-' + os.path.basename(sGstFile));
3154 reporter.log2('Downloading file "%s" to "%s" ...' % (sGstFile, sTmpFile));
3155 fRc = self.txsDownloadFile(oSession, oTxsSession, sGstFile, sTmpFile, 30 * 1000, fIgnoreErrors);
3156 try: os.unlink(sTmpFile);
3157 except: pass;
3158 if fRc:
3159 reporter.addLogFile(sTmpFile, 'misc/other', 'guest - ' + sGstFile);
3160 else:
3161 if fIgnoreErrors is not True:
3162 reporter.error('error downloading file "%s" to "%s"' % (sGstFile, sTmpFile));
3163 return fRc;
3164 reporter.log('warning: file "%s" was not downloaded, ignoring.' % (sGstFile,));
3165 return True;
3166
3167 def txsDownloadString(self, oSession, oTxsSession, sRemoteFile, cMsTimeout = 30000, fIgnoreErrors = False):
3168 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncDownloadString,
3169 (sRemoteFile, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3170
3171 def txsUnpackFile(self, oSession, oTxsSession, sRemoteFile, sRemoteDir, cMsTimeout = 30000, fIgnoreErrors = False):
3172 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncUnpackFile, \
3173 (sRemoteFile, sRemoteDir, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3174
3175 # pylint: enable=C0111
3176
3177 def txsCdWait(self, oSession, oTxsSession, cMsTimeout = 30000, sFileCdWait = 'vboxtxs-readme.txt'):
3178 """
3179 Mostly an internal helper for txsRebootAndReconnectViaTcp and
3180 startVmAndConnectToTxsViaTcp that waits for the CDROM drive to become
3181 ready. It does this by polling for a file it knows to exist on the CD.
3182
3183 Returns True on success.
3184
3185 Returns False on failure, logged.
3186 """
3187
3188 fRemoveVm = self.addTask(oSession);
3189 fRemoveTxs = self.addTask(oTxsSession);
3190 cMsTimeout = self.adjustTimeoutMs(cMsTimeout);
3191 msStart = base.timestampMilli();
3192 cMsTimeout2 = cMsTimeout;
3193 fRc = oTxsSession.asyncIsFile('${CDROM}/%s' % (sFileCdWait), cMsTimeout2);
3194 if fRc is True:
3195 while True:
3196 # wait for it to complete.
3197 oTask = self.waitForTasks(cMsTimeout2 + 1);
3198 if oTask is not oTxsSession:
3199 oTxsSession.cancelTask();
3200 if oTask is None:
3201 reporter.errorTimeout('txsToCdWait: The task timed out (after %s ms).'
3202 % (base.timestampMilli() - msStart,));
3203 elif oTask is oSession:
3204 reporter.error('txsToCdWait: The VM terminated unexpectedly');
3205 else:
3206 reporter.error('txsToCdWait: An unknown task %s was returned' % (oTask,));
3207 fRc = False;
3208 break;
3209 if oTxsSession.isSuccess():
3210 break;
3211
3212 # Check for timeout.
3213 cMsElapsed = base.timestampMilli() - msStart;
3214 if cMsElapsed >= cMsTimeout:
3215 reporter.error('txsToCdWait: timed out');
3216 fRc = False;
3217 break;
3218
3219 # delay.
3220 self.sleep(1);
3221
3222 # resubmitt the task.
3223 cMsTimeout2 = msStart + cMsTimeout - base.timestampMilli();
3224 if cMsTimeout2 < 500:
3225 cMsTimeout2 = 500;
3226 fRc = oTxsSession.asyncIsFile('${CDROM}/%s' % (sFileCdWait), cMsTimeout2);
3227 if fRc is not True:
3228 reporter.error('txsToCdWait: asyncIsFile failed');
3229 break;
3230 else:
3231 reporter.error('txsToCdWait: asyncIsFile failed');
3232
3233 if fRemoveTxs:
3234 self.removeTask(oTxsSession);
3235 if fRemoveVm:
3236 self.removeTask(oSession);
3237 return fRc;
3238
3239 def txsDoConnectViaTcp(self, oSession, cMsTimeout, fNatForwardingForTxs = False):
3240 """
3241 Mostly an internal worker for connecting to TXS via TCP used by the
3242 *ViaTcp methods.
3243
3244 Returns a tuplet with True/False and TxsSession/None depending on the
3245 result. Errors are logged.
3246 """
3247
3248 reporter.log2('txsDoConnectViaTcp: oSession=%s, cMsTimeout=%s, fNatForwardingForTxs=%s'
3249 % (oSession, cMsTimeout, fNatForwardingForTxs));
3250
3251 cMsTimeout = self.adjustTimeoutMs(cMsTimeout);
3252 oTxsConnect = oSession.txsConnectViaTcp(cMsTimeout, fNatForwardingForTxs = fNatForwardingForTxs);
3253 if oTxsConnect is not None:
3254 self.addTask(oTxsConnect);
3255 fRemoveVm = self.addTask(oSession);
3256 oTask = self.waitForTasks(cMsTimeout + 1);
3257 reporter.log2('txsDoConnectViaTcp: waitForTasks returned %s' % (oTask,));
3258 self.removeTask(oTxsConnect);
3259 if oTask is oTxsConnect:
3260 oTxsSession = oTxsConnect.getResult();
3261 if oTxsSession is not None:
3262 reporter.log('txsDoConnectViaTcp: Connected to TXS on %s.' % (oTxsSession.oTransport.sHostname,));
3263 return (True, oTxsSession);
3264
3265 reporter.error('txsDoConnectViaTcp: failed to connect to TXS.');
3266 else:
3267 oTxsConnect.cancelTask();
3268 if oTask is None:
3269 reporter.errorTimeout('txsDoConnectViaTcp: connect stage 1 timed out');
3270 elif oTask is oSession:
3271 oSession.reportPrematureTermination('txsDoConnectViaTcp: ');
3272 else:
3273 reporter.error('txsDoConnectViaTcp: unknown/wrong task %s' % (oTask,));
3274 if fRemoveVm:
3275 self.removeTask(oSession);
3276 else:
3277 reporter.error('txsDoConnectViaTcp: txsConnectViaTcp failed');
3278 return (False, None);
3279
3280 def startVmAndConnectToTxsViaTcp(self, sVmName, fCdWait = False, cMsTimeout = 15*60000, \
3281 cMsCdWait = 30000, sFileCdWait = 'vboxtxs-readme.txt', \
3282 fNatForwardingForTxs = False):
3283 """
3284 Starts the specified VM and tries to connect to its TXS via TCP.
3285 The VM will be powered off if TXS doesn't respond before the specified
3286 time has elapsed.
3287
3288 Returns a the VM and TXS sessions (a two tuple) on success. The VM
3289 session is in the task list, the TXS session is not.
3290 Returns (None, None) on failure, fully logged.
3291 """
3292
3293 # Zap the guest IP to make sure we're not getting a stale entry
3294 # (unless we're restoring the VM of course).
3295 oTestVM = self.oTestVmSet.findTestVmByName(sVmName) if self.oTestVmSet is not None else None;
3296 if oTestVM is None \
3297 or oTestVM.fSnapshotRestoreCurrent is False:
3298 try:
3299 oSession1 = self.openSession(self.getVmByName(sVmName));
3300 oSession1.delGuestPropertyValue('/VirtualBox/GuestInfo/Net/0/V4/IP');
3301 oSession1.saveSettings(True);
3302 del oSession1;
3303 except:
3304 reporter.logXcpt();
3305
3306 # Start the VM.
3307 reporter.log('startVmAndConnectToTxsViaTcp: Starting(/preparing) "%s" (timeout %s s)...' % (sVmName, cMsTimeout / 1000));
3308 reporter.flushall();
3309 oSession = self.startVmByName(sVmName);
3310 if oSession is not None:
3311 # Connect to TXS.
3312 reporter.log2('startVmAndConnectToTxsViaTcp: Started(/prepared) "%s", connecting to TXS ...' % (sVmName,));
3313 (fRc, oTxsSession) = self.txsDoConnectViaTcp(oSession, cMsTimeout, fNatForwardingForTxs);
3314 if fRc is True:
3315 if fCdWait:
3316 # Wait for CD?
3317 fRc = self.txsCdWait(oSession, oTxsSession, cMsCdWait, sFileCdWait);
3318 if fRc is not True:
3319 reporter.error('startVmAndConnectToTxsViaTcp: txsCdWait failed');
3320 if fRc is True:
3321 # Success!
3322 return (oSession, oTxsSession);
3323 else:
3324 reporter.error('startVmAndConnectToTxsViaTcp: txsDoConnectViaTcp failed');
3325 # If something went wrong while waiting for TXS to be started - take VM screenshot before terminate it
3326 self.terminateVmBySession(oSession);
3327 return (None, None);
3328
3329 def txsRebootAndReconnectViaTcp(self, oSession, oTxsSession, fCdWait = False, cMsTimeout = 15*60000, \
3330 cMsCdWait = 30000, sFileCdWait = 'vboxtxs-readme.txt', fNatForwardingForTxs = False):
3331 """
3332 Executes the TXS reboot command
3333
3334 Returns A tuple of True and the new TXS session on success.
3335
3336 Returns A tuple of False and either the old TXS session or None on failure.
3337 """
3338 reporter.log2('txsRebootAndReconnect: cMsTimeout=%u' % (cMsTimeout,));
3339
3340 #
3341 # This stuff is a bit complicated because of rebooting being kind of
3342 # disruptive to the TXS and such... The protocol is that TXS will:
3343 # - ACK the reboot command.
3344 # - Shutdown the transport layer, implicitly disconnecting us.
3345 # - Execute the reboot operation.
3346 # - On failure, it will be re-init the transport layer and be
3347 # available pretty much immediately. UUID unchanged.
3348 # - On success, it will be respawed after the reboot (hopefully),
3349 # with a different UUID.
3350 #
3351 fRc = False;
3352 iStart = base.timestampMilli();
3353
3354 # Get UUID.
3355 cMsTimeout2 = min(60000, cMsTimeout);
3356 sUuidBefore = self.txsUuid(oSession, oTxsSession, self.adjustTimeoutMs(cMsTimeout2, 60000));
3357 if sUuidBefore is not False:
3358 # Reboot.
3359 cMsElapsed = base.timestampMilli() - iStart;
3360 cMsTimeout2 = cMsTimeout - cMsElapsed;
3361 fRc = self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncReboot,
3362 (self.adjustTimeoutMs(cMsTimeout2, 60000), False));
3363 if fRc is True:
3364 # Reconnect.
3365 if fNatForwardingForTxs is True:
3366 self.sleep(22); # NAT fudge - Two fixes are wanted: 1. TXS connect retries. 2. Main API reboot/reset hint.
3367 cMsElapsed = base.timestampMilli() - iStart;
3368 (fRc, oTxsSession) = self.txsDoConnectViaTcp(oSession, cMsTimeout - cMsElapsed, fNatForwardingForTxs);
3369 if fRc is True:
3370 # Check the UUID.
3371 cMsElapsed = base.timestampMilli() - iStart;
3372 cMsTimeout2 = min(60000, cMsTimeout - cMsElapsed);
3373 sUuidAfter = self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncUuid,
3374 (self.adjustTimeoutMs(cMsTimeout2, 60000), False));
3375 if sUuidBefore is not False:
3376 if sUuidAfter != sUuidBefore:
3377 reporter.log('The guest rebooted (UUID %s -> %s)' % (sUuidBefore, sUuidAfter))
3378
3379 # Do CD wait if specified.
3380 if fCdWait:
3381 fRc = self.txsCdWait(oSession, oTxsSession, cMsCdWait, sFileCdWait);
3382 if fRc is not True:
3383 reporter.error('txsRebootAndReconnectViaTcp: txsCdWait failed');
3384 else:
3385 reporter.error('txsRebootAndReconnectViaTcp: failed to get UUID (after)');
3386 else:
3387 reporter.error('txsRebootAndReconnectViaTcp: did not reboot (UUID %s)' % (sUuidBefore,));
3388 else:
3389 reporter.error('txsRebootAndReconnectViaTcp: txsDoConnectViaTcp failed');
3390 else:
3391 reporter.error('txsRebootAndReconnectViaTcp: reboot failed');
3392 else:
3393 reporter.error('txsRebootAndReconnectViaTcp: failed to get UUID (before)');
3394 return (fRc, oTxsSession);
3395
3396 # pylint: disable=R0914,R0913
3397
3398 def txsRunTest(self, oTxsSession, sTestName, cMsTimeout, sExecName, asArgs = (), asAddEnv = (), sAsUser = ""):
3399 """
3400 Executes the specified test task, waiting till it completes or times out.
3401
3402 The VM session (if any) must be in the task list.
3403
3404 Returns True if we executed the task and nothing abnormal happend.
3405 Query the process status from the TXS session.
3406
3407 Returns False if some unexpected task was signalled or we failed to
3408 submit the job.
3409 """
3410 reporter.testStart(sTestName);
3411 reporter.log2('txsRunTest: cMsTimeout=%u sExecName=%s asArgs=%s' % (cMsTimeout, sExecName, asArgs));
3412
3413 # Submit the job.
3414 fRc = False;
3415 if oTxsSession.asyncExec(sExecName, asArgs, asAddEnv, sAsUser, cMsTimeout = self.adjustTimeoutMs(cMsTimeout)):
3416 self.addTask(oTxsSession);
3417
3418 # Wait for the job to complete.
3419 while True:
3420 oTask = self.waitForTasks(cMsTimeout + 1);
3421 if oTask is None:
3422 reporter.log('txsRunTest: waitForTasks timed out');
3423 break;
3424 if oTask is oTxsSession:
3425 fRc = True;
3426 reporter.log('txsRunTest: isSuccess=%s getResult=%s' % (oTxsSession.isSuccess(), oTxsSession.getResult()));
3427 break;
3428 if not self.handleTask(oTask, 'txsRunTest'):
3429 break;
3430
3431 self.removeTask(oTxsSession);
3432 if not oTxsSession.pollTask():
3433 oTxsSession.cancelTask();
3434 else:
3435 reporter.error('txsRunTest: asyncExec failed');
3436
3437 reporter.testDone();
3438 return fRc;
3439
3440 def txsRunTestRedirectStd(self, oTxsSession, sTestName, cMsTimeout, sExecName, asArgs = (), asAddEnv = (), sAsUser = "",
3441 oStdIn = '/dev/null', oStdOut = '/dev/null', oStdErr = '/dev/null', oTestPipe = '/dev/null'):
3442 """
3443 Executes the specified test task, waiting till it completes or times out,
3444 redirecting stdin, stdout and stderr to the given objects.
3445
3446 The VM session (if any) must be in the task list.
3447
3448 Returns True if we executed the task and nothing abnormal happend.
3449 Query the process status from the TXS session.
3450
3451 Returns False if some unexpected task was signalled or we failed to
3452 submit the job.
3453 """
3454 reporter.testStart(sTestName);
3455 reporter.log2('txsRunTestRedirectStd: cMsTimeout=%u sExecName=%s asArgs=%s' % (cMsTimeout, sExecName, asArgs));
3456
3457 # Submit the job.
3458 fRc = False;
3459 if oTxsSession.asyncExecEx(sExecName, asArgs, asAddEnv, oStdIn, oStdOut, oStdErr,
3460 oTestPipe, sAsUser, cMsTimeout = self.adjustTimeoutMs(cMsTimeout)):
3461 self.addTask(oTxsSession);
3462
3463 # Wait for the job to complete.
3464 while True:
3465 oTask = self.waitForTasks(cMsTimeout + 1);
3466 if oTask is None:
3467 reporter.log('txsRunTestRedirectStd: waitForTasks timed out');
3468 break;
3469 if oTask is oTxsSession:
3470 fRc = True;
3471 reporter.log('txsRunTestRedirectStd: isSuccess=%s getResult=%s'
3472 % (oTxsSession.isSuccess(), oTxsSession.getResult()));
3473 break;
3474 if not self.handleTask(oTask, 'txsRunTestRedirectStd'):
3475 break;
3476
3477 self.removeTask(oTxsSession);
3478 if not oTxsSession.pollTask():
3479 oTxsSession.cancelTask();
3480 else:
3481 reporter.error('txsRunTestRedirectStd: asyncExec failed');
3482
3483 reporter.testDone();
3484 return fRc;
3485
3486 def txsRunTest2(self, oTxsSession1, oTxsSession2, sTestName, cMsTimeout,
3487 sExecName1, asArgs1,
3488 sExecName2, asArgs2,
3489 asAddEnv1 = (), sAsUser1 = '', fWithTestPipe1 = True,
3490 asAddEnv2 = (), sAsUser2 = '', fWithTestPipe2 = True):
3491 """
3492 Executes the specified test tasks, waiting till they complete or
3493 times out. The 1st task is started after the 2nd one.
3494
3495 The VM session (if any) must be in the task list.
3496
3497 Returns True if we executed the task and nothing abnormal happend.
3498 Query the process status from the TXS sessions.
3499
3500 Returns False if some unexpected task was signalled or we failed to
3501 submit the job.
3502 """
3503 reporter.testStart(sTestName);
3504
3505 # Submit the jobs.
3506 fRc = False;
3507 if oTxsSession1.asyncExec(sExecName1, asArgs1, asAddEnv1, sAsUser1, fWithTestPipe1, '1-',
3508 self.adjustTimeoutMs(cMsTimeout)):
3509 self.addTask(oTxsSession1);
3510
3511 self.sleep(2); # fudge! grr
3512
3513 if oTxsSession2.asyncExec(sExecName2, asArgs2, asAddEnv2, sAsUser2, fWithTestPipe2, '2-',
3514 self.adjustTimeoutMs(cMsTimeout)):
3515 self.addTask(oTxsSession2);
3516
3517 # Wait for the jobs to complete.
3518 cPendingJobs = 2;
3519 while True:
3520 oTask = self.waitForTasks(cMsTimeout + 1);
3521 if oTask is None:
3522 reporter.log('txsRunTest2: waitForTasks timed out');
3523 break;
3524
3525 if oTask is oTxsSession1 or oTask is oTxsSession2:
3526 if oTask is oTxsSession1: iTask = 1;
3527 else: iTask = 2;
3528 reporter.log('txsRunTest2: #%u - isSuccess=%s getResult=%s' \
3529 % (iTask, oTask.isSuccess(), oTask.getResult()));
3530 self.removeTask(oTask);
3531 cPendingJobs -= 1;
3532 if cPendingJobs <= 0:
3533 fRc = True;
3534 break;
3535
3536 elif not self.handleTask(oTask, 'txsRunTest'):
3537 break;
3538
3539 self.removeTask(oTxsSession2);
3540 if not oTxsSession2.pollTask():
3541 oTxsSession2.cancelTask();
3542 else:
3543 reporter.error('txsRunTest2: asyncExec #2 failed');
3544
3545 self.removeTask(oTxsSession1);
3546 if not oTxsSession1.pollTask():
3547 oTxsSession1.cancelTask();
3548 else:
3549 reporter.error('txsRunTest2: asyncExec #1 failed');
3550
3551 reporter.testDone();
3552 return fRc;
3553
3554 # pylint: enable=R0914,R0913
3555
Note: See TracBrowser for help on using the repository browser.

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