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  }