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 }