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 3efd987e authored by thl-cmk's avatar thl-cmk :flag_na:
Browse files

update project

parent 10d741e4
No related branches found
No related tags found
No related merge requests found
[PACKAGE]: ../../raw/master/mkp/cisco_meraki-1.2.14-20240521.mkp "cisco_meraki-1.2.14-20240521.mkp"
[PACKAGE]: ../../raw/master/mkp/cisco_meraki-1.3.0-20240623.mkp "cisco_meraki-1.3.0-20240623.mkp"
[SDK]: ../../raw/master/mkp/MerakiSDK-1.46.0-20240516.mkp "MerakiSDK-1.46.0-20240516.mkpp"
# Cisco Meraki special agent
......
File added
#!/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 : 2023-11-04
# File : cisco_meraki_appliance_performance.py (check plugin)
from _collections_abc import Mapping
from cmk.base.plugins.agent_based.agent_based_api.v1 import (
register,
check_levels,
Service,
render,
)
from cmk.base.plugins.agent_based.agent_based_api.v1.type_defs import (
CheckResult,
DiscoveryResult,
StringTable,
)
from cmk.base.plugins.agent_based.utils.cisco_meraki import (
load_json,
)
# sample agent output
# {"perfScore": 1}
# sample string_table
# [['[{"perfScore": 0}]']]
def parse_appliance_performance(string_table: StringTable) -> int:
json_data = load_json(string_table)
if (json_data := json_data[0]) is None:
return
perfscore = json_data.get('perfScore')
if isinstance(perfscore, int):
return int(perfscore)
register.agent_section(
name="cisco_meraki_org_appliance_performance",
parse_function=parse_appliance_performance,
)
def discover_appliance_performance(section: int) -> DiscoveryResult:
yield Service()
def check_appliance_performance(params: Mapping[str, any], section: int) -> CheckResult:
yield from check_levels(
value=section,
label='Utilization',
levels_upper=params.get('levels_upper'),
render_func=render.percent,
metric_name='utilization',
boundaries=(0, 100),
)
register.check_plugin(
name='cisco_meraki_org_appliance_performance',
service_name='Utilization',
discovery_function=discover_appliance_performance,
check_function=check_appliance_performance,
check_default_parameters={
'levels_upper': (60, 80),
},
check_ruleset_name='cisco_meraki_org_appliance_performance',
)
......@@ -9,6 +9,9 @@
# File : cisco_meraki_org_wireless_device_status.py (check plugin)
# 2024-04-27: made data parsing more robust
# 2024-06-23: fixed crash on empty json_data
# moved data parsing in to SSID class
from _collections_abc import Mapping
from dataclasses import dataclass
......@@ -32,7 +35,7 @@ from cmk.base.plugins.agent_based.utils.cisco_meraki import (
@dataclass(frozen=True)
class SSDI:
class SSID:
band: str | None
broadcasting: bool | None
bssid: str | None
......@@ -44,10 +47,26 @@ class SSDI:
power: str | None
visible: bool | None
@classmethod
def parse(cls, ssid: Mapping[str: object]):
return cls(
band=str(ssid['band']) if ssid.get('band') is not None else None,
broadcasting=bool(ssid['broadcasting']) if ssid.get('broadcasting') is not None else None,
bssid=str(ssid['bssid']) if ssid.get('') is not None else None,
channel=get_int(ssid.get('channel')),
channel_width=str(ssid['channelWidth']) if ssid.get('channelWidth') is not None else None,
enabled=bool(ssid['enabled']) if ssid.get('enabled') is not None else None,
name=str(ssid['ssidName']) if ssid.get('ssidName') is not None else None,
number=get_int(ssid.get('ssidNumber')),
power=str(ssid['power']) if ssid.get('power') is not None else None,
visible=bool(ssid['enabled']) if ssid.get('enabled') is not None else None,
)
def parse_wireless_device_status(string_table: StringTable):
def parse_wireless_device_status(string_table: StringTable) -> Mapping[str, SSID] | None:
json_data = load_json(string_table)
json_data = json_data[0]
if (json_data := json_data[0]) is None:
return
ssids = {}
for row in json_data.get('basicServiceSets', []):
......@@ -59,18 +78,7 @@ def parse_wireless_device_status(string_table: StringTable):
item = str(ssid_number) + ' on band ' + row.get('band')
ssids[item] = SSDI(
band=str(row['band']) if row.get('band') is not None else None,
broadcasting=bool(row['broadcasting']) if row.get('broadcasting') is not None else None,
bssid=str(row['bssid']) if row.get('') is not None else None,
channel=get_int(row.get('channel')),
channel_width=str(row['channelWidth']) if row.get('channelWidth') is not None else None,
enabled=bool(row['enabled']) if row.get('enabled') is not None else None,
name=str(row['ssidName']) if row.get('ssidName') is not None else None,
number=get_int(row.get('ssidNumber')),
power=str(row['power']) if row.get('power') is not None else None,
visible=bool(row['enabled']) if row.get('enabled') is not None else None,
)
ssids[item] = SSID.parse(row)
return ssids
......@@ -86,7 +94,7 @@ _is = {
}
def discover_wireless_device_status(section: Mapping[str, SSDI]) -> DiscoveryResult:
def discover_wireless_device_status(section: Mapping[str, SSID]) -> DiscoveryResult:
for ssid in section.keys():
yield Service(item=ssid)
......@@ -94,10 +102,10 @@ def discover_wireless_device_status(section: Mapping[str, SSDI]) -> DiscoveryRes
def check_wireless_device_status(
item: str,
params: Mapping[str, any],
section: Mapping[str, SSDI]
section: Mapping[str, SSID]
) -> CheckResult:
try:
ssid: SSDI = section[item]
ssid: SSID = section[item]
except KeyError:
return None
......
......@@ -28,6 +28,7 @@ _SEC_NAME_ORG_API_REQUESTS: Final = "api-requests-by-organization" # internal u
_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-statuses"
_SEC_NAME_DEVICE_UPLINKS_INFO: Final = "device-uplinks-info"
......
......@@ -282,6 +282,29 @@ perfometer_info.append({
"exponent": 5,
})
# appliance performance/utitlization
metric_info["utilization"] = {
"title": _("Utilization"),
"unit": "%",
"color": "16/a",
}
graph_info['cisco_meraki.cisco_meraki_appliance.utilization'] = {
'title': _('Cisco Meraki Appliance Utilization'),
'metrics': [
('utilization', 'area'),
],
'scalars': [
('utilization:crit', _('crit')),
('utilization:warn', _('warn')),
],
'range': (0, 100),
}
perfometer_info.append({
'type': 'linear',
'segments': ['utilization'],
'total': 100,
})
# testing only
# metric_info["usage_out"] = {
# "title": _("Usage Out"),
......
#!/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 : 2024-06-23
# File : cisco_meraki_org_appliance_performance.py (WATO)
from cmk.gui.i18n import _
from cmk.gui.plugins.wato.utils import (
CheckParameterRulespecWithoutItem,
rulespec_registry,
RulespecGroupCheckParametersNetworking,
)
from cmk.gui.valuespec import (
Dictionary,
Integer,
Tuple,
)
def _parameter_valuespec_cisco_meraki_org_appliance_performance():
return Dictionary(
title=_('Cisco Meraki Appliance Utilization'),
optional_keys=True,
elements=[
('levels_upper',
Tuple(
title=_('Upper Levels'),
elements=[
Integer(
title=_("Warning at"),
unit='%',
default_value=60,
minvalue=0,
maxvalue=101,
),
Integer(
title=_("Critical at"),
unit='%',
default_value=80,
minvalue=0,
maxvalue=101,
),
],
help=_(
'The device utilization data reported to the Meraki'
' dashboard is based on a load average measured over a'
' period of one minute. The load value is returned in'
' numeric values ranging from 1 through 100. A lower'
' value indicates a lower load, and a higher value'
' indicates a more intense workload. Currently, the'
' device utilization value is calculated based upon the'
' CPU utilization of the MX as well as its traffic load.'
' If an MX device is consistently over 50% utilization'
' during normal operation, upgrading to a higher'
' throughput model or reducing the per-device load'
' through horizontal scaling should be considered. For'
' more information see:'
' https://documentation.meraki.com-MX-Monitoring?and?'
'Reporting-Device?Utiliyation'),
)),
],
)
rulespec_registry.register(
CheckParameterRulespecWithoutItem(
title=lambda: _('Cisco Meraki Appliance Utilization'),
check_group_name='cisco_meraki_org_appliance_performance',
group=RulespecGroupCheckParametersNetworking,
parameter_valuespec=_parameter_valuespec_cisco_meraki_org_appliance_performance,
match_type='dict',
)
)
......@@ -83,8 +83,9 @@ from cmk.special_agents.utils.misc import DataCache
from cmk.base.plugins.agent_based.utils.cisco_meraki import (
MerakiNetwork, # type: ignore[import]
_SEC_NAME_APPLIANCE_UPLINKS, # type: ignore[import]
_SEC_NAME_APPLIANCE_UPLINKS_USAGE, # type: ignore[import]
_SEC_NAME_APPLIANCE_UPLINKS_USAGE, # type: ignore[import]
_SEC_NAME_APPLIANCE_VPNS, # type: ignore[import]
_SEC_NAME_APPLIANCE_PERFORMANCE, # type: ignore[import]
_SEC_NAME_CELLULAR_UPLINKS, # type: ignore[import]
_SEC_NAME_DEVICE_INFO, # type: ignore[import]
_SEC_NAME_DEVICE_STATUSES, # type: ignore[import]
......@@ -122,6 +123,7 @@ _SECTION_NAME_MAP = {
_SEC_NAME_APPLIANCE_UPLINKS: "appliance_uplinks",
_SEC_NAME_APPLIANCE_UPLINKS_USAGE: "appliance_uplinks_usage",
_SEC_NAME_APPLIANCE_VPNS: "appliance_vpns",
_SEC_NAME_APPLIANCE_PERFORMANCE: "appliance_performance",
_SEC_NAME_CELLULAR_UPLINKS: "cellular_uplinks",
_SEC_NAME_DEVICE_INFO: "device_info",
_SEC_NAME_DEVICE_STATUSES: "device_status",
......@@ -257,6 +259,7 @@ class _Organisation(TypedDict):
# |
# |--> MerakiGetDeviceSwitchPortsStatuses -> default 60+
# |--> MerakiGetDeviceWirelessStatus -> default 60+
# |--> MerakiGetDeviceAppliancePerformance
class MerakiSection(DataCache):
def __init__(
......@@ -489,6 +492,22 @@ class MerakiGetOrganizationApplianceVpnStatuses(MerakiSectionOrg):
return []
class MerakiGetDeviceAppliancePerformance(MerakiSectionSerial):
@property
def name(self):
return f'getDeviceAppliancePerformance_{self._serial}'
def get_live_data(self):
try:
return self._config.dashboard.appliance.getDeviceAppliancePerformance(
self._serial)
except meraki.exceptions.APIError as e:
_LOGGER.debug("Serial: %r: Get appliance device performance: %r",
self._serial, e)
return []
class MerakiGetOrganizationSensorReadingsLatest(MerakiSectionOrg):
@property
def name(self):
......@@ -802,6 +821,22 @@ class MerakiOrganisation:
piggyback=self._adjust_piggyback(host=piggyback),
)
if _SEC_NAME_APPLIANCE_PERFORMANCE not in self.config.excluded_sections:
for device in devices_by_type[_API_NAME_DEVICE_TYPE_APPLIANCE]:
appliance_performance = MerakiGetDeviceAppliancePerformance(
config=self.config,
serial=device[_API_NAME_DEVICE_SERIAL]
).get_data(use_cache=self.config.use_cache)
if piggyback := self._get_device_piggyback({
_API_NAME_DEVICE_SERIAL: device[_API_NAME_DEVICE_SERIAL]
}, devices_by_serial):
yield self._make_section(
name=_SEC_NAME_APPLIANCE_PERFORMANCE,
data=appliance_performance,
piggyback=self._adjust_piggyback(host=piggyback),
)
if devices_by_type.get(_API_NAME_DEVICE_TYPE_SWITCH):
if _SEC_NAME_SWITCH_PORTS_STATUSES not in self.config.excluded_sections:
for switch in devices_by_type[_API_NAME_DEVICE_TYPE_SWITCH]:
......@@ -1111,9 +1146,9 @@ def _get_organisations(config: MerakiConfig, org_ids: Sequence[str]) -> Sequence
def get_proxy(raw_proxy: str) -> str | None:
match raw_proxy:
# export https_proxy=http://192.168.10.144:3128
# export http_proxy=http://192.168.10.144:3128
# export ftp_proxy=http://192.168.10.144:3128
# export https_proxy=http://192.168.10.144:3128
# export http_proxy=http://192.168.10.144:3128
# export ftp_proxy=http://192.168.10.144:3128
case 'NO_PROXY':
# environ['NO_PROXY'] = 'api.meraki.com' # did not work
environ['no_proxy'] = 'api.meraki.com' # explicit disable proxy for meraki
......
{'author': 'Th.L. (thl-cmk[at]outlook[dot]com)',
'description': 'Improved version of the build in Cisco Meraki special agent\n'
'\n'
'- new Appliance Utilization\n'
'- new Appliance Uplinks\n'
'- new Appliance VPNs\n'
'- improved Device Info\n'
......@@ -40,7 +41,8 @@
'cisco_meraki_org_wireless_ethernet_statuses.py',
'cisco_meraki_org_cellular_uplinks.py',
'cisco_meraki_organisations_api.py',
'cisco_meraki_org_networks.py'],
'cisco_meraki_org_networks.py',
'cisco_meraki_org_appliance_performance.py'],
'agents': ['special/agent_cisco_meraki'],
'checks': ['agent_cisco_meraki'],
'gui': ['metrics/cisco_meraki.py',
......@@ -51,13 +53,14 @@
'wato/check_parameters/cisco_meraki_organisations.py',
'wato/check_parameters/cisco_meraki_organisations_api.py',
'wato/check_parameters/cisco_meraki_org_wireless_device_status.py',
'wato/check_parameters/cisco_meraki_switch_ports_statuses.py'],
'wato/check_parameters/cisco_meraki_switch_ports_statuses.py',
'wato/check_parameters/cisco_meraki_org_appliance_performance.py'],
'lib': ['python3/cmk/special_agents/agent_cisco_meraki.py'],
'web': ['plugins/views/cisco_meraki.py',
'plugins/wato/agent_cisco_meraki.py']},
'name': 'cisco_meraki',
'title': 'Cisco Meraki special agent',
'version': '1.2.14-20240521',
'version': '1.3.0-20240623',
'version.min_required': '2.2.0b1',
'version.packaged': '2.2.0p24',
'version.packaged': '2.2.0p27',
'version.usable_until': '2.3.0b1'}
......@@ -52,6 +52,7 @@ from cmk.base.plugins.agent_based.utils.cisco_meraki import (
_SEC_NAME_APPLIANCE_UPLINKS, # type: ignore[import]
_SEC_NAME_APPLIANCE_UPLINKS_USAGE, # type: ignore[import]
_SEC_NAME_APPLIANCE_VPNS, # type: ignore[import]
_SEC_NAME_APPLIANCE_PERFORMANCE,
_SEC_NAME_SWITCH_PORTS_STATUSES, # type: ignore[import]
_SEC_NAME_WIRELESS_ETHERNET_STATUSES, # type: ignore[import]
_SEC_NAME_WIRELESS_DEVICE_STATUS, # type: ignore[import]
......@@ -107,22 +108,28 @@ def _valuespec_special_agent_cisco_meraki() -> ValueSpec:
ListChoice(
title=_('excluded Sections'),
choices=[
(_SEC_NAME_ORG_API_REQUESTS, _('API request')),
(_SEC_NAME_APPLIANCE_UPLINKS, _('Appliances uplinks')),
(_SEC_NAME_APPLIANCE_UPLINKS_USAGE, _('Appliances uplinks usage')),
(_SEC_NAME_APPLIANCE_VPNS, _('Appliances VPNs')),
(_SEC_NAME_CELLULAR_UPLINKS, _('Cellular devices uplinks')),
(_SEC_NAME_DEVICE_STATUSES, _('Devices status')),
(_SEC_NAME_DEVICE_UPLINKS_INFO, _('Devices uplink info')),
(_SEC_NAME_LICENSES_OVERVIEW, _('Licenses overview')),
(_SEC_NAME_SENSOR_READINGS, _('Sensors readings')),
(_SEC_NAME_SWITCH_PORTS_STATUSES, _('Switch ports status')),
(_SEC_NAME_WIRELESS_ETHERNET_STATUSES, _('Wireless devices ethernet status')),
(_SEC_NAME_WIRELESS_DEVICE_STATUS, _('Wireless devices SSIDs status')),
(_SEC_NAME_ORG_SWITCH_PORTS_STATUSES, _('Org switch port status (Early Access)')),
(_SEC_NAME_ORG_API_REQUESTS, _('API request (Organizaion)')),
(_SEC_NAME_APPLIANCE_UPLINKS, _('Appliances uplinks (Organizaion)')),
(_SEC_NAME_APPLIANCE_UPLINKS_USAGE, _('Appliances uplinks usage (Organizaion)')),
(_SEC_NAME_APPLIANCE_VPNS, _('Appliances VPNs (Organizaion)')),
(_SEC_NAME_APPLIANCE_PERFORMANCE, _('Appliances Utilization (Device)')),
(_SEC_NAME_CELLULAR_UPLINKS, _('Cellular devices uplinks (Organizaion)')),
(_SEC_NAME_DEVICE_STATUSES, _('Devices status (Organizaion)')),
(_SEC_NAME_DEVICE_UPLINKS_INFO, _('Devices uplink info (Organizaion)')),
(_SEC_NAME_LICENSES_OVERVIEW, _('Licenses overview (Organizaion)')),
(_SEC_NAME_SENSOR_READINGS, _('Sensors readings (Organizaion)')),
(_SEC_NAME_SWITCH_PORTS_STATUSES, _('Switch ports status (Device)')),
(_SEC_NAME_WIRELESS_ETHERNET_STATUSES, _('Wireless devices ethernet status (Organizaion)')),
(_SEC_NAME_WIRELESS_DEVICE_STATUS, _('Wireless devices SSIDs status (Device)')),
(_SEC_NAME_ORG_SWITCH_PORTS_STATUSES, _('Switch port status (Organizaion/Early Access)')),
],
help=_('Query only the selected sections. Default is Query all sections.'),
default_value=[_SEC_NAME_ORG_SWITCH_PORTS_STATUSES],
default_value=[
_SEC_NAME_ORG_SWITCH_PORTS_STATUSES,
_SEC_NAME_APPLIANCE_PERFORMANCE,
_SEC_NAME_SWITCH_PORTS_STATUSES,
_SEC_NAME_WIRELESS_DEVICE_STATUS,
],
)),
('orgs',
ListOfStrings(
......
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