From 9698cfbf6d7d52518b7a3e8f4a371da8030a4140 Mon Sep 17 00:00:00 2001 From: 2Shirt <2xShirt@gmail.com> Date: Thu, 20 Sep 2018 00:35:05 -0600 Subject: [PATCH] Initial osTicket drive report section * Formatting is off, need to remove ASCII color escapes --- .bin/Scripts/functions/hw_diags.py | 156 ++++++++++++++++++++++++++++- 1 file changed, 153 insertions(+), 3 deletions(-) diff --git a/.bin/Scripts/functions/hw_diags.py b/.bin/Scripts/functions/hw_diags.py index 627ffa6d..2deecbd0 100644 --- a/.bin/Scripts/functions/hw_diags.py +++ b/.bin/Scripts/functions/hw_diags.py @@ -368,6 +368,155 @@ def osticket_set_drive_result(ticket_id, passed): except: ost_db['Errors'] = True +def post_drive_results(ticket_number): + """Post drive test results to osTicket.""" + tested = False + + # Check if test(s) were run + for t in ['NVMe/SMART', 'badblocks', 'iobenchmark']: + tested |= TESTS[t]['Enabled'] + if not tested or TESTS['NVMe/SMART']['Quick']: + # No tests were run so no post necessary + return + + # Build reports for all tested devices + for name, dev in sorted(TESTS['NVMe/SMART']['Devices'].items()): + dev_failed = False + dev_passed = True + dev_unknown = False + report = [] + + # Check all test results for dev + for t in ['NVMe/SMART', 'badblocks', 'iobenchmark']: + if not TESTS[t]['Enabled']: + continue + status = TESTS[t]['Status'].get(name, 'Unknown') + dev_failed |= status == 'NS' + dev_passed &= status == 'CS' + dev_unknown |= status in ('Working', 'Unknown') + if not dev_failed and not dev_passed and not dev_unknown: + # Assuming drive was skipped so no reply is needed + continue + + # Start drive report + if dev_failed: + report.append('Drive hardware diagnostics tests: FAILED') + elif dev_unknown: + report.append('Drive hardware diagnostics tests: UNKNOWN') + elif dev_passed: + report.append('Drive hardware diagnostics tests: Passed') + report.append('') + + # Drive description + report.append('{size} ({tran}) {model} {serial}'.format( + size=dev['lsblk'].get('size', '???b'), + tran=dev['lsblk'].get('tran', '???'), + model=dev['lsblk'].get('model', 'Unknown Model'), + serial=dev['lsblk'].get('serial', 'Unknown Serial'), + )) + report.append('') + + # Warnings (if any) + if dev.get('NVMe Disk', False): + if dev['Quick Health Ok']: + report.append('WARNING: NVMe support is still experimental') + else: + report.append('ERROR: NVMe disk is reporting critical warnings') + report.append('') + elif not dev['SMART Support']: + report.append('ERROR: Unable to retrieve SMART data') + report.append('') + elif not dev['SMART Pass']: + report.append('ERROR: SMART overall-health assessment result: FAILED') + report.append('') + + # NVMe/SMART Attributes + if dev.get('NVMe Disk', False): + report.append('NVMe Attributes:') + for attrib in sorted(ATTRIBUTES['NVMe'].keys()): + if attrib in dev['nvme-cli']: + report.append('{attrib:30}{value}'.format( + attrib=attrib, + value=dev['nvme-cli'][attrib], + )) + report[-1] = report[-1].strip().replace(' ', '.') + report[-1] = report[-1].replace('_', ' ') + elif dev['smartctl'].get('ata_smart_attributes', None): + report.append('SMART Attributes:') + s_table = dev['smartctl'].get('ata_smart_attributes', {}).get( + 'table', {}) + s_table = {a.get('id', 'Unknown'): a for a in s_table} + for attrib in sorted(ATTRIBUTES['SMART'].keys()): + if attrib in s_table: + report.append('{:0>2}/{:24}{} ({})'.format( + str(hex(int(attrib))).upper()[2:], + attrib, + s_table[attrib]['raw']['string'], + s_table[attrib]['name'], + )) + report[-1] = report[-1].strip().replace(' ', '.') + report[-1] = report[-1].replace('_', ' ') + report.append('') + + # badblocks + bb_status = TESTS['badblocks']['Status'].get(name, None) + if TESTS['badblocks']['Enabled'] and bb_status not in ['Denied', 'Skipped']: + report.append('badblocks:') + bb_result = TESTS['badblocks']['Results'].get( + name, + 'ERROR: Failed to read log.') + for line in bb_result.splitlines(): + line = line.strip() + if not line: + continue + if re.search('Pass completed', line, re.IGNORECASE): + line = re.sub( + r'Pass completed,?\s+', + r'', + line, + re.IGNORECASE) + report.append(line) + report.append('') + + # I/O Benchmark + io_status = TESTS['iobenchmark']['Status'].get(name, None) + if TESTS['iobenchmark']['Enabled'] and io_status not in ['Denied', 'Skipped']: + report.append('I/O Benchmark:') + io_result = TESTS['iobenchmark']['Results'].get( + name, + 'ERROR: Failed to read log.') + for line in io_result.splitlines(): + line = line.strip() + if not line: + continue + report.append(line) + report.append('') + + # TODO-REMOVE TESTING + with open('/home/twoshirt/__ost_report_{}.txt'.format(name), 'w') as f: + for line in report: + f.write('{}\n'.format(line.strip())) + + # Post reply for drive + osticket_post_reply( + ticket_id=ticket_number, + response='\n'.join(report)) + + # Mark ticket HDD/SSD pass/fail checkbox (as needed) + if dev_failed: + osticket_set_drive_result( + ticket_id=ticket_number, + passed=False) + elif dev_unknown: + pass + elif dev_passed: + osticket_set_drive_result( + ticket_id=ticket_number, + passed=True) + + # Mark ticket as NEEDS ATTENTION + osticket_needs_attention(ticket_id=ticket_number) + def run_badblocks(ticket_number): """Run a read-only test for all detected disks.""" aborted = False @@ -695,7 +844,7 @@ def run_mprime(ticket_number): if r: report.append(r.group(1)) except: - report.append(' ERROR: Failed to read log.') + report.append('ERROR: Failed to read log.') report.append('') report.append('Final temps:') log_path = '{}/Final Temps.log'.format(global_vars['LogDir']) @@ -708,7 +857,7 @@ def run_mprime(ticket_number): break report.append(line) except: - report.append(' ERROR: Failed to read log.') + report.append('ERROR: Failed to read log.') # Upload osTicket report osticket_post_reply( @@ -846,6 +995,7 @@ def run_tests(tests, ticket_number=None): run_iobenchmark(ticket_number) # Show results + post_drive_results(ticket_number) show_results() # Open log @@ -947,7 +1097,7 @@ def scan_disks(full_paths=False, only_path=None): if ask('Run tests on this device anyway?'): TESTS['NVMe/SMART']['Status'][dev_name] = 'OVERRIDE' else: - TESTS['NVMe/SMART']['Status'][dev_name] = 'NS' + TESTS['NVMe/SMART']['Status'][dev_name] = 'Skipped' TESTS['badblocks']['Status'][dev_name] = 'Denied' TESTS['iobenchmark']['Status'][dev_name] = 'Denied' print_standard(' ') # In case there's more than one "OVERRIDE" disk