From a1f8bbf453855e998f4150cfe72edf5fc458b10e Mon Sep 17 00:00:00 2001 From: homelab-runtime-publisher Date: Wed, 23 Sep 2026 22:18:51 +0000 Subject: [PATCH] runtime: publish SUPPORT-260924-FLEET-CAPTCHA-SHARED-WIDGET-DOMAINS-RECONCILE-FIX-0119R1 --- ...D-WIDGET-DOMAINS-RECONCILE-FIX-0119R1.json | 38 + ...ED-WIDGET-DOMAINS-RECONCILE-FIX-0119R1.txt | 86 +++ runtime/latest.json | 16 +- runtime/latest.txt | 665 ++---------------- 4 files changed, 188 insertions(+), 617 deletions(-) create mode 100644 runtime/history/SUPPORT-260924-FLEET-CAPTCHA-SHARED-WIDGET-DOMAINS-RECONCILE-FIX-0119R1.json create mode 100644 runtime/history/SUPPORT-260924-FLEET-CAPTCHA-SHARED-WIDGET-DOMAINS-RECONCILE-FIX-0119R1.txt diff --git a/runtime/history/SUPPORT-260924-FLEET-CAPTCHA-SHARED-WIDGET-DOMAINS-RECONCILE-FIX-0119R1.json b/runtime/history/SUPPORT-260924-FLEET-CAPTCHA-SHARED-WIDGET-DOMAINS-RECONCILE-FIX-0119R1.json new file mode 100644 index 00000000..b382dfca --- /dev/null +++ b/runtime/history/SUPPORT-260924-FLEET-CAPTCHA-SHARED-WIDGET-DOMAINS-RECONCILE-FIX-0119R1.json @@ -0,0 +1,38 @@ +{ + "schema_version": 1, + "channel": "homelab-runtime", + "command_id": "SUPPORT-260924-FLEET-CAPTCHA-SHARED-WIDGET-DOMAINS-RECONCILE-FIX-0119R1", + "status": "OK", + "rc": 0, + "host": "pve01", + "mode": "read-only", + "component": "forum-captcha", + "started_at_utc": "2026-09-23T22:18:48Z", + "finished_at_utc": "2026-09-23T22:18:50Z", + "reference_register_checked": true, + "reference_sha256": "5a3d8e5154c41cb582a4e0aca68090be1f0138918bf82131a948df326f9d8d66", + "error_register_checked": true, + "error_register_sha256": "3b09a553ec0f527ed3afeed4753f52a74ee3036045bbfb3c685e9f8af4ba7ba0", + "command_sha256": "ba8787e7b885617e285e2a52c96350817e90ca7b882b71d3424f84a02ab601fb", + "duplicate_failed_command_blocked": false, + "block_reason": null, + "execution_started": true, + "change_declared": false, + "result_contract_valid": true, + "result_contract_status": null, + "result_contract_error": null, + "command_rc": 0, + "changes_made": false, + "rollback_started": false, + "rollback_restored": null, + "mutation_outcome": "NO_MUTATION", + "sanitized": true, + "secrets_included": false, + "private_addresses_included": false, + "raw_evidence_retained_locally": true, + "raw_evidence_sha256": "12bc50352aad0e5bf2e5d55491007dea4047ffde164f8f754a4b5b13d30a4dfe", + "sanitized_output_sha256": "12bc50352aad0e5bf2e5d55491007dea4047ffde164f8f754a4b5b13d30a4dfe", + "output_truncated_in_json": false, + "full_sanitized_output_url": "https://git.gram1.ru/.well-known/homelab-runtime/latest.txt", + "output": "REFERENCE_SHA256=5a3d8e5154c41cb582a4e0aca68090be1f0138918bf82131a948df326f9d8d66\nERROR_REGISTER_SHA256=3b09a553ec0f527ed3afeed4753f52a74ee3036045bbfb3c685e9f8af4ba7ba0\nRUNNER_SHA256=b248a4c32c9cc64e5747e7dce6c7fc0a23f5124a77c71ce72e27a81aceae9d2d\nGITEA_AUTH_SUPPORT-260924-FLEET-CAPTCHA-SHARED-WIDGET-DOMAINS-RECONCILE-0107R1_TXT_SHA256=3affbd310f46b6f807edb9a4f411460798fa9ddd974238877f6dab21c2afa59d\nGITEA_AUTH_SUPPORT-260924-FLEET-CAPTCHA-PRODUCTION-SERIAL-MIGRATION-0048R1_TXT_SHA256=1ab5ca9366b467d0d2ed85bc56364f06e9a867977f5106eb8cdd5d15b14c6c71\nGITEA_AUTH_SUPPORT-260924-FLEET-CAPTCHA-CF-LIST-AUTOSELECT-0006R1_TXT_SHA256=469b6cb88197f3fc7ff461f7b0dd46ea218c58ffa5f944222d339941510ac360\nGITEA_BRANCH_HEAD=df13b49dffd2cc5b4be705ca3a96241b11a53fdd\nCF_TOKEN_SHA16=7fb2f75e3a1f8a3b\nCF_TOKEN_VERIFY_HTTP=200\nCF_ZONE_LOOKUP_HTTP=200\nCF_PVEPRO_ZONE_MATCH_COUNT=1\nCF_ACCOUNT_ID_SHA256=49ace22b1853724543e1b3b00f02d6a03acd8bd4f9046f8cf5c3c9b6343984f3\nCF_WIDGET_LIST_HTTP=200\nCF_WIDGET_COUNT=2\nSHARED_WIDGET_MODE=managed\nSHARED_WIDGET_NAME_SHA256=2ce0d0392662a166fef18981747dffc4e729571d5ebc953f9a5a2ba7b63c4742\nSHARED_WIDGET_DOMAIN_COUNT=10\nSHARED_WIDGET_UNIQUE_DOMAIN_COUNT=10\nSHARED_WIDGET_DOMAIN_01=codevipe.ru\nSHARED_WIDGET_DOMAIN_01_CLASS=PRODUCTION_FORUM\nSHARED_WIDGET_DOMAIN_02=dsmods.ru\nSHARED_WIDGET_DOMAIN_02_CLASS=PRODUCTION_FORUM\nSHARED_WIDGET_DOMAIN_03=gamevipe.ru\nSHARED_WIDGET_DOMAIN_03_CLASS=PRODUCTION_FORUM\nSHARED_WIDGET_DOMAIN_04=gram1.ru\nSHARED_WIDGET_DOMAIN_04_CLASS=UNKNOWN\nSHARED_WIDGET_DOMAIN_04_UNKNOWN_DNS_V4_COUNT=1\nSHARED_WIDGET_DOMAIN_04_UNKNOWN_HTTPS=200\nSHARED_WIDGET_DOMAIN_05=hkmods.ru\nSHARED_WIDGET_DOMAIN_05_CLASS=PRODUCTION_FORUM\nSHARED_WIDGET_DOMAIN_06=kingofwolk.ru\nSHARED_WIDGET_DOMAIN_06_CLASS=PRODUCTION_FORUM\nSHARED_WIDGET_DOMAIN_07=newfi.ru\nSHARED_WIDGET_DOMAIN_07_CLASS=PRODUCTION_FORUM\nSHARED_WIDGET_DOMAIN_08=newfilya.ru\nSHARED_WIDGET_DOMAIN_08_CLASS=UNKNOWN\nSHARED_WIDGET_DOMAIN_08_UNKNOWN_DNS_V4_COUNT=1\nSHARED_WIDGET_DOMAIN_08_UNKNOWN_HTTPS=200\nSHARED_WIDGET_DOMAIN_09=pvepro.ru\nSHARED_WIDGET_DOMAIN_09_CLASS=PRODUCTION_FORUM\nSHARED_WIDGET_DOMAIN_10=zakrutim.ru\nSHARED_WIDGET_DOMAIN_10_CLASS=PRODUCTION_FORUM\nSHARED_WIDGET_PRODUCTION_FORUM_DOMAIN_COUNT=8\nSHARED_WIDGET_KNOWN_STAGING_DOMAIN_COUNT=0\nSHARED_WIDGET_UNKNOWN_DOMAIN_COUNT=2\nSHARED_WIDGET_DOMAINS_SHA256=55180f23ff32cb88c56d10d162185bcb7a7031be0aa2469ecb97b53f06352df6\nCLASSIFICATION_ENGINE=PYTHON_NO_STDIN_CONSUMPTION\nCLASSIFIED_DOMAIN_ROWS=10\nMUTATION_BOUNDARY=READ_ONLY;CLOUDFLARE_GET_ONLY;NO_WIDGET_CREATE_UPDATE_DELETE;NO_QGA;NO_XENFORO;NO_DB;NO_VM_CONFIG;NO_EDGE;NO_BACKUP\nSHARED_WIDGET_DOMAIN_RECONCILE_FIXED_COMPLETE=true\nDECISION=PASS_FLEET_CAPTCHA_SHARED_WIDGET_DOMAIN_RECONCILE_FIX\nNEXT_GATE=REVIEW_UNKNOWN_SHARED_WIDGET_DOMAINS\nTASK_RESULT=SHARED_WIDGET_UNKNOWN_DOMAINS_EXACTLY_IDENTIFIED\nHOMELAB_RESULT_CONTRACT={\"version\":1,\"command_id\":\"SUPPORT-260924-FLEET-CAPTCHA-SHARED-WIDGET-DOMAINS-RECONCILE-FIX-0119R1\",\"status\":\"OK\",\"changes_made\":false,\"rollback_started\":false,\"rollback_restored\":null}\n" +} diff --git a/runtime/history/SUPPORT-260924-FLEET-CAPTCHA-SHARED-WIDGET-DOMAINS-RECONCILE-FIX-0119R1.txt b/runtime/history/SUPPORT-260924-FLEET-CAPTCHA-SHARED-WIDGET-DOMAINS-RECONCILE-FIX-0119R1.txt new file mode 100644 index 00000000..e07896b0 --- /dev/null +++ b/runtime/history/SUPPORT-260924-FLEET-CAPTCHA-SHARED-WIDGET-DOMAINS-RECONCILE-FIX-0119R1.txt @@ -0,0 +1,86 @@ +CHAT_OUTPUT_BEGIN +COMMAND_ID=SUPPORT-260924-FLEET-CAPTCHA-SHARED-WIDGET-DOMAINS-RECONCILE-FIX-0119R1 +STATUS=OK +RC=0 +HOST=pve01 +MODE=read-only +COMPONENT=forum-captcha +REFERENCE_REGISTER_CHECK=OK +REFERENCE_SHA256=5a3d8e5154c41cb582a4e0aca68090be1f0138918bf82131a948df326f9d8d66 +ERROR_REGISTER_CHECK=OK +ERROR_REGISTER_SHA256=3b09a553ec0f527ed3afeed4753f52a74ee3036045bbfb3c685e9f8af4ba7ba0 +COMMAND_SHA256=ba8787e7b885617e285e2a52c96350817e90ca7b882b71d3424f84a02ab601fb +DUPLICATE_FAILED_COMMAND_BLOCKED=false +EXECUTION_STARTED=true +CHANGE_DECLARED=false +RESULT_CONTRACT_VALID=true +RESULT_CONTRACT_STATUS=NOT_APPLICABLE +RESULT_CONTRACT_ERROR=NONE +COMMAND_RC=0 +CHANGES_MADE=false +ROLLBACK_STARTED=false +ROLLBACK_RESTORED=null +MUTATION_OUTCOME=NO_MUTATION +SANITIZED=yes +SECRETS_INCLUDED=no +PRIVATE_ADDRESSES_INCLUDED=no +RAW_EVIDENCE_SHA256=12bc50352aad0e5bf2e5d55491007dea4047ffde164f8f754a4b5b13d30a4dfe +SANITIZED_OUTPUT_SHA256=12bc50352aad0e5bf2e5d55491007dea4047ffde164f8f754a4b5b13d30a4dfe +OUTPUT_BEGIN +REFERENCE_SHA256=5a3d8e5154c41cb582a4e0aca68090be1f0138918bf82131a948df326f9d8d66 +ERROR_REGISTER_SHA256=3b09a553ec0f527ed3afeed4753f52a74ee3036045bbfb3c685e9f8af4ba7ba0 +RUNNER_SHA256=b248a4c32c9cc64e5747e7dce6c7fc0a23f5124a77c71ce72e27a81aceae9d2d +GITEA_AUTH_SUPPORT-260924-FLEET-CAPTCHA-SHARED-WIDGET-DOMAINS-RECONCILE-0107R1_TXT_SHA256=3affbd310f46b6f807edb9a4f411460798fa9ddd974238877f6dab21c2afa59d +GITEA_AUTH_SUPPORT-260924-FLEET-CAPTCHA-PRODUCTION-SERIAL-MIGRATION-0048R1_TXT_SHA256=1ab5ca9366b467d0d2ed85bc56364f06e9a867977f5106eb8cdd5d15b14c6c71 +GITEA_AUTH_SUPPORT-260924-FLEET-CAPTCHA-CF-LIST-AUTOSELECT-0006R1_TXT_SHA256=469b6cb88197f3fc7ff461f7b0dd46ea218c58ffa5f944222d339941510ac360 +GITEA_BRANCH_HEAD=df13b49dffd2cc5b4be705ca3a96241b11a53fdd +CF_TOKEN_SHA16=7fb2f75e3a1f8a3b +CF_TOKEN_VERIFY_HTTP=200 +CF_ZONE_LOOKUP_HTTP=200 +CF_PVEPRO_ZONE_MATCH_COUNT=1 +CF_ACCOUNT_ID_SHA256=49ace22b1853724543e1b3b00f02d6a03acd8bd4f9046f8cf5c3c9b6343984f3 +CF_WIDGET_LIST_HTTP=200 +CF_WIDGET_COUNT=2 +SHARED_WIDGET_MODE=managed +SHARED_WIDGET_NAME_SHA256=2ce0d0392662a166fef18981747dffc4e729571d5ebc953f9a5a2ba7b63c4742 +SHARED_WIDGET_DOMAIN_COUNT=10 +SHARED_WIDGET_UNIQUE_DOMAIN_COUNT=10 +SHARED_WIDGET_DOMAIN_01=codevipe.ru +SHARED_WIDGET_DOMAIN_01_CLASS=PRODUCTION_FORUM +SHARED_WIDGET_DOMAIN_02=dsmods.ru +SHARED_WIDGET_DOMAIN_02_CLASS=PRODUCTION_FORUM +SHARED_WIDGET_DOMAIN_03=gamevipe.ru +SHARED_WIDGET_DOMAIN_03_CLASS=PRODUCTION_FORUM +SHARED_WIDGET_DOMAIN_04=gram1.ru +SHARED_WIDGET_DOMAIN_04_CLASS=UNKNOWN +SHARED_WIDGET_DOMAIN_04_UNKNOWN_DNS_V4_COUNT=1 +SHARED_WIDGET_DOMAIN_04_UNKNOWN_HTTPS=200 +SHARED_WIDGET_DOMAIN_05=hkmods.ru +SHARED_WIDGET_DOMAIN_05_CLASS=PRODUCTION_FORUM +SHARED_WIDGET_DOMAIN_06=kingofwolk.ru +SHARED_WIDGET_DOMAIN_06_CLASS=PRODUCTION_FORUM +SHARED_WIDGET_DOMAIN_07=newfi.ru +SHARED_WIDGET_DOMAIN_07_CLASS=PRODUCTION_FORUM +SHARED_WIDGET_DOMAIN_08=newfilya.ru +SHARED_WIDGET_DOMAIN_08_CLASS=UNKNOWN +SHARED_WIDGET_DOMAIN_08_UNKNOWN_DNS_V4_COUNT=1 +SHARED_WIDGET_DOMAIN_08_UNKNOWN_HTTPS=200 +SHARED_WIDGET_DOMAIN_09=pvepro.ru +SHARED_WIDGET_DOMAIN_09_CLASS=PRODUCTION_FORUM +SHARED_WIDGET_DOMAIN_10=zakrutim.ru +SHARED_WIDGET_DOMAIN_10_CLASS=PRODUCTION_FORUM +SHARED_WIDGET_PRODUCTION_FORUM_DOMAIN_COUNT=8 +SHARED_WIDGET_KNOWN_STAGING_DOMAIN_COUNT=0 +SHARED_WIDGET_UNKNOWN_DOMAIN_COUNT=2 +SHARED_WIDGET_DOMAINS_SHA256=55180f23ff32cb88c56d10d162185bcb7a7031be0aa2469ecb97b53f06352df6 +CLASSIFICATION_ENGINE=PYTHON_NO_STDIN_CONSUMPTION +CLASSIFIED_DOMAIN_ROWS=10 +MUTATION_BOUNDARY=READ_ONLY;CLOUDFLARE_GET_ONLY;NO_WIDGET_CREATE_UPDATE_DELETE;NO_QGA;NO_XENFORO;NO_DB;NO_VM_CONFIG;NO_EDGE;NO_BACKUP +SHARED_WIDGET_DOMAIN_RECONCILE_FIXED_COMPLETE=true +DECISION=PASS_FLEET_CAPTCHA_SHARED_WIDGET_DOMAIN_RECONCILE_FIX +NEXT_GATE=REVIEW_UNKNOWN_SHARED_WIDGET_DOMAINS +TASK_RESULT=SHARED_WIDGET_UNKNOWN_DOMAINS_EXACTLY_IDENTIFIED +HOMELAB_RESULT_CONTRACT={"version":1,"command_id":"SUPPORT-260924-FLEET-CAPTCHA-SHARED-WIDGET-DOMAINS-RECONCILE-FIX-0119R1","status":"OK","changes_made":false,"rollback_started":false,"rollback_restored":null} + +OUTPUT_END +CHAT_OUTPUT_END diff --git a/runtime/latest.json b/runtime/latest.json index 5b25c8a1..b382dfca 100644 --- a/runtime/latest.json +++ b/runtime/latest.json @@ -1,19 +1,19 @@ { "schema_version": 1, "channel": "homelab-runtime", - "command_id": "SUPPORT-260924-HOMELAB-MOLDOVA-BASE-RELEASE-AUTH-1147R1", + "command_id": "SUPPORT-260924-FLEET-CAPTCHA-SHARED-WIDGET-DOMAINS-RECONCILE-FIX-0119R1", "status": "OK", "rc": 0, "host": "pve01", "mode": "read-only", - "component": "homelab-moldova-base-release-auth", - "started_at_utc": "2026-09-23T22:17:37Z", - "finished_at_utc": "2026-09-23T22:17:37Z", + "component": "forum-captcha", + "started_at_utc": "2026-09-23T22:18:48Z", + "finished_at_utc": "2026-09-23T22:18:50Z", "reference_register_checked": true, "reference_sha256": "5a3d8e5154c41cb582a4e0aca68090be1f0138918bf82131a948df326f9d8d66", "error_register_checked": true, "error_register_sha256": "3b09a553ec0f527ed3afeed4753f52a74ee3036045bbfb3c685e9f8af4ba7ba0", - "command_sha256": "f3fe1cb2e034d22c5917b5946f86b1c8ea517c0abd0a92891868a481e2aa5da7", + "command_sha256": "ba8787e7b885617e285e2a52c96350817e90ca7b882b71d3424f84a02ab601fb", "duplicate_failed_command_blocked": false, "block_reason": null, "execution_started": true, @@ -30,9 +30,9 @@ "secrets_included": false, "private_addresses_included": false, "raw_evidence_retained_locally": true, - "raw_evidence_sha256": "add93d5fb111ea9426df8d172911f8506ad95531ae42be7e0c338901a61b8608", - "sanitized_output_sha256": "e60b4493bf44bffbb9ab84838090dbefdb74cfb16219ea4cc0d3a9e834e59ae4", + "raw_evidence_sha256": "12bc50352aad0e5bf2e5d55491007dea4047ffde164f8f754a4b5b13d30a4dfe", + "sanitized_output_sha256": "12bc50352aad0e5bf2e5d55491007dea4047ffde164f8f754a4b5b13d30a4dfe", "output_truncated_in_json": false, "full_sanitized_output_url": "https://git.gram1.ru/.well-known/homelab-runtime/latest.txt", - "output": "WORKERS2_MOLDOVA_BASE_RELEASE_AUTH_BEGIN=1\nCOMMAND_ID=SUPPORT-260924-HOMELAB-MOLDOVA-BASE-RELEASE-AUTH-1147R1\nMODE=read-only\nCOMPONENT=homelab-moldova-base-release-auth\nMUTATION_BOUNDARY=NONE;ENTRY504_PROVENANCE_AND_LITERAL_BASE_RELEASE_STATIC_INSPECTION_ONLY;NO_MOLDOVA_CONNECTION;NO_REMOTE_SSH;NO_PROVIDER_CALL;NO_SYSTEMD_ACTION;NO_BACKUP_RUN;NO_BACKUP_MUTATION;NO_TARGET_POLICY_MUTATION;NO_NETBIRD_MUTATION;NO_MONITORING_MUTATION;NO_GIT_WRITE;NO_SECRET_VALUE_READOUT\nPREDECESSOR_1146=PASS_MOLDOVA_1146_RELEASE_AUTH_EXACT_COLLECTED\nPREDECESSOR_1146_CLASS=VPS_MD_HANDLER_NOT_LOCATED\nCARRIER_BASELINE=KNOWN_GOOD_1137_BYTE_SEMANTICS\nREFERENCE_SHA256=5a3d8e5154c41cb582a4e0aca68090be1f0138918bf82131a948df326f9d8d66\nERROR_REGISTER_SHA256=3b09a553ec0f527ed3afeed4753f52a74ee3036045bbfb3c685e9f8af4ba7ba0\nRUNNER_SHA256=b248a4c32c9cc64e5747e7dce6c7fc0a23f5124a77c71ce72e27a81aceae9d2d\nTARGETS_SHA256=5cba5f1e14c65211c9fab6d11594c19279ab68369b4db3f7283708c368348288\nENTRY504_SHA256=ca6876fa9c3b1465b045344a8ad46962de16972c7dadc4ac6257c7d225047793\nMD_RELAY_COUNT=1\nMD_RELAY_ENABLED=false\nENTRY504_PROVENANCE_BLOCK_BEGIN=1\nENTRY504|line=20|text= while True:\nENTRY504|line=21|text= b=f.read(1<<20)\nENTRY504|line=22|text= if not b: break\nENTRY504|line=23|text= h.update(b)\nENTRY504|line=24|text= return h.hexdigest()\nENTRY504|line=25|text=\nENTRY504|line=26|text=def _verify_base():\nENTRY504|line=27|text= s=os.lstat(BASE)\nENTRY504|line=28|text= if not stat.S_ISDIR(s.st_mode) or stat.S_ISLNK(s.st_mode) or s.st_uid!=0 or s.st_gid!=0:\nENTRY504|line=29|text= raise RuntimeError('BASE_RELEASE_ROOT')\nENTRY504|line=30|text= for rel,want in BASE_EXPECTED.items():\nENTRY504|line=31|text= p=BASE/rel; st=os.lstat(p)\nENTRY504|line=32|text= if not stat.S_ISREG(st.st_mode) or stat.S_ISLNK(st.st_mode) or st.st_uid!=0 or st.st_gid!=0 or _sha(p)!=want:\nENTRY504|line=33|text= raise RuntimeError('BASE_RELEASE_FILE_'+rel)\nENTRY504|line=34|text=\nENTRY504|line=35|text=class _GlobalsProxy:\nENTRY504|line=36|text= __slots__=('_g',)\nENTRY504|line=37|text= def __init__(self,g): object.__setattr__(self,'_g',g)\nENTRY504|line=38|text= def __getattr__(self,n):\nENTRY504|line=39|text= try: return self._g[n]\nENTRY504|line=40|text= except KeyError as e: raise AttributeError(n) from e\nENTRY504|line=41|text= def __setattr__(self,n,v): self._g[n]=v\nENTRY504|line=42|text=\nENTRY504|line=43|text=def _wire(wrapper,lxc_adapter,qemu_adapter,scheduler_adapter,base=BASE):\nENTRY504|line=44|text= legacy_path=(pathlib.Path(base)/'legacy_engine.py').resolve()\nENTRY504|line=45|text= found={}\nENTRY504|line=46|text= for value in vars(wrapper).values():\nENTRY504|line=47|text= if isinstance(value,types.ModuleType):\nENTRY504|line=48|text= g=vars(value); raw=g.get('__file__')\nENTRY504|line=49|text= elif isinstance(value,types.FunctionType):\nENTRY504|line=50|text= g=value.__globals__; raw=g.get('__file__')\nENTRY504|line=51|text= else:\nENTRY504|line=52|text= continue\nENTRY504|line=53|text= if not isinstance(raw,str):\nENTRY504|line=54|text= continue\nENTRY504|line=55|text= try: gp=pathlib.Path(raw).resolve()\nENTRY504|line=56|text= except Exception: continue\nENTRY504|line=57|text= if gp==legacy_path:\nENTRY504|line=58|text= found[id(g)]=g\nENTRY504|line=59|text= if len(found)!=1:\nENTRY504|line=60|text= raise RuntimeError('LEGACY_PROVENANCE_CARDINALITY')\nENTRY504|line=61|text= g=next(iter(found.values()))\nENTRY504|line=62|text= required=('capture_pve_guest','backup_target','load_targets','cluster_inventory','ssh_remote',\nENTRY504|line=63|text= '_default_unprivileged_lxc','mapped_scratch_probe','_stream_argv_to_local','run',\nENTRY504|line=64|text= 'choose_due_target','write_state','epoch')\nENTRY504|line=65|text= if any(not callable(g.get(n)) for n in required):\nENTRY504|line=66|text= raise RuntimeError('LEGACY_PROVENANCE_API')\nENTRY504|line=67|text= original=g['capture_pve_guest']\nENTRY504|line=68|text= proxy=_GlobalsProxy(g)\nENTRY504|line=69|text= if not isinstance(g.get('SCP'),str) or not g['SCP'].startswith('/'):\nENTRY504|line=70|text= raise RuntimeError('LEGACY_SCP_API')\nENTRY504|line=71|text= qemu_patched=qemu_adapter.bind(proxy,original)\nENTRY504|line=72|text= if not callable(qemu_patched):\nENTRY504|line=73|text= raise RuntimeError('QEMU_ADAPTER_BIND_RESULT')\nENTRY504|line=74|text= patched=lxc_adapter.bind(proxy,qemu_patched)\nENTRY504|line=75|text= if not callable(patched):\nENTRY504|line=76|text= raise RuntimeError('LXC_ADAPTER_BIND_RESULT')\nENTRY504|line=77|text= g['capture_pve_guest']=patched\nENTRY504|line=78|text= scheduler_wired=scheduler_adapter.bind(proxy,g['choose_due_target'],g['write_state'])\nENTRY504|line=79|text= if g.get('choose_due_target') is not scheduler_wired['choose_due_target'] or g.get('write_state') is not scheduler_wired['write_state']:\nENTRY504|line=80|text= raise RuntimeError('SCHEDULER_ADAPTER_BIND_RESULT')\nENTRY504|line=81|text= return {'legacy_globals':g,'legacy':proxy,'original_capture':original,'qemu_patched_capture':qemu_patched,'patched_capture':patched,'scheduler_wired':scheduler_wired}\nENTRY504|line=82|text=\nENTRY504|line=83|text=def _load():\nENTRY504|line=84|text= _verify_base()\nENTRY504|line=85|text= sys.dont_write_bytecode=True\nENTRY504|line=86|text= sys.path.insert(0,str(BASE))\nENTRY504|line=87|text= wrapper=importlib.import_module('wrapper')\nENTRY504|line=88|text= # Baseline accepted wrapper selftest stays the compatibility gate; no full old legacy selftest is reintroduced.\nENTRY504|line=89|text= wrapper.selftest()\nENTRY504|line=90|text= sys.path.insert(0,str(HERE))\nENTRY504|line=91|text= lxc_adapter=importlib.import_module('lxc_adapter')\nENTRY504|line=92|text= qemu_adapter=importlib.import_module('qemu_adapter')\nENTRY504|line=93|text= scheduler_adapter=importlib.import_module('scheduler_adapter')\nENTRY504|line=94|text= if _sha(HERE/'lxc_adapter.py')!='3fa7d194010d76f7c8bc95035ae8876262e9337903e310f45f45df88c266dc8d':\nENTRY504|line=95|text= raise RuntimeError('LXC_502_BYTE_IDENTITY')\nENTRY504|line=96|text= wired=_wire(wrapper,lxc_adapter,qemu_adapter,scheduler_adapter,BASE)\nENTRY504|line=97|text= return wrapper,lxc_adapter,qemu_adapter,scheduler_adapter,wired\nENTRY504|line=98|text=\nENTRY504|line=99|text=def _target(wrapper,lid):\nENTRY504|line=100|text= rows=wrapper.load_targets(); hits=[x for x in rows if x.get('logical_id')==lid]\nENTRY504|line=101|text= if len(hits)!=1: raise RuntimeError('TARGET_CARDINALITY')\nENTRY504|line=102|text= return hits[0]\nENTRY504|line=103|text=\nENTRY504|line=104|text=def selftest():\nENTRY504|line=105|text= wrapper,lxc_adapter,qemu_adapter,scheduler_adapter,wired=_load()\nENTRY504|line=106|text= if wired['legacy_globals'].get('capture_pve_guest') is not wired['patched_capture']:\nENTRY504|line=107|text= raise RuntimeError('PATCH_BINDING')\nENTRY504|line=108|text= if wired['legacy_globals'].get('choose_due_target') is not wired['scheduler_wired']['choose_due_target']:\nENTRY504|line=109|text= raise RuntimeError('SCHEDULER_CHOOSE_BINDING')\nENTRY504|line=110|text= if wired['legacy_globals'].get('write_state') is not wired['scheduler_wired']['write_state']:\nENTRY504|line=111|text= raise RuntimeError('SCHEDULER_WRITE_BINDING')\nENTRY504|line=112|text= if [scheduler_adapter.backoff_seconds(n) for n in range(1,7)]!=[1800,3600,7200,14400,21600,21600]:\nENTRY504|line=113|text= raise RuntimeError('SCHEDULER_BACKOFF_CONTRACT')\nENTRY504|line=114|text= if not callable(wrapper.backup_target) or not callable(wrapper.main) or not callable(wrapper.load_targets):\nENTRY504|line=115|text= raise RuntimeError('WRAPPER_PUBLIC_API')\nENTRY504|line=116|text= print('SELFTEST_OVERLAY504=PASS',flush=True)\nENTRY504|line=117|text= return 0\nENTRY504|line=118|text=\nENTRY504|line=119|text=def main(argv=None):\nENTRY504|line=120|text= argv=list(sys.argv[1:] if argv is None else argv)\nENTRY504_PROVENANCE_BLOCK_END=1\nENTRY504_RELEASE_LITERAL_COUNT=1\nENTRY504_RELEASE_LITERAL=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1\nBASE_CANDIDATE_COUNT=1\nBASE_CANDIDATE|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1|kind=dir|mode=500|uid=0|gid=0\nBASE_PY_FILE_COUNT=21\nBASE_FILE|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/adapter.py|bytes=2989|sha256=18eb29cef165371b48bcb78e1a456e1939e6eff253787218133162cc89ab9fa3|mode=500|uid=0|gid=0\nBASE_FILE|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/guest266.py|bytes=7385|sha256=86433bc424d5fa5017c20f7a237c82874b02357a3283660d8a6b0039ef2bc868|mode=400|uid=0|gid=0\nBASE_FILE|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/large264.py|bytes=16514|sha256=5cc4641fc35e4dd554495a36146da7dbbb7f142d1450e04cc5d3c89678b8497a|mode=400|uid=0|gid=0\nBASE_FILE|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/nodeplan263.py|bytes=4603|sha256=db5c16ee24d25aa1694530332d3190ab7cc5d4f8eb69067d025e27ad5c995060|mode=400|uid=0|gid=0\nBASE_FILE|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/pve265.py|bytes=12888|sha256=cd2f634414a8e38a3789015aeca5a9b47821f77d7d1fa61dcdbdc866172963be|mode=400|uid=0|gid=0\nBASE_FILE|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/pve270.py|bytes=19595|sha256=97015277a4f3f8129c0fd11eb70c06293026366f1f8232a97feeed513a2dbcef|mode=400|uid=0|gid=0\nBASE_FILE|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/pve282.py|bytes=6195|sha256=73c6cb7773eb8e82d1e7ed5ab4733b4afb88f74ae69c09125d0378d6e480e012|mode=400|uid=0|gid=0\nBASE_FILE|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/restore262.py|bytes=14701|sha256=56d1b961c8d5654d5e2ee2755268db46bd2202bca11e69788febf991cfaa79f1|mode=400|uid=0|gid=0\nBASE_FILE|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/stream263.py|bytes=8831|sha256=a83d03a86b93a855716658652dd073fcb281cd387fc6200fe4bc2a922f39ea16|mode=400|uid=0|gid=0\nBASE_FILE|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/test_guest266.py|bytes=10752|sha256=460b950924a475ce0a28dc759512e7dbe2323ae2cc42f7ef4c6181710a2a13da|mode=400|uid=0|gid=0\nBASE_FILE|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/test_perl270.py|bytes=5441|sha256=50fc10865078a7b9b255dc627059824184441a47c4ede52e23f24c1186e3512f|mode=400|uid=0|gid=0\nBASE_FILE|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/test_producer282.py|bytes=12215|sha256=a67d927cc428935791f9eecc3acf571d089f80761162f4589b7d684fba6b8af7|mode=400|uid=0|gid=0\nBASE_FILE|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/test_pve265.py|bytes=11781|sha256=16a9407d01bf73db687ef38684c59205fc2e6a9223cd9cee6ec6e8dde9d38707|mode=400|uid=0|gid=0\nBASE_FILE|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/test_pve270.py|bytes=12077|sha256=9fccd20b490613fdd5b14b4105cf0d9fd744f204e1ed1d6893e54c9f69bf6fb4|mode=400|uid=0|gid=0\nBASE_FILE|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/test_pve282.py|bytes=5785|sha256=1056407aab76e17181b05e043cb42c086058545a8e0027689ab4e03eee6531c4|mode=400|uid=0|gid=0\nBASE_FILE|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/transport261.py|bytes=15364|sha256=b9b92f957bf441cb5d1702f4881897328e6c0338f8c3f6d41d27d9a349b37688|mode=400|uid=0|gid=0\nBASE_FILE|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/v2core.py|bytes=11755|sha256=e285bf9a5678d07dcbb90261f9f93e7826ff524e04cfec780bf5a3c4b0dc29f2|mode=400|uid=0|gid=0\nBASE_FILE|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/ct112_handler.py|bytes=10955|sha256=8cac6e627d5e7abe5d3163cdd24911995a1e3f45c776f9373c2aa68784f5ec6c|mode=400|uid=0|gid=0\nBASE_FILE|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|bytes=59719|sha256=b6e79f087b9f1d21dac4f77a7b46b743299bbb85bc716e80d2ea67c2e038bcd7|mode=400|uid=0|gid=0\nBASE_FILE|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/representative_driver.py|bytes=4214|sha256=b3050a8f95e6d9826a4f0786cd3ca32012b6ab10486d10e4981b50b8a0affff2|mode=400|uid=0|gid=0\nBASE_FILE|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/wrapper.py|bytes=5055|sha256=2b5eb4fc0f6f466a0d3e5a007af987092b6c17baa3ff9241a6afc2c2e97df1d5|mode=400|uid=0|gid=0\nBASE_AUTH_SNIPPETS_BEGIN=1\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/large264.py|line=12|text=import subprocess\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/large264.py|line=27|text= p=subprocess.Popen(argv,stdin=subprocess.DEVNULL,stdout=subprocess.PIPE,stderr=subprocess.PIPE,\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/large264.py|line=41|text= except subprocess.TimeoutExpired:raise t.Stop('STREAM_TIMEOUT') from None\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/pve265.py|line=13|text=import subprocess\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/pve265.py|line=144|text= except subprocess.TimeoutExpired:\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/pve265.py|line=160|text= p=subprocess.Popen(argv,stdin=subprocess.DEVNULL,stdout=subprocess.PIPE,stderr=subprocess.PIPE,\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/pve265.py|line=176|text= except subprocess.TimeoutExpired:raise PVEError('PRODUCER_TIMEOUT') from None\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/pve270.py|line=6|text=import hashlib,json,os,re,secrets,selectors,signal,stat,subprocess,sys,time\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/pve270.py|line=110|text= p=subprocess.Popen(['/proc/self/fd/'+str(self.fd),*args],pass_fds=(self.fd,),\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/pve270.py|line=111|text= stdin=subprocess.DEVNULL,stdout=subprocess.PIPE,stderr=subprocess.PIPE,\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/pve270.py|line=124|text= except subprocess.TimeoutExpired:raise old.PVEError('PERL_TIMEOUT') from None\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/pve270.py|line=217|text= p=subprocess.run([sys.executable,'-I','-B','-c',code,*args],stdin=subprocess.DEVNULL,stdout=subprocess.DEVNULL,\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/pve270.py|line=218|text= stderr=subprocess.DEVNULL,env=dict(old.ENV),user=u,group=g,extra_groups=[],close_fds=True,timeout=15)\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/pve270.py|line=247|text= p=subprocess.Popen(argv,stdin=subprocess.DEVNULL,stdout=subprocess.PIPE,stderr=subprocess.PIPE,\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/pve270.py|line=266|text= except subprocess.TimeoutExpired:raise old.PVEError('PRODUCER_TIMEOUT') from None\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/test_producer282.py|line=2|text=import copy,hashlib,json,os,stat,subprocess,sys,tempfile,time,unittest\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/test_producer282.py|line=10|text= return subprocess.run([sys.executable,'-I','-B','-c',code,*map(str,args)],user=uid,group=gid,extra_groups=[],stdin=subprocess.DEVNULL,stdout=subprocess.PIPE,stderr=subprocess.PIPE,timeout=10)\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/test_producer282.py|line=134|text= \"\"\"Actual subprocess + tar/zstd, synthetic data; never runs vzdump on a guest.\"\"\"\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/test_producer282.py|line=152|text= code='''import os,sys,subprocess,shutil\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/test_producer282.py|line=158|text=a=subprocess.run(['/usr/bin/tar','-cf','-','-C',str(p),'./etc/vzdump/pct.conf'],user=100000,group=100000,extra_groups=[],stdout=subprocess.PIPE,stderr=subprocess.PIPE)\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/test_producer282.py|line=161|text=z=subprocess.run(['/usr/bin/zstd','-q','-c'],input=a.stdout,stdout=subprocess.PIPE,stderr=subprocess.PIPE)\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/test_producer282.py|line=193|text= def crc(p):return subprocess.run(['/usr/bin/zstd','--test','--quiet',str(p)],stdout=subprocess.DEVNULL,stderr=subprocess.DEVNULL).returncode==0\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/test_pve265.py|line=1|text=\"\"\"Synthetic PVE fixtures; subprocess tests never execute real PVE tools.\"\"\"\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/test_pve270.py|line=2|text=import copy,hashlib,json,os,stat,subprocess,sys,tempfile,time,unittest\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/test_pve270.py|line=10|text= return subprocess.run([sys.executable,'-I','-B','-c',code,*map(str,args)],user=uid,group=gid,extra_groups=[],stdin=subprocess.DEVNULL,stdout=subprocess.PIPE,stderr=subprocess.PIPE,timeout=10)\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/test_pve270.py|line=134|text= \"\"\"Actual subprocess + tar/zstd, synthetic data; never runs vzdump on a guest.\"\"\"\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/test_pve270.py|line=152|text= code='''import os,sys,subprocess,shutil\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/test_pve270.py|line=157|text=a=subprocess.run(['/usr/bin/tar','-cf','-','-C',str(p),'./etc/vzdump/pct.conf'],user=100000,group=100000,extra_groups=[],stdout=subprocess.PIPE,stderr=subprocess.PIPE)\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/test_pve270.py|line=160|text=z=subprocess.run(['/usr/bin/zstd','-q','-c'],input=a.stdout,stdout=subprocess.PIPE,stderr=subprocess.PIPE)\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/test_pve270.py|line=192|text= def crc(p):return subprocess.run(['/usr/bin/zstd','--test','--quiet',str(p)],stdout=subprocess.DEVNULL,stderr=subprocess.DEVNULL).returncode==0\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/test_pve282.py|line=2|text=import copy, json, unittest, tempfile, time, subprocess\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/transport261.py|line=15|text=import subprocess\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/transport261.py|line=149|text= p=subprocess.Popen(argv,stdin=subprocess.DEVNULL,stdout=subprocess.PIPE,stderr=subprocess.PIPE,\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/transport261.py|line=163|text= except subprocess.TimeoutExpired:raise Stop('PROCESS_TIMEOUT') from None\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/ct112_handler.py|line=3|text=import hashlib,json,os,secrets,shutil,stat,subprocess,sys,time\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/ct112_handler.py|line=26|text= p=subprocess.run(argv,stdout=subprocess.PIPE,stderr=subprocess.PIPE,timeout=45,check=False,env=ENV)\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/ct112_handler.py|line=166|text= p=subprocess.run([str(ZSTD),'--test','--quiet','--',str(path)],stdout=subprocess.DEVNULL,stderr=subprocess.DEVNULL,timeout=3600,check=False,env=ENV)\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=2|text=import ast, base64, datetime as dt, hashlib, io, json, os, pathlib, random, re, select, shlex, shutil, stat, subprocess, sys, tarfile, tempfile, time\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=12|text=SSH=shutil.which('ssh') or '/usr/bin/ssh'\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=13|text=SCP=shutil.which('scp') or '/usr/bin/scp'\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=14|text=SSH_KEYSCAN=shutil.which('ssh-keyscan') or '/usr/bin/ssh-keyscan'\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=15|text=SSHPASS=shutil.which('sshpass') or '/usr/bin/sshpass'\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=16|text=OPERATOR_KEY=pathlib.Path('[SENSITIVE_PATH]\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=158|text= {'logical_id':'md-relay','scope':'vps','type':'vps_md','host':'[ADDR]','port':22,'user':'ops','hostkey_fingerprint':'SHA256:DXJGwunC5tEVvnC3nRxyayTN8FXmyJcVgf+Oa6udQsY','due_seconds':d,'enabled':True,'green1':True},\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=159|text= {'logical_id':'spb-ingress','scope':'vps','type':'vps_spb','host':'[ADDR]','port':22,'user':'edgeadmin','hostkey_fingerprint':'SHA256:Ef+HsZb8lIXoadvYBXuGKIL3H7NyJMmf9EYCoJRoSv8','due_seconds':d,'enabled':True,'green1':True},\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=160|text= {'logical_id':'us-netbird','scope':'vps','type':'vps_us','host':'[ADDR]','port':22,'user':'root','hostkey_fingerprint':'SHA256:805Q9eh/fePUci42dfY58I6q0lFiiPHn0/M+mckypmE','due_seconds':d,'enabled':True,'green1':True},\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=191|text= p=subprocess.run(argv,input=input,stdout=subprocess.PIPE,stderr=subprocess.PIPE,timeout=timeout,env=env)\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=192|text= except subprocess.TimeoutExpired: raise BackupError('COMMAND_TIMEOUT')\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=199|text= try:p=subprocess.run(argv,input=input,stdout=f,stderr=subprocess.PIPE,timeout=timeout)\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=200|text= except subprocess.TimeoutExpired: raise BackupError('STREAM_COMMAND_TIMEOUT')\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=206|text= src=subprocess.Popen(argv,stdin=subprocess.PIPE if input is not None else None,stdout=subprocess.PIPE,stderr=subprocess.PIPE)\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=207|text= z=subprocess.Popen([zstd,'-T0','-3','-q','-o',str(out_path)],stdin=src.stdout,stdout=subprocess.DEVNULL,stderr=subprocess.PIPE)\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=213|text= except subprocess.TimeoutExpired:\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=216|text= except subprocess.TimeoutExpired:\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=236|text=def load_targets():\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=289|text= p=subprocess.Popen(argv,stdin=subprocess.PIPE,stdout=subprocess.PIPE,stderr=subprocess.PIPE)\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=311|text= with subprocess.Popen(rclone_base_args()+['cat',remote_path,'--contimeout','20s','--timeout','5m'],stdout=subprocess.PIPE,stderr=subprocess.PIPE) as p:\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=365|text= with subprocess.Popen(rclone_base_args()+['cat',base+'/parts/'+part['name'],'--contimeout','20s','--timeout','5m'],stdout=subprocess.PIPE,stderr=subprocess.PIPE) as p:\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=379|text= p1=subprocess.Popen([zstd,'-dc',str(path)],stdout=subprocess.PIPE,stderr=subprocess.PIPE)\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=380|text= p2=subprocess.Popen([tar,'-tf','-'],stdin=p1.stdout,stdout=subprocess.PIPE,stderr=subprocess.PIPE)\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=389|text= if typ in ('pve_node_bundle','vps_md','vps_spb','vps_us','router'):\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=399|text= p1=subprocess.Popen([zstd,'-dc',str(path)],stdout=subprocess.PIPE,stderr=subprocess.PIPE)\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=400|text= p2=subprocess.Popen([tar,'-xOf','-','db.sql'],stdin=p1.stdout,stdout=subprocess.PIPE,stderr=subprocess.PIPE)\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=405|text=def cluster_inventory():\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=410|text=def ssh_remote(node,argv,input=None,timeout=1800,check=True):\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=413|text= return run([SSH,'-o','BatchMode=yes','-o','StrictHostKeyChecking=yes','root@'+node,cmd],input=input,timeout=timeout,check=check)\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=430|text= p=subprocess.run([sys.executable,'-B','-c',_HISTORICAL_HELPER,'import_all'],stdout=subprocess.PIPE,stderr=subprocess.PIPE,env=env,cwd='/',timeout=30)\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=441|text= p=subprocess.run([sys.executable,'-B','-c',_HISTORICAL_HELPER,'dump_argv',str(GUEST266)],input=canonical_json(req),stdout=subprocess.PIPE,stderr=subprocess.PIPE,env=env,cwd='/',timeout=30)\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=442|text= if p.returncode!=0: raise BackupError('GUEST266_SUBPROCESS_FAILED',f'rc_{p.returncode}_stderr_sha256_{hashlib.sha256(p.stderr).hexdigest()}')\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=444|text= except Exception: raise BackupError('GUEST266_SUBPROCESS_JSON')\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=476|text= q=ssh_remote(node,['pct','config',str(vmid)],timeout=30)\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=484|text= q=ssh_remote(node,pos,timeout=30,check=False)\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=492|text= cmd=[SSH,'-o','BatchMode=yes','-o','StrictHostKeyChecking=yes','root@'+node,remote]\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=494|text= try:p=subprocess.run(cmd,stdout=f,stderr=subprocess.PIPE,timeout=timeout)\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=495|text= except subprocess.TimeoutExpired:\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=514|text= ssh_remote(node,['install','-d','-m','710','-o','0','-g','100000',scratch],timeout=30)\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=515|text= q=ssh_remote(node,['stat','-c','%a:%u:%g',scratch],timeout=30)\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=524|text= ssh_remote(node,['rm','-rf','--',scratch],timeout=60,check=False);raise\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=529|text= ssh_remote(node,['install','-d','-m','700','-o','0','-g','0',srcdir],timeout=30)\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=531|text= ssh_remote(node,['vzdump',str(vmid),'--mode','snapshot','--compress','zstd','--dumpdir',srcdir],timeout=7200)\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=532|text= p=ssh_remote(node,['find',srcdir,'-maxdepth','1','-type','f','-printf','%p\\n'],timeout=30)\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=537|text= else: run([SCP,'-q','-o','BatchMode=yes','-o','StrictHostKeyChecking=yes','root@'+node+':'+src,str(local)],timeout=7200)\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=540|text= ssh_remote(node,['rm','-rf','--',srcdir],timeout=60,check=False);raise\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=545|text= p=run(['ssh-keygen','-lf',str(OPERATOR_KEY),'-E','sha256'],timeout=30)\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=552|text= p=run([SSH_KEYSCAN,'-T','8','-p',str(port),'-t','ed25519',host],timeout=15)\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=557|text= if len(f)<3 or f[1]!='ssh-ed25519': continue\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=568|text= if t['type'] not in ('vps_md','vps_spb','vps_us'): continue\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=569|text= lines.append(scan_hostkey_line(t['host'],t['port'],t['hostkey_fingerprint']))\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=572|text=def vps_ssh_argv(t, *, use_agent=False):\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=573|text= a=[SSH,'-p',str(t['port']),'-o','BatchMode=yes','-o','StrictHostKeyChecking=yes','-o','UserKnownHostsFile='+str(VPS_KNOWN_HOSTS),'-o','ConnectTimeout=15']\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=575|text= a += ['-o','IdentitiesOnly=no']\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=577|text= a += ['-i',str(OPERATOR_KEY),'-o','IdentitiesOnly=yes','-o','IdentityAgent=none']\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=583|text= return run(vps_ssh_argv(t,use_agent=use_agent)+[remote],input=input,timeout=timeout,check=check)\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=588|text= pub=run(['ssh-keygen','-y','-f',str(OPERATOR_KEY)],timeout=30).stdout.strip()+b'\\n'\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=591|text= if t['type'] not in ('vps_md','vps_spb'): continue\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=595|text= if not os.environ.get('SSH_AUTH_SOCK'): raise BackupError('FORWARDED_AGENT_REQUIRED_FOR_BOOTSTRAP')\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=596|text= script=b\"umask 077; mkdir -p \\\"$HOME/.ssh\\\"; chmod 700 \\\"$HOME/.ssh\\\"; touch \\\"$HOME/.ssh/authorized_keys\\\"; chmod 600 \\\"$HOME/.ssh/authorized_keys\\\"; IFS= read -r k; if grep -qF -- \\\"$k\\\" \\\"$HOME/.ssh/authorized_keys\\\"; then printf 'ALREADY\\\\n'; else printf '%s\\\\n' \\\"$k\\\" >> \\\"$HOME/.ssh/authorized_keys\\\"; printf 'ADDED\\\\n'; fi\"\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=612|text=def capture_vps_md(t,gen,work):\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=617|text= argv=vps_ssh_argv(t,use_agent=False)+[remote]\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=621|text= return payload,{'generation':gen,'source_class':'vps_md','consistency':'FILESET_PRE_POST_HASH_STABILITY','hashset_sha256':pre},None\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=627|text= argv=vps_ssh_argv(t,use_agent=False)+[remote]\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=664|text= argv=vps_ssh_argv(t,use_agent=False)+[remote]\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=732|text= pat=re.compile(r\"\\bsshpass\\b[^\\n]*?(?:^|\\s)-f\\s+(\\\"[^\\\"]*\\\"|'[^']*'|[^\\s;]+)\")\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=756|text= roots=[pathlib.Path('/etc'),pathlib.Path('[SENSITIVE_PATH]'),pathlib.Path('/var/lib/homelab-backup'),pathlib.Path('/var/lib/homelab-backup-v2')]\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=784|text= # sshpass requires password prompting to remain enabled; batch mode must stay off.\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=785|text= # The proven SSHPASS_FILE credential path otherwise cannot be used.\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=786|text= argv=[SSHPASS,'-f',str(pf),SSH,'-p','2222','-o','BatchMode=no','-o','NumberOfPasswordPrompts=1','-o','StrictHostKeyChecking=yes','-o','UserKnownHostsFile='+str(ROUTER_KNOWN_HOSTS),'-o','ConnectTimeout=15','routerbackup@'+target,'show running-config']\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=806|text= p=ssh_remote(node,args,input=input_bytes,timeout=timeout+60)\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=818|text= return f'''import hashlib,json,os,pathlib,select,shutil,subprocess,tempfile,time\\nroot={root!r}; db={db!r}; strategy={strategy!r}; token=[REDACTED] not root.startswith('/var/www/forums/') or not db.startswith('xf_'): raise SystemExit(20)\\ntmp=pathlib.Path('/tmp/homelab-v2-xf-'+token); tmp.mkdir(mode=0o700,exist_ok=False)\\ndump=tmp/'db.sql'; out=pathlib.Path('/tmp/homelab-v2-xf-'+token+'.tar.zst')\\nlock=None\\ntry:\\n if strategy=='global_read_lock':\\n lock=subprocess.Popen(['mariadb','--user=root','--batch','--skip-column-names','--unbuffered'],stdin=subprocess.PIPE,stdout=subprocess.PIPE,stderr=subprocess.DEVNULL,text=True,bufsize=1)\\n lock.stdin.write(\"FLUSH TABLES WITH READ LOCK; SELECT 'LOCKED';\\\\n\"); lock.stdin.flush()\\n rr,_,_=select.select([lock.stdout],[],[],5)\\n if not rr or lock.stdout.readline().strip()!='LOCKED': raise RuntimeError('LOCK_ACQUIRE')\\n dump_timeout=15\\n else: dump_timeout=180\\n with open(dump,'wb') as f:\\n argv=['mariadb-dump','--user=ro...[TRUNCATED]\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=851|text= if OPERATOR_KEY.exists(): p.append('root/.ssh/id_ed25519_netbird_operator')\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=852|text= if pathlib.Path(str(OPERATOR_KEY)+'.pub').exists(): p.append('root/.ssh/id_ed25519_netbird_operator.pub')\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=859|text= else: ok=(ssh_remote(node,['test','-e','/'+rel],timeout=20,check=False).returncode==0)\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=868|text= pipe_to_zstd([SSH,'-o','BatchMode=yes','-o','StrictHostKeyChecking=yes','root@'+node,remote],payload,timeout=600)\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=875|text= if typ=='vps_md': return capture_vps_md(t,gen,work)\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=885|text= ssh_remote(cleanup['node'],['rm','-rf','--',cleanup['path']],timeout=60)\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=903|text=def backup_target(t,green_verify=False):\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=991|text= assert {t['logical_id'] for t in default_targets() if t.get('green1')}=={'ct112','node-pve01','md-relay','spb-ingress','us-netbird','router-netcraze','xf-codevipe','xf-zakrutim','control-bootstrap-v2'}\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/representative_driver.py|line=17|text= 'md-relay',\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/wrapper.py|line=7|text=LEGACY_IDS=('node-pve01','md-relay','spb-ingress','us-netbird','router-netcraze','xf-codevipe','xf-zakrutim','control-bootstrap-v2')\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/wrapper.py|line=37|text=def load_targets():\nBASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/wrapper.py|line=40|text=def backup_target(t,green_verify=False):\nBASE_AUTH_SNIPPET_COUNT=121\nBASE_AUTH_SNIPPETS_END=1\nBASE_KEY_FUNCTIONS_BEGIN=1\nFUNCTION_BEGIN|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|name=load_targets|line=236\ndef load_targets():\n j=json.loads(TARGETS_PATH.read_text())\n if j.get('schema')!=1 or not isinstance(j.get('targets'),list): raise BackupError('TARGET_MANIFEST_SCHEMA')\n ids=[x.get('logical_id') for x in j['targets']]\n if len(ids)!=len(set(ids)): raise BackupError('TARGET_MANIFEST_DUPLICATE')\n return j['targets']\nFUNCTION_END|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|name=load_targets\nFUNCTION_BEGIN|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|name=cluster_inventory|line=405\ndef cluster_inventory():\n p=run(['pvesh','get','/cluster/resources','--type','vm','--output-format','json'],timeout=60)\n try:return json.loads(p.stdout)\n except Exception: raise BackupError('PVE_INVENTORY_JSON')\nFUNCTION_END|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|name=cluster_inventory\nFUNCTION_BEGIN|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|name=ssh_remote|line=410\ndef ssh_remote(node,argv,input=None,timeout=1800,check=True):\n cmd='PATH=/usr/sbin:/usr/bin:/sbin:/bin; export PATH; exec '+shlex.join([str(x) for x in argv])\n if node=='pve01': return run([str(x) for x in argv],input=input,timeout=timeout,check=check)\n return run([SSH,'-o','BatchMode=yes','-o','StrictHostKeyChecking=yes','root@'+node,cmd],input=input,timeout=timeout,check=check)\nFUNCTION_END|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|name=ssh_remote\nFUNCTION_BEGIN|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|name=backup_target|line=903\ndef backup_target(t,green_verify=False):\n if t['type']=='control': return control_verify(t)\n gen=generation_id(t['logical_id']); work=ensure_dir(WORK_ROOT/gen,0o700)\n payload=None; cleanup=None; sealed=False\n try:\n payload,meta,cleanup=capture_target(t,gen,work); meta['generation']=gen\n manifest,msha,alloc=upload_and_seal(t,payload,meta); sealed=True\n # SEALED-first cleanup invariant: only now remove source/local payload.\n try:\n if pathlib.Path(payload).exists(): pathlib.Path(payload).unlink()\n finally: cleanup_source(cleanup)\n if green_verify:\n a=work/'verify-a.bin'; b=work/'verify-b.bin'\n ha=reconstruct_replica(manifest,manifest['replicas'][0],a); validate_payload(t,a)\n hb=reconstruct_replica(manifest,manifest['replicas'][1],b); validate_payload(t,b)\n if ha!=hb or ha!=manifest['payload_sha256']: raise BackupError('GREEN_REPLICA_COMPARE')\n a.unlink(missing_ok=True); b.unlink(missing_ok=True)\n shutil.rmtree(work,ignore_errors=True)\n write_state(t['logical_id'],'SUCCESS',generation=gen,payload_sha256=manifest['payload_sha256'],manifest_sha256=msha,replicas=manifest['replicas'],green_verified=bool(green_verify),last_success_epoch=epoch())\n return {'logical_id':t['logical_id'],'generation':gen,'result':'SUCCESS','payload_bytes':manifest['payload_bytes'],'replicas':2,'allocator_healthy':alloc['healthy']}\n except BackupError as e:\n result='PARTIAL_VALID' if sealed else 'FAIL'\n write_state(t['logical_id'],result,generation=gen,reason=e.code,sealed=sealed)\n raise\nFUNCTION_END|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|name=backup_target\nFUNCTION_BEGIN|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/wrapper.py|name=load_targets|line=37\ndef load_targets():\n base=canonical_targets();return base+[_ct_descriptor(base)]\nFUNCTION_END|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/wrapper.py|name=load_targets\nFUNCTION_BEGIN|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/wrapper.py|name=backup_target|line=40\ndef backup_target(t,green_verify=False):\n if not isinstance(t,dict) or t.get('logical_id')!=CT_ID:\n return _legacy_backup_target(t,green_verify)\n try:\n r=ct112.run_target(t,state_writer=legacy.write_state)\n return {'logical_id':CT_ID,'generation':r['generation'],'result':'SUCCESS','payload_bytes':r['payload_bytes'],\n 'replicas':2,'manifest_sha256':r['manifest_sha256'],'payload_sha256':r['payload_sha256']}\n except BaseException:\n try: legacy.write_state(CT_ID,'FAIL',reason='CT112_PIPELINE_FAILED')\n except BaseException: pass\n raise legacy.BackupError('CT112_PIPELINE_FAILED') from None\nFUNCTION_END|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/wrapper.py|name=backup_target\nBASE_KEY_FUNCTION_COUNT=6\nBASE_KEY_FUNCTIONS_END=1\nBASE_VPS_MD_CONTEXT_BEGIN=1\nVPS_CONTEXT_BEGIN|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=158\n146:def utcnow(): return dt.datetime.now(dt.timezone.utc).replace(microsecond=0).isoformat().replace('+00:00','Z')\n147:def epoch(): return int(time.time())\n148:\n149:def default_targets():\n150: d=86400\n151: out=[]\n152: # PVE guests. Source node is resolved live every run.\n153: for vmid,typ in [(110,'lxc'),(111,'lxc'),(112,'lxc'),(113,'lxc'),(130,'qemu'),(150,'qemu'),(160,'qemu'),(170,'qemu'),(171,'qemu'),(190,'qemu'),(210,'qemu'),(211,'qemu'),(9130,'qemu')]:\n154: out.append({'logical_id':('ct' if typ=='lxc' else 'vm')+str(vmid),'scope':'pve','type':'pve_guest','vmid':vmid,'guest_type':typ,'due_seconds':d,'enabled':True,'green1':vmid==112})\n155: for node in ('pve01','pve02','pve03'):\n156: out.append({'logical_id':'node-'+node,'scope':'node','type':'pve_node_bundle','node':node,'due_seconds':7*d,'enabled':True,'green1':node=='pve01'})\n157: out += [\n158: {'logical_id':'md-relay','scope':'vps','type':'vps_md','host':'[ADDR]','port':22,'user':'ops','hostkey_fingerprint':'SHA256:DXJGwunC5tEVvnC3nRxyayTN8FXmyJcVgf+Oa6udQsY','due_seconds':d,'enabled':True,'green1':True},\n159: {'logical_id':'spb-ingress','scope':'vps','type':'vps_spb','host':'[ADDR]','port':22,'user':'edgeadmin','hostkey_fingerprint':'SHA256:Ef+HsZb8lIXoadvYBXuGKIL3H7NyJMmf9EYCoJRoSv8','due_seconds':d,'enabled':True,'green1':True},\n160: {'logical_id':'us-netbird','scope':'vps','type':'vps_us','host':'[ADDR]','port':22,'user':'root','hostkey_fingerprint':'SHA256:805Q9eh/fePUci42dfY58I6q0lFiiPHn0/M+mckypmE','due_seconds':d,'enabled':True,'green1':True},\n161: {'logical_id':'router-netcraze','scope':'router','type':'router','due_seconds':d,'enabled':True,'green1':True},\n162: ]\n163: sites=[\n164: ('codevipe',160,'pve02','/var/www/forums/codevipe/public','xf_codevipe','single_transaction'),\n165: ('dsmods',160,'pve02','/var/www/forums/dsmods/public','xf_dsmods','single_transaction'),\n166: ('kingofwolk',160,'pve02','/var/www/forums/kingofwolk/public','xf_kingofwolk','single_transaction'),\n167: ('newfi',210,'pve01','/var/www/forums/newfi/public','xf_newfi','single_transaction'),\n168: ('gamevipe',160,'pve02','/var/www/forums/gamevipe/public','xf_gamevipe','global_read_lock'),\n169: ('hkmods',160,'pve02','/var/www/forums/hkmods/public','xf_hkmods','global_read_lock'),\n170: ('zakrutim',160,'pve02','/var/www/forums/zakrutim/public','xf_zakrutim','global_read_lock')]\nVPS_CONTEXT_END|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=158\nVPS_CONTEXT_BEGIN|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=389\n377: zstd=shutil.which('zstd') or '/usr/bin/zstd'; tar=shutil.which('tar') or '/usr/bin/tar'\n378: p=run([zstd,'-t','-q',str(path)],timeout=300)\n379: p1=subprocess.Popen([zstd,'-dc',str(path)],stdout=subprocess.PIPE,stderr=subprocess.PIPE)\n380: p2=subprocess.Popen([tar,'-tf','-'],stdin=p1.stdout,stdout=subprocess.PIPE,stderr=subprocess.PIPE)\n381: p1.stdout.close(); out,err2=p2.communicate(timeout=300); err1=p1.stderr.read(); r1=p1.wait()\n382: if r1!=0 or p2.returncode!=0: raise BackupError('TAR_ZST_LIST_FAILED')\n383: names=out.decode('utf-8','replace').splitlines()\n384: if not names: raise BackupError('TAR_ZST_EMPTY')\n385: if required_member and required_member not in names: raise BackupError('REQUIRED_MEMBER_MISSING')\n386:\n387:def validate_payload(t,path):\n388: typ=t['type']\n389: if typ in ('pve_node_bundle','vps_md','vps_spb','vps_us','router'):\n390: validate_tar_zst(path)\n391: elif typ=='pve_guest':\n392: if t['guest_type']=='lxc': validate_tar_zst(path)\n393: else:\n394: # Green set is intentionally CT-only. Scheduled QEMU backups still get dual remote byte readback.\n395: run([shutil.which('zstd') or '/usr/bin/zstd','-t','-q',str(path)],timeout=600)\n396: elif typ=='xenforo':\n397: validate_tar_zst(path,'db.sql')\n398: zstd=shutil.which('zstd') or '/usr/bin/zstd'; tar=shutil.which('tar') or '/usr/bin/tar'\n399: p1=subprocess.Popen([zstd,'-dc',str(path)],stdout=subprocess.PIPE,stderr=subprocess.PIPE)\n400: p2=subprocess.Popen([tar,'-xOf','-','db.sql'],stdin=p1.stdout,stdout=subprocess.PIPE,stderr=subprocess.PIPE)\n401: p1.stdout.close(); data,err=p2.communicate(timeout=300); p1.wait()\nVPS_CONTEXT_END|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=389\nVPS_CONTEXT_BEGIN|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=568\n556: f=line.split()\n557: if len(f)<3 or f[1]!='ssh-ed25519': continue\n558: try: blob=base64.b64decode(f[2])\n559: except Exception: continue\n560: fp='SHA256:'+base64.b64encode(hashlib.sha256(blob).digest()).decode().rstrip('=')\n561: if fp==expected: good.append(line)\n562: if len(good)!=1: raise BackupError('HOSTKEY_PIN_MISMATCH')\n563: return good[0]\n564:\n565:def ensure_vps_known_hosts(targets):\n566: lines=[]\n567: for t in targets:\n568: if t['type'] not in ('vps_md','vps_spb','vps_us'): continue\n569: lines.append(scan_hostkey_line(t['host'],t['port'],t['hostkey_fingerprint']))\n570: atomic_write(VPS_KNOWN_HOSTS,('\\n'.join(lines)+'\\n').encode(),0o600)\n571:\n572:def vps_ssh_argv(t, *, use_agent=False):\n573: a=[SSH,'-p',str(t['port']),'-o','BatchMode=yes','-o','StrictHostKeyChecking=yes','-o','UserKnownHostsFile='+str(VPS_KNOWN_HOSTS),'-o','ConnectTimeout=15']\n574: if use_agent:\n575: a += ['-o','IdentitiesOnly=no']\n576: else:\n577: a += ['-i',str(OPERATOR_KEY),'-o','IdentitiesOnly=yes','-o','IdentityAgent=none']\n578: a += [t['user']+'@'+t['host']]\n579: return a\n580:\nVPS_CONTEXT_END|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=568\nVPS_CONTEXT_BEGIN|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=591\n579: return a\n580:\n581:def vps_run(t, remote_argv, *, input=None, timeout=300, use_agent=False, check=True):\n582: remote=shlex.join([str(x) for x in remote_argv])\n583: return run(vps_ssh_argv(t,use_agent=use_agent)+[remote],input=input,timeout=timeout,check=check)\n584:\n585:def bootstrap_vps_keys(targets):\n586: operator_key_fingerprint()\n587: ensure_vps_known_hosts(targets)\n588: pub=run(['ssh-keygen','-y','-f',str(OPERATOR_KEY)],timeout=30).stdout.strip()+b'\\n'\n589: results={}\n590: for t in targets:\n591: if t['type'] not in ('vps_md','vps_spb'): continue\n592: probe=vps_run(t,['true'],timeout=30,use_agent=False,check=False)\n593: if probe.returncode==0:\n594: results[t['logical_id']]='ALREADY_OPERATOR_KEY'; continue\n595: if not os.environ.get('SSH_AUTH_SOCK'): raise BackupError('FORWARDED_AGENT_REQUIRED_FOR_BOOTSTRAP')\n596: script=b\"umask 077; mkdir -p \\\"$HOME/.ssh\\\"; chmod 700 \\\"$HOME/.ssh\\\"; touch \\\"$HOME/.ssh/authorized_keys\\\"; chmod 600 \\\"$HOME/.ssh/authorized_keys\\\"; IFS= read -r k; if grep -qF -- \\\"$k\\\" \\\"$HOME/.ssh/authorized_keys\\\"; then printf 'ALREADY\\\\n'; else printf '%s\\\\n' \\\"$k\\\" >> \\\"$HOME/.ssh/authorized_keys\\\"; printf 'ADDED\\\\n'; fi\"\n597: p=vps_run(t,['sh','-c',script.decode()],input=pub,timeout=45,use_agent=True)\n598: tag=p.stdout.decode().strip()\n599: if tag not in ('ADDED','ALREADY'): raise BackupError('VPS_AUTHORIZED_KEY_BOOTSTRAP_RESULT')\n600: vps_run(t,['true'],timeout=30,use_agent=False)\n601: results[t['logical_id']]=tag\n602: # US was already operator-key based; prove it agentless too.\n603: us=[x for x in targets if x['type']=='vps_us'][0]\nVPS_CONTEXT_END|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=591\nVPS_CONTEXT_BEGIN|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=612\n600: vps_run(t,['true'],timeout=30,use_agent=False)\n601: results[t['logical_id']]=tag\n602: # US was already operator-key based; prove it agentless too.\n603: us=[x for x in targets if x['type']=='vps_us'][0]\n604: vps_run(us,['true'],timeout=30,use_agent=False)\n605: results[us['logical_id']]='ALREADY_US_OPERATOR_KEY'\n606: return results\n607:\n608:def remote_hashset(t,command):\n609: p=vps_run(t,['sh','-c',command],timeout=120,use_agent=False)\n610: return sha256_bytes(p.stdout)\n611:\n612:def capture_vps_md(t,gen,work):\n613: files=['etc/netbird/install.conf','var/lib/netbird/state.json','var/lib/netbird/service.json','var/lib/netbird/active_profile.json','var/lib/netbird/default.json']\n614: cmd=\"sudo -n sha256sum -- \"+' '.join(shlex.quote('/'+x) for x in files)\n615: pre=remote_hashset(t,cmd)\n616: remote=shlex.join(['sudo','-n','tar','--numeric-owner','-C','/','-cf','-']+files)\n617: argv=vps_ssh_argv(t,use_agent=False)+[remote]\n618: payload=work/'payload.tar.zst'; pipe_to_zstd(argv,payload,timeout=600)\n619: post=remote_hashset(t,cmd)\n620: if pre!=post: raise BackupError('MD_SOURCE_CHANGED_DURING_CAPTURE')\n621: return payload,{'generation':gen,'source_class':'vps_md','consistency':'FILESET_PRE_POST_HASH_STABILITY','hashset_sha256':pre},None\n622:\n623:def capture_vps_spb(t,gen,work):\n624: hcmd=\"find /etc/nginx -xdev -type f -print0 | sort -z | xargs -0 sha256sum\"\nVPS_CONTEXT_END|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=612\nVPS_CONTEXT_BEGIN|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=621\n609: p=vps_run(t,['sh','-c',command],timeout=120,use_agent=False)\n610: return sha256_bytes(p.stdout)\n611:\n612:def capture_vps_md(t,gen,work):\n613: files=['etc/netbird/install.conf','var/lib/netbird/state.json','var/lib/netbird/service.json','var/lib/netbird/active_profile.json','var/lib/netbird/default.json']\n614: cmd=\"sudo -n sha256sum -- \"+' '.join(shlex.quote('/'+x) for x in files)\n615: pre=remote_hashset(t,cmd)\n616: remote=shlex.join(['sudo','-n','tar','--numeric-owner','-C','/','-cf','-']+files)\n617: argv=vps_ssh_argv(t,use_agent=False)+[remote]\n618: payload=work/'payload.tar.zst'; pipe_to_zstd(argv,payload,timeout=600)\n619: post=remote_hashset(t,cmd)\n620: if pre!=post: raise BackupError('MD_SOURCE_CHANGED_DURING_CAPTURE')\n621: return payload,{'generation':gen,'source_class':'vps_md','consistency':'FILESET_PRE_POST_HASH_STABILITY','hashset_sha256':pre},None\n622:\n623:def capture_vps_spb(t,gen,work):\n624: hcmd=\"find /etc/nginx -xdev -type f -print0 | sort -z | xargs -0 sha256sum\"\n625: pre=remote_hashset(t,hcmd)\n626: remote=shlex.join(['tar','--numeric-owner','-C','/','-cf','-','etc/nginx'])\n627: argv=vps_ssh_argv(t,use_agent=False)+[remote]\n628: payload=work/'payload.tar.zst'; pipe_to_zstd(argv,payload,timeout=900)\n629: post=remote_hashset(t,hcmd)\n630: if pre!=post: raise BackupError('SPB_SOURCE_CHANGED_DURING_CAPTURE')\n631: return payload,{'generation':gen,'source_class':'vps_spb','consistency':'STATIC_CONFIG_FILESET_PRE_POST_HASH_STABILITY','tls_recovery':'REISSUE_TLS_ON_RESTORE','hashset_sha256':pre},None\n632:\n633:US_REMOTE_SCRIPT=r'''import io,os,sqlite3,sys,tarfile,time,json,hashlib\nVPS_CONTEXT_END|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=621\nVPS_CONTEXT_BEGIN|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=875\n863: payload=work/'payload.tar.zst'\n864: argv=['tar','--numeric-owner','--xattrs','--acls','-C','/','-cf','-']+paths\n865: if node=='pve01': pipe_to_zstd(argv,payload,timeout=300)\n866: else:\n867: remote='PATH=/usr/sbin:/usr/bin:/sbin:/bin; export PATH; exec '+shlex.join(argv)\n868: pipe_to_zstd([SSH,'-o','BatchMode=yes','-o','StrictHostKeyChecking=yes','root@'+node,remote],payload,timeout=600)\n869: return payload,{'generation':gen,'source_class':'pve_node_bundle','node':node,'recovery_policy':'CLEAN_INSTALL_JOIN_RECONCILE_NOT_BLIND_PMXCFS_OVERWRITE','member_roots':len(paths)},None\n870:\n871:def capture_target(t,gen,work):\n872: typ=t['type']\n873: if typ=='pve_guest': return capture_pve_guest(t,gen,work)\n874: if typ=='pve_node_bundle': return capture_node_bundle(t,gen,work)\n875: if typ=='vps_md': return capture_vps_md(t,gen,work)\n876: if typ=='vps_spb': return capture_vps_spb(t,gen,work)\n877: if typ=='vps_us': return capture_vps_us(t,gen,work)\n878: if typ=='router': return capture_router(t,gen,work)\n879: if typ=='xenforo': return capture_xenforo(t,gen,work)\n880: raise BackupError('CAPTURE_TYPE_UNKNOWN')\n881:\n882:def cleanup_source(cleanup):\n883: if not cleanup: return\n884: if cleanup.get('kind')=='pve_source_dir':\n885: ssh_remote(cleanup['node'],['rm','-rf','--',cleanup['path']],timeout=60)\n886:\n887:def control_verify(t):\nVPS_CONTEXT_END|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=875\nVPS_CONTEXT_BEGIN|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=991\n979: try:j=json.loads(q.stdout)\n980: except Exception: continue\n981: if j.get('status')=='SEALED' and j.get('generation')==g: bygen.setdefault(g,set()).add(physical)\n982: good=sorted(g for g,phys in bygen.items() if len(phys)>=2)\n983: print('DISCOVER_LOGICAL_ID='+logical_id); print('DISCOVER_TWO_PHYSICAL_SEALED_COUNT='+str(len(good)))\n984: for g in good[-20:]: print('DISCOVER_GENERATION='+g)\n985: return 0 if good else 2\n986:\n987:def selftest():\n988: assert PART_SIZE==536870912\n989: assert len(REMOTES)==13 and len({p for _,p in REMOTES})==13\n990: assert len(default_targets())>=28\n991: assert {t['logical_id'] for t in default_targets() if t.get('green1')}=={'ct112','node-pve01','md-relay','spb-ingress','us-netbird','router-netcraze','xf-codevipe','xf-zakrutim','control-bootstrap-v2'}\n992: assert _pve_dumpdir_spec('lxc')==(0o710,100000)\n993: assert _pve_dumpdir_spec('qemu')==(0o700,0)\n994: assert GUEST266_SHA=='86433bc424d5fa5017c20f7a237c82874b02357a3283660d8a6b0039ef2bc868'\n995: assert P282_SHA=='73c6cb7773eb8e82d1e7ed5ab4733b4afb88f74ae69c09125d0378d6e480e012'\n996: if GUEST266.is_file():\n997: assert _isolated_release_graph_selftest() is True\n998: av,_=proven_lxc_dump_argv({'vmid':112},{'maxdisk':8350298112,'maxmem':536870912,'status':'running','name':'unbound1'},'pve01','/var/tmp/homelab-vzdump-selftest')\n999: assert '--stdout' in av and '--tmpdir' in av and '/var/tmp/homelab-vzdump-selftest' in av\n1000: print('SELFTEST_ENGINE396=10')\n1001: return 0\n1002:\n1003:def main(argv):\nVPS_CONTEXT_END|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=991\nVPS_CONTEXT_BEGIN|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/representative_driver.py|line=17\n5:CT112 producer/cloud path. It routes CT112 to an explicitly supplied callback and\n6:isolates legacy engine state writes into a candidate-owned state directory.\n7:\"\"\"\n8:from __future__ import annotations\n9:\n10:import json\n11:import os\n12:from pathlib import Path\n13:from typing import Callable, Iterable, Mapping, Any\n14:\n15:LEGACY_IDS=(\n16: 'node-pve01',\n17: 'md-relay',\n18: 'spb-ingress',\n19: 'us-netbird',\n20: 'router-netcraze',\n21: 'xf-codevipe',\n22: 'xf-zakrutim',\n23: 'control-bootstrap-v2',\n24:)\n25:CT112_ID='guest-pve01-lxc-112'\n26:REPRESENTATIVE_IDS=(CT112_ID,)+LEGACY_IDS\n27:CANDIDATE_ROOT=Path(__file__).resolve().parent\n28:STATE_ROOT=CANDIDATE_ROOT/'state'\n29:\nVPS_CONTEXT_END|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/representative_driver.py|line=17\nVPS_CONTEXT_BEGIN|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/wrapper.py|line=7\n1:#!/usr/bin/env python3\n2:from __future__ import annotations\n3:import errno,fcntl,importlib.util,os,sys\n4:from pathlib import Path\n5:ROOT=Path(__file__).resolve().parent\n6:CT_ID='guest-pve01-lxc-112'\n7:LEGACY_IDS=('node-pve01','md-relay','spb-ingress','us-netbird','router-netcraze','xf-codevipe','xf-zakrutim','control-bootstrap-v2')\n8:LOCK=Path('/run/homelab-backup-heavy.lock')\n9:\n10:def _load(name,path):\n11: spec=importlib.util.spec_from_file_location(name,str(path))\n12: if spec is None or spec.loader is None: raise RuntimeError('MODULE_SPEC')\n13: mod=importlib.util.module_from_spec(spec);sys.modules[name]=mod;spec.loader.exec_module(mod);return mod\n14:\n15:legacy=_load('_backupv2_legacy486',ROOT/'legacy_engine.py')\n16:ct112=_load('_backupv2_ct112_486',ROOT/'ct112_handler.py')\n17:driver=_load('_backupv2_driver454_479',ROOT/'representative_driver.py')\n18:_legacy_load_targets=legacy.load_targets\n19:_legacy_backup_target=legacy.backup_target\nVPS_CONTEXT_END|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/wrapper.py|line=7\nBASE_VPS_MD_CONTEXT_COUNT=10\nBASE_VPS_MD_CONTEXT_END=1\nBASE_RELEASE_AUTH_CLASS=BASE_VPS_MD_BACKUP_TARGET_LOCATED\nDECISION=PASS_MOLDOVA_1147_BASE_RELEASE_AUTH_COLLECTED\nNEXT_GATE=READ_EXACT_1147_AND_BUILD_REMOTE_ROLE_PREFLIGHT_WITH_PROVEN_LEGACY_AUTH\nTASK_RESULT=PASS_MOLDOVA_BASE_RELEASE_AUTH\nHOMELAB_RESULT_CONTRACT={\"version\":1,\"command_id\":\"SUPPORT-260924-HOMELAB-MOLDOVA-BASE-RELEASE-AUTH-1147R1\",\"status\":\"OK\",\"changes_made\":false,\"rollback_started\":false,\"rollback_restored\":null}\nWORKERS2_MOLDOVA_BASE_RELEASE_AUTH_END=1\n" + "output": "REFERENCE_SHA256=5a3d8e5154c41cb582a4e0aca68090be1f0138918bf82131a948df326f9d8d66\nERROR_REGISTER_SHA256=3b09a553ec0f527ed3afeed4753f52a74ee3036045bbfb3c685e9f8af4ba7ba0\nRUNNER_SHA256=b248a4c32c9cc64e5747e7dce6c7fc0a23f5124a77c71ce72e27a81aceae9d2d\nGITEA_AUTH_SUPPORT-260924-FLEET-CAPTCHA-SHARED-WIDGET-DOMAINS-RECONCILE-0107R1_TXT_SHA256=3affbd310f46b6f807edb9a4f411460798fa9ddd974238877f6dab21c2afa59d\nGITEA_AUTH_SUPPORT-260924-FLEET-CAPTCHA-PRODUCTION-SERIAL-MIGRATION-0048R1_TXT_SHA256=1ab5ca9366b467d0d2ed85bc56364f06e9a867977f5106eb8cdd5d15b14c6c71\nGITEA_AUTH_SUPPORT-260924-FLEET-CAPTCHA-CF-LIST-AUTOSELECT-0006R1_TXT_SHA256=469b6cb88197f3fc7ff461f7b0dd46ea218c58ffa5f944222d339941510ac360\nGITEA_BRANCH_HEAD=df13b49dffd2cc5b4be705ca3a96241b11a53fdd\nCF_TOKEN_SHA16=7fb2f75e3a1f8a3b\nCF_TOKEN_VERIFY_HTTP=200\nCF_ZONE_LOOKUP_HTTP=200\nCF_PVEPRO_ZONE_MATCH_COUNT=1\nCF_ACCOUNT_ID_SHA256=49ace22b1853724543e1b3b00f02d6a03acd8bd4f9046f8cf5c3c9b6343984f3\nCF_WIDGET_LIST_HTTP=200\nCF_WIDGET_COUNT=2\nSHARED_WIDGET_MODE=managed\nSHARED_WIDGET_NAME_SHA256=2ce0d0392662a166fef18981747dffc4e729571d5ebc953f9a5a2ba7b63c4742\nSHARED_WIDGET_DOMAIN_COUNT=10\nSHARED_WIDGET_UNIQUE_DOMAIN_COUNT=10\nSHARED_WIDGET_DOMAIN_01=codevipe.ru\nSHARED_WIDGET_DOMAIN_01_CLASS=PRODUCTION_FORUM\nSHARED_WIDGET_DOMAIN_02=dsmods.ru\nSHARED_WIDGET_DOMAIN_02_CLASS=PRODUCTION_FORUM\nSHARED_WIDGET_DOMAIN_03=gamevipe.ru\nSHARED_WIDGET_DOMAIN_03_CLASS=PRODUCTION_FORUM\nSHARED_WIDGET_DOMAIN_04=gram1.ru\nSHARED_WIDGET_DOMAIN_04_CLASS=UNKNOWN\nSHARED_WIDGET_DOMAIN_04_UNKNOWN_DNS_V4_COUNT=1\nSHARED_WIDGET_DOMAIN_04_UNKNOWN_HTTPS=200\nSHARED_WIDGET_DOMAIN_05=hkmods.ru\nSHARED_WIDGET_DOMAIN_05_CLASS=PRODUCTION_FORUM\nSHARED_WIDGET_DOMAIN_06=kingofwolk.ru\nSHARED_WIDGET_DOMAIN_06_CLASS=PRODUCTION_FORUM\nSHARED_WIDGET_DOMAIN_07=newfi.ru\nSHARED_WIDGET_DOMAIN_07_CLASS=PRODUCTION_FORUM\nSHARED_WIDGET_DOMAIN_08=newfilya.ru\nSHARED_WIDGET_DOMAIN_08_CLASS=UNKNOWN\nSHARED_WIDGET_DOMAIN_08_UNKNOWN_DNS_V4_COUNT=1\nSHARED_WIDGET_DOMAIN_08_UNKNOWN_HTTPS=200\nSHARED_WIDGET_DOMAIN_09=pvepro.ru\nSHARED_WIDGET_DOMAIN_09_CLASS=PRODUCTION_FORUM\nSHARED_WIDGET_DOMAIN_10=zakrutim.ru\nSHARED_WIDGET_DOMAIN_10_CLASS=PRODUCTION_FORUM\nSHARED_WIDGET_PRODUCTION_FORUM_DOMAIN_COUNT=8\nSHARED_WIDGET_KNOWN_STAGING_DOMAIN_COUNT=0\nSHARED_WIDGET_UNKNOWN_DOMAIN_COUNT=2\nSHARED_WIDGET_DOMAINS_SHA256=55180f23ff32cb88c56d10d162185bcb7a7031be0aa2469ecb97b53f06352df6\nCLASSIFICATION_ENGINE=PYTHON_NO_STDIN_CONSUMPTION\nCLASSIFIED_DOMAIN_ROWS=10\nMUTATION_BOUNDARY=READ_ONLY;CLOUDFLARE_GET_ONLY;NO_WIDGET_CREATE_UPDATE_DELETE;NO_QGA;NO_XENFORO;NO_DB;NO_VM_CONFIG;NO_EDGE;NO_BACKUP\nSHARED_WIDGET_DOMAIN_RECONCILE_FIXED_COMPLETE=true\nDECISION=PASS_FLEET_CAPTCHA_SHARED_WIDGET_DOMAIN_RECONCILE_FIX\nNEXT_GATE=REVIEW_UNKNOWN_SHARED_WIDGET_DOMAINS\nTASK_RESULT=SHARED_WIDGET_UNKNOWN_DOMAINS_EXACTLY_IDENTIFIED\nHOMELAB_RESULT_CONTRACT={\"version\":1,\"command_id\":\"SUPPORT-260924-FLEET-CAPTCHA-SHARED-WIDGET-DOMAINS-RECONCILE-FIX-0119R1\",\"status\":\"OK\",\"changes_made\":false,\"rollback_started\":false,\"rollback_restored\":null}\n" } diff --git a/runtime/latest.txt b/runtime/latest.txt index 030726ff..e07896b0 100644 --- a/runtime/latest.txt +++ b/runtime/latest.txt @@ -1,15 +1,15 @@ CHAT_OUTPUT_BEGIN -COMMAND_ID=SUPPORT-260924-HOMELAB-MOLDOVA-BASE-RELEASE-AUTH-1147R1 +COMMAND_ID=SUPPORT-260924-FLEET-CAPTCHA-SHARED-WIDGET-DOMAINS-RECONCILE-FIX-0119R1 STATUS=OK RC=0 HOST=pve01 MODE=read-only -COMPONENT=homelab-moldova-base-release-auth +COMPONENT=forum-captcha REFERENCE_REGISTER_CHECK=OK REFERENCE_SHA256=5a3d8e5154c41cb582a4e0aca68090be1f0138918bf82131a948df326f9d8d66 ERROR_REGISTER_CHECK=OK ERROR_REGISTER_SHA256=3b09a553ec0f527ed3afeed4753f52a74ee3036045bbfb3c685e9f8af4ba7ba0 -COMMAND_SHA256=f3fe1cb2e034d22c5917b5946f86b1c8ea517c0abd0a92891868a481e2aa5da7 +COMMAND_SHA256=ba8787e7b885617e285e2a52c96350817e90ca7b882b71d3424f84a02ab601fb DUPLICATE_FAILED_COMMAND_BLOCKED=false EXECUTION_STARTED=true CHANGE_DECLARED=false @@ -24,616 +24,63 @@ MUTATION_OUTCOME=NO_MUTATION SANITIZED=yes SECRETS_INCLUDED=no PRIVATE_ADDRESSES_INCLUDED=no -RAW_EVIDENCE_SHA256=add93d5fb111ea9426df8d172911f8506ad95531ae42be7e0c338901a61b8608 -SANITIZED_OUTPUT_SHA256=e60b4493bf44bffbb9ab84838090dbefdb74cfb16219ea4cc0d3a9e834e59ae4 +RAW_EVIDENCE_SHA256=12bc50352aad0e5bf2e5d55491007dea4047ffde164f8f754a4b5b13d30a4dfe +SANITIZED_OUTPUT_SHA256=12bc50352aad0e5bf2e5d55491007dea4047ffde164f8f754a4b5b13d30a4dfe OUTPUT_BEGIN -WORKERS2_MOLDOVA_BASE_RELEASE_AUTH_BEGIN=1 -COMMAND_ID=SUPPORT-260924-HOMELAB-MOLDOVA-BASE-RELEASE-AUTH-1147R1 -MODE=read-only -COMPONENT=homelab-moldova-base-release-auth -MUTATION_BOUNDARY=NONE;ENTRY504_PROVENANCE_AND_LITERAL_BASE_RELEASE_STATIC_INSPECTION_ONLY;NO_MOLDOVA_CONNECTION;NO_REMOTE_SSH;NO_PROVIDER_CALL;NO_SYSTEMD_ACTION;NO_BACKUP_RUN;NO_BACKUP_MUTATION;NO_TARGET_POLICY_MUTATION;NO_NETBIRD_MUTATION;NO_MONITORING_MUTATION;NO_GIT_WRITE;NO_SECRET_VALUE_READOUT -PREDECESSOR_1146=PASS_MOLDOVA_1146_RELEASE_AUTH_EXACT_COLLECTED -PREDECESSOR_1146_CLASS=VPS_MD_HANDLER_NOT_LOCATED -CARRIER_BASELINE=KNOWN_GOOD_1137_BYTE_SEMANTICS REFERENCE_SHA256=5a3d8e5154c41cb582a4e0aca68090be1f0138918bf82131a948df326f9d8d66 ERROR_REGISTER_SHA256=3b09a553ec0f527ed3afeed4753f52a74ee3036045bbfb3c685e9f8af4ba7ba0 RUNNER_SHA256=b248a4c32c9cc64e5747e7dce6c7fc0a23f5124a77c71ce72e27a81aceae9d2d -TARGETS_SHA256=5cba5f1e14c65211c9fab6d11594c19279ab68369b4db3f7283708c368348288 -ENTRY504_SHA256=ca6876fa9c3b1465b045344a8ad46962de16972c7dadc4ac6257c7d225047793 -MD_RELAY_COUNT=1 -MD_RELAY_ENABLED=false -ENTRY504_PROVENANCE_BLOCK_BEGIN=1 -ENTRY504|line=20|text= while True: -ENTRY504|line=21|text= b=f.read(1<<20) -ENTRY504|line=22|text= if not b: break -ENTRY504|line=23|text= h.update(b) -ENTRY504|line=24|text= return h.hexdigest() -ENTRY504|line=25|text= -ENTRY504|line=26|text=def _verify_base(): -ENTRY504|line=27|text= s=os.lstat(BASE) -ENTRY504|line=28|text= if not stat.S_ISDIR(s.st_mode) or stat.S_ISLNK(s.st_mode) or s.st_uid!=0 or s.st_gid!=0: -ENTRY504|line=29|text= raise RuntimeError('BASE_RELEASE_ROOT') -ENTRY504|line=30|text= for rel,want in BASE_EXPECTED.items(): -ENTRY504|line=31|text= p=BASE/rel; st=os.lstat(p) -ENTRY504|line=32|text= if not stat.S_ISREG(st.st_mode) or stat.S_ISLNK(st.st_mode) or st.st_uid!=0 or st.st_gid!=0 or _sha(p)!=want: -ENTRY504|line=33|text= raise RuntimeError('BASE_RELEASE_FILE_'+rel) -ENTRY504|line=34|text= -ENTRY504|line=35|text=class _GlobalsProxy: -ENTRY504|line=36|text= __slots__=('_g',) -ENTRY504|line=37|text= def __init__(self,g): object.__setattr__(self,'_g',g) -ENTRY504|line=38|text= def __getattr__(self,n): -ENTRY504|line=39|text= try: return self._g[n] -ENTRY504|line=40|text= except KeyError as e: raise AttributeError(n) from e -ENTRY504|line=41|text= def __setattr__(self,n,v): self._g[n]=v -ENTRY504|line=42|text= -ENTRY504|line=43|text=def _wire(wrapper,lxc_adapter,qemu_adapter,scheduler_adapter,base=BASE): -ENTRY504|line=44|text= legacy_path=(pathlib.Path(base)/'legacy_engine.py').resolve() -ENTRY504|line=45|text= found={} -ENTRY504|line=46|text= for value in vars(wrapper).values(): -ENTRY504|line=47|text= if isinstance(value,types.ModuleType): -ENTRY504|line=48|text= g=vars(value); raw=g.get('__file__') -ENTRY504|line=49|text= elif isinstance(value,types.FunctionType): -ENTRY504|line=50|text= g=value.__globals__; raw=g.get('__file__') -ENTRY504|line=51|text= else: -ENTRY504|line=52|text= continue -ENTRY504|line=53|text= if not isinstance(raw,str): -ENTRY504|line=54|text= continue -ENTRY504|line=55|text= try: gp=pathlib.Path(raw).resolve() -ENTRY504|line=56|text= except Exception: continue -ENTRY504|line=57|text= if gp==legacy_path: -ENTRY504|line=58|text= found[id(g)]=g -ENTRY504|line=59|text= if len(found)!=1: -ENTRY504|line=60|text= raise RuntimeError('LEGACY_PROVENANCE_CARDINALITY') -ENTRY504|line=61|text= g=next(iter(found.values())) -ENTRY504|line=62|text= required=('capture_pve_guest','backup_target','load_targets','cluster_inventory','ssh_remote', -ENTRY504|line=63|text= '_default_unprivileged_lxc','mapped_scratch_probe','_stream_argv_to_local','run', -ENTRY504|line=64|text= 'choose_due_target','write_state','epoch') -ENTRY504|line=65|text= if any(not callable(g.get(n)) for n in required): -ENTRY504|line=66|text= raise RuntimeError('LEGACY_PROVENANCE_API') -ENTRY504|line=67|text= original=g['capture_pve_guest'] -ENTRY504|line=68|text= proxy=_GlobalsProxy(g) -ENTRY504|line=69|text= if not isinstance(g.get('SCP'),str) or not g['SCP'].startswith('/'): -ENTRY504|line=70|text= raise RuntimeError('LEGACY_SCP_API') -ENTRY504|line=71|text= qemu_patched=qemu_adapter.bind(proxy,original) -ENTRY504|line=72|text= if not callable(qemu_patched): -ENTRY504|line=73|text= raise RuntimeError('QEMU_ADAPTER_BIND_RESULT') -ENTRY504|line=74|text= patched=lxc_adapter.bind(proxy,qemu_patched) -ENTRY504|line=75|text= if not callable(patched): -ENTRY504|line=76|text= raise RuntimeError('LXC_ADAPTER_BIND_RESULT') -ENTRY504|line=77|text= g['capture_pve_guest']=patched -ENTRY504|line=78|text= scheduler_wired=scheduler_adapter.bind(proxy,g['choose_due_target'],g['write_state']) -ENTRY504|line=79|text= if g.get('choose_due_target') is not scheduler_wired['choose_due_target'] or g.get('write_state') is not scheduler_wired['write_state']: -ENTRY504|line=80|text= raise RuntimeError('SCHEDULER_ADAPTER_BIND_RESULT') -ENTRY504|line=81|text= return {'legacy_globals':g,'legacy':proxy,'original_capture':original,'qemu_patched_capture':qemu_patched,'patched_capture':patched,'scheduler_wired':scheduler_wired} -ENTRY504|line=82|text= -ENTRY504|line=83|text=def _load(): -ENTRY504|line=84|text= _verify_base() -ENTRY504|line=85|text= sys.dont_write_bytecode=True -ENTRY504|line=86|text= sys.path.insert(0,str(BASE)) -ENTRY504|line=87|text= wrapper=importlib.import_module('wrapper') -ENTRY504|line=88|text= # Baseline accepted wrapper selftest stays the compatibility gate; no full old legacy selftest is reintroduced. -ENTRY504|line=89|text= wrapper.selftest() -ENTRY504|line=90|text= sys.path.insert(0,str(HERE)) -ENTRY504|line=91|text= lxc_adapter=importlib.import_module('lxc_adapter') -ENTRY504|line=92|text= qemu_adapter=importlib.import_module('qemu_adapter') -ENTRY504|line=93|text= scheduler_adapter=importlib.import_module('scheduler_adapter') -ENTRY504|line=94|text= if _sha(HERE/'lxc_adapter.py')!='3fa7d194010d76f7c8bc95035ae8876262e9337903e310f45f45df88c266dc8d': -ENTRY504|line=95|text= raise RuntimeError('LXC_502_BYTE_IDENTITY') -ENTRY504|line=96|text= wired=_wire(wrapper,lxc_adapter,qemu_adapter,scheduler_adapter,BASE) -ENTRY504|line=97|text= return wrapper,lxc_adapter,qemu_adapter,scheduler_adapter,wired -ENTRY504|line=98|text= -ENTRY504|line=99|text=def _target(wrapper,lid): -ENTRY504|line=100|text= rows=wrapper.load_targets(); hits=[x for x in rows if x.get('logical_id')==lid] -ENTRY504|line=101|text= if len(hits)!=1: raise RuntimeError('TARGET_CARDINALITY') -ENTRY504|line=102|text= return hits[0] -ENTRY504|line=103|text= -ENTRY504|line=104|text=def selftest(): -ENTRY504|line=105|text= wrapper,lxc_adapter,qemu_adapter,scheduler_adapter,wired=_load() -ENTRY504|line=106|text= if wired['legacy_globals'].get('capture_pve_guest') is not wired['patched_capture']: -ENTRY504|line=107|text= raise RuntimeError('PATCH_BINDING') -ENTRY504|line=108|text= if wired['legacy_globals'].get('choose_due_target') is not wired['scheduler_wired']['choose_due_target']: -ENTRY504|line=109|text= raise RuntimeError('SCHEDULER_CHOOSE_BINDING') -ENTRY504|line=110|text= if wired['legacy_globals'].get('write_state') is not wired['scheduler_wired']['write_state']: -ENTRY504|line=111|text= raise RuntimeError('SCHEDULER_WRITE_BINDING') -ENTRY504|line=112|text= if [scheduler_adapter.backoff_seconds(n) for n in range(1,7)]!=[1800,3600,7200,14400,21600,21600]: -ENTRY504|line=113|text= raise RuntimeError('SCHEDULER_BACKOFF_CONTRACT') -ENTRY504|line=114|text= if not callable(wrapper.backup_target) or not callable(wrapper.main) or not callable(wrapper.load_targets): -ENTRY504|line=115|text= raise RuntimeError('WRAPPER_PUBLIC_API') -ENTRY504|line=116|text= print('SELFTEST_OVERLAY504=PASS',flush=True) -ENTRY504|line=117|text= return 0 -ENTRY504|line=118|text= -ENTRY504|line=119|text=def main(argv=None): -ENTRY504|line=120|text= argv=list(sys.argv[1:] if argv is None else argv) -ENTRY504_PROVENANCE_BLOCK_END=1 -ENTRY504_RELEASE_LITERAL_COUNT=1 -ENTRY504_RELEASE_LITERAL=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1 -BASE_CANDIDATE_COUNT=1 -BASE_CANDIDATE|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1|kind=dir|mode=500|uid=0|gid=0 -BASE_PY_FILE_COUNT=21 -BASE_FILE|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/adapter.py|bytes=2989|sha256=18eb29cef165371b48bcb78e1a456e1939e6eff253787218133162cc89ab9fa3|mode=500|uid=0|gid=0 -BASE_FILE|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/guest266.py|bytes=7385|sha256=86433bc424d5fa5017c20f7a237c82874b02357a3283660d8a6b0039ef2bc868|mode=400|uid=0|gid=0 -BASE_FILE|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/large264.py|bytes=16514|sha256=5cc4641fc35e4dd554495a36146da7dbbb7f142d1450e04cc5d3c89678b8497a|mode=400|uid=0|gid=0 -BASE_FILE|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/nodeplan263.py|bytes=4603|sha256=db5c16ee24d25aa1694530332d3190ab7cc5d4f8eb69067d025e27ad5c995060|mode=400|uid=0|gid=0 -BASE_FILE|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/pve265.py|bytes=12888|sha256=cd2f634414a8e38a3789015aeca5a9b47821f77d7d1fa61dcdbdc866172963be|mode=400|uid=0|gid=0 -BASE_FILE|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/pve270.py|bytes=19595|sha256=97015277a4f3f8129c0fd11eb70c06293026366f1f8232a97feeed513a2dbcef|mode=400|uid=0|gid=0 -BASE_FILE|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/pve282.py|bytes=6195|sha256=73c6cb7773eb8e82d1e7ed5ab4733b4afb88f74ae69c09125d0378d6e480e012|mode=400|uid=0|gid=0 -BASE_FILE|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/restore262.py|bytes=14701|sha256=56d1b961c8d5654d5e2ee2755268db46bd2202bca11e69788febf991cfaa79f1|mode=400|uid=0|gid=0 -BASE_FILE|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/stream263.py|bytes=8831|sha256=a83d03a86b93a855716658652dd073fcb281cd387fc6200fe4bc2a922f39ea16|mode=400|uid=0|gid=0 -BASE_FILE|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/test_guest266.py|bytes=10752|sha256=460b950924a475ce0a28dc759512e7dbe2323ae2cc42f7ef4c6181710a2a13da|mode=400|uid=0|gid=0 -BASE_FILE|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/test_perl270.py|bytes=5441|sha256=50fc10865078a7b9b255dc627059824184441a47c4ede52e23f24c1186e3512f|mode=400|uid=0|gid=0 -BASE_FILE|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/test_producer282.py|bytes=12215|sha256=a67d927cc428935791f9eecc3acf571d089f80761162f4589b7d684fba6b8af7|mode=400|uid=0|gid=0 -BASE_FILE|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/test_pve265.py|bytes=11781|sha256=16a9407d01bf73db687ef38684c59205fc2e6a9223cd9cee6ec6e8dde9d38707|mode=400|uid=0|gid=0 -BASE_FILE|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/test_pve270.py|bytes=12077|sha256=9fccd20b490613fdd5b14b4105cf0d9fd744f204e1ed1d6893e54c9f69bf6fb4|mode=400|uid=0|gid=0 -BASE_FILE|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/test_pve282.py|bytes=5785|sha256=1056407aab76e17181b05e043cb42c086058545a8e0027689ab4e03eee6531c4|mode=400|uid=0|gid=0 -BASE_FILE|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/transport261.py|bytes=15364|sha256=b9b92f957bf441cb5d1702f4881897328e6c0338f8c3f6d41d27d9a349b37688|mode=400|uid=0|gid=0 -BASE_FILE|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/v2core.py|bytes=11755|sha256=e285bf9a5678d07dcbb90261f9f93e7826ff524e04cfec780bf5a3c4b0dc29f2|mode=400|uid=0|gid=0 -BASE_FILE|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/ct112_handler.py|bytes=10955|sha256=8cac6e627d5e7abe5d3163cdd24911995a1e3f45c776f9373c2aa68784f5ec6c|mode=400|uid=0|gid=0 -BASE_FILE|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|bytes=59719|sha256=b6e79f087b9f1d21dac4f77a7b46b743299bbb85bc716e80d2ea67c2e038bcd7|mode=400|uid=0|gid=0 -BASE_FILE|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/representative_driver.py|bytes=4214|sha256=b3050a8f95e6d9826a4f0786cd3ca32012b6ab10486d10e4981b50b8a0affff2|mode=400|uid=0|gid=0 -BASE_FILE|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/wrapper.py|bytes=5055|sha256=2b5eb4fc0f6f466a0d3e5a007af987092b6c17baa3ff9241a6afc2c2e97df1d5|mode=400|uid=0|gid=0 -BASE_AUTH_SNIPPETS_BEGIN=1 -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/large264.py|line=12|text=import subprocess -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/large264.py|line=27|text= p=subprocess.Popen(argv,stdin=subprocess.DEVNULL,stdout=subprocess.PIPE,stderr=subprocess.PIPE, -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/large264.py|line=41|text= except subprocess.TimeoutExpired:raise t.Stop('STREAM_TIMEOUT') from None -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/pve265.py|line=13|text=import subprocess -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/pve265.py|line=144|text= except subprocess.TimeoutExpired: -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/pve265.py|line=160|text= p=subprocess.Popen(argv,stdin=subprocess.DEVNULL,stdout=subprocess.PIPE,stderr=subprocess.PIPE, -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/pve265.py|line=176|text= except subprocess.TimeoutExpired:raise PVEError('PRODUCER_TIMEOUT') from None -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/pve270.py|line=6|text=import hashlib,json,os,re,secrets,selectors,signal,stat,subprocess,sys,time -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/pve270.py|line=110|text= p=subprocess.Popen(['/proc/self/fd/'+str(self.fd),*args],pass_fds=(self.fd,), -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/pve270.py|line=111|text= stdin=subprocess.DEVNULL,stdout=subprocess.PIPE,stderr=subprocess.PIPE, -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/pve270.py|line=124|text= except subprocess.TimeoutExpired:raise old.PVEError('PERL_TIMEOUT') from None -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/pve270.py|line=217|text= p=subprocess.run([sys.executable,'-I','-B','-c',code,*args],stdin=subprocess.DEVNULL,stdout=subprocess.DEVNULL, -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/pve270.py|line=218|text= stderr=subprocess.DEVNULL,env=dict(old.ENV),user=u,group=g,extra_groups=[],close_fds=True,timeout=15) -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/pve270.py|line=247|text= p=subprocess.Popen(argv,stdin=subprocess.DEVNULL,stdout=subprocess.PIPE,stderr=subprocess.PIPE, -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/pve270.py|line=266|text= except subprocess.TimeoutExpired:raise old.PVEError('PRODUCER_TIMEOUT') from None -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/test_producer282.py|line=2|text=import copy,hashlib,json,os,stat,subprocess,sys,tempfile,time,unittest -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/test_producer282.py|line=10|text= return subprocess.run([sys.executable,'-I','-B','-c',code,*map(str,args)],user=uid,group=gid,extra_groups=[],stdin=subprocess.DEVNULL,stdout=subprocess.PIPE,stderr=subprocess.PIPE,timeout=10) -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/test_producer282.py|line=134|text= """Actual subprocess + tar/zstd, synthetic data; never runs vzdump on a guest.""" -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/test_producer282.py|line=152|text= code='''import os,sys,subprocess,shutil -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/test_producer282.py|line=158|text=a=subprocess.run(['/usr/bin/tar','-cf','-','-C',str(p),'./etc/vzdump/pct.conf'],user=100000,group=100000,extra_groups=[],stdout=subprocess.PIPE,stderr=subprocess.PIPE) -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/test_producer282.py|line=161|text=z=subprocess.run(['/usr/bin/zstd','-q','-c'],input=a.stdout,stdout=subprocess.PIPE,stderr=subprocess.PIPE) -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/test_producer282.py|line=193|text= def crc(p):return subprocess.run(['/usr/bin/zstd','--test','--quiet',str(p)],stdout=subprocess.DEVNULL,stderr=subprocess.DEVNULL).returncode==0 -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/test_pve265.py|line=1|text="""Synthetic PVE fixtures; subprocess tests never execute real PVE tools.""" -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/test_pve270.py|line=2|text=import copy,hashlib,json,os,stat,subprocess,sys,tempfile,time,unittest -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/test_pve270.py|line=10|text= return subprocess.run([sys.executable,'-I','-B','-c',code,*map(str,args)],user=uid,group=gid,extra_groups=[],stdin=subprocess.DEVNULL,stdout=subprocess.PIPE,stderr=subprocess.PIPE,timeout=10) -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/test_pve270.py|line=134|text= """Actual subprocess + tar/zstd, synthetic data; never runs vzdump on a guest.""" -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/test_pve270.py|line=152|text= code='''import os,sys,subprocess,shutil -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/test_pve270.py|line=157|text=a=subprocess.run(['/usr/bin/tar','-cf','-','-C',str(p),'./etc/vzdump/pct.conf'],user=100000,group=100000,extra_groups=[],stdout=subprocess.PIPE,stderr=subprocess.PIPE) -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/test_pve270.py|line=160|text=z=subprocess.run(['/usr/bin/zstd','-q','-c'],input=a.stdout,stdout=subprocess.PIPE,stderr=subprocess.PIPE) -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/test_pve270.py|line=192|text= def crc(p):return subprocess.run(['/usr/bin/zstd','--test','--quiet',str(p)],stdout=subprocess.DEVNULL,stderr=subprocess.DEVNULL).returncode==0 -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/test_pve282.py|line=2|text=import copy, json, unittest, tempfile, time, subprocess -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/transport261.py|line=15|text=import subprocess -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/transport261.py|line=149|text= p=subprocess.Popen(argv,stdin=subprocess.DEVNULL,stdout=subprocess.PIPE,stderr=subprocess.PIPE, -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/transport261.py|line=163|text= except subprocess.TimeoutExpired:raise Stop('PROCESS_TIMEOUT') from None -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/ct112_handler.py|line=3|text=import hashlib,json,os,secrets,shutil,stat,subprocess,sys,time -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/ct112_handler.py|line=26|text= p=subprocess.run(argv,stdout=subprocess.PIPE,stderr=subprocess.PIPE,timeout=45,check=False,env=ENV) -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/ct112_handler.py|line=166|text= p=subprocess.run([str(ZSTD),'--test','--quiet','--',str(path)],stdout=subprocess.DEVNULL,stderr=subprocess.DEVNULL,timeout=3600,check=False,env=ENV) -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=2|text=import ast, base64, datetime as dt, hashlib, io, json, os, pathlib, random, re, select, shlex, shutil, stat, subprocess, sys, tarfile, tempfile, time -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=12|text=SSH=shutil.which('ssh') or '/usr/bin/ssh' -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=13|text=SCP=shutil.which('scp') or '/usr/bin/scp' -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=14|text=SSH_KEYSCAN=shutil.which('ssh-keyscan') or '/usr/bin/ssh-keyscan' -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=15|text=SSHPASS=shutil.which('sshpass') or '/usr/bin/sshpass' -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=16|text=OPERATOR_KEY=pathlib.Path('[SENSITIVE_PATH] -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=158|text= {'logical_id':'md-relay','scope':'vps','type':'vps_md','host':'[ADDR]','port':22,'user':'ops','hostkey_fingerprint':'SHA256:DXJGwunC5tEVvnC3nRxyayTN8FXmyJcVgf+Oa6udQsY','due_seconds':d,'enabled':True,'green1':True}, -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=159|text= {'logical_id':'spb-ingress','scope':'vps','type':'vps_spb','host':'[ADDR]','port':22,'user':'edgeadmin','hostkey_fingerprint':'SHA256:Ef+HsZb8lIXoadvYBXuGKIL3H7NyJMmf9EYCoJRoSv8','due_seconds':d,'enabled':True,'green1':True}, -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=160|text= {'logical_id':'us-netbird','scope':'vps','type':'vps_us','host':'[ADDR]','port':22,'user':'root','hostkey_fingerprint':'SHA256:805Q9eh/fePUci42dfY58I6q0lFiiPHn0/M+mckypmE','due_seconds':d,'enabled':True,'green1':True}, -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=191|text= p=subprocess.run(argv,input=input,stdout=subprocess.PIPE,stderr=subprocess.PIPE,timeout=timeout,env=env) -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=192|text= except subprocess.TimeoutExpired: raise BackupError('COMMAND_TIMEOUT') -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=199|text= try:p=subprocess.run(argv,input=input,stdout=f,stderr=subprocess.PIPE,timeout=timeout) -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=200|text= except subprocess.TimeoutExpired: raise BackupError('STREAM_COMMAND_TIMEOUT') -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=206|text= src=subprocess.Popen(argv,stdin=subprocess.PIPE if input is not None else None,stdout=subprocess.PIPE,stderr=subprocess.PIPE) -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=207|text= z=subprocess.Popen([zstd,'-T0','-3','-q','-o',str(out_path)],stdin=src.stdout,stdout=subprocess.DEVNULL,stderr=subprocess.PIPE) -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=213|text= except subprocess.TimeoutExpired: -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=216|text= except subprocess.TimeoutExpired: -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=236|text=def load_targets(): -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=289|text= p=subprocess.Popen(argv,stdin=subprocess.PIPE,stdout=subprocess.PIPE,stderr=subprocess.PIPE) -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=311|text= with subprocess.Popen(rclone_base_args()+['cat',remote_path,'--contimeout','20s','--timeout','5m'],stdout=subprocess.PIPE,stderr=subprocess.PIPE) as p: -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=365|text= with subprocess.Popen(rclone_base_args()+['cat',base+'/parts/'+part['name'],'--contimeout','20s','--timeout','5m'],stdout=subprocess.PIPE,stderr=subprocess.PIPE) as p: -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=379|text= p1=subprocess.Popen([zstd,'-dc',str(path)],stdout=subprocess.PIPE,stderr=subprocess.PIPE) -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=380|text= p2=subprocess.Popen([tar,'-tf','-'],stdin=p1.stdout,stdout=subprocess.PIPE,stderr=subprocess.PIPE) -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=389|text= if typ in ('pve_node_bundle','vps_md','vps_spb','vps_us','router'): -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=399|text= p1=subprocess.Popen([zstd,'-dc',str(path)],stdout=subprocess.PIPE,stderr=subprocess.PIPE) -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=400|text= p2=subprocess.Popen([tar,'-xOf','-','db.sql'],stdin=p1.stdout,stdout=subprocess.PIPE,stderr=subprocess.PIPE) -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=405|text=def cluster_inventory(): -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=410|text=def ssh_remote(node,argv,input=None,timeout=1800,check=True): -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=413|text= return run([SSH,'-o','BatchMode=yes','-o','StrictHostKeyChecking=yes','root@'+node,cmd],input=input,timeout=timeout,check=check) -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=430|text= p=subprocess.run([sys.executable,'-B','-c',_HISTORICAL_HELPER,'import_all'],stdout=subprocess.PIPE,stderr=subprocess.PIPE,env=env,cwd='/',timeout=30) -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=441|text= p=subprocess.run([sys.executable,'-B','-c',_HISTORICAL_HELPER,'dump_argv',str(GUEST266)],input=canonical_json(req),stdout=subprocess.PIPE,stderr=subprocess.PIPE,env=env,cwd='/',timeout=30) -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=442|text= if p.returncode!=0: raise BackupError('GUEST266_SUBPROCESS_FAILED',f'rc_{p.returncode}_stderr_sha256_{hashlib.sha256(p.stderr).hexdigest()}') -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=444|text= except Exception: raise BackupError('GUEST266_SUBPROCESS_JSON') -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=476|text= q=ssh_remote(node,['pct','config',str(vmid)],timeout=30) -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=484|text= q=ssh_remote(node,pos,timeout=30,check=False) -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=492|text= cmd=[SSH,'-o','BatchMode=yes','-o','StrictHostKeyChecking=yes','root@'+node,remote] -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=494|text= try:p=subprocess.run(cmd,stdout=f,stderr=subprocess.PIPE,timeout=timeout) -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=495|text= except subprocess.TimeoutExpired: -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=514|text= ssh_remote(node,['install','-d','-m','710','-o','0','-g','100000',scratch],timeout=30) -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=515|text= q=ssh_remote(node,['stat','-c','%a:%u:%g',scratch],timeout=30) -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=524|text= ssh_remote(node,['rm','-rf','--',scratch],timeout=60,check=False);raise -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=529|text= ssh_remote(node,['install','-d','-m','700','-o','0','-g','0',srcdir],timeout=30) -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=531|text= ssh_remote(node,['vzdump',str(vmid),'--mode','snapshot','--compress','zstd','--dumpdir',srcdir],timeout=7200) -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=532|text= p=ssh_remote(node,['find',srcdir,'-maxdepth','1','-type','f','-printf','%p\n'],timeout=30) -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=537|text= else: run([SCP,'-q','-o','BatchMode=yes','-o','StrictHostKeyChecking=yes','root@'+node+':'+src,str(local)],timeout=7200) -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=540|text= ssh_remote(node,['rm','-rf','--',srcdir],timeout=60,check=False);raise -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=545|text= p=run(['ssh-keygen','-lf',str(OPERATOR_KEY),'-E','sha256'],timeout=30) -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=552|text= p=run([SSH_KEYSCAN,'-T','8','-p',str(port),'-t','ed25519',host],timeout=15) -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=557|text= if len(f)<3 or f[1]!='ssh-ed25519': continue -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=568|text= if t['type'] not in ('vps_md','vps_spb','vps_us'): continue -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=569|text= lines.append(scan_hostkey_line(t['host'],t['port'],t['hostkey_fingerprint'])) -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=572|text=def vps_ssh_argv(t, *, use_agent=False): -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=573|text= a=[SSH,'-p',str(t['port']),'-o','BatchMode=yes','-o','StrictHostKeyChecking=yes','-o','UserKnownHostsFile='+str(VPS_KNOWN_HOSTS),'-o','ConnectTimeout=15'] -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=575|text= a += ['-o','IdentitiesOnly=no'] -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=577|text= a += ['-i',str(OPERATOR_KEY),'-o','IdentitiesOnly=yes','-o','IdentityAgent=none'] -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=583|text= return run(vps_ssh_argv(t,use_agent=use_agent)+[remote],input=input,timeout=timeout,check=check) -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=588|text= pub=run(['ssh-keygen','-y','-f',str(OPERATOR_KEY)],timeout=30).stdout.strip()+b'\n' -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=591|text= if t['type'] not in ('vps_md','vps_spb'): continue -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=595|text= if not os.environ.get('SSH_AUTH_SOCK'): raise BackupError('FORWARDED_AGENT_REQUIRED_FOR_BOOTSTRAP') -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=596|text= script=b"umask 077; mkdir -p \"$HOME/.ssh\"; chmod 700 \"$HOME/.ssh\"; touch \"$HOME/.ssh/authorized_keys\"; chmod 600 \"$HOME/.ssh/authorized_keys\"; IFS= read -r k; if grep -qF -- \"$k\" \"$HOME/.ssh/authorized_keys\"; then printf 'ALREADY\\n'; else printf '%s\\n' \"$k\" >> \"$HOME/.ssh/authorized_keys\"; printf 'ADDED\\n'; fi" -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=612|text=def capture_vps_md(t,gen,work): -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=617|text= argv=vps_ssh_argv(t,use_agent=False)+[remote] -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=621|text= return payload,{'generation':gen,'source_class':'vps_md','consistency':'FILESET_PRE_POST_HASH_STABILITY','hashset_sha256':pre},None -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=627|text= argv=vps_ssh_argv(t,use_agent=False)+[remote] -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=664|text= argv=vps_ssh_argv(t,use_agent=False)+[remote] -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=732|text= pat=re.compile(r"\bsshpass\b[^\n]*?(?:^|\s)-f\s+(\"[^\"]*\"|'[^']*'|[^\s;]+)") -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=756|text= roots=[pathlib.Path('/etc'),pathlib.Path('[SENSITIVE_PATH]'),pathlib.Path('/var/lib/homelab-backup'),pathlib.Path('/var/lib/homelab-backup-v2')] -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=784|text= # sshpass requires password prompting to remain enabled; batch mode must stay off. -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=785|text= # The proven SSHPASS_FILE credential path otherwise cannot be used. -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=786|text= argv=[SSHPASS,'-f',str(pf),SSH,'-p','2222','-o','BatchMode=no','-o','NumberOfPasswordPrompts=1','-o','StrictHostKeyChecking=yes','-o','UserKnownHostsFile='+str(ROUTER_KNOWN_HOSTS),'-o','ConnectTimeout=15','routerbackup@'+target,'show running-config'] -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=806|text= p=ssh_remote(node,args,input=input_bytes,timeout=timeout+60) -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=818|text= return f'''import hashlib,json,os,pathlib,select,shutil,subprocess,tempfile,time\nroot={root!r}; db={db!r}; strategy={strategy!r}; token=[REDACTED] not root.startswith('/var/www/forums/') or not db.startswith('xf_'): raise SystemExit(20)\ntmp=pathlib.Path('/tmp/homelab-v2-xf-'+token); tmp.mkdir(mode=0o700,exist_ok=False)\ndump=tmp/'db.sql'; out=pathlib.Path('/tmp/homelab-v2-xf-'+token+'.tar.zst')\nlock=None\ntry:\n if strategy=='global_read_lock':\n lock=subprocess.Popen(['mariadb','--user=root','--batch','--skip-column-names','--unbuffered'],stdin=subprocess.PIPE,stdout=subprocess.PIPE,stderr=subprocess.DEVNULL,text=True,bufsize=1)\n lock.stdin.write("FLUSH TABLES WITH READ LOCK; SELECT 'LOCKED';\\n"); lock.stdin.flush()\n rr,_,_=select.select([lock.stdout],[],[],5)\n if not rr or lock.stdout.readline().strip()!='LOCKED': raise RuntimeError('LOCK_ACQUIRE')\n dump_timeout=15\n else: dump_timeout=180\n with open(dump,'wb') as f:\n argv=['mariadb-dump','--user=ro...[TRUNCATED] -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=851|text= if OPERATOR_KEY.exists(): p.append('root/.ssh/id_ed25519_netbird_operator') -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=852|text= if pathlib.Path(str(OPERATOR_KEY)+'.pub').exists(): p.append('root/.ssh/id_ed25519_netbird_operator.pub') -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=859|text= else: ok=(ssh_remote(node,['test','-e','/'+rel],timeout=20,check=False).returncode==0) -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=868|text= pipe_to_zstd([SSH,'-o','BatchMode=yes','-o','StrictHostKeyChecking=yes','root@'+node,remote],payload,timeout=600) -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=875|text= if typ=='vps_md': return capture_vps_md(t,gen,work) -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=885|text= ssh_remote(cleanup['node'],['rm','-rf','--',cleanup['path']],timeout=60) -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=903|text=def backup_target(t,green_verify=False): -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=991|text= assert {t['logical_id'] for t in default_targets() if t.get('green1')}=={'ct112','node-pve01','md-relay','spb-ingress','us-netbird','router-netcraze','xf-codevipe','xf-zakrutim','control-bootstrap-v2'} -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/representative_driver.py|line=17|text= 'md-relay', -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/wrapper.py|line=7|text=LEGACY_IDS=('node-pve01','md-relay','spb-ingress','us-netbird','router-netcraze','xf-codevipe','xf-zakrutim','control-bootstrap-v2') -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/wrapper.py|line=37|text=def load_targets(): -BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/wrapper.py|line=40|text=def backup_target(t,green_verify=False): -BASE_AUTH_SNIPPET_COUNT=121 -BASE_AUTH_SNIPPETS_END=1 -BASE_KEY_FUNCTIONS_BEGIN=1 -FUNCTION_BEGIN|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|name=load_targets|line=236 -def load_targets(): - j=json.loads(TARGETS_PATH.read_text()) - if j.get('schema')!=1 or not isinstance(j.get('targets'),list): raise BackupError('TARGET_MANIFEST_SCHEMA') - ids=[x.get('logical_id') for x in j['targets']] - if len(ids)!=len(set(ids)): raise BackupError('TARGET_MANIFEST_DUPLICATE') - return j['targets'] -FUNCTION_END|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|name=load_targets -FUNCTION_BEGIN|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|name=cluster_inventory|line=405 -def cluster_inventory(): - p=run(['pvesh','get','/cluster/resources','--type','vm','--output-format','json'],timeout=60) - try:return json.loads(p.stdout) - except Exception: raise BackupError('PVE_INVENTORY_JSON') -FUNCTION_END|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|name=cluster_inventory -FUNCTION_BEGIN|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|name=ssh_remote|line=410 -def ssh_remote(node,argv,input=None,timeout=1800,check=True): - cmd='PATH=/usr/sbin:/usr/bin:/sbin:/bin; export PATH; exec '+shlex.join([str(x) for x in argv]) - if node=='pve01': return run([str(x) for x in argv],input=input,timeout=timeout,check=check) - return run([SSH,'-o','BatchMode=yes','-o','StrictHostKeyChecking=yes','root@'+node,cmd],input=input,timeout=timeout,check=check) -FUNCTION_END|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|name=ssh_remote -FUNCTION_BEGIN|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|name=backup_target|line=903 -def backup_target(t,green_verify=False): - if t['type']=='control': return control_verify(t) - gen=generation_id(t['logical_id']); work=ensure_dir(WORK_ROOT/gen,0o700) - payload=None; cleanup=None; sealed=False - try: - payload,meta,cleanup=capture_target(t,gen,work); meta['generation']=gen - manifest,msha,alloc=upload_and_seal(t,payload,meta); sealed=True - # SEALED-first cleanup invariant: only now remove source/local payload. - try: - if pathlib.Path(payload).exists(): pathlib.Path(payload).unlink() - finally: cleanup_source(cleanup) - if green_verify: - a=work/'verify-a.bin'; b=work/'verify-b.bin' - ha=reconstruct_replica(manifest,manifest['replicas'][0],a); validate_payload(t,a) - hb=reconstruct_replica(manifest,manifest['replicas'][1],b); validate_payload(t,b) - if ha!=hb or ha!=manifest['payload_sha256']: raise BackupError('GREEN_REPLICA_COMPARE') - a.unlink(missing_ok=True); b.unlink(missing_ok=True) - shutil.rmtree(work,ignore_errors=True) - write_state(t['logical_id'],'SUCCESS',generation=gen,payload_sha256=manifest['payload_sha256'],manifest_sha256=msha,replicas=manifest['replicas'],green_verified=bool(green_verify),last_success_epoch=epoch()) - return {'logical_id':t['logical_id'],'generation':gen,'result':'SUCCESS','payload_bytes':manifest['payload_bytes'],'replicas':2,'allocator_healthy':alloc['healthy']} - except BackupError as e: - result='PARTIAL_VALID' if sealed else 'FAIL' - write_state(t['logical_id'],result,generation=gen,reason=e.code,sealed=sealed) - raise -FUNCTION_END|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|name=backup_target -FUNCTION_BEGIN|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/wrapper.py|name=load_targets|line=37 -def load_targets(): - base=canonical_targets();return base+[_ct_descriptor(base)] -FUNCTION_END|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/wrapper.py|name=load_targets -FUNCTION_BEGIN|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/wrapper.py|name=backup_target|line=40 -def backup_target(t,green_verify=False): - if not isinstance(t,dict) or t.get('logical_id')!=CT_ID: - return _legacy_backup_target(t,green_verify) - try: - r=ct112.run_target(t,state_writer=legacy.write_state) - return {'logical_id':CT_ID,'generation':r['generation'],'result':'SUCCESS','payload_bytes':r['payload_bytes'], - 'replicas':2,'manifest_sha256':r['manifest_sha256'],'payload_sha256':r['payload_sha256']} - except BaseException: - try: legacy.write_state(CT_ID,'FAIL',reason='CT112_PIPELINE_FAILED') - except BaseException: pass - raise legacy.BackupError('CT112_PIPELINE_FAILED') from None -FUNCTION_END|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/wrapper.py|name=backup_target -BASE_KEY_FUNCTION_COUNT=6 -BASE_KEY_FUNCTIONS_END=1 -BASE_VPS_MD_CONTEXT_BEGIN=1 -VPS_CONTEXT_BEGIN|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=158 -146:def utcnow(): return dt.datetime.now(dt.timezone.utc).replace(microsecond=0).isoformat().replace('+00:00','Z') -147:def epoch(): return int(time.time()) -148: -149:def default_targets(): -150: d=86400 -151: out=[] -152: # PVE guests. Source node is resolved live every run. -153: for vmid,typ in [(110,'lxc'),(111,'lxc'),(112,'lxc'),(113,'lxc'),(130,'qemu'),(150,'qemu'),(160,'qemu'),(170,'qemu'),(171,'qemu'),(190,'qemu'),(210,'qemu'),(211,'qemu'),(9130,'qemu')]: -154: out.append({'logical_id':('ct' if typ=='lxc' else 'vm')+str(vmid),'scope':'pve','type':'pve_guest','vmid':vmid,'guest_type':typ,'due_seconds':d,'enabled':True,'green1':vmid==112}) -155: for node in ('pve01','pve02','pve03'): -156: out.append({'logical_id':'node-'+node,'scope':'node','type':'pve_node_bundle','node':node,'due_seconds':7*d,'enabled':True,'green1':node=='pve01'}) -157: out += [ -158: {'logical_id':'md-relay','scope':'vps','type':'vps_md','host':'[ADDR]','port':22,'user':'ops','hostkey_fingerprint':'SHA256:DXJGwunC5tEVvnC3nRxyayTN8FXmyJcVgf+Oa6udQsY','due_seconds':d,'enabled':True,'green1':True}, -159: {'logical_id':'spb-ingress','scope':'vps','type':'vps_spb','host':'[ADDR]','port':22,'user':'edgeadmin','hostkey_fingerprint':'SHA256:Ef+HsZb8lIXoadvYBXuGKIL3H7NyJMmf9EYCoJRoSv8','due_seconds':d,'enabled':True,'green1':True}, -160: {'logical_id':'us-netbird','scope':'vps','type':'vps_us','host':'[ADDR]','port':22,'user':'root','hostkey_fingerprint':'SHA256:805Q9eh/fePUci42dfY58I6q0lFiiPHn0/M+mckypmE','due_seconds':d,'enabled':True,'green1':True}, -161: {'logical_id':'router-netcraze','scope':'router','type':'router','due_seconds':d,'enabled':True,'green1':True}, -162: ] -163: sites=[ -164: ('codevipe',160,'pve02','/var/www/forums/codevipe/public','xf_codevipe','single_transaction'), -165: ('dsmods',160,'pve02','/var/www/forums/dsmods/public','xf_dsmods','single_transaction'), -166: ('kingofwolk',160,'pve02','/var/www/forums/kingofwolk/public','xf_kingofwolk','single_transaction'), -167: ('newfi',210,'pve01','/var/www/forums/newfi/public','xf_newfi','single_transaction'), -168: ('gamevipe',160,'pve02','/var/www/forums/gamevipe/public','xf_gamevipe','global_read_lock'), -169: ('hkmods',160,'pve02','/var/www/forums/hkmods/public','xf_hkmods','global_read_lock'), -170: ('zakrutim',160,'pve02','/var/www/forums/zakrutim/public','xf_zakrutim','global_read_lock')] -VPS_CONTEXT_END|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=158 -VPS_CONTEXT_BEGIN|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=389 -377: zstd=shutil.which('zstd') or '/usr/bin/zstd'; tar=shutil.which('tar') or '/usr/bin/tar' -378: p=run([zstd,'-t','-q',str(path)],timeout=300) -379: p1=subprocess.Popen([zstd,'-dc',str(path)],stdout=subprocess.PIPE,stderr=subprocess.PIPE) -380: p2=subprocess.Popen([tar,'-tf','-'],stdin=p1.stdout,stdout=subprocess.PIPE,stderr=subprocess.PIPE) -381: p1.stdout.close(); out,err2=p2.communicate(timeout=300); err1=p1.stderr.read(); r1=p1.wait() -382: if r1!=0 or p2.returncode!=0: raise BackupError('TAR_ZST_LIST_FAILED') -383: names=out.decode('utf-8','replace').splitlines() -384: if not names: raise BackupError('TAR_ZST_EMPTY') -385: if required_member and required_member not in names: raise BackupError('REQUIRED_MEMBER_MISSING') -386: -387:def validate_payload(t,path): -388: typ=t['type'] -389: if typ in ('pve_node_bundle','vps_md','vps_spb','vps_us','router'): -390: validate_tar_zst(path) -391: elif typ=='pve_guest': -392: if t['guest_type']=='lxc': validate_tar_zst(path) -393: else: -394: # Green set is intentionally CT-only. Scheduled QEMU backups still get dual remote byte readback. -395: run([shutil.which('zstd') or '/usr/bin/zstd','-t','-q',str(path)],timeout=600) -396: elif typ=='xenforo': -397: validate_tar_zst(path,'db.sql') -398: zstd=shutil.which('zstd') or '/usr/bin/zstd'; tar=shutil.which('tar') or '/usr/bin/tar' -399: p1=subprocess.Popen([zstd,'-dc',str(path)],stdout=subprocess.PIPE,stderr=subprocess.PIPE) -400: p2=subprocess.Popen([tar,'-xOf','-','db.sql'],stdin=p1.stdout,stdout=subprocess.PIPE,stderr=subprocess.PIPE) -401: p1.stdout.close(); data,err=p2.communicate(timeout=300); p1.wait() -VPS_CONTEXT_END|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=389 -VPS_CONTEXT_BEGIN|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=568 -556: f=line.split() -557: if len(f)<3 or f[1]!='ssh-ed25519': continue -558: try: blob=base64.b64decode(f[2]) -559: except Exception: continue -560: fp='SHA256:'+base64.b64encode(hashlib.sha256(blob).digest()).decode().rstrip('=') -561: if fp==expected: good.append(line) -562: if len(good)!=1: raise BackupError('HOSTKEY_PIN_MISMATCH') -563: return good[0] -564: -565:def ensure_vps_known_hosts(targets): -566: lines=[] -567: for t in targets: -568: if t['type'] not in ('vps_md','vps_spb','vps_us'): continue -569: lines.append(scan_hostkey_line(t['host'],t['port'],t['hostkey_fingerprint'])) -570: atomic_write(VPS_KNOWN_HOSTS,('\n'.join(lines)+'\n').encode(),0o600) -571: -572:def vps_ssh_argv(t, *, use_agent=False): -573: a=[SSH,'-p',str(t['port']),'-o','BatchMode=yes','-o','StrictHostKeyChecking=yes','-o','UserKnownHostsFile='+str(VPS_KNOWN_HOSTS),'-o','ConnectTimeout=15'] -574: if use_agent: -575: a += ['-o','IdentitiesOnly=no'] -576: else: -577: a += ['-i',str(OPERATOR_KEY),'-o','IdentitiesOnly=yes','-o','IdentityAgent=none'] -578: a += [t['user']+'@'+t['host']] -579: return a -580: -VPS_CONTEXT_END|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=568 -VPS_CONTEXT_BEGIN|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=591 -579: return a -580: -581:def vps_run(t, remote_argv, *, input=None, timeout=300, use_agent=False, check=True): -582: remote=shlex.join([str(x) for x in remote_argv]) -583: return run(vps_ssh_argv(t,use_agent=use_agent)+[remote],input=input,timeout=timeout,check=check) -584: -585:def bootstrap_vps_keys(targets): -586: operator_key_fingerprint() -587: ensure_vps_known_hosts(targets) -588: pub=run(['ssh-keygen','-y','-f',str(OPERATOR_KEY)],timeout=30).stdout.strip()+b'\n' -589: results={} -590: for t in targets: -591: if t['type'] not in ('vps_md','vps_spb'): continue -592: probe=vps_run(t,['true'],timeout=30,use_agent=False,check=False) -593: if probe.returncode==0: -594: results[t['logical_id']]='ALREADY_OPERATOR_KEY'; continue -595: if not os.environ.get('SSH_AUTH_SOCK'): raise BackupError('FORWARDED_AGENT_REQUIRED_FOR_BOOTSTRAP') -596: script=b"umask 077; mkdir -p \"$HOME/.ssh\"; chmod 700 \"$HOME/.ssh\"; touch \"$HOME/.ssh/authorized_keys\"; chmod 600 \"$HOME/.ssh/authorized_keys\"; IFS= read -r k; if grep -qF -- \"$k\" \"$HOME/.ssh/authorized_keys\"; then printf 'ALREADY\\n'; else printf '%s\\n' \"$k\" >> \"$HOME/.ssh/authorized_keys\"; printf 'ADDED\\n'; fi" -597: p=vps_run(t,['sh','-c',script.decode()],input=pub,timeout=45,use_agent=True) -598: tag=p.stdout.decode().strip() -599: if tag not in ('ADDED','ALREADY'): raise BackupError('VPS_AUTHORIZED_KEY_BOOTSTRAP_RESULT') -600: vps_run(t,['true'],timeout=30,use_agent=False) -601: results[t['logical_id']]=tag -602: # US was already operator-key based; prove it agentless too. -603: us=[x for x in targets if x['type']=='vps_us'][0] -VPS_CONTEXT_END|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=591 -VPS_CONTEXT_BEGIN|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=612 -600: vps_run(t,['true'],timeout=30,use_agent=False) -601: results[t['logical_id']]=tag -602: # US was already operator-key based; prove it agentless too. -603: us=[x for x in targets if x['type']=='vps_us'][0] -604: vps_run(us,['true'],timeout=30,use_agent=False) -605: results[us['logical_id']]='ALREADY_US_OPERATOR_KEY' -606: return results -607: -608:def remote_hashset(t,command): -609: p=vps_run(t,['sh','-c',command],timeout=120,use_agent=False) -610: return sha256_bytes(p.stdout) -611: -612:def capture_vps_md(t,gen,work): -613: files=['etc/netbird/install.conf','var/lib/netbird/state.json','var/lib/netbird/service.json','var/lib/netbird/active_profile.json','var/lib/netbird/default.json'] -614: cmd="sudo -n sha256sum -- "+' '.join(shlex.quote('/'+x) for x in files) -615: pre=remote_hashset(t,cmd) -616: remote=shlex.join(['sudo','-n','tar','--numeric-owner','-C','/','-cf','-']+files) -617: argv=vps_ssh_argv(t,use_agent=False)+[remote] -618: payload=work/'payload.tar.zst'; pipe_to_zstd(argv,payload,timeout=600) -619: post=remote_hashset(t,cmd) -620: if pre!=post: raise BackupError('MD_SOURCE_CHANGED_DURING_CAPTURE') -621: return payload,{'generation':gen,'source_class':'vps_md','consistency':'FILESET_PRE_POST_HASH_STABILITY','hashset_sha256':pre},None -622: -623:def capture_vps_spb(t,gen,work): -624: hcmd="find /etc/nginx -xdev -type f -print0 | sort -z | xargs -0 sha256sum" -VPS_CONTEXT_END|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=612 -VPS_CONTEXT_BEGIN|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=621 -609: p=vps_run(t,['sh','-c',command],timeout=120,use_agent=False) -610: return sha256_bytes(p.stdout) -611: -612:def capture_vps_md(t,gen,work): -613: files=['etc/netbird/install.conf','var/lib/netbird/state.json','var/lib/netbird/service.json','var/lib/netbird/active_profile.json','var/lib/netbird/default.json'] -614: cmd="sudo -n sha256sum -- "+' '.join(shlex.quote('/'+x) for x in files) -615: pre=remote_hashset(t,cmd) -616: remote=shlex.join(['sudo','-n','tar','--numeric-owner','-C','/','-cf','-']+files) -617: argv=vps_ssh_argv(t,use_agent=False)+[remote] -618: payload=work/'payload.tar.zst'; pipe_to_zstd(argv,payload,timeout=600) -619: post=remote_hashset(t,cmd) -620: if pre!=post: raise BackupError('MD_SOURCE_CHANGED_DURING_CAPTURE') -621: return payload,{'generation':gen,'source_class':'vps_md','consistency':'FILESET_PRE_POST_HASH_STABILITY','hashset_sha256':pre},None -622: -623:def capture_vps_spb(t,gen,work): -624: hcmd="find /etc/nginx -xdev -type f -print0 | sort -z | xargs -0 sha256sum" -625: pre=remote_hashset(t,hcmd) -626: remote=shlex.join(['tar','--numeric-owner','-C','/','-cf','-','etc/nginx']) -627: argv=vps_ssh_argv(t,use_agent=False)+[remote] -628: payload=work/'payload.tar.zst'; pipe_to_zstd(argv,payload,timeout=900) -629: post=remote_hashset(t,hcmd) -630: if pre!=post: raise BackupError('SPB_SOURCE_CHANGED_DURING_CAPTURE') -631: return payload,{'generation':gen,'source_class':'vps_spb','consistency':'STATIC_CONFIG_FILESET_PRE_POST_HASH_STABILITY','tls_recovery':'REISSUE_TLS_ON_RESTORE','hashset_sha256':pre},None -632: -633:US_REMOTE_SCRIPT=r'''import io,os,sqlite3,sys,tarfile,time,json,hashlib -VPS_CONTEXT_END|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=621 -VPS_CONTEXT_BEGIN|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=875 -863: payload=work/'payload.tar.zst' -864: argv=['tar','--numeric-owner','--xattrs','--acls','-C','/','-cf','-']+paths -865: if node=='pve01': pipe_to_zstd(argv,payload,timeout=300) -866: else: -867: remote='PATH=/usr/sbin:/usr/bin:/sbin:/bin; export PATH; exec '+shlex.join(argv) -868: pipe_to_zstd([SSH,'-o','BatchMode=yes','-o','StrictHostKeyChecking=yes','root@'+node,remote],payload,timeout=600) -869: return payload,{'generation':gen,'source_class':'pve_node_bundle','node':node,'recovery_policy':'CLEAN_INSTALL_JOIN_RECONCILE_NOT_BLIND_PMXCFS_OVERWRITE','member_roots':len(paths)},None -870: -871:def capture_target(t,gen,work): -872: typ=t['type'] -873: if typ=='pve_guest': return capture_pve_guest(t,gen,work) -874: if typ=='pve_node_bundle': return capture_node_bundle(t,gen,work) -875: if typ=='vps_md': return capture_vps_md(t,gen,work) -876: if typ=='vps_spb': return capture_vps_spb(t,gen,work) -877: if typ=='vps_us': return capture_vps_us(t,gen,work) -878: if typ=='router': return capture_router(t,gen,work) -879: if typ=='xenforo': return capture_xenforo(t,gen,work) -880: raise BackupError('CAPTURE_TYPE_UNKNOWN') -881: -882:def cleanup_source(cleanup): -883: if not cleanup: return -884: if cleanup.get('kind')=='pve_source_dir': -885: ssh_remote(cleanup['node'],['rm','-rf','--',cleanup['path']],timeout=60) -886: -887:def control_verify(t): -VPS_CONTEXT_END|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=875 -VPS_CONTEXT_BEGIN|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=991 -979: try:j=json.loads(q.stdout) -980: except Exception: continue -981: if j.get('status')=='SEALED' and j.get('generation')==g: bygen.setdefault(g,set()).add(physical) -982: good=sorted(g for g,phys in bygen.items() if len(phys)>=2) -983: print('DISCOVER_LOGICAL_ID='+logical_id); print('DISCOVER_TWO_PHYSICAL_SEALED_COUNT='+str(len(good))) -984: for g in good[-20:]: print('DISCOVER_GENERATION='+g) -985: return 0 if good else 2 -986: -987:def selftest(): -988: assert PART_SIZE==536870912 -989: assert len(REMOTES)==13 and len({p for _,p in REMOTES})==13 -990: assert len(default_targets())>=28 -991: assert {t['logical_id'] for t in default_targets() if t.get('green1')}=={'ct112','node-pve01','md-relay','spb-ingress','us-netbird','router-netcraze','xf-codevipe','xf-zakrutim','control-bootstrap-v2'} -992: assert _pve_dumpdir_spec('lxc')==(0o710,100000) -993: assert _pve_dumpdir_spec('qemu')==(0o700,0) -994: assert GUEST266_SHA=='86433bc424d5fa5017c20f7a237c82874b02357a3283660d8a6b0039ef2bc868' -995: assert P282_SHA=='73c6cb7773eb8e82d1e7ed5ab4733b4afb88f74ae69c09125d0378d6e480e012' -996: if GUEST266.is_file(): -997: assert _isolated_release_graph_selftest() is True -998: av,_=proven_lxc_dump_argv({'vmid':112},{'maxdisk':8350298112,'maxmem':536870912,'status':'running','name':'unbound1'},'pve01','/var/tmp/homelab-vzdump-selftest') -999: assert '--stdout' in av and '--tmpdir' in av and '/var/tmp/homelab-vzdump-selftest' in av -1000: print('SELFTEST_ENGINE396=10') -1001: return 0 -1002: -1003:def main(argv): -VPS_CONTEXT_END|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=991 -VPS_CONTEXT_BEGIN|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/representative_driver.py|line=17 -5:CT112 producer/cloud path. It routes CT112 to an explicitly supplied callback and -6:isolates legacy engine state writes into a candidate-owned state directory. -7:""" -8:from __future__ import annotations -9: -10:import json -11:import os -12:from pathlib import Path -13:from typing import Callable, Iterable, Mapping, Any -14: -15:LEGACY_IDS=( -16: 'node-pve01', -17: 'md-relay', -18: 'spb-ingress', -19: 'us-netbird', -20: 'router-netcraze', -21: 'xf-codevipe', -22: 'xf-zakrutim', -23: 'control-bootstrap-v2', -24:) -25:CT112_ID='guest-pve01-lxc-112' -26:REPRESENTATIVE_IDS=(CT112_ID,)+LEGACY_IDS -27:CANDIDATE_ROOT=Path(__file__).resolve().parent -28:STATE_ROOT=CANDIDATE_ROOT/'state' -29: -VPS_CONTEXT_END|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/representative_driver.py|line=17 -VPS_CONTEXT_BEGIN|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/wrapper.py|line=7 -1:#!/usr/bin/env python3 -2:from __future__ import annotations -3:import errno,fcntl,importlib.util,os,sys -4:from pathlib import Path -5:ROOT=Path(__file__).resolve().parent -6:CT_ID='guest-pve01-lxc-112' -7:LEGACY_IDS=('node-pve01','md-relay','spb-ingress','us-netbird','router-netcraze','xf-codevipe','xf-zakrutim','control-bootstrap-v2') -8:LOCK=Path('/run/homelab-backup-heavy.lock') -9: -10:def _load(name,path): -11: spec=importlib.util.spec_from_file_location(name,str(path)) -12: if spec is None or spec.loader is None: raise RuntimeError('MODULE_SPEC') -13: mod=importlib.util.module_from_spec(spec);sys.modules[name]=mod;spec.loader.exec_module(mod);return mod -14: -15:legacy=_load('_backupv2_legacy486',ROOT/'legacy_engine.py') -16:ct112=_load('_backupv2_ct112_486',ROOT/'ct112_handler.py') -17:driver=_load('_backupv2_driver454_479',ROOT/'representative_driver.py') -18:_legacy_load_targets=legacy.load_targets -19:_legacy_backup_target=legacy.backup_target -VPS_CONTEXT_END|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/wrapper.py|line=7 -BASE_VPS_MD_CONTEXT_COUNT=10 -BASE_VPS_MD_CONTEXT_END=1 -BASE_RELEASE_AUTH_CLASS=BASE_VPS_MD_BACKUP_TARGET_LOCATED -DECISION=PASS_MOLDOVA_1147_BASE_RELEASE_AUTH_COLLECTED -NEXT_GATE=READ_EXACT_1147_AND_BUILD_REMOTE_ROLE_PREFLIGHT_WITH_PROVEN_LEGACY_AUTH -TASK_RESULT=PASS_MOLDOVA_BASE_RELEASE_AUTH -HOMELAB_RESULT_CONTRACT={"version":1,"command_id":"SUPPORT-260924-HOMELAB-MOLDOVA-BASE-RELEASE-AUTH-1147R1","status":"OK","changes_made":false,"rollback_started":false,"rollback_restored":null} -WORKERS2_MOLDOVA_BASE_RELEASE_AUTH_END=1 +GITEA_AUTH_SUPPORT-260924-FLEET-CAPTCHA-SHARED-WIDGET-DOMAINS-RECONCILE-0107R1_TXT_SHA256=3affbd310f46b6f807edb9a4f411460798fa9ddd974238877f6dab21c2afa59d +GITEA_AUTH_SUPPORT-260924-FLEET-CAPTCHA-PRODUCTION-SERIAL-MIGRATION-0048R1_TXT_SHA256=1ab5ca9366b467d0d2ed85bc56364f06e9a867977f5106eb8cdd5d15b14c6c71 +GITEA_AUTH_SUPPORT-260924-FLEET-CAPTCHA-CF-LIST-AUTOSELECT-0006R1_TXT_SHA256=469b6cb88197f3fc7ff461f7b0dd46ea218c58ffa5f944222d339941510ac360 +GITEA_BRANCH_HEAD=df13b49dffd2cc5b4be705ca3a96241b11a53fdd +CF_TOKEN_SHA16=7fb2f75e3a1f8a3b +CF_TOKEN_VERIFY_HTTP=200 +CF_ZONE_LOOKUP_HTTP=200 +CF_PVEPRO_ZONE_MATCH_COUNT=1 +CF_ACCOUNT_ID_SHA256=49ace22b1853724543e1b3b00f02d6a03acd8bd4f9046f8cf5c3c9b6343984f3 +CF_WIDGET_LIST_HTTP=200 +CF_WIDGET_COUNT=2 +SHARED_WIDGET_MODE=managed +SHARED_WIDGET_NAME_SHA256=2ce0d0392662a166fef18981747dffc4e729571d5ebc953f9a5a2ba7b63c4742 +SHARED_WIDGET_DOMAIN_COUNT=10 +SHARED_WIDGET_UNIQUE_DOMAIN_COUNT=10 +SHARED_WIDGET_DOMAIN_01=codevipe.ru +SHARED_WIDGET_DOMAIN_01_CLASS=PRODUCTION_FORUM +SHARED_WIDGET_DOMAIN_02=dsmods.ru +SHARED_WIDGET_DOMAIN_02_CLASS=PRODUCTION_FORUM +SHARED_WIDGET_DOMAIN_03=gamevipe.ru +SHARED_WIDGET_DOMAIN_03_CLASS=PRODUCTION_FORUM +SHARED_WIDGET_DOMAIN_04=gram1.ru +SHARED_WIDGET_DOMAIN_04_CLASS=UNKNOWN +SHARED_WIDGET_DOMAIN_04_UNKNOWN_DNS_V4_COUNT=1 +SHARED_WIDGET_DOMAIN_04_UNKNOWN_HTTPS=200 +SHARED_WIDGET_DOMAIN_05=hkmods.ru +SHARED_WIDGET_DOMAIN_05_CLASS=PRODUCTION_FORUM +SHARED_WIDGET_DOMAIN_06=kingofwolk.ru +SHARED_WIDGET_DOMAIN_06_CLASS=PRODUCTION_FORUM +SHARED_WIDGET_DOMAIN_07=newfi.ru +SHARED_WIDGET_DOMAIN_07_CLASS=PRODUCTION_FORUM +SHARED_WIDGET_DOMAIN_08=newfilya.ru +SHARED_WIDGET_DOMAIN_08_CLASS=UNKNOWN +SHARED_WIDGET_DOMAIN_08_UNKNOWN_DNS_V4_COUNT=1 +SHARED_WIDGET_DOMAIN_08_UNKNOWN_HTTPS=200 +SHARED_WIDGET_DOMAIN_09=pvepro.ru +SHARED_WIDGET_DOMAIN_09_CLASS=PRODUCTION_FORUM +SHARED_WIDGET_DOMAIN_10=zakrutim.ru +SHARED_WIDGET_DOMAIN_10_CLASS=PRODUCTION_FORUM +SHARED_WIDGET_PRODUCTION_FORUM_DOMAIN_COUNT=8 +SHARED_WIDGET_KNOWN_STAGING_DOMAIN_COUNT=0 +SHARED_WIDGET_UNKNOWN_DOMAIN_COUNT=2 +SHARED_WIDGET_DOMAINS_SHA256=55180f23ff32cb88c56d10d162185bcb7a7031be0aa2469ecb97b53f06352df6 +CLASSIFICATION_ENGINE=PYTHON_NO_STDIN_CONSUMPTION +CLASSIFIED_DOMAIN_ROWS=10 +MUTATION_BOUNDARY=READ_ONLY;CLOUDFLARE_GET_ONLY;NO_WIDGET_CREATE_UPDATE_DELETE;NO_QGA;NO_XENFORO;NO_DB;NO_VM_CONFIG;NO_EDGE;NO_BACKUP +SHARED_WIDGET_DOMAIN_RECONCILE_FIXED_COMPLETE=true +DECISION=PASS_FLEET_CAPTCHA_SHARED_WIDGET_DOMAIN_RECONCILE_FIX +NEXT_GATE=REVIEW_UNKNOWN_SHARED_WIDGET_DOMAINS +TASK_RESULT=SHARED_WIDGET_UNKNOWN_DOMAINS_EXACTLY_IDENTIFIED +HOMELAB_RESULT_CONTRACT={"version":1,"command_id":"SUPPORT-260924-FLEET-CAPTCHA-SHARED-WIDGET-DOMAINS-RECONCILE-FIX-0119R1","status":"OK","changes_made":false,"rollback_started":false,"rollback_restored":null} OUTPUT_END CHAT_OUTPUT_END