Skip to content

Instantly share code, notes, and snippets.

@sickel
Forked from ravnx/status2json.py
Last active February 14, 2025 12:29
Show Gist options
  • Save sickel/de8997122203ab3946f4ae43b45e021d to your computer and use it in GitHub Desktop.
Save sickel/de8997122203ab3946f4ae43b45e021d to your computer and use it in GitHub Desktop.
Parse the Nagios status.dat and print out lines to be read by postgresql file_fdw
#!/usr/bin/python3
"""
This script reads the nagios status.dat file and exports it as a csv-file
the output can be read directly by postgresql file_fdw foreign data wrapper.
To set this up, make sure fdw_file is ready to use and define the following table:
create foreign table nagiosstatus(
statustype text,
hostname text,
service text,
time integer,
comment text,
author text,
state integer,
statuschange integer,
statusinfo text,
acknowledged integer,
jsondata jsonb)
server nagiosstatus options(program '/home/nagios/vaktside/vakt/status2json.py');
I have selected those fields I need for our day to day work to be stored as field, all the
information is in the jsondata field and can be queried using the json query syntax. If
other fields are needed, the definition of the table can be changed, but then the lines for
printing out the data must also be changed.
the nagiosstatus server must be defined, cf documentation for fdw_file.
the program option must point to wherever this status2json.py script is installed-
The table can be queried directly, but if it is queried often, e.g. from
various grafana dashboards, it can be cached in a materialized view:
create materialized view nagiosstatus_cached as
select statustype, hostname, service, time, comment, author , state, statuschange,
statusinfo, acknowledged, jsondata
from nagiosstatus;
A materialized view will be stored as a table and needs to be updated through
refresh materialized view nagiosstatus_cached ;
It can be set up in crontab like this, given that the user can log on postgresql as the owner
of the materialized view without giving a password (e.g. using .pgpass)
*/2 * * * * /usr/bin/psql nagios -c 'refresh materialized view nagiosstatus_cached' > /dev/null
Forked from @alexwright
"""
import re
import json
import sys
STATUS_FILE_PATH = "/var/lib/nagios4/status.dat"
STATUS_FILE_PATH = "/usr/local/nagios/var/status.dat"
def read_status():
hosts = {}
hostcomments = []
servicecomments = []
translation = str.maketrans({'"': r'\"',chr(9):' '})
try:
fh = open(STATUS_FILE_PATH)
status_raw = fh.read()
except FileNotFoundError:
print(f'Cannot open {STATUS_FILE_PATH} - exiting')
sys.exit(2)
pattern = re.compile('([\w]+)\s+\{([\S\s]*?)\}',re.DOTALL)
matches = pattern.findall(status_raw)
for def_type, data in matches:
lines = [line.strip() for line in data.split("\n")]
# With the final json, there cannot be unescaped " in the string
lines = [line.translate(translation) for line in lines]
pairs = [line.split("=", 1) for line in lines if line != '']
data = dict(pairs)
if def_type == "servicestatus":
if 'host_name' in data:
hosts[data['host_name']]['services'][data['service_description']]=data
if def_type == "hoststatus":
data['services'] = {}
data['comments'] = []
hosts[data['host_name']] = data
if def_type == "hostcomment":
hostcomments.append(data)
if 'host_name' in data:
hosts[data['host_name']]['comments'].append(data)
if def_type == "servicecomment":
servicecomments.append(data)
if 'host_name' in data:
hosts[data['host_name']]['comments'].append(data)
return {
'hosts': hosts,
'servicecomments': servicecomments,
'hostcomments': hostcomments,
}
if __name__ == "__main__":
data = read_status()
"""
Output to be read by fdw_file.
Fields:
statustype,
host,
service, may be null
time, (entrytime or checktime)
comment, (for comment)
author, (for comment)
state,
problem start,
status information,
last change
plugin output
acked
jsondata
"""
comment = ''
author = ''
datatype = 'host'
for hostname in data['hosts']:
info = data['hosts'][hostname]
if 'services' in info:
datatype = 'service'
for servicename in info['services']:
service = info['services'][servicename]
jsondata = json.dumps(service)
print(f"{datatype}\t{hostname}\t{servicename}\t{service['last_check']}\t{comment}\t{author}\t\
{service['current_state']}\t{service['last_time_ok']}\t{service['plugin_output']}\t\
{service['problem_has_been_acknowledged']}\t{jsondata}")
# Do not want to keep the service info here.
del info['services']
servicename = ''
if 'comments' in info:
datatype = 'comment'
for comment in info['comments']:
hostname = comment['host_name']
if 'service_description' in comment:
servicename = comment['service_description']
else:
servicename = ''
author = comment['author']
commenttext = comment['comment_data']
jsondata = json.dumps(comment)
time = comment['entry_time'].strip()
state = ''
print(f"{datatype}\t{hostname}\t{servicename}\t{time}\t{commenttext}\t{author}\t-1\t-1\t\t-1\t{jsondata}")
del info['comments']
comment = ''
datatype = 'host'
jsondata = json.dumps(info)
print(f"{datatype}\t{hostname}\t{servicename}\t{info['last_check']}\t{comment}\t{author}\t\
{info['current_state']}\t{info['last_time_up']}\t{info['plugin_output']}\t\
{info['problem_has_been_acknowledged']}\t{jsondata}")
#sys.exit()
"""
for servicename in data['services']:
info = data['services'][servicename]
info['check_command'] = info['check_command'].replace('"',"'")
jsondata = json.dumps(info)
print(f"{datatype}\t{data['host_name']}\t{servicename}\t{info['last_check']}\t{comment}\t{author}\t\
{info['current_state']}\t{info['last_state_change']}\t{info['plugin_output']}\t\
{info['problem_has_been_acknowledged']}\t{jsondata}")
"""
@sickel
Copy link
Author

sickel commented Feb 14, 2025

At the moment this is working. It makes no sense to build up the data structure that is needed for the json just to print out lines, so each status object could be printed out directly. I may rewrite it one day, or I may keep it as is since it is easy to change it back for json output.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment