mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
feat(alerting): more model changes
This commit is contained in:
parent
a362984c57
commit
382f396247
@ -18,20 +18,20 @@ func init() {
|
||||
}
|
||||
|
||||
func updateDashboardAlerts(cmd *UpdateDashboardAlertsCommand) error {
|
||||
saveRulesCmd := m.SaveAlertsCommand{
|
||||
saveAlerts := m.SaveAlertsCommand{
|
||||
OrgId: cmd.OrgId,
|
||||
UserId: cmd.UserId,
|
||||
}
|
||||
|
||||
extractor := NewAlertRuleExtractor(cmd.Dashboard, cmd.OrgId)
|
||||
extractor := NewDashAlertExtractor(cmd.Dashboard, cmd.OrgId)
|
||||
|
||||
rules, err := extractor.GetRuleModels()
|
||||
alerts, err := extractor.GetRuleModels()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
saveRulesCmd.Alerts = rules
|
||||
if bus.Dispatch(&saveRulesCmd); err != nil {
|
||||
saveAlerts.Alerts = alerts
|
||||
if bus.Dispatch(&saveAlerts); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -9,21 +9,21 @@ import (
|
||||
m "github.com/grafana/grafana/pkg/models"
|
||||
)
|
||||
|
||||
type AlertRuleExtractor struct {
|
||||
type DashAlertExtractor struct {
|
||||
Dash *m.Dashboard
|
||||
OrgId int64
|
||||
log log.Logger
|
||||
}
|
||||
|
||||
func NewAlertRuleExtractor(dash *m.Dashboard, orgId int64) *AlertRuleExtractor {
|
||||
return &AlertRuleExtractor{
|
||||
func NewDashAlertExtractor(dash *m.Dashboard, orgId int64) *DashAlertExtractor {
|
||||
return &DashAlertExtractor{
|
||||
Dash: dash,
|
||||
OrgId: orgId,
|
||||
log: log.New("alerting.extractor"),
|
||||
}
|
||||
}
|
||||
|
||||
func (e *AlertRuleExtractor) lookupDatasourceId(dsName string) (int64, error) {
|
||||
func (e *DashAlertExtractor) lookupDatasourceId(dsName string) (int64, error) {
|
||||
if dsName == "" {
|
||||
query := &m.GetDataSourcesQuery{OrgId: e.OrgId}
|
||||
if err := bus.Dispatch(query); err != nil {
|
||||
@ -47,36 +47,36 @@ func (e *AlertRuleExtractor) lookupDatasourceId(dsName string) (int64, error) {
|
||||
return 0, errors.New("Could not find datasource id for " + dsName)
|
||||
}
|
||||
|
||||
func (e *AlertRuleExtractor) GetRuleModels() (m.AlertRules, error) {
|
||||
func (e *DashAlertExtractor) GetRuleModels() ([]*m.Alert, error) {
|
||||
|
||||
rules := make(m.AlertRules, 0)
|
||||
alerts := make([]*m.Alert, 0)
|
||||
|
||||
for _, rowObj := range e.Dash.Data.Get("rows").MustArray() {
|
||||
row := simplejson.NewFromAny(rowObj)
|
||||
|
||||
for _, panelObj := range row.Get("panels").MustArray() {
|
||||
panel := simplejson.NewFromAny(panelObj)
|
||||
jsonRule := panel.Get("alerting")
|
||||
jsonAlert := panel.Get("alert")
|
||||
|
||||
// check if marked for deletion
|
||||
deleted := jsonRule.Get("deleted").MustBool()
|
||||
deleted := jsonAlert.Get("deleted").MustBool()
|
||||
if deleted {
|
||||
e.log.Info("Deleted alert rule found")
|
||||
continue
|
||||
}
|
||||
|
||||
ruleModel := &m.Alert{
|
||||
alert := &m.Alert{
|
||||
DashboardId: e.Dash.Id,
|
||||
OrgId: e.OrgId,
|
||||
PanelId: panel.Get("id").MustInt64(),
|
||||
Id: jsonRule.Get("id").MustInt64(),
|
||||
Name: jsonRule.Get("name").MustString(),
|
||||
Scheduler: jsonRule.Get("scheduler").MustInt64(),
|
||||
Enabled: jsonRule.Get("enabled").MustBool(),
|
||||
Description: jsonRule.Get("description").MustString(),
|
||||
Id: jsonAlert.Get("id").MustInt64(),
|
||||
Name: jsonAlert.Get("name").MustString(),
|
||||
Scheduler: jsonAlert.Get("scheduler").MustInt64(),
|
||||
Enabled: jsonAlert.Get("enabled").MustBool(),
|
||||
Description: jsonAlert.Get("description").MustString(),
|
||||
}
|
||||
|
||||
valueQuery := jsonRule.Get("query")
|
||||
valueQuery := jsonAlert.Get("query")
|
||||
valueQueryRef := valueQuery.Get("refId").MustString()
|
||||
for _, targetsObj := range panel.Get("targets").MustArray() {
|
||||
target := simplejson.NewFromAny(targetsObj)
|
||||
@ -97,24 +97,24 @@ func (e *AlertRuleExtractor) GetRuleModels() (m.AlertRules, error) {
|
||||
|
||||
targetQuery := target.Get("target").MustString()
|
||||
if targetQuery != "" {
|
||||
jsonRule.SetPath([]string{"query", "query"}, targetQuery)
|
||||
jsonAlert.SetPath([]string{"query", "query"}, targetQuery)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ruleModel.Expression = jsonRule
|
||||
alert.Expression = jsonAlert
|
||||
|
||||
// validate
|
||||
_, err := NewAlertRuleFromDBModel(ruleModel)
|
||||
if err == nil && ruleModel.ValidToSave() {
|
||||
rules = append(rules, ruleModel)
|
||||
_, err := NewAlertRuleFromDBModel(alert)
|
||||
if err == nil && alert.ValidToSave() {
|
||||
alerts = append(alerts, alert)
|
||||
} else {
|
||||
e.log.Error("Failed to extract alert rules from dashboard", "error", err)
|
||||
return nil, errors.New("Failed to extract alert rules from dashboard")
|
||||
e.log.Error("Failed to extract alerts from dashboard", "error", err)
|
||||
return nil, errors.New("Failed to extract alerts from dashboard")
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return rules, nil
|
||||
return alerts, nil
|
||||
}
|
||||
|
@ -36,7 +36,7 @@ func TestAlertRuleExtraction(t *testing.T) {
|
||||
}
|
||||
],
|
||||
"datasource": null,
|
||||
"alerting": {
|
||||
"alert": {
|
||||
"name": "name1",
|
||||
"description": "desc1",
|
||||
"scheduler": 1,
|
||||
@ -71,7 +71,7 @@ func TestAlertRuleExtraction(t *testing.T) {
|
||||
}
|
||||
],
|
||||
"datasource": "graphite2",
|
||||
"alerting": {
|
||||
"alert": {
|
||||
"name": "name2",
|
||||
"description": "desc2",
|
||||
"scheduler": 0,
|
||||
@ -150,7 +150,7 @@ func TestAlertRuleExtraction(t *testing.T) {
|
||||
"title": "Broken influxdb panel",
|
||||
"transform": "table",
|
||||
"type": "table",
|
||||
"alerting": {
|
||||
"alert": {
|
||||
"deleted": true
|
||||
}
|
||||
}
|
||||
@ -164,7 +164,7 @@ func TestAlertRuleExtraction(t *testing.T) {
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
dash := m.NewDashboardFromJson(dashJson)
|
||||
extractor := NewAlertRuleExtractor(dash, 1)
|
||||
extractor := NewDashAlertExtractor(dash, 1)
|
||||
|
||||
// mock data
|
||||
defaultDs := &m.DataSource{Id: 12, OrgId: 2, Name: "I am default", IsDefault: true}
|
||||
|
@ -79,7 +79,7 @@ func GetAlertById(query *m.GetAlertByIdQuery) error {
|
||||
|
||||
func GetAllAlertQueryHandler(query *m.GetAllAlertsQuery) error {
|
||||
var alerts []*m.Alert
|
||||
err := x.Sql("select * from alert_rule").Find(&alerts)
|
||||
err := x.Sql("select * from alert").Find(&alerts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -90,7 +90,7 @@ func GetAllAlertQueryHandler(query *m.GetAllAlertsQuery) error {
|
||||
|
||||
func DeleteAlertById(cmd *m.DeleteAlertCommand) error {
|
||||
return inTransaction(func(sess *xorm.Session) error {
|
||||
if _, err := sess.Exec("DELETE FROM alert_rule WHERE id = ?", cmd.AlertId); err != nil {
|
||||
if _, err := sess.Exec("DELETE FROM alert WHERE id = ?", cmd.AlertId); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -103,7 +103,7 @@ func HandleAlertsQuery(query *m.GetAlertsQuery) error {
|
||||
params := make([]interface{}, 0)
|
||||
|
||||
sql.WriteString(`SELECT *
|
||||
from alert_rule
|
||||
from alert
|
||||
`)
|
||||
|
||||
sql.WriteString(`WHERE org_id = ?`)
|
||||
@ -141,15 +141,17 @@ func HandleAlertsQuery(query *m.GetAlertsQuery) error {
|
||||
}
|
||||
|
||||
func DeleteAlertDefinition(dashboardId int64, sess *xorm.Session) error {
|
||||
alerts := make(m.Alerts, 0)
|
||||
alerts := make([]*m.Alert, 0)
|
||||
sess.Where("dashboard_id = ?", dashboardId).Find(&alerts)
|
||||
|
||||
for _, alert := range alerts {
|
||||
_, err := sess.Exec("DELETE FROM alert_rule WHERE id = ? ", alert.Id)
|
||||
_, err := sess.Exec("DELETE FROM alert WHERE id = ? ", alert.Id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sqlog.Debug("Alert deleted (due to dashboard deletion)", "name", alert.Name, "id", alert.Id)
|
||||
|
||||
if err := SaveAlertChange("DELETED", alert, sess); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -194,6 +196,7 @@ func upsertAlerts(alerts []*m.Alert, posted []*m.Alert, sess *xorm.Session) erro
|
||||
return err
|
||||
}
|
||||
|
||||
sqlog.Debug("Alert updated", "name", alert.Name, "id", alert.Id)
|
||||
SaveAlertChange("UPDATED", alert, sess)
|
||||
}
|
||||
|
||||
@ -205,6 +208,8 @@ func upsertAlerts(alerts []*m.Alert, posted []*m.Alert, sess *xorm.Session) erro
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sqlog.Debug("Alert inserted", "name", alert.Name, "id", alert.Id)
|
||||
SaveAlertChange("CREATED", alert, sess)
|
||||
}
|
||||
}
|
||||
@ -223,11 +228,13 @@ func deleteMissingAlerts(alerts []*m.Alert, posted []*m.Alert, sess *xorm.Sessio
|
||||
}
|
||||
|
||||
if missing {
|
||||
_, err := sess.Exec("DELETE FROM alert_rule WHERE id = ?", missingAlert.Id)
|
||||
_, err := sess.Exec("DELETE FROM alert WHERE id = ?", missingAlert.Id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sqlog.Debug("Alert deleted", "name", missingAlert.Name, "id", missingAlert.Id)
|
||||
|
||||
err = SaveAlertChange("DELETED", missingAlert, sess)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -39,7 +39,7 @@ func GetAlertRuleChanges(query *m.GetAlertChangesQuery) error {
|
||||
params = append(params, query.Limit)
|
||||
}
|
||||
|
||||
alertChanges := make([]*m.AlertRuleChange, 0)
|
||||
alertChanges := make([]*m.AlertChange, 0)
|
||||
if err := x.Sql(sql.String(), params...).Find(&alertChanges); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -49,7 +49,7 @@ func GetAlertRuleChanges(query *m.GetAlertChangesQuery) error {
|
||||
}
|
||||
|
||||
func SaveAlertChange(change string, alert *m.Alert, sess *xorm.Session) error {
|
||||
_, err := sess.Insert(&m.AlertRuleChange{
|
||||
_, err := sess.Insert(&m.AlertChange{
|
||||
OrgId: alert.OrgId,
|
||||
Type: change,
|
||||
Created: time.Now(),
|
||||
|
@ -7,7 +7,7 @@ import (
|
||||
func addAlertMigrations(mg *Migrator) {
|
||||
|
||||
alertV1 := Table{
|
||||
Name: "alert_rule",
|
||||
Name: "alert",
|
||||
Columns: []*Column{
|
||||
{Name: "id", Type: DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true},
|
||||
{Name: "dashboard_id", Type: DB_BigInt, Nullable: false},
|
||||
@ -26,10 +26,10 @@ func addAlertMigrations(mg *Migrator) {
|
||||
}
|
||||
|
||||
// create table
|
||||
mg.AddMigration("create alert_rule table v2", NewAddTableMigration(alertV1))
|
||||
mg.AddMigration("create alert table v1", NewAddTableMigration(alertV1))
|
||||
|
||||
alert_changes := Table{
|
||||
Name: "alert_rule_change",
|
||||
Name: "alert_change",
|
||||
Columns: []*Column{
|
||||
{Name: "id", Type: DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true},
|
||||
{Name: "alert_id", Type: DB_BigInt, Nullable: false},
|
||||
@ -39,7 +39,7 @@ func addAlertMigrations(mg *Migrator) {
|
||||
},
|
||||
}
|
||||
|
||||
mg.AddMigration("create alert_rules_updates table v1", NewAddTableMigration(alert_changes))
|
||||
mg.AddMigration("create alert_change table v1", NewAddTableMigration(alert_changes))
|
||||
|
||||
alert_state_log := Table{
|
||||
Name: "alert_state",
|
||||
|
@ -107,7 +107,7 @@ func (mg *Migrator) Start() error {
|
||||
}
|
||||
|
||||
func (mg *Migrator) exec(m Migration) error {
|
||||
log.Info("Executing migration", "id", m.Id())
|
||||
mg.Logger.Info("Executing migration", "id", m.Id())
|
||||
|
||||
err := mg.inTransaction(func(sess *xorm.Session) error {
|
||||
|
||||
|
@ -22,7 +22,6 @@ var alertQueryDef = new QueryPartDef({
|
||||
export class AlertTabCtrl {
|
||||
panel: any;
|
||||
panelCtrl: any;
|
||||
alerting: any;
|
||||
metricTargets = [{ refId: '- select query -' } ];
|
||||
schedulers = [{text: 'Grafana', value: 1}, {text: 'External', value: 0}];
|
||||
transforms = [
|
||||
@ -36,7 +35,7 @@ export class AlertTabCtrl {
|
||||
},
|
||||
];
|
||||
aggregators = ['avg', 'sum', 'min', 'max', 'last'];
|
||||
rule: any;
|
||||
alert: any;
|
||||
query: any;
|
||||
queryParams: any;
|
||||
transformDef: any;
|
||||
@ -71,33 +70,37 @@ export class AlertTabCtrl {
|
||||
$scope.ctrl = this;
|
||||
|
||||
this.metricTargets = this.panel.targets.map(val => val);
|
||||
this.rule = this.panel.alerting = this.panel.alerting || {};
|
||||
|
||||
this.initAlertModel();
|
||||
}
|
||||
|
||||
initAlertModel() {
|
||||
this.alert = this.panel.alert = this.panel.alert || {};
|
||||
|
||||
// set defaults
|
||||
_.defaults(this.rule, this.defaultValues);
|
||||
_.defaults(this.alert, this.defaultValues);
|
||||
|
||||
var defaultName = (this.panelCtrl.dashboard.title + ' ' + this.panel.title + ' alert');
|
||||
this.rule.name = this.rule.name || defaultName;
|
||||
this.rule.description = this.rule.description || defaultName;
|
||||
this.rule.queryRef = this.panel.alerting.queryRef || this.metricTargets[0].refId;
|
||||
this.alert.name = this.alert.name || defaultName;
|
||||
this.alert.description = this.alert.description || defaultName;
|
||||
|
||||
// great temp working model
|
||||
this.queryParams = {
|
||||
params: [
|
||||
this.rule.query.refId,
|
||||
this.rule.query.from,
|
||||
this.rule.query.to
|
||||
this.alert.query.refId,
|
||||
this.alert.query.from,
|
||||
this.alert.query.to
|
||||
]
|
||||
};
|
||||
|
||||
// init the query part components model
|
||||
this.query = new QueryPart(this.queryParams, alertQueryDef);
|
||||
this.convertThresholdsToAlertThresholds();
|
||||
this.transformDef = _.findWhere(this.transforms, {type: this.rule.transform.type});
|
||||
this.transformDef = _.findWhere(this.transforms, {type: this.alert.transform.type});
|
||||
}
|
||||
|
||||
queryUpdated() {
|
||||
this.rule.query = {
|
||||
this.alert.query = {
|
||||
refId: this.query.params[0],
|
||||
from: this.query.params[1],
|
||||
to: this.query.params[2],
|
||||
@ -106,16 +109,16 @@ export class AlertTabCtrl {
|
||||
|
||||
transformChanged() {
|
||||
// clear model
|
||||
this.rule.transform = {type: this.rule.transform.type};
|
||||
this.transformDef = _.findWhere(this.transforms, {type: this.rule.transform.type});
|
||||
this.alert.transform = {type: this.alert.transform.type};
|
||||
this.transformDef = _.findWhere(this.transforms, {type: this.alert.transform.type});
|
||||
|
||||
switch (this.rule.transform.type) {
|
||||
switch (this.alert.transform.type) {
|
||||
case 'aggregation': {
|
||||
this.rule.transform.method = 'avg';
|
||||
this.alert.transform.method = 'avg';
|
||||
break;
|
||||
}
|
||||
case "forecast": {
|
||||
this.rule.transform.timespan = '7d';
|
||||
this.alert.transform.timespan = '7d';
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -124,45 +127,34 @@ export class AlertTabCtrl {
|
||||
convertThresholdsToAlertThresholds() {
|
||||
if (this.panel.grid
|
||||
&& this.panel.grid.threshold1
|
||||
&& this.rule.warnLevel === undefined
|
||||
&& this.alert.warnLevel === undefined
|
||||
) {
|
||||
this.rule.warning.op = '>';
|
||||
this.rule.warning.level = this.panel.grid.threshold1;
|
||||
this.alert.warning.op = '>';
|
||||
this.alert.warning.level = this.panel.grid.threshold1;
|
||||
}
|
||||
|
||||
if (this.panel.grid
|
||||
&& this.panel.grid.threshold2
|
||||
&& this.rule.critical.level === undefined
|
||||
&& this.alert.critical.level === undefined
|
||||
) {
|
||||
this.rule.critical.op = '>';
|
||||
this.rule.critical.level = this.panel.grid.threshold2;
|
||||
this.alert.critical.op = '>';
|
||||
this.alert.critical.level = this.panel.grid.threshold2;
|
||||
}
|
||||
}
|
||||
|
||||
delete() {
|
||||
this.rule = this.panel.alerting = this.defaultValues;
|
||||
this.rule.deleted = true;
|
||||
this.alert = this.panel.alert = {};
|
||||
this.alert.deleted = true;
|
||||
this.initAlertModel();
|
||||
}
|
||||
|
||||
enable() {
|
||||
delete this.rule.deleted;
|
||||
this.rule.enabled = true;
|
||||
delete this.alert.deleted;
|
||||
this.alert.enabled = true;
|
||||
}
|
||||
|
||||
disable() {
|
||||
this.rule.enabled = false;
|
||||
}
|
||||
|
||||
thresholdsUpdated() {
|
||||
if (this.panel.alerting.warnLevel) {
|
||||
this.panel.grid.threshold1 = parseInt(this.panel.alerting.warnLevel);
|
||||
}
|
||||
|
||||
if (this.panel.alerting.critLevel) {
|
||||
this.panel.grid.threshold2 = parseInt(this.panel.alerting.critLevel);
|
||||
}
|
||||
|
||||
this.panelCtrl.render();
|
||||
this.alert.enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -13,7 +13,7 @@
|
||||
<span class="gf-form-label">Transform using</span>
|
||||
<div class="gf-form-select-wrapper">
|
||||
<select class="gf-form-input"
|
||||
ng-model="ctrl.rule.transform.type"
|
||||
ng-model="ctrl.alert.transform.type"
|
||||
ng-options="f.type as f.text for f in ctrl.transforms"
|
||||
ng-change="ctrl.transformChanged()"
|
||||
>
|
||||
@ -24,14 +24,14 @@
|
||||
<span class="gf-form-label">Method</span>
|
||||
<div class="gf-form-select-wrapper">
|
||||
<select class="gf-form-input"
|
||||
ng-model="ctrl.rule.transform.method"
|
||||
ng-model="ctrl.alert.transform.method"
|
||||
ng-options="f for f in ctrl.aggregators">
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="gf-form" ng-if="ctrl.transformDef.type === 'forecast'">
|
||||
<span class="gf-form-label">Timespan</span>
|
||||
<input class="gf-form-input max-width-5" type="text" ng-model="ctrl.rule.transform.timespan" ng-change="ctrl.ruleUpdated()"></input>
|
||||
<input class="gf-form-input max-width-5" type="text" ng-model="ctrl.alert.transform.timespan" ng-change="ctrl.ruleUpdated()"></input>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -44,16 +44,16 @@
|
||||
<i class="icon-gf icon-gf-warn alert-icon-warn"></i>
|
||||
Warn if
|
||||
</span>
|
||||
<metric-segment-model property="ctrl.rule.warning.op" options="ctrl.levelOpList" custom="false" css-class="query-segment-operator"></metric-segment-model>
|
||||
<input class="gf-form-input max-width-7" type="number" ng-model="ctrl.rule.warnLevel" ng-change="ctrl.thresholdsUpdated()"></input>
|
||||
<metric-segment-model property="ctrl.alert.warning.op" options="ctrl.levelOpList" custom="false" css-class="query-segment-operator"></metric-segment-model>
|
||||
<input class="gf-form-input max-width-7" type="number" ng-model="ctrl.alert.warnLevel" ng-change="ctrl.thresholdsUpdated()"></input>
|
||||
</div>
|
||||
<div class="gf-form">
|
||||
<span class="gf-form-label">
|
||||
<i class="icon-gf icon-gf-warn alert-icon-critical"></i>
|
||||
Critcal if
|
||||
</span>
|
||||
<metric-segment-model property="ctrl.rule.critical.op" options="ctrl.levelOpList" custom="false" css-class="query-segment-operator"></metric-segment-model>
|
||||
<input class="gf-form-input max-width-7" type="number" ng-model="ctrl.rule.critLevel" ng-change="ctrl.thresholdsUpdated()"></input>
|
||||
<metric-segment-model property="ctrl.alert.critical.op" options="ctrl.levelOpList" custom="false" css-class="query-segment-operator"></metric-segment-model>
|
||||
<input class="gf-form-input max-width-7" type="number" ng-model="ctrl.alert.critLevel" ng-change="ctrl.thresholdsUpdated()"></input>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -67,14 +67,14 @@
|
||||
<span class="gf-form-label">Scheduler</span>
|
||||
<div class="gf-form-select-wrapper">
|
||||
<select class="gf-form-input"
|
||||
ng-model="ctrl.rule.scheduler"
|
||||
ng-model="ctrl.alert.scheduler"
|
||||
ng-options="f.value as f.text for f in ctrl.schedulers">
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="gf-form">
|
||||
<span class="gf-form-label">Evaluate every</span>
|
||||
<input class="gf-form-input max-width-7" type="text" ng-model="ctrl.rule.frequency"></input>
|
||||
<input class="gf-form-input max-width-7" type="text" ng-model="ctrl.alert.frequency"></input>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -83,7 +83,7 @@
|
||||
<div class="gf-form-inline">
|
||||
<div class="gf-form">
|
||||
<span class="gf-form-label">Groups</span>
|
||||
<bootstrap-tagsinput ng-model="ctrl.rule.notify" tagclass="label label-tag" placeholder="add tags">
|
||||
<bootstrap-tagsinput ng-model="ctrl.alert.notify" tagclass="label label-tag" placeholder="add tags">
|
||||
</bootstrap-tagsinput>
|
||||
</div>
|
||||
</div>
|
||||
@ -109,8 +109,8 @@
|
||||
|
||||
<div class="editor-row">
|
||||
<div class="gf-form-button-row">
|
||||
<button class="btn btn-danger" ng-click="ctrl.delete()" ng-show="ctrl.rule.enabled">Delete</button>
|
||||
<button class="btn btn-success" ng-click="ctrl.enable()" ng-hide="ctrl.rule.enabled">Enable</button>
|
||||
<button class="btn btn-secondary" ng-click="ctrl.disable()" ng-show="ctrl.rule.enabled">Disable</button>
|
||||
<button class="btn btn-danger" ng-click="ctrl.delete()" ng-show="ctrl.alert.enabled">Delete</button>
|
||||
<button class="btn btn-success" ng-click="ctrl.enable()" ng-hide="ctrl.alert.enabled">Enable</button>
|
||||
<button class="btn btn-secondary" ng-click="ctrl.disable()" ng-show="ctrl.alert.enabled">Disable</button>
|
||||
</div>
|
||||
</div>
|
||||
|
Loading…
Reference in New Issue
Block a user