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