Walking with Govmomi

This is the second post around developing Go code against the vSphere/vCenter APIs using govmomi, if you’re new to this then I would recommend you start with https://thebsdbox.co.uk/2019/07/04/First-steps-with-Govmomi to fully grasp the basic concepts.

Extending our example

The example that we built in the previous post was a very simplistic example that contained only the basic functionality to log into a vSphere/vCenter server, other than testing a users credentials it had no real functionality. In this post we will extend its functionality in order for it to actually interact with various objects and types that exist in both vSphere and vCenter.

Proposed functionality

The next version of our demo application will have the following functionality:

  • Retrieve objects
  • Select objects via type VM/Network/Storage
  • Implement RegEX filters to sort results

The credentials will be passed via environment variables, and the the search parameters will be passed in through CLI flags.

The Code

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
func parseCredentials(v *vc) (*url.URL, error) {

// Check that an address was actually entered
if v.address == "" {
return nil, fmt.Errorf("No VMware vCenter URL/Address has been submitted")
}

// Check that the URL can be parsed
u, err := url.Parse(v.address)
if err != nil {
return nil, fmt.Errorf("URL can't be parsed, ensure it is https://username:password/<address>/sdk")
}

// Check if a username was entered
if v.username == "" {
// if no username does one exist as part of the url
if u.User.Username() == "" {
return nil, fmt.Errorf("No VMware vCenter Username has been submitted")
}
} else {
// A username was submitted update the url
u.User = url.User(v.username)
}

if v.password == "" {
_, set := u.User.Password()
if set == false {
return nil, fmt.Errorf("No VMware vCenter Password has been submitted")
}
} else {
u.User = url.UserPassword(u.User.Username(), v.password)
}
return u, nil
}
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
func (i *vcInternal) parseInternals(c *govmomi.Client) error {

ctx, cancel := context.WithCancel(context.Background())
defer cancel()

// Create a new finder that will discover the defaults and are looked for Networks/Datastores
f := find.NewFinder(c.Client, true)

// Find one and only datacenter, not sure how VMware linked mode will work
dc, err := f.DatacenterOrDefault(ctx, "")
if err != nil {
return fmt.Errorf("No Datacenter instance could be found inside of vCenter %v", err)
}

// Make future calls local to this datacenter
f.SetDatacenter(dc)

// Find Datastore/Network
i.datastore, err = f.DatastoreOrDefault(ctx, i.findDataStore)
if err != nil {
return fmt.Errorf("%v", err)
}

i.dcFolders, err = dc.Folders(ctx)
if err != nil {
return fmt.Errorf("Error locating default datacenter folder")
}

// Set the host that the VM will be created on
i.hostSystem, err = f.HostSystemOrDefault(ctx, i.findHost)
if err != nil {
return fmt.Errorf("%v", err)
}

// Find the resource pool attached to this host
i.resourcePool, err = i.hostSystem.ResourcePool(ctx)
if err != nil {
return fmt.Errorf("Error locating default resource pool")
}

i.network, err = f.NetworkOrDefault(ctx, i.findNetwork)
if err != nil {
return fmt.Errorf("Network could not be found")
}

return nil
}
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
//VMInventory will create an inventory
func VMInventory(c *govmomi.Client, sortVMs bool) ([]*object.VirtualMachine, error) {

ctx, cancel := context.WithCancel(context.Background())
defer cancel()

// Create a new finder that will discover the defaults and are looked for Networks/Datastores
f := find.NewFinder(c.Client, true)

// Find one and only datacenter, not sure how VMware linked mode will work
dc, err := f.DatacenterOrDefault(ctx, "")
if err != nil {
return nil, fmt.Errorf("No Datacenter instance could be found inside of vCenter %v", err)
}

// Make future calls local to this datacenter
f.SetDatacenter(dc)

vms, err := f.VirtualMachineList(ctx, "*")

if sortVMs == true {
// Sort function to sort by name
sort.Slice(vms, func(i, j int) bool {
switch strings.Compare(vms[i].Name(), vms[j].Name()) {
case -1:
return true
case 1:
return false
}
return vms[i].Name() > vms[j].Name()
})
}

return vms, nil
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
func searchVMS(searchString string, v []*object.VirtualMachine) ([]*object.VirtualMachine, error) {

var newVMList []*object.VirtualMachine
var err error
for x := range v {
matched, err := regexp.MatchString(searchString, v[x].Name())
if err != nil {
break
}
// If the regex matches then add it to the new subset
if matched == true {
newVMList = append(newVMList, v[x])
}
}
if err == nil {
return newVMList, nil
}
return nil, err
}