From 2c8ffa90d5ef1cfc6f9a0f14f9c7a6b147c6bf9e Mon Sep 17 00:00:00 2001 From: "pontus@rydin@gmail.com" Date: Sat, 30 Sep 2017 16:07:01 -0400 Subject: [PATCH 001/100] Added vsphere input plugin. --- plugins/inputs/all/all.go | 1 + plugins/inputs/vsphere/vsphere.go | 237 +++++++++++++++++++ plugins/inputs/zipkin/codec/codec.go | 2 +- plugins/inputs/zipkin/codec/jsonV1/jsonV1.go | 2 +- plugins/inputs/zipkin/codec/thrift/thrift.go | 2 +- 5 files changed, 241 insertions(+), 3 deletions(-) create mode 100644 plugins/inputs/vsphere/vsphere.go diff --git a/plugins/inputs/all/all.go b/plugins/inputs/all/all.go index 45d040503a9e3..2e03ca5fb16d7 100644 --- a/plugins/inputs/all/all.go +++ b/plugins/inputs/all/all.go @@ -90,6 +90,7 @@ import ( _ "github.com/influxdata/telegraf/plugins/inputs/twemproxy" _ "github.com/influxdata/telegraf/plugins/inputs/udp_listener" _ "github.com/influxdata/telegraf/plugins/inputs/varnish" + _ "github.com/influxdata/telegraf/plugins/inputs/vsphere" _ "github.com/influxdata/telegraf/plugins/inputs/webhooks" _ "github.com/influxdata/telegraf/plugins/inputs/win_perf_counters" _ "github.com/influxdata/telegraf/plugins/inputs/win_services" diff --git a/plugins/inputs/vsphere/vsphere.go b/plugins/inputs/vsphere/vsphere.go new file mode 100644 index 0000000000000..0930f71fcbc5e --- /dev/null +++ b/plugins/inputs/vsphere/vsphere.go @@ -0,0 +1,237 @@ +package vsphere + +import ( + "github.com/influxdata/telegraf" + "github.com/vmware/govmomi/vim25/soap" + "github.com/vmware/govmomi" + "github.com/vmware/govmomi/view" + "github.com/vmware/govmomi/vim25/mo" + "context" + "github.com/vmware/govmomi/performance" + "github.com/vmware/govmomi/vim25/types" + "sync" + "github.com/influxdata/telegraf/plugins/inputs" +) + +const Interval = 20 + +type Endpoint struct { + Url string + ciCache map[int32]types.PerfCounterInfo + mux sync.Mutex +} + +type VSphere struct { + Vcenters []string + endpoints []Endpoint +} + +func (e *Endpoint) Init(p *performance.Manager) error { + e.mux.Lock() + defer e.mux.Unlock() + if e.ciCache != nil { + return nil + } + ctx := context.Background() + defer p.Destroy(ctx) + e.ciCache = make(map[int32]types.PerfCounterInfo) + cis, err := p.CounterInfo(ctx) + if err != nil { + return err + } + for _, ci := range cis { + e.ciCache[ci.Key] = ci + } + return nil +} + +func (e *Endpoint) CollectResourceType(p *performance.Manager, ctx context.Context, alias string, acc telegraf.Accumulator, + objects map[string]types.ManagedObjectReference) error { + + for name, mor := range objects { + // Collect metrics + // + ams, err := p.AvailableMetric(ctx, mor, Interval) + if err != nil { + return err + } + pqs := types.PerfQuerySpec{ + Entity: mor, + MaxSample: 1, + MetricId: ams, + IntervalId: 20, + } + metrics, err := p.Query(ctx, []types.PerfQuerySpec{ pqs }) + if err != nil { + return err + } + fields := make(map[string]interface{}) + ems, err := p.ToMetricSeries(ctx, metrics) + if err != nil { + return err + } + + // Iterate through result and fields list + // + for _, em := range ems { + for _, v := range em.Value { + name := v.Name + if v.Instance != "" { + name += "." + v.Instance + } + fields[name] = v.Value[0] + } + } + tags := map[string]string { + "entityName": name, + "entityId": mor.Value} + acc.AddFields("vsphere." + alias, fields, tags) + } + return nil +} + +func (e *Endpoint) Collect(acc telegraf.Accumulator) error { + ctx := context.Background() + u, err := soap.ParseURL(e.Url) + if(err != nil) { + return err + } + c, err := govmomi.NewClient(ctx, u, true) + if(err != nil) { + return err + } + + defer c.Logout(ctx) + + m := view.NewManager(c.Client) + v, err := m.CreateContainerView(ctx, c.ServiceContent.RootFolder, []string{"VirtualMachine"}, true) + if err != nil { + return err + } + + defer v.Destroy(ctx) + + p := performance.NewManager(c.Client) + p.Destroy(ctx) + + // Load cache if needed + e.Init(p) + + vms, err := e.getVMs(ctx, v) + if err != nil { + return err + } + err = e.CollectResourceType(p, ctx, "vm", acc, vms) + if err != nil { + return err + } + hosts, err := e.getHosts(ctx, v) + if err != nil { + return err + } + err = e.CollectResourceType(p, ctx, "host", acc, hosts) + if err != nil { + return err + } + clusters, err := e.getClusters(ctx, v) + if err != nil { + return err + } + err = e.CollectResourceType(p, ctx, "cluster", acc, clusters) + if err != nil { + return err + } + datastores, err := e.getDatastores(ctx, v) + if err != nil { + return err + } + err = e.CollectResourceType(p, ctx, "datastore", acc, datastores) + if err != nil { + return err + } + return nil +} + +func (e *Endpoint) getVMs(ctx context.Context, root *view.ContainerView) (map[string]types.ManagedObjectReference, error) { + var resources []mo.VirtualMachine + err := root.Retrieve(ctx, []string{"VirtualMachine"}, []string{"summary"}, &resources) + if err != nil { + return nil, err + } + m := make(map[string]types.ManagedObjectReference) + for _, r := range resources { + m[r.Summary.Config.Name] = r.ExtensibleManagedObject.Reference() + } + return m, nil +} + +func (e *Endpoint) getHosts(ctx context.Context, root *view.ContainerView) (map[string]types.ManagedObjectReference, error) { + var resources []mo.HostSystem + err := root.Retrieve(ctx, []string{"HostSystem"}, []string{"summary"}, &resources) + if err != nil { + return nil, err + } + m := make(map[string]types.ManagedObjectReference) + for _, r := range resources { + m[r.Summary.Config.Name] = r.ExtensibleManagedObject.Reference() + } + return m, nil +} + +func (e *Endpoint) getClusters(ctx context.Context, root *view.ContainerView) (map[string]types.ManagedObjectReference, error) { + var resources []mo.ClusterComputeResource + err := root.Retrieve(ctx, []string{"ClusterComputeResource"}, []string{"summary"}, &resources) + if err != nil { + return nil, err + } + m := make(map[string]types.ManagedObjectReference) + for _, r := range resources { + m[r.Name] = r.ExtensibleManagedObject.Reference() + } + return m, nil +} + +func (e *Endpoint) getDatastores(ctx context.Context, root *view.ContainerView) (map[string]types.ManagedObjectReference, error) { + var resources []mo.Datastore + err := root.Retrieve(ctx, []string{"Datastore"}, []string{"summary"}, &resources) + if err != nil { + return nil, err + } + m := make(map[string]types.ManagedObjectReference) + for _, r := range resources { + m[r.Summary.Name] = r.ExtensibleManagedObject.Reference() + } + return m, nil +} + +var sampleConfig = ` + ## List of vCenter URLs, including credentials. Note the "@" characted must be escaped as %40 + # vcenters = [ "https://administrator%40vsphere.local:VMware1!@vcenter.local/sdk" ] +` + +func (v *VSphere) SampleConfig() string { + return sampleConfig +} + +func (v *VSphere) Description() string { + return "Read metrics from VMware vCenter" +} + +func (v *VSphere) Gather(acc telegraf.Accumulator) error { + for _, ep := range v.endpoints { + err := ep.Collect(acc) + if err != nil { + return err + } + } + return nil +} + +func init() { + inputs.Add("vsphere", func() telegraf.Input { + return &VSphere{ Vcenters: []string {} } + }) +} + + + diff --git a/plugins/inputs/zipkin/codec/codec.go b/plugins/inputs/zipkin/codec/codec.go index 781aad72aa73a..167b8ec24f1a3 100644 --- a/plugins/inputs/zipkin/codec/codec.go +++ b/plugins/inputs/zipkin/codec/codec.go @@ -4,7 +4,7 @@ import ( "time" "github.com/influxdata/telegraf/plugins/inputs/zipkin/trace" - "github.com/openzipkin/zipkin-go-opentracing/_thrift/gen-go/zipkincore" + "github.com/openzipkin/zipkin-go-opentracing/thrift/gen-go/zipkincore" ) //now is a mockable time for now diff --git a/plugins/inputs/zipkin/codec/jsonV1/jsonV1.go b/plugins/inputs/zipkin/codec/jsonV1/jsonV1.go index 53670dd298336..52b2c028e5f43 100644 --- a/plugins/inputs/zipkin/codec/jsonV1/jsonV1.go +++ b/plugins/inputs/zipkin/codec/jsonV1/jsonV1.go @@ -7,7 +7,7 @@ import ( "time" "github.com/influxdata/telegraf/plugins/inputs/zipkin/codec" - "github.com/openzipkin/zipkin-go-opentracing/_thrift/gen-go/zipkincore" + "github.com/openzipkin/zipkin-go-opentracing/thrift/gen-go/zipkincore" ) // JSON decodes spans from bodies `POST`ed to the spans endpoint diff --git a/plugins/inputs/zipkin/codec/thrift/thrift.go b/plugins/inputs/zipkin/codec/thrift/thrift.go index c2170e87acb8b..07938038b117c 100644 --- a/plugins/inputs/zipkin/codec/thrift/thrift.go +++ b/plugins/inputs/zipkin/codec/thrift/thrift.go @@ -10,7 +10,7 @@ import ( "github.com/influxdata/telegraf/plugins/inputs/zipkin/codec" "github.com/apache/thrift/lib/go/thrift" - "github.com/openzipkin/zipkin-go-opentracing/_thrift/gen-go/zipkincore" + "github.com/openzipkin/zipkin-go-opentracing/thrift/gen-go/zipkincore" ) // UnmarshalThrift converts raw bytes in thrift format to a slice of spans From 037983c3fdd7876ea498600f29113d2fa8e81f38 Mon Sep 17 00:00:00 2001 From: "pontus@rydin@gmail.com" Date: Sat, 30 Sep 2017 16:31:42 -0400 Subject: [PATCH 002/100] First "working" version of vsphere input plugin --- plugins/inputs/vsphere/vsphere.go | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/plugins/inputs/vsphere/vsphere.go b/plugins/inputs/vsphere/vsphere.go index 0930f71fcbc5e..df3f3cbe07979 100644 --- a/plugins/inputs/vsphere/vsphere.go +++ b/plugins/inputs/vsphere/vsphere.go @@ -24,6 +24,7 @@ type Endpoint struct { type VSphere struct { Vcenters []string endpoints []Endpoint + mux sync.Mutex } func (e *Endpoint) Init(p *performance.Manager) error { @@ -217,7 +218,22 @@ func (v *VSphere) Description() string { return "Read metrics from VMware vCenter" } +func (v *VSphere) Init() { + v.mux.Lock() + defer v.mux.Unlock() + + + if v.endpoints != nil { + return + } + v.endpoints = make([]Endpoint, len(v.Vcenters)) + for i, u := range v.Vcenters { + v.endpoints[i] = Endpoint{ Url: u } + } +} + func (v *VSphere) Gather(acc telegraf.Accumulator) error { + v.Init() for _, ep := range v.endpoints { err := ep.Collect(acc) if err != nil { From 2ca7cf45dd646f577f00d052289279df76b380a5 Mon Sep 17 00:00:00 2001 From: "pontus@rydin@gmail.com" Date: Sun, 1 Oct 2017 15:10:33 -0400 Subject: [PATCH 003/100] Optimizations of vSphere plugin. Fixed bug with missing newlines in Wavefront plugin. --- plugins/inputs/vsphere/vsphere.go | 151 ++++++++++++++----------- plugins/outputs/wavefront/wavefront.go | 2 +- 2 files changed, 88 insertions(+), 65 deletions(-) diff --git a/plugins/inputs/vsphere/vsphere.go b/plugins/inputs/vsphere/vsphere.go index df3f3cbe07979..8b8c16e6a359c 100644 --- a/plugins/inputs/vsphere/vsphere.go +++ b/plugins/inputs/vsphere/vsphere.go @@ -11,83 +11,113 @@ import ( "github.com/vmware/govmomi/vim25/types" "sync" "github.com/influxdata/telegraf/plugins/inputs" + "time" + "fmt" ) -const Interval = 20 - type Endpoint struct { + Parent *VSphere Url string - ciCache map[int32]types.PerfCounterInfo + intervals []int32 mux sync.Mutex } type VSphere struct { Vcenters []string + VmInterval int32 + HostInterval int32 + ClusterInterval int32 + DatastoreInterval int32 endpoints []Endpoint mux sync.Mutex } +type ResourceGetter func (context.Context, *view.ContainerView) (map[string]types.ManagedObjectReference, error) + +type InstanceMetrics map[string]map[string]interface{} + func (e *Endpoint) Init(p *performance.Manager) error { e.mux.Lock() defer e.mux.Unlock() - if e.ciCache != nil { - return nil - } - ctx := context.Background() - defer p.Destroy(ctx) - e.ciCache = make(map[int32]types.PerfCounterInfo) - cis, err := p.CounterInfo(ctx) - if err != nil { - return err - } - for _, ci := range cis { - e.ciCache[ci.Key] = ci + if e.intervals == nil { + // Load interval table + // + ctx := context.Background() + list, err := p.HistoricalInterval(ctx) + if err != nil { + return err + } + e.intervals = make([]int32, len(list)) + for k, i := range list { + e.intervals[k] = i.SamplingPeriod + } } return nil } func (e *Endpoint) CollectResourceType(p *performance.Manager, ctx context.Context, alias string, acc telegraf.Accumulator, - objects map[string]types.ManagedObjectReference) error { + getter ResourceGetter, root *view.ContainerView, interval int32) error { + start := time.Now() + objects, err := getter(ctx, root) + if err != nil { + return err + } + pqs := make([]types.PerfQuerySpec, len(objects)) + nameLookup := make(map[string]string) + idx := 0 for name, mor := range objects { + nameLookup[mor.Reference().Value] = name; // Collect metrics // - ams, err := p.AvailableMetric(ctx, mor, Interval) + ams, err := p.AvailableMetric(ctx, mor, interval) if err != nil { return err } - pqs := types.PerfQuerySpec{ + pqs[idx] = types.PerfQuerySpec{ Entity: mor, MaxSample: 1, MetricId: ams, - IntervalId: 20, - } - metrics, err := p.Query(ctx, []types.PerfQuerySpec{ pqs }) - if err != nil { - return err - } - fields := make(map[string]interface{}) - ems, err := p.ToMetricSeries(ctx, metrics) - if err != nil { - return err + IntervalId: interval, } + idx++ + } - // Iterate through result and fields list - // - for _, em := range ems { - for _, v := range em.Value { - name := v.Name - if v.Instance != "" { - name += "." + v.Instance - } - fields[name] = v.Value[0] + metrics, err := p.Query(ctx, pqs ) + if err != nil { + return err + } + + ems, err := p.ToMetricSeries(ctx, metrics) + if err != nil { + return err + } + + // Iterate through result and fields list + // + for _, em := range ems { + im := make(InstanceMetrics) + for _, v := range em.Value { + name := v.Name + m, found := im[v.Instance] + if !found { + m = make(map[string]interface{}) + im[v.Instance] = m } + m[name] = v.Value[0] + } + for k, m := range im { + moid := em.Entity.Reference().Value + tags := map[string]string{ + "source": nameLookup[moid], + "moid": moid} + if k != "" { + tags["instance"] = k + } + acc.AddFields("vsphere." + alias, m, tags) } - tags := map[string]string { - "entityName": name, - "entityId": mor.Value} - acc.AddFields("vsphere." + alias, fields, tags) } + fmt.Println(time.Now().Sub(start)) return nil } @@ -105,7 +135,7 @@ func (e *Endpoint) Collect(acc telegraf.Accumulator) error { defer c.Logout(ctx) m := view.NewManager(c.Client) - v, err := m.CreateContainerView(ctx, c.ServiceContent.RootFolder, []string{"VirtualMachine"}, true) + v, err := m.CreateContainerView(ctx, c.ServiceContent.RootFolder, []string{ }, true) if err != nil { return err } @@ -118,35 +148,22 @@ func (e *Endpoint) Collect(acc telegraf.Accumulator) error { // Load cache if needed e.Init(p) - vms, err := e.getVMs(ctx, v) - if err != nil { - return err - } - err = e.CollectResourceType(p, ctx, "vm", acc, vms) + err = e.CollectResourceType(p, ctx, "vm", acc, e.getVMs, v, e.Parent.VmInterval) if err != nil { return err } - hosts, err := e.getHosts(ctx, v) - if err != nil { - return err - } - err = e.CollectResourceType(p, ctx, "host", acc, hosts) - if err != nil { - return err - } - clusters, err := e.getClusters(ctx, v) - if err != nil { - return err - } - err = e.CollectResourceType(p, ctx, "cluster", acc, clusters) + + err = e.CollectResourceType(p, ctx, "host", acc, e.getHosts, v, e.Parent.HostInterval) if err != nil { return err } - datastores, err := e.getDatastores(ctx, v) + + err = e.CollectResourceType(p, ctx, "cluster", acc, e.getClusters, v, e.Parent.ClusterInterval) if err != nil { return err } - err = e.CollectResourceType(p, ctx, "datastore", acc, datastores) + + err = e.CollectResourceType(p, ctx, "datastore", acc, e.getDatastores, v, e.Parent.DatastoreInterval) if err != nil { return err } @@ -228,7 +245,7 @@ func (v *VSphere) Init() { } v.endpoints = make([]Endpoint, len(v.Vcenters)) for i, u := range v.Vcenters { - v.endpoints[i] = Endpoint{ Url: u } + v.endpoints[i] = Endpoint{ Url: u, Parent: v } } } @@ -245,7 +262,13 @@ func (v *VSphere) Gather(acc telegraf.Accumulator) error { func init() { inputs.Add("vsphere", func() telegraf.Input { - return &VSphere{ Vcenters: []string {} } + return &VSphere{ + Vcenters: []string {}, + VmInterval: 20, + HostInterval: 20, + ClusterInterval: 300, + DatastoreInterval: 300, + } }) } diff --git a/plugins/outputs/wavefront/wavefront.go b/plugins/outputs/wavefront/wavefront.go index 773e970bc0b53..be083ef67e7bc 100644 --- a/plugins/outputs/wavefront/wavefront.go +++ b/plugins/outputs/wavefront/wavefront.go @@ -261,7 +261,7 @@ func formatMetricPoint(metricPoint *MetricPoint, w *Wavefront) string { buffer.WriteString(tagValueReplacer.Replace(v)) buffer.WriteString("\"") } - + buffer.WriteString("\n") return buffer.String() } From 3f2e1b916ec2c80380b653505c1ac246975391f0 Mon Sep 17 00:00:00 2001 From: "pontus@rydin@gmail.com" Date: Tue, 3 Oct 2017 09:25:52 -0700 Subject: [PATCH 004/100] Performance improvements --- plugins/inputs/vsphere/vsphere.go | 150 ++++++++++++++++++------------ 1 file changed, 92 insertions(+), 58 deletions(-) diff --git a/plugins/inputs/vsphere/vsphere.go b/plugins/inputs/vsphere/vsphere.go index 8b8c16e6a359c..a6335679fa711 100644 --- a/plugins/inputs/vsphere/vsphere.go +++ b/plugins/inputs/vsphere/vsphere.go @@ -9,17 +9,16 @@ import ( "context" "github.com/vmware/govmomi/performance" "github.com/vmware/govmomi/vim25/types" - "sync" "github.com/influxdata/telegraf/plugins/inputs" "time" - "fmt" + "github.com/Sirupsen/logrus" ) type Endpoint struct { Parent *VSphere Url string intervals []int32 - mux sync.Mutex + lastColl map[string]time.Time } type VSphere struct { @@ -28,17 +27,16 @@ type VSphere struct { HostInterval int32 ClusterInterval int32 DatastoreInterval int32 + MaxSamples int32 + MaxQuery int32 endpoints []Endpoint - mux sync.Mutex } type ResourceGetter func (context.Context, *view.ContainerView) (map[string]types.ManagedObjectReference, error) type InstanceMetrics map[string]map[string]interface{} -func (e *Endpoint) Init(p *performance.Manager) error { - e.mux.Lock() - defer e.mux.Unlock() +func (e *Endpoint) init(p *performance.Manager) error { if e.intervals == nil { // Load interval table // @@ -55,73 +53,101 @@ func (e *Endpoint) Init(p *performance.Manager) error { return nil } -func (e *Endpoint) CollectResourceType(p *performance.Manager, ctx context.Context, alias string, acc telegraf.Accumulator, +func (e *Endpoint) collectResourceType(p *performance.Manager, ctx context.Context, alias string, acc telegraf.Accumulator, getter ResourceGetter, root *view.ContainerView, interval int32) error { + // Do we have new data yet? + // + nIntervals := int32(1) + latest, hasLatest := e.lastColl[alias] + if(hasLatest) { + elapsed := time.Now().Sub(latest).Seconds() + if elapsed < float64(interval) { + // No new data would be available. We're outta here! + // + return nil; + } + nIntervals := int32(elapsed / (float64(interval))) + if nIntervals > e.Parent.MaxSamples { + nIntervals = e.Parent.MaxSamples + } + } + log.Printf("Collecting %d intervals for %s", nIntervals, alias) + + fullAlias := "vsphere." + alias + start := time.Now() objects, err := getter(ctx, root) if err != nil { return err } - pqs := make([]types.PerfQuerySpec, len(objects)) + pqs := make([]types.PerfQuerySpec, 0, e.Parent.MaxQuery) nameLookup := make(map[string]string) - idx := 0 + total := 0; for name, mor := range objects { nameLookup[mor.Reference().Value] = name; + // Collect metrics // ams, err := p.AvailableMetric(ctx, mor, interval) if err != nil { return err } - pqs[idx] = types.PerfQuerySpec{ + + pq := types.PerfQuerySpec{ Entity: mor, - MaxSample: 1, + MaxSample: nIntervals, MetricId: ams, IntervalId: interval, } - idx++ - } - - metrics, err := p.Query(ctx, pqs ) - if err != nil { - return err - } + if(e.Parent.MaxSamples > 1 && hasLatest) { + pq.StartTime = &start + } + pqs = append(pqs, pq) + total++ - ems, err := p.ToMetricSeries(ctx, metrics) - if err != nil { - return err - } + // Filled up a chunk or at end of data? Run a query with the collected objects + // + if len(pqs) >= int(e.Parent.MaxQuery) || total == len(objects) { + log.Printf("Querying %d objects of type %s. Total processed: %d. Total objects %d\n", len(pqs), alias, total, len(objects)) + metrics, err := p.Query(ctx, pqs) + if err != nil { + return err + } - // Iterate through result and fields list - // - for _, em := range ems { - im := make(InstanceMetrics) - for _, v := range em.Value { - name := v.Name - m, found := im[v.Instance] - if !found { - m = make(map[string]interface{}) - im[v.Instance] = m + ems, err := p.ToMetricSeries(ctx, metrics) + if err != nil { + return err } - m[name] = v.Value[0] - } - for k, m := range im { - moid := em.Entity.Reference().Value - tags := map[string]string{ - "source": nameLookup[moid], - "moid": moid} - if k != "" { - tags["instance"] = k + + // Iterate through result and fields list + // + for _, em := range ems { + moid := em.Entity.Reference().Value + for _, v := range em.Value { + name := v.Name + for idx, value := range v.Value { + f := map[string]interface{} { name: value } + tags := map[string]string{ + "source": nameLookup[moid], + "moid": moid} + if v.Instance != "" { + tags["instance"] = v.Instance + } + acc.AddFields(fullAlias, f, tags, em.SampleInfo[idx].Timestamp) + } + } } - acc.AddFields("vsphere." + alias, m, tags) + pqs = make([]types.PerfQuerySpec, 0, e.Parent.MaxQuery) } } - fmt.Println(time.Now().Sub(start)) + + + log.Printf("Collection of %s took %v\n", alias, time.Now().Sub(start)) return nil } -func (e *Endpoint) Collect(acc telegraf.Accumulator) error { +func (e *Endpoint) collect(acc telegraf.Accumulator) error { ctx := context.Background() u, err := soap.ParseURL(e.Url) if(err != nil) { @@ -146,24 +172,24 @@ func (e *Endpoint) Collect(acc telegraf.Accumulator) error { p.Destroy(ctx) // Load cache if needed - e.Init(p) + e.init(p) - err = e.CollectResourceType(p, ctx, "vm", acc, e.getVMs, v, e.Parent.VmInterval) + err = e.collectResourceType(p, ctx, "vm", acc, e.getVMs, v, e.Parent.VmInterval) if err != nil { return err } - err = e.CollectResourceType(p, ctx, "host", acc, e.getHosts, v, e.Parent.HostInterval) + err = e.collectResourceType(p, ctx, "host", acc, e.getHosts, v, e.Parent.HostInterval) if err != nil { return err } - err = e.CollectResourceType(p, ctx, "cluster", acc, e.getClusters, v, e.Parent.ClusterInterval) + err = e.collectResourceType(p, ctx, "cluster", acc, e.getClusters, v, e.Parent.ClusterInterval) if err != nil { return err } - err = e.CollectResourceType(p, ctx, "datastore", acc, e.getDatastores, v, e.Parent.DatastoreInterval) + err = e.collectResourceType(p, ctx, "datastore", acc, e.getDatastores, v, e.Parent.DatastoreInterval) if err != nil { return err } @@ -236,28 +262,34 @@ func (v *VSphere) Description() string { } func (v *VSphere) Init() { - v.mux.Lock() - defer v.mux.Unlock() - - if v.endpoints != nil { return } v.endpoints = make([]Endpoint, len(v.Vcenters)) for i, u := range v.Vcenters { - v.endpoints[i] = Endpoint{ Url: u, Parent: v } + v.endpoints[i] = Endpoint{ + Url: u, + Parent: v, + lastColl: make(map[string]time.Time)} } } func (v *VSphere) Gather(acc telegraf.Accumulator) error { v.Init() + results := make(chan error) for _, ep := range v.endpoints { - err := ep.Collect(acc) + go func() { + results <- ep.collect(acc) + }() + } + var finalErr error = nil + for range v.endpoints { + err := <- results if err != nil { - return err + finalErr = err } } - return nil + return finalErr } func init() { @@ -268,6 +300,8 @@ func init() { HostInterval: 20, ClusterInterval: 300, DatastoreInterval: 300, + MaxSamples: 10, + MaxQuery: 64, } }) } From 4985053fa25f530ccfdfb5e3454b38222a07190f Mon Sep 17 00:00:00 2001 From: "pontus@rydin@gmail.com" Date: Tue, 3 Oct 2017 09:27:17 -0700 Subject: [PATCH 005/100] Use logrus instead of built-in log --- plugins/inputs/vsphere/vsphere.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/inputs/vsphere/vsphere.go b/plugins/inputs/vsphere/vsphere.go index a6335679fa711..c0d20dbf4546a 100644 --- a/plugins/inputs/vsphere/vsphere.go +++ b/plugins/inputs/vsphere/vsphere.go @@ -72,7 +72,7 @@ func (e *Endpoint) collectResourceType(p *performance.Manager, ctx context.Conte nIntervals = e.Parent.MaxSamples } } - log.Printf("Collecting %d intervals for %s", nIntervals, alias) + logrus.Debugf("Collecting %d intervals for %s", nIntervals, alias) fullAlias := "vsphere." + alias @@ -109,7 +109,7 @@ func (e *Endpoint) collectResourceType(p *performance.Manager, ctx context.Conte // Filled up a chunk or at end of data? Run a query with the collected objects // if len(pqs) >= int(e.Parent.MaxQuery) || total == len(objects) { - log.Printf("Querying %d objects of type %s. Total processed: %d. Total objects %d\n", len(pqs), alias, total, len(objects)) + logrus.Debugf("Querying %d objects of type %s. Total processed: %d. Total objects %d\n", len(pqs), alias, total, len(objects)) metrics, err := p.Query(ctx, pqs) if err != nil { return err @@ -143,7 +143,7 @@ func (e *Endpoint) collectResourceType(p *performance.Manager, ctx context.Conte } - log.Printf("Collection of %s took %v\n", alias, time.Now().Sub(start)) + logrus.Debugf("Collection of %s took %v\n", alias, time.Now().Sub(start)) return nil } From 36a28267dd6ce5f296fb290e36b5c253307a6f4f Mon Sep 17 00:00:00 2001 From: "pontus@rydin@gmail.com" Date: Fri, 13 Oct 2017 16:34:59 -0400 Subject: [PATCH 006/100] Performance improvments. --- plugins/inputs/vsphere/vsphere.go | 54 ++++++++++++++++++------------- 1 file changed, 31 insertions(+), 23 deletions(-) diff --git a/plugins/inputs/vsphere/vsphere.go b/plugins/inputs/vsphere/vsphere.go index c0d20dbf4546a..9fe47d750ac85 100644 --- a/plugins/inputs/vsphere/vsphere.go +++ b/plugins/inputs/vsphere/vsphere.go @@ -11,12 +11,13 @@ import ( "github.com/vmware/govmomi/vim25/types" "github.com/influxdata/telegraf/plugins/inputs" "time" - "github.com/Sirupsen/logrus" + "log" + "net/url" ) type Endpoint struct { Parent *VSphere - Url string + Url *url.URL intervals []int32 lastColl map[string]time.Time } @@ -56,6 +57,12 @@ func (e *Endpoint) init(p *performance.Manager) error { func (e *Endpoint) collectResourceType(p *performance.Manager, ctx context.Context, alias string, acc telegraf.Accumulator, getter ResourceGetter, root *view.ContainerView, interval int32) error { + // Interval = -1 means collection for this metric was diabled, so don't even bother. + // + if interval == -1 { + return nil + } + // Do we have new data yet? // nIntervals := int32(1) @@ -72,7 +79,12 @@ func (e *Endpoint) collectResourceType(p *performance.Manager, ctx context.Conte nIntervals = e.Parent.MaxSamples } } - logrus.Debugf("Collecting %d intervals for %s", nIntervals, alias) + // log.Printf("URL: %s" + url.U e.Url) + // Get all possible metric ids + // + + + log.Printf("D! Collecting %d intervals for %s", nIntervals, alias) fullAlias := "vsphere." + alias @@ -81,23 +93,17 @@ func (e *Endpoint) collectResourceType(p *performance.Manager, ctx context.Conte if err != nil { return err } + log.Printf("D! Query for %s returned %d objects", alias, len(objects)) pqs := make([]types.PerfQuerySpec, 0, e.Parent.MaxQuery) nameLookup := make(map[string]string) total := 0; for name, mor := range objects { - nameLookup[mor.Reference().Value] = name; - - // Collect metrics - // - ams, err := p.AvailableMetric(ctx, mor, interval) - if err != nil { - return err - } + nameLookup[mor.Reference().Value] = name pq := types.PerfQuerySpec{ Entity: mor, MaxSample: nIntervals, - MetricId: ams, + MetricId: nil, IntervalId: interval, } if(e.Parent.MaxSamples > 1 && hasLatest) { @@ -109,7 +115,7 @@ func (e *Endpoint) collectResourceType(p *performance.Manager, ctx context.Conte // Filled up a chunk or at end of data? Run a query with the collected objects // if len(pqs) >= int(e.Parent.MaxQuery) || total == len(objects) { - logrus.Debugf("Querying %d objects of type %s. Total processed: %d. Total objects %d\n", len(pqs), alias, total, len(objects)) + log.Printf("D! Querying %d objects of type %s for %s. Total processed: %d. Total objects %d\n", len(pqs), alias, e.Url.Host, total, len(objects)) metrics, err := p.Query(ctx, pqs) if err != nil { return err @@ -129,6 +135,7 @@ func (e *Endpoint) collectResourceType(p *performance.Manager, ctx context.Conte for idx, value := range v.Value { f := map[string]interface{} { name: value } tags := map[string]string{ + "vcenter": e.Url.Host, "source": nameLookup[moid], "moid": moid} if v.Instance != "" { @@ -143,17 +150,13 @@ func (e *Endpoint) collectResourceType(p *performance.Manager, ctx context.Conte } - logrus.Debugf("Collection of %s took %v\n", alias, time.Now().Sub(start)) + log.Printf("D! Collection of %s took %v\n", alias, time.Now().Sub(start)) return nil } func (e *Endpoint) collect(acc telegraf.Accumulator) error { ctx := context.Background() - u, err := soap.ParseURL(e.Url) - if(err != nil) { - return err - } - c, err := govmomi.NewClient(ctx, u, true) + c, err := govmomi.NewClient(ctx, e.Url, true) if(err != nil) { return err } @@ -266,7 +269,11 @@ func (v *VSphere) Init() { return } v.endpoints = make([]Endpoint, len(v.Vcenters)) - for i, u := range v.Vcenters { + for i, rawUrl := range v.Vcenters { + u, err := soap.ParseURL(rawUrl); + if(err != nil) { + log.Printf("E! Can't parse URL %s\n", rawUrl) + } v.endpoints[i] = Endpoint{ Url: u, Parent: v, @@ -278,14 +285,15 @@ func (v *VSphere) Gather(acc telegraf.Accumulator) error { v.Init() results := make(chan error) for _, ep := range v.endpoints { - go func() { - results <- ep.collect(acc) - }() + go func(target Endpoint) { + results <- target.collect(acc) + }(ep) } var finalErr error = nil for range v.endpoints { err := <- results if err != nil { + log.Println("E!", err) finalErr = err } } From 0460ba8bffca022d110092950410fcbfbd5d084c Mon Sep 17 00:00:00 2001 From: "pontus@rydin@gmail.com" Date: Tue, 17 Oct 2017 09:48:09 -0700 Subject: [PATCH 007/100] Fixed premature destroy of performance manager --- plugins/inputs/vsphere/vsphere.go | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/plugins/inputs/vsphere/vsphere.go b/plugins/inputs/vsphere/vsphere.go index 9fe47d750ac85..c548f27a6a6c8 100644 --- a/plugins/inputs/vsphere/vsphere.go +++ b/plugins/inputs/vsphere/vsphere.go @@ -79,10 +79,6 @@ func (e *Endpoint) collectResourceType(p *performance.Manager, ctx context.Conte nIntervals = e.Parent.MaxSamples } } - // log.Printf("URL: %s" + url.U e.Url) - // Get all possible metric ids - // - log.Printf("D! Collecting %d intervals for %s", nIntervals, alias) @@ -172,7 +168,7 @@ func (e *Endpoint) collect(acc telegraf.Accumulator) error { defer v.Destroy(ctx) p := performance.NewManager(c.Client) - p.Destroy(ctx) + defer p.Destroy(ctx) // Load cache if needed e.init(p) From c9b1adfa37a4024c40ece15dd034023d2e7074d7 Mon Sep 17 00:00:00 2001 From: "pontus@rydin@gmail.com" Date: Wed, 18 Oct 2017 19:15:06 -0400 Subject: [PATCH 008/100] Removed call to PerformanceManager.Destroy since it floods the vCenter console with error messages. --- plugins/inputs/vsphere/vsphere.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/plugins/inputs/vsphere/vsphere.go b/plugins/inputs/vsphere/vsphere.go index c548f27a6a6c8..df53c15f0e783 100644 --- a/plugins/inputs/vsphere/vsphere.go +++ b/plugins/inputs/vsphere/vsphere.go @@ -168,7 +168,12 @@ func (e *Endpoint) collect(acc telegraf.Accumulator) error { defer v.Destroy(ctx) p := performance.NewManager(c.Client) - defer p.Destroy(ctx) + + // This causes strange error messages in the vCenter console. Possibly due to a bug in + // govmomi. We're commenting it out for now. Should be benign since the logout should + // destroy all resources anyway. + // + //defer p.Destroy(ctx) // Load cache if needed e.init(p) From 748961a4bfd672e515f2956e3e36bc1da4b4f2ca Mon Sep 17 00:00:00 2001 From: "pontus@rydin@gmail.com" Date: Sun, 22 Oct 2017 13:14:55 -0400 Subject: [PATCH 009/100] Include name of parent in output. --- plugins/inputs/vsphere/vsphere.go | 123 ++++++++++++++++++++---------- 1 file changed, 81 insertions(+), 42 deletions(-) diff --git a/plugins/inputs/vsphere/vsphere.go b/plugins/inputs/vsphere/vsphere.go index df53c15f0e783..99e60d5495f68 100644 --- a/plugins/inputs/vsphere/vsphere.go +++ b/plugins/inputs/vsphere/vsphere.go @@ -33,7 +33,13 @@ type VSphere struct { endpoints []Endpoint } -type ResourceGetter func (context.Context, *view.ContainerView) (map[string]types.ManagedObjectReference, error) +type objectRef struct { + name string + ref types.ManagedObjectReference + parentRef *types.ManagedObjectReference //Pointer because it must be nillable +} + +type objectMap map[string]objectRef type InstanceMetrics map[string]map[string]interface{} @@ -55,7 +61,7 @@ func (e *Endpoint) init(p *performance.Manager) error { } func (e *Endpoint) collectResourceType(p *performance.Manager, ctx context.Context, alias string, acc telegraf.Accumulator, - getter ResourceGetter, root *view.ContainerView, interval int32) error { + objects objectMap, nameCache map[string]string, interval int32) error { // Interval = -1 means collection for this metric was diabled, so don't even bother. // @@ -65,11 +71,12 @@ func (e *Endpoint) collectResourceType(p *performance.Manager, ctx context.Conte // Do we have new data yet? // + now := time.Now() nIntervals := int32(1) latest, hasLatest := e.lastColl[alias] - if(hasLatest) { + if (hasLatest) { elapsed := time.Now().Sub(latest).Seconds() - if elapsed < float64(interval) { + if elapsed < float64(interval) { // No new data would be available. We're outta here! // return nil; @@ -79,31 +86,29 @@ func (e *Endpoint) collectResourceType(p *performance.Manager, ctx context.Conte nIntervals = e.Parent.MaxSamples } } - + e.lastColl[alias] = now log.Printf("D! Collecting %d intervals for %s", nIntervals, alias) - fullAlias := "vsphere." + alias start := time.Now() - objects, err := getter(ctx, root) - if err != nil { - return err - } log.Printf("D! Query for %s returned %d objects", alias, len(objects)) pqs := make([]types.PerfQuerySpec, 0, e.Parent.MaxQuery) - nameLookup := make(map[string]string) total := 0; - for name, mor := range objects { - nameLookup[mor.Reference().Value] = name - + for _, object := range objects { pq := types.PerfQuerySpec{ - Entity: mor, + Entity: object.ref, MaxSample: nIntervals, MetricId: nil, IntervalId: interval, } + if(interval > 20) { + startTime := now.Add(-time.Duration(interval) * time.Second) + pq.StartTime = &startTime + pq.EndTime = &now + } if(e.Parent.MaxSamples > 1 && hasLatest) { - pq.StartTime = &start + pq.StartTime = &latest + pq.EndTime = &now } pqs = append(pqs, pq) total++ @@ -130,14 +135,23 @@ func (e *Endpoint) collectResourceType(p *performance.Manager, ctx context.Conte name := v.Name for idx, value := range v.Value { f := map[string]interface{} { name: value } - tags := map[string]string{ + objectName := nameCache[moid] + parent := "" + parentRef := objects[moid].parentRef + //log.Printf("Parentref=%s", parentRef) + if parentRef != nil { + parent = nameCache[parentRef.Value] + } + + t := map[string]string{ "vcenter": e.Url.Host, - "source": nameLookup[moid], - "moid": moid} + "source": objectName, + "moid": moid, + "parent": parent} if v.Instance != "" { - tags["instance"] = v.Instance + t["instance"] = v.Instance } - acc.AddFields(fullAlias, f, tags, em.SampleInfo[idx].Timestamp) + acc.AddFields(fullAlias, f, t, em.SampleInfo[idx].Timestamp) } } } @@ -178,76 +192,100 @@ func (e *Endpoint) collect(acc telegraf.Accumulator) error { // Load cache if needed e.init(p) - err = e.collectResourceType(p, ctx, "vm", acc, e.getVMs, v, e.Parent.VmInterval) + nameCache := make(map[string]string) + + // Collect cluster metrics + // + clusterMap, err := e.getClusters(ctx, v); if err != nil { return err } - - err = e.collectResourceType(p, ctx, "host", acc, e.getHosts, v, e.Parent.HostInterval) + for _, cluster := range clusterMap { + nameCache[cluster.ref.Reference().Value] = cluster.name + } + err = e.collectResourceType(p, ctx, "cluster", acc, clusterMap, nameCache, e.Parent.ClusterInterval) if err != nil { return err } - err = e.collectResourceType(p, ctx, "cluster", acc, e.getClusters, v, e.Parent.ClusterInterval) + // Collect host metrics + // + hostMap, err := e.getHosts(ctx, v) + if err != nil { + return err + } + for _, host := range hostMap { + nameCache[host.ref.Reference().Value] = host.name + } + err = e.collectResourceType(p, ctx, "host", acc, hostMap, nameCache, e.Parent.HostInterval) if err != nil { return err } - err = e.collectResourceType(p, ctx, "datastore", acc, e.getDatastores, v, e.Parent.DatastoreInterval) + // Collect vm metrics + // + vmMap, err := e.getVMs(ctx, v) + for _, vm := range vmMap { + nameCache[vm.ref.Reference().Value] = vm.name + } + err = e.collectResourceType(p, ctx, "vm", acc, vmMap, nameCache, e.Parent.VmInterval) if err != nil { return err } return nil } -func (e *Endpoint) getVMs(ctx context.Context, root *view.ContainerView) (map[string]types.ManagedObjectReference, error) { +func (e *Endpoint) getVMs(ctx context.Context, root *view.ContainerView) (objectMap, error) { var resources []mo.VirtualMachine - err := root.Retrieve(ctx, []string{"VirtualMachine"}, []string{"summary"}, &resources) + err := root.Retrieve(ctx, []string{"VirtualMachine"}, []string{"summary", "runtime.host"}, &resources) if err != nil { return nil, err } - m := make(map[string]types.ManagedObjectReference) + m := make(objectMap) for _, r := range resources { - m[r.Summary.Config.Name] = r.ExtensibleManagedObject.Reference() + m[r.ExtensibleManagedObject.Reference().Value] = objectRef{ + name: r.Summary.Config.Name, ref: r.ExtensibleManagedObject.Reference(), parentRef: r.Runtime.Host } } return m, nil } -func (e *Endpoint) getHosts(ctx context.Context, root *view.ContainerView) (map[string]types.ManagedObjectReference, error) { +func (e *Endpoint) getHosts(ctx context.Context, root *view.ContainerView) (objectMap, error) { var resources []mo.HostSystem - err := root.Retrieve(ctx, []string{"HostSystem"}, []string{"summary"}, &resources) + err := root.Retrieve(ctx, []string{"HostSystem"}, []string{"summary", "parent"}, &resources) if err != nil { return nil, err } - m := make(map[string]types.ManagedObjectReference) + m := make(objectMap) for _, r := range resources { - m[r.Summary.Config.Name] = r.ExtensibleManagedObject.Reference() + m[r.ExtensibleManagedObject.Reference().Value] = objectRef{ + name: r.Summary.Config.Name, ref: r.ExtensibleManagedObject.Reference(), parentRef: r.Parent } } return m, nil } -func (e *Endpoint) getClusters(ctx context.Context, root *view.ContainerView) (map[string]types.ManagedObjectReference, error) { +func (e *Endpoint) getClusters(ctx context.Context, root *view.ContainerView) (objectMap, error) { var resources []mo.ClusterComputeResource - err := root.Retrieve(ctx, []string{"ClusterComputeResource"}, []string{"summary"}, &resources) + err := root.Retrieve(ctx, []string{"ClusterComputeResource"}, []string{"summary", "name", "parent"}, &resources) if err != nil { return nil, err } - m := make(map[string]types.ManagedObjectReference) + m := make(objectMap) for _, r := range resources { - m[r.Name] = r.ExtensibleManagedObject.Reference() + m[r.ExtensibleManagedObject.Reference().Value] = objectRef{ + name: r.Name, ref: r.ExtensibleManagedObject.Reference(), parentRef: r.Parent } } return m, nil } -func (e *Endpoint) getDatastores(ctx context.Context, root *view.ContainerView) (map[string]types.ManagedObjectReference, error) { +func (e *Endpoint) getDatastores(ctx context.Context, root *view.ContainerView) (objectMap, error) { var resources []mo.Datastore - err := root.Retrieve(ctx, []string{"Datastore"}, []string{"summary"}, &resources) + err := root.Retrieve(ctx, []string{"Datastore"}, []string{"summary" }, &resources) if err != nil { return nil, err } - m := make(map[string]types.ManagedObjectReference) + m := make(objectMap) for _, r := range resources { - m[r.Summary.Name] = r.ExtensibleManagedObject.Reference() + m[r.Summary.Name] = objectRef{ ref:r.ExtensibleManagedObject.Reference(), parentRef: r.Parent } } return m, nil } @@ -285,6 +323,7 @@ func (v *VSphere) Init() { func (v *VSphere) Gather(acc telegraf.Accumulator) error { v.Init() results := make(chan error) + defer close(results) for _, ep := range v.endpoints { go func(target Endpoint) { results <- target.collect(acc) From 80c1baef4bf958b7bec1c95e3427d2da6db9aeb2 Mon Sep 17 00:00:00 2001 From: "pontus@rydin@gmail.com" Date: Mon, 6 Nov 2017 13:47:21 -0500 Subject: [PATCH 010/100] Reorganized code. Add parent as tag. --- plugins/inputs/vsphere/vsphere.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plugins/inputs/vsphere/vsphere.go b/plugins/inputs/vsphere/vsphere.go index 99e60d5495f68..41a208209e164 100644 --- a/plugins/inputs/vsphere/vsphere.go +++ b/plugins/inputs/vsphere/vsphere.go @@ -321,6 +321,7 @@ func (v *VSphere) Init() { } func (v *VSphere) Gather(acc telegraf.Accumulator) error { + start := time.Now() v.Init() results := make(chan error) defer close(results) @@ -337,6 +338,8 @@ func (v *VSphere) Gather(acc telegraf.Accumulator) error { finalErr = err } } + acc.AddCounter("telegraf.vsphere", + map[string]interface{}{ "gather.duration": time.Now().Sub(start).Seconds()}, nil, time.Now()) return finalErr } From a0f084be1b82aeb53c155a165760a02a61869a8e Mon Sep 17 00:00:00 2001 From: Pierre Tessier Date: Tue, 14 Nov 2017 09:45:55 -0500 Subject: [PATCH 011/100] changed Interval properties to durations --- Godeps | 1 + plugins/inputs/vsphere/vsphere.go | 136 ++++++++++++++++-------------- 2 files changed, 72 insertions(+), 65 deletions(-) diff --git a/Godeps b/Godeps index 02e20755e0906..0ffc38f48eab1 100644 --- a/Godeps +++ b/Godeps @@ -72,6 +72,7 @@ github.com/streadway/amqp 63795daa9a446c920826655f26ba31c81c860fd6 github.com/stretchr/objx 1a9d0bb9f541897e62256577b352fdbc1fb4fd94 github.com/stretchr/testify 4d4bfba8f1d1027c4fdbe371823030df51419987 github.com/vjeantet/grok d73e972b60935c7fec0b4ffbc904ed39ecaf7efe +github.com/vmware/govmomi 7d879bac14d09f2f2a45a0477c1e45fbf52240f5 github.com/wvanbergen/kafka bc265fedb9ff5b5c5d3c0fdcef4a819b3523d3ee github.com/wvanbergen/kazoo-go 968957352185472eacb69215fa3dbfcfdbac1096 github.com/yuin/gopher-lua 66c871e454fcf10251c61bf8eff02d0978cae75a diff --git a/plugins/inputs/vsphere/vsphere.go b/plugins/inputs/vsphere/vsphere.go index 41a208209e164..ad33cb7c97002 100644 --- a/plugins/inputs/vsphere/vsphere.go +++ b/plugins/inputs/vsphere/vsphere.go @@ -1,41 +1,42 @@ package vsphere import ( + "context" "github.com/influxdata/telegraf" - "github.com/vmware/govmomi/vim25/soap" + "github.com/influxdata/telegraf/internal" + "github.com/influxdata/telegraf/plugins/inputs" "github.com/vmware/govmomi" + "github.com/vmware/govmomi/performance" "github.com/vmware/govmomi/view" "github.com/vmware/govmomi/vim25/mo" - "context" - "github.com/vmware/govmomi/performance" + "github.com/vmware/govmomi/vim25/soap" "github.com/vmware/govmomi/vim25/types" - "github.com/influxdata/telegraf/plugins/inputs" - "time" "log" "net/url" + "time" ) type Endpoint struct { - Parent *VSphere - Url *url.URL + Parent *VSphere + Url *url.URL intervals []int32 - lastColl map[string]time.Time + lastColl map[string]time.Time } type VSphere struct { - Vcenters []string - VmInterval int32 - HostInterval int32 - ClusterInterval int32 - DatastoreInterval int32 - MaxSamples int32 - MaxQuery int32 - endpoints []Endpoint + Vcenters []string + VmInterval internal.Duration + HostInterval internal.Duration + ClusterInterval internal.Duration + DatastoreInterval internal.Duration + MaxSamples int32 + MaxQuery int32 + endpoints []Endpoint } type objectRef struct { - name string - ref types.ManagedObjectReference + name string + ref types.ManagedObjectReference parentRef *types.ManagedObjectReference //Pointer because it must be nillable } @@ -61,11 +62,12 @@ func (e *Endpoint) init(p *performance.Manager) error { } func (e *Endpoint) collectResourceType(p *performance.Manager, ctx context.Context, alias string, acc telegraf.Accumulator, - objects objectMap, nameCache map[string]string, interval int32) error { + objects objectMap, nameCache map[string]string, intervalDuration internal.Duration) error { - // Interval = -1 means collection for this metric was diabled, so don't even bother. + // Interval = 0 means collection for this metric was diabled, so don't even bother. // - if interval == -1 { + interval := int32(intervalDuration.Duration.Seconds()) + if interval == 0 { return nil } @@ -74,12 +76,12 @@ func (e *Endpoint) collectResourceType(p *performance.Manager, ctx context.Conte now := time.Now() nIntervals := int32(1) latest, hasLatest := e.lastColl[alias] - if (hasLatest) { + if hasLatest { elapsed := time.Now().Sub(latest).Seconds() if elapsed < float64(interval) { // No new data would be available. We're outta here! // - return nil; + return nil } nIntervals := int32(elapsed / (float64(interval))) if nIntervals > e.Parent.MaxSamples { @@ -93,20 +95,20 @@ func (e *Endpoint) collectResourceType(p *performance.Manager, ctx context.Conte start := time.Now() log.Printf("D! Query for %s returned %d objects", alias, len(objects)) pqs := make([]types.PerfQuerySpec, 0, e.Parent.MaxQuery) - total := 0; + total := 0 for _, object := range objects { pq := types.PerfQuerySpec{ - Entity: object.ref, - MaxSample: nIntervals, - MetricId: nil, + Entity: object.ref, + MaxSample: nIntervals, + MetricId: nil, IntervalId: interval, } - if(interval > 20) { + if interval > 20 { startTime := now.Add(-time.Duration(interval) * time.Second) pq.StartTime = &startTime pq.EndTime = &now } - if(e.Parent.MaxSamples > 1 && hasLatest) { + if e.Parent.MaxSamples > 1 && hasLatest { pq.StartTime = &latest pq.EndTime = &now } @@ -115,7 +117,7 @@ func (e *Endpoint) collectResourceType(p *performance.Manager, ctx context.Conte // Filled up a chunk or at end of data? Run a query with the collected objects // - if len(pqs) >= int(e.Parent.MaxQuery) || total == len(objects) { + if len(pqs) >= int(e.Parent.MaxQuery) || total == len(objects) { log.Printf("D! Querying %d objects of type %s for %s. Total processed: %d. Total objects %d\n", len(pqs), alias, e.Url.Host, total, len(objects)) metrics, err := p.Query(ctx, pqs) if err != nil { @@ -134,20 +136,19 @@ func (e *Endpoint) collectResourceType(p *performance.Manager, ctx context.Conte for _, v := range em.Value { name := v.Name for idx, value := range v.Value { - f := map[string]interface{} { name: value } + f := map[string]interface{}{name: value} objectName := nameCache[moid] parent := "" parentRef := objects[moid].parentRef - //log.Printf("Parentref=%s", parentRef) if parentRef != nil { parent = nameCache[parentRef.Value] } t := map[string]string{ - "vcenter": e.Url.Host, - "source": objectName, - "moid": moid, - "parent": parent} + "vcenter": e.Url.Host, + "hostname": objectName, + "moid": moid, + "parent": parent} if v.Instance != "" { t["instance"] = v.Instance } @@ -159,7 +160,6 @@ func (e *Endpoint) collectResourceType(p *performance.Manager, ctx context.Conte } } - log.Printf("D! Collection of %s took %v\n", alias, time.Now().Sub(start)) return nil } @@ -167,14 +167,14 @@ func (e *Endpoint) collectResourceType(p *performance.Manager, ctx context.Conte func (e *Endpoint) collect(acc telegraf.Accumulator) error { ctx := context.Background() c, err := govmomi.NewClient(ctx, e.Url, true) - if(err != nil) { + if err != nil { return err } defer c.Logout(ctx) m := view.NewManager(c.Client) - v, err := m.CreateContainerView(ctx, c.ServiceContent.RootFolder, []string{ }, true) + v, err := m.CreateContainerView(ctx, c.ServiceContent.RootFolder, []string{}, true) if err != nil { return err } @@ -183,6 +183,7 @@ func (e *Endpoint) collect(acc telegraf.Accumulator) error { p := performance.NewManager(c.Client) + //TODO: // This causes strange error messages in the vCenter console. Possibly due to a bug in // govmomi. We're commenting it out for now. Should be benign since the logout should // destroy all resources anyway. @@ -196,7 +197,7 @@ func (e *Endpoint) collect(acc telegraf.Accumulator) error { // Collect cluster metrics // - clusterMap, err := e.getClusters(ctx, v); + clusterMap, err := e.getClusters(ctx, v) if err != nil { return err } @@ -244,7 +245,7 @@ func (e *Endpoint) getVMs(ctx context.Context, root *view.ContainerView) (object m := make(objectMap) for _, r := range resources { m[r.ExtensibleManagedObject.Reference().Value] = objectRef{ - name: r.Summary.Config.Name, ref: r.ExtensibleManagedObject.Reference(), parentRef: r.Runtime.Host } + name: r.Summary.Config.Name, ref: r.ExtensibleManagedObject.Reference(), parentRef: r.Runtime.Host} } return m, nil } @@ -258,7 +259,7 @@ func (e *Endpoint) getHosts(ctx context.Context, root *view.ContainerView) (obje m := make(objectMap) for _, r := range resources { m[r.ExtensibleManagedObject.Reference().Value] = objectRef{ - name: r.Summary.Config.Name, ref: r.ExtensibleManagedObject.Reference(), parentRef: r.Parent } + name: r.Summary.Config.Name, ref: r.ExtensibleManagedObject.Reference(), parentRef: r.Parent} } return m, nil } @@ -272,20 +273,20 @@ func (e *Endpoint) getClusters(ctx context.Context, root *view.ContainerView) (o m := make(objectMap) for _, r := range resources { m[r.ExtensibleManagedObject.Reference().Value] = objectRef{ - name: r.Name, ref: r.ExtensibleManagedObject.Reference(), parentRef: r.Parent } + name: r.Name, ref: r.ExtensibleManagedObject.Reference(), parentRef: r.Parent} } return m, nil } func (e *Endpoint) getDatastores(ctx context.Context, root *view.ContainerView) (objectMap, error) { var resources []mo.Datastore - err := root.Retrieve(ctx, []string{"Datastore"}, []string{"summary" }, &resources) + err := root.Retrieve(ctx, []string{"Datastore"}, []string{"summary"}, &resources) if err != nil { return nil, err } m := make(objectMap) for _, r := range resources { - m[r.Summary.Name] = objectRef{ ref:r.ExtensibleManagedObject.Reference(), parentRef: r.Parent } + m[r.Summary.Name] = objectRef{ref: r.ExtensibleManagedObject.Reference(), parentRef: r.Parent} } return m, nil } @@ -303,26 +304,31 @@ func (v *VSphere) Description() string { return "Read metrics from VMware vCenter" } -func (v *VSphere) Init() { +func (v *VSphere) vSphereInit() { if v.endpoints != nil { return } + v.endpoints = make([]Endpoint, len(v.Vcenters)) for i, rawUrl := range v.Vcenters { - u, err := soap.ParseURL(rawUrl); - if(err != nil) { + u, err := soap.ParseURL(rawUrl) + if err != nil { log.Printf("E! Can't parse URL %s\n", rawUrl) } v.endpoints[i] = Endpoint{ - Url: u, - Parent: v, - lastColl: make(map[string]time.Time)} + Url: u, + Parent: v, + lastColl: make(map[string]time.Time), + } } } func (v *VSphere) Gather(acc telegraf.Accumulator) error { + + v.vSphereInit() + start := time.Now() - v.Init() + results := make(chan error) defer close(results) for _, ep := range v.endpoints { @@ -330,32 +336,32 @@ func (v *VSphere) Gather(acc telegraf.Accumulator) error { results <- target.collect(acc) }(ep) } + var finalErr error = nil for range v.endpoints { - err := <- results + err := <-results if err != nil { log.Println("E!", err) finalErr = err } } - acc.AddCounter("telegraf.vsphere", - map[string]interface{}{ "gather.duration": time.Now().Sub(start).Seconds()}, nil, time.Now()) + + // Add another counter to show how long it took to gather all the metrics on this cycle (can be used to tune # of vCenters and collection intervals per telegraf agent) + acc.AddCounter("vsphere", map[string]interface{}{"gather.duration": time.Now().Sub(start).Seconds()}, nil, time.Now()) + return finalErr } func init() { inputs.Add("vsphere", func() telegraf.Input { return &VSphere{ - Vcenters: []string {}, - VmInterval: 20, - HostInterval: 20, - ClusterInterval: 300, - DatastoreInterval: 300, - MaxSamples: 10, - MaxQuery: 64, + Vcenters: []string{}, + VmInterval: internal.Duration{Duration: time.Second * 150}, + HostInterval: internal.Duration{Duration: time.Second * 150}, + ClusterInterval: internal.Duration{Duration: time.Second * 300}, + DatastoreInterval: internal.Duration{Duration: time.Second * 300}, + MaxSamples: 10, + MaxQuery: 64, } }) } - - - From 313c89dcde996789c2217a7c1672f03b40e883d4 Mon Sep 17 00:00:00 2001 From: "pontus@rydin@gmail.com" Date: Tue, 14 Nov 2017 17:13:10 -0800 Subject: [PATCH 012/100] Started on concurrent object discovery. Not finished yet. --- plugins/inputs/vsphere/connection.go | 48 ++++++ plugins/inputs/vsphere/vsphere.go | 218 ++++++++++++++++----------- 2 files changed, 177 insertions(+), 89 deletions(-) create mode 100644 plugins/inputs/vsphere/connection.go diff --git a/plugins/inputs/vsphere/connection.go b/plugins/inputs/vsphere/connection.go new file mode 100644 index 0000000000000..aa036a7346fbf --- /dev/null +++ b/plugins/inputs/vsphere/connection.go @@ -0,0 +1,48 @@ +package vsphere + +import ( + "context" + "github.com/vmware/govmomi" + "github.com/vmware/govmomi/performance" + "github.com/vmware/govmomi/view" + "net/url" +) + +type Connection struct { + Client *govmomi.Client + Views *view.Manager + Root *view.ContainerView + Perf *performance.Manager +} + +func NewConnection(url *url.URL) (*Connection, error) { + ctx := context.Background() + c, err := govmomi.NewClient(ctx, url, true) + if err != nil { + return nil, err + } + + m := view.NewManager(c.Client) + v, err := m.CreateContainerView(ctx, c.ServiceContent.RootFolder, []string{}, true) + if err != nil { + return nil, err + } + p := performance.NewManager(c.Client) + + return &Connection{ + Client: c, + Views: m, + Root: v, + Perf: p, + }, nil +} + +func (c *Connection) Close() { + ctx := context.Background() + if c.Views != nil { + c.Views.Destroy(ctx) + } + if c.Client != nil { + c.Client.Logout(ctx) + } +} diff --git a/plugins/inputs/vsphere/vsphere.go b/plugins/inputs/vsphere/vsphere.go index ad33cb7c97002..ef75d4ddb9aff 100644 --- a/plugins/inputs/vsphere/vsphere.go +++ b/plugins/inputs/vsphere/vsphere.go @@ -5,7 +5,6 @@ import ( "github.com/influxdata/telegraf" "github.com/influxdata/telegraf/internal" "github.com/influxdata/telegraf/plugins/inputs" - "github.com/vmware/govmomi" "github.com/vmware/govmomi/performance" "github.com/vmware/govmomi/view" "github.com/vmware/govmomi/vim25/mo" @@ -13,25 +12,35 @@ import ( "github.com/vmware/govmomi/vim25/types" "log" "net/url" + "sync" "time" ) +type objectMap map[string]objectRef + type Endpoint struct { - Parent *VSphere - Url *url.URL - intervals []int32 - lastColl map[string]time.Time + Parent *VSphere + Url *url.URL + intervals []int32 + lastColl map[string]time.Time + hostMap objectMap + vmMap objectMap + clusterMap objectMap + nameCache map[string]string + bgObjectDisc bool + collectMux sync.RWMutex } type VSphere struct { - Vcenters []string - VmInterval internal.Duration - HostInterval internal.Duration - ClusterInterval internal.Duration - DatastoreInterval internal.Duration - MaxSamples int32 - MaxQuery int32 - endpoints []Endpoint + Vcenters []string + VmInterval internal.Duration + HostInterval internal.Duration + ClusterInterval internal.Duration + DatastoreInterval internal.Duration + ObjectDiscoveryInterval internal.Duration + MaxSamples int32 + MaxQuery int32 + endpoints []Endpoint } type objectRef struct { @@ -40,36 +49,110 @@ type objectRef struct { parentRef *types.ManagedObjectReference //Pointer because it must be nillable } -type objectMap map[string]objectRef - type InstanceMetrics map[string]map[string]interface{} -func (e *Endpoint) init(p *performance.Manager) error { - if e.intervals == nil { - // Load interval table - // - ctx := context.Background() - list, err := p.HistoricalInterval(ctx) - if err != nil { - return err - } - e.intervals = make([]int32, len(list)) - for k, i := range list { - e.intervals[k] = i.SamplingPeriod - } +func NewEndpoint(parent *VSphere, url *url.URL) Endpoint { + hostMap := make(objectMap) + vmMap := make(objectMap) + clusterMap := make(objectMap) + e := Endpoint{ + Url: url, + Parent: parent, + lastColl: make(map[string]time.Time), + hostMap: hostMap, + vmMap: vmMap, + clusterMap: clusterMap, + nameCache: make(map[string]string), + } + e.init() + return e +} + +func (e *Endpoint) init() error { + conn, err := NewConnection(e.Url) + if err != nil { + return err + } + defer conn.Close() + // Load interval table + // + ctx := context.Background() + list, err := conn.Perf.HistoricalInterval(ctx) + if err != nil { + return err + } + e.intervals = make([]int32, len(list)) + for k, i := range list { + e.intervals[k] = i.SamplingPeriod } return nil } +func (e *Endpoint) discover() error { + conn, err := NewConnection(e.Url) + if err != nil { + return err + } + + defer conn.Close() + + nameCache := make(map[string]string) + + // Discover clusters + // + ctx := context.Background() + clusterMap, err := e.getClusters(ctx, conn.Root) + if err != nil { + return err + } + for _, cluster := range clusterMap { + nameCache[cluster.ref.Reference().Value] = cluster.name + } + + // Discover hosts + // + hostMap, err := e.getHosts(ctx, conn.Root) + if err != nil { + return err + } + for _, host := range hostMap { + nameCache[host.ref.Reference().Value] = host.name + } + + // Discover VMs + // + vmMap, err := e.getVMs(ctx, conn.Root) + for _, vm := range vmMap { + nameCache[vm.ref.Reference().Value] = vm.name + } + + // Atomically swap maps + // + e.collectMux.Lock() + defer e.collectMux.Unlock() + + e.nameCache = nameCache + e.vmMap = vmMap + e.hostMap = hostMap + e.clusterMap = clusterMap + return nil +} + func (e *Endpoint) collectResourceType(p *performance.Manager, ctx context.Context, alias string, acc telegraf.Accumulator, objects objectMap, nameCache map[string]string, intervalDuration internal.Duration) error { + // Object maps may change, so we need to hold the collect lock + // + e.collectMux.RLock() + defer e.collectMux.RUnlock() + // Interval = 0 means collection for this metric was diabled, so don't even bother. // interval := int32(intervalDuration.Duration.Seconds()) - if interval == 0 { + if interval <= 0 { return nil } + log.Printf("D! Resource type: %s, Interval is %d", alias, interval) // Do we have new data yet? // @@ -121,6 +204,7 @@ func (e *Endpoint) collectResourceType(p *performance.Manager, ctx context.Conte log.Printf("D! Querying %d objects of type %s for %s. Total processed: %d. Total objects %d\n", len(pqs), alias, e.Url.Host, total, len(objects)) metrics, err := p.Query(ctx, pqs) if err != nil { + log.Printf("E! Error processing resource type %s", alias) return err } @@ -165,71 +249,30 @@ func (e *Endpoint) collectResourceType(p *performance.Manager, ctx context.Conte } func (e *Endpoint) collect(acc telegraf.Accumulator) error { - ctx := context.Background() - c, err := govmomi.NewClient(ctx, e.Url, true) + err := e.discover() if err != nil { return err } - defer c.Logout(ctx) - - m := view.NewManager(c.Client) - v, err := m.CreateContainerView(ctx, c.ServiceContent.RootFolder, []string{}, true) + conn, err := NewConnection(e.Url) if err != nil { return err } - defer v.Destroy(ctx) - - p := performance.NewManager(c.Client) - - //TODO: - // This causes strange error messages in the vCenter console. Possibly due to a bug in - // govmomi. We're commenting it out for now. Should be benign since the logout should - // destroy all resources anyway. - // - //defer p.Destroy(ctx) - - // Load cache if needed - e.init(p) - - nameCache := make(map[string]string) + defer conn.Close() - // Collect cluster metrics - // - clusterMap, err := e.getClusters(ctx, v) - if err != nil { - return err - } - for _, cluster := range clusterMap { - nameCache[cluster.ref.Reference().Value] = cluster.name - } - err = e.collectResourceType(p, ctx, "cluster", acc, clusterMap, nameCache, e.Parent.ClusterInterval) + ctx := context.Background() + err = e.collectResourceType(conn.Perf, ctx, "cluster", acc, e.clusterMap, e.nameCache, e.Parent.ClusterInterval) if err != nil { return err } - // Collect host metrics - // - hostMap, err := e.getHosts(ctx, v) - if err != nil { - return err - } - for _, host := range hostMap { - nameCache[host.ref.Reference().Value] = host.name - } - err = e.collectResourceType(p, ctx, "host", acc, hostMap, nameCache, e.Parent.HostInterval) + err = e.collectResourceType(conn.Perf, ctx, "host", acc, e.hostMap, e.nameCache, e.Parent.HostInterval) if err != nil { return err } - // Collect vm metrics - // - vmMap, err := e.getVMs(ctx, v) - for _, vm := range vmMap { - nameCache[vm.ref.Reference().Value] = vm.name - } - err = e.collectResourceType(p, ctx, "vm", acc, vmMap, nameCache, e.Parent.VmInterval) + err = e.collectResourceType(conn.Perf, ctx, "vm", acc, e.vmMap, e.nameCache, e.Parent.VmInterval) if err != nil { return err } @@ -315,11 +358,7 @@ func (v *VSphere) vSphereInit() { if err != nil { log.Printf("E! Can't parse URL %s\n", rawUrl) } - v.endpoints[i] = Endpoint{ - Url: u, - Parent: v, - lastColl: make(map[string]time.Time), - } + v.endpoints[i] = NewEndpoint(v, u) } } @@ -355,13 +394,14 @@ func (v *VSphere) Gather(acc telegraf.Accumulator) error { func init() { inputs.Add("vsphere", func() telegraf.Input { return &VSphere{ - Vcenters: []string{}, - VmInterval: internal.Duration{Duration: time.Second * 150}, - HostInterval: internal.Duration{Duration: time.Second * 150}, - ClusterInterval: internal.Duration{Duration: time.Second * 300}, - DatastoreInterval: internal.Duration{Duration: time.Second * 300}, - MaxSamples: 10, - MaxQuery: 64, + Vcenters: []string{}, + VmInterval: internal.Duration{Duration: time.Second * 20}, + HostInterval: internal.Duration{Duration: time.Second * 20}, + ClusterInterval: internal.Duration{Duration: time.Second * 300}, + DatastoreInterval: internal.Duration{Duration: time.Second * 300}, + ObjectDiscoveryInterval: internal.Duration{Duration: time.Second * 300}, + MaxSamples: 10, + MaxQuery: 64, } }) } From f8355a2641207e02e074b39728e1f653e68d2ea7 Mon Sep 17 00:00:00 2001 From: Pierre Tessier Date: Tue, 14 Nov 2017 21:12:36 -0500 Subject: [PATCH 013/100] use sync.WaitGroup to manage async --- plugins/inputs/vsphere/vsphere.go | 59 ++++++++++++++----------------- 1 file changed, 27 insertions(+), 32 deletions(-) diff --git a/plugins/inputs/vsphere/vsphere.go b/plugins/inputs/vsphere/vsphere.go index ef75d4ddb9aff..b363d7414a99b 100644 --- a/plugins/inputs/vsphere/vsphere.go +++ b/plugins/inputs/vsphere/vsphere.go @@ -32,15 +32,15 @@ type Endpoint struct { } type VSphere struct { - Vcenters []string - VmInterval internal.Duration - HostInterval internal.Duration - ClusterInterval internal.Duration - DatastoreInterval internal.Duration - ObjectDiscoveryInterval internal.Duration - MaxSamples int32 - MaxQuery int32 - endpoints []Endpoint + Vcenters []string + VmSamplingInterval internal.Duration + HostSamplingInterval internal.Duration + ClusterSamplingInterval internal.Duration + DatastoreSamplingInterval internal.Duration + ObjDiscoverySamplingInterval internal.Duration + MaxSamples int32 + MaxQuery int32 + endpoints []Endpoint } type objectRef struct { @@ -262,17 +262,17 @@ func (e *Endpoint) collect(acc telegraf.Accumulator) error { defer conn.Close() ctx := context.Background() - err = e.collectResourceType(conn.Perf, ctx, "cluster", acc, e.clusterMap, e.nameCache, e.Parent.ClusterInterval) + err = e.collectResourceType(conn.Perf, ctx, "cluster", acc, e.clusterMap, e.nameCache, e.Parent.ClusterSamplingInterval) if err != nil { return err } - err = e.collectResourceType(conn.Perf, ctx, "host", acc, e.hostMap, e.nameCache, e.Parent.HostInterval) + err = e.collectResourceType(conn.Perf, ctx, "host", acc, e.hostMap, e.nameCache, e.Parent.HostSamplingInterval) if err != nil { return err } - err = e.collectResourceType(conn.Perf, ctx, "vm", acc, e.vmMap, e.nameCache, e.Parent.VmInterval) + err = e.collectResourceType(conn.Perf, ctx, "vm", acc, e.vmMap, e.nameCache, e.Parent.VmSamplingInterval) if err != nil { return err } @@ -368,40 +368,35 @@ func (v *VSphere) Gather(acc telegraf.Accumulator) error { start := time.Now() - results := make(chan error) - defer close(results) + var wg sync.WaitGroup + for _, ep := range v.endpoints { + wg.Add(1) go func(target Endpoint) { - results <- target.collect(acc) + defer wg.Done() + acc.AddError(target.collect(acc)) }(ep) } - var finalErr error = nil - for range v.endpoints { - err := <-results - if err != nil { - log.Println("E!", err) - finalErr = err - } - } + wg.Wait() // Add another counter to show how long it took to gather all the metrics on this cycle (can be used to tune # of vCenters and collection intervals per telegraf agent) acc.AddCounter("vsphere", map[string]interface{}{"gather.duration": time.Now().Sub(start).Seconds()}, nil, time.Now()) - return finalErr + return nil } func init() { inputs.Add("vsphere", func() telegraf.Input { return &VSphere{ - Vcenters: []string{}, - VmInterval: internal.Duration{Duration: time.Second * 20}, - HostInterval: internal.Duration{Duration: time.Second * 20}, - ClusterInterval: internal.Duration{Duration: time.Second * 300}, - DatastoreInterval: internal.Duration{Duration: time.Second * 300}, - ObjectDiscoveryInterval: internal.Duration{Duration: time.Second * 300}, - MaxSamples: 10, - MaxQuery: 64, + Vcenters: []string{}, + VmSamplingInterval: internal.Duration{Duration: time.Second * 20}, + HostSamplingInterval: internal.Duration{Duration: time.Second * 20}, + ClusterSamplingInterval: internal.Duration{Duration: time.Second * 300}, + DatastoreSamplingInterval: internal.Duration{Duration: time.Second * 300}, + ObjDiscoverySamplingInterval: internal.Duration{Duration: time.Second * 300}, + MaxSamples: 10, + MaxQuery: 64, } }) } From db90cfffb297fa71e64614230a2d06eee04423e9 Mon Sep 17 00:00:00 2001 From: Pierre Tessier Date: Tue, 14 Nov 2017 22:09:18 -0500 Subject: [PATCH 014/100] datastore, and instance context --- plugins/inputs/vsphere/vsphere.go | 123 +++++++++++++++++++----------- 1 file changed, 78 insertions(+), 45 deletions(-) diff --git a/plugins/inputs/vsphere/vsphere.go b/plugins/inputs/vsphere/vsphere.go index b363d7414a99b..254518a13aa9d 100644 --- a/plugins/inputs/vsphere/vsphere.go +++ b/plugins/inputs/vsphere/vsphere.go @@ -12,6 +12,7 @@ import ( "github.com/vmware/govmomi/vim25/types" "log" "net/url" + "strings" "sync" "time" ) @@ -26,21 +27,21 @@ type Endpoint struct { hostMap objectMap vmMap objectMap clusterMap objectMap + datastoreMap objectMap nameCache map[string]string bgObjectDisc bool collectMux sync.RWMutex } type VSphere struct { - Vcenters []string - VmSamplingInterval internal.Duration - HostSamplingInterval internal.Duration - ClusterSamplingInterval internal.Duration - DatastoreSamplingInterval internal.Duration - ObjDiscoverySamplingInterval internal.Duration - MaxSamples int32 - MaxQuery int32 - endpoints []Endpoint + Vcenters []string + VmSamplingPeriod internal.Duration + HostSamplingPeriod internal.Duration + ClusterSamplingPeriod internal.Duration + DatastoreSamplingPeriod internal.Duration + ObjectDiscoveryInterval internal.Duration + ObjectsPerQuery int32 + endpoints []Endpoint } type objectRef struct { @@ -55,14 +56,16 @@ func NewEndpoint(parent *VSphere, url *url.URL) Endpoint { hostMap := make(objectMap) vmMap := make(objectMap) clusterMap := make(objectMap) + datastoreMap := make(objectMap) e := Endpoint{ - Url: url, - Parent: parent, - lastColl: make(map[string]time.Time), - hostMap: hostMap, - vmMap: vmMap, - clusterMap: clusterMap, - nameCache: make(map[string]string), + Url: url, + Parent: parent, + lastColl: make(map[string]time.Time), + hostMap: hostMap, + vmMap: vmMap, + clusterMap: clusterMap, + datastoreMap: datastoreMap, + nameCache: make(map[string]string), } e.init() return e @@ -122,16 +125,28 @@ func (e *Endpoint) discover() error { // Discover VMs // vmMap, err := e.getVMs(ctx, conn.Root) + if err != nil { + return err + } for _, vm := range vmMap { nameCache[vm.ref.Reference().Value] = vm.name } + datastoreMap, err := e.getDatastores(ctx, conn.Root) + if err != nil { + return err + } + for _, datastore := range datastoreMap { + nameCache[datastore.ref.Reference().Value] = datastore.name + } + // Atomically swap maps // e.collectMux.Lock() defer e.collectMux.Unlock() e.nameCache = nameCache + e.datastoreMap = datastoreMap e.vmMap = vmMap e.hostMap = hostMap e.clusterMap = clusterMap @@ -139,7 +154,7 @@ func (e *Endpoint) discover() error { } func (e *Endpoint) collectResourceType(p *performance.Manager, ctx context.Context, alias string, acc telegraf.Accumulator, - objects objectMap, nameCache map[string]string, intervalDuration internal.Duration) error { + objects objectMap, nameCache map[string]string, intervalDuration internal.Duration, isRealTime bool) error { // Object maps may change, so we need to hold the collect lock // @@ -152,12 +167,11 @@ func (e *Endpoint) collectResourceType(p *performance.Manager, ctx context.Conte if interval <= 0 { return nil } - log.Printf("D! Resource type: %s, Interval is %d", alias, interval) + log.Printf("D! Resource type: %s, sampling interval is: %d", alias, interval) // Do we have new data yet? // now := time.Now() - nIntervals := int32(1) latest, hasLatest := e.lastColl[alias] if hasLatest { elapsed := time.Now().Sub(latest).Seconds() @@ -166,41 +180,35 @@ func (e *Endpoint) collectResourceType(p *performance.Manager, ctx context.Conte // return nil } - nIntervals := int32(elapsed / (float64(interval))) - if nIntervals > e.Parent.MaxSamples { - nIntervals = e.Parent.MaxSamples - } } e.lastColl[alias] = now - log.Printf("D! Collecting %d intervals for %s", nIntervals, alias) + log.Printf("D! Collecting for %s", alias) fullAlias := "vsphere." + alias start := time.Now() log.Printf("D! Query for %s returned %d objects", alias, len(objects)) - pqs := make([]types.PerfQuerySpec, 0, e.Parent.MaxQuery) + pqs := make([]types.PerfQuerySpec, 0, e.Parent.ObjectsPerQuery) total := 0 for _, object := range objects { pq := types.PerfQuerySpec{ Entity: object.ref, - MaxSample: nIntervals, + MaxSample: 1, MetricId: nil, IntervalId: interval, } - if interval > 20 { + + if !isRealTime { startTime := now.Add(-time.Duration(interval) * time.Second) pq.StartTime = &startTime pq.EndTime = &now } - if e.Parent.MaxSamples > 1 && hasLatest { - pq.StartTime = &latest - pq.EndTime = &now - } + pqs = append(pqs, pq) total++ // Filled up a chunk or at end of data? Run a query with the collected objects // - if len(pqs) >= int(e.Parent.MaxQuery) || total == len(objects) { + if len(pqs) >= int(e.Parent.ObjectsPerQuery) || total == len(objects) { log.Printf("D! Querying %d objects of type %s for %s. Total processed: %d. Total objects %d\n", len(pqs), alias, e.Url.Host, total, len(objects)) metrics, err := p.Query(ctx, pqs) if err != nil { @@ -233,14 +241,34 @@ func (e *Endpoint) collectResourceType(p *performance.Manager, ctx context.Conte "hostname": objectName, "moid": moid, "parent": parent} + if v.Instance != "" { - t["instance"] = v.Instance + if strings.HasPrefix(name, "cpu.") { + t["cpu"] = v.Instance + } else if strings.HasPrefix(name, "net.") { + t["interface"] = v.Instance + } else if strings.HasPrefix(name, "sys.resource") { + t["resource"] = v.Instance + } else if strings.HasPrefix(name, "disk.") || strings.HasPrefix(name, "virtualDisk.") { + t["disk"] = v.Instance + } else if strings.HasPrefix(name, "datastore.") { + t["datastore"] = v.Instance + } else if strings.HasPrefix(name, "storagePath.") { + t["path"] = v.Instance + } else if strings.HasPrefix(name, "storageAdapter.") { + t["adapter"] = v.Instance + } else if strings.HasPrefix(name, "vflashModule.") { + t["module"] = v.Instance + } else { + // default to instance + t["instance"] = v.Instance + } } acc.AddFields(fullAlias, f, t, em.SampleInfo[idx].Timestamp) } } } - pqs = make([]types.PerfQuerySpec, 0, e.Parent.MaxQuery) + pqs = make([]types.PerfQuerySpec, 0, e.Parent.ObjectsPerQuery) } } @@ -262,20 +290,26 @@ func (e *Endpoint) collect(acc telegraf.Accumulator) error { defer conn.Close() ctx := context.Background() - err = e.collectResourceType(conn.Perf, ctx, "cluster", acc, e.clusterMap, e.nameCache, e.Parent.ClusterSamplingInterval) + err = e.collectResourceType(conn.Perf, ctx, "cluster", acc, e.clusterMap, e.nameCache, e.Parent.ClusterSamplingPeriod, false) if err != nil { return err } - err = e.collectResourceType(conn.Perf, ctx, "host", acc, e.hostMap, e.nameCache, e.Parent.HostSamplingInterval) + err = e.collectResourceType(conn.Perf, ctx, "host", acc, e.hostMap, e.nameCache, e.Parent.HostSamplingPeriod, true) if err != nil { return err } - err = e.collectResourceType(conn.Perf, ctx, "vm", acc, e.vmMap, e.nameCache, e.Parent.VmSamplingInterval) + err = e.collectResourceType(conn.Perf, ctx, "vm", acc, e.vmMap, e.nameCache, e.Parent.VmSamplingPeriod, true) if err != nil { return err } + + err = e.collectResourceType(conn.Perf, ctx, "datastore", acc, e.datastoreMap, e.nameCache, e.Parent.DatastoreSamplingPeriod, false) + if err != nil { + return err + } + return nil } @@ -389,14 +423,13 @@ func (v *VSphere) Gather(acc telegraf.Accumulator) error { func init() { inputs.Add("vsphere", func() telegraf.Input { return &VSphere{ - Vcenters: []string{}, - VmSamplingInterval: internal.Duration{Duration: time.Second * 20}, - HostSamplingInterval: internal.Duration{Duration: time.Second * 20}, - ClusterSamplingInterval: internal.Duration{Duration: time.Second * 300}, - DatastoreSamplingInterval: internal.Duration{Duration: time.Second * 300}, - ObjDiscoverySamplingInterval: internal.Duration{Duration: time.Second * 300}, - MaxSamples: 10, - MaxQuery: 64, + Vcenters: []string{}, + VmSamplingPeriod: internal.Duration{Duration: time.Second * 20}, + HostSamplingPeriod: internal.Duration{Duration: time.Second * 20}, + ClusterSamplingPeriod: internal.Duration{Duration: time.Second * 300}, + DatastoreSamplingPeriod: internal.Duration{Duration: time.Second * 300}, + ObjectDiscoveryInterval: internal.Duration{Duration: time.Second * 300}, + ObjectsPerQuery: 500, } }) } From 14ff78d20c3939bead0503366ede93fa9336251f Mon Sep 17 00:00:00 2001 From: "pontus@rydin@gmail.com" Date: Wed, 15 Nov 2017 13:47:40 -0800 Subject: [PATCH 015/100] Run object discovery in the background. --- plugins/inputs/vsphere/connection.go | 1 + plugins/inputs/vsphere/vsphere.go | 61 +++++++++++++++++----------- 2 files changed, 38 insertions(+), 24 deletions(-) diff --git a/plugins/inputs/vsphere/connection.go b/plugins/inputs/vsphere/connection.go index aa036a7346fbf..7b0173413c58b 100644 --- a/plugins/inputs/vsphere/connection.go +++ b/plugins/inputs/vsphere/connection.go @@ -6,6 +6,7 @@ import ( "github.com/vmware/govmomi/performance" "github.com/vmware/govmomi/view" "net/url" + "sync" ) type Connection struct { diff --git a/plugins/inputs/vsphere/vsphere.go b/plugins/inputs/vsphere/vsphere.go index 254518a13aa9d..435ce6f22be54 100644 --- a/plugins/inputs/vsphere/vsphere.go +++ b/plugins/inputs/vsphere/vsphere.go @@ -20,17 +20,16 @@ import ( type objectMap map[string]objectRef type Endpoint struct { - Parent *VSphere - Url *url.URL - intervals []int32 - lastColl map[string]time.Time - hostMap objectMap - vmMap objectMap - clusterMap objectMap - datastoreMap objectMap - nameCache map[string]string - bgObjectDisc bool - collectMux sync.RWMutex + Parent *VSphere + Url *url.URL + lastColl map[string]time.Time + hostMap objectMap + vmMap objectMap + clusterMap objectMap + datastoreMap objectMap + nameCache map[string]string + discoveryTicker *time.Ticker + collectMux sync.RWMutex } type VSphere struct { @@ -77,16 +76,24 @@ func (e *Endpoint) init() error { return err } defer conn.Close() - // Load interval table + + // Start background discovery if requested // - ctx := context.Background() - list, err := conn.Perf.HistoricalInterval(ctx) - if err != nil { - return err - } - e.intervals = make([]int32, len(list)) - for k, i := range list { - e.intervals[k] = i.SamplingPeriod + if e.Parent.ObjectDiscoveryInterval.Duration.Seconds() > 0 { + e.discoveryTicker = time.NewTicker(e.Parent.ObjectDiscoveryInterval.Duration) + go func() { + for _ = range e.discoveryTicker.C { + err := e.discover() + if err != nil { + log.Printf("E! Error in discovery") + log.Println(err) + } + } + }() + + // Run an initial disovery. + // + e.discover() } return nil } @@ -150,12 +157,17 @@ func (e *Endpoint) discover() error { e.vmMap = vmMap e.hostMap = hostMap e.clusterMap = clusterMap + + log.Printf("D! Discovered %d objects\n", len(e.nameCache)) + return nil } func (e *Endpoint) collectResourceType(p *performance.Manager, ctx context.Context, alias string, acc telegraf.Accumulator, objects objectMap, nameCache map[string]string, intervalDuration internal.Duration, isRealTime bool) error { + p. + // Object maps may change, so we need to hold the collect lock // e.collectMux.RLock() @@ -277,9 +289,11 @@ func (e *Endpoint) collectResourceType(p *performance.Manager, ctx context.Conte } func (e *Endpoint) collect(acc telegraf.Accumulator) error { - err := e.discover() - if err != nil { - return err + if e.Parent.ObjectDiscoveryInterval.Duration.Seconds() == 0 { + err := e.discover() + if err != nil { + return err + } } conn, err := NewConnection(e.Url) @@ -309,7 +323,6 @@ func (e *Endpoint) collect(acc telegraf.Accumulator) error { if err != nil { return err } - return nil } From efac9b9af0d35b6e6d19e40c672e2dc204400af2 Mon Sep 17 00:00:00 2001 From: Pierre Tessier Date: Thu, 16 Nov 2017 23:25:58 -0500 Subject: [PATCH 016/100] added gather_ properties --- plugins/inputs/vsphere/connection.go | 1 - plugins/inputs/vsphere/vsphere.go | 149 ++++++++++++++++----------- 2 files changed, 90 insertions(+), 60 deletions(-) diff --git a/plugins/inputs/vsphere/connection.go b/plugins/inputs/vsphere/connection.go index 7b0173413c58b..aa036a7346fbf 100644 --- a/plugins/inputs/vsphere/connection.go +++ b/plugins/inputs/vsphere/connection.go @@ -6,7 +6,6 @@ import ( "github.com/vmware/govmomi/performance" "github.com/vmware/govmomi/view" "net/url" - "sync" ) type Connection struct { diff --git a/plugins/inputs/vsphere/vsphere.go b/plugins/inputs/vsphere/vsphere.go index 435ce6f22be54..42a3158431d26 100644 --- a/plugins/inputs/vsphere/vsphere.go +++ b/plugins/inputs/vsphere/vsphere.go @@ -17,6 +17,21 @@ import ( "time" ) +type VSphere struct { + Vcenters []string + GatherClusters bool + GatherHosts bool + GatherVms bool + GatherDatastores bool + ObjectsPerQuery int32 + VmSamplingPeriod internal.Duration + HostSamplingPeriod internal.Duration + ClusterSamplingPeriod internal.Duration + DatastoreSamplingPeriod internal.Duration + ObjectDiscoveryInterval internal.Duration + endpoints []Endpoint +} + type objectMap map[string]objectRef type Endpoint struct { @@ -32,17 +47,6 @@ type Endpoint struct { collectMux sync.RWMutex } -type VSphere struct { - Vcenters []string - VmSamplingPeriod internal.Duration - HostSamplingPeriod internal.Duration - ClusterSamplingPeriod internal.Duration - DatastoreSamplingPeriod internal.Duration - ObjectDiscoveryInterval internal.Duration - ObjectsPerQuery int32 - endpoints []Endpoint -} - type objectRef struct { name string ref types.ManagedObjectReference @@ -82,16 +86,15 @@ func (e *Endpoint) init() error { if e.Parent.ObjectDiscoveryInterval.Duration.Seconds() > 0 { e.discoveryTicker = time.NewTicker(e.Parent.ObjectDiscoveryInterval.Duration) go func() { - for _ = range e.discoveryTicker.C { + for range e.discoveryTicker.C { err := e.discover() if err != nil { - log.Printf("E! Error in discovery") - log.Println(err) + log.Printf("E! Error in discovery %v", err) } } }() - // Run an initial disovery. + // Run an initial discovery. // e.discover() } @@ -105,46 +108,60 @@ func (e *Endpoint) discover() error { } defer conn.Close() + ctx := context.Background() nameCache := make(map[string]string) + clusterMap := e.clusterMap + hostMap := e.hostMap + vmMap := e.vmMap + datastoreMap := e.datastoreMap // Discover clusters // - ctx := context.Background() - clusterMap, err := e.getClusters(ctx, conn.Root) - if err != nil { - return err - } - for _, cluster := range clusterMap { - nameCache[cluster.ref.Reference().Value] = cluster.name + if e.Parent.GatherClusters { + clusterMap, err = e.getClusters(ctx, conn.Root) + if err != nil { + return err + } + for _, cluster := range clusterMap { + nameCache[cluster.ref.Reference().Value] = cluster.name + } } // Discover hosts // - hostMap, err := e.getHosts(ctx, conn.Root) - if err != nil { - return err - } - for _, host := range hostMap { - nameCache[host.ref.Reference().Value] = host.name + if e.Parent.GatherHosts { + hostMap, err = e.getHosts(ctx, conn.Root) + if err != nil { + return err + } + for _, host := range hostMap { + nameCache[host.ref.Reference().Value] = host.name + } } // Discover VMs // - vmMap, err := e.getVMs(ctx, conn.Root) - if err != nil { - return err - } - for _, vm := range vmMap { - nameCache[vm.ref.Reference().Value] = vm.name + if e.Parent.GatherVms { + vmMap, err = e.getVMs(ctx, conn.Root) + if err != nil { + return err + } + for _, vm := range vmMap { + nameCache[vm.ref.Reference().Value] = vm.name + } } - datastoreMap, err := e.getDatastores(ctx, conn.Root) - if err != nil { - return err - } - for _, datastore := range datastoreMap { - nameCache[datastore.ref.Reference().Value] = datastore.name + // Discover Datastores + // + if e.Parent.GatherDatastores { + datastoreMap, err = e.getDatastores(ctx, conn.Root) + if err != nil { + return err + } + for _, datastore := range datastoreMap { + nameCache[datastore.ref.Reference().Value] = datastore.name + } } // Atomically swap maps @@ -166,8 +183,6 @@ func (e *Endpoint) discover() error { func (e *Endpoint) collectResourceType(p *performance.Manager, ctx context.Context, alias string, acc telegraf.Accumulator, objects objectMap, nameCache map[string]string, intervalDuration internal.Duration, isRealTime bool) error { - p. - // Object maps may change, so we need to hold the collect lock // e.collectMux.RLock() @@ -302,27 +317,36 @@ func (e *Endpoint) collect(acc telegraf.Accumulator) error { } defer conn.Close() - ctx := context.Background() - err = e.collectResourceType(conn.Perf, ctx, "cluster", acc, e.clusterMap, e.nameCache, e.Parent.ClusterSamplingPeriod, false) - if err != nil { - return err + + if e.Parent.GatherClusters { + err = e.collectResourceType(conn.Perf, ctx, "cluster", acc, e.clusterMap, e.nameCache, e.Parent.ClusterSamplingPeriod, false) + if err != nil { + return err + } } - err = e.collectResourceType(conn.Perf, ctx, "host", acc, e.hostMap, e.nameCache, e.Parent.HostSamplingPeriod, true) - if err != nil { - return err + if e.Parent.GatherHosts { + err = e.collectResourceType(conn.Perf, ctx, "host", acc, e.hostMap, e.nameCache, e.Parent.HostSamplingPeriod, true) + if err != nil { + return err + } } - err = e.collectResourceType(conn.Perf, ctx, "vm", acc, e.vmMap, e.nameCache, e.Parent.VmSamplingPeriod, true) - if err != nil { - return err + if e.Parent.GatherVms { + err = e.collectResourceType(conn.Perf, ctx, "vm", acc, e.vmMap, e.nameCache, e.Parent.VmSamplingPeriod, true) + if err != nil { + return err + } } - err = e.collectResourceType(conn.Perf, ctx, "datastore", acc, e.datastoreMap, e.nameCache, e.Parent.DatastoreSamplingPeriod, false) - if err != nil { - return err + if e.Parent.GatherDatastores { + err = e.collectResourceType(conn.Perf, ctx, "datastore", acc, e.datastoreMap, e.nameCache, e.Parent.DatastoreSamplingPeriod, false) + if err != nil { + return err + } } + return nil } @@ -437,12 +461,19 @@ func init() { inputs.Add("vsphere", func() telegraf.Input { return &VSphere{ Vcenters: []string{}, - VmSamplingPeriod: internal.Duration{Duration: time.Second * 20}, - HostSamplingPeriod: internal.Duration{Duration: time.Second * 20}, + + GatherClusters: true, + GatherHosts: true, + GatherVms: true, + GatherDatastores: true, + + ObjectsPerQuery: 500, + ObjectDiscoveryInterval: internal.Duration{Duration: time.Second * 300}, + ClusterSamplingPeriod: internal.Duration{Duration: time.Second * 300}, + HostSamplingPeriod: internal.Duration{Duration: time.Second * 20}, + VmSamplingPeriod: internal.Duration{Duration: time.Second * 20}, DatastoreSamplingPeriod: internal.Duration{Duration: time.Second * 300}, - ObjectDiscoveryInterval: internal.Duration{Duration: time.Second * 300}, - ObjectsPerQuery: 500, } }) } From bad3279cba0b6d3efdd16f19d6b6562751a8c065 Mon Sep 17 00:00:00 2001 From: Pierre Tessier Date: Thu, 16 Nov 2017 23:26:10 -0500 Subject: [PATCH 017/100] initial readme --- plugins/inputs/vsphere/README.MD | 46 ++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 plugins/inputs/vsphere/README.MD diff --git a/plugins/inputs/vsphere/README.MD b/plugins/inputs/vsphere/README.MD new file mode 100644 index 0000000000000..6d3b55c715399 --- /dev/null +++ b/plugins/inputs/vsphere/README.MD @@ -0,0 +1,46 @@ +# VMware vSphere Input Plugin + +The VMware vSphere plugin uses the vSphere API to gather metrics from multiple vCenters. + +* Clusters +* Hosts +* VMs +* Data stores + +## Configuration + +``` +# Read metrics from one or many vCenters +[[inputs.vsphere]] + ## List of vCenter URLs, including credentials. + ## ://:@/sdk + ## Note the "@" characted must be escaped as %40 + ## e.g. + ## https://administrator%40vsphere.local:password@vcenter.local/sdk + # + vcenters = [ "https://administrator%40vsphere.local:password@vcenter.local/sdk" ] + + ## gather metrics from Clusters (default: true) + gather_clusters = true + + ## gather metrics from Hosts (default: true) + gather_hosts = true + + ## gather metrics from VMs (default: true) + gather_vms = true + + ## gather metrics from Data stores (default: true) + gather_datastores = true + + ## Number of objects to retreive per query (default: 500) + objects_per_query = 500 + + ## Object Discover duration is the interval to wait between collection cycles to (re)discover objects subject to gathering metrics (default: 300s) + object_discover_interval = 300s + + ## Optional: if you have customized the default sampling period in vcenter you can specify them below + cluster_sampling_period = 300s + host_sampling_period = 20s + vm_sampling_period = 20s + datastore_sampling_period = 300s +``` From 61d37c8c974c6deb989eac287de619307b953b07 Mon Sep 17 00:00:00 2001 From: "pontus@rydin@gmail.com" Date: Fri, 17 Nov 2017 18:00:23 -0500 Subject: [PATCH 018/100] Initial commit of include/exclude. Basic testing done. --- plugins/inputs/vsphere/vsphere.go | 115 +++++++++++++++++++++++++----- 1 file changed, 98 insertions(+), 17 deletions(-) diff --git a/plugins/inputs/vsphere/vsphere.go b/plugins/inputs/vsphere/vsphere.go index 42a3158431d26..726d42a69c617 100644 --- a/plugins/inputs/vsphere/vsphere.go +++ b/plugins/inputs/vsphere/vsphere.go @@ -2,6 +2,7 @@ package vsphere import ( "context" + "github.com/gobwas/glob" "github.com/influxdata/telegraf" "github.com/influxdata/telegraf/internal" "github.com/influxdata/telegraf/plugins/inputs" @@ -19,16 +20,24 @@ import ( type VSphere struct { Vcenters []string - GatherClusters bool - GatherHosts bool - GatherVms bool - GatherDatastores bool + GatherClusters bool + GatherHosts bool + GatherVms bool + GatherDatastores bool ObjectsPerQuery int32 VmSamplingPeriod internal.Duration HostSamplingPeriod internal.Duration ClusterSamplingPeriod internal.Duration DatastoreSamplingPeriod internal.Duration ObjectDiscoveryInterval internal.Duration + VmMetrics []string + HostMetrics []string + ClusterMetrics []string + DatastoreMetrics []string + vmMetricIds []types.PerfMetricId + hostMetricIds []types.PerfMetricId + clusterMetricIds []types.PerfMetricId + datastoreMetricIds []types.PerfMetricId endpoints []Endpoint } @@ -81,6 +90,30 @@ func (e *Endpoint) init() error { } defer conn.Close() + // Load metric IDs if specified + // + ctx := context.Background() + metricMap, err := conn.Perf.CounterInfoByName(ctx) + if err != nil { + return err + } + e.Parent.vmMetricIds, err = resolveMetricWildcards(metricMap, e.Parent.VmMetrics) + if err != nil { + return err + } + e.Parent.hostMetricIds, err = resolveMetricWildcards(metricMap, e.Parent.HostMetrics) + if err != nil { + return err + } + e.Parent.clusterMetricIds, err = resolveMetricWildcards(metricMap, e.Parent.ClusterMetrics) + if err != nil { + return err + } + e.Parent.datastoreMetricIds, err = resolveMetricWildcards(metricMap, e.Parent.DatastoreMetrics) + if err != nil { + return err + } + // Start background discovery if requested // if e.Parent.ObjectDiscoveryInterval.Duration.Seconds() > 0 { @@ -101,6 +134,44 @@ func (e *Endpoint) init() error { return nil } +func resolveMetricWildcards(metricMap map[string]*types.PerfCounterInfo, wildcards []string) ([]types.PerfMetricId, error) { + // Nothing specified assumes we're looking at everything + // + if wildcards == nil { + return nil, nil + } + tmpMap := make(map[string]types.PerfMetricId) + for _, pattern := range wildcards { + exclude := false + if pattern[0] == '!' { + pattern = pattern[1:] + exclude = true + } + p, err := glob.Compile(pattern) + if err != nil { + return nil, err + } + for name, info := range metricMap { + if p.Match(name) { + if exclude { + delete(tmpMap, name) + log.Printf("D! excluded %s", name) + } else { + tmpMap[name] = types.PerfMetricId{CounterId: info.Key} + log.Printf("D! included %s", name) + } + } + } + } + result := make([]types.PerfMetricId, len(tmpMap)) + idx := 0 + for _, id := range tmpMap { + result[idx] = id + idx++ + } + return result, nil +} + func (e *Endpoint) discover() error { conn, err := NewConnection(e.Url) if err != nil { @@ -181,7 +252,7 @@ func (e *Endpoint) discover() error { } func (e *Endpoint) collectResourceType(p *performance.Manager, ctx context.Context, alias string, acc telegraf.Accumulator, - objects objectMap, nameCache map[string]string, intervalDuration internal.Duration, isRealTime bool) error { + objects objectMap, nameCache map[string]string, intervalDuration internal.Duration, isRealTime bool, metricIds []types.PerfMetricId) error { // Object maps may change, so we need to hold the collect lock // @@ -220,7 +291,7 @@ func (e *Endpoint) collectResourceType(p *performance.Manager, ctx context.Conte pq := types.PerfQuerySpec{ Entity: object.ref, MaxSample: 1, - MetricId: nil, + MetricId: metricIds, IntervalId: interval, } @@ -320,28 +391,32 @@ func (e *Endpoint) collect(acc telegraf.Accumulator) error { ctx := context.Background() if e.Parent.GatherClusters { - err = e.collectResourceType(conn.Perf, ctx, "cluster", acc, e.clusterMap, e.nameCache, e.Parent.ClusterSamplingPeriod, false) + err = e.collectResourceType(conn.Perf, ctx, "cluster", acc, e.clusterMap, e.nameCache, + e.Parent.ClusterSamplingPeriod, false, e.Parent.clusterMetricIds) if err != nil { return err } } if e.Parent.GatherHosts { - err = e.collectResourceType(conn.Perf, ctx, "host", acc, e.hostMap, e.nameCache, e.Parent.HostSamplingPeriod, true) + err = e.collectResourceType(conn.Perf, ctx, "host", acc, e.hostMap, e.nameCache, + e.Parent.HostSamplingPeriod, true, e.Parent.hostMetricIds) if err != nil { return err } } if e.Parent.GatherVms { - err = e.collectResourceType(conn.Perf, ctx, "vm", acc, e.vmMap, e.nameCache, e.Parent.VmSamplingPeriod, true) + err = e.collectResourceType(conn.Perf, ctx, "vm", acc, e.vmMap, e.nameCache, e.Parent.VmSamplingPeriod, + true, e.Parent.vmMetricIds) if err != nil { return err } } if e.Parent.GatherDatastores { - err = e.collectResourceType(conn.Perf, ctx, "datastore", acc, e.datastoreMap, e.nameCache, e.Parent.DatastoreSamplingPeriod, false) + err = e.collectResourceType(conn.Perf, ctx, "datastore", acc, e.datastoreMap, e.nameCache, + e.Parent.DatastoreSamplingPeriod, false, e.Parent.datastoreMetricIds) if err != nil { return err } @@ -419,6 +494,7 @@ func (v *VSphere) Description() string { } func (v *VSphere) vSphereInit() { + log.Printf("v.endpoints: %s", v.endpoints) if v.endpoints != nil { return } @@ -460,20 +536,25 @@ func (v *VSphere) Gather(acc telegraf.Accumulator) error { func init() { inputs.Add("vsphere", func() telegraf.Input { return &VSphere{ - Vcenters: []string{}, + Vcenters: []string{}, - GatherClusters: true, - GatherHosts: true, - GatherVms: true, - GatherDatastores: true, + GatherClusters: true, + GatherHosts: true, + GatherVms: true, + GatherDatastores: true, - ObjectsPerQuery: 500, - ObjectDiscoveryInterval: internal.Duration{Duration: time.Second * 300}, + ObjectsPerQuery: 500, + ObjectDiscoveryInterval: internal.Duration{Duration: time.Second * 300}, ClusterSamplingPeriod: internal.Duration{Duration: time.Second * 300}, HostSamplingPeriod: internal.Duration{Duration: time.Second * 20}, VmSamplingPeriod: internal.Duration{Duration: time.Second * 20}, DatastoreSamplingPeriod: internal.Duration{Duration: time.Second * 300}, + + HostMetrics: nil, + VmMetrics: nil, + ClusterMetrics: nil, + DatastoreMetrics: nil, } }) } From ac72acabe33e897a38cb118ec3a4ee864e18eda5 Mon Sep 17 00:00:00 2001 From: "pontus@rydin@gmail.com" Date: Sun, 19 Nov 2017 09:26:12 -0500 Subject: [PATCH 019/100] Moved object ID maps to Endpoint. --- plugins/inputs/vsphere/connection.go | 1 - plugins/inputs/vsphere/vsphere.go | 44 ++++++++++++++-------------- 2 files changed, 22 insertions(+), 23 deletions(-) diff --git a/plugins/inputs/vsphere/connection.go b/plugins/inputs/vsphere/connection.go index aa036a7346fbf..561b8c75e5d44 100644 --- a/plugins/inputs/vsphere/connection.go +++ b/plugins/inputs/vsphere/connection.go @@ -21,7 +21,6 @@ func NewConnection(url *url.URL) (*Connection, error) { if err != nil { return nil, err } - m := view.NewManager(c.Client) v, err := m.CreateContainerView(ctx, c.ServiceContent.RootFolder, []string{}, true) if err != nil { diff --git a/plugins/inputs/vsphere/vsphere.go b/plugins/inputs/vsphere/vsphere.go index 726d42a69c617..46c4b4dca5109 100644 --- a/plugins/inputs/vsphere/vsphere.go +++ b/plugins/inputs/vsphere/vsphere.go @@ -34,26 +34,26 @@ type VSphere struct { HostMetrics []string ClusterMetrics []string DatastoreMetrics []string - vmMetricIds []types.PerfMetricId - hostMetricIds []types.PerfMetricId - clusterMetricIds []types.PerfMetricId - datastoreMetricIds []types.PerfMetricId endpoints []Endpoint } type objectMap map[string]objectRef type Endpoint struct { - Parent *VSphere - Url *url.URL - lastColl map[string]time.Time - hostMap objectMap - vmMap objectMap - clusterMap objectMap - datastoreMap objectMap - nameCache map[string]string - discoveryTicker *time.Ticker - collectMux sync.RWMutex + Parent *VSphere + Url *url.URL + lastColl map[string]time.Time + hostMap objectMap + vmMap objectMap + clusterMap objectMap + datastoreMap objectMap + nameCache map[string]string + vmMetricIds []types.PerfMetricId + hostMetricIds []types.PerfMetricId + clusterMetricIds []types.PerfMetricId + datastoreMetricIds []types.PerfMetricId + discoveryTicker *time.Ticker + collectMux sync.RWMutex } type objectRef struct { @@ -97,19 +97,19 @@ func (e *Endpoint) init() error { if err != nil { return err } - e.Parent.vmMetricIds, err = resolveMetricWildcards(metricMap, e.Parent.VmMetrics) + e.vmMetricIds, err = resolveMetricWildcards(metricMap, e.Parent.VmMetrics) if err != nil { return err } - e.Parent.hostMetricIds, err = resolveMetricWildcards(metricMap, e.Parent.HostMetrics) + e.hostMetricIds, err = resolveMetricWildcards(metricMap, e.Parent.HostMetrics) if err != nil { return err } - e.Parent.clusterMetricIds, err = resolveMetricWildcards(metricMap, e.Parent.ClusterMetrics) + e.clusterMetricIds, err = resolveMetricWildcards(metricMap, e.Parent.ClusterMetrics) if err != nil { return err } - e.Parent.datastoreMetricIds, err = resolveMetricWildcards(metricMap, e.Parent.DatastoreMetrics) + e.datastoreMetricIds, err = resolveMetricWildcards(metricMap, e.Parent.DatastoreMetrics) if err != nil { return err } @@ -392,7 +392,7 @@ func (e *Endpoint) collect(acc telegraf.Accumulator) error { if e.Parent.GatherClusters { err = e.collectResourceType(conn.Perf, ctx, "cluster", acc, e.clusterMap, e.nameCache, - e.Parent.ClusterSamplingPeriod, false, e.Parent.clusterMetricIds) + e.Parent.ClusterSamplingPeriod, false, e.clusterMetricIds) if err != nil { return err } @@ -400,7 +400,7 @@ func (e *Endpoint) collect(acc telegraf.Accumulator) error { if e.Parent.GatherHosts { err = e.collectResourceType(conn.Perf, ctx, "host", acc, e.hostMap, e.nameCache, - e.Parent.HostSamplingPeriod, true, e.Parent.hostMetricIds) + e.Parent.HostSamplingPeriod, true, e.hostMetricIds) if err != nil { return err } @@ -408,7 +408,7 @@ func (e *Endpoint) collect(acc telegraf.Accumulator) error { if e.Parent.GatherVms { err = e.collectResourceType(conn.Perf, ctx, "vm", acc, e.vmMap, e.nameCache, e.Parent.VmSamplingPeriod, - true, e.Parent.vmMetricIds) + true, e.vmMetricIds) if err != nil { return err } @@ -416,7 +416,7 @@ func (e *Endpoint) collect(acc telegraf.Accumulator) error { if e.Parent.GatherDatastores { err = e.collectResourceType(conn.Perf, ctx, "datastore", acc, e.datastoreMap, e.nameCache, - e.Parent.DatastoreSamplingPeriod, false, e.Parent.datastoreMetricIds) + e.Parent.DatastoreSamplingPeriod, false, e.datastoreMetricIds) if err != nil { return err } From 09e3c5131418d114ccb1a6783cd0160be64df910 Mon Sep 17 00:00:00 2001 From: Pierre Tessier Date: Tue, 21 Nov 2017 23:15:00 -0500 Subject: [PATCH 020/100] readme and datastore source name --- plugins/inputs/vsphere/README.MD | 110 +++++++++++++++++++++++---- plugins/inputs/vsphere/connection.go | 1 + plugins/inputs/vsphere/vsphere.go | 49 ++++++++---- 3 files changed, 134 insertions(+), 26 deletions(-) diff --git a/plugins/inputs/vsphere/README.MD b/plugins/inputs/vsphere/README.MD index 6d3b55c715399..35e3284d20d57 100644 --- a/plugins/inputs/vsphere/README.MD +++ b/plugins/inputs/vsphere/README.MD @@ -21,26 +21,110 @@ The VMware vSphere plugin uses the vSphere API to gather metrics from multiple v vcenters = [ "https://administrator%40vsphere.local:password@vcenter.local/sdk" ] ## gather metrics from Clusters (default: true) - gather_clusters = true + # gather_clusters = true + # cluster_metrics = ["*"] ## gather metrics from Hosts (default: true) - gather_hosts = true + # gather_hosts = true + # host_metrics = ["*"] ## gather metrics from VMs (default: true) - gather_vms = true + # gather_vms = true + # vm_metrics = ["*"] ## gather metrics from Data stores (default: true) - gather_datastores = true + # gather_datastores = true + # datastore_metrics = ["*"] - ## Number of objects to retreive per query (default: 500) - objects_per_query = 500 + ## number of objects to retreive per query (default: 500) + # objects_per_query = 500 - ## Object Discover duration is the interval to wait between collection cycles to (re)discover objects subject to gathering metrics (default: 300s) - object_discover_interval = 300s + ## the interval before (re)discovering objects subject to metrics collection (default: 300s) + # object_discover_interval = 300s - ## Optional: if you have customized the default sampling period in vcenter you can specify them below - cluster_sampling_period = 300s - host_sampling_period = 20s - vm_sampling_period = 20s - datastore_sampling_period = 300s + ## Optional for configurations with customized sampling periods in vcenter + # cluster_sampling_period = 300s + # host_sampling_period = 20s + # vm_sampling_period = 20s + # datastore_sampling_period = 300s ``` + +### Metrics Selection + +vSphere metrics are available in 4 categories: Clusters, Hosts, VMs, and Datastores. Each category can be enabled +independently, and for each category a list with wildcards can be applied to filter metrics. If the _metrics +property is not specified all metrics will be included for the category. Items can be specified with standard glob +style wildcards (*, ?). Any item can be negated with an exclamation mark (!) as the first character. Metrics will be +checked against every item in the list that applies, meaning you can include multiple metrics with the first item, and +negate selectively within that list on a subsequent item. + +This configuration will only include all metrics under the host category that start with `cpu.*` but will not include +`cpu.idle.summation`. + +``` + host_metrics = ["cpu.*", "!cpu.idle.summation"] + +``` + +### Sampling Periods + +The plugin is called using the standard interval period. To maintain optimal performance, metrics for a given category +will only be gathered, if that category's sampling period has passed since the last gather. Cluster and Datastore metrics +have significantly longer sampling periods by default (300s vs 20s). The *_sampling_period properties should only be +changed if you have changed these default settings within vCenter server. + + +## Measurements & Fields + +- Cluster Stats + - Cluster services: CPU, memory, failover + - CPU: total, usage + - Memory: consumed, total, vmmemctl + - VM operations: # changes, clone, create, deploy, destroy, power, reboot, reconfigure, register, reset, shutdown, standby, vmotion +- Host Stats: + - CPU: total, usage, cost, mhz + - Datastore: iops, latency, read/write bytes, # reads/writes + - Disk: commands, latency, kernel reads/writes, # reads/writes, queues + - Memory: total, usage, active, latency, swap, shared, vmmemctl + - Network: broadcast, bytes, dropped, errors, multicast, packets, usage + - Power: energy, usage, capacity + - Res CPU: active, max, running + - Storage Adapter: commands, latency, # reads/writes + - Storage Path: commands, latency, # reads/writes + - System Resources: cpu active, cpu max, cpu running, cpu usage, mem allocated, mem consumed, mem shared, swap + - System: uptime + - Flash Module: active VMDKs +- VM Stats: + - CPU: demand, usage, readiness, cost, mhz + - Datastore: latency, # reads/writes + - Disk: commands, latency, # reads/writes, provisioned, usage + - Memory: granted, usage, active, swap, vmmemctl + - Network: broadcast, bytes, dropped, multicast, packets, usage + - Power: energy, usage + - Res CPU: active, max, running + - System: operating system uptime, uptime + - Virtual Disk: seeks, # reads/writes, latency, load +- Datastore stats: + - Disk: Capacity, provisioned, used + +## Tags +- cpu stats for Host and VM + - cpu (cpu core - not all CPU fields will have this tag) +- datastore stats for Host and VM + - datastore (id of datastore) +- disk stats for Host and VM + - disk (name of disk) +- disk.used.capacity for Datastore + - disk (type of disk) +- net stats for Host and VM + - interface (name of network interface) +- storageAdapter stats for Host + - adapter (name of storage adapter) +- storagePath stats for Host + - path (id of storage path) +- sys.resource* stats for Host + - resource (resource type) +- vflashModule stats for Host + - module (name of flash module) +- virtualDisk stats for VM + - disk (name of virtual disk) diff --git a/plugins/inputs/vsphere/connection.go b/plugins/inputs/vsphere/connection.go index 561b8c75e5d44..253e5eefef64f 100644 --- a/plugins/inputs/vsphere/connection.go +++ b/plugins/inputs/vsphere/connection.go @@ -40,6 +40,7 @@ func (c *Connection) Close() { ctx := context.Background() if c.Views != nil { c.Views.Destroy(ctx) + } if c.Client != nil { c.Client.Logout(ctx) diff --git a/plugins/inputs/vsphere/vsphere.go b/plugins/inputs/vsphere/vsphere.go index 46c4b4dca5109..0782509b16c6f 100644 --- a/plugins/inputs/vsphere/vsphere.go +++ b/plugins/inputs/vsphere/vsphere.go @@ -343,25 +343,28 @@ func (e *Endpoint) collectResourceType(p *performance.Manager, ctx context.Conte if v.Instance != "" { if strings.HasPrefix(name, "cpu.") { t["cpu"] = v.Instance - } else if strings.HasPrefix(name, "net.") { - t["interface"] = v.Instance - } else if strings.HasPrefix(name, "sys.resource") { - t["resource"] = v.Instance - } else if strings.HasPrefix(name, "disk.") || strings.HasPrefix(name, "virtualDisk.") { - t["disk"] = v.Instance } else if strings.HasPrefix(name, "datastore.") { t["datastore"] = v.Instance - } else if strings.HasPrefix(name, "storagePath.") { - t["path"] = v.Instance + } else if strings.HasPrefix(name, "disk.") { + t["disk"] = cleanDiskTag(v.Instance) + } else if strings.HasPrefix(name, "net.") { + t["interface"] = v.Instance } else if strings.HasPrefix(name, "storageAdapter.") { t["adapter"] = v.Instance + } else if strings.HasPrefix(name, "storagePath.") { + t["path"] = v.Instance + } else if strings.HasPrefix(name, "sys.resource") { + t["resource"] = v.Instance } else if strings.HasPrefix(name, "vflashModule.") { t["module"] = v.Instance + } else if strings.HasPrefix(name, "virtualDisk.") { + t["disk"] = v.Instance } else { // default to instance t["instance"] = v.Instance } } + acc.AddFields(fullAlias, f, t, em.SampleInfo[idx].Timestamp) } } @@ -375,6 +378,9 @@ func (e *Endpoint) collectResourceType(p *performance.Manager, ctx context.Conte } func (e *Endpoint) collect(acc telegraf.Accumulator) error { + + start := time.Now() + if e.Parent.ObjectDiscoveryInterval.Duration.Seconds() == 0 { err := e.discover() if err != nil { @@ -407,8 +413,8 @@ func (e *Endpoint) collect(acc telegraf.Accumulator) error { } if e.Parent.GatherVms { - err = e.collectResourceType(conn.Perf, ctx, "vm", acc, e.vmMap, e.nameCache, e.Parent.VmSamplingPeriod, - true, e.vmMetricIds) + err = e.collectResourceType(conn.Perf, ctx, "vm", acc, e.vmMap, e.nameCache, + e.Parent.VmSamplingPeriod, true, e.vmMetricIds) if err != nil { return err } @@ -422,6 +428,8 @@ func (e *Endpoint) collect(acc telegraf.Accumulator) error { } } + acc.AddCounter("vsphere", map[string]interface{}{"gather.duration": time.Now().Sub(start).Seconds()}, map[string]string{"vcenter": e.Url.Host}, time.Now()) + return nil } @@ -475,11 +483,27 @@ func (e *Endpoint) getDatastores(ctx context.Context, root *view.ContainerView) } m := make(objectMap) for _, r := range resources { - m[r.Summary.Name] = objectRef{ref: r.ExtensibleManagedObject.Reference(), parentRef: r.Parent} + m[r.Summary.Name] = objectRef{ + name: r.Summary.Name, ref: r.ExtensibleManagedObject.Reference(), parentRef: r.Parent} } return m, nil } +func cleanDiskTag(disk string) string { + if strings.HasPrefix(disk, "<") { + i := strings.Index(disk, ">") + if i > -1 { + s1 := disk[1:i] + s2 := disk[i+1:] + if s1 == s2 { + return s1 + } + } + } + + return disk +} + var sampleConfig = ` ## List of vCenter URLs, including credentials. Note the "@" characted must be escaped as %40 # vcenters = [ "https://administrator%40vsphere.local:VMware1!@vcenter.local/sdk" ] @@ -494,7 +518,6 @@ func (v *VSphere) Description() string { } func (v *VSphere) vSphereInit() { - log.Printf("v.endpoints: %s", v.endpoints) if v.endpoints != nil { return } @@ -551,9 +574,9 @@ func init() { VmSamplingPeriod: internal.Duration{Duration: time.Second * 20}, DatastoreSamplingPeriod: internal.Duration{Duration: time.Second * 300}, + ClusterMetrics: nil, HostMetrics: nil, VmMetrics: nil, - ClusterMetrics: nil, DatastoreMetrics: nil, } }) From c8b616b951207668f1233f61661348197ff6e773 Mon Sep 17 00:00:00 2001 From: Pierre Tessier Date: Tue, 21 Nov 2017 23:48:18 -0500 Subject: [PATCH 021/100] typo --- plugins/inputs/vsphere/README.MD | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/inputs/vsphere/README.MD b/plugins/inputs/vsphere/README.MD index 35e3284d20d57..fa4fcc6d90aff 100644 --- a/plugins/inputs/vsphere/README.MD +++ b/plugins/inputs/vsphere/README.MD @@ -68,7 +68,7 @@ This configuration will only include all metrics under the host category that st ### Sampling Periods -The plugin is called using the standard interval period. To maintain optimal performance, metrics for a given category +The plugin is called using the standard interval property. To maintain optimal performance, metrics for a given category will only be gathered, if that category's sampling period has passed since the last gather. Cluster and Datastore metrics have significantly longer sampling periods by default (300s vs 20s). The *_sampling_period properties should only be changed if you have changed these default settings within vCenter server. From 8a11e3724c88ab657225f4e9ba51a137275b664c Mon Sep 17 00:00:00 2001 From: Pierre Tessier Date: Wed, 22 Nov 2017 09:55:22 -0500 Subject: [PATCH 022/100] gauges and grammar --- plugins/inputs/vsphere/README.MD | 10 +++++----- plugins/inputs/vsphere/vsphere.go | 14 ++++++++++---- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/plugins/inputs/vsphere/README.MD b/plugins/inputs/vsphere/README.MD index fa4fcc6d90aff..48709835c3725 100644 --- a/plugins/inputs/vsphere/README.MD +++ b/plugins/inputs/vsphere/README.MD @@ -1,6 +1,6 @@ # VMware vSphere Input Plugin -The VMware vSphere plugin uses the vSphere API to gather metrics from multiple vCenters. +The VMware vSphere plugin uses the vSphere API to gather metrics from multiple vCenter servers. * Clusters * Hosts @@ -58,7 +58,7 @@ style wildcards (*, ?). Any item can be negated with an exclamation mark (!) as checked against every item in the list that applies, meaning you can include multiple metrics with the first item, and negate selectively within that list on a subsequent item. -This configuration will only include all metrics under the host category that start with `cpu.*` but will not include +A configuration to include all metrics under the host category that start with `cpu.*` but will not include `cpu.idle.summation`. ``` @@ -68,10 +68,10 @@ This configuration will only include all metrics under the host category that st ### Sampling Periods -The plugin is called using the standard interval property. To maintain optimal performance, metrics for a given category +The plugin is called using the standard interval property. To maintain optimal performance, metrics for a given category will only be gathered, if that category's sampling period has passed since the last gather. Cluster and Datastore metrics -have significantly longer sampling periods by default (300s vs 20s). The *_sampling_period properties should only be -changed if you have changed these default settings within vCenter server. +have significantly longer sampling periods by default (300s vs 20s). The *_sampling_period properties should only be changed if you have changed these default +settings within vCenter server. ## Measurements & Fields diff --git a/plugins/inputs/vsphere/vsphere.go b/plugins/inputs/vsphere/vsphere.go index 0782509b16c6f..d6fa0a7427cdd 100644 --- a/plugins/inputs/vsphere/vsphere.go +++ b/plugins/inputs/vsphere/vsphere.go @@ -338,7 +338,8 @@ func (e *Endpoint) collectResourceType(p *performance.Manager, ctx context.Conte "vcenter": e.Url.Host, "hostname": objectName, "moid": moid, - "parent": parent} + "parent": parent, + } if v.Instance != "" { if strings.HasPrefix(name, "cpu.") { @@ -381,6 +382,8 @@ func (e *Endpoint) collect(acc telegraf.Accumulator) error { start := time.Now() + // If discovery interval is disabled (0), discover on each collection cycle + // if e.Parent.ObjectDiscoveryInterval.Duration.Seconds() == 0 { err := e.discover() if err != nil { @@ -428,7 +431,9 @@ func (e *Endpoint) collect(acc telegraf.Accumulator) error { } } - acc.AddCounter("vsphere", map[string]interface{}{"gather.duration": time.Now().Sub(start).Seconds()}, map[string]string{"vcenter": e.Url.Host}, time.Now()) + // Add gauge to show how long it took to gather all the metrics on this cycle for a specific vcenter/endpoint + // + acc.AddGauge("vsphere", map[string]interface{}{"gather.duration": time.Now().Sub(start).Seconds()}, map[string]string{"vcenter": e.Url.Host}, time.Now()) return nil } @@ -550,8 +555,9 @@ func (v *VSphere) Gather(acc telegraf.Accumulator) error { wg.Wait() - // Add another counter to show how long it took to gather all the metrics on this cycle (can be used to tune # of vCenters and collection intervals per telegraf agent) - acc.AddCounter("vsphere", map[string]interface{}{"gather.duration": time.Now().Sub(start).Seconds()}, nil, time.Now()) + // Add gauge to show how long it took to gather all the metrics on this cycle + // + acc.AddGauge("vsphere", map[string]interface{}{"gather.duration": time.Now().Sub(start).Seconds()}, nil, time.Now()) return nil } From 213241b66c7364b5b67f7c5ccaee24632e45693c Mon Sep 17 00:00:00 2001 From: Pierre Tessier Date: Wed, 22 Nov 2017 15:38:33 -0500 Subject: [PATCH 023/100] parrallel on vSphere.init --- plugins/inputs/vsphere/README.MD | 10 +++++----- plugins/inputs/vsphere/vsphere.go | 17 +++++++++++++---- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/plugins/inputs/vsphere/README.MD b/plugins/inputs/vsphere/README.MD index 48709835c3725..4bf7090db4aff 100644 --- a/plugins/inputs/vsphere/README.MD +++ b/plugins/inputs/vsphere/README.MD @@ -40,13 +40,13 @@ The VMware vSphere plugin uses the vSphere API to gather metrics from multiple v # objects_per_query = 500 ## the interval before (re)discovering objects subject to metrics collection (default: 300s) - # object_discover_interval = 300s + # object_discovery_interval = "300s" ## Optional for configurations with customized sampling periods in vcenter - # cluster_sampling_period = 300s - # host_sampling_period = 20s - # vm_sampling_period = 20s - # datastore_sampling_period = 300s + # cluster_sampling_period = "300s" + # host_sampling_period = "20s" + # vm_sampling_period = "20s" + # datastore_sampling_period = "300s" ``` ### Metrics Selection diff --git a/plugins/inputs/vsphere/vsphere.go b/plugins/inputs/vsphere/vsphere.go index d6fa0a7427cdd..6d3f753c72110 100644 --- a/plugins/inputs/vsphere/vsphere.go +++ b/plugins/inputs/vsphere/vsphere.go @@ -527,14 +527,23 @@ func (v *VSphere) vSphereInit() { return } + var wg sync.WaitGroup + v.endpoints = make([]Endpoint, len(v.Vcenters)) for i, rawUrl := range v.Vcenters { u, err := soap.ParseURL(rawUrl) if err != nil { log.Printf("E! Can't parse URL %s\n", rawUrl) } - v.endpoints[i] = NewEndpoint(v, u) + + wg.Add(1) + go func() { + defer wg.Done() + v.endpoints[i] = NewEndpoint(v, u) + }() } + + wg.Wait() } func (v *VSphere) Gather(acc telegraf.Accumulator) error { @@ -547,10 +556,10 @@ func (v *VSphere) Gather(acc telegraf.Accumulator) error { for _, ep := range v.endpoints { wg.Add(1) - go func(target Endpoint) { + go func() { defer wg.Done() - acc.AddError(target.collect(acc)) - }(ep) + acc.AddError(ep.collect(acc)) + }() } wg.Wait() From e2c211ec8588fc95715b7f13ed5d4d2d5a4bdf12 Mon Sep 17 00:00:00 2001 From: Pierre Tessier Date: Sat, 25 Nov 2017 00:15:04 -0500 Subject: [PATCH 024/100] refactored endpoint, connection timeout --- plugins/inputs/vsphere/connection.go | 4 +- plugins/inputs/vsphere/endpoint.go | 490 +++++++++++++++++++++++++ plugins/inputs/vsphere/vsphere.go | 530 ++------------------------- 3 files changed, 521 insertions(+), 503 deletions(-) create mode 100644 plugins/inputs/vsphere/endpoint.go diff --git a/plugins/inputs/vsphere/connection.go b/plugins/inputs/vsphere/connection.go index 253e5eefef64f..53901f174ac93 100644 --- a/plugins/inputs/vsphere/connection.go +++ b/plugins/inputs/vsphere/connection.go @@ -6,6 +6,7 @@ import ( "github.com/vmware/govmomi/performance" "github.com/vmware/govmomi/view" "net/url" + "github.com/influxdata/telegraf/internal" ) type Connection struct { @@ -15,9 +16,10 @@ type Connection struct { Perf *performance.Manager } -func NewConnection(url *url.URL) (*Connection, error) { +func NewConnection(url *url.URL, timeout internal.Duration) (*Connection, error) { ctx := context.Background() c, err := govmomi.NewClient(ctx, url, true) + c.Timeout = timeout.Duration if err != nil { return nil, err } diff --git a/plugins/inputs/vsphere/endpoint.go b/plugins/inputs/vsphere/endpoint.go new file mode 100644 index 0000000000000..7891706569fef --- /dev/null +++ b/plugins/inputs/vsphere/endpoint.go @@ -0,0 +1,490 @@ +package vsphere + +import ( + "context" + "github.com/gobwas/glob" + "github.com/influxdata/telegraf" + "github.com/influxdata/telegraf/internal" + "github.com/vmware/govmomi/performance" + "github.com/vmware/govmomi/view" + "github.com/vmware/govmomi/vim25/mo" + "github.com/vmware/govmomi/vim25/types" + "log" + "net/url" + "strings" + "sync" + "time" +) + +type objectMap map[string]objectRef + +type Endpoint struct { + Parent *VSphere + Url *url.URL + lastColl map[string]time.Time + hostMap objectMap + vmMap objectMap + clusterMap objectMap + datastoreMap objectMap + nameCache map[string]string + vmMetricIds []types.PerfMetricId + hostMetricIds []types.PerfMetricId + clusterMetricIds []types.PerfMetricId + datastoreMetricIds []types.PerfMetricId + discoveryTicker *time.Ticker + collectMux sync.RWMutex +} + +type objectRef struct { + name string + ref types.ManagedObjectReference + parentRef *types.ManagedObjectReference //Pointer because it must be nillable +} + +type InstanceMetrics map[string]map[string]interface{} + +func NewEndpoint(parent *VSphere, url *url.URL) Endpoint { + hostMap := make(objectMap) + vmMap := make(objectMap) + clusterMap := make(objectMap) + datastoreMap := make(objectMap) + e := Endpoint{ + Url: url, + Parent: parent, + lastColl: make(map[string]time.Time), + hostMap: hostMap, + vmMap: vmMap, + clusterMap: clusterMap, + datastoreMap: datastoreMap, + nameCache: make(map[string]string), + } + e.init() + return e +} + +func (e *Endpoint) init() error { + conn, err := NewConnection(e.Url, e.Parent.Timeout) + if err != nil { + return err + } + defer conn.Close() + + // Load metric IDs if specified + // + ctx := context.Background() + metricMap, err := conn.Perf.CounterInfoByName(ctx) + if err != nil { + return err + } + e.vmMetricIds, err = resolveMetricWildcards(metricMap, e.Parent.VmMetrics) + if err != nil { + return err + } + e.hostMetricIds, err = resolveMetricWildcards(metricMap, e.Parent.HostMetrics) + if err != nil { + return err + } + e.clusterMetricIds, err = resolveMetricWildcards(metricMap, e.Parent.ClusterMetrics) + if err != nil { + return err + } + e.datastoreMetricIds, err = resolveMetricWildcards(metricMap, e.Parent.DatastoreMetrics) + if err != nil { + return err + } + + // Start background discovery if requested + // + if e.Parent.ObjectDiscoveryInterval.Duration.Seconds() > 0 { + e.discoveryTicker = time.NewTicker(e.Parent.ObjectDiscoveryInterval.Duration) + go func() { + for range e.discoveryTicker.C { + err := e.discover() + if err != nil { + log.Printf("E! Error in discovery %v", err) + } + } + }() + + // Run an initial discovery. + // + e.discover() + } + return nil +} + +func resolveMetricWildcards(metricMap map[string]*types.PerfCounterInfo, wildcards []string) ([]types.PerfMetricId, error) { + // Nothing specified assumes we're looking at everything + // + if wildcards == nil { + return nil, nil + } + tmpMap := make(map[string]types.PerfMetricId) + for _, pattern := range wildcards { + exclude := false + if pattern[0] == '!' { + pattern = pattern[1:] + exclude = true + } + p, err := glob.Compile(pattern) + if err != nil { + return nil, err + } + for name, info := range metricMap { + if p.Match(name) { + if exclude { + delete(tmpMap, name) + log.Printf("D! excluded %s", name) + } else { + tmpMap[name] = types.PerfMetricId{CounterId: info.Key} + log.Printf("D! included %s", name) + } + } + } + } + result := make([]types.PerfMetricId, len(tmpMap)) + idx := 0 + for _, id := range tmpMap { + result[idx] = id + idx++ + } + return result, nil +} + +func (e *Endpoint) discover() error { + conn, err := NewConnection(e.Url, e.Parent.Timeout) + if err != nil { + return err + } + + defer conn.Close() + ctx := context.Background() + + nameCache := make(map[string]string) + clusterMap := e.clusterMap + hostMap := e.hostMap + vmMap := e.vmMap + datastoreMap := e.datastoreMap + + // Discover clusters + // + if e.Parent.GatherClusters { + clusterMap, err = e.getClusters(ctx, conn.Root) + if err != nil { + return err + } + for _, cluster := range clusterMap { + nameCache[cluster.ref.Reference().Value] = cluster.name + } + } + + // Discover hosts + // + if e.Parent.GatherHosts { + hostMap, err = e.getHosts(ctx, conn.Root) + if err != nil { + return err + } + for _, host := range hostMap { + nameCache[host.ref.Reference().Value] = host.name + } + } + + // Discover VMs + // + if e.Parent.GatherVms { + vmMap, err = e.getVMs(ctx, conn.Root) + if err != nil { + return err + } + for _, vm := range vmMap { + nameCache[vm.ref.Reference().Value] = vm.name + } + } + + // Discover Datastores + // + if e.Parent.GatherDatastores { + datastoreMap, err = e.getDatastores(ctx, conn.Root) + if err != nil { + return err + } + for _, datastore := range datastoreMap { + nameCache[datastore.ref.Reference().Value] = datastore.name + } + } + + // Atomically swap maps + // + e.collectMux.Lock() + defer e.collectMux.Unlock() + + e.nameCache = nameCache + e.datastoreMap = datastoreMap + e.vmMap = vmMap + e.hostMap = hostMap + e.clusterMap = clusterMap + + log.Printf("D! Discovered %d objects\n", len(e.nameCache)) + + return nil +} + +func (e *Endpoint) collectResourceType(p *performance.Manager, ctx context.Context, alias string, acc telegraf.Accumulator, + objects objectMap, nameCache map[string]string, intervalDuration internal.Duration, isRealTime bool, metricIds []types.PerfMetricId) error { + + // Object maps may change, so we need to hold the collect lock + // + e.collectMux.RLock() + defer e.collectMux.RUnlock() + + // Interval = 0 means collection for this metric was diabled, so don't even bother. + // + interval := int32(intervalDuration.Duration.Seconds()) + if interval <= 0 { + return nil + } + log.Printf("D! Resource type: %s, sampling interval is: %d", alias, interval) + + // Do we have new data yet? + // + now := time.Now() + latest, hasLatest := e.lastColl[alias] + if hasLatest { + elapsed := time.Now().Sub(latest).Seconds() + if elapsed < float64(interval) { + // No new data would be available. We're outta here! + // + return nil + } + } + e.lastColl[alias] = now + log.Printf("D! Collecting for %s", alias) + fullAlias := "vsphere." + alias + + start := time.Now() + log.Printf("D! Query for %s returned %d objects", alias, len(objects)) + pqs := make([]types.PerfQuerySpec, 0, e.Parent.ObjectsPerQuery) + total := 0 + for _, object := range objects { + pq := types.PerfQuerySpec{ + Entity: object.ref, + MaxSample: 1, + MetricId: metricIds, + IntervalId: interval, + } + + if !isRealTime { + startTime := now.Add(-time.Duration(interval) * time.Second) + pq.StartTime = &startTime + pq.EndTime = &now + } + + pqs = append(pqs, pq) + total++ + + // Filled up a chunk or at end of data? Run a query with the collected objects + // + if len(pqs) >= int(e.Parent.ObjectsPerQuery) || total == len(objects) { + log.Printf("D! Querying %d objects of type %s for %s. Total processed: %d. Total objects %d\n", len(pqs), alias, e.Url.Host, total, len(objects)) + metrics, err := p.Query(ctx, pqs) + if err != nil { + log.Printf("E! Error processing resource type %s", alias) + return err + } + + ems, err := p.ToMetricSeries(ctx, metrics) + if err != nil { + return err + } + + // Iterate through result and fields list + // + for _, em := range ems { + moid := em.Entity.Reference().Value + for _, v := range em.Value { + name := v.Name + for idx, value := range v.Value { + f := map[string]interface{}{name: value} + objectName := nameCache[moid] + parent := "" + parentRef := objects[moid].parentRef + if parentRef != nil { + parent = nameCache[parentRef.Value] + } + + t := map[string]string{ + "vcenter": e.Url.Host, + "hostname": objectName, + "moid": moid, + "parent": parent, + } + + if v.Instance != "" { + if strings.HasPrefix(name, "cpu.") { + t["cpu"] = v.Instance + } else if strings.HasPrefix(name, "datastore.") { + t["datastore"] = v.Instance + } else if strings.HasPrefix(name, "disk.") { + t["disk"] = cleanDiskTag(v.Instance) + } else if strings.HasPrefix(name, "net.") { + t["interface"] = v.Instance + } else if strings.HasPrefix(name, "storageAdapter.") { + t["adapter"] = v.Instance + } else if strings.HasPrefix(name, "storagePath.") { + t["path"] = v.Instance + } else if strings.HasPrefix(name, "sys.resource") { + t["resource"] = v.Instance + } else if strings.HasPrefix(name, "vflashModule.") { + t["module"] = v.Instance + } else if strings.HasPrefix(name, "virtualDisk.") { + t["disk"] = v.Instance + } else { + // default to instance + t["instance"] = v.Instance + } + } + + acc.AddFields(fullAlias, f, t, em.SampleInfo[idx].Timestamp) + } + } + } + pqs = make([]types.PerfQuerySpec, 0, e.Parent.ObjectsPerQuery) + } + } + + log.Printf("D! Collection of %s took %v\n", alias, time.Now().Sub(start)) + return nil +} + +func (e *Endpoint) collect(acc telegraf.Accumulator) error { + + start := time.Now() + + // If discovery interval is disabled (0), discover on each collection cycle + // + if e.Parent.ObjectDiscoveryInterval.Duration.Seconds() == 0 { + err := e.discover() + if err != nil { + return err + } + } + + conn, err := NewConnection(e.Url, e.Parent.Timeout) + if err != nil { + return err + } + + defer conn.Close() + ctx := context.Background() + + if e.Parent.GatherClusters { + err = e.collectResourceType(conn.Perf, ctx, "cluster", acc, e.clusterMap, e.nameCache, + e.Parent.ClusterSamplingPeriod, false, e.clusterMetricIds) + if err != nil { + return err + } + } + + if e.Parent.GatherHosts { + err = e.collectResourceType(conn.Perf, ctx, "host", acc, e.hostMap, e.nameCache, + e.Parent.HostSamplingPeriod, true, e.hostMetricIds) + if err != nil { + return err + } + } + + if e.Parent.GatherVms { + err = e.collectResourceType(conn.Perf, ctx, "vm", acc, e.vmMap, e.nameCache, + e.Parent.VmSamplingPeriod, true, e.vmMetricIds) + if err != nil { + return err + } + } + + if e.Parent.GatherDatastores { + err = e.collectResourceType(conn.Perf, ctx, "datastore", acc, e.datastoreMap, e.nameCache, + e.Parent.DatastoreSamplingPeriod, false, e.datastoreMetricIds) + if err != nil { + return err + } + } + + + // Add gauge to show how long it took to gather all the metrics on this cycle for a specific vcenter/endpoint + // + acc.AddGauge("vsphere", map[string]interface{}{"gather.duration": time.Now().Sub(start).Seconds()}, map[string]string{"vcenter": e.Url.Host}, time.Now()) + + return nil +} + +func (e *Endpoint) getVMs(ctx context.Context, root *view.ContainerView) (objectMap, error) { + var resources []mo.VirtualMachine + err := root.Retrieve(ctx, []string{"VirtualMachine"}, []string{"summary", "runtime.host"}, &resources) + if err != nil { + return nil, err + } + m := make(objectMap) + for _, r := range resources { + m[r.ExtensibleManagedObject.Reference().Value] = objectRef{ + name: r.Summary.Config.Name, ref: r.ExtensibleManagedObject.Reference(), parentRef: r.Runtime.Host} + } + return m, nil +} + +func (e *Endpoint) getHosts(ctx context.Context, root *view.ContainerView) (objectMap, error) { + var resources []mo.HostSystem + err := root.Retrieve(ctx, []string{"HostSystem"}, []string{"summary", "parent"}, &resources) + if err != nil { + return nil, err + } + m := make(objectMap) + for _, r := range resources { + m[r.ExtensibleManagedObject.Reference().Value] = objectRef{ + name: r.Summary.Config.Name, ref: r.ExtensibleManagedObject.Reference(), parentRef: r.Parent} + } + return m, nil +} + +func (e *Endpoint) getClusters(ctx context.Context, root *view.ContainerView) (objectMap, error) { + var resources []mo.ClusterComputeResource + err := root.Retrieve(ctx, []string{"ClusterComputeResource"}, []string{"summary", "name", "parent"}, &resources) + if err != nil { + return nil, err + } + m := make(objectMap) + for _, r := range resources { + m[r.ExtensibleManagedObject.Reference().Value] = objectRef{ + name: r.Name, ref: r.ExtensibleManagedObject.Reference(), parentRef: r.Parent} + } + return m, nil +} + +func (e *Endpoint) getDatastores(ctx context.Context, root *view.ContainerView) (objectMap, error) { + var resources []mo.Datastore + err := root.Retrieve(ctx, []string{"Datastore"}, []string{"summary"}, &resources) + if err != nil { + return nil, err + } + m := make(objectMap) + for _, r := range resources { + m[r.Summary.Name] = objectRef{ + name: r.Summary.Name, ref: r.ExtensibleManagedObject.Reference(), parentRef: r.Parent} + } + return m, nil +} + +func cleanDiskTag(disk string) string { + if strings.HasPrefix(disk, "<") { + i := strings.Index(disk, ">") + if i > -1 { + s1 := disk[1:i] + s2 := disk[i+1:] + if s1 == s2 { + return s1 + } + } + } + + return disk +} diff --git a/plugins/inputs/vsphere/vsphere.go b/plugins/inputs/vsphere/vsphere.go index 6d3f753c72110..f6f64d45eb1c7 100644 --- a/plugins/inputs/vsphere/vsphere.go +++ b/plugins/inputs/vsphere/vsphere.go @@ -1,512 +1,38 @@ package vsphere import ( - "context" - "github.com/gobwas/glob" "github.com/influxdata/telegraf" "github.com/influxdata/telegraf/internal" "github.com/influxdata/telegraf/plugins/inputs" - "github.com/vmware/govmomi/performance" - "github.com/vmware/govmomi/view" - "github.com/vmware/govmomi/vim25/mo" "github.com/vmware/govmomi/vim25/soap" - "github.com/vmware/govmomi/vim25/types" "log" "net/url" - "strings" "sync" "time" ) type VSphere struct { - Vcenters []string - GatherClusters bool - GatherHosts bool - GatherVms bool - GatherDatastores bool + Vcenters []string + + GatherClusters bool + ClusterMetrics []string + GatherHosts bool + HostMetrics []string + GatherVms bool + VmMetrics []string + GatherDatastores bool + DatastoreMetrics []string + ObjectsPerQuery int32 + ObjectDiscoveryInterval internal.Duration + Timeout internal.Duration + VmSamplingPeriod internal.Duration HostSamplingPeriod internal.Duration ClusterSamplingPeriod internal.Duration DatastoreSamplingPeriod internal.Duration - ObjectDiscoveryInterval internal.Duration - VmMetrics []string - HostMetrics []string - ClusterMetrics []string - DatastoreMetrics []string - endpoints []Endpoint -} - -type objectMap map[string]objectRef - -type Endpoint struct { - Parent *VSphere - Url *url.URL - lastColl map[string]time.Time - hostMap objectMap - vmMap objectMap - clusterMap objectMap - datastoreMap objectMap - nameCache map[string]string - vmMetricIds []types.PerfMetricId - hostMetricIds []types.PerfMetricId - clusterMetricIds []types.PerfMetricId - datastoreMetricIds []types.PerfMetricId - discoveryTicker *time.Ticker - collectMux sync.RWMutex -} - -type objectRef struct { - name string - ref types.ManagedObjectReference - parentRef *types.ManagedObjectReference //Pointer because it must be nillable -} - -type InstanceMetrics map[string]map[string]interface{} - -func NewEndpoint(parent *VSphere, url *url.URL) Endpoint { - hostMap := make(objectMap) - vmMap := make(objectMap) - clusterMap := make(objectMap) - datastoreMap := make(objectMap) - e := Endpoint{ - Url: url, - Parent: parent, - lastColl: make(map[string]time.Time), - hostMap: hostMap, - vmMap: vmMap, - clusterMap: clusterMap, - datastoreMap: datastoreMap, - nameCache: make(map[string]string), - } - e.init() - return e -} - -func (e *Endpoint) init() error { - conn, err := NewConnection(e.Url) - if err != nil { - return err - } - defer conn.Close() - - // Load metric IDs if specified - // - ctx := context.Background() - metricMap, err := conn.Perf.CounterInfoByName(ctx) - if err != nil { - return err - } - e.vmMetricIds, err = resolveMetricWildcards(metricMap, e.Parent.VmMetrics) - if err != nil { - return err - } - e.hostMetricIds, err = resolveMetricWildcards(metricMap, e.Parent.HostMetrics) - if err != nil { - return err - } - e.clusterMetricIds, err = resolveMetricWildcards(metricMap, e.Parent.ClusterMetrics) - if err != nil { - return err - } - e.datastoreMetricIds, err = resolveMetricWildcards(metricMap, e.Parent.DatastoreMetrics) - if err != nil { - return err - } - - // Start background discovery if requested - // - if e.Parent.ObjectDiscoveryInterval.Duration.Seconds() > 0 { - e.discoveryTicker = time.NewTicker(e.Parent.ObjectDiscoveryInterval.Duration) - go func() { - for range e.discoveryTicker.C { - err := e.discover() - if err != nil { - log.Printf("E! Error in discovery %v", err) - } - } - }() - - // Run an initial discovery. - // - e.discover() - } - return nil -} - -func resolveMetricWildcards(metricMap map[string]*types.PerfCounterInfo, wildcards []string) ([]types.PerfMetricId, error) { - // Nothing specified assumes we're looking at everything - // - if wildcards == nil { - return nil, nil - } - tmpMap := make(map[string]types.PerfMetricId) - for _, pattern := range wildcards { - exclude := false - if pattern[0] == '!' { - pattern = pattern[1:] - exclude = true - } - p, err := glob.Compile(pattern) - if err != nil { - return nil, err - } - for name, info := range metricMap { - if p.Match(name) { - if exclude { - delete(tmpMap, name) - log.Printf("D! excluded %s", name) - } else { - tmpMap[name] = types.PerfMetricId{CounterId: info.Key} - log.Printf("D! included %s", name) - } - } - } - } - result := make([]types.PerfMetricId, len(tmpMap)) - idx := 0 - for _, id := range tmpMap { - result[idx] = id - idx++ - } - return result, nil -} - -func (e *Endpoint) discover() error { - conn, err := NewConnection(e.Url) - if err != nil { - return err - } - - defer conn.Close() - ctx := context.Background() - - nameCache := make(map[string]string) - clusterMap := e.clusterMap - hostMap := e.hostMap - vmMap := e.vmMap - datastoreMap := e.datastoreMap - - // Discover clusters - // - if e.Parent.GatherClusters { - clusterMap, err = e.getClusters(ctx, conn.Root) - if err != nil { - return err - } - for _, cluster := range clusterMap { - nameCache[cluster.ref.Reference().Value] = cluster.name - } - } - - // Discover hosts - // - if e.Parent.GatherHosts { - hostMap, err = e.getHosts(ctx, conn.Root) - if err != nil { - return err - } - for _, host := range hostMap { - nameCache[host.ref.Reference().Value] = host.name - } - } - - // Discover VMs - // - if e.Parent.GatherVms { - vmMap, err = e.getVMs(ctx, conn.Root) - if err != nil { - return err - } - for _, vm := range vmMap { - nameCache[vm.ref.Reference().Value] = vm.name - } - } - - // Discover Datastores - // - if e.Parent.GatherDatastores { - datastoreMap, err = e.getDatastores(ctx, conn.Root) - if err != nil { - return err - } - for _, datastore := range datastoreMap { - nameCache[datastore.ref.Reference().Value] = datastore.name - } - } - - // Atomically swap maps - // - e.collectMux.Lock() - defer e.collectMux.Unlock() - - e.nameCache = nameCache - e.datastoreMap = datastoreMap - e.vmMap = vmMap - e.hostMap = hostMap - e.clusterMap = clusterMap - - log.Printf("D! Discovered %d objects\n", len(e.nameCache)) - - return nil -} - -func (e *Endpoint) collectResourceType(p *performance.Manager, ctx context.Context, alias string, acc telegraf.Accumulator, - objects objectMap, nameCache map[string]string, intervalDuration internal.Duration, isRealTime bool, metricIds []types.PerfMetricId) error { - - // Object maps may change, so we need to hold the collect lock - // - e.collectMux.RLock() - defer e.collectMux.RUnlock() - - // Interval = 0 means collection for this metric was diabled, so don't even bother. - // - interval := int32(intervalDuration.Duration.Seconds()) - if interval <= 0 { - return nil - } - log.Printf("D! Resource type: %s, sampling interval is: %d", alias, interval) - - // Do we have new data yet? - // - now := time.Now() - latest, hasLatest := e.lastColl[alias] - if hasLatest { - elapsed := time.Now().Sub(latest).Seconds() - if elapsed < float64(interval) { - // No new data would be available. We're outta here! - // - return nil - } - } - e.lastColl[alias] = now - log.Printf("D! Collecting for %s", alias) - fullAlias := "vsphere." + alias - - start := time.Now() - log.Printf("D! Query for %s returned %d objects", alias, len(objects)) - pqs := make([]types.PerfQuerySpec, 0, e.Parent.ObjectsPerQuery) - total := 0 - for _, object := range objects { - pq := types.PerfQuerySpec{ - Entity: object.ref, - MaxSample: 1, - MetricId: metricIds, - IntervalId: interval, - } - - if !isRealTime { - startTime := now.Add(-time.Duration(interval) * time.Second) - pq.StartTime = &startTime - pq.EndTime = &now - } - - pqs = append(pqs, pq) - total++ - - // Filled up a chunk or at end of data? Run a query with the collected objects - // - if len(pqs) >= int(e.Parent.ObjectsPerQuery) || total == len(objects) { - log.Printf("D! Querying %d objects of type %s for %s. Total processed: %d. Total objects %d\n", len(pqs), alias, e.Url.Host, total, len(objects)) - metrics, err := p.Query(ctx, pqs) - if err != nil { - log.Printf("E! Error processing resource type %s", alias) - return err - } - - ems, err := p.ToMetricSeries(ctx, metrics) - if err != nil { - return err - } - - // Iterate through result and fields list - // - for _, em := range ems { - moid := em.Entity.Reference().Value - for _, v := range em.Value { - name := v.Name - for idx, value := range v.Value { - f := map[string]interface{}{name: value} - objectName := nameCache[moid] - parent := "" - parentRef := objects[moid].parentRef - if parentRef != nil { - parent = nameCache[parentRef.Value] - } - - t := map[string]string{ - "vcenter": e.Url.Host, - "hostname": objectName, - "moid": moid, - "parent": parent, - } - - if v.Instance != "" { - if strings.HasPrefix(name, "cpu.") { - t["cpu"] = v.Instance - } else if strings.HasPrefix(name, "datastore.") { - t["datastore"] = v.Instance - } else if strings.HasPrefix(name, "disk.") { - t["disk"] = cleanDiskTag(v.Instance) - } else if strings.HasPrefix(name, "net.") { - t["interface"] = v.Instance - } else if strings.HasPrefix(name, "storageAdapter.") { - t["adapter"] = v.Instance - } else if strings.HasPrefix(name, "storagePath.") { - t["path"] = v.Instance - } else if strings.HasPrefix(name, "sys.resource") { - t["resource"] = v.Instance - } else if strings.HasPrefix(name, "vflashModule.") { - t["module"] = v.Instance - } else if strings.HasPrefix(name, "virtualDisk.") { - t["disk"] = v.Instance - } else { - // default to instance - t["instance"] = v.Instance - } - } - - acc.AddFields(fullAlias, f, t, em.SampleInfo[idx].Timestamp) - } - } - } - pqs = make([]types.PerfQuerySpec, 0, e.Parent.ObjectsPerQuery) - } - } - - log.Printf("D! Collection of %s took %v\n", alias, time.Now().Sub(start)) - return nil -} - -func (e *Endpoint) collect(acc telegraf.Accumulator) error { - - start := time.Now() - - // If discovery interval is disabled (0), discover on each collection cycle - // - if e.Parent.ObjectDiscoveryInterval.Duration.Seconds() == 0 { - err := e.discover() - if err != nil { - return err - } - } - - conn, err := NewConnection(e.Url) - if err != nil { - return err - } - - defer conn.Close() - ctx := context.Background() - - if e.Parent.GatherClusters { - err = e.collectResourceType(conn.Perf, ctx, "cluster", acc, e.clusterMap, e.nameCache, - e.Parent.ClusterSamplingPeriod, false, e.clusterMetricIds) - if err != nil { - return err - } - } - - if e.Parent.GatherHosts { - err = e.collectResourceType(conn.Perf, ctx, "host", acc, e.hostMap, e.nameCache, - e.Parent.HostSamplingPeriod, true, e.hostMetricIds) - if err != nil { - return err - } - } - - if e.Parent.GatherVms { - err = e.collectResourceType(conn.Perf, ctx, "vm", acc, e.vmMap, e.nameCache, - e.Parent.VmSamplingPeriod, true, e.vmMetricIds) - if err != nil { - return err - } - } - - if e.Parent.GatherDatastores { - err = e.collectResourceType(conn.Perf, ctx, "datastore", acc, e.datastoreMap, e.nameCache, - e.Parent.DatastoreSamplingPeriod, false, e.datastoreMetricIds) - if err != nil { - return err - } - } - - // Add gauge to show how long it took to gather all the metrics on this cycle for a specific vcenter/endpoint - // - acc.AddGauge("vsphere", map[string]interface{}{"gather.duration": time.Now().Sub(start).Seconds()}, map[string]string{"vcenter": e.Url.Host}, time.Now()) - - return nil -} -func (e *Endpoint) getVMs(ctx context.Context, root *view.ContainerView) (objectMap, error) { - var resources []mo.VirtualMachine - err := root.Retrieve(ctx, []string{"VirtualMachine"}, []string{"summary", "runtime.host"}, &resources) - if err != nil { - return nil, err - } - m := make(objectMap) - for _, r := range resources { - m[r.ExtensibleManagedObject.Reference().Value] = objectRef{ - name: r.Summary.Config.Name, ref: r.ExtensibleManagedObject.Reference(), parentRef: r.Runtime.Host} - } - return m, nil -} - -func (e *Endpoint) getHosts(ctx context.Context, root *view.ContainerView) (objectMap, error) { - var resources []mo.HostSystem - err := root.Retrieve(ctx, []string{"HostSystem"}, []string{"summary", "parent"}, &resources) - if err != nil { - return nil, err - } - m := make(objectMap) - for _, r := range resources { - m[r.ExtensibleManagedObject.Reference().Value] = objectRef{ - name: r.Summary.Config.Name, ref: r.ExtensibleManagedObject.Reference(), parentRef: r.Parent} - } - return m, nil -} - -func (e *Endpoint) getClusters(ctx context.Context, root *view.ContainerView) (objectMap, error) { - var resources []mo.ClusterComputeResource - err := root.Retrieve(ctx, []string{"ClusterComputeResource"}, []string{"summary", "name", "parent"}, &resources) - if err != nil { - return nil, err - } - m := make(objectMap) - for _, r := range resources { - m[r.ExtensibleManagedObject.Reference().Value] = objectRef{ - name: r.Name, ref: r.ExtensibleManagedObject.Reference(), parentRef: r.Parent} - } - return m, nil -} - -func (e *Endpoint) getDatastores(ctx context.Context, root *view.ContainerView) (objectMap, error) { - var resources []mo.Datastore - err := root.Retrieve(ctx, []string{"Datastore"}, []string{"summary"}, &resources) - if err != nil { - return nil, err - } - m := make(objectMap) - for _, r := range resources { - m[r.Summary.Name] = objectRef{ - name: r.Summary.Name, ref: r.ExtensibleManagedObject.Reference(), parentRef: r.Parent} - } - return m, nil -} - -func cleanDiskTag(disk string) string { - if strings.HasPrefix(disk, "<") { - i := strings.Index(disk, ">") - if i > -1 { - s1 := disk[1:i] - s2 := disk[i+1:] - if s1 == s2 { - return s1 - } - } - } - - return disk + endpoints []Endpoint } var sampleConfig = ` @@ -537,10 +63,10 @@ func (v *VSphere) vSphereInit() { } wg.Add(1) - go func() { + go func(url *url.URL, j int) { defer wg.Done() - v.endpoints[i] = NewEndpoint(v, u) - }() + v.endpoints[j] = NewEndpoint(v, url) + }(u, i) } wg.Wait() @@ -556,10 +82,10 @@ func (v *VSphere) Gather(acc telegraf.Accumulator) error { for _, ep := range v.endpoints { wg.Add(1) - go func() { + go func(endpoint Endpoint) { defer wg.Done() - acc.AddError(ep.collect(acc)) - }() + acc.AddError(endpoint.collect(acc)) + }(ep) } wg.Wait() @@ -577,22 +103,22 @@ func init() { Vcenters: []string{}, GatherClusters: true, + ClusterMetrics: nil, GatherHosts: true, + HostMetrics: nil, GatherVms: true, + VmMetrics: nil, GatherDatastores: true, + DatastoreMetrics: nil, - ObjectsPerQuery: 500, - + ObjectsPerQuery: 500, ObjectDiscoveryInterval: internal.Duration{Duration: time.Second * 300}, + Timeout: internal.Duration{Duration: time.Second * 20}, + ClusterSamplingPeriod: internal.Duration{Duration: time.Second * 300}, HostSamplingPeriod: internal.Duration{Duration: time.Second * 20}, VmSamplingPeriod: internal.Duration{Duration: time.Second * 20}, DatastoreSamplingPeriod: internal.Duration{Duration: time.Second * 300}, - - ClusterMetrics: nil, - HostMetrics: nil, - VmMetrics: nil, - DatastoreMetrics: nil, } }) } From d3acb400be6f9ca2d902cd2a475d504eb4bd7850 Mon Sep 17 00:00:00 2001 From: Pierre Tessier Date: Sat, 25 Nov 2017 00:16:45 -0500 Subject: [PATCH 025/100] timeout readmen --- plugins/inputs/vsphere/README.MD | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plugins/inputs/vsphere/README.MD b/plugins/inputs/vsphere/README.MD index 4bf7090db4aff..255aeafb83f80 100644 --- a/plugins/inputs/vsphere/README.MD +++ b/plugins/inputs/vsphere/README.MD @@ -41,6 +41,9 @@ The VMware vSphere plugin uses the vSphere API to gather metrics from multiple v ## the interval before (re)discovering objects subject to metrics collection (default: 300s) # object_discovery_interval = "300s" + + ## timeout applies to any of the connection request made to vcenter + # timeout = "20s" ## Optional for configurations with customized sampling periods in vcenter # cluster_sampling_period = "300s" From 98ad75b6b35b8a861f7fb866361ca44d8749a410 Mon Sep 17 00:00:00 2001 From: Pierre Tessier Date: Sat, 25 Nov 2017 19:55:02 -0500 Subject: [PATCH 026/100] connection reuse --- plugins/inputs/vsphere/endpoint.go | 34 ++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/plugins/inputs/vsphere/endpoint.go b/plugins/inputs/vsphere/endpoint.go index 7891706569fef..b2eb06c25e7e3 100644 --- a/plugins/inputs/vsphere/endpoint.go +++ b/plugins/inputs/vsphere/endpoint.go @@ -21,6 +21,7 @@ type objectMap map[string]objectRef type Endpoint struct { Parent *VSphere Url *url.URL + connection *Connection lastColl map[string]time.Time hostMap objectMap vmMap objectMap @@ -32,6 +33,7 @@ type Endpoint struct { clusterMetricIds []types.PerfMetricId datastoreMetricIds []types.PerfMetricId discoveryTicker *time.Ticker + connectionMux sync.Mutex collectMux sync.RWMutex } @@ -63,11 +65,10 @@ func NewEndpoint(parent *VSphere, url *url.URL) Endpoint { } func (e *Endpoint) init() error { - conn, err := NewConnection(e.Url, e.Parent.Timeout) + conn, err := e.getConnection() if err != nil { return err } - defer conn.Close() // Load metric IDs if specified // @@ -78,7 +79,7 @@ func (e *Endpoint) init() error { } e.vmMetricIds, err = resolveMetricWildcards(metricMap, e.Parent.VmMetrics) if err != nil { - return err + return err } e.hostMetricIds, err = resolveMetricWildcards(metricMap, e.Parent.HostMetrics) if err != nil { @@ -101,7 +102,7 @@ func (e *Endpoint) init() error { for range e.discoveryTicker.C { err := e.discover() if err != nil { - log.Printf("E! Error in discovery %v", err) + log.Printf("E! Error in discovery for %s: %v", e.Url.Host, err) } } }() @@ -113,6 +114,23 @@ func (e *Endpoint) init() error { return nil } +func (e *Endpoint) getConnection() (*Connection, error) { + log.Printf("getConnection() %s\n", e.Url.Host) + if e.connection == nil { + e.connectionMux.Lock() + defer e.connectionMux.Unlock() + if e.connection == nil { + log.Printf("getConnection() - creating new %s\n", e.Url.Host) + conn, err := NewConnection(e.Url, e.Parent.Timeout) + if err != nil { + return nil, err + } + e.connection = conn + } + } + return e.connection, nil +} + func resolveMetricWildcards(metricMap map[string]*types.PerfCounterInfo, wildcards []string) ([]types.PerfMetricId, error) { // Nothing specified assumes we're looking at everything // @@ -152,12 +170,11 @@ func resolveMetricWildcards(metricMap map[string]*types.PerfCounterInfo, wildcar } func (e *Endpoint) discover() error { - conn, err := NewConnection(e.Url, e.Parent.Timeout) + conn, err := e.getConnection() if err != nil { return err } - defer conn.Close() ctx := context.Background() nameCache := make(map[string]string) @@ -289,7 +306,7 @@ func (e *Endpoint) collectResourceType(p *performance.Manager, ctx context.Conte log.Printf("D! Querying %d objects of type %s for %s. Total processed: %d. Total objects %d\n", len(pqs), alias, e.Url.Host, total, len(objects)) metrics, err := p.Query(ctx, pqs) if err != nil { - log.Printf("E! Error processing resource type %s", alias) + log.Printf("E! Error processing resource type %s on %s", alias, e.Url.Host) return err } @@ -370,12 +387,11 @@ func (e *Endpoint) collect(acc telegraf.Accumulator) error { } } - conn, err := NewConnection(e.Url, e.Parent.Timeout) + conn, err := e.getConnection() if err != nil { return err } - defer conn.Close() ctx := context.Background() if e.Parent.GatherClusters { From af648ac0fdbb379b14f71f9fdce41fa329afca14 Mon Sep 17 00:00:00 2001 From: "pontus@rydin@gmail.com" Date: Mon, 27 Nov 2017 08:26:14 -0500 Subject: [PATCH 027/100] Moved reference to c.Timeout in NewConnection. Now accessed after checking for error. --- plugins/inputs/vsphere/connection.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/inputs/vsphere/connection.go b/plugins/inputs/vsphere/connection.go index 53901f174ac93..89b9b0b32a87e 100644 --- a/plugins/inputs/vsphere/connection.go +++ b/plugins/inputs/vsphere/connection.go @@ -2,11 +2,11 @@ package vsphere import ( "context" + "github.com/influxdata/telegraf/internal" "github.com/vmware/govmomi" "github.com/vmware/govmomi/performance" "github.com/vmware/govmomi/view" "net/url" - "github.com/influxdata/telegraf/internal" ) type Connection struct { @@ -19,10 +19,10 @@ type Connection struct { func NewConnection(url *url.URL, timeout internal.Duration) (*Connection, error) { ctx := context.Background() c, err := govmomi.NewClient(ctx, url, true) - c.Timeout = timeout.Duration if err != nil { return nil, err } + c.Timeout = timeout.Duration m := view.NewManager(c.Client) v, err := m.CreateContainerView(ctx, c.ServiceContent.RootFolder, []string{}, true) if err != nil { From 9d77d88708e7533397b156833656d39ab33d5880 Mon Sep 17 00:00:00 2001 From: Pierre Tessier Date: Sat, 2 Dec 2017 14:09:24 -0500 Subject: [PATCH 028/100] refactored client, fixed init --- plugins/inputs/vsphere/README.MD | 22 +- .../vsphere/{connection.go => client.go} | 8 +- plugins/inputs/vsphere/endpoint.go | 514 +++++++++--------- plugins/inputs/vsphere/vsphere.go | 37 +- 4 files changed, 261 insertions(+), 320 deletions(-) rename plugins/inputs/vsphere/{connection.go => client.go} (83%) diff --git a/plugins/inputs/vsphere/README.MD b/plugins/inputs/vsphere/README.MD index 255aeafb83f80..e4c4cc73cfab7 100644 --- a/plugins/inputs/vsphere/README.MD +++ b/plugins/inputs/vsphere/README.MD @@ -22,19 +22,19 @@ The VMware vSphere plugin uses the vSphere API to gather metrics from multiple v ## gather metrics from Clusters (default: true) # gather_clusters = true - # cluster_metrics = ["*"] + # cluster_metrics = [] ## gather metrics from Hosts (default: true) # gather_hosts = true - # host_metrics = ["*"] + # host_metrics = [] ## gather metrics from VMs (default: true) # gather_vms = true - # vm_metrics = ["*"] + # vm_metrics = [] ## gather metrics from Data stores (default: true) # gather_datastores = true - # datastore_metrics = ["*"] + # datastore_metrics = [] ## number of objects to retreive per query (default: 500) # objects_per_query = 500 @@ -44,12 +44,6 @@ The VMware vSphere plugin uses the vSphere API to gather metrics from multiple v ## timeout applies to any of the connection request made to vcenter # timeout = "20s" - - ## Optional for configurations with customized sampling periods in vcenter - # cluster_sampling_period = "300s" - # host_sampling_period = "20s" - # vm_sampling_period = "20s" - # datastore_sampling_period = "300s" ``` ### Metrics Selection @@ -69,14 +63,6 @@ A configuration to include all metrics under the host category that start with ` ``` -### Sampling Periods - -The plugin is called using the standard interval property. To maintain optimal performance, metrics for a given category -will only be gathered, if that category's sampling period has passed since the last gather. Cluster and Datastore metrics -have significantly longer sampling periods by default (300s vs 20s). The *_sampling_period properties should only be changed if you have changed these default -settings within vCenter server. - - ## Measurements & Fields - Cluster Stats diff --git a/plugins/inputs/vsphere/connection.go b/plugins/inputs/vsphere/client.go similarity index 83% rename from plugins/inputs/vsphere/connection.go rename to plugins/inputs/vsphere/client.go index 89b9b0b32a87e..8d583ac776291 100644 --- a/plugins/inputs/vsphere/connection.go +++ b/plugins/inputs/vsphere/client.go @@ -9,14 +9,14 @@ import ( "net/url" ) -type Connection struct { +type Client struct { Client *govmomi.Client Views *view.Manager Root *view.ContainerView Perf *performance.Manager } -func NewConnection(url *url.URL, timeout internal.Duration) (*Connection, error) { +func NewClient(url *url.URL, timeout internal.Duration) (*Client, error) { ctx := context.Background() c, err := govmomi.NewClient(ctx, url, true) if err != nil { @@ -30,7 +30,7 @@ func NewConnection(url *url.URL, timeout internal.Duration) (*Connection, error) } p := performance.NewManager(c.Client) - return &Connection{ + return &Client{ Client: c, Views: m, Root: v, @@ -38,7 +38,7 @@ func NewConnection(url *url.URL, timeout internal.Duration) (*Connection, error) }, nil } -func (c *Connection) Close() { +func (c *Client) Close() { ctx := context.Background() if c.Views != nil { c.Views.Destroy(ctx) diff --git a/plugins/inputs/vsphere/endpoint.go b/plugins/inputs/vsphere/endpoint.go index b2eb06c25e7e3..a4d76ff203dbf 100644 --- a/plugins/inputs/vsphere/endpoint.go +++ b/plugins/inputs/vsphere/endpoint.go @@ -4,8 +4,6 @@ import ( "context" "github.com/gobwas/glob" "github.com/influxdata/telegraf" - "github.com/influxdata/telegraf/internal" - "github.com/vmware/govmomi/performance" "github.com/vmware/govmomi/view" "github.com/vmware/govmomi/vim25/mo" "github.com/vmware/govmomi/vim25/types" @@ -16,87 +14,76 @@ import ( "time" ) -type objectMap map[string]objectRef - type Endpoint struct { Parent *VSphere Url *url.URL - connection *Connection - lastColl map[string]time.Time - hostMap objectMap - vmMap objectMap - clusterMap objectMap - datastoreMap objectMap + client *Client + lastColls map[string]time.Time nameCache map[string]string - vmMetricIds []types.PerfMetricId - hostMetricIds []types.PerfMetricId - clusterMetricIds []types.PerfMetricId - datastoreMetricIds []types.PerfMetricId + resources map[string]resource discoveryTicker *time.Ticker - connectionMux sync.Mutex - collectMux sync.RWMutex + clientMux *sync.Mutex + collectMux *sync.RWMutex + initialized bool } +type resource struct { + enabled bool + realTime bool + sampling int32 + objects objectMap + metricIds []types.PerfMetricId + wildcards []string +} + +type objectMap map[string]objectRef + type objectRef struct { name string ref types.ManagedObjectReference parentRef *types.ManagedObjectReference //Pointer because it must be nillable } -type InstanceMetrics map[string]map[string]interface{} - -func NewEndpoint(parent *VSphere, url *url.URL) Endpoint { - hostMap := make(objectMap) - vmMap := make(objectMap) - clusterMap := make(objectMap) - datastoreMap := make(objectMap) +func NewEndpoint(parent *VSphere, url *url.URL) *Endpoint { e := Endpoint{ Url: url, Parent: parent, - lastColl: make(map[string]time.Time), - hostMap: hostMap, - vmMap: vmMap, - clusterMap: clusterMap, - datastoreMap: datastoreMap, + lastColls: make(map[string]time.Time), nameCache: make(map[string]string), + clientMux: &sync.Mutex{}, + collectMux: &sync.RWMutex{}, + initialized: false, + } + + e.resources = map[string]resource{ + "cluster": {enabled: parent.GatherClusters, realTime: false, sampling: 300, objects: make(objectMap), wildcards: parent.ClusterMetrics}, + "host": {enabled: parent.GatherHosts, realTime: true, sampling: 20, objects: make(objectMap), wildcards: parent.HostMetrics}, + "vm": {enabled: parent.GatherVms, realTime: true, sampling: 20, objects: make(objectMap), wildcards: parent.VmMetrics}, + "datastore": {enabled: parent.GatherDatastores, realTime: false, sampling: 300, objects: make(objectMap), wildcards: parent.DatastoreMetrics}, } - e.init() - return e + + return &e } func (e *Endpoint) init() error { - conn, err := e.getConnection() - if err != nil { - return err - } - // Load metric IDs if specified - // - ctx := context.Background() - metricMap, err := conn.Perf.CounterInfoByName(ctx) - if err != nil { - return err - } - e.vmMetricIds, err = resolveMetricWildcards(metricMap, e.Parent.VmMetrics) - if err != nil { - return err - } - e.hostMetricIds, err = resolveMetricWildcards(metricMap, e.Parent.HostMetrics) - if err != nil { - return err - } - e.clusterMetricIds, err = resolveMetricWildcards(metricMap, e.Parent.ClusterMetrics) - if err != nil { - return err - } - e.datastoreMetricIds, err = resolveMetricWildcards(metricMap, e.Parent.DatastoreMetrics) + err := e.setupMetricIds() if err != nil { + log.Printf("E! Error in metric setup for %s: %v", e.Url.Host, err) return err } - // Start background discovery if requested - // if e.Parent.ObjectDiscoveryInterval.Duration.Seconds() > 0 { + // Run an initial discovery. + // + err = e.discover() + if err != nil { + log.Printf("E! Error in initial discovery for %s: %v", e.Url.Host, err) + return err + } + + // Create discovery ticker + // e.discoveryTicker = time.NewTicker(e.Parent.ObjectDiscoveryInterval.Duration) go func() { for range e.discoveryTicker.C { @@ -106,29 +93,32 @@ func (e *Endpoint) init() error { } } }() - - // Run an initial discovery. - // - e.discover() } + + e.initialized = true return nil } -func (e *Endpoint) getConnection() (*Connection, error) { - log.Printf("getConnection() %s\n", e.Url.Host) - if e.connection == nil { - e.connectionMux.Lock() - defer e.connectionMux.Unlock() - if e.connection == nil { - log.Printf("getConnection() - creating new %s\n", e.Url.Host) - conn, err := NewConnection(e.Url, e.Parent.Timeout) - if err != nil { - return nil, err - } - e.connection = conn +func (e *Endpoint) setupMetricIds() error { + conn, err := e.getConnection() + if err != nil { + return err + } + ctx := context.Background() + + metricMap, err := conn.Perf.CounterInfoByName(ctx) + if err != nil { + return err + } + + for _, res := range e.resources { + res.metricIds, err = resolveMetricWildcards(metricMap, res.wildcards) + if err != nil { + return err } } - return e.connection, nil + + return nil } func resolveMetricWildcards(metricMap map[string]*types.PerfCounterInfo, wildcards []string) ([]types.PerfMetricId, error) { @@ -170,85 +160,164 @@ func resolveMetricWildcards(metricMap map[string]*types.PerfCounterInfo, wildcar } func (e *Endpoint) discover() error { + log.Printf("I! Discover new objects for %s", e.Url.Host) + conn, err := e.getConnection() if err != nil { return err } - ctx := context.Background() - nameCache := make(map[string]string) - clusterMap := e.clusterMap - hostMap := e.hostMap - vmMap := e.vmMap - datastoreMap := e.datastoreMap + resources := e.resources + + for k, res := range resources { + if res.enabled { + var objects objectMap + switch k { + case "cluster": + objects, err = e.getClusters(conn.Root) + case "host": + objects, err = e.getHosts(conn.Root) + case "vm": + objects, err = e.getVMs(conn.Root) + case "datastore": + objects, err = e.getDatastores(conn.Root) + } + if err != nil { + return err + } - // Discover clusters - // - if e.Parent.GatherClusters { - clusterMap, err = e.getClusters(ctx, conn.Root) - if err != nil { - return err - } - for _, cluster := range clusterMap { - nameCache[cluster.ref.Reference().Value] = cluster.name + for _, obj := range res.objects { + nameCache[obj.ref.Reference().Value] = obj.name + } + res.objects = objects + resources[k] = res } } - // Discover hosts + // Atomically swap maps // - if e.Parent.GatherHosts { - hostMap, err = e.getHosts(ctx, conn.Root) - if err != nil { - return err - } - for _, host := range hostMap { - nameCache[host.ref.Reference().Value] = host.name - } + if e.collectMux == nil { + e.collectMux = &sync.RWMutex{} } + e.collectMux.Lock() + defer e.collectMux.Unlock() - // Discover VMs - // - if e.Parent.GatherVms { - vmMap, err = e.getVMs(ctx, conn.Root) + e.nameCache = nameCache + e.resources = resources + + log.Printf("D! Discovered %d objects\n", len(e.nameCache)) + + return nil +} + +func (e *Endpoint) getClusters(root *view.ContainerView) (objectMap, error) { + var resources []mo.ClusterComputeResource + err := root.Retrieve(context.Background(), []string{"ClusterComputeResource"}, []string{"summary", "name", "parent"}, &resources) + if err != nil { + e.checkConnection() + return nil, err + } + m := make(objectMap) + for _, r := range resources { + m[r.ExtensibleManagedObject.Reference().Value] = objectRef{ + name: r.Name, ref: r.ExtensibleManagedObject.Reference(), parentRef: r.Parent} + } + return m, nil +} + +func (e *Endpoint) getHosts(root *view.ContainerView) (objectMap, error) { + var resources []mo.HostSystem + err := root.Retrieve(context.Background(), []string{"HostSystem"}, []string{"summary", "parent"}, &resources) + if err != nil { + e.checkConnection() + return nil, err + } + m := make(objectMap) + for _, r := range resources { + m[r.ExtensibleManagedObject.Reference().Value] = objectRef{ + name: r.Summary.Config.Name, ref: r.ExtensibleManagedObject.Reference(), parentRef: r.Parent} + } + return m, nil +} + +func (e *Endpoint) getVMs(root *view.ContainerView) (objectMap, error) { + var resources []mo.VirtualMachine + err := root.Retrieve(context.Background(), []string{"VirtualMachine"}, []string{"summary", "runtime.host"}, &resources) + if err != nil { + e.checkConnection() + return nil, err + } + m := make(objectMap) + for _, r := range resources { + m[r.ExtensibleManagedObject.Reference().Value] = objectRef{ + name: r.Summary.Config.Name, ref: r.ExtensibleManagedObject.Reference(), parentRef: r.Runtime.Host} + } + return m, nil +} + +func (e *Endpoint) getDatastores(root *view.ContainerView) (objectMap, error) { + var resources []mo.Datastore + err := root.Retrieve(context.Background(), []string{"Datastore"}, []string{"summary"}, &resources) + if err != nil { + e.checkConnection() + return nil, err + } + m := make(objectMap) + for _, r := range resources { + m[r.Summary.Name] = objectRef{ + name: r.Summary.Name, ref: r.ExtensibleManagedObject.Reference(), parentRef: r.Parent} + } + return m, nil +} + +func (e *Endpoint) collect(acc telegraf.Accumulator) error { + + var err error + if !e.initialized { + err := e.init() if err != nil { return err } - for _, vm := range vmMap { - nameCache[vm.ref.Reference().Value] = vm.name - } } - // Discover Datastores + // If discovery interval is disabled (0), discover on each collection cycle // - if e.Parent.GatherDatastores { - datastoreMap, err = e.getDatastores(ctx, conn.Root) + if e.Parent.ObjectDiscoveryInterval.Duration.Seconds() == 0 { + err = e.discover() if err != nil { + log.Printf("E! Error in discovery prior to collect for %s: %v", e.Url.Host, err) return err } - for _, datastore := range datastoreMap { - nameCache[datastore.ref.Reference().Value] = datastore.name - } } - // Atomically swap maps - // - e.collectMux.Lock() - defer e.collectMux.Unlock() - - e.nameCache = nameCache - e.datastoreMap = datastoreMap - e.vmMap = vmMap - e.hostMap = hostMap - e.clusterMap = clusterMap + for k, res := range e.resources { + if res.enabled { + count, duration, err := e.collectResource(k, acc) + if err != nil { + return err + } - log.Printf("D! Discovered %d objects\n", len(e.nameCache)) + acc.AddGauge("vsphere", + map[string]interface{}{"gather.count": count, "gather.duration": duration}, + map[string]string{"vcenter": e.Url.Host, "type": k}, + time.Now()) + } + } return nil } -func (e *Endpoint) collectResourceType(p *performance.Manager, ctx context.Context, alias string, acc telegraf.Accumulator, - objects objectMap, nameCache map[string]string, intervalDuration internal.Duration, isRealTime bool, metricIds []types.PerfMetricId) error { +func (e *Endpoint) collectResource(resourceType string, acc telegraf.Accumulator) (int, float64, error) { + + sampling := e.resources[resourceType].sampling + realTime := e.resources[resourceType].realTime + + conn, err := e.getConnection() + if err != nil { + return 0, 0, err + } + ctx := context.Background() // Object maps may change, so we need to hold the collect lock // @@ -256,43 +325,40 @@ func (e *Endpoint) collectResourceType(p *performance.Manager, ctx context.Conte defer e.collectMux.RUnlock() // Interval = 0 means collection for this metric was diabled, so don't even bother. - // - interval := int32(intervalDuration.Duration.Seconds()) - if interval <= 0 { - return nil - } - log.Printf("D! Resource type: %s, sampling interval is: %d", alias, interval) + log.Printf("D! Resource type: %s, sampling period is: %d", resourceType, sampling) // Do we have new data yet? // now := time.Now() - latest, hasLatest := e.lastColl[alias] + latest, hasLatest := e.lastColls[resourceType] if hasLatest { elapsed := time.Now().Sub(latest).Seconds() - if elapsed < float64(interval) { + if elapsed < float64(sampling) { // No new data would be available. We're outta here! // - return nil + return 0, 0, nil } } - e.lastColl[alias] = now - log.Printf("D! Collecting for %s", alias) - fullAlias := "vsphere." + alias + e.lastColls[resourceType] = now + + objects := e.resources[resourceType].objects + log.Printf("D! Collecting data metrics for %d objects of type %s for %s", len(objects), resourceType, e.Url.Host) + measurementName := "vsphere." + resourceType + count := 0 start := time.Now() - log.Printf("D! Query for %s returned %d objects", alias, len(objects)) pqs := make([]types.PerfQuerySpec, 0, e.Parent.ObjectsPerQuery) total := 0 for _, object := range objects { pq := types.PerfQuerySpec{ Entity: object.ref, MaxSample: 1, - MetricId: metricIds, - IntervalId: interval, + MetricId: e.resources[resourceType].metricIds, + IntervalId: sampling, } - if !isRealTime { - startTime := now.Add(-time.Duration(interval) * time.Second) + if !realTime { + startTime := now.Add(-time.Duration(sampling) * time.Second) pq.StartTime = &startTime pq.EndTime = &now } @@ -303,16 +369,18 @@ func (e *Endpoint) collectResourceType(p *performance.Manager, ctx context.Conte // Filled up a chunk or at end of data? Run a query with the collected objects // if len(pqs) >= int(e.Parent.ObjectsPerQuery) || total == len(objects) { - log.Printf("D! Querying %d objects of type %s for %s. Total processed: %d. Total objects %d\n", len(pqs), alias, e.Url.Host, total, len(objects)) - metrics, err := p.Query(ctx, pqs) + log.Printf("D! Querying %d objects of type %s for %s. Total processed: %d. Total objects %d\n", len(pqs), resourceType, e.Url.Host, total, len(objects)) + metrics, err := conn.Perf.Query(ctx, pqs) if err != nil { - log.Printf("E! Error processing resource type %s on %s", alias, e.Url.Host) - return err + log.Printf("E! Error querying metrics for %s on %s", resourceType, e.Url.Host) + e.checkConnection() + return count, time.Now().Sub(start).Seconds(), err } - ems, err := p.ToMetricSeries(ctx, metrics) + ems, err := conn.Perf.ToMetricSeries(ctx, metrics) if err != nil { - return err + e.checkConnection() + return count, time.Now().Sub(start).Seconds(), err } // Iterate through result and fields list @@ -323,11 +391,11 @@ func (e *Endpoint) collectResourceType(p *performance.Manager, ctx context.Conte name := v.Name for idx, value := range v.Value { f := map[string]interface{}{name: value} - objectName := nameCache[moid] + objectName := e.nameCache[moid] parent := "" parentRef := objects[moid].parentRef if parentRef != nil { - parent = nameCache[parentRef.Value] + parent = e.nameCache[parentRef.Value] } t := map[string]string{ @@ -362,7 +430,8 @@ func (e *Endpoint) collectResourceType(p *performance.Manager, ctx context.Conte } } - acc.AddFields(fullAlias, f, t, em.SampleInfo[idx].Timestamp) + acc.AddFields(measurementName, f, t, em.SampleInfo[idx].Timestamp) + count++ } } } @@ -370,124 +439,8 @@ func (e *Endpoint) collectResourceType(p *performance.Manager, ctx context.Conte } } - log.Printf("D! Collection of %s took %v\n", alias, time.Now().Sub(start)) - return nil -} - -func (e *Endpoint) collect(acc telegraf.Accumulator) error { - - start := time.Now() - - // If discovery interval is disabled (0), discover on each collection cycle - // - if e.Parent.ObjectDiscoveryInterval.Duration.Seconds() == 0 { - err := e.discover() - if err != nil { - return err - } - } - - conn, err := e.getConnection() - if err != nil { - return err - } - - ctx := context.Background() - - if e.Parent.GatherClusters { - err = e.collectResourceType(conn.Perf, ctx, "cluster", acc, e.clusterMap, e.nameCache, - e.Parent.ClusterSamplingPeriod, false, e.clusterMetricIds) - if err != nil { - return err - } - } - - if e.Parent.GatherHosts { - err = e.collectResourceType(conn.Perf, ctx, "host", acc, e.hostMap, e.nameCache, - e.Parent.HostSamplingPeriod, true, e.hostMetricIds) - if err != nil { - return err - } - } - - if e.Parent.GatherVms { - err = e.collectResourceType(conn.Perf, ctx, "vm", acc, e.vmMap, e.nameCache, - e.Parent.VmSamplingPeriod, true, e.vmMetricIds) - if err != nil { - return err - } - } - - if e.Parent.GatherDatastores { - err = e.collectResourceType(conn.Perf, ctx, "datastore", acc, e.datastoreMap, e.nameCache, - e.Parent.DatastoreSamplingPeriod, false, e.datastoreMetricIds) - if err != nil { - return err - } - } - - - // Add gauge to show how long it took to gather all the metrics on this cycle for a specific vcenter/endpoint - // - acc.AddGauge("vsphere", map[string]interface{}{"gather.duration": time.Now().Sub(start).Seconds()}, map[string]string{"vcenter": e.Url.Host}, time.Now()) - - return nil -} - -func (e *Endpoint) getVMs(ctx context.Context, root *view.ContainerView) (objectMap, error) { - var resources []mo.VirtualMachine - err := root.Retrieve(ctx, []string{"VirtualMachine"}, []string{"summary", "runtime.host"}, &resources) - if err != nil { - return nil, err - } - m := make(objectMap) - for _, r := range resources { - m[r.ExtensibleManagedObject.Reference().Value] = objectRef{ - name: r.Summary.Config.Name, ref: r.ExtensibleManagedObject.Reference(), parentRef: r.Runtime.Host} - } - return m, nil -} - -func (e *Endpoint) getHosts(ctx context.Context, root *view.ContainerView) (objectMap, error) { - var resources []mo.HostSystem - err := root.Retrieve(ctx, []string{"HostSystem"}, []string{"summary", "parent"}, &resources) - if err != nil { - return nil, err - } - m := make(objectMap) - for _, r := range resources { - m[r.ExtensibleManagedObject.Reference().Value] = objectRef{ - name: r.Summary.Config.Name, ref: r.ExtensibleManagedObject.Reference(), parentRef: r.Parent} - } - return m, nil -} - -func (e *Endpoint) getClusters(ctx context.Context, root *view.ContainerView) (objectMap, error) { - var resources []mo.ClusterComputeResource - err := root.Retrieve(ctx, []string{"ClusterComputeResource"}, []string{"summary", "name", "parent"}, &resources) - if err != nil { - return nil, err - } - m := make(objectMap) - for _, r := range resources { - m[r.ExtensibleManagedObject.Reference().Value] = objectRef{ - name: r.Name, ref: r.ExtensibleManagedObject.Reference(), parentRef: r.Parent} - } - return m, nil -} - -func (e *Endpoint) getDatastores(ctx context.Context, root *view.ContainerView) (objectMap, error) { - var resources []mo.Datastore - err := root.Retrieve(ctx, []string{"Datastore"}, []string{"summary"}, &resources) - if err != nil { - return nil, err - } - m := make(objectMap) - for _, r := range resources { - m[r.Summary.Name] = objectRef{ - name: r.Summary.Name, ref: r.ExtensibleManagedObject.Reference(), parentRef: r.Parent} - } - return m, nil + log.Printf("D! Collection of %s took %v\n", resourceType, time.Now().Sub(start)) + return count, time.Now().Sub(start).Seconds(), nil } func cleanDiskTag(disk string) string { @@ -504,3 +457,30 @@ func cleanDiskTag(disk string) string { return disk } + +func (e *Endpoint) getConnection() (*Client, error) { + if e.client == nil { + e.clientMux.Lock() + defer e.clientMux.Unlock() + if e.client == nil { + log.Printf("D! Creating new vCenter client for: %s\n", e.Url.Host) + conn, err := NewClient(e.Url, e.Parent.Timeout) + if err != nil { + return nil, err + } + e.client = conn + } + } + return e.client, nil +} + +func (e *Endpoint) checkConnection() { + if e.client != nil { + active, err := e.client.Client.SessionManager.SessionIsActive(context.Background()) + if !active || err != nil { + log.Printf("I! vCenter session no longer active, reseting client: %s", e.Url.Host) + e.client = nil + } + } +} + diff --git a/plugins/inputs/vsphere/vsphere.go b/plugins/inputs/vsphere/vsphere.go index f6f64d45eb1c7..15bddfff4bcc5 100644 --- a/plugins/inputs/vsphere/vsphere.go +++ b/plugins/inputs/vsphere/vsphere.go @@ -6,7 +6,6 @@ import ( "github.com/influxdata/telegraf/plugins/inputs" "github.com/vmware/govmomi/vim25/soap" "log" - "net/url" "sync" "time" ) @@ -27,12 +26,7 @@ type VSphere struct { ObjectDiscoveryInterval internal.Duration Timeout internal.Duration - VmSamplingPeriod internal.Duration - HostSamplingPeriod internal.Duration - ClusterSamplingPeriod internal.Duration - DatastoreSamplingPeriod internal.Duration - - endpoints []Endpoint + endpoints []*Endpoint } var sampleConfig = ` @@ -48,41 +42,31 @@ func (v *VSphere) Description() string { return "Read metrics from VMware vCenter" } -func (v *VSphere) vSphereInit() { +func (v *VSphere) checkEndpoints() { if v.endpoints != nil { return } - var wg sync.WaitGroup - - v.endpoints = make([]Endpoint, len(v.Vcenters)) + v.endpoints = make([]*Endpoint, len(v.Vcenters)) for i, rawUrl := range v.Vcenters { u, err := soap.ParseURL(rawUrl) if err != nil { log.Printf("E! Can't parse URL %s\n", rawUrl) } - wg.Add(1) - go func(url *url.URL, j int) { - defer wg.Done() - v.endpoints[j] = NewEndpoint(v, url) - }(u, i) + v.endpoints[i] = NewEndpoint(v, u) } - - wg.Wait() } func (v *VSphere) Gather(acc telegraf.Accumulator) error { - v.vSphereInit() - - start := time.Now() + v.checkEndpoints() var wg sync.WaitGroup for _, ep := range v.endpoints { wg.Add(1) - go func(endpoint Endpoint) { + go func(endpoint *Endpoint) { defer wg.Done() acc.AddError(endpoint.collect(acc)) }(ep) @@ -90,10 +74,6 @@ func (v *VSphere) Gather(acc telegraf.Accumulator) error { wg.Wait() - // Add gauge to show how long it took to gather all the metrics on this cycle - // - acc.AddGauge("vsphere", map[string]interface{}{"gather.duration": time.Now().Sub(start).Seconds()}, nil, time.Now()) - return nil } @@ -114,11 +94,6 @@ func init() { ObjectsPerQuery: 500, ObjectDiscoveryInterval: internal.Duration{Duration: time.Second * 300}, Timeout: internal.Duration{Duration: time.Second * 20}, - - ClusterSamplingPeriod: internal.Duration{Duration: time.Second * 300}, - HostSamplingPeriod: internal.Duration{Duration: time.Second * 20}, - VmSamplingPeriod: internal.Duration{Duration: time.Second * 20}, - DatastoreSamplingPeriod: internal.Duration{Duration: time.Second * 300}, } }) } From bc716deecec9791e34ae12f67b0563860f781aba Mon Sep 17 00:00:00 2001 From: Pierre Tessier Date: Sat, 2 Dec 2017 14:16:29 -0500 Subject: [PATCH 029/100] changed a log output debug --- plugins/inputs/vsphere/endpoint.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/inputs/vsphere/endpoint.go b/plugins/inputs/vsphere/endpoint.go index a4d76ff203dbf..dfbdf52055faf 100644 --- a/plugins/inputs/vsphere/endpoint.go +++ b/plugins/inputs/vsphere/endpoint.go @@ -160,7 +160,7 @@ func resolveMetricWildcards(metricMap map[string]*types.PerfCounterInfo, wildcar } func (e *Endpoint) discover() error { - log.Printf("I! Discover new objects for %s", e.Url.Host) + log.Printf("D! Discover new objects for %s", e.Url.Host) conn, err := e.getConnection() if err != nil { From d75ed2760ca81d023a7c96b9dbf9f11c195fc9f7 Mon Sep 17 00:00:00 2001 From: Pierre Tessier Date: Thu, 7 Dec 2017 10:35:44 -0500 Subject: [PATCH 030/100] full SSL support, objects_per_query default to 256 --- plugins/inputs/vsphere/README.MD | 17 +++++++- plugins/inputs/vsphere/client.go | 26 ++++++++++-- plugins/inputs/vsphere/endpoint.go | 64 +++++++++++++++--------------- plugins/inputs/vsphere/vsphere.go | 13 +++++- 4 files changed, 81 insertions(+), 39 deletions(-) diff --git a/plugins/inputs/vsphere/README.MD b/plugins/inputs/vsphere/README.MD index e4c4cc73cfab7..0e165201b7237 100644 --- a/plugins/inputs/vsphere/README.MD +++ b/plugins/inputs/vsphere/README.MD @@ -36,14 +36,21 @@ The VMware vSphere plugin uses the vSphere API to gather metrics from multiple v # gather_datastores = true # datastore_metrics = [] - ## number of objects to retreive per query (default: 500) - # objects_per_query = 500 + ## number of objects to retreive per query. set to 64 for vCenter 5.5 and 6.0 (default: 256) + # objects_per_query = 256 ## the interval before (re)discovering objects subject to metrics collection (default: 300s) # object_discovery_interval = "300s" ## timeout applies to any of the connection request made to vcenter # timeout = "20s" + + ## Optional SSL Config + # ssl_ca = /path/to/cafile + # ssl_cert = /path/to/certfile + # ssl_key = /path/to/keyfile + ## Use SSL but skip chain & host verification + # insecure_skip_verify = false ``` ### Metrics Selection @@ -63,6 +70,12 @@ A configuration to include all metrics under the host category that start with ` ``` +### Objects Per Query + +Default settings for vCenter 6.5 and above is 256. Prior versions of vCenter have this set to 64. A vCenter adminitrator +can change this setting, which should be reflected in this plugin. See this [VMware KB article](https://kb.vmware.com/s/article/2107096) +for more information. + ## Measurements & Fields - Cluster Stats diff --git a/plugins/inputs/vsphere/client.go b/plugins/inputs/vsphere/client.go index 8d583ac776291..a9514b89208c1 100644 --- a/plugins/inputs/vsphere/client.go +++ b/plugins/inputs/vsphere/client.go @@ -7,6 +7,7 @@ import ( "github.com/vmware/govmomi/performance" "github.com/vmware/govmomi/view" "net/url" + "log" ) type Client struct { @@ -16,18 +17,37 @@ type Client struct { Perf *performance.Manager } -func NewClient(url *url.URL, timeout internal.Duration) (*Client, error) { +func NewClient(url *url.URL, vs *VSphere) (*Client, error) { + + tlsCfg, err := internal.GetTLSConfig(vs.SSLCert, vs.SSLKey, vs.SSLCA, vs.InsecureSkipVerify) + if err != nil { + return nil, err + } + ctx := context.Background() - c, err := govmomi.NewClient(ctx, url, true) + + var c *govmomi.Client + if tlsCfg != nil && len(tlsCfg.Certificates) > 0 { + //TODO: remove this log output before final release + log.Printf("Creating client with Certificate: %s", url.Host) + c, err = govmomi.NewClientWithCertificate(ctx, url, vs.InsecureSkipVerify, tlsCfg.Certificates[0]) + } else { + //TODO: remove this log output before final release + log.Printf("Creating client: %s", url.Host) + c, err = govmomi.NewClient(ctx, url, vs.InsecureSkipVerify) + } if err != nil { return nil, err } - c.Timeout = timeout.Duration + c.Timeout = vs.Timeout.Duration + m := view.NewManager(c.Client) + v, err := m.CreateContainerView(ctx, c.ServiceContent.RootFolder, []string{}, true) if err != nil { return nil, err } + p := performance.NewManager(c.Client) return &Client{ diff --git a/plugins/inputs/vsphere/endpoint.go b/plugins/inputs/vsphere/endpoint.go index dfbdf52055faf..f8d66c85fbb59 100644 --- a/plugins/inputs/vsphere/endpoint.go +++ b/plugins/inputs/vsphere/endpoint.go @@ -100,13 +100,13 @@ func (e *Endpoint) init() error { } func (e *Endpoint) setupMetricIds() error { - conn, err := e.getConnection() + client, err := e.getClient() if err != nil { return err } ctx := context.Background() - metricMap, err := conn.Perf.CounterInfoByName(ctx) + metricMap, err := client.Perf.CounterInfoByName(ctx) if err != nil { return err } @@ -162,7 +162,7 @@ func resolveMetricWildcards(metricMap map[string]*types.PerfCounterInfo, wildcar func (e *Endpoint) discover() error { log.Printf("D! Discover new objects for %s", e.Url.Host) - conn, err := e.getConnection() + client, err := e.getClient() if err != nil { return err } @@ -175,13 +175,13 @@ func (e *Endpoint) discover() error { var objects objectMap switch k { case "cluster": - objects, err = e.getClusters(conn.Root) + objects, err = e.getClusters(client.Root) case "host": - objects, err = e.getHosts(conn.Root) + objects, err = e.getHosts(client.Root) case "vm": - objects, err = e.getVMs(conn.Root) + objects, err = e.getVMs(client.Root) case "datastore": - objects, err = e.getDatastores(conn.Root) + objects, err = e.getDatastores(client.Root) } if err != nil { return err @@ -215,7 +215,7 @@ func (e *Endpoint) getClusters(root *view.ContainerView) (objectMap, error) { var resources []mo.ClusterComputeResource err := root.Retrieve(context.Background(), []string{"ClusterComputeResource"}, []string{"summary", "name", "parent"}, &resources) if err != nil { - e.checkConnection() + e.checkClient() return nil, err } m := make(objectMap) @@ -230,7 +230,7 @@ func (e *Endpoint) getHosts(root *view.ContainerView) (objectMap, error) { var resources []mo.HostSystem err := root.Retrieve(context.Background(), []string{"HostSystem"}, []string{"summary", "parent"}, &resources) if err != nil { - e.checkConnection() + e.checkClient() return nil, err } m := make(objectMap) @@ -245,7 +245,7 @@ func (e *Endpoint) getVMs(root *view.ContainerView) (objectMap, error) { var resources []mo.VirtualMachine err := root.Retrieve(context.Background(), []string{"VirtualMachine"}, []string{"summary", "runtime.host"}, &resources) if err != nil { - e.checkConnection() + e.checkClient() return nil, err } m := make(objectMap) @@ -260,7 +260,7 @@ func (e *Endpoint) getDatastores(root *view.ContainerView) (objectMap, error) { var resources []mo.Datastore err := root.Retrieve(context.Background(), []string{"Datastore"}, []string{"summary"}, &resources) if err != nil { - e.checkConnection() + e.checkClient() return nil, err } m := make(objectMap) @@ -311,19 +311,6 @@ func (e *Endpoint) collect(acc telegraf.Accumulator) error { func (e *Endpoint) collectResource(resourceType string, acc telegraf.Accumulator) (int, float64, error) { sampling := e.resources[resourceType].sampling - realTime := e.resources[resourceType].realTime - - conn, err := e.getConnection() - if err != nil { - return 0, 0, err - } - ctx := context.Background() - - // Object maps may change, so we need to hold the collect lock - // - e.collectMux.RLock() - defer e.collectMux.RUnlock() - // Interval = 0 means collection for this metric was diabled, so don't even bother. log.Printf("D! Resource type: %s, sampling period is: %d", resourceType, sampling) @@ -344,6 +331,18 @@ func (e *Endpoint) collectResource(resourceType string, acc telegraf.Accumulator objects := e.resources[resourceType].objects log.Printf("D! Collecting data metrics for %d objects of type %s for %s", len(objects), resourceType, e.Url.Host) + + client, err := e.getClient() + if err != nil { + return 0, 0, err + } + ctx := context.Background() + + // Object maps may change, so we need to hold the collect lock + // + e.collectMux.RLock() + defer e.collectMux.RUnlock() + measurementName := "vsphere." + resourceType count := 0 start := time.Now() @@ -357,6 +356,7 @@ func (e *Endpoint) collectResource(resourceType string, acc telegraf.Accumulator IntervalId: sampling, } + realTime := e.resources[resourceType].realTime if !realTime { startTime := now.Add(-time.Duration(sampling) * time.Second) pq.StartTime = &startTime @@ -370,16 +370,16 @@ func (e *Endpoint) collectResource(resourceType string, acc telegraf.Accumulator // if len(pqs) >= int(e.Parent.ObjectsPerQuery) || total == len(objects) { log.Printf("D! Querying %d objects of type %s for %s. Total processed: %d. Total objects %d\n", len(pqs), resourceType, e.Url.Host, total, len(objects)) - metrics, err := conn.Perf.Query(ctx, pqs) + metrics, err := client.Perf.Query(ctx, pqs) if err != nil { log.Printf("E! Error querying metrics for %s on %s", resourceType, e.Url.Host) - e.checkConnection() + e.checkClient() return count, time.Now().Sub(start).Seconds(), err } - ems, err := conn.Perf.ToMetricSeries(ctx, metrics) + ems, err := client.Perf.ToMetricSeries(ctx, metrics) if err != nil { - e.checkConnection() + e.checkClient() return count, time.Now().Sub(start).Seconds(), err } @@ -458,23 +458,23 @@ func cleanDiskTag(disk string) string { return disk } -func (e *Endpoint) getConnection() (*Client, error) { +func (e *Endpoint) getClient() (*Client, error) { if e.client == nil { e.clientMux.Lock() defer e.clientMux.Unlock() if e.client == nil { log.Printf("D! Creating new vCenter client for: %s\n", e.Url.Host) - conn, err := NewClient(e.Url, e.Parent.Timeout) + client, err := NewClient(e.Url, e.Parent) if err != nil { return nil, err } - e.client = conn + e.client = client } } return e.client, nil } -func (e *Endpoint) checkConnection() { +func (e *Endpoint) checkClient() { if e.client != nil { active, err := e.client.Client.SessionManager.SessionIsActive(context.Background()) if !active || err != nil { diff --git a/plugins/inputs/vsphere/vsphere.go b/plugins/inputs/vsphere/vsphere.go index 15bddfff4bcc5..9eb998a43625c 100644 --- a/plugins/inputs/vsphere/vsphere.go +++ b/plugins/inputs/vsphere/vsphere.go @@ -26,7 +26,16 @@ type VSphere struct { ObjectDiscoveryInterval internal.Duration Timeout internal.Duration - endpoints []*Endpoint + // Path to CA file + SSLCA string `toml:"ssl_ca"` + // Path to host cert file + SSLCert string `toml:"ssl_cert"` + // Path to cert key file + SSLKey string `toml:"ssl_key"` + // Use SSL but skip chain & host verification + InsecureSkipVerify bool + + endpoints []*Endpoint } var sampleConfig = ` @@ -91,7 +100,7 @@ func init() { GatherDatastores: true, DatastoreMetrics: nil, - ObjectsPerQuery: 500, + ObjectsPerQuery: 256, ObjectDiscoveryInterval: internal.Duration{Duration: time.Second * 300}, Timeout: internal.Duration{Duration: time.Second * 20}, } From 718dfb2ab52886e73c411cb4d7d58d6d4887e72c Mon Sep 17 00:00:00 2001 From: Pierre Tessier Date: Sun, 10 Dec 2017 10:08:54 -0500 Subject: [PATCH 031/100] typo + initial unit test commit --- plugins/inputs/vsphere/README.MD | 2 +- plugins/inputs/vsphere/vsphere_test.go | 57 ++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 plugins/inputs/vsphere/vsphere_test.go diff --git a/plugins/inputs/vsphere/README.MD b/plugins/inputs/vsphere/README.MD index 0e165201b7237..9ede76594fb6d 100644 --- a/plugins/inputs/vsphere/README.MD +++ b/plugins/inputs/vsphere/README.MD @@ -72,7 +72,7 @@ A configuration to include all metrics under the host category that start with ` ### Objects Per Query -Default settings for vCenter 6.5 and above is 256. Prior versions of vCenter have this set to 64. A vCenter adminitrator +Default settings for vCenter 6.5 and above is 256. Prior versions of vCenter have this set to 64. A vCenter administrator can change this setting, which should be reflected in this plugin. See this [VMware KB article](https://kb.vmware.com/s/article/2107096) for more information. diff --git a/plugins/inputs/vsphere/vsphere_test.go b/plugins/inputs/vsphere/vsphere_test.go new file mode 100644 index 0000000000000..0d61d0156ef35 --- /dev/null +++ b/plugins/inputs/vsphere/vsphere_test.go @@ -0,0 +1,57 @@ +package vsphere + +import ( + "github.com/vmware/govmomi/simulator" + "crypto/tls" + "testing" + "fmt" + "time" + "github.com/influxdata/telegraf/internal" + "github.com/influxdata/telegraf/testutil" + "github.com/stretchr/testify/require" +) + +func defaultVSphere() *VSphere { + return &VSphere{ + GatherClusters: true, + ClusterMetrics: nil, + GatherHosts: true, + HostMetrics: nil, + GatherVms: true, + VmMetrics: nil, + GatherDatastores: true, + DatastoreMetrics: nil, + InsecureSkipVerify: true, + + ObjectsPerQuery: 256, + ObjectDiscoveryInterval: internal.Duration{Duration: time.Second * 300}, + Timeout: internal.Duration{Duration: time.Second * 20}, + } +} + +func createSim() (*simulator.Model, *simulator.Server) { + model := simulator.VPX() + + err := model.Create() + if err != nil { + fmt.Errorf("Error creating model: %s\n", err) + } + + model.Service.TLS = new(tls.Config) + + s := model.Service.NewServer() + fmt.Printf("Server created at: %s\n", s.URL) + + return model, s +} + +func TestAll(t *testing.T) { + m, s := createSim() + defer m.Remove() + defer s.Close() + + var acc testutil.Accumulator + v := defaultVSphere() + v.Vcenters = []string{s.URL.String()} + require.NoError(t, v.Gather(&acc)) +} \ No newline at end of file From 950018f1d60a3ff3aca88c026b2510bc7745e535 Mon Sep 17 00:00:00 2001 From: Pierre Tessier Date: Wed, 28 Feb 2018 10:21:22 -0500 Subject: [PATCH 032/100] refactored resources --- plugins/inputs/vsphere/README.MD | 11 +- plugins/inputs/vsphere/endpoint.go | 162 +++++++++++++++++------------ 2 files changed, 101 insertions(+), 72 deletions(-) diff --git a/plugins/inputs/vsphere/README.MD b/plugins/inputs/vsphere/README.MD index 9ede76594fb6d..e45c05c0e9fed 100644 --- a/plugins/inputs/vsphere/README.MD +++ b/plugins/inputs/vsphere/README.MD @@ -56,11 +56,12 @@ The VMware vSphere plugin uses the vSphere API to gather metrics from multiple v ### Metrics Selection vSphere metrics are available in 4 categories: Clusters, Hosts, VMs, and Datastores. Each category can be enabled -independently, and for each category a list with wildcards can be applied to filter metrics. If the _metrics -property is not specified all metrics will be included for the category. Items can be specified with standard glob -style wildcards (*, ?). Any item can be negated with an exclamation mark (!) as the first character. Metrics will be -checked against every item in the list that applies, meaning you can include multiple metrics with the first item, and -negate selectively within that list on a subsequent item. +independently, and for each category a list with wildcards can be applied to filter metrics. If the <category>_metrics +property is not specified all metrics will be included for the category. Metrics can be specified with standard glob +style wildcards (*, ?). You can specify metrics to be negated, by using an exclamation mark (!) as the first character. +Metrics will be checked against every item in the list in order, meaning you can include multiple metrics with the first +item, and negate selectively within that list on a subsequent item. Negating metrics that have not been whitelisted in +a prior item of the list, has no effect. A configuration to include all metrics under the host category that start with `cpu.*` but will not include `cpu.idle.summation`. diff --git a/plugins/inputs/vsphere/endpoint.go b/plugins/inputs/vsphere/endpoint.go index f8d66c85fbb59..445f60c56298f 100644 --- a/plugins/inputs/vsphere/endpoint.go +++ b/plugins/inputs/vsphere/endpoint.go @@ -15,25 +15,26 @@ import ( ) type Endpoint struct { - Parent *VSphere - Url *url.URL - client *Client - lastColls map[string]time.Time - nameCache map[string]string - resources map[string]resource - discoveryTicker *time.Ticker - clientMux *sync.Mutex - collectMux *sync.RWMutex - initialized bool + Parent *VSphere + Url *url.URL + client *Client + lastColls map[string]time.Time + nameCache map[string]string + resources map[string]resource + discoveryTicker *time.Ticker + clientMux *sync.Mutex + collectMux *sync.RWMutex + initialized bool } type resource struct { - enabled bool - realTime bool - sampling int32 - objects objectMap - metricIds []types.PerfMetricId - wildcards []string + enabled bool + realTime bool + sampling int32 + objects objectMap + metricIds []types.PerfMetricId + wildcards []string + getObjects func(*view.ContainerView) (objectMap, error) } type objectMap map[string]objectRef @@ -46,20 +47,48 @@ type objectRef struct { func NewEndpoint(parent *VSphere, url *url.URL) *Endpoint { e := Endpoint{ - Url: url, - Parent: parent, - lastColls: make(map[string]time.Time), - nameCache: make(map[string]string), - clientMux: &sync.Mutex{}, - collectMux: &sync.RWMutex{}, - initialized: false, + Url: url, + Parent: parent, + lastColls: make(map[string]time.Time), + nameCache: make(map[string]string), + clientMux: &sync.Mutex{}, + collectMux: &sync.RWMutex{}, + initialized: false, } e.resources = map[string]resource{ - "cluster": {enabled: parent.GatherClusters, realTime: false, sampling: 300, objects: make(objectMap), wildcards: parent.ClusterMetrics}, - "host": {enabled: parent.GatherHosts, realTime: true, sampling: 20, objects: make(objectMap), wildcards: parent.HostMetrics}, - "vm": {enabled: parent.GatherVms, realTime: true, sampling: 20, objects: make(objectMap), wildcards: parent.VmMetrics}, - "datastore": {enabled: parent.GatherDatastores, realTime: false, sampling: 300, objects: make(objectMap), wildcards: parent.DatastoreMetrics}, + "cluster": { + enabled: parent.GatherClusters, + realTime: false, + sampling: 300, + objects: make(objectMap), + wildcards: parent.ClusterMetrics, + getObjects: getClusters, + }, + "host": { + enabled: parent.GatherHosts, + realTime: true, + sampling: 20, + objects: make(objectMap), + wildcards: parent.HostMetrics, + getObjects: getHosts, + }, + "vm": { + enabled: parent.GatherVms, + realTime: true, + sampling: 20, + objects: make(objectMap), + wildcards: parent.VmMetrics, + getObjects: getVMs, + }, + "datastore": { + enabled: parent.GatherDatastores, + realTime: false, + sampling: 300, + objects: make(objectMap), + wildcards: parent.DatastoreMetrics, + getObjects: getDatastores, + }, } return &e @@ -111,10 +140,13 @@ func (e *Endpoint) setupMetricIds() error { return err } - for _, res := range e.resources { - res.metricIds, err = resolveMetricWildcards(metricMap, res.wildcards) - if err != nil { - return err + for k, res := range e.resources { + if res.enabled { + res.metricIds, err = resolveMetricWildcards(metricMap, res.wildcards) + if err != nil { + return err + } + e.resources[k] = res } } @@ -124,7 +156,7 @@ func (e *Endpoint) setupMetricIds() error { func resolveMetricWildcards(metricMap map[string]*types.PerfCounterInfo, wildcards []string) ([]types.PerfMetricId, error) { // Nothing specified assumes we're looking at everything // - if wildcards == nil { + if wildcards == nil || len(wildcards) == 0 { return nil, nil } tmpMap := make(map[string]types.PerfMetricId) @@ -170,24 +202,18 @@ func (e *Endpoint) discover() error { nameCache := make(map[string]string) resources := e.resources + // Populate resource objects, and endpoint name cache + // for k, res := range resources { if res.enabled { var objects objectMap - switch k { - case "cluster": - objects, err = e.getClusters(client.Root) - case "host": - objects, err = e.getHosts(client.Root) - case "vm": - objects, err = e.getVMs(client.Root) - case "datastore": - objects, err = e.getDatastores(client.Root) - } + objects, err = res.getObjects(client.Root) if err != nil { + e.checkClient() return err } - for _, obj := range res.objects { + for _, obj := range objects { nameCache[obj.ref.Reference().Value] = obj.name } res.objects = objects @@ -206,16 +232,15 @@ func (e *Endpoint) discover() error { e.nameCache = nameCache e.resources = resources - log.Printf("D! Discovered %d objects\n", len(e.nameCache)) + log.Printf("D! Discovered %d objects for %s", len(nameCache), e.Url.Host) return nil } -func (e *Endpoint) getClusters(root *view.ContainerView) (objectMap, error) { +func getClusters(root *view.ContainerView) (objectMap, error) { var resources []mo.ClusterComputeResource err := root.Retrieve(context.Background(), []string{"ClusterComputeResource"}, []string{"summary", "name", "parent"}, &resources) if err != nil { - e.checkClient() return nil, err } m := make(objectMap) @@ -226,11 +251,10 @@ func (e *Endpoint) getClusters(root *view.ContainerView) (objectMap, error) { return m, nil } -func (e *Endpoint) getHosts(root *view.ContainerView) (objectMap, error) { +func getHosts(root *view.ContainerView) (objectMap, error) { var resources []mo.HostSystem err := root.Retrieve(context.Background(), []string{"HostSystem"}, []string{"summary", "parent"}, &resources) if err != nil { - e.checkClient() return nil, err } m := make(objectMap) @@ -241,11 +265,10 @@ func (e *Endpoint) getHosts(root *view.ContainerView) (objectMap, error) { return m, nil } -func (e *Endpoint) getVMs(root *view.ContainerView) (objectMap, error) { +func getVMs(root *view.ContainerView) (objectMap, error) { var resources []mo.VirtualMachine err := root.Retrieve(context.Background(), []string{"VirtualMachine"}, []string{"summary", "runtime.host"}, &resources) if err != nil { - e.checkClient() return nil, err } m := make(objectMap) @@ -256,11 +279,10 @@ func (e *Endpoint) getVMs(root *view.ContainerView) (objectMap, error) { return m, nil } -func (e *Endpoint) getDatastores(root *view.ContainerView) (objectMap, error) { +func getDatastores(root *view.ContainerView) (objectMap, error) { var resources []mo.Datastore err := root.Retrieve(context.Background(), []string{"Datastore"}, []string{"summary"}, &resources) if err != nil { - e.checkClient() return nil, err } m := make(objectMap) @@ -310,12 +332,9 @@ func (e *Endpoint) collect(acc telegraf.Accumulator) error { func (e *Endpoint) collectResource(resourceType string, acc telegraf.Accumulator) (int, float64, error) { - sampling := e.resources[resourceType].sampling - // Interval = 0 means collection for this metric was diabled, so don't even bother. - log.Printf("D! Resource type: %s, sampling period is: %d", resourceType, sampling) - // Do we have new data yet? // + sampling := e.resources[resourceType].sampling now := time.Now() latest, hasLatest := e.lastColls[resourceType] if hasLatest { @@ -323,14 +342,19 @@ func (e *Endpoint) collectResource(resourceType string, acc telegraf.Accumulator if elapsed < float64(sampling) { // No new data would be available. We're outta here! // + log.Printf("D! Sampling period for %s of %d has not elapsed for %s", resourceType, sampling, e.Url.Host) return 0, 0, nil } } e.lastColls[resourceType] = now + metricIds := e.resources[resourceType].metricIds objects := e.resources[resourceType].objects - log.Printf("D! Collecting data metrics for %d objects of type %s for %s", len(objects), resourceType, e.Url.Host) - + if len(metricIds) == 0 { + log.Printf("D! Collecting all metrics for %d objects of type %s for %s", len(objects), resourceType, e.Url.Host) + } else { + log.Printf("D! Collecting %d metrics for %d objects of type %s for %s", len(metricIds), len(objects), resourceType, e.Url.Host) + } client, err := e.getClient() if err != nil { @@ -352,13 +376,14 @@ func (e *Endpoint) collectResource(resourceType string, acc telegraf.Accumulator pq := types.PerfQuerySpec{ Entity: object.ref, MaxSample: 1, - MetricId: e.resources[resourceType].metricIds, + MetricId: metricIds, IntervalId: sampling, } realTime := e.resources[resourceType].realTime if !realTime { - startTime := now.Add(-time.Duration(sampling) * time.Second) + var startTime = time.Time{} + startTime = now.Add(-time.Duration(sampling) * time.Second) pq.StartTime = &startTime pq.EndTime = &now } @@ -369,10 +394,10 @@ func (e *Endpoint) collectResource(resourceType string, acc telegraf.Accumulator // Filled up a chunk or at end of data? Run a query with the collected objects // if len(pqs) >= int(e.Parent.ObjectsPerQuery) || total == len(objects) { - log.Printf("D! Querying %d objects of type %s for %s. Total processed: %d. Total objects %d\n", len(pqs), resourceType, e.Url.Host, total, len(objects)) + log.Printf("D! Querying %d objects of type %s for %s. Object count: %d. Total objects %d", len(pqs), resourceType, e.Url.Host, total, len(objects)) metrics, err := client.Perf.Query(ctx, pqs) if err != nil { - log.Printf("E! Error querying metrics for %s on %s", resourceType, e.Url.Host) + log.Printf("E! Error querying metrics of %s for %s", resourceType, e.Url.Host) e.checkClient() return count, time.Now().Sub(start).Seconds(), err } @@ -439,7 +464,7 @@ func (e *Endpoint) collectResource(resourceType string, acc telegraf.Accumulator } } - log.Printf("D! Collection of %s took %v\n", resourceType, time.Now().Sub(start)) + log.Printf("D! Collection of %s for %s, took %v returning %d metrics", resourceType, e.Url.Host, time.Now().Sub(start), count) return count, time.Now().Sub(start).Seconds(), nil } @@ -463,7 +488,7 @@ func (e *Endpoint) getClient() (*Client, error) { e.clientMux.Lock() defer e.clientMux.Unlock() if e.client == nil { - log.Printf("D! Creating new vCenter client for: %s\n", e.Url.Host) + log.Printf("D! Creating new vCenter client for: %s", e.Url.Host) client, err := NewClient(e.Url, e.Parent) if err != nil { return nil, err @@ -477,10 +502,13 @@ func (e *Endpoint) getClient() (*Client, error) { func (e *Endpoint) checkClient() { if e.client != nil { active, err := e.client.Client.SessionManager.SessionIsActive(context.Background()) - if !active || err != nil { - log.Printf("I! vCenter session no longer active, reseting client: %s", e.Url.Host) + if err != nil { + log.Printf("E! SessionIsActive returned an error on %s: %v", e.Url.Host, err) + e.client = nil + } + if !active { + log.Printf("I! Session no longer active, reseting client: %s", e.Url.Host) e.client = nil } } } - From 664e73b68a5dd30a43bbb3fe27594053a496f7a8 Mon Sep 17 00:00:00 2001 From: Pierre Tessier Date: Thu, 15 Mar 2018 10:57:55 -0500 Subject: [PATCH 033/100] Fixed 5m timing issue and point tags --- plugins/inputs/vsphere/endpoint.go | 63 +++++++++++++++++++----------- 1 file changed, 40 insertions(+), 23 deletions(-) diff --git a/plugins/inputs/vsphere/endpoint.go b/plugins/inputs/vsphere/endpoint.go index 445f60c56298f..3537b9008d048 100644 --- a/plugins/inputs/vsphere/endpoint.go +++ b/plugins/inputs/vsphere/endpoint.go @@ -334,26 +334,28 @@ func (e *Endpoint) collectResource(resourceType string, acc telegraf.Accumulator // Do we have new data yet? // - sampling := e.resources[resourceType].sampling + res := e.resources[resourceType] now := time.Now() latest, hasLatest := e.lastColls[resourceType] if hasLatest { elapsed := time.Now().Sub(latest).Seconds() - if elapsed < float64(sampling) { + if elapsed < float64(res.sampling) { // No new data would be available. We're outta here! // - log.Printf("D! Sampling period for %s of %d has not elapsed for %s", resourceType, sampling, e.Url.Host) + log.Printf("D! Sampling period for %s of %d has not elapsed for %s", resourceType, res.sampling, e.Url.Host) return 0, 0, nil } } - e.lastColls[resourceType] = now - metricIds := e.resources[resourceType].metricIds - objects := e.resources[resourceType].objects - if len(metricIds) == 0 { - log.Printf("D! Collecting all metrics for %d objects of type %s for %s", len(objects), resourceType, e.Url.Host) + if !hasLatest { + latest = now.Add(-time.Duration(res.sampling) * time.Second) + e.lastColls[resourceType] = latest + } + + if len(res.metricIds) == 0 { + log.Printf("D! Collecting all metrics for %d objects of type %s for %s", len(res.objects), resourceType, e.Url.Host) } else { - log.Printf("D! Collecting %d metrics for %d objects of type %s for %s", len(metricIds), len(objects), resourceType, e.Url.Host) + log.Printf("D! Collecting %d metrics for %d objects of type %s for %s", len(res.metricIds), len(res.objects), resourceType, e.Url.Host) } client, err := e.getClient() @@ -370,21 +372,19 @@ func (e *Endpoint) collectResource(resourceType string, acc telegraf.Accumulator measurementName := "vsphere." + resourceType count := 0 start := time.Now() - pqs := make([]types.PerfQuerySpec, 0, e.Parent.ObjectsPerQuery) total := 0 - for _, object := range objects { + lastTS := latest + pqs := make([]types.PerfQuerySpec, 0, e.Parent.ObjectsPerQuery) + for _, object := range res.objects { pq := types.PerfQuerySpec{ Entity: object.ref, MaxSample: 1, - MetricId: metricIds, - IntervalId: sampling, + MetricId: res.metricIds, + IntervalId: res.sampling, } - realTime := e.resources[resourceType].realTime - if !realTime { - var startTime = time.Time{} - startTime = now.Add(-time.Duration(sampling) * time.Second) - pq.StartTime = &startTime + if !res.realTime { + pq.StartTime = &latest pq.EndTime = &now } @@ -393,8 +393,8 @@ func (e *Endpoint) collectResource(resourceType string, acc telegraf.Accumulator // Filled up a chunk or at end of data? Run a query with the collected objects // - if len(pqs) >= int(e.Parent.ObjectsPerQuery) || total == len(objects) { - log.Printf("D! Querying %d objects of type %s for %s. Object count: %d. Total objects %d", len(pqs), resourceType, e.Url.Host, total, len(objects)) + if len(pqs) >= int(e.Parent.ObjectsPerQuery) || total == len(res.objects) { + log.Printf("D! Querying %d objects of type %s for %s. Object count: %d. Total objects %d", len(pqs), resourceType, e.Url.Host, total, len(res.objects)) metrics, err := client.Perf.Query(ctx, pqs) if err != nil { log.Printf("E! Error querying metrics of %s for %s", resourceType, e.Url.Host) @@ -418,7 +418,7 @@ func (e *Endpoint) collectResource(resourceType string, acc telegraf.Accumulator f := map[string]interface{}{name: value} objectName := e.nameCache[moid] parent := "" - parentRef := objects[moid].parentRef + parentRef := res.objects[moid].parentRef if parentRef != nil { parent = e.nameCache[parentRef.Value] } @@ -427,7 +427,15 @@ func (e *Endpoint) collectResource(resourceType string, acc telegraf.Accumulator "vcenter": e.Url.Host, "hostname": objectName, "moid": moid, - "parent": parent, + //"parent": parent, + } + switch resourceType { + case "host": + t["cluster"] = parent + break + case "vm": + t["esxhost"] = parent + break } if v.Instance != "" { @@ -455,7 +463,12 @@ func (e *Endpoint) collectResource(resourceType string, acc telegraf.Accumulator } } - acc.AddFields(measurementName, f, t, em.SampleInfo[idx].Timestamp) + ts := em.SampleInfo[idx].Timestamp + if ts.After(lastTS) { + lastTS = ts + } + + acc.AddFields(measurementName, f, t, ts) count++ } } @@ -464,6 +477,10 @@ func (e *Endpoint) collectResource(resourceType string, acc telegraf.Accumulator } } + if count > 0 { + e.lastColls[resourceType] = lastTS + } + log.Printf("D! Collection of %s for %s, took %v returning %d metrics", resourceType, e.Url.Host, time.Now().Sub(start), count) return count, time.Now().Sub(start).Seconds(), nil } From c87db2b1bb0eb33f3541f23f2d7254b47a4bae89 Mon Sep 17 00:00:00 2001 From: Pierre Tessier Date: Wed, 28 Mar 2018 18:37:30 +0100 Subject: [PATCH 034/100] added cluster point tag to vm --- plugins/inputs/vsphere/endpoint.go | 61 +++++++++++++++++------------- 1 file changed, 35 insertions(+), 26 deletions(-) diff --git a/plugins/inputs/vsphere/endpoint.go b/plugins/inputs/vsphere/endpoint.go index 3537b9008d048..fd07ee9a7b73e 100644 --- a/plugins/inputs/vsphere/endpoint.go +++ b/plugins/inputs/vsphere/endpoint.go @@ -205,16 +205,17 @@ func (e *Endpoint) discover() error { // Populate resource objects, and endpoint name cache // for k, res := range resources { - if res.enabled { - var objects objectMap - objects, err = res.getObjects(client.Root) + // Need to do this for all resource types even if they are not enabled (but datastore) + // + if res.enabled || k != "datastore" { + objects, err := res.getObjects(client.Root) if err != nil { e.checkClient() return err } for _, obj := range objects { - nameCache[obj.ref.Reference().Value] = obj.name + nameCache[obj.ref.Value] = obj.name } res.objects = objects resources[k] = res @@ -239,7 +240,7 @@ func (e *Endpoint) discover() error { func getClusters(root *view.ContainerView) (objectMap, error) { var resources []mo.ClusterComputeResource - err := root.Retrieve(context.Background(), []string{"ClusterComputeResource"}, []string{"summary", "name", "parent"}, &resources) + err := root.Retrieve(context.Background(), []string{"ClusterComputeResource"}, []string{"name", "parent"}, &resources) if err != nil { return nil, err } @@ -253,42 +254,42 @@ func getClusters(root *view.ContainerView) (objectMap, error) { func getHosts(root *view.ContainerView) (objectMap, error) { var resources []mo.HostSystem - err := root.Retrieve(context.Background(), []string{"HostSystem"}, []string{"summary", "parent"}, &resources) + err := root.Retrieve(context.Background(), []string{"HostSystem"}, []string{"name", "parent"}, &resources) if err != nil { return nil, err } m := make(objectMap) for _, r := range resources { m[r.ExtensibleManagedObject.Reference().Value] = objectRef{ - name: r.Summary.Config.Name, ref: r.ExtensibleManagedObject.Reference(), parentRef: r.Parent} + name: r.Name, ref: r.ExtensibleManagedObject.Reference(), parentRef: r.Parent} } return m, nil } func getVMs(root *view.ContainerView) (objectMap, error) { var resources []mo.VirtualMachine - err := root.Retrieve(context.Background(), []string{"VirtualMachine"}, []string{"summary", "runtime.host"}, &resources) + err := root.Retrieve(context.Background(), []string{"VirtualMachine"}, []string{"name", "runtime.host"}, &resources) if err != nil { return nil, err } m := make(objectMap) for _, r := range resources { m[r.ExtensibleManagedObject.Reference().Value] = objectRef{ - name: r.Summary.Config.Name, ref: r.ExtensibleManagedObject.Reference(), parentRef: r.Runtime.Host} + name: r.Name, ref: r.ExtensibleManagedObject.Reference(), parentRef: r.Runtime.Host} } return m, nil } func getDatastores(root *view.ContainerView) (objectMap, error) { var resources []mo.Datastore - err := root.Retrieve(context.Background(), []string{"Datastore"}, []string{"summary"}, &resources) + err := root.Retrieve(context.Background(), []string{"Datastore"}, []string{"name", "parent"}, &resources) if err != nil { return nil, err } m := make(objectMap) for _, r := range resources { m[r.Summary.Name] = objectRef{ - name: r.Summary.Name, ref: r.ExtensibleManagedObject.Reference(), parentRef: r.Parent} + name: r.Name, ref: r.ExtensibleManagedObject.Reference(), parentRef: r.Parent} } return m, nil } @@ -415,27 +416,34 @@ func (e *Endpoint) collectResource(resourceType string, acc telegraf.Accumulator for _, v := range em.Value { name := v.Name for idx, value := range v.Value { - f := map[string]interface{}{name: value} - objectName := e.nameCache[moid] - parent := "" - parentRef := res.objects[moid].parentRef - if parentRef != nil { - parent = e.nameCache[parentRef.Value] - } + objectName := e.nameCache[moid] t := map[string]string{ "vcenter": e.Url.Host, "hostname": objectName, "moid": moid, - //"parent": parent, } - switch resourceType { - case "host": - t["cluster"] = parent - break - case "vm": - t["esxhost"] = parent - break + + objectRef, ok := res.objects[moid] + if ok { + parent := e.nameCache[objectRef.parentRef.Value] + switch resourceType { + case "host": + t["cluster"] = parent + break + + case "vm": + t["esxhost"] = parent + hostRes := e.resources["host"] + hostRef, ok := hostRes.objects[objectRef.parentRef.Value] + if ok { + cluster, ok := e.nameCache[hostRef.parentRef.Value] + if ok { + t["cluster"] = cluster + } + } + break + } } if v.Instance != "" { @@ -468,6 +476,7 @@ func (e *Endpoint) collectResource(resourceType string, acc telegraf.Accumulator lastTS = ts } + f := map[string]interface{}{name: value} acc.AddFields(measurementName, f, t, ts) count++ } From ce9dbb5491ac36383f7b6e32fab24a8dc1af22b1 Mon Sep 17 00:00:00 2001 From: "pontus@rydin@gmail.com" Date: Sat, 30 Sep 2017 16:07:01 -0400 Subject: [PATCH 035/100] Added vsphere input plugin. --- plugins/inputs/all/all.go | 1 + plugins/inputs/vsphere/vsphere.go | 237 +++++++++++++++++++ plugins/inputs/zipkin/codec/codec.go | 2 +- plugins/inputs/zipkin/codec/jsonV1/jsonV1.go | 2 +- plugins/inputs/zipkin/codec/thrift/thrift.go | 2 +- 5 files changed, 241 insertions(+), 3 deletions(-) create mode 100644 plugins/inputs/vsphere/vsphere.go diff --git a/plugins/inputs/all/all.go b/plugins/inputs/all/all.go index e3264ef8be4bf..f6c71ebbf45f1 100644 --- a/plugins/inputs/all/all.go +++ b/plugins/inputs/all/all.go @@ -103,6 +103,7 @@ import ( _ "github.com/influxdata/telegraf/plugins/inputs/udp_listener" _ "github.com/influxdata/telegraf/plugins/inputs/unbound" _ "github.com/influxdata/telegraf/plugins/inputs/varnish" + _ "github.com/influxdata/telegraf/plugins/inputs/vsphere" _ "github.com/influxdata/telegraf/plugins/inputs/webhooks" _ "github.com/influxdata/telegraf/plugins/inputs/win_perf_counters" _ "github.com/influxdata/telegraf/plugins/inputs/win_services" diff --git a/plugins/inputs/vsphere/vsphere.go b/plugins/inputs/vsphere/vsphere.go new file mode 100644 index 0000000000000..0930f71fcbc5e --- /dev/null +++ b/plugins/inputs/vsphere/vsphere.go @@ -0,0 +1,237 @@ +package vsphere + +import ( + "github.com/influxdata/telegraf" + "github.com/vmware/govmomi/vim25/soap" + "github.com/vmware/govmomi" + "github.com/vmware/govmomi/view" + "github.com/vmware/govmomi/vim25/mo" + "context" + "github.com/vmware/govmomi/performance" + "github.com/vmware/govmomi/vim25/types" + "sync" + "github.com/influxdata/telegraf/plugins/inputs" +) + +const Interval = 20 + +type Endpoint struct { + Url string + ciCache map[int32]types.PerfCounterInfo + mux sync.Mutex +} + +type VSphere struct { + Vcenters []string + endpoints []Endpoint +} + +func (e *Endpoint) Init(p *performance.Manager) error { + e.mux.Lock() + defer e.mux.Unlock() + if e.ciCache != nil { + return nil + } + ctx := context.Background() + defer p.Destroy(ctx) + e.ciCache = make(map[int32]types.PerfCounterInfo) + cis, err := p.CounterInfo(ctx) + if err != nil { + return err + } + for _, ci := range cis { + e.ciCache[ci.Key] = ci + } + return nil +} + +func (e *Endpoint) CollectResourceType(p *performance.Manager, ctx context.Context, alias string, acc telegraf.Accumulator, + objects map[string]types.ManagedObjectReference) error { + + for name, mor := range objects { + // Collect metrics + // + ams, err := p.AvailableMetric(ctx, mor, Interval) + if err != nil { + return err + } + pqs := types.PerfQuerySpec{ + Entity: mor, + MaxSample: 1, + MetricId: ams, + IntervalId: 20, + } + metrics, err := p.Query(ctx, []types.PerfQuerySpec{ pqs }) + if err != nil { + return err + } + fields := make(map[string]interface{}) + ems, err := p.ToMetricSeries(ctx, metrics) + if err != nil { + return err + } + + // Iterate through result and fields list + // + for _, em := range ems { + for _, v := range em.Value { + name := v.Name + if v.Instance != "" { + name += "." + v.Instance + } + fields[name] = v.Value[0] + } + } + tags := map[string]string { + "entityName": name, + "entityId": mor.Value} + acc.AddFields("vsphere." + alias, fields, tags) + } + return nil +} + +func (e *Endpoint) Collect(acc telegraf.Accumulator) error { + ctx := context.Background() + u, err := soap.ParseURL(e.Url) + if(err != nil) { + return err + } + c, err := govmomi.NewClient(ctx, u, true) + if(err != nil) { + return err + } + + defer c.Logout(ctx) + + m := view.NewManager(c.Client) + v, err := m.CreateContainerView(ctx, c.ServiceContent.RootFolder, []string{"VirtualMachine"}, true) + if err != nil { + return err + } + + defer v.Destroy(ctx) + + p := performance.NewManager(c.Client) + p.Destroy(ctx) + + // Load cache if needed + e.Init(p) + + vms, err := e.getVMs(ctx, v) + if err != nil { + return err + } + err = e.CollectResourceType(p, ctx, "vm", acc, vms) + if err != nil { + return err + } + hosts, err := e.getHosts(ctx, v) + if err != nil { + return err + } + err = e.CollectResourceType(p, ctx, "host", acc, hosts) + if err != nil { + return err + } + clusters, err := e.getClusters(ctx, v) + if err != nil { + return err + } + err = e.CollectResourceType(p, ctx, "cluster", acc, clusters) + if err != nil { + return err + } + datastores, err := e.getDatastores(ctx, v) + if err != nil { + return err + } + err = e.CollectResourceType(p, ctx, "datastore", acc, datastores) + if err != nil { + return err + } + return nil +} + +func (e *Endpoint) getVMs(ctx context.Context, root *view.ContainerView) (map[string]types.ManagedObjectReference, error) { + var resources []mo.VirtualMachine + err := root.Retrieve(ctx, []string{"VirtualMachine"}, []string{"summary"}, &resources) + if err != nil { + return nil, err + } + m := make(map[string]types.ManagedObjectReference) + for _, r := range resources { + m[r.Summary.Config.Name] = r.ExtensibleManagedObject.Reference() + } + return m, nil +} + +func (e *Endpoint) getHosts(ctx context.Context, root *view.ContainerView) (map[string]types.ManagedObjectReference, error) { + var resources []mo.HostSystem + err := root.Retrieve(ctx, []string{"HostSystem"}, []string{"summary"}, &resources) + if err != nil { + return nil, err + } + m := make(map[string]types.ManagedObjectReference) + for _, r := range resources { + m[r.Summary.Config.Name] = r.ExtensibleManagedObject.Reference() + } + return m, nil +} + +func (e *Endpoint) getClusters(ctx context.Context, root *view.ContainerView) (map[string]types.ManagedObjectReference, error) { + var resources []mo.ClusterComputeResource + err := root.Retrieve(ctx, []string{"ClusterComputeResource"}, []string{"summary"}, &resources) + if err != nil { + return nil, err + } + m := make(map[string]types.ManagedObjectReference) + for _, r := range resources { + m[r.Name] = r.ExtensibleManagedObject.Reference() + } + return m, nil +} + +func (e *Endpoint) getDatastores(ctx context.Context, root *view.ContainerView) (map[string]types.ManagedObjectReference, error) { + var resources []mo.Datastore + err := root.Retrieve(ctx, []string{"Datastore"}, []string{"summary"}, &resources) + if err != nil { + return nil, err + } + m := make(map[string]types.ManagedObjectReference) + for _, r := range resources { + m[r.Summary.Name] = r.ExtensibleManagedObject.Reference() + } + return m, nil +} + +var sampleConfig = ` + ## List of vCenter URLs, including credentials. Note the "@" characted must be escaped as %40 + # vcenters = [ "https://administrator%40vsphere.local:VMware1!@vcenter.local/sdk" ] +` + +func (v *VSphere) SampleConfig() string { + return sampleConfig +} + +func (v *VSphere) Description() string { + return "Read metrics from VMware vCenter" +} + +func (v *VSphere) Gather(acc telegraf.Accumulator) error { + for _, ep := range v.endpoints { + err := ep.Collect(acc) + if err != nil { + return err + } + } + return nil +} + +func init() { + inputs.Add("vsphere", func() telegraf.Input { + return &VSphere{ Vcenters: []string {} } + }) +} + + + diff --git a/plugins/inputs/zipkin/codec/codec.go b/plugins/inputs/zipkin/codec/codec.go index 781aad72aa73a..167b8ec24f1a3 100644 --- a/plugins/inputs/zipkin/codec/codec.go +++ b/plugins/inputs/zipkin/codec/codec.go @@ -4,7 +4,7 @@ import ( "time" "github.com/influxdata/telegraf/plugins/inputs/zipkin/trace" - "github.com/openzipkin/zipkin-go-opentracing/_thrift/gen-go/zipkincore" + "github.com/openzipkin/zipkin-go-opentracing/thrift/gen-go/zipkincore" ) //now is a mockable time for now diff --git a/plugins/inputs/zipkin/codec/jsonV1/jsonV1.go b/plugins/inputs/zipkin/codec/jsonV1/jsonV1.go index 8c229b9f315d5..1803486742301 100644 --- a/plugins/inputs/zipkin/codec/jsonV1/jsonV1.go +++ b/plugins/inputs/zipkin/codec/jsonV1/jsonV1.go @@ -7,7 +7,7 @@ import ( "time" "github.com/influxdata/telegraf/plugins/inputs/zipkin/codec" - "github.com/openzipkin/zipkin-go-opentracing/_thrift/gen-go/zipkincore" + "github.com/openzipkin/zipkin-go-opentracing/thrift/gen-go/zipkincore" ) // JSON decodes spans from bodies `POST`ed to the spans endpoint diff --git a/plugins/inputs/zipkin/codec/thrift/thrift.go b/plugins/inputs/zipkin/codec/thrift/thrift.go index b3fc94897b4b3..65a9e1488c2c4 100644 --- a/plugins/inputs/zipkin/codec/thrift/thrift.go +++ b/plugins/inputs/zipkin/codec/thrift/thrift.go @@ -10,7 +10,7 @@ import ( "github.com/influxdata/telegraf/plugins/inputs/zipkin/codec" "github.com/apache/thrift/lib/go/thrift" - "github.com/openzipkin/zipkin-go-opentracing/_thrift/gen-go/zipkincore" + "github.com/openzipkin/zipkin-go-opentracing/thrift/gen-go/zipkincore" ) // UnmarshalThrift converts raw bytes in thrift format to a slice of spans From c855efe8dc2587df753fd4d263fe2601a3d21546 Mon Sep 17 00:00:00 2001 From: "pontus@rydin@gmail.com" Date: Sat, 30 Sep 2017 16:31:42 -0400 Subject: [PATCH 036/100] First "working" version of vsphere input plugin --- plugins/inputs/vsphere/vsphere.go | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/plugins/inputs/vsphere/vsphere.go b/plugins/inputs/vsphere/vsphere.go index 0930f71fcbc5e..df3f3cbe07979 100644 --- a/plugins/inputs/vsphere/vsphere.go +++ b/plugins/inputs/vsphere/vsphere.go @@ -24,6 +24,7 @@ type Endpoint struct { type VSphere struct { Vcenters []string endpoints []Endpoint + mux sync.Mutex } func (e *Endpoint) Init(p *performance.Manager) error { @@ -217,7 +218,22 @@ func (v *VSphere) Description() string { return "Read metrics from VMware vCenter" } +func (v *VSphere) Init() { + v.mux.Lock() + defer v.mux.Unlock() + + + if v.endpoints != nil { + return + } + v.endpoints = make([]Endpoint, len(v.Vcenters)) + for i, u := range v.Vcenters { + v.endpoints[i] = Endpoint{ Url: u } + } +} + func (v *VSphere) Gather(acc telegraf.Accumulator) error { + v.Init() for _, ep := range v.endpoints { err := ep.Collect(acc) if err != nil { From f2a8535a7f9779df78261cebf8c0e591613d79b1 Mon Sep 17 00:00:00 2001 From: "pontus@rydin@gmail.com" Date: Sun, 1 Oct 2017 15:10:33 -0400 Subject: [PATCH 037/100] Optimizations of vSphere plugin. Fixed bug with missing newlines in Wavefront plugin. --- plugins/inputs/vsphere/vsphere.go | 151 +++++++------ plugins/outputs/wavefront/wavefront.go | 292 ------------------------- 2 files changed, 87 insertions(+), 356 deletions(-) delete mode 100644 plugins/outputs/wavefront/wavefront.go diff --git a/plugins/inputs/vsphere/vsphere.go b/plugins/inputs/vsphere/vsphere.go index df3f3cbe07979..8b8c16e6a359c 100644 --- a/plugins/inputs/vsphere/vsphere.go +++ b/plugins/inputs/vsphere/vsphere.go @@ -11,83 +11,113 @@ import ( "github.com/vmware/govmomi/vim25/types" "sync" "github.com/influxdata/telegraf/plugins/inputs" + "time" + "fmt" ) -const Interval = 20 - type Endpoint struct { + Parent *VSphere Url string - ciCache map[int32]types.PerfCounterInfo + intervals []int32 mux sync.Mutex } type VSphere struct { Vcenters []string + VmInterval int32 + HostInterval int32 + ClusterInterval int32 + DatastoreInterval int32 endpoints []Endpoint mux sync.Mutex } +type ResourceGetter func (context.Context, *view.ContainerView) (map[string]types.ManagedObjectReference, error) + +type InstanceMetrics map[string]map[string]interface{} + func (e *Endpoint) Init(p *performance.Manager) error { e.mux.Lock() defer e.mux.Unlock() - if e.ciCache != nil { - return nil - } - ctx := context.Background() - defer p.Destroy(ctx) - e.ciCache = make(map[int32]types.PerfCounterInfo) - cis, err := p.CounterInfo(ctx) - if err != nil { - return err - } - for _, ci := range cis { - e.ciCache[ci.Key] = ci + if e.intervals == nil { + // Load interval table + // + ctx := context.Background() + list, err := p.HistoricalInterval(ctx) + if err != nil { + return err + } + e.intervals = make([]int32, len(list)) + for k, i := range list { + e.intervals[k] = i.SamplingPeriod + } } return nil } func (e *Endpoint) CollectResourceType(p *performance.Manager, ctx context.Context, alias string, acc telegraf.Accumulator, - objects map[string]types.ManagedObjectReference) error { + getter ResourceGetter, root *view.ContainerView, interval int32) error { + start := time.Now() + objects, err := getter(ctx, root) + if err != nil { + return err + } + pqs := make([]types.PerfQuerySpec, len(objects)) + nameLookup := make(map[string]string) + idx := 0 for name, mor := range objects { + nameLookup[mor.Reference().Value] = name; // Collect metrics // - ams, err := p.AvailableMetric(ctx, mor, Interval) + ams, err := p.AvailableMetric(ctx, mor, interval) if err != nil { return err } - pqs := types.PerfQuerySpec{ + pqs[idx] = types.PerfQuerySpec{ Entity: mor, MaxSample: 1, MetricId: ams, - IntervalId: 20, - } - metrics, err := p.Query(ctx, []types.PerfQuerySpec{ pqs }) - if err != nil { - return err - } - fields := make(map[string]interface{}) - ems, err := p.ToMetricSeries(ctx, metrics) - if err != nil { - return err + IntervalId: interval, } + idx++ + } - // Iterate through result and fields list - // - for _, em := range ems { - for _, v := range em.Value { - name := v.Name - if v.Instance != "" { - name += "." + v.Instance - } - fields[name] = v.Value[0] + metrics, err := p.Query(ctx, pqs ) + if err != nil { + return err + } + + ems, err := p.ToMetricSeries(ctx, metrics) + if err != nil { + return err + } + + // Iterate through result and fields list + // + for _, em := range ems { + im := make(InstanceMetrics) + for _, v := range em.Value { + name := v.Name + m, found := im[v.Instance] + if !found { + m = make(map[string]interface{}) + im[v.Instance] = m } + m[name] = v.Value[0] + } + for k, m := range im { + moid := em.Entity.Reference().Value + tags := map[string]string{ + "source": nameLookup[moid], + "moid": moid} + if k != "" { + tags["instance"] = k + } + acc.AddFields("vsphere." + alias, m, tags) } - tags := map[string]string { - "entityName": name, - "entityId": mor.Value} - acc.AddFields("vsphere." + alias, fields, tags) } + fmt.Println(time.Now().Sub(start)) return nil } @@ -105,7 +135,7 @@ func (e *Endpoint) Collect(acc telegraf.Accumulator) error { defer c.Logout(ctx) m := view.NewManager(c.Client) - v, err := m.CreateContainerView(ctx, c.ServiceContent.RootFolder, []string{"VirtualMachine"}, true) + v, err := m.CreateContainerView(ctx, c.ServiceContent.RootFolder, []string{ }, true) if err != nil { return err } @@ -118,35 +148,22 @@ func (e *Endpoint) Collect(acc telegraf.Accumulator) error { // Load cache if needed e.Init(p) - vms, err := e.getVMs(ctx, v) - if err != nil { - return err - } - err = e.CollectResourceType(p, ctx, "vm", acc, vms) + err = e.CollectResourceType(p, ctx, "vm", acc, e.getVMs, v, e.Parent.VmInterval) if err != nil { return err } - hosts, err := e.getHosts(ctx, v) - if err != nil { - return err - } - err = e.CollectResourceType(p, ctx, "host", acc, hosts) - if err != nil { - return err - } - clusters, err := e.getClusters(ctx, v) - if err != nil { - return err - } - err = e.CollectResourceType(p, ctx, "cluster", acc, clusters) + + err = e.CollectResourceType(p, ctx, "host", acc, e.getHosts, v, e.Parent.HostInterval) if err != nil { return err } - datastores, err := e.getDatastores(ctx, v) + + err = e.CollectResourceType(p, ctx, "cluster", acc, e.getClusters, v, e.Parent.ClusterInterval) if err != nil { return err } - err = e.CollectResourceType(p, ctx, "datastore", acc, datastores) + + err = e.CollectResourceType(p, ctx, "datastore", acc, e.getDatastores, v, e.Parent.DatastoreInterval) if err != nil { return err } @@ -228,7 +245,7 @@ func (v *VSphere) Init() { } v.endpoints = make([]Endpoint, len(v.Vcenters)) for i, u := range v.Vcenters { - v.endpoints[i] = Endpoint{ Url: u } + v.endpoints[i] = Endpoint{ Url: u, Parent: v } } } @@ -245,7 +262,13 @@ func (v *VSphere) Gather(acc telegraf.Accumulator) error { func init() { inputs.Add("vsphere", func() telegraf.Input { - return &VSphere{ Vcenters: []string {} } + return &VSphere{ + Vcenters: []string {}, + VmInterval: 20, + HostInterval: 20, + ClusterInterval: 300, + DatastoreInterval: 300, + } }) } diff --git a/plugins/outputs/wavefront/wavefront.go b/plugins/outputs/wavefront/wavefront.go deleted file mode 100644 index 1e015c9f0a60a..0000000000000 --- a/plugins/outputs/wavefront/wavefront.go +++ /dev/null @@ -1,292 +0,0 @@ -package wavefront - -import ( - "bytes" - "fmt" - "log" - "net" - "regexp" - "strconv" - "strings" - - "time" - - "github.com/influxdata/telegraf" - "github.com/influxdata/telegraf/plugins/outputs" -) - -type Wavefront struct { - Prefix string - Host string - Port int - SimpleFields bool - MetricSeparator string - ConvertPaths bool - ConvertBool bool - UseRegex bool - SourceOverride []string - StringToNumber map[string][]map[string]float64 -} - -// catch many of the invalid chars that could appear in a metric or tag name -var sanitizedChars = strings.NewReplacer( - "!", "-", "@", "-", "#", "-", "$", "-", "%", "-", "^", "-", "&", "-", - "*", "-", "(", "-", ")", "-", "+", "-", "`", "-", "'", "-", "\"", "-", - "[", "-", "]", "-", "{", "-", "}", "-", ":", "-", ";", "-", "<", "-", - ">", "-", ",", "-", "?", "-", "/", "-", "\\", "-", "|", "-", " ", "-", - "=", "-", -) - -// instead of Replacer which may miss some special characters we can use a regex pattern, but this is significantly slower than Replacer -var sanitizedRegex = regexp.MustCompile("[^a-zA-Z\\d_.-]") - -var tagValueReplacer = strings.NewReplacer("\"", "\\\"", "*", "-") - -var pathReplacer = strings.NewReplacer("_", "_") - -var sampleConfig = ` - ## DNS name of the wavefront proxy server - host = "wavefront.example.com" - - ## Port that the Wavefront proxy server listens on - port = 2878 - - ## prefix for metrics keys - #prefix = "my.specific.prefix." - - ## whether to use "value" for name of simple fields - #simple_fields = false - - ## character to use between metric and field name. defaults to . (dot) - #metric_separator = "." - - ## Convert metric name paths to use metricSeperator character - ## When true (default) will convert all _ (underscore) chartacters in final metric name - #convert_paths = true - - ## Use Regex to sanitize metric and tag names from invalid characters - ## Regex is more thorough, but significantly slower - #use_regex = false - - ## point tags to use as the source name for Wavefront (if none found, host will be used) - #source_override = ["hostname", "agent_host", "node_host"] - - ## whether to convert boolean values to numeric values, with false -> 0.0 and true -> 1.0. default true - #convert_bool = true - - ## Define a mapping, namespaced by metric prefix, from string values to numeric values - ## The example below maps "green" -> 1.0, "yellow" -> 0.5, "red" -> 0.0 for - ## any metrics beginning with "elasticsearch" - #[[outputs.wavefront.string_to_number.elasticsearch]] - # green = 1.0 - # yellow = 0.5 - # red = 0.0 -` - -type MetricPoint struct { - Metric string - Value float64 - Timestamp int64 - Source string - Tags map[string]string -} - -func (w *Wavefront) Connect() error { - if w.ConvertPaths && w.MetricSeparator == "_" { - w.ConvertPaths = false - } - if w.ConvertPaths { - pathReplacer = strings.NewReplacer("_", w.MetricSeparator) - } - - // Test Connection to Wavefront proxy Server - uri := fmt.Sprintf("%s:%d", w.Host, w.Port) - _, err := net.ResolveTCPAddr("tcp", uri) - if err != nil { - return fmt.Errorf("Wavefront: TCP address cannot be resolved %s", err.Error()) - } - connection, err := net.Dial("tcp", uri) - if err != nil { - return fmt.Errorf("Wavefront: TCP connect fail %s", err.Error()) - } - defer connection.Close() - return nil -} - -func (w *Wavefront) Write(metrics []telegraf.Metric) error { - - // Send Data to Wavefront proxy Server - uri := fmt.Sprintf("%s:%d", w.Host, w.Port) - connection, err := net.Dial("tcp", uri) - if err != nil { - return fmt.Errorf("Wavefront: TCP connect fail %s", err.Error()) - } - defer connection.Close() - connection.SetWriteDeadline(time.Now().Add(5 * time.Second)) - - for _, m := range metrics { - for _, metricPoint := range buildMetrics(m, w) { - metricLine := formatMetricPoint(metricPoint, w) - _, err := connection.Write([]byte(metricLine)) - if err != nil { - return fmt.Errorf("Wavefront: TCP writing error %s", err.Error()) - } - } - } - - return nil -} - -func buildMetrics(m telegraf.Metric, w *Wavefront) []*MetricPoint { - ret := []*MetricPoint{} - - for fieldName, value := range m.Fields() { - var name string - if !w.SimpleFields && fieldName == "value" { - name = fmt.Sprintf("%s%s", w.Prefix, m.Name()) - } else { - name = fmt.Sprintf("%s%s%s%s", w.Prefix, m.Name(), w.MetricSeparator, fieldName) - } - - if w.UseRegex { - name = sanitizedRegex.ReplaceAllLiteralString(name, "-") - } else { - name = sanitizedChars.Replace(name) - } - - if w.ConvertPaths { - name = pathReplacer.Replace(name) - } - - metric := &MetricPoint{ - Metric: name, - Timestamp: m.Time().Unix(), - } - - metricValue, buildError := buildValue(value, metric.Metric, w) - if buildError != nil { - log.Printf("D! Output [wavefront] %s\n", buildError.Error()) - continue - } - metric.Value = metricValue - - source, tags := buildTags(m.Tags(), w) - metric.Source = source - metric.Tags = tags - - ret = append(ret, metric) - } - return ret -} - -func buildTags(mTags map[string]string, w *Wavefront) (string, map[string]string) { - var source string - sourceTagFound := false - - for _, s := range w.SourceOverride { - for k, v := range mTags { - if k == s { - source = v - mTags["telegraf_host"] = mTags["host"] - sourceTagFound = true - delete(mTags, k) - break - } - } - if sourceTagFound { - break - } - } - - if !sourceTagFound { - source = mTags["host"] - } - delete(mTags, "host") - - return tagValueReplacer.Replace(source), mTags -} - -func buildValue(v interface{}, name string, w *Wavefront) (float64, error) { - switch p := v.(type) { - case bool: - if w.ConvertBool { - if p { - return 1, nil - } else { - return 0, nil - } - } - case int64: - return float64(v.(int64)), nil - case uint64: - return float64(v.(uint64)), nil - case float64: - return v.(float64), nil - case string: - for prefix, mappings := range w.StringToNumber { - if strings.HasPrefix(name, prefix) { - for _, mapping := range mappings { - val, hasVal := mapping[string(p)] - if hasVal { - return val, nil - } - } - } - } - return 0, fmt.Errorf("unexpected type: %T, with value: %v, for: %s", v, v, name) - default: - return 0, fmt.Errorf("unexpected type: %T, with value: %v, for: %s", v, v, name) - } - - return 0, fmt.Errorf("unexpected type: %T, with value: %v, for: %s", v, v, name) -} - -func formatMetricPoint(metricPoint *MetricPoint, w *Wavefront) string { - buffer := bytes.NewBufferString("") - buffer.WriteString(metricPoint.Metric) - buffer.WriteString(" ") - buffer.WriteString(strconv.FormatFloat(metricPoint.Value, 'f', 6, 64)) - buffer.WriteString(" ") - buffer.WriteString(strconv.FormatInt(metricPoint.Timestamp, 10)) - buffer.WriteString(" source=\"") - buffer.WriteString(metricPoint.Source) - buffer.WriteString("\"") - - for k, v := range metricPoint.Tags { - buffer.WriteString(" ") - if w.UseRegex { - buffer.WriteString(sanitizedRegex.ReplaceAllLiteralString(k, "-")) - } else { - buffer.WriteString(sanitizedChars.Replace(k)) - } - buffer.WriteString("=\"") - buffer.WriteString(tagValueReplacer.Replace(v)) - buffer.WriteString("\"") - } - - buffer.WriteString("\n") - - return buffer.String() -} - -func (w *Wavefront) SampleConfig() string { - return sampleConfig -} - -func (w *Wavefront) Description() string { - return "Configuration for Wavefront server to send metrics to" -} - -func (w *Wavefront) Close() error { - return nil -} - -func init() { - outputs.Add("wavefront", func() telegraf.Output { - return &Wavefront{ - MetricSeparator: ".", - ConvertPaths: true, - ConvertBool: true, - } - }) -} From 31bbcb2d04b2071982f908c50f98ba3d0148c6ee Mon Sep 17 00:00:00 2001 From: "pontus@rydin@gmail.com" Date: Tue, 3 Oct 2017 09:25:52 -0700 Subject: [PATCH 038/100] Performance improvements --- plugins/inputs/vsphere/vsphere.go | 150 ++++++++++++++++++------------ 1 file changed, 92 insertions(+), 58 deletions(-) diff --git a/plugins/inputs/vsphere/vsphere.go b/plugins/inputs/vsphere/vsphere.go index 8b8c16e6a359c..a6335679fa711 100644 --- a/plugins/inputs/vsphere/vsphere.go +++ b/plugins/inputs/vsphere/vsphere.go @@ -9,17 +9,16 @@ import ( "context" "github.com/vmware/govmomi/performance" "github.com/vmware/govmomi/vim25/types" - "sync" "github.com/influxdata/telegraf/plugins/inputs" "time" - "fmt" + "github.com/Sirupsen/logrus" ) type Endpoint struct { Parent *VSphere Url string intervals []int32 - mux sync.Mutex + lastColl map[string]time.Time } type VSphere struct { @@ -28,17 +27,16 @@ type VSphere struct { HostInterval int32 ClusterInterval int32 DatastoreInterval int32 + MaxSamples int32 + MaxQuery int32 endpoints []Endpoint - mux sync.Mutex } type ResourceGetter func (context.Context, *view.ContainerView) (map[string]types.ManagedObjectReference, error) type InstanceMetrics map[string]map[string]interface{} -func (e *Endpoint) Init(p *performance.Manager) error { - e.mux.Lock() - defer e.mux.Unlock() +func (e *Endpoint) init(p *performance.Manager) error { if e.intervals == nil { // Load interval table // @@ -55,73 +53,101 @@ func (e *Endpoint) Init(p *performance.Manager) error { return nil } -func (e *Endpoint) CollectResourceType(p *performance.Manager, ctx context.Context, alias string, acc telegraf.Accumulator, +func (e *Endpoint) collectResourceType(p *performance.Manager, ctx context.Context, alias string, acc telegraf.Accumulator, getter ResourceGetter, root *view.ContainerView, interval int32) error { + // Do we have new data yet? + // + nIntervals := int32(1) + latest, hasLatest := e.lastColl[alias] + if(hasLatest) { + elapsed := time.Now().Sub(latest).Seconds() + if elapsed < float64(interval) { + // No new data would be available. We're outta here! + // + return nil; + } + nIntervals := int32(elapsed / (float64(interval))) + if nIntervals > e.Parent.MaxSamples { + nIntervals = e.Parent.MaxSamples + } + } + log.Printf("Collecting %d intervals for %s", nIntervals, alias) + + fullAlias := "vsphere." + alias + start := time.Now() objects, err := getter(ctx, root) if err != nil { return err } - pqs := make([]types.PerfQuerySpec, len(objects)) + pqs := make([]types.PerfQuerySpec, 0, e.Parent.MaxQuery) nameLookup := make(map[string]string) - idx := 0 + total := 0; for name, mor := range objects { nameLookup[mor.Reference().Value] = name; + // Collect metrics // ams, err := p.AvailableMetric(ctx, mor, interval) if err != nil { return err } - pqs[idx] = types.PerfQuerySpec{ + + pq := types.PerfQuerySpec{ Entity: mor, - MaxSample: 1, + MaxSample: nIntervals, MetricId: ams, IntervalId: interval, } - idx++ - } - - metrics, err := p.Query(ctx, pqs ) - if err != nil { - return err - } + if(e.Parent.MaxSamples > 1 && hasLatest) { + pq.StartTime = &start + } + pqs = append(pqs, pq) + total++ - ems, err := p.ToMetricSeries(ctx, metrics) - if err != nil { - return err - } + // Filled up a chunk or at end of data? Run a query with the collected objects + // + if len(pqs) >= int(e.Parent.MaxQuery) || total == len(objects) { + log.Printf("Querying %d objects of type %s. Total processed: %d. Total objects %d\n", len(pqs), alias, total, len(objects)) + metrics, err := p.Query(ctx, pqs) + if err != nil { + return err + } - // Iterate through result and fields list - // - for _, em := range ems { - im := make(InstanceMetrics) - for _, v := range em.Value { - name := v.Name - m, found := im[v.Instance] - if !found { - m = make(map[string]interface{}) - im[v.Instance] = m + ems, err := p.ToMetricSeries(ctx, metrics) + if err != nil { + return err } - m[name] = v.Value[0] - } - for k, m := range im { - moid := em.Entity.Reference().Value - tags := map[string]string{ - "source": nameLookup[moid], - "moid": moid} - if k != "" { - tags["instance"] = k + + // Iterate through result and fields list + // + for _, em := range ems { + moid := em.Entity.Reference().Value + for _, v := range em.Value { + name := v.Name + for idx, value := range v.Value { + f := map[string]interface{} { name: value } + tags := map[string]string{ + "source": nameLookup[moid], + "moid": moid} + if v.Instance != "" { + tags["instance"] = v.Instance + } + acc.AddFields(fullAlias, f, tags, em.SampleInfo[idx].Timestamp) + } + } } - acc.AddFields("vsphere." + alias, m, tags) + pqs = make([]types.PerfQuerySpec, 0, e.Parent.MaxQuery) } } - fmt.Println(time.Now().Sub(start)) + + + log.Printf("Collection of %s took %v\n", alias, time.Now().Sub(start)) return nil } -func (e *Endpoint) Collect(acc telegraf.Accumulator) error { +func (e *Endpoint) collect(acc telegraf.Accumulator) error { ctx := context.Background() u, err := soap.ParseURL(e.Url) if(err != nil) { @@ -146,24 +172,24 @@ func (e *Endpoint) Collect(acc telegraf.Accumulator) error { p.Destroy(ctx) // Load cache if needed - e.Init(p) + e.init(p) - err = e.CollectResourceType(p, ctx, "vm", acc, e.getVMs, v, e.Parent.VmInterval) + err = e.collectResourceType(p, ctx, "vm", acc, e.getVMs, v, e.Parent.VmInterval) if err != nil { return err } - err = e.CollectResourceType(p, ctx, "host", acc, e.getHosts, v, e.Parent.HostInterval) + err = e.collectResourceType(p, ctx, "host", acc, e.getHosts, v, e.Parent.HostInterval) if err != nil { return err } - err = e.CollectResourceType(p, ctx, "cluster", acc, e.getClusters, v, e.Parent.ClusterInterval) + err = e.collectResourceType(p, ctx, "cluster", acc, e.getClusters, v, e.Parent.ClusterInterval) if err != nil { return err } - err = e.CollectResourceType(p, ctx, "datastore", acc, e.getDatastores, v, e.Parent.DatastoreInterval) + err = e.collectResourceType(p, ctx, "datastore", acc, e.getDatastores, v, e.Parent.DatastoreInterval) if err != nil { return err } @@ -236,28 +262,34 @@ func (v *VSphere) Description() string { } func (v *VSphere) Init() { - v.mux.Lock() - defer v.mux.Unlock() - - if v.endpoints != nil { return } v.endpoints = make([]Endpoint, len(v.Vcenters)) for i, u := range v.Vcenters { - v.endpoints[i] = Endpoint{ Url: u, Parent: v } + v.endpoints[i] = Endpoint{ + Url: u, + Parent: v, + lastColl: make(map[string]time.Time)} } } func (v *VSphere) Gather(acc telegraf.Accumulator) error { v.Init() + results := make(chan error) for _, ep := range v.endpoints { - err := ep.Collect(acc) + go func() { + results <- ep.collect(acc) + }() + } + var finalErr error = nil + for range v.endpoints { + err := <- results if err != nil { - return err + finalErr = err } } - return nil + return finalErr } func init() { @@ -268,6 +300,8 @@ func init() { HostInterval: 20, ClusterInterval: 300, DatastoreInterval: 300, + MaxSamples: 10, + MaxQuery: 64, } }) } From 43c270274dadd95adda7d729c02a025cdb1597e7 Mon Sep 17 00:00:00 2001 From: "pontus@rydin@gmail.com" Date: Tue, 3 Oct 2017 09:27:17 -0700 Subject: [PATCH 039/100] Use logrus instead of built-in log --- plugins/inputs/vsphere/vsphere.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/inputs/vsphere/vsphere.go b/plugins/inputs/vsphere/vsphere.go index a6335679fa711..c0d20dbf4546a 100644 --- a/plugins/inputs/vsphere/vsphere.go +++ b/plugins/inputs/vsphere/vsphere.go @@ -72,7 +72,7 @@ func (e *Endpoint) collectResourceType(p *performance.Manager, ctx context.Conte nIntervals = e.Parent.MaxSamples } } - log.Printf("Collecting %d intervals for %s", nIntervals, alias) + logrus.Debugf("Collecting %d intervals for %s", nIntervals, alias) fullAlias := "vsphere." + alias @@ -109,7 +109,7 @@ func (e *Endpoint) collectResourceType(p *performance.Manager, ctx context.Conte // Filled up a chunk or at end of data? Run a query with the collected objects // if len(pqs) >= int(e.Parent.MaxQuery) || total == len(objects) { - log.Printf("Querying %d objects of type %s. Total processed: %d. Total objects %d\n", len(pqs), alias, total, len(objects)) + logrus.Debugf("Querying %d objects of type %s. Total processed: %d. Total objects %d\n", len(pqs), alias, total, len(objects)) metrics, err := p.Query(ctx, pqs) if err != nil { return err @@ -143,7 +143,7 @@ func (e *Endpoint) collectResourceType(p *performance.Manager, ctx context.Conte } - log.Printf("Collection of %s took %v\n", alias, time.Now().Sub(start)) + logrus.Debugf("Collection of %s took %v\n", alias, time.Now().Sub(start)) return nil } From fcb5b442e489a06b74ecb3f72d2b770ade61d1a3 Mon Sep 17 00:00:00 2001 From: "pontus@rydin@gmail.com" Date: Fri, 13 Oct 2017 16:34:59 -0400 Subject: [PATCH 040/100] Performance improvments. --- plugins/inputs/vsphere/vsphere.go | 54 ++++++++++++++++++------------- 1 file changed, 31 insertions(+), 23 deletions(-) diff --git a/plugins/inputs/vsphere/vsphere.go b/plugins/inputs/vsphere/vsphere.go index c0d20dbf4546a..9fe47d750ac85 100644 --- a/plugins/inputs/vsphere/vsphere.go +++ b/plugins/inputs/vsphere/vsphere.go @@ -11,12 +11,13 @@ import ( "github.com/vmware/govmomi/vim25/types" "github.com/influxdata/telegraf/plugins/inputs" "time" - "github.com/Sirupsen/logrus" + "log" + "net/url" ) type Endpoint struct { Parent *VSphere - Url string + Url *url.URL intervals []int32 lastColl map[string]time.Time } @@ -56,6 +57,12 @@ func (e *Endpoint) init(p *performance.Manager) error { func (e *Endpoint) collectResourceType(p *performance.Manager, ctx context.Context, alias string, acc telegraf.Accumulator, getter ResourceGetter, root *view.ContainerView, interval int32) error { + // Interval = -1 means collection for this metric was diabled, so don't even bother. + // + if interval == -1 { + return nil + } + // Do we have new data yet? // nIntervals := int32(1) @@ -72,7 +79,12 @@ func (e *Endpoint) collectResourceType(p *performance.Manager, ctx context.Conte nIntervals = e.Parent.MaxSamples } } - logrus.Debugf("Collecting %d intervals for %s", nIntervals, alias) + // log.Printf("URL: %s" + url.U e.Url) + // Get all possible metric ids + // + + + log.Printf("D! Collecting %d intervals for %s", nIntervals, alias) fullAlias := "vsphere." + alias @@ -81,23 +93,17 @@ func (e *Endpoint) collectResourceType(p *performance.Manager, ctx context.Conte if err != nil { return err } + log.Printf("D! Query for %s returned %d objects", alias, len(objects)) pqs := make([]types.PerfQuerySpec, 0, e.Parent.MaxQuery) nameLookup := make(map[string]string) total := 0; for name, mor := range objects { - nameLookup[mor.Reference().Value] = name; - - // Collect metrics - // - ams, err := p.AvailableMetric(ctx, mor, interval) - if err != nil { - return err - } + nameLookup[mor.Reference().Value] = name pq := types.PerfQuerySpec{ Entity: mor, MaxSample: nIntervals, - MetricId: ams, + MetricId: nil, IntervalId: interval, } if(e.Parent.MaxSamples > 1 && hasLatest) { @@ -109,7 +115,7 @@ func (e *Endpoint) collectResourceType(p *performance.Manager, ctx context.Conte // Filled up a chunk or at end of data? Run a query with the collected objects // if len(pqs) >= int(e.Parent.MaxQuery) || total == len(objects) { - logrus.Debugf("Querying %d objects of type %s. Total processed: %d. Total objects %d\n", len(pqs), alias, total, len(objects)) + log.Printf("D! Querying %d objects of type %s for %s. Total processed: %d. Total objects %d\n", len(pqs), alias, e.Url.Host, total, len(objects)) metrics, err := p.Query(ctx, pqs) if err != nil { return err @@ -129,6 +135,7 @@ func (e *Endpoint) collectResourceType(p *performance.Manager, ctx context.Conte for idx, value := range v.Value { f := map[string]interface{} { name: value } tags := map[string]string{ + "vcenter": e.Url.Host, "source": nameLookup[moid], "moid": moid} if v.Instance != "" { @@ -143,17 +150,13 @@ func (e *Endpoint) collectResourceType(p *performance.Manager, ctx context.Conte } - logrus.Debugf("Collection of %s took %v\n", alias, time.Now().Sub(start)) + log.Printf("D! Collection of %s took %v\n", alias, time.Now().Sub(start)) return nil } func (e *Endpoint) collect(acc telegraf.Accumulator) error { ctx := context.Background() - u, err := soap.ParseURL(e.Url) - if(err != nil) { - return err - } - c, err := govmomi.NewClient(ctx, u, true) + c, err := govmomi.NewClient(ctx, e.Url, true) if(err != nil) { return err } @@ -266,7 +269,11 @@ func (v *VSphere) Init() { return } v.endpoints = make([]Endpoint, len(v.Vcenters)) - for i, u := range v.Vcenters { + for i, rawUrl := range v.Vcenters { + u, err := soap.ParseURL(rawUrl); + if(err != nil) { + log.Printf("E! Can't parse URL %s\n", rawUrl) + } v.endpoints[i] = Endpoint{ Url: u, Parent: v, @@ -278,14 +285,15 @@ func (v *VSphere) Gather(acc telegraf.Accumulator) error { v.Init() results := make(chan error) for _, ep := range v.endpoints { - go func() { - results <- ep.collect(acc) - }() + go func(target Endpoint) { + results <- target.collect(acc) + }(ep) } var finalErr error = nil for range v.endpoints { err := <- results if err != nil { + log.Println("E!", err) finalErr = err } } From c1544b306bd39571051cf8ab8a8a3ca5a782de8e Mon Sep 17 00:00:00 2001 From: "pontus@rydin@gmail.com" Date: Tue, 17 Oct 2017 09:48:09 -0700 Subject: [PATCH 041/100] Fixed premature destroy of performance manager --- plugins/inputs/vsphere/vsphere.go | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/plugins/inputs/vsphere/vsphere.go b/plugins/inputs/vsphere/vsphere.go index 9fe47d750ac85..c548f27a6a6c8 100644 --- a/plugins/inputs/vsphere/vsphere.go +++ b/plugins/inputs/vsphere/vsphere.go @@ -79,10 +79,6 @@ func (e *Endpoint) collectResourceType(p *performance.Manager, ctx context.Conte nIntervals = e.Parent.MaxSamples } } - // log.Printf("URL: %s" + url.U e.Url) - // Get all possible metric ids - // - log.Printf("D! Collecting %d intervals for %s", nIntervals, alias) @@ -172,7 +168,7 @@ func (e *Endpoint) collect(acc telegraf.Accumulator) error { defer v.Destroy(ctx) p := performance.NewManager(c.Client) - p.Destroy(ctx) + defer p.Destroy(ctx) // Load cache if needed e.init(p) From bb75063158504346dde7d35fe0f513f02aafa2f3 Mon Sep 17 00:00:00 2001 From: "pontus@rydin@gmail.com" Date: Wed, 18 Oct 2017 19:15:06 -0400 Subject: [PATCH 042/100] Removed call to PerformanceManager.Destroy since it floods the vCenter console with error messages. --- plugins/inputs/vsphere/vsphere.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/plugins/inputs/vsphere/vsphere.go b/plugins/inputs/vsphere/vsphere.go index c548f27a6a6c8..df53c15f0e783 100644 --- a/plugins/inputs/vsphere/vsphere.go +++ b/plugins/inputs/vsphere/vsphere.go @@ -168,7 +168,12 @@ func (e *Endpoint) collect(acc telegraf.Accumulator) error { defer v.Destroy(ctx) p := performance.NewManager(c.Client) - defer p.Destroy(ctx) + + // This causes strange error messages in the vCenter console. Possibly due to a bug in + // govmomi. We're commenting it out for now. Should be benign since the logout should + // destroy all resources anyway. + // + //defer p.Destroy(ctx) // Load cache if needed e.init(p) From 3d66d868ed3c0dc392a8e94acdac8551da3e99cd Mon Sep 17 00:00:00 2001 From: "pontus@rydin@gmail.com" Date: Sun, 22 Oct 2017 13:14:55 -0400 Subject: [PATCH 043/100] Include name of parent in output. --- plugins/inputs/vsphere/vsphere.go | 123 ++++++++++++++++++++---------- 1 file changed, 81 insertions(+), 42 deletions(-) diff --git a/plugins/inputs/vsphere/vsphere.go b/plugins/inputs/vsphere/vsphere.go index df53c15f0e783..99e60d5495f68 100644 --- a/plugins/inputs/vsphere/vsphere.go +++ b/plugins/inputs/vsphere/vsphere.go @@ -33,7 +33,13 @@ type VSphere struct { endpoints []Endpoint } -type ResourceGetter func (context.Context, *view.ContainerView) (map[string]types.ManagedObjectReference, error) +type objectRef struct { + name string + ref types.ManagedObjectReference + parentRef *types.ManagedObjectReference //Pointer because it must be nillable +} + +type objectMap map[string]objectRef type InstanceMetrics map[string]map[string]interface{} @@ -55,7 +61,7 @@ func (e *Endpoint) init(p *performance.Manager) error { } func (e *Endpoint) collectResourceType(p *performance.Manager, ctx context.Context, alias string, acc telegraf.Accumulator, - getter ResourceGetter, root *view.ContainerView, interval int32) error { + objects objectMap, nameCache map[string]string, interval int32) error { // Interval = -1 means collection for this metric was diabled, so don't even bother. // @@ -65,11 +71,12 @@ func (e *Endpoint) collectResourceType(p *performance.Manager, ctx context.Conte // Do we have new data yet? // + now := time.Now() nIntervals := int32(1) latest, hasLatest := e.lastColl[alias] - if(hasLatest) { + if (hasLatest) { elapsed := time.Now().Sub(latest).Seconds() - if elapsed < float64(interval) { + if elapsed < float64(interval) { // No new data would be available. We're outta here! // return nil; @@ -79,31 +86,29 @@ func (e *Endpoint) collectResourceType(p *performance.Manager, ctx context.Conte nIntervals = e.Parent.MaxSamples } } - + e.lastColl[alias] = now log.Printf("D! Collecting %d intervals for %s", nIntervals, alias) - fullAlias := "vsphere." + alias start := time.Now() - objects, err := getter(ctx, root) - if err != nil { - return err - } log.Printf("D! Query for %s returned %d objects", alias, len(objects)) pqs := make([]types.PerfQuerySpec, 0, e.Parent.MaxQuery) - nameLookup := make(map[string]string) total := 0; - for name, mor := range objects { - nameLookup[mor.Reference().Value] = name - + for _, object := range objects { pq := types.PerfQuerySpec{ - Entity: mor, + Entity: object.ref, MaxSample: nIntervals, MetricId: nil, IntervalId: interval, } + if(interval > 20) { + startTime := now.Add(-time.Duration(interval) * time.Second) + pq.StartTime = &startTime + pq.EndTime = &now + } if(e.Parent.MaxSamples > 1 && hasLatest) { - pq.StartTime = &start + pq.StartTime = &latest + pq.EndTime = &now } pqs = append(pqs, pq) total++ @@ -130,14 +135,23 @@ func (e *Endpoint) collectResourceType(p *performance.Manager, ctx context.Conte name := v.Name for idx, value := range v.Value { f := map[string]interface{} { name: value } - tags := map[string]string{ + objectName := nameCache[moid] + parent := "" + parentRef := objects[moid].parentRef + //log.Printf("Parentref=%s", parentRef) + if parentRef != nil { + parent = nameCache[parentRef.Value] + } + + t := map[string]string{ "vcenter": e.Url.Host, - "source": nameLookup[moid], - "moid": moid} + "source": objectName, + "moid": moid, + "parent": parent} if v.Instance != "" { - tags["instance"] = v.Instance + t["instance"] = v.Instance } - acc.AddFields(fullAlias, f, tags, em.SampleInfo[idx].Timestamp) + acc.AddFields(fullAlias, f, t, em.SampleInfo[idx].Timestamp) } } } @@ -178,76 +192,100 @@ func (e *Endpoint) collect(acc telegraf.Accumulator) error { // Load cache if needed e.init(p) - err = e.collectResourceType(p, ctx, "vm", acc, e.getVMs, v, e.Parent.VmInterval) + nameCache := make(map[string]string) + + // Collect cluster metrics + // + clusterMap, err := e.getClusters(ctx, v); if err != nil { return err } - - err = e.collectResourceType(p, ctx, "host", acc, e.getHosts, v, e.Parent.HostInterval) + for _, cluster := range clusterMap { + nameCache[cluster.ref.Reference().Value] = cluster.name + } + err = e.collectResourceType(p, ctx, "cluster", acc, clusterMap, nameCache, e.Parent.ClusterInterval) if err != nil { return err } - err = e.collectResourceType(p, ctx, "cluster", acc, e.getClusters, v, e.Parent.ClusterInterval) + // Collect host metrics + // + hostMap, err := e.getHosts(ctx, v) + if err != nil { + return err + } + for _, host := range hostMap { + nameCache[host.ref.Reference().Value] = host.name + } + err = e.collectResourceType(p, ctx, "host", acc, hostMap, nameCache, e.Parent.HostInterval) if err != nil { return err } - err = e.collectResourceType(p, ctx, "datastore", acc, e.getDatastores, v, e.Parent.DatastoreInterval) + // Collect vm metrics + // + vmMap, err := e.getVMs(ctx, v) + for _, vm := range vmMap { + nameCache[vm.ref.Reference().Value] = vm.name + } + err = e.collectResourceType(p, ctx, "vm", acc, vmMap, nameCache, e.Parent.VmInterval) if err != nil { return err } return nil } -func (e *Endpoint) getVMs(ctx context.Context, root *view.ContainerView) (map[string]types.ManagedObjectReference, error) { +func (e *Endpoint) getVMs(ctx context.Context, root *view.ContainerView) (objectMap, error) { var resources []mo.VirtualMachine - err := root.Retrieve(ctx, []string{"VirtualMachine"}, []string{"summary"}, &resources) + err := root.Retrieve(ctx, []string{"VirtualMachine"}, []string{"summary", "runtime.host"}, &resources) if err != nil { return nil, err } - m := make(map[string]types.ManagedObjectReference) + m := make(objectMap) for _, r := range resources { - m[r.Summary.Config.Name] = r.ExtensibleManagedObject.Reference() + m[r.ExtensibleManagedObject.Reference().Value] = objectRef{ + name: r.Summary.Config.Name, ref: r.ExtensibleManagedObject.Reference(), parentRef: r.Runtime.Host } } return m, nil } -func (e *Endpoint) getHosts(ctx context.Context, root *view.ContainerView) (map[string]types.ManagedObjectReference, error) { +func (e *Endpoint) getHosts(ctx context.Context, root *view.ContainerView) (objectMap, error) { var resources []mo.HostSystem - err := root.Retrieve(ctx, []string{"HostSystem"}, []string{"summary"}, &resources) + err := root.Retrieve(ctx, []string{"HostSystem"}, []string{"summary", "parent"}, &resources) if err != nil { return nil, err } - m := make(map[string]types.ManagedObjectReference) + m := make(objectMap) for _, r := range resources { - m[r.Summary.Config.Name] = r.ExtensibleManagedObject.Reference() + m[r.ExtensibleManagedObject.Reference().Value] = objectRef{ + name: r.Summary.Config.Name, ref: r.ExtensibleManagedObject.Reference(), parentRef: r.Parent } } return m, nil } -func (e *Endpoint) getClusters(ctx context.Context, root *view.ContainerView) (map[string]types.ManagedObjectReference, error) { +func (e *Endpoint) getClusters(ctx context.Context, root *view.ContainerView) (objectMap, error) { var resources []mo.ClusterComputeResource - err := root.Retrieve(ctx, []string{"ClusterComputeResource"}, []string{"summary"}, &resources) + err := root.Retrieve(ctx, []string{"ClusterComputeResource"}, []string{"summary", "name", "parent"}, &resources) if err != nil { return nil, err } - m := make(map[string]types.ManagedObjectReference) + m := make(objectMap) for _, r := range resources { - m[r.Name] = r.ExtensibleManagedObject.Reference() + m[r.ExtensibleManagedObject.Reference().Value] = objectRef{ + name: r.Name, ref: r.ExtensibleManagedObject.Reference(), parentRef: r.Parent } } return m, nil } -func (e *Endpoint) getDatastores(ctx context.Context, root *view.ContainerView) (map[string]types.ManagedObjectReference, error) { +func (e *Endpoint) getDatastores(ctx context.Context, root *view.ContainerView) (objectMap, error) { var resources []mo.Datastore - err := root.Retrieve(ctx, []string{"Datastore"}, []string{"summary"}, &resources) + err := root.Retrieve(ctx, []string{"Datastore"}, []string{"summary" }, &resources) if err != nil { return nil, err } - m := make(map[string]types.ManagedObjectReference) + m := make(objectMap) for _, r := range resources { - m[r.Summary.Name] = r.ExtensibleManagedObject.Reference() + m[r.Summary.Name] = objectRef{ ref:r.ExtensibleManagedObject.Reference(), parentRef: r.Parent } } return m, nil } @@ -285,6 +323,7 @@ func (v *VSphere) Init() { func (v *VSphere) Gather(acc telegraf.Accumulator) error { v.Init() results := make(chan error) + defer close(results) for _, ep := range v.endpoints { go func(target Endpoint) { results <- target.collect(acc) From 5276fe3a8a19ad5ca2cc225484bce971792057ba Mon Sep 17 00:00:00 2001 From: "pontus@rydin@gmail.com" Date: Mon, 6 Nov 2017 13:47:21 -0500 Subject: [PATCH 044/100] Reorganized code. Add parent as tag. --- plugins/inputs/vsphere/vsphere.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plugins/inputs/vsphere/vsphere.go b/plugins/inputs/vsphere/vsphere.go index 99e60d5495f68..41a208209e164 100644 --- a/plugins/inputs/vsphere/vsphere.go +++ b/plugins/inputs/vsphere/vsphere.go @@ -321,6 +321,7 @@ func (v *VSphere) Init() { } func (v *VSphere) Gather(acc telegraf.Accumulator) error { + start := time.Now() v.Init() results := make(chan error) defer close(results) @@ -337,6 +338,8 @@ func (v *VSphere) Gather(acc telegraf.Accumulator) error { finalErr = err } } + acc.AddCounter("telegraf.vsphere", + map[string]interface{}{ "gather.duration": time.Now().Sub(start).Seconds()}, nil, time.Now()) return finalErr } From fbda5c594ff259d1704717b4817c2df5b0c76c80 Mon Sep 17 00:00:00 2001 From: Pierre Tessier Date: Tue, 14 Nov 2017 09:45:55 -0500 Subject: [PATCH 045/100] changed Interval properties to durations --- Godeps | 1 + plugins/inputs/vsphere/vsphere.go | 136 ++++++++++++++++-------------- 2 files changed, 72 insertions(+), 65 deletions(-) diff --git a/Godeps b/Godeps index 7c504a0b88c01..f24a3836f5298 100644 --- a/Godeps +++ b/Godeps @@ -78,6 +78,7 @@ github.com/stretchr/testify 12b6f73e6084dad08a7c6e575284b177ecafbc71 github.com/tidwall/gjson 0623bd8fbdbf97cc62b98d15108832851a658e59 github.com/tidwall/match 173748da739a410c5b0b813b956f89ff94730b4c github.com/vjeantet/grok d73e972b60935c7fec0b4ffbc904ed39ecaf7efe +github.com/vmware/govmomi 7d879bac14d09f2f2a45a0477c1e45fbf52240f5 github.com/wvanbergen/kafka bc265fedb9ff5b5c5d3c0fdcef4a819b3523d3ee github.com/wvanbergen/kazoo-go 968957352185472eacb69215fa3dbfcfdbac1096 github.com/yuin/gopher-lua 66c871e454fcf10251c61bf8eff02d0978cae75a diff --git a/plugins/inputs/vsphere/vsphere.go b/plugins/inputs/vsphere/vsphere.go index 41a208209e164..ad33cb7c97002 100644 --- a/plugins/inputs/vsphere/vsphere.go +++ b/plugins/inputs/vsphere/vsphere.go @@ -1,41 +1,42 @@ package vsphere import ( + "context" "github.com/influxdata/telegraf" - "github.com/vmware/govmomi/vim25/soap" + "github.com/influxdata/telegraf/internal" + "github.com/influxdata/telegraf/plugins/inputs" "github.com/vmware/govmomi" + "github.com/vmware/govmomi/performance" "github.com/vmware/govmomi/view" "github.com/vmware/govmomi/vim25/mo" - "context" - "github.com/vmware/govmomi/performance" + "github.com/vmware/govmomi/vim25/soap" "github.com/vmware/govmomi/vim25/types" - "github.com/influxdata/telegraf/plugins/inputs" - "time" "log" "net/url" + "time" ) type Endpoint struct { - Parent *VSphere - Url *url.URL + Parent *VSphere + Url *url.URL intervals []int32 - lastColl map[string]time.Time + lastColl map[string]time.Time } type VSphere struct { - Vcenters []string - VmInterval int32 - HostInterval int32 - ClusterInterval int32 - DatastoreInterval int32 - MaxSamples int32 - MaxQuery int32 - endpoints []Endpoint + Vcenters []string + VmInterval internal.Duration + HostInterval internal.Duration + ClusterInterval internal.Duration + DatastoreInterval internal.Duration + MaxSamples int32 + MaxQuery int32 + endpoints []Endpoint } type objectRef struct { - name string - ref types.ManagedObjectReference + name string + ref types.ManagedObjectReference parentRef *types.ManagedObjectReference //Pointer because it must be nillable } @@ -61,11 +62,12 @@ func (e *Endpoint) init(p *performance.Manager) error { } func (e *Endpoint) collectResourceType(p *performance.Manager, ctx context.Context, alias string, acc telegraf.Accumulator, - objects objectMap, nameCache map[string]string, interval int32) error { + objects objectMap, nameCache map[string]string, intervalDuration internal.Duration) error { - // Interval = -1 means collection for this metric was diabled, so don't even bother. + // Interval = 0 means collection for this metric was diabled, so don't even bother. // - if interval == -1 { + interval := int32(intervalDuration.Duration.Seconds()) + if interval == 0 { return nil } @@ -74,12 +76,12 @@ func (e *Endpoint) collectResourceType(p *performance.Manager, ctx context.Conte now := time.Now() nIntervals := int32(1) latest, hasLatest := e.lastColl[alias] - if (hasLatest) { + if hasLatest { elapsed := time.Now().Sub(latest).Seconds() if elapsed < float64(interval) { // No new data would be available. We're outta here! // - return nil; + return nil } nIntervals := int32(elapsed / (float64(interval))) if nIntervals > e.Parent.MaxSamples { @@ -93,20 +95,20 @@ func (e *Endpoint) collectResourceType(p *performance.Manager, ctx context.Conte start := time.Now() log.Printf("D! Query for %s returned %d objects", alias, len(objects)) pqs := make([]types.PerfQuerySpec, 0, e.Parent.MaxQuery) - total := 0; + total := 0 for _, object := range objects { pq := types.PerfQuerySpec{ - Entity: object.ref, - MaxSample: nIntervals, - MetricId: nil, + Entity: object.ref, + MaxSample: nIntervals, + MetricId: nil, IntervalId: interval, } - if(interval > 20) { + if interval > 20 { startTime := now.Add(-time.Duration(interval) * time.Second) pq.StartTime = &startTime pq.EndTime = &now } - if(e.Parent.MaxSamples > 1 && hasLatest) { + if e.Parent.MaxSamples > 1 && hasLatest { pq.StartTime = &latest pq.EndTime = &now } @@ -115,7 +117,7 @@ func (e *Endpoint) collectResourceType(p *performance.Manager, ctx context.Conte // Filled up a chunk or at end of data? Run a query with the collected objects // - if len(pqs) >= int(e.Parent.MaxQuery) || total == len(objects) { + if len(pqs) >= int(e.Parent.MaxQuery) || total == len(objects) { log.Printf("D! Querying %d objects of type %s for %s. Total processed: %d. Total objects %d\n", len(pqs), alias, e.Url.Host, total, len(objects)) metrics, err := p.Query(ctx, pqs) if err != nil { @@ -134,20 +136,19 @@ func (e *Endpoint) collectResourceType(p *performance.Manager, ctx context.Conte for _, v := range em.Value { name := v.Name for idx, value := range v.Value { - f := map[string]interface{} { name: value } + f := map[string]interface{}{name: value} objectName := nameCache[moid] parent := "" parentRef := objects[moid].parentRef - //log.Printf("Parentref=%s", parentRef) if parentRef != nil { parent = nameCache[parentRef.Value] } t := map[string]string{ - "vcenter": e.Url.Host, - "source": objectName, - "moid": moid, - "parent": parent} + "vcenter": e.Url.Host, + "hostname": objectName, + "moid": moid, + "parent": parent} if v.Instance != "" { t["instance"] = v.Instance } @@ -159,7 +160,6 @@ func (e *Endpoint) collectResourceType(p *performance.Manager, ctx context.Conte } } - log.Printf("D! Collection of %s took %v\n", alias, time.Now().Sub(start)) return nil } @@ -167,14 +167,14 @@ func (e *Endpoint) collectResourceType(p *performance.Manager, ctx context.Conte func (e *Endpoint) collect(acc telegraf.Accumulator) error { ctx := context.Background() c, err := govmomi.NewClient(ctx, e.Url, true) - if(err != nil) { + if err != nil { return err } defer c.Logout(ctx) m := view.NewManager(c.Client) - v, err := m.CreateContainerView(ctx, c.ServiceContent.RootFolder, []string{ }, true) + v, err := m.CreateContainerView(ctx, c.ServiceContent.RootFolder, []string{}, true) if err != nil { return err } @@ -183,6 +183,7 @@ func (e *Endpoint) collect(acc telegraf.Accumulator) error { p := performance.NewManager(c.Client) + //TODO: // This causes strange error messages in the vCenter console. Possibly due to a bug in // govmomi. We're commenting it out for now. Should be benign since the logout should // destroy all resources anyway. @@ -196,7 +197,7 @@ func (e *Endpoint) collect(acc telegraf.Accumulator) error { // Collect cluster metrics // - clusterMap, err := e.getClusters(ctx, v); + clusterMap, err := e.getClusters(ctx, v) if err != nil { return err } @@ -244,7 +245,7 @@ func (e *Endpoint) getVMs(ctx context.Context, root *view.ContainerView) (object m := make(objectMap) for _, r := range resources { m[r.ExtensibleManagedObject.Reference().Value] = objectRef{ - name: r.Summary.Config.Name, ref: r.ExtensibleManagedObject.Reference(), parentRef: r.Runtime.Host } + name: r.Summary.Config.Name, ref: r.ExtensibleManagedObject.Reference(), parentRef: r.Runtime.Host} } return m, nil } @@ -258,7 +259,7 @@ func (e *Endpoint) getHosts(ctx context.Context, root *view.ContainerView) (obje m := make(objectMap) for _, r := range resources { m[r.ExtensibleManagedObject.Reference().Value] = objectRef{ - name: r.Summary.Config.Name, ref: r.ExtensibleManagedObject.Reference(), parentRef: r.Parent } + name: r.Summary.Config.Name, ref: r.ExtensibleManagedObject.Reference(), parentRef: r.Parent} } return m, nil } @@ -272,20 +273,20 @@ func (e *Endpoint) getClusters(ctx context.Context, root *view.ContainerView) (o m := make(objectMap) for _, r := range resources { m[r.ExtensibleManagedObject.Reference().Value] = objectRef{ - name: r.Name, ref: r.ExtensibleManagedObject.Reference(), parentRef: r.Parent } + name: r.Name, ref: r.ExtensibleManagedObject.Reference(), parentRef: r.Parent} } return m, nil } func (e *Endpoint) getDatastores(ctx context.Context, root *view.ContainerView) (objectMap, error) { var resources []mo.Datastore - err := root.Retrieve(ctx, []string{"Datastore"}, []string{"summary" }, &resources) + err := root.Retrieve(ctx, []string{"Datastore"}, []string{"summary"}, &resources) if err != nil { return nil, err } m := make(objectMap) for _, r := range resources { - m[r.Summary.Name] = objectRef{ ref:r.ExtensibleManagedObject.Reference(), parentRef: r.Parent } + m[r.Summary.Name] = objectRef{ref: r.ExtensibleManagedObject.Reference(), parentRef: r.Parent} } return m, nil } @@ -303,26 +304,31 @@ func (v *VSphere) Description() string { return "Read metrics from VMware vCenter" } -func (v *VSphere) Init() { +func (v *VSphere) vSphereInit() { if v.endpoints != nil { return } + v.endpoints = make([]Endpoint, len(v.Vcenters)) for i, rawUrl := range v.Vcenters { - u, err := soap.ParseURL(rawUrl); - if(err != nil) { + u, err := soap.ParseURL(rawUrl) + if err != nil { log.Printf("E! Can't parse URL %s\n", rawUrl) } v.endpoints[i] = Endpoint{ - Url: u, - Parent: v, - lastColl: make(map[string]time.Time)} + Url: u, + Parent: v, + lastColl: make(map[string]time.Time), + } } } func (v *VSphere) Gather(acc telegraf.Accumulator) error { + + v.vSphereInit() + start := time.Now() - v.Init() + results := make(chan error) defer close(results) for _, ep := range v.endpoints { @@ -330,32 +336,32 @@ func (v *VSphere) Gather(acc telegraf.Accumulator) error { results <- target.collect(acc) }(ep) } + var finalErr error = nil for range v.endpoints { - err := <- results + err := <-results if err != nil { log.Println("E!", err) finalErr = err } } - acc.AddCounter("telegraf.vsphere", - map[string]interface{}{ "gather.duration": time.Now().Sub(start).Seconds()}, nil, time.Now()) + + // Add another counter to show how long it took to gather all the metrics on this cycle (can be used to tune # of vCenters and collection intervals per telegraf agent) + acc.AddCounter("vsphere", map[string]interface{}{"gather.duration": time.Now().Sub(start).Seconds()}, nil, time.Now()) + return finalErr } func init() { inputs.Add("vsphere", func() telegraf.Input { return &VSphere{ - Vcenters: []string {}, - VmInterval: 20, - HostInterval: 20, - ClusterInterval: 300, - DatastoreInterval: 300, - MaxSamples: 10, - MaxQuery: 64, + Vcenters: []string{}, + VmInterval: internal.Duration{Duration: time.Second * 150}, + HostInterval: internal.Duration{Duration: time.Second * 150}, + ClusterInterval: internal.Duration{Duration: time.Second * 300}, + DatastoreInterval: internal.Duration{Duration: time.Second * 300}, + MaxSamples: 10, + MaxQuery: 64, } }) } - - - From 270c41107d9b5e5b8b59359c7525b53c830fb2de Mon Sep 17 00:00:00 2001 From: Pontus Rydin Date: Fri, 13 Apr 2018 14:43:43 -0400 Subject: [PATCH 046/100] Copied wavefront.go from upstream to fix merge issues. --- plugins/outputs/wavefront/wavefront.go | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/plugins/outputs/wavefront/wavefront.go b/plugins/outputs/wavefront/wavefront.go index 6bcc817fc68f7..1e015c9f0a60a 100644 --- a/plugins/outputs/wavefront/wavefront.go +++ b/plugins/outputs/wavefront/wavefront.go @@ -9,6 +9,8 @@ import ( "strconv" "strings" + "time" + "github.com/influxdata/telegraf" "github.com/influxdata/telegraf/plugins/outputs" ) @@ -67,7 +69,7 @@ var sampleConfig = ` #use_regex = false ## point tags to use as the source name for Wavefront (if none found, host will be used) - #source_override = ["hostname", "snmp_host", "node_host"] + #source_override = ["hostname", "agent_host", "node_host"] ## whether to convert boolean values to numeric values, with false -> 0.0 and true -> 1.0. default true #convert_bool = true @@ -120,11 +122,11 @@ func (w *Wavefront) Write(metrics []telegraf.Metric) error { return fmt.Errorf("Wavefront: TCP connect fail %s", err.Error()) } defer connection.Close() + connection.SetWriteDeadline(time.Now().Add(5 * time.Second)) for _, m := range metrics { for _, metricPoint := range buildMetrics(m, w) { metricLine := formatMetricPoint(metricPoint, w) - log.Printf("D! Output [wavefront] %s", metricLine) _, err := connection.Write([]byte(metricLine)) if err != nil { return fmt.Errorf("Wavefront: TCP writing error %s", err.Error()) @@ -158,7 +160,7 @@ func buildMetrics(m telegraf.Metric, w *Wavefront) []*MetricPoint { metric := &MetricPoint{ Metric: name, - Timestamp: m.UnixNano() / 1000000000, + Timestamp: m.Time().Unix(), } metricValue, buildError := buildValue(value, metric.Metric, w) @@ -261,7 +263,9 @@ func formatMetricPoint(metricPoint *MetricPoint, w *Wavefront) string { buffer.WriteString(tagValueReplacer.Replace(v)) buffer.WriteString("\"") } + buffer.WriteString("\n") + return buffer.String() } From a5c8ae2ffd2d53230d6e821c2cafffc026fec9a2 Mon Sep 17 00:00:00 2001 From: Pontus Rydin Date: Fri, 13 Apr 2018 14:48:30 -0400 Subject: [PATCH 047/100] Fixed thrift directory naming issues (copied from upstream) --- plugins/inputs/zipkin/codec/codec.go | 2 +- plugins/inputs/zipkin/codec/jsonV1/jsonV1.go | 2 +- plugins/inputs/zipkin/codec/thrift/thrift.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/inputs/zipkin/codec/codec.go b/plugins/inputs/zipkin/codec/codec.go index 167b8ec24f1a3..781aad72aa73a 100644 --- a/plugins/inputs/zipkin/codec/codec.go +++ b/plugins/inputs/zipkin/codec/codec.go @@ -4,7 +4,7 @@ import ( "time" "github.com/influxdata/telegraf/plugins/inputs/zipkin/trace" - "github.com/openzipkin/zipkin-go-opentracing/thrift/gen-go/zipkincore" + "github.com/openzipkin/zipkin-go-opentracing/_thrift/gen-go/zipkincore" ) //now is a mockable time for now diff --git a/plugins/inputs/zipkin/codec/jsonV1/jsonV1.go b/plugins/inputs/zipkin/codec/jsonV1/jsonV1.go index 1803486742301..8c229b9f315d5 100644 --- a/plugins/inputs/zipkin/codec/jsonV1/jsonV1.go +++ b/plugins/inputs/zipkin/codec/jsonV1/jsonV1.go @@ -7,7 +7,7 @@ import ( "time" "github.com/influxdata/telegraf/plugins/inputs/zipkin/codec" - "github.com/openzipkin/zipkin-go-opentracing/thrift/gen-go/zipkincore" + "github.com/openzipkin/zipkin-go-opentracing/_thrift/gen-go/zipkincore" ) // JSON decodes spans from bodies `POST`ed to the spans endpoint diff --git a/plugins/inputs/zipkin/codec/thrift/thrift.go b/plugins/inputs/zipkin/codec/thrift/thrift.go index 65a9e1488c2c4..b3fc94897b4b3 100644 --- a/plugins/inputs/zipkin/codec/thrift/thrift.go +++ b/plugins/inputs/zipkin/codec/thrift/thrift.go @@ -10,7 +10,7 @@ import ( "github.com/influxdata/telegraf/plugins/inputs/zipkin/codec" "github.com/apache/thrift/lib/go/thrift" - "github.com/openzipkin/zipkin-go-opentracing/thrift/gen-go/zipkincore" + "github.com/openzipkin/zipkin-go-opentracing/_thrift/gen-go/zipkincore" ) // UnmarshalThrift converts raw bytes in thrift format to a slice of spans From 29546ce251c5d7d7eadc4f06a94cbb6fc52720a8 Mon Sep 17 00:00:00 2001 From: Pierre Tessier Date: Mon, 30 Apr 2018 22:14:07 -0400 Subject: [PATCH 048/100] added guest to vm metrics --- plugins/inputs/vsphere/endpoint.go | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/plugins/inputs/vsphere/endpoint.go b/plugins/inputs/vsphere/endpoint.go index fd07ee9a7b73e..099d55aabcb51 100644 --- a/plugins/inputs/vsphere/endpoint.go +++ b/plugins/inputs/vsphere/endpoint.go @@ -43,6 +43,7 @@ type objectRef struct { name string ref types.ManagedObjectReference parentRef *types.ManagedObjectReference //Pointer because it must be nillable + guest string } func NewEndpoint(parent *VSphere, url *url.URL) *Endpoint { @@ -268,14 +269,14 @@ func getHosts(root *view.ContainerView) (objectMap, error) { func getVMs(root *view.ContainerView) (objectMap, error) { var resources []mo.VirtualMachine - err := root.Retrieve(context.Background(), []string{"VirtualMachine"}, []string{"name", "runtime.host"}, &resources) + err := root.Retrieve(context.Background(), []string{"VirtualMachine"}, []string{"name", "runtime.host", "config.guestId"}, &resources) if err != nil { return nil, err } m := make(objectMap) for _, r := range resources { m[r.ExtensibleManagedObject.Reference().Value] = objectRef{ - name: r.Name, ref: r.ExtensibleManagedObject.Reference(), parentRef: r.Runtime.Host} + name: r.Name, ref: r.ExtensibleManagedObject.Reference(), parentRef: r.Runtime.Host, guest: cleanGuestId(r.Config.GuestId)} } return m, nil } @@ -398,6 +399,7 @@ func (e *Endpoint) collectResource(resourceType string, acc telegraf.Accumulator log.Printf("D! Querying %d objects of type %s for %s. Object count: %d. Total objects %d", len(pqs), resourceType, e.Url.Host, total, len(res.objects)) metrics, err := client.Perf.Query(ctx, pqs) if err != nil { + //TODO: Check the error and attempt to handle gracefully. (ie: object no longer exists) log.Printf("E! Error querying metrics of %s for %s", resourceType, e.Url.Host) e.checkClient() return count, time.Now().Sub(start).Seconds(), err @@ -433,6 +435,7 @@ func (e *Endpoint) collectResource(resourceType string, acc telegraf.Accumulator break case "vm": + t["guest"] = objectRef.guest t["esxhost"] = parent hostRes := e.resources["host"] hostRef, ok := hostRes.objects[objectRef.parentRef.Value] @@ -494,6 +497,14 @@ func (e *Endpoint) collectResource(resourceType string, acc telegraf.Accumulator return count, time.Now().Sub(start).Seconds(), nil } +func cleanGuestId(id string) string { + if strings.HasSuffix(id, "Guest") { + return id[:len(id) - 5] + } + + return id +} + func cleanDiskTag(disk string) string { if strings.HasPrefix(disk, "<") { i := strings.Index(disk, ">") From 580f8ed4814b43ad5fdbaf0eb8ef6fbb98658671 Mon Sep 17 00:00:00 2001 From: Pierre Tessier Date: Mon, 30 Apr 2018 22:27:42 -0400 Subject: [PATCH 049/100] go fmt --- plugins/inputs/vsphere/client.go | 2 +- plugins/inputs/vsphere/endpoint.go | 2 +- plugins/inputs/vsphere/vsphere_test.go | 24 ++++++++++++------------ 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/plugins/inputs/vsphere/client.go b/plugins/inputs/vsphere/client.go index a9514b89208c1..cc65a0945ba0f 100644 --- a/plugins/inputs/vsphere/client.go +++ b/plugins/inputs/vsphere/client.go @@ -6,8 +6,8 @@ import ( "github.com/vmware/govmomi" "github.com/vmware/govmomi/performance" "github.com/vmware/govmomi/view" - "net/url" "log" + "net/url" ) type Client struct { diff --git a/plugins/inputs/vsphere/endpoint.go b/plugins/inputs/vsphere/endpoint.go index 099d55aabcb51..d335db475d215 100644 --- a/plugins/inputs/vsphere/endpoint.go +++ b/plugins/inputs/vsphere/endpoint.go @@ -499,7 +499,7 @@ func (e *Endpoint) collectResource(resourceType string, acc telegraf.Accumulator func cleanGuestId(id string) string { if strings.HasSuffix(id, "Guest") { - return id[:len(id) - 5] + return id[:len(id)-5] } return id diff --git a/plugins/inputs/vsphere/vsphere_test.go b/plugins/inputs/vsphere/vsphere_test.go index 0d61d0156ef35..743a0ba6fb373 100644 --- a/plugins/inputs/vsphere/vsphere_test.go +++ b/plugins/inputs/vsphere/vsphere_test.go @@ -1,26 +1,26 @@ package vsphere import ( - "github.com/vmware/govmomi/simulator" "crypto/tls" - "testing" "fmt" - "time" "github.com/influxdata/telegraf/internal" "github.com/influxdata/telegraf/testutil" "github.com/stretchr/testify/require" + "github.com/vmware/govmomi/simulator" + "testing" + "time" ) func defaultVSphere() *VSphere { return &VSphere{ - GatherClusters: true, - ClusterMetrics: nil, - GatherHosts: true, - HostMetrics: nil, - GatherVms: true, - VmMetrics: nil, - GatherDatastores: true, - DatastoreMetrics: nil, + GatherClusters: true, + ClusterMetrics: nil, + GatherHosts: true, + HostMetrics: nil, + GatherVms: true, + VmMetrics: nil, + GatherDatastores: true, + DatastoreMetrics: nil, InsecureSkipVerify: true, ObjectsPerQuery: 256, @@ -54,4 +54,4 @@ func TestAll(t *testing.T) { v := defaultVSphere() v.Vcenters = []string{s.URL.String()} require.NoError(t, v.Gather(&acc)) -} \ No newline at end of file +} From 17453ef058e4f973789e5c23a13da346a59b749e Mon Sep 17 00:00:00 2001 From: Pierre Tessier Date: Mon, 30 Apr 2018 22:28:02 -0400 Subject: [PATCH 050/100] added tags --- plugins/inputs/vsphere/README.MD | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/plugins/inputs/vsphere/README.MD b/plugins/inputs/vsphere/README.MD index e45c05c0e9fed..fb502befdd9f4 100644 --- a/plugins/inputs/vsphere/README.MD +++ b/plugins/inputs/vsphere/README.MD @@ -111,6 +111,14 @@ for more information. - Disk: Capacity, provisioned, used ## Tags +- all metrics + - vcenter (vcenter url) +- all host metrics + - cluster (vcenter cluster) +- all vm metrics + - cluster (vcenter cluster) + - esxhost (name of ESXi host) + - guest (guest operating system id) - cpu stats for Host and VM - cpu (cpu core - not all CPU fields will have this tag) - datastore stats for Host and VM From 780484508e31cb9f80981180b3cf61f58ba0719b Mon Sep 17 00:00:00 2001 From: Pierre Tessier Date: Mon, 30 Apr 2018 22:34:28 -0400 Subject: [PATCH 051/100] updated govmomi to v0.17.1 --- Godeps | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Godeps b/Godeps index f24a3836f5298..6f5bd8adff0b9 100644 --- a/Godeps +++ b/Godeps @@ -78,7 +78,7 @@ github.com/stretchr/testify 12b6f73e6084dad08a7c6e575284b177ecafbc71 github.com/tidwall/gjson 0623bd8fbdbf97cc62b98d15108832851a658e59 github.com/tidwall/match 173748da739a410c5b0b813b956f89ff94730b4c github.com/vjeantet/grok d73e972b60935c7fec0b4ffbc904ed39ecaf7efe -github.com/vmware/govmomi 7d879bac14d09f2f2a45a0477c1e45fbf52240f5 +github.com/vmware/govmomi 123ed177021588bac57b5c87c1a84270ddf2eca8 github.com/wvanbergen/kafka bc265fedb9ff5b5c5d3c0fdcef4a819b3523d3ee github.com/wvanbergen/kazoo-go 968957352185472eacb69215fa3dbfcfdbac1096 github.com/yuin/gopher-lua 66c871e454fcf10251c61bf8eff02d0978cae75a From d78041793ed6cca2d9d7b46f1514744ae5c2f7d5 Mon Sep 17 00:00:00 2001 From: Pierre Tessier Date: Tue, 8 May 2018 15:30:54 -0400 Subject: [PATCH 052/100] add check for when guest info is not available --- plugins/inputs/vsphere/endpoint.go | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/plugins/inputs/vsphere/endpoint.go b/plugins/inputs/vsphere/endpoint.go index d335db475d215..8f60cd8389349 100644 --- a/plugins/inputs/vsphere/endpoint.go +++ b/plugins/inputs/vsphere/endpoint.go @@ -275,8 +275,16 @@ func getVMs(root *view.ContainerView) (objectMap, error) { } m := make(objectMap) for _, r := range resources { + var guest string + // Sometimes Config is unknown and returns a nil pointer + // + if r.Config != nil { + guest = cleanGuestId(r.Config.GuestId) + } else { + guest = "unknown" + } m[r.ExtensibleManagedObject.Reference().Value] = objectRef{ - name: r.Name, ref: r.ExtensibleManagedObject.Reference(), parentRef: r.Runtime.Host, guest: cleanGuestId(r.Config.GuestId)} + name: r.Name, ref: r.ExtensibleManagedObject.Reference(), parentRef: r.Runtime.Host, guest: guest} } return m, nil } From 69e659dc9fb94a51606e585d4aca0ac8f89ac4c5 Mon Sep 17 00:00:00 2001 From: Pontus Rydin Date: Fri, 11 May 2018 20:53:29 -0400 Subject: [PATCH 053/100] Fixed a bug in the test harness causing it to fail --- plugins/inputs/vsphere/vsphere_test.go | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/plugins/inputs/vsphere/vsphere_test.go b/plugins/inputs/vsphere/vsphere_test.go index 743a0ba6fb373..16d911f8b157c 100644 --- a/plugins/inputs/vsphere/vsphere_test.go +++ b/plugins/inputs/vsphere/vsphere_test.go @@ -29,12 +29,12 @@ func defaultVSphere() *VSphere { } } -func createSim() (*simulator.Model, *simulator.Server) { +func createSim() (*simulator.Model, *simulator.Server, error) { model := simulator.VPX() err := model.Create() if err != nil { - fmt.Errorf("Error creating model: %s\n", err) + return nil, nil, err } model.Service.TLS = new(tls.Config) @@ -42,11 +42,14 @@ func createSim() (*simulator.Model, *simulator.Server) { s := model.Service.NewServer() fmt.Printf("Server created at: %s\n", s.URL) - return model, s + return model, s, nil } func TestAll(t *testing.T) { - m, s := createSim() + m, s, err := createSim() + if err != nil { + t.Fatal(err) + } defer m.Remove() defer s.Close() From f31842025bffe679167b0c22447e765b45a0e9b4 Mon Sep 17 00:00:00 2001 From: prydin Date: Sat, 12 May 2018 22:40:05 -0400 Subject: [PATCH 054/100] Fixed code broken by new handling of TLS config in core Made look a but prettier in go vet. --- plugins/inputs/vsphere/client.go | 12 +++--- plugins/inputs/vsphere/endpoint.go | 59 ++++++++++++++------------ plugins/inputs/vsphere/vsphere.go | 33 +++++++------- plugins/inputs/vsphere/vsphere_test.go | 24 ++++++----- 4 files changed, 70 insertions(+), 58 deletions(-) diff --git a/plugins/inputs/vsphere/client.go b/plugins/inputs/vsphere/client.go index cc65a0945ba0f..e6944ed73490a 100644 --- a/plugins/inputs/vsphere/client.go +++ b/plugins/inputs/vsphere/client.go @@ -2,14 +2,15 @@ package vsphere import ( "context" - "github.com/influxdata/telegraf/internal" + "log" + "net/url" + "github.com/vmware/govmomi" "github.com/vmware/govmomi/performance" "github.com/vmware/govmomi/view" - "log" - "net/url" ) +// Client represents a connection to vSphere and is backed by a govmoni connection type Client struct { Client *govmomi.Client Views *view.Manager @@ -17,9 +18,9 @@ type Client struct { Perf *performance.Manager } +// NewClient creates a new vSphere client based on the url and setting passed as parameters. func NewClient(url *url.URL, vs *VSphere) (*Client, error) { - - tlsCfg, err := internal.GetTLSConfig(vs.SSLCert, vs.SSLKey, vs.SSLCA, vs.InsecureSkipVerify) + tlsCfg, err := vs.TLSConfig() if err != nil { return nil, err } @@ -58,6 +59,7 @@ func NewClient(url *url.URL, vs *VSphere) (*Client, error) { }, nil } +// Close disconnects a client from the vSphere backend and releases all assiciated resources. func (c *Client) Close() { ctx := context.Background() if c.Views != nil { diff --git a/plugins/inputs/vsphere/endpoint.go b/plugins/inputs/vsphere/endpoint.go index 8f60cd8389349..95ab546042d12 100644 --- a/plugins/inputs/vsphere/endpoint.go +++ b/plugins/inputs/vsphere/endpoint.go @@ -2,21 +2,24 @@ package vsphere import ( "context" - "github.com/gobwas/glob" - "github.com/influxdata/telegraf" - "github.com/vmware/govmomi/view" - "github.com/vmware/govmomi/vim25/mo" - "github.com/vmware/govmomi/vim25/types" "log" "net/url" "strings" "sync" "time" + + "github.com/gobwas/glob" + "github.com/influxdata/telegraf" + "github.com/vmware/govmomi/view" + "github.com/vmware/govmomi/vim25/mo" + "github.com/vmware/govmomi/vim25/types" ) +// Endpoint is a high-level representation of a connected vCenter endpoint. It is backed by the lower +// level Client type. type Endpoint struct { Parent *VSphere - Url *url.URL + URL *url.URL client *Client lastColls map[string]time.Time nameCache map[string]string @@ -46,9 +49,11 @@ type objectRef struct { guest string } +// NewEndpoint returns a new connection to a vCenter based on the URL and configuration passed +// as parameters. func NewEndpoint(parent *VSphere, url *url.URL) *Endpoint { e := Endpoint{ - Url: url, + URL: url, Parent: parent, lastColls: make(map[string]time.Time), nameCache: make(map[string]string), @@ -99,7 +104,7 @@ func (e *Endpoint) init() error { err := e.setupMetricIds() if err != nil { - log.Printf("E! Error in metric setup for %s: %v", e.Url.Host, err) + log.Printf("E! Error in metric setup for %s: %v", e.URL.Host, err) return err } @@ -108,7 +113,7 @@ func (e *Endpoint) init() error { // err = e.discover() if err != nil { - log.Printf("E! Error in initial discovery for %s: %v", e.Url.Host, err) + log.Printf("E! Error in initial discovery for %s: %v", e.URL.Host, err) return err } @@ -119,7 +124,7 @@ func (e *Endpoint) init() error { for range e.discoveryTicker.C { err := e.discover() if err != nil { - log.Printf("E! Error in discovery for %s: %v", e.Url.Host, err) + log.Printf("E! Error in discovery for %s: %v", e.URL.Host, err) } } }() @@ -193,7 +198,7 @@ func resolveMetricWildcards(metricMap map[string]*types.PerfCounterInfo, wildcar } func (e *Endpoint) discover() error { - log.Printf("D! Discover new objects for %s", e.Url.Host) + log.Printf("D! Discover new objects for %s", e.URL.Host) client, err := e.getClient() if err != nil { @@ -234,7 +239,7 @@ func (e *Endpoint) discover() error { e.nameCache = nameCache e.resources = resources - log.Printf("D! Discovered %d objects for %s", len(nameCache), e.Url.Host) + log.Printf("D! Discovered %d objects for %s", len(nameCache), e.URL.Host) return nil } @@ -279,7 +284,7 @@ func getVMs(root *view.ContainerView) (objectMap, error) { // Sometimes Config is unknown and returns a nil pointer // if r.Config != nil { - guest = cleanGuestId(r.Config.GuestId) + guest = cleanGuestID(r.Config.GuestId) } else { guest = "unknown" } @@ -318,7 +323,7 @@ func (e *Endpoint) collect(acc telegraf.Accumulator) error { if e.Parent.ObjectDiscoveryInterval.Duration.Seconds() == 0 { err = e.discover() if err != nil { - log.Printf("E! Error in discovery prior to collect for %s: %v", e.Url.Host, err) + log.Printf("E! Error in discovery prior to collect for %s: %v", e.URL.Host, err) return err } } @@ -332,7 +337,7 @@ func (e *Endpoint) collect(acc telegraf.Accumulator) error { acc.AddGauge("vsphere", map[string]interface{}{"gather.count": count, "gather.duration": duration}, - map[string]string{"vcenter": e.Url.Host, "type": k}, + map[string]string{"vcenter": e.URL.Host, "type": k}, time.Now()) } } @@ -352,7 +357,7 @@ func (e *Endpoint) collectResource(resourceType string, acc telegraf.Accumulator if elapsed < float64(res.sampling) { // No new data would be available. We're outta here! // - log.Printf("D! Sampling period for %s of %d has not elapsed for %s", resourceType, res.sampling, e.Url.Host) + log.Printf("D! Sampling period for %s of %d has not elapsed for %s", resourceType, res.sampling, e.URL.Host) return 0, 0, nil } } @@ -363,9 +368,9 @@ func (e *Endpoint) collectResource(resourceType string, acc telegraf.Accumulator } if len(res.metricIds) == 0 { - log.Printf("D! Collecting all metrics for %d objects of type %s for %s", len(res.objects), resourceType, e.Url.Host) + log.Printf("D! Collecting all metrics for %d objects of type %s for %s", len(res.objects), resourceType, e.URL.Host) } else { - log.Printf("D! Collecting %d metrics for %d objects of type %s for %s", len(res.metricIds), len(res.objects), resourceType, e.Url.Host) + log.Printf("D! Collecting %d metrics for %d objects of type %s for %s", len(res.metricIds), len(res.objects), resourceType, e.URL.Host) } client, err := e.getClient() @@ -404,11 +409,11 @@ func (e *Endpoint) collectResource(resourceType string, acc telegraf.Accumulator // Filled up a chunk or at end of data? Run a query with the collected objects // if len(pqs) >= int(e.Parent.ObjectsPerQuery) || total == len(res.objects) { - log.Printf("D! Querying %d objects of type %s for %s. Object count: %d. Total objects %d", len(pqs), resourceType, e.Url.Host, total, len(res.objects)) + log.Printf("D! Querying %d objects of type %s for %s. Object count: %d. Total objects %d", len(pqs), resourceType, e.URL.Host, total, len(res.objects)) metrics, err := client.Perf.Query(ctx, pqs) if err != nil { //TODO: Check the error and attempt to handle gracefully. (ie: object no longer exists) - log.Printf("E! Error querying metrics of %s for %s", resourceType, e.Url.Host) + log.Printf("E! Error querying metrics of %s for %s", resourceType, e.URL.Host) e.checkClient() return count, time.Now().Sub(start).Seconds(), err } @@ -429,7 +434,7 @@ func (e *Endpoint) collectResource(resourceType string, acc telegraf.Accumulator objectName := e.nameCache[moid] t := map[string]string{ - "vcenter": e.Url.Host, + "vcenter": e.URL.Host, "hostname": objectName, "moid": moid, } @@ -501,11 +506,11 @@ func (e *Endpoint) collectResource(resourceType string, acc telegraf.Accumulator e.lastColls[resourceType] = lastTS } - log.Printf("D! Collection of %s for %s, took %v returning %d metrics", resourceType, e.Url.Host, time.Now().Sub(start), count) + log.Printf("D! Collection of %s for %s, took %v returning %d metrics", resourceType, e.URL.Host, time.Now().Sub(start), count) return count, time.Now().Sub(start).Seconds(), nil } -func cleanGuestId(id string) string { +func cleanGuestID(id string) string { if strings.HasSuffix(id, "Guest") { return id[:len(id)-5] } @@ -533,8 +538,8 @@ func (e *Endpoint) getClient() (*Client, error) { e.clientMux.Lock() defer e.clientMux.Unlock() if e.client == nil { - log.Printf("D! Creating new vCenter client for: %s", e.Url.Host) - client, err := NewClient(e.Url, e.Parent) + log.Printf("D! Creating new vCenter client for: %s", e.URL.Host) + client, err := NewClient(e.URL, e.Parent) if err != nil { return nil, err } @@ -548,11 +553,11 @@ func (e *Endpoint) checkClient() { if e.client != nil { active, err := e.client.Client.SessionManager.SessionIsActive(context.Background()) if err != nil { - log.Printf("E! SessionIsActive returned an error on %s: %v", e.Url.Host, err) + log.Printf("E! SessionIsActive returned an error on %s: %v", e.URL.Host, err) e.client = nil } if !active { - log.Printf("I! Session no longer active, reseting client: %s", e.Url.Host) + log.Printf("I! Session no longer active, reseting client: %s", e.URL.Host) e.client = nil } } diff --git a/plugins/inputs/vsphere/vsphere.go b/plugins/inputs/vsphere/vsphere.go index 9eb998a43625c..8e3db4b46b58a 100644 --- a/plugins/inputs/vsphere/vsphere.go +++ b/plugins/inputs/vsphere/vsphere.go @@ -1,15 +1,19 @@ package vsphere import ( + "log" + "sync" + "time" + "github.com/influxdata/telegraf" "github.com/influxdata/telegraf/internal" + "github.com/influxdata/telegraf/internal/tls" "github.com/influxdata/telegraf/plugins/inputs" "github.com/vmware/govmomi/vim25/soap" - "log" - "sync" - "time" ) +// VSphere is the top level type for the vSphere input plugin. It contains all the configuration +// and a list of connected vSphere endpoints type VSphere struct { Vcenters []string @@ -26,16 +30,10 @@ type VSphere struct { ObjectDiscoveryInterval internal.Duration Timeout internal.Duration - // Path to CA file - SSLCA string `toml:"ssl_ca"` - // Path to host cert file - SSLCert string `toml:"ssl_cert"` - // Path to cert key file - SSLKey string `toml:"ssl_key"` - // Use SSL but skip chain & host verification - InsecureSkipVerify bool - endpoints []*Endpoint + + // Mix in the TLS/SSL goodness from core + tls.ClientConfig } var sampleConfig = ` @@ -43,10 +41,13 @@ var sampleConfig = ` # vcenters = [ "https://administrator%40vsphere.local:VMware1!@vcenter.local/sdk" ] ` +// SampleConfig returns a set of default configuration to be used as a boilerplate when setting up +// Telegraf. func (v *VSphere) SampleConfig() string { return sampleConfig } +// Description returns a short textual description of the plugin func (v *VSphere) Description() string { return "Read metrics from VMware vCenter" } @@ -57,16 +58,18 @@ func (v *VSphere) checkEndpoints() { } v.endpoints = make([]*Endpoint, len(v.Vcenters)) - for i, rawUrl := range v.Vcenters { - u, err := soap.ParseURL(rawUrl) + for i, rawURL := range v.Vcenters { + u, err := soap.ParseURL(rawURL) if err != nil { - log.Printf("E! Can't parse URL %s\n", rawUrl) + log.Printf("E! Can't parse URL %s\n", rawURL) } v.endpoints[i] = NewEndpoint(v, u) } } +// Gather is the main data collection function called by the Telegraf core. It performs all +// the data collection and writes all metrics into the Accumulator passed as an argument. func (v *VSphere) Gather(acc telegraf.Accumulator) error { v.checkEndpoints() diff --git a/plugins/inputs/vsphere/vsphere_test.go b/plugins/inputs/vsphere/vsphere_test.go index 16d911f8b157c..a9c42f29d2704 100644 --- a/plugins/inputs/vsphere/vsphere_test.go +++ b/plugins/inputs/vsphere/vsphere_test.go @@ -3,25 +3,27 @@ package vsphere import ( "crypto/tls" "fmt" + "testing" + "time" + "github.com/influxdata/telegraf/internal" + itls "github.com/influxdata/telegraf/internal/tls" "github.com/influxdata/telegraf/testutil" "github.com/stretchr/testify/require" "github.com/vmware/govmomi/simulator" - "testing" - "time" ) func defaultVSphere() *VSphere { return &VSphere{ - GatherClusters: true, - ClusterMetrics: nil, - GatherHosts: true, - HostMetrics: nil, - GatherVms: true, - VmMetrics: nil, - GatherDatastores: true, - DatastoreMetrics: nil, - InsecureSkipVerify: true, + GatherClusters: true, + ClusterMetrics: nil, + GatherHosts: true, + HostMetrics: nil, + GatherVms: true, + VmMetrics: nil, + GatherDatastores: true, + DatastoreMetrics: nil, + ClientConfig: itls.ClientConfig{InsecureSkipVerify: true}, ObjectsPerQuery: 256, ObjectDiscoveryInterval: internal.Duration{Duration: time.Second * 300}, From 265fce0bb1391726db051564b0e3edac15db81eb Mon Sep 17 00:00:00 2001 From: prydin Date: Mon, 14 May 2018 16:01:50 -0400 Subject: [PATCH 055/100] Safer handling of mutexes Minor debug messsage cleanups --- plugins/inputs/vsphere/endpoint.go | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/plugins/inputs/vsphere/endpoint.go b/plugins/inputs/vsphere/endpoint.go index 95ab546042d12..bfd1e06cebd18 100644 --- a/plugins/inputs/vsphere/endpoint.go +++ b/plugins/inputs/vsphere/endpoint.go @@ -25,8 +25,8 @@ type Endpoint struct { nameCache map[string]string resources map[string]resource discoveryTicker *time.Ticker - clientMux *sync.Mutex - collectMux *sync.RWMutex + clientMux sync.Mutex + collectMux sync.RWMutex initialized bool } @@ -57,8 +57,6 @@ func NewEndpoint(parent *VSphere, url *url.URL) *Endpoint { Parent: parent, lastColls: make(map[string]time.Time), nameCache: make(map[string]string), - clientMux: &sync.Mutex{}, - collectMux: &sync.RWMutex{}, initialized: false, } @@ -230,9 +228,6 @@ func (e *Endpoint) discover() error { // Atomically swap maps // - if e.collectMux == nil { - e.collectMux = &sync.RWMutex{} - } e.collectMux.Lock() defer e.collectMux.Unlock() From ad003a9c67ca62211c1710b8ca196053f7da18bd Mon Sep 17 00:00:00 2001 From: prydin Date: Tue, 15 May 2018 10:19:47 -0400 Subject: [PATCH 056/100] Cleaned up credentials handling --- plugins/inputs/vsphere/client.go | 22 +++++++++++++--------- plugins/inputs/vsphere/vsphere.go | 5 +++-- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/plugins/inputs/vsphere/client.go b/plugins/inputs/vsphere/client.go index e6944ed73490a..3765294271074 100644 --- a/plugins/inputs/vsphere/client.go +++ b/plugins/inputs/vsphere/client.go @@ -2,6 +2,7 @@ package vsphere import ( "context" + "fmt" "log" "net/url" @@ -19,29 +20,32 @@ type Client struct { } // NewClient creates a new vSphere client based on the url and setting passed as parameters. -func NewClient(url *url.URL, vs *VSphere) (*Client, error) { +func NewClient(u *url.URL, vs *VSphere) (*Client, error) { tlsCfg, err := vs.TLSConfig() if err != nil { return nil, err } + if vs.Username != "" { + if vs.Password == "" { + return nil, fmt.Errorf("vSphere password must be specified") + } + log.Printf("D! Logging in using explicit credentials: %s", vs.Username) + u.User = url.UserPassword(vs.Username, vs.Password) + } ctx := context.Background() - var c *govmomi.Client if tlsCfg != nil && len(tlsCfg.Certificates) > 0 { - //TODO: remove this log output before final release - log.Printf("Creating client with Certificate: %s", url.Host) - c, err = govmomi.NewClientWithCertificate(ctx, url, vs.InsecureSkipVerify, tlsCfg.Certificates[0]) + log.Printf("D! Creating client with Certificate: %s", u.Host) + c, err = govmomi.NewClientWithCertificate(ctx, u, vs.InsecureSkipVerify, tlsCfg.Certificates[0]) } else { - //TODO: remove this log output before final release - log.Printf("Creating client: %s", url.Host) - c, err = govmomi.NewClient(ctx, url, vs.InsecureSkipVerify) + log.Printf("D! Creating client: %s", u.Host) + c, err = govmomi.NewClient(ctx, u, vs.InsecureSkipVerify) } if err != nil { return nil, err } c.Timeout = vs.Timeout.Duration - m := view.NewManager(c.Client) v, err := m.CreateContainerView(ctx, c.ServiceContent.RootFolder, []string{}, true) diff --git a/plugins/inputs/vsphere/vsphere.go b/plugins/inputs/vsphere/vsphere.go index 8e3db4b46b58a..8e758f97441ce 100644 --- a/plugins/inputs/vsphere/vsphere.go +++ b/plugins/inputs/vsphere/vsphere.go @@ -15,8 +15,9 @@ import ( // VSphere is the top level type for the vSphere input plugin. It contains all the configuration // and a list of connected vSphere endpoints type VSphere struct { - Vcenters []string - + Vcenters []string + Username string + Password string GatherClusters bool ClusterMetrics []string GatherHosts bool From ca83bcc2b0644089a0c7f8e4860d5da42b901bce Mon Sep 17 00:00:00 2001 From: prydin Date: Wed, 16 May 2018 16:04:40 -0400 Subject: [PATCH 057/100] Rewrote metric include/exclude functionality to look more Telegraf-like. May be a bit faster too. --- plugins/inputs/vsphere/README.MD | 114 +++++++++++++--- plugins/inputs/vsphere/endpoint.go | 207 ++++++++++++++++------------- plugins/inputs/vsphere/vsphere.go | 164 ++++++++++++++++++++--- 3 files changed, 351 insertions(+), 134 deletions(-) diff --git a/plugins/inputs/vsphere/README.MD b/plugins/inputs/vsphere/README.MD index fb502befdd9f4..c93decca92291 100644 --- a/plugins/inputs/vsphere/README.MD +++ b/plugins/inputs/vsphere/README.MD @@ -12,29 +12,105 @@ The VMware vSphere plugin uses the vSphere API to gather metrics from multiple v ``` # Read metrics from one or many vCenters [[inputs.vsphere]] - ## List of vCenter URLs, including credentials. - ## ://:@/sdk - ## Note the "@" characted must be escaped as %40 - ## e.g. - ## https://administrator%40vsphere.local:password@vcenter.local/sdk - # - vcenters = [ "https://administrator%40vsphere.local:password@vcenter.local/sdk" ] + ## List of vCenter URLs to be monitored. + # vcenters = [ "https://vcenter.local/sdk" ] + # username = "user@corp.local" + # password = "secret" + + + ############### VMs ############### - ## gather metrics from Clusters (default: true) - # gather_clusters = true - # cluster_metrics = [] + # gather_vms = true # (default=true) + + # Typical VM metrics (if omitted, all metrics are collected) + # vm_metric_include = [ + # "cpu.ready.summation.delta.millisecond", + # "mem.swapinRate.average.rate.kiloBytesPerSecond", + # "virtualDisk.numberReadAveraged.average.rate.number", + # "virtualDisk.numberWriteAveraged.average.rate.number", + # "virtualDisk.totalReadLatency.average.absolute.millisecond", + # "virtualDisk.totalWriteLatency.average.absolute.millisecond", + # "virtualDisk.readOIO.latest.absolute.number", + # "virtualDisk.writeOIO.latest.absolute.number", + # "net.bytesRx.average.rate.kiloBytesPerSecond", + # "net.bytesTx.average.rate.kiloBytesPerSecond", + # "net.droppedRx.summation.delta.number", + # "net.droppedTx.summation.delta.number", + # "cpu.run.summation.delta.millisecond", + # "cpu.used.summation.delta.millisecond", + # "mem.swapoutRate.average.rate.kiloBytesPerSecond", + # "virtualDisk.read.average.rate.kiloBytesPerSecond", + # "virtualDisk.write.average.rate.kiloBytesPerSecond" ] + + # vm_metric_exclude [] + + ############### Hosts ############### + + # gather_hosts = true # (default=true) - ## gather metrics from Hosts (default: true) - # gather_hosts = true - # host_metrics = [] + ## Typical host metrics (if omitted, all metrics are collected) + # host_metric_include = [ + # "cpu.ready.summation.delta.millisecond", + # "cpu.latency.average.rate.percent", + # "cpu.coreUtilization.average.rate.percent", + # "mem.usage.average.absolute.percent", + # "mem.swapinRate.average.rate.kiloBytesPerSecond", + # "mem.state.latest.absolute.number", + # "mem.latency.average.absolute.percent", + # "mem.vmmemctl.average.absolute.kiloBytes", + # "disk.read.average.rate.kiloBytesPerSecond", + # "disk.write.average.rate.kiloBytesPerSecond", + # "disk.numberReadAveraged.average.rate.number", + # "disk.numberWriteAveraged.average.rate.number", + # "disk.deviceReadLatency.average.absolute.millisecond", + # "disk.deviceWriteLatency.average.absolute.millisecond", + # "disk.totalReadLatency.average.absolute.millisecond", + # "disk.totalWriteLatency.average.absolute.millisecond", + # "storageAdapter.read.average.rate.kiloBytesPerSecond", + # "storageAdapter.write.average.rate.kiloBytesPerSecond", + # "storageAdapter.numberReadAveraged.average.rate.number", + # "storageAdapter.numberWriteAveraged.average.rate.number", + # "net.errorsRx.summation.delta.number", + # "net.errorsTx.summation.delta.number", + # "net.bytesRx.average.rate.kiloBytesPerSecond", + # "net.bytesTx.average.rate.kiloBytesPerSecond", + # "cpu.used.summation.delta.millisecond", + # "cpu.usage.average.rate.percent", + # "cpu.utilization.average.rate.percent", + # "cpu.wait.summation.delta.millisecond", + # "cpu.idle.summation.delta.millisecond", + # "cpu.readiness.average.rate.percent", + # "cpu.costop.summation.delta.millisecond", + # "cpu.swapwait.summation.delta.millisecond", + # "mem.swapoutRate.average.rate.kiloBytesPerSecond", + # "disk.kernelReadLatency.average.absolute.millisecond", + # "disk.kernelWriteLatency.average.absolute.millisecond" ] + + # host_metric_exclude = [] # Nothing excluded by default + + ############### Clusters ############### + + # gather_clusters = true # (default=true) - ## gather metrics from VMs (default: true) - # gather_vms = true - # vm_metrics = [] + ## Typical cluster metrics (if omitted, all metrics are collected) + # cluster_metric_include = [ + # "cpu.usage.*", + # "cpu.usagemhz.*", + # "mem.usage.*", + # "mem.active.*" ] + + # cluster_metric_exclude [] # Nothing excluded by default + + ############### Datastores ############### + + # gather_datastore = true # (default=true) - ## gather metrics from Data stores (default: true) - # gather_datastores = true - # datastore_metrics = [] + ## Typical datastore metrics (if omitted, all metrics are collected) + # datastore_metric_include = [ + # "disk.used.*", + # "disk.provsioned.*" ] + + # storage_metric_exclude = [] # Nothing excluded by default ## number of objects to retreive per query. set to 64 for vCenter 5.5 and 6.0 (default: 256) # objects_per_query = 256 diff --git a/plugins/inputs/vsphere/endpoint.go b/plugins/inputs/vsphere/endpoint.go index bfd1e06cebd18..67e3ac7de4c02 100644 --- a/plugins/inputs/vsphere/endpoint.go +++ b/plugins/inputs/vsphere/endpoint.go @@ -10,6 +10,7 @@ import ( "github.com/gobwas/glob" "github.com/influxdata/telegraf" + "github.com/vmware/govmomi/performance" "github.com/vmware/govmomi/view" "github.com/vmware/govmomi/vim25/mo" "github.com/vmware/govmomi/vim25/types" @@ -22,8 +23,9 @@ type Endpoint struct { URL *url.URL client *Client lastColls map[string]time.Time - nameCache map[string]string + instanceInfo map[string]resourceInfo resources map[string]resource + metricNames map[int32]string discoveryTicker *time.Ticker clientMux sync.Mutex collectMux sync.RWMutex @@ -35,8 +37,8 @@ type resource struct { realTime bool sampling int32 objects objectMap - metricIds []types.PerfMetricId - wildcards []string + includes []string + excludes []string getObjects func(*view.ContainerView) (objectMap, error) } @@ -49,15 +51,20 @@ type objectRef struct { guest string } +type resourceInfo struct { + name string + metrics performance.MetricList +} + // NewEndpoint returns a new connection to a vCenter based on the URL and configuration passed // as parameters. func NewEndpoint(parent *VSphere, url *url.URL) *Endpoint { e := Endpoint{ - URL: url, - Parent: parent, - lastColls: make(map[string]time.Time), - nameCache: make(map[string]string), - initialized: false, + URL: url, + Parent: parent, + lastColls: make(map[string]time.Time), + instanceInfo: make(map[string]resourceInfo), + initialized: false, } e.resources = map[string]resource{ @@ -66,7 +73,8 @@ func NewEndpoint(parent *VSphere, url *url.URL) *Endpoint { realTime: false, sampling: 300, objects: make(objectMap), - wildcards: parent.ClusterMetrics, + includes: parent.ClusterMetricInclude, + excludes: parent.ClusterMetricExclude, getObjects: getClusters, }, "host": { @@ -74,7 +82,8 @@ func NewEndpoint(parent *VSphere, url *url.URL) *Endpoint { realTime: true, sampling: 20, objects: make(objectMap), - wildcards: parent.HostMetrics, + includes: parent.HostMetricInclude, + excludes: parent.HostMetricExclude, getObjects: getHosts, }, "vm": { @@ -82,7 +91,8 @@ func NewEndpoint(parent *VSphere, url *url.URL) *Endpoint { realTime: true, sampling: 20, objects: make(objectMap), - wildcards: parent.VmMetrics, + includes: parent.VmMetricInclude, + excludes: parent.VmMetricExclude, getObjects: getVMs, }, "datastore": { @@ -90,7 +100,8 @@ func NewEndpoint(parent *VSphere, url *url.URL) *Endpoint { realTime: false, sampling: 300, objects: make(objectMap), - wildcards: parent.DatastoreMetrics, + includes: parent.DatastoreMetricInclude, + excludes: parent.DatastoreMetricExclude, getObjects: getDatastores, }, } @@ -138,64 +149,19 @@ func (e *Endpoint) setupMetricIds() error { return err } ctx := context.Background() - - metricMap, err := client.Perf.CounterInfoByName(ctx) + mn, err := client.Perf.CounterInfoByName(ctx) if err != nil { return err } - - for k, res := range e.resources { - if res.enabled { - res.metricIds, err = resolveMetricWildcards(metricMap, res.wildcards) - if err != nil { - return err - } - e.resources[k] = res - } + e.metricNames = make(map[int32]string) + for name, m := range mn { + e.metricNames[m.Key] = name } - return nil } -func resolveMetricWildcards(metricMap map[string]*types.PerfCounterInfo, wildcards []string) ([]types.PerfMetricId, error) { - // Nothing specified assumes we're looking at everything - // - if wildcards == nil || len(wildcards) == 0 { - return nil, nil - } - tmpMap := make(map[string]types.PerfMetricId) - for _, pattern := range wildcards { - exclude := false - if pattern[0] == '!' { - pattern = pattern[1:] - exclude = true - } - p, err := glob.Compile(pattern) - if err != nil { - return nil, err - } - for name, info := range metricMap { - if p.Match(name) { - if exclude { - delete(tmpMap, name) - log.Printf("D! excluded %s", name) - } else { - tmpMap[name] = types.PerfMetricId{CounterId: info.Key} - log.Printf("D! included %s", name) - } - } - } - } - result := make([]types.PerfMetricId, len(tmpMap)) - idx := 0 - for _, id := range tmpMap { - result[idx] = id - idx++ - } - return result, nil -} - func (e *Endpoint) discover() error { + start := time.Now() log.Printf("D! Discover new objects for %s", e.URL.Host) client, err := e.getClient() @@ -203,12 +169,24 @@ func (e *Endpoint) discover() error { return err } - nameCache := make(map[string]string) + instInfo := make(map[string]resourceInfo) resources := e.resources // Populate resource objects, and endpoint name cache // for k, res := range resources { + // Don't be tempted to skip disabled resources here! We may need them to resolve parent references + // + // Precompile includes and excludes + // + cInc := make([]glob.Glob, len(res.includes)) + for i, p := range res.includes { + cInc[i] = glob.MustCompile(p) + } + cExc := make([]glob.Glob, len(res.excludes)) + for i, p := range res.excludes { + cExc[i] = glob.MustCompile(p) + } // Need to do this for all resource types even if they are not enabled (but datastore) // if res.enabled || k != "datastore" { @@ -217,9 +195,46 @@ func (e *Endpoint) discover() error { e.checkClient() return err } - for _, obj := range objects { - nameCache[obj.ref.Value] = obj.name + ctx := context.Background() + mList := make(performance.MetricList, 0) + metrics, err := e.client.Perf.AvailableMetric(ctx, obj.ref.Reference(), res.sampling) + if err != nil { + e.checkClient() + return err + } + + // Mmetric metadata gathering is only needed for enabled resource types. + // + if res.enabled { + for _, m := range metrics { + include := len(cInc) == 0 // Empty include list means include all + mName := e.metricNames[m.CounterId] + if !include { + for _, p := range cInc { + if p.Match(mName) { + include = true + break + } + } + } + if include { + for _, p := range cExc { + if p.Match(mName) { + include = false + log.Printf("D! Excluded: %s", mName) + break + } + } + if include { + mList = append(mList, m) + log.Printf("D! Included %s Sampling: %d", mName, res.sampling) + } + } + + } + } + instInfo[obj.ref.Value] = resourceInfo{name: obj.name, metrics: mList} } res.objects = objects resources[k] = res @@ -231,10 +246,10 @@ func (e *Endpoint) discover() error { e.collectMux.Lock() defer e.collectMux.Unlock() - e.nameCache = nameCache + e.instanceInfo = instInfo e.resources = resources - log.Printf("D! Discovered %d objects for %s", len(nameCache), e.URL.Host) + log.Printf("D! Discovered %d objects for %s. Took %s", len(instInfo), e.URL.Host, time.Now().Sub(start)) return nil } @@ -349,24 +364,19 @@ func (e *Endpoint) collectResource(resourceType string, acc telegraf.Accumulator latest, hasLatest := e.lastColls[resourceType] if hasLatest { elapsed := time.Now().Sub(latest).Seconds() + log.Printf("D! Latest: %s, elapsed: %f, resource: %s", latest, elapsed, resourceType) if elapsed < float64(res.sampling) { // No new data would be available. We're outta here! // log.Printf("D! Sampling period for %s of %d has not elapsed for %s", resourceType, res.sampling, e.URL.Host) return 0, 0, nil } - } - - if !hasLatest { + } else { latest = now.Add(-time.Duration(res.sampling) * time.Second) e.lastColls[resourceType] = latest } - if len(res.metricIds) == 0 { - log.Printf("D! Collecting all metrics for %d objects of type %s for %s", len(res.objects), resourceType, e.URL.Host) - } else { - log.Printf("D! Collecting %d metrics for %d objects of type %s for %s", len(res.metricIds), len(res.objects), resourceType, e.URL.Host) - } + log.Printf("D! Collecting metrics for %d objects of type %s for %s", len(res.objects), resourceType, e.URL.Host) client, err := e.getClient() if err != nil { @@ -386,10 +396,14 @@ func (e *Endpoint) collectResource(resourceType string, acc telegraf.Accumulator lastTS := latest pqs := make([]types.PerfQuerySpec, 0, e.Parent.ObjectsPerQuery) for _, object := range res.objects { + info, found := e.instanceInfo[object.ref.Value] + if !found { + log.Printf("E! Internal error: Instance info not found for MOID %s", object.ref) + } pq := types.PerfQuerySpec{ Entity: object.ref, MaxSample: 1, - MetricId: res.metricIds, + MetricId: info.metrics, IntervalId: res.sampling, } @@ -426,34 +440,39 @@ func (e *Endpoint) collectResource(resourceType string, acc telegraf.Accumulator for _, v := range em.Value { name := v.Name for idx, value := range v.Value { - - objectName := e.nameCache[moid] + instInfo, found := e.instanceInfo[moid] + if !found { + log.Printf("E! MOID % not found in cache. Skipping!", moid) + continue + } t := map[string]string{ "vcenter": e.URL.Host, - "hostname": objectName, + "hostname": instInfo.name, "moid": moid, } objectRef, ok := res.objects[moid] if ok { - parent := e.nameCache[objectRef.parentRef.Value] - switch resourceType { - case "host": - t["cluster"] = parent - break - - case "vm": - t["guest"] = objectRef.guest - t["esxhost"] = parent - hostRes := e.resources["host"] - hostRef, ok := hostRes.objects[objectRef.parentRef.Value] - if ok { - cluster, ok := e.nameCache[hostRef.parentRef.Value] + parent, found := e.instanceInfo[objectRef.parentRef.Value] + if found { + switch resourceType { + case "host": + t["cluster"] = parent.name + break + + case "vm": + t["guest"] = objectRef.guest + t["esxhost"] = parent.name + hostRes := e.resources["host"] + hostRef, ok := hostRes.objects[objectRef.parentRef.Value] if ok { - t["cluster"] = cluster + cluster, ok := e.instanceInfo[hostRef.parentRef.Value] + if ok { + t["cluster"] = cluster.name + } } + break } - break } } diff --git a/plugins/inputs/vsphere/vsphere.go b/plugins/inputs/vsphere/vsphere.go index 8e758f97441ce..9bd24d23b4e09 100644 --- a/plugins/inputs/vsphere/vsphere.go +++ b/plugins/inputs/vsphere/vsphere.go @@ -15,17 +15,21 @@ import ( // VSphere is the top level type for the vSphere input plugin. It contains all the configuration // and a list of connected vSphere endpoints type VSphere struct { - Vcenters []string - Username string - Password string - GatherClusters bool - ClusterMetrics []string - GatherHosts bool - HostMetrics []string - GatherVms bool - VmMetrics []string - GatherDatastores bool - DatastoreMetrics []string + Vcenters []string + Username string + Password string + GatherClusters bool + ClusterMetricInclude []string + ClusterMetricExclude []string + GatherHosts bool + HostMetricInclude []string + HostMetricExclude []string + GatherVms bool + VmMetricInclude []string + VmMetricExclude []string + GatherDatastores bool + DatastoreMetricInclude []string + DatastoreMetricExclude []string ObjectsPerQuery int32 ObjectDiscoveryInterval internal.Duration @@ -38,8 +42,122 @@ type VSphere struct { } var sampleConfig = ` - ## List of vCenter URLs, including credentials. Note the "@" characted must be escaped as %40 - # vcenters = [ "https://administrator%40vsphere.local:VMware1!@vcenter.local/sdk" ] +## List of vCenter URLs to be monitored. These three lines must be uncommented +## and edited for the plugin to work. +# vcenters = [ "https://vcenter.local/sdk" ] +# username = "user@corp.local" +# password = "secret" + + +############### VMs ############### + +# gather_vms = true # (default=true) + +# Typical VM metrics (if omitted, all metrics are collected) +# vm_metric_include = [ +# "cpu.ready.summation.delta.millisecond", +# "mem.swapinRate.average.rate.kiloBytesPerSecond", +# "virtualDisk.numberReadAveraged.average.rate.number", +# "virtualDisk.numberWriteAveraged.average.rate.number", +# "virtualDisk.totalReadLatency.average.absolute.millisecond", +# "virtualDisk.totalWriteLatency.average.absolute.millisecond", +# "virtualDisk.readOIO.latest.absolute.number", +# "virtualDisk.writeOIO.latest.absolute.number", +# "net.bytesRx.average.rate.kiloBytesPerSecond", +# "net.bytesTx.average.rate.kiloBytesPerSecond", +# "net.droppedRx.summation.delta.number", +# "net.droppedTx.summation.delta.number", +# "cpu.run.summation.delta.millisecond", +# "cpu.used.summation.delta.millisecond", +# "mem.swapoutRate.average.rate.kiloBytesPerSecond", +# "virtualDisk.read.average.rate.kiloBytesPerSecond", +# "virtualDisk.write.average.rate.kiloBytesPerSecond" ] + +# vm_metric_exclude [] + +############### Hosts ############### + +# gather_hosts = true # (default=true) + +## Typical host metrics (if omitted, all metrics are collected) +# host_metric_include = [ +# "cpu.ready.summation.delta.millisecond", +# "cpu.latency.average.rate.percent", +# "cpu.coreUtilization.average.rate.percent", +# "mem.usage.average.absolute.percent", +# "mem.swapinRate.average.rate.kiloBytesPerSecond", +# "mem.state.latest.absolute.number", +# "mem.latency.average.absolute.percent", +# "mem.vmmemctl.average.absolute.kiloBytes", +# "disk.read.average.rate.kiloBytesPerSecond", +# "disk.write.average.rate.kiloBytesPerSecond", +# "disk.numberReadAveraged.average.rate.number", +# "disk.numberWriteAveraged.average.rate.number", +# "disk.deviceReadLatency.average.absolute.millisecond", +# "disk.deviceWriteLatency.average.absolute.millisecond", +# "disk.totalReadLatency.average.absolute.millisecond", +# "disk.totalWriteLatency.average.absolute.millisecond", +# "storageAdapter.read.average.rate.kiloBytesPerSecond", +# "storageAdapter.write.average.rate.kiloBytesPerSecond", +# "storageAdapter.numberReadAveraged.average.rate.number", +# "storageAdapter.numberWriteAveraged.average.rate.number", +# "net.errorsRx.summation.delta.number", +# "net.errorsTx.summation.delta.number", +# "net.bytesRx.average.rate.kiloBytesPerSecond", +# "net.bytesTx.average.rate.kiloBytesPerSecond", +# "cpu.used.summation.delta.millisecond", +# "cpu.usage.average.rate.percent", +# "cpu.utilization.average.rate.percent", +# "cpu.wait.summation.delta.millisecond", +# "cpu.idle.summation.delta.millisecond", +# "cpu.readiness.average.rate.percent", +# "cpu.costop.summation.delta.millisecond", +# "cpu.swapwait.summation.delta.millisecond", +# "mem.swapoutRate.average.rate.kiloBytesPerSecond", +# "disk.kernelReadLatency.average.absolute.millisecond", +# "disk.kernelWriteLatency.average.absolute.millisecond" ] + +# host_metric_exclude = [] # Nothing excluded by default + +############### Clusters ############### + +# gather_clusters = true # (default=true) + +## Typical cluster metrics (if omitted, all metrics are collected) +# cluster_metric_include = [ +# "cpu.usage.*", +# "cpu.usagemhz.*", +# "mem.usage.*", +# "mem.active.*" ] + +# cluster_metric_exclude [] # Nothing excluded by default + +############### Datastores ############### + +# gather_datastore = true # (default=true) + +## Typical datastore metrics (if omitted, all metrics are collected) +# datastore_metric_include = [ +# "disk.used.*", +# "disk.provsioned.*" ] + +# storage_metric_exclude = [] # Nothing excluded by default + +## number of objects to retreive per query. set to 64 for vCenter 5.5 and 6.0 (default: 256) +# objects_per_query = 256 + +## the interval before (re)discovering objects subject to metrics collection (default: 300s) +# object_discovery_interval = "300s" + +## timeout applies to any of the connection request made to vcenter +# timeout = "20s" + +## Optional SSL Config +# ssl_ca = /path/to/cafile +# ssl_cert = /path/to/certfile +# ssl_key = /path/to/keyfile +## Use SSL but skip chain & host verification +# insecure_skip_verify = false ` // SampleConfig returns a set of default configuration to be used as a boilerplate when setting up @@ -95,14 +213,18 @@ func init() { return &VSphere{ Vcenters: []string{}, - GatherClusters: true, - ClusterMetrics: nil, - GatherHosts: true, - HostMetrics: nil, - GatherVms: true, - VmMetrics: nil, - GatherDatastores: true, - DatastoreMetrics: nil, + GatherClusters: true, + ClusterMetricInclude: nil, + ClusterMetricExclude: nil, + GatherHosts: true, + HostMetricInclude: nil, + HostMetricExclude: nil, + GatherVms: true, + VmMetricInclude: nil, + VmMetricExclude: nil, + GatherDatastores: true, + DatastoreMetricInclude: nil, + DatastoreMetricExclude: nil, ObjectsPerQuery: 256, ObjectDiscoveryInterval: internal.Duration{Duration: time.Second * 300}, From 90afda271b6586238f3b886e42c1ca5884ba9e4d Mon Sep 17 00:00:00 2001 From: prydin Date: Thu, 17 May 2018 13:56:27 -0400 Subject: [PATCH 058/100] Fixed some vet issues --- plugins/inputs/vsphere/client.go | 1 - plugins/inputs/vsphere/endpoint.go | 44 ++++--- plugins/inputs/vsphere/vsphere.go | 133 ++++++++++--------- plugins/inputs/vsphere/vsphere_test.go | 172 +++++++++++++++++++++++-- 4 files changed, 255 insertions(+), 95 deletions(-) diff --git a/plugins/inputs/vsphere/client.go b/plugins/inputs/vsphere/client.go index 3765294271074..0f0bc3e6458e3 100644 --- a/plugins/inputs/vsphere/client.go +++ b/plugins/inputs/vsphere/client.go @@ -25,7 +25,6 @@ func NewClient(u *url.URL, vs *VSphere) (*Client, error) { if err != nil { return nil, err } - if vs.Username != "" { if vs.Password == "" { return nil, fmt.Errorf("vSphere password must be specified") diff --git a/plugins/inputs/vsphere/endpoint.go b/plugins/inputs/vsphere/endpoint.go index 67e3ac7de4c02..a63e49aba0cc0 100644 --- a/plugins/inputs/vsphere/endpoint.go +++ b/plugins/inputs/vsphere/endpoint.go @@ -203,6 +203,7 @@ func (e *Endpoint) discover() error { e.checkClient() return err } + log.Printf("D! Obj: %s, metrics found: %d, enabled: %t", obj.name, len(metrics), res.enabled) // Mmetric metadata gathering is only needed for enabled resource types. // @@ -210,11 +211,11 @@ func (e *Endpoint) discover() error { for _, m := range metrics { include := len(cInc) == 0 // Empty include list means include all mName := e.metricNames[m.CounterId] - if !include { + //log.Printf("% %s", mName, cInc) + if !include { // If not included by default for _, p := range cInc { if p.Match(mName) { include = true - break } } } @@ -226,10 +227,8 @@ func (e *Endpoint) discover() error { break } } - if include { - mList = append(mList, m) - log.Printf("D! Included %s Sampling: %d", mName, res.sampling) - } + mList = append(mList, m) + log.Printf("D! Included %s Sampling: %d", mName, res.sampling) } } @@ -400,24 +399,31 @@ func (e *Endpoint) collectResource(resourceType string, acc telegraf.Accumulator if !found { log.Printf("E! Internal error: Instance info not found for MOID %s", object.ref) } - pq := types.PerfQuerySpec{ - Entity: object.ref, - MaxSample: 1, - MetricId: info.metrics, - IntervalId: res.sampling, - } + if len(info.metrics) > 0 { - if !res.realTime { - pq.StartTime = &latest - pq.EndTime = &now - } + pq := types.PerfQuerySpec{ + Entity: object.ref, + MaxSample: 1, + MetricId: info.metrics, + IntervalId: res.sampling, + } - pqs = append(pqs, pq) + if !res.realTime { + pq.StartTime = &latest + pq.EndTime = &now + } + + pqs = append(pqs, pq) + } else { + log.Printf("D! No metrics available for %s. Skipping.", info.name) + // Maintainers: Don't be tempted to skip a turn in the loop here! We still need to check if + // the chunk needs processing or we risk skipping the last chunk. (Ask me how I found this out... :) ) + } total++ // Filled up a chunk or at end of data? Run a query with the collected objects // - if len(pqs) >= int(e.Parent.ObjectsPerQuery) || total == len(res.objects) { + if len(pqs) >= int(e.Parent.ObjectsPerQuery) || total >= len(res.objects) { log.Printf("D! Querying %d objects of type %s for %s. Object count: %d. Total objects %d", len(pqs), resourceType, e.URL.Host, total, len(res.objects)) metrics, err := client.Perf.Query(ctx, pqs) if err != nil { @@ -442,7 +448,7 @@ func (e *Endpoint) collectResource(resourceType string, acc telegraf.Accumulator for idx, value := range v.Value { instInfo, found := e.instanceInfo[moid] if !found { - log.Printf("E! MOID % not found in cache. Skipping!", moid) + log.Printf("E! MOID %s not found in cache. Skipping!", moid) continue } t := map[string]string{ diff --git a/plugins/inputs/vsphere/vsphere.go b/plugins/inputs/vsphere/vsphere.go index 9bd24d23b4e09..03df287304afa 100644 --- a/plugins/inputs/vsphere/vsphere.go +++ b/plugins/inputs/vsphere/vsphere.go @@ -48,80 +48,79 @@ var sampleConfig = ` # username = "user@corp.local" # password = "secret" - ############### VMs ############### -# gather_vms = true # (default=true) +# gather_vms = true ### (default=true) -# Typical VM metrics (if omitted, all metrics are collected) +## Typical VM metrics (if omitted, all metrics are collected) # vm_metric_include = [ -# "cpu.ready.summation.delta.millisecond", -# "mem.swapinRate.average.rate.kiloBytesPerSecond", -# "virtualDisk.numberReadAveraged.average.rate.number", -# "virtualDisk.numberWriteAveraged.average.rate.number", -# "virtualDisk.totalReadLatency.average.absolute.millisecond", -# "virtualDisk.totalWriteLatency.average.absolute.millisecond", -# "virtualDisk.readOIO.latest.absolute.number", -# "virtualDisk.writeOIO.latest.absolute.number", -# "net.bytesRx.average.rate.kiloBytesPerSecond", -# "net.bytesTx.average.rate.kiloBytesPerSecond", -# "net.droppedRx.summation.delta.number", -# "net.droppedTx.summation.delta.number", -# "cpu.run.summation.delta.millisecond", -# "cpu.used.summation.delta.millisecond", -# "mem.swapoutRate.average.rate.kiloBytesPerSecond", -# "virtualDisk.read.average.rate.kiloBytesPerSecond", -# "virtualDisk.write.average.rate.kiloBytesPerSecond" ] - -# vm_metric_exclude [] +# "cpu.ready.summation", +# "mem.swapinRate.average", +# "virtualDisk.numberReadAveraged.average", +# "virtualDisk.numberWriteAveraged.average", +# "virtualDisk.totalReadLatency.average", +# "virtualDisk.totalWriteLatency.average", +# "virtualDisk.readOIO.latest", +# "virtualDisk.writeOIO.latest", +# "net.bytesRx.average", +# "net.bytesTx.average", +# "net.droppedRx.summation.", +# "net.droppedTx.summation.", +# "cpu.run.summation", +# "cpu.used.summation", +# "mem.swapoutRate.average", +# "virtualDisk.read.average", +# "virtualDisk.write.average" ] + +# vm_metric_exclude = [] ## Nothing is excluded by default ############### Hosts ############### -# gather_hosts = true # (default=true) +# gather_hosts = true ## (default=true) ## Typical host metrics (if omitted, all metrics are collected) # host_metric_include = [ -# "cpu.ready.summation.delta.millisecond", -# "cpu.latency.average.rate.percent", -# "cpu.coreUtilization.average.rate.percent", -# "mem.usage.average.absolute.percent", -# "mem.swapinRate.average.rate.kiloBytesPerSecond", -# "mem.state.latest.absolute.number", -# "mem.latency.average.absolute.percent", -# "mem.vmmemctl.average.absolute.kiloBytes", -# "disk.read.average.rate.kiloBytesPerSecond", -# "disk.write.average.rate.kiloBytesPerSecond", -# "disk.numberReadAveraged.average.rate.number", -# "disk.numberWriteAveraged.average.rate.number", -# "disk.deviceReadLatency.average.absolute.millisecond", -# "disk.deviceWriteLatency.average.absolute.millisecond", -# "disk.totalReadLatency.average.absolute.millisecond", -# "disk.totalWriteLatency.average.absolute.millisecond", -# "storageAdapter.read.average.rate.kiloBytesPerSecond", -# "storageAdapter.write.average.rate.kiloBytesPerSecond", -# "storageAdapter.numberReadAveraged.average.rate.number", -# "storageAdapter.numberWriteAveraged.average.rate.number", -# "net.errorsRx.summation.delta.number", -# "net.errorsTx.summation.delta.number", -# "net.bytesRx.average.rate.kiloBytesPerSecond", -# "net.bytesTx.average.rate.kiloBytesPerSecond", -# "cpu.used.summation.delta.millisecond", -# "cpu.usage.average.rate.percent", -# "cpu.utilization.average.rate.percent", -# "cpu.wait.summation.delta.millisecond", -# "cpu.idle.summation.delta.millisecond", -# "cpu.readiness.average.rate.percent", -# "cpu.costop.summation.delta.millisecond", -# "cpu.swapwait.summation.delta.millisecond", -# "mem.swapoutRate.average.rate.kiloBytesPerSecond", -# "disk.kernelReadLatency.average.absolute.millisecond", -# "disk.kernelWriteLatency.average.absolute.millisecond" ] - -# host_metric_exclude = [] # Nothing excluded by default +# "cpu.ready.summation", +# "cpu.latency.average", +# "cpu.coreUtilization.average", +# "mem.usage.average", +# "mem.swapinRate.average", +# "mem.state.latest", +# "mem.latency.average", +# "mem.vmmemctl.average", +# "disk.read.average", +# "disk.write.average", +# "disk.numberReadAveraged.average", +# "disk.numberWriteAveraged.average", +# "disk.deviceReadLatency.average", +# "disk.deviceWriteLatency.average", +# "disk.totalReadLatency.average", +# "disk.totalWriteLatency.average", +# "storageAdapter.read.average", +# "storageAdapter.write.average", +# "storageAdapter.numberReadAveraged.average", +# "storageAdapter.numberWriteAveraged.average", +# "net.errorsRx.summation", +# "net.errorsTx.summation", +# "net.bytesRx.average", +# "net.bytesTx.average", +# "cpu.used.summation", +# "cpu.usage.average", +# "cpu.utilization.average", +# "cpu.wait.summation.", +# "cpu.idle.summation", +# "cpu.readiness.average", +# "cpu.costop.summation", +# "cpu.swapwait.summation", +# "mem.swapoutRate.average", +# "disk.kernelReadLatency.average", +# "disk.kernelWriteLatency.average" ] + +# host_metric_exclude = [] ## Nothing excluded by default ############### Clusters ############### -# gather_clusters = true # (default=true) +# gather_clusters = true ## (default=true) ## Typical cluster metrics (if omitted, all metrics are collected) # cluster_metric_include = [ @@ -130,18 +129,18 @@ var sampleConfig = ` # "mem.usage.*", # "mem.active.*" ] -# cluster_metric_exclude [] # Nothing excluded by default +# cluster_metric_exclude = [] ## Nothing excluded by default ############### Datastores ############### -# gather_datastore = true # (default=true) +# gather_datastore = true ### (default=true) ## Typical datastore metrics (if omitted, all metrics are collected) # datastore_metric_include = [ # "disk.used.*", # "disk.provsioned.*" ] -# storage_metric_exclude = [] # Nothing excluded by default +# storage_metric_exclude = [] ## Nothing excluded by default ## number of objects to retreive per query. set to 64 for vCenter 5.5 and 6.0 (default: 256) # objects_per_query = 256 @@ -153,9 +152,9 @@ var sampleConfig = ` # timeout = "20s" ## Optional SSL Config -# ssl_ca = /path/to/cafile -# ssl_cert = /path/to/certfile -# ssl_key = /path/to/keyfile +# ssl_ca = "/path/to/cafile" +# ssl_cert = "/path/to/certfile" +# ssl_key = "/path/to/keyfile" ## Use SSL but skip chain & host verification # insecure_skip_verify = false ` diff --git a/plugins/inputs/vsphere/vsphere_test.go b/plugins/inputs/vsphere/vsphere_test.go index a9c42f29d2704..e85d8e92b6148 100644 --- a/plugins/inputs/vsphere/vsphere_test.go +++ b/plugins/inputs/vsphere/vsphere_test.go @@ -3,27 +3,173 @@ package vsphere import ( "crypto/tls" "fmt" + "regexp" "testing" "time" "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/simulator" ) +var configHeader = ` +# Telegraf Configuration +# +# Telegraf is entirely plugin driven. All metrics are gathered from the +# declared inputs, and sent to the declared outputs. +# +# Plugins must be declared in here to be active. +# To deactivate a plugin, comment out the name and any variables. +# +# Use 'telegraf -config telegraf.conf -test' to see what metrics a config +# file would generate. +# +# Environment variables can be used anywhere in this config file, simply prepend +# them with $. For strings the variable must be within quotes (ie, "$STR_VAR"), +# for numbers and booleans they should be plain (ie, $INT_VAR, $BOOL_VAR) + + +# Global tags can be specified here in key="value" format. +[global_tags] + # dc = "us-east-1" # will tag all metrics with dc=us-east-1 + # rack = "1a" + ## Environment variables can be used as tags, and throughout the config file + # user = "$USER" + + +# Configuration for telegraf agent +[agent] + ## Default data collection interval for all inputs + interval = "10s" + ## Rounds collection interval to 'interval' + ## ie, if interval="10s" then always collect on :00, :10, :20, etc. + round_interval = true + + ## Telegraf will send metrics to outputs in batches of at most + ## metric_batch_size metrics. + ## This controls the size of writes that Telegraf sends to output plugins. + metric_batch_size = 1000 + + ## For failed writes, telegraf will cache metric_buffer_limit metrics for each + ## output, and will flush this buffer on a successful write. Oldest metrics + ## are dropped first when this buffer fills. + ## This buffer only fills when writes fail to output plugin(s). + metric_buffer_limit = 10000 + + ## Collection jitter is used to jitter the collection by a random amount. + ## Each plugin will sleep for a random time within jitter before collecting. + ## This can be used to avoid many plugins querying things like sysfs at the + ## same time, which can have a measurable effect on the system. + collection_jitter = "0s" + + ## Default flushing interval for all outputs. You shouldn't set this below + ## interval. Maximum flush_interval will be flush_interval + flush_jitter + flush_interval = "10s" + ## Jitter the flush interval by a random amount. This is primarily to avoid + ## large write spikes for users running a large number of telegraf instances. + ## ie, a jitter of 5s and interval 10s means flushes will happen every 10-15s + flush_jitter = "0s" + + ## By default or when set to "0s", precision will be set to the same + ## timestamp order as the collection interval, with the maximum being 1s. + ## ie, when interval = "10s", precision will be "1s" + ## when interval = "250ms", precision will be "1ms" + ## Precision will NOT be used for service inputs. It is up to each individual + ## service input to set the timestamp at the appropriate precision. + ## Valid time units are "ns", "us" (or "µs"), "ms", "s". + precision = "" + + ## Logging configuration: + ## Run telegraf with debug log messages. + debug = false + ## Run telegraf in quiet mode (error log messages only). + quiet = false + ## Specify the log file name. The empty string means to log to stderr. + logfile = "" + + ## Override default hostname, if empty use os.Hostname() + hostname = "" + ## If set to true, do no set the "host" tag in the telegraf agent. + omit_hostname = false + +` + func defaultVSphere() *VSphere { return &VSphere{ - GatherClusters: true, - ClusterMetrics: nil, - GatherHosts: true, - HostMetrics: nil, - GatherVms: true, - VmMetrics: nil, + GatherClusters: true, + ClusterMetricInclude: []string{ + "cpu.usage.*", + "cpu.usagemhz.*", + "mem.usage.*", + "mem.active.*"}, + ClusterMetricExclude: nil, + GatherHosts: true, + HostMetricInclude: []string{ + "cpu.ready.summation.delta.millisecond", + "cpu.latency.average.rate.percent", + "cpu.coreUtilization.average.rate.percent", + "mem.usage.average.absolute.percent", + "mem.swapinRate.average.rate.kiloBytesPerSecond", + "mem.state.latest.absolute.number", + "mem.latency.average.absolute.percent", + "mem.vmmemctl.average.absolute.kiloBytes", + "disk.read.average.rate.kiloBytesPerSecond", + "disk.write.average.rate.kiloBytesPerSecond", + "disk.numberReadAveraged.average.rate.number", + "disk.numberWriteAveraged.average.rate.number", + "disk.deviceReadLatency.average.absolute.millisecond", + "disk.deviceWriteLatency.average.absolute.millisecond", + "disk.totalReadLatency.average.absolute.millisecond", + "disk.totalWriteLatency.average.absolute.millisecond", + "storageAdapter.read.average.rate.kiloBytesPerSecond", + "storageAdapter.write.average.rate.kiloBytesPerSecond", + "storageAdapter.numberReadAveraged.average.rate.number", + "storageAdapter.numberWriteAveraged.average.rate.number", + "net.errorsRx.summation.delta.number", + "net.errorsTx.summation.delta.number", + "net.bytesRx.average.rate.kiloBytesPerSecond", + "net.bytesTx.average.rate.kiloBytesPerSecond", + "cpu.used.summation.delta.millisecond", + "cpu.usage.average.rate.percent", + "cpu.utilization.average.rate.percent", + "cpu.wait.summation.delta.millisecond", + "cpu.idle.summation.delta.millisecond", + "cpu.readiness.average.rate.percent", + "cpu.costop.summation.delta.millisecond", + "cpu.swapwait.summation.delta.millisecond", + "mem.swapoutRate.average.rate.kiloBytesPerSecond", + "disk.kernelReadLatency.average.absolute.millisecond", + "disk.kernelWriteLatency.average.absolute.millisecond"}, + HostMetricExclude: nil, + GatherVms: true, + VmMetricInclude: []string{ + "cpu.ready.summation.delta.millisecond", + "mem.swapinRate.average.rate.kiloBytesPerSecond", + "virtualDisk.numberReadAveraged.average.rate.number", + "virtualDisk.numberWriteAveraged.average.rate.number", + "virtualDisk.totalReadLatency.average.absolute.millisecond", + "virtualDisk.totalWriteLatency.average.absolute.millisecond", + "virtualDisk.readOIO.latest.absolute.number", + "virtualDisk.writeOIO.latest.absolute.number", + "net.bytesRx.average.rate.kiloBytesPerSecond", + "net.bytesTx.average.rate.kiloBytesPerSecond", + "net.droppedRx.summation.delta.number", + "net.droppedTx.summation.delta.number", + "cpu.run.summation.delta.millisecond", + "cpu.used.summation.delta.millisecond", + "mem.swapoutRate.average.rate.kiloBytesPerSecond", + "virtualDisk.read.average.rate.kiloBytesPerSecond", + "virtualDisk.write.average.rate.kiloBytesPerSecond"}, + VmMetricExclude: nil, GatherDatastores: true, - DatastoreMetrics: nil, - ClientConfig: itls.ClientConfig{InsecureSkipVerify: true}, + DatastoreMetricInclude: []string{ + "disk.used.*", + "disk.provsioned.*"}, + DatastoreMetricExclude: nil, + ClientConfig: itls.ClientConfig{InsecureSkipVerify: true}, ObjectsPerQuery: 256, ObjectDiscoveryInterval: internal.Duration{Duration: time.Second * 300}, @@ -47,6 +193,16 @@ func createSim() (*simulator.Model, *simulator.Server, error) { return model, s, nil } +func TestParseConfig(t *testing.T) { + v := VSphere{} + c := v.SampleConfig() + p := regexp.MustCompile("#[^#]") + c = configHeader + "\n[[inputs.vsphere]]\n" + p.ReplaceAllLiteralString(c, "") + tab, err := toml.Parse([]byte(c)) + t.Log(tab.Fields) + require.NoError(t, err) +} + func TestAll(t *testing.T) { m, s, err := createSim() if err != nil { From 41033f31f22a6979ff36cb1adce8ba006c52f89d Mon Sep 17 00:00:00 2001 From: prydin Date: Thu, 17 May 2018 20:08:09 -0400 Subject: [PATCH 059/100] Optimized emission of metrics --- plugins/inputs/vsphere/endpoint.go | 249 +++++++++++++++++------------ 1 file changed, 150 insertions(+), 99 deletions(-) diff --git a/plugins/inputs/vsphere/endpoint.go b/plugins/inputs/vsphere/endpoint.go index a63e49aba0cc0..e7a455133dc4b 100644 --- a/plugins/inputs/vsphere/endpoint.go +++ b/plugins/inputs/vsphere/endpoint.go @@ -4,6 +4,7 @@ import ( "context" "log" "net/url" + "strconv" "strings" "sync" "time" @@ -42,6 +43,13 @@ type resource struct { getObjects func(*view.ContainerView) (objectMap, error) } +type metricEntry struct { + tags map[string]string + name string + ts time.Time + fields map[string]interface{} +} + type objectMap map[string]objectRef type objectRef struct { @@ -311,14 +319,13 @@ func getDatastores(root *view.ContainerView) (objectMap, error) { } m := make(objectMap) for _, r := range resources { - m[r.Summary.Name] = objectRef{ + m[r.ExtensibleManagedObject.Reference().Value] = objectRef{ name: r.Name, ref: r.ExtensibleManagedObject.Reference(), parentRef: r.Parent} } return m, nil } func (e *Endpoint) collect(acc telegraf.Accumulator) error { - var err error if !e.initialized { err := e.init() @@ -336,26 +343,22 @@ func (e *Endpoint) collect(acc telegraf.Accumulator) error { return err } } - for k, res := range e.resources { if res.enabled { count, duration, err := e.collectResource(k, acc) if err != nil { return err } - acc.AddGauge("vsphere", map[string]interface{}{"gather.count": count, "gather.duration": duration}, map[string]string{"vcenter": e.URL.Host, "type": k}, time.Now()) } } - return nil } func (e *Endpoint) collectResource(resourceType string, acc telegraf.Accumulator) (int, float64, error) { - // Do we have new data yet? // res := e.resources[resourceType] @@ -377,18 +380,11 @@ func (e *Endpoint) collectResource(resourceType string, acc telegraf.Accumulator log.Printf("D! Collecting metrics for %d objects of type %s for %s", len(res.objects), resourceType, e.URL.Host) - client, err := e.getClient() - if err != nil { - return 0, 0, err - } - ctx := context.Background() - - // Object maps may change, so we need to hold the collect lock + // Object maps may change, so we need to hold the collect lock in read mode // e.collectMux.RLock() defer e.collectMux.RUnlock() - measurementName := "vsphere." + resourceType count := 0 start := time.Now() total := 0 @@ -425,109 +421,164 @@ func (e *Endpoint) collectResource(resourceType string, acc telegraf.Accumulator // if len(pqs) >= int(e.Parent.ObjectsPerQuery) || total >= len(res.objects) { log.Printf("D! Querying %d objects of type %s for %s. Object count: %d. Total objects %d", len(pqs), resourceType, e.URL.Host, total, len(res.objects)) - metrics, err := client.Perf.Query(ctx, pqs) + n, err := e.collectChunk(pqs, resourceType, res, acc, &lastTS) if err != nil { - //TODO: Check the error and attempt to handle gracefully. (ie: object no longer exists) - log.Printf("E! Error querying metrics of %s for %s", resourceType, e.URL.Host) e.checkClient() return count, time.Now().Sub(start).Seconds(), err } + count += n + pqs = make([]types.PerfQuerySpec, 0, e.Parent.ObjectsPerQuery) + } + } - ems, err := client.Perf.ToMetricSeries(ctx, metrics) - if err != nil { - e.checkClient() - return count, time.Now().Sub(start).Seconds(), err - } + if count > 0 { + e.lastColls[resourceType] = lastTS + } + log.Printf("D! Collection of %s for %s, took %v returning %d metrics", resourceType, e.URL.Host, time.Now().Sub(start), count) + return count, time.Now().Sub(start).Seconds(), nil +} - // Iterate through result and fields list - // - for _, em := range ems { - moid := em.Entity.Reference().Value - for _, v := range em.Value { - name := v.Name - for idx, value := range v.Value { - instInfo, found := e.instanceInfo[moid] - if !found { - log.Printf("E! MOID %s not found in cache. Skipping!", moid) - continue - } - t := map[string]string{ - "vcenter": e.URL.Host, - "hostname": instInfo.name, - "moid": moid, - } +func (e *Endpoint) collectChunk(pqs []types.PerfQuerySpec, resourceType string, res resource, acc telegraf.Accumulator, lastTS *time.Time) (int, error) { + count := 0 + prefix := "vsphere." + resourceType + client, err := e.getClient() + if err != nil { + return 0, err + } + ctx := context.Background() + metrics, err := client.Perf.Query(ctx, pqs) + if err != nil { + //TODO: Check the error and attempt to handle gracefully. (ie: object no longer exists) + log.Printf("E! Error querying metrics of %s for %s", resourceType, e.URL.Host) + return count, err + } - objectRef, ok := res.objects[moid] - if ok { - parent, found := e.instanceInfo[objectRef.parentRef.Value] - if found { - switch resourceType { - case "host": - t["cluster"] = parent.name - break + ems, err := client.Perf.ToMetricSeries(ctx, metrics) + if err != nil { + return count, err + } - case "vm": - t["guest"] = objectRef.guest - t["esxhost"] = parent.name - hostRes := e.resources["host"] - hostRef, ok := hostRes.objects[objectRef.parentRef.Value] - if ok { - cluster, ok := e.instanceInfo[hostRef.parentRef.Value] - if ok { - t["cluster"] = cluster.name - } - } - break - } - } - } + // Iterate through results + // + for _, em := range ems { + moid := em.Entity.Reference().Value + instInfo, found := e.instanceInfo[moid] + if !found { + log.Printf("E! MOID %s not found in cache. Skipping! (This should not happen!)", moid) + continue + } + buckets := make(map[string]metricEntry) + for _, v := range em.Value { + name := v.Name + t := map[string]string{ + "vcenter": e.URL.Host, + "hostname": instInfo.name, + "moid": moid, + } + // Populate tags + // + objectRef, ok := res.objects[moid] + if !ok { + log.Printf("E! MOID %s not found in cache. Skipping", moid) + continue + } + e.populateTags(objectRef, resourceType, t, &v) - if v.Instance != "" { - if strings.HasPrefix(name, "cpu.") { - t["cpu"] = v.Instance - } else if strings.HasPrefix(name, "datastore.") { - t["datastore"] = v.Instance - } else if strings.HasPrefix(name, "disk.") { - t["disk"] = cleanDiskTag(v.Instance) - } else if strings.HasPrefix(name, "net.") { - t["interface"] = v.Instance - } else if strings.HasPrefix(name, "storageAdapter.") { - t["adapter"] = v.Instance - } else if strings.HasPrefix(name, "storagePath.") { - t["path"] = v.Instance - } else if strings.HasPrefix(name, "sys.resource") { - t["resource"] = v.Instance - } else if strings.HasPrefix(name, "vflashModule.") { - t["module"] = v.Instance - } else if strings.HasPrefix(name, "virtualDisk.") { - t["disk"] = v.Instance - } else { - // default to instance - t["instance"] = v.Instance - } - } + // Now deal with the values + // + for idx, value := range v.Value { - ts := em.SampleInfo[idx].Timestamp - if ts.After(lastTS) { - lastTS = ts - } + ts := em.SampleInfo[idx].Timestamp + if ts.After(*lastTS) { + *lastTS = ts + } - f := map[string]interface{}{name: value} - acc.AddFields(measurementName, f, t, ts) - count++ - } + // Organize the metrics into a bucket per measurement. + // Data SHOULD be presented to us with the same timestamp for all samples, but in case + // they don't we use the measurement name + timestamp as the key for the bucket. + // + mn, fn := makeMetricIdentifier(prefix, name) + bKey := mn + " " + v.Instance + " " + strconv.FormatInt(ts.UnixNano(), 10) + log.Printf("Bucket key: %s, resource: %s, field: %s", bKey, instInfo.name, fn) + bucket, found := buckets[bKey] + if !found { + bucket = metricEntry{name: mn, ts: ts, fields: make(map[string]interface{}), tags: t} + buckets[bKey] = bucket } + bucket.fields[fn] = value + count++ } - pqs = make([]types.PerfQuerySpec, 0, e.Parent.ObjectsPerQuery) + } + // We've iterated through all the metrics and collected buckets for each + // measurement name. Now emit them! + // + log.Printf("D! Collected %d buckets for %s", len(buckets), instInfo.name) + for key, bucket := range buckets { + log.Printf("D! Key: %s, Tags: %s", key, bucket.tags) + acc.AddFields(bucket.name, bucket.fields, bucket.tags, bucket.ts) } } + return count, nil +} - if count > 0 { - e.lastColls[resourceType] = lastTS +func (e *Endpoint) populateTags(objectRef objectRef, resourceType string, t map[string]string, v *performance.MetricSeries) { + parent, found := e.instanceInfo[objectRef.parentRef.Value] + if found { + switch resourceType { + case "host": + t["cluster"] = parent.name + break + + case "vm": + t["guest"] = objectRef.guest + t["esxhost"] = parent.name + hostRes := e.resources["host"] + hostRef, ok := hostRes.objects[objectRef.parentRef.Value] + if ok { + cluster, ok := e.instanceInfo[hostRef.parentRef.Value] + if ok { + t["cluster"] = cluster.name + } + } + break + } } - log.Printf("D! Collection of %s for %s, took %v returning %d metrics", resourceType, e.URL.Host, time.Now().Sub(start), count) - return count, time.Now().Sub(start).Seconds(), nil + // Determine which point tag to map to the instance + // + name := v.Name + if v.Instance != "" { + if strings.HasPrefix(name, "cpu.") { + t["cpu"] = v.Instance + } else if strings.HasPrefix(name, "datastore.") { + t["datastore"] = v.Instance + } else if strings.HasPrefix(name, "disk.") { + t["disk"] = cleanDiskTag(v.Instance) + } else if strings.HasPrefix(name, "net.") { + t["interface"] = v.Instance + } else if strings.HasPrefix(name, "storageAdapter.") { + t["adapter"] = v.Instance + } else if strings.HasPrefix(name, "storagePath.") { + t["path"] = v.Instance + } else if strings.HasPrefix(name, "sys.resource") { + t["resource"] = v.Instance + } else if strings.HasPrefix(name, "vflashModule.") { + t["module"] = v.Instance + } else if strings.HasPrefix(name, "virtualDisk.") { + t["disk"] = v.Instance + } else { + // default to instance + t["instance"] = v.Instance + } + } +} + +func makeMetricIdentifier(prefix, metric string) (string, string) { + parts := strings.Split(metric, ".") + if len(parts) == 1 { + return prefix, parts[0] + } + return prefix + "." + parts[0], strings.Join(parts[1:], ".") } func cleanGuestID(id string) string { From 4cf70bbaa84617da1143bc9f53a72de730e643a1 Mon Sep 17 00:00:00 2001 From: prydin Date: Thu, 17 May 2018 20:16:00 -0400 Subject: [PATCH 060/100] Prevent from sending empty queries --- plugins/inputs/vsphere/endpoint.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/inputs/vsphere/endpoint.go b/plugins/inputs/vsphere/endpoint.go index e7a455133dc4b..b652456fb2de9 100644 --- a/plugins/inputs/vsphere/endpoint.go +++ b/plugins/inputs/vsphere/endpoint.go @@ -419,7 +419,7 @@ func (e *Endpoint) collectResource(resourceType string, acc telegraf.Accumulator // Filled up a chunk or at end of data? Run a query with the collected objects // - if len(pqs) >= int(e.Parent.ObjectsPerQuery) || total >= len(res.objects) { + if len(pqs) > 0 && len(pqs) >= int(e.Parent.ObjectsPerQuery) || total >= len(res.objects) { log.Printf("D! Querying %d objects of type %s for %s. Object count: %d. Total objects %d", len(pqs), resourceType, e.URL.Host, total, len(res.objects)) n, err := e.collectChunk(pqs, resourceType, res, acc, &lastTS) if err != nil { From fceacc80a128ee73e4ad99c3dc7ca4f59e4cf2a8 Mon Sep 17 00:00:00 2001 From: prydin Date: Thu, 17 May 2018 20:21:16 -0400 Subject: [PATCH 061/100] Prevent from sending empty queries part 2 --- plugins/inputs/vsphere/endpoint.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/inputs/vsphere/endpoint.go b/plugins/inputs/vsphere/endpoint.go index b652456fb2de9..e29593d9470bd 100644 --- a/plugins/inputs/vsphere/endpoint.go +++ b/plugins/inputs/vsphere/endpoint.go @@ -419,7 +419,7 @@ func (e *Endpoint) collectResource(resourceType string, acc telegraf.Accumulator // Filled up a chunk or at end of data? Run a query with the collected objects // - if len(pqs) > 0 && len(pqs) >= int(e.Parent.ObjectsPerQuery) || total >= len(res.objects) { + if len(pqs) > 0 && (len(pqs) >= int(e.Parent.ObjectsPerQuery) || total >= len(res.objects)) { log.Printf("D! Querying %d objects of type %s for %s. Object count: %d. Total objects %d", len(pqs), resourceType, e.URL.Host, total, len(res.objects)) n, err := e.collectChunk(pqs, resourceType, res, acc, &lastTS) if err != nil { From 66615686b570e28d83180a794ec0b45a06484d50 Mon Sep 17 00:00:00 2001 From: prydin Date: Fri, 18 May 2018 12:04:02 -0400 Subject: [PATCH 062/100] Added separator substitution --- plugins/inputs/vsphere/README.MD | 118 +++++++++++++++-------------- plugins/inputs/vsphere/endpoint.go | 11 +-- plugins/inputs/vsphere/vsphere.go | 16 ++-- 3 files changed, 78 insertions(+), 67 deletions(-) diff --git a/plugins/inputs/vsphere/README.MD b/plugins/inputs/vsphere/README.MD index c93decca92291..44012ab9263f7 100644 --- a/plugins/inputs/vsphere/README.MD +++ b/plugins/inputs/vsphere/README.MD @@ -24,23 +24,23 @@ The VMware vSphere plugin uses the vSphere API to gather metrics from multiple v # Typical VM metrics (if omitted, all metrics are collected) # vm_metric_include = [ - # "cpu.ready.summation.delta.millisecond", - # "mem.swapinRate.average.rate.kiloBytesPerSecond", - # "virtualDisk.numberReadAveraged.average.rate.number", - # "virtualDisk.numberWriteAveraged.average.rate.number", - # "virtualDisk.totalReadLatency.average.absolute.millisecond", - # "virtualDisk.totalWriteLatency.average.absolute.millisecond", - # "virtualDisk.readOIO.latest.absolute.number", - # "virtualDisk.writeOIO.latest.absolute.number", - # "net.bytesRx.average.rate.kiloBytesPerSecond", - # "net.bytesTx.average.rate.kiloBytesPerSecond", - # "net.droppedRx.summation.delta.number", - # "net.droppedTx.summation.delta.number", - # "cpu.run.summation.delta.millisecond", - # "cpu.used.summation.delta.millisecond", - # "mem.swapoutRate.average.rate.kiloBytesPerSecond", - # "virtualDisk.read.average.rate.kiloBytesPerSecond", - # "virtualDisk.write.average.rate.kiloBytesPerSecond" ] + # "cpu.ready.summation", + # "mem.swapinRate.average", + # "virtualDisk.numberReadAveraged.average", + # "virtualDisk.numberWriteAveraged.average", + # "virtualDisk.totalReadLatency.average", + # "virtualDisk.totalWriteLatency.average", + # "virtualDisk.readOIO.latest", + # "virtualDisk.writeOIO.latest", + # "net.bytesRx.average", + # "net.bytesTx.average", + # "net.droppedRx.summation", + # "net.droppedTx.summation", + # "cpu.run.summation", + # "cpu.used.summation", + # "mem.swapoutRate.average", + # "virtualDisk.read.average", + # "virtualDisk.write.average" ] # vm_metric_exclude [] @@ -50,41 +50,41 @@ The VMware vSphere plugin uses the vSphere API to gather metrics from multiple v ## Typical host metrics (if omitted, all metrics are collected) # host_metric_include = [ - # "cpu.ready.summation.delta.millisecond", - # "cpu.latency.average.rate.percent", - # "cpu.coreUtilization.average.rate.percent", - # "mem.usage.average.absolute.percent", - # "mem.swapinRate.average.rate.kiloBytesPerSecond", - # "mem.state.latest.absolute.number", - # "mem.latency.average.absolute.percent", - # "mem.vmmemctl.average.absolute.kiloBytes", - # "disk.read.average.rate.kiloBytesPerSecond", - # "disk.write.average.rate.kiloBytesPerSecond", - # "disk.numberReadAveraged.average.rate.number", - # "disk.numberWriteAveraged.average.rate.number", - # "disk.deviceReadLatency.average.absolute.millisecond", - # "disk.deviceWriteLatency.average.absolute.millisecond", - # "disk.totalReadLatency.average.absolute.millisecond", - # "disk.totalWriteLatency.average.absolute.millisecond", - # "storageAdapter.read.average.rate.kiloBytesPerSecond", - # "storageAdapter.write.average.rate.kiloBytesPerSecond", - # "storageAdapter.numberReadAveraged.average.rate.number", - # "storageAdapter.numberWriteAveraged.average.rate.number", - # "net.errorsRx.summation.delta.number", - # "net.errorsTx.summation.delta.number", - # "net.bytesRx.average.rate.kiloBytesPerSecond", - # "net.bytesTx.average.rate.kiloBytesPerSecond", - # "cpu.used.summation.delta.millisecond", - # "cpu.usage.average.rate.percent", - # "cpu.utilization.average.rate.percent", - # "cpu.wait.summation.delta.millisecond", - # "cpu.idle.summation.delta.millisecond", - # "cpu.readiness.average.rate.percent", - # "cpu.costop.summation.delta.millisecond", - # "cpu.swapwait.summation.delta.millisecond", - # "mem.swapoutRate.average.rate.kiloBytesPerSecond", - # "disk.kernelReadLatency.average.absolute.millisecond", - # "disk.kernelWriteLatency.average.absolute.millisecond" ] + # "cpu.ready.summation", + # "cpu.latency.average", + # "cpu.coreUtilization.average", + # "mem.usage.average", + # "mem.swapinRate.average", + # "mem.state.latest", + # "mem.latency.average", + # "mem.vmmemctl.average", + # "disk.read.average", + # "disk.write.average", + # "disk.numberReadAveraged.average", + # "disk.numberWriteAveraged.average", + # "disk.deviceReadLatency.average", + # "disk.deviceWriteLatency.average", + # "disk.totalReadLatency.average", + # "disk.totalWriteLatency.average", + # "storageAdapter.read.average", + # "storageAdapter.write.average", + # "storageAdapter.numberReadAveraged.average", + # "storageAdapter.numberWriteAveraged.average", + # "net.errorsRx.summation", + # "net.errorsTx.summation", + # "net.bytesRx.average", + # "net.bytesTx.average", + # "cpu.used.summation", + # "cpu.usage.average", + # "cpu.utilization.average", + # "cpu.wait.summation", + # "cpu.idle.summation", + # "cpu.readiness.average", + # "cpu.costop.summation", + # "cpu.swapwait.summation", + # "mem.swapoutRate.average", + # "disk.kernelReadLatency.average", + # "disk.kernelWriteLatency.average" ] # host_metric_exclude = [] # Nothing excluded by default @@ -106,11 +106,15 @@ The VMware vSphere plugin uses the vSphere API to gather metrics from multiple v # gather_datastore = true # (default=true) ## Typical datastore metrics (if omitted, all metrics are collected) - # datastore_metric_include = [ - # "disk.used.*", - # "disk.provsioned.*" ] - - # storage_metric_exclude = [] # Nothing excluded by default + # + # WARNING: It is not recommended to include all metrics. Your collection will probably time out if you do! + # + datastore_metric_include = [ + "datastore.read.average" + "datastore.write.average" + "datastore.throughput.usage.average" + "datastore.throughput.contention.average" ] + datastore_metric_exclude = [ "disk.*" ] ## number of objects to retreive per query. set to 64 for vCenter 5.5 and 6.0 (default: 256) # objects_per_query = 256 diff --git a/plugins/inputs/vsphere/endpoint.go b/plugins/inputs/vsphere/endpoint.go index e29593d9470bd..b412b7033bb85 100644 --- a/plugins/inputs/vsphere/endpoint.go +++ b/plugins/inputs/vsphere/endpoint.go @@ -440,7 +440,7 @@ func (e *Endpoint) collectResource(resourceType string, acc telegraf.Accumulator func (e *Endpoint) collectChunk(pqs []types.PerfQuerySpec, resourceType string, res resource, acc telegraf.Accumulator, lastTS *time.Time) (int, error) { count := 0 - prefix := "vsphere." + resourceType + prefix := "vsphere" + e.Parent.Separator + resourceType client, err := e.getClient() if err != nil { return 0, err @@ -497,9 +497,9 @@ func (e *Endpoint) collectChunk(pqs []types.PerfQuerySpec, resourceType string, // Data SHOULD be presented to us with the same timestamp for all samples, but in case // they don't we use the measurement name + timestamp as the key for the bucket. // - mn, fn := makeMetricIdentifier(prefix, name) + mn, fn := e.makeMetricIdentifier(prefix, name) bKey := mn + " " + v.Instance + " " + strconv.FormatInt(ts.UnixNano(), 10) - log.Printf("Bucket key: %s, resource: %s, field: %s", bKey, instInfo.name, fn) + log.Printf("D! Bucket key: %s, resource: %s, field: %s", bKey, instInfo.name, fn) bucket, found := buckets[bKey] if !found { bucket = metricEntry{name: mn, ts: ts, fields: make(map[string]interface{}), tags: t} @@ -573,12 +573,13 @@ func (e *Endpoint) populateTags(objectRef objectRef, resourceType string, t map[ } } -func makeMetricIdentifier(prefix, metric string) (string, string) { +func (e *Endpoint) makeMetricIdentifier(prefix, metric string) (string, string) { parts := strings.Split(metric, ".") if len(parts) == 1 { return prefix, parts[0] } - return prefix + "." + parts[0], strings.Join(parts[1:], ".") + + return prefix + "." + parts[0], strings.Join(parts[1:], e.Parent.Separator) } func cleanGuestID(id string) string { diff --git a/plugins/inputs/vsphere/vsphere.go b/plugins/inputs/vsphere/vsphere.go index 03df287304afa..64aa20da0a412 100644 --- a/plugins/inputs/vsphere/vsphere.go +++ b/plugins/inputs/vsphere/vsphere.go @@ -30,6 +30,7 @@ type VSphere struct { GatherDatastores bool DatastoreMetricInclude []string DatastoreMetricExclude []string + Separator string ObjectsPerQuery int32 ObjectDiscoveryInterval internal.Duration @@ -136,11 +137,15 @@ var sampleConfig = ` # gather_datastore = true ### (default=true) ## Typical datastore metrics (if omitted, all metrics are collected) -# datastore_metric_include = [ -# "disk.used.*", -# "disk.provsioned.*" ] - -# storage_metric_exclude = [] ## Nothing excluded by default +## +## WARNING: It is not recommended to include all metrics. Your collection will probably time out if you do! +## +datastore_metric_include = [ + "datastore.read.average" + "datastore.write.average" + "datastore.throughput.usage.average" + "datastore.throughput.contention.average" ] +datastore_metric_exclude = [ "disk.*" ] ## number of objects to retreive per query. set to 64 for vCenter 5.5 and 6.0 (default: 256) # objects_per_query = 256 @@ -224,6 +229,7 @@ func init() { GatherDatastores: true, DatastoreMetricInclude: nil, DatastoreMetricExclude: nil, + Separator: ".", ObjectsPerQuery: 256, ObjectDiscoveryInterval: internal.Duration{Duration: time.Second * 300}, From 96e9e421f59b96b7f42a5adb83a50a193fa14afc Mon Sep 17 00:00:00 2001 From: prydin Date: Wed, 6 Jun 2018 18:44:43 -0400 Subject: [PATCH 063/100] Fixed datastore naming issue --- plugins/inputs/vsphere/README.MD | 246 ++++++++++++------------- plugins/inputs/vsphere/endpoint.go | 46 +++-- plugins/inputs/vsphere/vsphere.go | 12 +- plugins/inputs/vsphere/vsphere_test.go | 9 +- 4 files changed, 151 insertions(+), 162 deletions(-) diff --git a/plugins/inputs/vsphere/README.MD b/plugins/inputs/vsphere/README.MD index 44012ab9263f7..a9a60dafaf3da 100644 --- a/plugins/inputs/vsphere/README.MD +++ b/plugins/inputs/vsphere/README.MD @@ -12,143 +12,125 @@ The VMware vSphere plugin uses the vSphere API to gather metrics from multiple v ``` # Read metrics from one or many vCenters [[inputs.vsphere]] - ## List of vCenter URLs to be monitored. - # vcenters = [ "https://vcenter.local/sdk" ] - # username = "user@corp.local" - # password = "secret" - - - ############### VMs ############### - - # gather_vms = true # (default=true) - - # Typical VM metrics (if omitted, all metrics are collected) - # vm_metric_include = [ - # "cpu.ready.summation", - # "mem.swapinRate.average", - # "virtualDisk.numberReadAveraged.average", - # "virtualDisk.numberWriteAveraged.average", - # "virtualDisk.totalReadLatency.average", - # "virtualDisk.totalWriteLatency.average", - # "virtualDisk.readOIO.latest", - # "virtualDisk.writeOIO.latest", - # "net.bytesRx.average", - # "net.bytesTx.average", - # "net.droppedRx.summation", - # "net.droppedTx.summation", - # "cpu.run.summation", - # "cpu.used.summation", - # "mem.swapoutRate.average", - # "virtualDisk.read.average", - # "virtualDisk.write.average" ] - - # vm_metric_exclude [] - - ############### Hosts ############### - - # gather_hosts = true # (default=true) - - ## Typical host metrics (if omitted, all metrics are collected) - # host_metric_include = [ - # "cpu.ready.summation", - # "cpu.latency.average", - # "cpu.coreUtilization.average", - # "mem.usage.average", - # "mem.swapinRate.average", - # "mem.state.latest", - # "mem.latency.average", - # "mem.vmmemctl.average", - # "disk.read.average", - # "disk.write.average", - # "disk.numberReadAveraged.average", - # "disk.numberWriteAveraged.average", - # "disk.deviceReadLatency.average", - # "disk.deviceWriteLatency.average", - # "disk.totalReadLatency.average", - # "disk.totalWriteLatency.average", - # "storageAdapter.read.average", - # "storageAdapter.write.average", - # "storageAdapter.numberReadAveraged.average", - # "storageAdapter.numberWriteAveraged.average", - # "net.errorsRx.summation", - # "net.errorsTx.summation", - # "net.bytesRx.average", - # "net.bytesTx.average", - # "cpu.used.summation", - # "cpu.usage.average", - # "cpu.utilization.average", - # "cpu.wait.summation", - # "cpu.idle.summation", - # "cpu.readiness.average", - # "cpu.costop.summation", - # "cpu.swapwait.summation", - # "mem.swapoutRate.average", - # "disk.kernelReadLatency.average", - # "disk.kernelWriteLatency.average" ] - - # host_metric_exclude = [] # Nothing excluded by default - - ############### Clusters ############### - - # gather_clusters = true # (default=true) - - ## Typical cluster metrics (if omitted, all metrics are collected) - # cluster_metric_include = [ - # "cpu.usage.*", - # "cpu.usagemhz.*", - # "mem.usage.*", - # "mem.active.*" ] - - # cluster_metric_exclude [] # Nothing excluded by default - - ############### Datastores ############### - - # gather_datastore = true # (default=true) - - ## Typical datastore metrics (if omitted, all metrics are collected) - # - # WARNING: It is not recommended to include all metrics. Your collection will probably time out if you do! - # - datastore_metric_include = [ - "datastore.read.average" - "datastore.write.average" - "datastore.throughput.usage.average" + # List of vCenter URLs to be monitored. These three lines must be uncommented +## and edited for the plugin to work. +# vcenters = [ "https://vcenter.local/sdk" ] +# username = "user@corp.local" +# password = "secret" + +############### VMs ############### + +# gather_vms = true ### (default=true) + +## Typical VM metrics (if omitted, all metrics are collected) +# vm_metric_include = [ +# "cpu.ready.summation", +# "mem.swapinRate.average", +# "virtualDisk.numberReadAveraged.average", +# "virtualDisk.numberWriteAveraged.average", +# "virtualDisk.totalReadLatency.average", +# "virtualDisk.totalWriteLatency.average", +# "virtualDisk.readOIO.latest", +# "virtualDisk.writeOIO.latest", +# "net.bytesRx.average", +# "net.bytesTx.average", +# "net.droppedRx.summation.", +# "net.droppedTx.summation.", +# "cpu.run.summation", +# "cpu.used.summation", +# "mem.swapoutRate.average", +# "virtualDisk.read.average", +# "virtualDisk.write.average" ] + +# vm_metric_exclude = [] ## Nothing is excluded by default + +############### Hosts ############### + +# gather_hosts = true ## (default=true) + +## Typical host metrics (if omitted, all metrics are collected) +# host_metric_include = [ +# "cpu.ready.summation", +# "cpu.latency.average", +# "cpu.coreUtilization.average", +# "mem.usage.average", +# "mem.swapinRate.average", +# "mem.state.latest", +# "mem.latency.average", +# "mem.vmmemctl.average", +# "disk.read.average", +# "disk.write.average", +# "disk.numberReadAveraged.average", +# "disk.numberWriteAveraged.average", +# "disk.deviceReadLatency.average", +# "disk.deviceWriteLatency.average", +# "disk.totalReadLatency.average", +# "disk.totalWriteLatency.average", +# "storageAdapter.read.average", +# "storageAdapter.write.average", +# "storageAdapter.numberReadAveraged.average", +# "storageAdapter.numberWriteAveraged.average", +# "net.errorsRx.summation", +# "net.errorsTx.summation", +# "net.bytesRx.average", +# "net.bytesTx.average", +# "cpu.used.summation", +# "cpu.usage.average", +# "cpu.utilization.average", +# "cpu.wait.summation.", +# "cpu.idle.summation", +# "cpu.readiness.average", +# "cpu.costop.summation", +# "cpu.swapwait.summation", +# "mem.swapoutRate.average", +# "disk.kernelReadLatency.average", +# "disk.kernelWriteLatency.average" ] + +# host_metric_exclude = [] ## Nothing excluded by default + +############### Clusters ############### + +# gather_clusters = true ## (default=true) + +## Typical cluster metrics (if omitted, all metrics are collected) +#cluster_metric_include = [ +# "cpu.usage.*", +# "cpu.usagemhz.*", +# "mem.usage.*", +# "mem.active.*" ] + +# cluster_metric_exclude = [] ## Nothing excluded by default + +############### Datastores ############### + +# gather_datastores = true ## (default=true) + +## Typical datastore metrics (if omitted, all metrics are collected) +## +## WARNING: It is not recommended to include all metrics. Your collection will probably time out if you do! +## + +datastore_metric_include = [ + "datastore.read.average", + "datastore.write.average", + "datastore.throughput.usage.average", "datastore.throughput.contention.average" ] - datastore_metric_exclude = [ "disk.*" ] - - ## number of objects to retreive per query. set to 64 for vCenter 5.5 and 6.0 (default: 256) - # objects_per_query = 256 - - ## the interval before (re)discovering objects subject to metrics collection (default: 300s) - # object_discovery_interval = "300s" - - ## timeout applies to any of the connection request made to vcenter - # timeout = "20s" - - ## Optional SSL Config - # ssl_ca = /path/to/cafile - # ssl_cert = /path/to/certfile - # ssl_key = /path/to/keyfile - ## Use SSL but skip chain & host verification - # insecure_skip_verify = false -``` - -### Metrics Selection -vSphere metrics are available in 4 categories: Clusters, Hosts, VMs, and Datastores. Each category can be enabled -independently, and for each category a list with wildcards can be applied to filter metrics. If the <category>_metrics -property is not specified all metrics will be included for the category. Metrics can be specified with standard glob -style wildcards (*, ?). You can specify metrics to be negated, by using an exclamation mark (!) as the first character. -Metrics will be checked against every item in the list in order, meaning you can include multiple metrics with the first -item, and negate selectively within that list on a subsequent item. Negating metrics that have not been whitelisted in -a prior item of the list, has no effect. +## number of objects to retreive per query. set to 64 for vCenter 5.5 and 6.0 (default: 256) +# objects_per_query = 256 -A configuration to include all metrics under the host category that start with `cpu.*` but will not include -`cpu.idle.summation`. +## the interval before (re)discovering objects subject to metrics collection (default: 300s) +# object_discovery_interval = "300s" -``` - host_metrics = ["cpu.*", "!cpu.idle.summation"] +## timeout applies to any of the connection request made to vcenter +# timeout = "20s" +## Optional SSL Config +# ssl_ca = "/path/to/cafile" +# ssl_cert = "/path/to/certfile" +# ssl_key = "/path/to/keyfile" +## Use SSL but skip chain & host verification +# insecure_skip_verify = false ``` ### Objects Per Query diff --git a/plugins/inputs/vsphere/endpoint.go b/plugins/inputs/vsphere/endpoint.go index b412b7033bb85..afc029356154d 100644 --- a/plugins/inputs/vsphere/endpoint.go +++ b/plugins/inputs/vsphere/endpoint.go @@ -235,8 +235,10 @@ func (e *Endpoint) discover() error { break } } - mList = append(mList, m) - log.Printf("D! Included %s Sampling: %d", mName, res.sampling) + if include { // If still included after processing excludes + mList = append(mList, m) + log.Printf("D! Included %s Sampling: %d", mName, res.sampling) + } } } @@ -374,9 +376,9 @@ func (e *Endpoint) collectResource(resourceType string, acc telegraf.Accumulator return 0, 0, nil } } else { - latest = now.Add(-time.Duration(res.sampling) * time.Second) - e.lastColls[resourceType] = latest + latest = time.Now().Add(time.Duration(-res.sampling) * time.Second) } + log.Printf("Start of sample period deemed to be %s", latest) log.Printf("D! Collecting metrics for %d objects of type %s for %s", len(res.objects), resourceType, e.URL.Host) @@ -388,7 +390,6 @@ func (e *Endpoint) collectResource(resourceType string, acc telegraf.Accumulator count := 0 start := time.Now() total := 0 - lastTS := latest pqs := make([]types.PerfQuerySpec, 0, e.Parent.ObjectsPerQuery) for _, object := range res.objects { info, found := e.instanceInfo[object.ref.Value] @@ -421,7 +422,7 @@ func (e *Endpoint) collectResource(resourceType string, acc telegraf.Accumulator // if len(pqs) > 0 && (len(pqs) >= int(e.Parent.ObjectsPerQuery) || total >= len(res.objects)) { log.Printf("D! Querying %d objects of type %s for %s. Object count: %d. Total objects %d", len(pqs), resourceType, e.URL.Host, total, len(res.objects)) - n, err := e.collectChunk(pqs, resourceType, res, acc, &lastTS) + n, err := e.collectChunk(pqs, resourceType, res, acc) if err != nil { e.checkClient() return count, time.Now().Sub(start).Seconds(), err @@ -430,15 +431,12 @@ func (e *Endpoint) collectResource(resourceType string, acc telegraf.Accumulator pqs = make([]types.PerfQuerySpec, 0, e.Parent.ObjectsPerQuery) } } - - if count > 0 { - e.lastColls[resourceType] = lastTS - } + e.lastColls[resourceType] = now // Use value captured at the beginning to avoid blind spots. log.Printf("D! Collection of %s for %s, took %v returning %d metrics", resourceType, e.URL.Host, time.Now().Sub(start), count) return count, time.Now().Sub(start).Seconds(), nil } -func (e *Endpoint) collectChunk(pqs []types.PerfQuerySpec, resourceType string, res resource, acc telegraf.Accumulator, lastTS *time.Time) (int, error) { +func (e *Endpoint) collectChunk(pqs []types.PerfQuerySpec, resourceType string, res resource, acc telegraf.Accumulator) (int, error) { count := 0 prefix := "vsphere" + e.Parent.Separator + resourceType client, err := e.getClient() @@ -487,11 +485,7 @@ func (e *Endpoint) collectChunk(pqs []types.PerfQuerySpec, resourceType string, // Now deal with the values // for idx, value := range v.Value { - ts := em.SampleInfo[idx].Timestamp - if ts.After(*lastTS) { - *lastTS = ts - } // Organize the metrics into a bucket per measurement. // Data SHOULD be presented to us with the same timestamp for all samples, but in case @@ -499,7 +493,7 @@ func (e *Endpoint) collectChunk(pqs []types.PerfQuerySpec, resourceType string, // mn, fn := e.makeMetricIdentifier(prefix, name) bKey := mn + " " + v.Instance + " " + strconv.FormatInt(ts.UnixNano(), 10) - log.Printf("D! Bucket key: %s, resource: %s, field: %s", bKey, instInfo.name, fn) + //log.Printf("D! Bucket key: %s, resource: %s, field: %s", bKey, instInfo.name, fn) bucket, found := buckets[bKey] if !found { bucket = metricEntry{name: mn, ts: ts, fields: make(map[string]interface{}), tags: t} @@ -514,7 +508,7 @@ func (e *Endpoint) collectChunk(pqs []types.PerfQuerySpec, resourceType string, // log.Printf("D! Collected %d buckets for %s", len(buckets), instInfo.name) for key, bucket := range buckets { - log.Printf("D! Key: %s, Tags: %s", key, bucket.tags) + log.Printf("D! Key: %s, Tags: %s, Fields: %s", key, bucket.tags, bucket.fields) acc.AddFields(bucket.name, bucket.fields, bucket.tags, bucket.ts) } } @@ -522,6 +516,19 @@ func (e *Endpoint) collectChunk(pqs []types.PerfQuerySpec, resourceType string, } func (e *Endpoint) populateTags(objectRef objectRef, resourceType string, t map[string]string, v *performance.MetricSeries) { + // Map name of object. For vms and hosts, we use the default "hostname". + // + switch resourceType { + case "datastore": + t["dsname"] = objectRef.name + case "disk": + t["diskname"] = objectRef.name + case "cluster": + t["clustername"] = objectRef.name + } + + // Map parent reference + // parent, found := e.instanceInfo[objectRef.parentRef.Value] if found { switch resourceType { @@ -551,7 +558,7 @@ func (e *Endpoint) populateTags(objectRef objectRef, resourceType string, t map[ if strings.HasPrefix(name, "cpu.") { t["cpu"] = v.Instance } else if strings.HasPrefix(name, "datastore.") { - t["datastore"] = v.Instance + t["lun"] = v.Instance } else if strings.HasPrefix(name, "disk.") { t["disk"] = cleanDiskTag(v.Instance) } else if strings.HasPrefix(name, "net.") { @@ -578,8 +585,7 @@ func (e *Endpoint) makeMetricIdentifier(prefix, metric string) (string, string) if len(parts) == 1 { return prefix, parts[0] } - - return prefix + "." + parts[0], strings.Join(parts[1:], e.Parent.Separator) + return prefix + e.Parent.Separator + parts[0], strings.Join(parts[1:], e.Parent.Separator) } func cleanGuestID(id string) string { diff --git a/plugins/inputs/vsphere/vsphere.go b/plugins/inputs/vsphere/vsphere.go index 64aa20da0a412..349f59bb5f5f6 100644 --- a/plugins/inputs/vsphere/vsphere.go +++ b/plugins/inputs/vsphere/vsphere.go @@ -124,7 +124,7 @@ var sampleConfig = ` # gather_clusters = true ## (default=true) ## Typical cluster metrics (if omitted, all metrics are collected) -# cluster_metric_include = [ +#cluster_metric_include = [ # "cpu.usage.*", # "cpu.usagemhz.*", # "mem.usage.*", @@ -134,18 +134,18 @@ var sampleConfig = ` ############### Datastores ############### -# gather_datastore = true ### (default=true) +# gather_datastores = true ## (default=true) ## Typical datastore metrics (if omitted, all metrics are collected) ## ## WARNING: It is not recommended to include all metrics. Your collection will probably time out if you do! ## + datastore_metric_include = [ - "datastore.read.average" - "datastore.write.average" - "datastore.throughput.usage.average" + "datastore.read.average", + "datastore.write.average", + "datastore.throughput.usage.average", "datastore.throughput.contention.average" ] -datastore_metric_exclude = [ "disk.*" ] ## number of objects to retreive per query. set to 64 for vCenter 5.5 and 6.0 (default: 256) # objects_per_query = 256 diff --git a/plugins/inputs/vsphere/vsphere_test.go b/plugins/inputs/vsphere/vsphere_test.go index e85d8e92b6148..e506c94415e1e 100644 --- a/plugins/inputs/vsphere/vsphere_test.go +++ b/plugins/inputs/vsphere/vsphere_test.go @@ -188,7 +188,7 @@ func createSim() (*simulator.Model, *simulator.Server, error) { model.Service.TLS = new(tls.Config) s := model.Service.NewServer() - fmt.Printf("Server created at: %s\n", s.URL) + //fmt.Printf("Server created at: %s\n", s.URL) return model, s, nil } @@ -196,11 +196,12 @@ func createSim() (*simulator.Model, *simulator.Server, error) { func TestParseConfig(t *testing.T) { v := VSphere{} c := v.SampleConfig() - p := regexp.MustCompile("#[^#]") - c = configHeader + "\n[[inputs.vsphere]]\n" + p.ReplaceAllLiteralString(c, "") + p := regexp.MustCompile("\n#") + fmt.Printf("Source=%s", p.ReplaceAllLiteralString(c, "\n")) + c = configHeader + "\n[[inputs.vsphere]]\n" + p.ReplaceAllLiteralString(c, "\n") tab, err := toml.Parse([]byte(c)) - t.Log(tab.Fields) require.NoError(t, err) + require.NotNil(t, tab) } func TestAll(t *testing.T) { From 624e813c2dd835e8b004b5c09701d00ad847507b Mon Sep 17 00:00:00 2001 From: prydin Date: Tue, 12 Jun 2018 17:23:01 -0700 Subject: [PATCH 064/100] Cleaned up tag naming --- plugins/inputs/vsphere/endpoint.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/plugins/inputs/vsphere/endpoint.go b/plugins/inputs/vsphere/endpoint.go index afc029356154d..d8bfa0bc96715 100644 --- a/plugins/inputs/vsphere/endpoint.go +++ b/plugins/inputs/vsphere/endpoint.go @@ -480,7 +480,7 @@ func (e *Endpoint) collectChunk(pqs []types.PerfQuerySpec, resourceType string, log.Printf("E! MOID %s not found in cache. Skipping", moid) continue } - e.populateTags(objectRef, resourceType, t, &v) + e.populateTags(&objectRef, resourceType, t, &v) // Now deal with the values // @@ -515,7 +515,7 @@ func (e *Endpoint) collectChunk(pqs []types.PerfQuerySpec, resourceType string, return count, nil } -func (e *Endpoint) populateTags(objectRef objectRef, resourceType string, t map[string]string, v *performance.MetricSeries) { +func (e *Endpoint) populateTags(objectRef *objectRef, resourceType string, t map[string]string, v *performance.MetricSeries) { // Map name of object. For vms and hosts, we use the default "hostname". // switch resourceType { @@ -533,18 +533,18 @@ func (e *Endpoint) populateTags(objectRef objectRef, resourceType string, t map[ if found { switch resourceType { case "host": - t["cluster"] = parent.name + t["clustername"] = parent.name break case "vm": t["guest"] = objectRef.guest - t["esxhost"] = parent.name + t["esxhostname"] = parent.name hostRes := e.resources["host"] hostRef, ok := hostRes.objects[objectRef.parentRef.Value] if ok { cluster, ok := e.instanceInfo[hostRef.parentRef.Value] if ok { - t["cluster"] = cluster.name + t["clustername"] = cluster.name } } break From bb11db706331f7c52149c33d16f6cfae38a4e114 Mon Sep 17 00:00:00 2001 From: prydin Date: Tue, 12 Jun 2018 19:22:53 -0700 Subject: [PATCH 065/100] Changed comment to reflect new logic --- plugins/inputs/vsphere/endpoint.go | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/plugins/inputs/vsphere/endpoint.go b/plugins/inputs/vsphere/endpoint.go index d8bfa0bc96715..b025e9492198f 100644 --- a/plugins/inputs/vsphere/endpoint.go +++ b/plugins/inputs/vsphere/endpoint.go @@ -34,6 +34,7 @@ type Endpoint struct { } type resource struct { + pKey string enabled bool realTime bool sampling int32 @@ -77,6 +78,7 @@ func NewEndpoint(parent *VSphere, url *url.URL) *Endpoint { e.resources = map[string]resource{ "cluster": { + pKey: "clustername", enabled: parent.GatherClusters, realTime: false, sampling: 300, @@ -86,6 +88,7 @@ func NewEndpoint(parent *VSphere, url *url.URL) *Endpoint { getObjects: getClusters, }, "host": { + pKey: "esxhostname", enabled: parent.GatherHosts, realTime: true, sampling: 20, @@ -95,6 +98,7 @@ func NewEndpoint(parent *VSphere, url *url.URL) *Endpoint { getObjects: getHosts, }, "vm": { + pKey: "vmname", enabled: parent.GatherVms, realTime: true, sampling: 20, @@ -104,6 +108,7 @@ func NewEndpoint(parent *VSphere, url *url.URL) *Endpoint { getObjects: getVMs, }, "datastore": { + pKey: "dsname", enabled: parent.GatherDatastores, realTime: false, sampling: 300, @@ -378,7 +383,7 @@ func (e *Endpoint) collectResource(resourceType string, acc telegraf.Accumulator } else { latest = time.Now().Add(time.Duration(-res.sampling) * time.Second) } - log.Printf("Start of sample period deemed to be %s", latest) + log.Printf("D! Start of sample period deemed to be %s", latest) log.Printf("D! Collecting metrics for %d objects of type %s for %s", len(res.objects), resourceType, e.URL.Host) @@ -480,7 +485,7 @@ func (e *Endpoint) collectChunk(pqs []types.PerfQuerySpec, resourceType string, log.Printf("E! MOID %s not found in cache. Skipping", moid) continue } - e.populateTags(&objectRef, resourceType, t, &v) + e.populateTags(&objectRef, resourceType, &res, t, &v) // Now deal with the values // @@ -515,16 +520,11 @@ func (e *Endpoint) collectChunk(pqs []types.PerfQuerySpec, resourceType string, return count, nil } -func (e *Endpoint) populateTags(objectRef *objectRef, resourceType string, t map[string]string, v *performance.MetricSeries) { - // Map name of object. For vms and hosts, we use the default "hostname". +func (e *Endpoint) populateTags(objectRef *objectRef, resourceType string, resource *resource, t map[string]string, v *performance.MetricSeries) { + // Map name of object. // - switch resourceType { - case "datastore": - t["dsname"] = objectRef.name - case "disk": - t["diskname"] = objectRef.name - case "cluster": - t["clustername"] = objectRef.name + if resource.pKey != "" { + t[resource.pKey] = objectRef.name } // Map parent reference From 805a5733ffd8f21f553615b32a32113fd726c822 Mon Sep 17 00:00:00 2001 From: prydin Date: Thu, 5 Jul 2018 21:05:12 -0400 Subject: [PATCH 066/100] Removed race condition. Optimized discovery --- plugins/inputs/vsphere/endpoint.go | 67 +++++++++--------------------- 1 file changed, 19 insertions(+), 48 deletions(-) diff --git a/plugins/inputs/vsphere/endpoint.go b/plugins/inputs/vsphere/endpoint.go index b025e9492198f..80c0da774a691 100644 --- a/plugins/inputs/vsphere/endpoint.go +++ b/plugins/inputs/vsphere/endpoint.go @@ -22,7 +22,7 @@ import ( type Endpoint struct { Parent *VSphere URL *url.URL - client *Client + pool Pool lastColls map[string]time.Time instanceInfo map[string]resourceInfo resources map[string]resource @@ -69,6 +69,7 @@ type resourceInfo struct { // as parameters. func NewEndpoint(parent *VSphere, url *url.URL) *Endpoint { e := Endpoint{ + pool: Pool{u: url, v: parent, root: nil}, URL: url, Parent: parent, lastColls: make(map[string]time.Time), @@ -157,10 +158,11 @@ func (e *Endpoint) init() error { } func (e *Endpoint) setupMetricIds() error { - client, err := e.getClient() + client, err := e.pool.Take() if err != nil { return err } + defer e.pool.Return(client) ctx := context.Background() mn, err := client.Perf.CounterInfoByName(ctx) if err != nil { @@ -177,17 +179,18 @@ func (e *Endpoint) discover() error { start := time.Now() log.Printf("D! Discover new objects for %s", e.URL.Host) - client, err := e.getClient() + client, err := e.pool.Take() if err != nil { return err } + defer e.pool.Return(client) instInfo := make(map[string]resourceInfo) - resources := e.resources + resources := make(map[string]resource) // Populate resource objects, and endpoint name cache // - for k, res := range resources { + for k, res := range e.resources { // Don't be tempted to skip disabled resources here! We may need them to resolve parent references // // Precompile includes and excludes @@ -202,18 +205,18 @@ func (e *Endpoint) discover() error { } // Need to do this for all resource types even if they are not enabled (but datastore) // - if res.enabled || k != "datastore" { + if res.enabled || (k != "datastore" && k != "vm") { objects, err := res.getObjects(client.Root) if err != nil { - e.checkClient() + client = nil // Don't reuse this one! return err } for _, obj := range objects { ctx := context.Background() mList := make(performance.MetricList, 0) - metrics, err := e.client.Perf.AvailableMetric(ctx, obj.ref.Reference(), res.sampling) + metrics, err := client.Perf.AvailableMetric(ctx, obj.ref.Reference(), res.sampling) if err != nil { - e.checkClient() + client = nil // Don't reuse this one! return err } log.Printf("D! Obj: %s, metrics found: %d, enabled: %t", obj.name, len(metrics), res.enabled) @@ -242,7 +245,7 @@ func (e *Endpoint) discover() error { } if include { // If still included after processing excludes mList = append(mList, m) - log.Printf("D! Included %s Sampling: %d", mName, res.sampling) + //log.Printf("D! Included %s Sampling: %d", mName, res.sampling) } } @@ -341,6 +344,9 @@ func (e *Endpoint) collect(acc telegraf.Accumulator) error { } } + e.collectMux.RLock() + defer e.collectMux.RUnlock() + // If discovery interval is disabled (0), discover on each collection cycle // if e.Parent.ObjectDiscoveryInterval.Duration.Seconds() == 0 { @@ -384,14 +390,8 @@ func (e *Endpoint) collectResource(resourceType string, acc telegraf.Accumulator latest = time.Now().Add(time.Duration(-res.sampling) * time.Second) } log.Printf("D! Start of sample period deemed to be %s", latest) - log.Printf("D! Collecting metrics for %d objects of type %s for %s", len(res.objects), resourceType, e.URL.Host) - // Object maps may change, so we need to hold the collect lock in read mode - // - e.collectMux.RLock() - defer e.collectMux.RUnlock() - count := 0 start := time.Now() total := 0 @@ -429,7 +429,6 @@ func (e *Endpoint) collectResource(resourceType string, acc telegraf.Accumulator log.Printf("D! Querying %d objects of type %s for %s. Object count: %d. Total objects %d", len(pqs), resourceType, e.URL.Host, total, len(res.objects)) n, err := e.collectChunk(pqs, resourceType, res, acc) if err != nil { - e.checkClient() return count, time.Now().Sub(start).Seconds(), err } count += n @@ -444,10 +443,11 @@ func (e *Endpoint) collectResource(resourceType string, acc telegraf.Accumulator func (e *Endpoint) collectChunk(pqs []types.PerfQuerySpec, resourceType string, res resource, acc telegraf.Accumulator) (int, error) { count := 0 prefix := "vsphere" + e.Parent.Separator + resourceType - client, err := e.getClient() + client, err := e.pool.Take() if err != nil { return 0, err } + defer e.pool.Return(client) ctx := context.Background() metrics, err := client.Perf.Query(ctx, pqs) if err != nil { @@ -458,6 +458,7 @@ func (e *Endpoint) collectChunk(pqs []types.PerfQuerySpec, resourceType string, ems, err := client.Perf.ToMetricSeries(ctx, metrics) if err != nil { + client = nil return count, err } @@ -610,33 +611,3 @@ func cleanDiskTag(disk string) string { return disk } - -func (e *Endpoint) getClient() (*Client, error) { - if e.client == nil { - e.clientMux.Lock() - defer e.clientMux.Unlock() - if e.client == nil { - log.Printf("D! Creating new vCenter client for: %s", e.URL.Host) - client, err := NewClient(e.URL, e.Parent) - if err != nil { - return nil, err - } - e.client = client - } - } - return e.client, nil -} - -func (e *Endpoint) checkClient() { - if e.client != nil { - active, err := e.client.Client.SessionManager.SessionIsActive(context.Background()) - if err != nil { - log.Printf("E! SessionIsActive returned an error on %s: %v", e.URL.Host, err) - e.client = nil - } - if !active { - log.Printf("I! Session no longer active, reseting client: %s", e.URL.Host) - e.client = nil - } - } -} From f0142f298237b94af36046da2ef3dff2d77d3bbf Mon Sep 17 00:00:00 2001 From: prydin Date: Thu, 5 Jul 2018 21:05:43 -0400 Subject: [PATCH 067/100] Removed race condition. Optimized discovery --- plugins/inputs/vsphere/pool.go | 48 ++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 plugins/inputs/vsphere/pool.go diff --git a/plugins/inputs/vsphere/pool.go b/plugins/inputs/vsphere/pool.go new file mode 100644 index 0000000000000..0c825db33e555 --- /dev/null +++ b/plugins/inputs/vsphere/pool.go @@ -0,0 +1,48 @@ +package vsphere + +import ( + "log" + "net/url" + "sync" +) + +type poolMember struct { + Client *Client + next *poolMember +} + +// Pool is a simple free-list based pool of vSphere clients +type Pool struct { + u *url.URL + v *VSphere + root *poolMember + mux sync.Mutex +} + +// Take returns a client, either by picking an available one from the pool or creating a new one +func (p *Pool) Take() (*Client, error) { + p.mux.Lock() + defer p.mux.Unlock() + if p.root != nil { + r := p.root + p.root = r.next + log.Printf("D! //////// Getting connection from pool") + return r.Client, nil + } + // Pool is empty, create a new client! + // + log.Printf("D! ******* Pool is empty, creating new client") + return NewClient(p.u, p.v) +} + +// Return put a client back to the free list +func (p *Pool) Return(client *Client) { + if client == nil { + log.Printf("E! Connection taken out of pool due to error") + return // Useful when you want to override a deferred Return + } + p.mux.Lock() + defer p.mux.Unlock() + r := &poolMember{client, p.root} + p.root = r +} From 76a4d4f08e8738f8df5bd97d20d3af87cbbe0938 Mon Sep 17 00:00:00 2001 From: prydin Date: Fri, 6 Jul 2018 10:14:33 -0400 Subject: [PATCH 068/100] Added Gopkg --- Gopkg.lock | 38 ++++++++++++++++++++++++++++++++++++-- Gopkg.toml | 4 ++++ 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/Gopkg.lock b/Gopkg.lock index 194bb61e64678..314b801c171b3 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -18,13 +18,13 @@ version = "v0.3.0" [[projects]] + branch = "master" name = "github.com/Microsoft/ApplicationInsights-Go" packages = [ "appinsights", "appinsights/contracts" ] revision = "d2df5d440eda5372f24fcac03839a64d6cb5f7e5" - version = "v0.4.2" [[projects]] name = "github.com/Microsoft/go-winio" @@ -350,6 +350,12 @@ revision = "3af367b6b30c263d47e8895973edcca9a49cf029" version = "v0.2.0" +[[projects]] + name = "github.com/google/uuid" + packages = ["."] + revision = "064e2069ce9c359c118179501254f67d7d37ba24" + version = "0.2" + [[projects]] name = "github.com/gorilla/context" packages = ["."] @@ -730,6 +736,34 @@ revision = "ce01e59abcf6fbc9833b7deb5e4b8ee1769bcc53" version = "v1.0.0" +[[projects]] + name = "github.com/vmware/govmomi" + packages = [ + ".", + "find", + "list", + "nfc", + "object", + "performance", + "property", + "session", + "simulator", + "simulator/esx", + "simulator/vpx", + "task", + "view", + "vim25", + "vim25/debug", + "vim25/methods", + "vim25/mo", + "vim25/progress", + "vim25/soap", + "vim25/types", + "vim25/xml" + ] + revision = "e3a01f9611c32b2362366434bcd671516e78955d" + version = "v0.18.0" + [[projects]] branch = "master" name = "github.com/wvanbergen/kafka" @@ -968,6 +1002,6 @@ [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "024194b983d91b9500fe97e0aa0ddb5fe725030cb51ddfb034e386cae1098370" + inputs-digest = "5201186bc1dbb97c0427ab3cfa769dd5242d21d199a5e97f5a413d775760aa7e" solver-name = "gps-cdcl" solver-version = 1 diff --git a/Gopkg.toml b/Gopkg.toml index 78d3749a99313..b0d94f7debe19 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -241,3 +241,7 @@ [[override]] source = "https://github.com/fsnotify/fsnotify/archive/v1.4.7.tar.gz" name = "gopkg.in/fsnotify.v1" + +[[constraint]] + name = "github.com/vmware/govmomi" + version = "0.18.0" From 6856bb6dee1fe4274dcc18479268b3b03056ae0b Mon Sep 17 00:00:00 2001 From: prydin Date: Fri, 6 Jul 2018 11:38:55 -0400 Subject: [PATCH 069/100] Fixed dependencies --- plugins/inputs/vsphere/client.go | 25 +++++++++++++------------ plugins/inputs/vsphere/pool.go | 20 +++++++++++++++----- 2 files changed, 28 insertions(+), 17 deletions(-) diff --git a/plugins/inputs/vsphere/client.go b/plugins/inputs/vsphere/client.go index 0f0bc3e6458e3..15ec8564adf4d 100644 --- a/plugins/inputs/vsphere/client.go +++ b/plugins/inputs/vsphere/client.go @@ -2,7 +2,6 @@ package vsphere import ( "context" - "fmt" "log" "net/url" @@ -17,6 +16,7 @@ type Client struct { Views *view.Manager Root *view.ContainerView Perf *performance.Manager + Valid bool } // NewClient creates a new vSphere client based on the url and setting passed as parameters. @@ -26,24 +26,24 @@ func NewClient(u *url.URL, vs *VSphere) (*Client, error) { return nil, err } if vs.Username != "" { - if vs.Password == "" { - return nil, fmt.Errorf("vSphere password must be specified") - } log.Printf("D! Logging in using explicit credentials: %s", vs.Username) u.User = url.UserPassword(vs.Username, vs.Password) } ctx := context.Background() - var c *govmomi.Client - if tlsCfg != nil && len(tlsCfg.Certificates) > 0 { - log.Printf("D! Creating client with Certificate: %s", u.Host) - c, err = govmomi.NewClientWithCertificate(ctx, u, vs.InsecureSkipVerify, tlsCfg.Certificates[0]) - } else { - log.Printf("D! Creating client: %s", u.Host) - c, err = govmomi.NewClient(ctx, u, vs.InsecureSkipVerify) - } + + log.Printf("D! Creating client: %s", u.Host) + c, err := govmomi.NewClient(ctx, u, vs.InsecureSkipVerify) if err != nil { return nil, err } + + // Add certificate if we have it + // + if tlsCfg != nil && len(tlsCfg.Certificates) > 0 { + log.Printf("D! Creating client with Certificate: %s", u.Host) + c.Client.SetCertificate(tlsCfg.Certificates[0]) + } + c.Timeout = vs.Timeout.Duration m := view.NewManager(c.Client) @@ -59,6 +59,7 @@ func NewClient(u *url.URL, vs *VSphere) (*Client, error) { Views: m, Root: v, Perf: p, + Valid: true, }, nil } diff --git a/plugins/inputs/vsphere/pool.go b/plugins/inputs/vsphere/pool.go index 0c825db33e555..71771d1305ef5 100644 --- a/plugins/inputs/vsphere/pool.go +++ b/plugins/inputs/vsphere/pool.go @@ -4,11 +4,15 @@ import ( "log" "net/url" "sync" + "time" ) +const ttl time.Duration = time.Minute * 30 // Time before we discard a pooled connection + type poolMember struct { Client *Client next *poolMember + expiry time.Time } // Pool is a simple free-list based pool of vSphere clients @@ -23,11 +27,13 @@ type Pool struct { func (p *Pool) Take() (*Client, error) { p.mux.Lock() defer p.mux.Unlock() - if p.root != nil { + for p.root != nil { r := p.root p.root = r.next - log.Printf("D! //////// Getting connection from pool") - return r.Client, nil + if r.Client.Valid && r.expiry.UnixNano() > time.Now().UnixNano() { + log.Printf("D! //////// Getting connection from pool") + return r.Client, nil + } } // Pool is empty, create a new client! // @@ -37,12 +43,16 @@ func (p *Pool) Take() (*Client, error) { // Return put a client back to the free list func (p *Pool) Return(client *Client) { - if client == nil { + if client == nil || !client.Valid { log.Printf("E! Connection taken out of pool due to error") return // Useful when you want to override a deferred Return } p.mux.Lock() defer p.mux.Unlock() - r := &poolMember{client, p.root} + r := &poolMember{ + Client: client, + next: p.root, + expiry: time.Now().Add(ttl), + } p.root = r } From b11e3a1509b2b3120f61f7649420b8b2a06ac902 Mon Sep 17 00:00:00 2001 From: prydin Date: Thu, 19 Jul 2018 11:53:32 -0400 Subject: [PATCH 070/100] Added configuration parameter metric_count --- Gopkg.lock | 407 ++++++++++++++++++++++++++--- plugins/inputs/vsphere/client.go | 1 - plugins/inputs/vsphere/endpoint.go | 70 +++-- plugins/inputs/vsphere/vsphere.go | 4 +- 4 files changed, 412 insertions(+), 70 deletions(-) diff --git a/Gopkg.lock b/Gopkg.lock index 314b801c171b3..86a9729174b3f 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -3,48 +3,61 @@ [[projects]] branch = "master" + digest = "1:d7582b4af1b0b953ff2bb9573a50f787c7e1669cb148fb086a3d1c670a1ac955" name = "code.cloudfoundry.org/clock" packages = ["."] + pruneopts = "" revision = "02e53af36e6c978af692887ed449b74026d76fec" [[projects]] + digest = "1:ce7dc0f1ffcd9a2aacc50ae6d322eebff8f4faa2d6c5f445c874cd0b77a63de7" name = "collectd.org" packages = [ "api", "cdtime", - "network" + "network", ] + pruneopts = "" revision = "2ce144541b8903101fb8f1483cc0497a68798122" version = "v0.3.0" [[projects]] branch = "master" + digest = "1:c1269bfaddefd090935401c291ad5df6c03de605a440e941ecc568e19f0f9e3b" name = "github.com/Microsoft/ApplicationInsights-Go" packages = [ "appinsights", - "appinsights/contracts" + "appinsights/contracts", ] + pruneopts = "" revision = "d2df5d440eda5372f24fcac03839a64d6cb5f7e5" [[projects]] + digest = "1:e43b98837e21e7f9ef552445e1fcfa92e36bfc0a3e0bd78f2a5e0bef7a4378e3" name = "github.com/Microsoft/go-winio" packages = ["."] + pruneopts = "" revision = "7da180ee92d8bd8bb8c37fc560e673e6557c392f" version = "v0.4.7" [[projects]] + digest = "1:14af5ba5ac88efec490fb59734df34e1bd973198caefa7b0cceed0900ef6164c" name = "github.com/Shopify/sarama" packages = ["."] + pruneopts = "" revision = "35324cf48e33d8260e1c7c18854465a904ade249" version = "v1.17.0" [[projects]] + digest = "1:f82b8ac36058904227087141017bb82f4b0fc58272990a4cdae3e2d6d222644e" name = "github.com/StackExchange/wmi" packages = ["."] + pruneopts = "" revision = "5d049714c4a64225c3c79a7cf7d02f7fb5b96338" version = "1.0.0" [[projects]] + digest = "1:537feef3c3a65e8b924d992df29aacb41f28f7faa4e4610287322bc63e02a33b" name = "github.com/aerospike/aerospike-client-go" packages = [ ".", @@ -57,42 +70,52 @@ "types/atomic", "types/particle_type", "types/rand", - "utils/buffer" + "utils/buffer", ] + pruneopts = "" revision = "c10b5393e43bd60125aca6289c7b24879edb1787" version = "v1.33.0" [[projects]] branch = "master" + digest = "1:1399282ad03ac819f0e8a747c888407c5c98bb497d33821a7047c7bae667ede0" name = "github.com/alecthomas/template" packages = [ ".", - "parse" + "parse", ] + pruneopts = "" revision = "a0175ee3bccc567396460bf5acd36800cb10c49c" [[projects]] branch = "master" + digest = "1:8483994d21404c8a1d489f6be756e25bfccd3b45d65821f25695577791a08e68" name = "github.com/alecthomas/units" packages = ["."] + pruneopts = "" revision = "2efee857e7cfd4f3d0138cc3cbb1b4966962b93a" [[projects]] branch = "master" + digest = "1:072692f8d76356228f31f64ca3140041a140011c7dea26e746206e8649c71b31" name = "github.com/amir/raidman" packages = [ ".", - "proto" + "proto", ] + pruneopts = "" revision = "1ccc43bfb9c93cb401a4025e49c64ba71e5e668b" [[projects]] branch = "master" + digest = "1:0be63b870133f215b6540f82611928cc00b1f6fdf8b63125c1b75a417a88394c" name = "github.com/apache/thrift" packages = ["lib/go/thrift"] + pruneopts = "" revision = "f5f430df56871bc937950274b2c86681d3db6e59" [[projects]] + digest = "1:413443b87e4881a8c9f9b64bca9ed7d96458466f9654aef3b3813e1714f0ef4f" name = "github.com/aws/aws-sdk-go" packages = [ "aws", @@ -124,75 +147,95 @@ "private/protocol/xml/xmlutil", "service/cloudwatch", "service/kinesis", - "service/sts" + "service/sts", ] + pruneopts = "" revision = "bfc1a07cf158c30c41a3eefba8aae043d0bb5bff" version = "v1.14.8" [[projects]] branch = "master" + digest = "1:fca298802a2ab834d6eb0e284788ae037ebc324c0f325ff92c5eea592d189cc5" name = "github.com/beorn7/perks" packages = ["quantile"] + pruneopts = "" revision = "3a771d992973f24aa725d07868b467d1ddfceafb" [[projects]] + digest = "1:0edb96edcfeee9aeba92e605536fbb1542b0bf6a10cea9d0b5a2227d5a703eae" name = "github.com/bsm/sarama-cluster" packages = ["."] + pruneopts = "" revision = "cf455bc755fe41ac9bb2861e7a961833d9c2ecc3" version = "v2.1.13" [[projects]] + digest = "1:f619cb9b07aebe5416262cdd8b86082e8d5bdc5264cb3b615ff858df0b645f97" name = "github.com/cenkalti/backoff" packages = ["."] + pruneopts = "" revision = "2ea60e5f094469f9e65adb9cd103795b73ae743e" version = "v2.0.0" [[projects]] branch = "master" + digest = "1:65ae2d1625584ba8d16d1e15b25db1fc62334e2040f22dbbbdc7531c909843b2" name = "github.com/couchbase/go-couchbase" packages = ["."] + pruneopts = "" revision = "16db1f1fe037412f12738fa4d8448c549c4edd77" [[projects]] branch = "master" + digest = "1:5db54de7054c072f47806c91ef7625ffa00489ca2da5fbc6ca1c78e08018f6bf" name = "github.com/couchbase/gomemcached" packages = [ ".", - "client" + "client", ] + pruneopts = "" revision = "0da75df145308b9a4e6704d762ca9d9b77752efc" [[projects]] branch = "master" + digest = "1:0deaa0f28c823119725c8308703f019797bc077e251d1ed3f2b8eae2cc7791d7" name = "github.com/couchbase/goutils" packages = [ "logging", - "scramsha" + "scramsha", ] + pruneopts = "" revision = "e865a1461c8ac0032bd37e2d4dab3289faea3873" [[projects]] + digest = "1:0a39ec8bf5629610a4bc7873a92039ee509246da3cef1a0ea60f1ed7e5f9cea5" name = "github.com/davecgh/go-spew" packages = ["spew"] + pruneopts = "" revision = "346938d642f2ec3594ed81d874461961cd0faa76" version = "v1.1.0" [[projects]] + digest = "1:2426da75f49e5b8507a6ed5d4c49b06b2ff795f4aec401c106b7db8fb2625cd7" name = "github.com/dgrijalva/jwt-go" packages = ["."] + pruneopts = "" revision = "06ea1031745cb8b3dab3f6a236daf2b0aa468b7e" version = "v3.2.0" [[projects]] + digest = "1:3152d7c68fd4797ca2d96524b7b229cd8237e2c2b2605584ebd736cc7be2a523" name = "github.com/docker/distribution" packages = [ "digest", - "reference" + "reference", ] + pruneopts = "" revision = "48294d928ced5dd9b378f7fd7c6f5da3ff3f2c89" version = "v2.6.2" [[projects]] + digest = "1:2f161cdaf1f3617afa09983c677b1392c4b044f12fc9ccfe9ae65d72dca2e6f7" name = "github.com/docker/docker" packages = [ "api/types", @@ -210,76 +253,96 @@ "api/types/versions", "api/types/volume", "client", - "pkg/tlsconfig" + "pkg/tlsconfig", ] + pruneopts = "" revision = "eef6495eddab52828327aade186443681ed71a4e" version = "v17.03.2-ce-rc1" [[projects]] + digest = "1:5b20afc76a36d3994194e2612e83b51bc2b12db3d4d2a722b24474b2d0e3a890" name = "github.com/docker/go-connections" packages = [ "nat", "sockets", - "tlsconfig" + "tlsconfig", ] + pruneopts = "" revision = "3ede32e2033de7505e6500d6c868c2b9ed9f169d" version = "v0.3.0" [[projects]] + digest = "1:582d54fcb7233da8dde1dfd2210a5b9675d0685f84246a8d317b07d680c18b1b" name = "github.com/docker/go-units" packages = ["."] + pruneopts = "" revision = "47565b4f722fb6ceae66b95f853feed578a4a51c" version = "v0.3.3" [[projects]] + digest = "1:7bbb118aeef9a6b9fef3d57b6cc5378f7cd6e915cabf4dea695e318e1a1bd4e6" name = "github.com/eapache/go-resiliency" packages = ["breaker"] + pruneopts = "" revision = "ea41b0fad31007accc7f806884dcdf3da98b79ce" version = "v1.1.0" [[projects]] branch = "master" + digest = "1:1f7503fa58a852a1416556ae2ddb219b49a1304fd408391948e2e3676514c48d" name = "github.com/eapache/go-xerial-snappy" packages = ["."] + pruneopts = "" revision = "bb955e01b9346ac19dc29eb16586c90ded99a98c" [[projects]] + digest = "1:d8d46d21073d0f65daf1740ebf4629c65e04bf92e14ce93c2201e8624843c3d3" name = "github.com/eapache/queue" packages = ["."] + pruneopts = "" revision = "44cc805cf13205b55f69e14bcb69867d1ae92f98" version = "v1.1.0" [[projects]] + digest = "1:d2e2aebcb8e8027345e16f9d0be8cdee3bb470ba406c7a54cb7457ae3ad4ace5" name = "github.com/eclipse/paho.mqtt.golang" packages = [ ".", - "packets" + "packets", ] + pruneopts = "" revision = "36d01c2b4cbeb3d2a12063e4880ce30800af9560" version = "v1.1.1" [[projects]] + digest = "1:f7c22bf37088211029335cd4d356774eeca0db1af4448e5b8178386090f98dfb" name = "github.com/go-ini/ini" packages = ["."] + pruneopts = "" revision = "06f5f3d67269ccec1fe5fe4134ba6e982984f7f5" version = "v1.37.0" [[projects]] + digest = "1:6a4a01d58b227c4b6b11111b9f172ec5c17682b82724e58e6daf3f19f4faccd8" name = "github.com/go-logfmt/logfmt" packages = ["."] + pruneopts = "" revision = "390ab7935ee28ec6b286364bba9b4dd6410cb3d5" version = "v0.3.0" [[projects]] + digest = "1:c3a5ae14424a38c244439732c31a08b5f956c46c4acdc159fc285a52dbf11de0" name = "github.com/go-ole/go-ole" packages = [ ".", - "oleutil" + "oleutil", ] + pruneopts = "" revision = "a41e3c4b706f6ae8dfbff342b06e40fa4d2d0506" version = "v1.2.1" [[projects]] + digest = "1:f2f6a616a1ca8aed667d956c98f7f6178efe72bbe0a419bd33b9d99841c7de69" name = "github.com/go-redis/redis" packages = [ ".", @@ -289,18 +352,22 @@ "internal/pool", "internal/proto", "internal/singleflight", - "internal/util" + "internal/util", ] + pruneopts = "" revision = "83fb42932f6145ce52df09860384a4653d2d332a" version = "v6.12.0" [[projects]] + digest = "1:dc876ae7727280d95f97af5320308131278b93d6c6f5cf953065e18cb8c88fd2" name = "github.com/go-sql-driver/mysql" packages = ["."] + pruneopts = "" revision = "d523deb1b23d913de5bdada721a6071e71283618" version = "v1.4.0" [[projects]] + digest = "1:b7a7e17513aeee6492d93015c7bf29c86a0c1c91210ea56b21e36c1a40958cba" name = "github.com/gobwas/glob" packages = [ ".", @@ -310,131 +377,165 @@ "syntax/ast", "syntax/lexer", "util/runes", - "util/strings" + "util/strings", ] + pruneopts = "" revision = "5ccd90ef52e1e632236f7326478d4faa74f99438" version = "v0.2.3" [[projects]] + digest = "1:602c5827bcd5eca47c041a39aff7b626dda114da57a0334a9230bdc5cf8ed3ed" name = "github.com/gogo/protobuf" packages = ["proto"] + pruneopts = "" revision = "1adfc126b41513cc696b209667c8656ea7aac67c" version = "v1.0.0" [[projects]] + digest = "1:b1d3041d568e065ab4d76f7477844458e9209c0bb241eaccdc0770bf0a13b120" name = "github.com/golang/protobuf" packages = [ "proto", "ptypes", "ptypes/any", "ptypes/duration", - "ptypes/timestamp" + "ptypes/timestamp", ] + pruneopts = "" revision = "b4deda0973fb4c70b50d226b1af49f3da59f5265" version = "v1.1.0" [[projects]] branch = "master" + digest = "1:075128b9fc42e6d99067da1a2e6c0a634a6043b5a60abe6909c51f5ecad37b6d" name = "github.com/golang/snappy" packages = ["."] + pruneopts = "" revision = "2e65f85255dbc3072edf28d6b5b8efc472979f5a" [[projects]] + digest = "1:cc082d7b9cc3f832f2aed9d06d1cbb33b6984a61d8ec403535b086415c181607" name = "github.com/google/go-cmp" packages = [ "cmp", "cmp/internal/diff", "cmp/internal/function", - "cmp/internal/value" + "cmp/internal/value", ] + pruneopts = "" revision = "3af367b6b30c263d47e8895973edcca9a49cf029" version = "v0.2.0" [[projects]] + digest = "1:c1d7e883c50a26ea34019320d8ae40fad86c9e5d56e63a1ba2cb618cef43e986" name = "github.com/google/uuid" packages = ["."] + pruneopts = "" revision = "064e2069ce9c359c118179501254f67d7d37ba24" version = "0.2" [[projects]] + digest = "1:dbbeb8ddb0be949954c8157ee8439c2adfd8dc1c9510eb44a6e58cb68c3dce28" name = "github.com/gorilla/context" packages = ["."] + pruneopts = "" revision = "08b5f424b9271eedf6f9f0ce86cb9396ed337a42" version = "v1.1.1" [[projects]] + digest = "1:c2c8666b4836c81a1d247bdf21c6a6fc1ab586538ab56f74437c2e0df5c375e1" name = "github.com/gorilla/mux" packages = ["."] + pruneopts = "" revision = "e3702bed27f0d39777b0b37b664b6280e8ef8fbf" version = "v1.6.2" [[projects]] branch = "master" + digest = "1:60b7bc5e043a11213472ae05252527287d20e0a6ccc18f6ae67fad88e41004de" name = "github.com/hailocab/go-hostpool" packages = ["."] + pruneopts = "" revision = "e80d13ce29ede4452c43dea11e79b9bc8a15b478" [[projects]] + digest = "1:69f3f1b3e95b8da5b2e3deb0fc821f7641557e5d500857ec35a3822c8b8b782f" name = "github.com/hashicorp/consul" packages = ["api"] + pruneopts = "" revision = "5174058f0d2bda63fa5198ab96c33d9a909c58ed" version = "v1.1.0" [[projects]] branch = "master" + digest = "1:f5d25fd7bdda08e39e01193ef94a1ebf7547b1b931bcdec785d08050598f306c" name = "github.com/hashicorp/go-cleanhttp" packages = ["."] + pruneopts = "" revision = "d5fe4b57a186c716b0e00b8c301cbd9b4182694d" [[projects]] branch = "master" + digest = "1:cd5813053beac0114f96a7da3924fc8a15e0cd2b139f079e0fcce5d3244ae304" name = "github.com/hashicorp/go-rootcerts" packages = ["."] + pruneopts = "" revision = "6bb64b370b90e7ef1fa532be9e591a81c3493e00" [[projects]] + digest = "1:d2b2cff454cb23a9769ef3c9075741f5985773a998584b3b3ce203fe4b1abbea" name = "github.com/hashicorp/serf" packages = ["coordinate"] + pruneopts = "" revision = "d6574a5bb1226678d7010325fb6c985db20ee458" version = "v0.8.1" [[projects]] + digest = "1:cc0cf2e12280074e5c6dc0f15a4bb3d6c43509e6091cdcdcc83eea491577257b" name = "github.com/influxdata/go-syslog" packages = [ "rfc5424", - "rfc5425" + "rfc5425", ] + pruneopts = "" revision = "eecd51df3ad85464a2bab9b7d3a45bc1e299059e" version = "v1.0.1" [[projects]] branch = "master" + digest = "1:effc58ad45323ad15159bbca533be4870eaddb2d9a513d3488d8bfe822c83532" name = "github.com/influxdata/tail" packages = [ ".", "ratelimiter", "util", "watch", - "winfile" + "winfile", ] + pruneopts = "" revision = "c43482518d410361b6c383d7aebce33d0471d7bc" [[projects]] branch = "master" + digest = "1:d31edcf33a3b36218de96e43f3fec18ea96deb2a28b838a3a01a4df856ded345" name = "github.com/influxdata/toml" packages = [ ".", - "ast" + "ast", ] + pruneopts = "" revision = "2a2e3012f7cfbef64091cc79776311e65dfa211b" [[projects]] branch = "master" + digest = "1:a0c157916be0b4de1d4565b1f094b8d746109f94968140dff40a42780fa6ccef" name = "github.com/influxdata/wlog" packages = ["."] + pruneopts = "" revision = "7c63b0a71ef8300adc255344d275e10e5c3a71ec" [[projects]] + digest = "1:4197871f269749786aa2406557dba15f10cf79161cdc3998180614c62c8b6351" name = "github.com/jackc/pgx" packages = [ ".", @@ -443,138 +544,176 @@ "pgio", "pgproto3", "pgtype", - "stdlib" + "stdlib", ] + pruneopts = "" revision = "da3231b0b66e2e74cdb779f1d46c5e958ba8be27" version = "v3.1.0" [[projects]] + digest = "1:4f767a115bc8e08576f6d38ab73c376fc1b1cd3bb5041171c9e8668cc7739b52" name = "github.com/jmespath/go-jmespath" packages = ["."] + pruneopts = "" revision = "0b12d6b5" [[projects]] branch = "master" + digest = "1:2c5ad58492804c40bdaf5d92039b0cde8b5becd2b7feeb37d7d1cc36a8aa8dbe" name = "github.com/kardianos/osext" packages = ["."] + pruneopts = "" revision = "ae77be60afb1dcacde03767a8c37337fad28ac14" [[projects]] branch = "master" + digest = "1:2df59f23f11c5c59982f737c98c5523b276bfc85a4773a04b411190402bb30fd" name = "github.com/kardianos/service" packages = ["."] + pruneopts = "" revision = "615a14ed75099c9eaac6949e22ac2341bf9d3197" [[projects]] branch = "master" + digest = "1:63e7368fcf6b54804076eaec26fd9cf0c4466166b272393db4b93102e1e962df" name = "github.com/kballard/go-shellquote" packages = ["."] + pruneopts = "" revision = "95032a82bc518f77982ea72343cc1ade730072f0" [[projects]] branch = "master" + digest = "1:1ed9eeebdf24aadfbca57eb50e6455bd1d2474525e0f0d4454de8c8e9bc7ee9a" name = "github.com/kr/logfmt" packages = ["."] + pruneopts = "" revision = "b84e30acd515aadc4b783ad4ff83aff3299bdfe0" [[projects]] branch = "master" + digest = "1:175f85474bb8dd4ef02b1fad5540345545b78b192e64b961e99e607cce32a716" name = "github.com/mailru/easyjson" packages = [ ".", "buffer", "jlexer", - "jwriter" + "jwriter", ] + pruneopts = "" revision = "3fdea8d05856a0c8df22ed4bc71b3219245e4485" [[projects]] + digest = "1:49a8b01a6cd6558d504b65608214ca40a78000e1b343ed0da5c6a9ccd83d6d30" name = "github.com/matttproud/golang_protobuf_extensions" packages = ["pbutil"] + pruneopts = "" revision = "c12348ce28de40eed0136aa2b644d0ee0650e56c" version = "v1.0.1" [[projects]] + digest = "1:f0bad0fece0fb73c6ea249c18d8e80ffbe86be0457715b04463068f04686cf39" name = "github.com/miekg/dns" packages = ["."] + pruneopts = "" revision = "5a2b9fab83ff0f8bfc99684bd5f43a37abe560f1" version = "v1.0.8" [[projects]] branch = "master" + digest = "1:99651e95333755cbe5c9768c1b80031300acca64a80870b40309202b32585a5a" name = "github.com/mitchellh/go-homedir" packages = ["."] + pruneopts = "" revision = "3864e76763d94a6df2f9960b16a20a33da9f9a66" [[projects]] branch = "master" + digest = "1:eb9117392ee8e7aa44f78e0db603f70b1050ee0ebda4bd40040befb5b218c546" name = "github.com/mitchellh/mapstructure" packages = ["."] + pruneopts = "" revision = "bb74f1db0675b241733089d5a1faa5dd8b0ef57b" [[projects]] + digest = "1:ee2e62b00a9ccc2dba1525f93396e35c847f90f87939df6f361b86315ea5f69a" name = "github.com/multiplay/go-ts3" packages = ["."] + pruneopts = "" revision = "d0d44555495c8776880a17e439399e715a4ef319" version = "v1.0.0" [[projects]] + digest = "1:ccd0def9f0b82b61c5e54fcbfccf528eabb13b489d008e46dc16b808c2e1f765" name = "github.com/naoina/go-stringutil" packages = ["."] + pruneopts = "" revision = "6b638e95a32d0c1131db0e7fe83775cbea4a0d0b" version = "v0.1.0" [[projects]] + digest = "1:7196e09de0e0e057aad363ae7c5a83dba55da7c3865ac1ddabdcd39a72aeb27e" name = "github.com/nats-io/gnatsd" packages = [ "conf", "logger", "server", "server/pse", - "util" + "util", ] + pruneopts = "" revision = "add6d7930ae6d4bff8823b28999ea87bf1bfd23d" version = "v1.1.0" [[projects]] + digest = "1:88f1bde4c172e27b05ed46adfbd0e79dc1663a6281e4b39fa3e39d71ead9621d" name = "github.com/nats-io/go-nats" packages = [ ".", "encoders/builtin", - "util" + "util", ] + pruneopts = "" revision = "062418ea1c2181f52dc0f954f6204370519a868b" version = "v1.5.0" [[projects]] + digest = "1:be61e8224b84064109eaba8157cbb4bbe6ca12443e182b6624fdfa1c0dcf53d9" name = "github.com/nats-io/nuid" packages = ["."] + pruneopts = "" revision = "289cccf02c178dc782430d534e3c1f5b72af807f" version = "v1.0.0" [[projects]] + digest = "1:501cce26a54c785458b0dd54a08ddd984d4ad0c198255430d5d37cd2efe23149" name = "github.com/nsqio/go-nsq" packages = ["."] + pruneopts = "" revision = "eee57a3ac4174c55924125bb15eeeda8cffb6e6f" version = "v1.0.7" [[projects]] branch = "master" + digest = "1:2da0e5077ed40453dc281b9a2428d84cf6ad14063aed189f6296ca5dd25cf13d" name = "github.com/opentracing-contrib/go-observer" packages = ["."] + pruneopts = "" revision = "a52f2342449246d5bcc273e65cbdcfa5f7d6c63c" [[projects]] + digest = "1:bba12aa4747b212f75db3e7fee73fe1b66d303cb3ff0c1984b7f2ad20e8bd2bc" name = "github.com/opentracing/opentracing-go" packages = [ ".", "ext", - "log" + "log", ] + pruneopts = "" revision = "1949ddbfd147afd4d964a9f00b24eb291e0e7c38" version = "v1.0.2" [[projects]] + digest = "1:c6c0db6294924072f98a0de090d200bae4b7102b12a443ba9569c4ba7df52aa1" name = "github.com/openzipkin/zipkin-go-opentracing" packages = [ ".", @@ -582,88 +721,110 @@ "thrift/gen-go/scribe", "thrift/gen-go/zipkincore", "types", - "wire" + "wire", ] + pruneopts = "" revision = "26cf9707480e6b90e5eff22cf0bbf05319154232" version = "v0.3.4" [[projects]] + digest = "1:681dd6745809b277541914db18b4ba159b7f1158a0e37627ce8081d5291fc1ec" name = "github.com/pierrec/lz4" packages = [ ".", - "internal/xxh32" + "internal/xxh32", ] + pruneopts = "" revision = "6b9367c9ff401dbc54fabce3fb8d972e799b702d" version = "v2.0.2" [[projects]] + digest = "1:7365acd48986e205ccb8652cc746f09c8b7876030d53710ea6ef7d0bd0dcd7ca" name = "github.com/pkg/errors" packages = ["."] + pruneopts = "" revision = "645ef00459ed84a119197bfb8d8205042c6df63d" version = "v0.8.0" [[projects]] + digest = "1:256484dbbcd271f9ecebc6795b2df8cad4c458dd0f5fd82a8c2fa0c29f233411" name = "github.com/pmezard/go-difflib" packages = ["difflib"] + pruneopts = "" revision = "792786c7400a136282c1664665ae0a8db921c6c2" version = "v1.0.0" [[projects]] + digest = "1:981835985f655d1d380cc6aa7d9fa9ad7abfaf40c75da200fd40d864cd05a7c3" name = "github.com/prometheus/client_golang" packages = [ "prometheus", - "prometheus/promhttp" + "prometheus/promhttp", ] + pruneopts = "" revision = "c5b7fccd204277076155f10851dad72b76a49317" version = "v0.8.0" [[projects]] branch = "master" + digest = "1:83bf37d060fca77e959fe5ceee81e58bbd1b01836f4addc70043a948e9912547" name = "github.com/prometheus/client_model" packages = ["go"] + pruneopts = "" revision = "99fa1f4be8e564e8a6b613da7fa6f46c9edafc6c" [[projects]] branch = "master" + digest = "1:6a8420870eb2935977da1fff0f3afca9bdb3f1e66258c9e91a8a7ce0b5417c3b" name = "github.com/prometheus/common" packages = [ "expfmt", "internal/bitbucket.org/ww/goautoneg", "log", - "model" + "model", ] + pruneopts = "" revision = "7600349dcfe1abd18d72d3a1770870d9800a7801" [[projects]] branch = "master" + digest = "1:f691cc506fb101ec4d3aacf7ff7bee68419d5b75cc1d3d5be25324bbaa58529a" name = "github.com/prometheus/procfs" packages = [ ".", "internal/util", "nfs", - "xfs" + "xfs", ] + pruneopts = "" revision = "7d6f385de8bea29190f15ba9931442a0eaef9af7" [[projects]] branch = "master" + digest = "1:1b65925989a4dfb6d98ef1d530cda33ab1ff25945b14a22a8b8bb27cc282af70" name = "github.com/rcrowley/go-metrics" packages = ["."] + pruneopts = "" revision = "e2704e165165ec55d062f5919b4b29494e9fa790" [[projects]] branch = "master" + digest = "1:d8fe9f454582e04b5693b59cdebe3f0bd9dc29ad9651bfb1633cba4658b66c65" name = "github.com/samuel/go-zookeeper" packages = ["zk"] + pruneopts = "" revision = "c4fab1ac1bec58281ad0667dc3f0907a9476ac47" [[projects]] + digest = "1:7f569d906bdd20d906b606415b7d794f798f91a62fcfb6a4daa6d50690fb7a3f" name = "github.com/satori/go.uuid" packages = ["."] + pruneopts = "" revision = "f58768cc1a7a7e77a3bd49e98cdd21419399b6a3" version = "v1.2.0" [[projects]] + digest = "1:ad351e057e5b6deef320ac9e8322ed825aba3542229da71e708576b01fa83b6e" name = "github.com/shirou/gopsutil" packages = [ "cpu", @@ -673,70 +834,90 @@ "load", "mem", "net", - "process" + "process", ] + pruneopts = "" revision = "eeb1d38d69593f121e060d24d17f7b1f0936b203" version = "v2.18.05" [[projects]] branch = "master" + digest = "1:99c6a6dab47067c9b898e8c8b13d130c6ab4ffbcc4b7cc6236c2cd0b1e344f5b" name = "github.com/shirou/w32" packages = ["."] + pruneopts = "" revision = "bb4de0191aa41b5507caa14b0650cdbddcd9280b" [[projects]] + digest = "1:f2cc92b78b2f3b76ab0f9daddddd28627bcfcc6cacf119029aa3850082d95079" name = "github.com/sirupsen/logrus" packages = ["."] + pruneopts = "" revision = "c155da19408a8799da419ed3eeb0cb5db0ad5dbc" version = "v1.0.5" [[projects]] branch = "master" + digest = "1:1df855e31f96fac0b7a18115e029bb6ca1c5f40996a06e07bf1ce524d925d454" name = "github.com/soniah/gosnmp" packages = ["."] + pruneopts = "" revision = "bcf840db66be7d64bf96c3c0e075c92e3d98f793" [[projects]] branch = "master" + digest = "1:0a1f8d01a0191f558910bcbfd7e1dc11a53ac374473d13b68b8fe520f21efb07" name = "github.com/streadway/amqp" packages = ["."] + pruneopts = "" revision = "e5adc2ada8b8efff032bf61173a233d143e9318e" [[projects]] + digest = "1:34062a2274daa6ec4d2f50d6070cc51cf4674d6d553ed76b406cb3425b9528e8" name = "github.com/stretchr/objx" packages = ["."] + pruneopts = "" revision = "477a77ecc69700c7cdeb1fa9e129548e1c1c393c" version = "v0.1.1" [[projects]] + digest = "1:bc2a12c8863e1080226b7bc69192efd6c37aaa9b85cec508b0a8f54fabb9bd9f" name = "github.com/stretchr/testify" packages = [ "assert", "mock", - "require" + "require", ] + pruneopts = "" revision = "f35b8ab0b5a2cef36673838d662e249dd9c94686" version = "v1.2.2" [[projects]] + digest = "1:bcc2f7db4796010b35ccd2425fe46849096a4e98ed53f572567cb76de1187b3a" name = "github.com/tidwall/gjson" packages = ["."] + pruneopts = "" revision = "afaeb9562041a8018c74e006551143666aed08bf" version = "v1.1.1" [[projects]] branch = "master" + digest = "1:4db4f92bb9cb04cfc4fccb36aba2598b02a988008c4cc0692b241214ad8ac96e" name = "github.com/tidwall/match" packages = ["."] + pruneopts = "" revision = "1731857f09b1f38450e2c12409748407822dc6be" [[projects]] + digest = "1:23e2b9f3a20cd4a6427147377255ec2f6237e8606fa6ef0707ed79b7bfbe3a83" name = "github.com/vjeantet/grok" packages = ["."] + pruneopts = "" revision = "ce01e59abcf6fbc9833b7deb5e4b8ee1769bcc53" version = "v1.0.0" [[projects]] + digest = "1:e27a90b038891891bb33cb72cb30b8f1b30307e99fd0caf18d3073ac730d4160" name = "github.com/vmware/govmomi" packages = [ ".", @@ -759,42 +940,52 @@ "vim25/progress", "vim25/soap", "vim25/types", - "vim25/xml" + "vim25/xml", ] + pruneopts = "" revision = "e3a01f9611c32b2362366434bcd671516e78955d" version = "v0.18.0" [[projects]] branch = "master" + digest = "1:5383edd40c7f6c95a7dc46a47bf0c83de4bf40a4252f12fa803f790037addffc" name = "github.com/wvanbergen/kafka" packages = ["consumergroup"] + pruneopts = "" revision = "e2edea948ddfee841ea9a263b32ccca15f7d6c2f" [[projects]] branch = "master" + digest = "1:f936b4936e1b092cc41c9b33fdc990ad78386545f1ffeca8427c72b2605bca85" name = "github.com/wvanbergen/kazoo-go" packages = ["."] + pruneopts = "" revision = "f72d8611297a7cf105da904c04198ad701a60101" [[projects]] branch = "master" + digest = "1:9dcb103385e63db2f0eb7e6a8fa2103acddcfb48b25957959f56a0f15188674b" name = "github.com/yuin/gopher-lua" packages = [ ".", "ast", "parse", - "pm" + "pm", ] + pruneopts = "" revision = "ca850f594eaafa5468da2bd53b865e4ee55be18b" [[projects]] branch = "master" + digest = "1:84e9087a94f336c204887281046891769d2ed7bf1d2b31c21ff6fb5e1743abce" name = "github.com/zensqlmonitor/go-mssqldb" packages = ["."] + pruneopts = "" revision = "e8fbf836e44e86764eba398361d1825651709547" [[projects]] branch = "master" + digest = "1:dec0d91bef11bc02ba7d1f72bf251a8309c2e55a0fbea699195d037406c219b0" name = "golang.org/x/crypto" packages = [ "bcrypt", @@ -803,12 +994,14 @@ "ed25519/internal/edwards25519", "md4", "pbkdf2", - "ssh/terminal" + "ssh/terminal", ] + pruneopts = "" revision = "027cca12c2d63e3d62b670d901e8a2c95854feec" [[projects]] branch = "master" + digest = "1:3edb9c19d0b874999053badbbcc08edab3cde0262d2beb36ad6c0d78391c19ac" name = "golang.org/x/net" packages = [ "bpf", @@ -829,12 +1022,14 @@ "ipv6", "proxy", "trace", - "websocket" + "websocket", ] + pruneopts = "" revision = "db08ff08e8622530d9ed3a0e8ac279f6d4c02196" [[projects]] branch = "master" + digest = "1:a90f95e710f8986cec0fc8ca053fa984f3e2e8963941de2e2ab44e565f37b30a" name = "golang.org/x/sys" packages = [ "unix", @@ -843,11 +1038,13 @@ "windows/svc", "windows/svc/debug", "windows/svc/eventlog", - "windows/svc/mgr" + "windows/svc/mgr", ] + pruneopts = "" revision = "6c888cc515d3ed83fc103cf1d84468aad274b0a7" [[projects]] + digest = "1:af9bfca4298ef7502c52b1459df274eed401a4f5498b900e9a92d28d3d87ac5a" name = "golang.org/x/text" packages = [ "collate", @@ -875,24 +1072,30 @@ "unicode/bidi", "unicode/cldr", "unicode/norm", - "unicode/rangetable" + "unicode/rangetable", ] + pruneopts = "" revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0" version = "v0.3.0" [[projects]] + digest = "1:eede11c81b63c8f6fd06ef24ba0a640dc077196ec9b7a58ecde03c82eee2f151" name = "google.golang.org/appengine" packages = ["cloudsql"] + pruneopts = "" revision = "b1f26356af11148e710935ed1ac8a7f5702c7612" version = "v1.1.0" [[projects]] branch = "master" + digest = "1:bca49d2bc942d5839d13a2ee882e6475b96cc5352b549000ecafa13dbd158265" name = "google.golang.org/genproto" packages = ["googleapis/rpc/status"] + pruneopts = "" revision = "32ee49c4dd805befd833990acba36cb75042378c" [[projects]] + digest = "1:6648dc575e3043e47e6bc3a41054a7468dbeef331898eae513df19af09765c30" name = "google.golang.org/grpc" packages = [ ".", @@ -918,90 +1121,208 @@ "stats", "status", "tap", - "transport" + "transport", ] + pruneopts = "" revision = "7a6a684ca69eb4cae85ad0a484f2e531598c047b" version = "v1.12.2" [[projects]] + digest = "1:2840683aa0e9980689f85bf48b2a56ec7a108fd089f12af8ea7d98c172819589" name = "gopkg.in/alecthomas/kingpin.v2" packages = ["."] + pruneopts = "" revision = "947dcec5ba9c011838740e680966fd7087a71d0d" version = "v2.2.6" [[projects]] + digest = "1:a8f8c1725195c4324d4350fae001524ca7489e40d9b6bb47598772e3faa103ba" name = "gopkg.in/asn1-ber.v1" packages = ["."] + pruneopts = "" revision = "379148ca0225df7a432012b8df0355c2a2063ac0" version = "v1.2" [[projects]] + digest = "1:581450ae66d7970d91ef9132459fa583e937c6e502f1b96e4ee7783a56fa0b44" name = "gopkg.in/fatih/pool.v2" packages = ["."] + pruneopts = "" revision = "010e0b745d12eaf8426c95f9c3924d81dd0b668f" version = "v2.0.0" [[projects]] + digest = "1:b2106f1668ea5efc1ecc480f7e922a093adb9563fd9ce58585292871f0d0f229" name = "gopkg.in/fsnotify.v1" packages = ["."] + pruneopts = "" revision = "c2828203cd70a50dcccfb2761f8b1f8ceef9a8e9" source = "https://github.com/fsnotify/fsnotify/archive/v1.4.7.tar.gz" version = "v1.4.7" [[projects]] + digest = "1:5fa5df18f3bd9cad28ed7f263b15da217945735110898fa2b9af25cdafb9cbf3" name = "gopkg.in/gorethink/gorethink.v3" packages = [ ".", "encoding", "ql2", - "types" + "types", ] + pruneopts = "" revision = "7f5bdfd858bb064d80559b2a32b86669c5de5d3b" version = "v3.0.5" [[projects]] + digest = "1:74163d1887c0821951e6f1795a1d10338f45f09d9067cb4a8edcf7ee481724ee" name = "gopkg.in/ldap.v2" packages = ["."] + pruneopts = "" revision = "bb7a9ca6e4fbc2129e3db588a34bc970ffe811a9" version = "v2.5.1" [[projects]] branch = "v2" + digest = "1:95fc0dfa0995fff96935315321df859c5c6b1b81df687ed87a2dcf1767f1b465" name = "gopkg.in/mgo.v2" packages = [ ".", "bson", "internal/json", "internal/sasl", - "internal/scram" + "internal/scram", ] + pruneopts = "" revision = "3f83fa5005286a7fe593b055f0d7771a7dce4655" [[projects]] + digest = "1:e7f4f325f729e4c0e93fdac23b05946ab5f9761af4a61615144024774a25b0cb" name = "gopkg.in/olivere/elastic.v5" packages = [ ".", "config", - "uritemplates" + "uritemplates", ] + pruneopts = "" revision = "b708306d715bea9b983685e94ab4602cdc9f988b" version = "v5.0.69" [[projects]] branch = "v1" + digest = "1:a96d16bd088460f2e0685d46c39bcf1208ba46e0a977be2df49864ec7da447dd" name = "gopkg.in/tomb.v1" packages = ["."] + pruneopts = "" revision = "dd632973f1e7218eb1089048e0798ec9ae7dceb8" [[projects]] + digest = "1:f0620375dd1f6251d9973b5f2596228cc8042e887cd7f827e4220bc1ce8c30e2" name = "gopkg.in/yaml.v2" packages = ["."] + pruneopts = "" revision = "5420a8b6744d3b0345ab293f6fcba19c978f1183" version = "v2.2.1" [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "5201186bc1dbb97c0427ab3cfa769dd5242d21d199a5e97f5a413d775760aa7e" + input-imports = [ + "collectd.org/api", + "collectd.org/network", + "github.com/Microsoft/ApplicationInsights-Go/appinsights", + "github.com/Shopify/sarama", + "github.com/StackExchange/wmi", + "github.com/aerospike/aerospike-client-go", + "github.com/amir/raidman", + "github.com/apache/thrift/lib/go/thrift", + "github.com/aws/aws-sdk-go/aws", + "github.com/aws/aws-sdk-go/aws/client", + "github.com/aws/aws-sdk-go/aws/credentials", + "github.com/aws/aws-sdk-go/aws/credentials/stscreds", + "github.com/aws/aws-sdk-go/aws/session", + "github.com/aws/aws-sdk-go/service/cloudwatch", + "github.com/aws/aws-sdk-go/service/kinesis", + "github.com/aws/aws-sdk-go/service/sts", + "github.com/bsm/sarama-cluster", + "github.com/couchbase/go-couchbase", + "github.com/dgrijalva/jwt-go", + "github.com/docker/docker/api/types", + "github.com/docker/docker/api/types/container", + "github.com/docker/docker/api/types/filters", + "github.com/docker/docker/api/types/registry", + "github.com/docker/docker/api/types/swarm", + "github.com/docker/docker/client", + "github.com/docker/go-connections/sockets", + "github.com/eclipse/paho.mqtt.golang", + "github.com/go-redis/redis", + "github.com/go-sql-driver/mysql", + "github.com/gobwas/glob", + "github.com/golang/protobuf/proto", + "github.com/google/go-cmp/cmp", + "github.com/gorilla/mux", + "github.com/hashicorp/consul/api", + "github.com/influxdata/go-syslog/rfc5424", + "github.com/influxdata/go-syslog/rfc5425", + "github.com/influxdata/tail", + "github.com/influxdata/toml", + "github.com/influxdata/toml/ast", + "github.com/influxdata/wlog", + "github.com/jackc/pgx/stdlib", + "github.com/kardianos/service", + "github.com/kballard/go-shellquote", + "github.com/matttproud/golang_protobuf_extensions/pbutil", + "github.com/miekg/dns", + "github.com/multiplay/go-ts3", + "github.com/nats-io/gnatsd/server", + "github.com/nats-io/go-nats", + "github.com/nsqio/go-nsq", + "github.com/openzipkin/zipkin-go-opentracing", + "github.com/openzipkin/zipkin-go-opentracing/thrift/gen-go/zipkincore", + "github.com/prometheus/client_golang/prometheus", + "github.com/prometheus/client_golang/prometheus/promhttp", + "github.com/prometheus/client_model/go", + "github.com/prometheus/common/expfmt", + "github.com/prometheus/common/log", + "github.com/satori/go.uuid", + "github.com/shirou/gopsutil/cpu", + "github.com/shirou/gopsutil/disk", + "github.com/shirou/gopsutil/host", + "github.com/shirou/gopsutil/load", + "github.com/shirou/gopsutil/mem", + "github.com/shirou/gopsutil/net", + "github.com/shirou/gopsutil/process", + "github.com/soniah/gosnmp", + "github.com/streadway/amqp", + "github.com/stretchr/testify/assert", + "github.com/stretchr/testify/mock", + "github.com/stretchr/testify/require", + "github.com/tidwall/gjson", + "github.com/vjeantet/grok", + "github.com/vmware/govmomi", + "github.com/vmware/govmomi/performance", + "github.com/vmware/govmomi/simulator", + "github.com/vmware/govmomi/view", + "github.com/vmware/govmomi/vim25/mo", + "github.com/vmware/govmomi/vim25/soap", + "github.com/vmware/govmomi/vim25/types", + "github.com/wvanbergen/kafka/consumergroup", + "github.com/zensqlmonitor/go-mssqldb", + "golang.org/x/net/context", + "golang.org/x/net/html/charset", + "golang.org/x/sys/unix", + "golang.org/x/sys/windows", + "golang.org/x/sys/windows/svc", + "golang.org/x/sys/windows/svc/mgr", + "google.golang.org/grpc", + "google.golang.org/grpc/codes", + "google.golang.org/grpc/credentials", + "google.golang.org/grpc/status", + "gopkg.in/gorethink/gorethink.v3", + "gopkg.in/ldap.v2", + "gopkg.in/mgo.v2", + "gopkg.in/mgo.v2/bson", + "gopkg.in/olivere/elastic.v5", + "gopkg.in/yaml.v2", + ] solver-name = "gps-cdcl" solver-version = 1 diff --git a/plugins/inputs/vsphere/client.go b/plugins/inputs/vsphere/client.go index 15ec8564adf4d..aa759963c3b70 100644 --- a/plugins/inputs/vsphere/client.go +++ b/plugins/inputs/vsphere/client.go @@ -40,7 +40,6 @@ func NewClient(u *url.URL, vs *VSphere) (*Client, error) { // Add certificate if we have it // if tlsCfg != nil && len(tlsCfg.Certificates) > 0 { - log.Printf("D! Creating client with Certificate: %s", u.Host) c.Client.SetCertificate(tlsCfg.Certificates[0]) } diff --git a/plugins/inputs/vsphere/endpoint.go b/plugins/inputs/vsphere/endpoint.go index 80c0da774a691..5c04f257cfae9 100644 --- a/plugins/inputs/vsphere/endpoint.go +++ b/plugins/inputs/vsphere/endpoint.go @@ -208,7 +208,7 @@ func (e *Endpoint) discover() error { if res.enabled || (k != "datastore" && k != "vm") { objects, err := res.getObjects(client.Root) if err != nil { - client = nil // Don't reuse this one! + client.Valid = false // Don't reuse this one! return err } for _, obj := range objects { @@ -216,7 +216,7 @@ func (e *Endpoint) discover() error { mList := make(performance.MetricList, 0) metrics, err := client.Perf.AvailableMetric(ctx, obj.ref.Reference(), res.sampling) if err != nil { - client = nil // Don't reuse this one! + client.Valid = false // Don't reuse this one! return err } log.Printf("D! Obj: %s, metrics found: %d, enabled: %t", obj.name, len(metrics), res.enabled) @@ -394,19 +394,28 @@ func (e *Endpoint) collectResource(resourceType string, acc telegraf.Accumulator count := 0 start := time.Now() - total := 0 pqs := make([]types.PerfQuerySpec, 0, e.Parent.ObjectsPerQuery) + metrics := 0 + total := 0 + nRes := 0 for _, object := range res.objects { info, found := e.instanceInfo[object.ref.Value] if !found { log.Printf("E! Internal error: Instance info not found for MOID %s", object.ref) } - if len(info.metrics) > 0 { - + mr := len(info.metrics) + for mr > 0 { + mc := mr + headroom := e.Parent.MetricsPerQuery - metrics + if !res.realTime && mc > headroom { // Metric query limit only applies to non-realtime metrics + mc = headroom + } + fm := len(info.metrics) - mr + log.Printf("D! mr: %d, mm: %d, fm: %d, headroom before add: %d", mr, mc, fm, headroom) pq := types.PerfQuerySpec{ Entity: object.ref, MaxSample: 1, - MetricId: info.metrics, + MetricId: info.metrics[fm : fm+mc], IntervalId: res.sampling, } @@ -414,26 +423,37 @@ func (e *Endpoint) collectResource(resourceType string, acc telegraf.Accumulator pq.StartTime = &latest pq.EndTime = &now } - pqs = append(pqs, pq) - } else { - log.Printf("D! No metrics available for %s. Skipping.", info.name) - // Maintainers: Don't be tempted to skip a turn in the loop here! We still need to check if - // the chunk needs processing or we risk skipping the last chunk. (Ask me how I found this out... :) ) - } - total++ + mr -= mc + metrics += mc - // Filled up a chunk or at end of data? Run a query with the collected objects - // - if len(pqs) > 0 && (len(pqs) >= int(e.Parent.ObjectsPerQuery) || total >= len(res.objects)) { - log.Printf("D! Querying %d objects of type %s for %s. Object count: %d. Total objects %d", len(pqs), resourceType, e.URL.Host, total, len(res.objects)) - n, err := e.collectChunk(pqs, resourceType, res, acc) - if err != nil { - return count, time.Now().Sub(start).Seconds(), err + // We need to dump the current chunk of metrics for one of three reasons: + // 1) We filled up the metric quota while processing the current resource + // 2) The toral number of metrics exceeds the max + // 3) We are at the last resource and have no more data to process. + // + if mr > 0 || (!res.realTime && metrics >= e.Parent.MetricsPerQuery) || total >= len(res.objects)-1 || nRes >= e.Parent.ObjectsPerQuery { + log.Printf("D! Querying %d objects, %d metrics (%d remaining) of type %s for %s. Processed objects: %d. Total objects %d, metrics %d", + len(pqs), metrics, mr, resourceType, e.URL.Host, total+1, len(res.objects), count) + n, err := e.collectChunk(pqs, resourceType, res, acc) + if err != nil { + return count, time.Now().Sub(start).Seconds(), err + } + log.Printf("D! Query returned %d metrics", n) + count += n + pqs = make([]types.PerfQuerySpec, 0, e.Parent.ObjectsPerQuery) + metrics = 0 + nRes = 0 } - count += n - pqs = make([]types.PerfQuerySpec, 0, e.Parent.ObjectsPerQuery) + log.Printf("D! %d metrics remaining. Total metrics %d", mr, metrics) } + total++ + nRes++ + } + // We should never have resources in the buffer when we get here. That indicates some bug! + // + if len(pqs) > 0 { + log.Printf("E! INTERNAL ERROR: Dangling items in query buffer. len(pqs) == %d", len(pqs)) } e.lastColls[resourceType] = now // Use value captured at the beginning to avoid blind spots. log.Printf("D! Collection of %s for %s, took %v returning %d metrics", resourceType, e.URL.Host, time.Now().Sub(start), count) @@ -458,7 +478,7 @@ func (e *Endpoint) collectChunk(pqs []types.PerfQuerySpec, resourceType string, ems, err := client.Perf.ToMetricSeries(ctx, metrics) if err != nil { - client = nil + client.Valid = false return count, err } @@ -513,8 +533,8 @@ func (e *Endpoint) collectChunk(pqs []types.PerfQuerySpec, resourceType string, // measurement name. Now emit them! // log.Printf("D! Collected %d buckets for %s", len(buckets), instInfo.name) - for key, bucket := range buckets { - log.Printf("D! Key: %s, Tags: %s, Fields: %s", key, bucket.tags, bucket.fields) + for _, bucket := range buckets { + //log.Printf("D! Key: %s, Tags: %s, Fields: %s", key, bucket.tags, bucket.fields) acc.AddFields(bucket.name, bucket.fields, bucket.tags, bucket.ts) } } diff --git a/plugins/inputs/vsphere/vsphere.go b/plugins/inputs/vsphere/vsphere.go index 349f59bb5f5f6..96ceb2071a17b 100644 --- a/plugins/inputs/vsphere/vsphere.go +++ b/plugins/inputs/vsphere/vsphere.go @@ -32,7 +32,8 @@ type VSphere struct { DatastoreMetricExclude []string Separator string - ObjectsPerQuery int32 + ObjectsPerQuery int + MetricsPerQuery int ObjectDiscoveryInterval internal.Duration Timeout internal.Duration @@ -232,6 +233,7 @@ func init() { Separator: ".", ObjectsPerQuery: 256, + MetricsPerQuery: 256, ObjectDiscoveryInterval: internal.Duration{Duration: time.Second * 300}, Timeout: internal.Duration{Duration: time.Second * 20}, } From 1e27ea129c1fe38bcd0f6a00009624632bde7f5a Mon Sep 17 00:00:00 2001 From: prydin Date: Thu, 19 Jul 2018 14:32:49 -0400 Subject: [PATCH 071/100] Run chunk collection in goroutines --- plugins/inputs/vsphere/endpoint.go | 65 ++++++++++++++++++++++++------ plugins/inputs/vsphere/vsphere.go | 2 + 2 files changed, 54 insertions(+), 13 deletions(-) diff --git a/plugins/inputs/vsphere/endpoint.go b/plugins/inputs/vsphere/endpoint.go index 5c04f257cfae9..c45d3b8edd0a4 100644 --- a/plugins/inputs/vsphere/endpoint.go +++ b/plugins/inputs/vsphere/endpoint.go @@ -7,6 +7,7 @@ import ( "strconv" "strings" "sync" + "sync/atomic" "time" "github.com/gobwas/glob" @@ -227,7 +228,7 @@ func (e *Endpoint) discover() error { for _, m := range metrics { include := len(cInc) == 0 // Empty include list means include all mName := e.metricNames[m.CounterId] - //log.Printf("% %s", mName, cInc) + //log.Printf("%s %s", mName, m.Instance) if !include { // If not included by default for _, p := range cInc { if p.Match(mName) { @@ -392,7 +393,33 @@ func (e *Endpoint) collectResource(resourceType string, acc telegraf.Accumulator log.Printf("D! Start of sample period deemed to be %s", latest) log.Printf("D! Collecting metrics for %d objects of type %s for %s", len(res.objects), resourceType, e.URL.Host) - count := 0 + // Set up collection goroutines + // + count := int64(0) + chunkCh := make(chan []types.PerfQuerySpec) + errorCh := make(chan error, e.Parent.CollectConcurrency) // Try not to block on errors. + doneCh := make(chan bool) + for i := 0; i < e.Parent.CollectConcurrency; i++ { + go func() { + for { + select { + case chunk, valid := <-chunkCh: + if !valid { + doneCh <- true + log.Printf("D! No more work. Exiting collection goroutine") + return + } + n, err := e.collectChunk(chunk, resourceType, res, acc) + log.Printf("D! Query returned %d metrics", n) + if err != nil { + errorCh <- err + } + atomic.AddInt64(&count, int64(n)) + } + } + }() + } + start := time.Now() pqs := make([]types.PerfQuerySpec, 0, e.Parent.ObjectsPerQuery) metrics := 0 @@ -411,7 +438,7 @@ func (e *Endpoint) collectResource(resourceType string, acc telegraf.Accumulator mc = headroom } fm := len(info.metrics) - mr - log.Printf("D! mr: %d, mm: %d, fm: %d, headroom before add: %d", mr, mc, fm, headroom) + log.Printf("D! mr: %d, mm: %d, fm: %d", mr, mc, fm) pq := types.PerfQuerySpec{ Entity: object.ref, MaxSample: 1, @@ -435,12 +462,7 @@ func (e *Endpoint) collectResource(resourceType string, acc telegraf.Accumulator if mr > 0 || (!res.realTime && metrics >= e.Parent.MetricsPerQuery) || total >= len(res.objects)-1 || nRes >= e.Parent.ObjectsPerQuery { log.Printf("D! Querying %d objects, %d metrics (%d remaining) of type %s for %s. Processed objects: %d. Total objects %d, metrics %d", len(pqs), metrics, mr, resourceType, e.URL.Host, total+1, len(res.objects), count) - n, err := e.collectChunk(pqs, resourceType, res, acc) - if err != nil { - return count, time.Now().Sub(start).Seconds(), err - } - log.Printf("D! Query returned %d metrics", n) - count += n + chunkCh <- pqs pqs = make([]types.PerfQuerySpec, 0, e.Parent.ObjectsPerQuery) metrics = 0 nRes = 0 @@ -450,14 +472,31 @@ func (e *Endpoint) collectResource(resourceType string, acc telegraf.Accumulator total++ nRes++ } - // We should never have resources in the buffer when we get here. That indicates some bug! + // There may be dangling stuff in the queue. Handle them // if len(pqs) > 0 { - log.Printf("E! INTERNAL ERROR: Dangling items in query buffer. len(pqs) == %d", len(pqs)) + log.Printf("Pushing dangling buffer with %d objects for %s", len(pqs), resourceType) + chunkCh <- pqs } + + var err error + + // Inform collection goroutines that there's no more data and wait for them to finish + // + close(chunkCh) + alive := e.Parent.CollectConcurrency + for alive > 0 { + select { + case <-doneCh: + alive-- + case err = <-errorCh: + log.Printf("!E Error from collection goroutine: %s", err) + } + } + e.lastColls[resourceType] = now // Use value captured at the beginning to avoid blind spots. log.Printf("D! Collection of %s for %s, took %v returning %d metrics", resourceType, e.URL.Host, time.Now().Sub(start), count) - return count, time.Now().Sub(start).Seconds(), nil + return int(count), time.Now().Sub(start).Seconds(), err } func (e *Endpoint) collectChunk(pqs []types.PerfQuerySpec, resourceType string, res resource, acc telegraf.Accumulator) (int, error) { @@ -472,7 +511,7 @@ func (e *Endpoint) collectChunk(pqs []types.PerfQuerySpec, resourceType string, metrics, err := client.Perf.Query(ctx, pqs) if err != nil { //TODO: Check the error and attempt to handle gracefully. (ie: object no longer exists) - log.Printf("E! Error querying metrics of %s for %s", resourceType, e.URL.Host) + log.Printf("E! Error querying metrics of %s for %s %s", resourceType, e.URL.Host, err) return count, err } diff --git a/plugins/inputs/vsphere/vsphere.go b/plugins/inputs/vsphere/vsphere.go index 96ceb2071a17b..e4f0ef9ee3457 100644 --- a/plugins/inputs/vsphere/vsphere.go +++ b/plugins/inputs/vsphere/vsphere.go @@ -34,6 +34,7 @@ type VSphere struct { ObjectsPerQuery int MetricsPerQuery int + CollectConcurrency int ObjectDiscoveryInterval internal.Duration Timeout internal.Duration @@ -234,6 +235,7 @@ func init() { ObjectsPerQuery: 256, MetricsPerQuery: 256, + CollectConcurrency: 1, ObjectDiscoveryInterval: internal.Duration{Duration: time.Second * 300}, Timeout: internal.Duration{Duration: time.Second * 20}, } From b0d7677ced91908fd57ab9eede474e3f36eb2bfd Mon Sep 17 00:00:00 2001 From: prydin Date: Thu, 19 Jul 2018 14:50:34 -0400 Subject: [PATCH 072/100] Added collect_instance flags on resource types --- plugins/inputs/vsphere/endpoint.go | 88 ++++++++++++++++-------------- plugins/inputs/vsphere/vsphere.go | 8 +++ 2 files changed, 56 insertions(+), 40 deletions(-) diff --git a/plugins/inputs/vsphere/endpoint.go b/plugins/inputs/vsphere/endpoint.go index c45d3b8edd0a4..68679520d4c0f 100644 --- a/plugins/inputs/vsphere/endpoint.go +++ b/plugins/inputs/vsphere/endpoint.go @@ -35,14 +35,15 @@ type Endpoint struct { } type resource struct { - pKey string - enabled bool - realTime bool - sampling int32 - objects objectMap - includes []string - excludes []string - getObjects func(*view.ContainerView) (objectMap, error) + pKey string + enabled bool + realTime bool + sampling int32 + objects objectMap + includes []string + excludes []string + collectInstances bool + getObjects func(*view.ContainerView) (objectMap, error) } type metricEntry struct { @@ -80,44 +81,48 @@ func NewEndpoint(parent *VSphere, url *url.URL) *Endpoint { e.resources = map[string]resource{ "cluster": { - pKey: "clustername", - enabled: parent.GatherClusters, - realTime: false, - sampling: 300, - objects: make(objectMap), - includes: parent.ClusterMetricInclude, - excludes: parent.ClusterMetricExclude, - getObjects: getClusters, + pKey: "clustername", + enabled: parent.GatherClusters, + realTime: false, + sampling: 300, + objects: make(objectMap), + includes: parent.ClusterMetricInclude, + excludes: parent.ClusterMetricExclude, + collectInstances: parent.ClusterInstances, + getObjects: getClusters, }, "host": { - pKey: "esxhostname", - enabled: parent.GatherHosts, - realTime: true, - sampling: 20, - objects: make(objectMap), - includes: parent.HostMetricInclude, - excludes: parent.HostMetricExclude, - getObjects: getHosts, + pKey: "esxhostname", + enabled: parent.GatherHosts, + realTime: true, + sampling: 20, + objects: make(objectMap), + includes: parent.HostMetricInclude, + excludes: parent.HostMetricExclude, + collectInstances: parent.HostInstances, + getObjects: getHosts, }, "vm": { - pKey: "vmname", - enabled: parent.GatherVms, - realTime: true, - sampling: 20, - objects: make(objectMap), - includes: parent.VmMetricInclude, - excludes: parent.VmMetricExclude, - getObjects: getVMs, + pKey: "vmname", + enabled: parent.GatherVms, + realTime: true, + sampling: 20, + objects: make(objectMap), + includes: parent.VmMetricInclude, + excludes: parent.VmMetricExclude, + collectInstances: parent.VmInstances, + getObjects: getVMs, }, "datastore": { - pKey: "dsname", - enabled: parent.GatherDatastores, - realTime: false, - sampling: 300, - objects: make(objectMap), - includes: parent.DatastoreMetricInclude, - excludes: parent.DatastoreMetricExclude, - getObjects: getDatastores, + pKey: "dsname", + enabled: parent.GatherDatastores, + realTime: false, + sampling: 300, + objects: make(objectMap), + includes: parent.DatastoreMetricInclude, + excludes: parent.DatastoreMetricExclude, + collectInstances: parent.DatastoreInstances, + getObjects: getDatastores, }, } @@ -226,6 +231,9 @@ func (e *Endpoint) discover() error { // if res.enabled { for _, m := range metrics { + if m.Instance != "" && !res.collectInstances { + continue + } include := len(cInc) == 0 // Empty include list means include all mName := e.metricNames[m.CounterId] //log.Printf("%s %s", mName, m.Instance) diff --git a/plugins/inputs/vsphere/vsphere.go b/plugins/inputs/vsphere/vsphere.go index e4f0ef9ee3457..1176f32a929ca 100644 --- a/plugins/inputs/vsphere/vsphere.go +++ b/plugins/inputs/vsphere/vsphere.go @@ -19,15 +19,19 @@ type VSphere struct { Username string Password string GatherClusters bool + ClusterInstances bool ClusterMetricInclude []string ClusterMetricExclude []string GatherHosts bool + HostInstances bool HostMetricInclude []string HostMetricExclude []string GatherVms bool + VmInstances bool VmMetricInclude []string VmMetricExclude []string GatherDatastores bool + DatastoreInstances bool DatastoreMetricInclude []string DatastoreMetricExclude []string Separator string @@ -220,15 +224,19 @@ func init() { Vcenters: []string{}, GatherClusters: true, + ClusterInstances: true, ClusterMetricInclude: nil, ClusterMetricExclude: nil, GatherHosts: true, + HostInstances: true, HostMetricInclude: nil, HostMetricExclude: nil, GatherVms: true, + VmInstances: true, VmMetricInclude: nil, VmMetricExclude: nil, GatherDatastores: true, + DatastoreInstances: false, DatastoreMetricInclude: nil, DatastoreMetricExclude: nil, Separator: ".", From fedfa248f0d500893c4aec18801e26411446fcf7 Mon Sep 17 00:00:00 2001 From: prydin Date: Fri, 20 Jul 2018 12:48:37 -0400 Subject: [PATCH 073/100] Allow background execution of first discovery --- plugins/inputs/vsphere/endpoint.go | 75 ++++++++------- plugins/inputs/vsphere/vsphere.go | 142 ++++++++++++++++------------- 2 files changed, 124 insertions(+), 93 deletions(-) diff --git a/plugins/inputs/vsphere/endpoint.go b/plugins/inputs/vsphere/endpoint.go index 68679520d4c0f..cd6c9205deb5b 100644 --- a/plugins/inputs/vsphere/endpoint.go +++ b/plugins/inputs/vsphere/endpoint.go @@ -26,7 +26,7 @@ type Endpoint struct { pool Pool lastColls map[string]time.Time instanceInfo map[string]resourceInfo - resources map[string]resource + resourceKinds map[string]resourceKind metricNames map[int32]string discoveryTicker *time.Ticker clientMux sync.Mutex @@ -34,7 +34,7 @@ type Endpoint struct { initialized bool } -type resource struct { +type resourceKind struct { pKey string enabled bool realTime bool @@ -79,7 +79,7 @@ func NewEndpoint(parent *VSphere, url *url.URL) *Endpoint { initialized: false, } - e.resources = map[string]resource{ + e.resourceKinds = map[string]resourceKind{ "cluster": { pKey: "clustername", enabled: parent.GatherClusters, @@ -138,25 +138,36 @@ func (e *Endpoint) init() error { } if e.Parent.ObjectDiscoveryInterval.Duration.Seconds() > 0 { - // Run an initial discovery. - // - err = e.discover() - if err != nil { - log.Printf("E! Error in initial discovery for %s: %v", e.URL.Host, err) - return err + discoverFunc := func() { + err = e.discover() + if err != nil { + log.Printf("E! Error in initial discovery for %s: %v", e.URL.Host, err) + } + + // Create discovery ticker + // + e.discoveryTicker = time.NewTicker(e.Parent.ObjectDiscoveryInterval.Duration) + go func() { + for range e.discoveryTicker.C { + err := e.discover() + if err != nil { + log.Printf("E! Error in discovery for %s: %v", e.URL.Host, err) + } + } + }() } - // Create discovery ticker + // Run an initial discovery. If force_discovery_on_init isn't set, we kick it off as a + // goroutine without waiting for it. This will probably cause us to report an empty + // dataset on the first collection, but it solves the issue of the first collection timing out. // - e.discoveryTicker = time.NewTicker(e.Parent.ObjectDiscoveryInterval.Duration) - go func() { - for range e.discoveryTicker.C { - err := e.discover() - if err != nil { - log.Printf("E! Error in discovery for %s: %v", e.URL.Host, err) - } - } - }() + if e.Parent.ForceDiscoverOnInit { + log.Printf("Running initial discovery and waiting for it to finish") + discoverFunc() + } else { + log.Printf("Running initial discovery in the background") + go discoverFunc() + } } e.initialized = true @@ -192,11 +203,11 @@ func (e *Endpoint) discover() error { defer e.pool.Return(client) instInfo := make(map[string]resourceInfo) - resources := make(map[string]resource) + resourceKinds := make(map[string]resourceKind) // Populate resource objects, and endpoint name cache // - for k, res := range e.resources { + for k, res := range e.resourceKinds { // Don't be tempted to skip disabled resources here! We may need them to resolve parent references // // Precompile includes and excludes @@ -263,7 +274,7 @@ func (e *Endpoint) discover() error { instInfo[obj.ref.Value] = resourceInfo{name: obj.name, metrics: mList} } res.objects = objects - resources[k] = res + resourceKinds[k] = res } } @@ -273,7 +284,7 @@ func (e *Endpoint) discover() error { defer e.collectMux.Unlock() e.instanceInfo = instInfo - e.resources = resources + e.resourceKinds = resourceKinds log.Printf("D! Discovered %d objects for %s. Took %s", len(instInfo), e.URL.Host, time.Now().Sub(start)) @@ -365,7 +376,7 @@ func (e *Endpoint) collect(acc telegraf.Accumulator) error { return err } } - for k, res := range e.resources { + for k, res := range e.resourceKinds { if res.enabled { count, duration, err := e.collectResource(k, acc) if err != nil { @@ -383,7 +394,7 @@ func (e *Endpoint) collect(acc telegraf.Accumulator) error { func (e *Endpoint) collectResource(resourceType string, acc telegraf.Accumulator) (int, float64, error) { // Do we have new data yet? // - res := e.resources[resourceType] + res := e.resourceKinds[resourceType] now := time.Now() latest, hasLatest := e.lastColls[resourceType] if hasLatest { @@ -409,11 +420,12 @@ func (e *Endpoint) collectResource(resourceType string, acc telegraf.Accumulator doneCh := make(chan bool) for i := 0; i < e.Parent.CollectConcurrency; i++ { go func() { + defer func() { doneCh <- true }() + //timeout = time.After() for { select { case chunk, valid := <-chunkCh: if !valid { - doneCh <- true log.Printf("D! No more work. Exiting collection goroutine") return } @@ -498,7 +510,7 @@ func (e *Endpoint) collectResource(resourceType string, acc telegraf.Accumulator case <-doneCh: alive-- case err = <-errorCh: - log.Printf("!E Error from collection goroutine: %s", err) + log.Printf("E! Error from collection goroutine: %s", err) } } @@ -507,7 +519,7 @@ func (e *Endpoint) collectResource(resourceType string, acc telegraf.Accumulator return int(count), time.Now().Sub(start).Seconds(), err } -func (e *Endpoint) collectChunk(pqs []types.PerfQuerySpec, resourceType string, res resource, acc telegraf.Accumulator) (int, error) { +func (e *Endpoint) collectChunk(pqs []types.PerfQuerySpec, resourceType string, res resourceKind, acc telegraf.Accumulator) (int, error) { count := 0 prefix := "vsphere" + e.Parent.Separator + resourceType client, err := e.pool.Take() @@ -566,7 +578,6 @@ func (e *Endpoint) collectChunk(pqs []types.PerfQuerySpec, resourceType string, // mn, fn := e.makeMetricIdentifier(prefix, name) bKey := mn + " " + v.Instance + " " + strconv.FormatInt(ts.UnixNano(), 10) - //log.Printf("D! Bucket key: %s, resource: %s, field: %s", bKey, instInfo.name, fn) bucket, found := buckets[bKey] if !found { bucket = metricEntry{name: mn, ts: ts, fields: make(map[string]interface{}), tags: t} @@ -581,14 +592,14 @@ func (e *Endpoint) collectChunk(pqs []types.PerfQuerySpec, resourceType string, // log.Printf("D! Collected %d buckets for %s", len(buckets), instInfo.name) for _, bucket := range buckets { - //log.Printf("D! Key: %s, Tags: %s, Fields: %s", key, bucket.tags, bucket.fields) + //log.Printf("Bucket key: %s", key) acc.AddFields(bucket.name, bucket.fields, bucket.tags, bucket.ts) } } return count, nil } -func (e *Endpoint) populateTags(objectRef *objectRef, resourceType string, resource *resource, t map[string]string, v *performance.MetricSeries) { +func (e *Endpoint) populateTags(objectRef *objectRef, resourceType string, resource *resourceKind, t map[string]string, v *performance.MetricSeries) { // Map name of object. // if resource.pKey != "" { @@ -607,7 +618,7 @@ func (e *Endpoint) populateTags(objectRef *objectRef, resourceType string, resou case "vm": t["guest"] = objectRef.guest t["esxhostname"] = parent.name - hostRes := e.resources["host"] + hostRes := e.resourceKinds["host"] hostRef, ok := hostRes.objects[objectRef.parentRef.Value] if ok { cluster, ok := e.instanceInfo[hostRef.parentRef.Value] diff --git a/plugins/inputs/vsphere/vsphere.go b/plugins/inputs/vsphere/vsphere.go index 1176f32a929ca..fd78abb53f3a5 100644 --- a/plugins/inputs/vsphere/vsphere.go +++ b/plugins/inputs/vsphere/vsphere.go @@ -15,6 +15,7 @@ import ( // VSphere is the top level type for the vSphere input plugin. It contains all the configuration // and a list of connected vSphere endpoints type VSphere struct { + Interval time.Duration Vcenters []string Username string Password string @@ -39,6 +40,7 @@ type VSphere struct { ObjectsPerQuery int MetricsPerQuery int CollectConcurrency int + ForceDiscoverOnInit bool ObjectDiscoveryInterval internal.Duration Timeout internal.Duration @@ -59,25 +61,28 @@ var sampleConfig = ` # gather_vms = true ### (default=true) +## Collect VM instance metrics, such as individual cores? (default=true) +#vm_instances = true + ## Typical VM metrics (if omitted, all metrics are collected) -# vm_metric_include = [ -# "cpu.ready.summation", -# "mem.swapinRate.average", -# "virtualDisk.numberReadAveraged.average", -# "virtualDisk.numberWriteAveraged.average", -# "virtualDisk.totalReadLatency.average", -# "virtualDisk.totalWriteLatency.average", -# "virtualDisk.readOIO.latest", -# "virtualDisk.writeOIO.latest", -# "net.bytesRx.average", -# "net.bytesTx.average", -# "net.droppedRx.summation.", -# "net.droppedTx.summation.", -# "cpu.run.summation", -# "cpu.used.summation", -# "mem.swapoutRate.average", -# "virtualDisk.read.average", -# "virtualDisk.write.average" ] +vm_metric_include = [ + "cpu.ready.summation", + "mem.swapinRate.average", + "virtualDisk.numberReadAveraged.average", + "virtualDisk.numberWriteAveraged.average", + "virtualDisk.totalReadLatency.average", + "virtualDisk.totalWriteLatency.average", + "virtualDisk.readOIO.latest", + "virtualDisk.writeOIO.latest", + "net.bytesRx.average", + "net.bytesTx.average", + "net.droppedRx.summation.", + "net.droppedTx.summation.", + "cpu.run.summation", + "cpu.used.summation", + "mem.swapoutRate.average", + "virtualDisk.read.average", + "virtualDisk.write.average" ] # vm_metric_exclude = [] ## Nothing is excluded by default @@ -85,43 +90,46 @@ var sampleConfig = ` # gather_hosts = true ## (default=true) +## Collect host instance metrics, such as individual cores? (default=true) +#host_instances = true + ## Typical host metrics (if omitted, all metrics are collected) -# host_metric_include = [ -# "cpu.ready.summation", -# "cpu.latency.average", -# "cpu.coreUtilization.average", -# "mem.usage.average", -# "mem.swapinRate.average", -# "mem.state.latest", -# "mem.latency.average", -# "mem.vmmemctl.average", -# "disk.read.average", -# "disk.write.average", -# "disk.numberReadAveraged.average", -# "disk.numberWriteAveraged.average", -# "disk.deviceReadLatency.average", -# "disk.deviceWriteLatency.average", -# "disk.totalReadLatency.average", -# "disk.totalWriteLatency.average", -# "storageAdapter.read.average", -# "storageAdapter.write.average", -# "storageAdapter.numberReadAveraged.average", -# "storageAdapter.numberWriteAveraged.average", -# "net.errorsRx.summation", -# "net.errorsTx.summation", -# "net.bytesRx.average", -# "net.bytesTx.average", -# "cpu.used.summation", -# "cpu.usage.average", -# "cpu.utilization.average", -# "cpu.wait.summation.", -# "cpu.idle.summation", -# "cpu.readiness.average", -# "cpu.costop.summation", -# "cpu.swapwait.summation", -# "mem.swapoutRate.average", -# "disk.kernelReadLatency.average", -# "disk.kernelWriteLatency.average" ] +host_metric_include = [ + "cpu.ready.summation", + "cpu.latency.average", + "cpu.coreUtilization.average", + "mem.usage.average", + "mem.swapinRate.average", + "mem.state.latest", + "mem.latency.average", + "mem.vmmemctl.average", + "disk.read.average", + "disk.write.average", + "disk.numberReadAveraged.average", + "disk.numberWriteAveraged.average", + "disk.deviceReadLatency.average", + "disk.deviceWriteLatency.average", + "disk.totalReadLatency.average", + "disk.totalWriteLatency.average", + "storageAdapter.read.average", + "storageAdapter.write.average", + "storageAdapter.numberReadAveraged.average", + "storageAdapter.numberWriteAveraged.average", + "net.errorsRx.summation", + "net.errorsTx.summation", + "net.bytesRx.average", + "net.bytesTx.average", + "cpu.used.summation", + "cpu.usage.average", + "cpu.utilization.average", + "cpu.wait.summation.", + "cpu.idle.summation", + "cpu.readiness.average", + "cpu.costop.summation", + "cpu.swapwait.summation", + "mem.swapoutRate.average", + "disk.kernelReadLatency.average", + "disk.kernelWriteLatency.average" ] # host_metric_exclude = [] ## Nothing excluded by default @@ -129,12 +137,15 @@ var sampleConfig = ` # gather_clusters = true ## (default=true) +## Collect cluster instance metrics, such as individual cores? (default=true) +#cluster_instances = true + ## Typical cluster metrics (if omitted, all metrics are collected) -#cluster_metric_include = [ -# "cpu.usage.*", -# "cpu.usagemhz.*", -# "mem.usage.*", -# "mem.active.*" ] +cluster_metric_include = [ + "cpu.usage.*", + "cpu.usagemhz.*", + "mem.usage.*", + "mem.active.*" ] # cluster_metric_exclude = [] ## Nothing excluded by default @@ -142,20 +153,26 @@ var sampleConfig = ` # gather_datastores = true ## (default=true) +## Collect datastore instance metrics, such as individual LUNs and datafiles? (default=false) +#datastore_instances = false + ## Typical datastore metrics (if omitted, all metrics are collected) ## ## WARNING: It is not recommended to include all metrics. Your collection will probably time out if you do! ## - datastore_metric_include = [ "datastore.read.average", "datastore.write.average", "datastore.throughput.usage.average", "datastore.throughput.contention.average" ] -## number of objects to retreive per query. set to 64 for vCenter 5.5 and 6.0 (default: 256) +## Number of objects to retreive per query. # objects_per_query = 256 +## Number of metrics (data points) to retrieve in each query for non-realtime metrics (cluster and datastore). +## Set to 64 for vCenter 5.5 and 6.0 (default: 256) +# metrics_per_query = 256 + ## the interval before (re)discovering objects subject to metrics collection (default: 300s) # object_discovery_interval = "300s" @@ -201,6 +218,8 @@ func (v *VSphere) checkEndpoints() { // the data collection and writes all metrics into the Accumulator passed as an argument. func (v *VSphere) Gather(acc telegraf.Accumulator) error { + log.Printf("Interval: %v", v.Interval) + v.checkEndpoints() var wg sync.WaitGroup @@ -244,6 +263,7 @@ func init() { ObjectsPerQuery: 256, MetricsPerQuery: 256, CollectConcurrency: 1, + ForceDiscoverOnInit: true, ObjectDiscoveryInterval: internal.Duration{Duration: time.Second * 300}, Timeout: internal.Duration{Duration: time.Second * 20}, } From 2f4092fd641c8abc940974895824cf25b3f945dc Mon Sep 17 00:00:00 2001 From: prydin Date: Mon, 23 Jul 2018 07:12:21 -0400 Subject: [PATCH 074/100] Added concurrent discovery --- plugins/inputs/vsphere/endpoint.go | 128 ++++++++++++++++++++--------- plugins/inputs/vsphere/pool.go | 7 +- plugins/inputs/vsphere/vsphere.go | 2 + 3 files changed, 96 insertions(+), 41 deletions(-) diff --git a/plugins/inputs/vsphere/endpoint.go b/plugins/inputs/vsphere/endpoint.go index cd6c9205deb5b..65041214ee5d2 100644 --- a/plugins/inputs/vsphere/endpoint.go +++ b/plugins/inputs/vsphere/endpoint.go @@ -67,6 +67,11 @@ type resourceInfo struct { metrics performance.MetricList } +type metricQResponse struct { + obj objectRef + metrics *performance.MetricList +} + // NewEndpoint returns a new connection to a vCenter based on the URL and configuration passed // as parameters. func NewEndpoint(parent *VSphere, url *url.URL) *Endpoint { @@ -192,6 +197,34 @@ func (e *Endpoint) setupMetricIds() error { return nil } +func (e *Endpoint) runMetricMetadataGetter(res *resourceKind, in chan objectRef, out chan *metricQResponse) { + client, err := e.pool.Take() + if err != nil { + log.Printf("E! Error getting client. Discovery will be incomplete. Error: %s", err) + return + } + defer e.pool.Return(client) + for { + select { + case obj, ok := <-in: + if !ok { + // Sending nil means that we're done. + // + log.Println("D! Metadata getter done. Exiting goroutine") + out <- nil + return + } + log.Printf("D! Getting metric metadata for: %s", obj.name) + ctx := context.Background() + metrics, err := client.Perf.AvailableMetric(ctx, obj.ref.Reference(), res.sampling) + if err != nil { + log.Printf("E! Error while getting metric metadata. Discovery will be incomplete. Error: %s", err) + } + out <- &metricQResponse{metrics: &metrics, obj: obj} + } + } +} + func (e *Endpoint) discover() error { start := time.Now() log.Printf("D! Discover new objects for %s", e.URL.Host) @@ -228,50 +261,71 @@ func (e *Endpoint) discover() error { client.Valid = false // Don't reuse this one! return err } + + // Start metadata getters. + // The response channel must have enough buffer to hold all responses, or + // we may deadlock. We also reserve one slot per goroutine to hold the final + // "nil" representing the end of the job. + // + rqCh := make(chan objectRef) + resCh := make(chan *metricQResponse, len(objects)+e.Parent.DiscoverConcurrency) + for i := 0; i < e.Parent.DiscoverConcurrency; i++ { + go e.runMetricMetadataGetter(&res, rqCh, resCh) + } + + // Submit work to the getters + // for _, obj := range objects { - ctx := context.Background() - mList := make(performance.MetricList, 0) - metrics, err := client.Perf.AvailableMetric(ctx, obj.ref.Reference(), res.sampling) - if err != nil { - client.Valid = false // Don't reuse this one! - return err - } - log.Printf("D! Obj: %s, metrics found: %d, enabled: %t", obj.name, len(metrics), res.enabled) + rqCh <- obj + } + close(rqCh) - // Mmetric metadata gathering is only needed for enabled resource types. - // - if res.enabled { - for _, m := range metrics { - if m.Instance != "" && !res.collectInstances { - continue - } - include := len(cInc) == 0 // Empty include list means include all - mName := e.metricNames[m.CounterId] - //log.Printf("%s %s", mName, m.Instance) - if !include { // If not included by default - for _, p := range cInc { - if p.Match(mName) { - include = true + // Handle responses as they trickle in. Loop while there are still + // goroutines processing work. + // + alive := e.Parent.DiscoverConcurrency + for alive > 0 { + select { + case resp := <-resCh: + if resp == nil { + // Goroutine is done. + // + alive-- + } else { + mList := make(performance.MetricList, 0) + if res.enabled { + for _, m := range *resp.metrics { + if m.Instance != "" && !res.collectInstances { + continue } - } - } - if include { - for _, p := range cExc { - if p.Match(mName) { - include = false - log.Printf("D! Excluded: %s", mName) - break + include := len(cInc) == 0 // Empty include list means include all + mName := e.metricNames[m.CounterId] + //log.Printf("%s %s", mName, m.Instance) + if !include { // If not included by default + for _, p := range cInc { + if p.Match(mName) { + include = true + } + } + } + if include { + for _, p := range cExc { + if p.Match(mName) { + include = false + log.Printf("D! Excluded: %s", mName) + break + } + } + if include { // If still included after processing excludes + mList = append(mList, m) + //log.Printf("D! Included %s Sampling: %d", mName, res.sampling) + } } - } - if include { // If still included after processing excludes - mList = append(mList, m) - //log.Printf("D! Included %s Sampling: %d", mName, res.sampling) } } - + instInfo[resp.obj.ref.Value] = resourceInfo{name: resp.obj.name, metrics: mList} } } - instInfo[obj.ref.Value] = resourceInfo{name: obj.name, metrics: mList} } res.objects = objects resourceKinds[k] = res @@ -479,7 +533,7 @@ func (e *Endpoint) collectResource(resourceType string, acc telegraf.Accumulator // 2) The toral number of metrics exceeds the max // 3) We are at the last resource and have no more data to process. // - if mr > 0 || (!res.realTime && metrics >= e.Parent.MetricsPerQuery) || total >= len(res.objects)-1 || nRes >= e.Parent.ObjectsPerQuery { + if mr > 0 || (!res.realTime && metrics >= e.Parent.MetricsPerQuery) /* || total >= len(res.objects)-1 */ || nRes >= e.Parent.ObjectsPerQuery { log.Printf("D! Querying %d objects, %d metrics (%d remaining) of type %s for %s. Processed objects: %d. Total objects %d, metrics %d", len(pqs), metrics, mr, resourceType, e.URL.Host, total+1, len(res.objects), count) chunkCh <- pqs diff --git a/plugins/inputs/vsphere/pool.go b/plugins/inputs/vsphere/pool.go index 71771d1305ef5..4b15c81b8835a 100644 --- a/plugins/inputs/vsphere/pool.go +++ b/plugins/inputs/vsphere/pool.go @@ -1,7 +1,6 @@ package vsphere import ( - "log" "net/url" "sync" "time" @@ -31,20 +30,20 @@ func (p *Pool) Take() (*Client, error) { r := p.root p.root = r.next if r.Client.Valid && r.expiry.UnixNano() > time.Now().UnixNano() { - log.Printf("D! //////// Getting connection from pool") + //log.Printf("D! //////// Getting connection from pool") return r.Client, nil } } // Pool is empty, create a new client! // - log.Printf("D! ******* Pool is empty, creating new client") + //log.Printf("D! ******* Pool is empty, creating new client") return NewClient(p.u, p.v) } // Return put a client back to the free list func (p *Pool) Return(client *Client) { if client == nil || !client.Valid { - log.Printf("E! Connection taken out of pool due to error") + //log.Printf("E! Connection taken out of pool due to error") return // Useful when you want to override a deferred Return } p.mux.Lock() diff --git a/plugins/inputs/vsphere/vsphere.go b/plugins/inputs/vsphere/vsphere.go index fd78abb53f3a5..387a9d3cd73c7 100644 --- a/plugins/inputs/vsphere/vsphere.go +++ b/plugins/inputs/vsphere/vsphere.go @@ -40,6 +40,7 @@ type VSphere struct { ObjectsPerQuery int MetricsPerQuery int CollectConcurrency int + DiscoverConcurrency int ForceDiscoverOnInit bool ObjectDiscoveryInterval internal.Duration Timeout internal.Duration @@ -263,6 +264,7 @@ func init() { ObjectsPerQuery: 256, MetricsPerQuery: 256, CollectConcurrency: 1, + DiscoverConcurrency: 1, ForceDiscoverOnInit: true, ObjectDiscoveryInterval: internal.Duration{Duration: time.Second * 300}, Timeout: internal.Duration{Duration: time.Second * 20}, From 654135acc88c0dc43295d83f297d5c6aa9d11316 Mon Sep 17 00:00:00 2001 From: Pontus Rydin Date: Mon, 23 Jul 2018 17:37:46 -0400 Subject: [PATCH 075/100] Collection chunking (#4) * Added configuration parameter metric_count * Run chunk collection in goroutines * Added collection concurrency * Added collect_instance flags on resource types * Allow background execution of first discovery * Added concurrent discovery --- Gopkg.lock | 407 ++++++++++++++++++++++++++--- plugins/inputs/vsphere/client.go | 1 - plugins/inputs/vsphere/endpoint.go | 388 ++++++++++++++++++--------- plugins/inputs/vsphere/pool.go | 7 +- plugins/inputs/vsphere/vsphere.go | 158 ++++++----- 5 files changed, 723 insertions(+), 238 deletions(-) diff --git a/Gopkg.lock b/Gopkg.lock index 314b801c171b3..86a9729174b3f 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -3,48 +3,61 @@ [[projects]] branch = "master" + digest = "1:d7582b4af1b0b953ff2bb9573a50f787c7e1669cb148fb086a3d1c670a1ac955" name = "code.cloudfoundry.org/clock" packages = ["."] + pruneopts = "" revision = "02e53af36e6c978af692887ed449b74026d76fec" [[projects]] + digest = "1:ce7dc0f1ffcd9a2aacc50ae6d322eebff8f4faa2d6c5f445c874cd0b77a63de7" name = "collectd.org" packages = [ "api", "cdtime", - "network" + "network", ] + pruneopts = "" revision = "2ce144541b8903101fb8f1483cc0497a68798122" version = "v0.3.0" [[projects]] branch = "master" + digest = "1:c1269bfaddefd090935401c291ad5df6c03de605a440e941ecc568e19f0f9e3b" name = "github.com/Microsoft/ApplicationInsights-Go" packages = [ "appinsights", - "appinsights/contracts" + "appinsights/contracts", ] + pruneopts = "" revision = "d2df5d440eda5372f24fcac03839a64d6cb5f7e5" [[projects]] + digest = "1:e43b98837e21e7f9ef552445e1fcfa92e36bfc0a3e0bd78f2a5e0bef7a4378e3" name = "github.com/Microsoft/go-winio" packages = ["."] + pruneopts = "" revision = "7da180ee92d8bd8bb8c37fc560e673e6557c392f" version = "v0.4.7" [[projects]] + digest = "1:14af5ba5ac88efec490fb59734df34e1bd973198caefa7b0cceed0900ef6164c" name = "github.com/Shopify/sarama" packages = ["."] + pruneopts = "" revision = "35324cf48e33d8260e1c7c18854465a904ade249" version = "v1.17.0" [[projects]] + digest = "1:f82b8ac36058904227087141017bb82f4b0fc58272990a4cdae3e2d6d222644e" name = "github.com/StackExchange/wmi" packages = ["."] + pruneopts = "" revision = "5d049714c4a64225c3c79a7cf7d02f7fb5b96338" version = "1.0.0" [[projects]] + digest = "1:537feef3c3a65e8b924d992df29aacb41f28f7faa4e4610287322bc63e02a33b" name = "github.com/aerospike/aerospike-client-go" packages = [ ".", @@ -57,42 +70,52 @@ "types/atomic", "types/particle_type", "types/rand", - "utils/buffer" + "utils/buffer", ] + pruneopts = "" revision = "c10b5393e43bd60125aca6289c7b24879edb1787" version = "v1.33.0" [[projects]] branch = "master" + digest = "1:1399282ad03ac819f0e8a747c888407c5c98bb497d33821a7047c7bae667ede0" name = "github.com/alecthomas/template" packages = [ ".", - "parse" + "parse", ] + pruneopts = "" revision = "a0175ee3bccc567396460bf5acd36800cb10c49c" [[projects]] branch = "master" + digest = "1:8483994d21404c8a1d489f6be756e25bfccd3b45d65821f25695577791a08e68" name = "github.com/alecthomas/units" packages = ["."] + pruneopts = "" revision = "2efee857e7cfd4f3d0138cc3cbb1b4966962b93a" [[projects]] branch = "master" + digest = "1:072692f8d76356228f31f64ca3140041a140011c7dea26e746206e8649c71b31" name = "github.com/amir/raidman" packages = [ ".", - "proto" + "proto", ] + pruneopts = "" revision = "1ccc43bfb9c93cb401a4025e49c64ba71e5e668b" [[projects]] branch = "master" + digest = "1:0be63b870133f215b6540f82611928cc00b1f6fdf8b63125c1b75a417a88394c" name = "github.com/apache/thrift" packages = ["lib/go/thrift"] + pruneopts = "" revision = "f5f430df56871bc937950274b2c86681d3db6e59" [[projects]] + digest = "1:413443b87e4881a8c9f9b64bca9ed7d96458466f9654aef3b3813e1714f0ef4f" name = "github.com/aws/aws-sdk-go" packages = [ "aws", @@ -124,75 +147,95 @@ "private/protocol/xml/xmlutil", "service/cloudwatch", "service/kinesis", - "service/sts" + "service/sts", ] + pruneopts = "" revision = "bfc1a07cf158c30c41a3eefba8aae043d0bb5bff" version = "v1.14.8" [[projects]] branch = "master" + digest = "1:fca298802a2ab834d6eb0e284788ae037ebc324c0f325ff92c5eea592d189cc5" name = "github.com/beorn7/perks" packages = ["quantile"] + pruneopts = "" revision = "3a771d992973f24aa725d07868b467d1ddfceafb" [[projects]] + digest = "1:0edb96edcfeee9aeba92e605536fbb1542b0bf6a10cea9d0b5a2227d5a703eae" name = "github.com/bsm/sarama-cluster" packages = ["."] + pruneopts = "" revision = "cf455bc755fe41ac9bb2861e7a961833d9c2ecc3" version = "v2.1.13" [[projects]] + digest = "1:f619cb9b07aebe5416262cdd8b86082e8d5bdc5264cb3b615ff858df0b645f97" name = "github.com/cenkalti/backoff" packages = ["."] + pruneopts = "" revision = "2ea60e5f094469f9e65adb9cd103795b73ae743e" version = "v2.0.0" [[projects]] branch = "master" + digest = "1:65ae2d1625584ba8d16d1e15b25db1fc62334e2040f22dbbbdc7531c909843b2" name = "github.com/couchbase/go-couchbase" packages = ["."] + pruneopts = "" revision = "16db1f1fe037412f12738fa4d8448c549c4edd77" [[projects]] branch = "master" + digest = "1:5db54de7054c072f47806c91ef7625ffa00489ca2da5fbc6ca1c78e08018f6bf" name = "github.com/couchbase/gomemcached" packages = [ ".", - "client" + "client", ] + pruneopts = "" revision = "0da75df145308b9a4e6704d762ca9d9b77752efc" [[projects]] branch = "master" + digest = "1:0deaa0f28c823119725c8308703f019797bc077e251d1ed3f2b8eae2cc7791d7" name = "github.com/couchbase/goutils" packages = [ "logging", - "scramsha" + "scramsha", ] + pruneopts = "" revision = "e865a1461c8ac0032bd37e2d4dab3289faea3873" [[projects]] + digest = "1:0a39ec8bf5629610a4bc7873a92039ee509246da3cef1a0ea60f1ed7e5f9cea5" name = "github.com/davecgh/go-spew" packages = ["spew"] + pruneopts = "" revision = "346938d642f2ec3594ed81d874461961cd0faa76" version = "v1.1.0" [[projects]] + digest = "1:2426da75f49e5b8507a6ed5d4c49b06b2ff795f4aec401c106b7db8fb2625cd7" name = "github.com/dgrijalva/jwt-go" packages = ["."] + pruneopts = "" revision = "06ea1031745cb8b3dab3f6a236daf2b0aa468b7e" version = "v3.2.0" [[projects]] + digest = "1:3152d7c68fd4797ca2d96524b7b229cd8237e2c2b2605584ebd736cc7be2a523" name = "github.com/docker/distribution" packages = [ "digest", - "reference" + "reference", ] + pruneopts = "" revision = "48294d928ced5dd9b378f7fd7c6f5da3ff3f2c89" version = "v2.6.2" [[projects]] + digest = "1:2f161cdaf1f3617afa09983c677b1392c4b044f12fc9ccfe9ae65d72dca2e6f7" name = "github.com/docker/docker" packages = [ "api/types", @@ -210,76 +253,96 @@ "api/types/versions", "api/types/volume", "client", - "pkg/tlsconfig" + "pkg/tlsconfig", ] + pruneopts = "" revision = "eef6495eddab52828327aade186443681ed71a4e" version = "v17.03.2-ce-rc1" [[projects]] + digest = "1:5b20afc76a36d3994194e2612e83b51bc2b12db3d4d2a722b24474b2d0e3a890" name = "github.com/docker/go-connections" packages = [ "nat", "sockets", - "tlsconfig" + "tlsconfig", ] + pruneopts = "" revision = "3ede32e2033de7505e6500d6c868c2b9ed9f169d" version = "v0.3.0" [[projects]] + digest = "1:582d54fcb7233da8dde1dfd2210a5b9675d0685f84246a8d317b07d680c18b1b" name = "github.com/docker/go-units" packages = ["."] + pruneopts = "" revision = "47565b4f722fb6ceae66b95f853feed578a4a51c" version = "v0.3.3" [[projects]] + digest = "1:7bbb118aeef9a6b9fef3d57b6cc5378f7cd6e915cabf4dea695e318e1a1bd4e6" name = "github.com/eapache/go-resiliency" packages = ["breaker"] + pruneopts = "" revision = "ea41b0fad31007accc7f806884dcdf3da98b79ce" version = "v1.1.0" [[projects]] branch = "master" + digest = "1:1f7503fa58a852a1416556ae2ddb219b49a1304fd408391948e2e3676514c48d" name = "github.com/eapache/go-xerial-snappy" packages = ["."] + pruneopts = "" revision = "bb955e01b9346ac19dc29eb16586c90ded99a98c" [[projects]] + digest = "1:d8d46d21073d0f65daf1740ebf4629c65e04bf92e14ce93c2201e8624843c3d3" name = "github.com/eapache/queue" packages = ["."] + pruneopts = "" revision = "44cc805cf13205b55f69e14bcb69867d1ae92f98" version = "v1.1.0" [[projects]] + digest = "1:d2e2aebcb8e8027345e16f9d0be8cdee3bb470ba406c7a54cb7457ae3ad4ace5" name = "github.com/eclipse/paho.mqtt.golang" packages = [ ".", - "packets" + "packets", ] + pruneopts = "" revision = "36d01c2b4cbeb3d2a12063e4880ce30800af9560" version = "v1.1.1" [[projects]] + digest = "1:f7c22bf37088211029335cd4d356774eeca0db1af4448e5b8178386090f98dfb" name = "github.com/go-ini/ini" packages = ["."] + pruneopts = "" revision = "06f5f3d67269ccec1fe5fe4134ba6e982984f7f5" version = "v1.37.0" [[projects]] + digest = "1:6a4a01d58b227c4b6b11111b9f172ec5c17682b82724e58e6daf3f19f4faccd8" name = "github.com/go-logfmt/logfmt" packages = ["."] + pruneopts = "" revision = "390ab7935ee28ec6b286364bba9b4dd6410cb3d5" version = "v0.3.0" [[projects]] + digest = "1:c3a5ae14424a38c244439732c31a08b5f956c46c4acdc159fc285a52dbf11de0" name = "github.com/go-ole/go-ole" packages = [ ".", - "oleutil" + "oleutil", ] + pruneopts = "" revision = "a41e3c4b706f6ae8dfbff342b06e40fa4d2d0506" version = "v1.2.1" [[projects]] + digest = "1:f2f6a616a1ca8aed667d956c98f7f6178efe72bbe0a419bd33b9d99841c7de69" name = "github.com/go-redis/redis" packages = [ ".", @@ -289,18 +352,22 @@ "internal/pool", "internal/proto", "internal/singleflight", - "internal/util" + "internal/util", ] + pruneopts = "" revision = "83fb42932f6145ce52df09860384a4653d2d332a" version = "v6.12.0" [[projects]] + digest = "1:dc876ae7727280d95f97af5320308131278b93d6c6f5cf953065e18cb8c88fd2" name = "github.com/go-sql-driver/mysql" packages = ["."] + pruneopts = "" revision = "d523deb1b23d913de5bdada721a6071e71283618" version = "v1.4.0" [[projects]] + digest = "1:b7a7e17513aeee6492d93015c7bf29c86a0c1c91210ea56b21e36c1a40958cba" name = "github.com/gobwas/glob" packages = [ ".", @@ -310,131 +377,165 @@ "syntax/ast", "syntax/lexer", "util/runes", - "util/strings" + "util/strings", ] + pruneopts = "" revision = "5ccd90ef52e1e632236f7326478d4faa74f99438" version = "v0.2.3" [[projects]] + digest = "1:602c5827bcd5eca47c041a39aff7b626dda114da57a0334a9230bdc5cf8ed3ed" name = "github.com/gogo/protobuf" packages = ["proto"] + pruneopts = "" revision = "1adfc126b41513cc696b209667c8656ea7aac67c" version = "v1.0.0" [[projects]] + digest = "1:b1d3041d568e065ab4d76f7477844458e9209c0bb241eaccdc0770bf0a13b120" name = "github.com/golang/protobuf" packages = [ "proto", "ptypes", "ptypes/any", "ptypes/duration", - "ptypes/timestamp" + "ptypes/timestamp", ] + pruneopts = "" revision = "b4deda0973fb4c70b50d226b1af49f3da59f5265" version = "v1.1.0" [[projects]] branch = "master" + digest = "1:075128b9fc42e6d99067da1a2e6c0a634a6043b5a60abe6909c51f5ecad37b6d" name = "github.com/golang/snappy" packages = ["."] + pruneopts = "" revision = "2e65f85255dbc3072edf28d6b5b8efc472979f5a" [[projects]] + digest = "1:cc082d7b9cc3f832f2aed9d06d1cbb33b6984a61d8ec403535b086415c181607" name = "github.com/google/go-cmp" packages = [ "cmp", "cmp/internal/diff", "cmp/internal/function", - "cmp/internal/value" + "cmp/internal/value", ] + pruneopts = "" revision = "3af367b6b30c263d47e8895973edcca9a49cf029" version = "v0.2.0" [[projects]] + digest = "1:c1d7e883c50a26ea34019320d8ae40fad86c9e5d56e63a1ba2cb618cef43e986" name = "github.com/google/uuid" packages = ["."] + pruneopts = "" revision = "064e2069ce9c359c118179501254f67d7d37ba24" version = "0.2" [[projects]] + digest = "1:dbbeb8ddb0be949954c8157ee8439c2adfd8dc1c9510eb44a6e58cb68c3dce28" name = "github.com/gorilla/context" packages = ["."] + pruneopts = "" revision = "08b5f424b9271eedf6f9f0ce86cb9396ed337a42" version = "v1.1.1" [[projects]] + digest = "1:c2c8666b4836c81a1d247bdf21c6a6fc1ab586538ab56f74437c2e0df5c375e1" name = "github.com/gorilla/mux" packages = ["."] + pruneopts = "" revision = "e3702bed27f0d39777b0b37b664b6280e8ef8fbf" version = "v1.6.2" [[projects]] branch = "master" + digest = "1:60b7bc5e043a11213472ae05252527287d20e0a6ccc18f6ae67fad88e41004de" name = "github.com/hailocab/go-hostpool" packages = ["."] + pruneopts = "" revision = "e80d13ce29ede4452c43dea11e79b9bc8a15b478" [[projects]] + digest = "1:69f3f1b3e95b8da5b2e3deb0fc821f7641557e5d500857ec35a3822c8b8b782f" name = "github.com/hashicorp/consul" packages = ["api"] + pruneopts = "" revision = "5174058f0d2bda63fa5198ab96c33d9a909c58ed" version = "v1.1.0" [[projects]] branch = "master" + digest = "1:f5d25fd7bdda08e39e01193ef94a1ebf7547b1b931bcdec785d08050598f306c" name = "github.com/hashicorp/go-cleanhttp" packages = ["."] + pruneopts = "" revision = "d5fe4b57a186c716b0e00b8c301cbd9b4182694d" [[projects]] branch = "master" + digest = "1:cd5813053beac0114f96a7da3924fc8a15e0cd2b139f079e0fcce5d3244ae304" name = "github.com/hashicorp/go-rootcerts" packages = ["."] + pruneopts = "" revision = "6bb64b370b90e7ef1fa532be9e591a81c3493e00" [[projects]] + digest = "1:d2b2cff454cb23a9769ef3c9075741f5985773a998584b3b3ce203fe4b1abbea" name = "github.com/hashicorp/serf" packages = ["coordinate"] + pruneopts = "" revision = "d6574a5bb1226678d7010325fb6c985db20ee458" version = "v0.8.1" [[projects]] + digest = "1:cc0cf2e12280074e5c6dc0f15a4bb3d6c43509e6091cdcdcc83eea491577257b" name = "github.com/influxdata/go-syslog" packages = [ "rfc5424", - "rfc5425" + "rfc5425", ] + pruneopts = "" revision = "eecd51df3ad85464a2bab9b7d3a45bc1e299059e" version = "v1.0.1" [[projects]] branch = "master" + digest = "1:effc58ad45323ad15159bbca533be4870eaddb2d9a513d3488d8bfe822c83532" name = "github.com/influxdata/tail" packages = [ ".", "ratelimiter", "util", "watch", - "winfile" + "winfile", ] + pruneopts = "" revision = "c43482518d410361b6c383d7aebce33d0471d7bc" [[projects]] branch = "master" + digest = "1:d31edcf33a3b36218de96e43f3fec18ea96deb2a28b838a3a01a4df856ded345" name = "github.com/influxdata/toml" packages = [ ".", - "ast" + "ast", ] + pruneopts = "" revision = "2a2e3012f7cfbef64091cc79776311e65dfa211b" [[projects]] branch = "master" + digest = "1:a0c157916be0b4de1d4565b1f094b8d746109f94968140dff40a42780fa6ccef" name = "github.com/influxdata/wlog" packages = ["."] + pruneopts = "" revision = "7c63b0a71ef8300adc255344d275e10e5c3a71ec" [[projects]] + digest = "1:4197871f269749786aa2406557dba15f10cf79161cdc3998180614c62c8b6351" name = "github.com/jackc/pgx" packages = [ ".", @@ -443,138 +544,176 @@ "pgio", "pgproto3", "pgtype", - "stdlib" + "stdlib", ] + pruneopts = "" revision = "da3231b0b66e2e74cdb779f1d46c5e958ba8be27" version = "v3.1.0" [[projects]] + digest = "1:4f767a115bc8e08576f6d38ab73c376fc1b1cd3bb5041171c9e8668cc7739b52" name = "github.com/jmespath/go-jmespath" packages = ["."] + pruneopts = "" revision = "0b12d6b5" [[projects]] branch = "master" + digest = "1:2c5ad58492804c40bdaf5d92039b0cde8b5becd2b7feeb37d7d1cc36a8aa8dbe" name = "github.com/kardianos/osext" packages = ["."] + pruneopts = "" revision = "ae77be60afb1dcacde03767a8c37337fad28ac14" [[projects]] branch = "master" + digest = "1:2df59f23f11c5c59982f737c98c5523b276bfc85a4773a04b411190402bb30fd" name = "github.com/kardianos/service" packages = ["."] + pruneopts = "" revision = "615a14ed75099c9eaac6949e22ac2341bf9d3197" [[projects]] branch = "master" + digest = "1:63e7368fcf6b54804076eaec26fd9cf0c4466166b272393db4b93102e1e962df" name = "github.com/kballard/go-shellquote" packages = ["."] + pruneopts = "" revision = "95032a82bc518f77982ea72343cc1ade730072f0" [[projects]] branch = "master" + digest = "1:1ed9eeebdf24aadfbca57eb50e6455bd1d2474525e0f0d4454de8c8e9bc7ee9a" name = "github.com/kr/logfmt" packages = ["."] + pruneopts = "" revision = "b84e30acd515aadc4b783ad4ff83aff3299bdfe0" [[projects]] branch = "master" + digest = "1:175f85474bb8dd4ef02b1fad5540345545b78b192e64b961e99e607cce32a716" name = "github.com/mailru/easyjson" packages = [ ".", "buffer", "jlexer", - "jwriter" + "jwriter", ] + pruneopts = "" revision = "3fdea8d05856a0c8df22ed4bc71b3219245e4485" [[projects]] + digest = "1:49a8b01a6cd6558d504b65608214ca40a78000e1b343ed0da5c6a9ccd83d6d30" name = "github.com/matttproud/golang_protobuf_extensions" packages = ["pbutil"] + pruneopts = "" revision = "c12348ce28de40eed0136aa2b644d0ee0650e56c" version = "v1.0.1" [[projects]] + digest = "1:f0bad0fece0fb73c6ea249c18d8e80ffbe86be0457715b04463068f04686cf39" name = "github.com/miekg/dns" packages = ["."] + pruneopts = "" revision = "5a2b9fab83ff0f8bfc99684bd5f43a37abe560f1" version = "v1.0.8" [[projects]] branch = "master" + digest = "1:99651e95333755cbe5c9768c1b80031300acca64a80870b40309202b32585a5a" name = "github.com/mitchellh/go-homedir" packages = ["."] + pruneopts = "" revision = "3864e76763d94a6df2f9960b16a20a33da9f9a66" [[projects]] branch = "master" + digest = "1:eb9117392ee8e7aa44f78e0db603f70b1050ee0ebda4bd40040befb5b218c546" name = "github.com/mitchellh/mapstructure" packages = ["."] + pruneopts = "" revision = "bb74f1db0675b241733089d5a1faa5dd8b0ef57b" [[projects]] + digest = "1:ee2e62b00a9ccc2dba1525f93396e35c847f90f87939df6f361b86315ea5f69a" name = "github.com/multiplay/go-ts3" packages = ["."] + pruneopts = "" revision = "d0d44555495c8776880a17e439399e715a4ef319" version = "v1.0.0" [[projects]] + digest = "1:ccd0def9f0b82b61c5e54fcbfccf528eabb13b489d008e46dc16b808c2e1f765" name = "github.com/naoina/go-stringutil" packages = ["."] + pruneopts = "" revision = "6b638e95a32d0c1131db0e7fe83775cbea4a0d0b" version = "v0.1.0" [[projects]] + digest = "1:7196e09de0e0e057aad363ae7c5a83dba55da7c3865ac1ddabdcd39a72aeb27e" name = "github.com/nats-io/gnatsd" packages = [ "conf", "logger", "server", "server/pse", - "util" + "util", ] + pruneopts = "" revision = "add6d7930ae6d4bff8823b28999ea87bf1bfd23d" version = "v1.1.0" [[projects]] + digest = "1:88f1bde4c172e27b05ed46adfbd0e79dc1663a6281e4b39fa3e39d71ead9621d" name = "github.com/nats-io/go-nats" packages = [ ".", "encoders/builtin", - "util" + "util", ] + pruneopts = "" revision = "062418ea1c2181f52dc0f954f6204370519a868b" version = "v1.5.0" [[projects]] + digest = "1:be61e8224b84064109eaba8157cbb4bbe6ca12443e182b6624fdfa1c0dcf53d9" name = "github.com/nats-io/nuid" packages = ["."] + pruneopts = "" revision = "289cccf02c178dc782430d534e3c1f5b72af807f" version = "v1.0.0" [[projects]] + digest = "1:501cce26a54c785458b0dd54a08ddd984d4ad0c198255430d5d37cd2efe23149" name = "github.com/nsqio/go-nsq" packages = ["."] + pruneopts = "" revision = "eee57a3ac4174c55924125bb15eeeda8cffb6e6f" version = "v1.0.7" [[projects]] branch = "master" + digest = "1:2da0e5077ed40453dc281b9a2428d84cf6ad14063aed189f6296ca5dd25cf13d" name = "github.com/opentracing-contrib/go-observer" packages = ["."] + pruneopts = "" revision = "a52f2342449246d5bcc273e65cbdcfa5f7d6c63c" [[projects]] + digest = "1:bba12aa4747b212f75db3e7fee73fe1b66d303cb3ff0c1984b7f2ad20e8bd2bc" name = "github.com/opentracing/opentracing-go" packages = [ ".", "ext", - "log" + "log", ] + pruneopts = "" revision = "1949ddbfd147afd4d964a9f00b24eb291e0e7c38" version = "v1.0.2" [[projects]] + digest = "1:c6c0db6294924072f98a0de090d200bae4b7102b12a443ba9569c4ba7df52aa1" name = "github.com/openzipkin/zipkin-go-opentracing" packages = [ ".", @@ -582,88 +721,110 @@ "thrift/gen-go/scribe", "thrift/gen-go/zipkincore", "types", - "wire" + "wire", ] + pruneopts = "" revision = "26cf9707480e6b90e5eff22cf0bbf05319154232" version = "v0.3.4" [[projects]] + digest = "1:681dd6745809b277541914db18b4ba159b7f1158a0e37627ce8081d5291fc1ec" name = "github.com/pierrec/lz4" packages = [ ".", - "internal/xxh32" + "internal/xxh32", ] + pruneopts = "" revision = "6b9367c9ff401dbc54fabce3fb8d972e799b702d" version = "v2.0.2" [[projects]] + digest = "1:7365acd48986e205ccb8652cc746f09c8b7876030d53710ea6ef7d0bd0dcd7ca" name = "github.com/pkg/errors" packages = ["."] + pruneopts = "" revision = "645ef00459ed84a119197bfb8d8205042c6df63d" version = "v0.8.0" [[projects]] + digest = "1:256484dbbcd271f9ecebc6795b2df8cad4c458dd0f5fd82a8c2fa0c29f233411" name = "github.com/pmezard/go-difflib" packages = ["difflib"] + pruneopts = "" revision = "792786c7400a136282c1664665ae0a8db921c6c2" version = "v1.0.0" [[projects]] + digest = "1:981835985f655d1d380cc6aa7d9fa9ad7abfaf40c75da200fd40d864cd05a7c3" name = "github.com/prometheus/client_golang" packages = [ "prometheus", - "prometheus/promhttp" + "prometheus/promhttp", ] + pruneopts = "" revision = "c5b7fccd204277076155f10851dad72b76a49317" version = "v0.8.0" [[projects]] branch = "master" + digest = "1:83bf37d060fca77e959fe5ceee81e58bbd1b01836f4addc70043a948e9912547" name = "github.com/prometheus/client_model" packages = ["go"] + pruneopts = "" revision = "99fa1f4be8e564e8a6b613da7fa6f46c9edafc6c" [[projects]] branch = "master" + digest = "1:6a8420870eb2935977da1fff0f3afca9bdb3f1e66258c9e91a8a7ce0b5417c3b" name = "github.com/prometheus/common" packages = [ "expfmt", "internal/bitbucket.org/ww/goautoneg", "log", - "model" + "model", ] + pruneopts = "" revision = "7600349dcfe1abd18d72d3a1770870d9800a7801" [[projects]] branch = "master" + digest = "1:f691cc506fb101ec4d3aacf7ff7bee68419d5b75cc1d3d5be25324bbaa58529a" name = "github.com/prometheus/procfs" packages = [ ".", "internal/util", "nfs", - "xfs" + "xfs", ] + pruneopts = "" revision = "7d6f385de8bea29190f15ba9931442a0eaef9af7" [[projects]] branch = "master" + digest = "1:1b65925989a4dfb6d98ef1d530cda33ab1ff25945b14a22a8b8bb27cc282af70" name = "github.com/rcrowley/go-metrics" packages = ["."] + pruneopts = "" revision = "e2704e165165ec55d062f5919b4b29494e9fa790" [[projects]] branch = "master" + digest = "1:d8fe9f454582e04b5693b59cdebe3f0bd9dc29ad9651bfb1633cba4658b66c65" name = "github.com/samuel/go-zookeeper" packages = ["zk"] + pruneopts = "" revision = "c4fab1ac1bec58281ad0667dc3f0907a9476ac47" [[projects]] + digest = "1:7f569d906bdd20d906b606415b7d794f798f91a62fcfb6a4daa6d50690fb7a3f" name = "github.com/satori/go.uuid" packages = ["."] + pruneopts = "" revision = "f58768cc1a7a7e77a3bd49e98cdd21419399b6a3" version = "v1.2.0" [[projects]] + digest = "1:ad351e057e5b6deef320ac9e8322ed825aba3542229da71e708576b01fa83b6e" name = "github.com/shirou/gopsutil" packages = [ "cpu", @@ -673,70 +834,90 @@ "load", "mem", "net", - "process" + "process", ] + pruneopts = "" revision = "eeb1d38d69593f121e060d24d17f7b1f0936b203" version = "v2.18.05" [[projects]] branch = "master" + digest = "1:99c6a6dab47067c9b898e8c8b13d130c6ab4ffbcc4b7cc6236c2cd0b1e344f5b" name = "github.com/shirou/w32" packages = ["."] + pruneopts = "" revision = "bb4de0191aa41b5507caa14b0650cdbddcd9280b" [[projects]] + digest = "1:f2cc92b78b2f3b76ab0f9daddddd28627bcfcc6cacf119029aa3850082d95079" name = "github.com/sirupsen/logrus" packages = ["."] + pruneopts = "" revision = "c155da19408a8799da419ed3eeb0cb5db0ad5dbc" version = "v1.0.5" [[projects]] branch = "master" + digest = "1:1df855e31f96fac0b7a18115e029bb6ca1c5f40996a06e07bf1ce524d925d454" name = "github.com/soniah/gosnmp" packages = ["."] + pruneopts = "" revision = "bcf840db66be7d64bf96c3c0e075c92e3d98f793" [[projects]] branch = "master" + digest = "1:0a1f8d01a0191f558910bcbfd7e1dc11a53ac374473d13b68b8fe520f21efb07" name = "github.com/streadway/amqp" packages = ["."] + pruneopts = "" revision = "e5adc2ada8b8efff032bf61173a233d143e9318e" [[projects]] + digest = "1:34062a2274daa6ec4d2f50d6070cc51cf4674d6d553ed76b406cb3425b9528e8" name = "github.com/stretchr/objx" packages = ["."] + pruneopts = "" revision = "477a77ecc69700c7cdeb1fa9e129548e1c1c393c" version = "v0.1.1" [[projects]] + digest = "1:bc2a12c8863e1080226b7bc69192efd6c37aaa9b85cec508b0a8f54fabb9bd9f" name = "github.com/stretchr/testify" packages = [ "assert", "mock", - "require" + "require", ] + pruneopts = "" revision = "f35b8ab0b5a2cef36673838d662e249dd9c94686" version = "v1.2.2" [[projects]] + digest = "1:bcc2f7db4796010b35ccd2425fe46849096a4e98ed53f572567cb76de1187b3a" name = "github.com/tidwall/gjson" packages = ["."] + pruneopts = "" revision = "afaeb9562041a8018c74e006551143666aed08bf" version = "v1.1.1" [[projects]] branch = "master" + digest = "1:4db4f92bb9cb04cfc4fccb36aba2598b02a988008c4cc0692b241214ad8ac96e" name = "github.com/tidwall/match" packages = ["."] + pruneopts = "" revision = "1731857f09b1f38450e2c12409748407822dc6be" [[projects]] + digest = "1:23e2b9f3a20cd4a6427147377255ec2f6237e8606fa6ef0707ed79b7bfbe3a83" name = "github.com/vjeantet/grok" packages = ["."] + pruneopts = "" revision = "ce01e59abcf6fbc9833b7deb5e4b8ee1769bcc53" version = "v1.0.0" [[projects]] + digest = "1:e27a90b038891891bb33cb72cb30b8f1b30307e99fd0caf18d3073ac730d4160" name = "github.com/vmware/govmomi" packages = [ ".", @@ -759,42 +940,52 @@ "vim25/progress", "vim25/soap", "vim25/types", - "vim25/xml" + "vim25/xml", ] + pruneopts = "" revision = "e3a01f9611c32b2362366434bcd671516e78955d" version = "v0.18.0" [[projects]] branch = "master" + digest = "1:5383edd40c7f6c95a7dc46a47bf0c83de4bf40a4252f12fa803f790037addffc" name = "github.com/wvanbergen/kafka" packages = ["consumergroup"] + pruneopts = "" revision = "e2edea948ddfee841ea9a263b32ccca15f7d6c2f" [[projects]] branch = "master" + digest = "1:f936b4936e1b092cc41c9b33fdc990ad78386545f1ffeca8427c72b2605bca85" name = "github.com/wvanbergen/kazoo-go" packages = ["."] + pruneopts = "" revision = "f72d8611297a7cf105da904c04198ad701a60101" [[projects]] branch = "master" + digest = "1:9dcb103385e63db2f0eb7e6a8fa2103acddcfb48b25957959f56a0f15188674b" name = "github.com/yuin/gopher-lua" packages = [ ".", "ast", "parse", - "pm" + "pm", ] + pruneopts = "" revision = "ca850f594eaafa5468da2bd53b865e4ee55be18b" [[projects]] branch = "master" + digest = "1:84e9087a94f336c204887281046891769d2ed7bf1d2b31c21ff6fb5e1743abce" name = "github.com/zensqlmonitor/go-mssqldb" packages = ["."] + pruneopts = "" revision = "e8fbf836e44e86764eba398361d1825651709547" [[projects]] branch = "master" + digest = "1:dec0d91bef11bc02ba7d1f72bf251a8309c2e55a0fbea699195d037406c219b0" name = "golang.org/x/crypto" packages = [ "bcrypt", @@ -803,12 +994,14 @@ "ed25519/internal/edwards25519", "md4", "pbkdf2", - "ssh/terminal" + "ssh/terminal", ] + pruneopts = "" revision = "027cca12c2d63e3d62b670d901e8a2c95854feec" [[projects]] branch = "master" + digest = "1:3edb9c19d0b874999053badbbcc08edab3cde0262d2beb36ad6c0d78391c19ac" name = "golang.org/x/net" packages = [ "bpf", @@ -829,12 +1022,14 @@ "ipv6", "proxy", "trace", - "websocket" + "websocket", ] + pruneopts = "" revision = "db08ff08e8622530d9ed3a0e8ac279f6d4c02196" [[projects]] branch = "master" + digest = "1:a90f95e710f8986cec0fc8ca053fa984f3e2e8963941de2e2ab44e565f37b30a" name = "golang.org/x/sys" packages = [ "unix", @@ -843,11 +1038,13 @@ "windows/svc", "windows/svc/debug", "windows/svc/eventlog", - "windows/svc/mgr" + "windows/svc/mgr", ] + pruneopts = "" revision = "6c888cc515d3ed83fc103cf1d84468aad274b0a7" [[projects]] + digest = "1:af9bfca4298ef7502c52b1459df274eed401a4f5498b900e9a92d28d3d87ac5a" name = "golang.org/x/text" packages = [ "collate", @@ -875,24 +1072,30 @@ "unicode/bidi", "unicode/cldr", "unicode/norm", - "unicode/rangetable" + "unicode/rangetable", ] + pruneopts = "" revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0" version = "v0.3.0" [[projects]] + digest = "1:eede11c81b63c8f6fd06ef24ba0a640dc077196ec9b7a58ecde03c82eee2f151" name = "google.golang.org/appengine" packages = ["cloudsql"] + pruneopts = "" revision = "b1f26356af11148e710935ed1ac8a7f5702c7612" version = "v1.1.0" [[projects]] branch = "master" + digest = "1:bca49d2bc942d5839d13a2ee882e6475b96cc5352b549000ecafa13dbd158265" name = "google.golang.org/genproto" packages = ["googleapis/rpc/status"] + pruneopts = "" revision = "32ee49c4dd805befd833990acba36cb75042378c" [[projects]] + digest = "1:6648dc575e3043e47e6bc3a41054a7468dbeef331898eae513df19af09765c30" name = "google.golang.org/grpc" packages = [ ".", @@ -918,90 +1121,208 @@ "stats", "status", "tap", - "transport" + "transport", ] + pruneopts = "" revision = "7a6a684ca69eb4cae85ad0a484f2e531598c047b" version = "v1.12.2" [[projects]] + digest = "1:2840683aa0e9980689f85bf48b2a56ec7a108fd089f12af8ea7d98c172819589" name = "gopkg.in/alecthomas/kingpin.v2" packages = ["."] + pruneopts = "" revision = "947dcec5ba9c011838740e680966fd7087a71d0d" version = "v2.2.6" [[projects]] + digest = "1:a8f8c1725195c4324d4350fae001524ca7489e40d9b6bb47598772e3faa103ba" name = "gopkg.in/asn1-ber.v1" packages = ["."] + pruneopts = "" revision = "379148ca0225df7a432012b8df0355c2a2063ac0" version = "v1.2" [[projects]] + digest = "1:581450ae66d7970d91ef9132459fa583e937c6e502f1b96e4ee7783a56fa0b44" name = "gopkg.in/fatih/pool.v2" packages = ["."] + pruneopts = "" revision = "010e0b745d12eaf8426c95f9c3924d81dd0b668f" version = "v2.0.0" [[projects]] + digest = "1:b2106f1668ea5efc1ecc480f7e922a093adb9563fd9ce58585292871f0d0f229" name = "gopkg.in/fsnotify.v1" packages = ["."] + pruneopts = "" revision = "c2828203cd70a50dcccfb2761f8b1f8ceef9a8e9" source = "https://github.com/fsnotify/fsnotify/archive/v1.4.7.tar.gz" version = "v1.4.7" [[projects]] + digest = "1:5fa5df18f3bd9cad28ed7f263b15da217945735110898fa2b9af25cdafb9cbf3" name = "gopkg.in/gorethink/gorethink.v3" packages = [ ".", "encoding", "ql2", - "types" + "types", ] + pruneopts = "" revision = "7f5bdfd858bb064d80559b2a32b86669c5de5d3b" version = "v3.0.5" [[projects]] + digest = "1:74163d1887c0821951e6f1795a1d10338f45f09d9067cb4a8edcf7ee481724ee" name = "gopkg.in/ldap.v2" packages = ["."] + pruneopts = "" revision = "bb7a9ca6e4fbc2129e3db588a34bc970ffe811a9" version = "v2.5.1" [[projects]] branch = "v2" + digest = "1:95fc0dfa0995fff96935315321df859c5c6b1b81df687ed87a2dcf1767f1b465" name = "gopkg.in/mgo.v2" packages = [ ".", "bson", "internal/json", "internal/sasl", - "internal/scram" + "internal/scram", ] + pruneopts = "" revision = "3f83fa5005286a7fe593b055f0d7771a7dce4655" [[projects]] + digest = "1:e7f4f325f729e4c0e93fdac23b05946ab5f9761af4a61615144024774a25b0cb" name = "gopkg.in/olivere/elastic.v5" packages = [ ".", "config", - "uritemplates" + "uritemplates", ] + pruneopts = "" revision = "b708306d715bea9b983685e94ab4602cdc9f988b" version = "v5.0.69" [[projects]] branch = "v1" + digest = "1:a96d16bd088460f2e0685d46c39bcf1208ba46e0a977be2df49864ec7da447dd" name = "gopkg.in/tomb.v1" packages = ["."] + pruneopts = "" revision = "dd632973f1e7218eb1089048e0798ec9ae7dceb8" [[projects]] + digest = "1:f0620375dd1f6251d9973b5f2596228cc8042e887cd7f827e4220bc1ce8c30e2" name = "gopkg.in/yaml.v2" packages = ["."] + pruneopts = "" revision = "5420a8b6744d3b0345ab293f6fcba19c978f1183" version = "v2.2.1" [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "5201186bc1dbb97c0427ab3cfa769dd5242d21d199a5e97f5a413d775760aa7e" + input-imports = [ + "collectd.org/api", + "collectd.org/network", + "github.com/Microsoft/ApplicationInsights-Go/appinsights", + "github.com/Shopify/sarama", + "github.com/StackExchange/wmi", + "github.com/aerospike/aerospike-client-go", + "github.com/amir/raidman", + "github.com/apache/thrift/lib/go/thrift", + "github.com/aws/aws-sdk-go/aws", + "github.com/aws/aws-sdk-go/aws/client", + "github.com/aws/aws-sdk-go/aws/credentials", + "github.com/aws/aws-sdk-go/aws/credentials/stscreds", + "github.com/aws/aws-sdk-go/aws/session", + "github.com/aws/aws-sdk-go/service/cloudwatch", + "github.com/aws/aws-sdk-go/service/kinesis", + "github.com/aws/aws-sdk-go/service/sts", + "github.com/bsm/sarama-cluster", + "github.com/couchbase/go-couchbase", + "github.com/dgrijalva/jwt-go", + "github.com/docker/docker/api/types", + "github.com/docker/docker/api/types/container", + "github.com/docker/docker/api/types/filters", + "github.com/docker/docker/api/types/registry", + "github.com/docker/docker/api/types/swarm", + "github.com/docker/docker/client", + "github.com/docker/go-connections/sockets", + "github.com/eclipse/paho.mqtt.golang", + "github.com/go-redis/redis", + "github.com/go-sql-driver/mysql", + "github.com/gobwas/glob", + "github.com/golang/protobuf/proto", + "github.com/google/go-cmp/cmp", + "github.com/gorilla/mux", + "github.com/hashicorp/consul/api", + "github.com/influxdata/go-syslog/rfc5424", + "github.com/influxdata/go-syslog/rfc5425", + "github.com/influxdata/tail", + "github.com/influxdata/toml", + "github.com/influxdata/toml/ast", + "github.com/influxdata/wlog", + "github.com/jackc/pgx/stdlib", + "github.com/kardianos/service", + "github.com/kballard/go-shellquote", + "github.com/matttproud/golang_protobuf_extensions/pbutil", + "github.com/miekg/dns", + "github.com/multiplay/go-ts3", + "github.com/nats-io/gnatsd/server", + "github.com/nats-io/go-nats", + "github.com/nsqio/go-nsq", + "github.com/openzipkin/zipkin-go-opentracing", + "github.com/openzipkin/zipkin-go-opentracing/thrift/gen-go/zipkincore", + "github.com/prometheus/client_golang/prometheus", + "github.com/prometheus/client_golang/prometheus/promhttp", + "github.com/prometheus/client_model/go", + "github.com/prometheus/common/expfmt", + "github.com/prometheus/common/log", + "github.com/satori/go.uuid", + "github.com/shirou/gopsutil/cpu", + "github.com/shirou/gopsutil/disk", + "github.com/shirou/gopsutil/host", + "github.com/shirou/gopsutil/load", + "github.com/shirou/gopsutil/mem", + "github.com/shirou/gopsutil/net", + "github.com/shirou/gopsutil/process", + "github.com/soniah/gosnmp", + "github.com/streadway/amqp", + "github.com/stretchr/testify/assert", + "github.com/stretchr/testify/mock", + "github.com/stretchr/testify/require", + "github.com/tidwall/gjson", + "github.com/vjeantet/grok", + "github.com/vmware/govmomi", + "github.com/vmware/govmomi/performance", + "github.com/vmware/govmomi/simulator", + "github.com/vmware/govmomi/view", + "github.com/vmware/govmomi/vim25/mo", + "github.com/vmware/govmomi/vim25/soap", + "github.com/vmware/govmomi/vim25/types", + "github.com/wvanbergen/kafka/consumergroup", + "github.com/zensqlmonitor/go-mssqldb", + "golang.org/x/net/context", + "golang.org/x/net/html/charset", + "golang.org/x/sys/unix", + "golang.org/x/sys/windows", + "golang.org/x/sys/windows/svc", + "golang.org/x/sys/windows/svc/mgr", + "google.golang.org/grpc", + "google.golang.org/grpc/codes", + "google.golang.org/grpc/credentials", + "google.golang.org/grpc/status", + "gopkg.in/gorethink/gorethink.v3", + "gopkg.in/ldap.v2", + "gopkg.in/mgo.v2", + "gopkg.in/mgo.v2/bson", + "gopkg.in/olivere/elastic.v5", + "gopkg.in/yaml.v2", + ] solver-name = "gps-cdcl" solver-version = 1 diff --git a/plugins/inputs/vsphere/client.go b/plugins/inputs/vsphere/client.go index 15ec8564adf4d..aa759963c3b70 100644 --- a/plugins/inputs/vsphere/client.go +++ b/plugins/inputs/vsphere/client.go @@ -40,7 +40,6 @@ func NewClient(u *url.URL, vs *VSphere) (*Client, error) { // Add certificate if we have it // if tlsCfg != nil && len(tlsCfg.Certificates) > 0 { - log.Printf("D! Creating client with Certificate: %s", u.Host) c.Client.SetCertificate(tlsCfg.Certificates[0]) } diff --git a/plugins/inputs/vsphere/endpoint.go b/plugins/inputs/vsphere/endpoint.go index 80c0da774a691..65041214ee5d2 100644 --- a/plugins/inputs/vsphere/endpoint.go +++ b/plugins/inputs/vsphere/endpoint.go @@ -7,6 +7,7 @@ import ( "strconv" "strings" "sync" + "sync/atomic" "time" "github.com/gobwas/glob" @@ -25,7 +26,7 @@ type Endpoint struct { pool Pool lastColls map[string]time.Time instanceInfo map[string]resourceInfo - resources map[string]resource + resourceKinds map[string]resourceKind metricNames map[int32]string discoveryTicker *time.Ticker clientMux sync.Mutex @@ -33,15 +34,16 @@ type Endpoint struct { initialized bool } -type resource struct { - pKey string - enabled bool - realTime bool - sampling int32 - objects objectMap - includes []string - excludes []string - getObjects func(*view.ContainerView) (objectMap, error) +type resourceKind struct { + pKey string + enabled bool + realTime bool + sampling int32 + objects objectMap + includes []string + excludes []string + collectInstances bool + getObjects func(*view.ContainerView) (objectMap, error) } type metricEntry struct { @@ -65,6 +67,11 @@ type resourceInfo struct { metrics performance.MetricList } +type metricQResponse struct { + obj objectRef + metrics *performance.MetricList +} + // NewEndpoint returns a new connection to a vCenter based on the URL and configuration passed // as parameters. func NewEndpoint(parent *VSphere, url *url.URL) *Endpoint { @@ -77,46 +84,50 @@ func NewEndpoint(parent *VSphere, url *url.URL) *Endpoint { initialized: false, } - e.resources = map[string]resource{ + e.resourceKinds = map[string]resourceKind{ "cluster": { - pKey: "clustername", - enabled: parent.GatherClusters, - realTime: false, - sampling: 300, - objects: make(objectMap), - includes: parent.ClusterMetricInclude, - excludes: parent.ClusterMetricExclude, - getObjects: getClusters, + pKey: "clustername", + enabled: parent.GatherClusters, + realTime: false, + sampling: 300, + objects: make(objectMap), + includes: parent.ClusterMetricInclude, + excludes: parent.ClusterMetricExclude, + collectInstances: parent.ClusterInstances, + getObjects: getClusters, }, "host": { - pKey: "esxhostname", - enabled: parent.GatherHosts, - realTime: true, - sampling: 20, - objects: make(objectMap), - includes: parent.HostMetricInclude, - excludes: parent.HostMetricExclude, - getObjects: getHosts, + pKey: "esxhostname", + enabled: parent.GatherHosts, + realTime: true, + sampling: 20, + objects: make(objectMap), + includes: parent.HostMetricInclude, + excludes: parent.HostMetricExclude, + collectInstances: parent.HostInstances, + getObjects: getHosts, }, "vm": { - pKey: "vmname", - enabled: parent.GatherVms, - realTime: true, - sampling: 20, - objects: make(objectMap), - includes: parent.VmMetricInclude, - excludes: parent.VmMetricExclude, - getObjects: getVMs, + pKey: "vmname", + enabled: parent.GatherVms, + realTime: true, + sampling: 20, + objects: make(objectMap), + includes: parent.VmMetricInclude, + excludes: parent.VmMetricExclude, + collectInstances: parent.VmInstances, + getObjects: getVMs, }, "datastore": { - pKey: "dsname", - enabled: parent.GatherDatastores, - realTime: false, - sampling: 300, - objects: make(objectMap), - includes: parent.DatastoreMetricInclude, - excludes: parent.DatastoreMetricExclude, - getObjects: getDatastores, + pKey: "dsname", + enabled: parent.GatherDatastores, + realTime: false, + sampling: 300, + objects: make(objectMap), + includes: parent.DatastoreMetricInclude, + excludes: parent.DatastoreMetricExclude, + collectInstances: parent.DatastoreInstances, + getObjects: getDatastores, }, } @@ -132,25 +143,36 @@ func (e *Endpoint) init() error { } if e.Parent.ObjectDiscoveryInterval.Duration.Seconds() > 0 { - // Run an initial discovery. - // - err = e.discover() - if err != nil { - log.Printf("E! Error in initial discovery for %s: %v", e.URL.Host, err) - return err + discoverFunc := func() { + err = e.discover() + if err != nil { + log.Printf("E! Error in initial discovery for %s: %v", e.URL.Host, err) + } + + // Create discovery ticker + // + e.discoveryTicker = time.NewTicker(e.Parent.ObjectDiscoveryInterval.Duration) + go func() { + for range e.discoveryTicker.C { + err := e.discover() + if err != nil { + log.Printf("E! Error in discovery for %s: %v", e.URL.Host, err) + } + } + }() } - // Create discovery ticker + // Run an initial discovery. If force_discovery_on_init isn't set, we kick it off as a + // goroutine without waiting for it. This will probably cause us to report an empty + // dataset on the first collection, but it solves the issue of the first collection timing out. // - e.discoveryTicker = time.NewTicker(e.Parent.ObjectDiscoveryInterval.Duration) - go func() { - for range e.discoveryTicker.C { - err := e.discover() - if err != nil { - log.Printf("E! Error in discovery for %s: %v", e.URL.Host, err) - } - } - }() + if e.Parent.ForceDiscoverOnInit { + log.Printf("Running initial discovery and waiting for it to finish") + discoverFunc() + } else { + log.Printf("Running initial discovery in the background") + go discoverFunc() + } } e.initialized = true @@ -175,6 +197,34 @@ func (e *Endpoint) setupMetricIds() error { return nil } +func (e *Endpoint) runMetricMetadataGetter(res *resourceKind, in chan objectRef, out chan *metricQResponse) { + client, err := e.pool.Take() + if err != nil { + log.Printf("E! Error getting client. Discovery will be incomplete. Error: %s", err) + return + } + defer e.pool.Return(client) + for { + select { + case obj, ok := <-in: + if !ok { + // Sending nil means that we're done. + // + log.Println("D! Metadata getter done. Exiting goroutine") + out <- nil + return + } + log.Printf("D! Getting metric metadata for: %s", obj.name) + ctx := context.Background() + metrics, err := client.Perf.AvailableMetric(ctx, obj.ref.Reference(), res.sampling) + if err != nil { + log.Printf("E! Error while getting metric metadata. Discovery will be incomplete. Error: %s", err) + } + out <- &metricQResponse{metrics: &metrics, obj: obj} + } + } +} + func (e *Endpoint) discover() error { start := time.Now() log.Printf("D! Discover new objects for %s", e.URL.Host) @@ -186,11 +236,11 @@ func (e *Endpoint) discover() error { defer e.pool.Return(client) instInfo := make(map[string]resourceInfo) - resources := make(map[string]resource) + resourceKinds := make(map[string]resourceKind) // Populate resource objects, and endpoint name cache // - for k, res := range e.resources { + for k, res := range e.resourceKinds { // Don't be tempted to skip disabled resources here! We may need them to resolve parent references // // Precompile includes and excludes @@ -208,53 +258,77 @@ func (e *Endpoint) discover() error { if res.enabled || (k != "datastore" && k != "vm") { objects, err := res.getObjects(client.Root) if err != nil { - client = nil // Don't reuse this one! + client.Valid = false // Don't reuse this one! return err } + + // Start metadata getters. + // The response channel must have enough buffer to hold all responses, or + // we may deadlock. We also reserve one slot per goroutine to hold the final + // "nil" representing the end of the job. + // + rqCh := make(chan objectRef) + resCh := make(chan *metricQResponse, len(objects)+e.Parent.DiscoverConcurrency) + for i := 0; i < e.Parent.DiscoverConcurrency; i++ { + go e.runMetricMetadataGetter(&res, rqCh, resCh) + } + + // Submit work to the getters + // for _, obj := range objects { - ctx := context.Background() - mList := make(performance.MetricList, 0) - metrics, err := client.Perf.AvailableMetric(ctx, obj.ref.Reference(), res.sampling) - if err != nil { - client = nil // Don't reuse this one! - return err - } - log.Printf("D! Obj: %s, metrics found: %d, enabled: %t", obj.name, len(metrics), res.enabled) + rqCh <- obj + } + close(rqCh) - // Mmetric metadata gathering is only needed for enabled resource types. - // - if res.enabled { - for _, m := range metrics { - include := len(cInc) == 0 // Empty include list means include all - mName := e.metricNames[m.CounterId] - //log.Printf("% %s", mName, cInc) - if !include { // If not included by default - for _, p := range cInc { - if p.Match(mName) { - include = true + // Handle responses as they trickle in. Loop while there are still + // goroutines processing work. + // + alive := e.Parent.DiscoverConcurrency + for alive > 0 { + select { + case resp := <-resCh: + if resp == nil { + // Goroutine is done. + // + alive-- + } else { + mList := make(performance.MetricList, 0) + if res.enabled { + for _, m := range *resp.metrics { + if m.Instance != "" && !res.collectInstances { + continue } - } - } - if include { - for _, p := range cExc { - if p.Match(mName) { - include = false - log.Printf("D! Excluded: %s", mName) - break + include := len(cInc) == 0 // Empty include list means include all + mName := e.metricNames[m.CounterId] + //log.Printf("%s %s", mName, m.Instance) + if !include { // If not included by default + for _, p := range cInc { + if p.Match(mName) { + include = true + } + } + } + if include { + for _, p := range cExc { + if p.Match(mName) { + include = false + log.Printf("D! Excluded: %s", mName) + break + } + } + if include { // If still included after processing excludes + mList = append(mList, m) + //log.Printf("D! Included %s Sampling: %d", mName, res.sampling) + } } - } - if include { // If still included after processing excludes - mList = append(mList, m) - //log.Printf("D! Included %s Sampling: %d", mName, res.sampling) } } - + instInfo[resp.obj.ref.Value] = resourceInfo{name: resp.obj.name, metrics: mList} } } - instInfo[obj.ref.Value] = resourceInfo{name: obj.name, metrics: mList} } res.objects = objects - resources[k] = res + resourceKinds[k] = res } } @@ -264,7 +338,7 @@ func (e *Endpoint) discover() error { defer e.collectMux.Unlock() e.instanceInfo = instInfo - e.resources = resources + e.resourceKinds = resourceKinds log.Printf("D! Discovered %d objects for %s. Took %s", len(instInfo), e.URL.Host, time.Now().Sub(start)) @@ -356,7 +430,7 @@ func (e *Endpoint) collect(acc telegraf.Accumulator) error { return err } } - for k, res := range e.resources { + for k, res := range e.resourceKinds { if res.enabled { count, duration, err := e.collectResource(k, acc) if err != nil { @@ -374,7 +448,7 @@ func (e *Endpoint) collect(acc telegraf.Accumulator) error { func (e *Endpoint) collectResource(resourceType string, acc telegraf.Accumulator) (int, float64, error) { // Do we have new data yet? // - res := e.resources[resourceType] + res := e.resourceKinds[resourceType] now := time.Now() latest, hasLatest := e.lastColls[resourceType] if hasLatest { @@ -392,21 +466,57 @@ func (e *Endpoint) collectResource(resourceType string, acc telegraf.Accumulator log.Printf("D! Start of sample period deemed to be %s", latest) log.Printf("D! Collecting metrics for %d objects of type %s for %s", len(res.objects), resourceType, e.URL.Host) - count := 0 + // Set up collection goroutines + // + count := int64(0) + chunkCh := make(chan []types.PerfQuerySpec) + errorCh := make(chan error, e.Parent.CollectConcurrency) // Try not to block on errors. + doneCh := make(chan bool) + for i := 0; i < e.Parent.CollectConcurrency; i++ { + go func() { + defer func() { doneCh <- true }() + //timeout = time.After() + for { + select { + case chunk, valid := <-chunkCh: + if !valid { + log.Printf("D! No more work. Exiting collection goroutine") + return + } + n, err := e.collectChunk(chunk, resourceType, res, acc) + log.Printf("D! Query returned %d metrics", n) + if err != nil { + errorCh <- err + } + atomic.AddInt64(&count, int64(n)) + } + } + }() + } + start := time.Now() - total := 0 pqs := make([]types.PerfQuerySpec, 0, e.Parent.ObjectsPerQuery) + metrics := 0 + total := 0 + nRes := 0 for _, object := range res.objects { info, found := e.instanceInfo[object.ref.Value] if !found { log.Printf("E! Internal error: Instance info not found for MOID %s", object.ref) } - if len(info.metrics) > 0 { - + mr := len(info.metrics) + for mr > 0 { + mc := mr + headroom := e.Parent.MetricsPerQuery - metrics + if !res.realTime && mc > headroom { // Metric query limit only applies to non-realtime metrics + mc = headroom + } + fm := len(info.metrics) - mr + log.Printf("D! mr: %d, mm: %d, fm: %d", mr, mc, fm) pq := types.PerfQuerySpec{ Entity: object.ref, MaxSample: 1, - MetricId: info.metrics, + MetricId: info.metrics[fm : fm+mc], IntervalId: res.sampling, } @@ -414,33 +524,56 @@ func (e *Endpoint) collectResource(resourceType string, acc telegraf.Accumulator pq.StartTime = &latest pq.EndTime = &now } - pqs = append(pqs, pq) - } else { - log.Printf("D! No metrics available for %s. Skipping.", info.name) - // Maintainers: Don't be tempted to skip a turn in the loop here! We still need to check if - // the chunk needs processing or we risk skipping the last chunk. (Ask me how I found this out... :) ) + mr -= mc + metrics += mc + + // We need to dump the current chunk of metrics for one of three reasons: + // 1) We filled up the metric quota while processing the current resource + // 2) The toral number of metrics exceeds the max + // 3) We are at the last resource and have no more data to process. + // + if mr > 0 || (!res.realTime && metrics >= e.Parent.MetricsPerQuery) /* || total >= len(res.objects)-1 */ || nRes >= e.Parent.ObjectsPerQuery { + log.Printf("D! Querying %d objects, %d metrics (%d remaining) of type %s for %s. Processed objects: %d. Total objects %d, metrics %d", + len(pqs), metrics, mr, resourceType, e.URL.Host, total+1, len(res.objects), count) + chunkCh <- pqs + pqs = make([]types.PerfQuerySpec, 0, e.Parent.ObjectsPerQuery) + metrics = 0 + nRes = 0 + } + log.Printf("D! %d metrics remaining. Total metrics %d", mr, metrics) } total++ + nRes++ + } + // There may be dangling stuff in the queue. Handle them + // + if len(pqs) > 0 { + log.Printf("Pushing dangling buffer with %d objects for %s", len(pqs), resourceType) + chunkCh <- pqs + } - // Filled up a chunk or at end of data? Run a query with the collected objects - // - if len(pqs) > 0 && (len(pqs) >= int(e.Parent.ObjectsPerQuery) || total >= len(res.objects)) { - log.Printf("D! Querying %d objects of type %s for %s. Object count: %d. Total objects %d", len(pqs), resourceType, e.URL.Host, total, len(res.objects)) - n, err := e.collectChunk(pqs, resourceType, res, acc) - if err != nil { - return count, time.Now().Sub(start).Seconds(), err - } - count += n - pqs = make([]types.PerfQuerySpec, 0, e.Parent.ObjectsPerQuery) + var err error + + // Inform collection goroutines that there's no more data and wait for them to finish + // + close(chunkCh) + alive := e.Parent.CollectConcurrency + for alive > 0 { + select { + case <-doneCh: + alive-- + case err = <-errorCh: + log.Printf("E! Error from collection goroutine: %s", err) } } + e.lastColls[resourceType] = now // Use value captured at the beginning to avoid blind spots. log.Printf("D! Collection of %s for %s, took %v returning %d metrics", resourceType, e.URL.Host, time.Now().Sub(start), count) - return count, time.Now().Sub(start).Seconds(), nil + return int(count), time.Now().Sub(start).Seconds(), err } -func (e *Endpoint) collectChunk(pqs []types.PerfQuerySpec, resourceType string, res resource, acc telegraf.Accumulator) (int, error) { +func (e *Endpoint) collectChunk(pqs []types.PerfQuerySpec, resourceType string, res resourceKind, acc telegraf.Accumulator) (int, error) { count := 0 prefix := "vsphere" + e.Parent.Separator + resourceType client, err := e.pool.Take() @@ -452,13 +585,13 @@ func (e *Endpoint) collectChunk(pqs []types.PerfQuerySpec, resourceType string, metrics, err := client.Perf.Query(ctx, pqs) if err != nil { //TODO: Check the error and attempt to handle gracefully. (ie: object no longer exists) - log.Printf("E! Error querying metrics of %s for %s", resourceType, e.URL.Host) + log.Printf("E! Error querying metrics of %s for %s %s", resourceType, e.URL.Host, err) return count, err } ems, err := client.Perf.ToMetricSeries(ctx, metrics) if err != nil { - client = nil + client.Valid = false return count, err } @@ -499,7 +632,6 @@ func (e *Endpoint) collectChunk(pqs []types.PerfQuerySpec, resourceType string, // mn, fn := e.makeMetricIdentifier(prefix, name) bKey := mn + " " + v.Instance + " " + strconv.FormatInt(ts.UnixNano(), 10) - //log.Printf("D! Bucket key: %s, resource: %s, field: %s", bKey, instInfo.name, fn) bucket, found := buckets[bKey] if !found { bucket = metricEntry{name: mn, ts: ts, fields: make(map[string]interface{}), tags: t} @@ -513,15 +645,15 @@ func (e *Endpoint) collectChunk(pqs []types.PerfQuerySpec, resourceType string, // measurement name. Now emit them! // log.Printf("D! Collected %d buckets for %s", len(buckets), instInfo.name) - for key, bucket := range buckets { - log.Printf("D! Key: %s, Tags: %s, Fields: %s", key, bucket.tags, bucket.fields) + for _, bucket := range buckets { + //log.Printf("Bucket key: %s", key) acc.AddFields(bucket.name, bucket.fields, bucket.tags, bucket.ts) } } return count, nil } -func (e *Endpoint) populateTags(objectRef *objectRef, resourceType string, resource *resource, t map[string]string, v *performance.MetricSeries) { +func (e *Endpoint) populateTags(objectRef *objectRef, resourceType string, resource *resourceKind, t map[string]string, v *performance.MetricSeries) { // Map name of object. // if resource.pKey != "" { @@ -540,7 +672,7 @@ func (e *Endpoint) populateTags(objectRef *objectRef, resourceType string, resou case "vm": t["guest"] = objectRef.guest t["esxhostname"] = parent.name - hostRes := e.resources["host"] + hostRes := e.resourceKinds["host"] hostRef, ok := hostRes.objects[objectRef.parentRef.Value] if ok { cluster, ok := e.instanceInfo[hostRef.parentRef.Value] diff --git a/plugins/inputs/vsphere/pool.go b/plugins/inputs/vsphere/pool.go index 71771d1305ef5..4b15c81b8835a 100644 --- a/plugins/inputs/vsphere/pool.go +++ b/plugins/inputs/vsphere/pool.go @@ -1,7 +1,6 @@ package vsphere import ( - "log" "net/url" "sync" "time" @@ -31,20 +30,20 @@ func (p *Pool) Take() (*Client, error) { r := p.root p.root = r.next if r.Client.Valid && r.expiry.UnixNano() > time.Now().UnixNano() { - log.Printf("D! //////// Getting connection from pool") + //log.Printf("D! //////// Getting connection from pool") return r.Client, nil } } // Pool is empty, create a new client! // - log.Printf("D! ******* Pool is empty, creating new client") + //log.Printf("D! ******* Pool is empty, creating new client") return NewClient(p.u, p.v) } // Return put a client back to the free list func (p *Pool) Return(client *Client) { if client == nil || !client.Valid { - log.Printf("E! Connection taken out of pool due to error") + //log.Printf("E! Connection taken out of pool due to error") return // Useful when you want to override a deferred Return } p.mux.Lock() diff --git a/plugins/inputs/vsphere/vsphere.go b/plugins/inputs/vsphere/vsphere.go index 349f59bb5f5f6..387a9d3cd73c7 100644 --- a/plugins/inputs/vsphere/vsphere.go +++ b/plugins/inputs/vsphere/vsphere.go @@ -15,24 +15,33 @@ import ( // VSphere is the top level type for the vSphere input plugin. It contains all the configuration // and a list of connected vSphere endpoints type VSphere struct { + Interval time.Duration Vcenters []string Username string Password string GatherClusters bool + ClusterInstances bool ClusterMetricInclude []string ClusterMetricExclude []string GatherHosts bool + HostInstances bool HostMetricInclude []string HostMetricExclude []string GatherVms bool + VmInstances bool VmMetricInclude []string VmMetricExclude []string GatherDatastores bool + DatastoreInstances bool DatastoreMetricInclude []string DatastoreMetricExclude []string Separator string - ObjectsPerQuery int32 + ObjectsPerQuery int + MetricsPerQuery int + CollectConcurrency int + DiscoverConcurrency int + ForceDiscoverOnInit bool ObjectDiscoveryInterval internal.Duration Timeout internal.Duration @@ -53,25 +62,28 @@ var sampleConfig = ` # gather_vms = true ### (default=true) +## Collect VM instance metrics, such as individual cores? (default=true) +#vm_instances = true + ## Typical VM metrics (if omitted, all metrics are collected) -# vm_metric_include = [ -# "cpu.ready.summation", -# "mem.swapinRate.average", -# "virtualDisk.numberReadAveraged.average", -# "virtualDisk.numberWriteAveraged.average", -# "virtualDisk.totalReadLatency.average", -# "virtualDisk.totalWriteLatency.average", -# "virtualDisk.readOIO.latest", -# "virtualDisk.writeOIO.latest", -# "net.bytesRx.average", -# "net.bytesTx.average", -# "net.droppedRx.summation.", -# "net.droppedTx.summation.", -# "cpu.run.summation", -# "cpu.used.summation", -# "mem.swapoutRate.average", -# "virtualDisk.read.average", -# "virtualDisk.write.average" ] +vm_metric_include = [ + "cpu.ready.summation", + "mem.swapinRate.average", + "virtualDisk.numberReadAveraged.average", + "virtualDisk.numberWriteAveraged.average", + "virtualDisk.totalReadLatency.average", + "virtualDisk.totalWriteLatency.average", + "virtualDisk.readOIO.latest", + "virtualDisk.writeOIO.latest", + "net.bytesRx.average", + "net.bytesTx.average", + "net.droppedRx.summation.", + "net.droppedTx.summation.", + "cpu.run.summation", + "cpu.used.summation", + "mem.swapoutRate.average", + "virtualDisk.read.average", + "virtualDisk.write.average" ] # vm_metric_exclude = [] ## Nothing is excluded by default @@ -79,43 +91,46 @@ var sampleConfig = ` # gather_hosts = true ## (default=true) +## Collect host instance metrics, such as individual cores? (default=true) +#host_instances = true + ## Typical host metrics (if omitted, all metrics are collected) -# host_metric_include = [ -# "cpu.ready.summation", -# "cpu.latency.average", -# "cpu.coreUtilization.average", -# "mem.usage.average", -# "mem.swapinRate.average", -# "mem.state.latest", -# "mem.latency.average", -# "mem.vmmemctl.average", -# "disk.read.average", -# "disk.write.average", -# "disk.numberReadAveraged.average", -# "disk.numberWriteAveraged.average", -# "disk.deviceReadLatency.average", -# "disk.deviceWriteLatency.average", -# "disk.totalReadLatency.average", -# "disk.totalWriteLatency.average", -# "storageAdapter.read.average", -# "storageAdapter.write.average", -# "storageAdapter.numberReadAveraged.average", -# "storageAdapter.numberWriteAveraged.average", -# "net.errorsRx.summation", -# "net.errorsTx.summation", -# "net.bytesRx.average", -# "net.bytesTx.average", -# "cpu.used.summation", -# "cpu.usage.average", -# "cpu.utilization.average", -# "cpu.wait.summation.", -# "cpu.idle.summation", -# "cpu.readiness.average", -# "cpu.costop.summation", -# "cpu.swapwait.summation", -# "mem.swapoutRate.average", -# "disk.kernelReadLatency.average", -# "disk.kernelWriteLatency.average" ] +host_metric_include = [ + "cpu.ready.summation", + "cpu.latency.average", + "cpu.coreUtilization.average", + "mem.usage.average", + "mem.swapinRate.average", + "mem.state.latest", + "mem.latency.average", + "mem.vmmemctl.average", + "disk.read.average", + "disk.write.average", + "disk.numberReadAveraged.average", + "disk.numberWriteAveraged.average", + "disk.deviceReadLatency.average", + "disk.deviceWriteLatency.average", + "disk.totalReadLatency.average", + "disk.totalWriteLatency.average", + "storageAdapter.read.average", + "storageAdapter.write.average", + "storageAdapter.numberReadAveraged.average", + "storageAdapter.numberWriteAveraged.average", + "net.errorsRx.summation", + "net.errorsTx.summation", + "net.bytesRx.average", + "net.bytesTx.average", + "cpu.used.summation", + "cpu.usage.average", + "cpu.utilization.average", + "cpu.wait.summation.", + "cpu.idle.summation", + "cpu.readiness.average", + "cpu.costop.summation", + "cpu.swapwait.summation", + "mem.swapoutRate.average", + "disk.kernelReadLatency.average", + "disk.kernelWriteLatency.average" ] # host_metric_exclude = [] ## Nothing excluded by default @@ -123,12 +138,15 @@ var sampleConfig = ` # gather_clusters = true ## (default=true) +## Collect cluster instance metrics, such as individual cores? (default=true) +#cluster_instances = true + ## Typical cluster metrics (if omitted, all metrics are collected) -#cluster_metric_include = [ -# "cpu.usage.*", -# "cpu.usagemhz.*", -# "mem.usage.*", -# "mem.active.*" ] +cluster_metric_include = [ + "cpu.usage.*", + "cpu.usagemhz.*", + "mem.usage.*", + "mem.active.*" ] # cluster_metric_exclude = [] ## Nothing excluded by default @@ -136,20 +154,26 @@ var sampleConfig = ` # gather_datastores = true ## (default=true) +## Collect datastore instance metrics, such as individual LUNs and datafiles? (default=false) +#datastore_instances = false + ## Typical datastore metrics (if omitted, all metrics are collected) ## ## WARNING: It is not recommended to include all metrics. Your collection will probably time out if you do! ## - datastore_metric_include = [ "datastore.read.average", "datastore.write.average", "datastore.throughput.usage.average", "datastore.throughput.contention.average" ] -## number of objects to retreive per query. set to 64 for vCenter 5.5 and 6.0 (default: 256) +## Number of objects to retreive per query. # objects_per_query = 256 +## Number of metrics (data points) to retrieve in each query for non-realtime metrics (cluster and datastore). +## Set to 64 for vCenter 5.5 and 6.0 (default: 256) +# metrics_per_query = 256 + ## the interval before (re)discovering objects subject to metrics collection (default: 300s) # object_discovery_interval = "300s" @@ -195,6 +219,8 @@ func (v *VSphere) checkEndpoints() { // the data collection and writes all metrics into the Accumulator passed as an argument. func (v *VSphere) Gather(acc telegraf.Accumulator) error { + log.Printf("Interval: %v", v.Interval) + v.checkEndpoints() var wg sync.WaitGroup @@ -218,20 +244,28 @@ func init() { Vcenters: []string{}, GatherClusters: true, + ClusterInstances: true, ClusterMetricInclude: nil, ClusterMetricExclude: nil, GatherHosts: true, + HostInstances: true, HostMetricInclude: nil, HostMetricExclude: nil, GatherVms: true, + VmInstances: true, VmMetricInclude: nil, VmMetricExclude: nil, GatherDatastores: true, + DatastoreInstances: false, DatastoreMetricInclude: nil, DatastoreMetricExclude: nil, Separator: ".", ObjectsPerQuery: 256, + MetricsPerQuery: 256, + CollectConcurrency: 1, + DiscoverConcurrency: 1, + ForceDiscoverOnInit: true, ObjectDiscoveryInterval: internal.Duration{Duration: time.Second * 300}, Timeout: internal.Duration{Duration: time.Second * 20}, } From 4f74ea348033199858b1febe89088f431df54925 Mon Sep 17 00:00:00 2001 From: prydin Date: Thu, 26 Jul 2018 18:36:05 -0400 Subject: [PATCH 076/100] Added workerpool.go for uniform handling of worker pools Reworked and simplified all concurrency Removed some excessive log messages Reworked filter handling. --- plugins/inputs/vsphere/endpoint.go | 311 ++++++++++--------------- plugins/inputs/vsphere/vsphere_test.go | 25 ++ plugins/inputs/vsphere/workerpool.go | 80 +++++++ 3 files changed, 232 insertions(+), 184 deletions(-) create mode 100644 plugins/inputs/vsphere/workerpool.go diff --git a/plugins/inputs/vsphere/endpoint.go b/plugins/inputs/vsphere/endpoint.go index 65041214ee5d2..2e4a16f042517 100644 --- a/plugins/inputs/vsphere/endpoint.go +++ b/plugins/inputs/vsphere/endpoint.go @@ -2,6 +2,7 @@ package vsphere import ( "context" + "fmt" "log" "net/url" "strconv" @@ -10,7 +11,8 @@ import ( "sync/atomic" "time" - "github.com/gobwas/glob" + "github.com/influxdata/telegraf/filter" + "github.com/influxdata/telegraf" "github.com/vmware/govmomi/performance" "github.com/vmware/govmomi/view" @@ -35,13 +37,13 @@ type Endpoint struct { } type resourceKind struct { + name string pKey string enabled bool realTime bool sampling int32 objects objectMap - includes []string - excludes []string + filters filter.Filter collectInstances bool getObjects func(*view.ContainerView) (objectMap, error) } @@ -67,6 +69,11 @@ type resourceInfo struct { metrics performance.MetricList } +type metricQRequest struct { + res *resourceKind + obj objectRef +} + type metricQResponse struct { obj objectRef metrics *performance.MetricList @@ -86,46 +93,46 @@ func NewEndpoint(parent *VSphere, url *url.URL) *Endpoint { e.resourceKinds = map[string]resourceKind{ "cluster": { + name: "cluster", pKey: "clustername", enabled: parent.GatherClusters, realTime: false, sampling: 300, objects: make(objectMap), - includes: parent.ClusterMetricInclude, - excludes: parent.ClusterMetricExclude, + filters: newFilterOrPanic(parent.ClusterMetricInclude, parent.ClusterMetricExclude), collectInstances: parent.ClusterInstances, getObjects: getClusters, }, "host": { + name: "host", pKey: "esxhostname", enabled: parent.GatherHosts, realTime: true, sampling: 20, objects: make(objectMap), - includes: parent.HostMetricInclude, - excludes: parent.HostMetricExclude, + filters: newFilterOrPanic(parent.HostMetricInclude, parent.HostMetricExclude), collectInstances: parent.HostInstances, getObjects: getHosts, }, "vm": { + name: "vm", pKey: "vmname", enabled: parent.GatherVms, realTime: true, sampling: 20, objects: make(objectMap), - includes: parent.VmMetricInclude, - excludes: parent.VmMetricExclude, + filters: newFilterOrPanic(parent.VmMetricInclude, parent.VmMetricExclude), collectInstances: parent.VmInstances, getObjects: getVMs, }, "datastore": { + name: "datastore", pKey: "dsname", enabled: parent.GatherDatastores, realTime: false, sampling: 300, objects: make(objectMap), - includes: parent.DatastoreMetricInclude, - excludes: parent.DatastoreMetricExclude, + filters: newFilterOrPanic(parent.DatastoreMetricInclude, parent.DatastoreMetricExclude), collectInstances: parent.DatastoreInstances, getObjects: getDatastores, }, @@ -134,6 +141,14 @@ func NewEndpoint(parent *VSphere, url *url.URL) *Endpoint { return &e } +func newFilterOrPanic(include []string, exclude []string) filter.Filter { + f, err := filter.NewIncludeExcludeFilter(include, exclude) + if err != nil { + panic(fmt.Sprintf("Include/exclude filters are invalid: %s", err)) + } + return f +} + func (e *Endpoint) init() error { err := e.setupMetricIds() @@ -197,32 +212,20 @@ func (e *Endpoint) setupMetricIds() error { return nil } -func (e *Endpoint) runMetricMetadataGetter(res *resourceKind, in chan objectRef, out chan *metricQResponse) { +func (e *Endpoint) getMetadata(in interface{}) interface{} { + rq := in.(*metricQRequest) client, err := e.pool.Take() if err != nil { - log.Printf("E! Error getting client. Discovery will be incomplete. Error: %s", err) - return + panic(fmt.Sprintf("E! Error getting client. Error: %s", err)) } defer e.pool.Return(client) - for { - select { - case obj, ok := <-in: - if !ok { - // Sending nil means that we're done. - // - log.Println("D! Metadata getter done. Exiting goroutine") - out <- nil - return - } - log.Printf("D! Getting metric metadata for: %s", obj.name) - ctx := context.Background() - metrics, err := client.Perf.AvailableMetric(ctx, obj.ref.Reference(), res.sampling) - if err != nil { - log.Printf("E! Error while getting metric metadata. Discovery will be incomplete. Error: %s", err) - } - out <- &metricQResponse{metrics: &metrics, obj: obj} - } + ctx := context.Background() + //log.Printf("D! Querying metadata for %s", rq.obj.name) + metrics, err := client.Perf.AvailableMetric(ctx, rq.obj.ref.Reference(), rq.res.sampling) + if err != nil { + log.Printf("E! Error while getting metric metadata. Discovery will be incomplete. Error: %s", err) } + return &metricQResponse{metrics: &metrics, obj: rq.obj} } func (e *Endpoint) discover() error { @@ -238,23 +241,10 @@ func (e *Endpoint) discover() error { instInfo := make(map[string]resourceInfo) resourceKinds := make(map[string]resourceKind) - // Populate resource objects, and endpoint name cache + // Populate resource objects, and endpoint instance info. // for k, res := range e.resourceKinds { - // Don't be tempted to skip disabled resources here! We may need them to resolve parent references - // - // Precompile includes and excludes - // - cInc := make([]glob.Glob, len(res.includes)) - for i, p := range res.includes { - cInc[i] = glob.MustCompile(p) - } - cExc := make([]glob.Glob, len(res.excludes)) - for i, p := range res.excludes { - cExc[i] = glob.MustCompile(p) - } // Need to do this for all resource types even if they are not enabled (but datastore) - // if res.enabled || (k != "datastore" && k != "vm") { objects, err := res.getObjects(client.Root) if err != nil { @@ -262,71 +252,34 @@ func (e *Endpoint) discover() error { return err } - // Start metadata getters. - // The response channel must have enough buffer to hold all responses, or - // we may deadlock. We also reserve one slot per goroutine to hold the final - // "nil" representing the end of the job. - // - rqCh := make(chan objectRef) - resCh := make(chan *metricQResponse, len(objects)+e.Parent.DiscoverConcurrency) - for i := 0; i < e.Parent.DiscoverConcurrency; i++ { - go e.runMetricMetadataGetter(&res, rqCh, resCh) - } + // Set up a worker pool for processing metadata queries concurrently + wp := NewWorkerPool(10) + wp.Run(e.getMetadata, e.Parent.DiscoverConcurrency) - // Submit work to the getters - // - for _, obj := range objects { - rqCh <- obj - } - close(rqCh) - - // Handle responses as they trickle in. Loop while there are still - // goroutines processing work. - // - alive := e.Parent.DiscoverConcurrency - for alive > 0 { - select { - case resp := <-resCh: - if resp == nil { - // Goroutine is done. - // - alive-- - } else { - mList := make(performance.MetricList, 0) - if res.enabled { - for _, m := range *resp.metrics { - if m.Instance != "" && !res.collectInstances { - continue - } - include := len(cInc) == 0 // Empty include list means include all - mName := e.metricNames[m.CounterId] - //log.Printf("%s %s", mName, m.Instance) - if !include { // If not included by default - for _, p := range cInc { - if p.Match(mName) { - include = true - } - } - } - if include { - for _, p := range cExc { - if p.Match(mName) { - include = false - log.Printf("D! Excluded: %s", mName) - break - } - } - if include { // If still included after processing excludes - mList = append(mList, m) - //log.Printf("D! Included %s Sampling: %d", mName, res.sampling) - } - } - } + // Fill the input channels with resources that need to be queried + // for metadata. + wp.Fill(func(in chan interface{}) { + for _, obj := range objects { + in <- &metricQRequest{obj: obj, res: &res} + } + }) + + // Drain the resulting metadata and build instance infos. + wp.Drain(func(in interface{}) { + resp := in.(*metricQResponse) + mList := make(performance.MetricList, 0) + if res.enabled { + for _, m := range *resp.metrics { + if m.Instance != "" && !res.collectInstances { + continue + } + if res.filters.Match(e.metricNames[m.CounterId]) { + mList = append(mList, m) } - instInfo[resp.obj.ref.Value] = resourceInfo{name: resp.obj.name, metrics: mList} } } - } + instInfo[resp.obj.ref.Value] = resourceInfo{name: resp.obj.name, metrics: mList} + }) res.objects = objects resourceKinds[k] = res } @@ -351,7 +304,7 @@ func getClusters(root *view.ContainerView) (objectMap, error) { if err != nil { return nil, err } - m := make(objectMap) + m := make(objectMap, len(resources)) for _, r := range resources { m[r.ExtensibleManagedObject.Reference().Value] = objectRef{ name: r.Name, ref: r.ExtensibleManagedObject.Reference(), parentRef: r.Parent} @@ -445,56 +398,7 @@ func (e *Endpoint) collect(acc telegraf.Accumulator) error { return nil } -func (e *Endpoint) collectResource(resourceType string, acc telegraf.Accumulator) (int, float64, error) { - // Do we have new data yet? - // - res := e.resourceKinds[resourceType] - now := time.Now() - latest, hasLatest := e.lastColls[resourceType] - if hasLatest { - elapsed := time.Now().Sub(latest).Seconds() - log.Printf("D! Latest: %s, elapsed: %f, resource: %s", latest, elapsed, resourceType) - if elapsed < float64(res.sampling) { - // No new data would be available. We're outta here! - // - log.Printf("D! Sampling period for %s of %d has not elapsed for %s", resourceType, res.sampling, e.URL.Host) - return 0, 0, nil - } - } else { - latest = time.Now().Add(time.Duration(-res.sampling) * time.Second) - } - log.Printf("D! Start of sample period deemed to be %s", latest) - log.Printf("D! Collecting metrics for %d objects of type %s for %s", len(res.objects), resourceType, e.URL.Host) - - // Set up collection goroutines - // - count := int64(0) - chunkCh := make(chan []types.PerfQuerySpec) - errorCh := make(chan error, e.Parent.CollectConcurrency) // Try not to block on errors. - doneCh := make(chan bool) - for i := 0; i < e.Parent.CollectConcurrency; i++ { - go func() { - defer func() { doneCh <- true }() - //timeout = time.After() - for { - select { - case chunk, valid := <-chunkCh: - if !valid { - log.Printf("D! No more work. Exiting collection goroutine") - return - } - n, err := e.collectChunk(chunk, resourceType, res, acc) - log.Printf("D! Query returned %d metrics", n) - if err != nil { - errorCh <- err - } - atomic.AddInt64(&count, int64(n)) - } - } - }() - } - - start := time.Now() +func (e *Endpoint) chunker(in chan interface{}, res *resourceKind, now time.Time, latest time.Time) { pqs := make([]types.PerfQuerySpec, 0, e.Parent.ObjectsPerQuery) metrics := 0 total := 0 @@ -512,7 +416,6 @@ func (e *Endpoint) collectResource(resourceType string, acc telegraf.Accumulator mc = headroom } fm := len(info.metrics) - mr - log.Printf("D! mr: %d, mm: %d, fm: %d", mr, mc, fm) pq := types.PerfQuerySpec{ Entity: object.ref, MaxSample: 1, @@ -528,20 +431,18 @@ func (e *Endpoint) collectResource(resourceType string, acc telegraf.Accumulator mr -= mc metrics += mc - // We need to dump the current chunk of metrics for one of three reasons: + // We need to dump the current chunk of metrics for one of two reasons: // 1) We filled up the metric quota while processing the current resource - // 2) The toral number of metrics exceeds the max - // 3) We are at the last resource and have no more data to process. + // 2) We are at the last resource and have no more data to process. // - if mr > 0 || (!res.realTime && metrics >= e.Parent.MetricsPerQuery) /* || total >= len(res.objects)-1 */ || nRes >= e.Parent.ObjectsPerQuery { - log.Printf("D! Querying %d objects, %d metrics (%d remaining) of type %s for %s. Processed objects: %d. Total objects %d, metrics %d", - len(pqs), metrics, mr, resourceType, e.URL.Host, total+1, len(res.objects), count) - chunkCh <- pqs + if mr > 0 || (!res.realTime && metrics >= e.Parent.MetricsPerQuery) || nRes >= e.Parent.ObjectsPerQuery { + log.Printf("D! Querying %d objects, %d metrics (%d remaining) of type %s for %s. Processed objects: %d. Total objects %d", + len(pqs), metrics, mr, res.name, e.URL.Host, total+1, len(res.objects)) + in <- pqs pqs = make([]types.PerfQuerySpec, 0, e.Parent.ObjectsPerQuery) metrics = 0 nRes = 0 } - log.Printf("D! %d metrics remaining. Total metrics %d", mr, metrics) } total++ nRes++ @@ -549,31 +450,74 @@ func (e *Endpoint) collectResource(resourceType string, acc telegraf.Accumulator // There may be dangling stuff in the queue. Handle them // if len(pqs) > 0 { - log.Printf("Pushing dangling buffer with %d objects for %s", len(pqs), resourceType) - chunkCh <- pqs + in <- pqs } +} - var err error - - // Inform collection goroutines that there's no more data and wait for them to finish +func (e *Endpoint) collectResource(resourceType string, acc telegraf.Accumulator) (int, float64, error) { + // Do we have new data yet? // - close(chunkCh) - alive := e.Parent.CollectConcurrency - for alive > 0 { - select { - case <-doneCh: - alive-- - case err = <-errorCh: - log.Printf("E! Error from collection goroutine: %s", err) + res := e.resourceKinds[resourceType] + now := time.Now() + latest, hasLatest := e.lastColls[resourceType] + if hasLatest { + elapsed := time.Now().Sub(latest).Seconds() + 5.0 // Allow 5 second jitter. + log.Printf("D! Latest: %s, elapsed: %f, resource: %s", latest, elapsed, resourceType) + if elapsed < float64(res.sampling) { + // No new data would be available. We're outta here! + // + log.Printf("D! Sampling period for %s of %d has not elapsed for %s", + resourceType, res.sampling, e.URL.Host) + return 0, 0, nil } + } else { + latest = time.Now().Add(time.Duration(-res.sampling) * time.Second) } + log.Printf("D! Start of sample period deemed to be %s", latest) + log.Printf("D! Collecting metrics for %d objects of type %s for %s", + len(res.objects), resourceType, e.URL.Host) + + count := int64(0) + // chunkCh := make(chan []types.PerfQuerySpec) + // errorCh := make(chan error, e.Parent.CollectConcurrency) // Try not to block on errors. + // doneCh := make(chan bool) + + // Set up a worker pool for collecting chunk metrics + wp := NewWorkerPool(10) + wp.Run(func(in interface{}) interface{} { + chunk := in.([]types.PerfQuerySpec) + n, err := e.collectChunk(chunk, resourceType, res, acc) + log.Printf("D! Query returned %d metrics", n) + if err != nil { + return err + } + atomic.AddInt64(&count, int64(n)) + return nil + + }, 10) + + // Fill the input channel of the worker queue by running the chunking + // logic implemented in chunker() + wp.Fill(func(in chan interface{}) { + e.chunker(in, &res, now, latest) + }) + + // Drain the pool. We're getting errors back. They should all be nil + var err error + wp.Drain(func(in interface{}) { + if in != nil { + err = in.(error) + } + }) e.lastColls[resourceType] = now // Use value captured at the beginning to avoid blind spots. - log.Printf("D! Collection of %s for %s, took %v returning %d metrics", resourceType, e.URL.Host, time.Now().Sub(start), count) - return int(count), time.Now().Sub(start).Seconds(), err + log.Printf("D! Collection of %s for %s, took %v returning %d metrics", + resourceType, e.URL.Host, time.Now().Sub(now), count) + return int(count), time.Now().Sub(now).Seconds(), err } -func (e *Endpoint) collectChunk(pqs []types.PerfQuerySpec, resourceType string, res resourceKind, acc telegraf.Accumulator) (int, error) { +func (e *Endpoint) collectChunk(pqs []types.PerfQuerySpec, resourceType string, + res resourceKind, acc telegraf.Accumulator) (int, error) { count := 0 prefix := "vsphere" + e.Parent.Separator + resourceType client, err := e.pool.Take() @@ -644,7 +588,6 @@ func (e *Endpoint) collectChunk(pqs []types.PerfQuerySpec, resourceType string, // We've iterated through all the metrics and collected buckets for each // measurement name. Now emit them! // - log.Printf("D! Collected %d buckets for %s", len(buckets), instInfo.name) for _, bucket := range buckets { //log.Printf("Bucket key: %s", key) acc.AddFields(bucket.name, bucket.fields, bucket.tags, bucket.ts) diff --git a/plugins/inputs/vsphere/vsphere_test.go b/plugins/inputs/vsphere/vsphere_test.go index e506c94415e1e..0e4bcf58bf5da 100644 --- a/plugins/inputs/vsphere/vsphere_test.go +++ b/plugins/inputs/vsphere/vsphere_test.go @@ -4,6 +4,7 @@ import ( "crypto/tls" "fmt" "regexp" + "sort" "testing" "time" @@ -204,6 +205,30 @@ func TestParseConfig(t *testing.T) { require.NotNil(t, tab) } +func TestWorkerPool(t *testing.T) { + wp := NewWorkerPool(100) + wp.Run(func(p interface{}) interface{} { + return p.(int) * 2 + }, 10) + + n := 100000 + wp.Fill(func(in chan interface{}) { + for i := 0; i < n; i++ { + in <- i + } + }) + results := make([]int, n) + i := 0 + wp.Drain(func(p interface{}) { + results[i] = p.(int) + i++ + }) + sort.Ints(results) + for i := 0; i < n; i++ { + require.Equal(t, results[i], i*2) + } +} + func TestAll(t *testing.T) { m, s, err := createSim() if err != nil { diff --git a/plugins/inputs/vsphere/workerpool.go b/plugins/inputs/vsphere/workerpool.go new file mode 100644 index 0000000000000..21ad3e11d7632 --- /dev/null +++ b/plugins/inputs/vsphere/workerpool.go @@ -0,0 +1,80 @@ +package vsphere + +import ( + "sync" +) + +// WorkerFunc is a function that is supposed to do the actual work +// of the WorkerPool. It is similar to the "map" portion of the +// map/reduce semantics, in that it takes a single value as an input, +// does some processing and returns a single result. +type WorkerFunc func(interface{}) interface{} + +// DrainerFunc represents a function used to "drain" the WorkerPool, +// i.e. pull out all the results generated by the workers and processing +// them. The DrainerFunc is called once per result produced. +type DrainerFunc func(interface{}) + +// FillerFunc represents a function for filling the WorkerPool with jobs. +// It is called once and is responsible for pushing jobs onto the supplied channel. +type FillerFunc func(chan interface{}) + +// WorkerPool implements a simple work pooling mechanism. It runs a predefined +// number of goroutines to process jobs. Jobs are inserted using the Fill call +// and results are retrieved through the Drain function. +type WorkerPool struct { + wg sync.WaitGroup + In chan interface{} + Out chan interface{} +} + +// NewWorkerPool creates a worker pool +func NewWorkerPool(bufsize int) *WorkerPool { + return &WorkerPool{ + In: make(chan interface{}, bufsize), + Out: make(chan interface{}, bufsize), + } +} + +// Run takes a WorkerFunc and runs it in 'n' goroutines. +func (w *WorkerPool) Run(f WorkerFunc, n int) { + w.wg.Add(1) + go func() { + defer w.wg.Done() + var localWg sync.WaitGroup + localWg.Add(n) + for i := 0; i < n; i++ { + go func() { + defer localWg.Done() + for job := range w.In { + w.Out <- f(job) + } + }() + } + localWg.Wait() + close(w.Out) + }() +} + +// Fill runs a FillerFunc responsible for supplying work to the pool. You may only +// call Fill once. Calling it twice will panic. +func (w *WorkerPool) Fill(f FillerFunc) { + w.wg.Add(1) + go func() { + defer w.wg.Done() + f(w.In) + close(w.In) + }() +} + +// Drain runs a DrainerFunc for each result generated by the workers. +func (w *WorkerPool) Drain(f DrainerFunc) { + w.wg.Add(1) + go func() { + defer w.wg.Done() + for result := range w.Out { + f(result) + } + }() + w.wg.Wait() +} From 603993bcfedc4cde1e2e77a6639d139cc9d1f5b8 Mon Sep 17 00:00:00 2001 From: prydin Date: Sat, 28 Jul 2018 13:34:48 -0400 Subject: [PATCH 077/100] Logging timers and counters using the 'internal' plugin. --- plugins/inputs/vsphere/client.go | 3 + plugins/inputs/vsphere/endpoint.go | 80 ++++++++++++++------------ plugins/inputs/vsphere/pool.go | 57 ------------------ plugins/inputs/vsphere/vsphere.go | 15 ++--- plugins/inputs/vsphere/vsphere_test.go | 4 +- 5 files changed, 55 insertions(+), 104 deletions(-) delete mode 100644 plugins/inputs/vsphere/pool.go diff --git a/plugins/inputs/vsphere/client.go b/plugins/inputs/vsphere/client.go index aa759963c3b70..224b754c75fb7 100644 --- a/plugins/inputs/vsphere/client.go +++ b/plugins/inputs/vsphere/client.go @@ -21,6 +21,7 @@ type Client struct { // NewClient creates a new vSphere client based on the url and setting passed as parameters. func NewClient(u *url.URL, vs *VSphere) (*Client, error) { + sw := NewStopwatch("connect", u.Host) tlsCfg, err := vs.TLSConfig() if err != nil { return nil, err @@ -53,6 +54,8 @@ func NewClient(u *url.URL, vs *VSphere) (*Client, error) { p := performance.NewManager(c.Client) + sw.Stop() + return &Client{ Client: c, Views: m, diff --git a/plugins/inputs/vsphere/endpoint.go b/plugins/inputs/vsphere/endpoint.go index 2e4a16f042517..1b5629050da68 100644 --- a/plugins/inputs/vsphere/endpoint.go +++ b/plugins/inputs/vsphere/endpoint.go @@ -25,7 +25,6 @@ import ( type Endpoint struct { Parent *VSphere URL *url.URL - pool Pool lastColls map[string]time.Time instanceInfo map[string]resourceInfo resourceKinds map[string]resourceKind @@ -34,6 +33,8 @@ type Endpoint struct { clientMux sync.Mutex collectMux sync.RWMutex initialized bool + collectClient *Client + discoverClient *Client } type resourceKind struct { @@ -83,7 +84,6 @@ type metricQResponse struct { // as parameters. func NewEndpoint(parent *VSphere, url *url.URL) *Endpoint { e := Endpoint{ - pool: Pool{u: url, v: parent, root: nil}, URL: url, Parent: parent, lastColls: make(map[string]time.Time), @@ -121,8 +121,8 @@ func NewEndpoint(parent *VSphere, url *url.URL) *Endpoint { realTime: true, sampling: 20, objects: make(objectMap), - filters: newFilterOrPanic(parent.VmMetricInclude, parent.VmMetricExclude), - collectInstances: parent.VmInstances, + filters: newFilterOrPanic(parent.VMMetricInclude, parent.VMMetricExclude), + collectInstances: parent.VMInstances, getObjects: getVMs, }, "datastore": { @@ -182,24 +182,21 @@ func (e *Endpoint) init() error { // dataset on the first collection, but it solves the issue of the first collection timing out. // if e.Parent.ForceDiscoverOnInit { - log.Printf("Running initial discovery and waiting for it to finish") + log.Printf("D! Running initial discovery and waiting for it to finish") discoverFunc() - } else { - log.Printf("Running initial discovery in the background") - go discoverFunc() } } - e.initialized = true return nil } func (e *Endpoint) setupMetricIds() error { - client, err := e.pool.Take() + client, err := NewClient(e.URL, e.Parent) if err != nil { return err } - defer e.pool.Return(client) + defer client.Close() + ctx := context.Background() mn, err := client.Perf.CounterInfoByName(ctx) if err != nil { @@ -214,14 +211,9 @@ func (e *Endpoint) setupMetricIds() error { func (e *Endpoint) getMetadata(in interface{}) interface{} { rq := in.(*metricQRequest) - client, err := e.pool.Take() - if err != nil { - panic(fmt.Sprintf("E! Error getting client. Error: %s", err)) - } - defer e.pool.Return(client) ctx := context.Background() //log.Printf("D! Querying metadata for %s", rq.obj.name) - metrics, err := client.Perf.AvailableMetric(ctx, rq.obj.ref.Reference(), rq.res.sampling) + metrics, err := e.discoverClient.Perf.AvailableMetric(ctx, rq.obj.ref.Reference(), rq.res.sampling) if err != nil { log.Printf("E! Error while getting metric metadata. Discovery will be incomplete. Error: %s", err) } @@ -229,14 +221,18 @@ func (e *Endpoint) getMetadata(in interface{}) interface{} { } func (e *Endpoint) discover() error { - start := time.Now() - log.Printf("D! Discover new objects for %s", e.URL.Host) - - client, err := e.pool.Take() + sw := NewStopwatch("discover", e.URL.Host) + var err error + e.discoverClient, err = NewClient(e.URL, e.Parent) if err != nil { return err } - defer e.pool.Return(client) + defer func() { + e.discoverClient.Close() + e.discoverClient = nil + }() + + log.Printf("D! Discover new objects for %s", e.URL.Host) instInfo := make(map[string]resourceInfo) resourceKinds := make(map[string]resourceKind) @@ -246,9 +242,8 @@ func (e *Endpoint) discover() error { for k, res := range e.resourceKinds { // Need to do this for all resource types even if they are not enabled (but datastore) if res.enabled || (k != "datastore" && k != "vm") { - objects, err := res.getObjects(client.Root) + objects, err := res.getObjects(e.discoverClient.Root) if err != nil { - client.Valid = false // Don't reuse this one! return err } @@ -293,8 +288,8 @@ func (e *Endpoint) discover() error { e.instanceInfo = instInfo e.resourceKinds = resourceKinds - log.Printf("D! Discovered %d objects for %s. Took %s", len(instInfo), e.URL.Host, time.Now().Sub(start)) - + sw.Stop() + SendInternalCounter("discovered_objects", e.URL.Host, int64(len(instInfo))) return nil } @@ -374,6 +369,15 @@ func (e *Endpoint) collect(acc telegraf.Accumulator) error { e.collectMux.RLock() defer e.collectMux.RUnlock() + e.collectClient, err = NewClient(e.URL, e.Parent) + if err != nil { + return err + } + defer func() { + e.collectClient.Close() + e.collectClient = nil + }() + // If discovery interval is disabled (0), discover on each collection cycle // if e.Parent.ObjectDiscoveryInterval.Duration.Seconds() == 0 { @@ -473,6 +477,10 @@ func (e *Endpoint) collectResource(resourceType string, acc telegraf.Accumulator } else { latest = time.Now().Add(time.Duration(-res.sampling) * time.Second) } + + internalTags := map[string]string{"resourcetype": resourceType} + sw := NewStopwatchWithTags("endpoint_gather", e.URL.Host, internalTags) + log.Printf("D! Start of sample period deemed to be %s", latest) log.Printf("D! Collecting metrics for %d objects of type %s for %s", len(res.objects), resourceType, e.URL.Host) @@ -511,8 +519,9 @@ func (e *Endpoint) collectResource(resourceType string, acc telegraf.Accumulator }) e.lastColls[resourceType] = now // Use value captured at the beginning to avoid blind spots. - log.Printf("D! Collection of %s for %s, took %v returning %d metrics", - resourceType, e.URL.Host, time.Now().Sub(now), count) + + sw.Stop() + SendInternalCounterWithTags("endpoint_gather_count", e.URL.Host, internalTags, count) return int(count), time.Now().Sub(now).Seconds(), err } @@ -520,22 +529,17 @@ func (e *Endpoint) collectChunk(pqs []types.PerfQuerySpec, resourceType string, res resourceKind, acc telegraf.Accumulator) (int, error) { count := 0 prefix := "vsphere" + e.Parent.Separator + resourceType - client, err := e.pool.Take() - if err != nil { - return 0, err - } - defer e.pool.Return(client) + ctx := context.Background() - metrics, err := client.Perf.Query(ctx, pqs) + metrics, err := e.collectClient.Perf.Query(ctx, pqs) if err != nil { //TODO: Check the error and attempt to handle gracefully. (ie: object no longer exists) log.Printf("E! Error querying metrics of %s for %s %s", resourceType, e.URL.Host, err) return count, err } - ems, err := client.Perf.ToMetricSeries(ctx, metrics) + ems, err := e.collectClient.Perf.ToMetricSeries(ctx, metrics) if err != nil { - client.Valid = false return count, err } @@ -581,6 +585,10 @@ func (e *Endpoint) collectChunk(pqs []types.PerfQuerySpec, resourceType string, bucket = metricEntry{name: mn, ts: ts, fields: make(map[string]interface{}), tags: t} buckets[bKey] = bucket } + if value < 0 { + log.Printf("D! Negative value for %s on %s. Indicates missing samples", name, objectRef.name) + continue + } bucket.fields[fn] = value count++ } diff --git a/plugins/inputs/vsphere/pool.go b/plugins/inputs/vsphere/pool.go deleted file mode 100644 index 4b15c81b8835a..0000000000000 --- a/plugins/inputs/vsphere/pool.go +++ /dev/null @@ -1,57 +0,0 @@ -package vsphere - -import ( - "net/url" - "sync" - "time" -) - -const ttl time.Duration = time.Minute * 30 // Time before we discard a pooled connection - -type poolMember struct { - Client *Client - next *poolMember - expiry time.Time -} - -// Pool is a simple free-list based pool of vSphere clients -type Pool struct { - u *url.URL - v *VSphere - root *poolMember - mux sync.Mutex -} - -// Take returns a client, either by picking an available one from the pool or creating a new one -func (p *Pool) Take() (*Client, error) { - p.mux.Lock() - defer p.mux.Unlock() - for p.root != nil { - r := p.root - p.root = r.next - if r.Client.Valid && r.expiry.UnixNano() > time.Now().UnixNano() { - //log.Printf("D! //////// Getting connection from pool") - return r.Client, nil - } - } - // Pool is empty, create a new client! - // - //log.Printf("D! ******* Pool is empty, creating new client") - return NewClient(p.u, p.v) -} - -// Return put a client back to the free list -func (p *Pool) Return(client *Client) { - if client == nil || !client.Valid { - //log.Printf("E! Connection taken out of pool due to error") - return // Useful when you want to override a deferred Return - } - p.mux.Lock() - defer p.mux.Unlock() - r := &poolMember{ - Client: client, - next: p.root, - expiry: time.Now().Add(ttl), - } - p.root = r -} diff --git a/plugins/inputs/vsphere/vsphere.go b/plugins/inputs/vsphere/vsphere.go index 387a9d3cd73c7..230aa7ad71ecd 100644 --- a/plugins/inputs/vsphere/vsphere.go +++ b/plugins/inputs/vsphere/vsphere.go @@ -28,9 +28,9 @@ type VSphere struct { HostMetricInclude []string HostMetricExclude []string GatherVms bool - VmInstances bool - VmMetricInclude []string - VmMetricExclude []string + VMInstances bool `toml:"vm_instances"` + VMMetricInclude []string `toml:"vm_metric_include"` + VMMetricExclude []string `toml:"vm_metric_exclude"` GatherDatastores bool DatastoreInstances bool DatastoreMetricInclude []string @@ -218,9 +218,6 @@ func (v *VSphere) checkEndpoints() { // Gather is the main data collection function called by the Telegraf core. It performs all // the data collection and writes all metrics into the Accumulator passed as an argument. func (v *VSphere) Gather(acc telegraf.Accumulator) error { - - log.Printf("Interval: %v", v.Interval) - v.checkEndpoints() var wg sync.WaitGroup @@ -252,9 +249,9 @@ func init() { HostMetricInclude: nil, HostMetricExclude: nil, GatherVms: true, - VmInstances: true, - VmMetricInclude: nil, - VmMetricExclude: nil, + VMInstances: true, + VMMetricInclude: nil, + VMMetricExclude: nil, GatherDatastores: true, DatastoreInstances: false, DatastoreMetricInclude: nil, diff --git a/plugins/inputs/vsphere/vsphere_test.go b/plugins/inputs/vsphere/vsphere_test.go index 0e4bcf58bf5da..0ee130ab725da 100644 --- a/plugins/inputs/vsphere/vsphere_test.go +++ b/plugins/inputs/vsphere/vsphere_test.go @@ -146,7 +146,7 @@ func defaultVSphere() *VSphere { "disk.kernelWriteLatency.average.absolute.millisecond"}, HostMetricExclude: nil, GatherVms: true, - VmMetricInclude: []string{ + VMMetricInclude: []string{ "cpu.ready.summation.delta.millisecond", "mem.swapinRate.average.rate.kiloBytesPerSecond", "virtualDisk.numberReadAveraged.average.rate.number", @@ -164,7 +164,7 @@ func defaultVSphere() *VSphere { "mem.swapoutRate.average.rate.kiloBytesPerSecond", "virtualDisk.read.average.rate.kiloBytesPerSecond", "virtualDisk.write.average.rate.kiloBytesPerSecond"}, - VmMetricExclude: nil, + VMMetricExclude: nil, GatherDatastores: true, DatastoreMetricInclude: []string{ "disk.used.*", From 3d791c1f5ecc2e8b8d8af8307bfc10c687df38c5 Mon Sep 17 00:00:00 2001 From: prydin Date: Sat, 28 Jul 2018 13:35:15 -0400 Subject: [PATCH 078/100] Logging timers and counters using the 'internal' plugin. --- plugins/inputs/vsphere/selfhealth.go | 53 ++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 plugins/inputs/vsphere/selfhealth.go diff --git a/plugins/inputs/vsphere/selfhealth.go b/plugins/inputs/vsphere/selfhealth.go new file mode 100644 index 0000000000000..10fb86602f8d6 --- /dev/null +++ b/plugins/inputs/vsphere/selfhealth.go @@ -0,0 +1,53 @@ +package vsphere + +import ( + "time" + + "github.com/influxdata/telegraf/selfstat" +) + +// Stopwatch is a simple helper for recording timing information, +// such as gather times and discovery times. +type Stopwatch struct { + stat selfstat.Stat + start time.Time +} + +// NewStopwatch creates a new StopWatch and starts measuring time +// its creation. +func NewStopwatch(name, vCenter string) *Stopwatch { + return &Stopwatch{ + stat: selfstat.RegisterTiming("vsphere_timings", name+"_ms", map[string]string{"vcenter": vCenter}), + start: time.Now(), + } +} + +// NewStopwatchWithTags creates a new StopWatch and starts measuring time +// its creation. Allows additional tags. +func NewStopwatchWithTags(name, vCenter string, tags map[string]string) *Stopwatch { + tags["vcenter"] = vCenter + return &Stopwatch{ + stat: selfstat.RegisterTiming("vsphere_timings", name+"_ms", tags), + start: time.Now(), + } +} + +// Stop stops a Stopwatch and records the time. +func (s *Stopwatch) Stop() { + s.stat.Set(time.Since(s.start).Nanoseconds() / 1000000) +} + +// SendInternalCounter is a convenience method for sending +// non-timing internal metrics. +func SendInternalCounter(name, vCenter string, value int64) { + s := selfstat.Register("vsphere_counters", name, map[string]string{"vcenter": vCenter}) + s.Set(value) +} + +// SendInternalCounterWithTags is a convenience method for sending +// non-timing internal metrics. Allows additional tags +func SendInternalCounterWithTags(name, vCenter string, tags map[string]string, value int64) { + tags["vcenter"] = vCenter + s := selfstat.Register("vsphere_counters", name, tags) + s.Set(value) +} From ca51e6c5b8b94c89d35ff335730bbc8bc071e0ec Mon Sep 17 00:00:00 2001 From: prydin Date: Tue, 31 Jul 2018 08:56:25 -0400 Subject: [PATCH 079/100] Fixed Gopkg.lock issues --- Gopkg.lock | 407 ++++-------------------------- plugins/inputs/vsphere/vsphere.go | 13 +- 2 files changed, 51 insertions(+), 369 deletions(-) diff --git a/Gopkg.lock b/Gopkg.lock index 86a9729174b3f..314b801c171b3 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -3,61 +3,48 @@ [[projects]] branch = "master" - digest = "1:d7582b4af1b0b953ff2bb9573a50f787c7e1669cb148fb086a3d1c670a1ac955" name = "code.cloudfoundry.org/clock" packages = ["."] - pruneopts = "" revision = "02e53af36e6c978af692887ed449b74026d76fec" [[projects]] - digest = "1:ce7dc0f1ffcd9a2aacc50ae6d322eebff8f4faa2d6c5f445c874cd0b77a63de7" name = "collectd.org" packages = [ "api", "cdtime", - "network", + "network" ] - pruneopts = "" revision = "2ce144541b8903101fb8f1483cc0497a68798122" version = "v0.3.0" [[projects]] branch = "master" - digest = "1:c1269bfaddefd090935401c291ad5df6c03de605a440e941ecc568e19f0f9e3b" name = "github.com/Microsoft/ApplicationInsights-Go" packages = [ "appinsights", - "appinsights/contracts", + "appinsights/contracts" ] - pruneopts = "" revision = "d2df5d440eda5372f24fcac03839a64d6cb5f7e5" [[projects]] - digest = "1:e43b98837e21e7f9ef552445e1fcfa92e36bfc0a3e0bd78f2a5e0bef7a4378e3" name = "github.com/Microsoft/go-winio" packages = ["."] - pruneopts = "" revision = "7da180ee92d8bd8bb8c37fc560e673e6557c392f" version = "v0.4.7" [[projects]] - digest = "1:14af5ba5ac88efec490fb59734df34e1bd973198caefa7b0cceed0900ef6164c" name = "github.com/Shopify/sarama" packages = ["."] - pruneopts = "" revision = "35324cf48e33d8260e1c7c18854465a904ade249" version = "v1.17.0" [[projects]] - digest = "1:f82b8ac36058904227087141017bb82f4b0fc58272990a4cdae3e2d6d222644e" name = "github.com/StackExchange/wmi" packages = ["."] - pruneopts = "" revision = "5d049714c4a64225c3c79a7cf7d02f7fb5b96338" version = "1.0.0" [[projects]] - digest = "1:537feef3c3a65e8b924d992df29aacb41f28f7faa4e4610287322bc63e02a33b" name = "github.com/aerospike/aerospike-client-go" packages = [ ".", @@ -70,52 +57,42 @@ "types/atomic", "types/particle_type", "types/rand", - "utils/buffer", + "utils/buffer" ] - pruneopts = "" revision = "c10b5393e43bd60125aca6289c7b24879edb1787" version = "v1.33.0" [[projects]] branch = "master" - digest = "1:1399282ad03ac819f0e8a747c888407c5c98bb497d33821a7047c7bae667ede0" name = "github.com/alecthomas/template" packages = [ ".", - "parse", + "parse" ] - pruneopts = "" revision = "a0175ee3bccc567396460bf5acd36800cb10c49c" [[projects]] branch = "master" - digest = "1:8483994d21404c8a1d489f6be756e25bfccd3b45d65821f25695577791a08e68" name = "github.com/alecthomas/units" packages = ["."] - pruneopts = "" revision = "2efee857e7cfd4f3d0138cc3cbb1b4966962b93a" [[projects]] branch = "master" - digest = "1:072692f8d76356228f31f64ca3140041a140011c7dea26e746206e8649c71b31" name = "github.com/amir/raidman" packages = [ ".", - "proto", + "proto" ] - pruneopts = "" revision = "1ccc43bfb9c93cb401a4025e49c64ba71e5e668b" [[projects]] branch = "master" - digest = "1:0be63b870133f215b6540f82611928cc00b1f6fdf8b63125c1b75a417a88394c" name = "github.com/apache/thrift" packages = ["lib/go/thrift"] - pruneopts = "" revision = "f5f430df56871bc937950274b2c86681d3db6e59" [[projects]] - digest = "1:413443b87e4881a8c9f9b64bca9ed7d96458466f9654aef3b3813e1714f0ef4f" name = "github.com/aws/aws-sdk-go" packages = [ "aws", @@ -147,95 +124,75 @@ "private/protocol/xml/xmlutil", "service/cloudwatch", "service/kinesis", - "service/sts", + "service/sts" ] - pruneopts = "" revision = "bfc1a07cf158c30c41a3eefba8aae043d0bb5bff" version = "v1.14.8" [[projects]] branch = "master" - digest = "1:fca298802a2ab834d6eb0e284788ae037ebc324c0f325ff92c5eea592d189cc5" name = "github.com/beorn7/perks" packages = ["quantile"] - pruneopts = "" revision = "3a771d992973f24aa725d07868b467d1ddfceafb" [[projects]] - digest = "1:0edb96edcfeee9aeba92e605536fbb1542b0bf6a10cea9d0b5a2227d5a703eae" name = "github.com/bsm/sarama-cluster" packages = ["."] - pruneopts = "" revision = "cf455bc755fe41ac9bb2861e7a961833d9c2ecc3" version = "v2.1.13" [[projects]] - digest = "1:f619cb9b07aebe5416262cdd8b86082e8d5bdc5264cb3b615ff858df0b645f97" name = "github.com/cenkalti/backoff" packages = ["."] - pruneopts = "" revision = "2ea60e5f094469f9e65adb9cd103795b73ae743e" version = "v2.0.0" [[projects]] branch = "master" - digest = "1:65ae2d1625584ba8d16d1e15b25db1fc62334e2040f22dbbbdc7531c909843b2" name = "github.com/couchbase/go-couchbase" packages = ["."] - pruneopts = "" revision = "16db1f1fe037412f12738fa4d8448c549c4edd77" [[projects]] branch = "master" - digest = "1:5db54de7054c072f47806c91ef7625ffa00489ca2da5fbc6ca1c78e08018f6bf" name = "github.com/couchbase/gomemcached" packages = [ ".", - "client", + "client" ] - pruneopts = "" revision = "0da75df145308b9a4e6704d762ca9d9b77752efc" [[projects]] branch = "master" - digest = "1:0deaa0f28c823119725c8308703f019797bc077e251d1ed3f2b8eae2cc7791d7" name = "github.com/couchbase/goutils" packages = [ "logging", - "scramsha", + "scramsha" ] - pruneopts = "" revision = "e865a1461c8ac0032bd37e2d4dab3289faea3873" [[projects]] - digest = "1:0a39ec8bf5629610a4bc7873a92039ee509246da3cef1a0ea60f1ed7e5f9cea5" name = "github.com/davecgh/go-spew" packages = ["spew"] - pruneopts = "" revision = "346938d642f2ec3594ed81d874461961cd0faa76" version = "v1.1.0" [[projects]] - digest = "1:2426da75f49e5b8507a6ed5d4c49b06b2ff795f4aec401c106b7db8fb2625cd7" name = "github.com/dgrijalva/jwt-go" packages = ["."] - pruneopts = "" revision = "06ea1031745cb8b3dab3f6a236daf2b0aa468b7e" version = "v3.2.0" [[projects]] - digest = "1:3152d7c68fd4797ca2d96524b7b229cd8237e2c2b2605584ebd736cc7be2a523" name = "github.com/docker/distribution" packages = [ "digest", - "reference", + "reference" ] - pruneopts = "" revision = "48294d928ced5dd9b378f7fd7c6f5da3ff3f2c89" version = "v2.6.2" [[projects]] - digest = "1:2f161cdaf1f3617afa09983c677b1392c4b044f12fc9ccfe9ae65d72dca2e6f7" name = "github.com/docker/docker" packages = [ "api/types", @@ -253,96 +210,76 @@ "api/types/versions", "api/types/volume", "client", - "pkg/tlsconfig", + "pkg/tlsconfig" ] - pruneopts = "" revision = "eef6495eddab52828327aade186443681ed71a4e" version = "v17.03.2-ce-rc1" [[projects]] - digest = "1:5b20afc76a36d3994194e2612e83b51bc2b12db3d4d2a722b24474b2d0e3a890" name = "github.com/docker/go-connections" packages = [ "nat", "sockets", - "tlsconfig", + "tlsconfig" ] - pruneopts = "" revision = "3ede32e2033de7505e6500d6c868c2b9ed9f169d" version = "v0.3.0" [[projects]] - digest = "1:582d54fcb7233da8dde1dfd2210a5b9675d0685f84246a8d317b07d680c18b1b" name = "github.com/docker/go-units" packages = ["."] - pruneopts = "" revision = "47565b4f722fb6ceae66b95f853feed578a4a51c" version = "v0.3.3" [[projects]] - digest = "1:7bbb118aeef9a6b9fef3d57b6cc5378f7cd6e915cabf4dea695e318e1a1bd4e6" name = "github.com/eapache/go-resiliency" packages = ["breaker"] - pruneopts = "" revision = "ea41b0fad31007accc7f806884dcdf3da98b79ce" version = "v1.1.0" [[projects]] branch = "master" - digest = "1:1f7503fa58a852a1416556ae2ddb219b49a1304fd408391948e2e3676514c48d" name = "github.com/eapache/go-xerial-snappy" packages = ["."] - pruneopts = "" revision = "bb955e01b9346ac19dc29eb16586c90ded99a98c" [[projects]] - digest = "1:d8d46d21073d0f65daf1740ebf4629c65e04bf92e14ce93c2201e8624843c3d3" name = "github.com/eapache/queue" packages = ["."] - pruneopts = "" revision = "44cc805cf13205b55f69e14bcb69867d1ae92f98" version = "v1.1.0" [[projects]] - digest = "1:d2e2aebcb8e8027345e16f9d0be8cdee3bb470ba406c7a54cb7457ae3ad4ace5" name = "github.com/eclipse/paho.mqtt.golang" packages = [ ".", - "packets", + "packets" ] - pruneopts = "" revision = "36d01c2b4cbeb3d2a12063e4880ce30800af9560" version = "v1.1.1" [[projects]] - digest = "1:f7c22bf37088211029335cd4d356774eeca0db1af4448e5b8178386090f98dfb" name = "github.com/go-ini/ini" packages = ["."] - pruneopts = "" revision = "06f5f3d67269ccec1fe5fe4134ba6e982984f7f5" version = "v1.37.0" [[projects]] - digest = "1:6a4a01d58b227c4b6b11111b9f172ec5c17682b82724e58e6daf3f19f4faccd8" name = "github.com/go-logfmt/logfmt" packages = ["."] - pruneopts = "" revision = "390ab7935ee28ec6b286364bba9b4dd6410cb3d5" version = "v0.3.0" [[projects]] - digest = "1:c3a5ae14424a38c244439732c31a08b5f956c46c4acdc159fc285a52dbf11de0" name = "github.com/go-ole/go-ole" packages = [ ".", - "oleutil", + "oleutil" ] - pruneopts = "" revision = "a41e3c4b706f6ae8dfbff342b06e40fa4d2d0506" version = "v1.2.1" [[projects]] - digest = "1:f2f6a616a1ca8aed667d956c98f7f6178efe72bbe0a419bd33b9d99841c7de69" name = "github.com/go-redis/redis" packages = [ ".", @@ -352,22 +289,18 @@ "internal/pool", "internal/proto", "internal/singleflight", - "internal/util", + "internal/util" ] - pruneopts = "" revision = "83fb42932f6145ce52df09860384a4653d2d332a" version = "v6.12.0" [[projects]] - digest = "1:dc876ae7727280d95f97af5320308131278b93d6c6f5cf953065e18cb8c88fd2" name = "github.com/go-sql-driver/mysql" packages = ["."] - pruneopts = "" revision = "d523deb1b23d913de5bdada721a6071e71283618" version = "v1.4.0" [[projects]] - digest = "1:b7a7e17513aeee6492d93015c7bf29c86a0c1c91210ea56b21e36c1a40958cba" name = "github.com/gobwas/glob" packages = [ ".", @@ -377,165 +310,131 @@ "syntax/ast", "syntax/lexer", "util/runes", - "util/strings", + "util/strings" ] - pruneopts = "" revision = "5ccd90ef52e1e632236f7326478d4faa74f99438" version = "v0.2.3" [[projects]] - digest = "1:602c5827bcd5eca47c041a39aff7b626dda114da57a0334a9230bdc5cf8ed3ed" name = "github.com/gogo/protobuf" packages = ["proto"] - pruneopts = "" revision = "1adfc126b41513cc696b209667c8656ea7aac67c" version = "v1.0.0" [[projects]] - digest = "1:b1d3041d568e065ab4d76f7477844458e9209c0bb241eaccdc0770bf0a13b120" name = "github.com/golang/protobuf" packages = [ "proto", "ptypes", "ptypes/any", "ptypes/duration", - "ptypes/timestamp", + "ptypes/timestamp" ] - pruneopts = "" revision = "b4deda0973fb4c70b50d226b1af49f3da59f5265" version = "v1.1.0" [[projects]] branch = "master" - digest = "1:075128b9fc42e6d99067da1a2e6c0a634a6043b5a60abe6909c51f5ecad37b6d" name = "github.com/golang/snappy" packages = ["."] - pruneopts = "" revision = "2e65f85255dbc3072edf28d6b5b8efc472979f5a" [[projects]] - digest = "1:cc082d7b9cc3f832f2aed9d06d1cbb33b6984a61d8ec403535b086415c181607" name = "github.com/google/go-cmp" packages = [ "cmp", "cmp/internal/diff", "cmp/internal/function", - "cmp/internal/value", + "cmp/internal/value" ] - pruneopts = "" revision = "3af367b6b30c263d47e8895973edcca9a49cf029" version = "v0.2.0" [[projects]] - digest = "1:c1d7e883c50a26ea34019320d8ae40fad86c9e5d56e63a1ba2cb618cef43e986" name = "github.com/google/uuid" packages = ["."] - pruneopts = "" revision = "064e2069ce9c359c118179501254f67d7d37ba24" version = "0.2" [[projects]] - digest = "1:dbbeb8ddb0be949954c8157ee8439c2adfd8dc1c9510eb44a6e58cb68c3dce28" name = "github.com/gorilla/context" packages = ["."] - pruneopts = "" revision = "08b5f424b9271eedf6f9f0ce86cb9396ed337a42" version = "v1.1.1" [[projects]] - digest = "1:c2c8666b4836c81a1d247bdf21c6a6fc1ab586538ab56f74437c2e0df5c375e1" name = "github.com/gorilla/mux" packages = ["."] - pruneopts = "" revision = "e3702bed27f0d39777b0b37b664b6280e8ef8fbf" version = "v1.6.2" [[projects]] branch = "master" - digest = "1:60b7bc5e043a11213472ae05252527287d20e0a6ccc18f6ae67fad88e41004de" name = "github.com/hailocab/go-hostpool" packages = ["."] - pruneopts = "" revision = "e80d13ce29ede4452c43dea11e79b9bc8a15b478" [[projects]] - digest = "1:69f3f1b3e95b8da5b2e3deb0fc821f7641557e5d500857ec35a3822c8b8b782f" name = "github.com/hashicorp/consul" packages = ["api"] - pruneopts = "" revision = "5174058f0d2bda63fa5198ab96c33d9a909c58ed" version = "v1.1.0" [[projects]] branch = "master" - digest = "1:f5d25fd7bdda08e39e01193ef94a1ebf7547b1b931bcdec785d08050598f306c" name = "github.com/hashicorp/go-cleanhttp" packages = ["."] - pruneopts = "" revision = "d5fe4b57a186c716b0e00b8c301cbd9b4182694d" [[projects]] branch = "master" - digest = "1:cd5813053beac0114f96a7da3924fc8a15e0cd2b139f079e0fcce5d3244ae304" name = "github.com/hashicorp/go-rootcerts" packages = ["."] - pruneopts = "" revision = "6bb64b370b90e7ef1fa532be9e591a81c3493e00" [[projects]] - digest = "1:d2b2cff454cb23a9769ef3c9075741f5985773a998584b3b3ce203fe4b1abbea" name = "github.com/hashicorp/serf" packages = ["coordinate"] - pruneopts = "" revision = "d6574a5bb1226678d7010325fb6c985db20ee458" version = "v0.8.1" [[projects]] - digest = "1:cc0cf2e12280074e5c6dc0f15a4bb3d6c43509e6091cdcdcc83eea491577257b" name = "github.com/influxdata/go-syslog" packages = [ "rfc5424", - "rfc5425", + "rfc5425" ] - pruneopts = "" revision = "eecd51df3ad85464a2bab9b7d3a45bc1e299059e" version = "v1.0.1" [[projects]] branch = "master" - digest = "1:effc58ad45323ad15159bbca533be4870eaddb2d9a513d3488d8bfe822c83532" name = "github.com/influxdata/tail" packages = [ ".", "ratelimiter", "util", "watch", - "winfile", + "winfile" ] - pruneopts = "" revision = "c43482518d410361b6c383d7aebce33d0471d7bc" [[projects]] branch = "master" - digest = "1:d31edcf33a3b36218de96e43f3fec18ea96deb2a28b838a3a01a4df856ded345" name = "github.com/influxdata/toml" packages = [ ".", - "ast", + "ast" ] - pruneopts = "" revision = "2a2e3012f7cfbef64091cc79776311e65dfa211b" [[projects]] branch = "master" - digest = "1:a0c157916be0b4de1d4565b1f094b8d746109f94968140dff40a42780fa6ccef" name = "github.com/influxdata/wlog" packages = ["."] - pruneopts = "" revision = "7c63b0a71ef8300adc255344d275e10e5c3a71ec" [[projects]] - digest = "1:4197871f269749786aa2406557dba15f10cf79161cdc3998180614c62c8b6351" name = "github.com/jackc/pgx" packages = [ ".", @@ -544,176 +443,138 @@ "pgio", "pgproto3", "pgtype", - "stdlib", + "stdlib" ] - pruneopts = "" revision = "da3231b0b66e2e74cdb779f1d46c5e958ba8be27" version = "v3.1.0" [[projects]] - digest = "1:4f767a115bc8e08576f6d38ab73c376fc1b1cd3bb5041171c9e8668cc7739b52" name = "github.com/jmespath/go-jmespath" packages = ["."] - pruneopts = "" revision = "0b12d6b5" [[projects]] branch = "master" - digest = "1:2c5ad58492804c40bdaf5d92039b0cde8b5becd2b7feeb37d7d1cc36a8aa8dbe" name = "github.com/kardianos/osext" packages = ["."] - pruneopts = "" revision = "ae77be60afb1dcacde03767a8c37337fad28ac14" [[projects]] branch = "master" - digest = "1:2df59f23f11c5c59982f737c98c5523b276bfc85a4773a04b411190402bb30fd" name = "github.com/kardianos/service" packages = ["."] - pruneopts = "" revision = "615a14ed75099c9eaac6949e22ac2341bf9d3197" [[projects]] branch = "master" - digest = "1:63e7368fcf6b54804076eaec26fd9cf0c4466166b272393db4b93102e1e962df" name = "github.com/kballard/go-shellquote" packages = ["."] - pruneopts = "" revision = "95032a82bc518f77982ea72343cc1ade730072f0" [[projects]] branch = "master" - digest = "1:1ed9eeebdf24aadfbca57eb50e6455bd1d2474525e0f0d4454de8c8e9bc7ee9a" name = "github.com/kr/logfmt" packages = ["."] - pruneopts = "" revision = "b84e30acd515aadc4b783ad4ff83aff3299bdfe0" [[projects]] branch = "master" - digest = "1:175f85474bb8dd4ef02b1fad5540345545b78b192e64b961e99e607cce32a716" name = "github.com/mailru/easyjson" packages = [ ".", "buffer", "jlexer", - "jwriter", + "jwriter" ] - pruneopts = "" revision = "3fdea8d05856a0c8df22ed4bc71b3219245e4485" [[projects]] - digest = "1:49a8b01a6cd6558d504b65608214ca40a78000e1b343ed0da5c6a9ccd83d6d30" name = "github.com/matttproud/golang_protobuf_extensions" packages = ["pbutil"] - pruneopts = "" revision = "c12348ce28de40eed0136aa2b644d0ee0650e56c" version = "v1.0.1" [[projects]] - digest = "1:f0bad0fece0fb73c6ea249c18d8e80ffbe86be0457715b04463068f04686cf39" name = "github.com/miekg/dns" packages = ["."] - pruneopts = "" revision = "5a2b9fab83ff0f8bfc99684bd5f43a37abe560f1" version = "v1.0.8" [[projects]] branch = "master" - digest = "1:99651e95333755cbe5c9768c1b80031300acca64a80870b40309202b32585a5a" name = "github.com/mitchellh/go-homedir" packages = ["."] - pruneopts = "" revision = "3864e76763d94a6df2f9960b16a20a33da9f9a66" [[projects]] branch = "master" - digest = "1:eb9117392ee8e7aa44f78e0db603f70b1050ee0ebda4bd40040befb5b218c546" name = "github.com/mitchellh/mapstructure" packages = ["."] - pruneopts = "" revision = "bb74f1db0675b241733089d5a1faa5dd8b0ef57b" [[projects]] - digest = "1:ee2e62b00a9ccc2dba1525f93396e35c847f90f87939df6f361b86315ea5f69a" name = "github.com/multiplay/go-ts3" packages = ["."] - pruneopts = "" revision = "d0d44555495c8776880a17e439399e715a4ef319" version = "v1.0.0" [[projects]] - digest = "1:ccd0def9f0b82b61c5e54fcbfccf528eabb13b489d008e46dc16b808c2e1f765" name = "github.com/naoina/go-stringutil" packages = ["."] - pruneopts = "" revision = "6b638e95a32d0c1131db0e7fe83775cbea4a0d0b" version = "v0.1.0" [[projects]] - digest = "1:7196e09de0e0e057aad363ae7c5a83dba55da7c3865ac1ddabdcd39a72aeb27e" name = "github.com/nats-io/gnatsd" packages = [ "conf", "logger", "server", "server/pse", - "util", + "util" ] - pruneopts = "" revision = "add6d7930ae6d4bff8823b28999ea87bf1bfd23d" version = "v1.1.0" [[projects]] - digest = "1:88f1bde4c172e27b05ed46adfbd0e79dc1663a6281e4b39fa3e39d71ead9621d" name = "github.com/nats-io/go-nats" packages = [ ".", "encoders/builtin", - "util", + "util" ] - pruneopts = "" revision = "062418ea1c2181f52dc0f954f6204370519a868b" version = "v1.5.0" [[projects]] - digest = "1:be61e8224b84064109eaba8157cbb4bbe6ca12443e182b6624fdfa1c0dcf53d9" name = "github.com/nats-io/nuid" packages = ["."] - pruneopts = "" revision = "289cccf02c178dc782430d534e3c1f5b72af807f" version = "v1.0.0" [[projects]] - digest = "1:501cce26a54c785458b0dd54a08ddd984d4ad0c198255430d5d37cd2efe23149" name = "github.com/nsqio/go-nsq" packages = ["."] - pruneopts = "" revision = "eee57a3ac4174c55924125bb15eeeda8cffb6e6f" version = "v1.0.7" [[projects]] branch = "master" - digest = "1:2da0e5077ed40453dc281b9a2428d84cf6ad14063aed189f6296ca5dd25cf13d" name = "github.com/opentracing-contrib/go-observer" packages = ["."] - pruneopts = "" revision = "a52f2342449246d5bcc273e65cbdcfa5f7d6c63c" [[projects]] - digest = "1:bba12aa4747b212f75db3e7fee73fe1b66d303cb3ff0c1984b7f2ad20e8bd2bc" name = "github.com/opentracing/opentracing-go" packages = [ ".", "ext", - "log", + "log" ] - pruneopts = "" revision = "1949ddbfd147afd4d964a9f00b24eb291e0e7c38" version = "v1.0.2" [[projects]] - digest = "1:c6c0db6294924072f98a0de090d200bae4b7102b12a443ba9569c4ba7df52aa1" name = "github.com/openzipkin/zipkin-go-opentracing" packages = [ ".", @@ -721,110 +582,88 @@ "thrift/gen-go/scribe", "thrift/gen-go/zipkincore", "types", - "wire", + "wire" ] - pruneopts = "" revision = "26cf9707480e6b90e5eff22cf0bbf05319154232" version = "v0.3.4" [[projects]] - digest = "1:681dd6745809b277541914db18b4ba159b7f1158a0e37627ce8081d5291fc1ec" name = "github.com/pierrec/lz4" packages = [ ".", - "internal/xxh32", + "internal/xxh32" ] - pruneopts = "" revision = "6b9367c9ff401dbc54fabce3fb8d972e799b702d" version = "v2.0.2" [[projects]] - digest = "1:7365acd48986e205ccb8652cc746f09c8b7876030d53710ea6ef7d0bd0dcd7ca" name = "github.com/pkg/errors" packages = ["."] - pruneopts = "" revision = "645ef00459ed84a119197bfb8d8205042c6df63d" version = "v0.8.0" [[projects]] - digest = "1:256484dbbcd271f9ecebc6795b2df8cad4c458dd0f5fd82a8c2fa0c29f233411" name = "github.com/pmezard/go-difflib" packages = ["difflib"] - pruneopts = "" revision = "792786c7400a136282c1664665ae0a8db921c6c2" version = "v1.0.0" [[projects]] - digest = "1:981835985f655d1d380cc6aa7d9fa9ad7abfaf40c75da200fd40d864cd05a7c3" name = "github.com/prometheus/client_golang" packages = [ "prometheus", - "prometheus/promhttp", + "prometheus/promhttp" ] - pruneopts = "" revision = "c5b7fccd204277076155f10851dad72b76a49317" version = "v0.8.0" [[projects]] branch = "master" - digest = "1:83bf37d060fca77e959fe5ceee81e58bbd1b01836f4addc70043a948e9912547" name = "github.com/prometheus/client_model" packages = ["go"] - pruneopts = "" revision = "99fa1f4be8e564e8a6b613da7fa6f46c9edafc6c" [[projects]] branch = "master" - digest = "1:6a8420870eb2935977da1fff0f3afca9bdb3f1e66258c9e91a8a7ce0b5417c3b" name = "github.com/prometheus/common" packages = [ "expfmt", "internal/bitbucket.org/ww/goautoneg", "log", - "model", + "model" ] - pruneopts = "" revision = "7600349dcfe1abd18d72d3a1770870d9800a7801" [[projects]] branch = "master" - digest = "1:f691cc506fb101ec4d3aacf7ff7bee68419d5b75cc1d3d5be25324bbaa58529a" name = "github.com/prometheus/procfs" packages = [ ".", "internal/util", "nfs", - "xfs", + "xfs" ] - pruneopts = "" revision = "7d6f385de8bea29190f15ba9931442a0eaef9af7" [[projects]] branch = "master" - digest = "1:1b65925989a4dfb6d98ef1d530cda33ab1ff25945b14a22a8b8bb27cc282af70" name = "github.com/rcrowley/go-metrics" packages = ["."] - pruneopts = "" revision = "e2704e165165ec55d062f5919b4b29494e9fa790" [[projects]] branch = "master" - digest = "1:d8fe9f454582e04b5693b59cdebe3f0bd9dc29ad9651bfb1633cba4658b66c65" name = "github.com/samuel/go-zookeeper" packages = ["zk"] - pruneopts = "" revision = "c4fab1ac1bec58281ad0667dc3f0907a9476ac47" [[projects]] - digest = "1:7f569d906bdd20d906b606415b7d794f798f91a62fcfb6a4daa6d50690fb7a3f" name = "github.com/satori/go.uuid" packages = ["."] - pruneopts = "" revision = "f58768cc1a7a7e77a3bd49e98cdd21419399b6a3" version = "v1.2.0" [[projects]] - digest = "1:ad351e057e5b6deef320ac9e8322ed825aba3542229da71e708576b01fa83b6e" name = "github.com/shirou/gopsutil" packages = [ "cpu", @@ -834,90 +673,70 @@ "load", "mem", "net", - "process", + "process" ] - pruneopts = "" revision = "eeb1d38d69593f121e060d24d17f7b1f0936b203" version = "v2.18.05" [[projects]] branch = "master" - digest = "1:99c6a6dab47067c9b898e8c8b13d130c6ab4ffbcc4b7cc6236c2cd0b1e344f5b" name = "github.com/shirou/w32" packages = ["."] - pruneopts = "" revision = "bb4de0191aa41b5507caa14b0650cdbddcd9280b" [[projects]] - digest = "1:f2cc92b78b2f3b76ab0f9daddddd28627bcfcc6cacf119029aa3850082d95079" name = "github.com/sirupsen/logrus" packages = ["."] - pruneopts = "" revision = "c155da19408a8799da419ed3eeb0cb5db0ad5dbc" version = "v1.0.5" [[projects]] branch = "master" - digest = "1:1df855e31f96fac0b7a18115e029bb6ca1c5f40996a06e07bf1ce524d925d454" name = "github.com/soniah/gosnmp" packages = ["."] - pruneopts = "" revision = "bcf840db66be7d64bf96c3c0e075c92e3d98f793" [[projects]] branch = "master" - digest = "1:0a1f8d01a0191f558910bcbfd7e1dc11a53ac374473d13b68b8fe520f21efb07" name = "github.com/streadway/amqp" packages = ["."] - pruneopts = "" revision = "e5adc2ada8b8efff032bf61173a233d143e9318e" [[projects]] - digest = "1:34062a2274daa6ec4d2f50d6070cc51cf4674d6d553ed76b406cb3425b9528e8" name = "github.com/stretchr/objx" packages = ["."] - pruneopts = "" revision = "477a77ecc69700c7cdeb1fa9e129548e1c1c393c" version = "v0.1.1" [[projects]] - digest = "1:bc2a12c8863e1080226b7bc69192efd6c37aaa9b85cec508b0a8f54fabb9bd9f" name = "github.com/stretchr/testify" packages = [ "assert", "mock", - "require", + "require" ] - pruneopts = "" revision = "f35b8ab0b5a2cef36673838d662e249dd9c94686" version = "v1.2.2" [[projects]] - digest = "1:bcc2f7db4796010b35ccd2425fe46849096a4e98ed53f572567cb76de1187b3a" name = "github.com/tidwall/gjson" packages = ["."] - pruneopts = "" revision = "afaeb9562041a8018c74e006551143666aed08bf" version = "v1.1.1" [[projects]] branch = "master" - digest = "1:4db4f92bb9cb04cfc4fccb36aba2598b02a988008c4cc0692b241214ad8ac96e" name = "github.com/tidwall/match" packages = ["."] - pruneopts = "" revision = "1731857f09b1f38450e2c12409748407822dc6be" [[projects]] - digest = "1:23e2b9f3a20cd4a6427147377255ec2f6237e8606fa6ef0707ed79b7bfbe3a83" name = "github.com/vjeantet/grok" packages = ["."] - pruneopts = "" revision = "ce01e59abcf6fbc9833b7deb5e4b8ee1769bcc53" version = "v1.0.0" [[projects]] - digest = "1:e27a90b038891891bb33cb72cb30b8f1b30307e99fd0caf18d3073ac730d4160" name = "github.com/vmware/govmomi" packages = [ ".", @@ -940,52 +759,42 @@ "vim25/progress", "vim25/soap", "vim25/types", - "vim25/xml", + "vim25/xml" ] - pruneopts = "" revision = "e3a01f9611c32b2362366434bcd671516e78955d" version = "v0.18.0" [[projects]] branch = "master" - digest = "1:5383edd40c7f6c95a7dc46a47bf0c83de4bf40a4252f12fa803f790037addffc" name = "github.com/wvanbergen/kafka" packages = ["consumergroup"] - pruneopts = "" revision = "e2edea948ddfee841ea9a263b32ccca15f7d6c2f" [[projects]] branch = "master" - digest = "1:f936b4936e1b092cc41c9b33fdc990ad78386545f1ffeca8427c72b2605bca85" name = "github.com/wvanbergen/kazoo-go" packages = ["."] - pruneopts = "" revision = "f72d8611297a7cf105da904c04198ad701a60101" [[projects]] branch = "master" - digest = "1:9dcb103385e63db2f0eb7e6a8fa2103acddcfb48b25957959f56a0f15188674b" name = "github.com/yuin/gopher-lua" packages = [ ".", "ast", "parse", - "pm", + "pm" ] - pruneopts = "" revision = "ca850f594eaafa5468da2bd53b865e4ee55be18b" [[projects]] branch = "master" - digest = "1:84e9087a94f336c204887281046891769d2ed7bf1d2b31c21ff6fb5e1743abce" name = "github.com/zensqlmonitor/go-mssqldb" packages = ["."] - pruneopts = "" revision = "e8fbf836e44e86764eba398361d1825651709547" [[projects]] branch = "master" - digest = "1:dec0d91bef11bc02ba7d1f72bf251a8309c2e55a0fbea699195d037406c219b0" name = "golang.org/x/crypto" packages = [ "bcrypt", @@ -994,14 +803,12 @@ "ed25519/internal/edwards25519", "md4", "pbkdf2", - "ssh/terminal", + "ssh/terminal" ] - pruneopts = "" revision = "027cca12c2d63e3d62b670d901e8a2c95854feec" [[projects]] branch = "master" - digest = "1:3edb9c19d0b874999053badbbcc08edab3cde0262d2beb36ad6c0d78391c19ac" name = "golang.org/x/net" packages = [ "bpf", @@ -1022,14 +829,12 @@ "ipv6", "proxy", "trace", - "websocket", + "websocket" ] - pruneopts = "" revision = "db08ff08e8622530d9ed3a0e8ac279f6d4c02196" [[projects]] branch = "master" - digest = "1:a90f95e710f8986cec0fc8ca053fa984f3e2e8963941de2e2ab44e565f37b30a" name = "golang.org/x/sys" packages = [ "unix", @@ -1038,13 +843,11 @@ "windows/svc", "windows/svc/debug", "windows/svc/eventlog", - "windows/svc/mgr", + "windows/svc/mgr" ] - pruneopts = "" revision = "6c888cc515d3ed83fc103cf1d84468aad274b0a7" [[projects]] - digest = "1:af9bfca4298ef7502c52b1459df274eed401a4f5498b900e9a92d28d3d87ac5a" name = "golang.org/x/text" packages = [ "collate", @@ -1072,30 +875,24 @@ "unicode/bidi", "unicode/cldr", "unicode/norm", - "unicode/rangetable", + "unicode/rangetable" ] - pruneopts = "" revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0" version = "v0.3.0" [[projects]] - digest = "1:eede11c81b63c8f6fd06ef24ba0a640dc077196ec9b7a58ecde03c82eee2f151" name = "google.golang.org/appengine" packages = ["cloudsql"] - pruneopts = "" revision = "b1f26356af11148e710935ed1ac8a7f5702c7612" version = "v1.1.0" [[projects]] branch = "master" - digest = "1:bca49d2bc942d5839d13a2ee882e6475b96cc5352b549000ecafa13dbd158265" name = "google.golang.org/genproto" packages = ["googleapis/rpc/status"] - pruneopts = "" revision = "32ee49c4dd805befd833990acba36cb75042378c" [[projects]] - digest = "1:6648dc575e3043e47e6bc3a41054a7468dbeef331898eae513df19af09765c30" name = "google.golang.org/grpc" packages = [ ".", @@ -1121,208 +918,90 @@ "stats", "status", "tap", - "transport", + "transport" ] - pruneopts = "" revision = "7a6a684ca69eb4cae85ad0a484f2e531598c047b" version = "v1.12.2" [[projects]] - digest = "1:2840683aa0e9980689f85bf48b2a56ec7a108fd089f12af8ea7d98c172819589" name = "gopkg.in/alecthomas/kingpin.v2" packages = ["."] - pruneopts = "" revision = "947dcec5ba9c011838740e680966fd7087a71d0d" version = "v2.2.6" [[projects]] - digest = "1:a8f8c1725195c4324d4350fae001524ca7489e40d9b6bb47598772e3faa103ba" name = "gopkg.in/asn1-ber.v1" packages = ["."] - pruneopts = "" revision = "379148ca0225df7a432012b8df0355c2a2063ac0" version = "v1.2" [[projects]] - digest = "1:581450ae66d7970d91ef9132459fa583e937c6e502f1b96e4ee7783a56fa0b44" name = "gopkg.in/fatih/pool.v2" packages = ["."] - pruneopts = "" revision = "010e0b745d12eaf8426c95f9c3924d81dd0b668f" version = "v2.0.0" [[projects]] - digest = "1:b2106f1668ea5efc1ecc480f7e922a093adb9563fd9ce58585292871f0d0f229" name = "gopkg.in/fsnotify.v1" packages = ["."] - pruneopts = "" revision = "c2828203cd70a50dcccfb2761f8b1f8ceef9a8e9" source = "https://github.com/fsnotify/fsnotify/archive/v1.4.7.tar.gz" version = "v1.4.7" [[projects]] - digest = "1:5fa5df18f3bd9cad28ed7f263b15da217945735110898fa2b9af25cdafb9cbf3" name = "gopkg.in/gorethink/gorethink.v3" packages = [ ".", "encoding", "ql2", - "types", + "types" ] - pruneopts = "" revision = "7f5bdfd858bb064d80559b2a32b86669c5de5d3b" version = "v3.0.5" [[projects]] - digest = "1:74163d1887c0821951e6f1795a1d10338f45f09d9067cb4a8edcf7ee481724ee" name = "gopkg.in/ldap.v2" packages = ["."] - pruneopts = "" revision = "bb7a9ca6e4fbc2129e3db588a34bc970ffe811a9" version = "v2.5.1" [[projects]] branch = "v2" - digest = "1:95fc0dfa0995fff96935315321df859c5c6b1b81df687ed87a2dcf1767f1b465" name = "gopkg.in/mgo.v2" packages = [ ".", "bson", "internal/json", "internal/sasl", - "internal/scram", + "internal/scram" ] - pruneopts = "" revision = "3f83fa5005286a7fe593b055f0d7771a7dce4655" [[projects]] - digest = "1:e7f4f325f729e4c0e93fdac23b05946ab5f9761af4a61615144024774a25b0cb" name = "gopkg.in/olivere/elastic.v5" packages = [ ".", "config", - "uritemplates", + "uritemplates" ] - pruneopts = "" revision = "b708306d715bea9b983685e94ab4602cdc9f988b" version = "v5.0.69" [[projects]] branch = "v1" - digest = "1:a96d16bd088460f2e0685d46c39bcf1208ba46e0a977be2df49864ec7da447dd" name = "gopkg.in/tomb.v1" packages = ["."] - pruneopts = "" revision = "dd632973f1e7218eb1089048e0798ec9ae7dceb8" [[projects]] - digest = "1:f0620375dd1f6251d9973b5f2596228cc8042e887cd7f827e4220bc1ce8c30e2" name = "gopkg.in/yaml.v2" packages = ["."] - pruneopts = "" revision = "5420a8b6744d3b0345ab293f6fcba19c978f1183" version = "v2.2.1" [solve-meta] analyzer-name = "dep" analyzer-version = 1 - input-imports = [ - "collectd.org/api", - "collectd.org/network", - "github.com/Microsoft/ApplicationInsights-Go/appinsights", - "github.com/Shopify/sarama", - "github.com/StackExchange/wmi", - "github.com/aerospike/aerospike-client-go", - "github.com/amir/raidman", - "github.com/apache/thrift/lib/go/thrift", - "github.com/aws/aws-sdk-go/aws", - "github.com/aws/aws-sdk-go/aws/client", - "github.com/aws/aws-sdk-go/aws/credentials", - "github.com/aws/aws-sdk-go/aws/credentials/stscreds", - "github.com/aws/aws-sdk-go/aws/session", - "github.com/aws/aws-sdk-go/service/cloudwatch", - "github.com/aws/aws-sdk-go/service/kinesis", - "github.com/aws/aws-sdk-go/service/sts", - "github.com/bsm/sarama-cluster", - "github.com/couchbase/go-couchbase", - "github.com/dgrijalva/jwt-go", - "github.com/docker/docker/api/types", - "github.com/docker/docker/api/types/container", - "github.com/docker/docker/api/types/filters", - "github.com/docker/docker/api/types/registry", - "github.com/docker/docker/api/types/swarm", - "github.com/docker/docker/client", - "github.com/docker/go-connections/sockets", - "github.com/eclipse/paho.mqtt.golang", - "github.com/go-redis/redis", - "github.com/go-sql-driver/mysql", - "github.com/gobwas/glob", - "github.com/golang/protobuf/proto", - "github.com/google/go-cmp/cmp", - "github.com/gorilla/mux", - "github.com/hashicorp/consul/api", - "github.com/influxdata/go-syslog/rfc5424", - "github.com/influxdata/go-syslog/rfc5425", - "github.com/influxdata/tail", - "github.com/influxdata/toml", - "github.com/influxdata/toml/ast", - "github.com/influxdata/wlog", - "github.com/jackc/pgx/stdlib", - "github.com/kardianos/service", - "github.com/kballard/go-shellquote", - "github.com/matttproud/golang_protobuf_extensions/pbutil", - "github.com/miekg/dns", - "github.com/multiplay/go-ts3", - "github.com/nats-io/gnatsd/server", - "github.com/nats-io/go-nats", - "github.com/nsqio/go-nsq", - "github.com/openzipkin/zipkin-go-opentracing", - "github.com/openzipkin/zipkin-go-opentracing/thrift/gen-go/zipkincore", - "github.com/prometheus/client_golang/prometheus", - "github.com/prometheus/client_golang/prometheus/promhttp", - "github.com/prometheus/client_model/go", - "github.com/prometheus/common/expfmt", - "github.com/prometheus/common/log", - "github.com/satori/go.uuid", - "github.com/shirou/gopsutil/cpu", - "github.com/shirou/gopsutil/disk", - "github.com/shirou/gopsutil/host", - "github.com/shirou/gopsutil/load", - "github.com/shirou/gopsutil/mem", - "github.com/shirou/gopsutil/net", - "github.com/shirou/gopsutil/process", - "github.com/soniah/gosnmp", - "github.com/streadway/amqp", - "github.com/stretchr/testify/assert", - "github.com/stretchr/testify/mock", - "github.com/stretchr/testify/require", - "github.com/tidwall/gjson", - "github.com/vjeantet/grok", - "github.com/vmware/govmomi", - "github.com/vmware/govmomi/performance", - "github.com/vmware/govmomi/simulator", - "github.com/vmware/govmomi/view", - "github.com/vmware/govmomi/vim25/mo", - "github.com/vmware/govmomi/vim25/soap", - "github.com/vmware/govmomi/vim25/types", - "github.com/wvanbergen/kafka/consumergroup", - "github.com/zensqlmonitor/go-mssqldb", - "golang.org/x/net/context", - "golang.org/x/net/html/charset", - "golang.org/x/sys/unix", - "golang.org/x/sys/windows", - "golang.org/x/sys/windows/svc", - "golang.org/x/sys/windows/svc/mgr", - "google.golang.org/grpc", - "google.golang.org/grpc/codes", - "google.golang.org/grpc/credentials", - "google.golang.org/grpc/status", - "gopkg.in/gorethink/gorethink.v3", - "gopkg.in/ldap.v2", - "gopkg.in/mgo.v2", - "gopkg.in/mgo.v2/bson", - "gopkg.in/olivere/elastic.v5", - "gopkg.in/yaml.v2", - ] + inputs-digest = "5201186bc1dbb97c0427ab3cfa769dd5242d21d199a5e97f5a413d775760aa7e" solver-name = "gps-cdcl" solver-version = 1 diff --git a/plugins/inputs/vsphere/vsphere.go b/plugins/inputs/vsphere/vsphere.go index 230aa7ad71ecd..031830c3ae197 100644 --- a/plugins/inputs/vsphere/vsphere.go +++ b/plugins/inputs/vsphere/vsphere.go @@ -1,7 +1,6 @@ package vsphere import ( - "log" "sync" "time" @@ -199,26 +198,30 @@ func (v *VSphere) Description() string { return "Read metrics from VMware vCenter" } -func (v *VSphere) checkEndpoints() { +func (v *VSphere) checkEndpoints() error { if v.endpoints != nil { - return + return nil } v.endpoints = make([]*Endpoint, len(v.Vcenters)) for i, rawURL := range v.Vcenters { u, err := soap.ParseURL(rawURL) if err != nil { - log.Printf("E! Can't parse URL %s\n", rawURL) + return err } v.endpoints[i] = NewEndpoint(v, u) } + return nil } // Gather is the main data collection function called by the Telegraf core. It performs all // the data collection and writes all metrics into the Accumulator passed as an argument. func (v *VSphere) Gather(acc telegraf.Accumulator) error { - v.checkEndpoints() + err := v.checkEndpoints() + if err != nil { + return err + } var wg sync.WaitGroup From 2d544742616f449c80ba35a3c76c6efc32296ece Mon Sep 17 00:00:00 2001 From: prydin Date: Thu, 2 Aug 2018 09:44:00 -0400 Subject: [PATCH 080/100] Graceful shutdown and reload --- Gopkg.lock | 416 ++++++++++++++++++++++++--- plugins/inputs/vsphere/client.go | 4 +- plugins/inputs/vsphere/endpoint.go | 205 +++++++------ plugins/inputs/vsphere/vsphere.go | 32 ++- plugins/inputs/vsphere/workerpool.go | 54 +++- 5 files changed, 563 insertions(+), 148 deletions(-) diff --git a/Gopkg.lock b/Gopkg.lock index 314b801c171b3..995013dfa895e 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -3,48 +3,61 @@ [[projects]] branch = "master" + digest = "1:fc0802104acded1f48e4860a9f2db85b82b4a754fca9eae750ff4e8b8cdf2116" name = "code.cloudfoundry.org/clock" packages = ["."] + pruneopts = "" revision = "02e53af36e6c978af692887ed449b74026d76fec" [[projects]] + digest = "1:ca3acef20fd660d4df327accbf3ca2df9a12213d914f3113305dcd56579324b9" name = "collectd.org" packages = [ "api", "cdtime", - "network" + "network", ] + pruneopts = "" revision = "2ce144541b8903101fb8f1483cc0497a68798122" version = "v0.3.0" [[projects]] branch = "master" + digest = "1:298712a3ee36b59c3ca91f4183bd75d174d5eaa8b4aed5072831f126e2e752f6" name = "github.com/Microsoft/ApplicationInsights-Go" packages = [ "appinsights", - "appinsights/contracts" + "appinsights/contracts", ] + pruneopts = "" revision = "d2df5d440eda5372f24fcac03839a64d6cb5f7e5" [[projects]] + digest = "1:6331095c1906771fbe129fe4a1f94ac5b5a97b0f60f2f80653bb95c3e5dad81e" name = "github.com/Microsoft/go-winio" packages = ["."] + pruneopts = "" revision = "7da180ee92d8bd8bb8c37fc560e673e6557c392f" version = "v0.4.7" [[projects]] + digest = "1:9362b2212139b7821f73a86169bf80ce6b0264956f87d82ab3aeedb2b5c08fea" name = "github.com/Shopify/sarama" packages = ["."] + pruneopts = "" revision = "35324cf48e33d8260e1c7c18854465a904ade249" version = "v1.17.0" [[projects]] + digest = "1:f82b8ac36058904227087141017bb82f4b0fc58272990a4cdae3e2d6d222644e" name = "github.com/StackExchange/wmi" packages = ["."] + pruneopts = "" revision = "5d049714c4a64225c3c79a7cf7d02f7fb5b96338" version = "1.0.0" [[projects]] + digest = "1:7b368a3c948013ea05b8c07789ad3d4ff42c141f22dd7cd4d3adcc8adc30e833" name = "github.com/aerospike/aerospike-client-go" packages = [ ".", @@ -57,42 +70,52 @@ "types/atomic", "types/particle_type", "types/rand", - "utils/buffer" + "utils/buffer", ] + pruneopts = "" revision = "c10b5393e43bd60125aca6289c7b24879edb1787" version = "v1.33.0" [[projects]] branch = "master" + digest = "1:a74730e052a45a3fab1d310fdef2ec17ae3d6af16228421e238320846f2aaec8" name = "github.com/alecthomas/template" packages = [ ".", - "parse" + "parse", ] + pruneopts = "" revision = "a0175ee3bccc567396460bf5acd36800cb10c49c" [[projects]] branch = "master" + digest = "1:8483994d21404c8a1d489f6be756e25bfccd3b45d65821f25695577791a08e68" name = "github.com/alecthomas/units" packages = ["."] + pruneopts = "" revision = "2efee857e7cfd4f3d0138cc3cbb1b4966962b93a" [[projects]] branch = "master" + digest = "1:7f21a8f175ee7f91c659f919c61032e11889fba5dc25c0cec555087cbb87435a" name = "github.com/amir/raidman" packages = [ ".", - "proto" + "proto", ] + pruneopts = "" revision = "1ccc43bfb9c93cb401a4025e49c64ba71e5e668b" [[projects]] branch = "master" + digest = "1:a98b511083f2e0ad1b3e02edd46107f8294b257216188a0813ee7b434f62f612" name = "github.com/apache/thrift" packages = ["lib/go/thrift"] + pruneopts = "" revision = "f5f430df56871bc937950274b2c86681d3db6e59" [[projects]] + digest = "1:f9dc8648e19ca5c4ccdf32e13301eaaff14a6662826c98926ba401d98bdea315" name = "github.com/aws/aws-sdk-go" packages = [ "aws", @@ -124,75 +147,95 @@ "private/protocol/xml/xmlutil", "service/cloudwatch", "service/kinesis", - "service/sts" + "service/sts", ] + pruneopts = "" revision = "bfc1a07cf158c30c41a3eefba8aae043d0bb5bff" version = "v1.14.8" [[projects]] branch = "master" + digest = "1:c0bec5f9b98d0bc872ff5e834fac186b807b656683bd29cb82fb207a1513fabb" name = "github.com/beorn7/perks" packages = ["quantile"] + pruneopts = "" revision = "3a771d992973f24aa725d07868b467d1ddfceafb" [[projects]] + digest = "1:c5978131c797af795972c27c25396c81d1bf53b7b6e8e3e0259e58375765c071" name = "github.com/bsm/sarama-cluster" packages = ["."] + pruneopts = "" revision = "cf455bc755fe41ac9bb2861e7a961833d9c2ecc3" version = "v2.1.13" [[projects]] + digest = "1:f619cb9b07aebe5416262cdd8b86082e8d5bdc5264cb3b615ff858df0b645f97" name = "github.com/cenkalti/backoff" packages = ["."] + pruneopts = "" revision = "2ea60e5f094469f9e65adb9cd103795b73ae743e" version = "v2.0.0" [[projects]] branch = "master" + digest = "1:298e42868718da06fc0899ae8fdb99c48a14477045234c9274d81caa79af6a8f" name = "github.com/couchbase/go-couchbase" packages = ["."] + pruneopts = "" revision = "16db1f1fe037412f12738fa4d8448c549c4edd77" [[projects]] branch = "master" + digest = "1:c734658274a6be88870a36742fdea96a3fce4fc99a7b90946c9e84335ceae71a" name = "github.com/couchbase/gomemcached" packages = [ ".", - "client" + "client", ] + pruneopts = "" revision = "0da75df145308b9a4e6704d762ca9d9b77752efc" [[projects]] branch = "master" + digest = "1:c1195c02bc8fbf5307cfb95bc79eddaa1351ee3587cc4a7bbe6932e2fb966ff2" name = "github.com/couchbase/goutils" packages = [ "logging", - "scramsha" + "scramsha", ] + pruneopts = "" revision = "e865a1461c8ac0032bd37e2d4dab3289faea3873" [[projects]] + digest = "1:56c130d885a4aacae1dd9c7b71cfe39912c7ebc1ff7d2b46083c8812996dc43b" name = "github.com/davecgh/go-spew" packages = ["spew"] + pruneopts = "" revision = "346938d642f2ec3594ed81d874461961cd0faa76" version = "v1.1.0" [[projects]] + digest = "1:6098222470fe0172157ce9bbef5d2200df4edde17ee649c5d6e48330e4afa4c6" name = "github.com/dgrijalva/jwt-go" packages = ["."] + pruneopts = "" revision = "06ea1031745cb8b3dab3f6a236daf2b0aa468b7e" version = "v3.2.0" [[projects]] + digest = "1:a9e4ff75555e4500e409dc87c1d708b090bb8dd77f889bbf266773f3dc23af70" name = "github.com/docker/distribution" packages = [ "digest", - "reference" + "reference", ] + pruneopts = "" revision = "48294d928ced5dd9b378f7fd7c6f5da3ff3f2c89" version = "v2.6.2" [[projects]] + digest = "1:e197595b6d3711c79b556868fd869faa27ff8d047b68383e5a1fa623240a7c0d" name = "github.com/docker/docker" packages = [ "api/types", @@ -210,76 +253,96 @@ "api/types/versions", "api/types/volume", "client", - "pkg/tlsconfig" + "pkg/tlsconfig", ] + pruneopts = "" revision = "eef6495eddab52828327aade186443681ed71a4e" version = "v17.03.2-ce-rc1" [[projects]] + digest = "1:a5ecc2e70260a87aa263811281465a5effcfae8a54bac319cee87c4625f04d63" name = "github.com/docker/go-connections" packages = [ "nat", "sockets", - "tlsconfig" + "tlsconfig", ] + pruneopts = "" revision = "3ede32e2033de7505e6500d6c868c2b9ed9f169d" version = "v0.3.0" [[projects]] + digest = "1:582d54fcb7233da8dde1dfd2210a5b9675d0685f84246a8d317b07d680c18b1b" name = "github.com/docker/go-units" packages = ["."] + pruneopts = "" revision = "47565b4f722fb6ceae66b95f853feed578a4a51c" version = "v0.3.3" [[projects]] + digest = "1:6d6672f85a84411509885eaa32f597577873de00e30729b9bb0eb1e1faa49c12" name = "github.com/eapache/go-resiliency" packages = ["breaker"] + pruneopts = "" revision = "ea41b0fad31007accc7f806884dcdf3da98b79ce" version = "v1.1.0" [[projects]] branch = "master" + digest = "1:1f7503fa58a852a1416556ae2ddb219b49a1304fd408391948e2e3676514c48d" name = "github.com/eapache/go-xerial-snappy" packages = ["."] + pruneopts = "" revision = "bb955e01b9346ac19dc29eb16586c90ded99a98c" [[projects]] + digest = "1:d8d46d21073d0f65daf1740ebf4629c65e04bf92e14ce93c2201e8624843c3d3" name = "github.com/eapache/queue" packages = ["."] + pruneopts = "" revision = "44cc805cf13205b55f69e14bcb69867d1ae92f98" version = "v1.1.0" [[projects]] + digest = "1:3fa846cb3feb4e65371fe3c347c299de9b5bc3e71e256c0d940cd19b767a6ba0" name = "github.com/eclipse/paho.mqtt.golang" packages = [ ".", - "packets" + "packets", ] + pruneopts = "" revision = "36d01c2b4cbeb3d2a12063e4880ce30800af9560" version = "v1.1.1" [[projects]] + digest = "1:617b3e0f5989d4ff866a1820480990c65dfc9257eb080da749a45e2d76681b02" name = "github.com/go-ini/ini" packages = ["."] + pruneopts = "" revision = "06f5f3d67269ccec1fe5fe4134ba6e982984f7f5" version = "v1.37.0" [[projects]] + digest = "1:6a4a01d58b227c4b6b11111b9f172ec5c17682b82724e58e6daf3f19f4faccd8" name = "github.com/go-logfmt/logfmt" packages = ["."] + pruneopts = "" revision = "390ab7935ee28ec6b286364bba9b4dd6410cb3d5" version = "v0.3.0" [[projects]] + digest = "1:96c4a6ff4206086347bfe28e96e092642882128f45ecb8dc8f15f3e6f6703af0" name = "github.com/go-ole/go-ole" packages = [ ".", - "oleutil" + "oleutil", ] + pruneopts = "" revision = "a41e3c4b706f6ae8dfbff342b06e40fa4d2d0506" version = "v1.2.1" [[projects]] + digest = "1:3dfd659219b6f63dc0677a62b8d4e8f10b5cf53900aef40858db10a19407e41d" name = "github.com/go-redis/redis" packages = [ ".", @@ -289,18 +352,22 @@ "internal/pool", "internal/proto", "internal/singleflight", - "internal/util" + "internal/util", ] + pruneopts = "" revision = "83fb42932f6145ce52df09860384a4653d2d332a" version = "v6.12.0" [[projects]] + digest = "1:c07de423ca37dc2765396d6971599ab652a339538084b9b58c9f7fc533b28525" name = "github.com/go-sql-driver/mysql" packages = ["."] + pruneopts = "" revision = "d523deb1b23d913de5bdada721a6071e71283618" version = "v1.4.0" [[projects]] + digest = "1:9ab1b1c637d7c8f49e39d8538a650d7eb2137b076790cff69d160823b505964c" name = "github.com/gobwas/glob" packages = [ ".", @@ -310,131 +377,165 @@ "syntax/ast", "syntax/lexer", "util/runes", - "util/strings" + "util/strings", ] + pruneopts = "" revision = "5ccd90ef52e1e632236f7326478d4faa74f99438" version = "v0.2.3" [[projects]] + digest = "1:0a3f6a0c68ab8f3d455f8892295503b179e571b7fefe47cc6c556405d1f83411" name = "github.com/gogo/protobuf" packages = ["proto"] + pruneopts = "" revision = "1adfc126b41513cc696b209667c8656ea7aac67c" version = "v1.0.0" [[projects]] + digest = "1:f958a1c137db276e52f0b50efee41a1a389dcdded59a69711f3e872757dab34b" name = "github.com/golang/protobuf" packages = [ "proto", "ptypes", "ptypes/any", "ptypes/duration", - "ptypes/timestamp" + "ptypes/timestamp", ] + pruneopts = "" revision = "b4deda0973fb4c70b50d226b1af49f3da59f5265" version = "v1.1.0" [[projects]] branch = "master" + digest = "1:2a5888946cdbc8aa360fd43301f9fc7869d663f60d5eedae7d4e6e5e4f06f2bf" name = "github.com/golang/snappy" packages = ["."] + pruneopts = "" revision = "2e65f85255dbc3072edf28d6b5b8efc472979f5a" [[projects]] + digest = "1:f9f45f75f332e03fc7e9fe9188ea4e1ce4d14779ef34fa1b023da67518e36327" name = "github.com/google/go-cmp" packages = [ "cmp", "cmp/internal/diff", "cmp/internal/function", - "cmp/internal/value" + "cmp/internal/value", ] + pruneopts = "" revision = "3af367b6b30c263d47e8895973edcca9a49cf029" version = "v0.2.0" [[projects]] + digest = "1:c1d7e883c50a26ea34019320d8ae40fad86c9e5d56e63a1ba2cb618cef43e986" name = "github.com/google/uuid" packages = ["."] + pruneopts = "" revision = "064e2069ce9c359c118179501254f67d7d37ba24" version = "0.2" [[projects]] + digest = "1:dbbeb8ddb0be949954c8157ee8439c2adfd8dc1c9510eb44a6e58cb68c3dce28" name = "github.com/gorilla/context" packages = ["."] + pruneopts = "" revision = "08b5f424b9271eedf6f9f0ce86cb9396ed337a42" version = "v1.1.1" [[projects]] + digest = "1:c2c8666b4836c81a1d247bdf21c6a6fc1ab586538ab56f74437c2e0df5c375e1" name = "github.com/gorilla/mux" packages = ["."] + pruneopts = "" revision = "e3702bed27f0d39777b0b37b664b6280e8ef8fbf" version = "v1.6.2" [[projects]] branch = "master" + digest = "1:60b7bc5e043a11213472ae05252527287d20e0a6ccc18f6ae67fad88e41004de" name = "github.com/hailocab/go-hostpool" packages = ["."] + pruneopts = "" revision = "e80d13ce29ede4452c43dea11e79b9bc8a15b478" [[projects]] + digest = "1:df4902533e8a103e5b055a847dd22fe0c33a4ab9634a084643bfd06b3e742623" name = "github.com/hashicorp/consul" packages = ["api"] + pruneopts = "" revision = "5174058f0d2bda63fa5198ab96c33d9a909c58ed" version = "v1.1.0" [[projects]] branch = "master" + digest = "1:f5d25fd7bdda08e39e01193ef94a1ebf7547b1b931bcdec785d08050598f306c" name = "github.com/hashicorp/go-cleanhttp" packages = ["."] + pruneopts = "" revision = "d5fe4b57a186c716b0e00b8c301cbd9b4182694d" [[projects]] branch = "master" + digest = "1:ff65bf6fc4d1116f94ac305342725c21b55c16819c2606adc8f527755716937f" name = "github.com/hashicorp/go-rootcerts" packages = ["."] + pruneopts = "" revision = "6bb64b370b90e7ef1fa532be9e591a81c3493e00" [[projects]] + digest = "1:f72168ea995f398bab88e84bd1ff58a983466ba162fb8d50d47420666cd57fad" name = "github.com/hashicorp/serf" packages = ["coordinate"] + pruneopts = "" revision = "d6574a5bb1226678d7010325fb6c985db20ee458" version = "v0.8.1" [[projects]] + digest = "1:a39ef049cdeee03a57b132e7d60e32711b9d949c78458da78e702d9864c54369" name = "github.com/influxdata/go-syslog" packages = [ "rfc5424", - "rfc5425" + "rfc5425", ] + pruneopts = "" revision = "eecd51df3ad85464a2bab9b7d3a45bc1e299059e" version = "v1.0.1" [[projects]] branch = "master" + digest = "1:bc3eb5ddfd59781ea1183f2b3d1eb105a1495d421f09b2ccd360c7fced0b612d" name = "github.com/influxdata/tail" packages = [ ".", "ratelimiter", "util", "watch", - "winfile" + "winfile", ] + pruneopts = "" revision = "c43482518d410361b6c383d7aebce33d0471d7bc" [[projects]] branch = "master" + digest = "1:7fb6cc9607eaa6ef309edebc42b57f704244bd4b9ab23bff128829c4ad09b95d" name = "github.com/influxdata/toml" packages = [ ".", - "ast" + "ast", ] + pruneopts = "" revision = "2a2e3012f7cfbef64091cc79776311e65dfa211b" [[projects]] branch = "master" + digest = "1:a0c157916be0b4de1d4565b1f094b8d746109f94968140dff40a42780fa6ccef" name = "github.com/influxdata/wlog" packages = ["."] + pruneopts = "" revision = "7c63b0a71ef8300adc255344d275e10e5c3a71ec" [[projects]] + digest = "1:2de1791b9e43f26c696e36950e42676565e7da7499a870bc02213da4b59b1d14" name = "github.com/jackc/pgx" packages = [ ".", @@ -443,138 +544,176 @@ "pgio", "pgproto3", "pgtype", - "stdlib" + "stdlib", ] + pruneopts = "" revision = "da3231b0b66e2e74cdb779f1d46c5e958ba8be27" version = "v3.1.0" [[projects]] + digest = "1:6f49eae0c1e5dab1dafafee34b207aeb7a42303105960944828c2079b92fc88e" name = "github.com/jmespath/go-jmespath" packages = ["."] + pruneopts = "" revision = "0b12d6b5" [[projects]] branch = "master" + digest = "1:2c5ad58492804c40bdaf5d92039b0cde8b5becd2b7feeb37d7d1cc36a8aa8dbe" name = "github.com/kardianos/osext" packages = ["."] + pruneopts = "" revision = "ae77be60afb1dcacde03767a8c37337fad28ac14" [[projects]] branch = "master" + digest = "1:fed90fa725d3b1bac0a760de64426834dfef4546474cf182f2ec94285afa74a8" name = "github.com/kardianos/service" packages = ["."] + pruneopts = "" revision = "615a14ed75099c9eaac6949e22ac2341bf9d3197" [[projects]] branch = "master" + digest = "1:63e7368fcf6b54804076eaec26fd9cf0c4466166b272393db4b93102e1e962df" name = "github.com/kballard/go-shellquote" packages = ["."] + pruneopts = "" revision = "95032a82bc518f77982ea72343cc1ade730072f0" [[projects]] branch = "master" + digest = "1:1ed9eeebdf24aadfbca57eb50e6455bd1d2474525e0f0d4454de8c8e9bc7ee9a" name = "github.com/kr/logfmt" packages = ["."] + pruneopts = "" revision = "b84e30acd515aadc4b783ad4ff83aff3299bdfe0" [[projects]] branch = "master" + digest = "1:d9e483f4b9e306facf126bd90b02d512bd22ea4471e1568867e32221a8abbb16" name = "github.com/mailru/easyjson" packages = [ ".", "buffer", "jlexer", - "jwriter" + "jwriter", ] + pruneopts = "" revision = "3fdea8d05856a0c8df22ed4bc71b3219245e4485" [[projects]] + digest = "1:63722a4b1e1717be7b98fc686e0b30d5e7f734b9e93d7dee86293b6deab7ea28" name = "github.com/matttproud/golang_protobuf_extensions" packages = ["pbutil"] + pruneopts = "" revision = "c12348ce28de40eed0136aa2b644d0ee0650e56c" version = "v1.0.1" [[projects]] + digest = "1:4c8d8358c45ba11ab7bb15df749d4df8664ff1582daead28bae58cf8cbe49890" name = "github.com/miekg/dns" packages = ["."] + pruneopts = "" revision = "5a2b9fab83ff0f8bfc99684bd5f43a37abe560f1" version = "v1.0.8" [[projects]] branch = "master" + digest = "1:99651e95333755cbe5c9768c1b80031300acca64a80870b40309202b32585a5a" name = "github.com/mitchellh/go-homedir" packages = ["."] + pruneopts = "" revision = "3864e76763d94a6df2f9960b16a20a33da9f9a66" [[projects]] branch = "master" + digest = "1:eb9117392ee8e7aa44f78e0db603f70b1050ee0ebda4bd40040befb5b218c546" name = "github.com/mitchellh/mapstructure" packages = ["."] + pruneopts = "" revision = "bb74f1db0675b241733089d5a1faa5dd8b0ef57b" [[projects]] + digest = "1:ee2e62b00a9ccc2dba1525f93396e35c847f90f87939df6f361b86315ea5f69a" name = "github.com/multiplay/go-ts3" packages = ["."] + pruneopts = "" revision = "d0d44555495c8776880a17e439399e715a4ef319" version = "v1.0.0" [[projects]] + digest = "1:ccd0def9f0b82b61c5e54fcbfccf528eabb13b489d008e46dc16b808c2e1f765" name = "github.com/naoina/go-stringutil" packages = ["."] + pruneopts = "" revision = "6b638e95a32d0c1131db0e7fe83775cbea4a0d0b" version = "v0.1.0" [[projects]] + digest = "1:2e8b074a9e9fb52071f5d0c514448c3cedd33785f14832499231f34e5e1bdf6f" name = "github.com/nats-io/gnatsd" packages = [ "conf", "logger", "server", "server/pse", - "util" + "util", ] + pruneopts = "" revision = "add6d7930ae6d4bff8823b28999ea87bf1bfd23d" version = "v1.1.0" [[projects]] + digest = "1:665af347df4c5d1ae4c3eacd0754f5337a301f6a3f2444c9993b996605c8c02b" name = "github.com/nats-io/go-nats" packages = [ ".", "encoders/builtin", - "util" + "util", ] + pruneopts = "" revision = "062418ea1c2181f52dc0f954f6204370519a868b" version = "v1.5.0" [[projects]] + digest = "1:be61e8224b84064109eaba8157cbb4bbe6ca12443e182b6624fdfa1c0dcf53d9" name = "github.com/nats-io/nuid" packages = ["."] + pruneopts = "" revision = "289cccf02c178dc782430d534e3c1f5b72af807f" version = "v1.0.0" [[projects]] + digest = "1:7a69f6a3a33929f8b66aa39c93868ad1698f06417fe627ae067559beb94504bd" name = "github.com/nsqio/go-nsq" packages = ["."] + pruneopts = "" revision = "eee57a3ac4174c55924125bb15eeeda8cffb6e6f" version = "v1.0.7" [[projects]] branch = "master" + digest = "1:2da0e5077ed40453dc281b9a2428d84cf6ad14063aed189f6296ca5dd25cf13d" name = "github.com/opentracing-contrib/go-observer" packages = ["."] + pruneopts = "" revision = "a52f2342449246d5bcc273e65cbdcfa5f7d6c63c" [[projects]] + digest = "1:78fb99d6011c2ae6c72f3293a83951311147b12b06a5ffa43abf750c4fab6ac5" name = "github.com/opentracing/opentracing-go" packages = [ ".", "ext", - "log" + "log", ] + pruneopts = "" revision = "1949ddbfd147afd4d964a9f00b24eb291e0e7c38" version = "v1.0.2" [[projects]] + digest = "1:fea0e67285d900e5a0a7ec19ff4b4c82865a28dddbee8454c5360ad908f7069c" name = "github.com/openzipkin/zipkin-go-opentracing" packages = [ ".", @@ -582,88 +721,110 @@ "thrift/gen-go/scribe", "thrift/gen-go/zipkincore", "types", - "wire" + "wire", ] + pruneopts = "" revision = "26cf9707480e6b90e5eff22cf0bbf05319154232" version = "v0.3.4" [[projects]] + digest = "1:ae207f9ee2d8ec63fa2830a7d53626c36890d5ca33b88c80efc22eb1aee0ba78" name = "github.com/pierrec/lz4" packages = [ ".", - "internal/xxh32" + "internal/xxh32", ] + pruneopts = "" revision = "6b9367c9ff401dbc54fabce3fb8d972e799b702d" version = "v2.0.2" [[projects]] + digest = "1:7365acd48986e205ccb8652cc746f09c8b7876030d53710ea6ef7d0bd0dcd7ca" name = "github.com/pkg/errors" packages = ["."] + pruneopts = "" revision = "645ef00459ed84a119197bfb8d8205042c6df63d" version = "v0.8.0" [[projects]] + digest = "1:256484dbbcd271f9ecebc6795b2df8cad4c458dd0f5fd82a8c2fa0c29f233411" name = "github.com/pmezard/go-difflib" packages = ["difflib"] + pruneopts = "" revision = "792786c7400a136282c1664665ae0a8db921c6c2" version = "v1.0.0" [[projects]] + digest = "1:4142d94383572e74b42352273652c62afec5b23f325222ed09198f46009022d1" name = "github.com/prometheus/client_golang" packages = [ "prometheus", - "prometheus/promhttp" + "prometheus/promhttp", ] + pruneopts = "" revision = "c5b7fccd204277076155f10851dad72b76a49317" version = "v0.8.0" [[projects]] branch = "master" + digest = "1:60aca47f4eeeb972f1b9da7e7db51dee15ff6c59f7b401c1588b8e6771ba15ef" name = "github.com/prometheus/client_model" packages = ["go"] + pruneopts = "" revision = "99fa1f4be8e564e8a6b613da7fa6f46c9edafc6c" [[projects]] branch = "master" + digest = "1:bfbc121ef802d245ef67421cff206615357d9202337a3d492b8f668906b485a8" name = "github.com/prometheus/common" packages = [ "expfmt", "internal/bitbucket.org/ww/goautoneg", "log", - "model" + "model", ] + pruneopts = "" revision = "7600349dcfe1abd18d72d3a1770870d9800a7801" [[projects]] branch = "master" + digest = "1:cccf925b20ad15bfad909bdfed67a0d9a29c7e2264597199858067e7eeada232" name = "github.com/prometheus/procfs" packages = [ ".", "internal/util", "nfs", - "xfs" + "xfs", ] + pruneopts = "" revision = "7d6f385de8bea29190f15ba9931442a0eaef9af7" [[projects]] branch = "master" + digest = "1:15bcdc717654ef21128e8af3a63eec39a6d08a830e297f93d65163f87c8eb523" name = "github.com/rcrowley/go-metrics" packages = ["."] + pruneopts = "" revision = "e2704e165165ec55d062f5919b4b29494e9fa790" [[projects]] branch = "master" + digest = "1:7fc2f428767a2521abc63f1a663d981f61610524275d6c0ea645defadd4e916f" name = "github.com/samuel/go-zookeeper" packages = ["zk"] + pruneopts = "" revision = "c4fab1ac1bec58281ad0667dc3f0907a9476ac47" [[projects]] + digest = "1:7f569d906bdd20d906b606415b7d794f798f91a62fcfb6a4daa6d50690fb7a3f" name = "github.com/satori/go.uuid" packages = ["."] + pruneopts = "" revision = "f58768cc1a7a7e77a3bd49e98cdd21419399b6a3" version = "v1.2.0" [[projects]] + digest = "1:5e9a035ad95373cc44be4e0682e30923d02cdc238e280b8937073092dba8e68e" name = "github.com/shirou/gopsutil" packages = [ "cpu", @@ -673,70 +834,98 @@ "load", "mem", "net", - "process" + "process", ] + pruneopts = "" revision = "eeb1d38d69593f121e060d24d17f7b1f0936b203" version = "v2.18.05" [[projects]] branch = "master" + digest = "1:99c6a6dab47067c9b898e8c8b13d130c6ab4ffbcc4b7cc6236c2cd0b1e344f5b" name = "github.com/shirou/w32" packages = ["."] + pruneopts = "" revision = "bb4de0191aa41b5507caa14b0650cdbddcd9280b" [[projects]] + digest = "1:8cf46b6c18a91068d446e26b67512cf16f1540b45d90b28b9533706a127f0ca6" name = "github.com/sirupsen/logrus" packages = ["."] + pruneopts = "" revision = "c155da19408a8799da419ed3eeb0cb5db0ad5dbc" version = "v1.0.5" [[projects]] branch = "master" + digest = "1:dfd05e2ca27305910d7cd344c780322de31abddb43c7ab5c62663b19252bc7d2" name = "github.com/soniah/gosnmp" packages = ["."] + pruneopts = "" revision = "bcf840db66be7d64bf96c3c0e075c92e3d98f793" [[projects]] branch = "master" + digest = "1:4e8f1cae8e6d83af9000d82566efb8823907dae77ba4f1d76ff28fdd197c3c90" name = "github.com/streadway/amqp" packages = ["."] + pruneopts = "" revision = "e5adc2ada8b8efff032bf61173a233d143e9318e" [[projects]] + digest = "1:711eebe744c0151a9d09af2315f0bb729b2ec7637ef4c410fa90a18ef74b65b6" name = "github.com/stretchr/objx" packages = ["."] + pruneopts = "" revision = "477a77ecc69700c7cdeb1fa9e129548e1c1c393c" version = "v0.1.1" [[projects]] + digest = "1:c587772fb8ad29ad4db67575dad25ba17a51f072ff18a22b4f0257a4d9c24f75" name = "github.com/stretchr/testify" packages = [ "assert", "mock", - "require" + "require", ] + pruneopts = "" revision = "f35b8ab0b5a2cef36673838d662e249dd9c94686" version = "v1.2.2" [[projects]] + branch = "master" + digest = "1:86e6712cfd4070a2120c03fcec41cfcbbc51813504a74e28d74479edfaf669ee" + name = "github.com/tevino/abool" + packages = ["."] + pruneopts = "" + revision = "9b9efcf221b50905aab9bbabd3daed56dc10f339" + +[[projects]] + digest = "1:bcc2f7db4796010b35ccd2425fe46849096a4e98ed53f572567cb76de1187b3a" name = "github.com/tidwall/gjson" packages = ["."] + pruneopts = "" revision = "afaeb9562041a8018c74e006551143666aed08bf" version = "v1.1.1" [[projects]] branch = "master" + digest = "1:4db4f92bb9cb04cfc4fccb36aba2598b02a988008c4cc0692b241214ad8ac96e" name = "github.com/tidwall/match" packages = ["."] + pruneopts = "" revision = "1731857f09b1f38450e2c12409748407822dc6be" [[projects]] + digest = "1:343f20460c11a0d0529fe532553bfef9446918d1a1fda6d8661eb27d5b1a68b8" name = "github.com/vjeantet/grok" packages = ["."] + pruneopts = "" revision = "ce01e59abcf6fbc9833b7deb5e4b8ee1769bcc53" version = "v1.0.0" [[projects]] + digest = "1:f9fe29bf856d49f9a51d6001588cb5ee5d65c8a7ff5e8b0dd5423c3a510f0833" name = "github.com/vmware/govmomi" packages = [ ".", @@ -759,42 +948,52 @@ "vim25/progress", "vim25/soap", "vim25/types", - "vim25/xml" + "vim25/xml", ] + pruneopts = "" revision = "e3a01f9611c32b2362366434bcd671516e78955d" version = "v0.18.0" [[projects]] branch = "master" + digest = "1:98ed05e9796df287b90c1d96854e3913c8e349dbc546412d3cabb472ecf4b417" name = "github.com/wvanbergen/kafka" packages = ["consumergroup"] + pruneopts = "" revision = "e2edea948ddfee841ea9a263b32ccca15f7d6c2f" [[projects]] branch = "master" + digest = "1:12aff3cc417907bf9f683a6bf1dc78ffb08e41bc69f829491e593ea9b951a3cf" name = "github.com/wvanbergen/kazoo-go" packages = ["."] + pruneopts = "" revision = "f72d8611297a7cf105da904c04198ad701a60101" [[projects]] branch = "master" + digest = "1:e9c11e83ef8ba26e751bd4cc06344d37072de8552a03dfcd3ed6d3d9f764cb1c" name = "github.com/yuin/gopher-lua" packages = [ ".", "ast", "parse", - "pm" + "pm", ] + pruneopts = "" revision = "ca850f594eaafa5468da2bd53b865e4ee55be18b" [[projects]] branch = "master" + digest = "1:2fcfc6c3fb8dfe0d80d7789272230d3ac7db15022b66817113f98d9fff880225" name = "github.com/zensqlmonitor/go-mssqldb" packages = ["."] + pruneopts = "" revision = "e8fbf836e44e86764eba398361d1825651709547" [[projects]] branch = "master" + digest = "1:419d8420cd7231162a9620ca6bc3b2c9ac98270590773d3f25d90950ccc984cc" name = "golang.org/x/crypto" packages = [ "bcrypt", @@ -803,12 +1002,14 @@ "ed25519/internal/edwards25519", "md4", "pbkdf2", - "ssh/terminal" + "ssh/terminal", ] + pruneopts = "" revision = "027cca12c2d63e3d62b670d901e8a2c95854feec" [[projects]] branch = "master" + digest = "1:5dc6753986b9eeba4abdf05dedc5ba06bb52dad43cc8aad35ffb42bb7adfa68f" name = "golang.org/x/net" packages = [ "bpf", @@ -829,12 +1030,14 @@ "ipv6", "proxy", "trace", - "websocket" + "websocket", ] + pruneopts = "" revision = "db08ff08e8622530d9ed3a0e8ac279f6d4c02196" [[projects]] branch = "master" + digest = "1:7e5298358e5f751305289e82373c7ac6832bdc492055d6da23c72fa1d8053c3f" name = "golang.org/x/sys" packages = [ "unix", @@ -843,11 +1046,13 @@ "windows/svc", "windows/svc/debug", "windows/svc/eventlog", - "windows/svc/mgr" + "windows/svc/mgr", ] + pruneopts = "" revision = "6c888cc515d3ed83fc103cf1d84468aad274b0a7" [[projects]] + digest = "1:5acd3512b047305d49e8763eef7ba423901e85d5dd2fd1e71778a0ea8de10bd4" name = "golang.org/x/text" packages = [ "collate", @@ -875,24 +1080,30 @@ "unicode/bidi", "unicode/cldr", "unicode/norm", - "unicode/rangetable" + "unicode/rangetable", ] + pruneopts = "" revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0" version = "v0.3.0" [[projects]] + digest = "1:c1771ca6060335f9768dff6558108bc5ef6c58506821ad43377ee23ff059e472" name = "google.golang.org/appengine" packages = ["cloudsql"] + pruneopts = "" revision = "b1f26356af11148e710935ed1ac8a7f5702c7612" version = "v1.1.0" [[projects]] branch = "master" + digest = "1:d88c2eb6750028f5707971ed1340a02407d5f0967af85bf6ac70c031533f6c15" name = "google.golang.org/genproto" packages = ["googleapis/rpc/status"] + pruneopts = "" revision = "32ee49c4dd805befd833990acba36cb75042378c" [[projects]] + digest = "1:dda79c5192c1c59f3f60bbc109a6c710f37e3ebef4d1e7527f586e11ec9ba2af" name = "google.golang.org/grpc" packages = [ ".", @@ -918,90 +1129,209 @@ "stats", "status", "tap", - "transport" + "transport", ] + pruneopts = "" revision = "7a6a684ca69eb4cae85ad0a484f2e531598c047b" version = "v1.12.2" [[projects]] + digest = "1:15d017551627c8bb091bde628215b2861bed128855343fdd570c62d08871f6e1" name = "gopkg.in/alecthomas/kingpin.v2" packages = ["."] + pruneopts = "" revision = "947dcec5ba9c011838740e680966fd7087a71d0d" version = "v2.2.6" [[projects]] + digest = "1:3cad99e0d1f94b8c162787c12e59d0a0b9df1ef75590eb145cdd625479091efe" name = "gopkg.in/asn1-ber.v1" packages = ["."] + pruneopts = "" revision = "379148ca0225df7a432012b8df0355c2a2063ac0" version = "v1.2" [[projects]] + digest = "1:581450ae66d7970d91ef9132459fa583e937c6e502f1b96e4ee7783a56fa0b44" name = "gopkg.in/fatih/pool.v2" packages = ["."] + pruneopts = "" revision = "010e0b745d12eaf8426c95f9c3924d81dd0b668f" version = "v2.0.0" [[projects]] + digest = "1:eb53021a8aa3f599d29c7102e65026242bdedce998a54837dc67f14b6a97c5fd" name = "gopkg.in/fsnotify.v1" packages = ["."] + pruneopts = "" revision = "c2828203cd70a50dcccfb2761f8b1f8ceef9a8e9" source = "https://github.com/fsnotify/fsnotify/archive/v1.4.7.tar.gz" version = "v1.4.7" [[projects]] + digest = "1:960720207d3d0992995f4576e1366fd9e9b1483473b07fb7243144f75f5b1546" name = "gopkg.in/gorethink/gorethink.v3" packages = [ ".", "encoding", "ql2", - "types" + "types", ] + pruneopts = "" revision = "7f5bdfd858bb064d80559b2a32b86669c5de5d3b" version = "v3.0.5" [[projects]] + digest = "1:367baf06b7dbd0ef0bbdd785f6a79f929c96b0c18e9d3b29c0eed1ac3f5db133" name = "gopkg.in/ldap.v2" packages = ["."] + pruneopts = "" revision = "bb7a9ca6e4fbc2129e3db588a34bc970ffe811a9" version = "v2.5.1" [[projects]] branch = "v2" + digest = "1:c80894778314c7fb90d94a5ab925214900e1341afeddc953cda7398b8cdcd006" name = "gopkg.in/mgo.v2" packages = [ ".", "bson", "internal/json", "internal/sasl", - "internal/scram" + "internal/scram", ] + pruneopts = "" revision = "3f83fa5005286a7fe593b055f0d7771a7dce4655" [[projects]] + digest = "1:a7a0e1001d22f58b036e79c0087a5d07238892966e4c7f6b75070c09b3225fb0" name = "gopkg.in/olivere/elastic.v5" packages = [ ".", "config", - "uritemplates" + "uritemplates", ] + pruneopts = "" revision = "b708306d715bea9b983685e94ab4602cdc9f988b" version = "v5.0.69" [[projects]] branch = "v1" + digest = "1:a96d16bd088460f2e0685d46c39bcf1208ba46e0a977be2df49864ec7da447dd" name = "gopkg.in/tomb.v1" packages = ["."] + pruneopts = "" revision = "dd632973f1e7218eb1089048e0798ec9ae7dceb8" [[projects]] + digest = "1:f0620375dd1f6251d9973b5f2596228cc8042e887cd7f827e4220bc1ce8c30e2" name = "gopkg.in/yaml.v2" packages = ["."] + pruneopts = "" revision = "5420a8b6744d3b0345ab293f6fcba19c978f1183" version = "v2.2.1" [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "5201186bc1dbb97c0427ab3cfa769dd5242d21d199a5e97f5a413d775760aa7e" + input-imports = [ + "collectd.org/api", + "collectd.org/network", + "github.com/Microsoft/ApplicationInsights-Go/appinsights", + "github.com/Shopify/sarama", + "github.com/StackExchange/wmi", + "github.com/aerospike/aerospike-client-go", + "github.com/amir/raidman", + "github.com/apache/thrift/lib/go/thrift", + "github.com/aws/aws-sdk-go/aws", + "github.com/aws/aws-sdk-go/aws/client", + "github.com/aws/aws-sdk-go/aws/credentials", + "github.com/aws/aws-sdk-go/aws/credentials/stscreds", + "github.com/aws/aws-sdk-go/aws/session", + "github.com/aws/aws-sdk-go/service/cloudwatch", + "github.com/aws/aws-sdk-go/service/kinesis", + "github.com/aws/aws-sdk-go/service/sts", + "github.com/bsm/sarama-cluster", + "github.com/couchbase/go-couchbase", + "github.com/dgrijalva/jwt-go", + "github.com/docker/docker/api/types", + "github.com/docker/docker/api/types/container", + "github.com/docker/docker/api/types/filters", + "github.com/docker/docker/api/types/registry", + "github.com/docker/docker/api/types/swarm", + "github.com/docker/docker/client", + "github.com/docker/go-connections/sockets", + "github.com/eclipse/paho.mqtt.golang", + "github.com/go-redis/redis", + "github.com/go-sql-driver/mysql", + "github.com/gobwas/glob", + "github.com/golang/protobuf/proto", + "github.com/google/go-cmp/cmp", + "github.com/gorilla/mux", + "github.com/hashicorp/consul/api", + "github.com/influxdata/go-syslog/rfc5424", + "github.com/influxdata/go-syslog/rfc5425", + "github.com/influxdata/tail", + "github.com/influxdata/toml", + "github.com/influxdata/toml/ast", + "github.com/influxdata/wlog", + "github.com/jackc/pgx/stdlib", + "github.com/kardianos/service", + "github.com/kballard/go-shellquote", + "github.com/matttproud/golang_protobuf_extensions/pbutil", + "github.com/miekg/dns", + "github.com/multiplay/go-ts3", + "github.com/nats-io/gnatsd/server", + "github.com/nats-io/go-nats", + "github.com/nsqio/go-nsq", + "github.com/openzipkin/zipkin-go-opentracing", + "github.com/openzipkin/zipkin-go-opentracing/thrift/gen-go/zipkincore", + "github.com/prometheus/client_golang/prometheus", + "github.com/prometheus/client_golang/prometheus/promhttp", + "github.com/prometheus/client_model/go", + "github.com/prometheus/common/expfmt", + "github.com/prometheus/common/log", + "github.com/satori/go.uuid", + "github.com/shirou/gopsutil/cpu", + "github.com/shirou/gopsutil/disk", + "github.com/shirou/gopsutil/host", + "github.com/shirou/gopsutil/load", + "github.com/shirou/gopsutil/mem", + "github.com/shirou/gopsutil/net", + "github.com/shirou/gopsutil/process", + "github.com/soniah/gosnmp", + "github.com/streadway/amqp", + "github.com/stretchr/testify/assert", + "github.com/stretchr/testify/mock", + "github.com/stretchr/testify/require", + "github.com/tevino/abool", + "github.com/tidwall/gjson", + "github.com/vjeantet/grok", + "github.com/vmware/govmomi", + "github.com/vmware/govmomi/performance", + "github.com/vmware/govmomi/simulator", + "github.com/vmware/govmomi/view", + "github.com/vmware/govmomi/vim25/mo", + "github.com/vmware/govmomi/vim25/soap", + "github.com/vmware/govmomi/vim25/types", + "github.com/wvanbergen/kafka/consumergroup", + "github.com/zensqlmonitor/go-mssqldb", + "golang.org/x/net/context", + "golang.org/x/net/html/charset", + "golang.org/x/sys/unix", + "golang.org/x/sys/windows", + "golang.org/x/sys/windows/svc", + "golang.org/x/sys/windows/svc/mgr", + "google.golang.org/grpc", + "google.golang.org/grpc/codes", + "google.golang.org/grpc/credentials", + "google.golang.org/grpc/status", + "gopkg.in/gorethink/gorethink.v3", + "gopkg.in/ldap.v2", + "gopkg.in/mgo.v2", + "gopkg.in/mgo.v2/bson", + "gopkg.in/olivere/elastic.v5", + "gopkg.in/yaml.v2", + ] solver-name = "gps-cdcl" solver-version = 1 diff --git a/plugins/inputs/vsphere/client.go b/plugins/inputs/vsphere/client.go index 224b754c75fb7..c7beefada034f 100644 --- a/plugins/inputs/vsphere/client.go +++ b/plugins/inputs/vsphere/client.go @@ -27,12 +27,12 @@ func NewClient(u *url.URL, vs *VSphere) (*Client, error) { return nil, err } if vs.Username != "" { - log.Printf("D! Logging in using explicit credentials: %s", vs.Username) + log.Printf("D! [input.vsphere]: Logging in using explicit credentials: %s", vs.Username) u.User = url.UserPassword(vs.Username, vs.Password) } ctx := context.Background() - log.Printf("D! Creating client: %s", u.Host) + log.Printf("D! [input.vsphere]: Creating client: %s", u.Host) c, err := govmomi.NewClient(ctx, u, vs.InsecureSkipVerify) if err != nil { return nil, err diff --git a/plugins/inputs/vsphere/endpoint.go b/plugins/inputs/vsphere/endpoint.go index 1b5629050da68..0893645f91bc9 100644 --- a/plugins/inputs/vsphere/endpoint.go +++ b/plugins/inputs/vsphere/endpoint.go @@ -30,11 +30,12 @@ type Endpoint struct { resourceKinds map[string]resourceKind metricNames map[int32]string discoveryTicker *time.Ticker - clientMux sync.Mutex collectMux sync.RWMutex initialized bool + stopped uint32 collectClient *Client discoverClient *Client + wg sync.WaitGroup } type resourceKind struct { @@ -46,7 +47,7 @@ type resourceKind struct { objects objectMap filters filter.Filter collectInstances bool - getObjects func(*view.ContainerView) (objectMap, error) + getObjects func(context.Context, *view.ContainerView) (objectMap, error) } type metricEntry struct { @@ -88,6 +89,7 @@ func NewEndpoint(parent *VSphere, url *url.URL) *Endpoint { Parent: parent, lastColls: make(map[string]time.Time), instanceInfo: make(map[string]resourceInfo), + stopped: 0, initialized: false, } @@ -149,56 +151,81 @@ func newFilterOrPanic(include []string, exclude []string) filter.Filter { return f } -func (e *Endpoint) init() error { +func (e *Endpoint) startDiscovery(ctx context.Context) { + e.discoveryTicker = time.NewTicker(e.Parent.ObjectDiscoveryInterval.Duration) + go func() { + for { + select { + case <-e.discoveryTicker.C: + err := e.discover(ctx) + if err != nil && err != context.Canceled { + log.Printf("E! [input.vsphere]: Error in discovery for %s: %v", e.URL.Host, err) + } + case <-ctx.Done(): + log.Printf("D! [input.vsphere]: Exiting discovery goroutine for %s", e.URL.Host) + e.discoveryTicker.Stop() + return + } + } + }() +} - err := e.setupMetricIds() +func (e *Endpoint) init(ctx context.Context) error { + + err := e.setupMetricIds(ctx) if err != nil { - log.Printf("E! Error in metric setup for %s: %v", e.URL.Host, err) return err } if e.Parent.ObjectDiscoveryInterval.Duration.Seconds() > 0 { - discoverFunc := func() { - err = e.discover() + + // Run an initial discovery. If force_discovery_on_init isn't set, we kick it off as a + // goroutine without waiting for it. This will probably cause us to report an empty + // dataset on the first collection, but it solves the issue of the first collection timing out. + if e.Parent.ForceDiscoverOnInit { + log.Printf("D! [input.vsphere]: Running initial discovery and waiting for it to finish") + err := e.discover(ctx) if err != nil { - log.Printf("E! Error in initial discovery for %s: %v", e.URL.Host, err) + return err } - // Create discovery ticker - // - e.discoveryTicker = time.NewTicker(e.Parent.ObjectDiscoveryInterval.Duration) + // Now schedule a recurring discovery after the configured delay period go func() { - for range e.discoveryTicker.C { - err := e.discover() - if err != nil { - log.Printf("E! Error in discovery for %s: %v", e.URL.Host, err) - } + log.Println("D! [input.vsphere]: Waiting to schedule recurring discovery") + tmo := time.After(e.Parent.ObjectDiscoveryInterval.Duration) + + // Wait for either initial collection delay to expire or shutdown. + select { + case <-tmo: + log.Println("D! [input.vsphere]: Scheduling recurring discovery") + e.startDiscovery(ctx) + case <-ctx.Done(): + // Shutdown requested before first scheduled discovery. Bail out! + log.Printf("D! [input.vsphere]: Exiting discovery goroutine for %s", e.URL.Host) + return } }() - } - - // Run an initial discovery. If force_discovery_on_init isn't set, we kick it off as a - // goroutine without waiting for it. This will probably cause us to report an empty - // dataset on the first collection, but it solves the issue of the first collection timing out. - // - if e.Parent.ForceDiscoverOnInit { - log.Printf("D! Running initial discovery and waiting for it to finish") - discoverFunc() + } else { + // Otherwise, just run it in the background. We'll probably have an incomplete first metric + // collection this way. + e.startDiscovery(ctx) } } e.initialized = true return nil } -func (e *Endpoint) setupMetricIds() error { +func (e *Endpoint) setupMetricIds(ctx context.Context) error { client, err := NewClient(e.URL, e.Parent) if err != nil { return err } defer client.Close() - ctx := context.Background() + log.Println("Getting infos") mn, err := client.Perf.CounterInfoByName(ctx) + log.Println("Done Getting infos") + if err != nil { return err } @@ -209,18 +236,20 @@ func (e *Endpoint) setupMetricIds() error { return nil } -func (e *Endpoint) getMetadata(in interface{}) interface{} { +func (e *Endpoint) getMetadata(ctx context.Context, in interface{}) interface{} { rq := in.(*metricQRequest) - ctx := context.Background() - //log.Printf("D! Querying metadata for %s", rq.obj.name) + //log.Printf("D! [input.vsphere]: Querying metadata for %s", rq.obj.name) metrics, err := e.discoverClient.Perf.AvailableMetric(ctx, rq.obj.ref.Reference(), rq.res.sampling) - if err != nil { - log.Printf("E! Error while getting metric metadata. Discovery will be incomplete. Error: %s", err) + if err != nil && err != context.Canceled { + log.Printf("E! [input.vsphere]: Error while getting metric metadata. Discovery will be incomplete. Error: %s", err) } return &metricQResponse{metrics: &metrics, obj: rq.obj} } -func (e *Endpoint) discover() error { +func (e *Endpoint) discover(ctx context.Context) error { + e.wg.Add(1) + defer e.wg.Done() + sw := NewStopwatch("discover", e.URL.Host) var err error e.discoverClient, err = NewClient(e.URL, e.Parent) @@ -232,35 +261,34 @@ func (e *Endpoint) discover() error { e.discoverClient = nil }() - log.Printf("D! Discover new objects for %s", e.URL.Host) + log.Printf("D! [input.vsphere]: Discover new objects for %s", e.URL.Host) instInfo := make(map[string]resourceInfo) resourceKinds := make(map[string]resourceKind) // Populate resource objects, and endpoint instance info. - // for k, res := range e.resourceKinds { // Need to do this for all resource types even if they are not enabled (but datastore) if res.enabled || (k != "datastore" && k != "vm") { - objects, err := res.getObjects(e.discoverClient.Root) + objects, err := res.getObjects(ctx, e.discoverClient.Root) if err != nil { return err } // Set up a worker pool for processing metadata queries concurrently wp := NewWorkerPool(10) - wp.Run(e.getMetadata, e.Parent.DiscoverConcurrency) + wp.Run(ctx, e.getMetadata, e.Parent.DiscoverConcurrency) // Fill the input channels with resources that need to be queried // for metadata. - wp.Fill(func(in chan interface{}) { + wp.Fill(ctx, func(ctx context.Context, f PushFunc) { for _, obj := range objects { - in <- &metricQRequest{obj: obj, res: &res} + f(ctx, &metricQRequest{obj: obj, res: &res}) } }) // Drain the resulting metadata and build instance infos. - wp.Drain(func(in interface{}) { + wp.Drain(ctx, func(ctx context.Context, in interface{}) { resp := in.(*metricQResponse) mList := make(performance.MetricList, 0) if res.enabled { @@ -293,9 +321,9 @@ func (e *Endpoint) discover() error { return nil } -func getClusters(root *view.ContainerView) (objectMap, error) { +func getClusters(ctx context.Context, root *view.ContainerView) (objectMap, error) { var resources []mo.ClusterComputeResource - err := root.Retrieve(context.Background(), []string{"ClusterComputeResource"}, []string{"name", "parent"}, &resources) + err := root.Retrieve(ctx, []string{"ClusterComputeResource"}, []string{"name", "parent"}, &resources) if err != nil { return nil, err } @@ -307,9 +335,9 @@ func getClusters(root *view.ContainerView) (objectMap, error) { return m, nil } -func getHosts(root *view.ContainerView) (objectMap, error) { +func getHosts(ctx context.Context, root *view.ContainerView) (objectMap, error) { var resources []mo.HostSystem - err := root.Retrieve(context.Background(), []string{"HostSystem"}, []string{"name", "parent"}, &resources) + err := root.Retrieve(ctx, []string{"HostSystem"}, []string{"name", "parent"}, &resources) if err != nil { return nil, err } @@ -321,9 +349,9 @@ func getHosts(root *view.ContainerView) (objectMap, error) { return m, nil } -func getVMs(root *view.ContainerView) (objectMap, error) { +func getVMs(ctx context.Context, root *view.ContainerView) (objectMap, error) { var resources []mo.VirtualMachine - err := root.Retrieve(context.Background(), []string{"VirtualMachine"}, []string{"name", "runtime.host", "config.guestId"}, &resources) + err := root.Retrieve(ctx, []string{"VirtualMachine"}, []string{"name", "runtime.host", "config.guestId"}, &resources) if err != nil { return nil, err } @@ -343,9 +371,9 @@ func getVMs(root *view.ContainerView) (objectMap, error) { return m, nil } -func getDatastores(root *view.ContainerView) (objectMap, error) { +func getDatastores(ctx context.Context, root *view.ContainerView) (objectMap, error) { var resources []mo.Datastore - err := root.Retrieve(context.Background(), []string{"Datastore"}, []string{"name", "parent"}, &resources) + err := root.Retrieve(ctx, []string{"Datastore"}, []string{"name", "parent"}, &resources) if err != nil { return nil, err } @@ -357,10 +385,13 @@ func getDatastores(root *view.ContainerView) (objectMap, error) { return m, nil } -func (e *Endpoint) collect(acc telegraf.Accumulator) error { +func (e *Endpoint) collect(ctx context.Context, acc telegraf.Accumulator) error { + e.wg.Add(1) + defer e.wg.Done() + var err error if !e.initialized { - err := e.init() + err := e.init(ctx) if err != nil { return err } @@ -381,15 +412,14 @@ func (e *Endpoint) collect(acc telegraf.Accumulator) error { // If discovery interval is disabled (0), discover on each collection cycle // if e.Parent.ObjectDiscoveryInterval.Duration.Seconds() == 0 { - err = e.discover() + err = e.discover(ctx) if err != nil { - log.Printf("E! Error in discovery prior to collect for %s: %v", e.URL.Host, err) return err } } for k, res := range e.resourceKinds { if res.enabled { - count, duration, err := e.collectResource(k, acc) + count, duration, err := e.collectResource(ctx, k, acc) if err != nil { return err } @@ -402,7 +432,7 @@ func (e *Endpoint) collect(acc telegraf.Accumulator) error { return nil } -func (e *Endpoint) chunker(in chan interface{}, res *resourceKind, now time.Time, latest time.Time) { +func (e *Endpoint) chunker(ctx context.Context, f PushFunc, res *resourceKind, now time.Time, latest time.Time) { pqs := make([]types.PerfQuerySpec, 0, e.Parent.ObjectsPerQuery) metrics := 0 total := 0 @@ -410,7 +440,7 @@ func (e *Endpoint) chunker(in chan interface{}, res *resourceKind, now time.Time for _, object := range res.objects { info, found := e.instanceInfo[object.ref.Value] if !found { - log.Printf("E! Internal error: Instance info not found for MOID %s", object.ref) + log.Printf("E! [input.vsphere]: Internal error: Instance info not found for MOID %s", object.ref) } mr := len(info.metrics) for mr > 0 { @@ -438,11 +468,17 @@ func (e *Endpoint) chunker(in chan interface{}, res *resourceKind, now time.Time // We need to dump the current chunk of metrics for one of two reasons: // 1) We filled up the metric quota while processing the current resource // 2) We are at the last resource and have no more data to process. - // if mr > 0 || (!res.realTime && metrics >= e.Parent.MetricsPerQuery) || nRes >= e.Parent.ObjectsPerQuery { - log.Printf("D! Querying %d objects, %d metrics (%d remaining) of type %s for %s. Processed objects: %d. Total objects %d", + log.Printf("D! [input.vsphere]: Querying %d objects, %d metrics (%d remaining) of type %s for %s. Processed objects: %d. Total objects %d", len(pqs), metrics, mr, res.name, e.URL.Host, total+1, len(res.objects)) - in <- pqs + + // To prevent deadlocks, don't send work items if the context has been cancelled. + if ctx.Err() == context.Canceled { + return + } + + // Call push function + f(ctx, pqs) pqs = make([]types.PerfQuerySpec, 0, e.Parent.ObjectsPerQuery) metrics = 0 nRes = 0 @@ -454,23 +490,23 @@ func (e *Endpoint) chunker(in chan interface{}, res *resourceKind, now time.Time // There may be dangling stuff in the queue. Handle them // if len(pqs) > 0 { - in <- pqs + // Call push function + f(ctx, pqs) } } -func (e *Endpoint) collectResource(resourceType string, acc telegraf.Accumulator) (int, float64, error) { +func (e *Endpoint) collectResource(ctx context.Context, resourceType string, acc telegraf.Accumulator) (int, float64, error) { + // Do we have new data yet? - // res := e.resourceKinds[resourceType] now := time.Now() latest, hasLatest := e.lastColls[resourceType] if hasLatest { elapsed := time.Now().Sub(latest).Seconds() + 5.0 // Allow 5 second jitter. - log.Printf("D! Latest: %s, elapsed: %f, resource: %s", latest, elapsed, resourceType) + log.Printf("D! [input.vsphere]: Latest: %s, elapsed: %f, resource: %s", latest, elapsed, resourceType) if elapsed < float64(res.sampling) { - // No new data would be available. We're outta here! - // - log.Printf("D! Sampling period for %s of %d has not elapsed for %s", + // No new data would be available. We're outta herE! [input.vsphere]: + log.Printf("D! [input.vsphere]: Sampling period for %s of %d has not elapsed for %s", resourceType, res.sampling, e.URL.Host) return 0, 0, nil } @@ -481,21 +517,18 @@ func (e *Endpoint) collectResource(resourceType string, acc telegraf.Accumulator internalTags := map[string]string{"resourcetype": resourceType} sw := NewStopwatchWithTags("endpoint_gather", e.URL.Host, internalTags) - log.Printf("D! Start of sample period deemed to be %s", latest) - log.Printf("D! Collecting metrics for %d objects of type %s for %s", + log.Printf("D! [input.vsphere]: [input.vsphere] Start of sample period deemed to be %s", latest) + log.Printf("D! [input.vsphere]: Collecting metrics for %d objects of type %s for %s", len(res.objects), resourceType, e.URL.Host) count := int64(0) - // chunkCh := make(chan []types.PerfQuerySpec) - // errorCh := make(chan error, e.Parent.CollectConcurrency) // Try not to block on errors. - // doneCh := make(chan bool) // Set up a worker pool for collecting chunk metrics wp := NewWorkerPool(10) - wp.Run(func(in interface{}) interface{} { + wp.Run(ctx, func(ctx context.Context, in interface{}) interface{} { chunk := in.([]types.PerfQuerySpec) - n, err := e.collectChunk(chunk, resourceType, res, acc) - log.Printf("D! Query returned %d metrics", n) + n, err := e.collectChunk(ctx, chunk, resourceType, res, acc) + log.Printf("D! [input.vsphere]: Query returned %d metrics", n) if err != nil { return err } @@ -506,13 +539,13 @@ func (e *Endpoint) collectResource(resourceType string, acc telegraf.Accumulator // Fill the input channel of the worker queue by running the chunking // logic implemented in chunker() - wp.Fill(func(in chan interface{}) { - e.chunker(in, &res, now, latest) + wp.Fill(ctx, func(ctx context.Context, f PushFunc) { + e.chunker(ctx, f, &res, now, latest) }) // Drain the pool. We're getting errors back. They should all be nil var err error - wp.Drain(func(in interface{}) { + wp.Drain(ctx, func(ctx context.Context, in interface{}) { if in != nil { err = in.(error) } @@ -525,16 +558,13 @@ func (e *Endpoint) collectResource(resourceType string, acc telegraf.Accumulator return int(count), time.Now().Sub(now).Seconds(), err } -func (e *Endpoint) collectChunk(pqs []types.PerfQuerySpec, resourceType string, +func (e *Endpoint) collectChunk(ctx context.Context, pqs []types.PerfQuerySpec, resourceType string, res resourceKind, acc telegraf.Accumulator) (int, error) { count := 0 prefix := "vsphere" + e.Parent.Separator + resourceType - ctx := context.Background() metrics, err := e.collectClient.Perf.Query(ctx, pqs) if err != nil { - //TODO: Check the error and attempt to handle gracefully. (ie: object no longer exists) - log.Printf("E! Error querying metrics of %s for %s %s", resourceType, e.URL.Host, err) return count, err } @@ -544,12 +574,11 @@ func (e *Endpoint) collectChunk(pqs []types.PerfQuerySpec, resourceType string, } // Iterate through results - // for _, em := range ems { moid := em.Entity.Reference().Value instInfo, found := e.instanceInfo[moid] if !found { - log.Printf("E! MOID %s not found in cache. Skipping! (This should not happen!)", moid) + log.Printf("E! [input.vsphere]: MOID %s not found in cache. Skipping! (This should not happen!)", moid) continue } buckets := make(map[string]metricEntry) @@ -560,24 +589,22 @@ func (e *Endpoint) collectChunk(pqs []types.PerfQuerySpec, resourceType string, "hostname": instInfo.name, "moid": moid, } + // Populate tags - // objectRef, ok := res.objects[moid] if !ok { - log.Printf("E! MOID %s not found in cache. Skipping", moid) + log.Printf("E! [input.vsphere]: MOID %s not found in cache. Skipping", moid) continue } e.populateTags(&objectRef, resourceType, &res, t, &v) // Now deal with the values - // for idx, value := range v.Value { ts := em.SampleInfo[idx].Timestamp // Organize the metrics into a bucket per measurement. // Data SHOULD be presented to us with the same timestamp for all samples, but in case // they don't we use the measurement name + timestamp as the key for the bucket. - // mn, fn := e.makeMetricIdentifier(prefix, name) bKey := mn + " " + v.Instance + " " + strconv.FormatInt(ts.UnixNano(), 10) bucket, found := buckets[bKey] @@ -586,7 +613,7 @@ func (e *Endpoint) collectChunk(pqs []types.PerfQuerySpec, resourceType string, buckets[bKey] = bucket } if value < 0 { - log.Printf("D! Negative value for %s on %s. Indicates missing samples", name, objectRef.name) + log.Printf("D! [input.vsphere]: Negative value for %s on %s. Indicates missing samples", name, objectRef.name) continue } bucket.fields[fn] = value @@ -595,7 +622,6 @@ func (e *Endpoint) collectChunk(pqs []types.PerfQuerySpec, resourceType string, } // We've iterated through all the metrics and collected buckets for each // measurement name. Now emit them! - // for _, bucket := range buckets { //log.Printf("Bucket key: %s", key) acc.AddFields(bucket.name, bucket.fields, bucket.tags, bucket.ts) @@ -606,13 +632,11 @@ func (e *Endpoint) collectChunk(pqs []types.PerfQuerySpec, resourceType string, func (e *Endpoint) populateTags(objectRef *objectRef, resourceType string, resource *resourceKind, t map[string]string, v *performance.MetricSeries) { // Map name of object. - // if resource.pKey != "" { t[resource.pKey] = objectRef.name } // Map parent reference - // parent, found := e.instanceInfo[objectRef.parentRef.Value] if found { switch resourceType { @@ -636,7 +660,6 @@ func (e *Endpoint) populateTags(objectRef *objectRef, resourceType string, resou } // Determine which point tag to map to the instance - // name := v.Name if v.Instance != "" { if strings.HasPrefix(name, "cpu.") { diff --git a/plugins/inputs/vsphere/vsphere.go b/plugins/inputs/vsphere/vsphere.go index 031830c3ae197..a3165566d7e82 100644 --- a/plugins/inputs/vsphere/vsphere.go +++ b/plugins/inputs/vsphere/vsphere.go @@ -1,6 +1,8 @@ package vsphere import ( + "context" + "log" "sync" "time" @@ -45,6 +47,8 @@ type VSphere struct { Timeout internal.Duration endpoints []*Endpoint + rootCtx context.Context + cancel context.CancelFunc // Mix in the TLS/SSL goodness from core tls.ClientConfig @@ -215,6 +219,25 @@ func (v *VSphere) checkEndpoints() error { return nil } +func (v *VSphere) Start(acc telegraf.Accumulator) error { + log.Println("D! [input.vsphere]: Starting plugin") + rootCtx, cancel := context.WithCancel(context.Background()) + v.rootCtx = rootCtx + v.cancel = cancel + return nil +} + +func (v *VSphere) Stop() { + log.Println("D! [input.vsphere]: Stopping plugin") + v.cancel() + + // Wait for all endpoints to finish + for _, ep := range v.endpoints { + log.Printf("D! [input.vsphere]: Waiting for endpoint %s to finish", ep.URL.Host) + ep.wg.Wait() + } +} + // Gather is the main data collection function called by the Telegraf core. It performs all // the data collection and writes all metrics into the Accumulator passed as an argument. func (v *VSphere) Gather(acc telegraf.Accumulator) error { @@ -229,12 +252,17 @@ func (v *VSphere) Gather(acc telegraf.Accumulator) error { wg.Add(1) go func(endpoint *Endpoint) { defer wg.Done() - acc.AddError(endpoint.collect(acc)) + err := endpoint.collect(v.rootCtx, acc) + if err == context.Canceled { + + // No need to signal errors if we were merely canceled. + err = nil + } + acc.AddError(err) }(ep) } wg.Wait() - return nil } diff --git a/plugins/inputs/vsphere/workerpool.go b/plugins/inputs/vsphere/workerpool.go index 21ad3e11d7632..75140c3e8153c 100644 --- a/plugins/inputs/vsphere/workerpool.go +++ b/plugins/inputs/vsphere/workerpool.go @@ -1,6 +1,8 @@ package vsphere import ( + "context" + "log" "sync" ) @@ -8,16 +10,18 @@ import ( // of the WorkerPool. It is similar to the "map" portion of the // map/reduce semantics, in that it takes a single value as an input, // does some processing and returns a single result. -type WorkerFunc func(interface{}) interface{} +type WorkerFunc func(context.Context, interface{}) interface{} + +type PushFunc func(context.Context, interface{}) bool // DrainerFunc represents a function used to "drain" the WorkerPool, // i.e. pull out all the results generated by the workers and processing // them. The DrainerFunc is called once per result produced. -type DrainerFunc func(interface{}) +type DrainerFunc func(context.Context, interface{}) // FillerFunc represents a function for filling the WorkerPool with jobs. // It is called once and is responsible for pushing jobs onto the supplied channel. -type FillerFunc func(chan interface{}) +type FillerFunc func(context.Context, PushFunc) // WorkerPool implements a simple work pooling mechanism. It runs a predefined // number of goroutines to process jobs. Jobs are inserted using the Fill call @@ -36,8 +40,26 @@ func NewWorkerPool(bufsize int) *WorkerPool { } } +func (w *WorkerPool) push(ctx context.Context, job interface{}) bool { + select { + case w.In <- job: + return true + case <-ctx.Done(): + return false + } +} + +func (w *WorkerPool) pushOut(ctx context.Context, result interface{}) bool { + select { + case w.Out <- result: + return true + case <-ctx.Done(): + return false + } +} + // Run takes a WorkerFunc and runs it in 'n' goroutines. -func (w *WorkerPool) Run(f WorkerFunc, n int) { +func (w *WorkerPool) Run(ctx context.Context, f WorkerFunc, n int) bool { w.wg.Add(1) go func() { defer w.wg.Done() @@ -46,35 +68,47 @@ func (w *WorkerPool) Run(f WorkerFunc, n int) { for i := 0; i < n; i++ { go func() { defer localWg.Done() - for job := range w.In { - w.Out <- f(job) + for { + select { + case job, ok := <-w.In: + if !ok { + return + } + w.pushOut(ctx, f(ctx, job)) + case <-ctx.Done(): + log.Printf("D! [input.vsphere]: Stop requested for worker pool. Exiting.") + return + } } }() } localWg.Wait() close(w.Out) }() + return ctx.Err() == nil } // Fill runs a FillerFunc responsible for supplying work to the pool. You may only // call Fill once. Calling it twice will panic. -func (w *WorkerPool) Fill(f FillerFunc) { +func (w *WorkerPool) Fill(ctx context.Context, f FillerFunc) bool { w.wg.Add(1) go func() { defer w.wg.Done() - f(w.In) + f(ctx, w.push) close(w.In) }() + return true } // Drain runs a DrainerFunc for each result generated by the workers. -func (w *WorkerPool) Drain(f DrainerFunc) { +func (w *WorkerPool) Drain(ctx context.Context, f DrainerFunc) bool { w.wg.Add(1) go func() { defer w.wg.Done() for result := range w.Out { - f(result) + f(ctx, result) } }() w.wg.Wait() + return ctx.Err() == nil } From 3f583e7b7361a487f1308bdb1733531a084d4553 Mon Sep 17 00:00:00 2001 From: prydin Date: Sat, 4 Aug 2018 15:26:17 -0400 Subject: [PATCH 081/100] Fixed questionable use of WaitGroup --- plugins/inputs/vsphere/conc_waitgroup.go | 58 ++++++++++++++++++++++++ plugins/inputs/vsphere/endpoint.go | 19 ++++++-- plugins/inputs/vsphere/vsphere_test.go | 30 ++++++++++-- 3 files changed, 98 insertions(+), 9 deletions(-) create mode 100644 plugins/inputs/vsphere/conc_waitgroup.go diff --git a/plugins/inputs/vsphere/conc_waitgroup.go b/plugins/inputs/vsphere/conc_waitgroup.go new file mode 100644 index 0000000000000..89c093ec72d79 --- /dev/null +++ b/plugins/inputs/vsphere/conc_waitgroup.go @@ -0,0 +1,58 @@ +package vsphere + +import ( + "sync" +) + +// ConcurrentWaitGroup is a WaitGroup with special semantics. While a standard wait group +// requires Add() and Wait() to be called from the same goroutine, a ConcurrentWaitGroup +// allows Add() and Wait() to be called concurrently. +// By allowing this, we can no longer tell whether the jobs we're monitoring are truly +// finished or whether the job count temporarily dropped to zero. To prevent this, we +// add the requirement that no more jobs can be started once the counter has reached zero. +// This is done by returning a flag from Add() that, when set to false, means that +// a Wait() has become unblocked and that the caller must abort its attempt to run a job. +type ConcurrentWaitGroup struct { + mux sync.Mutex + cond *sync.Cond + jobs int + done bool +} + +// NewConcurrentWaitGroup returns a new NewConcurrentWaitGroup. +func NewConcurrentWaitGroup() *ConcurrentWaitGroup { + c := &ConcurrentWaitGroup{} + c.cond = sync.NewCond(&c.mux) + return c +} + +// Add signals the beginning of one or more jobs. The function returns false +// if a Wait() has already been unblocked and callers should not run the job. +func (c *ConcurrentWaitGroup) Add(inc int) bool { + if c.done { + return false + } + c.mux.Lock() + defer c.mux.Unlock() + c.jobs += inc + if c.jobs == 0 { + c.cond.Broadcast() + } + return true +} + +// Done signals that a job is done. Once the number of running jobs reaches +// zero, any blocked calls to Wait() will unblock. +func (c *ConcurrentWaitGroup) Done() { + c.Add(-1) +} + +// Wait blocks until the number of running jobs reaches zero. +func (c *ConcurrentWaitGroup) Wait() { + c.mux.Lock() + defer c.mux.Unlock() + for c.jobs != 0 { + c.cond.Wait() + } + c.done = true +} diff --git a/plugins/inputs/vsphere/endpoint.go b/plugins/inputs/vsphere/endpoint.go index 0893645f91bc9..eb5f34424ad3f 100644 --- a/plugins/inputs/vsphere/endpoint.go +++ b/plugins/inputs/vsphere/endpoint.go @@ -35,7 +35,7 @@ type Endpoint struct { stopped uint32 collectClient *Client discoverClient *Client - wg sync.WaitGroup + wg *ConcurrentWaitGroup } type resourceKind struct { @@ -91,6 +91,7 @@ func NewEndpoint(parent *VSphere, url *url.URL) *Endpoint { instanceInfo: make(map[string]resourceInfo), stopped: 0, initialized: false, + wg: NewConcurrentWaitGroup(), } e.resourceKinds = map[string]resourceKind{ @@ -222,9 +223,7 @@ func (e *Endpoint) setupMetricIds(ctx context.Context) error { } defer client.Close() - log.Println("Getting infos") mn, err := client.Perf.CounterInfoByName(ctx) - log.Println("Done Getting infos") if err != nil { return err @@ -247,7 +246,12 @@ func (e *Endpoint) getMetadata(ctx context.Context, in interface{}) interface{} } func (e *Endpoint) discover(ctx context.Context) error { - e.wg.Add(1) + // Add returning false means we've been released from Wait and no + // more tasks are allowed. This happens when the plugin is stopped + // or reloaded. + if !e.wg.Add(1) { + return context.Canceled + } defer e.wg.Done() sw := NewStopwatch("discover", e.URL.Host) @@ -386,7 +390,12 @@ func getDatastores(ctx context.Context, root *view.ContainerView) (objectMap, er } func (e *Endpoint) collect(ctx context.Context, acc telegraf.Accumulator) error { - e.wg.Add(1) + // Add returning false means we've been released from Wait and no + // more tasks are allowed. This happens when the plugin is stopped + // or reloaded. + if !e.wg.Add(1) { + return context.Canceled + } defer e.wg.Done() var err error diff --git a/plugins/inputs/vsphere/vsphere_test.go b/plugins/inputs/vsphere/vsphere_test.go index 0ee130ab725da..190d362ec0a1e 100644 --- a/plugins/inputs/vsphere/vsphere_test.go +++ b/plugins/inputs/vsphere/vsphere_test.go @@ -1,6 +1,7 @@ package vsphere import ( + "context" "crypto/tls" "fmt" "regexp" @@ -207,19 +208,20 @@ func TestParseConfig(t *testing.T) { func TestWorkerPool(t *testing.T) { wp := NewWorkerPool(100) - wp.Run(func(p interface{}) interface{} { + ctx := context.Background() + wp.Run(ctx, func(ctx context.Context, p interface{}) interface{} { return p.(int) * 2 }, 10) n := 100000 - wp.Fill(func(in chan interface{}) { + wp.Fill(ctx, func(ctx context.Context, f PushFunc) { for i := 0; i < n; i++ { - in <- i + f(ctx, i) } }) results := make([]int, n) i := 0 - wp.Drain(func(p interface{}) { + wp.Drain(ctx, func(ctx context.Context, p interface{}) { results[i] = p.(int) i++ }) @@ -229,6 +231,26 @@ func TestWorkerPool(t *testing.T) { } } +func TestWaitGroup(t *testing.T) { + c := NewConcurrentWaitGroup() + fmt.Println("Running...") + c.Add(1) + for i := 0; i < 5; i++ { + go func() { + require.True(t, c.Add(1)) // Should return true + defer c.Done() + time.Sleep(2 * time.Second) + }() + } + + time.Sleep(1 * time.Second) + go c.Done() + c.Wait() + + // Subsequent adds should return false. + require.True(t, !c.Add(1)) +} + func TestAll(t *testing.T) { m, s, err := createSim() if err != nil { From c2a2f57513e6105b9267eeb48bf4c4592be64691 Mon Sep 17 00:00:00 2001 From: prydin Date: Sat, 4 Aug 2018 18:32:59 -0400 Subject: [PATCH 082/100] Removed gather enabled flags and fixed delayed discovery bug --- plugins/inputs/vsphere/endpoint.go | 45 ++++++++++++++---------------- plugins/inputs/vsphere/vsphere.go | 16 ----------- 2 files changed, 21 insertions(+), 40 deletions(-) diff --git a/plugins/inputs/vsphere/endpoint.go b/plugins/inputs/vsphere/endpoint.go index eb5f34424ad3f..ffbf93260df9d 100644 --- a/plugins/inputs/vsphere/endpoint.go +++ b/plugins/inputs/vsphere/endpoint.go @@ -32,7 +32,6 @@ type Endpoint struct { discoveryTicker *time.Ticker collectMux sync.RWMutex initialized bool - stopped uint32 collectClient *Client discoverClient *Client wg *ConcurrentWaitGroup @@ -89,7 +88,6 @@ func NewEndpoint(parent *VSphere, url *url.URL) *Endpoint { Parent: parent, lastColls: make(map[string]time.Time), instanceInfo: make(map[string]resourceInfo), - stopped: 0, initialized: false, wg: NewConcurrentWaitGroup(), } @@ -98,7 +96,7 @@ func NewEndpoint(parent *VSphere, url *url.URL) *Endpoint { "cluster": { name: "cluster", pKey: "clustername", - enabled: parent.GatherClusters, + enabled: anythingEnabled(parent.ClusterMetricExclude), realTime: false, sampling: 300, objects: make(objectMap), @@ -109,7 +107,7 @@ func NewEndpoint(parent *VSphere, url *url.URL) *Endpoint { "host": { name: "host", pKey: "esxhostname", - enabled: parent.GatherHosts, + enabled: anythingEnabled(parent.HostMetricExclude), realTime: true, sampling: 20, objects: make(objectMap), @@ -120,7 +118,7 @@ func NewEndpoint(parent *VSphere, url *url.URL) *Endpoint { "vm": { name: "vm", pKey: "vmname", - enabled: parent.GatherVms, + enabled: anythingEnabled(parent.VMMetricExclude), realTime: true, sampling: 20, objects: make(objectMap), @@ -131,7 +129,7 @@ func NewEndpoint(parent *VSphere, url *url.URL) *Endpoint { "datastore": { name: "datastore", pKey: "dsname", - enabled: parent.GatherDatastores, + enabled: anythingEnabled(parent.DatastoreMetricExclude), realTime: false, sampling: 300, objects: make(objectMap), @@ -144,6 +142,15 @@ func NewEndpoint(parent *VSphere, url *url.URL) *Endpoint { return &e } +func anythingEnabled(ex []string) bool { + for _, s := range ex { + if s == "*" { + return false + } + } + return true +} + func newFilterOrPanic(include []string, exclude []string) filter.Filter { f, err := filter.NewIncludeExcludeFilter(include, exclude) if err != nil { @@ -189,27 +196,17 @@ func (e *Endpoint) init(ctx context.Context) error { if err != nil { return err } - - // Now schedule a recurring discovery after the configured delay period - go func() { - log.Println("D! [input.vsphere]: Waiting to schedule recurring discovery") - tmo := time.After(e.Parent.ObjectDiscoveryInterval.Duration) - - // Wait for either initial collection delay to expire or shutdown. - select { - case <-tmo: - log.Println("D! [input.vsphere]: Scheduling recurring discovery") - e.startDiscovery(ctx) - case <-ctx.Done(): - // Shutdown requested before first scheduled discovery. Bail out! - log.Printf("D! [input.vsphere]: Exiting discovery goroutine for %s", e.URL.Host) - return - } - }() + e.startDiscovery(ctx) } else { // Otherwise, just run it in the background. We'll probably have an incomplete first metric // collection this way. - e.startDiscovery(ctx) + go func() { + err := e.discover(ctx) + if err != nil && err != context.Canceled { + log.Printf("E! [input.vsphere]: Error in discovery for %s: %v", e.URL.Host, err) + } + e.startDiscovery(ctx) + }() } } e.initialized = true diff --git a/plugins/inputs/vsphere/vsphere.go b/plugins/inputs/vsphere/vsphere.go index a3165566d7e82..7257772a30dd6 100644 --- a/plugins/inputs/vsphere/vsphere.go +++ b/plugins/inputs/vsphere/vsphere.go @@ -20,19 +20,15 @@ type VSphere struct { Vcenters []string Username string Password string - GatherClusters bool ClusterInstances bool ClusterMetricInclude []string ClusterMetricExclude []string - GatherHosts bool HostInstances bool HostMetricInclude []string HostMetricExclude []string - GatherVms bool VMInstances bool `toml:"vm_instances"` VMMetricInclude []string `toml:"vm_metric_include"` VMMetricExclude []string `toml:"vm_metric_exclude"` - GatherDatastores bool DatastoreInstances bool DatastoreMetricInclude []string DatastoreMetricExclude []string @@ -63,8 +59,6 @@ var sampleConfig = ` ############### VMs ############### -# gather_vms = true ### (default=true) - ## Collect VM instance metrics, such as individual cores? (default=true) #vm_instances = true @@ -92,8 +86,6 @@ vm_metric_include = [ ############### Hosts ############### -# gather_hosts = true ## (default=true) - ## Collect host instance metrics, such as individual cores? (default=true) #host_instances = true @@ -139,8 +131,6 @@ host_metric_include = [ ############### Clusters ############### -# gather_clusters = true ## (default=true) - ## Collect cluster instance metrics, such as individual cores? (default=true) #cluster_instances = true @@ -155,8 +145,6 @@ cluster_metric_include = [ ############### Datastores ############### -# gather_datastores = true ## (default=true) - ## Collect datastore instance metrics, such as individual LUNs and datafiles? (default=false) #datastore_instances = false @@ -271,19 +259,15 @@ func init() { return &VSphere{ Vcenters: []string{}, - GatherClusters: true, ClusterInstances: true, ClusterMetricInclude: nil, ClusterMetricExclude: nil, - GatherHosts: true, HostInstances: true, HostMetricInclude: nil, HostMetricExclude: nil, - GatherVms: true, VMInstances: true, VMMetricInclude: nil, VMMetricExclude: nil, - GatherDatastores: true, DatastoreInstances: false, DatastoreMetricInclude: nil, DatastoreMetricExclude: nil, From 4c78eb87bcffd4e8689a586818776f7d5ffa8c42 Mon Sep 17 00:00:00 2001 From: prydin Date: Fri, 10 Aug 2018 12:02:24 -0400 Subject: [PATCH 083/100] Fixed Gopkg.lock --- Gopkg.lock | 1221 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1221 insertions(+) create mode 100644 Gopkg.lock diff --git a/Gopkg.lock b/Gopkg.lock new file mode 100644 index 0000000000000..4a70b057dd88c --- /dev/null +++ b/Gopkg.lock @@ -0,0 +1,1221 @@ +# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. + + +[[projects]] + branch = "master" + digest = "1:d7582b4af1b0b953ff2bb9573a50f787c7e1669cb148fb086a3d1c670a1ac955" + name = "code.cloudfoundry.org/clock" + packages = ["."] + pruneopts = "" + revision = "02e53af36e6c978af692887ed449b74026d76fec" + +[[projects]] + digest = "1:ce7dc0f1ffcd9a2aacc50ae6d322eebff8f4faa2d6c5f445c874cd0b77a63de7" + name = "collectd.org" + packages = [ + "api", + "cdtime", + "network", + ] + pruneopts = "" + revision = "2ce144541b8903101fb8f1483cc0497a68798122" + version = "v0.3.0" + +[[projects]] + branch = "master" + digest = "1:c1269bfaddefd090935401c291ad5df6c03de605a440e941ecc568e19f0f9e3b" + name = "github.com/Microsoft/ApplicationInsights-Go" + packages = [ + "appinsights", + "appinsights/contracts", + ] + pruneopts = "" + revision = "d2df5d440eda5372f24fcac03839a64d6cb5f7e5" + +[[projects]] + digest = "1:ec6a42cd98d70f0916216d8f6df8ca61145edeaad041014aa9c003068de7364c" + name = "github.com/Microsoft/go-winio" + packages = ["."] + pruneopts = "" + revision = "a6d595ae73cf27a1b8fc32930668708f45ce1c85" + version = "v0.4.9" + +[[projects]] + digest = "1:14af5ba5ac88efec490fb59734df34e1bd973198caefa7b0cceed0900ef6164c" + name = "github.com/Shopify/sarama" + packages = ["."] + pruneopts = "" + revision = "35324cf48e33d8260e1c7c18854465a904ade249" + version = "v1.17.0" + +[[projects]] + digest = "1:f82b8ac36058904227087141017bb82f4b0fc58272990a4cdae3e2d6d222644e" + name = "github.com/StackExchange/wmi" + packages = ["."] + pruneopts = "" + revision = "5d049714c4a64225c3c79a7cf7d02f7fb5b96338" + version = "1.0.0" + +[[projects]] + digest = "1:855af787df6b733016849082d66ffda5e0e00856513fcac08a7cf199a23515c2" + name = "github.com/aerospike/aerospike-client-go" + packages = [ + ".", + "internal/lua", + "internal/lua/resources", + "logger", + "pkg/bcrypt", + "pkg/ripemd160", + "types", + "types/atomic", + "types/particle_type", + "types/rand", + "utils/buffer", + ] + pruneopts = "" + revision = "1dc8cf203d24cd454e71ce40ab4cd0bf3112df90" + version = "v1.27.0" + +[[projects]] + branch = "master" + digest = "1:1399282ad03ac819f0e8a747c888407c5c98bb497d33821a7047c7bae667ede0" + name = "github.com/alecthomas/template" + packages = [ + ".", + "parse", + ] + pruneopts = "" + revision = "a0175ee3bccc567396460bf5acd36800cb10c49c" + +[[projects]] + branch = "master" + digest = "1:8483994d21404c8a1d489f6be756e25bfccd3b45d65821f25695577791a08e68" + name = "github.com/alecthomas/units" + packages = ["."] + pruneopts = "" + revision = "2efee857e7cfd4f3d0138cc3cbb1b4966962b93a" + +[[projects]] + branch = "master" + digest = "1:072692f8d76356228f31f64ca3140041a140011c7dea26e746206e8649c71b31" + name = "github.com/amir/raidman" + packages = [ + ".", + "proto", + ] + pruneopts = "" + revision = "1ccc43bfb9c93cb401a4025e49c64ba71e5e668b" + +[[projects]] + branch = "master" + digest = "1:83a67d925714169fa5121021abef0276605c6e4d51c467dd1f0c04344abad1ff" + name = "github.com/apache/thrift" + packages = ["lib/go/thrift"] + pruneopts = "" + revision = "f2867c24984aa53edec54a138c03db934221bdea" + +[[projects]] + digest = "1:ca172b51bfe0a1ae7725dc782339fed4ba697dcd44e29a0a1c765fffdbf05ddc" + name = "github.com/aws/aws-sdk-go" + packages = [ + "aws", + "aws/awserr", + "aws/awsutil", + "aws/client", + "aws/client/metadata", + "aws/corehandlers", + "aws/credentials", + "aws/credentials/ec2rolecreds", + "aws/credentials/endpointcreds", + "aws/credentials/stscreds", + "aws/csm", + "aws/defaults", + "aws/ec2metadata", + "aws/endpoints", + "aws/request", + "aws/session", + "aws/signer/v4", + "internal/sdkio", + "internal/sdkrand", + "internal/sdkuri", + "internal/shareddefaults", + "private/protocol", + "private/protocol/json/jsonutil", + "private/protocol/jsonrpc", + "private/protocol/query", + "private/protocol/query/queryutil", + "private/protocol/rest", + "private/protocol/xml/xmlutil", + "service/cloudwatch", + "service/kinesis", + "service/sts", + ] + pruneopts = "" + revision = "8cf662a972fa7fba8f2c1ec57648cf840e2bb401" + version = "v1.14.30" + +[[projects]] + branch = "master" + digest = "1:fca298802a2ab834d6eb0e284788ae037ebc324c0f325ff92c5eea592d189cc5" + name = "github.com/beorn7/perks" + packages = ["quantile"] + pruneopts = "" + revision = "3a771d992973f24aa725d07868b467d1ddfceafb" + +[[projects]] + digest = "1:0edb96edcfeee9aeba92e605536fbb1542b0bf6a10cea9d0b5a2227d5a703eae" + name = "github.com/bsm/sarama-cluster" + packages = ["."] + pruneopts = "" + revision = "cf455bc755fe41ac9bb2861e7a961833d9c2ecc3" + version = "v2.1.13" + +[[projects]] + digest = "1:f619cb9b07aebe5416262cdd8b86082e8d5bdc5264cb3b615ff858df0b645f97" + name = "github.com/cenkalti/backoff" + packages = ["."] + pruneopts = "" + revision = "2ea60e5f094469f9e65adb9cd103795b73ae743e" + version = "v2.0.0" + +[[projects]] + branch = "master" + digest = "1:65ae2d1625584ba8d16d1e15b25db1fc62334e2040f22dbbbdc7531c909843b2" + name = "github.com/couchbase/go-couchbase" + packages = ["."] + pruneopts = "" + revision = "16db1f1fe037412f12738fa4d8448c549c4edd77" + +[[projects]] + branch = "master" + digest = "1:5db54de7054c072f47806c91ef7625ffa00489ca2da5fbc6ca1c78e08018f6bf" + name = "github.com/couchbase/gomemcached" + packages = [ + ".", + "client", + ] + pruneopts = "" + revision = "0da75df145308b9a4e6704d762ca9d9b77752efc" + +[[projects]] + branch = "master" + digest = "1:0deaa0f28c823119725c8308703f019797bc077e251d1ed3f2b8eae2cc7791d7" + name = "github.com/couchbase/goutils" + packages = [ + "logging", + "scramsha", + ] + pruneopts = "" + revision = "e865a1461c8ac0032bd37e2d4dab3289faea3873" + +[[projects]] + branch = "master" + digest = "1:4c015b7445aa37becc220fde9bdbc4d4329f75af72ca1c98f9b0bd698d6068cb" + name = "github.com/crewjam/rfc5424" + packages = ["."] + pruneopts = "" + revision = "6ae4b209c3f0d5071494be6b883a1970acadda94" + +[[projects]] + digest = "1:0a39ec8bf5629610a4bc7873a92039ee509246da3cef1a0ea60f1ed7e5f9cea5" + name = "github.com/davecgh/go-spew" + packages = ["spew"] + pruneopts = "" + revision = "346938d642f2ec3594ed81d874461961cd0faa76" + version = "v1.1.0" + +[[projects]] + digest = "1:2426da75f49e5b8507a6ed5d4c49b06b2ff795f4aec401c106b7db8fb2625cd7" + name = "github.com/dgrijalva/jwt-go" + packages = ["."] + pruneopts = "" + revision = "06ea1031745cb8b3dab3f6a236daf2b0aa468b7e" + version = "v3.2.0" + +[[projects]] + digest = "1:68df19ee476d93359596377b7437bbe49d233fe014becd060ded757aeed531cd" + name = "github.com/docker/distribution" + packages = [ + "digestset", + "reference", + ] + pruneopts = "" + revision = "edc3ab29cdff8694dd6feb85cfeb4b5f1b38ed9c" + +[[projects]] + digest = "1:a21509491bfd5bd1f99abe1d38430fddd16c8c8dc0092f954e224b93ad87f06b" + name = "github.com/docker/docker" + packages = [ + "api", + "api/types", + "api/types/blkiodev", + "api/types/container", + "api/types/events", + "api/types/filters", + "api/types/image", + "api/types/mount", + "api/types/network", + "api/types/registry", + "api/types/strslice", + "api/types/swarm", + "api/types/swarm/runtime", + "api/types/time", + "api/types/versions", + "api/types/volume", + "client", + ] + pruneopts = "" + revision = "ed7b6428c133e7c59404251a09b7d6b02fa83cc2" + +[[projects]] + digest = "1:5b20afc76a36d3994194e2612e83b51bc2b12db3d4d2a722b24474b2d0e3a890" + name = "github.com/docker/go-connections" + packages = [ + "nat", + "sockets", + "tlsconfig", + ] + pruneopts = "" + revision = "3ede32e2033de7505e6500d6c868c2b9ed9f169d" + version = "v0.3.0" + +[[projects]] + digest = "1:582d54fcb7233da8dde1dfd2210a5b9675d0685f84246a8d317b07d680c18b1b" + name = "github.com/docker/go-units" + packages = ["."] + pruneopts = "" + revision = "47565b4f722fb6ceae66b95f853feed578a4a51c" + version = "v0.3.3" + +[[projects]] + digest = "1:7bbb118aeef9a6b9fef3d57b6cc5378f7cd6e915cabf4dea695e318e1a1bd4e6" + name = "github.com/eapache/go-resiliency" + packages = ["breaker"] + pruneopts = "" + revision = "ea41b0fad31007accc7f806884dcdf3da98b79ce" + version = "v1.1.0" + +[[projects]] + branch = "master" + digest = "1:7b28f7f7c9fb914b30dff111fb910d49bd61d275101f665aea79409bb3ba2ae2" + name = "github.com/eapache/go-xerial-snappy" + packages = ["."] + pruneopts = "" + revision = "040cc1a32f578808623071247fdbd5cc43f37f5f" + +[[projects]] + digest = "1:d8d46d21073d0f65daf1740ebf4629c65e04bf92e14ce93c2201e8624843c3d3" + name = "github.com/eapache/queue" + packages = ["."] + pruneopts = "" + revision = "44cc805cf13205b55f69e14bcb69867d1ae92f98" + version = "v1.1.0" + +[[projects]] + digest = "1:d2e2aebcb8e8027345e16f9d0be8cdee3bb470ba406c7a54cb7457ae3ad4ace5" + name = "github.com/eclipse/paho.mqtt.golang" + packages = [ + ".", + "packets", + ] + pruneopts = "" + revision = "36d01c2b4cbeb3d2a12063e4880ce30800af9560" + version = "v1.1.1" + +[[projects]] + digest = "1:d19c78214e03e297e9e30d2eb11892f731358b2951f2a5c7374658a156373e4c" + name = "github.com/go-ini/ini" + packages = ["."] + pruneopts = "" + revision = "358ee7663966325963d4e8b2e1fbd570c5195153" + version = "v1.38.1" + +[[projects]] + digest = "1:6a4a01d58b227c4b6b11111b9f172ec5c17682b82724e58e6daf3f19f4faccd8" + name = "github.com/go-logfmt/logfmt" + packages = ["."] + pruneopts = "" + revision = "390ab7935ee28ec6b286364bba9b4dd6410cb3d5" + version = "v0.3.0" + +[[projects]] + digest = "1:c3a5ae14424a38c244439732c31a08b5f956c46c4acdc159fc285a52dbf11de0" + name = "github.com/go-ole/go-ole" + packages = [ + ".", + "oleutil", + ] + pruneopts = "" + revision = "a41e3c4b706f6ae8dfbff342b06e40fa4d2d0506" + version = "v1.2.1" + +[[projects]] + digest = "1:f2f6a616a1ca8aed667d956c98f7f6178efe72bbe0a419bd33b9d99841c7de69" + name = "github.com/go-redis/redis" + packages = [ + ".", + "internal", + "internal/consistenthash", + "internal/hashtag", + "internal/pool", + "internal/proto", + "internal/singleflight", + "internal/util", + ] + pruneopts = "" + revision = "83fb42932f6145ce52df09860384a4653d2d332a" + version = "v6.12.0" + +[[projects]] + digest = "1:dc876ae7727280d95f97af5320308131278b93d6c6f5cf953065e18cb8c88fd2" + name = "github.com/go-sql-driver/mysql" + packages = ["."] + pruneopts = "" + revision = "d523deb1b23d913de5bdada721a6071e71283618" + version = "v1.4.0" + +[[projects]] + digest = "1:b7a7e17513aeee6492d93015c7bf29c86a0c1c91210ea56b21e36c1a40958cba" + name = "github.com/gobwas/glob" + packages = [ + ".", + "compiler", + "match", + "syntax", + "syntax/ast", + "syntax/lexer", + "util/runes", + "util/strings", + ] + pruneopts = "" + revision = "5ccd90ef52e1e632236f7326478d4faa74f99438" + version = "v0.2.3" + +[[projects]] + digest = "1:673df1d02ca0c6f51458fe94bbb6fae0b05e54084a31db2288f1c4321255c2da" + name = "github.com/gogo/protobuf" + packages = ["proto"] + pruneopts = "" + revision = "636bf0302bc95575d69441b25a2603156ffdddf1" + version = "v1.1.1" + +[[projects]] + digest = "1:b1d3041d568e065ab4d76f7477844458e9209c0bb241eaccdc0770bf0a13b120" + name = "github.com/golang/protobuf" + packages = [ + "proto", + "ptypes", + "ptypes/any", + "ptypes/duration", + "ptypes/timestamp", + ] + pruneopts = "" + revision = "b4deda0973fb4c70b50d226b1af49f3da59f5265" + version = "v1.1.0" + +[[projects]] + branch = "master" + digest = "1:075128b9fc42e6d99067da1a2e6c0a634a6043b5a60abe6909c51f5ecad37b6d" + name = "github.com/golang/snappy" + packages = ["."] + pruneopts = "" + revision = "2e65f85255dbc3072edf28d6b5b8efc472979f5a" + +[[projects]] + digest = "1:cc082d7b9cc3f832f2aed9d06d1cbb33b6984a61d8ec403535b086415c181607" + name = "github.com/google/go-cmp" + packages = [ + "cmp", + "cmp/internal/diff", + "cmp/internal/function", + "cmp/internal/value", + ] + pruneopts = "" + revision = "3af367b6b30c263d47e8895973edcca9a49cf029" + version = "v0.2.0" + +[[projects]] + digest = "1:dbbeb8ddb0be949954c8157ee8439c2adfd8dc1c9510eb44a6e58cb68c3dce28" + name = "github.com/gorilla/context" + packages = ["."] + pruneopts = "" + revision = "08b5f424b9271eedf6f9f0ce86cb9396ed337a42" + version = "v1.1.1" + +[[projects]] + digest = "1:c2c8666b4836c81a1d247bdf21c6a6fc1ab586538ab56f74437c2e0df5c375e1" + name = "github.com/gorilla/mux" + packages = ["."] + pruneopts = "" + revision = "e3702bed27f0d39777b0b37b664b6280e8ef8fbf" + version = "v1.6.2" + +[[projects]] + branch = "master" + digest = "1:60b7bc5e043a11213472ae05252527287d20e0a6ccc18f6ae67fad88e41004de" + name = "github.com/hailocab/go-hostpool" + packages = ["."] + pruneopts = "" + revision = "e80d13ce29ede4452c43dea11e79b9bc8a15b478" + +[[projects]] + digest = "1:db58383b43f583c44fb47c3331de943a11bb73ea951c2def55d29a454a57f4ee" + name = "github.com/hashicorp/consul" + packages = ["api"] + pruneopts = "" + revision = "39f93f011e591c842acc8053a7f5972aa6e592fd" + version = "v1.2.1" + +[[projects]] + branch = "master" + digest = "1:f5d25fd7bdda08e39e01193ef94a1ebf7547b1b931bcdec785d08050598f306c" + name = "github.com/hashicorp/go-cleanhttp" + packages = ["."] + pruneopts = "" + revision = "d5fe4b57a186c716b0e00b8c301cbd9b4182694d" + +[[projects]] + branch = "master" + digest = "1:cd5813053beac0114f96a7da3924fc8a15e0cd2b139f079e0fcce5d3244ae304" + name = "github.com/hashicorp/go-rootcerts" + packages = ["."] + pruneopts = "" + revision = "6bb64b370b90e7ef1fa532be9e591a81c3493e00" + +[[projects]] + digest = "1:d2b2cff454cb23a9769ef3c9075741f5985773a998584b3b3ce203fe4b1abbea" + name = "github.com/hashicorp/serf" + packages = ["coordinate"] + pruneopts = "" + revision = "d6574a5bb1226678d7010325fb6c985db20ee458" + version = "v0.8.1" + +[[projects]] + digest = "1:cc0cf2e12280074e5c6dc0f15a4bb3d6c43509e6091cdcdcc83eea491577257b" + name = "github.com/influxdata/go-syslog" + packages = [ + "rfc5424", + "rfc5425", + ] + pruneopts = "" + revision = "eecd51df3ad85464a2bab9b7d3a45bc1e299059e" + version = "v1.0.1" + +[[projects]] + branch = "master" + digest = "1:effc58ad45323ad15159bbca533be4870eaddb2d9a513d3488d8bfe822c83532" + name = "github.com/influxdata/tail" + packages = [ + ".", + "ratelimiter", + "util", + "watch", + "winfile", + ] + pruneopts = "" + revision = "c43482518d410361b6c383d7aebce33d0471d7bc" + +[[projects]] + branch = "master" + digest = "1:d31edcf33a3b36218de96e43f3fec18ea96deb2a28b838a3a01a4df856ded345" + name = "github.com/influxdata/toml" + packages = [ + ".", + "ast", + ] + pruneopts = "" + revision = "2a2e3012f7cfbef64091cc79776311e65dfa211b" + +[[projects]] + branch = "master" + digest = "1:a0c157916be0b4de1d4565b1f094b8d746109f94968140dff40a42780fa6ccef" + name = "github.com/influxdata/wlog" + packages = ["."] + pruneopts = "" + revision = "7c63b0a71ef8300adc255344d275e10e5c3a71ec" + +[[projects]] + digest = "1:4197871f269749786aa2406557dba15f10cf79161cdc3998180614c62c8b6351" + name = "github.com/jackc/pgx" + packages = [ + ".", + "chunkreader", + "internal/sanitize", + "pgio", + "pgproto3", + "pgtype", + "stdlib", + ] + pruneopts = "" + revision = "da3231b0b66e2e74cdb779f1d46c5e958ba8be27" + version = "v3.1.0" + +[[projects]] + digest = "1:4f767a115bc8e08576f6d38ab73c376fc1b1cd3bb5041171c9e8668cc7739b52" + name = "github.com/jmespath/go-jmespath" + packages = ["."] + pruneopts = "" + revision = "0b12d6b5" + +[[projects]] + branch = "master" + digest = "1:2c5ad58492804c40bdaf5d92039b0cde8b5becd2b7feeb37d7d1cc36a8aa8dbe" + name = "github.com/kardianos/osext" + packages = ["."] + pruneopts = "" + revision = "ae77be60afb1dcacde03767a8c37337fad28ac14" + +[[projects]] + branch = "master" + digest = "1:2df59f23f11c5c59982f737c98c5523b276bfc85a4773a04b411190402bb30fd" + name = "github.com/kardianos/service" + packages = ["."] + pruneopts = "" + revision = "615a14ed75099c9eaac6949e22ac2341bf9d3197" + +[[projects]] + branch = "master" + digest = "1:63e7368fcf6b54804076eaec26fd9cf0c4466166b272393db4b93102e1e962df" + name = "github.com/kballard/go-shellquote" + packages = ["."] + pruneopts = "" + revision = "95032a82bc518f77982ea72343cc1ade730072f0" + +[[projects]] + branch = "master" + digest = "1:1ed9eeebdf24aadfbca57eb50e6455bd1d2474525e0f0d4454de8c8e9bc7ee9a" + name = "github.com/kr/logfmt" + packages = ["."] + pruneopts = "" + revision = "b84e30acd515aadc4b783ad4ff83aff3299bdfe0" + +[[projects]] + branch = "master" + digest = "1:28ca57775f285ae87cbdc7280aad91c5f2ed3c2af98d9f035d75956d1ca97fe6" + name = "github.com/mailru/easyjson" + packages = [ + ".", + "buffer", + "jlexer", + "jwriter", + ] + pruneopts = "" + revision = "efc7eb8984d6655c26b5c9d2e65c024e5767c37c" + +[[projects]] + digest = "1:49a8b01a6cd6558d504b65608214ca40a78000e1b343ed0da5c6a9ccd83d6d30" + name = "github.com/matttproud/golang_protobuf_extensions" + packages = ["pbutil"] + pruneopts = "" + revision = "c12348ce28de40eed0136aa2b644d0ee0650e56c" + version = "v1.0.1" + +[[projects]] + digest = "1:f0bad0fece0fb73c6ea249c18d8e80ffbe86be0457715b04463068f04686cf39" + name = "github.com/miekg/dns" + packages = ["."] + pruneopts = "" + revision = "5a2b9fab83ff0f8bfc99684bd5f43a37abe560f1" + version = "v1.0.8" + +[[projects]] + branch = "master" + digest = "1:99651e95333755cbe5c9768c1b80031300acca64a80870b40309202b32585a5a" + name = "github.com/mitchellh/go-homedir" + packages = ["."] + pruneopts = "" + revision = "3864e76763d94a6df2f9960b16a20a33da9f9a66" + +[[projects]] + branch = "master" + digest = "1:f43ed2c836208c14f45158fd01577c985688a4d11cf9fd475a939819fef3b321" + name = "github.com/mitchellh/mapstructure" + packages = ["."] + pruneopts = "" + revision = "f15292f7a699fcc1a38a80977f80a046874ba8ac" + +[[projects]] + digest = "1:ee2e62b00a9ccc2dba1525f93396e35c847f90f87939df6f361b86315ea5f69a" + name = "github.com/multiplay/go-ts3" + packages = ["."] + pruneopts = "" + revision = "d0d44555495c8776880a17e439399e715a4ef319" + version = "v1.0.0" + +[[projects]] + digest = "1:ccd0def9f0b82b61c5e54fcbfccf528eabb13b489d008e46dc16b808c2e1f765" + name = "github.com/naoina/go-stringutil" + packages = ["."] + pruneopts = "" + revision = "6b638e95a32d0c1131db0e7fe83775cbea4a0d0b" + version = "v0.1.0" + +[[projects]] + digest = "1:e5894541d6ceec5dd283e24e3530aadf59c06449695d19189a7a27bb4c15840d" + name = "github.com/nats-io/gnatsd" + packages = [ + "conf", + "logger", + "server", + "server/pse", + "util", + ] + pruneopts = "" + revision = "6608e9ac3be979dcb0614b772cc86a87b71acaa3" + version = "v1.2.0" + +[[projects]] + digest = "1:88f1bde4c172e27b05ed46adfbd0e79dc1663a6281e4b39fa3e39d71ead9621d" + name = "github.com/nats-io/go-nats" + packages = [ + ".", + "encoders/builtin", + "util", + ] + pruneopts = "" + revision = "062418ea1c2181f52dc0f954f6204370519a868b" + version = "v1.5.0" + +[[projects]] + digest = "1:be61e8224b84064109eaba8157cbb4bbe6ca12443e182b6624fdfa1c0dcf53d9" + name = "github.com/nats-io/nuid" + packages = ["."] + pruneopts = "" + revision = "289cccf02c178dc782430d534e3c1f5b72af807f" + version = "v1.0.0" + +[[projects]] + digest = "1:501cce26a54c785458b0dd54a08ddd984d4ad0c198255430d5d37cd2efe23149" + name = "github.com/nsqio/go-nsq" + packages = ["."] + pruneopts = "" + revision = "eee57a3ac4174c55924125bb15eeeda8cffb6e6f" + version = "v1.0.7" + +[[projects]] + digest = "1:5d9b668b0b4581a978f07e7d2e3314af18eb27b3fb5d19b70185b7c575723d11" + name = "github.com/opencontainers/go-digest" + packages = ["."] + pruneopts = "" + revision = "279bed98673dd5bef374d3b6e4b09e2af76183bf" + version = "v1.0.0-rc1" + +[[projects]] + digest = "1:0d08f7224705b1df80beee92ffbdc63ab13fd6f6eb80bf287735f9bc7e8b83eb" + name = "github.com/opencontainers/image-spec" + packages = [ + "specs-go", + "specs-go/v1", + ] + pruneopts = "" + revision = "d60099175f88c47cd379c4738d158884749ed235" + version = "v1.0.1" + +[[projects]] + branch = "master" + digest = "1:2da0e5077ed40453dc281b9a2428d84cf6ad14063aed189f6296ca5dd25cf13d" + name = "github.com/opentracing-contrib/go-observer" + packages = ["."] + pruneopts = "" + revision = "a52f2342449246d5bcc273e65cbdcfa5f7d6c63c" + +[[projects]] + digest = "1:bba12aa4747b212f75db3e7fee73fe1b66d303cb3ff0c1984b7f2ad20e8bd2bc" + name = "github.com/opentracing/opentracing-go" + packages = [ + ".", + "ext", + "log", + ] + pruneopts = "" + revision = "1949ddbfd147afd4d964a9f00b24eb291e0e7c38" + version = "v1.0.2" + +[[projects]] + digest = "1:c6c0db6294924072f98a0de090d200bae4b7102b12a443ba9569c4ba7df52aa1" + name = "github.com/openzipkin/zipkin-go-opentracing" + packages = [ + ".", + "flag", + "thrift/gen-go/scribe", + "thrift/gen-go/zipkincore", + "types", + "wire", + ] + pruneopts = "" + revision = "26cf9707480e6b90e5eff22cf0bbf05319154232" + version = "v0.3.4" + +[[projects]] + digest = "1:41de12a4684237dd55a11260c941c2c58a055951985e9473ba1661175a13fea7" + name = "github.com/pierrec/lz4" + packages = [ + ".", + "internal/xxh32", + ] + pruneopts = "" + revision = "1958fd8fff7f115e79725b1288e0b878b3e06b00" + version = "v2.0.3" + +[[projects]] + digest = "1:7365acd48986e205ccb8652cc746f09c8b7876030d53710ea6ef7d0bd0dcd7ca" + name = "github.com/pkg/errors" + packages = ["."] + pruneopts = "" + revision = "645ef00459ed84a119197bfb8d8205042c6df63d" + version = "v0.8.0" + +[[projects]] + digest = "1:256484dbbcd271f9ecebc6795b2df8cad4c458dd0f5fd82a8c2fa0c29f233411" + name = "github.com/pmezard/go-difflib" + packages = ["difflib"] + pruneopts = "" + revision = "792786c7400a136282c1664665ae0a8db921c6c2" + version = "v1.0.0" + +[[projects]] + digest = "1:981835985f655d1d380cc6aa7d9fa9ad7abfaf40c75da200fd40d864cd05a7c3" + name = "github.com/prometheus/client_golang" + packages = [ + "prometheus", + "prometheus/promhttp", + ] + pruneopts = "" + revision = "c5b7fccd204277076155f10851dad72b76a49317" + version = "v0.8.0" + +[[projects]] + branch = "master" + digest = "1:562d53e436b244a9bb5c1ff43bcaf4882e007575d34ec37717b15751c65cc63a" + name = "github.com/prometheus/client_model" + packages = ["go"] + pruneopts = "" + revision = "5c3871d89910bfb32f5fcab2aa4b9ec68e65a99f" + +[[projects]] + branch = "master" + digest = "1:6a8420870eb2935977da1fff0f3afca9bdb3f1e66258c9e91a8a7ce0b5417c3b" + name = "github.com/prometheus/common" + packages = [ + "expfmt", + "internal/bitbucket.org/ww/goautoneg", + "log", + "model", + ] + pruneopts = "" + revision = "7600349dcfe1abd18d72d3a1770870d9800a7801" + +[[projects]] + branch = "master" + digest = "1:00fca823dfcdd8107226f67215afd948b001525223ed955a05b33a4c885c9591" + name = "github.com/prometheus/procfs" + packages = [ + ".", + "internal/util", + "nfs", + "xfs", + ] + pruneopts = "" + revision = "ae68e2d4c00fed4943b5f6698d504a5fe083da8a" + +[[projects]] + branch = "master" + digest = "1:1b65925989a4dfb6d98ef1d530cda33ab1ff25945b14a22a8b8bb27cc282af70" + name = "github.com/rcrowley/go-metrics" + packages = ["."] + pruneopts = "" + revision = "e2704e165165ec55d062f5919b4b29494e9fa790" + +[[projects]] + branch = "master" + digest = "1:d8fe9f454582e04b5693b59cdebe3f0bd9dc29ad9651bfb1633cba4658b66c65" + name = "github.com/samuel/go-zookeeper" + packages = ["zk"] + pruneopts = "" + revision = "c4fab1ac1bec58281ad0667dc3f0907a9476ac47" + +[[projects]] + digest = "1:7f569d906bdd20d906b606415b7d794f798f91a62fcfb6a4daa6d50690fb7a3f" + name = "github.com/satori/go.uuid" + packages = ["."] + pruneopts = "" + revision = "f58768cc1a7a7e77a3bd49e98cdd21419399b6a3" + version = "v1.2.0" + +[[projects]] + digest = "1:987ce58e999676c2e209831390f2d56621ff98def2ecca4928e73fe1e2569954" + name = "github.com/shirou/gopsutil" + packages = [ + "cpu", + "disk", + "host", + "internal/common", + "load", + "mem", + "net", + "process", + ] + pruneopts = "" + revision = "4a180b209f5f494e5923cfce81ea30ba23915877" + version = "v2.18.06" + +[[projects]] + branch = "master" + digest = "1:99c6a6dab47067c9b898e8c8b13d130c6ab4ffbcc4b7cc6236c2cd0b1e344f5b" + name = "github.com/shirou/w32" + packages = ["."] + pruneopts = "" + revision = "bb4de0191aa41b5507caa14b0650cdbddcd9280b" + +[[projects]] + digest = "1:f2cc92b78b2f3b76ab0f9daddddd28627bcfcc6cacf119029aa3850082d95079" + name = "github.com/sirupsen/logrus" + packages = ["."] + pruneopts = "" + revision = "c155da19408a8799da419ed3eeb0cb5db0ad5dbc" + version = "v1.0.5" + +[[projects]] + branch = "master" + digest = "1:79e73b87cb07e380d1a3aaa14fbcc418e0d42eede5f971e7ee2f4a6e6d531deb" + name = "github.com/soniah/gosnmp" + packages = ["."] + pruneopts = "" + revision = "96b86229e9b3ffb4b954144cdc7f98fe3ee1003f" + +[[projects]] + branch = "master" + digest = "1:0a1f8d01a0191f558910bcbfd7e1dc11a53ac374473d13b68b8fe520f21efb07" + name = "github.com/streadway/amqp" + packages = ["."] + pruneopts = "" + revision = "e5adc2ada8b8efff032bf61173a233d143e9318e" + +[[projects]] + digest = "1:34062a2274daa6ec4d2f50d6070cc51cf4674d6d553ed76b406cb3425b9528e8" + name = "github.com/stretchr/objx" + packages = ["."] + pruneopts = "" + revision = "477a77ecc69700c7cdeb1fa9e129548e1c1c393c" + version = "v0.1.1" + +[[projects]] + digest = "1:bc2a12c8863e1080226b7bc69192efd6c37aaa9b85cec508b0a8f54fabb9bd9f" + name = "github.com/stretchr/testify" + packages = [ + "assert", + "mock", + "require", + ] + pruneopts = "" + revision = "f35b8ab0b5a2cef36673838d662e249dd9c94686" + version = "v1.2.2" + +[[projects]] + digest = "1:e139a0dfe24e723193005b291ed82a975041718cfcab9136aa6c9540df70a4ff" + name = "github.com/tidwall/gjson" + packages = ["."] + pruneopts = "" + revision = "f123b340873a0084cb27267eddd8ff615115fbff" + version = "v1.1.2" + +[[projects]] + branch = "master" + digest = "1:4db4f92bb9cb04cfc4fccb36aba2598b02a988008c4cc0692b241214ad8ac96e" + name = "github.com/tidwall/match" + packages = ["."] + pruneopts = "" + revision = "1731857f09b1f38450e2c12409748407822dc6be" + +[[projects]] + digest = "1:23e2b9f3a20cd4a6427147377255ec2f6237e8606fa6ef0707ed79b7bfbe3a83" + name = "github.com/vjeantet/grok" + packages = ["."] + pruneopts = "" + revision = "ce01e59abcf6fbc9833b7deb5e4b8ee1769bcc53" + version = "v1.0.0" + +[[projects]] + branch = "master" + digest = "1:5383edd40c7f6c95a7dc46a47bf0c83de4bf40a4252f12fa803f790037addffc" + name = "github.com/wvanbergen/kafka" + packages = ["consumergroup"] + pruneopts = "" + revision = "e2edea948ddfee841ea9a263b32ccca15f7d6c2f" + +[[projects]] + branch = "master" + digest = "1:f936b4936e1b092cc41c9b33fdc990ad78386545f1ffeca8427c72b2605bca85" + name = "github.com/wvanbergen/kazoo-go" + packages = ["."] + pruneopts = "" + revision = "f72d8611297a7cf105da904c04198ad701a60101" + +[[projects]] + branch = "master" + digest = "1:9946d558a909f63e31332c77b82649522da97ae7f7cfbfebc6f53549ab6b3e0f" + name = "github.com/yuin/gopher-lua" + packages = [ + ".", + "ast", + "parse", + "pm", + ] + pruneopts = "" + revision = "46796da1b0b4794e1e341883a399f12cc7574b55" + +[[projects]] + branch = "master" + digest = "1:84e9087a94f336c204887281046891769d2ed7bf1d2b31c21ff6fb5e1743abce" + name = "github.com/zensqlmonitor/go-mssqldb" + packages = ["."] + pruneopts = "" + revision = "e8fbf836e44e86764eba398361d1825651709547" + +[[projects]] + branch = "master" + digest = "1:21100b2e8b6922303dd109da81b3134ed0eff05cb3402881eabde9cce8f4e5e6" + name = "golang.org/x/crypto" + packages = [ + "bcrypt", + "blowfish", + "ed25519", + "ed25519/internal/edwards25519", + "md4", + "pbkdf2", + "ssh/terminal", + ] + pruneopts = "" + revision = "a2144134853fc9a27a7b1e3eb4f19f1a76df13c9" + +[[projects]] + branch = "master" + digest = "1:58d8f8f3ad415b10d2145316519e5b7995b7cf9e663b33a1e9e0c2ddd96c1d58" + name = "golang.org/x/net" + packages = [ + "bpf", + "context", + "context/ctxhttp", + "html", + "html/atom", + "html/charset", + "http/httpguts", + "http2", + "http2/hpack", + "idna", + "internal/iana", + "internal/socket", + "internal/socks", + "internal/timeseries", + "ipv4", + "ipv6", + "proxy", + "trace", + "websocket", + ] + pruneopts = "" + revision = "a680a1efc54dd51c040b3b5ce4939ea3cf2ea0d1" + +[[projects]] + branch = "master" + digest = "1:a8944db88149e7ecbea4b760c625b9ccf455fceae21387bc8890c3589d28b623" + name = "golang.org/x/sys" + packages = [ + "unix", + "windows", + "windows/registry", + "windows/svc", + "windows/svc/debug", + "windows/svc/eventlog", + "windows/svc/mgr", + ] + pruneopts = "" + revision = "ac767d655b305d4e9612f5f6e33120b9176c4ad4" + +[[projects]] + digest = "1:af9bfca4298ef7502c52b1459df274eed401a4f5498b900e9a92d28d3d87ac5a" + name = "golang.org/x/text" + packages = [ + "collate", + "collate/build", + "encoding", + "encoding/charmap", + "encoding/htmlindex", + "encoding/internal", + "encoding/internal/identifier", + "encoding/japanese", + "encoding/korean", + "encoding/simplifiedchinese", + "encoding/traditionalchinese", + "encoding/unicode", + "internal/colltab", + "internal/gen", + "internal/tag", + "internal/triegen", + "internal/ucd", + "internal/utf8internal", + "language", + "runes", + "secure/bidirule", + "transform", + "unicode/bidi", + "unicode/cldr", + "unicode/norm", + "unicode/rangetable", + ] + pruneopts = "" + revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0" + version = "v0.3.0" + +[[projects]] + digest = "1:eede11c81b63c8f6fd06ef24ba0a640dc077196ec9b7a58ecde03c82eee2f151" + name = "google.golang.org/appengine" + packages = ["cloudsql"] + pruneopts = "" + revision = "b1f26356af11148e710935ed1ac8a7f5702c7612" + version = "v1.1.0" + +[[projects]] + branch = "master" + digest = "1:8d093c040b734e160cbe8291c7b539c36d2c6dd4581c4bb37cff56078c65bd07" + name = "google.golang.org/genproto" + packages = ["googleapis/rpc/status"] + pruneopts = "" + revision = "fedd2861243fd1a8152376292b921b394c7bef7e" + +[[projects]] + digest = "1:05f2028524c4eada11e3f46d23139f23e9e0a40b2552207a5af278e8063ce782" + name = "google.golang.org/grpc" + packages = [ + ".", + "balancer", + "balancer/base", + "balancer/roundrobin", + "codes", + "connectivity", + "credentials", + "encoding", + "encoding/proto", + "grpclog", + "internal", + "internal/backoff", + "internal/channelz", + "internal/grpcrand", + "keepalive", + "metadata", + "naming", + "peer", + "resolver", + "resolver/dns", + "resolver/passthrough", + "stats", + "status", + "tap", + "transport", + ] + pruneopts = "" + revision = "168a6198bcb0ef175f7dacec0b8691fc141dc9b8" + version = "v1.13.0" + +[[projects]] + digest = "1:2840683aa0e9980689f85bf48b2a56ec7a108fd089f12af8ea7d98c172819589" + name = "gopkg.in/alecthomas/kingpin.v2" + packages = ["."] + pruneopts = "" + revision = "947dcec5ba9c011838740e680966fd7087a71d0d" + version = "v2.2.6" + +[[projects]] + digest = "1:a8f8c1725195c4324d4350fae001524ca7489e40d9b6bb47598772e3faa103ba" + name = "gopkg.in/asn1-ber.v1" + packages = ["."] + pruneopts = "" + revision = "379148ca0225df7a432012b8df0355c2a2063ac0" + version = "v1.2" + +[[projects]] + digest = "1:581450ae66d7970d91ef9132459fa583e937c6e502f1b96e4ee7783a56fa0b44" + name = "gopkg.in/fatih/pool.v2" + packages = ["."] + pruneopts = "" + revision = "010e0b745d12eaf8426c95f9c3924d81dd0b668f" + version = "v2.0.0" + +[[projects]] + digest = "1:b2106f1668ea5efc1ecc480f7e922a093adb9563fd9ce58585292871f0d0f229" + name = "gopkg.in/fsnotify.v1" + packages = ["."] + pruneopts = "" + revision = "c2828203cd70a50dcccfb2761f8b1f8ceef9a8e9" + source = "https://github.com/fsnotify/fsnotify/archive/v1.4.7.tar.gz" + version = "v1.4.7" + +[[projects]] + digest = "1:5fa5df18f3bd9cad28ed7f263b15da217945735110898fa2b9af25cdafb9cbf3" + name = "gopkg.in/gorethink/gorethink.v3" + packages = [ + ".", + "encoding", + "ql2", + "types", + ] + pruneopts = "" + revision = "7f5bdfd858bb064d80559b2a32b86669c5de5d3b" + version = "v3.0.5" + +[[projects]] + digest = "1:74163d1887c0821951e6f1795a1d10338f45f09d9067cb4a8edcf7ee481724ee" + name = "gopkg.in/ldap.v2" + packages = ["."] + pruneopts = "" + revision = "bb7a9ca6e4fbc2129e3db588a34bc970ffe811a9" + version = "v2.5.1" + +[[projects]] + branch = "v2" + digest = "1:f799e95918890212dcf4ce5951291061d318f689977ec9cea0417b08433c2a9d" + name = "gopkg.in/mgo.v2" + packages = [ + ".", + "bson", + "internal/json", + "internal/sasl", + "internal/scram", + ] + pruneopts = "" + revision = "9856a29383ce1c59f308dd1cf0363a79b5bef6b5" + +[[projects]] + digest = "1:427414c304a47b497759094220ce42dd2e838ab7d52de197c633b800c6ff84b5" + name = "gopkg.in/olivere/elastic.v5" + packages = [ + ".", + "config", + "uritemplates", + ] + pruneopts = "" + revision = "52741dc2ce53629cbe1e673869040d886cba2cd5" + version = "v5.0.70" + +[[projects]] + branch = "v1" + digest = "1:a96d16bd088460f2e0685d46c39bcf1208ba46e0a977be2df49864ec7da447dd" + name = "gopkg.in/tomb.v1" + packages = ["."] + pruneopts = "" + revision = "dd632973f1e7218eb1089048e0798ec9ae7dceb8" + +[[projects]] + digest = "1:f0620375dd1f6251d9973b5f2596228cc8042e887cd7f827e4220bc1ce8c30e2" + name = "gopkg.in/yaml.v2" + packages = ["."] + pruneopts = "" + revision = "5420a8b6744d3b0345ab293f6fcba19c978f1183" + version = "v2.2.1" + +[solve-meta] + analyzer-name = "dep" + analyzer-version = 1 + inputs-digest = "726abf0a241126b415293c203dddc516e4d8be9b0f2913fb3ab2c4eb332e3ce2" + solver-name = "gps-cdcl" + solver-version = 1 From ca5d2d8baea227baee84222c395d4f13b2c3ddf9 Mon Sep 17 00:00:00 2001 From: prydin Date: Fri, 10 Aug 2018 12:08:08 -0400 Subject: [PATCH 084/100] Fixed failing test cases --- plugins/inputs/vsphere/vsphere_test.go | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/plugins/inputs/vsphere/vsphere_test.go b/plugins/inputs/vsphere/vsphere_test.go index 190d362ec0a1e..b05cd8f9ab115 100644 --- a/plugins/inputs/vsphere/vsphere_test.go +++ b/plugins/inputs/vsphere/vsphere_test.go @@ -101,14 +101,12 @@ var configHeader = ` func defaultVSphere() *VSphere { return &VSphere{ - GatherClusters: true, ClusterMetricInclude: []string{ "cpu.usage.*", "cpu.usagemhz.*", "mem.usage.*", "mem.active.*"}, ClusterMetricExclude: nil, - GatherHosts: true, HostMetricInclude: []string{ "cpu.ready.summation.delta.millisecond", "cpu.latency.average.rate.percent", @@ -146,7 +144,6 @@ func defaultVSphere() *VSphere { "disk.kernelReadLatency.average.absolute.millisecond", "disk.kernelWriteLatency.average.absolute.millisecond"}, HostMetricExclude: nil, - GatherVms: true, VMMetricInclude: []string{ "cpu.ready.summation.delta.millisecond", "mem.swapinRate.average.rate.kiloBytesPerSecond", @@ -165,8 +162,7 @@ func defaultVSphere() *VSphere { "mem.swapoutRate.average.rate.kiloBytesPerSecond", "virtualDisk.read.average.rate.kiloBytesPerSecond", "virtualDisk.write.average.rate.kiloBytesPerSecond"}, - VMMetricExclude: nil, - GatherDatastores: true, + VMMetricExclude: nil, DatastoreMetricInclude: []string{ "disk.used.*", "disk.provsioned.*"}, From 5bbb56771778d72671540a8c714ba473d5a92b47 Mon Sep 17 00:00:00 2001 From: prydin Date: Fri, 10 Aug 2018 12:16:56 -0400 Subject: [PATCH 085/100] Added govmomi to Gopkg.lock --- Gopkg.lock | 310 +++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 219 insertions(+), 91 deletions(-) diff --git a/Gopkg.lock b/Gopkg.lock index 4a70b057dd88c..fffbb8545cb70 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -3,14 +3,14 @@ [[projects]] branch = "master" - digest = "1:d7582b4af1b0b953ff2bb9573a50f787c7e1669cb148fb086a3d1c670a1ac955" + digest = "1:fc0802104acded1f48e4860a9f2db85b82b4a754fca9eae750ff4e8b8cdf2116" name = "code.cloudfoundry.org/clock" packages = ["."] pruneopts = "" revision = "02e53af36e6c978af692887ed449b74026d76fec" [[projects]] - digest = "1:ce7dc0f1ffcd9a2aacc50ae6d322eebff8f4faa2d6c5f445c874cd0b77a63de7" + digest = "1:ca3acef20fd660d4df327accbf3ca2df9a12213d914f3113305dcd56579324b9" name = "collectd.org" packages = [ "api", @@ -23,7 +23,7 @@ [[projects]] branch = "master" - digest = "1:c1269bfaddefd090935401c291ad5df6c03de605a440e941ecc568e19f0f9e3b" + digest = "1:298712a3ee36b59c3ca91f4183bd75d174d5eaa8b4aed5072831f126e2e752f6" name = "github.com/Microsoft/ApplicationInsights-Go" packages = [ "appinsights", @@ -33,7 +33,7 @@ revision = "d2df5d440eda5372f24fcac03839a64d6cb5f7e5" [[projects]] - digest = "1:ec6a42cd98d70f0916216d8f6df8ca61145edeaad041014aa9c003068de7364c" + digest = "1:45ec6eb579713a01991ad07f538fed3b576ee55f5ce9f248320152a9270d9258" name = "github.com/Microsoft/go-winio" packages = ["."] pruneopts = "" @@ -41,7 +41,7 @@ version = "v0.4.9" [[projects]] - digest = "1:14af5ba5ac88efec490fb59734df34e1bd973198caefa7b0cceed0900ef6164c" + digest = "1:9362b2212139b7821f73a86169bf80ce6b0264956f87d82ab3aeedb2b5c08fea" name = "github.com/Shopify/sarama" packages = ["."] pruneopts = "" @@ -57,7 +57,7 @@ version = "1.0.0" [[projects]] - digest = "1:855af787df6b733016849082d66ffda5e0e00856513fcac08a7cf199a23515c2" + digest = "1:f296e8b29c60c94efed3b8cfae08d793cb95149cdd7343e6a9834b4ac7136475" name = "github.com/aerospike/aerospike-client-go" packages = [ ".", @@ -78,7 +78,7 @@ [[projects]] branch = "master" - digest = "1:1399282ad03ac819f0e8a747c888407c5c98bb497d33821a7047c7bae667ede0" + digest = "1:a74730e052a45a3fab1d310fdef2ec17ae3d6af16228421e238320846f2aaec8" name = "github.com/alecthomas/template" packages = [ ".", @@ -97,7 +97,7 @@ [[projects]] branch = "master" - digest = "1:072692f8d76356228f31f64ca3140041a140011c7dea26e746206e8649c71b31" + digest = "1:7f21a8f175ee7f91c659f919c61032e11889fba5dc25c0cec555087cbb87435a" name = "github.com/amir/raidman" packages = [ ".", @@ -108,14 +108,14 @@ [[projects]] branch = "master" - digest = "1:83a67d925714169fa5121021abef0276605c6e4d51c467dd1f0c04344abad1ff" + digest = "1:0828d8c0f95689f832cf348fe23827feb7640cd698d612ef59e2f9d041f54c68" name = "github.com/apache/thrift" packages = ["lib/go/thrift"] pruneopts = "" revision = "f2867c24984aa53edec54a138c03db934221bdea" [[projects]] - digest = "1:ca172b51bfe0a1ae7725dc782339fed4ba697dcd44e29a0a1c765fffdbf05ddc" + digest = "1:65a05bde9b02f645c73afa61c9f6af92d94d726c81a268f45cc70218bd58de65" name = "github.com/aws/aws-sdk-go" packages = [ "aws", @@ -156,14 +156,14 @@ [[projects]] branch = "master" - digest = "1:fca298802a2ab834d6eb0e284788ae037ebc324c0f325ff92c5eea592d189cc5" + digest = "1:c0bec5f9b98d0bc872ff5e834fac186b807b656683bd29cb82fb207a1513fabb" name = "github.com/beorn7/perks" packages = ["quantile"] pruneopts = "" revision = "3a771d992973f24aa725d07868b467d1ddfceafb" [[projects]] - digest = "1:0edb96edcfeee9aeba92e605536fbb1542b0bf6a10cea9d0b5a2227d5a703eae" + digest = "1:c5978131c797af795972c27c25396c81d1bf53b7b6e8e3e0259e58375765c071" name = "github.com/bsm/sarama-cluster" packages = ["."] pruneopts = "" @@ -180,7 +180,7 @@ [[projects]] branch = "master" - digest = "1:65ae2d1625584ba8d16d1e15b25db1fc62334e2040f22dbbbdc7531c909843b2" + digest = "1:298e42868718da06fc0899ae8fdb99c48a14477045234c9274d81caa79af6a8f" name = "github.com/couchbase/go-couchbase" packages = ["."] pruneopts = "" @@ -188,7 +188,7 @@ [[projects]] branch = "master" - digest = "1:5db54de7054c072f47806c91ef7625ffa00489ca2da5fbc6ca1c78e08018f6bf" + digest = "1:c734658274a6be88870a36742fdea96a3fce4fc99a7b90946c9e84335ceae71a" name = "github.com/couchbase/gomemcached" packages = [ ".", @@ -199,7 +199,7 @@ [[projects]] branch = "master" - digest = "1:0deaa0f28c823119725c8308703f019797bc077e251d1ed3f2b8eae2cc7791d7" + digest = "1:c1195c02bc8fbf5307cfb95bc79eddaa1351ee3587cc4a7bbe6932e2fb966ff2" name = "github.com/couchbase/goutils" packages = [ "logging", @@ -209,15 +209,7 @@ revision = "e865a1461c8ac0032bd37e2d4dab3289faea3873" [[projects]] - branch = "master" - digest = "1:4c015b7445aa37becc220fde9bdbc4d4329f75af72ca1c98f9b0bd698d6068cb" - name = "github.com/crewjam/rfc5424" - packages = ["."] - pruneopts = "" - revision = "6ae4b209c3f0d5071494be6b883a1970acadda94" - -[[projects]] - digest = "1:0a39ec8bf5629610a4bc7873a92039ee509246da3cef1a0ea60f1ed7e5f9cea5" + digest = "1:56c130d885a4aacae1dd9c7b71cfe39912c7ebc1ff7d2b46083c8812996dc43b" name = "github.com/davecgh/go-spew" packages = ["spew"] pruneopts = "" @@ -225,7 +217,7 @@ version = "v1.1.0" [[projects]] - digest = "1:2426da75f49e5b8507a6ed5d4c49b06b2ff795f4aec401c106b7db8fb2625cd7" + digest = "1:6098222470fe0172157ce9bbef5d2200df4edde17ee649c5d6e48330e4afa4c6" name = "github.com/dgrijalva/jwt-go" packages = ["."] pruneopts = "" @@ -233,7 +225,7 @@ version = "v3.2.0" [[projects]] - digest = "1:68df19ee476d93359596377b7437bbe49d233fe014becd060ded757aeed531cd" + digest = "1:522eff2a1f014a64fb403db60fc0110653e4dc5b59779894d208e697b0708ddc" name = "github.com/docker/distribution" packages = [ "digestset", @@ -243,7 +235,7 @@ revision = "edc3ab29cdff8694dd6feb85cfeb4b5f1b38ed9c" [[projects]] - digest = "1:a21509491bfd5bd1f99abe1d38430fddd16c8c8dc0092f954e224b93ad87f06b" + digest = "1:d149605f1b00713fdc48150122892d77d49d30c825f690dd92f497aeb6cf18f5" name = "github.com/docker/docker" packages = [ "api", @@ -268,7 +260,7 @@ revision = "ed7b6428c133e7c59404251a09b7d6b02fa83cc2" [[projects]] - digest = "1:5b20afc76a36d3994194e2612e83b51bc2b12db3d4d2a722b24474b2d0e3a890" + digest = "1:a5ecc2e70260a87aa263811281465a5effcfae8a54bac319cee87c4625f04d63" name = "github.com/docker/go-connections" packages = [ "nat", @@ -288,7 +280,7 @@ version = "v0.3.3" [[projects]] - digest = "1:7bbb118aeef9a6b9fef3d57b6cc5378f7cd6e915cabf4dea695e318e1a1bd4e6" + digest = "1:6d6672f85a84411509885eaa32f597577873de00e30729b9bb0eb1e1faa49c12" name = "github.com/eapache/go-resiliency" packages = ["breaker"] pruneopts = "" @@ -297,7 +289,7 @@ [[projects]] branch = "master" - digest = "1:7b28f7f7c9fb914b30dff111fb910d49bd61d275101f665aea79409bb3ba2ae2" + digest = "1:7b12ea8b50040c6c2378ec5b5a1ab722730b2bfb46e8724ded57f2c3905431fa" name = "github.com/eapache/go-xerial-snappy" packages = ["."] pruneopts = "" @@ -312,7 +304,7 @@ version = "v1.1.0" [[projects]] - digest = "1:d2e2aebcb8e8027345e16f9d0be8cdee3bb470ba406c7a54cb7457ae3ad4ace5" + digest = "1:3fa846cb3feb4e65371fe3c347c299de9b5bc3e71e256c0d940cd19b767a6ba0" name = "github.com/eclipse/paho.mqtt.golang" packages = [ ".", @@ -323,7 +315,7 @@ version = "v1.1.1" [[projects]] - digest = "1:d19c78214e03e297e9e30d2eb11892f731358b2951f2a5c7374658a156373e4c" + digest = "1:858b7fe7b0f4bc7ef9953926828f2816ea52d01a88d72d1c45bc8c108f23c356" name = "github.com/go-ini/ini" packages = ["."] pruneopts = "" @@ -339,7 +331,7 @@ version = "v0.3.0" [[projects]] - digest = "1:c3a5ae14424a38c244439732c31a08b5f956c46c4acdc159fc285a52dbf11de0" + digest = "1:96c4a6ff4206086347bfe28e96e092642882128f45ecb8dc8f15f3e6f6703af0" name = "github.com/go-ole/go-ole" packages = [ ".", @@ -350,7 +342,7 @@ version = "v1.2.1" [[projects]] - digest = "1:f2f6a616a1ca8aed667d956c98f7f6178efe72bbe0a419bd33b9d99841c7de69" + digest = "1:3dfd659219b6f63dc0677a62b8d4e8f10b5cf53900aef40858db10a19407e41d" name = "github.com/go-redis/redis" packages = [ ".", @@ -367,7 +359,7 @@ version = "v6.12.0" [[projects]] - digest = "1:dc876ae7727280d95f97af5320308131278b93d6c6f5cf953065e18cb8c88fd2" + digest = "1:c07de423ca37dc2765396d6971599ab652a339538084b9b58c9f7fc533b28525" name = "github.com/go-sql-driver/mysql" packages = ["."] pruneopts = "" @@ -375,7 +367,7 @@ version = "v1.4.0" [[projects]] - digest = "1:b7a7e17513aeee6492d93015c7bf29c86a0c1c91210ea56b21e36c1a40958cba" + digest = "1:9ab1b1c637d7c8f49e39d8538a650d7eb2137b076790cff69d160823b505964c" name = "github.com/gobwas/glob" packages = [ ".", @@ -392,7 +384,7 @@ version = "v0.2.3" [[projects]] - digest = "1:673df1d02ca0c6f51458fe94bbb6fae0b05e54084a31db2288f1c4321255c2da" + digest = "1:6e73003ecd35f4487a5e88270d3ca0a81bc80dc88053ac7e4dcfec5fba30d918" name = "github.com/gogo/protobuf" packages = ["proto"] pruneopts = "" @@ -400,7 +392,7 @@ version = "v1.1.1" [[projects]] - digest = "1:b1d3041d568e065ab4d76f7477844458e9209c0bb241eaccdc0770bf0a13b120" + digest = "1:f958a1c137db276e52f0b50efee41a1a389dcdded59a69711f3e872757dab34b" name = "github.com/golang/protobuf" packages = [ "proto", @@ -415,14 +407,14 @@ [[projects]] branch = "master" - digest = "1:075128b9fc42e6d99067da1a2e6c0a634a6043b5a60abe6909c51f5ecad37b6d" + digest = "1:2a5888946cdbc8aa360fd43301f9fc7869d663f60d5eedae7d4e6e5e4f06f2bf" name = "github.com/golang/snappy" packages = ["."] pruneopts = "" revision = "2e65f85255dbc3072edf28d6b5b8efc472979f5a" [[projects]] - digest = "1:cc082d7b9cc3f832f2aed9d06d1cbb33b6984a61d8ec403535b086415c181607" + digest = "1:f9f45f75f332e03fc7e9fe9188ea4e1ce4d14779ef34fa1b023da67518e36327" name = "github.com/google/go-cmp" packages = [ "cmp", @@ -434,6 +426,14 @@ revision = "3af367b6b30c263d47e8895973edcca9a49cf029" version = "v0.2.0" +[[projects]] + digest = "1:c1d7e883c50a26ea34019320d8ae40fad86c9e5d56e63a1ba2cb618cef43e986" + name = "github.com/google/uuid" + packages = ["."] + pruneopts = "" + revision = "064e2069ce9c359c118179501254f67d7d37ba24" + version = "0.2" + [[projects]] digest = "1:dbbeb8ddb0be949954c8157ee8439c2adfd8dc1c9510eb44a6e58cb68c3dce28" name = "github.com/gorilla/context" @@ -459,7 +459,7 @@ revision = "e80d13ce29ede4452c43dea11e79b9bc8a15b478" [[projects]] - digest = "1:db58383b43f583c44fb47c3331de943a11bb73ea951c2def55d29a454a57f4ee" + digest = "1:e7224669901bab4094e6d6697c136557b7177db6ceb01b7fc8b20d08f4b5aacd" name = "github.com/hashicorp/consul" packages = ["api"] pruneopts = "" @@ -476,14 +476,14 @@ [[projects]] branch = "master" - digest = "1:cd5813053beac0114f96a7da3924fc8a15e0cd2b139f079e0fcce5d3244ae304" + digest = "1:ff65bf6fc4d1116f94ac305342725c21b55c16819c2606adc8f527755716937f" name = "github.com/hashicorp/go-rootcerts" packages = ["."] pruneopts = "" revision = "6bb64b370b90e7ef1fa532be9e591a81c3493e00" [[projects]] - digest = "1:d2b2cff454cb23a9769ef3c9075741f5985773a998584b3b3ce203fe4b1abbea" + digest = "1:f72168ea995f398bab88e84bd1ff58a983466ba162fb8d50d47420666cd57fad" name = "github.com/hashicorp/serf" packages = ["coordinate"] pruneopts = "" @@ -491,7 +491,7 @@ version = "v0.8.1" [[projects]] - digest = "1:cc0cf2e12280074e5c6dc0f15a4bb3d6c43509e6091cdcdcc83eea491577257b" + digest = "1:a39ef049cdeee03a57b132e7d60e32711b9d949c78458da78e702d9864c54369" name = "github.com/influxdata/go-syslog" packages = [ "rfc5424", @@ -503,7 +503,7 @@ [[projects]] branch = "master" - digest = "1:effc58ad45323ad15159bbca533be4870eaddb2d9a513d3488d8bfe822c83532" + digest = "1:bc3eb5ddfd59781ea1183f2b3d1eb105a1495d421f09b2ccd360c7fced0b612d" name = "github.com/influxdata/tail" packages = [ ".", @@ -517,7 +517,7 @@ [[projects]] branch = "master" - digest = "1:d31edcf33a3b36218de96e43f3fec18ea96deb2a28b838a3a01a4df856ded345" + digest = "1:7fb6cc9607eaa6ef309edebc42b57f704244bd4b9ab23bff128829c4ad09b95d" name = "github.com/influxdata/toml" packages = [ ".", @@ -535,7 +535,7 @@ revision = "7c63b0a71ef8300adc255344d275e10e5c3a71ec" [[projects]] - digest = "1:4197871f269749786aa2406557dba15f10cf79161cdc3998180614c62c8b6351" + digest = "1:2de1791b9e43f26c696e36950e42676565e7da7499a870bc02213da4b59b1d14" name = "github.com/jackc/pgx" packages = [ ".", @@ -551,7 +551,7 @@ version = "v3.1.0" [[projects]] - digest = "1:4f767a115bc8e08576f6d38ab73c376fc1b1cd3bb5041171c9e8668cc7739b52" + digest = "1:6f49eae0c1e5dab1dafafee34b207aeb7a42303105960944828c2079b92fc88e" name = "github.com/jmespath/go-jmespath" packages = ["."] pruneopts = "" @@ -567,7 +567,7 @@ [[projects]] branch = "master" - digest = "1:2df59f23f11c5c59982f737c98c5523b276bfc85a4773a04b411190402bb30fd" + digest = "1:fed90fa725d3b1bac0a760de64426834dfef4546474cf182f2ec94285afa74a8" name = "github.com/kardianos/service" packages = ["."] pruneopts = "" @@ -591,7 +591,7 @@ [[projects]] branch = "master" - digest = "1:28ca57775f285ae87cbdc7280aad91c5f2ed3c2af98d9f035d75956d1ca97fe6" + digest = "1:7e9956922e349af0190afa0b6621befcd201072679d8e51a9047ff149f2afe93" name = "github.com/mailru/easyjson" packages = [ ".", @@ -603,7 +603,7 @@ revision = "efc7eb8984d6655c26b5c9d2e65c024e5767c37c" [[projects]] - digest = "1:49a8b01a6cd6558d504b65608214ca40a78000e1b343ed0da5c6a9ccd83d6d30" + digest = "1:63722a4b1e1717be7b98fc686e0b30d5e7f734b9e93d7dee86293b6deab7ea28" name = "github.com/matttproud/golang_protobuf_extensions" packages = ["pbutil"] pruneopts = "" @@ -611,7 +611,7 @@ version = "v1.0.1" [[projects]] - digest = "1:f0bad0fece0fb73c6ea249c18d8e80ffbe86be0457715b04463068f04686cf39" + digest = "1:4c8d8358c45ba11ab7bb15df749d4df8664ff1582daead28bae58cf8cbe49890" name = "github.com/miekg/dns" packages = ["."] pruneopts = "" @@ -651,7 +651,7 @@ version = "v0.1.0" [[projects]] - digest = "1:e5894541d6ceec5dd283e24e3530aadf59c06449695d19189a7a27bb4c15840d" + digest = "1:e5ec850ce66beb0014fc40d8e64b7482172eee71d86d734d66def5e9eac16797" name = "github.com/nats-io/gnatsd" packages = [ "conf", @@ -665,7 +665,7 @@ version = "v1.2.0" [[projects]] - digest = "1:88f1bde4c172e27b05ed46adfbd0e79dc1663a6281e4b39fa3e39d71ead9621d" + digest = "1:665af347df4c5d1ae4c3eacd0754f5337a301f6a3f2444c9993b996605c8c02b" name = "github.com/nats-io/go-nats" packages = [ ".", @@ -685,7 +685,7 @@ version = "v1.0.0" [[projects]] - digest = "1:501cce26a54c785458b0dd54a08ddd984d4ad0c198255430d5d37cd2efe23149" + digest = "1:7a69f6a3a33929f8b66aa39c93868ad1698f06417fe627ae067559beb94504bd" name = "github.com/nsqio/go-nsq" packages = ["."] pruneopts = "" @@ -701,7 +701,7 @@ version = "v1.0.0-rc1" [[projects]] - digest = "1:0d08f7224705b1df80beee92ffbdc63ab13fd6f6eb80bf287735f9bc7e8b83eb" + digest = "1:f26c8670b11e29a49c8e45f7ec7f2d5bac62e8fd4e3c0ae1662baa4a697f984a" name = "github.com/opencontainers/image-spec" packages = [ "specs-go", @@ -720,7 +720,7 @@ revision = "a52f2342449246d5bcc273e65cbdcfa5f7d6c63c" [[projects]] - digest = "1:bba12aa4747b212f75db3e7fee73fe1b66d303cb3ff0c1984b7f2ad20e8bd2bc" + digest = "1:78fb99d6011c2ae6c72f3293a83951311147b12b06a5ffa43abf750c4fab6ac5" name = "github.com/opentracing/opentracing-go" packages = [ ".", @@ -732,7 +732,7 @@ version = "v1.0.2" [[projects]] - digest = "1:c6c0db6294924072f98a0de090d200bae4b7102b12a443ba9569c4ba7df52aa1" + digest = "1:fea0e67285d900e5a0a7ec19ff4b4c82865a28dddbee8454c5360ad908f7069c" name = "github.com/openzipkin/zipkin-go-opentracing" packages = [ ".", @@ -747,7 +747,7 @@ version = "v0.3.4" [[projects]] - digest = "1:41de12a4684237dd55a11260c941c2c58a055951985e9473ba1661175a13fea7" + digest = "1:29e34e58f26655c4d73135cdfc0517ea2ff1483eff34e5d5ef4b6fddbb81e31b" name = "github.com/pierrec/lz4" packages = [ ".", @@ -774,7 +774,7 @@ version = "v1.0.0" [[projects]] - digest = "1:981835985f655d1d380cc6aa7d9fa9ad7abfaf40c75da200fd40d864cd05a7c3" + digest = "1:4142d94383572e74b42352273652c62afec5b23f325222ed09198f46009022d1" name = "github.com/prometheus/client_golang" packages = [ "prometheus", @@ -786,7 +786,7 @@ [[projects]] branch = "master" - digest = "1:562d53e436b244a9bb5c1ff43bcaf4882e007575d34ec37717b15751c65cc63a" + digest = "1:185cf55b1f44a1bf243558901c3f06efa5c64ba62cfdcbb1bf7bbe8c3fb68561" name = "github.com/prometheus/client_model" packages = ["go"] pruneopts = "" @@ -794,7 +794,7 @@ [[projects]] branch = "master" - digest = "1:6a8420870eb2935977da1fff0f3afca9bdb3f1e66258c9e91a8a7ce0b5417c3b" + digest = "1:bfbc121ef802d245ef67421cff206615357d9202337a3d492b8f668906b485a8" name = "github.com/prometheus/common" packages = [ "expfmt", @@ -807,7 +807,7 @@ [[projects]] branch = "master" - digest = "1:00fca823dfcdd8107226f67215afd948b001525223ed955a05b33a4c885c9591" + digest = "1:b694a6bdecdace488f507cff872b30f6f490fdaf988abd74d87ea56406b23b6e" name = "github.com/prometheus/procfs" packages = [ ".", @@ -820,7 +820,7 @@ [[projects]] branch = "master" - digest = "1:1b65925989a4dfb6d98ef1d530cda33ab1ff25945b14a22a8b8bb27cc282af70" + digest = "1:15bcdc717654ef21128e8af3a63eec39a6d08a830e297f93d65163f87c8eb523" name = "github.com/rcrowley/go-metrics" packages = ["."] pruneopts = "" @@ -828,7 +828,7 @@ [[projects]] branch = "master" - digest = "1:d8fe9f454582e04b5693b59cdebe3f0bd9dc29ad9651bfb1633cba4658b66c65" + digest = "1:7fc2f428767a2521abc63f1a663d981f61610524275d6c0ea645defadd4e916f" name = "github.com/samuel/go-zookeeper" packages = ["zk"] pruneopts = "" @@ -843,7 +843,7 @@ version = "v1.2.0" [[projects]] - digest = "1:987ce58e999676c2e209831390f2d56621ff98def2ecca4928e73fe1e2569954" + digest = "1:fce9909f20bc6a6363a6d589e478bdcf8111044b41566d37d7552bf92d955540" name = "github.com/shirou/gopsutil" packages = [ "cpu", @@ -868,7 +868,7 @@ revision = "bb4de0191aa41b5507caa14b0650cdbddcd9280b" [[projects]] - digest = "1:f2cc92b78b2f3b76ab0f9daddddd28627bcfcc6cacf119029aa3850082d95079" + digest = "1:8cf46b6c18a91068d446e26b67512cf16f1540b45d90b28b9533706a127f0ca6" name = "github.com/sirupsen/logrus" packages = ["."] pruneopts = "" @@ -877,7 +877,7 @@ [[projects]] branch = "master" - digest = "1:79e73b87cb07e380d1a3aaa14fbcc418e0d42eede5f971e7ee2f4a6e6d531deb" + digest = "1:4b0cabe65ca903a7b2a3e6272c5304eb788ce196d35ecb901c6563e5e7582443" name = "github.com/soniah/gosnmp" packages = ["."] pruneopts = "" @@ -885,14 +885,14 @@ [[projects]] branch = "master" - digest = "1:0a1f8d01a0191f558910bcbfd7e1dc11a53ac374473d13b68b8fe520f21efb07" + digest = "1:4e8f1cae8e6d83af9000d82566efb8823907dae77ba4f1d76ff28fdd197c3c90" name = "github.com/streadway/amqp" packages = ["."] pruneopts = "" revision = "e5adc2ada8b8efff032bf61173a233d143e9318e" [[projects]] - digest = "1:34062a2274daa6ec4d2f50d6070cc51cf4674d6d553ed76b406cb3425b9528e8" + digest = "1:711eebe744c0151a9d09af2315f0bb729b2ec7637ef4c410fa90a18ef74b65b6" name = "github.com/stretchr/objx" packages = ["."] pruneopts = "" @@ -900,7 +900,7 @@ version = "v0.1.1" [[projects]] - digest = "1:bc2a12c8863e1080226b7bc69192efd6c37aaa9b85cec508b0a8f54fabb9bd9f" + digest = "1:c587772fb8ad29ad4db67575dad25ba17a51f072ff18a22b4f0257a4d9c24f75" name = "github.com/stretchr/testify" packages = [ "assert", @@ -928,16 +928,46 @@ revision = "1731857f09b1f38450e2c12409748407822dc6be" [[projects]] - digest = "1:23e2b9f3a20cd4a6427147377255ec2f6237e8606fa6ef0707ed79b7bfbe3a83" + digest = "1:343f20460c11a0d0529fe532553bfef9446918d1a1fda6d8661eb27d5b1a68b8" name = "github.com/vjeantet/grok" packages = ["."] pruneopts = "" revision = "ce01e59abcf6fbc9833b7deb5e4b8ee1769bcc53" version = "v1.0.0" +[[projects]] + digest = "1:f9fe29bf856d49f9a51d6001588cb5ee5d65c8a7ff5e8b0dd5423c3a510f0833" + name = "github.com/vmware/govmomi" + packages = [ + ".", + "find", + "list", + "nfc", + "object", + "performance", + "property", + "session", + "simulator", + "simulator/esx", + "simulator/vpx", + "task", + "view", + "vim25", + "vim25/debug", + "vim25/methods", + "vim25/mo", + "vim25/progress", + "vim25/soap", + "vim25/types", + "vim25/xml", + ] + pruneopts = "" + revision = "e3a01f9611c32b2362366434bcd671516e78955d" + version = "v0.18.0" + [[projects]] branch = "master" - digest = "1:5383edd40c7f6c95a7dc46a47bf0c83de4bf40a4252f12fa803f790037addffc" + digest = "1:98ed05e9796df287b90c1d96854e3913c8e349dbc546412d3cabb472ecf4b417" name = "github.com/wvanbergen/kafka" packages = ["consumergroup"] pruneopts = "" @@ -945,7 +975,7 @@ [[projects]] branch = "master" - digest = "1:f936b4936e1b092cc41c9b33fdc990ad78386545f1ffeca8427c72b2605bca85" + digest = "1:12aff3cc417907bf9f683a6bf1dc78ffb08e41bc69f829491e593ea9b951a3cf" name = "github.com/wvanbergen/kazoo-go" packages = ["."] pruneopts = "" @@ -953,7 +983,7 @@ [[projects]] branch = "master" - digest = "1:9946d558a909f63e31332c77b82649522da97ae7f7cfbfebc6f53549ab6b3e0f" + digest = "1:c5918689b7e187382cc1066bf0260de54ba9d1b323105f46ed2551d2fb4a17c7" name = "github.com/yuin/gopher-lua" packages = [ ".", @@ -966,7 +996,7 @@ [[projects]] branch = "master" - digest = "1:84e9087a94f336c204887281046891769d2ed7bf1d2b31c21ff6fb5e1743abce" + digest = "1:2fcfc6c3fb8dfe0d80d7789272230d3ac7db15022b66817113f98d9fff880225" name = "github.com/zensqlmonitor/go-mssqldb" packages = ["."] pruneopts = "" @@ -974,7 +1004,7 @@ [[projects]] branch = "master" - digest = "1:21100b2e8b6922303dd109da81b3134ed0eff05cb3402881eabde9cce8f4e5e6" + digest = "1:0773b5c3be42874166670a20aa177872edb450cd9fc70b1df97303d977702a50" name = "golang.org/x/crypto" packages = [ "bcrypt", @@ -990,7 +1020,7 @@ [[projects]] branch = "master" - digest = "1:58d8f8f3ad415b10d2145316519e5b7995b7cf9e663b33a1e9e0c2ddd96c1d58" + digest = "1:00ff990baae4665bb0a8174af5ff78228574227ed96c89671247a56852a50e21" name = "golang.org/x/net" packages = [ "bpf", @@ -1018,7 +1048,7 @@ [[projects]] branch = "master" - digest = "1:a8944db88149e7ecbea4b760c625b9ccf455fceae21387bc8890c3589d28b623" + digest = "1:677e38cad6833ad266ec843739d167755eda1e6f2d8af1c63102b0426ad820db" name = "golang.org/x/sys" packages = [ "unix", @@ -1033,7 +1063,7 @@ revision = "ac767d655b305d4e9612f5f6e33120b9176c4ad4" [[projects]] - digest = "1:af9bfca4298ef7502c52b1459df274eed401a4f5498b900e9a92d28d3d87ac5a" + digest = "1:5acd3512b047305d49e8763eef7ba423901e85d5dd2fd1e71778a0ea8de10bd4" name = "golang.org/x/text" packages = [ "collate", @@ -1068,7 +1098,7 @@ version = "v0.3.0" [[projects]] - digest = "1:eede11c81b63c8f6fd06ef24ba0a640dc077196ec9b7a58ecde03c82eee2f151" + digest = "1:c1771ca6060335f9768dff6558108bc5ef6c58506821ad43377ee23ff059e472" name = "google.golang.org/appengine" packages = ["cloudsql"] pruneopts = "" @@ -1077,14 +1107,14 @@ [[projects]] branch = "master" - digest = "1:8d093c040b734e160cbe8291c7b539c36d2c6dd4581c4bb37cff56078c65bd07" + digest = "1:b1443b4e3cc990c84d27fcdece9d3302158c67dba870e33a6937a2c0076388c2" name = "google.golang.org/genproto" packages = ["googleapis/rpc/status"] pruneopts = "" revision = "fedd2861243fd1a8152376292b921b394c7bef7e" [[projects]] - digest = "1:05f2028524c4eada11e3f46d23139f23e9e0a40b2552207a5af278e8063ce782" + digest = "1:5f31b45ee9da7a87f140bef3ed0a7ca34ea2a6d38eb888123b8e28170e8aa4f2" name = "google.golang.org/grpc" packages = [ ".", @@ -1118,7 +1148,7 @@ version = "v1.13.0" [[projects]] - digest = "1:2840683aa0e9980689f85bf48b2a56ec7a108fd089f12af8ea7d98c172819589" + digest = "1:15d017551627c8bb091bde628215b2861bed128855343fdd570c62d08871f6e1" name = "gopkg.in/alecthomas/kingpin.v2" packages = ["."] pruneopts = "" @@ -1126,7 +1156,7 @@ version = "v2.2.6" [[projects]] - digest = "1:a8f8c1725195c4324d4350fae001524ca7489e40d9b6bb47598772e3faa103ba" + digest = "1:3cad99e0d1f94b8c162787c12e59d0a0b9df1ef75590eb145cdd625479091efe" name = "gopkg.in/asn1-ber.v1" packages = ["."] pruneopts = "" @@ -1142,7 +1172,7 @@ version = "v2.0.0" [[projects]] - digest = "1:b2106f1668ea5efc1ecc480f7e922a093adb9563fd9ce58585292871f0d0f229" + digest = "1:eb53021a8aa3f599d29c7102e65026242bdedce998a54837dc67f14b6a97c5fd" name = "gopkg.in/fsnotify.v1" packages = ["."] pruneopts = "" @@ -1151,7 +1181,7 @@ version = "v1.4.7" [[projects]] - digest = "1:5fa5df18f3bd9cad28ed7f263b15da217945735110898fa2b9af25cdafb9cbf3" + digest = "1:960720207d3d0992995f4576e1366fd9e9b1483473b07fb7243144f75f5b1546" name = "gopkg.in/gorethink/gorethink.v3" packages = [ ".", @@ -1164,7 +1194,7 @@ version = "v3.0.5" [[projects]] - digest = "1:74163d1887c0821951e6f1795a1d10338f45f09d9067cb4a8edcf7ee481724ee" + digest = "1:367baf06b7dbd0ef0bbdd785f6a79f929c96b0c18e9d3b29c0eed1ac3f5db133" name = "gopkg.in/ldap.v2" packages = ["."] pruneopts = "" @@ -1173,7 +1203,7 @@ [[projects]] branch = "v2" - digest = "1:f799e95918890212dcf4ce5951291061d318f689977ec9cea0417b08433c2a9d" + digest = "1:f54ba71a035aac92ced3e902d2bff3734a15d1891daff73ec0f90ef236750139" name = "gopkg.in/mgo.v2" packages = [ ".", @@ -1186,7 +1216,7 @@ revision = "9856a29383ce1c59f308dd1cf0363a79b5bef6b5" [[projects]] - digest = "1:427414c304a47b497759094220ce42dd2e838ab7d52de197c633b800c6ff84b5" + digest = "1:b49c4d3115800eace659c9a6a5c384a922f5b210178b24a01abb10731f404ea2" name = "gopkg.in/olivere/elastic.v5" packages = [ ".", @@ -1216,6 +1246,104 @@ [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "726abf0a241126b415293c203dddc516e4d8be9b0f2913fb3ab2c4eb332e3ce2" + input-imports = [ + "collectd.org/api", + "collectd.org/network", + "github.com/Microsoft/ApplicationInsights-Go/appinsights", + "github.com/Shopify/sarama", + "github.com/StackExchange/wmi", + "github.com/aerospike/aerospike-client-go", + "github.com/amir/raidman", + "github.com/apache/thrift/lib/go/thrift", + "github.com/aws/aws-sdk-go/aws", + "github.com/aws/aws-sdk-go/aws/client", + "github.com/aws/aws-sdk-go/aws/credentials", + "github.com/aws/aws-sdk-go/aws/credentials/stscreds", + "github.com/aws/aws-sdk-go/aws/session", + "github.com/aws/aws-sdk-go/service/cloudwatch", + "github.com/aws/aws-sdk-go/service/kinesis", + "github.com/aws/aws-sdk-go/service/sts", + "github.com/bsm/sarama-cluster", + "github.com/couchbase/go-couchbase", + "github.com/dgrijalva/jwt-go", + "github.com/docker/docker/api/types", + "github.com/docker/docker/api/types/container", + "github.com/docker/docker/api/types/filters", + "github.com/docker/docker/api/types/registry", + "github.com/docker/docker/api/types/swarm", + "github.com/docker/docker/client", + "github.com/eclipse/paho.mqtt.golang", + "github.com/go-redis/redis", + "github.com/go-sql-driver/mysql", + "github.com/gobwas/glob", + "github.com/golang/protobuf/proto", + "github.com/google/go-cmp/cmp", + "github.com/gorilla/mux", + "github.com/hashicorp/consul/api", + "github.com/influxdata/go-syslog/rfc5424", + "github.com/influxdata/go-syslog/rfc5425", + "github.com/influxdata/tail", + "github.com/influxdata/toml", + "github.com/influxdata/toml/ast", + "github.com/influxdata/wlog", + "github.com/jackc/pgx", + "github.com/jackc/pgx/pgtype", + "github.com/jackc/pgx/stdlib", + "github.com/kardianos/service", + "github.com/kballard/go-shellquote", + "github.com/matttproud/golang_protobuf_extensions/pbutil", + "github.com/miekg/dns", + "github.com/multiplay/go-ts3", + "github.com/nats-io/gnatsd/server", + "github.com/nats-io/go-nats", + "github.com/nsqio/go-nsq", + "github.com/openzipkin/zipkin-go-opentracing", + "github.com/openzipkin/zipkin-go-opentracing/thrift/gen-go/zipkincore", + "github.com/prometheus/client_golang/prometheus", + "github.com/prometheus/client_golang/prometheus/promhttp", + "github.com/prometheus/client_model/go", + "github.com/prometheus/common/expfmt", + "github.com/prometheus/common/log", + "github.com/satori/go.uuid", + "github.com/shirou/gopsutil/cpu", + "github.com/shirou/gopsutil/disk", + "github.com/shirou/gopsutil/host", + "github.com/shirou/gopsutil/load", + "github.com/shirou/gopsutil/mem", + "github.com/shirou/gopsutil/net", + "github.com/shirou/gopsutil/process", + "github.com/soniah/gosnmp", + "github.com/streadway/amqp", + "github.com/stretchr/testify/assert", + "github.com/stretchr/testify/mock", + "github.com/stretchr/testify/require", + "github.com/tidwall/gjson", + "github.com/vjeantet/grok", + "github.com/vmware/govmomi", + "github.com/vmware/govmomi/performance", + "github.com/vmware/govmomi/simulator", + "github.com/vmware/govmomi/view", + "github.com/vmware/govmomi/vim25/mo", + "github.com/vmware/govmomi/vim25/soap", + "github.com/vmware/govmomi/vim25/types", + "github.com/wvanbergen/kafka/consumergroup", + "github.com/zensqlmonitor/go-mssqldb", + "golang.org/x/net/context", + "golang.org/x/net/html/charset", + "golang.org/x/sys/unix", + "golang.org/x/sys/windows", + "golang.org/x/sys/windows/svc", + "golang.org/x/sys/windows/svc/mgr", + "google.golang.org/grpc", + "google.golang.org/grpc/codes", + "google.golang.org/grpc/credentials", + "google.golang.org/grpc/status", + "gopkg.in/gorethink/gorethink.v3", + "gopkg.in/ldap.v2", + "gopkg.in/mgo.v2", + "gopkg.in/mgo.v2/bson", + "gopkg.in/olivere/elastic.v5", + "gopkg.in/yaml.v2", + ] solver-name = "gps-cdcl" solver-version = 1 From 573f325fa845005490e446d0f3d172d39a3c3dc7 Mon Sep 17 00:00:00 2001 From: prydin Date: Fri, 10 Aug 2018 13:19:47 -0400 Subject: [PATCH 086/100] Properly initialize plugin in test cases --- plugins/inputs/vsphere/vsphere_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/inputs/vsphere/vsphere_test.go b/plugins/inputs/vsphere/vsphere_test.go index b05cd8f9ab115..a42638fe27942 100644 --- a/plugins/inputs/vsphere/vsphere_test.go +++ b/plugins/inputs/vsphere/vsphere_test.go @@ -258,5 +258,7 @@ func TestAll(t *testing.T) { var acc testutil.Accumulator v := defaultVSphere() v.Vcenters = []string{s.URL.String()} + v.Start(nil) // We're not using the Accumulator, so it can be nil. + defer v.Stop() require.NoError(t, v.Gather(&acc)) } From 9539c3bf25ed0c56bf1adc0ab76e399984222e62 Mon Sep 17 00:00:00 2001 From: prydin Date: Tue, 14 Aug 2018 15:20:07 -0400 Subject: [PATCH 087/100] Fixed broken support for certs and CA chains --- plugins/inputs/vsphere/README.MD | 8 ----- plugins/inputs/vsphere/client.go | 45 +++++++++++++++++++++++----- plugins/inputs/vsphere/endpoint.go | 2 +- plugins/inputs/vsphere/workerpool.go | 2 ++ 4 files changed, 41 insertions(+), 16 deletions(-) diff --git a/plugins/inputs/vsphere/README.MD b/plugins/inputs/vsphere/README.MD index a9a60dafaf3da..13a1031b3b9ae 100644 --- a/plugins/inputs/vsphere/README.MD +++ b/plugins/inputs/vsphere/README.MD @@ -20,8 +20,6 @@ The VMware vSphere plugin uses the vSphere API to gather metrics from multiple v ############### VMs ############### -# gather_vms = true ### (default=true) - ## Typical VM metrics (if omitted, all metrics are collected) # vm_metric_include = [ # "cpu.ready.summation", @@ -46,8 +44,6 @@ The VMware vSphere plugin uses the vSphere API to gather metrics from multiple v ############### Hosts ############### -# gather_hosts = true ## (default=true) - ## Typical host metrics (if omitted, all metrics are collected) # host_metric_include = [ # "cpu.ready.summation", @@ -90,8 +86,6 @@ The VMware vSphere plugin uses the vSphere API to gather metrics from multiple v ############### Clusters ############### -# gather_clusters = true ## (default=true) - ## Typical cluster metrics (if omitted, all metrics are collected) #cluster_metric_include = [ # "cpu.usage.*", @@ -103,8 +97,6 @@ The VMware vSphere plugin uses the vSphere API to gather metrics from multiple v ############### Datastores ############### -# gather_datastores = true ## (default=true) - ## Typical datastore metrics (if omitted, all metrics are collected) ## ## WARNING: It is not recommended to include all metrics. Your collection will probably time out if you do! diff --git a/plugins/inputs/vsphere/client.go b/plugins/inputs/vsphere/client.go index c7beefada034f..02ea10b3197b9 100644 --- a/plugins/inputs/vsphere/client.go +++ b/plugins/inputs/vsphere/client.go @@ -7,7 +7,10 @@ import ( "github.com/vmware/govmomi" "github.com/vmware/govmomi/performance" + "github.com/vmware/govmomi/session" "github.com/vmware/govmomi/view" + "github.com/vmware/govmomi/vim25" + "github.com/vmware/govmomi/vim25/soap" ) // Client represents a connection to vSphere and is backed by a govmoni connection @@ -22,26 +25,54 @@ type Client struct { // NewClient creates a new vSphere client based on the url and setting passed as parameters. func NewClient(u *url.URL, vs *VSphere) (*Client, error) { sw := NewStopwatch("connect", u.Host) - tlsCfg, err := vs.TLSConfig() + tlsCfg, err := vs.ClientConfig.TLSConfig() if err != nil { return nil, err } if vs.Username != "" { - log.Printf("D! [input.vsphere]: Logging in using explicit credentials: %s", vs.Username) u.User = url.UserPassword(vs.Username, vs.Password) } ctx := context.Background() log.Printf("D! [input.vsphere]: Creating client: %s", u.Host) - c, err := govmomi.NewClient(ctx, u, vs.InsecureSkipVerify) + soapClient := soap.NewClient(u, tlsCfg.InsecureSkipVerify) + + // Add certificate if we have it. Use it to log us in. + if tlsCfg != nil && len(tlsCfg.Certificates) > 0 { + soapClient.SetCertificate(tlsCfg.Certificates[0]) + } + + // Set up custom CA chain if specified. We need to do this before we create the vim25 client, + // since it might fail on missing CA chains otherwise. + if vs.TLSCA != "" { + if err := soapClient.SetRootCAs(vs.TLSCA); err != nil { + return nil, err + } + } + + vimClient, err := vim25.NewClient(ctx, soapClient) if err != nil { return nil, err } + sm := session.NewManager(vimClient) - // Add certificate if we have it - // - if tlsCfg != nil && len(tlsCfg.Certificates) > 0 { - c.Client.SetCertificate(tlsCfg.Certificates[0]) + // If TSLKey is specified, try to log in as an extension using a cert. + if vs.TLSKey != "" { + sm.LoginExtensionByCertificate(ctx, vs.TLSKey) + } + + // Create the govmomi client. + c := &govmomi.Client{ + Client: vimClient, + SessionManager: sm, + } + + // Only login if the URL contains user information. + if u.User != nil { + err = c.Login(ctx, u.User) + if err != nil { + return nil, err + } } c.Timeout = vs.Timeout.Duration diff --git a/plugins/inputs/vsphere/endpoint.go b/plugins/inputs/vsphere/endpoint.go index ffbf93260df9d..89b1fbbd7da37 100644 --- a/plugins/inputs/vsphere/endpoint.go +++ b/plugins/inputs/vsphere/endpoint.go @@ -510,7 +510,7 @@ func (e *Endpoint) collectResource(ctx context.Context, resourceType string, acc if hasLatest { elapsed := time.Now().Sub(latest).Seconds() + 5.0 // Allow 5 second jitter. log.Printf("D! [input.vsphere]: Latest: %s, elapsed: %f, resource: %s", latest, elapsed, resourceType) - if elapsed < float64(res.sampling) { + if !res.realTime && elapsed < float64(res.sampling) { // No new data would be available. We're outta herE! [input.vsphere]: log.Printf("D! [input.vsphere]: Sampling period for %s of %d has not elapsed for %s", resourceType, res.sampling, e.URL.Host) diff --git a/plugins/inputs/vsphere/workerpool.go b/plugins/inputs/vsphere/workerpool.go index 75140c3e8153c..828c46c19677f 100644 --- a/plugins/inputs/vsphere/workerpool.go +++ b/plugins/inputs/vsphere/workerpool.go @@ -12,6 +12,8 @@ import ( // does some processing and returns a single result. type WorkerFunc func(context.Context, interface{}) interface{} +// PushFunc is called from a FillerFunc to push a workitem onto +// the input channel. Wraps some logic for gracefulk shutdowns. type PushFunc func(context.Context, interface{}) bool // DrainerFunc represents a function used to "drain" the WorkerPool, From 84e570079cf82eed848bb2ca8ed84f13d6aa48d6 Mon Sep 17 00:00:00 2001 From: prydin Date: Wed, 15 Aug 2018 12:13:22 -0400 Subject: [PATCH 088/100] More efficient handling of vSphere clients --- plugins/inputs/vsphere/client.go | 123 +++++++++++++++++++++++++---- plugins/inputs/vsphere/endpoint.go | 58 +++++++------- plugins/inputs/vsphere/vsphere.go | 4 + 3 files changed, 142 insertions(+), 43 deletions(-) diff --git a/plugins/inputs/vsphere/client.go b/plugins/inputs/vsphere/client.go index 02ea10b3197b9..d47696b3f70b8 100644 --- a/plugins/inputs/vsphere/client.go +++ b/plugins/inputs/vsphere/client.go @@ -4,6 +4,8 @@ import ( "context" "log" "net/url" + "sync" + "time" "github.com/vmware/govmomi" "github.com/vmware/govmomi/performance" @@ -13,13 +15,75 @@ import ( "github.com/vmware/govmomi/vim25/soap" ) +// ClientFactory is used to obtain Clients to be used throughout the plugin. Typically, +// a single Client is reused across all functions and goroutines, but the client +// is periodically recycled to avoid authentication expiration issues. +type ClientFactory struct { + client *Client + mux sync.Mutex + url *url.URL + parent *VSphere + recycler *time.Ticker +} + // Client represents a connection to vSphere and is backed by a govmoni connection type Client struct { - Client *govmomi.Client - Views *view.Manager - Root *view.ContainerView - Perf *performance.Manager - Valid bool + Client *govmomi.Client + Views *view.Manager + Root *view.ContainerView + Perf *performance.Manager + Valid bool + refcount int32 + mux sync.Mutex + idle *sync.Cond +} + +// NewClientFactory creates a new ClientFactory and prepares it for use. +func NewClientFactory(ctx context.Context, url *url.URL, parent *VSphere) *ClientFactory { + cf := &ClientFactory{ + client: nil, + parent: parent, + url: url, + recycler: time.NewTicker(30 * time.Minute), + } + + // Perdiodically recycle clients to make sure they don't expire + go func() { + for { + select { + case <-cf.recycler.C: + cf.destroyCurrent() + case <-ctx.Done(): + cf.destroyCurrent() // Kill the current connection when we're done. + return + } + } + }() + return cf +} + +// GetClient returns a client. The caller is responsible for calling Release() +// on the client once it's done using it. +func (cf *ClientFactory) GetClient() (*Client, error) { + cf.mux.Lock() + defer cf.mux.Unlock() + if cf.client == nil { + var err error + if cf.client, err = NewClient(cf.url, cf.parent); err != nil { + return nil, err + } + } + cf.client.grab() + return cf.client, nil +} + +func (cf *ClientFactory) destroyCurrent() { + cf.mux.Lock() + defer cf.mux.Unlock() + go func(c *Client) { + c.closeWhenIdle() + }(cf.client) + cf.client = nil } // NewClient creates a new vSphere client based on the url and setting passed as parameters. @@ -87,17 +151,19 @@ func NewClient(u *url.URL, vs *VSphere) (*Client, error) { sw.Stop() - return &Client{ - Client: c, - Views: m, - Root: v, - Perf: p, - Valid: true, - }, nil + result := &Client{ + Client: c, + Views: m, + Root: v, + Perf: p, + Valid: true, + refcount: 0, + } + result.idle = sync.NewCond(&result.mux) + return result, nil } -// Close disconnects a client from the vSphere backend and releases all assiciated resources. -func (c *Client) Close() { +func (c *Client) close() { ctx := context.Background() if c.Views != nil { c.Views.Destroy(ctx) @@ -107,3 +173,32 @@ func (c *Client) Close() { c.Client.Logout(ctx) } } + +func (c *Client) closeWhenIdle() { + c.mux.Lock() + defer c.mux.Unlock() + log.Printf("D! [input.vsphere]: Waiting to close connection") + for c.refcount > 0 { + c.idle.Wait() + } + log.Printf("[input.vsphere]: Closing connection") + c.close() +} + +// Release indicates that a caller is no longer using the client and it can +// be recycled if needed. +func (c *Client) Release() { + c.mux.Lock() + defer c.mux.Unlock() + if c.refcount--; c.refcount == 0 { + c.idle.Broadcast() + } + //log.Printf("D! [input.vsphere]: Release. Connection refcount:%d", c.refcount) +} + +func (c *Client) grab() { + c.mux.Lock() + defer c.mux.Unlock() + c.refcount++ + //log.Printf("D! [input.vsphere]: Grab. Connection refcount:%d", c.refcount) +} diff --git a/plugins/inputs/vsphere/endpoint.go b/plugins/inputs/vsphere/endpoint.go index 89b1fbbd7da37..8032888195bd0 100644 --- a/plugins/inputs/vsphere/endpoint.go +++ b/plugins/inputs/vsphere/endpoint.go @@ -32,8 +32,7 @@ type Endpoint struct { discoveryTicker *time.Ticker collectMux sync.RWMutex initialized bool - collectClient *Client - discoverClient *Client + clientFactory *ClientFactory wg *ConcurrentWaitGroup } @@ -84,12 +83,13 @@ type metricQResponse struct { // as parameters. func NewEndpoint(parent *VSphere, url *url.URL) *Endpoint { e := Endpoint{ - URL: url, - Parent: parent, - lastColls: make(map[string]time.Time), - instanceInfo: make(map[string]resourceInfo), - initialized: false, - wg: NewConcurrentWaitGroup(), + URL: url, + Parent: parent, + lastColls: make(map[string]time.Time), + instanceInfo: make(map[string]resourceInfo), + initialized: false, + wg: NewConcurrentWaitGroup(), + clientFactory: NewClientFactory(parent.rootCtx, url, parent), } e.resourceKinds = map[string]resourceKind{ @@ -214,11 +214,11 @@ func (e *Endpoint) init(ctx context.Context) error { } func (e *Endpoint) setupMetricIds(ctx context.Context) error { - client, err := NewClient(e.URL, e.Parent) + client, err := e.clientFactory.GetClient() if err != nil { return err } - defer client.Close() + defer client.Release() mn, err := client.Perf.CounterInfoByName(ctx) @@ -233,9 +233,14 @@ func (e *Endpoint) setupMetricIds(ctx context.Context) error { } func (e *Endpoint) getMetadata(ctx context.Context, in interface{}) interface{} { + client, err := e.clientFactory.GetClient() + if err != nil { + return err + } + defer client.Release() + rq := in.(*metricQRequest) - //log.Printf("D! [input.vsphere]: Querying metadata for %s", rq.obj.name) - metrics, err := e.discoverClient.Perf.AvailableMetric(ctx, rq.obj.ref.Reference(), rq.res.sampling) + metrics, err := client.Perf.AvailableMetric(ctx, rq.obj.ref.Reference(), rq.res.sampling) if err != nil && err != context.Canceled { log.Printf("E! [input.vsphere]: Error while getting metric metadata. Discovery will be incomplete. Error: %s", err) } @@ -253,14 +258,12 @@ func (e *Endpoint) discover(ctx context.Context) error { sw := NewStopwatch("discover", e.URL.Host) var err error - e.discoverClient, err = NewClient(e.URL, e.Parent) + + client, err := e.clientFactory.GetClient() if err != nil { return err } - defer func() { - e.discoverClient.Close() - e.discoverClient = nil - }() + defer client.Release() log.Printf("D! [input.vsphere]: Discover new objects for %s", e.URL.Host) @@ -271,7 +274,7 @@ func (e *Endpoint) discover(ctx context.Context) error { for k, res := range e.resourceKinds { // Need to do this for all resource types even if they are not enabled (but datastore) if res.enabled || (k != "datastore" && k != "vm") { - objects, err := res.getObjects(ctx, e.discoverClient.Root) + objects, err := res.getObjects(ctx, client.Root) if err != nil { return err } @@ -406,15 +409,6 @@ func (e *Endpoint) collect(ctx context.Context, acc telegraf.Accumulator) error e.collectMux.RLock() defer e.collectMux.RUnlock() - e.collectClient, err = NewClient(e.URL, e.Parent) - if err != nil { - return err - } - defer func() { - e.collectClient.Close() - e.collectClient = nil - }() - // If discovery interval is disabled (0), discover on each collection cycle // if e.Parent.ObjectDiscoveryInterval.Duration.Seconds() == 0 { @@ -569,12 +563,18 @@ func (e *Endpoint) collectChunk(ctx context.Context, pqs []types.PerfQuerySpec, count := 0 prefix := "vsphere" + e.Parent.Separator + resourceType - metrics, err := e.collectClient.Perf.Query(ctx, pqs) + client, err := e.clientFactory.GetClient() + if err != nil { + return 0, err + } + defer client.Release() + + metrics, err := client.Perf.Query(ctx, pqs) if err != nil { return count, err } - ems, err := e.collectClient.Perf.ToMetricSeries(ctx, metrics) + ems, err := client.Perf.ToMetricSeries(ctx, metrics) if err != nil { return count, err } diff --git a/plugins/inputs/vsphere/vsphere.go b/plugins/inputs/vsphere/vsphere.go index 7257772a30dd6..ab70ddc57ff45 100644 --- a/plugins/inputs/vsphere/vsphere.go +++ b/plugins/inputs/vsphere/vsphere.go @@ -207,6 +207,8 @@ func (v *VSphere) checkEndpoints() error { return nil } +// Start is called from telegraf core when a plugin is started and allows it to +// perform initialization tasks. func (v *VSphere) Start(acc telegraf.Accumulator) error { log.Println("D! [input.vsphere]: Starting plugin") rootCtx, cancel := context.WithCancel(context.Background()) @@ -215,6 +217,8 @@ func (v *VSphere) Start(acc telegraf.Accumulator) error { return nil } +// Stop is called from telegraf core when a plugin is stopped and allows it to +// perform shutdown tasks. func (v *VSphere) Stop() { log.Println("D! [input.vsphere]: Stopping plugin") v.cancel() From 82875f8f1b33babc95c98d0b2d00739341076689 Mon Sep 17 00:00:00 2001 From: prydin Date: Wed, 15 Aug 2018 12:36:34 -0400 Subject: [PATCH 089/100] Prevent possible race at shutdown --- plugins/inputs/vsphere/client.go | 38 ++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/plugins/inputs/vsphere/client.go b/plugins/inputs/vsphere/client.go index d47696b3f70b8..abc91357ffe3c 100644 --- a/plugins/inputs/vsphere/client.go +++ b/plugins/inputs/vsphere/client.go @@ -28,14 +28,15 @@ type ClientFactory struct { // Client represents a connection to vSphere and is backed by a govmoni connection type Client struct { - Client *govmomi.Client - Views *view.Manager - Root *view.ContainerView - Perf *performance.Manager - Valid bool - refcount int32 - mux sync.Mutex - idle *sync.Cond + Client *govmomi.Client + Views *view.Manager + Root *view.ContainerView + Perf *performance.Manager + Valid bool + refcount int32 + mux sync.Mutex + idle *sync.Cond + closeGate sync.Once } // NewClientFactory creates a new ClientFactory and prepares it for use. @@ -164,14 +165,19 @@ func NewClient(u *url.URL, vs *VSphere) (*Client, error) { } func (c *Client) close() { - ctx := context.Background() - if c.Views != nil { - c.Views.Destroy(ctx) - } - if c.Client != nil { - c.Client.Logout(ctx) - } + // Use a Once to prevent us from panics stemming from trying + // to close it multiple times. + c.closeGate.Do(func() { + ctx := context.Background() + if c.Views != nil { + c.Views.Destroy(ctx) + + } + if c.Client != nil { + c.Client.Logout(ctx) + } + }) } func (c *Client) closeWhenIdle() { @@ -181,7 +187,7 @@ func (c *Client) closeWhenIdle() { for c.refcount > 0 { c.idle.Wait() } - log.Printf("[input.vsphere]: Closing connection") + log.Printf("D! [input.vsphere]: Closing connection") c.close() } From afe61250c6fb0e4db1e96ff5e665dad3db651e7a Mon Sep 17 00:00:00 2001 From: prydin Date: Thu, 16 Aug 2018 15:56:41 -0400 Subject: [PATCH 090/100] Simplfied connection handling. Use 'source' as primary key --- plugins/inputs/vsphere/client.go | 113 +++++++++-------------------- plugins/inputs/vsphere/endpoint.go | 26 ++++--- plugins/inputs/vsphere/vsphere.go | 3 +- 3 files changed, 52 insertions(+), 90 deletions(-) diff --git a/plugins/inputs/vsphere/client.go b/plugins/inputs/vsphere/client.go index abc91357ffe3c..09d24004dc634 100644 --- a/plugins/inputs/vsphere/client.go +++ b/plugins/inputs/vsphere/client.go @@ -5,13 +5,13 @@ import ( "log" "net/url" "sync" - "time" "github.com/vmware/govmomi" "github.com/vmware/govmomi/performance" "github.com/vmware/govmomi/session" "github.com/vmware/govmomi/view" "github.com/vmware/govmomi/vim25" + "github.com/vmware/govmomi/vim25/methods" "github.com/vmware/govmomi/vim25/soap" ) @@ -19,11 +19,10 @@ import ( // a single Client is reused across all functions and goroutines, but the client // is periodically recycled to avoid authentication expiration issues. type ClientFactory struct { - client *Client - mux sync.Mutex - url *url.URL - parent *VSphere - recycler *time.Ticker + client *Client + mux sync.Mutex + url *url.URL + parent *VSphere } // Client represents a connection to vSphere and is backed by a govmoni connection @@ -33,39 +32,21 @@ type Client struct { Root *view.ContainerView Perf *performance.Manager Valid bool - refcount int32 - mux sync.Mutex - idle *sync.Cond closeGate sync.Once } // NewClientFactory creates a new ClientFactory and prepares it for use. func NewClientFactory(ctx context.Context, url *url.URL, parent *VSphere) *ClientFactory { - cf := &ClientFactory{ - client: nil, - parent: parent, - url: url, - recycler: time.NewTicker(30 * time.Minute), + return &ClientFactory{ + client: nil, + parent: parent, + url: url, } - - // Perdiodically recycle clients to make sure they don't expire - go func() { - for { - select { - case <-cf.recycler.C: - cf.destroyCurrent() - case <-ctx.Done(): - cf.destroyCurrent() // Kill the current connection when we're done. - return - } - } - }() - return cf } // GetClient returns a client. The caller is responsible for calling Release() // on the client once it's done using it. -func (cf *ClientFactory) GetClient() (*Client, error) { +func (cf *ClientFactory) GetClient(ctx context.Context) (*Client, error) { cf.mux.Lock() defer cf.mux.Unlock() if cf.client == nil { @@ -74,17 +55,18 @@ func (cf *ClientFactory) GetClient() (*Client, error) { return nil, err } } - cf.client.grab() - return cf.client, nil -} -func (cf *ClientFactory) destroyCurrent() { - cf.mux.Lock() - defer cf.mux.Unlock() - go func(c *Client) { - c.closeWhenIdle() - }(cf.client) - cf.client = nil + // Execute a dummy call against the server to make sure the client is + // still functional. If not, try to log back in. If that doesn't work, + // we give up. + if _, err := methods.GetCurrentTime(ctx, cf.client.Client); err != nil { + log.Printf("I! [input.vsphere]: Client session seems to have time out. Reauthenticating!") + if cf.client.Client.SessionManager.Login(ctx, url.UserPassword(cf.parent.Username, cf.parent.Password)) != nil { + return nil, err + } + } + + return cf.client, nil } // NewClient creates a new vSphere client based on the url and setting passed as parameters. @@ -152,16 +134,22 @@ func NewClient(u *url.URL, vs *VSphere) (*Client, error) { sw.Stop() - result := &Client{ - Client: c, - Views: m, - Root: v, - Perf: p, - Valid: true, - refcount: 0, + return &Client{ + Client: c, + Views: m, + Root: v, + Perf: p, + Valid: true, + }, nil +} + +// Close shuts down a ClientFactory and releases any resources associated with it. +func (cf *ClientFactory) Close() { + cf.mux.Lock() + defer cf.mux.Unlock() + if cf.client != nil { + cf.client.close() } - result.idle = sync.NewCond(&result.mux) - return result, nil } func (c *Client) close() { @@ -179,32 +167,3 @@ func (c *Client) close() { } }) } - -func (c *Client) closeWhenIdle() { - c.mux.Lock() - defer c.mux.Unlock() - log.Printf("D! [input.vsphere]: Waiting to close connection") - for c.refcount > 0 { - c.idle.Wait() - } - log.Printf("D! [input.vsphere]: Closing connection") - c.close() -} - -// Release indicates that a caller is no longer using the client and it can -// be recycled if needed. -func (c *Client) Release() { - c.mux.Lock() - defer c.mux.Unlock() - if c.refcount--; c.refcount == 0 { - c.idle.Broadcast() - } - //log.Printf("D! [input.vsphere]: Release. Connection refcount:%d", c.refcount) -} - -func (c *Client) grab() { - c.mux.Lock() - defer c.mux.Unlock() - c.refcount++ - //log.Printf("D! [input.vsphere]: Grab. Connection refcount:%d", c.refcount) -} diff --git a/plugins/inputs/vsphere/endpoint.go b/plugins/inputs/vsphere/endpoint.go index 8032888195bd0..b8d32fceabc68 100644 --- a/plugins/inputs/vsphere/endpoint.go +++ b/plugins/inputs/vsphere/endpoint.go @@ -214,11 +214,10 @@ func (e *Endpoint) init(ctx context.Context) error { } func (e *Endpoint) setupMetricIds(ctx context.Context) error { - client, err := e.clientFactory.GetClient() + client, err := e.clientFactory.GetClient(ctx) if err != nil { return err } - defer client.Release() mn, err := client.Perf.CounterInfoByName(ctx) @@ -233,11 +232,10 @@ func (e *Endpoint) setupMetricIds(ctx context.Context) error { } func (e *Endpoint) getMetadata(ctx context.Context, in interface{}) interface{} { - client, err := e.clientFactory.GetClient() + client, err := e.clientFactory.GetClient(ctx) if err != nil { return err } - defer client.Release() rq := in.(*metricQRequest) metrics, err := client.Perf.AvailableMetric(ctx, rq.obj.ref.Reference(), rq.res.sampling) @@ -259,11 +257,10 @@ func (e *Endpoint) discover(ctx context.Context) error { sw := NewStopwatch("discover", e.URL.Host) var err error - client, err := e.clientFactory.GetClient() + client, err := e.clientFactory.GetClient(ctx) if err != nil { return err } - defer client.Release() log.Printf("D! [input.vsphere]: Discover new objects for %s", e.URL.Host) @@ -389,7 +386,13 @@ func getDatastores(ctx context.Context, root *view.ContainerView) (objectMap, er return m, nil } -func (e *Endpoint) collect(ctx context.Context, acc telegraf.Accumulator) error { +// Close shuts down an Endpoint and releases any resources associated with it. +func (e *Endpoint) Close() { + e.clientFactory.Close() +} + +// Collect runs a round of data collections as specified in the configuration. +func (e *Endpoint) Collect(ctx context.Context, acc telegraf.Accumulator) error { // Add returning false means we've been released from Wait and no // more tasks are allowed. This happens when the plugin is stopped // or reloaded. @@ -563,11 +566,10 @@ func (e *Endpoint) collectChunk(ctx context.Context, pqs []types.PerfQuerySpec, count := 0 prefix := "vsphere" + e.Parent.Separator + resourceType - client, err := e.clientFactory.GetClient() + client, err := e.clientFactory.GetClient(ctx) if err != nil { return 0, err } - defer client.Release() metrics, err := client.Perf.Query(ctx, pqs) if err != nil { @@ -591,9 +593,9 @@ func (e *Endpoint) collectChunk(ctx context.Context, pqs []types.PerfQuerySpec, for _, v := range em.Value { name := v.Name t := map[string]string{ - "vcenter": e.URL.Host, - "hostname": instInfo.name, - "moid": moid, + "vcenter": e.URL.Host, + "source": instInfo.name, + "moid": moid, } // Populate tags diff --git a/plugins/inputs/vsphere/vsphere.go b/plugins/inputs/vsphere/vsphere.go index ab70ddc57ff45..d211ae016b90e 100644 --- a/plugins/inputs/vsphere/vsphere.go +++ b/plugins/inputs/vsphere/vsphere.go @@ -227,6 +227,7 @@ func (v *VSphere) Stop() { for _, ep := range v.endpoints { log.Printf("D! [input.vsphere]: Waiting for endpoint %s to finish", ep.URL.Host) ep.wg.Wait() + ep.Close() } } @@ -244,7 +245,7 @@ func (v *VSphere) Gather(acc telegraf.Accumulator) error { wg.Add(1) go func(endpoint *Endpoint) { defer wg.Done() - err := endpoint.collect(v.rootCtx, acc) + err := endpoint.Collect(v.rootCtx, acc) if err == context.Canceled { // No need to signal errors if we were merely canceled. From 54a7f4d6f99014fb5d52098003874a2e8fb905d8 Mon Sep 17 00:00:00 2001 From: prydin Date: Sat, 18 Aug 2018 12:09:27 -0400 Subject: [PATCH 091/100] Resolved potential race condition --- plugins/inputs/vsphere/conc_waitgroup.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/plugins/inputs/vsphere/conc_waitgroup.go b/plugins/inputs/vsphere/conc_waitgroup.go index 89c093ec72d79..9ff0a38c192f7 100644 --- a/plugins/inputs/vsphere/conc_waitgroup.go +++ b/plugins/inputs/vsphere/conc_waitgroup.go @@ -29,11 +29,12 @@ func NewConcurrentWaitGroup() *ConcurrentWaitGroup { // Add signals the beginning of one or more jobs. The function returns false // if a Wait() has already been unblocked and callers should not run the job. func (c *ConcurrentWaitGroup) Add(inc int) bool { + c.mux.Lock() + defer c.mux.Unlock() + if c.done { return false } - c.mux.Lock() - defer c.mux.Unlock() c.jobs += inc if c.jobs == 0 { c.cond.Broadcast() From fafe93d864440bbff37e7d67e7cbdacd83425c5b Mon Sep 17 00:00:00 2001 From: prydin Date: Tue, 21 Aug 2018 09:18:44 -0400 Subject: [PATCH 092/100] Added clarifying comment to ConcurrentWaitGroup.Wait(). Added clarifying comment to README --- plugins/inputs/vsphere/README.MD | 3 +++ plugins/inputs/vsphere/conc_waitgroup.go | 5 ++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/plugins/inputs/vsphere/README.MD b/plugins/inputs/vsphere/README.MD index 13a1031b3b9ae..d6ce6133cbd8e 100644 --- a/plugins/inputs/vsphere/README.MD +++ b/plugins/inputs/vsphere/README.MD @@ -9,6 +9,9 @@ The VMware vSphere plugin uses the vSphere API to gather metrics from multiple v ## Configuration +NOTE: To disable collection av a specific resource type, simply exclude all metrics using the XX_metric_exclude. For example, to disable collection of VMs, add this: +```vm_metric_exclude = [ "*" ]``` + ``` # Read metrics from one or many vCenters [[inputs.vsphere]] diff --git a/plugins/inputs/vsphere/conc_waitgroup.go b/plugins/inputs/vsphere/conc_waitgroup.go index 9ff0a38c192f7..2d66291642dae 100644 --- a/plugins/inputs/vsphere/conc_waitgroup.go +++ b/plugins/inputs/vsphere/conc_waitgroup.go @@ -48,7 +48,10 @@ func (c *ConcurrentWaitGroup) Done() { c.Add(-1) } -// Wait blocks until the number of running jobs reaches zero. +// Wait blocks until the number of running jobs reaches zero. Since the number +// of jobs dropping to zero during a Wait() will prevent new jobs from being +// started, this should only be called when it's desired to end all jobs, +// e.g. after some worker pool construct has been shut down. func (c *ConcurrentWaitGroup) Wait() { c.mux.Lock() defer c.mux.Unlock() From 457f686ae08e514775c6226130424eb74eed2557 Mon Sep 17 00:00:00 2001 From: prydin Date: Wed, 29 Aug 2018 14:22:47 -0400 Subject: [PATCH 093/100] Changes from second review --- plugins/inputs/vsphere/COUNTERS.MD | 283 +++++++++++++++++++++++ plugins/inputs/vsphere/README.MD | 125 +++++----- plugins/inputs/vsphere/conc_waitgroup.go | 62 ----- plugins/inputs/vsphere/endpoint.go | 142 ++++++------ plugins/inputs/vsphere/selfhealth.go | 10 +- plugins/inputs/vsphere/vsphere.go | 191 +++++++-------- plugins/inputs/vsphere/vsphere_test.go | 25 +- plugins/inputs/vsphere/workerpool.go | 9 +- 8 files changed, 517 insertions(+), 330 deletions(-) create mode 100644 plugins/inputs/vsphere/COUNTERS.MD delete mode 100644 plugins/inputs/vsphere/conc_waitgroup.go diff --git a/plugins/inputs/vsphere/COUNTERS.MD b/plugins/inputs/vsphere/COUNTERS.MD new file mode 100644 index 0000000000000..6b494c3c02269 --- /dev/null +++ b/plugins/inputs/vsphere/COUNTERS.MD @@ -0,0 +1,283 @@ +# Common vSphere Performance Counters +The set of performance counters in vSphere is open ended. Counters may be added or removed in new releases and the set of available counters may vary depending hardware, as well as what plugins and add-on products are installed. Therefore, providing a definitive list of available metrics is difficult. The metrics listed below are the most +commonly available as of vSphere 6.5. + +To list the exact set in your environment, please use the govc tool available [here](https://github.com/vmware/govmomi/tree/master/govc) + +To obtain the set of counters for e.g. a VM, you may use the following command: +``` +govc metric.ls vm/* +``` + +## Virtual Machine Counters +``` +cpu.demandEntitlementRatio.latest +cpu.usage.average +cpu.ready.summation +cpu.run.summation +cpu.system.summation +cpu.swapwait.summation +cpu.costop.summation +cpu.demand.average +cpu.readiness.average +cpu.maxlimited.summation +cpu.wait.summation +cpu.usagemhz.average +cpu.latency.average +cpu.used.summation +cpu.overlap.summation +cpu.idle.summation +cpu.entitlement.latest +datastore.maxTotalLatency.latest +disk.usage.average +disk.read.average +disk.write.average +disk.maxTotalLatency.latest +mem.llSwapUsed.average +mem.swapin.average +mem.vmmemctltarget.average +mem.activewrite.average +mem.overhead.average +mem.vmmemctl.average +mem.zero.average +mem.swapoutRate.average +mem.active.average +mem.llSwapOutRate.average +mem.swapout.average +mem.llSwapInRate.average +mem.swapinRate.average +mem.granted.average +mem.latency.average +mem.overheadMax.average +mem.swapped.average +mem.compressionRate.average +mem.swaptarget.average +mem.shared.average +mem.zipSaved.latest +mem.overheadTouched.average +mem.zipped.latest +mem.consumed.average +mem.entitlement.average +mem.usage.average +mem.decompressionRate.average +mem.compressed.average +net.multicastRx.summation +net.transmitted.average +net.received.average +net.usage.average +net.broadcastTx.summation +net.broadcastRx.summation +net.packetsRx.summation +net.pnicBytesRx.average +net.multicastTx.summation +net.bytesTx.average +net.bytesRx.average +net.droppedRx.summation +net.pnicBytesTx.average +net.droppedTx.summation +net.packetsTx.summation +power.power.average +power.energy.summation +rescpu.runpk1.latest +rescpu.runpk15.latest +rescpu.maxLimited5.latest +rescpu.actpk5.latest +rescpu.samplePeriod.latest +rescpu.runav1.latest +rescpu.runav15.latest +rescpu.sampleCount.latest +rescpu.actpk1.latest +rescpu.runpk5.latest +rescpu.runav5.latest +rescpu.actav15.latest +rescpu.actav1.latest +rescpu.actpk15.latest +rescpu.actav5.latest +rescpu.maxLimited1.latest +rescpu.maxLimited15.latest +sys.osUptime.latest +sys.uptime.latest +sys.heartbeat.latest +virtualDisk.write.average +virtualDisk.read.average +``` + +## Host System Counters +``` +cpu.corecount.contention.average +cpu.usage.average +cpu.reservedCapacity.average +cpu.usagemhz.minimum +cpu.usagemhz.maximum +cpu.usage.minimum +cpu.usage.maximum +cpu.capacity.provisioned.average +cpu.capacity.usage.average +cpu.capacity.demand.average +cpu.capacity.contention.average +cpu.corecount.provisioned.average +cpu.corecount.usage.average +cpu.usagemhz.average +disk.throughput.contention.average +disk.throughput.usage.average +mem.decompressionRate.average +mem.granted.average +mem.active.average +mem.shared.average +mem.zero.average +mem.swapused.average +mem.vmmemctl.average +mem.compressed.average +mem.compressionRate.average +mem.reservedCapacity.average +mem.capacity.provisioned.average +mem.capacity.usable.average +mem.capacity.usage.average +mem.capacity.entitlement.average +mem.capacity.contention.average +mem.usage.minimum +mem.overhead.minimum +mem.consumed.minimum +mem.granted.minimum +mem.active.minimum +mem.shared.minimum +mem.zero.minimum +mem.swapused.minimum +mem.consumed.average +mem.usage.maximum +mem.overhead.maximum +mem.consumed.maximum +mem.granted.maximum +mem.overhead.average +mem.shared.maximum +mem.zero.maximum +mem.swapused.maximum +mem.vmmemctl.maximum +mem.usage.average +mem.active.maximum +mem.vmmemctl.minimum +net.throughput.contention.summation +net.throughput.usage.average +net.throughput.usable.average +net.throughput.provisioned.average +power.power.average +power.powerCap.average +power.energy.summation +vmop.numShutdownGuest.latest +vmop.numPoweroff.latest +vmop.numSuspend.latest +vmop.numReset.latest +vmop.numRebootGuest.latest +vmop.numStandbyGuest.latest +vmop.numPoweron.latest +vmop.numCreate.latest +vmop.numDestroy.latest +vmop.numRegister.latest +vmop.numUnregister.latest +vmop.numReconfigure.latest +vmop.numClone.latest +vmop.numDeploy.latest +vmop.numChangeHost.latest +vmop.numChangeDS.latest +vmop.numChangeHostDS.latest +vmop.numVMotion.latest +vmop.numSVMotion.latest +vmop.numXVMotion.latest +``` + +## Cluster Counters +``` +cpu.corecount.contention.average +cpu.usage.average +cpu.reservedCapacity.average +cpu.usagemhz.minimum +cpu.usagemhz.maximum +cpu.usage.minimum +cpu.usage.maximum +cpu.capacity.provisioned.average +cpu.capacity.usage.average +cpu.capacity.demand.average +cpu.capacity.contention.average +cpu.corecount.provisioned.average +cpu.corecount.usage.average +cpu.usagemhz.average +disk.throughput.contention.average +disk.throughput.usage.average +mem.decompressionRate.average +mem.granted.average +mem.active.average +mem.shared.average +mem.zero.average +mem.swapused.average +mem.vmmemctl.average +mem.compressed.average +mem.compressionRate.average +mem.reservedCapacity.average +mem.capacity.provisioned.average +mem.capacity.usable.average +mem.capacity.usage.average +mem.capacity.entitlement.average +mem.capacity.contention.average +mem.usage.minimum +mem.overhead.minimum +mem.consumed.minimum +mem.granted.minimum +mem.active.minimum +mem.shared.minimum +mem.zero.minimum +mem.swapused.minimum +mem.consumed.average +mem.usage.maximum +mem.overhead.maximum +mem.consumed.maximum +mem.granted.maximum +mem.overhead.average +mem.shared.maximum +mem.zero.maximum +mem.swapused.maximum +mem.vmmemctl.maximum +mem.usage.average +mem.active.maximum +mem.vmmemctl.minimum +net.throughput.contention.summation +net.throughput.usage.average +net.throughput.usable.average +net.throughput.provisioned.average +power.power.average +power.powerCap.average +power.energy.summation +vmop.numShutdownGuest.latest +vmop.numPoweroff.latest +vmop.numSuspend.latest +vmop.numReset.latest +vmop.numRebootGuest.latest +vmop.numStandbyGuest.latest +vmop.numPoweron.latest +vmop.numCreate.latest +vmop.numDestroy.latest +vmop.numRegister.latest +vmop.numUnregister.latest +vmop.numReconfigure.latest +vmop.numClone.latest +vmop.numDeploy.latest +vmop.numChangeHost.latest +vmop.numChangeDS.latest +vmop.numChangeHostDS.latest +vmop.numVMotion.latest +vmop.numSVMotion.latest +vmop.numXVMotion.latest + +## Datastore Counters +datastore.numberReadAveraged.average +datastore.throughput.contention.average +datastore.throughput.usage.average +datastore.write.average +datastore.read.average +datastore.numberWriteAveraged.average +disk.used.latest +disk.provisioned.latest +disk.capacity.latest +disk.capacity.contention.average +disk.capacity.provisioned.average +disk.capacity.usage.average +``` \ No newline at end of file diff --git a/plugins/inputs/vsphere/README.MD b/plugins/inputs/vsphere/README.MD index d6ce6133cbd8e..aabcadf62b6ab 100644 --- a/plugins/inputs/vsphere/README.MD +++ b/plugins/inputs/vsphere/README.MD @@ -21,74 +21,71 @@ NOTE: To disable collection av a specific resource type, simply exclude all metr # username = "user@corp.local" # password = "secret" -############### VMs ############### - +## VM counter configuration ## Typical VM metrics (if omitted, all metrics are collected) # vm_metric_include = [ -# "cpu.ready.summation", -# "mem.swapinRate.average", -# "virtualDisk.numberReadAveraged.average", -# "virtualDisk.numberWriteAveraged.average", -# "virtualDisk.totalReadLatency.average", -# "virtualDisk.totalWriteLatency.average", -# "virtualDisk.readOIO.latest", -# "virtualDisk.writeOIO.latest", -# "net.bytesRx.average", -# "net.bytesTx.average", -# "net.droppedRx.summation.", -# "net.droppedTx.summation.", -# "cpu.run.summation", -# "cpu.used.summation", -# "mem.swapoutRate.average", -# "virtualDisk.read.average", -# "virtualDisk.write.average" ] +# "cpu.ready.summation", +# "mem.swapinRate.average", +# "virtualDisk.numberReadAveraged.average", +# "virtualDisk.numberWriteAveraged.average", +# "virtualDisk.totalReadLatency.average", +# "virtualDisk.totalWriteLatency.average", +# "virtualDisk.readOIO.latest", +# "virtualDisk.writeOIO.latest", +# "net.bytesRx.average", +# "net.bytesTx.average", +# "net.droppedRx.summation.", +# "net.droppedTx.summation.", +# "cpu.run.summation", +# "cpu.used.summation", +# "mem.swapoutRate.average", +# "virtualDisk.read.average", +# "virtualDisk.write.average" ] # vm_metric_exclude = [] ## Nothing is excluded by default -############### Hosts ############### - +## Host counter configuration ## Typical host metrics (if omitted, all metrics are collected) # host_metric_include = [ -# "cpu.ready.summation", -# "cpu.latency.average", -# "cpu.coreUtilization.average", -# "mem.usage.average", -# "mem.swapinRate.average", -# "mem.state.latest", -# "mem.latency.average", -# "mem.vmmemctl.average", -# "disk.read.average", -# "disk.write.average", -# "disk.numberReadAveraged.average", -# "disk.numberWriteAveraged.average", -# "disk.deviceReadLatency.average", -# "disk.deviceWriteLatency.average", -# "disk.totalReadLatency.average", -# "disk.totalWriteLatency.average", -# "storageAdapter.read.average", -# "storageAdapter.write.average", -# "storageAdapter.numberReadAveraged.average", -# "storageAdapter.numberWriteAveraged.average", -# "net.errorsRx.summation", -# "net.errorsTx.summation", -# "net.bytesRx.average", -# "net.bytesTx.average", -# "cpu.used.summation", -# "cpu.usage.average", -# "cpu.utilization.average", -# "cpu.wait.summation.", -# "cpu.idle.summation", -# "cpu.readiness.average", -# "cpu.costop.summation", -# "cpu.swapwait.summation", -# "mem.swapoutRate.average", -# "disk.kernelReadLatency.average", -# "disk.kernelWriteLatency.average" ] +# "cpu.ready.summation", +# "cpu.latency.average", +# "cpu.coreUtilization.average", +# "mem.usage.average", +# "mem.swapinRate.average", +# "mem.state.latest", +# "mem.latency.average", +# "mem.vmmemctl.average", +# "disk.read.average", +# "disk.write.average", +# "disk.numberReadAveraged.average", +# "disk.numberWriteAveraged.average", +# "disk.deviceReadLatency.average", +# "disk.deviceWriteLatency.average", +# "disk.totalReadLatency.average", +# "disk.totalWriteLatency.average", +# "storageAdapter.read.average", +# "storageAdapter.write.average", +# "storageAdapter.numberReadAveraged.average", +# "storageAdapter.numberWriteAveraged.average", +# "net.errorsRx.summation", +# "net.errorsTx.summation", +# "net.bytesRx.average", +# "net.bytesTx.average", +# "cpu.used.summation", +# "cpu.usage.average", +# "cpu.utilization.average", +# "cpu.wait.summation.", +# "cpu.idle.summation", +# "cpu.readiness.average", +# "cpu.costop.summation", +# "cpu.swapwait.summation", +# "mem.swapoutRate.average", +# "disk.kernelReadLatency.average", +# "disk.kernelWriteLatency.average" ] # host_metric_exclude = [] ## Nothing excluded by default -############### Clusters ############### - +## Cluster counter configuration ## Typical cluster metrics (if omitted, all metrics are collected) #cluster_metric_include = [ # "cpu.usage.*", @@ -98,13 +95,11 @@ NOTE: To disable collection av a specific resource type, simply exclude all metr # cluster_metric_exclude = [] ## Nothing excluded by default -############### Datastores ############### - +## Datastore counter configuration ## Typical datastore metrics (if omitted, all metrics are collected) ## ## WARNING: It is not recommended to include all metrics. Your collection will probably time out if you do! ## - datastore_metric_include = [ "datastore.read.average", "datastore.write.average", @@ -112,7 +107,11 @@ datastore_metric_include = [ "datastore.throughput.contention.average" ] ## number of objects to retreive per query. set to 64 for vCenter 5.5 and 6.0 (default: 256) -# objects_per_query = 256 +# max_query_objects = 256 + +## Number of metrics (data points) to retrieve in each query for non-realtime metrics (cluster and datastore). +## Set to 64 for vCenter 5.5 and 6.0 (default: 256) +# max_query_metrics = 256 ## the interval before (re)discovering objects subject to metrics collection (default: 300s) # object_discovery_interval = "300s" @@ -166,6 +165,8 @@ for more information. - Virtual Disk: seeks, # reads/writes, latency, load - Datastore stats: - Disk: Capacity, provisioned, used + +For a detailed list of commonly available metrics, please refer to [COUNTERS.MD](COUNTERS.MD) ## Tags - all metrics diff --git a/plugins/inputs/vsphere/conc_waitgroup.go b/plugins/inputs/vsphere/conc_waitgroup.go deleted file mode 100644 index 2d66291642dae..0000000000000 --- a/plugins/inputs/vsphere/conc_waitgroup.go +++ /dev/null @@ -1,62 +0,0 @@ -package vsphere - -import ( - "sync" -) - -// ConcurrentWaitGroup is a WaitGroup with special semantics. While a standard wait group -// requires Add() and Wait() to be called from the same goroutine, a ConcurrentWaitGroup -// allows Add() and Wait() to be called concurrently. -// By allowing this, we can no longer tell whether the jobs we're monitoring are truly -// finished or whether the job count temporarily dropped to zero. To prevent this, we -// add the requirement that no more jobs can be started once the counter has reached zero. -// This is done by returning a flag from Add() that, when set to false, means that -// a Wait() has become unblocked and that the caller must abort its attempt to run a job. -type ConcurrentWaitGroup struct { - mux sync.Mutex - cond *sync.Cond - jobs int - done bool -} - -// NewConcurrentWaitGroup returns a new NewConcurrentWaitGroup. -func NewConcurrentWaitGroup() *ConcurrentWaitGroup { - c := &ConcurrentWaitGroup{} - c.cond = sync.NewCond(&c.mux) - return c -} - -// Add signals the beginning of one or more jobs. The function returns false -// if a Wait() has already been unblocked and callers should not run the job. -func (c *ConcurrentWaitGroup) Add(inc int) bool { - c.mux.Lock() - defer c.mux.Unlock() - - if c.done { - return false - } - c.jobs += inc - if c.jobs == 0 { - c.cond.Broadcast() - } - return true -} - -// Done signals that a job is done. Once the number of running jobs reaches -// zero, any blocked calls to Wait() will unblock. -func (c *ConcurrentWaitGroup) Done() { - c.Add(-1) -} - -// Wait blocks until the number of running jobs reaches zero. Since the number -// of jobs dropping to zero during a Wait() will prevent new jobs from being -// started, this should only be called when it's desired to end all jobs, -// e.g. after some worker pool construct has been shut down. -func (c *ConcurrentWaitGroup) Wait() { - c.mux.Lock() - defer c.mux.Unlock() - for c.jobs != 0 { - c.cond.Wait() - } - c.done = true -} diff --git a/plugins/inputs/vsphere/endpoint.go b/plugins/inputs/vsphere/endpoint.go index b8d32fceabc68..90cd5644ce4fb 100644 --- a/plugins/inputs/vsphere/endpoint.go +++ b/plugins/inputs/vsphere/endpoint.go @@ -33,7 +33,7 @@ type Endpoint struct { collectMux sync.RWMutex initialized bool clientFactory *ClientFactory - wg *ConcurrentWaitGroup + busy sync.Mutex } type resourceKind struct { @@ -81,15 +81,14 @@ type metricQResponse struct { // NewEndpoint returns a new connection to a vCenter based on the URL and configuration passed // as parameters. -func NewEndpoint(parent *VSphere, url *url.URL) *Endpoint { +func NewEndpoint(ctx context.Context, parent *VSphere, url *url.URL) *Endpoint { e := Endpoint{ URL: url, Parent: parent, lastColls: make(map[string]time.Time), instanceInfo: make(map[string]resourceInfo), initialized: false, - wg: NewConcurrentWaitGroup(), - clientFactory: NewClientFactory(parent.rootCtx, url, parent), + clientFactory: NewClientFactory(ctx, url, parent), } e.resourceKinds = map[string]resourceKind{ @@ -139,6 +138,9 @@ func NewEndpoint(parent *VSphere, url *url.URL) *Endpoint { }, } + // Start discover and other goodness + e.init(ctx) + return &e } @@ -185,7 +187,7 @@ func (e *Endpoint) init(ctx context.Context) error { return err } - if e.Parent.ObjectDiscoveryInterval.Duration.Seconds() > 0 { + if e.Parent.ObjectDiscoveryInterval.Duration > 0 { // Run an initial discovery. If force_discovery_on_init isn't set, we kick it off as a // goroutine without waiting for it. This will probably cause us to report an empty @@ -246,13 +248,11 @@ func (e *Endpoint) getMetadata(ctx context.Context, in interface{}) interface{} } func (e *Endpoint) discover(ctx context.Context) error { - // Add returning false means we've been released from Wait and no - // more tasks are allowed. This happens when the plugin is stopped - // or reloaded. - if !e.wg.Add(1) { - return context.Canceled + e.busy.Lock() + defer e.busy.Unlock() + if ctx.Err() != nil { + return ctx.Err() } - defer e.wg.Done() sw := NewStopwatch("discover", e.URL.Host) var err error @@ -289,20 +289,26 @@ func (e *Endpoint) discover(ctx context.Context) error { }) // Drain the resulting metadata and build instance infos. - wp.Drain(ctx, func(ctx context.Context, in interface{}) { - resp := in.(*metricQResponse) - mList := make(performance.MetricList, 0) - if res.enabled { - for _, m := range *resp.metrics { - if m.Instance != "" && !res.collectInstances { - continue - } - if res.filters.Match(e.metricNames[m.CounterId]) { - mList = append(mList, m) + wp.Drain(ctx, func(ctx context.Context, in interface{}) bool { + switch resp := in.(type) { + case *metricQResponse: + mList := make(performance.MetricList, 0) + if res.enabled { + for _, m := range *resp.metrics { + if m.Instance != "" && !res.collectInstances { + continue + } + if res.filters.Match(e.metricNames[m.CounterId]) { + mList = append(mList, m) + } } } + instInfo[resp.obj.ref.Value] = resourceInfo{name: resp.obj.name, metrics: mList} + case error: + log.Printf("[input.vsphere]: Error while discovering resources: %s", resp) + return false } - instInfo[resp.obj.ref.Value] = resourceInfo{name: resp.obj.name, metrics: mList} + return true }) res.objects = objects resourceKinds[k] = res @@ -393,50 +399,34 @@ func (e *Endpoint) Close() { // Collect runs a round of data collections as specified in the configuration. func (e *Endpoint) Collect(ctx context.Context, acc telegraf.Accumulator) error { - // Add returning false means we've been released from Wait and no - // more tasks are allowed. This happens when the plugin is stopped - // or reloaded. - if !e.wg.Add(1) { - return context.Canceled - } - defer e.wg.Done() - - var err error - if !e.initialized { - err := e.init(ctx) - if err != nil { - return err - } - } - e.collectMux.RLock() defer e.collectMux.RUnlock() + if ctx.Err() != nil { + return ctx.Err() + } + // If discovery interval is disabled (0), discover on each collection cycle // - if e.Parent.ObjectDiscoveryInterval.Duration.Seconds() == 0 { - err = e.discover(ctx) + if e.Parent.ObjectDiscoveryInterval.Duration == 0 { + err := e.discover(ctx) if err != nil { return err } } for k, res := range e.resourceKinds { if res.enabled { - count, duration, err := e.collectResource(ctx, k, acc) + err := e.collectResource(ctx, k, acc) if err != nil { return err } - acc.AddGauge("vsphere", - map[string]interface{}{"gather.count": count, "gather.duration": duration}, - map[string]string{"vcenter": e.URL.Host, "type": k}, - time.Now()) } } return nil } func (e *Endpoint) chunker(ctx context.Context, f PushFunc, res *resourceKind, now time.Time, latest time.Time) { - pqs := make([]types.PerfQuerySpec, 0, e.Parent.ObjectsPerQuery) + pqs := make([]types.PerfQuerySpec, 0, e.Parent.MaxQueryObjects) metrics := 0 total := 0 nRes := 0 @@ -448,7 +438,7 @@ func (e *Endpoint) chunker(ctx context.Context, f PushFunc, res *resourceKind, n mr := len(info.metrics) for mr > 0 { mc := mr - headroom := e.Parent.MetricsPerQuery - metrics + headroom := e.Parent.MaxQueryMetrics - metrics if !res.realTime && mc > headroom { // Metric query limit only applies to non-realtime metrics mc = headroom } @@ -471,7 +461,7 @@ func (e *Endpoint) chunker(ctx context.Context, f PushFunc, res *resourceKind, n // We need to dump the current chunk of metrics for one of two reasons: // 1) We filled up the metric quota while processing the current resource // 2) We are at the last resource and have no more data to process. - if mr > 0 || (!res.realTime && metrics >= e.Parent.MetricsPerQuery) || nRes >= e.Parent.ObjectsPerQuery { + if mr > 0 || (!res.realTime && metrics >= e.Parent.MaxQueryMetrics) || nRes >= e.Parent.MaxQueryObjects { log.Printf("D! [input.vsphere]: Querying %d objects, %d metrics (%d remaining) of type %s for %s. Processed objects: %d. Total objects %d", len(pqs), metrics, mr, res.name, e.URL.Host, total+1, len(res.objects)) @@ -482,7 +472,7 @@ func (e *Endpoint) chunker(ctx context.Context, f PushFunc, res *resourceKind, n // Call push function f(ctx, pqs) - pqs = make([]types.PerfQuerySpec, 0, e.Parent.ObjectsPerQuery) + pqs = make([]types.PerfQuerySpec, 0, e.Parent.MaxQueryObjects) metrics = 0 nRes = 0 } @@ -498,7 +488,7 @@ func (e *Endpoint) chunker(ctx context.Context, f PushFunc, res *resourceKind, n } } -func (e *Endpoint) collectResource(ctx context.Context, resourceType string, acc telegraf.Accumulator) (int, float64, error) { +func (e *Endpoint) collectResource(ctx context.Context, resourceType string, acc telegraf.Accumulator) error { // Do we have new data yet? res := e.resourceKinds[resourceType] @@ -511,16 +501,16 @@ func (e *Endpoint) collectResource(ctx context.Context, resourceType string, acc // No new data would be available. We're outta herE! [input.vsphere]: log.Printf("D! [input.vsphere]: Sampling period for %s of %d has not elapsed for %s", resourceType, res.sampling, e.URL.Host) - return 0, 0, nil + return nil } } else { latest = time.Now().Add(time.Duration(-res.sampling) * time.Second) } internalTags := map[string]string{"resourcetype": resourceType} - sw := NewStopwatchWithTags("endpoint_gather", e.URL.Host, internalTags) + sw := NewStopwatchWithTags("gather_duration", e.URL.Host, internalTags) - log.Printf("D! [input.vsphere]: [input.vsphere] Start of sample period deemed to be %s", latest) + log.Printf("D! [input.vsphere]: Start of sample period deemed to be %s", latest) log.Printf("D! [input.vsphere]: Collecting metrics for %d objects of type %s for %s", len(res.objects), resourceType, e.URL.Host) @@ -548,17 +538,19 @@ func (e *Endpoint) collectResource(ctx context.Context, resourceType string, acc // Drain the pool. We're getting errors back. They should all be nil var err error - wp.Drain(ctx, func(ctx context.Context, in interface{}) { + wp.Drain(ctx, func(ctx context.Context, in interface{}) bool { if in != nil { err = in.(error) + return false } + return true }) e.lastColls[resourceType] = now // Use value captured at the beginning to avoid blind spots. sw.Stop() - SendInternalCounterWithTags("endpoint_gather_count", e.URL.Host, internalTags, count) - return int(count), time.Now().Sub(now).Seconds(), err + SendInternalCounterWithTags("gather_count", e.URL.Host, internalTags, count) + return err } func (e *Endpoint) collectChunk(ctx context.Context, pqs []types.PerfQuerySpec, resourceType string, @@ -571,6 +563,11 @@ func (e *Endpoint) collectChunk(ctx context.Context, pqs []types.PerfQuerySpec, return 0, err } + metricInfo, err := client.Perf.CounterInfoByName(ctx) + if err != nil { + return count, err + } + metrics, err := client.Perf.Query(ctx, pqs) if err != nil { return count, err @@ -624,14 +621,23 @@ func (e *Endpoint) collectChunk(ctx context.Context, pqs []types.PerfQuerySpec, log.Printf("D! [input.vsphere]: Negative value for %s on %s. Indicates missing samples", name, objectRef.name) continue } - bucket.fields[fn] = value + + // Percentage values must be scaled down by 100. + info, ok := metricInfo[name] + if !ok { + log.Printf("E! [input.vsphere]: Could not determine unit for %s. Skipping", name) + } + if info.UnitInfo.GetElementDescription().Key == "percent" { + bucket.fields[fn] = float64(value) / 100.0 + } else { + bucket.fields[fn] = value + } count++ } } // We've iterated through all the metrics and collected buckets for each // measurement name. Now emit them! for _, bucket := range buckets { - //log.Printf("Bucket key: %s", key) acc.AddFields(bucket.name, bucket.fields, bucket.tags, bucket.ts) } } @@ -704,24 +710,10 @@ func (e *Endpoint) makeMetricIdentifier(prefix, metric string) (string, string) } func cleanGuestID(id string) string { - if strings.HasSuffix(id, "Guest") { - return id[:len(id)-5] - } - - return id + return strings.TrimSuffix(id, "Guest") } func cleanDiskTag(disk string) string { - if strings.HasPrefix(disk, "<") { - i := strings.Index(disk, ">") - if i > -1 { - s1 := disk[1:i] - s2 := disk[i+1:] - if s1 == s2 { - return s1 - } - } - } - - return disk + // Remove enclosing "<>" + return strings.TrimSuffix(strings.TrimPrefix(disk, "<"), ">") } diff --git a/plugins/inputs/vsphere/selfhealth.go b/plugins/inputs/vsphere/selfhealth.go index 10fb86602f8d6..66069ca759e4b 100644 --- a/plugins/inputs/vsphere/selfhealth.go +++ b/plugins/inputs/vsphere/selfhealth.go @@ -17,7 +17,7 @@ type Stopwatch struct { // its creation. func NewStopwatch(name, vCenter string) *Stopwatch { return &Stopwatch{ - stat: selfstat.RegisterTiming("vsphere_timings", name+"_ms", map[string]string{"vcenter": vCenter}), + stat: selfstat.RegisterTiming("vsphere", name+"_ns", map[string]string{"vcenter": vCenter}), start: time.Now(), } } @@ -27,20 +27,20 @@ func NewStopwatch(name, vCenter string) *Stopwatch { func NewStopwatchWithTags(name, vCenter string, tags map[string]string) *Stopwatch { tags["vcenter"] = vCenter return &Stopwatch{ - stat: selfstat.RegisterTiming("vsphere_timings", name+"_ms", tags), + stat: selfstat.RegisterTiming("vsphere", name+"_ns", tags), start: time.Now(), } } // Stop stops a Stopwatch and records the time. func (s *Stopwatch) Stop() { - s.stat.Set(time.Since(s.start).Nanoseconds() / 1000000) + s.stat.Set(time.Since(s.start).Nanoseconds()) } // SendInternalCounter is a convenience method for sending // non-timing internal metrics. func SendInternalCounter(name, vCenter string, value int64) { - s := selfstat.Register("vsphere_counters", name, map[string]string{"vcenter": vCenter}) + s := selfstat.Register("vsphere", name, map[string]string{"vcenter": vCenter}) s.Set(value) } @@ -48,6 +48,6 @@ func SendInternalCounter(name, vCenter string, value int64) { // non-timing internal metrics. Allows additional tags func SendInternalCounterWithTags(name, vCenter string, tags map[string]string, value int64) { tags["vcenter"] = vCenter - s := selfstat.Register("vsphere_counters", name, tags) + s := selfstat.Register("vsphere", name, tags) s.Set(value) } diff --git a/plugins/inputs/vsphere/vsphere.go b/plugins/inputs/vsphere/vsphere.go index d211ae016b90e..aef71e99ccb92 100644 --- a/plugins/inputs/vsphere/vsphere.go +++ b/plugins/inputs/vsphere/vsphere.go @@ -16,7 +16,6 @@ import ( // VSphere is the top level type for the vSphere input plugin. It contains all the configuration // and a list of connected vSphere endpoints type VSphere struct { - Interval time.Duration Vcenters []string Username string Password string @@ -34,8 +33,8 @@ type VSphere struct { DatastoreMetricExclude []string Separator string - ObjectsPerQuery int - MetricsPerQuery int + MaxQueryObjects int + MaxQueryMetrics int CollectConcurrency int DiscoverConcurrency int ForceDiscoverOnInit bool @@ -43,7 +42,6 @@ type VSphere struct { Timeout internal.Duration endpoints []*Endpoint - rootCtx context.Context cancel context.CancelFunc // Mix in the TLS/SSL goodness from core @@ -57,94 +55,90 @@ var sampleConfig = ` # username = "user@corp.local" # password = "secret" -############### VMs ############### - +## VM counter configuration ## Collect VM instance metrics, such as individual cores? (default=true) #vm_instances = true ## Typical VM metrics (if omitted, all metrics are collected) vm_metric_include = [ - "cpu.ready.summation", - "mem.swapinRate.average", - "virtualDisk.numberReadAveraged.average", - "virtualDisk.numberWriteAveraged.average", - "virtualDisk.totalReadLatency.average", - "virtualDisk.totalWriteLatency.average", - "virtualDisk.readOIO.latest", - "virtualDisk.writeOIO.latest", - "net.bytesRx.average", - "net.bytesTx.average", - "net.droppedRx.summation.", - "net.droppedTx.summation.", - "cpu.run.summation", - "cpu.used.summation", - "mem.swapoutRate.average", - "virtualDisk.read.average", - "virtualDisk.write.average" ] + "cpu.ready.summation", + "mem.swapinRate.average", + "virtualDisk.numberReadAveraged.average", + "virtualDisk.numberWriteAveraged.average", + "virtualDisk.totalReadLatency.average", + "virtualDisk.totalWriteLatency.average", + "virtualDisk.readOIO.latest", + "virtualDisk.writeOIO.latest", + "net.bytesRx.average", + "net.bytesTx.average", + "net.droppedRx.summation.", + "net.droppedTx.summation.", + "cpu.run.summation", + "cpu.used.summation", + "mem.swapoutRate.average", + "virtualDisk.read.average", + "virtualDisk.write.average" ] # vm_metric_exclude = [] ## Nothing is excluded by default -############### Hosts ############### - +## Host counter configuration ## Collect host instance metrics, such as individual cores? (default=true) #host_instances = true ## Typical host metrics (if omitted, all metrics are collected) host_metric_include = [ - "cpu.ready.summation", - "cpu.latency.average", - "cpu.coreUtilization.average", - "mem.usage.average", - "mem.swapinRate.average", - "mem.state.latest", - "mem.latency.average", - "mem.vmmemctl.average", - "disk.read.average", - "disk.write.average", - "disk.numberReadAveraged.average", - "disk.numberWriteAveraged.average", - "disk.deviceReadLatency.average", - "disk.deviceWriteLatency.average", - "disk.totalReadLatency.average", - "disk.totalWriteLatency.average", - "storageAdapter.read.average", - "storageAdapter.write.average", - "storageAdapter.numberReadAveraged.average", - "storageAdapter.numberWriteAveraged.average", - "net.errorsRx.summation", - "net.errorsTx.summation", - "net.bytesRx.average", - "net.bytesTx.average", - "cpu.used.summation", - "cpu.usage.average", - "cpu.utilization.average", - "cpu.wait.summation.", - "cpu.idle.summation", - "cpu.readiness.average", - "cpu.costop.summation", - "cpu.swapwait.summation", - "mem.swapoutRate.average", - "disk.kernelReadLatency.average", - "disk.kernelWriteLatency.average" ] + "cpu.ready.summation", + "cpu.latency.average", + "cpu.coreUtilization.average", + "mem.usage.average", + "mem.swapinRate.average", + "mem.state.latest", + "mem.latency.average", + "mem.vmmemctl.average", + "disk.read.average", + "disk.write.average", + "disk.numberReadAveraged.average", + "disk.numberWriteAveraged.average", + "disk.deviceReadLatency.average", + "disk.deviceWriteLatency.average", + "disk.totalReadLatency.average", + "disk.totalWriteLatency.average", + "storageAdapter.read.average", + "storageAdapter.write.average", + "storageAdapter.numberReadAveraged.average", + "storageAdapter.numberWriteAveraged.average", + "net.errorsRx.summation", + "net.errorsTx.summation", + "net.bytesRx.average", + "net.bytesTx.average", + "cpu.used.summation", + "cpu.usage.average", + "cpu.utilization.average", + "cpu.wait.summation.", + "cpu.idle.summation", + "cpu.readiness.average", + "cpu.costop.summation", + "cpu.swapwait.summation", + "mem.swapoutRate.average", + "disk.kernelReadLatency.average", + "disk.kernelWriteLatency.average" ] # host_metric_exclude = [] ## Nothing excluded by default -############### Clusters ############### - +## Cluster counter configuration ## Collect cluster instance metrics, such as individual cores? (default=true) #cluster_instances = true ## Typical cluster metrics (if omitted, all metrics are collected) cluster_metric_include = [ - "cpu.usage.*", - "cpu.usagemhz.*", - "mem.usage.*", - "mem.active.*" ] + "cpu.usage.*", + "cpu.usagemhz.*", + "mem.usage.*", + "mem.active.*" ] # cluster_metric_exclude = [] ## Nothing excluded by default -############### Datastores ############### - +## Datastore counter configuration ## Collect datastore instance metrics, such as individual LUNs and datafiles? (default=false) #datastore_instances = false @@ -153,17 +147,17 @@ cluster_metric_include = [ ## WARNING: It is not recommended to include all metrics. Your collection will probably time out if you do! ## datastore_metric_include = [ - "datastore.read.average", - "datastore.write.average", - "datastore.throughput.usage.average", - "datastore.throughput.contention.average" ] + "datastore.read.average", + "datastore.write.average", + "datastore.throughput.usage.average", + "datastore.throughput.contention.average" ] ## Number of objects to retreive per query. -# objects_per_query = 256 +# max_query_objects = 256 ## Number of metrics (data points) to retrieve in each query for non-realtime metrics (cluster and datastore). ## Set to 64 for vCenter 5.5 and 6.0 (default: 256) -# metrics_per_query = 256 +# max_query_metrics = 256 ## the interval before (re)discovering objects subject to metrics collection (default: 300s) # object_discovery_interval = "300s" @@ -190,11 +184,14 @@ func (v *VSphere) Description() string { return "Read metrics from VMware vCenter" } -func (v *VSphere) checkEndpoints() error { - if v.endpoints != nil { - return nil - } +// Start is called from telegraf core when a plugin is started and allows it to +// perform initialization tasks. +func (v *VSphere) Start(acc telegraf.Accumulator) error { + log.Println("D! [input.vsphere]: Starting plugin") + ctx, cancel := context.WithCancel(context.Background()) + v.cancel = cancel + // Create endpoints, one for each vCenter we're monitoring v.endpoints = make([]*Endpoint, len(v.Vcenters)) for i, rawURL := range v.Vcenters { u, err := soap.ParseURL(rawURL) @@ -202,50 +199,42 @@ func (v *VSphere) checkEndpoints() error { return err } - v.endpoints[i] = NewEndpoint(v, u) + v.endpoints[i] = NewEndpoint(ctx, v, u) } return nil } -// Start is called from telegraf core when a plugin is started and allows it to -// perform initialization tasks. -func (v *VSphere) Start(acc telegraf.Accumulator) error { - log.Println("D! [input.vsphere]: Starting plugin") - rootCtx, cancel := context.WithCancel(context.Background()) - v.rootCtx = rootCtx - v.cancel = cancel - return nil -} - // Stop is called from telegraf core when a plugin is stopped and allows it to // perform shutdown tasks. func (v *VSphere) Stop() { log.Println("D! [input.vsphere]: Stopping plugin") v.cancel() - // Wait for all endpoints to finish + // Wait for all endpoints to finish. No need to wait for + // Gather() to finish here, since it Stop() will only be called + // after the last Gather() has finished. We do, however, need to + // wait for any discovery to complete by trying to grab the + // "busy" mutex. for _, ep := range v.endpoints { log.Printf("D! [input.vsphere]: Waiting for endpoint %s to finish", ep.URL.Host) - ep.wg.Wait() - ep.Close() + func() { + ep.busy.Lock() // Wait until discovery is finished + defer ep.busy.Unlock() + ep.Close() + }() } } // Gather is the main data collection function called by the Telegraf core. It performs all // the data collection and writes all metrics into the Accumulator passed as an argument. func (v *VSphere) Gather(acc telegraf.Accumulator) error { - err := v.checkEndpoints() - if err != nil { - return err - } - var wg sync.WaitGroup for _, ep := range v.endpoints { wg.Add(1) go func(endpoint *Endpoint) { defer wg.Done() - err := endpoint.Collect(v.rootCtx, acc) + err := endpoint.Collect(context.Background(), acc) if err == context.Canceled { // No need to signal errors if we were merely canceled. @@ -276,10 +265,10 @@ func init() { DatastoreInstances: false, DatastoreMetricInclude: nil, DatastoreMetricExclude: nil, - Separator: ".", + Separator: "_", - ObjectsPerQuery: 256, - MetricsPerQuery: 256, + MaxQueryObjects: 256, + MaxQueryMetrics: 256, CollectConcurrency: 1, DiscoverConcurrency: 1, ForceDiscoverOnInit: true, diff --git a/plugins/inputs/vsphere/vsphere_test.go b/plugins/inputs/vsphere/vsphere_test.go index a42638fe27942..2bee9c8f124f9 100644 --- a/plugins/inputs/vsphere/vsphere_test.go +++ b/plugins/inputs/vsphere/vsphere_test.go @@ -169,7 +169,7 @@ func defaultVSphere() *VSphere { DatastoreMetricExclude: nil, ClientConfig: itls.ClientConfig{InsecureSkipVerify: true}, - ObjectsPerQuery: 256, + MaxQueryObjects: 256, ObjectDiscoveryInterval: internal.Duration{Duration: time.Second * 300}, Timeout: internal.Duration{Duration: time.Second * 20}, } @@ -217,9 +217,10 @@ func TestWorkerPool(t *testing.T) { }) results := make([]int, n) i := 0 - wp.Drain(ctx, func(ctx context.Context, p interface{}) { + wp.Drain(ctx, func(ctx context.Context, p interface{}) bool { results[i] = p.(int) i++ + return true }) sort.Ints(results) for i := 0; i < n; i++ { @@ -227,26 +228,6 @@ func TestWorkerPool(t *testing.T) { } } -func TestWaitGroup(t *testing.T) { - c := NewConcurrentWaitGroup() - fmt.Println("Running...") - c.Add(1) - for i := 0; i < 5; i++ { - go func() { - require.True(t, c.Add(1)) // Should return true - defer c.Done() - time.Sleep(2 * time.Second) - }() - } - - time.Sleep(1 * time.Second) - go c.Done() - c.Wait() - - // Subsequent adds should return false. - require.True(t, !c.Add(1)) -} - func TestAll(t *testing.T) { m, s, err := createSim() if err != nil { diff --git a/plugins/inputs/vsphere/workerpool.go b/plugins/inputs/vsphere/workerpool.go index 828c46c19677f..6695735ce3a22 100644 --- a/plugins/inputs/vsphere/workerpool.go +++ b/plugins/inputs/vsphere/workerpool.go @@ -19,7 +19,8 @@ type PushFunc func(context.Context, interface{}) bool // DrainerFunc represents a function used to "drain" the WorkerPool, // i.e. pull out all the results generated by the workers and processing // them. The DrainerFunc is called once per result produced. -type DrainerFunc func(context.Context, interface{}) +// If the function returns false, the draining of the pool is aborted. +type DrainerFunc func(context.Context, interface{}) bool // FillerFunc represents a function for filling the WorkerPool with jobs. // It is called once and is responsible for pushing jobs onto the supplied channel. @@ -108,9 +109,11 @@ func (w *WorkerPool) Drain(ctx context.Context, f DrainerFunc) bool { go func() { defer w.wg.Done() for result := range w.Out { - f(ctx, result) + if !f(ctx, result) { + break + } } }() w.wg.Wait() - return ctx.Err() == nil + return ctx.Err() != nil } From fa79c8508a2a750f79ed502f39e85e8c3cfe66fe Mon Sep 17 00:00:00 2001 From: prydin Date: Thu, 30 Aug 2018 16:26:56 -0400 Subject: [PATCH 094/100] Added sample output --- plugins/inputs/vsphere/README.MD | 109 +++++++++++++++++++++++++++++++ 1 file changed, 109 insertions(+) diff --git a/plugins/inputs/vsphere/README.MD b/plugins/inputs/vsphere/README.MD index aabcadf62b6ab..97ab0c29b5134 100644 --- a/plugins/inputs/vsphere/README.MD +++ b/plugins/inputs/vsphere/README.MD @@ -197,3 +197,112 @@ For a detailed list of commonly available metrics, please refer to [COUNTERS.MD] - module (name of flash module) - virtualDisk stats for VM - disk (name of virtual disk) + +## Sample output + +``` +vsphere_vm_cpu,esxhostname=DC0_H0,guest=other,host=host.example.com,moid=vm-35,os=Mac,source=DC0_H0_VM0,vcenter=localhost:8989,vmname=DC0_H0_VM0 run_summation=2608i,ready_summation=129i,usage_average=5.01,used_summation=2134i,demand_average=326i 1535660299000000000 +vsphere_vm_net,esxhostname=DC0_H0,guest=other,host=host.example.com,moid=vm-35,os=Mac,source=DC0_H0_VM0,vcenter=localhost:8989,vmname=DC0_H0_VM0 bytesRx_average=321i,bytesTx_average=335i 1535660299000000000 +vsphere_vm_virtualDisk,esxhostname=DC0_H0,guest=other,host=host.example.com,moid=vm-35,os=Mac,source=DC0_H0_VM0,vcenter=localhost:8989,vmname=DC0_H0_VM0 write_average=144i,read_average=4i 1535660299000000000 +vsphere_vm_net,esxhostname=DC0_H0,guest=other,host=host.example.com,moid=vm-38,os=Mac,source=DC0_H0_VM1,vcenter=localhost:8989,vmname=DC0_H0_VM1 bytesRx_average=242i,bytesTx_average=308i 1535660299000000000 +vsphere_vm_virtualDisk,esxhostname=DC0_H0,guest=other,host=host.example.com,moid=vm-38,os=Mac,source=DC0_H0_VM1,vcenter=localhost:8989,vmname=DC0_H0_VM1 write_average=232i,read_average=4i 1535660299000000000 +vsphere_vm_cpu,esxhostname=DC0_H0,guest=other,host=host.example.com,moid=vm-38,os=Mac,source=DC0_H0_VM1,vcenter=localhost:8989,vmname=DC0_H0_VM1 usage_average=5.49,used_summation=1804i,demand_average=308i,run_summation=2001i,ready_summation=120i 1535660299000000000 +vsphere_vm_cpu,clustername=DC0_C0,esxhostname=DC0_C0_H0,guest=other,host=host.example.com,moid=vm-41,os=Mac,source=DC0_C0_RP0_VM0,vcenter=localhost:8989,vmname=DC0_C0_RP0_VM0 usage_average=4.19,used_summation=2108i,demand_average=285i,run_summation=1793i,ready_summation=93i 1535660299000000000 +vsphere_vm_net,clustername=DC0_C0,esxhostname=DC0_C0_H0,guest=other,host=host.example.com,moid=vm-41,os=Mac,source=DC0_C0_RP0_VM0,vcenter=localhost:8989,vmname=DC0_C0_RP0_VM0 bytesRx_average=272i,bytesTx_average=419i 1535660299000000000 +vsphere_vm_virtualDisk,clustername=DC0_C0,esxhostname=DC0_C0_H0,guest=other,host=host.example.com,moid=vm-41,os=Mac,source=DC0_C0_RP0_VM0,vcenter=localhost:8989,vmname=DC0_C0_RP0_VM0 write_average=229i,read_average=4i 1535660299000000000 +vsphere_vm_cpu,clustername=DC0_C0,esxhostname=DC0_C0_H0,guest=other,host=host.example.com,moid=vm-44,os=Mac,source=DC0_C0_RP0_VM1,vcenter=localhost:8989,vmname=DC0_C0_RP0_VM1 run_summation=2277i,ready_summation=118i,usage_average=4.67,used_summation=2546i,demand_average=289i 1535660299000000000 +vsphere_vm_net,clustername=DC0_C0,esxhostname=DC0_C0_H0,guest=other,host=host.example.com,moid=vm-44,os=Mac,source=DC0_C0_RP0_VM1,vcenter=localhost:8989,vmname=DC0_C0_RP0_VM1 bytesRx_average=243i,bytesTx_average=296i 1535660299000000000 +vsphere_vm_virtualDisk,clustername=DC0_C0,esxhostname=DC0_C0_H0,guest=other,host=host.example.com,moid=vm-44,os=Mac,source=DC0_C0_RP0_VM1,vcenter=localhost:8989,vmname=DC0_C0_RP0_VM1 write_average=158i,read_average=4i 1535660299000000000 +vsphere_host_net,esxhostname=DC0_H0,host=host.example.com,interface=vmnic0,moid=host-19,os=Mac,source=DC0_H0,vcenter=localhost:8989 usage_average=1042i,bytesTx_average=753i,bytesRx_average=660i 1535660299000000000 +vsphere_host_cpu,esxhostname=DC0_H0,host=host.example.com,moid=host-19,os=Mac,source=DC0_H0,vcenter=localhost:8989 utilization_average=10.46,usage_average=22.4,readiness_average=0.4,costop_summation=2i,coreUtilization_average=19.61,wait_summation=5148518i,idle_summation=58581i,latency_average=0.6,ready_summation=13370i,used_summation=19219i 1535660299000000000 +vsphere_host_cpu,cpu=0,esxhostname=DC0_H0,host=host.example.com,moid=host-19,os=Mac,source=DC0_H0,vcenter=localhost:8989 coreUtilization_average=25.6,utilization_average=11.58,used_summation=24306i,usage_average=24.26,idle_summation=86688i 1535660299000000000 +vsphere_host_cpu,cpu=1,esxhostname=DC0_H0,host=host.example.com,moid=host-19,os=Mac,source=DC0_H0,vcenter=localhost:8989 coreUtilization_average=12.29,utilization_average=8.32,used_summation=31312i,usage_average=22.47,idle_summation=94934i 1535660299000000000 +vsphere_host_disk,esxhostname=DC0_H0,host=host.example.com,moid=host-19,os=Mac,source=DC0_H0,vcenter=localhost:8989 read_average=331i,write_average=2800i 1535660299000000000 +vsphere_host_disk,disk=/var/folders/rf/txwdm4pj409f70wnkdlp7sz80000gq/T/govcsim-DC0-LocalDS_0-367088371@folder-5,esxhostname=DC0_H0,host=host.example.com,moid=host-19,os=Mac,source=DC0_H0,vcenter=localhost:8989 write_average=2701i,read_average=258i 1535660299000000000 +vsphere_host_mem,esxhostname=DC0_H0,host=host.example.com,moid=host-19,os=Mac,source=DC0_H0,vcenter=localhost:8989 usage_average=93.27 1535660299000000000 +vsphere_host_net,esxhostname=DC0_H0,host=host.example.com,moid=host-19,os=Mac,source=DC0_H0,vcenter=localhost:8989 bytesTx_average=650i,usage_average=1414i,bytesRx_average=569i 1535660299000000000 +vsphere_host_cpu,clustername=DC0_C0,cpu=1,esxhostname=DC0_C0_H0,host=host.example.com,moid=host-30,os=Mac,source=DC0_C0_H0,vcenter=localhost:8989 utilization_average=12.6,used_summation=25775i,usage_average=24.44,idle_summation=68886i,coreUtilization_average=17.59 1535660299000000000 +vsphere_host_disk,clustername=DC0_C0,esxhostname=DC0_C0_H0,host=host.example.com,moid=host-30,os=Mac,source=DC0_C0_H0,vcenter=localhost:8989 read_average=340i,write_average=2340i 1535660299000000000 +vsphere_host_disk,clustername=DC0_C0,disk=/var/folders/rf/txwdm4pj409f70wnkdlp7sz80000gq/T/govcsim-DC0-LocalDS_0-367088371@folder-5,esxhostname=DC0_C0_H0,host=host.example.com,moid=host-30,os=Mac,source=DC0_C0_H0,vcenter=localhost:8989 write_average=2277i,read_average=282i 1535660299000000000 +vsphere_host_mem,clustername=DC0_C0,esxhostname=DC0_C0_H0,host=host.example.com,moid=host-30,os=Mac,source=DC0_C0_H0,vcenter=localhost:8989 usage_average=104.78 1535660299000000000 +vsphere_host_net,clustername=DC0_C0,esxhostname=DC0_C0_H0,host=host.example.com,moid=host-30,os=Mac,source=DC0_C0_H0,vcenter=localhost:8989 bytesTx_average=463i,usage_average=1131i,bytesRx_average=719i 1535660299000000000 +vsphere_host_net,clustername=DC0_C0,esxhostname=DC0_C0_H0,host=host.example.com,interface=vmnic0,moid=host-30,os=Mac,source=DC0_C0_H0,vcenter=localhost:8989 usage_average=1668i,bytesTx_average=838i,bytesRx_average=921i 1535660299000000000 +vsphere_host_cpu,clustername=DC0_C0,esxhostname=DC0_C0_H0,host=host.example.com,moid=host-30,os=Mac,source=DC0_C0_H0,vcenter=localhost:8989 used_summation=28952i,utilization_average=11.36,idle_summation=93261i,latency_average=0.46,ready_summation=12837i,usage_average=21.56,readiness_average=0.39,costop_summation=2i,coreUtilization_average=27.19,wait_summation=3820829i 1535660299000000000 +vsphere_host_cpu,clustername=DC0_C0,cpu=0,esxhostname=DC0_C0_H0,host=host.example.com,moid=host-30,os=Mac,source=DC0_C0_H0,vcenter=localhost:8989 coreUtilization_average=24.12,utilization_average=13.83,used_summation=22462i,usage_average=24.69,idle_summation=96993i 1535660299000000000 +internal_vsphere,host=host.example.com,os=Mac,vcenter=localhost:8989 connect_ns=4727607i,discover_ns=65389011i,discovered_objects=8i 1535660309000000000 +internal_vsphere,host=host.example.com,os=Mac,resourcetype=datastore,vcenter=localhost:8989 gather_duration_ns=296223i,gather_count=0i 1535660309000000000 +internal_vsphere,host=host.example.com,os=Mac,resourcetype=vm,vcenter=192.168.1.151 gather_duration_ns=136050i,gather_count=0i 1535660309000000000 +internal_vsphere,host=host.example.com,os=Mac,resourcetype=host,vcenter=localhost:8989 gather_count=62i,gather_duration_ns=8788033i 1535660309000000000 +internal_vsphere,host=host.example.com,os=Mac,resourcetype=host,vcenter=192.168.1.151 gather_count=0i,gather_duration_ns=162002i 1535660309000000000 +internal_gather,host=host.example.com,input=vsphere,os=Mac gather_time_ns=17483653i,metrics_gathered=28i 1535660309000000000 +internal_vsphere,host=host.example.com,os=Mac,vcenter=192.168.1.151 connect_ns=0i 1535660309000000000 +internal_vsphere,host=host.example.com,os=Mac,resourcetype=vm,vcenter=localhost:8989 gather_duration_ns=7291897i,gather_count=36i 1535660309000000000 +internal_vsphere,host=host.example.com,os=Mac,resourcetype=datastore,vcenter=192.168.1.151 gather_duration_ns=958474i,gather_count=0i 1535660309000000000 +vsphere_vm_cpu,esxhostname=DC0_H0,guest=other,host=host.example.com,moid=vm-38,os=Mac,source=DC0_H0_VM1,vcenter=localhost:8989,vmname=DC0_H0_VM1 usage_average=8.82,used_summation=3192i,demand_average=283i,run_summation=2419i,ready_summation=115i 1535660319000000000 +vsphere_vm_net,esxhostname=DC0_H0,guest=other,host=host.example.com,moid=vm-38,os=Mac,source=DC0_H0_VM1,vcenter=localhost:8989,vmname=DC0_H0_VM1 bytesRx_average=277i,bytesTx_average=343i 1535660319000000000 +vsphere_vm_virtualDisk,esxhostname=DC0_H0,guest=other,host=host.example.com,moid=vm-38,os=Mac,source=DC0_H0_VM1,vcenter=localhost:8989,vmname=DC0_H0_VM1 read_average=1i,write_average=741i 1535660319000000000 +vsphere_vm_net,clustername=DC0_C0,esxhostname=DC0_C0_H0,guest=other,host=host.example.com,moid=vm-41,os=Mac,source=DC0_C0_RP0_VM0,vcenter=localhost:8989,vmname=DC0_C0_RP0_VM0 bytesRx_average=386i,bytesTx_average=369i 1535660319000000000 +vsphere_vm_virtualDisk,clustername=DC0_C0,esxhostname=DC0_C0_H0,guest=other,host=host.example.com,moid=vm-41,os=Mac,source=DC0_C0_RP0_VM0,vcenter=localhost:8989,vmname=DC0_C0_RP0_VM0 write_average=814i,read_average=1i 1535660319000000000 +vsphere_vm_cpu,clustername=DC0_C0,esxhostname=DC0_C0_H0,guest=other,host=host.example.com,moid=vm-41,os=Mac,source=DC0_C0_RP0_VM0,vcenter=localhost:8989,vmname=DC0_C0_RP0_VM0 run_summation=1778i,ready_summation=111i,usage_average=7.54,used_summation=2339i,demand_average=297i 1535660319000000000 +vsphere_vm_cpu,clustername=DC0_C0,esxhostname=DC0_C0_H0,guest=other,host=host.example.com,moid=vm-44,os=Mac,source=DC0_C0_RP0_VM1,vcenter=localhost:8989,vmname=DC0_C0_RP0_VM1 usage_average=6.98,used_summation=2125i,demand_average=211i,run_summation=2990i,ready_summation=141i 1535660319000000000 +vsphere_vm_net,clustername=DC0_C0,esxhostname=DC0_C0_H0,guest=other,host=host.example.com,moid=vm-44,os=Mac,source=DC0_C0_RP0_VM1,vcenter=localhost:8989,vmname=DC0_C0_RP0_VM1 bytesRx_average=357i,bytesTx_average=268i 1535660319000000000 +vsphere_vm_virtualDisk,clustername=DC0_C0,esxhostname=DC0_C0_H0,guest=other,host=host.example.com,moid=vm-44,os=Mac,source=DC0_C0_RP0_VM1,vcenter=localhost:8989,vmname=DC0_C0_RP0_VM1 write_average=528i,read_average=1i 1535660319000000000 +vsphere_vm_cpu,esxhostname=DC0_H0,guest=other,host=host.example.com,moid=vm-35,os=Mac,source=DC0_H0_VM0,vcenter=localhost:8989,vmname=DC0_H0_VM0 used_summation=2374i,demand_average=195i,run_summation=3454i,ready_summation=110i,usage_average=7.34 1535660319000000000 +vsphere_vm_net,esxhostname=DC0_H0,guest=other,host=host.example.com,moid=vm-35,os=Mac,source=DC0_H0_VM0,vcenter=localhost:8989,vmname=DC0_H0_VM0 bytesRx_average=308i,bytesTx_average=246i 1535660319000000000 +vsphere_vm_virtualDisk,esxhostname=DC0_H0,guest=other,host=host.example.com,moid=vm-35,os=Mac,source=DC0_H0_VM0,vcenter=localhost:8989,vmname=DC0_H0_VM0 write_average=1178i,read_average=1i 1535660319000000000 +vsphere_host_net,esxhostname=DC0_H0,host=host.example.com,interface=vmnic0,moid=host-19,os=Mac,source=DC0_H0,vcenter=localhost:8989 bytesRx_average=773i,usage_average=1521i,bytesTx_average=890i 1535660319000000000 +vsphere_host_cpu,esxhostname=DC0_H0,host=host.example.com,moid=host-19,os=Mac,source=DC0_H0,vcenter=localhost:8989 wait_summation=3421258i,idle_summation=67994i,latency_average=0.36,usage_average=29.86,readiness_average=0.37,used_summation=25244i,costop_summation=2i,coreUtilization_average=21.94,utilization_average=17.19,ready_summation=15897i 1535660319000000000 +vsphere_host_cpu,cpu=0,esxhostname=DC0_H0,host=host.example.com,moid=host-19,os=Mac,source=DC0_H0,vcenter=localhost:8989 utilization_average=11.32,used_summation=19333i,usage_average=14.29,idle_summation=92708i,coreUtilization_average=27.68 1535660319000000000 +vsphere_host_cpu,cpu=1,esxhostname=DC0_H0,host=host.example.com,moid=host-19,os=Mac,source=DC0_H0,vcenter=localhost:8989 used_summation=28596i,usage_average=25.32,idle_summation=79553i,coreUtilization_average=28.01,utilization_average=11.33 1535660319000000000 +vsphere_host_disk,esxhostname=DC0_H0,host=host.example.com,moid=host-19,os=Mac,source=DC0_H0,vcenter=localhost:8989 read_average=86i,write_average=1659i 1535660319000000000 +vsphere_host_disk,disk=/var/folders/rf/txwdm4pj409f70wnkdlp7sz80000gq/T/govcsim-DC0-LocalDS_0-367088371@folder-5,esxhostname=DC0_H0,host=host.example.com,moid=host-19,os=Mac,source=DC0_H0,vcenter=localhost:8989 write_average=1997i,read_average=58i 1535660319000000000 +vsphere_host_mem,esxhostname=DC0_H0,host=host.example.com,moid=host-19,os=Mac,source=DC0_H0,vcenter=localhost:8989 usage_average=68.45 1535660319000000000 +vsphere_host_net,esxhostname=DC0_H0,host=host.example.com,moid=host-19,os=Mac,source=DC0_H0,vcenter=localhost:8989 bytesTx_average=679i,usage_average=2286i,bytesRx_average=719i 1535660319000000000 +vsphere_host_cpu,clustername=DC0_C0,cpu=1,esxhostname=DC0_C0_H0,host=host.example.com,moid=host-30,os=Mac,source=DC0_C0_H0,vcenter=localhost:8989 utilization_average=10.52,used_summation=21693i,usage_average=23.09,idle_summation=84590i,coreUtilization_average=29.92 1535660319000000000 +vsphere_host_disk,clustername=DC0_C0,esxhostname=DC0_C0_H0,host=host.example.com,moid=host-30,os=Mac,source=DC0_C0_H0,vcenter=localhost:8989 read_average=113i,write_average=1236i 1535660319000000000 +vsphere_host_disk,clustername=DC0_C0,disk=/var/folders/rf/txwdm4pj409f70wnkdlp7sz80000gq/T/govcsim-DC0-LocalDS_0-367088371@folder-5,esxhostname=DC0_C0_H0,host=host.example.com,moid=host-30,os=Mac,source=DC0_C0_H0,vcenter=localhost:8989 write_average=1708i,read_average=110i 1535660319000000000 +vsphere_host_mem,clustername=DC0_C0,esxhostname=DC0_C0_H0,host=host.example.com,moid=host-30,os=Mac,source=DC0_C0_H0,vcenter=localhost:8989 usage_average=111.46 1535660319000000000 +vsphere_host_net,clustername=DC0_C0,esxhostname=DC0_C0_H0,host=host.example.com,moid=host-30,os=Mac,source=DC0_C0_H0,vcenter=localhost:8989 bytesTx_average=998i,usage_average=2000i,bytesRx_average=881i 1535660319000000000 +vsphere_host_net,clustername=DC0_C0,esxhostname=DC0_C0_H0,host=host.example.com,interface=vmnic0,moid=host-30,os=Mac,source=DC0_C0_H0,vcenter=localhost:8989 usage_average=1683i,bytesTx_average=675i,bytesRx_average=1078i 1535660319000000000 +vsphere_host_cpu,clustername=DC0_C0,esxhostname=DC0_C0_H0,host=host.example.com,moid=host-30,os=Mac,source=DC0_C0_H0,vcenter=localhost:8989 used_summation=28531i,wait_summation=3139129i,utilization_average=9.99,idle_summation=98579i,latency_average=0.51,costop_summation=2i,coreUtilization_average=14.35,ready_summation=16121i,usage_average=34.19,readiness_average=0.4 1535660319000000000 +vsphere_host_cpu,clustername=DC0_C0,cpu=0,esxhostname=DC0_C0_H0,host=host.example.com,moid=host-30,os=Mac,source=DC0_C0_H0,vcenter=localhost:8989 utilization_average=12.2,used_summation=22750i,usage_average=18.84,idle_summation=99539i,coreUtilization_average=23.05 1535660319000000000 +internal_vsphere,host=host.example.com,os=Mac,resourcetype=host,vcenter=localhost:8989 gather_duration_ns=7076543i,gather_count=62i 1535660339000000000 +internal_vsphere,host=host.example.com,os=Mac,resourcetype=host,vcenter=192.168.1.151 gather_duration_ns=4051303i,gather_count=0i 1535660339000000000 +internal_gather,host=host.example.com,input=vsphere,os=Mac metrics_gathered=56i,gather_time_ns=13555029i 1535660339000000000 +internal_vsphere,host=host.example.com,os=Mac,vcenter=192.168.1.151 connect_ns=0i 1535660339000000000 +internal_vsphere,host=host.example.com,os=Mac,resourcetype=vm,vcenter=localhost:8989 gather_duration_ns=6335467i,gather_count=36i 1535660339000000000 +internal_vsphere,host=host.example.com,os=Mac,resourcetype=datastore,vcenter=192.168.1.151 gather_duration_ns=958474i,gather_count=0i 1535660339000000000 +internal_vsphere,host=host.example.com,os=Mac,vcenter=localhost:8989 discover_ns=65389011i,discovered_objects=8i,connect_ns=4727607i 1535660339000000000 +internal_vsphere,host=host.example.com,os=Mac,resourcetype=datastore,vcenter=localhost:8989 gather_duration_ns=296223i,gather_count=0i 1535660339000000000 +internal_vsphere,host=host.example.com,os=Mac,resourcetype=vm,vcenter=192.168.1.151 gather_count=0i,gather_duration_ns=1540920i 1535660339000000000 +vsphere_vm_virtualDisk,esxhostname=DC0_H0,guest=other,host=host.example.com,moid=vm-35,os=Mac,source=DC0_H0_VM0,vcenter=localhost:8989,vmname=DC0_H0_VM0 write_average=302i,read_average=11i 1535660339000000000 +vsphere_vm_cpu,esxhostname=DC0_H0,guest=other,host=host.example.com,moid=vm-35,os=Mac,source=DC0_H0_VM0,vcenter=localhost:8989,vmname=DC0_H0_VM0 usage_average=5.58,used_summation=2941i,demand_average=298i,run_summation=3255i,ready_summation=96i 1535660339000000000 +vsphere_vm_net,esxhostname=DC0_H0,guest=other,host=host.example.com,moid=vm-35,os=Mac,source=DC0_H0_VM0,vcenter=localhost:8989,vmname=DC0_H0_VM0 bytesRx_average=155i,bytesTx_average=241i 1535660339000000000 +vsphere_vm_cpu,esxhostname=DC0_H0,guest=other,host=host.example.com,moid=vm-38,os=Mac,source=DC0_H0_VM1,vcenter=localhost:8989,vmname=DC0_H0_VM1 usage_average=10.3,used_summation=3053i,demand_average=346i,run_summation=3289i,ready_summation=122i 1535660339000000000 +vsphere_vm_net,esxhostname=DC0_H0,guest=other,host=host.example.com,moid=vm-38,os=Mac,source=DC0_H0_VM1,vcenter=localhost:8989,vmname=DC0_H0_VM1 bytesRx_average=215i,bytesTx_average=275i 1535660339000000000 +vsphere_vm_virtualDisk,esxhostname=DC0_H0,guest=other,host=host.example.com,moid=vm-38,os=Mac,source=DC0_H0_VM1,vcenter=localhost:8989,vmname=DC0_H0_VM1 write_average=252i,read_average=14i 1535660339000000000 +vsphere_vm_cpu,clustername=DC0_C0,esxhostname=DC0_C0_H0,guest=other,host=host.example.com,moid=vm-41,os=Mac,source=DC0_C0_RP0_VM0,vcenter=localhost:8989,vmname=DC0_C0_RP0_VM0 usage_average=8,used_summation=2183i,demand_average=354i,run_summation=3542i,ready_summation=128i 1535660339000000000 +vsphere_vm_net,clustername=DC0_C0,esxhostname=DC0_C0_H0,guest=other,host=host.example.com,moid=vm-41,os=Mac,source=DC0_C0_RP0_VM0,vcenter=localhost:8989,vmname=DC0_C0_RP0_VM0 bytesRx_average=178i,bytesTx_average=200i 1535660339000000000 +vsphere_vm_virtualDisk,clustername=DC0_C0,esxhostname=DC0_C0_H0,guest=other,host=host.example.com,moid=vm-41,os=Mac,source=DC0_C0_RP0_VM0,vcenter=localhost:8989,vmname=DC0_C0_RP0_VM0 write_average=283i,read_average=12i 1535660339000000000 +vsphere_vm_cpu,clustername=DC0_C0,esxhostname=DC0_C0_H0,guest=other,host=host.example.com,moid=vm-44,os=Mac,source=DC0_C0_RP0_VM1,vcenter=localhost:8989,vmname=DC0_C0_RP0_VM1 demand_average=328i,run_summation=3481i,ready_summation=122i,usage_average=7.95,used_summation=2167i 1535660339000000000 +vsphere_vm_net,clustername=DC0_C0,esxhostname=DC0_C0_H0,guest=other,host=host.example.com,moid=vm-44,os=Mac,source=DC0_C0_RP0_VM1,vcenter=localhost:8989,vmname=DC0_C0_RP0_VM1 bytesTx_average=282i,bytesRx_average=196i 1535660339000000000 +vsphere_vm_virtualDisk,clustername=DC0_C0,esxhostname=DC0_C0_H0,guest=other,host=host.example.com,moid=vm-44,os=Mac,source=DC0_C0_RP0_VM1,vcenter=localhost:8989,vmname=DC0_C0_RP0_VM1 write_average=321i,read_average=13i 1535660339000000000 +vsphere_host_disk,esxhostname=DC0_H0,host=host.example.com,moid=host-19,os=Mac,source=DC0_H0,vcenter=localhost:8989 read_average=39i,write_average=2635i 1535660339000000000 +vsphere_host_disk,disk=/var/folders/rf/txwdm4pj409f70wnkdlp7sz80000gq/T/govcsim-DC0-LocalDS_0-367088371@folder-5,esxhostname=DC0_H0,host=host.example.com,moid=host-19,os=Mac,source=DC0_H0,vcenter=localhost:8989 write_average=2635i,read_average=30i 1535660339000000000 +vsphere_host_mem,esxhostname=DC0_H0,host=host.example.com,moid=host-19,os=Mac,source=DC0_H0,vcenter=localhost:8989 usage_average=98.5 1535660339000000000 +vsphere_host_net,esxhostname=DC0_H0,host=host.example.com,moid=host-19,os=Mac,source=DC0_H0,vcenter=localhost:8989 usage_average=1887i,bytesRx_average=662i,bytesTx_average=251i 1535660339000000000 +vsphere_host_net,esxhostname=DC0_H0,host=host.example.com,interface=vmnic0,moid=host-19,os=Mac,source=DC0_H0,vcenter=localhost:8989 usage_average=1481i,bytesTx_average=899i,bytesRx_average=992i 1535660339000000000 +vsphere_host_cpu,esxhostname=DC0_H0,host=host.example.com,moid=host-19,os=Mac,source=DC0_H0,vcenter=localhost:8989 used_summation=50405i,costop_summation=2i,utilization_average=17.32,latency_average=0.61,ready_summation=14843i,usage_average=27.94,coreUtilization_average=32.12,wait_summation=3058787i,idle_summation=56600i,readiness_average=0.36 1535660339000000000 +vsphere_host_cpu,cpu=0,esxhostname=DC0_H0,host=host.example.com,moid=host-19,os=Mac,source=DC0_H0,vcenter=localhost:8989 coreUtilization_average=37.61,utilization_average=17.05,used_summation=38013i,usage_average=32.66,idle_summation=89575i 1535660339000000000 +vsphere_host_cpu,cpu=1,esxhostname=DC0_H0,host=host.example.com,moid=host-19,os=Mac,source=DC0_H0,vcenter=localhost:8989 coreUtilization_average=25.92,utilization_average=18.72,used_summation=39790i,usage_average=40.42,idle_summation=69457i 1535660339000000000 +vsphere_host_net,clustername=DC0_C0,esxhostname=DC0_C0_H0,host=host.example.com,interface=vmnic0,moid=host-30,os=Mac,source=DC0_C0_H0,vcenter=localhost:8989 usage_average=1246i,bytesTx_average=673i,bytesRx_average=781i 1535660339000000000 +vsphere_host_cpu,clustername=DC0_C0,esxhostname=DC0_C0_H0,host=host.example.com,moid=host-30,os=Mac,source=DC0_C0_H0,vcenter=localhost:8989 coreUtilization_average=33.8,idle_summation=77121i,ready_summation=15857i,readiness_average=0.39,used_summation=29554i,costop_summation=2i,wait_summation=4338417i,utilization_average=17.87,latency_average=0.44,usage_average=28.78 1535660339000000000 +vsphere_host_cpu,clustername=DC0_C0,cpu=0,esxhostname=DC0_C0_H0,host=host.example.com,moid=host-30,os=Mac,source=DC0_C0_H0,vcenter=localhost:8989 idle_summation=86610i,coreUtilization_average=34.36,utilization_average=19.03,used_summation=28766i,usage_average=23.72 1535660339000000000 +vsphere_host_cpu,clustername=DC0_C0,cpu=1,esxhostname=DC0_C0_H0,host=host.example.com,moid=host-30,os=Mac,source=DC0_C0_H0,vcenter=localhost:8989 coreUtilization_average=33.15,utilization_average=16.8,used_summation=44282i,usage_average=30.08,idle_summation=93490i 1535660339000000000 +vsphere_host_disk,clustername=DC0_C0,esxhostname=DC0_C0_H0,host=host.example.com,moid=host-30,os=Mac,source=DC0_C0_H0,vcenter=localhost:8989 read_average=56i,write_average=1672i 1535660339000000000 +vsphere_host_disk,clustername=DC0_C0,disk=/var/folders/rf/txwdm4pj409f70wnkdlp7sz80000gq/T/govcsim-DC0-LocalDS_0-367088371@folder-5,esxhostname=DC0_C0_H0,host=host.example.com,moid=host-30,os=Mac,source=DC0_C0_H0,vcenter=localhost:8989 write_average=2110i,read_average=48i 1535660339000000000 +vsphere_host_mem,clustername=DC0_C0,esxhostname=DC0_C0_H0,host=host.example.com,moid=host-30,os=Mac,source=DC0_C0_H0,vcenter=localhost:8989 usage_average=116.21 1535660339000000000 +vsphere_host_net,clustername=DC0_C0,esxhostname=DC0_C0_H0,host=host.example.com,moid=host-30,os=Mac,source=DC0_C0_H0,vcenter=localhost:8989 bytesRx_average=726i,bytesTx_average=643i,usage_average=1504i 1535660339000000000 +vsphere_host_mem,clustername=DC0_C0,esxhostname=DC0_C0_H0,host=host.example.com,moid=host-30,os=Mac,source=DC0_C0_H0,vcenter=localhost:8989 usage_average=116.21 1535660339000000000 +vsphere_host_net,clustername=DC0_C0,esxhostname=DC0_C0_H0,host=host.example.com,moid=host-30,os=Mac,source=DC0_C0_H0,vcenter=localhost:8989 bytesRx_average=726i,bytesTx_average=643i,usage_average=1504i 1535660339000000000 +``` From a4f59902c9957ccc7f191dc4a9ae418094da2fac Mon Sep 17 00:00:00 2001 From: Pierre Tessier Date: Sat, 1 Sep 2018 09:28:12 -0400 Subject: [PATCH 095/100] updated readme and sample config --- .../vsphere/{COUNTERS.MD => METRICS.MD} | 20 +- plugins/inputs/vsphere/README.MD | 244 +++++++++------- plugins/inputs/vsphere/vsphere.go | 268 ++++++++++-------- 3 files changed, 301 insertions(+), 231 deletions(-) rename plugins/inputs/vsphere/{COUNTERS.MD => METRICS.MD} (91%) diff --git a/plugins/inputs/vsphere/COUNTERS.MD b/plugins/inputs/vsphere/METRICS.MD similarity index 91% rename from plugins/inputs/vsphere/COUNTERS.MD rename to plugins/inputs/vsphere/METRICS.MD index 6b494c3c02269..0b9e0482fd8f8 100644 --- a/plugins/inputs/vsphere/COUNTERS.MD +++ b/plugins/inputs/vsphere/METRICS.MD @@ -1,15 +1,17 @@ -# Common vSphere Performance Counters -The set of performance counters in vSphere is open ended. Counters may be added or removed in new releases and the set of available counters may vary depending hardware, as well as what plugins and add-on products are installed. Therefore, providing a definitive list of available metrics is difficult. The metrics listed below are the most -commonly available as of vSphere 6.5. +# Common vSphere Performance Metrics +The set of performance metrics in vSphere is open ended. Metrics may be added or removed in new releases +and the set of available metrics may vary depending hardware, as well as what plugins and add-on products +are installed. Therefore, providing a definitive list of available metrics is difficult. The metrics listed +below are the most commonly available as of vSphere 6.5. To list the exact set in your environment, please use the govc tool available [here](https://github.com/vmware/govmomi/tree/master/govc) -To obtain the set of counters for e.g. a VM, you may use the following command: +To obtain the set of metrics for e.g. a VM, you may use the following command: ``` govc metric.ls vm/* ``` -## Virtual Machine Counters +## Virtual Machine Metrics ``` cpu.demandEntitlementRatio.latest cpu.usage.average @@ -102,7 +104,7 @@ virtualDisk.write.average virtualDisk.read.average ``` -## Host System Counters +## Host System Metrics ``` cpu.corecount.contention.average cpu.usage.average @@ -185,7 +187,7 @@ vmop.numSVMotion.latest vmop.numXVMotion.latest ``` -## Cluster Counters +## Cluster Metrics ``` cpu.corecount.contention.average cpu.usage.average @@ -266,8 +268,10 @@ vmop.numChangeHostDS.latest vmop.numVMotion.latest vmop.numSVMotion.latest vmop.numXVMotion.latest +``` -## Datastore Counters +## Datastore Metrics +``` datastore.numberReadAveraged.average datastore.throughput.contention.average datastore.throughput.usage.average diff --git a/plugins/inputs/vsphere/README.MD b/plugins/inputs/vsphere/README.MD index 97ab0c29b5134..1635058f44ecc 100644 --- a/plugins/inputs/vsphere/README.MD +++ b/plugins/inputs/vsphere/README.MD @@ -9,130 +9,171 @@ The VMware vSphere plugin uses the vSphere API to gather metrics from multiple v ## Configuration -NOTE: To disable collection av a specific resource type, simply exclude all metrics using the XX_metric_exclude. For example, to disable collection of VMs, add this: +NOTE: To disable collection of a specific resource type, simply exclude all metrics using the XX_metric_exclude. +For example, to disable collection of VMs, add this: ```vm_metric_exclude = [ "*" ]``` ``` # Read metrics from one or many vCenters [[inputs.vsphere]] - # List of vCenter URLs to be monitored. These three lines must be uncommented -## and edited for the plugin to work. -# vcenters = [ "https://vcenter.local/sdk" ] -# username = "user@corp.local" -# password = "secret" + ## List of vCenter URLs to be monitored. These three lines must be uncommented + ## and edited for the plugin to work. + vcenters = [ "https://vcenter.local/sdk" ] + username = "user@corp.local" + password = "secret" -## VM counter configuration -## Typical VM metrics (if omitted, all metrics are collected) -# vm_metric_include = [ -# "cpu.ready.summation", -# "mem.swapinRate.average", -# "virtualDisk.numberReadAveraged.average", -# "virtualDisk.numberWriteAveraged.average", -# "virtualDisk.totalReadLatency.average", -# "virtualDisk.totalWriteLatency.average", -# "virtualDisk.readOIO.latest", -# "virtualDisk.writeOIO.latest", -# "net.bytesRx.average", -# "net.bytesTx.average", -# "net.droppedRx.summation.", -# "net.droppedTx.summation.", -# "cpu.run.summation", -# "cpu.used.summation", -# "mem.swapoutRate.average", -# "virtualDisk.read.average", -# "virtualDisk.write.average" ] + ############### VMs ############### + ## Typical VM metrics (if omitted or empty, all metrics are collected) + vm_metric_include = [ + "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", + ] + # vm_metric_exclude = [] ## Nothing is excluded by default + # vm_instances = true ## true by default -# vm_metric_exclude = [] ## Nothing is excluded by default + ############### Hosts ############### + ## Typical host metrics (if omitted or empty, all metrics are collected) + host_metric_include = [ + "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", + ] + # host_metric_exclude = [] ## Nothing excluded by default + # host_instances = true ## true by default -## Host counter configuration -## Typical host metrics (if omitted, all metrics are collected) -# host_metric_include = [ -# "cpu.ready.summation", -# "cpu.latency.average", -# "cpu.coreUtilization.average", -# "mem.usage.average", -# "mem.swapinRate.average", -# "mem.state.latest", -# "mem.latency.average", -# "mem.vmmemctl.average", -# "disk.read.average", -# "disk.write.average", -# "disk.numberReadAveraged.average", -# "disk.numberWriteAveraged.average", -# "disk.deviceReadLatency.average", -# "disk.deviceWriteLatency.average", -# "disk.totalReadLatency.average", -# "disk.totalWriteLatency.average", -# "storageAdapter.read.average", -# "storageAdapter.write.average", -# "storageAdapter.numberReadAveraged.average", -# "storageAdapter.numberWriteAveraged.average", -# "net.errorsRx.summation", -# "net.errorsTx.summation", -# "net.bytesRx.average", -# "net.bytesTx.average", -# "cpu.used.summation", -# "cpu.usage.average", -# "cpu.utilization.average", -# "cpu.wait.summation.", -# "cpu.idle.summation", -# "cpu.readiness.average", -# "cpu.costop.summation", -# "cpu.swapwait.summation", -# "mem.swapoutRate.average", -# "disk.kernelReadLatency.average", -# "disk.kernelWriteLatency.average" ] + ############### Clusters ############### + # cluster_metric_include = [] ## if omitted or empty, all metrics are collected + # cluster_metric_exclude = [] ## Nothing excluded by default + # cluster_instances = true ## true by default -# host_metric_exclude = [] ## Nothing excluded by default + ############### Datastores ############### + # datastore_metric_include = [] ## if omitted or empty, all metrics are collected + # datastore_metric_exclude = [] ## Nothing excluded by default + # datastore_instances = false ## false by default for Datastores only -## Cluster counter configuration -## Typical cluster metrics (if omitted, all metrics are collected) -#cluster_metric_include = [ -# "cpu.usage.*", -# "cpu.usagemhz.*", -# "mem.usage.*", -# "mem.active.*" ] -# cluster_metric_exclude = [] ## Nothing excluded by default + ############### Plugin Settings ############### + ## separator character to use for measurement and field names (default: "_") + # separator = "_" -## Datastore counter configuration -## Typical datastore metrics (if omitted, all metrics are collected) -## -## WARNING: It is not recommended to include all metrics. Your collection will probably time out if you do! -## -datastore_metric_include = [ - "datastore.read.average", - "datastore.write.average", - "datastore.throughput.usage.average", - "datastore.throughput.contention.average" ] + ## number of objects to retreive per query for realtime resources (vms and hosts) + ## set to 64 for vCenter 5.5 and 6.0 (default: 256) + # objects_per_query = 256 -## number of objects to retreive per query. set to 64 for vCenter 5.5 and 6.0 (default: 256) -# max_query_objects = 256 + ## number of metrics to retreive per query for non-realtime resources (clusters and datastores) + ## set to 64 for vCenter 5.5 and 6.0 (default: 256) + # metrics_per_query = 256 -## Number of metrics (data points) to retrieve in each query for non-realtime metrics (cluster and datastore). -## Set to 64 for vCenter 5.5 and 6.0 (default: 256) -# max_query_metrics = 256 + ## number of go routines to use for collection and discovery of objects and metrics + # collect_concurrency = 1 + # discover_concurrency = 1 -## the interval before (re)discovering objects subject to metrics collection (default: 300s) -# object_discovery_interval = "300s" + ## whether or not to force discovery of new objects on initial gather call before collecting metrics + ## when true for large environments this may cause errors for time elapsed while collecting metrics + ## when false (default) the first collection cycle may result in no or limited metrics while objects are discovered + # force_discover_on_init = false -## timeout applies to any of the connection request made to vcenter -# timeout = "20s" + ## the interval before (re)discovering objects subject to metrics collection (default: 300s) + # object_discovery_interval = "300s" -## Optional SSL Config -# ssl_ca = "/path/to/cafile" -# ssl_cert = "/path/to/certfile" -# ssl_key = "/path/to/keyfile" -## Use SSL but skip chain & host verification -# insecure_skip_verify = false + ## timeout applies to any of the api request made to vcenter + # timeout = "20s" + + ## Optional SSL Config + # ssl_ca = "/path/to/cafile" + # ssl_cert = "/path/to/certfile" + # ssl_key = "/path/to/keyfile" + ## Use SSL but skip chain & host verification + # insecure_skip_verify = false ``` -### Objects Per Query +### Objects and Metrics Per Query Default settings for vCenter 6.5 and above is 256. Prior versions of vCenter have this set to 64. A vCenter administrator can change this setting, which should be reflected in this plugin. See this [VMware KB article](https://kb.vmware.com/s/article/2107096) for more information. +### Collection and Discovery concurrency + +On large vCenter setups it may be prudent to have multiple concurrent go routines collect performance metrics +in order to avoid potential errors for time elapsed during a collection cycle. This should never be greater than 8, +though the default of 1 (no concurrency) should be sufficient for most configurations. + ## Measurements & Fields - Cluster Stats @@ -166,9 +207,10 @@ for more information. - Datastore stats: - Disk: Capacity, provisioned, used -For a detailed list of commonly available metrics, please refer to [COUNTERS.MD](COUNTERS.MD) +For a detailed list of commonly available metrics, please refer to [METRICS.MD](METRICS.MD) ## Tags + - all metrics - vcenter (vcenter url) - all host metrics diff --git a/plugins/inputs/vsphere/vsphere.go b/plugins/inputs/vsphere/vsphere.go index aef71e99ccb92..4f06fa804eae9 100644 --- a/plugins/inputs/vsphere/vsphere.go +++ b/plugins/inputs/vsphere/vsphere.go @@ -49,128 +49,152 @@ type VSphere struct { } var sampleConfig = ` -## List of vCenter URLs to be monitored. These three lines must be uncommented -## and edited for the plugin to work. -# vcenters = [ "https://vcenter.local/sdk" ] -# username = "user@corp.local" -# password = "secret" - -## VM counter configuration -## Collect VM instance metrics, such as individual cores? (default=true) -#vm_instances = true - -## Typical VM metrics (if omitted, all metrics are collected) -vm_metric_include = [ - "cpu.ready.summation", - "mem.swapinRate.average", - "virtualDisk.numberReadAveraged.average", - "virtualDisk.numberWriteAveraged.average", - "virtualDisk.totalReadLatency.average", - "virtualDisk.totalWriteLatency.average", - "virtualDisk.readOIO.latest", - "virtualDisk.writeOIO.latest", - "net.bytesRx.average", - "net.bytesTx.average", - "net.droppedRx.summation.", - "net.droppedTx.summation.", - "cpu.run.summation", - "cpu.used.summation", - "mem.swapoutRate.average", - "virtualDisk.read.average", - "virtualDisk.write.average" ] - -# vm_metric_exclude = [] ## Nothing is excluded by default - -## Host counter configuration -## Collect host instance metrics, such as individual cores? (default=true) -#host_instances = true - -## Typical host metrics (if omitted, all metrics are collected) -host_metric_include = [ - "cpu.ready.summation", - "cpu.latency.average", - "cpu.coreUtilization.average", - "mem.usage.average", - "mem.swapinRate.average", - "mem.state.latest", - "mem.latency.average", - "mem.vmmemctl.average", - "disk.read.average", - "disk.write.average", - "disk.numberReadAveraged.average", - "disk.numberWriteAveraged.average", - "disk.deviceReadLatency.average", - "disk.deviceWriteLatency.average", - "disk.totalReadLatency.average", - "disk.totalWriteLatency.average", - "storageAdapter.read.average", - "storageAdapter.write.average", - "storageAdapter.numberReadAveraged.average", - "storageAdapter.numberWriteAveraged.average", - "net.errorsRx.summation", - "net.errorsTx.summation", - "net.bytesRx.average", - "net.bytesTx.average", - "cpu.used.summation", - "cpu.usage.average", - "cpu.utilization.average", - "cpu.wait.summation.", - "cpu.idle.summation", - "cpu.readiness.average", - "cpu.costop.summation", - "cpu.swapwait.summation", - "mem.swapoutRate.average", - "disk.kernelReadLatency.average", - "disk.kernelWriteLatency.average" ] - -# host_metric_exclude = [] ## Nothing excluded by default - -## Cluster counter configuration -## Collect cluster instance metrics, such as individual cores? (default=true) -#cluster_instances = true - -## Typical cluster metrics (if omitted, all metrics are collected) -cluster_metric_include = [ - "cpu.usage.*", - "cpu.usagemhz.*", - "mem.usage.*", - "mem.active.*" ] - -# cluster_metric_exclude = [] ## Nothing excluded by default - -## Datastore counter configuration -## Collect datastore instance metrics, such as individual LUNs and datafiles? (default=false) -#datastore_instances = false - -## Typical datastore metrics (if omitted, all metrics are collected) -## -## WARNING: It is not recommended to include all metrics. Your collection will probably time out if you do! -## -datastore_metric_include = [ - "datastore.read.average", - "datastore.write.average", - "datastore.throughput.usage.average", - "datastore.throughput.contention.average" ] - -## Number of objects to retreive per query. -# max_query_objects = 256 - -## Number of metrics (data points) to retrieve in each query for non-realtime metrics (cluster and datastore). -## Set to 64 for vCenter 5.5 and 6.0 (default: 256) -# max_query_metrics = 256 - -## the interval before (re)discovering objects subject to metrics collection (default: 300s) -# object_discovery_interval = "300s" - -## timeout applies to any of the connection request made to vcenter -# timeout = "20s" - -## Optional SSL Config -# ssl_ca = "/path/to/cafile" -# ssl_cert = "/path/to/certfile" -# ssl_key = "/path/to/keyfile" -## Use SSL but skip chain & host verification -# insecure_skip_verify = false +# Read metrics from one or many vCenters +[[inputs.vsphere]] + ## List of vCenter URLs to be monitored. These three lines must be uncommented + ## and edited for the plugin to work. + vcenters = [ "https://vcenter.local/sdk" ] + username = "user@corp.local" + password = "secret" + + ############### VMs ############### + ## Typical VM metrics (if omitted or empty, all metrics are collected) + vm_metric_include = [ + "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", + ] + # vm_metric_exclude = [] ## Nothing is excluded by default + # vm_instances = true ## true by default + + ############### Hosts ############### + ## Typical host metrics (if omitted or empty, all metrics are collected) + host_metric_include = [ + "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", + ] + # host_metric_exclude = [] ## Nothing excluded by default + # host_instances = true ## true by default + + ############### Clusters ############### + # cluster_metric_include = [] ## if omitted or empty, all metrics are collected + # cluster_metric_exclude = [] ## Nothing excluded by default + # cluster_instances = true ## true by default + + ############### Datastores ############### + # datastore_metric_include = [] ## if omitted or empty, all metrics are collected + # datastore_metric_exclude = [] ## Nothing excluded by default + # datastore_instances = false ## false by default for Datastores only + + + ############### Plugin Settings ############### + ## separator character to use for measurement and field names (default: "_") + # separator = "_" + + ## number of objects to retreive per query for realtime resources (vms and hosts) + ## set to 64 for vCenter 5.5 and 6.0 (default: 256) + # objects_per_query = 256 + + ## number of metrics to retreive per query for non-realtime resources (clusters and datastores) + ## set to 64 for vCenter 5.5 and 6.0 (default: 256) + # metrics_per_query = 256 + + ## number of go routines to use for collection and discovery of objects and metrics + # collect_concurrency = 1 + # discover_concurrency = 1 + + ## whether or not to force discovery of new objects on initial gather call before collecting metrics + ## when true for large environments this may cause errors for time elapsed while collecting metrics + ## when false (default) the first collection cycle may result in no or limited metrics while objects are discovered + # force_discover_on_init = false + + ## the interval before (re)discovering objects subject to metrics collection (default: 300s) + # object_discovery_interval = "300s" + + ## timeout applies to any of the api request made to vcenter + # timeout = "20s" + + ## Optional SSL Config + # ssl_ca = "/path/to/cafile" + # ssl_cert = "/path/to/certfile" + # ssl_key = "/path/to/keyfile" + ## Use SSL but skip chain & host verification + # insecure_skip_verify = false ` // SampleConfig returns a set of default configuration to be used as a boilerplate when setting up From e2e20403375aaa560582db2575c9d38588d95609 Mon Sep 17 00:00:00 2001 From: Pierre Tessier Date: Sat, 1 Sep 2018 09:41:20 -0400 Subject: [PATCH 096/100] force_discover_on_init default to false --- plugins/inputs/vsphere/vsphere.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/inputs/vsphere/vsphere.go b/plugins/inputs/vsphere/vsphere.go index 4f06fa804eae9..1001c0b9ecd8d 100644 --- a/plugins/inputs/vsphere/vsphere.go +++ b/plugins/inputs/vsphere/vsphere.go @@ -295,7 +295,7 @@ func init() { MaxQueryMetrics: 256, CollectConcurrency: 1, DiscoverConcurrency: 1, - ForceDiscoverOnInit: true, + ForceDiscoverOnInit: false, ObjectDiscoveryInterval: internal.Duration{Duration: time.Second * 300}, Timeout: internal.Duration{Duration: time.Second * 20}, } From 7370f23c25a31b9c55a8b996cd9a85a5f0e314d0 Mon Sep 17 00:00:00 2001 From: prydin Date: Tue, 4 Sep 2018 11:53:43 -0500 Subject: [PATCH 097/100] Set ForceDiscoverOnInit to true in test to avoid false positives --- plugins/inputs/vsphere/vsphere_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/inputs/vsphere/vsphere_test.go b/plugins/inputs/vsphere/vsphere_test.go index 2bee9c8f124f9..0dfcf47e0386d 100644 --- a/plugins/inputs/vsphere/vsphere_test.go +++ b/plugins/inputs/vsphere/vsphere_test.go @@ -172,6 +172,7 @@ func defaultVSphere() *VSphere { MaxQueryObjects: 256, ObjectDiscoveryInterval: internal.Duration{Duration: time.Second * 300}, Timeout: internal.Duration{Duration: time.Second * 20}, + ForceDiscoverOnInit: true, } } From 81f5e8436a9f8d23f37602a4e10529005509dbdd Mon Sep 17 00:00:00 2001 From: prydin Date: Tue, 4 Sep 2018 12:10:33 -0500 Subject: [PATCH 098/100] Fixed typo in default config --- plugins/inputs/vsphere/vsphere.go | 6 ++---- plugins/inputs/vsphere/vsphere_test.go | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/plugins/inputs/vsphere/vsphere.go b/plugins/inputs/vsphere/vsphere.go index 1001c0b9ecd8d..6df5253447054 100644 --- a/plugins/inputs/vsphere/vsphere.go +++ b/plugins/inputs/vsphere/vsphere.go @@ -49,8 +49,6 @@ type VSphere struct { } var sampleConfig = ` -# Read metrics from one or many vCenters -[[inputs.vsphere]] ## List of vCenter URLs to be monitored. These three lines must be uncommented ## and edited for the plugin to work. vcenters = [ "https://vcenter.local/sdk" ] @@ -168,11 +166,11 @@ var sampleConfig = ` ## number of objects to retreive per query for realtime resources (vms and hosts) ## set to 64 for vCenter 5.5 and 6.0 (default: 256) - # objects_per_query = 256 + # max_query_objects = 256 ## number of metrics to retreive per query for non-realtime resources (clusters and datastores) ## set to 64 for vCenter 5.5 and 6.0 (default: 256) - # metrics_per_query = 256 + # max_query_metrics = 256 ## number of go routines to use for collection and discovery of objects and metrics # collect_concurrency = 1 diff --git a/plugins/inputs/vsphere/vsphere_test.go b/plugins/inputs/vsphere/vsphere_test.go index 0dfcf47e0386d..20c61d92bf2cd 100644 --- a/plugins/inputs/vsphere/vsphere_test.go +++ b/plugins/inputs/vsphere/vsphere_test.go @@ -96,7 +96,6 @@ var configHeader = ` hostname = "" ## If set to true, do no set the "host" tag in the telegraf agent. omit_hostname = false - ` func defaultVSphere() *VSphere { @@ -198,6 +197,7 @@ func TestParseConfig(t *testing.T) { 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) From 238e0154461f14e1ef93c3e109df26f6424ab784 Mon Sep 17 00:00:00 2001 From: prydin Date: Thu, 6 Sep 2018 18:09:06 -0500 Subject: [PATCH 099/100] Better error handling --- plugins/inputs/vsphere/client.go | 12 ++++++-- plugins/inputs/vsphere/endpoint.go | 44 ++++++++++++++++++++++++++---- plugins/inputs/vsphere/vsphere.go | 5 ++-- 3 files changed, 51 insertions(+), 10 deletions(-) diff --git a/plugins/inputs/vsphere/client.go b/plugins/inputs/vsphere/client.go index 09d24004dc634..b9547b17947e8 100644 --- a/plugins/inputs/vsphere/client.go +++ b/plugins/inputs/vsphere/client.go @@ -2,6 +2,7 @@ package vsphere import ( "context" + "crypto/tls" "log" "net/url" "sync" @@ -76,6 +77,10 @@ func NewClient(u *url.URL, vs *VSphere) (*Client, error) { if err != nil { return nil, err } + // Use a default TLS config if it's missing + if tlsCfg == nil { + tlsCfg = &tls.Config{} + } if vs.Username != "" { u.User = url.UserPassword(vs.Username, vs.Password) } @@ -105,7 +110,9 @@ func NewClient(u *url.URL, vs *VSphere) (*Client, error) { // If TSLKey is specified, try to log in as an extension using a cert. if vs.TLSKey != "" { - sm.LoginExtensionByCertificate(ctx, vs.TLSKey) + if err := sm.LoginExtensionByCertificate(ctx, vs.TLSKey); err != nil { + return nil, err + } } // Create the govmomi client. @@ -116,8 +123,7 @@ func NewClient(u *url.URL, vs *VSphere) (*Client, error) { // Only login if the URL contains user information. if u.User != nil { - err = c.Login(ctx, u.User) - if err != nil { + if err := c.Login(ctx, u.User); err != nil { return nil, err } } diff --git a/plugins/inputs/vsphere/endpoint.go b/plugins/inputs/vsphere/endpoint.go index 90cd5644ce4fb..238e0bc7c07eb 100644 --- a/plugins/inputs/vsphere/endpoint.go +++ b/plugins/inputs/vsphere/endpoint.go @@ -79,6 +79,8 @@ type metricQResponse struct { metrics *performance.MetricList } +type multiError []error + // NewEndpoint returns a new connection to a vCenter based on the URL and configuration passed // as parameters. func NewEndpoint(ctx context.Context, parent *VSphere, url *url.URL) *Endpoint { @@ -144,6 +146,24 @@ func NewEndpoint(ctx context.Context, parent *VSphere, url *url.URL) *Endpoint { return &e } +func (m multiError) Error() string { + switch len(m) { + case 0: + return "No error recorded. Something is wrong!" + case 1: + return m[0].Error() + default: + s := "Multiple errors detected concurrently: " + for i, e := range m { + if i != 0 { + s += ", " + } + s += e.Error() + } + return s + } +} + func anythingEnabled(ex []string) bool { for _, s := range ex { if s == "*" { @@ -399,6 +419,14 @@ func (e *Endpoint) Close() { // Collect runs a round of data collections as specified in the configuration. func (e *Endpoint) Collect(ctx context.Context, acc telegraf.Accumulator) error { + // If we never managed to do a discovery, collection will be a no-op. Therefore, + // we need to check that a connection is available, or the collection will + // silently fail. + // + if _, err := e.clientFactory.GetClient(ctx); err != nil { + return err + } + e.collectMux.RLock() defer e.collectMux.RUnlock() @@ -523,12 +551,13 @@ func (e *Endpoint) collectResource(ctx context.Context, resourceType string, acc n, err := e.collectChunk(ctx, chunk, resourceType, res, acc) log.Printf("D! [input.vsphere]: Query returned %d metrics", n) if err != nil { + log.Println(err) return err } atomic.AddInt64(&count, int64(n)) return nil - }, 10) + }, e.Parent.CollectConcurrency) // Fill the input channel of the worker queue by running the chunking // logic implemented in chunker() @@ -537,20 +566,25 @@ func (e *Endpoint) collectResource(ctx context.Context, resourceType string, acc }) // Drain the pool. We're getting errors back. They should all be nil - var err error + var mux sync.Mutex + err := make(multiError, 0) wp.Drain(ctx, func(ctx context.Context, in interface{}) bool { if in != nil { - err = in.(error) + mux.Unlock() + defer mux.Unlock() + err = append(err, in.(error)) return false } return true }) - e.lastColls[resourceType] = now // Use value captured at the beginning to avoid blind spots. sw.Stop() SendInternalCounterWithTags("gather_count", e.URL.Host, internalTags, count) - return err + if len(err) > 0 { + return err + } + return nil } func (e *Endpoint) collectChunk(ctx context.Context, pqs []types.PerfQuerySpec, resourceType string, diff --git a/plugins/inputs/vsphere/vsphere.go b/plugins/inputs/vsphere/vsphere.go index 6df5253447054..6b24c265266cc 100644 --- a/plugins/inputs/vsphere/vsphere.go +++ b/plugins/inputs/vsphere/vsphere.go @@ -251,7 +251,6 @@ func (v *VSphere) Stop() { // the data collection and writes all metrics into the Accumulator passed as an argument. func (v *VSphere) Gather(acc telegraf.Accumulator) error { var wg sync.WaitGroup - for _, ep := range v.endpoints { wg.Add(1) go func(endpoint *Endpoint) { @@ -262,7 +261,9 @@ func (v *VSphere) Gather(acc telegraf.Accumulator) error { // No need to signal errors if we were merely canceled. err = nil } - acc.AddError(err) + if err != nil { + acc.AddError(err) + } }(ep) } From 99d11116580ec028a598b96eb4bcd91a55f4b412 Mon Sep 17 00:00:00 2001 From: prydin Date: Tue, 11 Sep 2018 11:30:39 -0400 Subject: [PATCH 100/100] RC1: Final tweaks and added datacenter tags --- plugins/inputs/vsphere/README.MD | 20 ++- plugins/inputs/vsphere/endpoint.go | 245 ++++++++++++++++++++--------- plugins/inputs/vsphere/vsphere.go | 56 ++++--- 3 files changed, 217 insertions(+), 104 deletions(-) diff --git a/plugins/inputs/vsphere/README.MD b/plugins/inputs/vsphere/README.MD index 1635058f44ecc..12332ea669efb 100644 --- a/plugins/inputs/vsphere/README.MD +++ b/plugins/inputs/vsphere/README.MD @@ -16,13 +16,13 @@ For example, to disable collection of VMs, add this: ``` # Read metrics from one or many vCenters [[inputs.vsphere]] - ## List of vCenter URLs to be monitored. These three lines must be uncommented + ## List of vCenter URLs to be monitored. These three lines must be uncommented ## and edited for the plugin to work. vcenters = [ "https://vcenter.local/sdk" ] username = "user@corp.local" password = "secret" - ############### VMs ############### + ## VMs ## Typical VM metrics (if omitted or empty, all metrics are collected) vm_metric_include = [ "cpu.demand.average", @@ -63,7 +63,7 @@ For example, to disable collection of VMs, add this: # vm_metric_exclude = [] ## Nothing is excluded by default # vm_instances = true ## true by default - ############### Hosts ############### + ## Hosts ## Typical host metrics (if omitted or empty, all metrics are collected) host_metric_include = [ "cpu.coreUtilization.average", @@ -116,28 +116,32 @@ For example, to disable collection of VMs, add this: # host_metric_exclude = [] ## Nothing excluded by default # host_instances = true ## true by default - ############### Clusters ############### + ## Clusters # cluster_metric_include = [] ## if omitted or empty, all metrics are collected # cluster_metric_exclude = [] ## Nothing excluded by default # cluster_instances = true ## true by default - ############### Datastores ############### + ## Datastores # datastore_metric_include = [] ## if omitted or empty, all metrics are collected # datastore_metric_exclude = [] ## Nothing excluded by default # datastore_instances = false ## false by default for Datastores only + ## Datacenters + datacenter_metric_include = [] ## if omitted or empty, all metrics are collected + datacenter_metric_exclude = [ "*" ] ## Datacenters are not collected by default. + # datacenter_instances = false ## false by default for Datastores only - ############### Plugin Settings ############### + ## Plugin Settings ## separator character to use for measurement and field names (default: "_") # separator = "_" ## number of objects to retreive per query for realtime resources (vms and hosts) ## set to 64 for vCenter 5.5 and 6.0 (default: 256) - # objects_per_query = 256 + # max_query_objects = 256 ## number of metrics to retreive per query for non-realtime resources (clusters and datastores) ## set to 64 for vCenter 5.5 and 6.0 (default: 256) - # metrics_per_query = 256 + # max_query_metrics = 256 ## number of go routines to use for collection and discovery of objects and metrics # collect_concurrency = 1 diff --git a/plugins/inputs/vsphere/endpoint.go b/plugins/inputs/vsphere/endpoint.go index 238e0bc7c07eb..cad4dec00a1b1 100644 --- a/plugins/inputs/vsphere/endpoint.go +++ b/plugins/inputs/vsphere/endpoint.go @@ -14,6 +14,7 @@ import ( "github.com/influxdata/telegraf/filter" "github.com/influxdata/telegraf" + "github.com/vmware/govmomi/object" "github.com/vmware/govmomi/performance" "github.com/vmware/govmomi/view" "github.com/vmware/govmomi/vim25/mo" @@ -28,7 +29,6 @@ type Endpoint struct { lastColls map[string]time.Time instanceInfo map[string]resourceInfo resourceKinds map[string]resourceKind - metricNames map[int32]string discoveryTicker *time.Ticker collectMux sync.RWMutex initialized bool @@ -39,6 +39,7 @@ type Endpoint struct { type resourceKind struct { name string pKey string + parentTag string enabled bool realTime bool sampling int32 @@ -62,11 +63,13 @@ type objectRef struct { ref types.ManagedObjectReference parentRef *types.ManagedObjectReference //Pointer because it must be nillable guest string + dcname string } type resourceInfo struct { - name string - metrics performance.MetricList + name string + metrics performance.MetricList + parentRef *types.ManagedObjectReference } type metricQRequest struct { @@ -83,7 +86,7 @@ type multiError []error // NewEndpoint returns a new connection to a vCenter based on the URL and configuration passed // as parameters. -func NewEndpoint(ctx context.Context, parent *VSphere, url *url.URL) *Endpoint { +func NewEndpoint(ctx context.Context, parent *VSphere, url *url.URL) (*Endpoint, error) { e := Endpoint{ URL: url, Parent: parent, @@ -94,9 +97,22 @@ func NewEndpoint(ctx context.Context, parent *VSphere, url *url.URL) *Endpoint { } e.resourceKinds = map[string]resourceKind{ + "datacenter": { + name: "datacenter", + pKey: "dcname", + parentTag: "", + enabled: anythingEnabled(parent.DatacenterMetricExclude), + realTime: false, + sampling: 300, + objects: make(objectMap), + filters: newFilterOrPanic(parent.DatacenterMetricInclude, parent.DatacenterMetricExclude), + collectInstances: parent.DatacenterInstances, + getObjects: getDatacenters, + }, "cluster": { name: "cluster", pKey: "clustername", + parentTag: "dcname", enabled: anythingEnabled(parent.ClusterMetricExclude), realTime: false, sampling: 300, @@ -108,6 +124,7 @@ func NewEndpoint(ctx context.Context, parent *VSphere, url *url.URL) *Endpoint { "host": { name: "host", pKey: "esxhostname", + parentTag: "clustername", enabled: anythingEnabled(parent.HostMetricExclude), realTime: true, sampling: 20, @@ -119,6 +136,7 @@ func NewEndpoint(ctx context.Context, parent *VSphere, url *url.URL) *Endpoint { "vm": { name: "vm", pKey: "vmname", + parentTag: "esxhostname", enabled: anythingEnabled(parent.VMMetricExclude), realTime: true, sampling: 20, @@ -141,9 +159,9 @@ func NewEndpoint(ctx context.Context, parent *VSphere, url *url.URL) *Endpoint { } // Start discover and other goodness - e.init(ctx) + err := e.init(ctx) - return &e + return &e, err } func (m multiError) Error() string { @@ -200,12 +218,15 @@ func (e *Endpoint) startDiscovery(ctx context.Context) { }() } -func (e *Endpoint) init(ctx context.Context) error { - - err := e.setupMetricIds(ctx) - if err != nil { - return err +func (e *Endpoint) initalDiscovery(ctx context.Context) { + err := e.discover(ctx) + if err != nil && err != context.Canceled { + log.Printf("E! [input.vsphere]: Error in discovery for %s: %v", e.URL.Host, err) } + e.startDiscovery(ctx) +} + +func (e *Endpoint) init(ctx context.Context) error { if e.Parent.ObjectDiscoveryInterval.Duration > 0 { @@ -214,43 +235,33 @@ func (e *Endpoint) init(ctx context.Context) error { // dataset on the first collection, but it solves the issue of the first collection timing out. if e.Parent.ForceDiscoverOnInit { log.Printf("D! [input.vsphere]: Running initial discovery and waiting for it to finish") - err := e.discover(ctx) - if err != nil { - return err - } - e.startDiscovery(ctx) + e.initalDiscovery(ctx) } else { // Otherwise, just run it in the background. We'll probably have an incomplete first metric // collection this way. - go func() { - err := e.discover(ctx) - if err != nil && err != context.Canceled { - log.Printf("E! [input.vsphere]: Error in discovery for %s: %v", e.URL.Host, err) - } - e.startDiscovery(ctx) - }() + go e.initalDiscovery(ctx) } } e.initialized = true return nil } -func (e *Endpoint) setupMetricIds(ctx context.Context) error { +func (e *Endpoint) getMetricNameMap(ctx context.Context) (map[int32]string, error) { client, err := e.clientFactory.GetClient(ctx) if err != nil { - return err + return nil, err } mn, err := client.Perf.CounterInfoByName(ctx) if err != nil { - return err + return nil, err } - e.metricNames = make(map[int32]string) + names := make(map[int32]string) for name, m := range mn { - e.metricNames[m.Key] = name + names[m.Key] = name } - return nil + return names, nil } func (e *Endpoint) getMetadata(ctx context.Context, in interface{}) interface{} { @@ -267,6 +278,41 @@ func (e *Endpoint) getMetadata(ctx context.Context, in interface{}) interface{} return &metricQResponse{metrics: &metrics, obj: rq.obj} } +func (e *Endpoint) getDatacenterName(ctx context.Context, client *Client, cache map[string]string, r types.ManagedObjectReference) string { + path := make([]string, 0) + returnVal := "" + here := r + for { + if name, ok := cache[here.Reference().String()]; ok { + // Populate cache for the entire chain of objects leading here. + returnVal = name + break + } + path = append(path, here.Reference().String()) + o := object.NewCommon(client.Client.Client, r) + var result mo.ManagedEntity + err := o.Properties(ctx, here, []string{"parent", "name"}, &result) + if err != nil { + log.Printf("W! [input.vsphere]: Error while resolving parent. Assuming no parent exists. Error: %s", err) + break + } + if result.Reference().Type == "Datacenter" { + // Populate cache for the entire chain of objects leading here. + returnVal = result.Name + break + } + if result.Parent == nil { + log.Printf("D! [input.vsphere]: No parent found for %s (ascending from %s)", here.Reference(), r.Reference()) + break + } + here = result.Parent.Reference() + } + for _, s := range path { + cache[s] = returnVal + } + return returnVal +} + func (e *Endpoint) discover(ctx context.Context) error { e.busy.Lock() defer e.busy.Unlock() @@ -274,8 +320,12 @@ func (e *Endpoint) discover(ctx context.Context) error { return ctx.Err() } + metricNames, err := e.getMetricNameMap(ctx) + if err != nil { + return err + } + sw := NewStopwatch("discover", e.URL.Host) - var err error client, err := e.clientFactory.GetClient(ctx) if err != nil { @@ -286,9 +336,11 @@ func (e *Endpoint) discover(ctx context.Context) error { instInfo := make(map[string]resourceInfo) resourceKinds := make(map[string]resourceKind) + dcNameCache := make(map[string]string) // Populate resource objects, and endpoint instance info. for k, res := range e.resourceKinds { + log.Printf("D! [input.vsphere] Discovering resources for %s", res.name) // Need to do this for all resource types even if they are not enabled (but datastore) if res.enabled || (k != "datastore" && k != "vm") { objects, err := res.getObjects(ctx, client.Root) @@ -296,6 +348,16 @@ func (e *Endpoint) discover(ctx context.Context) error { return err } + // Fill in datacenter names where available (no need to do it for Datacenters) + if res.name != "Datacenter" { + for k, obj := range objects { + if obj.parentRef != nil { + obj.dcname = e.getDatacenterName(ctx, client, dcNameCache, *obj.parentRef) + objects[k] = obj + } + } + } + // Set up a worker pool for processing metadata queries concurrently wp := NewWorkerPool(10) wp.Run(ctx, e.getMetadata, e.Parent.DiscoverConcurrency) @@ -318,14 +380,14 @@ func (e *Endpoint) discover(ctx context.Context) error { if m.Instance != "" && !res.collectInstances { continue } - if res.filters.Match(e.metricNames[m.CounterId]) { + if res.filters.Match(metricNames[m.CounterId]) { mList = append(mList, m) } } } - instInfo[resp.obj.ref.Value] = resourceInfo{name: resp.obj.name, metrics: mList} + instInfo[resp.obj.ref.Value] = resourceInfo{name: resp.obj.name, metrics: mList, parentRef: resp.obj.parentRef} case error: - log.Printf("[input.vsphere]: Error while discovering resources: %s", resp) + log.Printf("W! [input.vsphere]: Error while discovering resources: %s", resp) return false } return true @@ -348,16 +410,46 @@ func (e *Endpoint) discover(ctx context.Context) error { return nil } +func getDatacenters(ctx context.Context, root *view.ContainerView) (objectMap, error) { + var resources []mo.Datacenter + err := root.Retrieve(ctx, []string{"Datacenter"}, []string{"name", "parent"}, &resources) + if err != nil { + return nil, err + } + m := make(objectMap, len(resources)) + for _, r := range resources { + m[r.ExtensibleManagedObject.Reference().Value] = objectRef{ + name: r.Name, ref: r.ExtensibleManagedObject.Reference(), parentRef: r.Parent, dcname: r.Name} + } + return m, nil +} + func getClusters(ctx context.Context, root *view.ContainerView) (objectMap, error) { var resources []mo.ClusterComputeResource err := root.Retrieve(ctx, []string{"ClusterComputeResource"}, []string{"name", "parent"}, &resources) if err != nil { return nil, err } + cache := make(map[string]*types.ManagedObjectReference) m := make(objectMap, len(resources)) for _, r := range resources { + // We're not interested in the immediate parent (a folder), but the data center. + p, ok := cache[r.Parent.Value] + if !ok { + o := object.NewFolder(root.Client(), *r.Parent) + var folder mo.Folder + err := o.Properties(ctx, *r.Parent, []string{"parent"}, &folder) + if err != nil { + log.Printf("W! [input.vsphere] Error while getting folder parent: %e", err) + p = nil + } else { + pp := folder.Parent.Reference() + p = &pp + cache[r.Parent.Value] = p + } + } m[r.ExtensibleManagedObject.Reference().Value] = objectRef{ - name: r.Name, ref: r.ExtensibleManagedObject.Reference(), parentRef: r.Parent} + name: r.Name, ref: r.ExtensibleManagedObject.Reference(), parentRef: p} } return m, nil } @@ -551,7 +643,6 @@ func (e *Endpoint) collectResource(ctx context.Context, resourceType string, acc n, err := e.collectChunk(ctx, chunk, resourceType, res, acc) log.Printf("D! [input.vsphere]: Query returned %d metrics", n) if err != nil { - log.Println(err) return err } atomic.AddInt64(&count, int64(n)) @@ -678,6 +769,16 @@ func (e *Endpoint) collectChunk(ctx context.Context, pqs []types.PerfQuerySpec, return count, nil } +func (e *Endpoint) getParent(obj resourceInfo) (resourceInfo, bool) { + p := obj.parentRef + if p == nil { + log.Printf("D! [input.vsphere] No parent found for %s", obj.name) + return resourceInfo{}, false + } + r, ok := e.instanceInfo[p.Value] + return r, ok +} + func (e *Endpoint) populateTags(objectRef *objectRef, resourceType string, resource *resourceKind, t map[string]string, v *performance.MetricSeries) { // Map name of object. if resource.pKey != "" { @@ -687,51 +788,49 @@ func (e *Endpoint) populateTags(objectRef *objectRef, resourceType string, resou // Map parent reference parent, found := e.instanceInfo[objectRef.parentRef.Value] if found { - switch resourceType { - case "host": - t["clustername"] = parent.name - break - - case "vm": - t["guest"] = objectRef.guest - t["esxhostname"] = parent.name - hostRes := e.resourceKinds["host"] - hostRef, ok := hostRes.objects[objectRef.parentRef.Value] - if ok { - cluster, ok := e.instanceInfo[hostRef.parentRef.Value] - if ok { - t["clustername"] = cluster.name - } + t[resource.parentTag] = parent.name + if resourceType == "vm" { + if objectRef.guest != "" { + t["guest"] = objectRef.guest + } + if c, ok := e.getParent(parent); ok { + t["clustername"] = c.name } - break } } + // Fill in Datacenter name + if objectRef.dcname != "" { + t["dcname"] = objectRef.dcname + } + // Determine which point tag to map to the instance name := v.Name + instance := "instance-total" if v.Instance != "" { - if strings.HasPrefix(name, "cpu.") { - t["cpu"] = v.Instance - } else if strings.HasPrefix(name, "datastore.") { - t["lun"] = v.Instance - } else if strings.HasPrefix(name, "disk.") { - t["disk"] = cleanDiskTag(v.Instance) - } else if strings.HasPrefix(name, "net.") { - t["interface"] = v.Instance - } else if strings.HasPrefix(name, "storageAdapter.") { - t["adapter"] = v.Instance - } else if strings.HasPrefix(name, "storagePath.") { - t["path"] = v.Instance - } else if strings.HasPrefix(name, "sys.resource") { - t["resource"] = v.Instance - } else if strings.HasPrefix(name, "vflashModule.") { - t["module"] = v.Instance - } else if strings.HasPrefix(name, "virtualDisk.") { - t["disk"] = v.Instance - } else { - // default to instance - t["instance"] = v.Instance - } + instance = v.Instance + } + if strings.HasPrefix(name, "cpu.") { + t["cpu"] = instance + } else if strings.HasPrefix(name, "datastore.") { + t["lun"] = instance + } else if strings.HasPrefix(name, "disk.") { + t["disk"] = cleanDiskTag(instance) + } else if strings.HasPrefix(name, "net.") { + t["interface"] = instance + } else if strings.HasPrefix(name, "storageAdapter.") { + t["adapter"] = instance + } else if strings.HasPrefix(name, "storagePath.") { + t["path"] = instance + } else if strings.HasPrefix(name, "sys.resource") { + t["resource"] = instance + } else if strings.HasPrefix(name, "vflashModule.") { + t["module"] = instance + } else if strings.HasPrefix(name, "virtualDisk.") { + t["disk"] = instance + } else if v.Instance != "" { + // default + t["instance"] = v.Instance } } diff --git a/plugins/inputs/vsphere/vsphere.go b/plugins/inputs/vsphere/vsphere.go index 6b24c265266cc..26af1e8cc4eff 100644 --- a/plugins/inputs/vsphere/vsphere.go +++ b/plugins/inputs/vsphere/vsphere.go @@ -16,22 +16,25 @@ import ( // VSphere is the top level type for the vSphere input plugin. It contains all the configuration // and a list of connected vSphere endpoints type VSphere struct { - Vcenters []string - Username string - Password string - ClusterInstances bool - ClusterMetricInclude []string - ClusterMetricExclude []string - HostInstances bool - HostMetricInclude []string - HostMetricExclude []string - VMInstances bool `toml:"vm_instances"` - VMMetricInclude []string `toml:"vm_metric_include"` - VMMetricExclude []string `toml:"vm_metric_exclude"` - DatastoreInstances bool - DatastoreMetricInclude []string - DatastoreMetricExclude []string - Separator string + Vcenters []string + Username string + Password string + DatacenterInstances bool + DatacenterMetricInclude []string + DatacenterMetricExclude []string + ClusterInstances bool + ClusterMetricInclude []string + ClusterMetricExclude []string + HostInstances bool + HostMetricInclude []string + HostMetricExclude []string + VMInstances bool `toml:"vm_instances"` + VMMetricInclude []string `toml:"vm_metric_include"` + VMMetricExclude []string `toml:"vm_metric_exclude"` + DatastoreInstances bool + DatastoreMetricInclude []string + DatastoreMetricExclude []string + Separator string MaxQueryObjects int MaxQueryMetrics int @@ -55,7 +58,7 @@ var sampleConfig = ` username = "user@corp.local" password = "secret" - ############### VMs ############### + ## VMs ## Typical VM metrics (if omitted or empty, all metrics are collected) vm_metric_include = [ "cpu.demand.average", @@ -96,7 +99,7 @@ var sampleConfig = ` # vm_metric_exclude = [] ## Nothing is excluded by default # vm_instances = true ## true by default - ############### Hosts ############### + ## Hosts ## Typical host metrics (if omitted or empty, all metrics are collected) host_metric_include = [ "cpu.coreUtilization.average", @@ -149,18 +152,22 @@ var sampleConfig = ` # host_metric_exclude = [] ## Nothing excluded by default # host_instances = true ## true by default - ############### Clusters ############### + ## Clusters # cluster_metric_include = [] ## if omitted or empty, all metrics are collected # cluster_metric_exclude = [] ## Nothing excluded by default # cluster_instances = true ## true by default - ############### Datastores ############### + ## Datastores # datastore_metric_include = [] ## if omitted or empty, all metrics are collected # datastore_metric_exclude = [] ## Nothing excluded by default # datastore_instances = false ## false by default for Datastores only + ## Datacenters + datacenter_metric_include = [] ## if omitted or empty, all metrics are collected + datacenter_metric_exclude = [ "*" ] ## Datacenters are not collected by default. + # datacenter_instances = false ## false by default for Datastores only - ############### Plugin Settings ############### + ## Plugin Settings ## separator character to use for measurement and field names (default: "_") # separator = "_" @@ -220,8 +227,11 @@ func (v *VSphere) Start(acc telegraf.Accumulator) error { if err != nil { return err } - - v.endpoints[i] = NewEndpoint(ctx, v, u) + ep, err := NewEndpoint(ctx, v, u) + if err != nil { + return err + } + v.endpoints[i] = ep } return nil }