summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFlavio Castelli <fcastelli@suse.com>2016-07-04 19:03:45 +0200
committerFlavio Castelli <fcastelli@suse.com>2016-07-20 13:11:13 +0200
commitc5661d890f83f2e118f89867a0edf73dc1da282e (patch)
treefd1e488f769a2c01a318e75ff542216b9f43df3a
parent0cc42cc992323bcd42db36536227d8616dca3189 (diff)
downloadterraform-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.markdown4
-rw-r--r--libvirt/qemu_agent.go90
-rw-r--r--libvirt/resource_libvirt_domain.go35
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
+}