WizardKit/scripts/wk/hw/osticket.py

216 lines
5.8 KiB
Python

"""WizardKit: osTicket hardware diagnostic functions"""
# vim: sts=2 sw=2 ts=2
import logging
import re
from wk import osticket
from wk.cfg.hw import (
REGEX_BLOCK_GRAPH,
REGEX_SMART_ATTRIBUTES,
)
from wk.hw import smart as hw_smart
from wk.ui import cli
# STATIC VARIABLES
LOG = logging.getLogger(__name__)
# Functions
def build_report(dev, dev_type, num_disk_tests=None):
"""Build report for posting to osTicket, returns str."""
report = []
# Combined result
if dev_type == 'CPU' or len(dev.tests) == num_disk_tests:
# Build list of failed tests (if any)
failed_tests = [t.name for t in dev.tests if t.failed]
failed_tests = [name.replace('Disk ', '') for name in failed_tests]
if len(failed_tests) > 2:
failed_tests = f'{", ".join(failed_tests[:-1])}, & {failed_tests[-1]}'
else:
failed_tests = ' & '.join(failed_tests)
# Get overall result
result = 'UNKNOWN'
if any(t.failed for t in dev.tests):
result = 'FAILED'
elif all(t.passed for t in dev.tests):
result = 'PASSED'
# Add to report
report.append(
f'{dev_type} hardware diagnostic tests: {result}'
f'{" ("+failed_tests+")" if failed_tests else ""}'
)
report.append('')
# Description
if hasattr(dev, 'cpu_description'):
report.append(dev.cpu_description)
else:
report.append(dev.description)
if hasattr(dev, 'ram_total'):
if len(dev.ram_dimms) == 1 and 'justTotalRAM' in dev.ram_dimms[0]:
report.append(f'{dev.ram_total} (Total - no DIMM info available)')
else:
report.append(f'{dev.ram_total} ({", ".join(dev.ram_dimms)})')
if hasattr(dev, 'serial') and dev.serial:
report.append(f'Serial Number: {dev.serial}')
report.append('')
# Notes
if hasattr(dev, 'notes') and dev.notes:
report.append('Notes')
report.extend([f'... {note}' for note in dev.notes])
report.append('')
# Tests
for test in dev.tests:
report.append(f'{test.name} ({test.status})')
# Report
if test.name == 'Disk Attributes' and dev.attributes:
report.extend(
convert_report(
hw_smart.generate_attribute_report(dev),
start_index=0,
),
)
else:
report.extend(convert_report(test.report, start_index=1))
# I/O graph upload report
report.extend(getattr(test, 'upload_report', []))
# Spacer
report.append('')
# Remove last line if empty
if not report[-1].strip():
report.pop(-1)
# Done
return cli.strip_colors('\n'.join(report))
def convert_report(original_report, start_index):
"""Convert report to an osTicket compatible type, returns list."""
report = []
# Convert report
for line in original_report[start_index:]:
# Remove colors and leading spaces
line = cli.strip_colors(line)
line = re.sub(r'^\s+', '', line)
# Disk I/O Benchmark
if REGEX_BLOCK_GRAPH.search(line):
line = REGEX_BLOCK_GRAPH.sub('', line)
line = line.strip()
# SMART attributes
match = REGEX_SMART_ATTRIBUTES.search(line)
if match:
# Switch decimal and hex labels
_dec = f'{match.group("decimal"):>3}'
_dec = osticket.pad_with_dots(_dec)
_hex = match.group('hex')
_data = match.group('data')
line = f'{_hex}/{_dec}: {_data}'
line = line.replace('failed', 'FAILED')
# Skip empty lines
if not line.strip():
continue
# Fix inner spacing
for spacing in re.findall(r'\s\s+', line):
new_padding = osticket.pad_with_dots(spacing)
new_padding += ' '
line = line.replace(spacing, new_padding)
# Indent line
line = f'... {line}'
# Add to (converted) report
report.append(line)
# Done
return report
def post_disk_results(state, num_disk_tests):
"""Post disk test results for all disks."""
disk_tests = []
for group in state.test_groups:
if group.name.startswith('Disk'):
disk_tests.extend(group.test_objects)
# Bail if no disk tests were run
if not disk_tests or state.ost.disabled:
return
# Post disk results
cli.print_info('Posting results to osTicket...')
for disk in state.disks:
state.ost.post_response(
build_report(disk, 'Disk', num_disk_tests),
color='Diags FAIL' if any(t.failed for t in disk.tests) else 'Diags',
)
def update_checkboxes(state, num_disk_tests):
"""Update osTicket checkboxes after confirmation."""
cpu_tests = []
disk_tests = []
num_disk_tests_run = len(state.test_groups)
# Build list of tests
for group in state.test_groups:
if group.name.startswith('CPU'):
cpu_tests.extend(group.test_objects)
num_disk_tests_run -= 1
elif group.name.startswith('Disk'):
disk_tests.extend(group.test_objects)
elif group.name.startswith('System'):
num_disk_tests_run -= 1
# Bail if osTicket integration disabled
if state.ost.disabled:
return
# Bail if values not confirmed
if not cli.ask('Update osTicket checkboxes using the data above?'):
return
# CPU max temp and pass/fail
if cpu_tests:
state.ost.set_cpu_max_temp(state.sensors.get_cpu_temp('Max'))
if any(t.failed for t in cpu_tests):
state.ost.set_flag_failed('CPU')
elif all(t.passed for t in cpu_tests):
state.ost.set_flag_passed('CPU')
# Check results for all disks
if state.disks:
all_disks_passed = True
for disk in state.disks:
if any(t.failed for t in disk.tests):
# Mark failed disk in osTicket and stop checking results
all_disks_passed = False
state.ost.set_flag_failed('Disk')
break
if not all(t.passed for t in disk.tests):
all_disks_passed = False
break
# All disks passed
if all_disks_passed and num_disk_tests_run == num_disk_tests:
# Only mark as passed if a full disk diagnostic passed
state.ost.set_flag_passed('Disk')
if __name__ == '__main__':
print("This file is not meant to be called directly.")