640 lines
60 KiB
Plaintext
640 lines
60 KiB
Plaintext
CHAT_OUTPUT_BEGIN
|
|
COMMAND_ID=SUPPORT-260924-HOMELAB-MOLDOVA-BASE-RELEASE-AUTH-1147R1
|
|
STATUS=OK
|
|
RC=0
|
|
HOST=pve01
|
|
MODE=read-only
|
|
COMPONENT=homelab-moldova-base-release-auth
|
|
REFERENCE_REGISTER_CHECK=OK
|
|
REFERENCE_SHA256=5a3d8e5154c41cb582a4e0aca68090be1f0138918bf82131a948df326f9d8d66
|
|
ERROR_REGISTER_CHECK=OK
|
|
ERROR_REGISTER_SHA256=3b09a553ec0f527ed3afeed4753f52a74ee3036045bbfb3c685e9f8af4ba7ba0
|
|
COMMAND_SHA256=f3fe1cb2e034d22c5917b5946f86b1c8ea517c0abd0a92891868a481e2aa5da7
|
|
DUPLICATE_FAILED_COMMAND_BLOCKED=false
|
|
EXECUTION_STARTED=true
|
|
CHANGE_DECLARED=false
|
|
RESULT_CONTRACT_VALID=true
|
|
RESULT_CONTRACT_STATUS=NOT_APPLICABLE
|
|
RESULT_CONTRACT_ERROR=NONE
|
|
COMMAND_RC=0
|
|
CHANGES_MADE=false
|
|
ROLLBACK_STARTED=false
|
|
ROLLBACK_RESTORED=null
|
|
MUTATION_OUTCOME=NO_MUTATION
|
|
SANITIZED=yes
|
|
SECRETS_INCLUDED=no
|
|
PRIVATE_ADDRESSES_INCLUDED=no
|
|
RAW_EVIDENCE_SHA256=add93d5fb111ea9426df8d172911f8506ad95531ae42be7e0c338901a61b8608
|
|
SANITIZED_OUTPUT_SHA256=e60b4493bf44bffbb9ab84838090dbefdb74cfb16219ea4cc0d3a9e834e59ae4
|
|
OUTPUT_BEGIN
|
|
WORKERS2_MOLDOVA_BASE_RELEASE_AUTH_BEGIN=1
|
|
COMMAND_ID=SUPPORT-260924-HOMELAB-MOLDOVA-BASE-RELEASE-AUTH-1147R1
|
|
MODE=read-only
|
|
COMPONENT=homelab-moldova-base-release-auth
|
|
MUTATION_BOUNDARY=NONE;ENTRY504_PROVENANCE_AND_LITERAL_BASE_RELEASE_STATIC_INSPECTION_ONLY;NO_MOLDOVA_CONNECTION;NO_REMOTE_SSH;NO_PROVIDER_CALL;NO_SYSTEMD_ACTION;NO_BACKUP_RUN;NO_BACKUP_MUTATION;NO_TARGET_POLICY_MUTATION;NO_NETBIRD_MUTATION;NO_MONITORING_MUTATION;NO_GIT_WRITE;NO_SECRET_VALUE_READOUT
|
|
PREDECESSOR_1146=PASS_MOLDOVA_1146_RELEASE_AUTH_EXACT_COLLECTED
|
|
PREDECESSOR_1146_CLASS=VPS_MD_HANDLER_NOT_LOCATED
|
|
CARRIER_BASELINE=KNOWN_GOOD_1137_BYTE_SEMANTICS
|
|
REFERENCE_SHA256=5a3d8e5154c41cb582a4e0aca68090be1f0138918bf82131a948df326f9d8d66
|
|
ERROR_REGISTER_SHA256=3b09a553ec0f527ed3afeed4753f52a74ee3036045bbfb3c685e9f8af4ba7ba0
|
|
RUNNER_SHA256=b248a4c32c9cc64e5747e7dce6c7fc0a23f5124a77c71ce72e27a81aceae9d2d
|
|
TARGETS_SHA256=5cba5f1e14c65211c9fab6d11594c19279ab68369b4db3f7283708c368348288
|
|
ENTRY504_SHA256=ca6876fa9c3b1465b045344a8ad46962de16972c7dadc4ac6257c7d225047793
|
|
MD_RELAY_COUNT=1
|
|
MD_RELAY_ENABLED=false
|
|
ENTRY504_PROVENANCE_BLOCK_BEGIN=1
|
|
ENTRY504|line=20|text= while True:
|
|
ENTRY504|line=21|text= b=f.read(1<<20)
|
|
ENTRY504|line=22|text= if not b: break
|
|
ENTRY504|line=23|text= h.update(b)
|
|
ENTRY504|line=24|text= return h.hexdigest()
|
|
ENTRY504|line=25|text=
|
|
ENTRY504|line=26|text=def _verify_base():
|
|
ENTRY504|line=27|text= s=os.lstat(BASE)
|
|
ENTRY504|line=28|text= if not stat.S_ISDIR(s.st_mode) or stat.S_ISLNK(s.st_mode) or s.st_uid!=0 or s.st_gid!=0:
|
|
ENTRY504|line=29|text= raise RuntimeError('BASE_RELEASE_ROOT')
|
|
ENTRY504|line=30|text= for rel,want in BASE_EXPECTED.items():
|
|
ENTRY504|line=31|text= p=BASE/rel; st=os.lstat(p)
|
|
ENTRY504|line=32|text= if not stat.S_ISREG(st.st_mode) or stat.S_ISLNK(st.st_mode) or st.st_uid!=0 or st.st_gid!=0 or _sha(p)!=want:
|
|
ENTRY504|line=33|text= raise RuntimeError('BASE_RELEASE_FILE_'+rel)
|
|
ENTRY504|line=34|text=
|
|
ENTRY504|line=35|text=class _GlobalsProxy:
|
|
ENTRY504|line=36|text= __slots__=('_g',)
|
|
ENTRY504|line=37|text= def __init__(self,g): object.__setattr__(self,'_g',g)
|
|
ENTRY504|line=38|text= def __getattr__(self,n):
|
|
ENTRY504|line=39|text= try: return self._g[n]
|
|
ENTRY504|line=40|text= except KeyError as e: raise AttributeError(n) from e
|
|
ENTRY504|line=41|text= def __setattr__(self,n,v): self._g[n]=v
|
|
ENTRY504|line=42|text=
|
|
ENTRY504|line=43|text=def _wire(wrapper,lxc_adapter,qemu_adapter,scheduler_adapter,base=BASE):
|
|
ENTRY504|line=44|text= legacy_path=(pathlib.Path(base)/'legacy_engine.py').resolve()
|
|
ENTRY504|line=45|text= found={}
|
|
ENTRY504|line=46|text= for value in vars(wrapper).values():
|
|
ENTRY504|line=47|text= if isinstance(value,types.ModuleType):
|
|
ENTRY504|line=48|text= g=vars(value); raw=g.get('__file__')
|
|
ENTRY504|line=49|text= elif isinstance(value,types.FunctionType):
|
|
ENTRY504|line=50|text= g=value.__globals__; raw=g.get('__file__')
|
|
ENTRY504|line=51|text= else:
|
|
ENTRY504|line=52|text= continue
|
|
ENTRY504|line=53|text= if not isinstance(raw,str):
|
|
ENTRY504|line=54|text= continue
|
|
ENTRY504|line=55|text= try: gp=pathlib.Path(raw).resolve()
|
|
ENTRY504|line=56|text= except Exception: continue
|
|
ENTRY504|line=57|text= if gp==legacy_path:
|
|
ENTRY504|line=58|text= found[id(g)]=g
|
|
ENTRY504|line=59|text= if len(found)!=1:
|
|
ENTRY504|line=60|text= raise RuntimeError('LEGACY_PROVENANCE_CARDINALITY')
|
|
ENTRY504|line=61|text= g=next(iter(found.values()))
|
|
ENTRY504|line=62|text= required=('capture_pve_guest','backup_target','load_targets','cluster_inventory','ssh_remote',
|
|
ENTRY504|line=63|text= '_default_unprivileged_lxc','mapped_scratch_probe','_stream_argv_to_local','run',
|
|
ENTRY504|line=64|text= 'choose_due_target','write_state','epoch')
|
|
ENTRY504|line=65|text= if any(not callable(g.get(n)) for n in required):
|
|
ENTRY504|line=66|text= raise RuntimeError('LEGACY_PROVENANCE_API')
|
|
ENTRY504|line=67|text= original=g['capture_pve_guest']
|
|
ENTRY504|line=68|text= proxy=_GlobalsProxy(g)
|
|
ENTRY504|line=69|text= if not isinstance(g.get('SCP'),str) or not g['SCP'].startswith('/'):
|
|
ENTRY504|line=70|text= raise RuntimeError('LEGACY_SCP_API')
|
|
ENTRY504|line=71|text= qemu_patched=qemu_adapter.bind(proxy,original)
|
|
ENTRY504|line=72|text= if not callable(qemu_patched):
|
|
ENTRY504|line=73|text= raise RuntimeError('QEMU_ADAPTER_BIND_RESULT')
|
|
ENTRY504|line=74|text= patched=lxc_adapter.bind(proxy,qemu_patched)
|
|
ENTRY504|line=75|text= if not callable(patched):
|
|
ENTRY504|line=76|text= raise RuntimeError('LXC_ADAPTER_BIND_RESULT')
|
|
ENTRY504|line=77|text= g['capture_pve_guest']=patched
|
|
ENTRY504|line=78|text= scheduler_wired=scheduler_adapter.bind(proxy,g['choose_due_target'],g['write_state'])
|
|
ENTRY504|line=79|text= if g.get('choose_due_target') is not scheduler_wired['choose_due_target'] or g.get('write_state') is not scheduler_wired['write_state']:
|
|
ENTRY504|line=80|text= raise RuntimeError('SCHEDULER_ADAPTER_BIND_RESULT')
|
|
ENTRY504|line=81|text= return {'legacy_globals':g,'legacy':proxy,'original_capture':original,'qemu_patched_capture':qemu_patched,'patched_capture':patched,'scheduler_wired':scheduler_wired}
|
|
ENTRY504|line=82|text=
|
|
ENTRY504|line=83|text=def _load():
|
|
ENTRY504|line=84|text= _verify_base()
|
|
ENTRY504|line=85|text= sys.dont_write_bytecode=True
|
|
ENTRY504|line=86|text= sys.path.insert(0,str(BASE))
|
|
ENTRY504|line=87|text= wrapper=importlib.import_module('wrapper')
|
|
ENTRY504|line=88|text= # Baseline accepted wrapper selftest stays the compatibility gate; no full old legacy selftest is reintroduced.
|
|
ENTRY504|line=89|text= wrapper.selftest()
|
|
ENTRY504|line=90|text= sys.path.insert(0,str(HERE))
|
|
ENTRY504|line=91|text= lxc_adapter=importlib.import_module('lxc_adapter')
|
|
ENTRY504|line=92|text= qemu_adapter=importlib.import_module('qemu_adapter')
|
|
ENTRY504|line=93|text= scheduler_adapter=importlib.import_module('scheduler_adapter')
|
|
ENTRY504|line=94|text= if _sha(HERE/'lxc_adapter.py')!='3fa7d194010d76f7c8bc95035ae8876262e9337903e310f45f45df88c266dc8d':
|
|
ENTRY504|line=95|text= raise RuntimeError('LXC_502_BYTE_IDENTITY')
|
|
ENTRY504|line=96|text= wired=_wire(wrapper,lxc_adapter,qemu_adapter,scheduler_adapter,BASE)
|
|
ENTRY504|line=97|text= return wrapper,lxc_adapter,qemu_adapter,scheduler_adapter,wired
|
|
ENTRY504|line=98|text=
|
|
ENTRY504|line=99|text=def _target(wrapper,lid):
|
|
ENTRY504|line=100|text= rows=wrapper.load_targets(); hits=[x for x in rows if x.get('logical_id')==lid]
|
|
ENTRY504|line=101|text= if len(hits)!=1: raise RuntimeError('TARGET_CARDINALITY')
|
|
ENTRY504|line=102|text= return hits[0]
|
|
ENTRY504|line=103|text=
|
|
ENTRY504|line=104|text=def selftest():
|
|
ENTRY504|line=105|text= wrapper,lxc_adapter,qemu_adapter,scheduler_adapter,wired=_load()
|
|
ENTRY504|line=106|text= if wired['legacy_globals'].get('capture_pve_guest') is not wired['patched_capture']:
|
|
ENTRY504|line=107|text= raise RuntimeError('PATCH_BINDING')
|
|
ENTRY504|line=108|text= if wired['legacy_globals'].get('choose_due_target') is not wired['scheduler_wired']['choose_due_target']:
|
|
ENTRY504|line=109|text= raise RuntimeError('SCHEDULER_CHOOSE_BINDING')
|
|
ENTRY504|line=110|text= if wired['legacy_globals'].get('write_state') is not wired['scheduler_wired']['write_state']:
|
|
ENTRY504|line=111|text= raise RuntimeError('SCHEDULER_WRITE_BINDING')
|
|
ENTRY504|line=112|text= if [scheduler_adapter.backoff_seconds(n) for n in range(1,7)]!=[1800,3600,7200,14400,21600,21600]:
|
|
ENTRY504|line=113|text= raise RuntimeError('SCHEDULER_BACKOFF_CONTRACT')
|
|
ENTRY504|line=114|text= if not callable(wrapper.backup_target) or not callable(wrapper.main) or not callable(wrapper.load_targets):
|
|
ENTRY504|line=115|text= raise RuntimeError('WRAPPER_PUBLIC_API')
|
|
ENTRY504|line=116|text= print('SELFTEST_OVERLAY504=PASS',flush=True)
|
|
ENTRY504|line=117|text= return 0
|
|
ENTRY504|line=118|text=
|
|
ENTRY504|line=119|text=def main(argv=None):
|
|
ENTRY504|line=120|text= argv=list(sys.argv[1:] if argv is None else argv)
|
|
ENTRY504_PROVENANCE_BLOCK_END=1
|
|
ENTRY504_RELEASE_LITERAL_COUNT=1
|
|
ENTRY504_RELEASE_LITERAL=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1
|
|
BASE_CANDIDATE_COUNT=1
|
|
BASE_CANDIDATE|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1|kind=dir|mode=500|uid=0|gid=0
|
|
BASE_PY_FILE_COUNT=21
|
|
BASE_FILE|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/adapter.py|bytes=2989|sha256=18eb29cef165371b48bcb78e1a456e1939e6eff253787218133162cc89ab9fa3|mode=500|uid=0|gid=0
|
|
BASE_FILE|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/guest266.py|bytes=7385|sha256=86433bc424d5fa5017c20f7a237c82874b02357a3283660d8a6b0039ef2bc868|mode=400|uid=0|gid=0
|
|
BASE_FILE|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/large264.py|bytes=16514|sha256=5cc4641fc35e4dd554495a36146da7dbbb7f142d1450e04cc5d3c89678b8497a|mode=400|uid=0|gid=0
|
|
BASE_FILE|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/nodeplan263.py|bytes=4603|sha256=db5c16ee24d25aa1694530332d3190ab7cc5d4f8eb69067d025e27ad5c995060|mode=400|uid=0|gid=0
|
|
BASE_FILE|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/pve265.py|bytes=12888|sha256=cd2f634414a8e38a3789015aeca5a9b47821f77d7d1fa61dcdbdc866172963be|mode=400|uid=0|gid=0
|
|
BASE_FILE|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/pve270.py|bytes=19595|sha256=97015277a4f3f8129c0fd11eb70c06293026366f1f8232a97feeed513a2dbcef|mode=400|uid=0|gid=0
|
|
BASE_FILE|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/pve282.py|bytes=6195|sha256=73c6cb7773eb8e82d1e7ed5ab4733b4afb88f74ae69c09125d0378d6e480e012|mode=400|uid=0|gid=0
|
|
BASE_FILE|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/restore262.py|bytes=14701|sha256=56d1b961c8d5654d5e2ee2755268db46bd2202bca11e69788febf991cfaa79f1|mode=400|uid=0|gid=0
|
|
BASE_FILE|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/stream263.py|bytes=8831|sha256=a83d03a86b93a855716658652dd073fcb281cd387fc6200fe4bc2a922f39ea16|mode=400|uid=0|gid=0
|
|
BASE_FILE|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/test_guest266.py|bytes=10752|sha256=460b950924a475ce0a28dc759512e7dbe2323ae2cc42f7ef4c6181710a2a13da|mode=400|uid=0|gid=0
|
|
BASE_FILE|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/test_perl270.py|bytes=5441|sha256=50fc10865078a7b9b255dc627059824184441a47c4ede52e23f24c1186e3512f|mode=400|uid=0|gid=0
|
|
BASE_FILE|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/test_producer282.py|bytes=12215|sha256=a67d927cc428935791f9eecc3acf571d089f80761162f4589b7d684fba6b8af7|mode=400|uid=0|gid=0
|
|
BASE_FILE|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/test_pve265.py|bytes=11781|sha256=16a9407d01bf73db687ef38684c59205fc2e6a9223cd9cee6ec6e8dde9d38707|mode=400|uid=0|gid=0
|
|
BASE_FILE|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/test_pve270.py|bytes=12077|sha256=9fccd20b490613fdd5b14b4105cf0d9fd744f204e1ed1d6893e54c9f69bf6fb4|mode=400|uid=0|gid=0
|
|
BASE_FILE|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/test_pve282.py|bytes=5785|sha256=1056407aab76e17181b05e043cb42c086058545a8e0027689ab4e03eee6531c4|mode=400|uid=0|gid=0
|
|
BASE_FILE|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/transport261.py|bytes=15364|sha256=b9b92f957bf441cb5d1702f4881897328e6c0338f8c3f6d41d27d9a349b37688|mode=400|uid=0|gid=0
|
|
BASE_FILE|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/v2core.py|bytes=11755|sha256=e285bf9a5678d07dcbb90261f9f93e7826ff524e04cfec780bf5a3c4b0dc29f2|mode=400|uid=0|gid=0
|
|
BASE_FILE|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/ct112_handler.py|bytes=10955|sha256=8cac6e627d5e7abe5d3163cdd24911995a1e3f45c776f9373c2aa68784f5ec6c|mode=400|uid=0|gid=0
|
|
BASE_FILE|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|bytes=59719|sha256=b6e79f087b9f1d21dac4f77a7b46b743299bbb85bc716e80d2ea67c2e038bcd7|mode=400|uid=0|gid=0
|
|
BASE_FILE|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/representative_driver.py|bytes=4214|sha256=b3050a8f95e6d9826a4f0786cd3ca32012b6ab10486d10e4981b50b8a0affff2|mode=400|uid=0|gid=0
|
|
BASE_FILE|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/wrapper.py|bytes=5055|sha256=2b5eb4fc0f6f466a0d3e5a007af987092b6c17baa3ff9241a6afc2c2e97df1d5|mode=400|uid=0|gid=0
|
|
BASE_AUTH_SNIPPETS_BEGIN=1
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/large264.py|line=12|text=import subprocess
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/large264.py|line=27|text= p=subprocess.Popen(argv,stdin=subprocess.DEVNULL,stdout=subprocess.PIPE,stderr=subprocess.PIPE,
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/large264.py|line=41|text= except subprocess.TimeoutExpired:raise t.Stop('STREAM_TIMEOUT') from None
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/pve265.py|line=13|text=import subprocess
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/pve265.py|line=144|text= except subprocess.TimeoutExpired:
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/pve265.py|line=160|text= p=subprocess.Popen(argv,stdin=subprocess.DEVNULL,stdout=subprocess.PIPE,stderr=subprocess.PIPE,
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/pve265.py|line=176|text= except subprocess.TimeoutExpired:raise PVEError('PRODUCER_TIMEOUT') from None
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/pve270.py|line=6|text=import hashlib,json,os,re,secrets,selectors,signal,stat,subprocess,sys,time
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/pve270.py|line=110|text= p=subprocess.Popen(['/proc/self/fd/'+str(self.fd),*args],pass_fds=(self.fd,),
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/pve270.py|line=111|text= stdin=subprocess.DEVNULL,stdout=subprocess.PIPE,stderr=subprocess.PIPE,
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/pve270.py|line=124|text= except subprocess.TimeoutExpired:raise old.PVEError('PERL_TIMEOUT') from None
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/pve270.py|line=217|text= p=subprocess.run([sys.executable,'-I','-B','-c',code,*args],stdin=subprocess.DEVNULL,stdout=subprocess.DEVNULL,
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/pve270.py|line=218|text= stderr=subprocess.DEVNULL,env=dict(old.ENV),user=u,group=g,extra_groups=[],close_fds=True,timeout=15)
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/pve270.py|line=247|text= p=subprocess.Popen(argv,stdin=subprocess.DEVNULL,stdout=subprocess.PIPE,stderr=subprocess.PIPE,
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/pve270.py|line=266|text= except subprocess.TimeoutExpired:raise old.PVEError('PRODUCER_TIMEOUT') from None
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/test_producer282.py|line=2|text=import copy,hashlib,json,os,stat,subprocess,sys,tempfile,time,unittest
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/test_producer282.py|line=10|text= return subprocess.run([sys.executable,'-I','-B','-c',code,*map(str,args)],user=uid,group=gid,extra_groups=[],stdin=subprocess.DEVNULL,stdout=subprocess.PIPE,stderr=subprocess.PIPE,timeout=10)
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/test_producer282.py|line=134|text= """Actual subprocess + tar/zstd, synthetic data; never runs vzdump on a guest."""
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/test_producer282.py|line=152|text= code='''import os,sys,subprocess,shutil
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/test_producer282.py|line=158|text=a=subprocess.run(['/usr/bin/tar','-cf','-','-C',str(p),'./etc/vzdump/pct.conf'],user=100000,group=100000,extra_groups=[],stdout=subprocess.PIPE,stderr=subprocess.PIPE)
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/test_producer282.py|line=161|text=z=subprocess.run(['/usr/bin/zstd','-q','-c'],input=a.stdout,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/test_producer282.py|line=193|text= def crc(p):return subprocess.run(['/usr/bin/zstd','--test','--quiet',str(p)],stdout=subprocess.DEVNULL,stderr=subprocess.DEVNULL).returncode==0
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/test_pve265.py|line=1|text="""Synthetic PVE fixtures; subprocess tests never execute real PVE tools."""
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/test_pve270.py|line=2|text=import copy,hashlib,json,os,stat,subprocess,sys,tempfile,time,unittest
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/test_pve270.py|line=10|text= return subprocess.run([sys.executable,'-I','-B','-c',code,*map(str,args)],user=uid,group=gid,extra_groups=[],stdin=subprocess.DEVNULL,stdout=subprocess.PIPE,stderr=subprocess.PIPE,timeout=10)
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/test_pve270.py|line=134|text= """Actual subprocess + tar/zstd, synthetic data; never runs vzdump on a guest."""
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/test_pve270.py|line=152|text= code='''import os,sys,subprocess,shutil
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/test_pve270.py|line=157|text=a=subprocess.run(['/usr/bin/tar','-cf','-','-C',str(p),'./etc/vzdump/pct.conf'],user=100000,group=100000,extra_groups=[],stdout=subprocess.PIPE,stderr=subprocess.PIPE)
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/test_pve270.py|line=160|text=z=subprocess.run(['/usr/bin/zstd','-q','-c'],input=a.stdout,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/test_pve270.py|line=192|text= def crc(p):return subprocess.run(['/usr/bin/zstd','--test','--quiet',str(p)],stdout=subprocess.DEVNULL,stderr=subprocess.DEVNULL).returncode==0
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/test_pve282.py|line=2|text=import copy, json, unittest, tempfile, time, subprocess
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/transport261.py|line=15|text=import subprocess
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/transport261.py|line=149|text= p=subprocess.Popen(argv,stdin=subprocess.DEVNULL,stdout=subprocess.PIPE,stderr=subprocess.PIPE,
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/transport261.py|line=163|text= except subprocess.TimeoutExpired:raise Stop('PROCESS_TIMEOUT') from None
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/ct112_handler.py|line=3|text=import hashlib,json,os,secrets,shutil,stat,subprocess,sys,time
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/ct112_handler.py|line=26|text= p=subprocess.run(argv,stdout=subprocess.PIPE,stderr=subprocess.PIPE,timeout=45,check=False,env=ENV)
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/ct112_handler.py|line=166|text= p=subprocess.run([str(ZSTD),'--test','--quiet','--',str(path)],stdout=subprocess.DEVNULL,stderr=subprocess.DEVNULL,timeout=3600,check=False,env=ENV)
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=2|text=import ast, base64, datetime as dt, hashlib, io, json, os, pathlib, random, re, select, shlex, shutil, stat, subprocess, sys, tarfile, tempfile, time
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=12|text=SSH=shutil.which('ssh') or '/usr/bin/ssh'
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=13|text=SCP=shutil.which('scp') or '/usr/bin/scp'
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=14|text=SSH_KEYSCAN=shutil.which('ssh-keyscan') or '/usr/bin/ssh-keyscan'
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=15|text=SSHPASS=shutil.which('sshpass') or '/usr/bin/sshpass'
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=16|text=OPERATOR_KEY=pathlib.Path('[SENSITIVE_PATH]
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=158|text= {'logical_id':'md-relay','scope':'vps','type':'vps_md','host':'[ADDR]','port':22,'user':'ops','hostkey_fingerprint':'SHA256:DXJGwunC5tEVvnC3nRxyayTN8FXmyJcVgf+Oa6udQsY','due_seconds':d,'enabled':True,'green1':True},
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=159|text= {'logical_id':'spb-ingress','scope':'vps','type':'vps_spb','host':'[ADDR]','port':22,'user':'edgeadmin','hostkey_fingerprint':'SHA256:Ef+HsZb8lIXoadvYBXuGKIL3H7NyJMmf9EYCoJRoSv8','due_seconds':d,'enabled':True,'green1':True},
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=160|text= {'logical_id':'us-netbird','scope':'vps','type':'vps_us','host':'[ADDR]','port':22,'user':'root','hostkey_fingerprint':'SHA256:805Q9eh/fePUci42dfY58I6q0lFiiPHn0/M+mckypmE','due_seconds':d,'enabled':True,'green1':True},
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=191|text= p=subprocess.run(argv,input=input,stdout=subprocess.PIPE,stderr=subprocess.PIPE,timeout=timeout,env=env)
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=192|text= except subprocess.TimeoutExpired: raise BackupError('COMMAND_TIMEOUT')
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=199|text= try:p=subprocess.run(argv,input=input,stdout=f,stderr=subprocess.PIPE,timeout=timeout)
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=200|text= except subprocess.TimeoutExpired: raise BackupError('STREAM_COMMAND_TIMEOUT')
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=206|text= src=subprocess.Popen(argv,stdin=subprocess.PIPE if input is not None else None,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=207|text= z=subprocess.Popen([zstd,'-T0','-3','-q','-o',str(out_path)],stdin=src.stdout,stdout=subprocess.DEVNULL,stderr=subprocess.PIPE)
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=213|text= except subprocess.TimeoutExpired:
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=216|text= except subprocess.TimeoutExpired:
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=236|text=def load_targets():
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=289|text= p=subprocess.Popen(argv,stdin=subprocess.PIPE,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=311|text= with subprocess.Popen(rclone_base_args()+['cat',remote_path,'--contimeout','20s','--timeout','5m'],stdout=subprocess.PIPE,stderr=subprocess.PIPE) as p:
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=365|text= with subprocess.Popen(rclone_base_args()+['cat',base+'/parts/'+part['name'],'--contimeout','20s','--timeout','5m'],stdout=subprocess.PIPE,stderr=subprocess.PIPE) as p:
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=379|text= p1=subprocess.Popen([zstd,'-dc',str(path)],stdout=subprocess.PIPE,stderr=subprocess.PIPE)
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=380|text= p2=subprocess.Popen([tar,'-tf','-'],stdin=p1.stdout,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=389|text= if typ in ('pve_node_bundle','vps_md','vps_spb','vps_us','router'):
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=399|text= p1=subprocess.Popen([zstd,'-dc',str(path)],stdout=subprocess.PIPE,stderr=subprocess.PIPE)
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=400|text= p2=subprocess.Popen([tar,'-xOf','-','db.sql'],stdin=p1.stdout,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=405|text=def cluster_inventory():
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=410|text=def ssh_remote(node,argv,input=None,timeout=1800,check=True):
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=413|text= return run([SSH,'-o','BatchMode=yes','-o','StrictHostKeyChecking=yes','root@'+node,cmd],input=input,timeout=timeout,check=check)
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=430|text= p=subprocess.run([sys.executable,'-B','-c',_HISTORICAL_HELPER,'import_all'],stdout=subprocess.PIPE,stderr=subprocess.PIPE,env=env,cwd='/',timeout=30)
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=441|text= p=subprocess.run([sys.executable,'-B','-c',_HISTORICAL_HELPER,'dump_argv',str(GUEST266)],input=canonical_json(req),stdout=subprocess.PIPE,stderr=subprocess.PIPE,env=env,cwd='/',timeout=30)
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=442|text= if p.returncode!=0: raise BackupError('GUEST266_SUBPROCESS_FAILED',f'rc_{p.returncode}_stderr_sha256_{hashlib.sha256(p.stderr).hexdigest()}')
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=444|text= except Exception: raise BackupError('GUEST266_SUBPROCESS_JSON')
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=476|text= q=ssh_remote(node,['pct','config',str(vmid)],timeout=30)
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=484|text= q=ssh_remote(node,pos,timeout=30,check=False)
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=492|text= cmd=[SSH,'-o','BatchMode=yes','-o','StrictHostKeyChecking=yes','root@'+node,remote]
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=494|text= try:p=subprocess.run(cmd,stdout=f,stderr=subprocess.PIPE,timeout=timeout)
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=495|text= except subprocess.TimeoutExpired:
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=514|text= ssh_remote(node,['install','-d','-m','710','-o','0','-g','100000',scratch],timeout=30)
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=515|text= q=ssh_remote(node,['stat','-c','%a:%u:%g',scratch],timeout=30)
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=524|text= ssh_remote(node,['rm','-rf','--',scratch],timeout=60,check=False);raise
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=529|text= ssh_remote(node,['install','-d','-m','700','-o','0','-g','0',srcdir],timeout=30)
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=531|text= ssh_remote(node,['vzdump',str(vmid),'--mode','snapshot','--compress','zstd','--dumpdir',srcdir],timeout=7200)
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=532|text= p=ssh_remote(node,['find',srcdir,'-maxdepth','1','-type','f','-printf','%p\n'],timeout=30)
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=537|text= else: run([SCP,'-q','-o','BatchMode=yes','-o','StrictHostKeyChecking=yes','root@'+node+':'+src,str(local)],timeout=7200)
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=540|text= ssh_remote(node,['rm','-rf','--',srcdir],timeout=60,check=False);raise
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=545|text= p=run(['ssh-keygen','-lf',str(OPERATOR_KEY),'-E','sha256'],timeout=30)
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=552|text= p=run([SSH_KEYSCAN,'-T','8','-p',str(port),'-t','ed25519',host],timeout=15)
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=557|text= if len(f)<3 or f[1]!='ssh-ed25519': continue
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=568|text= if t['type'] not in ('vps_md','vps_spb','vps_us'): continue
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=569|text= lines.append(scan_hostkey_line(t['host'],t['port'],t['hostkey_fingerprint']))
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=572|text=def vps_ssh_argv(t, *, use_agent=False):
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=573|text= a=[SSH,'-p',str(t['port']),'-o','BatchMode=yes','-o','StrictHostKeyChecking=yes','-o','UserKnownHostsFile='+str(VPS_KNOWN_HOSTS),'-o','ConnectTimeout=15']
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=575|text= a += ['-o','IdentitiesOnly=no']
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=577|text= a += ['-i',str(OPERATOR_KEY),'-o','IdentitiesOnly=yes','-o','IdentityAgent=none']
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=583|text= return run(vps_ssh_argv(t,use_agent=use_agent)+[remote],input=input,timeout=timeout,check=check)
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=588|text= pub=run(['ssh-keygen','-y','-f',str(OPERATOR_KEY)],timeout=30).stdout.strip()+b'\n'
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=591|text= if t['type'] not in ('vps_md','vps_spb'): continue
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=595|text= if not os.environ.get('SSH_AUTH_SOCK'): raise BackupError('FORWARDED_AGENT_REQUIRED_FOR_BOOTSTRAP')
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=596|text= script=b"umask 077; mkdir -p \"$HOME/.ssh\"; chmod 700 \"$HOME/.ssh\"; touch \"$HOME/.ssh/authorized_keys\"; chmod 600 \"$HOME/.ssh/authorized_keys\"; IFS= read -r k; if grep -qF -- \"$k\" \"$HOME/.ssh/authorized_keys\"; then printf 'ALREADY\\n'; else printf '%s\\n' \"$k\" >> \"$HOME/.ssh/authorized_keys\"; printf 'ADDED\\n'; fi"
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=612|text=def capture_vps_md(t,gen,work):
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=617|text= argv=vps_ssh_argv(t,use_agent=False)+[remote]
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=621|text= return payload,{'generation':gen,'source_class':'vps_md','consistency':'FILESET_PRE_POST_HASH_STABILITY','hashset_sha256':pre},None
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=627|text= argv=vps_ssh_argv(t,use_agent=False)+[remote]
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=664|text= argv=vps_ssh_argv(t,use_agent=False)+[remote]
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=732|text= pat=re.compile(r"\bsshpass\b[^\n]*?(?:^|\s)-f\s+(\"[^\"]*\"|'[^']*'|[^\s;]+)")
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=756|text= roots=[pathlib.Path('/etc'),pathlib.Path('[SENSITIVE_PATH]'),pathlib.Path('/var/lib/homelab-backup'),pathlib.Path('/var/lib/homelab-backup-v2')]
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=784|text= # sshpass requires password prompting to remain enabled; batch mode must stay off.
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=785|text= # The proven SSHPASS_FILE credential path otherwise cannot be used.
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=786|text= argv=[SSHPASS,'-f',str(pf),SSH,'-p','2222','-o','BatchMode=no','-o','NumberOfPasswordPrompts=1','-o','StrictHostKeyChecking=yes','-o','UserKnownHostsFile='+str(ROUTER_KNOWN_HOSTS),'-o','ConnectTimeout=15','routerbackup@'+target,'show running-config']
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=806|text= p=ssh_remote(node,args,input=input_bytes,timeout=timeout+60)
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=818|text= return f'''import hashlib,json,os,pathlib,select,shutil,subprocess,tempfile,time\nroot={root!r}; db={db!r}; strategy={strategy!r}; token=[REDACTED] not root.startswith('/var/www/forums/') or not db.startswith('xf_'): raise SystemExit(20)\ntmp=pathlib.Path('/tmp/homelab-v2-xf-'+token); tmp.mkdir(mode=0o700,exist_ok=False)\ndump=tmp/'db.sql'; out=pathlib.Path('/tmp/homelab-v2-xf-'+token+'.tar.zst')\nlock=None\ntry:\n if strategy=='global_read_lock':\n lock=subprocess.Popen(['mariadb','--user=root','--batch','--skip-column-names','--unbuffered'],stdin=subprocess.PIPE,stdout=subprocess.PIPE,stderr=subprocess.DEVNULL,text=True,bufsize=1)\n lock.stdin.write("FLUSH TABLES WITH READ LOCK; SELECT 'LOCKED';\\n"); lock.stdin.flush()\n rr,_,_=select.select([lock.stdout],[],[],5)\n if not rr or lock.stdout.readline().strip()!='LOCKED': raise RuntimeError('LOCK_ACQUIRE')\n dump_timeout=15\n else: dump_timeout=180\n with open(dump,'wb') as f:\n argv=['mariadb-dump','--user=ro...[TRUNCATED]
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=851|text= if OPERATOR_KEY.exists(): p.append('root/.ssh/id_ed25519_netbird_operator')
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=852|text= if pathlib.Path(str(OPERATOR_KEY)+'.pub').exists(): p.append('root/.ssh/id_ed25519_netbird_operator.pub')
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=859|text= else: ok=(ssh_remote(node,['test','-e','/'+rel],timeout=20,check=False).returncode==0)
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=868|text= pipe_to_zstd([SSH,'-o','BatchMode=yes','-o','StrictHostKeyChecking=yes','root@'+node,remote],payload,timeout=600)
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=875|text= if typ=='vps_md': return capture_vps_md(t,gen,work)
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=885|text= ssh_remote(cleanup['node'],['rm','-rf','--',cleanup['path']],timeout=60)
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=903|text=def backup_target(t,green_verify=False):
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=991|text= assert {t['logical_id'] for t in default_targets() if t.get('green1')}=={'ct112','node-pve01','md-relay','spb-ingress','us-netbird','router-netcraze','xf-codevipe','xf-zakrutim','control-bootstrap-v2'}
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/representative_driver.py|line=17|text= 'md-relay',
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/wrapper.py|line=7|text=LEGACY_IDS=('node-pve01','md-relay','spb-ingress','us-netbird','router-netcraze','xf-codevipe','xf-zakrutim','control-bootstrap-v2')
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/wrapper.py|line=37|text=def load_targets():
|
|
BASE_SRC|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/wrapper.py|line=40|text=def backup_target(t,green_verify=False):
|
|
BASE_AUTH_SNIPPET_COUNT=121
|
|
BASE_AUTH_SNIPPETS_END=1
|
|
BASE_KEY_FUNCTIONS_BEGIN=1
|
|
FUNCTION_BEGIN|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|name=load_targets|line=236
|
|
def load_targets():
|
|
j=json.loads(TARGETS_PATH.read_text())
|
|
if j.get('schema')!=1 or not isinstance(j.get('targets'),list): raise BackupError('TARGET_MANIFEST_SCHEMA')
|
|
ids=[x.get('logical_id') for x in j['targets']]
|
|
if len(ids)!=len(set(ids)): raise BackupError('TARGET_MANIFEST_DUPLICATE')
|
|
return j['targets']
|
|
FUNCTION_END|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|name=load_targets
|
|
FUNCTION_BEGIN|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|name=cluster_inventory|line=405
|
|
def cluster_inventory():
|
|
p=run(['pvesh','get','/cluster/resources','--type','vm','--output-format','json'],timeout=60)
|
|
try:return json.loads(p.stdout)
|
|
except Exception: raise BackupError('PVE_INVENTORY_JSON')
|
|
FUNCTION_END|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|name=cluster_inventory
|
|
FUNCTION_BEGIN|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|name=ssh_remote|line=410
|
|
def ssh_remote(node,argv,input=None,timeout=1800,check=True):
|
|
cmd='PATH=/usr/sbin:/usr/bin:/sbin:/bin; export PATH; exec '+shlex.join([str(x) for x in argv])
|
|
if node=='pve01': return run([str(x) for x in argv],input=input,timeout=timeout,check=check)
|
|
return run([SSH,'-o','BatchMode=yes','-o','StrictHostKeyChecking=yes','root@'+node,cmd],input=input,timeout=timeout,check=check)
|
|
FUNCTION_END|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|name=ssh_remote
|
|
FUNCTION_BEGIN|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|name=backup_target|line=903
|
|
def backup_target(t,green_verify=False):
|
|
if t['type']=='control': return control_verify(t)
|
|
gen=generation_id(t['logical_id']); work=ensure_dir(WORK_ROOT/gen,0o700)
|
|
payload=None; cleanup=None; sealed=False
|
|
try:
|
|
payload,meta,cleanup=capture_target(t,gen,work); meta['generation']=gen
|
|
manifest,msha,alloc=upload_and_seal(t,payload,meta); sealed=True
|
|
# SEALED-first cleanup invariant: only now remove source/local payload.
|
|
try:
|
|
if pathlib.Path(payload).exists(): pathlib.Path(payload).unlink()
|
|
finally: cleanup_source(cleanup)
|
|
if green_verify:
|
|
a=work/'verify-a.bin'; b=work/'verify-b.bin'
|
|
ha=reconstruct_replica(manifest,manifest['replicas'][0],a); validate_payload(t,a)
|
|
hb=reconstruct_replica(manifest,manifest['replicas'][1],b); validate_payload(t,b)
|
|
if ha!=hb or ha!=manifest['payload_sha256']: raise BackupError('GREEN_REPLICA_COMPARE')
|
|
a.unlink(missing_ok=True); b.unlink(missing_ok=True)
|
|
shutil.rmtree(work,ignore_errors=True)
|
|
write_state(t['logical_id'],'SUCCESS',generation=gen,payload_sha256=manifest['payload_sha256'],manifest_sha256=msha,replicas=manifest['replicas'],green_verified=bool(green_verify),last_success_epoch=epoch())
|
|
return {'logical_id':t['logical_id'],'generation':gen,'result':'SUCCESS','payload_bytes':manifest['payload_bytes'],'replicas':2,'allocator_healthy':alloc['healthy']}
|
|
except BackupError as e:
|
|
result='PARTIAL_VALID' if sealed else 'FAIL'
|
|
write_state(t['logical_id'],result,generation=gen,reason=e.code,sealed=sealed)
|
|
raise
|
|
FUNCTION_END|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|name=backup_target
|
|
FUNCTION_BEGIN|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/wrapper.py|name=load_targets|line=37
|
|
def load_targets():
|
|
base=canonical_targets();return base+[_ct_descriptor(base)]
|
|
FUNCTION_END|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/wrapper.py|name=load_targets
|
|
FUNCTION_BEGIN|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/wrapper.py|name=backup_target|line=40
|
|
def backup_target(t,green_verify=False):
|
|
if not isinstance(t,dict) or t.get('logical_id')!=CT_ID:
|
|
return _legacy_backup_target(t,green_verify)
|
|
try:
|
|
r=ct112.run_target(t,state_writer=legacy.write_state)
|
|
return {'logical_id':CT_ID,'generation':r['generation'],'result':'SUCCESS','payload_bytes':r['payload_bytes'],
|
|
'replicas':2,'manifest_sha256':r['manifest_sha256'],'payload_sha256':r['payload_sha256']}
|
|
except BaseException:
|
|
try: legacy.write_state(CT_ID,'FAIL',reason='CT112_PIPELINE_FAILED')
|
|
except BaseException: pass
|
|
raise legacy.BackupError('CT112_PIPELINE_FAILED') from None
|
|
FUNCTION_END|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/wrapper.py|name=backup_target
|
|
BASE_KEY_FUNCTION_COUNT=6
|
|
BASE_KEY_FUNCTIONS_END=1
|
|
BASE_VPS_MD_CONTEXT_BEGIN=1
|
|
VPS_CONTEXT_BEGIN|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=158
|
|
146:def utcnow(): return dt.datetime.now(dt.timezone.utc).replace(microsecond=0).isoformat().replace('+00:00','Z')
|
|
147:def epoch(): return int(time.time())
|
|
148:
|
|
149:def default_targets():
|
|
150: d=86400
|
|
151: out=[]
|
|
152: # PVE guests. Source node is resolved live every run.
|
|
153: for vmid,typ in [(110,'lxc'),(111,'lxc'),(112,'lxc'),(113,'lxc'),(130,'qemu'),(150,'qemu'),(160,'qemu'),(170,'qemu'),(171,'qemu'),(190,'qemu'),(210,'qemu'),(211,'qemu'),(9130,'qemu')]:
|
|
154: out.append({'logical_id':('ct' if typ=='lxc' else 'vm')+str(vmid),'scope':'pve','type':'pve_guest','vmid':vmid,'guest_type':typ,'due_seconds':d,'enabled':True,'green1':vmid==112})
|
|
155: for node in ('pve01','pve02','pve03'):
|
|
156: out.append({'logical_id':'node-'+node,'scope':'node','type':'pve_node_bundle','node':node,'due_seconds':7*d,'enabled':True,'green1':node=='pve01'})
|
|
157: out += [
|
|
158: {'logical_id':'md-relay','scope':'vps','type':'vps_md','host':'[ADDR]','port':22,'user':'ops','hostkey_fingerprint':'SHA256:DXJGwunC5tEVvnC3nRxyayTN8FXmyJcVgf+Oa6udQsY','due_seconds':d,'enabled':True,'green1':True},
|
|
159: {'logical_id':'spb-ingress','scope':'vps','type':'vps_spb','host':'[ADDR]','port':22,'user':'edgeadmin','hostkey_fingerprint':'SHA256:Ef+HsZb8lIXoadvYBXuGKIL3H7NyJMmf9EYCoJRoSv8','due_seconds':d,'enabled':True,'green1':True},
|
|
160: {'logical_id':'us-netbird','scope':'vps','type':'vps_us','host':'[ADDR]','port':22,'user':'root','hostkey_fingerprint':'SHA256:805Q9eh/fePUci42dfY58I6q0lFiiPHn0/M+mckypmE','due_seconds':d,'enabled':True,'green1':True},
|
|
161: {'logical_id':'router-netcraze','scope':'router','type':'router','due_seconds':d,'enabled':True,'green1':True},
|
|
162: ]
|
|
163: sites=[
|
|
164: ('codevipe',160,'pve02','/var/www/forums/codevipe/public','xf_codevipe','single_transaction'),
|
|
165: ('dsmods',160,'pve02','/var/www/forums/dsmods/public','xf_dsmods','single_transaction'),
|
|
166: ('kingofwolk',160,'pve02','/var/www/forums/kingofwolk/public','xf_kingofwolk','single_transaction'),
|
|
167: ('newfi',210,'pve01','/var/www/forums/newfi/public','xf_newfi','single_transaction'),
|
|
168: ('gamevipe',160,'pve02','/var/www/forums/gamevipe/public','xf_gamevipe','global_read_lock'),
|
|
169: ('hkmods',160,'pve02','/var/www/forums/hkmods/public','xf_hkmods','global_read_lock'),
|
|
170: ('zakrutim',160,'pve02','/var/www/forums/zakrutim/public','xf_zakrutim','global_read_lock')]
|
|
VPS_CONTEXT_END|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=158
|
|
VPS_CONTEXT_BEGIN|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=389
|
|
377: zstd=shutil.which('zstd') or '/usr/bin/zstd'; tar=shutil.which('tar') or '/usr/bin/tar'
|
|
378: p=run([zstd,'-t','-q',str(path)],timeout=300)
|
|
379: p1=subprocess.Popen([zstd,'-dc',str(path)],stdout=subprocess.PIPE,stderr=subprocess.PIPE)
|
|
380: p2=subprocess.Popen([tar,'-tf','-'],stdin=p1.stdout,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
|
|
381: p1.stdout.close(); out,err2=p2.communicate(timeout=300); err1=p1.stderr.read(); r1=p1.wait()
|
|
382: if r1!=0 or p2.returncode!=0: raise BackupError('TAR_ZST_LIST_FAILED')
|
|
383: names=out.decode('utf-8','replace').splitlines()
|
|
384: if not names: raise BackupError('TAR_ZST_EMPTY')
|
|
385: if required_member and required_member not in names: raise BackupError('REQUIRED_MEMBER_MISSING')
|
|
386:
|
|
387:def validate_payload(t,path):
|
|
388: typ=t['type']
|
|
389: if typ in ('pve_node_bundle','vps_md','vps_spb','vps_us','router'):
|
|
390: validate_tar_zst(path)
|
|
391: elif typ=='pve_guest':
|
|
392: if t['guest_type']=='lxc': validate_tar_zst(path)
|
|
393: else:
|
|
394: # Green set is intentionally CT-only. Scheduled QEMU backups still get dual remote byte readback.
|
|
395: run([shutil.which('zstd') or '/usr/bin/zstd','-t','-q',str(path)],timeout=600)
|
|
396: elif typ=='xenforo':
|
|
397: validate_tar_zst(path,'db.sql')
|
|
398: zstd=shutil.which('zstd') or '/usr/bin/zstd'; tar=shutil.which('tar') or '/usr/bin/tar'
|
|
399: p1=subprocess.Popen([zstd,'-dc',str(path)],stdout=subprocess.PIPE,stderr=subprocess.PIPE)
|
|
400: p2=subprocess.Popen([tar,'-xOf','-','db.sql'],stdin=p1.stdout,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
|
|
401: p1.stdout.close(); data,err=p2.communicate(timeout=300); p1.wait()
|
|
VPS_CONTEXT_END|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=389
|
|
VPS_CONTEXT_BEGIN|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=568
|
|
556: f=line.split()
|
|
557: if len(f)<3 or f[1]!='ssh-ed25519': continue
|
|
558: try: blob=base64.b64decode(f[2])
|
|
559: except Exception: continue
|
|
560: fp='SHA256:'+base64.b64encode(hashlib.sha256(blob).digest()).decode().rstrip('=')
|
|
561: if fp==expected: good.append(line)
|
|
562: if len(good)!=1: raise BackupError('HOSTKEY_PIN_MISMATCH')
|
|
563: return good[0]
|
|
564:
|
|
565:def ensure_vps_known_hosts(targets):
|
|
566: lines=[]
|
|
567: for t in targets:
|
|
568: if t['type'] not in ('vps_md','vps_spb','vps_us'): continue
|
|
569: lines.append(scan_hostkey_line(t['host'],t['port'],t['hostkey_fingerprint']))
|
|
570: atomic_write(VPS_KNOWN_HOSTS,('\n'.join(lines)+'\n').encode(),0o600)
|
|
571:
|
|
572:def vps_ssh_argv(t, *, use_agent=False):
|
|
573: a=[SSH,'-p',str(t['port']),'-o','BatchMode=yes','-o','StrictHostKeyChecking=yes','-o','UserKnownHostsFile='+str(VPS_KNOWN_HOSTS),'-o','ConnectTimeout=15']
|
|
574: if use_agent:
|
|
575: a += ['-o','IdentitiesOnly=no']
|
|
576: else:
|
|
577: a += ['-i',str(OPERATOR_KEY),'-o','IdentitiesOnly=yes','-o','IdentityAgent=none']
|
|
578: a += [t['user']+'@'+t['host']]
|
|
579: return a
|
|
580:
|
|
VPS_CONTEXT_END|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=568
|
|
VPS_CONTEXT_BEGIN|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=591
|
|
579: return a
|
|
580:
|
|
581:def vps_run(t, remote_argv, *, input=None, timeout=300, use_agent=False, check=True):
|
|
582: remote=shlex.join([str(x) for x in remote_argv])
|
|
583: return run(vps_ssh_argv(t,use_agent=use_agent)+[remote],input=input,timeout=timeout,check=check)
|
|
584:
|
|
585:def bootstrap_vps_keys(targets):
|
|
586: operator_key_fingerprint()
|
|
587: ensure_vps_known_hosts(targets)
|
|
588: pub=run(['ssh-keygen','-y','-f',str(OPERATOR_KEY)],timeout=30).stdout.strip()+b'\n'
|
|
589: results={}
|
|
590: for t in targets:
|
|
591: if t['type'] not in ('vps_md','vps_spb'): continue
|
|
592: probe=vps_run(t,['true'],timeout=30,use_agent=False,check=False)
|
|
593: if probe.returncode==0:
|
|
594: results[t['logical_id']]='ALREADY_OPERATOR_KEY'; continue
|
|
595: if not os.environ.get('SSH_AUTH_SOCK'): raise BackupError('FORWARDED_AGENT_REQUIRED_FOR_BOOTSTRAP')
|
|
596: script=b"umask 077; mkdir -p \"$HOME/.ssh\"; chmod 700 \"$HOME/.ssh\"; touch \"$HOME/.ssh/authorized_keys\"; chmod 600 \"$HOME/.ssh/authorized_keys\"; IFS= read -r k; if grep -qF -- \"$k\" \"$HOME/.ssh/authorized_keys\"; then printf 'ALREADY\\n'; else printf '%s\\n' \"$k\" >> \"$HOME/.ssh/authorized_keys\"; printf 'ADDED\\n'; fi"
|
|
597: p=vps_run(t,['sh','-c',script.decode()],input=pub,timeout=45,use_agent=True)
|
|
598: tag=p.stdout.decode().strip()
|
|
599: if tag not in ('ADDED','ALREADY'): raise BackupError('VPS_AUTHORIZED_KEY_BOOTSTRAP_RESULT')
|
|
600: vps_run(t,['true'],timeout=30,use_agent=False)
|
|
601: results[t['logical_id']]=tag
|
|
602: # US was already operator-key based; prove it agentless too.
|
|
603: us=[x for x in targets if x['type']=='vps_us'][0]
|
|
VPS_CONTEXT_END|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=591
|
|
VPS_CONTEXT_BEGIN|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=612
|
|
600: vps_run(t,['true'],timeout=30,use_agent=False)
|
|
601: results[t['logical_id']]=tag
|
|
602: # US was already operator-key based; prove it agentless too.
|
|
603: us=[x for x in targets if x['type']=='vps_us'][0]
|
|
604: vps_run(us,['true'],timeout=30,use_agent=False)
|
|
605: results[us['logical_id']]='ALREADY_US_OPERATOR_KEY'
|
|
606: return results
|
|
607:
|
|
608:def remote_hashset(t,command):
|
|
609: p=vps_run(t,['sh','-c',command],timeout=120,use_agent=False)
|
|
610: return sha256_bytes(p.stdout)
|
|
611:
|
|
612:def capture_vps_md(t,gen,work):
|
|
613: files=['etc/netbird/install.conf','var/lib/netbird/state.json','var/lib/netbird/service.json','var/lib/netbird/active_profile.json','var/lib/netbird/default.json']
|
|
614: cmd="sudo -n sha256sum -- "+' '.join(shlex.quote('/'+x) for x in files)
|
|
615: pre=remote_hashset(t,cmd)
|
|
616: remote=shlex.join(['sudo','-n','tar','--numeric-owner','-C','/','-cf','-']+files)
|
|
617: argv=vps_ssh_argv(t,use_agent=False)+[remote]
|
|
618: payload=work/'payload.tar.zst'; pipe_to_zstd(argv,payload,timeout=600)
|
|
619: post=remote_hashset(t,cmd)
|
|
620: if pre!=post: raise BackupError('MD_SOURCE_CHANGED_DURING_CAPTURE')
|
|
621: return payload,{'generation':gen,'source_class':'vps_md','consistency':'FILESET_PRE_POST_HASH_STABILITY','hashset_sha256':pre},None
|
|
622:
|
|
623:def capture_vps_spb(t,gen,work):
|
|
624: hcmd="find /etc/nginx -xdev -type f -print0 | sort -z | xargs -0 sha256sum"
|
|
VPS_CONTEXT_END|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=612
|
|
VPS_CONTEXT_BEGIN|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=621
|
|
609: p=vps_run(t,['sh','-c',command],timeout=120,use_agent=False)
|
|
610: return sha256_bytes(p.stdout)
|
|
611:
|
|
612:def capture_vps_md(t,gen,work):
|
|
613: files=['etc/netbird/install.conf','var/lib/netbird/state.json','var/lib/netbird/service.json','var/lib/netbird/active_profile.json','var/lib/netbird/default.json']
|
|
614: cmd="sudo -n sha256sum -- "+' '.join(shlex.quote('/'+x) for x in files)
|
|
615: pre=remote_hashset(t,cmd)
|
|
616: remote=shlex.join(['sudo','-n','tar','--numeric-owner','-C','/','-cf','-']+files)
|
|
617: argv=vps_ssh_argv(t,use_agent=False)+[remote]
|
|
618: payload=work/'payload.tar.zst'; pipe_to_zstd(argv,payload,timeout=600)
|
|
619: post=remote_hashset(t,cmd)
|
|
620: if pre!=post: raise BackupError('MD_SOURCE_CHANGED_DURING_CAPTURE')
|
|
621: return payload,{'generation':gen,'source_class':'vps_md','consistency':'FILESET_PRE_POST_HASH_STABILITY','hashset_sha256':pre},None
|
|
622:
|
|
623:def capture_vps_spb(t,gen,work):
|
|
624: hcmd="find /etc/nginx -xdev -type f -print0 | sort -z | xargs -0 sha256sum"
|
|
625: pre=remote_hashset(t,hcmd)
|
|
626: remote=shlex.join(['tar','--numeric-owner','-C','/','-cf','-','etc/nginx'])
|
|
627: argv=vps_ssh_argv(t,use_agent=False)+[remote]
|
|
628: payload=work/'payload.tar.zst'; pipe_to_zstd(argv,payload,timeout=900)
|
|
629: post=remote_hashset(t,hcmd)
|
|
630: if pre!=post: raise BackupError('SPB_SOURCE_CHANGED_DURING_CAPTURE')
|
|
631: return payload,{'generation':gen,'source_class':'vps_spb','consistency':'STATIC_CONFIG_FILESET_PRE_POST_HASH_STABILITY','tls_recovery':'REISSUE_TLS_ON_RESTORE','hashset_sha256':pre},None
|
|
632:
|
|
633:US_REMOTE_SCRIPT=r'''import io,os,sqlite3,sys,tarfile,time,json,hashlib
|
|
VPS_CONTEXT_END|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=621
|
|
VPS_CONTEXT_BEGIN|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=875
|
|
863: payload=work/'payload.tar.zst'
|
|
864: argv=['tar','--numeric-owner','--xattrs','--acls','-C','/','-cf','-']+paths
|
|
865: if node=='pve01': pipe_to_zstd(argv,payload,timeout=300)
|
|
866: else:
|
|
867: remote='PATH=/usr/sbin:/usr/bin:/sbin:/bin; export PATH; exec '+shlex.join(argv)
|
|
868: pipe_to_zstd([SSH,'-o','BatchMode=yes','-o','StrictHostKeyChecking=yes','root@'+node,remote],payload,timeout=600)
|
|
869: return payload,{'generation':gen,'source_class':'pve_node_bundle','node':node,'recovery_policy':'CLEAN_INSTALL_JOIN_RECONCILE_NOT_BLIND_PMXCFS_OVERWRITE','member_roots':len(paths)},None
|
|
870:
|
|
871:def capture_target(t,gen,work):
|
|
872: typ=t['type']
|
|
873: if typ=='pve_guest': return capture_pve_guest(t,gen,work)
|
|
874: if typ=='pve_node_bundle': return capture_node_bundle(t,gen,work)
|
|
875: if typ=='vps_md': return capture_vps_md(t,gen,work)
|
|
876: if typ=='vps_spb': return capture_vps_spb(t,gen,work)
|
|
877: if typ=='vps_us': return capture_vps_us(t,gen,work)
|
|
878: if typ=='router': return capture_router(t,gen,work)
|
|
879: if typ=='xenforo': return capture_xenforo(t,gen,work)
|
|
880: raise BackupError('CAPTURE_TYPE_UNKNOWN')
|
|
881:
|
|
882:def cleanup_source(cleanup):
|
|
883: if not cleanup: return
|
|
884: if cleanup.get('kind')=='pve_source_dir':
|
|
885: ssh_remote(cleanup['node'],['rm','-rf','--',cleanup['path']],timeout=60)
|
|
886:
|
|
887:def control_verify(t):
|
|
VPS_CONTEXT_END|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=875
|
|
VPS_CONTEXT_BEGIN|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=991
|
|
979: try:j=json.loads(q.stdout)
|
|
980: except Exception: continue
|
|
981: if j.get('status')=='SEALED' and j.get('generation')==g: bygen.setdefault(g,set()).add(physical)
|
|
982: good=sorted(g for g,phys in bygen.items() if len(phys)>=2)
|
|
983: print('DISCOVER_LOGICAL_ID='+logical_id); print('DISCOVER_TWO_PHYSICAL_SEALED_COUNT='+str(len(good)))
|
|
984: for g in good[-20:]: print('DISCOVER_GENERATION='+g)
|
|
985: return 0 if good else 2
|
|
986:
|
|
987:def selftest():
|
|
988: assert PART_SIZE==536870912
|
|
989: assert len(REMOTES)==13 and len({p for _,p in REMOTES})==13
|
|
990: assert len(default_targets())>=28
|
|
991: assert {t['logical_id'] for t in default_targets() if t.get('green1')}=={'ct112','node-pve01','md-relay','spb-ingress','us-netbird','router-netcraze','xf-codevipe','xf-zakrutim','control-bootstrap-v2'}
|
|
992: assert _pve_dumpdir_spec('lxc')==(0o710,100000)
|
|
993: assert _pve_dumpdir_spec('qemu')==(0o700,0)
|
|
994: assert GUEST266_SHA=='86433bc424d5fa5017c20f7a237c82874b02357a3283660d8a6b0039ef2bc868'
|
|
995: assert P282_SHA=='73c6cb7773eb8e82d1e7ed5ab4733b4afb88f74ae69c09125d0378d6e480e012'
|
|
996: if GUEST266.is_file():
|
|
997: assert _isolated_release_graph_selftest() is True
|
|
998: av,_=proven_lxc_dump_argv({'vmid':112},{'maxdisk':8350298112,'maxmem':536870912,'status':'running','name':'unbound1'},'pve01','/var/tmp/homelab-vzdump-selftest')
|
|
999: assert '--stdout' in av and '--tmpdir' in av and '/var/tmp/homelab-vzdump-selftest' in av
|
|
1000: print('SELFTEST_ENGINE396=10')
|
|
1001: return 0
|
|
1002:
|
|
1003:def main(argv):
|
|
VPS_CONTEXT_END|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py|line=991
|
|
VPS_CONTEXT_BEGIN|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/representative_driver.py|line=17
|
|
5:CT112 producer/cloud path. It routes CT112 to an explicitly supplied callback and
|
|
6:isolates legacy engine state writes into a candidate-owned state directory.
|
|
7:"""
|
|
8:from __future__ import annotations
|
|
9:
|
|
10:import json
|
|
11:import os
|
|
12:from pathlib import Path
|
|
13:from typing import Callable, Iterable, Mapping, Any
|
|
14:
|
|
15:LEGACY_IDS=(
|
|
16: 'node-pve01',
|
|
17: 'md-relay',
|
|
18: 'spb-ingress',
|
|
19: 'us-netbird',
|
|
20: 'router-netcraze',
|
|
21: 'xf-codevipe',
|
|
22: 'xf-zakrutim',
|
|
23: 'control-bootstrap-v2',
|
|
24:)
|
|
25:CT112_ID='guest-pve01-lxc-112'
|
|
26:REPRESENTATIVE_IDS=(CT112_ID,)+LEGACY_IDS
|
|
27:CANDIDATE_ROOT=Path(__file__).resolve().parent
|
|
28:STATE_ROOT=CANDIDATE_ROOT/'state'
|
|
29:
|
|
VPS_CONTEXT_END|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/representative_driver.py|line=17
|
|
VPS_CONTEXT_BEGIN|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/wrapper.py|line=7
|
|
1:#!/usr/bin/env python3
|
|
2:from __future__ import annotations
|
|
3:import errno,fcntl,importlib.util,os,sys
|
|
4:from pathlib import Path
|
|
5:ROOT=Path(__file__).resolve().parent
|
|
6:CT_ID='guest-pve01-lxc-112'
|
|
7:LEGACY_IDS=('node-pve01','md-relay','spb-ingress','us-netbird','router-netcraze','xf-codevipe','xf-zakrutim','control-bootstrap-v2')
|
|
8:LOCK=Path('/run/homelab-backup-heavy.lock')
|
|
9:
|
|
10:def _load(name,path):
|
|
11: spec=importlib.util.spec_from_file_location(name,str(path))
|
|
12: if spec is None or spec.loader is None: raise RuntimeError('MODULE_SPEC')
|
|
13: mod=importlib.util.module_from_spec(spec);sys.modules[name]=mod;spec.loader.exec_module(mod);return mod
|
|
14:
|
|
15:legacy=_load('_backupv2_legacy486',ROOT/'legacy_engine.py')
|
|
16:ct112=_load('_backupv2_ct112_486',ROOT/'ct112_handler.py')
|
|
17:driver=_load('_backupv2_driver454_479',ROOT/'representative_driver.py')
|
|
18:_legacy_load_targets=legacy.load_targets
|
|
19:_legacy_backup_target=legacy.backup_target
|
|
VPS_CONTEXT_END|path=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/wrapper.py|line=7
|
|
BASE_VPS_MD_CONTEXT_COUNT=10
|
|
BASE_VPS_MD_CONTEXT_END=1
|
|
BASE_RELEASE_AUTH_CLASS=BASE_VPS_MD_BACKUP_TARGET_LOCATED
|
|
DECISION=PASS_MOLDOVA_1147_BASE_RELEASE_AUTH_COLLECTED
|
|
NEXT_GATE=READ_EXACT_1147_AND_BUILD_REMOTE_ROLE_PREFLIGHT_WITH_PROVEN_LEGACY_AUTH
|
|
TASK_RESULT=PASS_MOLDOVA_BASE_RELEASE_AUTH
|
|
HOMELAB_RESULT_CONTRACT={"version":1,"command_id":"SUPPORT-260924-HOMELAB-MOLDOVA-BASE-RELEASE-AUTH-1147R1","status":"OK","changes_made":false,"rollback_started":false,"rollback_restored":null}
|
|
WORKERS2_MOLDOVA_BASE_RELEASE_AUTH_END=1
|
|
|
|
OUTPUT_END
|
|
CHAT_OUTPUT_END
|