summaryrefslogtreecommitdiff
path: root/libvirt/qemu_agent.go
blob: 7f3ca7a40a9e363f529ec5890396683a7630db14 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
package libvirt

import (
	"encoding/json"
	"log"
	"strings"
	"time"

	"github.com/hashicorp/terraform/helper/resource"
	libvirt "github.com/libvirt/libvirt-go"
)

const qemuGetIfaceWait = "qemu-agent-wait"
const qemuGetIfaceDone = "qemu-agent-done"

// QemuAgentInterfacesResponse type
type QemuAgentInterfacesResponse struct {
	Interfaces []QemuAgentInterface `json:"return"`
}

// QemuAgentInterface type
type QemuAgentInterface struct {
	Name        string                        `json:"name"`
	Hwaddr      string                        `json:"hardware-address"`
	IPAddresses []QemuAgentInterfaceIPAddress `json:"ip-addresses"`
}

// QemuAgentInterfaceIPAddress type
type QemuAgentInterfaceIPAddress struct {
	Type    string `json:"ip-address-type"`
	Address string `json:"ip-address"`
	Prefix  uint   `json:"prefix"`
}

func qemuAgentInterfacesRefreshFunc(domain Domain, wait4ipv4 bool) resource.StateRefreshFunc {
	return func() (interface{}, string, error) {

		var interfaces []libvirt.DomainInterface

		log.Printf("[DEBUG] sending command to qemu-agent")
		result, err := domain.QemuAgentCommand(
			"{\"execute\":\"guest-network-get-interfaces\"}",
			libvirt.DOMAIN_QEMU_AGENT_COMMAND_DEFAULT,
			0)
		if err != nil {
			log.Printf("[DEBUG] command error: %s", err)
			return interfaces, qemuGetIfaceWait, nil
		}

		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: %+v", response)
			log.Print("[DEBUG] Returning an empty list of interfaces")
			return interfaces, "", nil
		}
		log.Printf("[DEBUG] Parsed response %+v", response)

		for _, iface := range response.Interfaces {
			if iface.Name == "lo" {
				// ignore loopback interface
				continue
			}

			libVirtIface := libvirt.DomainInterface{
				Name:   iface.Name,
				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.DomainIPAddress{
					Addr:   addr.Address,
					Prefix: addr.Prefix,
				}

				switch strings.ToLower(addr.Type) {
				case "ipv4":
					libVirtAddr.Type = int(libvirt.IP_ADDR_TYPE_IPV4)
					ipv4Assigned = true
				case "ipv6":
					libVirtAddr.Type = int(libvirt.IP_ADDR_TYPE_IPV6)
				default:
					log.Printf("[ERROR] Cannot handle unknown address type %s", addr.Type)
					continue
				}
				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, qemuGetIfaceDone, nil
	}
}

// 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 qemuAgentGetInterfacesInfo(domain Domain, wait4ipv4 bool) []libvirt.DomainInterface {

	qemuAgentQuery := &resource.StateChangeConf{
		Pending:    []string{qemuGetIfaceWait},
		Target:     []string{qemuGetIfaceDone},
		Refresh:    qemuAgentInterfacesRefreshFunc(domain, wait4ipv4),
		MinTimeout: 4 * time.Second,
		Delay:      4 * time.Second, // Wait this time before starting checks
		Timeout:    30 * time.Second,
	}

	interfaces, err := qemuAgentQuery.WaitForState()
	if err != nil {
		return []libvirt.DomainInterface{}
	}

	return interfaces.([]libvirt.DomainInterface)
}