diff --git a/agent_based/arista_bgp_peer.py b/agent_based/arista_bgp_peer.py new file mode 100644 index 0000000000000000000000000000000000000000..9570c4b25b670364607cb2856cf8416c2400cd25 --- /dev/null +++ b/agent_based/arista_bgp_peer.py @@ -0,0 +1,144 @@ +#!/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 : 2023-01-16 +# +# Monitor status of Arista Networks BGP Peers (IPv4 and IPv6) +# +# +# + +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_get_ip_address_from_oid, +) + +from cmk.base.plugins.agent_based.utils.arista_bgp_peer import ( + arista_afi_safi_mapping, +) + + +@dataclass +class AristaPrefixes: + afi: str + safi: str + accepted_prefixes: int + metric_count: List[Tuple[str, int]] + metric_rate: List[Tuple[str, int]] + + +def parse_arista_bgp_peer(string_table: List[StringByteTable]) -> Optional[Dict[str, BgpPeer]]: + BgpPeerTable, BgpPrefixCountersTable = string_table + + peer_table = {} + peer_prefixes = {} + + for entry in BgpPrefixCountersTable: + oid_end, in_prefixes, in_prefixes_accepted, out_prefixes = entry + oid_end_split = oid_end.split('.') + afi = oid_end_split[-2] + safi = oid_end_split[-1] + index = '.'.join(oid_end_split[:-2]) + prefixes = AristaPrefixes( + afi=afi, + safi=safi, + accepted_prefixes=int(in_prefixes_accepted), + metric_rate=[], + metric_count=[ + ('in_prefixes', int(in_prefixes)), + ('out_prefixes', int(out_prefixes)), + ], + ) + + if peer_prefixes.get(index): + peer_prefixes[index].append(prefixes) + else: + peer_prefixes[index] = [prefixes] + + for entry in BgpPeerTable: + oid_end, remote_as, remote_id, admin_state, peer_state, in_updates, out_updates, in_messages, out_messages, \ + fsm_established_transitions, fsm_established_time, in_update_elapsed_time = entry + + remote_addr = bgp_get_ip_address_from_oid(f'{oid_end[2:]}') + + bgp_peer = bgp_get_peer_entry( + remote_addr=remote_addr, + peer_state=peer_state, + admin_state=admin_state, + remote_as=remote_as, + in_updates=in_updates, + out_updates=out_updates, + in_messages=in_messages, + out_messages=out_messages, + fsm_established_transitions=fsm_established_transitions, + fsm_established_time=fsm_established_time, + in_update_elapsed_time=in_update_elapsed_time + ) + + prefixes = peer_prefixes.get(oid_end, []) + for address_family in prefixes: + address_family_str = arista_afi_safi_mapping(address_family.afi, address_family.safi) + item = f'{remote_addr} {address_family_str}' + peer_table[item] = copy.deepcopy(bgp_peer[remote_addr]) + 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'] = '' + return peer_table + + +register.snmp_section( + name='arista_bgp_peer', + parse_function=parse_arista_bgp_peer, + parsed_section_name='bgp_peer', + supersedes=['bgp_peer', 'arista_bgp'], + fetch=[ + SNMPTree( + base='.1.3.6.1.4.1.30065.4.1.1', # AARISTA-BGP4V2-MIB::aristaBgp4V2Objects + oids=[ + OIDEnd(), + '2.1.10', # aristaBgp4V2PeerRemoteAs + OIDBytes('2.1.11'), # aristaBgp4V2PeerRemoteIdentifier + '2.1.12', # aristaBgp4V2PeerAdminStatus + '2.1.13', # aristaBgp4V2PeerState + '7.1.1', # aristaBgp4V2PeerInUpdates + '7.1.2', # aristaBgp4V2PeerOutUpdates + '7.1.3', # aristaBgp4V2PeerInTotalMessages + '7.1.4', # aristaBgp4V2PeerOutTotalMessages + '7.1.5', # aristaBgp4V2PeerFsmEstablishedTransitions + '4.1.1', # aristaBgp4V2PeerFsmEstablishedTime + '4.1.2', # aristaBgp4V2PeerInUpdatesElapsedTime + ]), + SNMPTree( + base='.1.3.6.1.4.1.30065.4.1.1.8.1', # ARISTA-BGP4V2-MIB::aristaBgp4V2PrefixGaugesEntry + oids=[ + OIDEnd(), # index.afi.safi + '3', # aristaBgp4V2PrefixInPrefixes + '4', # aristaBgp4V2PrefixInPrefixesAccepted + '5', # aristaBgp4V2PrefixOutPrefixes + ] + ), + ], + detect=startswith('.1.3.6.1.2.1.1.2.0', '.1.3.6.1.4.1.30065'), # ARISTA-SMI-MIB::arista +), diff --git a/agent_based/inv_arista_bgp_peer.py b/agent_based/inv_arista_bgp_peer.py new file mode 100644 index 0000000000000000000000000000000000000000..b6380f9bb1368473242911d58420d07f9869ff3e --- /dev/null +++ b/agent_based/inv_arista_bgp_peer.py @@ -0,0 +1,119 @@ +#!/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 : 2023-01-17 +# +# inventory of Arista Networks BGP Peers (IPv4 and IPv6) +# +# + + +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, + bgp_get_ip_address_from_oid, +) +from cmk.base.plugins.agent_based.utils.arista_bgp_peer import ( + arista_afi_safi_mapping, +) + + +def parse_inv_arista_bgp_peer(string_table: List[StringByteTable]): + peers, address_families = string_table + + bgp_peers = [] + + peer_families = {} + for entry in address_families: + oid_end, in_prefixes = entry + oid_end_split = oid_end.split('.') + afi = oid_end_split[-2] + safi = oid_end_split[-1] + index = '.'.join(oid_end_split[:-2]) + + if peer_families.get(index): + peer_families[index] = peer_families[index] + [arista_afi_safi_mapping(afi, safi)] + else: + peer_families[index] = [arista_afi_safi_mapping(afi, safi)] + + for entry in peers: + oid_end, local_addr_type, local_addr, local_as, local_id, remote_as, remote_id, state, last_error_received, \ + fsm_established_time, fsm_established_trans = entry + + remote_addr = bgp_get_ip_address_from_oid(f'{oid_end[2:]}') + + # assuming IPv4 address format + local_id = bgp_render_ip_address('1', local_id) + remote_id = bgp_render_ip_address('1', remote_id) + + bgp_peer: InvBgpPeer = { + 'remote_addr': remote_addr, + '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_received), + 'last_error': bgp_error_as_string(last_error_received), + 'status_columns': {}, + 'address_family': ', '.join(peer_families.get(oid_end, [])) + } + bgp_peers.append(bgp_peer) + + return bgp_peers + + +register.snmp_section( + name='inv_arista_bgp_peer', + parse_function=parse_inv_arista_bgp_peer, + parsed_section_name='inv_bgp_peer', + supersedes=['inv_bgp_peer'], + fetch=[ + SNMPTree( + base='.1.3.6.1.4.1.30065.4.1.1', # AARISTA-BGP4V2-MIB::aristaBgp4V2Objects + oids=[ + OIDEnd(), + '2.1.2', # aristaBgp4V2PeerLocalAddrType + OIDBytes('2.1.3'), # aristaBgp4V2PeerLocalAddr + '2.1.7', # aristaBgp4V2PeerLocalAs + OIDBytes('2.1.8'), # aristaBgp4V2PeerLocalIdentifier + '2.1.10', # aristaBgp4V2PeerRemoteAs + OIDBytes('2.1.11'), # aristaBgp4V2PeerRemoteIdentifier + '2.1.13', # aristaBgp4V2PeerState + OIDBytes('3.1.1'), # aristaBgp4V2PeerLastErrorCodeReceived + '4.1.1', # aristaBgp4V2PeerFsmEstablishedTime + '7.1.5', # aristaBgp4V2PeerFsmEstablishedTransitions + ] + ), + SNMPTree( + base='.1.3.6.1.4.1.30065.4.1.1.8.1', # ARISTA-BGP4V2-MIB::aristaBgp4V2PrefixGaugesEntry + oids=[ + OIDEnd(), # index.addr-type.addr_length.addr.afi[-2].safi[-1] + '3', # aristaBgp4V2PrefixInPrefixes + ] + ), + ], + detect=startswith('.1.3.6.1.2.1.1.2.0', '.1.3.6.1.4.1.30065'), # ARISTA-SMI-MIB::arista +), diff --git a/agent_based/utils/arista_bgp_peer.py b/agent_based/utils/arista_bgp_peer.py new file mode 100644 index 0000000000000000000000000000000000000000..ac76ea645556a8098f6bb1c5b94dbe241504bdf0 --- /dev/null +++ b/agent_based/utils/arista_bgp_peer.py @@ -0,0 +1,41 @@ +#!/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 : 2023-01-16 +# +# include file, will be used with arista_bgp_peer and inv_arista_bgp_peer +# +# + +def arista_address_type(st: str): + names = { + '0': 'unknown', + '1': 'ipv4', + '2': 'ipv6', + } + return names.get(st, st) + + +def arista_address_family(st: str): + names = { + '0': 'unknown', + '1': 'ipv4', + '2': 'ipv6', + } + return names.get(st, st) + + +def arista_afi_safi_mapping(afi: str, safi: str) -> str: + names = { + (1, 1): 'IPv4 unicast', + (1, 2): 'IPv4 multicast', + (2, 1): 'IPv6 unicast', + (2, 2): 'IPv6 multicast', + (1, 4): 'MPLS IPv4', + (2, 4): 'MPLS IPv6', + } + return names.get((int(afi), int(safi)), f'AFI {afi} SAFI {safi}') diff --git a/arista_bgp_peer.mkp b/arista_bgp_peer.mkp new file mode 100644 index 0000000000000000000000000000000000000000..1908d9891cb3b68929da876be0815e96e0e4c532 Binary files /dev/null and b/arista_bgp_peer.mkp differ diff --git a/packages/arista_bgp_peer b/packages/arista_bgp_peer new file mode 100644 index 0000000000000000000000000000000000000000..9aaf4ed58e239b7ca4570a87496d50ffd1437546 --- /dev/null +++ b/packages/arista_bgp_peer @@ -0,0 +1,27 @@ +{'author': 'Th.L. (thl-cmk[at]outlook[dot]com)', + 'description': 'Monitors Arista Networks BGP Peers.\n' + '\n' + '- creates one service for each peer/address family (IPv4 ' + 'and IPv6)\n' + '- performance data include: peer uptime, prefixes accepted, ' + 'advertised,\n' + '- this package contains also a inventory plugin\n' + '\n' + 'NOTE: this plugin will supersede the original arista_bgp ' + 'plugin\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' + '\n', + 'download_url': 'https://thl-cmk.hopto.org/gitlab/arista/arista_bgp_peer', + 'files': {'agent_based': ['arista_bgp_peer.py', + 'inv_arista_bgp_peer.py', + 'utils/arista_bgp_peer.py']}, + 'name': 'arista_bgp_peer', + 'num_files': 3, + 'title': 'Arista BGP peer', + 'version': '20230117.v0.01', + 'version.min_required': '2.0.0', + 'version.packaged': '2021.09.20', + 'version.usable_until': None} \ No newline at end of file