package main import ( "context" "encoding/json" "flag" "fmt" "log" "net/url" "os" "reflect" "strings" "time" _ "time/tzdata" "github.com/vmware/govmomi" "github.com/vmware/govmomi/property" "github.com/vmware/govmomi/view" "github.com/vmware/govmomi/vim25/mo" "github.com/vmware/govmomi/vim25/types" ) var ( c *govmomi.Client ctx context.Context cancel context.CancelFunc sha1ver string // sha1 revision used to build the program buildTime string // when the executable was built results []DrsResults location *time.Location ) type DrsResults struct { ClusterName string DrsScore int32 Vcenter string NumVmsDrsBucket0To20 int32 NumVmsDrsBucket21To40 int32 NumVmsDrsBucket41To60 int32 NumVmsDrsBucket61To80 int32 NumVmsDrsBucket81To100 int32 } // 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 getScsiBusSharingVMs(client *govmomi.Client) error { ctx := context.Background() m := view.NewManager(client.Client) vms, err := m.CreateContainerView(ctx, client.ServiceContent.RootFolder, []string{"VirtualMachine"}, true) if err != nil { return err } defer vms.Destroy(ctx) var vmList []mo.VirtualMachine err = vms.Retrieve(ctx, []string{"VirtualMachine"}, nil, &vmList) if err != nil { log.Printf("Error retrieving vm list : '%s'\n", err) return err } // Iterate through VMs and check for SCSI bus sharing for _, vm := range vmList { fmt.Printf("vm: %s\n", vm.Name) if len(vm.Config.Hardware.Device) > 0 { for _, device := range vm.Config.Hardware.Device { //fmt.Printf("device: %v\n", device) if scsi, ok := device.(types.BaseVirtualSCSIController); ok { fmt.Printf("scsi: %v\n", scsi) controller := scsi.GetVirtualSCSIController() if controller.SharedBus != "noSharing" { fmt.Printf("VM %s is using SCSI bus sharing mode: %v\n", vm.Name, controller.SharedBus) } } } } /* // Retrieve VM's ConfigInfo var configInfo types.VirtualMachineConfigInfo err = property.DefaultCollector(client.Client).RetrieveOne(ctx, vm.Reference(), []string{"config"}, &configInfo) if err != nil { log.Fatal(err) } // Check if SCSI bus sharing is enabled if configInfo != nil && configInfo.GetVmConfigInfo() != nil && configInfo.GetVmConfigInfo().GetHardware() != nil { hardware := configInfo.GetVmConfigInfo().GetHardware() if devices := hardware.GetDevice(); devices != nil { for _, device := range devices { if scsi, ok := device.(object.VirtualSCSIController); ok { if scsi.SharedBus != nil && scsi.SharedBus.Value != "" { fmt.Printf("VM %s is using SCSI bus sharing mode: %s\n", vm.Name, scsi.SharedBus.Value) } } } } } */ } return nil } func getNumVmsPerDrsScoreBucket(client *govmomi.Client) error { //properties := []string{"drsVmConfig.numVmPerDrsScoreBucket"} 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 { log.Printf("Error retrieving cluster list : '%s'\n", err) return err } for _, cluster := range clusterList { log.Printf("Cluster: %s\n", cluster.Name) pc := property.DefaultCollector(client.Client) clusterProps := mo.ClusterComputeResource{} //err := pc.RetrieveOne(ctx, cluster.Reference(), properties, &clusterProps) err := pc.RetrieveOne(ctx, cluster.Reference(), nil, &clusterProps) if err != nil { log.Printf("Error retrieving property : '%s'\n", err) return err } // Properties defined at https://vdc-download.vmware.com/vmwb-repository/dcr-public/bf660c0a-f060-46e8-a94d-4b5e6ffc77ad/208bc706-e281-49b6-a0ce-b402ec19ef82/SDK/vsphere-ws/docs/ReferenceGuide/vim.ClusterComputeResource.Summary.html ccr := clusterProps.Summary.(*types.ClusterComputeResourceSummary) /* fmt.Printf("DRS Score: %v\n", ccr.DrsScore) fmt.Printf("VMs in DRS Score Bucket 0%% to 20%% : %d\n", ccr.NumVmsPerDrsScoreBucket[0]) fmt.Printf("VMs in DRS Score Bucket 21%% to 40%% : %d\n", ccr.NumVmsPerDrsScoreBucket[1]) fmt.Printf("VMs in DRS Score Bucket 41%% to 60%% : %d\n", ccr.NumVmsPerDrsScoreBucket[2]) fmt.Printf("VMs in DRS Score Bucket 61%% to 80%% : %d\n", ccr.NumVmsPerDrsScoreBucket[3]) fmt.Printf("VMs in DRS Score Bucket 81%% to 100%% : %d\n", ccr.NumVmsPerDrsScoreBucket[4]) */ // Create a new result result := DrsResults{ Vcenter: client.URL().Host, ClusterName: cluster.Name, DrsScore: ccr.DrsScore, NumVmsDrsBucket0To20: ccr.NumVmsPerDrsScoreBucket[0], NumVmsDrsBucket21To40: ccr.NumVmsPerDrsScoreBucket[1], NumVmsDrsBucket41To60: ccr.NumVmsPerDrsScoreBucket[2], NumVmsDrsBucket61To80: ccr.NumVmsPerDrsScoreBucket[3], NumVmsDrsBucket81To100: ccr.NumVmsPerDrsScoreBucket[4], } // Append to list of all results results = append(results, result) /* // 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) } */ } 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) /* err = getNumVmsPerDrsScoreBucket(c) if err != nil { log.Printf("Error retrieving NumVmsPerDrsScoreBucket: %s\n", err) return } */ err = getScsiBusSharingVMs(c) if err != nil { log.Printf("Error retrieving list of VMs with SCSI Bus Sharing : %s\n", err) return } // Output final results in JSON if len(results) > 0 { j, _ := json.Marshal(results) fmt.Println(string(j)) } else { fmt.Println("{}") } }