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 }