Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: New grub2_editenv_list parser #3481

Merged
merged 13 commits into from
Aug 18, 2022
58 changes: 38 additions & 20 deletions insights/parsers/grubenv.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
"""
GrubEnv - file ``/boot/grub2/grubenv``
======================================
GRUB environment block
======================
This module provides processing for the GRUB environment block.
Parsers included are:

This parser reads the GRUB environment block file. The file is laid out in a
key=value format similar to an ini file except it doesn't have any headers.

The parser stores the key/value pairs in itself which inherits a dict. This
file is only used in Grub 2, but in RHEL8 with BLS being the default. There
were several variables added that are referenced in the
``/boot/loader/entries/*.conf`` files.
GrubEnv - command ``grub2-editenv list``
----------------------------------------
"""
from insights import get_active_lines, parser, Parser
from insights.parsers import SkipException
Expand All @@ -18,10 +16,26 @@
@parser(Specs.grubenv)
class GrubEnv(Parser, dict):
"""
Parses the /boot/grub2/grubenv file and returns a dict
of the grubenv variables.
This parser parses the output of the command ``grub2-editenv list``, which
list the current variables in GRUB environment block file or error message.
The module stores the key/value pairs in itself which inherits a dict.

Sample output of the file::
This parser used to handle the contents of the ``/boot/grub2/grubenv`` file
before. Since the command output without error messages is the same as the
content in ``/boot/grub2/grubenv``, and the ``grub2-editenv list`` command
output will show the error messages if the file is corrupt or has serious
errors in content, the current parser processes the ``grub2-editenv list``
command and returns a dict of the grubenv variables.

Cause the sosreports only collect the grubenv file and don't collect the
``grub2-editenv list`` command, the GrubEnv parser only handles the
``/boot/grub2/grubenv`` content for sos_archive.

``/boot/grub2/grubenv`` file is only used in Grub 2, but in RHEL8 with BLS
being the default. There were several variables added that are referenced in
the ``/boot/loader/entries/*.conf`` files.

Sample output of the command::

saved_entry=295e1ba1696e4fad9e062f096f92d147-4.18.0-305.el8.x86_64
kernelopts=root=/dev/mapper/root_vg-lv_root ro crashkernel=auto resume=/dev/mapper/root_vg-lv_swap rd.lvm.lv=root_vg/lv_root rd.lvm.lv=root_vg/lv_swap console=tty0 console=ttyS0,115200
Expand All @@ -30,12 +44,6 @@ class GrubEnv(Parser, dict):
tuned_params=transparent_hugepages=never
tuned_initrd=

Attributes:
has_kernelopts (bool): Returns True/False depending on if kernelopts key is in the dict.
kernelopts (bool): Returns the string of kernelopts from the dict.
has_tuned_params (str): Returns True/False depending of if the tuned_params key is in the dict.
tuned_params (str): Returns the string of tuned_params from the dict.

Examples:
>>> type(grubenv)
<class 'insights.parsers.grubenv.GrubEnv'>
Expand All @@ -50,43 +58,53 @@ class GrubEnv(Parser, dict):
>>> grubenv['saved_entry']
'295e1ba1696e4fad9e062f096f92d147-4.18.0-305.el8.x86_64'
"""

def __init__(self, context):
super(GrubEnv, self).__init__(context)

def parse_content(self, content):
if not content:
raise SkipException("Empty output.")

self._errors = []
data = dict()
for line in get_active_lines(content):
if "=" not in line:
self._errors.append(line)
continue

key, value = line.split("=", 1)

# Some keys can have empty values, so just skip them.
if not value:
continue

data[key] = value

if not data:
if (not data) and (not self._errors):
raise SkipException("No parsed data.")

self.update(data)

@property
def has_kernelopts(self):
""" bool: Returns True/False depending on if kernelopts key is in the dict. """
return "kernelopts" in self

@property
def kernelopts(self):
""" str: Returns the string of kernelopts from the dict. """
return self.get("kernelopts", "")

@property
def has_tuned_params(self):
""" bool: Returns True/False depending of if the tuned_params key is in the dict. """
return "tuned_params" in self

@property
def tuned_params(self):
""" str: Returns the string of tuned_params from the dict."""
return self.get("tuned_params", "")

@property
def errors(self):
""" list: Returns the list of error message. """
return self._errors
2 changes: 1 addition & 1 deletion insights/specs/default.py
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ class DefaultSpecs(Specs):
gcp_license_codes = simple_command("/usr/bin/curl -s -H 'Metadata-Flavor: Google' http://metadata.google.internal/computeMetadata/v1/instance/licenses/?recursive=True --connect-timeout 5", deps=[IsGCP])
greenboot_status = simple_command("/usr/libexec/greenboot/greenboot-status")
group_info = command_with_args("/usr/bin/getent group %s", user_group.group_filters)
grubenv = first_file(["/boot/grub2/grubenv", "/boot/efi/EFI/redhat/grubenv"])
grubenv = simple_command("/usr/bin/grub2-editenv list")
grub_conf = simple_file("/boot/grub/grub.conf")
grub_config_perms = simple_command("/bin/ls -lH /boot/grub2/grub.cfg") # only RHEL7 and updwards
grub_efi_conf = simple_file("/boot/efi/EFI/redhat/grub.conf")
Expand Down
1 change: 1 addition & 0 deletions insights/specs/insights_archive.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ class InsightsArchiveSpecs(Specs):
getconf_page_size = simple_file("insights_commands/getconf_PAGE_SIZE")
getenforce = simple_file("insights_commands/getenforce")
getsebool = simple_file("insights_commands/getsebool_-a")
grubenv = first_file(["insights_commands/grub2-editenv_list", "/boot/grub2/grubenv", "/boot/efi/EFI/redhat/grubenv"])
grub1_config_perms = first_file(["insights_commands/ls_-lH_.boot.grub.grub.conf", "insights_commands/ls_-l_.boot.grub.grub.conf"])
grub_config_perms = first_file(["insights_commands/ls_-lH_.boot.grub2.grub.cfg", "insights_commands/ls_-l_.boot.grub2.grub.cfg"])
grubby_default_index = simple_file("insights_commands/grubby_--default-index")
Expand Down
1 change: 1 addition & 0 deletions insights/specs/sos_archive.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ class SosSpecs(Specs):
gluster_v_info = simple_file("sos_commands/gluster/gluster_volume_info")
gluster_v_status = simple_file("sos_commands/gluster/gluster_volume_status")
gluster_peer_status = simple_file("sos_commands/gluster/gluster_peer_status")
grubenv = first_file(["/boot/grub2/grubenv", "/boot/efi/EFI/redhat/grubenv"])
heat_engine_log = first_file(["/var/log/containers/heat/heat-engine.log", "/var/log/heat/heat-engine.log"])
hostname = first_file(["sos_commands/general/hostname_-f", "sos_commands/host/hostname_-f"])
hostname_default = first_file(["sos_commands/general/hostname", "sos_commands/host/hostname", "/etc/hostname", "hostname"])
Expand Down
16 changes: 10 additions & 6 deletions insights/tests/parsers/test_grubenv.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,31 +6,29 @@


GRUBENV_WITH_TUNED_PARAMS = """
# GRUB Environment Block
saved_entry=295e1ba1696e4fad9e062f096f92d147-4.18.0-305.el8.x86_64
kernelopts=root=/dev/mapper/root_vg-lv_root ro crashkernel=auto resume=/dev/mapper/root_vg-lv_swap rd.lvm.lv=root_vg/lv_root rd.lvm.lv=root_vg/lv_swap console=tty0 console=ttyS0,115200
boot_success=0
boot_indeterminate=2
tuned_params=transparent_hugepages=never
tuned_initrd=
###############################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################
""".strip() # noqa

GRUBENV_WITHOUT_TUNED_PARAMS = """
# GRUB Environment Block
saved_entry=295e1ba1696e4fad9e062f096f92d147-4.18.0-305.el8.x86_64
kernelopts=root=/dev/mapper/root_vg-lv_root ro crashkernel=auto resume=/dev/mapper/root_vg-lv_swap rd.lvm.lv=root_vg/lv_root rd.lvm.lv=root_vg/lv_swap console=tty0 console=ttyS0,115200
boot_success=0
boot_indeterminate=2
###############################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################
""".strip() # noqa

GRUBENV_RHEL7 = """
# GRUB Environment Block
saved_entry=Red Hat Enterprise Linux Server (3.10.0-1127.el7.x86_64) 7.8 (Maipo)
######################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################
""".strip() # noqa

GRUBEDITENVLIST_ERROR = """
grub2-editenv: error: invalid environment block.
""".strip()


def test_doc_examples():
env = {
Expand Down Expand Up @@ -77,3 +75,9 @@ def test_r7():
def test_skip():
skip_exception_check(grubenv.GrubEnv, output_str="# test")
skip_exception_check(grubenv.GrubEnv)


def test_error():
results = grubenv.GrubEnv(context_wrap(GRUBEDITENVLIST_ERROR))
assert results is not None
assert results.errors == ["grub2-editenv: error: invalid environment block."]