Initial commit

This commit is contained in:
2025-08-21 21:38:06 +02:00
commit c855ca2633
6 changed files with 883 additions and 0 deletions

135
agent_based/minecraft.py Normal file
View File

@@ -0,0 +1,135 @@
#!/usr/bin/env python3
import json
# omd su monitoring
#
# cd ~/local/lib/python3/cmk_addons/plugins/minecraft/agent_based
from cmk.agent_based.v2 import AgentSection, CheckPlugin, Service, Result, State, Metric, check_levels, IgnoreResults, get_value_store, check_levels
def parse_minecraft(string_table):
# Join all fragments into one JSON string
raw = "".join(string_table[0]).strip()
if not raw:
raise ValueError("Empty JSON section received")
try:
data = json.loads(raw)
except json.JSONDecodeError as e:
raise ValueError(f"Invalid JSON: {e}")
return data
def discover_minecraft(section):
for srv in section:
name = srv.get("server_name", "default")
yield Service(item=name) # Hauptservice
rcon = srv.get("rcon")
if isinstance(rcon, dict):
for world in rcon.get("worlds", []):
world_name = world.get("world")
if world_name:
yield Service(item=f"{name} - World: {world_name}")
##########################################################################################################################
def check_minecraft(item, section):
value_store = get_value_store()
if " - World: " in item:
server, world = item.split(" - World: ", 1)
# world specific service for tps / ticktime
else:
# server service
server = item
world = None
# look for server data and put it inside srv
srv = next((s for s in section if s.get("server_name") == server), None)
if not srv:
yield Result(state=State.UNKNOWN, summary=f"No data for server {server}")
return
# at this point we have data
status = srv.get("status", {})
rcon = srv.get("rcon", {}) # is null if we have no data
# check if something is in error (status or rcon)
status_error = status.get("error") if isinstance(status, dict) else None
rcon_error = rcon.get("error") if isinstance(rcon, dict) else None
if status_error or rcon_error:
error_text = status_error or rcon_error
fail_count = value_store.get("fail_count", 0) + 1
value_store["fail_count"] = fail_count
if fail_count >= 5:
yield Result(state=State.CRIT, summary=f"{error_text} (Failed {fail_count} times)")
return
else:
yield IgnoreResults()
return
else:
value_store["fail_count"] = 0
if world is None:
# Main server service
online = status.get("player_online", 0)
maxp = status.get("player_max", 0)
player_list = status.get("player_list", "-")
motd = status.get("motd", "-")
server_version = status.get("server_version", "-")
yield Result(state=State.OK, summary=f"{online}/{maxp} players online", details=f"MOTD: {motd}\nVersion: {server_version}\nPlayers: {online}/{maxp}\nPlayer: {player_list}")
yield Metric("player_online", online)
yield Metric("player_max", maxp)
ping = status.get("ping")
if ping is not None:
yield Metric("ping_ms", ping)
protocol = status.get("protocol_version")
if protocol is not None:
yield Metric("protocol_version", protocol)
else:
# World-specific service
if isinstance(rcon, dict):
for w in rcon.get("worlds", []):
if w.get("world") == world:
tps = w.get("tps")
tick = w.get("tick")
yield Result(state=State.OK, summary=f"TPS: {tps} - Tick: {tick} ms", details=f"TPS: {tps}\nTick Time: {tick} ms")
if tps is not None:
yield Metric("tps", tps)
if tick is not None:
yield Metric("tick_time", tick)
return
yield Result(state=State.UNKNOWN, summary=f"No data for world {world}")
agent_section_minecraft = AgentSection(
name = "minecraft",
parse_function = parse_minecraft,
)
check_plugin_minecraft = CheckPlugin(
name = "minecraft",
service_name = "Minecraft %s",
discovery_function = discover_minecraft,
check_function = check_minecraft,
)