ATLAS Offline Software
Loading...
Searching...
No Matches
CheckSteps.py
Go to the documentation of this file.
2# Copyright (C) 2002-2024 CERN for the benefit of the ATLAS collaboration
3#
4
5'''
6Definitions of post-exec check steps in Trigger ART tests
7'''
8
9import os
10import re
11import subprocess
12import json
13import glob
14
15from TrigValTools.TrigValSteering.Step import Step, get_step_from_list
16from TrigValTools.TrigValSteering.ExecStep import ExecStep
17from TrigValTools.TrigValSteering.Common import art_input_eos, art_input_cvmfs, running_in_CI
18
20 '''Base class for steps comparing a file to a reference'''
21
22 def __init__(self, name):
23 super(RefComparisonStep, self).__init__(name)
24 self.reference = None
25 self.ref_test_name = None
26 self.input_file = None
27 self.explicit_reference = False # True if reference doesn't exist at configuration time
28
29 def configure(self, test):
30 if self.reference and self.ref_test_name:
31 self.misconfig_abort('Both options "reference" and "ref_test_name" used. Use at most one of them.')
32
33 if not self.reference and not self.ref_test_name:
34 self.ref_test_name = test.name
35
36 if self.reference is not None:
37 # Do nothing if the reference will be produced later
38 if self.explicit_reference:
39 return super(RefComparisonStep, self).configure(test)
40 # Do nothing if the reference exists
41 if os.path.isfile(self.reference):
42 return super(RefComparisonStep, self).configure(test)
43 # Try to find the file in DATAPATH
44 full_path = subprocess.check_output('find_data.py {}'.format(self.reference), shell=True).decode('utf-8').strip()
45 if os.path.isfile(full_path):
46 self.log.debug('%s using reference %s', self.name, full_path)
47 self.reference = full_path
48 return super(RefComparisonStep, self).configure(test)
49 else:
50 self.log.warning(
51 '%s failed to find reference %s - wrong path?',
52 self.name, self.reference)
53 return super(RefComparisonStep, self).configure(test)
54
55 if self.input_file is None:
56 self.misconfig_abort('input_file not specified')
57
58 branch = os.environ.get('AtlasBuildBranch') # Available after asetup
59 if branch:
60 branch = branch.split('--')[0] # experimental nightlies, e.g. main--mainGAUDI
61 if not branch:
62 branch = os.environ.get('gitlabTargetBranch') # Available in CI
63 if not branch:
64 jobName = os.environ.get('JOB_NAME') # Available in nightly build system (ATR-21836)
65 if jobName:
66 branch = jobName.split('_')[0].split('--')[0]
67 if not branch:
68 msg = 'Cannot determine the branch name, all variables are empty: AtlasBuildBranch, gitlabTargetBranch, JOB_NAME'
69 if self.required:
70 self.misconfig_abort(msg)
71 else:
72 self.log.warning(msg)
73 branch = 'UNKNOWN_BRANCH'
74
75 sub_path = '{}/ref/{}/test_{}/'.format(
76 test.package_name, branch, self.ref_test_name)
77 ref_eos = art_input_eos + sub_path + self.input_file
78 ref_cvmfs = art_input_cvmfs + sub_path + self.input_file
79 if os.path.isfile(ref_eos) and os.access(ref_eos, os.R_OK):
80 self.log.debug('%s using reference from EOS: %s',
81 self.name, ref_eos)
82 self.reference = ref_eos
83 elif os.path.isfile(ref_cvmfs) and os.access(ref_cvmfs, os.R_OK):
84 self.log.debug('%s using reference from CVMFS: %s',
85 self.name, ref_cvmfs)
86 self.reference = ref_cvmfs
87 else:
88 self.log.warning('%s failed to find reference %s in %s or %s',
89 self.name, sub_path + self.input_file,
90 art_input_eos, art_input_cvmfs)
91 self.reference = None
92
93 return super(RefComparisonStep, self).configure(test)
94
95
97 '''Base class for steps executed only if the input file exists'''
98
99 def __init__(self, name=None):
100 super(InputDependentStep, self).__init__(name)
101 self.input_file = None
102
103 def run(self, dry_run=False):
104 if self.input_file is None:
105 self.log.error('%s misconfiguration - no input file specified',
106 self.name)
107 self.result = 1
109 self.report_result()
110 return self.result, '# (internal) {} -> failed'.format(self.name)
111
112 if not dry_run and not os.path.isfile(self.input_file):
113 self.log.debug('Skipping %s because %s does not exist',
114 self.name, self.input_file)
115 self.result = 0
116 return self.result, '# (internal) {} -> skipped'.format(self.name)
117
118 return super(InputDependentStep, self).run(dry_run)
119
120
121class LogMergeStep(Step):
122 '''Merge several log files into one for post-processing'''
123
124 def __init__(self, name='LogMerge'):
125 super(LogMergeStep, self).__init__(name)
126 self.log_files = None
127 self.extra_log_regex = None
128 self.merged_name = 'athena.merged.log'
129 self.warn_if_missing = True
130
131 def configure(self, test):
132 if self.log_files is None:
133 self.log_files = []
134 for step in test.exec_steps:
135 self.log_files.append(step.name)
136 # Protect against infinite loop
137 if self.merged_name in self.log_files:
138 self.misconfig_abort(
139 'output log name %s is same as one of the input log names.'
140 ' This will lead to infinite loop, aborting.', self.merged_name)
141 super(LogMergeStep, self).configure(test)
142
144 if self.extra_log_regex:
145 files = os.listdir('.')
146 r = re.compile(self.extra_log_regex)
147 match_files = filter(r.match, files)
148 for f in match_files:
149 self.log_files.append(f)
150
151 def merge_logs(self):
152 try:
153 with open(self.merged_name, 'w', encoding='utf-8') as merged_file:
154 for log_name in self.log_files:
155 if not os.path.isfile(log_name):
156 if self.warn_if_missing:
157 self.log.warning('Cannot open %s', log_name)
158 merged_file.write(
159 '### WARNING Missing {} ###\n'.format(log_name))
160 continue
161 with open(log_name, encoding='utf-8') as log_file:
162 merged_file.write('### {} ###\n'.format(log_name))
163 # temporary workaround to ignore false positives in AOD->DAOD log parsing
164 if "Derivation" in log_name:
165 for line in log_file:
166 merged_file.write(line.replace('Selected dynamic Aux', 'Selected Dynamic Aux'))
167 else:
168 for line in log_file:
169 merged_file.write(line)
170 return 0
171 except OSError as e:
172 self.log.error('%s merging failed due to OSError: %s',
173 self.name, e.strerror)
174 return 1
175
176 def run(self, dry_run=False):
178 # Sort log files by modification time
179 self.log_files.sort(key=lambda f : os.path.getmtime(f) if os.path.isfile(f) else 0)
180 self.log.info('Running %s merging logs %s into %s',
181 self.name, self.log_files, self.merged_name)
182 if dry_run:
183 self.result = 0
184 else:
185 self.result = self.merge_logs()
186 return self.result, '# (internal) {} in={} out={}'.format(self.name, self.log_files, self.merged_name)
187
188
189class RootMergeStep(Step):
190 '''
191 Merge root files with hadd. Parameters are:
192 input_file - file(s) to be merged
193 merged_file - output file name
194 rename_suffix - if merged_file exists, it is renamed by adding this suffix
195 '''
196
197 def __init__(self, name='RootMerge'):
198 super(RootMergeStep, self).__init__(name)
199 self.input_file = None
200 self.merged_file = None
201 self.rename_suffix = None
202 self.executable = 'hadd'
203
204 def configure(self, test=None):
205 self.args += ' ' + self.merged_file + ' ' + self.input_file
206 super(RootMergeStep, self).configure(test)
207
208 def run(self, dry_run=False):
209 file_list_to_check = self.input_file.split()
210 if os.path.isfile(self.merged_file) and self.rename_suffix:
211 old_name = os.path.splitext(self.merged_file)
212 new_name = old_name[0] + self.rename_suffix + old_name[1]
213 self.executable = 'mv {} {}; {}'.format(self.merged_file, new_name, self.executable)
214 if new_name in file_list_to_check:
215 file_list_to_check.remove(new_name)
216 file_list_to_check.append(self.merged_file)
217 self.log.debug('%s checking if the input files exist: %s', self.name, str(file_list_to_check))
218 if not dry_run:
219 for file_name in file_list_to_check:
220 if len(glob.glob(file_name)) < 1:
221 self.log.warning('%s: file %s requested to be merged but does not exist', self.name, file_name)
222 self.result = 1
223 return self.result, '# (internal) {} in={} out={} -> failed'.format(self.name, self.input_file, self.merged_file)
224 return super(RootMergeStep, self).run(dry_run)
225
226
227class ZipStep(Step):
228 '''Compress a large log file'''
229
230 def __init__(self, name='Zip'):
231 super(ZipStep, self).__init__(name)
232 self.zip_output = None
233 self.zip_input = None
234 self.executable = 'tar'
235 self.args = '-czf'
236 self.output_stream = Step.OutputStream.STDOUT_ONLY
237
238 def configure(self, test=None):
239 self.args += ' '+self.zip_output+' '+self.zip_input
240 # Remove the file after zipping
241 self.args += ' && rm ' + self.zip_input
242 super(ZipStep, self).configure(test)
243
244
245class CheckLogStep(Step):
246 '''Execute CheckLog looking for errors or warnings in a log file'''
247
248 def __init__(self, name):
249 super(CheckLogStep, self).__init__(name)
250 self.executable = 'check_log.py'
251 self.log_file = None
252 self.check_errors = True
253 self.check_warnings = False
254 self.config_file = None
255 self.args = '--showexcludestats'
256 # The following three are updated in configure() if not set
257 self.required = None
259 self.output_stream = None
260
261 def configure(self, test):
262 if self.config_file is None:
263 if test.package_name == 'TrigP1Test':
264 self.config_file = 'checklogTrigP1Test.conf'
265 elif test.package_name == 'TrigValTools':
266 self.config_file = 'checklogTrigValTools.conf'
267 else:
268 self.config_file = 'checklogTriggerTest.conf'
269 if self.log_file is None:
270 if len(test.exec_steps) == 1:
271 self.log_file = test.exec_steps[0].name+'.log'
272 else:
273 self.log_file = 'athena.log'
274 if self.check_errors:
275 self.args += ' --errors'
276 if self.check_warnings:
277 self.args += ' --warnings'
278
279 errors_only = self.check_errors and not self.check_warnings
280 if self.output_stream is None:
281 self.output_stream = Step.OutputStream.FILE_AND_STDOUT if errors_only else Step.OutputStream.FILE_ONLY
282 if self.auto_report_result is None:
283 self.auto_report_result = errors_only
284 if self.required is None:
285 self.required = errors_only
286
287 self.args += ' --config {} {}'.format(self.config_file, self.log_file)
288
289 super(CheckLogStep, self).configure(test)
290
291
293 '''Execute RegTest comparing a log file against a reference'''
294
295 def __init__(self, name='RegTest'):
296 super(RegTestStep, self).__init__(name)
297 self.regex = 'REGTEST'
298 self.executable = 'diff'
299 self.input_base_name = 'athena'
301 self.output_stream = Step.OutputStream.FILE_AND_STDOUT
302
303 def configure(self, test):
304 self.input_file = self.input_base_name+'.regtest'
305 RefComparisonStep.configure(self, test)
306 self.args += ' -U 2 -b {} {}'.format(self.input_file, self.reference)
307 Step.configure(self, test)
308
309 def prepare_inputs(self):
310 log_file = self.input_base_name+'.log'
311 if not os.path.isfile(log_file):
312 self.log.error('%s input file %s is missing', self.name, log_file)
313 return False
314 with open(log_file, encoding='utf-8') as f_in:
315 matches = re.findall('({}.*).*$'.format(self.regex),
316 f_in.read(), re.MULTILINE)
317 with open(self.input_file, 'w', encoding='utf-8') as f_out:
318 for line in matches:
319 linestr = str(line[0]) if type(line) is tuple else line
320 f_out.write(linestr+'\n')
321 return True
322
323 def rename_ref(self):
324 try:
325 if self.reference:
326 new_name = os.path.basename(self.reference) + '.new'
327 else:
328 new_name = os.path.basename(self.input_file) + '.new'
329 os.rename(self.input_file, new_name)
330 self.log.debug('Renamed %s to %s', self.input_file, new_name)
331 except OSError:
332 self.log.warning('Failed to rename %s to %s',
333 self.input_file, new_name)
334
335 def run(self, dry_run=False):
336 if not dry_run and not self.prepare_inputs():
337 self.log.error('%s failed in prepare_inputs()', self.name)
338 self.result = 1
339 if self.auto_report_result:
340 self.report_result()
341 return self.result, '# (internal) {} -> failed'.format(self.name)
342 if self.reference is None:
343 self.log.error('Missing reference for %s', self.name)
344 if not dry_run:
345 self.rename_ref()
346 self.result = 999
347 if self.auto_report_result:
348 self.report_result()
349 return self.result, '# (internal) {} -> failed'.format(self.name)
350 retcode, cmd = super(RegTestStep, self).run(dry_run)
351 if not dry_run:
352 self.rename_ref()
353 return retcode, cmd
354
355
357 '''Execute RootComp comparing histograms against a reference'''
358
359 def __init__(self, name='RootComp'):
360 super(RootCompStep, self).__init__(name)
361 self.input_file = 'expert-monitoring.root'
362 self.executable = 'rootcomp.py'
364
365 def configure(self, test):
366 RefComparisonStep.configure(self, test)
367 if running_in_CI():
368 # drawing the diff output may be slow and is not needed for CI
369 self.args += ' --noRoot --noPS'
370 self.args += ' {} {}'.format(self.reference, self.input_file)
371 Step.configure(self, test)
372
373 def run(self, dry_run=False):
374 if self.reference is None:
375 if not os.path.isfile(self.input_file):
376 self.log.debug(
377 'Skipping %s because both reference and input are missing',
378 self.name)
379 self.result = 0
380 return self.result, '# (internal) {} -> skipped'.format(self.name)
381 else: # input exists but reference not
382 self.log.error('Missing reference for %s', self.name)
383 self.result = 999
384 if self.auto_report_result:
385 self.report_result()
386 return self.result, '# (internal) {} -> failed'.format(self.name)
387 retcode, cmd = super(RootCompStep, self).run(dry_run)
388 return retcode, cmd
389
390
392 '''Execute the PerfMon ntuple post-processing'''
393
394 def __init__(self, name='PerfMon'):
395 super(PerfMonStep, self).__init__(name)
396 self.input_file = None
397 self.executable = 'perfmon.py'
398 self.args = '-f 0.90'
399
400 def configure(self, test):
401 if not self.input_file:
402 num_athenaHLT_steps = sum([1 for step in test.exec_steps if step.type == 'athenaHLT'])
403 if num_athenaHLT_steps > 0:
404 self.input_file = 'athenaHLT_workers/athenaHLT-01/ntuple.pmon.gz'
405 else:
406 self.input_file = 'ntuple.pmon.gz'
407 self.args += ' '+self.input_file
408 super(PerfMonStep, self).configure(test)
409
410
411class TailStep(Step):
412 '''Copy the last N lines of a log file into a separate file'''
413
414 def __init__(self, name='Tail'):
415 super(TailStep, self).__init__(name)
416 self.log_file = 'athena.log'
417 self.output_name = None
418 self.executable = 'tail'
419 self.num_lines = 5000
420 self.output_stream = Step.OutputStream.STDOUT_ONLY
421
422 def configure(self, test):
423 if self.output_name is None:
424 split = os.path.splitext(self.log_file)
425 self.output_name = split[0]+'.tail'
426 if len(split) > 1:
427 self.output_name += split[1]
428 self.args += ' -n {:d}'.format(self.num_lines)
429 self.args += ' '+self.log_file
430 self.args += ' >'+self.output_name
431 super(TailStep, self).configure(test)
432
433
434class DownloadRefStep(Step):
435 '''Execute art.py download to get results from previous days'''
436
437 def __init__(self, name='DownloadRef'):
438 super(DownloadRefStep, self).__init__(name)
439 self.executable = 'art.py'
440 self.args = 'download'
441 self.artpackage = None
442 self.artjobname = None
443 self.timeout = 20*60
444 self.required = True
446
447 def configure(self, test):
448 if not self.artpackage:
449 self.artpackage = test.package_name
450 if not self.artjobname:
451 self.artjobname = 'test_'+test.name+'.py'
452 self.args += ' '+self.artpackage+' '+self.artjobname
453 super(DownloadRefStep, self).configure(test)
454
455
457 '''Execute histSizes.py to count histograms in a ROOT file'''
458
459 def __init__(self, name='HistCount'):
460 super(HistCountStep, self).__init__(name)
461 self.input_file = 'expert-monitoring.root'
462 self.executable = 'histSizes.py'
463 self.args = '-t'
464
465 def configure(self, test):
466 self.args += ' '+self.input_file
467 super(HistCountStep, self).configure(test)
468
469
471 '''
472 Execute chainDump.py to print trigger counts from histograms to text files
473 '''
474
475 def __init__(self, name='ChainDump'):
476 super(ChainDumpStep, self).__init__(name)
477 self.input_file = 'expert-monitoring.root'
478 self.executable = 'chainDump.py'
479 self.args = '--json --yaml'
480
481 def configure(self, test):
482 self.args += ' -f '+self.input_file
483 super(ChainDumpStep, self).configure(test)
484
485
487 '''
488 Execute chainComp.py to compare counts from chainDump.py to a reference
489 '''
490
491 def __init__(self, name='ChainComp'):
492 super(ChainCompStep, self).__init__(name)
493 self.input_file = 'chainDump.yml'
495 self.executable = 'chainComp.py'
496 self.args = ''
498 self.output_stream = Step.OutputStream.FILE_AND_STDOUT
499 self.depends_on_exec = True # skip if ExecSteps failed
500
501 def configure(self, test):
502 if not self.reference_from_release:
503 RefComparisonStep.configure(self, test)
504 if self.reference:
505 self.args += ' -r ' + self.reference
506 # else chainComp.py finds the reference in DATAPATH on its own
507 self.args += ' ' + self.input_file
508 Step.configure(self, test)
509
510
512 '''Execute trig-test-json.py to create extra-results.json file'''
513
514 def __init__(self, name='TrigTestJson'):
515 super(TrigTestJsonStep, self).__init__(name)
516 self.executable = 'trig-test-json.py'
517
518
520 '''
521 Execute checkFile and checkxAOD for POOL files.
522 executable and input_file can have multiple comma-separated values
523 '''
524
525 def __init__(self,name='CheckFile',input_file='AOD.pool.root'):
526 super(CheckFileStep, self).__init__(name)
527 self.input_file = input_file
528 self.executable = 'checkFile.py,checkxAOD.py'
529 self.__executables__ = None
530 self.__input_files__ = None
531
532 def configure(self, test):
533 # Skip the check if all test steps are athenaHLT (no POOL files)
534 test_types = [step.type for step in test.exec_steps]
535 num_athenaHLT = sum(1 for tt in test_types if tt == 'athenaHLT')
536 if num_athenaHLT == len(test_types):
537 self.log.debug('%s will be skipped because all exec steps use athenaHLT')
538 self.__executables__ = []
539 self.__input_files__ = []
540 return
541 self.__executables__ = self.executable.split(',')
542 self.__input_files__ = set(self.input_file.split(','))
543 super(CheckFileStep, self).configure(test)
544
545 def run(self, dry_run=False):
546 ret_codes = []
547 commands = []
548 for f in self.__input_files__:
549 for ex in self.__executables__:
550 self.executable = ex
551 self.input_file = f
552 self.args = f
553 ex_base = ex.split('.')[0:-1]
554 self.log_file_name = f + '.' + ''.join(ex_base)
555 ret, cmd = super(CheckFileStep, self).run(dry_run)
556 ret_codes.append(ret)
557 commands.append(cmd)
558
559 # Merge executed commands for logging
560 merged_cmd = ''
561 for cmd in commands:
562 if '(internal)' not in cmd:
563 merged_cmd += cmd+'; '
564 if len(merged_cmd) == 0: # can happen if all exec steps are type athenaHLT
565 merged_cmd = '# (internal) {} -> skipped'.format(self.name)
566 ret_codes.append(0)
567
568 return max(ret_codes), merged_cmd
569
570
571class ZeroCountsStep(Step):
572 '''
573 Check if all counts are zero.
574 input_file can have multiple comma-separated values
575 '''
576
577 def __init__(self, name='ZeroCounts'):
578 super(ZeroCountsStep, self).__init__(name)
579 self.input_file = 'HLTChain.txt,HLTTE.txt,L1AV.txt'
581 self.required = True
582 self.__input_files__ = None
583
584 def configure(self, test=None):
585 self.__input_files__ = self.input_file.split(',')
586
587 def check_zero_counts(self, input_file):
588 if not os.path.isfile(input_file):
589 self.log.debug(
590 'Skipping %s for %s because the file does not exist',
591 self.name, input_file)
592 return -1
593 lines_checked = 0
594 with open(input_file, encoding='utf-8') as f_in:
595 for line in f_in.readlines():
596 split_line = line.split()
597 lines_checked += 1
598 if int(split_line[-1]) != 0:
599 return 0 # at least one non-zero count
600 if lines_checked == 0:
601 self.log.error('Failed to read counts from %s', input_file)
602 return 1 # all counts are zero
603
604 def run(self, dry_run=False):
605 results = []
606 for input_file in self.__input_files__:
607 results.append(self.check_zero_counts(input_file))
608
609 self.result = max(results)
610 cmd = '# (internal) {} for {}'.format(self.name, self.__input_files__)
611 if self.result < 0:
612 cmd = '# (internal) {} -> skipped'.format(self.name)
613 self.result = 0
614 return self.result, cmd
615 self.log.info('Running %s step', self.name)
616 if self.auto_report_result:
617 self.report_result()
618 return self.result, cmd
619
620
622 '''Count messages printed inside event loop'''
623
624 def __init__(self, name='MessageCount'):
625 super(MessageCountStep, self).__init__(name)
626 self.executable = 'messageCounter.py'
627 self.log_regex = r'(athena\.(?!.*tail).*log$|athenaHLT:.*\.out$|^log\.(.*to.*|Derivation))'
628 self.skip_logs = []
629 self.start_pattern = r'(HltEventLoopMgr|AthenaHiveEventLoopMgr).*INFO Starting loop on events'
630 self.end_pattern = r'(HltEventLoopMgr.*INFO All events processed|AthenaHiveEventLoopMgr.*INFO.*Loop Finished)'
631 self.print_on_fail = None
632 self.thresholds = {}
634 self.depends_on_exec = True # skip if ExecSteps failed
635
636 def configure(self, test):
637 self.args += ' -s "{:s}"'.format(self.start_pattern)
638 self.args += ' -e "{:s}"'.format(self.end_pattern)
639 if self.print_on_fail is None:
640 self.print_on_fail = self.required
641 if self.print_on_fail:
642 self.args += ' --saveAll'
643
644 max_events = test.exec_steps[0].max_events if isinstance(test.exec_steps[0], ExecStep) else 0
645 if 'WARNING' not in self.thresholds:
646 self.thresholds['WARNING'] = 0
647 if 'INFO' not in self.thresholds:
648 self.thresholds['INFO'] = max_events
649 if 'DEBUG' not in self.thresholds:
650 self.thresholds['DEBUG'] = 0
651 if 'VERBOSE' not in self.thresholds:
652 self.thresholds['VERBOSE'] = 0
653 if 'other' not in self.thresholds:
654 self.thresholds['other'] = max_events
655 super(MessageCountStep, self).configure(test)
656
657 def run(self, dry_run=False):
658 files = os.listdir('.')
659 r = re.compile(self.log_regex)
660 log_files = [f for f in filter(r.match, files) if f not in self.skip_logs]
661 if not log_files and not dry_run:
662 self.log.error('%s found no log files matching the pattern %s', self.name, self.log_regex)
663 self.result = 1
664 if self.auto_report_result:
665 self.report_result()
666 return self.result, '# (internal) {} -> failed'.format(self.name)
667 self.args += ' ' + ' '.join(log_files)
668 auto_report = self.auto_report_result
669 self.auto_report_result = False
670 ret, cmd = super(MessageCountStep, self).run(dry_run)
671 self.auto_report_result = auto_report
672 if ret != 0:
673 self.log.error('%s failed', self.name)
674 self.result = 1
675 if self.auto_report_result:
676 self.report_result()
677 return self.result, cmd
678
679 for log_file in log_files:
680 json_file = 'MessageCount.{:s}.json'.format(log_file)
681 if self.print_on_fail:
682 all_json_file = 'Messages.{:s}.json'.format(log_file)
683 if not os.path.isfile(json_file):
684 self.log.warning('%s cannot open file %s', self.name, json_file)
685 with open(json_file) as f:
686 summary = json.load(f)
687 for level, threshold in self.thresholds.items():
688 if summary[level] > threshold:
689 self.result += 1
690 self.log.info(
691 '%s Number of %s messages %s in %s is higher than threshold %s',
692 self.name, level, summary[level], log_file, threshold)
693 if self.print_on_fail:
694 self.log.info('%s Printing all %s messages from %s', self.name, level, log_file)
695 with open(all_json_file) as af:
696 all_msg = json.load(af)
697 for msg in all_msg[level]:
698 print(msg.strip()) # noqa: ATL901
699
700 if self.auto_report_result:
701 self.report_result()
702 return self.result, cmd
703
704
705def produces_log(step):
706 '''
707 Helper function checking whether a Step output_stream value
708 indicates that it will produce a log file
709 '''
710 return step.output_stream == Step.OutputStream.FILE_ONLY or \
711 step.output_stream == Step.OutputStream.FILE_AND_STDOUT
712
713
714def default_check_steps(test, checkfile_input='AOD.pool.root,ESD.pool.root,RDO_TRIG.pool.root,DAOD_PHYS.DAOD.pool.root'):
715 '''
716 Create the default list of check steps for a test. The configuration
717 depends on the package name and the type of exec steps (athena or
718 athenaHLT or transforms).
719 '''
720
721 check_steps = []
722
723 # Log merging
724 if len(test.exec_steps) == 1:
725 exec_step = test.exec_steps[0]
726 if exec_step.type == 'athenaHLT' and produces_log(exec_step):
727 logmerge = LogMergeStep()
728 logmerge.merged_name = 'athena.log'
729 logmerge.log_files = ['athenaHLT.log']
730 nforks = 1 if exec_step.forks is None else exec_step.forks
731 for n in range(1, 1+nforks):
732 logmerge.log_files.append('athenaHLT:{:02d}.out'.format(n))
733 logmerge.log_files.append('athenaHLT:{:02d}.err'.format(n))
734 check_steps.append(logmerge)
735 else:
736 logmerge = LogMergeStep()
737 logmerge.merged_name = 'athena.log'
738 logmerge.log_files = []
739 for exec_step in test.exec_steps:
740 if not produces_log(exec_step):
741 continue
742 logmerge.log_files.append(exec_step.get_log_file_name())
743 if exec_step.type == 'athenaHLT':
744 logmerge.extra_log_regex = 'athenaHLT:.*(.out|.err)'
745 check_steps.append(logmerge)
746 log_to_check = None
747 log_to_zip = None
748 if len(check_steps) > 0 and isinstance(check_steps[-1], LogMergeStep):
749 log_to_check = check_steps[-1].merged_name
750 log_to_zip = check_steps[-1].merged_name
751
752 # Reco_tf log merging
753 reco_tf_steps = [step for step in test.exec_steps if step.type in ['Reco_tf', 'Trig_reco_tf', 'Derivation_tf']]
754 if len(reco_tf_steps) > 0:
755 reco_tf_logmerge = LogMergeStep('LogMerge_Reco_tf')
756 reco_tf_logmerge.warn_if_missing = False
757 # FIXME: drop AODtoDAOD once test_trigAna_AODtoDAOD_run2_build.py is migrated to Derivation_tf
758 tf_names = ['HITtoRDO', 'Overlay', 'RDOtoRDOTrigger', 'RAWtoESD', 'ESDtoAOD',
759 'PhysicsValidation', 'RAWtoALL',
760 'BSRDOtoRAW', 'DRAWCOSTtoNTUPCOST', 'AODtoNTUPRATE', 'Derivation', 'AODtoDAOD']
761 reco_tf_logmerge.log_files = ['log.'+tf_name for tf_name in tf_names]
762 if not get_step_from_list('LogMerge', check_steps):
763 for step in reco_tf_steps:
764 reco_tf_logmerge.log_files.append(step.get_log_file_name())
765 reco_tf_logmerge.merged_name = 'athena.merged.log'
766 log_to_zip = reco_tf_logmerge.merged_name
767 if log_to_check is not None:
768 reco_tf_logmerge.log_files.append(log_to_check)
769 log_to_check = reco_tf_logmerge.merged_name
770 log_to_check = reco_tf_logmerge.merged_name
771 check_steps.append(reco_tf_logmerge)
772
773 # Histogram merging for athenaHLT forks
774 num_athenaHLT_steps = sum([1 for step in test.exec_steps if step.type == 'athenaHLT'])
775 if num_athenaHLT_steps > 0:
776 histmerge = RootMergeStep('HistMerge')
777 histmerge.merged_file = 'expert-monitoring.root'
778 histmerge.input_file = 'athenaHLT_workers/*/expert-monitoring.root expert-monitoring-mother.root'
779 histmerge.rename_suffix = '-mother'
780 check_steps.append(histmerge)
781
782 # CheckLog for errors
783 checklog = CheckLogStep('CheckLog')
784 if log_to_check is not None:
785 checklog.log_file = log_to_check
786 check_steps.append(checklog)
787
788 # CheckLog for warnings
789 checkwarn = CheckLogStep('Warnings')
790 checkwarn.check_errors = False
791 checkwarn.check_warnings = True
792 if log_to_check is not None:
793 checkwarn.log_file = log_to_check
794 check_steps.append(checkwarn)
795
796 # MessageCount
797 msgcount = MessageCountStep('MessageCount')
798 for logmerge in [step for step in check_steps if isinstance(step, LogMergeStep)]:
799 msgcount.skip_logs.append(logmerge.merged_name)
800 check_steps.append(msgcount)
801
802 # Tail (probably not so useful these days)
803 tail = TailStep()
804 if log_to_check is not None:
805 tail.log_file = log_to_check
806 check_steps.append(tail)
807
808 # Histogram-based steps
809 check_steps.append(RootCompStep())
810 check_steps.append(ChainDumpStep())
811 check_steps.append(HistCountStep())
812
813 # ZeroCounts
814 check_steps.append(ZeroCountsStep())
815
816 # Extra JSON
817 check_steps.append(TrigTestJsonStep())
818
819 # CheckFile
820 check_steps.append(CheckFileStep(input_file=checkfile_input))
821
822 # Zip the merged log (can be large and duplicates information)
823 if log_to_zip is not None:
824 zip_step = ZipStep()
825 zip_step.zip_input = log_to_zip
826 zip_step.zip_output = log_to_zip+'.tar.gz'
827 check_steps.append(zip_step)
828
829 # return the steps
830 return check_steps
831
832
833def add_step_after_type(step_list, ref_type, step_to_add):
834 '''
835 Insert step_to_add into step_list after the last step of type ref_type.
836 If the list has no steps of type ref_type, append step_to_add at the end of the list.
837 '''
838 index_to_add = -1
839 for index, step in enumerate(step_list):
840 if isinstance(step, ref_type):
841 index_to_add = index+1
842 if index_to_add > 0:
843 step_list.insert(index_to_add, step_to_add)
844 else:
845 step_list.append(step_to_add)
const bool debug
void sort(typename DataModel_detail::iterator< DVL > beg, typename DataModel_detail::iterator< DVL > end)
Specialization of sort for DataVector/List.
void print(char *figname, TCanvas *c1)
#define max(a, b)
Definition cfImp.cxx:41
__init__(self, name='CheckFile', input_file='AOD.pool.root')
STL class.
std::vector< std::string > split(const std::string &s, const std::string &t=":")
Definition hcg.cxx:177
default_check_steps(test, checkfile_input='AOD.pool.root, ESD.pool.root, RDO_TRIG.pool.root, DAOD_PHYS.DAOD.pool.root')
add_step_after_type(step_list, ref_type, step_to_add)
Definition run.py:1