From 5c658ad530463c27fb10c76fa31ae29d957d4999 Mon Sep 17 00:00:00 2001 From: "th.l" <thl-cmk@outlook.com> Date: Sun, 1 May 2022 18:13:56 +0200 Subject: [PATCH] update project --- agent_based/cisco_bgp_peer.py | 212 ++++++++---------------------- agent_based/inv_cisco_bgp_peer.py | 135 +++++++++++++++++++ cisco_bgp_peer.mkp | Bin 5328 -> 5523 bytes packages/cisco_bgp_peer | 6 +- 4 files changed, 194 insertions(+), 159 deletions(-) create mode 100644 agent_based/inv_cisco_bgp_peer.py diff --git a/agent_based/cisco_bgp_peer.py b/agent_based/cisco_bgp_peer.py index 794b3f8..7aeb676 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 0000000..d13f459 --- /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 GIT binary patch literal 5523 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 J$11|Y`ac$9w;BKd literal 5328 zcmZ{jRX`H}qecga^yriZk<r}<laNk9n!zYR*Z@Z(-2zh5og%Gtcc;WCX=I~AQr!LT z^ZoDla30R{`B>uc@t@!K*kEIz?>wO}Yg<RSHQe6bgI`2QMEE}w6Yvrc5)c<~b%6tp zEe}0crIM!|AE{05a7vkCzHr}_%IN04aCflGma0LfRPzp(hAOLVhm-QNBm{ymr%UUs zw*b)c6lZ3(nHLwix)_6E@$79O*p@MfyyJFC46wqIh@w@)Fp;P(#%=kuD9|w<kqr3@ zKJD+FVd#W}(JBNdbMLdg*)tbwK(=(_2MYU8)P~Mv2<<rAcL|%muxfp5y+LISOM)rd z!Dpxy;C8m}CL-P{j=m{+zwmoPZqKj<3}+)IOvqL+*NM5#`eP@A_Ek^d@4e-{aqmDM zp_6X;hf<e@#I9Le2Kq+Qhxg2c%yH4UU63SvSZ*sMj@2%7`t5rX+#`=wpbAwz?hWHj zUj5Ya;BkighJ!dZn`?J_VMQZ8xM4VuyKi6EF+stb(p;;8@r$CN{4DfPk1t2If+Bx< zP=>~<wuaShBUZ?ZU8^bLbQ6|ZwPa#sWBBK#?D-h*&JlyUZ(+(<3<t-7pJ)*EW&GH} zErgtF+?Kr1=9sBzW3VlDCeo+9q`pbl`%Foc$luqK;-@#aQxJeL>|}^F`pR2VP20SG zsT%;wn3iXY6W@0-__^%i3i*0m`6&k)i@@G%bV-xUs?!Sq7OIpqrNc0ze4cgc3p)hd z*EB{<m&i;}x~GSn4ov0)YwpFCl4t3tegR`*nF1ZWgdpAkI))qhCJKS;A@=j%Qf@`e z4--2D!*b+NQ*!LzN05hhzU<0Og(#r~vnH>U(w%H`*~`D{3#cc&naWNeoz#8X8*rc8 z*~d@q`hW-}bIWw!#?oe^q~mJ)Q1rDT0%26WbyBtM!=&xyZ5Rcu2}LkwfLqoqW$MQr z+b`2Hf-YVjhgu!69?5p`FTx4TR*XX3wNeZTUp~0?9KWMzJ4lll<2s4hiN#0uJn(~8 zJ`CBs?|W=?(A~fP)q<k_<~AbZ-W|Ex;;Xpj)NO*xpOj1is~l@O0~3=x-*33&bU5SC zM?<Sa#Gf!^H*;0AWH|dJD3A<xQ@$vnp@S^$7YrmN@R}2d;Jq$0a!_Y~={YEN4#jKq z22Sm6sVHHvb^-rHgA%bZ)BQnT#2Me+!la~}&NdQzgIr@UE0l{L88MCiRhGd)87R}U z(_+kUux0G_KYTlX=9do#LE;z5LWkdk(|`qe@n0%uoQQz;8Z(y*_>QtUcYa3~!b&_W zM0)l_0(Zu&;i{@wVVg5ZZiY;PrvO^w9>&m~M?^a|ctx_O7XF$<^BEk^rI-!!)r_Hp zY^0~;f^4va3IMUChf+~!l7&f;+fva}_37Di_HlbqVLgT4#T?qEQ8R|VHpY?qN6bT) zA49p7n2)(RXp_U3FG9LAF+op^awc!km_UQcGe`hy(9N}hQM~HI19Sni6NaP4lCXL5 z`dR@h=n%8BVt&i4iHVz=+VD!@UdxsKS91nizGv<!P;fr3`?rBriQBXz_<}En_E9Nd zZY3mDG8p+6zhJTUcXz#Ygy5@_hB&)@Y$vs!$ZD0U7vcOigd6yv5LELG3{sO{7ytA5 zSqJt=v>XjmEy(FlbzpLSsBS19f#R47b;Or?jy-I)sK;Kb4eXKP&3U=tRsvDL3f+mS zia;<hrxpBZ8_W?pY1#W|M<S=vr>-|ZkqG)4KHk6<*)YDNO{uS%skfG*$Rz~FP%g+# z#`;+i40~_EtmTy=R$uYf8xEEho`sr7y)j2Trtnd$E{+2@=V6md!DpS=bxPWlZ$>{u znk&Y`EOusd9_Ul}vgr3`7&9NvPU-$J_vUYHVOf~&W0n5RW$<NLwq<^0eH*OxsD}Pw zGG=2H`=rpIp2DUbhzF0Q!MR2B{so)|Y_B3?aerJ3I5e+mD~~=+;3B4_kWiwk`Yh~> zhb0;tJjRz}kBtv<IfWxxOz<mR9k)tIrdC!9pW+Y>z{SOTAj6L~+4=>B@>mrQCr3u+ z8ggi;eZwIB{Og+R4n#n{aVBELHMBo;6{&CxZYNT$r#c&O`0l?y<s;>(98J^Lue(P3 z$gT7KMKmD}1?w7su;`p^{GJg2i#AJkVR;eTBU~qq6b6bOKH<A;{4ti$vh!<&`Y8U- z)^<+rwnD{i#kWi=x5jR~m)?Vg5ou<Ab;yc&d*x%}KthG4xV>WBzE@<l!SlmH8U;^( zFb^ycMD^mtY18KQ@~?fUD;H{6;N<~PxPO`p9_~jIx0$v+JYwBP_Z2uk{+qtD6=+~G zc`jr!HS~j%@YH*dsYa|<Cln!QUA48=M)gekJ9c3r-q`&1gLsW=12q-9eM;v?e|3aU z;9b+(nZjWk{lk>)Mcb~>p<bo~Po^IG&!iv2e^=--#;j<NK?0Mugbo9k?<?|cr^h42 zJ(;FsypymhvAf<;w|H^w6evi&aRqsqRueZWPJcPddgEFAr^733W~%{J%eX|1Ji>&A zjQnOj{hdEPr2K~=0gW!zia|g`!iA@j8{?A*#c0|RaZDUuc`K6_>{mPte^dS?;w6{C zaacZt-UX<#A;-?43mm(luJ5jL)$9bC)vzL!az639h|#@We)}DmP{ik4R77z=hsb1( z0^7vl>aI!Ml!?XL35Z|Dw%kISfsPoNR=>9g_%0PPujo}ak%r(x?B#|kXB8-^1s=_p zZj5BX6FqRUH^0>S#O@q@ZbZmoXg2V2)+vteYQVt0P|>4*SB^{?pXD$vfG#Ii3-yto z3f6m|B@Y%fI>Z<Vm6<HOLR3vyLdQXPI$l_+eh|}K9v}|hpjL4oU#9BFg^;cdwV<~h zyl>=mMp9Y0MV_?HB(<2yjJ8P0BTU-cms2Gy^(F2h7v;#s(-a-{qeTyHn8+w&_ja!U zo`TtXWeIy4l{;U0Kjs_(QX^uGL@s?Y?jfQ1$fl9^cSjxBk<~)b1RbYbBBuC6{P0O< z&QT%qKT8tFr2#PA+F$vd9aVLe5~W>SfAYLhb^uXvd%Z2y4}!~eM4W5pA%`b4&*Q(- zeUWg^*PCpJaGPX1rhbF#(?xgw_w1ke|6coGK1#i6)j4S|*ql9ToBZb4i&+1oG|{M_ zjigaeNqc2TVkcy5)54Ahhb-a{WG%aYtfzi7!)(F*g_S;1WZg2ayR(Hb;<NWQFqz%A z`g36i4nT;6=7M2n9;7IbcpR6%zcIe~hIS|t%nK#@G}hP=j#{y{DkMcwGeb;K{U?U0 z;C(lTkTC;tG^jPv>RK(_hD`M?Fxq_#j;<<4&9Ui**6UL2QdXLuk-De<8WX7T2y=<l zDSvttAX}u8*(5U8OzM%Iva&uUSq=V%`^b$q`okhgN4ZlnzW$KQZe=NgxeFx%Z*f8| z^&kx^+cVXa_>9`#5@jTs)`!#Vt*U#gXB^M?>}|G}45mJ5NDzxhG>PpVKwaIJo(s?0 zbJA$sT_{dFjVflkUkk=$+dW$!fXlapydOL{t@v$OWy`%N5cQQ{X!h#rZ`u{eW3U39 zLNL+xr)4Ow1nD+JOqsBqpKA4NWEHCMesPrj%m6kPfsX%Zkv~XHEIu%@*o*uU@aW<s zQ?ukKAOHgsy831HST#wPI@d~+UHJQ|d=A-c@JsI9VSdO+<47SZ4Uf1A*x;qjKK0+5 zZkVinFC+8i*oFE5@3$N7LPBDujh0zyUh-IfB$_LAAN>XCy(^Yhto=@_lF*)G<S=cu zA7Qnr&EE_XEGy{;fykc#5>5`<j%^>Vq8!@h4?kqWZP*7@>;k1LMav`nY=!+^7z*F_ zm(!+5MEUt<Aw0jT+Y@n%eaksJ5;@fT#_QxyU-}A87f7+m4*DDdMYUZF=?zyf)ayGg zuUl#Vy2uc@0vl(S%P4+o(>Kg+7Ww=stG9HoBlFFZw7(v%%_FxxVoxv*7vd+{UNR*b zM~nuJ&8yYXYQ>~1LXTAL{gDPzKIdkh+_Kw(L_3-<pRyp+8&dp*xJ-wl=1m<PitP5p z3nHqoa8=4lt7B!>JyaeQL$Xn?v@2?*=}ZSZ#3ZOJ-86D<e-!O?aGlT(kY966_N@D@ zFBl#3Xeskw5_47P@>iRD_)ZO4=#%(Bpk08=5|b`T+6oG1*Lh`0M4=V-kBhoXi=yW_ z?%?Fe&En4wApTy^^^N=FD$GtBziysqw*?5JrgMGn&7<Pn2Vo@KrI<ZO<bCO?X(l@l zK;%4-HJTixrbekf_;}9vPBZouL@dACxle~1s&h`jX5AOOM<ZUB4;j*Nn5uGJQh)=~ zT&wDHH+Mg%KVvc4FBG_%z-X&}clJSyo+kT<URtS~QAZB`D$}peVtg<<PqdewZ|F$q z_=&GQ9b??rMcB$BO+{NV>i<K&c4y&WQ3hF1W0+oBoMj<B!hLnHgfwYgb2O!}tKvKn zS_374(L6NOX1tAg*Ty`Hr6ReP4Dn#;sO<4p*2IQFv61pfqdf6JFfAfjaJ&>W?x#X| zdoAbJ&M6t6D~W%E_KbJ&w6teCS<&)u%(P-h`f>jLsB%X*`yQ6#)|gH&vYe;OVudlN zE0HiCTr{NR?XMUX=}2DfAnf|!k=<N3zBLUPOM}Tgi?HF~kj$-iqr3S>EbWQF%%Ali z$VRYuA&t&>3Lx&FgMDb7M_~H~w2RDZfPKYcm99D?;fGHIjen6-1mURV+kP4lAW#gh z$5YZGG{J#a$HRz?#*s}iKQmKiB^4zWo`7UT-uZ9dGW8_=3S;XpHA&t^p^<7_CXz?6 z;ahDAS7kb*_o->;KZVWdjwCGV1bGg_^s5tEfs0JMByh-^%!b=G$_ZaQm0upWVO}m{ zDZQTCtmaU*#6hJla(H$^9&o*^Sb^}DUs|)VCj8`3Z*3OJ?RI5H<>+KMbD(j$X$xt^ znW-_~57NC35#1{Wz^>RV=BGL%C1yWK090L#>0R+YwavH&>!){63o*ZWqCoi=&y4r! zR$3D@E($0XU(0J|7GieQTS~#&A<dRgL_6qSO;Wqa_IEz`QEq(YGmI_k_`dApEs=RR z<%JVrw+^2g6p3aUaS8eiqvl9)<Mk*PuAbMo+{V?aE(r#^8uR!`RcXrhr+1xOGE=<# z;Y^(zvr!-j3bM$qpw|2PGSQZBK~EAj*i9V3!iVkzSbIgvZZxkwSM6lS02no{V`BVN z{(%PWpENIl5HO|1^yO8Zfu?Tt=%MnP1M^{p)&N==&EeltGMWo7t3<j<DSot2G5Qx& z*(S_FQo3qRDl}F7YO&Wk(-v=!&v%#aqJCa}^FGByS&qPH;Ny4XpYL!aTPquVKNRyu zMe6GkMgMUE)~t_>HSw_hx#wE_MHq0Ta>rB5F?U1Svop7<-=di!VpZOKw4%7=1&5e% zn2<7>mDw|u@~Vdc_F-M3QNXv+Y~!{hy`;eNuy7@mMt&Mp?4uYF{<`Gwt$sLe+_y)p zUqHCLRNpBlVbDlgdC_%q?272DFUxEcO<7~}>mYF(ygzdWHYk|ijS6+WdJJWCV5dQ3 zC1L(lJ`db8(y<a&B;LYvfExR0Us}X`A+G0;KWvxElvpb(aN{81=uNrU8kVM$xW09v z%tEm)HfCuiF{?P@9`L*=-<5pMMVjhnvW5e(hwS{23nu-k8GwGpv8xFfJiHYUa3d|l z+FAV?aHDp7BJXakC;-y%52^h4WA~YpX5GqhoE+XAi5~mkrOM^pgWZLLdeOBHrH3cR zNkexhF-c2x^ZX6GwJ@`lF2knTf)=iO9^#Bxo1n&c)9M(jyp<GCO!rfF$@Pw8RMwfM zdRCzyJ!fe3qFeE`v!~Q;y|){keO{mU9#lzl{8)g$?t)PLh2yfGRN_US?;C4CgPoV< zFqnVjsB?mC<2ru|NAf)2{!=&MjOS(k_F;QZYUj?}=udRtxvz62_MP!0N9zT7+Wwf; zG-_AfY$)pQb)lE?-f9`OCm*m)Yv~U;c5By=UN|_32JbAA=9b&_=KVOYhVQpK^hIh^ zaaX1>y^DeJa;65jQ%t$!x8SE@t;<3E^s@5V&!6@{y`OosR%n1<JzxaU6>rHTaaA-> zL7kTT@@2V#6P0WzvqgNRv@3Td@nL<-gg*3F2E!R|#)buxw1rChbskI7f#1B1&bym% zO2HZfW)d-zyR5z1I+j0cou#!fysfzpomrjGzd0FJ|FSO17#@R`|2lP<xm6TKGC2P? z90(f>$9`<?XR(&^`d2JolZTO7`#v;zm#9#J(Oga2sK-eDN7eE9NHCHDuT}l~!+Pna zF;A47rh@*GD?8%oUO_3%Kai=5*|Z4$%DT*G@Ac?2&i4)^-?4$WUrfkL$`W2|Yma-K z9e10C`me3(c=9o_zI<1I^cOBy&#y47&*VgvdBkUIJ)nFA8WrgL{(J*8aOtTL#o2_` zQGe4nLE|FKE$g+vmMhc&4ZH`^TX(CI*2kKx)?3axpg)%#Nq>xAE%OdO3lb^1FxC-( zZf_8HgC2;x3Dl>hQ}AB4qFy`lBdS*Ro%P(=gD7*IIlE)O#EfPE<!t*%llRclMsqsv z`*Ys<bTrAm#TT&kwJne<j$Xf*+0qHfXQ}h`xU*ueB<N=AN+V)gU<ZXZej{<C^lZ0y zFZ);m0wT4jfO-R$i8FM&=jN=};$5#^=u+n>GCQ_r^`>lvM4X<J;GMBt-sbK+w^HEu zi~bnF1k|e3`v?h9dac(u!`%ai$|*2qg-1}67DJI#a-+l(mwdKj7kfmNBfbVrg1$eT z91veyCbmt!-d9;xBF^JSyuOtJ3&L!zXX!HaJ0TSWzTs?_l(0xglcEKp6Ix)6Vbqr4 z+ewSRYS^M9!1!?3j$+^C@*Kz7Y$#9p!OgCa2QJ>TLdw$~ZZmX&*!P7oow-n+F6CMB zPQ@3~jIf_kHaFypxr8Z>J$nmCuZtjFL;A)Vx#&rU@%H~Oq5qcE|8W?tdxZ%*5g$MS G0PsI0Cvs8% diff --git a/packages/cisco_bgp_peer b/packages/cisco_bgp_peer index 414d2e7..f44dd4f 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 -- GitLab