github.com/vmware/govmomi@v0.43.0/simulator/registry.go (about)

     1  /*
     2  Copyright (c) 2017-2024 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 simulator
    18  
    19  import (
    20  	"encoding/json"
    21  	"fmt"
    22  	"log"
    23  	"os"
    24  	"reflect"
    25  	"strings"
    26  	"sync"
    27  	"sync/atomic"
    28  
    29  	"github.com/vmware/govmomi/simulator/internal"
    30  	"github.com/vmware/govmomi/vim25"
    31  	"github.com/vmware/govmomi/vim25/mo"
    32  	"github.com/vmware/govmomi/vim25/soap"
    33  	"github.com/vmware/govmomi/vim25/types"
    34  )
    35  
    36  // This is a map from a reference type name to a reference value name prefix.
    37  // It's a convention that VirtualCenter follows. The map is not complete, but
    38  // it should cover the most popular objects.
    39  var refValueMap = map[string]string{
    40  	"DistributedVirtualPortgroup":    "dvportgroup",
    41  	"EnvironmentBrowser":             "envbrowser",
    42  	"HostSystem":                     "host",
    43  	"ResourcePool":                   "resgroup",
    44  	"VirtualMachine":                 "vm",
    45  	"VirtualMachineSnapshot":         "snapshot",
    46  	"VmwareDistributedVirtualSwitch": "dvs",
    47  	"DistributedVirtualSwitch":       "dvs",
    48  	"ClusterComputeResource":         "domain-c",
    49  	"Folder":                         "group",
    50  	"StoragePod":                     "group-p",
    51  }
    52  
    53  // Map is the default Registry instance.
    54  //
    55  // TODO/WIP: To support the eventual removal of this unsyncronized global
    56  // variable, the Map should be accessed through any Context.Map that is passed
    57  // in to functions that may need it.
    58  var Map = NewRegistry()
    59  
    60  // RegisterObject interface supports callbacks when objects are created, updated and deleted from the Registry
    61  type RegisterObject interface {
    62  	mo.Reference
    63  	PutObject(mo.Reference)
    64  	UpdateObject(mo.Reference, []types.PropertyChange)
    65  	RemoveObject(*Context, types.ManagedObjectReference)
    66  }
    67  
    68  // Registry manages a map of mo.Reference objects
    69  type Registry struct {
    70  	counter  int64 // Keep first to ensure 64-bit alignment
    71  	m        sync.Mutex
    72  	objects  map[types.ManagedObjectReference]mo.Reference
    73  	handlers map[types.ManagedObjectReference]RegisterObject
    74  	locks    map[types.ManagedObjectReference]*internal.ObjectLock
    75  
    76  	Namespace string
    77  	Path      string
    78  	Handler   func(*Context, *Method) (mo.Reference, types.BaseMethodFault)
    79  
    80  	tagManager tagManager
    81  }
    82  
    83  // tagManager is an interface to simplify internal interaction with the vapi tag manager simulator.
    84  type tagManager interface {
    85  	AttachedObjects(types.VslmTagEntry) ([]types.ManagedObjectReference, types.BaseMethodFault)
    86  	AttachedTags(id types.ManagedObjectReference) ([]types.VslmTagEntry, types.BaseMethodFault)
    87  	AttachTag(types.ManagedObjectReference, types.VslmTagEntry) types.BaseMethodFault
    88  	DetachTag(types.ManagedObjectReference, types.VslmTagEntry) types.BaseMethodFault
    89  }
    90  
    91  // NewRegistry creates a new instances of Registry
    92  func NewRegistry() *Registry {
    93  	r := &Registry{
    94  		objects:  make(map[types.ManagedObjectReference]mo.Reference),
    95  		handlers: make(map[types.ManagedObjectReference]RegisterObject),
    96  		locks:    make(map[types.ManagedObjectReference]*internal.ObjectLock),
    97  
    98  		Namespace: vim25.Namespace,
    99  		Path:      vim25.Path,
   100  	}
   101  
   102  	return r
   103  }
   104  
   105  func (r *Registry) typeFunc(name string) (reflect.Type, bool) {
   106  	if r.Namespace != "" && r.Namespace != vim25.Namespace {
   107  		if kind, ok := defaultMapType(r.Namespace + ":" + name); ok {
   108  			return kind, ok
   109  		}
   110  	}
   111  	return defaultMapType(name)
   112  }
   113  
   114  // typeName returns the type of the given object.
   115  func typeName(item mo.Reference) string {
   116  	return reflect.TypeOf(item).Elem().Name()
   117  }
   118  
   119  // valuePrefix returns the value name prefix of a given object
   120  func valuePrefix(typeName string) string {
   121  	v, ok := refValueMap[typeName]
   122  	if ok {
   123  		if strings.Contains(v, "-") {
   124  			return v
   125  		}
   126  	} else {
   127  		v = strings.ToLower(typeName)
   128  	}
   129  
   130  	return v + "-"
   131  }
   132  
   133  // newReference returns a new MOR, where Type defaults to type of the given item
   134  // and Value defaults to a unique id for the given type.
   135  func (r *Registry) newReference(item mo.Reference) types.ManagedObjectReference {
   136  	ref := item.Reference()
   137  
   138  	if ref.Type == "" {
   139  		ref.Type = typeName(item)
   140  	}
   141  
   142  	if ref.Value == "" {
   143  		n := atomic.AddInt64(&r.counter, 1)
   144  		ref.Value = fmt.Sprintf("%s%d", valuePrefix(ref.Type), n)
   145  	}
   146  
   147  	return ref
   148  }
   149  
   150  func (r *Registry) setReference(item mo.Reference, ref types.ManagedObjectReference) {
   151  	// mo.Reference() returns a value, not a pointer so use reflect to set the Self field
   152  	reflect.ValueOf(item).Elem().FieldByName("Self").Set(reflect.ValueOf(ref))
   153  }
   154  
   155  // AddHandler adds a RegisterObject handler to the Registry.
   156  func (r *Registry) AddHandler(h RegisterObject) {
   157  	r.m.Lock()
   158  	r.handlers[h.Reference()] = h
   159  	r.m.Unlock()
   160  }
   161  
   162  // RemoveHandler removes a RegisterObject handler from the Registry.
   163  func (r *Registry) RemoveHandler(h RegisterObject) {
   164  	r.m.Lock()
   165  	delete(r.handlers, h.Reference())
   166  	r.m.Unlock()
   167  }
   168  
   169  // NewEntity sets Entity().Self with a new, unique Value.
   170  // Useful for creating object instances from templates.
   171  func (r *Registry) NewEntity(item mo.Entity) mo.Entity {
   172  	e := item.Entity()
   173  	e.Self.Value = ""
   174  	e.Self = r.newReference(item)
   175  	return item
   176  }
   177  
   178  // PutEntity sets item.Parent to that of parent.Self before adding item to the Registry.
   179  func (r *Registry) PutEntity(parent mo.Entity, item mo.Entity) mo.Entity {
   180  	e := item.Entity()
   181  
   182  	if parent != nil {
   183  		e.Parent = &parent.Entity().Self
   184  	}
   185  
   186  	r.Put(item)
   187  
   188  	return item
   189  }
   190  
   191  // Get returns the object for the given reference.
   192  func (r *Registry) Get(ref types.ManagedObjectReference) mo.Reference {
   193  	r.m.Lock()
   194  	defer r.m.Unlock()
   195  
   196  	return r.objects[ref]
   197  }
   198  
   199  // Any returns the first instance of entity type specified by kind.
   200  func (r *Registry) Any(kind string) mo.Entity {
   201  	r.m.Lock()
   202  	defer r.m.Unlock()
   203  
   204  	for ref, val := range r.objects {
   205  		if ref.Type == kind {
   206  			return val.(mo.Entity)
   207  		}
   208  	}
   209  
   210  	return nil
   211  }
   212  
   213  // All returns all entities of type specified by kind.
   214  // If kind is empty - all entities will be returned.
   215  func (r *Registry) All(kind string) []mo.Entity {
   216  	r.m.Lock()
   217  	defer r.m.Unlock()
   218  
   219  	var entities []mo.Entity
   220  	for ref, val := range r.objects {
   221  		if kind == "" || ref.Type == kind {
   222  			if e, ok := val.(mo.Entity); ok {
   223  				entities = append(entities, e)
   224  			}
   225  		}
   226  	}
   227  
   228  	return entities
   229  }
   230  
   231  // AllReference returns all mo.Reference objects of type specified by kind.
   232  // If kind is empty - all objects will be returned.
   233  func (r *Registry) AllReference(kind string) []mo.Reference {
   234  	r.m.Lock()
   235  	defer r.m.Unlock()
   236  
   237  	var objs []mo.Reference
   238  	for ref, val := range r.objects {
   239  		if kind == "" || ref.Type == kind {
   240  			objs = append(objs, val)
   241  		}
   242  	}
   243  
   244  	return objs
   245  }
   246  
   247  // applyHandlers calls the given func for each r.handlers
   248  func (r *Registry) applyHandlers(f func(o RegisterObject)) {
   249  	r.m.Lock()
   250  	handlers := make([]RegisterObject, 0, len(r.handlers))
   251  	for _, handler := range r.handlers {
   252  		handlers = append(handlers, handler)
   253  	}
   254  	r.m.Unlock()
   255  
   256  	for i := range handlers {
   257  		f(handlers[i])
   258  	}
   259  }
   260  
   261  func (r *Registry) reference(item mo.Reference) types.ManagedObjectReference {
   262  	ref := item.Reference()
   263  	if ref.Type == "" || ref.Value == "" {
   264  		ref = r.newReference(item)
   265  		r.setReference(item, ref)
   266  	}
   267  	return ref
   268  }
   269  
   270  // Put adds a new object to Registry, generating a ManagedObjectReference if not already set.
   271  func (r *Registry) Put(item mo.Reference) mo.Reference {
   272  	r.m.Lock()
   273  
   274  	if me, ok := item.(mo.Entity); ok {
   275  		me.Entity().ConfigStatus = types.ManagedEntityStatusGreen
   276  		me.Entity().OverallStatus = types.ManagedEntityStatusGreen
   277  		me.Entity().EffectiveRole = []int32{-1} // Admin
   278  	}
   279  
   280  	r.objects[r.reference(item)] = item
   281  
   282  	r.m.Unlock()
   283  
   284  	r.applyHandlers(func(o RegisterObject) {
   285  		o.PutObject(item)
   286  	})
   287  
   288  	return item
   289  }
   290  
   291  // Remove removes an object from the Registry.
   292  func (r *Registry) Remove(ctx *Context, item types.ManagedObjectReference) {
   293  	r.applyHandlers(func(o RegisterObject) {
   294  		o.RemoveObject(ctx, item)
   295  	})
   296  
   297  	r.m.Lock()
   298  	delete(r.objects, item)
   299  	delete(r.handlers, item)
   300  	delete(r.locks, item)
   301  	r.m.Unlock()
   302  }
   303  
   304  // Update dispatches object property changes to RegisterObject handlers,
   305  // such as any PropertyCollector instances with in-progress WaitForUpdates calls.
   306  // The changes are also applied to the given object via mo.ApplyPropertyChange,
   307  // so there is no need to set object fields directly.
   308  func (r *Registry) Update(obj mo.Reference, changes []types.PropertyChange) {
   309  	for i := range changes {
   310  		if changes[i].Op == "" {
   311  			changes[i].Op = types.PropertyChangeOpAssign
   312  		}
   313  		if changes[i].Val != nil {
   314  			rval := reflect.ValueOf(changes[i].Val)
   315  			changes[i].Val = wrapValue(rval, rval.Type())
   316  		}
   317  	}
   318  
   319  	val := getManagedObject(obj).Addr().Interface().(mo.Reference)
   320  
   321  	mo.ApplyPropertyChange(val, changes)
   322  
   323  	r.applyHandlers(func(o RegisterObject) {
   324  		o.UpdateObject(val, changes)
   325  	})
   326  }
   327  
   328  func (r *Registry) AtomicUpdate(ctx *Context, obj mo.Reference, changes []types.PropertyChange) {
   329  	r.WithLock(ctx, obj, func() {
   330  		r.Update(obj, changes)
   331  	})
   332  }
   333  
   334  // getEntityParent traverses up the inventory and returns the first object of type kind.
   335  // If no object of type kind is found, the method will panic when it reaches the
   336  // inventory root Folder where the Parent field is nil.
   337  func (r *Registry) getEntityParent(item mo.Entity, kind string) mo.Entity {
   338  	var ok bool
   339  	for {
   340  		parent := item.Entity().Parent
   341  
   342  		item, ok = r.Get(*parent).(mo.Entity)
   343  		if !ok {
   344  			return nil
   345  		}
   346  		if item.Reference().Type == kind {
   347  			return item
   348  		}
   349  	}
   350  }
   351  
   352  // getEntityDatacenter returns the Datacenter containing the given item
   353  func (r *Registry) getEntityDatacenter(item mo.Entity) *Datacenter {
   354  	dc, ok := r.getEntityParent(item, "Datacenter").(*Datacenter)
   355  	if ok {
   356  		return dc
   357  	}
   358  	return nil
   359  }
   360  
   361  func (r *Registry) getEntityFolder(item mo.Entity, kind string) *mo.Folder {
   362  	dc := r.getEntityDatacenter(item)
   363  
   364  	var ref types.ManagedObjectReference
   365  
   366  	switch kind {
   367  	case "datastore":
   368  		ref = dc.DatastoreFolder
   369  	}
   370  
   371  	folder, _ := asFolderMO(r.Get(ref))
   372  
   373  	// If Model was created with Folder option, use that Folder; else use top-level folder
   374  	for _, child := range folder.ChildEntity {
   375  		if child.Type == "Folder" {
   376  			folder, _ = asFolderMO(r.Get(child))
   377  			break
   378  		}
   379  	}
   380  
   381  	return folder
   382  }
   383  
   384  // getEntityComputeResource returns the ComputeResource parent for the given item.
   385  // A ResourcePool for example may have N Parents of type ResourcePool, but the top
   386  // most Parent pool is always a ComputeResource child.
   387  func (r *Registry) getEntityComputeResource(item mo.Entity) mo.Entity {
   388  	for {
   389  		parent := item.Entity().Parent
   390  
   391  		item = r.Get(*parent).(mo.Entity)
   392  
   393  		switch item.Reference().Type {
   394  		case "ComputeResource":
   395  			return item
   396  		case "ClusterComputeResource":
   397  			return item
   398  		}
   399  	}
   400  }
   401  
   402  func entityName(e mo.Entity) string {
   403  	name := e.Entity().Name
   404  	if name != "" {
   405  		return name
   406  	}
   407  
   408  	obj := getManagedObject(e).Addr().Interface()
   409  
   410  	// The types below have their own 'Name' field, so ManagedEntity.Name (me.Name) is empty.
   411  	// See also mo.Ancestors
   412  	switch x := obj.(type) {
   413  	case *mo.Network:
   414  		return x.Name
   415  	case *mo.DistributedVirtualSwitch:
   416  		return x.Name
   417  	case *mo.DistributedVirtualPortgroup:
   418  		return x.Name
   419  	case *mo.OpaqueNetwork:
   420  		return x.Name
   421  	}
   422  
   423  	log.Panicf("%T object %s does not have a Name", obj, e.Reference())
   424  	return name
   425  }
   426  
   427  // FindByName returns the first mo.Entity of the given refs whose Name field is equal to the given name.
   428  // If there is no match, nil is returned.
   429  // This method is useful for cases where objects are required to have a unique name, such as Datastore with
   430  // a HostStorageSystem or HostSystem within a ClusterComputeResource.
   431  func (r *Registry) FindByName(name string, refs []types.ManagedObjectReference) mo.Entity {
   432  	for _, ref := range refs {
   433  		if e, ok := r.Get(ref).(mo.Entity); ok {
   434  			if name == entityName(e) {
   435  				return e
   436  			}
   437  		}
   438  	}
   439  
   440  	return nil
   441  }
   442  
   443  // FindReference returns the 1st match found in refs, or nil if not found.
   444  func FindReference(refs []types.ManagedObjectReference, match ...types.ManagedObjectReference) *types.ManagedObjectReference {
   445  	for _, ref := range refs {
   446  		for _, m := range match {
   447  			if ref == m {
   448  				return &ref
   449  			}
   450  		}
   451  	}
   452  
   453  	return nil
   454  }
   455  
   456  // AppendReference appends the given refs to field.
   457  func (r *Registry) AppendReference(ctx *Context, obj mo.Reference, field *[]types.ManagedObjectReference, ref ...types.ManagedObjectReference) {
   458  	r.WithLock(ctx, obj, func() {
   459  		*field = append(*field, ref...)
   460  	})
   461  }
   462  
   463  // AddReference appends ref to field if not already in the given field.
   464  func (r *Registry) AddReference(ctx *Context, obj mo.Reference, field *[]types.ManagedObjectReference, ref types.ManagedObjectReference) {
   465  	r.WithLock(ctx, obj, func() {
   466  		if FindReference(*field, ref) == nil {
   467  			*field = append(*field, ref)
   468  		}
   469  	})
   470  }
   471  
   472  // RemoveReference removes ref from the given field.
   473  func RemoveReference(field *[]types.ManagedObjectReference, ref types.ManagedObjectReference) {
   474  	for i, r := range *field {
   475  		if r == ref {
   476  			*field = append((*field)[:i], (*field)[i+1:]...)
   477  			break
   478  		}
   479  	}
   480  }
   481  
   482  // RemoveReference removes ref from the given field.
   483  func (r *Registry) RemoveReference(ctx *Context, obj mo.Reference, field *[]types.ManagedObjectReference, ref types.ManagedObjectReference) {
   484  	r.WithLock(ctx, obj, func() {
   485  		RemoveReference(field, ref)
   486  	})
   487  }
   488  
   489  func (r *Registry) removeString(ctx *Context, obj mo.Reference, field *[]string, val string) {
   490  	r.WithLock(ctx, obj, func() {
   491  		for i, name := range *field {
   492  			if name == val {
   493  				*field = append((*field)[:i], (*field)[i+1:]...)
   494  				break
   495  			}
   496  		}
   497  	})
   498  }
   499  
   500  func (r *Registry) content() types.ServiceContent {
   501  	return r.Get(vim25.ServiceInstance).(*ServiceInstance).Content
   502  }
   503  
   504  // IsESX returns true if this Registry maps an ESX model
   505  func (r *Registry) IsESX() bool {
   506  	return r.content().About.ApiType == "HostAgent"
   507  }
   508  
   509  // IsVPX returns true if this Registry maps a VPX model
   510  func (r *Registry) IsVPX() bool {
   511  	return !r.IsESX()
   512  }
   513  
   514  // SearchIndex returns the SearchIndex singleton
   515  func (r *Registry) SearchIndex() *SearchIndex {
   516  	return r.Get(r.content().SearchIndex.Reference()).(*SearchIndex)
   517  }
   518  
   519  // AlarmManager returns the AlarmManager singleton
   520  func (r *Registry) AlarmManager() *AlarmManager {
   521  	ref := r.content().AlarmManager
   522  	if ref == nil {
   523  		return nil // ESX
   524  	}
   525  	return r.Get(*ref).(*AlarmManager)
   526  }
   527  
   528  // EventManager returns the EventManager singleton
   529  func (r *Registry) EventManager() *EventManager {
   530  	return r.Get(r.content().EventManager.Reference()).(*EventManager)
   531  }
   532  
   533  // FileManager returns the FileManager singleton
   534  func (r *Registry) FileManager() *FileManager {
   535  	return r.Get(r.content().FileManager.Reference()).(*FileManager)
   536  }
   537  
   538  type VirtualDiskManagerInterface interface {
   539  	mo.Reference
   540  	MO() mo.VirtualDiskManager
   541  	CreateVirtualDiskTask(*Context, *types.CreateVirtualDisk_Task) soap.HasFault
   542  	DeleteVirtualDiskTask(*Context, *types.DeleteVirtualDisk_Task) soap.HasFault
   543  	MoveVirtualDiskTask(*Context, *types.MoveVirtualDisk_Task) soap.HasFault
   544  	CopyVirtualDiskTask(*Context, *types.CopyVirtualDisk_Task) soap.HasFault
   545  	QueryVirtualDiskUuid(*Context, *types.QueryVirtualDiskUuid) soap.HasFault
   546  	SetVirtualDiskUuid(*Context, *types.SetVirtualDiskUuid) soap.HasFault
   547  }
   548  
   549  // VirtualDiskManager returns the VirtualDiskManager singleton
   550  func (r *Registry) VirtualDiskManager() VirtualDiskManagerInterface {
   551  	return r.Get(r.content().VirtualDiskManager.Reference()).(VirtualDiskManagerInterface)
   552  }
   553  
   554  // ViewManager returns the ViewManager singleton
   555  func (r *Registry) ViewManager() *ViewManager {
   556  	return r.Get(r.content().ViewManager.Reference()).(*ViewManager)
   557  }
   558  
   559  // UserDirectory returns the UserDirectory singleton
   560  func (r *Registry) UserDirectory() *UserDirectory {
   561  	return r.Get(r.content().UserDirectory.Reference()).(*UserDirectory)
   562  }
   563  
   564  // SessionManager returns the SessionManager singleton
   565  func (r *Registry) SessionManager() *SessionManager {
   566  	return r.Get(r.content().SessionManager.Reference()).(*SessionManager)
   567  }
   568  
   569  // OptionManager returns the OptionManager singleton
   570  func (r *Registry) OptionManager() *OptionManager {
   571  	return r.Get(r.content().Setting.Reference()).(*OptionManager)
   572  }
   573  
   574  // CustomFieldsManager returns CustomFieldsManager singleton
   575  func (r *Registry) CustomFieldsManager() *CustomFieldsManager {
   576  	return r.Get(r.content().CustomFieldsManager.Reference()).(*CustomFieldsManager)
   577  }
   578  
   579  // TenantManager returns TenantManager singleton
   580  func (r *Registry) TenantManager() *TenantManager {
   581  	return r.Get(r.content().TenantManager.Reference()).(*TenantManager)
   582  }
   583  
   584  // VmCompatibilityChecker returns VmCompatibilityChecker singleton
   585  func (r *Registry) VmCompatibilityChecker() *VmCompatibilityChecker {
   586  	return r.Get(r.content().VmCompatibilityChecker.Reference()).(*VmCompatibilityChecker)
   587  }
   588  
   589  // VmProvisioningChecker returns VmProvisioningChecker singleton
   590  func (r *Registry) VmProvisioningChecker() *VmProvisioningChecker {
   591  	return r.Get(r.content().VmProvisioningChecker.Reference()).(*VmProvisioningChecker)
   592  }
   593  
   594  // ExtensionManager returns the ExtensionManager singleton
   595  func (r *Registry) ExtensionManager() *ExtensionManager {
   596  	return r.Get(r.content().ExtensionManager.Reference()).(*ExtensionManager)
   597  }
   598  
   599  func (r *Registry) MarshalJSON() ([]byte, error) {
   600  	r.m.Lock()
   601  	defer r.m.Unlock()
   602  
   603  	vars := struct {
   604  		Objects int `json:"objects"`
   605  		Locks   int `json:"locks"`
   606  	}{
   607  		len(r.objects),
   608  		len(r.locks),
   609  	}
   610  
   611  	return json.Marshal(vars)
   612  }
   613  
   614  func (r *Registry) locker(obj mo.Reference) *internal.ObjectLock {
   615  	var ref types.ManagedObjectReference
   616  
   617  	switch x := obj.(type) {
   618  	case types.ManagedObjectReference:
   619  		ref = x
   620  		obj = r.Get(ref) // to check for sync.Locker
   621  	case *types.ManagedObjectReference:
   622  		ref = *x
   623  		obj = r.Get(ref) // to check for sync.Locker
   624  	default:
   625  		// Use of obj.Reference() may cause a read race, prefer the mo 'Self' field to avoid this
   626  		self := reflect.ValueOf(obj).Elem().FieldByName("Self")
   627  		if self.IsValid() {
   628  			ref = self.Interface().(types.ManagedObjectReference)
   629  		} else {
   630  			ref = obj.Reference()
   631  		}
   632  	}
   633  
   634  	if mu, ok := obj.(sync.Locker); ok {
   635  		// Objects that opt out of default locking are responsible for
   636  		// implementing their own lock sharing, if needed. Returning
   637  		// nil as heldBy means that WithLock will call Lock/Unlock
   638  		// every time.
   639  		return internal.NewObjectLock(mu)
   640  	}
   641  
   642  	r.m.Lock()
   643  	mu, ok := r.locks[ref]
   644  	if !ok {
   645  		mu = internal.NewObjectLock(new(sync.Mutex))
   646  		r.locks[ref] = mu
   647  	}
   648  	r.m.Unlock()
   649  
   650  	return mu
   651  }
   652  
   653  var enableLocker = os.Getenv("VCSIM_LOCKER") != "false"
   654  
   655  // WithLock holds a lock for the given object while then given function is run.
   656  func (r *Registry) WithLock(onBehalfOf *Context, obj mo.Reference, f func()) {
   657  	unlock := r.AcquireLock(onBehalfOf, obj)
   658  	f()
   659  	unlock()
   660  }
   661  
   662  // AcquireLock acquires the lock for onBehalfOf then returns. The lock MUST be
   663  // released by calling the returned function. WithLock should be preferred
   664  // wherever possible.
   665  func (r *Registry) AcquireLock(onBehalfOf *Context, obj mo.Reference) func() {
   666  	if onBehalfOf == nil {
   667  		panic(fmt.Sprintf("Attempt to lock %v with nil onBehalfOf", obj))
   668  	}
   669  
   670  	if !enableLocker {
   671  		return func() {}
   672  	}
   673  
   674  	l := r.locker(obj)
   675  	l.Acquire(onBehalfOf)
   676  	return func() {
   677  		l.Release(onBehalfOf)
   678  	}
   679  }
   680  
   681  // nopLocker can be embedded to opt-out of auto-locking (see Registry.WithLock)
   682  type nopLocker struct{}
   683  
   684  func (*nopLocker) Lock()   {}
   685  func (*nopLocker) Unlock() {}