mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
feat(alerting): show alertin state in panel header, closes #6136
This commit is contained in:
parent
2c4524bbfd
commit
7c339f0794
@ -25,6 +25,25 @@ func ValidateOrgAlert(c *middleware.Context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetAlertStatesForDashboard(c *middleware.Context) Response {
|
||||||
|
dashboardId := c.QueryInt64("dashboardId")
|
||||||
|
|
||||||
|
if dashboardId == 0 {
|
||||||
|
return ApiError(400, "Missing query parameter dashboardId", nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
query := models.GetAlertStatesForDashboardQuery{
|
||||||
|
OrgId: c.OrgId,
|
||||||
|
DashboardId: c.QueryInt64("dashboardId"),
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := bus.Dispatch(&query); err != nil {
|
||||||
|
return ApiError(500, "Failed to fetch alert states", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return Json(200, query.Result)
|
||||||
|
}
|
||||||
|
|
||||||
// GET /api/alerts
|
// GET /api/alerts
|
||||||
func GetAlerts(c *middleware.Context) Response {
|
func GetAlerts(c *middleware.Context) Response {
|
||||||
query := models.GetAlertsQuery{
|
query := models.GetAlertsQuery{
|
||||||
|
@ -254,6 +254,7 @@ func Register(r *macaron.Macaron) {
|
|||||||
r.Post("/test", bind(dtos.AlertTestCommand{}), wrap(AlertTest))
|
r.Post("/test", bind(dtos.AlertTestCommand{}), wrap(AlertTest))
|
||||||
r.Get("/:alertId", ValidateOrgAlert, wrap(GetAlert))
|
r.Get("/:alertId", ValidateOrgAlert, wrap(GetAlert))
|
||||||
r.Get("/", wrap(GetAlerts))
|
r.Get("/", wrap(GetAlerts))
|
||||||
|
r.Get("/states-for-dashboard", wrap(GetAlertStatesForDashboard))
|
||||||
})
|
})
|
||||||
|
|
||||||
r.Get("/alert-notifications", wrap(GetAlertNotifications))
|
r.Get("/alert-notifications", wrap(GetAlertNotifications))
|
||||||
|
@ -135,3 +135,18 @@ type GetAlertByIdQuery struct {
|
|||||||
|
|
||||||
Result *Alert
|
Result *Alert
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type GetAlertStatesForDashboardQuery struct {
|
||||||
|
OrgId int64
|
||||||
|
DashboardId int64
|
||||||
|
|
||||||
|
Result []*AlertStateInfoDTO
|
||||||
|
}
|
||||||
|
|
||||||
|
type AlertStateInfoDTO struct {
|
||||||
|
Id int64 `json:"id"`
|
||||||
|
DashboardId int64 `json:"dashboardId"`
|
||||||
|
PanelId int64 `json:"panelId"`
|
||||||
|
State AlertStateType `json:"state"`
|
||||||
|
NewStateDate time.Time `json:"newStateDate"`
|
||||||
|
}
|
||||||
|
@ -74,9 +74,9 @@ func (e *DashAlertExtractor) GetAlerts() ([]*m.Alert, error) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// backward compatability check, can be removed later
|
||||||
enabled, hasEnabled := jsonAlert.CheckGet("enabled")
|
enabled, hasEnabled := jsonAlert.CheckGet("enabled")
|
||||||
|
if hasEnabled && enabled.MustBool() == false {
|
||||||
if !hasEnabled || !enabled.MustBool() {
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,7 +42,6 @@ func TestAlertRuleExtraction(t *testing.T) {
|
|||||||
"name": "name1",
|
"name": "name1",
|
||||||
"message": "desc1",
|
"message": "desc1",
|
||||||
"handler": 1,
|
"handler": 1,
|
||||||
"enabled": true,
|
|
||||||
"frequency": "60s",
|
"frequency": "60s",
|
||||||
"conditions": [
|
"conditions": [
|
||||||
{
|
{
|
||||||
@ -66,7 +65,6 @@ func TestAlertRuleExtraction(t *testing.T) {
|
|||||||
"name": "name2",
|
"name": "name2",
|
||||||
"message": "desc2",
|
"message": "desc2",
|
||||||
"handler": 0,
|
"handler": 0,
|
||||||
"enabled": true,
|
|
||||||
"frequency": "60s",
|
"frequency": "60s",
|
||||||
"severity": "warning",
|
"severity": "warning",
|
||||||
"conditions": [
|
"conditions": [
|
||||||
|
@ -17,6 +17,7 @@ func init() {
|
|||||||
bus.AddHandler("sql", DeleteAlertById)
|
bus.AddHandler("sql", DeleteAlertById)
|
||||||
bus.AddHandler("sql", GetAllAlertQueryHandler)
|
bus.AddHandler("sql", GetAllAlertQueryHandler)
|
||||||
bus.AddHandler("sql", SetAlertState)
|
bus.AddHandler("sql", SetAlertState)
|
||||||
|
bus.AddHandler("sql", GetAlertStatesForDashboard)
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetAlertById(query *m.GetAlertByIdQuery) error {
|
func GetAlertById(query *m.GetAlertByIdQuery) error {
|
||||||
@ -241,3 +242,19 @@ func SetAlertState(cmd *m.SetAlertStateCommand) error {
|
|||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetAlertStatesForDashboard(query *m.GetAlertStatesForDashboardQuery) error {
|
||||||
|
var rawSql = `SELECT
|
||||||
|
id,
|
||||||
|
dashboard_id,
|
||||||
|
panel_id,
|
||||||
|
state,
|
||||||
|
new_state_date
|
||||||
|
FROM alert
|
||||||
|
WHERE org_id = ? AND dashboard_id = ?`
|
||||||
|
|
||||||
|
query.Result = make([]*m.AlertStateInfoDTO, 0)
|
||||||
|
err := x.Sql(rawSql, query.OrgId, query.DashboardId).Find(&query.Result)
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
@ -48,19 +48,18 @@ export class AlertTabCtrl {
|
|||||||
$onInit() {
|
$onInit() {
|
||||||
this.addNotificationSegment = this.uiSegmentSrv.newPlusButton();
|
this.addNotificationSegment = this.uiSegmentSrv.newPlusButton();
|
||||||
|
|
||||||
this.initModel();
|
// subscribe to graph threshold handle changes
|
||||||
this.validateModel();
|
var thresholdChangedEventHandler = this.graphThresholdChanged.bind(this);
|
||||||
|
this.panelCtrl.events.on('threshold-changed', thresholdChangedEventHandler);
|
||||||
|
|
||||||
// set panel alert edit mode
|
// set panel alert edit mode
|
||||||
this.$scope.$on("$destroy", () => {
|
this.$scope.$on("$destroy", () => {
|
||||||
|
this.panelCtrl.events.off("threshold-changed", thresholdChangedEventHandler);
|
||||||
this.panelCtrl.editingThresholds = false;
|
this.panelCtrl.editingThresholds = false;
|
||||||
this.panelCtrl.render();
|
this.panelCtrl.render();
|
||||||
});
|
});
|
||||||
|
|
||||||
// subscribe to graph threshold handle changes
|
// build notification model
|
||||||
this.panelCtrl.events.on('threshold-changed', this.graphThresholdChanged.bind(this));
|
|
||||||
|
|
||||||
// build notification model
|
|
||||||
this.notifications = [];
|
this.notifications = [];
|
||||||
this.alertNotifications = [];
|
this.alertNotifications = [];
|
||||||
this.alertHistory = [];
|
this.alertHistory = [];
|
||||||
@ -68,21 +67,8 @@ export class AlertTabCtrl {
|
|||||||
return this.backendSrv.get('/api/alert-notifications').then(res => {
|
return this.backendSrv.get('/api/alert-notifications').then(res => {
|
||||||
this.notifications = res;
|
this.notifications = res;
|
||||||
|
|
||||||
_.each(this.alert.notifications, item => {
|
this.initModel();
|
||||||
var model = _.find(this.notifications, {id: item.id});
|
this.validateModel();
|
||||||
if (model) {
|
|
||||||
model.iconClass = this.getNotificationIcon(model.type);
|
|
||||||
this.alertNotifications.push(model);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
_.each(this.notifications, item => {
|
|
||||||
if (item.isDefault) {
|
|
||||||
item.iconClass = this.getNotificationIcon(item.type);
|
|
||||||
item.bgColor = "#00678b";
|
|
||||||
this.alertNotifications.push(item);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -143,9 +129,8 @@ export class AlertTabCtrl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
initModel() {
|
initModel() {
|
||||||
var alert = this.alert = this.panel.alert = this.panel.alert || {enabled: false};
|
var alert = this.alert = this.panel.alert;
|
||||||
|
if (!alert) {
|
||||||
if (!this.alert.enabled) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -169,6 +154,22 @@ export class AlertTabCtrl {
|
|||||||
|
|
||||||
ThresholdMapper.alertToGraphThresholds(this.panel);
|
ThresholdMapper.alertToGraphThresholds(this.panel);
|
||||||
|
|
||||||
|
for (let addedNotification of alert.notifications) {
|
||||||
|
var model = _.find(this.notifications, {id: addedNotification.id});
|
||||||
|
if (model) {
|
||||||
|
model.iconClass = this.getNotificationIcon(model.type);
|
||||||
|
this.alertNotifications.push(model);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let notification of this.notifications) {
|
||||||
|
if (notification.isDefault) {
|
||||||
|
notification.iconClass = this.getNotificationIcon(notification.type);
|
||||||
|
notification.bgColor = "#00678b";
|
||||||
|
this.alertNotifications.push(notification);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.panelCtrl.editingThresholds = true;
|
this.panelCtrl.editingThresholds = true;
|
||||||
this.panelCtrl.render();
|
this.panelCtrl.render();
|
||||||
}
|
}
|
||||||
@ -193,7 +194,7 @@ export class AlertTabCtrl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
validateModel() {
|
validateModel() {
|
||||||
if (!this.alert.enabled) {
|
if (!this.alert) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -310,17 +311,17 @@ export class AlertTabCtrl {
|
|||||||
icon: 'fa-trash',
|
icon: 'fa-trash',
|
||||||
yesText: 'Delete',
|
yesText: 'Delete',
|
||||||
onConfirm: () => {
|
onConfirm: () => {
|
||||||
this.alert = this.panel.alert = {enabled: false};
|
delete this.panel.alert;
|
||||||
|
this.alert = null;
|
||||||
this.panel.thresholds = [];
|
this.panel.thresholds = [];
|
||||||
this.conditionModels = [];
|
this.conditionModels = [];
|
||||||
this.panelCtrl.render();
|
this.panelCtrl.render();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
enable() {
|
enable() {
|
||||||
this.alert.enabled = true;
|
this.panel.alert = {};
|
||||||
this.initModel();
|
this.initModel();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<div class="edit-tab-with-sidemenu" ng-if="ctrl.alert.enabled">
|
<div class="edit-tab-with-sidemenu" ng-if="ctrl.alert">
|
||||||
<aside class="edit-sidemenu-aside">
|
<aside class="edit-sidemenu-aside">
|
||||||
<ul class="edit-sidemenu">
|
<ul class="edit-sidemenu">
|
||||||
<li ng-class="{active: ctrl.subTabIndex === 0}">
|
<li ng-class="{active: ctrl.subTabIndex === 0}">
|
||||||
@ -151,7 +151,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="gf-form-group" ng-if="!ctrl.alert.enabled">
|
<div class="gf-form-group" ng-if="!ctrl.alert">
|
||||||
<div class="gf-form-button-row">
|
<div class="gf-form-button-row">
|
||||||
<button class="btn btn-inverse" ng-click="ctrl.enable()">
|
<button class="btn btn-inverse" ng-click="ctrl.enable()">
|
||||||
<i class="icon-gf icon-gf-alert"></i>
|
<i class="icon-gf icon-gf-alert"></i>
|
||||||
|
@ -9,6 +9,7 @@ import coreModule from 'app/core/core_module';
|
|||||||
|
|
||||||
export class AnnotationsSrv {
|
export class AnnotationsSrv {
|
||||||
globalAnnotationsPromise: any;
|
globalAnnotationsPromise: any;
|
||||||
|
alertStatesPromise: any;
|
||||||
|
|
||||||
/** @ngInject */
|
/** @ngInject */
|
||||||
constructor(private $rootScope,
|
constructor(private $rootScope,
|
||||||
@ -22,14 +23,27 @@ export class AnnotationsSrv {
|
|||||||
|
|
||||||
clearCache() {
|
clearCache() {
|
||||||
this.globalAnnotationsPromise = null;
|
this.globalAnnotationsPromise = null;
|
||||||
|
this.alertStatesPromise = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
getAnnotations(options) {
|
getAnnotations(options) {
|
||||||
return this.$q.all([
|
return this.$q.all([
|
||||||
this.getGlobalAnnotations(options),
|
this.getGlobalAnnotations(options),
|
||||||
this.getPanelAnnotations(options)
|
this.getPanelAnnotations(options),
|
||||||
]).then(allResults => {
|
this.getAlertStates(options)
|
||||||
return _.flattenDeep(allResults);
|
]).then(results => {
|
||||||
|
|
||||||
|
// combine the annotations and flatten results
|
||||||
|
var annotations = _.flattenDeep([results[0], results[1]]);
|
||||||
|
|
||||||
|
// look for alert state for this panel
|
||||||
|
var alertState = _.find(results[2], {panelId: options.panel.id});
|
||||||
|
|
||||||
|
return {
|
||||||
|
annotations: annotations,
|
||||||
|
alertState: alertState,
|
||||||
|
};
|
||||||
|
|
||||||
}).catch(err => {
|
}).catch(err => {
|
||||||
this.$rootScope.appEvent('alert-error', ['Annotations failed', (err.message || err)]);
|
this.$rootScope.appEvent('alert-error', ['Annotations failed', (err.message || err)]);
|
||||||
});
|
});
|
||||||
@ -39,7 +53,7 @@ export class AnnotationsSrv {
|
|||||||
var panel = options.panel;
|
var panel = options.panel;
|
||||||
var dashboard = options.dashboard;
|
var dashboard = options.dashboard;
|
||||||
|
|
||||||
if (panel && panel.alert && panel.alert.enabled) {
|
if (panel && panel.alert) {
|
||||||
return this.backendSrv.get('/api/annotations', {
|
return this.backendSrv.get('/api/annotations', {
|
||||||
from: options.range.from.valueOf(),
|
from: options.range.from.valueOf(),
|
||||||
to: options.range.to.valueOf(),
|
to: options.range.to.valueOf(),
|
||||||
@ -54,6 +68,28 @@ export class AnnotationsSrv {
|
|||||||
return this.$q.when([]);
|
return this.$q.when([]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getAlertStates(options) {
|
||||||
|
if (!options.dashboard.id) {
|
||||||
|
return this.$q.when([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ignore if no alerts
|
||||||
|
if (options.panel && !options.panel.alert) {
|
||||||
|
return this.$q.when([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.range.raw.to !== 'now') {
|
||||||
|
return this.$q.when([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.alertStatesPromise) {
|
||||||
|
return this.alertStatesPromise;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.alertStatesPromise = this.backendSrv.get('/api/alerts/states-for-dashboard', {dashboardId: options.dashboard.id});
|
||||||
|
return this.alertStatesPromise;
|
||||||
|
}
|
||||||
|
|
||||||
getGlobalAnnotations(options) {
|
getGlobalAnnotations(options) {
|
||||||
var dashboard = options.dashboard;
|
var dashboard = options.dashboard;
|
||||||
|
|
||||||
|
@ -159,7 +159,7 @@ export class DashNavCtrl {
|
|||||||
var confirmText = "";
|
var confirmText = "";
|
||||||
var text2 = $scope.dashboard.title;
|
var text2 = $scope.dashboard.title;
|
||||||
var alerts = $scope.dashboard.rows.reduce((memo, row) => {
|
var alerts = $scope.dashboard.rows.reduce((memo, row) => {
|
||||||
memo += row.panels.filter(panel => panel.alert && panel.alert.enabled).length;
|
memo += row.panels.filter(panel => panel.alert).length;
|
||||||
return memo;
|
return memo;
|
||||||
}, 0);
|
}, 0);
|
||||||
|
|
||||||
|
@ -131,7 +131,9 @@ class MetricsPanelCtrl extends PanelCtrl {
|
|||||||
var intervalOverride = this.panel.interval;
|
var intervalOverride = this.panel.interval;
|
||||||
|
|
||||||
// if no panel interval check datasource
|
// if no panel interval check datasource
|
||||||
if (!intervalOverride && this.datasource && this.datasource.interval) {
|
if (intervalOverride) {
|
||||||
|
intervalOverride = this.templateSrv.replace(intervalOverride, this.panel.scopedVars);
|
||||||
|
} else if (this.datasource && this.datasource.interval) {
|
||||||
intervalOverride = this.datasource.interval;
|
intervalOverride = this.datasource.interval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ import $ from 'jquery';
|
|||||||
var module = angular.module('grafana.directives');
|
var module = angular.module('grafana.directives');
|
||||||
|
|
||||||
var panelTemplate = `
|
var panelTemplate = `
|
||||||
<div class="panel-container" ng-class="{'panel-transparent': ctrl.panel.transparent}">
|
<div class="panel-container">
|
||||||
<div class="panel-header">
|
<div class="panel-header">
|
||||||
<span class="alert-error panel-error small pointer" ng-if="ctrl.error" ng-click="ctrl.openInspector()">
|
<span class="alert-error panel-error small pointer" ng-if="ctrl.error" ng-click="ctrl.openInspector()">
|
||||||
<span data-placement="top" bs-tooltip="ctrl.error">
|
<span data-placement="top" bs-tooltip="ctrl.error">
|
||||||
@ -65,6 +65,26 @@ module.directive('grafanaPanel', function() {
|
|||||||
link: function(scope, elem) {
|
link: function(scope, elem) {
|
||||||
var panelContainer = elem.find('.panel-container');
|
var panelContainer = elem.find('.panel-container');
|
||||||
var ctrl = scope.ctrl;
|
var ctrl = scope.ctrl;
|
||||||
|
|
||||||
|
// the reason for handling these classes this way is for performance
|
||||||
|
// limit the watchers on panels etc
|
||||||
|
|
||||||
|
ctrl.events.on('render', () => {
|
||||||
|
panelContainer.toggleClass('panel-transparent', ctrl.panel.transparent === true);
|
||||||
|
panelContainer.toggleClass('panel-has-alert', ctrl.panel.alert !== undefined);
|
||||||
|
|
||||||
|
if (panelContainer.hasClass('panel-has-alert')) {
|
||||||
|
panelContainer.removeClass('panel-alert-state--ok panel-alert-state--alerting');
|
||||||
|
}
|
||||||
|
|
||||||
|
// set special class for ok, or alerting states
|
||||||
|
if (ctrl.alertState) {
|
||||||
|
if (ctrl.alertState.state === 'ok' || ctrl.alertState.state === 'alerting') {
|
||||||
|
panelContainer.addClass('panel-alert-state--' + ctrl.alertState.state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
scope.$watchGroup(['ctrl.fullscreen', 'ctrl.containerHeight'], function() {
|
scope.$watchGroup(['ctrl.fullscreen', 'ctrl.containerHeight'], function() {
|
||||||
panelContainer.css({minHeight: ctrl.containerHeight});
|
panelContainer.css({minHeight: ctrl.containerHeight});
|
||||||
elem.toggleClass('panel-fullscreen', ctrl.fullscreen ? true : false);
|
elem.toggleClass('panel-fullscreen', ctrl.fullscreen ? true : false);
|
||||||
|
@ -12,6 +12,7 @@ function (angular, $, _, Tether) {
|
|||||||
.directive('panelMenu', function($compile, linkSrv) {
|
.directive('panelMenu', function($compile, linkSrv) {
|
||||||
var linkTemplate =
|
var linkTemplate =
|
||||||
'<span class="panel-title drag-handle pointer">' +
|
'<span class="panel-title drag-handle pointer">' +
|
||||||
|
'<span class="icon-gf panel-alert-icon"></span>' +
|
||||||
'<span class="panel-title-text drag-handle">{{ctrl.panel.title | interpolateTemplateVars:this}}</span>' +
|
'<span class="panel-title-text drag-handle">{{ctrl.panel.title | interpolateTemplateVars:this}}</span>' +
|
||||||
'<span class="panel-links-btn"><i class="fa fa-external-link"></i></span>' +
|
'<span class="panel-links-btn"><i class="fa fa-external-link"></i></span>' +
|
||||||
'<span class="panel-time-info" ng-show="ctrl.timeInfo"><i class="fa fa-clock-o"></i> {{ctrl.timeInfo}}</span>' +
|
'<span class="panel-time-info" ng-show="ctrl.timeInfo"><i class="fa fa-clock-o"></i> {{ctrl.timeInfo}}</span>' +
|
||||||
|
@ -8,11 +8,11 @@
|
|||||||
<span class="gf-form-label width-6">Span</span>
|
<span class="gf-form-label width-6">Span</span>
|
||||||
<select class="gf-form-input gf-size-auto" ng-model="ctrl.panel.span" ng-options="f for f in [0,1,2,3,4,5,6,7,8,9,10,11,12]"></select>
|
<select class="gf-form-input gf-size-auto" ng-model="ctrl.panel.span" ng-options="f for f in [0,1,2,3,4,5,6,7,8,9,10,11,12]"></select>
|
||||||
</div>
|
</div>
|
||||||
<div class="gf-form max-width-26">
|
<div class="gf-form">
|
||||||
<span class="gf-form-label width-8">Height</span>
|
<span class="gf-form-label width-8">Height</span>
|
||||||
<input type="text" class="gf-form-input max-width-6" ng-model='ctrl.panel.height' placeholder="100px"></input>
|
<input type="text" class="gf-form-input max-width-6" ng-model='ctrl.panel.height' placeholder="100px"></input>
|
||||||
<editor-checkbox text="Transparent" model="ctrl.panel.transparent"></editor-checkbox>
|
|
||||||
</div>
|
</div>
|
||||||
|
<gf-form-switch class="gf-form" label="Transparent" checked="ctrl.panel.transparent" on-change="ctrl.render()"></gf-form-switch>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="gf-form-inline">
|
<div class="gf-form-inline">
|
||||||
|
@ -62,7 +62,7 @@ module.directive('grafanaGraph', function($rootScope, timeSrv) {
|
|||||||
if (!data) {
|
if (!data) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
annotations = data.annotations || annotations;
|
annotations = ctrl.annotations;
|
||||||
render_panel();
|
render_panel();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -22,6 +22,9 @@ class GraphCtrl extends MetricsPanelCtrl {
|
|||||||
hiddenSeries: any = {};
|
hiddenSeries: any = {};
|
||||||
seriesList: any = [];
|
seriesList: any = [];
|
||||||
dataList: any = [];
|
dataList: any = [];
|
||||||
|
annotations: any = [];
|
||||||
|
alertState: any;
|
||||||
|
|
||||||
annotationsPromise: any;
|
annotationsPromise: any;
|
||||||
datapointsCount: number;
|
datapointsCount: number;
|
||||||
datapointsOutside: boolean;
|
datapointsOutside: boolean;
|
||||||
@ -167,11 +170,11 @@ class GraphCtrl extends MetricsPanelCtrl {
|
|||||||
|
|
||||||
onDataError(err) {
|
onDataError(err) {
|
||||||
this.seriesList = [];
|
this.seriesList = [];
|
||||||
|
this.annotations = [];
|
||||||
this.render([]);
|
this.render([]);
|
||||||
}
|
}
|
||||||
|
|
||||||
onDataReceived(dataList) {
|
onDataReceived(dataList) {
|
||||||
|
|
||||||
this.dataList = dataList;
|
this.dataList = dataList;
|
||||||
this.seriesList = this.processor.getSeriesList({dataList: dataList, range: this.range});
|
this.seriesList = this.processor.getSeriesList({dataList: dataList, range: this.range});
|
||||||
|
|
||||||
@ -186,9 +189,10 @@ class GraphCtrl extends MetricsPanelCtrl {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.annotationsPromise.then(annotations => {
|
this.annotationsPromise.then(result => {
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
this.seriesList.annotations = annotations;
|
this.alertState = result.alertState;
|
||||||
|
this.annotations = result.annotations;
|
||||||
this.render(this.seriesList);
|
this.render(this.seriesList);
|
||||||
}, () => {
|
}, () => {
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
|
@ -13,7 +13,7 @@ export class ThresholdFormCtrl {
|
|||||||
constructor($scope) {
|
constructor($scope) {
|
||||||
this.panel = this.panelCtrl.panel;
|
this.panel = this.panelCtrl.panel;
|
||||||
|
|
||||||
if (this.panel.alert && this.panel.alert.enabled) {
|
if (this.panel.alert) {
|
||||||
this.disabled = true;
|
this.disabled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,10 +34,7 @@
|
|||||||
ng-change="editor.render()"
|
ng-change="editor.render()"
|
||||||
ng-model-onblur>
|
ng-model-onblur>
|
||||||
</div>
|
</div>
|
||||||
<gf-form-switch class="gf-form" label-class="width-4"
|
<gf-form-switch class="gf-form" label-class="width-4" label="Scroll" checked="editor.panel.scroll" on-change="editor.render()"></gf-form-switch>
|
||||||
label="Scroll"
|
|
||||||
checked="editor.panel.scroll"
|
|
||||||
change="editor.render()"></gf-form-switch>
|
|
||||||
<div class="gf-form max-width-17">
|
<div class="gf-form max-width-17">
|
||||||
<label class="gf-form-label width-6">Font size</label>
|
<label class="gf-form-label width-6">Font size</label>
|
||||||
<div class="gf-form-select-wrapper max-width-15">
|
<div class="gf-form-select-wrapper max-width-15">
|
||||||
|
@ -38,3 +38,33 @@
|
|||||||
top: 2px;
|
top: 2px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.panel-has-alert {
|
||||||
|
.panel-alert-icon:before {
|
||||||
|
content: "\e611";
|
||||||
|
position: relative;
|
||||||
|
top: 1px;
|
||||||
|
left: -3px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-alert-state {
|
||||||
|
&--alerting {
|
||||||
|
box-shadow: 0 0 10px $critical;
|
||||||
|
|
||||||
|
.panel-alert-icon:before {
|
||||||
|
color: $critical;
|
||||||
|
content: "\e610";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&--ok {
|
||||||
|
//box-shadow: 0 0 5px rgba(0,200,0,10.8);
|
||||||
|
.panel-alert-icon:before {
|
||||||
|
color: $online;
|
||||||
|
content: "\e610";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user