56 lines
2.5 KiB
Python
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()
|