diff --git a/agent_based/inv_juniper_bgp_peer.py b/agent_based/inv_juniper_bgp_peer.py new file mode 100644 index 0000000000000000000000000000000000000000..8fec6aed3c93c3d1efe62aa15b345b1e95cb221e --- /dev/null +++ b/agent_based/inv_juniper_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-29 +# +# inventory of Juniper BGP Peers (IPv4 and IPv6) +# +# THX to Jeff Fern jeff[dash]cmk[at]fcse[dot]co[dot]uk for testing and providing feedback to make this addon happen +# +# 2022-04-30: code cleanup/streamlining +# + + +from typing import List +from cmk.base.plugins.agent_based.agent_based_api.v1 import ( + register, + SNMPTree, + OIDBytes, + startswith, + OIDEnd, +) +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_error_code_as_hex, + bgp_render_ip_address, + get_bgp_type, +) +from cmk.base.plugins.agent_based.utils.juniper_bgp_peer import ( + juniper_afi_safi_mapping, +) + + +def parse_inv_juniper_bgp_peer(string_table: List[StringByteTable]): + peers, address_families, local_identifier = string_table + + bgp_peers = [] + + try: + local_id = local_identifier[0][0] + except (ValueError, IndexError): + local_id = 'N/A' + + peer_families = {} + for entry in address_families: + index, afi, safi = entry[0].split('.') + if peer_families.get(index): + peer_families[index] = peer_families[index] + [juniper_afi_safi_mapping(afi, safi)] + else: + peer_families[index] = [juniper_afi_safi_mapping(afi, safi)] + + for entry in peers: + oid_end, remote_id, state, negotiated_version, local_addr_type, local_addr, local_as, remote_addr_type, \ + remote_addr, remote_as, peer_index, routing_instance, fsm_established_time, fsm_established_trans, \ + last_error_received = entry + + # assuming IPv4 address format + remote_id = '.'.join([str(m) for m in remote_id]) + + bgp_peer: InvBgpPeer = { + 'remote_addr': bgp_render_ip_address(remote_addr_type, remote_addr), + 'remote_id': remote_id, + 'version': negotiated_version, + '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_received), + 'last_error': bgp_error_as_string(last_error_received), + 'status_columns': {}, + 'address_family': ', '.join(peer_families.get(peer_index, [])) + } + + bgp_peers.append(bgp_peer) + + return bgp_peers + + +register.snmp_section( + name='inv_juniper_bgp_peer', + parse_function=parse_inv_juniper_bgp_peer, + parsed_section_name='inv_bgp_peer', + supersedes=['inv_bgp_peer'], + fetch=[ + SNMPTree( + base='.1.3.6.1.4.1.2636.5.1.1.2', # BGP4-V2-MIB-JUNIPER::jnxBgpM2Peer + oids=[ + OIDEnd(), + OIDBytes('1.1.1.1'), # jnxBgpM2PeerIdentifier + '1.1.1.2', # jnxBgpM2PeerState + # '1.1.1.3', # jnxBgpM2PeerStatus + '1.1.1.4', # jnxBgpM2PeerConfiguredVersion + # '1.1.1.5', # jnxBgpM2PeerNegotiatedVersion --> is Null '0' :-( + '1.1.1.6', # jnxBgpM2PeerLocalAddrType + OIDBytes('1.1.1.7'), # jnxBgpM2PeerLocalAddr + # '1.1.1.8', # jnxBgpM2PeerLocalPort + '1.1.1.9', # jnxBgpM2PeerLocalAs + '1.1.1.10', # jnxBgpM2PeerRemoteAddrType + OIDBytes('1.1.1.11'), # jnxBgpM2PeerRemoteAddr + # '1.1.1.12', # jnxBgpM2PeerRemotePort + '1.1.1.13', # jnxBgpM2PeerRemoteAs + '1.1.1.14', # jnxBgpM2PeerIndex + '1.1.1.15', # jnxBgpM2PeerRoutingInstance + '4.1.1.1', # jnxBgpM2PeerFsmEstablishedTime + '6.1.1.5', # jnxBgpM2PeerFsmEstablishedTrans + OIDBytes('2.1.1.1'), # jnxBgpM2PeerLastErrorReceived + ] + ), + SNMPTree( + base='.1.3.6.1.4.1.2636.5.1.1.2.6.2.1', # BGP4-V2-MIB-JUNIPER::jnxBgpM2PrefixCountersEntry + oids=[ + OIDEnd(), # index.afi.safi + # '1', # jnxBgpM2PrefixCountersAfi + # '2', # jnxBgpM2PrefixCountersSafi + ] + ), + SNMPTree( + base='.1.3.6.1.4.1.2636.5.1.1.1', # BGP4-V2-MIB-JUNIPER::nxBgpM2BaseScalars + oids=[ + '6', # jnxBgpM2LocalIdentifier + ] + ) + ], + detect=startswith('.1.3.6.1.2.1.1.2.0', '.1.3.6.1.4.1.2636.1.1.1'), +), diff --git a/agent_based/juniper_bgp_peer.py b/agent_based/juniper_bgp_peer.py new file mode 100644 index 0000000000000000000000000000000000000000..091b82c737feb77cc98f68c4e9b1693689e0df58 --- /dev/null +++ b/agent_based/juniper_bgp_peer.py @@ -0,0 +1,160 @@ +#!/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 : 2022-04-27 +# +# Monitor status of Juniper BGP Peers (IPv4 and IPv6) +# +# THX to Jeff Fern jeff[dash]cmk[at]fcse[dot]co[dot]uk for testing and providing feedback to make this addon happen +# +# 2022-04-30: code cleanup/streamlining +# 2022-05-09: added item to BgpPeer class, this is used in the discovery function +# + +import copy +from typing import List, Dict, Optional, Tuple +from dataclasses import dataclass + +from cmk.base.plugins.agent_based.agent_based_api.v1 import ( + register, + SNMPTree, + startswith, + 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 ( + BgpPeer, + bgp_get_peer_entry, + bgp_render_ip_address, +) + +from cmk.base.plugins.agent_based.utils.juniper_bgp_peer import ( + juniper_afi_safi_mapping, +) + + +@dataclass +class JuniperPrefixes: + afi: str + safi: str + accepted_prefixes: int + metric_count: List[Tuple[str, int]] + metric_rate: List[Tuple[str, int]] + + +def parse_juniper_bgp_peer(string_table: List[StringByteTable]) -> Optional[Dict[str, BgpPeer]]: + jnxBgpM2Peer, jnxBgpM2PrefixCountersEntry = string_table + peer_table = {} + peer_prefixes = {} + + for entry in jnxBgpM2PrefixCountersEntry: + oid_end, in_prefixes, in_prefixes_accepted, in_prefixes_rejected, out_prefixes, in_prefixes_active = entry + index, afi, safi = oid_end.split('.') # peer index, afi, safi + prefixes = JuniperPrefixes( + afi=afi, + safi=safi, + accepted_prefixes=int(in_prefixes_accepted), + metric_rate=[], + metric_count=[ + ('in_prefixes', int(in_prefixes)), + # ('in_prefixes_accepted', int(in_prefixes_accepted)), + ('in_prefixes_rejected', int(in_prefixes_rejected)), + ('out_prefixes', int(out_prefixes)), + ('in_prefixes_active', int(in_prefixes_active)), + ], + ) + + if peer_prefixes.get(index): + peer_prefixes[index].append(prefixes) + else: + peer_prefixes[index] = [prefixes] + + for entry in jnxBgpM2Peer: + # extract values and adjust format + oid_end = entry[0] + entry.pop(0) + PeerRemoteAddrType = entry[0] + entry.pop(0) + PeerIndex = entry[0] + entry.pop(0) + PeerRoutingInstance = entry[0] + entry.pop(0) + + remote_address = bgp_render_ip_address(PeerRemoteAddrType, entry[0]) + entry[0] = remote_address + + bgp_peer = bgp_get_peer_entry(entry) + + prefixes = peer_prefixes.get(PeerIndex, []) + for address_family in prefixes: + address_family_str = juniper_afi_safi_mapping(address_family.afi, address_family.safi) + item = f'{remote_address} {address_family_str}' + # ToDo: add vrf name + routing_instance = '' + if PeerRoutingInstance != '0': + routing_instance = f'VR {PeerRoutingInstance}' + item = f'{item} {routing_instance}' + peer_table[item] = copy.deepcopy(bgp_peer[remote_address]) + peer_table[item].accepted_prefixes = address_family.accepted_prefixes + peer_table[item].metric_count += address_family.metric_count + peer_table[item].metric_rate += address_family.metric_rate + peer_table[item].item['address_family'] = address_family_str + peer_table[item].item['routing_instance'] = routing_instance + return peer_table + + +register.snmp_section( + name='juniper_bgp_peer', + parse_function=parse_juniper_bgp_peer, + parsed_section_name='bgp_peer', + supersedes=['bgp_peer'], + fetch=[ + SNMPTree( + base='.1.3.6.1.4.1.2636.5.1.1.2', # BGP4-V2-MIB-JUNIPER::jnxBgpM2Peer + oids=[ + OIDEnd(), + '1.1.1.10', # jnxBgpM2PeerRemoteAddrType + '1.1.1.14', # jnxBgpM2PeerIndex + '1.1.1.15', # jnxBgpM2PeerRoutingInstance + # needs to be in this order to match bgp_peer + OIDBytes('1.1.1.11'), # jnxBgpM2PeerRemoteAddr + '1.1.1.2', # jnxBgpM2PeerState + '1.1.1.3', # jnxBgpM2PeerStatus (admin status(?)) + '6.1.1.1', # jnxBgpM2PeerInUpdates + '6.1.1.2', # jnxBgpM2PeerOutUpdates + '6.1.1.3', # jnxBgpM2PeerInTotalMessages + '6.1.1.4', # jnxBgpM2PeerOutTotalMessages + '6.1.1.5', # jnxBgpM2PeerFsmEstablishedTrans + '4.1.1.1', # jnxBgpM2PeerFsmEstablishedTime + '4.1.1.2', # jnxBgpM2PeerInUpdatesElapsedTime + ] + ), + SNMPTree( + base='.1.3.6.1.4.1.2636.5.1.1.2.6.2.1', # BGP4-V2-MIB-JUNIPER::jnxBgpM2PrefixCountersEntry + oids=[ + OIDEnd(), # index.afi.safi + # '1', # jnxBgpM2PrefixCountersAfi + # '2', # jnxBgpM2PrefixCountersSafi + '7', # jnxBgpM2PrefixInPrefixes + '8', # jnxBgpM2PrefixInPrefixesAccepted + '9', # jnxBgpM2PrefixInPrefixesRejected + '10', # jnxBgpM2PrefixOutPrefixes + '11', # jnxBgpM2PrefixInPrefixesActive + ] + ), + ], + detect=startswith('.1.3.6.1.2.1.1.2.0', '.1.3.6.1.4.1.2636.1.1.1'), +), + +# jnxBgpM2CfgPeerAdminStatus +# 1.3.6.1.4.1.2636.5.1.1.2.8.1.1.1 +# stop(1) +# start(2) diff --git a/agent_based/utils/juniper_bgp_peer.py b/agent_based/utils/juniper_bgp_peer.py new file mode 100644 index 0000000000000000000000000000000000000000..612201fcfc24be37d37ed7a183787371f0c4b257 --- /dev/null +++ b/agent_based/utils/juniper_bgp_peer.py @@ -0,0 +1,62 @@ +#!/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 : 2022-04-29 +# +# include file, will be used with juniper_bgp_peer and inv_juniper_bgp_peer +# +# +from typing import List +import ipaddress + + +def juniper_address_type(st: str): + names = { + '0': 'unknown', + '1': 'ipv4', + '2': 'ipv6', + '3': 'ipv4z', + '4': 'ipv6z', + '16': 'dns' + } + return names.get(st, st) + + +def juniper_address_family(st: str): + names = { + '0': 'unknown', + '1': 'ipv4', + '2': 'ipv6', + '3': 'ipv4z', + '4': 'ipv6z', + '16': 'dns' + } + return names.get(st, st) + + +def juniper_afi_safi_mapping(afi: str, safi: str) -> str: + names = { + (1, 1): 'IPv4 unicast', + (1, 2): 'IPv4 multicast', + (1, 128): 'L3VPN IPv4 unicast', + (1, 129): 'L3VPN IPv4 multicast', + (2, 1): 'IPv6 unicast', + (2, 2): 'IPv6 multicast', + (25, 65): 'BGP-VPLS/BGP-L2VPN', + (2, 128): 'L3VPN IPv6 unicast', + (2, 129): 'L3VPN IPv6 multicast', + (1, 132): 'RT-Constrain', + (1, 133): 'Flow-spec', + (1, 134): 'Flow-spec', + (3, 128): 'CLNS VPN', + (1, 5): 'NG-MVPN IPv4', + (2, 5): 'NG-MVPN IPv6', + (1, 66): 'MDT-SAFI', + (1, 4): 'labeled IPv4', + (2, 4): 'labeled IPv6 (6PE)', + } + return names.get((int(afi), int(safi)), f'AFI {afi} SAFI {safi}') diff --git a/juniper_bgp_peer.mkp b/juniper_bgp_peer.mkp new file mode 100644 index 0000000000000000000000000000000000000000..deb13e7c84ba8a765353f2d94ef355c8ec59a9e5 Binary files /dev/null and b/juniper_bgp_peer.mkp differ diff --git a/packages/juniper_bgp_peer b/packages/juniper_bgp_peer new file mode 100644 index 0000000000000000000000000000000000000000..dd8fe81cd0b60d793f901e861ca3e11f9e70ef47 --- /dev/null +++ b/packages/juniper_bgp_peer @@ -0,0 +1,20 @@ +{'author': 'thl-cmk[at]outlook[dot]com', + 'description': 'Monitors Juniper BGP peers BGP4-V2-MIB-JUNIPER). \n' + '\n' + '- creates one service for each peer/address family (IPv4 and ' + 'IPv6)\n' + '\n' + 'NOTE: to use this plugin you must also install my bgp_peer" ' + 'plugin ' + '(https://thl-cmk.hopto.org/gitlab/checkmk/vendor-independent/bgp_peer)\n', + 'download_url': 'https://thl-cmk.hopto.org', + 'files': {'agent_based': ['juniper_bgp_peer.py', + 'inv_juniper_bgp_peer.py', + 'utils/juniper_bgp_peer.py']}, + 'name': 'juniper_bgp_peer', + 'num_files': 3, + 'title': 'Juniper BGP peer', + 'version': '20220509.v.0.0.4', + 'version.min_required': '2.0.0', + 'version.packaged': '2021.09.20', + 'version.usable_until': None} \ No newline at end of file