import json import os import shutil import subprocess import sys import time import urllib.error import urllib.request ANODE_URL = os.environ.get("TUXFLOTTE_ANODE_URL", "https://anode.tuxflotte.de") CREDENTIALS_PATH = os.environ.get( "TUXFLOTTE_AGENT_CREDENTIALS", "/etc/tuxflotte/agent.credentials" ) FALLBACK_INTERVAL_SECONDS = 60 def log(message): print(f"[tuxflotte-agent] {message}", flush=True) def load_credentials(): with open(CREDENTIALS_PATH, "r", encoding="utf-8") as f: credentials = json.load(f) return credentials["device_id"], credentials["agent_secret"] def checkin(device_id, agent_secret): body = json.dumps({"device_id": device_id}).encode("utf-8") req = urllib.request.Request( ANODE_URL + "/api/v1/agent/checkin", data=body, headers={ "Content-Type": "application/json", "Authorization": f"Bearer {agent_secret}", }, method="POST", ) with urllib.request.urlopen(req, timeout=15) as response: return json.loads(response.read().decode("utf-8")) def apply_blueprints(ansible_repo, blueprints): roles = ",".join(b["ansible_role"] for b in blueprints) log(f"Applying roles via ansible-pull: {roles}") result = subprocess.run( [ "ansible-pull", "-U", ansible_repo, "--tags", roles, "-i", "localhost,", "site.yml", ], check=False, ) if result.returncode != 0: log(f"ansible-pull exited with status {result.returncode}") def run_once(device_id, agent_secret): result = checkin(device_id, agent_secret) if not result.get("success"): log(f"Check-in failed: {result.get('message', result.get('error'))}") return FALLBACK_INTERVAL_SECONDS blueprints = result.get("blueprints", []) if blueprints: apply_blueprints(result["ansible_repo"], blueprints) else: log("Check-in ok, nothing to do.") return result.get("poll_interval_seconds", FALLBACK_INTERVAL_SECONDS) def main(): if shutil.which("ansible-pull") is None: log("ansible-pull nicht gefunden. Bitte ansible-core installieren.") sys.exit(1) device_id, agent_secret = load_credentials() while True: try: interval = run_once(device_id, agent_secret) except (urllib.error.URLError, OSError) as exc: log(f"Check-in-Fehler: {exc}") interval = FALLBACK_INTERVAL_SECONDS time.sleep(interval) if __name__ == "__main__": main()