2017-01-11 06:57:07 -06:00
package notifiers
import (
2017-05-24 06:42:24 -05:00
"strconv"
"strings"
2017-01-11 06:57:07 -06:00
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/components/simplejson"
2019-05-13 01:45:54 -05:00
"github.com/grafana/grafana/pkg/infra/log"
2019-05-14 01:15:05 -05:00
"github.com/grafana/grafana/pkg/models"
2017-01-11 06:57:07 -06:00
"github.com/grafana/grafana/pkg/services/alerting"
)
func init ( ) {
alerting . RegisterNotifier ( & alerting . NotifierPlugin {
Type : "sensu" ,
Name : "Sensu" ,
Description : "Sends HTTP POST request to a Sensu API" ,
Factory : NewSensuNotifier ,
OptionsTemplate : `
< h3 class = "page-heading" > Sensu settings < / h3 >
< div class = "gf-form" >
< span class = "gf-form-label width-10" > Url < / span >
< input type = "text" required class = "gf-form-input max-width-26" ng - model = "ctrl.model.settings.url" placeholder = "http://sensu-api.local:4567/results" > < / input >
< / div >
2017-05-24 06:42:24 -05:00
< div class = "gf-form" >
< span class = "gf-form-label width-10" > Source < / span >
2017-06-05 07:20:34 -05:00
< input type = "text" class = "gf-form-input max-width-14" ng - model = "ctrl.model.settings.source" bs - tooltip = "'If empty rule id will be used'" data - placement = "right" > < / input >
2017-05-24 06:42:24 -05:00
< / div >
< div class = "gf-form" >
< span class = "gf-form-label width-10" > Handler < / span >
< input type = "text" class = "gf-form-input max-width-14" ng - model = "ctrl.model.settings.handler" placeholder = "default" > < / input >
< / div >
2017-01-11 06:57:07 -06:00
< div class = "gf-form" >
< span class = "gf-form-label width-10" > Username < / span >
< input type = "text" class = "gf-form-input max-width-14" ng - model = "ctrl.model.settings.username" > < / input >
< / div >
< div class = "gf-form" >
< span class = "gf-form-label width-10" > Password < / span >
< input type = "text" class = "gf-form-input max-width-14" ng - model = "ctrl.model.settings.password" > < / input >
< / div >
` ,
} )
}
2019-05-20 08:23:06 -05:00
// NewSensuNotifier is the constructor for the Sensu Notifier.
2019-05-14 01:15:05 -05:00
func NewSensuNotifier ( model * models . AlertNotification ) ( alerting . Notifier , error ) {
2017-01-11 06:57:07 -06:00
url := model . Settings . Get ( "url" ) . MustString ( )
if url == "" {
return nil , alerting . ValidationError { Reason : "Could not find url property in settings" }
}
return & SensuNotifier {
2018-06-05 03:27:29 -05:00
NotifierBase : NewNotifierBase ( model ) ,
2019-05-20 08:23:06 -05:00
URL : url ,
2017-01-11 06:57:07 -06:00
User : model . Settings . Get ( "username" ) . MustString ( ) ,
2017-05-24 06:42:24 -05:00
Source : model . Settings . Get ( "source" ) . MustString ( ) ,
2017-01-11 06:57:07 -06:00
Password : model . Settings . Get ( "password" ) . MustString ( ) ,
2017-05-24 06:42:24 -05:00
Handler : model . Settings . Get ( "handler" ) . MustString ( ) ,
2017-01-11 06:57:07 -06:00
log : log . New ( "alerting.notifier.sensu" ) ,
} , nil
}
2019-05-20 08:23:06 -05:00
// SensuNotifier is responsible for sending
// alert notifications to Sensu.
2017-01-11 06:57:07 -06:00
type SensuNotifier struct {
NotifierBase
2019-05-20 08:23:06 -05:00
URL string
2017-05-24 06:42:24 -05:00
Source string
2017-01-11 06:57:07 -06:00
User string
Password string
2017-05-24 06:42:24 -05:00
Handler string
2017-01-11 06:57:07 -06:00
log log . Logger
}
2019-05-20 08:23:06 -05:00
// Notify send alert notification to Sensu
func ( sn * SensuNotifier ) Notify ( evalContext * alerting . EvalContext ) error {
sn . log . Info ( "Sending sensu result" )
2017-01-11 06:57:07 -06:00
bodyJSON := simplejson . New ( )
2019-06-03 03:25:58 -05:00
bodyJSON . Set ( "ruleId" , evalContext . Rule . ID )
2017-01-11 06:57:07 -06:00
// Sensu alerts cannot have spaces in them
bodyJSON . Set ( "name" , strings . Replace ( evalContext . Rule . Name , " " , "_" , - 1 ) )
2017-05-24 06:42:24 -05:00
// Sensu alerts require a source. We set it to the user-specified value (optional),
// else we fallback and use the grafana ruleID.
2019-05-20 08:23:06 -05:00
if sn . Source != "" {
bodyJSON . Set ( "source" , sn . Source )
2017-05-24 06:42:24 -05:00
} else {
2019-06-03 03:25:58 -05:00
bodyJSON . Set ( "source" , "grafana_rule_" + strconv . FormatInt ( evalContext . Rule . ID , 10 ) )
2017-05-24 06:42:24 -05:00
}
2017-01-11 06:57:07 -06:00
// Finally, sensu expects an output
// We set it to a default output
bodyJSON . Set ( "output" , "Grafana Metric Condition Met" )
bodyJSON . Set ( "evalMatches" , evalContext . EvalMatches )
if evalContext . Rule . State == "alerting" {
bodyJSON . Set ( "status" , 2 )
} else if evalContext . Rule . State == "no_data" {
bodyJSON . Set ( "status" , 1 )
} else {
bodyJSON . Set ( "status" , 0 )
}
2019-05-20 08:23:06 -05:00
if sn . Handler != "" {
bodyJSON . Set ( "handler" , sn . Handler )
2017-05-24 06:42:24 -05:00
}
2019-06-03 03:25:58 -05:00
ruleURL , err := evalContext . GetRuleURL ( )
2017-01-11 06:57:07 -06:00
if err == nil {
2019-05-20 08:23:06 -05:00
bodyJSON . Set ( "ruleUrl" , ruleURL )
2017-01-11 06:57:07 -06:00
}
2020-03-30 17:46:01 -05:00
if sn . NeedsImage ( ) && evalContext . ImagePublicURL != "" {
2019-06-03 03:25:58 -05:00
bodyJSON . Set ( "imageUrl" , evalContext . ImagePublicURL )
2017-01-11 06:57:07 -06:00
}
if evalContext . Rule . Message != "" {
2017-10-17 16:22:56 -05:00
bodyJSON . Set ( "output" , evalContext . Rule . Message )
2017-01-11 06:57:07 -06:00
}
body , _ := bodyJSON . MarshalJSON ( )
2019-05-14 01:15:05 -05:00
cmd := & models . SendWebhookSync {
2019-05-20 08:23:06 -05:00
Url : sn . URL ,
User : sn . User ,
Password : sn . Password ,
2017-01-11 06:57:07 -06:00
Body : string ( body ) ,
HttpMethod : "POST" ,
}
if err := bus . DispatchCtx ( evalContext . Ctx , cmd ) ; err != nil {
2019-05-20 08:23:06 -05:00
sn . log . Error ( "Failed to send sensu event" , "error" , err , "sensu" , sn . Name )
2017-01-11 06:57:07 -06:00
return err
}
return nil
}