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