diff --git a/scripts/wk/clone/ddrescue.py b/scripts/wk/clone/ddrescue.py index b9209ba2..f0e27a24 100644 --- a/scripts/wk/clone/ddrescue.py +++ b/scripts/wk/clone/ddrescue.py @@ -2064,19 +2064,15 @@ def run_ddrescue(state, block_pair, pass_name, settings, dry_run=True) -> None: return # Sleep - i = 0 - while i < idle_minutes*60: + for i in range(1, idle_minutes*60, 1): if not poweroff_source_after_idle: # Countdown canceled, exit without powering-down drives return - if i % 600 == 0 and i > 0: - if i == 600: - cli.print_standard(' ', flush=True) + if i % 60 == 0: cli.print_warning( f'Powering off source in {int((idle_minutes*60-i)/60)} minutes...', ) - std.sleep(5) - i += 5 + std.sleep(1) # Power off drive cmd = ['sudo', 'hdparm', '-Y', source_dev] @@ -2094,6 +2090,12 @@ def run_ddrescue(state, block_pair, pass_name, settings, dry_run=True) -> None: now = datetime.datetime.now(tz=TIMEZONE).strftime('%Y-%m-%d %H:%M %Z') for dev_str in ('source', 'destination'): dev = getattr(state, dev_str) + + # Safety check + if not hasattr(dev, 'attributes'): + continue + + # Update SMART data out_path = f'{state.log_dir}/smart_{dev_str}.out' update_smart_details(dev) with open(out_path, 'w', encoding='utf-8') as _f: @@ -2133,7 +2135,7 @@ def run_ddrescue(state, block_pair, pass_name, settings, dry_run=True) -> None: warning_message = check_destination_health(state.destination) if warning_message: # Error detected on destination, stop recovery - exe.stop_process(proc) + proc.terminate() cli.print_error(warning_message) break _i += 1 @@ -2151,7 +2153,7 @@ def run_ddrescue(state, block_pair, pass_name, settings, dry_run=True) -> None: LOG.warning('ddrescue stopped by user') warning_message = 'Aborted' std.sleep(2) - exe.stop_process(proc, graceful=False) + proc.terminate() break except subprocess.TimeoutExpired: # Continue to next loop to update panes @@ -2191,7 +2193,7 @@ def run_ddrescue(state, block_pair, pass_name, settings, dry_run=True) -> None: # Stop source poweroff countdown cli.print_standard('Stopping device poweroff countdown...', flush=True) poweroff_source_after_idle = False - poweroff_thread.join() + poweroff_thread.join() # type: ignore[reportUnboundVariable] # Done raise std.GenericAbort() @@ -2226,11 +2228,12 @@ def run_recovery(state: State, main_menu, settings_menu, dry_run=True) -> None: update_layout=False, watch_file=f'{state.log_dir}/smart_source.out', ) - state.ui.add_info_pane( - percent=50, - update_layout=False, - watch_file=f'{state.log_dir}/smart_destination.out', - ) + if hasattr(state.destination, 'attributes'): + state.ui.add_info_pane( + percent=50, + update_layout=False, + watch_file=f'{state.log_dir}/smart_destination.out', + ) if PLATFORM == 'Linux': state.ui.add_worker_pane(lines=4, cmd='journal-datarec-monitor') state.ui.set_current_pane_height(DDRESCUE_OUTPUT_HEIGHT) diff --git a/scripts/wk/exe.py b/scripts/wk/exe.py index dd290ffa..a3a1b8e0 100644 --- a/scripts/wk/exe.py +++ b/scripts/wk/exe.py @@ -260,8 +260,9 @@ def run_program( pipe=pipe, shell=shell, **kwargs) + check = cmd_kwargs.pop('check', True) # Avoids linting warning try: - proc = subprocess.run(**cmd_kwargs) + proc = subprocess.run(check=check, **cmd_kwargs) except FileNotFoundError: LOG.error('Command not found: %s', cmd) raise diff --git a/scripts/wk/hw/disk.py b/scripts/wk/hw/disk.py index 161ff9f1..1e81ba24 100644 --- a/scripts/wk/hw/disk.py +++ b/scripts/wk/hw/disk.py @@ -1,7 +1,6 @@ """WizardKit: Disk object and functions""" # vim: sts=2 sw=2 ts=2 -import copy import logging import pathlib import platform @@ -39,7 +38,7 @@ class Disk: children: list[dict] = field(init=False, default_factory=list) description: str = field(init=False) filesystem: str = field(init=False) - initial_attributes: dict[Any, dict] = field(init=False) + initial_attributes: dict[Any, dict] = field(init=False, default_factory=dict) known_attributes: dict[Any, dict] = field(init=False, default_factory=dict) log_sec: int = field(init=False) model: str = field(init=False) @@ -62,7 +61,6 @@ class Disk: self.update_details() self.set_description() self.known_attributes = get_known_disk_attributes(self.model) - self.initial_attributes = copy.deepcopy(self.attributes) if not self.is_4k_aligned(): self.add_note('One or more partitions are not 4K aligned', 'YELLOW') diff --git a/scripts/wk/hw/smart.py b/scripts/wk/hw/smart.py index 915225bd..6c5c66e5 100644 --- a/scripts/wk/hw/smart.py +++ b/scripts/wk/hw/smart.py @@ -506,6 +506,10 @@ def update_smart_details(dev) -> None: if not updated_attributes: dev.add_note('No NVMe or SMART data available', 'YELLOW') + # Update iniital_attributes if needed + if not dev.initial_attributes: + dev.initial_attributes = copy.deepcopy(updated_attributes) + # Done dev.attributes.update(updated_attributes) diff --git a/scripts/wk/ui/tui.py b/scripts/wk/ui/tui.py index 15ee3b27..370ff496 100644 --- a/scripts/wk/ui/tui.py +++ b/scripts/wk/ui/tui.py @@ -235,6 +235,9 @@ class TUI(): ), )) + # Done + sleep(0.2) + def remove_all_info_panes(self) -> None: """Remove all info panes and update layout.""" self.layout['Info'].pop('height', None)