Просмотр исходного кода

feat(alerting): alert threshold handles progress

Torkel Ödegaard 9 лет назад
Родитель
Сommit
1500c0e954

+ 6 - 2
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")
 

+ 0 - 56
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
-}

+ 2 - 2
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": ">"
             }

+ 1 - 1
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
 	}
 

+ 0 - 55
pkg/services/sqlstore/alert_rule_parser_test.go

@@ -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, ">")
-		})
-	})
-}

+ 13 - 10
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);
       }
     };

+ 2 - 2
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;

+ 29 - 17
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 */

+ 56 - 0
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({

+ 47 - 8
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 = '&gt;'; }
+  if (op === '<') { op = '&lt;'; }
+
   return  `
-  <div class="alert-handle" style="position: absolute; top: 100px; right: -50px;">
-    <i class="icon-gf icon-gf-${type} alert-icon-${type}"></i>
-    > 100
+  <div class="alert-handle-wrapper alert-handle-wrapper--${type}">
+    <div class="alert-handle-line">
+    </div>
+    <div class="alert-handle">
+      <i class="icon-gf icon-gf-${type} alert-icon-${type}"></i>
+      ${op} ${value}
+    </div>
   </div>
   `;
 }
 
-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() {

+ 3 - 3
public/app/plugins/panel/graph/partials/tab_alerting.html

@@ -44,8 +44,8 @@
           <i class="icon-gf icon-gf-warn alert-icon-warn"></i>
           Warn if
         </span>
-        <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>
+        <metric-segment-model property="ctrl.alert.warn.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.warn.level" ng-change="ctrl.levelsUpdated()"></input>
       </div>
       <div class="gf-form">
         <span class="gf-form-label">
@@ -53,7 +53,7 @@
           Critcal if
         </span>
         <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>
+        <input class="gf-form-input max-width-7" type="number" ng-model="ctrl.alert.critical.level" ng-change="ctrl.levelsUpdated()"></input>
       </div>
     </div>
   </div>

+ 1 - 1
public/sass/_variables.dark.scss

@@ -44,7 +44,7 @@ $brand-text-highlight:  #f7941d;
 // Status colors
 // -------------------------
 $online:                #10a345;
-$warn:                  #ffc03c;
+$warn:                  #F79520;
 $critical:              #ed2e18;
 
 // Scaffolding

+ 57 - 4
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;
+    }
+  }
+
 }

+ 0 - 6
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;