github.com/vmware/govmomi@v0.51.0/property/collector_test.go (about)

     1  // © Broadcom. All Rights Reserved.
     2  // The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
     3  // SPDX-License-Identifier: Apache-2.0
     4  
     5  package property_test
     6  
     7  import (
     8  	"context"
     9  	"fmt"
    10  	"testing"
    11  	"time"
    12  
    13  	"github.com/vmware/govmomi/find"
    14  	"github.com/vmware/govmomi/object"
    15  	"github.com/vmware/govmomi/property"
    16  	"github.com/vmware/govmomi/simulator"
    17  	"github.com/vmware/govmomi/vim25"
    18  	"github.com/vmware/govmomi/vim25/types"
    19  )
    20  
    21  func TestWaitForUpdatesEx(t *testing.T) {
    22  	model := simulator.VPX()
    23  	model.Datacenter = 1
    24  	model.Cluster = 0
    25  	model.Pool = 0
    26  	model.Machine = 1
    27  	model.Autostart = false
    28  
    29  	simulator.Test(func(ctx context.Context, c *vim25.Client) {
    30  		// Set up the finder and get a VM.
    31  		finder := find.NewFinder(c, true)
    32  		datacenter, err := finder.DefaultDatacenter(ctx)
    33  		if err != nil {
    34  			t.Fatalf("default datacenter not found: %s", err)
    35  		}
    36  		finder.SetDatacenter(datacenter)
    37  		vmList, err := finder.VirtualMachineList(ctx, "*")
    38  		if len(vmList) == 0 {
    39  			t.Fatal("vmList == 0")
    40  		}
    41  		vm := vmList[0]
    42  
    43  		pc, err := property.DefaultCollector(c).Create(ctx)
    44  		if err != nil {
    45  			t.Fatalf("failed to create new property collector: %s", err)
    46  		}
    47  
    48  		// Start a goroutine to wait for power state changes to the VM.
    49  		chanResult := make(chan any)
    50  		cancelCtx, cancel := context.WithCancel(ctx)
    51  		defer cancel()
    52  		go func() {
    53  			defer close(chanResult)
    54  			if err := property.WaitForUpdatesEx(
    55  				cancelCtx,
    56  				pc,
    57  				&property.WaitFilter{
    58  					CreateFilter: types.CreateFilter{
    59  						Spec: getDatacenterToVMFolderFilter(datacenter),
    60  					},
    61  					WaitOptions: property.WaitOptions{
    62  						Options: &types.WaitOptions{
    63  							MaxWaitSeconds: addrOf(int32(3)),
    64  						},
    65  					},
    66  				},
    67  				func(updates []types.ObjectUpdate) bool {
    68  					return waitForPowerStateChanges(
    69  						cancelCtx,
    70  						vm,
    71  						chanResult,
    72  						updates,
    73  						types.VirtualMachinePowerStatePoweredOn)
    74  				},
    75  			); err != nil {
    76  				chanResult <- err
    77  				return
    78  			}
    79  		}()
    80  
    81  		// Power on the VM to cause a property change.
    82  		if _, err := vm.PowerOn(ctx); err != nil {
    83  			t.Fatalf("error while powering on vm: %s", err)
    84  		}
    85  
    86  		select {
    87  		case <-time.After(3 * time.Second):
    88  			t.Fatalf("timed out while waiting for property update")
    89  		case result := <-chanResult:
    90  			switch tResult := result.(type) {
    91  			case types.VirtualMachinePowerState:
    92  				if tResult != types.VirtualMachinePowerStatePoweredOn {
    93  					t.Fatalf("unexpected power state: %s", tResult)
    94  				}
    95  			case error:
    96  				t.Fatalf("error while waiting for updates: %s", tResult)
    97  			}
    98  		}
    99  	}, model)
   100  }
   101  
   102  func TestWaitForUpdatesExEmptyUpdateSet(t *testing.T) {
   103  	model := simulator.VPX()
   104  	model.Datacenter = 1
   105  	model.Cluster = 0
   106  	model.Pool = 0
   107  	model.Machine = 0
   108  	model.Autostart = false
   109  
   110  	simulator.Test(func(ctx context.Context, c *vim25.Client) {
   111  		// Set up the finder and get a VM.
   112  		finder := find.NewFinder(c, true)
   113  		datacenter, err := finder.DefaultDatacenter(ctx)
   114  		if err != nil {
   115  			t.Fatalf("default datacenter not found: %s", err.Error())
   116  		}
   117  		finder.SetDatacenter(datacenter)
   118  		vmList, err := finder.VirtualMachineList(ctx, "*")
   119  		if len(vmList) != 0 {
   120  			t.Fatalf("vmList != 0")
   121  		}
   122  
   123  		pc, err := property.DefaultCollector(c).Create(ctx)
   124  		if err != nil {
   125  			t.Fatalf("failed to create new property collector: %s", err.Error())
   126  		}
   127  
   128  		// Start a goroutine to wait for updates on any VMs.
   129  		// Since there are no VMs in the filter set, we expect to
   130  		// receive an empty update set.
   131  		chanResult := make(chan error)
   132  		cancelCtx, cancel := context.WithCancel(ctx)
   133  		go func() {
   134  			defer close(chanResult)
   135  			_ = pc.WaitForUpdatesEx(
   136  				cancelCtx,
   137  				&property.WaitOptions{},
   138  				func(updates []types.ObjectUpdate) bool {
   139  					var err error
   140  					if len(updates) > 0 {
   141  						err = fmt.Errorf("unexpected update")
   142  					}
   143  					chanResult <- err
   144  					cancel()
   145  					return true
   146  				})
   147  		}()
   148  
   149  		select {
   150  		case <-ctx.Done():
   151  			t.Fatalf("timed out while waiting for updates")
   152  		case err := <-chanResult:
   153  			if err != nil {
   154  				t.Fatalf("error while waiting for updates: %s", err.Error())
   155  			}
   156  		}
   157  	}, model)
   158  }
   159  
   160  func TestRetrievePropertiesOneAtATime(t *testing.T) {
   161  	model := simulator.VPX()
   162  	model.Datacenter = 1
   163  	model.Cluster = 0
   164  	model.Pool = 0
   165  	model.Machine = 3
   166  	model.Autostart = false
   167  
   168  	simulator.Test(func(ctx context.Context, c *vim25.Client) {
   169  		finder := find.NewFinder(c, true)
   170  		datacenter, err := finder.DefaultDatacenter(ctx)
   171  		if err != nil {
   172  			t.Fatalf("default datacenter not found: %s", err)
   173  		}
   174  		finder.SetDatacenter(datacenter)
   175  		pc := property.DefaultCollector(c)
   176  
   177  		resp, err := pc.RetrieveProperties(ctx, types.RetrieveProperties{
   178  			SpecSet: []types.PropertyFilterSpec{
   179  				getDatacenterToVMFolderFilter(datacenter),
   180  			},
   181  		}, 1)
   182  		if err != nil {
   183  			t.Fatalf("failed to retrieve properties one object at a time: %s", err)
   184  		}
   185  
   186  		vmRefs := map[types.ManagedObjectReference]struct{}{}
   187  		for i := range resp.Returnval {
   188  			oc := resp.Returnval[i]
   189  			vmRefs[oc.Obj] = struct{}{}
   190  		}
   191  
   192  		if a, e := len(vmRefs), 3; a != 3 {
   193  			t.Fatalf("unexpected number of vms: a=%d, e=%d", a, e)
   194  		}
   195  
   196  	}, model)
   197  }
   198  
   199  func waitForPowerStateChanges(
   200  	ctx context.Context,
   201  	vm *object.VirtualMachine,
   202  	chanResult chan any,
   203  	updates []types.ObjectUpdate,
   204  	expectedPowerState types.VirtualMachinePowerState) bool {
   205  
   206  	for _, u := range updates {
   207  		if ctx.Err() != nil {
   208  			return false
   209  		}
   210  		if u.Obj != vm.Reference() {
   211  			continue
   212  		}
   213  		for _, cs := range u.ChangeSet {
   214  			if cs.Name == "runtime.powerState" {
   215  				if cs.Val == expectedPowerState {
   216  					select {
   217  					case <-ctx.Done():
   218  						// No-op
   219  					default:
   220  						chanResult <- cs.Val
   221  					}
   222  					return true
   223  				}
   224  			}
   225  		}
   226  	}
   227  	return false
   228  }
   229  
   230  func getDatacenterToVMFolderFilter(dc *object.Datacenter) types.PropertyFilterSpec {
   231  	// Define a wait filter that looks for updates to VM power
   232  	// states for VMs under the specified datacenter.
   233  	return types.PropertyFilterSpec{
   234  		ObjectSet: []types.ObjectSpec{
   235  			{
   236  				Obj:  dc.Reference(),
   237  				Skip: addrOf(true),
   238  				SelectSet: []types.BaseSelectionSpec{
   239  					// Datacenter --> VM folder
   240  					&types.TraversalSpec{
   241  						SelectionSpec: types.SelectionSpec{
   242  							Name: "dcToVMFolder",
   243  						},
   244  						Type: "Datacenter",
   245  						Path: "vmFolder",
   246  						SelectSet: []types.BaseSelectionSpec{
   247  							&types.SelectionSpec{
   248  								Name: "visitFolders",
   249  							},
   250  						},
   251  					},
   252  					// Folder --> children (folder / VM)
   253  					&types.TraversalSpec{
   254  						SelectionSpec: types.SelectionSpec{
   255  							Name: "visitFolders",
   256  						},
   257  						Type: "Folder",
   258  						// Folder --> children (folder / VM)
   259  						Path: "childEntity",
   260  						SelectSet: []types.BaseSelectionSpec{
   261  							// Folder --> child folder
   262  							&types.SelectionSpec{
   263  								Name: "visitFolders",
   264  							},
   265  						},
   266  					},
   267  				},
   268  			},
   269  		},
   270  		PropSet: []types.PropertySpec{
   271  			{
   272  				Type:    "VirtualMachine",
   273  				PathSet: []string{"runtime.powerState"},
   274  			},
   275  		},
   276  	}
   277  }