github.com/vmware/govmomi@v0.37.1/simulator/property_collector.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 "context" 21 "errors" 22 "log" 23 "path" 24 "reflect" 25 "strings" 26 "sync" 27 "time" 28 29 "github.com/google/uuid" 30 31 "github.com/vmware/govmomi/object" 32 "github.com/vmware/govmomi/simulator/internal" 33 "github.com/vmware/govmomi/vim25" 34 "github.com/vmware/govmomi/vim25/methods" 35 "github.com/vmware/govmomi/vim25/mo" 36 "github.com/vmware/govmomi/vim25/soap" 37 "github.com/vmware/govmomi/vim25/types" 38 ) 39 40 type PropertyCollector struct { 41 mo.PropertyCollector 42 43 nopLocker 44 updates []types.ObjectUpdate 45 mu sync.Mutex 46 cancel context.CancelFunc 47 } 48 49 func NewPropertyCollector(ref types.ManagedObjectReference) object.Reference { 50 s := &PropertyCollector{} 51 s.Self = ref 52 return s 53 } 54 55 var errMissingField = errors.New("missing field") 56 var errEmptyField = errors.New("empty field") 57 var errInvalidField = errors.New("invalid field") 58 59 func getObject(ctx *Context, ref types.ManagedObjectReference) (reflect.Value, bool) { 60 var obj mo.Reference 61 if ctx.Session == nil { 62 // Even without permissions to access an object or specific fields, RetrieveProperties 63 // returns an ObjectContent response as long as the object exists. See retrieveResult.add() 64 obj = ctx.Map.Get(ref) 65 } else { 66 obj = ctx.Session.Get(ref) 67 } 68 69 if obj == nil { 70 return reflect.Value{}, false 71 } 72 73 if ctx.Session == nil && ref.Type == "SessionManager" { 74 // RetrieveProperties on SessionManager without a session always returns empty, 75 // rather than MissingSet + Fault.NotAuthenticated for each field. 76 obj = &mo.SessionManager{Self: ref} 77 } 78 79 // For objects that use internal types that differ from that of the vim25/mo field types. 80 // See EventHistoryCollector for example. 81 type get interface { 82 Get() mo.Reference 83 } 84 if o, ok := obj.(get); ok { 85 obj = o.Get() 86 } 87 88 return getManagedObject(obj), true 89 } 90 91 func getManagedObject(obj mo.Reference) reflect.Value { 92 rval := reflect.ValueOf(obj).Elem() 93 rtype := rval.Type() 94 95 // PropertyCollector is for Managed Object types only (package mo). 96 // If the registry object is not in the mo package, assume it is a wrapper 97 // type where the first field is an embedded mo type. 98 // We need to dig out the mo type for PropSet.All to work properly and 99 // for the case where the type has a field of the same name, for example: 100 // mo.ResourcePool.ResourcePool 101 for { 102 if path.Base(rtype.PkgPath()) == "mo" { 103 break 104 } 105 if rtype.Kind() != reflect.Struct || rtype.NumField() == 0 { 106 log.Panicf("%#v does not have an embedded mo type", obj.Reference()) 107 } 108 rval = rval.Field(0) 109 rtype = rval.Type() 110 if rtype.Kind() == reflect.Pointer { 111 rval = rval.Elem() 112 rtype = rval.Type() 113 } 114 } 115 116 return rval 117 } 118 119 // wrapValue converts slice types to the appropriate ArrayOf type used in property collector responses. 120 func wrapValue(rval reflect.Value, rtype reflect.Type) interface{} { 121 pval := rval.Interface() 122 123 if rval.Kind() == reflect.Slice { 124 // Convert slice to types.ArrayOf* 125 switch v := pval.(type) { 126 case []string: 127 pval = &types.ArrayOfString{ 128 String: v, 129 } 130 case []uint8: 131 pval = &types.ArrayOfByte{ 132 Byte: v, 133 } 134 case []int16: 135 pval = &types.ArrayOfShort{ 136 Short: v, 137 } 138 case []int32: 139 pval = &types.ArrayOfInt{ 140 Int: v, 141 } 142 case []int64: 143 pval = &types.ArrayOfLong{ 144 Long: v, 145 } 146 default: 147 kind := rtype.Elem().Name() 148 // Remove govmomi interface prefix name 149 kind = strings.TrimPrefix(kind, "Base") 150 akind, _ := defaultMapType("ArrayOf" + kind) 151 a := reflect.New(akind) 152 a.Elem().FieldByName(kind).Set(rval) 153 pval = a.Interface() 154 } 155 } 156 157 return pval 158 } 159 160 func fieldValueInterface(f reflect.StructField, rval reflect.Value) interface{} { 161 if rval.Kind() == reflect.Ptr { 162 rval = rval.Elem() 163 } 164 165 return wrapValue(rval, f.Type) 166 } 167 168 func fieldValue(rval reflect.Value, p string) (interface{}, error) { 169 var value interface{} 170 fields := strings.Split(p, ".") 171 172 for i, name := range fields { 173 kind := rval.Type().Kind() 174 175 if kind == reflect.Interface { 176 if rval.IsNil() { 177 continue 178 } 179 rval = rval.Elem() 180 kind = rval.Type().Kind() 181 } 182 183 if kind == reflect.Ptr { 184 if rval.IsNil() { 185 continue 186 } 187 rval = rval.Elem() 188 } 189 190 if kind == reflect.Slice { 191 // field of array field cannot be specified 192 return nil, errInvalidField 193 } 194 195 x := ucFirst(name) 196 val := rval.FieldByName(x) 197 if !val.IsValid() { 198 return nil, errMissingField 199 } 200 201 if isEmpty(val) { 202 return nil, errEmptyField 203 } 204 205 if i == len(fields)-1 { 206 ftype, _ := rval.Type().FieldByName(x) 207 value = fieldValueInterface(ftype, val) 208 break 209 } 210 211 rval = val 212 } 213 214 return value, nil 215 } 216 217 func fieldRefs(f interface{}) []types.ManagedObjectReference { 218 switch fv := f.(type) { 219 case types.ManagedObjectReference: 220 return []types.ManagedObjectReference{fv} 221 case *types.ArrayOfManagedObjectReference: 222 return fv.ManagedObjectReference 223 case nil: 224 // empty field 225 } 226 227 return nil 228 } 229 230 func isEmpty(rval reflect.Value) bool { 231 switch rval.Kind() { 232 case reflect.Ptr: 233 return rval.IsNil() 234 case reflect.String: 235 return rval.Len() == 0 236 } 237 238 return false 239 } 240 241 func isTrue(v *bool) bool { 242 return v != nil && *v 243 } 244 245 func isFalse(v *bool) bool { 246 return v == nil || !*v 247 } 248 249 func toString(v *string) string { 250 if v == nil { 251 return "" 252 } 253 return *v 254 } 255 256 func lcFirst(s string) string { 257 if len(s) < 1 { 258 return s 259 } 260 return strings.ToLower(s[:1]) + s[1:] 261 } 262 263 func ucFirst(s string) string { 264 if len(s) < 1 { 265 return s 266 } 267 return strings.ToUpper(s[:1]) + s[1:] 268 } 269 270 type retrieveResult struct { 271 *types.RetrieveResult 272 req *types.RetrievePropertiesEx 273 collected map[types.ManagedObjectReference]bool 274 specs map[string]*types.TraversalSpec 275 } 276 277 func (rr *retrieveResult) add(ctx *Context, name string, val types.AnyType, content *types.ObjectContent) { 278 if ctx.Session != nil { 279 content.PropSet = append(content.PropSet, types.DynamicProperty{ 280 Name: name, 281 Val: val, 282 }) 283 return 284 } 285 286 content.MissingSet = append(content.MissingSet, types.MissingProperty{ 287 Path: name, 288 Fault: types.LocalizedMethodFault{Fault: &types.NotAuthenticated{ 289 NoPermission: types.NoPermission{ 290 Object: &content.Obj, 291 PrivilegeId: "System.Read", 292 }}, 293 }, 294 }) 295 } 296 297 func (rr *retrieveResult) collectAll(ctx *Context, rval reflect.Value, rtype reflect.Type, content *types.ObjectContent) { 298 for i := 0; i < rval.NumField(); i++ { 299 val := rval.Field(i) 300 301 f := rtype.Field(i) 302 303 if isEmpty(val) || f.Name == "Self" { 304 continue 305 } 306 307 if f.Anonymous { 308 // recurse into embedded field 309 rr.collectAll(ctx, val, f.Type, content) 310 continue 311 } 312 313 rr.add(ctx, lcFirst(f.Name), fieldValueInterface(f, val), content) 314 } 315 } 316 317 func (rr *retrieveResult) collectFields(ctx *Context, rval reflect.Value, fields []string, content *types.ObjectContent) { 318 seen := make(map[string]bool) 319 320 for i := range content.PropSet { 321 seen[content.PropSet[i].Name] = true // mark any already collected via embedded field 322 } 323 324 for _, name := range fields { 325 if seen[name] { 326 // rvc 'ls' includes the "name" property twice, then fails with no error message or stack trace 327 // in RbVmomi::VIM::ObjectContent.to_hash_uncached when it sees the 2nd "name" property. 328 continue 329 } 330 seen[name] = true 331 332 val, err := fieldValue(rval, name) 333 334 switch err { 335 case nil, errEmptyField: 336 rr.add(ctx, name, val, content) 337 case errMissingField: 338 content.MissingSet = append(content.MissingSet, types.MissingProperty{ 339 Path: name, 340 Fault: types.LocalizedMethodFault{Fault: &types.InvalidProperty{ 341 Name: name, 342 }}, 343 }) 344 case errInvalidField: 345 content.MissingSet = append(content.MissingSet, types.MissingProperty{ 346 Path: name, 347 Fault: types.LocalizedMethodFault{Fault: &types.InvalidProperty{ 348 Name: name, 349 }}, 350 }) 351 } 352 } 353 } 354 355 func (rr *retrieveResult) collect(ctx *Context, ref types.ManagedObjectReference) { 356 if rr.collected[ref] { 357 return 358 } 359 360 content := types.ObjectContent{ 361 Obj: ref, 362 } 363 364 rval, ok := getObject(ctx, ref) 365 if !ok { 366 // Possible if a test uses ctx.Map.Remove instead of Destroy_Task 367 tracef("object %s no longer exists", ref) 368 return 369 } 370 371 rtype := rval.Type() 372 match := false 373 374 for _, spec := range rr.req.SpecSet { 375 for _, p := range spec.PropSet { 376 if p.Type != ref.Type && p.Type != rtype.Name() { 377 // e.g. ManagedEntity, ComputeResource 378 field, ok := rtype.FieldByName(p.Type) 379 380 if !(ok && field.Anonymous) { 381 continue 382 } 383 } 384 match = true 385 if isTrue(p.All) { 386 rr.collectAll(ctx, rval, rtype, &content) 387 continue 388 } 389 390 rr.collectFields(ctx, rval, p.PathSet, &content) 391 } 392 } 393 394 if match { 395 rr.Objects = append(rr.Objects, content) 396 } 397 398 rr.collected[ref] = true 399 } 400 401 func (rr *retrieveResult) selectSet(ctx *Context, obj reflect.Value, s []types.BaseSelectionSpec, refs *[]types.ManagedObjectReference) types.BaseMethodFault { 402 for _, ss := range s { 403 ts, ok := ss.(*types.TraversalSpec) 404 if ok { 405 if ts.Name != "" { 406 rr.specs[ts.Name] = ts 407 } 408 } 409 } 410 411 for _, ss := range s { 412 ts, ok := ss.(*types.TraversalSpec) 413 if !ok { 414 ts = rr.specs[ss.GetSelectionSpec().Name] 415 if ts == nil { 416 return &types.InvalidArgument{InvalidProperty: "undefined TraversalSpec name"} 417 } 418 } 419 420 f, _ := fieldValue(obj, ts.Path) 421 422 for _, ref := range fieldRefs(f) { 423 if isFalse(ts.Skip) { 424 *refs = append(*refs, ref) 425 } 426 427 rval, ok := getObject(ctx, ref) 428 if ok { 429 if err := rr.selectSet(ctx, rval, ts.SelectSet, refs); err != nil { 430 return err 431 } 432 } 433 } 434 } 435 436 return nil 437 } 438 439 func (pc *PropertyCollector) collect(ctx *Context, r *types.RetrievePropertiesEx) (*types.RetrieveResult, types.BaseMethodFault) { 440 var refs []types.ManagedObjectReference 441 442 rr := &retrieveResult{ 443 RetrieveResult: &types.RetrieveResult{}, 444 req: r, 445 collected: make(map[types.ManagedObjectReference]bool), 446 specs: make(map[string]*types.TraversalSpec), 447 } 448 449 // Select object references 450 for _, spec := range r.SpecSet { 451 for _, o := range spec.ObjectSet { 452 var rval reflect.Value 453 ok := false 454 ctx.WithLock(o.Obj, func() { rval, ok = getObject(ctx, o.Obj) }) 455 if !ok { 456 if isFalse(spec.ReportMissingObjectsInResults) { 457 return nil, &types.ManagedObjectNotFound{Obj: o.Obj} 458 } 459 continue 460 } 461 462 if o.SelectSet == nil || isFalse(o.Skip) { 463 refs = append(refs, o.Obj) 464 } 465 466 if err := rr.selectSet(ctx, rval, o.SelectSet, &refs); err != nil { 467 return nil, err 468 } 469 } 470 } 471 472 for _, ref := range refs { 473 ctx.WithLock(ref, func() { rr.collect(ctx, ref) }) 474 } 475 476 return rr.RetrieveResult, nil 477 } 478 479 func (pc *PropertyCollector) CreateFilter(ctx *Context, c *types.CreateFilter) soap.HasFault { 480 body := &methods.CreateFilterBody{} 481 482 filter := &PropertyFilter{ 483 pc: pc, 484 refs: make(map[types.ManagedObjectReference]struct{}), 485 } 486 filter.PartialUpdates = c.PartialUpdates 487 filter.Spec = c.Spec 488 489 pc.Filter = append(pc.Filter, ctx.Session.Put(filter).Reference()) 490 491 body.Res = &types.CreateFilterResponse{ 492 Returnval: filter.Self, 493 } 494 495 return body 496 } 497 498 func (pc *PropertyCollector) CreatePropertyCollector(ctx *Context, c *types.CreatePropertyCollector) soap.HasFault { 499 body := &methods.CreatePropertyCollectorBody{} 500 501 cpc := &PropertyCollector{} 502 503 body.Res = &types.CreatePropertyCollectorResponse{ 504 Returnval: ctx.Session.Put(cpc).Reference(), 505 } 506 507 return body 508 } 509 510 func (pc *PropertyCollector) DestroyPropertyCollector(ctx *Context, c *types.DestroyPropertyCollector) soap.HasFault { 511 pc.CancelWaitForUpdates(&types.CancelWaitForUpdates{This: c.This}) 512 513 body := &methods.DestroyPropertyCollectorBody{} 514 515 for _, ref := range pc.Filter { 516 filter := ctx.Session.Get(ref).(*PropertyFilter) 517 filter.DestroyPropertyFilter(ctx, &types.DestroyPropertyFilter{This: ref}) 518 } 519 520 ctx.Session.Remove(ctx, c.This) 521 ctx.Map.Remove(ctx, c.This) 522 523 body.Res = &types.DestroyPropertyCollectorResponse{} 524 525 return body 526 } 527 528 var retrievePropertiesExBook sync.Map 529 530 type retrievePropertiesExPage struct { 531 MaxObjects int32 532 Objects []types.ObjectContent 533 } 534 535 func (pc *PropertyCollector) ContinueRetrievePropertiesEx(ctx *Context, r *types.ContinueRetrievePropertiesEx) soap.HasFault { 536 body := &methods.ContinueRetrievePropertiesExBody{} 537 538 if r.Token == "" { 539 body.Fault_ = Fault("", &types.InvalidPropertyFault{Name: "token"}) 540 return body 541 } 542 543 obj, ok := retrievePropertiesExBook.LoadAndDelete(r.Token) 544 if !ok { 545 body.Fault_ = Fault("", &types.InvalidPropertyFault{Name: "token"}) 546 return body 547 } 548 549 page := obj.(retrievePropertiesExPage) 550 551 var ( 552 objsToStore []types.ObjectContent 553 objsToReturn []types.ObjectContent 554 ) 555 for i := range page.Objects { 556 if page.MaxObjects <= 0 || i < int(page.MaxObjects) { 557 objsToReturn = append(objsToReturn, page.Objects[i]) 558 } else { 559 objsToStore = append(objsToStore, page.Objects[i]) 560 } 561 } 562 563 if len(objsToStore) > 0 { 564 body.Res = &types.ContinueRetrievePropertiesExResponse{} 565 body.Res.Returnval.Token = uuid.NewString() 566 retrievePropertiesExBook.Store( 567 body.Res.Returnval.Token, 568 retrievePropertiesExPage{ 569 MaxObjects: page.MaxObjects, 570 Objects: objsToStore, 571 }) 572 } 573 574 if len(objsToReturn) > 0 { 575 if body.Res == nil { 576 body.Res = &types.ContinueRetrievePropertiesExResponse{} 577 } 578 body.Res.Returnval.Objects = objsToReturn 579 } 580 581 return body 582 } 583 584 func (pc *PropertyCollector) RetrievePropertiesEx(ctx *Context, r *types.RetrievePropertiesEx) soap.HasFault { 585 body := &methods.RetrievePropertiesExBody{} 586 587 res, fault := pc.collect(ctx, r) 588 589 if fault != nil { 590 switch fault.(type) { 591 case *types.ManagedObjectNotFound: 592 body.Fault_ = Fault("The object has already been deleted or has not been completely created", fault) 593 default: 594 body.Fault_ = Fault("", fault) 595 } 596 } else { 597 objects := res.Objects[:0] 598 599 var ( 600 objsToStore []types.ObjectContent 601 objsToReturn []types.ObjectContent 602 ) 603 for i := range res.Objects { 604 if r.Options.MaxObjects <= 0 || i < int(r.Options.MaxObjects) { 605 objsToReturn = append(objsToReturn, res.Objects[i]) 606 } else { 607 objsToStore = append(objsToStore, res.Objects[i]) 608 } 609 } 610 611 if len(objsToStore) > 0 { 612 res.Token = uuid.NewString() 613 retrievePropertiesExBook.Store(res.Token, retrievePropertiesExPage{ 614 MaxObjects: r.Options.MaxObjects, 615 Objects: objsToStore, 616 }) 617 } 618 619 for _, o := range objsToReturn { 620 propSet := o.PropSet[:0] 621 for _, p := range o.PropSet { 622 if p.Val != nil { 623 propSet = append(propSet, p) 624 } 625 } 626 o.PropSet = propSet 627 628 objects = append(objects, o) 629 } 630 res.Objects = objects 631 body.Res = &types.RetrievePropertiesExResponse{ 632 Returnval: res, 633 } 634 } 635 636 return body 637 } 638 639 // RetrieveProperties is deprecated, but govmomi is still using it at the moment. 640 func (pc *PropertyCollector) RetrieveProperties(ctx *Context, r *types.RetrieveProperties) soap.HasFault { 641 body := &methods.RetrievePropertiesBody{} 642 643 res := pc.RetrievePropertiesEx(ctx, &types.RetrievePropertiesEx{ 644 This: r.This, 645 SpecSet: r.SpecSet, 646 }) 647 648 if res.Fault() != nil { 649 body.Fault_ = res.Fault() 650 } else { 651 body.Res = &types.RetrievePropertiesResponse{ 652 Returnval: res.(*methods.RetrievePropertiesExBody).Res.Returnval.Objects, 653 } 654 } 655 656 return body 657 } 658 659 func (pc *PropertyCollector) CancelWaitForUpdates(r *types.CancelWaitForUpdates) soap.HasFault { 660 pc.mu.Lock() 661 if pc.cancel != nil { 662 pc.cancel() 663 } 664 pc.mu.Unlock() 665 666 return &methods.CancelWaitForUpdatesBody{Res: new(types.CancelWaitForUpdatesResponse)} 667 } 668 669 func (pc *PropertyCollector) update(u types.ObjectUpdate) { 670 pc.mu.Lock() 671 pc.updates = append(pc.updates, u) 672 pc.mu.Unlock() 673 } 674 675 func (pc *PropertyCollector) PutObject(o mo.Reference) { 676 pc.update(types.ObjectUpdate{ 677 Obj: o.Reference(), 678 Kind: types.ObjectUpdateKindEnter, 679 ChangeSet: nil, 680 }) 681 } 682 683 func (pc *PropertyCollector) UpdateObject(o mo.Reference, changes []types.PropertyChange) { 684 pc.update(types.ObjectUpdate{ 685 Obj: o.Reference(), 686 Kind: types.ObjectUpdateKindModify, 687 ChangeSet: changes, 688 }) 689 } 690 691 func (pc *PropertyCollector) RemoveObject(_ *Context, ref types.ManagedObjectReference) { 692 pc.update(types.ObjectUpdate{ 693 Obj: ref, 694 Kind: types.ObjectUpdateKindLeave, 695 ChangeSet: nil, 696 }) 697 } 698 699 func (pc *PropertyCollector) apply(ctx *Context, update *types.UpdateSet) types.BaseMethodFault { 700 for _, ref := range pc.Filter { 701 filter := ctx.Session.Get(ref).(*PropertyFilter) 702 703 r := &types.RetrievePropertiesEx{} 704 r.SpecSet = append(r.SpecSet, filter.Spec) 705 706 res, fault := pc.collect(ctx, r) 707 if fault != nil { 708 return fault 709 } 710 711 fu := types.PropertyFilterUpdate{ 712 Filter: ref, 713 } 714 715 for _, o := range res.Objects { 716 if _, ok := filter.refs[o.Obj]; ok { 717 continue 718 } 719 filter.refs[o.Obj] = struct{}{} 720 ou := types.ObjectUpdate{ 721 Obj: o.Obj, 722 Kind: types.ObjectUpdateKindEnter, 723 } 724 725 for _, p := range o.PropSet { 726 ou.ChangeSet = append(ou.ChangeSet, types.PropertyChange{ 727 Op: types.PropertyChangeOpAssign, 728 Name: p.Name, 729 Val: p.Val, 730 }) 731 } 732 733 fu.ObjectSet = append(fu.ObjectSet, ou) 734 } 735 736 if len(fu.ObjectSet) != 0 { 737 update.FilterSet = append(update.FilterSet, fu) 738 } 739 } 740 return nil 741 } 742 743 func (pc *PropertyCollector) WaitForUpdatesEx(ctx *Context, r *types.WaitForUpdatesEx) soap.HasFault { 744 wait, cancel := context.WithCancel(context.Background()) 745 oneUpdate := false 746 if r.Options != nil { 747 if max := r.Options.MaxWaitSeconds; max != nil { 748 // A value of 0 causes WaitForUpdatesEx to do one update calculation and return any results. 749 oneUpdate = (*max == 0) 750 if *max > 0 { 751 wait, cancel = context.WithTimeout(context.Background(), time.Second*time.Duration(*max)) 752 } 753 } 754 } 755 pc.mu.Lock() 756 pc.cancel = cancel 757 pc.mu.Unlock() 758 759 body := &methods.WaitForUpdatesExBody{} 760 761 set := &types.UpdateSet{ 762 Version: r.Version, 763 } 764 765 body.Res = &types.WaitForUpdatesExResponse{ 766 Returnval: set, 767 } 768 769 apply := func() bool { 770 if fault := pc.apply(ctx, set); fault != nil { 771 body.Fault_ = Fault("", fault) 772 body.Res = nil 773 return false 774 } 775 return true 776 } 777 778 if r.Version == "" { 779 ctx.Map.AddHandler(pc) // Listen for create, update, delete of managed objects 780 apply() // Collect current state 781 set.Version = "-" // Next request with Version set will wait via loop below 782 return body 783 } 784 785 ticker := time.NewTicker(20 * time.Millisecond) // allow for updates to accumulate 786 defer ticker.Stop() 787 // Start the wait loop, returning on one of: 788 // - Client calls CancelWaitForUpdates 789 // - MaxWaitSeconds was specified and has been exceeded 790 // - We have updates to send to the client 791 for { 792 select { 793 case <-wait.Done(): 794 body.Res.Returnval = nil 795 switch wait.Err() { 796 case context.Canceled: 797 tracef("%s: WaitForUpdates canceled", pc.Self) 798 body.Fault_ = Fault("", new(types.RequestCanceled)) // CancelWaitForUpdates was called 799 body.Res = nil 800 case context.DeadlineExceeded: 801 tracef("%s: WaitForUpdates MaxWaitSeconds exceeded", pc.Self) 802 } 803 804 return body 805 case <-ticker.C: 806 pc.mu.Lock() 807 updates := pc.updates 808 pc.updates = nil // clear updates collected by the managed object CRUD listeners 809 pc.mu.Unlock() 810 if len(updates) == 0 { 811 if oneUpdate { 812 body.Res.Returnval = nil 813 return body 814 } 815 continue 816 } 817 818 tracef("%s: applying %d updates to %d filters", pc.Self, len(updates), len(pc.Filter)) 819 820 for _, f := range pc.Filter { 821 filter := ctx.Session.Get(f).(*PropertyFilter) 822 fu := types.PropertyFilterUpdate{Filter: f} 823 824 for _, update := range updates { 825 switch update.Kind { 826 case types.ObjectUpdateKindEnter: // Create 827 if !apply() { 828 return body 829 } 830 case types.ObjectUpdateKindModify: // Update 831 tracef("%s has %d changes", update.Obj, len(update.ChangeSet)) 832 if !apply() { // An update may apply to collector traversal specs 833 return body 834 } 835 if _, ok := filter.refs[update.Obj]; ok { 836 // This object has already been applied by the filter, 837 // now check if the property spec applies for this update. 838 update = filter.apply(ctx, update) 839 if len(update.ChangeSet) != 0 { 840 fu.ObjectSet = append(fu.ObjectSet, update) 841 } 842 } 843 case types.ObjectUpdateKindLeave: // Delete 844 if _, ok := filter.refs[update.Obj]; !ok { 845 continue 846 } 847 delete(filter.refs, update.Obj) 848 fu.ObjectSet = append(fu.ObjectSet, update) 849 } 850 } 851 852 if len(fu.ObjectSet) != 0 { 853 set.FilterSet = append(set.FilterSet, fu) 854 } 855 } 856 if len(set.FilterSet) != 0 { 857 return body 858 } 859 if oneUpdate { 860 body.Res.Returnval = nil 861 return body 862 } 863 } 864 } 865 } 866 867 // WaitForUpdates is deprecated, but pyvmomi is still using it at the moment. 868 func (pc *PropertyCollector) WaitForUpdates(ctx *Context, r *types.WaitForUpdates) soap.HasFault { 869 body := &methods.WaitForUpdatesBody{} 870 871 res := pc.WaitForUpdatesEx(ctx, &types.WaitForUpdatesEx{ 872 This: r.This, 873 Version: r.Version, 874 }) 875 876 if res.Fault() != nil { 877 body.Fault_ = res.Fault() 878 } else { 879 body.Res = &types.WaitForUpdatesResponse{ 880 Returnval: *res.(*methods.WaitForUpdatesExBody).Res.Returnval, 881 } 882 } 883 884 return body 885 } 886 887 // Fetch is not documented in the vSphere SDK, but ovftool depends on it. 888 // A Fetch request is converted to a RetrievePropertiesEx method call by vcsim. 889 func (pc *PropertyCollector) Fetch(ctx *Context, req *internal.Fetch) soap.HasFault { 890 body := new(internal.FetchBody) 891 892 if req.This == vim25.ServiceInstance && req.Prop == "content" { 893 content := ctx.Map.content() 894 // ovftool uses API version for 6.0 and fails when these fields are non-nil; TODO 895 content.VStorageObjectManager = nil 896 content.HostProfileManager = nil 897 content.HostSpecManager = nil 898 content.CryptoManager = nil 899 content.HostProfileManager = nil 900 content.HealthUpdateManager = nil 901 content.FailoverClusterConfigurator = nil 902 content.FailoverClusterManager = nil 903 body.Res = &internal.FetchResponse{ 904 Returnval: content, 905 } 906 return body 907 } 908 909 if ctx.Map.Get(req.This) == nil { 910 // The Fetch method supports use of super class types, this is a quick hack to support the cases used by ovftool 911 switch req.This.Type { 912 case "ManagedEntity": 913 for o := range ctx.Map.objects { 914 if o.Value == req.This.Value { 915 req.This.Type = o.Type 916 break 917 } 918 } 919 case "ComputeResource": 920 req.This.Type = "Cluster" + req.This.Type 921 } 922 } 923 924 res := pc.RetrievePropertiesEx(ctx, &types.RetrievePropertiesEx{ 925 SpecSet: []types.PropertyFilterSpec{{ 926 PropSet: []types.PropertySpec{{ 927 Type: req.This.Type, 928 PathSet: []string{req.Prop}, 929 }}, 930 ObjectSet: []types.ObjectSpec{{ 931 Obj: req.This, 932 }}, 933 }}}) 934 935 if res.Fault() != nil { 936 return res 937 } 938 939 obj := res.(*methods.RetrievePropertiesExBody).Res.Returnval.Objects[0] 940 if len(obj.PropSet) == 0 { 941 if len(obj.MissingSet) > 0 { 942 fault := obj.MissingSet[0].Fault 943 body.Fault_ = Fault(fault.LocalizedMessage, fault.Fault) 944 return body 945 } 946 return res 947 } 948 949 body.Res = &internal.FetchResponse{ 950 Returnval: obj.PropSet[0].Val, 951 } 952 return body 953 }