{
  "checks": [
    {
      "name": "Ping",
      "auto_add": true,
      "check_mode": "passive",
      "check_type": "ping",
      "parameters": null,
      "sort_order": 0,
      "value_type": "duration",
      "description": "ICMP-Erreichbarkeit und Antwortzeit — Erreichbarkeits-Anker des Hosts.",
      "check_config": {},
      "is_telemetry": false,
      "interpretation": null,
      "threshold_crit": null,
      "threshold_warn": null,
      "interval_seconds": 60,
      "threshold_direction": "above",
      "is_reachability_hint": true,
      "confirmation_attempts": 3,
      "confirmation_interval_seconds": 10
    },
    {
      "name": "Proxmox API erreichbar",
      "auto_add": true,
      "check_mode": "active",
      "check_type": "plugin_local",
      "parameters": null,
      "sort_order": 0,
      "value_type": "gauge",
      "description": "Liveness der REST-API (Token).",
      "check_config": {
        "argv": [
          "--target",
          "{host}",
          "--token",
          "{api_token}",
          "--check",
          "api"
        ],
        "timeout": 15,
        "script_id": "d5dbb51c-3989-50a7-a621-3573138ca4db"
      },
      "is_telemetry": false,
      "interpretation": null,
      "threshold_crit": null,
      "threshold_warn": null,
      "interval_seconds": 60,
      "threshold_direction": "upper",
      "confirmation_attempts": 1,
      "confirmation_interval_seconds": 10
    },
    {
      "name": "Cluster-Quorum",
      "auto_add": true,
      "check_mode": "active",
      "check_type": "plugin_local",
      "parameters": null,
      "sort_order": 1,
      "value_type": "gauge",
      "description": "1 = Quorum vorhanden (OK), 0 = verloren (CRITICAL); Einzelknoten = OK.",
      "check_config": {
        "argv": [
          "--target",
          "{host}",
          "--token",
          "{api_token}",
          "--check",
          "quorum"
        ],
        "timeout": 15,
        "script_id": "d5dbb51c-3989-50a7-a621-3573138ca4db"
      },
      "is_telemetry": false,
      "interpretation": null,
      "threshold_crit": null,
      "threshold_warn": null,
      "interval_seconds": 60,
      "threshold_direction": "upper",
      "confirmation_attempts": 1,
      "confirmation_interval_seconds": 10
    },
    {
      "name": "Nodes offline",
      "auto_add": true,
      "check_mode": "active",
      "check_type": "plugin_local",
      "parameters": null,
      "sort_order": 2,
      "value_type": "gauge",
      "description": "Cluster-Nodes nicht online (CRITICAL ab 1).",
      "check_config": {
        "argv": [
          "--target",
          "{host}",
          "--token",
          "{api_token}",
          "--check",
          "nodes_offline"
        ],
        "timeout": 15,
        "script_id": "d5dbb51c-3989-50a7-a621-3573138ca4db"
      },
      "is_telemetry": false,
      "interpretation": null,
      "threshold_crit": null,
      "threshold_warn": null,
      "interval_seconds": 60,
      "threshold_direction": "upper",
      "confirmation_attempts": 1,
      "confirmation_interval_seconds": 10
    },
    {
      "name": "CPU-Auslastung",
      "auto_add": true,
      "check_mode": "active",
      "check_type": "plugin_local",
      "parameters": null,
      "sort_order": 3,
      "value_type": "gauge",
      "description": "CPU des Node in Prozent.",
      "check_config": {
        "argv": [
          "--target",
          "{host}",
          "--token",
          "{api_token}",
          "--check",
          "cpu"
        ],
        "timeout": 15,
        "script_id": "d5dbb51c-3989-50a7-a621-3573138ca4db"
      },
      "is_telemetry": false,
      "interpretation": null,
      "threshold_crit": 95,
      "threshold_warn": 80,
      "interval_seconds": 60,
      "threshold_direction": "upper",
      "confirmation_attempts": 1,
      "confirmation_interval_seconds": 10
    },
    {
      "name": "RAM-Auslastung",
      "auto_add": true,
      "check_mode": "active",
      "check_type": "plugin_local",
      "parameters": null,
      "sort_order": 4,
      "value_type": "gauge",
      "description": "Belegter RAM des Node in Prozent.",
      "check_config": {
        "argv": [
          "--target",
          "{host}",
          "--token",
          "{api_token}",
          "--check",
          "ram"
        ],
        "timeout": 15,
        "script_id": "d5dbb51c-3989-50a7-a621-3573138ca4db"
      },
      "is_telemetry": false,
      "interpretation": null,
      "threshold_crit": 95,
      "threshold_warn": 80,
      "interval_seconds": 60,
      "threshold_direction": "upper",
      "confirmation_attempts": 1,
      "confirmation_interval_seconds": 10
    },
    {
      "name": "Root-Dateisystem-Auslastung",
      "auto_add": true,
      "check_mode": "active",
      "check_type": "plugin_local",
      "parameters": null,
      "sort_order": 5,
      "value_type": "gauge",
      "description": "Root-FS des Node in Prozent.",
      "check_config": {
        "argv": [
          "--target",
          "{host}",
          "--token",
          "{api_token}",
          "--check",
          "disk"
        ],
        "timeout": 15,
        "script_id": "d5dbb51c-3989-50a7-a621-3573138ca4db"
      },
      "is_telemetry": false,
      "interpretation": null,
      "threshold_crit": 95,
      "threshold_warn": 80,
      "interval_seconds": 120,
      "threshold_direction": "upper",
      "confirmation_attempts": 1,
      "confirmation_interval_seconds": 10
    },
    {
      "name": "Uptime (Tage)",
      "auto_add": true,
      "check_mode": "active",
      "check_type": "plugin_local",
      "parameters": null,
      "sort_order": 6,
      "value_type": "gauge",
      "description": "Betriebszeit des Node in Tagen.",
      "check_config": {
        "argv": [
          "--target",
          "{host}",
          "--token",
          "{api_token}",
          "--check",
          "uptime"
        ],
        "timeout": 15,
        "script_id": "d5dbb51c-3989-50a7-a621-3573138ca4db"
      },
      "is_telemetry": false,
      "interpretation": null,
      "threshold_crit": null,
      "threshold_warn": null,
      "interval_seconds": 120,
      "threshold_direction": "upper",
      "confirmation_attempts": 1,
      "confirmation_interval_seconds": 10
    },
    {
      "name": "VMs laufend",
      "auto_add": true,
      "check_mode": "active",
      "check_type": "plugin_local",
      "parameters": null,
      "sort_order": 7,
      "value_type": "gauge",
      "description": "Anzahl laufender QEMU-VMs.",
      "check_config": {
        "argv": [
          "--target",
          "{host}",
          "--token",
          "{api_token}",
          "--check",
          "vms_running"
        ],
        "timeout": 15,
        "script_id": "d5dbb51c-3989-50a7-a621-3573138ca4db"
      },
      "is_telemetry": false,
      "interpretation": null,
      "threshold_crit": null,
      "threshold_warn": null,
      "interval_seconds": 120,
      "threshold_direction": "upper",
      "confirmation_attempts": 1,
      "confirmation_interval_seconds": 10
    },
    {
      "name": "VMs gestoppt",
      "auto_add": true,
      "check_mode": "active",
      "check_type": "plugin_local",
      "parameters": null,
      "sort_order": 8,
      "value_type": "gauge",
      "description": "Gestoppte QEMU-VMs (ohne Templates).",
      "check_config": {
        "argv": [
          "--target",
          "{host}",
          "--token",
          "{api_token}",
          "--check",
          "vms_stopped"
        ],
        "timeout": 15,
        "script_id": "d5dbb51c-3989-50a7-a621-3573138ca4db"
      },
      "is_telemetry": false,
      "interpretation": null,
      "threshold_crit": null,
      "threshold_warn": null,
      "interval_seconds": 120,
      "threshold_direction": "upper",
      "confirmation_attempts": 1,
      "confirmation_interval_seconds": 10
    },
    {
      "name": "LXC laufend",
      "auto_add": true,
      "check_mode": "active",
      "check_type": "plugin_local",
      "parameters": null,
      "sort_order": 9,
      "value_type": "gauge",
      "description": "Anzahl laufender LXC-Container.",
      "check_config": {
        "argv": [
          "--target",
          "{host}",
          "--token",
          "{api_token}",
          "--check",
          "lxc_running"
        ],
        "timeout": 15,
        "script_id": "d5dbb51c-3989-50a7-a621-3573138ca4db"
      },
      "is_telemetry": false,
      "interpretation": null,
      "threshold_crit": null,
      "threshold_warn": null,
      "interval_seconds": 120,
      "threshold_direction": "upper",
      "confirmation_attempts": 1,
      "confirmation_interval_seconds": 10
    },
    {
      "name": "LXC gestoppt",
      "auto_add": true,
      "check_mode": "active",
      "check_type": "plugin_local",
      "parameters": null,
      "sort_order": 10,
      "value_type": "gauge",
      "description": "Gestoppte LXC-Container (ohne Templates).",
      "check_config": {
        "argv": [
          "--target",
          "{host}",
          "--token",
          "{api_token}",
          "--check",
          "lxc_stopped"
        ],
        "timeout": 15,
        "script_id": "d5dbb51c-3989-50a7-a621-3573138ca4db"
      },
      "is_telemetry": false,
      "interpretation": null,
      "threshold_crit": null,
      "threshold_warn": null,
      "interval_seconds": 120,
      "threshold_direction": "upper",
      "confirmation_attempts": 1,
      "confirmation_interval_seconds": 10
    },
    {
      "name": "Storages nicht verfügbar",
      "auto_add": true,
      "check_mode": "active",
      "check_type": "plugin_local",
      "parameters": null,
      "sort_order": 11,
      "value_type": "gauge",
      "description": "Storage-Pools unavailable (CRITICAL ab 1).",
      "check_config": {
        "argv": [
          "--target",
          "{host}",
          "--token",
          "{api_token}",
          "--check",
          "storages"
        ],
        "timeout": 15,
        "script_id": "d5dbb51c-3989-50a7-a621-3573138ca4db"
      },
      "is_telemetry": false,
      "interpretation": null,
      "threshold_crit": null,
      "threshold_warn": null,
      "interval_seconds": 120,
      "threshold_direction": "upper",
      "confirmation_attempts": 1,
      "confirmation_interval_seconds": 10
    }
  ],
  "profile": {
    "icon": "server",
    "name": "Proxmox VE (API)",
    "tags": [
      "proxmox",
      "virtualization",
      "api",
      "plugin"
    ],
    "vendor": "proxmox",
    "category": "virtualization",
    "os_family": "linux",
    "description": "Proxmox VE über die REST-API (Token, kein SSH) — als editierbare Plugins. Am Host IP/DNS eines Node als Ziel + API-Token. Für Multi-Node den Node im Script-argv --node pinnen.",
    "ip_required": true,
    "port_layout": null,
    "visual_type": null,
    "snmp_enabled": false,
    "agent_capable": false,
    "requires_snmp": false,
    "description_md": "## Proxmox VE (API) — Plugin-basiert\n\nEditierbare Python-Plugins (Scripts-Tab) prüfen Proxmox über die REST-API auf :8006 mit einem read-only API-Token (PVEAuditor auf `/`, an **Token UND User**). Kein SSH.\n\nBraucht Vesana ≥ v1.9.117.",
    "requires_agent": false,
    "sysoid_patterns": null,
    "sysdescr_patterns": null,
    "requires_collector": false
  },
  "scripts": [
    {
      "name": "check_proxmox",
      "script_id": "d5dbb51c-3989-50a7-a621-3573138ca4db",
      "is_builtin": true,
      "description": "Proxmox-VE-API-Plugin (editierbar). Args: --target --token --check [--node].",
      "interpreter": "plugin_local",
      "script_body": "#!/usr/bin/env python3\n\"\"\"Vesana Custom-Plugin: Proxmox VE über die REST-API (Token, kein SSH).\n\nEditierbar im Scripts-Tab. Läuft auf dem Collector, nicht auf dem Proxmox.\nNagios-Konvention: eine Zeile \"MELDUNG | metric=wert\", Exit 0/1/2/3.\nDer Wert (Perfdata) wird von Vesana als Messwert/Graph genutzt; Schwellwerte\nsetzt du am Check in Vesana (nicht hier) — nur bei API-Fehler exit 2.\n\nAufruf:  check_proxmox.py --target <ip> --token <USER@REALM!ID=SECRET> --check <name>\nChecks:  api | quorum | nodes_offline | cpu | ram | disk | uptime |\n         vms_running | vms_stopped | lxc_running | lxc_stopped | storages\nMulti-Node: --node <name> pinnt Node-Checks (cpu/ram/disk/uptime) auf einen Node.\n\"\"\"\n\nimport argparse\nimport json\nimport ssl\nimport sys\nimport urllib.error\nimport urllib.request\n\nOK, WARNING, CRITICAL, UNKNOWN = 0, 1, 2, 3\n\n\ndef api(target, token, path):\n    ctx = ssl.create_default_context()\n    ctx.check_hostname = False\n    ctx.verify_mode = ssl.CERT_NONE\n    req = urllib.request.Request(\n        f\"https://{target}:8006/api2/json{path}\",\n        headers={\"Authorization\": f\"PVEAPIToken={token}\"},\n    )\n    with urllib.request.urlopen(req, timeout=10, context=ctx) as r:  # noqa: S310 — https, self-signed opt-in\n        return json.loads(r.read()).get(\"data\")\n\n\ndef _nodes(data, node=None):\n    ns = [d for d in data if d.get(\"type\") == \"node\"]\n    if node:\n        ns = [d for d in ns if d.get(\"node\") == node]\n    return ns\n\n\ndef emit(msg, metric, value, unit=\"\"):\n    print(f\"{msg} | {metric}={value}{unit}\")\n    sys.exit(OK)\n\n\ndef main():\n    ap = argparse.ArgumentParser()\n    ap.add_argument(\"--target\", required=True)\n    ap.add_argument(\"--token\", required=True)\n    ap.add_argument(\"--check\", required=True)\n    ap.add_argument(\"--node\", default=\"\")\n    a = ap.parse_args()\n\n    try:\n        if a.check == \"api\":\n            ver = api(a.target, a.token, \"/version\")\n            emit(f\"Proxmox API erreichbar (v{ver.get('version', '?')})\", \"reachable\", 1)\n\n        if a.check in (\"quorum\",):\n            data = api(a.target, a.token, \"/cluster/status\")\n            clu = [d for d in data if d.get(\"type\") == \"cluster\"]\n            if not clu:  # Einzelknoten ohne Cluster\n                emit(\"Einzelknoten (kein Cluster) — Quorum n/a\", \"quorate\", 1)\n            q = int(clu[0].get(\"quorate\", 0))\n            print(f\"Cluster {'quorat' if q else 'NICHT quorat'} | quorate={q}\")\n            sys.exit(OK if q == 1 else CRITICAL)\n\n        data = api(a.target, a.token, \"/cluster/resources\")\n\n        if a.check == \"nodes_offline\":\n            n = sum(1 for d in data if d.get(\"type\") == \"node\" and d.get(\"status\") != \"online\")\n            print(f\"{n} Node(s) offline | offline={n}\")\n            sys.exit(OK if n == 0 else CRITICAL)\n        if a.check == \"vms_running\":\n            n = sum(1 for d in data if d.get(\"type\") == \"qemu\" and d.get(\"status\") == \"running\")\n            emit(f\"{n} VMs laufen\", \"running\", n)\n        if a.check == \"vms_stopped\":\n            n = sum(\n                1 for d in data if d.get(\"type\") == \"qemu\" and d.get(\"status\") == \"stopped\" and not d.get(\"template\")\n            )\n            emit(f\"{n} VMs gestoppt\", \"stopped\", n)\n        if a.check == \"lxc_running\":\n            n = sum(1 for d in data if d.get(\"type\") == \"lxc\" and d.get(\"status\") == \"running\")\n            emit(f\"{n} LXC laufen\", \"running\", n)\n        if a.check == \"lxc_stopped\":\n            n = sum(\n                1 for d in data if d.get(\"type\") == \"lxc\" and d.get(\"status\") == \"stopped\" and not d.get(\"template\")\n            )\n            emit(f\"{n} LXC gestoppt\", \"stopped\", n)\n        if a.check == \"storages\":\n            n = sum(1 for d in data if d.get(\"type\") == \"storage\" and d.get(\"status\") == \"unavailable\")\n            print(f\"{n} Storage(s) nicht verfügbar | unavailable={n}\")\n            sys.exit(OK if n == 0 else CRITICAL)\n\n        # Node-Checks\n        ns = _nodes(data, a.node or None)\n        if not ns:\n            print(f\"Node {a.node or '(erster)'} nicht gefunden | found=0\")\n            sys.exit(UNKNOWN)\n        node = ns[0]\n        if a.check == \"cpu\":\n            pct = round(float(node.get(\"cpu\", 0)) * 100, 1)\n            emit(f\"CPU {pct}%\", \"cpu\", pct, \"%\")\n        if a.check == \"ram\":\n            mem, mx = float(node.get(\"mem\", 0)), float(node.get(\"maxmem\", 0)) or 1\n            emit(f\"RAM {round(mem / mx * 100, 1)}%\", \"ram\", round(mem / mx * 100, 1), \"%\")\n        if a.check == \"disk\":\n            dk, mx = float(node.get(\"disk\", 0)), float(node.get(\"maxdisk\", 0)) or 1\n            emit(f\"Root-FS {round(dk / mx * 100, 1)}%\", \"disk\", round(dk / mx * 100, 1), \"%\")\n        if a.check == \"uptime\":\n            days = round(float(node.get(\"uptime\", 0)) / 86400, 1)\n            emit(f\"Uptime {days} Tage\", \"uptime_days\", days)\n\n        print(f\"Unbekannter --check: {a.check} | -\")\n        sys.exit(UNKNOWN)\n\n    except urllib.error.HTTPError as e:\n        hint = {401: \"Token prüfen\", 403: \"Token-Rechte (PVEAuditor an Token UND User)\"}.get(e.code, \"HTTP-Fehler\")\n        print(f\"HTTP {e.code} — {hint} | -\")\n        sys.exit(CRITICAL)\n    except Exception as e:  # noqa: BLE001\n        print(f\"Fehler: {e} | -\")\n        sys.exit(CRITICAL)\n\n\nif __name__ == \"__main__\":\n    main()\n",
      "expected_output": "nagios"
    }
  ],
  "schema_version": 1
}