Govmomi and listening for vSphere events
For a while, i’ve wanted to get some experience in Go (or Golang, if you prefer), but never found the time. Recently I found the need for something that allows me to capture all the VM events on an ESXi host and handle according to them.
Next to pyVmomi, VMware also has a vSphere SDK for Go, called govmomi, and I thought that this was the perfect moment to dive into Go and get my hands dirty as i already know how to work with the pyVmomi library and the vSphere SDK/API.
Coming from a (basic) Python background and not being a real developer, moving to Go was quite an adjustment and after only one or two days of playing around with it, i still have to learn a lot.
However, i can compare govmomi vs pyVmomi and one thing is immediately clear about Go and govmomi: It is fast… Really fast. What normally would take a couple of seconds in pyVmomi, takes less than a second in govmomi.
To get started with Go, you can visit the Go Tour, it helps a lot to explain the basics of Go and the important aspects. However, as all developers know, the best way to get to know a programming language, is by using it. So let’s dive deeper in how govmomi works.
govmomi
govmomi has been around for a while now and it follows the standard vSphere API structure, so that makes it easier to get your head around what properties and methods or functions are available for each type of object (struct).
To get started, take a look at the following example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
package main import ( "context" "fmt" "net/url" "os" "github.com/vmware/govmomi" ) func main() { // Creating a connection context ctx, cancel := context.WithCancel(context.Background()) defer cancel() // Parsing URL url, err := url.Parse("https://administrator%40vsphere.local:vmware@vcenter01.intern.dellaert.org/sdk") if err != nil { fmt.Fprintf(os.Stderr, "Error: %s\n", err) os.Exit(1) } // Connecting to vCenter client, err := govmomi.NewClient(ctx, url, true) if err != nil { fmt.Fprintf(os.Stderr, "Error: %s\n", err) os.Exit(1) } // vCenter version info := client.ServiceContent.About fmt.Printf("Connected to vCenter version %s\n", info.Version) } |
This is a simple example that connects to a vCenter server and reads out the vCenter version it is running. Analysing this example, let’s look at some interesting sections.
On line 14, I create a context. This is needed for a proper functioning of govmomi so all goroutines used within a specific connection to a vSphere server (vCenter or Host) can be cleaned up properly when closing the connection. For more information on contexts, this blogpost on the golang website is a good start. In this case, I create a context with a cancel cleanup function, which I defer. The cancel function will be called upon the end of the execution, cleaning up.
On line 18, I create the URL format the govmomi client expects, this is in the format of https://username:password@hostname/sdk .
Finally, on line 25, I connect to the vCenter server using govmomi. With this connection, on line 32, I request the information of the vCenter server and print this information out.
Of course, throughout the whole process I check if any error is returned, and in case an error occurs, I exit with that error.
There is a lot more to do with govmomi, for now, I’m going into a bit of detail on how you can parse events.
Handling vSphere events with govmomi
To get and handle the events that happen on vCenter or on an ESXi host, you can use a govmomi event manager. You can decide to only get the last number of events on a set of entities in vSphere, or you can continuously listen for events happening on a set of entities. This set of entities can be anything that is available in vSphere.
Each event will have it’s own event type, which can be used to determine the what event you are dealing with. Digging deeper in the event object will also give you information on the involved entities like VMs, Hosts, Networks, …
The following code is a basic example of using the Event Manager in govmomi.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
package main import ( "context" "fmt" "net/url" "os" "reflect" "github.com/vmware/govmomi" "github.com/vmware/govmomi/event" "github.com/vmware/govmomi/find" "github.com/vmware/govmomi/vim25/types" ) func handleEvent(ref types.ManagedObjectReference, events []types.BaseEvent) (err error) { for _, event := range events { eventType := reflect.TypeOf(event).String() fmt.Printf("Event found of type %s\n", eventType) } return nil } func main() { // Creating a connection context ctx, cancel := context.WithCancel(context.Background()) defer cancel() // Parsing URL url, err := url.Parse("https://administrator%40vsphere.local:vmware@vcenter01.intern.dellaert.org/sdk") if err != nil { fmt.Fprintf(os.Stderr, "Error: %s\n", err) os.Exit(1) } // Connecting to vCenter client, err := govmomi.NewClient(ctx, url, true) if err != nil { fmt.Fprintf(os.Stderr, "Error: %s\n", err) os.Exit(1) } // Selecting default datacenter finder := find.NewFinder(client.Client, true) dc, err := finder.DefaultDatacenter(ctx) if err != nil { fmt.Fprintf(os.Stderr, "Error: %s\n", err) os.Exit(1) } refs := []types.ManagedObjectReference{dc.Reference()} // Setting up the event manager eventManager := event.NewManager(client.Client) err = eventManager.Events(ctx, refs, 10, false, false, handleEvent) if err != nil { fmt.Fprintf(os.Stderr, "Error: %s\n", err) os.Exit(1) } } |
A big chunk of this code should look familiar from what you’ve seen on the first example. I’ll go through the new sections to help explain the functionality.
On line 54 I create a new event manager and on line 55 I use this event manager to get the latest 10 events from the vSphere environment. The function to get the events, needs a few parameters:
- A context: In this case I reuse the same context as with the client.
- An array of references: These are the references to the items in vCenter or the ESXi host from which you wish to gather the events. I use the default datacenter of vCenter to keep it simple (line 46)
- Number of events to get
- Tail the events: If this bool is set to true, the events processor will keep running and will handle each event that happens in the referenced items
- Force: By default a maximum of 10 objects can be in the array of references, if more than 10, the Events function will return an error. By enabling force, you can override this behaviour and force it to go through anyway
- Handler function: This function will be called whenever an event happens.
I define a simple handler function on line 16 which just prints out the type of the event.
The output of this program, would look something like this:
1 2 3 4 5 6 7 8 9 10 11 |
$ go run get-events.go Event found of type *types.UserLogoutSessionEvent Event found of type *types.AlarmSnmpCompletedEvent Event found of type *types.AlarmStatusChangedEvent Event found of type *types.AlarmActionTriggeredEvent Event found of type *types.AlarmStatusChangedEvent Event found of type *types.ExtendedEvent Event found of type *types.ExtendedEvent Event found of type *types.ExtendedEvent Event found of type *types.ExtendedEvent Event found of type *types.AlarmSnmpCompletedEvent |
Extended example
For a more extended example of how to handle live events from an ESXi host, you can have a look at my gesxmon project. Just a warning: this project is still in full development and it is my first Go project, as such, there might be things in there that look odd or against best practices. Feel free to point me to better approaches ;)