From 7b0efb7399b77c8f30397a683f860ad02c3f6c75 Mon Sep 17 00:00:00 2001
From: Thomas Bellman <bellman@lysator.liu.se>
Date: Wed, 24 Jun 2020 14:24:42 +0200
Subject: [PATCH] Implement full kernel option management for Grub 2.

Our old implementation of for managing kernel options under Grub 2,
could only remove kernel options, not set them, neither to some value
or as a "bareword" option.  This limitation was because I was using
the 'regexp_replace_lines' type from the cfgfile module to try to
modify the GRUB_CMDLINE_LINUX variable in /etc/default/grub, but that
is a very complex task when using regular expressions...

Turns out there is an Augeas lens that can handle shell variables
whose value is a list of words and assignments, exactly the format
that the GRUB_CMDLINE_LINUX variable has.  So use that in the
bootloader::grub2::kernel_option definition.
---
 manifests/grub2/kernel_option.pp | 42 ++++++++++++++++++++------------
 manifests/kernel_option.pp       |  4 ---
 2 files changed, 26 insertions(+), 20 deletions(-)

diff --git a/manifests/grub2/kernel_option.pp b/manifests/grub2/kernel_option.pp
index d0b13fd..3b775d5 100644
--- a/manifests/grub2/kernel_option.pp
+++ b/manifests/grub2/kernel_option.pp
@@ -1,4 +1,4 @@
-# Copyright © 2017   Thomas Bellman, Linköping, Sweden
+# Copyright © 2017-2022   Thomas Bellman, Linköping, Sweden
 # Licensed under the GNU LGPL v3+; see the README file for more information.
 
 
@@ -9,18 +9,20 @@ define bootloader::grub2::kernel_option($ensure, $value)
 {
     include bootloader::grub2::rebuild_grub_cfg
 
-    $prefix = '^(GRUB_CMDLINE_LINUX="?(|.*\s))'
-    $suffix = '((|\s.*)("?))\s*$'
+    $grub_defaults_file = '/etc/default/grub'
     $qname = regexp_quote($name)
+    # Augeas path expression to find nodes for the kernel parameter
+    $pathexpr = "GRUB_CMDLINE_LINUX/value[. =~ regexp('${qname}(=.*)?')]"
 
     if ($ensure == 'absent')
     {
-	regexp_replace_lines {
+	augeas {
 	    "bootloader::grub2::kernel_option::${name}":
-		file => '/etc/default/grub',
-		pattern => "${prefix}${qname}(=[^ \t\"]*)?${suffix}",
-		replacement => '\1\4',
-		notify => Class['bootloader::grub2::rebuild_grub_cfg'];
+		incl    => $grub_defaults_file,
+		lens    => 'Shellvars_list.lns',
+		context => "/files${grub_defaults_file}",
+		changes => "rm ${pathexpr}",
+		notify  => Class['bootloader::grub2::rebuild_grub_cfg'];
 	}
     }
     elsif ($ensure != 'present')
@@ -30,16 +32,24 @@ define bootloader::grub2::kernel_option($ensure, $value)
     }
     elsif ($value == true or $value == false)
     {
-	# Detecting if the option is already present is not easily done
-	# using regexps...
-	fail("Bootloader::Grub2::Kernel_option[${title}]: ",
-	     "Setting a kernel option is not yet implemented.")
+	augeas {
+	    "bootloader::grub2::kernel_option::${name}":
+		incl    => $grub_defaults_file,
+		lens    => 'Shellvars_list.lns',
+		context => "/files${grub_defaults_file}",
+		changes => "set ${pathexpr} '${name}'",
+		notify  => Class['bootloader::grub2::rebuild_grub_cfg'];
+	}
     }
     else
     {
-	# Detecting if the option is already present is not easily done
-	# using regexps...
-	fail("Bootloader::Grub2::Kernel_option[${title}]: ",
-	     "Setting a kernel option is not yet implemented.")
+	augeas {
+	    "bootloader::grub2::kernel_option::${name}":
+		incl    => $grub_defaults_file,
+		lens    => 'Shellvars_list.lns',
+		context => "/files${grub_defaults_file}",
+		changes => "set ${pathexpr} '${name}=${value}'",
+		notify  => Class['bootloader::grub2::rebuild_grub_cfg'];
+	}
     }
 }
diff --git a/manifests/kernel_option.pp b/manifests/kernel_option.pp
index f2ba642..a610f21 100644
--- a/manifests/kernel_option.pp
+++ b/manifests/kernel_option.pp
@@ -13,19 +13,15 @@
  *  - name	Kernel commandline option to set or remove.
  *
  *  - ensure	One of 'present' or 'absent'.
- *		Currently only 'absent' is implemented!
  *
  *  - value	The value to set the option to.  If set to true or false,
  *		it will be a "bare" option, i.e. something like "single".
  *		If a string, a valued option will be set, i.e. something
  *		like "console=ttyS0".
- *		Since only ensure => absent is implemented, the value
- *		parameter is currently ignored.
  *
  *  - provider	The bootloader in use.  Supported values are 'grub0' and
  *		'grub2'.  If not specified, the bootloader will be guessed
  *		based on the operating system.
- *		The grub2 provider currently only supports removing options.
  */
 define bootloader::kernel_option(
 	$ensure='present', $value=undef, $provider=undef)
-- 
GitLab