diff options
author | Thomas Hipp <thipp@suse.de> | 2017-04-04 16:40:13 +0200 |
---|---|---|
committer | Thomas Hipp <thipp@suse.de> | 2017-06-07 08:47:15 +0200 |
commit | 88145c1cc8ad8c977cb74792de37cce98ad349c3 (patch) | |
tree | 98300b9694e44bef82ecb61b4a25b1002e84ad00 /libvirt | |
parent | 527fbf5b82d4baea0f26571d29da0cc20fad6ea7 (diff) | |
download | terraform-provider-libvirt-88145c1cc8ad8c977cb74792de37cce98ad349c3.tar terraform-provider-libvirt-88145c1cc8ad8c977cb74792de37cce98ad349c3.tar.gz |
domain: add configurable timeouts
Terraform supports configurable timeouts since v0.9.0.
With time commit, it's possible to configure the timeout for the
creation of a libvirt domain. It defaults to 5 minutes, but can be as
short as 10 seconds.
Signed-off-by: Thomas Hipp <thipp@suse.de>
Diffstat (limited to 'libvirt')
-rw-r--r-- | libvirt/resource_libvirt_domain.go | 165 |
1 files changed, 109 insertions, 56 deletions
diff --git a/libvirt/resource_libvirt_domain.go b/libvirt/resource_libvirt_domain.go index 170946c1..78e221ce 100644 --- a/libvirt/resource_libvirt_domain.go +++ b/libvirt/resource_libvirt_domain.go @@ -13,9 +13,15 @@ import ( "github.com/davecgh/go-spew/spew" libvirt "github.com/dmacvicar/libvirt-go" + "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/helper/schema" ) +type DomainMeta struct { + domain *libvirt.VirDomain + ifaces chan defNetworkInterface +} + var PoolSync = NewLibVirtPoolSync() func init() { @@ -34,6 +40,9 @@ func resourceLibvirtDomain() *schema.Resource { Delete: resourceLibvirtDomainDelete, Update: resourceLibvirtDomainUpdate, Exists: resourceLibvirtDomainExists, + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(5 * time.Minute), + }, Schema: map[string]*schema.Schema{ "name": &schema.Schema{ Type: schema.TypeString, @@ -272,7 +281,7 @@ func resourceLibvirtDomainCreate(d *schema.ResourceData, meta interface{}) error log.Printf("[DEBUG] scsiDisk: %s", scsiDisk) if scsiDisk { - controller := defController{Type:"scsi", Model:"virtio-scsi"} + controller := defController{Type: "scsi", Model: "virtio-scsi"} domainDef.Devices.Controller = append(domainDef.Devices.Controller, controller) } @@ -449,12 +458,26 @@ func resourceLibvirtDomainCreate(d *schema.ResourceData, meta interface{}) error log.Printf("[INFO] Domain ID: %s", d.Id()) - err = waitForDomainUp(domain) - if err != nil { - return fmt.Errorf("Error waiting for domain to reach RUNNING state: %s", err) + domainMeta := DomainMeta{ + &domain, + make(chan defNetworkInterface, len(netIfaces)), + } + + // populate interface channels + for _, iface := range netIfaces { + domainMeta.ifaces <- iface + } + + stateConf := &resource.StateChangeConf{ + Pending: []string{"blocked"}, + Target: []string{"running"}, + Refresh: resourceLibvirtDomainStateRefreshFunc(d, &domainMeta), + Timeout: d.Timeout(schema.TimeoutCreate), + MinTimeout: 10 * time.Second, + Delay: 10 * time.Second, } - err = waitForNetworkAddresses(netIfaces, domain) + _, err = stateConf.WaitForState() if err != nil { return err } @@ -825,72 +848,102 @@ func resourceLibvirtDomainDelete(d *schema.ResourceData, meta interface{}) error return nil } -// wait for domain to be up and timeout after 5 minutes. -func waitForDomainUp(domain libvirt.VirDomain) error { - start := time.Now() - for { - state, err := domain.GetState() +func resourceLibvirtDomainStateRefreshFunc( + d *schema.ResourceData, meta interface{}) resource.StateRefreshFunc { + return func() (interface{}, string, error) { + domain := meta.(*DomainMeta).domain + + state, err := getDomainState(*domain) if err != nil { - return err + return nil, "", err } - running := true - if state[0] != libvirt.VIR_DOMAIN_RUNNING { - running = false - } + if state == "running" { + // domain and interface(s) are up, we're done + if len(meta.(*DomainMeta).ifaces) == 0 { + return meta, state, nil + } - if running { - return nil - } - time.Sleep(1 * time.Second) - if time.Since(start) > 5*time.Minute { - return fmt.Errorf("Domain didn't switch to state RUNNING in 5 minutes") + // set state to "blocked" since we still have interfaces to check + state = "blocked" + + iface := <-meta.(*DomainMeta).ifaces + found, ignore, err := hasNetworkAddress(iface, *domain) + if err != nil { + return nil, "", err + } + + if found { + return meta, state, nil + } else if !found && !ignore { + // re-add the interface and deal with it later + meta.(*DomainMeta).ifaces <- iface + } } + + return meta, state, nil } } -func waitForNetworkAddresses(ifaces []defNetworkInterface, domain libvirt.VirDomain) error { - log.Printf("[DEBUG] waiting for network addresses.\n") - // wait for network interfaces with 'wait_for_lease' to get an address - for _, iface := range ifaces { - if !iface.waitForLease { - continue - } - - mac := strings.ToUpper(iface.Mac.Address) - if mac == "" { - log.Printf("[DEBUG] Can't wait without a mac address.\n") - // we can't get the ip without a mac address - continue - } +func hasNetworkAddress(iface defNetworkInterface, + domain libvirt.VirDomain) (found bool, ignore bool, err error) { - // loop until address appear, with timeout - start := time.Now() + if !iface.waitForLease { + return false, true, nil + } - waitLoop: - for { - log.Printf("[DEBUG] waiting for network address for interface with hwaddr: '%s'\n", iface.Mac.Address) - ifacesWithAddr, err := getDomainInterfaces(&domain) - if err != nil { - return fmt.Errorf("Error retrieving interface addresses: %s", err) - } - log.Printf("[DEBUG] ifaces with addresses: %+v\n", ifacesWithAddr) + mac := strings.ToUpper(iface.Mac.Address) + if mac == "" { + log.Printf("[DEBUG] Can't wait without a mac address.\n") + // we can't get the ip without a mac address + return false, true, nil + } - for _, ifaceWithAddr := range ifacesWithAddr { - // found - if mac == strings.ToUpper(ifaceWithAddr.Hwaddr) { - break waitLoop - } - } + log.Printf("[DEBUG] waiting for network address for interface with hwaddr: '%s'\n", iface.Mac.Address) + ifacesWithAddr, err := getDomainInterfaces(&domain) + if err != nil { + return false, false, fmt.Errorf("Error retrieving interface addresses: %s", err) + } + log.Printf("[DEBUG] ifaces with addresses: %+v\n", ifacesWithAddr) - time.Sleep(1 * time.Second) - if time.Since(start) > 5*time.Minute { - return fmt.Errorf("Timeout waiting for interface addresses") - } + for _, ifaceWithAddr := range ifacesWithAddr { + // found + if mac == strings.ToUpper(ifaceWithAddr.Hwaddr) { + return true, false, nil } } - return nil + return false, false, nil +} + +func getDomainState(domain libvirt.VirDomain) (string, error) { + state, err := domain.GetState() + if err != nil { + return "", err + } + + var stateStr string + + switch state[0] { + case libvirt.VIR_DOMAIN_NOSTATE: + stateStr = "nostate" + case libvirt.VIR_DOMAIN_RUNNING: + stateStr = "running" + case libvirt.VIR_DOMAIN_BLOCKED: + stateStr = "blocked" + case libvirt.VIR_DOMAIN_PAUSED: + stateStr = "paused" + case libvirt.VIR_DOMAIN_SHUTDOWN: + stateStr = "shutdown" + case libvirt.VIR_DOMAIN_CRASHED: + stateStr = "crashed" + case libvirt.VIR_DOMAIN_PMSUSPENDED: + stateStr = "pmsuspended" + case libvirt.VIR_DOMAIN_SHUTOFF: + stateStr = "shutoff" + } + + return stateStr, nil } func isDomainRunning(domain libvirt.VirDomain) (bool, error) { |