Changeset 61293 in vbox
- Timestamp:
- May 30, 2016 1:07:52 PM (8 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/ValidationKit/testmanager/batch/virtual_test_sheriff.py
r61283 r61293 47 47 48 48 # Test Manager imports 49 from testmanager.core.db import TMDatabaseConnection; 50 from testmanager.core.testbox import TestBoxLogic, TestBoxData; 51 from testmanager.core.testset import TestSetLogic, TestSetData; 52 from testmanager.core.testresults import TestResultLogic; 53 from testmanager.core.useraccount import UserAccountLogic; 49 from testmanager.core.db import TMDatabaseConnection; 50 from testmanager.core.build import BuildDataEx; 51 from testmanager.core.failurereason import FailureReasonLogic; 52 from testmanager.core.testbox import TestBoxLogic, TestBoxData; 53 from testmanager.core.testcase import TestCaseDataEx; 54 from testmanager.core.testgroup import TestGroupData; 55 from testmanager.core.testset import TestSetLogic, TestSetData; 56 from testmanager.core.testresults import TestResultLogic; 57 from testmanager.core.testresultfailures import TestResultFailureLogic, TestResultFailureData; 58 from testmanager.core.useraccount import UserAccountLogic; 59 60 61 class VirtualTestSheriffCaseFile(object): 62 """ 63 A failure investigation case file. 64 65 """ 66 67 68 ## Max log file we'll read into memory. (256 MB) 69 kcbMaxLogRead = 0x10000000; 70 71 72 def __init__(self, oSheriff, oTestSet, oTree, oBuild, oTestBox, oTestGroup, oTestCase): 73 self.oSheriff = oSheriff; 74 self.oTestSet = oTestSet; # TestSetData 75 self.oTree = oTree; # TestResultDataEx 76 self.oBuild = oBuild; # BuildDataEx 77 self.oTestBox = oTestBox; # TestBoxData 78 self.oTestGroup = oTestGroup; # TestGroupData 79 self.oTestCase = oTestCase; # TestCaseDataEx 80 self.sMainLog = ''; # The main log file. Empty string if not accessible. 81 82 # Generate a case file name. 83 self.sName = '#%u: %s' % (self.oTestSet.idTestSet, self.oTestCase.sName,) 84 self.sLongName = '#%u: "%s" on "%s" running %s %s (%s), "%s" by %s, using %s %s %s r%u' \ 85 % ( self.oTestSet.idTestSet, 86 self.oTestCase.sName, 87 self.oTestBox.sName, 88 self.oTestBox.sOs, 89 self.oTestBox.sOsVersion, 90 self.oTestBox.sCpuArch, 91 self.oTestBox.sCpuName, 92 self.oTestBox.sCpuVendor, 93 self.oBuild.oCat.sProduct, 94 self.oBuild.oCat.sBranch, 95 self.oBuild.oCat.sType, 96 self.oBuild.iRevision, ); 97 98 # Investigation notes. 99 self.tReason = None; # None or one of the ktReason_XXX constants. 100 self.dReasonForResultId = {}; # Reason assignments indexed by idTestResult. 101 102 # 103 # Reason. 104 # 105 106 def noteReason(self, tReason): 107 """ Notes down a possible reason. """ 108 self.oSheriff.dprint('noteReason: %s -> %s' % (self.tReason, tReason,)); 109 self.tReason = tReason; 110 111 def noteReasonForId(self, tReason, idTestResult): 112 """ Notes down a possible reason for a specific test result. """ 113 self.oSheriff.dprint('noteReasonForId: %u: %s -> %s' 114 % (idTestResult, self.dReasonForResultId.get(idTestResult, None), tReason,)); 115 self.dReasonForResultId[idTestResult] = tReason; 116 117 118 # 119 # Test classification. 120 # 121 122 def isVBoxTest(self): 123 """ Test classification: VirtualBox (using the build) """ 124 return self.oBuild.oCat.sProduct.lower() in [ 'virtualbox', 'vbox' ]; 125 126 def isVBoxUnitTest(self): 127 """ Test case classification: The unit test doing all our testcase/*.cpp stuff. """ 128 return self.isVBoxTest() \ 129 and self.oTestCase.sName.lower() == 'unit tests'; 130 131 def isVBoxInstallTest(self): 132 """ Test case classification: VirtualBox Guest installation test. """ 133 return self.isVBoxTest() \ 134 and self.oTestCase.sName.lower().startswith('install:'); 135 136 def isVBoxSmokeTest(self): 137 """ Test case classification: Smoke test. """ 138 return self.isVBoxTest() \ 139 and self.oTestCase.sName.lower().startswith('smoketest'); 140 141 142 # 143 # Utility methods. 144 # 145 146 def getMainLog(self): 147 """ 148 Tries to reads the main log file since this will be the first source of information. 149 """ 150 if len(self.sMainLog) > 0: 151 return self.sMainLog; 152 (oFile, oSizeOrError, _) = self.oTestSet.openFile('main.log', 'rb'); 153 if oFile is not None: 154 try: 155 self.sMainLog = oFile.read(min(self.kcbMaxLogRead, oSizeOrError)).decode('utf-8', 'replace'); 156 except Exception as oXcpt: 157 self.oSheriff.vprint('Error reading main log file: %s' % (oXcpt,)) 158 self.sMainLog = ''; 159 else: 160 self.oSheriff.vprint('Error opening main log file: %s' % (oSizeOrError,)); 161 return self.sMainLog; 162 163 def isSingleTestFailure(self): 164 """ 165 Figure out if this is a single test failing or if it's one of the 166 more complicated ones. 167 """ 168 if self.oTree.cErrors == 1: 169 return True; 170 if self.oTree.deepCountErrorContributers() <= 1: 171 return True; 172 return False; 173 54 174 55 175 … … 62 182 ksLoginName = 'vsheriff'; 63 183 64 65 184 def __init__(self): 66 185 """ 67 186 Parse command line. 68 187 """ 69 self.oDb = None; 70 self.tsNow = None; 71 self.oResultLogic = None; 72 self.oTestSetLogic = None; 73 self.oLogin = None; 74 self.uidSelf = -1; 75 self.oLogFile = None; 188 self.oDb = None; 189 self.tsNow = None; 190 self.oTestResultLogic = None; 191 self.oTestSetLogic = None; 192 self.oFailureReasonLogic = None; # FailureReasonLogic; 193 self.oTestResultFailureLogic = None; # TestResultFailureLogic 194 self.oLogin = None; 195 self.uidSelf = -1; 196 self.oLogFile = None; 76 197 77 198 oParser = OptionParser(); … … 84 205 oParser.add_option('-q', '--quiet', dest = 'fQuiet', action = 'store_true', default = False, 85 206 help = 'Quiet execution'); 86 oParser.add_option('-l', '--log', dest = 's BuildLogPath', metavar = '<url>', default = None,87 help = ' URL to the build logs (optional).');207 oParser.add_option('-l', '--log', dest = 'sLogFile', metavar = '<logfile>', default = None, 208 help = 'Where to log messages.'); 88 209 oParser.add_option('--debug', dest = 'fDebug', action = 'store_true', default = False, 89 210 help = 'Enables debug mode.'); … … 91 212 (self.oConfig, _) = oParser.parse_args(); 92 213 93 if self.oConfig.s BuildLogPath is not None and len(self.oConfig.sBuildLogPath) > 0:94 self.oLogFile = open(self.oConfig.s BuildLogPath, "a");214 if self.oConfig.sLogFile is not None and len(self.oConfig.sLogFile) > 0: 215 self.oLogFile = open(self.oConfig.sLogFile, "a"); 95 216 self.oLogFile.write('VirtualTestSheriff: $Revision$ \n'); 96 217 … … 126 247 self.oLogFile.write('info: %s\n' % (sText,)); 127 248 return 0; 249 250 251 def selfCheck(self): 252 """ Does some self checks, looking up things we expect to be in the database and such. """ 253 rcExit = 0; 254 for sAttr in dir(self.__class__): 255 if sAttr.startswith('ktReason_'): 256 tReason = getattr(self.__class__, sAttr); 257 oFailureReason = self.oFailureReasonLogic.cachedLookupByNameAndCategory(tReason[1], tReason[0]); 258 if oFailureReason is None: 259 rcExit = self.eprint('Failured to find failure reason "%s" in category "%s" in the database!' 260 % (tReason[1], tReason[0],)); 261 262 # Check the user account as well. 263 if self.oLogin is None: 264 oLogin = UserAccountLogic(self.oDb).tryFetchAccountByLoginName(VirtualTestSheriff.ksLoginName); 265 if oLogin is None: 266 rcExit = self.eprint('Cannot find my user account "%s"!' % (VirtualTestSheriff.ksLoginName,)); 267 return rcExit; 268 128 269 129 270 … … 165 306 166 307 # Get the most recent testsets for this box (descending on tsDone) and see how bad it is. 167 aoSets = self.oTestSetLogic.fetch ResultForTestBox(idTestBox, cHoursBack = cHoursBack, tsNow = tsNow);308 aoSets = self.oTestSetLogic.fetchSetsForTestBox(idTestBox, cHoursBack = cHoursBack, tsNow = tsNow); 168 309 cOkay = 0; 169 310 cBad = 0; … … 209 350 210 351 352 ## @name Failure reasons we know. 353 ## @{ 354 ktReason_Guru_Generic = ( 'Guru Meditations', 'Generic Guru Meditation' ); 355 ktReason_Guru_VERR_IEM_INSTR_NOT_IMPLEMENTED = ( 'Guru Meditations', 'VERR_IEM_INSTR_NOT_IMPLEMENTED' ); 356 ktReason_Guru_VERR_IEM_ASPECT_NOT_IMPLEMENTED = ( 'Guru Meditations', 'VERR_IEM_ASPECT_NOT_IMPLEMENTED' ); 357 ktReason_Guru_VINF_EM_TRIPLE_FAULT = ( 'Guru Meditations', 'VINF_EM_TRIPLE_FAULT' ); 358 ## @} 359 360 def caseClosed(self, oCaseFile): 361 """ 362 Reports the findings in the case and closes it. 363 """ 364 # 365 # Log it and create a dReasonForReasultId we can use below. 366 # 367 if len(oCaseFile.dReasonForResultId): 368 self.vprint('Closing %s with following reasons: %s' % (oCaseFile.sName, oCaseFile.dReasonForResultId,)); 369 dReasonForReasultId = oCaseFile.dReasonForResultId; 370 elif oCaseFile.tReason is not None: 371 self.vprint('Closing %s with following reason: %s' % (oCaseFile.sName, oCaseFile.tReason,)); 372 dReasonForReasultId = { oCaseFile.oTestSet.idTestResult: oCaseFile.tReason, }; 373 else: 374 self.vprint('Closing %s without a reason ... weird!' % (oCaseFile.sName,)); 375 return False; 376 377 # 378 # Add the test failure reason record(s). 379 # 380 for idTestResult, tReason in dReasonForReasultId.items(): 381 oFailureReason = self.oFailureReasonLogic.cachedLookupByNameAndCategory(tReason[1], tReason[0]); 382 if oFailureReason is not None: 383 oAdd = TestResultFailureData(); 384 oAdd.initFromValues(idTestResult = idTestResult, 385 idFailureReason = oFailureReason.idFailureReason, 386 uidAuthor = self.uidSelf, 387 idTestSet = oCaseFile.oTestSet.idTestSet, 388 sComment = 'Set by $Revision$',); # Handy for reverting later. 389 if self.oConfig.fRealRun: 390 try: 391 self.oTestResultFailureLogic.addEntry(oAdd, self.uidSelf, fCommit = True); 392 except Exception as oXcpt: 393 self.eprint('caseClosed: Exception "%s" while adding reason %s for %s' 394 % (oXcpt, oAdd, oCaseFile.sLongName,)); 395 else: 396 self.eprint('caseClosed: Cannot locate failure reason: %s / %s' % ( tReason[0], tReason[1],)); 397 return True; 398 399 400 def investigateBadTestBox(self, oCaseFile): 401 """ 402 Checks out bad-testbox statuses. 403 """ 404 _ = oCaseFile; 405 return False; 406 407 408 def investigateVBoxUnitTest(self, oCaseFile): 409 """ 410 Checks out a VBox unittest problem. 411 """ 412 _ = oCaseFile; 413 414 # 415 # As a first step we'll just fish out what failed here and report 416 # the unit test case name as the "reason". This can mostly be done 417 # using the TestResultDataEx bits, however in case it timed out and 418 # got killed we have to fish the test timing out from the logs. 419 # 420 421 # 422 # Report lone failures on the testcase, multiple failures must be 423 # reported directly on the failing test (will fix the test result 424 # listing to collect all of them). 425 # 426 return False; 427 428 429 ## Thing we search a main or VM log for to figure out why something went bust. 430 katSimpleMainAndVmLogReasons = [ 431 # ( Whether to stop on hit, needle, reason tuple ), 432 ( False, 'GuruMeditation', ktReason_Guru_Generic ), 433 ( True, 'VERR_IEM_INSTR_NOT_IMPLEMENTED', ktReason_Guru_VERR_IEM_INSTR_NOT_IMPLEMENTED ), 434 ( True, 'VERR_IEM_ASPECT_NOT_IMPLEMENTED', ktReason_Guru_VERR_IEM_ASPECT_NOT_IMPLEMENTED ), 435 ( True, 'VINF_EM_TRIPLE_FAULT', ktReason_Guru_VINF_EM_TRIPLE_FAULT ), 436 ]; 437 438 def investigateVBoxVMTest(self, oCaseFile, fSingleVM): 439 """ 440 Checks out a VBox VM test. 441 442 This is generic investigation of a test running one or more VMs, like 443 for example a smoke test or a guest installation test. 444 445 The fSingleVM parameter is a hint, which probably won't come in useful. 446 """ 447 _ = fSingleVM; 448 449 # 450 # Do some quick searches thru the main log to see if there is anything 451 # immediately incriminating evidence there. 452 # 453 sMainLog = oCaseFile.getMainLog(); 454 for fStopOnHit, sNeedle, tReason in self.katSimpleMainAndVmLogReasons: 455 if sMainLog.find(sNeedle) > 0: 456 oCaseFile.noteReason(tReason); 457 if fStopOnHit: 458 if oCaseFile.isSingleTestFailure(): 459 return self.caseClosed(oCaseFile); 460 break; 461 462 return False; 463 464 211 465 def reasoningFailures(self): 212 466 """ 213 467 Guess the reason for failures. 214 468 """ 215 469 # 470 # Get a list of failed test sets without any assigned failure reason. 471 # 472 aoTestSets = self.oTestSetLogic.fetchFailedSetsWithoutReason(cHoursBack = self.oConfig.cHoursBack, tsNow = self.tsNow); 473 for oTestSet in aoTestSets: 474 self.dprint(''); 475 self.dprint('reasoningFailures: Checking out test set #%u, status %s' % ( oTestSet.idTestSet, oTestSet.enmStatus,)) 476 477 # 478 # Open a case file and assign it to the right investigator. 479 # 480 (oTree, _ ) = self.oTestResultLogic.fetchResultTree(oTestSet.idTestSet); 481 oBuild = BuildDataEx().initFromDbWithId( self.oDb, oTestSet.idBuild, oTestSet.tsCreated); 482 oTestBox = TestBoxData().initFromDbWithGenId( self.oDb, oTestSet.idGenTestBox); 483 oTestGroup = TestGroupData().initFromDbWithId( self.oDb, oTestSet.idTestGroup, oTestSet.tsCreated); 484 oTestCase = TestCaseDataEx().initFromDbWithGenId( self.oDb, oTestSet.idGenTestCase, oTestSet.tsConfig); 485 486 oCaseFile = VirtualTestSheriffCaseFile(self, oTestSet, oTree, oBuild, oTestBox, oTestGroup, oTestCase); 487 488 if oTestSet.enmStatus == TestSetData.ksTestStatus_BadTestBox: 489 self.dprint('investigateBadTestBox is taking over %s.' % (oCaseFile.sLongName,)); 490 self.investigateBadTestBox(oCaseFile); 491 elif oCaseFile.isVBoxUnitTest(): 492 self.dprint('investigateVBoxUnitTest is taking over %s.' % (oCaseFile.sLongName,)); 493 self.investigateVBoxUnitTest(oCaseFile); 494 elif oCaseFile.isVBoxInstallTest(): 495 self.dprint('investigateVBoxVMTest is taking over %s.' % (oCaseFile.sLongName,)); 496 self.investigateVBoxVMTest(oCaseFile, fSingleVM = True); 497 elif oCaseFile.isVBoxSmokeTest(): 498 self.dprint('investigateVBoxVMTest is taking over %s.' % (oCaseFile.sLongName,)); 499 self.investigateVBoxVMTest(oCaseFile, fSingleVM = False); 500 else: 501 self.vprint('reasoningFailures: Unable to classify test set: %s' % (oCaseFile.sLongName,)); 216 502 return 0; 217 503 … … 223 509 """ 224 510 # Database stuff. 225 self.oDb = TMDatabaseConnection() 226 self.oResultLogic = TestResultLogic(self.oDb); 227 self.oTestSetLogic = TestSetLogic(self.oDb); 511 self.oDb = TMDatabaseConnection() 512 self.oTestResultLogic = TestResultLogic(self.oDb); 513 self.oTestSetLogic = TestSetLogic(self.oDb); 514 self.oFailureReasonLogic = FailureReasonLogic(self.oDb); 515 self.oTestResultFailureLogic = TestResultFailureLogic(self.oDb); 228 516 229 517 # Get a fix on our 'now' before we do anything.. … … 232 520 233 521 # If we're suppost to commit anything we need to get our user ID. 234 rcExit = 0;235 522 if self.oConfig.fRealRun: 236 523 self.oLogin = UserAccountLogic(self.oDb).tryFetchAccountByLoginName(VirtualTestSheriff.ksLoginName); … … 240 527 self.uidSelf = self.oLogin.uid; 241 528 529 # Do the stuff. 242 530 if rcExit == 0: 243 # Do the stuff. 531 rcExit = self.selfCheck(); 532 if rcExit == 0: 244 533 rcExit = self.badTestBoxManagement(); 245 534 rcExit2 = self.reasoningFailures(); … … 248 537 249 538 # Cleanup. 250 self.oTestSetLogic = None; 251 self.oResultLogic = None; 539 self.oFailureReasonLogic = None; 540 self.oTestResultFailureLogic = None; 541 self.oTestSetLogic = None; 542 self.oTestResultLogic = None; 252 543 self.oDb.close(); 253 544 self.oDb = None;
Note:
See TracChangeset
for help on using the changeset viewer.

