govc object.collect – An essential tool for govmomi developers

A while back, I was looking at ways that I could query vSphere resources and inventory using the Go language. My end goal was to develop a prototype Kubernetes Operator to extend Kubernetes so that a developer or K8s cluster admin could query vSphere resources and inventory from the kubectl interface. While the Go language itself has lots of code examples online, and is relatively intuitive to a novice programmer like myself, I struggled quite a bit with getting to grips with govmomi, the Go library for interacting with the VMware vSphere API. In particular, I had difficultly in trying to figure out the different sorts of attributes one could query via the API on a managed object, e.g. a host or a datastore for example. And yes there is the API reference guide, but context switching over and back to the documentation wasn’t the easiest. Last week, I was introduced to a feature of govc, a vSphere CLI tool that is built on top of govmomi, that greatly helps in understanding managed object attributes. While I had used govc for various actions in the past, the fact that it could display such detailed information about a managed object was new to me. I also think that anyone writing Go code to interact with govmomi will find this feature invaluable.

Let’s show a simple example from my lab environment. I can easily use the govc tree command, starting at the Datacenter and then hosts folder, to display clusters, hosts, datastores and networks, as shown below:

$ govc tree /OCTO-Datacenter/host
/OCTO-Datacenter/host
├── OCTO-Cluster-A
│  ├── Resources
│  ├── esxi-dell-e.rainpole.com
│  ├── esxi-dell-f.rainpole.com
│  └── esxi-dell-g.rainpole.com
├── OCTO-Cluster-B
│  ├── Resources
│  ├── esxi-dell-j.rainpole.com
│  ├── esxi-dell-k.rainpole.com
│  └── esxi-dell-l.rainpole.com
├── OCTO-Cluster-C
│  ├── Resources
│  ├── esxi-dell-h.rainpole.com
│  └── esxi-dell-i.rainpole.com
├── richardCluster
└── vcsa06-witness-01.rainpole.com
    ├── Resources
    └── vcsa06-witness-01.rainpole.com

$ govc tree /OCTO-Datacenter/datastore
/OCTO-Datacenter/datastore
├── isilon-01
├── vsan-OCTO-Cluster-A
├── vsan-OCTO-Cluster-B
└── vsan-OCTO-Cluster-C

$ govc tree /OCTO-Datacenter/network
/OCTO-Datacenter/network
├── DVS-A
│  ├── DVS-DVUplinks-81
│  ├── FrontEnd-50-DPG
│  ├── Mgmt-51-DPG
│  ├── VM-32-DVS-A
│  ├── VM-51-DPG
│  ├── VM70-DPortGroup
│  ├── VMOTION500-DPG
│  ├── VSAN500-DPG
│  ├── VSAN501-DPG
│  └── Workload-62-DPG
├── DVS-B
│  ├── DVS-B-DVUplinks-8045
│  ├── MGMT-51-DVS-B
│  ├── VM-51-DVS-B
│  ├── VM-62-DVS-B
│  └── VSAN-500-DVS-B
└── VM Network

Now let’s assume I want to do the same thing with some Go code using govmomi. I will skip over the context creation, client creation and login sections but you can review some sample code on how to do that in some govmomi code snippets available here. Here, I want to focus on how govc can help us quickly discover what attributes can be retrieved from managed object. In this example, we will focus on a host. Thus, we might create a container view, starting at the root folder and looking at host information, which looks some thing like this:

 v, err := m.CreateContainerView(ctx, c.ServiceContent.RootFolder, []string{"HostSystem"}, true)

As you can imagine, the “HostSystem” reference relates to ESXi hosts. Once the container view has been created, we can use it to retrieve interesting information about hosts. In this example, we have requested information in a host’s summary attribute, and this will be stored in an array (hss) of HostSystem managed objects.

var hss []mo.HostSystem
err = v.Retrieve(ctx, []string{"HostSystem"}, []string{"summary"}, &hss)

This immediately leads to some questions. The first is if there is other information other than summary that can be retrieved? The second is that once I have the summary information about the host, what is in the summary that I can use or display? This is where govc object.collect is a great feature. Previously, I would have to be switching over and back to either to the vCenter MOB (Managed Object Browser) or the API documentation mentioned previously. Now I can display this via govc.

Let’s focus on what information is available in the summary attribute? Using the command govc object.collector I can select a host in my inventory and query that summary attribute. This is what it displays.

$ govc object.collect /OCTO-Datacenter/host/OCTO-Cluster-A/esxi-dell-g.rainpole.com summary
summary.host                            *types.ManagedObjectReference              HostSystem:host-18
summary.hardware                        *types.HostHardwareSummary                 ...
summary.runtime                         *types.HostRuntimeInfo                     ...
summary.config                          types.HostConfigSummary                    ...
summary.quickStats                      types.HostListSummaryQuickStats            ...
summary.overallStatus                   types.ManagedEntityStatus                  yellow
summary.rebootRequired                  bool                                       false
summary.customValue                     []types.BaseCustomFieldValue
summary.managementServerIp              string                                     10.27.51.106
summary.maxEVCModeKey                   string                                     intel-broadwell
summary.currentEVCModeKey               string
summary.currentEVCGraphicsModeKey       string
summary.gateway                         *types.HostListSummaryGatewaySummary
summary.tpmAttestation                  *types.HostTpmAttestationInfo
summary.trustAuthorityAttestationInfos  []types.HostTrustAuthorityAttestationInfo

So there is a lot of useful information that can be retrieved from the summary, such as status (summary.overallStatus) and the management IP address (summary.managementServerIp). One thing that might strike you about this attribute is that while there is some interesting information here, there is no hostname in this output. From experience, I know that is it available in summary.config. To verify this, we can have the object collector display that as well.

$ govc object.collect /OCTO-Datacenter/host/OCTO-Cluster-A/esxi-dell-g.rainpole.com summary.config
summary.config.name                   string                          esxi-dell-g.rainpole.com
summary.config.port                   int32                           443
summary.config.sslThumbprint          string                          03:50:45:9E:C4:04:56:07:7E:E4:7F:63:4E:88:B9:94:59:1F:C0:E8
summary.config.product                *types.AboutInfo                ...
summary.config.vmotionEnabled         bool                            true
summary.config.faultToleranceEnabled  *bool                           false
summary.config.featureVersion         []types.HostFeatureVersionInfo
summary.config.agentVmDatastore       *types.ManagedObjectReference
summary.config.agentVmNetwork         *types.ManagedObjectReference

And now we have the hostname in summary.config.name. And you can continue to query these different attributes in the same way. Suppose that I was interested in the vendor and model of the host. These fields are available in the summary.hardware. Once again, we can confirm this with govc object.collect, along with other useful hardware information.

$ govc object.collect /OCTO-Datacenter/host/OCTO-Cluster-A/esxi-dell-g.rainpole.com summary.hardware
summary.hardware.vendor                string                                Dell Inc.
summary.hardware.model                 string                                PowerEdge R630
summary.hardware.uuid                  string                                4c4c4544-0057-4c10-804d-b3c04f534732
summary.hardware.otherIdentifyingInfo  []types.HostSystemIdentificationInfo  ...
summary.hardware.memorySize            int64                                 137340669952
summary.hardware.cpuModel              string                                Intel(R) Xeon(R) CPU E5-2630 v4 @ 2.20GHz
summary.hardware.cpuMhz                int32                                 2199
summary.hardware.numCpuPkgs            int16                                 2
summary.hardware.numCpuCores           int16                                 20
summary.hardware.numCpuThreads         int16                                 4
summary.hardware.numNics               int32                                 6
summary.hardware.numHBAs               int32                                 6
The fields we are interested in then are summary.hardware.vendor and summary.hardware.model. And to code that, we would simply do something like the following, which would display the hostname, vendor, model and IP Address of each host, referencing different parts or layers of the summary attribute:
for _, hs := range hss {
    fmt.Fprintf(tw, "- \tName:\t%s\n", hs.Summary.Config.Name)
    fmt.Fprintf(tw, "- \tHardware Vendor:\t%s\n", hs.Summary.Hardware.Vendor)
    fmt.Fprintf(tw, "- \tHardware Model:\t%s\n", hs.Summary.Hardware.Model)
    fmt.Fprintf(tw, "- \tIP Address:\t%s\n\n", hs.Summary.ManagementServerIp)
}

Now that we have seen how to query what information is available under the summary attributes, let’s see what other attributes can be retrieved. Once again, we can use the govc object.collect. In this case, just point it at the host without any attributes and it will display all available attributed that can be retrieved.

$ govc object.collect /OCTO-Datacenter/host/OCTO-Cluster-A/esxi-dell-g.rainpole.com
alarmActionsEnabled  bool                              true
availableField       []types.CustomFieldDef            ...
capability           types.HostCapability              ...
config               types.HostConfigInfo              ...
configIssue          []types.BaseEvent                 ...
configManager        types.HostConfigManager           ...
configStatus         types.ManagedEntityStatus         yellow
customValue          []types.BaseCustomFieldValue
datastore            []types.ManagedObjectReference    Datastore:datastore-38,Datastore:datastore-8030,Datastore:datastore-33
datastoreBrowser     types.ManagedObjectReference      HostDatastoreBrowser:datastoreBrowser-host-18
declaredAlarmState   []types.AlarmState                ...
disabledMethod       []string                          ExitMaintenanceMode_Task,PowerUpHostFromStandBy_Task,ReconnectHost_Task
effectiveRole        []int32                           -1
hardware             types.HostHardwareInfo            ...
licensableResource   types.HostLicensableResourceInfo  ...
name                 string                            esxi-dell-g.rainpole.com
network              []types.ManagedObjectReference    Network:network-12,DistributedVirtualPortgroup:dvportgroup-1010,DistributedVirtualPortgroup:dvportgroup-9036,DistributedVirtualPortgroup:dvportgroup-3012,DistributedVirtualPortgroup:dvportgroup-85,DistributedVirtualPortgroup:dvportgroup-3011,DistributedVirtualPortgroup:dvportgroup-546,DistributedVirtualPortgroup:dvportgroup-525,DistributedVirtualPortgroup:dvportgroup-84,DistributedVirtualPortgroup:dvportgroup-82,DistributedVirtualPortgroup:dvportgroup-83
overallStatus        types.ManagedEntityStatus         yellow
parent               types.ManagedObjectReference      ClusterComputeResource:domain-c22
permission           []types.Permission
recentTask           []types.ManagedObjectReference
runtime              types.HostRuntimeInfo             ...
summary              types.HostListSummary             ...
systemResources      types.HostSystemResourceInfo      ...
tag                  []types.Tag
triggeredAlarmState  []types.AlarmState
value                []types.BaseCustomFieldValue
vm                   []types.ManagedObjectReference    VirtualMachine:vm-7043,VirtualMachine:vm-7044,VirtualMachine:vm-9091,VirtualMachine:vm-9092,VirtualMachine:vm-9003

So we can see that there are a number of additional attributes other than summary that can be retrieved. Let’s look at runtime and see if there are some interesting fields in there.

$ govc object.collect /OCTO-Datacenter/host/OCTO-Cluster-A/esxi-dell-g.rainpole.com runtime
runtime.connectionState              types.HostSystemConnectionState                    connected
runtime.powerState                   types.HostSystemPowerState                         poweredOn
runtime.standbyMode                  string                                             none
runtime.inMaintenanceMode            bool                                               false
runtime.inQuarantineMode             *bool                                              false
runtime.bootTime                     *time.Time                                         2021-05-10 11:41:59.078 +0000 UTC
runtime.healthSystemRuntime          *types.HealthSystemRuntime                         ...
runtime.dasHostState                 *types.ClusterDasFdmHostState                      ...
runtime.tpmPcrValues                 []types.HostTpmDigestInfo
runtime.vsanRuntimeInfo              *types.VsanHostRuntimeInfo                         ...
runtime.networkRuntimeInfo           *types.HostRuntimeInfoNetworkRuntimeInfo           ...
runtime.vFlashResourceRuntimeInfo    *types.HostVFlashManagerVFlashResourceRunTimeInfo
runtime.hostMaxVirtualDiskCapacity   int64                                              0
runtime.cryptoState                  string                                             incapable
runtime.cryptoKeyId                  *types.CryptoKeyId
runtime.statelessNvdsMigrationReady  string


$ govc object.collect /OCTO-Datacenter/host/OCTO-Cluster-A/esxi-dell-g.rainpole.com  runtime.powerState
runtime.powerState  types.HostSystemPowerState  poweredOn


$ govc object.collect /OCTO-Datacenter/host/OCTO-Cluster-A/esxi-dell-g.rainpole.com  runtime.inMaintenanceMode
runtime.inMaintenanceMode  bool  false

In the runtime attribute, I took a look at both the powerState and the inMaintenanceMode on the host. Now, how would I retrieve the same thing in my Go code. First, I would have to retrieve the runtime attribute instead off the summary attribute. Thus, the retrieve might look something like this, storing the runtime attributes of the hosts in an array of HostSystem managed objects called hss2.

var hss2 []mo.HostSystem
err = v.Retrieve(ctx, []string{"HostSystem"}, []string{"runtime"}, &hss2)

To display the details around power and maintenance mode referenced earlier, it might now look something like this:

for _, hs2 := range hss2 {
    fmt.Fprintf(tw, "- \tPower state:\t%s\n", hs2.Runtime.PowerState)
    fmt.Fprintf(tw, "- \tMaintenance Mode:\t%t\n", hs2.Runtime.InMaintenanceMode)
}

So hopefully you can see how quickly we can use the govc object.collector to help us identify the attributes of vSphere managed objects for our Go code. I’ll leave you 2 more small examples. The first of these is for datastores. Note that the path in the inventory is now different than it was for hosts, but it also has a summary attribute.

$ govc object.collect /OCTO-Datacenter/datastore/vsan-OCTO-Cluster-A summary
summary.datastore           *types.ManagedObjectReference  Datastore:datastore-33
summary.name                string                         vsan-OCTO-Cluster-A
summary.url                 string                         ds:///vmfs/volumes/vsan:52e89a86c0e70766-93d617a43703ffde/
summary.capacity            int64                          4800934576128
summary.freeSpace           int64                          3340023596324
summary.uncommitted         int64                          3059076612096
summary.accessible          bool                           true
summary.multipleHostAccess  *bool                          true
summary.type                string                         vsan
summary.maintenanceMode     string                         normal

Thus, in our Go code, we might now have something like this if we wish to retrieve and display the datastore names. The container view is now created with Datastores as the focus, rather than HostSystems.

v, err := m.CreateContainerView(ctx, c.ServiceContent.RootFolder, []string{"Datastore"}, true)

We are once again going to retrieve summary attribute, but this time from datastores. These are stored in an array of datastore managed objects.

var dss []mo.Datastore
err = v.Retrieve(ctx, []string{"Datastore"}, []string{"summary"}, &dss)

And as before, we can print out fields, such as the name of the datastore, which is available in the summary attribute.

for _, ds := range dss {
    fmt.Fprintf(tw, "-- %s\n", ds.Summary.Name)
}

Let’s look at one last item, and that is the networks on the system. What is interesting to note is that networks does not have a summary attribute.

$ govc object.collect /OCTO-Datacenter/network summary
govc: ServerFaultCode: InvalidProperty

But it does have a name attribute.

$ govc object.collect /OCTO-Datacenter/network name
name  string  network

In this case, to display the list of network names, you could retrieve the name attribute with the container view focused on networks.

 v, err := m.CreateContainerView(ctx, c.ServiceContent.RootFolder, []string{"Network"}, true)

Retrieve the name attribute for Network.

 err = v.Retrieve(ctx, []string{"Network"}, []string{"name"}, &nws)

And then print out the name of the network:

for _, nw := range nws {
    fmt.Fprintf(tw, "--- %s\n", nw.Name)
}

That completes the post. Hopefully it has provided some good insight regarding the usefulness of the govc object.collect feature if you are planning to code with govmomi against the vSphere API. Much kudos to Doug MacEachern for his work on this feature, and teaching us about this extremely useful utility. If you want to learn more, check out these govmomi code snippets here.