From dce662beffb1b1ac3c6e57350c49fe7ac68f74b5 Mon Sep 17 00:00:00 2001 From: "th.l" <thl-cmk@outlook.com> Date: Sat, 2 Oct 2021 12:27:11 +0200 Subject: [PATCH] update project --- agent_based/ospfv3_general.py | 15 +- agent_based/ospfv3_interface.py | 20 +- agent_based/ospfv3_neighbor.py | 42 ++-- agent_based/ospfv3_virtuallink.py | 42 ++-- ospfv3.mkp | Bin 9626 -> 13187 bytes packages/ospfv3 | 8 +- web/plugins/metrics/ospfv3.py | 2 +- web/plugins/wato/ospfv3.py | 370 ++++++++++++++++++++++++++++++ 8 files changed, 453 insertions(+), 46 deletions(-) create mode 100644 web/plugins/wato/ospfv3.py diff --git a/agent_based/ospfv3_general.py b/agent_based/ospfv3_general.py index c6f909e..ff62f86 100644 --- a/agent_based/ospfv3_general.py +++ b/agent_based/ospfv3_general.py @@ -10,6 +10,7 @@ # Monitor status of OSPFv3 general status # # 2021-09-17: rewritten for CMK 2.0 +# 2021-10-02: added WATO options # # sample snmpwalk # .1.3.6.1.2.1.191.1.1.1.0 = Gauge32: 50529054 @@ -111,8 +112,11 @@ def parse_ospfv3_general(string_table: StringTable) -> Optional[OspfV3General]: ) -def discovery_ospfv3_general(section: OspfV3General) -> DiscoveryResult: - yield Service() +def discovery_ospfv3_general(params, section: OspfV3General) -> DiscoveryResult: + if section.AdminStatus != 'disabled': + yield Service() + elif params['admin_disabled']: + yield Service() def check_ospfv3_general(params, section: OspfV3General) -> CheckResult: @@ -128,7 +132,7 @@ def check_ospfv3_general(params, section: OspfV3General) -> CheckResult: yield Result(state=State.OK, notice=f'Stub router advertisement: {section.StubRouterAdvertisement}') if section.AdminStatus == 'disabled': - yield Result(state=State.WARN, notice='Admin disabled') + yield Result(state=State(params['state_admin_disabled']), notice='Admin disabled') yield Metric(name='ospf3_general_lsacount', value=section.AsScopeLsaCount) yield Metric(name='ospf3_general_rxnewlsas', value=section.RxNewLsas) @@ -178,6 +182,11 @@ register.check_plugin( discovery_function=discovery_ospfv3_general, check_function=check_ospfv3_general, check_default_parameters={ + 'state_admin_disabled': 1, }, check_ruleset_name='ospfv3_general', + discovery_ruleset_name='discovery_ospfv3_general', + discovery_default_parameters={ + 'admin_disabled': False, + }, ) diff --git a/agent_based/ospfv3_interface.py b/agent_based/ospfv3_interface.py index 9e01868..479e318 100644 --- a/agent_based/ospfv3_interface.py +++ b/agent_based/ospfv3_interface.py @@ -10,6 +10,7 @@ # Monitor status of OSPFv3 enabled interfaces # # 2021-09-17: rewritten for CMK 2.0 +# 2021-10-02: added WATO options # # sample snmpwalk # @@ -186,7 +187,13 @@ def check_ospfv3_interface(item, params, section: Dict[str, OspfV3Interface]) -> yield Result(state=State.OK, summary=f'Area: {interface.IfAreaId}') yield Result(state=State.OK, summary=f'DR: {interface.IfDesignatedRouter}') yield Result(state=State.OK, summary=f'BDR: {interface.IfBackupDesignatedRouter}') - yield Result(state=State.OK, summary=f'State: {interface.IfState}') + + if interface.IfState in ['down']: + yield Result(state=State(params['state_if_down']), notice=f'State: {interface.IfState}') + elif interface.IfState in ['waiting', 'standby']: + yield Result(state=State(params['state_if_wait_stby']), notice=f'State: {interface.IfState}') + else: + yield Result(state=State.OK, summary=f'State: {interface.IfState}') yield Result(state=State.OK, notice=f'Link scope LSA checksum: {interface.IfLinkLsaCksumSum}') yield Result(state=State.OK, notice=f'Interface type: {interface.IfType}') @@ -197,13 +204,7 @@ def check_ospfv3_interface(item, params, section: Dict[str, OspfV3Interface]) -> yield Result(state=State.OK, notice=f'Interface metric value: {interface.IfMetricValue}') if interface.IfAdminStatus == 'disabled': - yield Result(state=State.WARN, notice='Admin status is disabled') - - if interface.IfState in ['down']: - yield Result(state=State.CRIT, notice=f'State is {interface.IfState}') - - if interface.IfState in ['waiting', 'standby']: - yield Result(state=State.WARN, notice=f'state is {interface.IfState}') + yield Result(state=State(params['state_admin_disabled']), notice='Admin status is disabled') yield Metric(name='ospfv3_interface_events', value=interface.IfEvents) yield Metric(name='ospfv3_interface_linkscopelsacount', value=interface.IfLinkScopeLsaCount) @@ -259,6 +260,9 @@ register.check_plugin( discovery_function=discovery_ospfv3_interface, check_function=check_ospfv3_interface, check_default_parameters={ + 'state_admin_disabled': 1, + 'state_if_down': 2, + 'state_if_wait_stby': 1, }, check_ruleset_name='ospfv3_interface', ) diff --git a/agent_based/ospfv3_neighbor.py b/agent_based/ospfv3_neighbor.py index f074333..f349bad 100644 --- a/agent_based/ospfv3_neighbor.py +++ b/agent_based/ospfv3_neighbor.py @@ -10,6 +10,7 @@ # Monitor status of OSPFv3 neighbors # # 2021-09-17: rewritten for CMK 2.0 +# 2021-10-02: added WATO options # # .1.3.6.1.2.1.191.1.9.1.4.1.0.50529054 = INTEGER: 2 # .1.3.6.1.2.1.191.1.9.1.5.1.0.50529054 = Hex-STRING: FE 80 00 00 00 00 00 00 01 92 01 68 00 00 00 03 @@ -160,27 +161,41 @@ def discovery_ospfv3_neighbor(section: Dict[str, OspfV3Neighbor]) -> DiscoveryRe def check_ospfv3_neighbor(item, params, section: Dict[str, OspfV3Neighbor]) -> CheckResult: + + not_found_state = params['state_not_found'] + + for neighbour, neighbourAlias, neighbourNotFoundState in params.get('peer_list', []): + if item == neighbour: + yield Result(state=State.OK, summary=f'[{neighbourAlias}]') + not_found_state = neighbourNotFoundState + try: neighbor = section[item] except KeyError: - yield Result(state=State.UNKNOWN, notice='Item not found in SNMP data') + yield Result(state=State(not_found_state), notice='Item not found in SNMP data') return - nbrstatus = ospf_nbr_state(neighbor.nbrState) + # default monitoring states for ospfNbrState + neighbor_states = { + '1': 2, # down + '2': 1, # attempt + '3': 1, # init + '4': 0, # twoWay + '5': 1, # exchangeStart + '6': 1, # exchange + '7': 1, # loading + '8': 0, # full + } yield Result(state=State.OK, summary=f'Neighbor ID: {neighbor.nbrRtrId}') - # yield Result(state=State.OK, summary=f'. on interface: {neighbor.nbrLocalInterface}') + # yield Result(state=State.OK, summary=f'On interface: {neighbor.nbrLocalInterface}') - nbrstate = neighbor.nbrState + neighbor_states.update(params.get('neighbor_states', neighbor_states)) # update neighbor_states with params - if nbrstate in params['critical_states']: - yield Result(state=State.CRIT, notice=f'State: {nbrstatus}') - elif nbrstate in params['warning_states']: - yield Result(state=State.WARN, notice=f'State: {nbrstatus}') - elif nbrstate in params['ok_states']: - yield Result(state=State.OK, summary=f'State: {nbrstatus}') - else: - yield Result(state=State.UNKNOWN, notice='Invalid Output from Agent') + yield Result( + state=State(neighbor_states.get(neighbor.nbrState, 3)), + summary=f'Status: {ospf_nbr_state(neighbor.nbrState)}' + ) yield Result(state=State.OK, notice=f'Neighbor options: {neighbor.nbrOptions}') yield Result(state=State.OK, notice=f'Neighbor priority: {neighbor.nbrPriority}') @@ -232,9 +247,6 @@ register.check_plugin( discovery_function=discovery_ospfv3_neighbor, check_function=check_ospfv3_neighbor, check_default_parameters={ - 'ok_states': [8, 4], - 'warning_states': [2, 3, 5, 6, 7], - 'critical_states': [1], }, check_ruleset_name='ospfv3_neighbor', ) diff --git a/agent_based/ospfv3_virtuallink.py b/agent_based/ospfv3_virtuallink.py index f78aeb3..74c07fc 100644 --- a/agent_based/ospfv3_virtuallink.py +++ b/agent_based/ospfv3_virtuallink.py @@ -10,6 +10,7 @@ # Monitor status of OSPFv3 virtual-links # # 2021-09-17: rewritten for CMK 2.0 +# 2021-10-02: added WATO options # # .1.3.6.1.2.1.191.1.11.1.3.3.50529054 = INTEGER: 0 # .1.3.6.1.2.1.191.1.11.1.4.3.50529054 = Gauge32: 0 @@ -155,27 +156,41 @@ def discovery_ospfv3_virtuallink(section: Dict[str, OspfV3VirtualLink]) -> Disco def check_ospfv3_virtuallink(item, params, section: Dict[str, OspfV3VirtualLink]) -> CheckResult: + + not_found_state = params['state_not_found'] + + for virtual_link, alias, NotFoundState in params.get('peer_list', []): + if item == virtual_link: + yield Result(state=State.OK, summary=f'[{alias}]') + not_found_state = NotFoundState + try: virtual_link = section[item] except KeyError: - yield Result(state=State.UNKNOWN, notice='Item not found in SNMP data') + yield Result(state=State(not_found_state), notice='Item not found in SNMP data') return - nbrstatus = ospf_nbr_state(virtual_link.VirtNbrState) + # default monitoring states for ospfNbrState + neighbor_states = { + '1': 2, # down + '2': 1, # attempt + '3': 1, # init + '4': 0, # twoWay + '5': 1, # exchangeStart + '6': 1, # exchange + '7': 1, # loading + '8': 0, # full + } + + neighbor_states.update(params.get('neighbor_states', neighbor_states)) # update neighbor_states with params yield Result(state=State.OK, summary=f'Neighbor ID: {virtual_link.VirtNbrId}') yield Result(state=State.OK, summary=f'Area: {virtual_link.VirtNbrArea}') - nbrstate = virtual_link.VirtNbrState - - if nbrstate in params['critical_states']: - yield Result(state=State.CRIT, notice='State: {nbrstatus}') - elif nbrstate in params['warning_states']: - yield Result(state=State.WARN, notice=f'State: {nbrstatus}') - elif nbrstate in params['ok_states']: - yield Result(state=State.OK, summary=f', State: {nbrstatus}') - else: - yield Result(state=State.UNKNOWN, notice='Invalid Output from Agent') + yield Result( + state=State(neighbor_states.get(virtual_link.VirtNbrState, 3)), + summary=f'Status: {ospf_nbr_state(virtual_link.VirtNbrState)}' + ) yield Result(state=State.OK, notice=f'Virtual link options: {virtual_link.VirtNbrOptions}') yield Result(state=State.OK, notice=f'Virtual link hello suppressed: {virtual_link.VirtNbrHelloSuppressed}') @@ -228,9 +243,6 @@ register.check_plugin( discovery_function=discovery_ospfv3_virtuallink, check_function=check_ospfv3_virtuallink, check_default_parameters={ - 'ok_states': [8, 4], - 'warning_states': [2, 3, 5, 6, 7], - 'critical_states': [1], }, check_ruleset_name='ospfv3_virtuallink', ) diff --git a/ospfv3.mkp b/ospfv3.mkp index e46832ab2faabdf1ed8b4d742e190900e05650fa..5ad8fcccb6f6edaf3119ad1e22956d931158ef85 100644 GIT binary patch literal 13187 zcmbuFLz5;7({0OL=(26ww$WwVwr$(CZQHhO+pc=P^ZtVyaU*t4bCQ!>u@`<6BqV@Y z-2en&=VWi{%1q0|z{JSNz`#W3%0S0VXJcg#bmQaYw8hbQ_kANbkhDxI<z*sTtlMg9 zam96HyviDpWMpP~nL4H>Djh!qD{3h@8Oxq>=Q|H1aVVMaNX*QAe9OVMU=9=`Ruu4y zct2$FJcP{8`yCAd`bh74?3dGf^t=BT$8GtF`{?04EO*Ii`vG3{F3ilGP4aT2ohoiY zqSwJM@vQ2`W-*JRl|1$5qV%iQs%0@t->>)6`=BIkfeg0r{Aq+pivDb2^7dNe6@>I@ zU9Zd+MW__Nh(N&UeJV0_HS8(Q&hBgV8)Al^yf2JJ*clxnp)K9!5Xf=6_`>t?5|z5t z3%O>g9D8If*!|)U#ej0V?iz)RnVN1O+WQ?0x76y?*6(yw!kW$=Xv4=uI3o;-m!OF! zzl=in^J%$bEW*bcyPASF=THrP88$uS_x;l=Pu4Pgq8baiydL!ND{d!Ip~jyqD}EW> z8hNZQCqCh^N#wh3mO~96P=~NxXu-Vsx8gs8GnT0@gTUPqKP&=Totekoq7H-2gEt+q zOIIB`Te@v?<gRMI98n8>Y`!v|E3REVwsQIEUQcuFPw1F=ws-YcktA8eUC{H^$c7*K zjY||Fd%otmxOn(mfLs0~itpIhK6V_*SNyBl{06|BAwW<53t(v2vu+8{bFN0u{s`kK zInA4QTE%BdujZdeY6^<5FM&Ra1N6ERV)^B((D1w^vL9_*&8j-ti3a_JN?h@Ah}bhw zlBP0bX^B^`v((d9vg!enhPf%L3&>nwp=AQ&GJW<}WT?{4)K`HbR9DF*V6MbOw^a6k z-Yqq(v_}S$zp-ZPuY<$F{=$E=2d#k#z2CU$V<zj1dfju$Q7{7J9U;PAA<qJciLlIg zFqZtngvqGPSkB)h0o*~o+~=?b0P!EQ902WASXB%@jnsK-2|zM04e(-_G+bBveUA!l zSM$Q_3NiS*D{Te%@BTcR?*}i#o5fQ<pzcj?NM+qmXB0B?!=D$==vU-i08M;$wE+IP z#cHjzcs#^CL&FRmXqo4!4esvr4E23X60S9%B^)nG5UjQ{oZB~?Z@)o+56Y(nidH-P z(!ar(-26Ex!hi|eKDYL}XmZu)TSNYT3g)y9rTBXQ>jRHOdD2}=Tmot+wJUD=_vk(6 z9NRwbL4KX@Jg4*eJ$nc%%baXq&!?rVrihbdg;#3DSExDsBG`6XA2&l-2$Nx-gmu;> zILM>C?ip4h>nVya9urBEFrqnKOp`vM6N%%C=t&9iQX}RQbz`n;`0QUn%#QwcerI@i zsY-8x%K$C0jXDVrb&WPS>Z{bHs%exY)zeF5TRyUyO<!>=?H|QOUzJ5)lNguJ6qQxX z@u1f1cD)>T^`5cN6)$V$o-r}0I0rN&dauDN4QMOy?^h6jna(=zy1C<c9OE}mKm6w- zkZ&wR(Sd)rvy;@L7C-s515_IB<m$xU%hMhtE+rUu6lj@R4&`YCU`uOsVN%n*f*rn_ zL@?r{ZdD;0t&T#}vu_l31}W~xqf;DN-x9>HV@lOaXE)^3H_yYW&+Ez<N7ML>PHfFL z(8d4uD5vNhO)mX8jyc&nFqw4!Bf5u=-v{uZ6uqu@4l|oOzqgk}O7ZhEH1UvSFX%^3 zJ*MGrtKd($#l6xQl@(_S(=0RmiT^dhGe|w^)!WK16kzxwD9INPD*#Tl>`&9twTy`> z9qcyDn=^2QqU1O52*a&t*r-v{{jTeqGpd^o=FW*Xrr<xRlq7V{vq#;z{3n3o15t>u zIZ^T#tAqv^S$OE}*RDY2xa<Yk?6<`)ID(7>=G#?h(<`}~kT$CyV`^#EqN_OV>STjV zhAb7o^kZr*>YXMgA_fsT7RhMwgGaN-EkZ5z88ZrPO-Sk=yP7g!_^nLx&}Xv5Gqi+i zRa=JKvNvS^63qIv20a<|`4p{6(XjH6d>h-4GH<uL;D@Yd7V7P_{#MJ1UUoiOi>Sr@ ziT&WE+vWz4qaFbxvv|<?k@(8&Akm=m^BMCE^-JD8$ciR&NwT*`9@%hij#4~yB_MwL z6}x$c+fv89dM705Xy@jBawYM2jnUa@bZOYZ;zaHiowK#zY$<*{4VOv}=|WE*E%6f= z`G{18wzG(INLLwO2GO;oD7l3jf{whc+#p9wr;--udWmx75cQ=cJmF#|JJYjtjG#<3 zN8G)#626vMsQbOus4bJT98Dvb#(sB5r1mQ%Tz7e}Y<!^Q`B@_qfg~q=7L!V-hPvDF zdIrmuWPKto9u<K6j|P=1x~jt9*Q%lzOZUS)`_gnWRiJPR4i&+4F?9i=F(CQLoSB?@ zXP9scG(Xil;e)o2wXEwFx#z%?Ovm1xtn#Ilr!#NCXVDV<^=nsrfavL7(>GF8X?)QX zwNMsI50l`cKl!MpzM)E<b@rxkq;y>lK?c9oJ$eq@zR#HM7^g<*%FG!6QDsN7md<32 zSdxo`di(46`E(JsC(EshQPR$+^aaKo#9vRqb^uZgq+99Y{d_S_#}(u{q)XR;n#EI( zW><d!*G-Kgup7lAJ~jF;x?x}lk9#8lg^cU#`$#LQ*ikd6$zKu*$quenVw^(O{k7|f znDT0cC{~FykHKkEyl$>oTUY3DL`uwJu_bO+LpSk5F_q;dlG98lJ1IQ9R9RgJ1aUk0 z8wI)C-weSblg^`i_?r6PC)oh3Aqt&5kCyEq=V@D~ru*v6u;!+o9r@1gV-{@;by1-A zJLg(V32dVO3i4}Q?`dIx;Ll9$Oz3k>2zPAAP(SQXr7J_0j4OpdEo;41X!?^*Jv!|y zUCW%eTkYiY3f*-5h6yhX!*tC99FN>wgK^ZvbYw)IE`wUxq&Njhfoxv|%HS#Ri1x{d zzQ@PMW<hbgx9v`ga;+802)fx6u2jE@4jrQr%AqK;P`z^fdqB!eaewB~zbR`(ErRqw z1opmjSP>kMEX<P%L5XRbekHY5zlD&TN6akk@*TxU!_iQ>!v0a98)Xe8g-TMXGxjCx zrlR`bdR~T>7jBjI%~k2fBY>43;Ai4$BDgUGq8sYLI^8MaTa>8>!5V*u(V_(^j%aT} zD4z&hiYP!IRphW&X8M#a!ID79p(#{?Gnq03Q>+-5(&T9=PlB6mkx7~=5uf6CIWj`o zm|B)(%0;ix*;s02UV?M`5y$vmLgQ1b=@?XRl9OUiQamWx%-ojeuXlvzMYT5uOfWs| zmwYKt)a|TfGj&<9Ev}OS8pTMEqtqLeRx2ah3wxIh9#3LyTZe<(4{B$K8s9DQ<)@<C z@aduBDnJTD@*w5xRPd_&=_`}7<WTTQd41%y-X;gp4J(R7wp35@kv*4ccYg(2*Wx^L ze)mgypC1Kd<!KBMcH7?X2OnC5?<w2IkHP)`Ap7le`p&yxmM(_|%V&|b>pPRBv1<^3 zxoRtt!><nS$4$DF0DjNI@VSNE8QQhiC#r*>9?u8a8qC!=_T%m|C@IfJD!9uyA4R=@ zJ3hDAV{AQ<FU7@_)M)nd`fvpD+M^&uORnv2)KfT(1)H)D$7E52uWC$DK&j0*Yd37; z{-XlJUrj;Urj}52rCeR4<#OGw!aZJsYiv8jAk98D(v<#wu0MbG0?!g)tro4=C2S9U zV}N*E>Zu^IdNVcEvs1W+AO17wdz(iP_=AsF7m6F85naZGGS>}G$Nz%dJh<@z`3NGg z<s5jZr*}XD^;=MUFOxKbHF<sQu;u>y=^AgeZN`^S!U!^RYST&XA(ozdzaU?Zqhp3b z?^h!6urzZCJ<_?~lzni%eb2`To{Q|X{&77;**Qe1osM27r;PVbx_qwF*zS2&=S_F= zmliQ~1*hkAa3rTs!Y-0Uy;gxSTWJBUg*Gwvy_N+cGUKj+R5d$h0;YhUj+Ohv-FSL8 z8_(_0_VRvb39+lB$XQ(cGBlRPmyj!~JYgTcA?^hq-i!<&yMT(@3&Rz8?lD_=&s{ms zO=ciW5`=svDi?0j>>IK}jWm0%PVUa&P1xtOfWe10Fen_j9R*t^Gp;mLZfOFhE#rR` zTfx+-rA?J4e$I+ii@bZkT>_VLc`YJcdWe~=HtuUXnQboAA3+IJeHoW62>X4yDI)RI z#{n%n0SWuoCP8YhCq~tMJf*^ts4CzwbE3fFYRNQ18=^cVHveexalF7}Y!tm!s%~Z) zpS)hk05*Lf>z5(U$*I%&Yh}67PSa>(k_D3I+=mVRn;61IZ_UN=n4NZ7ohfe-(@OEM zu&CpPR-Hj-LuGJv>~82YvzY_?DTVzGURAVahJ)-8zG~u1=g6Jru>wiUpk)+dO*hv8 zEmm&YeyELKH$|PzL!;mJ*xN6Aj?@SBwgG|PYV*b7cn1E3uzxa{6z1Er=*X<EgSsnJ z03Vs~Zu;~6dE`+;q|c${oUbK$>T)bf@p`m1JI%Xbw}3*po}``j7t~La?S93xr7=Km zcjowDkIo*v7=c=j3f}yrReuy5V1XfB<X~J|K1%nCRLDKetniNNq{J$#&K%=%bXJcQ zz&hF~WF4b$i=pe4x}S;gXHv#m<GV(FFtI;Ne~>C<*O_L-f3_n-KKg_tWyn@CCO_*s zFsfK6nqqCC@f@ej+oXVVBQSMjLOF`UrFcFm(jh7Q%hPZ`W}<+JQR4kHD$lond5WNO zY4k&Pl5x<ec~B)RT`fKSMmNKgq7@zFPett~{Rt#~5v!_EwULZ~+q^VG(qT$m;*~&_ zw1PYV?{Ykz4spipjm5b@yxF*Pp*$ga@(Bv|Lka&|KhlDJsSrKIUkBEqWq5X9Pr!s; z#W9x$OfV~P-aWt3HZwavYhg|y<0=G?Xd)G`+<<M`2oG!UXgxWm%I1fgA!klxf`XEk zh@nzYvh_)dO6A8Y3{tY%3q4Y<PwhLspW!I;nBPt>_x04n-a`A@uwT}D9pCTO^Xr$^ zVc%bC+Y`^9W=4IVVsn|@28_+tc??q5k~ozw(?h|Pa~x9Fs*u<*5(@30r+Q0m8=1jz zR*pE7S<*smYDd?}a1+in1bm9@Fq2`XGo-^Iu0;;z6Kx{6va)4Qc_~^oFZt1Jg^-)n zCF)1I5;xR=Xz2=*iFVTEOMm0_XaftfHa!m%Zcr5-n!nna6|#sQ2q*f8LyDeioF;`L z8`+wmE0`+{z_aQHH>rwi7dt_kFMQ=HcSD-Om-VuKotO45^_@64siqZ5{z~HKwf`F} zvskAc&6iqMzs{D?3N0%Lmp?AaBe<)HZ}Q}Kv|)7p@EpVi)H%)sKLllT9L6SgO2JUf zZcIETBqkTI5IuO@k-(D|oIOFECNEz_K?-&ZiBpO43~;Ki30lb~uc1b(bUPRD&+}n| zanv&_I3N4=cl-B$qHw7bSRoaCVY}xkq45q=pt^rCMlOE8M`46fI12wte9HZ{xSf>Q zEn=gDQQY(-hHKL%P$Rvxi1;L!=aXWEiwM&$$dh1MM2HbBs7te?E(=II6CmMQM!<m+ zZ6;JQfVW^PCgj-$=98C?hbzD8pw!fLf0$9;#LGDi4?D!kIhTlp$z7XO4#B~DVEk4J z_nuzwt0*S2saKl~8h%V=IJ<_~f$4orVY)$wBdVq3yd><8*g$9KpH-pQx+K;JTeM#r zOAZl&|DGFD<|+nIIwMpkooHbBMgKt3>)d^80|W#SD#T3?K8=oPohARn|M{M@*b@#7 zyrSX0(EFjl+r&qUx(AAl>M4rjAx_y;<uyq2Fu$K!cz$eu0~UpW5Q}-Wk4U{iy@B+u z{p2e}5rc_5jWb-=iF+tz{>Pi(LyEX>T%3~a1~4Rp!&&}dG>rlBc@TKmN9v_CxTU1n za(r9`dag!3XJy`ViJv*)mUIYb7T!I^S9kKei`excT%Ebd5lNpYaPr7<j(O1CJQ}Fs zsw6-{F*yv!c92w6Ry8QU&BhLm@87vD^ILF_JVV2-_jb%qkv;4Qk#Na(je*}s6nOXP zlJWwHv;?8@?5al*&TE~DG(%O*=K59&P+4Sp5xfyb@(=2Vu1A?P$aTsuVXm+M%Z=o= zJlaoLLgjk9L>C(<5-C?%8;y{5cIcM;m1te}vZJ{w9|Z`2>cmLg-_#Jll6j{;!Ny7w zQ9UZ=CjmxaSe3=l^gr@62jz45_a|_O6TkB2?S!+C9seyb1Xjm8>0v92CRg5d><DHS zC~hzm8h1zvz1WU1^iRoyAyQAo3JW^^Xs&N73`C8CN~qNGRSloR%s=B{0P`jt3LsQr zbg+-ld})EyGl8ffYrTD!IV6Hl$o$r&{`*247pD2J98AuM6QZ407)a7KSY3}>$hieV z5uMwUkCMU~2gC}C+VS*_;>BFgGbkDyRrR$~3GQ7V6MW5jm6{JdN$x5_xrSxWRnP9} zPCTd4DD{Hi6@uLlm$1*d#?M=*-!{7j10ro(xRde(JKgh5iOu6@V7S{744zy14_u<@ zefDsGxvK)wcXcm?pBwWymCg<9bvxRk5o`{!+gL%FzXE#Tn7lN@IT>;<^!hu#vU;Bm z?n9kCrRtJvxnJQlnu>tZNfS?60|-w=*@Uvu(Rx(%(4cm|l;vCsbWl+peSzP=q}UjB zA#o$PA`w=E=>!7?m5kE7N{SoFg7d3r5ihbikXE5W)VatIlLZrp5tM(Hb-7le29Ldv z(^2GwkcE>3^C}1IAD`rPCb1;NPJqPN1~zO1iIa(Ug~oPn=18!)NqKKWv5I|lWFB5o zf7e=qG%Sbk@imc=8s)rnwkRjV2xLz%9q8Zk*hLrT<T5q&up;W>nS<05eOd*F7L8k9 zjaw$*_Q!r7r|IdARL}B@!6)MWmQe~|g6jCIMs+inOD%ymj@F9EgejmyYj)8hKaEeu zZ7CJqg{evgg~(w>5K*Tf4NwZIIX2C5^9JZD7lBw7?i50?mSXsUXh)I_Pb0&Q&gQ|3 zI$(~IkyRkRJS4z`PR#4}8K8Z3{$}D`&V6N{AN%{P_I*w!4<GflB7+)^5}|49%9Q+D z+20#SF}fuE0{?vK_oJ*SX(^9vb&GvfaPG1K;8rb+?i&l&p6263No=%|E$md=z?sIm z6u`I?z`0bA9l?JxmJK@}W7uoso+k{URlTPGB*t<~Rb}9Zj>#32v&l+BZ6$X$q|g3n z-T(xEQ*{}={_i(jcHGiZ=cy&Lk}fpZEY{9y;cP-RwQw_cTI9u5YCR!?)chm1Fb}?g zcC&iwomQVR;MXT?P{C#1loaB4+FcaV@q5quPjWApMh>kcvTkOCZ*ee1o%pq(I6)Ev zk&9=bIop*qRD5lm1_4rA<bbGRxZ=IErr48^2(=1$cnb3*zIm^YX5mf`&a=<&BIDP| z7vOk4*vGC2vYqwQsGgzJuL-<DZEr^mg;(bP+R5{JJMiLIxoX9LN5N`k@9sy7vnvP% zCkJ-XVM4%*7}my&P#3Vr%l%Ck928n!L@;nq5CRsT=-sKP)X<<R?<tCus*i=tO-KAH zrcux(_z~QQK~fL4>o%#rm_98HbB3hA2?oDLsmOU(<v;mAIimG_OM?n$2ICYa;a)Zf zGE{XPNqZafcMOI!Pgo*h8>&I|(%4RR%&Dz^odFjcUS*1_2B}gGLfCj`XmI7@upa2a zq>jqyz40#s))})^zn7uglb>6A?e)FR`b1Z)lCy^R1r?rXnXBs9yd<v9nX?oZI1Hhc zVSvz6n&Oaz^I{8m{unvFoj4k{|6r|k0yy(Z^s1`hd@A$l1MMp}SR2>J{W=KHG)oWI zA9WZQ^*`|Ch9FU^<=X#PYyAJBB!3xN_|X=B%nbg#s~mjI(A@`aP*_+Df=P{5JeC&C zjarN=vlBLpWV&)@QL?+gvM3T)>JRssfZ5pSvLTdhr!OZ!*^A!V@hV0BH7@27$k$I{ z7NR|tHLGs=J1FhLH^C+I&&gi8WQBP-J+;F1()J>P+I}RI(yHLQSLk*y@L!Ct@Km-W zRY6Vx^CKb|KK;ZE7nXhK2#x9B&54o<=VW}IuciOrCTq=T1Ko~D1mT8Ckiq!b3I#4= zcm7KO18(9&g-7S>%_-QeeZ@z;I1v0;tQi1b%wk(S{aC3&uZ}gL)+^RQ0=|A55IsH} z9jAjoI0VL_v!*>#jh(2(#)w*j=^mYpMXe%MZQW;X<nZ*@i-LbTZ3Wp)7y*->=0=7W zb>|h_71bcyM4#n&>kL?)<!E?)Q_GmBE1|qZyO^73cm@XooX1^Bd*l@no9){Z3IDO- z=_^p*UBcUi&NKEO;`%TTIGRpu`fjl^hWR%Rkw}^Pv7FVE67Yn!eR%yw_(voI?05)f zs{z%f0%7=!=^(p}?_|OmUDcDf04HUV&`0mNZSV@G&a9V0f#`(<l7w|!PNgi7aSPd! zw!81=P*|B5b4Ly6O{x_r%K`Qf&fy<6vkcK<@@j$J$SbxK;Vd0%H5fp~n|g9hHy9;z zSOK00!`TEdi$`f!0kcyrN_K{Q6nj7y%h;XnIF<=8hep~8C%~Y@eXYD2@*0&Vn!g=f zbsDYk)DarJV6emri6+rZ6BZ4Vgwf^Z7d2mgO5FL^(w~5t43!}s^P)(A0DcBRB0LAO zOj>nBz*dI<3plowj?5IhAQrmAMucnA5lC8?D^;>dp{{^8-U3!&SU4lrGSdb^cJuQ+ z$wo|evot=*210DpB!Fn^Z(MWC1Ho2wOmka^uQe^fe2SAW$@FiP!9GRpKzIQ1m=rEa zwy|TgK>IdSZAcI@tup3cS@92ln4@;GpAP}cYS=d4Y};-i|F<}J;H^*q7Wd<aAE?{5 z-Mt>{@02?rs|g%1^6Zi4E%`W%!4Rf9y}C9C=}GDVVVGN!X0`yc_345j1J~1CV>fvA z+_H}oI-213Iz`3CrfhwlY|O=A%tbNbL~*(ii{Z3BWAAB@rZVU7_l0`qIJ2|7+4n~$ za#J)YyP$)F*8NnCCso$zu2W=Ax+#!ZS5eDI7q4u!E=CbKgXffkAvMKdMJhCikzS|h z)rxpl-8Gc!YHcvPp_}N|IxIFZ!R%AD_yHp0UN@wbW->IMu?Ws*bq~?2)ClUm=@;&t z8J8X|V0AB)zbcUL3TC}27jl>?$m7JEd8e-j=aO}~p!*!kwg}>Q1*i3OjMKViz?~JN zuB_kvM=A~F-iIWn+hhqfEOK%i){#mw)D`ImBt%`v97?I<anpZ!QEos=p(LDkD7p+q z5xrx?hV~k4_wac3UMrfImn^^E9;(hPB|+PpEj*~wn#_x;T-rG@r0=4&>9Tk7jw{jB z87yYMK_^hwxw&(m@f=96=@6clN`_KyBs`@`;LlWgS}qWp(uo2T1DPqa|ENe|Ybr2a zVhGKzwmGB18Xhj=tD>?;e@p}jtI=5LB?jtSpXe;n7Kn%GG+nv|aW!?>ynbN&ZCP41 znqO80Gf6rN-@jw<(I`Iy(>a5lVD?iSs#EDS{aHQCuaYu$=Gs_bX0!5f-l0IuMJmUN zU?}Q2^T80P&RYGUU}a=JnRM!Z?bN<PG~y8xN?NeR7<npDXh&6=hzzC5-w6yx-a6c= z;tPiG5D^dBZo?5Cs0RjH@&-cuY~%I(q)gY`ptY5T3+^gcr^r@2S&<hN(OkOm#r&Ri zOe8X6uI%RN2}`mFjHbIZOj0oE>9z4W`-UKXu<g=n^Fac-jsYO^(rTMQz<QlXl;>7f z4yXfql~3B5?ctDHnh{|HL(bFqfe1?vTbPuiGq0%qhBz-p`y)7KaW1C0?F3tnj=9au z^(T<LE*ae?IJ}HOh<c?k3YfsAmNJ%8z&?SUw%oaLWl>wQ=l^vsHsmi3TKHSCSIk=V zHf67v(F?7@Uz0Oqda!n*3@CA)=b4sG&F0K_REqEprRg0^^BDo}!0pTpKHNfc<Fmii zlMj+`fXREkpa5XNNp$vZ{C;1y;2!S&o|kOjniKZ7S`c7yzmEw8{opR_Nxw;8brHV) z&F}0_fJ;a<Y%bQhV|WZoob|8NnyJ=gL3nC3e9Lk|Xq3rA9Y%u<;<{-F(m5;UF}oDg zQzGVPV8_zF#2Ar<wT6&xf)2try-vNbqZHdHZ5D2Uos+oGl}4LE-CzSxqp}%GHDuy3 zS{5)5fFV*%S3aS~%JlAhqfFrv{LS6R(ZvODFZ9<&ViD{5t7RiEHxHjT+oQujl4+gI zKy1i)s`&&rDJbB<4vW?E;Sl`e?amL-B`Valt~01Ib=dk`mmT=K!GvmjR-eeG6Z#Cb z+^~lnIawuKgeCf7o?C)XZowj_01cd-{klS3bpj{8x>x;ere&b^-D}svA*M~nM?G=? zG%={XnyToICk1>FgHK<Nqw<?xoK2s$A|F%o(_((ucB@uP_FhNC|4Tgg`fon(n^l$I zR>bAHs~Ys~7ov}{b<28Kp5Qh3dsZ9Y>%!nIQrh#%gmMjqcaZ$|*erq)n)M=2eilvb zM_2fPZIZ&4jZk-lhJ^*NZp>l@M+-)Y|FnHU=JiHd28YoMw%a>JdFGDU0M(7L`y1X= zzt$G;=WGorTk+NQBxILW^S}kRxciT!_tAAPj&6{jR4B<!@i5Uw`#hm>kr8V+&;$_) z1G0$57u2eNF-aD}*Wd~<eQ$eyD9P+SlI4Y8z>Q8&CWQhBQgU$j4ne1|L)8wcbQ+lH zW@<kxvH8@S86wu^dqu@IOu$M>e*k5Naty}7v{P}NusB7l5$E8K085Mu1*xC`nl@R3 zNgQ|F8RB(y>))-Lf1W>*b!Fds891HyPR{*()7A}JLmgyrWDfrNhmbFULp@fHCAJcH zR%U%BgpW{LdeW<S*-%GP%jm5;KW4#chSw`0{0}vNf93RB$}a*thU+qRFI=krj*8pT z0;|2osDJD+LatvRAr+qJZCQWP@EAu+K!F&QUQ-%GWZ9cOYw4hR^P_rFRjmPlUA3|$ zKxoD&+phvb%Zij#+uUNM3G0+VLSb_~3%Er~6kiY>G2qBE)pe#%0cT;F_tuZ)<`#l` zfW&^N9H^EcTK5wVhGfaYz`9~5H-<V9p52hC;priX*CPhnIM`hdUQXn<C;hrBEki2> zJ(Ba8EGf6<8QOx@%8(3Kg(2f@*mc18tinH1d#5b?(4S;a>jnczXgi<2hC;Vl_2xB% zyCeJg_tv)wW`?R=2hDW6)x52(E9(VICSF+}Bf}tTbn`TRAN;+WnQg)=N(-gTSE#g1 zHPO-<MV<Yh_l&Lxf^%~$6P&B~-{y_n<j0sg^aCkFMyS?u$<0$MNUc+5G>JMDaseLd zmwusN4Dq#N?yXpiho*-|2V8<{xCcsvrzR!8keq7xtYkUN#AI_^@P(J27h<SOT1$5N zQf$|an^0D*bWQ7_IPGH67St;ESA8_hh{fH1<z?#ykLt_~aC|}lW7bauh&QQ_reWf4 zj!)7m?;9zYHp$gWM(7J4aL1CTsK1tK;QqBxQTVGhGrUsCnZXJ-T=!`DWxn3Up2t-O z%hKnH?YgY#S<j)yFCZ?*OV138iO-ygOC%4jReW0IYq)>Vti`m>Seg_cz~s2OA~Rix z8xs1Zi#TFIl<OjO?ay|qdU)OjL*VxV#>x?43-~A9MSwG<O}hd04pQ82k-9x%uP2r& z$%H!=^(v^uxo+JUmZm=#q?I|Np-AM$EZmb=gk&{g;qArpS$#(}6g*uwZK?%o4#%{o zxfIZrYPG7lT(tUkR6i&_!G6h5nWVjYU=@9*6qQqR0O5a_A3pu8KTTco6~et4gfJ|9 z8A^^pe4Y5HA9#5WbwH<JX(^9)qsCzUr%|M2I0#pxwZGNWVIVF^^Ikh`P=g91`+*ik z`}DUqdF>H=085};yZ~<p`mp&Pp3Nv}YzK?&E5-z_0e1&G_<JpXw;K}B-}9+Ts)eH6 zy)*emXs)<gyFZu55ou_b&JmP=8Vaa*Xgl0OlqiOc3LmDz_SDUxwE`)vaTV~c)e<0r zwb$hh^g<~g+u*mZ<?tXZ{!wU)HE_j^&+^sB@(!^okIb_Fx14qHyPa2Nc|Ihi4)_3I z;-2r^f%?qe_@l>;@b}|)2AX^UQB{u^F~`_%<R0r5b#)Yqw-&u%^nHLK2d^N!uV1fu z{KUJyKSvAsAHQFmx%@{z__0lUPES+NwqIyUx=~cjvJ-MFy}R!^dz_F>agC;4hMKPH z4x@q`Tj{;U<P(d+!A5pILq9!Y4@^X5Z1HKw&--b6M}&jw`6c*;+8Fl<(8>sKwn5K# zigmoQq3Ese;J-8y-JIL90=N05dBvr^SOi{%Gn)TvEu%`CmPD>$^S>c$Es~Ce9`FT4 zdXJsO>(0+IOiV`ggq*`*WdDYYn#X=?ap>I4R%!v4NrBI&IZJo#75}~K^#X5Z)j#`( z30)EqM_}>5Lgixt=n=#Q%RAC3V~<;GJycpWvouj4;GB2Dt65-k34JobYZIrO_UVac zFQ^z<7tkd6K-`px?9v=|&|IBOvxi+;xN52t<(h3J>Ip`{vZH_L_H3x_s#&MXk>YzQ zw<-VJiiArVrtWQ}P_f_;9HcU*RTdv{+VIP}2$<DkU&u)jb9M~Dyz-J&cOTuPHuczW zSIaG5$B9mg0&UeCy{I$VSPv)2;@+t;$!)8Ye@coL@ssS-S{hZPHHu9&=9kXOz_&9G zl7bnrlB#2}LV%H)n>HDy=9m1YX#Oq9l9RF`&&yD+NbFdkuOUc>ttypG{zr~V-8N+` zuSp(YkRzon`T#N#%GFI4ah;vWMU-fUUm-?S!(pTU_;YDkZTabTY5fn;W%$cIY3Nnc za%xfK)k~JZ`T#U4=f^y+daU>8n~)Z|Yu7VoP_znMPj-fXh#XaBb9)W<o)zI^5T<P? z>LZQ`$+?F}Xw1hrSD;x<$heGt5oy1hlB2D2?dtvvhOZrIP`OE(_mytz<}6;bw0lob zb<y}qJT<W~zEW*V97+Acxm_tFSK9Zm_=DHDEKA1~T?3i$W6rnn2f)qK$G^f@e{$9b z2T^-;{D5zEz4+A^HuLM(1hC@)tnd<i3tDZ|`uQ$Fy7^P{@?ug<5rGIHadPu(6m@tv z5OFSUMBP<5d<XC378&1uJ<GLE+zus@+N=UomHR^Z(1^Vr`uOrgyBQp70bn%0p-&>) z7#|;?7Uc`Xj&HN$HYyg(>;+hAU+jE6LDAdmu@N`6FnO7{tGva&k#&P>MENEtrS1yf znQISRH@|lYr9R^*B$*u-kzO*<XK=|oD=Ok$rTLvS&@_%^4E{;Kg4R15iWGxd0(GH& z{7^WL_1n71RPSe5??3NqX^m&?&0kB7t`!yf0-7AV{z1v1#0z&g|EYG8gMrhcCyqxg zRgt0@A8B}<0Oul2NhYmNiFJq0v;8LF4o$>StweTP<C3gF9i*c#eHy3a+qWN~hUZ$f zTu=hpT#ZRncWrU3Lx845%hi|%MOP}9a?pHQAv1X^tRBdcMz*>QY3I9EKL>Mzbf!@5 zHq7uSs`nubX&A+|3r9mhv<}?geMAEc?^2!8M-~XrwokEfQ=xT+%Om~TmT4G;uq}jo ziE`S1l2za~&Pp(jjcdhof7Vkr?Q;VvPg+?%%2xO7ju%6|XI6Y%x0JSs$|4dA)pQ~M z81EfX5^I01Wns7}*kF>krwLRuYKm&(7iCi_<id3q!lH%PEF5uDQKJ|D(A@ATMi}v^ z+Vx=H)CG5RzujFQFGN!gl5;NP?hG8Jn470CTlafI$e9r7*bp}ysN@Vu9XCV?Rbgr1 z`~>G@m(8VD)1Lh&=ec}-e#<T84ChATiV$e%*mw$DM6gq01m#iLzz(cyhd1{*H>8b? z0`#exRuNUhBTqg*7^1*@uH{#Hx*i^^h5q5TI=4ML%Pzz^47F8y^dBuvO6?Xl_TlE_ z+~zmVf~vFvjH$N9sHA5<J%oJzqh`I&@P9hlul?33Z`oxHQ@)wYtX|HWL?H;X!$66L zzHP`?#~5GjPz%_uYXgIZmxF1$HJX=p`dm*?5iO((ts_Uv8*&HZm;dVP_ZNq`HZ$Xy z)e9)AWwwdhDbQi0D3I8Z;61;+Fm}Mr1zTCmzkia`JVp#FY7j-A#c?Qu$gwFwc@)<X z)#W%;(I_73gb#O;W;-9>(0yg<5Qkh5>F-sC*OBVrL*W~I?Oh3U>B!hudV;CD=>n?T zC1sKlYhN^JVxHI|@aJMS!5Gs5vaKL4)f==GLWVV9=W=r4&$4z+;361mncx-XpC*SO zq){NCT&2_X#H|GxwT_Jf&ER3DatcYcG1f+s3|53-;M<he!@=1?VcK;cL$k-=m3B}E ze<Gpt(zIKM-Vb%Igw;FDnub6f0JPq5+r@}t2YhdMvmno>P8@uk#aBzgRfILNLOc(Z zAxL4tD_Kj2qN7bN-m7G+0md9oc1^1aJ)_2eKQziKw*Pv*NSOC;e{<aCnj@6KyF`@v zVa&oI6LFH;^TfW6c|;UzB2P{@)jk{|wMf&|z`4x`In$aj>`|k53zfjKgRp3yLY7#a zMq=kAkn69*nbn`0HlI+ZjGzav{%98)HaLlPt!mkG6=6U{%~MQQ3ZL(<aK-vdmst)l zYAzB6WON(IblNwvWvZKXAl~bLO$JqM&^Vf9_JyijU%meam6;B1nyEyGh$oT3Ub8Sr z>uu>?*xJS7p#6Ln1hrIUt|Qs%D)XHcPeXIuGTA+UWV46JvpB-s4^iSGs`>mj@dr5* zQ?i#txPZDjJIs<PvHv2f;*GOX2+*x?h0uvWx0HI}wePC(nVm8#C2p|rpIS<+%5*=} z>E5%gjwr{y*n_`K4u44kNf%&%7xzxY^wk<HLVDkjMIt3z^~RZG<onqmjPq?**WSm2 z4Go@_ee|Tj{4u@vP9+Pi)m5^)Z>R33b{CqZA>RQFI8A7sk5VW{G_G7GgxZg^K4o_i znQq-2j~;<0ql0^9)<qr;@0gT817Q_O=74UnAb|6J2@E>N^0KwgkiFBGLKhh`tIKzL z!c2yQz`jm*QW>wsen6?LLqAX97Kx3rj)R5q$EMOGy)?Ay5-L7Bp;D!1&jVL&XJqiK zet|%@hu6|QhTs4@@ybA|Hp1UZQ(hkqAI!8dd3+S!rihzPjU*8?V~5fR3Lq&Yc{n(> zSd@hi#oEqJTBc(ss3A1GP6Yj&C`LTR5<dBkoa97@-Iy8}sdYd<Zjyg7e84#;_nS^x zKc65=Mr1GG`P957L8+E89hgY<HcfvT7CjB6dA5~sfxlBfN$wZ$2S>~pe6<LJR!df| zaXS;Y+2*qYULiuO31ya!>J-{YwTA{qyVMV?%Q#FS&~sj0uYG|)3u@2EXaa{3s{53Z z$<{b8#lzU6R$kX*dabC?KaE=aAoy|FpKD;+R-<iP2OqFz6lar|YxF>apz1L8x3c-W zGHgn`HC;`p7CU7M+Xi|pqgmZht;flk6q_7KlvjBNJ<;<gN6Gqec)rKpo9{R1rWzU! zu12;2S#?Fj>~ZNF5=Gc>1Yyo!&ad%h&>MLyze78B(rZXG#-MJ0^$FC`M1ao>E$W27 zkQawPgBQohD;>A;m*lkG-k8&ln>|qdgacno9bwkNZKyRdZ8>Ku<aTs-vZgQJD9wUB zw~zT{i^<U{rpOzbjF3^)cJMU}Qi~*+l%hnxd}F*$-BwqP4k5E^%W%GF-DnhKw-~0; z4`c`?|BHjVl_Vu?%a}TyCCcUr_#W#o2^yu2Ird4U56*SmD*kAqDA?9;)*1ilI<I?U zgO~ppUB_qsXr?+=(uX6XYT(&0OJa>yBdpk0^p;vpn~brRU_Oa8cX)=sXm3;63Tf7# zcTLi_gKx6Y#7}l-rx!y>kqp|+bJ{5x!tqR&V;$!1`%^J!#07{`0!aVGnEuNVQ+LqD zKQgJIQPz9BT1Wx6hk*Kw{-^oKYvDjn0^%@OUHla$fe%$GG+IqFZW+ojgag+st`NE5 zV-VPu%iC86ov9CzCv5zkLY#yFyDBO#BzdE5j#<#zm8p_9DnXV2B_;LcBvV^Ph`DY# zF`zjw`kI}-Dt&w(#&CR(mMeCbW6u?rP@Jq2imr@zRzcStnl1~WFi>WKDotmg`YgZV zEl*1fKH}QHa~3jq`!j-+st!SkrO1xT8HLcxCnnR4Ij|uX1u}=uj*g#6x-pYw2)YUw zx7f?Vf{?Bk^c1y$Of<*h*=HN9{$LX-<4PY>6NSz~EHy!b!GE}EpRgTSSC&UZW5$}T zmMt{}*PtPq=RoaTTJGQLaGN@r7h0OOv^i3v{(@-eC4k#3rKtn|TW_3V)dfq(GX0c$ zfWW+#&>gBSNz9b;v}UolNG_zmAZ9>X`KH~5EiOpDA$qG2Mr}DOsII}vq$V_MqoBj3 zAVWQb<m}n4^~`SM3w+?8=pFV@e9H5?dqU3ul8MfpKYwC+Ok=(<Z4pO}MRe8vM*;&J zNqI;w=gZ@%CW>V1BH4f9(L~bI=||<T|7(K&tbtM~bvw7E!xz0lJ$k`EtU%FQ<5ym_ ze*|JStZAy``P(8UoogSZxP@EwLvkyoJM0=3R5w$k`Cy%QE*aylIzD8>q5D0Xx<KY_ z{FHN-3Y?wzNRJ(II<l=OlJ_M!z_^w4KiN3JnmXAAcl~nw=j8cp3BE5!6|>`qmQD6^ zS&Yr!DwrE_Kf5vhVoU~WBauE!Q|o)!Tf^pgztS6f$cMAV#7zosLE}R!Z8PO9<wkCs z9CBTObg@kpTc9)`qn<~aDl46!)P01FVX4S6UEC|c_J^}@p7ePnC<=#sHLA@H&NC^( z>0CB@VPN>;U-kg-wK4$9y`ag*YXBzj+!kW}-K*#-yGrSdvTKYB+MB*&Q}PuM#YS?P z>L%Pe;&Mf=@3seOi?(s7xp-sWIhTqq?g<@gZ$d-DhS#3rJ=Rq4BV;)l_16N*Q;1rq zA3i0a#=oW)hWV8S4wa6$>cp)9LVH_0E1jv=nqLcjZHu_}Wg^PDcz`8M^H_&PsBNRr zQh0Y}1vG7?Suh=?GHhi=)^5!<&H1=Q)_T{F5#~S3)xSFy4W=qGLR+-09)<BT*6_UP zj`Z}<OcVO)?nK>mX}{?+B|AKCyk?qzcKuQ%RMCw;RY{UT0S~s4x58)wI?(aAzL%6f zTKth7JQe<pv4t^$zo+Yi8X4(7%`S&WRyE{jk&35aZ6XceoLjba^#U`8%$@58VW)&J z)8wA*SRl9W_V|sx_cV9tb=gt~F<9QK`c3FFU$ffS2udaK41XK!NH^W|S<nst;M_~G j)_HSVu3R!rY7={CnR{yS{})Q1-#-jS2AM$dz(D^8wL<y< literal 9626 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 diff --git a/packages/ospfv3 b/packages/ospfv3 index 5ac9a2f..be7a5c4 100644 --- a/packages/ospfv3 +++ b/packages/ospfv3 @@ -7,11 +7,11 @@ 'ospfv3_neighbor.py', 'ospfv3_virtuallink.py', 'utils/ospfv3.py'], - 'web': ['plugins/metrics/ospfv3.py']}, + 'web': ['plugins/metrics/ospfv3.py', 'plugins/wato/ospfv3.py']}, 'name': 'ospfv3', - 'num_files': 7, + 'num_files': 8, 'title': 'Collection of OSPFv3 checks', - 'version': '20210917.v0.2', + 'version': '20211002.v0.3', 'version.min_required': '2.0.0', - 'version.packaged': '2021.07.14', + 'version.packaged': '2021.09.20', 'version.usable_until': None} \ No newline at end of file diff --git a/web/plugins/metrics/ospfv3.py b/web/plugins/metrics/ospfv3.py index 58e9e6c..0cb2beb 100644 --- a/web/plugins/metrics/ospfv3.py +++ b/web/plugins/metrics/ospfv3.py @@ -46,7 +46,7 @@ metric_info['ospfv3_events'] = { 'color': '26/a', } metric_info['ospfv3_lsretransqlen'] = { - 'title': _('queue length'), + 'title': _('Queue length'), 'unit': 'count', 'color': '16/a', } diff --git a/web/plugins/wato/ospfv3.py b/web/plugins/wato/ospfv3.py new file mode 100644 index 0000000..b6a632e --- /dev/null +++ b/web/plugins/wato/ospfv3.py @@ -0,0 +1,370 @@ +#!/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 : 2021-09-28 +# +# wato plugin for ospfv3_neighbor check +# +# +from cmk.gui.i18n import _ +from cmk.gui.valuespec import ( + Dictionary, + TextAscii, + ListOf, + Tuple, + TextUnicode, + MonitoringState, + FixedValue, +) + +from cmk.gui.plugins.wato import ( + CheckParameterRulespecWithItem, + rulespec_registry, + RulespecGroupCheckParametersNetworking, + RulespecGroupCheckParametersDiscovery, + HostRulespec, +) + +neighbor_states = ('neighbor_states', + Dictionary( + title=_('State to report for OSPF neighbor state'), + help=_('Map each OSPF state to a CheckMK monitoring state'), + elements=[ + ('1', + MonitoringState( + title=_('1 - down'), + help=_( + 'This is the first OSPF neighbor state. It means that no information (hellos) has ' + 'been received from this neighbor, but hello packets can still be sent to the ' + 'neighbor in this state. During the fully adjacent neighbor state, if a router ' + 'doesn\'t receive hello packet from a neighbor within the RouterDeadInterval time ' + '(RouterDeadInterval = 4*HelloInterval by default) or if the manually configured ' + 'neighbor is being removed from the configuration, then the neighbor state changes ' + 'from Full to Down. Default monitoring state is "CRIT"'), + default_value=2, + )), + ('2', + MonitoringState( + title=_('2 - attempt'), + help=_( + 'This state is only valid for manually configured neighbors in an NBMA ' + 'environment. In Attempt state, the router sends unicast hello packets every poll ' + 'interval to the neighbor, from which hellos have not been received within the ' + 'dead interval. Default monitoring state is "WARN"'), + default_value=1, + )), + ('3', + MonitoringState( + title=_('3 - init'), + help=_( + 'This state specifies that the router has received a hello packet from its ' + 'neighbor, but the receiving router\'s ID was not included in the hello packet. ' + 'When a router receives a hello packet from a neighbor, it should list the ' + 'sender\'s router ID in its hello packet as an acknowledgment that it received a ' + 'valid hello packet. Default monitoring state is "WARN"'), + default_value=1, + )), + ('4', + MonitoringState( + title=_('4 - twoWay'), + help=_( + 'This state designates that bi-directional communication has been established ' + 'between two routers. Bi-directional means that each router has seen the other\'s ' + 'hello packet. This state is attained when the router receiving the hello packet ' + 'sees its own Router ID within the received hello packet\'s neighbor field. At ' + 'this state, a router decides whether to become adjacent with this neighbor. On ' + 'broadcast media and non-broadcast multiaccess networks, a router becomes full ' + 'only with the designated router (DR) and the backup designated router (BDR); it ' + 'stays in the 2-way state with all other neighbors. On Point-to-point and ' + 'Point-to-multipoint networks, a router becomes full with all connected routers. ' + 'At the end of this stage, the DR and BDR for broadcast and non-broadcast ' + 'multiacess networks are elected. For more information on the DR election process, ' + 'refer to DR Election. Note: Receiving a Database Descriptor (DBD) packet from a ' + 'neighbor in the init state will also a cause a transition to 2-way state. Default ' + 'monitoring state is "OK"'), + default_value=0, + )), + ('5', + MonitoringState( + title=_('5 - exchangeStart'), + help=_('Once the DR and BDR are elected, the actual process of exchanging link state ' + 'information can start between the routers and their DR and BDR. In this state, ' + 'the routers and their DR and BDR establish a master-slave relationship and ' + 'choose the initial sequence number for adjacency formation. The router with ' + 'the higher router ID becomes the master and starts the exchange, and as such, ' + 'is the only router that can increment the sequence number. Note that one would ' + 'logically conclude that the DR/BDR with the highest router ID will become the ' + 'master during this process of master-slave relation. Remember that the DR/BDR ' + 'election might be purely by virtue of a higher priority configured on the ' + 'router instead of highest router ID. Thus, it is possible that a DR plays the ' + 'role of slave. And also note that master/slave election is on a per-neighbor ' + 'basis. Default monitoring state is "WARN"'), + default_value=1, + )), + ('6', + MonitoringState( + title=_('6 - exchange'), + help=_( + 'In the exchange state, OSPF routers exchange database descriptor (DBD) packets. ' + 'Database descriptors contain link-state advertisement (LSA) headers only and ' + 'describe the contents of the entire link-state database. Each DBD packet has a ' + 'sequence number which can be incremented only by master which is explicitly ' + 'acknowledged by slave. Routers also send link-state request packets and ' + 'link-state update packets (which contain the entire LSA) in this state. The ' + 'contents of the DBD received are compared to the information contained in the ' + 'routers link-state database to check if new or more current link-state ' + 'information is available with the neighbor. Default monitoring state is "WARN"'), + default_value=1, + )), + ('7', + MonitoringState( + title=_('7 - loading'), + help=_( + 'In this state, the actual exchange of link state information occurs. Based on the ' + 'information provided by the DBDs, routers send link-state request packets. The ' + 'neighbor then provides the requested link-state information in link-state update ' + 'packets. During the adjacency, if a router receives an outdated or missing LSA, ' + 'it requests that LSA by sending a link-state request packet. All link-state ' + 'update packets are acknowledged. Default monitoring state is "WARN"'), + default_value=1, + )), + ('8', + MonitoringState( + title=_('8 - full'), + help=_('In this state, routers are fully adjacent with each other. All the router and ' + 'network LSAs are exchanged and the routers databases are fully synchronized. ' + 'Full is the normal state for an OSPF router. If a router is stuck in another ' + 'state, it\'s an indication that there are problems in forming adjacencies. The ' + 'only exception to this is the 2-way state, which is normal in a broadcast ' + 'network. Routers achieve the full state with their DR and BDR only. Neighbors ' + 'always see each other as 2-way. Default monitoring state is "OK"'), + default_value=0, + )), + ])) + + +# ##################################################### +# +# OSPFv3 General +# +# ##################################################### + + +def _parameter_valuespec_ospfv3_general(): + return Dictionary( + elements=[ + ('state_admin_disabled', + MonitoringState( + title=_('State to report if OSPFv3 is admin disabled'), + help=_('Monitoring state if the OSPFv3 process is admin disabled. Default state is "WARNING"'), + default_value=1, + )), + ], + ) + + +rulespec_registry.register( + CheckParameterRulespecWithItem( + check_group_name='ospfv3_general', + group=RulespecGroupCheckParametersNetworking, + match_type='dict', + parameter_valuespec=_parameter_valuespec_ospfv3_general, + title=lambda: _('OSPFv3 general'), + )) + + +def _valuespec_discovery_ospfv3_general(): + return Dictionary( + title=_('OSPFv3 general'), + elements=[ + ('admin_disabled', + FixedValue( + True, + title=_('discover admin disabled OSPFv3 process'), + totext=_('discover admin disabled OSPFv3 process'), + default_value=False, + )), + ], + ) + + +rulespec_registry.register( + HostRulespec( + group=RulespecGroupCheckParametersDiscovery, + match_type='dict', + name='discovery_ospfv3_general', + valuespec=_valuespec_discovery_ospfv3_general, + )) + + +# ##################################################### +# +# OSPFv3 Interface +# +# ##################################################### + + +def _parameter_valuespec_ospfv3_interface(): + return Dictionary( + elements=[ + ('state_admin_disabled', + MonitoringState( + title=_('State to report if OSPFv3 is admin disabled'), + help=_('Monitoring state if OSPFv3 is admin disabled. Default state is "WARNING"'), + default_value=1, + )), + ('state_if_down', + MonitoringState( + title=_('State to report if OSPFv3 is down'), + help=_('Monitoring state if OSPFv3 is down. Default state is "CRITICAL"'), + default_value=2, + )), + ('state_if_wait_stby', + MonitoringState( + title=_('State to report if OSPFv3 is in waiting or standby'), + help=_('Monitoring state if OSPFv3 is waiting/standby. Default state is "WARNING"'), + default_value=1, + )), + ], + ) + + +rulespec_registry.register( + CheckParameterRulespecWithItem( + check_group_name='ospfv3_interface', + group=RulespecGroupCheckParametersNetworking, + item_spec=lambda: TextAscii(title=_('OSPFv3 interface'), ), + match_type='dict', + parameter_valuespec=_parameter_valuespec_ospfv3_interface, + title=lambda: _('OSPFv3 interface'), + )) + + +# ##################################################### +# +# OSPFv3 neighbor +# +# ##################################################### + + +def _parameter_valuespec_ospfv3_neighbor(): + return Dictionary( + elements=[ + ('state_not_found', + MonitoringState( + title=_('State to report if neighbor not found'), + help=_('Default monitoring state if the neighbor is not found in the SNMP data. ' + 'Default state is "UNKNOWN"'), + default_value=3, + )), + neighbor_states, + ('peer_list', + ListOf( + Tuple( + title=_('Neighbors'), + elements=[ + TextUnicode( + title=_('OSPFv3 Neighbor'), + help=_( + 'The configured value must match a OSPFv3 Neighbor item reported by the monitored ' + 'device. For example: "FE80::192:168:10:148 on Vlan1"'), + allow_empty=False, + ), + TextUnicode( + title=_('OSPFv3 Neighbor Alias'), + help=_('You can configure an individual alias here for the OSPF Neighbor matching ' + 'the text configured in the "OSPFv3 Neighbor" field. The alias will ' + 'be shown in the check info (i.e. [your alias])'), + allow_empty=False, + ), + MonitoringState( + default_value=2, + title=_('State if not found'), + help=_('You can configure an individual state if the OSPFv3 Neighbor matching the text ' + 'configured in the "OSPFv3 Neighbor" field is not found. ' + 'Default state is "CRITICAL".') + )]), + add_label=_('Add OSPFv3 Neighbor'), + movable=False, + title=_('Neighbor specific configuration'), + )), + ], + ) + + +rulespec_registry.register( + CheckParameterRulespecWithItem( + check_group_name='ospfv3_neighbor', + group=RulespecGroupCheckParametersNetworking, + item_spec=lambda: TextAscii(title=_('OSPFv3 Neighbor'), ), + match_type='dict', + parameter_valuespec=_parameter_valuespec_ospfv3_neighbor, + title=lambda: _('OSPFv3 neighbor'), + )) + + +# ##################################################### +# +# OSPFv3 virtual link +# +# ##################################################### + + +def _parameter_valuespec_ospfv3_virtuallink(): + return Dictionary( + elements=[ + ('state_not_found', + MonitoringState( + title=_('State to report if virtual link not found'), + help=_('Default monitoring state if the virtual link is not found in the SNMP data. ' + 'Default monitoring state is "UNKNOWN"'), + default_value=3, + )), + neighbor_states, + ('peer_list', + ListOf( + Tuple( + title=_('Virtual Link'), + elements=[ + TextUnicode( + title=_('OSPFv3 Virtual-Link'), + help=_( + 'The configured value must match a OSPFv3 Virtual-Link item reported by the monitored ' + 'device. For example: "2003::192:168:0:1"'), + allow_empty=False, + ), + TextUnicode( + title=_('OSPFv3 Virtual-Link Alias'), + help=_('You can configure an individual alias here for the OSPFv3 Virtual-Link matching ' + 'the text configured in the "OSPFv3 Virtual-Link" field. The alias will ' + 'be shown in the check info (i.e. [your alias])'), + allow_empty=False, + ), + MonitoringState( + default_value=2, + title=_('State if not found'), + help=_('You can configure an individual state if the OSPFv3 Virtual-Link matching the ' + 'text configured in the "OSPFv3 Virtual-Link" field is not found. ' + 'Default state is "CRITICAL".') + )]), + add_label=_('Add Virtual-Link'), + movable=False, + title=_('Virtual-Link specific configuration'), + )), + ], + ) + + +rulespec_registry.register( + CheckParameterRulespecWithItem( + check_group_name='ospfv3_virtuallink', + group=RulespecGroupCheckParametersNetworking, + item_spec=lambda: TextAscii(title=_('OSPFv3 virtual link'), ), + match_type='dict', + parameter_valuespec=_parameter_valuespec_ospfv3_virtuallink, + title=lambda: _('OSPFv3 virtual link'), + )) -- GitLab