diff --git a/pkg/api/alerting.go b/pkg/api/alerting.go index dcc9b40b07a..245931eb7af 100644 --- a/pkg/api/alerting.go +++ b/pkg/api/alerting.go @@ -259,10 +259,11 @@ func NotificationTest(c *middleware.Context, dto dtos.NotificationTestCommand) R //POST /api/alerts/:alertId/pause func PauseAlert(c *middleware.Context, dto dtos.PauseAlertCommand) Response { + alertId := c.ParamsInt64("alertId") cmd := models.PauseAlertCommand{ - OrgId: c.OrgId, - AlertId: c.ParamsInt64("alertId"), - Paused: dto.Paused, + OrgId: c.OrgId, + AlertIds: []int64{alertId}, + Paused: dto.Paused, } if err := bus.Dispatch(&cmd); err != nil { @@ -277,10 +278,36 @@ func PauseAlert(c *middleware.Context, dto dtos.PauseAlertCommand) Response { } result := map[string]interface{}{ - "alertId": cmd.AlertId, + "alertId": alertId, "state": response, "message": "alert " + pausedState, } return Json(200, result) } + +//POST /api/admin/pause-all-alerts +func PauseAllAlerts(c *middleware.Context, dto dtos.PauseAllAlertsCommand) Response { + updateCmd := models.PauseAllAlertCommand{ + Paused: dto.Paused, + } + + if err := bus.Dispatch(&updateCmd); err != nil { + return ApiError(500, "Failed to pause alerts", err) + } + + var response models.AlertStateType = models.AlertStatePending + pausedState := "un paused" + if updateCmd.Paused { + response = models.AlertStatePaused + pausedState = "paused" + } + + result := map[string]interface{}{ + "state": response, + "message": "alert " + pausedState, + "alertsAffected": updateCmd.ResultCount, + } + + return Json(200, result) +} diff --git a/pkg/api/api.go b/pkg/api/api.go index 90e0e1bdfa8..e931c5de601 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -289,6 +289,7 @@ func Register(r *macaron.Macaron) { r.Get("/users/:id/quotas", wrap(GetUserQuotas)) r.Put("/users/:id/quotas/:target", bind(m.UpdateUserQuotaCmd{}), wrap(UpdateUserQuota)) r.Get("/stats", AdminGetStats) + r.Post("/pause-all-alerts", bind(dtos.PauseAllAlertsCommand{}), wrap(PauseAllAlerts)) }, reqGrafanaAdmin) // rendering diff --git a/pkg/api/dtos/alerting.go b/pkg/api/dtos/alerting.go index c9ba30e2407..f3d64f89524 100644 --- a/pkg/api/dtos/alerting.go +++ b/pkg/api/dtos/alerting.go @@ -64,3 +64,7 @@ type PauseAlertCommand struct { AlertId int64 `json:"alertId"` Paused bool `json:"paused"` } + +type PauseAllAlertsCommand struct { + Paused bool `json:"paused"` +} diff --git a/pkg/models/alert.go b/pkg/models/alert.go index 0d2289c0678..13cae0715f7 100644 --- a/pkg/models/alert.go +++ b/pkg/models/alert.go @@ -3,6 +3,8 @@ package models import ( "time" + "fmt" + "github.com/grafana/grafana/pkg/components/simplejson" ) @@ -31,6 +33,10 @@ const ( ExecutionErrorKeepState ExecutionErrorOption = "keep_state" ) +var ( + ErrCannotChangeStateOnPausedAlert error = fmt.Errorf("Cannot change state on pause alert") +) + func (s AlertStateType) IsValid() bool { return s == AlertStateOK || s == AlertStateNoData || s == AlertStatePaused || s == AlertStatePending } @@ -132,9 +138,15 @@ type SaveAlertsCommand struct { } type PauseAlertCommand struct { - OrgId int64 - AlertId int64 - Paused bool + OrgId int64 + AlertIds []int64 + ResultCount int64 + Paused bool +} + +type PauseAllAlertCommand struct { + ResultCount int64 + Paused bool } type SetAlertStateCommand struct { diff --git a/pkg/services/alerting/result_handler.go b/pkg/services/alerting/result_handler.go index 2a7c6528b97..aab109088cb 100644 --- a/pkg/services/alerting/result_handler.go +++ b/pkg/services/alerting/result_handler.go @@ -86,6 +86,10 @@ func (handler *DefaultResultHandler) Handle(evalContext *EvalContext) error { } if err := bus.Dispatch(cmd); err != nil { + if err == m.ErrCannotChangeStateOnPausedAlert { + handler.log.Error("Cannot change state on alert thats pause", "error", err) + return err + } handler.log.Error("Failed to save state", "error", err) } diff --git a/pkg/services/sqlstore/alert.go b/pkg/services/sqlstore/alert.go index 44693f8d474..8718b03e786 100644 --- a/pkg/services/sqlstore/alert.go +++ b/pkg/services/sqlstore/alert.go @@ -3,6 +3,7 @@ package sqlstore import ( "bytes" "fmt" + "strings" "time" "github.com/go-xorm/xorm" @@ -18,7 +19,8 @@ func init() { bus.AddHandler("sql", GetAllAlertQueryHandler) bus.AddHandler("sql", SetAlertState) bus.AddHandler("sql", GetAlertStatesForDashboard) - bus.AddHandler("sql", PauseAlertRule) + bus.AddHandler("sql", PauseAlert) + bus.AddHandler("sql", PauseAllAlerts) } func GetAlertById(query *m.GetAlertByIdQuery) error { @@ -228,6 +230,10 @@ func SetAlertState(cmd *m.SetAlertStateCommand) error { return fmt.Errorf("Could not find alert") } + if alert.State == m.AlertStatePaused { + return m.ErrCannotChangeStateOnPausedAlert + } + alert.State = cmd.State alert.StateChanges += 1 alert.NewStateDate = time.Now() @@ -244,27 +250,50 @@ func SetAlertState(cmd *m.SetAlertStateCommand) error { }) } -func PauseAlertRule(cmd *m.PauseAlertCommand) error { +func PauseAlert(cmd *m.PauseAlertCommand) error { return inTransaction(func(sess *xorm.Session) error { - alert := m.Alert{} + if len(cmd.AlertIds) == 0 { + return fmt.Errorf("command contains no alertids") + } - has, err := x.Where("id = ? AND org_id=?", cmd.AlertId, cmd.OrgId).Get(&alert) + var buffer bytes.Buffer + params := make([]interface{}, 0) + buffer.WriteString(`UPDATE alert SET state = ?`) + if cmd.Paused { + params = append(params, string(m.AlertStatePaused)) + } else { + params = append(params, string(m.AlertStatePending)) + } + + buffer.WriteString(` WHERE id IN (?` + strings.Repeat(",?", len(cmd.AlertIds)-1) + `)`) + for _, v := range cmd.AlertIds { + params = append(params, v) + } + + res, err := sess.Exec(buffer.String(), params...) if err != nil { return err - } else if !has { - return fmt.Errorf("Could not find alert") } + cmd.ResultCount, _ = res.RowsAffected() + return nil + }) +} - var newState m.AlertStateType +func PauseAllAlerts(cmd *m.PauseAllAlertCommand) error { + return inTransaction(func(sess *xorm.Session) error { + var newState string if cmd.Paused { - newState = m.AlertStatePaused + newState = string(m.AlertStatePaused) } else { - newState = m.AlertStatePending + newState = string(m.AlertStatePending) } - alert.State = newState - sess.Id(alert.Id).Update(&alert) + res, err := sess.Exec(`UPDATE alert SET state = ?`, newState) + if err != nil { + return err + } + cmd.ResultCount, _ = res.RowsAffected() return nil }) } diff --git a/pkg/services/sqlstore/alert_heartbeat_test.go b/pkg/services/sqlstore/alert_heartbeat_test.go deleted file mode 100644 index 196d3fd7dde..00000000000 --- a/pkg/services/sqlstore/alert_heartbeat_test.go +++ /dev/null @@ -1,18 +0,0 @@ -package sqlstore - -import ( - "testing" - - // m "github.com/grafana/grafana/pkg/models" - . "github.com/smartystreets/goconvey/convey" -) - -func TestAlertingHeartbeatDataAccess(t *testing.T) { - - Convey("Testing Alerting data access", t, func() { - InitTestDB(t) - //send heartbeat from server 1 - //send heartbeat from server 2 - - }) -} diff --git a/pkg/services/sqlstore/alert_test.go b/pkg/services/sqlstore/alert_test.go index e22b1c48c47..daf4f774717 100644 --- a/pkg/services/sqlstore/alert_test.go +++ b/pkg/services/sqlstore/alert_test.go @@ -39,6 +39,37 @@ func TestAlertingDataAccess(t *testing.T) { So(err, ShouldBeNil) }) + Convey("Can set new states", func() { + Convey("new state ok", func() { + cmd := &m.SetAlertStateCommand{ + AlertId: 1, + State: m.AlertStateOK, + } + + err = SetAlertState(cmd) + So(err, ShouldBeNil) + }) + + Convey("can pause alert", func() { + cmd := &m.PauseAllAlertCommand{ + Paused: true, + } + + err = PauseAllAlerts(cmd) + So(err, ShouldBeNil) + + Convey("cannot updated paused alert", func() { + cmd := &m.SetAlertStateCommand{ + AlertId: 1, + State: m.AlertStateOK, + } + + err = SetAlertState(cmd) + So(err, ShouldNotBeNil) + }) + }) + }) + Convey("Can read properties", func() { alertQuery := m.GetAlertsQuery{DashboardId: testDash.Id, PanelId: 1, OrgId: 1} err2 := HandleAlertsQuery(&alertQuery)