python 路由表监测

来源:互联网 发布:视频网站如何优化 编辑:程序博客网 时间:2024/05/07 11:26

‘由于从事通信行业,经常有检测交换机路由器路由表变化的需要。之前比较用得多的是java,但是最近开始接触python,就想着用python来练练手。实际开发的时候发现用python写运维脚本还是挺方便的,最重要的是第三方库比较多。

这次主要用到的第三方库是pysnmp。
pysnmp依赖两个第三方的Python库:
(1) ASN.1 : Structure of Management Information:管理信息结构
(2) Cryptography Toolkit: 用来加密的
安装的过程为先安装ASN.1 和Cryptography的python库,最后安装pysnmp

其次还有一些平时可能比较少用python自带库,pickle,还有collections里的defaultdict。
我个人使用defaultdict较多,与dict类型不同,你不需要检查key是否存在。并且当你在一个字典中对一个键进行嵌套赋值时,如果这个键不存在,会触发keyError异常。 defaultdict允许我们用一个聪明的方式绕过这个问题。

import collectionsdef tree():    return defaultdict(int)some_dict = tree()some_dict['colours']['favourite'] = "yellow"

Python中可以使用 pickle 模块将对象转化为文件保存在磁盘上,在需要的时候再读取并还原。
使用pickle主要是为了方便存储读写defaultdict的数据。

下面直接撸代码。
检测到路由表变化,要把变化发送到syslog服务器上:

# -*- encoding: utf-8 -*-"""Python syslog client."""import socket# 设备类型FACILITY = {    'kern': 0, 'user': 1, 'mail': 2, 'daemon': 3,    'auth': 4, 'syslog': 5, 'lpr': 6, 'news': 7,    'uucp': 8, 'cron': 9, 'authpriv': 10, 'ftp': 11,    'local0': 16, 'local1': 17, 'local2': 18, 'local3': 19,    'local4': 20, 'local5': 21, 'local6': 22, 'local7': 23,}# 告警等级LEVEL = {    'emerg': 0, 'alert': 1, 'crit': 2, 'err': 3,    'warning': 4, 'notice': 5, 'info': 6, 'debug': 7}# 发送信息,告警等级,设备类型,syslog服务器地址、端口def syslog(message, level=LEVEL['notice'], facility=FACILITY['daemon'], host='localhost', port=514):    try:        sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)        data = '<%d>%s' % (level + facility * 8, message)        sock.sendto(data, (host, port))        sock.close()    except Exception:        pass

路由表监测主要代码(注释一开始用英文写,不过英文比较烂,必要的地方我在再改中文注释):

# -*encoding:utf-8*-import datetimefrom pysnmp.entity.rfc3413.oneliner import cmdgenfrom collections import defaultdictimport pickleimport threadingimport timefrom syslog import sendSyslog_version = '1.0''''def tree(): return defaultdict(int) => defaultdict(<function tree at 0x03BD9670>int:while no value return 0'''def tree():    return defaultdict(int)'''format nowtime to output ip routing-table changes and log'''def getNow():    now = datetime.datetime.now()    return str(now.strftime("%Y-%m-%d")) + ' ' + str(now.strftime("%H:%M:%S"))'''dict for route proto'''dict_proto = {'2': 'local', '3': 'netmgmt', '9': 'isis', '14': 'bgp'}'''dict for route oidsnmp oid节点'''oid = {    'nexthop': (1, 3, 6, 1, 2, 1, 4, 21, 1, 7),    'mask': (1, 3, 6, 1, 2, 1, 4, 21, 1, 11),    'proto': (1, 3, 6, 1, 2, 1, 4, 21, 1, 9),    'age': (1, 3, 6, 1, 2, 1, 4, 21, 1, 10)}'''sort by ip like:1.1.1.12.2.2.2ip地址排序'''def dictSort(defaultdict={}):    return sorted(defaultdict.iteritems(), key=lambda x: ip2int(str(x[0])))'''ip to int for sort'''def ip2int(s):    l = [int(i) for i in s.split('.')]    return (l[0] << 24) | (l[1] << 16) | (l[2] << 8) | l[3]'''like 255.255.255.0 => 24'''def mask2int(mask):    count_bit = lambda bin_str: len([i for i in bin_str if i == '1'])    mask_splited = mask.split('.')    mask_count = [count_bit(bin(int(i))) for i in mask_splited]    return sum(mask_count)'''like 2 => local'''def int2proto(num):    return dict_proto[num]'''compare new routing-table with old routing-tablereturn the changes"+":plus route"-":minus route'*':nexthop change"~":link unstableunstable_link_time=interval-snmptime,if route exists and age becomes less than interval-snmptime,link is unstable新ip route table与旧的比较:+:新增路由-:减少路由*:路由下一跳改变~:路由不稳定(如果路由项一直存在,age=1000,,但下一次监测到age=60,说明该路由曾经消失了又重新出现,则可以判断该链路不稳定,unstable_link_time为监测间隔)'''def cmpp(old, new, unstable_link_time):    tmp = defaultdict(tree)    for key, value in old.items():        if not new.has_key(key):            tmp[key] = value            tmp[key]['tag'] = '-'        else:            if int(new[key]['age']) <= unstable_link_time:                tmp[key] = value                tmp[key]['tag'] = '~'    for key, value in new.items():        if not old.has_key(key):            tmp[key] = value            tmp[key]['tag'] = '+'        elif old[key]['nexthop'] != new[key]['nexthop']:            tmp[key] = value            tmp[key]['tag'] = "*"            tmp[key]['_mask'] = old[key]['mask']            tmp[key]['mask'] = new[key]['mask']            tmp[key]['_nexthop'] = old[key]['nexthop']            tmp[key]['nexthop'] = new[key]['nexthop']    return tmp'''check attribute[nexthop,mask,proto,age] in tableif less some attribute,raise error 'route has changed when getting snmp data'正常获取snmp时,defaultdict[ip]里有四项,nexthop、mask、age、proto,如果在获取snmp数据期间如果路由发生变化,defaultdict[ip]里的值将少于四项,出现该状况时停止监测,等待下一次监测'''def checkAttr(table={}):    for key in table:        if len(table[key]) < 4:            # raise ValueError,'route has changed when getting snmp data'            print table[key]            return False    return True'''format to print like:+ 6.6.0.0/16 -> 6.6.6.66 local+ 6.6.6.66/32 -> 127.0.0.1 local+ 6.6.255.255/32 -> 6.6.6.66 local'''def prettyprint(changelist):    for ip in changelist:        if ip[1]['tag'] == '*':            print '%s %s/%s -> %s ===>>>  %s/%s -> %s  %s' % (                ip[1]['tag'], ip[0], mask2int(ip[1]['_mask']), ip[1]['_nexthop'], ip[0], mask2int(ip[1]['mask']),                ip[1]['nexthop'], int2proto(ip[1]['proto']))        else:            print '%s %s/%s -> %s %s' % (                ip[1]['tag'], ip[0], mask2int(ip[1]['mask']), ip[1]['nexthop'], int2proto(ip[1]['proto']))'''format to syslog like:+ 6.6.0.0/16 -> 6.6.6.66 local+ 6.6.6.66/32 -> 127.0.0.1 local+ 6.6.255.255/32 -> 6.6.6.66 local'''def prettysyslog(changelist, syslogIP='localhost'):    message = ''    for ip in changelist:        if ip[1]['tag'] == '*':            message += '%s %s/%s -> %s ===>>>  %s/%s -> %s  %s\n' % (                ip[1]['tag'], ip[0], mask2int(ip[1]['_mask']), ip[1]['_nexthop'], ip[0], mask2int(ip[1]['mask']),                ip[1]['nexthop'], int2proto(ip[1]['proto']))        else:            message += '%s %s/%s -> %s %s\n' % (                ip[1]['tag'], ip[0], mask2int(ip[1]['mask']), ip[1]['nexthop'], int2proto(ip[1]['proto']))    sendSyslog(message, host=syslogIP)'''save  log like:2016-10-02 18:06:22+ 6.6.0.0/16 -> 6.6.6.66 local+ 6.6.6.66/32 -> 127.0.0.1 local+ 6.6.255.255/32 -> 6.6.6.66 local'''def prettysave(time, changelist):    with open('log.txt', 'a') as mytxt:        mytxt.write('=' * 32 + time + '=' * 32 + '\n')        for ip in changelist:            if ip[1]['tag'] == '*':                mytxt.write('%s %s/%s -> %s ===>>>  %s/%s -> %s  %s\n' % (                    ip[1]['tag'], ip[0], mask2int(ip[1]['_mask']), ip[1]['_nexthop'], ip[0], mask2int(ip[1]['mask']),                    ip[1]['nexthop'], int2proto(ip[1]['proto'])))            else:                mytxt.write('%s %s/%s -> %s %s\n' % (                    ip[1]['tag'], ip[0], mask2int(ip[1]['mask']), ip[1]['nexthop'], int2proto(ip[1]['proto'])))'''use snmpwark for ip routing-tablepublic:snmp read string1:snmp v2c161:udp portreturn defaultdict name table'''def snmpwalk(ip, table, oid, type=''):    # try:    cg = cmdgen.CommandGenerator()    errorIndication, errorStatus, errorIndex, varBinds = cg.nextCmd(        cmdgen.CommunityData('my-agent', 'public', 1),        cmdgen.UdpTransportTarget((ip, 161)),        oid)    for varBind in varBinds:        for name, val in varBind:            table[name[-4:]][type] = val.prettyPrint(0)            # except ValueError:            #         print 'can not connect to device'class IpRoute():    def __init__(self, ip, interval=600, isThread=False, syslogIP='localhost'):        self.ip = ip        self.interval = interval        self.isThread = isThread        self.syslogIP = syslogIP    def setSyslogIP(self, syslogIP):        self.syslogIP = syslogIP    '''    run it with Threading may take more CPU,if route count more than 200,try not use it    old ip routing-table will use model 'pickle' to persist    interval:compare interval    time:run times    '''    def monitor(self):        # run times:10 year        times = 31536000 / self.interval        for i in range(times):            '''            defaultdict for saving snmp data            _table:旧路由表记录            table:新路由表记录            '''            _table = defaultdict(tree)            table = defaultdict(tree)            try:            #从pickle文件获取旧路由表记录                mydb = open('table', 'r')                _table = pickle.load(mydb)                mydb.close()            except IOError:                    pass            t0 = time.clock()    #以线程方式获取snmp数据,占用设备cpu过多,效果不明显,不建议            if self.isThread:                for key in oid:                    t = threading.Thread(target=snmpwalk, args=(self.ip, table, oid[key], key,))                    t.start()                # wait for all thread ned                for t in threading.enumerate():                    if t is threading.currentThread():                        continue                    t.join()            else:                try:                #按顺序获取snmp数据                    for key in oid:                        snmpwalk(self.ip, table, oid[key], key)                # not connect snmp sleep half time and rerun                except ValueError:                    # raise ValueError,'snmp connect error'                    print 'snmp connect error'                    time.sleep(self.interval / 2)                    continue            snmptime = time.clock() - t0            print getNow() + ' snmp time:' + str(snmptime)            unstable_link_time = self.interval - snmptime            if unstable_link_time < 0:                raise ValueError, 'interval is less than snmp time,please set it longer'            # miss some attr sleep half time and rerun            if not checkAttr(table):                print 'route has been changed while getting snmp data'                time.sleep(self.interval / 2)                continue            try:                change = dictSort(cmpp(_table, table, unstable_link_time))                if len(change) > 0:                    prettyprint(change)                    prettysave(getNow(), change)                    prettysyslog(change, self.syslogIP)            except Exception:                pass    #保存刚获取的路由表            with open('table', 'w') as mydb:                pickle.dump(table, mydb)            time.sleep(self.interval)if __name__ == '__main__':    router = IpRoute('x.x.x.x')#监测设备地址    router.setSyslogIP('x.x.x.x')#syslog服务器地址    router.monitor()

如果要监测多台设备路由器的话,用Threading多线程来运行。
然后修改一下prettyprint、prettysave、prettylog方法,把设备ip记录下来就可以。

有问题可以邮箱联系,545949185@qq.com

1 0
原创粉丝点击