#!/usr/bin/env python3 # qga-run.py - run a PowerShell snippet inside the win11 VM via qemu-guest-agent. # Stdin = PS snippet. Stdout = combined stdout, then optional STDERR block, # then "--- exit N ---". Exit code 0 on PS rc 0, 1 otherwise. import base64, json, os, subprocess, sys, time DOMAIN = os.environ.get('VM_DOMAIN', 'win11') TIMEOUT = int(os.environ.get('QGA_TIMEOUT', '300')) def virsh(cmd): p = subprocess.run( ['virsh', '-c', 'qemu:///system', 'qemu-agent-command', DOMAIN, json.dumps(cmd)], capture_output=True, text=True, timeout=120, ) if p.returncode != 0: sys.exit(f"virsh err: {p.stderr.strip()}") return json.loads(p.stdout)['return'] snippet = sys.stdin.read() if not snippet.strip(): sys.exit("qga-run.py: empty PowerShell snippet on stdin") args = ['-NoProfile', '-ExecutionPolicy', 'Bypass', '-Command', snippet] pid = virsh({'execute': 'guest-exec', 'arguments': {'path': 'powershell.exe', 'arg': args, 'capture-output': True}})['pid'] deadline = time.time() + TIMEOUT while time.time() < deadline: st = virsh({'execute': 'guest-exec-status', 'arguments': {'pid': pid}}) if st.get('exited'): out = base64.b64decode(st.get('out-data', '')).decode('utf-8', 'replace') err = base64.b64decode(st.get('err-data', '')).decode('utf-8', 'replace') rc = st.get('exitcode') sys.stdout.write(out) if err: sys.stdout.write('\n--- STDERR ---\n') sys.stdout.write(err) sys.stdout.write(f'\n--- exit {rc} ---\n') sys.exit(0 if rc == 0 else 1) time.sleep(0.5) sys.exit(f"timeout waiting for pid {pid}")