"""VM lifecycle endpoints backed by the existing UVM lifecycle service.""" from __future__ import annotations from fastapi import APIRouter, Depends, status from ..api_models import DestroyResponse, VmCreateRequest, VmResponse, vm_response from ..app import Application from ..domain import PortRule, VmSpec from ..validation import parse_cpu, parse_name, parse_password, parse_ports, parse_ram, parse_username from .dependencies import get_authorized_application router = APIRouter(prefix="/vms", tags=["vms"]) @router.get("", response_model=list[VmResponse]) def list_vms( application: Application = Depends(get_authorized_application), ) -> list[VmResponse]: return [ vm_response( listed.vm, observed_status=listed.observed_status, policy_status=listed.policy_status, ) for listed in application.lifecycle.list_vms() ] @router.post("", response_model=VmResponse, status_code=status.HTTP_201_CREATED) def create_vm( request: VmCreateRequest, application: Application = Depends(get_authorized_application), ) -> VmResponse: ports = parse_ports([f"{rule.port}:{rule.protocol}" for rule in request.ports or ()]) if not request.no_default_ports and not any(rule.port == 22 for rule in ports): ports = tuple(sorted((*ports, PortRule(22)), key=lambda rule: rule.port)) vm = application.lifecycle.create( VmSpec( cpu=parse_cpu(str(request.cpu)), ram_mib=parse_ram(str(request.ram)), guest_ip=request.guest_ip, username=parse_username(request.username), password=parse_password(request.password.get_secret_value()), name=parse_name(request.name) if request.name is not None else None, ports=ports, ) ) policy_status = "unmanaged" if vm.ports is None else "unknown" if vm.ports is not None and vm.status == "running" and vm.operation is None: policy_status = "enforced" return vm_response(vm, policy_status=policy_status) @router.post("/{vm_id}/stop", response_model=VmResponse) def stop_vm( vm_id: str, application: Application = Depends(get_authorized_application), ) -> VmResponse: return vm_response(application.lifecycle.stop(vm_id), policy_status="inactive") @router.delete("/{vm_id}", response_model=DestroyResponse) def destroy_vm( vm_id: str, application: Application = Depends(get_authorized_application), ) -> DestroyResponse: vm = application.lifecycle.destroy(vm_id) return DestroyResponse(id=vm.id) @router.get("/{identifier}", response_model=VmResponse) def get_vm( identifier: str, application: Application = Depends(get_authorized_application), ) -> VmResponse: listed = application.lifecycle.inspect(identifier) return vm_response( listed.vm, observed_status=listed.observed_status, policy_status=listed.policy_status, )