def __init__(self, builder, callback, mode, destination_path): # Lowercase mode (eg "backup", "restore", "verify") mode_prefix = mode.name.lower() settings = { 'server': builder.get_object(mode_prefix + "_network_server").get_text(), 'username': builder.get_object(mode_prefix + "_network_username").get_text(), 'password': builder.get_object(mode_prefix + "_network_password").get_text(), 'domain': builder.get_object(mode_prefix + "_network_domain").get_text(), 'version': builder.get_object(mode_prefix + "_network_version").get_text(), 'destination_path': destination_path } # restore_network_version self.callback = callback self.please_wait_popup = PleaseWaitModalPopup( builder, title=_("Please wait..."), message=_("Mounting...")) self.please_wait_popup.show() thread = threading.Thread(target=self._do_mount_command, args=(settings, )) thread.daemon = True thread.start()
def overwrite_partition_table_toggle(self, is_overwriting_partition_table): print("Overwrite partition table toggle changed to " + str(is_overwriting_partition_table)) if is_overwriting_partition_table: # Don't need to scan for existing LVM logical volumes when overwriting partition table. self.post_lvm_preparation(is_overwriting_partition_table, True, "") else: self.win.set_sensitive(False) self.please_wait_popup = PleaseWaitModalPopup( self.builder, title=_("Please wait..."), message= _("Scanning and unmounting any Logical Volume Manager (LVM) Logical Volumes..." )) self.please_wait_popup.show() if 'partitions' in self.dest_drive_dict.keys(): partitions = self.dest_drive_dict['partitions'] else: # TODO: Make this logic better. partitions = self.dest_drive_dict thread = threading.Thread( target=self._scan_and_unmount_existing_lvm, args=[ copy.deepcopy(partitions).keys(), is_overwriting_partition_table ]) thread.daemon = True thread.start()
def __init__(self, builder): self.builder = builder self.destination_partition_combobox_list = self.builder.get_object("destination_partition_combobox_list") self.NOT_RESTORING_PARTITION_KEY = "DISABLED" self.NOT_RESTORING_PARTITION_ENDUSER_FRIENDLY = _("Not restoring this partition") self.win = self.builder.get_object("main_window") self.overwriting_partition_table_message = "<b>" + _("You will be overwriting the partition table.") + "</b>" self.not_overwriting_partition_table_message = _("You will <b>not</b> be overwriting the partition table.") self.no_partition_table_message = _("The source does not contain a partition table.") self.lvm_lv_path_list = [] self.lvm_lv_path_lock = threading.Lock() # FIXME: Refactor the code to remove the need for this ugly initialization. self.dest_drive_dict = {"partitions": {}} self.mode_list = [Mode.RESTORE, Mode.CLONE] self.partition_table_checkbutton_dict = {Mode.RESTORE: self.builder.get_object("restore_overwrite_partition_table_checkbutton"), Mode.CLONE: self.builder.get_object("clone_overwrite_partition_table_checkbutton")} self.overwrite_partition_table_warning_label_dict = {Mode.RESTORE: self.builder.get_object("restore_step4_overwrite_partition_table_warning_label"), Mode.CLONE: self.builder.get_object("clone_step4_overwrite_partition_table_warning_label")} self.selected_image_text_dict = {Mode.RESTORE: self.builder.get_object("restore_step4_selected_image_text"), Mode.CLONE: self.builder.get_object("clone_step4_selected_drives_text")} self.partition_selection_list = self.builder.get_object("partition_selection_list") self.destination_partition_combobox_cell_renderer_dict = {Mode.RESTORE: self.builder.get_object("restore_destination_partition_combobox_cell_renderer"), Mode.CLONE: self.builder.get_object("clone_destination_partition_combobox_cell_renderer")} self.selected_image = None self.please_wait_popup = None self.dest_drive_key = "" self.dest_drive_node = {}
def __init__(self, builder, restore_image_partition_list): self.builder = builder self.restore_partition_selection_list = restore_image_partition_list self.destination_partition_combobox_list = self.builder.get_object( "destination_partition_combobox_list") self.restore_partition_treeview = self.builder.get_object( "restore_step4_image_partition_treeview") self.NOT_RESTORING_PARTITION_KEY = "DISABLED" self.NOT_RESTORING_PARTITION_ENDUSER_FRIENDLY = "Not restoring this partition" self.win = self.builder.get_object("main_window") self.overwriting_partition_table_message = "<b>" + _( "You will be overwriting the partition table.") + "</b>" self.not_overwriting_partition_table_message = _( "You will <b>not</b> be overwriting the partition table.") self.lvm_lv_path_list = [] self.lvm_lv_path_lock = threading.Lock() # FIXME: Refactor the code to remove the need for this ugly initialization. self.dest_drive_dict = {"partitions": {}} self.selected_image = None self.dest_drive_key = "" self.dest_drive_node = {}
def initialize_individual_partition_restore_list(self, selected_image, dest_drive_node, dest_drive_desc, dest_drive_dict): self.selected_image = selected_image self.dest_drive_node = dest_drive_node self.dest_drive_desc = dest_drive_desc self.dest_drive_dict = dest_drive_dict if isinstance(self.selected_image, ClonezillaImage): print("Got selected Clonezilla image: " + str(selected_image.image_format_dict_dict)) elif isinstance(self.selected_image, RedoBackupLegacyImage): print("Got selected RedoBackupLegacy image: " + str(selected_image.sfdisk_dict)) self._use_image_partition_table() info_string = "<b>" + _("Selected image") + "</b> " + GObject.markup_escape_text(self.selected_image.absolute_path) + "\n" + "<b>" + _("Destination device") + "</b> " + GObject.markup_escape_text(self.dest_drive_desc) if isinstance(self.selected_image, ClonezillaImage) and len( self.selected_image.short_device_node_disk_list) > 1: # FIXME: Support Clonezilla multidisk images with subdisk selection combobox info_string += "\n" + "<b>" + _("IMPORTANT: Only selecting FIRST disk in Clonezilla image containing MULTIPLE DISKS.") + "</b>" self.builder.get_object("restore_step4_selected_image_text").set_markup(info_string) print("Have selected image " + str(self.selected_image)) print("Have drive dict " + str(self.dest_drive_dict)) # If the image has a partition table, the overwrite toggle is enabled and defaults to True. Otherwise # it's not possible to overwrite the partition table. overwrite_partition_table_checkbutton = self.builder.get_object("overwrite_partition_table_checkbutton") overwrite_partition_table_checkbutton.set_sensitive(self.selected_image.has_partition_table()) overwrite_partition_table_checkbutton.set_active(self.selected_image.has_partition_table())
def confirm_backup_configuration(self): number = GObject.markup_escape_text( self.selected_drive_enduser_friendly_drive_number) device = GObject.markup_escape_text(self.selected_drive_key) size = GObject.markup_escape_text(self.selected_drive_capacity) if self.selected_drive_model is None: model = "" else: model = GObject.markup_escape_text(self.selected_drive_model) description = GObject.markup_escape_text( self.destination_partition_description) partition_list_string = "" for key in self.partitions_to_backup.keys(): partition_list_string += " " + GObject.markup_escape_text( key) + ": " + GObject.markup_escape_text( self.partitions_to_backup[key]['description']) + "\n" source_drive_heading = GObject.markup_escape_text(_("Source drive")) backup_partitions_heading = GObject.markup_escape_text( _("Backing up the following partition")) backup_image_destination_heading = GObject.markup_escape_text( _("The backup image will be written into folder {dest_dir} on {description}" ).format(dest_dir=self.dest_dir, description=description)) text_to_display = f""" <b>{source_drive_heading}</b> {number} [{size}] ({model}) <b>{backup_partitions_heading}</b>: {partition_list_string} <b>{backup_image_destination_heading}</b> """ self.builder.get_object( "backup_step6_confirm_config_program_defined_text").set_markup( text_to_display)
def selected_folder(self, text, is_allow_selecting_folder_outside_mount): print("Received path " + text) if not is_allow_selecting_folder_outside_mount and not MOUNT_DIR in text: error = ErrorMessageModalPopup( self.builder, _("You must select a folder inside {location}").format( location=MOUNT_DIR) + "\n" + _("Please select a different folder.")) else: self.image_folder_query.query_folder(text)
def populate_summary_page(self): with self.summary_message_lock: self.logger.write("Populating summary page with:\n\n" + self.summary_message) text_to_display = _("""<b>{heading}</b> {message}""").format(heading=_("Verification Summary"), message=GObject.markup_escape_text(self.summary_message)) self.builder.get_object("verify_step4_summary_program_defined_text" ).set_markup(text_to_display)
def adjust_ntfs_filesystem_chs_geometry_information( ntfs_partition_long_device_node, ntfs_partition_start_sector, destination_disk_geometry_dict, is_edd_geometry, logger): cylinders = str(destination_disk_geometry_dict['cylinders']) ntfsfixboot_geometry_options = [] if destination_disk_geometry_dict is not None: ntfsfixboot_geometry_options = [ # "-h, Specify number of heads per track. If omitted, determined via ioctl." "-h", str(destination_disk_geometry_dict['heads']), # "-t, Specify number of sectors per track. If omitted, determined via ioctl." "-t", str(destination_disk_geometry_dict['sectors']) ] if ntfs_partition_start_sector is not None: # "-s, New start sector to write. If omitted, determined via ioctl." ntfsfixboot_geometry_options += [ '-s', str(ntfs_partition_start_sector) ] ntfsfixboot_dry_run_cmd_list = [ "partclone.ntfsfixboot" ] + ntfsfixboot_geometry_options + [ntfs_partition_long_device_node] process, flat_command_string, failed_message = Utility.run( "Dry-run adjust filesystem geometry for the NTFS partition", ntfsfixboot_dry_run_cmd_list, use_c_locale=True, logger=logger) if process.returncode == 1: return True, _( "No changes needed for NTFS filesystem geometry of {ntfs_device}" ).format(ntfs_device=ntfs_partition_long_device_node) # Regardless of if a change is needed, try to write # "-w, Write new start sector to the partition." ntfsfixboot_cmd_list = ["partclone.ntfsfixboot"] + [ '-w' ] + ntfsfixboot_geometry_options + [ntfs_partition_long_device_node] process, flat_command_string, failed_message = Utility.run( "Adjust filesystem geometry for the NTFS partition", ntfsfixboot_cmd_list, use_c_locale=False, logger=logger) if process.returncode != 0: return False, failed_message # It's unclear why there are difference between EDD and sfdisk geometry, so let the user know the source. if is_edd_geometry: geometry_source = "EDD" else: geometry_source = "sfdisk" return True, _( "Successfully adjusted NTFS filesystem geometry of {ntfs_device} using values from {geometry_source}" ).format(ntfs_device=ntfs_partition_long_device_node, geometry_source=geometry_source)
def __init__(self, builder, callback, mode, network_widget_dict, destination_path): # Lowercase mode (eg "backup", "restore", "verify") mode_prefix = mode.name.lower() settings = { 'server': network_widget_dict["network_server"][mode].get_text().strip(), 'username': network_widget_dict["network_username"][mode].get_text().strip(), # For protocols that specify the remote path separately from the server 'remote_path': network_widget_dict["network_remote_path"] [mode].get_text().strip(), # NOT stripping whitespace from the password 'password': network_widget_dict["network_password"][mode].get_text(), 'domain': network_widget_dict["network_domain"][mode].get_text().strip(), 'version': network_widget_dict["network_version"][mode].get_text().strip(), 'ssh_idfile': network_widget_dict["network_ssh_idfile"][mode].get_text().strip(), 'destination_path': destination_path } network_protocol_key = Utility.get_combobox_key( network_widget_dict['network_protocol_combobox'][mode]) # restore_network_version self.callback = callback self.requested_stop_lock = threading.Lock() self.requested_stop = False self.please_wait_popup = PleaseWaitModalPopup( builder, title=_("Please wait..."), message=_("Mounting...") + "\n\n" + _("Close this popup to cancel the mount operation."), on_close_callback=self.cancel_mount) self.please_wait_popup.show() if network_protocol_key == "SMB": thread = threading.Thread(target=self._do_smb_mount_command, args=(settings, )) elif network_protocol_key == "SSH": thread = threading.Thread(target=self._do_ssh_mount_command, args=(settings, )) elif network_protocol_key == "NFS": thread = threading.Thread(target=self._do_nfs_mount_command, args=(settings, )) else: raise ValueError("Unknown network protocol: " + network_protocol_key) thread.daemon = True thread.start()
def start_query(self): print("Starting drive query...") self.win.set_sensitive(False) self.please_wait_popup = PleaseWaitModalPopup( self.builder, title=_("Please wait..."), message=_("Identifying disk drives...")) self.please_wait_popup.show() thread = threading.Thread(target=self._do_drive_query) thread.daemon = True thread.start()
def completed_clone(self, succeeded, message): clone_timeend = datetime.now() duration_minutes = Utility.get_human_readable_minutes_seconds( (clone_timeend - self.clone_timestart).total_seconds()) if isinstance(self.image, QemuImage): is_success, failed_message = self.image.deassociate_nbd( QEMU_NBD_NBD_DEVICE) if not is_success: with self.summary_message_lock: self.summary_message += message + "\n" error = ErrorMessageModalPopup(self.builder, failed_message) print(failed_message) if succeeded: if os.path.exists(self.temp_dir): shutil.rmtree(self.temp_dir) else: # Keep temp_dir for debugging print("Not deleting " + self.temp_dir) self.main_statusbar.remove_all( self.main_statusbar.get_context_id("restore")) self.main_statusbar.remove_all( self.main_statusbar.get_context_id("clone")) with self.clone_in_progress_lock: self.clone_in_progress = False if succeeded: print("Success") else: with self.summary_message_lock: self.summary_message += message + "\n" error = ErrorMessageModalPopup(self.builder, message) print("Failure") with self.summary_message_lock: self.summary_message += "\n" + _( "Operation took {num_minutes} minutes.").format( num_minutes=duration_minutes) + "\n" if self.post_task_action != "DO_NOTHING": if succeeded: has_scheduled, msg = Utility.schedule_shutdown_reboot( self.post_task_action) self.summary_message += "\n" + msg else: self.summary_message += "\n" + _( "Shutdown/Reboot cancelled due to errors.") is_unmounted, umount_message = Utility.umount_warn_on_busy( "/mnt/backup", is_lazy_umount=True) if not is_unmounted: print(umount_message) with self.summary_message_lock: self.summary_message += umount_message + "\n" self.populate_summary_page() self.completed_callback(succeeded)
def _do_mount_command(self, source_path, destination_path): try: if not os.path.exists(destination_path) and not os.path.isdir( destination_path): os.mkdir(destination_path, 0o755) if self.is_stop_requested(): GLib.idle_add(self.callback, False, _("Operation cancelled by user.")) return is_unmounted, message = Utility.umount_warn_on_busy( destination_path) if not is_unmounted: GLib.idle_add(self.please_wait_popup.destroy) GLib.idle_add(self.callback, False, message) return if self.is_stop_requested(): GLib.idle_add(self.callback, False, _("Operation cancelled by user.")) return is_unmounted, message = Utility.umount_warn_on_busy(source_path) if not is_unmounted: GLib.idle_add(self.please_wait_popup.destroy) GLib.idle_add(self.callback, False, message) return if self.is_stop_requested(): GLib.idle_add(self.callback, False, _("Operation cancelled by user.")) return mount_cmd_list = ['mount', source_path, destination_path] process, flat_command_string, failed_message = Utility.interruptable_run( "Mounting selected partition: ", mount_cmd_list, use_c_locale=False, is_shutdown_fn=self.is_stop_requested) if process.returncode != 0: GLib.idle_add(self.please_wait_popup.destroy) GLib.idle_add(self.callback, False, failed_message) return else: GLib.idle_add(self.please_wait_popup.destroy) GLib.idle_add(self.callback, True, "", destination_path) except Exception as e: tb = traceback.format_exc() print(tb) GLib.idle_add(self.please_wait_popup.destroy) GLib.idle_add(self.callback, False, "Error mounting folder: " + tb)
def set_mounted_state(self, is_mounted): if is_mounted: self.is_partition_mounted = True self.builder.get_object("button_mount").set_label(_("Unmount")) self.set_parts_of_image_explorer_page_sensitive(False) self.builder.get_object("button_open_file_manager").set_sensitive( True) else: self.is_partition_mounted = False self.builder.get_object("button_mount").set_label(_("Mount")) self.set_parts_of_image_explorer_page_sensitive(True) self.builder.get_object("button_open_file_manager").set_sensitive( False)
def _use_image_partition_table(self): # Populate image partition list self.destination_partition_combobox_list.clear() self.partition_selection_list.clear() if isinstance(self.selected_image, ClonezillaImage) or isinstance(self.selected_image, RedoBackupLegacyImage) or \ isinstance(self.selected_image, FogProjectImage) or isinstance(self.selected_image, RedoRescueImage) or \ isinstance(self.selected_image, FoxcloneImage) or isinstance(self.selected_image, MetadataOnlyImage): for image_format_dict_key in self.selected_image.image_format_dict_dict.keys(): print("ClonezillaImage contains partition " + image_format_dict_key) if self.selected_image.does_image_key_belong_to_device(image_format_dict_key): if self.selected_image.image_format_dict_dict[image_format_dict_key]['is_lvm_logical_volume']: # The destination of an LVM logical volume within a partition (eg /dev/cl/root) is unchanged dest_partition = self.selected_image.image_format_dict_dict[image_format_dict_key][ 'logical_volume_long_device_node'] flat_description = "Logical Volume " + image_format_dict_key + ": " + self.selected_image.flatten_partition_string(image_format_dict_key) else: # The destination partition of a regular partition in the image (eg, /dev/sda4) is dependent on # the destination drive node (eg /dev/sdb) so we need to split and join the device so the # mapping correctly reads "/dev/sdb4". image_base_device_node, image_partition_number = Utility.split_device_string(image_format_dict_key) # Combine image partition number with destination device node base dest_partition = Utility.join_device_string(self.dest_drive_node, image_partition_number) flat_description = _("Partition {partition_number}").format(partition_number=str( image_partition_number)) + ": " + self.selected_image.flatten_partition_string(image_format_dict_key) self.destination_partition_combobox_list.append([dest_partition, flat_description]) self.partition_selection_list.append( [image_format_dict_key, True, flat_description, dest_partition, flat_description, dest_partition, flat_description]) elif isinstance(self.selected_image, FsArchiverImage): # Doesn't appear that FsArchiver images ever have an partition table backup associated with it. But # keeping this section for reference, especially if a frontend like qt-fsarchiver adds partition table # backups. for fs_key in self.selected_image.fsa_dict['filesystems'].keys(): long_device_node = self.selected_image.fsa_dict['filesystems'][fs_key]['original_long_device_node'] image_base_device_node, image_partition_number = Utility.split_device_string(long_device_node) # Combine image partition number with destination device node base dest_partition = Utility.join_device_string(self.dest_drive_node, image_partition_number) flat_description = _("Partition {partition_number}").format(partition_number=str( image_partition_number)) + " (" + dest_partition + "): " + self.selected_image.flatten_partition_string( fs_key) self.destination_partition_combobox_list.append([dest_partition, flat_description]) self.partition_selection_list.append( [fs_key, True, flat_description, dest_partition, flat_description, dest_partition, flat_description]) elif isinstance(self.selected_image, ApartGtkImage): # Shouldn't be called because ApartGTK images don't have a partition table print("Error: Images created with apart-gtk don't have partition tables") for mode in self.mode_list: self.destination_partition_combobox_cell_renderer_dict[mode].set_sensitive(False)
def get_enduser_friendly_partition_description(self): flat_string = "" drive_index = 0 for short_disk_key in self.short_device_node_disk_list: if self.ecryptfs_info_dict is not None and 'size' in self.ecryptfs_info_dict.keys( ): flat_string += "ECRYPTFS: " if len(self.short_device_node_disk_list) > 1: if drive_index == 0: # Capitalized text that (in conjunction with a error box) will help assist Rescuezilla users in # understanding Clonezilla multidisk images are not yet fully supported. flat_string += "MULTIDISK " flat_string += _("Drive {drive_number}".format( drive_number=str(drive_index + 1))) + ": " for image_format_dict_key in self.image_format_dict_dict.keys(): if self.does_image_key_belong_to_device( image_format_dict_key, short_disk_key): if self.image_format_dict_dict[image_format_dict_key][ 'is_lvm_logical_volume']: flat_string += "(" + image_format_dict_key + ": " + self.flatten_partition_string( short_disk_key, image_format_dict_key) + ") " else: base_device_node, partition_number = Utility.split_device_string( image_format_dict_key) flat_string += "(" + str( partition_number ) + ": " + self.flatten_partition_string( short_disk_key, image_format_dict_key) + ") " drive_index += 1 return flat_string
def initialize_individual_partition_restore_list(self, selected_image, dest_drive_node, dest_drive_desc, dest_drive_dict): self.selected_image = selected_image self.dest_drive_node = dest_drive_node self.dest_drive_desc = dest_drive_desc self.dest_drive_dict = dest_drive_dict if isinstance(self.selected_image, ClonezillaImage): print("Got selected Clonezilla image: " + str(selected_image.image_format_dict_dict)) elif isinstance(self.selected_image, RedoBackupLegacyImage): print("Got selected RedoBackupLegacy image: " + str(selected_image.normalized_sfdisk_dict)) self._use_image_partition_table() info_string = "<b>" + _("Selected image") + "</b> " + GObject.markup_escape_text(self.selected_image.absolute_path) + "\n" + "<b>" + _("Destination device") + "</b> " + GObject.markup_escape_text(self.dest_drive_desc) for mode in self.mode_list: self.selected_image_text_dict[mode].set_markup(info_string) print("Have selected image " + str(self.selected_image)) print("Have drive dict " + str(self.dest_drive_dict)) # If the image has a partition table, the overwrite toggle is enabled and defaults to True. Otherwise # it's not possible to overwrite the partition table. for overwrite_partition_table_checkbutton in self.partition_table_checkbutton_dict.values(): overwrite_partition_table_checkbutton.set_sensitive(self.selected_image.has_partition_table()) overwrite_partition_table_checkbutton.set_active(self.selected_image.has_partition_table()) self.set_overwriting_partition_warning_label(self.selected_image.has_partition_table())
def toggle_restore_of_row(self, iter, new_toggle_state): # Need to be able to disable restore of individual partitions. is_empty_dest_partition = False if self.restore_partition_selection_list.get_value( iter, 5) == self.NOT_RESTORING_PARTITION_KEY: is_empty_dest_partition = True if new_toggle_state and is_empty_dest_partition: print( "Blocking enabling the toggle when the destination partition is not set" ) error = ErrorMessageModalPopup( self.builder, _("No destination partition selected. Use the destination partition drop-down menu to select the destination" )) return # Update the underlying model to ensure the checkbox will reflect the new state self.restore_partition_selection_list.set_value( iter, 1, new_toggle_state) self._swap_destination_partition_node_with_backup(iter) # If the row has been disabled, update the combobox if not new_toggle_state: self.restore_partition_selection_list.set_value( iter, 3, self.NOT_RESTORING_PARTITION_KEY) self.restore_partition_selection_list.set_value( iter, 4, self.NOT_RESTORING_PARTITION_ENDUSER_FRIENDLY)
def __init__(self, builder, callback, source_path, destination_path): self.destination_path = destination_path self.source_path = source_path self.destination_path = destination_path self.callback = callback self.please_wait_popup = PleaseWaitModalPopup( builder, title=_("Please wait..."), message=_("Mounting...")) self.please_wait_popup.show() thread = threading.Thread(target=self._do_mount_command, args=( source_path, destination_path, )) thread.daemon = True thread.start()
def query_folder(self, path): self.query_path = path self.image_list_store.clear() print("Starting scan of provided path " + self.query_path) self.backup_label.set_text(self.query_path) self.restore_label.set_text(self.query_path) self.image_list_store.clear() self.failed_to_read_image_dict.clear() self.win.set_sensitive(False) self.please_wait_popup = PleaseWaitModalPopup( self.builder, title=_("Please wait..."), message=_("Scanning folder for backup images...")) self.please_wait_popup.show() thread = threading.Thread(target=self.scan_image_directory) thread.daemon = True thread.start()
def scan_image_directory(self): self.image_dict.clear() self.failed_to_read_image_dict.clear() try: # list files and directories for filename in os.listdir(self.query_path): if self.is_stop_requested(): break abs_base_scan_path = os.path.abspath( join(self.query_path, filename)) print("Scanning " + abs_base_scan_path) if isfile(abs_base_scan_path): print("Scanning file " + abs_base_scan_path) is_image = self.scan_file(abs_base_scan_path, filename, filename) if is_image: GLib.idle_add( self.please_wait_popup.set_secondary_label_text, _("Scanned: {filename}").format(filename=filename)) elif isdir(abs_base_scan_path): # List the subdirectory (1 level deep) for subdir_filename in os.listdir(abs_base_scan_path): if self.is_stop_requested(): break absolute_path = join(abs_base_scan_path, subdir_filename) enduser_filename = os.path.join( filename, subdir_filename) if isfile(absolute_path): print("Scanning subdir file " + absolute_path) is_image = self.scan_file(absolute_path, subdir_filename, enduser_filename) if is_image: GLib.idle_add( self.please_wait_popup. set_secondary_label_text, _("Scanned: {filename}").format( filename=enduser_filename)) except Exception as e: tb = traceback.format_exc() GLib.idle_add( ErrorMessageModalPopup.display_nonfatal_warning_message, self.builder, "Failed to scan for images: " + tb) # Relying on CPython GIL to access the self.image_list GLib.idle_add(self._populate_image_list_table)
def _do_drive_query_wrapper(self): try: self._do_drive_query() except Exception as exception: tb = traceback.format_exc() traceback.print_exc() GLib.idle_add(self.error_message_callback, False, _("Error querying drives: ") + tb) return
def start_query(self, error_message_callback): print("Starting drive query...") self.win.set_sensitive(False) self.requested_stop_lock = threading.Lock() self.requested_stop = False self.please_wait_popup = PleaseWaitModalPopup( self.builder, title=_("Please wait..."), message=_("Identifying disk drives..."), on_close_callback=self.cancel_query) self.please_wait_popup.show() self.error_message_callback = error_message_callback thread = threading.Thread(target=self._do_drive_query_wrapper) thread.daemon = True thread.start()
def scan_file(self, absolute_path, filename, enduser_filename): print("Scan file " + absolute_path) is_image = False try: image = None if isfile(absolute_path): # Identify Clonezilla images by presence of a file named "parts". Cannot use "clonezilla-img" or # "dev-fs.list" because these files were not created by in earlier Clonezilla versions. Cannot use # "disk" as Clonezilla's 'saveparts' function does not create it. But both 'savedisk' and 'saveparts' # always creates a file named 'parts' across every version of Clonezilla tested. error_suffix = "" if absolute_path.endswith("parts"): print("Found Clonezilla image " + filename) image = ClonezillaImage(absolute_path, enduser_filename) error_suffix = _( "This can happen when loading images which Clonezilla was unable to completely backup. Any other filesystems within the image should be restorable as normal." ) is_image = True elif absolute_path.endswith(".backup"): print( "Found a legacy Redo Backup / Rescuezilla v1.0.5 image " + filename) image = RedoBackupLegacyImage(absolute_path, enduser_filename, filename) error_suffix = _( "Any other filesystems within the image should be restorable as normal." ) is_image = True if is_image: image_warning_message = "" for short_partition_key in image.warning_dict.keys(): image_warning_message += " " + short_partition_key + ": " + image.warning_dict[ short_partition_key] + "\n" if len(image_warning_message) > 0: self.failed_to_read_image_dict[absolute_path] = _( "Unable to fully process the image associated with the following partitions:" ) + "\n" + image_warning_message + error_suffix if image is not None: self.image_dict[image.absolute_path] = image except Exception as e: print("Failed to read: " + absolute_path) tb = traceback.format_exc() self.failed_to_read_image_dict[enduser_filename] = tb traceback.print_exc() return is_image
def do_backup_wrapper(self): try: self.do_backup() except Exception as exception: tb = traceback.format_exc() traceback.print_exc() GLib.idle_add(self.completed_backup, False, _("Error creating backup: ") + tb) return
def do_clone_wrapper(self): try: self.do_clone() except Exception as exception: tb = traceback.format_exc() traceback.print_exc() GLib.idle_add(self.completed_clone, False, _("Error restoring image: ") + tb) return
def do_verify_wrapper(self): try: self.do_verify() except Exception as exception: tb = traceback.format_exc() traceback.print_exc() GLib.idle_add(self.completed_verify, False, _("Error verifying image: ") + tb) return
def _do_nfs_mount_command(self, settings): destination_path = settings['destination_path'] try: if not os.path.exists(destination_path) and not os.path.isdir( destination_path): os.mkdir(destination_path, 0o755) is_unmounted, message = Utility.umount_warn_on_busy( destination_path) if not is_unmounted: GLib.idle_add(self.please_wait_popup.destroy) GLib.idle_add(self.callback, False, message) return if settings['server'] != "": server = settings['server'] else: GLib.idle_add(self.please_wait_popup.destroy) GLib.idle_add(self.callback, False, "Must specify server.") return if settings['remote_path'] != "": exported_dir = settings['remote_path'] else: GLib.idle_add(self.please_wait_popup.destroy) GLib.idle_add(self.callback, False, "Must specify exported directory.") return mount_cmd_list = [ "mount.nfs", server + ":" + exported_dir, settings['destination_path'] ] mount_process, mount_flat_command_string, mount_failed_message = Utility.interruptable_run( "Mounting network shared folder with NFS: ", mount_cmd_list, use_c_locale=False, is_shutdown_fn=self.is_stop_requested) if mount_process.returncode != 0: check_password_msg = _( "Please ensure the server and exported path are correct, and try again." ) GLib.idle_add(self.please_wait_popup.destroy) GLib.idle_add( self.callback, False, mount_failed_message + "\n\n" + check_password_msg) return else: GLib.idle_add(self.please_wait_popup.destroy) GLib.idle_add(self.callback, True, "", destination_path) except Exception as e: tb = traceback.format_exc() print(tb) GLib.idle_add(self.please_wait_popup.destroy) GLib.idle_add(self.callback, False, "Error mounting NFS folder: " + tb)
def __init__(self, builder, callback, prefix, destination_path): settings = { 'server': builder.get_object(prefix + "_server").get_text(), 'username': builder.get_object(prefix + "_username").get_text(), 'password': builder.get_object(prefix + "_password").get_text(), 'domain': builder.get_object(prefix + "_domain").get_text(), 'version': builder.get_object(prefix + "_version").get_text(), 'destination_path': destination_path } # restore_source_version self.callback = callback self.please_wait_popup = PleaseWaitModalPopup( builder, title=_("Please wait..."), message=_("Mounting...")) self.please_wait_popup.show() thread = threading.Thread(target=self._do_mount_command, args=(settings, )) thread.daemon = True thread.start()
def update(drawing=drawing, usage_label=usage_label, node=node): usage_label.set_text(_('Network usage so far: ') + utility.human_size(node.network_usage)) if not drawing.flags() & gtk.VISIBLE: return 0 drawing.queue_draw() return 1
def confirm_restore_configuration(self): # number = GObject.markup_escape_text(self.selected_drive_enduser_friendly_drive_number) print("Partitions to restore is " + str(self.partitions_to_restore)) source_image_absolute_path = self.selected_image_absolute_path destination_drive_description = self.restore_destination_drive_desc restore_partition_list_string = "" for key in self.partitions_to_restore.keys(): image_part_description = GObject.markup_escape_text( self.partitions_to_restore[key]["description"]) dest_key = GObject.markup_escape_text( self.partitions_to_restore[key]["dest_key"]) dest_description = GObject.markup_escape_text( self.partitions_to_restore[key]["dest_description"]) restore_partition_list_string += " " + GObject.markup_escape_text( key ) + " (" + image_part_description + ") ----> " + dest_key + " (" + dest_description + ")\n" restore_partition_list_string += "\n" if self.is_overwriting_partition_table: overwriting_partition_table_string = "<b>" + _( "WILL BE OVERWRITING PARTITION TABLE") + "</b>" else: overwriting_partition_table_string = _( "Will <b>NOT</b> be overwriting partition table") source_image_heading = _("Source image") destination_drive_msg = _("Destination drive") restoring_following_partition_msg = _( "Restoring the following partitions") text_to_display = f""" <b>{source_image_heading}</b> {source_image_absolute_path} <b>{destination_drive_msg}</b> {destination_drive_description} <b>{restoring_following_partition_msg}</b>: {restore_partition_list_string} {overwriting_partition_table_string} """ self.builder.get_object( "restore_step5_confirm_config_program_defined_text").set_markup( text_to_display)