Merge pull request #14 from DG-i/master

Small adjustments
This commit is contained in:
Adrian Todorov 2017-05-11 11:57:57 +02:00 committed by GitHub
commit ddc76df434
4 changed files with 89 additions and 73 deletions

13
.gitignore vendored Normal file
View File

@ -0,0 +1,13 @@
### Basic ignore file
# Binaries for programs and plugins
vsphere-influxdb
# Test binary, build with `go test -c`
*.test
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
# Configuration file
vsphere-influxdb.json

View File

@ -25,7 +25,7 @@ go get github.com/oxalide/vsphere-influxdb-go
This will install the project in your $GOBIN($GOPATH/bin). If you have appended $GOBIN to your $PATH, you will be able to call it directly. Otherwise, you'll have to call it with its full path. This will install the project in your $GOBIN($GOPATH/bin). If you have appended $GOBIN to your $PATH, you will be able to call it directly. Otherwise, you'll have to call it with its full path.
Example: Example:
``` ```
vsphere-influxdb-go vsphere-influxdb-go
``` ```
or : or :
``` ```
@ -45,9 +45,9 @@ To see all available metrics, check out [this](http://www.virten.net/2015/05/vsp
Note: Not all metrics are available directly, you might need to change your metric collection level. Note: Not all metrics are available directly, you might need to change your metric collection level.
A table with the level needed for each metric is availble [here](http://www.virten.net/2015/05/which-performance-counters-are-available-in-each-statistic-level/), and you can find a PowerCLI script that changes the collect level [here](http://www.valcolabs.com/2012/02/06/modify-historical-statistics-level-using-powercli/) A table with the level needed for each metric is availble [here](http://www.virten.net/2015/05/which-performance-counters-are-available-in-each-statistic-level/), and you can find a PowerCLI script that changes the collect level [here](http://www.valcolabs.com/2012/02/06/modify-historical-statistics-level-using-powercli/)
An example of configuration file is [here](./vsphere-influxdb-go.json). An example of configuration file is [here](./vsphere-influxdb.json.sample).
You need to place it at /etc/*binaryname*.json (/etc/vsphere-influxdb-go.json by default) You need to place it at /etc/*binaryname*.json (/etc/vsphere-influxdb.json by default) or you can specify a different location using the config flag.
# Run as a service # Run as a service

View File

@ -21,8 +21,6 @@ import (
"encoding/json" "encoding/json"
"flag" "flag"
"fmt" "fmt"
"github.com/davecgh/go-spew/spew"
"golang.org/x/net/context"
"log" "log"
"math" "math"
"net/url" "net/url"
@ -32,6 +30,9 @@ import (
"strings" "strings"
"time" "time"
"github.com/davecgh/go-spew/spew"
"golang.org/x/net/context"
influxclient "github.com/influxdata/influxdb/client/v2" influxclient "github.com/influxdata/influxdb/client/v2"
"github.com/vmware/govmomi" "github.com/vmware/govmomi"
"github.com/vmware/govmomi/property" "github.com/vmware/govmomi/property"
@ -46,7 +47,7 @@ const (
description = "send vsphere stats to influxdb" description = "send vsphere stats to influxdb"
) )
// Configuration // Configuration is used to store config data
type Configuration struct { type Configuration struct {
VCenters []*VCenter VCenters []*VCenter
Metrics []Metric Metrics []Metric
@ -55,7 +56,7 @@ type Configuration struct {
InfluxDB InfluxDB InfluxDB InfluxDB
} }
// InfluxDB description // InfluxDB is used for InfluxDB connections
type InfluxDB struct { type InfluxDB struct {
Hostname string Hostname string
Username string Username string
@ -63,7 +64,7 @@ type InfluxDB struct {
Database string Database string
} }
// VCenter description // VCenter for VMware vCenter connections
type VCenter struct { type VCenter struct {
Hostname string Hostname string
Username string Username string
@ -71,30 +72,30 @@ type VCenter struct {
MetricGroups []*MetricGroup MetricGroups []*MetricGroup
} }
// Metric Definition // MetricDef metric definition
type MetricDef struct { type MetricDef struct {
Metric string Metric string
Instances string Instances string
Key int32 Key int32
} }
// Metrics description in config var vmRefs []types.ManagedObjectReference
var vm_refs []types.ManagedObjectReference
var debug bool var debug bool
// Metric is used for metrics retrieval
type Metric struct { type Metric struct {
ObjectType []string ObjectType []string
Definition []MetricDef Definition []MetricDef
} }
// Metric Grouping for retrieval // MetricGroup is used for grouping metrics retrieval
type MetricGroup struct { type MetricGroup struct {
ObjectType string ObjectType string
Metrics []MetricDef Metrics []MetricDef
Mor []types.ManagedObjectReference Mor []types.ManagedObjectReference
} }
// Informations to query about an entity // EntityQuery are informations to query about an entity
type EntityQuery struct { type EntityQuery struct {
Name string Name string
Entity types.ManagedObjectReference Entity types.ManagedObjectReference
@ -106,6 +107,7 @@ var dependencies = []string{}
var stdlog, errlog *log.Logger var stdlog, errlog *log.Logger
// Connect to the actual vCenter connection used to query data
func (vcenter *VCenter) Connect() (*govmomi.Client, error) { func (vcenter *VCenter) Connect() (*govmomi.Client, error) {
// Prepare vCenter Connections // Prepare vCenter Connections
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
@ -126,7 +128,7 @@ func (vcenter *VCenter) Connect() (*govmomi.Client, error) {
return client, nil return client, nil
} }
// Initialise vcenter // Init the VCenter connection
func (vcenter *VCenter) Init(config Configuration) { func (vcenter *VCenter) Init(config Configuration) {
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
defer cancel() defer cancel()
@ -243,33 +245,33 @@ func (vcenter *VCenter) Query(config Configuration, InfluxDBClient influxclient.
mors = append(mors, containerView.View...) mors = append(mors, containerView.View...)
} }
// Create MORS for each object type // Create MORS for each object type
vm_refs := []types.ManagedObjectReference{} vmRefs := []types.ManagedObjectReference{}
host_refs := []types.ManagedObjectReference{} hostRefs := []types.ManagedObjectReference{}
cluster_refs := []types.ManagedObjectReference{} clusterRefs := []types.ManagedObjectReference{}
new_mors := []types.ManagedObjectReference{} newMors := []types.ManagedObjectReference{}
spew.Dump(mors) spew.Dump(mors)
// Assign each MORS type to a specific array // Assign each MORS type to a specific array
for _, mor := range mors { for _, mor := range mors {
if mor.Type == "VirtualMachine" { if mor.Type == "VirtualMachine" {
vm_refs = append(vm_refs, mor) vmRefs = append(vmRefs, mor)
new_mors = append(new_mors, mor) newMors = append(newMors, mor)
} else if mor.Type == "HostSystem" { } else if mor.Type == "HostSystem" {
host_refs = append(host_refs, mor) hostRefs = append(hostRefs, mor)
new_mors = append(new_mors, mor) newMors = append(newMors, mor)
} else if mor.Type == "ClusterComputeResource" { } else if mor.Type == "ClusterComputeResource" {
cluster_refs = append(cluster_refs, mor) clusterRefs = append(clusterRefs, mor)
} }
} }
// Copy the mors without the clusters // Copy the mors without the clusters
mors = new_mors mors = newMors
pc := property.DefaultCollector(client.Client) pc := property.DefaultCollector(client.Client)
// Retrieve properties for all vms // Retrieve properties for all vms
var vmmo []mo.VirtualMachine var vmmo []mo.VirtualMachine
err = pc.Retrieve(ctx, vm_refs, []string{"summary"}, &vmmo) err = pc.Retrieve(ctx, vmRefs, []string{"summary"}, &vmmo)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
return return
@ -277,7 +279,7 @@ func (vcenter *VCenter) Query(config Configuration, InfluxDBClient influxclient.
// Retrieve properties for hosts // Retrieve properties for hosts
var hsmo []mo.HostSystem var hsmo []mo.HostSystem
err = pc.Retrieve(ctx, host_refs, []string{"summary"}, &hsmo) err = pc.Retrieve(ctx, hostRefs, []string{"summary"}, &hsmo)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
return return
@ -287,12 +289,12 @@ func (vcenter *VCenter) Query(config Configuration, InfluxDBClient influxclient.
vmToCluster := make(map[types.ManagedObjectReference]string) vmToCluster := make(map[types.ManagedObjectReference]string)
// Retrieve properties for clusters, if any // Retrieve properties for clusters, if any
if len(cluster_refs) > 0 { if len(clusterRefs) > 0 {
if debug == true { if debug == true {
stdlog.Println("going inside clusters") stdlog.Println("going inside clusters")
} }
var clmo []mo.ClusterComputeResource var clmo []mo.ClusterComputeResource
err = pc.Retrieve(ctx, cluster_refs, []string{"name", "configuration"}, &clmo) err = pc.Retrieve(ctx, clusterRefs, []string{"name", "configuration"}, &clmo)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
return return
@ -301,7 +303,7 @@ func (vcenter *VCenter) Query(config Configuration, InfluxDBClient influxclient.
if debug == true { if debug == true {
stdlog.Println("---cluster name - you should see every cluster here---") stdlog.Println("---cluster name - you should see every cluster here---")
stdlog.Println(cl.Name) stdlog.Println(cl.Name)
stdlog.Println("You should see the cluster object, clsuter configuration object, and cluster configuration dasvmconfig which should contain all VMs") stdlog.Println("You should see the cluster object, cluster configuration object, and cluster configuration dasvmconfig which should contain all VMs")
spew.Dump(cl) spew.Dump(cl)
spew.Dump(cl.Configuration) spew.Dump(cl.Configuration)
spew.Dump(cl.Configuration.DasVmConfig) spew.Dump(cl.Configuration.DasVmConfig)
@ -318,32 +320,32 @@ func (vcenter *VCenter) Query(config Configuration, InfluxDBClient influxclient.
} }
// Retrieve properties for the hosts // Retrieve properties for the hosts
host_summary := make(map[types.ManagedObjectReference]map[string]string) hostSummary := make(map[types.ManagedObjectReference]map[string]string)
host_extra_metrics := make(map[types.ManagedObjectReference]map[string]int64) hostExtraMetrics := make(map[types.ManagedObjectReference]map[string]int64)
for _, host := range hsmo { for _, host := range hsmo {
host_summary[host.Self] = make(map[string]string) hostSummary[host.Self] = make(map[string]string)
host_summary[host.Self]["name"] = host.Summary.Config.Name hostSummary[host.Self]["name"] = host.Summary.Config.Name
host_extra_metrics[host.Self] = make(map[string]int64) hostExtraMetrics[host.Self] = make(map[string]int64)
host_extra_metrics[host.Self]["cpu_corecount_total"] = int64(host.Summary.Hardware.NumCpuThreads) hostExtraMetrics[host.Self]["cpu_corecount_total"] = int64(host.Summary.Hardware.NumCpuThreads)
} }
// Initialize the map that will hold all extra tags // Initialize the map that will hold all extra tags
vm_summary := make(map[types.ManagedObjectReference]map[string]string) vmSummary := make(map[types.ManagedObjectReference]map[string]string)
// Assign extra details per VM in vm_summary // Assign extra details per VM in vmSummary
for _, vm := range vmmo { for _, vm := range vmmo {
vm_summary[vm.Self] = make(map[string]string) vmSummary[vm.Self] = make(map[string]string)
// Ugly way to extract datastore value // Ugly way to extract datastore value
re, err := regexp.Compile(`\[(.*?)\]`) re, err := regexp.Compile(`\[(.*?)\]`)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
} }
vm_summary[vm.Self]["datastore"] = strings.Replace(strings.Replace(re.FindString(fmt.Sprintln(vm.Summary.Config)), "[", "", -1), "]", "", -1) vmSummary[vm.Self]["datastore"] = strings.Replace(strings.Replace(re.FindString(fmt.Sprintln(vm.Summary.Config)), "[", "", -1), "]", "", -1)
if vmToCluster[vm.Self] != "" { if vmToCluster[vm.Self] != "" {
vm_summary[vm.Self]["cluster"] = vmToCluster[vm.Self] vmSummary[vm.Self]["cluster"] = vmToCluster[vm.Self]
} }
vm_summary[vm.Self]["esx"] = host_summary[*vm.Summary.Runtime.Host]["name"] vmSummary[vm.Self]["esx"] = hostSummary[*vm.Summary.Runtime.Host]["name"]
} }
// get object names // get object names
@ -392,9 +394,9 @@ func (vcenter *VCenter) Query(config Configuration, InfluxDBClient influxclient.
queries := []types.PerfQuerySpec{} queries := []types.PerfQuerySpec{}
// Common parameters // Common parameters
intervalIdint := 20 intervalIDint := 20
var intervalId int32 var intervalID int32
intervalId = int32(intervalIdint) intervalID = int32(intervalIDint)
endTime := time.Now().Add(time.Duration(-1) * time.Second) endTime := time.Now().Add(time.Duration(-1) * time.Second)
startTime := endTime.Add(time.Duration(-config.Interval) * time.Second) startTime := endTime.Add(time.Duration(-config.Interval) * time.Second)
@ -409,7 +411,7 @@ func (vcenter *VCenter) Query(config Configuration, InfluxDBClient influxclient.
} }
} }
} }
queries = append(queries, types.PerfQuerySpec{Entity: mor, StartTime: &startTime, EndTime: &endTime, MetricId: metricIds, IntervalId: intervalId}) queries = append(queries, types.PerfQuerySpec{Entity: mor, StartTime: &startTime, EndTime: &endTime, MetricId: metricIds, IntervalId: intervalID})
} }
// Query the performances // Query the performances
@ -445,19 +447,19 @@ func (vcenter *VCenter) Query(config Configuration, InfluxDBClient influxclient.
tags := map[string]string{"host": vcName, "name": name} tags := map[string]string{"host": vcName, "name": name}
// Add extra per VM tags // Add extra per VM tags
if summary, ok := vm_summary[pem.Entity]; ok { if summary, ok := vmSummary[pem.Entity]; ok {
for key, tag := range summary { for key, tag := range summary {
tags[key] = tag tags[key] = tag
} }
} }
if summary, ok := host_summary[pem.Entity]; ok { if summary, ok := hostSummary[pem.Entity]; ok {
for key, tag := range summary { for key, tag := range summary {
tags[key] = tag tags[key] = tag
} }
} }
special_fields := make(map[string]map[string]map[string]map[string]interface{}) specialFields := make(map[string]map[string]map[string]map[string]interface{})
special_tags := make(map[string]map[string]map[string]map[string]string) specialTags := make(map[string]map[string]map[string]map[string]string)
nowTime := time.Now() nowTime := time.Now()
for _, baseserie := range pem.Value { for _, baseserie := range pem.Value {
serie := baseserie.(*types.PerfMetricIntSeries) serie := baseserie.(*types.PerfMetricIntSeries)
@ -487,34 +489,34 @@ func (vcenter *VCenter) Query(config Configuration, InfluxDBClient influxclient.
fields[influxMetricName] = value fields[influxMetricName] = value
} else { } else {
// init maps // init maps
if special_fields[measurementName] == nil { if specialFields[measurementName] == nil {
special_fields[measurementName] = make(map[string]map[string]map[string]interface{}) specialFields[measurementName] = make(map[string]map[string]map[string]interface{})
special_tags[measurementName] = make(map[string]map[string]map[string]string) specialTags[measurementName] = make(map[string]map[string]map[string]string)
} }
if special_fields[measurementName][tags["name"]] == nil { if specialFields[measurementName][tags["name"]] == nil {
special_fields[measurementName][tags["name"]] = make(map[string]map[string]interface{}) specialFields[measurementName][tags["name"]] = make(map[string]map[string]interface{})
special_tags[measurementName][tags["name"]] = make(map[string]map[string]string) specialTags[measurementName][tags["name"]] = make(map[string]map[string]string)
} }
if special_fields[measurementName][tags["name"]][instanceName] == nil { if specialFields[measurementName][tags["name"]][instanceName] == nil {
special_fields[measurementName][tags["name"]][instanceName] = make(map[string]interface{}) specialFields[measurementName][tags["name"]][instanceName] = make(map[string]interface{})
special_tags[measurementName][tags["name"]][instanceName] = make(map[string]string) specialTags[measurementName][tags["name"]][instanceName] = make(map[string]string)
} }
special_fields[measurementName][tags["name"]][instanceName][influxMetricName] = value specialFields[measurementName][tags["name"]][instanceName][influxMetricName] = value
for k, v := range tags { for k, v := range tags {
special_tags[measurementName][tags["name"]][instanceName][k] = v specialTags[measurementName][tags["name"]][instanceName][k] = v
} }
special_tags[measurementName][tags["name"]][instanceName]["instance"] = instanceName specialTags[measurementName][tags["name"]][instanceName]["instance"] = instanceName
} }
} }
if metrics, ok := host_extra_metrics[pem.Entity]; ok { if metrics, ok := hostExtraMetrics[pem.Entity]; ok {
for key, value := range metrics { for key, value := range metrics {
fields[key] = value fields[key] = value
} }
@ -527,10 +529,10 @@ func (vcenter *VCenter) Query(config Configuration, InfluxDBClient influxclient.
} }
bp.AddPoint(pt) bp.AddPoint(pt)
for measurement, v := range special_fields { for measurement, v := range specialFields {
for name, metric := range v { for name, metric := range v {
for instance, value := range metric { for instance, value := range metric {
pt2, err := influxclient.NewPoint(measurement, special_tags[measurement][name][instance], value, time.Now()) pt2, err := influxclient.NewPoint(measurement, specialTags[measurement][name][instance], value, time.Now())
if err != nil { if err != nil {
errlog.Println(err) errlog.Println(err)
} }
@ -582,7 +584,7 @@ func max(n ...int64) int64 {
} }
func sum(n ...int64) int64 { func sum(n ...int64) int64 {
var total int64 = 0 var total int64
for _, i := range n { for _, i := range n {
if i > 0 { if i > 0 {
total += i total += i
@ -592,12 +594,12 @@ func sum(n ...int64) int64 {
} }
func average(n ...int64) int64 { func average(n ...int64) int64 {
var total int64 = 0 var total int64
var count int64 = 0 var count int64
for _, i := range n { for _, i := range n {
if i >= 0 { if i >= 0 {
count += 1 count++
total += i total++
} }
} }
favg := float64(total) / float64(count) favg := float64(total) / float64(count)
@ -613,6 +615,7 @@ func queryVCenter(vcenter VCenter, config Configuration, InfluxDBClient influxcl
func main() { func main() {
flag.BoolVar(&debug, "debug", false, "Debug mode") flag.BoolVar(&debug, "debug", false, "Debug mode")
var cfgFile = flag.String("config", "/etc/"+path.Base(os.Args[0])+".json", "Config file to use. Default is /etc/"+path.Base(os.Args[0])+".json")
flag.Parse() flag.Parse()
stdlog = log.New(os.Stdout, "", log.Ldate|log.Ltime) stdlog = log.New(os.Stdout, "", log.Ldate|log.Ltime)
@ -620,16 +623,16 @@ func main() {
stdlog.Println("Starting :", path.Base(os.Args[0])) stdlog.Println("Starting :", path.Base(os.Args[0]))
// read the configuration // read the configuration
file, err := os.Open("/etc/" + path.Base(os.Args[0]) + ".json") file, err := os.Open(*cfgFile)
if err != nil { if err != nil {
errlog.Println("Could not open configuration file") errlog.Println("Could not open configuration file " + *cfgFile)
errlog.Println(err) errlog.Println(err)
} }
jsondec := json.NewDecoder(file) jsondec := json.NewDecoder(file)
config := Configuration{} config := Configuration{}
err = jsondec.Decode(&config) err = jsondec.Decode(&config)
if err != nil { if err != nil {
errlog.Println("Could not decode configuration file") errlog.Println("Could not decode configuration file " + *cfgFile)
errlog.Println(err) errlog.Println(err)
} }
@ -645,7 +648,7 @@ func main() {
errlog.Println("Could not connect to InfluxDB") errlog.Println("Could not connect to InfluxDB")
errlog.Println(err) errlog.Println(err)
} else { } else {
stdlog.Println("Successfully connected to Influx\n") stdlog.Println("Successfully connected to Influx")
} }
for _, vcenter := range config.VCenters { for _, vcenter := range config.VCenters {
queryVCenter(*vcenter, config, InfluxDBClient) queryVCenter(*vcenter, config, InfluxDBClient)