1/* 2Copyright (c) 2015 VMware, Inc. All Rights Reserved. 3 4Licensed under the Apache License, Version 2.0 (the "License"); 5you may not use this file except in compliance with the License. 6You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10Unless required by applicable law or agreed to in writing, software 11distributed under the License is distributed on an "AS IS" BASIS, 12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13See the License for the specific language governing permissions and 14limitations under the License. 15*/ 16 17package event 18 19import ( 20 "context" 21 "fmt" 22 "reflect" 23 "sync" 24 25 "github.com/vmware/govmomi/object" 26 "github.com/vmware/govmomi/property" 27 "github.com/vmware/govmomi/vim25" 28 "github.com/vmware/govmomi/vim25/methods" 29 "github.com/vmware/govmomi/vim25/mo" 30 "github.com/vmware/govmomi/vim25/types" 31) 32 33type Manager struct { 34 object.Common 35 36 eventCategory map[string]string 37 eventCategoryMu *sync.Mutex 38 maxObjects int 39} 40 41func NewManager(c *vim25.Client) *Manager { 42 m := Manager{ 43 Common: object.NewCommon(c, *c.ServiceContent.EventManager), 44 45 eventCategory: make(map[string]string), 46 eventCategoryMu: new(sync.Mutex), 47 maxObjects: 10, 48 } 49 50 return &m 51} 52 53func (m Manager) CreateCollectorForEvents(ctx context.Context, filter types.EventFilterSpec) (*HistoryCollector, error) { 54 req := types.CreateCollectorForEvents{ 55 This: m.Common.Reference(), 56 Filter: filter, 57 } 58 59 res, err := methods.CreateCollectorForEvents(ctx, m.Client(), &req) 60 if err != nil { 61 return nil, err 62 } 63 64 return NewHistoryCollector(m.Client(), res.Returnval), nil 65} 66 67func (m Manager) LogUserEvent(ctx context.Context, entity types.ManagedObjectReference, msg string) error { 68 req := types.LogUserEvent{ 69 This: m.Common.Reference(), 70 Entity: entity, 71 Msg: msg, 72 } 73 74 _, err := methods.LogUserEvent(ctx, m.Client(), &req) 75 if err != nil { 76 return err 77 } 78 79 return nil 80} 81 82func (m Manager) PostEvent(ctx context.Context, eventToPost types.BaseEvent, taskInfo types.TaskInfo) error { 83 req := types.PostEvent{ 84 This: m.Common.Reference(), 85 EventToPost: eventToPost, 86 TaskInfo: &taskInfo, 87 } 88 89 _, err := methods.PostEvent(ctx, m.Client(), &req) 90 if err != nil { 91 return err 92 } 93 94 return nil 95} 96 97func (m Manager) QueryEvents(ctx context.Context, filter types.EventFilterSpec) ([]types.BaseEvent, error) { 98 req := types.QueryEvents{ 99 This: m.Common.Reference(), 100 Filter: filter, 101 } 102 103 res, err := methods.QueryEvents(ctx, m.Client(), &req) 104 if err != nil { 105 return nil, err 106 } 107 108 return res.Returnval, nil 109} 110 111func (m Manager) RetrieveArgumentDescription(ctx context.Context, eventTypeID string) ([]types.EventArgDesc, error) { 112 req := types.RetrieveArgumentDescription{ 113 This: m.Common.Reference(), 114 EventTypeId: eventTypeID, 115 } 116 117 res, err := methods.RetrieveArgumentDescription(ctx, m.Client(), &req) 118 if err != nil { 119 return nil, err 120 } 121 122 return res.Returnval, nil 123} 124 125func (m Manager) eventCategoryMap(ctx context.Context) (map[string]string, error) { 126 m.eventCategoryMu.Lock() 127 defer m.eventCategoryMu.Unlock() 128 129 if len(m.eventCategory) != 0 { 130 return m.eventCategory, nil 131 } 132 133 var o mo.EventManager 134 135 ps := []string{"description.eventInfo"} 136 err := property.DefaultCollector(m.Client()).RetrieveOne(ctx, m.Common.Reference(), ps, &o) 137 if err != nil { 138 return nil, err 139 } 140 141 for _, info := range o.Description.EventInfo { 142 m.eventCategory[info.Key] = info.Category 143 } 144 145 return m.eventCategory, nil 146} 147 148// EventCategory returns the category for an event, such as "info" or "error" for example. 149func (m Manager) EventCategory(ctx context.Context, event types.BaseEvent) (string, error) { 150 // Most of the event details are included in the Event.FullFormattedMessage, but the category 151 // is only available via the EventManager description.eventInfo property. The value of this 152 // property is static, so we fetch and once and cache. 153 eventCategory, err := m.eventCategoryMap(ctx) 154 if err != nil { 155 return "", err 156 } 157 158 switch e := event.(type) { 159 case *types.EventEx: 160 if e.Severity == "" { 161 return "info", nil 162 } 163 return e.Severity, nil 164 } 165 166 class := reflect.TypeOf(event).Elem().Name() 167 168 return eventCategory[class], nil 169} 170 171// Get the events from the specified object(s) and optionanlly tail the event stream 172func (m Manager) Events(ctx context.Context, objects []types.ManagedObjectReference, pageSize int32, tail bool, force bool, f func(types.ManagedObjectReference, []types.BaseEvent) error, kind ...string) error { 173 // TODO: deprecated this method and add one that uses a single config struct, so we can extend further without breaking the method signature. 174 if len(objects) >= m.maxObjects && !force { 175 return fmt.Errorf("Maximum number of objects to monitor (%d) exceeded, refine search", m.maxObjects) 176 } 177 178 proc := newEventProcessor(m, pageSize, f, kind) 179 for _, o := range objects { 180 proc.addObject(ctx, o) 181 } 182 183 defer proc.destroy() 184 185 return proc.run(ctx, tail) 186} 187