├── .gitignore ├── bgpdiscovery.py ├── bgpmon.py ├── README.md ├── zbx_export_templates.xml └── RosAPI.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | .idea/ -------------------------------------------------------------------------------- /bgpdiscovery.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | from RosAPI import Core 3 | from sys import argv 4 | import json 5 | 6 | try: 7 | a = Core(argv[1]) 8 | except: 9 | print("No connection") 10 | 11 | a.login("bgpmonlogin", "bgpmonpass") 12 | peers = a.response_handler(a.talk(["/routing/bgp/peer/print"])) 13 | out = {'data': []} 14 | if len(peers) > 0: 15 | for peer in peers: 16 | out['data'].append({"{#BGPPEER}": peer['remote-address']}) 17 | 18 | print(json.dumps(out, indent=4, sort_keys=True)) 19 | -------------------------------------------------------------------------------- /bgpmon.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | from RosAPI import Core 3 | from sys import argv 4 | 5 | try: 6 | a = Core(argv[1]) 7 | except: 8 | print("No connection") 9 | a.login("bgpmonlogin", "bgpmonpass") 10 | peers = a.response_handler(a.talk(["/routing/bgp/peer/print"])) 11 | peers_dict = {} 12 | if len(peers) > 0: 13 | for peer in peers: 14 | # print("{} {} {}".format(peer['remote-address'], peer['remote-as'], peer['established'])) 15 | peers_dict[peer['remote-address']] = peer['established'] 16 | 17 | if argv[2] in peers_dict: 18 | if peers_dict[argv[2]] == 'true': 19 | print(1) 20 | else: 21 | print(0) 22 | else: 23 | print(0) 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | Two scripts and template for monitoring bgp sessions in zabbix. 4 | 5 | Import template to your zabbix instalation. 6 | 7 | Place scripts in zabbix external scripts folder. 8 | 9 | 10 | # Files 11 | 12 | * bgpdiscovery.py - used for returning peers list to zabbix 13 | * bgpmon.py - get bgp session state and return 0 or 1. 14 | * RosAPI - RouterOS API library for python (thx David Jelić and Luka Blašković) 15 | 16 | # Configs 17 | 18 | For change connection credentials you should replace placeholders in function a.login(). 19 | 20 | In bgpmon it will be line 9 and in bgpdiscovery it will be line 11. 21 | 22 | # Authors 23 | 24 | * bsod (t1bur1an) 25 | 26 | -------------------------------------------------------------------------------- /zbx_export_templates.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 2.0 4 | 2016-01-12T14:43:55Z 5 | 6 | 7 | RouterOS 8 | 9 | 10 | 11 | 123 | 124 | 125 | -------------------------------------------------------------------------------- /RosAPI.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # 4 | # RosAPI.py 5 | # 6 | # Copyright 2010 David Jelić 7 | # Copyright 2010 Luka Blašković 8 | # 9 | 10 | """Python binding for Mikrotik RouterOS API""" 11 | __all__ = ["RosAPICore", "Networking"] 12 | 13 | class Core: 14 | """Core part of Router OS API 15 | 16 | It contains methods necessary to extract raw data from the router. 17 | If object is instanced with DEBUG = True parameter, it runs in verbosity mode. 18 | 19 | Core part is taken mostly from http://wiki.mikrotik.com/wiki/Manual:API#Example_client.""" 20 | 21 | def __init__(self, hostname, port=8728, DEBUG=False): 22 | import socket 23 | self.DEBUG = DEBUG 24 | self.hostname = hostname 25 | self.port = port 26 | self.currenttag = 0 27 | self.sk = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 28 | self.sk.connect((self.hostname, self.port)) 29 | 30 | def login(self, username, pwd): 31 | import binascii 32 | from hashlib import md5 33 | 34 | for repl, attrs in self.talk(["/login"]): 35 | chal = binascii.unhexlify(attrs['=ret']) 36 | md = md5() 37 | md.update('\x00') 38 | md.update(pwd) 39 | md.update(chal) 40 | self.talk(["/login", "=name=" + username, "=response=00" + binascii.hexlify(md.digest())]) 41 | 42 | def talk(self, words): 43 | if self.writeSentence(words) == 0: return 44 | r = [] 45 | while 1: 46 | i = self.readSentence(); 47 | if len(i) == 0: continue 48 | reply = i[0] 49 | attrs = {} 50 | for w in i[1:]: 51 | j = w.find('=', 1) 52 | if (j == -1): 53 | attrs[w] = '' 54 | else: 55 | attrs[w[:j]] = w[j+1:] 56 | r.append((reply, attrs)) 57 | if reply == '!done': return r 58 | 59 | def writeSentence(self, words): 60 | ret = 0 61 | for w in words: 62 | self.writeWord(w) 63 | ret += 1 64 | self.writeWord('') 65 | return ret 66 | 67 | def readSentence(self): 68 | r = [] 69 | while 1: 70 | w = self.readWord() 71 | if w == '': return r 72 | r.append(w) 73 | 74 | def writeWord(self, w): 75 | if self.DEBUG: 76 | print "<<< " + w 77 | self.writeLen(len(w)) 78 | self.writeStr(w) 79 | 80 | def readWord(self): 81 | ret = self.readStr(self.readLen()) 82 | if self.DEBUG: 83 | print ">>> " + ret 84 | return ret 85 | 86 | def writeLen(self, l): 87 | if l < 0x80: 88 | self.writeStr(chr(l)) 89 | elif l < 0x4000: 90 | l |= 0x8000 91 | self.writeStr(chr((l >> 8) & 0xFF)) 92 | self.writeStr(chr(l & 0xFF)) 93 | elif l < 0x200000: 94 | l |= 0xC00000 95 | self.writeStr(chr((l >> 16) & 0xFF)) 96 | self.writeStr(chr((l >> 8) & 0xFF)) 97 | self.writeStr(chr(l & 0xFF)) 98 | elif l < 0x10000000: 99 | l |= 0xE0000000 100 | self.writeStr(chr((l >> 24) & 0xFF)) 101 | self.writeStr(chr((l >> 16) & 0xFF)) 102 | self.writeStr(chr((l >> 8) & 0xFF)) 103 | self.writeStr(chr(l & 0xFF)) 104 | else: 105 | self.writeStr(chr(0xF0)) 106 | self.writeStr(chr((l >> 24) & 0xFF)) 107 | self.writeStr(chr((l >> 16) & 0xFF)) 108 | self.writeStr(chr((l >> 8) & 0xFF)) 109 | self.writeStr(chr(l & 0xFF)) 110 | 111 | def readLen(self): 112 | c = ord(self.readStr(1)) 113 | if (c & 0x80) == 0x00: 114 | pass 115 | elif (c & 0xC0) == 0x80: 116 | c &= ~0xC0 117 | c <<= 8 118 | c += ord(self.readStr(1)) 119 | elif (c & 0xE0) == 0xC0: 120 | c &= ~0xE0 121 | c <<= 8 122 | c += ord(self.readStr(1)) 123 | c <<= 8 124 | c += ord(self.readStr(1)) 125 | elif (c & 0xF0) == 0xE0: 126 | c &= ~0xF0 127 | c <<= 8 128 | c += ord(self.readStr(1)) 129 | c <<= 8 130 | c += ord(self.readStr(1)) 131 | c <<= 8 132 | c += ord(self.readStr(1)) 133 | elif (c & 0xF8) == 0xF0: 134 | c = ord(self.readStr(1)) 135 | c <<= 8 136 | c += ord(self.readStr(1)) 137 | c <<= 8 138 | c += ord(self.readStr(1)) 139 | c <<= 8 140 | c += ord(self.readStr(1)) 141 | return c 142 | 143 | def writeStr(self, str): 144 | n = 0; 145 | while n < len(str): 146 | r = self.sk.send(str[n:]) 147 | if r == 0: raise RuntimeError, "connection closed by remote end" 148 | n += r 149 | 150 | def readStr(self, length): 151 | ret = '' 152 | while len(ret) < length: 153 | s = self.sk.recv(length - len(ret)) 154 | if s == '': raise RuntimeError, "connection closed by remote end" 155 | ret += s 156 | return ret 157 | 158 | def response_handler(self, response): 159 | """Handles API response and remove unnessesary data""" 160 | 161 | # if respons end up successfully 162 | if response[-1][0] == "!done": 163 | r = [] 164 | # for each returned element 165 | for elem in response[:-1]: 166 | # if response is valid Mikrotik returns !re, if error !trap 167 | # before each valid element, there is !re 168 | if elem[0] == "!re": 169 | # take whole dictionary of single element 170 | element = elem[1] 171 | # with this loop we strip equals in front of each keyword 172 | for att in element.keys(): 173 | element[att[1:]] = element[att] 174 | element.pop(att) 175 | # collect modified data in new array 176 | r.append(element) 177 | return r 178 | 179 | def run_interpreter(self): 180 | import select, sys 181 | inputsentence = [] 182 | 183 | while 1: 184 | r = select.select([self.sk, sys.stdin], [], [], None) 185 | if self.sk in r[0]: 186 | # something to read in socket, read sentence 187 | x = self.readSentence() 188 | 189 | if sys.stdin in r[0]: 190 | # read line from input and strip off newline 191 | l = sys.stdin.readline() 192 | l = l[:-1] 193 | 194 | # if empty line, send sentence and start with new 195 | # otherwise append to input sentence 196 | if l == '': 197 | self.writeSentence(inputsentence) 198 | inputsentence = [] 199 | else: 200 | inputsentence.append(l) 201 | return 0 202 | 203 | class Networking(Core): 204 | """Handles network part of Mikrotik Router OS 205 | 206 | Contains functions for pulling informations about interfaces, 207 | routes, wireless registrations, etc.""" 208 | 209 | def get_all_interfaces(self): 210 | """Pulls out all available data related to network interfaces""" 211 | 212 | word = ["/interface/print"] 213 | response = Core.talk(self, word) 214 | response = Core.response_handler(self, response) 215 | return response 216 | 217 | def test(): 218 | tik = Core("172.16.1.1", DEBUG=True) 219 | tik.login("admin", "") 220 | tik.run_interpreter() 221 | 222 | if __name__ == "__main__": 223 | test() 224 | --------------------------------------------------------------------------------