github.com/vmware/govmomi@v0.51.0/property/example_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  	"time"
    11  
    12  	"github.com/vmware/govmomi/find"
    13  	"github.com/vmware/govmomi/property"
    14  	"github.com/vmware/govmomi/simulator"
    15  	"github.com/vmware/govmomi/view"
    16  	"github.com/vmware/govmomi/vim25"
    17  	"github.com/vmware/govmomi/vim25/mo"
    18  	"github.com/vmware/govmomi/vim25/types"
    19  )
    20  
    21  // Example to retrieve properties from a single object
    22  func ExampleCollector_RetrieveOne() {
    23  	simulator.Run(func(ctx context.Context, c *vim25.Client) error {
    24  		pc := property.DefaultCollector(c)
    25  
    26  		obj, err := find.NewFinder(c).VirtualMachine(ctx, "DC0_H0_VM0")
    27  		if err != nil {
    28  			return err
    29  		}
    30  
    31  		var vm mo.VirtualMachine
    32  		err = pc.RetrieveOne(ctx, obj.Reference(), []string{"config.version"}, &vm)
    33  		if err != nil {
    34  			return err
    35  		}
    36  
    37  		fmt.Printf("hardware version %s", vm.Config.Version)
    38  		return nil
    39  	})
    40  	// Output: hardware version vmx-13
    41  }
    42  
    43  func ExampleCollector_Retrieve() {
    44  	simulator.Run(func(ctx context.Context, c *vim25.Client) error {
    45  		pc := property.DefaultCollector(c)
    46  
    47  		obj, err := find.NewFinder(c).HostSystem(ctx, "DC0_H0")
    48  		if err != nil {
    49  			return err
    50  		}
    51  
    52  		var host mo.HostSystem
    53  		err = pc.RetrieveOne(ctx, obj.Reference(), []string{"vm"}, &host)
    54  		if err != nil {
    55  			return err
    56  		}
    57  
    58  		var vms []mo.VirtualMachine
    59  		err = pc.Retrieve(ctx, host.Vm, []string{"name"}, &vms)
    60  		if err != nil {
    61  			return err
    62  		}
    63  
    64  		fmt.Printf("host has %d vms:", len(vms))
    65  		for i := range vms {
    66  			fmt.Print(" ", vms[i].Name)
    67  		}
    68  
    69  		return nil
    70  	})
    71  	// Output: host has 2 vms: DC0_H0_VM0 DC0_H0_VM1
    72  }
    73  
    74  func ExampleWait() {
    75  	simulator.Run(func(ctx context.Context, c *vim25.Client) error {
    76  		pc := property.DefaultCollector(c)
    77  
    78  		vm, err := find.NewFinder(c).VirtualMachine(ctx, "DC0_H0_VM0")
    79  		if err != nil {
    80  			return err
    81  		}
    82  
    83  		// power off VM after some time
    84  		go func() {
    85  			time.Sleep(time.Millisecond * 100)
    86  			_, err := vm.PowerOff(ctx)
    87  			if err != nil {
    88  				panic(err)
    89  			}
    90  		}()
    91  
    92  		return property.Wait(ctx, pc, vm.Reference(), []string{"runtime.powerState"}, func(changes []types.PropertyChange) bool {
    93  			for _, change := range changes {
    94  				state := change.Val.(types.VirtualMachinePowerState)
    95  				fmt.Println(state)
    96  				if state == types.VirtualMachinePowerStatePoweredOff {
    97  					return true
    98  				}
    99  			}
   100  
   101  			// continue polling
   102  			return false
   103  		})
   104  	})
   105  	// Output:
   106  	// poweredOn
   107  	// poweredOff
   108  }
   109  
   110  func ExampleCollector_WaitForUpdatesEx_addingRemovingPropertyFilters() {
   111  	model := simulator.VPX()
   112  	model.Datacenter = 1
   113  	model.Cluster = 0
   114  	model.Pool = 0
   115  	model.Machine = 1
   116  	model.Autostart = false
   117  
   118  	simulator.Run(func(ctx context.Context, c *vim25.Client) error {
   119  		// Set up the finder and get a VM.
   120  		finder := find.NewFinder(c, true)
   121  		datacenter, err := finder.DefaultDatacenter(ctx)
   122  		if err != nil {
   123  			return fmt.Errorf("default datacenter not found: %w", err)
   124  		}
   125  		finder.SetDatacenter(datacenter)
   126  		vmList, err := finder.VirtualMachineList(ctx, "*")
   127  		if len(vmList) == 0 {
   128  			return fmt.Errorf("vmList == 0")
   129  		}
   130  		vm := vmList[0]
   131  
   132  		pc, err := property.DefaultCollector(c).Create(ctx)
   133  		if err != nil {
   134  			return fmt.Errorf("failed to create new property collector: %w", err)
   135  		}
   136  
   137  		// Start a goroutine to wait for power state changes to the VM. They
   138  		// should not be triggered as there is no property filter yet defined.
   139  		chanResult := make(chan any)
   140  		cancelCtx, cancel := context.WithCancel(ctx)
   141  		defer cancel()
   142  		go func() {
   143  			if err := pc.WaitForUpdatesEx(
   144  				cancelCtx,
   145  				&property.WaitOptions{},
   146  				func(updates []types.ObjectUpdate) bool {
   147  					return waitForPowerStateChanges(
   148  						cancelCtx,
   149  						vm,
   150  						chanResult,
   151  						updates,
   152  						types.VirtualMachinePowerStatePoweredOff)
   153  				}); err != nil {
   154  
   155  				chanResult <- err
   156  				return
   157  			}
   158  		}()
   159  
   160  		// Power on the VM to cause a property change.
   161  		if _, err := vm.PowerOn(ctx); err != nil {
   162  			return fmt.Errorf("error while powering on vm: %w", err)
   163  		}
   164  
   165  		// The power change should be ignored.
   166  		select {
   167  		case <-time.After(3 * time.Second):
   168  			fmt.Println("poweredOn event not received")
   169  		case result := <-chanResult:
   170  			switch tResult := result.(type) {
   171  			case types.VirtualMachinePowerState:
   172  				return fmt.Errorf("update should not have been received without a property filter")
   173  			case error:
   174  				return fmt.Errorf("error while waiting for updates: %v", tResult)
   175  			}
   176  		}
   177  
   178  		// Now create a property filter that will catch the update.
   179  		pf, err := pc.CreateFilter(
   180  			ctx,
   181  			types.CreateFilter{Spec: getDatacenterToVMFolderFilter(datacenter)},
   182  		)
   183  		if err != nil {
   184  			return fmt.Errorf("failed to create dc2vm property filter: %w", err)
   185  		}
   186  
   187  		// Power off the VM to cause a property change.
   188  		if _, err := vm.PowerOff(ctx); err != nil {
   189  			return fmt.Errorf("error while powering off vm: %w", err)
   190  		}
   191  
   192  		// The power change should now be noticed.
   193  		select {
   194  		case <-time.After(3 * time.Second):
   195  			return fmt.Errorf("timed out while waiting for property update")
   196  		case result := <-chanResult:
   197  			switch tResult := result.(type) {
   198  			case types.VirtualMachinePowerState:
   199  				if tResult != types.VirtualMachinePowerStatePoweredOff {
   200  					return fmt.Errorf("unexpected power state: %v", tResult)
   201  				}
   202  				fmt.Println("poweredOff event received")
   203  			case error:
   204  				return fmt.Errorf("error while waiting for updates: %w", tResult)
   205  			}
   206  		}
   207  
   208  		// Destroy the property filter and repeat, and the power change should
   209  		// once again be ignored.
   210  		if err := pf.Destroy(ctx); err != nil {
   211  			return fmt.Errorf("failed to destroy property filter: %w", err)
   212  		}
   213  
   214  		// Power on the VM to cause a property change.
   215  		if _, err := vm.PowerOn(ctx); err != nil {
   216  			return fmt.Errorf("error while powering on vm: %w", err)
   217  		}
   218  
   219  		// The power change should be ignored.
   220  		select {
   221  		case <-time.After(3 * time.Second):
   222  			fmt.Println("poweredOn event not received")
   223  		case result := <-chanResult:
   224  			switch tResult := result.(type) {
   225  			case types.VirtualMachinePowerState:
   226  				return fmt.Errorf("update should not have been received after property filter was destroyed")
   227  			case error:
   228  				return fmt.Errorf("error while waiting for updates: %v", tResult)
   229  			}
   230  		}
   231  
   232  		return nil
   233  	}, model)
   234  
   235  	// Output:
   236  	// poweredOn event not received
   237  	// poweredOff event received
   238  	// poweredOn event not received
   239  }
   240  
   241  func ExampleCollector_WaitForUpdatesEx_errConcurrentCollector() {
   242  	simulator.Run(func(ctx context.Context, c *vim25.Client) error {
   243  		pc := property.DefaultCollector(c)
   244  
   245  		waitOptions := property.WaitOptions{
   246  			Options: &types.WaitOptions{
   247  				MaxWaitSeconds: addrOf(int32(1)),
   248  			},
   249  		}
   250  
   251  		onUpdatesFn := func(_ []types.ObjectUpdate) bool {
   252  			return false
   253  		}
   254  
   255  		waitForChanges := func(chanErr chan error) {
   256  			defer close(chanErr)
   257  			chanErr <- pc.WaitForUpdatesEx(ctx, &waitOptions, onUpdatesFn)
   258  		}
   259  
   260  		// Start two goroutines that wait for changes, but only one will begin
   261  		// waiting -- the other will return property.ErrConcurrentCollector.
   262  		chanErr1, chanErr2 := make(chan error), make(chan error)
   263  		go waitForChanges(chanErr1)
   264  		go waitForChanges(chanErr2)
   265  
   266  		err1 := <-chanErr1
   267  		err2 := <-chanErr2
   268  
   269  		if err1 == nil && err2 == nil {
   270  			return fmt.Errorf(
   271  				"one of the WaitForUpdate calls should have returned %s",
   272  				property.ErrConcurrentCollector)
   273  		}
   274  
   275  		if err1 == property.ErrConcurrentCollector &&
   276  			err2 == property.ErrConcurrentCollector {
   277  
   278  			return fmt.Errorf(
   279  				"both of the WaitForUpdate calls returned %s",
   280  				property.ErrConcurrentCollector)
   281  		}
   282  
   283  		fmt.Println("WaitForUpdatesEx call succeeded")
   284  		fmt.Println("WaitForUpdatesEx call returned ErrConcurrentCollector")
   285  
   286  		// The third WaitForUpdatesEx call should be able to successfully obtain
   287  		// the lock since the other two calls are completed.
   288  		if err := pc.WaitForUpdatesEx(ctx, &waitOptions, onUpdatesFn); err != nil {
   289  			return fmt.Errorf(
   290  				"unexpected error from third call to WaitForUpdatesEx: %s", err)
   291  		}
   292  
   293  		fmt.Println("WaitForUpdatesEx call succeeded")
   294  
   295  		return nil
   296  	})
   297  
   298  	// Output:
   299  	// WaitForUpdatesEx call succeeded
   300  	// WaitForUpdatesEx call returned ErrConcurrentCollector
   301  	// WaitForUpdatesEx call succeeded
   302  }
   303  
   304  func ExampleCollector_WaitForUpdatesEx_pagination() {
   305  	model := simulator.VPX()
   306  	model.Cluster = 3
   307  	model.Machine = 42
   308  
   309  	simulator.Run(func(ctx context.Context, c *vim25.Client) error {
   310  		pc := property.DefaultCollector(c)
   311  		m := view.NewManager(c)
   312  
   313  		// Note: both types can be collected with 1 ContainerView and 1 PropertyFilter,
   314  		// but we are creating 2 PropertyFilter for example purposes.
   315  		kinds := []string{"HostSystem", "VirtualMachine"}
   316  		for _, kind := range kinds {
   317  			v, err := m.CreateContainerView(ctx, c.ServiceContent.RootFolder, []string{kind}, true)
   318  			if err != nil {
   319  				return err
   320  			}
   321  
   322  			defer v.Destroy(ctx)
   323  
   324  			filter := new(property.WaitFilter).Add(v.Reference(), kind, []string{"name"}, v.TraversalSpec())
   325  
   326  			f, err := pc.CreateFilter(ctx, filter.CreateFilter)
   327  			if err != nil {
   328  				return err
   329  			}
   330  
   331  			defer f.Destroy(ctx)
   332  		}
   333  
   334  		options := &property.WaitOptions{
   335  			Options: &types.WaitOptions{MaxObjectUpdates: 50},
   336  		}
   337  
   338  		// Callback is invoked once for each FilterSet:
   339  		// 1st WaitForUpdatesEx call returns 2 FilterSet, 10 hosts + 40 vms
   340  		// Next 4 calls are 1 FilterSet of 50, 50 and 28 vms
   341  		callbacks := 0
   342  		objects := make(map[string]int)
   343  
   344  		err := pc.WaitForUpdatesEx(ctx, options, func(updates []types.ObjectUpdate) bool {
   345  			for _, update := range updates {
   346  				objects[update.Obj.Type]++
   347  			}
   348  			callbacks++
   349  			return options.Truncated == false
   350  		})
   351  		if err != nil {
   352  			return err
   353  		}
   354  
   355  		fmt.Printf("%d Callbacks\n", callbacks)
   356  		for _, kind := range kinds {
   357  			fmt.Printf("%d %s\n", objects[kind], kind)
   358  		}
   359  		return nil
   360  	}, model)
   361  	// Output:
   362  	// 5 Callbacks
   363  	// 10 HostSystem
   364  	// 168 VirtualMachine
   365  }