Browse Source

Merge branch 'master' of github.com:grafana/grafana

Conflicts:
	CHANGELOG.md
Torkel Ödegaard 11 years ago
parent
commit
bf5f6ce97c

+ 5 - 0
CHANGELOG.md

@@ -1,9 +1,14 @@
 # 1.9.0 (unreleased)
 # 1.9.0 (unreleased)
 
 
+**Enhancements**
+- [Issue #1130](https://github.com/grafana/grafana/issues/1130). SinglestatPanel: Added null point handling, and value to text mapping
+
+
 **Fixes**
 **Fixes**
 - [Issue #1087](https://github.com/grafana/grafana/issues/1087). Panel: Fixed IE9 crash due to angular drag drop
 - [Issue #1087](https://github.com/grafana/grafana/issues/1087). Panel: Fixed IE9 crash due to angular drag drop
 - [Issue #1093](https://github.com/grafana/grafana/issues/1093). SingleStatPanel: Fixed position for drilldown link tooltip when dashboard requires scrolling
 - [Issue #1093](https://github.com/grafana/grafana/issues/1093). SingleStatPanel: Fixed position for drilldown link tooltip when dashboard requires scrolling
 - [Issue #1095](https://github.com/grafana/grafana/issues/1095). DrilldownLink: template variables in params property was not interpolated
 - [Issue #1095](https://github.com/grafana/grafana/issues/1095). DrilldownLink: template variables in params property was not interpolated
+- [Issue #1114](https://github.com/grafana/grafana/issues/1114). Graphite: Lexer fix, allow equal sign (=) in metric paths
 - [Issue #1136](https://github.com/grafana/grafana/issues/1136). Graph: Fix to legend value Max and negative values
 - [Issue #1136](https://github.com/grafana/grafana/issues/1136). Graph: Fix to legend value Max and negative values
 
 
 # 1.9.0-rc1 (2014-11-17)
 # 1.9.0-rc1 (2014-11-17)

+ 0 - 1
src/app/components/panelmeta.js

@@ -5,7 +5,6 @@ function () {
 
 
   function PanelMeta(options) {
   function PanelMeta(options) {
     this.description = options.description;
     this.description = options.description;
-    this.titlePos = options.titlePos;
     this.fullscreen = options.fullscreen;
     this.fullscreen = options.fullscreen;
     this.menu = [];
     this.menu = [];
     this.editorTabs = [];
     this.editorTabs = [];

+ 3 - 3
src/app/dashboards/scripted.js

@@ -17,7 +17,7 @@
 var window, document, ARGS, $, jQuery, moment, kbn;
 var window, document, ARGS, $, jQuery, moment, kbn;
 
 
 // Setup some variables
 // Setup some variables
-var dashboard, timspan;
+var dashboard;
 
 
 // All url parameters are available via the ARGS object
 // All url parameters are available via the ARGS object
 var ARGS;
 var ARGS;
@@ -30,11 +30,11 @@ dashboard = {
 // Set a title
 // Set a title
 dashboard.title = 'Scripted dash';
 dashboard.title = 'Scripted dash';
 
 
-// set default time
+// Set default time
 // time can be overriden in the url using from/to parameteres, but this is
 // time can be overriden in the url using from/to parameteres, but this is
 // handled automatically in grafana core during dashboard initialization
 // handled automatically in grafana core during dashboard initialization
 dashboard.time = {
 dashboard.time = {
-  from: 'now-6h',
+  from: "now-6h",
   to: "now"
   to: "now"
 };
 };
 
 

+ 7 - 6
src/app/dashboards/scripted_async.js

@@ -22,10 +22,7 @@ var window, document, ARGS, $, jQuery, moment, kbn;
 return function(callback) {
 return function(callback) {
 
 
   // Setup some variables
   // Setup some variables
-  var dashboard, timspan;
-
-  // Set a default timespan if one isn't specified
-  timspan = ARGS.from || 'now-1d';
+  var dashboard;
 
 
   // Intialize a skeleton with nothing but a rows array and service object
   // Intialize a skeleton with nothing but a rows array and service object
   dashboard = {
   dashboard = {
@@ -35,9 +32,13 @@ return function(callback) {
 
 
   // Set a title
   // Set a title
   dashboard.title = 'Scripted dash';
   dashboard.title = 'Scripted dash';
+
+  // Set default time
+  // time can be overriden in the url using from/to parameteres, but this is
+  // handled automatically in grafana core during dashboard initialization
   dashboard.time = {
   dashboard.time = {
-    from: timspan,
-    to: "now"
+      from: "now-6h",
+      to: "now"
   };
   };
 
 
   var rows = 1;
   var rows = 1;

+ 8 - 6
src/app/dashboards/scripted_templated.js

@@ -17,25 +17,27 @@
 var window, document, ARGS, $, jQuery, moment, kbn;
 var window, document, ARGS, $, jQuery, moment, kbn;
 
 
 // Setup some variables
 // Setup some variables
-var dashboard, timspan;
+var dashboard;
 
 
 // All url parameters are available via the ARGS object
 // All url parameters are available via the ARGS object
 var ARGS;
 var ARGS;
 
 
-// Set a default timespan if one isn't specified
-timspan = ARGS.from || 'now-1d';
-
 // Intialize a skeleton with nothing but a rows array and service object
 // Intialize a skeleton with nothing but a rows array and service object
 dashboard = {
 dashboard = {
   rows : [],
   rows : [],
 };
 };
 
 
 // Set a title
 // Set a title
-dashboard.title = 'Scripted dash';
+dashboard.title = 'Scripted and templated dash';
+
+// Set default time
+// time can be overriden in the url using from/to parameteres, but this is
+// handled automatically in grafana core during dashboard initialization
 dashboard.time = {
 dashboard.time = {
-  from: timspan,
+  from: "now-6h",
   to: "now"
   to: "now"
 };
 };
+
 dashboard.templating = {
 dashboard.templating = {
   enable: true,
   enable: true,
   list: [
   list: [

+ 0 - 5
src/app/directives/panelMenu.js

@@ -147,11 +147,6 @@ function (angular, $, _) {
             dismiss(2200);
             dismiss(2200);
           };
           };
 
 
-          if ($scope.panelMeta.titlePos && $scope.panel.title) {
-            elem.css('text-align', 'left');
-            $link.css('padding-left', '10px');
-          }
-
           elem.click(showMenu);
           elem.click(showMenu);
           $compile(elem.contents())($scope);
           $compile(elem.contents())($scope);
         }
         }

+ 41 - 2
src/app/panels/singlestat/editor.html

@@ -13,6 +13,10 @@
 			<label class="small">Postfix</label>
 			<label class="small">Postfix</label>
 			<input type="text" class="input-small" ng-model="panel.postfix" ng-blur="render()" ng-trim="false"></input>
 			<input type="text" class="input-small" ng-model="panel.postfix" ng-blur="render()" ng-trim="false"></input>
 		</div>
 		</div>
+    <div class="editor-option">
+      <label class="small">Null point mode<tip>Define how null values should handled, connected = ignored</tip></label>
+      <select class="input-medium" ng-model="panel.nullPointMode" ng-options="f for f in ['connected', 'null', 'null as zero']" ng-change="get_data()"></select>
+    </div>
 	</div>
 	</div>
 
 
 	<div class="section">
 	<div class="section">
@@ -38,6 +42,9 @@
 			<select class="input-small" ng-model="panel.format" ng-options="f for f in ['none','short','bytes', 'bits', 'bps', 's', 'ms', 'µs', 'ns', 'percent']" ng-change="render()"></select>
 			<select class="input-small" ng-model="panel.format" ng-options="f for f in ['none','short','bytes', 'bits', 'bps', 's', 'ms', 'µs', 'ns', 'percent']" ng-change="render()"></select>
 		</div>
 		</div>
 	</div>
 	</div>
+</div>
+
+<div class="editor-row">
 	<div class="section">
 	<div class="section">
     <h5>Coloring</h5>
     <h5>Coloring</h5>
 		<editor-opt-bool text="Background" model="panel.colorBackground" change="setColoring({background: true})"></editor-opt-bool>
 		<editor-opt-bool text="Background" model="panel.colorBackground" change="setColoring({background: true})"></editor-opt-bool>
@@ -54,9 +61,7 @@
 			<a class="pointer" ng-click="invertColorOrder()">invert order</a>
 			<a class="pointer" ng-click="invertColorOrder()">invert order</a>
 		</div>
 		</div>
 	</div>
 	</div>
-</div>
 
 
-<div class="editor-row">
 	<div class="section">
 	<div class="section">
 		<h5>Spark lines</h5>
 		<h5>Spark lines</h5>
 		<editor-opt-bool text="Spark line" model="panel.sparkline.show" change="render()"></editor-opt-bool>
 		<editor-opt-bool text="Spark line" model="panel.sparkline.show" change="render()"></editor-opt-bool>
@@ -72,5 +77,39 @@
 	</div>
 	</div>
 </div>
 </div>
 
 
+<div class="editor-row">
+	<div class="section">
+		<h5>Value to text mapping</h5>
+		<div class="editor-option">
+			<label class="small">Specify mappings</label>
+			<div class="grafana-target">
+				<div class="grafana-target-inner">
+					<ul class="grafana-segment-list">
+						<li class="grafana-target-segment"  ng-repeat-start="map in panel.valueMaps">
+							<i class="icon-remove pointer" ng-click="removeValueMap(map)"></i>
+						</li>
+
+						<li>
+							<input type="text" ng-model="map.value" placeholder="value" class="input-mini grafana-target-segment-input" ng-blur="render()">
+						</li>
+						<li class="grafana-target-segment">
+							<i class="icon-arrow-right"></i>
+						</li>
+						<li ng-repeat-end>
+							<input type="text" placeholder="text" ng-model="map.text" class="input-mini grafana-target-segment-input" ng-blur="render()">
+						</li>
+
+						<li>
+							<a class="pointer grafana-target-segment" ng-click="addValueMap();">
+								<i class="icon-plus"></i>
+							</a>
+						</li>
+
+					</ul>
+					<div class="clearfix"></div>
+				</div>
+			</div>
+		</div>
+	</div>
 </div>
 </div>
 
 

+ 47 - 8
src/app/panels/singlestat/module.js

@@ -18,7 +18,6 @@ function (angular, app, _, TimeSeries, kbn, PanelMeta) {
 
 
     $scope.panelMeta = new PanelMeta({
     $scope.panelMeta = new PanelMeta({
       description: 'Singlestat panel',
       description: 'Singlestat panel',
-      titlePos: 'left',
       fullscreen: true,
       fullscreen: true,
       metricsEditor: true
       metricsEditor: true
     });
     });
@@ -35,9 +34,14 @@ function (angular, app, _, TimeSeries, kbn, PanelMeta) {
       format: 'none',
       format: 'none',
       prefix: '',
       prefix: '',
       postfix: '',
       postfix: '',
+      nullText: null,
+      valueMaps: [
+        { value: 'null', op: '=', text: 'N/A' }
+      ],
+      nullPointMode: 'connected',
       valueName: 'avg',
       valueName: 'avg',
       prefixFontSize: '50%',
       prefixFontSize: '50%',
-      valueFontSize: '100%',
+      valueFontSize: '80%',
       postfixFontSize: '50%',
       postfixFontSize: '50%',
       thresholds: '',
       thresholds: '',
       colorBackground: false,
       colorBackground: false,
@@ -99,7 +103,7 @@ function (angular, app, _, TimeSeries, kbn, PanelMeta) {
         alias: seriesData.target,
         alias: seriesData.target,
       });
       });
 
 
-      series.flotpairs = series.getFlotPairs('connected');
+      series.flotpairs = series.getFlotPairs($scope.panel.nullPointMode);
 
 
       return series;
       return series;
     };
     };
@@ -170,15 +174,12 @@ function (angular, app, _, TimeSeries, kbn, PanelMeta) {
       if (!$scope.series || $scope.series.length === 0) {
       if (!$scope.series || $scope.series.length === 0) {
         data.flotpairs = [];
         data.flotpairs = [];
         data.mainValue = Number.NaN;
         data.mainValue = Number.NaN;
-        data.mainValueFormated = 'NaN';
+        data.mainValueFormated = $scope.getFormatedValue(null);
       }
       }
       else {
       else {
         var series = $scope.series[0];
         var series = $scope.series[0];
         data.mainValue = series.stats[$scope.panel.valueName];
         data.mainValue = series.stats[$scope.panel.valueName];
-        var decimalInfo = $scope.getDecimalsForValue(data.mainValue);
-        var formatFunc = kbn.valueFormats[$scope.panel.format];
-
-        data.mainValueFormated = formatFunc(data.mainValue, decimalInfo.decimals, decimalInfo.scaledDecimals);
+        data.mainValueFormated = $scope.getFormatedValue(data.mainValue);
         data.flotpairs = series.flotpairs;
         data.flotpairs = series.flotpairs;
       }
       }
 
 
@@ -192,6 +193,44 @@ function (angular, app, _, TimeSeries, kbn, PanelMeta) {
       $scope.$emit('render');
       $scope.$emit('render');
     };
     };
 
 
+    $scope.getFormatedValue = function(mainValue) {
+
+      // first check value to text mappings
+      for(var i = 0; i < $scope.panel.valueMaps.length; i++) {
+        var map = $scope.panel.valueMaps[i];
+        // special null case
+        if (map.value === 'null') {
+          if (mainValue === null || mainValue === void 0) {
+            return map.text;
+          }
+          continue;
+        }
+        // value/number to text mapping
+        var value = parseFloat(map.value);
+        if (value === mainValue) {
+          return map.text;
+        }
+      }
+
+      if (mainValue === null || mainValue === void 0) {
+        return "no value";
+      }
+
+      var decimalInfo = $scope.getDecimalsForValue(mainValue);
+      var formatFunc = kbn.valueFormats[$scope.panel.format];
+      return formatFunc(mainValue, decimalInfo.decimals, decimalInfo.scaledDecimals);
+    };
+
+    $scope.removeValueMap = function(map) {
+      var index = _.indexOf($scope.panel.valueMaps, map);
+      $scope.panel.valueMaps.splice(index, 1);
+      $scope.render();
+    };
+
+    $scope.addValueMap = function() {
+      $scope.panel.valueMaps.push({value: '', op: '=', text: '' });
+    };
+
     $scope.init();
     $scope.init();
   });
   });
 });
 });

+ 14 - 1
src/app/services/graphite/gfunc.js

@@ -194,7 +194,7 @@ function (_) {
     name: "aliasSub",
     name: "aliasSub",
     category: categories.Special,
     category: categories.Special,
     params: [{ name: "search", type: 'string' }, { name: "replace", type: 'string' }],
     params: [{ name: "search", type: 'string' }, { name: "replace", type: 'string' }],
-    defaultParams: ['', '']
+    defaultParams: ['', '\\1']
   });
   });
 
 
   addFuncDef({
   addFuncDef({
@@ -241,6 +241,8 @@ function (_) {
     params: [
     params: [
       { name: "node", type: "int", options: [0,1,2,3,4,5,6,7,8,9,10,12] },
       { name: "node", type: "int", options: [0,1,2,3,4,5,6,7,8,9,10,12] },
       { name: "node", type: "int", options: [0,-1,-2,-3,-4,-5,-6,-7], optional: true },
       { name: "node", type: "int", options: [0,-1,-2,-3,-4,-5,-6,-7], optional: true },
+      { name: "node", type: "int", options: [0,-1,-2,-3,-4,-5,-6,-7], optional: true },
+      { name: "node", type: "int", options: [0,-1,-2,-3,-4,-5,-6,-7], optional: true },
     ],
     ],
     defaultParams: [3]
     defaultParams: [3]
   });
   });
@@ -357,6 +359,17 @@ function (_) {
     defaultParams: ['1d']
     defaultParams: ['1d']
   });
   });
 
 
+  addFuncDef({
+    name: 'timeStack',
+    category: categories.Transform,
+    params: [
+      { name: "timeShiftUnit", type: "select", options: ['1h', '6h', '12h', '1d', '2d', '7d', '14d', '30d'] },
+      { name: "timeShiftStart", type: "int" },
+      { name: "timeShiftEnd", type: "int" }
+    ],
+    defaultParams: ['1d', 0, 7]
+  });
+
   addFuncDef({
   addFuncDef({
     name: 'summarize',
     name: 'summarize',
     category: categories.Transform,
     category: categories.Transform,

+ 1 - 0
src/app/services/graphite/lexer.js

@@ -129,6 +129,7 @@ define([
       i === 63 ||           // ?
       i === 63 ||           // ?
       i === 37 ||           // %
       i === 37 ||           // %
       i === 35 ||           // #
       i === 35 ||           // #
+      i === 61 ||           // =
       i >= 97 && i <= 122;  // a-z
       i >= 97 && i <= 122;  // a-z
   }
   }
 
 

+ 2 - 1
src/app/services/influxdb/influxdbDatasource.js

@@ -372,8 +372,9 @@ function (angular, _, kbn, InfluxSeries, InfluxQueryBuilder) {
     function getTimeFilter(options) {
     function getTimeFilter(options) {
       var from = getInfluxTime(options.range.from);
       var from = getInfluxTime(options.range.from);
       var until = getInfluxTime(options.range.to);
       var until = getInfluxTime(options.range.to);
+      var fromIsAbsolute = from[from.length-1] === 's';
 
 
-      if (until === 'now()') {
+      if (until === 'now()' && !fromIsAbsolute) {
         return 'time > now() - ' + from;
         return 'time > now() - ' + from;
       }
       }
 
 

+ 4 - 4
src/css/less/variables.light.less

@@ -14,7 +14,7 @@
 @grayDark:              lighten(#000, 20%);   // #333
 @grayDark:              lighten(#000, 20%);   // #333
 @gray:                  lighten(#000, 33.5%); // #555
 @gray:                  lighten(#000, 33.5%); // #555
 @grayLight:             lighten(#000, 60%);   // #999
 @grayLight:             lighten(#000, 60%);   // #999
-@grayLighter:           lighten(#000, 93.5%); // #eee
+@grayLighter:           lighten(#000, 97.5%); // #eee
 @white:                 #fff;
 @white:                 #fff;
 
 
 
 
@@ -53,14 +53,14 @@
 
 
 // Scaffolding
 // Scaffolding
 // -------------------------
 // -------------------------
-@bodyBackground:        @grayLighter;
+@bodyBackground:        #EAEAEA;
 @textColor:             #666;
 @textColor:             #666;
 
 
 
 
 // Links
 // Links
 // -------------------------
 // -------------------------
-@linkColor:             @textColor;
-@linkColorDisabled:     lighten(@linkColor,35%);
+@linkColor:             darken(@textColor, 20%);
+@linkColorDisabled:     lighten(@linkColor,30%);
 @linkColorHover:        @blue;
 @linkColorHover:        @blue;
 
 
 
 

+ 6 - 0
src/test/specs/lexer-specs.js

@@ -29,6 +29,12 @@ define([
       expect(tokens[2].value).to.be('192-168-1-1');
       expect(tokens[2].value).to.be('192-168-1-1');
     });
     });
 
 
+    it('should tokenize metric expression with equal sign', function() {
+      var lexer = new Lexer('apps=test');
+      var tokens = lexer.tokenize();
+      expect(tokens[0].value).to.be('apps=test');
+    });
+
     it('simple function2', function() {
     it('simple function2', function() {
       var lexer = new Lexer('offset(test.metric, -100)');
       var lexer = new Lexer('offset(test.metric, -100)');
       var tokens = lexer.tokenize();
       var tokens = lexer.tokenize();