Diagnostic Walkthrough

A simple use-case for troubleshooting is the identification of installed software on the system. In this example, we will examine checking a system for the usage of bash based on data from the rpm command. This “walkthrough” will avoid going into details. Instead, it will simply lay out how the use-case could be handled using insights-core. More detailed tutorials can be found in the docs.

We’ll assume we have insights-core already installed following the instructions on the README.rst. Next we need to import the necessary modules.

import sys
sys.path.insert(0, "../..")
from insights import rule, make_fail, make_pass, run
from insights.parsers.installed_rpms import InstalledRpms

The first import line has the most common components used when creating rules. The rule decorator marks a function that encodes logic to be applied by the framework and the required or optional components it needs to execute. @rule decorated functions use make_fail or make_pass to return results. The run method executes the system, simplifying usage of insights-core for small, standalone scripts and from the python interpreter.

We also import the InstalledRpms parser. This is a class that structures the results of the rpm -qa command.

Next, we create our “rule” function.

def report(rpms):
    rpm = rpms.get_max("bash")
    if rpm:
        return make_pass("BASH_INSTALLED", version=rpm.nvr)
    return make_fail("BASH_INSTALLED")

Here, the report method will let us know if the bash package is installed, and if so, the latest version encountered. The name of the function isn’t important. The @rule decorator defines the report function as a “rule” component type and indicates that it depends on the InstalledRpms parser. This parser will be passed into the function as the first argument.

The rest of the report function is fairly easy to understand, noting that the get_max function returns the maximum version encountered of the package specified, or None if the package is not found.

Let’s try running this function using the run method.

results = run(report)
{'pass_key': 'BASH_INSTALLED',
 'type': 'pass',
 'version': u'bash-4.4.23-1.fc28'}

The run command executed the framework collecting rpm information from my system, parsing it using the InstalledRpms class, and then running the report function. It found that bash was installed.

The results are keyed by function (report in this case). Multiple functions can be executed, each with its own response.

The InstalledRpms class has structured the results of the rpm -qa command, parsing the rows from the command output. That is, each package NVR is separated into its own fields. One consequence of this is that the package name is distinct. When we look for bash, the parser doesn’t match, for example, bash-completion (also on my system.) It also means the version information is understood. So, we can do things like check a range of versions.

First, let’s define our range using the bash NVRs we care about. We’ll imagine there’s a particular bug that affects bash starting in 4.4.16-1 and is fixed in 4.4.22-1.

from insights.parsers.installed_rpms import InstalledRpm

lower = InstalledRpm.from_package("bash-4.4.16-1.fc27")
upper = InstalledRpm.from_package("bash-4.4.22-1.fc27")

Now, we’ll modify the report function to check ranges.

def report(rpms):
    rpm = rpms.get_max("bash")
    if rpm and rpm >= lower and rpm < upper:
        return make_fail("BASH_AFFECTED", version=rpm.nvr)
    elif rpm:
        return make_pass("BASH_AFFECTED", version=rpm.nvr)
        return make_pass("NO_BASH")

Now we can run this as before.

results = run(report)
{'pass_key': 'BASH_AFFECTED', 'type': 'pass', 'version': u'bash-4.4.23-1.fc28'}

A few notes about this example:

  • The code here could be packaged up in a script, along with other rules, to be easily reused.

  • The rule can be executed against a live host, sosreport, Red Hat Insights archive, or a directory formed from an expanded archive.

  • While we defined only a rule, we could also define other components like the command to be run and a parser to structure the content. The stand_alone.py is a simple example containing these three components.

The code above, (and this notebook) can be executed if insights-core (and jupyter-notebook) is installed. So feel free to run and experiment with the example.