Bind, мониторинг с помощью Zabbix
Если нужна установка и настройка bind в качестве dns сервера для делегации домена, то Вам сюда. Здесь я буду рассматривать как данный сервер мониторить с помощью zabbix.
Используется:
1 |
sudo named -v |
1 |
BIND 9.11.5-P4-5.1+deb10u5-Debian (Extended Support Version) <id:998753c> |
1 |
lsb_release -a |
1 2 3 4 5 |
No LSB modules are available. Distributor ID: Debian Description: Debian GNU/Linux 10 (buster) Release: 10 Codename: buster |
В первую очередь идем на следующий сайт тык
Открываем конфигурация bind
1 |
sudo nano /etc/bind/named.conf |
И добавляем
1 2 3 |
statistics-channels { inet 127.0.0.1 port 8653 allow { 127.0.0.1; }; }; |
Создаем файл и заполняем
1 |
sudo nano /etc/zabbix/zabbix_agent2.d/userparameter_bind.conf |
1 2 3 4 5 6 7 8 9 10 11 12 |
UserParameter=bind.discoverzones,/usr/local/bin/bind-stats.py discoverzones UserParameter=bind.json,/usr/local/bin/bind-stats.py json UserParameter=bind.jsonzone[*],/usr/local/bin/bind-stats.py jsonzone -z $1 UserParameter=bind.counter[*],/usr/local/bin/bind-stats.py counter -c $1 UserParameter=bind.zonecounter[*],/usr/local/bin/bind-stats.py zonecounter -z $1 -c $2 UserParameter=bind.zonemaintenancecounter[*],/usr/local/bin/bind-stats.py zonemaintenancecounter -c $1 UserParameter=bind.resolvercounter[*],/usr/local/bin/bind-stats.py resolvercounter -c $1 UserParameter=bind.socketcounter[*],/usr/local/bin/bind-stats.py socketcounter -c $1 UserParameter=bind.incounter[*],/usr/local/bin/bind-stats.py incounter -c $1 UserParameter=bind.outcounter[*],/usr/local/bin/bind-stats.py outcounter -c $1 UserParameter=bind.memory[*],/usr/local/bin/bind-stats.py memory -c $1 UserParameter=bind.cache[*],/usr/local/bin/bind-stats.py cache -c $1 |
1 |
sudo chown zabbix:zabbix /etc/zabbix/zabbix_agent2.d/userparameter_bind.conf |
Теперь сам скрипт и даем права
1 |
sudo nano /usr/local/bin/bind-stats.py |
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 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 |
#!/usr/bin/env python3 # -*- coding: utf-8 -*- # __author__ = 'https://github.com/Pesticles/Zabbix-Bind9-Statistics-Collection' import argparse import json import os import sys import time import re JSONFILE = '/tmp/bindstats.json' CACHELIFE = 60 parser = argparse.ArgumentParser() parser.add_argument("action", help="discoverzones | counter | zonecounter | zonemaintenancecounter | resolvercounter " "| socketcounter | incounter | outcounter | json") parser.add_argument("-z", help="zone") parser.add_argument("-c", help="counter name") parser.add_argument("-p", help="bind stats port") args = parser.parse_args() # Configurable port port = 8653 if args.p: port = args.p # Read from the cache if it exists and is less than a minute old, so we don't hit Bind directly too often. if os.path.exists(JSONFILE) and time.time() - os.path.getmtime(JSONFILE) <= CACHELIFE: with open(JSONFILE) as f: j = json.load(f) else: import http.client conn = http.client.HTTPConnection('localhost:{0}'.format(port)) conn.request('GET', '/') resp = conn.getresponse() if not resp.status == 200: print("HTTP GET Failed") sys.exit(1) content = resp.read() conn.close() import xml.etree.ElementTree as ElementTree root = ElementTree.fromstring(content) # first, we need to see what statistics version we are. 2.x or 3.x # if root tag is isc = we have probably stats version 2, if it stats with statisics we have version 3 or newer if root.tag == 'isc': # get statistics version from isc/bind/statistics attrib version = root.find('./bind/statistics').attrib['version'] elif root.tag == 'statistics': version = root.attrib['version'] else: print("Unknown root tag: {}".format(root.ag), file=sys.stderr) print("ZBX_NOTSUPPORTED") # check the statistics version here v = re.match('^(\d{1})\.', version) version = int(v.group(1)) if version < 0 or version > 3: print("Unsupported bind statistics version: {}".format(root.attrib), file=sys.stderr) print("ZBX_NOTSUPPORTED") # Build the JSON cache j = { 'zones': {}, 'counter': {}, 'zonemaintenancecounter': {}, 'resolvercounter': {}, 'socketcounter': {}, 'incounter': {}, 'outcounter': {}, 'cache': {}, 'memory': {} } # this is for version 2 if version == 2: for view in root.iterfind('./bind/statistics/views/view'): if view.findtext('./name') in ('_default',): for zone in view.iterfind('./zones/zone'): if zone.find('./counters') is not None: counters = {} for counter in zone.iterfind('./counters/*'): counters[counter.tag] = counter.text j['zones'][zone.findtext('./name')] = counters for stat in root.iterfind('./bind/statistics/server/nsstat'): j['counter'][stat.findtext('./name')] = stat.findtext('./counter') for stat in root.iterfind('./bind/statistics/server/zonestat'): j['zonemaintenancecounter'][stat.findtext('./name')] = stat.findtext('./counter') for view in root.iterfind('./bind/statistics/views/view'): if view.findtext('./name') in ('_default',): for stat in view.iterfind('./resstat'): j['resolvercounter'][stat.findtext('./name')] = stat.findtext('./counter') for stat in root.iterfind('./bind/statistics/server/sockstat'): j['socketcounter'][stat.findtext('./name')] = stat.findtext('./counter') for stat in root.iterfind('./bind/statistics/server/queries-in/rdtype'): j['incounter'][stat.findtext('./name')] = stat.findtext('./counter') for stat in root.iterfind('./bind/statistics/views/view/rdtype'): j['outcounter'][stat.findtext('./name')] = stat.findtext('./counter') # Memory for child in root.iterfind('./bind/statistics/memory/summary/*'): j['memory'][child.tag] = child.text # Cache for local for child in root.iterfind('./bind/statistics/views/view/cache'): if child.attrib['name'] == 'localhost_resolver': for stat in child.iterfind('./rrset'): j['cache'][stat.findtext('./name')] = stat.findtext('./counter') # this is for newer version 3 if version == 3: for child in root.iterfind('./server/counters'): # V2 ./bind/statistics/server/nsstat if child.attrib['type'] == 'nsstat': for stat in child.iterfind('./counter'): j['counter'][stat.attrib['name']] = stat.text # V2 ./bind/statistics/server/sockstat if child.attrib['type'] == 'sockstat': for stat in child.iterfind('./counter'): j['socketcounter'][stat.attrib['name']] = stat.text # V2 ./bind/statistics/server/zonestat if child.attrib['type'] == 'zonestat': for stat in child.iterfind('./counter'): j['zonemaintenancecounter'][stat.attrib['name']] = stat.text # V2 ./bind/statistics/server/queries-in/rdtype if child.attrib['type'] == 'qtype': for stat in child.iterfind('./counter'): j['incounter'][stat.attrib['name']] = stat.text # they are only for block _default for child in root.iterfind('./views/view/counters'): # V2 ./bind/statistics/views/view/rdtype if child.attrib['type'] == 'resqtype': for stat in child.iterfind('./counter'): j['outcounter'][stat.attrib['name']] = stat.text # V2 ./bind/statistics/views/view => _default name only if child.attrib['type'] == 'resstats': for stat in child.iterfind('./counter'): j['resolvercounter'][stat.attrib['name']] = stat.text # V2: no (only in memory detail stats) if child.attrib['type'] == 'cachestats': for stat in child.iterfind('./counter'): j['cache'][stat.attrib['name']] = stat.text # V2 has @name = localhost_resolver, interal, external for child in root.iterfind('./views/view/cache'): if (child.attrib['name'] == '_default'): for stat in child.iterfind('./rrset'): j['cache'][stat.findtext('./name')] = stat.findtext('./counter') # for sets stating with !, we replace that with an _ (! is not allowed in zabbix) if re.match('^!', stat.findtext('./name')): j['cache'][stat.findtext('./name').replace('!', '_')] = stat.findtext('./counter') # for all the Zone stats only for child in root.iterfind('./views/view'): # only for default if (child.attrib['name'] == '_default'): # V2 ./bind/statistics/views/view -> ./zones/zone => _default name only for zone in child.iterfind('./zones/zone'): counters = {} for stat in zone.iterfind('./counters'): if stat.attrib['type'] == 'rcode' or stat.attrib['type'] == 'qtype': for counter in stat.iterfind('./counter'): counters[counter.attrib['name']] = counter.text j['zones'][zone.attrib['name']] = counters # V2 ./bind/statistics/memory/summary/* for child in root.iterfind('./memory/summary/*'): j['memory'][child.tag] = child.text # write to cache is the same in both version with open(JSONFILE, 'w') as f: json.dump(j, f) if args.action == 'discoverzones': d = {'data': [{'{#ZONE}': zone} for zone in j['zones'].keys() if len(j['zones'][zone])>0]} print(json.dumps(d)) sys.exit(0) elif args.action == 'zonecounter': if not (args.z and args.c): print("Missing argument", file=sys.stderr) print("ZBX_NOTSUPPORTED") sys.exit(1) if args.z in j['zones'] and args.c in j['zones'][args.z]: print(j['zones'][args.z][args.c]) sys.exit(0) else: print("ZBX_NOTSUPPORTED") sys.exit(1) elif args.action == 'jsonzone': if not args.z: print("Missing argument", file=sys.stderr) print("ZBX_NOTSUPPORTED") sys.exit(1) if args.z in j['zones']: print(json.dumps(j['zones'][args.z])) sys.exit(0) else: print("ZBX_NOTSUPPORTED") sys.exit(1) elif args.action == 'json': del j['zones'] print(json.dumps(j)) #print(json.dumps(j, indent=4, separators=(',', ': ') ) ) sys.exit(0) else: if not args.c: print("Missing argument", file=sys.stderr) print("ZBX_NOTSUPPORTED") sys.exit(1) if args.c in j[args.action]: print(j[args.action][args.c]) sys.exit(0) else: print("ZBX_NOTSUPPORTED") sys.exit(1) |
1 |
sudo chown zabbix:zabbix /usr/local/bin/bind-stats.py |
1 |
sudo chmod u+x /usr/local/bin/bind-stats.py |
И перезапускаем агент zabbix
1 |
sudo systemctl restart zabbix-agent2.service |
Импортируем сам шаблон в zabbix и пользуемся