diff --git a/agent_based/cisco_bgp_peer.py b/agent_based/cisco_bgp_peer.py index 794b3f82efa1b7605abd759b9db646069b92e4c2..7aeb6768ff1383f39fe690bd2e4b0a07aba1cb4d 100644 --- a/agent_based/cisco_bgp_peer.py +++ b/agent_based/cisco_bgp_peer.py @@ -48,8 +48,13 @@ # 2021-11-14: merged check function with bgp_peer # merged parse function with bgp_peer # added basic support for VRFs (cbgpPeer3Entry) +# 2022.04.22: moved inventory plugin to inv_cisco_bgp_peer.py +# 2022-04-29: added info if device is admin prefix limit capable (device_admin_limit) +# 2022-04-30: code cleanup/streamlining +# -from typing import List, Dict, Optional +from typing import List, Dict, Optional, Tuple +from dataclasses import dataclass from cmk.base.plugins.agent_based.agent_based_api.v1 import ( register, @@ -66,11 +71,7 @@ from cmk.base.plugins.agent_based.agent_based_api.v1.type_defs import ( ) from cmk.base.plugins.agent_based.utils.bgp_peer import ( - bgp_peerstate, - bgp_errors, - bgp_render_ip_address, - bgp_get_peer, - ByteToHex, + bgp_get_ip_address_from_oid, BgpPeer, bgp_get_peer_entry, ) @@ -80,35 +81,38 @@ from cmk.base.plugins.agent_based.utils.bgp_peer import ( # cisco_bgp_peer (CISCO-BGP4-MIB:cbgpPeer2Entry) # ########################################################################### +@dataclass +class CiscoPrefixes: + address_family: str + metric_count: List[Tuple[str, int]] + metric_rate: List[Tuple[str, int]] + accepted_prefixes: Optional[int] + prefix_admin_limit: Optional[int] + prefix_clear_threshold: Optional[int] + prefix_threshold: Optional[int] def parse_cisco_bgp_peer(string_table: List[StringTable]) -> Dict[str, BgpPeer]: cbgpPeer2Entry, cbgpPeer2AddrFamily = string_table - # create dictionary from cbgpPeer2Entry (peer ip address as index) peer_table = {} - for entry in cbgpPeer2Entry: - entry[0] = bgp_get_peer(entry[0]) # replace OID_END with remote address - bgp_peer = bgp_get_peer_entry(entry) - if bgp_peer: - peer_table.update(bgp_peer) - - prefixes_table = {} - # create dictionary from cbgpPeer2AddrFamily (with 'remoteip addrfamilyname' as index) + peer_prefixes = {} + # create dictionary by peer address and list of entries per address family for entry in cbgpPeer2AddrFamily: oid_end, addrfamilyname, acceptedprefixes, deniedprefixes, prefixadminlimit, prefixthreshold, \ prefixclearthreshold, advertisedprefixes, suppressedprefixes, withdrawnprefixes = entry if not oid_end == '': - - remoteaddr = bgp_get_peer(oid_end) - # merge peer info with prefixes - prefixes = peer_table[remoteaddr] - prefixes.prefix_admin_limit = int(prefixadminlimit) if prefixadminlimit.isdigit() else None - prefixes.accepted_prefixes = int(acceptedprefixes) if acceptedprefixes.isdigit() else None - prefixes.prefix_clear_threshold = int(prefixclearthreshold) if prefixclearthreshold.isdigit() else None - prefixes.prefix_threshold = int(prefixthreshold) if prefixthreshold.isdigit() else None - + remote_addr = bgp_get_ip_address_from_oid(oid_end) + prefixes = CiscoPrefixes( + address_family=addrfamilyname, + metric_count=[], + metric_rate=[], + accepted_prefixes=int(acceptedprefixes) if acceptedprefixes.isdigit() else None, + prefix_admin_limit=int(prefixadminlimit) if prefixadminlimit.isdigit() else None, + prefix_clear_threshold=int(prefixthreshold) if prefixthreshold.isdigit() else None, + prefix_threshold=int(prefixthreshold) if prefixthreshold.isdigit() else None + ) for key, value in [ ('advertisedprefixes', advertisedprefixes), ('deniedprefixes', deniedprefixes), @@ -120,20 +124,34 @@ def parse_cisco_bgp_peer(string_table: List[StringTable]) -> Dict[str, BgpPeer]: except ValueError: pass - prefixes_table.update({f'{remoteaddr} {addrfamilyname}': prefixes}) + if peer_prefixes.get(remote_addr): + peer_prefixes[remote_addr].append(prefixes) + else: + peer_prefixes[remote_addr] = [prefixes] + + # create dictionary from cbgpPeer2Entry (peer ip address as index) + peer_table: Dict[str, BgpPeer] = {} + for entry in cbgpPeer2Entry: + remote_address = bgp_get_ip_address_from_oid(entry[0]) + entry[0] = remote_address # replace OID_END with remote address + bgp_peer = bgp_get_peer_entry(entry) + if bgp_peer: + prefixes = peer_prefixes.get(remote_address, []) + if prefixes: + for address_family in prefixes: + item = f'{remote_address} {address_family.address_family}' + peer_table[item] = bgp_peer[remote_address] + peer_table[item].accepted_prefixes = address_family.accepted_prefixes + peer_table[item].prefix_admin_limit = address_family.prefix_admin_limit + peer_table[item].prefix_clear_threshold = address_family.prefix_clear_threshold + peer_table[item].prefix_threshold = address_family.prefix_threshold + peer_table[item].device_admin_limit = True + peer_table[item].metric_rate += address_family.metric_count + peer_table[item].metric_rate += address_family.metric_rate + else: + peer_table.update(bgp_peer) - if prefixes_table: - for peer in peer_table.keys(): - no_address_family = False - for address_family in prefixes_table.keys(): - if address_family.startswith(peer): - no_address_family = True - break - if not no_address_family: - prefixes_table.update({peer:peer_table[peer]}) - return prefixes_table - else: - return peer_table + return peer_table register.snmp_section( @@ -188,7 +206,7 @@ register.snmp_section( ########################################################################### -# ToDo: add support for non ipv6, needs sample +# ToDo: add support for non IPv4, needs sample def parse_cisco_bgp_peer_3(string_table: StringByteTable) -> Optional[Dict[str, BgpPeer]]: peer_table = {} for entry in string_table: @@ -257,121 +275,3 @@ register.snmp_section( # '30', # cbgpPeer3LastErrorTxt # '31', # cbgpPeer3PrevState -########################################################################### -# -# INVENTORY Plugin inv_cisco_bgp_peer (CISCO-BGP4-MIB::cbgpPeer2Entry) -# -########################################################################### - - -def parse_inv_cisco_bgp_peer(string_table: List[StringTable]): - peers, address_family, base = string_table - - bgp_peers = [] - - # convert families from list to dict - # [ - # ['1.4.77.235.182.229.1.1', 'IPv4 Unicast'], - # ['1.4.217.119.208.2.1.1', 'IPv4 Unicast'], - # ['1.4.217.119.208.34.1.1', 'IPv4 Unicast'], - # ['2.16.42.0.28.160.16.0.1.53.0.0.0.0.0.0.0.1.2.1', 'IPv6 Unicast'], - # ['2.16.42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.17.2.1', 'IPv6 Unicast'] - # ] - # - # { - # '1.4.77.235.182.229.1.1': 'IPv4 Unicast', - # '1.4.217.119.208.2.1.1': 'IPv4 Unicast', - # '1.4.217.119.208.34.1.1': 'IPv4 Unicast', - # '2.16.42.0.28.160.16.0.1.53.0.0.0.0.0.0.0.1.2.1': 'IPv6 Unicast', - # '2.16.42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.17.2.1': 'IPv6 Unicast' - # } - peer_families = dict(map(lambda x: (x[0], x[1]), address_family)) - - for entry in peers: - try: - oid_end, local_addr, local_as, local_id, remote_as, remote_id, last_error, last_errortxt, \ - prev_state = entry - except ValueError: - return - - if local_as == '0': - try: - local_as = base[0][0] - except (ValueError, IndexError): - local_as = 'N/A' - - address_families = [] - for key in peer_families: - if key.startswith(oid_end): - address_families.append(peer_families[key]) - - peer = { - 'remote_addr': bgp_get_peer(oid_end), - 'remote_id': remote_id, - 'version': '4', - 'local_addr': bgp_render_ip_address(local_addr), - 'remote_as': remote_as, - 'local_as': local_as, - 'local_id': local_id, - 'status_columns': { - 'last_error_code': ByteToHex(last_error), - 'last_error': bgp_errors(last_error), - 'prev_state': bgp_peerstate(prev_state), - }, - } - - if local_as.isdigit(): - if local_as == remote_as: - peer['bgp_type'] = 'iBGP' - else: - peer['bgp_type'] = 'eBGP' - else: - peer['bgp_type'] = 'N/A' - - if address_families: - peer['address_family'] = ','.join(address_families) - - bgp_peers.append(peer) - - return bgp_peers - - -register.snmp_section( - name='inv_cisco_bgp_peer', - parse_function=parse_inv_cisco_bgp_peer, - parsed_section_name='inv_bgp_peer', - supersedes=['inv_bgp_peer'], - fetch=[ - SNMPTree( - base='.1.3.6.1.4.1.9.9.187.1.2.5.1', # CISCO-BGP4-MIB::cbgpPeer2Entry - oids=[ - OIDEnd(), - '6', # cbgpPeer2LocalAddr - '8', # cbgpPeer2LocalAs -> empty - '9', # cbgpPeer2LocalIdentifier - '11', # cbgpPeer2RemoteAs - '12', # cbgpPeer2RemoteIdentifier - '17', # cbgpPeer2LastError - '28', # cbgpPeer2LastErrorTxt - '29', # cbgpPeer2PrevState - ] - ), - SNMPTree( - base='.1.3.6.1.4.1.9.9.187.1.2', # cbgpPeer - oids=[ - OIDEnd(), # - '7.1.3', # cbgpPeer2AddrFamilyName - ] - ), - SNMPTree( - base='.1.3.6.1.2.1.15', # BGP-4-MIB - oids=[ - '2', # bgpLocalAs - ] - ) - ], - detect=all_of( - contains('.1.3.6.1.2.1.1.1.0', 'Cisco'), - exists('.1.3.6.1.4.1.9.9.187.1.2.5.1.3.*') - ), -) diff --git a/agent_based/inv_cisco_bgp_peer.py b/agent_based/inv_cisco_bgp_peer.py new file mode 100644 index 0000000000000000000000000000000000000000..d13f459377a866277f382753a6c2b8a2816b4c6d --- /dev/null +++ b/agent_based/inv_cisco_bgp_peer.py @@ -0,0 +1,135 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# License: GNU General Public License v2 +# +# Author: thl-cmk[at]outlook[dot]com +# URL : https://thl-cmk.hopto.org +# Date : 2020-04-22 +# +# inventory of Cisco BGP Peers (IPv4 and IPv6) +# +# 2022-04-22: moved here from cisco_bgp_peer +# 2022-04-30: code cleanup/streamlining +# + +from typing import List +from cmk.base.plugins.agent_based.agent_based_api.v1 import ( + register, + SNMPTree, + exists, + contains, + all_of, + OIDEnd, + OIDBytes, +) +from cmk.base.plugins.agent_based.agent_based_api.v1.type_defs import ( + StringByteTable +) +from cmk.base.plugins.agent_based.utils.bgp_peer import ( + InvBgpPeer, + bgp_error_as_string, + bgp_render_ip_address, + bgp_get_ip_address_from_oid, + bgp_error_code_as_hex, + get_bgp_type, +) + + +def parse_inv_cisco_bgp_peer(string_table: List[StringByteTable]): + peers, address_family, base = string_table + try: + base_local_as = base[0][0] + except (ValueError, IndexError): + base_local_as = 'N/A' + + bgp_peers = [] + + peer_families = dict(address_family) + + for entry in peers: + try: + oid_end, state, local_addr, local_as, local_id, remote_as, remote_id, last_error, \ + fsm_established_time, last_errortxt = entry + except ValueError: + return + + if len(local_addr) == 4: + local_addr_type = '1' # IPv4 + elif len(local_addr) == 16: + local_addr_type = '2' # IPv6 + else: + local_addr_type = '0' + + if local_as == '0': + local_as = base_local_as + + address_families = [] + for key in peer_families: + if key.startswith(oid_end): + address_families.append(peer_families[key]) + + bgp_peer: InvBgpPeer = { + 'remote_addr': bgp_get_ip_address_from_oid(oid_end), + 'remote_id': remote_id, + 'version': '4', + 'local_addr': bgp_render_ip_address(local_addr_type, local_addr), + 'remote_as': remote_as, + 'local_as': local_as, + 'local_id': local_id, + 'bgp_type': get_bgp_type(local_as, remote_as), + 'fsm_established_time': int(fsm_established_time), + 'peer_state': 1 if state == '6' else 2, # adjust to match if_oper_status for inventory painter + 'last_error_code': bgp_error_code_as_hex(last_error), + 'last_error': bgp_error_as_string(last_error), + 'status_columns': {}, + 'address_family': ','.join(address_families) + } + + bgp_peers.append(bgp_peer) + + return bgp_peers + + +register.snmp_section( + name='inv_cisco_bgp_peer', + parse_function=parse_inv_cisco_bgp_peer, + parsed_section_name='inv_bgp_peer', + supersedes=['inv_bgp_peer'], + fetch=[ + SNMPTree( + base='.1.3.6.1.4.1.9.9.187.1.2.5.1', # CISCO-BGP4-MIB::cbgpPeer2Entry + oids=[ + OIDEnd(), + '3', # cbgpPeer2State + OIDBytes('6'), # cbgpPeer2LocalAddr + '8', # cbgpPeer2LocalAs -> empty + '9', # cbgpPeer2LocalIdentifier + '11', # cbgpPeer2RemoteAs + '12', # cbgpPeer2RemoteIdentifier + OIDBytes('17'), # cbgpPeer2LastError + '19', # cbgpPeer2FsmEstablishedTime + '28', # cbgpPeer2LastErrorTxt + ] + ), + SNMPTree( + base='.1.3.6.1.4.1.9.9.187.1.2', # cbgpPeer + oids=[ + OIDEnd(), # + '7.1.3', # cbgpPeer2AddrFamilyName + ] + ), + SNMPTree( + base='.1.3.6.1.2.1.15', # BGP-4-MIB + oids=[ + '2', # bgpLocalAs + ] + ) + ], + detect=all_of( + contains('.1.3.6.1.2.1.1.1.0', 'Cisco'), + exists('.1.3.6.1.4.1.9.9.187.1.2.5.1.3.*') + ), +) + + diff --git a/cisco_bgp_peer.mkp b/cisco_bgp_peer.mkp index c942943cd31c75950e96fad95d0e1282fee88992..19858fe91a3c82956d309e90955c2de15c8de518 100644 Binary files a/cisco_bgp_peer.mkp and b/cisco_bgp_peer.mkp differ diff --git a/packages/cisco_bgp_peer b/packages/cisco_bgp_peer index 414d2e7b88a820e13a5cc618037de5d504d30a5e..f44dd4f6aa0224d450c26a432ec12dfed60f956c 100644 --- a/packages/cisco_bgp_peer +++ b/packages/cisco_bgp_peer @@ -21,12 +21,12 @@ 'v0.5: merged with bgp_peer ' '(https://thl-cmk.hopto.org/gitlab/checkmk/vendor-independent/bgp_peer)\n', 'download_url': 'https://thl-cmk.hopto.org', - 'files': {'agent_based': ['cisco_bgp_peer.py'], + 'files': {'agent_based': ['cisco_bgp_peer.py', 'inv_cisco_bgp_peer.py'], 'checkman': ['cisco_bgp_peer']}, 'name': 'cisco_bgp_peer', - 'num_files': 2, + 'num_files': 3, 'title': 'Cisco BGP Peer', - 'version': '20211114.v.0.5', + 'version': '20220422.v.0.6', 'version.min_required': '2.0.0', 'version.packaged': '2021.09.20', 'version.usable_until': None} \ No newline at end of file