checks.py 7.68 KB
Newer Older
1
from .subprocess_compat import TimeoutExpired, Popen, PIPE
2
import re
3
from . import mails
4
from collections import Iterable
5 6


Jonathan Michalon's avatar
Jonathan Michalon committed
7
class Host(object):
8
    def __init__(self, ipv4='192.0.2.1', ipv6='2001:db8::1', name=None):
Jonathan Michalon's avatar
Jonathan Michalon committed
9 10
        self.ipv4 = ipv4
        self.ipv6 = ipv6
11
        self.name = name if name is not None else "%s/%s" % (ipv4, ipv6)
Jonathan Michalon's avatar
Jonathan Michalon committed
12 13 14 15 16 17

    def __repr__(self):
        return '<Host ipv4="%s" ipv6="%s">' % (self.ipv4, self.ipv6)


class Checks(list):
18 19 20 21 22 23 24
    def add(self, checks, dests, **options):
        if not isinstance(checks, Iterable):
            checks = [checks]
        if not isinstance(dests, Iterable):
            dests = [dests]
        for check in checks:
            self += [check(d, **options) for d in dests]
Jonathan Michalon's avatar
Jonathan Michalon committed
25 26 27 28


class Check(object):
    def __init__(self, **options):
29 30 31 32 33 34 35
        self._options    = options
        self.retry       = options.get('retry', 0)
        self.retry_count = 0
        self.every       = options.get('every', 1)
        self.every_count = 0
        self.errmsg      = ''
        self.ok          = True
36
        self.target_name = options.get('target_name', 'Unknown')
37
        self.timeout     = options.get('timeout', 2)
Jonathan Michalon's avatar
Jonathan Michalon committed
38 39

    def __repr__(self):
40
        return '{:<15s} N={}/{}, R={}/{}, {}'.format(self.__class__.__name__,
Colomban Wendling's avatar
Colomban Wendling committed
41 42 43 44 45
                                                     self.every_count,
                                                     self.every,
                                                     self.retry_count,
                                                     self.retry,
                                                     self._options)
Jonathan Michalon's avatar
Jonathan Michalon committed
46 47

    def setup(self):
Colomban Wendling's avatar
Colomban Wendling committed
48
        pass
Jonathan Michalon's avatar
Jonathan Michalon committed
49 50 51 52 53 54 55

    def teardown(self):
        pass

    def check(self, host, addr):
        pass

56
    def run(self, immediate=False):
57 58
        self.every_count = (self.every_count + 1) % self.every
        if self.every_count == 0 or immediate:
Colomban Wendling's avatar
Colomban Wendling committed
59 60
            self.setup()
            if not self.check():
61 62
                self.retry_count = min(self.retry_count + 1, self.retry)
                if self.retry_count == self.retry or immediate:
63 64 65
                    if self.ok:
                        self.ok = False
                        mails.send_email_for_check(self)
66
            else:
67
                if not self.ok:
68
                    self.ok = True
69
                    mails.send_email_for_check(self)
70
                self.retry_count = 0
Colomban Wendling's avatar
Colomban Wendling committed
71
            self.teardown()
Jonathan Michalon's avatar
Jonathan Michalon committed
72 73
        return self.ok

74 75
    def exec_with_timeout(self, command, timeout=None, pattern=''):
        timeout = self.timeout if timeout is None else timeout
Jonathan Michalon's avatar
Jonathan Michalon committed
76
        self.errmsg = ''
77 78 79 80 81
        try:
            p = Popen(command, stdout=PIPE, stderr=PIPE)
        except OSError as e:
            self.errmsg = 'Check not available: ' + e.strerror
            return False
Jonathan Michalon's avatar
Jonathan Michalon committed
82 83 84 85 86 87
        try:
            out, err = p.communicate(timeout=timeout)
        except TimeoutExpired:
            p.kill()
            out, err = p.communicate()
            self.errmsg += "Operation timed out\n"
88
            return False
Jonathan Michalon's avatar
Jonathan Michalon committed
89
        if p.returncode != 0:
90
            if len(out) > 0:
91 92
                self.errmsg += "stdout:\n" + \
                               out.decode(errors='replace') + '\n'
93
            if len(err) > 0:
94 95
                self.errmsg += "stderr:\n" + \
                               err.decode(errors='replace') + '\n'
96
        if re.search(pattern, str(out), flags=re.M) is None:
97 98
            self.errmsg += ("Pattern '%s' not found in reply.\nstdout: %s"
                            % (pattern, out.decode(errors='replace')))
99
            return False
Jonathan Michalon's avatar
Jonathan Michalon committed
100 101 102 103
        return p.returncode == 0


class CheckIP(Check):
104 105 106 107
    def __init__(self, host, **options):
        super().__init__(**options)
        self.target_name = host.name

Jonathan Michalon's avatar
Jonathan Michalon committed
108
    def __repr__(self):
109
        return '<%s on %s>' % (super().__repr__(), self.addr)
Jonathan Michalon's avatar
Jonathan Michalon committed
110 111 112 113


class Check4(CheckIP):
    def __init__(self, host, **options):
114
        super().__init__(host, **options)
Jonathan Michalon's avatar
Jonathan Michalon committed
115 116 117 118 119
        self.addr = host.ipv4


class Check6(CheckIP):
    def __init__(self, host, **options):
120
        super().__init__(host, **options)
Jonathan Michalon's avatar
Jonathan Michalon committed
121 122 123 124 125
        self.addr = host.ipv6


class CheckPing4(Check4):
    def check(self):
126
        command = ['/bin/ping', '-c', '1', '-W', str(self.timeout), self.addr]
Colomban Wendling's avatar
Colomban Wendling committed
127
        return self.exec_with_timeout(command, timeout=self.timeout + 1)
Jonathan Michalon's avatar
Jonathan Michalon committed
128 129 130 131


class CheckPing6(Check6):
    def check(self):
132
        command = ['/bin/ping6', '-c', '1', '-W', str(self.timeout), self.addr]
Colomban Wendling's avatar
Colomban Wendling committed
133
        return self.exec_with_timeout(command, timeout=self.timeout + 1)
Jonathan Michalon's avatar
Jonathan Michalon committed
134 135 136 137 138 139


class CheckDNSZone(Check):
    def __init__(self, zone, **options):
        super().__init__(**options)
        self.zone = zone
140
        self.target_name = "zone '%s'" % zone
Jonathan Michalon's avatar
Jonathan Michalon committed
141 142

    def __repr__(self):
143
        return '<%s for %s>' % (super().__repr__(), self.zone)
Jonathan Michalon's avatar
Jonathan Michalon committed
144 145

    def check(self):
146 147 148
        command = ['check_dns_soa', '-H', self.zone]
        if self._options.get('ip_version', 0) in [4, 6]:
            command.append('-' + str(self._options['ip_version']))
Jonathan Michalon's avatar
Jonathan Michalon committed
149 150 151 152
        return self.exec_with_timeout(command)


class CheckDNSRec(Check):
153 154 155 156 157 158 159 160 161 162 163
    def check(self):
        command = ['dig', 'www.google.com', '@' + self.addr]
        return self.exec_with_timeout(command, pattern='status: NOERROR')


class CheckDNSRec4(CheckDNSRec, Check4):
    pass


class CheckDNSRec6(CheckDNSRec, Check6):
    pass
Jonathan Michalon's avatar
Jonathan Michalon committed
164 165 166 167 168 169


class CheckDNSAut(Check):
    def check(self, host, addr):
        self.errmsg = "Unimplemented"
        return False
170 171 172 173 174


class CheckHTTP(Check):
    def build_command(self):
        command = ['/usr/lib/nagios/plugins/check_http',
175
                   '-I', self.addr, '-t', str(self.timeout)]
176 177 178 179 180 181
        if 'status' in self._options:
            command += ['-e', str(self._options['status'])]
        if 'vhost' in self._options:
            command += ['-H', str(self._options['vhost'])]
        if 'string' in self._options:
            command += ['-s', str(self._options['string'])]
182 183
        if 'url' in self._options:
            command += ['-u', str(self._options['url'])]
184 185 186 187
        return command

    def check(self):
        command = self.build_command()
Colomban Wendling's avatar
Colomban Wendling committed
188
        return self.exec_with_timeout(command, timeout=self.timeout + 1)
189 190 191 192 193


class CheckHTTPS(CheckHTTP):
    def check(self):
        command = self.build_command() + ['--ssl']
Colomban Wendling's avatar
Colomban Wendling committed
194
        return self.exec_with_timeout(command, timeout=self.timeout + 1)
195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210


class CheckHTTP4(CheckHTTP, Check4):
    pass


class CheckHTTP6(CheckHTTP, Check6):
    pass


class CheckHTTPS4(CheckHTTPS, Check4):
    pass


class CheckHTTPS6(CheckHTTPS, Check6):
    pass
211 212 213 214 215 216


class CheckSMTP(Check):
    def build_command(self):
        command = ['/usr/lib/nagios/plugins/check_smtp',
                   '-H', self.addr,
217 218
                   '-f', self._options.get('from_addr',
                                           'picomon@localhost.local'),
219
                   '-t', str(self.timeout)]
220 221 222 223 224 225 226 227
        if 'command' in self._options:
            command += ['-C', str(self._options['command'])]
        if 'response' in self._options:
            command += ['-R', str(self._options['response'])]
        return command

    def check(self):
        command = self.build_command()
Colomban Wendling's avatar
Colomban Wendling committed
228
        return self.exec_with_timeout(command, timeout=self.timeout + 1)
229 230 231 232 233 234 235 236


class CheckSMTP4(CheckSMTP, Check4):
    pass


class CheckSMTP6(CheckSMTP, Check6):
    pass
237 238 239 240 241 242 243 244 245 246 247 248


class CheckOpenVPN(Check):
    def check(self):
        command = ['/usr/lib/nagios/plugins/check_udp',
                   '-H', self.addr,
                   '-p', "1194",
                   '-m', "1",
                   '-M', "ok",  # actualy just having a reply is enough
                   '-s', "\x38\x01\x01\x01\x01\x01\x01\x01\x42",
                   '-e', "@",
                   '-t', str(self.timeout)]
Colomban Wendling's avatar
Colomban Wendling committed
249
        return self.exec_with_timeout(command, timeout=self.timeout + 1)
250 251 252 253 254 255 256 257


class CheckOpenVPN4(CheckOpenVPN, Check4):
    pass


class CheckOpenVPN6(CheckOpenVPN, Check6):
    pass