summaryrefslogtreecommitdiffstats
path: root/contrib/inventory/nagios_livestatus.py
blob: 91c72dac60674c891692673c4fe11b3098559a79 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
#!/usr/bin/env python

# (c) 2015, Yannig Perre <yannig.perre@gmail.com>
#
# This file is part of Ansible,
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.

'''
Nagios livestatus inventory script. Before using this script, please
update nagios_livestatus.ini file.

Livestatus is a nagios/naemon/shinken module which let you retrieve
informations stored in the monitoring core.

This plugin inventory need livestatus API for python. Please install it
before using this script (apt/pip/yum/...).

Checkmk livestatus: https://mathias-kettner.de/checkmk_livestatus.html
Livestatus API: http://www.naemon.org/documentation/usersguide/livestatus.html
'''

import os
import re
import argparse
import sys

try:
    import configparser
except ImportError:
    import ConfigParser
    configparser = ConfigParser
import json

try:
    from mk_livestatus import Socket
except ImportError:
    sys.exit("Error: mk_livestatus is needed. Try something like: pip install python-mk-livestatus")


class NagiosLivestatusInventory(object):

    def parse_ini_file(self):
        config = configparser.SafeConfigParser()
        config.read(os.path.dirname(os.path.realpath(__file__)) + '/nagios_livestatus.ini')
        for section in config.sections():
            if not config.has_option(section, 'livestatus_uri'):
                continue

            # If fields_to_retrieve is not set, using default fields
            fields_to_retrieve = self.default_fields_to_retrieve
            if config.has_option(section, 'fields_to_retrieve'):
                fields_to_retrieve = [field.strip() for field in config.get(section, 'fields_to_retrieve').split(',')]
                fields_to_retrieve = tuple(fields_to_retrieve)

            # default section values
            section_values = {
                'var_prefix': 'livestatus_',
                'host_filter': None,
                'host_field': 'name',
                'group_field': 'groups'
            }
            for key, value in section_values.items():
                if config.has_option(section, key):
                    section_values[key] = config.get(section, key).strip()

            # Retrieving livestatus string connection
            livestatus_uri = config.get(section, 'livestatus_uri')
            backend_definition = None

            # Local unix socket
            unix_match = re.match('unix:(.*)', livestatus_uri)
            if unix_match is not None:
                backend_definition = {'connection': unix_match.group(1)}

            # Remote tcp connection
            tcp_match = re.match('tcp:(.*):([^:]*)', livestatus_uri)
            if tcp_match is not None:
                backend_definition = {'connection': (tcp_match.group(1), int(tcp_match.group(2)))}

            # No valid livestatus_uri => exiting
            if backend_definition is None:
                raise Exception('livestatus_uri field is invalid (%s). Expected: unix:/path/to/live or tcp:host:port' % livestatus_uri)

            # Updating backend_definition with current value
            backend_definition['name'] = section
            backend_definition['fields'] = fields_to_retrieve
            for key, value in section_values.items():
                backend_definition[key] = value

            self.backends.append(backend_definition)

    def parse_options(self):
        parser = argparse.ArgumentParser()
        parser.add_argument('--host', nargs=1)
        parser.add_argument('--list', action='store_true')
        parser.add_argument('--pretty', action='store_true')
        self.options = parser.parse_args()

    def add_host(self, hostname, group):
        if group not in self.result:
            self.result[group] = {}
            self.result[group]['hosts'] = []
        if hostname not in self.result[group]['hosts']:
            self.result[group]['hosts'].append(hostname)

    def query_backend(self, backend, host=None):
        '''Query a livestatus backend'''
        hosts_request = Socket(backend['connection']).hosts.columns(backend['host_field'], backend['group_field'])

        if backend['host_filter'] is not None:
            hosts_request = hosts_request.filter(backend['host_filter'])

        if host is not None:
            hosts_request = hosts_request.filter('name = ' + host[0])

        hosts_request._columns += backend['fields']

        hosts = hosts_request.call()
        for host in hosts:
            hostname = host[backend['host_field']]
            hostgroups = host[backend['group_field']]
            if not isinstance(hostgroups, list):
                hostgroups = [hostgroups]
            self.add_host(hostname, 'all')
            self.add_host(hostname, backend['name'])
            for group in hostgroups:
                self.add_host(hostname, group)
            for field in backend['fields']:
                var_name = backend['var_prefix'] + field
                if hostname not in self.result['_meta']['hostvars']:
                    self.result['_meta']['hostvars'][hostname] = {}
                self.result['_meta']['hostvars'][hostname][var_name] = host[field]

    def __init__(self):

        self.defaultgroup = 'group_all'
        self.default_fields_to_retrieve = ('address', 'alias', 'display_name', 'childs', 'parents')
        self.backends = []
        self.options = None

        self.parse_ini_file()
        self.parse_options()

        self.result = {}
        self.result['_meta'] = {}
        self.result['_meta']['hostvars'] = {}
        self.json_indent = None
        if self.options.pretty:
            self.json_indent = 2

        if len(self.backends) == 0:
            sys.exit("Error: Livestatus configuration is missing. See nagios_livestatus.ini.")

        for backend in self.backends:
            self.query_backend(backend, self.options.host)

        if self.options.host:
            print(json.dumps(self.result['_meta']['hostvars'][self.options.host[0]], indent=self.json_indent))
        elif self.options.list:
            print(json.dumps(self.result, indent=self.json_indent))
        else:
            sys.exit("usage: --list or --host HOSTNAME [--pretty]")

NagiosLivestatusInventory()