diff options
author | Duncan Mac-Vicar P <dmacvicar@suse.de> | 2016-05-22 01:16:49 +0200 |
---|---|---|
committer | Duncan Mac-Vicar P <dmacvicar@suse.de> | 2016-05-22 01:16:49 +0200 |
commit | c1ee126e821b1a13aca72e3380f3873f3bc1d9a0 (patch) | |
tree | e3462df027282097dd80d16beff4daa3f07b2b74 | |
parent | 751af6b6bb99a5e805e2fe5753359812983a06ae (diff) | |
parent | 9edf9cbfd7013b6d7ffed798bed31fda6a84bbc8 (diff) | |
download | terraform-provider-libvirt-c1ee126e821b1a13aca72e3380f3873f3bc1d9a0.tar terraform-provider-libvirt-c1ee126e821b1a13aca72e3380f3873f3bc1d9a0.tar.gz |
Adds the ip address to the schema and enable the connect/ssh for provisioners.
Closes #18.
Merge branch 'connect_ip_address'
-rw-r--r-- | docs/providers/libvirt/r/domain.html.markdown | 2 | ||||
-rw-r--r-- | libvirt/network_interface_def.go | 41 | ||||
-rw-r--r-- | libvirt/resource_libvirt_domain.go | 99 | ||||
-rw-r--r-- | libvirt/utils.go | 16 |
4 files changed, 154 insertions, 4 deletions
diff --git a/docs/providers/libvirt/r/domain.html.markdown b/docs/providers/libvirt/r/domain.html.markdown index 36c5216e..22da6dda 100644 --- a/docs/providers/libvirt/r/domain.html.markdown +++ b/docs/providers/libvirt/r/domain.html.markdown @@ -61,4 +61,4 @@ The `network_interface` block supports: * `mac` - (Optional) The specific MAC address to use for this interface. * `network` - (Optional) The network to attach this interface to. - +* `wait_for_lease`- (Optional) When creating the domain resource, wait until the network interface gets a DHCP lease from libvirt, so that the computed ip addresses will be available when the domain is up and the plan applied. diff --git a/libvirt/network_interface_def.go b/libvirt/network_interface_def.go index 4e401a6f..5043ac24 100644 --- a/libvirt/network_interface_def.go +++ b/libvirt/network_interface_def.go @@ -9,8 +9,8 @@ type defNetworkInterface struct { XMLName xml.Name `xml:"interface"` Type string `xml:"type,attr"` Mac struct { - Address string `xml:"address,attr,omitempty"` - } `xml:"mac,omitempty"` + Address string `xml:"address,attr"` + } `xml:"mac"` Source struct { Network string `xml:"network,attr"` } `xml:"source"` @@ -19,6 +19,26 @@ type defNetworkInterface struct { } `xml:"model"` } +func networkAddressCommonSchema() map[string]*schema.Schema { + return map[string]*schema.Schema{ + "type": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "address": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "prefix": &schema.Schema{ + Type: schema.TypeInt, + Optional: true, + Computed: true, + }, + } +} + func networkInterfaceCommonSchema() map[string]*schema.Schema { return map[string]*schema.Schema{ "network": &schema.Schema{ @@ -30,6 +50,23 @@ func networkInterfaceCommonSchema() map[string]*schema.Schema { Type: schema.TypeString, Optional: true, ForceNew: true, + DefaultFunc: func() (interface{}, error) { + return RandomMACAddress() + }, + }, + "wait_for_lease": &schema.Schema{ + Type: schema.TypeBool, + Optional: true, + Default: false, + ForceNew: true, + }, + "address": &schema.Schema{ + Type: schema.TypeList, + Optional: true, + Computed: true, + Elem: &schema.Resource{ + Schema: networkAddressCommonSchema(), + }, }, } } diff --git a/libvirt/resource_libvirt_domain.go b/libvirt/resource_libvirt_domain.go index 095ebab7..b24b5cf0 100644 --- a/libvirt/resource_libvirt_domain.go +++ b/libvirt/resource_libvirt_domain.go @@ -147,6 +147,11 @@ func resourceLibvirtDomainCreate(d *schema.ResourceData, meta interface{}) error return fmt.Errorf("Error waiting for domain to reach RUNNING state: %s", err) } + err = waitForNetworkAddresses(d, domain) + if err != nil { + return err + } + return resourceLibvirtDomainRead(d, meta) } @@ -203,16 +208,62 @@ func resourceLibvirtDomainRead(d *schema.ResourceData, meta interface{}) error { } d.Set("disks", disks) + // look interfaces with addresses + ifacesWithAddr, err := domain.ListAllInterfaceAddresses(libvirt.VIR_DOMAIN_INTERFACE_ADDRESSES_SRC_LEASE) + if err != nil { + return fmt.Errorf("Error retrieving interface addresses: %s", err) + } + netIfaces := make([]map[string]interface{}, 0) for _, networkInterfaceDef := range domainDef.Devices.NetworkInterfaces { + + if networkInterfaceDef.Type != "network" { + log.Printf("[DEBUG] ignoring interface of type '%s'", networkInterfaceDef.Type) + continue + } + netIface := map[string]interface{}{ "network": networkInterfaceDef.Source.Network, "mac": networkInterfaceDef.Mac.Address, } + + netIfaceAddrs := make([]map[string]interface{}, 0) + // look for an ip address and try to match it with the mac address + // not sure if using the target device name is a better idea here + for _, ifaceWithAddr := range ifacesWithAddr { + if ifaceWithAddr.Hwaddr == networkInterfaceDef.Mac.Address { + for _, addr := range ifaceWithAddr.Addrs { + netIfaceAddr := map[string]interface{}{ + "type": func() string { + switch addr.Type { + case libvirt.VIR_IP_ADDR_TYPE_IPV4: + return "ipv4" + case libvirt.VIR_IP_ADDR_TYPE_IPV6: + return "ipv6" + default: + return "other" + } + }(), + "address": addr.Addr, + "prefix": addr.Prefix, + } + netIfaceAddrs = append(netIfaceAddrs, netIfaceAddr) + } + } + } + + log.Printf("[DEBUG] %d addresses for %s\n", len(netIfaceAddrs), networkInterfaceDef.Mac.Address) + netIface["address"] = netIfaceAddrs netIfaces = append(netIfaces, netIface) } - d.Set("network_interfaces", netIfaces) + d.Set("network_interface", netIfaces) + if len(ifacesWithAddr) > 0 { + d.SetConnInfo(map[string]string{ + "type": "ssh", + "host": ifacesWithAddr[0].Addrs[0].Addr, + }) + } return nil } @@ -280,3 +331,49 @@ func waitForDomainDestroyed(virConn *libvirt.VirConnection, uuid string) error { } } } + +func waitForNetworkAddresses(d *schema.ResourceData, domain libvirt.VirDomain) error { + log.Printf("[DEBUG] waiting for network addresses.\n") + // wait for network interfaces with 'wait_for_lease' to get an address + netIfacesCount := d.Get("network_interface.#").(int) + for i := 0; i < netIfacesCount; i++ { + prefix := fmt.Sprintf("network_interface.%d", i) + + if wait, ok := d.GetOk(prefix + ".wait_for_lease"); !ok || !wait.(bool) { + continue + } + + var mac string + if v, ok := d.GetOk(prefix + ".mac"); !ok { + // we can't get the ip without a mac address + continue + } else { + mac = v.(string) + } + log.Printf("[DEBUG] waiting for network addresses on %s\n", mac) + + // loop until address appear, with timeout + start := time.Now() + waitLoop: + for { + ifacesWithAddr, err := domain.ListAllInterfaceAddresses(libvirt.VIR_DOMAIN_INTERFACE_ADDRESSES_SRC_LEASE) + if err != nil { + return fmt.Errorf("Error retrieving interface addresses: %s", err) + } + + for _, ifaceWithAddr := range ifacesWithAddr { + // found + if mac == ifaceWithAddr.Hwaddr { + break waitLoop + } + } + + time.Sleep(1 * time.Second) + if time.Since(start) > 5*time.Minute { + return fmt.Errorf("Timeout waiting for interface addresses") + } + } + } + + return nil +} diff --git a/libvirt/utils.go b/libvirt/utils.go index 3dc8e17e..ffaa8ba6 100644 --- a/libvirt/utils.go +++ b/libvirt/utils.go @@ -1,6 +1,7 @@ package libvirt import ( + "crypto/rand" "fmt" "time" ) @@ -35,3 +36,18 @@ func WaitForSuccess(errorMessage string, f func() error) error { } } } + +func RandomMACAddress() (string, error) { + buf := make([]byte, 6) + _, err := rand.Read(buf) + if err != nil { + return "", err + } + + // set local bit and unicast + buf[0] = (buf[0] | 2) & 0xfe + // Set the local bit + buf[0] |= 2 + + return fmt.Sprintf("%02x:%02x:%02x:%02x:%02x:%02x", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]), nil +} |