Esempio n. 1
0
    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
Esempio n. 2
0
 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
Esempio n. 3
0
    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)
Esempio n. 4
0
    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)
Esempio n. 6
0
 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)
Esempio n. 7
0
 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)
Esempio n. 8
0
 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)
Esempio n. 9
0
    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)
Esempio n. 10
0
    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()
Esempio n. 11
0
    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)
Esempio n. 12
0
 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."
     )
Esempio n. 13
0
 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)
Esempio n. 14
0
    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)
Esempio n. 15
0
    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)