Merge remote-tracking branch 'upstream/master'
This commit is contained in:
@@ -41,7 +41,9 @@ Proxy access means that the Grafana backend will proxy all requests from the bro
|
||||
Click the ``Select metric`` link to start navigating the metric space. One you start you can continue using the mouse
|
||||
or keyboard arrow keys. You can select a wildcard and still continue.
|
||||
|
||||

|
||||
{{< docs-imagebox img="/img/docs/v45/graphite_query1_still.png" class="docs-image--center"
|
||||
animated-gif="/img/docs/v45/graphite_query1.gif" >}}
|
||||
|
||||
|
||||
### Functions
|
||||
|
||||
@@ -50,13 +52,17 @@ a function is selected it will be added and your focus will be in the text box o
|
||||
a parameter just click on it and it will turn into a text box. To delete a function click the function name followed
|
||||
by the x icon.
|
||||
|
||||

|
||||
{{< docs-imagebox img="/img/docs/v45/graphite_query2_still.png" class="docs-image--center"
|
||||
animated-gif="/img/docs/v45/graphite_query2.gif" >}}
|
||||
|
||||
|
||||
### Optional parameters
|
||||
|
||||
Some functions like aliasByNode support an optional second argument. To add this parameter specify for example 3,-2 as the first parameter and the function editor will adapt and move the -2 to a second parameter. To remove the second optional parameter just click on it and leave it blank and the editor will remove it.
|
||||
|
||||

|
||||
{{< docs-imagebox img="/img/docs/v45/graphite_query3_still.png" class="docs-image--center"
|
||||
animated-gif="/img/docs/v45/graphite_query3.gif" >}}
|
||||
|
||||
|
||||
### Nested Queries
|
||||
|
||||
|
||||
@@ -41,7 +41,9 @@ mode is also more secure as the username & password will never reach the browser
|
||||
|
||||
## Query Editor
|
||||
|
||||

|
||||
{{< docs-imagebox img="/img/docs/v45/influxdb_query_still.png" class="docs-image--center"
|
||||
animated-gif="/img/docs/v45/influxdb_query.gif" >}}
|
||||
|
||||
|
||||
You find the InfluxDB editor in the metrics tab in Graph or Singlestat panel's edit mode. You enter edit mode by clicking the
|
||||
panel title, then edit. The editor allows you to select metrics and tags.
|
||||
|
||||
@@ -50,15 +50,11 @@ populate the template variable to a desired value from the link.
|
||||
The metrics tab defines what series data and sources to render. Each datasource provides different
|
||||
options.
|
||||
|
||||
## Axes & Grid
|
||||
## Axes
|
||||
|
||||

|
||||
|
||||
The Axes & Grid tab controls the display of axes, grids and legend.
|
||||
|
||||
### Axes
|
||||
|
||||
The ``Left Y`` and ``Right Y`` can be customized using:
|
||||
The Axes tab controls the display of axes, grids and legend. The ``Left Y`` and ``Right Y`` can be customized using:
|
||||
|
||||
- ``Unit`` - The display unit for the Y value
|
||||
- ``Grid Max`` - The maximum Y value. (default auto)
|
||||
|
||||
@@ -27,7 +27,7 @@ this folder to anywhere you want Grafana to run from. Go into the
|
||||
|
||||
The default Grafana port is `3000`, this port requires extra permissions
|
||||
on windows. Edit `custom.ini` and uncomment the `http_port`
|
||||
configuration option and change it to something like `8080` or similar.
|
||||
configuration option (`;` is the comment character in ini files) and change it to something like `8080` or similar.
|
||||
That port should not require extra Windows privileges.
|
||||
|
||||
Start Grafana by executing `grafana-server.exe`, preferably from the
|
||||
|
||||
@@ -43,3 +43,25 @@ Playlists can also be manually controlled utilizing the Playlist controls at the
|
||||
Click the stop button to stop the Playlist, and exit to the current Dashboard.
|
||||
Click the next button to advance to the next Dashboard in the Playlist.
|
||||
Click the back button to rewind to the previous Dashboard in the Playlist.
|
||||
|
||||
## TV or Kiosk Mode
|
||||
|
||||
In TV mode the top navbar, row & panel controls will all fade to transparent.
|
||||
|
||||
This happens automatically after one minute of user inactivity but can also be toggled manually
|
||||
with the `d v` sequence shortcut. Any mouse movement or keyboard action will
|
||||
restore navbar & controls.
|
||||
|
||||
Another feature is the kiosk mode - in kiosk mode the navbar is completely hidden/removed from view. This can be enabled with the `d k`
|
||||
shortcut.
|
||||
|
||||
To put a playlist into kiosk mode, use the `d k` shortcut after the playlist has started. The same shortcut will toggle the playlist out of kiosk mode.
|
||||
|
||||
### Linking to the Playlist in Kiosk Mode
|
||||
|
||||
If you want to create a link to the playlist with kiosk mode enabled:
|
||||
|
||||
1. Copy the Start Url (by right clicking on the Play button and choosing Copy link address).
|
||||
2. Add the `?kiosk` parameter to the url.
|
||||
|
||||
For example, to open the first playlist on the Grafana Play site in kiosk mode: [http://play.grafana.org/playlists/play/1?kiosk](http://play.grafana.org/playlists/play/1?kiosk)
|
||||
|
||||
@@ -53,10 +53,6 @@
|
||||
"systemjs": "0.19.41",
|
||||
"zone.js": "^0.7.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": "4.x",
|
||||
"npm": "2.14.x"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "./node_modules/grunt-cli/bin/grunt",
|
||||
"test": "./node_modules/grunt-cli/bin/grunt test",
|
||||
|
||||
@@ -78,6 +78,7 @@ func init() {
|
||||
"AWS/Lambda": {"Invocations", "Errors", "Duration", "Throttles", "IteratorAge"},
|
||||
"AWS/Logs": {"IncomingBytes", "IncomingLogEvents", "ForwardedBytes", "ForwardedLogEvents", "DeliveryErrors", "DeliveryThrottling"},
|
||||
"AWS/ML": {"PredictCount", "PredictFailureCount"},
|
||||
"AWS/NATGateway": {"PacketsOutToDestination", "PacketsOutToSource", "PacketsInFromSource", "PacketsInFromDestination", "BytesOutToDestination", "BytesOutToSource", "BytesInFromSource", "BytesInFromDestination", "ErrorPortAllocation", "ActiveConnectionCount", "ConnectionAttemptCount", "ConnectionEstablishedCount", "IdleTimeoutCount", "PacketsDropCount"},
|
||||
"AWS/OpsWorks": {"cpu_idle", "cpu_nice", "cpu_system", "cpu_user", "cpu_waitio", "load_1", "load_5", "load_15", "memory_buffers", "memory_cached", "memory_free", "memory_swap", "memory_total", "memory_used", "procs"},
|
||||
"AWS/Redshift": {"CPUUtilization", "DatabaseConnections", "HealthStatus", "MaintenanceMode", "NetworkReceiveThroughput", "NetworkTransmitThroughput", "PercentageDiskSpaceUsed", "ReadIOPS", "ReadLatency", "ReadThroughput", "WriteIOPS", "WriteLatency", "WriteThroughput"},
|
||||
"AWS/RDS": {"ActiveTransactions", "AuroraBinlogReplicaLag", "AuroraReplicaLag", "AuroraReplicaLagMaximum", "AuroraReplicaLagMinimum", "BinLogDiskUsage", "BlockedTransactions", "BufferCacheHitRatio", "CommitLatency", "CommitThroughput", "BinLogDiskUsage", "CPUCreditBalance", "CPUCreditUsage", "CPUUtilization", "DatabaseConnections", "DDLLatency", "DDLThroughput", "Deadlocks", "DeleteLatency", "DeleteThroughput", "DiskQueueDepth", "DMLLatency", "DMLThroughput", "EngineUptime", "FailedSqlStatements", "FreeableMemory", "FreeLocalStorage", "FreeStorageSpace", "InsertLatency", "InsertThroughput", "LoginFailures", "NetworkReceiveThroughput", "NetworkTransmitThroughput", "NetworkThroughput", "Queries", "ReadIOPS", "ReadLatency", "ReadThroughput", "ReplicaLag", "ResultSetCacheHitRatio", "SelectLatency", "SelectThroughput", "SwapUsage", "TotalConnections", "UpdateLatency", "UpdateThroughput", "VolumeBytesUsed", "VolumeReadIOPS", "VolumeWriteIOPS", "WriteIOPS", "WriteLatency", "WriteThroughput"},
|
||||
@@ -122,6 +123,7 @@ func init() {
|
||||
"AWS/Lambda": {"FunctionName", "Resource", "Version", "Alias"},
|
||||
"AWS/Logs": {"LogGroupName", "DestinationType", "FilterName"},
|
||||
"AWS/ML": {"MLModelId", "RequestMode"},
|
||||
"AWS/NATGateway": {"NatGatewayId"},
|
||||
"AWS/OpsWorks": {"StackId", "LayerId", "InstanceId"},
|
||||
"AWS/Redshift": {"NodeID", "ClusterIdentifier"},
|
||||
"AWS/RDS": {"DBInstanceIdentifier", "DBClusterIdentifier", "DatabaseClass", "EngineName", "Role"},
|
||||
|
||||
+1
-1
@@ -29,7 +29,7 @@ func QueryMetrics(c *middleware.Context, reqDto dtos.MetricRequest) Response {
|
||||
return ApiError(400, "Query missing datasourceId", nil)
|
||||
}
|
||||
|
||||
dsQuery := models.GetDataSourceByIdQuery{Id: dsId}
|
||||
dsQuery := models.GetDataSourceByIdQuery{Id: dsId, OrgId: c.OrgId}
|
||||
if err := bus.Dispatch(&dsQuery); err != nil {
|
||||
return ApiError(500, "failed to fetch data source", err)
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
gocontext "context"
|
||||
|
||||
"github.com/grafana/grafana/pkg/bus"
|
||||
"github.com/grafana/grafana/pkg/components/null"
|
||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||
@@ -112,6 +114,10 @@ func (c *QueryCondition) executeQuery(context *alerting.EvalContext, timeRange *
|
||||
|
||||
resp, err := c.HandleRequest(context.Ctx, req)
|
||||
if err != nil {
|
||||
if err == gocontext.DeadlineExceeded {
|
||||
return nil, fmt.Errorf("Alert execution exceeded the timeout")
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("tsdb.HandleRequest() error %v", err)
|
||||
}
|
||||
|
||||
|
||||
@@ -27,12 +27,13 @@ func Decrypt(payload []byte, secret string) ([]byte, error) {
|
||||
}
|
||||
iv := payload[saltLength : saltLength+aes.BlockSize]
|
||||
payload = payload[saltLength+aes.BlockSize:]
|
||||
payloadDst := make([]byte, len(payload))
|
||||
|
||||
stream := cipher.NewCFBDecrypter(block, iv)
|
||||
|
||||
// XORKeyStream can work in-place if the two arguments are the same.
|
||||
stream.XORKeyStream(payload, payload)
|
||||
return payload, nil
|
||||
stream.XORKeyStream(payloadDst, payload)
|
||||
return payloadDst, nil
|
||||
}
|
||||
|
||||
func Encrypt(payload []byte, secret string) ([]byte, error) {
|
||||
|
||||
@@ -111,6 +111,17 @@ function link(scope, elem, attrs) {
|
||||
let textarea = elem.find("textarea");
|
||||
textarea.addClass('gf-form-input');
|
||||
|
||||
if (scope.codeEditorFocus) {
|
||||
setTimeout(function () {
|
||||
textarea.focus();
|
||||
var domEl = textarea[0];
|
||||
if (domEl.setSelectionRange) {
|
||||
var pos = textarea.val().length * 2;
|
||||
domEl.setSelectionRange(pos, pos);
|
||||
}
|
||||
}, 100);
|
||||
}
|
||||
|
||||
// Event handlers
|
||||
editorSession.on('change', (e) => {
|
||||
scope.$apply(() => {
|
||||
@@ -199,6 +210,7 @@ export function codeEditorDirective() {
|
||||
template: editorTemplate,
|
||||
scope: {
|
||||
content: "=",
|
||||
codeEditorFocus: "<",
|
||||
onChange: "&",
|
||||
getCompleter: "&"
|
||||
},
|
||||
|
||||
@@ -65,20 +65,20 @@ var PrometheusHighlightRules = function() {
|
||||
regex : "\\s+"
|
||||
} ],
|
||||
"start-label-matcher" : [ {
|
||||
token : "label.name",
|
||||
token : "keyword",
|
||||
regex : '[a-zA-Z_][a-zA-Z0-9_]*'
|
||||
}, {
|
||||
token : "label.matching_operator",
|
||||
regex : '=|!=|=~|!~'
|
||||
token : "keyword.operator",
|
||||
regex : '=~|=|!~|!='
|
||||
}, {
|
||||
token : "label.value",
|
||||
token : "string",
|
||||
regex : '"[^"]*"|\'[^\']*\''
|
||||
}, {
|
||||
token : "label.matching_delimiter",
|
||||
token : "punctuation.operator",
|
||||
regex : ",",
|
||||
push : 'start-label-matcher'
|
||||
}, {
|
||||
token : "label.matching_end",
|
||||
token : "paren.rparen",
|
||||
regex : "}",
|
||||
next : "start"
|
||||
} ]
|
||||
|
||||
@@ -16,7 +16,7 @@ export class AlertSrv {
|
||||
|
||||
init() {
|
||||
this.$rootScope.onAppEvent('alert-error', (e, alert) => {
|
||||
this.set(alert[0], alert[1], 'error', 7000);
|
||||
this.set(alert[0], alert[1], 'error', 12000);
|
||||
}, this.$rootScope);
|
||||
|
||||
this.$rootScope.onAppEvent('alert-warning', (e, alert) => {
|
||||
@@ -33,6 +33,14 @@ export class AlertSrv {
|
||||
appEvents.on('confirm-modal', this.showConfirmModal.bind(this));
|
||||
}
|
||||
|
||||
getIconForSeverity(severity) {
|
||||
switch (severity) {
|
||||
case 'success': return 'fa fa-check';
|
||||
case 'error': return 'fa fa-exclamation-triangle';
|
||||
default: return 'fa fa-exclamation';
|
||||
}
|
||||
}
|
||||
|
||||
set(title, text, severity, timeout) {
|
||||
if (_.isObject(text)) {
|
||||
console.log('alert error', text);
|
||||
@@ -45,6 +53,7 @@ export class AlertSrv {
|
||||
title: title || '',
|
||||
text: text || '',
|
||||
severity: severity || 'info',
|
||||
icon: this.getIconForSeverity(severity)
|
||||
};
|
||||
|
||||
var newAlertJson = angular.toJson(newAlert);
|
||||
|
||||
@@ -64,7 +64,13 @@ export class BackendSrv {
|
||||
}
|
||||
|
||||
if (data.message) {
|
||||
this.alertSrv.set("Problem!", data.message, data.severity, 10000);
|
||||
let description = "";
|
||||
let message = data.message;
|
||||
if (message.length > 80) {
|
||||
description = message;
|
||||
message = "Error";
|
||||
}
|
||||
this.alertSrv.set(message, description, data.severity, 10000);
|
||||
}
|
||||
|
||||
throw data;
|
||||
@@ -97,7 +103,7 @@ export class BackendSrv {
|
||||
return results.data;
|
||||
}, err => {
|
||||
// handle unauthorized
|
||||
if (err.status === 401 && firstAttempt) {
|
||||
if (err.status === 401 && this.contextSrv.user.isSignedIn && firstAttempt) {
|
||||
return this.loginPing().then(() => {
|
||||
options.retry = 1;
|
||||
return this.request(options);
|
||||
|
||||
@@ -88,19 +88,16 @@ function($, _) {
|
||||
// 9h
|
||||
case (interval < 32400000):
|
||||
return 21600000; // 6h
|
||||
// 24h
|
||||
// 1d
|
||||
case (interval < 86400000):
|
||||
return 43200000; // 12h
|
||||
// 48h
|
||||
case (interval < 172800000):
|
||||
return 86400000; // 24h
|
||||
// 1w
|
||||
case (interval < 604800000):
|
||||
return 86400000; // 24h
|
||||
return 86400000; // 1d
|
||||
// 3w
|
||||
case (interval < 1814400000):
|
||||
return 604800000; // 1w
|
||||
// 2y
|
||||
// 6w
|
||||
case (interval < 3628800000):
|
||||
return 2592000000; // 30d
|
||||
default:
|
||||
|
||||
@@ -83,7 +83,7 @@ export class DashboardSrv {
|
||||
}
|
||||
|
||||
this.$rootScope.appEvent('dashboard-saved', this.dash);
|
||||
this.$rootScope.appEvent('alert-success', ['Dashboard saved', 'Saved as ' + clone.title]);
|
||||
this.$rootScope.appEvent('alert-success', ['Dashboard saved']);
|
||||
}
|
||||
|
||||
save(clone, options) {
|
||||
|
||||
@@ -10,9 +10,11 @@ export class QueryCtrl {
|
||||
panel: any;
|
||||
hasRawMode: boolean;
|
||||
error: string;
|
||||
isLastQuery: boolean;
|
||||
|
||||
constructor(public $scope, private $injector) {
|
||||
this.panel = this.panelCtrl.panel;
|
||||
this.isLastQuery = _.indexOf(this.panel.targets, this.target) === (this.panel.targets.length - 1);
|
||||
}
|
||||
|
||||
refresh() {
|
||||
|
||||
@@ -126,21 +126,18 @@ export class DataSourceEditCtrl {
|
||||
return;
|
||||
}
|
||||
|
||||
this.testing = {done: false};
|
||||
this.testing = {done: false, status: 'error'};
|
||||
|
||||
// make test call in no backend cache context
|
||||
this.backendSrv.withNoBackendCache(() => {
|
||||
return datasource.testDatasource().then(result => {
|
||||
this.testing.message = result.message;
|
||||
this.testing.status = result.status;
|
||||
this.testing.title = result.title;
|
||||
}).catch(err => {
|
||||
if (err.statusText) {
|
||||
this.testing.message = err.statusText;
|
||||
this.testing.title = "HTTP Error";
|
||||
this.testing.message = 'HTTP Error ' + err.statusText;
|
||||
} else {
|
||||
this.testing.message = err.message;
|
||||
this.testing.title = "Unknown error";
|
||||
}
|
||||
});
|
||||
}).finally(() => {
|
||||
|
||||
@@ -59,9 +59,14 @@
|
||||
|
||||
<div ng-if="ctrl.testing" class="gf-form-group">
|
||||
<h5 ng-show="!ctrl.testing.done">Testing.... <i class="fa fa-spiner fa-spin"></i></h5>
|
||||
<div class="alert-{{ctrl.testing.status}} alert">
|
||||
<div class="alert-title">{{ctrl.testing.title}}</div>
|
||||
<div ng-bind='ctrl.testing.message'></div>
|
||||
<div class="alert-{{ctrl.testing.status}} alert" ng-show="ctrl.testing.done">
|
||||
<div class="alert-icon">
|
||||
<i class="fa fa-exclamation-triangle" ng-show="ctrl.testing.status === 'error'"></i>
|
||||
<i class="fa fa-check" ng-show="ctrl.testing.status !== 'error'"></i>
|
||||
</div>
|
||||
<div class="alert-body">
|
||||
<div class="alert-title">{{ctrl.testing.message}}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -335,7 +335,7 @@ function (angular, _, moment, dateMath, kbn, templatingVariable, CloudWatchAnnot
|
||||
var dimensions = {};
|
||||
|
||||
return this.getDimensionValues(region, namespace, metricName, 'ServiceName', dimensions).then(function () {
|
||||
return { status: 'success', message: 'Data source is working', title: 'Success' };
|
||||
return { status: 'success', message: 'Data source is working' };
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -175,9 +175,9 @@ function (angular, _, moment, kbn, ElasticQueryBuilder, IndexPattern, ElasticRes
|
||||
return this.getFields({type: 'date'}).then(function(dateFields) {
|
||||
var timeField = _.find(dateFields, {text: this.timeField});
|
||||
if (!timeField) {
|
||||
return { status: "error", message: "No date field named " + this.timeField + ' found', title: "Error" };
|
||||
return { status: "error", message: "No date field named " + this.timeField + ' found' };
|
||||
}
|
||||
return { status: "success", message: "Index OK. Time field name OK.", title: "Success" };
|
||||
return { status: "success", message: "Index OK. Time field name OK." };
|
||||
}.bind(this), function(err) {
|
||||
console.log(err);
|
||||
if (err.data && err.data.error) {
|
||||
@@ -185,9 +185,9 @@ function (angular, _, moment, kbn, ElasticQueryBuilder, IndexPattern, ElasticRes
|
||||
if (err.data.error.reason) {
|
||||
message = err.data.error.reason;
|
||||
}
|
||||
return { status: "error", message: message, title: "Error" };
|
||||
return { status: "error", message: message };
|
||||
} else {
|
||||
return { status: "error", message: err.status, title: "Error" };
|
||||
return { status: "error", message: err.status };
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
@@ -205,7 +205,7 @@ export function GraphiteDatasource(instanceSettings, $q, backendSrv, templateSrv
|
||||
|
||||
this.testDatasource = function() {
|
||||
return this.metricFindQuery('*').then(function () {
|
||||
return { status: "success", message: "Data source is working", title: "Success" };
|
||||
return { status: "success", message: "Data source is working"};
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -24,6 +24,11 @@ describe('GraphiteQueryCtrl', function() {
|
||||
ctx.target = {target: 'aliasByNode(scaleToSeconds(test.prod.*,1),2)'};
|
||||
ctx.datasource.metricFindQuery = sinon.stub().returns(ctx.$q.when([]));
|
||||
ctx.panelCtrl = {panel: {}};
|
||||
ctx.panelCtrl = {
|
||||
panel: {
|
||||
targets: [ctx.target]
|
||||
}
|
||||
};
|
||||
ctx.panelCtrl.refresh = sinon.spy();
|
||||
|
||||
ctx.ctrl = $controller(GraphiteQueryCtrl, {$scope: ctx.scope}, {
|
||||
|
||||
@@ -196,11 +196,11 @@ export default class InfluxDatasource {
|
||||
return this.metricFindQuery('SHOW DATABASES').then(res => {
|
||||
let found = _.find(res, {text: this.database});
|
||||
if (!found) {
|
||||
return { status: "error", message: "Could not find the specified database name.", title: "DB Not found" };
|
||||
return { status: "error", message: "Could not find the specified database name." };
|
||||
}
|
||||
return { status: "success", message: "Data source is working", title: "Success" };
|
||||
return { status: "success", message: "Data source is working" };
|
||||
}).catch(err => {
|
||||
return { status: "error", message: err.message, title: "Test Failed" };
|
||||
return { status: "error", message: err.message };
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -92,7 +92,7 @@
|
||||
<div class="gf-form-inline" ng-if="ctrl.target.orderByTime === 'DESC'">
|
||||
<div class="gf-form">
|
||||
<label class="gf-form-label query-keyword width-7">ORDER BY</label>
|
||||
<label class="gf-form-label pointer" ng-click="ctrl.removeOrderByTime()">time <span classs="query-keyword">DESC</span> <i class="fa fa-remove"></i></label>
|
||||
<label class="gf-form-label pointer" ng-click="ctrl.removeOrderByTime()">time <span class="query-keyword">DESC</span> <i class="fa fa-remove"></i></label>
|
||||
</div>
|
||||
<div class="gf-form gf-form--grow">
|
||||
<div class="gf-form-label gf-form-label--grow"></div>
|
||||
|
||||
@@ -19,9 +19,13 @@ describe('InfluxDBQueryCtrl', function() {
|
||||
ctx.$q = $q;
|
||||
ctx.scope = $rootScope.$new();
|
||||
ctx.datasource.metricFindQuery = sinon.stub().returns(ctx.$q.when([]));
|
||||
ctx.panelCtrl = {panel: {}};
|
||||
ctx.panelCtrl.refresh = sinon.spy();
|
||||
ctx.target = {target: {}};
|
||||
ctx.panelCtrl = {
|
||||
panel: {
|
||||
targets: [ctx.target]
|
||||
}
|
||||
};
|
||||
ctx.panelCtrl.refresh = sinon.spy();
|
||||
ctx.ctrl = $controller(InfluxQueryCtrl, {$scope: ctx.scope}, {
|
||||
panelCtrl: ctx.panelCtrl,
|
||||
target: ctx.target,
|
||||
|
||||
@@ -118,13 +118,13 @@ export class MysqlDatasource {
|
||||
}],
|
||||
}
|
||||
}).then(res => {
|
||||
return { status: "success", message: "Database Connection OK", title: "Success" };
|
||||
return { status: "success", message: "Database Connection OK"};
|
||||
}).catch(err => {
|
||||
console.log(err);
|
||||
if (err.data && err.data.message) {
|
||||
return { status: "error", message: err.data.message, title: "Error" };
|
||||
return { status: "error", message: err.data.message };
|
||||
} else {
|
||||
return { status: "error", message: err.status, title: "Error" };
|
||||
return { status: "error", message: err.status };
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -296,7 +296,7 @@ function (angular, _, dateMath) {
|
||||
|
||||
this.testDatasource = function() {
|
||||
return this._performSuggestQuery('cpu', 'metrics').then(function () {
|
||||
return { status: "success", message: "Data source is working", title: "Success" };
|
||||
return { status: "success", message: "Data source is working" };
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -18,7 +18,11 @@ describe('OpenTsQueryCtrl', function() {
|
||||
ctx.$q = $q;
|
||||
ctx.scope = $rootScope.$new();
|
||||
ctx.target = {target: ''};
|
||||
ctx.panelCtrl = {panel: {}};
|
||||
ctx.panelCtrl = {
|
||||
panel: {
|
||||
targets: [ctx.target]
|
||||
}
|
||||
};
|
||||
ctx.panelCtrl.refresh = sinon.spy();
|
||||
ctx.datasource.getAggregators = sinon.stub().returns(ctx.$q.when([]));
|
||||
ctx.datasource.getFilterTypes = sinon.stub().returns(ctx.$q.when([]));
|
||||
|
||||
@@ -241,7 +241,7 @@ export class PrometheusDatasource {
|
||||
|
||||
testDatasource() {
|
||||
return this.metricFindQuery('metrics(.*)').then(function() {
|
||||
return { status: 'success', message: 'Data source is working', title: 'Success' };
|
||||
return { status: 'success', message: 'Data source is working'};
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div class="gf-form-inline">
|
||||
<div class="gf-form gf-form--grow">
|
||||
<code-editor content="ctrl.target.expr" on-change="ctrl.refreshMetricData()"
|
||||
get-completer="ctrl.getCompleter()" data-mode="prometheus">
|
||||
get-completer="ctrl.getCompleter()" data-mode="prometheus" code-editor-focus="ctrl.isLastQuery">
|
||||
</code-editor>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
import {describe, beforeEach, it, sinon, expect} from 'test/lib/common';
|
||||
|
||||
import {PromCompleter} from '../completer';
|
||||
import {PrometheusDatasource} from '../datasource';
|
||||
|
||||
describe('Prometheus editor completer', function() {
|
||||
|
||||
let editor = {};
|
||||
let session = {
|
||||
getTokenAt: sinon.stub().returns({}),
|
||||
getLine: sinon.stub().returns(""),
|
||||
};
|
||||
|
||||
let datasourceStub = <PrometheusDatasource>{};
|
||||
let completer = new PromCompleter(datasourceStub);
|
||||
|
||||
describe("When inside brackets", () => {
|
||||
|
||||
it("Should return range vectors", () => {
|
||||
completer.getCompletions(editor, session, 10, "[", (s, res) => {
|
||||
expect(res[0]).to.eql({caption: '1s', value: '[1s', meta: 'range vector'});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
@@ -3,7 +3,7 @@
|
||||
<div class="gf-form">
|
||||
<span class="gf-form-label">Mode</span>
|
||||
<span class="gf-form-select-wrapper">
|
||||
<select class="gf-form-input" ng-model="ctrl.panel.mode" ng-options="f for f in ['html','markdown','text']"></select>
|
||||
<select class="gf-form-input" ng-model="ctrl.panel.mode" ng-options="f for f in ['html','markdown']"></select>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -103,7 +103,7 @@ $tight-form-func-bg: #333;
|
||||
$tight-form-func-highlight-bg: #444;
|
||||
|
||||
$modal-background: $black;
|
||||
$code-tag-bg: $dark-5;
|
||||
$code-tag-bg: $gray-1;
|
||||
$code-tag-border: lighten($code-tag-bg, 2%);
|
||||
|
||||
|
||||
@@ -238,17 +238,15 @@ $paginationActiveBackground: $blue;
|
||||
|
||||
// Form states and alerts
|
||||
// -------------------------
|
||||
$state-warning-text: $warn;
|
||||
$state-warning-bg: $brand-warning;
|
||||
$warning-text-color: $warn;
|
||||
$error-text-color: #E84D4D;
|
||||
$success-text-color: #12D95A;
|
||||
$info-text-color: $blue-dark;
|
||||
|
||||
$errorText: #E84D4D;
|
||||
$errorBackground: $btn-danger-bg;
|
||||
|
||||
$successText: #12D95A;
|
||||
$successBackground: $btn-success-bg;
|
||||
|
||||
$infoText: $blue-dark;
|
||||
$infoBackground: $blue-dark;
|
||||
$alert-error-bg: linear-gradient(90deg, #d44939, #e0603d);
|
||||
$alert-success-bg: linear-gradient(90deg, #3aa655, #47b274);
|
||||
$alert-warning-bg: linear-gradient(90deg, #d44939, #e0603d);
|
||||
$alert-info-bg: linear-gradient(100deg, #1a4552, #00374a);
|
||||
|
||||
// popover
|
||||
$popover-bg: $panel-bg;
|
||||
@@ -258,6 +256,8 @@ $popover-border-color: $gray-1;
|
||||
$popover-help-bg: $btn-secondary-bg;
|
||||
$popover-help-color: $text-color;
|
||||
|
||||
$popover-error-bg: $btn-danger-bg;
|
||||
|
||||
// Tooltips and popovers
|
||||
// -------------------------
|
||||
$tooltipColor: $popover-help-color;
|
||||
@@ -276,7 +276,7 @@ $card-background-hover: linear-gradient(135deg, #343434, #262626);
|
||||
$card-shadow: -1px -1px 0 0 hsla(0, 0%, 100%, .1), 1px 1px 0 0 rgba(0, 0, 0, .3);
|
||||
|
||||
// info box
|
||||
$info-box-background: linear-gradient(100deg, #1a4552, #0b2127);
|
||||
$info-box-background: linear-gradient(100deg, #1a4552, #00374a);
|
||||
|
||||
// footer
|
||||
$footer-link-color: $gray-1;
|
||||
|
||||
@@ -259,29 +259,23 @@ $paginationActiveBackground: $blue;
|
||||
|
||||
// Form states and alerts
|
||||
// -------------------------
|
||||
$state-warning-text: lighten($orange, 10%);
|
||||
$state-warning-bg: $orange;
|
||||
$warningBorder: transparent;
|
||||
$warning-text-color: lighten($orange, 10%);
|
||||
$error-text-color: lighten($red, 10%);
|
||||
$success-text-color: lighten($green, 10%);
|
||||
$info-text-color: $blue;
|
||||
|
||||
$errorText: lighten($red, 10%);
|
||||
$errorBackground: $red;
|
||||
$errorBorder: transparent;
|
||||
|
||||
$successText: lighten($green, 10%);
|
||||
$successBackground: $green;
|
||||
$successBorder: transparent;
|
||||
|
||||
$infoText: $blue;
|
||||
$infoBackground: $blue-dark;
|
||||
$infoBorder: transparent;
|
||||
$alert-error-bg: linear-gradient(90deg, #d44939, #e0603d);
|
||||
$alert-success-bg: linear-gradient(90deg, #3aa655, #47b274);
|
||||
$alert-warning-bg: linear-gradient(90deg, #d44939, #e0603d);
|
||||
$alert-info-bg: $blue-dark;
|
||||
|
||||
// popover
|
||||
$popover-bg: $gray-5;
|
||||
$popover-color: $text-color;
|
||||
$popover-border-color: $gray-3;
|
||||
|
||||
$popover-help-bg: $blue-dark;
|
||||
$popover-help-color: $gray-6;
|
||||
$popover-error-bg: $btn-danger-bg;
|
||||
|
||||
// Tooltips and popovers
|
||||
// -------------------------
|
||||
|
||||
@@ -31,21 +31,21 @@ cite { font-style: normal; }
|
||||
a.muted:hover,
|
||||
a.muted:focus { color: darken($text-muted, 10%); }
|
||||
|
||||
.text-warning { color: $state-warning-text; }
|
||||
.text-warning { color: $warning-text-color; }
|
||||
a.text-warning:hover,
|
||||
a.text-warning:focus { color: darken($state-warning-text, 10%); }
|
||||
a.text-warning:focus { color: darken($warning-text-color, 10%); }
|
||||
|
||||
.text-error { color: $errorText; }
|
||||
.text-error { color: $error-text-color; }
|
||||
a.text-error:hover,
|
||||
a.text-error:focus { color: darken($errorText, 10%); }
|
||||
a.text-error:focus { color: darken($error-text-color, 10%); }
|
||||
|
||||
.text-info { color: $infoText; }
|
||||
.text-info { color: $info-text-color; }
|
||||
a.text-info:hover,
|
||||
a.text-info:focus { color: darken($infoText, 10%); }
|
||||
a.text-info:focus { color: darken($info-text-color, 10%); }
|
||||
|
||||
.text-success { color: $successText; }
|
||||
.text-success { color: $success-text-color; }
|
||||
a.text-success:hover,
|
||||
a.text-success:focus { color: darken($successText, 10%); }
|
||||
a.text-success:focus { color: darken($success-text-color, 10%); }
|
||||
a { cursor: pointer; }
|
||||
|
||||
a[disabled] {
|
||||
@@ -130,7 +130,7 @@ small,
|
||||
mark,
|
||||
.mark {
|
||||
padding: .2em;
|
||||
background-color: $state-warning-bg;
|
||||
background: $alert-warning-bg;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -7,60 +7,57 @@
|
||||
// -------------------------
|
||||
|
||||
.alert {
|
||||
padding: 0.5rem 2rem 0.5rem 1rem;
|
||||
padding: 1.25rem 2rem 1.25rem 1.5rem;
|
||||
margin-bottom: $line-height-base;
|
||||
text-shadow: 0 1px 0 rgba(255,255,255,.5);
|
||||
background-color: $state-warning-bg;
|
||||
text-shadow: 0 2px 0 rgba(255,255,255,.5);
|
||||
background: $alert-error-bg;
|
||||
position: relative;
|
||||
color: $white;
|
||||
text-shadow: 0 1px 0 rgba(0,0,0,.5);
|
||||
text-shadow: 0 1px 0 rgba(0,0,0,.2);
|
||||
border-radius: 2px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
// Alternate styles
|
||||
// -------------------------
|
||||
|
||||
.alert-success {
|
||||
background-color: $successBackground;
|
||||
background: $alert-success-bg;
|
||||
}
|
||||
|
||||
.alert-danger,
|
||||
.alert-error {
|
||||
background-color: $errorBackground;
|
||||
background: $alert-error-bg;
|
||||
}
|
||||
|
||||
.alert-info {
|
||||
background-color: $infoBackground;
|
||||
background: $alert-info-bg;
|
||||
}
|
||||
|
||||
.alert-warning {
|
||||
background-color: $state-warning-bg;
|
||||
background: $alert-warning-bg;
|
||||
}
|
||||
|
||||
.page-alert-list {
|
||||
z-index: 8000;
|
||||
min-width: 300px;
|
||||
max-width: 300px;
|
||||
min-width: 400px;
|
||||
max-width: 600px;
|
||||
position: fixed;
|
||||
right: 20px;
|
||||
top: 56px;
|
||||
}
|
||||
|
||||
.alert-close {
|
||||
position: absolute;
|
||||
top: -4px;
|
||||
right: -2px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
padding: 0;
|
||||
background: $white;
|
||||
border-radius: 50%;
|
||||
padding: 0 0 0 1rem;
|
||||
border: none;
|
||||
font-size: 1.1rem;
|
||||
color: $dark-4;
|
||||
background: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.fa {
|
||||
position: relative;
|
||||
top: -2px;
|
||||
align-self: flex-end;
|
||||
font-size: 1.5rem;
|
||||
color: rgba(255,255,255,.75)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,3 +65,18 @@
|
||||
font-weight: $font-weight-semi-bold;
|
||||
padding-bottom: 2px;
|
||||
}
|
||||
|
||||
.alert-icon {
|
||||
padding: 0 1rem 0 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 2.5rem;
|
||||
.fa {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.alert-body {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
background-color: $dropdownBackground !important;
|
||||
color: $dropdownLinkColor !important;
|
||||
border: 1px solid $dropdownBorder !important;
|
||||
width: 320px !important;
|
||||
width: 550px !important;
|
||||
|
||||
.ace_scroller {
|
||||
.ace_selected, .ace_active-line, .ace_line-hover {
|
||||
@@ -77,3 +77,7 @@ $doc-font-size: $font-size-sm;
|
||||
.ace_tooltip {
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.ace_hidden-cursors .ace_cursor {
|
||||
opacity: 0 !important;
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ $useDropShadow: false;
|
||||
$attachmentOffset: 0%;
|
||||
$easing: cubic-bezier(0, 0, 0.265, 1.00);
|
||||
|
||||
@include drop-theme("error", $errorBackground, $popover-color);
|
||||
@include drop-theme("error", $popover-error-bg, $popover-color);
|
||||
@include drop-theme("popover", $popover-bg, $popover-color, $popover-border-color);
|
||||
@include drop-theme("help", $popover-help-bg, $popover-help-color);
|
||||
|
||||
|
||||
@@ -138,7 +138,7 @@ div.flot-text {
|
||||
&--error {
|
||||
display: block;
|
||||
color: $text-color;
|
||||
@include panel-corner-color($errorBackground);
|
||||
@include panel-corner-color($popover-error-bg);
|
||||
.fa:before {
|
||||
content: "\f12a";
|
||||
}
|
||||
|
||||
@@ -176,7 +176,7 @@ define([
|
||||
});
|
||||
|
||||
it('86399s 1 resolution', function() {
|
||||
var range = { from: dateMath.parse('now-86399s'), to: dateMath.parse('now') };
|
||||
var range = { from: dateMath.parse('now-86390s'), to: dateMath.parse('now') };
|
||||
var res = kbn.calculateInterval(range, 1, null);
|
||||
expect(res.interval).to.be('12h');
|
||||
expect(res.intervalMs).to.be(43200000);
|
||||
|
||||
@@ -31,11 +31,14 @@
|
||||
|
||||
<div class="page-alert-list">
|
||||
<div ng-repeat='alert in dashAlerts.list' class="alert-{{alert.severity}} alert">
|
||||
<div class="alert-icon"><i class="{{alert.icon}}"></i></div>
|
||||
<div class="alert-body">
|
||||
<div class="alert-title">{{alert.title}}</div>
|
||||
<div class="alert-text" ng-bind='alert.text'></div>
|
||||
</div>
|
||||
<button type="button" class="alert-close" ng-click="dashAlerts.clear(alert)">
|
||||
<i class="fa fa-times-circle"></i>
|
||||
<i class="fa fa fa-remove"></i>
|
||||
</button>
|
||||
<div class="alert-title">{{alert.title}}</div>
|
||||
<div ng-bind='alert.text'></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user