From cb3684afbe5fa144519e37766af3b61cdea86372 Mon Sep 17 00:00:00 2001 From: "th.l" <thl-cmk@outlook.com> Date: Tue, 3 Aug 2021 21:37:04 +0200 Subject: [PATCH] update project --- agent_based/cisco_vpn_tunnel.py | 381 ++++++++++++++++++++++++ cisco_vpn_tunnel.mkp | Bin 7099 -> 5905 bytes packages/cisco_vpn_tunnel | 19 +- web/plugins/metrics/cisco_vpn_tunnel.py | 252 ++++------------ web/plugins/wato/cisco_vpn_tunnel.py | 93 ++++-- 5 files changed, 526 insertions(+), 219 deletions(-) create mode 100644 agent_based/cisco_vpn_tunnel.py diff --git a/agent_based/cisco_vpn_tunnel.py b/agent_based/cisco_vpn_tunnel.py new file mode 100644 index 0000000..9b49333 --- /dev/null +++ b/agent_based/cisco_vpn_tunnel.py @@ -0,0 +1,381 @@ +#!/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 : 2017-12-28 +# +# Monitor status of Cisco VPN tunnel phase 1 and 2 +# +# 2018-01-10: added handling for tunnel not found +# 2018-01-23: removed unnecessary counters +# 2018-02-15: removed ipsec tunnel status, changed ike ipv4 check +# 2018-02-16: readded tunnel alias +# 2018-07-11: added parameter for missing IPSec SA, changed 'parsed' to use peer ip as index +# 2021-08-03: rewritten for CMK 2.0 +# +# snmpwalk sample +# +# +import time +from dataclasses import dataclass +from typing import List, Dict + +from cmk.base.plugins.agent_based.agent_based_api.v1 import ( + register, + Service, + Result, + check_levels, + State, + SNMPTree, + contains, + OIDEnd, + get_rate, + GetRateError, + get_value_store, + IgnoreResultsError, + Metric, + render, + all_of, + exists, +) +from cmk.base.plugins.agent_based.agent_based_api.v1.type_defs import ( + DiscoveryResult, + CheckResult, + StringTable, +) + + +@dataclass +class IpsecSa: + sa_count: int + ike_tunnel_alive: int + active_time: int + hc_in_octets: int + in_pkts: int + in_drop_pkts: int + hc_out_octets: int + out_pkts: int + out_drop_pkts: int + + +@dataclass +class IkeSa: + # local_type: int + # local_value: str + local_addr: str + # local_name: str + # remote_type: int + # remote_value: str + remote_addr: str + # remote_name: str + active_time: int + in_octets: int + in_pkts: int + in_drop_pkts: int + out_octets: int + out_pkts: int + out_drop_pkts: int + status: int + nego_mode: int + ipsec_summary: IpsecSa + + +########################################################################### +# +# Helper functions +# +########################################################################### + +def _ikepeertype(st: int) -> str: + name = { + 1: 'ipAddrPeer', + 2: 'namePeer', + } + return name.get(st, f'unknown ({st})') + + +def _ikenegomode(st: int) -> str: + name = { + 1: 'main', + 2: 'aggressive', + 3: 'IKEv2 main([3]?)' + } + return name.get(st, f'unknown ({st})') + + +def _tunnelstatus(st: int) -> str: + name = { + 1: 'active', + 2: 'destroy', + } + return name.get(st, f'unknown ({st})') + + +def _cisco_vpn_tunnel_render_ipv4_address(bytestring): + return '.'.join([f'{ord(m)}' for m in bytestring]) + +########################################################################### +# +# DATA Parser function +# +########################################################################### + + +def parse_cisco_vpn_tunnel(string_table: List[StringTable]) -> Dict[str, IkeSa]: + ipsec_sa_summary: Dict[str, IpsecSa] = {} + vpntunnel = {} + ike_tunnel_entry, ipsec_tunnel_entry = string_table + + # summarize IPSec SAs, ASSUMPTION: except for counters all SA attributes are identical per IKE index + for ike_tunnel_index, ike_tunnel_alive, active_time, hc_in_octets, in_pkts, in_drop_pkts, hc_out_octets, \ + out_pkts, out_drop_pkts in ipsec_tunnel_entry: + + if ike_tunnel_index.isdigit(): + ipsec_sa = ipsec_sa_summary.setdefault( + ike_tunnel_index, + IpsecSa(0, 0, 0, 0, 0, 0, 0, 0, 0) + ) + ipsec_sa.sa_count += 1 + ipsec_sa.hc_in_octets += int(hc_in_octets) + ipsec_sa.in_pkts += int(in_pkts) + ipsec_sa.in_drop_pkts += int(in_drop_pkts) + ipsec_sa.hc_out_octets += int(hc_out_octets) + ipsec_sa.out_pkts += int(out_pkts) + ipsec_sa.out_drop_pkts += int(out_drop_pkts) + if int(active_time) // 100 > ipsec_sa.active_time: + ipsec_sa.active_time = int(active_time) // 100 + + # IKE SA + for index, local_type, local_value, local_addr, local_name, remote_type, remote_value, remote_addr, remote_name, \ + active_time, in_octets, in_pkts, in_droppkts, out_octets, out_pkts, out_droppkts, status, \ + nego_mode in ike_tunnel_entry: + + if index.isdigit(): + # if int(nego_mode) == 2: # drop agressive mode tunnel, likely Remote Access + remote_addr = _cisco_vpn_tunnel_render_ipv4_address(remote_addr) + if remote_addr.split('.') != 4: + remote_addr = remote_value + if len(remote_addr.split('.')) == 4: + ike_sa = IkeSa( + # local_type=int(local_type), + # local_value=local_value, + local_addr=_cisco_vpn_tunnel_render_ipv4_address(local_addr), + # local_name=local_name, + # remote_type=int(remote_type), + # remote_value=remote_value, + remote_addr=remote_addr, + # remote_name=remote_name, + active_time=int(active_time) // 100, + in_octets=int(in_octets), + in_pkts=int(in_pkts), + in_drop_pkts=int(in_droppkts), + out_octets=int(out_octets), + out_pkts=int(out_pkts), + out_drop_pkts=int(out_droppkts), + status=int(status), + nego_mode=int(nego_mode), + ipsec_summary=ipsec_sa_summary.get(index) + ) + vpntunnel.update({remote_addr: ike_sa}) + + return vpntunnel + +########################################################################### +# +# Inventory function +# +########################################################################### + + +def discovery_cisco_vpn_tunnel(params, section: Dict[str, IkeSa]) -> DiscoveryResult: + discover_aggressive_mode = params['discover_aggressive_mode'] + for cikeTunRemoteAddr in section.keys(): + if section[cikeTunRemoteAddr].nego_mode != 2: + yield Service(item=cikeTunRemoteAddr) + elif discover_aggressive_mode: + yield Service(item=cikeTunRemoteAddr) + + +########################################################################### +# +# Check function +# +########################################################################### + + +def check_cisco_vpn_tunnel(item, params, section: Dict[str, IkeSa]) -> CheckResult: + tunnel_not_found_state = params['state'] + missing_ipsec_sa_state = params['missing_ipsec_sa_state'] + + for tunnel_ip, tunnel_alias, not_found_state, ipsec_sa_state in params['tunnels']: + if item == tunnel_ip: + yield Result(state=State.OK, summary=f'[{tunnel_alias}]') + tunnel_not_found_state = not_found_state + missing_ipsec_sa_state = ipsec_sa_state + + try: + tunnel = section[item] + except KeyError: + yield Result(state=State(tunnel_not_found_state), summary='VPN Tunnel not found in SNMP data') + return + + yield from check_levels( + value=tunnel.active_time, + label='IKE uptime', + render_func=render.timespan, + metric_name='cisco_vpn_tunnel_cikeTunActiveTime' + ) + + yield Result(state=State.OK, notice=f'IKE Status: {_tunnelstatus(tunnel.status)}') + yield Result(state=State.OK, notice=f'Tunnel address local: {tunnel.local_addr}') + yield Result(state=State.OK, notice=f'Tunnel address remote : {tunnel.remote_addr}') + yield Result(state=State.OK, notice=f'Negotiation mode : {_ikenegomode(tunnel.nego_mode)}') + + now_time = time.time() + value_store = get_value_store() + rate_item = item.replace(' ', '_').replace(':', '_') + raise_ingore_res = False + + # convert to octets/packets per second + for key, value in [ + ('cikeTunInOctets', tunnel.in_octets), + ('cikeTunOutOctets', tunnel.out_octets), + ('cikeTunInPkts', tunnel.in_pkts), + ('cikeTunOutPkts', tunnel.out_pkts), + ('cikeTunInDropPkts', tunnel.in_drop_pkts), + ('cikeTunOutDropPkts', tunnel.out_drop_pkts), + + ]: + try: + value = get_rate(value_store, f'cisco_vpn_tunnel.{key}.{rate_item}', now_time, value, raise_overflow=False) + except GetRateError: + raise_ingore_res = True + value = 0 + yield Metric(name=f'cisco_vpn_tunnel_{key}', value=value, boundaries=(0, None)) + + if raise_ingore_res: + raise IgnoreResultsError('Initializing counters') + + ipsecsummary: IpsecSa = tunnel.ipsec_summary + if ipsecsummary is not None: + + yield from check_levels( + label='IPSec uptime', + value=ipsecsummary.active_time, + render_func=render.timespan, + metric_name='cisco_vpn_tunnel_cipSecTunActiveTime' + ) + yield Result(state=State.OK, summary=f'SAs: {ipsecsummary.sa_count}') + ipsec_in_octets = 0 + ipsec_out_octets = 0 + # convert to octets/packets per second + for key, value in [ + ('cipSecTunHcInOctets', ipsecsummary.hc_in_octets), + ('cipSecTunHcOutOctets', ipsecsummary.hc_out_octets), + ('cipSecTunInPkts', ipsecsummary.in_pkts), + ('cipSecTunOutPkts', ipsecsummary.out_pkts), + ('cipSecTunInDropPkts', ipsecsummary.in_drop_pkts), + ('cipSecTunOutDropPkts', ipsecsummary.out_drop_pkts), + ]: + try: + value = get_rate(value_store, f'cisco_vpn_tunnel.{key}.{rate_item}', + now_time, value, raise_overflow=False) + except GetRateError: + raise_ingore_res = True + value = 0 + yield Metric(name=f'cisco_vpn_tunnel_{key}', value=value, boundaries=(0, None)) + if key == 'cipSecTunHcInOctets': + ipsec_in_octets = value + elif key == 'cipSecTunHcOutOctets': + ipsec_out_octets = value + + yield from check_levels( + label='In', + value=ipsec_in_octets, + render_func=render.networkbandwidth, + ) + yield from check_levels( + label='Out', + value=ipsec_out_octets, + render_func=render.networkbandwidth, + ) + + if raise_ingore_res: + raise IgnoreResultsError('Initializing counters') + else: + yield Result(state=State(missing_ipsec_sa_state), notice='No IPSec sa found') + +########################################################################### +# +# Check info +# +########################################################################### + + +register.snmp_section( + name='cisco_vpn_tunnel', + parse_function=parse_cisco_vpn_tunnel, + fetch=[ + SNMPTree( + base='.1.3.6.1.4.1.9.9.171.1.2.3.1', # + oids=[ + OIDEnd(), # TunnelIndex + '2', # cikeTunLocalType + '3', # cikeTunLocalValue + '4', # cikeTunLocalAddr + '5', # cikeTunLocalName + '6', # cikeTunRemoteType + '7', # cikeTunRemoteValue + '8', # cikeTunRemoteAddr + '9', # cikeTunRemoteName + '16', # cikeTunActiveTime + '19', # cikeTunInOctets + '20', # cikeTunInPkts + '21', # cikeTunInDropPkts + '27', # cikeTunOutOctets + '28', # cikeTunOutPkts + '29', # cikeTunOutDropPkts + '35', # cikeTunStatus + '10', # cikeTunNegoMode + ] + ), + SNMPTree( + base='.1.3.6.1.4.1.9.9.171.1.3.2.1', # CISCO-IPSEC-FLOW-MONITOR-MIB::cipSecTunnelEntry + oids=[ + '2', # ike tunnel index + '3', # cipSecTunIkeTunnelAlive + '10', # cipSecTunActiveTime + '27', # cipSecTunHcInOctets + '32', # cipSecTunInPkts + '33', # cipSecTunInDropPkts + '40', # cipSecTunHcOutOctets + '45', # cipSecTunOutPkts + '46', # cipSecTunOutDropPkts + ] + ), + ], + detect=all_of( + contains('.1.3.6.1.2.1.1.1.0', 'Cisco'), + exists('.1.3.6.1.4.1.9.9.171.1.2.3.1.2.*') # CISCO-IPSEC-FLOW-MONITOR-MIB::cikeTunnelEntry + ), +) + +register.check_plugin( + name='cisco_vpn_tunnel', + service_name='VPN Tunnel %s', + discovery_function=discovery_cisco_vpn_tunnel, + discovery_ruleset_name='discovery_cisco_vpn_tunnel', + discovery_default_parameters={ + 'discover_aggressive_mode': False + }, + check_function=check_cisco_vpn_tunnel, + check_default_parameters={ + 'state': 3, # default state for tunnel not found + 'missing_ipsec_sa_state': 1, + 'tunnels': [], # list of tunnel specific not found states ('<ip-address>', '<alias>', <state>) + }, + check_ruleset_name='cisco_vpn_tunnel', +) diff --git a/cisco_vpn_tunnel.mkp b/cisco_vpn_tunnel.mkp index 09ab45a2793b925a5c45bec767a6113743ad6290..0398d590158d29b061508cfcb405ff3c613c9ed1 100644 GIT binary patch literal 5905 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 DZr+|M literal 7099 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 diff --git a/packages/cisco_vpn_tunnel b/packages/cisco_vpn_tunnel index 75801ea..cbca0d5 100644 --- a/packages/cisco_vpn_tunnel +++ b/packages/cisco_vpn_tunnel @@ -1,12 +1,17 @@ -{'author': u'Th.L. (thl-cmk[at]outlook[dot]com)', - 'description': u'Monitors Cisco VPN Tunnel. Complete rewrite of the original check.\nCreates one service for each VPN Tunnel.\nperfdata contains: IKE and IPSec statistics for uptime, in/out octets and packets.\n', +{'author': 'Th.L. (thl-cmk[at]outlook[dot]com)', + 'description': 'Monitors Cisco VPN Tunnel. Complete rewrite of the original ' + 'check.\n' + 'Creates one service for each VPN Tunnel.\n' + 'perfdata contains: IKE and IPSec statistics for uptime, ' + 'in/out octets and packets.\n', 'download_url': 'https://thl-cmk.hopto.org', - 'files': {'checks': ['cisco_vpn_tunnel'], + 'files': {'agent_based': ['cisco_vpn_tunnel.py'], 'web': ['plugins/metrics/cisco_vpn_tunnel.py', 'plugins/wato/cisco_vpn_tunnel.py']}, 'name': 'cisco_vpn_tunnel', 'num_files': 3, - 'title': u'Monitor Cisco VPN Tunnel', - 'version': '20180806v.0.1g', - 'version.min_required': '1.2.8b8', - 'version.packaged': '1.4.0p35'} \ No newline at end of file + 'title': 'Monitor Cisco VPN Tunnel', + 'version': '20210803v.0.2', + '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/cisco_vpn_tunnel.py b/web/plugins/metrics/cisco_vpn_tunnel.py index ab5dc4a..2fba642 100644 --- a/web/plugins/metrics/cisco_vpn_tunnel.py +++ b/web/plugins/metrics/cisco_vpn_tunnel.py @@ -1,58 +1,23 @@ -#!/usr/bin/python -# -*- encoding: utf-8; py-indent-offset: 4 -*- +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- # -# Cisco VPN Tunnel metrics plugin +# License: GNU General Public License v2 # -# Author: Th.L. +# Author: thl-cmk[at]outlook[dot]com +# URL : https://thl-cmk.hopto.org # Date : 2017-12-29 # - -# key a green 11/a 21/a 31/a 41/a 12/a 22/a 32/a 42/a -colors_a = ['#80F000', '#a500ff', '#ffc600', '#00ffb2', '#0075ff', '#cc00ff', '#ffd600', '#00ffff', '#0047ff', - # 13/a 23/a 33/a 43/a 14/a 24/a 34/a 44/a 15/a - '#f900ff', '#ffed00', '#00e8ff', '#000aff', '#ff4c00', '#e2ff00', '#00d1ff', '#4200ff', '#ff7a00', - # 25/a 35/a 45/a 16/a 26/a 36/a 46/a 51/a 52/a - '#bcff00', '#00b2ff', '#6000ff', '#ffa000', '#7fff00', '#0093ff', '#7f00ff', '#7f7f7f', '#7f4a26', - # 53/a - '#8c531c'] -# key b green 11/b 21/b 31/b 41/b 12/b 22/b 32/b 42/b -colors_b = ['#80F000', '#c966ff', '#cc9f00', '#00cc8e', '#66acff', '#e066ff', '#ccab00', '#00cccc', '#6690ff', - # 13/b 23/b 33/b 43/b 14/b 24/b 34/b 44/b 15/b - '#fb66ff', '#ccbd00', '#00b9cc', '#666cff', '#ff9366', '#b5cc00', '#00a7cc', '#8d66ff', '#ffaf66', - # 25/b 35/b 45/b 16/b 26/b 36/b 46/b 51/b 52/b - '#96cc00', '#008ecc', '#a066ff', '#ffc666', '#66cc00', '#0076cc', '#b266ff', '#7f7f7f', '#7f5f49', - # 53/b - '#8c6a48'] - - -def cisco_vpn_tunnel_render_uptime(uptime): # expects time in seconds - m, s = divmod(uptime, 60) # break in seconds / minutes - h, m = divmod(m, 60) # break in mintes / hours - if h >= 24: # more then one day - d, h = divmod(h, 24) # break in hours / days - else: - return '%02d:%02d:%02d' % (h, m, s) - if d >= 365: # more the one year - y, d = divmod(d, 365) # break in days / years - return '%dy %dd %02d:%02d:%02d' % (y, d, h, m, s) - else: - return '%dd %02d:%02d:%02d' % (d, h, m, s) - -##################################################################################################################### -# -# define units for cisco_vpm_tunnel perfdata +# Cisco VPN Tunnel metrics plugin # -##################################################################################################################### +from cmk.gui.i18n import _ -unit_info['active_time'] = { - 'title': _('Last update'), - 'description': _('SA active time'), - 'symbol': _(''), - 'render': lambda v: cisco_vpn_tunnel_render_uptime(v), - 'stepping': 'time', # for vertical graph labels -} - +from cmk.gui.plugins.metrics import ( + metric_info, + graph_info, + perfometer_info, + unit_info, +) ##################################################################################################################### # @@ -64,266 +29,216 @@ unit_info['active_time'] = { metric_info['cisco_vpn_tunnel_cikeTunActiveTime'] = { 'title': _('IKE active time'), 'help': _(''), - #'unit': 'active_time', 'unit': 's', - 'color': colors_a[0], + 'color': '26/a', } metric_info['cisco_vpn_tunnel_cikeTunInOctets'] = { 'title': _('IKE Bytes in'), 'unit': 'bytes/s', - 'color': colors_a[1], + 'color': '11/a', } metric_info['cisco_vpn_tunnel_cikeTunOutOctets'] = { 'title': _('IKE Bytes out'), 'help': _(''), 'unit': 'bytes/s', - 'color': colors_a[2], + 'color': '21/a', } metric_info['cisco_vpn_tunnel_cikeTunInPkts'] = { 'title': _('IKE packets in'), 'help': _(''), 'unit': '1/s', - 'color': colors_a[3], + 'color': '31/a', } metric_info['cisco_vpn_tunnel_cikeTunOutPkts'] = { 'title': _('IKE packets out'), 'help': _(''), 'unit': '1/s', - 'color': colors_a[4], + 'color': '41/a', } metric_info['cisco_vpn_tunnel_cikeTunInDropPkts'] = { 'title': _('IKE packets dropped in'), 'help': _(''), 'unit': '1/s', - 'color': colors_a[5], + 'color': '12/a', } metric_info['cisco_vpn_tunnel_cikeTunOutDropPkts'] = { 'title': _('IKE packets dropped out'), 'help': _(''), 'unit': '1/s', - 'color': colors_a[6], + 'color': '22/a', } metric_info['cisco_vpn_tunnel_cikeTunInNotifys'] = { 'title': _('IKE in notifies'), 'help': _(''), 'unit': 'count', - 'color': colors_a[7], + 'color': '32/a', } metric_info['cisco_vpn_tunnel_cikeTunOutNotifys'] = { 'title': _('IKE out notifies'), 'help': _(''), 'unit': 'count', - 'color': colors_a[8], + 'color': '42/a', } metric_info['cisco_vpn_tunnel_cikeTunInP2Exchgs'] = { 'title': _('IKE in phase 2 exchanges'), 'help': _(''), 'unit': 'count', - 'color': colors_a[9], + 'color': '13/a', } metric_info['cisco_vpn_tunnel_cikeTunOutP2Exchgs'] = { 'title': _('IKE out phase 2 exchanges'), 'help': _(''), 'unit': 'count', - 'color': colors_a[10], + 'color': '23/a', } metric_info['cisco_vpn_tunnel_cikeTunInP2ExchgInvalids'] = { 'title': _('IKE in phase 2 exchanges invalid'), 'help': _(''), 'unit': 'count', - 'color': colors_a[11], + 'color': '33/a', } metric_info['cisco_vpn_tunnel_cikeTunOutP2ExchgInvalids'] = { 'title': _('IKE out phase 2 exchanges invalid'), 'help': _(''), 'unit': 'count', - 'color': colors_a[12], + 'color': '43/a', } metric_info['cisco_vpn_tunnel_cikeTunInP2ExchgRejects'] = { 'title': _('IKE in phase 2 exchanges rejected'), 'help': _(''), 'unit': 'count', - 'color': colors_a[13], + 'color': '14/a', } metric_info['cisco_vpn_tunnel_cikeTunOutP2ExchgRejects'] = { 'title': _('IKE out phase 2 exchanges rejected'), 'help': _(''), 'unit': 'count', - 'color': colors_a[14], + 'color': '24/a', } metric_info['cisco_vpn_tunnel_cikeTunInP2SaDelRequests'] = { 'title': _('IKE in phase 2 SA delete requests'), 'help': _(''), 'unit': 'count', - 'color': colors_a[15], + 'color': '34/a', } metric_info['cisco_vpn_tunnel_cikeTunOutP2SaDelRequests'] = { 'title': _('IKE out phase 2 SA delete requests'), 'help': _(''), 'unit': 'count', - 'color': colors_a[16], + 'color': '44/a', } - # IPSec counter metric_info['cisco_vpn_tunnel_cipSecTunActiveTime'] = { 'title': _('IPSec active time'), 'help': _(''), 'unit': 's', - 'color': colors_b[0], + 'color': '26/b', } metric_info['cisco_vpn_tunnel_cipSecTunHcInOctets'] = { 'title': _('IPSec Bytes in'), 'help': _(''), 'unit': 'bytes/s', - 'color': colors_b[1], + 'color': '11/b', } metric_info['cisco_vpn_tunnel_cipSecTunHcOutOctets'] = { 'title': _('IPSec Bytes out'), 'help': _(''), 'unit': 'bytes/s', - 'color': colors_b[2], + 'color': '21/b', } metric_info['cisco_vpn_tunnel_cipSecTunInPkts'] = { 'title': _('IPSec packets in'), 'help': _(''), 'unit': '1/s', - 'color': colors_b[3], + 'color': '31/b', } metric_info['cisco_vpn_tunnel_cipSecTunOutPkts'] = { 'title': _('IPSec packets out'), 'help': _(''), 'unit': '1/s', - 'color': colors_b[4], + 'color': '41/b', } metric_info['cisco_vpn_tunnel_cipSecTunInDropPkts'] = { 'title': _('IPSec packets dropped in'), 'help': _(''), 'unit': '1/s', - 'color': colors_b[5], + 'color': '11/b', } metric_info['cisco_vpn_tunnel_cipSecTunOutDropPkts'] = { 'title': _('IPSec packets dropped out'), 'help': _(''), 'unit': '1/s', - 'color': colors_b[6], + 'color': '21/b', } metric_info['cisco_vpn_tunnel_cipSecTunHcInDecompOctets'] = { 'title': _('IPSec in decompressed octets'), 'help': _(''), 'unit': '1/s', - 'color': colors_b[7], + 'color': '32/b', } metric_info['cisco_vpn_tunnel_cipSecTunHcOutUncompOctets'] = { 'title': _('IPSec out compressed octets'), 'help': _(''), 'unit': '1/s', - 'color': colors_b[8], + 'color': '41/b', } metric_info['cisco_vpn_tunnel_cipSecTunInAuths'] = { 'title': _('IPSec in authentication\'s'), 'help': _(''), 'unit': 'count', - 'color': colors_b[9], + 'color': '13/b', } metric_info['cisco_vpn_tunnel_cipSecTunOutAuths'] = { 'title': _('IPSec out authentication\'s'), 'help': _(''), 'unit': 'count', - 'color': colors_b[10], + 'color': '23/b', } metric_info['cisco_vpn_tunnel_cipSecTunInAuthFails'] = { 'title': _('IPSec in authentication\'s failed'), 'help': _(''), 'unit': 'count', - 'color': colors_b[11], + 'color': '33/b', } metric_info['cisco_vpn_tunnel_cipSecTunOutAuthFails'] = { 'title': _('IPSec out authentication\'s failed'), 'help': _(''), 'unit': 'count', - 'color': colors_b[12], + 'color': '43/b', } metric_info['cisco_vpn_tunnel_cipSecTunInDecrypts'] = { 'title': _('IPSec in decryption\'s'), 'help': _(''), 'unit': 'count', - 'color': colors_b[13], + 'color': '15/b', } metric_info['cisco_vpn_tunnel_cipSecTunOutEncrypts'] = { 'title': _('IPSec out encryption\'s'), 'help': _(''), 'unit': 'count', - 'color': colors_b[14], + 'color': '25/b', } metric_info['cisco_vpn_tunnel_cipSecTunInDecryptFails'] = { 'title': _('IPSec in decryption\'s failed'), 'help': _(''), 'unit': 'count', - 'color': colors_b[15], + 'color': '35/b', } metric_info['cisco_vpn_tunnel_cipSecTunOutEncryptFails'] = { 'title': _('IPSec out encryption\'s failed'), 'help': _(''), 'unit': 'count', - 'color': colors_b[16], + 'color': '45/b', } metric_info['cisco_vpn_tunnel_cipSecTunInReplayDropPkts'] = { 'title': _('IPSec in replay packets dropped'), 'help': _(''), 'unit': 'count', - 'color': colors_b[17], -} - - -###################################################################################################################### -# -# map bgp peer perfdata to metric, not really necessary but makes sure to use the right metrics -# -###################################################################################################################### - - -check_metrics['check_mk-cisco_vpn_tunnel'] = { - 'cikeTunInOctets': {'name': 'cisco_vpn_tunnel_cikeTunInOctets'}, - 'cikeTunOutOctets': {'name': 'cisco_vpn_tunnel_cikeTunOutOctets'}, - 'cikeTunInPkts': {'name': 'cisco_vpn_tunnel_cikeTunInPkts'}, - 'cikeTunOutPkts': {'name': 'cisco_vpn_tunnel_cikeTunOutPkts'}, - 'cikeTunInDropPkts': {'name': 'cisco_vpn_tunnel_cikeTunInDropPkts'}, - 'cikeTunOutDropPkts': {'name': 'cisco_vpn_tunnel_cikeTunOutDropPkts'}, - 'cikeTunInNotifys': {'name': 'cisco_vpn_tunnel_cikeTunInNotifys'}, - 'cikeTunOutNotifys': {'name': 'cisco_vpn_tunnel_cikeTunOutNotifys'}, - 'cikeTunInP2Exchgs': {'name': 'cisco_vpn_tunnel_cikeTunInP2Exchgs', 'auto_graph' : False}, - 'cikeTunOutP2Exchgs': {'name': 'cisco_vpn_tunnel_cikeTunOutP2Exchgs'}, - 'cikeTunInP2ExchgInvalids': {'name': 'cisco_vpn_tunnel_cikeTunInP2ExchgInvalids'}, - 'cikeTunOutP2ExchgInvalids': {'name': 'cisco_vpn_tunnel_cikeTunOutP2ExchgInvalids'}, - 'cikeTunInP2ExchgRejects': {'name': 'cisco_vpn_tunnel_cikeTunInP2ExchgRejects'}, - 'cikeTunOutP2ExchgRejects': {'name': 'cisco_vpn_tunnel_cikeTunOutP2ExchgRejects'}, - 'cikeTunInP2SaDelRequests': {'name': 'cisco_vpn_tunnel_cikeTunInP2SaDelRequests'}, - 'cikeTunOutP2SaDelRequests': {'name': 'cisco_vpn_tunnel_cikeTunOutP2SaDelRequests'}, - 'cikeTunActiveTime': {'name': 'cisco_vpn_tunnel_cikeTunActiveTime'}, - - 'cipSecTunHcInOctets': {'name': 'cisco_vpn_tunnel_cipSecTunHcInOctets'}, - 'cipSecTunHcOutOctets': {'name': 'cisco_vpn_tunnel_cipSecTunHcOutOctets'}, - 'cipSecTunHcInDecompOctets': {'name': 'cisco_vpn_tunnel_cipSecTunHcInDecompOctets'}, - 'cipSecTunHcOutUncompOctets': {'name': 'cisco_vpn_tunnel_cipSecTunHcOutUncompOctets'}, - 'cipSecTunInPkts': {'name': 'cisco_vpn_tunnel_cipSecTunInPkts'}, - 'cipSecTunOutPkts': {'name': 'cisco_vpn_tunnel_cipSecTunOutPkts'}, - 'cipSecTunInDropPkts': {'name': 'cisco_vpn_tunnel_cipSecTunInDropPkts'}, - 'cipSecTunOutDropPkts': {'name': 'cisco_vpn_tunnel_cipSecTunOutDropPkts'}, - 'cipSecTunInAuths': {'name': 'cisco_vpn_tunnel_cipSecTunInAuths'}, - 'cipSecTunOutAuths': {'name': 'cisco_vpn_tunnel_cipSecTunOutAuths'}, - 'cipSecTunInAuthFails': {'name': 'cisco_vpn_tunnel_cipSecTunInAuthFails'}, - 'cipSecTunOutAuthFails': {'name': 'cisco_vpn_tunnel_cipSecTunOutAuthFails'}, - 'cipSecTunInDecrypts': {'name': 'cisco_vpn_tunnel_cipSecTunInDecrypts'}, - 'cipSecTunOutEncrypts': {'name': 'cisco_vpn_tunnel_cipSecTunOutEncrypts'}, - 'cipSecTunInDecryptFails': {'name': 'cisco_vpn_tunnel_cipSecTunInDecryptFails'}, - 'cipSecTunOutEncryptFails': {'name': 'cisco_vpn_tunnel_cipSecTunOutEncryptFails'}, - 'cipSecTunInReplayDropPkts': {'name': 'cisco_vpn_tunnel_cipSecTunInReplayDropPkts'}, - 'cipSecTunActiveTime': {'name': 'cisco_vpn_tunnel_cipSecTunActiveTime'}, - + 'color': '16/b', } ###################################################################################################################### @@ -333,20 +248,20 @@ check_metrics['check_mk-cisco_vpn_tunnel'] = { ###################################################################################################################### -graph_info.append({ +graph_info['cisco_vpn_tunnel_ike_uptime'] = { 'title': _('IKE active time'), 'metrics': [ ('cisco_vpn_tunnel_cikeTunActiveTime', 'area'), ], -}) -graph_info.append({ +} +graph_info['cisco_vpn_tunnel_ike_octets'] = { 'title': _('IKE Bytes/s'), 'metrics': [ ('cisco_vpn_tunnel_cikeTunOutOctets', '-area'), ('cisco_vpn_tunnel_cikeTunInOctets', 'area'), ], -}) -graph_info.append({ +} +graph_info['cisco_vpn_tunnel_ike_packets'] = { 'title': _('IKE packets/s'), 'metrics': [ ('cisco_vpn_tunnel_cikeTunOutDropPkts', '-line'), @@ -354,45 +269,22 @@ graph_info.append({ ('cisco_vpn_tunnel_cikeTunOutPkts', '-line'), ('cisco_vpn_tunnel_cikeTunInPkts', 'line'), ], -}) - -# graph_info.append({ -# 'title': _('IKE in data'), -# 'metrics': [ -# ('cisco_vpn_tunnel_cikeTunInNotifys', 'line'), -# # ('cisco_vpn_tunnel_cikeTunInP2Exchgs', 'line'), -# ('cisco_vpn_tunnel_cikeTunInP2ExchgInvalids', 'line'), -# ('cisco_vpn_tunnel_cikeTunInP2ExchgRejects', 'line'), -# ('cisco_vpn_tunnel_cikeTunInP2SaDelRequests', 'line'), -# ], -# }) -# -# graph_info.append({ -# 'title': _('IKE out data'), -# 'metrics': [ -# -# ('cisco_vpn_tunnel_cikeTunOutNotifys', '-line'), -# # ('cisco_vpn_tunnel_cikeTunOutP2Exchgs', '-line'), -# ('cisco_vpn_tunnel_cikeTunOutP2ExchgInvalids', '-line'), -# ('cisco_vpn_tunnel_cikeTunOutP2ExchgRejects', '-line'), -# ('cisco_vpn_tunnel_cikeTunOutP2SaDelRequests', '-line'), -# ], -# }) +} -graph_info.append({ +graph_info['cisco_vpn_tunnel_ipsec_uptime'] = { 'title': _('IPSec active time'), 'metrics': [ ('cisco_vpn_tunnel_cipSecTunActiveTime', 'area'), ], -}) -graph_info.append({ +} +graph_info['cisco_vpn_tunnel_ipsec_octets'] = { 'title': _('IPSec Bytes/s'), 'metrics': [ ('cisco_vpn_tunnel_cipSecTunHcOutOctets', '-area'), ('cisco_vpn_tunnel_cipSecTunHcInOctets', 'area'), ], -}) -graph_info.append({ +} +graph_info['cisco_vpn_tunnel_pckets'] = { 'title': _('IPSec packets/s'), 'metrics': [ ('cisco_vpn_tunnel_cipSecTunOutDropPkts', '-stack'), @@ -400,31 +292,7 @@ graph_info.append({ ('cisco_vpn_tunnel_cipSecTunOutPkts', '-stack'), ('cisco_vpn_tunnel_cipSecTunInPkts', 'stack'), ], -}) - -# graph_info.append({ -# 'title': _('IPSec in data'), -# 'metrics': [ -# # ('cisco_vpn_tunnel_cipSecTunHcInDecompOctets', 'line'), -# # ('cisco_vpn_tunnel_cipSecTunInAuths', 'line'), -# ('cisco_vpn_tunnel_cipSecTunInAuthFails', 'line'), -# # ('cisco_vpn_tunnel_cipSecTunInDecrypts', 'line'), -# ('cisco_vpn_tunnel_cipSecTunInDecryptFails', 'line'), -# ('cisco_vpn_tunnel_cipSecTunInReplayDropPkts', 'line'), -# ], -# }) -# -# graph_info.append({ -# 'title': _('IPSec out data'), -# 'metrics': [ -# # ('cisco_vpn_tunnel_cipSecTunHcOutUncompOctets', '-line'), -# # ('cisco_vpn_tunnel_cipSecTunOutAuths', '-line'), -# ('cisco_vpn_tunnel_cipSecTunOutAuthFails', '-line'), -# # ('cisco_vpn_tunnel_cipSecTunOutEncrypts', '-line'), -# ('cisco_vpn_tunnel_cipSecTunOutEncryptFails', '-line'), -# ], -# }) - +} ###################################################################################################################### # diff --git a/web/plugins/wato/cisco_vpn_tunnel.py b/web/plugins/wato/cisco_vpn_tunnel.py index ce6ed68..c298d92 100644 --- a/web/plugins/wato/cisco_vpn_tunnel.py +++ b/web/plugins/wato/cisco_vpn_tunnel.py @@ -1,22 +1,45 @@ -#!/usr/bin/python -# -*- encoding: utf-8; py-indent-offset: 4 -*- - -register_check_parameters( - subgroup_networking, - 'vpn_tunnel', - _('VPN Tunnel'), - Dictionary( +#!/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 : 2017-12-28 + +from cmk.gui.i18n import _ +from cmk.gui.valuespec import ( + Dictionary, + TextAscii, + Tuple, + MonitoringState, + ListOf, + IPv4Address, + TextUnicode, + FixedValue, +) + +from cmk.gui.plugins.wato import ( + CheckParameterRulespecWithItem, + rulespec_registry, + RulespecGroupCheckParametersNetworking, + RulespecGroupCheckParametersDiscovery, + HostRulespec, +) + + +def _parameter_valuespec_cisco_vpn_tunnel(): + return Dictionary( elements=[ ('tunnels', ListOf( Tuple( - title=('VPN Tunnel Endpoints'), + title=_('VPN Tunnel Endpoints'), elements=[ IPv4Address( title=_('Peer IP-Address'), help=_('The configured value must match a tunnel reported by the monitored ' 'device.'), - allow_empty=False, ), TextUnicode( title=_('Tunnel Alias'), @@ -28,7 +51,7 @@ register_check_parameters( title=_('State if tunnel is not found'), ), MonitoringState( - default_value=2, + default_value=1, title=_('State if tunnel has no active IPSec SA'), ), ]), @@ -41,19 +64,49 @@ register_check_parameters( title=_('Default state to report when tunnel can not be found anymore'), help=_('Default state if a tunnel, which is not listed above in this rule, ' 'can no longer be found.'), - default_value=3, - ), - ), + default_value=2, + )), ('missing_ipsec_sa_state', MonitoringState( title=_('Default state to report when tunnel has no active IPSec SA'), help=_('Default state if a tunnel, which is not listed above in this rule, ' 'has no active IPSec SA.'), default_value=1, - ), - ), + )), ], - ), - TextAscii(title=_('IP-Address of Tunnel Endpoint')), - match_type='dict', -) + ) + + +rulespec_registry.register( + CheckParameterRulespecWithItem( + check_group_name='cisco_vpn_tunnel', + group=RulespecGroupCheckParametersNetworking, + item_spec=lambda: TextAscii(title=_('IP-Address of Tunnel Endpoint'), ), + match_type='dict', + parameter_valuespec=_parameter_valuespec_cisco_vpn_tunnel, + title=lambda: _('Cisco VPN Tunnel'), + )) + + +def _valuespec_discovery_cisco_vpn_tunnel(): + return Dictionary( + title=_("VPN Tunnel discovery"), + elements=[( + 'discover_aggressive_mode', + FixedValue( + True, + default_value=False, + title=_('Discover aggressive mode VPN Tunnel'), + totext=_('Discover aggressive mode VPN Tunnel'), + ), + )], + ) + + +rulespec_registry.register( + HostRulespec( + group=RulespecGroupCheckParametersDiscovery, + match_type="dict", + name="discovery_cisco_vpn_tunnel", + valuespec=_valuespec_discovery_cisco_vpn_tunnel, + )) -- GitLab