github.com/vmware/govmomi@v0.51.0/event/processor.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 event
     6  
     7  import (
     8  	"context"
     9  	"fmt"
    10  
    11  	"github.com/vmware/govmomi/property"
    12  	"github.com/vmware/govmomi/view"
    13  	"github.com/vmware/govmomi/vim25/types"
    14  )
    15  
    16  type tailInfo struct {
    17  	t         *eventTailer
    18  	obj       types.ManagedObjectReference
    19  	collector *HistoryCollector
    20  }
    21  
    22  type eventProcessor struct {
    23  	mgr      Manager
    24  	pageSize int32
    25  	kind     []string
    26  	tailers  map[types.ManagedObjectReference]*tailInfo // tailers by collector ref
    27  	callback func(types.ManagedObjectReference, []types.BaseEvent) error
    28  }
    29  
    30  func newEventProcessor(mgr Manager, pageSize int32, callback func(types.ManagedObjectReference, []types.BaseEvent) error, kind []string) *eventProcessor {
    31  	return &eventProcessor{
    32  		mgr:      mgr,
    33  		tailers:  make(map[types.ManagedObjectReference]*tailInfo),
    34  		callback: callback,
    35  		pageSize: pageSize,
    36  		kind:     kind,
    37  	}
    38  }
    39  
    40  func (p *eventProcessor) addObject(ctx context.Context, obj types.ManagedObjectReference) error {
    41  	filter := types.EventFilterSpec{
    42  		Entity: &types.EventFilterSpecByEntity{
    43  			Entity:    obj,
    44  			Recursion: types.EventFilterSpecRecursionOptionAll,
    45  		},
    46  		EventTypeId: p.kind,
    47  	}
    48  
    49  	collector, err := p.mgr.CreateCollectorForEvents(ctx, filter)
    50  	if err != nil {
    51  		return fmt.Errorf("[%#v] %s", obj, err)
    52  	}
    53  
    54  	err = collector.SetPageSize(ctx, p.pageSize)
    55  	if err != nil {
    56  		return err
    57  	}
    58  
    59  	p.tailers[collector.Reference()] = &tailInfo{
    60  		t:         newEventTailer(),
    61  		obj:       obj,
    62  		collector: collector,
    63  	}
    64  
    65  	return nil
    66  }
    67  
    68  func (p *eventProcessor) destroy() {
    69  	for _, info := range p.tailers {
    70  		_ = info.collector.Destroy(context.Background())
    71  	}
    72  }
    73  
    74  func (p *eventProcessor) run(ctx context.Context, tail bool) error {
    75  	if len(p.tailers) == 0 {
    76  		return nil
    77  	}
    78  
    79  	var collectors []types.ManagedObjectReference
    80  	for ref := range p.tailers {
    81  		collectors = append(collectors, ref)
    82  	}
    83  
    84  	c := property.DefaultCollector(p.mgr.Client())
    85  	props := []string{"latestPage"}
    86  
    87  	if len(collectors) == 1 {
    88  		// only one object to follow, don't bother creating a view
    89  		return property.Wait(ctx, c, collectors[0], props, func(pc []types.PropertyChange) bool {
    90  			if err := p.process(collectors[0], pc); err != nil {
    91  				return false
    92  			}
    93  
    94  			return !tail
    95  		})
    96  	}
    97  
    98  	// create and populate a ListView
    99  	m := view.NewManager(p.mgr.Client())
   100  
   101  	list, err := m.CreateListView(ctx, collectors)
   102  	if err != nil {
   103  		return err
   104  	}
   105  
   106  	defer func() {
   107  		_ = list.Destroy(context.Background())
   108  	}()
   109  
   110  	ref := list.Reference()
   111  	filter := new(property.WaitFilter).Add(ref, collectors[0].Type, props, list.TraversalSpec())
   112  
   113  	return property.WaitForUpdates(ctx, c, filter, func(updates []types.ObjectUpdate) bool {
   114  		for _, update := range updates {
   115  			if err := p.process(update.Obj, update.ChangeSet); err != nil {
   116  				return false
   117  			}
   118  		}
   119  
   120  		return !tail
   121  	})
   122  }
   123  
   124  func (p *eventProcessor) process(c types.ManagedObjectReference, pc []types.PropertyChange) error {
   125  	t := p.tailers[c]
   126  	if t == nil {
   127  		return fmt.Errorf("unknown collector %s", c.String())
   128  	}
   129  
   130  	for _, u := range pc {
   131  		evs := t.t.newEvents(u.Val.(types.ArrayOfEvent).Event)
   132  		if len(evs) == 0 {
   133  			continue
   134  		}
   135  
   136  		if err := p.callback(t.obj, evs); err != nil {
   137  			return err
   138  		}
   139  	}
   140  
   141  	return nil
   142  }
   143  
   144  const invalidKey = int32(-1)
   145  
   146  type eventTailer struct {
   147  	lastKey int32
   148  }
   149  
   150  func newEventTailer() *eventTailer {
   151  	return &eventTailer{
   152  		lastKey: invalidKey,
   153  	}
   154  }
   155  
   156  func (t *eventTailer) newEvents(evs []types.BaseEvent) []types.BaseEvent {
   157  	var ret []types.BaseEvent
   158  	if t.lastKey == invalidKey {
   159  		ret = evs
   160  	} else {
   161  		found := false
   162  		for i := range evs {
   163  			if evs[i].GetEvent().Key != t.lastKey {
   164  				continue
   165  			}
   166  
   167  			found = true
   168  			ret = evs[:i]
   169  			break
   170  		}
   171  
   172  		if !found {
   173  			ret = evs
   174  		}
   175  	}
   176  
   177  	if len(ret) > 0 {
   178  		t.lastKey = ret[0].GetEvent().Key
   179  	}
   180  
   181  	return ret
   182  }