Source

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

Full commit
#!/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_fstab.py
text mode module to interact /etc/fstab
"""
__author__ = "Moises Henriquez"
__author_email__ = "moc.liamg@xnl.E0M"[::-1]

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

logger = logging.getLogger('vasm')

VASM_CATEGORY = "System"
VASM_LABEL = "Mount Points"

class AddEntry(widgets.TuiModule):
    def __init__(self, parent, datamodel, buttons = []):
        self.parent = parent
        self.datamodel = datamodel
        _choices = []
        _body = [widgets.blank,
            urwid.Text(_("Select which partition to add"))]
        radios = []
        self.entryobject = None
        self.mpointentry = urwid.Edit(caption=_("Mountpoint") + " : ")
        self.optionsentry = urwid.Edit(caption=_("Options") + " : ")
        self.mountnowcheckbox = urwid.CheckBox(
            _("Mount partition now"), state=True,
            on_state_change = self.mountcheckbox_toggle_event)
        # make up the list of available partitions
        i = 0
        for item in fstab.list_all_system_partitions():
            if not self.datamodel.hasEntry(item):
                wid = urwid.RadioButton(group=radios,
                    on_state_change = self._radio_toggle_event,
                    label=item, state = i == 0)
                wrap = urwid.AttrWrap(wid, 'button', 'button sel')
                #_body.append(wrap) 
                _choices.append(wrap)
            i += 1
        _pile_list = [
            ('flow', widgets.blank),
            ('flow', urwid.Text(_("Select a partition to add"))),
            urwid.ListBox(urwid.SimpleListWalker(_choices))]
        _pile_list.append(('flow', 
            urwid.AttrWrap(self.mpointentry, 'entry done', 'entry')))
        _pile_list.append(('flow', 
            urwid.AttrWrap(self.optionsentry, 'entry done', 'entry')))
        _pile_list.append(('flow', widgets.blank))
        _pile_list.append(('flow', 
            urwid.AttrWrap(self.mountnowcheckbox, 'button', 'button sel')))

        body = urwid.Pile(_pile_list)
        widgets.TuiModule.__init__(self,
            parent = self.parent,
            body = body,
            header = _("Add new entry to /etc/fstab"),
            desc = [],
            buttons = buttons)
        self.set_focus('body')
    
    def mountcheckbox_toggle_event(self, checkbox, newstate):
        """ Toggle the flag to mount or not mount the entry """
        self.entryobject._mount_now = newstate
        return        
    
    def _radio_toggle_event(self, radio, newstate):
        if not newstate:
            return
        self.entryobject = fstab.FstabEntry(device = radio.get_label())
        self.entryobject.device = radio.get_label()
        self.entryobject.filesystem = self.entryobject.find_filesystem()
        self.entryobject.mountpoint = self.entryobject.find_default_mountpoint()
        self.entryobject.filesystem = self.entryobject.find_filesystem()
        self.entryobject.options = self.entryobject.find_default_options()
        self.entryobject._mount_now = self.mountnowcheckbox.get_state()
        if self.mpointentry:
            self.mpointentry.set_edit_text(self.entryobject.mountpoint)
        if self.optionsentry:
            self.optionsentry.set_edit_text(self.entryobject.options)

class AboutEntry(widgets.TuiModule):
    def __init__(self, parent=None, datamodel = None, entry_device=None):
        self.parent = parent
        self.datamodel = datamodel
        self.entry = self.datamodel.getEntry(entry_device)
        _body = []
        lbmpoint = urwid.Text("%s : %s"% (_("Mount Point"), self.entry.mountpoint))
        lbfs = urwid.Text("%s : %s"% (_("Filesystem"), self.entry.filesystem))
        lbmopts = urwid.Text("%s : %s"% (_("Mount Options"), self.entry.options))
        lbuuid = urwid.Text("UUID : %s"% self.entry.uuid or "Not Available")
        _body.extend([lbmpoint, lbfs, lbmopts, lbuuid])
        if entry_device.startswith('/') and self.entry.mountpoint != '/':
            # Display a delete option button only for non  / mountpoints.
            _buttons = [
                (_("Back"), self.parent.return_to_module),
                (_("Delete"), self.delete_entry),]
        else:
            _buttons = [
                (_("Back"), self.parent.return_to_module)]
        widgets.TuiModule.__init__(self,
            parent = self.parent,
            buttons = _buttons,
            body = _body,
            desc = [widgets.blank, 
                urwid.Text(_("Entry Details.")),
                urwid.Divider(u'='), widgets.blank],
            header = _("About") + " " + self.entry.device)

    def delete_entry(self, widget=None):
        return self.parent._confirm_delete_entry(widget, self.entry)
    
    def keypress(self, size, key):
        if key == 'esc':
            return self.parent.return_to_module()
        return super(AboutEntry, self).keypress(size, key)

class FstabMod(dialogs.ModuleDialog):
    def __init__(self, parent):
        self.parent = parent
        self.fstabmodel = fstab.FstabModel()
        self.fstabmodel.add_observer(self.refresh_fstab_viewer)
        self._title_text = _("System Mountpoint Configuration")
        desc = _("Setup partitions to be mounted during system startup.")

        self.grid = widgets.GridView([
            (_("Partition"), -1),
            (_("Format"),-1),
            (_("Mountpoint"), 0),
            (_("Mount options"), 0)], divspace=1)
        dialogs.ModuleDialog.__init__(
            self,
            parent = self.parent,
            body = self.grid,
            header= self._title_text,
            desc = [widgets.blank],
            buttons = [("Add", self.add_entry_event),
                       ("Done", self.close_module)]
            )
        # Initial view
        self.fstabmodel.notify()
        txt = _("Select an entry for options.")
        self.set_status_text(txt)
    
    def _confirm_delete_entry(self, widget=None, data=None):
        self.return_to_module()
        self.entryToDelete = data
        dia = dialogs.Question(
            message = _("Are you sure you want to delete this entry from /etc/fstab?"),
            parent = self,
            buttons = [("YES", self._do_delete_entry),
            ("NO", self.return_to_module)])            
        return self.pop_dialog(dia)
    
    def _do_delete_entry(self, widget=None):
        try:
            self.fstabmodel.removeEntry(self.entryToDelete.device)
            logger.info('Deleted %s from /etc/fstab'% self.entryToDelete.device)
        except AssertionError, e:
            dia = dialogs.Error(
                message = "%s"% e,
                parent = self,
                buttons = [("OK", self.return_to_module)]
            )
            logger.error('Unable to delete %s from /etc/fstab: %s'% (
                self.entryToDelete.device, e))
            return self.pop_dialog(dia)
        return self.return_to_module()
    
    def add_entry_event(self, widget=None):
        #buttons = [("OK", self.return_to_module),
        #    ("Cancel", self.return_to_module)]
        child = AddEntry(parent=self, datamodel = self.fstabmodel, 
            buttons= [])
        # create an object
        child.entryobject = fstab.FstabEntry()
        child.add_button("OK", self._do_add_entry, child)
        child.add_button("Cancel", self.return_to_module, None)
        self.pop_dialog(child, 2)
    
    def _check_entry_object(self, entry=None):
        """ Check the parameters on the entry.
        Raise an exception as needed """
        assert entry.device not in ('',None), \
            "No device path was selected."
        assert entry.mountpoint.strip() not in (' ','',None), \
            "Invalid mountpoint."
        assert entry.options.strip() not in (' ','',None), \
            "Invalid mountpoint."
        return
    
    def _do_add_entry(self, caller=None, data=None):
        self.return_to_module(None, None)
        # run some checks on the object
        entry = data.entryobject
        entry.mountpoint = data.mpointentry.get_edit_text().strip()
        entry.options = data.optionsentry.get_edit_text().strip()
        try:
            self._check_entry_object(entry)
        except AssertionError, e:
            dia = dialogs.Error(
                message = "%s"% e,
                buttons = [("OK", self.return_to_module)],
                parent = self)
            logger.error("Tests for % failed: %s"% (entry.device, e))
            return self.pop_dialog(dia)
        try:
            self.fstabmodel.addEntry(entry)
            logger.info("%s has been added to /etc/fstab"% entry.device)
        except AssertionError, e:
            dia = dialogs.Error(
                message = "%s"% e,
                buttons = [("OK", self.return_to_module)],
                parent = self)
            logger.error("Unable to add %s to /etc/fstab: %s"% (entry.device, e))
            return self.pop_dialog(dia)
        # Mount the entry if necessary
        if entry._mount_now:
            logger.debug("Mounting %s"%entry.device)
            return entry.mount()
        return

    def refresh_fstab_viewer(self):
        viewer = self.grid
        # Clear the contents before we re-draw them
        viewer.clear()
        for entry in self.fstabmodel.listEntries():
            row = widgets.GridRow(
                columns = [
                    urwid.AttrWrap(widgets.SensibleLabel(
                        markup=entry.device,
                        actuator = self.show_entry_details,
                        user_data = entry.device,
                        on_focus = None,
                        ), 'button', 'button sel'),
                    entry.filesystem,
                    entry.mountpoint,
                    entry.options,
                ],
                parent = viewer,
                tag = entry.device
                )
            viewer.append_row(row)

    def show_entry_details(self, widget=None):
        """ widget is a SensibleLabel which has an user_data property """
        about = AboutEntry(parent = self,
            datamodel = self.fstabmodel, entry_device = widget.user_data)
        return self.pop_dialog(about, 2)
        
        selected = self.fstabmodel.getEntry(widget.user_data)
        _body = []
        _body.append(_("About") + " %s"% selected.device)
        _body.append('\n')
        _body.append(_("Mount Point") + " %s"% selected.mountpoint)
        
        dia = dialogs.Info(
            parent = self.parent,
            message = _body,
            buttons = [],
            )
        return self.pop_dialog(dia)



def __vasm_test__():
    """ Set of tests to determine who sees this module and when """
    return os.geteuid() == 0

def __run__(parent):
    """ Define what to do when this module is selected from the launcher """
    wid = FstabMod(parent)
    parent.pop_module(wid)