~1chb1n/charms/trusty/ceph-osd/15.10-stable-flip-tests-helper-syncs

« back to all changes in this revision

Viewing changes to hooks/charmhelpers/contrib/charmsupport/nrpe.py

  • Committer: Liam Young
  • Date: 2015-01-12 17:24:51 UTC
  • mfrom: (31.1.8 ceph-osd)
  • Revision ID: liam.young@canonical.com-20150112172451-tcckwobla60wsyls
[gnuoy,r=james-page] Add support for nrpe

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
"""Compatibility with the nrpe-external-master charm"""
 
2
# Copyright 2012 Canonical Ltd.
 
3
#
 
4
# Authors:
 
5
#  Matthew Wedgwood <matthew.wedgwood@canonical.com>
 
6
 
 
7
import subprocess
 
8
import pwd
 
9
import grp
 
10
import os
 
11
import re
 
12
import shlex
 
13
import yaml
 
14
 
 
15
from charmhelpers.core.hookenv import (
 
16
    config,
 
17
    local_unit,
 
18
    log,
 
19
    relation_ids,
 
20
    relation_set,
 
21
    relations_of_type,
 
22
)
 
23
 
 
24
from charmhelpers.core.host import service
 
25
 
 
26
# This module adds compatibility with the nrpe-external-master and plain nrpe
 
27
# subordinate charms. To use it in your charm:
 
28
#
 
29
# 1. Update metadata.yaml
 
30
#
 
31
#   provides:
 
32
#     (...)
 
33
#     nrpe-external-master:
 
34
#       interface: nrpe-external-master
 
35
#       scope: container
 
36
#
 
37
#   and/or
 
38
#
 
39
#   provides:
 
40
#     (...)
 
41
#     local-monitors:
 
42
#       interface: local-monitors
 
43
#       scope: container
 
44
 
 
45
#
 
46
# 2. Add the following to config.yaml
 
47
#
 
48
#    nagios_context:
 
49
#      default: "juju"
 
50
#      type: string
 
51
#      description: |
 
52
#        Used by the nrpe subordinate charms.
 
53
#        A string that will be prepended to instance name to set the host name
 
54
#        in nagios. So for instance the hostname would be something like:
 
55
#            juju-myservice-0
 
56
#        If you're running multiple environments with the same services in them
 
57
#        this allows you to differentiate between them.
 
58
#    nagios_servicegroups:
 
59
#      default: ""
 
60
#      type: string
 
61
#      description: |
 
62
#        A comma-separated list of nagios servicegroups.
 
63
#        If left empty, the nagios_context will be used as the servicegroup
 
64
#
 
65
# 3. Add custom checks (Nagios plugins) to files/nrpe-external-master
 
66
#
 
67
# 4. Update your hooks.py with something like this:
 
68
#
 
69
#    from charmsupport.nrpe import NRPE
 
70
#    (...)
 
71
#    def update_nrpe_config():
 
72
#        nrpe_compat = NRPE()
 
73
#        nrpe_compat.add_check(
 
74
#            shortname = "myservice",
 
75
#            description = "Check MyService",
 
76
#            check_cmd = "check_http -w 2 -c 10 http://localhost"
 
77
#            )
 
78
#        nrpe_compat.add_check(
 
79
#            "myservice_other",
 
80
#            "Check for widget failures",
 
81
#            check_cmd = "/srv/myapp/scripts/widget_check"
 
82
#            )
 
83
#        nrpe_compat.write()
 
84
#
 
85
#    def config_changed():
 
86
#        (...)
 
87
#        update_nrpe_config()
 
88
#
 
89
#    def nrpe_external_master_relation_changed():
 
90
#        update_nrpe_config()
 
91
#
 
92
#    def local_monitors_relation_changed():
 
93
#        update_nrpe_config()
 
94
#
 
95
# 5. ln -s hooks.py nrpe-external-master-relation-changed
 
96
#    ln -s hooks.py local-monitors-relation-changed
 
97
 
 
98
 
 
99
class CheckException(Exception):
 
100
    pass
 
101
 
 
102
 
 
103
class Check(object):
 
104
    shortname_re = '[A-Za-z0-9-_]+$'
 
105
    service_template = ("""
 
106
#---------------------------------------------------
 
107
# This file is Juju managed
 
108
#---------------------------------------------------
 
109
define service {{
 
110
    use                             active-service
 
111
    host_name                       {nagios_hostname}
 
112
    service_description             {nagios_hostname}[{shortname}] """
 
113
                        """{description}
 
114
    check_command                   check_nrpe!{command}
 
115
    servicegroups                   {nagios_servicegroup}
 
116
}}
 
117
""")
 
118
 
 
119
    def __init__(self, shortname, description, check_cmd):
 
120
        super(Check, self).__init__()
 
121
        # XXX: could be better to calculate this from the service name
 
122
        if not re.match(self.shortname_re, shortname):
 
123
            raise CheckException("shortname must match {}".format(
 
124
                Check.shortname_re))
 
125
        self.shortname = shortname
 
126
        self.command = "check_{}".format(shortname)
 
127
        # Note: a set of invalid characters is defined by the
 
128
        # Nagios server config
 
129
        # The default is: illegal_object_name_chars=`~!$%^&*"|'<>?,()=
 
130
        self.description = description
 
131
        self.check_cmd = self._locate_cmd(check_cmd)
 
132
 
 
133
    def _locate_cmd(self, check_cmd):
 
134
        search_path = (
 
135
            '/usr/lib/nagios/plugins',
 
136
            '/usr/local/lib/nagios/plugins',
 
137
        )
 
138
        parts = shlex.split(check_cmd)
 
139
        for path in search_path:
 
140
            if os.path.exists(os.path.join(path, parts[0])):
 
141
                command = os.path.join(path, parts[0])
 
142
                if len(parts) > 1:
 
143
                    command += " " + " ".join(parts[1:])
 
144
                return command
 
145
        log('Check command not found: {}'.format(parts[0]))
 
146
        return ''
 
147
 
 
148
    def write(self, nagios_context, hostname, nagios_servicegroups=None):
 
149
        nrpe_check_file = '/etc/nagios/nrpe.d/{}.cfg'.format(
 
150
            self.command)
 
151
        with open(nrpe_check_file, 'w') as nrpe_check_config:
 
152
            nrpe_check_config.write("# check {}\n".format(self.shortname))
 
153
            nrpe_check_config.write("command[{}]={}\n".format(
 
154
                self.command, self.check_cmd))
 
155
 
 
156
        if not os.path.exists(NRPE.nagios_exportdir):
 
157
            log('Not writing service config as {} is not accessible'.format(
 
158
                NRPE.nagios_exportdir))
 
159
        else:
 
160
            self.write_service_config(nagios_context, hostname,
 
161
                                      nagios_servicegroups)
 
162
 
 
163
    def write_service_config(self, nagios_context, hostname,
 
164
                             nagios_servicegroups=None):
 
165
        for f in os.listdir(NRPE.nagios_exportdir):
 
166
            if re.search('.*{}.cfg'.format(self.command), f):
 
167
                os.remove(os.path.join(NRPE.nagios_exportdir, f))
 
168
 
 
169
        if not nagios_servicegroups:
 
170
            nagios_servicegroups = nagios_context
 
171
 
 
172
        templ_vars = {
 
173
            'nagios_hostname': hostname,
 
174
            'nagios_servicegroup': nagios_servicegroups,
 
175
            'description': self.description,
 
176
            'shortname': self.shortname,
 
177
            'command': self.command,
 
178
        }
 
179
        nrpe_service_text = Check.service_template.format(**templ_vars)
 
180
        nrpe_service_file = '{}/service__{}_{}.cfg'.format(
 
181
            NRPE.nagios_exportdir, hostname, self.command)
 
182
        with open(nrpe_service_file, 'w') as nrpe_service_config:
 
183
            nrpe_service_config.write(str(nrpe_service_text))
 
184
 
 
185
    def run(self):
 
186
        subprocess.call(self.check_cmd)
 
187
 
 
188
 
 
189
class NRPE(object):
 
190
    nagios_logdir = '/var/log/nagios'
 
191
    nagios_exportdir = '/var/lib/nagios/export'
 
192
    nrpe_confdir = '/etc/nagios/nrpe.d'
 
193
 
 
194
    def __init__(self, hostname=None):
 
195
        super(NRPE, self).__init__()
 
196
        self.config = config()
 
197
        self.nagios_context = self.config['nagios_context']
 
198
        if 'nagios_servicegroups' in self.config:
 
199
            self.nagios_servicegroups = self.config['nagios_servicegroups']
 
200
        else:
 
201
            self.nagios_servicegroups = 'juju'
 
202
        self.unit_name = local_unit().replace('/', '-')
 
203
        if hostname:
 
204
            self.hostname = hostname
 
205
        else:
 
206
            self.hostname = "{}-{}".format(self.nagios_context, self.unit_name)
 
207
        self.checks = []
 
208
 
 
209
    def add_check(self, *args, **kwargs):
 
210
        self.checks.append(Check(*args, **kwargs))
 
211
 
 
212
    def write(self):
 
213
        try:
 
214
            nagios_uid = pwd.getpwnam('nagios').pw_uid
 
215
            nagios_gid = grp.getgrnam('nagios').gr_gid
 
216
        except:
 
217
            log("Nagios user not set up, nrpe checks not updated")
 
218
            return
 
219
 
 
220
        if not os.path.exists(NRPE.nagios_logdir):
 
221
            os.mkdir(NRPE.nagios_logdir)
 
222
            os.chown(NRPE.nagios_logdir, nagios_uid, nagios_gid)
 
223
 
 
224
        nrpe_monitors = {}
 
225
        monitors = {"monitors": {"remote": {"nrpe": nrpe_monitors}}}
 
226
        for nrpecheck in self.checks:
 
227
            nrpecheck.write(self.nagios_context, self.hostname,
 
228
                            self.nagios_servicegroups)
 
229
            nrpe_monitors[nrpecheck.shortname] = {
 
230
                "command": nrpecheck.command,
 
231
            }
 
232
 
 
233
        service('restart', 'nagios-nrpe-server')
 
234
 
 
235
        for rid in relation_ids("local-monitors"):
 
236
            relation_set(relation_id=rid, monitors=yaml.dump(monitors))
 
237
 
 
238
 
 
239
def get_nagios_hostcontext(relation_name='nrpe-external-master'):
 
240
    """
 
241
    Query relation with nrpe subordinate, return the nagios_host_context
 
242
 
 
243
    :param str relation_name: Name of relation nrpe sub joined to
 
244
    """
 
245
    for rel in relations_of_type(relation_name):
 
246
        if 'nagios_hostname' in rel:
 
247
            return rel['nagios_host_context']
 
248
 
 
249
 
 
250
def get_nagios_hostname(relation_name='nrpe-external-master'):
 
251
    """
 
252
    Query relation with nrpe subordinate, return the nagios_hostname
 
253
 
 
254
    :param str relation_name: Name of relation nrpe sub joined to
 
255
    """
 
256
    for rel in relations_of_type(relation_name):
 
257
        if 'nagios_hostname' in rel:
 
258
            return rel['nagios_hostname']
 
259
 
 
260
 
 
261
def get_nagios_unit_name(relation_name='nrpe-external-master'):
 
262
    """
 
263
    Return the nagios unit name prepended with host_context if needed
 
264
 
 
265
    :param str relation_name: Name of relation nrpe sub joined to
 
266
    """
 
267
    host_context = get_nagios_hostcontext(relation_name)
 
268
    if host_context:
 
269
        unit = "%s:%s" % (host_context, local_unit())
 
270
    else:
 
271
        unit = local_unit()
 
272
    return unit
 
273
 
 
274
 
 
275
def add_init_service_checks(nrpe, services, unit_name):
 
276
    """
 
277
    Add checks for each service in list
 
278
 
 
279
    :param NRPE nrpe: NRPE object to add check to
 
280
    :param list services: List of services to check
 
281
    :param str unit_name: Unit name to use in check description
 
282
    """
 
283
    for svc in services:
 
284
        upstart_init = '/etc/init/%s.conf' % svc
 
285
        sysv_init = '/etc/init.d/%s' % svc
 
286
        if os.path.exists(upstart_init):
 
287
            nrpe.add_check(
 
288
                shortname=svc,
 
289
                description='process check {%s}' % unit_name,
 
290
                check_cmd='check_upstart_job %s' % svc
 
291
            )
 
292
        elif os.path.exists(sysv_init):
 
293
            cronpath = '/etc/cron.d/nagios-service-check-%s' % svc
 
294
            cron_file = ('*/5 * * * * root '
 
295
                         '/usr/local/lib/nagios/plugins/check_exit_status.pl '
 
296
                         '-s /etc/init.d/%s status > '
 
297
                         '/var/lib/nagios/service-check-%s.txt\n' % (svc,
 
298
                                                                     svc)
 
299
                         )
 
300
            f = open(cronpath, 'w')
 
301
            f.write(cron_file)
 
302
            f.close()
 
303
            nrpe.add_check(
 
304
                shortname=svc,
 
305
                description='process check {%s}' % unit_name,
 
306
                check_cmd='check_status_file.py -f '
 
307
                          '/var/lib/nagios/service-check-%s.txt' % svc,
 
308
            )