Moved SMART sections to a separate script
* Refresh rate is now handled by 'watch --interval' * Allows for much simpler ddrescue execution / tracking * Removed all 'SMART Report' sections from functions/ddrescue.py * functions/hw_diags.py has been further extended * Supports full device paths (only for displaying attributes ATM) * Adds a timestamp when only displaying attributes
This commit is contained in:
parent
358191539c
commit
a12a591279
3 changed files with 79 additions and 42 deletions
39
.bin/Scripts/ddrescue-tui-smart-display
Executable file
39
.bin/Scripts/ddrescue-tui-smart-display
Executable file
|
|
@ -0,0 +1,39 @@
|
|||
#!/bin/python3
|
||||
#
|
||||
## Wizard Kit: SMART attributes display for ddrescue TUI
|
||||
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
|
||||
# Init
|
||||
os.chdir(os.path.dirname(os.path.realpath(__file__)))
|
||||
sys.path.append(os.getcwd())
|
||||
from functions.hw_diags import *
|
||||
#init_global_vars()
|
||||
|
||||
if __name__ == '__main__':
|
||||
try:
|
||||
# Prep
|
||||
clear_screen()
|
||||
dev_path = sys.argv[1]
|
||||
devs = scan_disks(True, dev_path)
|
||||
|
||||
# Warn if SMART unavailable
|
||||
if dev_path not in devs:
|
||||
print_error('SMART data not available')
|
||||
input('')
|
||||
|
||||
# Initial screen
|
||||
dev = devs[dev_path]
|
||||
clear_screen()
|
||||
show_disk_details(dev, only_attributes=True)
|
||||
|
||||
# Done
|
||||
exit_script()
|
||||
except SystemExit:
|
||||
pass
|
||||
except:
|
||||
major_exception()
|
||||
|
||||
# vim: sts=4 sw=4 ts=4
|
||||
|
|
@ -510,38 +510,28 @@ def run_ddrescue(source, settings):
|
|||
height_ddrescue = height - height_smart
|
||||
|
||||
# Show SMART status
|
||||
update_smart_report(source)
|
||||
smart_pane = tmux_splitw(
|
||||
'-bdvl', str(height_smart),
|
||||
'-PF', '#D',
|
||||
'watch', '--color', '--no-title', '--interval', '5',
|
||||
'cat', source['SMART Report'])
|
||||
'watch', '--color', '--no-title', '--interval', '300',
|
||||
'ddrescue-tui-smart-display', source['Dev Path'])
|
||||
|
||||
# Start ddrescue
|
||||
return_code = None
|
||||
try:
|
||||
clear_screen()
|
||||
#ddrescue_proc = popen_program('ddrescue who.dd wat.dd why.map'.split())
|
||||
ddrescue_proc = popen_program(['./__choose_exit'])
|
||||
while True:
|
||||
sleep(3)
|
||||
with open(source['SMART Report'], 'a') as f:
|
||||
f.write('heh.\n')
|
||||
return_code = ddrescue_proc.poll()
|
||||
if return_code:
|
||||
# i.e. not None and not 0
|
||||
print_error('Error(s) encountered, see message above.')
|
||||
break
|
||||
elif return_code is not None:
|
||||
# Assuming normal exit
|
||||
break
|
||||
ddrescue_proc = popen_program(['./__choose_exit', *settings])
|
||||
ddrescue_proc.wait()
|
||||
except KeyboardInterrupt:
|
||||
# Catch user abort
|
||||
pass
|
||||
|
||||
# Was ddrescue aborted?
|
||||
return_code = ddrescue_proc.poll()
|
||||
if return_code is None:
|
||||
print_warning('Aborted')
|
||||
elif return_code:
|
||||
# i.e. not None and not 0
|
||||
print_error('Error(s) encountered, see message above.')
|
||||
|
||||
# TODO
|
||||
update_progress(source)
|
||||
|
|
@ -840,23 +830,6 @@ def update_progress(source):
|
|||
with open(source['Progress Out'], 'w') as f:
|
||||
f.writelines(output)
|
||||
|
||||
def update_smart_report(source):
|
||||
"""Update smart report file."""
|
||||
if 'SMART Report' not in source:
|
||||
source['SMART Report'] = '{}/smart_report.out'.format(
|
||||
global_vars['LogDir'])
|
||||
output = []
|
||||
|
||||
# TODO
|
||||
output.append('SMART Report')
|
||||
output.append('TODO')
|
||||
|
||||
# Add line-endings
|
||||
output = ['{}\n'.format(line) for line in output]
|
||||
|
||||
with open(source['SMART Report'], 'w') as f:
|
||||
f.writelines(output)
|
||||
|
||||
if __name__ == '__main__':
|
||||
print("This file is not meant to be called directly.")
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
# Wizard Kit: Functions - HW Diagnostics
|
||||
|
||||
import json
|
||||
import time
|
||||
|
||||
from functions.common import *
|
||||
|
||||
|
|
@ -56,7 +57,9 @@ def get_read_rate(s):
|
|||
|
||||
def get_smart_details(dev):
|
||||
"""Get SMART data for dev if possible, returns dict."""
|
||||
cmd = 'sudo smartctl --all --json /dev/{}'.format(dev).split()
|
||||
cmd = 'sudo smartctl --all --json {}{}'.format(
|
||||
'' if '/dev/' in dev else '/dev/',
|
||||
dev).split()
|
||||
result = run_program(cmd, check=False)
|
||||
try:
|
||||
return json.loads(result.stdout.decode())
|
||||
|
|
@ -509,12 +512,17 @@ def run_tests(tests):
|
|||
global_vars['LogFile']))
|
||||
pause('Press Enter to exit...')
|
||||
|
||||
def scan_disks():
|
||||
def scan_disks(full_paths=False, only_path=None):
|
||||
"""Scan for disks eligible for hardware testing."""
|
||||
clear_screen()
|
||||
|
||||
# Get eligible disk list
|
||||
result = run_program(['lsblk', '-J', '-O'])
|
||||
cmd = ['lsblk', '-J', '-O']
|
||||
if full_paths:
|
||||
cmd.append('-p')
|
||||
if only_path:
|
||||
cmd.append(only_path)
|
||||
result = run_program(cmd)
|
||||
json_data = json.loads(result.stdout.decode())
|
||||
devs = {}
|
||||
for d in json_data.get('blockdevices', []):
|
||||
|
|
@ -536,13 +544,18 @@ def scan_disks():
|
|||
for dev, data in devs.items():
|
||||
# Get SMART attributes
|
||||
run_program(
|
||||
cmd = 'sudo smartctl -s on /dev/{}'.format(dev).split(),
|
||||
cmd = 'sudo smartctl -s on {}{}'.format(
|
||||
'' if full_paths else '/dev/',
|
||||
dev).split(),
|
||||
check = False)
|
||||
data['smartctl'] = get_smart_details(dev)
|
||||
|
||||
# Get NVMe attributes
|
||||
if data['lsblk']['tran'] == 'nvme':
|
||||
cmd = 'sudo nvme smart-log /dev/{} -o json'.format(dev).split()
|
||||
cmd = 'sudo nvme smart-log {}{} -o json'.format(
|
||||
'' if full_paths else '/dev/',
|
||||
dev).split()
|
||||
result = run_program(cmd, check=False)
|
||||
try:
|
||||
data['nvme-cli'] = json.loads(result.stdout.decode())
|
||||
|
|
@ -595,7 +608,9 @@ def show_disk_details(dev, only_attributes=False):
|
|||
dev_name = dev['lsblk']['name']
|
||||
if not only_attributes:
|
||||
# Device description
|
||||
print_info('Device: /dev/{}'.format(dev['lsblk']['name']))
|
||||
print_info('Device: {}{}'.format(
|
||||
'' if '/dev/' in dev['lsblk']['name'] else '/dev/',
|
||||
dev['lsblk']['name']))
|
||||
print_standard(' {:>4} ({}) {} {}'.format(
|
||||
str(dev['lsblk'].get('size', '???b')).strip(),
|
||||
str(dev['lsblk'].get('tran', '???')).strip().upper().replace(
|
||||
|
|
@ -617,7 +632,12 @@ def show_disk_details(dev, only_attributes=False):
|
|||
|
||||
# Attributes
|
||||
if dev.get('NVMe Disk', False):
|
||||
print_info('Attributes:')
|
||||
if only_attributes:
|
||||
print_info('SMART Attributes:', end='')
|
||||
print_warning(' Updated: {}'.format(
|
||||
time.strftime('%Y-%m-%d %H:%M %Z')))
|
||||
else:
|
||||
print_info('Attributes:')
|
||||
for attrib, threshold in sorted(ATTRIBUTES['NVMe'].items()):
|
||||
if attrib in dev['nvme-cli']:
|
||||
print_standard(
|
||||
|
|
@ -638,7 +658,12 @@ def show_disk_details(dev, only_attributes=False):
|
|||
print_success(raw_str, timestamp=False)
|
||||
elif dev['smartctl'].get('ata_smart_attributes', None):
|
||||
# SMART attributes
|
||||
print_info('Attributes:')
|
||||
if only_attributes:
|
||||
print_info('SMART Attributes:', end='')
|
||||
print_warning(' Updated: {}'.format(
|
||||
time.strftime('%Y-%m-%d %H:%M %Z')))
|
||||
else:
|
||||
print_info('Attributes:')
|
||||
s_table = dev['smartctl'].get('ata_smart_attributes', {}).get(
|
||||
'table', {})
|
||||
s_table = {a.get('id', 'Unknown'): a for a in s_table}
|
||||
|
|
|
|||
Loading…
Reference in a new issue