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 }