List all vCenter VMs with Go (govmomi)

Posted by

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.

3 comments

  1. 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

    1. 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

      1. 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.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s