Skip to content

Commit 87b51f1

Browse files
committed
updated rules
1 parent 7fbdb9c commit 87b51f1

File tree

2 files changed

+250
-0
lines changed

2 files changed

+250
-0
lines changed

CHANGES.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
88
## [Unreleased]
99

1010
### New
11+
- added more information and examples to cmk-plugin-guide.md
1112

1213
### Changed
1314

cmk-plugin-guide.md

Lines changed: 249 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -774,6 +774,10 @@ check_plugin_my_service_multi = CheckPlugin(
774774
```
775775

776776
#### Using check_levels Helper
777+
778+
The `check_levels` function is the recommended way to handle threshold checking in CheckMK 2.3+. It automatically handles SimpleLevels from rulesets, generates proper Result and Metric objects, and supports custom formatting.
779+
780+
##### Basic Usage
777781
```python
778782
from cmk.agent_based.v2 import check_levels
779783

@@ -790,6 +794,251 @@ def check_my_service_levels(item: str, params: Mapping[str, Any], section: Dict[
790794
)
791795
```
792796

797+
##### Complete check_levels Parameters
798+
799+
According to the CheckMK 2.3 documentation, `check_levels` supports these parameters:
800+
801+
```python
802+
check_levels(
803+
value, # float: The currently measured value
804+
*,
805+
levels_upper=None, # Upper level parameters from SimpleLevels
806+
levels_lower=None, # Lower level parameters from SimpleLevels
807+
metric_name=None, # str: Name for performance data metric
808+
render_func=None, # Callable: Function to format values
809+
label=None, # str: Label to prepend to output
810+
boundaries=None, # tuple: (min, max) for metric boundaries
811+
notice_only=False # bool: Only show in details if not OK
812+
)
813+
```
814+
815+
##### SimpleLevels Format Handling
816+
817+
CheckMK 2.3 rulesets generate SimpleLevels in dictionary format. The `check_levels` function expects levels in `("fixed", (warn, crit))` format. This applies to both `levels_upper` and `levels_lower`:
818+
819+
```python
820+
def check_with_simple_levels(item: str, params: Mapping[str, Any], section: Dict[str, Any]) -> CheckResult:
821+
value = section.get("storage_usage_percent", 0)
822+
823+
# Handle SimpleLevels format from rulesets - UPPER levels
824+
storage_levels = params.get('storage_levels')
825+
if storage_levels and isinstance(storage_levels, dict) and 'levels_upper' in storage_levels:
826+
levels_upper = ("fixed", storage_levels['levels_upper'])
827+
else:
828+
levels_upper = None
829+
830+
yield from check_levels(
831+
value,
832+
levels_upper=levels_upper,
833+
metric_name="storage_used_percent",
834+
label="Storage utilization",
835+
boundaries=(0.0, 100.0),
836+
render_func=render.percent,
837+
)
838+
839+
def check_with_lower_levels(item: str, params: Mapping[str, Any], section: Dict[str, Any]) -> CheckResult:
840+
temperature = section.get("temperature_celsius", 0)
841+
842+
# Handle SimpleLevels format for LOWER levels (same pattern)
843+
temp_upper_levels = params.get('temperature_levels_upper')
844+
temp_lower_levels = params.get('temperature_levels_lower')
845+
846+
# Convert both upper and lower levels using identical pattern
847+
levels_upper = None
848+
if temp_upper_levels and isinstance(temp_upper_levels, dict) and 'levels_upper' in temp_upper_levels:
849+
levels_upper = ("fixed", temp_upper_levels['levels_upper'])
850+
851+
levels_lower = None
852+
if temp_lower_levels and isinstance(temp_lower_levels, dict) and 'levels_lower' in temp_lower_levels:
853+
levels_lower = ("fixed", temp_lower_levels['levels_lower'])
854+
855+
yield from check_levels(
856+
temperature,
857+
levels_upper=levels_upper, # High temperature warnings
858+
levels_lower=levels_lower, # Low temperature warnings
859+
metric_name="temperature",
860+
label="Temperature",
861+
render_func=lambda v: f"{v:.1f}°C",
862+
)
863+
```
864+
865+
**Key Point**: The SimpleLevels conversion pattern is identical for both upper and lower levels:
866+
- Upper: `{"levels_upper": (warn, crit)}``("fixed", (warn, crit))`
867+
- Lower: `{"levels_lower": (warn, crit)}``("fixed", (warn, crit))`
868+
869+
##### Custom Render Functions
870+
871+
Use built-in render functions or create custom ones for proper value formatting:
872+
873+
```python
874+
from cmk.agent_based.v2 import render
875+
876+
# Built-in render functions
877+
def check_with_builtin_renders(item: str, params: Mapping[str, Any], section: Dict[str, Any]) -> CheckResult:
878+
# Percentage values
879+
yield from check_levels(
880+
section.get("cpu_usage", 0),
881+
levels_upper=("fixed", (80.0, 90.0)),
882+
metric_name="cpu_usage",
883+
render_func=render.percent,
884+
label="CPU usage"
885+
)
886+
887+
# Byte values
888+
yield from check_levels(
889+
section.get("memory_bytes", 0),
890+
levels_upper=("fixed", (1024*1024*1024, 2*1024*1024*1024)),
891+
metric_name="memory_usage",
892+
render_func=render.bytes,
893+
label="Memory usage"
894+
)
895+
896+
# Custom render functions
897+
def _render_operations_per_second(value: float) -> str:
898+
"""Custom render function for operations per second."""
899+
return f"{value:.1f}/s"
900+
901+
def _render_milliseconds(value: float) -> str:
902+
"""Custom render function for milliseconds."""
903+
return f"{value:.2f}ms"
904+
905+
def check_with_custom_renders(item: str, params: Mapping[str, Any], section: Dict[str, Any]) -> CheckResult:
906+
# I/O operations
907+
yield from check_levels(
908+
section.get("read_ops", 0),
909+
levels_upper=("fixed", (1000, 2000)),
910+
metric_name="read_ops",
911+
render_func=_render_operations_per_second,
912+
label="Read operations"
913+
)
914+
915+
# Wait times
916+
yield from check_levels(
917+
section.get("response_time", 0),
918+
levels_upper=("fixed", (50.0, 100.0)),
919+
metric_name="response_time",
920+
render_func=_render_milliseconds,
921+
label="Response time"
922+
)
923+
```
924+
925+
##### Default Parameters with SimpleLevels Format
926+
927+
When defining default parameters, use SimpleLevels dictionary format for CheckMK 2.3 compatibility:
928+
929+
```python
930+
check_plugin_my_service = CheckPlugin(
931+
name="my_service",
932+
service_name="My Service",
933+
sections=["my_service"],
934+
discovery_function=discover_my_service,
935+
check_function=check_my_service,
936+
check_ruleset_name="my_service",
937+
check_default_parameters={
938+
# SimpleLevels format for CheckMK 2.3 - Upper levels
939+
'storage_levels': {'levels_upper': (80.0, 90.0)},
940+
'cpu_levels': {'levels_upper': (80.0, 90.0)},
941+
942+
# SimpleLevels format for lower levels (same pattern)
943+
'temperature_levels_upper': {'levels_upper': (80.0, 90.0)}, # High temp
944+
'temperature_levels_lower': {'levels_lower': (10.0, 5.0)}, # Low temp
945+
946+
# Other parameters can remain None
947+
'memory_levels': None,
948+
'response_time_levels': None,
949+
},
950+
)
951+
```
952+
953+
##### Manual vs check_levels Comparison
954+
955+
**❌ Manual threshold checking (deprecated approach):**
956+
```python
957+
def check_manual_thresholds(item: str, params: Mapping[str, Any], section: Dict[str, Any]) -> CheckResult:
958+
value = section.get("metric_value", 0)
959+
960+
# Manual threshold checking - NOT recommended
961+
levels = params.get('levels')
962+
if levels and isinstance(levels, dict) and 'levels_upper' in levels:
963+
warn, crit = levels['levels_upper']
964+
if value >= crit:
965+
yield Result(state=State.CRIT, summary=f"Critical: {value} (>= {crit})")
966+
elif value >= warn:
967+
yield Result(state=State.WARN, summary=f"Warning: {value} (>= {warn})")
968+
else:
969+
yield Result(state=State.OK, summary=f"OK: {value}")
970+
971+
# Manual metric creation
972+
yield Metric("my_metric", value, levels=(warn, crit) if levels else None)
973+
```
974+
975+
**✅ Using check_levels (recommended approach):**
976+
```python
977+
def check_with_check_levels(item: str, params: Mapping[str, Any], section: Dict[str, Any]) -> CheckResult:
978+
value = section.get("metric_value", 0)
979+
980+
# Recommended: Use check_levels for everything
981+
levels = params.get('levels')
982+
if levels and isinstance(levels, dict) and 'levels_upper' in levels:
983+
levels_upper = ("fixed", levels['levels_upper'])
984+
else:
985+
levels_upper = None
986+
987+
yield from check_levels(
988+
value,
989+
levels_upper=levels_upper,
990+
metric_name="my_metric",
991+
label="My metric",
992+
render_func=lambda v: f"{v:.1f}", # Custom formatting
993+
)
994+
```
995+
996+
##### Benefits of check_levels
997+
998+
- **Automatic state determination**: Handles WARN/CRIT state logic
999+
- **Consistent formatting**: Proper threshold display in output
1000+
- **Metric generation**: Creates performance data automatically
1001+
- **SimpleLevels compatibility**: Works with CheckMK 2.3 rulesets
1002+
- **Error handling**: Robust handling of invalid parameters
1003+
- **Extensibility**: Supports predictive levels and other advanced features
1004+
1005+
##### Common Patterns
1006+
1007+
**Multiple metrics with different render functions:**
1008+
```python
1009+
def check_comprehensive_metrics(item: str, params: Mapping[str, Any], section: Dict[str, Any]) -> CheckResult:
1010+
# Storage percentage
1011+
if 'storage_levels' in params:
1012+
yield from check_levels(
1013+
section.get("storage_percent", 0),
1014+
levels_upper=("fixed", params['storage_levels'].get('levels_upper')) if params.get('storage_levels') else None,
1015+
metric_name="storage_percent",
1016+
render_func=render.percent,
1017+
label="Storage usage",
1018+
boundaries=(0.0, 100.0)
1019+
)
1020+
1021+
# Network throughput
1022+
if 'network_levels' in params:
1023+
yield from check_levels(
1024+
section.get("network_bytes", 0),
1025+
levels_upper=("fixed", params['network_levels'].get('levels_upper')) if params.get('network_levels') else None,
1026+
metric_name="network_throughput",
1027+
render_func=render.bytes,
1028+
label="Network throughput"
1029+
)
1030+
1031+
# Response time
1032+
if 'latency_levels' in params:
1033+
yield from check_levels(
1034+
section.get("latency_ms", 0),
1035+
levels_upper=("fixed", params['latency_levels'].get('levels_upper')) if params.get('latency_levels') else None,
1036+
metric_name="latency",
1037+
render_func=_render_milliseconds,
1038+
label="Response latency"
1039+
)
1040+
```
1041+
7931042
## Agent Bakery Integration
7941043

7951044
The Agent Bakery allows centralized configuration and deployment of agent plugins across multiple hosts using the `cmk.base.pyugins.bakery.bakery_api.v1` API.

0 commit comments

Comments
 (0)