Merge remote-tracking branch 'upstream/dev' into dev
This commit is contained in:
commit
873be60ec6
4 changed files with 55 additions and 41 deletions
|
|
@ -15,6 +15,7 @@ ATTRIBUTE_COLORS = (
|
||||||
('Maximum', 'PURPLE'),
|
('Maximum', 'PURPLE'),
|
||||||
)
|
)
|
||||||
# NOTE: Force 4K read block size for disks >= 3TB
|
# NOTE: Force 4K read block size for disks >= 3TB
|
||||||
|
BADBLOCKS_EXTRA_LARGE_DISK = 15 * 1024**4
|
||||||
BADBLOCKS_LARGE_DISK = 3 * 1024**4
|
BADBLOCKS_LARGE_DISK = 3 * 1024**4
|
||||||
BADBLOCKS_REGEX = re.compile(
|
BADBLOCKS_REGEX = re.compile(
|
||||||
r'^Pass completed, (\d+) bad blocks found. .(\d+)/(\d+)/(\d+) errors',
|
r'^Pass completed, (\d+) bad blocks found. .(\d+)/(\d+)/(\d+) errors',
|
||||||
|
|
|
||||||
|
|
@ -650,6 +650,7 @@ def disk_self_test(state, test_objects, test_mode=False) -> None:
|
||||||
f'Disk self-test{"s" if len(test_objects) > 1 else ""}',
|
f'Disk self-test{"s" if len(test_objects) > 1 else ""}',
|
||||||
)
|
)
|
||||||
std.print_info(f'Starting self-test{"s" if len(test_objects) > 1 else ""}')
|
std.print_info(f'Starting self-test{"s" if len(test_objects) > 1 else ""}')
|
||||||
|
show_failed_attributes(state)
|
||||||
for test in reversed(test_objects):
|
for test in reversed(test_objects):
|
||||||
if test.disabled:
|
if test.disabled:
|
||||||
# Skip
|
# Skip
|
||||||
|
|
@ -748,19 +749,7 @@ def disk_surface_scan(state, test_objects, test_mode=False) -> None:
|
||||||
std.print_info(
|
std.print_info(
|
||||||
f'Starting disk surface scan{"s" if len(test_objects) > 1 else ""}',
|
f'Starting disk surface scan{"s" if len(test_objects) > 1 else ""}',
|
||||||
)
|
)
|
||||||
for disk in state.disks:
|
show_failed_attributes(state)
|
||||||
failed_attributes = [
|
|
||||||
line for line in hw_smart.generate_attribute_report(disk) if 'failed' in line
|
|
||||||
]
|
|
||||||
if failed_attributes:
|
|
||||||
size_str = std.bytes_to_string(disk.size, use_binary=False)
|
|
||||||
std.print_colored(
|
|
||||||
['[', disk.path.name, ' ', size_str, ']'],
|
|
||||||
[None, 'BLUE', None, 'CYAN', None],
|
|
||||||
sep='',
|
|
||||||
)
|
|
||||||
std.print_report(failed_attributes)
|
|
||||||
std.print_standard('')
|
|
||||||
|
|
||||||
# Run surface scans
|
# Run surface scans
|
||||||
for test in reversed([test for test in test_objects if not test.disabled]):
|
for test in reversed([test for test in test_objects if not test.disabled]):
|
||||||
|
|
@ -1006,6 +995,16 @@ def run_diags(state, menu, quick_mode=False, test_mode=False) -> None:
|
||||||
state.top_text = std.color_string('Hardware Diagnostics', 'GREEN')
|
state.top_text = std.color_string('Hardware Diagnostics', 'GREEN')
|
||||||
|
|
||||||
|
|
||||||
|
def show_failed_attributes(state) -> None:
|
||||||
|
"""Show failed attributes for all disks."""
|
||||||
|
for dev in state.disks:
|
||||||
|
std.print_colored([dev.name, dev.description], ['CYAN', None])
|
||||||
|
std.print_report(
|
||||||
|
hw_smart.generate_attribute_report(dev, only_failed=True),
|
||||||
|
)
|
||||||
|
std.print_standard('')
|
||||||
|
|
||||||
|
|
||||||
def show_results(state) -> None:
|
def show_results(state) -> None:
|
||||||
"""Show test results by device."""
|
"""Show test results by device."""
|
||||||
std.sleep(0.5)
|
std.sleep(0.5)
|
||||||
|
|
|
||||||
|
|
@ -112,20 +112,14 @@ def enable_smart(dev) -> None:
|
||||||
run_program(cmd, check=False)
|
run_program(cmd, check=False)
|
||||||
|
|
||||||
|
|
||||||
def generate_attribute_report(dev) -> list[str]:
|
def generate_attribute_report(dev, only_failed=False) -> list[str]:
|
||||||
"""Generate attribute report, returns list."""
|
"""Generate attribute report, returns list."""
|
||||||
report = []
|
report = []
|
||||||
for attr, value in sorted(dev.attributes.items()):
|
for attr, value in sorted(dev.attributes.items()):
|
||||||
note = ''
|
|
||||||
value_color = 'GREEN'
|
|
||||||
|
|
||||||
# Skip attributes not in our list
|
# Skip attributes not in our list
|
||||||
if attr not in dev.known_attributes:
|
if attr not in dev.known_attributes:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Check for attribute note
|
|
||||||
note = dev.known_attributes[attr].get('Note', '')
|
|
||||||
|
|
||||||
# ID / Name
|
# ID / Name
|
||||||
label = f'{attr:>3}'
|
label = f'{attr:>3}'
|
||||||
if isinstance(attr, int):
|
if isinstance(attr, int):
|
||||||
|
|
@ -133,28 +127,13 @@ def generate_attribute_report(dev) -> list[str]:
|
||||||
label += f' / {str(hex(attr))[2:].upper():0>2}: {value["name"]}'
|
label += f' / {str(hex(attr))[2:].upper():0>2}: {value["name"]}'
|
||||||
label = f' {label.replace("_", " "):38}'
|
label = f' {label.replace("_", " "):38}'
|
||||||
|
|
||||||
# Value color
|
# Color & Note
|
||||||
if dev.known_attributes[attr].get('PercentageLife', False):
|
value_color, note = get_attribute_value_color_and_note(dev, attr, value)
|
||||||
# PercentageLife values
|
|
||||||
if 0 <= value['raw'] <= dev.known_attributes[attr]['Error']:
|
|
||||||
value_color = 'RED'
|
|
||||||
note = '(failed, % life remaining)'
|
|
||||||
elif value['raw'] < 0 or value['raw'] > 100:
|
|
||||||
value_color = 'PURPLE'
|
|
||||||
note = '(invalid?)'
|
|
||||||
else:
|
|
||||||
for threshold, color in ATTRIBUTE_COLORS:
|
|
||||||
threshold_val = dev.known_attributes[attr].get(threshold, None)
|
|
||||||
if threshold_val and value['raw'] >= threshold_val:
|
|
||||||
value_color = color
|
|
||||||
if threshold == 'Error':
|
|
||||||
note = '(failed)'
|
|
||||||
elif threshold == 'Maximum':
|
|
||||||
note = '(invalid?)'
|
|
||||||
|
|
||||||
# 199/C7 warning
|
# Skip non-failing attributes if requested
|
||||||
if str(attr) == '199' and value['raw'] > 0:
|
## NOTE: This is a naive test and will include 'invalid' attributes
|
||||||
note = '(bad cable?)'
|
if only_failed and not note:
|
||||||
|
continue
|
||||||
|
|
||||||
# Build colored string and append to report
|
# Build colored string and append to report
|
||||||
line = color_string(
|
line = color_string(
|
||||||
|
|
@ -167,6 +146,38 @@ def generate_attribute_report(dev) -> list[str]:
|
||||||
return report
|
return report
|
||||||
|
|
||||||
|
|
||||||
|
def get_attribute_value_color_and_note(dev, attr, value) -> tuple[str, str]:
|
||||||
|
"""Get attribute color and note based on SMART data."""
|
||||||
|
value_color = 'GREEN'
|
||||||
|
note = dev.known_attributes[attr].get('Note', '')
|
||||||
|
|
||||||
|
# Value value_color
|
||||||
|
if dev.known_attributes[attr].get('PercentageLife', False):
|
||||||
|
# PercentageLife values
|
||||||
|
if 0 <= value['raw'] <= dev.known_attributes[attr]['Error']:
|
||||||
|
value_color = 'RED'
|
||||||
|
note = '(failed, % life remaining)'
|
||||||
|
elif value['raw'] < 0 or value['raw'] > 100:
|
||||||
|
value_color = 'PURPLE'
|
||||||
|
note = '(invalid?)'
|
||||||
|
else:
|
||||||
|
for threshold, color in ATTRIBUTE_COLORS:
|
||||||
|
threshold_val = dev.known_attributes[attr].get(threshold, None)
|
||||||
|
if threshold_val and value['raw'] >= threshold_val:
|
||||||
|
value_color = color
|
||||||
|
if threshold == 'Error':
|
||||||
|
note = '(failed)'
|
||||||
|
elif threshold == 'Maximum':
|
||||||
|
note = '(invalid?)'
|
||||||
|
|
||||||
|
# 199/C7 warning
|
||||||
|
if str(attr) == '199' and value['raw'] > 0:
|
||||||
|
note = '(bad cable?)'
|
||||||
|
|
||||||
|
# Done
|
||||||
|
return (value_color, note)
|
||||||
|
|
||||||
|
|
||||||
def get_attribute_value_string(dev, attr) -> str:
|
def get_attribute_value_string(dev, attr) -> str:
|
||||||
"""Get attribute value string and report if it has changed."""
|
"""Get attribute value string and report if it has changed."""
|
||||||
current_value = dev.attributes.get(attr, {})
|
current_value = dev.attributes.get(attr, {})
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import logging
|
||||||
from subprocess import STDOUT
|
from subprocess import STDOUT
|
||||||
|
|
||||||
from wk.cfg.hw import (
|
from wk.cfg.hw import (
|
||||||
|
BADBLOCKS_EXTRA_LARGE_DISK,
|
||||||
BADBLOCKS_LARGE_DISK,
|
BADBLOCKS_LARGE_DISK,
|
||||||
BADBLOCKS_REGEX,
|
BADBLOCKS_REGEX,
|
||||||
BADBLOCKS_RESULTS_REGEX,
|
BADBLOCKS_RESULTS_REGEX,
|
||||||
|
|
@ -70,6 +71,8 @@ def run_scan(test_obj, log_path, test_mode=False) -> None:
|
||||||
test_obj.set_status('Working')
|
test_obj.set_status('Working')
|
||||||
|
|
||||||
# Increase block size if necessary
|
# Increase block size if necessary
|
||||||
|
if dev.size >= BADBLOCKS_EXTRA_LARGE_DISK:
|
||||||
|
block_size = '8192'
|
||||||
if (dev.phy_sec == 4096
|
if (dev.phy_sec == 4096
|
||||||
or dev.size >= BADBLOCKS_LARGE_DISK):
|
or dev.size >= BADBLOCKS_LARGE_DISK):
|
||||||
block_size = '4096'
|
block_size = '4096'
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue