github.com/vmware/govmomi@v0.37.1/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  // EventManager returns the EventManager singleton
   520  func (r *Registry) EventManager() *EventManager {
   521  	return r.Get(r.content().EventManager.Reference()).(*EventManager)
   522  }
   523  
   524  // FileManager returns the FileManager singleton
   525  func (r *Registry) FileManager() *FileManager {
   526  	return r.Get(r.content().FileManager.Reference()).(*FileManager)
   527  }
   528  
   529  type VirtualDiskManagerInterface interface {
   530  	mo.Reference
   531  	MO() mo.VirtualDiskManager
   532  	CreateVirtualDiskTask(*Context, *types.CreateVirtualDisk_Task) soap.HasFault
   533  	DeleteVirtualDiskTask(*Context, *types.DeleteVirtualDisk_Task) soap.HasFault
   534  	MoveVirtualDiskTask(*Context, *types.MoveVirtualDisk_Task) soap.HasFault
   535  	CopyVirtualDiskTask(*Context, *types.CopyVirtualDisk_Task) soap.HasFault
   536  	QueryVirtualDiskUuid(*Context, *types.QueryVirtualDiskUuid) soap.HasFault
   537  	SetVirtualDiskUuid(*Context, *types.SetVirtualDiskUuid) soap.HasFault
   538  }
   539  
   540  // VirtualDiskManager returns the VirtualDiskManager singleton
   541  func (r *Registry) VirtualDiskManager() VirtualDiskManagerInterface {
   542  	return r.Get(r.content().VirtualDiskManager.Reference()).(VirtualDiskManagerInterface)
   543  }
   544  
   545  // ViewManager returns the ViewManager singleton
   546  func (r *Registry) ViewManager() *ViewManager {
   547  	return r.Get(r.content().ViewManager.Reference()).(*ViewManager)
   548  }
   549  
   550  // UserDirectory returns the UserDirectory singleton
   551  func (r *Registry) UserDirectory() *UserDirectory {
   552  	return r.Get(r.content().UserDirectory.Reference()).(*UserDirectory)
   553  }
   554  
   555  // SessionManager returns the SessionManager singleton
   556  func (r *Registry) SessionManager() *SessionManager {
   557  	return r.Get(r.content().SessionManager.Reference()).(*SessionManager)
   558  }
   559  
   560  // OptionManager returns the OptionManager singleton
   561  func (r *Registry) OptionManager() *OptionManager {
   562  	return r.Get(r.content().Setting.Reference()).(*OptionManager)
   563  }
   564  
   565  // CustomFieldsManager returns CustomFieldsManager singleton
   566  func (r *Registry) CustomFieldsManager() *CustomFieldsManager {
   567  	return r.Get(r.content().CustomFieldsManager.Reference()).(*CustomFieldsManager)
   568  }
   569  
   570  // TenantManager returns TenantManager singleton
   571  func (r *Registry) TenantManager() *TenantManager {
   572  	return r.Get(r.content().TenantManager.Reference()).(*TenantManager)
   573  }
   574  
   575  // ExtensionManager returns the ExtensionManager singleton
   576  func (r *Registry) ExtensionManager() *ExtensionManager {
   577  	return r.Get(r.content().ExtensionManager.Reference()).(*ExtensionManager)
   578  }
   579  
   580  func (r *Registry) MarshalJSON() ([]byte, error) {
   581  	r.m.Lock()
   582  	defer r.m.Unlock()
   583  
   584  	vars := struct {
   585  		Objects int `json:"objects"`
   586  		Locks   int `json:"locks"`
   587  	}{
   588  		len(r.objects),
   589  		len(r.locks),
   590  	}
   591  
   592  	return json.Marshal(vars)
   593  }
   594  
   595  func (r *Registry) locker(obj mo.Reference) *internal.ObjectLock {
   596  	var ref types.ManagedObjectReference
   597  
   598  	switch x := obj.(type) {
   599  	case types.ManagedObjectReference:
   600  		ref = x
   601  		obj = r.Get(ref) // to check for sync.Locker
   602  	case *types.ManagedObjectReference:
   603  		ref = *x
   604  		obj = r.Get(ref) // to check for sync.Locker
   605  	default:
   606  		// Use of obj.Reference() may cause a read race, prefer the mo 'Self' field to avoid this
   607  		self := reflect.ValueOf(obj).Elem().FieldByName("Self")
   608  		if self.IsValid() {
   609  			ref = self.Interface().(types.ManagedObjectReference)
   610  		} else {
   611  			ref = obj.Reference()
   612  		}
   613  	}
   614  
   615  	if mu, ok := obj.(sync.Locker); ok {
   616  		// Objects that opt out of default locking are responsible for
   617  		// implementing their own lock sharing, if needed. Returning
   618  		// nil as heldBy means that WithLock will call Lock/Unlock
   619  		// every time.
   620  		return internal.NewObjectLock(mu)
   621  	}
   622  
   623  	r.m.Lock()
   624  	mu, ok := r.locks[ref]
   625  	if !ok {
   626  		mu = internal.NewObjectLock(new(sync.Mutex))
   627  		r.locks[ref] = mu
   628  	}
   629  	r.m.Unlock()
   630  
   631  	return mu
   632  }
   633  
   634  var enableLocker = os.Getenv("VCSIM_LOCKER") != "false"
   635  
   636  // WithLock holds a lock for the given object while then given function is run.
   637  func (r *Registry) WithLock(onBehalfOf *Context, obj mo.Reference, f func()) {
   638  	unlock := r.AcquireLock(onBehalfOf, obj)
   639  	f()
   640  	unlock()
   641  }
   642  
   643  // AcquireLock acquires the lock for onBehalfOf then returns. The lock MUST be
   644  // released by calling the returned function. WithLock should be preferred
   645  // wherever possible.
   646  func (r *Registry) AcquireLock(onBehalfOf *Context, obj mo.Reference) func() {
   647  	if onBehalfOf == nil {
   648  		panic(fmt.Sprintf("Attempt to lock %v with nil onBehalfOf", obj))
   649  	}
   650  
   651  	if !enableLocker {
   652  		return func() {}
   653  	}
   654  
   655  	l := r.locker(obj)
   656  	l.Acquire(onBehalfOf)
   657  	return func() {
   658  		l.Release(onBehalfOf)
   659  	}
   660  }
   661  
   662  // nopLocker can be embedded to opt-out of auto-locking (see Registry.WithLock)
   663  type nopLocker struct{}
   664  
   665  func (*nopLocker) Lock()   {}
   666  func (*nopLocker) Unlock() {}