1package models
2
3import (
4	"encoding/json"
5	"fmt"
6	"math"
7	"strconv"
8	"time"
9
10	"bosun.org/opentsdb"
11)
12
13type IncidentState struct {
14	// Since IncidentState is embedded into a template's Context these fields
15	// are available to users. Changes to this object should be reflected
16	// in Bosun's documentation and changes that might break user's teamplates.
17	// need to be considered.
18	Id       int64
19	Start    time.Time
20	End      *time.Time
21	AlertKey AlertKey
22	Alert    string // helper data since AlertKeys don't serialize to JSON well
23	Tags     string // string representation of Group
24
25	*Result
26
27	// Most recent last.
28	Events  []Event  `json:",omitempty"`
29	Actions []Action `json:",omitempty"`
30
31	Subject string
32
33	NeedAck bool
34	Open    bool
35
36	Unevaluated bool
37
38	CurrentStatus Status
39	WorstStatus   Status
40
41	LastAbnormalStatus Status
42
43	LastAbnormalTime Epoch
44
45	PreviousIds []int64 // A list to the previous IncidentIds for the same alert key (alertname+tagset)
46	NextId      int64   // The id of the next Incident Id for the same alert key, only added once a future incident has been created
47
48	// set of notifications we have already sent alerts to during the lifetime of the incident
49	Notifications []string
50}
51
52// SetNotified marks the notification name as "active" for this incident.
53// All future actions and unknown notifications will go to all "active" notifications
54// it returns true if the set was changed (and needs resaving)
55func (i *IncidentState) SetNotified(not string) bool {
56	for _, n := range i.Notifications {
57		if n == not {
58			return false
59		}
60	}
61	i.Notifications = append(i.Notifications, not)
62	return true
63}
64
65type Epoch struct {
66	time.Time
67}
68
69func (t Epoch) MarshalJSON() ([]byte, error) {
70	return []byte(fmt.Sprintf("%v", t.UTC().Unix())), nil
71}
72
73func (t *Epoch) UnmarshalJSON(b []byte) (err error) {
74	if len(b) == 0 {
75		t.Time = time.Time{}
76		return
77	}
78	epoch, err := strconv.ParseInt(string(b), 10, 64)
79	if err != nil {
80		return err
81	}
82	t.Time = time.Unix(epoch, 0)
83	return
84}
85
86type RenderedTemplates struct {
87	Subject      string
88	Body         string
89	EmailBody    []byte
90	EmailSubject []byte
91	Custom       map[string]string
92	Attachments  []*Attachment
93}
94
95func (r *RenderedTemplates) Get(name string) string {
96	if name == "subject" {
97		return r.Subject
98	}
99	if name == "body" {
100		return r.Body
101	}
102	if name == "emailBody" {
103		if r.EmailBody != nil {
104			return string(r.EmailBody)
105		}
106		return r.Body
107	}
108	if name == "emailSubject" {
109		if r.EmailSubject != nil {
110			return string(r.EmailSubject)
111		}
112		return r.Subject
113	}
114	if t, ok := r.Custom[name]; ok {
115		return t
116	}
117	return ""
118}
119
120func (r *RenderedTemplates) GetDefault(name string, defaultName string) string {
121	if name == "" {
122		name = defaultName
123	}
124	return r.Get(name)
125}
126
127func (s *IncidentState) Group() opentsdb.TagSet {
128	return s.AlertKey.Group()
129}
130
131func (s *IncidentState) Last() Event {
132	if len(s.Events) == 0 {
133		return Event{}
134	}
135	return s.Events[len(s.Events)-1]
136}
137
138func (s *IncidentState) IsActive() bool {
139	return s.CurrentStatus > StNormal
140}
141
142type Event struct {
143	Warn, Crit  *Result `json:",omitempty"`
144	Status      Status
145	Time        time.Time
146	Unevaluated bool
147}
148
149type EventsByTime []Event
150
151func (a EventsByTime) Len() int           { return len(a) }
152func (a EventsByTime) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
153func (a EventsByTime) Less(i, j int) bool { return a[i].Time.Before(a[j].Time) }
154
155// custom float type to support json marshalling of NaN
156type Float float64
157
158func (m Float) MarshalJSON() ([]byte, error) {
159	if math.IsNaN(float64(m)) {
160		return []byte("null"), nil
161	}
162	return json.Marshal(float64(m))
163}
164
165func (m *Float) UnmarshalJSON(b []byte) error {
166	if string(b) == "null" {
167		*m = Float(math.NaN())
168		return nil
169	}
170	var f float64
171	err := json.Unmarshal(b, &f)
172	*m = Float(f)
173	return err
174}
175
176type Result struct {
177	Computations `json:",omitempty"`
178	Value        Float
179	Expr         string
180}
181
182type Computations []Computation
183
184type Computation struct {
185	Text  string
186	Value interface{}
187}
188
189type FuncType int
190
191func (f FuncType) String() string {
192	switch f {
193	case TypeNumberSet:
194		return "number"
195	case TypeString:
196		return "string"
197	case TypeSeriesSet:
198		return "series"
199	case TypeScalar:
200		return "scalar"
201	case TypeESQuery:
202		return "esquery"
203	case TypeESIndexer:
204		return "esindexer"
205	case TypeNumberExpr:
206		return "numberexpr"
207	case TypeSeriesExpr:
208		return "seriesexpr"
209	case TypePrefix:
210		return "prefix"
211	case TypeTable:
212		return "table"
213	case TypeVariantSet:
214		return "variantSet"
215	case TypeAzureResourceList:
216		return "azureResources"
217	case TypeAzureAIApps:
218		return "azureAIApps"
219	case TypeInfo:
220		return "info"
221	default:
222		return "unknown"
223	}
224}
225
226const (
227	TypeString FuncType = iota
228	TypePrefix
229	TypeScalar
230	TypeNumberSet
231	TypeSeriesSet
232	TypeESQuery
233	TypeESIndexer
234	TypeNumberExpr
235	TypeSeriesExpr // No implementation yet
236	TypeTable
237	TypeVariantSet
238	TypeAzureResourceList
239	TypeAzureAIApps
240	TypeInfo
241	TypeUnexpected
242)
243
244type Status int
245
246const (
247	StNone Status = iota
248	StNormal
249	StWarning
250	StCritical
251	StUnknown
252)
253
254func (s Status) String() string {
255	switch s {
256	case StNormal:
257		return "normal"
258	case StWarning:
259		return "warning"
260	case StCritical:
261		return "critical"
262	case StUnknown:
263		return "unknown"
264	default:
265		return "none"
266	}
267}
268
269func (s Status) MarshalJSON() ([]byte, error) {
270	return json.Marshal(s.String())
271}
272
273func (s *Status) UnmarshalJSON(b []byte) error {
274	switch string(b) {
275	case `"normal"`:
276		*s = StNormal
277	case `"warning"`:
278		*s = StWarning
279	case `"critical"`:
280		*s = StCritical
281	case `"unknown"`:
282		*s = StUnknown
283	default:
284		*s = StNone
285	}
286	return nil
287}
288
289func (s Status) IsNormal() bool   { return s == StNormal }
290func (s Status) IsWarning() bool  { return s == StWarning }
291func (s Status) IsCritical() bool { return s == StCritical }
292func (s Status) IsUnknown() bool  { return s == StUnknown }
293
294type Action struct {
295	// These are available to users via the template language. Changes here
296	// should be reflected in the documentation
297	User       string
298	Message    string
299	Time       time.Time
300	Type       ActionType
301	Deadline   *time.Time `json:",omitempty"`
302	Fullfilled bool
303	Cancelled  bool
304}
305
306type ActionType int // Available to users in templates, document changes in Bosun docs
307
308const (
309	ActionNone ActionType = iota
310	ActionAcknowledge
311	ActionClose
312	ActionForget
313	ActionForceClose
314	ActionPurge
315	ActionNote
316	ActionDelayedClose
317	ActionCancelClose
318)
319
320//ActionShortNames is a map of keys we use in config file (notifications mostly) to reference action types
321var ActionShortNames = map[string]ActionType{
322	"Ack":          ActionAcknowledge,
323	"Close":        ActionClose,
324	"Forget":       ActionForget,
325	"ForceClose":   ActionForceClose,
326	"Purge":        ActionPurge,
327	"Note":         ActionNote,
328	"DelayedClose": ActionDelayedClose,
329	"CancelClose":  ActionCancelClose,
330}
331
332// HumanString gives a better human readable form than the default stringer, which we can't change due to marshalling compatibility now
333func (a ActionType) HumanString() string {
334	switch a {
335	case ActionAcknowledge:
336		return "Acknowledged"
337	case ActionClose:
338		return "Closed"
339	case ActionForget:
340		return "Forgot"
341	case ActionForceClose:
342		return "Force Closed"
343	case ActionPurge:
344		return "Purged"
345	case ActionNote:
346		return "Commented On"
347	case ActionDelayedClose:
348		return "Delayed Closed"
349	case ActionCancelClose:
350		return "Canceled Close"
351	default:
352		return "none"
353	}
354}
355
356func (a ActionType) String() string {
357	switch a {
358	case ActionAcknowledge:
359		return "Acknowledged"
360	case ActionClose:
361		return "Closed"
362	case ActionForget:
363		return "Forgotten"
364	case ActionForceClose:
365		return "ForceClosed"
366	case ActionPurge:
367		return "Purged"
368	case ActionNote:
369		return "Note"
370	case ActionDelayedClose:
371		return "DelayedClose"
372	case ActionCancelClose:
373		return "CancelClose"
374	default:
375		return "none"
376	}
377}
378
379func (a ActionType) MarshalJSON() ([]byte, error) {
380	return json.Marshal(a.String())
381}
382
383func (a *ActionType) UnmarshalJSON(b []byte) error {
384	switch string(b) {
385	case `"Acknowledged"`:
386		*a = ActionAcknowledge
387	case `"Closed"`:
388		*a = ActionClose
389	case `"Forgotten"`:
390		*a = ActionForget
391	case `"Purged"`:
392		*a = ActionPurge
393	case `"ForceClosed"`:
394		*a = ActionForceClose
395	case `"Note"`:
396		*a = ActionNote
397	case `"DelayedClose"`:
398		*a = ActionDelayedClose
399	case `"CancelClose"`:
400		*a = ActionCancelClose
401	default:
402		*a = ActionNone
403	}
404	return nil
405}
406
407type Attachment struct {
408	Data        []byte
409	Filename    string
410	ContentType string
411}
412