498 lines
13 KiB
Go
498 lines
13 KiB
Go
package vsphere
|
|
|
|
import (
|
|
"context"
|
|
"crypto/tls"
|
|
"fmt"
|
|
"regexp"
|
|
"sort"
|
|
"sync"
|
|
"sync/atomic"
|
|
"testing"
|
|
"time"
|
|
"unsafe"
|
|
|
|
"github.com/influxdata/telegraf/internal"
|
|
itls "github.com/influxdata/telegraf/internal/tls"
|
|
"github.com/influxdata/telegraf/testutil"
|
|
"github.com/influxdata/toml"
|
|
"github.com/stretchr/testify/require"
|
|
"github.com/vmware/govmomi/object"
|
|
"github.com/vmware/govmomi/simulator"
|
|
"github.com/vmware/govmomi/vim25/mo"
|
|
"github.com/vmware/govmomi/vim25/types"
|
|
)
|
|
|
|
var configHeader = `
|
|
[agent]
|
|
interval = "10s"
|
|
round_interval = true
|
|
metric_batch_size = 1000
|
|
metric_buffer_limit = 10000
|
|
collection_jitter = "0s"
|
|
flush_interval = "10s"
|
|
flush_jitter = "0s"
|
|
precision = ""
|
|
debug = false
|
|
quiet = false
|
|
logfile = ""
|
|
hostname = ""
|
|
omit_hostname = false
|
|
`
|
|
|
|
func defaultVSphere() *VSphere {
|
|
return &VSphere{
|
|
Log: testutil.Logger{},
|
|
ClusterMetricInclude: []string{
|
|
"cpu.usage.*",
|
|
"cpu.usagemhz.*",
|
|
"mem.usage.*",
|
|
"mem.active.*"},
|
|
ClusterMetricExclude: nil,
|
|
ClusterInclude: []string{"/**"},
|
|
HostMetricInclude: []string{
|
|
"cpu.coreUtilization.average",
|
|
"cpu.costop.summation",
|
|
"cpu.demand.average",
|
|
"cpu.idle.summation",
|
|
"cpu.latency.average",
|
|
"cpu.readiness.average",
|
|
"cpu.ready.summation",
|
|
"cpu.swapwait.summation",
|
|
"cpu.usage.average",
|
|
"cpu.usagemhz.average",
|
|
"cpu.used.summation",
|
|
"cpu.utilization.average",
|
|
"cpu.wait.summation",
|
|
"disk.deviceReadLatency.average",
|
|
"disk.deviceWriteLatency.average",
|
|
"disk.kernelReadLatency.average",
|
|
"disk.kernelWriteLatency.average",
|
|
"disk.numberReadAveraged.average",
|
|
"disk.numberWriteAveraged.average",
|
|
"disk.read.average",
|
|
"disk.totalReadLatency.average",
|
|
"disk.totalWriteLatency.average",
|
|
"disk.write.average",
|
|
"mem.active.average",
|
|
"mem.latency.average",
|
|
"mem.state.latest",
|
|
"mem.swapin.average",
|
|
"mem.swapinRate.average",
|
|
"mem.swapout.average",
|
|
"mem.swapoutRate.average",
|
|
"mem.totalCapacity.average",
|
|
"mem.usage.average",
|
|
"mem.vmmemctl.average",
|
|
"net.bytesRx.average",
|
|
"net.bytesTx.average",
|
|
"net.droppedRx.summation",
|
|
"net.droppedTx.summation",
|
|
"net.errorsRx.summation",
|
|
"net.errorsTx.summation",
|
|
"net.usage.average",
|
|
"power.power.average",
|
|
"storageAdapter.numberReadAveraged.average",
|
|
"storageAdapter.numberWriteAveraged.average",
|
|
"storageAdapter.read.average",
|
|
"storageAdapter.write.average",
|
|
"sys.uptime.latest"},
|
|
HostMetricExclude: nil,
|
|
HostInclude: []string{"/**"},
|
|
VMMetricInclude: []string{
|
|
"cpu.demand.average",
|
|
"cpu.idle.summation",
|
|
"cpu.latency.average",
|
|
"cpu.readiness.average",
|
|
"cpu.ready.summation",
|
|
"cpu.run.summation",
|
|
"cpu.usagemhz.average",
|
|
"cpu.used.summation",
|
|
"cpu.wait.summation",
|
|
"mem.active.average",
|
|
"mem.granted.average",
|
|
"mem.latency.average",
|
|
"mem.swapin.average",
|
|
"mem.swapinRate.average",
|
|
"mem.swapout.average",
|
|
"mem.swapoutRate.average",
|
|
"mem.usage.average",
|
|
"mem.vmmemctl.average",
|
|
"net.bytesRx.average",
|
|
"net.bytesTx.average",
|
|
"net.droppedRx.summation",
|
|
"net.droppedTx.summation",
|
|
"net.usage.average",
|
|
"power.power.average",
|
|
"virtualDisk.numberReadAveraged.average",
|
|
"virtualDisk.numberWriteAveraged.average",
|
|
"virtualDisk.read.average",
|
|
"virtualDisk.readOIO.latest",
|
|
"virtualDisk.throughput.usage.average",
|
|
"virtualDisk.totalReadLatency.average",
|
|
"virtualDisk.totalWriteLatency.average",
|
|
"virtualDisk.write.average",
|
|
"virtualDisk.writeOIO.latest",
|
|
"sys.uptime.latest"},
|
|
VMMetricExclude: nil,
|
|
VMInclude: []string{"/**"},
|
|
DatastoreMetricInclude: []string{
|
|
"disk.used.*",
|
|
"disk.provsioned.*"},
|
|
DatastoreMetricExclude: nil,
|
|
DatastoreInclude: []string{"/**"},
|
|
DatacenterMetricInclude: nil,
|
|
DatacenterMetricExclude: nil,
|
|
DatacenterInclude: []string{"/**"},
|
|
ClientConfig: itls.ClientConfig{InsecureSkipVerify: true},
|
|
|
|
MaxQueryObjects: 256,
|
|
MaxQueryMetrics: 256,
|
|
ObjectDiscoveryInterval: internal.Duration{Duration: time.Second * 300},
|
|
Timeout: internal.Duration{Duration: time.Second * 20},
|
|
ForceDiscoverOnInit: true,
|
|
DiscoverConcurrency: 1,
|
|
CollectConcurrency: 1,
|
|
}
|
|
}
|
|
|
|
func createSim(folders int) (*simulator.Model, *simulator.Server, error) {
|
|
model := simulator.VPX()
|
|
|
|
model.Folder = folders
|
|
model.Datacenter = 2
|
|
//model.App = 1
|
|
|
|
err := model.Create()
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
model.Service.TLS = new(tls.Config)
|
|
|
|
s := model.Service.NewServer()
|
|
return model, s, nil
|
|
}
|
|
|
|
func testAlignUniform(t *testing.T, n int) {
|
|
now := time.Now().Truncate(60 * time.Second)
|
|
info := make([]types.PerfSampleInfo, n)
|
|
values := make([]int64, n)
|
|
for i := 0; i < n; i++ {
|
|
info[i] = types.PerfSampleInfo{
|
|
Timestamp: now.Add(time.Duration(20*i) * time.Second),
|
|
Interval: 20,
|
|
}
|
|
values[i] = 1
|
|
}
|
|
newInfo, newValues := alignSamples(info, values, 60*time.Second)
|
|
require.Equal(t, n/3, len(newInfo), "Aligned infos have wrong size")
|
|
require.Equal(t, n/3, len(newValues), "Aligned values have wrong size")
|
|
for _, v := range newValues {
|
|
require.Equal(t, 1.0, v, "Aligned value should be 1")
|
|
}
|
|
}
|
|
|
|
func TestAlignMetrics(t *testing.T) {
|
|
testAlignUniform(t, 3)
|
|
testAlignUniform(t, 30)
|
|
testAlignUniform(t, 333)
|
|
|
|
// 20s to 60s of 1,2,3,1,2,3... (should average to 2)
|
|
n := 30
|
|
now := time.Now().Truncate(60 * time.Second)
|
|
info := make([]types.PerfSampleInfo, n)
|
|
values := make([]int64, n)
|
|
for i := 0; i < n; i++ {
|
|
info[i] = types.PerfSampleInfo{
|
|
Timestamp: now.Add(time.Duration(20*i) * time.Second),
|
|
Interval: 20,
|
|
}
|
|
values[i] = int64(i%3 + 1)
|
|
}
|
|
newInfo, newValues := alignSamples(info, values, 60*time.Second)
|
|
require.Equal(t, n/3, len(newInfo), "Aligned infos have wrong size")
|
|
require.Equal(t, n/3, len(newValues), "Aligned values have wrong size")
|
|
for _, v := range newValues {
|
|
require.Equal(t, 2.0, v, "Aligned value should be 2")
|
|
}
|
|
}
|
|
|
|
func TestParseConfig(t *testing.T) {
|
|
v := VSphere{}
|
|
c := v.SampleConfig()
|
|
p := regexp.MustCompile("\n#")
|
|
fmt.Printf("Source=%s", p.ReplaceAllLiteralString(c, "\n"))
|
|
c = configHeader + "\n[[inputs.vsphere]]\n" + p.ReplaceAllLiteralString(c, "\n")
|
|
fmt.Printf("Source=%s", c)
|
|
tab, err := toml.Parse([]byte(c))
|
|
require.NoError(t, err)
|
|
require.NotNil(t, tab)
|
|
}
|
|
|
|
func TestThrottledExecutor(t *testing.T) {
|
|
max := int64(0)
|
|
ngr := int64(0)
|
|
n := 10000
|
|
var mux sync.Mutex
|
|
results := make([]int, 0, n)
|
|
te := NewThrottledExecutor(5)
|
|
for i := 0; i < n; i++ {
|
|
func(i int) {
|
|
te.Run(context.Background(), func() {
|
|
atomic.AddInt64(&ngr, 1)
|
|
mux.Lock()
|
|
defer mux.Unlock()
|
|
results = append(results, i*2)
|
|
if ngr > max {
|
|
max = ngr
|
|
}
|
|
time.Sleep(100 * time.Microsecond)
|
|
atomic.AddInt64(&ngr, -1)
|
|
})
|
|
}(i)
|
|
}
|
|
te.Wait()
|
|
sort.Ints(results)
|
|
for i := 0; i < n; i++ {
|
|
require.Equal(t, results[i], i*2, "Some jobs didn't run")
|
|
}
|
|
require.Equal(t, int64(5), max, "Wrong number of goroutines spawned")
|
|
}
|
|
|
|
func TestMaxQuery(t *testing.T) {
|
|
// Don't run test on 32-bit machines due to bug in simulator.
|
|
// https://github.com/vmware/govmomi/issues/1330
|
|
var i int
|
|
if unsafe.Sizeof(i) < 8 {
|
|
return
|
|
}
|
|
m, s, err := createSim(0)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer m.Remove()
|
|
defer s.Close()
|
|
|
|
v := defaultVSphere()
|
|
v.MaxQueryMetrics = 256
|
|
ctx := context.Background()
|
|
c, err := NewClient(ctx, s.URL, v)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
require.Equal(t, 256, v.MaxQueryMetrics)
|
|
|
|
om := object.NewOptionManager(c.Client.Client, *c.Client.Client.ServiceContent.Setting)
|
|
err = om.Update(ctx, []types.BaseOptionValue{&types.OptionValue{
|
|
Key: "config.vpxd.stats.maxQueryMetrics",
|
|
Value: "42",
|
|
}})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
v.MaxQueryMetrics = 256
|
|
ctx = context.Background()
|
|
c2, err := NewClient(ctx, s.URL, v)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
require.Equal(t, 42, v.MaxQueryMetrics)
|
|
c.close()
|
|
c2.close()
|
|
}
|
|
|
|
func testLookupVM(ctx context.Context, t *testing.T, f *Finder, path string, expected int, expectedName string) {
|
|
poweredOn := types.VirtualMachinePowerState("poweredOn")
|
|
var vm []mo.VirtualMachine
|
|
err := f.Find(ctx, "VirtualMachine", path, &vm)
|
|
require.NoError(t, err)
|
|
require.Equal(t, expected, len(vm))
|
|
if expectedName != "" {
|
|
require.Equal(t, expectedName, vm[0].Name)
|
|
}
|
|
for _, v := range vm {
|
|
require.Equal(t, poweredOn, v.Runtime.PowerState)
|
|
}
|
|
}
|
|
|
|
func TestFinder(t *testing.T) {
|
|
// Don't run test on 32-bit machines due to bug in simulator.
|
|
// https://github.com/vmware/govmomi/issues/1330
|
|
var i int
|
|
if unsafe.Sizeof(i) < 8 {
|
|
return
|
|
}
|
|
|
|
m, s, err := createSim(0)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer m.Remove()
|
|
defer s.Close()
|
|
|
|
v := defaultVSphere()
|
|
ctx := context.Background()
|
|
|
|
c, err := NewClient(ctx, s.URL, v)
|
|
|
|
f := Finder{c}
|
|
|
|
var dc []mo.Datacenter
|
|
err = f.Find(ctx, "Datacenter", "/DC0", &dc)
|
|
require.NoError(t, err)
|
|
require.Equal(t, 1, len(dc))
|
|
require.Equal(t, "DC0", dc[0].Name)
|
|
|
|
var host []mo.HostSystem
|
|
err = f.Find(ctx, "HostSystem", "/DC0/host/DC0_H0/DC0_H0", &host)
|
|
require.NoError(t, err)
|
|
require.Equal(t, 1, len(host))
|
|
require.Equal(t, "DC0_H0", host[0].Name)
|
|
|
|
host = []mo.HostSystem{}
|
|
err = f.Find(ctx, "HostSystem", "/DC0/host/DC0_C0/DC0_C0_H0", &host)
|
|
require.NoError(t, err)
|
|
require.Equal(t, 1, len(host))
|
|
require.Equal(t, "DC0_C0_H0", host[0].Name)
|
|
|
|
host = []mo.HostSystem{}
|
|
err = f.Find(ctx, "HostSystem", "/DC0/host/DC0_C0/*", &host)
|
|
require.NoError(t, err)
|
|
require.Equal(t, 3, len(host))
|
|
|
|
var vm []mo.VirtualMachine
|
|
testLookupVM(ctx, t, &f, "/DC0/vm/DC0_H0_VM0", 1, "")
|
|
testLookupVM(ctx, t, &f, "/DC0/vm/DC0_C0*", 2, "")
|
|
testLookupVM(ctx, t, &f, "/DC0/*/DC0_H0_VM0", 1, "DC0_H0_VM0")
|
|
testLookupVM(ctx, t, &f, "/DC0/*/DC0_H0_*", 2, "")
|
|
testLookupVM(ctx, t, &f, "/DC0/**/DC0_H0_VM*", 2, "")
|
|
testLookupVM(ctx, t, &f, "/DC0/**", 4, "")
|
|
testLookupVM(ctx, t, &f, "/DC1/**", 4, "")
|
|
testLookupVM(ctx, t, &f, "/**", 8, "")
|
|
testLookupVM(ctx, t, &f, "/**/vm/**", 8, "")
|
|
testLookupVM(ctx, t, &f, "/*/host/**/*DC*", 8, "")
|
|
testLookupVM(ctx, t, &f, "/*/host/**/*DC*VM*", 8, "")
|
|
testLookupVM(ctx, t, &f, "/*/host/**/*DC*/*/*DC*", 4, "")
|
|
|
|
vm = []mo.VirtualMachine{}
|
|
err = f.FindAll(ctx, "VirtualMachine", []string{"/DC0/vm/DC0_H0*", "/DC0/vm/DC0_C0*"}, []string{}, &vm)
|
|
require.NoError(t, err)
|
|
require.Equal(t, 4, len(vm))
|
|
|
|
rf := ResourceFilter{
|
|
finder: &f,
|
|
paths: []string{"/DC0/vm/DC0_H0*", "/DC0/vm/DC0_C0*"},
|
|
excludePaths: []string{"/DC0/vm/DC0_H0_VM0"},
|
|
resType: "VirtualMachine",
|
|
}
|
|
vm = []mo.VirtualMachine{}
|
|
require.NoError(t, rf.FindAll(ctx, &vm))
|
|
require.Equal(t, 3, len(vm))
|
|
|
|
rf = ResourceFilter{
|
|
finder: &f,
|
|
paths: []string{"/DC0/vm/DC0_H0*", "/DC0/vm/DC0_C0*"},
|
|
excludePaths: []string{"/**"},
|
|
resType: "VirtualMachine",
|
|
}
|
|
vm = []mo.VirtualMachine{}
|
|
require.NoError(t, rf.FindAll(ctx, &vm))
|
|
require.Equal(t, 0, len(vm))
|
|
|
|
rf = ResourceFilter{
|
|
finder: &f,
|
|
paths: []string{"/**"},
|
|
excludePaths: []string{"/**"},
|
|
resType: "VirtualMachine",
|
|
}
|
|
vm = []mo.VirtualMachine{}
|
|
require.NoError(t, rf.FindAll(ctx, &vm))
|
|
require.Equal(t, 0, len(vm))
|
|
|
|
rf = ResourceFilter{
|
|
finder: &f,
|
|
paths: []string{"/**"},
|
|
excludePaths: []string{"/this won't match anything"},
|
|
resType: "VirtualMachine",
|
|
}
|
|
vm = []mo.VirtualMachine{}
|
|
require.NoError(t, rf.FindAll(ctx, &vm))
|
|
require.Equal(t, 8, len(vm))
|
|
|
|
rf = ResourceFilter{
|
|
finder: &f,
|
|
paths: []string{"/**"},
|
|
excludePaths: []string{"/**/*VM0"},
|
|
resType: "VirtualMachine",
|
|
}
|
|
vm = []mo.VirtualMachine{}
|
|
require.NoError(t, rf.FindAll(ctx, &vm))
|
|
require.Equal(t, 4, len(vm))
|
|
}
|
|
|
|
func TestFolders(t *testing.T) {
|
|
// Don't run test on 32-bit machines due to bug in simulator.
|
|
// https://github.com/vmware/govmomi/issues/1330
|
|
var i int
|
|
if unsafe.Sizeof(i) < 8 {
|
|
return
|
|
}
|
|
|
|
m, s, err := createSim(1)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer m.Remove()
|
|
defer s.Close()
|
|
|
|
v := defaultVSphere()
|
|
ctx := context.Background()
|
|
|
|
c, err := NewClient(ctx, s.URL, v)
|
|
|
|
f := Finder{c}
|
|
|
|
var folder []mo.Folder
|
|
err = f.Find(ctx, "Folder", "/F0", &folder)
|
|
require.NoError(t, err)
|
|
require.Equal(t, 1, len(folder))
|
|
require.Equal(t, "F0", folder[0].Name)
|
|
|
|
var dc []mo.Datacenter
|
|
err = f.Find(ctx, "Datacenter", "/F0/DC1", &dc)
|
|
require.NoError(t, err)
|
|
require.Equal(t, 1, len(dc))
|
|
require.Equal(t, "DC1", dc[0].Name)
|
|
|
|
testLookupVM(ctx, t, &f, "/F0/DC0/vm/**/F*", 0, "")
|
|
testLookupVM(ctx, t, &f, "/F0/DC1/vm/**/F*/*VM*", 4, "")
|
|
testLookupVM(ctx, t, &f, "/F0/DC1/vm/**/F*/**", 4, "")
|
|
}
|
|
|
|
func TestAll(t *testing.T) {
|
|
// Don't run test on 32-bit machines due to bug in simulator.
|
|
// https://github.com/vmware/govmomi/issues/1330
|
|
var i int
|
|
if unsafe.Sizeof(i) < 8 {
|
|
return
|
|
}
|
|
|
|
m, s, err := createSim(0)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer m.Remove()
|
|
defer s.Close()
|
|
|
|
var acc testutil.Accumulator
|
|
v := defaultVSphere()
|
|
v.Vcenters = []string{s.URL.String()}
|
|
v.Start(&acc)
|
|
defer v.Stop()
|
|
require.NoError(t, v.Gather(&acc))
|
|
require.Equal(t, 0, len(acc.Errors), fmt.Sprintf("Errors found: %s", acc.Errors))
|
|
require.True(t, len(acc.Metrics) > 0, "No metrics were collected")
|
|
}
|