checks.py 4.13 KB
Newer Older
Jonathan Michalon's avatar
Jonathan Michalon committed
1
from lib.subprocess_compat import TimeoutExpired, Popen, PIPE
2
import re
3 4


Jonathan Michalon's avatar
Jonathan Michalon committed
5 6 7 8 9 10 11 12 13 14
class Host(object):
    def __init__(self, ipv4, ipv6):
        self.ipv4 = ipv4
        self.ipv6 = ipv6

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


class Checks(list):
15 16
    def add(self, check, dests, **options):
        self += [check(d, **options) for d in dests]
Jonathan Michalon's avatar
Jonathan Michalon committed
17 18 19 20


class Check(object):
    def __init__(self, **options):
21 22 23 24 25 26 27
        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
Jonathan Michalon's avatar
Jonathan Michalon committed
28 29

    def __repr__(self):
30 31 32
        return '{:<15s} N={}/{}, R={}/{}, {}'.format(self.__class__.__name__,
            self.every_count, self.every, self.retry_count, self.retry,
            self._options)
Jonathan Michalon's avatar
Jonathan Michalon committed
33 34

    def setup(self):
Colomban Wendling's avatar
Colomban Wendling committed
35
        pass
Jonathan Michalon's avatar
Jonathan Michalon committed
36 37 38 39 40 41 42

    def teardown(self):
        pass

    def check(self, host, addr):
        pass

43
    def run(self, immediate=False):
44 45
        self.every_count = (self.every_count + 1) % self.every
        if self.every_count == 0 or immediate:
Colomban Wendling's avatar
Colomban Wendling committed
46 47
            self.setup()
            if not self.check():
48 49
                self.retry_count = min(self.retry_count + 1, self.retry)
                if self.retry_count == self.retry or immediate:
50 51 52
                    self.ok = False
            else:
                self.ok = True
53
                self.retry_count = 0
Colomban Wendling's avatar
Colomban Wendling committed
54
            self.teardown()
Jonathan Michalon's avatar
Jonathan Michalon committed
55 56
        return self.ok

57
    def exec_with_timeout(self, command, timeout=2, pattern=''):
Jonathan Michalon's avatar
Jonathan Michalon committed
58
        self.errmsg = ''
59 60 61 62 63
        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
64 65 66 67 68 69
        try:
            out, err = p.communicate(timeout=timeout)
        except TimeoutExpired:
            p.kill()
            out, err = p.communicate()
            self.errmsg += "Operation timed out\n"
70
            return False
Jonathan Michalon's avatar
Jonathan Michalon committed
71
        if p.returncode != 0:
72
            if len(out) > 0:
73 74
                self.errmsg += "stdout:\n" + \
                               out.decode(errors='replace') + '\n'
75
            if len(err) > 0:
76 77
                self.errmsg += "stderr:\n" + \
                               err.decode(errors='replace') + '\n'
78
        if re.search(pattern, str(out), flags=re.M) is None:
79 80
            self.errmsg += ("Pattern '%s' not found in reply.\nstdout: %s"
                            % (pattern, out.decode(errors='replace')))
81
            return False
Jonathan Michalon's avatar
Jonathan Michalon committed
82 83 84 85 86
        return p.returncode == 0


class CheckIP(Check):
    def __repr__(self):
87
        return '<%s on %s>' % (super().__repr__(), self.addr)
Jonathan Michalon's avatar
Jonathan Michalon committed
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


class Check4(CheckIP):
    def __init__(self, host, **options):
        super().__init__(**options)
        self.addr = host.ipv4


class Check6(CheckIP):
    def __init__(self, host, **options):
        super().__init__(**options)
        self.addr = host.ipv6


class CheckPing4(Check4):
    def check(self):
        command = ['/bin/ping', '-c', '1', '-W', '2', self.addr]
        return self.exec_with_timeout(command, timeout=3)


class CheckPing6(Check6):
    def check(self):
        command = ['/bin/ping6', '-c', '1', '-W', '2', self.addr]
        return self.exec_with_timeout(command, timeout=3)


class CheckDNSZone(Check):
    def __init__(self, zone, **options):
        super().__init__(**options)
        self.zone = zone

    def __repr__(self):
120
        return '<%s for %s>' % (super().__repr__(), self.zone)
Jonathan Michalon's avatar
Jonathan Michalon committed
121 122

    def check(self):
123 124 125
        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
126 127 128 129
        return self.exec_with_timeout(command)


class CheckDNSRec(Check):
130 131 132 133 134 135 136 137 138 139 140
    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
141 142 143 144 145 146


class CheckDNSAut(Check):
    def check(self, host, addr):
        self.errmsg = "Unimplemented"
        return False