Search code examples
pythonregexnagiosicinga

Parse Nagios / Icinga Config with Python Regex


I'm trying to parse a Nagios / Icinga config so I can do further processing on it with Python. Since I could not find a working library to do that (pynag does not seem to work at all), I'm trying to write a simple Python script using regexes to do so.

Basically I want to get from this configfile (it uses tabs for indentation):

define host {
    address 123.123.123.123
    passive_checks_enabled  1
    }

define service {
    service_description Crondaemon
    check_command   check_nrpe_1arg!check_crondaemon
    }

to something like this Python tuple:

(
 ('host', ('address', '123.123.123.123'), ('passive_checks_enabled', '1')), 
 ('service', ('service_description', 'Crondaemon'), ('check_command', 'check_nrpe_1arg!check_crondaemon'))
)

This is my full script with parsing logic including an example to test:

import re

# white spaces are tabs!
TEST_STR = """
define host {
    address 123.123.123.123
    passive_checks_enabled  1
    }

define service {
    service_description Crondaemon
    check_command   check_nrpe_1arg!check_crondaemon
    }
"""

cfg_all_regex = re.compile(
    r'define\s+(\w+)\s*\{'
    '(.*?)'
    '\t}',
    re.DOTALL
)
# basic regex works
print(re.findall(cfg_all_regex, TEST_STR))

cfg_all_regex = re.compile(
    r'define\s+(\w+)\s*{\n'
    '(\t(.*)?\t(.*)?\n)*'
    '\t}',
    re.DOTALL
)
# more specific regex to extract all key values fails
print(re.findall(cfg_all_regex, TEST_STR))

Unfortunately I cannot get the full parsing to work, it always matches everything or nothing. Can you please give me a hint how to fix my regex so I can extract all key value pairs from my Icinga config?


Solution

  • re module doesn't support repeated captures, so

    '(\t(.*)?\t(.*)?\n)*'
    

    only preserves last group capture.

    Likewise I would transform this like that

    '\t(\w+)\s+([^\n]*)\n\'
    

    So a possible solution, given the structure of your data, can be creates a regular expression that will match either pattern:

    regex = r'define\s+(\w+)\s+\{\n|\t(\w+)\s+([^\n]*)\n|\t\}'
    matches = re.finditer(regex, TEST_STR, re.DOTALL)
    

    With a for loop you can iterate over the groups

    for match in matches:
        for groupNum in range(0, len(match.groups())):
            groupNum = groupNum + 1
            if match.group(groupNum):
                print("Group {}: {}".format(groupNum, match.group(groupNum)))
    

    return:

    Group 1: host
    Group 2: address
    Group 3: 123.123.123.123
    Group 2: passive_checks_enabled
    Group 3: 1
    Group 1: service
    Group 2: service_description
    Group 3: Crondaemon
    Group 2: check_command
    Group 3: check_nrpe_1arg!check_crondaemon