proxmox: config block, IDs, marks, state mapping, classify, registration
Phase 2 of the proxmox provider plan. Adds the proxmox: config block with its validation, registers the type in the config allow-list and the constructor map, and starts internal/provider/proxmox: the api seam and its data types, providerIDs (vms/<vmid>/<name>) with the UID-derived VMID, the description mark, state mapping, and error classification. Tests read the responses captured from pv1-1 in Phase 0 rather than repeating Proxmox's wording, and every matcher is run against every captured error so none fires on the wrong message. The contract methods are Transient stubs until Phases 5 to 7. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,52 @@
|
||||
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 {
|
||||
if status != "running" {
|
||||
return provider.StateStopped
|
||||
}
|
||||
switch qmpStatus {
|
||||
case "running", "":
|
||||
if ip == "" {
|
||||
return provider.StateProvisioning
|
||||
}
|
||||
return provider.StateRunning
|
||||
case "prelaunch":
|
||||
return provider.StateProvisioning
|
||||
default:
|
||||
return provider.StateStopped
|
||||
}
|
||||
}
|
||||
|
||||
// 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 ""
|
||||
}
|
||||
Reference in New Issue
Block a user