Add children pass, status, and update sections
* Updating device / child device status/progress done in mark_*() functions * Add current pass description to main menu * Current pass (overall) only updated if all children have passed * Fix Pass 4 crash
This commit is contained in:
parent
c705ba6afc
commit
9d91a28d7a
1 changed files with 98 additions and 33 deletions
|
|
@ -106,19 +106,53 @@ def get_status_color(s, t_success=99, t_warn=90):
|
||||||
return color
|
return color
|
||||||
|
|
||||||
def mark_pass_complete(source):
|
def mark_pass_complete(source):
|
||||||
"""Mark current pass complete and set next pass as current."""
|
"""Mark current pass complete for device, and overall if applicable."""
|
||||||
current_pass = source['Current Pass']
|
current_pass = source['Current Pass']
|
||||||
current_pass_num = int(current_pass[-1:])
|
current_pass_num = int(current_pass[-1:])
|
||||||
next_pass_num = current_pass_num + 1
|
next_pass_num = current_pass_num + 1
|
||||||
next_pass = 'Pass {}'.format(next_pass_num)
|
if 1 <= next_pass_num <= 3:
|
||||||
|
next_pass = 'Pass {}'.format(next_pass_num)
|
||||||
|
else:
|
||||||
|
next_pass = 'Done'
|
||||||
|
|
||||||
|
# Check children progress
|
||||||
|
pass_complete_for_all_devs = True
|
||||||
|
for child in source['Children']:
|
||||||
|
if child['Dev Path'] == source['Current Device']:
|
||||||
|
# This function was called for this device, mark complete
|
||||||
|
child[current_pass]['Done'] = True
|
||||||
|
# TODO remove test code
|
||||||
|
child[current_pass]['Status'] = str(12.5 * current_pass_num * 2.75)
|
||||||
|
if not child[current_pass]['Done']:
|
||||||
|
pass_complete_for_all_devs = False
|
||||||
|
|
||||||
# Update source vars
|
# Update source vars
|
||||||
source['Current Pass'] = next_pass
|
if pass_complete_for_all_devs:
|
||||||
source[current_pass]['Done'] = True
|
source['Current Pass'] = next_pass
|
||||||
|
source[current_pass]['Done'] = True
|
||||||
|
|
||||||
# TODO Remove test code
|
# TODO Remove test code
|
||||||
source[current_pass]['Status'] = str(11.078 * current_pass_num * 3)
|
source[current_pass]['Status'] = str(11.078 * current_pass_num * 3)
|
||||||
|
|
||||||
|
def mark_pass_incomplete(source):
|
||||||
|
"""Mark current pass incomplete."""
|
||||||
|
current_pass = source['Current Pass']
|
||||||
|
source[current_pass]['Status'] = 'Incomplete'
|
||||||
|
for child in source['Children']:
|
||||||
|
if child['Dev Path'] == source['Current Device']:
|
||||||
|
# This function was called for this device, mark incomplete
|
||||||
|
child[current_pass]['Status'] = 'Incomplete'
|
||||||
|
|
||||||
|
def mark_all_passes_pending(source):
|
||||||
|
"""Mark all devs and passes as pending in preparation for retry."""
|
||||||
|
source['Current Pass'] = 'Pass 1'
|
||||||
|
for p_num in ['Pass 1', 'Pass 2', 'Pass 3']:
|
||||||
|
source[p_num]['Status'] = 'Pending'
|
||||||
|
source[p_num]['Done'] = False
|
||||||
|
for child in source['Children']:
|
||||||
|
child[p_num]['Status'] = 'Pending'
|
||||||
|
child[p_num]['Done'] = False
|
||||||
|
|
||||||
def menu_clone(source_path, dest_path):
|
def menu_clone(source_path, dest_path):
|
||||||
"""ddrescue cloning menu."""
|
"""ddrescue cloning menu."""
|
||||||
|
|
||||||
|
|
@ -210,6 +244,8 @@ def menu_image(source_path, dest_path):
|
||||||
|
|
||||||
def menu_main(source):
|
def menu_main(source):
|
||||||
"""Main menu is used to set ddrescue settings."""
|
"""Main menu is used to set ddrescue settings."""
|
||||||
|
title = '{GREEN}ddrescue TUI: Main Menu{CLEAR}\n\n'.format(**COLORS)
|
||||||
|
title += '{BLUE}Current pass: {CLEAR}'.format(**COLORS)
|
||||||
if 'Settings' not in source:
|
if 'Settings' not in source:
|
||||||
source['Settings'] = DDRESCUE_SETTINGS.copy()
|
source['Settings'] = DDRESCUE_SETTINGS.copy()
|
||||||
|
|
||||||
|
|
@ -228,6 +264,13 @@ def menu_main(source):
|
||||||
|
|
||||||
# Show menu
|
# Show menu
|
||||||
while True:
|
while True:
|
||||||
|
display_pass = '1 "Initial Read"'
|
||||||
|
if source['Current Pass'] == 'Pass 2':
|
||||||
|
display_pass = '2 "Trimming bad areas"'
|
||||||
|
elif source['Current Pass'] == 'Pass 3':
|
||||||
|
display_pass = '3 "Scraping bad areas"'
|
||||||
|
elif source['Current Pass'] == 'Done':
|
||||||
|
display_pass = 'Done'
|
||||||
# Update entries
|
# Update entries
|
||||||
for opt in main_options:
|
for opt in main_options:
|
||||||
opt['Name'] = '{} {}'.format(
|
opt['Name'] = '{} {}'.format(
|
||||||
|
|
@ -235,7 +278,7 @@ def menu_main(source):
|
||||||
opt['Base Name'])
|
opt['Base Name'])
|
||||||
|
|
||||||
selection = menu_select(
|
selection = menu_select(
|
||||||
title = '{GREEN}ddrescue TUI: Main Menu{CLEAR}'.format(**COLORS),
|
title = title + display_pass,
|
||||||
main_entries = main_options,
|
main_entries = main_options,
|
||||||
action_entries = actions)
|
action_entries = actions)
|
||||||
|
|
||||||
|
|
@ -258,10 +301,7 @@ def menu_main(source):
|
||||||
for opt in main_options:
|
for opt in main_options:
|
||||||
if 'Retry' in opt['Base Name'] and opt['Enabled']:
|
if 'Retry' in opt['Base Name'] and opt['Enabled']:
|
||||||
settings.extend(['--retrim', '--try-again'])
|
settings.extend(['--retrim', '--try-again'])
|
||||||
source['Current Pass'] = 'Pass 1'
|
mark_all_passes_pending(source)
|
||||||
source['Pass 1']['Done'] = False
|
|
||||||
source['Pass 2']['Done'] = False
|
|
||||||
source['Pass 3']['Done'] = False
|
|
||||||
if 'Reverse' in opt['Base Name'] and opt['Enabled']:
|
if 'Reverse' in opt['Base Name'] and opt['Enabled']:
|
||||||
settings.append('--reverse')
|
settings.append('--reverse')
|
||||||
# Disable for next pass
|
# Disable for next pass
|
||||||
|
|
@ -511,8 +551,8 @@ def menu_settings(source):
|
||||||
def run_ddrescue(source, settings):
|
def run_ddrescue(source, settings):
|
||||||
"""Run ddrescue pass."""
|
"""Run ddrescue pass."""
|
||||||
current_pass = source['Current Pass']
|
current_pass = source['Current Pass']
|
||||||
source[current_pass]['Status'] = 'Working'
|
|
||||||
update_progress(source)
|
# Set pass options
|
||||||
if current_pass == 'Pass 1':
|
if current_pass == 'Pass 1':
|
||||||
settings.extend(['--no-trim', '--no-scrape'])
|
settings.extend(['--no-trim', '--no-scrape'])
|
||||||
elif current_pass == 'Pass 2':
|
elif current_pass == 'Pass 2':
|
||||||
|
|
@ -521,9 +561,21 @@ def run_ddrescue(source, settings):
|
||||||
elif current_pass == 'Pass 3':
|
elif current_pass == 'Pass 3':
|
||||||
# Allow trimming and scraping
|
# Allow trimming and scraping
|
||||||
pass
|
pass
|
||||||
|
elif current_pass == 'Done':
|
||||||
|
clear_screen()
|
||||||
|
print_warning('Recovery already completed?')
|
||||||
|
pause('Press Enter to return to main menu...')
|
||||||
|
return
|
||||||
else:
|
else:
|
||||||
raise GenericError("This shouldn't happen?")
|
raise GenericError("This shouldn't happen?")
|
||||||
|
|
||||||
|
# Set device(s) to clone/image
|
||||||
|
source[current_pass]['Status'] = 'Working'
|
||||||
|
devs = [source]
|
||||||
|
if source['Children']:
|
||||||
|
# Use only selected child devices
|
||||||
|
devs = source['Children']
|
||||||
|
|
||||||
# Set heights
|
# Set heights
|
||||||
## NOTE: 12/33 is based on min heights for SMART/ddrescue panes (12+22+1sep)
|
## NOTE: 12/33 is based on min heights for SMART/ddrescue panes (12+22+1sep)
|
||||||
result = run_program(['tput', 'lines'])
|
result = run_program(['tput', 'lines'])
|
||||||
|
|
@ -537,33 +589,46 @@ def run_ddrescue(source, settings):
|
||||||
'-PF', '#D',
|
'-PF', '#D',
|
||||||
'watch', '--color', '--no-title', '--interval', '300',
|
'watch', '--color', '--no-title', '--interval', '300',
|
||||||
'ddrescue-tui-smart-display', source['Dev Path'])
|
'ddrescue-tui-smart-display', source['Dev Path'])
|
||||||
|
|
||||||
|
# Start pass for each selected device
|
||||||
|
for dev in devs:
|
||||||
|
if dev[current_pass]['Done']:
|
||||||
|
# Move to next device
|
||||||
|
continue
|
||||||
|
source['Current Device'] = dev['Dev Path']
|
||||||
|
dev[current_pass]['Status'] = 'Working'
|
||||||
|
update_progress(source)
|
||||||
|
|
||||||
|
# Start ddrescue
|
||||||
|
try:
|
||||||
|
clear_screen()
|
||||||
|
print_info('Current dev: {}'.format(dev['Dev Path']))
|
||||||
|
ddrescue_proc = popen_program(['./__choose_exit', *settings])
|
||||||
|
ddrescue_proc.wait()
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
# Catch user abort
|
||||||
|
pass
|
||||||
|
|
||||||
# Start ddrescue
|
# Was ddrescue aborted?
|
||||||
try:
|
return_code = ddrescue_proc.poll()
|
||||||
clear_screen()
|
if return_code is None:
|
||||||
ddrescue_proc = popen_program(['./__choose_exit', *settings])
|
print_warning('Aborted')
|
||||||
ddrescue_proc.wait()
|
mark_pass_incomplete(source)
|
||||||
except KeyboardInterrupt:
|
break
|
||||||
# Catch user abort
|
elif return_code:
|
||||||
pass
|
# i.e. not None and not 0
|
||||||
|
print_error('Error(s) encountered, see message above.')
|
||||||
# Was ddrescue aborted?
|
mark_pass_incomplete(source)
|
||||||
return_code = ddrescue_proc.poll()
|
break
|
||||||
if return_code is None:
|
else:
|
||||||
print_warning('Aborted')
|
# Not None and not non-zero int, assuming 0
|
||||||
source[current_pass]['Status'] = 'Incomplete'
|
mark_pass_complete(source)
|
||||||
elif return_code:
|
|
||||||
# i.e. not None and not 0
|
|
||||||
print_error('Error(s) encountered, see message above.')
|
|
||||||
source[current_pass]['Status'] = 'Incomplete'
|
|
||||||
else:
|
|
||||||
# Not None and not non-zero int, assuming 0
|
|
||||||
mark_pass_complete(source)
|
|
||||||
|
|
||||||
# TODO
|
# TODO
|
||||||
update_progress(source)
|
update_progress(source)
|
||||||
print_info('Return: {}'.format(return_code))
|
print_info('Return: {}'.format(return_code))
|
||||||
pause()
|
if str(return_code) != '0':
|
||||||
|
pause()
|
||||||
run_program(['tmux', 'kill-pane', '-t', smart_pane])
|
run_program(['tmux', 'kill-pane', '-t', smart_pane])
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue