Source

vasm / src / vasm / ui / tui / modules / tui_bootloaders.py

#!/usr/bin/env python

#    This file is part of VASM.
#
#    VASM is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License v3 as published by
#    the Free Software Foundation.
#
#    VASM is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with VASM.  If not, see <http://www.gnu.org/licenses/>.

""" tui_bootloaders.py
    Text mode module to interact with available bootloaders in the system. """

__author__ = "Moises Henriquez"
__author_email__ = "moc.liamg@xnl.E0M"[::-1]

import urwid
import os
import time
from vasm.backend import bootloaders, fstab
from vasm.backend.utils import _
from vasm.ui.tui.support import widgets, dialogs
import logging

logger = logging.getLogger('vasm')

class BootloaderTui(dialogs.ModuleDialog):
    def __init__(self, parent):
        self.parent = parent
        bsector = bootloaders.get_current_bootsector()
        # FIXME:
        # When running in a chroot environment, this will produce an error
        # because the chroot has no / mountpoint            
	if not bsector:
	    logger.error('Unable to find current bootsector.  Possible chroot environment.')
	    logger.error('%s bailing out.'% __file__)
	    self.parent.return_home()

        if bsector:
            rootdev = bsector[:len('/dev/sda'):]
            currentbootloader = bootloaders.MBR(rootdev).read_bootloader() or \
            bootloaders.DEFAULT_BOOTLOADER
        else:
            currentbootloader = bootloaders.DEFAULT_BOOTLOADER
        msg = (
            _("Select which bootloader you would like to use."), "\n",
            _("WARNING: this tool will over-write the target you selecct below."), "  ",
            _("Selecting the wrong options here could render your system unbootable."),)
        
        self.blpicker = widgets.HChoiceBox(
            label = 'Select Bootloader:',
            choices = [("LILO", 'lilo'), ("GRUB2", 'grub2')],
            on_state_change = self.blpicker_event,
            on_focus = self.refresh_module_status_text
            )
        self.targetpicker = widgets.LabeledPicker(
            label = "Installation Target:",
            picker_title = _("Select the installation target for your bootloader."),
            options = self._get_valid_targets(currentbootloader),
            parent = self,
            on_focus = self.refresh_module_status_text
            )
        self.videosizepicker = widgets.LabeledPicker(
            label = _("Video Resolution:"),
            picker_title = _("Select the video resolution for your boot screen."),
            options = self._get_valid_video_sizes(currentbootloader),
            parent = self,
            on_focus = self.refresh_module_status_text
            )
        self.timeoutspinner = widgets.LabeledSpinner(
            label = "Timeout (seconds):",
            default_value=5,
            range_max = 60,
            on_focus = self.refresh_module_status_text
            )
        actionlabel = urwid.Text(_("Select the action to be performed with your bootloader."))
        self.actionpicker = widgets.HChoiceBox(
            label = _("Action:"),
            choices = [
                (_("Install and refresh boot menu"),bootloaders.Install),
                (_("Refresh boot menu only"), bootloaders.Refresh)],
                on_state_change = self.action_toggle_event,
                on_focus = self.refresh_module_status_text
            )
        self.actionpicker.set_value(_("Refresh boot menu only"))
        
        _body = [self.blpicker,
                 self.targetpicker,
                 self.videosizepicker,
                 self.timeoutspinner,
                 widgets.blank,
                 actionlabel,
                 self.actionpicker
                 ]
        dialogs.ModuleDialog.__init__(
            self,
            header = _("System Boot Menu"),
            desc = [''.join(msg), urwid.Divider('_'),widgets.blank],
            parent = self.parent,
            body = _body,
            buttons = [("Save", self.module_ok_event),
                ("Cancel", self.parent.return_home)])
        # Set the default values
        bootsec = bootloaders.get_current_bootsector()
        currentroot = bootsec[:len('/dev/sda'):]
        mbr_read = bootloaders.MBR(currentroot).read_bootloader() or "LILO"
        mbr_read = mbr_read.upper()
        #mbr_read = bootloaders.MBR(currentroot).read_bootloader().upper() or "LILO"
        self.blpicker.set_value(mbr_read)
        self.populate_pickers(mbr_read)
        # Move the focus to the body section of the module
        self.set_focus('body')

    def _check_selections(self):
        # check if target has a value
        assert self.targetpicker.get_value().startswith("/"),\
            ' '.join((
                _("Select a valid target from the options list."),
                _("Access the options list by pressing Enter or Space on the selection area")))
    
    def test_bootsector_available(self, widget=None):
        bsector = bootloaders.get_current_bootsector()
        if bsector is None:
            msg = (
                _("We are unable to determine which partition is being used as /."),
                _("You will not be able to save changes in this environment"))
            logger.error("Unable to find current bootsector... Possible chroot environment")
            dia = dialogs.Error(
                parent = self.parent,
                buttons = (["OK", self.return_to_module]),
                message = '  '.join(msg))
            return self.pop_dialog(dia)
        return True

    def module_ok_event(self, widget=None):
        """ Triggered when "Save" is pressed on this module """
        if not self.test.bootsector_available():
            # Cancel because we cannot read the bootsector 
            return
        try:
            self._check_selections()
        except AssertionError as e:
            dia = dialogs.Error(
                parent = self,
                message = "%s"% e,
                buttons = [("OK", self.return_to_module)]
                )
            return self.pop_dialog(dia)
        bootloader = self.blpicker.get_value()
        target = self.targetpicker.get_value()
        vgasize = self.videosizepicker.get_value()
        vgaquality = "high" # we are default to 'high'
        timeout = self.timeoutspinner.get_value()
        action = self.actionpicker.get_value()
        dia = dialogs.Question(
            parent = self,
            message = ''.join((
                _("WARNING: You are about to overwrite the bootloader on"), ' ',target,'.  ',
                _("Are you sure you want to continue?")
                )),
            buttons = [
                ("YES", self._do_write_changes),
                ("NO", self.return_to_module)
            ])
        return self.pop_dialog(dia)

    def _do_write_changes(self, widget=None):
        # create the bootloader object
        sloader = self.blpicker.get_value().upper()
        tgt = self.targetpicker.get_value()
        timeout = self.timeoutspinner.get_value()
        vgasize = self.videosizepicker.get_value()
        vgaquality = "high"
        action = self.actionpicker.get_value_data()
        methods = []
        if sloader == 'LILO':
            blobject = bootloaders.LILO(
                target=tgt,
                timeout = timeout,
                vga_size = vgasize,
                vga_quality = vgaquality,
                include_running_os=True
                )
            methods.append(blobject.install)
        elif sloader == 'GRUB2':
            blobject = bootloaders.GRUB2(
                target=tgt,
                timeout=timeout,
                vga_size=vgasize,
                vga_quality=vgaquality
                )
            if action == bootloaders.Install:
                methods.append(blobject.install)
                methods.append(blobject.refresh_menu)
            elif action == bootloaders.Refresh:
                methods.append(blobject.refresh_menu)
        # run the changes.  First drop the UI and go to a terminal
        self.pause_ui()
        print "Saving changes to %s on %s"% (blobject.type, blobject.target)
        print "This may take a few seconds... Please wait."
        for method in methods:
            try:
                logger.debug("Running %s"% method.func_name)
                method()
            except AssertionError, e:
                logger.fatal("%s"% e)
                dia = dialogs.Error(
                    parent = self,
                    message = ''.join((
                        _("The following error was encoutered while saving changes."), '\n\n%s'%e,
                        '\n\n',_('DO NOT restart your computer until you are sure your bootloder is fine.')
                        )),
                    buttons = [("OK", self.return_to_module)]
                    )
                self.return_to_module()
                self.pop_dialog(dia, 2)
                logger.error("Error while running %s: %s"%(method.func_name, e))
                return self.resume_ui()
            time.sleep(1)
        logger.info('%s updated'% sloader)
        dia = dialogs.Info(
            parent = self,
            message = ''.join((
                _("Changes saved successfully."), "  ",
                _("You will see the changes applied the next time the system restarts.")
                )),
            buttons = [("OK", self.close_module)]                
            )
        self.return_to_module()
        logger.debug("Bootloader settings saved.")
        self.pop_dialog(dia)
        self.resume_ui()

    def do_save_changes(self, widget=None):
        return self._do_write_changes()


    def refresh_module_status_text(self, widget=None):
        if isinstance(widget, widgets.SensibleLabel):
            return self.set_status_text('Press Enter/Space to activate')
        elif isinstance(widget, widgets.TimeSpinner):
            return self.set_status_text("Use +/- to increate / decrease value")
        elif isinstance(widget, urwid.RadioButton):
           return self.set_status_text("Press Enter/Space to select")            

    def action_toggle_event(self, radio=None, new_state=None, user_data=None):
        pass

    def _get_valid_targets(self, bootloader=bootloaders.DEFAULT_BOOTLOADER):
        partitions = fstab.list_all_system_partitions()
        drives = fstab.list_all_system_drives()
        if bootloader.upper() == 'LILO':
            targets = partitions
            targets.extend(drives)
            targets.sort()
            return targets
        elif bootloader.upper() == 'GRUB2':
            targets = drives
            targets.sort()
            return targets
        return []

    def _get_valid_video_sizes(self, bootloader=bootloaders.DEFAULT_BOOTLOADER):
        ret = []
        if bootloader.upper() in ("LILO","GRUB2"):
            ret = ["600x480","800x600","1024x768", "1280x1024"]
            return ret
        return ret

    def _get_valid_video_quality(self, bootloader=bootloaders.DEFAULT_BOOTLOADER):
        ret = []
        if bootloader.upper() == 'LILO':
            ret = ['low','med','hight']
        elif bootloader.upper() == 'GRUB2':
            ret = ['med']
        return ret
            
    
    def populate_pickers(self, bootloader):
        # populate the targets.
        self.targetpicker.set_options(self._get_valid_targets(bootloader))
        self.videosizepicker.set_options(self._get_valid_video_sizes(bootloader))
        self.videosizepicker.set_value('800x600')
        # We want to force the target to reset because we dont want
        # an invalid (ie, bootsector) value in the target when grub is selected
        return self.targetpicker.reset_value()        

    def blpicker_event(self, radio=None, new_state=None, user_data=None):
        if new_state is True:
            return self.populate_pickers(radio.get_label())
            dia = dialogs.Info(radio.get_label(), parent=self.parent,
                                   buttons = [("OK", self.return_to_module)])
            return self.pop_dialog(dia)

def __vasm_test__():
    return os.geteuid() == 0


def __run__(parent):
    # check if we can read the bootsector.
    bsector = bootloaders.get_current_bootsector()
    if bsector is None:
	msg = (
		_("This module is not available because your current bootsector cannot be read."),
		_("This could indicate that you are working inside a chroot environment."),
		)
	dia = dialogs.Error(
		parent = parent,
		buttons = [("OK", parent.return_home)],
		message = '  '.join(msg)
		)
	logger.error('Unable to read bootsector.  %s bailing out'% __file__)
	return parent.pop_module(dia)
    mod = BootloaderTui(parent)
    parent.pop_module(mod)

VASM_CATEGORY = "System"
VASM_LABEL = "Sytem Boot Menu"
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.