VirtualBox

source: vbox/trunk/src/VBox/ValidationKit/testmanager/batch/virtual_test_sheriff.py@ 103077

Last change on this file since 103077 was 103077, checked in by vboxsync, 12 months ago

vsheriff: Taught it about Python bindings installation problems. bugref:10579

  • Property svn:eol-style set to LF
  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
File size: 93.7 KB
Line 
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3# $Id: virtual_test_sheriff.py 103077 2024-01-26 08:40:12Z vboxsync $
4# pylint: disable=line-too-long
5
6"""
7Virtual Test Sheriff.
8
9Duties:
10 - Try to a assign failure reasons to recently failed tests.
11 - Reboot or disable bad test boxes.
12
13"""
14
15from __future__ import print_function;
16
17__copyright__ = \
18"""
19Copyright (C) 2012-2023 Oracle and/or its affiliates.
20
21This file is part of VirtualBox base platform packages, as
22available from https://www.virtualbox.org.
23
24This program is free software; you can redistribute it and/or
25modify it under the terms of the GNU General Public License
26as published by the Free Software Foundation, in version 3 of the
27License.
28
29This program is distributed in the hope that it will be useful, but
30WITHOUT ANY WARRANTY; without even the implied warranty of
31MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
32General Public License for more details.
33
34You should have received a copy of the GNU General Public License
35along with this program; if not, see <https://www.gnu.org/licenses>.
36
37The contents of this file may alternatively be used under the terms
38of the Common Development and Distribution License Version 1.0
39(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
40in the VirtualBox distribution, in which case the provisions of the
41CDDL are applicable instead of those of the GPL.
42
43You may elect to license modified versions of this file under the
44terms and conditions of either the GPL or the CDDL or both.
45
46SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
47"""
48__version__ = "$Revision: 103077 $"
49
50
51# Standard python imports
52import hashlib;
53import os;
54import re;
55import smtplib;
56#import subprocess;
57import sys;
58from email.mime.multipart import MIMEMultipart;
59from email.mime.text import MIMEText;
60from email.utils import COMMASPACE;
61
62if sys.version_info[0] >= 3:
63 from io import BytesIO as BytesIO; # pylint: disable=import-error,no-name-in-module,useless-import-alias
64else:
65 from StringIO import StringIO as BytesIO; # pylint: disable=import-error,no-name-in-module,useless-import-alias
66from optparse import OptionParser; # pylint: disable=deprecated-module
67from PIL import Image; # pylint: disable=import-error
68
69# Add Test Manager's modules path
70g_ksTestManagerDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))));
71sys.path.append(g_ksTestManagerDir);
72
73# Test Manager imports
74from common import utils;
75from testmanager.core.db import TMDatabaseConnection;
76from testmanager.core.build import BuildDataEx;
77from testmanager.core.failurereason import FailureReasonLogic;
78from testmanager.core.testbox import TestBoxLogic, TestBoxData;
79from testmanager.core.testcase import TestCaseDataEx;
80from testmanager.core.testgroup import TestGroupData;
81from testmanager.core.testset import TestSetLogic, TestSetData;
82from testmanager.core.testresults import TestResultLogic, TestResultFileData;
83from testmanager.core.testresultfailures import TestResultFailureLogic, TestResultFailureData;
84from testmanager.core.useraccount import UserAccountLogic;
85from testmanager.config import g_ksSmtpHost, g_kcSmtpPort, g_ksAlertFrom, \
86 g_ksAlertSubject, g_asAlertList #, g_ksLomPassword;
87
88# Python 3 hacks:
89if sys.version_info[0] >= 3:
90 xrange = range; # pylint: disable=redefined-builtin,invalid-name
91
92
93class VirtualTestSheriffCaseFile(object):
94 """
95 A failure investigation case file.
96
97 """
98
99
100 ## Max log file we'll read into memory. (256 MB)
101 kcbMaxLogRead = 0x10000000;
102
103 def __init__(self, oSheriff, oTestSet, oTree, oBuild, oTestBox, oTestGroup, oTestCase):
104 self.oSheriff = oSheriff;
105 self.oTestSet = oTestSet; # TestSetData
106 self.oTree = oTree; # TestResultDataEx
107 self.oBuild = oBuild; # BuildDataEx
108 self.oTestBox = oTestBox; # TestBoxData
109 self.oTestGroup = oTestGroup; # TestGroupData
110 self.oTestCase = oTestCase; # TestCaseDataEx
111 self.sMainLog = ''; # The main log file. Empty string if not accessible.
112 self.sSvcLog = ''; # The VBoxSVC log file. Empty string if not accessible.
113
114 # Generate a case file name.
115 self.sName = '#%u: %s' % (self.oTestSet.idTestSet, self.oTestCase.sName,)
116 self.sLongName = '#%u: "%s" on "%s" running %s %s (%s), "%s" by %s, using %s %s %s r%u' \
117 % ( self.oTestSet.idTestSet,
118 self.oTestCase.sName,
119 self.oTestBox.sName,
120 self.oTestBox.sOs,
121 self.oTestBox.sOsVersion,
122 self.oTestBox.sCpuArch,
123 self.oTestBox.sCpuName,
124 self.oTestBox.sCpuVendor,
125 self.oBuild.oCat.sProduct,
126 self.oBuild.oCat.sBranch,
127 self.oBuild.oCat.sType,
128 self.oBuild.iRevision, );
129
130 # Investigation notes.
131 self.tReason = None; # None or one of the ktReason_XXX constants.
132 self.dReasonForResultId = {}; # Reason assignments indexed by idTestResult.
133 self.dCommentForResultId = {}; # Comment assignments indexed by idTestResult.
134
135 #
136 # Reason.
137 #
138
139 def noteReason(self, tReason):
140 """ Notes down a possible reason. """
141 self.oSheriff.dprint(u'noteReason: %s -> %s' % (self.tReason, tReason,));
142 self.tReason = tReason;
143 return True;
144
145 def noteReasonForId(self, tReason, idTestResult, sComment = None):
146 """ Notes down a possible reason for a specific test result. """
147 self.oSheriff.dprint(u'noteReasonForId: %u: %s -> %s%s'
148 % (idTestResult, self.dReasonForResultId.get(idTestResult, None), tReason,
149 (u' (%s)' % (sComment,)) if sComment is not None else ''));
150 self.dReasonForResultId[idTestResult] = tReason;
151 if sComment is not None:
152 self.dCommentForResultId[idTestResult] = sComment;
153 return True;
154
155
156 #
157 # Test classification.
158 #
159
160 def isVBoxTest(self):
161 """ Test classification: VirtualBox (using the build) """
162 return self.oBuild.oCat.sProduct.lower() in [ 'virtualbox', 'vbox' ];
163
164 def isVBoxUnitTest(self):
165 """ Test case classification: The unit test doing all our testcase/*.cpp stuff. """
166 return self.isVBoxTest() \
167 and (self.oTestCase.sName.lower() == 'unit tests' or self.oTestCase.sName.lower().startswith('misc: unit tests'));
168
169 def isVBoxInstallTest(self):
170 """ Test case classification: VirtualBox Guest installation test. """
171 return self.isVBoxTest() \
172 and self.oTestCase.sName.lower().startswith('install:');
173
174 def isVBoxUnattendedInstallTest(self):
175 """ Test case classification: VirtualBox Guest installation test. """
176 return self.isVBoxTest() \
177 and self.oTestCase.sName.lower().startswith('uinstall:');
178
179 def isVBoxUSBTest(self):
180 """ Test case classification: VirtualBox USB test. """
181 return self.isVBoxTest() \
182 and self.oTestCase.sName.lower().startswith('usb:');
183
184 def isVBoxStorageTest(self):
185 """ Test case classification: VirtualBox Storage test. """
186 return self.isVBoxTest() \
187 and self.oTestCase.sName.lower().startswith('storage:');
188
189 def isVBoxGAsTest(self):
190 """ Test case classification: VirtualBox Guest Additions test. """
191 return self.isVBoxTest() \
192 and ( self.oTestCase.sName.lower().startswith('guest additions')
193 or self.oTestCase.sName.lower().startswith('ga\'s tests'));
194
195 def isVBoxAPITest(self):
196 """ Test case classification: VirtualBox API test. """
197 return self.isVBoxTest() \
198 and self.oTestCase.sName.lower().startswith('api:');
199
200 def isVBoxBenchmarkTest(self):
201 """ Test case classification: VirtualBox Benchmark test. """
202 return self.isVBoxTest() \
203 and self.oTestCase.sName.lower().startswith('benchmark:');
204
205 def isVBoxSmokeTest(self):
206 """ Test case classification: Smoke test. """
207 return self.isVBoxTest() \
208 and self.oTestCase.sName.lower().startswith('smoketest');
209
210 def isVBoxSerialTest(self):
211 """ Test case classification: Smoke test. """
212 return self.isVBoxTest() \
213 and self.oTestCase.sName.lower().startswith('serial:');
214
215
216 #
217 # Utility methods.
218 #
219
220 def getMainLog(self):
221 """
222 Tries to read the main log file since this will be the first source of information.
223 """
224 if self.sMainLog:
225 return self.sMainLog;
226 (oFile, oSizeOrError, _) = self.oTestSet.openFile('main.log', 'rb');
227 if oFile is not None:
228 try:
229 self.sMainLog = oFile.read(min(self.kcbMaxLogRead, oSizeOrError)).decode('utf-8', 'replace');
230 except Exception as oXcpt:
231 self.oSheriff.vprint(u'Error reading main log file: %s' % (oXcpt,))
232 self.sMainLog = '';
233 else:
234 self.oSheriff.vprint(u'Error opening main log file: %s' % (oSizeOrError,));
235 return self.sMainLog;
236
237 def getLogFile(self, oFile):
238 """
239 Tries to read the given file as a utf-8 log file.
240 oFile is a TestFileDataEx instance.
241 Returns empty string if problems opening or reading the file.
242 """
243 sContent = '';
244 (oFile, oSizeOrError, _) = self.oTestSet.openFile(oFile.sFile, 'rb');
245 if oFile is not None:
246 try:
247 sContent = oFile.read(min(self.kcbMaxLogRead, oSizeOrError)).decode('utf-8', 'replace');
248 except Exception as oXcpt:
249 self.oSheriff.vprint(u'Error reading the "%s" log file: %s' % (oFile.sFile, oXcpt,))
250 else:
251 self.oSheriff.vprint(u'Error opening the "%s" log file: %s' % (oFile.sFile, oSizeOrError,));
252 return sContent;
253
254 def getSvcLog(self):
255 """
256 Tries to read the VBoxSVC log file as it typically not associated with a failing test result.
257 Note! Returns the first VBoxSVC log file we find.
258 """
259 if not self.sSvcLog:
260 aoSvcLogFiles = self.oTree.getListOfLogFilesByKind(TestResultFileData.ksKind_LogReleaseSvc);
261 if aoSvcLogFiles:
262 self.sSvcLog = self.getLogFile(aoSvcLogFiles[0]);
263 return self.sSvcLog;
264
265 def getScreenshotSha256(self, oFile):
266 """
267 Tries to read the given screenshot file, uncompress it, and do SHA-2
268 on the raw pixels.
269 Returns SHA-2 digest string on success, None on failure.
270 """
271 (oImgFile, _, _) = self.oTestSet.openFile(oFile.sFile, 'rb');
272 try:
273 abImageFile = oImgFile.read();
274 except Exception as oXcpt:
275 self.oSheriff.vprint(u'Error reading the "%s" image file: %s' % (oFile.sFile, oXcpt,))
276 else:
277 try:
278 oImage = Image.open(BytesIO(abImageFile));
279 except Exception as oXcpt:
280 self.oSheriff.vprint(u'Error opening the "%s" image bytes using PIL.Image.open: %s' % (oFile.sFile, oXcpt,))
281 else:
282 try:
283 oHash = hashlib.sha256();
284 if hasattr(oImage, 'tobytes'):
285 oHash.update(oImage.tobytes());
286 else:
287 oHash.update(oImage.tostring()); # pylint: disable=no-member
288 except Exception as oXcpt:
289 self.oSheriff.vprint(u'Error hashing the uncompressed image bytes for "%s": %s' % (oFile.sFile, oXcpt,))
290 else:
291 return oHash.hexdigest();
292 return None;
293
294
295
296 def isSingleTestFailure(self):
297 """
298 Figure out if this is a single test failing or if it's one of the
299 more complicated ones.
300 """
301 if self.oTree.cErrors == 1:
302 return True;
303 if self.oTree.deepCountErrorContributers() <= 1:
304 return True;
305 return False;
306
307
308
309class VirtualTestSheriff(object): # pylint: disable=too-few-public-methods
310 """
311 Add build info into Test Manager database.
312 """
313
314 ## The user account for the virtual sheriff.
315 ksLoginName = 'vsheriff';
316
317 def __init__(self):
318 """
319 Parse command line.
320 """
321 self.oDb = None;
322 self.tsNow = None;
323 self.oTestResultLogic = None;
324 self.oTestSetLogic = None;
325 self.oFailureReasonLogic = None; # FailureReasonLogic;
326 self.oTestResultFailureLogic = None; # TestResultFailureLogic
327 self.oLogin = None;
328 self.uidSelf = -1;
329 self.oLogFile = None;
330 self.asBsodReasons = [];
331 self.asUnitTestReasons = [];
332
333 oParser = OptionParser();
334 oParser.add_option('--start-hours-ago', dest = 'cStartHoursAgo', metavar = '<hours>', default = 0, type = 'int',
335 help = 'When to start specified as hours relative to current time. Defauls is right now.', );
336 oParser.add_option('--hours-period', dest = 'cHoursBack', metavar = '<period-in-hours>', default = 2, type = 'int',
337 help = 'Work period specified in hours. Defauls is 2 hours.');
338 oParser.add_option('--real-run-back', dest = 'fRealRun', action = 'store_true', default = False,
339 help = 'Whether to commit the findings to the database. Default is a dry run.');
340 oParser.add_option('--testset', dest = 'aidTestSets', metavar = '<id>', default = [], type = 'int', action = 'append',
341 help = 'Only investigate this one. Accumulates IDs when repeated.');
342 oParser.add_option('-q', '--quiet', dest = 'fQuiet', action = 'store_true', default = False,
343 help = 'Quiet execution');
344 oParser.add_option('-l', '--log', dest = 'sLogFile', metavar = '<logfile>', default = None,
345 help = 'Where to log messages.');
346 oParser.add_option('--debug', dest = 'fDebug', action = 'store_true', default = False,
347 help = 'Enables debug mode.');
348
349 (self.oConfig, _) = oParser.parse_args();
350
351 if self.oConfig.sLogFile:
352 self.oLogFile = open(self.oConfig.sLogFile, "a"); # pylint: disable=consider-using-with,unspecified-encoding
353 self.oLogFile.write('VirtualTestSheriff: $Revision: 103077 $ \n');
354
355
356 def eprint(self, sText):
357 """
358 Prints error messages.
359 Returns 1 (for exit code usage.)
360 """
361 print('error: %s' % (sText,));
362 if self.oLogFile is not None:
363 if sys.version_info[0] >= 3:
364 self.oLogFile.write(u'error: %s\n' % (sText,));
365 else:
366 self.oLogFile.write((u'error: %s\n' % (sText,)).encode('utf-8'));
367 return 1;
368
369 def dprint(self, sText):
370 """
371 Prints debug info.
372 """
373 if self.oConfig.fDebug:
374 if not self.oConfig.fQuiet:
375 print('debug: %s' % (sText, ));
376 if self.oLogFile is not None:
377 if sys.version_info[0] >= 3:
378 self.oLogFile.write(u'debug: %s\n' % (sText,));
379 else:
380 self.oLogFile.write((u'debug: %s\n' % (sText,)).encode('utf-8'));
381 return 0;
382
383 def vprint(self, sText):
384 """
385 Prints verbose info.
386 """
387 if not self.oConfig.fQuiet:
388 print('info: %s' % (sText,));
389 if self.oLogFile is not None:
390 if sys.version_info[0] >= 3:
391 self.oLogFile.write(u'info: %s\n' % (sText,));
392 else:
393 self.oLogFile.write((u'info: %s\n' % (sText,)).encode('utf-8'));
394 return 0;
395
396 def getFailureReason(self, tReason):
397 """ Gets the failure reason object for tReason. """
398 return self.oFailureReasonLogic.cachedLookupByNameAndCategory(tReason[1], tReason[0]);
399
400 def selfCheck(self):
401 """ Does some self checks, looking up things we expect to be in the database and such. """
402 rcExit = 0;
403 for sAttr in dir(self.__class__):
404 if sAttr.startswith('ktReason_'):
405 tReason = getattr(self.__class__, sAttr);
406 oFailureReason = self.getFailureReason(tReason);
407 if oFailureReason is None:
408 rcExit = self.eprint(u'Failed to find failure reason "%s" in category "%s" in the database!'
409 % (tReason[1], tReason[0],));
410
411 # Check the user account as well.
412 if self.oLogin is None:
413 oLogin = UserAccountLogic(self.oDb).tryFetchAccountByLoginName(VirtualTestSheriff.ksLoginName);
414 if oLogin is None:
415 rcExit = self.eprint(u'Cannot find my user account "%s"!' % (VirtualTestSheriff.ksLoginName,));
416 return rcExit;
417
418 def sendEmailAlert(self, uidAuthor, sBodyText):
419 """
420 Sends email alert.
421 """
422
423 # Get author email
424 self.oDb.execute('SELECT sEmail FROM Users WHERE uid=%s', (uidAuthor,));
425 sFrom = self.oDb.fetchOne();
426 if sFrom is not None:
427 sFrom = sFrom[0];
428 else:
429 sFrom = g_ksAlertFrom;
430
431 # Gather recipient list.
432 asEmailList = [];
433 for sUser in g_asAlertList:
434 self.oDb.execute('SELECT sEmail FROM Users WHERE sUsername=%s', (sUser,));
435 sEmail = self.oDb.fetchOne();
436 if sEmail:
437 asEmailList.append(sEmail[0]);
438 if not asEmailList:
439 return self.eprint('No email addresses to send alter to!');
440
441 # Compose the message.
442 oMsg = MIMEMultipart();
443 oMsg['From'] = sFrom;
444 oMsg['To'] = COMMASPACE.join(asEmailList);
445 oMsg['Subject'] = g_ksAlertSubject;
446 oMsg.attach(MIMEText(sBodyText, 'plain'))
447
448 # Try send it.
449 try:
450 oSMTP = smtplib.SMTP(g_ksSmtpHost, g_kcSmtpPort);
451 oSMTP.sendmail(sFrom, asEmailList, oMsg.as_string())
452 oSMTP.quit()
453 except smtplib.SMTPException as oXcpt:
454 return self.eprint('Failed to send mail: %s' % (oXcpt,));
455
456 return 0;
457
458 def badTestBoxManagement(self):
459 """
460 Looks for bad test boxes and first tries once to reboot them then disables them.
461 """
462 rcExit = 0;
463
464 #
465 # We skip this entirely if we're running in the past and not in harmless debug mode.
466 #
467 if self.oConfig.cStartHoursAgo != 0 \
468 and (not self.oConfig.fDebug or self.oConfig.fRealRun):
469 return rcExit;
470 tsNow = self.tsNow if self.oConfig.fDebug else None;
471 cHoursBack = self.oConfig.cHoursBack if self.oConfig.fDebug else 2;
472 oTestBoxLogic = TestBoxLogic(self.oDb);
473
474 #
475 # Generate a list of failures reasons we consider bad-testbox behavior.
476 #
477 aidFailureReasons = [
478 self.getFailureReason(self.ktReason_Host_DriverNotLoaded).idFailureReason,
479 self.getFailureReason(self.ktReason_Host_DriverNotUnloading).idFailureReason,
480 self.getFailureReason(self.ktReason_Host_DriverNotCompilable).idFailureReason,
481 self.getFailureReason(self.ktReason_Host_InstallationFailed).idFailureReason,
482 ];
483
484 #
485 # Get list of bad test boxes for given period and check them out individually.
486 #
487 aidBadTestBoxes = self.oTestSetLogic.fetchBadTestBoxIds(cHoursBack = cHoursBack, tsNow = tsNow,
488 aidFailureReasons = aidFailureReasons);
489 for idTestBox in aidBadTestBoxes:
490 # Skip if the testbox is already disabled or has a pending reboot command.
491 try:
492 oTestBox = TestBoxData().initFromDbWithId(self.oDb, idTestBox);
493 except Exception as oXcpt:
494 rcExit = self.eprint('Failed to get data for test box #%u in badTestBoxManagement: %s' % (idTestBox, oXcpt,));
495 continue;
496 if not oTestBox.fEnabled:
497 self.dprint(u'badTestBoxManagement: Skipping test box #%u (%s) as it has been disabled already.'
498 % ( idTestBox, oTestBox.sName, ));
499 continue;
500 if oTestBox.enmPendingCmd != TestBoxData.ksTestBoxCmd_None:
501 self.dprint(u'badTestBoxManagement: Skipping test box #%u (%s) as it has a command pending: %s'
502 % ( idTestBox, oTestBox.sName, oTestBox.enmPendingCmd));
503 continue;
504
505 # Get the most recent testsets for this box (descending on tsDone) and see how bad it is.
506 aoSets = self.oTestSetLogic.fetchSetsForTestBox(idTestBox, cHoursBack = cHoursBack, tsNow = tsNow);
507 cOkay = 0;
508 cBad = 0;
509 iFirstOkay = len(aoSets);
510 for iSet, oSet in enumerate(aoSets):
511 if oSet.enmStatus == TestSetData.ksTestStatus_BadTestBox:
512 cBad += 1;
513 else:
514 # Check for bad failure reasons.
515 oFailure = None;
516 if oSet.enmStatus in TestSetData.kasBadTestStatuses:
517 (oTree, _ ) = self.oTestResultLogic.fetchResultTree(oSet.idTestSet)
518 aoFailedResults = oTree.getListOfFailures();
519 for oFailedResult in aoFailedResults:
520 oFailure = self.oTestResultFailureLogic.getById(oFailedResult.idTestResult);
521 if oFailure is not None and oFailure.idFailureReason in aidFailureReasons:
522 break;
523 oFailure = None;
524 if oFailure is not None:
525 cBad += 1;
526 else:
527 # This is an okay test result then.
528 ## @todo maybe check the elapsed time here, it could still be a bad run?
529 cOkay += 1;
530 iFirstOkay = min(iFirstOkay, iSet);
531 if iSet > 10:
532 break;
533
534 # We react if there are two or more bad-testbox statuses at the head of the
535 # history and at least three in the last 10 results.
536 if iFirstOkay >= 2 and cBad > 2:
537 if oTestBoxLogic.hasTestBoxRecentlyBeenRebooted(idTestBox, cHoursBack = cHoursBack, tsNow = tsNow):
538 sComment = u'Disabling testbox #%u (%s) - iFirstOkay=%u cBad=%u cOkay=%u' \
539 % (idTestBox, oTestBox.sName, iFirstOkay, cBad, cOkay);
540 self.vprint(sComment);
541 self.sendEmailAlert(self.uidSelf, sComment);
542 if self.oConfig.fRealRun is True:
543 try:
544 oTestBoxLogic.disableTestBox(idTestBox, self.uidSelf, fCommit = True,
545 sComment = 'Automatically disabled (iFirstOkay=%u cBad=%u cOkay=%u)'
546 % (iFirstOkay, cBad, cOkay),);
547 except Exception as oXcpt:
548 rcExit = self.eprint(u'Error disabling testbox #%u (%u): %s\n' % (idTestBox, oTestBox.sName, oXcpt,));
549 else:
550 sComment = u'Rebooting testbox #%u (%s) - iFirstOkay=%u cBad=%u cOkay=%u' \
551 % (idTestBox, oTestBox.sName, iFirstOkay, cBad, cOkay);
552 self.vprint(sComment);
553 self.sendEmailAlert(self.uidSelf, sComment);
554 if self.oConfig.fRealRun is True:
555 try:
556 oTestBoxLogic.rebootTestBox(idTestBox, self.uidSelf, fCommit = True,
557 sComment = 'Automatically rebooted (iFirstOkay=%u cBad=%u cOkay=%u)'
558 % (iFirstOkay, cBad, cOkay),);
559 except Exception as oXcpt:
560 rcExit = self.eprint(u'Error rebooting testbox #%u (%s): %s\n' % (idTestBox, oTestBox.sName, oXcpt,));
561 else:
562 self.dprint(u'badTestBoxManagement: #%u (%s) looks ok: iFirstOkay=%u cBad=%u cOkay=%u'
563 % ( idTestBox, oTestBox.sName, iFirstOkay, cBad, cOkay));
564
565 ## @todo r=bird: review + rewrite;
566 ## - no selecting here, that belongs in the core/*.py files.
567 ## - preserve existing comments.
568 ## - doing way too much in the try/except block.
569 ## - No password quoting in the sshpass command that always fails (127).
570 ## - Timeout is way to low. testboxmem1 need more than 10 min to take a dump, ages to
571 ## get thru POST and another 5 just to time out in grub. Should be an hour or so.
572 ## Besides, it need to be constant elsewhere in the file, not a variable here.
573 ##
574 ##
575 ## Reset hanged testboxes
576 ##
577 #cStatusTimeoutMins = 10;
578 #
579 #self.oDb.execute('SELECT TestBoxStatuses.idTestBox\n'
580 # ' FROM TestBoxStatuses, TestBoxes\n'
581 # ' WHERE TestBoxStatuses.tsUpdated >= (CURRENT_TIMESTAMP - interval \'%s hours\')\n'
582 # ' AND TestBoxStatuses.tsUpdated < (CURRENT_TIMESTAMP - interval \'%s minutes\')\n'
583 # ' AND TestBoxStatuses.idTestBox = TestBoxes.idTestBox\n'
584 # ' AND Testboxes.tsExpire = \'infinity\'::timestamp', (cHoursBack,cStatusTimeoutMins));
585 #for idTestBox in self.oDb.fetchAll():
586 # idTestBox = idTestBox[0];
587 # try:
588 # oTestBox = TestBoxData().initFromDbWithId(self.oDb, idTestBox);
589 # except Exception as oXcpt:
590 # rcExit = self.eprint('Failed to get data for test box #%u in badTestBoxManagement: %s' % (idTestBox, oXcpt,));
591 # continue;
592 # # Skip if the testbox is already disabled, already reset or there's no iLOM
593 # if not oTestBox.fEnabled or oTestBox.ipLom is None or oTestBox.sComment is not None and oTestBox.sComment.find('Automatically reset') >= 0:
594 # self.dprint(u'badTestBoxManagement: Skipping test box #%u (%s) as it has been disabled already.'
595 # % ( idTestBox, oTestBox.sName, ));
596 # continue;
597 # ## @todo get iLOM credentials from a table?
598 # sCmd = 'sshpass -p%s ssh -oStrictHostKeyChecking=no root@%s show /SP && reset /SYS' % (g_ksLomPassword, oTestBox.ipLom,);
599 # try:
600 # oPs = subprocess.Popen(sCmd, stdout=subprocess.PIPE, shell=True);
601 # sStdout = oPs.communicate()[0];
602 # iRC = oPs.wait();
603 #
604 # oTestBox.sComment = 'Automatically reset (iRC=%u sStdout=%s)' % (iRC, sStdout,);
605 # oTestBoxLogic.editEntry(oTestBox, self.uidSelf, fCommit = True);
606 #
607 # sComment = u'Reset testbox #%u (%s) - iRC=%u sStduot=%s' % ( idTestBox, oTestBox.sName, iRC, sStdout);
608 # self.vprint(sComment);
609 # self.sendEmailAlert(self.uidSelf, sComment);
610 #
611 # except Exception as oXcpt:
612 # rcExit = self.eprint(u'Error resetting testbox #%u (%s): %s\n' % (idTestBox, oTestBox.sName, oXcpt,));
613 #
614 return rcExit;
615
616
617 ## @name Failure reasons we know.
618 ## @{
619
620 ktReason_Add_Installer_Win_Failed = ( 'Additions', 'Win GA install' );
621 ktReason_Add_ShFl = ( 'Additions', 'ShFl' );
622 ktReason_Add_ShFl_Automount = ( 'Additions', 'Automounting' );
623 ktReason_Add_ShFl_FsPerf = ( 'Additions', 'FsPerf' );
624 ktReason_Add_ShFl_FsPerf_Abend = ( 'Additions', 'FsPerf abend' );
625 ktReason_Add_GstCtl = ( 'Additions', 'GstCtl' );
626 ktReason_Add_GstCtl_Preparations = ( 'Additions', 'GstCtl preparations' );
627 ktReason_Add_GstCtl_SessionBasics = ( 'Additions', 'Session basics' );
628 ktReason_Add_GstCtl_SessionProcRefs = ( 'Additions', 'Session process' );
629 ktReason_Add_GstCtl_Session_Reboot = ( 'Additions', 'Session reboot' );
630 ktReason_Add_GstCtl_CopyFromGuest_Timeout = ( 'Additions', 'CopyFromGuest timeout' );
631 ktReason_Add_GstCtl_CopyToGuest_Timeout = ( 'Additions', 'CopyToGuest timeout' );
632 ktReason_Add_GstCtl_CopyToGuest_DstEmpty = ( 'Additions', 'CopyToGuest dst empty' );
633 ktReason_Add_GstCtl_CopyToGuest_DstExists = ( 'Additions', 'CopyToGuest dst exists' );
634 ktReason_Add_FlushViewOfFile = ( 'Additions', 'FlushViewOfFile' );
635 ktReason_Add_Mmap_Coherency = ( 'Additions', 'mmap coherency' );
636 ktReason_BSOD_Recovery = ( 'BSOD', 'Recovery' );
637 ktReason_BSOD_Automatic_Repair = ( 'BSOD', 'Automatic Repair' );
638 ktReason_BSOD_0000007F = ( 'BSOD', '0x0000007F' );
639 ktReason_BSOD_000000D1 = ( 'BSOD', '0x000000D1' );
640 ktReason_BSOD_C0000225 = ( 'BSOD', '0xC0000225 (boot)' );
641 ktReason_Guru_Generic = ( 'Guru Meditations', 'Generic Guru Meditation' );
642 ktReason_Guru_VERR_IEM_INSTR_NOT_IMPLEMENTED = ( 'Guru Meditations', 'VERR_IEM_INSTR_NOT_IMPLEMENTED' );
643 ktReason_Guru_VERR_IEM_ASPECT_NOT_IMPLEMENTED = ( 'Guru Meditations', 'VERR_IEM_ASPECT_NOT_IMPLEMENTED' );
644 ktReason_Guru_VERR_TRPM_DONT_PANIC = ( 'Guru Meditations', 'VERR_TRPM_DONT_PANIC' );
645 ktReason_Guru_VERR_PGM_PHYS_PAGE_RESERVED = ( 'Guru Meditations', 'VERR_PGM_PHYS_PAGE_RESERVED' );
646 ktReason_Guru_VERR_VMX_INVALID_GUEST_STATE = ( 'Guru Meditations', 'VERR_VMX_INVALID_GUEST_STATE' );
647 ktReason_Guru_VINF_EM_TRIPLE_FAULT = ( 'Guru Meditations', 'VINF_EM_TRIPLE_FAULT' );
648 ktReason_Host_HostMemoryLow = ( 'Host', 'HostMemoryLow' );
649 ktReason_Host_DriverNotLoaded = ( 'Host', 'Driver not loaded' );
650 ktReason_Host_DriverNotUnloading = ( 'Host', 'Driver not unloading' );
651 ktReason_Host_DriverNotCompilable = ( 'Host', 'Driver not compilable' );
652 ktReason_Host_InstallationFailed = ( 'Host', 'Installation failed' );
653 ktReason_Host_InstallationWantReboot = ( 'Host', 'Installation want reboot' );
654 ktReason_Host_InvalidPackage = ( 'Host', 'ERROR_INSTALL_PACKAGE_INVALID' );
655 ktReason_Host_InstallSourceAbsent = ( 'Host', 'ERROR_INSTALL_SOURCE_ABSENT' );
656 ktReason_Host_Install_Hang = ( 'Host', 'Install hang' );
657 ktReason_Host_NotSignedWithBuildCert = ( 'Host', 'Not signed with build cert' );
658 ktReason_Host_DiskFull = ( 'Host', 'Host disk full' );
659 ktReason_Host_DoubleFreeHeap = ( 'Host', 'Double free or corruption' );
660 ktReason_Host_LeftoverService = ( 'Host', 'Leftover service' );
661 ktReason_Host_win32com_gen_py_not_found = ( 'Host', 'win32com.gen_py not found' );
662 ktReason_Host_Reboot_OSX_Watchdog_Timeout = ( 'Host Reboot', 'OSX Watchdog Timeout' );
663 ktReason_Host_Modprobe_Failed = ( 'Host', 'Modprobe failed' );
664 ktReason_Host_NetworkMisconfiguration = ( 'Host', 'Network misconfiguration' );
665 ktReason_Host_Python_vboxapi_not_found = ( 'Host', 'Python API (vboxapi) not found' );
666 ktReason_Host_Python_xpcom_not_found = ( 'Host', 'Python API (xpcom) not found' );
667 ktReason_Host_TSTInfo_Accuracy_OOR = ( 'Host', 'TSTInfo accuracy out of range' );
668 ktReason_Host_UninstallationFailed = ( 'Host', 'Uninstallation failed' );
669 ktReason_Networking_Nonexistent_host_nic = ( 'Networking', 'Nonexistent host networking interface' );
670 ktReason_Networking_VERR_INTNET_FLT_IF_NOT_FOUND = ( 'Networking', 'VERR_INTNET_FLT_IF_NOT_FOUND' );
671 ktReason_OSInstall_GRUB_hang = ( 'O/S Install', 'GRUB hang' );
672 ktReason_OSInstall_Udev_hang = ( 'O/S Install', 'udev hang' );
673 ktReason_OSInstall_Sata_no_BM = ( 'O/S Install', 'SATA busmaster bit not set' );
674 ktReason_Panic_BootManagerC000000F = ( 'Panic', 'Hardware Changed' );
675 ktReason_Panic_MP_BIOS_IO_APIC = ( 'Panic', 'MP-BIOS/IO-APIC' );
676 ktReason_Panic_HugeMemory = ( 'Panic', 'Huge memory assertion' );
677 ktReason_Panic_IOAPICDoesntWork = ( 'Panic', 'IO-APIC and timer does not work' );
678 ktReason_Panic_TxUnitHang = ( 'Panic', 'Tx Unit Hang' );
679 ktReason_API_std_bad_alloc = ( 'API / (XP)COM', 'std::bad_alloc' );
680 ktReason_API_Digest_Mismatch = ( 'API / (XP)COM', 'Digest mismatch' );
681 ktReason_API_MoveVM_SharingViolation = ( 'API / (XP)COM', 'MoveVM sharing violation' );
682 ktReason_API_MoveVM_InvalidParameter = ( 'API / (XP)COM', 'MoveVM invalid parameter' );
683 ktReason_API_Open_Session_Failed = ( 'API / (XP)COM', 'Open session failed' );
684 ktReason_XPCOM_Exit_Minus_11 = ( 'API / (XP)COM', 'exit -11' );
685 ktReason_XPCOM_VBoxSVC_Hang = ( 'API / (XP)COM', 'VBoxSVC hang' );
686 ktReason_XPCOM_VBoxSVC_Hang_Plus_Heap_Corruption = ( 'API / (XP)COM', 'VBoxSVC hang + heap corruption' );
687 ktReason_XPCOM_NS_ERROR_CALL_FAILED = ( 'API / (XP)COM', 'NS_ERROR_CALL_FAILED' );
688 ktReason_BootManager_Image_corrupt = ( 'Unknown', 'BOOTMGR Image corrupt' );
689 ktReason_Unknown_Heap_Corruption = ( 'Unknown', 'Heap corruption' );
690 ktReason_Unknown_Reboot_Loop = ( 'Unknown', 'Reboot loop' );
691 ktReason_Unknown_File_Not_Found = ( 'Unknown', 'File not found' );
692 ktReason_Unknown_HalReturnToFirmware = ( 'Unknown', 'HalReturnToFirmware' );
693 ktReason_Unknown_VM_Crash = ( 'Unknown', 'VM crash' );
694 ktReason_Unknown_VM_Terminated = ( 'Unknown', 'VM terminated' );
695 ktReason_Unknown_VM_Start_Error = ( 'Unknown', 'VM Start Error' );
696 ktReason_Unknown_VM_Runtime_Error = ( 'Unknown', 'VM Runtime Error' );
697 ktReason_VMM_Assert = ( 'VMM', 'Assert' );
698 ktReason_VMM_kvm_lock_spinning = ( 'VMM', 'kvm_lock_spinning' );
699 ktReason_Ignore_Buggy_Test_Driver = ( 'Ignore', 'Buggy test driver' );
700 ktReason_Ignore_Stale_Files = ( 'Ignore', 'Stale files' );
701 ktReason_Buggy_Build_Broken_Build = ( 'Broken Build', 'Buggy build' );
702 ktReason_GuestBug_CompizVBoxQt = ( 'Guest Bug', 'Compiz + VirtualBox Qt GUI crash' );
703 ## @}
704
705 ## BSOD category.
706 ksBsodCategory = 'BSOD';
707 ## Special reason indicating that the flesh and blood sheriff has work to do.
708 ksBsodAddNew = 'Add new BSOD';
709
710 ## Unit test category.
711 ksUnitTestCategory = 'Unit';
712 ## Special reason indicating that the flesh and blood sheriff has work to do.
713 ksUnitTestAddNew = 'Add new';
714
715 ## Used for indica that we shouldn't report anything for this test result ID and
716 ## consider promoting the previous error to test set level if it's the only one.
717 ktHarmless = ( 'Probably', 'Caused by previous error' );
718
719
720 def caseClosed(self, oCaseFile):
721 """
722 Reports the findings in the case and closes it.
723 """
724 #
725 # Log it and create a dReasonForReasultId we can use below.
726 #
727 dCommentForResultId = oCaseFile.dCommentForResultId;
728 if oCaseFile.dReasonForResultId:
729 # Must weed out ktHarmless.
730 dReasonForResultId = {};
731 for idKey, tReason in oCaseFile.dReasonForResultId.items():
732 if tReason is not self.ktHarmless:
733 dReasonForResultId[idKey] = tReason;
734 if not dReasonForResultId:
735 self.vprint(u'TODO: Closing %s without a real reason, only %s.'
736 % (oCaseFile.sName, oCaseFile.dReasonForResultId));
737 return False;
738
739 # Try promote to single reason.
740 atValues = dReasonForResultId.values();
741 fSingleReason = True;
742 if len(dReasonForResultId) == 1 and next(iter(dReasonForResultId.keys())) != oCaseFile.oTestSet.idTestResult:
743 self.dprint(u'Promoting single reason to whole set: %s' % (next(iter(atValues)),));
744 elif len(dReasonForResultId) > 1 and len(atValues) == list(atValues).count(next(iter(atValues))):
745 self.dprint(u'Merged %d reasons to a single one: %s' % (len(atValues), next(iter(atValues))));
746 else:
747 fSingleReason = False;
748 if fSingleReason:
749 dReasonForResultId = { oCaseFile.oTestSet.idTestResult: next(iter(atValues)), };
750 if dCommentForResultId:
751 dCommentForResultId = { oCaseFile.oTestSet.idTestResult: next(iter(dCommentForResultId.values())), };
752 elif oCaseFile.tReason is not None:
753 dReasonForResultId = { oCaseFile.oTestSet.idTestResult: oCaseFile.tReason, };
754 else:
755 self.vprint(u'Closing %s without a reason - this should not happen!' % (oCaseFile.sName,));
756 return False;
757
758 self.vprint(u'Closing %s with following reason%s: %s'
759 % ( oCaseFile.sName, 's' if len(dReasonForResultId) > 1 else '', dReasonForResultId, ));
760
761 #
762 # Add the test failure reason record(s).
763 #
764 for idTestResult, tReason in dReasonForResultId.items():
765 oFailureReason = self.getFailureReason(tReason);
766 if oFailureReason is not None:
767 sComment = 'Set by $Revision: 103077 $' # Handy for reverting later.
768 if idTestResult in dCommentForResultId:
769 sComment += ': ' + dCommentForResultId[idTestResult];
770
771 oAdd = TestResultFailureData();
772 oAdd.initFromValues(idTestResult = idTestResult,
773 idFailureReason = oFailureReason.idFailureReason,
774 uidAuthor = self.uidSelf,
775 idTestSet = oCaseFile.oTestSet.idTestSet,
776 sComment = sComment,);
777 if self.oConfig.fRealRun:
778 try:
779 self.oTestResultFailureLogic.addEntry(oAdd, self.uidSelf, fCommit = True);
780 except Exception as oXcpt:
781 self.eprint(u'caseClosed: Exception "%s" while adding reason %s for %s'
782 % (oXcpt, oAdd, oCaseFile.sLongName,));
783 else:
784 self.eprint(u'caseClosed: Cannot locate failure reason: %s / %s' % ( tReason[0], tReason[1],));
785 return True;
786
787 #
788 # Tools for assiting log parsing.
789 #
790
791 @staticmethod
792 def matchFollowedByLines(sStr, off, asFollowingLines):
793 """ Worker for isThisFollowedByTheseLines. """
794
795 # Advance off to the end of the line.
796 off = sStr.find('\n', off);
797 if off < 0:
798 return False;
799 off += 1;
800
801 # Match each string with the subsequent lines.
802 for iLine, sLine in enumerate(asFollowingLines):
803 offEnd = sStr.find('\n', off);
804 if offEnd < 0:
805 return iLine + 1 == len(asFollowingLines) and sStr.find(sLine, off) < 0;
806 if sLine and sStr.find(sLine, off, offEnd) < 0:
807 return False;
808
809 # next line.
810 off = offEnd + 1;
811
812 return True;
813
814 @staticmethod
815 def isThisFollowedByTheseLines(sStr, sFirst, asFollowingLines):
816 """
817 Looks for a line contining sFirst which is then followed by lines
818 with the strings in asFollowingLines. (No newline chars anywhere!)
819 Returns True / False.
820 """
821 off = sStr.find(sFirst, 0);
822 while off >= 0:
823 if VirtualTestSheriff.matchFollowedByLines(sStr, off, asFollowingLines):
824 return True;
825 off = sStr.find(sFirst, off + 1);
826 return False;
827
828 @staticmethod
829 def findAndReturnRestOfLine(sHaystack, sNeedle):
830 """
831 Looks for sNeedle in sHaystack.
832 Returns The text following the needle up to the end of the line.
833 Returns None if not found.
834 """
835 if sHaystack is None:
836 return None;
837 off = sHaystack.find(sNeedle);
838 if off < 0:
839 return None;
840 off += len(sNeedle)
841 offEol = sHaystack.find('\n', off);
842 if offEol < 0:
843 offEol = len(sHaystack);
844 return sHaystack[off:offEol]
845
846 @staticmethod
847 def findInAnyAndReturnRestOfLine(asHaystacks, sNeedle):
848 """
849 Looks for sNeedle in zeroe or more haystacks (asHaystack).
850 Returns The text following the first needed found up to the end of the line.
851 Returns None if not found.
852 """
853 for sHaystack in asHaystacks:
854 sRet = VirtualTestSheriff.findAndReturnRestOfLine(sHaystack, sNeedle);
855 if sRet is not None:
856 return sRet;
857 return None;
858
859
860 #
861 # The investigative units.
862 #
863
864 katSimpleInstallUninstallMainLogReasons = [
865 # ( Whether to stop on hit, reason tuple, needle text. )
866 ( False, ktReason_Host_LeftoverService,
867 'SERVICE_NAME: vbox' ),
868 ( False, ktReason_Host_LeftoverService,
869 'Seems installation was skipped. Old version lurking behind? Not the fault of this build/test run!'),
870 ];
871
872 kdatSimpleInstallUninstallMainLogReasonsPerOs = {
873 'darwin': [
874 # ( Whether to stop on hit, reason tuple, needle text. )
875 ( True, ktReason_Host_DriverNotUnloading,
876 'Can\'t remove kext org.virtualbox.kext.VBoxDrv; services failed to terminate - 0xe00002c7' ),
877 ],
878 'linux': [
879 # ( Whether to stop on hit, reason tuple, needle text. )
880 ( True, ktReason_Host_DriverNotCompilable,
881 'This system is not currently set up to build kernel modules' ),
882 ( True, ktReason_Host_DriverNotCompilable,
883 'This system is currently not set up to build kernel modules' ),
884 ( True, ktReason_Host_InstallationFailed,
885 'vboxdrv.sh: failed: Look at /var/log/vbox-install.log to find out what went wrong.' ),
886 ( True, ktReason_Host_DriverNotUnloading,
887 'Cannot unload module vboxdrv'),
888 ],
889 'solaris': [
890 # ( Whether to stop on hit, reason tuple, needle text. )
891 ( True, ktReason_Host_DriverNotUnloading, 'can\'t unload the module: Device busy' ),
892 ( True, ktReason_Host_DriverNotUnloading, 'Unloading: Host module ...FAILED!' ),
893 ( True, ktReason_Host_DriverNotUnloading, 'Unloading: NetFilter (Crossbow) module ...FAILED!' ),
894 ( True, ktReason_Host_InstallationFailed, 'svcadm: Couldn\'t bind to svc.configd.' ),
895 ( True, ktReason_Host_InstallationFailed, 'pkgadd: ERROR: postinstall script did not complete successfully' ),
896 ],
897 'win': [
898 # ( Whether to stop on hit, reason tuple, needle text. )
899 ( True, ktReason_Host_InstallationWantReboot, 'ERROR_SUCCESS_REBOOT_REQUIRED' ),
900 ( False, ktReason_Host_InstallationFailed, 'Installation error.' ),
901 ( True, ktReason_Host_InvalidPackage, 'Uninstaller failed, exit code: 1620' ),
902 ( True, ktReason_Host_InstallSourceAbsent, 'Uninstaller failed, exit code: 1612' ),
903 ],
904 };
905
906
907 def investigateInstallUninstallFailure(self, oCaseFile, oFailedResult, sResultLog, fInstall):
908 """
909 Investigates an install or uninstall failure.
910
911 We lump the two together since the installation typically also performs
912 an uninstall first and will be seeing similar issues to the uninstall.
913 """
914 self.dprint(u'%s + %s <<\n%s\n<<' % (oFailedResult.tsCreated, oFailedResult.tsElapsed, sResultLog,));
915
916 if fInstall and oFailedResult.enmStatus == TestSetData.ksTestStatus_TimedOut:
917 oCaseFile.noteReasonForId(self.ktReason_Host_Install_Hang, oFailedResult.idTestResult)
918 return True;
919
920 atSimple = self.katSimpleInstallUninstallMainLogReasons;
921 if oCaseFile.oTestBox.sOs in self.kdatSimpleInstallUninstallMainLogReasonsPerOs:
922 atSimple = self.kdatSimpleInstallUninstallMainLogReasonsPerOs[oCaseFile.oTestBox.sOs] + atSimple;
923
924 fFoundSomething = False;
925 for fStopOnHit, tReason, sNeedle in atSimple:
926 if sResultLog.find(sNeedle) > 0:
927 oCaseFile.noteReasonForId(tReason, oFailedResult.idTestResult);
928 if fStopOnHit:
929 return True;
930 fFoundSomething = True;
931
932 # If we didn't find something specific, just add a general install/uninstall
933 # failure reason so it's easier to get an idea why a test failed when
934 # looking at the failure listing in the test manager.
935 if not fFoundSomething:
936 if fInstall:
937 oCaseFile.noteReasonForId(self.ktReason_Host_InstallationFailed, oFailedResult.idTestResult);
938 else:
939 oCaseFile.noteReasonForId(self.ktReason_Host_UninstallationFailed, oFailedResult.idTestResult);
940
941 return fFoundSomething if fFoundSomething else None;
942
943
944 def investigateBadTestBox(self, oCaseFile):
945 """
946 Checks out bad-testbox statuses.
947 """
948 _ = oCaseFile;
949 return False;
950
951
952 def investigateVBoxUnitTest(self, oCaseFile):
953 """
954 Checks out a VBox unittest problem.
955 """
956
957 # Determine if this is a host or guest run before we start.
958 fRunsInGuest = '(guest)' in oCaseFile.oTestCase.sName or 'selected VMs' in oCaseFile.oTestCase.sName;
959
960 #
961 # Process simple test case failures first, using their name as reason.
962 # We do the reason management just like for BSODs.
963 #
964 cRelevantOnes = 0;
965 sMainLog = oCaseFile.getMainLog();
966 aoFailedResults = oCaseFile.oTree.getListOfFailures();
967 for oFailedResult in aoFailedResults:
968 if oFailedResult is oCaseFile.oTree:
969 self.vprint('TODO: toplevel failure');
970 cRelevantOnes += 1
971
972 elif oFailedResult.sName == 'Installing VirtualBox':
973 sResultLog = TestSetData.extractLogSectionElapsed(sMainLog, oFailedResult.tsCreated, oFailedResult.tsElapsed);
974 self.investigateInstallUninstallFailure(oCaseFile, oFailedResult, sResultLog, fInstall = True)
975 cRelevantOnes += 1
976
977 elif oFailedResult.sName == 'Uninstalling VirtualBox':
978 sResultLog = TestSetData.extractLogSectionElapsed(sMainLog, oFailedResult.tsCreated, oFailedResult.tsElapsed);
979 self.investigateInstallUninstallFailure(oCaseFile, oFailedResult, sResultLog, fInstall = False)
980 cRelevantOnes += 1
981
982 elif oFailedResult.oParent is not None:
983 # Host: Get the 2nd level node because that's where we'll find the unit test name.
984 # Guest: Get the 6th level node.
985 aoParents = [];
986 oParent = oFailedResult.oParent;
987 while oParent is not None:
988 aoParents.insert(0, oParent);
989 oParent = oParent.oParent;
990 if not fRunsInGuest:
991 oFailedResult = aoParents[min(2, len(aoParents) - 1)];
992 else:
993 oFailedResult = aoParents[min(5, len(aoParents) - 1)];
994
995 # Only report a failure once.
996 if oFailedResult.idTestResult not in oCaseFile.dReasonForResultId:
997 sKey = oFailedResult.sName;
998 if sKey.startswith('testcase/'):
999 sKey = sKey[9:];
1000 if sKey in self.asUnitTestReasons:
1001 tReason = ( self.ksUnitTestCategory, sKey );
1002 oCaseFile.noteReasonForId(tReason, oFailedResult.idTestResult);
1003 else:
1004 self.dprint(u'Unit test failure "%s" not found in %s;' % (sKey, self.asUnitTestReasons));
1005 sResultLog = TestSetData.extractLogSectionElapsed(sMainLog, oFailedResult.tsCreated,
1006 oFailedResult.tsElapsed);
1007 if 'AudioMixer.cpp' in sResultLog: # Pipe drain assertion.
1008 tReason = self.ktReason_VMM_Assert;
1009 oCaseFile.noteReasonForId(tReason, oFailedResult.idTestResult, sComment = 'AudioMixer.cpp');
1010 else:
1011 tReason = ( self.ksUnitTestCategory, self.ksUnitTestAddNew );
1012 oCaseFile.noteReasonForId(tReason, oFailedResult.idTestResult, sComment = sKey);
1013 cRelevantOnes += 1
1014 else:
1015 self.vprint(u'Internal error: expected oParent to NOT be None for %s' % (oFailedResult,));
1016
1017 #
1018 # If we've caught all the relevant ones by now, report the result.
1019 #
1020 if len(oCaseFile.dReasonForResultId) >= cRelevantOnes:
1021 return self.caseClosed(oCaseFile);
1022 return False;
1023
1024 def extractGuestCpuStack(self, sInfoText):
1025 """
1026 Extracts the guest CPU stacks from the input file.
1027
1028 Returns a dictionary keyed by the CPU number, value being a list of
1029 raw stack lines (no header).
1030 Returns empty dictionary if no stacks where found.
1031 """
1032 dRet = {};
1033 off = 0;
1034 while True:
1035 # Find the stack.
1036 offStart = sInfoText.find('=== start guest stack VCPU ', off);
1037 if offStart < 0:
1038 break;
1039 offEnd = sInfoText.find('=== end guest stack', offStart + 20);
1040 if offEnd >= 0:
1041 offEnd += 3;
1042 else:
1043 offEnd = sInfoText.find('=== start guest stack VCPU', offStart + 20);
1044 if offEnd < 0:
1045 offEnd = len(sInfoText);
1046
1047 sStack = sInfoText[offStart : offEnd];
1048 sStack = sStack.replace('\r',''); # paranoia
1049 asLines = sStack.split('\n');
1050
1051 # Figure the CPU.
1052 asWords = asLines[0].split();
1053 if len(asWords) < 6 or not asWords[5].isdigit():
1054 break;
1055 iCpu = int(asWords[5]);
1056
1057 # Add it and advance.
1058 dRet[iCpu] = [sLine.rstrip() for sLine in asLines[2:-1]]
1059 off = offEnd;
1060 return dRet;
1061
1062 def investigateInfoKvmLockSpinning(self, oCaseFile, sInfoText, dLogs):
1063 """ Investigates kvm_lock_spinning deadlocks """
1064 #
1065 # Extract the stacks. We need more than one CPU to create a deadlock.
1066 #
1067 dStacks = self.extractGuestCpuStack(sInfoText);
1068 self.dprint('kvm_lock_spinning: found %s stacks' % (len(dStacks),));
1069 if len(dStacks) >= 2:
1070 #
1071 # Examin each of the stacks. Each must have kvm_lock_spinning in
1072 # one of the first three entries.
1073 #
1074 cHits = 0;
1075 for asBacktrace in dStacks.values():
1076 for iFrame in xrange(min(3, len(asBacktrace))):
1077 if asBacktrace[iFrame].find('kvm_lock_spinning') >= 0:
1078 cHits += 1;
1079 break;
1080 self.dprint('kvm_lock_spinning: %s/%s hits' % (cHits, len(dStacks),));
1081 if cHits == len(dStacks):
1082 return (True, self.ktReason_VMM_kvm_lock_spinning);
1083
1084 _ = dLogs; _ = oCaseFile;
1085 return (False, None);
1086
1087 def investigateInfoHalReturnToFirmware(self, oCaseFile, sInfoText, dLogs):
1088 """ Investigates HalReturnToFirmware hangs """
1089 del oCaseFile
1090 del sInfoText
1091 del dLogs
1092 # hope that's sufficient
1093 return (True, self.ktReason_Unknown_HalReturnToFirmware);
1094
1095 ## Things we search a main or VM log for to figure out why something went bust.
1096 ## @note DO NOT ADD MORE STUFF HERE!
1097 ## Please use katSimpleMainLogReasons and katSimpleVmLogReasons instead!
1098 katSimpleMainAndVmLogReasonsDeprecated = [
1099 # ( Whether to stop on hit, reason tuple, needle text. )
1100 ( False, ktReason_Guru_Generic, 'GuruMeditation' ),
1101 ( False, ktReason_Guru_Generic, 'Guru Meditation' ),
1102 ( True, ktReason_Guru_VERR_IEM_INSTR_NOT_IMPLEMENTED, 'VERR_IEM_INSTR_NOT_IMPLEMENTED' ),
1103 ( True, ktReason_Guru_VERR_IEM_ASPECT_NOT_IMPLEMENTED, 'VERR_IEM_ASPECT_NOT_IMPLEMENTED' ),
1104 ( True, ktReason_Guru_VERR_TRPM_DONT_PANIC, 'VERR_TRPM_DONT_PANIC' ),
1105 ( True, ktReason_Guru_VERR_PGM_PHYS_PAGE_RESERVED, 'VERR_PGM_PHYS_PAGE_RESERVED' ),
1106 ( True, ktReason_Guru_VERR_VMX_INVALID_GUEST_STATE, 'VERR_VMX_INVALID_GUEST_STATE' ),
1107 ( True, ktReason_Guru_VINF_EM_TRIPLE_FAULT, 'VINF_EM_TRIPLE_FAULT' ),
1108 ( True, ktReason_Networking_Nonexistent_host_nic,
1109 'rc=E_FAIL text="Nonexistent host networking interface, name \'eth0\' (VERR_INTERNAL_ERROR)"' ),
1110 ( True, ktReason_Networking_VERR_INTNET_FLT_IF_NOT_FOUND,
1111 'Failed to attach the network LUN (VERR_INTNET_FLT_IF_NOT_FOUND)' ),
1112 ( True, ktReason_Host_Reboot_OSX_Watchdog_Timeout, ': "OSX Watchdog Timeout: ' ),
1113 ( False, ktReason_XPCOM_NS_ERROR_CALL_FAILED,
1114 'Exception: 0x800706be (Call to remote object failed (NS_ERROR_CALL_FAILED))' ),
1115 ( True, ktReason_API_std_bad_alloc, 'Unexpected exception: std::bad_alloc' ),
1116 ( True, ktReason_Host_HostMemoryLow, 'HostMemoryLow' ),
1117 ( True, ktReason_Host_HostMemoryLow, 'Failed to procure handy pages; rc=VERR_NO_MEMORY' ),
1118 ( True, ktReason_Unknown_File_Not_Found,
1119 'Error: failed to start machine. Error message: File not found. (VERR_FILE_NOT_FOUND)' ),
1120 ( True, ktReason_Unknown_File_Not_Found, # lump it in with file-not-found for now.
1121 'Error: failed to start machine. Error message: Not supported. (VERR_NOT_SUPPORTED)' ),
1122 ( False, ktReason_Unknown_VM_Crash, 'txsDoConnectViaTcp: Machine state: Aborted' ),
1123 ( True, ktReason_Host_Modprobe_Failed, 'Kernel driver not installed' ),
1124 ( True, ktReason_OSInstall_Sata_no_BM, 'PCHS=14128/14134/8224' ),
1125 ( True, ktReason_Host_DoubleFreeHeap, 'double free or corruption' ),
1126 #( False, ktReason_Unknown_VM_Start_Error, 'VMSetError: ' ), - false positives for stuff like:
1127 # "VMSetError: VD: Backend 'VBoxIsoMaker' does not support async I/O"
1128 ( False, ktReason_Unknown_VM_Start_Error, 'error: failed to open session for' ),
1129 ( False, ktReason_Unknown_VM_Runtime_Error, 'Console: VM runtime error: fatal=true' ),
1130 ];
1131
1132 ## This we search a main log for to figure out why something went bust.
1133 katSimpleMainLogReasons = [
1134 # ( Whether to stop on hit, reason tuple, needle text. )
1135 ( False, ktReason_Host_win32com_gen_py_not_found, 'ModuleNotFoundError: No module named \'win32com.gen_py' ),
1136 ( False, ktReason_Host_Python_vboxapi_not_found, 'ImportError: No module named vboxapi' ),
1137 ( False, ktReason_Host_Python_xpcom_not_found, 'ImportError: No module named xpcom' ),
1138 ];
1139
1140 ## This we search a VM log for to figure out why something went bust.
1141 katSimpleVmLogReasons = [
1142 # ( Whether to stop on hit, reason tuple, needle text. )
1143 # Note: Works for ATA and VD drivers.
1144 ( False, ktReason_Host_DiskFull, '_DISKFULL' ),
1145 ];
1146
1147 ## Things we search a VBoxHardening.log file for to figure out why something went bust.
1148 katSimpleVBoxHardeningLogReasons = [
1149 # ( Whether to stop on hit, reason tuple, needle text. )
1150 ( True, ktReason_Host_DriverNotLoaded, 'Error opening VBoxDrvStub: STATUS_OBJECT_NAME_NOT_FOUND' ),
1151 ( True, ktReason_Host_NotSignedWithBuildCert, 'Not signed with the build certificate' ),
1152 ( True, ktReason_Host_TSTInfo_Accuracy_OOR, 'RTCRTSPTSTINFO::Accuracy::Millis: Out of range' ),
1153 ( False, ktReason_Unknown_VM_Crash, 'Quitting: ExitCode=0xc0000005 (rcNtWait=' ),
1154 ];
1155
1156 ## Things we search a kernel.log file for to figure out why something went bust.
1157 katSimpleKernelLogReasons = [
1158 # ( Whether to stop on hit, reason tuple, needle text. )
1159 ( True, ktReason_Panic_HugeMemory, 'mm/huge_memory.c:1988' ),
1160 ( True, ktReason_Panic_IOAPICDoesntWork, 'IO-APIC + timer doesn\'t work' ),
1161 ( True, ktReason_Panic_TxUnitHang, 'Detected Tx Unit Hang' ),
1162 ( True, ktReason_GuestBug_CompizVBoxQt, 'error 4 in libQt5CoreVBox' ),
1163 ( True, ktReason_GuestBug_CompizVBoxQt, 'error 4 in libgtk-3' ),
1164 ];
1165
1166 ## Things we search the _RIGHT_ _STRIPPED_ vgatext for.
1167 katSimpleVgaTextReasons = [
1168 # ( Whether to stop on hit, reason tuple, needle text. )
1169 ( True, ktReason_Panic_MP_BIOS_IO_APIC,
1170 "..MP-BIOS bug: 8254 timer not connected to IO-APIC\n\n" ),
1171 ( True, ktReason_Panic_MP_BIOS_IO_APIC,
1172 "..MP-BIOS bug: 8254 timer not connected to IO-APIC\n"
1173 "...trying to set up timer (IRQ0) through the 8259A ... failed.\n"
1174 "...trying to set up timer as Virtual Wire IRQ... failed.\n"
1175 "...trying to set up timer as ExtINT IRQ... failed :(.\n"
1176 "Kernel panic - not syncing: IO-APIC + timer doesn't work! Boot with apic=debug\n"
1177 "and send a report. Then try booting with the 'noapic' option\n"
1178 "\n" ),
1179 ( True, ktReason_OSInstall_GRUB_hang,
1180 "-----\nGRUB Loading stage2..\n\n\n\n" ),
1181 ( True, ktReason_OSInstall_GRUB_hang,
1182 "-----\nGRUB Loading stage2...\n\n\n\n" ), # the 3 dot hang appears to be less frequent
1183 ( True, ktReason_OSInstall_GRUB_hang,
1184 "-----\nGRUB Loading stage2....\n\n\n\n" ), # the 4 dot hang appears to be very infrequent
1185 ( True, ktReason_OSInstall_GRUB_hang,
1186 "-----\nGRUB Loading stage2.....\n\n\n\n" ), # the 5 dot hang appears to be more frequent again
1187 ( True, ktReason_OSInstall_Udev_hang,
1188 "\nStarting udev:\n\n\n\n" ),
1189 ( True, ktReason_OSInstall_Udev_hang,
1190 "\nStarting udev:\n------" ),
1191 ( True, ktReason_Panic_BootManagerC000000F,
1192 "Windows failed to start. A recent hardware or software change might be the" ),
1193 ( True, ktReason_BootManager_Image_corrupt,
1194 "BOOTMGR image is corrupt. The system cannot boot." ),
1195 ];
1196
1197 ## Things we search for in the info.txt file. Require handlers for now.
1198 katInfoTextHandlers = [
1199 # ( Trigger text, handler method )
1200 ( "kvm_lock_spinning", investigateInfoKvmLockSpinning ),
1201 ( "HalReturnToFirmware", investigateInfoHalReturnToFirmware ),
1202 ];
1203
1204 ## Mapping screenshot/failure SHA-256 hashes to failure reasons.
1205 katSimpleScreenshotHashReasons = [
1206 # ( Whether to stop on hit, reason tuple, lowercased sha-256 of PIL.Image.tostring output )
1207 ( True, ktReason_BSOD_Recovery, '576f8e38d62b311cac7e3dc3436a0d0b9bd8cfd7fa9c43aafa95631520a45eac' ),
1208 ( True, ktReason_BSOD_Automatic_Repair, 'c6a72076cc619937a7a39cfe9915b36d94cee0d4e3ce5ce061485792dcee2749' ),
1209 ( True, ktReason_BSOD_Automatic_Repair, '26c4d8a724ff2c5e1051f3d5b650dbda7b5fdee0aa3e3c6059797f7484a515df' ),
1210 ( True, ktReason_BSOD_0000007F, '57e1880619e13042a87100e7a38c8974b85ce3866501be621bea0cc696bb2c63' ),
1211 ( True, ktReason_BSOD_000000D1, '134621281f00a3f8aeeb7660064bffbf6187ed56d5852142328d0bcb18ef0ede' ),
1212 ( True, ktReason_BSOD_000000D1, '279f11258150c9d2fef041eca65501f3141da8df39256d8f6377e897e3b45a93' ),
1213 ( True, ktReason_BSOD_C0000225, 'bd13a144be9dcdfb16bc863ff4c8f02a86e263c174f2cd5ffd27ca5f3aa31789' ),
1214 ( True, ktReason_BSOD_C0000225, '8348b465e7ee9e59dd4e785880c57fd8677de05d11ac21e786bfde935307b42f' ),
1215 ( True, ktReason_BSOD_C0000225, '1316e1fc818a73348412788e6910b8c016f237d8b4e15b20caf4a866f7a7840e' ),
1216 ( True, ktReason_BSOD_C0000225, '54e0acbff365ce20a85abbe42bcd53647b8b9e80c68e45b2cd30e86bf177a0b5' ),
1217 ( True, ktReason_BSOD_C0000225, '50fec50b5199923fa48b3f3e782687cc381e1c8a788ebda14e6a355fbe3bb1b3' ),
1218 ];
1219
1220
1221 def scanLog(self, asLogs, atNeedles, oCaseFile, idTestResult):
1222 """
1223 Scans for atNeedles in sLog.
1224
1225 Returns True if a stop-on-hit neelde was found.
1226 Returns None if a no-stop reason was found.
1227 Returns False if no hit.
1228 """
1229 fRet = False;
1230 for fStopOnHit, tReason, oNeedle in atNeedles:
1231 fMatch = False;
1232 if utils.isString(oNeedle):
1233 for sLog in asLogs:
1234 if sLog:
1235 fMatch |= sLog.find(oNeedle) > 0;
1236 else:
1237 for sLog in asLogs:
1238 if sLog:
1239 fMatch |= oNeedle.search(sLog) is not None;
1240 if fMatch:
1241 oCaseFile.noteReasonForId(tReason, idTestResult);
1242 if fStopOnHit:
1243 return True;
1244 fRet = None;
1245 return fRet;
1246
1247
1248 ## Fallback reasons based on GA test groups.
1249 kdGATestFallbacks = {
1250 'Guest Control': ktReason_Add_GstCtl,
1251 'Shared Folders': ktReason_Add_ShFl,
1252 };
1253
1254 def investigateGATest(self, oCaseFile, oFailedResult, sResultLog):
1255 """
1256 Investigates a failed VM run.
1257 """
1258 enmReason = None;
1259 sParentName = oFailedResult.oParent.sName if oFailedResult.oParent else '';
1260 if oFailedResult.sName == 'VBoxWindowsAdditions.exe' or sResultLog.find('VBoxWindowsAdditions.exe" failed with') > 0:
1261 enmReason = self.ktReason_Add_Installer_Win_Failed;
1262 # guest control:
1263 elif sParentName == 'Guest Control' and oFailedResult.sName == 'Preparations':
1264 enmReason = self.ktReason_Add_GstCtl_Preparations;
1265 elif oFailedResult.sName == 'Session Basics':
1266 enmReason = self.ktReason_Add_GstCtl_SessionBasics;
1267 elif oFailedResult.sName == 'Session Process References':
1268 enmReason = self.ktReason_Add_GstCtl_SessionProcRefs;
1269 elif oFailedResult.sName == 'Copy from guest':
1270 if sResultLog.find('*** abort action ***') >= 0:
1271 enmReason = self.ktReason_Add_GstCtl_CopyFromGuest_Timeout;
1272 elif oFailedResult.sName == 'Copy to guest':
1273 off = sResultLog.find('"Guest directory "');
1274 if off > 0 and sResultLog.find('" already exists"', off, off + 80):
1275 enmReason = self.ktReason_Add_GstCtl_CopyToGuest_DstExists;
1276 elif sResultLog.find('Guest destination must not be empty') >= 0:
1277 enmReason = self.ktReason_Add_GstCtl_CopyToGuest_DstEmpty;
1278 elif sResultLog.find('*** abort action ***') >= 0:
1279 enmReason = self.ktReason_Add_GstCtl_CopyToGuest_Timeout;
1280 elif oFailedResult.sName.find('Session w/ Guest Reboot') >= 0:
1281 enmReason = self.ktReason_Add_GstCtl_Session_Reboot;
1282 # shared folders:
1283 elif sParentName == 'Shared Folders' and oFailedResult.sName == 'Automounting':
1284 enmReason = self.ktReason_Add_ShFl_Automount;
1285 elif oFailedResult.sName == 'mmap':
1286 if sResultLog.find('FsPerf: Flush issue at offset ') >= 0:
1287 enmReason = self.ktReason_Add_Mmap_Coherency;
1288 elif sResultLog.find('FlushViewOfFile') >= 0:
1289 enmReason = self.ktReason_Add_FlushViewOfFile;
1290 elif sParentName == 'Shared Folders' and oFailedResult.sName == 'Running FsPerf':
1291 enmReason = self.ktReason_Add_ShFl_FsPerf; ## Maybe it would be better to be more specific...
1292
1293 if enmReason is not None:
1294 return oCaseFile.noteReasonForId(enmReason, oFailedResult.idTestResult);
1295
1296 # Generalistic fallbacks:
1297 for sKey in utils.iteritems(self.kdGATestFallbacks):
1298 oTmpFailedResult = oFailedResult;
1299 while oTmpFailedResult:
1300 if oTmpFailedResult.sName == sKey:
1301 return oCaseFile.noteReasonForId(self.kdGATestFallbacks[sKey], oFailedResult.idTestResult);
1302 oTmpFailedResult = oTmpFailedResult.oParent;
1303
1304 self.vprint(u'TODO: Cannot place GA failure idTestResult=%u - %s' % (oFailedResult.idTestResult, oFailedResult.sName,));
1305 self.dprint(u'%s + %s <<\n%s\n<<' % (oFailedResult.tsCreated, oFailedResult.tsElapsed, sResultLog,));
1306 return False;
1307
1308 def isResultFromGATest(self, oCaseFile, oFailedResult):
1309 """
1310 Checks if this result and corresponding log snippet looks like a GA test run.
1311 """
1312 while oFailedResult is not None:
1313 if oFailedResult.sName in [ 'Guest Control', 'Shared Folders', 'FsPerf', 'VBoxWindowsAdditions.exe' ]:
1314 return True;
1315 if oCaseFile.oTestCase.sName == 'Guest Additions' and oFailedResult.sName in [ 'Install', ]:
1316 return True;
1317 oFailedResult = oFailedResult.oParent;
1318 return False;
1319
1320
1321 def investigateVMResult(self, oCaseFile, oFailedResult, sResultLog):
1322 """
1323 Investigates a failed VM run.
1324 """
1325
1326 def investigateLogSet():
1327 """
1328 Investigates the current set of VM related logs.
1329 """
1330 self.dprint('investigateLogSet: log lengths: result %u, VM %u, kernel %u, vga text %u, info text %u, hard %u'
1331 % ( len(sResultLog if sResultLog else ''),
1332 len(sVMLog if sVMLog else ''),
1333 len(sKrnlLog if sKrnlLog else ''),
1334 len(sVgaText if sVgaText else ''),
1335 len(sInfoText if sInfoText else ''),
1336 len(sNtHardLog if sNtHardLog else ''),));
1337
1338 #self.dprint(u'main.log<<<\n%s\n<<<\n' % (sResultLog,));
1339 #self.dprint(u'vbox.log<<<\n%s\n<<<\n' % (sVMLog,));
1340 #self.dprint(u'krnl.log<<<\n%s\n<<<\n' % (sKrnlLog,));
1341 #self.dprint(u'vgatext.txt<<<\n%s\n<<<\n' % (sVgaText,));
1342 #self.dprint(u'info.txt<<<\n%s\n<<<\n' % (sInfoText,));
1343 #self.dprint(u'hard.txt<<<\n%s\n<<<\n' % (sNtHardLog,));
1344
1345 # TODO: more
1346
1347 #
1348 # Look for BSODs. Some stupid stupid inconsistencies in reason and log messages here, so don't try prettify this.
1349 #
1350 sDetails = self.findInAnyAndReturnRestOfLine([ sVMLog, sResultLog ],
1351 'GIM: HyperV: Guest indicates a fatal condition! P0=');
1352 if sDetails is not None:
1353 # P0=%#RX64 P1=%#RX64 P2=%#RX64 P3=%#RX64 P4=%#RX64 "
1354 sKey = sDetails.split(' ', 1)[0];
1355 try: sKey = '0x%08X' % (int(sKey, 16),);
1356 except: pass;
1357 if sKey in self.asBsodReasons:
1358 tReason = ( self.ksBsodCategory, sKey );
1359 elif sKey.lower() in self.asBsodReasons: # just in case.
1360 tReason = ( self.ksBsodCategory, sKey.lower() );
1361 else:
1362 self.dprint(u'BSOD "%s" not found in %s;' % (sKey, self.asBsodReasons));
1363 tReason = ( self.ksBsodCategory, self.ksBsodAddNew );
1364 return oCaseFile.noteReasonForId(tReason, oFailedResult.idTestResult, sComment = sDetails.strip());
1365
1366 fFoundSomething = False;
1367
1368 #
1369 # Look for linux panic.
1370 #
1371 if sKrnlLog is not None:
1372 fRet = self.scanLog([sKrnlLog,], self.katSimpleKernelLogReasons, oCaseFile, oFailedResult.idTestResult);
1373 if fRet is True:
1374 return fRet;
1375 fFoundSomething |= fRet is None;
1376
1377 #
1378 # Loop thru the simple stuff.
1379 #
1380
1381 # Main log.
1382 fRet = self.scanLog([sResultLog,], self.katSimpleMainLogReasons, oCaseFile, oFailedResult.idTestResult);
1383 if fRet is True:
1384 return fRet;
1385 fFoundSomething |= fRet is None;
1386
1387 # VM log.
1388 fRet = self.scanLog([sVMLog,], self.katSimpleVmLogReasons, oCaseFile, oFailedResult.idTestResult);
1389 if fRet is True:
1390 return fRet;
1391 fFoundSomething |= fRet is None;
1392
1393 # Old main + vm log.
1394 fRet = self.scanLog([sResultLog, sVMLog], self.katSimpleMainAndVmLogReasonsDeprecated,
1395 oCaseFile, oFailedResult.idTestResult);
1396 if fRet is True:
1397 return fRet;
1398 fFoundSomething |= fRet is None;
1399
1400 # Continue with vga text.
1401 if sVgaText:
1402 fRet = self.scanLog([sVgaText,], self.katSimpleVgaTextReasons, oCaseFile, oFailedResult.idTestResult);
1403 if fRet is True:
1404 return fRet;
1405 fFoundSomething |= fRet is None;
1406
1407 # Continue with screen hashes.
1408 if sScreenHash is not None:
1409 for fStopOnHit, tReason, sHash in self.katSimpleScreenshotHashReasons:
1410 if sScreenHash == sHash:
1411 oCaseFile.noteReasonForId(tReason, oFailedResult.idTestResult);
1412 if fStopOnHit:
1413 return True;
1414 fFoundSomething = True;
1415
1416 # Check VBoxHardening.log.
1417 if sNtHardLog is not None:
1418 fRet = self.scanLog([sNtHardLog,], self.katSimpleVBoxHardeningLogReasons, oCaseFile, oFailedResult.idTestResult);
1419 if fRet is True:
1420 return fRet;
1421 fFoundSomething |= fRet is None;
1422
1423 #
1424 # Complicated stuff.
1425 #
1426 dLogs = {
1427 'sVMLog': sVMLog,
1428 'sNtHardLog': sNtHardLog,
1429 'sScreenHash': sScreenHash,
1430 'sKrnlLog': sKrnlLog,
1431 'sVgaText': sVgaText,
1432 'sInfoText': sInfoText,
1433 };
1434
1435 # info.txt.
1436 if sInfoText:
1437 for sNeedle, fnHandler in self.katInfoTextHandlers:
1438 if sInfoText.find(sNeedle) > 0:
1439 (fStop, tReason) = fnHandler(self, oCaseFile, sInfoText, dLogs);# ? pylint: disable=too-many-function-args
1440 if tReason is not None:
1441 oCaseFile.noteReasonForId(tReason, oFailedResult.idTestResult);
1442 if fStop:
1443 return True;
1444 fFoundSomething = True;
1445
1446 #
1447 # Check for repeated reboots...
1448 #
1449 if sVMLog is not None:
1450 cResets = sVMLog.count('Changing the VM state from \'RUNNING\' to \'RESETTING\'');
1451 if cResets > 10:
1452 return oCaseFile.noteReasonForId(self.ktReason_Unknown_Reboot_Loop, oFailedResult.idTestResult,
1453 sComment = 'Counted %s reboots' % (cResets,));
1454
1455 return fFoundSomething;
1456
1457 #
1458 # Check if we got any VM or/and kernel logs. Treat them as sets in
1459 # case we run multiple VMs here (this is of course ASSUMING they
1460 # appear in the order that terminateVmBySession uploads them).
1461 #
1462 cTimes = 0;
1463 sVMLog = None;
1464 sNtHardLog = None;
1465 sScreenHash = None;
1466 sKrnlLog = None;
1467 sVgaText = None;
1468 sInfoText = None;
1469 for oFile in oFailedResult.aoFiles:
1470 if oFile.sKind == TestResultFileData.ksKind_LogReleaseVm:
1471 if 'VBoxHardening.log' not in oFile.sFile:
1472 if sVMLog is not None:
1473 if investigateLogSet() is True:
1474 return True;
1475 cTimes += 1;
1476 sInfoText = None;
1477 sVgaText = None;
1478 sKrnlLog = None;
1479 sScreenHash = None;
1480 sNtHardLog = None;
1481 sVMLog = oCaseFile.getLogFile(oFile);
1482 else:
1483 sNtHardLog = oCaseFile.getLogFile(oFile);
1484 elif oFile.sKind == TestResultFileData.ksKind_LogGuestKernel:
1485 sKrnlLog = oCaseFile.getLogFile(oFile);
1486 elif oFile.sKind == TestResultFileData.ksKind_InfoVgaText:
1487 sVgaText = '\n'.join([sLine.rstrip() for sLine in oCaseFile.getLogFile(oFile).split('\n')]);
1488 elif oFile.sKind == TestResultFileData.ksKind_InfoCollection:
1489 sInfoText = oCaseFile.getLogFile(oFile);
1490 elif oFile.sKind == TestResultFileData.ksKind_ScreenshotFailure:
1491 sScreenHash = oCaseFile.getScreenshotSha256(oFile);
1492 if sScreenHash is not None:
1493 sScreenHash = sScreenHash.lower();
1494 self.vprint(u'%s %s' % ( sScreenHash, oFile.sFile,));
1495
1496 if ( sVMLog is not None \
1497 or sNtHardLog is not None \
1498 or cTimes == 0) \
1499 and investigateLogSet() is True:
1500 return True;
1501
1502 return None;
1503
1504 def isResultFromVMRun(self, oFailedResult, sResultLog):
1505 """
1506 Checks if this result and corresponding log snippet looks like a VM run.
1507 """
1508
1509 # Look for startVmEx/ startVmAndConnectToTxsViaTcp and similar output in the log.
1510 if sResultLog.find(' startVm') > 0:
1511 return True;
1512
1513 # Any other indicators? No?
1514 _ = oFailedResult;
1515 return False;
1516
1517
1518 ## Things we search a VBoxSVC log for to figure out why something went bust.
1519 katSimpleSvcLogReasons = [
1520 # ( Whether to stop on hit, reason tuple, needle text. )
1521 ( False, ktReason_Unknown_VM_Crash, re.compile(r'Reaper.* exited normally: -1073741819 \(0xc0000005\)') ),
1522 ( False, ktReason_Unknown_VM_Crash, re.compile(r'Reaper.* was signalled: 11 \(0xb\)') ), # For VBox < 6.1.
1523 ( False, ktReason_Unknown_VM_Crash, re.compile(r'Reaper.* was signalled: SIGABRT.*') ), # Since VBox 7.0.
1524 ( False, ktReason_Unknown_VM_Crash, re.compile(r'Reaper.* was signalled: SIGSEGV.*') ),
1525 ( False, ktReason_Unknown_VM_Terminated, re.compile(r'Reaper.* was signalled: SIGTERM.*') ),
1526 ( False, ktReason_Unknown_VM_Terminated, re.compile(r'Reaper.* was signalled: SIGKILL.*') ),
1527 ];
1528
1529 def investigateSvcLogForVMRun(self, oCaseFile, sSvcLog):
1530 """
1531 Check the VBoxSVC log for a single VM run.
1532 """
1533 if sSvcLog:
1534 fRet = self.scanLog([sSvcLog,], self.katSimpleSvcLogReasons, oCaseFile, oCaseFile.oTree.idTestResult);
1535 if fRet is True or fRet is None:
1536 return True;
1537 return False;
1538
1539 def investigateNtHardLogForVMRun(self, oCaseFile):
1540 """
1541 Check if the hardening log for a single VM run contains VM crash indications.
1542 """
1543 aoLogFiles = oCaseFile.oTree.getListOfLogFilesByKind(TestResultFileData.ksKind_LogReleaseVm);
1544 for oLogFile in aoLogFiles:
1545 if oLogFile.sFile.find('VBoxHardening.log') >= 0:
1546 sLog = oCaseFile.getLogFile(oLogFile);
1547 if sLog.find('Quitting: ExitCode=0xc0000005') >= 0:
1548 return oCaseFile.noteReasonForId(self.ktReason_Unknown_VM_Crash, oCaseFile.oTree.idTestResult);
1549 return False;
1550
1551
1552 def investigateVBoxVMTest(self, oCaseFile, fSingleVM):
1553 """
1554 Checks out a VBox VM test.
1555
1556 This is generic investigation of a test running one or more VMs, like
1557 for example a smoke test or a guest installation test.
1558
1559 The fSingleVM parameter is a hint, which probably won't come in useful.
1560 """
1561 _ = fSingleVM;
1562
1563 #
1564 # Get a list of test result failures we should be looking into and the main log.
1565 #
1566 aoFailedResults = oCaseFile.oTree.getListOfFailures();
1567 sMainLog = oCaseFile.getMainLog();
1568
1569 #
1570 # There are a set of errors ending up on the top level result record.
1571 # Should deal with these first.
1572 #
1573 if len(aoFailedResults) == 1 and aoFailedResults[0] == oCaseFile.oTree:
1574 # Check if we've just got that XPCOM client smoke test shutdown issue. This will currently always
1575 # be reported on the top result because vboxinstall.py doesn't add an error for it. It is easy to
1576 # ignore other failures in the test if we're not a little bit careful here.
1577 if sMainLog.find('vboxinstaller: Exit code: -11 (') > 0:
1578 oCaseFile.noteReason(self.ktReason_XPCOM_Exit_Minus_11);
1579 return self.caseClosed(oCaseFile);
1580
1581 # Hang after starting VBoxSVC (e.g. idTestSet=136307258)
1582 if self.isThisFollowedByTheseLines(sMainLog, 'oVBoxMgr=<vboxapi.VirtualBoxManager object at',
1583 (' Timeout: ', ' Attempting to abort child...',) ):
1584 if sMainLog.find('*** glibc detected *** /') > 0:
1585 oCaseFile.noteReason(self.ktReason_XPCOM_VBoxSVC_Hang_Plus_Heap_Corruption);
1586 else:
1587 oCaseFile.noteReason(self.ktReason_XPCOM_VBoxSVC_Hang);
1588 return self.caseClosed(oCaseFile);
1589
1590 # Look for heap corruption without visible hang.
1591 if sMainLog.find('*** glibc detected *** /') > 0 \
1592 or sMainLog.find("-1073740940") > 0: # STATUS_HEAP_CORRUPTION / 0xc0000374
1593 oCaseFile.noteReason(self.ktReason_Unknown_Heap_Corruption);
1594 return self.caseClosed(oCaseFile);
1595
1596 # Out of memory w/ timeout.
1597 if sMainLog.find('sErrId=HostMemoryLow') > 0:
1598 oCaseFile.noteReason(self.ktReason_Host_HostMemoryLow);
1599 return self.caseClosed(oCaseFile);
1600
1601 # Stale files like vts_rm.exe (windows).
1602 offEnd = sMainLog.rfind('*** The test driver exits successfully. ***');
1603 if offEnd > 0 and sMainLog.find('[Error 145] The directory is not empty: ', offEnd) > 0:
1604 oCaseFile.noteReason(self.ktReason_Ignore_Stale_Files);
1605 return self.caseClosed(oCaseFile);
1606
1607 #
1608 # XPCOM screwup
1609 #
1610 if sMainLog.find('AttributeError: \'NoneType\' object has no attribute \'addObserver\'') > 0:
1611 oCaseFile.noteReason(self.ktReason_Buggy_Build_Broken_Build);
1612 return self.caseClosed(oCaseFile);
1613
1614 #
1615 # Go thru each failed result.
1616 #
1617 for oFailedResult in aoFailedResults:
1618 self.dprint(u'Looking at test result #%u - %s' % (oFailedResult.idTestResult, oFailedResult.getFullName(),));
1619 sResultLog = TestSetData.extractLogSectionElapsed(sMainLog, oFailedResult.tsCreated, oFailedResult.tsElapsed);
1620 if oFailedResult.sName == 'Installing VirtualBox':
1621 self.investigateInstallUninstallFailure(oCaseFile, oFailedResult, sResultLog, fInstall = True)
1622
1623 elif oFailedResult.sName == 'Uninstalling VirtualBox':
1624 self.investigateInstallUninstallFailure(oCaseFile, oFailedResult, sResultLog, fInstall = False)
1625
1626 elif self.isResultFromVMRun(oFailedResult, sResultLog):
1627 self.investigateVMResult(oCaseFile, oFailedResult, sResultLog);
1628
1629 elif self.isResultFromGATest(oCaseFile, oFailedResult):
1630 self.investigateGATest(oCaseFile, oFailedResult, sResultLog);
1631
1632 elif sResultLog.find('most likely not unique') > 0:
1633 oCaseFile.noteReasonForId(self.ktReason_Host_NetworkMisconfiguration, oFailedResult.idTestResult)
1634 elif sResultLog.find('Exception: 0x800706be (Call to remote object failed (NS_ERROR_CALL_FAILED))') > 0:
1635 oCaseFile.noteReasonForId(self.ktReason_XPCOM_NS_ERROR_CALL_FAILED, oFailedResult.idTestResult);
1636
1637 elif sResultLog.find('The machine is not mutable (state is ') > 0:
1638 self.vprint('Ignoring "machine not mutable" error as it is probably due to an earlier problem');
1639 oCaseFile.noteReasonForId(self.ktHarmless, oFailedResult.idTestResult);
1640
1641 elif sResultLog.find('** error: no action was specified') > 0 \
1642 or sResultLog.find('(len(self._asXml, asText))') > 0:
1643 oCaseFile.noteReasonForId(self.ktReason_Ignore_Buggy_Test_Driver, oFailedResult.idTestResult);
1644
1645 else:
1646 self.vprint(u'TODO: Cannot place idTestResult=%u - %s' % (oFailedResult.idTestResult, oFailedResult.sName,));
1647 self.dprint(u'%s + %s <<\n%s\n<<' % (oFailedResult.tsCreated, oFailedResult.tsElapsed, sResultLog,));
1648
1649 #
1650 # Python bindings installation problem.
1651 #
1652 if sMainLog.find('ImportError: No module named vboxapi') > 0:
1653 oCaseFile.noteReason(self.ktReason_Host_Python_vboxapi_not_found);
1654 return self.caseClosed(oCaseFile);
1655
1656 if sMainLog.find('ImportError: No module named xpcom') > 0:
1657 oCaseFile.noteReason(self.ktReason_Host_Python_xpcom_not_found);
1658 return self.caseClosed(oCaseFile);
1659
1660 #
1661 # Windows python/com screwup.
1662 #
1663 if sMainLog.find('ModuleNotFoundError: No module named \'win32com.gen_py') > 0:
1664 oCaseFile.noteReason(self.ktReason_Host_win32com_gen_py_not_found);
1665 return self.caseClosed(oCaseFile);
1666
1667 #
1668 # Check VBoxSVC.log and VBoxHardening.log for VM crashes if inconclusive on single VM runs.
1669 #
1670 if fSingleVM and len(oCaseFile.dReasonForResultId) < len(aoFailedResults):
1671 self.dprint(u'Got %u out of %u - checking VBoxSVC.log...'
1672 % (len(oCaseFile.dReasonForResultId), len(aoFailedResults)));
1673 if self.investigateSvcLogForVMRun(oCaseFile, oCaseFile.getSvcLog()):
1674 return self.caseClosed(oCaseFile);
1675 if self.investigateNtHardLogForVMRun(oCaseFile):
1676 return self.caseClosed(oCaseFile);
1677
1678 #
1679 # Report home and close the case if we got them all, otherwise log it.
1680 #
1681 if len(oCaseFile.dReasonForResultId) >= len(aoFailedResults):
1682 return self.caseClosed(oCaseFile);
1683
1684 if oCaseFile.dReasonForResultId:
1685 self.vprint(u'TODO: Got %u out of %u - close, but no cigar. :-/'
1686 % (len(oCaseFile.dReasonForResultId), len(aoFailedResults)));
1687 else:
1688 self.vprint(u'XXX: Could not figure out anything at all! :-(');
1689 return False;
1690
1691
1692 ## Things we search a main log for to figure out why something in the API test went bust.
1693 katSimpleApiMainLogReasons = [
1694 # ( Whether to stop on hit, reason tuple, needle text. )
1695 ( True, ktReason_Networking_Nonexistent_host_nic,
1696 'rc=E_FAIL text="Nonexistent host networking interface, name \'eth0\' (VERR_INTERNAL_ERROR)"' ),
1697 ( False, ktReason_XPCOM_NS_ERROR_CALL_FAILED,
1698 'Exception: 0x800706be (Call to remote object failed (NS_ERROR_CALL_FAILED))' ),
1699 ( True, ktReason_API_std_bad_alloc, 'Unexpected exception: std::bad_alloc' ),
1700 ( True, ktReason_API_Digest_Mismatch, 'Digest mismatch (VERR_NOT_EQUAL)' ),
1701 ( True, ktReason_API_MoveVM_SharingViolation, 'rc=VBOX_E_IPRT_ERROR text="Could not copy the log file ' ),
1702 ( True, ktReason_API_MoveVM_InvalidParameter,
1703 'rc=VBOX_E_IPRT_ERROR text="Could not copy the setting file ' ),
1704 ( True, ktReason_API_Open_Session_Failed, 'error: failed to open session for' ),
1705 ];
1706
1707 def investigateVBoxApiTest(self, oCaseFile):
1708 """
1709 Checks out a VBox API test.
1710 """
1711
1712 #
1713 # Get a list of test result failures we should be looking into and the main log.
1714 #
1715 aoFailedResults = oCaseFile.oTree.getListOfFailures();
1716 sMainLog = oCaseFile.getMainLog();
1717
1718 #
1719 # Go thru each failed result.
1720 #
1721 for oFailedResult in aoFailedResults:
1722 self.dprint(u'Looking at test result #%u - %s' % (oFailedResult.idTestResult, oFailedResult.getFullName(),));
1723 sResultLog = TestSetData.extractLogSectionElapsed(sMainLog, oFailedResult.tsCreated, oFailedResult.tsElapsed);
1724 if oFailedResult.sName == 'Installing VirtualBox':
1725 self.investigateInstallUninstallFailure(oCaseFile, oFailedResult, sResultLog, fInstall = True)
1726
1727 elif oFailedResult.sName == 'Uninstalling VirtualBox':
1728 self.investigateInstallUninstallFailure(oCaseFile, oFailedResult, sResultLog, fInstall = False)
1729
1730 elif sResultLog.find('Exception: 0x800706be (Call to remote object failed (NS_ERROR_CALL_FAILED))') > 0:
1731 oCaseFile.noteReasonForId(self.ktReason_XPCOM_NS_ERROR_CALL_FAILED, oFailedResult.idTestResult);
1732
1733 else:
1734 fFoundSomething = False;
1735 for fStopOnHit, tReason, sNeedle in self.katSimpleApiMainLogReasons:
1736 if sResultLog.find(sNeedle) > 0:
1737 oCaseFile.noteReasonForId(tReason, oFailedResult.idTestResult);
1738 fFoundSomething = True;
1739 if fStopOnHit:
1740 break;
1741 if fFoundSomething:
1742 self.vprint(u'TODO: Cannot place idTestResult=%u - %s' % (oFailedResult.idTestResult, oFailedResult.sName,));
1743 self.dprint(u'%s + %s <<\n%s\n<<' % (oFailedResult.tsCreated, oFailedResult.tsElapsed, sResultLog,));
1744
1745 #
1746 # Report home and close the case if we got them all, otherwise log it.
1747 #
1748 if len(oCaseFile.dReasonForResultId) >= len(aoFailedResults):
1749 return self.caseClosed(oCaseFile);
1750
1751 if oCaseFile.dReasonForResultId:
1752 self.vprint(u'TODO: Got %u out of %u - close, but no cigar. :-/'
1753 % (len(oCaseFile.dReasonForResultId), len(aoFailedResults)));
1754 else:
1755 self.vprint(u'XXX: Could not figure out anything at all! :-(');
1756 return False;
1757
1758
1759 def reasoningFailures(self):
1760 """
1761 Guess the reason for failures.
1762 """
1763 #
1764 # Get a list of failed test sets without any assigned failure reason.
1765 #
1766 cGot = 0;
1767 if not self.oConfig.aidTestSets:
1768 aoTestSets = self.oTestSetLogic.fetchFailedSetsWithoutReason(cHoursBack = self.oConfig.cHoursBack,
1769 tsNow = self.tsNow);
1770 else:
1771 aoTestSets = [self.oTestSetLogic.getById(idTestSet) for idTestSet in self.oConfig.aidTestSets];
1772 for oTestSet in aoTestSets:
1773 self.dprint(u'----------------------------------- #%u, status %s -----------------------------------'
1774 % ( oTestSet.idTestSet, oTestSet.enmStatus,));
1775
1776 #
1777 # Open a case file and assign it to the right investigator.
1778 #
1779 (oTree, _ ) = self.oTestResultLogic.fetchResultTree(oTestSet.idTestSet);
1780 oBuild = BuildDataEx().initFromDbWithId( self.oDb, oTestSet.idBuild, oTestSet.tsCreated);
1781 oTestBox = TestBoxData().initFromDbWithGenId( self.oDb, oTestSet.idGenTestBox);
1782 oTestGroup = TestGroupData().initFromDbWithId( self.oDb, oTestSet.idTestGroup, oTestSet.tsCreated);
1783 oTestCase = TestCaseDataEx().initFromDbWithGenId( self.oDb, oTestSet.idGenTestCase, oTestSet.tsConfig);
1784
1785 oCaseFile = VirtualTestSheriffCaseFile(self, oTestSet, oTree, oBuild, oTestBox, oTestGroup, oTestCase);
1786
1787 if oTestSet.enmStatus == TestSetData.ksTestStatus_BadTestBox:
1788 self.dprint(u'investigateBadTestBox is taking over %s.' % (oCaseFile.sLongName,));
1789 fRc = self.investigateBadTestBox(oCaseFile);
1790
1791 elif oCaseFile.isVBoxUnitTest():
1792 self.dprint(u'investigateVBoxUnitTest is taking over %s.' % (oCaseFile.sLongName,));
1793 fRc = self.investigateVBoxUnitTest(oCaseFile);
1794
1795 elif oCaseFile.isVBoxInstallTest() or oCaseFile.isVBoxUnattendedInstallTest():
1796 self.dprint(u'investigateVBoxVMTest is taking over %s.' % (oCaseFile.sLongName,));
1797 fRc = self.investigateVBoxVMTest(oCaseFile, fSingleVM = True);
1798
1799 elif oCaseFile.isVBoxUSBTest():
1800 self.dprint(u'investigateVBoxVMTest is taking over %s.' % (oCaseFile.sLongName,));
1801 fRc = self.investigateVBoxVMTest(oCaseFile, fSingleVM = True);
1802
1803 elif oCaseFile.isVBoxStorageTest():
1804 self.dprint(u'investigateVBoxVMTest is taking over %s.' % (oCaseFile.sLongName,));
1805 fRc = self.investigateVBoxVMTest(oCaseFile, fSingleVM = True);
1806
1807 elif oCaseFile.isVBoxGAsTest():
1808 self.dprint(u'investigateVBoxVMTest is taking over %s.' % (oCaseFile.sLongName,));
1809 fRc = self.investigateVBoxVMTest(oCaseFile, fSingleVM = True);
1810
1811 elif oCaseFile.isVBoxAPITest():
1812 self.dprint(u'investigateVBoxApiTest is taking over %s.' % (oCaseFile.sLongName,));
1813 fRc = self.investigateVBoxApiTest(oCaseFile);
1814
1815 elif oCaseFile.isVBoxBenchmarkTest():
1816 self.dprint(u'investigateVBoxVMTest is taking over %s.' % (oCaseFile.sLongName,));
1817 fRc = self.investigateVBoxVMTest(oCaseFile, fSingleVM = False);
1818
1819 elif oCaseFile.isVBoxSmokeTest():
1820 self.dprint(u'investigateVBoxVMTest is taking over %s.' % (oCaseFile.sLongName,));
1821 fRc = self.investigateVBoxVMTest(oCaseFile, fSingleVM = False);
1822
1823 elif oCaseFile.isVBoxSerialTest():
1824 self.dprint(u'investigateVBoxVMTest is taking over %s.' % (oCaseFile.sLongName,));
1825 fRc = self.investigateVBoxVMTest(oCaseFile, fSingleVM = False);
1826
1827 else:
1828 self.vprint(u'reasoningFailures: Unable to classify test set: %s' % (oCaseFile.sLongName,));
1829 fRc = False;
1830 cGot += fRc is True;
1831
1832 self.vprint(u'reasoningFailures: Got %u out of %u' % (cGot, len(aoTestSets), ));
1833 return 0;
1834
1835
1836 def main(self):
1837 """
1838 The 'main' function.
1839 Return exit code (0, 1, etc).
1840 """
1841 # Database stuff.
1842 self.oDb = TMDatabaseConnection()
1843 self.oTestResultLogic = TestResultLogic(self.oDb);
1844 self.oTestSetLogic = TestSetLogic(self.oDb);
1845 self.oFailureReasonLogic = FailureReasonLogic(self.oDb);
1846 self.oTestResultFailureLogic = TestResultFailureLogic(self.oDb);
1847 self.asBsodReasons = self.oFailureReasonLogic.fetchForSheriffByNamedCategory(self.ksBsodCategory);
1848 self.asUnitTestReasons = self.oFailureReasonLogic.fetchForSheriffByNamedCategory(self.ksUnitTestCategory);
1849
1850 # Get a fix on our 'now' before we do anything..
1851 self.oDb.execute('SELECT CURRENT_TIMESTAMP - interval \'%s hours\'', (self.oConfig.cStartHoursAgo,));
1852 self.tsNow = self.oDb.fetchOne();
1853
1854 # If we're suppost to commit anything we need to get our user ID.
1855 rcExit = 0;
1856 if self.oConfig.fRealRun:
1857 self.oLogin = UserAccountLogic(self.oDb).tryFetchAccountByLoginName(VirtualTestSheriff.ksLoginName);
1858 if self.oLogin is None:
1859 rcExit = self.eprint('Cannot find my user account "%s"!' % (VirtualTestSheriff.ksLoginName,));
1860 else:
1861 self.uidSelf = self.oLogin.uid;
1862
1863 #
1864 # Do the stuff.
1865 #
1866 if rcExit == 0:
1867 rcExit = self.selfCheck();
1868 if rcExit == 0:
1869 rcExit = self.badTestBoxManagement();
1870 rcExit2 = self.reasoningFailures();
1871 if rcExit == 0:
1872 rcExit = rcExit2;
1873 # Redo the bad testbox management after failure reasons have been assigned (got timing issues).
1874 if rcExit == 0:
1875 rcExit = self.badTestBoxManagement();
1876
1877 # Cleanup.
1878 self.oFailureReasonLogic = None;
1879 self.oTestResultFailureLogic = None;
1880 self.oTestSetLogic = None;
1881 self.oTestResultLogic = None;
1882 self.oDb.close();
1883 self.oDb = None;
1884 if self.oLogFile is not None:
1885 self.oLogFile.close();
1886 self.oLogFile = None;
1887 return rcExit;
1888
1889if __name__ == '__main__':
1890 sys.exit(VirtualTestSheriff().main());
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