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