Updated run_ddrescue() to use new objects

This commit is contained in:
2Shirt 2018-08-15 22:38:54 -07:00
parent 53a899f967
commit bb270715c1
Signed by: 2Shirt
GPG key ID: 152FAC923B0E132C

View file

@ -65,6 +65,7 @@ class BlockPair():
def __init__(self, mode, source, dest): def __init__(self, mode, source, dest):
self.mode = mode self.mode = mode
self.source = source self.source = source
self.source_path = source.path
self.dest = dest self.dest = dest
self.pass_done = [False, False, False] self.pass_done = [False, False, False]
self.resumed = False self.resumed = False
@ -97,6 +98,7 @@ class BlockPair():
def finish_pass(self, pass_num): def finish_pass(self, pass_num):
"""Mark pass as done and check if 100% recovered.""" """Mark pass as done and check if 100% recovered."""
map_data = read_map_file(self.map_path)
if map_data['full recovery']: if map_data['full recovery']:
self.pass_done = [True, True, True] self.pass_done = [True, True, True]
self.rescued = self.size self.rescued = self.size
@ -111,10 +113,6 @@ class BlockPair():
else: else:
self.pass_done[pass_num] = True self.pass_done[pass_num] = True
def get_pass_done(self, pass_num):
"""Return pass number's done state."""
return self.pass_done[pass_num]
def load_map_data(self): def load_map_data(self):
"""Load data from map file and set progress.""" """Load data from map file and set progress."""
map_data = read_map_file(self.map_path) map_data = read_map_file(self.map_path)
@ -128,8 +126,8 @@ class BlockPair():
# Initial pass incomplete # Initial pass incomplete
pass pass
elif map_data['non-trimmed'] > 0: elif map_data['non-trimmed'] > 0:
self.pass_done[0] = True self.pass_done = [True, False, False]
self.status[0] = 'Skipped' self.status = ['Skipped', 'Pending', 'Pending']
elif map_data['non-scraped'] > 0: elif map_data['non-scraped'] > 0:
self.pass_done = [True, True, False] self.pass_done = [True, True, False]
self.status = ['Skipped', 'Skipped', 'Pending'] self.status = ['Skipped', 'Skipped', 'Pending']
@ -259,6 +257,7 @@ class RecoveryState():
def __init__(self, mode, source, dest): def __init__(self, mode, source, dest):
self.mode = mode.lower() self.mode = mode.lower()
self.source = source self.source = source
self.source_path = source.path
self.dest = dest self.dest = dest
self.block_pairs = [] self.block_pairs = []
self.current_pass = 0 self.current_pass = 0
@ -320,7 +319,7 @@ class RecoveryState():
"""Checks if pass is done for all block-pairs, returns bool.""" """Checks if pass is done for all block-pairs, returns bool."""
done = True done = True
for bp in self.block_pairs: for bp in self.block_pairs:
done &= bp.get_pass_done(self.current_pass) done &= bp.pass_done[self.current_pass]
return done return done
def current_pass_min(self): def current_pass_min(self):
@ -345,7 +344,7 @@ class RecoveryState():
# Iterate backwards through passes # Iterate backwards through passes
pass_done = True pass_done = True
for bp in self.block_pairs: for bp in self.block_pairs:
pass_done &= bp.get_pass_done(pass_num) pass_done &= bp.pass_done[pass_num]
if pass_done: if pass_done:
# All block-pairs reported being done # All block-pairs reported being done
# Set to next pass, unless we're on the last pass (2) # Set to next pass, unless we're on the last pass (2)
@ -806,7 +805,11 @@ def menu_settings(source):
def read_map_file(map_path): def read_map_file(map_path):
"""Read map file with ddrescuelog and return data as dict.""" """Read map file with ddrescuelog and return data as dict."""
map_data = {} map_data = {}
result = run_program(['ddrescuelog', '-t', map_path]) try:
result = run_program(['ddrescuelog', '-t', map_path])
except CalledProcessError:
# (Grossly) assuming map_data hasn't been saved yet, return empty dict
return map_data
# Parse output # Parse output
for line in result.stdout.decode().splitlines(): for line in result.stdout.decode().splitlines():
@ -824,7 +827,7 @@ def read_map_file(map_path):
# Check if 100% done # Check if 100% done
try: try:
run_program(['ddrescuelog', '-D', map_path]) run_program(['ddrescuelog', '-D', map_path])
except subprocess.CalledProcessError: except CalledProcessError:
map_data['full recovery'] = False map_data['full recovery'] = False
else: else:
map_data['full recovery'] = True map_data['full recovery'] = True
@ -832,25 +835,16 @@ def read_map_file(map_path):
return map_data return map_data
def run_ddrescue(source, dest, settings): def run_ddrescue(state):
"""Run ddrescue pass.""" """Run ddrescue pass."""
current_pass = source['Current Pass']
return_code = None return_code = None
if current_pass == 'Done': if state.finished:
clear_screen() clear_screen()
print_warning('Recovery already completed?') print_warning('Recovery already completed?')
pause('Press Enter to return to main menu...') pause('Press Enter to return to main menu...')
return return
# Set device(s) to clone/image
source[current_pass]['Status'] = 'Working'
source['Started Recovery'] = True
source_devs = [source]
if source['Children']:
# Use only selected child devices
source_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'])
@ -863,39 +857,35 @@ def run_ddrescue(source, dest, settings):
'-bdvl', str(height_smart), '-bdvl', str(height_smart),
'-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', state.source_path)
# Start pass for each selected device # Run pass for each block-pair
for s_dev in source_devs: for bp in state.block_pairs:
if s_dev[current_pass]['Done']: if bp.pass_done[state.current_pass]:
# Move to next device # Skip to next block-pair
continue continue
source['Current Device'] = s_dev['Dev Path'] bp.status[state.current_pass] = 'Working'
s_dev[current_pass]['Status'] = 'Working' update_progress(state)
update_progress(source)
# Set ddrescue cmd # Set ddrescue cmd
if source['Type'] == 'clone': cmd = [
cmd = [ 'ddrescue', *settings,
'ddrescue', *settings, '--force', s_dev['Dev Path'], bp.source_path, bp.dest_path, bp.map_path]
dest['Dev Path'], s_dev['Dest Paths']['Map']] if state.mode == 'clone':
else: cmd.append('--force')
cmd = [ if current_pass == 0:
'ddrescue', *settings, s_dev['Dev Path'],
s_dev['Dest Paths']['image'], s_dev['Dest Paths']['Map']]
if current_pass == 'Pass 1':
cmd.extend(['--no-trim', '--no-scrape']) cmd.extend(['--no-trim', '--no-scrape'])
elif current_pass == 'Pass 2': elif current_pass == 1:
# Allow trimming # Allow trimming
cmd.append('--no-scrape') cmd.append('--no-scrape')
elif current_pass == 'Pass 3': elif current_pass == 2:
# Allow trimming and scraping # Allow trimming and scraping
pass pass
# Start ddrescue # Start ddrescue
try: try:
clear_screen() clear_screen()
print_info('Current dev: {}'.format(s_dev['Dev Path'])) print_info('Current dev: {}'.format(bp.source_path))
ddrescue_proc = popen_program(['./__choose_exit', *cmd]) ddrescue_proc = popen_program(['./__choose_exit', *cmd])
# ddrescue_proc = popen_program(['./__exit_ok', *cmd]) # ddrescue_proc = popen_program(['./__exit_ok', *cmd])
# ddrescue_proc = popen_program(cmd) # ddrescue_proc = popen_program(cmd)
@ -903,10 +893,10 @@ def run_ddrescue(source, dest, settings):
try: try:
ddrescue_proc.wait(timeout=10) ddrescue_proc.wait(timeout=10)
sleep(2) sleep(2)
update_progress(source) update_progress(state)
break break
except subprocess.TimeoutExpired: except subprocess.TimeoutExpired:
update_progress(source) update_progress(state)
except KeyboardInterrupt: except KeyboardInterrupt:
# Catch user abort # Catch user abort
pass pass
@ -921,6 +911,9 @@ def run_ddrescue(source, dest, settings):
# i.e. not None and not 0 # i.e. not None and not 0
print_error('Error(s) encountered, see message above.') print_error('Error(s) encountered, see message above.')
break break
else:
# Mark pass finished
bp.finish_pass(state.current_pass)
# Done # Done
if str(return_code) != '0': if str(return_code) != '0':