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:
2Shirt 2018-07-17 21:05:37 -06:00
parent 358191539c
commit a12a591279
Signed by: 2Shirt
GPG key ID: 152FAC923B0E132C
3 changed files with 79 additions and 42 deletions

View 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

View file

@ -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.")

View file

@ -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}