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