The kvm2 block reuses KVMConfig; validateKVM gains a block-name variant so messages say kvm2. The package skeleton has a non-dialing New, strict providerID parsing, the state table and a 34-row classify test. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
50 lines
1.5 KiB
Go
50 lines
1.5 KiB
Go
package kvm2
|
|
|
|
import (
|
|
"net/netip"
|
|
|
|
"github.com/digitalocean/go-libvirt"
|
|
|
|
"gitea.home.hrajfrisbee.cz/kacerr/egress-proxies-operator/internal/provider"
|
|
)
|
|
|
|
// mapState turns a libvirt domain state plus the discovered address into the
|
|
// contract's state. nostate means libvirt could not tell, so the next poll
|
|
// decides; an active domain is Running only once it has an address; every
|
|
// inactive state, and anything unknown, is Stopped, which the reconciler
|
|
// answers by replacing the instance.
|
|
func mapState(s libvirt.DomainState, ip string) provider.InstanceState {
|
|
switch {
|
|
case s == libvirt.DomainNostate:
|
|
return provider.StateProvisioning
|
|
case isActive(s) && ip == "":
|
|
return provider.StateProvisioning
|
|
case isActive(s):
|
|
return provider.StateRunning
|
|
default:
|
|
return provider.StateStopped
|
|
}
|
|
}
|
|
|
|
func isActive(s libvirt.DomainState) bool {
|
|
return s == libvirt.DomainRunning || s == libvirt.DomainBlocked
|
|
}
|
|
|
|
// firstIPv4 picks the first usable IPv4 address from the guest's interfaces:
|
|
// not loopback, not link-local, not unspecified. Empty means no address yet.
|
|
func firstIPv4(ifaces []libvirt.DomainInterface) string {
|
|
for _, iface := range ifaces {
|
|
for _, a := range iface.Addrs {
|
|
if libvirt.IPAddrType(a.Type) != libvirt.IPAddrTypeIpv4 {
|
|
continue
|
|
}
|
|
addr, err := netip.ParseAddr(a.Addr)
|
|
if err != nil || !addr.Is4() || addr.IsLoopback() || addr.IsLinkLocalUnicast() || addr.IsUnspecified() {
|
|
continue
|
|
}
|
|
return addr.String()
|
|
}
|
|
}
|
|
return ""
|
|
}
|