github.com/vmware/govmomi@v0.43.0/simulator/event_manager.go (about)

     1  /*
     2  Copyright (c) 2018-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  	"bytes"
    21  	"container/list"
    22  	"log"
    23  	"reflect"
    24  	"text/template"
    25  	"time"
    26  
    27  	"github.com/vmware/govmomi/simulator/esx"
    28  	"github.com/vmware/govmomi/vim25/methods"
    29  	"github.com/vmware/govmomi/vim25/mo"
    30  	"github.com/vmware/govmomi/vim25/soap"
    31  	"github.com/vmware/govmomi/vim25/types"
    32  )
    33  
    34  var (
    35  	logEvents = false
    36  )
    37  
    38  type EventManager struct {
    39  	mo.EventManager
    40  
    41  	history   *history
    42  	key       int32
    43  	templates map[string]*template.Template
    44  }
    45  
    46  func (m *EventManager) init(r *Registry) {
    47  	if len(m.Description.EventInfo) == 0 {
    48  		m.Description.EventInfo = esx.EventInfo
    49  	}
    50  	if m.MaxCollector == 0 {
    51  		// In real VC this default can be changed via OptionManager "event.maxCollectors"
    52  		m.MaxCollector = maxCollectors
    53  	}
    54  
    55  	m.history = newHistory()
    56  	m.templates = make(map[string]*template.Template)
    57  }
    58  
    59  func (m *EventManager) createCollector(ctx *Context, req *types.CreateCollectorForEvents) (*EventHistoryCollector, *soap.Fault) {
    60  	size, err := validatePageSize(req.Filter.MaxCount)
    61  	if err != nil {
    62  		return nil, err
    63  	}
    64  
    65  	if len(m.history.collectors) >= int(m.MaxCollector) {
    66  		return nil, Fault("Too many event collectors to create", new(types.InvalidState))
    67  	}
    68  
    69  	collector := &EventHistoryCollector{
    70  		HistoryCollector: newHistoryCollector(ctx, m.history, size),
    71  	}
    72  	collector.Filter = req.Filter
    73  
    74  	return collector, nil
    75  }
    76  
    77  func (m *EventManager) CreateCollectorForEvents(ctx *Context, req *types.CreateCollectorForEvents) soap.HasFault {
    78  	body := new(methods.CreateCollectorForEventsBody)
    79  	collector, err := m.createCollector(ctx, req)
    80  	if err != nil {
    81  		body.Fault_ = err
    82  		return body
    83  	}
    84  
    85  	collector.fill = func(x *Context) { m.fillPage(x, collector) }
    86  	collector.fill(ctx)
    87  
    88  	body.Res = &types.CreateCollectorForEventsResponse{
    89  		Returnval: m.history.add(ctx, collector),
    90  	}
    91  
    92  	return body
    93  }
    94  
    95  func (m *EventManager) QueryEvents(ctx *Context, req *types.QueryEvents) soap.HasFault {
    96  	if ctx.Map.IsESX() {
    97  		return &methods.QueryEventsBody{
    98  			Fault_: Fault("", new(types.NotImplemented)),
    99  		}
   100  	}
   101  
   102  	body := new(methods.QueryEventsBody)
   103  	collector, err := m.createCollector(ctx, &types.CreateCollectorForEvents{Filter: req.Filter})
   104  	if err != nil {
   105  		body.Fault_ = err
   106  		return body
   107  	}
   108  
   109  	m.fillPage(ctx, collector)
   110  
   111  	body.Res = &types.QueryEventsResponse{
   112  		Returnval: collector.GetLatestPage(),
   113  	}
   114  
   115  	return body
   116  }
   117  
   118  // formatMessage applies the EventDescriptionEventDetail.FullFormat template to the given event's FullFormattedMessage field.
   119  func (m *EventManager) formatMessage(event types.BaseEvent) {
   120  	id := reflect.ValueOf(event).Elem().Type().Name()
   121  	e := event.GetEvent()
   122  
   123  	t, ok := m.templates[id]
   124  	if !ok {
   125  		for _, info := range m.Description.EventInfo {
   126  			if info.Key == id {
   127  				t = template.Must(template.New(id).Parse(info.FullFormat))
   128  				m.templates[id] = t
   129  				break
   130  			}
   131  		}
   132  	}
   133  
   134  	if t != nil {
   135  		var buf bytes.Buffer
   136  		if err := t.Execute(&buf, event); err != nil {
   137  			log.Print(err)
   138  		}
   139  		e.FullFormattedMessage = buf.String()
   140  	}
   141  
   142  	if logEvents {
   143  		log.Printf("[%s] %s", id, e.FullFormattedMessage)
   144  	}
   145  }
   146  
   147  func (m *EventManager) PostEvent(ctx *Context, req *types.PostEvent) soap.HasFault {
   148  	m.key++
   149  	event := req.EventToPost.GetEvent()
   150  	event.Key = m.key
   151  	event.ChainId = event.Key
   152  	event.CreatedTime = time.Now()
   153  	event.UserName = ctx.Session.UserName
   154  
   155  	m.formatMessage(req.EventToPost)
   156  
   157  	pushHistory(m.history.page, req.EventToPost)
   158  
   159  	for _, hc := range m.history.collectors {
   160  		c := hc.(*EventHistoryCollector)
   161  		ctx.WithLock(c, func() {
   162  			if c.eventMatches(ctx, req.EventToPost) {
   163  				pushHistory(c.page, req.EventToPost)
   164  				ctx.Map.Update(c, []types.PropertyChange{{Name: "latestPage", Val: c.GetLatestPage()}})
   165  			}
   166  		})
   167  	}
   168  
   169  	if m := ctx.Map.AlarmManager(); m != nil {
   170  		ctx.WithLock(m, func() { m.postEvent(ctx, req.EventToPost) })
   171  	}
   172  
   173  	return &methods.PostEventBody{
   174  		Res: new(types.PostEventResponse),
   175  	}
   176  }
   177  
   178  type EventHistoryCollector struct {
   179  	mo.EventHistoryCollector
   180  
   181  	*HistoryCollector
   182  }
   183  
   184  // doEntityEventArgument calls f for each entity argument in the event.
   185  // If f returns true, the iteration stops.
   186  func doEntityEventArgument(event types.BaseEvent, f func(types.ManagedObjectReference, *types.EntityEventArgument) bool) bool {
   187  	e := event.GetEvent()
   188  
   189  	if arg := e.Vm; arg != nil {
   190  		if f(arg.Vm, &arg.EntityEventArgument) {
   191  			return true
   192  		}
   193  	}
   194  
   195  	if arg := e.Host; arg != nil {
   196  		if f(arg.Host, &arg.EntityEventArgument) {
   197  			return true
   198  		}
   199  	}
   200  
   201  	if arg := e.ComputeResource; arg != nil {
   202  		if f(arg.ComputeResource, &arg.EntityEventArgument) {
   203  			return true
   204  		}
   205  	}
   206  
   207  	if arg := e.Ds; arg != nil {
   208  		if f(arg.Datastore, &arg.EntityEventArgument) {
   209  			return true
   210  		}
   211  	}
   212  
   213  	if arg := e.Net; arg != nil {
   214  		if f(arg.Network, &arg.EntityEventArgument) {
   215  			return true
   216  		}
   217  	}
   218  
   219  	if arg := e.Dvs; arg != nil {
   220  		if f(arg.Dvs, &arg.EntityEventArgument) {
   221  			return true
   222  		}
   223  	}
   224  
   225  	if arg := e.Datacenter; arg != nil {
   226  		if f(arg.Datacenter, &arg.EntityEventArgument) {
   227  			return true
   228  		}
   229  	}
   230  
   231  	return false
   232  }
   233  
   234  // eventFilterSelf returns true if self is one of the entity arguments in the event.
   235  func eventFilterSelf(event types.BaseEvent, self types.ManagedObjectReference) bool {
   236  	if x, ok := event.(*types.EventEx); ok {
   237  		if self.Type == x.ObjectType && self.Value == x.ObjectId {
   238  			return true
   239  		}
   240  	}
   241  	return doEntityEventArgument(event, func(ref types.ManagedObjectReference, _ *types.EntityEventArgument) bool {
   242  		return self == ref
   243  	})
   244  }
   245  
   246  // eventFilterChildren returns true if a child of self is one of the entity arguments in the event.
   247  func eventFilterChildren(ctx *Context, root types.ManagedObjectReference, event types.BaseEvent) bool {
   248  	return doEntityEventArgument(event, func(ref types.ManagedObjectReference, _ *types.EntityEventArgument) bool {
   249  		seen := false
   250  
   251  		var match func(types.ManagedObjectReference)
   252  
   253  		match = func(child types.ManagedObjectReference) {
   254  			if child == ref {
   255  				seen = true
   256  				return
   257  			}
   258  
   259  			walk(ctx.Map.Get(child), match)
   260  		}
   261  
   262  		walk(ctx.Map.Get(root), match)
   263  
   264  		return seen
   265  	})
   266  }
   267  
   268  // entityMatches returns true if the spec Entity filter matches the event.
   269  func (c *EventHistoryCollector) entityMatches(ctx *Context, event types.BaseEvent, spec *types.EventFilterSpec) bool {
   270  	e := spec.Entity
   271  	if e == nil {
   272  		return true
   273  	}
   274  
   275  	isRootFolder := c.root == e.Entity
   276  
   277  	switch e.Recursion {
   278  	case types.EventFilterSpecRecursionOptionSelf:
   279  		return isRootFolder || eventFilterSelf(event, e.Entity)
   280  	case types.EventFilterSpecRecursionOptionChildren:
   281  		return eventFilterChildren(ctx, e.Entity, event)
   282  	case types.EventFilterSpecRecursionOptionAll:
   283  		if isRootFolder || eventFilterSelf(event, e.Entity) {
   284  			return true
   285  		}
   286  		return eventFilterChildren(ctx, e.Entity, event)
   287  	}
   288  
   289  	return false
   290  }
   291  
   292  // chainMatches returns true if spec.EventChainId matches the event.
   293  func (c *EventHistoryCollector) chainMatches(_ *Context, event types.BaseEvent, spec *types.EventFilterSpec) bool {
   294  	e := event.GetEvent()
   295  	if spec.EventChainId != 0 {
   296  		if e.ChainId != spec.EventChainId {
   297  			return false
   298  		}
   299  	}
   300  	return true
   301  }
   302  
   303  // typeMatches returns true if one of the spec EventTypeId types matches the event.
   304  func (c *EventHistoryCollector) typeMatches(_ *Context, event types.BaseEvent, spec *types.EventFilterSpec) bool {
   305  	if len(spec.EventTypeId) == 0 {
   306  		return true
   307  	}
   308  
   309  	matches := func(name string) bool {
   310  		for _, id := range spec.EventTypeId {
   311  			if id == name {
   312  				return true
   313  			}
   314  		}
   315  		return false
   316  	}
   317  
   318  	if x, ok := event.(*types.EventEx); ok {
   319  		return matches(x.EventTypeId)
   320  	}
   321  
   322  	kind := reflect.ValueOf(event).Elem().Type()
   323  
   324  	if matches(kind.Name()) {
   325  		return true // concrete type
   326  	}
   327  
   328  	field, ok := kind.FieldByNameFunc(matches)
   329  	if ok {
   330  		return field.Anonymous // base type (embedded field)
   331  	}
   332  	return false
   333  }
   334  
   335  func (c *EventHistoryCollector) timeMatches(_ *Context, event types.BaseEvent, spec *types.EventFilterSpec) bool {
   336  	if spec.Time == nil {
   337  		return true
   338  	}
   339  
   340  	created := event.GetEvent().CreatedTime
   341  
   342  	if begin := spec.Time.BeginTime; begin != nil {
   343  		if created.Before(*begin) {
   344  			return false
   345  		}
   346  	}
   347  
   348  	if end := spec.Time.EndTime; end != nil {
   349  		if created.After(*end) {
   350  			return false
   351  		}
   352  	}
   353  
   354  	return true
   355  }
   356  
   357  // eventMatches returns true one of the filters matches the event.
   358  func (c *EventHistoryCollector) eventMatches(ctx *Context, event types.BaseEvent) bool {
   359  	spec := c.Filter.(types.EventFilterSpec)
   360  
   361  	matchers := []func(*Context, types.BaseEvent, *types.EventFilterSpec) bool{
   362  		c.chainMatches,
   363  		c.typeMatches,
   364  		c.timeMatches,
   365  		c.entityMatches,
   366  		// TODO: spec.UserName, etc
   367  	}
   368  
   369  	for _, match := range matchers {
   370  		if !match(ctx, event, &spec) {
   371  			return false
   372  		}
   373  	}
   374  
   375  	return true
   376  }
   377  
   378  // fillPage copies the manager's latest events into the collector's page with Filter applied.
   379  func (m *EventManager) fillPage(ctx *Context, c *EventHistoryCollector) {
   380  	m.history.Lock()
   381  	defer m.history.Unlock()
   382  
   383  	for e := m.history.page.Front(); e != nil; e = e.Next() {
   384  		event := e.Value.(types.BaseEvent)
   385  		if c.eventMatches(ctx, event) {
   386  			c.page.PushBack(event)
   387  		}
   388  	}
   389  }
   390  
   391  func (c *EventHistoryCollector) ReadNextEvents(ctx *Context, req *types.ReadNextEvents) soap.HasFault {
   392  	body := &methods.ReadNextEventsBody{}
   393  	if req.MaxCount <= 0 {
   394  		body.Fault_ = Fault("", errInvalidArgMaxCount)
   395  		return body
   396  	}
   397  	body.Res = new(types.ReadNextEventsResponse)
   398  
   399  	c.next(req.MaxCount, func(e *list.Element) {
   400  		body.Res.Returnval = append(body.Res.Returnval, e.Value.(types.BaseEvent))
   401  	})
   402  
   403  	return body
   404  }
   405  
   406  func (c *EventHistoryCollector) ReadPreviousEvents(ctx *Context, req *types.ReadPreviousEvents) soap.HasFault {
   407  	body := &methods.ReadPreviousEventsBody{}
   408  	if req.MaxCount <= 0 {
   409  		body.Fault_ = Fault("", errInvalidArgMaxCount)
   410  		return body
   411  	}
   412  	body.Res = new(types.ReadPreviousEventsResponse)
   413  
   414  	c.prev(req.MaxCount, func(e *list.Element) {
   415  		body.Res.Returnval = append(body.Res.Returnval, e.Value.(types.BaseEvent))
   416  	})
   417  
   418  	return body
   419  }
   420  
   421  func (c *EventHistoryCollector) GetLatestPage() []types.BaseEvent {
   422  	var latestPage []types.BaseEvent
   423  
   424  	e := c.page.Back()
   425  	for i := 0; i < c.size; i++ {
   426  		if e == nil {
   427  			break
   428  		}
   429  		latestPage = append(latestPage, e.Value.(types.BaseEvent))
   430  		e = e.Prev()
   431  	}
   432  
   433  	return latestPage
   434  }
   435  
   436  func (c *EventHistoryCollector) Get() mo.Reference {
   437  	clone := *c
   438  
   439  	clone.LatestPage = clone.GetLatestPage()
   440  
   441  	return &clone
   442  }