bosun.org@v0.0.0-20210513094433-e25bc3e69a1f/models/incidents.go (about)

     1  package models
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"math"
     7  	"strconv"
     8  	"time"
     9  
    10  	"bosun.org/opentsdb"
    11  )
    12  
    13  type 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)
    55  func (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  
    65  type Epoch struct {
    66  	time.Time
    67  }
    68  
    69  func (t Epoch) MarshalJSON() ([]byte, error) {
    70  	return []byte(fmt.Sprintf("%v", t.UTC().Unix())), nil
    71  }
    72  
    73  func (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  
    86  type RenderedTemplates struct {
    87  	Subject      string
    88  	Body         string
    89  	EmailBody    []byte
    90  	EmailSubject []byte
    91  	Custom       map[string]string
    92  	Attachments  []*Attachment
    93  }
    94  
    95  func (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  
   120  func (r *RenderedTemplates) GetDefault(name string, defaultName string) string {
   121  	if name == "" {
   122  		name = defaultName
   123  	}
   124  	return r.Get(name)
   125  }
   126  
   127  func (s *IncidentState) Group() opentsdb.TagSet {
   128  	return s.AlertKey.Group()
   129  }
   130  
   131  func (s *IncidentState) Last() Event {
   132  	if len(s.Events) == 0 {
   133  		return Event{}
   134  	}
   135  	return s.Events[len(s.Events)-1]
   136  }
   137  
   138  func (s *IncidentState) IsActive() bool {
   139  	return s.CurrentStatus > StNormal
   140  }
   141  
   142  type Event struct {
   143  	Warn, Crit  *Result `json:",omitempty"`
   144  	Status      Status
   145  	Time        time.Time
   146  	Unevaluated bool
   147  }
   148  
   149  type EventsByTime []Event
   150  
   151  func (a EventsByTime) Len() int           { return len(a) }
   152  func (a EventsByTime) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
   153  func (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
   156  type Float float64
   157  
   158  func (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  
   165  func (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  
   176  type Result struct {
   177  	Computations `json:",omitempty"`
   178  	Value        Float
   179  	Expr         string
   180  }
   181  
   182  type Computations []Computation
   183  
   184  type Computation struct {
   185  	Text  string
   186  	Value interface{}
   187  }
   188  
   189  type FuncType int
   190  
   191  func (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  
   226  const (
   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  
   244  type Status int
   245  
   246  const (
   247  	StNone Status = iota
   248  	StNormal
   249  	StWarning
   250  	StCritical
   251  	StUnknown
   252  )
   253  
   254  func (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  
   269  func (s Status) MarshalJSON() ([]byte, error) {
   270  	return json.Marshal(s.String())
   271  }
   272  
   273  func (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  
   289  func (s Status) IsNormal() bool   { return s == StNormal }
   290  func (s Status) IsWarning() bool  { return s == StWarning }
   291  func (s Status) IsCritical() bool { return s == StCritical }
   292  func (s Status) IsUnknown() bool  { return s == StUnknown }
   293  
   294  type 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  
   306  type ActionType int // Available to users in templates, document changes in Bosun docs
   307  
   308  const (
   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
   321  var 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
   333  func (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  
   356  func (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  
   379  func (a ActionType) MarshalJSON() ([]byte, error) {
   380  	return json.Marshal(a.String())
   381  }
   382  
   383  func (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  
   407  type Attachment struct {
   408  	Data        []byte
   409  	Filename    string
   410  	ContentType string
   411  }