Collection of CheckMK checks (see https://checkmk.com/). All checks and plugins are provided as is. Absolutely no warranty. Send any comments to thl-cmk[at]outlook[dot]com

Skip to content
Snippets Groups Projects
Commit 14d5e8d0 authored by thl-cmk's avatar thl-cmk :flag_na:
Browse files

added support for Network Visualization

 - wireless_ethernet_statuses.py
   - incompatible: changed service from Port to Interface
   - added interface inventory
 - switch_ports_statuses.py
   - incompatible: changed service from Port to Interface
   - incompatible: reworked service discovery rule (Cisco Meraki Switch Ports)
   - added interface inventory
   - added host label nvdct/has_lldp_neighbours
parent 908ee0c4
No related branches found
No related tags found
No related merge requests found
Showing
with 587 additions and 331 deletions
[PACKAGE]: ../../raw/master/mkp/cisco_meraki-1.3.7-20241116.mkp "cisco_meraki-1.3.7-20241116.mkp"
[PACKAGE]: ../../raw/master/mkp/cisco_meraki-1.4.1-20241217.mkp "cisco_meraki-1.4.1-20241217.mkp"
[SDK]: ../../raw/master/mkp/MerakiSDK-1.52.0-20241116.mkp "MerakiSDK-1.52.0-20241116.mkp"
# Cisco Meraki special agent
......
File added
......@@ -3,7 +3,7 @@
# Copyright (C) 2022 Checkmk GmbH - License: GNU General Public License v2
# This file is part of Checkmk (https://checkmk.com). It is subject to the terms and
# conditions defined in the file COPYING, which is part of this source code package.
from cmk.gui.forms import section
# enhancements by thl-cmk[at]outlook[dot]com, https://thl-cmk.hopto.org
# - made device status configurable via WATO
# - added last_reported as check_levels, levels_upper can be configured via WATO
......
......@@ -14,11 +14,12 @@
# 2024-05-15: fixed typo in output of uplink.received (in -> In) (ThX to Rickard Eriksson)
# moved parse function to the dataclasses
# 2024-05-19: reworked appliance uplinks usage
# 2024-04-24: fixed, we can have no traffic if uplinc is not connected
# 2024-04-24: fixed, we can have no traffic if uplink is not connected
# 2024-06-29: refactored for CMK 2.3
# changed service name from "Appliance Uplink" to "Uplink"
# changed service name from 'Appliance Uplink' to 'Uplink'
# fixed render function for bandwidth -> uses now render.networkbandwidth
# 2024-06-30: renamed from cisco_meraki_org_appliance_uplinks.py in to appliance_uplinks.py
# 2024-12-13: added connecting to _STATUS_MAP
from collections.abc import Mapping
from dataclasses import dataclass
......@@ -61,8 +62,8 @@ __appliance_uplinks = [
'publicIp': '20.197.135.251',
'secondaryDns': '9.9.9.9',
'status': 'active',
"received": 52320006, # bytes
"sent": 52928038, # bytes
'received': 52320006, # bytes
'sent': 52928038, # bytes
},
{
'gateway': '192.168.5.100',
......@@ -78,7 +79,7 @@ __appliance_uplinks = [
}
]
_LAST_REPORTED_AT = "%Y-%m-%dT%H:%M:%SZ"
_LAST_REPORTED_AT = '%Y-%m-%dT%H:%M:%SZ'
@dataclass(frozen=True)
......@@ -152,7 +153,7 @@ def parse_appliance_uplinks(string_table: StringTable) -> Appliance | None:
agent_section_cisco_meraki_org_appliance_uplinks = AgentSection(
name="cisco_meraki_org_appliance_uplinks",
name='cisco_meraki_org_appliance_uplinks',
parse_function=parse_appliance_uplinks,
)
......@@ -163,10 +164,11 @@ def discover_appliance_uplinks(section: Appliance) -> DiscoveryResult:
_STATUS_MAP = {
"active": 0,
"failed": 2,
"not_connected": 1,
"ready": 0,
'active': 0,
'failed': 2,
'not_connected': 1,
'ready': 0,
'connecting': 1,
}
_TIMESPAN = 60
......
......@@ -180,11 +180,11 @@ def check_appliance_vpns(item: str, params: Mapping[str, any], section: Mapping[
return None
if peer.reachability is not None and peer.reachability.lower() in ['reachable']:
yield Result(state=State.OK, summary=f'{peer.reachability}')
yield Result(state=State.OK, summary=f'Status: {peer.reachability}')
else:
yield Result(
state=State(params.get('status_not_reachable', 1)),
summary=f'{peer.reachability}',
summary=f'Status: {peer.reachability}',
)
yield Result(state=State.OK, summary=f'Type: {peer.type}')
......
......@@ -11,7 +11,7 @@
# 2024-04-27: made data parsing more robust
# 2024-06-29: refactored for CMK 2.3
# moved parse functions to class methods
# changed service name from "Cellular uplink" to "Uplink"
# changed service name from 'Cellular uplink' to 'Uplink'
# 2024-06-30: renamed from cisco_meraki_org_cellular_uplinks.py in to cellular_uplinks.py
from collections.abc import Mapping
......@@ -35,39 +35,39 @@ from cmk_addons.plugins.meraki.lib.utils import get_int, load_json
__cellular_uplinks = [
{
"highAvailability": {
"enabled": False,
"role": "primary"
'highAvailability': {
'enabled': False,
'role': 'primary'
},
"lastReportedAt": "2023-11-13T19:52:06Z",
"model": "MG41",
"networkId": "L_575897802350012343",
"serial": "QQQQ-XXXX-ZZZZ",
"uplinks": [
'lastReportedAt': '2023-11-13T19:52:06Z',
'model': 'MG41',
'networkId': 'L_575897802350012343',
'serial': 'QQQQ-XXXX-ZZZZ',
'uplinks': [
{
"apn": "apn.name",
"connectionType": "lte",
"dns1": None,
"dns2": None,
"gateway": None,
"iccid": "89492027206012345518",
"interface": "cellular",
"ip": None,
"model": "integrated",
"provider": "provider.name",
"publicIp": "2.3.4.5",
"signalStat": {
"rsrp": "-111",
"rsrq": "-8"
'apn': 'apn.name',
'connectionType': 'lte',
'dns1': None,
'dns2': None,
'gateway': None,
'iccid': '89492027206012345518',
'interface': 'cellular',
'ip': None,
'model': 'integrated',
'provider': 'provider.name',
'publicIp': '2.3.4.5',
'signalStat': {
'rsrp': '-111',
'rsrq': '-8'
},
"signalType": None,
"status": "active"
'signalType': None,
'status': 'active'
}
]
}
]
_LAST_REPORTED_AT = "%Y-%m-%dT%H:%M:%SZ"
_LAST_REPORTED_AT = '%Y-%m-%dT%H:%M:%SZ'
@dataclass(frozen=True)
......@@ -159,7 +159,7 @@ def parse_cellular_uplinks(string_table: StringTable) -> CellularGateway | None:
agent_section_cisco_meraki_org_cellular_uplinks = AgentSection(
name="cisco_meraki_org_cellular_uplinks",
name='cisco_meraki_org_cellular_uplinks',
parse_function=parse_cellular_uplinks,
)
......
......@@ -15,6 +15,7 @@
# 2024-07-13: fixed crash on missing metrics (device dormant) ThX to Leon Buhleier
# 2024-08-07: fixed crash on missing power value (unit only) ThX to Leon Buhleier
# 2024-09-12: fixed missing SSID 0 ThX to Andreas Doehler
# 2024-12-13: fixed crash if no valid data received (json_data = [[]])
from collections.abc import Mapping
from dataclasses import dataclass
......@@ -64,7 +65,7 @@ class SSID:
def parse_wireless_device_status(string_table: StringTable) -> Mapping[str, SSID] | None:
json_data = load_json(string_table)
if (json_data := json_data[0]) is None:
if not (json_data := json_data[0]):
return
ssids = {}
......@@ -78,7 +79,6 @@ def parse_wireless_device_status(string_table: StringTable) -> Mapping[str, SSID
item = str(ssid_number) + ' on band ' + row.get('band')
ssids[item] = SSID.parse(row)
return ssids
......
......@@ -12,6 +12,9 @@
# 2024-06-29: refactored for CMK 2.3
# moved parse functions to class methods
# 2024-06-30: renamed from cisco_meraki_org_wireless_ethernet_statuses.py in to wireless_ethernet_statuses.py
# 2024-11-17: incompatible change item from "Port %s" to "Interface %s" -> rediscover your devices
# added interface inventory
# 2024-12-12: fixed crash if speed is None
# ToDo: create ruleset cisco_meraki_wireless_ethernet_statuses
......@@ -28,6 +31,9 @@ from cmk.agent_based.v2 import (
State,
StringTable,
render,
InventoryPlugin,
InventoryResult,
TableRow,
)
from cmk_addons.plugins.meraki.lib.utils import get_int, load_json
......@@ -176,7 +182,7 @@ def check_wireless_ethernet_statuses(
check_plugin_cisco_meraki_org_wireless_ethernet_statuses = CheckPlugin(
name='cisco_meraki_org_wireless_ethernet_statuses',
service_name='Port %s',
service_name='Interface %s',
discovery_function=discover_wireless_ethernet_statuses,
check_function=check_wireless_ethernet_statuses,
check_default_parameters={
......@@ -187,3 +193,29 @@ check_plugin_cisco_meraki_org_wireless_ethernet_statuses = CheckPlugin(
},
# check_ruleset_name='cisco_meraki_wireless_ethernet_statuses',
)
def inventory_meraki_wireless_ethernet(section: Mapping[str, WirelessEthernetPort]) -> InventoryResult:
for port in section.values():
yield TableRow(
path=['networking', 'interfaces'],
key_columns={
"index": port.name.split(' ')[-1],
},
inventory_columns={
'alias': port.name,
'description': port.name,
'name': port.name,
'admin_status': 1,
'oper_status': 1,
**({'speed': render.nicspeed(port.speed)} if port.speed is not None else {}),
'if_port_type': '6 - ethernetCsmacd',
},
)
inventory_plugin_inv_meraki_wireless_ethernet = InventoryPlugin(
name='inv_meraki_wireless_ethernet',
sections=['cisco_meraki_org_wireless_ethernet_statuses'],
inventory_function=inventory_meraki_wireless_ethernet,
)
This diff is collapsed.
......@@ -23,23 +23,24 @@ from cmk.base.plugins.agent_based.agent_based_api.v1.type_defs import CheckResul
MerakiAPIData = Mapping[str, object]
# parameter names for agent options
_SEC_NAME_ORGANISATIONS: Final = "_organisations" # internal use runs always
_SEC_NAME_DEVICE_INFO: Final = "_device_info" # Not configurable, needed for piggyback
_SEC_NAME_NETWORKS: Final = "_networks" # internal use, runs always, needed for network names
_SEC_NAME_ORG_API_REQUESTS: Final = "api-requests-by-organization" # internal use, runs always
_SEC_NAME_APPLIANCE_UPLINKS: Final = "appliance-uplinks"
_SEC_NAME_APPLIANCE_UPLINKS_USAGE: Final = "appliance-uplinks-usage"
_SEC_NAME_APPLIANCE_VPNS: Final = "appliance-vpns"
_SEC_NAME_APPLIANCE_PERFORMANCE: Final = "appliance-performance"
_SEC_NAME_CELLULAR_UPLINKS: Final = "cellular-uplinks"
_SEC_NAME_DEVICE_STATUSES: Final = "device-status"
_SEC_NAME_DEVICE_UPLINKS_INFO: Final = "device-uplinks-info"
_SEC_NAME_LICENSES_OVERVIEW: Final = "licenses-overview"
_SEC_NAME_SENSOR_READINGS: Final = "sensor-readings"
_SEC_NAME_SWITCH_PORTS_STATUSES: Final = "switch-ports-statuses"
_SEC_NAME_WIRELESS_DEVICE_STATUS: Final = "wireless-device-status"
_SEC_NAME_WIRELESS_ETHERNET_STATUSES: Final = "wireless-ethernet-statuses"
_SEC_NAME_ORGANISATIONS: Final = '_organisations' # internal use runs always
_SEC_NAME_DEVICE_INFO: Final = '_device_info' # Not configurable, needed for piggyback
_SEC_NAME_NETWORKS: Final = '_networks' # internal use, runs always, needed for network names
_SEC_NAME_ORG_API_REQUESTS: Final = 'api-requests-by-organization' # internal use, runs always
_SEC_NAME_APPLIANCE_UPLINKS: Final = 'appliance-uplinks'
_SEC_NAME_APPLIANCE_PORTS: Final = 'appliance-ports'
_SEC_NAME_APPLIANCE_UPLINKS_USAGE: Final = 'appliance-uplinks-usage'
_SEC_NAME_APPLIANCE_VPNS: Final = 'appliance-vpns'
_SEC_NAME_APPLIANCE_PERFORMANCE: Final = 'appliance-performance'
_SEC_NAME_CELLULAR_UPLINKS: Final = 'cellular-uplinks'
_SEC_NAME_DEVICE_STATUSES: Final = 'device-status'
_SEC_NAME_DEVICE_UPLINKS_INFO: Final = 'device-uplinks-info'
_SEC_NAME_LICENSES_OVERVIEW: Final = 'licenses-overview'
_SEC_NAME_SENSOR_READINGS: Final = 'sensor-readings'
_SEC_NAME_SWITCH_PORTS_STATUSES: Final = 'switch-ports-statuses'
_SEC_NAME_WIRELESS_DEVICE_STATUS: Final = 'wireless-device-status'
_SEC_NAME_WIRELESS_ETHERNET_STATUSES: Final = 'wireless-ethernet-statuses'
# api cache defaults per section
......@@ -60,22 +61,21 @@ _SEC_CACHE_SENSOR_READINGS = 0
_SEC_CACHE_SWITCH_PORTS_STATUSES = 0
_SEC_CACHE_WIRELESS_DEVICE_STATUS = 30
_SEC_CACHE_WIRELESS_ETHERNET_STATUSES = 30
_SEC_CACHE_APPLIANCE_PORTS = 30
# Early Access
_SEC_NAME_ORG_SWITCH_PORTS_STATUSES: Final = "org-switch-ports-statuses"
_SEC_NAME_ORG_SWITCH_PORTS_STATUSES: Final = 'org-switch-ports-statuses'
@dataclass(frozen=True)
class MerakiNetwork:
id: str # "N_24329156",
name: str # "Main Office",
product_types: Sequence[str] # ["appliance", "switch", "wireless"]
time_zone: str # "America/Los_Angeles",
tags: Sequence[str] # [ "tag1", "tag2" ],
enrollment_string: str | None # "my-enrollment-string",
notes: str # "Additional description of the network",
id: str # 'N_24329156',
name: str # 'Main Office',
product_types: Sequence[str] # ['appliance', 'switch', 'wireless']
time_zone: str # 'America/Los_Angeles',
tags: Sequence[str] # [ 'tag1', 'tag2' ],
enrollment_string: str | None # 'my-enrollment-string',
notes: str # 'Additional description of the network',
is_bound_to_config_template: bool # false
organisation_id: str
organisation_name: str
......@@ -97,7 +97,7 @@ def check_last_reported_ts(
if (age := time.time() - last_reported_ts) < 0:
yield Result(
state=State.OK,
summary="Negative timespan since last report time.",
summary='Negative timespan since last report time.',
)
return
if levels_upper:
......
......@@ -15,6 +15,10 @@
# 2024-06-27: refactored for CMK 2.3
# 2024-06-30: renamed from cisco_meraki_switch_ports_statuses.py in to switch_ports_statuses.py
# added params from discovery as render only
# 2024-11-17: incompatible change to match changed port status check -> recreate your discovery rule
# 2024-11-23: added missing discovery parameters admin_state and operational_state
# removed discovery parameters 'enabled' and 'status'
# reference to section organization_switch_ports removed, missing traffic, lldp, cdp, stp, ...
from cmk.rulesets.v1 import Label, Title, Help
from cmk.rulesets.v1.form_specs import (
......@@ -29,11 +33,6 @@ from cmk.rulesets.v1.form_specs import (
)
from cmk.rulesets.v1.rule_specs import CheckParameters, DiscoveryParameters, HostAndItemCondition, Topic
from cmk_addons.plugins.meraki.lib.utils import (
_SEC_NAME_ORG_SWITCH_PORTS_STATUSES,
_SEC_NAME_SWITCH_PORTS_STATUSES,
)
def _parameter_form():
return Dictionary(
......@@ -55,17 +54,17 @@ def _parameter_form():
)),
'state_speed_change': DictElement(
parameter_form=ServiceState(
title=Title('Monitoring state if speed is changed'),
title=Title('Monitoring state if speed has changed'),
prefill=DefaultValue(ServiceState.WARN),
)),
'state_admin_change': DictElement(
parameter_form=ServiceState(
title=Title('Monitoring state if admin state is changed'),
title=Title('Monitoring state if admin state has changed'),
prefill=DefaultValue(ServiceState.WARN),
)),
'state_op_change': DictElement(
parameter_form=ServiceState(
title=Title('Monitoring state if operational state is changed'),
title=Title('Monitoring state if operational state has changed'),
prefill=DefaultValue(ServiceState.WARN),
)),
'show_traffic': DictElement(
......@@ -76,23 +75,20 @@ def _parameter_form():
help_text=Help(
'Use only with cache disabled in the Meraki special agent settings. '
'Depending on your Meraki organization size (in terms of number of switches) '
'this will exceeds the limits of the allowed API requests per second. You can try to '
'enable "Early Access" in the Meraki dashboard. In the Meraki special agent settings '
f'switch from "{_SEC_NAME_SWITCH_PORTS_STATUSES}" to "{_SEC_NAME_ORG_SWITCH_PORTS_STATUSES}". '
'This will fetch all the switch data with one API request instead of one request for each '
'switch.'
'this will exceeds the limits of the allowed API requests per second.'
),
)),
# params from discovery
'enabled': DictElement(
'admin_state': DictElement(
render_only=True,
parameter_form=String(
title=Title('Discovered admin state')
)),
'status': DictElement(
)
),
'operational_state': DictElement(
render_only=True,
parameter_form=String(
title=Title('Discovered status')
title=Title('Discovered operational state')
)
),
'speed': DictElement(
......@@ -100,7 +96,7 @@ def _parameter_form():
parameter_form=String(
title=Title('Discovered speed')
)
)
),
},
)
......@@ -117,33 +113,42 @@ rule_spec_cisco_meraki_switch_ports_statuses = CheckParameters(
def _discovery_form():
return Dictionary(
elements={
'discovered_port_states': DictElement(
'operational_port_states': DictElement(
parameter_form=MultipleChoice(
title=Title('Select Ports to discover'),
title=Title('Match port states'),
elements=[
MultipleChoiceElement(
title=Title('Admin enabled'),
name='admin_enabled',
title=Title('1 - up'),
name='up',
),
MultipleChoiceElement(
title=Title('Admin disabled'),
name='admin_disabled',
title=Title('2 - down'),
name='down',
),
],
help_text=Help('Apply this rule only to interfaces whose port state is listed below.'),
prefill=DefaultValue([
'up',
'down',
])
)),
'admin_port_states': DictElement(
parameter_form=MultipleChoice(
title=Title('Match admin states'),
elements=[
MultipleChoiceElement(
title=Title('Connected'),
name='connected',
title=Title('1 - up'),
name='up',
),
MultipleChoiceElement(
title=Title('Disconnected'),
name='disconnected',
title=Title('2 - down'),
name='down',
),
],
help_text=Help('Select the port states for discovery'),
help_text=Help('Apply this rule only to interfaces whose admin state is listed below'),
prefill=DefaultValue([
'admin_enabled',
'admin_disabled',
'connected',
'disconnected',
'up',
'down',
])
)),
},
......
......@@ -63,7 +63,7 @@
'web': ['plugins/views/cisco_meraki.py']},
'name': 'cisco_meraki',
'title': 'Cisco Meraki special agent',
'version': '1.3.7-20241116',
'version': '1.4.1-20241217',
'version.min_required': '2.3.0b1',
'version.packaged': 'cmk-mkp-tool 0.2.0',
'version.usable_until': '2.4.0b1'}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment