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