diff --git a/.bin/Scripts/ddrescue-tui-smart-display b/.bin/Scripts/ddrescue-tui-smart-display new file mode 100755 index 00000000..a53a2dca --- /dev/null +++ b/.bin/Scripts/ddrescue-tui-smart-display @@ -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 diff --git a/.bin/Scripts/functions/ddrescue.py b/.bin/Scripts/functions/ddrescue.py index fa454ef1..ebc8b0f2 100644 --- a/.bin/Scripts/functions/ddrescue.py +++ b/.bin/Scripts/functions/ddrescue.py @@ -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.") diff --git a/.bin/Scripts/functions/hw_diags.py b/.bin/Scripts/functions/hw_diags.py index 1b61d8ca..26547d07 100644 --- a/.bin/Scripts/functions/hw_diags.py +++ b/.bin/Scripts/functions/hw_diags.py @@ -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}