github.com/vmware/govmomi@v0.51.0/simulator/race_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 simulator 6 7 import ( 8 "context" 9 "fmt" 10 "sync" 11 "sync/atomic" 12 "testing" 13 "time" 14 15 "github.com/vmware/govmomi" 16 "github.com/vmware/govmomi/event" 17 "github.com/vmware/govmomi/fault" 18 "github.com/vmware/govmomi/find" 19 "github.com/vmware/govmomi/property" 20 "github.com/vmware/govmomi/view" 21 "github.com/vmware/govmomi/vim25" 22 "github.com/vmware/govmomi/vim25/types" 23 ) 24 25 func TestRace(t *testing.T) { 26 ctx := context.Background() 27 28 m := VPX() 29 30 defer m.Remove() 31 32 err := m.Create() 33 if err != nil { 34 t.Fatal(err) 35 } 36 37 s := m.Service.NewServer() 38 defer s.Close() 39 40 c, err := govmomi.NewClient(ctx, s.URL, true) 41 if err != nil { 42 t.Fatal(err) 43 } 44 45 content := c.Client.ServiceContent 46 47 wctx, cancel := context.WithCancel(ctx) 48 var wg, collectors sync.WaitGroup 49 50 nevents := -1 51 em := event.NewManager(c.Client) 52 53 wg.Add(1) 54 collectors.Add(1) 55 go func() { 56 defer collectors.Done() 57 58 werr := em.Events(wctx, []types.ManagedObjectReference{content.RootFolder}, 50, true, false, 59 func(_ types.ManagedObjectReference, e []types.BaseEvent) error { 60 if nevents == -1 { 61 wg.Done() // make sure we are called at least once before cancel() below 62 nevents = 0 63 } 64 65 nevents += len(e) 66 return nil 67 }) 68 if werr != nil { 69 t.Error(werr) 70 } 71 }() 72 73 collectors.Add(1) 74 go func() { 75 defer collectors.Done() 76 77 ec, werr := em.CreateCollectorForEvents(ctx, types.EventFilterSpec{}) 78 if werr != nil { 79 t.Error(werr) 80 } 81 82 n := 0 83 for { 84 events, werr := ec.ReadNextEvents(ctx, 10) 85 if werr != nil { 86 t.Error(werr) 87 } 88 89 n += len(events) 90 if len(events) != 0 { 91 continue 92 } 93 94 select { 95 case <-wctx.Done(): 96 logf := t.Logf 97 if n == 0 { 98 logf = t.Errorf 99 } 100 logf("ReadNextEvents=%d", n) 101 return 102 case <-time.After(time.Millisecond * 100): 103 } 104 } 105 }() 106 107 ntasks := -1 108 tv, err := view.NewManager(c.Client).CreateTaskView(ctx, content.TaskManager) 109 if err != nil { 110 t.Fatal(err) 111 } 112 113 lv, err := view.NewManager(c.Client).CreateListView(ctx, nil) 114 if err != nil { 115 t.Fatal(err) 116 } 117 118 wg.Add(1) 119 collectors.Add(1) 120 go func() { 121 defer collectors.Done() 122 123 werr := tv.Collect(ctx, func(tasks []types.TaskInfo) { 124 if ntasks == -1 { 125 wg.Done() // make sure we are called at least once before cancel() below 126 ntasks = 0 127 } 128 ntasks += len(tasks) 129 }) 130 if werr != nil { 131 t.Error(werr) 132 } 133 }() 134 135 for i := 0; i < 2; i++ { 136 spec := types.VirtualMachineConfigSpec{ 137 Name: fmt.Sprintf("race-test-%d", i), 138 GuestId: string(types.VirtualMachineGuestOsIdentifierOtherGuest), 139 Files: &types.VirtualMachineFileInfo{ 140 VmPathName: "[LocalDS_0]", 141 }, 142 } 143 144 wg.Add(1) 145 go func() { 146 defer wg.Done() 147 148 finder := find.NewFinder(c.Client, false) 149 pc := property.DefaultCollector(c.Client) 150 dc, err := finder.DefaultDatacenter(ctx) 151 if err != nil { 152 t.Error(err) 153 } 154 155 finder.SetDatacenter(dc) 156 157 f, err := dc.Folders(ctx) 158 if err != nil { 159 t.Error(err) 160 } 161 162 pool, err := finder.ResourcePool(ctx, "DC0_C0/Resources") 163 if err != nil { 164 t.Error(err) 165 } 166 167 for j := 0; j < 2; j++ { 168 cspec := spec // copy spec and give it a unique name 169 cspec.Name += fmt.Sprintf("-%d", j) 170 171 wg.Add(1) 172 go func() { 173 defer wg.Done() 174 175 task, _ := f.VmFolder.CreateVM(ctx, cspec, pool, nil) 176 r, terr := task.WaitForResult(ctx, nil) 177 if terr != nil { 178 t.Error(terr) 179 } 180 _, terr = lv.Add(ctx, []types.ManagedObjectReference{r.Result.(types.ManagedObjectReference)}) 181 if terr != nil { 182 t.Error(terr) 183 } 184 }() 185 } 186 187 vms, err := finder.VirtualMachineList(ctx, "*") 188 if err != nil { 189 t.Error(err) 190 } 191 192 for i := range vms { 193 props := []string{"runtime.powerState"} 194 vm := vms[i] 195 196 wg.Add(1) 197 go func() { 198 defer wg.Done() 199 200 werr := property.Wait(ctx, pc, vm.Reference(), props, func(changes []types.PropertyChange) bool { 201 for _, change := range changes { 202 if change.Name != props[0] { 203 t.Errorf("unexpected property: %s", change.Name) 204 } 205 if change.Val == types.VirtualMachinePowerStatePoweredOff { 206 return true 207 } 208 } 209 210 wg.Add(1) 211 time.AfterFunc(100*time.Millisecond, func() { 212 defer wg.Done() 213 214 _, _ = lv.Remove(ctx, []types.ManagedObjectReference{vm.Reference()}) 215 task, _ := vm.PowerOff(ctx) 216 _ = task.Wait(ctx) 217 }) 218 219 return false 220 221 }) 222 if werr != nil { 223 if werr != context.Canceled { 224 t.Error(werr) 225 } 226 } 227 }() 228 } 229 }() 230 } 231 232 wg.Wait() 233 234 // cancel event and tasks collectors, waiting for them to complete 235 cancel() 236 collectors.Wait() 237 238 t.Logf("collected %d events, %d tasks", nevents, ntasks) 239 if nevents == 0 { 240 t.Error("no events collected") 241 } 242 if ntasks == 0 { 243 t.Error("no tasks collected") 244 } 245 } 246 247 // Race VirtualMachine.Destroy vs Folder.Destroy; the latter removes the VM parent and the former should not panic in that case 248 func TestRaceDestroy(t *testing.T) { 249 ctx := context.Background() 250 251 m := VPX() 252 m.Folder = 1 253 m.Autostart = false 254 defer m.Remove() 255 256 err := m.Create() 257 if err != nil { 258 t.Fatal(err) 259 } 260 261 s := m.Service.NewServer() 262 defer s.Close() 263 264 c, err := govmomi.NewClient(ctx, s.URL, true) 265 if err != nil { 266 t.Fatal(err) 267 } 268 269 finder := find.NewFinder(c.Client) 270 vms, err := finder.VirtualMachineList(ctx, "*") 271 if err != nil { 272 t.Fatal(err) 273 } 274 275 folder, err := finder.Folder(ctx, "vm/F0") 276 if err != nil { 277 t.Fatal(err) 278 } 279 280 notFound := false 281 var wg sync.WaitGroup 282 283 wg.Add(1) 284 go func() { 285 defer wg.Done() 286 for _, vm := range vms { 287 task, err := vm.Destroy(ctx) 288 if err != nil { 289 t.Error(err) 290 } 291 err = task.Wait(ctx) 292 if err != nil { 293 if fault.Is(err, &types.ManagedObjectNotFound{}) { 294 notFound = true 295 } else { 296 t.Error(err) 297 } 298 } 299 } 300 }() 301 302 wg.Add(1) 303 go func() { 304 defer wg.Done() 305 task, err := folder.Destroy(ctx) 306 if err != nil { 307 t.Error(err) 308 } 309 err = task.Wait(ctx) 310 if err != nil { 311 t.Error(err) 312 } 313 }() 314 315 wg.Wait() 316 317 if !notFound { 318 t.Error("expected ManagedObjectNotFound") 319 } 320 } 321 322 func TestRaceVmRelocate(t *testing.T) { 323 Test(func(ctx context.Context, c *vim25.Client) { 324 finder := find.NewFinder(c) 325 326 dc, err := finder.DefaultDatacenter(ctx) 327 if err != nil { 328 t.Fatal(err) 329 } 330 331 finder.SetDatacenter(dc) 332 333 vm, err := finder.VirtualMachine(ctx, "DC0_H0_VM0") 334 if err != nil { 335 t.Fatal(err) 336 } 337 338 folders, err := dc.Folders(ctx) 339 if err != nil { 340 t.Fatal(err) 341 } 342 343 vmFolder := folders.VmFolder 344 345 var failed atomic.Int32 346 var wg sync.WaitGroup 347 348 for i := 0; i < 10; i++ { 349 spec := types.VirtualMachineRelocateSpec{ 350 Folder: types.NewReference(vmFolder.Reference()), 351 } 352 353 wg.Add(1) 354 go func() { 355 defer wg.Done() 356 task, err := vm.Relocate(ctx, spec, types.VirtualMachineMovePriorityDefaultPriority) 357 if err != nil { 358 panic(err) 359 } 360 if err = task.Wait(ctx); err != nil { 361 failed.Add(1) 362 } 363 }() 364 365 vmFolder, err = vmFolder.CreateFolder(ctx, "child") 366 if err != nil { 367 t.Fatal("Failed to create folder", err) 368 } 369 } 370 371 wg.Wait() 372 373 if n := failed.Load(); n != 0 { 374 t.Errorf("%d relocate calls failed", n) 375 } 376 }) 377 }