From 255bc34a2c0f3a64900e33cabab5659eae886fd1 Mon Sep 17 00:00:00 2001
From: "th.l" <thl-cmk@outlook.com>
Date: Sun, 30 Mar 2025 17:08:27 +0200
Subject: [PATCH] modified for check APIv2, ruleset APIv1, graphing APIv1

---
 README.md                                     |   2 +-
 mkp/bgp_peer-2.3.0-20250329.mkp               | Bin 0 -> 18055 bytes
 source/agent_based/bgp_peer.py                | 328 +---------------
 .../bgp_peer/agent_based/bgp_peer.py          | 352 ++++++++++++++++++
 .../bgp_peer}/agent_based/inv_bgp_peer.py     |  18 +-
 .../bgp_peer}/checkman/bgp_peer               |   0
 .../bgp_peer/graphing/bgp_peer.py             | 290 +++++++++++++++
 .../bgp_peer/lib}/bgp_peer.py                 |   0
 .../bgp_peer/rulesets/bgp_peer.py             | 336 +++++++++++++++++
 .../bgp_peer/rulesets/inv_bgp_peer.py         | 124 ++++++
 source/gui/metrics/bgp_peer.py                | 253 -------------
 source/gui/wato/check_parameters/bgp_peer.py  | 254 +------------
 .../gui/wato/check_parameters/inv_bgp_peer.py | 109 ------
 source/packages/bgp_peer                      |  20 +-
 14 files changed, 1125 insertions(+), 961 deletions(-)
 create mode 100644 mkp/bgp_peer-2.3.0-20250329.mkp
 create mode 100644 source/cmk_addons_plugins/bgp_peer/agent_based/bgp_peer.py
 rename source/{ => cmk_addons_plugins/bgp_peer}/agent_based/inv_bgp_peer.py (94%)
 rename source/{ => cmk_addons_plugins/bgp_peer}/checkman/bgp_peer (100%)
 create mode 100644 source/cmk_addons_plugins/bgp_peer/graphing/bgp_peer.py
 rename source/{agent_based/utils => cmk_addons_plugins/bgp_peer/lib}/bgp_peer.py (100%)
 create mode 100644 source/cmk_addons_plugins/bgp_peer/rulesets/bgp_peer.py
 create mode 100644 source/cmk_addons_plugins/bgp_peer/rulesets/inv_bgp_peer.py
 delete mode 100644 source/gui/metrics/bgp_peer.py
 delete mode 100644 source/gui/wato/check_parameters/inv_bgp_peer.py

diff --git a/README.md b/README.md
index 424b923..66e44bc 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-[PACKAGE]: ../../raw/master/mkp/bgp_peer-2.2.8-20250117.mkp "bgp_peer-2.2.8-20250117.mkp"
+[PACKAGE]: ../../raw/master/mkp/bgp_peer-2.3.0-20250329.mkp "bgp_peer-2.3.0-20250329.mkp"
 # BGP Peer
 
 Check plugin to monitor the status of BGP peers and inventory plugin for static BGP peer data.
diff --git a/mkp/bgp_peer-2.3.0-20250329.mkp b/mkp/bgp_peer-2.3.0-20250329.mkp
new file mode 100644
index 0000000000000000000000000000000000000000..e3f51db18d304f8228fdfbd54a2c2c9c0632aed8
GIT binary patch
literal 18055
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
MeY2<p%<%yJ22tfRlK=n!

literal 0
HcmV?d00001

diff --git a/source/agent_based/bgp_peer.py b/source/agent_based/bgp_peer.py
index df8172b..32f65c7 100644
--- a/source/agent_based/bgp_peer.py
+++ b/source/agent_based/bgp_peer.py
@@ -1,328 +1,2 @@
 #!/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-08-20
-
-# based on the BGP peer plugin by Thomas Wollner (tw@wollner-net.de)
-#
-# 2021-08-20: rewritten for CMK 2.0 by thl-cmk[at]outlook[dot]com
-# 2021-08-21: added more perfdata, added metrics file
-# 2021-08-22: added WATO options, streamlined with cisco_bgp_peer
-# 2021-08-29: moved static (longoutput) to inventory plugin
-#             moved helper functions to utils/bgp_peer
-# 2021-11-14: merged check function with cisco_bgp_peer
-#             moved parse function to utils/bgp_peer
-# 2021-04-02: rewritten bgp neighbor state handling (made configurable)
-# 2022-04-29: added upper/lower prefix limits from wato
-#             added info if device is admin prefix limit capable (device_admin_limit)
-# 2022-05-09: made item name configurable (don't use address-family/routing-instance/VRF)
-# 2022-05-11: changed bgp_get_peer_entry to get proper parameters instead of Nontransparent list
-#             added remote_as to BgpPeerItem
-# 2022-09-05: added missing wato parameters to register.check_plugin check_default_parameters
-# 2022-09-10: made more reliable on limited data (fsm_established_time and admin_state)
-#             (THX to martin[dot]pechstein[at]posteo[dot]de)
-# 2022-09-11: optimized internal flow: > alias > not found > admin down > peer state > ...
-# 2023-01-21: changed to always yield fsm_established_time (not only if beep connects, but also on all other states)
-# 2023-01-22: fix output for admin_state
-# 2023-02-16: changed for CMK 2.1 (moved gui files from local/share/.. to local/lib/..)
-#             fix type error in discovery (CMK2.1 GUI only)
-# 2023-02-20: fix crash if metric data is None
-# 2023-03-26: optimized output of metrics, GetRateError will not be set to 0 anymore
-# 2023-08-17: fix removed internal_item form check_default_parameters (THX mail[at]bastian-kuhn[dot]de)
-#             added warning if internal_item is missing in params
-# 2024-06-07: fixed crash on configured state mapping
-# 2024-12-20: added local address/as/identifier for bgp_topology
-
-# Example Agent Output:
-# BGP4-MIB
-
-# .1.3.6.1.2.1.15.3.1.1.192.168.254.2 = IpAddress: 192.168.254.2
-# .1.3.6.1.2.1.15.3.1.2.192.168.254.2 = INTEGER: 3
-# .1.3.6.1.2.1.15.3.1.3.192.168.254.2 = INTEGER: 2
-# .1.3.6.1.2.1.15.3.1.4.192.168.254.2 = INTEGER: 4
-# .1.3.6.1.2.1.15.3.1.5.192.168.254.2 = IpAddress: 0.0.0.0
-# .1.3.6.1.2.1.15.3.1.7.192.168.254.2 = IpAddress: 192.168.254.2
-# .1.3.6.1.2.1.15.3.1.9.192.168.254.2 = INTEGER: 65301
-# .1.3.6.1.2.1.15.3.1.10.192.168.254.2 = Counter32: 0
-# .1.3.6.1.2.1.15.3.1.11.192.168.254.2 = Counter32: 5
-# .1.3.6.1.2.1.15.3.1.14.192.168.254.2 = Hex-STRING: 04 00
-# .1.3.6.1.2.1.15.3.1.16.192.168.254.2 = Gauge32: 6586822
-#
-
-import time
-from typing import Dict, List
-
-from cmk.base.plugins.agent_based.agent_based_api.v1 import (
-    register,
-    Service,
-    Result,
-    check_levels,
-    State,
-    SNMPTree,
-    exists,
-    get_rate,
-    GetRateError,
-    get_value_store,
-    # IgnoreResultsError,
-    Metric,
-    render,
-)
-from cmk.base.plugins.agent_based.agent_based_api.v1.type_defs import (
-    DiscoveryResult,
-    CheckResult,
-    StringTable,
-)
-from cmk.base.plugins.agent_based.utils.bgp_peer import (
-    BgpPeer,
-    bgp_get_peer_entry,
-    bgp_adminstate,
-    bgp_peerstate,
-)
-
-
-def parse_bgp_peer(string_table: List[StringTable]) -> Dict[str, BgpPeer] | None:
-    peer_table = {}
-    try:
-        peer_info, local_info = string_table
-    except ValueError:
-        return
-
-    try:
-        local_as, local_id = local_info[0]
-    except (IndexError, ValueError):
-        local_as, local_id = None, None
-
-    for entry in peer_info:
-        remote_addr, remote_id, peer_state, admin_state, local_addr, remote_as, in_updates, out_updates, \
-        in_messages, out_messages, fsm_established_transitions, fsm_established_time, in_update_elapsed_time = entry
-
-        bgp_peer = bgp_get_peer_entry(
-            admin_state=admin_state,
-            fsm_established_time=fsm_established_time,
-            fsm_established_transitions=fsm_established_transitions,
-            in_messages=in_messages,
-            in_update_elapsed_time=in_update_elapsed_time,
-            in_updates=in_updates,
-            local_addr=local_addr,
-            out_messages=out_messages,
-            out_updates=out_updates,
-            peer_state=peer_state,
-            remote_addr=remote_addr,
-            remote_as=remote_as,
-            remote_id=remote_id,
-            local_as=local_as,
-            local_id=local_id,
-        )
-        if bgp_peer:
-            peer_table.update(bgp_peer)
-    return peer_table
-
-
-def discovery_bgp_peer(params, section: Dict[str, BgpPeer]) -> DiscoveryResult:
-    _item_parts = [
-        'remote_address',
-        'remote_as',
-        'address_family',
-        'routing_instance',
-    ]
-    for key in section.keys():
-        parameters = {'internal_item': key}
-        item = ''
-        for item_part in _item_parts:
-            if item_part not in params['build_item']:
-                item += f'{section[key].item[item_part]} '
-        item = item.strip(' ')
-        yield Service(item=item, parameters=parameters)
-
-
-def bgp_peer_base_info(peer: BgpPeer) -> GeneratorExit:
-    for message, value in [
-        ('Local AS:', peer.local_as),
-        ('Local address:', peer.local_addr),
-        ('Local identifier:', peer.local_id),
-        ('Remote AS:', peer.remote_as),
-        ('Remote address:', peer.remote_addr),
-        ('Remote identifier:', peer.remote_id),
-    ]:
-        if value is not None:
-            yield Result(state=State.OK, notice=f'{message} {value}')
-
-
-def check_bgp_peer(item, params, section: Dict[str, BgpPeer]) -> CheckResult:
-    if not params.get('internal_item'):
-        yield Result(
-            state=State.WARN,
-            summary='This service is using old parameters (see details)',
-            details='This service is using old parameters. To refresh the parameters you need to do a "Tabula rasa" '
-                    '(Discover services -> Remove all and find new)',
-        )
-    else:
-        item = params['internal_item']
-
-    neighborstate = {
-        '1': 2,  # idle
-        '2': 1,  # connect
-        '3': 1,  # active
-        '4': 1,  # opensent
-        '5': 1,  # openconfirm
-        '6': 0,  # established
-    }
-
-    neighborstate.update(params['neighborstate'])
-    peer_not_found_state = params['peernotfound']
-
-    for bgp_connection, bgp_alias, not_found_state in params.get('peer_list', []):
-        if item == bgp_connection:
-            if bgp_alias != '':
-                yield Result(state=State.OK, summary=f'[{bgp_alias}]', details=' ')
-            peer_not_found_state = not_found_state
-
-    # get item data
-    try:
-        peer = section[item]
-    except KeyError:
-        yield Result(state=State(peer_not_found_state), summary='Item not found in SNMP data')
-        return
-
-    if peer.description:
-        yield Result(state=State.OK, summary=f'[{peer.description}]', details=' ')
-
-    if peer.admin_state == 1:  # shutdown
-        yield Result(state=State(params['admindown']), summary=f'Admin state: {bgp_adminstate(peer.admin_state)}')
-        yield from bgp_peer_base_info(peer)
-        return
-
-    yield Result(
-        state=State(neighborstate.get(str(peer.peer_state))),
-        notice=f'Peer state: {bgp_peerstate(peer.peer_state)}'
-    )
-
-    if peer.fsm_established_time:  # always yield fsm_established time
-        yield Metric(
-            name=f'bgp_peer_fsmestablishedtime',
-            value=peer.fsm_established_time,
-            boundaries=(0, None)
-        )
-
-    if not peer.peer_state == 6:  # not established
-        yield from bgp_peer_base_info(peer)
-        return
-
-    if peer.fsm_established_time:  # if peer is established use fsm_established_time as uptime
-        yield from check_levels(
-            value=peer.fsm_established_time,
-            label='Uptime',
-            levels_lower=params['minuptime'],
-            render_func=render.timespan,
-            metric_name='bgp_peer_fsmestablishedtime',
-        )
-
-    if peer.peer_unavail_reason != 0:  # huawei peer unavailable state
-        yield Result(state=State.CRIT, notice=F'Peer unavailable reason: {peer.peer_unavail_reason_str}')
-
-    if peer.device_admin_limit and peer.prefix_admin_limit is None:
-        yield Result(
-            state=State(params['noprefixlimit']),
-            notice='Prefix limit/warn threshold not configured on the device.',
-        )
-
-    acceptedprefixes = peer.accepted_prefixes
-    prefixadminlimit = peer.prefix_admin_limit
-    prefixthreshold = peer.prefix_threshold
-    warnthreshold = None
-
-    if prefixadminlimit is not None and prefixthreshold is not None:
-        warnthreshold = int(prefixadminlimit / 100.0 * prefixthreshold)  # use float (100.0) to get xx.xx in division
-    if acceptedprefixes is not None and peer.peer_state == 6:  # peer established and prefixes accepted
-        yield from check_levels(
-            value=acceptedprefixes,
-            metric_name='bgp_peer_acceptedprefixes',
-            levels_upper=params.get('accepted_prefixes_upper_levels', (warnthreshold, prefixadminlimit)),
-            levels_lower=params.get('accepted_prefixes_lower_levels'),
-            label='Prefixes accepted',
-            render_func=lambda v: f'{v}'
-        )
-
-    now_time = time.time()
-    value_store = get_value_store()
-
-    for key, value in peer.metric_rate:
-        try:
-            value = get_rate(value_store, f'{key}', now_time, value, raise_overflow=True)
-        except GetRateError:
-            continue
-        yield Metric(name=f'bgp_peer_{key}', value=value, boundaries=(0, None))
-
-    for key, value in peer.metric_count:
-        if value is not None:
-            yield Metric(name=f'bgp_peer_{key}', value=value, boundaries=(0, None))
-
-    yield from bgp_peer_base_info(peer)
-
-
-register.snmp_section(
-    name='bgp_peer',
-    parse_function=parse_bgp_peer,
-    fetch=[
-        SNMPTree(
-            base='.1.3.6.1.2.1.15.3.1',  # BGP4-MIB::BgpPeerEntry
-            oids=[
-                '7',  # bgpPeerRemoteAddr
-                '1',  # bgpPeerIdentifier
-                '2',  # bgpPeerState
-                '3',  # bgpPeerAdminStatus
-                '5',  # bgpPeerLocalAddr
-                '9',  # bgpPeerRemoteAs
-                '10',  # bgpPeerInUpdates
-                '11',  # bgpPeerOutUpdates
-                '12',  # bgpPeerInTotalMessages
-                '13',  # bgpPeerOutTotalMessages
-                '15',  # bgpPeerFsmEstablishedTransitions
-                '16',  # bgpPeerFsmEstablishedTime
-                '24',  # bgpPeerInUpdateElapsedTime
-            ]),
-        SNMPTree(
-            base='.1.3.6.1.2.1.15',  # BGP4-MIB
-            oids=[
-                '2',  # bgpLocalAs
-                '4',  # bgpIdentifier (local)
-            ])
-    ],
-    detect=exists('.1.3.6.1.2.1.15.3.1.1.*')
-)
-
-register.check_plugin(
-    name='bgp_peer',
-    service_name='BGP peer %s',
-    discovery_function=discovery_bgp_peer,
-    discovery_default_parameters={
-        'build_item': [
-            # 'remote_address',
-            'remote_as',
-            # 'address_family',
-            # 'routing_instance',
-        ]
-    },
-    discovery_ruleset_name='discovery_bgp_peer',
-    check_function=check_bgp_peer,
-    check_default_parameters={
-        'minuptime': (7200, 3600),
-        'peer_list': [],
-        'peernotfound': 2,
-        'admindown': 1,
-        'noprefixlimit': 1,
-        'neighborstate': {
-            '1': 2,  # idle
-            '2': 1,  # connect
-            '3': 1,  # active
-            '4': 1,  # opensent
-            '5': 1,  # openconfirm
-            '6': 0,  # established
-        },
-    },
-    check_ruleset_name='bgp_peer',
-)
+# dummy needed for CMK 2.30x to shadow the original file
\ No newline at end of file
diff --git a/source/cmk_addons_plugins/bgp_peer/agent_based/bgp_peer.py b/source/cmk_addons_plugins/bgp_peer/agent_based/bgp_peer.py
new file mode 100644
index 0000000..e2f29fe
--- /dev/null
+++ b/source/cmk_addons_plugins/bgp_peer/agent_based/bgp_peer.py
@@ -0,0 +1,352 @@
+#!/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-08-20
+
+# based on the BGP peer plugin by Thomas Wollner (tw@wollner-net.de)
+#
+# 2021-08-20: rewritten for CMK 2.0 by thl-cmk[at]outlook[dot]com
+# 2021-08-21: added more perfdata, added metrics file
+# 2021-08-22: added WATO options, streamlined with cisco_bgp_peer
+# 2021-08-29: moved static (longoutput) to inventory plugin
+#             moved helper functions to utils/bgp_peer
+# 2021-11-14: merged check function with cisco_bgp_peer
+#             moved parse function to utils/bgp_peer
+# 2021-04-02: rewritten bgp neighbor state handling (made configurable)
+# 2022-04-29: added upper/lower prefix limits from wato
+#             added info if device is admin prefix limit capable (device_admin_limit)
+# 2022-05-09: made item name configurable (don't use address-family/routing-instance/VRF)
+# 2022-05-11: changed bgp_get_peer_entry to get proper parameters instead of Nontransparent list
+#             added remote_as to BgpPeerItem
+# 2022-09-05: added missing wato parameters to register.check_plugin check_default_parameters
+# 2022-09-10: made more reliable on limited data (fsm_established_time and admin_state)
+#             (THX to martin[dot]pechstein[at]posteo[dot]de)
+# 2022-09-11: optimized internal flow: > alias > not found > admin down > peer state > ...
+# 2023-01-21: changed to always yield fsm_established_time (not only if beep connects, but also on all other states)
+# 2023-01-22: fix output for admin_state
+# 2023-02-16: changed for CMK 2.1 (moved gui files from local/share/.. to local/lib/..)
+#             fix type error in discovery (CMK2.1 GUI only)
+# 2023-02-20: fix crash if metric data is None
+# 2023-03-26: optimized output of metrics, GetRateError will not be set to 0 anymore
+# 2023-08-17: fix removed internal_item form check_default_parameters (THX mail[at]bastian-kuhn[dot]de)
+#             added warning if internal_item is missing in params
+# 2024-06-07: fixed crash on configured state mapping
+# 2024-12-20: added local address/as/identifier for bgp_topology
+# 2025-03-29: refactored for ruleset APIv1
+
+# Example Agent Output:
+# BGP4-MIB
+
+# .1.3.6.1.2.1.15.3.1.1.192.168.254.2 = IpAddress: 192.168.254.2
+# .1.3.6.1.2.1.15.3.1.2.192.168.254.2 = INTEGER: 3
+# .1.3.6.1.2.1.15.3.1.3.192.168.254.2 = INTEGER: 2
+# .1.3.6.1.2.1.15.3.1.4.192.168.254.2 = INTEGER: 4
+# .1.3.6.1.2.1.15.3.1.5.192.168.254.2 = IpAddress: 0.0.0.0
+# .1.3.6.1.2.1.15.3.1.7.192.168.254.2 = IpAddress: 192.168.254.2
+# .1.3.6.1.2.1.15.3.1.9.192.168.254.2 = INTEGER: 65301
+# .1.3.6.1.2.1.15.3.1.10.192.168.254.2 = Counter32: 0
+# .1.3.6.1.2.1.15.3.1.11.192.168.254.2 = Counter32: 5
+# .1.3.6.1.2.1.15.3.1.14.192.168.254.2 = Hex-STRING: 04 00
+# .1.3.6.1.2.1.15.3.1.16.192.168.254.2 = Gauge32: 6586822
+#
+
+import time
+from typing import Dict, List, Generator
+
+from cmk.agent_based.v2 import (
+    CheckPlugin,
+    CheckResult,
+    DiscoveryResult,
+    GetRateError,
+    Metric,
+    Result,
+    SNMPSection,
+    SNMPTree,
+    Service,
+    State,
+    StringTable,
+    check_levels,
+    exists,
+    get_rate,
+    get_value_store,
+    render,
+)
+
+from cmk_addons.plugins.bgp_peer.lib.bgp_peer import (
+    BgpPeer,
+    bgp_adminstate,
+    bgp_get_peer_entry,
+    bgp_peerstate,
+)
+
+
+def parse_bgp_peer(string_table: List[StringTable]) -> Dict[str, BgpPeer] | None:
+    peer_table = {}
+    try:
+        peer_info, local_info = string_table
+    except ValueError:
+        return None
+
+    try:
+        local_as, local_id = local_info[0]
+    except (IndexError, ValueError):
+        local_as, local_id = None, None
+
+    for entry in peer_info:
+        remote_addr, remote_id, peer_state, admin_state, local_addr, remote_as, in_updates, out_updates, \
+            in_messages, out_messages, fsm_established_transitions, fsm_established_time, in_update_elapsed_time = entry
+
+        bgp_peer = bgp_get_peer_entry(
+            admin_state=admin_state,
+            fsm_established_time=fsm_established_time,
+            fsm_established_transitions=fsm_established_transitions,
+            in_messages=in_messages,
+            in_update_elapsed_time=in_update_elapsed_time,
+            in_updates=in_updates,
+            local_addr=local_addr,
+            out_messages=out_messages,
+            out_updates=out_updates,
+            peer_state=peer_state,
+            remote_addr=remote_addr,
+            remote_as=remote_as,
+            remote_id=remote_id,
+            local_as=local_as,
+            local_id=local_id,
+        )
+        if bgp_peer:
+            peer_table.update(bgp_peer)
+    return peer_table
+
+
+def discovery_bgp_peer(params, section: Dict[str, BgpPeer]) -> DiscoveryResult:
+    _item_parts = [
+        'remote_address',
+        'remote_as',
+        'address_family',
+        'routing_instance',
+    ]
+    for key in section.keys():
+        parameters = {'internal_item': key}
+        item = ''
+        for item_part in _item_parts:
+            if item_part not in params['build_item']:
+                item += f'{section[key].item[item_part]} '
+        item = item.strip(' ')
+        yield Service(item=item, parameters=parameters)
+
+
+def bgp_peer_base_info(peer: BgpPeer) -> Generator:
+    for message, value in [
+        ('Local AS:', peer.local_as),
+        ('Local address:', peer.local_addr),
+        ('Local identifier:', peer.local_id),
+        ('Remote AS:', peer.remote_as),
+        ('Remote address:', peer.remote_addr),
+        ('Remote identifier:', peer.remote_id),
+    ]:
+        if value is not None:
+            yield Result(state=State.OK, notice=f'{message} {value}')
+
+
+def _rewrite_ruleset_needed(params: dict) -> bool:
+    for param, value in params.items():
+        match param:
+            case 'accepted_prefixes_lower_levels' | 'accepted_prefixes_upper_levels' | 'minuptime':
+                if isinstance(value, tuple) and len(value) == 2 \
+                        and isinstance(value[0], int) and isinstance(value[1], int):
+                    return True
+            case 'neighborstate':
+                for key in value.keys():
+                    if key in '123456':
+                        return True
+            case 'peer_list':
+                if isinstance(value[0], tuple):
+                    return True
+    return False
+
+
+def check_bgp_peer(item, params, section: Dict[str, BgpPeer]) -> CheckResult:
+    if not params.get('internal_item'):
+        yield Result(
+            state=State.WARN,
+            summary='This service is using old parameters (see details)',
+            details='This service is using old parameters. To refresh the parameters you need to do a "Tabula rasa" '
+                    '(Discover services -> Remove all and find new)',
+        )
+    else:
+        item = params['internal_item']
+
+    if _rewrite_ruleset_needed(params):
+        yield Result(state=State.WARN,
+                     summary='BGP ruleset needs to be opened and saved to migrate the ruleset to ruleset APIv1!')
+        return
+
+    neighbor_state = {
+        '1': 2,  # idle
+        '2': 1,  # connect
+        '3': 1,  # active
+        '4': 1,  # opensent
+        '5': 1,  # openconfirm
+        '6': 0,  # established
+    }
+
+    neighbor_state.update(params['neighborstate'])
+    peer_not_found_state = params['peernotfound']
+
+    for entry in params.get('peer_list', []):
+        if item == entry['bgp_peer']:
+            if entry.get('bgp_peer_alias'):
+                yield Result(state=State.OK, summary=f'[{entry["bgp_peer_alias"]}]', details=' ')
+            peer_not_found_state = entry.get('state_not_found', params['peernotfound'])
+
+    # get item data
+    try:
+        peer = section[item]
+    except KeyError:
+        yield Result(state=State(peer_not_found_state), summary='Item not found in SNMP data')
+        return
+
+    if peer.description:
+        yield Result(state=State.OK, summary=f'[{peer.description}]', details=' ')
+
+    if peer.admin_state == 1:  # shutdown
+        yield Result(state=State(params['admindown']), summary=f'Admin state: {bgp_adminstate(peer.admin_state)}')
+        yield from bgp_peer_base_info(peer)
+        return
+
+    yield Result(
+        state=State(neighbor_state.get(str(peer.peer_state))),
+        notice=f'Peer state: {bgp_peerstate(peer.peer_state)}'
+    )
+
+    if peer.fsm_established_time:  # always yield fsm_established time
+        yield Metric(
+            name=f'bgp_peer_fsmestablishedtime',
+            value=peer.fsm_established_time,
+            boundaries=(0, None)
+        )
+
+    if not peer.peer_state == 6:  # not established
+        yield from bgp_peer_base_info(peer)
+        return
+
+    if peer.fsm_established_time:  # if peer is established use fsm_established_time as uptime
+        yield from check_levels(
+            value=peer.fsm_established_time,
+            label='Uptime',
+            levels_upper=('no_levels', None),
+            levels_lower=params['minuptime'],
+            render_func=render.timespan,
+            metric_name='bgp_peer_fsmestablishedtime',
+        )
+
+    if peer.peer_unavail_reason != 0:  # huawei peer unavailable state
+        yield Result(state=State.CRIT, notice=F'Peer unavailable reason: {peer.peer_unavail_reason_str}')
+
+    if peer.device_admin_limit and peer.prefix_admin_limit is None:
+        yield Result(
+            state=State(params['noprefixlimit']),
+            notice='Prefix limit/warn threshold not configured on the device.',
+        )
+
+    if peer.accepted_prefixes is not None and peer.peer_state == 6:  # peer established and prefixes accepted
+        if peer.prefix_admin_limit is None or peer.prefix_threshold is None:
+            levels_upper = None
+        else:
+            # use float (100.0) to get xx.xx in division
+            levels_upper = (
+                'fixed', (int(peer.prefix_admin_limit / 100.0 * peer.prefix_threshold), peer.prefix_admin_limit)
+            )
+
+        yield from check_levels(
+            value=peer.accepted_prefixes,
+            metric_name='bgp_peer_acceptedprefixes',
+            levels_upper=params.get('accepted_prefixes_upper_levels', levels_upper),
+            levels_lower=params.get('accepted_prefixes_lower_levels'),
+            label='Prefixes accepted',
+            render_func=lambda v: f'{v}'
+        )
+
+    now_time = time.time()
+    value_store = get_value_store()
+
+    for key, value in peer.metric_rate:
+        try:
+            value = get_rate(value_store, f'{key}', now_time, value, raise_overflow=True)
+        except GetRateError:
+            continue
+        yield Metric(name=f'bgp_peer_{key}', value=value, boundaries=(0, None))
+
+    for key, value in peer.metric_count:
+        if value is not None:
+            yield Metric(name=f'bgp_peer_{key}', value=value, boundaries=(0, None))
+
+    yield from bgp_peer_base_info(peer)
+
+
+snmp_section_bgp_peer = SNMPSection(
+    name='bgp_peer',
+    parse_function=parse_bgp_peer,
+    fetch=[
+        SNMPTree(
+            base='.1.3.6.1.2.1.15.3.1',  # BGP4-MIB::BgpPeerEntry
+            oids=[
+                '7',  # bgpPeerRemoteAddr
+                '1',  # bgpPeerIdentifier
+                '2',  # bgpPeerState
+                '3',  # bgpPeerAdminStatus
+                '5',  # bgpPeerLocalAddr
+                '9',  # bgpPeerRemoteAs
+                '10',  # bgpPeerInUpdates
+                '11',  # bgpPeerOutUpdates
+                '12',  # bgpPeerInTotalMessages
+                '13',  # bgpPeerOutTotalMessages
+                '15',  # bgpPeerFsmEstablishedTransitions
+                '16',  # bgpPeerFsmEstablishedTime
+                '24',  # bgpPeerInUpdateElapsedTime
+            ]),
+        SNMPTree(
+            base='.1.3.6.1.2.1.15',  # BGP4-MIB
+            oids=[
+                '2',  # bgpLocalAs
+                '4',  # bgpIdentifier (local)
+            ])
+    ],
+    detect=exists('.1.3.6.1.2.1.15.3.1.1.*')
+)
+
+check_plugin_bgp_peer = CheckPlugin(
+    name='bgp_peer',
+    service_name='BGP peer %s',
+    discovery_function=discovery_bgp_peer,
+    discovery_default_parameters={
+        'build_item': [
+            # 'remote_address',
+            'remote_as',
+            # 'address_family',
+            # 'routing_instance',
+        ]
+    },
+    discovery_ruleset_name='discovery_bgp_peer',
+    check_function=check_bgp_peer,
+    check_default_parameters={
+        'minuptime': ('fixed', (7200, 3600)),
+        'peer_list': [],
+        'peernotfound': 2,
+        'admindown': 1,
+        'noprefixlimit': 1,
+        'neighborstate': {
+            'idle': 2,  # ........1
+            'connect': 1,  # .....2
+            'active': 1,  # ......3
+            'open_sent': 1,  # ...4
+            'open_confirm': 1,  # 5
+            'established': 0,  # .6
+        },
+    },
+    check_ruleset_name='bgp_peer',
+)
diff --git a/source/agent_based/inv_bgp_peer.py b/source/cmk_addons_plugins/bgp_peer/agent_based/inv_bgp_peer.py
similarity index 94%
rename from source/agent_based/inv_bgp_peer.py
rename to source/cmk_addons_plugins/bgp_peer/agent_based/inv_bgp_peer.py
index 869b337..ce96d07 100644
--- a/source/agent_based/inv_bgp_peer.py
+++ b/source/cmk_addons_plugins/bgp_peer/agent_based/inv_bgp_peer.py
@@ -18,18 +18,18 @@
 import time
 from typing import List
 
-from cmk.base.plugins.agent_based.agent_based_api.v1 import (
+from cmk.agent_based.v2 import (
+    InventoryPlugin,
+    InventoryResult,
     OIDBytes,
+    SNMPSection,
     SNMPTree,
+    StringByteTable,
     TableRow,
     exists,
-    register,
-)
-from cmk.base.plugins.agent_based.agent_based_api.v1.type_defs import (
-    InventoryResult,
-    StringByteTable,
 )
-from cmk.base.plugins.agent_based.utils.bgp_peer import (
+
+from cmk_addons.plugins.bgp_peer.lib.bgp_peer import (
     BgpWhois,
     InvBgpPeer,
     bgp_error_as_string,
@@ -132,7 +132,7 @@ def inventory_bgp_peers(params, section) -> InventoryResult:
         del whois
 
 
-register.snmp_section(
+snmp_section_inv_bgp_peer = SNMPSection(
     name='inv_bgp_peer',
     parse_function=parse_inv_bgp_peer,
     fetch=[
@@ -160,7 +160,7 @@ register.snmp_section(
     detect=exists('.1.3.6.1.2.1.15.3.1.1.*')
 )
 
-register.inventory_plugin(
+inventory_plugin_inv_bgp_peer = InventoryPlugin(
     name='inv_bgp_peer',
     inventory_function=inventory_bgp_peers,
     inventory_default_parameters={
diff --git a/source/checkman/bgp_peer b/source/cmk_addons_plugins/bgp_peer/checkman/bgp_peer
similarity index 100%
rename from source/checkman/bgp_peer
rename to source/cmk_addons_plugins/bgp_peer/checkman/bgp_peer
diff --git a/source/cmk_addons_plugins/bgp_peer/graphing/bgp_peer.py b/source/cmk_addons_plugins/bgp_peer/graphing/bgp_peer.py
new file mode 100644
index 0000000..b40ffd9
--- /dev/null
+++ b/source/cmk_addons_plugins/bgp_peer/graphing/bgp_peer.py
@@ -0,0 +1,290 @@
+#!/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-08-21
+#
+# BGP Peer metrics plugin
+#
+# 2024-05-17: fixed typo in range mac -> max (crashed graphs in cmk 2.3.)
+# 2025-03-29: rewritten for graphing API v1
+
+from cmk.graphing.v1 import Title, graphs, metrics, perfometers
+
+
+UNIT_COUNTER = metrics.Unit(metrics.DecimalNotation(''), metrics.StrictPrecision(2))
+UNIT_PER_SECOND = metrics.Unit(metrics.DecimalNotation('/s'))
+UNIT_TIME = metrics.Unit(metrics.TimeNotation())
+
+metric_bgp_peer_accepted_prefixes = metrics.Metric(
+    name='bgp_peer_acceptedprefixes',
+    title=Title("Prefixes accepted"),
+    unit=UNIT_COUNTER,
+    color=metrics.Color.DARK_PURPLE,
+)
+
+metric_bgp_peer_advertised_prefixes = metrics.Metric(
+    name='bgp_peer_advertisedprefixes',
+    title=Title("Prefixes advertised"),
+    unit=UNIT_PER_SECOND,
+    color=metrics.Color.CYAN,
+)
+
+metric_bgp_peer_denied_prefixes = metrics.Metric(
+    name='bgp_peer_deniedprefixes',
+    title=Title("Prefixes denied"),
+    unit=UNIT_PER_SECOND,
+    color=metrics.Color.YELLOW,
+)
+
+metric_bgp_peer_fsm_established_time = metrics.Metric(
+    name='bgp_peer_fsmestablishedtime',
+    title=Title("FSM last change"),
+    unit=UNIT_TIME,
+    color=metrics.Color.GREEN,
+)
+
+metric_bgp_peer_fsm_established_transitions = metrics.Metric(
+    name='bgp_peer_fsmestablishedtransitions',
+    title=Title("FSM transitions"),
+    unit=UNIT_COUNTER,
+    color=metrics.Color.YELLOW,
+)
+
+metric_bgp_peer_in_total_messages = metrics.Metric(
+    name='bgp_peer_intotalmessages',
+    title=Title("Messages received"),
+    unit=UNIT_PER_SECOND,
+    color=metrics.Color.DARK_BLUE,
+)
+
+metric_bgp_peer_in_update_elapsed_time = metrics.Metric(
+    name='bgp_peer_inupdateelapsedtime',
+    title=Title("Last update received"),
+    unit=UNIT_TIME,
+    color=metrics.Color.DARK_BLUE,
+)
+
+metric_bgp_peer_in_updates = metrics.Metric(
+    name='bgp_peer_inupdates',
+    title=Title("Updates received"),
+    unit=UNIT_PER_SECOND,
+    color=metrics.Color.YELLOW,
+)
+
+metric_bgp_peer_out_total_messages = metrics.Metric(
+    name='bgp_peer_outtotalmessages',
+    title=Title("Messages send"),
+    unit=UNIT_PER_SECOND,
+    color=metrics.Color.DARK_PINK,
+)
+
+metric_bgp_peer_out_updates = metrics.Metric(
+    name='bgp_peer_outupdates',
+    title=Title("Updates send"),
+    unit=UNIT_PER_SECOND,
+    color=metrics.Color.CYAN,
+)
+
+metric_bgp_peer_suppressed_prefixes = metrics.Metric(
+    name='bgp_peer_suppressedprefixes',
+    title=Title("Prefixes suppressed"),
+    unit=UNIT_PER_SECOND,
+    color=metrics.Color.DARK_PINK,
+)
+
+metric_bgp_peer_withdrawn_prefixes = metrics.Metric(
+    name='bgp_peer_withdrawnprefixes',
+    title=Title("Prefixes withdrawn"),
+    unit=UNIT_PER_SECOND,
+    color=metrics.Color.BLUE,
+)
+
+# Juniper specific metrics
+metric_bgp_peer_in_prefixes = metrics.Metric(
+    name='bgp_peer_in_prefixes',
+    title=Title("Prefixes in"),
+    unit=UNIT_COUNTER,
+    color=metrics.Color.DARK_PURPLE,
+)
+
+metric_bgp_peer_in_prefixes_rejected = metrics.Metric(
+    name='bgp_peer_in_prefixes_rejected',
+    title=Title("Prefixes in rejected"),
+    unit=UNIT_COUNTER,
+    color=metrics.Color.YELLOW,
+)
+
+metric_bgp_peer_in_prefixes_active = metrics.Metric(
+    name='bgp_peer_in_prefixes_active',
+    title=Title("Prefixes in active"),
+    unit=UNIT_COUNTER,
+    color=metrics.Color.CYAN,
+)
+
+metric_bgp_peer_out_prefixes = metrics.Metric(
+    name='bgp_peer_out_prefixes',
+    title=Title("Prefixes out"),
+    unit=UNIT_COUNTER,
+    color=metrics.Color.BLUE,
+)
+
+# Huawei specific metrics
+metric_bgp_peer_prefix_adv_counter = metrics.Metric(
+    name='bgp_peer_prefixadvcounter',
+    title=Title("Prefixes advertised"),
+    unit=UNIT_COUNTER,
+    color=metrics.Color.DARK_BLUE,
+)
+
+metric_bgp_peer_prefix_active_counter = metrics.Metric(
+    name='bgp_peer_prefixactivecounter',
+    title=Title("Prefixes active"),
+    unit=UNIT_COUNTER,
+    color=metrics.Color.CYAN,
+)
+
+metric_bgp_peer_prefix_rcv_counter = metrics.Metric(
+    name='bgp_peer_prefixrcvcounter',
+    title=Title("Prefixes received"),
+    unit=UNIT_COUNTER,
+    color=metrics.Color.DARK_PURPLE,
+)
+
+graph_bgp_peer_fms_transitions_last_change = graphs.Graph(
+    name='bgp_peer.fms_transitions_last_change',
+    title=Title("FSM established last change"),
+    minimal_range=graphs.MinimalRange(0, metrics.MaximumOf('bgp_peer_fsmestablishedtime', metrics.Color.GRAY, ), ),
+    compound_lines=['bgp_peer_fsmestablishedtime'],
+)
+
+graph_bgp_peer_prefixes_accepted = graphs.Graph(
+    name='bgp_peer.prefixes_accepted',
+    title=Title("Accepted Prefixes"),
+    minimal_range=graphs.MinimalRange(0, metrics.MaximumOf('bgp_peer_acceptedprefixes', metrics.Color.GRAY, ), ),
+    compound_lines=['bgp_peer_acceptedprefixes'],
+    simple_lines=[
+        metrics.CriticalOf('bgp_peer_acceptedprefixes'),
+        metrics.WarningOf('bgp_peer_acceptedprefixes'),
+    ],
+)
+
+graph_bgp_peer_prefixes_per_second = graphs.Graph(
+    name='bgp_peer.prefixes_per_second',
+    title=Title("Prefixes/s"),
+    simple_lines=[
+        'bgp_peer_withdrawnprefixes',
+        'bgp_peer_suppressedprefixes',
+        'bgp_peer_deniedprefixes',
+        'bgp_peer_advertisedprefixes',
+    ],
+    optional=[
+        'bgp_peer_withdrawnprefixes',
+        'bgp_peer_suppressedprefixes',
+        'bgp_peer_deniedprefixes',
+        'bgp_peer_advertisedprefixes',
+    ],
+)
+
+graph_bgp_peer_updates_in_out = graphs.Bidirectional(
+    name='bgp_peer.updates_in_out',
+    title=Title("Updates"),
+    lower=graphs.Graph(
+        name='bgp_peer.updates_in_out_lower',
+        title=Title("Updates"),
+        compound_lines=['bgp_peer_outupdates'],
+    ),
+    upper=graphs.Graph(
+        name='bgp_peer.updates_in_out_upper',
+        title=Title("Updates"),
+        compound_lines=['bgp_peer_inupdates'],
+    ),
+)
+
+graph_bgp_peer_messages_in_out = graphs.Bidirectional(
+    name='bgp_peer.messages_in_out',
+    title=Title("Total messages"),
+    lower=graphs.Graph(
+        name='bgp_peer.messages_in_out_lower',
+        title=Title("Total messages"),
+        compound_lines=['bgp_peer_outtotalmessages'],
+    ),
+    upper=graphs.Graph(
+        name='bgp_peer.messages_in_out_upper',
+        title=Title("Total messages"),
+        compound_lines=['bgp_peer_intotalmessages'],
+    ),
+)
+
+graph_bgp_peer_fms_transitions_from_to = graphs.Graph(
+    name='bgp_peer.fms_transitions_from_to',
+    title=Title("FSM transitions from/to established"),
+    minimal_range=graphs.MinimalRange(
+        0, metrics.MaximumOf('bgp_peer_fsmestablishedtransitions', metrics.Color.GRAY, ),
+    ),
+    compound_lines=['bgp_peer_fsmestablishedtransitions'],
+)
+
+graph_bgp_peer_time_since_last_update = graphs.Graph(
+    name='bgp_peer.time_since_last_update',
+    title=Title("Time since last update received"),
+    minimal_range=graphs.MinimalRange(
+        0, metrics.MaximumOf('bgp_peer_inupdateelapsedtime', metrics.Color.GRAY, ),
+    ),
+    compound_lines=['bgp_peer_inupdateelapsedtime'],
+)
+
+# juniper prefixes
+graph_bgp_peer_juniper_prefixes = graphs.Bidirectional(
+    name='bgp_peer.juniper_prefixes',
+    title=Title("Prefixes in/out"),
+    lower=graphs.Graph(
+        name='bgp_peer.juniper_prefixes_lower',
+        title=Title("Prefixes in/out"),
+        simple_lines=['bgp_peer_out_prefixes'],
+    ),
+    upper=graphs.Graph(
+        name='bgp_peer.juniper_prefixes_upper',
+        title=Title("Prefixes in/out"),
+        simple_lines=[
+            'bgp_peer_in_prefixes_rejected',
+            'bgp_peer_in_prefixes_active',
+            'bgp_peer_in_prefixes',
+        ],
+    ),
+)
+
+# huawei prefixes
+graph_huawei_bgp_peer_counter = graphs.Graph(
+    name='huawei_bgp_peer_counter',
+    title=Title("BGP prefix counter"),
+    simple_lines=[
+        'bgp_peer_prefixrcvcounter',
+        'bgp_peer_prefixactivecounter',
+        'bgp_peer_prefixadvcounter',
+    ],
+)
+
+
+perfometer_bgp_peer_accepted_prefixes = perfometers.Stacked(
+    name='bgp_peer_stacked',
+    upper=perfometers.Perfometer(
+        name='bgp_peer_fsmestablishedtime',
+        segments=['bgp_peer_fsmestablishedtime'],
+        focus_range=perfometers.FocusRange(perfometers.Closed(0), perfometers.Open(2592000)) # ome month
+    ),
+    lower=perfometers.Perfometer(
+        name='bgp_peer_acceptedprefixes',
+        segments=['bgp_peer_acceptedprefixes'],
+        focus_range=perfometers.FocusRange(perfometers.Closed(0), perfometers.Open(500000))
+    )
+)
+
+perfometer_bgp_peer_fsm_established_time = perfometers.Perfometer(
+    name='bgp_peer_fsmestablishedtime',
+    segments=['bgp_peer_fsmestablishedtime'],
+    focus_range=perfometers.FocusRange(perfometers.Closed(0), perfometers.Open(2592000)) # ome month
+)
\ No newline at end of file
diff --git a/source/agent_based/utils/bgp_peer.py b/source/cmk_addons_plugins/bgp_peer/lib/bgp_peer.py
similarity index 100%
rename from source/agent_based/utils/bgp_peer.py
rename to source/cmk_addons_plugins/bgp_peer/lib/bgp_peer.py
diff --git a/source/cmk_addons_plugins/bgp_peer/rulesets/bgp_peer.py b/source/cmk_addons_plugins/bgp_peer/rulesets/bgp_peer.py
new file mode 100644
index 0000000..fe1df4b
--- /dev/null
+++ b/source/cmk_addons_plugins/bgp_peer/rulesets/bgp_peer.py
@@ -0,0 +1,336 @@
+#!/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-25
+#
+# Check_MK bgp_peers WATO plugin
+#
+# 2021-03-27: rewrite for CMK 2.0
+# 2021-08-21: modified for bgp_peer plugin (from cisco_bgp_peer)
+# 2021-08-29: removed htmloutput and infotext_values option
+# 2022-04-02: added bgp neighbour states
+# 2022-04-29: added upper/lower prefix limit
+# 2022-05-09: added discovery rule set
+# 2022-05-11: added remote_as to build_item
+# 2022-09-05: added internal_item to avoid warnings on cmk updates (THX to Jay2k1 for reporting the issue)
+# 2023-06-11: moved wato file from ~local/lib/check_mk/gui/plugins/wato
+#             to ~/local/lib/check_mk/gui/plugins/wato/check_parameters to override the build in wato plugin
+# 2024-06-15: fixed typo (no not)
+#
+# Known issue: the override of the build in wato plugin will break the build in aritsa bgp peer plugin
+from click import help_option
+from cmk.rulesets.v1 import Help, Label, Title
+from cmk.rulesets.v1.form_specs import (
+    DefaultValue,
+    DictElement,
+    Dictionary,
+    InputHint,
+    Integer,
+    LevelDirection,
+    LevelsType,
+    List,
+    MultipleChoice,
+    MultipleChoiceElement,
+    ServiceState,
+    SimpleLevels,
+    String,
+    TimeMagnitude,
+    TimeSpan,
+    migrate_to_integer_simple_levels,
+)
+from cmk.rulesets.v1.rule_specs import CheckParameters, DiscoveryParameters, Topic, HostAndItemCondition
+from cmk.rulesets.v1.form_specs.validators import NumberInRange, Message, LengthInRange
+
+item_parts = [
+    # MultipleChoiceElement(name='remote_address', title=Title('Peer remote address')),
+    MultipleChoiceElement(name='remote_as', title=Title('Remote AS')),
+    MultipleChoiceElement(name='address_family', title=Title('Address family')),
+    MultipleChoiceElement(name='routing_instance', title=Title('Routing instance/VRF')),
+]
+
+def _migrate_to_float(value: object) -> float:
+    if isinstance(value, int | float):
+        return float(value)
+
+    raise TypeError(value)
+
+def _migrate_bgp_peer_list(value: object) -> list:
+    if not isinstance(value, list):
+        raise TypeError(value)
+
+    peer_list = []
+
+    for entry in value:
+        if isinstance(entry, tuple):
+            bgp_peer, bgp_peer_alias, state_not_found = entry
+            peer_list.append({
+                'bgp_peer': bgp_peer,
+                'bgp_peer_alias': bgp_peer_alias,
+                'state_not_found': state_not_found
+            })
+        else:
+            peer_list.append(entry)
+
+    return peer_list
+
+
+def _migrate_neighbor_state(value: object) -> dict:
+    if not isinstance(value, dict):
+        raise TypeError(value)
+
+    state_map = {}
+
+    for state, value in value.items():
+        match state:
+            case '1':
+                state_map['idle'] = value
+            case '2':
+                state_map['connect'] = value
+            case '3':
+                state_map['active'] = value
+            case '4':
+                state_map['open_sent'] = value
+            case '5':
+                state_map['open_confirm'] = value
+            case '6':
+                state_map['established'] = value
+            case _:
+                state_map[state] = value
+
+    return state_map
+
+def _parameter_form_check_bgp_peer():
+    return Dictionary(
+        elements={
+            'minuptime': DictElement(
+                parameter_form=SimpleLevels(
+                    title=Title('Minimum uptime for peer'),
+                    help_text=Help('Set the time in seconds, a peer must be up before the peer is considered sable.'),
+                    prefill_levels_type=DefaultValue(LevelsType.FIXED),
+                    prefill_fixed_levels=InputHint((7200, 3600)),
+                    level_direction=LevelDirection.LOWER,
+                    migrate=migrate_to_integer_simple_levels,
+                    form_spec_template=TimeSpan(
+                        displayed_magnitudes=[TimeMagnitude.SECOND, TimeMagnitude.MINUTE, TimeMagnitude.HOUR],
+                        custom_validate=(NumberInRange(min_value=0),),
+                        migrate=_migrate_to_float,
+                    ))),
+            'accepted_prefixes_upper_levels': DictElement(
+                parameter_form=SimpleLevels(
+                    title=Title('Accepted prefixes upper levels'),
+                    help_text=Help('The values from WATO are preferred to the values from the device.'),
+                    prefill_levels_type=DefaultValue(LevelsType.FIXED),
+                    prefill_fixed_levels=InputHint((0, 0)),
+                    level_direction=LevelDirection.UPPER,
+                    migrate=migrate_to_integer_simple_levels,
+                    form_spec_template=Integer(
+                        unit_symbol='prefixes',
+                        custom_validate=(NumberInRange(min_value=0, ),)
+                    ))),
+            'accepted_prefixes_lower_levels': DictElement(
+                parameter_form=SimpleLevels(
+                    title=Title('Accepted prefixes lower levels'),
+                    help_text=Help('The values from WATO are preferred to the values from the device.'),
+                    prefill_levels_type=DefaultValue(LevelsType.FIXED),
+                    prefill_fixed_levels=InputHint((0, 0)),
+                    level_direction=LevelDirection.LOWER,
+                    migrate=migrate_to_integer_simple_levels,
+                    form_spec_template=Integer(
+                        unit_symbol='prefixes',
+                        custom_validate=(NumberInRange(min_value=0, ),),
+                    ))),
+            'peernotfound': DictElement(
+                parameter_form=ServiceState(
+                    title=Title('State if peer is not found.'),
+                    help_text=Help('Default monitoring state if the peer is not found in the SNMP data'),
+                    prefill=DefaultValue(2)
+                )),
+            'admindown': DictElement(
+                parameter_form=ServiceState(
+                    prefill=DefaultValue(1),
+                    title=Title('State if peer is admin shutdown.'),
+                    help_text=Help('Monitoring state if the peer is admin shutdown')
+                )),
+
+            'neighborstate': DictElement(
+                parameter_form=Dictionary(
+                    title=Title('State to report for BGP neighbor state'),
+                    help_text=Help('Map each BGP state to a CheckMK monitoring state'),
+                    migrate=_migrate_neighbor_state,
+                    elements={
+                        'idle': DictElement(
+                            parameter_form=ServiceState(
+                                title=Title('1 - idle'),
+                                help_text=Help(
+                                    'This is the first stage of the BGP FSM. BGP detects a start event, tries to initiate a '
+                                    'TCP connection to the BGP peer, and also listens for a new connect from a peer router. '
+                                    'If an error causes BGP to go back to the Idle state for a second time, the '
+                                    'ConnectRetryTimer is set to 60 seconds and must decrement to zero before the connection '
+                                    'is initiated again. Further failures to leave the Idle state result in the '
+                                    'ConnectRetryTimer doubling in length from the previous time. '
+                                    'Default monitoring state is "CRIT"'
+                                ),
+                                prefill=DefaultValue(2)
+                            )),
+                        'connect': DictElement(
+                            parameter_form=ServiceState(
+                                title=Title('2 - connect'),
+                                help_text=Help(
+                                    'In this state, BGP initiates the TCP connection. If the 3-way TCP handshake completes, '
+                                    'the established BGP Session BGP process resets the ConnectRetryTimer and sends the Open '
+                                    'message to the neighbor, and then changes to the OpenSent State.'
+                                    'Default monitoring state is "WARN"'),
+                                prefill=DefaultValue(1),
+                            )),
+                        'active': DictElement(
+                            parameter_form=ServiceState(
+                                title=Title('3 - active'),
+                                help_text=Help(
+                                    'In this state, BGP starts a new 3-way TCP handshake. If a connection is established, '
+                                    'an Open message is sent, the Hold Timer is set to 4 minutes, and the state moves to '
+                                    'OpenSent. If this attempt for TCP connection fails, the state moves back to the Connect '
+                                    'state and resets the ConnectRetryTimer. '
+                                    'Default monitoring state is "WARN"'),
+                                prefill=DefaultValue(1),
+                            )),
+                        'open_sent': DictElement(
+                            parameter_form=ServiceState(
+                                title=Title('4 - opensent'),
+                                help_text=Help(
+                                    'In this state, an Open message has been sent from the originating router and is awaiting '
+                                    'an Open message from the other router. After the originating router receives the OPEN '
+                                    'message from the other router, both OPEN messages are checked for errors. If the Open '
+                                    'messages do not have any errors, the Hold Time is negotiated (using the lower value), '
+                                    'and a KEEPALIVE message is sent (assuming the value is not set to zero). The connection '
+                                    'state is then moved to OpenConfirm. If an error is found in the OPEN message, a '
+                                    'Notification message is sent, and the state is moved back to Idle.'
+                                    ' Default monitoring state is "WARN"'),
+                                prefill=DefaultValue(1),
+                            )),
+                        'open_confirm': DictElement(
+                            parameter_form=ServiceState(
+                                title=Title('5 - openconfirm'),
+                                help_text=Help(
+                                    'In this state, BGP waits for a Keepalive or Notification message. Upon receipt of a '
+                                    'neighbor’s Keepalive, the state is moved to Established. If the hold timer expires, a '
+                                    'stop event occurs, or a Notification message is received, and the state is moved to '
+                                    'Idle. '
+                                    'Default monitoring state is "WARN"'),
+                                prefill=DefaultValue(1),
+                            )),
+                        'established': DictElement(
+                            parameter_form=ServiceState(
+                                title=Title('6 - established'),
+                                help_text=Help(
+                                    'In this state, the BGP session is established. BGP neighbors exchange routes via Update '
+                                    'messages. As Update and Keepalive messages are received, the Hold Timer is reset. If the '
+                                    'Hold Timer expires, an error is detected and BGP moves the neighbor back to the Idle '
+                                    'state. '
+                                    'Default monitoring state is "OK"'),
+                                prefill=DefaultValue(0),
+                            )),
+                    }
+                )),
+
+            'noprefixlimit': DictElement(
+                parameter_form=ServiceState(
+                    title=Title('State if no admin prefix limit/warn threshold is configured.'),
+                    help_text=Help(
+                        'The admin prefix limit and warn threshold needs to be configured on the device. '
+                        'For example: "neighbor 172.17.10.10 maximum-prefix 10000 80". The threshold is in percentage '
+                        'of the prefix limit.'
+                    ),
+                    prefill=DefaultValue(1),
+                )),
+            'peer_list': DictElement(
+                parameter_form=List(
+                    title=Title('BGP Peers'),
+                    add_element_label=Label('Add BGP peer'),
+                    remove_element_label=Label('remove BGP peer'),
+                    no_element_label=Label('Add at least one BGP peer'),
+                    migrate=_migrate_bgp_peer_list,
+                    element_template=Dictionary(
+                        elements={
+                            'bgp_peer': DictElement(
+                                required=True,
+                                parameter_form=String(
+                                    title=Title('BGP peer'),
+                                    help_text=Help(
+                                        'The configured value must match a BGP item reported by the monitored '
+                                        'device. For example: "10.194.115.98" or "2A10:1CD0:1020:135::20 IPv6 Unicast"'
+                                    ),
+                                    custom_validate=(LengthInRange(min_value=1, ),),
+                                )),
+                            'bgp_peer_alias': DictElement(
+                                parameter_form=String(
+                                    title=Title('BGP Peer Alias'),
+                                    help_text=Help(
+                                        'You can configure an individual alias here for the BGP peer matching '
+                                        'the text configured in the "BGP Peer IP-address" field. The alias will '
+                                        'be shown in the check info'
+                                    ),
+                                    custom_validate=(LengthInRange(min_value=1, ),),
+                                )),
+                            'state_not_found': DictElement(
+                                required=True,
+                                parameter_form=ServiceState(
+                                    title=Title('State if not found'),
+                                    help_text=Help(
+                                        'You can configure an individual state if the BGP peer matching the text '
+                                        'configured in the "BGP Peer IP-address" field is not found'
+                                    ),
+                                    prefill=DefaultValue(2),
+                                )),
+                        }
+                    )
+                )),
+            'internal_item': DictElement(
+                render_only=True,
+                parameter_form=MultipleChoice(
+                    title=Title('Information not to use in the item name'),
+                    elements=item_parts,
+                )),
+        }
+    )
+
+
+rule_spec_check_bgp_peer = CheckParameters(
+    name='bgp_peer',
+    parameter_form=_parameter_form_check_bgp_peer,
+    title=Title('BGP Peer'),
+    topic=Topic.NETWORKING,
+    condition=HostAndItemCondition(item_title=Title('BGP peer')),
+)
+
+
+def _parameter_form_discovery_bgp_peer():
+    return Dictionary(
+        elements={
+            'build_item': DictElement(
+                parameter_form=MultipleChoice(
+                    title=Title('Information not to use in the item name'),
+                    help_text=Help(
+                        'The Peer remote address is always used as the item name. By default the check will add the '
+                        'address-family and the routing instance/VRF if available. You can decide to not use these '
+                        'additional information in the item name. Do so only if your peers have only one address-'
+                        'family configured and you don\'t have the same peer remote address in different routing '
+                        'instances/VRFs configured.'
+                    ),
+                    elements=item_parts,
+                    prefill=DefaultValue(['remote_as'])
+                )
+            )
+        }
+    )
+
+
+rule_spec_discovery_bgp_beer = DiscoveryParameters(
+    title=Title('BGP peer'),
+    topic=Topic.NETWORKING,
+    name='discovery_bgp_peer',
+    parameter_form=_parameter_form_discovery_bgp_peer,
+)
diff --git a/source/cmk_addons_plugins/bgp_peer/rulesets/inv_bgp_peer.py b/source/cmk_addons_plugins/bgp_peer/rulesets/inv_bgp_peer.py
new file mode 100644
index 0000000..f1d30bc
--- /dev/null
+++ b/source/cmk_addons_plugins/bgp_peer/rulesets/inv_bgp_peer.py
@@ -0,0 +1,124 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+#
+# Author: thl-cmk[at]outlook[dot]com
+# URL   : https://thl-cmk.hopto.org
+# Date  : 2022-04-24
+#
+# 2022-04-24: added option for BGP down time
+#             added option to remove some columns from inventory
+# 2022-04-28: added Whois options
+# 2023-06-12: moved wato file from ~local/lib/check_mk/gui/plugins/wato
+#             to ~/local/lib/check_mk/gui/plugins/wato/check_parameters
+# 2025-03-29: rewritten for rulesets API v1
+
+from cmk.rulesets.v1 import Title, Help
+from cmk.rulesets.v1.form_specs import (
+    DefaultValue,
+    DictElement,
+    Dictionary,
+    Integer,
+    MultipleChoice,
+    MultipleChoiceElement,
+    SingleChoice,
+    SingleChoiceElement,
+    TimeMagnitude,
+    TimeSpan,
+)
+from cmk.rulesets.v1.rule_specs import InventoryParameters, Topic
+from cmk.rulesets.v1.form_specs.validators import NumberInRange, Message
+
+def _migrate_to_float(value: object) -> float:
+    if isinstance(value, int | float):
+        return float(value)
+
+    raise TypeError(value)
+
+
+def _parameter_form_inv_bgp_peer():
+    remove_columns = [
+        # MultipleChoiceElement(name='remote_as', title=Title('Remote AS')),
+        # MultipleChoiceElement(name='remote_id', title=Title('Remote ID')),
+        # MultipleChoiceElement(name='local_addr', title=Title('Local address')),
+        # MultipleChoiceElement(name='local_as', title=Title('Local AS')),
+        # MultipleChoiceElement(name='local_id', title=Title('Local ID')),
+        # MultipleChoiceElement(name='prev_state', title=Title('Previous state')),
+        MultipleChoiceElement(name='address_family', title=Title('Address family')),
+        MultipleChoiceElement(name='last_error', title=Title('Last error')),
+        MultipleChoiceElement(name='last_error_code', title=Title('Last error code')),
+        MultipleChoiceElement(name='as_name', title=Title('Remote AS name')),
+        MultipleChoiceElement(name='as_org_name', title=Title('Remote AS Org Name')),
+        MultipleChoiceElement(name='bgp_type', title=Title('Type')),
+        MultipleChoiceElement(name='version', title=Title('Version')),
+    ]
+
+    rir_s = [
+        SingleChoiceElement(name='afrinic', title=Title('AFRINIC (https://rdap.afrinic.net/rdap)')),
+        SingleChoiceElement(name='apnic', title=Title('APNIC (https://rdap.apnic.net)')),
+        SingleChoiceElement(name='arin', title=Title('ARIN (https://rdap.arin.net/registry)')),
+        SingleChoiceElement(name='ripe', title=Title('RIPE (https://rdap.db.ripe.net)')),
+        SingleChoiceElement(name='lacnic', title=Title('LACNIC (https://rdap.apnic.net)')),
+    ]
+
+    return Dictionary(
+        elements={
+            'not_in_service_time': DictElement(
+                parameter_form=TimeSpan(
+                    title=Title('Time peer is not up until considered not in service'),
+                    displayed_magnitudes=[TimeMagnitude.MINUTE, TimeMagnitude.HOUR, TimeMagnitude.DAY],
+                    prefill=DefaultValue(2592000.0),  # 30 days in seconds
+                    migrate=_migrate_to_float,
+                )),
+            'remove_columns': DictElement(
+                parameter_form=MultipleChoice(
+                    title=Title('List of columns to remove'),
+                    help_text=Help('Information to remove from inventory'),
+                    elements=remove_columns,
+                )),
+            'whois_enable': DictElement(
+                parameter_form=Dictionary(
+                    title=Title('Add whois data to the inventory'),
+                    help_text=Help(
+                        'The whois data will be fetched via RDAP from the registries. For this the the plugin tries to'
+                        'find the best registry via the RDAP bootstrap data from https://data.iana.org/rdap/asn.json.'
+                        'The query it self will go to the found registry via http(s). Note: the request might be get '
+                        'redirected if there a different authoritative registry for the ASn'
+                    ),
+                    elements={
+                        'whois_rir': DictElement(
+                            parameter_form=SingleChoice(
+                                title=Title('Preferred RIR to fetch whois data'),
+                                help_text=Help(
+                                    'This registry will be used if the plugin can not determine the authoritative registry '
+                                    'based on the bootstrap data.'
+                                ),
+                                elements=rir_s,
+                                prefill=DefaultValue('ripe'),
+                            )),
+                        'whois_timeout': DictElement(
+                            parameter_form=Integer(
+                                custom_validate=(
+                                    NumberInRange(
+                                        min_value=1,
+                                        max_value=None,
+                                        error_msg=Message('The timeout must be greater than 0')
+                                    ),
+                                ),
+                                help_text=Help('The connection timeout for each whois request.'),
+                                prefill=DefaultValue(5),
+                                title=Title('Timeout for connections to RIRs'),
+                                unit_symbol='seconds',
+                            )),
+                    }
+                )
+            )
+        }
+    )
+
+
+rule_spec_inv_bgp_peer = InventoryParameters(
+    name='inv_bgp_peer',
+    parameter_form=_parameter_form_inv_bgp_peer,
+    title=Title('BGP peer'),
+    topic=Topic.NETWORKING,
+)
diff --git a/source/gui/metrics/bgp_peer.py b/source/gui/metrics/bgp_peer.py
deleted file mode 100644
index a6f8bcf..0000000
--- a/source/gui/metrics/bgp_peer.py
+++ /dev/null
@@ -1,253 +0,0 @@
-#!/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-08-21
-#
-# BGP Peer metrics plugin
-#
-# 2024-05-17: fixed typo in range mac -> max (crashed graphs in cmk 2.3.)
-#
-from cmk.gui.i18n import _
-
-from cmk.gui.plugins.metrics.utils import (
-    metric_info,
-    graph_info,
-    perfometer_info
-)
-
-#####################################################################################################################
-#
-# define metrics for bgp peer perfdata
-#
-#####################################################################################################################
-
-metric_info['bgp_peer_inupdates'] = {
-    'title': _('Updates received'),
-    'unit': '1/s',
-    'color': '22/a',
-}
-metric_info['bgp_peer_outupdates'] = {
-    'title': _('Updates send'),
-    'unit': '1/s',
-    'color': '32/a',
-}
-metric_info['bgp_peer_intotalmessages'] = {
-    'title': _('Messages received'),
-    'unit': '1/s',
-    'color': '42/a',
-}
-metric_info['bgp_peer_outtotalmessages'] = {
-    'title': _('Messages send'),
-    'unit': '1/s',
-    'color': '13/a',
-}
-metric_info['bgp_peer_fsmestablishedtransitions'] = {
-    'title': _('FSM transitions'),
-    'unit': 'count',
-    'color': '23/a',
-}
-metric_info['bgp_peer_fsmestablishedtime'] = {
-    'title': _('FSM last change'),
-    'unit': 's',
-    'color': '26/a',
-}
-metric_info['bgp_peer_inupdateelapsedtime'] = {
-    'title': _('Last update received'),
-    'unit': 's',
-    'color': '43/a',
-}
-
-metric_info['bgp_peer_acceptedprefixes'] = {
-    'title': _('Prefixes accepted'),
-    'help': _('number of accepted prefixes'),
-    'unit': 'count',
-    'color': '11/a',
-}
-metric_info['bgp_peer_deniedprefixes'] = {
-    'title': _('Prefixes denied'),
-    'unit': '1/s',
-    'color': '21/a',
-}
-metric_info['bgp_peer_advertisedprefixes'] = {
-    'title': _('Prefixes advertised'),
-    'unit': '1/s',
-    'color': '31/a',
-}
-metric_info['bgp_peer_withdrawnprefixes'] = {
-    'title': _('Prefixes withdrawn'),
-    'unit': '1/s',
-    'color': '41/a',
-}
-metric_info['bgp_peer_suppressedprefixes'] = {
-    'title': _('Prefixes suppressed'),
-    'unit': '1/s',
-    'color': '12/a',
-}
-
-# Juniper specific metrics
-metric_info['bgp_peer_in_prefixes'] = {
-    'title': _('Prefixes in'),
-    'unit': 'count',
-    'color': '11/a',
-}
-metric_info['bgp_peer_in_prefixes_rejected'] = {
-    'title': _('Prefixes in rejected'),
-    'unit': 'count',
-    'color': '21/a',
-}
-metric_info['bgp_peer_in_prefixes_active'] = {
-    'title': _('Prefixes in active'),
-    'unit': 'count',
-    'color': '31/a',
-}
-metric_info['bgp_peer_out_prefixes'] = {
-    'title': _('Prefixes out'),
-    'unit': 'count',
-    'color': '41/a',
-}
-
-# Hawei spezific metrics
-metric_info['bgp_peer_prefixrcvcounter'] = {
-    'title': _('Prefixes received'),
-    'unit': 'count',
-    'color': '11/a',
-}
-metric_info['bgp_peer_prefixactivecounter'] = {
-    'title': _('Prefixes active'),
-    'unit': 'count',
-    'color': '33/a',
-}
-metric_info['bgp_peer_prefixadvcounter'] = {
-    'title': _('Prefixes advertised'),
-    'unit': 'count',
-    'color': '43/a',
-}
-
-######################################################################################################################
-#
-# how to graph perdata for bgp peer
-#
-######################################################################################################################
-
-graph_info['bgp_peer.fms_transitions_last_change'] = {
-    'title': _('FSM established last change'),
-    'metrics': [
-        ('bgp_peer_fsmestablishedtime', 'area'),
-    ],
-    'range': (0, 'bgp_peer_fsmestablishedtime:max'),
-}
-
-graph_info['bgp_peer.prefixes_accepted'] = {
-    'title': _('Accepted Prefixes'),
-    'metrics': [
-        ('bgp_peer_acceptedprefixes', 'area'),
-    ],
-    'scalars': [
-        ('bgp_peer_acceptedprefixes:crit', _('crit')),
-        ('bgp_peer_acceptedprefixes:warn', _('warn')),
-    ],
-    'range': (0, 'bgp_peer_acceptedprefixes:max'),
-}
-
-graph_info['bgp_peer.prefixes_per_second'] = {
-    'title': _('Prefixes/s'),
-    'metrics': [
-        ('bgp_peer_withdrawnprefixes', 'line'),
-        ('bgp_peer_suppressedprefixes', 'line'),
-        ('bgp_peer_deniedprefixes', 'line'),
-        ('bgp_peer_advertisedprefixes', 'line'),
-    ],
-    'optional_metrics': [
-        'bgp_peer_withdrawnprefixes',
-        'bgp_peer_suppressedprefixes',
-        'bgp_peer_deniedprefixes',
-        'bgp_peer_advertisedprefixes',
-    ],
-}
-
-graph_info['bgp_peer.updates_in_out'] = {
-    'title': _('Updates'),
-    'metrics': [
-        ('bgp_peer_outupdates', '-area'),
-        ('bgp_peer_inupdates', 'area'),
-    ]
-}
-
-graph_info['bgp_peer.messages_in_out'] = {
-    'title': _('Total messages'),
-    'metrics': [
-        ('bgp_peer_outtotalmessages', '-area'),
-        ('bgp_peer_intotalmessages', 'area'),
-    ]
-}
-
-graph_info['bgp_peer.fms_transitions_from_to'] = {
-    'title': _('FSM transitions from/to established'),
-    'metrics': [
-        ('bgp_peer_fsmestablishedtransitions', 'area'),
-    ],
-    'range': (0, 'bgp_peer_fsmestablishedtransitions:max'),
-}
-
-
-graph_info['bgp_peer.time_since_last_update'] = {
-    'title': _('Time since last update received'),
-    'metrics': [
-        ('bgp_peer_inupdateelapsedtime', 'area'),
-    ],
-    'range': (0, 'bgp_peer_inupdateelapsedtime:max'),
-}
-
-# juniper prefixes
-graph_info['bgp_peer.juniper_prefixes'] = {
-    'title': _('Prefixes in/out'),
-    'metrics': [
-        ('bgp_peer_out_prefixes', '-line'),
-        ('bgp_peer_in_prefixes_rejected', 'line'),
-        ('bgp_peer_in_prefixes_active', 'line'),
-        ('bgp_peer_in_prefixes', 'line'),
-    ],
-}
-
-# huawei prefixes
-graph_info['huawei_bgp_peer_counter'] = {
-    'title': _('BGP prefix counter'),
-    'metrics': [
-        ('bgp_peer_prefixrcvcounter', 'line'),
-        ('bgp_peer_prefixactivecounter', 'line'),
-        ('bgp_peer_prefixadvcounter', 'line'),
-    ]
-}
-
-
-######################################################################################################################
-#
-# define perf-o-meter for bgp peer uptime + prefixes accepted/advertised
-#
-######################################################################################################################
-
-perfometer_info.append(('stacked', [
-    {
-        'type': 'logarithmic',
-        'metric': 'bgp_peer_fsmestablishedtime',
-        'half_value': 2592000.0,  # ome month
-        'exponent': 2,
-    },
-    {
-        'type': 'logarithmic',
-        'metric': 'bgp_peer_acceptedprefixes',
-        'half_value': 500000.0,
-        'exponent': 2,
-    }
-]))
-
-perfometer_info.append({
-    'type': 'logarithmic',
-    'metric': 'bgp_peer_fsmestablishedtime',
-    'half_value': 2592000.0,  # ome month
-    'exponent': 2,
-})
diff --git a/source/gui/wato/check_parameters/bgp_peer.py b/source/gui/wato/check_parameters/bgp_peer.py
index 32744b0..812659d 100644
--- a/source/gui/wato/check_parameters/bgp_peer.py
+++ b/source/gui/wato/check_parameters/bgp_peer.py
@@ -1,256 +1,4 @@
 #!/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-25
-#
-# Check_MK bgp_peers WATO plugin
-#
-# 2021-03-27: rewrite for CMK 2.0
-# 2021-08-21: modified for bgp_peer plugin (from cisco_bgp_peer)
-# 2021-08-29: removed htmloutput and infotext_values option
-# 2022-04-02: added bgp neighbour states
-# 2022-04-29: added upper/lower prefix limit
-# 2022-05-09: added discovery rule set
-# 2022-05-11: added remote_as to build_item
-# 2022-09-05: added internal_item to avoid warnings on cmk updates (THX to Jay2k1 for reporting the issue)
-# 2023-06-11: moved wato file from ~local/lib/check_mk/gui/plugins/wato
-#             to ~/local/lib/check_mk/gui/plugins/wato/check_parameters to override the build in wato plugin
-# 2024-06-15: fixed typo (no not)
-#
-# Known issue: the override of the build in wato plugin will break the build in aritsa bgp peer plugin
 
-from cmk.gui.i18n import _
-from cmk.gui.valuespec import (
-    Dictionary,
-    Integer,
-    TextAscii,
-    ListOf,
-    Tuple,
-    TextUnicode,
-    MonitoringState,
-    ListChoice,
-    TextInput,
-)
-
-from cmk.gui.plugins.wato.utils import (
-    CheckParameterRulespecWithItem,
-    rulespec_registry,
-    RulespecGroupCheckParametersNetworking,
-    HostRulespec,
-    RulespecGroupCheckParametersDiscovery,
-)
-
-
-def _parameter_valuespec_bgp_peer():
-    return Dictionary(
-        elements=[
-            ('minuptime',
-             Tuple(
-                 title=_('Minimum uptime for peer'),
-                 orientation='horizontal',
-                 help=_('Set the time in seconds, a peer must be up before the peer is considered sable.'),
-                 elements=[
-                     Integer(title=_('Warning below'), unit='seconds', default_value=7200, minvalue=0),
-                     Integer(title=_('Critical below'), unit='seconds', default_value=3600, minvalue=0)
-                 ],
-             )),
-            ('accepted_prefixes_upper_levels',
-             Tuple(
-                 title=_('Accepted prefixes upper levels'),
-                 help=_('The values from WATO are preferred to the values from the device.'),
-                 orientation='horizontal',
-                 elements=[
-                     Integer(title=_('Warning at'), minvalue=0, unit=_('prefixes'), size=5),
-                     Integer(title=_('Critical at'), minvalue=0, unit=_('prefixes'), size=5),
-                 ],
-             )),
-            ('accepted_prefixes_lower_levels',
-             Tuple(
-                 title=_('Accepted prefixes lower levels'),
-                 orientation='horizontal',
-                 elements=[
-                     Integer(title=_('Warning below'), minvalue=0, unit=_('prefixes'), size=5),
-                     Integer(title=_('Critical below'), minvalue=0, unit=_('prefixes'), size=5),
-                 ],
-             )),
-            ('peernotfound',
-             MonitoringState(
-                 default_value=2,
-                 title=_('State if peer is not found.'),
-                 help=_('Default monitoring state if the peer is not found in the SNMP data')
-             )),
-            ('admindown',
-             MonitoringState(
-                 default_value=1,
-                 title=_('State if peer is admin shutdown.'),
-                 help=_('Monitoring state if the peer is admin shutdown')
-             )),
-            ('neighborstate',
-             Dictionary(
-                 title=_('State to report for BGP neighbor state'),
-                 help=_('Map each BGP state to a CheckMK monitoring state'),
-                 elements=[
-                     ('1',
-                      MonitoringState(
-                          title=_('1 - idle'),
-                          help=_(
-                              'This is the first stage of the BGP FSM. BGP detects a start event, tries to initiate a '
-                              'TCP connection to the BGP peer, and also listens for a new connect from a peer router. '
-                              'If an error causes BGP to go back to the Idle state for a second time, the '
-                              'ConnectRetryTimer is set to 60 seconds and must decrement to zero before the connection '
-                              'is initiated again. Further failures to leave the Idle state result in the '
-                              'ConnectRetryTimer doubling in length from the previous time. '
-                              'Default monitoring state is "CRIT"'),
-                          default_value=2,
-                      )),
-                     ('2',
-                      MonitoringState(
-                          title=_('2 - connect'),
-                          help=_(
-                              'In this state, BGP initiates the TCP connection. If the 3-way TCP handshake completes, '
-                              'the established BGP Session BGP process resets the ConnectRetryTimer and sends the Open '
-                              'message to the neighbor, and then changes to the OpenSent State.'
-                              'Default monitoring state is "WARN"'),
-                          default_value=1,
-                      )),
-                     ('3',
-                      MonitoringState(
-                          title=_('3 - active'),
-                          help=_('In this state, BGP starts a new 3-way TCP handshake. If a connection is established, '
-                                 'an Open message is sent, the Hold Timer is set to 4 minutes, and the state moves to '
-                                 'OpenSent. If this attempt for TCP connection fails, the state moves back to the Connect '
-                                 'state and resets the ConnectRetryTimer. '
-                                 'Default monitoring state is "WARN"'),
-                          default_value=1,
-                      )),
-                     ('4',
-                      MonitoringState(
-                          title=_('4 - opensent'),
-                          help=_(
-                              'In this state, an Open message has been sent from the originating router and is awaiting '
-                              'an Open message from the other router. After the originating router receives the OPEN '
-                              'message from the other router, both OPEN messages are checked for errors. If the Open '
-                              'messages do not have any errors, the Hold Time is negotiated (using the lower value), '
-                              'and a KEEPALIVE message is sent (assuming the value is not set to zero). The connection '
-                              'state is then moved to OpenConfirm. If an error is found in the OPEN message, a '
-                              'Notification message is sent, and the state is moved back to Idle.'
-                              ' Default monitoring state is "WARN"'),
-                          default_value=1,
-                      )),
-                     ('5',
-                      MonitoringState(
-                          title=_('5 - openconfirm'),
-                          help=_('In this state, BGP waits for a Keepalive or Notification message. Upon receipt of a '
-                                 'neighbor’s Keepalive, the state is moved to Established. If the hold timer expires, a '
-                                 'stop event occurs, or a Notification message is received, and the state is moved to '
-                                 'Idle. '
-                                 'Default monitoring state is "WARN"'),
-                          default_value=1,
-                      )),
-                     ('6',
-                      MonitoringState(
-                          title=_('6 - established'),
-                          help=_(
-                              'In this state, the BGP session is established. BGP neighbors exchange routes via Update '
-                              'messages. As Update and Keepalive messages are received, the Hold Timer is reset. If the '
-                              'Hold Timer expires, an error is detected and BGP moves the neighbor back to the Idle '
-                              'state. '
-                              'Default monitoring state is "OK"'),
-                          default_value=0,
-                      )),
-                 ])),
-            ('noprefixlimit',
-             MonitoringState(
-                 default_value=1,
-                 title=_('State if no admin prefix limit/warn threshold is configured.'),
-                 help=_('The admin prefix limit and warn threshold needs to be configured on the device. '
-                        'For example: "neighbor 172.17.10.10 maximum-prefix 10000 80". The threshold is in percentage '
-                        'of the prefix limit.')
-             )),
-            ('peer_list',
-             ListOf(
-                 Tuple(
-                     orientation='horizontal',
-                     elements=[
-                         TextUnicode(
-                             title=_('BGP Peer'),
-                             help=_('The configured value must match a BGP item reported by the monitored '
-                                    'device. For example: "10.194.115.98" or "2A10:1CD0:1020:135::20 IPv6 Unicast"'),
-                             allow_empty=False,
-                             size=50,
-                         ),
-                         TextUnicode(
-                             title=_('BGP Peer Alias'),
-                             help=_('You can configure an individual alias here for the BGP peer matching '
-                                    'the text configured in the "BGP Peer IP-address" field. The alias will '
-                                    'be shown in the check info'),
-                             size=50,
-                         ),
-                         MonitoringState(
-                             default_value=2,
-                             title=_('State if not found'),
-                             help=_('You can configure an individual state if the BGP peer matching the text '
-                                    'configured in the "BGP Peer IP-address" field is not found')
-                         ),
-                     ]),
-                 add_label=_('Add BGP peer'),
-                 movable=False,
-                 title=_('BGP Peers'),
-             )),
-            ('internal_item',  # added by plugin discovery function
-             TextUnicode()),
-        ],
-        hidden_keys=['internal_item'],
-    )
-
-
-rulespec_registry.register(
-    CheckParameterRulespecWithItem(
-        check_group_name='bgp_peer',
-        group=RulespecGroupCheckParametersNetworking,
-        item_spec=lambda: TextInput(title=_('BGP peer'), ),
-        match_type='dict',
-        parameter_valuespec=_parameter_valuespec_bgp_peer,
-        title=lambda: _('BGP peer'),
-    ))
-
-
-def _valuespec_discovery_bgp_peer():
-    item_parts = [
-        # ('remote_address', 'Peer remote address'),
-        ('remote_as', 'Remote AS'),
-        ('address_family', 'Address family'),
-        ('routing_instance', 'Routing instance/VRF'),
-
-    ]
-    return Dictionary(
-            title=_('BGP peer'),
-            elements=[
-                ('build_item',
-                 ListChoice(
-                     title=_('Information not to use in the item name'),
-                     help=_(
-                         'The Peer remote address is always used as the item name. By default the check will add the '
-                         'address-family and the routing instance/VRF if available. You can decide to not use these '
-                         'additional information in the item name. Do so only if your peers have only one address-'
-                         'family configured and you don\'t have the same peer remote address in different routing '
-                         'instances/VRFs configured.'
-                     ),
-                     choices=item_parts,
-                     default_value=['remote_as'],
-                 )),
-            ],
-        )
-
-
-rulespec_registry.register(
-    HostRulespec(
-        group=RulespecGroupCheckParametersDiscovery,
-        match_type='dict',
-        name='discovery_bgp_peer',
-        valuespec=_valuespec_discovery_bgp_peer,
-    ))
+# dummy to shadow build in bgp peer rule set
\ No newline at end of file
diff --git a/source/gui/wato/check_parameters/inv_bgp_peer.py b/source/gui/wato/check_parameters/inv_bgp_peer.py
deleted file mode 100644
index e728182..0000000
--- a/source/gui/wato/check_parameters/inv_bgp_peer.py
+++ /dev/null
@@ -1,109 +0,0 @@
-#!/usr/bin/env python3
-# -*- coding: utf-8 -*-
-#
-# Author: thl-cmk[at]outlook[dot]com
-# URL   : https://thl-cmk.hopto.org
-# Date  : 2022-04-24
-#
-# 2022-04-24: added option for BGP down time
-#             added option to remove some columns from inventory
-# 2022-04-28: added Whois options
-# 2023-06-12: moved wato file from ~local/lib/check_mk/gui/plugins/wato
-#             to ~/local/lib/check_mk/gui/plugins/wato/check_parameters
-
-from cmk.gui.i18n import _
-from cmk.gui.plugins.wato.utils import (
-    HostRulespec,
-    rulespec_registry,
-)
-from cmk.gui.valuespec import (
-    Dictionary,
-    ListChoice,
-    Age,
-    DropdownChoice,
-    Integer,
-)
-
-from cmk.gui.plugins.wato.inventory import (
-    RulespecGroupInventory,
-)
-
-
-def _valuespec_inv_bgp_peer():
-    removecolumns = [
-        # ('remote_as', 'Remote AS'),
-        # ('remote_id', 'Remote ID'),
-        # ('local_addr', 'Local address'),
-        # ('local_as', 'Local AS'),
-        # ('local_id', 'Local ID'),
-        ('address_family', 'Address family'),
-        ('last_error', 'Last error'),
-        ('last_error_code', 'Last error code'),
-        # ('prev_state', 'Previous state'),
-        ('as_name', 'Remote AS name'),
-        ('as_org_name', 'Remote AS Org Name'),
-        ('bgp_type', 'Type'),
-        ('version', 'Version'),
-    ]
-
-    return Dictionary(
-        title=_('BGP peer'),
-        elements=[
-            ('not_in_service_time',
-             Age(
-                 title=_('Time peer is not up until considered not in service'),
-                 default_value=2592000,  # 30 days in seconds,
-             )),
-            ('remove_columns',
-             ListChoice(
-                 title=_('List of columns to remove'),
-                 help=_('Information to remove from inventory'),
-                 choices=removecolumns,
-                 default_value=[],
-             )),
-            ('whois_enable',
-             Dictionary(
-                 title=_('Add whois data to the inventory'),
-                 help=_(
-                     'The whois data will be fetched via RDAP from the registries. For this the the plugin tries to'
-                     'find the best registry via the RDAP bootstrap data from https://data.iana.org/rdap/asn.json.'
-                     'The query it self will go to the found registry via http(s). Note: the request might be get '
-                     'redirected if there a different authoritative registry for the ASn'
-                 ),
-                 elements=[
-                     ('whois_rir',
-                      DropdownChoice(
-                          title='Preferred RIR to fetch whois data',
-                          help=_(
-                              'This registry will be used if the plugin can not determine the authoritative registry '
-                              'based on the bootstrap data.'
-                          ),
-                          choices=[
-                              ('afrinic', _('AFRINIC (https://rdap.afrinic.net/rdap)')),
-                              ('apnic', _('APNIC (https://rdap.apnic.net)')),
-                              ('arin', _('ARIN (https://rdap.arin.net/registry)')),
-                              ('ripe', _('RIPE (https://rdap.db.ripe.net)')),
-                              ('lacnic', _('LACNIC (https://rdap.apnic.net)')),
-                          ]
-                      )),
-                     ('whois_timeout',
-                      Integer(
-                          title='Timeout for connections to RIRs',
-                          help=_('The connection timeout for each whois request.'),
-                          default_value=5,
-                          minvalue=1,
-                          unit=_('seconds'),
-                      )),
-                 ]
-             )),
-        ],
-    )
-
-
-rulespec_registry.register(
-    HostRulespec(
-        group=RulespecGroupInventory,
-        match_type='dict',
-        name='inv_parameters:inv_bgp_peer',
-        valuespec=_valuespec_inv_bgp_peer,
-    ))
diff --git a/source/packages/bgp_peer b/source/packages/bgp_peer
index 0bc5758..a897999 100644
--- a/source/packages/bgp_peer
+++ b/source/packages/bgp_peer
@@ -27,17 +27,19 @@
                 'https://thl-cmk.hopto.org/gitlab/checkmk/juniper-networks/juniper_bgp_peer\n'
                 '\n',
  'download_url': 'https://thl-cmk.hopto.org/vendor-independent/bgp_peer',
- 'files': {'agent_based': ['bgp_peer.py',
-                           'inv_bgp_peer.py',
-                           'utils/bgp_peer.py'],
-           'checkman': ['bgp_peer'],
-           'gui': ['metrics/bgp_peer.py',
-                   'wato/check_parameters/bgp_peer.py',
-                   'wato/check_parameters/inv_bgp_peer.py'],
+ 'files': {'agent_based': ['bgp_peer.py'],
+           'cmk_addons_plugins': ['bgp_peer/agent_based/bgp_peer.py',
+                                  'bgp_peer/agent_based/inv_bgp_peer.py',
+                                  'bgp_peer/graphing/bgp_peer.py',
+                                  'bgp_peer/lib/bgp_peer.py',
+                                  'bgp_peer/rulesets/bgp_peer.py',
+                                  'bgp_peer/rulesets/inv_bgp_peer.py',
+                                  'bgp_peer/checkman/bgp_peer'],
+           'gui': ['wato/check_parameters/bgp_peer.py'],
            'web': ['plugins/views/inv_bgp_peer.py']},
  'name': 'bgp_peer',
  'title': 'BGP Peer',
- 'version': '2.2.8-20250117',
+ 'version': '2.3.0-20250329',
  'version.min_required': '2.3.0b1',
  'version.packaged': 'cmk-mkp-tool 0.2.0',
- 'version.usable_until': '2.4.0b1'}
+ 'version.usable_until': '2.5.0b1'}
-- 
GitLab