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