kfan / rpms / kexec-tools

Forked from rpms/kexec-tools 3 years ago
Clone
Blob Blame History Raw
#
# firstboot_kdump.py - kdump configuration page for firstboot
# Copyright 2006 Red Hat, Inc.
# Author: Jarod Wilson <jwilson@redhat.com>
# Contributors:
#     Neil Horman <nhorman@redhat.com>
#     Dave Lehman <dlehman@redhat.com>
#
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program 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 this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

import sys
sys.path.append('/usr/share/system-config-kdump/')

from gtk import *
import string
import os
import os.path
import time
import gtk
import gobject
import functions
import commands
import rhpl.executil as executil
from firstboot import start_process

from rhpl.translate import _, N_
from rhpl import translate
translate.textdomain("firstboot")


class childWindow:
    # runPriority determines the order in which this module runs in firstboot
    runPriority = 70
    moduleName = _("Kdump")
    windowName = moduleName
    needsReboot = False

    # possible bootloaders we'll need to adjust
    #             bootloader : (config file, kdump offset)
    bootloaders = { "grub"   : ("/boot/grub/grub.conf", 16),
                    "yaboot" : ("/boot/etc/yaboot.conf", 32),
                    "elilo"  : ("/boot/efi/EFI/redhat/elilo.conf", 256) }
    bootloader = None
    offset = 0

    # list of architectures without kdump support
    unsupportedArches = [ "ppc", "s390", "s390x", "i386", "i586" ]

    # list of platforms that have a separate kernel-kdump
    kernelKdumpArches = [ "ppc64" ]
    kernelKdumpInstalled = False

    # toggle sensitivity of kdump config bits
    def showHide(self, status):
        self.totalMem.set_sensitive(status)
        self.kdumpMem.set_sensitive(status)
        self.systemUsableMem.set_sensitive(status)
        self.labelTotal.set_sensitive(status)
        self.labelKdump.set_sensitive(status)
        self.labelSys.set_sensitive(status)
        self.kdumpEnabled = status

    def on_enableKdumpCheck_toggled(self, *args):
        showHideStatus = self.enableKdumpCheck.get_active()
        self.showHide(showHideStatus)

    def updateAvail(self, widget, spin):
        self.remMem = self.availMem - spin.get_value_as_int()
        self.systemUsableMem.set_text("%s" % self.remMem)

    def getBootloader(self):
        for (name, (conf, offset)) in self.bootloaders.items():
            if os.access(conf, os.W_OK):
                self.bootloader = name
        return self.bootloader

    def launch(self, doDebug = None):
        self.doDebug = doDebug

        if doDebug:
            print "initializing kdump module"

        # What kernel are we running?
        self.runningKernel = os.popen("/bin/uname -r").read().strip()

        # What arch are we running on?
        self.arch = os.popen("/bin/uname -m").read().strip()

        # Check for a xen kernel, kdump doesn't work w/xen just yet...
        self.xenKernel = self.runningKernel.find("xen")

        # Fedora or RHEL?
        releaseFile = '/etc/redhat-release'
        self.distro = 'rhel'
        lines = open(releaseFile).readlines()
        for line in lines:
            if line.find("Fedora") != -1:
                self.distro = 'fedora'
                kernelKdumpArchesFC = [ "i686", "x86_64" ]
                self.kernelKdumpArches.extend(kernelKdumpArchesFC)
                break

        # If we need kernel-kdump, check to see if its already installed
        if self.arch in self.kernelKdumpArches:
            self.kernelKdump = "/boot/vmlinux-%skdump" % self.runningKernel
            if os.access(self.kernelKdump, os.R_OK):
                self.kernelKdumpInstalled = True

        # Ascertain how much memory is in the system
        memInfo = open("/proc/meminfo").readlines()
        self.availMem = 0
        for line in memInfo:
                if line.startswith("MemTotal:"):
                    self.availMem = int(line.split()[1]) / 1024
                    break

        # Fix up memory calculations if kdump is already on
        cmdLine = open("/proc/cmdline").read()
        self.kdumpMem = 0
        self.kdumpOffset = 0
        self.origCrashKernel = ""
	chkConfigStatus=commands.getoutput('/sbin/chkconfig --list kdump')
        if chkConfigStatus.find("on") > -1:
            self.kdumpEnabled = True
	    self.kdumpMemInitial = 0
	    if cmdLine.find("crashkernel") > -1:
		    crashString = filter(lambda t: t.startswith("crashkernel="),
					 cmdLine.split())[0].split("=")[1]
		    if self.doDebug:
			print "crashString is %s" % crashString
		    (self.kdumpMem, self.kdumpOffset) = [int(m[:-1]) for m in crashString.split("@")]
		    self.availMem += self.kdumpMem
		    self.origCrashKernel = "%dM@%dM" % (self.kdumpMem, self.kdumpOffset)
		    self.kdumpMemInitial = self.kdumpMem
        else:
            self.kdumpEnabled = False
            self.kdumpMemInitial = 0
        self.initialState = self.kdumpEnabled

        # Do some sanity-checking and try to present only sane options.
        #
        # Defaults
        lowerBound = 128
        minUsable = 256
        step = 64
        self.enoughMem = True
        if self.arch == 'ia64':
            # ia64 usually needs at *least* 256M, page-aligned... :(
            lowerBound = 256
            minUsable = 512
            step = 256
        elif self.arch == 'ppc64':
            # ppc64 often fails w/128M lately, and we want at least 1G
            # of RAM for normal use, due to 64k page size... :\
            lowerBound = 256
            minUsable = 1024

        upperBound = (self.availMem - minUsable) - (self.availMem % step)

        if upperBound < lowerBound:
            self.enoughMem = False

        # Set spinner to lowerBound unless already set on kernel command line
        if self.kdumpMem == 0:
            self.kdumpMem = lowerBound
        else:
            # round down to a multiple of step value
            self.kdumpMem = self.kdumpMem - (self.kdumpMem % step)

        # kdump enable/disable checkbox
        self.enableKdumpCheck = gtk.CheckButton("Enable kdump?")
        self.enableKdumpCheck.set_alignment(xalign=0, yalign=0)

        # detected total amount of system memory
        self.totalMem = gtk.Label(_("%s" % self.availMem))
        self.labelTotal = gtk.Label(_("_Total System Memory (MB):"))
        self.labelTotal.set_use_underline(True)
        self.labelTotal.set_mnemonic_widget(self.totalMem)
        self.labelTotal.set_alignment(0.0, 0.5)
        self.labelTotal.set_width_chars(32)

        # how much ram to reserve for kdump
        self.memSpin = gtk.Adjustment(self.kdumpMem, lowerBound, upperBound, step, step, 64)
        self.kdumpMem = gtk.SpinButton(self.memSpin, 0, 0)
        self.kdumpMem.set_update_policy(gtk.UPDATE_IF_VALID)
        self.kdumpMem.set_numeric(True)
        self.memSpin.connect("value_changed", self.updateAvail, self.kdumpMem)
        self.labelKdump = gtk.Label(_("_Kdump Memory (MB):"))
        self.labelKdump.set_use_underline(True)
        self.labelKdump.set_mnemonic_widget(self.kdumpMem)
        self.labelKdump.set_alignment(0.0, 0.5)

        # remaining usable system memory
        self.resMem = eval(string.strip(self.kdumpMem.get_text()))
        self.remMem = self.availMem - self.resMem
        self.systemUsableMem = gtk.Label(_("%s" % self.remMem))
        self.labelSys = gtk.Label(_("_Usable System Memory (MB):"))
        self.labelSys.set_use_underline(True)
        self.labelSys.set_mnemonic_widget(self.systemUsableMem)
        self.labelSys.set_alignment(0.0, 0.5)
        
        self.vbox = gtk.VBox()
        self.vbox.set_size_request(400, 200)

        title_pix = functions.imageFromFile("workstation.png")

        internalVBox = gtk.VBox()
        internalVBox.set_border_width(10)
        internalVBox.set_spacing(10)

        label = gtk.Label(_("Kdump is a kernel crash dumping mechanism. In the event of a "
                            "system crash, kdump will capture information from your system "
                            "that can be invaluable in determining the cause of the crash. "
                            "Note that kdump does require reserving a portion of system "
                            "memory that will be unavailable for other uses."))

        label.set_line_wrap(True)
        label.set_alignment(0.0, 0.5)
        label.set_size_request(500, -1)
        internalVBox.pack_start(label, False, True)

        table = gtk.Table(2, 4)

        table.attach(self.enableKdumpCheck, 0, 2, 0, 1, gtk.FILL, gtk.FILL, 5, 5)

        table.attach(self.labelTotal, 0, 1, 1, 2, gtk.FILL)
        table.attach(self.totalMem, 1, 2, 1, 2, gtk.SHRINK, gtk.FILL, 5, 5)

        table.attach(self.labelKdump, 0, 1, 2, 3, gtk.FILL)
        table.attach(self.kdumpMem, 1, 2, 2, 3, gtk.SHRINK, gtk.FILL, 5, 5)

        table.attach(self.labelSys, 0, 1, 3, 4, gtk.FILL)
        table.attach(self.systemUsableMem, 1, 2, 3, 4, gtk.SHRINK, gtk.FILL, 5, 5)

        # disable until user clicks check box, if not already enabled
        if self.initialState is False:
            self.showHide(False)
        else:
            self.enableKdumpCheck.set_active(True)

        internalVBox.pack_start(table, True, 15)

        # toggle sensitivity of Mem items
        self.enableKdumpCheck.connect("toggled", self.on_enableKdumpCheck_toggled)

        self.vbox.pack_start(internalVBox, False, 15)

        return self.vbox, title_pix, self.moduleName

    def grabFocus(self):
        self.enableKdumpCheck.grab_focus()

    def apply(self, *args):
        if self.kdumpEnabled:
            totalSysMem = self.totalMem.get_text()
            totalSysMem = eval(string.strip(totalSysMem))
            reservedMem = self.kdumpMem.get_value_as_int()
            remainingMem = totalSysMem - reservedMem
        else:
            reservedMem = self.kdumpMemInitial

        if self.doDebug:
            print "Running kernel %s on %s architecture" % (self.runningKernel, self.arch)
            if self.enableKdumpCheck.get_active():
                print "System Mem: %s MB    Kdump Mem: %s MB    Avail Mem: %s MB" % (totalSysMem, reservedMem, remainingMem)
            else:
                print "Kdump will be disabled"

        # If the user simply doesn't have enough memory for kdump to be viable/supportable, tell 'em
        if self.enoughMem is False and self.kdumpEnabled:
            self.showErrorMessage(_("Sorry, your system does not have enough memory for kdump to be viable!"))
            self.enableKdumpCheck.set_active(False)
            self.showHide(False)
            return 0
        # Alert user that we're not going to turn on kdump if they're running a xen kernel
        elif self.xenKernel != -1 and self.kdumpEnabled:
            self.showErrorMessage(_("Sorry, Xen kernels do not support kdump at this time!"))
            self.enableKdumpCheck.set_active(False)
            self.showHide(False)
            return 0
        # If there's no kdump support on this arch, let the user know and don't configure
        elif self.arch in self.unsupportedArches:
            self.showErrorMessage(_("Sorry, the %s architecture does not support kdump at this time!" % self.arch))
            self.enableKdumpCheck.set_active(False)
            self.showHide(False)
            return 0

        # If running on an arch w/a separate kernel-kdump (i.e., non-relocatable kernel), check to
        # see that its installed, otherwise, alert the user they need to install it, and give them
        # the chance to abort configuration.
        if self.arch in self.kernelKdumpArches and self.kernelKdumpInstalled is False:
            kernelKdumpNote = "\n\nNote that the %s architecture does not feature a relocatable kernel at this time, and thus requires a separate kernel-kdump package to be installed for kdump to function. This can be installed via 'yum install kernel-kdump' at your convenience.\n\n" % self.arch
        else:
            kernelKdumpNote = ""

        # Don't alert if nothing has changed
        if self.initialState != self.kdumpEnabled or reservedMem != self.kdumpMemInitial:
            dlg = gtk.MessageDialog(None, 0, gtk.MESSAGE_INFO,
                                    gtk.BUTTONS_YES_NO,
                                    _("Changing Kdump settings requires rebooting the "
                                      "system to reallocate memory accordingly. %sWould you "
                                      "like to continue with this change and reboot the "
                                      "system after firstboot is complete?" % kernelKdumpNote))
            dlg.set_position(gtk.WIN_POS_CENTER)
            dlg.show_all()
            rc = dlg.run()
            dlg.destroy()

            if rc == gtk.RESPONSE_NO:
                self.needsReboot = False
                return None
            else:
                self.needsReboot = True

                # Find bootloader if it exists, and update accordingly
                if self.getBootloader() == None:
                    self.showErrorMessage(_("Error! No bootloader config file found, aborting configuration!"))
                    self.enableKdumpCheck.set_active(False)
                    self.showHide(False)
                    return 0
                else:
                    self.offset = self.bootloaders[self.bootloader][1]

                # Are we adding or removing the crashkernel param?
                if self.kdumpEnabled:
                    grubbyCmd = "/sbin/grubby --%s --update-kernel=/boot/vmlinuz-%s --args=crashkernel=%iM@%iM" \
                                % (self.bootloader, self.runningKernel, reservedMem, self.offset)
                    chkconfigStatus = "on"
                else:
                    grubbyCmd = "/sbin/grubby --%s --update-kernel=/boot/vmlinuz-%s --remove-args=crashkernel=%s" \
                                % (self.bootloader, self.runningKernel, self.origCrashKernel)
                    chkconfigStatus = "off" 

                if self.doDebug:
                    print "Using %s bootloader with %iM offset" % (self.bootloader, self.offset)
                    print "Grubby command would be:\n    %s" % grubbyCmd
                else:
                    os.system(grubbyCmd)
                    os.system("/sbin/chkconfig kdump %s" % chkconfigStatus)
                    if self.bootloader == 'yaboot':
                        os.system('/sbin/ybin')
        else:
            self.needsReboot = False


        return 0

    def showErrorMessage(self, text):
        dlg = gtk.MessageDialog(None, 0, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, text)
        dlg.set_position(gtk.WIN_POS_CENTER)
        dlg.set_modal(True)
        rc = dlg.run()
        dlg.destroy()
        return None