/
hyperv_wmi.go
235 lines (204 loc) · 5.79 KB
/
hyperv_wmi.go
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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
// +build windows
package hyperv
import (
"net"
"regexp"
"strconv"
"strings"
"time"
"github.com/StackExchange/wmi"
"github.com/sirupsen/logrus"
"github.com/cloudradar-monitoring/cagent/pkg/common"
)
var guestNetworkRegexp *regexp.Regexp
func init() {
guestNetworkRegexp = regexp.MustCompile(`^Microsoft:GuestNetwork\\(\w{8}-\w{4}-\w{4}-\w{4}-\w{12})\\(\w{8}-\w{4}-\w{4}-\w{4}-\w{12})$`)
}
type vmStat struct {
InstanceName string
CPUWaitTimePerDispatch float64
State string
CPUUsagePercent float32
AssignedMemory string
Uptime string
IPAddresses []string
Heartbeat string
GUID string
}
type EnabledState uint16
type HealthState uint16
type Heartbeat uint16
type msvm_ComputerSystem struct {
ElementName string
InstallDate string
HealthState HealthState
EnabledState EnabledState
OtherEnabledState string
RequestedState EnabledState
TimeOfLastStateChange string
Name string
ProcessID uint32
}
// some of fields are pointers instead of values as in some cases they may not exists
// during WMI query. For example if VM is not running MemoryUsage, ProcessorLoad and Heartbeat
// are not available
type msvm_SummaryInformation struct {
Name string
ElementName string
GuestOperatingSystem *string
Version string
EnabledState EnabledState
HealthState HealthState
CreationTime time.Time
// MemoryUsage is in megabytes
MemoryUsage *uint64
Uptime uint64
ProcessorLoad *uint16
Heartbeat *Heartbeat
NumberOfProcessors uint16
}
type msvm_GuestNetworkAdapterConfiguration struct {
InstanceID string
IPAddresses []string
}
type hypervStat struct {
TotalCPUWaitTimePerDispatch float64
List map[string]vmStat
}
var hypervPath = "\\Hyper-V Hypervisor Virtual processor(*)\\CPU Wait Time Per Dispatch"
func (im *impl) GetMeasurements() (common.MeasurementsMap, error) {
meas := make(common.MeasurementsMap)
stat := &hypervStat{
List: make(map[string]vmStat),
}
var countersErr error
res, countersErr := im.watcher.GetFormattedQueryData(hypervPath)
if countersErr == nil {
for _, c := range res {
switch c.InstanceName {
case "_Total":
meas["cpu_wait_time_per_dispatch_total_ns"] = int64(c.Value)
default:
name := strings.Split(c.InstanceName, ":")[0]
stat.List[name] = vmStat{
InstanceName: name,
CPUWaitTimePerDispatch: c.Value,
}
}
}
} else {
logrus.Errorf("[vmstat:hyperv] couldn't fetch perfcounters: %s", countersErr.Error())
}
var list []interface{}
ipMap := make(map[string][]string)
var q string
var ips []msvm_GuestNetworkAdapterConfiguration
q = wmi.CreateQuery(&ips, "")
if err := wmi.QueryNamespace(q, &ips, `root\virtualization\v2`); err == nil {
for i := range ips {
// InstanceID is in form of Microsoft:GuestNetwork\GUID\GUID
// First GUID matches field Name in struct msvm_SummaryInformation
if !guestNetworkRegexp.Match([]byte(ips[i].InstanceID)) {
logrus.Warnf("[vmstat:hyperv] invalid InstanceID in hyper-v response. expected form:"+
"[Microsoft:GuestNetwork\\<GUID>\\<GUID>] actual form: [%s]", ips[i].InstanceID)
} else {
id := guestNetworkRegexp.FindAllStringSubmatch(ips[i].InstanceID, -1)
ipMap[id[0][1]] = ips[i].IPAddresses
}
}
} else {
logrus.Errorf("[vmstat:hyperv] query : %s", err.Error())
}
var dst []msvm_SummaryInformation
q = wmi.CreateQuery(&dst, "")
if err := wmi.QueryNamespace(q, &dst, `root\virtualization\v2`); err != nil {
logrus.Errorf("[vmstat:hyperv] couldn't query vms: %s", err.Error())
} else {
for i := range dst {
vmEntry := make(map[string]interface{})
// include perfcounters only when perf has data
if countersErr == nil {
vmEntry["cpu_wait_time_per_dispatch_ns"] = int64(stat.List[dst[i].ElementName].CPUWaitTimePerDispatch)
}
vmEntry["name"] = dst[i].ElementName
vmEntry["guid"] = dst[i].Name
vmEntry["guest_operating_system"] = dst[i].GuestOperatingSystem
vmEntry["version"] = dst[i].Version
vmEntry["enabled_state"] = dst[i].EnabledState.String()
vmEntry["health_state"] = dst[i].HealthState.String()
vmEntry["creation_time"] = dst[i].CreationTime
vmEntry["heartbeat"] = dst[i].Heartbeat.String()
vmEntry["number_of_processors"] = dst[i].NumberOfProcessors
vmEntry["uptime_s"] = dst[i].Uptime
if dst[i].MemoryUsage != nil {
vmEntry["assigned_memory_B"] = *dst[i].MemoryUsage * 0x100000
}
if dst[i].ProcessorLoad != nil {
vmEntry["processor_load_percent"] = dst[i].ProcessorLoad
}
ipv4Count := 1
ipv6Count := 1
for j := range ipMap[dst[i].Name] {
ip := net.ParseIP(ipMap[dst[i].Name][j])
if p4 := ip.To4(); len(p4) == net.IPv4len {
vmEntry["ipv4."+strconv.Itoa(ipv4Count)] = ipMap[dst[i].Name][j]
ipv4Count++
} else {
vmEntry["ipv6."+strconv.Itoa(ipv6Count)] = ipMap[dst[i].Name][j]
ipv6Count++
}
}
list = append(list, vmEntry)
}
}
meas["list"] = list
return meas, nil
}
func (st EnabledState) String() string {
switch st {
case 2:
return "running"
case 3:
return "shutdown"
case 4:
return "shutting down"
case 6:
return "offline"
case 7:
return "in test"
case 8:
return "deferred"
case 9:
return "quiesce"
case 10:
return "starting"
default:
return "unknown"
}
}
func (st HealthState) String() string {
switch st {
case 5:
return "ok"
case 20:
return "major failure"
case 25:
return "critical failure"
default:
return "unknown"
}
}
func (st Heartbeat) String() string {
switch st {
case 2:
return "ok"
case 6:
return "error"
case 12:
return "no contact"
case 13:
return "lost communication"
default:
return "unknown"
}
}