|
22 | 22 | """Generic user specific functionality.""" |
23 | 23 |
|
24 | 24 | import fnmatch |
| 25 | +import json |
25 | 26 | import logging |
26 | 27 | import os |
27 | 28 | import re |
@@ -203,6 +204,49 @@ def update_job_data(job: object) -> None: |
203 | 204 | job: job object. |
204 | 205 | """ |
205 | 206 | validate_output_data(job) |
| 207 | + lift_payload_report(job) |
| 208 | + |
| 209 | + |
| 210 | +def lift_payload_report(job: JobData) -> None: |
| 211 | + """Lift error information from the payload job report into the job object. |
| 212 | +
|
| 213 | + If the payload wrote a job report (config.Payload.jobreport) in the work directory, parse it and copy its |
| 214 | + error fields into the job object, so that the payload's own description of a failure is stored with the |
| 215 | + job record (exeErrorCode/exeErrorDiag) rather than only the pilot's interpretation of the wrapper output. |
| 216 | + The report is optional: a missing or unparsable file leaves the job object unchanged. |
| 217 | +
|
| 218 | + Expected report fields: exitCode (int), exitMsg (str). |
| 219 | +
|
| 220 | + Args: |
| 221 | + job: job object. |
| 222 | + """ |
| 223 | + path = os.path.join(job.workdir, config.Payload.jobreport) |
| 224 | + if not os.path.exists(path): |
| 225 | + logger.debug(f'no payload job report at {path}') |
| 226 | + return |
| 227 | + |
| 228 | + try: |
| 229 | + with open(path, 'r', encoding='utf-8') as _fp: |
| 230 | + report = json.load(_fp) |
| 231 | + except (OSError, ValueError) as error: |
| 232 | + logger.warning(f'failed to read payload job report {path}: {error}') |
| 233 | + return |
| 234 | + |
| 235 | + if not isinstance(report, dict): |
| 236 | + logger.warning(f'payload job report {path} does not contain a JSON object - ignoring it') |
| 237 | + return |
| 238 | + |
| 239 | + job.metadata = report |
| 240 | + |
| 241 | + exit_code = report.get('exitCode') |
| 242 | + exit_msg = report.get('exitMsg') |
| 243 | + if isinstance(exit_code, int) and exit_code != 0: |
| 244 | + job.exeerrorcode = exit_code |
| 245 | + if isinstance(exit_msg, str) and exit_msg: |
| 246 | + job.exeerrordiag = exit_msg[:500] |
| 247 | + logger.warning(f'payload job report: exitCode={exit_code} exitMsg={exit_msg}') |
| 248 | + elif isinstance(exit_code, int): |
| 249 | + logger.info('payload job report: exitCode=0') |
206 | 250 |
|
207 | 251 |
|
208 | 252 | def validate_output_data(job: JobData) -> None: |
|
0 commit comments