Sophie

Sophie

distrib > Scientific%20Linux > 5x > x86_64 > by-pkgid > f8954580ba158b6b6d09fc47ad70af4c > files > 1

kexec-tools-1.102pre-21.el5.src.rpm

#
# 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("kexec-tools")


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, kernel path)
    bootloaders = { "grub"   : ("/boot/grub/grub.conf", 16, "/boot"),
                    "yaboot" : ("/boot/etc/yaboot.conf", 32, "/boot"),
                    "elilo"  : ("/boot/efi/EFI/redhat/elilo.conf", 256, "/boot/efi/EFI/redhat") }
    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, kpath)) in self.bootloaders.items():
            if os.access(conf, os.W_OK):
                self.bootloader = name
        return self.bootloader

    # convert hex memory values into MB of RAM so we can
    # read /proc/iomem and show something a human understands
    def hex2mb(self, hex):
        divisor = 1048575
        mb = int(hex,16) / divisor
        return mb

    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, we do things a bit different w/xen
        self.xenKernel = False
        if os.access("/proc/xen", os.R_OK):
            self.xenKernel = True

        # 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'
                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

        # Check to see if kdump memory is already reserved
        # Read from /proc/iomem so we're portable across xen and non-xen
        self.kdumpMem = 0
        self.kdumpOffset = 0
        self.kdumpMemInitial = 0
        self.origCrashKernel = ""
        self.kdumpEnabled = False
        # PowerPC64 doesn't list crashkernel reservation in /proc/iomem
        if self.arch != 'ppc64':
            ioMem = open("/proc/iomem").readlines()
            for line in ioMem:
                if line.find("Crash kernel") != -1:
                    hexCKstart = line.strip().split("-")[0]
                    hexCKend = line.strip().split("-")[1].split(":")[0].strip()
                    self.kdumpOffset = self.hex2mb(hexCKstart)
                    self.kdumpMem = self.hex2mb(hexCKend) - self.kdumpOffset
                    break
        else:
            cmdLine = open("/proc/cmdline").read()
            if cmdLine.find("crashkernel=") > -1:
                crashString = filter(lambda t: t.startswith("crashkernel="),
                                     cmdLine.split())[0].split("=")[1]
                (self.kdumpMem, self.kdumpOffset) = \
                    [int(m[:-1]) for m in crashString.split("@")]

        # i686 xen requires kernel-PAE for kdump if any memory
        # is mapped above 4GB
        self.xenKdumpKernel = "kernel"
        if self.arch == "i686" and self.xenKernel:
            for line in ioMem:
                if line.find("System RAM") != -1:
                    rangeEnd = line.strip().split("-")[1].split(":")[0].strip()
                    if rangeEnd >= 0x100000000L:
                        self.xenKdumpKernel = "kernel-PAE"
                        break

        # Fix up memory calculations, if need be
        if self.kdumpMem != 0:
            self.kdumpEnabled = True
            self.availMem += self.kdumpMem
            self.kdumpMemInitial = self.kdumpMem
            self.origCrashKernel = "%dM@%dM" % (self.kdumpMem, self.kdumpOffset)
            if doDebug:
                print "crashkernel region of %sM at offset %sM found" \
                       % (self.kdumpMem, self.kdumpOffset)
                if self.arch != 'ppc64':
                    print "(hex offset %s to %s)" % (hexCKstart, hexCKend)
        self.initialState = self.kdumpEnabled

        # Do some sanity-checking and try to present only sane options.
        #
        # Defaults
        lowerBound = 128
        defRes = 128
        minUsable = 256
        step = 64
        self.enoughMem = True
        if self.arch == 'ia64':
            # ia64 usually needs at *least* 256M, page-aligned... :(
            lowerBound = 128
            defRes = 256
            minUsable = 512
        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 = 128
            defRes = 256
            minUsable = 1024

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

        if upperBound < lowerBound:
            self.enoughMem = False
            lowerBound = 0
        elif upperBound < defRes:
            defRes = lowerBound

        # Set spinner to lowerBound unless already set on kernel command line
        if self.kdumpMem == 0:
            self.kdumpMem = defRes
        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 we're running on non-ia64 xen, we still need a non-xen kernel to kexec
        # into for kdump to be able to capture a core (ia64 xen not supported yet)
        # FIXME: can we set this up automagically if we already have a
        # non-xen kernel available?
        if self.xenKernel and self.kdumpEnabled and self.arch == 'ia64':
            self.showErrorMessage(_("Sorry, ia64 xen kernels do not support kdump at this time."))
            self.enableKdumpCheck.set_active(False)
            self.showHide(False)
            return 0
        elif self.xenKernel and self.kdumpEnabled:
            self.showErrorMessage(_("WARNING: xen kdump support requires a non-xen %s RPM to perform actual crash dump capture. Please be sure you have the non-xen %s RPM of the same version as your xen kernel installed." % (self.xenKdumpKernel, self.xenKdumpKernel)))

        # 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
        # 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.kpath = self.bootloaders[self.bootloader][2]
                    # x86_64 typically use a 16M offset, but w/xen, we need 32M atm
                    if self.arch == 'x86_64' and self.xenKernel:
                        self.offset = 32
                    else:
                        self.offset = self.bootloaders[self.bootloader][1]

                # Are we adding or removing the crashkernel param?
                if self.kdumpEnabled and not self.xenKernel:
                    # crashkernel goes on the kernel line, --args puts it there for us
                    grubbyCmd = "/sbin/grubby --%s --update-kernel=%s/vmlinuz-%s --args=crashkernel=%iM@%iM" \
                                % (self.bootloader, self.kpath, self.runningKernel, reservedMem, self.offset)
                    chkconfigStatus = "on"
                elif self.kdumpEnabled and self.xenKernel:
                    # crashkernel goes on the xen line, --mbargs puts it there for us
                    grubbyCmd = "/sbin/grubby --%s --update-kernel=%s/vmlinuz-%s --mbargs=crashkernel=%iM@%iM" \
                                % (self.bootloader, self.kpath, self.runningKernel, reservedMem, self.offset)
                    chkconfigStatus = "on"
                elif self.xenKernel:
                    grubbyCmd = "/sbin/grubby --%s --update-kernel=%s/vmlinuz-%s --remove-mbargs=crashkernel=%s" \
                                % (self.bootloader, self.kpath, self.runningKernel, self.origCrashKernel)
                    chkconfigStatus = "off"
                else:
                    grubbyCmd = "/sbin/grubby --%s --update-kernel=%s/vmlinuz-%s --remove-args=crashkernel=%s" \
                                % (self.bootloader, self.kpath, 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