A few months ago I saw a that VMware’s govmomi was updated and since I’ve recently started to learn the Go programming language I thought it would be a good exercise to do something simple like list all virtual machines in a vCenter server.
I’m going to use VMware’s Photon 1.0 TP2 as a development environment since the full install comes with tools such as git and go. I built a Photon VM using these instructions and performed a full install. To keep things simple for this post I enabled ssh for root but you’ll probably want to create a user for yourself.
Installing govmomi
Let’s make a directory where we will install govmomi.
mkdir -p $GOPATH/src/github.com/vmware
Change into this directory and clone the govmomi repo:
cd $GOPATH/src/github.com/vmware
git clone https://github.com/vmware/govmomi.git
I’m not sure if I’m doing something wrong but I needed to install these otherwise I received errors when running the examples:
go get golang.org/x/tools/cmd/vet
go get golang.org/x/tools/cmd/goimports
go get golang.org/x/net/context
I found these in a travis-ci job, but I no longer have the link.
Running an example
Change into the directory that has the only example that is currently in the repo:
cd govmomi/examples/datastores
There is a single file in this directory named main.go and it will list all the datastores in your vCenter. Actually if I remember correctly, it will only process the first datacenter in your vCenter. The program needs several parameters to run and they can be retrieved from shell environment variables or by passing them into the program. If you want to use shell environment variables, you’ll need to define them. Here is what mine look like:
export GOVMOMI_URL=’https://vc5c.vmware.local/sdk‘
export GOVMOMI_USERNAME=’vmware\api’
export GOVMOMI_PASSWORD=’vmware123′
export GOVMOMI_INSECURE=’true’
Now I can run the program as such:
go run main.go
My output looks like this:
Name: Type: Capacity: Free: nfs-ds412-momentus1 NFS 683.1GB 148.0GB nfs-ds412-5400 NFS 453.9GB 293.6GB nfs-ds412-hybrid0 NFS 1.8TB 1.0TB nfs-ds412-hybrid1 NFS 1.8TB 1002.9GB iscsi-ds412-momentus1-0 VMFS 749.8GB 132.3GB local-esx-5-1 VMFS 144.0GB 61.8GB diskstation-nfs NFS 908.0GB 308.7GB nfs-async NFS 908.0GB 308.7GB
Alternatively, I could have passed in the parameters:
go run main.go –url https://vmware\\api:vmware123@vc5c.vmware.local/sdk –insecure true
Listing all VMs
Since I’m just learning Go and don’t know much about govmomi, I’m going to modify the datastore program instead of writing one from scratch. I copied the main.go file to list-vms.go. Most of the changes are pretty straightforward. Instead of searching for DataStores you need to search for VirtualMachines, etc. Here are few of the changes that I made:
Line 148:
// Find datastores in datacenter dss, err := f.DatastoreList(ctx, "*") to // Find virtual machines in datacenter vms, err := f.VirtualMachineList(ctx, "*")
Line 162:
// Retrieve summary property for all datastores var dst []mo.Datastore err = pc.Retrieve(ctx, refs, []string{"summary"}, &dst) if err != nil { exit(err) } to // Retrieve name property for all vms var vmt []mo.VirtualMachine err = pc.Retrieve(ctx, refs, []string{"name"}, &vmt) if err != nil { exit(err) }
Line 171:
fmt.Fprintf(tw, "Name:\tType:\tCapacity:\tFree:\n") for _, ds := range dst { fmt.Fprintf(tw, "%s\t", ds.Summary.Name) fmt.Fprintf(tw, "%s\t", ds.Summary.Type) fmt.Fprintf(tw, "%s\t", units.ByteSize(ds.Summary.Capacity)) fmt.Fprintf(tw, "%s\t", units.ByteSize(ds.Summary.FreeSpace)) fmt.Fprintf(tw, "\n") } to fmt.Println("Virtual machines found:", len(vmt)) for _, vm := range vmt { fmt.Fprintf(tw, "%s\n", vm.Name) }
I can retrieve a listing of all VMs as such:
go run list-vms.go
Here is the truncated output from my lab:
Virtual machines found: 84
fw1
Linux Minimal Template2
vc55b
haproxy
db3
puppet-agent3-centos7
coreos
dhcptftp
cell3
dhcpAutodeploy
Looks good but they aren’t sorted. Let’s fix that.
Sorting the VMs
I really just wanted to do this to see how to sort in Go. I used the example here as a reference.
First we need to add “sort” to the import statement.
Next we need to implement the sort.Interface for ByName:
type ByName []mo.VirtualMachine
func (n ByName) Len() int { return len(n) }
func (n ByName) Swap(i, j int) { n[i], n[j] = n[j], n[i] }
func (n ByName) Less(i, j int) bool { return n[i].Name < n[j].Name }
Now before we loop through and print out the VM names we sort them first:
sort.Sort(ByName(vmt))
Now when I run the program the truncated results look like this:
Virtual machines found: 84
2k3-template
Linux Minimal Template2
cell3
cell55a
cell55b
cell55c
cell55d
centos66
centos7-openstack
centos7-template
coreos
db1
You can view the script on github.
Hi Chris, Great blog!
I am running your example on Go 1.5.2 and a fresh pull of govmomi and getting a different output when attempting to list all VM’s:
—————-
Error: vm ‘*’ not found
—————-
The included datastore list example works just fine.
f = &find.Finder{client:(*vim25.Client)(0xc08219a500), recurser:list.Recurser{Collector:(*property.Collector)(0xc0821ca1b0), All:true, TraverseLeafs:false}, dc:(*object.Datacenter)(0xc0821ca780), folders:(*object.DatacenterFolders)(nil)}
dc = &object.Datacenter{Common:object.Common{c:(*vim25.Client)(0xc08219a500), r:types.ManagedObjectReference{Type:”Datacenter”, Value:”datacenter-2″}}}
Any ideas?
-d
Hmmm, I think I just answered my own question. It seems the command does not recurse into folders. When I drill down to a folder containing VM’s (found using “govc ls”) and give the path to VirtualMachineList like this:
vms, err := f.VirtualMachineList(ctx, “/folder/subfolder/*”)
I get some VM names to work with…Looks like its up to the end user to roll their own recursion.
-d
Hmm… yeah I moved all of my VMs into a subfolder and it didn’t find any of them. This is still new to me, but check this out. Go here https://github.com/vmware/govmomi/blob/8f2d79fd740b69c4f64d10e8ac0b8c6b30247c0d/find/finder.go#L537 and then look at line 538. The third option if false. If you go to line 39, you’ll see NewFinder and it looks like its third option is for recursion. In the repo that I cloned I set false to true on like 538 and now it’s finding all of my VMs. I created this new function in finder.go with the third option set to true:
func (f *Finder) VirtualMachineListAll(ctx context.Context, path string) ([]*object.VirtualMachine, error) {
es, err := f.find(ctx, f.vmFolder, true, path)
if err != nil {
return nil, err
}
…
Then in my script I changed
vms, err := f.VirtualMachineList(ctx, “*”)
to
vms, err := f.VirtualMachineListAll(ctx, “*”)
It’s probably the way it is for a reason so take this for what it’s worth.