Compare commits

...

21 Commits
2.0 ... master

Author SHA1 Message Date
d4b0c9497d Add support for proxy to Azure plugin. 2024-11-07 15:54:45 +01:00
270d9ac22c Rename some Azure plugin files just to be safe that they don't unexpectedly conflict with CheckMK's own built-in azure files. 2024-08-26 22:11:26 +02:00
36a998cfc6 Fix crit/warn bug with Defender alerts. 2024-08-25 23:20:00 +02:00
5fa472f450 Add initial plugin for monitoring Azure KeyVault and Firewall metrics, and Defender alerts. 2024-08-25 23:00:54 +02:00
52f417215b Forgot to add old Azure keyvault certificate checks. 2024-08-24 20:40:29 +02:00
62518edf2c Update AMD GPU plugin to work around item_name bug in CheckMK. 2024-07-25 12:38:09 +02:00
5ba99c1c15 Add AMD GPU plugin. 2024-06-17 17:27:56 +02:00
8f9970a40c Add some Sentry PDU changes that have been laying around for a few months. 2024-06-15 15:02:33 +02:00
775f9515a1 Add Sentry PDU (3rd line) outlet power checks, for CheckMK 1.6 and 2.2. 2024-02-27 08:47:28 +01:00
15f69ddc0c Update Graylog Input Metrics MKP due to download_url metadata. 2024-02-08 14:22:54 +01:00
3d9e54f470 Add Graylog Input Metrics plugin for CheckMK 2.2.0. 2024-02-08 14:16:13 +01:00
6de602a399 add selinux 2024-01-26 12:14:20 +02:00
44aa214b85 Add 'local checks/livestatusstats.py'
monitor some livestsatus stats, not sure it is completely right ..
2023-09-16 06:53:17 +00:00
bb424f90fe Add three Cisco plugins (GDOI, SLA, BGP) used for BCCD-13. 2023-07-03 15:14:25 +02:00
George Pochiscan
0aa461839b added checkmk checks for clever_pdu with firmware 1.2.0 and 1.3.0 2023-06-27 18:37:41 +03:00
George Pochiscan
d7ba6d93f4 added data checks 2023-05-31 12:47:38 +03:00
George Pochiscan
c26de603bb added sane check for line lenght 2023-05-26 12:24:19 +03:00
George Pochiscan
b3a43a96b1 added scality ring developed check 2023-02-24 12:45:49 +02:00
George Pochiscan
0f60e5725e added mk_informix extensions 2022-11-23 19:03:53 +02:00
a1b017d260 added 101 vers with 1.6p28 minver 2022-08-30 14:49:33 +03:00
George Pochiscan
2b522d2553 Added checks for Avocent ACS800 Devices for checkmk 2.0 and 2.1 2022-07-19 11:42:36 +03:00
88 changed files with 8657 additions and 0 deletions

3
.gitmodules vendored
View File

@ -1,3 +1,6 @@
[submodule "juniper"]
path = juniper
url = https://github.com/spearheadsys/check_mk.git
[submodule "check_mk-check-selinux"]
path = check_mk-check-selinux
url = https://code.spearhead.cloud/Spearhead/check_mk-check-selinux.git

Binary file not shown.

View File

@ -0,0 +1,106 @@
#!/usr/bin/env python3
#
# Copyright 2024 Spearhead Systems SRL
from cmk.base.plugins.agent_based.agent_based_api.v1 import (
register,
Service,
Result,
Metric,
State,
)
def discovery_amd_gpu(section):
name = section[0][0]
yield Service(item=name)
def check_state(alert_percentages, measured_percent):
if alert_percentages:
if alert_percentages[1] <= measured_percent:
return State.CRIT
elif alert_percentages[0] <= measured_percent:
return State.WARN
return State.OK
def get_levels(alert_levels, total=None):
if alert_levels == None:
return
if total == None:
return alert_levels
return (alert_levels[0] / 100 * total, alert_levels[1] / 100 * total)
def check_amd_gpu(item, params, section):
if item != section[0][0]:
return
gpu_percent = int(float(section[1][0]))
vram_bytes_used = int(section[2][0])
vram_bytes_total = int(section[3][0])
vram_bytes_free = max(0, vram_bytes_total - vram_bytes_used)
vram_mb_used = vram_bytes_used // 1048576
vram_mb_total = vram_bytes_total // 1048576
vram_mb_free = vram_bytes_free // 1048576
alert_gpu_percent = params.get("gpu_percent")
alert_vram_used_percent = params.get("vram_used_percent")
alert_vram_free_percent = params.get("vram_free_percent")
vram_used_percent = vram_bytes_used / vram_bytes_total * 100
vram_free_percent = 100 - vram_used_percent
yield Result(
state=check_state(alert_gpu_percent, gpu_percent),
summary=f"GPU: {gpu_percent}%"
)
yield Result(
state=check_state(alert_vram_free_percent, vram_free_percent),
summary=f"VRAM free: {vram_mb_free} MiB"
)
yield Result(
state=check_state(alert_vram_used_percent, vram_used_percent),
summary=f"VRAM used: {vram_mb_used} MiB"
)
yield Result(
state=State.OK,
summary=f"VRAM total: {vram_mb_total} MiB"
)
yield Metric(
name="gpu_percent",
value=gpu_percent,
levels=get_levels(alert_gpu_percent),
boundaries=(0, 100)
)
yield Metric(
name="vram_used",
value=vram_mb_used,
levels=get_levels(alert_vram_used_percent, vram_mb_total),
boundaries=(0, vram_mb_total)
)
yield Metric(
name="vram_free",
value=vram_mb_free,
levels=get_levels(alert_vram_free_percent, vram_mb_total),
boundaries=(0, vram_mb_total)
)
register.check_plugin(
name='amd_gpu',
service_name='AMD GPU - %s',
discovery_function=discovery_amd_gpu,
check_function=check_amd_gpu,
check_default_parameters={},
check_ruleset_name='amd_gpu',
)

View File

@ -0,0 +1,86 @@
#!/usr/bin/env python3
#
# Copyright 2024 Spearhead Systems SRL
from cmk.gui.i18n import _
from cmk.gui.plugins.wato.utils import (
CheckParameterRulespecWithItem,
rulespec_registry,
RulespecGroupCheckParametersHardware,
)
from cmk.gui.valuespec import Dictionary, Percentage, TextInput, Tuple
def _parameter_valuespec_amd_gpu():
return Dictionary(
title=_("GPU utilization"),
help=_(
"These metrics are queried directly from the AMD GPU. "
"Upper and lower levels can be specified for individual metrics."
),
elements=[
(
"gpu_percent",
Tuple(
title=_("GPU Used"),
help=_("If usage of total GPU compute goes above these percentages, issue alerts."),
elements=[
Percentage(
title=_("Warn if above"),
default_value=90
),
Percentage(
title=_("Crit if above"),
default_value=100
)
]
)
),
(
"vram_free_percent",
Tuple(
title=_("VRAM Free"),
help=_("If free VRAM goes above these percentages, issue alerts."),
elements=[
Percentage(
title="Warn if above",
default_value=70
),
Percentage(
title="Crit if above",
default_value=90
)
]
)
),
(
"vram_used_percent",
Tuple(
title=_("VRAM Used"),
help=_("If used VRAM goes above these percentages, issue alerts."),
elements=[
Percentage(
title="Warn if above",
default_value=70
),
Percentage(
title="Crit if above",
default_value=90
)
]
)
)
]
)
rulespec_registry.register(
CheckParameterRulespecWithItem(
check_group_name="amd_gpu",
group=RulespecGroupCheckParametersHardware,
match_type="dict",
parameter_valuespec=_parameter_valuespec_amd_gpu,
item_spec=lambda: TextInput(title=_("GPU")),
title=lambda: _("AMD GPU Metrics"),
)
)

View File

@ -0,0 +1,20 @@
# Copyright 2024 Spearhead Systems SRL
#
# This goes in C:\ProgramData\checkmk\agent\plugins. It should be added automatically by
# baking a new MSI after setting "Agent Rules" > "Deploy Custom Files With Agent" with
# "Deploy Custom Files With Agent" including "amd_gpu".
foreach ($Item in Get-ChildItem "HKLM:\SYSTEM\CurrentControlSet\Control\Class\{4d36e968-e325-11ce-bfc1-08002be10318}" -Name -Include 000*) {
$Name = Get-ItemPropertyValue "HKLM:\SYSTEM\CurrentControlSet\Control\Class\{4d36e968-e325-11ce-bfc1-08002be10318}\$Item" "DriverDesc"
if ($Name -match 'Radeon') {
$GpuBytesTotal = Get-ItemPropertyValue "HKLM:\SYSTEM\CurrentControlSet\Control\Class\{4d36e968-e325-11ce-bfc1-08002be10318}\$Item" "HardwareInformation.qwMemorySize"
$GpuRawName = Get-ItemPropertyValue "HKLM:\SYSTEM\CurrentControlSet\Control\Class\{4d36e968-e325-11ce-bfc1-08002be10318}\$Item" "HardwareInformation.AdapterString"
break
}
}
$GpuName = [System.Text.Encoding]::Unicode.GetString($GpuRawName)
$GpuPercent = (((Get-Counter "\GPU Engine(*)\Utilization Percentage" ).CounterSamples).CookedValue | measure -sum).sum
$GpuBytesUsed = (((Get-Counter "\GPU Process Memory(*)\Dedicated Usage").CounterSamples).CookedValue | measure -sum).sum
Write-Output "<<<amd_gpu:sep(0)>>>", $GpuName, $GpuPercent, $GpuBytesUsed, $GpuBytesTotal

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,92 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Copyright (C) 2019 tribe29 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 typing import List
from cmk.base.plugins.agent_based.agent_based_api.v1 import (
register,
Result,
Service,
SNMPTree,
startswith,
State,
)
from cmk.base.plugins.agent_based.agent_based_api.v1.type_defs import (
CheckResult,
DiscoveryResult,
StringTable,
)
Section = []
def parse_avocent_psu(string_table: List[StringTable]) -> Section:
return string_table[0][0]
register.snmp_section(
name="avocent_psu",
detect=startswith(".1.3.6.1.2.1.1.1.0", "Avocent"),
parse_function=parse_avocent_psu,
fetch=[
SNMPTree(
base=".1.3.6.1.4.1.10418.26.2.1.8",
oids=[
"1", #Number of PSU installed
"2", #PowerSupply1 state
"3", #PowerSupply2 state
],
),
],
)
def discovery_avocent_psu(section: Section) -> DiscoveryResult:
yield Service()
def _power_supply_status_descr(status_nr: str) -> str:
return {
"1": "Powered On",
"2": "Powered Off",
"9999": "Power Supply is not installed",
}.get(status_nr, status_nr)
def _power_supply_state(status_nr: str) -> State:
return {
"1": State.OK,
"2": State.CRIT,
"9999": State.OK
}.get(status_nr, State.UNKNOWN)
def check_avocent_psu(
section: Section,
) -> CheckResult:
number_of_psu=section[0]
state_psu_1=section[1]
state_psu_2=section[2]
yield Result(
state=State.OK,
summary="Number of PSU installed: %s" % number_of_psu,
)
yield Result(
state=_power_supply_state(state_psu_1),
summary="Power Supply 1 is %s" % _power_supply_status_descr(state_psu_1),
)
yield Result(
state=_power_supply_state(state_psu_2),
summary="Power Supply 2 is %s" % _power_supply_status_descr(state_psu_2),
)
register.check_plugin(
name="avocent_psu",
sections=["avocent_psu"],
service_name="Power Supplies",
discovery_function=discovery_avocent_psu,
check_function=check_avocent_psu
)

View File

@ -0,0 +1,134 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Copyright (C) 2019 tribe29 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.
import dataclasses
from typing import Mapping
from .agent_based_api.v1 import (
contains,
get_value_store,
Metric,
register,
Result,
Service,
SNMPTree,
State,
startswith
)
from .agent_based_api.v1.type_defs import CheckResult, DiscoveryResult, StringTable
from .utils.temperature import check_temperature, TempParamType
@dataclasses.dataclass(frozen=True)
class Sensor:
value: float
@dataclasses.dataclass(frozen=True)
class VoltageSensor(Sensor):
...
@dataclasses.dataclass(frozen=True)
class Section:
temperature_sensors: Mapping[str, Sensor]
voltage_sensors: Mapping[str, Sensor]
temperature_sensors_name = ['CPU','Board']
voltage_sensors_name = ['PSU 1','PSU 2']
def parse_avocent_sensors(string_table: StringTable) -> Section:
temperature_sensors = {}
voltage_sensors = {}
position = 0
for temp_sens_name in temperature_sensors_name:
temperature_sensors[temp_sens_name] = Sensor(value=int(string_table[0][position]))
position +=1
pos = 2
for volt_sens_name in voltage_sensors_name:
voltage_sensors[volt_sens_name] = Sensor(value=float(string_table[0][pos])/100)
pos += 1
return Section(
temperature_sensors=temperature_sensors,
voltage_sensors=voltage_sensors,
)
register.snmp_section(
name="avocent_sensors",
detect=startswith(".1.3.6.1.2.1.1.1.0", "Avocent"),
parse_function=parse_avocent_sensors,
fetch=SNMPTree(
base=".1.3.6.1.4.1.10418.26.2.7",
oids=[
"1", #acsSensorsInternalCurrentCPUTemperature
"6", #acsSensorsInternalCurrentBoardTemperature
"17", #acsSensorsVoltagePowerSupply1
"18", #acsSensorsVoltagePowerSupply2
],
),
)
def discover_avocent_voltage_sensors(section: Section) -> DiscoveryResult:
yield from (Service(item=sensor_name) for sensor_name in section.voltage_sensors)
def check_avocent_voltage_sensors(
item: str,
section: Section,
) -> CheckResult:
if not (sensor := section.voltage_sensors.get(item)):
return
yield Result(
state=State.OK,
summary=f"{sensor.value:.1f} V",
)
yield Metric(
name="voltage",
value=sensor.value,
)
register.check_plugin(
name="avocent_voltage_sensors",
sections=["avocent_sensors"],
service_name="Voltage %s",
discovery_function=discover_avocent_voltage_sensors,
check_function=check_avocent_voltage_sensors,
)
def discover_avocent_sensors_temp(section: Section) -> DiscoveryResult:
yield from (Service(item=sensor_name) for sensor_name in section.temperature_sensors)
def check_avocent_sensors_temp(
item: str,
params: TempParamType,
section: Section,
) -> CheckResult:
if not (sensor := section.temperature_sensors.get(item)):
return
yield from check_temperature(
reading=sensor.value,
params=params,
unique_name=item,
value_store=get_value_store(),
)
register.check_plugin(
name="avocent_sensors_temp",
sections=["avocent_sensors"],
service_name="Temperature %s",
discovery_function=discover_avocent_sensors_temp,
check_function=check_avocent_sensors_temp,
check_ruleset_name="temperature",
check_default_parameters={"device_levels_handling": "devdefault"},
)

View File

@ -0,0 +1,152 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Copyright (C) 2019 tribe29 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 ..agent_based_api.v1 import (
all_of,
any_of,
contains,
equals,
exists,
not_contains,
not_equals,
not_exists,
not_startswith,
startswith,
)
# We are not sure how to safely detect the UCD SNMP Daemon. We know that
# it is mainly used on Linux, but not only. But fetching and OID outside
# of the info area for scanning is not a good idea. It will slow down
# scans for *all* hosts.
# ---ucd cpu load---------------------------------------------------------
# We prefer HOST-RESOURCES-MIB implementation but not in case
# of check 'ucd_cpu_load' because the HR-MIB has not data
# about cpu load
# ---general ucd/hr-------------------------------------------------------
HR = exists(".1.3.6.1.2.1.25.1.1.0")
_NOT_HR = not_exists(".1.3.6.1.2.1.25.1.1.0")
UCD = any_of(
contains(".1.3.6.1.2.1.1.1.0", "linux"),
contains(".1.3.6.1.2.1.1.1.0", "cmc-tc"),
contains(".1.3.6.1.2.1.1.1.0", "hp onboard administrator"),
contains(".1.3.6.1.2.1.1.1.0", "barracuda"),
contains(".1.3.6.1.2.1.1.1.0", "pfsense"),
contains(".1.3.6.1.2.1.1.1.0", "genugate"),
contains(".1.3.6.1.2.1.1.1.0", "bomgar"),
contains(".1.3.6.1.2.1.1.1.0", "pulse secure"),
contains(".1.3.6.1.2.1.1.1.0", "microsens"),
contains(".1.3.6.1.2.1.1.1.0", "avocent"),
all_of( # Artec email archive appliances
equals(".1.3.6.1.2.1.1.2.0", ".1.3.6.1.4.1.8072.3.2.10"),
contains(".1.3.6.1.2.1.1.1.0", "version"),
contains(".1.3.6.1.2.1.1.1.0", "serial"),
),
all_of(
equals(".1.3.6.1.2.1.1.1.0", ""),
exists(".1.3.6.1.4.1.2021.*"),
),
)
_NOT_UCD = all_of(
# This is an explicit negation of the constant above.
# We don't have a generic negation function as we want
# discourage constructs like this.
# In the future this will be acomplished using the 'supersedes'
# feature (according to CMK-4232), and this can be removed.
not_contains(".1.3.6.1.2.1.1.1.0", "linux"),
not_contains(".1.3.6.1.2.1.1.1.0", "cmc-tc"),
not_contains(".1.3.6.1.2.1.1.1.0", "hp onboard administrator"),
not_contains(".1.3.6.1.2.1.1.1.0", "barracuda"),
not_contains(".1.3.6.1.2.1.1.1.0", "pfsense"),
not_contains(".1.3.6.1.2.1.1.1.0", "genugate"),
not_contains(".1.3.6.1.2.1.1.1.0", "bomgar"),
not_contains(".1.3.6.1.2.1.1.1.0", "pulse secure"),
not_contains(".1.3.6.1.2.1.1.1.0", "microsens"),
not_contains(".1.3.6.1.2.1.1.1.0", "avocent"),
any_of( # Artec email archive appliances
not_equals(".1.3.6.1.2.1.1.2.0", ".1.3.6.1.4.1.8072.3.2.10"),
not_contains(".1.3.6.1.2.1.1.1.0", "version"),
not_contains(".1.3.6.1.2.1.1.1.0", "serial"),
),
)
PREFER_HR_ELSE_UCD = all_of(UCD, _NOT_HR)
# ---helper---------------------------------------------------------------
# Within _is_ucd or _is_ucd_mem we make use of a whitelist
# in order to expand this list of devices easily.
_UCD_MEM = any_of(
# Devices for which ucd_mem should be used
# if and only if HR-table is not available
all_of(
contains(".1.3.6.1.2.1.1.1.0", "pfsense"),
not_exists(".1.3.6.1.2.1.25.1.1.0"),
),
all_of(
contains(".1.3.6.1.2.1.1.1.0", "ironport model c3"),
not_exists(".1.3.6.1.2.1.25.1.1.0"),
),
all_of(
contains(".1.3.6.1.2.1.1.1.0", "bomgar"),
not_exists(".1.3.6.1.2.1.25.1.1.0"),
),
all_of(
# Astaro and Synology are Linux but should use hr_mem
# Otherwise Cache/Buffers are included in used memory
# generating critical state
not_startswith(".1.3.6.1.2.1.1.2.0", ".1.3.6.1.4.1.8072."),
# Otherwise use ucd_mem for listed devices in UCD.
UCD,
),
)
_NOT_UCD_MEM = all_of(
# This is an explicit negation of the constant above.
# We don't have a generic negation function as we want
# discourage constructs like this.
# In the future this will be acomplished using the 'supersedes'
# feature (according to CMK-4232), and this can be removed.
any_of(
not_contains(".1.3.6.1.2.1.1.1.0", "pfsense"),
exists(".1.3.6.1.2.1.25.1.1.0"),
),
any_of(
not_contains(".1.3.6.1.2.1.1.1.0", "ironport model c3"),
exists(".1.3.6.1.2.1.25.1.1.0"),
),
any_of(
not_contains(".1.3.6.1.2.1.1.1.0", "bomgar"),
exists(".1.3.6.1.2.1.25.1.1.0"),
),
any_of(
# Astaro and Synology are Linux but should use hr_mem
# Otherwise Cache/Buffers are included in used memory
# generating critical state
startswith(".1.3.6.1.2.1.1.2.0", ".1.3.6.1.4.1.8072."),
# Otherwise use ucd_mem for listed devices in UCD.
_NOT_UCD,
),
)
# Some devices report incorrect data on both HR and UCD, eg. F5 BigIP
_NOT_BROKEN_MEM = all_of(
not_startswith(".1.3.6.1.2.1.1.2.0", ".1.3.6.1.4.1.3375"),
not_startswith(".1.3.6.1.2.1.1.2.0", ".1.3.6.1.4.1.2620"),
)
# ---memory---------------------------------------------------------------
USE_UCD_MEM = all_of(_NOT_BROKEN_MEM, _UCD_MEM)
USE_HR_MEM = all_of(_NOT_BROKEN_MEM, _NOT_UCD_MEM)

View File

@ -0,0 +1,15 @@
title: Avocent ACS 800 CPU and Board Temperature
agents: snmp
catalog: hw/network/avocent
license: GPLv2
distribution: check_mk
description:
Checks by SNMP the Temperature for CPU and Board sensors of Avocent ACS 800 devices.
Return {OK} if no temperature rule is created, otherwise based on the level in
the configured temperature rule.
item:
CPU Temperature and Board Temperature
discovery:
One service is created for CPU Temperature and one service for Board Temperature

View File

@ -0,0 +1,15 @@
title: Avocent ACS 800 Power Supply Voltage Sensors
agents: snmp
catalog: hw/network/avocent
license: GPLv2
distribution: check_mk
description:
Checks by SNMP the power supply voltage sensors of Avocent ACS 800 devices.
Returns {OK} Always.
item:
The Voltage for each power supply.
discovery:
Two services created, one for each Power Supply

View File

@ -0,0 +1,72 @@
#!/usr/bin/env python3
# Copyright (C) 2023 Spearhead Systems SRL - License: GNU General Public License v2
import json
from datetime import datetime, timezone
from cmk.base.plugins.agent_based.agent_based_api.v1 import register, Result, Service, State
# Convert JSON entries into dictionaries indexed by certificate name.
def parse_keyvault(string_table):
raw_json = ""
cert_data = []
for row in string_table:
line = row[0]
raw_json += line
if line == "]":
cert_data.extend(json.loads(raw_json))
raw_json = ""
lookup = {}
for cert in cert_data:
lookup[cert["name"]] = cert
return lookup
register.agent_section(
name="azure_keyvault",
parse_function=parse_keyvault
)
# Produce a list of certificates based on the parsed output.
def discover_keyvault(section):
for name, details in sorted(section.items()):
yield Service(item=name)
# Given a specific certificate, look it up in the parsed output, and produce
# results on that service based upon the certificate's expiry.
def check_keyvault(item, params, section):
warn_days = params.get("warn_days")
crit_days = params.get("crit_days")
cert = section.get(item)
if cert is None:
return
expires = datetime.fromisoformat(cert["attributes"]["expires"])
now = datetime.now(timezone.utc)
remaining_days = (expires - now).days
state = State.OK
if crit_days is not None and remaining_days < crit_days:
state = State.CRIT
elif warn_days is not None and remaining_days < warn_days:
state = State.WARN
yield Result(state=state, summary="Expires in %d days" % remaining_days)
register.check_plugin(
name="azure_keyvault",
service_name="Azure Keyvault Certificate %s",
check_function=check_keyvault,
check_default_parameters={},
check_ruleset_name="azure_keyvault",
discovery_function=discover_keyvault,
)

View File

@ -0,0 +1,26 @@
#!/bin/bash
# Copyright (C) 2023 Spearhead Systems SRL - License: GNU General Public License v2
az=/usr/bin/az
set -euo pipefail
if [ "$#" -lt 4 ]; then
echo "Usage: $0 <tenant> <user> <password> <vault1> ... [vaultN]" >&2
exit 1
fi
tenant="$1"
user="$2"
password="$3"
vaults="${@:4}"
echo "<<<azure_keyvault:sep(0)>>>"
"$az" login --service-principal --tenant="$tenant" --user="$user" --password="$password" > /dev/null
for vault in $vaults; do
"$az" keyvault certificate list --vault-name="$vault"
done
"$az" logout

View File

@ -0,0 +1,16 @@
#!/usr/bin/env python3
# Copyright (C) 2023 Spearhead Systems SRL - License: GNU General Public License v2
def agent_azure_keyvault(params, hostname, ipaddress):
tenant = params["tenant"]
client = params["client"]
secret = params["secret"]
args = [tenant, client, secret]
for vault in params["vaults"]:
args.extend([vault.strip()])
return args
special_agent_info["azure_keyvault"] = agent_azure_keyvault

View File

@ -0,0 +1,110 @@
#!/usr/bin/env python3
# Copyright (C) 2023 Spearhead Systems SRL - License: GNU General Public License v2
import copy
from cmk.gui.i18n import _
from cmk.gui.plugins.wato.utils import (
rulespec_registry,
HostRulespec,
IndividualOrStoredPassword,
RulespecGroupCheckParametersDiscovery,
CheckParameterRulespecWithItem,
RulespecGroupCheckParametersApplications,
)
from cmk.gui.watolib.rulespecs import Rulespec
from cmk.gui.valuespec import (
Dictionary,
TextInput,
Integer,
ListOfStrings,
Password
)
def _valuespec_special_agents_azure_keyvault_check():
return Dictionary(
title=_("Azure Key Vault Certificate Checks"),
optional_keys=["warn_days", "crit_days"],
elements=[
(
"warn_days",
Integer(
minvalue=0,
default_value=30,
title=_("Certificate Days to Warn"),
help=_(
"How many days to warn before a certificate in this key vault will expire"
),
),
),
(
"crit_days",
Integer(
minvalue=0,
default_value=3,
title=_("Certificate Days to Crit"),
help=_(
"How many days to crit before a certificate in this key vault will expire"
),
),
),
],
)
def _valuespec_special_agents_azure_keyvault_discovery():
return Dictionary(
title=_("Azure Key Vault Certificate Discovery"),
elements=[
(
"tenant",
TextInput(
title=_("Tenant ID / Directory ID"),
allow_empty=False,
size=45,
),
),
(
"client",
TextInput(
title=_("Client ID / Application ID"),
allow_empty=False,
size=45,
),
),
(
"secret",
IndividualOrStoredPassword(
# Password(
title=_("Client Secret"),
allow_empty=False,
size=45,
),
),
(
"vaults",
ListOfStrings(
title=_("Keyvaults"),
allow_empty=False,
),
),
],
)
rulespec_registry.register(
CheckParameterRulespecWithItem(
check_group_name="azure_keyvault",
group=RulespecGroupCheckParametersApplications,
match_type='dict',
parameter_valuespec=_valuespec_special_agents_azure_keyvault_check,
)
)
rulespec_registry.register(
HostRulespec(
group=RulespecGroupCheckParametersDiscovery,
match_type='dict',
name="special_agents:azure_keyvault",
valuespec=_valuespec_special_agents_azure_keyvault_discovery,
)
)

Binary file not shown.

View File

@ -0,0 +1,278 @@
#!/usr/bin/env python3
# Copyright (C) 2024 Spearhead Systems SRL
import json
from datetime import datetime, timezone
from cmk.base.plugins.agent_based.agent_based_api.v1 import register, Result, Service, State, Metric
def check_state_below(alert_percentages, measured_percent):
if alert_percentages:
if alert_percentages[1] >= measured_percent:
return State.CRIT
elif alert_percentages[0] >= measured_percent:
return State.WARN
return State.OK
def check_state_above(alert_percentages, measured_percent):
if alert_percentages:
if alert_percentages[1] <= measured_percent:
return State.CRIT
elif alert_percentages[0] <= measured_percent:
return State.WARN
return State.OK
# Convert JSON entries into dictionaries indexed by name. We're assuming here
# that the name is unique across AZs and resource groups. If not, add the
# 'location' and 'resource_group' fields in each object to the name.
def parse(string_table):
lookup = {}
for json_data in string_table:
obj = json.loads(json_data[0])
name = obj["name"]
group = obj["resource_group"]
lookup[f"{name}#{group}"] = obj
return lookup
# Produce a list of Azure objects for discovery.
def discover(section):
for name, details in sorted(section.items()):
yield Service(item=name)
# Given a specific keyvault metric, look it up in the parsed output, and produce
# results on that service based upon the metric's range.
def check_keyvault(item, params, section):
vault = section.get(item)
if vault is None:
return
metrics = vault["metrics"]
availability = metrics.get("Availability")
capacity = metrics.get("SaturationShoebox")
latency = metrics.get("ServiceApiLatency")
hits = metrics.get("ServiceApiHit")
results = metrics.get("ServiceApiResult")
alert_availability_percent = params.get("availability")
alert_capacity_percent = params.get("capacity")
alert_latency_milliseconds = params.get("latency")
if availability is not None:
yield Result(
state=check_state_below(alert_availability_percent, availability),
summary=f"Availability: {availability}%",
)
yield Metric(
name="availability",
value=availability,
boundaries=(0, 100),
)
else:
yield Result(
state=State.UNKNOWN,
summary="Availability: N/A",
)
if capacity is not None:
yield Result(
state=check_state_above(alert_capacity_percent, capacity),
summary=f"Capacity: {capacity}%"
)
yield Metric(
name="capacity",
value=capacity,
boundaries=(0, 100),
)
else:
yield Result(
state=State.UNKNOWN,
summary="Capacity: N/A",
)
if latency is not None:
yield Result(
state=check_state_above(alert_latency_milliseconds, latency),
summary=f"Latency: {latency}ms",
)
yield Metric(
name="latency",
value=latency,
boundaries=(0, None),
)
else:
yield Result(
state=State.UNKNOWN,
summary="Latency: N/A",
)
if hits is not None:
yield Metric(
name="hits",
value=hits,
boundaries=(0, None),
)
else:
yield Result(
state=State.UNKNOWN,
summary="Hits: N/A",
)
if results is not None:
yield Metric(
name="results",
value=results,
boundaries=(0, None),
)
else:
yield Result(
state=State.UNKNOWN,
summary="Results: N/A",
)
# Given a specific firewall metric, look it up in the parsed output, and produce
# results on that service based upon the metric's range.
def check_firewall(item, params, section):
firewall = section.get(item)
if firewall is None:
return
metrics = firewall["metrics"]
availability = metrics.get("FirewallHealth")
throughput = metrics.get("Throughput")
latency = metrics.get("FirewallLatencyPng")
alert_availability_percent = params.get("availability")
alert_latency_milliseconds = params.get("latency")
if availability is not None:
yield Result(
state=check_state_below(alert_availability_percent, availability),
summary=f"Availability: {availability}%",
)
yield Metric(
name="availability",
value=availability,
boundaries=(0, 100)
)
else:
yield Result(
state=State.UNKNOWN,
summary="Availability: N/A",
)
if latency is not None:
yield Result(
state=check_state_above(alert_latency_milliseconds, latency),
summary=f"Latency: {latency}ms",
)
yield Metric(
name="latency",
value=latency,
boundaries=(0, None)
)
else:
yield Result(
state=State.UNKNOWN,
summary="Latency: N/A",
)
if throughput is not None:
yield Metric(
name="throughput",
value=throughput,
boundaries=(0, None)
)
else:
yield Result(
state=State.UNKNOWN,
summary="Throughput: N/A",
)
def check_defender(item, params, section):
alert = section.get(item)
if alert is None:
return
details = alert["alert"]
status = details["status"]
if status != "Active" and status != "InProgress":
return
severity = details["severity"]
url = details["url"]
info = details["info"]
if severity == "High":
state = State.CRIT
elif severity == "Medium":
state = State.WARN
elif severity == "Low":
state = State.OK
else:
state = State.UNKNOWN
yield Result(
state=state,
summary=f"{status}: {info}: {url}"
)
register.agent_section(
name="azure_keyvault",
parse_function=parse
)
register.check_plugin(
name="azure_keyvault",
service_name="Azure Keyvault Metric %s",
check_function=check_keyvault,
check_default_parameters={},
check_ruleset_name="azure_keyvault",
discovery_function=discover,
)
register.agent_section(
name="azure_firewall",
parse_function=parse
)
register.check_plugin(
name="azure_firewall",
service_name="Azure Firewall Metric %s",
check_function=check_firewall,
check_default_parameters={},
check_ruleset_name="azure_firewall",
discovery_function=discover,
)
register.agent_section(
name="azure_defender",
parse_function=parse
)
register.check_plugin(
name="azure_defender",
service_name="Azure Defender Alert %s",
check_function=check_defender,
check_default_parameters={},
check_ruleset_name="azure_defender",
discovery_function=discover,
)

View File

@ -0,0 +1,184 @@
#!/usr/bin/env python3
# Copyright (C) 2024 Spearhead Systems SRL
from urllib import request, parse, error
from datetime import datetime, timezone, timedelta
import json
import sys
import re
VAULT_METRICS = [
'Availability',
'SaturationShoebox',
'ServiceApiLatency',
'ServiceApiHit',
'ServiceApiResult',
]
FIREWALL_METRICS = [
'FirewallHealth',
'Throughput',
'FirewallLatencyPng',
]
REGION_RE = re.compile('/locations/(.+?)/')
RESOURCE_GROUP_RE = re.compile('/resourceGroups/(.+?)/')
# https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/request-limits-and-throttling
def get_url(req, default):
try:
res = request.urlopen(req)
return res.read()
except error.HTTPError as e:
if e.code == 429:
return default
else:
raise e
def set_proxy(req, proxy):
if proxy is None or proxy == '':
return
match = re.match('(https?)://(.+?)/?$', proxy, re.I)
req.set_proxy(match[2], match[1].lower())
def get_token(tenant, username, password, proxy):
data = parse.urlencode({
'username': username,
'password': password,
'grant_type': 'password',
'claims': '{"access_token": {"xms_cc": {"values": ["CP1"]}}}',
'scope': 'https://management.core.windows.net//.default offline_access openid profile',
'client_info': 1,
# This is actually the client ID of the Azure CLI tools
'client_id': '04b07795-8ddb-461a-bbee-02f9e1bf7b46',
})
req = request.Request(f'https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token',
data=str.encode(data))
set_proxy(req, proxy)
res = get_url(req, None)
if res is None:
return
token_data = json.loads(res)
token = token_data['access_token']
return token
def get_json(token, proxy, path, version='2023-07-01'):
url = f"https://management.azure.com{path}{'?' in path and '&' or '?'}api-version={version}"
req = request.Request(url, headers={'Authorization': f'Bearer {token}'})
set_proxy(req, proxy)
res = get_url(req, "[]")
data = json.loads(res)
return data['value']
def list_subscriptions(token, proxy):
return get_json(token, proxy, '/subscriptions')
def list_vaults(token, proxy, subscription):
return get_json(token, proxy, f'/subscriptions/{subscription}/resources?$filter=resourceType%20eq%20%27Microsoft.KeyVault%2Fvaults%27')
def list_firewalls(token, proxy, subscription):
return get_json(token, proxy, f'/subscriptions/{subscription}/resources?$filter=resourceType%20eq%20%27Microsoft.Network%2FazureFirewalls%27')
def list_defender_alerts(token, proxy, subscription):
return get_json(token, proxy, f'/subscriptions/{subscription}/providers/Microsoft.Security/alerts', '2022-01-01')
def get_recent_metrics(token, proxy, path, metrics):
end = datetime.now()
start = end - timedelta(minutes=2)
start_str = start.isoformat().split('.')[0] + 'Z'
end_str = end.isoformat().split('.')[0] + 'Z'
metrics_str = ','.join(metrics)
return get_json(token, proxy, f'{path}/providers/microsoft.insights/metrics?metricnames={metrics_str}&timespan={start_str}/{end_str}', '2023-10-01')
def metrics_to_lookup(metrics):
lookup = {}
for metric in metrics:
name = metric['name']['value']
series = metric['timeseries']
if series:
value = series[0]['data'][-1]
key = next(filter(lambda foo: foo != 'timeStamp', value), None)
lookup[name] = value.get(key)
return lookup
def get_args(argv):
if (len(argv) != 5 and len(argv) != 6) or argv[1] not in ['keyvault', 'firewall', 'defender']:
print(f"{sys.argv[0]} <command> <tenant ID> <username> <password> <proxy>", file=sys.stderr)
print(f"Valid commands are: 'keyvault', 'firewall', 'defender'", file=sys.stderr)
print(f"Proxy is an optional argument", file=sys.stderr)
exit(1)
return argv[1], argv[2], argv[3], argv[4], (argv[5] if len(argv) == 6 else None)
def print_json(obj):
print(json.dumps(obj))
command, tenant, username, password, proxy = get_args(sys.argv)
token = get_token(tenant, username, password, proxy)
for subscription in list_subscriptions(token, proxy):
subscription_id = subscription['subscriptionId']
if command == 'defender':
for alert in list_defender_alerts(token, proxy, subscription_id):
properties = alert['properties']
status = properties['status']
if not status in ['Active', 'InProgress']:
continue
print_json({
'type': command,
'name': alert['name'],
'location': re.search(REGION_RE, alert['id'])[1],
'resource_group': re.search(RESOURCE_GROUP_RE, alert['id'])[1],
'alert': {
'status': status,
'severity': properties['severity'],
'url': properties['alertUri'],
'info': properties['alertDisplayName']
}
})
elif command == 'firewall':
for firewall in list_firewalls(token, proxy, subscription_id):
metrics = get_recent_metrics(token, proxy, firewall['id'], FIREWALL_METRICS)
print_json({
'type': command,
'name': firewall['name'],
'location': firewall['location'],
'resource_group': re.search(RESOURCE_GROUP_RE, firewall['id'])[1],
'metrics': metrics_to_lookup(metrics),
})
elif command == 'keyvault':
for vault in list_vaults(token, proxy, subscription_id):
metrics = get_recent_metrics(token, proxy, vault['id'], VAULT_METRICS)
print_json({
'type': command,
'name': vault['name'],
'location': vault['location'],
'resource_group': re.search(RESOURCE_GROUP_RE, vault['id'])[1],
'metrics': metrics_to_lookup(metrics),
})

View File

@ -0,0 +1,6 @@
#!/bin/bash
echo '<<<azure_defender:sep(0)>>>'
dir=$(dirname -- "${BASH_SOURCE[0]}")
"$dir"/agent_azure_common defender "$1" "$2" "$3" "$4"

View File

@ -0,0 +1,6 @@
#!/bin/bash
echo '<<<azure_firewall:sep(0)>>>'
dir=$(dirname -- "${BASH_SOURCE[0]}")
"$dir"/agent_azure_common firewall "$1" "$2" "$3" "$4"

View File

@ -0,0 +1,6 @@
#!/bin/bash
echo '<<<azure_keyvault:sep(0)>>>'
dir=$(dirname -- "${BASH_SOURCE[0]}")
"$dir"/agent_azure_common keyvault "$1" "$2" "$3" "$4"

View File

@ -0,0 +1,25 @@
#!/usr/bin/env python3
# Copyright (C) 2024 Spearhead Systems SRL
import cmk.utils.password_store
def agent_azure_args(params, hostname, ipaddress):
# Extract password either from params, or from password store:
# ('password', '<some password>'): password is in params directly
# ('store', '<password name>'): password must be looked up in store by name
password_info = params["password"]
if password_info[0] == "password":
password = password_info[1]
else:
password = cmk.utils.password_store.extract(password_info[1])
return [
params["tenant"],
params["username"],
password,
params.get("proxy") or "" # optional
]
special_agent_info["azure_keyvault"] = agent_azure_args
special_agent_info["azure_firewall"] = agent_azure_args
special_agent_info["azure_defender"] = agent_azure_args

View File

@ -0,0 +1,217 @@
#!/usr/bin/env python3
# Copyright (C) 2024 Spearhead Systems SRL
import copy
from cmk.gui.i18n import _
from cmk.gui.plugins.wato.utils import (
rulespec_registry,
HostRulespec,
IndividualOrStoredPassword,
RulespecGroupCheckParametersDiscovery,
CheckParameterRulespecWithItem,
RulespecGroupCheckParametersApplications,
)
from cmk.gui.watolib.rulespecs import Rulespec
from cmk.gui.valuespec import (
Dictionary,
TextInput,
Integer,
ListOfStrings,
Password
)
def _discovery(title):
return Dictionary(
title=_(title),
required_keys=["tenant", "username", "password"],
elements=[
(
"tenant",
TextInput(
title=_("Tenant ID"),
allow_empty=False,
),
),
(
"username",
TextInput(
title=_("Username"),
allow_empty=False,
),
),
(
"password",
IndividualOrStoredPassword(
# Password(
title=_("Password"),
allow_empty=False,
),
),
(
"proxy",
TextInput(
title=_("Proxy"),
),
),
],
)
def _valuespec_special_agents_azure_keyvault_discovery():
return _discovery("Azure Key Vault Metrics Discovery")
def _valuespec_special_agents_azure_firewall_discovery():
return _discovery("Azure Firewall Metrics Discovery")
def _valuespec_special_agents_azure_defender_discovery():
return _discovery("Azure Defender Alerts Discovery")
def _valuespec_special_agents_azure_keyvault_check():
return Dictionary(
title=_("Azure Key Vault Metric Checks"),
elements=[
(
"availability",
Tuple(
title=_("Availability"),
help=_("If drops below these percentages over the past minute, issue alert"),
elements=[
Percentage(
title=_("Warn if below"),
default_value=98
),
Percentage(
title=_("Crit if below"),
default_value=90
)
]
)
),
(
"capacity",
Tuple(
title=_("Capacity used"),
help=_("If goes above these percentages over the past minute, issue alert"),
elements=[
Percentage(
title=_("Warn if above"),
default_value=80
),
Percentage(
title=_("Crit if above"),
default_value=98
)
]
)
),
(
"latency",
Tuple(
title=_("Request latency"),
help=_("If goes above the average milliseconds over the past minute, issue alert"),
elements=[
Integer(
title=_("Warn if above"),
default_value=100,
minvalue=0,
),
Integer(
title=_("Crit if above"),
default_value=2000,
minvalue=0,
)
]
)
),
],
)
def _valuespec_special_agents_azure_firewall_check():
return Dictionary(
title=_("Azure Firewall Metric Checks"),
elements=[
(
"availability",
Tuple(
title=_("Availability"),
help=_("If drops below these percentages over the past minute, issue alert"),
elements=[
Percentage(
title=_("Warn if below"),
default_value=98
),
Percentage(
title=_("Crit if below"),
default_value=90
)
]
)
),
(
"latency",
Tuple(
title=_("Request latency"),
help=_("If goes above the average milliseconds over the past minute, issue alert"),
elements=[
Integer(
title=_("Warn if above"),
default_value=100,
minvalue=0,
),
Integer(
title=_("Crit if above"),
default_value=2000,
minvalue=0,
)
]
)
),
],
)
rulespec_registry.register(
HostRulespec(
name="special_agents:azure_keyvault",
group=RulespecGroupCheckParametersDiscovery,
match_type='dict',
valuespec=_valuespec_special_agents_azure_keyvault_discovery,
)
)
rulespec_registry.register(
HostRulespec(
name="special_agents:azure_firewall",
group=RulespecGroupCheckParametersDiscovery,
match_type='dict',
valuespec=_valuespec_special_agents_azure_firewall_discovery,
)
)
rulespec_registry.register(
HostRulespec(
name="special_agents:azure_defender",
group=RulespecGroupCheckParametersDiscovery,
match_type='dict',
valuespec=_valuespec_special_agents_azure_defender_discovery,
)
)
rulespec_registry.register(
CheckParameterRulespecWithItem(
check_group_name="azure_keyvault",
group=RulespecGroupCheckParametersApplications,
match_type="dict",
parameter_valuespec=_valuespec_special_agents_azure_keyvault_check,
item_spec=lambda: TextInput(title=_("Key Vault")),
title=lambda: _("Azure Key Vault Metrics"),
)
)
rulespec_registry.register(
CheckParameterRulespecWithItem(
check_group_name="azure_firewall",
group=RulespecGroupCheckParametersApplications,
match_type="dict",
parameter_valuespec=_valuespec_special_agents_azure_firewall_check,
item_spec=lambda: TextInput(title=_("Firewall")),
title=lambda: _("Azure Firewall Metrics"),
)
)

@ -0,0 +1 @@
Subproject commit d041278cbe5e329ee3a8a63b1d21530f69fbfaa2

Binary file not shown.

View File

@ -0,0 +1 @@
This is a modification of a GPL plugin to additionally support CISCO-BGP4-MIB::CbgpPeer3Entry

View File

@ -0,0 +1,665 @@
#!/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-26
#
# Monitor status of Cisco BGP Peer (IPv4 and IPv6)
#
# 2018-05-24: changed counters to 1/s
# 2018-05-25: a lot of code cleanup
# packet name changed from cisco_bgp to cisco_bgp_peer
# added support of more then one address family per peer
# (changed item from remoteip to remoteip+familyname, rewrite of parer, inventory and check function)
# 2018-05-27: changed scan function from '.1.3.6.1.4.1.9.9.187.1.2.7.1.3.* to sysdecr contains cisco
# 2018-05-28: changed wato, added peer alias, state if not found, infotext values
# 2018-05-29: fixed longoutpout (removed not configured)
# 2018-11-02: modified scanfunction (from "find 'cisco' =-1" to "'cisco' in OID"
# 2019-18-02: added fix for empty values ("" instead of "0") sugested by Laurent Barbier (lbarbier[at]arkane-studios[dot]com)
# 2020-02-24: added workaround for missing cbgpPeer2AddrFamily (example L2VPN EVPN peers, Fix for jonale82[at]gmail[dot]com)
# 2020-03-02: changed handling of perfdata, add only data the are really there (not None, instead of setting them to 0)
# 2020-06-04: code cleanup --> changed isdigit test to try/except loop, changed peer.get test to try/except loop
# 2020-09-10: fixed typo in metrics file. FMS --> FSM (thanks martin[dot]pechstein[at]posteo[dot]de)
# 2021-03-27: rewrite for CMK2.0
# 2021-03-28: added warning for missing admin prefix limit/warn threshold
# 2023-01-15: modified by Spearhead Systems to support CISCO-BGP4-MIB::CbgpPeer3Entry
#
# snmpwalk sample
#
# CISCO-BGP4-MIB::cbgpPeer2AddrFamilyEntry
#
# OMD[mysite]:~$ snmpwalk -ObentU -v2c -c <removed> simulant 1.3.6.1.4.1.9.9.187.1.2.7
# .1.3.6.1.4.1.9.9.187.1.2.7.1.3.1.4.62.214.127.57.1.1 = STRING: "IPv4 Unicast"
# .1.3.6.1.4.1.9.9.187.1.2.7.1.3.1.4.217.119.208.1.1.1 = STRING: "IPv4 Unicast"
# .1.3.6.1.4.1.9.9.187.1.2.7.1.3.1.4.217.119.208.33.1.1 = STRING: "IPv4 Unicast"
# .1.3.6.1.4.1.9.9.187.1.2.7.1.3.2.16.32.1.20.56.7.0.0.39.0.0.0.0.0.0.0.1.2.1 = STRING: "IPv6 Unicast"
# .1.3.6.1.4.1.9.9.187.1.2.7.1.3.2.16.42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.16.2.1 = STRING: "IPv6 Unicast"
#
# CISCO-BGP4-MIB::CbgpPeer2Entry (IPv4)
#
# OMD[mysite]:~$ snmpwalk -ObentU -v2c -c <removed> simulant 1.3.6.1.4.1.9.9.187.1.2.5.1| grep 62.214.127.57
# .1.3.6.1.4.1.9.9.187.1.2.5.1.3.1.4.62.214.127.57 = INTEGER: 6
# .1.3.6.1.4.1.9.9.187.1.2.5.1.4.1.4.62.214.127.57 = INTEGER: 2
# .1.3.6.1.4.1.9.9.187.1.2.5.1.5.1.4.62.214.127.57 = INTEGER: 4
# .1.3.6.1.4.1.9.9.187.1.2.5.1.6.1.4.62.214.127.57 = Hex-STRING: 3E D6 7F 3A
# .1.3.6.1.4.1.9.9.187.1.2.5.1.7.1.4.62.214.127.57 = Gauge32: 29418
# .1.3.6.1.4.1.9.9.187.1.2.5.1.8.1.4.62.214.127.57 = Gauge32: 0
# .1.3.6.1.4.1.9.9.187.1.2.5.1.9.1.4.62.214.127.57 = IpAddress: 217.119.208.2
# .1.3.6.1.4.1.9.9.187.1.2.5.1.10.1.4.62.214.127.57 = Gauge32: 179
# .1.3.6.1.4.1.9.9.187.1.2.5.1.11.1.4.62.214.127.57 = Gauge32: 8881
# .1.3.6.1.4.1.9.9.187.1.2.5.1.12.1.4.62.214.127.57 = IpAddress: 62.214.127.57
# .1.3.6.1.4.1.9.9.187.1.2.5.1.13.1.4.62.214.127.57 = Counter32: 18
# .1.3.6.1.4.1.9.9.187.1.2.5.1.14.1.4.62.214.127.57 = Counter32: 2
# .1.3.6.1.4.1.9.9.187.1.2.5.1.15.1.4.62.214.127.57 = Counter32: 205
# .1.3.6.1.4.1.9.9.187.1.2.5.1.16.1.4.62.214.127.57 = Counter32: 195
# .1.3.6.1.4.1.9.9.187.1.2.5.1.17.1.4.62.214.127.57 = Hex-STRING: 00 00
# .1.3.6.1.4.1.9.9.187.1.2.5.1.18.1.4.62.214.127.57 = Counter32: 1
# .1.3.6.1.4.1.9.9.187.1.2.5.1.19.1.4.62.214.127.57 = Gauge32: 10446
# .1.3.6.1.4.1.9.9.187.1.2.5.1.20.1.4.62.214.127.57 = INTEGER: 60
# .1.3.6.1.4.1.9.9.187.1.2.5.1.21.1.4.62.214.127.57 = INTEGER: 180
# .1.3.6.1.4.1.9.9.187.1.2.5.1.22.1.4.62.214.127.57 = INTEGER: 60
# .1.3.6.1.4.1.9.9.187.1.2.5.1.23.1.4.62.214.127.57 = INTEGER: 180
# .1.3.6.1.4.1.9.9.187.1.2.5.1.24.1.4.62.214.127.57 = INTEGER: 60
# .1.3.6.1.4.1.9.9.187.1.2.5.1.25.1.4.62.214.127.57 = INTEGER: 30
# .1.3.6.1.4.1.9.9.187.1.2.5.1.26.1.4.62.214.127.57 = INTEGER: 30
# .1.3.6.1.4.1.9.9.187.1.2.5.1.27.1.4.62.214.127.57 = Gauge32: 5824
# .1.3.6.1.4.1.9.9.187.1.2.5.1.28.1.4.62.214.127.57 = ""
# .1.3.6.1.4.1.9.9.187.1.2.5.1.29.1.4.62.214.127.57 = INTEGER: 5
#
#
# CISCO-BGP4-MIB::CbgpPeer2Entry (IPv6)
#
# OMD[mysite]:~$ snmpwalk -ObentU -v2c -c <removed> simulant 1.3.6.1.4.1.9.9.187.1.2.5.1| grep 16.42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.16
# .1.3.6.1.4.1.9.9.187.1.2.5.1.3.2.16.42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.16 = INTEGER: 6
# .1.3.6.1.4.1.9.9.187.1.2.5.1.4.2.16.42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.16 = INTEGER: 2
# .1.3.6.1.4.1.9.9.187.1.2.5.1.5.2.16.42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.16 = INTEGER: 4
# .1.3.6.1.4.1.9.9.187.1.2.5.1.6.2.16.42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.16 = Hex-STRING: 2A 05 57 C0 00 00 FF FF 00 00 00 00 00 00 00 11
# .1.3.6.1.4.1.9.9.187.1.2.5.1.7.2.16.42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.16 = Gauge32: 179
# .1.3.6.1.4.1.9.9.187.1.2.5.1.8.2.16.42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.16 = Gauge32: 0
# .1.3.6.1.4.1.9.9.187.1.2.5.1.9.2.16.42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.16 = IpAddress: 217.119.208.2
# .1.3.6.1.4.1.9.9.187.1.2.5.1.10.2.16.42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.16 = Gauge32: 35062
# .1.3.6.1.4.1.9.9.187.1.2.5.1.11.2.16.42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.16 = Gauge32: 31259
# .1.3.6.1.4.1.9.9.187.1.2.5.1.12.2.16.42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.16 = IpAddress: 217.119.208.1
# .1.3.6.1.4.1.9.9.187.1.2.5.1.13.2.16.42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.16 = Counter32: 5
# .1.3.6.1.4.1.9.9.187.1.2.5.1.14.2.16.42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.16 = Counter32: 6
# .1.3.6.1.4.1.9.9.187.1.2.5.1.15.2.16.42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.16 = Counter32: 157
# .1.3.6.1.4.1.9.9.187.1.2.5.1.16.2.16.42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.16 = Counter32: 161
# .1.3.6.1.4.1.9.9.187.1.2.5.1.17.2.16.42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.16 = Hex-STRING: 06 04
# .1.3.6.1.4.1.9.9.187.1.2.5.1.18.2.16.42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.16 = Counter32: 2
# .1.3.6.1.4.1.9.9.187.1.2.5.1.19.2.16.42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.16 = Gauge32: 8430
# .1.3.6.1.4.1.9.9.187.1.2.5.1.20.2.16.42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.16 = INTEGER: 60
# .1.3.6.1.4.1.9.9.187.1.2.5.1.21.2.16.42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.16 = INTEGER: 180
# .1.3.6.1.4.1.9.9.187.1.2.5.1.22.2.16.42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.16 = INTEGER: 60
# .1.3.6.1.4.1.9.9.187.1.2.5.1.23.2.16.42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.16 = INTEGER: 180
# .1.3.6.1.4.1.9.9.187.1.2.5.1.24.2.16.42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.16 = INTEGER: 60
# .1.3.6.1.4.1.9.9.187.1.2.5.1.25.2.16.42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.16 = INTEGER: 0
# .1.3.6.1.4.1.9.9.187.1.2.5.1.26.2.16.42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.16 = INTEGER: 0
# .1.3.6.1.4.1.9.9.187.1.2.5.1.27.2.16.42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.16 = Gauge32: 1494
# .1.3.6.1.4.1.9.9.187.1.2.5.1.28.2.16.42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.16 = STRING: "Administrative Reset"
# .1.3.6.1.4.1.9.9.187.1.2.5.1.29.2.16.42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.16 = INTEGER: 5
#
#
# CISCO-BGP4-MIB::cbgpPeer2AddrFamilyPrefixEntry (IPv4)
#
# OMD[mysite]:~$ snmpwalk -ObentU -v2c -c <removed> simulant 1.3.6.1.4.1.9.9.187.1.2.8.1| grep 16.42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.16
# .1.3.6.1.4.1.9.9.187.1.2.8.1.1.2.16.42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.16.2.1 = Counter32: 2
# .1.3.6.1.4.1.9.9.187.1.2.8.1.2.2.16.42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.16.2.1 = Gauge32: 0
# .1.3.6.1.4.1.9.9.187.1.2.8.1.3.2.16.42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.16.2.1 = Gauge32: 100000
# .1.3.6.1.4.1.9.9.187.1.2.8.1.4.2.16.42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.16.2.1 = Gauge32: 85
# .1.3.6.1.4.1.9.9.187.1.2.8.1.5.2.16.42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.16.2.1 = Gauge32: 80
# .1.3.6.1.4.1.9.9.187.1.2.8.1.6.2.16.42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.16.2.1 = Gauge32: 10
# .1.3.6.1.4.1.9.9.187.1.2.8.1.7.2.16.42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.16.2.1 = Gauge32: 0
# .1.3.6.1.4.1.9.9.187.1.2.8.1.8.2.16.42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.16.2.1 = Gauge32: 0
#
#
# CISCO-BGP4-MIB::cbgpPeer2AddrFamilyPrefixEntry (IPv6)
#
# OMD[mysite]:~$ snmpwalk -ObentU -v2c -c <removed> simulant 1.3.6.1.4.1.9.9.187.1.2.8.1| grep 16.42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.16
# .1.3.6.1.4.1.9.9.187.1.2.8.1.1.2.16.42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.16.2.1 = Counter32: 2
# .1.3.6.1.4.1.9.9.187.1.2.8.1.2.2.16.42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.16.2.1 = Gauge32: 0
# .1.3.6.1.4.1.9.9.187.1.2.8.1.3.2.16.42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.16.2.1 = Gauge32: 100000
# .1.3.6.1.4.1.9.9.187.1.2.8.1.4.2.16.42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.16.2.1 = Gauge32: 85
# .1.3.6.1.4.1.9.9.187.1.2.8.1.5.2.16.42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.16.2.1 = Gauge32: 80
# .1.3.6.1.4.1.9.9.187.1.2.8.1.6.2.16.42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.16.2.1 = Gauge32: 10
# .1.3.6.1.4.1.9.9.187.1.2.8.1.7.2.16.42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.16.2.1 = Gauge32: 0
# .1.3.6.1.4.1.9.9.187.1.2.8.1.8.2.16.42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.16.2.1 = Gauge32: 0
#
#
# sample info
#
# [
# [
# ['1.4.77.235.182.229', '6', '2', 'M\xeb\xb6\xe6', '0', '217.119.208.1', '21413', '77.235.182.229', '1', '3', '48',
# '53', '\x04\x00', '8', '2581', '2581', 'hold time expired', '5'],
# ['1.4.217.119.208.2', '6', '2', '\xd9w\xd0\x01', '0', '217.119.208.1', '31259', '217.119.208.2', '11', '23', '168',
# '170', '\x06\x04', '3', '8380', '5774', 'Administrative Reset', '5'],
# ['1.4.217.119.208.34', '6', '2', '\xd9w\xd0!', '0', '217.119.208.1', '31259', '217.119.208.2', '11', '23', '168',
# '170', '\x06\x04', '3', '8377', '5774', 'Administrative Reset', '5'],
# ['2.16.42.0.28.160.16.0.1.53.0.0.0.0.0.0.0.1', '6', '2', '*\x00\x1c\xa0\x10\x00\x015\x00\x00\x00\x00\x00\x00\x00\x02',
# '0', '217.119.208.1', '21413', '77.235.182.229', '0', '4', '108', '121', '\x06\x04', '6', '6295', '0',
# 'Administrative Reset', '5'],
# ['2.16.42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.17', '6', '2', '*\x05W\xc0\x00\x00\xff\xff\x00\x00\x00\x00\x00\x00\x00\x10',
# '0', '217.119.208.1', '31259', '217.119.208.2', '6', '5', '160', '157', '\x06\x04', '2', '8380', '1409',
# 'Administrative Reset', '5']
# ],
# [
# ['1.4.77.235.182.229.1.1', 'IPv4 Unicast', '1', '0', '', '', '', '6', '0', '0'],
# ['1.4.217.119.208.2.1.1', 'IPv4 Unicast', '4', '0', '', '', '', '17', '0', '10'],
# ['1.4.217.119.208.34.1.1', 'IPv4 Unicast', '4', '0', '', '', '', '17', '0', '10'],
# ['2.16.42.0.28.160.16.0.1.53.0.0.0.0.0.0.0.1.2.1', 'IPv6 Unicast', '0', '0', '100000', '85', '80', '6', '0', '0'],
# ['2.16.42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.17.2.1', 'IPv6 Unicast', '2', '0', '100000', '85', '80', '8', '0', '0']
# ]
# ]
#
#
#
from dataclasses import dataclass
import re, time
from typing import Mapping, Dict, List, Tuple, NamedTuple
from cmk.base.plugins.agent_based.agent_based_api.v1.type_defs import (
DiscoveryResult,
CheckResult,
StringTable,
)
from cmk.base.plugins.agent_based.agent_based_api.v1 import (
register,
Service,
Result,
check_levels,
State,
SNMPTree,
contains,
OIDEnd,
get_rate,
get_value_store,
Metric,
render,
)
@dataclass
class Section:
peer_prefixes: dict
peer_table: dict
###########################################################################
#
# DATA Parser function
#
###########################################################################
def parse_cisco_bgp_peer(string_table: List[StringTable]) -> Section:
def bgp_render_ipv4_address(bytestring):
return ".".join(["%s" % ord(m) for m in bytestring])
def bgp_shorten_ipv6_adress(address):
address = address.split(':')
span = 2
address = [''.join(address[i:i + span]) for i in range(0, len(address), span)]
for m in range(0, len(address)):
address[m] = re.sub(r'^0{1,3}', r'', address[m])
address = ':'.join(address)
zeros = ':0:0:0:0:0:0:'
while not zeros == '':
if zeros in address:
address = re.sub(r'%s' % zeros, r'::', address)
zeros = ''
else:
zeros = zeros[:-2]
return address
def bgp_render_ipv6_address(bytestring):
address = ":".join(["%02s" % hex(ord(m))[2:] for m in bytestring]).replace(' ', '0').upper()
address = bgp_shorten_ipv6_adress(address)
return address
def bgp_render_ip_address(bytestring):
if len(bytestring) == 4:
return bgp_render_ipv4_address(bytestring)
elif len(bytestring) == 16:
return bgp_render_ipv6_address(bytestring)
else:
return ''
def cisco_bgp_get_peer(OID_END):
# returns peer address string from OID_END
# u'1.4.217.119.208.34.1.1' --> 217.119.208.34
# u'2.16.42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.17.2.1' --> 42.5.87.192.0.0.255.255.0.0.0.0.0.0.0.17
peer_ip = ''
OID_END = OID_END.split('.')
if int(len(OID_END)) == 7: # length of ip address
peer_ip = '.'.join(OID_END[3:7]) # ipv4 address
elif int(OID_END[1]) == 16: # ipv6 address
peer_ip = ':'.join('%02s' % hex(int(m))[2:] for m in OID_END[2:18]).replace(' ', '0').upper()
peer_ip = bgp_shorten_ipv6_adress(peer_ip)
return peer_ip
def cisco_bgp_errors(bytestring):
lasterrorhex = ''.join(["%02X " % ord(x) for x in bytestring]).strip()
byte1, byte2 = lasterrorhex.split()
names = {}
names[0] = {0: 'NO ERROR'}
names[1] = {0: 'Message',
2: 'Connection Not Synchronized',
3: 'Bad Message Length',
4: 'Bad Message Type',
}
names[2] = {0: 'OPEN',
1: 'Unsupported Version Number',
2: 'Bad Peer AS',
3: 'Bad BGP Identifier',
4: 'Unsupported Optional Parameter',
5: 'Authentication Failure',
6: 'Unacceptable Hold',
}
names[3] = {0: 'UPDATE',
1: 'Malformed Attribute List',
2: 'Unrecognized Well-known Attribute',
3: 'Missing Well-known Attribute',
4: 'Attribute Flags Error',
5: 'Attribute Length Error',
6: 'Invalid ORIGIN Attribute',
7: 'AS Routing Loop',
8: 'Invalid NEXT_HOP Attribute',
9: 'Optional Attribute Error',
10: 'Invalid Network Field',
11: 'Malformed AS_PATH',
}
names[4] = {0: 'Hold Timer Expired', }
names[5] = {0: 'Finite State Machine Error', }
names[6] = {0: 'Administratively Shutdown',
1: 'Max Prefix Reached',
2: 'Peer Unconfigured',
3: 'Administratively Reset',
4: 'Connection Rejected',
5: 'Other Configuration Change',
}
return names[int(byte1, 16)].get(int(byte2, 16))
# bgp not active
if not string_table == [[], [], []]:
cbgpPeer2Entry, cbgpPeer3Entry, cbgpPeer2AddrFamily = string_table
cbgpPeerEntry = cbgpPeer2Entry + cbgpPeer3Entry
peer_prefixes = {}
# create dictionary from cbgpPeer2AddrFamily ('remoteip addrfamilyname' as index)
if len(cbgpPeer2AddrFamily) > 0:
for entry in cbgpPeer2AddrFamily:
oid_end, addrfamilyname, acceptedprefixes, deniedprefixes, prefixadminlimit, prefixthreshold, \
prefixclearthreshold, advertisedprefixes, suppressedprefixes, withdrawnprefixes = entry
remoteaddr = cisco_bgp_get_peer(entry[0])
peer = {
'remoteaddr': remoteaddr,
'addrfamilyname': addrfamilyname
}
for key, value in [
('prefixadminlimit', prefixadminlimit),
('prefixthreshold', prefixthreshold),
('prefixclearthreshold', prefixclearthreshold),
('acceptedprefixes', acceptedprefixes),
('advertisedprefixes', advertisedprefixes),
('deniedprefixes', deniedprefixes),
('suppressedprefixes', suppressedprefixes),
('withdrawnprefixes', withdrawnprefixes),
]:
try:
peer[key] = int(value)
except ValueError:
pass
peer_prefixes.update({'%s %s' % (remoteaddr, addrfamilyname): peer})
# workaround: get remote ip from cbgpPeerEntry if cbgpPeer2AddrFamilyName is missing :-(
elif len(cbgpPeerEntry) > 0:
for entry in cbgpPeerEntry:
remoteaddr = cisco_bgp_get_peer(entry[0])
addrfamilyname = ''
peer = {'remoteaddr': remoteaddr, }
peer_prefixes.update({'%s %s' % (remoteaddr, addrfamilyname): peer})
# create dictionary from cbgpPeerEntry (peer ip address as index)
peer_table = {}
for entry in cbgpPeerEntry:
oid_end, state, adminstatus, localaddr, localas, localidentifier, remoteas, remoteidentifier, inupdates, \
outupdates, intotalmessages, outtotalmessages, lasterror, fsmestablishedtransitions, fsmestablishedtime, \
inupdateelapsedtime, lasterrortxt, prevstate = entry
peer = {'remoteaddr': cisco_bgp_get_peer(oid_end),
'localaddr': bgp_render_ip_address(localaddr),
'localid': localidentifier,
'remoteid': remoteidentifier,
'lasterror': cisco_bgp_errors(lasterror),
'lasterrortxt': lasterrortxt,
'prevstate': int(prevstate),
}
for key, value in [
('state', state),
('adminstate', adminstatus),
('localas', localas),
('remoteas', remoteas),
('inupdates', inupdates),
('outupdates', outupdates),
('intotalmessages', intotalmessages),
('outtotalmessages', outtotalmessages),
('fsmestablishedtransitions', fsmestablishedtransitions),
('fsmestablishedtime', fsmestablishedtime),
('inupdateelapsedtime', inupdateelapsedtime),
]:
try:
peer[key] = int(value)
except ValueError:
pass
peer_table.update({'%s' % cisco_bgp_get_peer(oid_end): peer})
return Section(
peer_prefixes=peer_prefixes,
peer_table=peer_table,
)
###########################################################################
#
# INVENTORY function
#
###########################################################################
def discovery_cisco_bgp_peer(section: Section) -> DiscoveryResult:
for key in section.peer_prefixes.keys():
yield Service(item=key)
###########################################################################
#
# CHECK function
#
###########################################################################
def check_cisco_bgp_peer(item, params, section) -> CheckResult:
def cisco_bgp_adminstate(state):
names = {1: 'stop',
2: 'start', }
return names.get(state, 'unknown (%s)' % state)
def cisco_bgp_peerstate(state):
names = {0: 'none',
1: 'idle',
2: 'connect',
3: 'active',
4: 'opensned',
5: 'openconfirm',
6: 'established'}
return names.get(state, 'unknown (%s)' % state)
peer_prefixes = section.peer_prefixes
peer_table = section.peer_table
prefixes = peer_prefixes.get(item, None)
alias = ''
peer_not_found_state = 3
for bgp_connection, bgp_alias, not_found_state in params.get('peer_list', []):
if item == bgp_connection:
alias = bgp_alias
peer_not_found_state = not_found_state
if prefixes:
longoutput = ''
peer = peer_table.get(prefixes.get('remoteaddr'))
if peer.get('localas') == 0:
peer.update({'localas': params.get('useaslocalas')})
if alias != '':
yield Result(state=State.OK, summary='Alias: %s' % alias)
peerstate = peer.get('state')
adminstate = peer.get('adminstate')
establishedtime = peer.get('fsmestablishedtime')
if peerstate == 1: # idle
yield Result(state=State.CRIT, summary='Peer state: %s' % cisco_bgp_peerstate(peerstate))
elif peerstate == 6: # established
yield from check_levels(
value=establishedtime,
label='Uptime',
levels_lower=params['minuptime'],
render_func=render.timespan
)
else: # everything else
yield Result(state=State.WARN, summary='Peer state: %s' % cisco_bgp_peerstate(peerstate))
if not adminstate == 2: # not start
yield Result(state=State.WARN, summary='Admin state: %s' % cisco_bgp_adminstate(adminstate))
for key, value in [
('remoteid', 'Remote ID: %s'),
('remoteas', 'Remote AS: %s'),
('localaddr', 'Local address: %s'),
('localid', 'Local ID: %s'),
('localas', 'Local AS: %s'),
]:
if key in params['infotext_values']:
try:
yield Result(state=State.OK, summary=value % peer[key])
except KeyError:
pass
bgptype = ''
if not peer.get('localas') == 0:
if peer.get('remoteas') == peer.get('localas'):
bgptype = ' (iBGP)'
else:
bgptype = ' (eBGP)'
longoutput_data = [
['IP-address (remote/local)', peer.get('remoteaddr'), peer.get('localaddr')],
['Router-ID (remote/local)', peer.get('remoteid'), peer.get('localid')],
['Autonomus System (remote/local)', peer.get('remoteas'), str(peer.get('localas')) + bgptype],
['State', cisco_bgp_peerstate(peerstate), ''],
['Admin state', cisco_bgp_adminstate(adminstate), ''],
['Last error', peer.get('lasterror'), ''],
['Last error text', peer.get('lasterrortxt'), ''],
['Previous state', cisco_bgp_peerstate(peer.get('prevstate')), ''],
['Address family name', prefixes.get('addrfamilyname', 'unknown'), ''],
['Prefix clear threshold (%)', '%.0d' % prefixes.get('prefixclearthreshold', 0), '']
,
]
acceptedprefixes = prefixes.get('acceptedprefixes', None)
prefixadminlimit = prefixes.get('prefixadminlimit', None)
prefixthreshold = prefixes.get('prefixthreshold', None)
if prefixadminlimit is not None and prefixthreshold is not None:
warnthreshold = prefixadminlimit / 100.0 * prefixthreshold # use float (100.0) to get xx.xx in division
longoutput_data.append(['Prefix admin limit (prefixes)', '%.0d' % prefixadminlimit, ''])
longoutput_data.append(['Prefix threshold (prefixes/%)', '%.0d' % warnthreshold, '%.0d' % prefixthreshold])
else:
yield Result(state=State(params['noprefixlimit']), notice='No admin prefix limit/warn threshold configured on the device.')
warnthreshold = None
if params.get('htmloutput', False):
#
# disable 'Escape HTML codes in plugin output' in wato --> global settings
#
table_bracket = '<table border="1">%s</table>'
line_bracket = '<tr>%s</tr>'
cell_bracket = '<td>%s</td><td>%s</td><td>%s</td>'
cell_seperator = ''
longoutput = '\n' + table_bracket % (''.join(
[line_bracket % cell_seperator.join([cell_bracket % (entry[0], entry[1], entry[2])]) for entry in
longoutput_data]))
else:
longoutput += '\nfor nicer output' \
'\ndisable \'Escape HTML codes in plugin output\' in wato -> global settings and enable HTML output in \'Parameters for this service\''
for entry in longoutput_data:
if not entry[2] == '':
longoutput += '\n{}: {} / {}'.format(entry[0], entry[1], entry[2])
else:
longoutput += '\n{}: {}'.format(entry[0], entry[1])
if prefixadminlimit is not None:
yield from check_levels(
value=acceptedprefixes,
metric_name='cisco_bgp_peer_acceptedprefixes',
levels_upper=(warnthreshold, prefixadminlimit),
label='Prefixes accepted',
render_func=lambda v: '%s' % str(v)
)
now_time = time.time()
value_store = get_value_store()
rate_item = item.replace(' ', '_').replace(':', '_')
for key in [
'deniedprefixes',
'advertisedprefixes',
'withdrawnprefixes',
'suppressedprefixes',
'inupdates',
'outupdates',
'intotalmessages',
'outtotalmessages',
]:
try:
value = get_rate(value_store, 'cisco_bgp_peer.%s.%s' % (key, rate_item), now_time, prefixes[key],
raise_overflow=False)
yield Metric(name='cisco_bgp_peer_%s' % key, value=value, boundaries=(0, None))
except KeyError:
pass
for key in [
'fsmestablishedtransitions',
'fsmestablishedtime',
'inupdateelapsedtime'
]:
try:
yield Metric(name='cisco_bgp_peer_%s' % key, value=peer[key], boundaries=(0, None))
except KeyError:
pass
yield Result(state=State.OK, notice=longoutput)
else:
if alias != '':
yield Result(state=State.OK, summary=', Alias: %s' % alias)
yield Result(state=State(peer_not_found_state), summary='Item not found in SNMP data')
###########################################################################
#
# CHECK info
#
###########################################################################
register.snmp_section(
name='cisco_bgp_peer',
parse_function=parse_cisco_bgp_peer,
fetch=[
SNMPTree(
base='.1.3.6.1.4.1.9.9.187.1.2.5.1', # CCISCO-BGP4-MIB::cbgpPeer2Entry
oids=[
OIDEnd(),
'3', # cbgpPeer2State
'4', # cbgpPeer2AdminStatus
'6', # cbgpPeer2LocalAddr
'8', # cbgpPeer2LocalAs -> empty
'9', # cbgpPeer2LocalIdentifier
'11', # cbgpPeer2RemoteAs
'12', # cbgpPeer2RemoteIdentifier
'13', # cbgpPeer2InUpdates
'14', # cbgpPeer2OutUpdates
'15', # cbgpPeer2InTotalMessages
'16', # cbgpPeer2OutTotalMessages
'17', # cbgpPeer2LastError
'18', # cbgpPeer2FsmEstablishedTransitions
'19', # cbgpPeer2FsmEstablishedTime
'27', # cbgpPeer2InUpdateElapsedTime
'28', # cbgpPeer2LastErrorTxt
'29', # cbgpPeer2PrevState
]
),
SNMPTree(
base='.1.3.6.1.4.1.9.9.187.1.2.9.1', # CCISCO-BGP4-MIB::cbgpPeer3Entry
oids=[
OIDEnd(),
'5', # cbgpPeer3State
'6', # cbgpPeer3AdminStatus
'8', # cbgpPeer3LocalAddr
'10', # cbgpPeer3LocalAs -> empty
'11', # cbgpPeer3LocalIdentifier
'13', # cbgpPeer3RemoteAs
'14', # cbgpPeer3RemoteIdentifier
'15', # cbgpPeer3InUpdates
'16', # cbgpPeer3OutUpdates
'17', # cbgpPeer3InTotalMessages
'18', # cbgpPeer3OutTotalMessages
'19', # cbgpPeer3LastError
'20', # cbgpPeer3FsmEstablishedTransitions
'21', # cbgpPeer3FsmEstablishedTime
'29', # cbgpPeer3InUpdateElapsedTime
'30', # cbgpPeer3LastErrorTxt
'31', # cbgpPeer3PrevState
]
),
SNMPTree(
base='.1.3.6.1.4.1.9.9.187.1.2', # cbgpPeer
oids=[
OIDEnd(), #
# .7.1 --> cbgpPeer2AddrFamilyEntry
'7.1.3', # cbgpPeer2AddrFamilyName
# .8.1 --> cbgpPeer2AddrFamilyPrefixEntry
'8.1.1', # cbgpPeer2AcceptedPrefixes
'8.1.2', # cbgpPeer2DeniedPrefixes
'8.1.3', # cbgpPeer2PrefixAdminLimit
'8.1.4', # cbgpPeer2PrefixThreshold
'8.1.5', # cbgpPeer2PrefixClearThreshold
'8.1.6', # cbgpPeer2AdvertisedPrefixes
'8.1.7', # cbgpPeer2SuppressedPrefixes
'8.1.8', # cbgpPeer2WithdrawnPrefixes
]
)
],
detect=contains('.1.3.6.1.2.1.1.1.0', 'Cisco'),
)
register.check_plugin(
name='cisco_bgp_peer',
service_name='Cisco BGP peer %s',
discovery_function=discovery_cisco_bgp_peer,
check_function=check_cisco_bgp_peer,
check_default_parameters={
'minuptime': (7200, 3600),
'useaslocalas': 0,
'htmloutput': False,
'noprefixlimit': 1,
'infotext_values': [],
'peer_list': [],
},
check_ruleset_name='cisco_bgp_peer',
)

View File

@ -0,0 +1,178 @@
#!/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-26
#
# Cisco BGP Peer metrics plugin
#
# 2018-05-25: cleanup
# 2020-09-10: fixed typo FMS --> FSM (Thanks martin[dot]pechstein[at]posteo[dot]de)
# 2021-03-27: rewrite for CMK 2.0
#
from cmk.gui.i18n import _
from cmk.gui.plugins.metrics import (
metric_info,
graph_info,
perfometer_info
)
#####################################################################################################################
#
# define metrics for bgp peer perfdata
#
#####################################################################################################################
metric_info['cisco_bgp_peer_acceptedprefixes'] = {
'title': _('Prefixes accepted'),
'help': _('number of accepted prefixes'),
'unit': 'count',
'color': '11/a',
}
metric_info['cisco_bgp_peer_deniedprefixes'] = {
'title': _('Prefixes denied'),
'unit': '1/s',
'color': '21/a',
}
metric_info['cisco_bgp_peer_advertisedprefixes'] = {
'title': _('Prefixes advertised'),
'unit': '1/s',
'color': '31/a',
}
metric_info['cisco_bgp_peer_withdrawnprefixes'] = {
'title': _('Prefixes withdrawn'),
'unit': '1/s',
'color': '41/a',
}
metric_info['cisco_bgp_peer_suppressedprefixes'] = {
'title': _('Prefixes suppressed'),
'unit': '1/s',
'color': '12/a',
}
metric_info['cisco_bgp_peer_inupdates'] = {
'title': _('Updates received'),
'unit': '1/s',
'color': '22/a',
}
metric_info['cisco_bgp_peer_outupdates'] = {
'title': _('Updates send'),
'unit': '1/s',
'color': '32/a',
}
metric_info['cisco_bgp_peer_intotalmessages'] = {
'title': _('Total messages received'),
'unit': '1/s',
'color': '42/a',
}
metric_info['cisco_bgp_peer_outtotalmessages'] = {
'title': _('Total messages send'),
'unit': '1/s',
'color': '13/a',
}
metric_info['cisco_bgp_peer_fsmestablishedtransitions'] = {
'title': _('FSM transitions'),
'unit': 'count',
'color': '23/a',
}
metric_info['cisco_bgp_peer_fsmestablishedtime'] = {
'title': _('FSM last change'),
'unit': 's',
'color': '26/a',
}
metric_info['cisco_bgp_peer_inupdateelapsedtime'] = {
'title': _('Last update received'),
'unit': 's',
'color': '43/a',
}
######################################################################################################################
#
# how to graph perdata for bgp peer
#
######################################################################################################################
graph_info['cisco_bgp_peer.prefixes_accepted']={
'title': _('Accepted Prefixes'),
'metrics': [
('cisco_bgp_peer_acceptedprefixes', 'line'),
],
'scalars': [
('cisco_bgp_peer_acceptedprefixes:crit', _('crit')),
('cisco_bgp_peer_acceptedprefixes:warn', _('warn')),
],
}
graph_info['cisco_bgp_peer.prefixes_per_second']={
'title': _('Prefixes/s'),
'metrics': [
('cisco_bgp_peer_deniedprefixes', 'line'),
('cisco_bgp_peer_advertisedprefixes', 'line'),
('cisco_bgp_peer_withdrawnprefixes', 'line'),
('cisco_bgp_peer_suppressedprefixes', 'line'),
],
}
graph_info['cisco_bgp_peer.updates_in_out']={
'title': _('Updates'),
'metrics': [
('cisco_bgp_peer_inupdates', 'area'),
('cisco_bgp_peer_outupdates', '-area'),
]
}
graph_info['cisco_bgp_peer.messages_in_out']={
'title': _('Total messages'),
'metrics': [
('cisco_bgp_peer_intotalmessages', 'area'),
('cisco_bgp_peer_outtotalmessages', '-area'),
]
}
graph_info['cisco_bgp_peer.fms_transitions_from_to']={
'title': _('FSM transitions from/to established'),
'metrics': [
('cisco_bgp_peer_fsmestablishedtransitions', 'line'),
],
}
graph_info['cisco_bgp_peer.fms_transitions_last_change']={
'title': _('FSM established last change'),
'metrics': [
('cisco_bgp_peer_fsmestablishedtime', 'line'),
]
}
graph_info['cisco_bgp_peer.time_since_last_update']={
'title': _('Time since last update received'),
'metrics': [
('cisco_bgp_peer_inupdateelapsedtime', 'line'),
]
}
######################################################################################################################
#
# define perf-o-meter for bgp peer uptime + prefixes accepted/advertised
#
######################################################################################################################
perfometer_info.append(('stacked', [
{
'type': 'logarithmic',
'metric': 'cisco_bgp_peer_fsmestablishedtime',
'half_value': 2592000.0, # ome month
'exponent': 2,
},
{
'type': 'logarithmic',
'metric': 'cisco_bgp_peer_acceptedprefixes',
'half_value': 500000.0,
'exponent': 2,
}
]))

View File

@ -0,0 +1,126 @@
#!/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 cisco_bgp_peers WATO plugin
#
# 2021-03-27: rewrite for CMK 2.0
#
from cmk.gui.i18n import _
from cmk.gui.valuespec import (
Dictionary,
Integer,
TextAscii,
ListOfStrings,
FixedValue,
ListChoice,
ListOf,
Tuple,
TextUnicode,
MonitoringState,
)
from cmk.gui.plugins.wato import (
CheckParameterRulespecWithItem,
rulespec_registry,
RulespecGroupCheckParametersNetworking,
)
cisco_bgp_peer_infotext_values = [
('remoteid', 'Remote router ID'),
('remoteas', 'Remote autonomous system'),
('localaddr', 'Local peer IP address'),
('localid', 'Local router ID'),
('localas', 'Local autonomous system'),
]
def _parameter_valuespec_cisco_bgp_peer():
return Dictionary(elements=[
('minuptime',
Tuple(
title=_('Minimum uptime for peer'),
help=_('Set the time in seconds, a peer must be up before the peer is considered sable.'
'If the peer uptime less then X, the check outcome is set to warning.'),
elements=[
Integer(title=_('Warning if below'), unit='seconds', default_value=7200, minvalue=0),
Integer(title=_('Critical if below'), unit='seconsa', default_value=3600, minvalue=0)
],
),
),
('useaslocalas',
Integer(
help=_('Use this AS number if the SNMP Value for CISCO-BGP4-MIB::cbgpPeer2LocalAs is \'0\'.'),
title=_('Use AS as local AS, if SNMP cbgpPeer2LocalAs is not valid.'),
default_value=0,
# allow_empty=False,
),
),
('htmloutput',
FixedValue(
True,
help=_('render long output of check plugin (multiline) as HTML table. Needs \'Escape HTML codes in plugin output\' in wato --> global settings disabled'),
title=_('enable HTML Output for long output of check plugin (multiline)'),
totext=_('enable HTML Output for long output of check plugin (multiline)'),
default_value=False,
)),
('noprefixlimit',
MonitoringState(
default_value=1,
title=_('State if no admin prefix limit/warn threshold 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')
)),
('infotext_values',
ListChoice(
title=_('Add values to check info'),
help=_('Select values to add to the check output.'),
choices=cisco_bgp_peer_infotext_values,
default_value=[],
)),
('peer_list',
ListOf(
Tuple(
# title=('BGP Peers'),
elements=[
TextUnicode(
title=_('BGP Peer item name (without "Cisco BGP peer")'),
help=_('The configured value must match a BGP item reported by the monitored '
'device. For example: "10.194.115.98 IPv4 Unicast"'),
allow_empty=False,
),
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 item name" field. The alias will '
'be shown in the infotext'),
),
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 item name" field is not found')
),
]),
add_label=_('Add BGP peer'),
movable=False,
title=_('BGP Peers'),
)),
])
rulespec_registry.register(
CheckParameterRulespecWithItem(
check_group_name='cisco_bgp_peer',
group=RulespecGroupCheckParametersNetworking,
item_spec=lambda: TextAscii(title=_('BGP peer specific configuration'), ),
match_type='dict',
parameter_valuespec=_parameter_valuespec_cisco_bgp_peer,
title=lambda: _('Cisco BGP peer'),
))

View File

@ -0,0 +1 @@
This plugin checks the registration status (and KEK key timeout) of GDOI Group Members with GDOI Key Servers.

Binary file not shown.

View File

@ -0,0 +1,98 @@
#!/usr/bin/env python3
#
# More information about this Cisco system:
# https://www.cisco.com/en/US/docs/ios-xml/ios/sec_conn_getvpn/configuration/15-2mt/sec-get-vpn.html
#
from dataclasses import dataclass
from typing import Dict, List
from cmk.base.plugins.agent_based.agent_based_api.v1 import (
register,
Service,
Result,
State,
SNMPTree,
contains,
OIDEnd,
)
@dataclass
class Section:
kek_info: dict
def chars_to_ip_addr(chars):
return ".".join(map(lambda c: str(ord(c)), [*chars]))
conversions = {
"1": "using",
"2": "new",
"3": "old",
}
# SNMP parsing function
def parse_cisco_gdoi(string_table):
def parse(data):
lookup = {}
for val in data:
ip = chars_to_ip_addr(val[0])
remaining = int(val[1])
state = conversions[val[2]]
lookup.setdefault(ip, {})
lookup[ip][state] = remaining
return lookup
if string_table == [[]]:
return
return Section(
kek_info=parse(string_table[0]),
)
# Inventory function, returning inventory based upon SNMP parsed result above
def discovery_cisco_gdoi(section):
yield Service(item="Keyservers", parameters=section.kek_info)
# Check function, returning ok/crit based upon SNMP parsed result above
def check_cisco_gdoi(item, params, section):
state = params
registered = False
for ip, state in params.items():
in_use = state.get("using")
if in_use > 0:
registered = True
yield Result(state=State.OK, summary="Registered, using KEK from " + ip)
if not registered:
yield Result(state=State.CRIT, summary="Unregistered")
register.snmp_section(
name="cisco_gdoi",
parse_function=parse_cisco_gdoi,
fetch=[
SNMPTree(
# ciscoGdoiMIB::cgmGdoiGmKekRemainingLifetime
base=".1.3.6.1.4.1.9.9.759.1.3.2.1",
oids=[
"5", # cgmGdoiGmKekSrcIdValue
"20", # cgmGdoiGmKekRemainingLifetime
"21", # cgmGdoiGmKekStatus
]
),
],
detect=contains(".1.3.6.1.2.1.1.1.0", "Cisco"),
)
register.check_plugin(
name="cisco_gdoi",
service_name="Cisco GDOI %s",
discovery_function=discovery_cisco_gdoi,
check_function=check_cisco_gdoi,
check_default_parameters={},
check_ruleset_name="cisco_gdoi",
)

View File

@ -0,0 +1 @@
This is a modification of a Tribe29 GPL plugin to support tracking packet loss as well.

Binary file not shown.

View File

@ -0,0 +1,208 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Copyright (C) 2019 tribe29 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.i18n import _
from cmk.gui.plugins.wato.utils import (
CheckParameterRulespecWithItem,
rulespec_registry,
RulespecGroupCheckParametersNetworking,
)
from cmk.gui.valuespec import Dictionary, DropdownChoice, Integer, TextInput, Tuple
def _item_spec_cisco_ip_sla():
return TextInput(
title=_("RTT row index of the service"),
allow_empty=True,
)
def _parameter_valuespec_cisco_ip_sla():
return Dictionary(
elements=[
(
"rtt_type",
DropdownChoice(
title=_("RTT type"),
choices=[
("echo", _("echo")),
("path echo", _("path echo")),
("file IO", _("file IO")),
("UDP echo", _("UDP echo")),
("TCP connect", _("TCP connect")),
("HTTP", _("HTTP")),
("DNS", _("DNS")),
("jitter", _("jitter")),
("DLSw", _("DLSw")),
("DHCP", _("DHCP")),
("FTP", _("FTP")),
("VoIP", _("VoIP")),
("RTP", _("RTP")),
("LSP group", _("LSP group")),
("ICMP jitter", _("ICMP jitter")),
("LSP ping", _("LSP ping")),
("LSP trace", _("LSP trace")),
("ethernet ping", _("ethernet ping")),
("ethernet jitter", _("ethernet jitter")),
("LSP ping pseudowire", _("LSP ping pseudowire")),
],
default_value="echo",
),
),
(
"threshold",
Integer(
title=_("Treshold"),
help=_(
"Depending on the precision the unit can be "
"either milliseconds or micoseconds."
),
unit=_("ms/us"),
minvalue=1,
default_value=5000,
),
),
(
"state",
DropdownChoice(
title=_("State"),
choices=[
("active", _("active")),
("inactive", _("inactive")),
("reset", _("reset")),
("orderly stop", _("orderly stop")),
("immediate stop", _("immediate stop")),
("pending", _("pending")),
("restart", _("restart")),
],
default_value="active",
),
),
(
"connection_lost_occured",
DropdownChoice(
title=_("Connection lost occured"),
choices=[
("yes", _("yes")),
("no", _("no")),
],
default_value="no",
),
),
(
"timeout_occured",
DropdownChoice(
title=_("Timeout occured"),
choices=[
("yes", _("yes")),
("no", _("no")),
],
default_value="no",
),
),
(
"completion_time_over_treshold_occured",
DropdownChoice(
title=_("Completion time over treshold occured"),
choices=[
("yes", _("yes")),
("no", _("no")),
],
default_value="no",
),
),
(
"latest_rtt_completion_time",
Tuple(
title=_("Latest RTT completion time"),
help=_(
"Depending on the precision the unit can be "
"either milliseconds or micoseconds."
),
elements=[
Integer(
title=_("Warning at"),
unit=_("ms/us"),
minvalue=1,
default_value=100,
),
Integer(
title=_("Critical at"),
unit=_("ms/us"),
minvalue=1,
default_value=200,
),
],
),
),
(
"latest_rtt_state",
DropdownChoice(
title=_("Latest RTT state"),
choices=[
("ok", _("OK")),
("disconnected", _("disconnected")),
("over treshold", _("over treshold")),
("timeout", _("timeout")),
("other", _("other")),
],
default_value="ok",
),
),
(
"packets_lost_src->dest",
Tuple(
title=_("Packets lost src->dest"),
elements=[
Integer(
title=_("Warning at"),
unit=_("packets"),
minvalue=1,
default_value=100,
),
Integer(
title=_("Critical at"),
unit=_("packets"),
minvalue=1,
default_value=1000,
),
],
),
),
(
"packets_lost_dest->src",
Tuple(
title=_("Packets lost dest->src"),
elements=[
Integer(
title=_("Warning at"),
unit=_("packets"),
minvalue=1,
default_value=100,
),
Integer(
title=_("Critical at"),
unit=_("packets"),
minvalue=1,
default_value=1000,
),
],
),
),
],
)
rulespec_registry.register(
CheckParameterRulespecWithItem(
check_group_name="cisco_ip_sla",
group=RulespecGroupCheckParametersNetworking,
item_spec=_item_spec_cisco_ip_sla,
match_type="dict",
parameter_valuespec=_parameter_valuespec_cisco_ip_sla,
title=lambda: _("Cisco IP SLA"),
)
)

View File

@ -0,0 +1,237 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Copyright (C) 2019 tribe29 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.
factory_settings["cisco_ip_sla_default_levels"] = {
"state": "active",
"connection_lost_occured": "no",
"timeout_occured": "no",
"completion_time_over_treshold_occured": "no",
"latest_rtt_completion_time": (250, 500),
"latest_rtt_state": "ok",
"packets_lost_src->dest": (100, 1000),
"packets_lost_dest->src": (100, 1000),
}
def parse_cisco_ip_sla(info):
precisions = {line[0]: "ms" if line[-1] == "1" else "us" for line in info[0]}
rtt_types = {
"1": "echo",
"2": "path echo",
"3": "file IO",
"4": "script",
"5": "UDP echo",
"6": "TCP connect",
"7": "HTTP",
"8": "DNS",
"9": "jitter",
"10": "DLSw",
"11": "DHCP",
"12": "FTP",
"13": "VoIP",
"14": "RTP",
"15": "LSP group",
"16": "ICMP jitter",
"17": "LSP ping",
"18": "LSP trace",
"19": "ethernet ping",
"20": "ethernet jitter",
"21": "LSP ping pseudowire",
}
states = {
"1": "reset",
"2": "orderly stop",
"3": "immediate stop",
"4": "pending",
"5": "inactive",
"6": "active",
"7": "restart",
}
rtt_states = {
"0": "other",
"1": "ok",
"2": "disconnected",
"3": "over threshold",
"4": "timeout",
"5": "busy",
"6": "not connected",
"7": "dropped",
"8": "sequence error",
"9": "verify error",
"10": "application specific error",
}
def to_ip_address(int_list):
if len(int_list) == 4:
return "%d.%d.%d.%d" % tuple(int_list)
elif len(int_list) == 6:
return "%d:%d:%d:%d:%d:%d" % tuple(int_list)
return ""
# contains description, parse function, unit and type
contents = [
( # rttMonEchoAdminEntry
("Target address", to_ip_address, "", None),
("Source address", to_ip_address, "", None),
# rttMonEchoAdminPrecision is deliberatly dropped by zip below
),
( # rttMonCtrlAdminEntry
("Owner", None, "", None),
("Tag", None, "", None),
("RTT type", lambda x: rtt_types.get(x, "unknown"), "", "option"),
("Threshold", int, "ms", "option"),
),
( # rttMonCtrlOperEntry
("State", lambda x: states.get(x, "unknown"), "", "option"),
("Text", None, "", None),
("Connection lost occured", lambda x: "yes" if x == "1" else "no", "", "option"),
("Timeout occured", lambda x: "yes" if x == "1" else "no", "", "option"),
(
"Completion time over treshold occured",
lambda x: "yes" if x == "1" else "no",
"",
"option",
),
),
( # rttMonLatestRttOperEntry
("Latest RTT completion time", int, "ms/us", "level"),
("Latest RTT state", lambda x: rtt_states.get(x, "unknown"), "", "option"),
),
( # rttMonJitterStatsEntry
("Packets lost src->dest", int, "packets", "level"),
("Packets lost dest->src", int, "packets", "level"),
),
]
parsed = {}
for content, entries in zip(contents, info):
if not entries:
continue
for entry in entries:
index, values = entry[0], entry[1:]
data = parsed.setdefault(index, [])
for (description, parser, unit, type_), value in zip(content, values):
if parser:
value = parser(value)
if unit == "ms/us":
unit = precisions[index]
data.append((description, value, unit, type_))
return parsed
def inventory_cisco_ip_sla(parsed):
for index in parsed:
yield index, {}
@get_parsed_item_data
def check_cisco_ip_sla(_item, params, data):
for description, value, unit, type_ in data:
if not value and "packets" not in unit:
continue
state = 0
if unit:
infotext = "%s: %s %s" % (description, value, unit)
else:
infotext = "%s: %s" % (description, value)
perfdata = []
param = params.get(description.lower().replace(" ", "_"))
if type_ == "option":
if param and param != value:
state = 1
infotext += " (expected %s)" % param
elif type_ == "level":
warn, crit = param # a default level hat to exist
if value >= crit:
state = 2
elif value >= warn:
state = 1
if state:
infotext += " (warn/crit at %s/%s)" % (warn, crit)
if unit == "ms/us":
factor = 1e3 if unit == "ms" else 1e6
perfdata = [
("rtt", value / factor, warn / factor, crit / factor)
] # fixed: true-division
elif unit == "packets":
perfdata = [
("lost", value, warn, crit)
]
yield state, infotext, perfdata
check_info["cisco_ip_sla"] = {
"parse_function": parse_cisco_ip_sla,
"inventory_function": inventory_cisco_ip_sla,
"check_function": check_cisco_ip_sla,
"service_description": "Cisco IP SLA %s",
"group": "cisco_ip_sla",
"default_levels_variable": "cisco_ip_sla_default_levels",
"has_perfdata": True,
"snmp_scan_function": lambda oid: "cisco" in oid(".1.3.6.1.2.1.1.1.0").lower()
and "ios" in oid(".1.3.6.1.2.1.1.1.0").lower()
and oid(".1.3.6.1.4.1.9.9.42.1.2.2.1.37.*"),
"snmp_info": [
(
".1.3.6.1.4.1.9.9.42.1.2.2.1",
[
OID_END,
BINARY(2), # rttMonEchoAdminTargetAddress
BINARY(6), # rttMonEchoAdminSourceAddress
# only needed to determine the unit (ms/us)
37, # rttMonEchoAdminPrecision
],
),
(
".1.3.6.1.4.1.9.9.42.1.2.1.1",
[
OID_END,
2, # rttMonCtrlAdminOwner
3, # rttMonCtrlAdminTag
4, # rttMonCtrlAdminRttType
5, # rttMonCtrlAdminThreshold
],
),
(
".1.3.6.1.4.1.9.9.42.1.2.9.1",
[
OID_END,
10, # rttMonCtrlOperState
2, # rttMonCtrlOperDiagText
5, # rttMonCtrlOperConnectionLostOccurred
6, # rttMonCtrlOperTimeoutOccurred
7, # rttMonCtrlOperOverThresholdOccurred
],
),
(
".1.3.6.1.4.1.9.9.42.1.2.10.1",
[
OID_END,
1, # rttMonLatestRttOperCompletionTime
2, # rttMonLatestRttOperSense
],
),
(
".1.3.6.1.4.1.9.9.42.1.5.2.1",
[
OID_END,
26, # rttMonLatestJitterOperPacketLossSD
27, # rttMonLatestJitterOperPacketLossDS
]
)
],
}

View File

@ -0,0 +1,20 @@
{'author': u'George Pochiscan',
'description': u'',
'download_url': '',
'files': {'checkman': ['clever_pdu',
'clever_pdu_humidity',
'clever_pdu_temp'],
'checks': ['clever_pdu_120',
'clever_pdu_130',
'clever_pdu_humidity_120',
'clever_pdu_humidity_130',
'clever_pdu_temp_120',
'clever_pdu_temp_130'],
'web': ['plugins/wato/clever_pdu.py']},
'name': 'clever_pdu_1-6',
'num_files': 10,
'title': u'Clever PDU checks for Checkmk 1.6',
'version': '1.1',
'version.min_required': '1.6.0p20',
'version.packaged': '1.6.0p29',
'version.usable_until': '2.0.0p1'}

Binary file not shown.

View File

@ -0,0 +1,13 @@
title: Clever PDU Units: Power and Voltage
agents: snmp
catalog: hw/power/clever
license: GPLv2
distribution: check_mk
description:
Monitors Power, Voltage and energy on Clever PDU Units.
item:
ID of the Line.
discovery:
One service is created for each Line.

View File

@ -0,0 +1,13 @@
title: Clever PDU Units: Master Humidity
agents: snmp
catalog: hw/power/clever
license: GPLv2
distribution: check_mk
description:
Monitors Master Humidity on Clever PDU Units.
item:
Master Humidity.
discovery:
One service is created.

View File

@ -0,0 +1,13 @@
title: Clever PDU Units: Master Temperature
agents: snmp
catalog: hw/power/clever
license: GPLv2
distribution: check_mk
description:
Monitors Master Temperature on Clever PDU Units.
item:
Master Temperature.
discovery:
One service is created.

View File

@ -0,0 +1,129 @@
#!/usr/bin/python
# -*- encoding: utf-8; py-indent-offset: 4 -*-
# +------------------------------------------------------------------+
# | ____ _ _ __ __ _ __ |
# | / ___| |__ ___ ___| | __ | \/ | |/ / |
# | | | | '_ \ / _ \/ __| |/ / | |\/| | ' / |
# | | |___| | | | __/ (__| < | | | | . \ |
# | \____|_| |_|\___|\___|_|\_\___|_| |_|_|\_\ |
# | |
# | Copyright Mathias Kettner 2016 mk@mathias-kettner.de |
# +------------------------------------------------------------------+
#
# This file is part of Check_MK.
# The official homepage is at http://mathias-kettner.de/check_mk.
#
# check_mk is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation in version 2. check_mk is distributed
# in the hope that it will be useful, but WITHOUT ANY WARRANTY; with-
# out even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE. See the GNU General Public License for more de-
# tails. You should have received a copy of the GNU General Public
# License along with GNU Make; see the file COPYING. If not, write
# to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
# Boston, MA 02110-1301 USA.
factory_settings["clever_pdu_default_levels"] ={
"voltage": (240, 250),
"current": (32, 33),
"energy": (35000, 36000),
}
lines = {"Line 1", "Line 2", "Line 3"}
_UNIT_MAP = {
"voltage": "V" ,
"current": "A" ,
"energy": "W",
}
def parse_clever_pdu_120(info):
data=info[0]
parsed = {}
parsed = {
"Line 1" : {
"voltage": float(data[0]),
"current": float(data[3])/10,
"energy": float(data[6]),
},
"Line 2" : {
"voltage": float(data[1]),
"current": float(data[4])/10,
"energy": float(data[7]),
},
"Line 3" : {
"voltage": float(data[2]),
"current": float(data[5])/10,
"energy": float(data[8]),
},
"Total Energy" : {
"energy" : float((float(data[0])*float(data[3])/10)) + float((float(data[1])*float(data[4])/10)) + float((float(data[2])*float(data[5])/10)),
},
}
return parsed
def inventory_clever_pdu_120(parsed):
for line in parsed:
yield line, {}
def check_clever_pdu_120(item, params, parsed):
if "Total" not in item:
for param in params:
levels_lower = levels_upper = None
warn, crit = params.get(param)
if warn > crit:
levels_lower = warn, crit
else:
levels_upper = warn, crit
yield check_levels(
parsed.get(item).get(param),
param,
(warn, crit),
unit = _UNIT_MAP.get(param),
infoname = param
)
else:
for param in params:
if "energy" in param:
levels_lower = levels_upper = None
warn, crit = params.get(param)
if warn > crit:
levels_lower = warn, crit
else:
levels_upper = warn, crit
yield check_levels(
parsed.get(item).get(param),
param,
(warn, crit),
unit = _UNIT_MAP.get(param),
infoname = param
)
check_info['clever_pdu_120'] = {
'parse_function' : parse_clever_pdu_120,
'inventory_function' : inventory_clever_pdu_120,
'check_function' : check_clever_pdu_120,
'service_description' : '%s',
'has_perfdata' : True,
'group' : "clever_pdu",
'snmp_info' : ('.1.3.6.1.4.1.30966.10.3.2',
[
'1',
'2',
'3',
'4',
'5',
'6',
'7',
'8',
'9',
],
),
'snmp_scan_function' : lambda oid: oid(".1.3.6.1.2.1.1.2.0").startswith(".1.3.6.1.4.1.30966") and oid(".1.3.6.1.4.1.30966.10.3.2.70.0"),
'default_levels_variable' : 'clever_pdu_default_levels',
}

View File

@ -0,0 +1,127 @@
#!/usr/bin/python
# -*- encoding: utf-8; py-indent-offset: 4 -*-
# +------------------------------------------------------------------+
# | ____ _ _ __ __ _ __ |
# | / ___| |__ ___ ___| | __ | \/ | |/ / |
# | | | | '_ \ / _ \/ __| |/ / | |\/| | ' / |
# | | |___| | | | __/ (__| < | | | | . \ |
# | \____|_| |_|\___|\___|_|\_\___|_| |_|_|\_\ |
# | |
# | Copyright Mathias Kettner 2016 mk@mathias-kettner.de |
# +------------------------------------------------------------------+
#
# This file is part of Check_MK.
# The official homepage is at http://mathias-kettner.de/check_mk.
#
# check_mk is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation in version 2. check_mk is distributed
# in the hope that it will be useful, but WITHOUT ANY WARRANTY; with-
# out even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE. See the GNU General Public License for more de-
# tails. You should have received a copy of the GNU General Public
# License along with GNU Make; see the file COPYING. If not, write
# to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
# Boston, MA 02110-1301 USA.
factory_settings["clever_pdu_default_levels"] ={
"voltage": (240, 250),
"current": (32, 33),
"energy": (35000, 36000),
}
lines = {"Line 1", "Line 2", "Line 3"}
_UNIT_MAP = {
"voltage": "V" ,
"current": "A" ,
"energy": "W",
}
def parse_clever_pdu(info):
data=info[0]
parsed = {}
parsed = {
"Line 1" : {
"voltage": float(data[0]),
"current": float(float(data[3])/10),
"energy" : float((float(data[0])*float(data[3])/10)),
},
"Line 2" : {
"voltage": float(data[1]),
"current": float(float(data[4])/10),
"energy" : float((float(data[1])*float(data[4])/10)),
},
"Line 3" : {
"voltage": float(data[2]),
"current": float(float(data[5])/10),
"energy" : float((float(data[2])*float(data[5])/10)),
},
"Total Energy" : {
"energy" : float((float(data[0])*float(data[3])/10)) + float((float(data[1])*float(data[4])/10)) + float((float(data[2])*float(data[5])/10)),
},
}
return parsed
def inventory_clever_pdu(parsed):
for line in parsed:
yield line, {}
def check_clever_pdu(item, params, parsed):
if "Total" not in item:
for param in params:
levels_lower = levels_upper = None
warn, crit = params.get(param)
if warn > crit:
levels_lower = warn, crit
else:
levels_upper = warn, crit
yield check_levels(
parsed.get(item).get(param),
param,
(warn, crit),
unit = _UNIT_MAP.get(param),
infoname = param
)
else:
for param in params:
if "energy" in param:
levels_lower = levels_upper = None
warn, crit = params.get(param)
if warn > crit:
levels_lower = warn, crit
else:
levels_upper = warn, crit
yield check_levels(
parsed.get(item).get(param),
param,
(warn, crit),
unit = _UNIT_MAP.get(param),
infoname = param
)
check_info['clever_pdu'] = {
'parse_function' : parse_clever_pdu,
'inventory_function' : inventory_clever_pdu,
'check_function' : check_clever_pdu,
'service_description' : '%s',
'has_perfdata' : True,
'group' : "clever_pdu",
'snmp_info' : ('.1.3.6.1.4.1.30966.10.3.2',
[
'1',
'2',
'3',
'4',
'5',
'6',
],
),
'snmp_scan_function' : lambda oid: oid(".1.3.6.1.2.1.1.2.0").startswith(".1.3.6.1.4.1.30966") and not oid(".1.3.6.1.4.1.30966.10.3.2.70.0"),
'default_levels_variable' : 'clever_pdu_default_levels',
}

View File

@ -0,0 +1,49 @@
#!/usr/bin/python
# -*- encoding: utf-8; py-indent-offset: 4 -*-
# +------------------------------------------------------------------+
# | ____ _ _ __ __ _ __ |
# | / ___| |__ ___ ___| | __ | \/ | |/ / |
# | | | | '_ \ / _ \/ __| |/ / | |\/| | ' / |
# | | |___| | | | __/ (__| < | | | | . \ |
# | \____|_| |_|\___|\___|_|\_\___|_| |_|_|\_\ |
# | |
# | Copyright Mathias Kettner 2016 mk@mathias-kettner.de |
# +------------------------------------------------------------------+
#
# This file is part of Check_MK.
# The official homepage is at http://mathias-kettner.de/check_mk.
#
# check_mk is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation in version 2. check_mk is distributed
# in the hope that it will be useful, but WITHOUT ANY WARRANTY; with-
# out even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE. See the GNU General Public License for more de-
# tails. You should have received a copy of the GNU General Public
# License along with GNU Make; see the file COPYING. If not, write
# to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
# Boston, MA 02110-1301 USA.
factory_settings["clever_pdu_humidity_default_levels"] = {
"levels": (60, 70),
}
def inventory_clever_pdu_humidity_120(info):
yield "Master humidity", {}
def check_clever_pdu_humidity_120(item, params, info):
return check_humidity(float(info[0][0]), params)
check_info['clever_pdu_humidity_120'] = {
'inventory_function' : inventory_clever_pdu_humidity_120,
'check_function' : check_clever_pdu_humidity_120,
'service_description' : '%s',
'has_perfdata' : True,
'snmp_info' : ('.1.3.6.1.4.1.30966.10.3.2.14', ['0']),
'snmp_scan_function' : lambda oid: oid(".1.3.6.1.2.1.1.2.0").startswith(".1.3.6.1.4.1.30966") and oid(".1.3.6.1.4.1.30966.10.3.2.70.0"),
'group' : 'humidity',
'default_levels_variable' : 'clever_pdu_humidity_default_levels',
'includes' : ['humidity.include'],
}

View File

@ -0,0 +1,49 @@
#!/usr/bin/python
# -*- encoding: utf-8; py-indent-offset: 4 -*-
# +------------------------------------------------------------------+
# | ____ _ _ __ __ _ __ |
# | / ___| |__ ___ ___| | __ | \/ | |/ / |
# | | | | '_ \ / _ \/ __| |/ / | |\/| | ' / |
# | | |___| | | | __/ (__| < | | | | . \ |
# | \____|_| |_|\___|\___|_|\_\___|_| |_|_|\_\ |
# | |
# | Copyright Mathias Kettner 2016 mk@mathias-kettner.de |
# +------------------------------------------------------------------+
#
# This file is part of Check_MK.
# The official homepage is at http://mathias-kettner.de/check_mk.
#
# check_mk is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation in version 2. check_mk is distributed
# in the hope that it will be useful, but WITHOUT ANY WARRANTY; with-
# out even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE. See the GNU General Public License for more de-
# tails. You should have received a copy of the GNU General Public
# License along with GNU Make; see the file COPYING. If not, write
# to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
# Boston, MA 02110-1301 USA.
factory_settings["clever_pdu_humidity_default_levels"] = {
"levels": (60, 70),
}
def inventory_clever_pdu_humidity(info):
yield "Master humidity", {}
def check_clever_pdu_humidity(item, params, info):
return check_humidity(float(info[0][0]), params)
check_info['clever_pdu_humidity'] = {
'inventory_function' : inventory_clever_pdu_humidity,
'check_function' : check_clever_pdu_humidity,
'service_description' : '%s',
'has_perfdata' : True,
'snmp_info' : ('.1.3.6.1.4.1.30966.10.3.2.11', ['0']),
'snmp_scan_function' : lambda oid: oid(".1.3.6.1.2.1.1.2.0").startswith(".1.3.6.1.4.1.30966") and not oid(".1.3.6.1.4.1.30966.10.3.2.70.0"),
'group' : 'humidity',
'default_levels_variable' : 'clever_pdu_humidity_default_levels',
'includes' : ['humidity.include'],
}

View File

@ -0,0 +1,49 @@
#!/usr/bin/python
# -*- encoding: utf-8; py-indent-offset: 4 -*-
# +------------------------------------------------------------------+
# | ____ _ _ __ __ _ __ |
# | / ___| |__ ___ ___| | __ | \/ | |/ / |
# | | | | '_ \ / _ \/ __| |/ / | |\/| | ' / |
# | | |___| | | | __/ (__| < | | | | . \ |
# | \____|_| |_|\___|\___|_|\_\___|_| |_|_|\_\ |
# | |
# | Copyright Mathias Kettner 2016 mk@mathias-kettner.de |
# +------------------------------------------------------------------+
#
# This file is part of Check_MK.
# The official homepage is at http://mathias-kettner.de/check_mk.
#
# check_mk is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation in version 2. check_mk is distributed
# in the hope that it will be useful, but WITHOUT ANY WARRANTY; with-
# out even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE. See the GNU General Public License for more de-
# tails. You should have received a copy of the GNU General Public
# License along with GNU Make; see the file COPYING. If not, write
# to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
# Boston, MA 02110-1301 USA.
factory_settings["clever_pdu_temp_default_levels"] = {
"levels": (60, 70),
}
def inventory_clever_pdu_temp_120(info):
yield "Master Temperature", {}
def check_clever_pdu_temp_120(item, params, info):
return check_temperature(float(info[0][0]), params, "Master Temperature %s" %item)
check_info['clever_pdu_temp_120'] = {
'inventory_function' : inventory_clever_pdu_temp_120,
'check_function' : check_clever_pdu_temp_120,
'service_description' : '%s',
'has_perfdata' : True,
'snmp_info' : ('.1.3.6.1.4.1.30966.10.3.2.13', ['0']),
'snmp_scan_function' : lambda oid: oid(".1.3.6.1.2.1.1.2.0").startswith(".1.3.6.1.4.1.30966") and oid(".1.3.6.1.4.1.30966.10.3.2.70.0"),
'group' : 'temperature',
'default_levels_variable' : 'clever_pdu_temp_default_levels',
'includes' : ['temperature.include'],
}

View File

@ -0,0 +1,49 @@
#!/usr/bin/python
# -*- encoding: utf-8; py-indent-offset: 4 -*-
# +------------------------------------------------------------------+
# | ____ _ _ __ __ _ __ |
# | / ___| |__ ___ ___| | __ | \/ | |/ / |
# | | | | '_ \ / _ \/ __| |/ / | |\/| | ' / |
# | | |___| | | | __/ (__| < | | | | . \ |
# | \____|_| |_|\___|\___|_|\_\___|_| |_|_|\_\ |
# | |
# | Copyright Mathias Kettner 2016 mk@mathias-kettner.de |
# +------------------------------------------------------------------+
#
# This file is part of Check_MK.
# The official homepage is at http://mathias-kettner.de/check_mk.
#
# check_mk is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation in version 2. check_mk is distributed
# in the hope that it will be useful, but WITHOUT ANY WARRANTY; with-
# out even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE. See the GNU General Public License for more de-
# tails. You should have received a copy of the GNU General Public
# License along with GNU Make; see the file COPYING. If not, write
# to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
# Boston, MA 02110-1301 USA.
factory_settings["clever_pdu_temp_default_levels"] = {
"levels": (60, 70),
}
def inventory_clever_pdu_temp(info):
yield "Master Temperature", {}
def check_clever_pdu_temp(item, params, info):
return check_temperature(float(info[0][0]), params, "Master Temperature %s" %item)
check_info['clever_pdu_temp'] = {
'inventory_function' : inventory_clever_pdu_temp,
'check_function' : check_clever_pdu_temp,
'service_description' : '%s',
'has_perfdata' : True,
'snmp_info' : ('.1.3.6.1.4.1.30966.10.3.2.10', ['0']),
'snmp_scan_function' : lambda oid: oid(".1.3.6.1.2.1.1.2.0").startswith(".1.3.6.1.4.1.30966") and not oid(".1.3.6.1.4.1.30966.10.3.2.70.0"),
'group' : 'temperature',
'default_levels_variable' : 'clever_pdu_temp_default_levels',
'includes' : ['temperature.include'],
}

View File

@ -0,0 +1,83 @@
#!/usr/bin/python
# -*- encoding: utf-8; py-indent-offset: 4 -*-
# +------------------------------------------------------------------+
# | ____ _ _ __ __ _ __ |
# | / ___| |__ ___ ___| | __ | \/ | |/ / |
# | | | | '_ \ / _ \/ __| |/ / | |\/| | ' / |
# | | |___| | | | __/ (__| < | | | | . \ |
# | \____|_| |_|\___|\___|_|\_\___|_| |_|_|\_\ |
# | |
# | Copyright Mathias Kettner 2014 mk@mathias-kettner.de |
# +------------------------------------------------------------------+
#
# This file is part of Check_MK.
# The official homepage is at http://mathias-kettner.de/check_mk.
#
# check_mk is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation in version 2. check_mk is distributed
# in the hope that it will be useful, but WITHOUT ANY WARRANTY; with-
# out even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE. See the GNU General Public License for more de-
# tails. You should have received a copy of the GNU General Public
# License along with GNU Make; see the file COPYING. If not, write
# to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
# Boston, MA 02110-1301 USA.
from cmk.gui.i18n import _
from cmk.gui.plugins.wato import (
CheckParameterRulespecWithItem,
rulespec_registry,
RulespecGroupCheckParametersEnvironment,
)
from cmk.gui.valuespec import Dictionary, Integer, TextAscii, Tuple
def _parameter_valuespec_clever_pdu():
return Dictionary(
elements=[
(
"voltage",
Tuple(
title=_("Voltage on Line"),
elements=[
Integer(title=_("warning at"), unit=_("V")),
Integer(title=_("critical at"), unit=_("V")),
],
),
),
(
"current",
Tuple(
title=_("Current on Power Channel"),
elements=[
Integer(title=_("warning if below"), unit=_("A")),
Integer(title=_("critical if below"), unit=_("A")),
],
),
),
(
"energy",
Tuple(
title=_("Active Energy of Line"),
elements=[
Integer(title=_("warning at"), unit=_("W")),
Integer(title=_("critical at"), unit=_("W")),
],
),
),
],
)
rulespec_registry.register(
CheckParameterRulespecWithItem(
check_group_name="clever_pdu",
group=RulespecGroupCheckParametersEnvironment,
item_spec=lambda: TextAscii(title=_("Line"),),
match_type="dict",
parameter_valuespec=_parameter_valuespec_clever_pdu,
title=lambda: _("Levels for Clever AC PDU Devices"),
)
)

View File

@ -0,0 +1,13 @@
title: Clever PDU Units: Power and Voltage
agents: snmp
catalog: hw/power/clever
license: GPLv2
distribution: check_mk
description:
Monitors Power, Voltage and energy on Clever PDU Units.
item:
ID of the Line.
discovery:
One service is created for each Line.

View File

@ -0,0 +1,13 @@
title: Clever PDU Units: Master Humidity
agents: snmp
catalog: hw/power/clever
license: GPLv2
distribution: check_mk
description:
Monitors Master Humidity on Clever PDU Units.
item:
Master Humidity.
discovery:
One service is created.

View File

@ -0,0 +1,13 @@
title: Clever PDU Units: Master Temperature
agents: snmp
catalog: hw/power/clever
license: GPLv2
distribution: check_mk
description:
Monitors Master Temperature on Clever PDU Units.
item:
Master Temperature.
discovery:
One service is created.

Binary file not shown.

View File

@ -0,0 +1,20 @@
{'author': 'George Pochiscan',
'description': 'Ported Clever AC PDU from 2.1.0 checkmk version to 2.0.0 '
'checkmk version.\n',
'download_url': '',
'files': {'agent_based': ['utils/humidity.py',
'clever_pdu_120.py',
'clever_pdu_130.py',
'clever_pdu_humidity_120.py',
'clever_pdu_humidity_130.py',
'clever_pdu_temp_120.py',
'clever_pdu_temp_130.py'],
'checkman': ['clever_pdu', 'clever_pdu_humidity', 'clever_pdu_temp'],
'web': ['plugins/wato/clever_pdu.py']},
'name': 'clever_pdu_2',
'num_files': 11,
'title': 'Clever PDU checks for 2.0 checkmk version',
'version': '1.0.1',
'version.min_required': '2.0.0p20',
'version.packaged': '2.0.0p29',
'version.usable_until': '2.1.0p20'}

View File

@ -0,0 +1,126 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Copyright (C) 2019 tribe29 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 dataclasses import dataclass
from typing import Iterable, Mapping, Tuple, NamedTuple
from .agent_based_api.v1 import check_levels, equals, register, Service, SNMPTree, all_of, exists
from .agent_based_api.v1.type_defs import CheckResult, DiscoveryResult, StringTable
def parse_clever_pdu_120(string_table: StringTable):
data=string_table[0]
parsed = {}
parsed = {
"Line 1" : {
"voltage": float(data[0]),
"current": float(data[3])/10,
"energy": float(data[6]),
},
"Line 2" : {
"voltage": float(data[1]),
"current": float(data[4])/10,
"energy": float(data[7]),
},
"Line 3" : {
"voltage": float(data[2]),
"current": float(data[5])/10,
"energy": float(data[8]),
},
"Total Energy" : {
"energy" : float((float(data[0])*float(data[3])/10)) + float((float(data[1])*float(data[4])/10)) + float((float(data[2])*float(data[5])/10)),
},
}
return parsed
_UNIT_MAP = {
"voltage": "V" ,
"current": "A" ,
"energy": "W",
}
register.snmp_section(
name="clever_pdu_120",
parsed_section_name="clever_pdu_120",
parse_function=parse_clever_pdu_120,
detect = all_of(
equals(
".1.3.6.1.2.1.1.2.0",
".1.3.6.1.4.1.30966",
),
exists(".1.3.6.1.4.1.30966.10.3.2.70.0"),
),
fetch=SNMPTree(
".1.3.6.1.4.1.30966.10.3.2",
[
"1", # mVoltageA
"2", # mVoltageB
"3", # mVoltageC
"4", # mCurrentA
"5", # mCurrentB
"6", # mCurrentC
"7", # mEnergyA
"8", # mEnergyB
"9", # mEnergyC
],
),
)
def discover_clever_pdu_120(section) -> DiscoveryResult:
yield from (Service(item=line_num) for line_num in section)
def check_clever_pdu_120(item, params, section) -> CheckResult:
if "Total" not in item:
for param in params:
levels_lower = levels_upper = None
warn, crit = params.get(param)
if warn > crit:
levels_lower = warn, crit
else:
levels_upper = warn, crit
yield from check_levels(
section.get(item)[param],
levels_upper = levels_upper,
levels_lower = levels_lower,
metric_name = param,
render_func=lambda v: f"{v:.2f} {_UNIT_MAP[param]}",
label = param,
)
else:
for param in params:
if "energy" in param:
levels_lower = levels_upper = None
warn, crit = params.get(param)
if warn > crit:
levels_lower = warn, crit
else:
levels_upper = warn, crit
yield from check_levels(
section.get(item)[param],
levels_upper = levels_upper,
levels_lower = levels_lower,
metric_name = param,
render_func=lambda v: f"{v:.2f} {_UNIT_MAP[param]}",
label = param,
)
register.check_plugin(
name="clever_pdu_120",
service_name="%s",
discovery_function=discover_clever_pdu_120,
check_function=check_clever_pdu_120,
check_ruleset_name="clever_pdu",
check_default_parameters={
"voltage": (220, 210),
"current": (32, 33),
"energy": (35000, 36000),
},
)

View File

@ -0,0 +1,125 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Copyright (C) 2019 tribe29 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 dataclasses import dataclass
from typing import Iterable, Mapping, Tuple, NamedTuple
from .agent_based_api.v1 import check_levels, equals, register, Service, SNMPTree, all_of, not_exists
from .agent_based_api.v1.type_defs import CheckResult, DiscoveryResult, StringTable
def parse_clever_pdu_130(string_table: StringTable):
data=string_table[0]
parsed = {}
parsed = {
"Line 1" : {
"voltage": float(data[0]),
"current": float(data[3])/10,
"energy" : float((float(data[0])*float(data[3])/10)),
},
"Line 2" : {
"voltage": float(data[1]),
"current": float(data[4])/10,
"energy" : float((float(data[1])*float(data[4])/10)),
},
"Line 3" : {
"voltage": float(data[2]),
"current": float(data[5])/10,
"energy" : float((float(data[2])*float(data[5])/10)),
},
"Total Energy" : {
"energy" : float((float(data[0])*float(data[3])/10)) + float((float(data[1])*float(data[4])/10)) + float((float(data[2])*float(data[5])/10)),
},
}
return parsed
lines = {"Line 1", "Line 2", "Line 3"}
_UNIT_MAP = {
"voltage": "V" ,
"current": "A" ,
"energy": "W",
}
register.snmp_section(
name="clever_pdu_130",
parsed_section_name="clever_pdu_130",
parse_function=parse_clever_pdu_130,
detect = all_of(
equals(
".1.3.6.1.2.1.1.2.0",
".1.3.6.1.4.1.30966",
),
not_exists(".1.3.6.1.4.1.30966.10.3.2.70.0"),
),
fetch=SNMPTree(
".1.3.6.1.4.1.30966.10.3.2",
[
"1", # mVoltageA
"2", # mVoltageB
"3", # mVoltageC
"4", # mCurrentA
"5", # mCurrentB
"6", # mCurrentC
],
),
)
def discover_clever_pdu_130(section) -> DiscoveryResult:
yield from (Service(item=line_num) for line_num in section)
def check_clever_pdu_130(item, params, section) -> CheckResult:
if "Total" not in item:
for param in params:
levels_lower = levels_upper = None
warn, crit = params.get(param)
if warn > crit:
levels_lower = warn, crit
else:
levels_upper = warn, crit
yield from check_levels(
section.get(item)[param],
levels_upper = levels_upper,
levels_lower = levels_lower,
metric_name = param,
render_func=lambda v: f"{v:.2f} {_UNIT_MAP[param]}",
label = param,
)
else:
for param in params:
if "energy" in param:
levels_lower = levels_upper = None
warn, crit = params.get(param)
if warn > crit:
levels_lower = warn, crit
else:
levels_upper = warn, crit
yield from check_levels(
section.get(item)[param],
levels_upper = levels_upper,
levels_lower = levels_lower,
metric_name = param,
render_func=lambda v: f"{v:.2f} {_UNIT_MAP[param]}",
label = param,
)
register.check_plugin(
name="clever_pdu_130",
service_name="%s",
discovery_function=discover_clever_pdu_130,
check_function=check_clever_pdu_130,
check_ruleset_name="clever_pdu",
check_default_parameters={
"voltage": (220, 210),
"current": (32, 33),
"energy": (35000, 36000),
},
)

View File

@ -0,0 +1,64 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Copyright (C) 2019 tribe29 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 typing import Mapping, Any
from .agent_based_api.v1 import check_levels, equals, register, Service, SNMPTree, get_value_store, all_of, exists
from .agent_based_api.v1.type_defs import CheckResult, DiscoveryResult, StringTable
from .utils.humidity import check_humidity
CheckParams = Mapping[str, Any]
def parse_clever_pdu_humidity_120(string_table: StringTable):
data=string_table[0]
parsed = {}
parsed = {
"Master Humidity" : int(data[0]),
}
return parsed
register.snmp_section(
name="clever_pdu_humidity_120",
parsed_section_name="clever_pdu_humidity_120",
parse_function=parse_clever_pdu_humidity_120,
detect=all_of(
equals(
".1.3.6.1.2.1.1.2.0",
".1.3.6.1.4.1.30966",
),
exists(".1.3.6.1.4.1.30966.10.3.2.70.0"),
),
fetch=SNMPTree(
".1.3.6.1.4.1.30966.10.3.2",
[
"14", # mHumidity
],
),
)
def discover_clever_pdu_humidity_120(section) -> DiscoveryResult:
if section.get("Master Humidity") == 0:
return
else:
yield from (Service(item=item) for item in section)
def check_clever_pdu_humidity_120(item, params: CheckParams, section) -> CheckResult:
yield from check_humidity(
section.get("Master Humidity"),
params,
)
register.check_plugin(
name="clever_pdu_humidity_120",
service_name="%s",
discovery_function=discover_clever_pdu_humidity_120,
check_function=check_clever_pdu_humidity_120,
check_ruleset_name="humidity",
check_default_parameters={},
)

View File

@ -0,0 +1,64 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Copyright (C) 2019 tribe29 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 typing import Mapping, Any
from .agent_based_api.v1 import check_levels, equals, register, Service, SNMPTree, get_value_store, all_of, not_exists
from .agent_based_api.v1.type_defs import CheckResult, DiscoveryResult, StringTable
from .utils.humidity import check_humidity
CheckParams = Mapping[str, Any]
def parse_clever_pdu_humidity(string_table: StringTable):
data=string_table[0]
parsed = {}
parsed = {
"Master Humidity" : int(data[0]),
}
return parsed
register.snmp_section(
name="clever_pdu_humidity",
parsed_section_name="clever_pdu_humidity",
parse_function=parse_clever_pdu_humidity,
detect=all_of(
equals(
".1.3.6.1.2.1.1.2.0",
".1.3.6.1.4.1.30966",
),
not_exists(".1.3.6.1.4.1.30966.10.3.2.70.0"),
),
fetch=SNMPTree(
".1.3.6.1.4.1.30966.10.3.2",
[
"11", # mHumidity
],
),
)
def discover_clever_pdu_humidity(section) -> DiscoveryResult:
if section.get("Master Humidity") == 0:
return
else:
yield from (Service(item=item) for item in section)
def check_clever_pdu_humidity(item, params: CheckParams, section) -> CheckResult:
yield from check_humidity(
section.get("Master Humidity"),
params,
)
register.check_plugin(
name="clever_pdu_humidity",
service_name="%s",
discovery_function=discover_clever_pdu_humidity,
check_function=check_clever_pdu_humidity,
check_ruleset_name="humidity",
check_default_parameters={},
)

View File

@ -0,0 +1,69 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Copyright (C) 2019 tribe29 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 dataclasses import dataclass
from typing import Iterable, Mapping, Tuple, NamedTuple
from .agent_based_api.v1 import check_levels, equals, register, Service, SNMPTree, get_value_store, all_of, exists
from .agent_based_api.v1.type_defs import CheckResult, DiscoveryResult, StringTable
from .utils.temperature import check_temperature, TempParamDict
def parse_clever_pdu_temp_120(string_table: StringTable):
data=string_table[0]
parsed = {}
parsed = {
"Master Temperature" : int(data[0]),
}
return parsed
register.snmp_section(
name="clever_pdu_temp_120",
parsed_section_name="clever_pdu_temp_120",
parse_function=parse_clever_pdu_temp_120,
detect = all_of(
equals(
".1.3.6.1.2.1.1.2.0",
".1.3.6.1.4.1.30966",
),
exists(".1.3.6.1.4.1.30966.10.3.2.70.0"),
),
fetch=SNMPTree(
".1.3.6.1.4.1.30966.10.3.2",
[
"13", # mTemperature
],
),
)
def discover_clever_pdu_temp_120(section) -> DiscoveryResult:
if section.get("Master Temperature") == 0:
return
else:
yield from (Service(item=item) for item in section)
def check_clever_pdu_temp_120(item, params: TempParamDict, section) -> CheckResult:
if (temperature := section.get(item)) is None:
return
yield from check_temperature(
reading=temperature,
params=params,
unique_name=item,
value_store=get_value_store(),
)
register.check_plugin(
name="clever_pdu_temp_120",
service_name="%s",
discovery_function=discover_clever_pdu_temp_120,
check_function=check_clever_pdu_temp_120,
check_ruleset_name="temperature",
check_default_parameters={},
)

View File

@ -0,0 +1,69 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Copyright (C) 2019 tribe29 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 dataclasses import dataclass
from typing import Iterable, Mapping, Tuple, NamedTuple
from .agent_based_api.v1 import check_levels, equals, register, Service, SNMPTree, get_value_store, all_of, not_exists
from .agent_based_api.v1.type_defs import CheckResult, DiscoveryResult, StringTable
from .utils.temperature import check_temperature, TempParamDict
def parse_clever_pdu_temp(string_table: StringTable):
data=string_table[0]
parsed = {}
parsed = {
"Master Temperature" : int(data[0]),
}
return parsed
register.snmp_section(
name="clever_pdu_temp",
parsed_section_name="clever_pdu_temp",
parse_function=parse_clever_pdu_temp,
detect = all_of(
equals(
".1.3.6.1.2.1.1.2.0",
".1.3.6.1.4.1.30966",
),
not_exists(".1.3.6.1.4.1.30966.10.3.2.70.0"),
),
fetch=SNMPTree(
".1.3.6.1.4.1.30966.10.3.2",
[
"10", # mTemperature
],
),
)
def discover_clever_pdu_temp(section) -> DiscoveryResult:
if section.get("Master Temperature") == 0:
return
else:
yield from (Service(item=item) for item in section)
def check_clever_pdu_temp(item, params: TempParamDict, section) -> CheckResult:
if (temperature := section.get(item)) is None:
return
yield from check_temperature(
reading=temperature,
params=params,
unique_name=item,
value_store=get_value_store(),
)
register.check_plugin(
name="clever_pdu_temp",
service_name="%s",
discovery_function=discover_clever_pdu_temp,
check_function=check_clever_pdu_temp,
check_ruleset_name="temperature",
check_default_parameters={},
)

View File

@ -0,0 +1,33 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Copyright (C) 2019 tribe29 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 typing import Any, List, Mapping, Optional, Tuple, Union
from ..agent_based_api.v1 import check_levels, render, type_defs
CheckParams = Union[
None, Mapping[str, Any], Optional[List[float]], Tuple[float, float, float, float]
]
def check_humidity(humidity: float, params: CheckParams) -> type_defs.CheckResult:
levels_upper, levels_lower = None, None
if isinstance(params, dict):
levels_upper = params.get("levels") or None
levels_lower = params.get("levels_lower") or None
elif isinstance(params, (list, tuple)):
# old params = (crit_low , warn_low, warn, crit)
levels_upper = params[2], params[3]
levels_lower = params[1], params[0]
yield from check_levels(
humidity,
levels_upper=levels_upper,
levels_lower=levels_lower,
metric_name="humidity",
render_func=render.percent,
boundaries=(0, 100),
)

View File

@ -0,0 +1,65 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Copyright (C) 2019 tribe29 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.i18n import _
from cmk.gui.plugins.wato.utils import (
CheckParameterRulespecWithItem,
rulespec_registry,
RulespecGroupCheckParametersEnvironment,
)
from cmk.gui.valuespec import Dictionary, Integer, TextInput, Tuple
def _parameter_valuespec_clever_pdu():
return Dictionary(
elements=[
(
"voltage",
Tuple(
title=_("Voltage on Line"),
elements=[
Integer(title=_("warning at"), unit=_("V")),
Integer(title=_("critical at"), unit=_("V")),
],
),
),
(
"current",
Tuple(
title=_("Current on Power Channel"),
elements=[
Integer(title=_("warning if below"), unit=_("A")),
Integer(title=_("critical if below"), unit=_("A")),
],
),
),
(
"energy",
Tuple(
title=_("Active Energy of Line"),
elements=[
Integer(title=_("warning at"), unit=_("W")),
Integer(title=_("critical at"), unit=_("W")),
],
),
),
],
)
rulespec_registry.register(
CheckParameterRulespecWithItem(
check_group_name="clever_pdu",
group=RulespecGroupCheckParametersEnvironment,
item_spec=lambda: TextInput(
title=_("Line"), help=_("The Line Number. Example: 'Line 1'.")
),
match_type="dict",
parameter_valuespec=_parameter_valuespec_clever_pdu,
title=lambda: _("Levels for Clever AC PDU Devices"),
)
)

View File

@ -0,0 +1,92 @@
#!/usr/bin/env python3
# Copyright (C) 2019 tribe29 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.i18n import _
from cmk.gui.plugins.wato.utils import (
CheckParameterRulespecWithItem,
rulespec_registry,
RulespecGroupCheckParametersDiscovery,
)
from cmk.gui.valuespec import Dictionary, Float, Tuple
defaults={
"im_rate_upper": 999999,
"im_rate_lower": 0,
"rs_rate_upper": 999999,
"rs_rate_lower": 0,
}
mappings={
"im": "messages",
"rs": "bytes",
"upper": "above",
"lower": "below",
}
def _rate(metric, time, level):
unit=mappings[metric]
direction=mappings[level]
return (
f"{metric}_{time}_rate_{level}",
Tuple(
title=_(f"{level.capitalize()} level"),
elements=[
Float(
title=_(f"Warning {direction}"),
unit=f"{unit}/{time[1:]}min",
default_value=defaults[f"{metric}_rate_{level}"],
),
Float(
title=_(f"Critical {direction}"),
unit=f"{unit}/{time[1:]}min",
default_value=defaults[f"{metric}_rate_{level}"],
),
],
),
)
def _element(metric, time):
return (
f"{metric}_{time}_rate",
Dictionary(
title=_(f"Incoming {mappings[metric]} for past {time[1:]} minute"),
elements=[
_rate(metric, time, "upper"),
_rate(metric, time, "lower")
]
),
)
def _parameter_valuespec_graylog_input_metrics():
matrix=[
("im", "m1"), ("im", "m5"), ("im", "m15"),
("rs", "m1"), ("rs", "m5"), ("rs", "m15"),
]
elements=[
_element(metric, time) for metric, time in matrix
]
return Dictionary(
title=_("Message and data rates"),
help=_(
"These rates are queried directly from the Graylog instance. "
"Upper and lower levels can be specified for individual metric."
),
elements=elements
)
rulespec_registry.register(
CheckParameterRulespecWithItem(
check_group_name="graylog_input_metrics",
group=RulespecGroupCheckParametersDiscovery,
match_type="dict",
parameter_valuespec=_parameter_valuespec_graylog_input_metrics,
title=lambda: _("Graylog input metrics"),
)
)

View File

@ -0,0 +1,154 @@
#!/usr/bin/env python3
# Copyright (C) 2019 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.
import argparse
import json
import sys
from typing import NamedTuple
import requests
import urllib3
import cmk.utils.password_store
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
cmk.utils.password_store.replace_passwords()
class GraylogSection(NamedTuple):
name: str
uri: str
def main(argv=None):
if argv is None:
argv = sys.argv[1:]
args = parse_arguments(argv)
try:
handle_request(args)
except Exception:
if args.debug:
return 1
return 0
def handle_request(args): # pylint: disable=too-many-branches
url_base = f"{args.proto}://{args.hostname}:{args.port}/api"
url = url_base + "/system/metrics"
value = handle_response(url, args).json()
# Handle the input_metrics section. We need to merge information from
# both inputstates and input_metrics, and we do that by extracting the
# ids returned by both calls. Once merged, we return a single dictionary
# with state, name, type, port, and the various rates (either raw or
# incomingMessages).
url_inputs_data = url_base + "/cluster/inputstates"
inputs_data = handle_response(url_inputs_data, args).json()
inputs_data = tuple(inputs_data.values())[0]
metrics_data = value.get("meters")
if inputs_data is None or metrics_data is None:
return
# Create a dictionary, containing all metrics with substrings
# "incomingMessages" and "rawSize".
# All rates should exist, created and with values as low as "0.0".
metrics_dict = {}
for metric, metric_rate in metrics_data.items():
metric_id = metric.split(".")[-2]
metric_type = None
if "incomingMessages" in metric:
metric_type = "im"
elif "rawSize" in metric:
metric_type = "rs"
if metric_type:
metric_key = metrics_dict.setdefault(metric_id, {})
metric_key[f"{metric_type}_m1_rate"] = metric_rate["m1_rate"]
metric_key[f"{metric_type}_m5_rate"] = metric_rate["m5_rate"]
metric_key[f"{metric_type}_m15_rate"] = metric_rate["m15_rate"]
# Create a dictionary with all inputs and add the rates from
# the previous dictionary, metrics_dict. This is passed as output.
# Some inputs don't have a "port", so we handle this with .get("port").
inputs_dict = {}
for inputs in inputs_data:
message_input = inputs["message_input"]
input_id = inputs["id"]
input_state = inputs["state"]
input_name = message_input["title"]
input_type = message_input["name"]
input_port = message_input["attributes"].get("port")
input_rate = metrics_dict[input_id]
inputs_dict[input_id] = {
"input_state": input_state,
"input_name": input_name,
"input_type": input_type,
"input_port": input_port,
"im_m1_rate": input_rate["im_m1_rate"],
"im_m5_rate": input_rate["im_m5_rate"],
"im_m15_rate": input_rate["im_m15_rate"],
"rs_m1_rate": input_rate["rs_m1_rate"],
"rs_m5_rate": input_rate["rs_m5_rate"],
"rs_m15_rate": input_rate["rs_m15_rate"]
}
if inputs_dict:
handle_output(inputs_dict)
def handle_response(url, args):
try:
return requests.get(url, auth=(args.user, args.password), verify=not args.no_cert_check)
except requests.exceptions.RequestException as e:
sys.stderr.write("Error: %s\n" % e)
if args.debug:
raise
def handle_output(value):
sys.stdout.write("<<<graylog_input_metrics:sep(0)>>>\n")
if isinstance(value, list):
for entry in value:
sys.stdout.write("%s\n" % json.dumps(entry))
return
sys.stdout.write("%s\n" % json.dumps(value))
return
def parse_arguments(argv):
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("-u", "--user", default=None, help="Username for graylog login")
parser.add_argument("-s", "--password", default=None, help="Password for graylog login")
parser.add_argument(
"-P",
"--proto",
default="https",
help="Use 'http' or 'https' for connection to graylog (default=https)",
)
parser.add_argument(
"-p", "--port", default=443, type=int, help="Use alternative port (default: 443)"
)
parser.add_argument(
"--debug", action="store_true", help="Debug mode: let Python exceptions come through"
)
parser.add_argument(
"--no-cert-check", action="store_true", help="Disable SSL certificate validation"
)
parser.add_argument(
"hostname", metavar="HOSTNAME", help="Name of the graylog instance to query."
)
return parser.parse_args(argv)
if __name__ == "__main__":
sys.exit(main())

View File

@ -0,0 +1,11 @@
#!/usr/bin/env python3
# Copyright (C) 2019 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.
import sys
from cmk.special_agents.agent_graylog_input_metrics import main
if __name__ == "__main__":
sys.exit(main())

View File

@ -0,0 +1,39 @@
#!/usr/bin/env python3
# Copyright (C) 2019 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.
#
# {
# 'proto': 'http',
# 'port': 9000,
# 'user': 'hell',
# 'password': 'yeah',
# }
from typing import Any, Mapping, Optional, Sequence, Union
def agent_graylog_input_metrics_arguments(
params: Mapping[str, Any], hostname: str, ipaddress: Optional[str]
) -> Sequence[Union[str, tuple[str, str, str]]]:
args = [
"-P",
params["protocol"],
"-u",
params["user"],
"-s",
passwordstore_get_cmdline("%s", params["password"]),
]
if "port" in params:
args += ["-p", params["port"]]
if "no-cert-check" in params:
args += ["--no-cert-check"]
args.append(params["instance"])
return args
special_agent_info["graylog_input_metrics"] = agent_graylog_input_metrics_arguments

View File

@ -0,0 +1,81 @@
#!/usr/bin/env python3
# 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.
# NOTE: Careful when replacing the *-import below with a more specific import. This can cause
# problems because it might remove variables from the check-context which are necessary for
# resolving legacy discovery results such as [("SUMMARY", "diskstat_default_levels")]. Furthermore,
# it might also remove variables needed for accessing discovery rulesets.
import json
#<<<graylog_input_metrics:sep(0)>>>
# {"641e88d05d447a677efde199": {"input_state": "FAILED", "input_name": "kafka_cef_test",
# "input_type": "CEF Kafka", "input_port": null, "im_m1_rate": 0.0, "im_m5_rate": 0.0,
# "im_m15_rate": 0.0, "rs_m1_rate": 0.0, "rs_m5_rate": 0.0, "rs_m15_rate": 0.0},
# "641e32885d447a677efd2dbf": {"input_state": "RUNNING", "input_name": "UDP-test",
# "input_type": "Syslog UDP", "input_port": 1514, "im_m1_rate": 1.0846244336700077,
# "im_m5_rate": 1.3700826278955827, "im_m15_rate": 1.254406787430692, "rs_m1_rate": 145.45579305762527,
# "rs_m5_rate": 180.6486220431909, "rs_m15_rate": 165.26666376319292},
# "641e32795d447a677efd2d9e": {"input_state": "RUNNING", "input_name": "testTCP", "input_type": "Syslog TCP",
# "input_port": 1515, "im_m1_rate": 1.057872514816615, "im_m5_rate": 1.364957693749168,
# "im_m15_rate": 1.2528742858546844, "rs_m1_rate": 140.4719944116262, "rs_m5_rate": 178.57816158901215,
# "rs_m15_rate": 163.80530659055356}}
def parse_graylog_input_metrics(section):
parsed = json.loads(section[0][0])
return parsed
def inventory_graylog_input_metrics(parsed):
for input_id, input_info in parsed.items():
input_name = input_info["input_name"]
yield Service(f"{input_name} ({input_id})")
def check_graylog_input_metrics(item, params, parsed):
# if parsed is None: return
item_id = item.split()[-1][1:-1]
input_info = parsed[item_id]
input_state = input_info["input_state"]
state = 1
if input_state == "RUNNING": state = 0
elif input_state == "FAILED": state = 2
yield state, "State: %s" % input_state
yield 0, "Type: %s" % input_info["input_type"]
if input_info["input_port"]:
yield 0, "Port: %s" % input_info["input_port"]
for key, dsname, unit, unit_func, infotext in [
("im_m1_rate", "im_m1_rate", "/1min", float, "Incoming messages"),
("im_m5_rate", "im_m5_rate", "/5min", float, ""),
("im_m15_rate", "im_m15_rate", "/15min", float, ""),
("rs_m1_rate", "rs_m1_rate", "/1min", get_bytes_human_readable, "Incoming data"),
("rs_m5_rate", "rs_m5_rate", "/5min", get_bytes_human_readable, ""),
("rs_m15_rate", "rs_m15_rate", "/15min", get_bytes_human_readable, ""),
]:
value = parsed[item_id][key]
value = round(value, 2)
rate_upper = params.get(key, {}).get("%s_upper" % key, (None, None))
rate_lower = params.get(key, {}).get("%s_lower" % key, (None, None))
yield check_levels(
value,
dsname,
rate_upper + rate_lower,
human_readable_func=unit_func,
unit=unit,
infoname=infotext
)
check_info["graylog_input_metrics"] = {
"parse_function": parse_graylog_input_metrics,
"check_function": check_graylog_input_metrics,
"inventory_function": inventory_graylog_input_metrics,
"service_description": "Graylog Input %s",
"has_perfdata": True,
"group": "graylog_input_metrics",
}

View File

@ -0,0 +1,97 @@
#!/usr/bin/env python3
# Copyright (C) 2019 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.i18n import _
from cmk.gui.plugins.wato.special_agents.common import RulespecGroupDatasourceProgramsApps
from cmk.gui.plugins.wato.utils import HostRulespec, IndividualOrStoredPassword, rulespec_registry
from cmk.gui.valuespec import Age, Dictionary, DropdownChoice, Integer, ListChoice, TextInput
from cmk.gui.watolib.rulespecs import Rulespec
def _factory_default_special_agents_graylog_input_metrics():
# No default, do not use setting if no rule matches
return Rulespec.FACTORY_DEFAULT_UNUSED
def _valuespec_special_agents_graylog_input_metrics():
return Dictionary(
title=_("Graylog Input Metrics"),
help=_("Requests input metrics data from a Graylog instance."),
optional_keys=["port", "no-cert-check"],
elements=[
(
"instance",
TextInput(
title=_("Graylog instance to query"),
help=_(
"Use this option to set which instance should be "
"checked by the special agent. Please add the "
"hostname here, eg. my_graylog.com."
),
size=32,
allow_empty=False,
),
),
(
"user",
TextInput(
title=_("Username"),
help=_(
"The username that should be used for accessing the "
"Graylog API. Has to have read permissions at least."
),
size=32,
allow_empty=False,
),
),
(
"password",
IndividualOrStoredPassword(
title=_("Password of the user"),
allow_empty=False,
),
),
(
"protocol",
DropdownChoice(
title=_("Protocol"),
choices=[
("http", "HTTP"),
("https", "HTTPS"),
],
default_value="https",
),
),
(
"port",
Integer(
title=_("Port"),
help=_(
"Use this option to query a port which is different from standard port 443."
),
default_value=443,
),
),
(
"no-cert-check",
FixedValue(
True,
title=_("Disable SSL certificate validation"),
totext=_("SSL certificate validation is disabled"),
),
),
],
)
rulespec_registry.register(
HostRulespec(
factory_default=_factory_default_special_agents_graylog_input_metrics(),
group=RulespecGroupDatasourceProgramsApps,
name="special_agents:graylog_input_metrics",
valuespec=_valuespec_special_agents_graylog_input_metrics,
)
)

Binary file not shown.

View File

@ -0,0 +1,22 @@
{'author': 'George Pochiscan',
'description': 'This package extends current informix package with the '
'following checks:\n'
'\n'
'Alert if the number of locks kept by a session is higher than '
'a specific number (configured by WATO rule).\n'
'Inactive transactions with locks \n'
'Long transactions\n',
'download_url': '',
'files': {'agents': ['plugins/mk_informix'],
'checkman': ['informix_transactions_activity',
'informix_transactions_locks',
'informix_transactions_long_transactions'],
'checks': ['informix_transactions'],
'web': ['plugins/wato/informix_nlocks.py']},
'name': 'Informix_transactions',
'num_files': 6,
'title': 'Informix monitoring',
'version': '1.0.1',
'version.min_required': '2.0.0p17',
'version.packaged': '2.0.0p17',
'version.usable_until': None}

View File

@ -0,0 +1,16 @@
title: Informix DB: Sessions with no activity in current log
agents: linux
catalog: app/informix
license: GPL
distribution: check_mk
description:
This checks monitors the transactions that doesn't
have activity in current log in Informix Database application.
No rules are needed.
item:
Name of the instance
inventory:
One service is created for each instance.

View File

@ -0,0 +1,16 @@
title: Informix DB: Sessions with high number of locks
agents: linux
catalog: app/informix
license: GPL
distribution: check_mk
description:
This checks monitors the sessions that have
high number of locks in Informix Database application.
WARN and CRIT levels can be specified.
item:
Name of the instance
inventory:
One service is created for each instance.

View File

@ -0,0 +1,16 @@
title: Informix DB: Long transactions that are using more than 3 logical logs
agents: linux
catalog: app/informix
license: GPL
distribution: check_mk
description:
This checks monitors the transactions that are using
more than 3 logical logs in Informix Database application.
No rules are needed.
item:
Name of the instance
inventory:
One service is created for each instance.

View File

@ -0,0 +1,143 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Copyright (C) 2019 tribe29 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.
factory_settings['informix_transactions_default_levels'] = {
'levels': (70, 80),
}
#first line represents current log ID
#line[2]= sessionID
#line[3]= number of locks kept by sessionID
#line[4]= begin log position
#line[5]= current log position
def parse_informix_transactions(info):
parsed = {}
instance = None
entry = None
for line in info:
if line[0].startswith("[[[") and line[0].endswith("]]]"):
instance = line[0][3:-3]
elif instance is not None:
entry = {}
parsed.setdefault(instance, [])
parsed[instance].append(entry)
if len(line) > 2:
if "C" in line[2]:
entry.setdefault("current_log_id", line[3])
elif entry is not None:
if ":" in str(line[4]):
line[4] = line[4].split(":",1)[0]
if ":" in str(line[5]):
line[5] = line[5].split(":",1)[0]
entry.setdefault("session", [line[2], line[3], line[4], line[5]])
return parsed
#############Transaction with more than 70 locks
def inventory_informix_transactions_locks(parsed):
return [(instance, {}) for instance in parsed]
def check_informix_transactions_locks(item, params, parsed):
if item in parsed:
warn, crit = params['levels']
data = parsed[item]
infotext = ""
state = 0
for session in data[1:]:
if session and session["session"][2]:
if "-" not in session["session"][1]:
if int(session["session"][1]) >= crit:
state = 2
infotext += 'Session with ID %s has %s locks; ' % (session["session"][0], session["session"][1])
elif int(session["session"][1]) >= warn:
state = 1
infotext += 'Session with ID %s has %s locks; ' % (session["session"][0], session["session"][1])
if state:
infotext += " (warn/crit at %s/%s)" % (warn, crit)
else:
infotext = 'There are no sessions with high number of locks'
return state, infotext, []
check_info['informix_transactions.locks'] = {
'parse_function': parse_informix_transactions,
'inventory_function': inventory_informix_transactions_locks,
'check_function': check_informix_transactions_locks,
'has_perfdata': False,
'service_description': 'Informix sessions nLocks %s',
"group": "informix_nlocks",
'default_levels_variable': 'informix_transactions_default_levels',
}
#############Sesiuni fara activitate in logul curent
def inventory_informix_transactions_activity(parsed):
return [(instance, {}) for instance in parsed]
def check_informix_transactions_activity(item, no_params, parsed):
if item in parsed:
data = parsed[item]
state = 0
infotext = ''
for session in data[1:]:
if session and session["session"][2]:
if "-" not in session["session"][2]:
if int(session["session"][2]) < int(data[0]['current_log_id']):
state = 2
infotext += "Session %s doesn't have activity in current log; " % (session["session"][0])
if not state:
infotext = 'There are no sessions with no activity in current log; '
return state, infotext, []
check_info['informix_transactions.activity'] = {
'parse_function': parse_informix_transactions,
'inventory_function': inventory_informix_transactions_activity,
'check_function': check_informix_transactions_activity,
'has_perfdata': False,
'service_description': 'Informix sessions activity %s',
}
#############Long transactions (over 3 logical logs used)
def inventory_informix_long_transactions(parsed):
return [(instance, {}) for instance in parsed]
def check_informix_long_transactions(item, no_params, parsed):
if item in parsed:
data = parsed[item]
state = 0
infotext = ''
for session in data[1:]:
if session and session["session"][2]:
if "-" not in session["session"][2]:
if (int(session["session"][2]) - int(session["session"][3])) < 2:
state = 2
infotext += "Session %s is using more than 3 logical logs; " % (session["session"][0])
if not state:
infotext = 'There are no long running transactions; '
return state, infotext, []
check_info['informix_transactions.long_transactions'] = {
'parse_function': parse_informix_transactions,
'inventory_function': inventory_informix_long_transactions,
'check_function': check_informix_long_transactions,
'has_perfdata': False,
'service_description': 'Informix sessions Long transactions %s',
}

View File

@ -0,0 +1,296 @@
#!/bin/bash
# Copyright (C) 2019 tribe29 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.
# Reason for this no-op: shellcheck disable=... before the first command disables the error for the
# entire script.
:
# Disable unused variable error (needed to keep track of version)
# shellcheck disable=SC2034
CMK_VERSION="2.0.0p17"
# Informix
# Make ENV-VARs avail for subshells
set -a
# .--helper--------------------------------------------------------------.
# | _ _ |
# | | |__ ___| |_ __ ___ _ __ |
# | | '_ \ / _ \ | '_ \ / _ \ '__| |
# | | | | | __/ | |_) | __/ | |
# | |_| |_|\___|_| .__/ \___|_| |
# | |_| |
# '----------------------------------------------------------------------'
function do_check () {
# $1:section, $2:excludelist
if echo "$2" | grep -qe "${1}"; then
return 1
else
return 0
fi
}
function sql () {
db="sysmaster"
sqltxt="$1"
export DBDELIMITER="|"
echo "$sqltxt" | dbaccess ${db}
}
function set_excludes () {
excludes=""
if [ "$EXCLUDES" = "ALL" ]; then
excludes="$all_sections"
global_exclude=true
elif [ ! -z "$EXCLUDES" ]; then
excludes=$EXCLUDES
global_exclude=true
else
global_exclude=false
fi
if [ "$global_exclude" = "false" ]; then
excludes_i="EXCLUDES_${1}"
if [ "${!excludes_i}" = "ALL" ]; then
excludes="$all_sections"
elif [ ! -z "${!excludes_i}" ]; then
excludes=${!excludes_i}
fi
fi
}
#.
# .--sqls----------------------------------------------------------------.
# | _ |
# | ___ __ _| |___ |
# | / __|/ _` | / __| |
# | \__ \ (_| | \__ \ |
# | |___/\__, |_|___/ |
# | |_| |
# '----------------------------------------------------------------------'
all_sections="sessions locks tabextents dbspaces logusage"
function informix_status(){
echo "<<<informix_status:sep(58)>>>"
echo "[[[$INFORMIXSERVER/$SERVERNUM]]]"
$INFORMIXDIR/bin/onstat - >/dev/null 2>&1
state=$?
echo "Status:"$state
$INFORMIXDIR/bin/onstat -g dis
port=$(grep $INFORMIXSERVER /etc/services)
echo "PORT:"$port
}
function informix_sessions(){
echo "<<<informix_sessions>>>"
echo "[[[$INFORMIXSERVER/$SERVERNUM]]]"
# don't count our own session
sql "select 'SESSIONS', (count(*)-1)::int from syssessions"
}
function informix_locks(){
echo "<<<informix_locks>>>"
echo "[[[$INFORMIXSERVER/$SERVERNUM]]]"
# don't count our own session
sql "select 'LOCKS', (count(*)-1)::int, type from syslocks group by type"
}
function informix_tabextents(){
echo "<<<informix_tabextents>>>"
echo "[[[$INFORMIXSERVER/$SERVERNUM]]]"
sql "select first 10
'TABEXTENTS',
trim(n.dbsname) db,
trim(n.tabname) tab,
h.nextns extents,
nrows
from sysptnhdr h, systabnames n
where h.partnum = n.partnum
and nrows > 0
and n.dbsname not in ( 'sysadmin', 'sysuser', 'sysutils', 'sysmaster' )
and n.tabname not like 'sys%'
order by extents desc"
}
function informix_dbspaces(){
echo "<<<informix_dbspaces>>>"
echo "[[[$INFORMIXSERVER/$SERVERNUM]]]"
sql "select
trim(sd.name) || ' DBSPACE',
sd.dbsnum,
sd.is_temp,
sd.flags,
'CHUNK',
sc.fname,
sc.pagesize,
sc.chksize,
sc.nfree,
sc.flags,
trim(sc.mfname),
sc.mflags
from sysdbspaces sd, syschunks sc
where sd.dbsnum = sc.dbsnum
-- NO SBSPACE CURRENTLY
and sd.is_sbspace = 0
order by sd.name"
}
function informix_logusage(){
echo "<<<informix_logusage>>>"
echo "[[[$INFORMIXSERVER/$SERVERNUM]]]"
sql "select 'LOGUSAGE',
number,
sh_pagesize,
size,
used,
flags,
'is_used:'||is_used,
'is_current:'||is_current,
'is_backed_up:'||is_backed_up,
'is_new:'||is_new,
'is_archived:'||is_archived,
'is_temp:'||is_temp,
'is_pre_dropped:'||is_pre_dropped
from syslogs, sysshmvals
order by number"
}
function informix_transactions(){
echo "<<<informix_transactions>>>"
echo "[[[$INFORMIXSERVER/$SERVERNUM]]]"
$INFORMIXDIR/bin/onstat -l | grep C | tail -n 1
$INFORMIXDIR/bin/onstat -x |egrep -v "IBM|maximum|Transactions|est.|userthread" | sed 's/:[^:]*$//'
}
#.
# .--config--------------------------------------------------------------.
# | __ _ |
# | ___ ___ _ __ / _(_) __ _ |
# | / __/ _ \| '_ \| |_| |/ _` | |
# | | (_| (_) | | | | _| | (_| | |
# | \___\___/|_| |_|_| |_|\__, | |
# | |___/ |
# '----------------------------------------------------------------------'
# Config opts:
# - oninit-path; Default is empty, which means autodetection:
# ONINIT_PATH=<path to oninit-binary>
# - Excluding sections ("status sessions locks tabextents dbspaces logusage"):
# EXCLUDES_INFORMIX_INSTANCE="SECTION SECTION ..."
# EXCLUDES_INFORMIX_INSTANCE=ALL
# EXCLUDES="SECTION SECTION ..."
# EXCLUDES=ALL
if [ -f "$MK_CONFDIR/informix.cfg" ]; then
. $MK_CONFDIR/informix.cfg
fi
if [ -z "$ONINIT_PATH" -o ! -x "$ONINIT_PATH" ]; then
ONINIT=$(UNIX95=true ps ax | grep oninit | grep -v grep | head -1 | awk '{print $1 " " $5}')
if [ -z "$ONINIT" ]; then
exit 0
fi
ONINIT_PATH=${ONINIT#* }
ONINIT_PID=${ONINIT% *}
case "$ONINIT_PATH" in
/*)
;;
*) # BUG not platform independent!
ONINIT_PATH=$(ls -l /proc/$ONINIT_PID/exe 2>/dev/null| sed 's/.* //')
;;
esac
# If not set in config or not found we end up here
if [ -z "$ONINIT_PATH" -o ! -f "$ONINIT_PATH" ]; then
exit 1
fi
fi
#.
# .--main----------------------------------------------------------------.
# | _ |
# | _ __ ___ __ _(_)_ __ |
# | | '_ ` _ \ / _` | | '_ \ |
# | | | | | | | (_| | | | | | |
# | |_| |_| |_|\__,_|_|_| |_| |
# | |
# '----------------------------------------------------------------------'
for IDSENV in $( export INFORMIXDIR=${ONINIT_PATH%/bin*}
$INFORMIXDIR/bin/onstat -g dis | \
egrep '^Server[ ]*:|^Server Number[ ]*:|^INFORMIX|^SQLHOSTS|^ONCONFIG' | \
sed -e 's/Server Number/SERVERNUM/' \
-e 's/Server/INFORMIXSERVER/' \
-e 's/SQLHOSTS/INFORMIXSQLHOSTS/' \
-e 's/[ ]*:[ ]*/=/' | \
tr '\n' ';' | \
sed -e 's/;$/\\n/' -e 's/;\(INFORMIXSERVER=[^;]*;\)/\\n\1/g' | \
awk '{ gsub(/\\n/,"\n")}1'
) ; do
(
# Set environment
eval $IDSENV
PATH=$INFORMIXDIR/bin:$PATH
# try to set them via 'onstat -g env' otherwise
# DB HAS TO BE RUNNING
if [ -z "$INFORMIXSQLHOSTS" -o -z "$ONCONFIG" ]; then
onstat -g env | egrep -e '^INFORMIXSQLHOSTS' \
-e '^ONCONFIG' | \
sed -e 's/[ ][ ]*/=/'
fi
informix_status
set_excludes $INFORMIXSERVER
if do_check "sessions" "$excludes"; then
informix_sessions
fi
if do_check "locks" "$excludes"; then
informix_locks
fi
if do_check "tabextents" "$excludes"; then
informix_tabextents
fi
if do_check "dbspaces" "$excludes"; then
informix_dbspaces
fi
if do_check "logusage" "$excludes"; then
informix_logusage
fi
if do_check "transactions" "$excludes"; then
informix_transactions
fi
)
done

View File

@ -0,0 +1,44 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Copyright (C) 2019 tribe29 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.i18n import _
from cmk.gui.valuespec import (
Dictionary,
Integer,
Tuple,
)
from cmk.gui.plugins.wato import (
CheckParameterRulespecWithoutItem,
rulespec_registry,
RulespecGroupCheckParametersApplications,
)
def _parameter_valuespec_informix_nlocks():
return Dictionary(elements=[
("levels",
Tuple(
title=_("Levels for number of locks for a session"),
help=
_("You can set a limit to the number of locks for a session in Informix Database application"
),
elements=[
Integer(title=_("Warning at"), default_value=40),
Integer(title=_("Critical at"), default_value=70),
],
)),
])
rulespec_registry.register(
CheckParameterRulespecWithoutItem(
check_group_name="informix_nlocks",
group=RulespecGroupCheckParametersApplications,
match_type="dict",
parameter_valuespec=_parameter_valuespec_informix_nlocks,
title=lambda: _("Informix number of Locks"),
))

View File

@ -0,0 +1,9 @@
This plugin monitors Scality Ring Supervisor through SNMP.
New checks added:
Ring Disks: Monitors the number of disks per ring. Parameters can be set for warn/crit if the number of disks reported is below a value.
Ring Stage: Monitors the Ring Status. If the ring is not in Run State, service will be CRIT.
Ring Storage: Storage used per Ring. Parameters can be set and the general filesystem parameters are used.
Supervisor Status: Monitors the supervisor status and server status. If the supervisor is not available or any of the servers are not OK, the service goes CRIT.

Binary file not shown.

View File

@ -0,0 +1,167 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Copyright (C) 2019 tribe29 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.
# NOTE: Careful when replacing the *-import below with a more specific import. This can cause
# problems because it might remove variables from the check-context which are necessary for
# resolving legacy discovery results such as [("SUMMARY", "diskstat_default_levels")]. Furthermore,
# it might also remove variables needed for accessing discovery rulesets.
#from cmk.base.check_legacy_includes.cisco_sensor_item import * # pylint: disable=wildcard-import,unused-wildcard-import
def inventory_scality_disk(info):
for line in info[0]:
yield (line[0],{})
def check_scality_disk(item, params, info):
if params.get("disks"):
warn, crit = params["disks"]
for line in info[0]:
if item in line:
ringdisktotal = int(line[3])
perfdata = [("Disks", ringdisktotal)]
infotext = "Number of disks: %s. " %ringdisktotal
if ringdisktotal <= warn:
if ringdisktotal <= crit:
infotext += "This is lower or equal with %s, critical level" %crit
yield 2, infotext, perfdata
else:
infotext += "This is lower or equal with %s, warning level" %warn
yield 1, infotext, perfdata
else:
yield 0, infotext, perfdata
else:
for line in info[0]:
if item in line:
ringdisktotal = line[3]
perfdata = [("Disks", ringdisktotal)]
infotext = "Number of disks: %s" %ringdisktotal
yield 0, infotext, perfdata
check_info["scality_ring.disk"] = {
"check_function": check_scality_disk,
"inventory_function": inventory_scality_disk,
"service_description": "Ring %s Disks",
"has_perfdata": True,
"group": "scality_disks"
}
##############RING STATUS############
def inventory_scality_supervisor(info):
for line in info[1]:
yield (line[0],{})
def check_scality_supervisor(item, no_params, info):
for line in info[1]:
if item == str(line[0]):
infotext = ""
status = 0
supNbSrvTotal = int(line[1])
supNbSrvOk = int(line[2])
supNbSrvNok = int(line[3])
supAvailable = int(line[4])
if supAvailable != 1 :
status = 2
infotext += "Supervisor is not available"
yield status, infotext
else:
infotext = "Supervisor is available "
yield status, infotext
if supNbSrvNok > 0 :
status = 2
infotext = "There are %s unavailable servers" %supNbSrvNok
yield status, infotext
else:
infotext = "All servers are available"
yield status, infotext
check_info["scality_ring.supervisor"] = {
"check_function": check_scality_supervisor,
"inventory_function": inventory_scality_supervisor,
"service_description": "Supervisor %s",
}
#############Storage#############
from cmk.base.check_legacy_includes.df import *
from cmk.base.check_legacy_includes.size_trend import *
#factory_settings["filesystem_default_levels"] = FILESYSTEM_DEFAULT_LEVELS
def inventory_scality_storage(info):
for line in info[0]:
yield (line[0],{})
def check_scality_storage(item, params, info):
for line in info[0]:
if item in line:
ringStorageAvailable = float(line[5])
ringStorageTotal = float(line[6])
fslist=[(item, ringStorageTotal, ringStorageAvailable, 0)]
return df_check_filesystem_list(item, params, fslist)
check_info["scality_ring.storage"] = {
"check_function": check_scality_storage,
"inventory_function": inventory_scality_storage,
"service_description": "Ring %s Storage",
"default_levels_variable": "filesystem_default_levels",
"has_perfdata": True,
"group": "filesystem",
}
##############RING STATUS############
def inventory_scality_ring(info):
for line in info[0]:
yield (line[0],{})
def check_scality_ring(item, no_params, info):
for line in info[0]:
if item in line:
ringstaterun = line[1]
ringstate = line[2]
if ringstate == "RUN":
status = 0
text = "Ring is in Run State"
elif ringstate =="LOOP":
status = 2
text = "Ring is LOOP State"
else:
status = 2
text = "Ring is in Balancing State"
yield status, text
check_info["scality_ring"] = {
"check_function": check_scality_ring,
"inventory_function": inventory_scality_ring,
"service_description": "Ring %s State",
"snmp_info": [
(".1.3.6.1.4.1.37489.2.1.1.1.4.1.1",
[
"2", #ringName
"3", #ringStateRun
"8", #ringState
"9", #ringDiskTotal
"13", #ringStorageUsed
"14", #ringStorageAvailable
"15", #ringStorageTotal
]),
(".1.3.6.1.4.1.37489.2.1.1.1.4.2.1",
[
"2", #supName
"5", #supNbSrvTotal
"6", #supNbSrvOK
"7", #supNbSrvNok
"8", #supAvailable
],
),
],
"snmp_scan_function": lambda oid: "scality" in oid(".1.3.6.1.2.1.1.1.0").lower(),
}

View File

@ -0,0 +1,52 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Copyright (C) 2019 tribe29 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.i18n import _
from cmk.gui.plugins.wato.utils import (
CheckParameterRulespecWithItem,
rulespec_registry,
RulespecGroupCheckParametersApplications,
Transform,
)
from cmk.gui.valuespec import Dictionary, Integer, Tuple, TextInput
def _item_spec_scality_disks():
return TextInput(
title=_("Ring"), help=_("Ring Name")
)
def _parameter_valuespec_scality_disks():
return Dictionary(
elements=[
(
"disks",
Tuple(
help=_(
"This rule sets lower limits for the number of disks in the "
"scality ring system and it's applied for each RING"
),
title=_("Minimum number of disks:"),
elements=[
Integer(title=_("Warning below")),
Integer(title=_("Critical below")),
],
),
),
],
)
rulespec_registry.register(
CheckParameterRulespecWithItem(
check_group_name="scality_disks",
group=RulespecGroupCheckParametersApplications,
item_spec=_item_spec_scality_disks,
match_type="dict",
parameter_valuespec=_parameter_valuespec_scality_disks,
title=lambda: _("Scality Ring Disk Number"),
)
)

2365
check_mk-sentry-pdu/Sentry3.mib Executable file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,17 @@
title: Sentry PDU Devices: Outlet Power
agents: snmp
catalog: hw/power/servertech
license: Proprietary
distribution: Spearhead Systems SRL
description:
This check monitors the power of outlets in a Sentry PDU Device.
Without configuration the status will always be OK, except when an infeed is
returning no data.
You are able to set warn/crit levels, such that the status may change to
WARN or CRIT dependent on the levels.
item:
The items are named "Outlet" with following socket ID and names.
inventory:
One service per socket.

View File

@ -0,0 +1,196 @@
#!/usr/bin/env python
#
# Copyright 2024 Spearhead Systems SRL
#
# Docs for this system:
# https://cdn10.servertech.com/assets/documents/documents/135/original/manual_CDU_Y-30932L.pdf
# https://cdn10.servertech.com/assets/documents/documents/793/original/Sentry3.mib
from itertools import chain
# A note about MAX_SOCKETS:
#
# According to the MIB, MAX_SOCKETS should be 64. However, since CheckMK 1.6
# doesn't appear to have something like 2.*'s SNMPTree functionality, we
# brute-force by explicitly querying every possible socket. Unfortunately,
# this means querying ~3K OIDs. In practice, the data received from the customer
# shows that they only have two or four sockets per infeed, so I've set the
# MAX_SOCKETS here to 16, which cuts the queried OIDs to 800. Using MAX_SOCKETS
# of 4 would still satisfy the customer's current needs, but leaves no leeway
# if the get more sockets in an infeed.
MAX_TOWERS = 4
MAX_INFEEDS = 4
MAX_SOCKETS = 16 # XXX
TOWER_ARR_SIZE = MAX_TOWERS
INFEED_ARR_SIZE = MAX_TOWERS * MAX_INFEEDS
SOCKET_ARR_SIZE = MAX_TOWERS * MAX_INFEEDS * MAX_SOCKETS
TOWER_NUM_OFFSET = 0
INFEED_NUM_OFFSET = TOWER_NUM_OFFSET + 1
INFEED_ID_OFFSET = INFEED_NUM_OFFSET + TOWER_ARR_SIZE
INFEED_NAME_OFFSET = INFEED_ID_OFFSET + INFEED_ARR_SIZE
INFEED_VOLT_OFFSET = INFEED_NAME_OFFSET + INFEED_ARR_SIZE
SOCKET_NUM_OFFSET = INFEED_VOLT_OFFSET + INFEED_ARR_SIZE
SOCKET_ID_OFFSET = SOCKET_NUM_OFFSET + INFEED_ARR_SIZE
SOCKET_NAME_OFFSET = SOCKET_ID_OFFSET + SOCKET_ARR_SIZE
SOCKET_LOAD_OFFSET = SOCKET_NAME_OFFSET + SOCKET_ARR_SIZE
def parse_sentry_pdu(info):
outlets_info = {}
results = info[0]
num_towers = int(results[TOWER_NUM_OFFSET])
for tower_id in range(num_towers):
num_infeeds = int(results[INFEED_NUM_OFFSET + tower_id])
for infeed_id in range(num_infeeds):
infeed_idx = tower_id * MAX_TOWERS + infeed_id
infeed_sid = results[INFEED_ID_OFFSET + infeed_idx]
infeed_name = results[INFEED_NAME_OFFSET + infeed_idx]
infeed_voltage = float(results[INFEED_VOLT_OFFSET + infeed_idx]) / 10
num_outlets = int(results[SOCKET_NUM_OFFSET + infeed_idx])
for outlet_id in range(num_outlets):
outlet_idx = MAX_INFEEDS * MAX_SOCKETS * tower_id + MAX_SOCKETS * infeed_id + outlet_id
outlet_sid = results[SOCKET_ID_OFFSET + outlet_idx]
outlet_name = results[SOCKET_NAME_OFFSET + outlet_idx]
outlet_load = float(results[SOCKET_LOAD_OFFSET + outlet_idx]) / 100
outlet_key = '%s.%s.%s' % (tower_id, infeed_id, outlet_id)
outlets_info[outlet_key] = {
'infeed_id': infeed_sid,
'infeed_name': infeed_name,
'infeed_voltage': infeed_voltage,
'outlet_id': outlet_sid,
'outlet_name': outlet_name,
'outlet_load': outlet_load,
}
return outlets_info
# Check function, returning warn/crit based upon SNMP parsed result above
def check_sentry_pdu(item, params, section):
outlet = section.get(params['id'])
if outlet is None:
return (3, 'item not found in snmp data')
voltage = outlet['infeed_voltage']
amps = outlet['outlet_load']
if voltage < 0:
return (1, 'Infeed voltage unavailable')
if params['type'] == 'infeed':
return check_metric(params, 'volts', voltage)
elif params['type'] == 'outlet':
if amps < 0:
return (1, 'Outlet load unavailable')
return check_metric(params, 'watts', voltage * amps)
def check_metric(params, metric_name, metric_value):
crit_metric_above = params.get('crit_%s_above' % metric_name)
warn_metric_above = params.get('warn_%s_above' % metric_name)
warn_metric_below = params.get('warn_%s_below' % metric_name)
crit_metric_below = params.get('crit_%s_below' % metric_name)
state = 0
if crit_metric_above and crit_metric_above < metric_value:
state = 2
elif crit_metric_below and crit_metric_below > metric_value:
state = 2
elif warn_metric_above and warn_metric_above < metric_value:
state = 1
elif warn_metric_below and warn_metric_below > metric_value:
state = 1
return (state, '%.1f %s' % (metric_value, metric_name))
# Inventory function, returning inventory based upon SNMP parsed result above
def inventory_sentry_pdu(parsed):
items = []
for id, outlet in parsed.items():
plug_name = '%s %s [infeed %s] power' % (
outlet['outlet_id'],
outlet['outlet_name'],
outlet['infeed_id']
)
infeed_name = 'Infeed %s %s' % (
outlet['infeed_id'],
outlet['infeed_name']
)
items.append((plug_name, { 'id': id, 'type': 'outlet' }))
items.append((infeed_name, { 'id': id, 'type': 'infeed' }))
return items
check_info['sentry_pdu_outlets_power'] = {
'parse_function': parse_sentry_pdu,
'check_function': check_sentry_pdu,
'inventory_function': inventory_sentry_pdu,
'service_description': 'Outlet %s',
'group': 'sentry_pdu_outlets_power',
'snmp_info': (
'.1.3.6.1.4.1.1718.3',
# In CheckMK 2.* there is SMPTree, but 1.6 doesn't seem to have that.
# Therefore we resort to this sledge-hammer approach to get the
# information we need. It's... not ideal.
list(chain(
# Number of towers
['1.4.0'],
# Number of infeeds
# .1.3.6.1.4.1.1718.3.2.1.1.5.<tower #>
['2.1.1.5.%s' % (x) for x in range(1, MAX_TOWERS+1)],
# Infeed IDs:
# .1.3.6.1.4.1.1718.3.2.2.1.2.<tower #>.<infeed #>
['2.2.1.2.%s.%s' % (x, y) for x in range(1, MAX_TOWERS+1)
for y in range(1, MAX_INFEEDS+1)],
# Infeed names:
# .1.3.6.1.4.1.1718.3.2.2.1.3.<tower #>.<infeed #>
['2.2.1.3.%s.%s' % (x, y) for x in range(1, MAX_TOWERS+1)
for y in range(1, MAX_INFEEDS+1)],
# Infeed voltage:
# .1.3.6.1.4.1.1718.3.2.2.1.11.<tower #>.<infeed #>
['2.2.1.11.%s.%s' % (x, y) for x in range(1, MAX_TOWERS+1)
for y in range(1, MAX_INFEEDS+1)],
# Number of outlets
# .1.3.6.1.4.1.1718.3.2.2.1.9.<tower #>.<infeed #>
['2.2.1.9.%s.%s' % (x, y) for x in range(1, MAX_TOWERS+1)
for y in range(1, MAX_INFEEDS+1)],
# Outlet IDs:
# .1.3.6.1.4.1.1718.3.2.3.1.2.<tower #>.<infeed #>.<outlet #>
['2.3.1.2.%s.%s.%s' % (x, y, z) for x in range(1, MAX_TOWERS+1)
for y in range(1, MAX_INFEEDS+1)
for z in range(1, MAX_SOCKETS+1)],
# Outlet names:
# .1.3.6.1.4.1.1718.3.2.3.1.3.<tower #>.<infeed #>.<outlet #>
['2.3.1.3.%s.%s.%s' % (x, y, z) for x in range(1, MAX_TOWERS+1)
for y in range(1, MAX_INFEEDS+1)
for z in range(1, MAX_SOCKETS+1)],
# Outlet load:
# .1.3.6.1.4.1.1718.3.2.3.1.7.<tower #>.<infeed #>.<outlet #>
['2.3.1.7.%s.%s.%s' % (x, y, z) for x in range(1, MAX_TOWERS+1)
for y in range(1, MAX_INFEEDS+1)
for z in range(1, MAX_SOCKETS+1)],
))),
'snmp_scan_function': lambda oid: 'Sentry Switched -48 VDC' in oid('.1.3.6.1.2.1.1.1.0')
}

View File

@ -0,0 +1,139 @@
#!/usr/bin/env python3
#
# Copyright 2024 Spearhead Systems SRL
from cmk.gui.i18n import _
from cmk.gui.plugins.wato import (
rulespec_registry,
CheckParameterRulespecWithItem,
RulespecGroupCheckParametersEnvironment,
)
from cmk.gui.valuespec import (
Dictionary,
Integer,
)
def _valuespec_outlets_power_check():
return Dictionary(
optional_keys=[
'crit_volts_above',
'warn_volts_above',
'warn_volts_below',
'crit_volts_below',
'crit_watts_above',
'warn_watts_above',
'warn_watts_below',
'crit_watts_below',
'type',
],
elements=[
(
'crit_volts_above',
Integer(
minvalue=0,
title=_('Crit when infeed above voltage'),
unit=_('Volts'),
help=_(
'If the Voltage of an infeed goes above this number, enter a critical state.'
),
),
),
(
'warn_volts_above',
Integer(
minvalue=0,
title=_('Warn when infeed above voltage'),
unit=_('Volts'),
help=_(
'If the Voltage of an infeed goes above this number, enter a warning state.'
),
),
),
(
'warn_volts_below',
Integer(
minvalue=0,
title=_('Warn when infeed below voltage'),
unit=_('Volts'),
help=_(
'If the Voltage of an infeed goes below this number, enter a warning state.'
),
),
),
(
'crit_volts_below',
Integer(
minvalue=0,
title=_('Crit when infeed below voltage'),
unit=_('Volts'),
help=_(
'If the Voltage of an infeed goes below this number, enter a critical state.'
),
),
),
(
'crit_watts_above',
Integer(
minvalue=0,
title=_('Crit when outlet above power'),
unit=_('Watts'),
help=_(
'If the Wattage of an outlet goes above this number, enter a critical state.'
),
),
),
(
'warn_watts_above',
Integer(
minvalue=0,
title=_('Warn when outlet above power'),
unit=_('Watts'),
help=_(
'If the Wattage of an outlet goes above this number, enter a warning state.'
),
),
),
(
'warn_watts_below',
Integer(
minvalue=0,
title=_('Warn when outlet below power'),
unit=_('Watts'),
help=_(
'If the Wattage of an outlet goes below this number, enter a warning state.'
),
),
),
(
'crit_watts_below',
Integer(
minvalue=0,
title=_('Crit when outlet below power'),
unit=_('Watts'),
help=_(
'If the Wattage of an outlet goes below this number, enter a critical state.'
),
),
),
],
)
def _item_spec_outlets_power_check():
return TextAscii(title=_('Socket Name'),
help=_('The name of the socket'))
rulespec_registry.register(
CheckParameterRulespecWithItem(
check_group_name='sentry_pdu_outlets_power',
group=RulespecGroupCheckParametersEnvironment,
match_type='dict',
item_spec=_item_spec_outlets_power_check,
parameter_valuespec=_valuespec_outlets_power_check,
title=lambda: _('Sentry PDU Outlets Power Checks'),
)
)

View File

@ -0,0 +1,184 @@
#!/usr/bin/env python3
#
# Copyright 2024 Spearhead Systems SRL
#
# Docs for this system:
# https://cdn10.servertech.com/assets/documents/documents/135/original/manual_CDU_Y-30932L.pdf
# https://cdn10.servertech.com/assets/documents/documents/793/original/Sentry3.mib
from cmk.base.plugins.agent_based.agent_based_api.v1 import (
register,
Service,
Result,
State,
SNMPTree,
contains,
OIDEnd,
)
def extract_outlet(tower_id, infeed_id, outlet_id, data):
return next(filter(lambda x: x[0] == f'{tower_id+1}.{infeed_id+1}.{outlet_id+1}', data))[1]
# SNMP parsing function
def parse_sentry_pdu(string_table):
outlets_info = {}
num_towers = int(string_table[0][0][0])
for tower_id in range(num_towers):
num_infeeds = int(string_table[1][0][tower_id])
for infeed_id in range(num_infeeds):
infeed_sid = string_table[2][infeed_id][tower_id]
infeed_name = string_table[3][infeed_id][tower_id]
infeed_voltage = float(string_table[4][infeed_id][tower_id]) / 10
num_outlets = int(string_table[5][infeed_id][tower_id])
for outlet_id in range(num_outlets):
outlet_sid = extract_outlet(tower_id, infeed_id, outlet_id, string_table[6])
outlet_name = extract_outlet(tower_id, infeed_id, outlet_id, string_table[7])
outlet_load = float(extract_outlet(tower_id, infeed_id, outlet_id, string_table[8])) / 100
outlets_info[f'{tower_id}.{infeed_id}.{outlet_id}'] = {
'infeed_id': infeed_sid,
'infeed_name': infeed_name,
'infeed_voltage': infeed_voltage,
'outlet_id': outlet_sid,
'outlet_name': outlet_name,
'outlet_load': outlet_load
}
return outlets_info
# Inventory function, returning inventory based upon SNMP parsed result above
def discovery_sentry_pdu(section):
for key, outlet in section.items():
plug_name = f'{outlet["outlet_id"]} {outlet["outlet_name"]} (infeed {outlet["infeed_id"]}) power'
infeed_name = f'Infeed {outlet["infeed_id"]} {outlet["infeed_name"]}'
yield Service(item=plug_name, parameters={ 'id': key, 'type': 'outlet' })
yield Service(item=infeed_name, parameters={ 'id': key, 'type': 'infeed' })
# Check function, returning warn/crit based upon SNMP parsed result above
def check_sentry_pdu(item, params, section):
outlet = section.get(params['id'])
if outlet is None:
return
voltage = outlet['infeed_voltage']
amps = outlet['outlet_load']
if voltage < 0:
yield Result(state=State.WARN, summary='Infeed voltage unavailable')
return
if params['type'] == 'infeed':
yield check_metric(params, 'volts', voltage)
elif params['type'] == 'outlet':
if amps < 0:
yield Result(state=State.WARN, summary='Outlet load unavailable')
return
yield check_metric(params, 'watts', voltage * amps)
def check_metric(params, metric_name, metric_value):
crit_metric_above = params.get('crit_%s_above' % metric_name)
warn_metric_above = params.get('warn_%s_above' % metric_name)
warn_metric_below = params.get('warn_%s_below' % metric_name)
crit_metric_below = params.get('crit_%s_below' % metric_name)
state = State.OK
if crit_metric_above and crit_metric_above < metric_value:
state = State.CRIT
elif crit_metric_below and crit_metric_below > metric_value:
state = State.CRIT
elif warn_metric_above and warn_metric_above < metric_value:
state = State.WARN
elif warn_metric_below and warn_metric_below > metric_value:
state = State.WARN
return Result(state=state, summary=f'{metric_value:.1f} {metric_name}')
register.snmp_section(
name='sentry_pdu_outlets_power',
parse_function=parse_sentry_pdu,
fetch=[
# Number of towers (integer from 0 to 4)
SNMPTree(
base='.1.3.6.1.4.1.1718.3.1.4',
oids=['0']
),
# Number of infeeds (integer from 0 to 4):
# .1.3.6.1.4.1.1718.3.2.1.1.5.<tower #>
SNMPTree(
base='.1.3.6.1.4.1.1718.3.2.1.1.5',
oids=['1', '2', '3', '4'],
),
# Infeed IDs:
# .1.3.6.1.4.1.1718.3.2.2.1.2.<tower #>.<infeed #>
SNMPTree(
base='.1.3.6.1.4.1.1718.3.2.2.1.2',
oids=['1', '2', '3', '4'],
),
# Infeed names:
# .1.3.6.1.4.1.1718.3.2.2.1.3.<tower #>.<infeed #>
SNMPTree(
base='.1.3.6.1.4.1.1718.3.2.2.1.3',
oids=['1', '2', '3', '4'],
),
# Infeed voltage:
# .1.3.6.1.4.1.1718.3.2.2.1.11.<tower #>.<infeed #>
SNMPTree(
base='.1.3.6.1.4.1.1718.3.2.2.1.11',
oids=['1', '2', '3', '4'],
),
# Number of outlets (integer from 0 to 64):
# .1.3.6.1.4.1.1718.3.2.2.1.9.<tower #>.<infeed #>
SNMPTree(
base='.1.3.6.1.4.1.1718.3.2.2.1.9',
oids=['1', '2', '3', '4'],
),
# Outlet IDs:
# .1.3.6.1.4.1.1718.3.2.3.1.2.<tower #>.<infeed #>.<outlet #>
SNMPTree(
base='.1.3.6.1.4.1.1718.3.2.3.1',
oids=[OIDEnd(), '2'],
),
# Outlet names:
# .1.3.6.1.4.1.1718.3.2.3.1.3.<tower #>.<infeed #>.<outlet #>
SNMPTree(
base='.1.3.6.1.4.1.1718.3.2.3.1',
oids=[OIDEnd(), '3'],
),
# Outlet load:
# .1.3.6.1.4.1.1718.3.2.3.1.7.<tower #>.<infeed #>.<outlet #>
SNMPTree(
base='.1.3.6.1.4.1.1718.3.2.3.1',
oids=[OIDEnd(), '7']
),
],
detect=contains('.1.3.6.1.2.1.1.1.0', 'Sentry Switched -48 VDC'),
)
register.check_plugin(
name='sentry_pdu_outlets_power',
service_name='Outlet %s',
discovery_function=discovery_sentry_pdu,
check_function=check_sentry_pdu,
check_default_parameters={},
check_ruleset_name='sentry_pdu_outlets_power',
)

View File

@ -0,0 +1,131 @@
#!/usr/bin/env python3
#
# Copyright 2024 Spearhead Systems SRL
from cmk.gui.i18n import _
from cmk.gui.plugins.wato.utils import (
rulespec_registry,
CheckParameterRulespecWithItem,
RulespecGroupCheckParametersEnvironment,
)
from cmk.gui.valuespec import (
Dictionary,
Integer,
)
def _valuespec_agents_sentry_pdu_outlets_power_check():
return Dictionary(
title=_('Sentry PDU Outlets Power Checks'),
optional_keys=[
'crit_volts_above',
'warn_volts_above',
'warn_volts_below',
'crit_volts_below',
'crit_watts_above',
'warn_watts_above',
'warn_watts_below',
'crit_watts_below',
],
elements=[
(
'crit_volts_above',
Integer(
minvalue=0,
title=_('Crit when infeed above voltage'),
unit=_('Volts'),
help=_(
'If the Voltage of an infeed goes above this number, enter a critical state.'
),
),
),
(
'warn_volts_above',
Integer(
minvalue=0,
title=_('Warn when infeed above voltage'),
unit=_('Volts'),
help=_(
'If the Voltage of an infeed goes above this number, enter a warning state.'
),
),
),
(
'warn_volts_below',
Integer(
minvalue=0,
title=_('Warn when infeed below voltage'),
unit=_('Volts'),
help=_(
'If the Voltage of an infeed goes below this number, enter a warning state.'
),
),
),
(
'crit_volts_below',
Integer(
minvalue=0,
title=_('Crit when infeed below voltage'),
unit=_('Volts'),
help=_(
'If the Voltage of an infeed goes below this number, enter a critical state.'
),
),
),
(
'crit_watts_above',
Integer(
minvalue=0,
title=_('Crit when outlet above power'),
unit=_('Watts'),
help=_(
'If the Wattage of an outlet goes above this number, enter a critical state.'
),
),
),
(
'warn_watts_above',
Integer(
minvalue=0,
title=_('Warn when outlet above power'),
unit=_('Watts'),
help=_(
'If the Wattage of an outlet goes above this number, enter a warning state.'
),
),
),
(
'warn_watts_below',
Integer(
minvalue=0,
title=_('Warn when outlet below power'),
unit=_('Watts'),
help=_(
'If the Wattage of an outlet goes below this number, enter a warning state.'
),
),
),
(
'crit_watts_below',
Integer(
minvalue=0,
title=_('Crit when outlet below power'),
unit=_('Watts'),
help=_(
'If the Wattage of an outlet goes below this number, enter a critical state.'
),
),
),
],
)
rulespec_registry.register(
CheckParameterRulespecWithItem(
check_group_name='sentry_pdu_outlets_power',
group=RulespecGroupCheckParametersEnvironment,
match_type='dict',
parameter_valuespec=_valuespec_agents_sentry_pdu_outlets_power_check,
)
)

View File

@ -0,0 +1,17 @@
#!/usr/bin/env python
with open("/opt/omd/sites/<SITENAME>/var/log/liveproxyd.state") as f:
out = {}
for line in f:
if line.strip().startswith('['):
sitename = line
if line.strip().startswith('Channels'):
tmp = []
for line in f:
if line.strip().startswith("Clients"):
out[sitename] = tmp
break
tmp.append(line)
for k,v in out.items():
print("0 lp-{s} total={t};;|ready={r};;|busy={b};;|heartbeat={h};; {s} site livestatus channel states").format(s=k.strip()[1:-1],r=sum('ready' in s for s in v),b=sum('busy' in s for s in v),h=sum('heartbeat' in s for s in v),t=len(v))