VirtualBox

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

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

(C) year

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