summaryrefslogtreecommitdiff
path: root/libvirt/domain.go
diff options
context:
space:
mode:
Diffstat (limited to 'libvirt/domain.go')
-rw-r--r--libvirt/domain.go179
1 files changed, 179 insertions, 0 deletions
diff --git a/libvirt/domain.go b/libvirt/domain.go
new file mode 100644
index 00000000..9991dc42
--- /dev/null
+++ b/libvirt/domain.go
@@ -0,0 +1,179 @@
+package libvirt
+
+import (
+ "errors"
+ "fmt"
+ "log"
+ "os"
+ "strings"
+ "time"
+
+ "github.com/davecgh/go-spew/spew"
+ "github.com/hashicorp/terraform/helper/resource"
+ libvirt "github.com/libvirt/libvirt-go"
+ "github.com/libvirt/libvirt-go-xml"
+)
+
+const skipQemuEnvVar = "TF_SKIP_QEMU_AGENT"
+
+const domWaitLeaseStillWaiting = "waiting-addresses"
+const domWaitLeaseDone = "all-addresses-obtained"
+
+var errDomainInvalidState = errors.New("invalid state for domain")
+
+func domainWaitForLeases(domain *libvirt.Domain, waitForLeases map[libvirtxml.DomainInterface]struct{}, timeout time.Duration) error {
+ waitFunc := func() (interface{}, string, error) {
+ state, err := domainGetState(*domain)
+ if err != nil {
+ return false, "", err
+ }
+
+ for _, fatalState := range []string{"crashed", "shutoff", "shutdown", "pmsuspended"} {
+ if state == fatalState {
+ return false, "", errDomainInvalidState
+ }
+ }
+
+ if state != "running" {
+ return false, domWaitLeaseStillWaiting, nil
+ }
+
+ // check we have IPs for all the interfaces we are waiting for
+ for iface := range waitForLeases {
+ found, ignore, err := domainIfaceHasAddress(*domain, iface)
+ if err != nil {
+ return false, "", err
+ }
+ if ignore {
+ log.Printf("[DEBUG] we don't care about the IP address for %+v", iface)
+ continue
+ }
+ if !found {
+ log.Printf("[DEBUG] IP address not found for iface=%+v: will try in a while", strings.ToUpper(iface.MAC.Address))
+ return false, domWaitLeaseStillWaiting, nil
+ }
+ }
+
+ log.Printf("[DEBUG] all the %d IP addresses obtained for the domain", len(waitForLeases))
+ return true, domWaitLeaseDone, nil
+ }
+
+ stateConf := &resource.StateChangeConf{
+ Pending: []string{domWaitLeaseStillWaiting},
+ Target: []string{domWaitLeaseDone},
+ Refresh: waitFunc,
+ Timeout: timeout,
+ MinTimeout: 10 * time.Second,
+ Delay: 5 * time.Second,
+ }
+
+ _, err := stateConf.WaitForState()
+ log.Print("[DEBUG] wait-for-leases was successful")
+ return err
+}
+
+func domainIfaceHasAddress(domain libvirt.Domain, iface libvirtxml.DomainInterface) (found bool, ignore bool, err error) {
+
+ mac := strings.ToUpper(iface.MAC.Address)
+ if mac == "" {
+ log.Printf("[DEBUG] Can't wait without a MAC address: ignoring interface %+v.\n", iface)
+ // we can't get the ip without a mac address
+ return false, true, nil
+ }
+
+ log.Printf("[DEBUG] waiting for network address for iface=%s\n", mac)
+ ifacesWithAddr, err := domainGetIfacesInfo(domain)
+ if err != nil {
+ return false, false, fmt.Errorf("Error retrieving interface addresses: %s", err)
+ }
+ log.Printf("[DEBUG] ifaces with addresses: %+v\n", ifacesWithAddr)
+
+ for _, ifaceWithAddr := range ifacesWithAddr {
+ if mac == strings.ToUpper(ifaceWithAddr.Hwaddr) {
+ log.Printf("[DEBUG] found IPs for MAC=%+v: %+v\n", mac, ifaceWithAddr.Addrs)
+ return true, false, nil
+ }
+ }
+
+ log.Printf("[DEBUG] %+v doesn't have IP address(es) yet...\n", mac)
+ return false, false, nil
+}
+
+func domainGetState(domain libvirt.Domain) (string, error) {
+ state, _, err := domain.GetState()
+ if err != nil {
+ return "", err
+ }
+
+ var stateStr string
+
+ switch state {
+ case libvirt.DOMAIN_NOSTATE:
+ stateStr = "nostate"
+ case libvirt.DOMAIN_RUNNING:
+ stateStr = "running"
+ case libvirt.DOMAIN_BLOCKED:
+ stateStr = "blocked"
+ case libvirt.DOMAIN_PAUSED:
+ stateStr = "paused"
+ case libvirt.DOMAIN_SHUTDOWN:
+ stateStr = "shutdown"
+ case libvirt.DOMAIN_CRASHED:
+ stateStr = "crashed"
+ case libvirt.DOMAIN_PMSUSPENDED:
+ stateStr = "pmsuspended"
+ case libvirt.DOMAIN_SHUTOFF:
+ stateStr = "shutoff"
+ default:
+ stateStr = fmt.Sprintf("unknown: %v", state)
+ }
+
+ return stateStr, nil
+}
+
+func domainIsRunning(domain libvirt.Domain) (bool, error) {
+ state, _, err := domain.GetState()
+ if err != nil {
+ return false, fmt.Errorf("Couldn't get state of domain: %s", err)
+ }
+
+ return state == libvirt.DOMAIN_RUNNING, nil
+}
+
+func domainGetIfacesInfo(domain libvirt.Domain) ([]libvirt.DomainInterface, error) {
+
+ _, found := os.LookupEnv(skipQemuEnvVar)
+ if found {
+ log.Printf("[DEBUG] %s defined in environment: skipping qemu-agent", skipQemuEnvVar)
+ } else {
+ // get all the interfaces using the qemu-agent, this includes also
+ // interfaces that are not attached to networks managed by libvirt
+ // (eg. bridges, macvtap,...)
+ log.Print("[DEBUG] fetching networking interfaces using qemu-agent")
+ interfaces := qemuAgentGetInterfacesInfo(&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
+ }
+ }
+
+ // get all the interfaces attached to libvirt networks
+ log.Print("[DEBUG] no interfaces could be obtained with qemu-agent: falling back to the libvirt API")
+ interfaces, err := domain.ListAllInterfaceAddresses(libvirt.DOMAIN_INTERFACE_ADDRESSES_SRC_LEASE)
+ if err != nil {
+ switch err.(type) {
+ default:
+ return interfaces, fmt.Errorf("Error retrieving interface addresses: %s", err)
+ case libvirt.Error:
+ virErr := err.(libvirt.Error)
+ if virErr.Code != libvirt.ERR_OPERATION_INVALID || virErr.Domain != libvirt.FROM_QEMU {
+ return interfaces, fmt.Errorf("Error retrieving interface addresses: %s", err)
+ }
+ }
+ }
+ log.Printf("[DEBUG] Interfaces info obtained with libvirt API:\n%s\n", spew.Sdump(interfaces))
+
+ return interfaces, nil
+}