#!/usr/bin/env python3 """Create a snapshot of ALL running servers in the Hetzner account.""" import urllib.request, json, os, sys, time API = "https://api.hetzner.cloud/v1" # Read token from file to avoid shell issues script_dir = os.path.dirname(os.path.abspath(__file__)) token_file = os.path.join(script_dir, ".hetzner_token") try: with open(token_file) as f: TOKEN = f.read().strip() except FileNotFoundError: print(f"HETZNER_TOKEN file not found at {token_file}") sys.exit(1) HEADERS = {"Authorization": f"Bearer {TOKEN}", "Content-Type": "application/json"} def api(path, data=None, method=None): req = urllib.request.Request(f"{API}/{path}", headers=HEADERS, method=method) if data: req.data = json.dumps(data).encode() try: resp = urllib.request.urlopen(req) return json.loads(resp.read()) if resp.status != 204 else {} except urllib.error.HTTPError as e: body = e.read().decode() print(f" ERROR {e.code}: {body}") return None # Get all servers servers = api("servers") or {"servers": []} if not servers["servers"]: print("No servers found.") sys.exit(0) print(f"Found {len(servers['servers'])} servers\n") for s in servers["servers"]: name = s["name"] sid = s["id"] status = s["status"] if status != "running": print(f" SKIP {name:30s} — {status}") continue print(f" SNAPSHOT {name:30s} (ID: {sid}) ...", end=" ", flush=True) result = api(f"servers/{sid}/actions/create_image", { "type": "snapshot", "description": f"auto-weekly-{name}-{time.strftime('%Y-%m-%d')}" }, method="POST") if result: action = result.get("action", {}) print(f"started (action {action.get('id', '?')})") else: print("FAILED") print("\nDone.")