#!/usr/bin/env python3 """ TSM Backup Status Agent Plugin for CheckMK - Reads CSV files from /mnt/CMK_TSM - Normalizes RRZ*/NFRZ* node names for redundancy - Aggregates backups per logical node - Outputs data as CheckMK agent section Installation: /usr/lib/check_mk_agent/plugins/tsm_backups Permissions: chmod +x tsm_backups Author: Marius Gielnik Version: 1.0 - Agent Plugin for CheckMK 2.3+ """ import csv import json from datetime import datetime from pathlib import Path from collections import defaultdict import re import sys CSV_DIR = Path("/mnt/CMK_TSM") class TSMParser: def __init__(self): self.backups = [] def normalize_node_name(self, node): """Normalisiert Node-Namen für Redundanz-Logik""" pattern = r'(RRZ|NFRZ|RZ)\d+(_)' normalized = re.sub(pattern, r'\2', node) pattern_end = r'(RRZ|NFRZ|RZ)\d+$' normalized = re.sub(pattern_end, '', normalized) return normalized def is_valid_node(self, node, status): """Prüft ob ein Node valide ist""" if not node or len(node) < 3 or not status: return False if "MAINTENANCE" in node: return False return True def parse_csv(self, csv_file): """Parst CSV-Datei""" try: with open(csv_file, 'r', encoding='utf-8') as f: reader = csv.reader(f) for row in reader: if not row or len(row) < 5: continue time_str = row[0].strip() node = row[2].strip() schedule = row[3].strip() status = row[4].strip() if not self.is_valid_node(node, status): continue normalized_node = self.normalize_node_name(node) self.backups.append({ "time": time_str, "node": normalized_node, "status": status, "schedule": schedule, }) except Exception: pass def aggregate(self): """Aggregiert Backups pro logischem Node""" nodes = defaultdict(lambda: { "statuses": [], "schedules": [], "last": None, "count": 0, }) for b in self.backups: node = b["node"] nodes[node]["count"] += 1 nodes[node]["statuses"].append(b["status"]) nodes[node]["schedules"].append(b["schedule"]) try: t = datetime.strptime(b["time"], "%Y-%m-%d %H:%M:%S") if not nodes[node]["last"] or t > nodes[node]["last"]: nodes[node]["last"] = t except Exception: pass # Convert datetime to timestamp for JSON serialization result = {} for node, data in nodes.items(): result[node] = { "statuses": data["statuses"], "schedules": data["schedules"], "last": int(data["last"].timestamp()) if data["last"] else None, "count": data["count"] } return result def main(): if not CSV_DIR.exists(): # Output empty section if directory doesn't exist print("<<>>") print(json.dumps({})) return csv_files = list(CSV_DIR.glob("*_SCHED_24H.CSV")) if not csv_files: csv_files = list(CSV_DIR.glob("*.CSV")) + list(CSV_DIR.glob("*.csv")) parser = TSMParser() for csv_file in csv_files: parser.parse_csv(csv_file) nodes = parser.aggregate() # Output CheckMK agent section print("<<>>") print(json.dumps(nodes)) if __name__ == "__main__": main()