Updated menu_ddrescue() and related sections
* RecoveryState is updated before confirmation(s) * New confirmation prompt that supports both cloning and imaging modes * Refactored show_selection_details() to use new objects * Allows resumed state to be detected and prompt switched to "Resume?" * Renamed function show_safety_check() to double_confirm_clone() for clarity
This commit is contained in:
parent
4047b956f5
commit
6cdc4015e7
1 changed files with 39 additions and 35 deletions
|
|
@ -44,6 +44,7 @@ class BlockPair():
|
||||||
self.mode = mode
|
self.mode = mode
|
||||||
self.name = source.name
|
self.name = source.name
|
||||||
self.pass_done = [False, False, False]
|
self.pass_done = [False, False, False]
|
||||||
|
self.resumed = False
|
||||||
self.rescued = 0
|
self.rescued = 0
|
||||||
self.status = ['Pending', 'Pending', 'Pending']
|
self.status = ['Pending', 'Pending', 'Pending']
|
||||||
self.total_size = source.size
|
self.total_size = source.size
|
||||||
|
|
@ -64,6 +65,7 @@ class BlockPair():
|
||||||
prefix=source.prefix)
|
prefix=source.prefix)
|
||||||
if os.path.exists(self.map_path):
|
if os.path.exists(self.map_path):
|
||||||
self.load_map_data()
|
self.load_map_data()
|
||||||
|
self.resumed = True
|
||||||
|
|
||||||
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."""
|
||||||
|
|
@ -186,9 +188,12 @@ class RecoveryState():
|
||||||
self.block_pairs.append(BlockPair(source, dest))
|
self.block_pairs.append(BlockPair(source, dest))
|
||||||
|
|
||||||
def self_checks(self):
|
def self_checks(self):
|
||||||
"""Run self-checks for each BlockPair object."""
|
"""Run self-checks for each BlockPair and update state values."""
|
||||||
|
self.total_size = 0
|
||||||
for bp in self.block_pairs:
|
for bp in self.block_pairs:
|
||||||
bp.self_check()
|
bp.self_check()
|
||||||
|
self.resumed |= bp.resumed
|
||||||
|
self.total_size += bp.size
|
||||||
|
|
||||||
def set_pass_num(self):
|
def set_pass_num(self):
|
||||||
"""Set current pass based on all block-pair's progress."""
|
"""Set current pass based on all block-pair's progress."""
|
||||||
|
|
@ -210,10 +215,8 @@ class RecoveryState():
|
||||||
def update_progress(self):
|
def update_progress(self):
|
||||||
"""Update overall progress using block_pairs."""
|
"""Update overall progress using block_pairs."""
|
||||||
self.rescued = 0
|
self.rescued = 0
|
||||||
self.total_size = 0
|
|
||||||
for bp in self.block_pairs:
|
for bp in self.block_pairs:
|
||||||
self.rescued += bp.rescued
|
self.rescued += bp.rescued
|
||||||
self.total_size += bp.size
|
|
||||||
self.status_percent = get_formatted_status(
|
self.status_percent = get_formatted_status(
|
||||||
label='Recovered:', data=(self.rescued/self.total_size)*100)
|
label='Recovered:', data=(self.rescued/self.total_size)*100)
|
||||||
self.status_amount = get_formatted_status(
|
self.status_amount = get_formatted_status(
|
||||||
|
|
@ -367,6 +370,16 @@ def create_path_obj(path):
|
||||||
return obj
|
return obj
|
||||||
|
|
||||||
|
|
||||||
|
def double_confirm_clone():
|
||||||
|
"""Display warning and get 2nd confirmation from user, returns bool."""
|
||||||
|
print_standard('\nSAFETY CHECK')
|
||||||
|
print_warning('All data will be DELETED from the '
|
||||||
|
'destination device and partition(s) listed above.')
|
||||||
|
print_warning('This is irreversible and will lead '
|
||||||
|
'to {CLEAR}{RED}DATA LOSS.'.format(**COLORS))
|
||||||
|
return ask('Asking again to confirm, is this correct?')
|
||||||
|
|
||||||
|
|
||||||
def get_device_details(dev_path):
|
def get_device_details(dev_path):
|
||||||
"""Get device details via lsblk, returns JSON dict."""
|
"""Get device details via lsblk, returns JSON dict."""
|
||||||
try:
|
try:
|
||||||
|
|
@ -584,11 +597,22 @@ def menu_ddrescue(source_path, dest_path, run_mode):
|
||||||
# TODO select dev or child dev(s)
|
# TODO select dev or child dev(s)
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
# Update state
|
||||||
|
state.self_checks()
|
||||||
|
state.set_pass_num()
|
||||||
|
state.update_progress()
|
||||||
|
|
||||||
# Confirmations
|
# Confirmations
|
||||||
# TODO Show selection details
|
clear_screen()
|
||||||
# TODO resume?
|
show_selection_details(state, source, dest)
|
||||||
# TODO Proceed? (maybe merge with resume? prompt?)
|
prompt = 'Start {}?'.format(state.mode.replace('e', 'ing'))
|
||||||
# TODO double-confirm for clones for safety
|
if state.resumed:
|
||||||
|
print_info('Map data detected and loaded.')
|
||||||
|
prompt = prompt.replace('Start', 'Resume')
|
||||||
|
if not ask(prompt):
|
||||||
|
raise GenericAbort()
|
||||||
|
if state.mode == 'clone' and not double_confirm_clone():
|
||||||
|
raise GenericAbort()
|
||||||
|
|
||||||
# Main menu
|
# Main menu
|
||||||
build_outer_panes(source, dest)
|
build_outer_panes(source, dest)
|
||||||
|
|
@ -1252,40 +1276,20 @@ def show_device_details(dev_path):
|
||||||
print_standard(line)
|
print_standard(line)
|
||||||
|
|
||||||
|
|
||||||
def show_safety_check():
|
def show_selection_details(state, source, dest):
|
||||||
"""Display safety check message and get confirmation from user."""
|
"""Show selection details."""
|
||||||
print_standard('\nSAFETY CHECK')
|
|
||||||
print_warning('All data will be DELETED from the '
|
|
||||||
'destination device and partition(s) listed above.')
|
|
||||||
print_warning('This is irreversible and will lead '
|
|
||||||
'to {CLEAR}{RED}DATA LOSS.'.format(**COLORS))
|
|
||||||
if not ask('Asking again to confirm, is this correct?'):
|
|
||||||
raise GenericAbort()
|
|
||||||
|
|
||||||
|
|
||||||
def show_selection_details(source, dest):
|
|
||||||
clear_screen()
|
|
||||||
|
|
||||||
# Source
|
# Source
|
||||||
print_success('Source device')
|
print_success('Source')
|
||||||
if source['Is Image']:
|
print_standard(source.report)
|
||||||
print_standard('Using image file: {}'.format(source['Path']))
|
|
||||||
print_standard(' (via loopback device: {})'.format(
|
|
||||||
source['Dev Path']))
|
|
||||||
show_device_details(source['Dev Path'])
|
|
||||||
print_standard(' ')
|
print_standard(' ')
|
||||||
|
|
||||||
# Destination
|
# Destination
|
||||||
if source['Type'] == 'clone':
|
if state.mode == 'clone':
|
||||||
print_success('Destination device ', end='')
|
print_success('Destination ', end='')
|
||||||
print_error('(ALL DATA WILL BE DELETED)', timestamp=False)
|
print_error('(ALL DATA WILL BE DELETED)', timestamp=False)
|
||||||
show_device_details(dest['Dev Path'])
|
|
||||||
else:
|
else:
|
||||||
print_success('Destination path')
|
print_success('Destination')
|
||||||
print_standard(dest['Path'])
|
print_standard(dest.report)
|
||||||
print_info('{:<8}{}'.format('FREE', 'FSTYPE'))
|
|
||||||
print_standard('{:<8}{}'.format(
|
|
||||||
dest['Free Space'], dest['Filesystem']))
|
|
||||||
print_standard(' ')
|
print_standard(' ')
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue