github.com/vmware/govmomi@v0.51.0/simulator/property_filter.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 "reflect" 9 "strings" 10 11 "github.com/vmware/govmomi/vim25/methods" 12 "github.com/vmware/govmomi/vim25/mo" 13 "github.com/vmware/govmomi/vim25/soap" 14 "github.com/vmware/govmomi/vim25/types" 15 ) 16 17 type PropertyFilter struct { 18 mo.PropertyFilter 19 20 pc *PropertyCollector 21 refs map[types.ManagedObjectReference]struct{} 22 sync bool 23 } 24 25 func (f *PropertyFilter) UpdateObject(ctx *Context, o mo.Reference, changes []types.PropertyChange) { 26 // A PropertyFilter's traversal spec is "applied" on the initial call to WaitForUpdates, 27 // with matching objects tracked in the `refs` field. 28 // New and deleted objects matching the filter are accounted for within PropertyCollector. 29 // But when an object used for the traversal itself is updated (e.g. ListView), 30 // we need to update the tracked `refs` on the next call to WaitForUpdates. 31 ref := o.Reference() 32 33 for _, set := range f.Spec.ObjectSet { 34 if set.Obj == ref && len(set.SelectSet) != 0 { 35 ctx.WithLock(f, func() { f.sync = true }) 36 break 37 } 38 } 39 } 40 41 func (_ *PropertyFilter) PutObject(_ *Context, _ mo.Reference) {} 42 43 func (_ *PropertyFilter) RemoveObject(_ *Context, _ types.ManagedObjectReference) {} 44 45 func (f *PropertyFilter) DestroyPropertyFilter(ctx *Context, c *types.DestroyPropertyFilter) soap.HasFault { 46 body := &methods.DestroyPropertyFilterBody{} 47 48 ctx.WithLock(f.pc, func() { 49 RemoveReference(&f.pc.Filter, c.This) 50 }) 51 52 ctx.Map.RemoveHandler(f) 53 ctx.Session.Remove(ctx, c.This) 54 55 body.Res = &types.DestroyPropertyFilterResponse{} 56 57 return body 58 } 59 60 func (f *PropertyFilter) collect(ctx *Context) (*types.RetrieveResult, types.BaseMethodFault) { 61 req := &types.RetrievePropertiesEx{ 62 SpecSet: []types.PropertyFilterSpec{f.Spec}, 63 } 64 return collect(ctx, req) 65 } 66 67 func (f *PropertyFilter) update(ctx *Context) { 68 ctx.WithLock(f, func() { 69 if f.sync { 70 f.sync = false 71 clear(f.refs) 72 _, _ = f.collect(ctx) 73 } 74 }) 75 } 76 77 // matches returns true if the change matches one of the filter Spec.PropSet 78 func (f *PropertyFilter) matches(ctx *Context, ref types.ManagedObjectReference, change *types.PropertyChange) bool { 79 var kind reflect.Type 80 81 for _, p := range f.Spec.PropSet { 82 if p.Type != ref.Type { 83 if kind == nil { 84 obj := ctx.Map.Get(ref) 85 if obj == nil { // object may have since been deleted 86 continue 87 } 88 kind = getManagedObject(obj).Type() 89 } 90 // e.g. ManagedEntity, ComputeResource 91 field, ok := kind.FieldByName(p.Type) 92 if !(ok && field.Anonymous) { 93 continue 94 } 95 } 96 97 if isTrue(p.All) { 98 return true 99 } 100 101 for _, name := range p.PathSet { 102 if name == change.Name { 103 return true 104 } 105 106 var field mo.Field 107 if field.FromString(name) && field.Item != "" { 108 // "field[key].item" -> "field[key]" 109 item := field.Item 110 field.Item = "" 111 if field.String() == change.Name { 112 change.Name = name 113 change.Val, _ = fieldValue(reflect.ValueOf(change.Val), item) 114 return true 115 } 116 } 117 118 if field.FromString(change.Name) && field.Key != nil { 119 continue // case below does not apply to property index 120 } 121 122 // strings.HasPrefix("runtime.powerState", "runtime") == parent field matches 123 if strings.HasPrefix(change.Name, name) { 124 if obj := ctx.Map.Get(ref); obj != nil { // object may have since been deleted 125 change.Name = name 126 change.Val, _ = fieldValue(reflect.ValueOf(obj), name) 127 } 128 129 return true 130 } 131 } 132 } 133 134 return false 135 } 136 137 // apply the PropertyFilter.Spec to the given ObjectUpdate 138 func (f *PropertyFilter) apply(ctx *Context, change types.ObjectUpdate) types.ObjectUpdate { 139 parents := make(map[string]bool) 140 set := change.ChangeSet 141 change.ChangeSet = nil 142 143 for i, p := range set { 144 if f.matches(ctx, change.Obj, &p) { 145 if p.Name != set[i].Name { 146 // update matches a parent field from the spec. 147 if parents[p.Name] { 148 continue // only return 1 instance of the parent 149 } 150 parents[p.Name] = true 151 } 152 change.ChangeSet = append(change.ChangeSet, p) 153 } 154 } 155 156 return change 157 }