216 lines
5.8 KiB
Python
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.")
|