caf1c13f53
Memory reservation could fail, so add a new widget to show the actual reserved size. If crashkernel is set but reserved size is 0, we still regard kdump is enabled, the actual reserved widget just show the reservation status. vivek: label with text "Memory Currently Reserved" Signed-off-by: Dave Young <dyoung@redhat.com> Reviewed-by: Vivek Goyal <vgoyal@redhat.com>
418 lines
14 KiB
Python
Executable File
418 lines
14 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 showHide(self, status):
|
|
self.totalMem.set_sensitive(status)
|
|
self.kdumpMemspin.set_sensitive(status)
|
|
self.systemUsableMem.set_sensitive(status)
|
|
self.labelTotal.set_sensitive(status)
|
|
self.labelKdump.set_sensitive(status)
|
|
self.labelSys.set_sensitive(status)
|
|
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 updateAvail(self, widget, spin):
|
|
self.remainingMem = self.availMem - spin.get_value_as_int()
|
|
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'
|
|
self.distro = 'rhel'
|
|
lines = open(releaseFile).readlines()
|
|
for line in lines:
|
|
if line.find("Fedora") != -1:
|
|
self.distro = 'fedora'
|
|
|
|
# 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
|
|
|
|
kexec_crash_size = open("/sys/kernel/kexec_crash_size").read()
|
|
self.reservedMem = int(kexec_crash_size)/(1024*1024)
|
|
self.kdumpMem = 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
|
|
if crashString.find("@") != -1:
|
|
(self.kdumpMem, self.kdumpOffset) = [int(m[:-1]) for m in crashString.split("@")]
|
|
else:
|
|
self.kdumpMem=int(crashString[:-1])
|
|
self.kdumpOffset = 0
|
|
self.availMem += self.reservedMem
|
|
self.origCrashKernel = "%dM" % (self.kdumpMem)
|
|
self.kdumpMemInitial = self.kdumpMem
|
|
self.kdumpEnabled = True
|
|
else:
|
|
self.kdumpEnabled = False
|
|
|
|
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.memAdjustment = gtk.Adjustment(self.kdumpMem, lowerBound, upperBound, step, step, 64)
|
|
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(_("_Kdump Memory (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_use_underline(True)
|
|
self.labelSys.set_mnemonic_widget(self.systemUsableMem)
|
|
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)
|
|
|
|
# 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)
|
|
|
|
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)
|
|
|
|
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 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.initialState != self.kdumpEnabled or self.reserveMem != 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. 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:
|
|
grubbyCmd = "/sbin/grubby --%s --update-kernel=/boot/vmlinuz-%s --args=crashkernel=%iM" \
|
|
% (self.bootloader, self.runningKernel, self.reserveMem)
|
|
chkconfigStatus = "enable"
|
|
else:
|
|
grubbyCmd = "/sbin/grubby --%s --update-kernel=/boot/vmlinuz-%s --remove-args=crashkernel=%s" \
|
|
% (self.bootloader, self.runningKernel, 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
|
|
|