Checkmk/TSM/tsm_backups.py

128 lines
3.7 KiB
Python
Raw Normal View History

2026-01-13 23:25:08 +01:00
#!/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("<<<tsm_backups:sep(0)>>>")
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("<<<tsm_backups:sep(0)>>>")
print(json.dumps(nodes))
if __name__ == "__main__":
main()