def populate_partition_selection_table(self, drive_key): print('Received drive key ' + drive_key) print('drive state is ' + str(self.drive_state)) self.save_partition_list_store.clear() try: if 'partitions' in self.drive_state[drive_key].keys(): for partition_key in self.drive_state[drive_key][ 'partitions'].keys(): flattened_partition_description = CombinedDriveState.flatten_partition_description( self.drive_state, drive_key, partition_key) # Add row that's ticked self.save_partition_list_store.append( [partition_key, True, flattened_partition_description]) else: # Add the drive itself flattened_partition_description = CombinedDriveState.flatten_partition_description( self.drive_state, drive_key, drive_key) # Add row that's ticked self.save_partition_list_store.append( [drive_key, True, flattened_partition_description]) except Exception as exception: tb = traceback.format_exc() traceback.print_exc() ErrorMessageModalPopup.display_nonfatal_warning_message( self.builder, tb) return
def populate_mount_partition_table(self, ignore_drive_key=None): print('drive state is ' + str(self.drive_state)) self.mount_partition_list_store.clear() index = 0 for drive_key in self.drive_state.keys(): try: if drive_key == ignore_drive_key: continue if 'partitions' not in self.drive_state[drive_key].keys(): continue for partition_key in self.drive_state[drive_key]['partitions'].keys(): with self._is_displaying_advanced_information_lock: if self._is_displaying_advanced_information: # Display a advanced-user partition name eg, "nvme0n1p1". human_friendly_partition_name = partition_key else: if self.drive_state[drive_key]['type'] == 'loop': # Don't display certain non-block device if user has chosen to hide them. # TODO: Evaluate other partition types to be hidden. continue # Display a advanced-user partition name eg, "#4". human_friendly_partition_name = "#" + str(index + 1) flattened_partition_description = CombinedDriveState.flatten_partition_description(self.drive_state, drive_key, partition_key) if 'size' in self.drive_state[drive_key]['partitions'][partition_key].keys(): size_in_bytes = self.drive_state[drive_key]['partitions'][partition_key]['size'] enduser_readable_size = Utility.human_readable_filesize(int(size_in_bytes)) else: enduser_readable_size = "unknown_size" self.mount_partition_list_store.append([partition_key, human_friendly_partition_name, enduser_readable_size, flattened_partition_description]) index = index + 1 except Exception as exception: tb = traceback.format_exc() traceback.print_exc() ErrorMessageModalPopup.display_nonfatal_warning_message(self.builder, tb) return
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 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 post_lvm_preparation(self, is_overwriting_partition_table, is_lvm_shutdown_success, lvm_error_message): if self.please_wait_popup is not None: self.please_wait_popup.destroy() self.please_wait_popup = None if not is_lvm_shutdown_success or len(lvm_error_message) != 0: error = ErrorMessageModalPopup(self.builder, lvm_error_message) # Ensure that the overwrite partition table button stays checked. is_overwriting_partition_table = True 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()) if is_overwriting_partition_table: overwrite_partition_table_warning_text = self.overwriting_partition_table_message + " " + _("The \"destination partition\" column has been updated using the information stored within the backup image.\n\n<b>If partitions have been resized, new partitions added, or additional operating systems installed <i>since the backup image was created</i>, then the destination drive's partition table will not match the backup image, and overwriting the destination drive's partition table will render these resized and additional partitions permanently inaccessible.</b> If you have not modified the partition table in such a way since creating this backup then overwriting the partition table is completely safe and will have no negative effects.") self.builder.get_object( "restore_step4_overwrite_partition_table_warning_label").set_markup( overwrite_partition_table_warning_text) self._use_image_partition_table() else: target_node_warning_text = self.not_overwriting_partition_table_message + " " + _("The \"destination partition\" column has been updated with destination drive's existing partition table information.\n\n<b>The destination partition column can be modified as a dropdown menu. Incorrectly mapping the destination partitions may cause operating systems to no longer boot.</b> If you are unsure of the mapping, consider if it's more suitable to instead overwrite the partition table.") self.builder.get_object( "restore_step4_overwrite_partition_table_warning_label").set_markup(target_node_warning_text) self._use_existing_drive_partition_table() self.builder.get_object("destination_partition_combobox_cell_renderer").set_sensitive(not is_overwriting_partition_table)
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 _post_backup_image_mount_callback(self, is_success, message=""): if not is_success: error = ErrorMessageModalPopup(self.builder, message) self.duration_label.set_label("") self.duration_label.set_visible(False) self.set_support_information_linkbutton_visible(True) self.set_patreon_call_to_action_visible(False) else: self.duration_label.set_label(message) self.duration_label.set_visible(True) self.set_mounted_state(True) self.set_support_information_linkbutton_visible(False) self.set_patreon_call_to_action_visible(True)
def _post_mount_callback(self, is_success, error_message, mounted_path=None): if not is_success or mounted_path is None: error = ErrorMessageModalPopup(self.builder, error_message) else: if self.mode == Mode.BACKUP: self.current_page = Page.BACKUP_DESTINATION_FOLDER self.builder.get_object("backup_tabs").set_current_page(3) self.selected_folder(mounted_path, False) else: self.current_page = Page.RESTORE_SOURCE_IMAGE_SELECTION self.builder.get_object("restore_tabs").set_current_page(1) self.selected_folder(mounted_path, True)
def post_lvm_preparation(self, is_overwriting_partition_table, is_lvm_shutdown_success, lvm_error_message): if self.please_wait_popup is not None: self.please_wait_popup.destroy() self.please_wait_popup = None if not is_lvm_shutdown_success or len(lvm_error_message) != 0: error = ErrorMessageModalPopup(self.builder, lvm_error_message) # Ensure that the overwrite partition table button stays checked. is_overwriting_partition_table = True for overwrite_partition_table_checkbutton in self.partition_table_checkbutton_dict: 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(is_overwriting_partition_table) for mode in self.mode_list: self.destination_partition_combobox_cell_renderer_dict[mode].set_sensitive(not is_overwriting_partition_table)
def _populate_image_list_table(self): print("Populating image list table. Image dict is length: " + str(len(self.image_dict))) self.image_list_store.clear() traceback_messages = "" for key in self.image_dict.keys(): try: image = self.image_dict[key] format = image.image_format if len(image.warning_dict.keys()) > 0: warning_icon = self.icon_pixbufs['warning'] else: warning_icon = None if image.is_needs_decryption: lock_icon = self.icon_pixbufs['padlock'] else: lock_icon = None self.image_list_store.append([ key, format, warning_icon, lock_icon, self.icon_pixbufs[format], image.enduser_filename, image.enduser_readable_size, str(image.last_modified_timestamp), image.user_notes, image.get_enduser_friendly_partition_description() ]) except Exception as e: tb = traceback.format_exc() traceback_messages += image.enduser_filename + ":\n" + tb + "\n\n" # Highlight first image if there is only 1 image. if len(self.image_dict.keys()) == 1: self.builder.get_object( "restore_image_selection_treeselection").select_path(0) if len(self.failed_to_read_image_dict.keys()) > 0: for key in self.failed_to_read_image_dict.keys(): traceback_messages += key + ": " + self.failed_to_read_image_dict[ key] + "\n\n" if len(traceback_messages) > 0: ErrorMessageModalPopup( self.builder, str(traceback_messages), error_heading=_("Error processing the following images:")) self.please_wait_popup.destroy()
def completed_verify(self, succeeded, message): verify_timeend = datetime.now() duration_minutes = (verify_timeend - self.verify_timestart).total_seconds() / 60.0 self.main_statusbar.remove_all( self.main_statusbar.get_context_id("verify")) self.verify_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" self.populate_summary_page() self.logger.close() self.completed_callback(succeeded)
def find_network_share(self, button): # FIXME: Overhaul network share handling. error = ErrorMessageModalPopup( self.builder, "Search network function is disabled and will be re-introduced in the next version.\n\nPlease enter the network details manually." )
def prev_tab(self, button): try: print("Currently mode=" + str(self.mode) + " on page " + str(self.current_page)) if self.mode == Mode.BACKUP: if self.current_page == Page.WELCOME: print("Previous backup summary page") self.builder.get_object("mode_tabs").set_current_page(1) self.builder.get_object("backup_tabs").set_current_page(7) self.current_page = Page.BACKUP_SUMMARY_SCREEN self.builder.get_object("button_back").set_sensitive(False) self.builder.get_object("button_next").set_sensitive(True) elif self.current_page == Page.BACKUP_SOURCE_DRIVE_SELECTION: self.current_page = Page.WELCOME self.display_welcome_page() elif self.current_page == Page.BACKUP_SOURCE_PARTITION_SELECTION: self.current_page = Page.BACKUP_SOURCE_DRIVE_SELECTION self.builder.get_object("backup_tabs").set_current_page(0) elif self.current_page == Page.BACKUP_DESTINATION_LOCATION_SELECTION: self.current_page = Page.BACKUP_SOURCE_PARTITION_SELECTION self.builder.get_object("backup_tabs").set_current_page(1) elif self.current_page == Page.BACKUP_DESTINATION_FOLDER: self.current_page = Page.BACKUP_DESTINATION_LOCATION_SELECTION self.builder.get_object("backup_tabs").set_current_page(2) elif self.current_page == Page.BACKUP_IMAGE_NAME_SELECTION: self.current_page = Page.BACKUP_DESTINATION_FOLDER self.builder.get_object("backup_tabs").set_current_page(3) elif self.current_page == Page.BACKUP_CONFIRM_CONFIGURATION: self.current_page = Page.BACKUP_IMAGE_NAME_SELECTION self.builder.get_object("backup_tabs").set_current_page(4) elif self.current_page == Page.BACKUP_PROGRESS: self.current_page = Page.BACKUP_CONFIRM_CONFIGURATION self.builder.get_object("backup_tabs").set_current_page(5) elif self.current_page == Page.BACKUP_SUMMARY_SCREEN: self.current_page = Page.BACKUP_PROGRESS self.builder.get_object("backup_tabs").set_current_page(6) else: print("Unexpected") elif self.mode == Mode.RESTORE: if self.current_page == Page.WELCOME: print("Previous restore summary page") self.builder.get_object("mode_tabs").set_current_page(2) self.builder.get_object("restore_tabs").set_current_page(6) self.current_page = Page.RESTORE_SUMMARY_SCREEN self.builder.get_object("button_back").set_sensitive(False) self.builder.get_object("button_next").set_sensitive(True) elif self.current_page == Page.RESTORE_SOURCE_LOCATION_SELECTION: self.current_page = Page.WELCOME self.display_welcome_page() elif self.current_page == Page.BACKUP_SOURCE_PARTITION_SELECTION: self.builder.get_object("mode_tabs").set_current_page(0) elif self.current_page == Page.RESTORE_SOURCE_IMAGE_SELECTION: self.current_page = Page.RESTORE_SOURCE_LOCATION_SELECTION self.builder.get_object("restore_tabs").set_current_page(0) elif self.current_page == Page.RESTORE_DESTINATION_DRIVE_SELECTION: self.current_page = Page.RESTORE_SOURCE_IMAGE_SELECTION self.builder.get_object("restore_tabs").set_current_page(1) elif self.current_page == Page.RESTORE_DESTINATION_PARTITION_SELECTION: self.current_page = Page.RESTORE_DESTINATION_DRIVE_SELECTION self.builder.get_object("restore_tabs").set_current_page(2) elif self.current_page == Page.RESTORE_CONFIRM_CONFIGURATION: self.current_page = Page.RESTORE_DESTINATION_PARTITION_SELECTION self.builder.get_object("restore_tabs").set_current_page(3) elif self.current_page == Page.RESTORE_PROGRESS: self.current_page = Page.RESTORE_CONFIRM_CONFIGURATION self.builder.get_object("restore_tabs").set_current_page(4) elif self.current_page == Page.RESTORE_SUMMARY_SCREEN: self.current_page = Page.RESTORE_PROGRESS self.builder.get_object("restore_tabs").set_current_page(5) else: print("Unexpected") else: print("Unexpected") print("Moving to mode=" + str(self.mode) + " on page " + str(self.current_page)) except Exception as e: tb = traceback.format_exc() traceback.print_exc() error = ErrorMessageModalPopup(self.builder, tb)
def next_tab(self, button): try: print("Currently mode=" + str(self.mode) + " on page " + str(self.current_page)) if self.mode == Mode.BACKUP: if self.current_page == Page.WELCOME: print("Unexpected") elif self.current_page == Page.BACKUP_SOURCE_DRIVE_SELECTION: list_store, iter = self.get_row( "backup_drive_selection_treeselection") if iter is None: error = ErrorMessageModalPopup( self.builder, "No source drive selected. Please select source " "drive to backup") else: # Get first column (which is hidden/invisible) containing the drive shortdevname (eg, 'sda') self.selected_drive_key = list_store.get(iter, 0)[0] print("User selected drive: " + self.selected_drive_key) self.drive_query.populate_partition_selection_table( self.selected_drive_key) self.selected_drive_enduser_friendly_drive_number = list_store.get( iter, 1)[0] self.selected_drive_capacity = list_store.get(iter, 2)[0] # FIXME: May be None for devices like /dev/md127 self.selected_drive_model = list_store.get(iter, 3)[0] self.current_page = Page.BACKUP_SOURCE_PARTITION_SELECTION self.builder.get_object( "backup_tabs").set_current_page(1) elif self.current_page == Page.BACKUP_SOURCE_PARTITION_SELECTION: partition_list_store = self.builder.get_object( "save_partition_list") if 'partitions' not in self.drive_query.drive_state[ self.selected_drive_key].keys(): error = ErrorMessageModalPopup( self.builder, "Backup of drives without a partition table not yet supported by current version of Rescuezilla.\n\nSupport for this will be added in a future version.\n\nHowever, as a temporary workaround, it is possible to use Clonezilla's 'savedisk' option to backup the drive, and then use Rescuezilla to restore that image." ) return self.partitions_to_backup = collections.OrderedDict() has_atleast_one = False for row in partition_list_store: print("row is " + str(row)) if row[1]: self.partitions_to_backup[ row[0]] = self.drive_query.drive_state[ self.selected_drive_key]['partitions'][ row[0]] self.partitions_to_backup[ row[0]]['description'] = row[2] has_atleast_one = True if not has_atleast_one: error = ErrorMessageModalPopup(self.builder, "Nothing selected!") else: self.drive_query.populate_mount_partition_table( ignore_drive_key=self.selected_drive_key) self.current_page = Page.BACKUP_DESTINATION_LOCATION_SELECTION self.builder.get_object( "backup_tabs").set_current_page(2) elif self.current_page == Page.BACKUP_DESTINATION_LOCATION_SELECTION: backup_dest_use_local_radiobutton = self.builder.get_object( "backup_dest_use_local_radiobutton") if backup_dest_use_local_radiobutton.get_active(): list_store, iter = self.get_row( "backup_destination_partition_selection_treeselection" ) if iter is None: error = ErrorMessageModalPopup( self.builder, "Please select destination drive to mount") else: # Get first column (which is hidden/invisible) containing the drive shortdevname (eg, 'sda') selected_partition_key = list_store.get(iter, 0)[0] self.destination_partition_description = list_store.get( iter, 3)[0] print("User selected partition: " + selected_partition_key) # Callback determines whether wizard proceeds MountLocalPath(self.builder, self._post_mount_callback, selected_partition_key, MOUNT_DIR) else: self.destination_partition_description = "network share" MountNetworkPath(self.builder, self._post_mount_callback, "backup_dest", MOUNT_DIR) elif self.current_page == Page.BACKUP_DESTINATION_FOLDER: enduser_date = datetime.today().strftime( '%Y-%m-%d-%H%M') + "-img-rescuezilla" self.builder.get_object("backup_name").set_text( enduser_date) self.current_page = Page.BACKUP_IMAGE_NAME_SELECTION self.builder.get_object("backup_tabs").set_current_page(4) elif self.current_page == Page.BACKUP_IMAGE_NAME_SELECTION: selected_directory = self.builder.get_object( "backup_folder_label").get_text() folder_name = self.builder.get_object( "backup_name").get_text() self.dest_dir = os.path.join(selected_directory, folder_name) print("going to write to" + str(self.dest_dir)) self.confirm_backup_configuration() self.current_page = Page.BACKUP_CONFIRM_CONFIGURATION self.builder.get_object("backup_tabs").set_current_page(5) elif self.current_page == Page.BACKUP_CONFIRM_CONFIGURATION: self.current_page = Page.BACKUP_PROGRESS self.builder.get_object("backup_tabs").set_current_page(6) self.backup_manager.start_backup( self.selected_drive_key, self.partitions_to_backup, self.drive_query.drive_state, self.dest_dir, self._on_backup_restore_operation_completed_callback) # Disable back/next button until the restore completes self.builder.get_object("button_next").set_sensitive(False) self.builder.get_object("button_back").set_sensitive(False) # On success, display the Patreon call-to-action. self.set_patreon_call_to_action_visible(True) # Disable back/next button until the backup completes self.builder.get_object("button_back").set_sensitive(False) # self.builder.get_object("button_next").set_sensitive(False) elif self.current_page == Page.BACKUP_PROGRESS: self.current_page = Page.BACKUP_SUMMARY_SCREEN self.builder.get_object("backup_tabs").set_current_page(7) self.builder.get_object("button_next").set_sensitive(True) self.builder.get_object("button_back").set_sensitive(False) elif self.current_page == Page.BACKUP_SUMMARY_SCREEN: self.has_prior_summary_page = True self.display_welcome_page() else: print("Unexpected") elif self.mode == Mode.RESTORE: if self.current_page == Page.WELCOME: print("Unexpected") elif self.current_page == Page.RESTORE_SOURCE_LOCATION_SELECTION: restore_source_use_local_radiobutton = self.builder.get_object( "restore_source_use_local_radiobutton") if restore_source_use_local_radiobutton.get_active(): list_store, iter = self.get_row( "source_partition_selection_treeselection") if iter is None: error = ErrorMessageModalPopup( self.builder, "Please select drive to mount") else: # Get first column (which is hidden/invisible) containing the drive shortdevname (eg, 'sda') self.image_source_partition_key = list_store.get( iter, 0)[0] print("User selected partition: " + self.image_source_partition_key) # Callback determines whether wizard proceeds MountLocalPath(self.builder, self._post_mount_callback, self.image_source_partition_key, MOUNT_DIR) else: # In network mode, there is no partition key for the source drive self.image_source_partition_key = None MountNetworkPath(self.builder, self._post_mount_callback, "restore_source", MOUNT_DIR) elif self.current_page == Page.RESTORE_SOURCE_IMAGE_SELECTION: list_store, iter = self.get_row( "restore_partition_selection_treeselection") if iter is None: error = ErrorMessageModalPopup(self.builder, "No image selected") else: self.selected_image_absolute_path = list_store.get( iter, 0)[0] print("User image: " + self.selected_image_absolute_path) image = self.image_folder_query.image_dict[ self.selected_image_absolute_path] if image.is_needs_decryption: error = ErrorMessageModalPopup( self.builder, "Ecryptfs encrypted images are not supported by current version of Rescuezilla.\n\nSupport for ecryptfs will be improved in a future version.\n\nHowever, as a temporary workaround, it is possible to carefully use the ecryptfs command line utility to mount and decrypt the image, and then point Rescuezilla to this ecryptfs mount point and then use Rescuezilla to restore the image as normal." ) else: if len(image.short_device_node_disk_list) > 1: # Unlike Rescuezilla, Clonezilla is able to backup multiple devices at the same time into # a single image. The Rescuezilla user-interface doesn't yet support this, so the first # disk is always selected. error = ErrorMessageModalPopup( self.builder, _("IMPORTANT: Only selecting FIRST disk in Clonezilla image containing MULTIPLE DISKS." ) + "\n\n" + "Multidisk Clonezilla images are not fully supported by the current version of Rescuezilla.\n\nOnly the FIRST disk in the multidisk image has been selected.\n\nBefore proceeding, please double-check if this is suitable." ) self.current_page = Page.RESTORE_DESTINATION_DRIVE_SELECTION self.builder.get_object( "restore_tabs").set_current_page(2) elif self.current_page == Page.RESTORE_DESTINATION_DRIVE_SELECTION: list_store, iter = self.get_row( "restore_step3_drive_selection_treeselection") if iter is None: error = ErrorMessageModalPopup( self.builder, "Please select destination drive to mount") else: self.restore_destination_drive = list_store.get( iter, 0)[0] if self.image_source_partition_key is not None and self.image_source_partition_key.startswith( self.restore_destination_drive): # TODO: Handle this situation more effectively than "startswith" on the device nodes. error = ErrorMessageModalPopup( self.builder, "Destination device cannot be the same as source image device." ) return # Set a nice description like "sdc: 8.00 GB (TOSHIBA USB DRV) self.restore_destination_drive_desc = list_store.get( iter, 1)[0] + ": " + list_store.get(iter, 2)[0] drive_model = list_store.get(iter, 3)[0] if drive_model is not None: # Some devices, such as RAID devices /dev/md127 don't set the drive model field. self.restore_destination_drive_desc += " (" + drive_model + ")" print("User selected destination drive: " + self.restore_destination_drive) self.selected_image = self.image_folder_query.image_dict[ self.selected_image_absolute_path] drive_dict = self.drive_query.drive_state[ self.restore_destination_drive] # TODO: Compare self.selected_image.size_bytes to drive_dict["capacity"] in bytes try: self.backup_image.initialize_individual_partition_restore_list( self.selected_image, self.restore_destination_drive, self.restore_destination_drive_desc, drive_dict) except Exception as e: tb = traceback.format_exc() traceback.print_exc() error = ErrorMessageModalPopup( self.builder, "Unable to process image " + tb) self.current_page = Page.RESTORE_DESTINATION_PARTITION_SELECTION self.builder.get_object( "restore_tabs").set_current_page(3) elif self.current_page == Page.RESTORE_DESTINATION_PARTITION_SELECTION: self.is_overwriting_partition_table = self.builder.get_object( "overwrite_partition_table_checkbutton").get_active() restore_partition_selection_list = self.builder.get_object( "restore_partition_selection_list") self.partitions_to_restore = collections.OrderedDict() has_atleast_one = False for row in restore_partition_selection_list: print("row is " + str(row)) if row[1]: image_key = row[0] self.partitions_to_restore[image_key] = { "description": row[2], "dest_key": row[3], "dest_description": row[4] } print("Added " + image_key + " " + str(self.partitions_to_restore[image_key])) has_atleast_one = True if not has_atleast_one: error = ErrorMessageModalPopup( self.builder, "Please select partitions to restore!") else: self.confirm_restore_configuration() self.current_page = Page.RESTORE_CONFIRM_CONFIGURATION self.builder.get_object( "restore_tabs").set_current_page(4) elif self.current_page == Page.RESTORE_CONFIRM_CONFIGURATION: # Disable back/next button until the restore completes self.builder.get_object("button_next").set_sensitive(False) self.builder.get_object("button_back").set_sensitive(False) AreYouSureModalPopup( self.builder, _("Are you sure you want to restore the backup to {destination_drive}? Doing so will permanently overwrite the data on this drive!" ).format(destination_drive=self. restore_destination_drive), self._restore_confirmation_callback) elif self.current_page == Page.RESTORE_PROGRESS: self.current_page = Page.RESTORE_SUMMARY_SCREEN self.builder.get_object("restore_tabs").set_current_page(6) self.builder.get_object("button_back").set_sensitive(False) self.builder.get_object("button_next").set_sensitive(True) elif self.current_page == Page.RESTORE_SUMMARY_SCREEN: self.has_prior_summary_page = True self.display_welcome_page() else: print("Unexpected") else: print("Unexpected") print(" Moving to mode=" + str(self.mode) + " on page " + str(self.current_page)) except Exception as e: tb = traceback.format_exc() traceback.print_exc() error = ErrorMessageModalPopup(self.builder, tb)
def completed_backup(self, succeeded, message): backup_timeend = datetime.now() duration_minutes = (backup_timeend - self.backup_timestart).total_seconds() / 60.0 duration_message = _("Operation took {num_minutes} minutes.").format( num_minutes=duration_minutes) self.main_statusbar.remove_all( self.main_statusbar.get_context_id("backup")) with self.summary_message_lock: if succeeded: if not self.at_least_one_non_fatal_error: self.summary_message = _( "Backup saved successfully." ) + "\n\n" + self.summary_message + "\n\n" + message + "\n" else: self.summary_message = _( "Backup succeeded with some errors:" ) + "\n\n" + self.summary_message + "\n\n" + message + "\n" else: self.summary_message = _( "Backup operation failed:" ) + "\n\n" + self.summary_message + "\n\n" + message + "\n" error = ErrorMessageModalPopup(self.builder, self.summary_message) with self.summary_message_lock: self.summary_message += duration_message + "\n" self.populate_summary_page() # Clonezilla writes a file named "Info-img-id.txt" which contains a sha512sum of the clonezilla-img log file. # "Generate a checksum for identifying the image later. This is based on the file $img_dir/clonezilla-img." # It doesn't seem that useful, but because Clonezilla does this, Rescuezilla does this too. clonezilla_img_filepath = os.path.join(self.dest_dir, "clonezilla-img") info_img_id_filepath = os.path.join(self.dest_dir, "Info-img-id.txt") self.logger.write("Closing the logfile " + clonezilla_img_filepath + " and generating a tag file for this image: " + info_img_id_filepath) self.logger.close() process, flat_command_string, failed_message = Utility.run( "Checksumming clonezilla-img file", ["sha512sum", clonezilla_img_filepath], use_c_locale=True, output_filepath=None, logger=None) if process.returncode != 0: GLib.idle_add( ErrorMessageModalPopup.display_nonfatal_warning_message, self.builder, failed_message) else: sha512sum_output = process.stdout split = sha512sum_output.split(" ") if len(split) == 2: with open(info_img_id_filepath, 'w') as filehandle: filehandle.write( "# This checksum is only for identifying the image.\n") filehandle.write('IMG_ID=%s' % split[0]) filehandle.flush() else: GLib.idle_add( ErrorMessageModalPopup.display_nonfatal_warning_message, self.builder, "Failed to output checksum file Info-img-id.txt: " + process.stdout + "\n\n" + process.stderr) self.backup_in_progress = False is_unmounted, umount_message = Utility.umount_warn_on_busy( "/mnt/backup", is_lazy_umount=True) if not is_unmounted: self.logger.write(umount_message) with self.summary_message_lock: self.summary_message += umount_message + "\n" self.completed_backup_callback(succeeded)