Update HW diags and ddrescue to use new HW classes

This commit is contained in:
2Shirt 2022-04-04 18:31:52 -06:00
parent 56e145942a
commit 8582046948
Signed by: 2Shirt
GPG key ID: 152FAC923B0E132C
2 changed files with 52 additions and 46 deletions

View file

@ -27,7 +27,10 @@ from wk.cfg.ddrescue import (
DDRESCUE_SETTINGS, DDRESCUE_SETTINGS,
DDRESCUE_SPECIFIC_PASS_SETTINGS, DDRESCUE_SPECIFIC_PASS_SETTINGS,
) )
from wk.hw import obj as hw_obj from wk.hw import disk as hw_disk
from wk.hw import sensors as hw_sensors
from wk.hw import system as hw_system
from wk.hw.test import Test
# STATIC VARIABLES # STATIC VARIABLES
@ -272,7 +275,7 @@ class BlockPair():
"""Run safety check and abort if necessary.""" """Run safety check and abort if necessary."""
dest_size = -1 dest_size = -1
if self.destination.exists(): if self.destination.exists():
dest_obj = hw_obj.Disk(self.destination) dest_obj = hw_disk.Disk(self.destination)
dest_size = dest_obj.details['size'] dest_size = dest_obj.details['size']
del dest_obj del dest_obj
@ -488,7 +491,7 @@ class State():
if settings['Partition Mapping']: if settings['Partition Mapping']:
# Resume previous run, load pairs from settings file # Resume previous run, load pairs from settings file
for part_map in settings['Partition Mapping']: for part_map in settings['Partition Mapping']:
bp_source = hw_obj.Disk( bp_source = hw_disk.Disk(
f'{self.source.path}{source_sep}{part_map[0]}', f'{self.source.path}{source_sep}{part_map[0]}',
) )
bp_dest = pathlib.Path( bp_dest = pathlib.Path(
@ -948,7 +951,7 @@ class State():
"""Run safety checks for destination and abort if necessary.""" """Run safety checks for destination and abort if necessary."""
try: try:
self.destination.safety_checks() self.destination.safety_checks()
except hw_obj.CriticalHardwareError as err: except hw_disk.CriticalHardwareError as err:
std.print_error( std.print_error(
f'Critical error(s) detected for: {self.destination.path}', f'Critical error(s) detected for: {self.destination.path}',
) )
@ -1097,7 +1100,7 @@ class State():
string = '' string = ''
# Build base string # Build base string
if isinstance(obj, hw_obj.Disk): if isinstance(obj, hw_disk.Disk):
string = f'{obj.path} {obj.description}' string = f'{obj.path} {obj.description}'
elif obj.is_dir(): elif obj.is_dir():
string = f'{obj}/' string = f'{obj}/'
@ -1122,7 +1125,7 @@ class State():
if self.source: if self.source:
source_exists = self.source.path.exists() source_exists = self.source.path.exists()
if self.destination: if self.destination:
if isinstance(self.destination, hw_obj.Disk): if isinstance(self.destination, hw_disk.Disk):
dest_exists = self.destination.path.exists() dest_exists = self.destination.path.exists()
else: else:
dest_exists = self.destination.exists() dest_exists = self.destination.exists()
@ -1485,18 +1488,18 @@ def check_destination_health(destination):
result = '' result = ''
# Bail early # Bail early
if not isinstance(destination, hw_obj.Disk): if not isinstance(destination, hw_disk.Disk):
# Return empty string # Return empty string
return result return result
# Run safety checks # Run safety checks
try: try:
destination.safety_checks() destination.safety_checks()
except hw_obj.CriticalHardwareError: except hw_disk.CriticalHardwareError:
result = 'Critical hardware error detected on destination' result = 'Critical hardware error detected on destination'
except hw_obj.SMARTSelfTestInProgressError: except hw_disk.SMARTSelfTestInProgressError:
result = 'SMART self-test in progress on destination' result = 'SMART self-test in progress on destination'
except hw_obj.SMARTNotSupportedError: except hw_disk.SMARTNotSupportedError:
pass pass
# Done # Done
@ -1668,20 +1671,20 @@ def get_object(path):
# Check path # Check path
path = pathlib.Path(path).resolve() path = pathlib.Path(path).resolve()
if path.is_block_device() or path.is_char_device(): if path.is_block_device() or path.is_char_device():
obj = hw_obj.Disk(path) obj = hw_disk.Disk(path)
# Child/Parent check # Child/Parent check
parent = obj.details['parent'] parent = obj.details['parent']
if parent: if parent:
std.print_warning(f'"{obj.path}" is a child device') std.print_warning(f'"{obj.path}" is a child device')
if std.ask(f'Use parent device "{parent}" instead?'): if std.ask(f'Use parent device "{parent}" instead?'):
obj = hw_obj.Disk(parent) obj = hw_disk.Disk(parent)
elif path.is_dir(): elif path.is_dir():
obj = path obj = path
elif path.is_file(): elif path.is_file():
# Assuming file is a raw image, mounting # Assuming file is a raw image, mounting
loop_path = mount_raw_image(path) loop_path = mount_raw_image(path)
obj = hw_obj.Disk(loop_path) obj = hw_disk.Disk(loop_path)
# Abort if obj not set # Abort if obj not set
if not obj: if not obj:
@ -1830,8 +1833,8 @@ def source_or_destination_changed(state):
elif hasattr(obj, 'exists'): elif hasattr(obj, 'exists'):
# Assuming dest path # Assuming dest path
changed = changed or not obj.exists() changed = changed or not obj.exists()
elif isinstance(obj, hw_obj.Disk): elif isinstance(obj, hw_disk.Disk):
compare_dev = hw_obj.Disk(obj.path) compare_dev = hw_disk.Disk(obj.path)
for key in ('model', 'serial'): for key in ('model', 'serial'):
changed = changed or obj.details[key] != compare_dev.details[key] changed = changed or obj.details[key] != compare_dev.details[key]
@ -2217,7 +2220,7 @@ def run_recovery(state, main_menu, settings_menu, dry_run=True):
def select_disk(prompt, skip_disk=None): def select_disk(prompt, skip_disk=None):
"""Select disk from list, returns Disk().""" """Select disk from list, returns Disk()."""
std.print_info('Scanning disks...') std.print_info('Scanning disks...')
disks = hw_obj.get_disks() disks = hw_disk.get_disks()
menu = std.Menu( menu = std.Menu(
title=std.color_string(f'ddrescue TUI: {prompt} Selection', 'GREEN'), title=std.color_string(f'ddrescue TUI: {prompt} Selection', 'GREEN'),
) )
@ -2327,10 +2330,10 @@ def select_disk_parts(prompt, disk):
# Replace part list with whole disk obj # Replace part list with whole disk obj
object_list = [disk.path] object_list = [disk.path]
# Convert object_list to hw_obj.Disk() objects # Convert object_list to hw_disk.Disk() objects
print(' ') print(' ')
std.print_info('Getting disk/partition details...') std.print_info('Getting disk/partition details...')
object_list = [hw_obj.Disk(path) for path in object_list] object_list = [hw_disk.Disk(path) for path in object_list]
# Done # Done
return object_list return object_list

View file

@ -15,8 +15,10 @@ from docopt import docopt
from wk import cfg, debug, exe, graph, log, net, std, tmux from wk import cfg, debug, exe, graph, log, net, std, tmux
from wk import os as wk_os from wk import os as wk_os
from wk.hw import obj as hw_obj from wk.hw import disk as hw_disk
from wk.hw import sensors as hw_sensors from wk.hw import sensors as hw_sensors
from wk.hw import system as hw_system
from wk.hw.test import Test
# STATIC VARIABLES # STATIC VARIABLES
@ -100,11 +102,11 @@ class DeviceTooSmallError(RuntimeError):
class State(): class State():
"""Object for tracking hardware diagnostic data.""" """Object for tracking hardware diagnostic data."""
def __init__(self): def __init__(self):
self.cpu = None
self.disks = [] self.disks = []
self.layout = cfg.hw.TMUX_LAYOUT.copy() self.layout = cfg.hw.TMUX_LAYOUT.copy()
self.log_dir = None self.log_dir = None
self.panes = {} self.panes = {}
self.system = None
self.tests = OrderedDict({ self.tests = OrderedDict({
'CPU & Cooling': { 'CPU & Cooling': {
'Enabled': False, 'Enabled': False,
@ -165,12 +167,12 @@ class State():
disable_tests = False disable_tests = False
# Skip already disabled devices # Skip already disabled devices
if all(test.disabled for test in disk.tests.values()): if all(test.disabled for test in disk.tests):
continue continue
try: try:
disk.safety_checks() disk.safety_checks()
except hw_obj.CriticalHardwareError: except hw_disk.CriticalHardwareError:
disable_tests = True disable_tests = True
disk.add_note('Critical hardware error detected.', 'RED') disk.add_note('Critical hardware error detected.', 'RED')
if 'Disk Attributes' in disk.tests: if 'Disk Attributes' in disk.tests:
@ -183,7 +185,7 @@ class State():
'Critical hardware error detected during diagnostics', 'Critical hardware error detected during diagnostics',
'YELLOW', 'YELLOW',
) )
except hw_obj.SMARTSelfTestInProgressError as err: except hw_disk.SMARTSelfTestInProgressError as err:
if prep: if prep:
std.print_warning(f'SMART self-test(s) in progress for {disk.path}') std.print_warning(f'SMART self-test(s) in progress for {disk.path}')
if std.ask('Continue with all tests disabled for this device?'): if std.ask('Continue with all tests disabled for this device?'):
@ -293,8 +295,8 @@ class State():
) )
# Add HW Objects # Add HW Objects
self.cpu = hw_obj.CpuRam() self.system = hw_system.System()
self.disks = hw_obj.get_disks(skip_kits=True) self.disks = hw_disk.get_disks(skip_kits=True)
# Add test objects # Add test objects
for name, details in menu.options.items(): for name, details in menu.options.items():
@ -304,16 +306,17 @@ class State():
if 'CPU' in name: if 'CPU' in name:
# Create two Test objects which will both be used by cpu_stress_tests # Create two Test objects which will both be used by cpu_stress_tests
# NOTE: Prime95 should be added first # NOTE: Prime95 should be added first
test_mprime_obj = hw_obj.Test(dev=self.cpu, label='Prime95') self.system.tests.append(
test_cooling_obj = hw_obj.Test(dev=self.cpu, label='Cooling') Test(dev=self.system, label='Prime95', name=name),
self.cpu.tests[test_mprime_obj.label] = test_mprime_obj )
self.cpu.tests[test_cooling_obj.label] = test_cooling_obj self.system.tests.append(
self.tests[name]['Objects'].append(test_mprime_obj) Test(dev=self.system, label='Cooling', name=name),
self.tests[name]['Objects'].append(test_cooling_obj) )
self.tests[name]['Objects'].extend(self.system.tests)
elif 'Disk' in name: elif 'Disk' in name:
for disk in self.disks: for disk in self.disks:
test_obj = hw_obj.Test(dev=disk, label=disk.path.name) test_obj = Test(dev=disk, label=disk.path.name, name=name)
disk.tests[name] = test_obj disk.test.append(test_obj)
self.tests[name]['Objects'].append(test_obj) self.tests[name]['Objects'].append(test_obj)
# Run safety checks # Run safety checks
@ -360,14 +363,6 @@ class State():
with open(f'{debug_dir}/state.report', 'a', encoding='utf-8') as _f: with open(f'{debug_dir}/state.report', 'a', encoding='utf-8') as _f:
_f.write('\n'.join(debug.generate_object_report(self))) _f.write('\n'.join(debug.generate_object_report(self)))
# CPU/RAM
with open(f'{debug_dir}/cpu.report', 'a', encoding='utf-8') as _f:
_f.write('\n'.join(debug.generate_object_report(self.cpu)))
_f.write('\n\n[Tests]')
for name, test in self.cpu.tests.items():
_f.write(f'\n{name}:\n')
_f.write('\n'.join(debug.generate_object_report(test, indent=1)))
# Disks # Disks
for disk in self.disks: for disk in self.disks:
with open( with open(
@ -375,8 +370,8 @@ class State():
encoding='utf-8') as _f: encoding='utf-8') as _f:
_f.write('\n'.join(debug.generate_object_report(disk))) _f.write('\n'.join(debug.generate_object_report(disk)))
_f.write('\n\n[Tests]') _f.write('\n\n[Tests]')
for name, test in disk.tests.items(): for test in disk.tests:
_f.write(f'\n{name}:\n') _f.write(f'\n{test.name}:\n')
_f.write('\n'.join(debug.generate_object_report(test, indent=1))) _f.write('\n'.join(debug.generate_object_report(test, indent=1)))
# SMC # SMC
@ -394,6 +389,14 @@ class State():
with open(f'{debug_dir}/smc.data', 'a', encoding='utf-8') as _f: with open(f'{debug_dir}/smc.data', 'a', encoding='utf-8') as _f:
_f.write('\n'.join(data)) _f.write('\n'.join(data))
# System
with open(f'{debug_dir}/system.report', 'a', encoding='utf-8') as _f:
_f.write('\n'.join(debug.generate_object_report(self.system)))
_f.write('\n\n[Tests]')
for test in self.system.tests:
_f.write(f'\n{test.name}:\n')
_f.write('\n'.join(debug.generate_object_report(test, indent=1)))
def update_clock(self): def update_clock(self):
"""Update 'Started' pane following clock sync.""" """Update 'Started' pane following clock sync."""
tmux.respawn_pane( tmux.respawn_pane(
@ -725,7 +728,7 @@ def cpu_stress_tests(state, test_objects):
return return
# Prep # Prep
state.update_top_pane(test_mprime_obj.dev.description) state.update_top_pane(test_mprime_obj.dev.cpu_description)
test_cooling_obj.set_status('Working') test_cooling_obj.set_status('Working')
test_mprime_obj.set_status('Working') test_mprime_obj.set_status('Working')
@ -1008,7 +1011,7 @@ def disk_self_test(state, test_objects):
except TimeoutError: except TimeoutError:
test_obj.failed = True test_obj.failed = True
result = 'TimedOut' result = 'TimedOut'
except hw_obj.SMARTNotSupportedError: except hw_disk.SMARTNotSupportedError:
# Pass test since it doesn't apply # Pass test since it doesn't apply
test_obj.passed = True test_obj.passed = True
result = 'N/A' result = 'N/A'
@ -1463,7 +1466,7 @@ def show_results(state):
if name.startswith('CPU')] if name.startswith('CPU')]
if any(cpu_tests_enabled): if any(cpu_tests_enabled):
std.print_success('CPU:') std.print_success('CPU:')
std.print_report(state.cpu.generate_report()) std.print_report(state.system.generate_report())
std.print_standard(' ') std.print_standard(' ')
# Disk Tests # Disk Tests