Esempio n. 1
0
    def initialize(self):
        """
        The initialize method that is called after the instance is created.
        The difference between __init__ and this method is that this may take
        a long time and thus could be called in a separated thread.

        :see: pyanaconda.ui.common.UIObject.initialize
        """
        NormalSpoke.initialize(self)
        self.initialize_start()

        config.log_dir = "/tmp"

        box = self.builder.get_object("BlivetGuiViewport")
        self._label_actions = self.builder.get_object("summary_label")
        self._button_reset = self.builder.get_object("resetAllButton")
        self._button_undo = self.builder.get_object("undoLastActionButton")

        self._client = BlivetGUIAnacondaClient()
        self._blivetgui = BlivetGUIAnaconda(self._client, self, box)

        # this needs to be done when the spoke is already "realized"
        self.entered.connect(self._blivetgui.ui_refresh)

        # set up keyboard shurtcuts for blivet-gui (and unset them after
        # user lefts the spoke)
        self.entered.connect(self._blivetgui.set_keyboard_shortcuts)
        self.exited.connect(self._blivetgui.unset_keyboard_shortcuts)

        self.initialize_done()
Esempio n. 2
0
class BlivetGuiSpoke(NormalSpoke, StorageCheckHandler):
    ### class attributes defined by API ###

    # list all top-level objects from the .glade file that should be exposed
    # to the spoke or leave empty to extract everything
    builderObjects = ["blivetGuiSpokeWindow"]

    # the name of the main window widget
    mainWidgetName = "blivetGuiSpokeWindow"

    # name of the .glade file in the same directory as this source
    uiFile = "spokes/blivet_gui.glade"

    # category this spoke belongs to
    category = SystemCategory

    # title of the spoke (will be displayed on the hub)
    title = CN_("GUI|Spoke", "_Blivet-GUI Partitioning")

    helpFile = "blivet-gui/index.page"

    ### methods defined by API ###
    def __init__(self, data, storage, payload):
        """
        :see: pyanaconda.ui.common.Spoke.__init__
        :param data: data object passed to every spoke to load/store data
                     from/to it
        :type data: pykickstart.base.BaseHandler
        :param storage: object storing storage-related information
                        (disks, partitioning, bootloader, etc.)
        :type storage: blivet.Blivet
        :param payload: object storing payload-related information
        :type payload: pyanaconda.payload.Payload
        """
        self._error = None
        self._back_already_clicked = False
        self._label_actions = None
        self._button_reset = None
        self._button_undo = None

        self._client = None
        self._blivetgui = None
        self._partitioning = None
        self._device_tree = None

        self._storage_module = STORAGE.get_proxy()

        StorageCheckHandler.__init__(self)
        NormalSpoke.__init__(self, data, storage, payload)

    @property
    def label_actions(self):
        """The summary label.

        This property is required by Blivet-GUI.
        """
        return self._label_actions

    def initialize(self):
        """
        The initialize method that is called after the instance is created.
        The difference between __init__ and this method is that this may take
        a long time and thus could be called in a separated thread.

        :see: pyanaconda.ui.common.UIObject.initialize
        """
        NormalSpoke.initialize(self)
        self.initialize_start()

        config.log_dir = "/tmp"

        box = self.builder.get_object("BlivetGuiViewport")
        self._label_actions = self.builder.get_object("summary_label")
        self._button_reset = self.builder.get_object("resetAllButton")
        self._button_undo = self.builder.get_object("undoLastActionButton")

        self._client = BlivetGUIAnacondaClient()
        self._blivetgui = BlivetGUIAnaconda(self._client, self, box)

        # this needs to be done when the spoke is already "realized"
        self.entered.connect(self._blivetgui.ui_refresh)

        # set up keyboard shurtcuts for blivet-gui (and unset them after
        # user lefts the spoke)
        self.entered.connect(self._blivetgui.set_keyboard_shortcuts)
        self.exited.connect(self._blivetgui.unset_keyboard_shortcuts)

        self.initialize_done()

    def refresh(self):
        """
        The refresh method that is called every time the spoke is displayed.
        It should update the UI elements according to the contents of
        self.data.

        :see: pyanaconda.ui.common.UIObject.refresh
        """
        for thread_name in [THREAD_EXECUTE_STORAGE, THREAD_STORAGE]:
            threadMgr.wait(thread_name)

        if not self._partitioning:
            # Create the partitioning now. It cannot by done earlier, because
            # the storage spoke would use it as a default partitioning.
            self._partitioning = create_partitioning(
                PARTITIONING_METHOD_BLIVET)
            self._device_tree = STORAGE.get_proxy(
                self._partitioning.GetDeviceTree())

        self._back_already_clicked = False
        self._client.initialize(self._partitioning.SendRequest)
        self._blivetgui.initialize()

        # if we re-enter blivet-gui spoke, actions from previous visit were
        # not removed, we need to update number of blivet-gui actions
        self._blivetgui.set_actions(self._client.get_actions())

    def apply(self):
        """
        The apply method that is called when the spoke is left. It should
        update the contents of self.data with values set in the GUI elements.
        """
        pass

    @property
    def indirect(self):
        return True

    # This spoke has no status since it's not in a hub
    @property
    def status(self):
        return None

    def clear_errors(self):
        self._error = None
        self.clear_info()

    def _do_check(self):
        self.clear_errors()
        StorageCheckHandler.errors = []
        StorageCheckHandler.warnings = []

        try:
            log.debug("Generating updated storage configuration")
            task_path = self._partitioning.ConfigureWithTask()
            task_proxy = STORAGE.get_proxy(task_path)
            sync_run_task(task_proxy)
        except BootloaderConfigurationError as e:
            log.error("Storage configuration failed: %s", e)
            StorageCheckHandler.errors = [str(e)]
            reset_bootloader()
        else:
            log.debug("Checking storage configuration...")
            task_path = self._partitioning.ValidateWithTask()
            task_proxy = STORAGE.get_proxy(task_path)
            sync_run_task(task_proxy)

            result = unwrap_variant(task_proxy.GetResult())
            report = ValidationReport.from_structure(result)

            log.debug("Validation has been completed: %s", report)
            StorageCheckHandler.errors = report.error_messages
            StorageCheckHandler.warnings = report.warning_messages

            if report.is_valid():
                self._storage_module.ApplyPartitioning(
                    get_object_path(self._partitioning))

        if self.errors:
            self.set_warning(
                _("Error checking storage configuration.  <a href=\"\">Click for details</a> or press Done again to continue."
                  ))
        elif self.warnings:
            self.set_warning(
                _("Warning checking storage configuration.  <a href=\"\">Click for details</a> or press Done again to continue."
                  ))

        # on_info_bar_clicked requires self._error to be set, so set it to the
        # list of all errors and warnings that storage checking found.
        self._error = "\n".join(self.errors + self.warnings)

        return self._error == ""

    def activate_action_buttons(self, activate):
        self._button_undo.set_sensitive(activate)
        self._button_reset.set_sensitive(activate)

    ### handlers ###
    def on_info_bar_clicked(self, *args):
        log.debug("info bar clicked: %s (%s)", self._error, args)
        if not self._error:
            return

        dlg = Gtk.MessageDialog(flags=Gtk.DialogFlags.MODAL,
                                message_type=Gtk.MessageType.ERROR,
                                buttons=Gtk.ButtonsType.CLOSE,
                                message_format=str(self._error))
        dlg.set_decorated(False)

        with self.main_window.enlightbox(dlg):
            dlg.run()
            dlg.destroy()

    def on_back_clicked(self, button):
        # Clear any existing errors
        self.clear_errors()

        # If back has been clicked on once already and no other changes made on the screen,
        # run the storage check now.  This handles displaying any errors in the info bar.
        if not self._back_already_clicked:
            self._back_already_clicked = True

            # If we hit any errors while saving things above, stop and let the
            # user think about what they have done
            if self._error is not None:
                return

            if not self._do_check():
                return

        dialog = ActionSummaryDialog(self.data, self._device_tree)
        dialog.refresh()

        if dialog.actions:
            with self.main_window.enlightbox(dialog.window):
                rc = dialog.run()

            if rc != 1:
                # Cancel.  Stay on the blivet-gui screen.
                return

        NormalSpoke.on_back_clicked(self, button)

    def on_summary_button_clicked(self, _button):
        self._blivetgui.show_actions()

    def on_undo_action_button_clicked(self, _button):
        self._blivetgui.actions_undo()

    # This callback is for the button that just resets the UI to anaconda's
    # current understanding of the disk layout.
    def on_reset_button_clicked(self, *args):
        msg = _(
            "Continuing with this action will reset all your partitioning selections "
            "to their current on-disk state.")

        dlg = Gtk.MessageDialog(flags=Gtk.DialogFlags.MODAL,
                                message_type=Gtk.MessageType.WARNING,
                                buttons=Gtk.ButtonsType.NONE,
                                message_format=msg)
        dlg.set_decorated(False)
        dlg.add_buttons(
            C_("GUI|Custom Partitioning|Reset Dialog", "_Reset selections"), 0,
            C_("GUI|Custom Partitioning|Reset Dialog",
               "_Preserve current selections"), 1)
        dlg.set_default_response(1)

        with self.main_window.enlightbox(dlg):
            rc = dlg.run()
            dlg.destroy()

        if rc == 0:
            self.refresh()
            self._blivetgui.reload()

            # XXX: Reset currently preserves actions set in previous runs
            # of the spoke, so we need to 're-add' these to the ui
            self._blivetgui.set_actions(self._client.get_actions())