github.com/vmware/govmomi@v0.43.0/property/example_test.go (about)

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