#!/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, )