mirror of
https://github.com/grafana/grafana.git
synced 2025-08-03 00:42:03 +08:00
Merge branch 'master' into alerting_definitions
This commit is contained in:
@ -1,11 +1,16 @@
|
||||
# 3.0.0 stable (unreleased)
|
||||
|
||||
* **Templating**: Fixed issue with new data source variable not persisting current selected value, fixes [#4934](https://github.com/grafana/grafana/issues/4934)
|
||||
|
||||
# 3.0.0-beta7 (2016-05-02)
|
||||
|
||||
### Bug fixes
|
||||
* **Dashboard title**: Fixed max dashboard title width (media query) for large screens, fixes [#4859](https://github.com/grafana/grafana/issues/4859)
|
||||
* **Annotations**: Fixed issue with entering annotation edit view, fixes [#4857](https://github.com/grafana/grafana/issues/4857)
|
||||
* **Remove query**: Fixed issue with removing query for data sources without collapsable query editors, fixes [#4856](https://github.com/grafana/grafana/issues/4856)
|
||||
* **Graphite PNG*: Fixed issue graphite png rendering option, fixes [#4864](https://github.com/grafana/grafana/issues/4864)
|
||||
* **Graphite PNG**: Fixed issue graphite png rendering option, fixes [#4864](https://github.com/grafana/grafana/issues/4864)
|
||||
* **InfluxDB**: Fixed issue missing plus group by iconn, fixes [#4862](https://github.com/grafana/grafana/issues/4862)
|
||||
* **Graph**: Fixes missing line mode for thresholds, fixes [#4902](https://github.com/grafana/grafana/pull/4902)
|
||||
|
||||
### Enhancements
|
||||
* **InfluxDB**: Added new functions moving_average and difference to query editor, closes [#4698](https://github.com/grafana/grafana/issues/4698)
|
||||
|
24
README.md
24
README.md
@ -78,7 +78,7 @@ the latest master builds [here](http://grafana.org/download/builds)
|
||||
### Dependencies
|
||||
|
||||
- Go 1.5
|
||||
- NodeJS
|
||||
- NodeJS v0.12.0
|
||||
- [Godep](https://github.com/tools/godep)
|
||||
|
||||
### Get Code
|
||||
@ -87,8 +87,19 @@ the latest master builds [here](http://grafana.org/download/builds)
|
||||
go get github.com/grafana/grafana
|
||||
```
|
||||
|
||||
Since imports of dependencies use the absolute path github.com/grafana/grafana within the $GOPATH,
|
||||
you will need to put your version of the code in $GOPATH/src/github.com/grafana/grafana to be able
|
||||
to develop and build grafana on a cloned repository. To do so, you can clone your forked repository
|
||||
directly to $GOPATH/src/github.com/grafana or you can create a symbolic link from your version
|
||||
of the code to $GOPATH/src/github.com/grafana/grafana. The last options makes it possible to change
|
||||
easily the grafana repository you want to build.
|
||||
```bash
|
||||
go get github.com/*your_account*/grafana
|
||||
mkdir $GOPATH/src/github.com/grafana
|
||||
ln -s github.com/*your_account*/grafana $GOPATH/src/github.com/grafana/grafana
|
||||
```
|
||||
|
||||
### Building the backend
|
||||
Replace X.Y.Z by actual version number.
|
||||
```bash
|
||||
cd $GOPATH/src/github.com/grafana/grafana
|
||||
go run build.go setup (only needed once to install godep)
|
||||
@ -98,7 +109,7 @@ go run build.go build
|
||||
|
||||
### Building frontend assets
|
||||
|
||||
To build less to css for the frontend you will need a recent version of of node (v0.12.0),
|
||||
To build less to css for the frontend you will need a recent version of of **node (v0.12.0)**,
|
||||
npm (v2.5.0) and grunt (v0.4.5). Run the following:
|
||||
|
||||
```bash
|
||||
@ -106,6 +117,13 @@ npm install
|
||||
npm run build
|
||||
```
|
||||
|
||||
To build the frontend assets only on changes:
|
||||
|
||||
```bash
|
||||
sudo npm install -g grunt-cli # to do only once to install grunt command line interface
|
||||
grunt watch
|
||||
```
|
||||
|
||||
### Recompile backend on source change
|
||||
To rebuild on source change (requires that you executed godep restore)
|
||||
```bash
|
||||
|
2
build.go
2
build.go
@ -136,7 +136,7 @@ func readVersionFromPackageJson() {
|
||||
// add timestamp to iteration
|
||||
linuxPackageIteration = fmt.Sprintf("%s%v", linuxPackageIteration, time.Now().Unix())
|
||||
}
|
||||
log.Println(fmt.Sprintf("teration %v", linuxPackageIteration))
|
||||
log.Println(fmt.Sprintf("Iteration %v", linuxPackageIteration))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -241,14 +241,14 @@ templates_pattern = emails/*.html
|
||||
|
||||
#################################### Logging ##########################
|
||||
[log]
|
||||
# Either "console", "file", default is "console"
|
||||
# Either "console", "file", "syslog". Default is console and file
|
||||
# Use comma to separate multiple modes, e.g. "console, file"
|
||||
mode = console, file
|
||||
|
||||
# Buffer length of channel, keep it as it is if you don't know what it is.
|
||||
buffer_len = 10000
|
||||
|
||||
# Either "Trace", "Debug", "Info", "Warn", "Error", "Critical", default is "Trace"
|
||||
# Either "Trace", "Debug", "Info", "Warn", "Error", "Critical", default is "Info"
|
||||
level = Info
|
||||
|
||||
# For "console" mode only
|
||||
|
@ -223,14 +223,14 @@ check_for_updates = true
|
||||
|
||||
#################################### Logging ##########################
|
||||
[log]
|
||||
# Either "console", "file", default is "console"
|
||||
# Either "console", "file", "syslog". Default is console and file
|
||||
# Use comma to separate multiple modes, e.g. "console, file"
|
||||
;mode = console, file
|
||||
|
||||
# Buffer length of channel, keep it as it is if you don't know what it is.
|
||||
;buffer_len = 10000
|
||||
|
||||
# Either "Trace", "Debug", "Info", "Warn", "Error", "Critical", default is "Trace"
|
||||
# Either "Trace", "Debug", "Info", "Warn", "Error", "Critical", default is "Info"
|
||||
;level = Info
|
||||
|
||||
# For "console" mode only
|
||||
|
@ -30,7 +30,7 @@ Access | Proxy = access via Grafana backend, Direct = access directory from brow
|
||||
## Query editor
|
||||
Open a graph in edit mode by click the title.
|
||||
|
||||

|
||||

|
||||
|
||||
For details on KairosDB metric queries checkout the official.
|
||||
- [Query Metrics - KairosDB 0.9.4 documentation](http://kairosdb.github.io/kairosdocs/restapi/QueryMetrics.html).
|
||||
|
@ -86,7 +86,7 @@ func init() {
|
||||
"AWS/EC2": {"AutoScalingGroupName", "ImageId", "InstanceId", "InstanceType"},
|
||||
"AWS/ELB": {"LoadBalancerName", "AvailabilityZone"},
|
||||
"AWS/ElasticMapReduce": {"ClusterId", "JobFlowId", "JobId"},
|
||||
"AWS/ES": {},
|
||||
"AWS/ES": {"ClientId", "DomainName"},
|
||||
"AWS/Events": {"RuleName"},
|
||||
"AWS/Kinesis": {"StreamName", "ShardID"},
|
||||
"AWS/Lambda": {"FunctionName"},
|
||||
|
@ -29,6 +29,7 @@ type PluginListItem struct {
|
||||
Info *plugins.PluginInfo `json:"info"`
|
||||
LatestVersion string `json:"latestVersion"`
|
||||
HasUpdate bool `json:"hasUpdate"`
|
||||
DefaultNavUrl string `json:"defaultNavUrl"`
|
||||
}
|
||||
|
||||
type PluginList []PluginListItem
|
||||
|
@ -8,6 +8,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/middleware"
|
||||
m "github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
func GetPluginList(c *middleware.Context) Response {
|
||||
@ -46,6 +47,7 @@ func GetPluginList(c *middleware.Context) Response {
|
||||
Info: &pluginDef.Info,
|
||||
LatestVersion: pluginDef.GrafanaNetVersion,
|
||||
HasUpdate: pluginDef.GrafanaNetHasUpdate,
|
||||
DefaultNavUrl: pluginDef.DefaultNavUrl,
|
||||
}
|
||||
|
||||
if pluginSetting, exists := pluginSettingsMap[pluginDef.Id]; exists {
|
||||
@ -53,6 +55,10 @@ func GetPluginList(c *middleware.Context) Response {
|
||||
listItem.Pinned = pluginSetting.Pinned
|
||||
}
|
||||
|
||||
if listItem.DefaultNavUrl == "" || !listItem.Enabled {
|
||||
listItem.DefaultNavUrl = setting.AppSubUrl + "/plugins/" + listItem.Id + "/edit"
|
||||
}
|
||||
|
||||
// filter out disabled
|
||||
if enabledFilter == "1" && !listItem.Enabled {
|
||||
continue
|
||||
|
@ -31,7 +31,7 @@ func RenderToPng(c *middleware.Context) {
|
||||
Width: queryReader.Get("width", "800"),
|
||||
Height: queryReader.Get("height", "400"),
|
||||
SessionId: c.Session.ID(),
|
||||
Timeout: queryReader.Get("timeout", "15"),
|
||||
Timeout: queryReader.Get("timeout", "30"),
|
||||
}
|
||||
|
||||
renderOpts.Url = setting.ToAbsUrl(renderOpts.Url)
|
||||
|
@ -1,6 +1,7 @@
|
||||
package renderer
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
@ -72,6 +73,7 @@ func RenderToPng(params *RenderOpts) (string, error) {
|
||||
if err := cmd.Process.Kill(); err != nil {
|
||||
log.Error(4, "failed to kill: %v", err)
|
||||
}
|
||||
return "", fmt.Errorf("PhantomRenderer::renderToPng timeout (>%vs)", timeout)
|
||||
case <-done:
|
||||
}
|
||||
|
||||
|
@ -76,8 +76,6 @@ func (app *AppPlugin) initApp() {
|
||||
}
|
||||
}
|
||||
|
||||
app.DefaultNavUrl = setting.AppSubUrl + "/plugins/" + app.Id + "/edit"
|
||||
|
||||
// slugify pages
|
||||
for _, include := range app.Includes {
|
||||
if include.Slug == "" {
|
||||
|
@ -42,6 +42,8 @@ export class GrafanaApp {
|
||||
app.constant('grafanaVersion', "@grafanaVersion@");
|
||||
|
||||
app.config(($locationProvider, $controllerProvider, $compileProvider, $filterProvider, $provide) => {
|
||||
//$compileProvider.debugInfoEnabled(false);
|
||||
|
||||
this.registerFunctions.controller = $controllerProvider.register;
|
||||
this.registerFunctions.directive = $compileProvider.directive;
|
||||
this.registerFunctions.factory = $provide.factory;
|
||||
|
@ -86,7 +86,7 @@
|
||||
<button class="btn btn-inverse gf-from-btn" ng-click="_.move(dashboard.rows,$index,$index+1)">
|
||||
<i ng-class="{'invisible': $last}" class="fa fa-arrow-down"></i>
|
||||
</button>
|
||||
<button class="btn btn-inverse gf-form-btn" click="dashboard.rows = _.without(dashboard.rows,row)">
|
||||
<button class="btn btn-inverse gf-form-btn" ng-click="dashboard.rows = _.without(dashboard.rows,row)">
|
||||
<i class="fa fa-trash"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
@ -8,14 +8,16 @@
|
||||
|
||||
<p class="playlist-description">Each organization contains their own dashboards, data sources and configuration, and cannot be shared between orgs. While users may belong to more than one, mutiple organization are most frequently used in multi-tenant deployments. </p>
|
||||
|
||||
<div class="gf-form-group">
|
||||
<div class="gf-form">
|
||||
<span class="gf-form-label width-10">Org. name</span>
|
||||
<input type="text" ng-model="newOrg.name" required class="gf-form-input max-width-21" placeholder="organization name">
|
||||
<form>
|
||||
<div class="gf-form-group">
|
||||
<div class="gf-form">
|
||||
<span class="gf-form-label width-10">Org. name</span>
|
||||
<input type="text" ng-model="newOrg.name" required class="gf-form-input max-width-21" placeholder="organization name">
|
||||
</div>
|
||||
<br>
|
||||
<div class="gf-form-buttons-row">
|
||||
<button type="submit" class="btn btn-success" ng-click="createOrg()">Create</button>
|
||||
</div>
|
||||
</div>
|
||||
<br>
|
||||
<div class="gf-form-buttons-row">
|
||||
<button class="btn btn-success" ng-click="createOrg()">Create</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
@ -213,8 +213,7 @@ function (angular, _, kbn) {
|
||||
this.updateOptions = function(variable) {
|
||||
if (variable.type !== 'query') {
|
||||
self._updateNonQueryVariable(variable);
|
||||
self.setVariableValue(variable, variable.options[0]);
|
||||
return $q.when([]);
|
||||
return self.validateVariableSelectionState(variable);
|
||||
}
|
||||
|
||||
return datasourceSrv.get(variable.datasource)
|
||||
@ -251,7 +250,7 @@ function (angular, _, kbn) {
|
||||
if (_.isArray(variable.current.value)) {
|
||||
self.selectOptionsForCurrentValue(variable);
|
||||
} else {
|
||||
var currentOption = _.findWhere(variable.options, { text: variable.current.text });
|
||||
var currentOption = _.findWhere(variable.options, {text: variable.current.text});
|
||||
if (currentOption) {
|
||||
return self.setVariableValue(variable, currentOption, true);
|
||||
} else {
|
||||
|
@ -376,11 +376,16 @@ function (angular, _, dateMath) {
|
||||
|
||||
if (target.filters && target.filters.length > 0) {
|
||||
query.filters = angular.copy(target.filters);
|
||||
if(query.filters){
|
||||
for(var filter_key in query.filters){
|
||||
query.filters[filter_key].filter = templateSrv.replace(query.filters[filter_key].filter, options.scopedVars, 'pipe');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
query.tags = angular.copy(target.tags);
|
||||
if(query.tags){
|
||||
for(var key in query.tags){
|
||||
query.tags[key] = templateSrv.replace(query.tags[key], options.scopedVars, 'pipe');
|
||||
for(var tag_key in query.tags){
|
||||
query.tags[tag_key] = templateSrv.replace(query.tags[tag_key], options.scopedVars, 'pipe');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -46,7 +46,7 @@
|
||||
<h5 class="section-heading">Thresholds</h5>
|
||||
<div class="gf-form-inline">
|
||||
<div class="gf-form">
|
||||
<label class="gf-form-label width-5">Level 1</label>
|
||||
<label class="gf-form-label width-6">Level 1</label>
|
||||
<input type="number" class="gf-form-input max-width-5" ng-model="ctrl.panel.grid.threshold1" ng-change="ctrl.render()" ng-model-onblur>
|
||||
</div>
|
||||
<div class="gf-form">
|
||||
@ -58,7 +58,7 @@
|
||||
</div>
|
||||
<div class="gf-form-inline">
|
||||
<div class="gf-form">
|
||||
<label class="gf-form-label width-5">Level 2</label>
|
||||
<label class="gf-form-label width-6">Level 2</label>
|
||||
<input type="number" class="gf-form-input max-width-5" ng-model="ctrl.panel.grid.threshold2" ng-change="ctrl.render()" ng-model-onblur>
|
||||
</div>
|
||||
<div class="gf-form">
|
||||
@ -68,5 +68,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<gf-form-switch class="gf-form" label="Line Mode" label-class="width-6" checked="ctrl.panel.grid.thresholdLine" on-change="ctrl.render()"></gf-form-switch>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -4,7 +4,7 @@
|
||||
{{category.header}}
|
||||
</h6>
|
||||
<div class="pluginlist-item" ng-repeat="plugin in category.list">
|
||||
<a class="pluginlist-link pluginlist-link-{{plugin.state}} pointer" href="plugins/{{plugin.id}}/edit">
|
||||
<a class="pluginlist-link pluginlist-link-{{plugin.state}} pointer" href="{{plugin.defaultNavUrl}}">
|
||||
<span>
|
||||
<img ng-src="{{plugin.info.logos.small}}" class="pluginlist-image">
|
||||
<span class="pluginlist-title">{{plugin.name}}</span>
|
||||
@ -23,7 +23,7 @@
|
||||
</div>
|
||||
<div class="pluginlist-item" ng-show="category.list.length === 0">
|
||||
<a class="pluginlist-link pluginlist-link-{{plugin.state}}" href="https://grafana.net/plugins">
|
||||
<span class="pluginlist-none-installed">No additional panels installed. <span class="pluginlist-emphasis">Browse Grafana.net</span></span>
|
||||
<span class="pluginlist-none-installed">None installed. <span class="pluginlist-emphasis">Browse Grafana.net</span></span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -82,7 +82,7 @@ class SingleStatCtrl extends MetricsPanelCtrl {
|
||||
}
|
||||
|
||||
onDataError(err) {
|
||||
this.onDataReceived({data: []});
|
||||
this.onDataReceived([]);
|
||||
}
|
||||
|
||||
onDataReceived(dataList) {
|
||||
@ -91,11 +91,6 @@ class SingleStatCtrl extends MetricsPanelCtrl {
|
||||
var data: any = {};
|
||||
this.setValues(data);
|
||||
|
||||
data.thresholds = this.panel.thresholds.split(',').map(function(strVale) {
|
||||
return Number(strVale.trim());
|
||||
});
|
||||
|
||||
data.colorMap = this.panel.colors;
|
||||
this.data = data;
|
||||
this.render();
|
||||
}
|
||||
@ -324,9 +319,9 @@ class SingleStatCtrl extends MetricsPanelCtrl {
|
||||
? 'rgb(230,230,230)'
|
||||
: 'rgb(38,38,38)';
|
||||
|
||||
|
||||
var fontScale = parseInt(panel.valueFontSize) / 100;
|
||||
var dimension = Math.min(width, height);
|
||||
var fontSize = Math.min(dimension/4, 100);
|
||||
var fontSize = Math.min(dimension/5, 100) * fontScale;
|
||||
var gaugeWidth = Math.min(dimension/6, 60);
|
||||
var thresholdMarkersWidth = gaugeWidth/5;
|
||||
|
||||
@ -374,15 +369,6 @@ class SingleStatCtrl extends MetricsPanelCtrl {
|
||||
$.plot(plotCanvas, [plotSeries], options);
|
||||
}
|
||||
|
||||
function getGaugeFontSize() {
|
||||
if (panel.valueFontSize) {
|
||||
var num = parseInt(panel.valueFontSize.substring(0, panel.valueFontSize.length - 1));
|
||||
return (30 * (num / 100)) + 15;
|
||||
} else {
|
||||
return 30;
|
||||
}
|
||||
}
|
||||
|
||||
function addSparkline() {
|
||||
var width = elem.width() + 20;
|
||||
if (width < 30) {
|
||||
@ -444,8 +430,14 @@ class SingleStatCtrl extends MetricsPanelCtrl {
|
||||
|
||||
function render() {
|
||||
if (!ctrl.data) { return; }
|
||||
ctrl.setValues(ctrl.data);
|
||||
data = ctrl.data;
|
||||
|
||||
// get thresholds
|
||||
data.thresholds = panel.thresholds.split(',').map(function(strVale) {
|
||||
return Number(strVale.trim());
|
||||
});
|
||||
data.colorMap = panel.colors;
|
||||
|
||||
setElementHeight();
|
||||
|
||||
var body = panel.gauge.show ? '' : getBigValueHtml();
|
||||
|
@ -47,6 +47,7 @@ dashboard.templating = {
|
||||
refresh: true,
|
||||
options: [],
|
||||
current: null,
|
||||
type: 'custom'
|
||||
},
|
||||
{
|
||||
name: 'test2',
|
||||
@ -54,6 +55,7 @@ dashboard.templating = {
|
||||
refresh: true,
|
||||
options: [],
|
||||
current: null,
|
||||
type: 'custom'
|
||||
}
|
||||
]
|
||||
};
|
||||
|
@ -166,7 +166,7 @@ define([
|
||||
|
||||
describeUpdateVariable('update custom variable', function(scenario) {
|
||||
scenario.setup(function() {
|
||||
scenario.variable = { type: 'custom', query: 'hej, hop, asd', name: 'test'};
|
||||
scenario.variable = {type: 'custom', query: 'hej, hop, asd', name: 'test'};
|
||||
});
|
||||
|
||||
it('should update options array', function() {
|
||||
@ -286,7 +286,13 @@ define([
|
||||
|
||||
describeUpdateVariable('datasource variable with regex filter', function(scenario) {
|
||||
scenario.setup(function() {
|
||||
scenario.variable = {type: 'datasource', query: 'graphite', name: 'test', current: {}, regex: '/pee$/' };
|
||||
scenario.variable = {
|
||||
type: 'datasource',
|
||||
query: 'graphite',
|
||||
name: 'test',
|
||||
current: {value: 'backend4_pee', text: 'backend4_pee'},
|
||||
regex: '/pee$/'
|
||||
};
|
||||
scenario.metricSources = [
|
||||
{name: 'backend1', meta: {id: 'influx'}},
|
||||
{name: 'backend2_pee', meta: {id: 'graphite'}},
|
||||
@ -300,6 +306,10 @@ define([
|
||||
expect(scenario.variable.options[0].value).to.be('backend2_pee');
|
||||
expect(scenario.variable.options[1].value).to.be('backend4_pee');
|
||||
});
|
||||
|
||||
it('should keep current value if available', function() {
|
||||
expect(scenario.variable.current.value).to.be('backend4_pee');
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
@ -15,8 +15,6 @@ module.exports = function(config,grunt) {
|
||||
src = confDir+src;
|
||||
}
|
||||
|
||||
var exec = require('child_process').execFileSync;
|
||||
|
||||
try {
|
||||
grunt.config('copy.phantom_bin', {
|
||||
src: src,
|
||||
|
Reference in New Issue
Block a user