Erstmalig getrackt (war bisher lokal ungetrackt). Enthält gegenüber dem Root-Stand: PBSPORT, DYNROUTE/DDNS-Konfig, zfs_replace()/wait_replace() für Resilver-Abwarten vor Shutdown, BatchMode SSH, write_zsync_config()- Anpassungen (Snapshot-Label-Präfix, checkzfs_spool_maxage).
91 lines
2.9 KiB
Python
91 lines
2.9 KiB
Python
#!/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. |