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  }