diff --git a/runtime/history/SUPPORT-260924-HOMELAB-MOLDOVA-BASE-RELEASE-AUTH-1147R1.json b/runtime/history/SUPPORT-260924-HOMELAB-MOLDOVA-BASE-RELEASE-AUTH-1147R1.json new file mode 100644 index 00000000..5b25c8a1 --- /dev/null +++ b/runtime/history/SUPPORT-260924-HOMELAB-MOLDOVA-BASE-RELEASE-AUTH-1147R1.json @@ -0,0 +1,38 @@ +{ + "schema_version": 1, + "channel": "homelab-runtime", + "command_id": "SUPPORT-260924-HOMELAB-MOLDOVA-BASE-RELEASE-AUTH-1147R1", + "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", + "reference_register_checked": true, + "reference_sha256": "5a3d8e5154c41cb582a4e0aca68090be1f0138918bf82131a948df326f9d8d66", + "error_register_checked": true, + "error_register_sha256": "3b09a553ec0f527ed3afeed4753f52a74ee3036045bbfb3c685e9f8af4ba7ba0", + "command_sha256": "f3fe1cb2e034d22c5917b5946f86b1c8ea517c0abd0a92891868a481e2aa5da7", + "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": "add93d5fb111ea9426df8d172911f8506ad95531ae42be7e0c338901a61b8608", + "sanitized_output_sha256": "e60b4493bf44bffbb9ab84838090dbefdb74cfb16219ea4cc0d3a9e834e59ae4", + "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" +} diff --git a/runtime/history/SUPPORT-260924-HOMELAB-MOLDOVA-BASE-RELEASE-AUTH-1147R1.txt b/runtime/history/SUPPORT-260924-HOMELAB-MOLDOVA-BASE-RELEASE-AUTH-1147R1.txt new file mode 100644 index 00000000..030726ff --- /dev/null +++ b/runtime/history/SUPPORT-260924-HOMELAB-MOLDOVA-BASE-RELEASE-AUTH-1147R1.txt @@ -0,0 +1,639 @@ +CHAT_OUTPUT_BEGIN +COMMAND_ID=SUPPORT-260924-HOMELAB-MOLDOVA-BASE-RELEASE-AUTH-1147R1 +STATUS=OK +RC=0 +HOST=pve01 +MODE=read-only +COMPONENT=homelab-moldova-base-release-auth +REFERENCE_REGISTER_CHECK=OK +REFERENCE_SHA256=5a3d8e5154c41cb582a4e0aca68090be1f0138918bf82131a948df326f9d8d66 +ERROR_REGISTER_CHECK=OK +ERROR_REGISTER_SHA256=3b09a553ec0f527ed3afeed4753f52a74ee3036045bbfb3c685e9f8af4ba7ba0 +COMMAND_SHA256=f3fe1cb2e034d22c5917b5946f86b1c8ea517c0abd0a92891868a481e2aa5da7 +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=add93d5fb111ea9426df8d172911f8506ad95531ae42be7e0c338901a61b8608 +SANITIZED_OUTPUT_SHA256=e60b4493bf44bffbb9ab84838090dbefdb74cfb16219ea4cc0d3a9e834e59ae4 +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 + +OUTPUT_END +CHAT_OUTPUT_END diff --git a/runtime/latest.json b/runtime/latest.json index 50082211..5b25c8a1 100644 --- a/runtime/latest.json +++ b/runtime/latest.json @@ -1,27 +1,27 @@ { "schema_version": 1, "channel": "homelab-runtime", - "command_id": "FORUM-260924-EXPAND-8FORUM-SENDERFIX6B91R1", - "status": "FAIL", - "rc": 3, + "command_id": "SUPPORT-260924-HOMELAB-MOLDOVA-BASE-RELEASE-AUTH-1147R1", + "status": "OK", + "rc": 0, "host": "pve01", - "mode": "change", - "component": "forum-eight-fleet-senderfix", - "started_at_utc": "2026-09-23T22:16:58Z", - "finished_at_utc": "2026-09-23T22:17:06Z", + "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", "reference_register_checked": true, "reference_sha256": "5a3d8e5154c41cb582a4e0aca68090be1f0138918bf82131a948df326f9d8d66", "error_register_checked": true, "error_register_sha256": "3b09a553ec0f527ed3afeed4753f52a74ee3036045bbfb3c685e9f8af4ba7ba0", - "command_sha256": "ed810450d2fb903445f957b0071b3f4e8cbd2fd56b21f7ddd860b2b3c273216e", + "command_sha256": "f3fe1cb2e034d22c5917b5946f86b1c8ea517c0abd0a92891868a481e2aa5da7", "duplicate_failed_command_blocked": false, "block_reason": null, "execution_started": true, - "change_declared": true, + "change_declared": false, "result_contract_valid": true, - "result_contract_status": "FAIL", + "result_contract_status": null, "result_contract_error": null, - "command_rc": 3, + "command_rc": 0, "changes_made": false, "rollback_started": false, "rollback_restored": null, @@ -30,9 +30,9 @@ "secrets_included": false, "private_addresses_included": false, "raw_evidence_retained_locally": true, - "raw_evidence_sha256": "7ddda097f070623740ba0c963158c2acfd7761d8b5975667495b88fead821357", - "sanitized_output_sha256": "7ddda097f070623740ba0c963158c2acfd7761d8b5975667495b88fead821357", + "raw_evidence_sha256": "add93d5fb111ea9426df8d172911f8506ad95531ae42be7e0c338901a61b8608", + "sanitized_output_sha256": "e60b4493bf44bffbb9ab84838090dbefdb74cfb16219ea4cc0d3a9e834e59ae4", "output_truncated_in_json": false, "full_sanitized_output_url": "https://git.gram1.ru/.well-known/homelab-runtime/latest.txt", - "output": "CID=FORUM-260924-EXPAND-8FORUM-SENDERFIX6B91R1\nMODE=change\nCOMPONENT=forum-eight-fleet-senderfix\nMUTATION_BOUNDARY=PVE01_FORUM_PUBLIC_SMOKE_6_TO_8;VM160_WATCHER_FLEET_TEXT_ONLY;VM100_NEW_ERROR_WATCHER_EXPLICIT_ENVELOPE_FROM;VM210_NEW_ERROR_WATCHER_EXPLICIT_ENVELOPE_FROM;TWO_RUSSIAN_CANARY_EMAILS;NO_XF_DB_WRITE;NO_XF_ERROR_DELETE;NO_MSMTP_CONFIG_CHANGE;NO_NGINX_PHP_MARIADB_RESTART;NO_VM_CONFIG;NO_CLOUD\nEXECUTION_STARTED=true\nCHANGES_MADE=false\nROLLBACK_STARTED=false\nROLLBACK_RESTORED=null\nALERT_RECIPIENT_SHA256=0d4cf60ca8cd53a12ad50dc5419d3c9c32192f0852000eb10051fbdef52b04bf\nHEALTH_PRE_SHA256=4ee0c432fd906ba2d73198c965d0e57e4e7001781d61161b64f3c51625f034bd\n=== VM100 PREFLIGHT ===\nVM100_STATE=running\nVM100_QGA_TRANSPORT_RC=0\nGUEST_EXITCODE=0\nEXPLICIT_FROM_PRETEND_RC=0\nEXPLICIT_FROM_PRETEND_SHA256=0d4cf60ca8cd53a12ad50dc5419d3c9c32192f0852000eb10051fbdef52b04bf\nROOTS=1\nDOMAINS=pvepro.ru\nFLEET_DOMAINS=codevipe.ru,dsmods.ru,gamevipe.ru,hkmods.ru,kingofwolk.ru,zakrutim.ru,pvepro.ru,newfi.ru\nSOURCE_LABEL=pvepro.ru\nALERT_RECIPIENT_SHA256=0d4cf60ca8cd53a12ad50dc5419d3c9c32192f0852000eb10051fbdef52b04bf\nCONTACT_RECIPIENT_HASH_SET_COUNT=1\nPREFLIGHT=PASS\nALERT_RECIPIENT_SHA256=0d4cf60ca8cd53a12ad50dc5419d3c9c32192f0852000eb10051fbdef52b04bf\nGUEST_PREFLIGHT=PASS\nGUEST_STDERR_PRESENT=false\n=== VM210 PREFLIGHT ===\nVM210_STATE=running\nVM210_QGA_TRANSPORT_RC=0\nGUEST_EXITCODE=0\nEXPLICIT_FROM_PRETEND_RC=0\nEXPLICIT_FROM_PRETEND_SHA256=0d4cf60ca8cd53a12ad50dc5419d3c9c32192f0852000eb10051fbdef52b04bf\nROOTS=1\nDOMAINS=newfi.ru\nFLEET_DOMAINS=codevipe.ru,dsmods.ru,gamevipe.ru,hkmods.ru,kingofwolk.ru,zakrutim.ru,pvepro.ru,newfi.ru\nSOURCE_LABEL=newfi.ru\nALERT_RECIPIENT_SHA256=0d4cf60ca8cd53a12ad50dc5419d3c9c32192f0852000eb10051fbdef52b04bf\nCONTACT_RECIPIENT_HASH_SET_COUNT=1\nPREFLIGHT=PASS\nALERT_RECIPIENT_SHA256=0d4cf60ca8cd53a12ad50dc5419d3c9c32192f0852000eb10051fbdef52b04bf\nGUEST_PREFLIGHT=PASS\nGUEST_STDERR_PRESENT=false\n=== VM160 PREFLIGHT ===\nVM160_STATE=running\nVM160_QGA_TRANSPORT_RC=0\nVM160_GUEST_EXITCODE=80\nVM160_MONITOR_PRE_SHA256=3417dc26c8292ac3b07be8aafb27bd5228d5876bda61bc1a0a38be4e85ea7052\nGUEST_FAILURE=MONITOR_PRE_SHA\nVM160_GUEST_STDERR_PRESENT=false\nFAILURE=VM160_PREFLIGHT_RC_80\nDECISION=FAIL_EIGHT_FORUM_SENDERFIX_PREFLIGHT\nNEXT_GATE=REVIEW_FAILURE\nTASK_RESULT=FAIL_EIGHT_FORUM_SENDERFIX\nHOMELAB_RESULT_CONTRACT={\"version\":1,\"command_id\":\"FORUM-260924-EXPAND-8FORUM-SENDERFIX6B91R1\",\"status\":\"FAIL\",\"changes_made\":false,\"rollback_started\":false,\"rollback_restored\":null}\n" + "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" } diff --git a/runtime/latest.txt b/runtime/latest.txt index a40d9f6e..030726ff 100644 --- a/runtime/latest.txt +++ b/runtime/latest.txt @@ -1,22 +1,22 @@ CHAT_OUTPUT_BEGIN -COMMAND_ID=FORUM-260924-EXPAND-8FORUM-SENDERFIX6B91R1 -STATUS=FAIL -RC=3 +COMMAND_ID=SUPPORT-260924-HOMELAB-MOLDOVA-BASE-RELEASE-AUTH-1147R1 +STATUS=OK +RC=0 HOST=pve01 -MODE=change -COMPONENT=forum-eight-fleet-senderfix +MODE=read-only +COMPONENT=homelab-moldova-base-release-auth REFERENCE_REGISTER_CHECK=OK REFERENCE_SHA256=5a3d8e5154c41cb582a4e0aca68090be1f0138918bf82131a948df326f9d8d66 ERROR_REGISTER_CHECK=OK ERROR_REGISTER_SHA256=3b09a553ec0f527ed3afeed4753f52a74ee3036045bbfb3c685e9f8af4ba7ba0 -COMMAND_SHA256=ed810450d2fb903445f957b0071b3f4e8cbd2fd56b21f7ddd860b2b3c273216e +COMMAND_SHA256=f3fe1cb2e034d22c5917b5946f86b1c8ea517c0abd0a92891868a481e2aa5da7 DUPLICATE_FAILED_COMMAND_BLOCKED=false EXECUTION_STARTED=true -CHANGE_DECLARED=true +CHANGE_DECLARED=false RESULT_CONTRACT_VALID=true -RESULT_CONTRACT_STATUS=FAIL +RESULT_CONTRACT_STATUS=NOT_APPLICABLE RESULT_CONTRACT_ERROR=NONE -COMMAND_RC=3 +COMMAND_RC=0 CHANGES_MADE=false ROLLBACK_STARTED=false ROLLBACK_RESTORED=null @@ -24,63 +24,616 @@ MUTATION_OUTCOME=NO_MUTATION SANITIZED=yes SECRETS_INCLUDED=no PRIVATE_ADDRESSES_INCLUDED=no -RAW_EVIDENCE_SHA256=7ddda097f070623740ba0c963158c2acfd7761d8b5975667495b88fead821357 -SANITIZED_OUTPUT_SHA256=7ddda097f070623740ba0c963158c2acfd7761d8b5975667495b88fead821357 +RAW_EVIDENCE_SHA256=add93d5fb111ea9426df8d172911f8506ad95531ae42be7e0c338901a61b8608 +SANITIZED_OUTPUT_SHA256=e60b4493bf44bffbb9ab84838090dbefdb74cfb16219ea4cc0d3a9e834e59ae4 OUTPUT_BEGIN -CID=FORUM-260924-EXPAND-8FORUM-SENDERFIX6B91R1 -MODE=change -COMPONENT=forum-eight-fleet-senderfix -MUTATION_BOUNDARY=PVE01_FORUM_PUBLIC_SMOKE_6_TO_8;VM160_WATCHER_FLEET_TEXT_ONLY;VM100_NEW_ERROR_WATCHER_EXPLICIT_ENVELOPE_FROM;VM210_NEW_ERROR_WATCHER_EXPLICIT_ENVELOPE_FROM;TWO_RUSSIAN_CANARY_EMAILS;NO_XF_DB_WRITE;NO_XF_ERROR_DELETE;NO_MSMTP_CONFIG_CHANGE;NO_NGINX_PHP_MARIADB_RESTART;NO_VM_CONFIG;NO_CLOUD -EXECUTION_STARTED=true -CHANGES_MADE=false -ROLLBACK_STARTED=false -ROLLBACK_RESTORED=null -ALERT_RECIPIENT_SHA256=0d4cf60ca8cd53a12ad50dc5419d3c9c32192f0852000eb10051fbdef52b04bf -HEALTH_PRE_SHA256=4ee0c432fd906ba2d73198c965d0e57e4e7001781d61161b64f3c51625f034bd -=== VM100 PREFLIGHT === -VM100_STATE=running -VM100_QGA_TRANSPORT_RC=0 -GUEST_EXITCODE=0 -EXPLICIT_FROM_PRETEND_RC=0 -EXPLICIT_FROM_PRETEND_SHA256=0d4cf60ca8cd53a12ad50dc5419d3c9c32192f0852000eb10051fbdef52b04bf -ROOTS=1 -DOMAINS=pvepro.ru -FLEET_DOMAINS=codevipe.ru,dsmods.ru,gamevipe.ru,hkmods.ru,kingofwolk.ru,zakrutim.ru,pvepro.ru,newfi.ru -SOURCE_LABEL=pvepro.ru -ALERT_RECIPIENT_SHA256=0d4cf60ca8cd53a12ad50dc5419d3c9c32192f0852000eb10051fbdef52b04bf -CONTACT_RECIPIENT_HASH_SET_COUNT=1 -PREFLIGHT=PASS -ALERT_RECIPIENT_SHA256=0d4cf60ca8cd53a12ad50dc5419d3c9c32192f0852000eb10051fbdef52b04bf -GUEST_PREFLIGHT=PASS -GUEST_STDERR_PRESENT=false -=== VM210 PREFLIGHT === -VM210_STATE=running -VM210_QGA_TRANSPORT_RC=0 -GUEST_EXITCODE=0 -EXPLICIT_FROM_PRETEND_RC=0 -EXPLICIT_FROM_PRETEND_SHA256=0d4cf60ca8cd53a12ad50dc5419d3c9c32192f0852000eb10051fbdef52b04bf -ROOTS=1 -DOMAINS=newfi.ru -FLEET_DOMAINS=codevipe.ru,dsmods.ru,gamevipe.ru,hkmods.ru,kingofwolk.ru,zakrutim.ru,pvepro.ru,newfi.ru -SOURCE_LABEL=newfi.ru -ALERT_RECIPIENT_SHA256=0d4cf60ca8cd53a12ad50dc5419d3c9c32192f0852000eb10051fbdef52b04bf -CONTACT_RECIPIENT_HASH_SET_COUNT=1 -PREFLIGHT=PASS -ALERT_RECIPIENT_SHA256=0d4cf60ca8cd53a12ad50dc5419d3c9c32192f0852000eb10051fbdef52b04bf -GUEST_PREFLIGHT=PASS -GUEST_STDERR_PRESENT=false -=== VM160 PREFLIGHT === -VM160_STATE=running -VM160_QGA_TRANSPORT_RC=0 -VM160_GUEST_EXITCODE=80 -VM160_MONITOR_PRE_SHA256=3417dc26c8292ac3b07be8aafb27bd5228d5876bda61bc1a0a38be4e85ea7052 -GUEST_FAILURE=MONITOR_PRE_SHA -VM160_GUEST_STDERR_PRESENT=false -FAILURE=VM160_PREFLIGHT_RC_80 -DECISION=FAIL_EIGHT_FORUM_SENDERFIX_PREFLIGHT -NEXT_GATE=REVIEW_FAILURE -TASK_RESULT=FAIL_EIGHT_FORUM_SENDERFIX -HOMELAB_RESULT_CONTRACT={"version":1,"command_id":"FORUM-260924-EXPAND-8FORUM-SENDERFIX6B91R1","status":"FAIL","changes_made":false,"rollback_started":false,"rollback_restored":null} +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 OUTPUT_END CHAT_OUTPUT_END