Skip to content

Commit

Permalink
Add bootloader_settings module to configure settings instead of tasks
Browse files Browse the repository at this point in the history
  • Loading branch information
spetrosi committed Nov 23, 2023
1 parent 02248ab commit 50551a7
Show file tree
Hide file tree
Showing 8 changed files with 217 additions and 107 deletions.
12 changes: 5 additions & 7 deletions library/bootloader_facts.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
# Copyright: (c) 2023, Sergei Petrosian <[email protected]>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function

__metaclass__ = type

DOCUMENTATION = r"""
Expand Down Expand Up @@ -47,23 +48,20 @@ def run_module():
# supports check mode
module = AnsibleModule(argument_spec=module_args, supports_check_mode=True)

# Keys from `grubby --info=ALL`, values for which this module returns
grubby_keys = ["args", "id", "index", "initrd", "kernel", "root", "title"]

rc, stdout, stderr = module.run_command("grubby --info=ALL")
if "Permission denied" in stderr:
module.fail_json(msg="You must run this as sudo", **result)
stdout_lines = stdout.strip().split('\n')
stdout_lines = stdout.strip().split("\n")
kernels = []
index_count = 0
for line in stdout_lines:
if re.search("index=\d+", line):
if re.search(r"index=\d+", line):
index_count += 1
kernels.append({})
search = re.search("(.*?)=(.*)", line)
search = re.search(r"(.*?)=(.*)", line)
key = search.group(1).strip('"')
value = search.group(2).strip('"')
kernels[index_count -1].update({key: value})
kernels[index_count - 1].update({key: value})
result["ansible_facts"]["bootloader_facts"] = kernels

# in the event of a successful module execution, you will want to
Expand Down
203 changes: 203 additions & 0 deletions library/bootloader_settings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
#!/usr/bin/python

# Copyright: (c) 2023, Sergei Petrosian <[email protected]>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function

__metaclass__ = type

DOCUMENTATION = r"""
---
module: bootloader_settings
short_description: Configure grubby boot loader arguments for specified kernels
version_added: "0.0.1"
description:
- "WARNING: Do not use this module directly! It is only for role internal use."
- Configure grubby boot loader arguments for specified kernels
options:
bootloader_settings:
description: List of kernels and their command line parameters that you want to set.
required: true
type: list
author:
- Sergei Petrosian (@spetrosi)
"""

EXAMPLES = r"""
- name: Test with a message
bootloader_settings:
bootloader_settings:
"""

RETURN = r"""
# These are examples of possible return values, and in general should use other names for return values.
# original_message:
# description: The original name param that was passed in.
# type: str
# returned: always
# sample: 'hello world'
# message:
# description: The output message that the test module generates.
# type: str
# returned: always
# sample: 'goodbye'
"""

import re

from ansible.module_utils.basic import AnsibleModule

# import ansible.module_utils.six as ansible_six
import ansible.module_utils.six.moves as ansible_six_moves


def get_grubby_args(kernel):
module_args = dict(bootloader_settings=dict(type="list", required=True))
module = AnsibleModule(argument_spec=module_args, supports_check_mode=True)
rc, kernel_info, stderr = module.run_command("grubby --info=" + kernel)
# kernel_info = "index=0\nkernel=\"/boot/vmlinuz-5.14.0-386.el9.x86_64\"\nargs=\"console=tty0 console=ttyS0,115200n8 no_timer_check net.ifnames=0 crashkernel=1G-4G:192M,4G-64G:256M,64G-:512M $tuned_params\"\nroot=\"UUID=b4f467ad-fe53-4523-af54-30fcad90aacc\"\ninitrd=\"/boot/initramfs-5.14.0-386.el9.x86_64.img $tuned_initrd\"\ntitle=\"Red Hat Enterprise Linux (5.14.0-386.el9.x86_64) 9.4 (Plow)\"\nid=\"ffffffffffffffffffffffffffffffff-5.14.0-386.el9.x86_64\"\n"
return re.search(r'args="(.*)"', kernel_info).group(1)


def escapeval(val):
"""make sure val is quoted as in shell"""
return ansible_six_moves.shlex_quote(str(val))


def run_module():
# define available arguments/parameters a user can pass to the module
module_args = dict(bootloader_settings=dict(type="list", required=True))

# seed the result dict in the object
# we primarily care about changed and state
# changed is if this module effectively modified the target
# state will include any data that you want your module to pass back
# for consumption, for example, in a subsequent task
result = dict(
changed=False,
)

# the AnsibleModule object will be our abstraction working with Ansible
# this includes instantiation, a couple of common attr would be the
# args/params passed to the execution, as well as if the module
# supports check mode
module = AnsibleModule(argument_spec=module_args, supports_check_mode=True)

kernels = []
# kernels_keys = ["kernel_index", "kernel_path", "kernel_title"]
for bootloader_setting in module.params["bootloader_settings"]:
# if type(bootloader_setting["kernel"]) is not str:
# bootloader_kernel = bootloader_setting["kernel"]
# for kernel_key in kernels_keys:
# kernel_key_list = []
# if kernel_key in bootloader_setting["kernel"]:
# if type(bootloader_setting["kernel"][kernel_key]) is list:
# for kernel_val in bootloader_setting["kernel"][kernel_key]:
# kernel_key_list.append(
# {"name": escapeval(kernel_val)}
# )
# kernel_key_list.append(
# {"options": bootloader_setting["options"]}
# )
# else:
# kernel_key_list.append(
# {"name": escapeval(bootloader_setting["kernel"][kernel_key])}
# )
# kernel_key_list.append(
# {"options": bootloader_setting["options"]}
# )
# kernels.append({kernel_key: kernel_key_list})
# else:
# kernels.append({bootloader_setting["kernel"]: bootloader_setting["options"]})

if "kernel_path" in bootloader_setting["kernel"]:
kernels = bootloader_setting["kernel"]["kernel_path"]
elif "kernel_index" in bootloader_setting["kernel"]:
kernels = bootloader_setting["kernel"]["kernel_index"]
elif "kernel_title" in bootloader_setting["kernel"]:
if isinstance(bootloader_setting["kernel"], str):
kernels = "TITLE=" + bootloader_setting["kernel"]["kernel_title"]
else:
for kernel_title in bootloader_setting["kernel"]["kernel_title"]:
kernels.append(
"TITLE=" + bootloader_setting["kernel"]["kernel_title"]
)
elif (
bootloader_setting["kernel"] == "ALL"
or bootloader_setting["kernel"] == "DEFAULT"
):
kernels = bootloader_setting["kernel"]
else:
module.fail_json(
msg='bootloader_settings.kernel must contain one of kernel_path, kernel_index, kernel_title, "ALL", "DEFAULT"',
**result
)
if not isinstance(kernels, list):
kernels = [kernels]

for kernel in kernels:
kernel = escapeval(kernel)
# Remove all existing boot settings
if {"previous": "replaced"} in bootloader_setting["options"]:
bootloader_args = get_grubby_args(kernel)
if len(bootloader_args) > 0:
rc, stdout, stderr = module.run_command(
"grubby --update-kernel="
+ kernel
+ " --remove-args="
+ escapeval(bootloader_args)
)
# result['remove_args_cmd'] = "grubby --update-kernel=" + kernel + " --remove-args=" + escapeval(bootloader_args)
result["changed"] = True
# Configure boot settings
bootloader_absent_args = ""
bootloader_present_args = ""
bootloader_mod_args = ""
for kernel_setting in bootloader_setting["options"]:
if {"previous": "replaced"} == kernel_setting:
continue
if "value" in kernel_setting:
setting_name = (
kernel_setting["name"] + "=" + str(kernel_setting["value"])
)
else:
setting_name = kernel_setting["name"]
if "state" in kernel_setting and kernel_setting["state"] == "absent":
bootloader_absent_args += setting_name + " "
else:
bootloader_present_args += setting_name + " "
if len(bootloader_absent_args) > 0:
bootloader_mod_args = " --remove-args=" + escapeval(
bootloader_absent_args
)
if len(bootloader_present_args) > 0:
bootloader_mod_args += " --args=" + escapeval(bootloader_present_args)
if len(bootloader_mod_args) > 0:
# result['mod_args_command'] = "grubby --update-kernel=" + kernel + bootloader_mod_args
rc, stdout, stderr = module.run_command(
"grubby --update-kernel=" + kernel + bootloader_mod_args
)
result["changed"] = True

# if the user is working with this module in only check mode we do not
# want to make any changes to the environment, just return the current
# state with no modifications
# if module.check_mode:
# module.exit_json(**result)

# in the event of a successful module execution, you will want to
# simple AnsibleModule.exit_json(), passing the key/value results
module.exit_json(**result)


def main():
run_module()


if __name__ == "__main__":
main()
7 changes: 0 additions & 7 deletions tasks/loop_kernels.yml

This file was deleted.

24 changes: 6 additions & 18 deletions tasks/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,12 @@
name: "{{ __bootloader_packages }}"
state: present

- name: Remove for previous replaced then modify boot settings
include_tasks: loop_kernels.yml
vars:
__bootloader_kernel_prep: >-
{%- if item.kernel.kernel_path is defined -%}
{{ item.kernel.kernel_path }}
{%- elif item.kernel.kernel_index is defined -%}
{{ item.kernel.kernel_index }}
{%- elif item.kernel.kernel_title is defined -%}
TITLE={{ item.kernel.kernel_title | quote }}
{%- elif item.kernel in ["ALL", "DEFAULT"] -%}
{{ item.kernel }}
{%- endif -%}
__bootloader_kernel: "{{
__bootloader_kernel_prep
if __bootloader_kernel_prep | type_debug == 'list' else
[__bootloader_kernel_prep] }}"
loop: "{{ bootloader_settings }}"
- name: Ensure boot loader settings
bootloader_settings:
bootloader_settings: "{{ bootloader_settings }}"
notify:
- Fix default kernel boot parameters
- Reboot system

- name: Update boot loader timeout configuration
lineinfile:
Expand Down
37 changes: 0 additions & 37 deletions tasks/modify_settings.yml

This file was deleted.

21 changes: 0 additions & 21 deletions tasks/remove_settings.yml

This file was deleted.

15 changes: 0 additions & 15 deletions tasks/rm_mod_settings.yml

This file was deleted.

5 changes: 3 additions & 2 deletions tests/tests_settings.yml
Original file line number Diff line number Diff line change
Expand Up @@ -137,8 +137,9 @@
- name: Verify settings
assert:
that: >-
(bootloader_facts | selectattr('index', 'search', '0') | first).args |
regex_search('^.*console=tty0 print-fatal-signals=1 no_timer_check( |)$')
(bootloader_facts | selectattr('index', 'search', '0') | first).args
| regex_search('^.*console=tty0 print-fatal-signals=1
no_timer_check( |)$')
- name: Verify boot loader timeout configuration
command: cat {{ __bootloader_grub_conf }}
Expand Down

0 comments on commit 50551a7

Please sign in to comment.