#!/usr/bin/env python3 import os, sys, subprocess, logging, time, tempfile, shutil from pathlib import Path from datetime import datetime # === Logging Setup === SCRIPT_NAME = Path(sys.argv[0]).stem LOGFILE = f"/var/log/{SCRIPT_NAME}.log" logging.basicConfig( filename=LOGFILE, level=logging.INFO, format='[%(asctime)s] %(message)s', datefmt='%Y-%m-%d %H:%M:%S' ) console = logging.StreamHandler() console.setLevel(logging.INFO) formatter = logging.Formatter('[%(asctime)s] %(message)s', '%Y-%m-%d %H:%M:%S') console.setFormatter(formatter) logging.getLogger().addHandler(console) def log(msg): logging.info(msg) # === Config Loader === import yaml CONFIG = {} def load_config(path): global CONFIG if not os.path.isfile(path): log(f"ERROR: Config file not found: {path}") sys.exit(1) with open(path) as f: CONFIG = yaml.safe_load(f) log("Config loaded successfully") # === SSH / SCP Wrappers === def rssh(cmd, port=None, host=None): port = port or CONFIG.get('SOURCEPORT', 22) host = host or CONFIG.get('SOURCEHOST') ssh_cmd = ["ssh", "-o", "BatchMode=yes", "-o", "ConnectTimeout=10", "-p", str(port), f"root@{host}"] + cmd return subprocess.run(ssh_cmd, capture_output=True, text=True) def rscp(local, remote, port=None, host=None): port = port or CONFIG.get('SOURCEPORT', 22) host = host or CONFIG.get('SOURCEHOST') scp_cmd = ["scp", "-P", str(port), local, f"root@{host}:{remote}"] return subprocess.run(scp_cmd) # === Beispiel-Funktionen 1:1 === def get_sourcehostname(): if not CONFIG.get('SOURCEHOSTNAME'): log(f"SOURCEHOSTNAME empty, retrieving from {CONFIG['SOURCEHOST']}...") result = rssh(["hostname"]) CONFIG['SOURCEHOSTNAME'] = result.stdout.strip() log(f"Detected SOURCEHOSTNAME: {CONFIG['SOURCEHOSTNAME']}") def shutdown_now(): if CONFIG.get('SHUTDOWN', 'no').lower() == 'yes': # Hier würde Python wait_replace() aufrufen, send_piggyback() etc. log("Shutdown requested. Would execute now...") else: log("No shutdown requested.") # === Weitere Funktionen nach Bash === # zfs_replace(), wait_replace(), write_zsync_config(), run_zsync(), # run_remote_updates(), send_piggyback(), run_pbs_backup(), etc. # === Main Execution === def main(): import argparse parser = argparse.ArgumentParser() parser.add_argument("-c", "--config", help="Path to config") parser.add_argument("function", nargs='?', help="Run specific function") args = parser.parse_args() if args.config: load_config(args.config) if args.function: func = globals().get(args.function) if func: func() else: log(f"Function {args.function} not found") sys.exit(1) else: # Full backup run log("Running full backup...") # Full run calls all functions like in Bash # wait(), set_dynamic_route(), write_zsync_config(), run_zsync(), etc.