diff options
author | Flavio Castelli <fcastelli@suse.com> | 2016-07-04 19:03:45 +0200 |
---|---|---|
committer | Flavio Castelli <fcastelli@suse.com> | 2016-07-20 13:11:13 +0200 |
commit | c5661d890f83f2e118f89867a0edf73dc1da282e (patch) | |
tree | fd1e488f769a2c01a318e75ff542216b9f43df3a | |
parent | 0cc42cc992323bcd42db36536227d8616dca3189 (diff) | |
download | terraform-provider-libvirt-c5661d890f83f2e118f89867a0edf73dc1da282e.tar terraform-provider-libvirt-c5661d890f83f2e118f89867a0edf73dc1da282e.tar.gz |
Discover IP address of the network cards attached to a LAN
When a network card is attached to a LAN the `domain.ListAllInterfaceAddresses`
API cannot be used to get the IP address associated with the interfaces.
The only solution to this problem is to use the [Qemu guest agent](http://wiki.libvirt.org/page/Qemu_guest_agent)
to fetch this information.
This PR will take advantage of the qemu guest ageint (when installed
inside of the domain) to workaround the limitations of the
`domain.ListAllInterfaceAddresses`.
In theory this PR could also allow to relax the required version of
libvirt.
Signed-off-by: Flavio Castelli <fcastelli@suse.com>
-rw-r--r-- | docs/providers/libvirt/r/domain.html.markdown | 4 | ||||
-rw-r--r-- | libvirt/qemu_agent.go | 90 | ||||
-rw-r--r-- | libvirt/resource_libvirt_domain.go | 35 |
3 files changed, 128 insertions, 1 deletions
diff --git a/docs/providers/libvirt/r/domain.html.markdown b/docs/providers/libvirt/r/domain.html.markdown index 752d55bd..b4a3624e 100644 --- a/docs/providers/libvirt/r/domain.html.markdown +++ b/docs/providers/libvirt/r/domain.html.markdown @@ -120,3 +120,7 @@ resource "libvirt_domain" "my-domain" { } } ``` + +**Warning:** the [Qemu guest agent](http://wiki.libvirt.org/page/Qemu_guest_agent) +must be installed and running inside of the domain in order to discover the IP +addresses of all the network interfaces attached to a LAN. diff --git a/libvirt/qemu_agent.go b/libvirt/qemu_agent.go new file mode 100644 index 00000000..10bf45cc --- /dev/null +++ b/libvirt/qemu_agent.go @@ -0,0 +1,90 @@ +package libvirt + +import ( + "encoding/json" + "log" + "strings" + + libvirt "github.com/dmacvicar/libvirt-go" +) + +type QemuAgentInterfacesResponse struct { + Interfaces []QemuAgentInterface `json:"return"` +} + +type QemuAgentInterface struct { + Name string `json:"name"` + Hwaddr string `json:"hardware-address"` + IpAddresses []struct { + Type string `json:"ip-address-type"` + Address string `json:"ip-address"` + Prefix uint `json:"prefix"` + } `json:"ip-addresses"` +} + +// Retrieve all the interfaces attached to a domain and their addresses. Only +// the interfaces with at least an IP address are returned. +// When wait4ipv4 is turned on the code will not report interfaces that don't +// have a ipv4 address set. This is useful when a domain gets the ipv6 address +// before the ipv4 one. +func getDomainInterfacesViaQemuAgent(domain *libvirt.VirDomain, wait4ipv4 bool) []libvirt.VirDomainInterface { + log.Print("[DEBUG] get network interfaces using qemu agent") + + var interfaces []libvirt.VirDomainInterface + + result := domain.QemuAgentCommand( + "{\"execute\":\"guest-network-get-interfaces\"}", + libvirt.VIR_DOMAIN_QEMU_AGENT_COMMAND_DEFAULT, + 0) + + log.Printf("[DEBUG] qemu-agent response: %s", result) + + response := QemuAgentInterfacesResponse{} + if err := json.Unmarshal([]byte(result), &response); err != nil { + log.Printf("[DEBUG] Error converting Qemu agent response about domain interfaces: %s", err) + log.Printf("[DEBUG] Original message: %s", response) + log.Print("[DEBUG] Returning an empty list of interfaces") + return interfaces + } + log.Printf("[DEBUG] Parsed response %+v", response) + + for _, iface := range response.Interfaces { + if iface.Name == "lo" { + // ignore loopback interface + continue + } + + libVirtIface := libvirt.VirDomainInterface{} + libVirtIface.Name = iface.Name + libVirtIface.Hwaddr = iface.Hwaddr + + ipv4Assigned := false + for _, addr := range iface.IpAddresses { + if addr.Address == "" { + // ignore interfaces without an address (eg. waiting for dhcp lease) + continue + } + + libVirtAddr := libvirt.VirDomainIPAddress{} + libVirtAddr.Addr = addr.Address + libVirtAddr.Prefix = addr.Prefix + switch strings.ToLower(addr.Type) { + case "ipv4": + libVirtAddr.Type = libvirt.VIR_IP_ADDR_TYPE_IPV4 + ipv4Assigned = true + case "ipv6": + libVirtAddr.Type = libvirt.VIR_IP_ADDR_TYPE_IPV6 + default: + log.Printf("[ERROR] Cannot handle unknown address type %s", addr.Type) + } + libVirtIface.Addrs = append(libVirtIface.Addrs, libVirtAddr) + } + if len(libVirtIface.Addrs) > 0 && (ipv4Assigned || !wait4ipv4) { + interfaces = append(interfaces, libVirtIface) + } + } + + log.Printf("[DEBUG] Interfaces obtained via qemu Agent: %+v", interfaces) + + return interfaces +} diff --git a/libvirt/resource_libvirt_domain.go b/libvirt/resource_libvirt_domain.go index e1fc7dc6..bbf0a110 100644 --- a/libvirt/resource_libvirt_domain.go +++ b/libvirt/resource_libvirt_domain.go @@ -692,7 +692,7 @@ func waitForNetworkAddresses(ifaces []defNetworkInterface, domain libvirt.VirDom waitLoop: for { log.Printf("[DEBUG] waiting for network address for interface with hwaddr: '%s'\n", iface.Mac.Address) - ifacesWithAddr, err := domain.ListAllInterfaceAddresses(libvirt.VIR_DOMAIN_INTERFACE_ADDRESSES_SRC_LEASE) + ifacesWithAddr, err := getDomainInterfaces(&domain) if err != nil { return fmt.Errorf("Error retrieving interface addresses: %s", err) } @@ -749,3 +749,36 @@ func newDiskForCloudInit(virConn *libvirt.VirConnection, volumeKey string) (defD return disk, nil } + +func getDomainInterfaces(domain *libvirt.VirDomain) ([]libvirt.VirDomainInterface, error) { + + // get all the interfaces using the qemu-agent, this includes also + // interfaces that are not attached to networks managed by libvirt + // (eg. bridges, macvtap,...) + interfaces := getDomainInterfacesViaQemuAgent(domain, true) + if len(interfaces) > 0 { + // the agent will always return all the interfaces, both the + // ones managed by libvirt and the ones attached to bridge interfaces + // or macvtap. Hence it has the highest priority + return interfaces, nil + } + + log.Print("[DEBUG] fetching networking interfaces using libvirt API") + + // get all the interfaces attached to libvirt networks + interfaces, err := domain.ListAllInterfaceAddresses(libvirt.VIR_DOMAIN_INTERFACE_ADDRESSES_SRC_LEASE) + if err != nil { + switch err.(type) { + default: + return interfaces, fmt.Errorf("Error retrieving interface addresses: %s", err) + case libvirt.VirError: + virErr := err.(libvirt.VirError) + if virErr.Code != libvirt.VIR_ERR_OPERATION_INVALID || virErr.Domain != libvirt.VIR_FROM_QEMU { + return interfaces, fmt.Errorf("Error retrieving interface addresses: %s", err) + } + } + } + log.Printf("[DEBUG] Interfaces %+v", interfaces) + + return interfaces, nil +} |