package main import ( "context" "flag" "fmt" "log" "net/url" "os" "reflect" "strings" "time" _ "time/tzdata" "github.com/vmware/govmomi" "github.com/vmware/govmomi/object" "github.com/vmware/govmomi/performance" "github.com/vmware/govmomi/property" "github.com/vmware/govmomi/view" "github.com/vmware/govmomi/vim25" "github.com/vmware/govmomi/vim25/mo" "github.com/vmware/govmomi/vim25/types" ) var ( c *govmomi.Client ctx context.Context cancel context.CancelFunc location *time.Location sha1ver string // sha1 revision used to build the program buildTime string // when the executable was built ) /* func findPerfCounter(perfManager *vim25.PerformanceManager, counterName string) (*vim25.PerfCounterInfo, error) { perfCounters, err := perfManager.QueryPerfCounter(context.Background(), nil) if err != nil { return nil, err } for _, counter := range perfCounters { if counter.NameInfo.GetElement().Key == counterName { return &counter, nil } } return nil, fmt.Errorf("Performance counter '%s' not found", counterName) } */ func findVMByName(ctx context.Context, client *vim25.Client, vmName string) ([]mo.VirtualMachine, error) { m := view.NewManager(client) vms, err := m.CreateContainerView(ctx, client.ServiceContent.RootFolder, []string{"VirtualMachine"}, true) if err != nil { return nil, err } defer vms.Destroy(ctx) var matchingVMs []mo.VirtualMachine err = vms.Retrieve(ctx, []string{"VirtualMachine"}, []string{"name"}, &matchingVMs) if err != nil { return nil, err } var result []mo.VirtualMachine for _, vm := range matchingVMs { if vm.Name == vmName { result = append(result, vm) } } return result, nil } func findClusterByName(ctx context.Context, client *vim25.Client, name string) mo.ClusterComputeResource { // Create a container view so that we can search vCenter m := view.NewManager(client) cv, _ := m.CreateContainerView(ctx, c.ServiceContent.RootFolder, []string{"ClusterComputeResource"}, true) var clusters []mo.ClusterComputeResource log.Printf("Searching for Cluster '%s'\n", name) err := cv.Retrieve(ctx, []string{"ClusterComputeResource"}, []string{"summary", "name"}, &clusters) if err != nil { log.Printf("Failed searching for Cluster %s : %s\n", name, err) return mo.ClusterComputeResource{} } else { for _, cluster := range clusters { if cluster.Name == name { log.Printf("Found corresponding Cluster with MoRef '%s'\n", cluster.Reference()) return cluster } } } // If we reached here then we didn't find the Cluster return mo.ClusterComputeResource{} } func getMetricIds(counters []types.PerfCounterInfo) []types.PerfMetricId { var metricIds []types.PerfMetricId for _, counter := range counters { var metricId types.PerfMetricId metricId.CounterId = counter.Key metricIds = append(metricIds, metricId) } fmt.Printf("metric IDs : '%v'\n", metricIds) return metricIds } func getSpecificVMMetrics(ctx context.Context, c *vim25.Client, vmName string) { //perfManager := c.ServiceContent.PerfManager perfManager := performance.NewManager(c) var perfCounterInfos []types.PerfCounterInfo startTime := time.Now().Add(-time.Hour) timeNow := time.Now() // Find the performance counters for CPU Entitlement, CPU Consumed, and CPU Demand counterNames := []string{"cpu.entitlement.latest", "cpu.usage.average", "cpu.demand.average", "cpu.readiness.average"} //counterNames := []string{"cpu.entitlement.latest", "cpu.usage.average"} // Retrieve counters name list counters, err := perfManager.CounterInfoByName(ctx) if err != nil { //return err fmt.Printf("Error getting counter info : '%s'\n", err) return } fmt.Printf("Found '%d' counters\n", len(counters)) for _, counter := range counters { for _, counterName := range counterNames { if counter.Name() == counterName { groupInfo := counter.GroupInfo.GetElementDescription() nameInfo := counter.NameInfo.GetElementDescription() fmt.Printf("Found counter matching name '%s' : '%s';'%s'\n", counterName, groupInfo, nameInfo) perfCounterInfos = append(perfCounterInfos, *counter) } else { //fmt.Printf("Ignoring '%s' : '%s'\n", counter.StatsType, counter.Name()) } } } fmt.Printf("Ended search with %d counter infos\n", len(perfCounterInfos)) // Find the VM by its name vmMatches, err := findVMByName(ctx, c, vmName) if err != nil { fmt.Println("Error finding VM:", err) return } for _, vm := range vmMatches { // Create PerfQuerySpec as per https://github.com/vmware/govmomi/blob/main/performance/example_test.go spec := types.PerfQuerySpec{ //MaxSample: 180, // In one hour there are 180 instances of 20 second intervals MetricId: getMetricIds(perfCounterInfos), IntervalId: 20, StartTime: &startTime, // 1 hour ago EndTime: &timeNow, } // Query metrics sample, err := perfManager.SampleByName(ctx, spec, counterNames, []types.ManagedObjectReference{vm.Reference()}) if err != nil { fmt.Printf("Error getting SampleByName : '%s'\n", err) return } fmt.Printf("Sample result is %d\n", len(sample)) /* for _, v := range sample { fmt.Printf("%v\n", v.GetPerfEntityMetricBase().Entity) } */ result, err := perfManager.ToMetricSeries(ctx, sample) if err != nil { fmt.Printf("Error getting ToMetricSeries : '%s'\n", err) return } fmt.Printf("Retrieved %d metric series\n", len(result)) // Read result for _, metric := range result { vm := object.NewVirtualMachine(c, metric.Entity) name, err := vm.ObjectName(ctx) if err != nil { fmt.Printf("Error getting vm name : '%s'\n", err) return } fmt.Printf("Vm %s has %d metric values : %v\n", name, len(metric.Value), metric) for i, v := range metric.Value { fmt.Printf("Processing [%d] : '%v'\n", i, v) counter := counters[v.Name] units := counter.UnitInfo.GetElementDescription().Label instance := v.Instance if instance == "" { instance = "-" } if len(v.Value) != 0 { //fmt.Printf("%s\t%s\t%s\t%s : %v\n", name, instance, v.Name, units, v.ValueCSV()) fmt.Printf("%s\t%s\t%s\t%s : %v (%d)\n", name, instance, v.Name, units, v.Value, len(v.Value)) } } } fmt.Printf("Finished receiving result.\n") /* // Create a query specification query := perfManager.QueryPerf(ctx, &vim25.QueryPerf{ Entity: []vim25.ManagedObjectReference{vm.Reference()}, MaxSample: 1, MetricId: getMetricIds(perfCounterInfos), IntervalId: 20, // 20-second interval StartTime: time.Now().Add(-time.Hour), // 1 hour ago EndTime: time.Now(), }) // Process the query result for _, perf := range query { fmt.Println("VM:", vmName) for i, value := range perf.Value { counterInfo := perfCounterInfos[i] fmt.Printf("%s: %v\n", counterInfo.NameInfo.GetElement().Key, value.Value[0]) } } */ } } // From https://stackoverflow.com/a/33769379 func Scan(d interface{}) { v := reflect.ValueOf(d) i := reflect.Indirect(v) s := i.Type() println(s.NumField()) // will print out 0, if you change Host to have 1 field, it prints out 1 } func query(t reflect.Type) { value := reflect.New(t).Interface() Scan(value) } func getNumVmsPerDrsScoreBucket(client *govmomi.Client) error { //properties := []string{"drsVmConfig.numVmPerDrsScoreBucket"} properties := []string{"configurationEx"} ctx := context.Background() m := view.NewManager(client.Client) clusters, err := m.CreateContainerView(ctx, client.ServiceContent.RootFolder, []string{"ClusterComputeResource"}, true) if err != nil { return err } defer clusters.Destroy(ctx) var clusterList []mo.ClusterComputeResource err = clusters.Retrieve(ctx, []string{"ClusterComputeResource"}, nil, &clusterList) if err != nil { return err } for _, cluster := range clusterList { fmt.Printf("Cluster: %s\n", cluster.Name) pc := property.DefaultCollector(client.Client) //var clusterProps *types.ComputeResourceSummary clusterProps := mo.ClusterComputeResource{} err := pc.RetrieveOne(ctx, cluster.Reference(), properties, &clusterProps) if err != nil { fmt.Printf("Error retrieving property : '%s'\n", err) return err } foo := clusterProps.ConfigurationEx bar := foo.GetComputeResourceConfigInfo() fmt.Printf("foo: %v\n", foo) fmt.Printf("bar: %v\n", bar) /* // Print properties (fields) objType := reflect.TypeOf(clusterProps) for i := 0; i < objType.NumField(); i++ { field := objType.Field(i) fmt.Printf("Field %d: %s (Type: %s)\n", i, field.Name, field.Type) } // Print methods methods := reflect.TypeOf(clusterProps) for i := 0; i < methods.NumMethod(); i++ { method := methods.Method(i) fmt.Printf("Method %d: %s\n", i, method.Name) } */ //bar := types.ClusterConfigInfoEx(foo).getNumVmsPerDrsScoreBucket() //fmt.Printf("%v\n", clusterProps) //fmt.Printf("Cluster: %s\n", clusterProps.Name) //fmt.Printf("NumVmsPerDrsScoreBucket: %d\n", clusterProps.DrsVmConfig.NumVmPerDrsScoreBucket) } return nil } func main() { // Command line flags vURL := flag.String("url", "", "The URL of a vCenter server, eg https://server.domain.example/sdk") vUser := flag.String("user", "", "The username to use when connecting to vCenter") vPass := flag.String("password", "", "The password to use when connecting to vCenter") vTZ := flag.String("tz", "Australia/Sydney", "The timezone to use when converting vCenter UTC times") vInsecure := flag.Bool("insecure", true, "Allow insecure connections to vCenter") //vmName := flag.String("vmname", "example-vm", "The vm to query metrics") //begin := flag.Duration("b", time.Hour, "Begin time") // default BeginTime is 1h ago flag.Parse() // Print logs to file f, err := os.OpenFile("log.txt", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666) if err != nil { log.Fatalf("error opening file: %v", err) } defer f.Close() log.SetOutput(f) log.Printf("Starting execution. Built on %s from sha1 %s\n", buildTime, sha1ver) // So we can convert vCenter UTC to our local timezone log.Printf("Setting timezone to '%s'\n", *vTZ) location, err = time.LoadLocation(*vTZ) if err != nil { fmt.Fprintf(os.Stderr, "Error setting timezone to %s : %s\n", *vTZ, err) os.Exit(1) } u, err := url.Parse(*vURL) if err != nil { fmt.Fprintf(os.Stderr, "Error parsing url %s : %s\n", *vURL, err) os.Exit(1) } else { if !strings.HasSuffix(u.Path, "/sdk") { u.Path, _ = url.JoinPath(u.Path, "/sdk") log.Printf("Updated vCenter URL to '%v'\n", u) } } log.Printf("Connecting to vCenter %s\n", u) u.User = url.UserPassword(*vUser, *vPass) ctx, cancel = context.WithCancel(context.Background()) defer cancel() // Login to vcenter c, err = govmomi.NewClient(ctx, u, *vInsecure) if err != nil { fmt.Fprintf(os.Stderr, "Logging in error: %s\n", err) os.Exit(1) } defer c.Logout(ctx) //getSpecificVMMetrics(ctx, c.Client, *vmName) err = getNumVmsPerDrsScoreBucket(c) if err != nil { fmt.Println("Error retrieving NumVmsPerDrsScoreBucket:", err) return } }