diff --git a/pkg/services/alerting/alert_rule.go b/pkg/services/alerting/alert_rule.go index 8678311a6a2..cfcf17f0a56 100644 --- a/pkg/services/alerting/alert_rule.go +++ b/pkg/services/alerting/alert_rule.go @@ -25,6 +25,10 @@ type AlertRule struct { Transformer Transformer } +func getTimeDurationStringToSeconds(str string) int64 { + return 60 +} + func NewAlertRuleFromDBModel(ruleDef *m.Alert) (*AlertRule, error) { model := &AlertRule{} model.Id = ruleDef.Id @@ -39,13 +43,13 @@ func NewAlertRuleFromDBModel(ruleDef *m.Alert) (*AlertRule, error) { Level: critical.Get("level").MustFloat64(), } - warning := ruleDef.Expression.Get("warning") + warning := ruleDef.Expression.Get("warn") model.Warning = Level{ Operator: warning.Get("op").MustString(), Level: warning.Get("level").MustFloat64(), } - model.Frequency = ruleDef.Expression.Get("frequency").MustInt64() + model.Frequency = getTimeDurationStringToSeconds(ruleDef.Expression.Get("frequency").MustString()) model.Transform = ruleDef.Expression.Get("transform").Get("type").MustString() model.TransformParams = *ruleDef.Expression.Get("transform") diff --git a/pkg/services/alerting/commands.go b/pkg/services/alerting/commands.go index 2d32396c7e1..4e269aca695 100644 --- a/pkg/services/alerting/commands.go +++ b/pkg/services/alerting/commands.go @@ -1,8 +1,6 @@ package alerting import ( - "fmt" - "github.com/grafana/grafana/pkg/bus" m "github.com/grafana/grafana/pkg/models" ) @@ -38,57 +36,3 @@ func updateDashboardAlerts(cmd *UpdateDashboardAlertsCommand) error { return nil } - -func getTimeDurationStringToSeconds(str string) int64 { - return 60 -} - -func ConvetAlertModelToAlertRule(ruleDef *m.Alert) (*AlertRule, error) { - model := &AlertRule{} - model.Id = ruleDef.Id - model.OrgId = ruleDef.OrgId - model.Name = ruleDef.Name - model.Description = ruleDef.Description - model.State = ruleDef.State - - critical := ruleDef.Expression.Get("critical") - model.Critical = Level{ - Operator: critical.Get("op").MustString(), - Level: critical.Get("level").MustFloat64(), - } - - warning := ruleDef.Expression.Get("warning") - model.Warning = Level{ - Operator: warning.Get("op").MustString(), - Level: warning.Get("level").MustFloat64(), - } - - model.Frequency = getTimeDurationStringToSeconds(ruleDef.Expression.Get("frequency").MustString()) - model.Transform = ruleDef.Expression.Get("transform").Get("type").MustString() - model.TransformParams = *ruleDef.Expression.Get("transform") - - if model.Transform == "aggregation" { - model.Transformer = &AggregationTransformer{ - Method: ruleDef.Expression.Get("transform").Get("method").MustString(), - } - } - - query := ruleDef.Expression.Get("query") - model.Query = AlertQuery{ - Query: query.Get("query").MustString(), - DatasourceId: query.Get("datasourceId").MustInt64(), - From: query.Get("from").MustString(), - To: query.Get("to").MustString(), - Aggregator: query.Get("agg").MustString(), - } - - if model.Query.Query == "" { - return nil, fmt.Errorf("missing query.query") - } - - if model.Query.DatasourceId == 0 { - return nil, fmt.Errorf("missing query.datasourceId") - } - - return model, nil -} diff --git a/pkg/services/alerting/extractor_test.go b/pkg/services/alerting/extractor_test.go index 069489dfb23..6eda321f0dc 100644 --- a/pkg/services/alerting/extractor_test.go +++ b/pkg/services/alerting/extractor_test.go @@ -55,7 +55,7 @@ func TestAlertRuleExtraction(t *testing.T) { "method": "avg", "type": "aggregation" }, - "warning": { + "warn": { "level": 10, "op": ">" } @@ -90,7 +90,7 @@ func TestAlertRuleExtraction(t *testing.T) { "method": "avg", "name": "aggregation" }, - "warning": { + "warn": { "level": 10, "op": ">" } diff --git a/pkg/services/alerting/reader.go b/pkg/services/alerting/reader.go index 7f8f6b2c5de..db7da930746 100644 --- a/pkg/services/alerting/reader.go +++ b/pkg/services/alerting/reader.go @@ -49,7 +49,7 @@ func (arr *AlertRuleReader) Fetch() []*AlertRule { res := make([]*AlertRule, len(cmd.Result)) for i, ruleDef := range cmd.Result { - model, _ := ConvetAlertModelToAlertRule(ruleDef) + model, _ := NewAlertRuleFromDBModel(ruleDef) res[i] = model } diff --git a/pkg/services/sqlstore/alert_rule_parser_test.go b/pkg/services/sqlstore/alert_rule_parser_test.go deleted file mode 100644 index 8ec7c24429b..00000000000 --- a/pkg/services/sqlstore/alert_rule_parser_test.go +++ /dev/null @@ -1,55 +0,0 @@ -package sqlstore - -import ( - "testing" - - "github.com/grafana/grafana/pkg/components/simplejson" - m "github.com/grafana/grafana/pkg/models" - "github.com/grafana/grafana/pkg/services/alerting" - . "github.com/smartystreets/goconvey/convey" -) - -func TestAlertRuleModelParsing(t *testing.T) { - - Convey("Parsing alertRule from expression", t, func() { - alertRuleDAO := &m.Alert{} - json, _ := simplejson.NewJson([]byte(` - { - "frequency": 10, - "warning": { - "op": ">", - "level": 10 - }, - "critical": { - "op": ">", - "level": 20 - }, - "query": { - "refId": "A", - "from": "5m", - "to": "now", - "datasourceId": 1, - "query": "aliasByNode(statsd.fakesite.counters.session_start.*.count, 4)" - }, - "transform": { - "type": "aggregation", - "method": "avg" - } - }`)) - - alertRuleDAO.Name = "Test" - alertRuleDAO.Expression = json - rule, _ := alerting.ConvetAlertModelToAlertRule(alertRuleDAO) - - Convey("Confirm that all properties are set", func() { - So(rule.Query.Query, ShouldEqual, "aliasByNode(statsd.fakesite.counters.session_start.*.count, 4)") - So(rule.Query.From, ShouldEqual, "5m") - So(rule.Query.To, ShouldEqual, "now") - So(rule.Query.DatasourceId, ShouldEqual, 1) - So(rule.Warning.Level, ShouldEqual, 10) - So(rule.Warning.Operator, ShouldEqual, ">") - So(rule.Critical.Level, ShouldEqual, 20) - So(rule.Critical.Operator, ShouldEqual, ">") - }) - }) -} diff --git a/public/app/features/dashboard/viewStateSrv.js b/public/app/features/dashboard/viewStateSrv.js index 035bfb6ae6e..fe1277c59c0 100644 --- a/public/app/features/dashboard/viewStateSrv.js +++ b/public/app/features/dashboard/viewStateSrv.js @@ -120,25 +120,28 @@ function (angular, _, $) { if (this.panelScopes.length === 0) { return; } if (this.dashboard.meta.fullscreen) { - if (this.fullscreenPanel) { - this.leaveFullscreen(false); - } var panelScope = this.getPanelScope(this.state.panelId); - // panel could be about to be created/added and scope does - // not exist yet if (!panelScope) { return; } + if (this.fullscreenPanel) { + // if already fullscreen + if (this.fullscreenPanel === panelScope) { + return; + } else { + this.leaveFullscreen(false); + } + } + if (!panelScope.ctrl.editModeInitiated) { panelScope.ctrl.initEditMode(); } - this.enterFullscreen(panelScope); - return; - } - - if (this.fullscreenPanel) { + if (!panelScope.ctrl.fullscreen) { + this.enterFullscreen(panelScope); + } + } else if (this.fullscreenPanel) { this.leaveFullscreen(true); } }; diff --git a/public/app/features/panel/panel_ctrl.ts b/public/app/features/panel/panel_ctrl.ts index 0f253b5048a..bcb1980f854 100644 --- a/public/app/features/panel/panel_ctrl.ts +++ b/public/app/features/panel/panel_ctrl.ts @@ -152,8 +152,8 @@ export class PanelCtrl { calculatePanelHeight() { if (this.fullscreen) { var docHeight = $(window).height(); - var editHeight = Math.floor(docHeight * 0.3); - var fullscreenHeight = Math.floor(docHeight * 0.7); + var editHeight = Math.floor(docHeight * 0.4); + var fullscreenHeight = Math.floor(docHeight * 0.6); this.containerHeight = this.editMode ? editHeight : fullscreenHeight; } else { this.containerHeight = this.panel.height || this.row.height; diff --git a/public/app/plugins/panel/graph/alert_tab_ctrl.ts b/public/app/plugins/panel/graph/alert_tab_ctrl.ts index 3f83e6db38e..d16274efb56 100644 --- a/public/app/plugins/panel/graph/alert_tab_ctrl.ts +++ b/public/app/plugins/panel/graph/alert_tab_ctrl.ts @@ -50,7 +50,7 @@ export class AlertTabCtrl { notify: [], enabled: false, scheduler: 1, - warning: { op: '>', level: undefined }, + warn: { op: '>', level: undefined }, critical: { op: '>', level: undefined }, query: { refId: 'A', @@ -70,8 +70,16 @@ export class AlertTabCtrl { $scope.ctrl = this; this.metricTargets = this.panel.targets.map(val => val); - this.initAlertModel(); + + // set panel alert edit mode + this.panelCtrl.editingAlert = true; + this.panelCtrl.render(); + + $scope.$on("$destroy", () => { + this.panelCtrl.editingAlert = false; + this.panelCtrl.render(); + }); } initAlertModel() { @@ -125,21 +133,21 @@ export class AlertTabCtrl { } convertThresholdsToAlertThresholds() { - if (this.panel.grid - && this.panel.grid.threshold1 - && this.alert.warnLevel === undefined - ) { - this.alert.warning.op = '>'; - this.alert.warning.level = this.panel.grid.threshold1; - } - - if (this.panel.grid - && this.panel.grid.threshold2 - && this.alert.critical.level === undefined - ) { - this.alert.critical.op = '>'; - this.alert.critical.level = this.panel.grid.threshold2; - } + // if (this.panel.grid + // && this.panel.grid.threshold1 + // && this.alert.warnLevel === undefined + // ) { + // this.alert.warning.op = '>'; + // this.alert.warning.level = this.panel.grid.threshold1; + // } + // + // if (this.panel.grid + // && this.panel.grid.threshold2 + // && this.alert.critical.level === undefined + // ) { + // this.alert.critical.op = '>'; + // this.alert.critical.level = this.panel.grid.threshold2; + // } } delete() { @@ -156,6 +164,10 @@ export class AlertTabCtrl { disable() { this.alert.enabled = false; } + + levelsUpdated() { + this.panelCtrl.render(); + } } /** @ngInject */ diff --git a/public/app/plugins/panel/graph/graph.js b/public/app/plugins/panel/graph/graph.js index 621229d9d88..23c9cd65a9f 100755 --- a/public/app/plugins/panel/graph/graph.js +++ b/public/app/plugins/panel/graph/graph.js @@ -169,6 +169,7 @@ function (angular, $, moment, _, kbn, GraphTooltip) { var right = panel.yaxes[1]; if (left.show && left.label) { gridMargin.left = 20; } if (right.show && right.label) { gridMargin.right = 20; } + } // Function for rendering panel @@ -178,6 +179,12 @@ function (angular, $, moment, _, kbn, GraphTooltip) { panelWidth = panelWidthCache[panel.span] = elem.width(); } + if (ctrl.editingAlert) { + elem.css('margin-right', '220px'); + } else { + elem.css('margin-right', ''); + } + if (shouldAbortRender()) { return; } @@ -186,6 +193,10 @@ function (angular, $, moment, _, kbn, GraphTooltip) { // Populate element var options = { + alerting: { + editing: ctrl.editingAlert, + alert: panel.alert, + }, hooks: { draw: [drawHook], processOffset: [processOffsetHook], @@ -260,6 +271,7 @@ function (angular, $, moment, _, kbn, GraphTooltip) { function callPlot(incrementRenderCounter) { try { + console.log('rendering'); $.plot(elem, sortedSeries, options); } catch (e) { console.log('flotcharts error', e); @@ -312,6 +324,50 @@ function (angular, $, moment, _, kbn, GraphTooltip) { } function addGridThresholds(options, panel) { + if (panel.alert && panel.alert.enabled) { + var crit = panel.alert.critical; + var warn = panel.alert.warn; + var critEdge = Infinity; + var warnEdge = crit.level; + + if (_.isNumber(crit.level)) { + if (crit.op === '<') { + critEdge = -Infinity; + } + + // fill + options.grid.markings.push({ + yaxis: {from: crit.level, to: critEdge}, + color: 'rgba(234, 112, 112, 0.10)', + }); + + // line + options.grid.markings.push({ + yaxis: {from: crit.level, to: crit.level}, + color: '#ed2e18' + }); + } + + if (_.isNumber(warn.level)) { + // if (warn.op === '<') { + // } + + // fill + options.grid.markings.push({ + yaxis: {from: warn.level, to: warnEdge}, + color: 'rgba(216, 200, 27, 0.10)', + }); + + // line + options.grid.markings.push({ + yaxis: {from: warn.level, to: warn.level}, + color: '#F79520' + }); + } + + return; + } + if (_.isNumber(panel.grid.threshold1)) { var limit1 = panel.grid.thresholdLine ? panel.grid.threshold1 : (panel.grid.threshold2 || null); options.grid.markings.push({ diff --git a/public/app/plugins/panel/graph/jquery.flot.alerts.ts b/public/app/plugins/panel/graph/jquery.flot.alerts.ts index e95e0871d92..65229b31065 100644 --- a/public/app/plugins/panel/graph/jquery.flot.alerts.ts +++ b/public/app/plugins/panel/graph/jquery.flot.alerts.ts @@ -2,24 +2,63 @@ import 'jquery.flot'; import $ from 'jquery'; +import _ from 'lodash'; var options = {}; -function getHandleTemplate(type) { +function getHandleTemplate(type, op, value) { + if (op === '>') { op = '>'; } + if (op === '<') { op = '<'; } + return ` -
- - > 100 +
+
+
+
+ + ${op} ${value} +
`; } -function drawAlertHandles(plot, canvascontext) { - var $warnHandle = $(getHandleTemplate('warn')); +function drawAlertHandles(plot) { + var options = plot.getOptions(); var $placeholder = plot.getPlaceholder(); - $placeholder.find(".alert-warn-handle").remove(); - $placeholder.append($warnHandle); + + if (!options.alerting.editing) { + $placeholder.find(".alert-handle").remove(); + return; + } + + var alert = options.alerting.alert; + var height = plot.height(); + + function renderHandle(type, model) { + var $handle = $placeholder.find(`.alert-handle-${type}`); + + if (!_.isNumber(model.level)) { + $handle.remove(); + return; + } + + if ($handle.length === 0) { + $handle = $(getHandleTemplate(type, model.op, model.level)); + $placeholder.append($handle); + } else { + $handle.html(getHandleTemplate(type, model.op, model.level)); + } + + var levelCanvasPos = plot.p2c({x: 0, y: model.level}); + console.log('canvas level pos', levelCanvasPos.top); + + var levelTopPos = Math.min(Math.max(levelCanvasPos.top, 0), height) - 6; + $handle.css({top: levelTopPos}); + } + + renderHandle('critical', alert.critical); + renderHandle('warn', alert.warn); } function shutdown() { diff --git a/public/app/plugins/panel/graph/partials/tab_alerting.html b/public/app/plugins/panel/graph/partials/tab_alerting.html index 2b7b4e112e3..a81bbe2f996 100644 --- a/public/app/plugins/panel/graph/partials/tab_alerting.html +++ b/public/app/plugins/panel/graph/partials/tab_alerting.html @@ -44,8 +44,8 @@ Warn if - - + +
@@ -53,7 +53,7 @@ Critcal if - +
diff --git a/public/sass/_variables.dark.scss b/public/sass/_variables.dark.scss index 047e50faa73..99070cad837 100644 --- a/public/sass/_variables.dark.scss +++ b/public/sass/_variables.dark.scss @@ -44,7 +44,7 @@ $brand-text-highlight: #f7941d; // Status colors // ------------------------- $online: #10a345; -$warn: #ffc03c; +$warn: #F79520; $critical: #ed2e18; // Scaffolding diff --git a/public/sass/components/_panel_graph.scss b/public/sass/components/_panel_graph.scss index b040b95ce31..20b9376b7dd 100644 --- a/public/sass/components/_panel_graph.scss +++ b/public/sass/components/_panel_graph.scss @@ -315,8 +315,61 @@ font-size: 12px; } -.alert-handle { - padding: 0.4rem;; - background-color: $dark-4; - box-shadow: $search-shadow; +.alert-handle-wrapper { + position: absolute; + + &--warn { + right: -221px; + width: 238px; + + .alert-handle-line { + float: left; + height: 2px; + width: 138px; + margin-top: 14px; + background-color: $warn; + z-index: 0; + position: relative; + } + } + + &--critical { + right: -105px; + width: 123px; + + .alert-handle-line { + float: left; + height: 2px; + width: 23px; + margin-top: 14px; + background-color: $critical; + z-index: 0; + position: relative; + } + } + + + .alert-handle { + z-index: 10; + position: relative; + float: right; + padding: 0.4rem;; + background-color: $btn-inverse-bg; + box-shadow: $search-shadow; + cursor: pointer; + width: 100px; + font-size: $font-size-sm; + box-shadow: 4px 4px 3px 0px $body-bg; + border-radius: 4px; + border-width: 0 1px 1px 0; + border-style: solid; + border-color: $black; + + .icon-gf { + font-size: 17px; + position: relative; + top: 2px; + } + } + } diff --git a/public/sass/pages/_dashboard.scss b/public/sass/pages/_dashboard.scss index 8fb1e6bcdaa..eec86e78234 100644 --- a/public/sass/pages/_dashboard.scss +++ b/public/sass/pages/_dashboard.scss @@ -197,12 +197,6 @@ div.flot-text { bottom: 0; } -.panel-fullscreen { - .panel-title-container { - padding: 8px; - } -} - .panel-full-edit { margin-top: 20px; margin-bottom: 20px;