From b603f24611d18ce81d009c9ce1d67163cd9ac973 Mon Sep 17 00:00:00 2001 From: "th.l" <thl-cmk@outlook.com> Date: Wed, 15 Sep 2021 20:37:01 +0200 Subject: [PATCH] update project --- agent_based/ospf_neighbor.py | 272 +++++++++++++++++++++++++++ ospf_neighbor.mkp | Bin 7249 -> 7629 bytes packages/ospf_neighbor | 28 ++- web/plugins/metrics/ospf_neighbor.py | 39 ++-- web/plugins/wato/ospf_neighbor.py | 205 ++++++++++---------- 5 files changed, 415 insertions(+), 129 deletions(-) create mode 100644 agent_based/ospf_neighbor.py diff --git a/agent_based/ospf_neighbor.py b/agent_based/ospf_neighbor.py new file mode 100644 index 0000000..0b1662a --- /dev/null +++ b/agent_based/ospf_neighbor.py @@ -0,0 +1,272 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# +############################################################################### +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +############################################################################### + +############################################################################### +# $Id: ospf_neighbor 288 2012-07-10 11:06:38Z twollner $ +# Descr: OSPF Neighbor State check_mk check +# $Author: twollner $ +# $Date: 2012-07-10 13:06:38 +0200 (Tue, 10 Jul 2012) $ +# $Rev: 288 $ +############################################################################### +# Author: Thomas Wollner (tw@wollner-net.de) +############################################################################### +# +# changes by: thl-cmk[at]outlook[dot]com +# url : https://thl-cmk.hopto.org +# +# 2018-06-15: changed item from neighbor id to neighbor ip +# added events as perfdata (incl. metrics file) +# moved part of the output to long output +# a little code cleanup to better match coding guide lines +# 2019-11-03: moved 'events' from infotext to longoutput +# 2020-07-26: added parse section, alias, wato for alias and state +# 2021-09-15: rewritten for CMK 2.0 +# +############################################################################### + +# Example Agent Output: +# OSPF-MIB + +# 1.3.6.1.2.1.14.10.1.1.172.20.2.214.0 = IpAddress: 172.20.2.214 +# 1.3.6.1.2.1.14.10.1.2.172.20.2.214.0 = INTEGER: 0 +# 1.3.6.1.2.1.14.10.1.3.172.20.2.214.0 = IpAddress: 192.168.1.2 +# 1.3.6.1.2.1.14.10.1.4.172.20.2.214.0 = INTEGER: 2 +# 1.3.6.1.2.1.14.10.1.5.172.20.2.214.0 = INTEGER: 1 +# 1.3.6.1.2.1.14.10.1.6.172.20.2.214.0 = INTEGER: 8 +# 1.3.6.1.2.1.14.10.1.7.172.20.2.214.0 = Counter32: 6 +# 1.3.6.1.2.1.14.10.1.8.172.20.2.214.0 = Gauge32: 0 +# 1.3.6.1.2.1.14.10.1.9.172.20.2.214.0 = INTEGER: 1 +# 1.3.6.1.2.1.14.10.1.10.172.20.2.214.0 = INTEGER: 1 +# 1.3.6.1.2.1.14.10.1.11.172.20.2.214.0 = INTEGER: 2 +# +# sample parsed +# { +# '172.17.108.52': {'helperage': '', 'prio': '1', 'permanence': 'dynamic', 'helperstatus': '', 'options': '2', +# 'state': '8', 'hellosup': 'false', 'helperexitreason': '', 'events': 6, 'rtrid': '10.250.128.130'}, +# '172.17.108.60': {'helperage': '', 'prio': '1', 'permanence': 'dynamic', 'helperstatus': '', 'options': '2', +# 'state': '8', 'hellosup': 'false', 'helperexitreason': '', 'events': 6, 'rtrid': '10.253.128.101'}, +# '172.17.108.58': {'helperage': '', 'prio': '1', 'permanence': 'dynamic', 'helperstatus': '', 'options': '2', +# 'state': '8', 'hellosup': 'false', 'helperexitreason': '', 'events': 12, 'rtrid': '172.17.0.2'}, +# '172.17.108.49': {'helperage': '', 'prio': '1', 'permanence': 'dynamic', 'helperstatus': '', 'options': '2', +# 'state': '8', 'hellosup': 'false', 'helperexitreason': '', 'events': 9, 'rtrid': '172.17.0.2'} +# } +# + +from dataclasses import dataclass +from typing import Dict + +from cmk.base.plugins.agent_based.agent_based_api.v1 import ( + register, + Service, + Result, + State, + SNMPTree, + exists, + Metric, +) +from cmk.base.plugins.agent_based.agent_based_api.v1.type_defs import ( + DiscoveryResult, + CheckResult, + StringTable, +) + + +@dataclass +class OspfNeighbor: + rtrid: str + options: str + prio: str + state: str + events: int + permanence: str + hellosup: str + helperstatus: str + helperage: str + helperexitreason: str + + +def parse_ospf_neighbor(string_table: StringTable) -> Dict[str, OspfNeighbor]: + def ospf_nbr_hellosuppressed(st: str) -> str: + names = {'1': 'true', + '2': 'false'} + return names.get(st, st) + + def ospf_nbr_permanence(st: str) -> str: + names = {'1': 'dynamic', + '2': 'permanent'} + return names.get(st, st) + + def ospf_nbr_helperstatus(st: str) -> str: + names = {'1': 'notHelping', + '2': 'helping'} + return names.get(st, st) + + def ospf_nbr_helperexitreason(st: str) -> str: + names = {'1': 'none', + '2': 'inProgress', + '3': 'completed', + '4': 'timedOut', + '5': 'topologyChanged'} + return names.get(st, st) + + def ospf_nbr_options(st: str) -> str: + """ + A bit mask corresponding to the neighbor's options field. + Bit 0, if set, indicates that the system will operate on Type of Service metrics other than TOS 0. + If zero, the neighbor will ignore all metrics except the TOS 0 metric. + Bit 1, if set, indicates that the associated area accepts and operates on external information; + if zero, it is a stub area. + Bit 2, if set, indicates that the system is capable of routing IP multicast datagrams, that is that it + implements the multicast extensions to OSPF. + Bit 3, if set, indicates that the associated area is an NSSA. These areas are capable of carrying type-7 + external advertisements, which are translated into type-5 external advertisements at NSSA borders. + """ + try: + st = ord(st) + except TypeError: + return 'unknown' + + options = [] + for key, value in [ + (1, 'non TOS 0 service metrics accepted'), + (2, 'not a stub area'), + (4, 'IP multicast routing capable'), + (8, 'is NSSA'), + ]: + if st & key == key: + options.append(value) + + options = ', '.join(options) + if options == '': + return 'unknown' + else: + return options + + parsed = {} + for ip, rtrid, options, prio, state, events, permanence, hellosup, helperstatus, helperage, \ + helperexitreason in string_table: + parsed[ip] = OspfNeighbor( + rtrid=rtrid, + options=ospf_nbr_options(options), + prio=prio, + state=state, + events=int(events), + permanence=ospf_nbr_permanence(str(permanence)), + hellosup=ospf_nbr_hellosuppressed(hellosup), + helperstatus=ospf_nbr_helperstatus(helperstatus), + helperage=helperage, + helperexitreason=ospf_nbr_helperexitreason(helperexitreason), + ) + return parsed + + +def discovery_ospf_neighbor(section: Dict[str, OspfNeighbor]) -> DiscoveryResult: + for neighbor in section.keys(): + yield Service(item=neighbor) + + +def check_ospf_neighbor(item, params, section: Dict[str, OspfNeighbor]) -> CheckResult: + def ospf_nbr_state(st): + names = {'1': 'down', + '2': 'attempt', + '3': 'init', + '4': 'twoWay', + '5': 'exchangeStart', + '6': 'exchange', + '7': 'loading', + '8': 'full'} + return names.get(st, 'unknown: %s' % st) + + # default checkmk states for ospfNbrState + neighborstate = { + '1': 2, # down + '2': 1, # attempt + '3': 1, # init + '4': 0, # twoWay + '5': 1, # exchangeStart + '6': 1, # exchange + '7': 1, # loading + '8': 0, # full + } + + notFoundState = 2 + + for neighbour, neighbourAlias, neighbourNotFoundState in params.get('peer_list', []): + if item == neighbour: + yield Result(state=State.OK, summary=f'[{neighbourAlias}]') + notFoundState = neighbourNotFoundState + + try: + neighbor = section[item] + except KeyError: + yield Result(state=State(notFoundState), notice='Item not found in SNMP data') + return + + yield Result(state=State.OK, summary=f'Neighbor ID: {neighbor.rtrid}') + + neighborstate.update(params.get('neighborstate', neighborstate)) # update neighborstatus with params + + yield Result(state=State(neighborstate.get(neighbor.state, 3)), summary=f'Status {ospf_nbr_state(neighbor.state)}') + + yield Metric(value=neighbor.events, name='ospf_neighbor_ospf_events') + + for text, value in [ + ('options', neighbor.options), + ('priority', neighbor.prio), + ('permanence', neighbor.permanence), + ('hello suppressed', neighbor.hellosup), + ('helper status', neighbor.helperstatus), + ('helper age', neighbor.helperage), + ('helper exit reason', neighbor.helperexitreason), + ]: + if value != '': + yield Result(state=State.OK, notice=f'Neighbor {text}: {value}') + + +register.snmp_section( + name='ospf_neighbor', + parse_function=parse_ospf_neighbor, + fetch=SNMPTree( + base='.1.3.6.1.2.1.14.10.1', # OSPF-MIB::ospfNbrEntry + oids=[ + '1', # 'ospfNbrIpAddr' + '3', # 'ospfNbrRtrId' + '4', # 'ospfNbrOptions' + '5', # 'ospfNbrPriority' + '6', # 'ospfNbrState + '7', # 'ospfNbrEvents' + '10', # 'ospfNbrPermanence' + '11', # 'ospfNbrHelloSuppressed' + '12', # 'ospfNbrRestartHelperStatus' + '13', # 'ospfNbrRestartHelperAge' + '14', # 'ospfNbrRestartHelperExitReason' + ] + ), + detect=exists('.1.3.6.1.2.1.14.10.1.1.*') +) + +register.check_plugin( + name='ospf_neighbor', + service_name='OSPF neighbor %s', + discovery_function=discovery_ospf_neighbor, + check_function=check_ospf_neighbor, + check_default_parameters={ + }, + check_ruleset_name='ospf_neighbor', +) diff --git a/ospf_neighbor.mkp b/ospf_neighbor.mkp index 6c4dc70afda94f7814e4fc23f3cc38a047992696..f53c528d4152ff900f6478b5c36304833055d6d2 100644 GIT binary patch literal 7629 zcma*qRYMbi0zhGMgmi;+w{&;6bP0%bNyCsFAT3=3iP1f!yHi@aJ4a5s?|r%N_wD?I z^U=hiqpM5GeL;G2^K!TQ>}u<1?_lNT$;HRR$IBziE6DB3%PqkD)!7~4Ow!qFRV3xX z|B={u>Qps5-9hp>_MoA0a_P^#5N_t#++5CLNui=jUnG4ailg$+(U9(QfD8c<)2iy} zau&TW3&|hA^an`0Y$08k>iuc5I@;*QB+g5O?(^!&>21Cbu&r}aE5xmNHDuw~x2^M> zf8pCL3R80Jdqnolun`H5?$m!3=ge@<A24LYZm{Sts)cC#-~fp?_v0a>F9c6t?zd;& z?N)h+xGj7VG`wq}RJOv8<LNP7gzizlL%uzKzGey-8}#<2QaY9JwVn*lhQrZ(J8eko z-Otr-)sLPK6JPP?oU65ACBnn}!eGxB&FbNk$_A%jvyu+L+m-coM7;?=je)>PW*rgx zP+5IHPMrF<{CwxR$h%+`SJ7IBi?P646Y^R-_Wyey*o=k?2ER{Ra{W1IBeRm*1`? z7Y??u8=BmUz!8MBdHfLn<bmiK8h~C<Yvi>>+4{2E-7SEE!$H}T`GIV3!3z4pY}u-r zTR;f0@;JLS)JUmYK?izLwt6nXLZbXEMBp)w>~vWs9AYn7hTN!P+qc?-Ywnh&AO#!e zOd$3A-C3gu8}C!36#F8;<gk~B!ZSU=cIl}=0a(nu(dK4mU|_eet^VBT)YJrOn^?Cb zZbAPx_6x&dn?(<Q<SW<Dvvg`x4t;AMJ#6w8(?b}43Vw@M!oZ5(bgGRMpThZ8yzzik zFO*7N{~}UZH&JXa@bDXOvGrI?{2^-645cXgjRPN_w+2VoU5sR)k17OV(!Z3dtp39L zOo`yw;wdl<rB)`i6C3&U!NlOzPZ9J2d}Zt3YOa4<Uq5&4eFcg?y^Ov}$GmLn-iA2$ zG@UoSed20L=YKOk9vgp?4tt`w{ZPMC@#huu=~;2;o`dt3Q2pKS3dYDBRYlQi!3uPZ zApk^XB}*~lWUQv}Y^4p!-CD_r?ud6u%ilBeY#>jVVw8&~&E?%0qpl?o1$TsfC$+IM zLVLxAF2|P0Yl`!y3dn)*3So#b+b~UaTz-#>UIsPhg5i89rN1V3vnXQxI6Rzidv@xr z{dzU;Er2%M=`|!)mog=h`AyPF6BuIu`DJ~5z8ZD|+{(+4X6>gQ>E2uo(tY5+Jr%?6 zYDPSV9(K?EThDJIB>(z3&hjUkuuCn9J?+tAm-QLViVLMbJs8KB;NuFKU)e3y>%}U` z(Yw(>4UlX?pX1&n`E!l}<qtcN7`OFk+$BDZr6uehT7S8)##XH!lxVWE10nEtQCcyR z%8V^n0zV0Q4yr2$?)X2!H@A`jgS~I=LB82$Pgdg=alE)a5=Fuw;2sv|ZKEs3Nr!^O zq#*UFExKm=Xp$G=6hrA5^2tvN?^G@lrOK?x_YAN2@BTPCA<d{Cgk!67h&KwFjYOlC zu|N)bp~xou4ut5P#9dQRQFX?A#$a_pq^;A;)VTc3&Th*nw-DQI%Dh~vuFm@#WdyY2 zK0WWya$erg_ed*%9eUyDOqp<}-3j>eM``(zeo~fSAGw23O_#<}<C=#L4^Q{o`F%&U zaHqJng8knfFMs5ANs9*ogHL&UC-DWL*-JfV7%^D)$M7ZME{)?i;sH)X#Z7DeyU6gC zVKd0#ei%1*2JoG(;BJa~<Q$UUkWSB~O<x#~YpD3p@*89fg?jevtcD+(;5xl2X#U6A zYt_}n%`-k=@{VN#WTGc>LPg1QnH6yK-B-MNmCE}T@Dl!J4X^R+TE5eUqvzhsR!nJ4 zZaTcmzVv9}u*s1vaRr8;^dMJNb-`j^Py@GcoXr?{(6?USn59Am4LbRQ`Dr=3Lr@~g z^w0c|t+H3U9rsod6R8q~yMqp=gGlDEgcJl3(6!nVH~}s-hft-#OjH@Gg2+BnCF#M@ zyB{Hjk2X@@KS(L7BfZ_PVW*%jv>xzIhIk26Pc>wHnN>hY`hdhOxQ>S+%Q{DMbbf;Q z&{jxk#BKG;t4P<s<!sHfK6Jo(ijPHi9q{}&f-S3F-g|6WoZb=P&I8i4nbRQyhRy<S zbD*=~Z*AgQ&EX-r=fP9QK3jIU=RQkGxtur#coLQm_RbVX=>nIxJNNVOWsuLp+_BsF zwP?>dm1VBr4N&O16*_qyDYh=?b1O6_YOH4;(m46i@>$mLsS|PalirDC;{u0pmS=T+ zLflk0i2nI-JH74a1NMCJj1&bowOL4Cf%dSexuwcs&+^&dw&x#DJV7$2(dr{NBvl{i z@pmq`%|WyK012{LYc~wUF0x%@1MFdAdQHuJqQADkHrTajAB6GHJt05g@>!ghLjyNV zQ!58Fij*Ekm5QDnzYejI$juaZ2LILx^AtC~(>M+tc+7n~G)$iEoTQ1K?xJs)<)ZVx z+XAXPG~4S`Iw1M0m#=+$Z7=!vAVy#C@&1<~gL8C7;AuNSpwTr<o@efu3l;wZse|oJ zKLc-qkHNL5JL1S+XCJL#P1d`BJ?`&bvw~a#M}x)#Wq3uGDxJ*YhkvpCreQlj#s(5^ zkZr!dKHN?mqbeA{Fwrw>^}9Ed&}J$HyU{<Oq3<yyek_jPPEN9Rrzy0hrZJUl!2baf zCv6#uLZ5RA8Cz0*s{?M?y*K&>aZs+n>amYL(sfqwMNa2vr#BzrP7g%e8XCY9G3de? zV*TO6GC#0mZm9}4EH#9w)&QP$W}ikd-&3R<(F~4&Wb@u@F1&I$jxm86IJwQ(ge+4l zYfl_(v@IZnJ(3QY{-uUn7qme?W2?FdIHy_20w7BsX_s}tni~rtQ>n5<25J@^$clTZ zM5HTiX`HMp2fA%wr!e>N$mgg8NB&p7w109P!n$}(uUI!SoG0*XN6@bx_E|8RC5a1O zF1i-)Bj77*#4!WbfWnsVz#0eYGTjTF*LkS48D_0I-ZuSp|M5Q-Vum1c!fP^%k53B= zhL2B4=5b_JWEi!cWQZ|wJP;K?A0}4v4DfYk92?KfW7BJf#6sppD=r3OfEknuf?0!^ zicPnAh4yr<s+HeK(&2bUPxph&d(5Je!FlepTU^D0Y~|Rr7n3H#Y@R1JeXkQmzisKt zvq@(W7H!P#9kX|7h64-m3J$K3e}c}HkSt+X!+nFqoTNl?t=2BOil4G+V~AuKhl_e{ z*r4It5zmjc(VaX?^36yq2+NF%bI^F*3n_^Rz|c9;p~#*M3Y=l|d@pUHmtBUcLW31b z`6t7|Gu7_+D=PoomgwNZlHvl&46zD<R5|L9hfw7Ung?zS&eH1Dm3)->xCqp2$6+w! zYT(B@t7!^zSYE?j+(>DMuO!cp+9XV`=xBSS9S;}Igf*E*sHvYq&`_lB;=B!rn|@Fx zO9H%-GXp139t(}Y{B1u1Q9?$uj-))Mbh`H-ibytBOx*V;2t@B&g;=QPXVVGUWEvf7 zVl7Eu+QzN71owGh-^@5c5l7<)!G>-oWLjz_U-%qXX0?ix=XW{)(4JF-A0?VfgAEWv zc1jskJRUy2x6{!B1Sj?J*Y|&{`srTDt2JV>NfX+QFMFKv;1hg?1!c$2{6w<yn`3(j zFU9T%#@+Hb756>5nPIRYA`<pKh4+P}#~55DjcMxhe80Io3rt6SoE$SN(!J76E8^zr z%!&*T2pX59bzNeaac+7&19ep*HcO1n5EH)Tmwic%Ji;NKX(T|zGQx`cQjeWrgA=pR zcaP8(#dnpf$4tl)$;0;^W?$QxCg1O=%<5#5IU#4;KeKIGHfi?DP=kqw^Un7lX-T83 zRlkNW6GGN?<cWFaPQ01XgsB$Gja-yxoFpMDk-2rFi-_?dsx$6#EFYl4(4M0%)CA;h zIX<eyKikCQk2;hg?Ct?{s1y|RbTZgb!b1F{f=pf>P>>ZDpq2xi5y<(m*ghn(u=wcj zibck?bCZyVuF%mt+>RaMk$EC1@f&MzRhEZ5UAR6IjU!@78U5MSE|(!l2ClU81QboO zgT4PPJtp?#b)X#_rQs9WujX)t>qM!pi?(;OdJ?_!B}{0Bk<7+>g~Njq3tI^NWscJV zc-&>@H?dRa|GgS&@_aTOG`N>NXkyTB<u<WXfvsKNQjzDFvA`)+Nu8YK{Z_w&yQJx# z=nrK(pN|b^LR{Ajp{^W!ynd`PFh}u6NHU}4+x<I2N-&4K2Fjrh7c9zTwj7<waN#cj z6i~d!7QE6Woow&!-%>m95;nzQxf%N?WS9R7rdgFs|7|~-BbzIcHJ2^Yj`K79s8M5% z(KWD(Q$+D(+SS-k;8Qf|ICuXeMjc{MCWe$EU?&!<wM|s5VU$DQ;!pOq;?AGHpddAA zcDk$kR;Y4R$%R(kCEj8OIv2=)3_9&ns8nIPkg8C5*I8LI;UMfn`FoMb<jm>1m-T>E z+(<HzeLT&##!jsWX3S;JcF2)CMYTJyWy9CdjcqjBZgKBySA?|bneP8P-kX@uI<+45 zMZh78p8(E>8+GRbae3g!&3M50X6cD&d_cHYmDYlW-8uQ^n!Xa-{D7cX(7n+YmC`k% zw{XzjpUS6XGFIH(Rueb2JH*t-N7$*G+CX&w?fcFn7BYx}F}FO+Kvx-XHK>y4y04aT z6xyWjl;xU&f+ERj(x~T7NS$aeUtgUqTHYceRxQr-D-0&^;U_RQJXA<+7;#+LB#Bl+ zw4tb^4QjGTJhv(p@EILU`|f}<L8}94Bmw)M7KcZT<v%WnUQu6T*kCh+uRXh7hh(6Z z!UDBTEMl^*KpTP6eal||=E{YL2oiv5;r_hzV*&10e2``p{~AdVHB0R6D+Hnm@5-;J zO&UzORzK+;ww@j+Fb#7{bHaSSK%3sO(~3xEsa)8*(EUR7F_8!*=xd5;tMpIQ-th}9 zpP+aS=oT>&n@fy+PSkAIQZyDa9_60`&swR&g2I8rl;I*z-RwrEA{B#5-SkTR5_W?M zwf{5sr7OYu`H30N@MZG{ngH>t2b%WwPObDeI9u#Z#jf>e$k*55^Wd`0`F|H@XQ5l- z>l+rp=Fzu%q6p8^43@9}LI6FE2hb+I?yF^AjAd7OYl-NBlbFv2s8cK`X!ci0#XFCW zrd*i<{@QwuIF1Kjyyjz81iuE+a@t$b@#-vb9U`9P4#p3jlCJG<bL(&P#fV3ajwSE$ zo>!+YLwybMHAP*u?52g!dvE5oIG<NGH+&;1NcvYH21Q?wxew&A+{@0{MT9V?TS6j* z5_i1Tw!V`S?5_R}I^m{L+!V}j6-K&}vf3GS9uFna7!Zg-xOl$}!%eK8O39_Ds@+X| zw~6g=C4RQcOdTPHO(yZCz$wOHmqUr|9=Aewa<>AGmO<gox2kK8-aR~`<y{j@*xNlg z;}kSXWeUAF>U&`#q?U<^Sz*spM+Ys&?lPb4eIbaF6~QI^GwJx$p@;19V+dUQIeH3J zCO;<|YmMEU(sSHp2;%>!{?ZUOBa&;%y(8rT0_WO#2;|D9Q;1`i5Vfp!DtO82yg%&m z(<n(9gNqC$j^=uZY##L9K3$!G6YW>wXun0T`Fl?B!^ZF}e#rE6Ty+HeKJ*ns%bmP> zE$*ihjXMlZ-J5o$3GEA9XyZ&f&_p>(tr=8Pr1T}I7X>1BHcF#kIpTmfv{!BTNpAr2 z>x^Cc6#@USTy(-dHogI74T~a+BL5h(AS?bM)zfC>mE)kuEn?*3Q$8~fYk56Rs4a{a zVPbKQ=k}w^l$8`(GErgGSP#^~IMQZIoLOWz_h$jio&f;#2UnKyDQNOdSIB3TDi>xS zCL%|f6Zi_!Py#as6vw^#*^5Y)Rn*1}U@{X!E_g6Ts5{4RawZ=Fjr5UZSmyv78%r@J z^|5I+%4xSGIYcb(JEA3u%DazDqU)eyg)B?HC{i~Za$HvhA^=ze(VNM#Er<byf>;ee zYyNQuy)+G}iNbx2ua|_5`AUWSD~FtVaxsA<YolI;p@&kv!3P!@T*fq^3KMrE!e-5G zv&&j=bl@vyya3FHJM*M+Bf6!0%Za{+ec47o^K=iL*`EI`;bmaVc4(pz(-(>9rxWzt zEW`Kwpe^#7J_}^fyqMh_eb~Eiy4m%eHi#PEx{7U-_Rkg&Z{u<8=cFSUOrcYBC*9Y0 zggx?hrMpG_XOX4QpumW3m|0vC3w>h{md!h_<s5-fyJYWwJ(@C(+}2;EdxdO;t|Qhp zT&;Gv%N4a8QcmEqF}AR}9-B`O>mt62ploZU^uZwhZDX@=97Z_7P(~h$%23vMN%h!b zl|8J`3)YkLXTrT^tr%4eBT%7O-;;7&3NvSSIVW=&Jf~I*w5%_hzHtCgw4#x`4+^Kn zT4{X(uOg>@SE<I7NRS#aTdbt1phC%^@ct<J#~;w*;k{7IeF+!T&f*+}SRgDV7MIqx zrzzb99Qiq@YSVxRm6?#$4&N6Z3w-~wme7BT6rvq+%vGqg-W>;Szy&WMX%`d8R-pEN zQM=OJ#f{dy5%Bd^%0(g<khkkF8(Wv~ViU%`t5A;W6!=zrH6(zgA&nB%OrjHA(;$<{ ztcJXGTpJAg-Au=9C$648XRFNpgVxA1orU~)0dN0mr@VD?$&q)f^hqDHhiWmCt}Wj+ zEX!YCmr9lu5l1xZn0)J=;p=9&=$1LpfuireicvMnA!&770Zvv8@Q4Aw3tb9akhPoe zkBBGHO^m;ISD)gBPzU<CPWd6($0)+(6)}ZQ4xy*%+Cc0g$2w|y*Mv+huTBdRHQ6hu z2n)9f#_lV8?r?%6WO6cnpP?b2*HE6v8VJ486iXy4F`z~+w3>NV64Ognb(+?NN^58R zu;<$S`xi1l@fRiL8M&X(L$dRVzE3Te$ej#C&1#JV-Cy!UvT5Rb|INE8-mE@m`Z#Rd zMJ10)(^OPVVt1f|=uinm&Ey;uJSFPUu;d}8aWKsbL;VCQLMjj2MYT}PNwPktILPkY z`<Bn#P#S=bfW#gDN^Gj_3eHz%x-=#Gkmv5H6GQuIqPCu`W~cbQry*>o7fw|CY>?*4 zp{B<-f*n3Yl6LwDSDS1?uC!kxhH_|G)sih#$HQ|wKA{X(lAptGzaD8kNV$rZfIK2H zInw46wkk&uiF1dPG|C~3U*t>jWC35L5VY09%v!CnB%BdzjXuw97IOI8x?u+d&Z@B^ zLM2ho_=o7@o(&UF)x-E)l@&%ff*##AOs{mUD;zbc&ci3hKZ`Vq@lq-g?1z903xgY) zKh<i%Ec1i}{Ih|gDKRDyeg}o$<wro{7kwy>+mwA@&&l(bvPli3vxFUsKlfQy|DyLY zC2z4E!irC2mHT!8+k28BJsu*KvUOCCY9}UZUj1c2Ph9l#wxP=YS)U<vM(`?71Yk2g z&p+%%j1h^ktK_HVpZ&>!R&X9TqQxsgWk#fU47gb`xLo@*7H8RcT5~Co`*fx);B|y- zMgt#FmJ>w>>p<99Rn<i|zDN7IeYTEmW6~+ZdNC#V(iJKS?DQ{`+D8k`ppJ~KDpaqr zZ6I(>rAS)T+{lIr31_Al73a_lScuEeOvoY(eV&t?)1#UIGH3Ss0sfZU3yW$Vbvs^> zKqSU1$xK#grSC+++<dm8e^Vg~)@zkH>h&rh98nXcDFue1QH8WZyhh}PU@aUDC&b#% zaU}FgCE5b;at+}X<yfyO4$+W`bWOEvlmWIw4X2EKyJEP8j9(7aZ!c({7!3e-pHXDW znRNZjx?Mn*RX%NWRB<EKSzK~uyj8eB7vSO9yRnu<(Euh-n?Fw`yx#sMuxz8ZGv1!+ z7yOlE#x2A5+A$?Sis$E{Q3<>wgk-8rWwnB6dC!!5+6iUKtZT=LTYWJq*24)F-}*{) zGI`(>UBxL*6?qxaSojR1f?l|P)=2j%XSfwKkMOS>oK+nVfv>Yty=GIVUkz#(t<w}M zmM_tMOeQXZ?Anx?O2W%kQ>+QN{WT(mW&k~ERQx>7qQb6>Dy4ZzSpik`hPLNs4bQw| zk~Nc7lLVUf1t**xjmxMV9&|hq8*f0IoJBU`GGkGO9n+qD<8zdMk)e9Mx7Ogd(S}bc zy`>#zfg+9^S4@?S>c;SKB%bQ8#|Sfzpy@WxMs2`a~m+(i+OWu^pR638PZ|Ii-A zs%6Ce$qwE^$&U=>VOOLbs|fd_bVM`HCiGyHn<cn$)RF(SYvo;^f%cb~dCutoLOre) z(b}|4auD&J=jD7L7`bq>omLy<L{mxR#c~ifE;1te!vO<zJwAQWwK!fxXyU=Dl<gla z<aq3~ao&LIX@UX*@bKzkMPuUTmJ5M_mWaxy-+nz>3MYO<Q5q9SUH))LKiZ*~7an}7 zrRcCgHI{H_w~dxwpqU{x?{+5?3MP#NloCZ(_4@<%xri(-YP+$PZO^)6EwOJMIR<^> zu$P#`9crnR4y#T5Dx)l)d*)7?F4Gc&!q_2|J#AQthMfb}hiPaiNtw5zm6N!ijH*py zpNOMd9tBGOccaoSt^13K566e3!EejUXJ`7GAySHe?VZ1#lw_U_i{J)c-R0+Q1pji| z+g!cqzdGO0D*GJt_CU11eHcR8<;L&qtCs9^OV=^!jE4JuNF*NHlVusn*nePE06*b~ z8s%WFNoOM1PAi0;$|S12;wOXIiT<$}U7f|a=OURz6jA73&2gKjbvK=8Ua=9)k1MFR z%i{tCug%hCw_VE92W0-Obm9*N{<^ZcU9?c#rE;Cc)^s2LZU3+`$!WNSs^z3J=$-VB zqq%|3ucWTQukk2_jB;^~1v3<TQdH3$^qHG#l(dN&`83=(pPE~^C~LkWIPRx@UR8{n zzbd0Ne_2SGwZ>4^5O=96DVawsNqWQHHv)%@$7m=SkD*fG#ztARGdud4dp`Ol+0?5Z zQApreFAj%`h*9B<B6}IUe@e9fA{`6w4tP_x59v1g{nq}pZejcOCV%#J;$1YndUr~U z>$mrKD$sg6xFlgMEICFvt-SiV4Q>D)2qp_|x$e7nqMSrLdRcakJVy+!{D8K)O0kp` z5hvo^=it<eci;eY%GYu?V$aEgrn3Jw4O48<oQ%AQ-q6~%A0K3}nQ&^N@Fab<4rCpc zD<4~w8@gbA)Tz7t2Dp*J>rn_|Sk+Dy$GJn=T-yF=D(2fXcSXm}_5FyfOs~EvF!Ij{ zqT?Dxcv*XSBtsCenD6fW8un!ss|Ja$_`e$euK@H)es{QgxeG=>nrE3iPP&wxG7Whu zL0YatGJHx(D*hz1I>MScjg37AxxP)i6FT3J#6}rLr{As)ios`LhOS`DtJWKAOammm z(19m4H;Hk5ZY~Io?iY1JXPTyQW~Sq+Xt18F4d%;((jf|O$W3!uF<Kg!u#Z|r<BlWx zSQ?Wzh&ED-KR`~+mpUSa3ZjtTaVujg_E~o9ylA@aUBgn@fMkK+Ep<e3C#C8~)rr?4 z?Rwlf@a$b)5;$C$4q!(Am~rYjG-2?keTBgI1gm47V${e)o?i6B`w^qGlODbo30H=P zJKkJW`(y6cLq*Gwr=+jqou3#VWpo_&TxBrpx$SOAEFO~_^R}djg@>koVbWMldxVc} zk95_|ujXzmz2vR?Q%cw91(=Vcp}D-9vYUC^Y$L6KUPLuBG<1$_t1o6gCx`(XA2jzA z(O9Jv^nI>cRoKz5=8xkqFUK56KiOQ*S3G14ou}ADY;Lcmz7bi<bNH2ivUq;%Z?5sd zY_XL(QfSmM{yQvydqmRwZ_@o!&PdmWl0Y!wH);2TK(M&!`LEsKn*;EuTP5Q$^c5SH zJo=1?6wu-i{zxSl4~#x?q9TO+O}?=3qN$L+miSrT*zq$;_}*kk>BX4e^Tu$qwllx2 zp{!80eKdDa!=8a;SAUpN5SVW)tfz3(J5SWV^7oPOcupJ%M7|-6r<EA}eM8Ck%TfE4 z^AwfM7()EhQqeOWgW!Mpw)s0De|$fQ1EV<#IJ{*N6cH<1+k)xBI9Hv}Q1ySqH&O4E O=HmVz8Nvtx!hZla!|jg% literal 7249 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 A@&Et; diff --git a/packages/ospf_neighbor b/packages/ospf_neighbor index 8b478bd..b05a2c8 100644 --- a/packages/ospf_neighbor +++ b/packages/ospf_neighbor @@ -1,13 +1,23 @@ -{'author': u'Thomas Wollner', - 'description': u'OSPF Neighborship State Check\nchanges by thl-cmk[at]outlook[dot]com]\n2018-06-15: changed item from neighbor id to neighbor address\n added events as perfdata (incl. metrics file)\n moved part of the output to long output\n2020-07-26: added parse section, alias, wato for alias and state\n', - 'download_url': 'http://exchange.check-mk.org/', - 'files': {'checkman': ['ospf_neighbor'], - 'checks': ['ospf_neighbor'], +{'author': 'Th.L. (thl-cmk[at]outlook[dot]com)', + 'description': 'based on OSPF Neighbor State Check by Thomas Wollner\n' + '\n' + 'changes by thl-cmk[at]outlook[dot]com]\n' + '2018-06-15: changed item from neighbor id to neighbor ' + 'address\n' + ' added events as perfdata (incl. metrics file)\n' + ' moved part of the output to long output\n' + '2020-07-26: added parse section, alias, wato for alias and ' + 'state\n' + '2021-09-15: rewritten for CMK 2.0\n', + 'download_url': 'https://thl-cmk.hopto.org', + 'files': {'agent_based': ['ospf_neighbor.py'], + 'checkman': ['ospf_neighbor'], 'web': ['plugins/metrics/ospf_neighbor.py', 'plugins/wato/ospf_neighbor.py']}, 'name': 'ospf_neighbor', 'num_files': 4, - 'title': u'OSPF Neighbor State Check', - 'version': '20200726.v1.3', - 'version.min_required': '1.2.8b8', - 'version.packaged': '1.4.0p38'} \ No newline at end of file + 'title': 'OSPF Neighbor State Check', + 'version': '20210915.v1.4', + 'version.min_required': '2.0.0', + 'version.packaged': '2021.07.14', + 'version.usable_until': None} \ No newline at end of file diff --git a/web/plugins/metrics/ospf_neighbor.py b/web/plugins/metrics/ospf_neighbor.py index b157f66..5ed4d45 100644 --- a/web/plugins/metrics/ospf_neighbor.py +++ b/web/plugins/metrics/ospf_neighbor.py @@ -1,18 +1,18 @@ -#!/usr/bin/python -# -*- encoding: utf-8; py-indent-offset: 4 -*- +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- # # OSPF neighbor metrics plugin # # Author: Th.L. # Date : 2018-06-15 # +from cmk.gui.i18n import _ -##################################################################################################################### -# -# define units for OSPF neighbor perfdata -# -##################################################################################################################### - +from cmk.gui.plugins.metrics import ( + metric_info, + graph_info, + perfometer_info +) ##################################################################################################################### # @@ -26,29 +26,18 @@ metric_info['ospf_neighbor_ospf_events'] = { 'color': '26/a', } - -###################################################################################################################### -# -# map OSPF neighbor perfdata to metric -# -###################################################################################################################### - -check_metrics['check_mk-ospf_neighbor'] = { - 'ospf_events': {'name': 'ospf_neighbor_ospf_events', }, -} - ###################################################################################################################### # # how to graph perdata for OSPF neighbor # ###################################################################################################################### -graph_info.append({ +graph_info['ospf_neighbor_ospf_events'] = { 'title': _('OSPF neighbor events'), 'metrics': [ ('ospf_neighbor_ospf_events', 'area'), ], -}) +} ###################################################################################################################### # @@ -57,7 +46,7 @@ graph_info.append({ ###################################################################################################################### perfometer_info.append({ - 'type': 'linear', - 'segments': ['ospf_neighbor_ospf_events'], - 'total': 100, - }) + 'type': 'linear', + 'segments': ['ospf_neighbor_ospf_events'], + 'total': 100, +}) diff --git a/web/plugins/wato/ospf_neighbor.py b/web/plugins/wato/ospf_neighbor.py index 898e6c3..7b4122f 100644 --- a/web/plugins/wato/ospf_neighbor.py +++ b/web/plugins/wato/ospf_neighbor.py @@ -1,5 +1,5 @@ -#!/usr/bin/python -# -*- encoding: utf-8; py-indent-offset: 4 -*- +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- # # License: GNU General Public License v2 # @@ -10,98 +10,113 @@ # wato plugin for ospf_neighbor check # # +from cmk.gui.i18n import _ +from cmk.gui.valuespec import ( + Dictionary, + TextAscii, + ListOf, + Tuple, + TextUnicode, + MonitoringState, + Transform, +) -register_check_parameters( - subgroup_networking, - 'ospf_neighbor', - _('OSPF neighbor'), - Dictionary( - elements=[ - ('neighborstate', - Dictionary( - title=_('State to report for OSPF neighbor state'), - elements=[ - ('1', - MonitoringState( - title=_('1 - down'), - default_value=2, - ), - ), - ('2', - MonitoringState( - title=_('2 - attempt'), - default_value=1, - ), - ), - ('3', - MonitoringState( - title=_('3 - init'), - default_value=1, - ), - ), - ('4', - MonitoringState( - title=_('4 - twoWay'), - default_value=0, - ), - ), - ('5', - MonitoringState( - title=_('5 - exchangeStart'), - default_value=1, - ), - ), - ('6', - MonitoringState( - title=_('6 - exchange'), - default_value=1, - ), - ), - ('7', - MonitoringState( - title=_('7 - loading'), - default_value=1, - ), - ), - ('8', - MonitoringState( - title=_('8 - full'), - default_value=0, - ), - ), - ] - ) - ), - ('peer_list', - ListOf( - Tuple( - title=('OSPF Neighbors'), +from cmk.gui.plugins.wato import ( + CheckParameterRulespecWithItem, + rulespec_registry, + RulespecGroupCheckParametersNetworking, +) + + +def _parameter_valuespec_ospf_neighbor(): + return Transform( + Dictionary( + elements=[ + ('neighborstate', + Dictionary( + title=_('State to report for OSPF neighbor state'), elements=[ - TextUnicode( - title=_('OSPF Neighbor IP address'), - help=_('The configured value must match a OSPF Neighbor item reported by the monitored ' - 'device. For example: "10.10.10.10"'), - allow_empty=False, - ), - TextUnicode( - title=_('OSPF Neighbor Alias'), - help=_('You can configure an individual alias here for the OSPF Neighbor matching ' - 'the text configured in the "OSPF Neighbor IP address" field. The alias will ' - 'be shown in the infotext'), - allow_empty=False, - ), - MonitoringState( - default_value=2, - title=_('State if not found'), - help=_('You can configure an individual state if the OSPF Neighbor matching the text ' - 'configured in the "OSPF Neighbor IP address" field is not found') - )]), - add_label=_('Add OSPF Neighbor'), - movable=False, - title=_('OSPF Neighbor specific configuration'), - )), - ], - ), - TextAscii(title=_('OSPF Neighbor IP address')), - match_type='dict', -) \ No newline at end of file + ('1', + MonitoringState( + title=_('1 - down'), + default_value=2, + )), + ('2', + MonitoringState( + title=_('2 - attempt'), + default_value=1, + )), + ('3', + MonitoringState( + title=_('3 - init'), + default_value=1, + )), + ('4', + MonitoringState( + title=_('4 - twoWay'), + default_value=0, + )), + ('5', + MonitoringState( + title=_('5 - exchangeStart'), + default_value=1, + )), + ('6', + MonitoringState( + title=_('6 - exchange'), + default_value=1, + )), + ('7', + MonitoringState( + title=_('7 - loading'), + default_value=1, + )), + ('8', + MonitoringState( + title=_('8 - full'), + default_value=0, + )), + ]) + ), + ('peer_list', + ListOf( + Tuple( + title=('OSPF Neighbors'), + elements=[ + TextUnicode( + title=_('OSPF Neighbor IP address'), + help=_( + 'The configured value must match a OSPF Neighbor item reported by the monitored ' + 'device. For example: "10.10.10.10"'), + allow_empty=False, + ), + TextUnicode( + title=_('OSPF Neighbor Alias'), + help=_('You can configure an individual alias here for the OSPF Neighbor matching ' + 'the text configured in the "OSPF Neighbor IP address" field. The alias will ' + 'be shown in the infotext'), + allow_empty=False, + ), + MonitoringState( + default_value=2, + title=_('State if not found'), + help=_('You can configure an individual state if the OSPF Neighbor matching the text ' + 'configured in the "OSPF Neighbor IP address" field is not found') + )]), + add_label=_('Add OSPF Neighbor'), + movable=False, + title=_('OSPF Neighbor specific configuration'), + )), + ], + )) + + +rulespec_registry.register( + CheckParameterRulespecWithItem( + check_group_name='ospf_neighbor', + group=RulespecGroupCheckParametersNetworking, + item_spec=lambda: TextAscii(title=_('OSPF Neighbor IP address'), ), + match_type='dict', + parameter_valuespec=_parameter_valuespec_ospf_neighbor, + title=lambda: _('OSPF neighbor'), + )) -- GitLab