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() {}