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

feat(alerting): refactoring alert model to use conditions concept

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

+ 55 - 50
public/app/plugins/panel/graph/alert_tab_ctrl.ts

@@ -19,27 +19,23 @@ var alertQueryDef = new QueryPartDef({
   defaultParams: ['#A', '5m', 'now', 'avg']
 });
 
+var reducerAvgDef = new QueryPartDef({
+  type: 'avg',
+  params: [],
+  defaultParams: []
+});
+
 export class AlertTabCtrl {
   panel: any;
   panelCtrl: any;
   metricTargets;
   handlers = [{text: 'Grafana', value: 1}, {text: 'External', value: 0}];
-  transforms = [
-    {
-      text: 'Aggregation',
-      type: 'aggregation',
-    },
-    {
-      text: 'Linear Forecast',
-      type: 'forecast',
-    },
+  conditionTypes = [
+    {text: 'Query', value: 'query'},
+    {text: 'Alert state', value: 'alert_state'},
   ];
-  aggregators = ['avg', 'sum', 'min', 'max', 'last'];
   alert: any;
-  thresholds: any;
-  query: any;
-  queryParams: any;
-  transformDef: any;
+  conditionModels: any;
   levelOpList = [
     {text: '>', value: '>'},
     {text: '<', value: '<'},
@@ -76,14 +72,10 @@ export class AlertTabCtrl {
     alert.warn = this.getThresholdWithDefaults(alert.warn);
     alert.crit = this.getThresholdWithDefaults(alert.crit);
 
-    alert.query = alert.query || {};
-    alert.query.refId = alert.query.refId || 'A';
-    alert.query.from = alert.query.from || '5m';
-    alert.query.to = alert.query.to || 'now';
-
-    alert.transform = alert.transform || {};
-    alert.transform.type = alert.transform.type || 'aggregation';
-    alert.transform.method = alert.transform.method || 'avg';
+    alert.conditions = alert.conditions || [];
+    if (alert.conditions.length === 0) {
+      alert.conditions.push(this.buildDefaultCondition());
+    }
 
     alert.frequency = alert.frequency || '60s';
     alert.handler = alert.handler || 1;
@@ -93,42 +85,55 @@ export class AlertTabCtrl {
     alert.name = alert.name || defaultName;
     alert.description = alert.description || defaultName;
 
-    // great temp working model
-    this.queryParams = {
-      params: [alert.query.refId, alert.query.from, alert.query.to]
-    };
-
-    // init the query part components model
-    this.query = new QueryPart(this.queryParams, alertQueryDef);
-    this.transformDef = _.findWhere(this.transforms, {type: alert.transform.type});
+    this.conditionModels = _.reduce(alert.conditions, (memo, value) => {
+      memo.push(this.buildConditionModel(value));
+      return memo;
+    }, []);
 
     this.panelCtrl.editingAlert = true;
     this.panelCtrl.render();
   }
 
-  queryUpdated() {
-    this.alert.query = {
-      refId: this.query.params[0],
-      from: this.query.params[1],
-      to: this.query.params[2],
+  buildDefaultCondition() {
+    return {
+      type: 'query',
+      refId: 'A',
+      from: '5m',
+      to: 'now',
+      reducer: 'avg',
+      reducerParams: [],
     };
   }
 
-  transformChanged() {
-    // clear model
-    this.alert.transform = {type: this.alert.transform.type};
-    this.transformDef = _.findWhere(this.transforms, {type: this.alert.transform.type});
-
-    switch (this.alert.transform.type) {
-      case 'aggregation':  {
-        this.alert.transform.method = 'avg';
-        break;
-      }
-      case "forecast": {
-        this.alert.transform.timespan = '7d';
-        break;
-      }
-    }
+  buildConditionModel(source) {
+    var cm: any = {source: source, type: source.type};
+
+    var queryPartModel = {
+      params: [source.refId, source.from, source.to]
+    };
+
+    cm.queryPart = new QueryPart(queryPartModel, alertQueryDef);
+    cm.reducerPart = new QueryPart({params: []}, reducerAvgDef);
+    return cm;
+  }
+
+  queryPartUpdated(conditionModel) {
+    conditionModel.source.refId = conditionModel.queryPart.params[0];
+    conditionModel.source.from = conditionModel.queryPart.params[1];
+    conditionModel.source.to = conditionModel.queryPart.params[2];
+  }
+
+  addCondition(type) {
+    var condition = this.buildDefaultCondition();
+    // add to persited model
+    this.alert.conditions.push(condition);
+    // add to view model
+    this.conditionModels.push(this.buildConditionModel(condition));
+  }
+
+  removeCondition(index) {
+    this.alert.conditions.splice(index, 1);
+    this.conditionModels.splice(index, 1);
   }
 
   delete() {

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

@@ -24,114 +24,94 @@
 </div>
 
 <div ng-if="ctrl.alert.enabled">
-  <div class="editor-row">
-    <div class="gf-form-group section" >
-      <h5 class="section-heading">Alert Query</h5>
-      <div class="gf-form-inline">
-        <div class="gf-form">
-          <query-part-editor
-             class="gf-form-label query-part"
-             part="ctrl.query"
-             part-updated="ctrl.queryUpdated()">
-          </query-part-editor>
-        </div>
-        <div class="gf-form">
-          <span class="gf-form-label">Transform using</span>
-          <div class="gf-form-select-wrapper">
-            <select   class="gf-form-input"
-                      ng-model="ctrl.alert.transform.type"
-                      ng-options="f.type as f.text for f in ctrl.transforms"
-                      ng-change="ctrl.transformChanged()"
-                      >
-            </select>
-          </div>
-        </div>
-        <div class="gf-form" ng-if="ctrl.transformDef.type === 'aggregation'">
-          <span class="gf-form-label">Method</span>
-          <div class="gf-form-select-wrapper">
-            <select   class="gf-form-input"
-                      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.alert.transform.timespan" ng-change="ctrl.ruleUpdated()"></input>
-        </div>
+  <div class="gf-form-group">
+    <h5 class="section-heading">Alert Rule</h5>
+    <div class="gf-form-inline">
+      <div class="gf-form">
+        <span class="gf-form-label width-8">Name</span>
+        <input type="text" class="gf-form-input width-22" ng-model="ctrl.alert.name">
       </div>
-    </div>
-
-    <div class="gf-form-group section">
-      <h5 class="section-heading">Thresholds</h5>
-      <div class="gf-form-inline">
-        <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.alert.crit.op" options="ctrl.operatorList" custom="false" css-class="query-segment-operator" on-change="ctrl.thresholdsUpdated()"></metric-segment-model>
-          <input class="gf-form-input max-width-7" type="number" ng-model="ctrl.alert.crit.value" ng-change="ctrl.thresholdsUpdated()"></input>
-        </div>
-        <div class="gf-form">
-          <span class="gf-form-label">
-            <i class="icon-gf icon-gf-warn alert-icon-warn"></i>
-            Warn if
-          </span>
-          <metric-segment-model property="ctrl.alert.warn.op" options="ctrl.operatorList" custom="false" css-class="query-segment-operator" on-change="ctrl.thresholdsUpdated()"></metric-segment-model>
-          <input class="gf-form-input max-width-7" type="number" ng-model="ctrl.alert.warn.value" ng-change="ctrl.thresholdsUpdated()"></input>
+      <div class="gf-form">
+        <span class="gf-form-label">Handler</span>
+        <div class="gf-form-select-wrapper">
+          <select   class="gf-form-input"
+                    ng-model="ctrl.alert.handler"
+                    ng-options="f.value as f.text for f in ctrl.handlers">
+          </select>
         </div>
       </div>
-    </div>
-  </div>
-
-  <div class="editor-row">
-    <div class="gf-form-group section">
-      <h5 class="section-heading">Execution</h5>
-      <div class="gf-form-inline">
-        <div class="gf-form">
-          <span class="gf-form-label">Handler</span>
-          <div class="gf-form-select-wrapper">
-            <select   class="gf-form-input"
-                      ng-model="ctrl.alert.handler"
-                      ng-options="f.value as f.text for f in ctrl.handlers">
-            </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.alert.frequency"></input>
-        </div>
+      <div class="gf-form">
+        <span class="gf-form-label width-8">Evaluate every</span>
+        <input class="gf-form-input max-width-7" type="text" ng-model="ctrl.alert.frequency"></input>
       </div>
     </div>
-    <div class="gf-form-group section">
-      <h5 class="section-heading">Notifications</h5>
-      <div class="gf-form-inline">
-        <div class="gf-form">
-          <span class="gf-form-label">Groups</span>
-          <input class="gf-form-input max-width-7" type="text" ng-model="ctrl.alert.notify"></input>
-          <!--
-          <bootstrap-tagsinput ng-model="ctrl.alert.notify" tagclass="label label-tag" placeholder="add tags">
-          </bootstrap-tagsinput>
-          -->
-        </div>
-      </div>
+    <div class="gf-form">
+      <span class="gf-form-label width-8">Notifications</span>
+      <input class="gf-form-input max-width-22" type="text" ng-model="ctrl.alert.notify"></input>
+      <!--
+        <bootstrap-tagsinput ng-model="ctrl.alert.notify" tagclass="label label-tag" placeholder="add tags">
+        </bootstrap-tagsinput>
+      -->
     </div>
   </div>
 
-  <div class="gf-form-group section">
-    <h5 class="section-heading">Information</h5>
-    <div class="gf-form">
-      <span class="gf-form-label width-10">Alert name</span>
-      <input type="text" class="gf-form-input width-22" ng-model="ctrl.alert.name">
-    </div>
-    <div class="gf-form-inline">
+  <div class="gf-form-group">
+    <h5 class="section-heading">Conditions</h5>
+    <div class="gf-form-inline" ng-repeat="conditionModel in ctrl.conditionModels">
       <div class="gf-form">
-        <span class="gf-form-label width-10" style="margin-top: -73px;">Alert description</span>
+        <span class="gf-form-label">{{$index+1}}</span>
       </div>
       <div class="gf-form">
-        <textarea rows="5" ng-model="ctrl.alert.description" class="gf-form-input width-22"></textarea>
+        <query-part-editor
+           class="gf-form-label query-part"
+           part="conditionModel.queryPart"
+           part-updated="ctrl.queryPartUpdated(conditionModel)">
+        </query-part-editor>
       </div>
+      <div class="gf-form">
+        <span class="gf-form-label">Reduce</span>
+        <query-part-editor
+              class="gf-form-label query-part"
+              part="conditionModel.reducerPart"
+              part-updated="ctrl.reducerPartUpdated(conditionModel)">
+        </query-part-editor>
+      </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.alert.crit.op" options="ctrl.operatorList" custom="false" css-class="query-segment-operator" on-change="ctrl.thresholdsUpdated()"></metric-segment-model>
+        <input class="gf-form-input max-width-7" type="number" ng-model="ctrl.alert.crit.value" ng-change="ctrl.thresholdsUpdated()"></input>
+      </div>
+      <div class="gf-form">
+        <span class="gf-form-label">
+          <i class="icon-gf icon-gf-warn alert-icon-warn"></i>
+          Warn if
+        </span>
+        <metric-segment-model property="ctrl.alert.warn.op" options="ctrl.operatorList" custom="false" css-class="query-segment-operator" on-change="ctrl.thresholdsUpdated()"></metric-segment-model>
+        <input class="gf-form-input max-width-7" type="number" ng-model="ctrl.alert.warn.value" ng-change="ctrl.thresholdsUpdated()"></input>
+
+        <label class="gf-form-label">
+          <a class="pointer" tabindex="1" ng-click="ctrl.removeCondition($index)">
+            <i class="fa fa-trash"></i>
+          </a>
+        </label>
+      </div>
+    </div>
+  </div>
+
+  <div class="gf-form-group">
+    <div class="dropdown">
+      <button class="btn btn-inverse dropdown-toggle gf-form-btn" data-toggle="dropdown">
+        <i class="fa fa-plus"></i>&nbsp;Add Condition
+      </button>
+
+      <ul class="dropdown-menu" role="menu">
+        <li ng-repeat="ct in ctrl.conditionTypes" role="menuitem">
+          <a ng-click="ctrl.addCondition(ct.value);">{{ct.text}}</a>
+        </li>
+      </ul>
     </div>
   </div>
 </div>