From e033d4b7263d022fe3aa3ed66ca80ae509663dd3 Mon Sep 17 00:00:00 2001 From: "th.l" <thl-cmk@outlook.com> Date: Sat, 18 Sep 2021 20:50:59 +0200 Subject: [PATCH] update project --- agent_based/ospfv3_area.py | 77 ++------ agent_based/ospfv3_general.py | 92 ++------- agent_based/ospfv3_interface.py | 72 ++----- agent_based/ospfv3_neighbor.py | 113 ++--------- agent_based/ospfv3_virtuallink.py | 130 +++---------- agent_based/utils/ospfv3.py | 306 ++++++++++++++++++++++++++++++ ospfv3.mkp | Bin 9372 -> 9626 bytes packages/ospfv3 | 5 +- web/plugins/metrics/ospfv3.py | 2 +- 9 files changed, 399 insertions(+), 398 deletions(-) create mode 100644 agent_based/utils/ospfv3.py diff --git a/agent_based/ospfv3_area.py b/agent_based/ospfv3_area.py index bbf909a..fc87fa8 100644 --- a/agent_based/ospfv3_area.py +++ b/agent_based/ospfv3_area.py @@ -43,7 +43,7 @@ # .1.3.6.1.2.1.191.1.2.1.16.0 = INTEGER: 2 # .1.3.6.1.2.1.191.1.2.1.16.3 = INTEGER: 2 # -# sample info +# sample string_table # [ # ['0', '1', '17', '4', '0', '18', '646544', '2', '1', '2', '3', '0', '0', '1', '2'], # ['1', '1', '3', '1', '0', '7', '317410', '2', '1', '2', '3', '0', '0', '1', '2'] @@ -69,6 +69,16 @@ from cmk.base.plugins.agent_based.agent_based_api.v1.type_defs import ( StringTable, ) +from cmk.base.plugins.agent_based.utils.ospfv3 import ( + ospf_area_impotasextern, + ospf_area_summary, + ospf_area_translatorrole, + ospf_area_translatorrolestate, + ospf_area_submetrictype, + ospf_area_teeenabled, + get_area_type +) + @dataclass class OspfV3Area: @@ -90,61 +100,6 @@ class OspfV3Area: def parse_ospfv3_area(string_table: StringTable) -> Dict[str, OspfV3Area]: - def ospf_area_impotasextern(st: str) -> str: - names = {'1': 'import External', - '2': 'import no External', - '3': 'import Nssa', - } - return names.get(st, st) - - def ospf_area_summary(st: str) -> str: - names = {'1': 'no Area Summary', - '2': 'send Area Summary'} - return names.get(st, st) - - def ospf_area_translatorrole(st: str) -> str: - names = { - '1': 'always', - '2': 'candidate', - } - return names.get(st, st) - - def ospf_area_translatorrolestate(st: str) -> str: - names = { - '1': 'enabled', - '2': 'elected', - '3': 'disabled', - } - return names.get(st, st) - - def ospf_area_submetrictype(st: str) -> str: - names = { - '1': 'ospfv3 Metric', - '2': 'comparable Cost', - '3': 'non Comparable', - } - return names.get(st, st) - - def ospf_area_teeenabled(st: str) -> str: - names = { - '1': 'enabled', - } - return names.get(st, 'disabled') - - def get_area_type(AreaImportAsExtern: str, AreaSummary: str) -> str: - areatype = 'UNKNOWN' - if int(AreaImportAsExtern) == 1 and int(AreaSummary) == 2: - areatype = 'normal' - if int(AreaImportAsExtern) == 2 and int(AreaSummary) == 1: - areatype = 'totaly subby' - if int(AreaImportAsExtern) == 2 and int(AreaSummary) == 2: - areatype = 'stubby' - if int(AreaImportAsExtern) == 3 and int(AreaSummary) == 2: - areatype = 'NSSA' - if int(AreaImportAsExtern) == 3 and int(AreaSummary) == 1: - areatype = 'totaly NSSA' - return areatype - areas = {} for area in string_table: AreaId, AreaImportAsExtern, AreaSpfRuns, AreaBdrRtrCount, AreaAsBdrRtrCount, AreaScopeLsaCount, \ @@ -194,12 +149,10 @@ def check_ospfv3_area(item, params, section: Dict[str, OspfV3Area]) -> CheckResu value=value, label=label, metric_name=f'ospfv3_area_{metric}', + render_func=lambda v: f'{v:.0f}' ) - yield Result(state=State.OK, summary=f'Area type: {area.AreaType}') - yield Result(state=State.OK, summary=f'area scope LSA checksum: {area.AreaScopeLsaCksumSum}') - - yield Metric(name='ospfv3_area_nssatranslatorevents', value=area.AreaNssaTranslatorEvents) - + yield Result(state=State.OK, notice=f'Area type: {area.AreaType}') + yield Result(state=State.OK, notice=f'Area scope LSA checksum: {area.AreaScopeLsaCksumSum}') yield Result(state=State.OK, notice=f'Area import as extern: {area.AreaImportAsExtern}') yield Result(state=State.OK, notice=f'Area summary: {area.AreaSummary}') yield Result(state=State.OK, notice=f'Area stub metric: {area.AreaStubMetric}') @@ -209,6 +162,8 @@ def check_ospfv3_area(item, params, section: Dict[str, OspfV3Area]) -> CheckResu yield Result(state=State.OK, notice=f'Area NSSA stub metric type: {area.AreaStubMetricType}') yield Result(state=State.OK, notice=f'Area Traffic engineering {area.AreaTEEnabled}') + yield Metric(name='ospfv3_area_nssatranslatorevents', value=area.AreaNssaTranslatorEvents) + register.snmp_section( name='ospfv3_area', diff --git a/agent_based/ospfv3_general.py b/agent_based/ospfv3_general.py index 7e0594b..c6f909e 100644 --- a/agent_based/ospfv3_general.py +++ b/agent_based/ospfv3_general.py @@ -38,7 +38,7 @@ # .1.3.6.1.2.1.191.1.1.24.0 = 0 # .1.3.6.1.2.1.191.1.1.25.0 = 0 # -# sample info +# sample string_table # [['0', '16843038', '1', '3', '1', '2', '0', '0', '256', '51', '0', '100000', '1', '1']] from dataclasses import dataclass @@ -53,7 +53,6 @@ from cmk.base.plugins.agent_based.agent_based_api.v1 import ( exists, Metric, OIDEnd, - check_levels, ) from cmk.base.plugins.agent_based.agent_based_api.v1.type_defs import ( DiscoveryResult, @@ -61,6 +60,14 @@ from cmk.base.plugins.agent_based.agent_based_api.v1.type_defs import ( StringTable, ) +from cmk.base.plugins.agent_based.utils.ospfv3 import ( + ospf_stub_router_advertisement, + ospf_admin_status, + ospf_abr_asbr_status, + ospf_stub_router_support, + render_ipv4_neighbor_id, +) + @dataclass class OspfV3General: @@ -80,69 +87,6 @@ class OspfV3General: def parse_ospfv3_general(string_table: StringTable) -> Optional[OspfV3General]: - def ospf_stub_router_advertisement(st: str) -> str: - names = {'1': 'do Not Advertise', - '2': 'advertise'} - return names.get(st, st) - - def ospf_exit_reason(st: str) -> str: - names = { - '1': 'none', - '2': 'in Progress', - '3': 'completed', - '4': 'timed Out', - '5': 'topology Changed', - } - return names.get(st, st) - - def ospf_restart_status(st: str) -> str: - names = { - '1': 'not Restarting', - '2': 'planned Restart', - '3': 'unplanned Restart', - } - return names.get(st, st) - - def ospf_restart_support(st: str) -> str: - names = { - '1': 'none', - '2': 'planned Only', - '3': 'planned And Unplanned', - } - return names.get(st, st) - - def ospf_admin_status(st: str) -> str: - names = { - '1': 'enabled', - '2': 'disabled', - } - return names.get(st, st) - - def ospf_abr_asbr_status(st: str) -> str: - names = { - '1': 'yes', - '2': 'no', - } - return names.get(st, st) - - def ospf_stub_router_support(st: str) -> str: - names = { - '1': 'yes', - '2': 'no', - } - return names.get(st, st) - - def render_ipv4_router_id(nbrRtrId: str) -> str: - nbrRtrId = int(nbrRtrId) - firstoctet = nbrRtrId / (256 * 256 * 256) - nbrRtrId = nbrRtrId - (firstoctet * 256 * 256 * 256) - secondocted = nbrRtrId / (256 * 256) - nbrRtrId = nbrRtrId - (secondocted * 256 * 256) - thirdorted = nbrRtrId / 256 - nbrRtrId = nbrRtrId - (thirdorted * 256) - nbrRtrId = '%d.%d.%d.%d' % (firstoctet, secondocted, thirdorted, nbrRtrId) - return nbrRtrId - try: OID_END, RouterId, AdminStatus, VersionNumber, AreaBdrRtrStatus, ASBdrRtrStatus, AsScopeLsaCount, \ AsScopeLsaCksumSum, OriginateNewLsas, RxNewLsas, ExtLsaCount, ExtAreaLsdbLimit, \ @@ -151,7 +95,7 @@ def parse_ospfv3_general(string_table: StringTable) -> Optional[OspfV3General]: return return OspfV3General( - RouterId=render_ipv4_router_id(RouterId), + RouterId=render_ipv4_neighbor_id(RouterId), AdminStatus=ospf_admin_status(AdminStatus), VersionNumber=int(VersionNumber), AreaBdrRtrStatus=ospf_abr_asbr_status(AreaBdrRtrStatus), @@ -173,23 +117,23 @@ def discovery_ospfv3_general(section: OspfV3General) -> DiscoveryResult: def check_ospfv3_general(params, section: OspfV3General) -> CheckResult: yield Result(state=State.OK, summary=f'Router ID: {section.RouterId}') - yield Result(state=State.OK, summary=f'Area border router: {section.AreaBdrRtrStatus}') - yield Result(state=State.OK, summary=f'Autonomous system border router: {section.ASBdrRtrStatus}') - - yield Metric(name='ospf_general_lsacount', value=section.AsScopeLsaCount) - yield Metric(name='ospf_general_rxnewlsas', value=section.RxNewLsas) - yield Metric(name='ospf_general_extlsacount', value=section.ExtLsaCount) + yield Result(state=State.OK, summary=f'ABR: {section.AreaBdrRtrStatus}') + yield Result(state=State.OK, summary=f'ASBR: {section.ASBdrRtrStatus}') yield Result(state=State.OK, notice=f'Version number: {section.VersionNumber}') yield Result(state=State.OK, notice=f'AS scope LSA checksum: {section.AsScopeLsaCksumSum}') yield Result(state=State.OK, notice=f'max # of non-default AS-external-LSAs: {section.ExtAreaLsdbLimit}') - yield Result(state=State.OK, notice=f'Reference bandwidth: %s KBPS' % section.ReferenceBandwidth) + yield Result(state=State.OK, notice=f'Reference bandwidth: {section.ReferenceBandwidth} KBPS') yield Result(state=State.OK, notice=f'Stub router support: {section.StubRouterSupport}') 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 Metric(name='ospf3_general_lsacount', value=section.AsScopeLsaCount) + yield Metric(name='ospf3_general_rxnewlsas', value=section.RxNewLsas) + yield Metric(name='ospf3_general_extlsacount', value=section.ExtLsaCount) + register.snmp_section( name='ospfv3_general', @@ -230,7 +174,7 @@ register.snmp_section( register.check_plugin( name='ospfv3_general', - service_name='OSPFv3', + service_name='OSPFv3 general', discovery_function=discovery_ospfv3_general, check_function=check_ospfv3_general, check_default_parameters={ diff --git a/agent_based/ospfv3_interface.py b/agent_based/ospfv3_interface.py index 2c135ad..9e01868 100644 --- a/agent_based/ospfv3_interface.py +++ b/agent_based/ospfv3_interface.py @@ -60,7 +60,7 @@ # .1.3.6.1.2.1.191.1.7.1.25.18.0 = INTEGER: 2 # .1.3.6.1.2.1.191.1.7.1.25.23.0 = INTEGER: 2 # -# sample info +# sample string_table # [ # [ # ['1.0', '0', '1', '1', '1', '1', '4', '120', '6', '50529055', '16843038', '6', '1', '4', '104400'], @@ -101,7 +101,6 @@ from cmk.base.plugins.agent_based.agent_based_api.v1 import ( exists, Metric, OIDEnd, - check_levels, ) from cmk.base.plugins.agent_based.agent_based_api.v1.type_defs import ( DiscoveryResult, @@ -109,6 +108,14 @@ from cmk.base.plugins.agent_based.agent_based_api.v1.type_defs import ( StringTable, ) +from cmk.base.plugins.agent_based.utils.ospfv3 import ( + ospf_if_type, + ospf_if_admin_status, + ospf_if_state, + render_ipv4_neighbor_id, + get_short_if_name, +) + @dataclass class OspfV3Interface: @@ -129,59 +136,6 @@ class OspfV3Interface: def parse_ospfv3_interface(string_table: List[StringTable]) -> Optional[Dict[str, OspfV3Interface]]: - def ospf_if_type(st: str) -> str: - names = {'1': 'broadcast', - '2': 'nbma', - '3': 'pointToPoint', - '5': 'pointToMultipoint', - } - return names.get(st, st) - - def ospf_if_admin_status(st: str) -> str: - names = {'1': 'enabled', - '2': 'disabled'} - return names.get(st, st) - - def ospf_if_state(st: str) -> str: - names = { - '1': 'down', - '2': 'loopback', - '3': 'waiting', - '4': 'P2P', - '5': 'DR', - '6': 'BDR', - '7': 'other DR', - '8': 'standby', - } - return names.get(st, st) - - def render_ipv4_neighbor_id(nbrRtrId: str) -> str: - nbrRtrId = int(nbrRtrId) - firstoctet = nbrRtrId / (256 * 256 * 256) - nbrRtrId = nbrRtrId - (firstoctet * 256 * 256 * 256) - secondocted = nbrRtrId / (256 * 256) - nbrRtrId = nbrRtrId - (secondocted * 256 * 256) - thirdorted = nbrRtrId / 256 - nbrRtrId = nbrRtrId - (thirdorted * 256) - nbrRtrId = '%d.%d.%d.%d' % (firstoctet, secondocted, thirdorted, nbrRtrId) - return nbrRtrId - - def get_short_if_name(st: str) -> str: - names = {'ethernet': 'Eth', - 'fastethernet': 'Fa', - 'gigabitethernet': 'Gi', - 'tengigabitethernet': 'Te', - 'fortygigabitethernet': 'Fo', - 'hundredgigabitethernet': 'H', - 'port-channel': 'Po', - 'tunnel': 'T', - 'loopback': 'Lo', - } - for item in names.keys(): - if st.lower().startswith(item): - st = st.lower().replace(item, names.get(item)) - return st - ospf_interfaces = {} interfaceinfo, interfaces = string_table @@ -233,11 +187,8 @@ def check_ospfv3_interface(item, params, section: Dict[str, OspfV3Interface]) -> 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}') - yield Result(state=State.OK, summary=f'link scope LSA checksum: {interface.IfLinkLsaCksumSum}') - - yield Metric(name='ospfv3_interface_events', value=interface.IfEvents) - yield Metric(name='ospfv3_interface_linkscopelsacount', value=interface.IfLinkScopeLsaCount) + yield Result(state=State.OK, notice=f'Link scope LSA checksum: {interface.IfLinkLsaCksumSum}') yield Result(state=State.OK, notice=f'Interface type: {interface.IfType}') yield Result(state=State.OK, notice=f'Interface priority: {interface.IfRtrPriority}') yield Result(state=State.OK, notice=f'Interface hello interval: {interface.IfHelloInterval}') @@ -254,6 +205,9 @@ def check_ospfv3_interface(item, params, section: Dict[str, OspfV3Interface]) -> if interface.IfState in ['waiting', 'standby']: yield Result(state=State.WARN, notice=f'state is {interface.IfState}') + yield Metric(name='ospfv3_interface_events', value=interface.IfEvents) + yield Metric(name='ospfv3_interface_linkscopelsacount', value=interface.IfLinkScopeLsaCount) + register.snmp_section( name='ospfv3_interface', diff --git a/agent_based/ospfv3_neighbor.py b/agent_based/ospfv3_neighbor.py index f75eda6..f074333 100644 --- a/agent_based/ospfv3_neighbor.py +++ b/agent_based/ospfv3_neighbor.py @@ -37,6 +37,8 @@ # OSPFV3-MIB::ospfv3NbrRestartHelperAge.1.0.50529054 = Gauge32: 0 seconds # OSPFV3-MIB::ospfv3NbrRestartHelperExitReason.1.0.50529054 = INTEGER: none(1) # +# +# sample string_table # [ # [ # ['1.0.50529054', '2', '\xfe\x80\x00\x00\x00\x00\x00\x00\x01\x92\x01h\x00\x00\x00\x03', @@ -67,9 +69,8 @@ # -import re from dataclasses import dataclass -from typing import Optional, Dict, List +from typing import Dict, List from cmk.base.plugins.agent_based.agent_based_api.v1 import ( register, @@ -80,7 +81,6 @@ from cmk.base.plugins.agent_based.agent_based_api.v1 import ( exists, Metric, OIDEnd, - check_levels, ) from cmk.base.plugins.agent_based.agent_based_api.v1.type_defs import ( DiscoveryResult, @@ -88,6 +88,17 @@ from cmk.base.plugins.agent_based.agent_based_api.v1.type_defs import ( StringTable, ) +from cmk.base.plugins.agent_based.utils.ospfv3 import ( + ospf_nbr_addresstype, + ospf_nbr_hellosuppressed, + ospf_nbr_helperstatus, + ospf_nbr_helperexitreason, + render_ipv6_address, + render_ipv4_neighbor_id, + get_short_if_name, + ospf_nbr_state, +) + @dataclass class OspfV3Neighbor: @@ -107,85 +118,6 @@ class OspfV3Neighbor: def parse_ospfv3_neighbor(string_table: List[StringTable]) -> Dict[str, OspfV3Neighbor]: - def ospf_nbr_addresstype(st: str) -> str: - names = {'0': 'unknown', - '1': 'ipv4', - '2': 'ipv6', - '3': 'ipv4z', - '4': 'ipv6z', - '16': 'dns', } - return names.get(st, st) - - def ospf_nbr_hellosuppressed(st: str) -> str: - names = {'1': 'true', - '2': 'false'} - return names.get(st, st) - - def ospf_nbr_helperstatus(st: str) -> str: - names = {'1': 'not Helping', - '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 shorten_ipv6_adress(address: str) -> str: - address = address.split(':') - span = 2 - address = [''.join(address[i:i + span]) for i in range(0, len(address), span)] - for m in range(0, len(address)): - address[m] = re.sub(r'^0{1,3}', r'', address[m]) - address = ':'.join(address) - zeros = ':0:0:0:0:0:0:' - while not zeros == '': - if zeros in address: - address = re.sub(r'%s' % zeros, r'::', address) - zeros = '' - else: - zeros = zeros[:-2] - if address == '0::0': - address = '::' - - return address - - def render_ipv6_address(bytestring) -> str: - address = ':'.join(['%02s' % hex(ord(m))[2:] for m in bytestring]).replace(' ', '0').upper() - address = shorten_ipv6_adress(address) - - return address - - def render_ipv4_neighbor_id(nbrRtrId: str) -> str: - nbrRtrId = int(nbrRtrId) - firstoctet = nbrRtrId / (256 * 256 * 256) - nbrRtrId = nbrRtrId - (firstoctet * 256 * 256 * 256) - secondocted = nbrRtrId / (256 * 256) - nbrRtrId = nbrRtrId - (secondocted * 256 * 256) - thirdorted = nbrRtrId / 256 - nbrRtrId = nbrRtrId - (thirdorted * 256) - nbrRtrId = '%d.%d.%d.%d' % (firstoctet, secondocted, thirdorted, nbrRtrId) - return nbrRtrId - - def get_short_if_name(st: str) -> str: - names = {'ethernet': 'Eth', - 'fastethernet': 'Fa', - 'gigabitethernet': 'Gi', - 'tengigabitethernet': 'Te', - 'fortygigabitethernet': 'Fo', - 'hundredgigabitethernet': 'H', - 'port-channel': 'Po', - 'tunnel': 'T', - 'loopback': 'Lo', - } - for item in names.keys(): - if st.lower().startswith(item): - st = st.lower().replace(item, names.get(item)) - return st - neighbors = {} neighborinfo, interfaces = string_table @@ -228,17 +160,6 @@ def discovery_ospfv3_neighbor(section: Dict[str, OspfV3Neighbor]) -> DiscoveryRe def check_ospfv3_neighbor(item, params, section: Dict[str, OspfV3Neighbor]) -> CheckResult: - def ospf_nbr_state(st: int) -> str: - names = {1: 'down', - 2: 'attempt', - 3: 'init', - 4: 'twoWay', - 5: 'exchangeStart', - 6: 'exchange', - 7: 'loading', - 8: 'full'} - return names.get(st, st) - try: neighbor = section[item] except KeyError: @@ -261,9 +182,6 @@ def check_ospfv3_neighbor(item, params, section: Dict[str, OspfV3Neighbor]) -> C else: yield Result(state=State.UNKNOWN, notice='Invalid Output from Agent') - yield Metric(name='ospfv3_events', value=neighbor.nbrEvents) - yield Metric(name='ospfv3_lsretransqlen', value=neighbor.nbrLsRetransQLen) - yield Result(state=State.OK, notice=f'Neighbor options: {neighbor.nbrOptions}') yield Result(state=State.OK, notice=f'Neighbor priority: {neighbor.nbrPriority}') yield Result(state=State.OK, notice=f'Neighbor hello suppressed: {neighbor.nbrHelloSuppressed}') @@ -271,6 +189,9 @@ def check_ospfv3_neighbor(item, params, section: Dict[str, OspfV3Neighbor]) -> C yield Result(state=State.OK, notice=f'Neighbor helper age: {neighbor.nbrRestartHelperAge}') yield Result(state=State.OK, notice=f'Neighbor helper exit reason: {neighbor.nbrRestartHelperExitReason}') + yield Metric(name='ospfv3_events', value=neighbor.nbrEvents) + yield Metric(name='ospfv3_lsretransqlen', value=neighbor.nbrLsRetransQLen) + register.snmp_section( name='ospfv3_neighbor', diff --git a/agent_based/ospfv3_virtuallink.py b/agent_based/ospfv3_virtuallink.py index df2af49..f78aeb3 100644 --- a/agent_based/ospfv3_virtuallink.py +++ b/agent_based/ospfv3_virtuallink.py @@ -39,10 +39,11 @@ # OSPFV3-MIB::ospfv3VirtNbrRestartHelperAge.3.50529054 = Gauge32: 0 seconds # OSPFV3-MIB::ospfv3VirtNbrRestartHelperExitReason.3.50529054 = INTEGER: none(1) # -# +# sample string_table # [ # [ -# ['3.50529054', '0', '0', '2', '0\x00\x00\x00\x00\x00\x00\x00\x00\x03\x00\x03\x00\x03\x000', '51', '8', '6', '0', '1', '26', '1', '0', '1'] +# ['3.50529054', '0', '0', '2', '0\x00\x00\x00\x00\x00\x00\x00\x00\x03\x00\x03\x00\x03\x000', +# '51', '8', '6', '0', '1', '26', '1', '0', '1'] # ], # [ # ['1', 'Vlan1'], @@ -66,9 +67,8 @@ # ] # -import re from dataclasses import dataclass -from typing import Optional, Dict, List +from typing import Dict, List from cmk.base.plugins.agent_based.agent_based_api.v1 import ( register, @@ -79,7 +79,6 @@ from cmk.base.plugins.agent_based.agent_based_api.v1 import ( exists, Metric, OIDEnd, - check_levels, ) from cmk.base.plugins.agent_based.agent_based_api.v1.type_defs import ( DiscoveryResult, @@ -87,6 +86,17 @@ from cmk.base.plugins.agent_based.agent_based_api.v1.type_defs import ( StringTable, ) +from cmk.base.plugins.agent_based.utils.ospfv3 import ( + ospf_nbr_addresstype, + ospf_nbr_hellosuppressed, + ospf_nbr_helperstatus, + ospf_nbr_helperexitreason, + render_ipv6_address, + render_ipv4_neighbor_id, + get_short_if_name, + ospf_nbr_state, +) + @dataclass class OspfV3VirtualLink: @@ -100,88 +110,9 @@ class OspfV3VirtualLink: VirtNbrRestartHelperStatus: str VirtNbrRestartHelperAge: int VirtNbrRestartHelperExitReason: str - - -def parse_ospfv3_virtuallink(string_table: List[StringTable]) -> Dict[str, OspfV3VirtualLink]: - def ospf_nbr_addresstype(st): - names = { '0': 'unknown', - '1': 'ipv4', - '2': 'ipv6', - '3': 'ipv4z', - '4': 'ipv6z', - '16':'dns',} - return names.get(st, st) - - def ospf_nbr_hellosuppressed(st): - names = {'1': 'true', - '2': 'false'} - return names.get(st, st) - - def ospf_nbr_helperstatus(st): - names = {'1': 'not Helping', - '2': 'helping'} - return names.get(st, st) - - def ospf_nbr_helperexitreason(st): - names = {'1': 'none', - '2': 'inProgress', - '3': 'completed', - '4': 'timedOut', - '5': 'topologyChanged'} - return names.get(st, st) - - def shorten_ipv6_adress(address): - address = address.split(':') - span = 2 - address = [''.join(address[i:i + span]) for i in range(0, len(address), span)] - for m in range(0, len(address)): - address[m] = re.sub(r'^0{1,3}', r'', address[m]) - address = ':'.join(address) - zeros = ':0:0:0:0:0:0:' - while not zeros == '': - if zeros in address: - address = re.sub(r'%s' % zeros, r'::', address) - zeros = '' - else: - zeros = zeros[:-2] - if address == '0::0': - address = '::' - - return address - - def render_ipv6_address(bytestring): - address = ":".join(["%02s" % hex(ord(m))[2:] for m in bytestring]).replace(' ', '0').upper() - address = shorten_ipv6_adress(address) - - return address - - def render_ipv4_neighbor_id(nbrRtrId: str)-> str: - nbrRtrId = int(nbrRtrId) - firstoctet = nbrRtrId / (256*256*256) - nbrRtrId = nbrRtrId - (firstoctet *256*256*256) - secondocted = nbrRtrId / (256*256) - nbrRtrId = nbrRtrId - (secondocted * 256 * 256) - thirdorted = nbrRtrId / 256 - nbrRtrId = nbrRtrId - (thirdorted * 256) - nbrRtrId = '%d.%d.%d.%d' % (firstoctet, secondocted, thirdorted, nbrRtrId) - return nbrRtrId - def get_short_if_name(st): - names = {'ethernet': 'Eth', - 'fastethernet': 'Fa', - 'gigabitethernet': 'Gi', - 'tengigabitethernet': 'Te', - 'fortygigabitethernet': 'Fo', - 'hundredgigabitethernet': 'H', - 'port-channel': 'Po', - 'tunnel': 'T', - 'loopback': 'Lo', - } - for item in names.keys(): - if st.lower().startswith(item): - st = st.lower().replace(item, names.get(item)) - return st +def parse_ospfv3_virtuallink(string_table: List[StringTable]) -> Dict[str, OspfV3VirtualLink]: virtual_links = {} virtual_links_info, interfaces = string_table @@ -202,7 +133,7 @@ def parse_ospfv3_virtuallink(string_table: List[StringTable]) -> Dict[str, OspfV nbrLocalInterface = get_short_if_name(ifName) # virtual_links[f'{VirtNbrAddress} on {nbrLocalInterface}']= OspfV3VirtualLink( - virtual_links[VirtNbrAddress]= OspfV3VirtualLink( + virtual_links[VirtNbrAddress] = OspfV3VirtualLink( VirtNbrId=render_ipv4_neighbor_id(VirtNbrId), VirtNbrArea=int(VirtNbrArea), VirtNbrOptions=VirtNbrOptions, @@ -224,31 +155,19 @@ def discovery_ospfv3_virtuallink(section: Dict[str, OspfV3VirtualLink]) -> Disco def check_ospfv3_virtuallink(item, params, section: Dict[str, OspfV3VirtualLink]) -> CheckResult: - - def ospf_nbr_state(st: int)-> str: - names = {1: 'down', - 2: 'attempt', - 3: 'init', - 4: 'twoWay', - 5: 'exchangeStart', - 6: 'exchange', - 7: 'loading', - 8: 'full'} - return names.get(st, st) - try: virtual_link = section[item] except KeyError: yield Result(state=State.UNKNOWN, notice='Item not found in SNMP data') return - + nbrstatus = ospf_nbr_state(virtual_link.VirtNbrState) yield Result(state=State.OK, summary=f'Neighbor ID: {virtual_link.VirtNbrId}') - yield Result(state=State.OK, summary=f', Area: {virtual_link.VirtNbrArea}') + 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']: @@ -258,14 +177,15 @@ def check_ospfv3_virtuallink(item, params, section: Dict[str, OspfV3VirtualLink] else: yield Result(state=State.UNKNOWN, notice='Invalid Output from Agent') - yield Metric(name='ospfv3_events', value=virtual_link.VirtNbrEvents) - yield Metric(name='ospfv3_lsretransqlen', value=virtual_link.VirtNbrLsRetransQLen) - 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}') yield Result(state=State.OK, notice=f'Virtual link helper status: {virtual_link.VirtNbrRestartHelperStatus}') yield Result(state=State.OK, notice=f'Virtual link helper age: {virtual_link.VirtNbrRestartHelperAge}') - yield Result(state=State.OK, notice=f'Virtual link helper exit reason: {virtual_link.VirtNbrRestartHelperExitReason}') + yield Result(state=State.OK, + notice=f'Virtual link helper exit reason: {virtual_link.VirtNbrRestartHelperExitReason}') + + yield Metric(name='ospfv3_events', value=virtual_link.VirtNbrEvents) + yield Metric(name='ospfv3_lsretransqlen', value=virtual_link.VirtNbrLsRetransQLen) register.snmp_section( diff --git a/agent_based/utils/ospfv3.py b/agent_based/utils/ospfv3.py new file mode 100644 index 0000000..832cf90 --- /dev/null +++ b/agent_based/utils/ospfv3.py @@ -0,0 +1,306 @@ +#!/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-17 +# +# include file, will be used with ospfv3_area, ospfv3_general, ospfv3_interface, ospfv3_neighbor, ospfv3_virtuallink +# +import re + + +def get_area_type(area_import_as_extern: str, area_summary: str) -> str: + areatype = 'UNKNOWN' + if int(area_import_as_extern) == 1 and int(area_summary) == 2: + areatype = 'normal' + if int(area_import_as_extern) == 2 and int(area_summary) == 1: + areatype = 'totaly subby' + if int(area_import_as_extern) == 2 and int(area_summary) == 2: + areatype = 'stubby' + if int(area_import_as_extern) == 3 and int(area_summary) == 2: + areatype = 'NSSA' + if int(area_import_as_extern) == 3 and int(area_summary) == 1: + areatype = 'totaly NSSA' + return areatype + + +def render_ipv4_neighbor_id(router_id: str) -> str: + """ + >>> from pprint import pprint + >>> pprint(render_ipv4_neighbor_id('50529054')) + 3.3.3.30 + """ + router_id = int(router_id) + firstoctet = router_id // (256 * 256 * 256) + router_id = router_id - (firstoctet * 256 * 256 * 256) + secondocted = router_id // (256 * 256) + router_id = router_id - (secondocted * 256 * 256) + thirdorted = router_id // 256 + router_id = router_id - (thirdorted * 256) + router_id = f'{firstoctet}.{secondocted}.{thirdorted}.{router_id}' + return router_id + + +_interface_displayhints = { + 'ethernet': 'eth', + 'fastethernet': 'Fa', + 'gigabitethernet': 'Gi', + 'tengigabitethernet': 'Te', + 'fortygigabitethernet': 'Fo', + 'hundredgigabitethernet': 'Hu', + 'port-channel': 'Po', + 'tunnel': 'Tu', + 'loopback': 'Lo', + 'cellular': 'Cel', + 'vlan': 'Vlan', + 'management': 'Ma', +} + + +def get_short_if_name(ifname: str) -> str: + """ + returns short interface name from long interface name + ifname: is the long interface name + :type ifname: str + """ + + for ifname_prefix in _interface_displayhints.keys(): + if ifname.lower().startswith(ifname_prefix.lower()): + ifname_short = _interface_displayhints[ifname_prefix] + return ifname.lower().replace(ifname_prefix.lower(), ifname_short, 1) + return ifname + + +def _shorten_ipv6_adress(address: str) -> str: + """ + >>> from pprint import pprint + >>> pprint(_shorten_ipv6_adress('20:03:00:00:00:00:00:00:01:92:01:68:00:00:00:01')) + 2003::192:168:0:1 + """ + # '20:03:00:00:00:00:00:00:01:92:01:68:00:00:00:01' + address = address.split(':') + span = 2 + address = [''.join(address[i:i + span]) for i in range(0, len(address), span)] + # ['2003', '0000', '0000', '0000', '0192', '0168', '0000', '0001'] + for m in range(0, len(address)): + address[m] = re.sub(r'^0{1,3}', r'', address[m]) + # ['2003', '0', '0', '0', '192', '168', '0', '1'] + address = ':'.join(address) + # '2003:0:0:0:192:168:0:1' + zeros = ':0:0:0:0:0:0:' + while not zeros == '': + if zeros in address: + address = address.replace(zeros, '::', 1) + zeros = '' + else: + zeros = zeros[:-2] + if address == '0::0': + address = '::' + # '2003::192:168:0:1' + return address + + +def render_ipv6_address(bytestring) -> str: + address = ':'.join([f'{hex(ord(m))[2:]:0>2}' for m in bytestring]).replace(' ', '0').upper() + address = _shorten_ipv6_adress(address) + + return address + + +def ospf_stub_router_advertisement(st: str) -> str: + names = { + '1': 'do Not Advertise', + '2': 'advertise', + } + return names.get(st, st) + + +def ospf_exit_reason(st: str) -> str: + names = { + '1': 'none', + '2': 'in Progress', + '3': 'completed', + '4': 'timed Out', + '5': 'topology Changed', + } + return names.get(st, st) + + +def ospf_restart_status(st: str) -> str: + names = { + '1': 'not Restarting', + '2': 'planned Restart', + '3': 'unplanned Restart', + } + return names.get(st, st) + + +def ospf_restart_support(st: str) -> str: + names = { + '1': 'none', + '2': 'planned Only', + '3': 'planned And Unplanned', + } + return names.get(st, st) + + +def ospf_admin_status(st: str) -> str: + names = { + '1': 'enabled', + '2': 'disabled', + } + return names.get(st, st) + + +def ospf_abr_asbr_status(st: str) -> str: + names = { + '1': 'yes', + '2': 'no', + } + return names.get(st, st) + + +def ospf_stub_router_support(st: str) -> str: + names = { + '1': 'yes', + '2': 'no', + } + return names.get(st, st) + + +def ospf_area_impotasextern(st: str) -> str: + names = { + '1': 'import External', + '2': 'import no External', + '3': 'import Nssa', + } + return names.get(st, st) + + +def ospf_area_summary(st: str) -> str: + names = { + '1': 'no Area Summary', + '2': 'send Area Summary', + } + return names.get(st, st) + + +def ospf_area_translatorrole(st: str) -> str: + names = { + '1': 'always', + '2': 'candidate', + } + return names.get(st, st) + + +def ospf_area_translatorrolestate(st: str) -> str: + names = { + '1': 'enabled', + '2': 'elected', + '3': 'disabled', + } + return names.get(st, st) + + +def ospf_area_submetrictype(st: str) -> str: + names = { + '1': 'ospfv3 Metric', + '2': 'comparable Cost', + '3': 'non Comparable', + } + return names.get(st, st) + + +def ospf_area_teeenabled(st: str) -> str: + names = { + '1': 'enabled', + } + return names.get(st, 'disabled') + + +def ospf_if_type(st: str) -> str: + names = { + '1': 'broadcast', + '2': 'nbma', + '3': 'pointToPoint', + '5': 'pointToMultipoint', + } + return names.get(st, st) + + +def ospf_if_admin_status(st: str) -> str: + names = { + '1': 'enabled', + '2': 'disabled', + } + return names.get(st, st) + + +def ospf_if_state(st: str) -> str: + names = { + '1': 'down', + '2': 'loopback', + '3': 'waiting', + '4': 'P2P', + '5': 'DR', + '6': 'BDR', + '7': 'other DR', + '8': 'standby', + } + return names.get(st, st) + + +def ospf_nbr_state(st: int) -> str: + names = { + 1: 'down', + 2: 'attempt', + 3: 'init', + 4: 'twoWay', + 5: 'exchangeStart', + 6: 'exchange', + 7: 'loading', + 8: 'full', + } + return names.get(st, st) + + +def ospf_nbr_addresstype(st: str) -> str: + names = { + '0': 'unknown', + '1': 'ipv4', + '2': 'ipv6', + '3': 'ipv4z', + '4': 'ipv6z', + '16': 'dns', + } + return names.get(st, st) + + +def ospf_nbr_hellosuppressed(st: str) -> str: + names = { + '1': 'true', + '2': 'false', + } + return names.get(st, st) + + +def ospf_nbr_helperstatus(st: str) -> str: + names = { + '1': 'not Helping', + '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) diff --git a/ospfv3.mkp b/ospfv3.mkp index 8fef4840fb4ed39cef766c46040cb98b66d7b03a..e46832ab2faabdf1ed8b4d742e190900e05650fa 100644 GIT binary patch literal 9626 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 literal 9372 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 diff --git a/packages/ospfv3 b/packages/ospfv3 index 5747a4d..5ac9a2f 100644 --- a/packages/ospfv3 +++ b/packages/ospfv3 @@ -5,10 +5,11 @@ 'ospfv3_general.py', 'ospfv3_interface.py', 'ospfv3_neighbor.py', - 'ospfv3_virtuallink.py'], + 'ospfv3_virtuallink.py', + 'utils/ospfv3.py'], 'web': ['plugins/metrics/ospfv3.py']}, 'name': 'ospfv3', - 'num_files': 6, + 'num_files': 7, 'title': 'Collection of OSPFv3 checks', 'version': '20210917.v0.2', 'version.min_required': '2.0.0', diff --git a/web/plugins/metrics/ospfv3.py b/web/plugins/metrics/ospfv3.py index 75b5e6a..58e9e6c 100644 --- a/web/plugins/metrics/ospfv3.py +++ b/web/plugins/metrics/ospfv3.py @@ -7,7 +7,7 @@ # URL : https://thl-cmk.hopto.org # Date : 2019-11-02 # -# Cisco OSPFv3 metrics plugin +# OSPFv3 metrics plugin # from cmk.gui.i18n import _ -- GitLab