Collection of CheckMK checks (see https://checkmk.com/). All checks and plugins are provided as is. Absolutely no warranty. Send any comments to thl-cmk[at]outlook[dot]com

Skip to content
cisco_bgp_peer 30.6 KiB
Newer Older
thl-cmk's avatar
thl-cmk committed
#!/usr/bin/python
# -*- encoding: utf-8; py-indent-offset: 4 -*-
#
# Cisco BGP Peer
#
# Author: Th.L.
# Date  : 2017-12-26
#
# Monitor status of Cisco BGP Peer (IPv4 and IPv6)
#
# 24.05.2018: changed counters to 1/s
# 25.05.2018: a lot of code cleanup
#             packet name changed from cisco_bgp to cisco_bgp_peer
#             added support of more then one address family per peer
#               (changed item from remoteip to remoteip+familyname, rewrite of parer, inventory and check function)
# 27.05.2018: changed scan function from  '.1.3.6.1.4.1.9.9.187.1.2.7.1.3.* to sysdecr contains cisco
# 28.05.2018: changed wato, added peer alias, state if not found, infotext values
# 29.05.2018: fixed longoutpout (removed not configured)
# 02.11.2018: modified scanfunction (from "find 'cisco' =-1"  to "'cisco' in OID"
# 02.18.2019: added fix for empty values ("" instead of "0") sugested by Laurent Barbier (lbarbier[at]arkane-studios[dot]com)
thl-cmk's avatar
thl-cmk committed
# 24.02.2020: added workaround for missing cbgpPeer2AddrFamily (example L2VPN EVPN peers, Fix for jonale82[at]gmail[dot]com)
#
thl-cmk's avatar
thl-cmk committed
#

#
# snmpwalk sample
#
#   CISCO-BGP4-MIB::cbgpPeer2AddrFamilyEntry
#
# OMD[mysite]:~$  snmpwalk -ObentU -v2c -c <removed> simulant 1.3.6.1.4.1.9.9.187.1.2.7
# .1.3.6.1.4.1.9.9.187.1.2.7.1.3.1.4.62.214.127.57.1.1 = STRING: "IPv4 Unicast"
# .1.3.6.1.4.1.9.9.187.1.2.7.1.3.1.4.217.119.208.1.1.1 = STRING: "IPv4 Unicast"
# .1.3.6.1.4.1.9.9.187.1.2.7.1.3.1.4.217.119.208.33.1.1 = STRING: "IPv4 Unicast"
# .1.3.6.1.4.1.9.9.187.1.2.7.1.3.2.16.32.1.20.56.7.0.0.39.0.0.0.0.0.0.0.1.2.1 = STRING: "IPv6 Unicast"
# .1.3.6.1.4.1.9.9.187.1.2.7.1.3.2.16.42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.16.2.1 = STRING: "IPv6 Unicast"
#
#  CISCO-BGP4-MIB::CbgpPeer2Entry (IPv4)
#
# OMD[mysite]:~$ snmpwalk -ObentU -v2c -c <removed> simulant 1.3.6.1.4.1.9.9.187.1.2.5.1| grep 62.214.127.57
# .1.3.6.1.4.1.9.9.187.1.2.5.1.3.1.4.62.214.127.57 = INTEGER: 6
# .1.3.6.1.4.1.9.9.187.1.2.5.1.4.1.4.62.214.127.57 = INTEGER: 2
# .1.3.6.1.4.1.9.9.187.1.2.5.1.5.1.4.62.214.127.57 = INTEGER: 4
# .1.3.6.1.4.1.9.9.187.1.2.5.1.6.1.4.62.214.127.57 = Hex-STRING: 3E D6 7F 3A 
# .1.3.6.1.4.1.9.9.187.1.2.5.1.7.1.4.62.214.127.57 = Gauge32: 29418
# .1.3.6.1.4.1.9.9.187.1.2.5.1.8.1.4.62.214.127.57 = Gauge32: 0
# .1.3.6.1.4.1.9.9.187.1.2.5.1.9.1.4.62.214.127.57 = IpAddress: 217.119.208.2
# .1.3.6.1.4.1.9.9.187.1.2.5.1.10.1.4.62.214.127.57 = Gauge32: 179
# .1.3.6.1.4.1.9.9.187.1.2.5.1.11.1.4.62.214.127.57 = Gauge32: 8881
# .1.3.6.1.4.1.9.9.187.1.2.5.1.12.1.4.62.214.127.57 = IpAddress: 62.214.127.57
# .1.3.6.1.4.1.9.9.187.1.2.5.1.13.1.4.62.214.127.57 = Counter32: 18
# .1.3.6.1.4.1.9.9.187.1.2.5.1.14.1.4.62.214.127.57 = Counter32: 2
# .1.3.6.1.4.1.9.9.187.1.2.5.1.15.1.4.62.214.127.57 = Counter32: 205
# .1.3.6.1.4.1.9.9.187.1.2.5.1.16.1.4.62.214.127.57 = Counter32: 195
# .1.3.6.1.4.1.9.9.187.1.2.5.1.17.1.4.62.214.127.57 = Hex-STRING: 00 00 
# .1.3.6.1.4.1.9.9.187.1.2.5.1.18.1.4.62.214.127.57 = Counter32: 1
# .1.3.6.1.4.1.9.9.187.1.2.5.1.19.1.4.62.214.127.57 = Gauge32: 10446
# .1.3.6.1.4.1.9.9.187.1.2.5.1.20.1.4.62.214.127.57 = INTEGER: 60
# .1.3.6.1.4.1.9.9.187.1.2.5.1.21.1.4.62.214.127.57 = INTEGER: 180
# .1.3.6.1.4.1.9.9.187.1.2.5.1.22.1.4.62.214.127.57 = INTEGER: 60
# .1.3.6.1.4.1.9.9.187.1.2.5.1.23.1.4.62.214.127.57 = INTEGER: 180
# .1.3.6.1.4.1.9.9.187.1.2.5.1.24.1.4.62.214.127.57 = INTEGER: 60
# .1.3.6.1.4.1.9.9.187.1.2.5.1.25.1.4.62.214.127.57 = INTEGER: 30
# .1.3.6.1.4.1.9.9.187.1.2.5.1.26.1.4.62.214.127.57 = INTEGER: 30
# .1.3.6.1.4.1.9.9.187.1.2.5.1.27.1.4.62.214.127.57 = Gauge32: 5824
# .1.3.6.1.4.1.9.9.187.1.2.5.1.28.1.4.62.214.127.57 = ""
# .1.3.6.1.4.1.9.9.187.1.2.5.1.29.1.4.62.214.127.57 = INTEGER: 5
#
#
# CISCO-BGP4-MIB::CbgpPeer2Entry (IPv6)
#
# OMD[mysite]:~$  snmpwalk -ObentU -v2c -c <removed> simulant 1.3.6.1.4.1.9.9.187.1.2.5.1| grep 16.42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.16
# .1.3.6.1.4.1.9.9.187.1.2.5.1.3.2.16.42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.16 = INTEGER: 6
# .1.3.6.1.4.1.9.9.187.1.2.5.1.4.2.16.42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.16 = INTEGER: 2
# .1.3.6.1.4.1.9.9.187.1.2.5.1.5.2.16.42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.16 = INTEGER: 4
# .1.3.6.1.4.1.9.9.187.1.2.5.1.6.2.16.42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.16 = Hex-STRING: 2A 05 57 C0 00 00 FF FF 00 00 00 00 00 00 00 11
# .1.3.6.1.4.1.9.9.187.1.2.5.1.7.2.16.42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.16 = Gauge32: 179
# .1.3.6.1.4.1.9.9.187.1.2.5.1.8.2.16.42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.16 = Gauge32: 0
# .1.3.6.1.4.1.9.9.187.1.2.5.1.9.2.16.42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.16 = IpAddress: 217.119.208.2
# .1.3.6.1.4.1.9.9.187.1.2.5.1.10.2.16.42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.16 = Gauge32: 35062
# .1.3.6.1.4.1.9.9.187.1.2.5.1.11.2.16.42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.16 = Gauge32: 31259
# .1.3.6.1.4.1.9.9.187.1.2.5.1.12.2.16.42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.16 = IpAddress: 217.119.208.1
# .1.3.6.1.4.1.9.9.187.1.2.5.1.13.2.16.42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.16 = Counter32: 5
# .1.3.6.1.4.1.9.9.187.1.2.5.1.14.2.16.42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.16 = Counter32: 6
# .1.3.6.1.4.1.9.9.187.1.2.5.1.15.2.16.42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.16 = Counter32: 157
# .1.3.6.1.4.1.9.9.187.1.2.5.1.16.2.16.42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.16 = Counter32: 161
# .1.3.6.1.4.1.9.9.187.1.2.5.1.17.2.16.42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.16 = Hex-STRING: 06 04
# .1.3.6.1.4.1.9.9.187.1.2.5.1.18.2.16.42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.16 = Counter32: 2
# .1.3.6.1.4.1.9.9.187.1.2.5.1.19.2.16.42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.16 = Gauge32: 8430
# .1.3.6.1.4.1.9.9.187.1.2.5.1.20.2.16.42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.16 = INTEGER: 60
# .1.3.6.1.4.1.9.9.187.1.2.5.1.21.2.16.42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.16 = INTEGER: 180
# .1.3.6.1.4.1.9.9.187.1.2.5.1.22.2.16.42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.16 = INTEGER: 60
# .1.3.6.1.4.1.9.9.187.1.2.5.1.23.2.16.42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.16 = INTEGER: 180
# .1.3.6.1.4.1.9.9.187.1.2.5.1.24.2.16.42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.16 = INTEGER: 60
# .1.3.6.1.4.1.9.9.187.1.2.5.1.25.2.16.42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.16 = INTEGER: 0
# .1.3.6.1.4.1.9.9.187.1.2.5.1.26.2.16.42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.16 = INTEGER: 0
# .1.3.6.1.4.1.9.9.187.1.2.5.1.27.2.16.42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.16 = Gauge32: 1494
# .1.3.6.1.4.1.9.9.187.1.2.5.1.28.2.16.42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.16 = STRING: "Administrative Reset"
# .1.3.6.1.4.1.9.9.187.1.2.5.1.29.2.16.42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.16 = INTEGER: 5
#
#
# CISCO-BGP4-MIB::cbgpPeer2AddrFamilyPrefixEntry (IPv4)
#
# OMD[mysite]:~$  snmpwalk -ObentU -v2c -c <removed> simulant 1.3.6.1.4.1.9.9.187.1.2.8.1| grep 16.42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.16
# .1.3.6.1.4.1.9.9.187.1.2.8.1.1.2.16.42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.16.2.1 = Counter32: 2
# .1.3.6.1.4.1.9.9.187.1.2.8.1.2.2.16.42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.16.2.1 = Gauge32: 0
# .1.3.6.1.4.1.9.9.187.1.2.8.1.3.2.16.42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.16.2.1 = Gauge32: 100000
# .1.3.6.1.4.1.9.9.187.1.2.8.1.4.2.16.42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.16.2.1 = Gauge32: 85
# .1.3.6.1.4.1.9.9.187.1.2.8.1.5.2.16.42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.16.2.1 = Gauge32: 80
# .1.3.6.1.4.1.9.9.187.1.2.8.1.6.2.16.42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.16.2.1 = Gauge32: 10
# .1.3.6.1.4.1.9.9.187.1.2.8.1.7.2.16.42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.16.2.1 = Gauge32: 0
# .1.3.6.1.4.1.9.9.187.1.2.8.1.8.2.16.42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.16.2.1 = Gauge32: 0
#
#
# CISCO-BGP4-MIB::cbgpPeer2AddrFamilyPrefixEntry (IPv6)
#
# OMD[mysite]:~$  snmpwalk -ObentU -v2c -c <removed> simulant 1.3.6.1.4.1.9.9.187.1.2.8.1| grep 16.42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.16
# .1.3.6.1.4.1.9.9.187.1.2.8.1.1.2.16.42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.16.2.1 = Counter32: 2
# .1.3.6.1.4.1.9.9.187.1.2.8.1.2.2.16.42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.16.2.1 = Gauge32: 0
# .1.3.6.1.4.1.9.9.187.1.2.8.1.3.2.16.42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.16.2.1 = Gauge32: 100000
# .1.3.6.1.4.1.9.9.187.1.2.8.1.4.2.16.42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.16.2.1 = Gauge32: 85
# .1.3.6.1.4.1.9.9.187.1.2.8.1.5.2.16.42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.16.2.1 = Gauge32: 80
# .1.3.6.1.4.1.9.9.187.1.2.8.1.6.2.16.42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.16.2.1 = Gauge32: 10
# .1.3.6.1.4.1.9.9.187.1.2.8.1.7.2.16.42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.16.2.1 = Gauge32: 0
# .1.3.6.1.4.1.9.9.187.1.2.8.1.8.2.16.42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.16.2.1 = Gauge32: 0
#
#
# sample info
#
# [
#  [
#   ['1.4.77.235.182.229', '6', '2', 'M\xeb\xb6\xe6', '0', '217.119.208.1', '21413', '77.235.182.229', '1', '3', '48',
#    '53', '\x04\x00', '8', '2581', '2581', 'hold time expired', '5'],
#   ['1.4.217.119.208.2', '6', '2', '\xd9w\xd0\x01', '0', '217.119.208.1', '31259', '217.119.208.2', '11', '23', '168',
#    '170', '\x06\x04', '3', '8380', '5774', 'Administrative Reset', '5'],
#   ['1.4.217.119.208.34', '6', '2', '\xd9w\xd0!', '0', '217.119.208.1', '31259', '217.119.208.2', '11', '23', '168',
#    '170', '\x06\x04', '3', '8377', '5774', 'Administrative Reset', '5'],
#   ['2.16.42.0.28.160.16.0.1.53.0.0.0.0.0.0.0.1', '6', '2', '*\x00\x1c\xa0\x10\x00\x015\x00\x00\x00\x00\x00\x00\x00\x02',
#    '0', '217.119.208.1', '21413', '77.235.182.229', '0', '4', '108', '121', '\x06\x04', '6', '6295', '0',
#    'Administrative Reset', '5'],
#   ['2.16.42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.17', '6', '2', '*\x05W\xc0\x00\x00\xff\xff\x00\x00\x00\x00\x00\x00\x00\x10',
#    '0', '217.119.208.1', '31259', '217.119.208.2', '6', '5', '160', '157', '\x06\x04', '2', '8380', '1409',
#     'Administrative Reset', '5']
#  ],
#  [
#   ['1.4.77.235.182.229.1.1', 'IPv4 Unicast', '1', '0', '', '', '', '6', '0', '0'],
#   ['1.4.217.119.208.2.1.1', 'IPv4 Unicast', '4', '0', '', '', '', '17', '0', '10'],
#   ['1.4.217.119.208.34.1.1', 'IPv4 Unicast', '4', '0', '', '', '', '17', '0', '10'],
#   ['2.16.42.0.28.160.16.0.1.53.0.0.0.0.0.0.0.1.2.1', 'IPv6 Unicast', '0', '0', '100000', '85', '80', '6', '0', '0'],
#   ['2.16.42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.17.2.1', 'IPv6 Unicast', '2', '0', '100000', '85', '80', '8', '0', '0']
#  ]
# ]
#
#
#

factory_settings['cisco_bgp_peer_defaults'] = {
    'minuptime': 86400,
    'useaslocalas': 0,
    'htmloutput': False,
    'infotext_values': [],
    'peer_list': [],
}

###########################################################################
#
#  DATA Parser function
#
###########################################################################


def parse_cisco_bgp_peer(info):

    def bgp_render_ipv4_address(bytestring):
        return ".".join(["%s" % ord(m) for m in bytestring])

    def bgp_shorten_ipv6_adress(address):
        address = address.split(':')
        span = 2
        address = [''.join(address[i:i + span]) for i in range(0, len(address), span)]
        for m in range(0, len(address)):
            address[m] = re.sub(r'^0{1,3}', r'', address[m])
        address = ':'.join(address)
        zeros = ':0:0:0:0:0:0:'
        while not zeros == '':
            if zeros in address:
                address = re.sub(r'%s' % zeros, r'::', address)
                zeros = ''
            else:
                zeros = zeros[:-2]
        return address

    def bgp_render_ipv6_address(bytestring):
        address = ":".join(["%02s" % hex(ord(m))[2:] for m in bytestring]).replace(' ', '0').upper()
        address = bgp_shorten_ipv6_adress(address)

        return address

    def bgp_render_ip_address(bytestring):
        if len(bytestring) == 4:
            return bgp_render_ipv4_address(bytestring)
        elif len(bytestring) == 16:
            return bgp_render_ipv6_address(bytestring)
        else:
            return ''

    def cisco_bgp_get_peer(OID_END):
        # returns peer address string from OID_END
        # u'1.4.217.119.208.34.1.1' --> 217.119.208.34
        # u'2.16.42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.17.2.1' -->  42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.17
        peer_ip = ''
        OID_END = OID_END.split('.')
        if int(OID_END[1]) == 4:  # length of ip address
thl-cmk's avatar
thl-cmk committed
            peer_ip = '.'.join(OID_END[2:6])  # ipv4 address
        elif int(OID_END[1]) == 16:  # ipv6 address
thl-cmk's avatar
thl-cmk committed
            peer_ip = ':'.join('%02s' % hex(int(m))[2:] for m in OID_END[2:18]).replace(' ', '0').upper()
            peer_ip = bgp_shorten_ipv6_adress(peer_ip)

        return peer_ip

    def cisco_bgp_errors(bytestring):
        lasterrorhex = ''.join(["%02X " % ord(x) for x in bytestring]).strip()
        byte1, byte2 = lasterrorhex.split()

        names = {}
        names[0] = {0: 'NO ERROR'}
        names[1] = {0: 'Message',
                    2: 'Connection Not Synchronized',
                    3: 'Bad Message Length',
                    4: 'Bad Message Type',
                    }
        names[2] = {0: 'OPEN',
                    1: 'Unsupported Version Number',
                    2: 'Bad Peer AS',
                    3: 'Bad BGP Identifier',
                    4: 'Unsupported Optional Parameter',
                    5: 'Authentication Failure',
                    6: 'Unacceptable Hold',
                    }
        names[3] = {0: 'UPDATE',
                    1: 'Malformed Attribute List',
                    2: 'Unrecognized Well-known Attribute',
                    3: 'Missing Well-known Attribute',
                    4: 'Attribute Flags Error',
                    5: 'Attribute Length Error',
                    6: 'Invalid ORIGIN Attribute',
                    7: 'AS Routing Loop',
                    8: 'Invalid NEXT_HOP Attribute',
                    9: 'Optional Attribute Error',
                    10: 'Invalid Network Field',
                    11: 'Malformed AS_PATH',
                    }
        names[4] = {0: 'Hold Timer Expired', }

        names[5] = {0: 'Finite State Machine Error', }

        names[6] = {0: 'Administratively Shutdown',
                    1: 'Max Prefix Reached',
                    2: 'Peer Unconfigured',
                    3: 'Administratively Reset',
                    4: 'Connection Rejected',
                    5: 'Other Configuration Change',
                    }

        return names[int(byte1, 16)].get(int(byte2, 16))

thl-cmk's avatar
thl-cmk committed

thl-cmk's avatar
thl-cmk committed
    # bgp not active
    if info == [[], []]:
        return None

thl-cmk's avatar
thl-cmk committed
    cbgpPeer2Entry, cbgpPeer2AddrFamily = info
thl-cmk's avatar
thl-cmk committed

    peer_prefixes = {}
    # create dictionary from cbgpPeer2AddrFamily ('remoteip addrfamilyname' as index)
thl-cmk's avatar
thl-cmk committed
    if len(cbgpPeer2AddrFamily) > 0 :
        for entry in cbgpPeer2AddrFamily:
            oid_end, addrfamilyname, acceptedprefixes, deniedprefixes, prefixadminlimit, prefixthreshold, \
            prefixclearthreshold, advertisedprefixes, suppressedprefixes, withdrawnprefixes = entry
thl-cmk's avatar
thl-cmk committed

thl-cmk's avatar
thl-cmk committed
            remoteaddr = cisco_bgp_get_peer(oid_end)
thl-cmk's avatar
thl-cmk committed

thl-cmk's avatar
thl-cmk committed
            # check if prefixadminlimit is set
            if prefixadminlimit.isdigit():
                prefixadminlimit = int(prefixadminlimit)
            else:
                prefixadminlimit = 0
thl-cmk's avatar
thl-cmk committed

thl-cmk's avatar
thl-cmk committed
            # check if prefixthreshold is set
            if prefixthreshold.isdigit():
                prefixthreshold = int(prefixthreshold)
            else:
                prefixthreshold = 0
thl-cmk's avatar
thl-cmk committed

thl-cmk's avatar
thl-cmk committed
            # check if prefixclearthreshold is set
            if prefixclearthreshold.isdigit():
                prefixclearthreshold = int(prefixclearthreshold)
            else:
                prefixclearthreshold = 0
thl-cmk's avatar
thl-cmk committed

thl-cmk's avatar
thl-cmk committed
            if acceptedprefixes.isdigit():
                acceptedprefixes = int(acceptedprefixes)
            else:
                acceptedprefixes = 0
thl-cmk's avatar
thl-cmk committed

thl-cmk's avatar
thl-cmk committed
            if advertisedprefixes.isdigit():
                advertisedprefixes = int(advertisedprefixes)
            else:
                advertisedprefixes = 0
thl-cmk's avatar
thl-cmk committed

thl-cmk's avatar
thl-cmk committed
            # fix sugested by Laurent Barbier (lbarbier[at]arkane-studios[dot]com)
            # some Cisco devices report not "0" if the value is 0, instead the report "". for example Nexus devices behave like this.
            if deniedprefixes.isdigit():
                deniedprefixes = int(deniedprefixes)
            else:
                deniedprefixes = 0
thl-cmk's avatar
thl-cmk committed

thl-cmk's avatar
thl-cmk committed
            if suppressedprefixes.isdigit():
                suppressedprefixes = int(suppressedprefixes)
            else:
                suppressedprefixes = 0
thl-cmk's avatar
thl-cmk committed

thl-cmk's avatar
thl-cmk committed
            if withdrawnprefixes.isdigit():
                withdrawnprefixes = int(withdrawnprefixes)
            else:
                withdrawnprefixes = 0


            peer = {'remoteaddr': remoteaddr,
                    'addrfamilyname': addrfamilyname,
                    'acceptedprefixes': acceptedprefixes,
                    'deniedprefixes': deniedprefixes,
                    'prefixadminlimit': prefixadminlimit,
                    'prefixthreshold': prefixthreshold,
                    'prefixclearthreshold': prefixclearthreshold,
                    'advertisedprefixes': advertisedprefixes,
                    'suppressedprefixes': suppressedprefixes,
                    'withdrawnprefixes': withdrawnprefixes, }

            peer_prefixes.update({'%s %s' % (remoteaddr, addrfamilyname): peer})

    #  workaround: get remote ip from cbgpPeer2Entry if cbgpPeer2AddrFamilyName is missing :-(
    elif len(cbgpPeer2Entry) > 0:
        for entry in cbgpPeer2Entry:
            remoteaddr = cisco_bgp_get_peer(entry[0])
            addrfamilyname = ''

            peer = {'remoteaddr': remoteaddr,
                    }

            peer_prefixes.update({'%s %s' % (remoteaddr, addrfamilyname): peer})

    # create dictionary from cbgpPeer2Entry (peer ip address as index)
thl-cmk's avatar
thl-cmk committed
    peer_table = {}
thl-cmk's avatar
thl-cmk committed
    for entry in cbgpPeer2Entry:
thl-cmk's avatar
thl-cmk committed
        oid_end, state, adminstatus, localaddr,  localas, localidentifier, remoteas, remoteidentifier, inupdates, \
        outupdates, intotalmessages, outtotalmessages, lasterror, fsmestablishedtransitions, fsmestablishedtime, \
        inupdateelapsedtime, lasterrortxt, prevstate = entry

        peer = {'remoteaddr': cisco_bgp_get_peer(oid_end),
                'state': int(state),
                'adminstate': int(adminstatus),
                'localaddr': bgp_render_ip_address(localaddr),
                'localas': int(localas),
                'localid': localidentifier,
                'remoteas': int(remoteas),
                'remoteid': remoteidentifier,
                'inupdates': int(inupdates),
                'outupdates': int(outupdates),
                'intotalmessages': int(intotalmessages),
                'outtotalmessages': int(outtotalmessages),
                'lasterror': cisco_bgp_errors(lasterror),
                'fsmestablishedtransitions': int(fsmestablishedtransitions),
                'fsmestablishedtime': int(fsmestablishedtime),
                'inupdateelapsedtime': inupdateelapsedtime,
                'lasterrortxt': lasterrortxt,
                'prevstate': int(prevstate),}

        peer_table.update({'%s' % cisco_bgp_get_peer(oid_end): peer})

    return [peer_prefixes, peer_table]

###########################################################################
#
#  INVENTORY function
#
###########################################################################


def inventory_cisco_bgp_peer(parsed):
    if parsed:
        peer_prefixes, peer_table = parsed
        for key in peer_prefixes.keys():
            yield key, {}

###########################################################################
#
#  CHECK function
#
###########################################################################


def check_cisco_bgp_peer(item, params, parsed):

    def bgp_render_uptime(uptime):  # expects time in seconds
        m, s = divmod(uptime, 60)  # break in seconds / minutes
        h, m = divmod(m, 60)  # break in mintes / hours
        if h >= 24:  # more then one day
            d, h = divmod(h, 24)  # break in hours / days
        else:
            return '%02d:%02d:%02d' % (h, m, s)
        if d >= 365:  # more the one year
            y, d = divmod(d, 365)  # break in days / years
            return '%dy %dd %02d:%02d:%02d' % (y, d, h, m, s)
        else:
            return '%dd %02d:%02d:%02d' % (d, h, m, s)

    def cisco_bgp_adminstate(state):
        names = {1: 'stop',
                 2: 'start', }
        return names.get(state, 'unknown (%s)' % state)

    def cisco_bgp_peerstate(state):
        names = {0: 'none',
                 1: 'idle',
                 2: 'connect',
                 3: 'active',
                 4: 'opensned',
                 5: 'openconfirm',
                 6: 'established'}
        return names.get(state, 'unknown (%s)' % state)

    peer_prefixes, peer_table = parsed

    prefixes = peer_prefixes.get(item, None)

    alias = ''
    peer_not_found_state = 3

    for bgp_connection, bgp_alias, not_found_state in params.get('peer_list', []):
        if item == bgp_connection:
            alias = bgp_alias
            peer_not_found_state = not_found_state

    if prefixes:
        state = 0
        perfdata = []
        infotext = ''
        longoutput = ''

        minuptime = 86400
        if params.get('minuptime'):
            minuptime = params.get('minuptime')

        peer = peer_table.get(prefixes.get('remoteaddr'))

        if peer.get('localas') == 0:
            peer.update({'localas': params.get('useaslocalas')})

        infotext_values = params.get('infotext_values')

        if alias != '':
            infotext += ', Alias: %s' % alias
        if 'remoteid' in infotext_values:
            infotext += ', Remote ID: %s' % peer.get('remoteid')
        if 'remoteas' in infotext_values:
            infotext += ', Remote AS: %s' % peer.get('remoteas')
        if 'localaddr' in infotext_values:
            infotext += ', Local address: %s' % peer.get('localaddr')
        if 'localid' in infotext_values:
            infotext += ', Local ID: %s' % peer.get('localid')
        if 'localas' in infotext_values:
            infotext += ', Local AS: %s' % peer.get('localas')

        peerstate = peer.get('state')
        adminstate = peer.get('adminstate')
        establishedtime = peer.get('fsmestablishedtime')

        if peerstate == 1:  # idle
            yield 2, 'Peer state: %s' % cisco_bgp_peerstate(peerstate)
        elif peerstate == 6:  # established
            if establishedtime >= minuptime:
                infotext = 'Uptime: %s' % bgp_render_uptime(establishedtime) + infotext
            else:
                yield 1, 'Uptime: %s is less then min. uptime (%s)' % (bgp_render_uptime(establishedtime), bgp_render_uptime(minuptime))

        else:  # everything else
            yield 1, 'Peer state: %s' % cisco_bgp_peerstate(peerstate)

        if not adminstate == 2:  # not start
            yield 1, 'Admin state: %s' % cisco_bgp_adminstate(adminstate)

        bgptype = ''
        if not peer.get('localas') == 0:
            if peer.get('remoteas') == peer.get('localas'):
                bgptype = ' (iBGP)'
            else:
                bgptype = ' (eBGP)'

        acceptedprefixes = prefixes.get('acceptedprefixes', 0)
        prefixadminlimit = prefixes.get('prefixadminlimit', 0)
        prefixthreshold = prefixes.get('prefixthreshold', 0)
        warnthreshold = prefixadminlimit / 100.0 * prefixthreshold  # use float (100.0) to get xx.xx in division

        longoutput_data = [
            ['IP-address (remote/local)', peer.get('remoteaddr'), peer.get('localaddr')],
            ['Router-ID (remote/local)', peer.get('remoteid'), peer.get('localid')],
            ['Autonomus System (remote/local)', peer.get('remoteas'), str(peer.get('localas')) + bgptype],
            ['State', cisco_bgp_peerstate(peerstate), ''],
            ['Admin state', cisco_bgp_adminstate(adminstate), ''],
            ['Last error', peer.get('lasterror'), ''],
            ['Last error text', peer.get('lasterrortxt'), ''],
            ['Previous state', cisco_bgp_peerstate(peer.get('prevstate')), ''],
thl-cmk's avatar
thl-cmk committed
            ['Address family name', prefixes.get('addrfamilyname', 'unknown'), ''],
thl-cmk's avatar
thl-cmk committed
            ['Prefix admin limit (prefixes)', '%.0d' % prefixadminlimit, ''],
            ['Prefix threshold (prefixes/%)', '%.0d' % warnthreshold, '%.0d' % prefixthreshold],
thl-cmk's avatar
thl-cmk committed
            ['Prefix clear threshold (%)', '%.0d' % prefixes.get('prefixclearthreshold', 0), '']
thl-cmk's avatar
thl-cmk committed
           ]

        if params.get('htmloutput', False):
            #
            # disable 'Escape HTML codes in plugin output' in wato --> global settings
            #
            table_bracket = '<table>%s</table>'
            line_bracket = '<tr>%s</tr>'
            cell_bracket = '<td>%s</td><td>%s</td><td>%s</td>'
            cell_seperator = ''

            longoutput = '\n' + table_bracket % (''.join([line_bracket % cell_seperator.join([cell_bracket % (entry[0], entry[1], entry[2])]) for entry in longoutput_data]))
        else:
            longoutput += '\nfor nicer output' \
thl-cmk's avatar
thl-cmk committed
                          '\ndisable \'Escape HTML codes in plugin output\' in wato --> global settings and enable HTML output in \'Parameters for this service\''
thl-cmk's avatar
thl-cmk committed
            for entry in longoutput_data:
                if not entry[2] == '':
                    longoutput += '\n{}: {} / {}'.format(entry[0], entry[1], entry[2])
                else:
                    longoutput += '\n{}: {}'.format(entry[0], entry[1])

        if prefixadminlimit > 0:
            if acceptedprefixes >= prefixadminlimit:
                state = max(2, state)
                infotext = 'Prefix admin limit reached(!!) ' + infotext
            elif prefixthreshold > 0:
                perfdata.append(('acceptedprefixes', acceptedprefixes, warnthreshold, prefixadminlimit))
                if acceptedprefixes >= warnthreshold:
                    state = max(1, state)
                    infotext = 'Prefix warn threshold reached(!) ' + infotext
            else:
                perfdata.append(('acceptedprefixes', acceptedprefixes, '', prefixadminlimit))
        else:
            perfdata.append(('acceptedprefixes', acceptedprefixes))

        now_time = time.time()
        rate_item = item.replace(' ', '_')

thl-cmk's avatar
thl-cmk committed
        deniedprefixes = get_rate('cisco_bgp_peer.%s.%s' % ('deniedprefixes', rate_item), now_time, prefixes.get('deniedprefixes', 0), onwrap=SKIP)
        advertisedprefixes = get_rate('cisco_bgp_peer.%s.%s' % ('advertisedprefixes', rate_item), now_time, prefixes.get('advertisedprefixes', 0), onwrap=SKIP)
        withdrawnprefixes = get_rate('cisco_bgp_peer.%s.%s' % ('withdrawnprefixes', rate_item), now_time, prefixes.get('withdrawnprefixes', 0), onwrap=SKIP)
        suppressedprefixes = get_rate('cisco_bgp_peer.%s.%s' % ('suppressedprefixes', rate_item), now_time, prefixes.get('suppressedprefixes', 0), onwrap=SKIP)
thl-cmk's avatar
thl-cmk committed

thl-cmk's avatar
thl-cmk committed
        inupdates = get_rate('cisco_bgp_peer.%s.%s' % ('inupdates', rate_item), now_time, peer.get('inupdates', 0), onwrap=SKIP)
        outupdates = get_rate('cisco_bgp_peer.%s.%s' % ('outupdates', rate_item), now_time, peer.get('outupdates', 0), onwrap=SKIP)
thl-cmk's avatar
thl-cmk committed

thl-cmk's avatar
thl-cmk committed
        intotalmessages = get_rate('cisco_bgp_peer.%s.%s' % ('intotalmessages', rate_item), now_time, peer.get('intotalmessages', 0), onwrap=SKIP)
        outtotalmessages = get_rate('cisco_bgp_peer.%s.%s' % ('outtotalmessages', rate_item), now_time, peer.get('outtotalmessages', 0), onwrap=SKIP)
thl-cmk's avatar
thl-cmk committed

        # prefixes
        # 'unit', <value>, <warn-at>, <crit-at>, <min value>, <max value>
        perfdata.append(('deniedprefixes', deniedprefixes))
        perfdata.append(('advertisedprefixes', advertisedprefixes))
        perfdata.append(('withdrawnprefixes', withdrawnprefixes))
        perfdata.append(('suppressedprefixes', suppressedprefixes))

        perfdata.append(('inupdates', inupdates))
        perfdata.append(('outupdates', outupdates))
        perfdata.append(('intotalmessages', intotalmessages))
        perfdata.append(('outtotalmessages', outtotalmessages))

        perfdata.append(('fsmestablishedtransitions', peer.get('fsmestablishedtransitions')))
        perfdata.append(('fsmestablishedtime', peer.get('fsmestablishedtime')))
        perfdata.append(('inupdateelapsedtime', peer.get('inupdateelapsedtime')))

        if infotext[0:2] == ', ':
            infotext = infotext[2:]

        yield 0, infotext + longoutput, perfdata
    else:
        infotext = 'Item not found in SNMP data'
        if alias != '':
            infotext += ', Alias: %s' % alias

        yield peer_not_found_state, infotext


###########################################################################
#
#  CHECK info
#
###########################################################################


check_info['cisco_bgp_peer'] = {
    'check_function'          : check_cisco_bgp_peer,
    'inventory_function'      : inventory_cisco_bgp_peer,
    'service_description'     : 'Cisco BGP peer %s',
    'has_perfdata'            : True,
    'group'                   : 'cisco_bgp_peer',
    'default_levels_variable' : 'cisco_bgp_peer_defaults',
    'parse_function'          : parse_cisco_bgp_peer,
    'snmp_scan_function'      : lambda oid: 'cisco'in oid('.1.3.6.1.2.1.1.1.0').lower(),
    'snmp_info'               : [
thl-cmk's avatar
thl-cmk committed
                                 ('.1.3.6.1.4.1.9.9.187.1.2.5.1',  # CISCO-BGP4-MIB::cbgpPeer2Entry
thl-cmk's avatar
thl-cmk committed
                                  [OID_END,
                                   '3',   # cbgpPeer2State
                                   '4',   # cbgpPeer2AdminStatus
                                   '6',   # cbgpPeer2LocalAddr
                                   '8',   # cbgpPeer2LocalAs -> empty
                                   '9',   # cbgpPeer2LocalIdentifier
                                   '11',  # cbgpPeer2RemoteAs
                                   '12',  # cbgpPeer2RemoteIdentifier
                                   '13',  # cbgpPeer2InUpdates
                                   '14',  # cbgpPeer2OutUpdates
                                   '15',  # cbgpPeer2InTotalMessages
                                   '16',  # cbgpPeer2OutTotalMessages
                                   '17',  # cbgpPeer2LastError
                                   '18',  # cbgpPeer2FsmEstablishedTransitions
                                   '19',  # cbgpPeer2FsmEstablishedTime
                                   '27',  # cbgpPeer2InUpdateElapsedTime
                                   '28',  # cbgpPeer2LastErrorTxt
                                   '29',  # cbgpPeer2PrevState
                                   ]),
                                 ('.1.3.6.1.4.1.9.9.187.1.2',  # cbgpPeer
                                  [OID_END,  #
                                              # .7.1 --> cbgpPeer2AddrFamilyEntry
                                   '7.1.3',   # cbgpPeer2AddrFamilyName
                                              # .8.1 --> cbgpPeer2AddrFamilyPrefixEntry
                                   '8.1.1',   # cbgpPeer2AcceptedPrefixes
                                   '8.1.2',   # cbgpPeer2DeniedPrefixes
                                   '8.1.3',   # cbgpPeer2PrefixAdminLimit
                                   '8.1.4',   # cbgpPeer2PrefixThreshold
                                   '8.1.5',   # cbgpPeer2PrefixClearThreshold
                                   '8.1.6',   # cbgpPeer2AdvertisedPrefixes
                                   '8.1.7',   # cbgpPeer2SuppressedPrefixes
                                   '8.1.8',   # cbgpPeer2WithdrawnPrefixes
                                   ]),
thl-cmk's avatar
thl-cmk committed
         ]

thl-cmk's avatar
thl-cmk committed
}