package proxmox import ( "net/netip" "gitea.home.hrajfrisbee.cz/kacerr/egress-proxies-operator/internal/provider" ) // mapState turns status/current's status and qmpstatus plus the discovered // address into the contract's state (spec entry 9). A VM is Running only // with an address; prelaunch is still starting; any other qmpstatus of a // running VM (paused, suspended, io-error, …) and any status not recognised // is Stopped, which the reconciler answers by replacing the VM. An empty // qmpstatus is taken as running: the field is missing, not a state. func mapState(status, qmpStatus, ip string) provider.InstanceState { switch { case guestRunning(status, qmpStatus): if ip == "" { return provider.StateProvisioning } return provider.StateRunning case status == "running" && qmpStatus == "prelaunch": return provider.StateProvisioning default: return provider.StateStopped } } // guestRunning reports a VM whose guest runs, the only state in which its // agent can answer and mapState can call it Running. func guestRunning(status, qmpStatus string) bool { return status == "running" && (qmpStatus == "running" || qmpStatus == "") } // firstIPv4 picks the first usable IPv4 from the guest agent's interfaces, // in the agent's order: not on the loopback interface, not loopback, // link-local or unspecified. Empty means no address yet. func firstIPv4(ifaces []agentInterface) string { for _, iface := range ifaces { if iface.Name == "lo" { continue } for _, a := range iface.IPAddresses { if a.Type != "ipv4" { continue } addr, err := netip.ParseAddr(a.Address) if err != nil || !addr.Is4() || addr.IsLoopback() || addr.IsLinkLocalUnicast() || addr.IsUnspecified() { continue } return addr.String() } } return "" }