diff --git a/agent_based/ospf_neighbor.py b/agent_based/ospf_neighbor.py new file mode 100644 index 0000000000000000000000000000000000000000..0b1662a6496cb99a7cfbbe2f21ffd602a135ba84 --- /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 Binary files a/ospf_neighbor.mkp and b/ospf_neighbor.mkp differ diff --git a/packages/ospf_neighbor b/packages/ospf_neighbor index 8b478bdf6d1fbc63c585715d3a5c60d5c8ca2aa9..b05a2c8dd0f7c126c1b5f676b4d53f241a1fb566 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 b157f66bf19fcd0eb148f649e33935705d35c397..5ed4d45e87fdabc7cac868a9224380f8e7286a67 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 898e6c3d605da2b531c9311dc4f9538300b24a24..7b4122fb8bdcfb0a28a9a3af3fb759ba4882fdc9 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'), + ))