kexec-tools/firstboot_kdump.py
Dave Young e02ec8b7e8 firstboot: add automatic and manual memory reservation for rhel
Add two radiobuttons for user to select automatic/manual memory reserve
For fedora they will not be added.

When switching from manual configure to auto reserve, the memory size and
summary widgets will be dimmed.

Testing below cases on virtual machine, all cases need reboot and verify:
Fedora:
1. Enable/Disable kdump
2. change reserve size
3. change kdump.conf content
4. Auto -> Manual
5. Manual -> Auto

RHEL:
modify the /etc/redhat-release and install the rhel7 kernel rpm in fedora vm.
1. auto reservation
2. manual specify reserve mem size
3. enable/disable kdump
4. change kdump.conf content

Vivek: label with text "Memory To Be Reserved"

Signed-off-by: Dave Young <dyoung@redhat.com>
Reviewed-by: Vivek Goyal <vgoyal@redhat.com>
2012-08-28 16:22:35 +08:00

496 lines
17 KiB
Python
Executable File

#
# 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 commands
from firstboot.config import *
from firstboot.constants import *
from firstboot.functions import *
from firstboot.module import *
import gettext
_ = lambda x: gettext.ldgettext("kexec-tools", x)
N_ = lambda x: x
class moduleClass(Module):
def __init__(self):
Module.__init__(self)
self.priority = 100
self.sidebarTitle = N_("Kdump")
self.title = N_("Kdump")
self.reboot = False
# runPriority determines the order in which this module runs in firstboot
runPriority = 70
moduleName = _("Kdump")
windowName = moduleName
reboot = False
# possible bootloaders we'll need to adjust
# todo: f18 grub2 for efi
# bootloader : (config file, kdump offset)
bootloaders = { "grub" : (["/boot/grub/grub.conf", "/boot/efi/EFI/redhat/grub.conf"], [16, 256]),
"grub2" : (["/boot/grub2/grub.cfg"], [16, 256]),
"zipl" : (["/etc/zipl.conf"], [0]),
"yaboot" : (["/boot/etc/yaboot.conf"], [32]) }
bootloader = None
offset = 0
# list of architectures without kdump support
unsupportedArches = [ "ppc", "s390", "i386", "i586" ]
def needsReboot(self):
return self.reboot
# toggle sensitivity of kdump config bits
def showHideReserve(self, status):
self.labelKdump.set_sensitive(status)
self.kdumpMemspin.set_sensitive(status)
self.totalMem.set_sensitive(status)
self.systemUsableMem.set_sensitive(status)
self.labelTotal.set_sensitive(status)
self.labelSys.set_sensitive(status)
# toggle sensitivity of kdump config bits
def showHide(self, status):
show_kdumpmem = status
if self.distro == 'rhel':
show_autoreserve = self.buttonAuto.get_active()
if status == True:
if self.buttonAuto.get_active() == True:
show_kdumpmem = False
self.buttonAuto.set_active(show_autoreserve)
self.buttonManual.set_active(not show_autoreserve)
self.labelReserve.set_sensitive(status)
self.buttonAuto.set_sensitive(status)
self.buttonManual.set_sensitive(status)
self.showHideReserve(show_kdumpmem)
self.labelReserved.set_sensitive(status)
self.labelReservedMemsize.set_sensitive(status)
self.kdumpEnabled = status
self.AdvWindow.set_sensitive(status)
def on_enableKdumpCheck_toggled(self, *args):
showHideStatus = self.enableKdumpCheck.get_active()
self.showHide(showHideStatus)
def on_auto_toggled(self, *args):
if self.distro == 'rhel':
self.showHideReserve(not self.buttonAuto.get_active())
def on_manual_toggled(self, *args):
if self.distro == 'rhel':
self.showHideReserve(self.buttonManual.get_active())
def updateAvail(self, widget, spin):
self.reserveMem = eval(string.strip(self.kdumpMemspin.get_text()))
self.remainingMem = self.availMem - self.reserveMem
self.systemUsableMem.set_text("%s" % self.remainingMem)
def getBootloader(self):
for (name, (conf, offset)) in self.bootloaders.items():
i = 0
for c in conf:
if os.access(c, os.W_OK):
self.bootloader = name
self.offset = i
return self.bootloader
i += 1
self.offset = None
self.bootloader = None
return None
def createScreen(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'
lines = open(releaseFile).readlines()
for line in lines:
if line.find("Fedora") != -1:
self.distro = 'fedora'
else:
self.distro = 'rhel'
# 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.kdumpOffset = 0
self.origCrashKernel = ""
self.kdumpEnabled = False
chkConfigStatus=commands.getoutput('/bin/systemctl is-enabled kdump.service')
if chkConfigStatus.find("enabled") > -1:
self.kdumpEnabled = True
self.kdumpMemInitial = 0
crashString = ""
kexec_crash_size = open("/sys/kernel/kexec_crash_size").read()
self.reservedMem = int(kexec_crash_size)/(1024*1024)
if cmdLine.find("crashkernel") != -1:
crashString = filter(lambda t: t.startswith("crashkernel="),
cmdLine.split())[0].split("=")[1]
self.origCrashKernel = "crashkernel=%s" % (crashString)
if self.doDebug:
print "crashString is %s" % crashString
if crashString.find("@") != -1:
(self.kdumpMemInitial, self.kdumpOffset) = [int(m[:-1]) for m in crashString.split("@")]
else:
#kdumpMemInitial = -1 means auto reservation
if self.distro == 'rhel' and self.origCrashKernel == 'crashkernel=auto':
self.kdumpMemInitial=-1
else:
self.kdumpMemInitial=int(crashString[:-1])
self.kdumpOffset = 0
if self.kdumpMemInitial != 0:
self.availMem += self.reservedMem
self.kdumpEnabled = True
else:
self.kdumpEnabled = False
if self.origCrashKernel.find("crashkernel=") != -1:
self.kdumpEnabled = True
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.kdumpMemInitial == 0 or self.kdumpMemInitial == -1:
self.kdumpMemInitial = lowerBound
else:
# round down to a multiple of step value
self.kdumpMemInitial = self.kdumpMemInitial - (self.kdumpMemInitial % 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_alignment(0.0, 0.5)
self.labelTotal.set_width_chars(32)
# how much ram to reserve for kdump
self.memAdjustment = gtk.Adjustment(self.kdumpMemInitial, lowerBound, upperBound, step, step, 0)
self.kdumpMemspin = gtk.SpinButton(self.memAdjustment, 0, 0)
self.kdumpMemspin.set_update_policy(gtk.UPDATE_IF_VALID)
self.kdumpMemspin.set_numeric(True)
self.memAdjustment.connect("value_changed", self.updateAvail, self.kdumpMemspin)
self.labelKdump = gtk.Label(_("Memory To Be _Reserved (MB):"))
self.labelKdump.set_use_underline(True)
self.labelKdump.set_mnemonic_widget(self.kdumpMemspin)
self.labelKdump.set_alignment(0.0, 0.5)
# remaining usable system memory
self.reserveMem = eval(string.strip(self.kdumpMemspin.get_text()))
self.remainingMem = self.availMem - self.reserveMem
self.systemUsableMem = gtk.Label(_("%s" % self.remainingMem))
self.labelSys = gtk.Label(_("Usable System Memory (MB):"))
self.labelSys.set_alignment(0.0, 0.5)
self.labelReserved=gtk.Label(_("Memory Currently Reserved (MB):"))
self.labelReservedMemsize=gtk.Label(_("%s" % self.reservedMem))
self.labelReserved.set_alignment(0.0, 0.5)
# rhel crashkernel=auto handling
if self.distro == 'rhel':
self.labelReserve = gtk.Label(_("Kdump Memory Reservation:"))
self.buttonAuto = gtk.RadioButton(None, _("_Automatic"))
self.buttonManual = gtk.RadioButton(self.buttonAuto, _("_Manual"))
self.buttonAuto.connect("toggled", self.on_auto_toggled, None)
self.buttonManual.connect("toggled", self.on_manual_toggled, None)
self.buttonAuto.set_use_underline(True)
self.buttonManual.set_use_underline(True)
self.labelReserve.set_alignment(0.0, 0.5)
if self.origCrashKernel == 'crashkernel=auto':
self.buttonAuto.set_active(True)
self.buttonManual.set_active(False)
else:
self.buttonAuto.set_active(False)
self.buttonManual.set_active(True)
self.autoinitial = self.buttonAuto.get_active()
# Add an advanced kdump config text widget
inputbuf = open("/etc/kdump.conf", "r")
self.AdvConfig = gtk.TextView()
AdvBuf = gtk.TextBuffer()
AdvBuf.set_text(inputbuf.read())
inputbuf.close()
self.AdvConfig.set_buffer(AdvBuf)
self.AdvWindow = gtk.ScrolledWindow()
self.AdvWindow.set_shadow_type(gtk.SHADOW_IN)
self.AdvWindow.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
self.AdvWindow.set_size_request(500, 300)
self.AdvWindow.add(self.AdvConfig)
self.AdvConfLabel = gtk.Label(_("\nAdvanced kdump configuration"))
self.AdvConfLabel.set_alignment(0.0, 0.5)
self.vbox = gtk.VBox()
self.vbox.set_size_request(400, 200)
# title_pix = loadPixbuf("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)
if self.distro == 'rhel':
table = gtk.Table(3, 100)
table.attach(self.enableKdumpCheck, 0, 3, 0, 1, gtk.FILL, gtk.FILL, 5, 5)
table.attach(self.labelReserve, 0, 1, 1, 2, gtk.FILL)
table.attach(self.buttonAuto, 1, 2, 1, 2, gtk.FILL, gtk.FILL, 5, 5)
table.attach(self.buttonManual, 2, 3, 1, 2, gtk.FILL, gtk.FILL, 5, 5)
table.attach(self.labelReserved, 0, 1, 2, 3, gtk.FILL)
table.attach(self.labelReservedMemsize, 2, 3, 2, 3, gtk.SHRINK, gtk.FILL, 5, 5)
table.attach(self.labelKdump, 0, 1, 3, 4, gtk.FILL)
table.attach(self.kdumpMemspin, 2, 3, 3, 4, gtk.SHRINK, gtk.FILL, 5, 5)
table.attach(self.labelTotal, 0, 1, 4, 5, gtk.FILL)
table.attach(self.totalMem, 2, 3, 4, 5, gtk.SHRINK, gtk.FILL, 5, 5)
table.attach(self.labelSys, 0, 1, 5, 6, gtk.FILL)
table.attach(self.systemUsableMem, 2, 3, 5, 6, gtk.SHRINK, gtk.FILL, 5, 5)
table.attach(self.AdvConfLabel, 0, 1, 6, 7, gtk.FILL)
table.attach(self.AdvWindow, 0, 3, 7, 100, gtk.FILL, gtk.FILL, 5, 5)
else:
table = gtk.Table(2, 100)
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.kdumpMemspin, 1, 2, 2, 3, gtk.SHRINK, gtk.FILL, 5, 5)
table.attach(self.labelReserved, 0, 1, 3, 4, gtk.FILL)
table.attach(self.labelReservedMemsize, 1, 2, 3, 4, gtk.SHRINK, gtk.FILL, 5, 5)
table.attach(self.labelSys, 0, 1, 4, 5, gtk.FILL)
table.attach(self.systemUsableMem, 1, 2, 4, 5, gtk.SHRINK, gtk.FILL, 5, 5)
table.attach(self.AdvConfLabel, 0, 1, 6, 7, gtk.FILL)
table.attach(self.AdvWindow, 0, 2, 7, 100, gtk.FILL, 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)
self.showHide(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)
def grabFocus(self):
self.enableKdumpCheck.grab_focus()
def configChanged(self):
if self.initialState == self.kdumpEnabled and self.initialState == False:
return False
if self.initialState != self.kdumpEnabled \
or (self.distro == 'rhel' and self.autoinitial != self.buttonAuto.get_active()) \
or (self.distro == 'rhel' and self.buttonManual.get_active() == True and self.reserveMem != self.kdumpMemInitial) \
or (self.distro != 'rhel' and self.reserveMem != self.kdumpMemInitial):
return True
return False
def apply(self, *args):
if self.kdumpEnabled:
self.reserveMem = self.kdumpMemspin.get_value_as_int()
else:
self.reserveMem = self.kdumpMemInitial
self.remainingMem = self.availMem - self.reserveMem
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, self.reserveMem, self.remainingMem)
else:
print "Kdump will be disabled"
# Before we do other checks we should save the users config
AdvBuf = self.AdvConfig.get_buffer()
start, end = AdvBuf.get_bounds()
outputbuf = open("/etc/kdump.conf", "rw+")
outputbuf.write(AdvBuf.get_text(start, end))
outputbuf.close()
# Regardless of what else happens we need to be sure to disalbe kdump if its disabled here, or
# else it will fail during startup
if (self.enableKdumpCheck.get_active() == False):
os.system("/bin/systemctl disable kdump.service")
# 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 RESULT_FAILURE
# 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 RESULT_FAILURE
# 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 RESULT_FAILURE
# Don't alert if nothing has changed
if self.configChanged() == True:
dlg = gtk.MessageDialog(None, 0, gtk.MESSAGE_INFO,
gtk.BUTTONS_YES_NO,
_("Changing Kdump settings requires rebooting the "
"system to reallocate memory accordingly. Would you "
"like to continue with this change and reboot the "
"system after firstboot is complete?"))
dlg.set_position(gtk.WIN_POS_CENTER)
dlg.show_all()
rc = dlg.run()
dlg.destroy()
if rc != gtk.RESPONSE_YES:
self.reboot = False
return RESULT_SUCCESS
else:
self.reboot = 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 RESULT_FAILURE
# Are we adding or removing the crashkernel param?
if self.kdumpEnabled:
_reserves = "%iM" % (self.reserveMem)
if self.distro == 'rhel':
if self.buttonAuto.get_active() == True:
_reserves = 'auto'
grubbyCmd = "/sbin/grubby --%s --update-kernel=ALL --args=crashkernel=%s" \
% (self.bootloader, _reserves)
chkconfigStatus = "enable"
else:
grubbyCmd = "/sbin/grubby --%s --update-kernel=ALL --remove-args=%s" \
% (self.bootloader, self.origCrashKernel)
chkconfigStatus = "disable"
if self.doDebug:
print "Using %s bootloader with %iM offset" % (self.bootloader, self.offset)
print "Grubby command would be:\n %s" % grubbyCmd
print "chkconfig status is %s" % chkconfigStatus
else:
os.system(grubbyCmd)
os.system("/bin/systemctl %s kdump.service" % (chkconfigStatus))
if self.bootloader == 'yaboot':
os.system('/sbin/ybin')
if self.bootloader == 'zipl':
os.system('/sbin/zipl')
else:
self.reboot = False
return RESULT_SUCCESS
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
def initializeUI(self):
pass