{
  "schema_version": 1,
  "channel": "homelab-runtime",
  "command_id": "BACKUP-FAILED-UNITS-SOURCE-217",
  "status": "OK",
  "rc": 0,
  "host": "pve01",
  "mode": "read-only",
  "component": "cluster-audit",
  "started_at_utc": "2026-07-22T15:35:58Z",
  "finished_at_utc": "2026-07-22T15:35:58Z",
  "reference_register_checked": true,
  "reference_sha256": "5763f2f8edc75fcf6f3951f8c95896d1cc112ceccb6cf5b64a383f540a4deb8e",
  "error_register_checked": true,
  "error_register_sha256": "08b3a769b8e38eaa1acec915352fb328180d70caa7ff6cd9d1ec0da426262752",
  "command_sha256": "2839e6de5b682a8f42432e3841c7b452be12e60f73cd670b0ac4412372ad4c86",
  "duplicate_failed_command_blocked": false,
  "block_reason": null,
  "execution_started": true,
  "changes_made": false,
  "sanitized": true,
  "secrets_included": false,
  "private_addresses_included": false,
  "raw_evidence_retained_locally": true,
  "raw_evidence_sha256": "96e72a8cec4b5e5cde52a48e6816f4ce162311b07530ee398c57efc67f3480b1",
  "sanitized_output_sha256": "dbe6bb8b0419312d9774755ff8668c435118bc102b49b0255667d04f06dda499",
  "output_truncated_in_json": false,
  "full_sanitized_output_url": "https://git.gram1.ru/.well-known/homelab-runtime/latest.txt",
  "output": "PREVIOUS_COMMAND_ID=RUNTIME-PUBLISH-GAP-DIAG-216\n===== UNIT homelab-backup-matrix-refresh =====\n# /etc/systemd/system/homelab-backup-matrix-refresh.service\n[Unit]\nDescription=Generate unified homelab backup matrix state\nAfter=local-fs.target\n\n[Service]\nType=oneshot\nExecStart=/usr/local/sbin/homelab-backup-matrix-refresh\nTimeoutStartSec=5min\n===== UNIT homelab-vm160-remote-backup =====\n# /etc/systemd/system/homelab-vm160-remote-backup.service\n[Unit]\nDescription=VM160 full-image backup streamed from pve02 to pve03\nAfter=network-online.target pve-cluster.service\nWants=network-online.target\n\n[Service]\nType=oneshot\nTimeoutStartSec=6h\nExecStart=/usr/local/sbin/homelab-vm160-remote-backup\n===== UNIT homelab-cluster-admin-pull =====\n# /etc/systemd/system/homelab-cluster-admin-pull.service\n[Unit]\nDescription=Pull cluster-admin observer health into pve01\n\n[Service]\nType=oneshot\nExecStart=/usr/local/sbin/homelab-cluster-admin-pull\n===== FILE /usr/local/sbin/homelab-backup-matrix-refresh =====\n/usr/local/sbin/homelab-backup-matrix-refresh: Python script, ASCII text executable, with very long lines (592)\n     1\t#!/usr/bin/env python3\n     2\tfrom pathlib import Path\n     3\timport shutil\n     4\timport json,subprocess,os,time,sys,hashlib\n     5\tM=Path(\"/etc/homelab-backup-matrix/matrix.json\")\n     6\tROOT=Path(\"/var/lib/homelab-backup-matrix\")\n     7\tRENDER=ROOT/\"rendered\"\n     8\tCOMPAT=ROOT/\"compat\"\n     9\tdef cmd(args):\n    10\t    p=subprocess.run(args,stdout=subprocess.PIPE,stderr=subprocess.DEVNULL,text=True,check=False)\n    11\t    return p.stdout.strip() or \"unknown\"\n    12\tdef atomic(path,text,mode=0o640):\n    13\t    path.parent.mkdir(parents=True,exist_ok=True)\n    14\t    tmp=path.with_name(path.name+\".tmp\")\n    15\t    tmp.write_text(text,encoding=\"utf-8\")\n    16\t    os.chmod(tmp,mode)\n    17\t    os.replace(tmp,path)\n    18\tdef parse_health(path):\n    19\t    if path.suffix.lower()==\".json\":\n    20\t        try:\n    21\t            data=json.loads(path.read_text(encoding=\"utf-8\",errors=\"replace\"))\n    22\t            return str(data.get(\"status\",data.get(\"STATUS\",data.get(\"result\",\"UNKNOWN\"))))\n    23\t        except Exception:\n    24\t            return \"ERROR\"\n    25\t    data={}\n    26\t    text=path.read_text(encoding=\"utf-8\",errors=\"replace\")\n    27\t    for token in text.replace(chr(10),\" \").split():\n    28\t        if \"=\" in token:\n    29\t            key,value=token.split(\"=\",1)\n    30\t            if key and key[0].isalpha():\n    31\t                data[key]=value\n    32\t    return str(data.get(\"STATUS\",data.get(\"status\",\"UNKNOWN\")))\n    33\tdef normalize(value):\n    34\t    value=str(value).strip().upper()\n    35\t    if value in {\"OK\",\"PASS\",\"PASSED\",\"SUCCESS\",\"HEALTHY\"}:\n    36\t        return \"OK\"\n    37\t    if value in {\"WARN\",\"WARNING\"}:\n    38\t        return \"WARN\"\n    39\t    if value in {\"FAIL\",\"FAILED\",\"ERROR\",\"CRITICAL\",\"BAD\"}:\n    40\t        return \"FAIL\"\n    41\t    return value or \"UNKNOWN\"\n    42\tmatrix=json.loads(M.read_text(encoding=\"utf-8\"))\n    43\tROOT.mkdir(parents=True,exist_ok=True)\n    44\tRENDER.mkdir(parents=True,exist_ok=True)\n    45\tCOMPAT.mkdir(parents=True,exist_ok=True)\n    46\tnow=int(time.time())\n    47\tnow_iso=time.strftime(\"%Y-%m-%dT%H:%M:%SZ\",time.gmtime(now))\n    48\tresults=[]\n    49\tcompat_map=[]\n    50\tfor job in matrix.get(\"jobs\",[]):\n    51\t    original_sources=list(job.get(\"evidence_sources_original\",[]))\n    52\t    evidence_inputs=list(job.get(\"evidence_inputs\",[]))\n    53\t    if original_sources and len(original_sources)!=len(evidence_inputs):\n    54\t        raise RuntimeError(\"evidence_mapping_length_mismatch:\"+str(job.get(\"name\",\"\")))\n    55\t    for source_raw,destination_raw in zip(original_sources,evidence_inputs):\n    56\t        source_path=Path(source_raw)\n    57\t        destination_path=Path(destination_raw)\n    58\t        if not source_path.is_file():\n    59\t            continue\n    60\t        head=source_path.read_text(encoding=\"utf-8\",errors=\"ignore\")[:4096]\n    61\t        if \"TYPE=backup-matrix-generated\" in head or \"CANONICAL_STATE=/var/lib/homelab-backup-matrix/state.json\" in head:\n    62\t            continue\n    63\t        destination_path.parent.mkdir(parents=True,exist_ok=True)\n    64\t        temporary=destination_path.with_name(destination_path.name+\".tmp-ingest\")\n    65\t        shutil.copy2(source_path,temporary)\n    66\t        os.replace(temporary,destination_path)\n    67\t    jid=str(job.get(\"id\",\"\"))\n    68\t    name=str(job.get(\"name\",jid))\n    69\t    timer=str(job.get(\"timer\",\"\"))\n    70\t    service=str(job.get(\"service\",\"\"))\n    71\t    enabled=cmd([\"systemctl\",\"is-enabled\",timer]) if timer else \"missing\"\n    72\t    active=cmd([\"systemctl\",\"is-active\",timer]) if timer else \"missing\"\n    73\t    source_kind=str(job.get(\"source_kind\",\"health_file\"))\n    74\t    best=None\n    75\t    for raw in job.get(\"evidence_inputs\",job.get(\"legacy_health_inputs\",[])):\n    76\t        path=Path(raw)\n    77\t        if not path.is_file():\n    78\t            continue\n    79\t        mtime=int(path.stat().st_mtime)\n    80\t        if best is None or mtime>best[0]:\n    81\t            best=(mtime,str(path),parse_health(path))\n    82\t    service_result=cmd([\"systemctl\",\"show\",service,\"-p\",\"Result\",\"--value\"]) if service else \"missing\"\n    83\t    service_exec_main_status=cmd([\"systemctl\",\"show\",service,\"-p\",\"ExecMainStatus\",\"--value\"]) if service else \"missing\"\n    84\t    evidence_level=\"artifact_or_health\"\n    85\t    if best is None and source_kind==\"systemd\":\n    86\t        stamp=Path(\"/var/lib/systemd/timers\")/(\"stamp-\"+timer)\n    87\t        mtime=int(stamp.stat().st_mtime) if stamp.is_file() else 0\n    88\t        source=\"systemd:\"+service\n    89\t        source_status=\"OK\" if service_result==\"success\" and service_exec_main_status==\"0\" else \"FAIL\"\n    90\t        evidence_level=\"execution_only\"\n    91\t    else:\n    92\t        source=best[1] if best else \"\"\n    93\t        source_status=normalize(best[2]) if best else \"UNMAPPED\"\n    94\t        mtime=best[0] if best else 0\n    95\t    age=now-mtime if mtime else -1\n    96\t    max_age=int(job.get(\"max_age_seconds\",172800))\n    97\t    critical=bool(job.get(\"critical\",False))\n    98\t    if not critical and enabled!=\"enabled\":\n    99\t        final=\"DISABLED\"\n   100\t    elif critical and (enabled!=\"enabled\" or active!=\"active\"):\n   101\t        final=\"FAIL\"\n   102\t    elif source_status==\"UNMAPPED\":\n   103\t        final=\"UNMAPPED\"\n   104\t    elif source_status!=\"OK\":\n   105\t        final=\"FAIL\"\n   106\t    elif age<0:\n   107\t        final=\"MISSING\"\n   108\t    elif age>max_age:\n   109\t        final=\"STALE\"\n   110\t    else:\n   111\t        final=\"OK\"\n   112\t    freshness=\"UNKNOWN\" if age<0 else (\"FRESH\" if age<=max_age else \"STALE\")\n   113\t    source_mtime=time.strftime(\"%Y-%m-%dT%H:%M:%SZ\",time.gmtime(mtime)) if mtime else \"\"\n   114\t    result={\"id\":jid,\"name\":name,\"category\":job.get(\"category\",\"unknown\"),\"critical\":critical,\"timer\":timer,\"service\":service,\"timer_enabled\":enabled,\"timer_active\":active,\"status\":final,\"source_kind\":source_kind,\"evidence_level\":evidence_level,\"service_result\":service_result,\"service_exec_main_status\":service_exec_main_status,\"source_health\":source,\"source_status\":source_status,\"source_mtime\":source_mtime,\"age_seconds\":age,\"max_age_seconds\":max_age,\"freshness\":freshness,\"generated_health_output\":job.get(\"generated_health_output\",\"\")}\n   115\t    results.append(result)\n   116\t    mode_value=matrix.get(\"mode\",\"shadow\"); lines=[f\"STATUS={final}\",f\"TS={now_iso}\",\"TYPE=backup-matrix-generated\",f\"JOB_ID={jid}\",f\"JOB_NAME={name}\",f\"MATRIX_MODE={mode_value}\",f\"TIMER={timer}\",f\"SERVICE={service}\",f\"TIMER_ENABLED={enabled}\",f\"TIMER_ACTIVE={active}\",f\"SOURCE_KIND={source_kind}\",f\"EVIDENCE_LEVEL={evidence_level}\",f\"SERVICE_RESULT={service_result}\",f\"SERVICE_EXEC_MAIN_STATUS={service_exec_main_status}\",f\"SOURCE_HEALTH={source}\",f\"SOURCE_STATUS={source_status}\",f\"SOURCE_MTIME_UTC={source_mtime}\",f\"AGE_SECONDS={age}\",f\"MAX_AGE_SECONDS={max_age}\",f\"FRESHNESS={freshness}\"]\n   117\t    atomic(Path(job.get(\"generated_health_output\",str(RENDER/(name+\".txt\")))),chr(10).join(lines)+chr(10))\n   118\t    for raw in job.get(\"compatibility_outputs\",[]):\n   119\t        legacy=str(raw)\n   120\t        digest=hashlib.sha256(legacy.encode(\"utf-8\")).hexdigest()[:12]\n   121\t        base_name=Path(legacy).name or \"health\"\n   122\t        safe=f\"{jid}--{base_name}--{digest}.txt\"\n   123\t        compat_path=COMPAT/safe\n   124\t        compat_lines=lines+[f\"LEGACY_SOURCE_PATH={legacy}\",f\"CANONICAL_JOB_ID={jid}\",f\"CANONICAL_STATE={ROOT}/state.json\"]\n   125\t        atomic(compat_path,chr(10).join(compat_lines)+chr(10))\n   126\t        atomic(Path(legacy),chr(10).join(compat_lines)+chr(10))\n   127\t        compat_map.append({\"job_id\":jid,\"legacy_path\":legacy,\"compatibility_path\":str(compat_path)})\n   128\tbad=sum(1 for item in results if item[\"status\"] in {\"FAIL\",\"STALE\",\"MISSING\"})\n   129\tunmapped=sum(1 for item in results if item[\"status\"]==\"UNMAPPED\")\n   130\tdisabled=sum(1 for item in results if item[\"status\"]==\"DISABLED\")\n   131\tok=sum(1 for item in results if item[\"status\"]==\"OK\")\n   132\tmode=str(matrix.get(\"mode\",\"shadow\"))\n   133\toverall=\"OK\" if bad==0 and unmapped==0 else (\"WARN\" if mode==\"shadow\" else \"FAIL\")\n   134\tcompat_map=sorted(compat_map,key=lambda item:(item[\"legacy_path\"],item[\"job_id\"]))\n   135\tatomic(ROOT/\"compat-map.json\",json.dumps(compat_map,ensure_ascii=False,indent=2,sort_keys=True)+chr(10))\n   136\tstate={\"version\":1,\"generated_at\":now_iso,\"matrix_mode\":mode,\"matrix_sha256\":hashlib.sha256(M.read_bytes()).hexdigest(),\"summary\":{\"status\":overall,\"jobs_total\":len(results),\"jobs_ok\":ok,\"jobs_bad\":bad,\"jobs_unmapped\":unmapped,\"jobs_disabled\":disabled},\"jobs\":results}\n   137\tstate[\"authority\"]=\"canonical\"\n   138\tstate[\"compatibility_map\"]=str(ROOT/\"compat-map.json\")\n   139\tstate[\"summary\"][\"compatibility_outputs\"]=len(compat_map)\n   140\tatomic(ROOT/\"state.json\",json.dumps(state,ensure_ascii=False,indent=2,sort_keys=True)+chr(10))\n===== FILE /usr/local/sbin/homelab-vm160-remote-backup =====\n/usr/local/sbin/homelab-vm160-remote-backup: Bourne-Again shell script, ASCII text executable\n     1\t#!/usr/bin/env bash\n     2\tset -Eeuo pipefail\n     3\texport SYSTEMD_PAGER=cat PAGER=cat SYSTEMD_LESS=FRXMK\n     4\tVMID=160\n     5\tSOURCE=root@[PRIVATE_IP]\n     6\tTARGET=root@[PRIVATE_IP]\n     7\tKH=[SENSITIVE_PATH]\n     8\tPARENT=/mnt/staging/vzdump\n     9\tPREFIX=vm160-pve02-\n    10\tHEALTH=/var/lib/homelab-health/vm160-remote-backup.txt\n    11\tLOCK=/run/lock/homelab-vm160-remote-backup.lock\n    12\tSSH=(-o BatchMode=yes -o ConnectTimeout=12 -o StrictHostKeyChecking=yes -o UserKnownHostsFile=\"$KH\")\n    13\tTARGET_WORKER=/usr/local/libexec/homelab-vm160-target-worker\n    14\ttarget_worker(){ ssh \"${SSH[@]}\" \"$TARGET\" bash -s -- \"$@\" < \"$TARGET_WORKER\"; }\n    15\tinstall -d -m 0755 \"$(dirname \"$HEALTH\")\" \"$(dirname \"$LOCK\")\"\n    16\texec 9>\"$LOCK\"\n    17\tflock -n 9\n    18\ttest \"$(hostname -s)\" = pve01\n    19\ttest -x \"$TARGET_WORKER\"\n    20\ttest \"$(ssh \"${SSH[@]}\" \"$SOURCE\" \"qm status $VMID\" | awk '{print $2}')\" = running\n    21\tSOURCE_ACTIVE_RC=0\n    22\tSOURCE_ACTIVE=\"$(ssh \"${SSH[@]}\" \"$SOURCE\" \"pgrep -af '(^|/)(vzdump|qmrestore|pctrestore|vma|zstd)([[:space:]]|$)'\")\" || SOURCE_ACTIVE_RC=$?\n    23\ttest \"$SOURCE_ACTIVE_RC\" -eq 0 || test \"$SOURCE_ACTIVE_RC\" -eq 1\n    24\ttest -z \"$SOURCE_ACTIVE\"\n    25\tTS=\"$(date -u +%Y%m%dT%H%M%SZ)\"\n    26\tDIR=\"$PARENT/${PREFIX}${TS}\"\n    27\tNAME=\"vzdump-qemu-${VMID}-${TS}.vma.zst\"\n    28\tPART=\"$DIR/.${NAME}.partial\"\n    29\tFINAL=\"$DIR/$NAME\"\n    30\tLOG=\"$(mktemp /tmp/vm160-remote-backup.XXXXXX.log)\"\n    31\tcleanup(){\n    32\t  rc=$?\n    33\t  trap - EXIT\n    34\t  if test \"$rc\" -ne 0; then\n    35\t    ssh \"${SSH[@]}\" \"$TARGET\" \"rm -rf -- '$DIR'\" >/dev/null 2>&1 || true\n    36\t    tmp=\"${HEALTH}.tmp\"\n    37\t    printf 'STATUS=FAIL\\nSERVICE=homelab-vm160-remote-backup\\nTIMESTAMP_UTC=%s\\nVMID=%s\\nTARGET_DIR=%s\\nRC=%s\\n' \"$TS\" \"$VMID\" \"$DIR\" \"$rc\" > \"$tmp\"\n    38\t    chmod 0644 \"$tmp\"\n    39\t    mv \"$tmp\" \"$HEALTH\"\n    40\t  fi\n    41\t  rm -f \"$LOG\"\n    42\t  exit \"$rc\"\n    43\t}\n    44\ttrap cleanup EXIT\n    45\tREMOTE_FREE=\"$(ssh \"${SSH[@]}\" \"$TARGET\" \"df -B1 --output=avail '$PARENT' | awk 'NR==2{print \\$1}'\")\"\n    46\tLATEST_SIZE=\"$(ssh \"${SSH[@]}\" \"$TARGET\" \"find '$PARENT' -mindepth 2 -maxdepth 2 -type f -path '$PARENT/${PREFIX}*/vzdump-qemu-${VMID}-*.vma.zst' -printf '%s\\n' | sort -nr | head -n1\")\"\n    47\tLATEST_SIZE=\"${LATEST_SIZE:-0}\"\n    48\tif test \"$LATEST_SIZE\" -gt 0; then\n    49\t  REQUIRED=\"$((LATEST_SIZE + LATEST_SIZE / 4 + 10737418240))\"\n    50\telse\n    51\t  REQUIRED=161061273600\n    52\tfi\n    53\ttest \"$REMOTE_FREE\" -ge \"$REQUIRED\"\n    54\tssh \"${SSH[@]}\" \"$TARGET\" \"install -d -m 0700 '$DIR'\"\n    55\tssh \"${SSH[@]}\" \"$SOURCE\" \"vzdump $VMID --stdout 1 --mode snapshot --compress zstd --ionice 7\" 2>\"$LOG\" | ssh \"${SSH[@]}\" \"$TARGET\" \"cat > '$PART'\"\n    56\ttarget_worker finalize \"$PARENT\" \"$PREFIX\" \"$PART\" \"$FINAL\" \"$DIR\" \"$NAME\"\n    57\tssh \"${SSH[@]}\" \"$TARGET\" \"cat > '$DIR/vzdump.log'\" < \"$LOG\"\n    58\tSIZE=\"$(ssh \"${SSH[@]}\" \"$TARGET\" \"stat -c %s '$FINAL'\")\"\n    59\tSHA=\"$(ssh \"${SSH[@]}\" \"$TARGET\" \"sha256sum '$FINAL' | awk '{print \\$1}'\")\"\n    60\ttarget_worker promote \"$PARENT\" \"$PREFIX\" \"$DIR\" \"$TS\"\n    61\ttmp=\"${HEALTH}.tmp\"\n    62\tprintf 'STATUS=OK\\nSERVICE=homelab-vm160-remote-backup\\nTIMESTAMP_UTC=%s\\nVMID=%s\\nSOURCE_NODE=pve02\\nTARGET_NODE=pve03\\nTARGET_DIR=%s\\nARCHIVE=%s\\nSIZE_BYTES=%s\\nSHA256=%s\\nREMOTE_RETENTION_GENERATIONS=1\\n' \"$TS\" \"$VMID\" \"$DIR\" \"$FINAL\" \"$SIZE\" \"$SHA\" > \"$tmp\"\n    63\tchmod 0644 \"$tmp\"\n    64\tmv \"$tmp\" \"$HEALTH\"\n    65\ttrap - EXIT\n    66\trm -f \"$LOG\"\n    67\tprintf 'VM160_REMOTE_BACKUP_STATUS=OK\\nTARGET_DIR=%s\\nARCHIVE=%s\\nSIZE_BYTES=%s\\nSHA256=%s\\n' \"$DIR\" \"$FINAL\" \"$SIZE\" \"$SHA\"\n===== FILE /usr/local/sbin/homelab-cluster-admin-pull =====\n/usr/local/sbin/homelab-cluster-admin-pull: Bourne-Again shell script, ASCII text executable, with very long lines (424)\n     1\t#!/usr/bin/env bash\n     2\tset +e +u +o pipefail 2>/dev/null || true\n     3\tTS=$(date -u +%Y-%m-%dT%H:%M:%SZ)\n     4\tROOT=/var/lib/homelab-cluster-admin\n     5\tHEALTH_ROOT=/var/lib/homelab-health\n     6\tmkdir -p \"$ROOT\" \"$HEALTH_ROOT\"\n     7\tREMOTE=$(ssh -n -o BatchMode=yes -o ConnectTimeout=8 debian@[PRIVATE_IP] 'sudo /opt/homelab-cluster-admin/cluster-observer.sh >/dev/null 2>&1 || true; sudo /opt/homelab-cluster-admin/full-observer.sh >/dev/null 2>&1 || true; printf \"__OBS__\\\\n\"; cat /var/lib/homelab-health/cluster-admin-observer.txt; printf \"__FULL__\\\\n\"; cat /var/lib/homelab-health/cluster-admin-full-observer.txt' 2>/tmp/homelab-cluster-admin-pull.err)\n     8\tRC=$?\n     9\tOBS=$(printf \"%s\\n\" \"$REMOTE\" | awk '/^__OBS__$/{f=1;next}/^__FULL__$/{f=0}f')\n    10\tFULL=$(printf \"%s\\n\" \"$REMOTE\" | awk '/^__FULL__$/{f=1;next}f')\n    11\tOBS_BAD=0; FULL_BAD=0\n    12\tprintf \"%s\\n\" \"$OBS\" | grep -q \"^STATUS=OK\" || OBS_BAD=1\n    13\tprintf \"%s\\n\" \"$FULL\" | grep -q \"^STATUS=OK\" || FULL_BAD=1\n    14\tprintf \"%s\\n\" \"$FULL\" | grep -q \"BAD=0\" || FULL_BAD=1\n    15\tPROBE_OK=$(printf \"%s\\n\" \"$FULL\" | sed -n 's/.*PROBE_OK=\\([0-9][0-9]*\\).*/\\1/p' | head -n1)\n    16\tPROBE_BAD=$(printf \"%s\\n\" \"$FULL\" | sed -n 's/.*PROBE_BAD=\\([0-9][0-9]*\\).*/\\1/p' | head -n1)\n    17\t[ \"${PROBE_BAD:-1}\" = 0 ] || FULL_BAD=1\n    18\tBAD=$((OBS_BAD+FULL_BAD)); [ \"$RC\" -eq 0 ] || BAD=$((BAD+1))\n    19\tprintf \"%s\\n\" \"$OBS\" > \"$ROOT/observer-remote-latest.txt\"\n    20\tprintf \"%s\\n\" \"$FULL\" > \"$ROOT/full-observer-central-latest.md\"\n    21\tOBS_STATUS=$([ \"$OBS_BAD\" -eq 0 ] && [ \"$RC\" -eq 0 ] && echo OK || echo WARN)\n    22\tFULL_STATUS=$([ \"$FULL_BAD\" -eq 0 ] && [ \"$RC\" -eq 0 ] && echo OK || echo WARN)\n    23\techo \"STATUS=$OBS_STATUS TS=$TS TYPE=cluster-admin-central-pull BAD=$OBS_BAD VMID=180 IP=[PRIVATE_IP] SSH_RC=$RC OBSERVER_STATUS=$OBS_STATUS REPORT=$ROOT/observer-remote-latest.txt\" > \"$HEALTH_ROOT/cluster-admin-observer.txt\"\n    24\techo \"STATUS=$FULL_STATUS TS=$TS TYPE=cluster-admin-full-observer-central BAD=$FULL_BAD WARN=0 VMID=180 PULL_RC=$RC FULL_STATUS=$FULL_STATUS PROBE_OK=${PROBE_OK:-0} PROBE_BAD=${PROBE_BAD:-1} REPORT=$ROOT/full-observer-central-latest.md\" > \"$HEALTH_ROOT/cluster-admin-full-observer.txt\"\n    25\techo \"STATUS=$FULL_STATUS TS=$TS TYPE=cluster-admin-restricted-probes BAD=$FULL_BAD VMID=180 MODE=FORCED_COMMAND_READONLY_CURRENT PROBE_OK=${PROBE_OK:-0} PROBE_BAD=${PROBE_BAD:-1} TARGETS=pve01,pve02,pve03,edge SOURCE=full-observer\" > \"$HEALTH_ROOT/cluster-admin-restricted-probes.txt\"\n    26\tcat \"$HEALTH_ROOT/cluster-admin-observer.txt\"\n    27\tcat \"$HEALTH_ROOT/cluster-admin-full-observer.txt\"\n    28\tcat \"$HEALTH_ROOT/cluster-admin-restricted-probes.txt\"\n    29\t[ \"$BAD\" -eq 0 ]\nCHANGES_MADE=NO\n"
}
