vdi-cloud/tools/deploy-guest-power-policy.py

56 lines
2.5 KiB
Python

#!/usr/bin/env python3
"""Apply/query/restore power-menu policy via the existing PVE guest agent.
Uses the repository's existing PVE authentication. Never powers off/reboots a VM.
"""
import argparse
import base64
import json
from pathlib import Path
import runpy
import sys
import time
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('action', choices=['apply', 'query', 'restore'])
parser.add_argument('--vm', type=int, required=True, choices=[100, 101, 102])
parser.add_argument('--user', help='Required for Linux guests')
parser.add_argument('--evidence', type=Path, help='Save the guest-agent result as JSON')
args = parser.parse_args()
root = Path(__file__).resolve().parent
api = runpy.run_path(str(root / 'pve_vm.py'))['api']
ticket, csrf = runpy.run_path(str(root / 'pve_term_exec.py'))['login']()
base = f'/api2/json/nodes/starhe/qemu/{args.vm}'
if api(base + '/status/current', ticket=ticket).get('status') != 'running':
raise SystemExit('Guest is not running; nothing changed')
if args.vm == 100:
source = (root / 'set-guest-power-policy.ps1').read_text(encoding='utf-8')
source = "$ProgressPreference='SilentlyContinue'\n& {\n" + source + "\n} -Action " + args.action.capitalize()
encoded = base64.b64encode(source.encode('utf-16le')).decode()
command = ['powershell.exe', '-NoProfile', '-NonInteractive', '-EncodedCommand', encoded]
else:
if not args.user:
parser.error('--user is required for Linux')
source = (root / 'set-guest-power-policy.py').read_text(encoding='utf-8')
command = ['python3', '-c', source, args.action, '--user', args.user]
pid = api(base + '/agent/exec', json_body={'command': command}, ticket=ticket, csrf=csrf)['pid']
for _ in range(60):
time.sleep(1)
result = api(base + f'/agent/exec-status?pid={pid}', ticket=ticket)
if result.get('exited'):
report = json.dumps({'vm': args.vm, **result}, ensure_ascii=True)
if args.evidence:
args.evidence.parent.mkdir(parents=True, exist_ok=True)
args.evidence.write_text(report + '\n', encoding='utf-8')
print(report)
if result.get('exitcode') or result.get('out-truncated') or result.get('err-truncated'):
raise SystemExit(1)
return
raise SystemExit('Guest operation timed out; query before retrying')
if __name__ == '__main__':
main()