From 74fb3d030cd3d1c6c9f0064553b77abdabbaa551 Mon Sep 17 00:00:00 2001
From: "th.l" <thl-cmk@outlook.com>
Date: Fri, 30 Sep 2022 17:28:28 +0200
Subject: [PATCH] update project

---
 agent_based/cisco_cellular_lte.py           | 472 ++++++++++++++++++++
 agent_based/inv_cisco_cellular_lte.py       | 170 +++++++
 cisco_cellular_lte.mkp                      | Bin 49 -> 6832 bytes
 packages/cisco_cellular_lte                 |  16 +
 web/plugins/metrics/cisco_cellular_lte.py   |  92 ++++
 web/plugins/views/inv_cisco_cellular_lte.py |  49 ++
 web/plugins/wato/cisco_cellular_lte.py      | 127 ++++++
 7 files changed, 926 insertions(+)
 create mode 100644 agent_based/cisco_cellular_lte.py
 create mode 100644 agent_based/inv_cisco_cellular_lte.py
 create mode 100644 packages/cisco_cellular_lte
 create mode 100644 web/plugins/metrics/cisco_cellular_lte.py
 create mode 100644 web/plugins/views/inv_cisco_cellular_lte.py
 create mode 100644 web/plugins/wato/cisco_cellular_lte.py

diff --git a/agent_based/cisco_cellular_lte.py b/agent_based/cisco_cellular_lte.py
new file mode 100644
index 0000000..d4be481
--- /dev/null
+++ b/agent_based/cisco_cellular_lte.py
@@ -0,0 +1,472 @@
+#!/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  : 2022-09-17
+#
+# Monitor status of Cisco cellular modems/connections/interfaces
+#
+# 2022-09-20: added WATO options
+#
+# snmpwalk sample
+#
+
+#
+# sample info
+#
+
+#
+# sample section
+#
+
+# IMSI -> International Mobile Subscriber Identifier
+# IMEI -> International Mobile Equipment Identifier
+# ICCID -> Integrated Circuit Card ID
+# MSISDN -> Mobile Subscriber Integrated Services Digital Network Number
+# RSSI -> Received Signal Strength Indicator
+# RSRP -> Reference Signal Received Power
+# RSRQ -> Reference Signal Received Quality
+# SNR -> Signal to Noise Ratio
+#
+# for Values https://usatcorp.com/faqs/understanding-lte-signal-strength-values/
+#
+
+import time
+from dataclasses import dataclass
+from typing import Optional, Dict, List
+from cmk.base.plugins.agent_based.agent_based_api.v1.type_defs import (
+    DiscoveryResult,
+    CheckResult,
+    StringByteTable,
+)
+
+from cmk.base.plugins.agent_based.agent_based_api.v1 import (
+    register,
+    Service,
+    SNMPTree,
+    contains,
+    check_levels,
+    OIDEnd,
+    OIDBytes,
+    Result,
+    render,
+    State,
+    all_of,
+    exists,
+    get_rate,
+    GetRateError,
+    get_value_store,
+    IgnoreResultsError,
+)
+
+
+###########################################################################
+#
+#  DATA Parser function
+#
+###########################################################################
+
+@dataclass
+class CiscoCellular3g:
+    modem_state: str
+    service_type: str
+    roaming_status: str
+    current_system_time: str
+    connection_status: str
+    total_bytes_tx: int
+    total_bytes_rx: int
+    modem_status: str
+    network: str
+    channel: int
+    rssi: int
+    band: str
+
+
+@dataclass
+class CiscoCellularRadio:
+    lte_rsrp: int
+    lte_rsrq: int
+
+
+@dataclass
+class CiscoCellularProfile:
+    apn_type: str
+    apn: str
+    radio_index: str
+
+
+@dataclass
+class CiscoCellularInterface:
+    conn_status: str
+    pdn_type: str
+    ipv4_addr: str
+    ipv6_addr: str
+    profile: Optional[CiscoCellularProfile]
+    radio: Optional[CiscoCellularRadio]
+    modem: Optional[CiscoCellular3g]
+
+
+_cisco_gsm_modem_state = {
+    '1': 'Unknown',
+    '2': 'Up',
+    '3': 'Down',
+}
+
+_cisco_gsm_modem_status = {
+    '1': 'unknown',
+    '2': 'Offline',
+    '3': 'Online',
+    '4': 'low Power Mode',
+}
+
+_cisco_gsm_service_type = {
+    '0': '1xRTT',
+    '1': 'EVDO Revision 0',
+    '2': 'EVDO Revision A',
+    '3': 'EVDO Revision B',
+    '4': 'GPRS',
+    '5': 'EDGE',
+    '6': 'UMTS/WCDMA',
+    '7': 'HSDPA',
+    '8': 'HSUPA',
+    '9': 'HSPA',
+    '10': 'HSPA Plus',
+    '11': 'LTE TDD',
+    '12': 'LTE FDD',
+}
+
+_cisco_gsm_band = {
+    '1': 'unknown',
+    '2': 'invalid',
+    '3': 'none',
+    '4': 'GSM-850',
+    '5': 'GSM-900',
+    '6': 'GSM-1800',
+    '7': 'GSM-1900',
+    '8': 'WCDMA-800',
+    '9': 'WCDMA-850',
+    '10': 'WCDMA-1900',
+    '11': 'WCDMA-2100',
+    '12': 'LTE Band',
+}
+
+_cisco_roaming_status = {
+    '1': 'Unknown',
+    '2': 'Yes',
+    '3': 'No',
+}
+
+_cisco_gsm_connection_status = {
+    '1': 'unknown',
+    '2': 'error',
+    '3': 'connecting',
+    '4': 'dormant',
+    '5': 'connected',
+    '6': 'disconnected',
+    '7': 'idle',
+    '8': 'active',
+    '9': 'inactive',
+}
+
+_cisco_wan_cell_ext_protocol_type = {
+    '1': 'unknown',
+    '2': 'IPv4',
+    '3': 'PPP',
+    '4': 'IPv6',
+    '5': 'IPv4v6',
+}
+
+_cisco_gsm_pdp_status = {
+    '1': 'unknown',
+    '2': 'active',
+    '3': 'inactive',
+}
+
+
+def parse_cisco_cellular_lte(string_table: List[StringByteTable]) -> Optional[Dict[str, CiscoCellularInterface]]:
+    section = {}
+    radios = {}
+    profiles = {}
+    modems = {}
+    interfaces = {}
+    modem_table, radio_table, profile_table, pdn_table, interface_table = string_table
+
+    for radio_oid_end, current_rsrp, current_rsrq in radio_table:
+        radios[radio_oid_end] = CiscoCellularRadio(
+                lte_rsrp=int(current_rsrp),
+                lte_rsrq=int(current_rsrq) // 10,  # See Cisco bug ID CSCvt55347
+            )
+
+    for profile_oid_end, apn_type, apn in profile_table:
+        radio_index, profile_index = profile_oid_end.split('.')
+        profiles[profile_index] = CiscoCellularProfile(
+            radio_index=radio_index,
+            apn=apn,
+            apn_type=apn_type
+        )
+
+    for modem_oid_end, modem_state, service_type, roaming_status, modem_time, conn_status,\
+         modem_status, network, totyl_bytes_tx, total_bytes_rx, rssi, channel, band in modem_table:
+
+        modems[modem_oid_end] = CiscoCellular3g(
+            modem_state=modem_state,
+            service_type=str(service_type[0]*256 + service_type[1]),
+            roaming_status=roaming_status,
+            current_system_time=modem_time,
+            connection_status=conn_status,
+            total_bytes_tx=int(totyl_bytes_tx),
+            total_bytes_rx=int(total_bytes_rx),
+            modem_status=modem_status,
+            network=network,
+            rssi=int(rssi),
+            channel=int(channel),
+            band=band,
+        )
+
+    for index, name in interface_table:
+        interfaces[index] = name
+
+    for if_index, pdn_profile_used, pdn_conn_status, pdn_type, pdn_ipv4_addr, pdn_ipv6_addr in pdn_table:
+        if interfaces.get(if_index):
+            profile = profiles.get(pdn_profile_used)
+            if profile:
+                radio_index = profile.radio_index
+                radio = radios.get(radio_index)
+                modem = modems.get(radio_index)
+            else:
+                radio = None
+                modem = None
+
+            section[interfaces.get(if_index)] = CiscoCellularInterface(
+                conn_status=pdn_conn_status,
+                pdn_type=pdn_type,
+                ipv4_addr=pdn_ipv4_addr,
+                ipv6_addr=pdn_ipv6_addr,
+                profile=profile,
+                radio=radio,
+                modem=modem,
+            )
+
+    return section
+
+
+###########################################################################
+#
+#  INVENTORY function
+#
+###########################################################################
+
+
+def discovery_cisco_cellular_lte(params, section: Dict[str, CiscoCellularInterface]) -> DiscoveryResult:
+    for item, interface in section.items():
+        if interface.profile is not None or params['not_configured']:
+            yield Service(item=item)
+
+###########################################################################
+#
+#  CHECK function
+#
+###########################################################################
+
+
+def check_cisco_cellular_lte(item, params, section: Dict[str, CiscoCellularInterface]) -> Optional[CheckResult]:
+
+    try:
+        interface = section[item]
+    except KeyError:
+        # yield Result(state=State.UNKNOWN, notice='Item not found.')
+        return
+
+    if interface.profile is None:
+        yield Result(state=State(params['no_profile_state']), summary='No GSM/LTE profile configured')
+        return
+
+    metric_prefix = 'cisco_cellular_'
+
+    text = f'Status: {_cisco_gsm_pdp_status.get(interface.conn_status)}'
+    if interface.conn_status == '2':  # active
+        yield Result(state=State.OK, summary=text)
+    else:
+        yield Result(state=State(params['connection_state']), summary=text)
+
+    text = f'Modem state: {_cisco_gsm_modem_state.get(interface.modem.modem_state)}'
+    if interface.modem.modem_state == '2':  # UP
+        yield Result(state=State.OK, notice=text)
+    else:
+        yield Result(state=State(params['modem_not_up_state']), notice=text)
+
+    text = f'Modem status: {_cisco_gsm_modem_status.get(interface.modem.modem_status)}'
+    if interface.modem.modem_status == '3':  # online
+        yield Result(state=State.OK, notice=text)
+    else:
+        yield Result(state=State(params['modem_not_online_state']), notice=text)
+
+    yield Result(
+        state=State.OK,
+        notice=f'Connection status: {_cisco_gsm_connection_status.get(interface.modem.connection_status)}'
+    )
+
+    text = f'Roaming: {_cisco_roaming_status.get(interface.modem.roaming_status)}'
+    if interface.modem.roaming_status == '3':  # Home not roaming
+        yield Result(state=State.OK, notice=text)
+    else:
+        yield Result(state=State(params['roaming_state']), notice=text)
+
+    for value, label, render_func, levels_lower, metric in [
+        (interface.radio.lte_rsrp, 'RSRP', lambda v: f'{v} dBm', params['rsrp_levels_lower'], 'rsrp'),
+        (interface.radio.lte_rsrq, 'RSRQ', lambda v: f'{v} dB', params['rsrp_levels_lower'], 'rsrq',),
+        (interface.modem.rssi, 'RSSI', lambda v: f'{v} dBm', params['rssi_levels_lower'], 'rssi'),
+    ]:
+        yield from check_levels(
+            value=value,
+            label=label,
+            render_func=render_func,
+            levels_lower=levels_lower,
+            metric_name=f'{metric_prefix}{metric}',
+        )
+
+    now_time = time.time()
+    value_store = get_value_store()
+
+    for metric, value, label in [
+        ('if_in_bps', interface.modem.total_bytes_rx, 'In'),
+        ('if_out_bps', interface.modem.total_bytes_tx, 'Out'),
+    ]:
+        try:
+            value = get_rate(value_store, f'{metric}', now_time, value * 8, raise_overflow=True)
+        except GetRateError:
+            value = 0
+        yield from check_levels(
+            value=value,
+            label=label,
+            metric_name=metric,
+            render_func=render.networkbandwidth,
+        )
+
+    if interface.ipv4_addr:
+        yield Result(state=State.OK, summary=f'IPv4 Addr: {interface.ipv4_addr}')
+    if interface.ipv6_addr:
+        yield Result(state=State.OK, summary=f'IPv6 Addr: {interface.ipv6_addr}')
+
+    # if interface.profile.apn_type == interface.pdn_type:
+    #     yield Result(
+    #         state=State.OK,
+    #         notice=f'Current APN type: {_cisco_wan_cell_ext_protocol_type.get(interface.pdn_type)}'
+    #     )
+    # else:
+    #     yield Result(
+    #         state=State.WARN,
+    #         summary=f'Current APN type: {_cisco_wan_cell_ext_protocol_type.get(interface.pdn_type)}  '
+    #                 f'(expected: {_cisco_wan_cell_ext_protocol_type.get(interface.profile.apn_type)})'
+    #     )
+
+    yield Result(state=State.OK, notice=f'Networkcode: {interface.modem.network}')
+    expected_band = params['expected_band']
+    if interface.modem.band == expected_band:
+        yield Result(state=State.OK, summary=f'Band: {_cisco_gsm_band.get(interface.modem.band)}')
+    else:
+        yield Result(
+            state=State(params['state_not_expected_band']),
+            summary=f'Band: {_cisco_gsm_band.get(interface.modem.band)} (expected: {_cisco_gsm_band.get(expected_band)})'
+        )
+    yield Result(state=State.OK, notice=f'Channel: {interface.modem.channel}')
+    yield Result(state=State.OK, notice=f'Service type: {_cisco_gsm_service_type.get(interface.modem.service_type)}')
+    yield Result(state=State.OK, notice=f'Provider Time: {interface.modem.current_system_time}')
+
+    yield Result(state=State.OK, notice='\nInventory')
+    yield Result(state=State.OK, notice=f'APN: {interface.profile.apn}')
+    yield Result(state=State.OK, notice=f'Configured APN type: {_cisco_wan_cell_ext_protocol_type.get(interface.pdn_type)}')
+    yield Result(state=State.OK, notice=f'Current APN type: {_cisco_wan_cell_ext_protocol_type.get(interface.profile.apn_type)}')
+
+###########################################################################
+#
+#  SNMP section
+#
+###########################################################################
+
+
+register.snmp_section(
+    name='cisco_cellular_lte',
+    parse_function=parse_cisco_cellular_lte,
+    fetch=[
+        SNMPTree(
+            base='.1.3.6.1.4.1.9.9.661.1',  # CISCO-WAN-3G-MIB::ciscoWan3gMIBObjects
+            oids=[
+                OIDEnd(),
+                '1.1.3',  # c3gModemState
+                OIDBytes('1.1.5'),  # c3gCurrentServiceType
+                '1.1.6',  # c3gRoamingStatus
+                '1.1.7',  # c3gCurrentSystemTime
+                '1.1.8',  # c3gConnectionStatus
+                '3.1.1.6',  # c3gModemStatus
+                '3.2.1.9',  # c3gGsmNetwork
+                '3.2.1.19',  # c3gGsmTotalByteTransmitted
+                '3.2.1.20',  # c3gGsmTotalByteReceived
+                '3.4.1.1.1',  # c3gCurrentGsmRssi
+                '3.4.1.1.4',  # c3gGsmChannelNumber
+                '3.4.1.1.3',  # c3gGsmCurrentBand
+            ]),
+        SNMPTree(
+            base='.1.3.6.1.4.1.9.9.817.1.1.1.1.1',  # CISCO-WAN-CELL-EXT-MIB::cwceLteRadioEntry
+            oids=[
+                OIDEnd(),
+                '1',  # cwceLteCurrRsrp
+                '2',  # cwceLteCurrRsrq
+            ]),
+        SNMPTree(
+            base='.1.3.6.1.4.1.9.9.817.1.1.2.3.1',  # CISCO-WAN-CELL-EXT-MIB::cwceLteProfileEntry
+            oids=[
+                OIDEnd(),
+                '2',  # cwceLteProfileType
+                '5',  # cwceLteProfileApn
+            ]),
+        SNMPTree(
+            base='.1.3.6.1.4.1.9.9.817.1.1.2.4.1',  # CISCO-WAN-CELL-EXT-MIB::cwceLtePdnEntry
+            oids=[
+                OIDEnd(),
+                '2',  # cwceLtePdnProfileUsed
+                '3',  # cwceLtePdnConnStatus
+                '4',  # cwceLtePdnType
+                '5',  # cwceLtePdnIpv4Addr
+                '6',  # cwceLtePdnIpv6Addr
+            ]),
+
+        SNMPTree(
+            base='.1.3.6.1.2.1.31.1.1.1',  # IF-MIB::ifXEntry
+            oids=[
+                OIDEnd(),
+                '1',  # ifName
+            ]
+        )
+    ],
+    detect=all_of(
+        contains('.1.3.6.1.2.1.1.1.0', 'cisco'),  # sysDescr
+        exists('.1.3.6.1.4.1.9.9.661.1.3.1.1.2.*'),  # CISCO-WAN-3G-MIB::c3gImei
+    ))
+
+register.check_plugin(
+    name='cisco_cellular_lte',
+    service_name='Cellular %s',
+    discovery_function=discovery_cisco_cellular_lte,
+    discovery_ruleset_name='discovery_cisco_cellular_lte',
+    discovery_default_parameters={
+        'not_configured': False,
+    },
+    check_function=check_cisco_cellular_lte,
+    check_default_parameters={
+        'rsrp_levels_lower': (-100, -115),
+        'rsrq_levels_lower': (-8, -15),
+        'rssi_levels_lower': (-75, -85),
+        'roaming_state': 1,
+        'no_profile_state': 1,
+        'connection_state': 2,
+        'modem_not_up_state': 2,
+        'modem_not_online_state': 2,
+        'expected_band': '12',
+        'state_not_expected_band': 0,
+    },
+    check_ruleset_name='cisco_cellular_lte',
+)
diff --git a/agent_based/inv_cisco_cellular_lte.py b/agent_based/inv_cisco_cellular_lte.py
new file mode 100644
index 0000000..bd00724
--- /dev/null
+++ b/agent_based/inv_cisco_cellular_lte.py
@@ -0,0 +1,170 @@
+#!/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  : 2022-09-26
+# File  : inv_cisco_cellular_lte
+#
+# inventory of Cisco cellular interfaces (SIM cards)
+#
+#
+
+from typing import List
+from dataclasses import dataclass
+from cmk.base.plugins.agent_based.agent_based_api.v1 import (
+    register,
+    SNMPTree,
+    TableRow,
+    OIDEnd,
+    OIDBytes,
+    all_of,
+    contains,
+    exists,
+)
+from cmk.base.plugins.agent_based.agent_based_api.v1.type_defs import (
+    StringByteTable,
+    InventoryResult,
+)
+
+
+@dataclass
+class CiscoCellularProfile:
+    apn_type: str
+    apn: str
+
+
+_cisco_gsm_service_type = {
+    '0': '1xRTT',
+    '1': 'EVDO Revision 0',
+    '2': 'EVDO Revision A',
+    '3': 'EVDO Revision B',
+    '4': 'GPRS',
+    '5': 'EDGE',
+    '6': 'UMTS/WCDMA',
+    '7': 'HSDPA',
+    '8': 'HSUPA',
+    '9': 'HSPA',
+    '10': 'HSPA Plus',
+    '11': 'LTE TDD',
+    '12': 'LTE FDD',
+}
+
+
+_cisco_gsm_band = {
+    '1': 'unknown',
+    '2': 'invalid',
+    '3': 'none',
+    '4': 'GSM-850',
+    '5': 'GSM-900',
+    '6': 'GSM-1800',
+    '7': 'GSM-1900',
+    '8': 'WCDMA-800',
+    '9': 'WCDMA-850',
+    '10': 'WCDMA-1900',
+    '11': 'WCDMA-2100',
+    '12': 'LTE Band',
+}
+
+_cisco_wan_cell_ext_protocol_type = {
+    '1': 'unknown',
+    '2': 'IPv4',
+    '3': 'PPP',
+    '4': 'IPv6',
+    '5': 'IPv4v6',
+}
+
+
+def parse_inv_cisco_cellular_lte(string_table: List[StringByteTable]):
+    section = []
+    profiles = {}
+
+    modem_table, profile_table = string_table
+
+    for profile_oid_end, apn_type, apn in profile_table:
+        radio_index, profile_index = profile_oid_end.split('.')
+        profiles[radio_index] = CiscoCellularProfile(
+            apn=apn,
+            apn_type=apn_type
+        )
+
+    for oid_end, service_type, imsi, imei, iccid, msisdn, country, network, current_band in modem_table:
+        entry = {
+            'key_columns': {
+                'iccid': iccid,
+                'imei': imei,
+            },
+            'inventory_columns': {
+                'imsi': imsi if imsi else None,
+                'msisdn': msisdn if msisdn else None,
+                'country': country if country else None,
+                'network': network if network else None,
+                'current_band': _cisco_gsm_band.get(current_band, f'Unknown {current_band}'),
+                'apn': profiles[oid_end].apn if profiles.get(oid_end) else None,
+                'apn_type': _cisco_wan_cell_ext_protocol_type.get(profiles[oid_end].apn_type) if profiles.get(oid_end) else None,
+                'service_type': _cisco_gsm_service_type.get(str(service_type[0]*256 + service_type[1]))
+            },
+            'status_columns': {
+
+            }
+        }
+
+        keys = list(entry['inventory_columns'].keys())
+        for key in keys:
+            if not entry['inventory_columns'][key]:
+                entry['inventory_columns'].pop(key)
+
+        section.append(entry)
+
+    return section
+
+
+def inventory_cisco_cellular_lte(params, section) -> InventoryResult:
+    path = ['networking', 'cellular']
+
+    for entry in section:
+        yield TableRow(
+            path=path,
+            key_columns=entry['key_columns'],
+            inventory_columns=entry['inventory_columns'],
+            status_columns=entry['status_columns']
+        )
+
+
+register.snmp_section(
+    name='inv_cisco_cellular_lte',
+    parse_function=parse_inv_cisco_cellular_lte,
+    fetch=[SNMPTree(
+        base='.1.3.6.1.4.1.9.9.661.1',  # CISCO-WAN-3G-MIB::ciscoWan3gMIBObjects
+        oids=[
+            OIDEnd(),
+            OIDBytes('1.1.5'),  # c3gCurrentServiceType
+            '3.1.1.1',  # c3gImsi
+            '3.1.1.2',  # c3gImei
+            '3.1.1.3',  # c3gIccId
+            '3.1.1.4',  # c3gMsisdn
+            '3.2.1.8',  # c3gGsmCountry
+            '3.2.1.9',  # c3gGsmNetwork
+            '3.4.1.1.3',  # c3gGsmCurrentBand
+        ]),
+        SNMPTree(
+            base='.1.3.6.1.4.1.9.9.817.1.1.2.3.1',  # CISCO-WAN-CELL-EXT-MIB::cwceLteProfileEntry
+            oids=[
+                OIDEnd(),
+                '2',  # cwceLteProfileType
+                '5',  # cwceLteProfileApn
+            ])],
+    detect=all_of(
+        contains('.1.3.6.1.2.1.1.1.0', 'cisco'),  # sysDescr
+        exists('.1.3.6.1.4.1.9.9.661.1.3.1.1.2.*'),  # CISCO-WAN-3G-MIB::c3gImei
+    ))
+
+register.inventory_plugin(
+    name='inv_cisco_cellular_lte',
+    inventory_function=inventory_cisco_cellular_lte,
+    inventory_default_parameters={
+    },
+    inventory_ruleset_name='inv_cisco_cellular_lte',
+)
diff --git a/cisco_cellular_lte.mkp b/cisco_cellular_lte.mkp
index cd03f1fa6160413f51b2b3ffb6dcc9c09e074697..243b86fe520023343700c1550021e122636e3146 100644
GIT binary patch
literal 6832
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

delta 13
UcmdmBYRD$v&A}1GG?7gk02ea??EnA(

diff --git a/packages/cisco_cellular_lte b/packages/cisco_cellular_lte
new file mode 100644
index 0000000..e1783f5
--- /dev/null
+++ b/packages/cisco_cellular_lte
@@ -0,0 +1,16 @@
+{'author': 'Th.L. (thl-cmk[at]outlook[dot]com)',
+ 'description': 'Monitors Cisco cellular interfaces/modems/radios (LTE '
+                'connections)\n',
+ 'download_url': 'https://thl-cmk.hopto.org',
+ 'files': {'agent_based': ['cisco_cellular_lte.py',
+                           'inv_cisco_cellular_lte.py'],
+           'web': ['plugins/wato/cisco_cellular_lte.py',
+                   'plugins/metrics/cisco_cellular_lte.py',
+                   'plugins/views/inv_cisco_cellular_lte.py']},
+ 'name': 'cisco_cellular_lte',
+ 'num_files': 5,
+ 'title': 'Cisco cellular LTE',
+ 'version': '20220920.v0.0.1',
+ 'version.min_required': '2.0.0',
+ 'version.packaged': '2021.09.20',
+ 'version.usable_until': None}
\ No newline at end of file
diff --git a/web/plugins/metrics/cisco_cellular_lte.py b/web/plugins/metrics/cisco_cellular_lte.py
new file mode 100644
index 0000000..ebef830
--- /dev/null
+++ b/web/plugins/metrics/cisco_cellular_lte.py
@@ -0,0 +1,92 @@
+#!/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  : 2022-09-26
+#
+# Cisco Cellular Lte metrics plugin
+#
+#
+from cmk.gui.i18n import _
+
+from cmk.gui.plugins.metrics import (
+    metric_info,
+    graph_info,
+    perfometer_info
+)
+
+
+metric_info['cisco_cellular_rsrp'] = {
+    'title': _('RSRP'),
+    'unit': 'dbm',
+    'color': '22/a',
+}
+metric_info['cisco_cellular_rsrq'] = {
+    'title': _('RSRQ'),
+    'unit': 'db',
+    'color': '32/a',
+}
+metric_info['cisco_cellular_rssi'] = {
+    'title': _('RSSI'),
+    'unit': 'dbm',
+    'color': '42/a',
+}
+
+######################################################################################################################
+#
+# how to graph perdata for bgp peer
+#
+######################################################################################################################
+
+graph_info['cisco_cellular.rsrp'] = {
+    'title': _('Reference Signal Received Power'),
+    'metrics': [
+        ('cisco_cellular_rsrp', 'area'),
+    ],
+    'range': (0, 'cisco_cellular_rsrp:max'),
+}
+
+graph_info['cisco_cellular.rsrq'] = {
+    'title': _('Reference Signal Received Quality'),
+    'metrics': [
+        ('cisco_cellular_rsrq', 'area'),
+    ],
+    'scalars': [
+        ('cisco_cellular_rsrq:crit', _('crit')),
+        ('cisco_cellular_rsrq:warn', _('warn')),
+    ],
+    'range': (0, 'cisco_cellular_rsrq:max'),
+}
+
+graph_info['cisco_cellular.rssi'] = {
+    'title': _('Received Signal Strength Indicator'),
+    'metrics': [
+        ('cisco_cellular_rssi', 'area'),
+    ],
+}
+
+
+# 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,
+# })
\ No newline at end of file
diff --git a/web/plugins/views/inv_cisco_cellular_lte.py b/web/plugins/views/inv_cisco_cellular_lte.py
new file mode 100644
index 0000000..7554aaf
--- /dev/null
+++ b/web/plugins/views/inv_cisco_cellular_lte.py
@@ -0,0 +1,49 @@
+#!/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  : 2022-09-26
+# File  : view/inv_cisco_cellular_lte
+#
+
+from cmk.gui.i18n import _
+from cmk.gui.plugins.views import (
+    inventory_displayhints,
+)
+
+from cmk.gui.plugins.views.inventory import declare_invtable_view
+
+inventory_displayhints.update({
+    '.networking.cellular:': {
+        'title': _('Cellular (LTE)'),
+        'keyorder': [
+            'imei',
+            'iccid',
+            'imsi',
+            'network',
+            'apn',
+            'apn_type',
+            'service_type'
+        ],
+        'view': 'invcellularlte_of_host',
+    },
+    '.networking.cellular:*.iccid': {'short': _('ICC ID'), 'title': _('Integrated Circuit Card ID (ICC ID)')},
+    '.networking.cellular:*.imei': {'short': _('IMEI'), 'title': _('International Mobile Equipment Identifier (IMEI)')},
+    '.networking.cellular:*.imsi': {'short': _('IMSI'),
+                                    'title': _('International Mobile Subscriber Identifier (IMSI)')},
+    '.networking.cellular:*.msisdn': {
+        'short': _('MSISDN'),
+        'title': _('Mobile Subscriber Integrated Services Digital Network Number (MSISDN')
+    },
+    '.networking.cellular:*.country': {'title': _('Country'), },
+    '.networking.cellular:*.network': {'title': _('Network'), },
+    '.networking.cellular:*.current_band': {'title': _('Current band'), },
+    '.networking.cellular:*.apn': {'short': _('APN'), 'title': _('Access Point Name (APN)')},
+    '.networking.cellular:*.apn_type': {'title': _('APN type'), },
+    '.networking.cellular:*.service_type': {'title': _('Service type'), },
+})
+
+declare_invtable_view('invcellularlte', '.networking.cellular:', _('Cellular (LTE)'), _('Cellular (LTE)'))
diff --git a/web/plugins/wato/cisco_cellular_lte.py b/web/plugins/wato/cisco_cellular_lte.py
new file mode 100644
index 0000000..a5c7785
--- /dev/null
+++ b/web/plugins/wato/cisco_cellular_lte.py
@@ -0,0 +1,127 @@
+#!/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  : 2022-09-20
+# File  : wato/cisco_cellular_lte
+#
+
+from cmk.gui.i18n import _
+from cmk.gui.valuespec import (
+    Dictionary,
+    TextAscii,
+    MonitoringState,
+    FixedValue,
+    ListOfStrings,
+    ListOf,
+    Integer,
+    Tuple,
+    DropdownChoice,
+)
+
+from cmk.gui.plugins.wato import (
+    CheckParameterRulespecWithItem,
+    rulespec_registry,
+    RulespecGroupCheckParametersNetworking,
+    RulespecGroupCheckParametersDiscovery,
+    HostRulespec,
+)
+
+
+def _parameter_valuespec_cisco_cellular_lte():
+    return Dictionary(
+        elements=[
+            ('rsrp_levels_lower',
+             Tuple(
+                 title=_('Lower levels for RSRP'),
+                 help=_('Lower levels for RSRP (Reference Signal Received Power) in dBm'),
+                 elements=[
+                     Integer(title=_('Warning below'), default_value=-100, unit=_('dBm')),
+                     Integer(title=_('Critical below'), default_value=-115, unit=_('dBm')),
+                 ])),
+            ('rsrq_levels_lower',
+             Tuple(
+                 title=_('Lower levels for RSRQ'),
+                 help=_('Lower levels for RSRQ (Reference Signal Received Quality) in dB'),
+                 elements=[
+                     Integer(title=_('Warning below'), default_value=-8, unit=_('dB')),
+                     Integer(title=_('Critical below'), default_value=-15, unit=_('dB')),
+                 ])),
+            ('rssi_levels_lower',
+             Tuple(
+                 title=_('Lower levels for RSSI'),
+                 help=_('Lower levels for RSSI (Received Signal Strength Indicator) in dBm'),
+                 elements=[
+                     Integer(title=_('Warning below'), default_value=-75, unit=_('dBm')),
+                     Integer(title=_('Critical below'), default_value=-85, unit=_('dBm')),
+                 ])),
+            ('roaming_state',
+             MonitoringState(
+                 title=_('Monitoring state if the device is roaming'),
+                 help=_('Monitoring state if the device is roaming. Default is WARN.'),
+                 default_value=1,
+             )),
+            ('no_profile_state',
+             MonitoringState(
+                 title=_('Monitoring state if no GSM/LTE profile configured'),
+                 help=_('Monitoring state if no GSM/LTE  profile is configured. Default is WARN.'),
+                 default_value=1,
+             )),
+            ('connection_state',
+             MonitoringState(
+                 title=_('Monitoring state if the device is not connected'),
+                 help=_('Monitoring state if the device is not connected. Default is CRIT.'),
+                 default_value=1,
+             )),
+            ('modem_not_up_state',
+             MonitoringState(
+                 title=_('Monitoring state if the modem is not up'),
+                 help=_('Monitoring state if the the modem is not up. Default is CRIT.'),
+                 default_value=1,
+             )),
+            ('modem_not_online_state',
+             MonitoringState(
+                 title=_('Monitoring state if the modem is not online'),
+                 help=_('Monitoring state if the the modem is not online. Default is CRIT.'),
+                 default_value=1,
+             )),
+        ],
+    )
+
+
+rulespec_registry.register(
+    CheckParameterRulespecWithItem(
+        check_group_name='cisco_cellular_lte',
+        group=RulespecGroupCheckParametersNetworking,
+        match_type='dict',
+        parameter_valuespec=_parameter_valuespec_cisco_cellular_lte,
+        title=lambda: _('Cisco cellular LTE'),
+        item_spec=lambda: TextAscii(title=_('Item'), ),
+    ))
+
+
+def _valuespec_discovery_cisco_cellular_lte():
+    return Dictionary(
+            title=_('Cisco cellular LTE'),
+            elements=[
+                ('not_configured',
+                 FixedValue(
+                     True,
+                     title=_('Discover not configured interfaces'),
+                     help=_('If enabled the plugin will also discover cellular interfaces without a profile attached.'),
+                     totext=_('')
+                 )),
+            ],
+        )
+
+
+rulespec_registry.register(
+    HostRulespec(
+        group=RulespecGroupCheckParametersDiscovery,
+        match_type='dict',
+        name='discovery_cisco_cellular_lte',
+        valuespec=_valuespec_discovery_cisco_cellular_lte,
+    ))
-- 
GitLab