package main import ( "context" "flag" "fmt" "log" "net/url" "os" "strings" "time" "github.com/vmware/govmomi" "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 location *time.Location sha1ver string // sha1 revision used to build the program buildTime string // when the executable was built ) 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") //begin := flag.Duration("b", time.Hour, "Begin time") // default BeginTime is 1h ago flag.Parse() var err error fmt.Printf("Starting execution. Built on %s from sha1 %s\n", buildTime, sha1ver) // So we can convert vCenter UTC to our local timezone fmt.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) } } fmt.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) // TODO : Refactor to iterate through all datacenters /* // Create a finder finder := find.NewFinder(c.Client, true) // Find one and only datacenter, not sure how VMware linked mode will work dc, err := finder.DatacenterOrDefault(ctx, "") if err != nil { fmt.Printf("No Datacenter instance could be found inside of vCenter : '%v'\n", err) } fmt.Printf("Looking at datacenter %s\n", dc.Name()) // Make future calls local to this datacenter finder.SetDatacenter(dc) // Find all datastores datastores, err := finder.DatastoreList(ctx, "*") if err != nil { fmt.Printf("DatastoreList err: %v\n", err) } // Retrieve ManagedObjectReference for each datastore var datastoreRefs []types.ManagedObjectReference for _, ds := range datastores { ref := ds.Reference() datastoreRefs = append(datastoreRefs, ref) } */ datastoreRefs, err := getDatastoreRefs(ctx, c) if err != nil { fmt.Printf("err: %v\n", err) } for i, val := range datastoreRefs { fmt.Printf("val %d: %v\n", i, val) } /* // Find a host hostObj, err := finder.DefaultHostSystem(ctx) if err != nil { fmt.Println("Failed to find host:", err) return } // Find all datastores on the host datastores, err := hostObj.Datastores(ctx) if err != nil { fmt.Println("Failed to retrieve datastores:", err) return } // Iterate over each datastore for _, ds := range datastores { fmt.Println("Datastore:", ds.Name()) // Get the host's datastore browser hostBrowser, err := hostObj.ConfigManager().DatastoreBrowser(ctx) if err != nil { fmt.Printf("Failed to get datastore browser for datastore %s: %v\n", ds.Name(), err) continue } // Recursively list files and folders in the datastore dsPath := "[" + ds.Name() + "]" err = listFilesRecursively(ctx, hostBrowser, dsPath, ds.Name()) if err != nil { fmt.Printf("Failed to list files in datastore %s: %v\n", ds.Name(), err) continue } } */ } // getDatastoreRefs returns a list of ManagedObjectReference objects corresponding to every datastore in the vCenter. func getDatastoreRefs(ctx context.Context, client *govmomi.Client) ([]types.ManagedObjectReference, error) { var results []types.ManagedObjectReference m := view.NewManager(client.Client) view, err := m.CreateContainerView(ctx, client.ServiceContent.RootFolder, []string{"Datastore"}, true) if err != nil { return nil, fmt.Errorf("failed to create view: %v", err) } defer view.Destroy(ctx) var refs []mo.Datastore err = view.Retrieve(ctx, []string{"Datastore"}, []string{"name", "summary"}, &refs) if err != nil { return nil, fmt.Errorf("failed to retrieve datastores: %v", err) } for i := range refs { //fmt.Printf("refs[i]: %v\n", refs[i]) fmt.Printf("Datastore '%s', Shared : '%t'\n", refs[i].Name, *refs[i].Summary.MultipleHostAccess) results = append(results, refs[i].Reference()) } return results, nil } /* func listFilesRecursively(ctx context.Context, browser *host.DatastoreBrowser, path string, datastoreName string) error { searchResult, err := browser.SearchDatastoreSubFolders(ctx, path, "*", true) if err != nil { return err } for _, file := range searchResult.File { fmt.Printf("File: %s\n", file.Path) } for _, folder := range searchResult.Folder { fmt.Printf("Folder: %s\n", folder.Path) err := listFilesRecursively(ctx, browser, folder.Path, datastoreName) if err != nil { return err } } return nil } */ /* func listFilesRecursively(ctx context.Context, browser *object.HostDatastoreBrowser, path string, datastoreName string) error { var files []mo.FileInfo err := browser.List(ctx, path, &files) if err != nil { return err } // Get list of VMs on the datastore var vms []mo.VirtualMachine browser. err = browser.Datastore().VirtualMachines(ctx, &vms) if err != nil { return err } // Create a map to store VM file paths vmFilePaths := make(map[string]bool) for _, vm := range vms { for _, filePath := range vm.LayoutEx.File { vmFilePaths[filePath.Name] = true } } // Compare file list with VM file paths for _, fileInfo := range files { if !vmFilePaths[fileInfo.Path] { fmt.Printf("File %s on datastore %s is not owned by any VM\n", fileInfo.Path, datastoreName) } if fileInfo.IsDir { err := listFilesRecursively(ctx, browser, fileInfo.Path, datastoreName) if err != nil { return err } } } return nil } */