diff --git a/runtime/history/SUPPORT-260920-PVEPRO-RUSSIAN-UX-STAGING-CLEANUP-739R1.json b/runtime/history/SUPPORT-260920-PVEPRO-RUSSIAN-UX-STAGING-CLEANUP-739R1.json new file mode 100644 index 00000000..70f7a793 --- /dev/null +++ b/runtime/history/SUPPORT-260920-PVEPRO-RUSSIAN-UX-STAGING-CLEANUP-739R1.json @@ -0,0 +1,38 @@ +{ + "schema_version": 1, + "channel": "homelab-runtime", + "command_id": "SUPPORT-260920-PVEPRO-RUSSIAN-UX-STAGING-CLEANUP-739R1", + "status": "FAIL", + "rc": 3, + "host": "pve01", + "mode": "change", + "component": "pvepro-russian-ux-staging-cleanup", + "started_at_utc": "2026-09-20T02:34:31Z", + "finished_at_utc": "2026-09-20T02:38:14Z", + "reference_register_checked": true, + "reference_sha256": "5a3d8e5154c41cb582a4e0aca68090be1f0138918bf82131a948df326f9d8d66", + "error_register_checked": true, + "error_register_sha256": "3b09a553ec0f527ed3afeed4753f52a74ee3036045bbfb3c685e9f8af4ba7ba0", + "command_sha256": "65df9732f610c571636fb5781f4edb94759ad3474790883156388931ec46cc28", + "duplicate_failed_command_blocked": false, + "block_reason": null, + "execution_started": true, + "change_declared": true, + "result_contract_valid": true, + "result_contract_status": "FAIL", + "result_contract_error": null, + "command_rc": 3, + "changes_made": true, + "rollback_started": true, + "rollback_restored": false, + "mutation_outcome": "ROLLBACK_FAILED", + "sanitized": true, + "secrets_included": false, + "private_addresses_included": false, + "raw_evidence_retained_locally": true, + "raw_evidence_sha256": "9eb6c53be7349b4410bac641cbe1975ef8c8c90fcb0f2ca1a52d8d9d24c627ce", + "sanitized_output_sha256": "9eb6c53be7349b4410bac641cbe1975ef8c8c90fcb0f2ca1a52d8d9d24c627ce", + "output_truncated_in_json": false, + "full_sanitized_output_url": "https://git.gram1.ru/.well-known/homelab-runtime/latest.txt", + "output": "W2UX739_BEGIN=True\nCOMMAND_ID=SUPPORT-260920-PVEPRO-RUSSIAN-UX-STAGING-CLEANUP-739R1\nMODE=change\nCOMPONENT=pvepro-russian-ux-staging-cleanup\nVM_SAFETY={\"production_snapshots\":[],\"production_status\":\"running\",\"staging_all_link_down\":true,\"staging_name\":\"pvepro-staging-v1\",\"staging_nic_count\":1,\"staging_snapshots\":[],\"staging_status\":\"stopped\"}\nPRODUCTION_PRESTATE={\"admins\":1,\"boardActive\":true,\"defaultLanguageId\":2,\"enCount\":1,\"errors\":19,\"languageRebuildJobs\":0,\"manualJobs\":0,\"nodes\":[{\"node_id\":1,\"node_type_id\":\"Category\",\"title\":\"Main category\"},{\"node_id\":2,\"node_type_id\":\"Forum\",\"title\":\"Main forum\"}],\"posts\":44,\"registrationEnabled\":false,\"ruCount\":1,\"ruPhraseCount\":11142,\"ruPhraseMapCount\":11194,\"threads\":44}\nSTAGING_SNAPSHOT_CREATED=True\nSTAGING_CLEANUP_ERROR_CLASS=RuntimeError\nSTAGING_CLEANUP_ERROR_SHA256=97bca49c0032b21299dd3981d766c52667d84153c1f07963ae6404d6ac2012f7\nROLLBACK_STARTED=True\nROLLBACK_ERROR_SHA256=97bca49c0032b21299dd3981d766c52667d84153c1f07963ae6404d6ac2012f7\nROLLBACK_RESTORED=False\nTASK_RESULT=PVEPRO_RUSSIAN_UX_STAGING_CLEANUP_FAILED\nHOMELAB_RESULT_CONTRACT={\"changes_made\":true,\"command_id\":\"SUPPORT-260920-PVEPRO-RUSSIAN-UX-STAGING-CLEANUP-739R1\",\"rollback_restored\":false,\"rollback_started\":true,\"status\":\"FAIL\",\"version\":1}\n" +} diff --git a/runtime/history/SUPPORT-260920-PVEPRO-RUSSIAN-UX-STAGING-CLEANUP-739R1.txt b/runtime/history/SUPPORT-260920-PVEPRO-RUSSIAN-UX-STAGING-CLEANUP-739R1.txt new file mode 100644 index 00000000..8e4af57d --- /dev/null +++ b/runtime/history/SUPPORT-260920-PVEPRO-RUSSIAN-UX-STAGING-CLEANUP-739R1.txt @@ -0,0 +1,46 @@ +CHAT_OUTPUT_BEGIN +COMMAND_ID=SUPPORT-260920-PVEPRO-RUSSIAN-UX-STAGING-CLEANUP-739R1 +STATUS=FAIL +RC=3 +HOST=pve01 +MODE=change +COMPONENT=pvepro-russian-ux-staging-cleanup +REFERENCE_REGISTER_CHECK=OK +REFERENCE_SHA256=5a3d8e5154c41cb582a4e0aca68090be1f0138918bf82131a948df326f9d8d66 +ERROR_REGISTER_CHECK=OK +ERROR_REGISTER_SHA256=3b09a553ec0f527ed3afeed4753f52a74ee3036045bbfb3c685e9f8af4ba7ba0 +COMMAND_SHA256=65df9732f610c571636fb5781f4edb94759ad3474790883156388931ec46cc28 +DUPLICATE_FAILED_COMMAND_BLOCKED=false +EXECUTION_STARTED=true +CHANGE_DECLARED=true +RESULT_CONTRACT_VALID=true +RESULT_CONTRACT_STATUS=FAIL +RESULT_CONTRACT_ERROR=NONE +COMMAND_RC=3 +CHANGES_MADE=true +ROLLBACK_STARTED=true +ROLLBACK_RESTORED=false +MUTATION_OUTCOME=ROLLBACK_FAILED +SANITIZED=yes +SECRETS_INCLUDED=no +PRIVATE_ADDRESSES_INCLUDED=no +RAW_EVIDENCE_SHA256=9eb6c53be7349b4410bac641cbe1975ef8c8c90fcb0f2ca1a52d8d9d24c627ce +SANITIZED_OUTPUT_SHA256=9eb6c53be7349b4410bac641cbe1975ef8c8c90fcb0f2ca1a52d8d9d24c627ce +OUTPUT_BEGIN +W2UX739_BEGIN=True +COMMAND_ID=SUPPORT-260920-PVEPRO-RUSSIAN-UX-STAGING-CLEANUP-739R1 +MODE=change +COMPONENT=pvepro-russian-ux-staging-cleanup +VM_SAFETY={"production_snapshots":[],"production_status":"running","staging_all_link_down":true,"staging_name":"pvepro-staging-v1","staging_nic_count":1,"staging_snapshots":[],"staging_status":"stopped"} +PRODUCTION_PRESTATE={"admins":1,"boardActive":true,"defaultLanguageId":2,"enCount":1,"errors":19,"languageRebuildJobs":0,"manualJobs":0,"nodes":[{"node_id":1,"node_type_id":"Category","title":"Main category"},{"node_id":2,"node_type_id":"Forum","title":"Main forum"}],"posts":44,"registrationEnabled":false,"ruCount":1,"ruPhraseCount":11142,"ruPhraseMapCount":11194,"threads":44} +STAGING_SNAPSHOT_CREATED=True +STAGING_CLEANUP_ERROR_CLASS=RuntimeError +STAGING_CLEANUP_ERROR_SHA256=97bca49c0032b21299dd3981d766c52667d84153c1f07963ae6404d6ac2012f7 +ROLLBACK_STARTED=True +ROLLBACK_ERROR_SHA256=97bca49c0032b21299dd3981d766c52667d84153c1f07963ae6404d6ac2012f7 +ROLLBACK_RESTORED=False +TASK_RESULT=PVEPRO_RUSSIAN_UX_STAGING_CLEANUP_FAILED +HOMELAB_RESULT_CONTRACT={"changes_made":true,"command_id":"SUPPORT-260920-PVEPRO-RUSSIAN-UX-STAGING-CLEANUP-739R1","rollback_restored":false,"rollback_started":true,"status":"FAIL","version":1} + +OUTPUT_END +CHAT_OUTPUT_END diff --git a/runtime/latest.json b/runtime/latest.json index d9ed6653..70f7a793 100644 --- a/runtime/latest.json +++ b/runtime/latest.json @@ -1,38 +1,38 @@ { "schema_version": 1, "channel": "homelab-runtime", - "command_id": "NEWFI-260920-NEWFDOM-MESSENGERS214-BASE213", - "status": "OK", - "rc": 0, + "command_id": "SUPPORT-260920-PVEPRO-RUSSIAN-UX-STAGING-CLEANUP-739R1", + "status": "FAIL", + "rc": 3, "host": "pve01", - "mode": "read-only", - "component": "newfi-newfdom-messengers214-backupv2-base-release-authority-aware-readonly", - "started_at_utc": "2026-09-20T02:30:57Z", - "finished_at_utc": "2026-09-20T02:30:57Z", + "mode": "change", + "component": "pvepro-russian-ux-staging-cleanup", + "started_at_utc": "2026-09-20T02:34:31Z", + "finished_at_utc": "2026-09-20T02:38:14Z", "reference_register_checked": true, "reference_sha256": "5a3d8e5154c41cb582a4e0aca68090be1f0138918bf82131a948df326f9d8d66", "error_register_checked": true, "error_register_sha256": "3b09a553ec0f527ed3afeed4753f52a74ee3036045bbfb3c685e9f8af4ba7ba0", - "command_sha256": "65d18e767349b7319c7197b07d4122dae093e606f12244bfd676798e5606e937", + "command_sha256": "65df9732f610c571636fb5781f4edb94759ad3474790883156388931ec46cc28", "duplicate_failed_command_blocked": false, "block_reason": null, "execution_started": true, - "change_declared": false, + "change_declared": true, "result_contract_valid": true, - "result_contract_status": null, + "result_contract_status": "FAIL", "result_contract_error": null, - "command_rc": 0, - "changes_made": false, - "rollback_started": false, - "rollback_restored": null, - "mutation_outcome": "NO_MUTATION", + "command_rc": 3, + "changes_made": true, + "rollback_started": true, + "rollback_restored": false, + "mutation_outcome": "ROLLBACK_FAILED", "sanitized": true, "secrets_included": false, "private_addresses_included": false, "raw_evidence_retained_locally": true, - "raw_evidence_sha256": "417d54bc3ee03b085ea2894f80cd2f3c4e8df0652fe89b0f35fa9f5df73e61c3", - "sanitized_output_sha256": "c2da47960582cda8715a7fd595b714f908f504dd277254ed2b277fdb5c900bf3", + "raw_evidence_sha256": "9eb6c53be7349b4410bac641cbe1975ef8c8c90fcb0f2ca1a52d8d9d24c627ce", + "sanitized_output_sha256": "9eb6c53be7349b4410bac641cbe1975ef8c8c90fcb0f2ca1a52d8d9d24c627ce", "output_truncated_in_json": false, "full_sanitized_output_url": "https://git.gram1.ru/.well-known/homelab-runtime/latest.txt", - "output": "NEWFI213_BEGIN=true\nMODE=read-only\nIMMUTABLE_PLAN_CHECK=PASS\nCHANGES_MADE=false\nMUTATION_OUTCOME=NO_MUTATION\nRUNNER_SHA256=b248a4c32c9cc64e5747e7dce6c7fc0a23f5124a77c71ce72e27a81aceae9d2d\nRUNNER_PIN_MATCH=true\nERROR_REGISTER_SHA256=3b09a553ec0f527ed3afeed4753f52a74ee3036045bbfb3c685e9f8af4ba7ba0\nERROR_REGISTER_PIN_MATCH=true\nREFERENCE_SHA256=5a3d8e5154c41cb582a4e0aca68090be1f0138918bf82131a948df326f9d8d66\nREFERENCE_PIN_MATCH=true\nWRAPPER_SHA256=5077444065c732451cb84898680f62361b21a24ef9eb4f191253e82ead524ea2\nWRAPPER_PIN_MATCH=true\nOVERLAY_SHA256=ca6876fa9c3b1465b045344a8ad46962de16972c7dadc4ac6257c7d225047793\nOVERLAY_PIN_MATCH=true\nPRODUCTION_ACCEPTANCE_SHA256=708a481f6719ff1ed8d0afec1902650ed36ac631e980611b015419f504f16df0\nPRODUCTION_ACCEPTANCE_PIN_MATCH=true\nAUTHORITY_PIN_MATCH=true\nBACKUP_SOURCE_PIN_MATCH=true\nACCEPTED_RELEASE=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1\nBASE_RELEASE=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1\nMATCHING_FILE_COUNT=26\nFILE_1_PATH=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/ACCEPTANCE.json\nFILE_1_BYTES=517\nFILE_1_SHA256=904dfccad43b55ad0b53cfbd29fb45fee36fd0fbfa5afa58da5c3cca0ee7153b\nFILE_1_CONTEXT_LINE_COUNT=1\nFILE_1_L1={\"command_id\":\"SUPPORT-260916-INFRA-DR-BACKUPV2-FINAL-REPLACEMENT-PLAN-STABILITY-SCHEMA-RCA-FIX-VALIDATE-PROMOTE-CUTOVER-SEAL-486R1\",\"ct112_manifest_sha256\":\"1ac4f16a54bcdd8b896c9a131abef966d315256aa56da3f83ed0b30845c4fcb0\",\"ct112_payload_bytes\":223875308,\"ct112_payload_sha256\":\"d443b85e76e806830fd9c372d0a09fc5b91ca9c9d35c44fb0ad7968cc996d01d\",\"legacy_ids\":[\"node-pve01\",\"md-relay\",\"spb-ingress\",\"us-netbird\",\"router-netcraze\",\"xf-codevipe\",\"xf-zakrutim\",\"control-bootstrap-v2\"],\"representative_pass\":9,\"schema\":1}\nFILE_2_PATH=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/RELEASE_MANIFEST.json\nFILE_2_BYTES=3301\nFILE_2_SHA256=a36dd0db88592ec3d355ef43e6b218da6c9ad649e9ccf8c8f680b96eb23a156e\nFILE_2_CONTEXT_LINE_COUNT=1\nFILE_2_L1={\"files\":{\"ACCEPTANCE.json\":{\"bytes\":517,\"mode\":256,\"sha256\":\"904dfccad43b55ad0b53cfbd29fb45fee36fd0fbfa5afa58da5c3cca0ee7153b\"},\"candidate411/CONTRACT.json\":{\"bytes\":2077,\"mode\":256,\"sha256\":\"a414715f1b91b3701c52073709a80c38218f76e3961618e8af61489211e50c01\"},\"candidate411/PLAN265_REFERENCE.json\":{\"bytes\":7210,\"mode\":256,\"sha256\":\"8d36e82080be93a8c929a9211191fa868166b9e7b6152c4c7689fa52b6720757\"},\"candidate411/PROVENANCE.json\":{\"bytes\":4310,\"mode\":256,\"sha256\":\"303f44dc8e91c8e6a675f9ea406ce4b7532ed98440a9b6807fa593ede9d33254\"},\"candidate411/adapter.py\":{\"bytes\":2989,\"mode\":320,\"sha256\":\"18eb29cef165371b48bcb78e1a456e1939e6eff253787218133162cc89ab9fa3\"},\"candidate411/guest266.py\":{\"bytes\":7385,\"mode\":256,\"sha256\":\"86433bc424d5fa5017c20f7a237c82874b02357a3283660d8a6b0039ef2bc868\"},\"candidate411/large264.py\":{\"bytes\":16514,\"mode\":256,\"sha256\":\"5cc4641fc35e4dd554495a36146da7dbbb7f142d1450e04cc5d3c89678b8497a\"},\"candidate411/nodeplan263.py\":{\"bytes\":4603,\"mode\":256,\"sha256\":\"db5c16ee24d25aa\nFILE_3_PATH=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/ct112_handler.py\nFILE_3_BYTES=10955\nFILE_3_SHA256=8cac6e627d5e7abe5d3163cdd24911995a1e3f45c776f9373c2aa68784f5ec6c\nFILE_3_CONTEXT_LINE_COUNT=98\nFILE_3_L1=#!/usr/bin/env python3\nFILE_3_L2=from __future__ import annotations\nFILE_3_L3=import hashlib,json,os,secrets,shutil,stat,subprocess,sys,time\nFILE_3_L4=from pathlib import Path\nFILE_3_L5=ROOT=Path(__file__).resolve().parent\nFILE_3_L6=CAND=ROOT/'candidate411'\nFILE_3_L7=if str(CAND) not in sys.path:sys.path.insert(0,str(CAND))\nFILE_3_L8=import guest266,large264 as large,pve265,pve282,transport261 as t,v2core as core\nFILE_3_L9=CT_ID='guest-pve01-lxc-112'\nFILE_3_L10=PLAN=CAND/'PLAN265_REFERENCE.json'\nFILE_3_L11=PLAN_SHA='8d36e82080be93a8c929a9211191fa868166b9e7b6152c4c7689fa52b6720757'\nFILE_3_L12=RCLONE=Path('/usr/bin/rclone'); RCLONE_SHA='20df3d5eac5d4e187ce638e4216354e7fa6778c3c51f0caf48c48acca9686354'\nFILE_3_L13=RCLONE_CFG=Path('/root/.config/rclone/[SENSITIVE_FILE]')\nFILE_3_L14=BINDING_FP='ed48c1345771c3e0f50db9516f13f26bb66047283e2f883d5df272f15e2282b8'\nFILE_3_L15=VZDUMP=Path('/usr/bin/vzdump'); VZDUMP_SHA='21339d009afd55cb7ec613b64c1f493b2a7dbd18b85b4d712909ef6d6710f4ea'\nFILE_3_L16=ZSTD=Path('/usr/bin/zstd'); ZSTD_SHA='3eb7744cf8fd1ecd46d617098ee870b3e7911fe570ffaba662c1c5b7d6456020'\nFILE_3_L26= p=subprocess.run(argv,stdout=subprocess.PIPE,stderr=subprocess.PIPE,timeout=45,check=False,env=ENV)\nFILE_3_L27= if p.returncode!=0 or len(p.stdout)>4*1024*1024:raise RuntimeError('PVESH_READ_FAILED')\nFILE_3_L28= try:return json.loads(p.stdout.decode('utf-8','strict'))\nFILE_3_L29= except (ValueError,UnicodeError):raise RuntimeError('PVESH_JSON') from None\nFILE_3_L30=\nFILE_3_L31=def _baseline():\nFILE_3_L32= b=t.read_secure(PLAN,1024*1024,private=True)[0]\nFILE_3_L33= if t.sha(b)!=PLAN_SHA:raise RuntimeError('PLAN_REFERENCE_IDENTITY')\nFILE_3_L34= try:return json.loads(b.decode('utf-8','strict'))\nFILE_3_L35= except (ValueError,UnicodeError):raise RuntimeError('PLAN_REFERENCE_JSON') from None\nFILE_3_L36=\nFILE_3_L37=def _defaults():\nFILE_3_L38= p=Path('/etc/vzdump.conf');st=os.stat(p,follow_symlinks=False)\nFILE_3_L100= return {'path':str(path),'sha256':want}\nFILE_3_L101=\nFILE_3_L102=def _private_config():\nFILE_3_L103= b,identity=t.read_secure(RCLONE_CFG,t.MAX_CONFIG_BYTES,private=True)\nFILE_3_L104= text=b.decode('utf-8-sig','strict');entries=core.inventory_from_config(text);binding=t.binding(text)\nFILE_3_L105= if len(entries)!=13 or len({x['account_id'] for x in entries})!=13:raise RuntimeError('RCLONE_INVENTORY')\nFILE_3_L106= if hashlib.sha256(binding.encode()).hexdigest()!=BINDING_FP:raise RuntimeError('RCLONE_BINDING')\nFILE_3_L107= return b,entries,binding\nFILE_3_L108=\nFILE_3_L109=def _write_private(path,data):\nFILE_3_L118=class LazyReader:\nFILE_3_L119= def __init__(self,work,entries,binding,guard,deadline,logical_id):\nFILE_3_L120= self.work=Path(work);self.entries=entries;self.binding=binding;self.guard=guard;self.deadline=deadline;self.logical_id=logical_id\nFILE_3_L121= self.snapshot=self.work/'rclone.snapshot';self.inner=None\nFILE_3_L122= def _ensure(self):\nFILE_3_L123= if self.inner is not None:return self.inner\nFILE_3_L124= b,e,bind=_private_config()\nFILE_3_L125= if e!=self.entries or bind!=self.binding:raise RuntimeError('RCLONE_SEMANTIC_DRIFT')\nFILE_3_L126= _write_private(self.snapshot,b)\nFILE_3_L127= self.inner=large.ReadOnlyLarge(str(RCLONE),self.snapshot,e,bind,self.guard,self.work,self.deadline,self.logical_id)\nFILE_3_L128= return self.inner\nFILE_3_L129= def list_generations(self,*a,**k):return self._ensure().list_generations(*a,**k)\nFILE_3_L130= def get(self,*a,**k):return self._ensure().get(*a,**k)\nFILE_3_L131= def read_stream(self,*a,**k):return self._ensure().read_stream(*a,**k)\nFILE_3_L132= def close(self):\nFILE_3_L135=\nFILE_3_L136=def selftest():\nFILE_3_L137= if _sha(PLAN,1024*1024)!=PLAN_SHA:return False\nFILE_3_L138= for p,h in ((RCLONE,RCLONE_SHA),(VZDUMP,VZDUMP_SHA),(ZSTD,ZSTD_SHA)):\nFILE_3_L139= if _sha(p)!=h:return False\nFILE_3_L140= return True\nFILE_3_L141=\nFILE_3_L142=def run_target(target,state_writer=None):\nFILE_3_L143= if not isinstance(target,dict) or target.get('logical_id')!=CT_ID or target.get('vmid')!=112:return (_ for _ in ()).throw(RuntimeError('CT112_DESCRIPTOR'))\nFILE_3_L144= if _sha(PLAN,1024*1024)!=PLAN_SHA:raise RuntimeError('PLAN_REFERENCE_IDENTITY')\nFILE_3_L145= tools={'vzdump':_tool(VZDUMP,VZDUMP_SHA),'zstd':_tool(ZSTD,ZSTD_SHA)};_tool(RCLONE,RCLONE_SHA)\nFILE_3_L146= pvesh_sha=_sha(PVESH);now=int(time.time());current=_fresh_plan(now);job=_select_live_ct112(current,now)\nFILE_3_L147= expected=job['config_sha256'];mapping=_canonical_host_mapping()\nFILE_3_L148= def producer_guard():\nFILE_3_L153= pve282.live_gate(fresh,str(PVESH),pvesh_sha,expected)\nFILE_3_L154= producer_guard()\nFILE_3_L155= cfg,entries,binding=_private_config()\nFILE_3_L156= generation='V2-'+time.strftime('%Y%m%dT%H%M%SZ',time.gmtime())+'-'+secrets.token_hex(6)\nFILE_3_L157= work=STAGING/('backup-v2-'+generation);work.mkdir(mode=0o700,parents=False,exist_ok=False)\nFILE_3_L158= deadline=time.monotonic()+21600\nFILE_3_L159= def cloud_guard():\nFILE_3_L160= _,e,b=_private_config()\nFILE_3_L161= if e!=entries or b!=binding:raise RuntimeError('RCLONE_SEMANTIC_DRIFT')\nFILE_3_L162= writer=large.CloudLarge(str(RCLONE),RCLONE_CFG,entries,binding,cloud_guard,work/'upload',deadline)\nFILE_3_L163= reader=LazyReader(work/'restore',entries,binding,cloud_guard,deadline,CT_ID)\nFILE_3_L164= def produce(j,w):return pve282.produce(j,w,tools,producer_guard,mapping=mapping,expected_config=expected,seconds=14400,emit=lambda k,v:None)\nFILE_3_L165= def compression(path):\nFILE_3_L166= p=subprocess.run([str(ZSTD),'--test','--quiet','--',str(path)],stdout=subprocess.DEVNULL,stderr=subprocess.DEVNULL,timeout=3600,check=False,env=ENV)\nFILE_3_L167= return p.returncode==0\nFILE_3_L168= try:\nFILE_3_L169= r=guest266.run_flow(job,work,entries,writer,reader,produce,producer_guard,cloud_guard,compression,generation,lambda k,v:None)\nFILE_3_L170= reps=r.get('replicas')\nFILE_3_L171= if not isinstance(reps,list) or len(reps)!=2 or len({x.get('account_id') for x in reps})!=2:raise RuntimeError('REPLICA_PHYSICAL_INDEPENDENCE')\nFILE_3_L172= if r.get('archive_cloud_restore_verified') is not True or r.get('local_payload_files_remaining')!=0:raise RuntimeError('CT112_RESTORE_CONTRACT')\nFILE_3_L173= out={**r,'status':'OK','cloud_only_independent_restore_verified':True,'local_payload_files_remaining':0}\nFILE_3_L174= reader.close();shutil.rmtree(work)\nFILE_3_L175= if work.exists():raise RuntimeError('CT112_WORKSPACE_CLEANUP')\nFILE_3_L176= if state_writer is not None:\nFILE_3_L177= state_writer(CT_ID,'SUCCESS',generation=r['generation'],payload_sha256=r['payload_sha256'],manifest_sha256=r['manifest_sha256'],replicas=r['replicas'],green_verified=True)\nFILE_3_L178= return out\nFILE_3_L179= except BaseException:\nFILE_3_L180= try:reader.close()\nFILE_3_L181= except BaseException:pass\nFILE_3_L182= raise\nFILE_4_PATH=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py\nFILE_4_BYTES=59719\nFILE_4_SHA256=b6e79f087b9f1d21dac4f77a7b46b743299bbb85bc716e80d2ea67c2e038bcd7\nFILE_4_CONTEXT_LINE_COUNT=180\nFILE_4_L1=#!/usr/bin/python3\nFILE_4_L2=import ast, base64, datetime as dt, hashlib, io, json, os, pathlib, random, re, select, shlex, shutil, stat, subprocess, sys, tarfile, tempfile, time\nFILE_4_L3=\nFILE_4_L4=PART_SIZE=536870912\nFILE_4_L5=RESERVE_BYTES=536870912\nFILE_4_L6=STATE_ROOT=pathlib.Path('/var/lib/homelab-backup-v2')\nFILE_4_L7=CONFIG_ROOT=pathlib.Path('/etc/homelab-backup-v2')\nFILE_4_L8=TARGETS_PATH=CONFIG_ROOT/'targets.json'\nFILE_4_L9=WORK_ROOT=pathlib.Path('/mnt/staging/homelab-backup-v2')\nFILE_4_L10=ENGINE_PATH=pathlib.Path('/usr/local/sbin/homelab-backup-v2')\nFILE_4_L11=RCLONE=shutil.which('rclone') or '/usr/bin/rclone'\nFILE_4_L12=SSH=shutil.which('ssh') or '/usr/bin/ssh'\nFILE_4_L13=SCP=shutil.which('scp') or '/usr/bin/scp'\nFILE_4_L14=SSH_KEYSCAN=shutil.which('ssh-keyscan') or '/usr/bin/ssh-keyscan'\nFILE_4_L30=P282_SHA='73c6cb7773eb8e82d1e7ed5ab4733b4afb88f74ae69c09125d0378d6e480e012'\nFILE_4_L31=P265=pathlib.Path('/opt/homelab-backup-v2/releases/pve-producer-265r1/pve265.py')\nFILE_4_L32=P265_SHA='cd2f634414a8e38a3789015aeca5a9b47821f77d7d1fa61dcdbdc866172963be'\nFILE_4_L33=RESULT_CLASSES=('SUCCESS','DEFER','PARTIAL_VALID','FAIL')\nFILE_4_L34=HISTORICAL_RELEASE_GRAPH=[\nFILE_4_L35= (pathlib.Path('/opt/homelab-backup-v2/releases/core-260r1/v2core.py'),'e285bf9a5678d07dcbb90261f9f93e7826ff524e04cfec780bf5a3c4b0dc29f2'),\nFILE_4_L36= (pathlib.Path('/opt/homelab-backup-v2/releases/transport-261r1/transport261.py'),'b9b92f957bf441cb5d1702f4881897328e6c0338f8c3f6d41d27d9a349b37688'),\nFILE_4_L51= pathlib.Path('/opt/homelab-backup-v2/releases/large-cloud-264r1'),\nFILE_4_L52= pathlib.Path('/opt/homelab-backup-v2/releases/stream-263r1'),\nFILE_4_L53= pathlib.Path('/opt/homelab-backup-v2/releases/transport-261r1'),\nFILE_4_L54= pathlib.Path('/opt/homelab-backup-v2/releases/restore-262r1'),\nFILE_4_L55= pathlib.Path('/opt/homelab-backup-v2/releases/core-260r1'),\nFILE_4_L56= pathlib.Path('/opt/homelab-backup-v2/releases/vm-backup-283r1'),\nFILE_4_L57=]\nFILE_4_L58=_HISTORICAL_HELPER = r'''import importlib,importlib.util,json,pathlib,sys\nFILE_4_L59=mode=sys.argv[1]\nFILE_4_L60=if mode=='import_all':\nFILE_4_L61= out={}\nFILE_4_L74='''\nFILE_4_L75=os.umask(0o077)\nFILE_4_L76=\nFILE_4_L77=REMOTES=[\nFILE_4_L78= ('pve01-mail-01-crypt','pve01-mail-01'),('pve01-mail-02-crypt','pve01-mail-02'),('pve01-mail-03-crypt','pve01-mail-03'),('pve01-mail-04-crypt','pve01-mail-04'),('pve01-mail-05-crypt','pve01-mail-05'),\nFILE_4_L79= ('pve02-mail-01-crypt','pve02-mail-01'),('pve02-mail-02-crypt','pve02-mail-02'),('pve02-mail-03-crypt','pve02-mail-03'),('pve02-mail-04-crypt','pve02-mail-04'),\nFILE_4_L80= ('pve03-mail-01-crypt','pve03-mail-01'),('pve03-mail-02-crypt','pve03-mail-02'),('pve03-mail-03-crypt','pve03-mail-03'),('pve03-mail-04-crypt','pve03-mail-04')]\nFILE_4_L81=\nFILE_4_L82=class BackupError(RuntimeError):\nFILE_4_L83= def __init__(self, code, detail=''):\nFILE_4_L102= if not b: break\nFILE_4_L103= h.update(b); n+=len(b)\nFILE_4_L104= if remain is not None: remain-=len(b)\nFILE_4_L105= if length is not None and n!=length: raise BackupError('LOCAL_PART_SHORT_READ')\nFILE_4_L106= return h.hexdigest()\nFILE_4_L107=\nFILE_4_L108=def part_plan(n):\nFILE_4_L109= if not isinstance(n,int) or n<=0: raise BackupError('PAYLOAD_SIZE_INVALID')\nFILE_4_L110= out=[]; off=0; idx=1\nFILE_4_L111= while offtimeout:\nFILE_4_L314= p.kill(); p.wait(); raise BackupError('RCLONE_CAT_TIMEOUT')\nFILE_4_L315= b=p.stdout.read(1024*1024)\nFILE_4_L316= if not b: break\nFILE_4_L317= h.update(b); n+=len(b)\nFILE_4_L318= p.stderr.read(); rc=p.wait()\nFILE_4_L319= if rc!=0: raise BackupError('RCLONE_CAT_FAILED',str(rc))\nFILE_4_L320= if expected_bytes is not None and n!=expected_bytes: raise BackupError('RCLONE_CAT_SIZE_MISMATCH')\nFILE_4_L321= return h.hexdigest(),n\nFILE_4_L322=\nFILE_4_L323=def rclone_cat_bytes(remote_path,max_bytes=4*1024*1024):\nFILE_4_L324= p=run(rclone_base_args()+['cat',remote_path,'--contimeout','20s','--timeout','60s'],timeout=90)\nFILE_4_L325= if len(p.stdout)>max_bytes: raise BackupError('RCLONE_SMALL_OBJECT_TOO_LARGE')\nFILE_4_L326= return p.stdout\nFILE_4_L327=\nFILE_4_L328=def generation_id(logical_id):\nFILE_4_L329= ts=dt.datetime.now(dt.timezone.utc).strftime('%Y%m%dT%H%M%SZ')\nFILE_4_L330= return f'v2-{logical_id}-{ts}-{os.urandom(6).hex()}'\nFILE_4_L331=\nFILE_4_L332=def upload_and_seal(t,payload_path,source_meta):\nFILE_4_L333= payload_path=pathlib.Path(payload_path); n=payload_path.stat().st_size\nFILE_4_L334= psha=sha256_file(payload_path); reps,bad,healthy=allocator(n)\nFILE_4_L335= gen=source_meta['generation']; rel=remote_generation_path(t['scope'],t['logical_id'],gen)\nFILE_4_L336= if remote_exists_any(t['scope'],t['logical_id'],gen): raise BackupError('GENERATION_COLLISION')\nFILE_4_L337= parts=part_plan(n)\nFILE_4_L338= for p in parts: p['sha256']=sha256_file(payload_path,p['offset'],p['bytes'])\nFILE_4_L339= manifest=make_manifest(t['scope'],t['logical_id'],gen,n,psha,parts,reps,source_meta)\nFILE_4_L340= mb=canonical_json(manifest)+b'\\n'; msha=sha256_bytes(mb)\nFILE_4_L341= for rep in reps:\nFILE_4_L342= base=rep['crypt']+':'+rel\nFILE_4_L343= for part in parts:\nFILE_4_L344= rp=base+'/parts/'+part['name']\nFILE_5_PATH=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/representative_driver.py\nFILE_5_BYTES=4214\nFILE_5_SHA256=b3050a8f95e6d9826a4f0786cd3ca32012b6ab10486d10e4981b50b8a0affff2\nFILE_5_CONTEXT_LINE_COUNT=60\nFILE_5_L3=\nFILE_5_L4=This module does not discover credentials, modify scheduler state, or implement the\nFILE_5_L5=CT112 producer/cloud path. It routes CT112 to an explicitly supplied callback and\nFILE_5_L6=isolates legacy engine state writes into a candidate-owned state directory.\nFILE_5_L7=\"\"\"\nFILE_5_L8=from __future__ import annotations\nFILE_5_L9=\nFILE_5_L10=import json\nFILE_5_L11=import os\nFILE_5_L12=from pathlib import Path\nFILE_5_L13=from typing import Callable, Iterable, Mapping, Any\nFILE_5_L14=\nFILE_5_L15=LEGACY_IDS=(\nFILE_5_L16= 'node-pve01',\nFILE_5_L24=)\nFILE_5_L25=CT112_ID='guest-pve01-lxc-112'\nFILE_5_L26=REPRESENTATIVE_IDS=(CT112_ID,)+LEGACY_IDS\nFILE_5_L27=CANDIDATE_ROOT=Path(__file__).resolve().parent\nFILE_5_L28=STATE_ROOT=CANDIDATE_ROOT/'state'\nFILE_5_L29=\nFILE_5_L30=class ContractError(RuntimeError):\nFILE_5_L31= pass\nFILE_5_L36= raise ContractError('TARGET_LOGICAL_ID')\nFILE_5_L37= return value\nFILE_5_L38=\nFILE_5_L39=def partition_targets(targets: Iterable[Mapping[str, Any]]):\nFILE_5_L40= selected={}\nFILE_5_L41= wanted=set(REPRESENTATIVE_IDS)\nFILE_5_L42= for target in targets:\nFILE_5_L62= with os.fdopen(fd,'wb',closefd=True) as f:\nFILE_5_L63= f.write(data);f.flush();os.fsync(f.fileno())\nFILE_5_L64= os.replace(tmp,path)\nFILE_5_L65= except BaseException:\nFILE_5_L66= try: os.unlink(tmp)\nFILE_5_L67= except FileNotFoundError: pass\nFILE_5_L68= raise\nFILE_5_L72= def write_state(logical_id,result,**kw):\nFILE_5_L73= if logical_id not in LEGACY_IDS:\nFILE_5_L74= raise ContractError('STATE_TARGET_FORBIDDEN')\nFILE_5_L75= payload={'logical_id':logical_id,'result':result,'meta':kw,'schema_version':1}\nFILE_5_L76= _atomic_json(root/(logical_id+'.json'),payload)\nFILE_5_L77= return write_state\nFILE_5_L78=\nFILE_5_L79=def run_legacy_lane(engine, target: Mapping[str, Any]):\nFILE_5_L97= raise ContractError('CT112_RESULT_SCHEMA')\nFILE_5_L98= if result.get('logical_id')!=CT112_ID or result.get('status')!='OK':\nFILE_5_L99= raise ContractError('CT112_RESULT_STATUS')\nFILE_5_L100= if result.get('cloud_only_independent_restore_verified') is not True:\nFILE_5_L101= raise ContractError('CT112_RESTORE_NOT_VERIFIED')\nFILE_5_L102= if result.get('local_payload_files_remaining')!=0:\nFILE_5_L103= raise ContractError('CT112_LOCAL_PAYLOAD_REMAINS')\nFILE_5_L104= return result\nFILE_5_L105=\nFILE_5_L106=def run_representative(engine, targets, ct112_callback: Callable[[Mapping[str, Any]], Any]):\nFILE_5_L107= if not callable(ct112_callback):\nFILE_5_L108= raise ContractError('CT112_CALLBACK_REQUIRED')\nFILE_5_L109= legacy,ct112=partition_targets(targets)\nFILE_5_L110= results=[_validate_ct112_result(ct112_callback(ct112))]\nFILE_5_L111= for target in legacy:\nFILE_5_L112= results.append(run_legacy_lane(engine,target))\nFILE_6_PATH=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/wrapper.py\nFILE_6_BYTES=5055\nFILE_6_SHA256=2b5eb4fc0f6f466a0d3e5a007af987092b6c17baa3ff9241a6afc2c2e97df1d5\nFILE_6_CONTEXT_LINE_COUNT=43\nFILE_6_L1=#!/usr/bin/env python3\nFILE_6_L2=from __future__ import annotations\nFILE_6_L3=import errno,fcntl,importlib.util,os,sys\nFILE_6_L4=from pathlib import Path\nFILE_6_L5=ROOT=Path(__file__).resolve().parent\nFILE_6_L6=CT_ID='guest-pve01-lxc-112'\nFILE_6_L7=LEGACY_IDS=('node-pve01','md-relay','spb-ingress','us-netbird','router-netcraze','xf-codevipe','xf-zakrutim','control-bootstrap-v2')\nFILE_6_L35= 'guest_type':'lxc','due_seconds':due,'enabled':True,'green1':True}\nFILE_6_L36=\nFILE_6_L37=def load_targets():\nFILE_6_L38= base=canonical_targets();return base+[_ct_descriptor(base)]\nFILE_6_L39=\nFILE_6_L40=def backup_target(t,green_verify=False):\nFILE_6_L41= if not isinstance(t,dict) or t.get('logical_id')!=CT_ID:\nFILE_6_L42= return _legacy_backup_target(t,green_verify)\nFILE_6_L43= try:\nFILE_6_L44= r=ct112.run_target(t,state_writer=legacy.write_state)\nFILE_6_L45= return {'logical_id':CT_ID,'generation':r['generation'],'result':'SUCCESS','payload_bytes':r['payload_bytes'],\nFILE_6_L46= 'replicas':2,'manifest_sha256':r['manifest_sha256'],'payload_sha256':r['payload_sha256']}\nFILE_6_L47= except BaseException:\nFILE_6_L48= try: legacy.write_state(CT_ID,'FAIL',reason='CT112_PIPELINE_FAILED')\nFILE_6_L49= except BaseException: pass\nFILE_6_L50= raise legacy.BackupError('CT112_PIPELINE_FAILED') from None\nFILE_6_L51=\nFILE_6_L52=legacy.load_targets=load_targets\nFILE_6_L53=legacy.backup_target=backup_target\nFILE_6_L54=\nFILE_6_L55=def _legacy_reused_boundary_selftest():\nFILE_6_L56= if legacy.PART_SIZE!=536870912: raise RuntimeError('LEGACY_PART_SIZE')\nFILE_6_L57= if len(legacy.REMOTES)!=13 or len({p for _,p in legacy.REMOTES})!=13: raise RuntimeError('LEGACY_REMOTES')\nFILE_6_L58= d=legacy.default_targets()\nFILE_6_L59= if not isinstance(d,list) or len(d)<28: raise RuntimeError('LEGACY_DEFAULT_TARGET_COUNT')\nFILE_6_L60= want={'ct112',*LEGACY_IDS}\nFILE_6_L67=\nFILE_6_L68=def selftest():\nFILE_6_L69= _legacy_reused_boundary_selftest()\nFILE_6_L70= base=canonical_targets(); eff=load_targets()\nFILE_6_L71= if len(eff)!=29 or sum(1 for x in eff if x.get('logical_id')==CT_ID)!=1: raise RuntimeError('EFFECTIVE_TARGETS')\nFILE_6_L72= legacy_rows,ct=driver.partition_targets(eff)\nFILE_6_L73= if [x['logical_id'] for x in legacy_rows]!=list(LEGACY_IDS) or ct.get('vmid')!=112: raise RuntimeError('REPRESENTATIVE_PARTITION')\nFILE_6_L74= if ct112.selftest() is not True: raise RuntimeError('CT112_HANDLER_SELFTEST')\nFILE_6_L75= print('SELFTEST_REPLACEMENT486=PASS',flush=True);return 0\nFILE_6_L76=\nFILE_7_PATH=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/CONTRACT.json\nFILE_7_BYTES=2077\nFILE_7_SHA256=a414715f1b91b3701c52073709a80c38218f76e3961618e8af61489211e50c01\nFILE_7_CONTEXT_LINE_COUNT=1\nFILE_7_L1={\"candidate\":\"pve-lxc-adapter-411r1\",\"next_gate\":\"PVE_LXC_REAL_ENVIRONMENT_DRY_RUN\",\"owner\":{\"MappedScratch\":\"pve270\",\"capture_archive\":\"pve270\",\"dump_argv\":\"pve265\",\"pve282.old\":\"pve265\",\"pve282.proven\":\"pve270\",\"validate_mapping\":\"pve270\"},\"schema\":1,\"signatures\":{\"pve265\":{\"dump_argv\":{\"args\":[\"job\",\"executable\",\"tmpdir\",\"now\"],\"defaults\":0,\"kw_defaults\":[],\"kwarg\":null,\"kwonly\":[],\"posonly\":[],\"vararg\":null},\"make_plan\":{\"args\":[\"rows\",\"configs\",\"storage_rows\",\"defaults\",\"staging_free\",\"now\",\"node\"],\"defaults\":1,\"kw_defaults\":[],\"kwarg\":null,\"kwonly\":[],\"posonly\":[],\"vararg\":null},\"produce\":{\"args\":[\"job\",\"work\",\"tools\",\"guard\",\"seconds\"],\"defaults\":1,\"kw_defaults\":[],\"kwarg\":null,\"kwonly\":[],\"posonly\":[],\"vararg\":null},\"validate_job\":{\"args\":[\"job\",\"now\"],\"defaults\":0,\"kw_defaults\":[],\"kwarg\":null,\"kwonly\":[],\"posonly\":[],\"vararg\":null}},\"pve270\":{\"capture_archive\":{\"args\":[\"argv\",\"destination\",\"cap\",\"seconds\",\"check\"],\"defaults\":0,\"kw_defaults\":[],\"kwarg\":null,\"kwonly\":[],\"posonl\nFILE_8_PATH=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/PLAN265_REFERENCE.json\nFILE_8_BYTES=7210\nFILE_8_SHA256=8d36e82080be93a8c929a9211191fa868166b9e7b6152c4c7689fa52b6720757\nFILE_8_CONTEXT_LINE_COUNT=1\nFILE_8_L1={\"all_guest_coverage_eligible\":true,\"backup_executed\":false,\"capacity_policy\":\"four archive copies plus 4GiB; capped producer; actual compressed size still unknown\",\"collected_at_utc\":\"2026-09-12T06:57:40.611329+00:00\",\"full_host_coverage\":false,\"global_blockers\":[],\"global_defaults\":{\"blockers\":[],\"keys\":[],\"present\":true,\"sha256\":\"cf34c8e3812547954bb1d08b50212b6e148ee9cf2b629e895a843ed49d623b38\"},\"guest_membership_and_configs_stable\":true,\"jobs\":[{\"application_consistency_verified\":false,\"archive_cap_bytes\":9516036916,\"blockers\":[],\"cdrom_count\":0,\"cdrom_media_included\":false,\"compression_dependent\":false,\"config_sha256\":\"09957aacd41ba9eeaf5fd4eda13936d580c9082a46895433379aa9dd0bad5969\",\"configured_archive_bound_bytes\":9516036916,\"configured_disk_bytes\":8589934592,\"configured_full_cycle_fits\":true,\"coverage_eligible\":true,\"disks\":[{\"backup_setting\":\"included_default_rootfs\",\"class\":\"managed-volume\",\"configured_size_bytes\":8589934592,\"slot\":\"rootfs\",\"storage\":\"local-lvm\"}],\"global_def\nFILE_9_PATH=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/PROVENANCE.json\nFILE_9_BYTES=4310\nFILE_9_SHA256=303f44dc8e91c8e6a675f9ea406ce4b7532ed98440a9b6807fa593ede9d33254\nFILE_9_CONTEXT_LINE_COUNT=1\nFILE_9_L1={\"anchors\":{\"SUPPORT-260915-INFRA-DR-BACKUPV2-PVE-LXC-EXACT-CALL-GRAPH-PROOF-401R1\":\"840b3f1e8b023899c0554305a97af2d55dc71cf9\",\"SUPPORT-260915-INFRA-DR-BACKUPV2-PVE-LXC-IMMUTABLE-ADAPTER-CANDIDATE-STAGE-FIXTURE-RCA-FIX-407R1\":\"0261475cdd35d3b762de0a29e46f3acd0b9af229\",\"SUPPORT-260915-INFRA-DR-BACKUPV2-PVE-LXC-IMMUTABLE-ADAPTER-CANDIDATE-STAGE-RCA-FIX-403R1\":\"e3f753ce9cf95e21750e9ad7f9574cafc199b3ce\",\"SUPPORT-260915-INFRA-DR-BACKUPV2-PVE-LXC-PERL270-HISTORICAL-TEST-RCA-404R1\":\"58f61ce62c3b6a57d8c5fc2465d2537d6728f9d5\",\"SUPPORT-260915-INFRA-DR-BACKUPV2-PVE-LXC-PERLPIN-EXEC-FS-RCA-409R1\":\"0db666a5a1f4796b567663adfc5568817e87efa5\",\"SUPPORT-260915-INFRA-DR-BACKUPV2-PVE-LXC-PERLPIN-NEED-TRACE-RCA-408R1\":\"44cdff7dab9e60aff1d69c12d2c32a4b26d04de8\",\"SUPPORT-260915-INFRA-DR-BACKUPV2-PVE-LXC-PERLPIN-PRODUCTION-VS-FIXTURE-RCA-406R1\":\"4c5836ff0ef73cb2ef306857be713383ba5e85f4\",\"SUPPORT-260915-INFRA-DR-BACKUPV2-PVE-LXC-PERLPIN-ROOT-SUITE-STRUCTURED-RCA-410R1\":\"4673bfaf04877370bc4ac02fef37885eb82cb1eb\nFILE_10_PATH=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/adapter.py\nFILE_10_BYTES=2989\nFILE_10_SHA256=18eb29cef165371b48bcb78e1a456e1939e6eff253787218133162cc89ab9fa3\nFILE_10_CONTEXT_LINE_COUNT=30\nFILE_10_L1=#!/usr/bin/python3\nFILE_10_L2=import hashlib,inspect,json,pathlib,sys\nFILE_10_L3=BASE=pathlib.Path(__file__).resolve().parent\nFILE_10_L4=EXPECTED={\nFILE_10_L5='v2core.py':'e285bf9a5678d07dcbb90261f9f93e7826ff524e04cfec780bf5a3c4b0dc29f2',\nFILE_10_L6='transport261.py':'b9b92f957bf441cb5d1702f4881897328e6c0338f8c3f6d41d27d9a349b37688',\nFILE_10_L7='restore262.py':'56d1b961c8d5654d5e2ee2755268db46bd2202bca11e69788febf991cfaa79f1',\nFILE_10_L8='stream263.py':'a83d03a86b93a855716658652dd073fcb281cd387fc6200fe4bc2a922f39ea16',\nFILE_10_L9='nodeplan263.py':'db5c16ee24d25aa1694530332d3190ab7cc5d4f8eb69067d025e27ad5c995060',\nFILE_10_L10='large264.py':'5cc4641fc35e4dd554495a36146da7dbbb7f142d1450e04cc5d3c89678b8497a',\nFILE_10_L19= return h.hexdigest()\nFILE_10_L20=def verify_sources():\nFILE_10_L21= for name,want in EXPECTED.items():\nFILE_10_L22= p=BASE/name\nFILE_10_L23= if not p.is_file() or p.is_symlink() or sha(p)!=want:raise RuntimeError('SOURCE_IDENTITY_'+name)\nFILE_10_L24=def load_graph():\nFILE_10_L25= import guest266,pve265,pve270,pve282\nFILE_10_L26= if hasattr(guest266,'dump_argv'):raise RuntimeError('GUEST266_UNEXPECTED_DUMP_ARGV')\nFILE_10_L27= if not hasattr(pve265,'dump_argv'):raise RuntimeError('PVE265_DUMP_ARGV_MISSING')\nFILE_10_L28= if getattr(pve270,'old',None) is not pve265:raise RuntimeError('PVE270_OLD_NOT_PVE265')\nFILE_10_L35=def describe():\nFILE_10_L36= _,p265,p270,p282=load_graph()\nFILE_10_L37= funcs={'pve265.dump_argv':p265.dump_argv,'pve265.validate_job':p265.validate_job,'pve265.make_plan':p265.make_plan,'pve270.validate_mapping':p270.validate_mapping,'pve270.capture_archive':p270.capture_archive,'pve270.produce':p270.produce,'pve282.select112':p282.select112,'pve282.produce':p282.produce}\nFILE_10_L38= return {'schema':1,'bundle':'pve-lxc-adapter-411r1','source_sha256':EXPECTED,'signatures':{k:str(inspect.signature(v)) for k,v in funcs.items()},'owner':{'dump_argv':'pve265','MappedScratch':'pve270','capture_archive':'pve270','validate_mapping':'pve270','pve282.old':'pve265','pve282.proven':'pve270'}}\nFILE_10_L39=def main():\nFILE_10_L40= verify_sources(); mode=sys.argv[1] if len(sys.argv)>1 else 'selftest'; d=describe()\nFILE_10_L41= if mode not in ('selftest','describe'):raise SystemExit(2)\nFILE_10_L42= print(json.dumps(d,sort_keys=True,separators=(',',':')))\nFILE_10_L43= if mode=='selftest':print('ADAPTER_SELFTEST=PASS')\nFILE_10_L44=if __name__=='__main__':main()\nFILE_11_PATH=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/guest266.py\nFILE_11_BYTES=7385\nFILE_11_SHA256=86433bc424d5fa5017c20f7a237c82874b02357a3283660d8a6b0039ef2bc868\nFILE_11_CONTEXT_LINE_COUNT=75\nFILE_11_L1=\"\"\"266R1 bounded real-guest orchestration. Import has no side effects.\nFILE_11_L2=\nFILE_11_L3=One small LXC rootfs, no compression-dependent admission. No node-wide approval,\nFILE_11_L4=no guest restore/start/delete, no cloud deletion. Archive CRC/SHA is not boot proof.\nFILE_11_L5=\"\"\"\nFILE_11_L6=import copy,os,time\nFILE_11_L7=from pathlib import Path\nFILE_11_L8=import v2core as core\nFILE_11_L9=import transport261 as t\nFILE_11_L10=import stream263 as stream\nFILE_11_L11=import large264 as large\nFILE_11_L12=import pve265 as producer\nFILE_11_L13=\nFILE_11_L14=MAX_CONFIGURED_BYTES=8*1024**3\nFILE_11_L15=RESERVE_BYTES=536870912\nFILE_11_L25=\nFILE_11_L26=def choose(plan,now):\nFILE_11_L27= rows=plan_rows(plan);t.need(plan.get('global_blockers')==[],'GLOBAL_DEFAULT_BLOCK')\nFILE_11_L28= candidates=[]\nFILE_11_L29= for job in rows:\nFILE_11_L30= if not (job.get('type')=='lxc' and job.get('coverage_eligible') is True and\nFILE_11_L31= job.get('blockers')==[] and job.get('compression_dependent') is False and\nFILE_11_L35= disks[0].get('class')=='managed-volume' and disks[0].get('backup_setting') in ('included','included_default_rootfs')):continue\nFILE_11_L36= n=job.get('configured_disk_bytes')\nFILE_11_L37= if type(n) is not int or not 0=max(1024**3,chosen['configured_disk_bytes']),'SNAPSHOT_HEADROOM_UNPROVEN')\nFILE_11_L45= return copy.deepcopy(chosen)\nFILE_11_L46=\nFILE_11_L47=def same_state(before,after,vmid):\nFILE_11_L48= old=plan_rows(before);new=plan_rows(after)\nFILE_11_L64= if type(task.get('endtime')) is int and task.get('status') not in (None,'RUNNING'):continue\nFILE_11_L65= kind=task['type'];target=str(task.get('id',''))\nFILE_11_L66= if kind in ('vncshell','vncterm','vncproxy','spiceproxy'):continue\nFILE_11_L67= t.need(target!=str(vmid) and not any(x in kind.lower() for x in ('dump','restore','migrat','snapshot','clone','move_disk','resize')),'COMPETING_PVE_TASK')\nFILE_11_L68=\nFILE_11_L69=def check_result(result,job,work):\nFILE_11_L70= t.need(type(result) is dict,'PRODUCER_RESULT_SCHEMA')\nFILE_11_L80= finally:os.close(fd)\nFILE_11_L81= return expected\nFILE_11_L82=\nFILE_11_L83=def run_flow(job,work,entries,cloud,reader,produce,state_guard,guard,compression_check,generation,say,clock=lambda:int(time.time())):\nFILE_11_L84= \"\"\"Caller provides real fresh PVE guards; simulation callbacks live in tests only.\"\"\"\nFILE_11_L85= producer.validate_job(job,clock());core.validate_inventory(entries)\nFILE_11_L86= t.need(job['type']=='lxc' and job.get('compression_dependent') is False,'FIRST_GUEST_POLICY')\nFILE_11_L87= t.need(core.valid_name(generation),'GENERATION_ID')\nFILE_11_L88= t.need(all(callable(f) for f in (produce,state_guard,guard,compression_check,say,clock)),'CALLBACKS_REQUIRED')\nFILE_11_L89= work=Path(work);state_guard();guard()\nFILE_11_L90= for sub in ('producer','upload','restore'):t.make_dir(work/sub,exclusive=True)\nFILE_11_L91= say('PHASE','REAL_SINGLE_GUEST_SNAPSHOT')\nFILE_11_L92= produced=produce(job,work/'producer')\nFILE_11_L93= source=check_result(produced,job,work/'producer');state_guard();guard()\nFILE_11_L94= t.create(work/'PRODUCER.json',core.canonical_bytes(produced))\nFILE_11_L95= say('PRODUCER_RC',0);say('PAYLOAD_BYTES',produced['size']);say('ARCHIVE_SHA256',produced['sha256'])\nFILE_11_L96= logical='guest-%s-%s-%d'%(job['node'],job['type'],job['vmid'])\nFILE_11_L97= say('PHASE','TWO_REPLICA_UPLOAD_AND_READBACK')\nFILE_11_L98= up=large.upload(source,work/'upload',entries,cloud,generation,logical,RESERVE_BYTES,guard,clock=clock)\nFILE_11_L99= t.need(up['manifest']['payload_bytes']==produced['size'] and up['manifest']['payload_sha256']==produced['sha256'],'MANIFEST_PRODUCER_MISMATCH')\nFILE_11_L100= say('SEALED_QUORUM_VERIFIED','YES');mh=core.manifest_hash(up['manifest']);say('MANIFEST_SHA256',mh)\nFILE_11_L101= say('PHASE','CLOUD_DISCOVERY_AND_ARCHIVE_RECONSTRUCTION')\nFILE_11_L102= found=large.discover(reader,entries,logical,mh)\nFILE_11_L103= restored=large.restore(work/'restore',reader,found)\nFILE_11_L104= t.need(compression_check(work/'restore/restored.bin') is True,'RECONSTRUCTED_COMPRESSION_FAILED')\nFILE_11_L105= state_guard();guard();say('PHASE','GUARDED_LOCAL_PAYLOAD_CLEANUP')\nFILE_11_L106= count=large.cleanup(up,restored,reader,guard)\nFILE_11_L107= return {'state':'SINGLE_GUEST_ARCHIVE_SEALED_AND_CLOUD_RECONSTRUCTED','vmid':job['vmid'],'type':job['type'],'node':job['node'],\nFILE_11_L108= 'generation':generation,'logical_id':logical,'manifest_sha256':mh,'payload_bytes':produced['size'],'payload_sha256':produced['sha256'],\nFILE_11_L109= 'replicas':up['manifest']['replicas'],'parts_count':len(up['manifest']['parts']),'archive_cloud_restore_verified':True,\nFILE_11_L110= 'guest_restore_verified':False,'application_consistency_verified':False,'whole_node_backup':False,'local_payload_files_remaining':0,\nFILE_11_L111= 'files_deleted':count,'accounts_scanned':found['accounts_scanned'],'config_sha256':job['config_sha256']}\nFILE_12_PATH=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/large264.py\nFILE_12_BYTES=16514\nFILE_12_SHA256=5cc4641fc35e4dd554495a36146da7dbbb7f142d1450e04cc5d3c89678b8497a\nFILE_12_CONTEXT_LINE_COUNT=180\nFILE_12_L1=\"\"\"264R1 streaming cloud adapter and synthetic boundary proof.\nFILE_12_L2=\nFILE_12_L3=No work on import. No guest/backup producer and no cloud deletion. Existing\nFILE_12_L4=small-canary adapters are not modified. Only caller-owned local payload is removed,\nFILE_12_L5=after two cloud seals and a separately cloud-discovered restore have been checked.\nFILE_12_L6=\"\"\"\nFILE_12_L7=import hashlib\nFILE_12_L8=import os\nFILE_12_L9=from pathlib import Path\nFILE_12_L10=import selectors\nFILE_12_L11=import signal\nFILE_12_L12=import subprocess\nFILE_12_L13=import time\nFILE_12_L14=import v2core as core\nFILE_12_L15=import transport261 as t\nFILE_12_L16=import restore262 as r\nFILE_12_L17=import stream263 as s\nFILE_12_L18=\nFILE_12_L19=BOUNDARY_BYTES=536870929\nFILE_12_L20=LOGICAL_ID='stream-canary-pve01'\nFILE_12_L21=\nFILE_12_L22=\nFILE_12_L23=def process_stream(argv,env,size,digest,consume,seconds):\nFILE_12_L24= \"\"\"Drain both pipes, hashing stdout incrementally; never retain the whole part.\"\"\"\nFILE_12_L25= t.need(type(argv) is list and argv and seconds>0,'STREAM_PROCESS_ARGUMENTS')\nFILE_12_L26= sink=s.DigestSink(size,digest)\nFILE_12_L27= p=subprocess.Popen(argv,stdin=subprocess.DEVNULL,stdout=subprocess.PIPE,stderr=subprocess.PIPE,\nFILE_12_L38= remaining=deadline-time.monotonic();t.need(remaining>0,'STREAM_TIMEOUT')\nFILE_12_L39= rc=p.wait(timeout=remaining);t.need(rc==0,'STREAM_PROCESS_RC_FAILED')\nFILE_12_L40= result=sink.finish();complete=True;return result\nFILE_12_L41= except subprocess.TimeoutExpired:raise t.Stop('STREAM_TIMEOUT') from None\nFILE_12_L42= finally:\nFILE_12_L43= if not complete or p.poll() is None:\nFILE_12_L44= try:os.killpg(p.pid,signal.SIGKILL)\nFILE_12_L53= '--cache-dir',str(Path(work)/'cache')]+args\nFILE_12_L54=\nFILE_12_L55=\nFILE_12_L56=class CloudLarge(t.RcloneIO):\nFILE_12_L57= \"\"\"Reuse the existing crypt inventory/namespace guards, not its in-memory payload I/O.\"\"\"\nFILE_12_L58= def call(self,args,limit):\nFILE_12_L59= t.need(type(args) is list and args and args[0] in ('about','lsjson','copyto','cat'),'RCLONE_VERB_FORBIDDEN')\nFILE_12_L60= t.need(type(limit) is int and 0<=limit<=core.MANIFEST_MAX_BYTES+1,'METADATA_BOUND')\nFILE_12_L61= self.check();seconds=min(3600 if args[0]=='copyto' else 90,self.deadline-time.monotonic())\nFILE_12_L62= t.need(seconds>0,'CLOUD_DEADLINE');self.calls+=1\nFILE_12_L63= if args[0]=='copyto':self.writes_attempted+=1\nFILE_12_L64= rc,out=t.bounded_process(flags(self.executable,self.config_path,self.work,args),self.env,limit,seconds)\nFILE_12_L65= self.check();return rc,out\nFILE_12_L66= def put(self,remote,name,path):\nFILE_12_L67= dest=self.address(remote,name)\nFILE_12_L68= if name in ('manifest.json','SEALED.json'):\nFILE_12_L69= return super().put(remote,name,path)\nFILE_12_L70= part=next((p for p in self.manifest['parts'] if p['name']==name),None)\nFILE_12_L71= t.need(part is not None,'PART_NOT_IN_MANIFEST')\nFILE_12_L72= n,h,identity=s.hash_file(path,part['size'])\nFILE_12_L73= t.need(n==part['size'] and h==part['sha256'],'UPLOAD_SOURCE_HASH')\nFILE_12_L74= rc,_=self.call(['lsjson',dest,'--stat'],65536)\nFILE_12_L75= t.need(rc in (3,4),'OBJECT_NOT_PROVEN_ABSENT')\nFILE_12_L76= with s.file_handle(path,part['size']) as (_,size,seen):\nFILE_12_L77= t.need(seen==identity,'UPLOAD_SOURCE_CHANGED')\nFILE_12_L78= rc,_=self.call(['copyto',str(path),dest,'--immutable','--no-update-modtime'],65536)\nFILE_12_L79= t.need(rc==0,'CLOUD_COPY_FAILED')\nFILE_12_L80= def read_stream(self,remote,name,size,digest,consume):\nFILE_12_L81= address=self.address(remote,name)\nFILE_12_L82= part=next((p for p in self.manifest['parts'] if p['name']==name),None)\nFILE_12_L83= t.need(part is not None and part['size']==size and part['sha256']==digest,'READBACK_EXPECTATION')\nFILE_12_L84= self.check();seconds=min(3600,self.deadline-time.monotonic());t.need(seconds>0,'CLOUD_DEADLINE')\nFILE_12_L85= self.calls+=1\nFILE_12_L86= out=process_stream(flags(self.executable,self.config_path,self.work,['cat',address,'--count',str(size+1)]),\nFILE_12_L90=\nFILE_12_L91=class ReadOnlyLarge(r.ReadOnlyCloud):\nFILE_12_L92= \"\"\"Metadata uses inherited bounded reads; this class has no upload API.\"\"\"\nFILE_12_L93= def read_stream(self,remote,generation,name,size,digest,consume):\nFILE_12_L94= t.need(type(size) is int and 00,'RESTORE_DEADLINE')\nFILE_12_L98= self.calls+=1\nFILE_12_L99= out=process_stream(flags(self.executable,self.snapshot,self.work,['cat',address,'--count',str(size+1)]),\nFILE_12_L100= self.env,size,digest,consume,seconds)\nFILE_12_L116=\nFILE_12_L117=\nFILE_12_L118=def receipt(rep,m,whole,count):\nFILE_12_L119= t.need(count==m['payload_bytes'] and whole.hexdigest()==m['payload_sha256'],'PAYLOAD_READBACK_MISMATCH')\nFILE_12_L120= return {**rep,'generation':m['generation'],'manifest_sha256':core.manifest_hash(m),\nFILE_12_L121= 'payload_sha256':whole.hexdigest(),'payload_bytes':count,'parts':m['parts'],\nFILE_12_L122= 'method':'download-sha256','ok':True}\nFILE_12_L123=\nFILE_12_L124=\nFILE_12_L125=def upload(source,work,entries,cloud,generation,logical_id,reserve,guard,clock=lambda:int(time.time())):\nFILE_12_L126= \"\"\"Upload and seal; retain all source bytes here. Cleanup is a later gated operation.\"\"\"\nFILE_12_L127= work=Path(work);source=Path(source);guard();core.validate_inventory(entries)\nFILE_12_L128= t.need(core.valid_name(generation) and core.valid_name(logical_id),'GENERATION_ID')\nFILE_12_L129= partdir=work/'parts';t.make_dir(partdir,exclusive=True)\nFILE_12_L130= record=s.split_file(source,partdir)\nFILE_12_L131= t.create(work/'parts-record.json',core.canonical_bytes(record))\nFILE_12_L132= quotas={e['account_id']:cloud.quota(e['crypt_remote']) for e in entries}\nFILE_12_L133= allocation=core.allocate(entries,quotas,record['payload_bytes'],reserve,clock())\nFILE_12_L134= m=core.make_manifest(generation,logical_id,record['payload_bytes'],record['payload_sha256'],record['parts'],allocation)\nFILE_12_L135= mb=core.canonical_bytes(m);mh=core.manifest_hash(m);cloud.bind(m)\nFILE_12_L136= t.create(work/'allocation.json',core.canonical_bytes(allocation));t.create(work/'manifest.json',mb)\nFILE_12_L137= for rep in m['replicas']:\nFILE_12_L138= guard();cloud.absent(rep['crypt_remote'])\nFILE_12_L139= receipts=[]\nFILE_12_L140= for rep in m['replicas']:\nFILE_12_L141= guard();remote=rep['crypt_remote'];q=cloud.quota(remote)\nFILE_12_L142= t.need(core.quota_reason(q,clock()) is None and q['free']>=allocation['required_free_bytes'],'SELECTED_ACCOUNT_CAPACITY_CHANGED')\nFILE_12_L143= s.verify_parts(partdir,record)\nFILE_12_L144= for part in m['parts']:cloud.put(remote,part['name'],partdir/part['name'])\nFILE_12_L145= cloud.put(remote,'manifest.json',work/'manifest.json')\nFILE_12_L146= t.need(cloud.get(remote,'manifest.json',len(mb)+1)==mb,'MANIFEST_READBACK_MISMATCH')\nFILE_12_L147= whole=hashlib.sha256();count=0\nFILE_12_L148= for part in m['parts']:\nFILE_12_L149= observed=cloud.read_stream(remote,part['name'],part['size'],part['sha256'],whole.update)\nFILE_12_L150= t.need(observed=={'size':part['size'],'sha256':part['sha256']},'PART_READBACK_MISMATCH');count+=observed['size']\nFILE_12_L151= receipts.append(receipt(rep,m,whole,count));t.create(work/('receipt-%d.json'%len(receipts)),core.canonical_bytes(receipts[-1]))\nFILE_12_L152= core.verify_receipts(m,receipts)\nFILE_12_L153= seal={'schema_version':2,'state':'SEALED','generation':generation,'logical_id':logical_id,\nFILE_12_L154= 'manifest_sha256':mh,'replicas':m['replicas'],'receipts':receipts,\nFILE_12_L155= 'acceptance':'IDENTICAL_SEAL_REQUIRED_ON_BOTH_NAMED_ACCOUNTS'}\nFILE_12_L156= sb=core.canonical_bytes(seal);t.need(len(sb)<=core.SEAL_MAX_BYTES,'SEAL_BOUND')\nFILE_12_L157= r.validate_seal(sb,entries,generation,logical_id);t.create(work/'SEALED.json',sb)\nFILE_12_L158= for rep in m['replicas']:\nFILE_12_L159= guard();cloud.put(rep['crypt_remote'],'SEALED.json',work/'SEALED.json')\nFILE_12_L160= for rep in m['replicas']:\nFILE_12_L161= t.need(cloud.get(rep['crypt_remote'],'SEALED.json',len(sb)+1)==sb,'SEALED_QUORUM_REQUIRED')\nFILE_12_L162= guard();s.verify_parts(partdir,record)\nFILE_12_L163= t.create(work/'QUORUM_VERIFIED.json',core.canonical_bytes({'manifest_sha256':mh,'seal_sha256':t.sha(sb)}))\nFILE_12_L164= return {'sealed':True,'manifest':m,'seal_bytes':sb,'parts_record':record,'source':str(source),'work':str(work)}\nFILE_12_L165=\nFILE_12_L166=\nFILE_12_L167=def discover(cloud,entries,logical_id,expected_hash):\nFILE_12_L168= \"\"\"Find placement from all cloud catalogs, using only an external manifest digest.\"\"\"\nFILE_12_L169= core.validate_inventory(entries)\nFILE_12_L170= t.need(core.valid_name(logical_id) and core.valid_hash(expected_hash),'DISCOVERY_INPUT')\nFILE_12_L171= listed={};generations=set()\nFILE_12_L172= for e in entries:\nFILE_12_L173= gs=cloud.list_generations(e['crypt_remote'])\nFILE_12_L174= t.need(type(gs) is list and len(gs)<=r.MAX_GENERATIONS and all(core.valid_name(g) for g in gs),'GENERATION_LIST_INVALID')\nFILE_12_L175= t.need(len(gs)==len(set(gs)),'GENERATION_LIST_DUPLICATE');listed[e['crypt_remote']]=set(gs);generations.update(gs)\nFILE_12_L176= t.need(len(generations)<=r.MAX_GENERATIONS,'TOTAL_GENERATIONS_BOUND')\nFILE_12_L177= candidates=[]\nFILE_12_L178= for gen in sorted(generations):\nFILE_12_L179= seen={}\nFILE_12_L180= for e in entries:\nFILE_12_L181= remote=e['crypt_remote']\nFILE_12_L182= if gen not in listed[remote]:continue\nFILE_12_L183= b=cloud.get(remote,gen,'SEALED.json',core.SEAL_MAX_BYTES+1,missing_ok=True)\nFILE_12_L184= if b is None:continue\nFILE_12_L185= seal=r.validate_seal(b,entries,gen,logical_id)\nFILE_12_L186= t.need(remote in {x['crypt_remote'] for x in seal['replicas']},'UNNAMED_SEAL_LOCATION');seen[remote]=(b,seal)\nFILE_12_L187= if not seen:continue\nFILE_12_L188= sb,seal=next(iter(seen.values()))\nFILE_12_L189= if seal['manifest_sha256']!=expected_hash:continue\nFILE_12_L190= named={x['crypt_remote'] for x in seal['replicas']}\nFILE_12_L191= t.need(set(seen)==named and all(b==sb for b,_ in seen.values()),'IDENTICAL_TWO_SEALS_REQUIRED')\nFILE_12_L192= blobs=[]\nFILE_12_L193= for rep in seal['replicas']:\nFILE_12_L194= b=cloud.get(rep['crypt_remote'],gen,'manifest.json',core.MANIFEST_MAX_BYTES+1)\nFILE_12_L195= t.need(type(b) is bytes and t.sha(b)==expected_hash,'MANIFEST_DIGEST_MISMATCH')\nFILE_12_L196= m=r.strict_json(b,core.MANIFEST_MAX_BYTES);core.validate_manifest(m)\nFILE_12_L197= t.need(m['generation']==gen and m['logical_id']==logical_id and m['replicas']==seal['replicas'],'MANIFEST_PLACEMENT')\nFILE_12_L198= t.need(core.canonical_bytes(m)==b,'MANIFEST_CANONICAL_BYTES');core.verify_receipts(m,seal['receipts']);blobs.append(b)\nFILE_12_L199= t.need(len(blobs)==2 and blobs[0]==blobs[1],'TWO_MANIFEST_COPIES_REQUIRED')\nFILE_12_L200= candidates.append({'manifest':m,'seal_bytes':sb,'accounts_scanned':len(listed)})\nFILE_12_L201= t.need(len(candidates)==1,'EXACTLY_ONE_ACCEPTED_GENERATION_REQUIRED')\nFILE_12_L202= return candidates[0]\nFILE_12_L203=\nFILE_12_L204=\nFILE_12_L205=def fresh_seals(cloud,discovery):\nFILE_12_L206= m=discovery['manifest'];core.validate_manifest(m);sb=discovery['seal_bytes']\nFILE_12_L207= for rep in m['replicas']:\nFILE_12_L208= t.need(cloud.get(rep['crypt_remote'],m['generation'],'SEALED.json',len(sb)+1)==sb,'SEAL_CHANGED_DURING_RESTORE')\nFILE_12_L209=\nFILE_12_L210=\nFILE_12_L211=def download_part(cloud,rep,m,part,path,whole):\nFILE_12_L212= path=Path(path);parent=t.open_dir(path.parent);fd=None\nFILE_12_L213= try:\nFILE_12_L214= fd=os.open(path.name,os.O_WRONLY|os.O_CREAT|os.O_EXCL|os.O_NOFOLLOW,0o600,dir_fd=parent)\nFILE_12_L215= def consume(block):whole.update(block);s.write_all(fd,block)\nFILE_12_L216= result=cloud.read_stream(rep['crypt_remote'],m['generation'],part['name'],part['size'],part['sha256'],consume)\nFILE_13_PATH=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/nodeplan263.py\nFILE_13_BYTES=4603\nFILE_13_SHA256=db5c16ee24d25aa1694530332d3190ab7cc5d4f8eb69067d025e27ad5c995060\nFILE_13_CONTEXT_LINE_COUNT=23\nFILE_13_L1=\"\"\"Pure, allowlisted PVE metadata projection. Does not execute any command.\"\"\"\nFILE_13_L2=from decimal import Decimal,InvalidOperation\nFILE_13_L3=import hashlib\nFILE_13_L4=import re\nFILE_13_L5=import v2core as core\nFILE_13_L6=\nFILE_13_L7=class PlanError(ValueError):pass\nFILE_13_L8=\nFILE_13_L15= if type(value) is not str or not re.fullmatch(r'\\d+(?:\\.\\d+)?[KMGTPE]?',value):return None\nFILE_13_L16= try:\nFILE_13_L17= suffix=value[-1] if value[-1] in 'KMGTPE' else ''\nFILE_13_L18= base=Decimal(value[:-1] if suffix else value)\nFILE_13_L19= b=base*(1024**('KMGTPE'.index(suffix)+1) if suffix else 1)\nFILE_13_L20= return int(b) if b==int(b) and 0time.monotonic(),'PRODUCER_TIMEOUT')\nFILE_14_L175= try:rc=p.wait(timeout=deadline-time.monotonic())\nFILE_14_L176= except subprocess.TimeoutExpired:raise PVEError('PRODUCER_TIMEOUT') from None\nFILE_14_L177= need(rc==0,'PRODUCER_RC_FAILED_PARTIAL_RETAINED');need(total>0,'EMPTY_ARCHIVE');check()\nFILE_14_L178= st=os.fstat(fd);named=os.stat(dest.name,dir_fd=parent,follow_symlinks=False)\nFILE_14_L179= need(st.st_dev==initial.st_dev and st.st_ino==initial.st_ino and t.ident(st)==t.ident(named) and\nFILE_14_L180= st.st_nlink==1 and st.st_size==total,'PRODUCER_FILE_IDENTITY_CHANGED')\nFILE_14_L200= \"\"\"Future explicit execution API. Caller must recheck full plan/coverage in guard.\nFILE_14_L201=\nFILE_14_L202= Compression CRC and SHA are verified; guest bootability remains unproved.\nFILE_14_L203= This function has no cloud/seal/delete operations and returns a readonly artifact.\nFILE_14_L204= \"\"\"\nFILE_14_L205= need(callable(guard),'GUARD_REQUIRED');guard();validate_job(job,int(time.time()));validate_tools(tools)\nFILE_14_L206= work=Path(work);parent=stream.private_dir(work)\nFILE_14_L209= tmp=work/'tmp';t.make_dir(tmp,exclusive=True)\nFILE_14_L210= dest=work/('guest-%d.%s.zst'%(job['vmid'],'tar' if job['type']=='lxc' else 'vma'))\nFILE_14_L211= def free_guard():\nFILE_14_L212= v=os.statvfs(work);need(v.f_frsize*v.f_bavail>=LOCAL_RESERVE,'STAGING_RESERVE_REACHED_PARTIAL_RETAINED')\nFILE_14_L213= free_guard()\nFILE_14_L214= result=capture_archive(dump_argv(job,tools['vzdump']['path'],tmp,int(time.time())),dest,job['archive_cap_bytes'],seconds,free_guard)\nFILE_14_L215= guard();validate_tools(tools)\nFILE_14_L219= need(n==result['size'] and h==result['sha256'] and list(identity)==result['identity'],'PRODUCER_READBACK_MISMATCH')\nFILE_14_L220= guard();validate_tools(tools)\nFILE_14_L221= return {**result,'path':str(dest),'vmid':job['vmid'],'type':job['type'],'node':job['node'],\nFILE_14_L222= 'config_sha256':job['config_sha256'],'compression_test_ok':True,'restored':False,\nFILE_14_L223= 'state':'PRODUCED_COMPRESSION_CHECKED_NOT_CLOUD_SEALED','local_payload_retained':True}\nFILE_15_PATH=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/pve270.py\nFILE_15_BYTES=19595\nFILE_15_SHA256=97015277a4f3f8129c0fd11eb70c06293026366f1f8232a97feeed513a2dbcef\nFILE_15_CONTEXT_LINE_COUNT=140\nFILE_15_L3=Does not modify installed pve265, old workspaces, staging permissions or guest config.\nFILE_15_L4=Only explicit produce() executes vzdump. Private bounded stderr/exit metadata retained.\nFILE_15_L5=\"\"\"\nFILE_15_L6=import hashlib,json,os,re,secrets,selectors,signal,stat,subprocess,sys,time\nFILE_15_L7=from pathlib import Path\nFILE_15_L8=import v2core as core\nFILE_15_L9=import transport261 as t\nFILE_15_L10=import stream263 as stream\nFILE_15_L11=import pve265 as old\nFILE_15_L12=need=old.need\nFILE_15_L13=MAX_STDERR=2*1024**2\nFILE_15_L14=EXPECTED_CFG='09957aacd41ba9eeaf5fd4eda13936d580c9082a46895433379aa9dd0bad5969'\nFILE_15_L39=def live_gate(job,pvesh_path,pvesh_sha):\nFILE_15_L40= need(job.get('vmid')==110 and job.get('type')=='lxc' and job.get('node')=='pve01' and job.get('config_sha256')==EXPECTED_CFG,'ONLY_ACCEPTED_CT110')\nFILE_15_L41= vals=[]\nFILE_15_L42= for part in ('config','status/current','snapshot'):\nFILE_15_L43= need(t.sha(t.read_secure(Path(pvesh_path),16*1024**2)[0])==pvesh_sha,'PVESH_CHANGED')\nFILE_15_L44= rc,b=t.bounded_process([str(pvesh_path),'get','/nodes/pve01/lxc/110/'+part,'--output-format','json'],dict(old.ENV),262144,30)\nFILE_15_L45= need(rc==0,'CT_GATE_READ_FAILED')\nFILE_15_L46= try:vals.append(json.loads(b))\nFILE_15_L47= except (ValueError,UnicodeError):raise old.PVEError('CT_GATE_JSON') from None\nFILE_15_L48= validate_ct(*vals,job['config_sha256'])\nFILE_15_L49= return True\nFILE_15_L50=\nFILE_15_L59= def __init__(self,path,alias):\nFILE_15_L60= self.path=Path(path);self.alias=Path(alias);self.parent=None;self.fd=None\nFILE_15_L61= need(self.path.name=='perl' and self.alias.name=='perl5.40.1' and self.path.parent==self.alias.parent,'PERL_PATH_POLICY')\nFILE_15_L62= need(self.path.is_absolute() and '..' not in self.path.parts,'PERL_PATH_POLICY')\nFILE_15_L63= try:\nFILE_15_L64= self.parent=os.open('/',os.O_RDONLY|os.O_DIRECTORY)\nFILE_15_L65= for part in self.path.parts[1:-1]:\nFILE_15_L66= nxt=os.open(part,os.O_RDONLY|os.O_DIRECTORY|os.O_NOFOLLOW,dir_fd=self.parent)\nFILE_15_L67= os.close(self.parent);self.parent=nxt\nFILE_15_L68= s=os.fstat(nxt)\nFILE_15_L69= need(s.st_uid==0 and not s.st_mode&0o022,'PERL_PARENT_UNTRUSTED')\nFILE_15_L75= need(os.pread(self.fd,4,0)==b'\\x7fELF','PERL_ELF_REQUIRED')\nFILE_15_L76= self.identity=t.ident(s)+(s.st_nlink,)\nFILE_15_L77= self.sha256=self._digest();self.check()\nFILE_15_L78= except BaseException:\nFILE_15_L79= self.close();raise\nFILE_15_L80= def _digest(self):\nFILE_15_L81= s=os.fstat(self.fd);need(00,'PERL_TIMEOUT')\nFILE_15_L123= rc=p.wait(timeout=left);self.check();return rc,bytes(out)\nFILE_15_L124= except subprocess.TimeoutExpired:raise old.PVEError('PERL_TIMEOUT') from None\nFILE_15_L125= finally:\nFILE_15_L126= if p is not None:\nFILE_15_L127= if p.poll() is None:\nFILE_15_L150= for p,h in pins.items():need(t.sha(t.read_secure(Path(p),2*1024**2)[0])==h,'PVE_MAPPING_SOURCE_CHANGED')\nFILE_15_L151= pin.check()\nFILE_15_L152= try:d=json.loads(b)\nFILE_15_L153= except (ValueError,UnicodeError):raise old.PVEError('PVE_IDMAP_JSON') from None\nFILE_15_L154= return validate_mapping(d)\nFILE_15_L155=\nFILE_15_L156=def _parent(path):\nFILE_15_L157= path=Path(path)\nFILE_15_L158= need(path.is_absolute() and '..' not in path.parts,'TEMP_PARENT_PATH')\nFILE_15_L159= fd=os.open('/',os.O_RDONLY|os.O_DIRECTORY)\nFILE_15_L160= try:\nFILE_15_L161= for i,part in enumerate(path.parts[1:]):\nFILE_15_L162= nxt=os.open(part,os.O_RDONLY|os.O_DIRECTORY|os.O_NOFOLLOW,dir_fd=fd);os.close(fd);fd=nxt\nFILE_15_L163= s=os.fstat(fd);last=i==len(path.parts)-2\nFILE_15_L164= need(s.st_uid==0,'TEMP_PARENT_OWNER')\nFILE_15_L165= need(not s.st_mode&0o022 or last and s.st_mode&stat.S_ISVTX,'TEMP_PARENT_WRITABLE')\nFILE_15_L166= result=fd;fd=None;return result\nFILE_15_L170=\nFILE_15_L171=class MappedScratch:\nFILE_15_L172= \"\"\"Root-owned, mapped-GID traverse-only, no access for others. No recursive cleanup.\"\"\"\nFILE_15_L173= def __init__(self,gid,base='/var/tmp'):\nFILE_15_L174= need(type(gid) is int and gid==100000,'TEMP_GID_POLICY')\nFILE_15_L175= self.parent=_parent(base);self.fd=None;self.gid=gid\nFILE_15_L176= self.name='homelab-vzdump-268-'+secrets.token_hex(16);self.path=Path(base)/self.name\nFILE_15_L177= try:\nFILE_15_L178= os.mkdir(self.name,0o700,dir_fd=self.parent)\nFILE_15_L179= self.fd=os.open(self.name,os.O_RDONLY|os.O_DIRECTORY|os.O_NOFOLLOW,dir_fd=self.parent)\nFILE_15_L181= os.fchown(self.fd,0,gid);os.fchmod(self.fd,0o710)\nFILE_15_L182= s=os.fstat(self.fd);self.identity=(s.st_dev,s.st_ino)\nFILE_15_L183= os.fsync(self.parent);self.check()\nFILE_15_L184= except BaseException:\nFILE_15_L185= if self.fd is not None:os.close(self.fd);self.fd=None\nFILE_15_L186= os.close(self.parent);self.parent=None\nFILE_15_L187= raise\nFILE_15_L199= name='access-probe';fd=os.open(name,os.O_WRONLY|os.O_CREAT|os.O_EXCL|os.O_NOFOLLOW,0o644,dir_fd=self.fd)\nFILE_15_L200= try:os.fchmod(fd,0o644);stream.write_all(fd,b'HOMELAB_ACCESS_PROBE\\n');os.fsync(fd);identity=t.ident(os.fstat(fd))\nFILE_15_L201= finally:os.close(fd)\nFILE_15_L202= positive='''import os,sys\nFILE_15_L203=p,private=sys.argv[1:]\nFILE_15_L204=assert open(p+'/access-probe','rb').read()==b'HOMELAB_ACCESS_PROBE\\\\n'\nFILE_15_L205=for action in (lambda:os.listdir(p),lambda:os.open(p+'/forbidden',os.O_WRONLY|os.O_CREAT,0o600),lambda:os.open(private,os.O_RDONLY|os.O_DIRECTORY)):\nFILE_15_L207= except PermissionError:continue\nFILE_15_L208= raise SystemExit(31)\nFILE_15_L209='''\nFILE_15_L210= negative='''import sys\nFILE_15_L211=try:open(sys.argv[1]+'/access-probe','rb').read()\nFILE_15_L212=except PermissionError:raise SystemExit(0)\nFILE_15_L213=raise SystemExit(32)\nFILE_15_L256= b=os.read(key.fileobj.fileno(),65536)\nFILE_15_L257= if not b:sel.unregister(key.fileobj);continue\nFILE_15_L258= if key.fileobj is p.stdout:\nFILE_15_L259= need(total+len(b)<=cap,'ARCHIVE_CAP_REACHED_PARTIAL_RETAINED')\nFILE_15_L260= stream.write_all(fd,b);digest.update(b);total+=len(b)\nFILE_15_L261= else:\nFILE_15_L262= err_total+=len(b);eh.update(b);chunk=b[:max(0,MAX_STDERR-err_kept)]\nFILE_15_L263= if chunk:stream.write_all(efd,chunk);err_kept+=len(chunk)\nFILE_15_L264= need(deadline>time.monotonic(),'PRODUCER_TIMEOUT')\nFILE_15_L265= try:rc=p.wait(timeout=deadline-time.monotonic())\nFILE_15_L266= except subprocess.TimeoutExpired:raise old.PVEError('PRODUCER_TIMEOUT') from None\nFILE_15_L267= need(rc==0,'PRODUCER_RC_FAILED_PARTIAL_RETAINED');need(total>0,'EMPTY_ARCHIVE');check()\nFILE_15_L268= st=os.fstat(fd);named=os.stat(dest.name,dir_fd=parent,follow_symlinks=False)\nFILE_15_L269= need(st.st_dev==initial.st_dev and st.st_ino==initial.st_ino and t.ident(st)==t.ident(named) and st.st_nlink==1 and st.st_size==total,'PRODUCER_FILE_IDENTITY_CHANGED')\nFILE_15_L270= os.fchmod(fd,0o400);os.fsync(fd);os.fsync(parent);good=True\nFILE_15_L306= scratch.probe(uid,work);emit('MAPPED_UID_ACCESS_AND_ISOLATION_PROBE','PASS')\nFILE_15_L307= def free_guard():\nFILE_15_L308= scratch.check()\nFILE_15_L309= for path,reserve,reason in ((work,old.LOCAL_RESERVE,'STAGING_RESERVE_REACHED_PARTIAL_RETAINED'),(scratch.path,256*1024**2,'PVE_TEMP_RESERVE_LOW')):\nFILE_15_L310= v=os.statvfs(path);need(v.f_frsize*v.f_bavail>=reserve,reason)\nFILE_15_L311= free_guard();guard();dest=work/'guest-110.tar.zst'\nFILE_15_L312= result=capture_archive(old.dump_argv(job,tools['vzdump']['path'],scratch.path,int(time.time())),dest,job['archive_cap_bytes'],seconds,free_guard)\nFILE_15_L317= need(n==result['size'] and h==result['sha256'] and list(identity)==result['identity'],'PRODUCER_READBACK_MISMATCH')\nFILE_15_L318= guard();old.validate_tools(tools);scratch.close(empty_only=True);complete=True;emit('PVE_TEMP_EMPTY_REMOVED','YES')\nFILE_15_L319= return {**result,'path':str(dest),'vmid':110,'type':'lxc','node':job['node'],'config_sha256':job['config_sha256'],\nFILE_15_L320= 'compression_test_ok':True,'restored':False,'state':'PRODUCED_COMPRESSION_CHECKED_NOT_CLOUD_SEALED','local_payload_retained':True}\nFILE_15_L321= finally:\nFILE_15_L322= m=work/'PRODUCER_PROCESS.json'\nFILE_15_L323= if m.exists():\nFILE_16_PATH=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/pve282.py\nFILE_16_BYTES=6195\nFILE_16_SHA256=73c6cb7773eb8e82d1e7ed5ab4733b4afb88f74ae69c09125d0378d6e480e012\nFILE_16_CONTEXT_LINE_COUNT=58\nFILE_16_L1=\"\"\"282R1 bounded CT112 adapter; proven 270 capture/temp/Perl implementation unchanged.\nFILE_16_L2=Expected source hash must be supplied from the immutable-hash-bound 265 plan.\nFILE_16_L3=No changes to source guest configuration; no source110 backup or guest deletion.\nFILE_16_L4=\"\"\"\nFILE_16_L5=import copy,json,os,time\nFILE_16_L6=from pathlib import Path\nFILE_16_L7=import v2core as core\nFILE_16_L8=import transport261 as t\nFILE_16_L9=import stream263 as stream\nFILE_16_L10=import pve265 as old\nFILE_16_L11=import pve270 as proven\nFILE_16_L12=import guest266 as g\nFILE_16_L13=need=old.need\nFILE_16_L14=PerlPin=proven.PerlPin\nFILE_16_L15=host_mapping=proven.host_mapping\nFILE_16_L26= need(core.valid_hash(expected_config) and job.get('config_sha256')==expected_config,'CT112_PLAN_CONFIG_ANCHOR')\nFILE_16_L27= return True\nFILE_16_L28=\nFILE_16_L29=def select112(current,baseline,now):\nFILE_16_L30= rows=g.plan_rows(current);previous=g.plan_rows(baseline)\nFILE_16_L31= by_id={j['vmid']:j for j in previous}\nFILE_16_L32= need(TARGET_VMID in by_id,'CT112_MISSING_FROM_ACCEPTED265')\nFILE_16_L33= g.same_state(baseline,current,TARGET_VMID)\nFILE_16_L34= need({j['vmid'] for j in rows}==set(by_id),'ACCEPTED265_MEMBERSHIP_CHANGED')\nFILE_16_L35= for row in rows:\nFILE_16_L36= need(row['config_sha256']==by_id[row['vmid']]['config_sha256'],'ACCEPTED265_GUEST_CONFIG_CHANGED')\nFILE_16_L37= selected=[j for j in rows if j['vmid']==TARGET_VMID]\nFILE_16_L38= need(len(selected)==1,'CT112_NOT_UNIQUE')\nFILE_16_L39= job=copy.deepcopy(selected[0]);old.validate_job(job,now)\nFILE_16_L40= need(current.get('global_blockers')==[],'GLOBAL_DEFAULT_BLOCK')\nFILE_16_L41= need(job.get('compression_dependent') is False and job.get('configured_full_cycle_fits') is True,'CT112_FULL_CYCLE_SPACE_UNPROVEN')\nFILE_16_L42= disks=job.get('disks');need(type(disks) is list and len(disks)==1,'CT112_SINGLE_ROOTFS_REQUIRED')\nFILE_16_L53=def live_gate(job,pvesh_path,pvesh_sha,expected_config):\nFILE_16_L54= require_target(job,expected_config)\nFILE_16_L55= vals=[]\nFILE_16_L56= for part in ('config','status/current','snapshot'):\nFILE_16_L57= need(t.sha(t.read_secure(Path(pvesh_path),16*1024**2)[0])==pvesh_sha,'PVESH_CHANGED')\nFILE_16_L58= rc,b=t.bounded_process([str(pvesh_path),'get','/nodes/pve01/lxc/112/'+part,'--output-format','json'],dict(old.ENV),262144,30)\nFILE_16_L59= need(rc==0,'CT112_GATE_READ_FAILED')\nFILE_16_L60= try:vals.append(json.loads(b))\nFILE_16_L61= except (ValueError,UnicodeError):raise old.PVEError('CT112_GATE_JSON') from None\nFILE_16_L62= validate_ct(*vals,expected_config)\nFILE_16_L63= return True\nFILE_16_L64=\nFILE_16_L79= scratch.probe(uid,work);emit('MAPPED_UID_ACCESS_AND_ISOLATION_PROBE','PASS')\nFILE_16_L80= def free_guard():\nFILE_16_L81= scratch.check()\nFILE_16_L82= for path,reserve,reason in ((work,old.LOCAL_RESERVE,'STAGING_RESERVE_REACHED_PARTIAL_RETAINED'),(scratch.path,256*1024**2,'PVE_TEMP_RESERVE_LOW')):\nFILE_16_L83= v=os.statvfs(path);need(v.f_frsize*v.f_bavail>=reserve,reason)\nFILE_16_L84= free_guard();guard();dest=work/'guest-112.tar.zst'\nFILE_16_L85= result=capture_archive(old.dump_argv(job,tools['vzdump']['path'],scratch.path,int(time.time())),dest,job['archive_cap_bytes'],seconds,free_guard)\nFILE_16_L90= need(n==result['size'] and h==result['sha256'] and list(identity)==result['identity'],'PRODUCER_READBACK_MISMATCH')\nFILE_16_L91= guard();old.validate_tools(tools);scratch.close(empty_only=True);complete=True;emit('PVE_TEMP_EMPTY_REMOVED','YES')\nFILE_16_L92= return {**result,'path':str(dest),'vmid':112,'type':'lxc','node':job['node'],'config_sha256':job['config_sha256'],\nFILE_16_L93= 'compression_test_ok':True,'restored':False,'state':'PRODUCED_COMPRESSION_CHECKED_NOT_CLOUD_SEALED','local_payload_retained':True}\nFILE_16_L94= finally:\nFILE_16_L95= m=work/'PRODUCER_PROCESS.json'\nFILE_16_L96= if m.exists():\nFILE_17_PATH=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/restore262.py\nFILE_17_BYTES=14701\nFILE_17_SHA256=56d1b961c8d5654d5e2ee2755268db46bd2202bca11e69788febf991cfaa79f1\nFILE_17_CONTEXT_LINE_COUNT=180\nFILE_17_L1=\"\"\"262R1 bounded cloud-discovered recovery. No live action on import.\nFILE_17_L2=\nFILE_17_L3=Cloud access is strictly lsjson/cat. No source-local workspace/manifest/receipt\nFILE_17_L4=is read. The external accepted manifest digest is a trust anchor, not placement.\nFILE_17_L5=\"\"\"\nFILE_17_L6=import hashlib\nFILE_17_L7=import io\nFILE_17_L8=import json\nFILE_17_L9=import os\nFILE_17_L10=from pathlib import Path\nFILE_17_L11=import re\nFILE_17_L12=import stat\nFILE_17_L13=import tarfile\nFILE_17_L14=import time\nFILE_17_L15=import v2core as core\nFILE_17_L16=import transport261 as t\nFILE_17_L17=\nFILE_17_L18=SOURCE_NAMES=frozenset(('etc/pve/HOMELAB_ASSISTANT_ERROR_REGISTER.md','etc/pve/31_HOMELAB_REFERENCE.md',\nFILE_17_L19= 'usr/local/sbin/homelab-control-backup','etc/systemd/system/homelab-control-backup.service'))\nFILE_17_L20=ALL_NAMES=SOURCE_NAMES|{'SOURCE_INDEX.json'}\nFILE_17_L21=MAX_GENERATIONS=64\nFILE_17_L22=SEAL_KEYS={'schema_version','state','generation','logical_id','manifest_sha256','replicas','receipts','acceptance'}\nFILE_17_L23=\nFILE_17_L24=def strict_json(data,limit):\nFILE_17_L25= t.need(type(data) is bytes and len(data)<=limit,'JSON_BOUND')\nFILE_17_L29= t.need(k not in result,'JSON_DUPLICATE_KEY');result[k]=v\nFILE_17_L30= return result\nFILE_17_L31= def bad_constant(value):raise t.Stop('JSON_NONFINITE_NUMBER')\nDEPENDENCY_PATH_COUNT=28\nDEPENDENCY_PATH_1=/opt/homelab-backup-v2/releases/core-260r1\nDEPENDENCY_PATH_2=/opt/homelab-backup-v2/releases/core-260r1/v2core.py\nDEPENDENCY_PATH_3=/opt/homelab-backup-v2/releases/ct-backup-282r1\nDEPENDENCY_PATH_4=/opt/homelab-backup-v2/releases/ct-backup-282r1/guest266.py\nDEPENDENCY_PATH_5=/opt/homelab-backup-v2/releases/ct-backup-282r1/pve282.py\nDEPENDENCY_PATH_6=/opt/homelab-backup-v2/releases/ct-backup-282r1/test_guest266.py\nDEPENDENCY_PATH_7=/opt/homelab-backup-v2/releases/ct-backup-282r1/test_perl270.py\nDEPENDENCY_PATH_8=/opt/homelab-backup-v2/releases/ct-backup-282r1/test_producer282.py\nDEPENDENCY_PATH_9=/opt/homelab-backup-v2/releases/ct-backup-282r1/test_pve282.py\nDEPENDENCY_PATH_10=/opt/homelab-backup-v2/releases/large-cloud-264r1\nDEPENDENCY_PATH_11=/opt/homelab-backup-v2/releases/large-cloud-264r1/large264.py\nDEPENDENCY_PATH_12=/opt/homelab-backup-v2/releases/pve-producer-265r1\nDEPENDENCY_PATH_13=/opt/homelab-backup-v2/releases/pve-producer-265r1/PLAN.json\nDEPENDENCY_PATH_14=/opt/homelab-backup-v2/releases/pve-producer-265r1/pve265.py\nDEPENDENCY_PATH_15=/opt/homelab-backup-v2/releases/pve-producer-265r1/test_pve265.py\nDEPENDENCY_PATH_16=/opt/homelab-backup-v2/releases/pve-retry-268r1\nDEPENDENCY_PATH_17=/opt/homelab-backup-v2/releases/pve-retry-270r1\nDEPENDENCY_PATH_18=/opt/homelab-backup-v2/releases/pve-retry-270r1/pve270.py\nDEPENDENCY_PATH_19=/opt/homelab-backup-v2/releases/pve-retry-270r1/test_pve270.py\nDEPENDENCY_PATH_20=/opt/homelab-backup-v2/releases/pve-run-266r1\nDEPENDENCY_PATH_21=/opt/homelab-backup-v2/releases/restore-262r1\nDEPENDENCY_PATH_22=/opt/homelab-backup-v2/releases/restore-262r1/restore262.py\nDEPENDENCY_PATH_23=/opt/homelab-backup-v2/releases/stream-263r1\nDEPENDENCY_PATH_24=/opt/homelab-backup-v2/releases/stream-263r1/nodeplan263.py\nDEPENDENCY_PATH_25=/opt/homelab-backup-v2/releases/stream-263r1/stream263.py\nDEPENDENCY_PATH_26=/opt/homelab-backup-v2/releases/transport-261r1\nDEPENDENCY_PATH_27=/opt/homelab-backup-v2/releases/transport-261r1/transport261.py\nDEPENDENCY_PATH_28=/opt/homelab-backup-v2/releases/vm-backup-283r1\nHAS_RCLONE_SOURCE=true\nHAS_OBJECT_PATH_CONTRACT=true\nDECISION=PASS_BACKUP213_EXACT_CLOUD_OBJECT_CONTRACT_CAPTURED\nNEXT_GATE=BUILD_CORRECTED_CLOUD_GENERATION_RECOVERY\nCLOUD_READS=0\nCLOUD_WRITES=0\nVPS_SSH_ATTEMPTS=0\nMESSENGERS214_CLOSED=false\nMAJOR_STAGES_REMAINING=8\nNEWFI213_END=true\n" + "output": "W2UX739_BEGIN=True\nCOMMAND_ID=SUPPORT-260920-PVEPRO-RUSSIAN-UX-STAGING-CLEANUP-739R1\nMODE=change\nCOMPONENT=pvepro-russian-ux-staging-cleanup\nVM_SAFETY={\"production_snapshots\":[],\"production_status\":\"running\",\"staging_all_link_down\":true,\"staging_name\":\"pvepro-staging-v1\",\"staging_nic_count\":1,\"staging_snapshots\":[],\"staging_status\":\"stopped\"}\nPRODUCTION_PRESTATE={\"admins\":1,\"boardActive\":true,\"defaultLanguageId\":2,\"enCount\":1,\"errors\":19,\"languageRebuildJobs\":0,\"manualJobs\":0,\"nodes\":[{\"node_id\":1,\"node_type_id\":\"Category\",\"title\":\"Main category\"},{\"node_id\":2,\"node_type_id\":\"Forum\",\"title\":\"Main forum\"}],\"posts\":44,\"registrationEnabled\":false,\"ruCount\":1,\"ruPhraseCount\":11142,\"ruPhraseMapCount\":11194,\"threads\":44}\nSTAGING_SNAPSHOT_CREATED=True\nSTAGING_CLEANUP_ERROR_CLASS=RuntimeError\nSTAGING_CLEANUP_ERROR_SHA256=97bca49c0032b21299dd3981d766c52667d84153c1f07963ae6404d6ac2012f7\nROLLBACK_STARTED=True\nROLLBACK_ERROR_SHA256=97bca49c0032b21299dd3981d766c52667d84153c1f07963ae6404d6ac2012f7\nROLLBACK_RESTORED=False\nTASK_RESULT=PVEPRO_RUSSIAN_UX_STAGING_CLEANUP_FAILED\nHOMELAB_RESULT_CONTRACT={\"changes_made\":true,\"command_id\":\"SUPPORT-260920-PVEPRO-RUSSIAN-UX-STAGING-CLEANUP-739R1\",\"rollback_restored\":false,\"rollback_started\":true,\"status\":\"FAIL\",\"version\":1}\n" } diff --git a/runtime/latest.txt b/runtime/latest.txt index 446db3f8..8e4af57d 100644 --- a/runtime/latest.txt +++ b/runtime/latest.txt @@ -1,1161 +1,46 @@ CHAT_OUTPUT_BEGIN -COMMAND_ID=NEWFI-260920-NEWFDOM-MESSENGERS214-BASE213 -STATUS=OK -RC=0 +COMMAND_ID=SUPPORT-260920-PVEPRO-RUSSIAN-UX-STAGING-CLEANUP-739R1 +STATUS=FAIL +RC=3 HOST=pve01 -MODE=read-only -COMPONENT=newfi-newfdom-messengers214-backupv2-base-release-authority-aware-readonly +MODE=change +COMPONENT=pvepro-russian-ux-staging-cleanup REFERENCE_REGISTER_CHECK=OK REFERENCE_SHA256=5a3d8e5154c41cb582a4e0aca68090be1f0138918bf82131a948df326f9d8d66 ERROR_REGISTER_CHECK=OK ERROR_REGISTER_SHA256=3b09a553ec0f527ed3afeed4753f52a74ee3036045bbfb3c685e9f8af4ba7ba0 -COMMAND_SHA256=65d18e767349b7319c7197b07d4122dae093e606f12244bfd676798e5606e937 +COMMAND_SHA256=65df9732f610c571636fb5781f4edb94759ad3474790883156388931ec46cc28 DUPLICATE_FAILED_COMMAND_BLOCKED=false EXECUTION_STARTED=true -CHANGE_DECLARED=false +CHANGE_DECLARED=true RESULT_CONTRACT_VALID=true -RESULT_CONTRACT_STATUS=NOT_APPLICABLE +RESULT_CONTRACT_STATUS=FAIL RESULT_CONTRACT_ERROR=NONE -COMMAND_RC=0 -CHANGES_MADE=false -ROLLBACK_STARTED=false -ROLLBACK_RESTORED=null -MUTATION_OUTCOME=NO_MUTATION +COMMAND_RC=3 +CHANGES_MADE=true +ROLLBACK_STARTED=true +ROLLBACK_RESTORED=false +MUTATION_OUTCOME=ROLLBACK_FAILED SANITIZED=yes SECRETS_INCLUDED=no PRIVATE_ADDRESSES_INCLUDED=no -RAW_EVIDENCE_SHA256=417d54bc3ee03b085ea2894f80cd2f3c4e8df0652fe89b0f35fa9f5df73e61c3 -SANITIZED_OUTPUT_SHA256=c2da47960582cda8715a7fd595b714f908f504dd277254ed2b277fdb5c900bf3 +RAW_EVIDENCE_SHA256=9eb6c53be7349b4410bac641cbe1975ef8c8c90fcb0f2ca1a52d8d9d24c627ce +SANITIZED_OUTPUT_SHA256=9eb6c53be7349b4410bac641cbe1975ef8c8c90fcb0f2ca1a52d8d9d24c627ce OUTPUT_BEGIN -NEWFI213_BEGIN=true -MODE=read-only -IMMUTABLE_PLAN_CHECK=PASS -CHANGES_MADE=false -MUTATION_OUTCOME=NO_MUTATION -RUNNER_SHA256=b248a4c32c9cc64e5747e7dce6c7fc0a23f5124a77c71ce72e27a81aceae9d2d -RUNNER_PIN_MATCH=true -ERROR_REGISTER_SHA256=3b09a553ec0f527ed3afeed4753f52a74ee3036045bbfb3c685e9f8af4ba7ba0 -ERROR_REGISTER_PIN_MATCH=true -REFERENCE_SHA256=5a3d8e5154c41cb582a4e0aca68090be1f0138918bf82131a948df326f9d8d66 -REFERENCE_PIN_MATCH=true -WRAPPER_SHA256=5077444065c732451cb84898680f62361b21a24ef9eb4f191253e82ead524ea2 -WRAPPER_PIN_MATCH=true -OVERLAY_SHA256=ca6876fa9c3b1465b045344a8ad46962de16972c7dadc4ac6257c7d225047793 -OVERLAY_PIN_MATCH=true -PRODUCTION_ACCEPTANCE_SHA256=708a481f6719ff1ed8d0afec1902650ed36ac631e980611b015419f504f16df0 -PRODUCTION_ACCEPTANCE_PIN_MATCH=true -AUTHORITY_PIN_MATCH=true -BACKUP_SOURCE_PIN_MATCH=true -ACCEPTED_RELEASE=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1 -BASE_RELEASE=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1 -MATCHING_FILE_COUNT=26 -FILE_1_PATH=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/ACCEPTANCE.json -FILE_1_BYTES=517 -FILE_1_SHA256=904dfccad43b55ad0b53cfbd29fb45fee36fd0fbfa5afa58da5c3cca0ee7153b -FILE_1_CONTEXT_LINE_COUNT=1 -FILE_1_L1={"command_id":"SUPPORT-260916-INFRA-DR-BACKUPV2-FINAL-REPLACEMENT-PLAN-STABILITY-SCHEMA-RCA-FIX-VALIDATE-PROMOTE-CUTOVER-SEAL-486R1","ct112_manifest_sha256":"1ac4f16a54bcdd8b896c9a131abef966d315256aa56da3f83ed0b30845c4fcb0","ct112_payload_bytes":223875308,"ct112_payload_sha256":"d443b85e76e806830fd9c372d0a09fc5b91ca9c9d35c44fb0ad7968cc996d01d","legacy_ids":["node-pve01","md-relay","spb-ingress","us-netbird","router-netcraze","xf-codevipe","xf-zakrutim","control-bootstrap-v2"],"representative_pass":9,"schema":1} -FILE_2_PATH=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/RELEASE_MANIFEST.json -FILE_2_BYTES=3301 -FILE_2_SHA256=a36dd0db88592ec3d355ef43e6b218da6c9ad649e9ccf8c8f680b96eb23a156e -FILE_2_CONTEXT_LINE_COUNT=1 -FILE_2_L1={"files":{"ACCEPTANCE.json":{"bytes":517,"mode":256,"sha256":"904dfccad43b55ad0b53cfbd29fb45fee36fd0fbfa5afa58da5c3cca0ee7153b"},"candidate411/CONTRACT.json":{"bytes":2077,"mode":256,"sha256":"a414715f1b91b3701c52073709a80c38218f76e3961618e8af61489211e50c01"},"candidate411/PLAN265_REFERENCE.json":{"bytes":7210,"mode":256,"sha256":"8d36e82080be93a8c929a9211191fa868166b9e7b6152c4c7689fa52b6720757"},"candidate411/PROVENANCE.json":{"bytes":4310,"mode":256,"sha256":"303f44dc8e91c8e6a675f9ea406ce4b7532ed98440a9b6807fa593ede9d33254"},"candidate411/adapter.py":{"bytes":2989,"mode":320,"sha256":"18eb29cef165371b48bcb78e1a456e1939e6eff253787218133162cc89ab9fa3"},"candidate411/guest266.py":{"bytes":7385,"mode":256,"sha256":"86433bc424d5fa5017c20f7a237c82874b02357a3283660d8a6b0039ef2bc868"},"candidate411/large264.py":{"bytes":16514,"mode":256,"sha256":"5cc4641fc35e4dd554495a36146da7dbbb7f142d1450e04cc5d3c89678b8497a"},"candidate411/nodeplan263.py":{"bytes":4603,"mode":256,"sha256":"db5c16ee24d25aa -FILE_3_PATH=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/ct112_handler.py -FILE_3_BYTES=10955 -FILE_3_SHA256=8cac6e627d5e7abe5d3163cdd24911995a1e3f45c776f9373c2aa68784f5ec6c -FILE_3_CONTEXT_LINE_COUNT=98 -FILE_3_L1=#!/usr/bin/env python3 -FILE_3_L2=from __future__ import annotations -FILE_3_L3=import hashlib,json,os,secrets,shutil,stat,subprocess,sys,time -FILE_3_L4=from pathlib import Path -FILE_3_L5=ROOT=Path(__file__).resolve().parent -FILE_3_L6=CAND=ROOT/'candidate411' -FILE_3_L7=if str(CAND) not in sys.path:sys.path.insert(0,str(CAND)) -FILE_3_L8=import guest266,large264 as large,pve265,pve282,transport261 as t,v2core as core -FILE_3_L9=CT_ID='guest-pve01-lxc-112' -FILE_3_L10=PLAN=CAND/'PLAN265_REFERENCE.json' -FILE_3_L11=PLAN_SHA='8d36e82080be93a8c929a9211191fa868166b9e7b6152c4c7689fa52b6720757' -FILE_3_L12=RCLONE=Path('/usr/bin/rclone'); RCLONE_SHA='20df3d5eac5d4e187ce638e4216354e7fa6778c3c51f0caf48c48acca9686354' -FILE_3_L13=RCLONE_CFG=Path('/root/.config/rclone/[SENSITIVE_FILE]') -FILE_3_L14=BINDING_FP='ed48c1345771c3e0f50db9516f13f26bb66047283e2f883d5df272f15e2282b8' -FILE_3_L15=VZDUMP=Path('/usr/bin/vzdump'); VZDUMP_SHA='21339d009afd55cb7ec613b64c1f493b2a7dbd18b85b4d712909ef6d6710f4ea' -FILE_3_L16=ZSTD=Path('/usr/bin/zstd'); ZSTD_SHA='3eb7744cf8fd1ecd46d617098ee870b3e7911fe570ffaba662c1c5b7d6456020' -FILE_3_L26= p=subprocess.run(argv,stdout=subprocess.PIPE,stderr=subprocess.PIPE,timeout=45,check=False,env=ENV) -FILE_3_L27= if p.returncode!=0 or len(p.stdout)>4*1024*1024:raise RuntimeError('PVESH_READ_FAILED') -FILE_3_L28= try:return json.loads(p.stdout.decode('utf-8','strict')) -FILE_3_L29= except (ValueError,UnicodeError):raise RuntimeError('PVESH_JSON') from None -FILE_3_L30= -FILE_3_L31=def _baseline(): -FILE_3_L32= b=t.read_secure(PLAN,1024*1024,private=True)[0] -FILE_3_L33= if t.sha(b)!=PLAN_SHA:raise RuntimeError('PLAN_REFERENCE_IDENTITY') -FILE_3_L34= try:return json.loads(b.decode('utf-8','strict')) -FILE_3_L35= except (ValueError,UnicodeError):raise RuntimeError('PLAN_REFERENCE_JSON') from None -FILE_3_L36= -FILE_3_L37=def _defaults(): -FILE_3_L38= p=Path('/etc/vzdump.conf');st=os.stat(p,follow_symlinks=False) -FILE_3_L100= return {'path':str(path),'sha256':want} -FILE_3_L101= -FILE_3_L102=def _private_config(): -FILE_3_L103= b,identity=t.read_secure(RCLONE_CFG,t.MAX_CONFIG_BYTES,private=True) -FILE_3_L104= text=b.decode('utf-8-sig','strict');entries=core.inventory_from_config(text);binding=t.binding(text) -FILE_3_L105= if len(entries)!=13 or len({x['account_id'] for x in entries})!=13:raise RuntimeError('RCLONE_INVENTORY') -FILE_3_L106= if hashlib.sha256(binding.encode()).hexdigest()!=BINDING_FP:raise RuntimeError('RCLONE_BINDING') -FILE_3_L107= return b,entries,binding -FILE_3_L108= -FILE_3_L109=def _write_private(path,data): -FILE_3_L118=class LazyReader: -FILE_3_L119= def __init__(self,work,entries,binding,guard,deadline,logical_id): -FILE_3_L120= self.work=Path(work);self.entries=entries;self.binding=binding;self.guard=guard;self.deadline=deadline;self.logical_id=logical_id -FILE_3_L121= self.snapshot=self.work/'rclone.snapshot';self.inner=None -FILE_3_L122= def _ensure(self): -FILE_3_L123= if self.inner is not None:return self.inner -FILE_3_L124= b,e,bind=_private_config() -FILE_3_L125= if e!=self.entries or bind!=self.binding:raise RuntimeError('RCLONE_SEMANTIC_DRIFT') -FILE_3_L126= _write_private(self.snapshot,b) -FILE_3_L127= self.inner=large.ReadOnlyLarge(str(RCLONE),self.snapshot,e,bind,self.guard,self.work,self.deadline,self.logical_id) -FILE_3_L128= return self.inner -FILE_3_L129= def list_generations(self,*a,**k):return self._ensure().list_generations(*a,**k) -FILE_3_L130= def get(self,*a,**k):return self._ensure().get(*a,**k) -FILE_3_L131= def read_stream(self,*a,**k):return self._ensure().read_stream(*a,**k) -FILE_3_L132= def close(self): -FILE_3_L135= -FILE_3_L136=def selftest(): -FILE_3_L137= if _sha(PLAN,1024*1024)!=PLAN_SHA:return False -FILE_3_L138= for p,h in ((RCLONE,RCLONE_SHA),(VZDUMP,VZDUMP_SHA),(ZSTD,ZSTD_SHA)): -FILE_3_L139= if _sha(p)!=h:return False -FILE_3_L140= return True -FILE_3_L141= -FILE_3_L142=def run_target(target,state_writer=None): -FILE_3_L143= if not isinstance(target,dict) or target.get('logical_id')!=CT_ID or target.get('vmid')!=112:return (_ for _ in ()).throw(RuntimeError('CT112_DESCRIPTOR')) -FILE_3_L144= if _sha(PLAN,1024*1024)!=PLAN_SHA:raise RuntimeError('PLAN_REFERENCE_IDENTITY') -FILE_3_L145= tools={'vzdump':_tool(VZDUMP,VZDUMP_SHA),'zstd':_tool(ZSTD,ZSTD_SHA)};_tool(RCLONE,RCLONE_SHA) -FILE_3_L146= pvesh_sha=_sha(PVESH);now=int(time.time());current=_fresh_plan(now);job=_select_live_ct112(current,now) -FILE_3_L147= expected=job['config_sha256'];mapping=_canonical_host_mapping() -FILE_3_L148= def producer_guard(): -FILE_3_L153= pve282.live_gate(fresh,str(PVESH),pvesh_sha,expected) -FILE_3_L154= producer_guard() -FILE_3_L155= cfg,entries,binding=_private_config() -FILE_3_L156= generation='V2-'+time.strftime('%Y%m%dT%H%M%SZ',time.gmtime())+'-'+secrets.token_hex(6) -FILE_3_L157= work=STAGING/('backup-v2-'+generation);work.mkdir(mode=0o700,parents=False,exist_ok=False) -FILE_3_L158= deadline=time.monotonic()+21600 -FILE_3_L159= def cloud_guard(): -FILE_3_L160= _,e,b=_private_config() -FILE_3_L161= if e!=entries or b!=binding:raise RuntimeError('RCLONE_SEMANTIC_DRIFT') -FILE_3_L162= writer=large.CloudLarge(str(RCLONE),RCLONE_CFG,entries,binding,cloud_guard,work/'upload',deadline) -FILE_3_L163= reader=LazyReader(work/'restore',entries,binding,cloud_guard,deadline,CT_ID) -FILE_3_L164= def produce(j,w):return pve282.produce(j,w,tools,producer_guard,mapping=mapping,expected_config=expected,seconds=14400,emit=lambda k,v:None) -FILE_3_L165= def compression(path): -FILE_3_L166= p=subprocess.run([str(ZSTD),'--test','--quiet','--',str(path)],stdout=subprocess.DEVNULL,stderr=subprocess.DEVNULL,timeout=3600,check=False,env=ENV) -FILE_3_L167= return p.returncode==0 -FILE_3_L168= try: -FILE_3_L169= r=guest266.run_flow(job,work,entries,writer,reader,produce,producer_guard,cloud_guard,compression,generation,lambda k,v:None) -FILE_3_L170= reps=r.get('replicas') -FILE_3_L171= if not isinstance(reps,list) or len(reps)!=2 or len({x.get('account_id') for x in reps})!=2:raise RuntimeError('REPLICA_PHYSICAL_INDEPENDENCE') -FILE_3_L172= if r.get('archive_cloud_restore_verified') is not True or r.get('local_payload_files_remaining')!=0:raise RuntimeError('CT112_RESTORE_CONTRACT') -FILE_3_L173= out={**r,'status':'OK','cloud_only_independent_restore_verified':True,'local_payload_files_remaining':0} -FILE_3_L174= reader.close();shutil.rmtree(work) -FILE_3_L175= if work.exists():raise RuntimeError('CT112_WORKSPACE_CLEANUP') -FILE_3_L176= if state_writer is not None: -FILE_3_L177= state_writer(CT_ID,'SUCCESS',generation=r['generation'],payload_sha256=r['payload_sha256'],manifest_sha256=r['manifest_sha256'],replicas=r['replicas'],green_verified=True) -FILE_3_L178= return out -FILE_3_L179= except BaseException: -FILE_3_L180= try:reader.close() -FILE_3_L181= except BaseException:pass -FILE_3_L182= raise -FILE_4_PATH=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py -FILE_4_BYTES=59719 -FILE_4_SHA256=b6e79f087b9f1d21dac4f77a7b46b743299bbb85bc716e80d2ea67c2e038bcd7 -FILE_4_CONTEXT_LINE_COUNT=180 -FILE_4_L1=#!/usr/bin/python3 -FILE_4_L2=import ast, base64, datetime as dt, hashlib, io, json, os, pathlib, random, re, select, shlex, shutil, stat, subprocess, sys, tarfile, tempfile, time -FILE_4_L3= -FILE_4_L4=PART_SIZE=536870912 -FILE_4_L5=RESERVE_BYTES=536870912 -FILE_4_L6=STATE_ROOT=pathlib.Path('/var/lib/homelab-backup-v2') -FILE_4_L7=CONFIG_ROOT=pathlib.Path('/etc/homelab-backup-v2') -FILE_4_L8=TARGETS_PATH=CONFIG_ROOT/'targets.json' -FILE_4_L9=WORK_ROOT=pathlib.Path('/mnt/staging/homelab-backup-v2') -FILE_4_L10=ENGINE_PATH=pathlib.Path('/usr/local/sbin/homelab-backup-v2') -FILE_4_L11=RCLONE=shutil.which('rclone') or '/usr/bin/rclone' -FILE_4_L12=SSH=shutil.which('ssh') or '/usr/bin/ssh' -FILE_4_L13=SCP=shutil.which('scp') or '/usr/bin/scp' -FILE_4_L14=SSH_KEYSCAN=shutil.which('ssh-keyscan') or '/usr/bin/ssh-keyscan' -FILE_4_L30=P282_SHA='73c6cb7773eb8e82d1e7ed5ab4733b4afb88f74ae69c09125d0378d6e480e012' -FILE_4_L31=P265=pathlib.Path('/opt/homelab-backup-v2/releases/pve-producer-265r1/pve265.py') -FILE_4_L32=P265_SHA='cd2f634414a8e38a3789015aeca5a9b47821f77d7d1fa61dcdbdc866172963be' -FILE_4_L33=RESULT_CLASSES=('SUCCESS','DEFER','PARTIAL_VALID','FAIL') -FILE_4_L34=HISTORICAL_RELEASE_GRAPH=[ -FILE_4_L35= (pathlib.Path('/opt/homelab-backup-v2/releases/core-260r1/v2core.py'),'e285bf9a5678d07dcbb90261f9f93e7826ff524e04cfec780bf5a3c4b0dc29f2'), -FILE_4_L36= (pathlib.Path('/opt/homelab-backup-v2/releases/transport-261r1/transport261.py'),'b9b92f957bf441cb5d1702f4881897328e6c0338f8c3f6d41d27d9a349b37688'), -FILE_4_L51= pathlib.Path('/opt/homelab-backup-v2/releases/large-cloud-264r1'), -FILE_4_L52= pathlib.Path('/opt/homelab-backup-v2/releases/stream-263r1'), -FILE_4_L53= pathlib.Path('/opt/homelab-backup-v2/releases/transport-261r1'), -FILE_4_L54= pathlib.Path('/opt/homelab-backup-v2/releases/restore-262r1'), -FILE_4_L55= pathlib.Path('/opt/homelab-backup-v2/releases/core-260r1'), -FILE_4_L56= pathlib.Path('/opt/homelab-backup-v2/releases/vm-backup-283r1'), -FILE_4_L57=] -FILE_4_L58=_HISTORICAL_HELPER = r'''import importlib,importlib.util,json,pathlib,sys -FILE_4_L59=mode=sys.argv[1] -FILE_4_L60=if mode=='import_all': -FILE_4_L61= out={} -FILE_4_L74=''' -FILE_4_L75=os.umask(0o077) -FILE_4_L76= -FILE_4_L77=REMOTES=[ -FILE_4_L78= ('pve01-mail-01-crypt','pve01-mail-01'),('pve01-mail-02-crypt','pve01-mail-02'),('pve01-mail-03-crypt','pve01-mail-03'),('pve01-mail-04-crypt','pve01-mail-04'),('pve01-mail-05-crypt','pve01-mail-05'), -FILE_4_L79= ('pve02-mail-01-crypt','pve02-mail-01'),('pve02-mail-02-crypt','pve02-mail-02'),('pve02-mail-03-crypt','pve02-mail-03'),('pve02-mail-04-crypt','pve02-mail-04'), -FILE_4_L80= ('pve03-mail-01-crypt','pve03-mail-01'),('pve03-mail-02-crypt','pve03-mail-02'),('pve03-mail-03-crypt','pve03-mail-03'),('pve03-mail-04-crypt','pve03-mail-04')] -FILE_4_L81= -FILE_4_L82=class BackupError(RuntimeError): -FILE_4_L83= def __init__(self, code, detail=''): -FILE_4_L102= if not b: break -FILE_4_L103= h.update(b); n+=len(b) -FILE_4_L104= if remain is not None: remain-=len(b) -FILE_4_L105= if length is not None and n!=length: raise BackupError('LOCAL_PART_SHORT_READ') -FILE_4_L106= return h.hexdigest() -FILE_4_L107= -FILE_4_L108=def part_plan(n): -FILE_4_L109= if not isinstance(n,int) or n<=0: raise BackupError('PAYLOAD_SIZE_INVALID') -FILE_4_L110= out=[]; off=0; idx=1 -FILE_4_L111= while offtimeout: -FILE_4_L314= p.kill(); p.wait(); raise BackupError('RCLONE_CAT_TIMEOUT') -FILE_4_L315= b=p.stdout.read(1024*1024) -FILE_4_L316= if not b: break -FILE_4_L317= h.update(b); n+=len(b) -FILE_4_L318= p.stderr.read(); rc=p.wait() -FILE_4_L319= if rc!=0: raise BackupError('RCLONE_CAT_FAILED',str(rc)) -FILE_4_L320= if expected_bytes is not None and n!=expected_bytes: raise BackupError('RCLONE_CAT_SIZE_MISMATCH') -FILE_4_L321= return h.hexdigest(),n -FILE_4_L322= -FILE_4_L323=def rclone_cat_bytes(remote_path,max_bytes=4*1024*1024): -FILE_4_L324= p=run(rclone_base_args()+['cat',remote_path,'--contimeout','20s','--timeout','60s'],timeout=90) -FILE_4_L325= if len(p.stdout)>max_bytes: raise BackupError('RCLONE_SMALL_OBJECT_TOO_LARGE') -FILE_4_L326= return p.stdout -FILE_4_L327= -FILE_4_L328=def generation_id(logical_id): -FILE_4_L329= ts=dt.datetime.now(dt.timezone.utc).strftime('%Y%m%dT%H%M%SZ') -FILE_4_L330= return f'v2-{logical_id}-{ts}-{os.urandom(6).hex()}' -FILE_4_L331= -FILE_4_L332=def upload_and_seal(t,payload_path,source_meta): -FILE_4_L333= payload_path=pathlib.Path(payload_path); n=payload_path.stat().st_size -FILE_4_L334= psha=sha256_file(payload_path); reps,bad,healthy=allocator(n) -FILE_4_L335= gen=source_meta['generation']; rel=remote_generation_path(t['scope'],t['logical_id'],gen) -FILE_4_L336= if remote_exists_any(t['scope'],t['logical_id'],gen): raise BackupError('GENERATION_COLLISION') -FILE_4_L337= parts=part_plan(n) -FILE_4_L338= for p in parts: p['sha256']=sha256_file(payload_path,p['offset'],p['bytes']) -FILE_4_L339= manifest=make_manifest(t['scope'],t['logical_id'],gen,n,psha,parts,reps,source_meta) -FILE_4_L340= mb=canonical_json(manifest)+b'\n'; msha=sha256_bytes(mb) -FILE_4_L341= for rep in reps: -FILE_4_L342= base=rep['crypt']+':'+rel -FILE_4_L343= for part in parts: -FILE_4_L344= rp=base+'/parts/'+part['name'] -FILE_5_PATH=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/representative_driver.py -FILE_5_BYTES=4214 -FILE_5_SHA256=b3050a8f95e6d9826a4f0786cd3ca32012b6ab10486d10e4981b50b8a0affff2 -FILE_5_CONTEXT_LINE_COUNT=60 -FILE_5_L3= -FILE_5_L4=This module does not discover credentials, modify scheduler state, or implement the -FILE_5_L5=CT112 producer/cloud path. It routes CT112 to an explicitly supplied callback and -FILE_5_L6=isolates legacy engine state writes into a candidate-owned state directory. -FILE_5_L7=""" -FILE_5_L8=from __future__ import annotations -FILE_5_L9= -FILE_5_L10=import json -FILE_5_L11=import os -FILE_5_L12=from pathlib import Path -FILE_5_L13=from typing import Callable, Iterable, Mapping, Any -FILE_5_L14= -FILE_5_L15=LEGACY_IDS=( -FILE_5_L16= 'node-pve01', -FILE_5_L24=) -FILE_5_L25=CT112_ID='guest-pve01-lxc-112' -FILE_5_L26=REPRESENTATIVE_IDS=(CT112_ID,)+LEGACY_IDS -FILE_5_L27=CANDIDATE_ROOT=Path(__file__).resolve().parent -FILE_5_L28=STATE_ROOT=CANDIDATE_ROOT/'state' -FILE_5_L29= -FILE_5_L30=class ContractError(RuntimeError): -FILE_5_L31= pass -FILE_5_L36= raise ContractError('TARGET_LOGICAL_ID') -FILE_5_L37= return value -FILE_5_L38= -FILE_5_L39=def partition_targets(targets: Iterable[Mapping[str, Any]]): -FILE_5_L40= selected={} -FILE_5_L41= wanted=set(REPRESENTATIVE_IDS) -FILE_5_L42= for target in targets: -FILE_5_L62= with os.fdopen(fd,'wb',closefd=True) as f: -FILE_5_L63= f.write(data);f.flush();os.fsync(f.fileno()) -FILE_5_L64= os.replace(tmp,path) -FILE_5_L65= except BaseException: -FILE_5_L66= try: os.unlink(tmp) -FILE_5_L67= except FileNotFoundError: pass -FILE_5_L68= raise -FILE_5_L72= def write_state(logical_id,result,**kw): -FILE_5_L73= if logical_id not in LEGACY_IDS: -FILE_5_L74= raise ContractError('STATE_TARGET_FORBIDDEN') -FILE_5_L75= payload={'logical_id':logical_id,'result':result,'meta':kw,'schema_version':1} -FILE_5_L76= _atomic_json(root/(logical_id+'.json'),payload) -FILE_5_L77= return write_state -FILE_5_L78= -FILE_5_L79=def run_legacy_lane(engine, target: Mapping[str, Any]): -FILE_5_L97= raise ContractError('CT112_RESULT_SCHEMA') -FILE_5_L98= if result.get('logical_id')!=CT112_ID or result.get('status')!='OK': -FILE_5_L99= raise ContractError('CT112_RESULT_STATUS') -FILE_5_L100= if result.get('cloud_only_independent_restore_verified') is not True: -FILE_5_L101= raise ContractError('CT112_RESTORE_NOT_VERIFIED') -FILE_5_L102= if result.get('local_payload_files_remaining')!=0: -FILE_5_L103= raise ContractError('CT112_LOCAL_PAYLOAD_REMAINS') -FILE_5_L104= return result -FILE_5_L105= -FILE_5_L106=def run_representative(engine, targets, ct112_callback: Callable[[Mapping[str, Any]], Any]): -FILE_5_L107= if not callable(ct112_callback): -FILE_5_L108= raise ContractError('CT112_CALLBACK_REQUIRED') -FILE_5_L109= legacy,ct112=partition_targets(targets) -FILE_5_L110= results=[_validate_ct112_result(ct112_callback(ct112))] -FILE_5_L111= for target in legacy: -FILE_5_L112= results.append(run_legacy_lane(engine,target)) -FILE_6_PATH=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/wrapper.py -FILE_6_BYTES=5055 -FILE_6_SHA256=2b5eb4fc0f6f466a0d3e5a007af987092b6c17baa3ff9241a6afc2c2e97df1d5 -FILE_6_CONTEXT_LINE_COUNT=43 -FILE_6_L1=#!/usr/bin/env python3 -FILE_6_L2=from __future__ import annotations -FILE_6_L3=import errno,fcntl,importlib.util,os,sys -FILE_6_L4=from pathlib import Path -FILE_6_L5=ROOT=Path(__file__).resolve().parent -FILE_6_L6=CT_ID='guest-pve01-lxc-112' -FILE_6_L7=LEGACY_IDS=('node-pve01','md-relay','spb-ingress','us-netbird','router-netcraze','xf-codevipe','xf-zakrutim','control-bootstrap-v2') -FILE_6_L35= 'guest_type':'lxc','due_seconds':due,'enabled':True,'green1':True} -FILE_6_L36= -FILE_6_L37=def load_targets(): -FILE_6_L38= base=canonical_targets();return base+[_ct_descriptor(base)] -FILE_6_L39= -FILE_6_L40=def backup_target(t,green_verify=False): -FILE_6_L41= if not isinstance(t,dict) or t.get('logical_id')!=CT_ID: -FILE_6_L42= return _legacy_backup_target(t,green_verify) -FILE_6_L43= try: -FILE_6_L44= r=ct112.run_target(t,state_writer=legacy.write_state) -FILE_6_L45= return {'logical_id':CT_ID,'generation':r['generation'],'result':'SUCCESS','payload_bytes':r['payload_bytes'], -FILE_6_L46= 'replicas':2,'manifest_sha256':r['manifest_sha256'],'payload_sha256':r['payload_sha256']} -FILE_6_L47= except BaseException: -FILE_6_L48= try: legacy.write_state(CT_ID,'FAIL',reason='CT112_PIPELINE_FAILED') -FILE_6_L49= except BaseException: pass -FILE_6_L50= raise legacy.BackupError('CT112_PIPELINE_FAILED') from None -FILE_6_L51= -FILE_6_L52=legacy.load_targets=load_targets -FILE_6_L53=legacy.backup_target=backup_target -FILE_6_L54= -FILE_6_L55=def _legacy_reused_boundary_selftest(): -FILE_6_L56= if legacy.PART_SIZE!=536870912: raise RuntimeError('LEGACY_PART_SIZE') -FILE_6_L57= if len(legacy.REMOTES)!=13 or len({p for _,p in legacy.REMOTES})!=13: raise RuntimeError('LEGACY_REMOTES') -FILE_6_L58= d=legacy.default_targets() -FILE_6_L59= if not isinstance(d,list) or len(d)<28: raise RuntimeError('LEGACY_DEFAULT_TARGET_COUNT') -FILE_6_L60= want={'ct112',*LEGACY_IDS} -FILE_6_L67= -FILE_6_L68=def selftest(): -FILE_6_L69= _legacy_reused_boundary_selftest() -FILE_6_L70= base=canonical_targets(); eff=load_targets() -FILE_6_L71= if len(eff)!=29 or sum(1 for x in eff if x.get('logical_id')==CT_ID)!=1: raise RuntimeError('EFFECTIVE_TARGETS') -FILE_6_L72= legacy_rows,ct=driver.partition_targets(eff) -FILE_6_L73= if [x['logical_id'] for x in legacy_rows]!=list(LEGACY_IDS) or ct.get('vmid')!=112: raise RuntimeError('REPRESENTATIVE_PARTITION') -FILE_6_L74= if ct112.selftest() is not True: raise RuntimeError('CT112_HANDLER_SELFTEST') -FILE_6_L75= print('SELFTEST_REPLACEMENT486=PASS',flush=True);return 0 -FILE_6_L76= -FILE_7_PATH=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/CONTRACT.json -FILE_7_BYTES=2077 -FILE_7_SHA256=a414715f1b91b3701c52073709a80c38218f76e3961618e8af61489211e50c01 -FILE_7_CONTEXT_LINE_COUNT=1 -FILE_7_L1={"candidate":"pve-lxc-adapter-411r1","next_gate":"PVE_LXC_REAL_ENVIRONMENT_DRY_RUN","owner":{"MappedScratch":"pve270","capture_archive":"pve270","dump_argv":"pve265","pve282.old":"pve265","pve282.proven":"pve270","validate_mapping":"pve270"},"schema":1,"signatures":{"pve265":{"dump_argv":{"args":["job","executable","tmpdir","now"],"defaults":0,"kw_defaults":[],"kwarg":null,"kwonly":[],"posonly":[],"vararg":null},"make_plan":{"args":["rows","configs","storage_rows","defaults","staging_free","now","node"],"defaults":1,"kw_defaults":[],"kwarg":null,"kwonly":[],"posonly":[],"vararg":null},"produce":{"args":["job","work","tools","guard","seconds"],"defaults":1,"kw_defaults":[],"kwarg":null,"kwonly":[],"posonly":[],"vararg":null},"validate_job":{"args":["job","now"],"defaults":0,"kw_defaults":[],"kwarg":null,"kwonly":[],"posonly":[],"vararg":null}},"pve270":{"capture_archive":{"args":["argv","destination","cap","seconds","check"],"defaults":0,"kw_defaults":[],"kwarg":null,"kwonly":[],"posonl -FILE_8_PATH=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/PLAN265_REFERENCE.json -FILE_8_BYTES=7210 -FILE_8_SHA256=8d36e82080be93a8c929a9211191fa868166b9e7b6152c4c7689fa52b6720757 -FILE_8_CONTEXT_LINE_COUNT=1 -FILE_8_L1={"all_guest_coverage_eligible":true,"backup_executed":false,"capacity_policy":"four archive copies plus 4GiB; capped producer; actual compressed size still unknown","collected_at_utc":"2026-09-12T06:57:40.611329+00:00","full_host_coverage":false,"global_blockers":[],"global_defaults":{"blockers":[],"keys":[],"present":true,"sha256":"cf34c8e3812547954bb1d08b50212b6e148ee9cf2b629e895a843ed49d623b38"},"guest_membership_and_configs_stable":true,"jobs":[{"application_consistency_verified":false,"archive_cap_bytes":9516036916,"blockers":[],"cdrom_count":0,"cdrom_media_included":false,"compression_dependent":false,"config_sha256":"09957aacd41ba9eeaf5fd4eda13936d580c9082a46895433379aa9dd0bad5969","configured_archive_bound_bytes":9516036916,"configured_disk_bytes":8589934592,"configured_full_cycle_fits":true,"coverage_eligible":true,"disks":[{"backup_setting":"included_default_rootfs","class":"managed-volume","configured_size_bytes":8589934592,"slot":"rootfs","storage":"local-lvm"}],"global_def -FILE_9_PATH=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/PROVENANCE.json -FILE_9_BYTES=4310 -FILE_9_SHA256=303f44dc8e91c8e6a675f9ea406ce4b7532ed98440a9b6807fa593ede9d33254 -FILE_9_CONTEXT_LINE_COUNT=1 -FILE_9_L1={"anchors":{"SUPPORT-260915-INFRA-DR-BACKUPV2-PVE-LXC-EXACT-CALL-GRAPH-PROOF-401R1":"840b3f1e8b023899c0554305a97af2d55dc71cf9","SUPPORT-260915-INFRA-DR-BACKUPV2-PVE-LXC-IMMUTABLE-ADAPTER-CANDIDATE-STAGE-FIXTURE-RCA-FIX-407R1":"0261475cdd35d3b762de0a29e46f3acd0b9af229","SUPPORT-260915-INFRA-DR-BACKUPV2-PVE-LXC-IMMUTABLE-ADAPTER-CANDIDATE-STAGE-RCA-FIX-403R1":"e3f753ce9cf95e21750e9ad7f9574cafc199b3ce","SUPPORT-260915-INFRA-DR-BACKUPV2-PVE-LXC-PERL270-HISTORICAL-TEST-RCA-404R1":"58f61ce62c3b6a57d8c5fc2465d2537d6728f9d5","SUPPORT-260915-INFRA-DR-BACKUPV2-PVE-LXC-PERLPIN-EXEC-FS-RCA-409R1":"0db666a5a1f4796b567663adfc5568817e87efa5","SUPPORT-260915-INFRA-DR-BACKUPV2-PVE-LXC-PERLPIN-NEED-TRACE-RCA-408R1":"44cdff7dab9e60aff1d69c12d2c32a4b26d04de8","SUPPORT-260915-INFRA-DR-BACKUPV2-PVE-LXC-PERLPIN-PRODUCTION-VS-FIXTURE-RCA-406R1":"4c5836ff0ef73cb2ef306857be713383ba5e85f4","SUPPORT-260915-INFRA-DR-BACKUPV2-PVE-LXC-PERLPIN-ROOT-SUITE-STRUCTURED-RCA-410R1":"4673bfaf04877370bc4ac02fef37885eb82cb1eb -FILE_10_PATH=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/adapter.py -FILE_10_BYTES=2989 -FILE_10_SHA256=18eb29cef165371b48bcb78e1a456e1939e6eff253787218133162cc89ab9fa3 -FILE_10_CONTEXT_LINE_COUNT=30 -FILE_10_L1=#!/usr/bin/python3 -FILE_10_L2=import hashlib,inspect,json,pathlib,sys -FILE_10_L3=BASE=pathlib.Path(__file__).resolve().parent -FILE_10_L4=EXPECTED={ -FILE_10_L5='v2core.py':'e285bf9a5678d07dcbb90261f9f93e7826ff524e04cfec780bf5a3c4b0dc29f2', -FILE_10_L6='transport261.py':'b9b92f957bf441cb5d1702f4881897328e6c0338f8c3f6d41d27d9a349b37688', -FILE_10_L7='restore262.py':'56d1b961c8d5654d5e2ee2755268db46bd2202bca11e69788febf991cfaa79f1', -FILE_10_L8='stream263.py':'a83d03a86b93a855716658652dd073fcb281cd387fc6200fe4bc2a922f39ea16', -FILE_10_L9='nodeplan263.py':'db5c16ee24d25aa1694530332d3190ab7cc5d4f8eb69067d025e27ad5c995060', -FILE_10_L10='large264.py':'5cc4641fc35e4dd554495a36146da7dbbb7f142d1450e04cc5d3c89678b8497a', -FILE_10_L19= return h.hexdigest() -FILE_10_L20=def verify_sources(): -FILE_10_L21= for name,want in EXPECTED.items(): -FILE_10_L22= p=BASE/name -FILE_10_L23= if not p.is_file() or p.is_symlink() or sha(p)!=want:raise RuntimeError('SOURCE_IDENTITY_'+name) -FILE_10_L24=def load_graph(): -FILE_10_L25= import guest266,pve265,pve270,pve282 -FILE_10_L26= if hasattr(guest266,'dump_argv'):raise RuntimeError('GUEST266_UNEXPECTED_DUMP_ARGV') -FILE_10_L27= if not hasattr(pve265,'dump_argv'):raise RuntimeError('PVE265_DUMP_ARGV_MISSING') -FILE_10_L28= if getattr(pve270,'old',None) is not pve265:raise RuntimeError('PVE270_OLD_NOT_PVE265') -FILE_10_L35=def describe(): -FILE_10_L36= _,p265,p270,p282=load_graph() -FILE_10_L37= funcs={'pve265.dump_argv':p265.dump_argv,'pve265.validate_job':p265.validate_job,'pve265.make_plan':p265.make_plan,'pve270.validate_mapping':p270.validate_mapping,'pve270.capture_archive':p270.capture_archive,'pve270.produce':p270.produce,'pve282.select112':p282.select112,'pve282.produce':p282.produce} -FILE_10_L38= return {'schema':1,'bundle':'pve-lxc-adapter-411r1','source_sha256':EXPECTED,'signatures':{k:str(inspect.signature(v)) for k,v in funcs.items()},'owner':{'dump_argv':'pve265','MappedScratch':'pve270','capture_archive':'pve270','validate_mapping':'pve270','pve282.old':'pve265','pve282.proven':'pve270'}} -FILE_10_L39=def main(): -FILE_10_L40= verify_sources(); mode=sys.argv[1] if len(sys.argv)>1 else 'selftest'; d=describe() -FILE_10_L41= if mode not in ('selftest','describe'):raise SystemExit(2) -FILE_10_L42= print(json.dumps(d,sort_keys=True,separators=(',',':'))) -FILE_10_L43= if mode=='selftest':print('ADAPTER_SELFTEST=PASS') -FILE_10_L44=if __name__=='__main__':main() -FILE_11_PATH=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/guest266.py -FILE_11_BYTES=7385 -FILE_11_SHA256=86433bc424d5fa5017c20f7a237c82874b02357a3283660d8a6b0039ef2bc868 -FILE_11_CONTEXT_LINE_COUNT=75 -FILE_11_L1="""266R1 bounded real-guest orchestration. Import has no side effects. -FILE_11_L2= -FILE_11_L3=One small LXC rootfs, no compression-dependent admission. No node-wide approval, -FILE_11_L4=no guest restore/start/delete, no cloud deletion. Archive CRC/SHA is not boot proof. -FILE_11_L5=""" -FILE_11_L6=import copy,os,time -FILE_11_L7=from pathlib import Path -FILE_11_L8=import v2core as core -FILE_11_L9=import transport261 as t -FILE_11_L10=import stream263 as stream -FILE_11_L11=import large264 as large -FILE_11_L12=import pve265 as producer -FILE_11_L13= -FILE_11_L14=MAX_CONFIGURED_BYTES=8*1024**3 -FILE_11_L15=RESERVE_BYTES=536870912 -FILE_11_L25= -FILE_11_L26=def choose(plan,now): -FILE_11_L27= rows=plan_rows(plan);t.need(plan.get('global_blockers')==[],'GLOBAL_DEFAULT_BLOCK') -FILE_11_L28= candidates=[] -FILE_11_L29= for job in rows: -FILE_11_L30= if not (job.get('type')=='lxc' and job.get('coverage_eligible') is True and -FILE_11_L31= job.get('blockers')==[] and job.get('compression_dependent') is False and -FILE_11_L35= disks[0].get('class')=='managed-volume' and disks[0].get('backup_setting') in ('included','included_default_rootfs')):continue -FILE_11_L36= n=job.get('configured_disk_bytes') -FILE_11_L37= if type(n) is not int or not 0=max(1024**3,chosen['configured_disk_bytes']),'SNAPSHOT_HEADROOM_UNPROVEN') -FILE_11_L45= return copy.deepcopy(chosen) -FILE_11_L46= -FILE_11_L47=def same_state(before,after,vmid): -FILE_11_L48= old=plan_rows(before);new=plan_rows(after) -FILE_11_L64= if type(task.get('endtime')) is int and task.get('status') not in (None,'RUNNING'):continue -FILE_11_L65= kind=task['type'];target=str(task.get('id','')) -FILE_11_L66= if kind in ('vncshell','vncterm','vncproxy','spiceproxy'):continue -FILE_11_L67= t.need(target!=str(vmid) and not any(x in kind.lower() for x in ('dump','restore','migrat','snapshot','clone','move_disk','resize')),'COMPETING_PVE_TASK') -FILE_11_L68= -FILE_11_L69=def check_result(result,job,work): -FILE_11_L70= t.need(type(result) is dict,'PRODUCER_RESULT_SCHEMA') -FILE_11_L80= finally:os.close(fd) -FILE_11_L81= return expected -FILE_11_L82= -FILE_11_L83=def run_flow(job,work,entries,cloud,reader,produce,state_guard,guard,compression_check,generation,say,clock=lambda:int(time.time())): -FILE_11_L84= """Caller provides real fresh PVE guards; simulation callbacks live in tests only.""" -FILE_11_L85= producer.validate_job(job,clock());core.validate_inventory(entries) -FILE_11_L86= t.need(job['type']=='lxc' and job.get('compression_dependent') is False,'FIRST_GUEST_POLICY') -FILE_11_L87= t.need(core.valid_name(generation),'GENERATION_ID') -FILE_11_L88= t.need(all(callable(f) for f in (produce,state_guard,guard,compression_check,say,clock)),'CALLBACKS_REQUIRED') -FILE_11_L89= work=Path(work);state_guard();guard() -FILE_11_L90= for sub in ('producer','upload','restore'):t.make_dir(work/sub,exclusive=True) -FILE_11_L91= say('PHASE','REAL_SINGLE_GUEST_SNAPSHOT') -FILE_11_L92= produced=produce(job,work/'producer') -FILE_11_L93= source=check_result(produced,job,work/'producer');state_guard();guard() -FILE_11_L94= t.create(work/'PRODUCER.json',core.canonical_bytes(produced)) -FILE_11_L95= say('PRODUCER_RC',0);say('PAYLOAD_BYTES',produced['size']);say('ARCHIVE_SHA256',produced['sha256']) -FILE_11_L96= logical='guest-%s-%s-%d'%(job['node'],job['type'],job['vmid']) -FILE_11_L97= say('PHASE','TWO_REPLICA_UPLOAD_AND_READBACK') -FILE_11_L98= up=large.upload(source,work/'upload',entries,cloud,generation,logical,RESERVE_BYTES,guard,clock=clock) -FILE_11_L99= t.need(up['manifest']['payload_bytes']==produced['size'] and up['manifest']['payload_sha256']==produced['sha256'],'MANIFEST_PRODUCER_MISMATCH') -FILE_11_L100= say('SEALED_QUORUM_VERIFIED','YES');mh=core.manifest_hash(up['manifest']);say('MANIFEST_SHA256',mh) -FILE_11_L101= say('PHASE','CLOUD_DISCOVERY_AND_ARCHIVE_RECONSTRUCTION') -FILE_11_L102= found=large.discover(reader,entries,logical,mh) -FILE_11_L103= restored=large.restore(work/'restore',reader,found) -FILE_11_L104= t.need(compression_check(work/'restore/restored.bin') is True,'RECONSTRUCTED_COMPRESSION_FAILED') -FILE_11_L105= state_guard();guard();say('PHASE','GUARDED_LOCAL_PAYLOAD_CLEANUP') -FILE_11_L106= count=large.cleanup(up,restored,reader,guard) -FILE_11_L107= return {'state':'SINGLE_GUEST_ARCHIVE_SEALED_AND_CLOUD_RECONSTRUCTED','vmid':job['vmid'],'type':job['type'],'node':job['node'], -FILE_11_L108= 'generation':generation,'logical_id':logical,'manifest_sha256':mh,'payload_bytes':produced['size'],'payload_sha256':produced['sha256'], -FILE_11_L109= 'replicas':up['manifest']['replicas'],'parts_count':len(up['manifest']['parts']),'archive_cloud_restore_verified':True, -FILE_11_L110= 'guest_restore_verified':False,'application_consistency_verified':False,'whole_node_backup':False,'local_payload_files_remaining':0, -FILE_11_L111= 'files_deleted':count,'accounts_scanned':found['accounts_scanned'],'config_sha256':job['config_sha256']} -FILE_12_PATH=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/large264.py -FILE_12_BYTES=16514 -FILE_12_SHA256=5cc4641fc35e4dd554495a36146da7dbbb7f142d1450e04cc5d3c89678b8497a -FILE_12_CONTEXT_LINE_COUNT=180 -FILE_12_L1="""264R1 streaming cloud adapter and synthetic boundary proof. -FILE_12_L2= -FILE_12_L3=No work on import. No guest/backup producer and no cloud deletion. Existing -FILE_12_L4=small-canary adapters are not modified. Only caller-owned local payload is removed, -FILE_12_L5=after two cloud seals and a separately cloud-discovered restore have been checked. -FILE_12_L6=""" -FILE_12_L7=import hashlib -FILE_12_L8=import os -FILE_12_L9=from pathlib import Path -FILE_12_L10=import selectors -FILE_12_L11=import signal -FILE_12_L12=import subprocess -FILE_12_L13=import time -FILE_12_L14=import v2core as core -FILE_12_L15=import transport261 as t -FILE_12_L16=import restore262 as r -FILE_12_L17=import stream263 as s -FILE_12_L18= -FILE_12_L19=BOUNDARY_BYTES=536870929 -FILE_12_L20=LOGICAL_ID='stream-canary-pve01' -FILE_12_L21= -FILE_12_L22= -FILE_12_L23=def process_stream(argv,env,size,digest,consume,seconds): -FILE_12_L24= """Drain both pipes, hashing stdout incrementally; never retain the whole part.""" -FILE_12_L25= t.need(type(argv) is list and argv and seconds>0,'STREAM_PROCESS_ARGUMENTS') -FILE_12_L26= sink=s.DigestSink(size,digest) -FILE_12_L27= p=subprocess.Popen(argv,stdin=subprocess.DEVNULL,stdout=subprocess.PIPE,stderr=subprocess.PIPE, -FILE_12_L38= remaining=deadline-time.monotonic();t.need(remaining>0,'STREAM_TIMEOUT') -FILE_12_L39= rc=p.wait(timeout=remaining);t.need(rc==0,'STREAM_PROCESS_RC_FAILED') -FILE_12_L40= result=sink.finish();complete=True;return result -FILE_12_L41= except subprocess.TimeoutExpired:raise t.Stop('STREAM_TIMEOUT') from None -FILE_12_L42= finally: -FILE_12_L43= if not complete or p.poll() is None: -FILE_12_L44= try:os.killpg(p.pid,signal.SIGKILL) -FILE_12_L53= '--cache-dir',str(Path(work)/'cache')]+args -FILE_12_L54= -FILE_12_L55= -FILE_12_L56=class CloudLarge(t.RcloneIO): -FILE_12_L57= """Reuse the existing crypt inventory/namespace guards, not its in-memory payload I/O.""" -FILE_12_L58= def call(self,args,limit): -FILE_12_L59= t.need(type(args) is list and args and args[0] in ('about','lsjson','copyto','cat'),'RCLONE_VERB_FORBIDDEN') -FILE_12_L60= t.need(type(limit) is int and 0<=limit<=core.MANIFEST_MAX_BYTES+1,'METADATA_BOUND') -FILE_12_L61= self.check();seconds=min(3600 if args[0]=='copyto' else 90,self.deadline-time.monotonic()) -FILE_12_L62= t.need(seconds>0,'CLOUD_DEADLINE');self.calls+=1 -FILE_12_L63= if args[0]=='copyto':self.writes_attempted+=1 -FILE_12_L64= rc,out=t.bounded_process(flags(self.executable,self.config_path,self.work,args),self.env,limit,seconds) -FILE_12_L65= self.check();return rc,out -FILE_12_L66= def put(self,remote,name,path): -FILE_12_L67= dest=self.address(remote,name) -FILE_12_L68= if name in ('manifest.json','SEALED.json'): -FILE_12_L69= return super().put(remote,name,path) -FILE_12_L70= part=next((p for p in self.manifest['parts'] if p['name']==name),None) -FILE_12_L71= t.need(part is not None,'PART_NOT_IN_MANIFEST') -FILE_12_L72= n,h,identity=s.hash_file(path,part['size']) -FILE_12_L73= t.need(n==part['size'] and h==part['sha256'],'UPLOAD_SOURCE_HASH') -FILE_12_L74= rc,_=self.call(['lsjson',dest,'--stat'],65536) -FILE_12_L75= t.need(rc in (3,4),'OBJECT_NOT_PROVEN_ABSENT') -FILE_12_L76= with s.file_handle(path,part['size']) as (_,size,seen): -FILE_12_L77= t.need(seen==identity,'UPLOAD_SOURCE_CHANGED') -FILE_12_L78= rc,_=self.call(['copyto',str(path),dest,'--immutable','--no-update-modtime'],65536) -FILE_12_L79= t.need(rc==0,'CLOUD_COPY_FAILED') -FILE_12_L80= def read_stream(self,remote,name,size,digest,consume): -FILE_12_L81= address=self.address(remote,name) -FILE_12_L82= part=next((p for p in self.manifest['parts'] if p['name']==name),None) -FILE_12_L83= t.need(part is not None and part['size']==size and part['sha256']==digest,'READBACK_EXPECTATION') -FILE_12_L84= self.check();seconds=min(3600,self.deadline-time.monotonic());t.need(seconds>0,'CLOUD_DEADLINE') -FILE_12_L85= self.calls+=1 -FILE_12_L86= out=process_stream(flags(self.executable,self.config_path,self.work,['cat',address,'--count',str(size+1)]), -FILE_12_L90= -FILE_12_L91=class ReadOnlyLarge(r.ReadOnlyCloud): -FILE_12_L92= """Metadata uses inherited bounded reads; this class has no upload API.""" -FILE_12_L93= def read_stream(self,remote,generation,name,size,digest,consume): -FILE_12_L94= t.need(type(size) is int and 00,'RESTORE_DEADLINE') -FILE_12_L98= self.calls+=1 -FILE_12_L99= out=process_stream(flags(self.executable,self.snapshot,self.work,['cat',address,'--count',str(size+1)]), -FILE_12_L100= self.env,size,digest,consume,seconds) -FILE_12_L116= -FILE_12_L117= -FILE_12_L118=def receipt(rep,m,whole,count): -FILE_12_L119= t.need(count==m['payload_bytes'] and whole.hexdigest()==m['payload_sha256'],'PAYLOAD_READBACK_MISMATCH') -FILE_12_L120= return {**rep,'generation':m['generation'],'manifest_sha256':core.manifest_hash(m), -FILE_12_L121= 'payload_sha256':whole.hexdigest(),'payload_bytes':count,'parts':m['parts'], -FILE_12_L122= 'method':'download-sha256','ok':True} -FILE_12_L123= -FILE_12_L124= -FILE_12_L125=def upload(source,work,entries,cloud,generation,logical_id,reserve,guard,clock=lambda:int(time.time())): -FILE_12_L126= """Upload and seal; retain all source bytes here. Cleanup is a later gated operation.""" -FILE_12_L127= work=Path(work);source=Path(source);guard();core.validate_inventory(entries) -FILE_12_L128= t.need(core.valid_name(generation) and core.valid_name(logical_id),'GENERATION_ID') -FILE_12_L129= partdir=work/'parts';t.make_dir(partdir,exclusive=True) -FILE_12_L130= record=s.split_file(source,partdir) -FILE_12_L131= t.create(work/'parts-record.json',core.canonical_bytes(record)) -FILE_12_L132= quotas={e['account_id']:cloud.quota(e['crypt_remote']) for e in entries} -FILE_12_L133= allocation=core.allocate(entries,quotas,record['payload_bytes'],reserve,clock()) -FILE_12_L134= m=core.make_manifest(generation,logical_id,record['payload_bytes'],record['payload_sha256'],record['parts'],allocation) -FILE_12_L135= mb=core.canonical_bytes(m);mh=core.manifest_hash(m);cloud.bind(m) -FILE_12_L136= t.create(work/'allocation.json',core.canonical_bytes(allocation));t.create(work/'manifest.json',mb) -FILE_12_L137= for rep in m['replicas']: -FILE_12_L138= guard();cloud.absent(rep['crypt_remote']) -FILE_12_L139= receipts=[] -FILE_12_L140= for rep in m['replicas']: -FILE_12_L141= guard();remote=rep['crypt_remote'];q=cloud.quota(remote) -FILE_12_L142= t.need(core.quota_reason(q,clock()) is None and q['free']>=allocation['required_free_bytes'],'SELECTED_ACCOUNT_CAPACITY_CHANGED') -FILE_12_L143= s.verify_parts(partdir,record) -FILE_12_L144= for part in m['parts']:cloud.put(remote,part['name'],partdir/part['name']) -FILE_12_L145= cloud.put(remote,'manifest.json',work/'manifest.json') -FILE_12_L146= t.need(cloud.get(remote,'manifest.json',len(mb)+1)==mb,'MANIFEST_READBACK_MISMATCH') -FILE_12_L147= whole=hashlib.sha256();count=0 -FILE_12_L148= for part in m['parts']: -FILE_12_L149= observed=cloud.read_stream(remote,part['name'],part['size'],part['sha256'],whole.update) -FILE_12_L150= t.need(observed=={'size':part['size'],'sha256':part['sha256']},'PART_READBACK_MISMATCH');count+=observed['size'] -FILE_12_L151= receipts.append(receipt(rep,m,whole,count));t.create(work/('receipt-%d.json'%len(receipts)),core.canonical_bytes(receipts[-1])) -FILE_12_L152= core.verify_receipts(m,receipts) -FILE_12_L153= seal={'schema_version':2,'state':'SEALED','generation':generation,'logical_id':logical_id, -FILE_12_L154= 'manifest_sha256':mh,'replicas':m['replicas'],'receipts':receipts, -FILE_12_L155= 'acceptance':'IDENTICAL_SEAL_REQUIRED_ON_BOTH_NAMED_ACCOUNTS'} -FILE_12_L156= sb=core.canonical_bytes(seal);t.need(len(sb)<=core.SEAL_MAX_BYTES,'SEAL_BOUND') -FILE_12_L157= r.validate_seal(sb,entries,generation,logical_id);t.create(work/'SEALED.json',sb) -FILE_12_L158= for rep in m['replicas']: -FILE_12_L159= guard();cloud.put(rep['crypt_remote'],'SEALED.json',work/'SEALED.json') -FILE_12_L160= for rep in m['replicas']: -FILE_12_L161= t.need(cloud.get(rep['crypt_remote'],'SEALED.json',len(sb)+1)==sb,'SEALED_QUORUM_REQUIRED') -FILE_12_L162= guard();s.verify_parts(partdir,record) -FILE_12_L163= t.create(work/'QUORUM_VERIFIED.json',core.canonical_bytes({'manifest_sha256':mh,'seal_sha256':t.sha(sb)})) -FILE_12_L164= return {'sealed':True,'manifest':m,'seal_bytes':sb,'parts_record':record,'source':str(source),'work':str(work)} -FILE_12_L165= -FILE_12_L166= -FILE_12_L167=def discover(cloud,entries,logical_id,expected_hash): -FILE_12_L168= """Find placement from all cloud catalogs, using only an external manifest digest.""" -FILE_12_L169= core.validate_inventory(entries) -FILE_12_L170= t.need(core.valid_name(logical_id) and core.valid_hash(expected_hash),'DISCOVERY_INPUT') -FILE_12_L171= listed={};generations=set() -FILE_12_L172= for e in entries: -FILE_12_L173= gs=cloud.list_generations(e['crypt_remote']) -FILE_12_L174= t.need(type(gs) is list and len(gs)<=r.MAX_GENERATIONS and all(core.valid_name(g) for g in gs),'GENERATION_LIST_INVALID') -FILE_12_L175= t.need(len(gs)==len(set(gs)),'GENERATION_LIST_DUPLICATE');listed[e['crypt_remote']]=set(gs);generations.update(gs) -FILE_12_L176= t.need(len(generations)<=r.MAX_GENERATIONS,'TOTAL_GENERATIONS_BOUND') -FILE_12_L177= candidates=[] -FILE_12_L178= for gen in sorted(generations): -FILE_12_L179= seen={} -FILE_12_L180= for e in entries: -FILE_12_L181= remote=e['crypt_remote'] -FILE_12_L182= if gen not in listed[remote]:continue -FILE_12_L183= b=cloud.get(remote,gen,'SEALED.json',core.SEAL_MAX_BYTES+1,missing_ok=True) -FILE_12_L184= if b is None:continue -FILE_12_L185= seal=r.validate_seal(b,entries,gen,logical_id) -FILE_12_L186= t.need(remote in {x['crypt_remote'] for x in seal['replicas']},'UNNAMED_SEAL_LOCATION');seen[remote]=(b,seal) -FILE_12_L187= if not seen:continue -FILE_12_L188= sb,seal=next(iter(seen.values())) -FILE_12_L189= if seal['manifest_sha256']!=expected_hash:continue -FILE_12_L190= named={x['crypt_remote'] for x in seal['replicas']} -FILE_12_L191= t.need(set(seen)==named and all(b==sb for b,_ in seen.values()),'IDENTICAL_TWO_SEALS_REQUIRED') -FILE_12_L192= blobs=[] -FILE_12_L193= for rep in seal['replicas']: -FILE_12_L194= b=cloud.get(rep['crypt_remote'],gen,'manifest.json',core.MANIFEST_MAX_BYTES+1) -FILE_12_L195= t.need(type(b) is bytes and t.sha(b)==expected_hash,'MANIFEST_DIGEST_MISMATCH') -FILE_12_L196= m=r.strict_json(b,core.MANIFEST_MAX_BYTES);core.validate_manifest(m) -FILE_12_L197= t.need(m['generation']==gen and m['logical_id']==logical_id and m['replicas']==seal['replicas'],'MANIFEST_PLACEMENT') -FILE_12_L198= t.need(core.canonical_bytes(m)==b,'MANIFEST_CANONICAL_BYTES');core.verify_receipts(m,seal['receipts']);blobs.append(b) -FILE_12_L199= t.need(len(blobs)==2 and blobs[0]==blobs[1],'TWO_MANIFEST_COPIES_REQUIRED') -FILE_12_L200= candidates.append({'manifest':m,'seal_bytes':sb,'accounts_scanned':len(listed)}) -FILE_12_L201= t.need(len(candidates)==1,'EXACTLY_ONE_ACCEPTED_GENERATION_REQUIRED') -FILE_12_L202= return candidates[0] -FILE_12_L203= -FILE_12_L204= -FILE_12_L205=def fresh_seals(cloud,discovery): -FILE_12_L206= m=discovery['manifest'];core.validate_manifest(m);sb=discovery['seal_bytes'] -FILE_12_L207= for rep in m['replicas']: -FILE_12_L208= t.need(cloud.get(rep['crypt_remote'],m['generation'],'SEALED.json',len(sb)+1)==sb,'SEAL_CHANGED_DURING_RESTORE') -FILE_12_L209= -FILE_12_L210= -FILE_12_L211=def download_part(cloud,rep,m,part,path,whole): -FILE_12_L212= path=Path(path);parent=t.open_dir(path.parent);fd=None -FILE_12_L213= try: -FILE_12_L214= fd=os.open(path.name,os.O_WRONLY|os.O_CREAT|os.O_EXCL|os.O_NOFOLLOW,0o600,dir_fd=parent) -FILE_12_L215= def consume(block):whole.update(block);s.write_all(fd,block) -FILE_12_L216= result=cloud.read_stream(rep['crypt_remote'],m['generation'],part['name'],part['size'],part['sha256'],consume) -FILE_13_PATH=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/nodeplan263.py -FILE_13_BYTES=4603 -FILE_13_SHA256=db5c16ee24d25aa1694530332d3190ab7cc5d4f8eb69067d025e27ad5c995060 -FILE_13_CONTEXT_LINE_COUNT=23 -FILE_13_L1="""Pure, allowlisted PVE metadata projection. Does not execute any command.""" -FILE_13_L2=from decimal import Decimal,InvalidOperation -FILE_13_L3=import hashlib -FILE_13_L4=import re -FILE_13_L5=import v2core as core -FILE_13_L6= -FILE_13_L7=class PlanError(ValueError):pass -FILE_13_L8= -FILE_13_L15= if type(value) is not str or not re.fullmatch(r'\d+(?:\.\d+)?[KMGTPE]?',value):return None -FILE_13_L16= try: -FILE_13_L17= suffix=value[-1] if value[-1] in 'KMGTPE' else '' -FILE_13_L18= base=Decimal(value[:-1] if suffix else value) -FILE_13_L19= b=base*(1024**('KMGTPE'.index(suffix)+1) if suffix else 1) -FILE_13_L20= return int(b) if b==int(b) and 0time.monotonic(),'PRODUCER_TIMEOUT') -FILE_14_L175= try:rc=p.wait(timeout=deadline-time.monotonic()) -FILE_14_L176= except subprocess.TimeoutExpired:raise PVEError('PRODUCER_TIMEOUT') from None -FILE_14_L177= need(rc==0,'PRODUCER_RC_FAILED_PARTIAL_RETAINED');need(total>0,'EMPTY_ARCHIVE');check() -FILE_14_L178= st=os.fstat(fd);named=os.stat(dest.name,dir_fd=parent,follow_symlinks=False) -FILE_14_L179= need(st.st_dev==initial.st_dev and st.st_ino==initial.st_ino and t.ident(st)==t.ident(named) and -FILE_14_L180= st.st_nlink==1 and st.st_size==total,'PRODUCER_FILE_IDENTITY_CHANGED') -FILE_14_L200= """Future explicit execution API. Caller must recheck full plan/coverage in guard. -FILE_14_L201= -FILE_14_L202= Compression CRC and SHA are verified; guest bootability remains unproved. -FILE_14_L203= This function has no cloud/seal/delete operations and returns a readonly artifact. -FILE_14_L204= """ -FILE_14_L205= need(callable(guard),'GUARD_REQUIRED');guard();validate_job(job,int(time.time()));validate_tools(tools) -FILE_14_L206= work=Path(work);parent=stream.private_dir(work) -FILE_14_L209= tmp=work/'tmp';t.make_dir(tmp,exclusive=True) -FILE_14_L210= dest=work/('guest-%d.%s.zst'%(job['vmid'],'tar' if job['type']=='lxc' else 'vma')) -FILE_14_L211= def free_guard(): -FILE_14_L212= v=os.statvfs(work);need(v.f_frsize*v.f_bavail>=LOCAL_RESERVE,'STAGING_RESERVE_REACHED_PARTIAL_RETAINED') -FILE_14_L213= free_guard() -FILE_14_L214= result=capture_archive(dump_argv(job,tools['vzdump']['path'],tmp,int(time.time())),dest,job['archive_cap_bytes'],seconds,free_guard) -FILE_14_L215= guard();validate_tools(tools) -FILE_14_L219= need(n==result['size'] and h==result['sha256'] and list(identity)==result['identity'],'PRODUCER_READBACK_MISMATCH') -FILE_14_L220= guard();validate_tools(tools) -FILE_14_L221= return {**result,'path':str(dest),'vmid':job['vmid'],'type':job['type'],'node':job['node'], -FILE_14_L222= 'config_sha256':job['config_sha256'],'compression_test_ok':True,'restored':False, -FILE_14_L223= 'state':'PRODUCED_COMPRESSION_CHECKED_NOT_CLOUD_SEALED','local_payload_retained':True} -FILE_15_PATH=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/pve270.py -FILE_15_BYTES=19595 -FILE_15_SHA256=97015277a4f3f8129c0fd11eb70c06293026366f1f8232a97feeed513a2dbcef -FILE_15_CONTEXT_LINE_COUNT=140 -FILE_15_L3=Does not modify installed pve265, old workspaces, staging permissions or guest config. -FILE_15_L4=Only explicit produce() executes vzdump. Private bounded stderr/exit metadata retained. -FILE_15_L5=""" -FILE_15_L6=import hashlib,json,os,re,secrets,selectors,signal,stat,subprocess,sys,time -FILE_15_L7=from pathlib import Path -FILE_15_L8=import v2core as core -FILE_15_L9=import transport261 as t -FILE_15_L10=import stream263 as stream -FILE_15_L11=import pve265 as old -FILE_15_L12=need=old.need -FILE_15_L13=MAX_STDERR=2*1024**2 -FILE_15_L14=EXPECTED_CFG='09957aacd41ba9eeaf5fd4eda13936d580c9082a46895433379aa9dd0bad5969' -FILE_15_L39=def live_gate(job,pvesh_path,pvesh_sha): -FILE_15_L40= need(job.get('vmid')==110 and job.get('type')=='lxc' and job.get('node')=='pve01' and job.get('config_sha256')==EXPECTED_CFG,'ONLY_ACCEPTED_CT110') -FILE_15_L41= vals=[] -FILE_15_L42= for part in ('config','status/current','snapshot'): -FILE_15_L43= need(t.sha(t.read_secure(Path(pvesh_path),16*1024**2)[0])==pvesh_sha,'PVESH_CHANGED') -FILE_15_L44= rc,b=t.bounded_process([str(pvesh_path),'get','/nodes/pve01/lxc/110/'+part,'--output-format','json'],dict(old.ENV),262144,30) -FILE_15_L45= need(rc==0,'CT_GATE_READ_FAILED') -FILE_15_L46= try:vals.append(json.loads(b)) -FILE_15_L47= except (ValueError,UnicodeError):raise old.PVEError('CT_GATE_JSON') from None -FILE_15_L48= validate_ct(*vals,job['config_sha256']) -FILE_15_L49= return True -FILE_15_L50= -FILE_15_L59= def __init__(self,path,alias): -FILE_15_L60= self.path=Path(path);self.alias=Path(alias);self.parent=None;self.fd=None -FILE_15_L61= need(self.path.name=='perl' and self.alias.name=='perl5.40.1' and self.path.parent==self.alias.parent,'PERL_PATH_POLICY') -FILE_15_L62= need(self.path.is_absolute() and '..' not in self.path.parts,'PERL_PATH_POLICY') -FILE_15_L63= try: -FILE_15_L64= self.parent=os.open('/',os.O_RDONLY|os.O_DIRECTORY) -FILE_15_L65= for part in self.path.parts[1:-1]: -FILE_15_L66= nxt=os.open(part,os.O_RDONLY|os.O_DIRECTORY|os.O_NOFOLLOW,dir_fd=self.parent) -FILE_15_L67= os.close(self.parent);self.parent=nxt -FILE_15_L68= s=os.fstat(nxt) -FILE_15_L69= need(s.st_uid==0 and not s.st_mode&0o022,'PERL_PARENT_UNTRUSTED') -FILE_15_L75= need(os.pread(self.fd,4,0)==b'\x7fELF','PERL_ELF_REQUIRED') -FILE_15_L76= self.identity=t.ident(s)+(s.st_nlink,) -FILE_15_L77= self.sha256=self._digest();self.check() -FILE_15_L78= except BaseException: -FILE_15_L79= self.close();raise -FILE_15_L80= def _digest(self): -FILE_15_L81= s=os.fstat(self.fd);need(00,'PERL_TIMEOUT') -FILE_15_L123= rc=p.wait(timeout=left);self.check();return rc,bytes(out) -FILE_15_L124= except subprocess.TimeoutExpired:raise old.PVEError('PERL_TIMEOUT') from None -FILE_15_L125= finally: -FILE_15_L126= if p is not None: -FILE_15_L127= if p.poll() is None: -FILE_15_L150= for p,h in pins.items():need(t.sha(t.read_secure(Path(p),2*1024**2)[0])==h,'PVE_MAPPING_SOURCE_CHANGED') -FILE_15_L151= pin.check() -FILE_15_L152= try:d=json.loads(b) -FILE_15_L153= except (ValueError,UnicodeError):raise old.PVEError('PVE_IDMAP_JSON') from None -FILE_15_L154= return validate_mapping(d) -FILE_15_L155= -FILE_15_L156=def _parent(path): -FILE_15_L157= path=Path(path) -FILE_15_L158= need(path.is_absolute() and '..' not in path.parts,'TEMP_PARENT_PATH') -FILE_15_L159= fd=os.open('/',os.O_RDONLY|os.O_DIRECTORY) -FILE_15_L160= try: -FILE_15_L161= for i,part in enumerate(path.parts[1:]): -FILE_15_L162= nxt=os.open(part,os.O_RDONLY|os.O_DIRECTORY|os.O_NOFOLLOW,dir_fd=fd);os.close(fd);fd=nxt -FILE_15_L163= s=os.fstat(fd);last=i==len(path.parts)-2 -FILE_15_L164= need(s.st_uid==0,'TEMP_PARENT_OWNER') -FILE_15_L165= need(not s.st_mode&0o022 or last and s.st_mode&stat.S_ISVTX,'TEMP_PARENT_WRITABLE') -FILE_15_L166= result=fd;fd=None;return result -FILE_15_L170= -FILE_15_L171=class MappedScratch: -FILE_15_L172= """Root-owned, mapped-GID traverse-only, no access for others. No recursive cleanup.""" -FILE_15_L173= def __init__(self,gid,base='/var/tmp'): -FILE_15_L174= need(type(gid) is int and gid==100000,'TEMP_GID_POLICY') -FILE_15_L175= self.parent=_parent(base);self.fd=None;self.gid=gid -FILE_15_L176= self.name='homelab-vzdump-268-'+secrets.token_hex(16);self.path=Path(base)/self.name -FILE_15_L177= try: -FILE_15_L178= os.mkdir(self.name,0o700,dir_fd=self.parent) -FILE_15_L179= self.fd=os.open(self.name,os.O_RDONLY|os.O_DIRECTORY|os.O_NOFOLLOW,dir_fd=self.parent) -FILE_15_L181= os.fchown(self.fd,0,gid);os.fchmod(self.fd,0o710) -FILE_15_L182= s=os.fstat(self.fd);self.identity=(s.st_dev,s.st_ino) -FILE_15_L183= os.fsync(self.parent);self.check() -FILE_15_L184= except BaseException: -FILE_15_L185= if self.fd is not None:os.close(self.fd);self.fd=None -FILE_15_L186= os.close(self.parent);self.parent=None -FILE_15_L187= raise -FILE_15_L199= name='access-probe';fd=os.open(name,os.O_WRONLY|os.O_CREAT|os.O_EXCL|os.O_NOFOLLOW,0o644,dir_fd=self.fd) -FILE_15_L200= try:os.fchmod(fd,0o644);stream.write_all(fd,b'HOMELAB_ACCESS_PROBE\n');os.fsync(fd);identity=t.ident(os.fstat(fd)) -FILE_15_L201= finally:os.close(fd) -FILE_15_L202= positive='''import os,sys -FILE_15_L203=p,private=sys.argv[1:] -FILE_15_L204=assert open(p+'/access-probe','rb').read()==b'HOMELAB_ACCESS_PROBE\\n' -FILE_15_L205=for action in (lambda:os.listdir(p),lambda:os.open(p+'/forbidden',os.O_WRONLY|os.O_CREAT,0o600),lambda:os.open(private,os.O_RDONLY|os.O_DIRECTORY)): -FILE_15_L207= except PermissionError:continue -FILE_15_L208= raise SystemExit(31) -FILE_15_L209=''' -FILE_15_L210= negative='''import sys -FILE_15_L211=try:open(sys.argv[1]+'/access-probe','rb').read() -FILE_15_L212=except PermissionError:raise SystemExit(0) -FILE_15_L213=raise SystemExit(32) -FILE_15_L256= b=os.read(key.fileobj.fileno(),65536) -FILE_15_L257= if not b:sel.unregister(key.fileobj);continue -FILE_15_L258= if key.fileobj is p.stdout: -FILE_15_L259= need(total+len(b)<=cap,'ARCHIVE_CAP_REACHED_PARTIAL_RETAINED') -FILE_15_L260= stream.write_all(fd,b);digest.update(b);total+=len(b) -FILE_15_L261= else: -FILE_15_L262= err_total+=len(b);eh.update(b);chunk=b[:max(0,MAX_STDERR-err_kept)] -FILE_15_L263= if chunk:stream.write_all(efd,chunk);err_kept+=len(chunk) -FILE_15_L264= need(deadline>time.monotonic(),'PRODUCER_TIMEOUT') -FILE_15_L265= try:rc=p.wait(timeout=deadline-time.monotonic()) -FILE_15_L266= except subprocess.TimeoutExpired:raise old.PVEError('PRODUCER_TIMEOUT') from None -FILE_15_L267= need(rc==0,'PRODUCER_RC_FAILED_PARTIAL_RETAINED');need(total>0,'EMPTY_ARCHIVE');check() -FILE_15_L268= st=os.fstat(fd);named=os.stat(dest.name,dir_fd=parent,follow_symlinks=False) -FILE_15_L269= need(st.st_dev==initial.st_dev and st.st_ino==initial.st_ino and t.ident(st)==t.ident(named) and st.st_nlink==1 and st.st_size==total,'PRODUCER_FILE_IDENTITY_CHANGED') -FILE_15_L270= os.fchmod(fd,0o400);os.fsync(fd);os.fsync(parent);good=True -FILE_15_L306= scratch.probe(uid,work);emit('MAPPED_UID_ACCESS_AND_ISOLATION_PROBE','PASS') -FILE_15_L307= def free_guard(): -FILE_15_L308= scratch.check() -FILE_15_L309= for path,reserve,reason in ((work,old.LOCAL_RESERVE,'STAGING_RESERVE_REACHED_PARTIAL_RETAINED'),(scratch.path,256*1024**2,'PVE_TEMP_RESERVE_LOW')): -FILE_15_L310= v=os.statvfs(path);need(v.f_frsize*v.f_bavail>=reserve,reason) -FILE_15_L311= free_guard();guard();dest=work/'guest-110.tar.zst' -FILE_15_L312= result=capture_archive(old.dump_argv(job,tools['vzdump']['path'],scratch.path,int(time.time())),dest,job['archive_cap_bytes'],seconds,free_guard) -FILE_15_L317= need(n==result['size'] and h==result['sha256'] and list(identity)==result['identity'],'PRODUCER_READBACK_MISMATCH') -FILE_15_L318= guard();old.validate_tools(tools);scratch.close(empty_only=True);complete=True;emit('PVE_TEMP_EMPTY_REMOVED','YES') -FILE_15_L319= return {**result,'path':str(dest),'vmid':110,'type':'lxc','node':job['node'],'config_sha256':job['config_sha256'], -FILE_15_L320= 'compression_test_ok':True,'restored':False,'state':'PRODUCED_COMPRESSION_CHECKED_NOT_CLOUD_SEALED','local_payload_retained':True} -FILE_15_L321= finally: -FILE_15_L322= m=work/'PRODUCER_PROCESS.json' -FILE_15_L323= if m.exists(): -FILE_16_PATH=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/pve282.py -FILE_16_BYTES=6195 -FILE_16_SHA256=73c6cb7773eb8e82d1e7ed5ab4733b4afb88f74ae69c09125d0378d6e480e012 -FILE_16_CONTEXT_LINE_COUNT=58 -FILE_16_L1="""282R1 bounded CT112 adapter; proven 270 capture/temp/Perl implementation unchanged. -FILE_16_L2=Expected source hash must be supplied from the immutable-hash-bound 265 plan. -FILE_16_L3=No changes to source guest configuration; no source110 backup or guest deletion. -FILE_16_L4=""" -FILE_16_L5=import copy,json,os,time -FILE_16_L6=from pathlib import Path -FILE_16_L7=import v2core as core -FILE_16_L8=import transport261 as t -FILE_16_L9=import stream263 as stream -FILE_16_L10=import pve265 as old -FILE_16_L11=import pve270 as proven -FILE_16_L12=import guest266 as g -FILE_16_L13=need=old.need -FILE_16_L14=PerlPin=proven.PerlPin -FILE_16_L15=host_mapping=proven.host_mapping -FILE_16_L26= need(core.valid_hash(expected_config) and job.get('config_sha256')==expected_config,'CT112_PLAN_CONFIG_ANCHOR') -FILE_16_L27= return True -FILE_16_L28= -FILE_16_L29=def select112(current,baseline,now): -FILE_16_L30= rows=g.plan_rows(current);previous=g.plan_rows(baseline) -FILE_16_L31= by_id={j['vmid']:j for j in previous} -FILE_16_L32= need(TARGET_VMID in by_id,'CT112_MISSING_FROM_ACCEPTED265') -FILE_16_L33= g.same_state(baseline,current,TARGET_VMID) -FILE_16_L34= need({j['vmid'] for j in rows}==set(by_id),'ACCEPTED265_MEMBERSHIP_CHANGED') -FILE_16_L35= for row in rows: -FILE_16_L36= need(row['config_sha256']==by_id[row['vmid']]['config_sha256'],'ACCEPTED265_GUEST_CONFIG_CHANGED') -FILE_16_L37= selected=[j for j in rows if j['vmid']==TARGET_VMID] -FILE_16_L38= need(len(selected)==1,'CT112_NOT_UNIQUE') -FILE_16_L39= job=copy.deepcopy(selected[0]);old.validate_job(job,now) -FILE_16_L40= need(current.get('global_blockers')==[],'GLOBAL_DEFAULT_BLOCK') -FILE_16_L41= need(job.get('compression_dependent') is False and job.get('configured_full_cycle_fits') is True,'CT112_FULL_CYCLE_SPACE_UNPROVEN') -FILE_16_L42= disks=job.get('disks');need(type(disks) is list and len(disks)==1,'CT112_SINGLE_ROOTFS_REQUIRED') -FILE_16_L53=def live_gate(job,pvesh_path,pvesh_sha,expected_config): -FILE_16_L54= require_target(job,expected_config) -FILE_16_L55= vals=[] -FILE_16_L56= for part in ('config','status/current','snapshot'): -FILE_16_L57= need(t.sha(t.read_secure(Path(pvesh_path),16*1024**2)[0])==pvesh_sha,'PVESH_CHANGED') -FILE_16_L58= rc,b=t.bounded_process([str(pvesh_path),'get','/nodes/pve01/lxc/112/'+part,'--output-format','json'],dict(old.ENV),262144,30) -FILE_16_L59= need(rc==0,'CT112_GATE_READ_FAILED') -FILE_16_L60= try:vals.append(json.loads(b)) -FILE_16_L61= except (ValueError,UnicodeError):raise old.PVEError('CT112_GATE_JSON') from None -FILE_16_L62= validate_ct(*vals,expected_config) -FILE_16_L63= return True -FILE_16_L64= -FILE_16_L79= scratch.probe(uid,work);emit('MAPPED_UID_ACCESS_AND_ISOLATION_PROBE','PASS') -FILE_16_L80= def free_guard(): -FILE_16_L81= scratch.check() -FILE_16_L82= for path,reserve,reason in ((work,old.LOCAL_RESERVE,'STAGING_RESERVE_REACHED_PARTIAL_RETAINED'),(scratch.path,256*1024**2,'PVE_TEMP_RESERVE_LOW')): -FILE_16_L83= v=os.statvfs(path);need(v.f_frsize*v.f_bavail>=reserve,reason) -FILE_16_L84= free_guard();guard();dest=work/'guest-112.tar.zst' -FILE_16_L85= result=capture_archive(old.dump_argv(job,tools['vzdump']['path'],scratch.path,int(time.time())),dest,job['archive_cap_bytes'],seconds,free_guard) -FILE_16_L90= need(n==result['size'] and h==result['sha256'] and list(identity)==result['identity'],'PRODUCER_READBACK_MISMATCH') -FILE_16_L91= guard();old.validate_tools(tools);scratch.close(empty_only=True);complete=True;emit('PVE_TEMP_EMPTY_REMOVED','YES') -FILE_16_L92= return {**result,'path':str(dest),'vmid':112,'type':'lxc','node':job['node'],'config_sha256':job['config_sha256'], -FILE_16_L93= 'compression_test_ok':True,'restored':False,'state':'PRODUCED_COMPRESSION_CHECKED_NOT_CLOUD_SEALED','local_payload_retained':True} -FILE_16_L94= finally: -FILE_16_L95= m=work/'PRODUCER_PROCESS.json' -FILE_16_L96= if m.exists(): -FILE_17_PATH=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/restore262.py -FILE_17_BYTES=14701 -FILE_17_SHA256=56d1b961c8d5654d5e2ee2755268db46bd2202bca11e69788febf991cfaa79f1 -FILE_17_CONTEXT_LINE_COUNT=180 -FILE_17_L1="""262R1 bounded cloud-discovered recovery. No live action on import. -FILE_17_L2= -FILE_17_L3=Cloud access is strictly lsjson/cat. No source-local workspace/manifest/receipt -FILE_17_L4=is read. The external accepted manifest digest is a trust anchor, not placement. -FILE_17_L5=""" -FILE_17_L6=import hashlib -FILE_17_L7=import io -FILE_17_L8=import json -FILE_17_L9=import os -FILE_17_L10=from pathlib import Path -FILE_17_L11=import re -FILE_17_L12=import stat -FILE_17_L13=import tarfile -FILE_17_L14=import time -FILE_17_L15=import v2core as core -FILE_17_L16=import transport261 as t -FILE_17_L17= -FILE_17_L18=SOURCE_NAMES=frozenset(('etc/pve/HOMELAB_ASSISTANT_ERROR_REGISTER.md','etc/pve/31_HOMELAB_REFERENCE.md', -FILE_17_L19= 'usr/local/sbin/homelab-control-backup','etc/systemd/system/homelab-control-backup.service')) -FILE_17_L20=ALL_NAMES=SOURCE_NAMES|{'SOURCE_INDEX.json'} -FILE_17_L21=MAX_GENERATIONS=64 -FILE_17_L22=SEAL_KEYS={'schema_version','state','generation','logical_id','manifest_sha256','replicas','receipts','acceptance'} -FILE_17_L23= -FILE_17_L24=def strict_json(data,limit): -FILE_17_L25= t.need(type(data) is bytes and len(data)<=limit,'JSON_BOUND') -FILE_17_L29= t.need(k not in result,'JSON_DUPLICATE_KEY');result[k]=v -FILE_17_L30= return result -FILE_17_L31= def bad_constant(value):raise t.Stop('JSON_NONFINITE_NUMBER') -DEPENDENCY_PATH_COUNT=28 -DEPENDENCY_PATH_1=/opt/homelab-backup-v2/releases/core-260r1 -DEPENDENCY_PATH_2=/opt/homelab-backup-v2/releases/core-260r1/v2core.py -DEPENDENCY_PATH_3=/opt/homelab-backup-v2/releases/ct-backup-282r1 -DEPENDENCY_PATH_4=/opt/homelab-backup-v2/releases/ct-backup-282r1/guest266.py -DEPENDENCY_PATH_5=/opt/homelab-backup-v2/releases/ct-backup-282r1/pve282.py -DEPENDENCY_PATH_6=/opt/homelab-backup-v2/releases/ct-backup-282r1/test_guest266.py -DEPENDENCY_PATH_7=/opt/homelab-backup-v2/releases/ct-backup-282r1/test_perl270.py -DEPENDENCY_PATH_8=/opt/homelab-backup-v2/releases/ct-backup-282r1/test_producer282.py -DEPENDENCY_PATH_9=/opt/homelab-backup-v2/releases/ct-backup-282r1/test_pve282.py -DEPENDENCY_PATH_10=/opt/homelab-backup-v2/releases/large-cloud-264r1 -DEPENDENCY_PATH_11=/opt/homelab-backup-v2/releases/large-cloud-264r1/large264.py -DEPENDENCY_PATH_12=/opt/homelab-backup-v2/releases/pve-producer-265r1 -DEPENDENCY_PATH_13=/opt/homelab-backup-v2/releases/pve-producer-265r1/PLAN.json -DEPENDENCY_PATH_14=/opt/homelab-backup-v2/releases/pve-producer-265r1/pve265.py -DEPENDENCY_PATH_15=/opt/homelab-backup-v2/releases/pve-producer-265r1/test_pve265.py -DEPENDENCY_PATH_16=/opt/homelab-backup-v2/releases/pve-retry-268r1 -DEPENDENCY_PATH_17=/opt/homelab-backup-v2/releases/pve-retry-270r1 -DEPENDENCY_PATH_18=/opt/homelab-backup-v2/releases/pve-retry-270r1/pve270.py -DEPENDENCY_PATH_19=/opt/homelab-backup-v2/releases/pve-retry-270r1/test_pve270.py -DEPENDENCY_PATH_20=/opt/homelab-backup-v2/releases/pve-run-266r1 -DEPENDENCY_PATH_21=/opt/homelab-backup-v2/releases/restore-262r1 -DEPENDENCY_PATH_22=/opt/homelab-backup-v2/releases/restore-262r1/restore262.py -DEPENDENCY_PATH_23=/opt/homelab-backup-v2/releases/stream-263r1 -DEPENDENCY_PATH_24=/opt/homelab-backup-v2/releases/stream-263r1/nodeplan263.py -DEPENDENCY_PATH_25=/opt/homelab-backup-v2/releases/stream-263r1/stream263.py -DEPENDENCY_PATH_26=/opt/homelab-backup-v2/releases/transport-261r1 -DEPENDENCY_PATH_27=/opt/homelab-backup-v2/releases/transport-261r1/transport261.py -DEPENDENCY_PATH_28=/opt/homelab-backup-v2/releases/vm-backup-283r1 -HAS_RCLONE_SOURCE=true -HAS_OBJECT_PATH_CONTRACT=true -DECISION=PASS_BACKUP213_EXACT_CLOUD_OBJECT_CONTRACT_CAPTURED -NEXT_GATE=BUILD_CORRECTED_CLOUD_GENERATION_RECOVERY -CLOUD_READS=0 -CLOUD_WRITES=0 -VPS_SSH_ATTEMPTS=0 -MESSENGERS214_CLOSED=false -MAJOR_STAGES_REMAINING=8 -NEWFI213_END=true +W2UX739_BEGIN=True +COMMAND_ID=SUPPORT-260920-PVEPRO-RUSSIAN-UX-STAGING-CLEANUP-739R1 +MODE=change +COMPONENT=pvepro-russian-ux-staging-cleanup +VM_SAFETY={"production_snapshots":[],"production_status":"running","staging_all_link_down":true,"staging_name":"pvepro-staging-v1","staging_nic_count":1,"staging_snapshots":[],"staging_status":"stopped"} +PRODUCTION_PRESTATE={"admins":1,"boardActive":true,"defaultLanguageId":2,"enCount":1,"errors":19,"languageRebuildJobs":0,"manualJobs":0,"nodes":[{"node_id":1,"node_type_id":"Category","title":"Main category"},{"node_id":2,"node_type_id":"Forum","title":"Main forum"}],"posts":44,"registrationEnabled":false,"ruCount":1,"ruPhraseCount":11142,"ruPhraseMapCount":11194,"threads":44} +STAGING_SNAPSHOT_CREATED=True +STAGING_CLEANUP_ERROR_CLASS=RuntimeError +STAGING_CLEANUP_ERROR_SHA256=97bca49c0032b21299dd3981d766c52667d84153c1f07963ae6404d6ac2012f7 +ROLLBACK_STARTED=True +ROLLBACK_ERROR_SHA256=97bca49c0032b21299dd3981d766c52667d84153c1f07963ae6404d6ac2012f7 +ROLLBACK_RESTORED=False +TASK_RESULT=PVEPRO_RUSSIAN_UX_STAGING_CLEANUP_FAILED +HOMELAB_RESULT_CONTRACT={"changes_made":true,"command_id":"SUPPORT-260920-PVEPRO-RUSSIAN-UX-STAGING-CLEANUP-739R1","rollback_restored":false,"rollback_started":true,"status":"FAIL","version":1} OUTPUT_END CHAT_OUTPUT_END