From 731070a68e54e01015b520e2c19141a912923218 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jaroslav=20=C5=A0karvada?= <jskarvad@redhat.com>
Date: Sun, 9 Dec 2018 20:16:50 +0100
Subject: [PATCH] Added support for Boot loader specification (BLS)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Resolves: rhbz#1576435
Signed-off-by: Jaroslav Škarvada <jskarvad@redhat.com>
---
91-tuned.install | 31 ++++++++++++++++++++++
Makefile | 6 ++++-
tuned.spec | 19 ++++++++++++++
tuned/consts.py | 3 +++
tuned/plugins/plugin_bootloader.py | 41 +++++++++++++++++++++++++++---
tuned/utils/commands.py | 15 ++++++-----
6 files changed, 104 insertions(+), 11 deletions(-)
create mode 100644 91-tuned.install
diff --git a/91-tuned.install b/91-tuned.install
new file mode 100644
index 0000000..18d00d9
--- /dev/null
+++ b/91-tuned.install
@@ -0,0 +1,31 @@
+#!/bin/bash
+
+COMMAND="$1"
+KERNEL_VERSION="$2"
+BOOT_DIR_ABS="$3"
+KERNEL_IMAGE="$4"
+
+if ! [[ $KERNEL_INSTALL_MACHINE_ID ]]; then
+ exit 0
+fi
+
+MACHINE_ID=$KERNEL_INSTALL_MACHINE_ID
+
+# with grub2 always /boot
+BOOT_ROOT="/boot"
+LOADER_ENTRIES="$BOOT_ROOT/loader/entries"
+
+[ -d "$LOADER_ENTRIES" ] || exit 0
+
+[ "$COMMAND" = "add" ] || exit 0
+
+pushd "$LOADER_ENTRIES"
+for f in `basename "$MACHINE_ID"`-*.conf; do
+ if [ -f "$f" -a "${f: -12}" != "-rescue.conf" ]; then
+ grep -q '^\s*options\s\+.*\$tuned_params' "$f" || sed -i '/^\s*options\s\+/ s/\(.*\)/\1 \$tuned_params/' "$f"
+ grep -q '^\s*initrd\s\+.*\$tuned_initrd' "$f" || sed -i '/^\s*initrd\s\+/ s/\(.*\)/\1 \$tuned_initrd/' "$f"
+ fi
+done
+popd
+
+exit 0
diff --git a/Makefile b/Makefile
index 816fcb1..f61cd01 100644
--- a/Makefile
+++ b/Makefile
@@ -40,6 +40,7 @@ PYTHON_SITELIB = $(shell $(PYTHON) -c 'from distutils.sysconfig import get_pytho
ifeq ($(PYTHON_SITELIB),)
$(error Failed to determine python library directory)
endif
+KERNELINSTALLHOOKDIR = /usr/lib/kernel/install.d
TUNED_PROFILESDIR = /usr/lib/tuned
TUNED_RECOMMEND_DIR = $(TUNED_PROFILESDIR)/recommend.d
TUNED_USER_RECOMMEND_DIR = $(SYSCONFDIR)/tuned/recommend.d
@@ -59,7 +60,7 @@ release-cp: release-dir
cp -a tuned.py tuned.spec tuned.service tuned.tmpfiles Makefile tuned-adm.py \
tuned-adm.bash dbus.conf recommend.conf tuned-main.conf 00_tuned \
- bootcmdline modules.conf com.redhat.tuned.policy \
+ 91-tuned.install bootcmdline modules.conf com.redhat.tuned.policy \
com.redhat.tuned.gui.policy tuned-gui.py tuned-gui.glade \
tuned-gui.desktop $(VERSIONED_NAME)
cp -a doc experiments libexec man profiles systemtap tuned contrib icons \
@@ -180,6 +181,9 @@ install: install-dirs
# grub template
install -Dpm 0755 00_tuned $(DESTDIR)$(SYSCONFDIR)/grub.d/00_tuned
+ # kernel install hook
+ install -Dpm 0755 91-tuned.install $(DESTDIR)$(KERNELINSTALLHOOKDIR)/91-tuned.install
+
# polkit configuration
install -Dpm 0644 com.redhat.tuned.policy $(DESTDIR)$(DATADIR)/polkit-1/actions/com.redhat.tuned.policy
install -Dpm 0644 com.redhat.tuned.gui.policy $(DESTDIR)$(DATADIR)/polkit-1/actions/com.redhat.tuned.gui.policy
diff --git a/tuned.spec b/tuned.spec
index 1ecf320..b7ee60d 100644
--- a/tuned.spec
+++ b/tuned.spec
@@ -278,6 +278,24 @@ if [ "$1" == 0 ]; then
if [ -r "%{_sysconfdir}/default/grub" ]; then
sed -i '/GRUB_CMDLINE_LINUX_DEFAULT="${GRUB_CMDLINE_LINUX_DEFAULT:+$GRUB_CMDLINE_LINUX_DEFAULT }\\$tuned_params"/d' %{_sysconfdir}/default/grub
fi
+
+# cleanup for Boot loader specification (BLS)
+
+# clear grubenv variables
+ grub2-editenv - unset tuned_params tuned_initrd &>/dev/null || :
+# unpatch BLS entries
+ MACHINE_ID=`cat /etc/machine-id 2>/dev/null`
+ if [ "$MACHINE_ID" ]
+ then
+ for f in /boot/loader/entries/$MACHINE_ID-*.conf
+ do
+ if [ -f "$f" -a "${f: -12}" != "-rescue.conf" ]
+ then
+ sed -i '/^\s*options\s\+.*\$tuned_params/ s/\s\+\$tuned_params\b//g' "$f" &>/dev/null || :
+ sed -i '/^\s*initrd\s\+.*\$tuned_initrd/ s/\s\+\$tuned_initrd\b//g' "$f" &>/dev/null || :
+ fi
+ done
+ fi
fi
@@ -374,6 +392,7 @@ fi
%{_datadir}/tuned/grub2
%{_datadir}/polkit-1/actions/com.redhat.tuned.policy
%ghost %{_sysconfdir}/modprobe.d/kvm.rt.tuned.conf
+%{_prefix}/lib/kernel/install.d/91-tuned.install
%files gtk
%defattr(-,root,root,-)
diff --git a/tuned/consts.py b/tuned/consts.py
index ab87c70..de94059 100644
--- a/tuned/consts.py
+++ b/tuned/consts.py
@@ -37,6 +37,9 @@ BOOT_CMDLINE_TUNED_VAR = "TUNED_BOOT_CMDLINE"
BOOT_CMDLINE_INITRD_ADD_VAR = "TUNED_BOOT_INITRD_ADD"
BOOT_CMDLINE_FILE = "/etc/tuned/bootcmdline"
PETITBOOT_DETECT_DIR = "/sys/firmware/opal"
+MACHINE_ID_FILE = "/etc/machine-id"
+KERNEL_UPDATE_HOOK_FILE = "/usr/lib/kernel/install.d/91-tuned.install"
+BLS_ENTRIES_PATH = "/boot/loader/entries"
# modules plugin configuration
MODULES_FILE = "/etc/modprobe.d/tuned.conf"
diff --git a/tuned/plugins/plugin_bootloader.py b/tuned/plugins/plugin_bootloader.py
index d88b62c..400b07e 100644
--- a/tuned/plugins/plugin_bootloader.py
+++ b/tuned/plugins/plugin_bootloader.py
@@ -94,7 +94,7 @@ class BootloaderPlugin(base.Plugin):
def _remove_grub2_tuning(self):
if not self._grub2_cfg_file_names:
- log.error("cannot find grub.cfg to patch, you need to regenerate it by hand using grub2-mkconfig")
+ log.info("cannot find grub.cfg to patch")
return
self._patch_bootcmdline({consts.BOOT_CMDLINE_TUNED_VAR : "", consts.BOOT_CMDLINE_INITRD_ADD_VAR : ""})
for f in self._grub2_cfg_file_names:
@@ -107,6 +107,7 @@ class BootloaderPlugin(base.Plugin):
if full_rollback:
log.info("removing grub2 tuning previously added by Tuned")
self._remove_grub2_tuning()
+ self._update_grubenv({"tuned_params" : "", "tuned_initrd" : ""})
def _grub2_cfg_unpatch(self, grub2_cfg):
log.debug("unpatching grub.cfg")
@@ -138,7 +139,7 @@ class BootloaderPlugin(base.Plugin):
def _grub2_default_env_patch(self):
grub2_default_env = self._cmd.read_file(consts.GRUB2_DEFAULT_ENV_FILE)
if len(grub2_default_env) <= 0:
- log.error("error reading '%s'" % consts.GRUB2_DEFAULT_ENV_FILE)
+ log.info("cannot read '%s'" % consts.GRUB2_DEFAULT_ENV_FILE)
return False
d = {"GRUB_CMDLINE_LINUX_DEFAULT" : consts.GRUB2_TUNED_VAR, "GRUB_INITRD_OVERLAY" : consts.GRUB2_TUNED_INITRD_VAR}
@@ -157,12 +158,12 @@ class BootloaderPlugin(base.Plugin):
def _grub2_cfg_patch(self, d):
log.debug("patching grub.cfg")
if not self._grub2_cfg_file_names:
- log.error("cannot find grub.cfg to patch, you need to regenerate it by hand by grub2-mkconfig")
+ log.info("cannot find grub.cfg to patch")
return False
for f in self._grub2_cfg_file_names:
grub2_cfg = self._cmd.read_file(f)
if len(grub2_cfg) <= 0:
- log.error("error patching %s, you need to regenerate it by hand by grub2-mkconfig" % f)
+ log.info("cannot patch %s" % f)
return False
log.debug("adding boot command line parameters to '%s'" % f)
grub2_cfg_new = grub2_cfg
@@ -187,6 +188,37 @@ class BootloaderPlugin(base.Plugin):
self._grub2_cfg_patch({consts.GRUB2_TUNED_VAR : self._cmdline_val, consts.GRUB2_TUNED_INITRD_VAR : self._initrd_val})
self._patch_bootcmdline({consts.BOOT_CMDLINE_TUNED_VAR : self._cmdline_val, consts.BOOT_CMDLINE_INITRD_ADD_VAR : self._initrd_val})
+ def _has_bls(self):
+ return os.path.exists(consts.BLS_ENTRIES_PATH)
+
+ def _update_grubenv(self, d):
+ log.debug("updating grubenv, setting %s" % str(d));
+ l = ["%s=%s" % (str(option), str(value)) for option, value in d.items()]
+ (rc, out) = self._cmd.execute(["grub2-editenv", "-", "set"] + l)
+ if rc != 0:
+ log.warn("cannot update grubenv: '%s'" % out)
+ return False;
+ return True
+
+ def _bls_entries_patch_initial(self):
+ machine_id = self._cmd.get_machine_id()
+ if machine_id == "":
+ return False
+ log.debug("running kernel update hook '%s' to patch BLS entries" % consts.KERNEL_UPDATE_HOOK_FILE)
+ (rc, out) = self._cmd.execute([consts.KERNEL_UPDATE_HOOK_FILE, "add"], env = {"KERNEL_INSTALL_MACHINE_ID" : machine_id})
+ if rc != 0:
+ log.warn("cannot patch BLS entries: '%s'" % out)
+ return False
+ return True
+
+ def _bls_update(self):
+ log.debug("updating BLS")
+ if self._has_bls() and \
+ self._update_grubenv({"tuned_params" : self._cmdline_val, "tuned_initrd" : self._initrd_val}) and \
+ self._bls_entries_patch_initial():
+ return True
+ return False
+
def _init_initrd_dst_img(self, name):
if self._initrd_dst_img_val is None:
self._initrd_dst_img_val = os.path.join(consts.BOOT_DIR, os.path.basename(name))
@@ -307,4 +339,5 @@ class BootloaderPlugin(base.Plugin):
def _instance_post_static(self, instance, enabling):
if enabling and self.update_grub2_cfg:
self._grub2_update()
+ self._bls_update()
self.update_grub2_cfg = False
diff --git a/tuned/utils/commands.py b/tuned/utils/commands.py
index 5692450..68d2ea5 100644
--- a/tuned/utils/commands.py
+++ b/tuned/utils/commands.py
@@ -25,7 +25,6 @@ log = tuned.logs.get()
class commands:
def __init__(self, logging = True):
- self._environment = None
self._logging = logging
def _error(self, msg):
@@ -208,21 +207,25 @@ class commands:
return self.write_to_file(f, data)
+ # returns machine ID or empty string "" in case of error
+ def get_machine_id(self, no_error = True):
+ return self.read_file(consts.MACHINE_ID_FILE, no_error).strip()
+
# "no_errors" can be list of return codes not treated as errors, if 0 is in no_errors, it means any error
# returns (retcode, out), where retcode is exit code of the executed process or -errno if
# OSError or IOError exception happened
- def execute(self, args, shell = False, cwd = None, no_errors = [], return_err = False):
+ def execute(self, args, shell = False, cwd = None, env = {}, no_errors = [], return_err = False):
retcode = 0
- if self._environment is None:
- self._environment = os.environ.copy()
- self._environment["LC_ALL"] = "C"
+ _environment = os.environ.copy()
+ _environment["LC_ALL"] = "C"
+ _environment.update(env)
self._debug("Executing %s." % str(args))
out = ""
err_msg = None
try:
proc = Popen(args, stdout = PIPE, stderr = PIPE, \
- env = self._environment, \
+ env = _environment, \
shell = shell, cwd = cwd, \
close_fds = True, \
universal_newlines = True)
--
2.17.2