Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
N
nexpie-grafana-theme
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Registry
Registry
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Kornkitt Poolsup
nexpie-grafana-theme
Commits
20fcffb7
Commit
20fcffb7
authored
Jul 19, 2016
by
Torkel Ödegaard
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat(alerting): working on alerting conditions model
parent
f38c9546
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
125 additions
and
131 deletions
+125
-131
pkg/services/alerting/alert_rule.go
+33
-46
pkg/services/alerting/alert_rule_test.go
+14
-25
pkg/services/alerting/conditions.go
+32
-0
pkg/services/alerting/result_handler_test.go
+4
-4
public/app/plugins/panel/graph/alert_tab_ctrl.ts
+14
-29
public/app/plugins/panel/graph/partials/tab_alerting.html
+28
-27
No files found.
pkg/services/alerting/alert_rule.go
View file @
20fcffb7
...
@@ -4,7 +4,6 @@ import (
...
@@ -4,7 +4,6 @@ import (
"fmt"
"fmt"
"regexp"
"regexp"
"strconv"
"strconv"
"strings"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/services/alerting/transformers"
"github.com/grafana/grafana/pkg/services/alerting/transformers"
...
@@ -31,6 +30,19 @@ type AlertRule struct {
...
@@ -31,6 +30,19 @@ type AlertRule struct {
NotificationGroups
[]
int64
NotificationGroups
[]
int64
}
}
type
AlertRule2
struct
{
Id
int64
OrgId
int64
DashboardId
int64
PanelId
int64
Frequency
int64
Name
string
Description
string
State
string
Conditions
[]
AlertCondition
Notifications
[]
int64
}
var
(
var
(
ValueFormatRegex
=
regexp
.
MustCompile
(
"^
\\
d+"
)
ValueFormatRegex
=
regexp
.
MustCompile
(
"^
\\
d+"
)
UnitFormatRegex
=
regexp
.
MustCompile
(
"
\\
w{1}$"
)
UnitFormatRegex
=
regexp
.
MustCompile
(
"
\\
w{1}$"
)
...
@@ -56,7 +68,11 @@ func getTimeDurationStringToSeconds(str string) int64 {
...
@@ -56,7 +68,11 @@ func getTimeDurationStringToSeconds(str string) int64 {
}
}
func
NewAlertRuleFromDBModel
(
ruleDef
*
m
.
Alert
)
(
*
AlertRule
,
error
)
{
func
NewAlertRuleFromDBModel
(
ruleDef
*
m
.
Alert
)
(
*
AlertRule
,
error
)
{
model
:=
&
AlertRule
{}
return
nil
,
nil
}
func
NewAlertRuleFromDBModel2
(
ruleDef
*
m
.
Alert
)
(
*
AlertRule2
,
error
)
{
model
:=
&
AlertRule2
{}
model
.
Id
=
ruleDef
.
Id
model
.
Id
=
ruleDef
.
Id
model
.
OrgId
=
ruleDef
.
OrgId
model
.
OrgId
=
ruleDef
.
OrgId
model
.
Name
=
ruleDef
.
Name
model
.
Name
=
ruleDef
.
Name
...
@@ -64,55 +80,26 @@ func NewAlertRuleFromDBModel(ruleDef *m.Alert) (*AlertRule, error) {
...
@@ -64,55 +80,26 @@ func NewAlertRuleFromDBModel(ruleDef *m.Alert) (*AlertRule, error) {
model
.
State
=
ruleDef
.
State
model
.
State
=
ruleDef
.
State
model
.
Frequency
=
ruleDef
.
Frequency
model
.
Frequency
=
ruleDef
.
Frequency
ngs
:=
ruleDef
.
Settings
.
Get
(
"notificationGroups"
)
.
MustString
()
for
_
,
v
:=
range
ruleDef
.
Settings
.
Get
(
"notifications"
)
.
MustArray
()
{
var
ids
[]
int64
if
id
,
ok
:=
v
.
(
int64
);
ok
{
for
_
,
v
:=
range
strings
.
Split
(
ngs
,
","
)
{
model
.
Notifications
=
append
(
model
.
Notifications
,
int64
(
id
))
id
,
err
:=
strconv
.
Atoi
(
v
)
if
err
==
nil
{
ids
=
append
(
ids
,
int64
(
id
))
}
}
}
}
model
.
NotificationGroups
=
ids
for
_
,
condition
:=
range
ruleDef
.
Settings
.
Get
(
"conditions"
)
.
MustArray
()
{
conditionModel
:=
simplejson
.
NewFromAny
(
condition
)
critical
:=
ruleDef
.
Settings
.
Get
(
"crit"
)
switch
conditionModel
.
Get
(
"type"
)
.
MustString
()
{
model
.
Critical
=
Level
{
case
"query"
:
Operator
:
critical
.
Get
(
"op"
)
.
MustString
(),
queryCondition
,
err
:=
NewQueryCondition
(
conditionModel
)
Value
:
critical
.
Get
(
"value"
)
.
MustFloat64
(),
if
err
!=
nil
{
}
return
nil
,
err
}
warning
:=
ruleDef
.
Settings
.
Get
(
"warn"
)
model
.
Conditions
=
append
(
model
.
Conditions
,
queryCondition
)
model
.
Warning
=
Level
{
}
Operator
:
warning
.
Get
(
"op"
)
.
MustString
(),
Value
:
warning
.
Get
(
"value"
)
.
MustFloat64
(),
}
model
.
Transform
=
ruleDef
.
Settings
.
Get
(
"transform"
)
.
Get
(
"type"
)
.
MustString
()
if
model
.
Transform
==
""
{
return
nil
,
fmt
.
Errorf
(
"missing transform"
)
}
model
.
TransformParams
=
*
ruleDef
.
Settings
.
Get
(
"transform"
)
if
model
.
Transform
==
"aggregation"
{
method
:=
ruleDef
.
Settings
.
Get
(
"transform"
)
.
Get
(
"method"
)
.
MustString
()
model
.
Transformer
=
transformers
.
NewAggregationTransformer
(
method
)
}
query
:=
ruleDef
.
Settings
.
Get
(
"query"
)
model
.
Query
=
AlertQuery
{
Query
:
query
.
Get
(
"query"
)
.
MustString
(),
DatasourceId
:
query
.
Get
(
"datasourceId"
)
.
MustInt64
(),
From
:
query
.
Get
(
"from"
)
.
MustString
(),
To
:
query
.
Get
(
"to"
)
.
MustString
(),
}
if
model
.
Query
.
Query
==
""
{
return
nil
,
fmt
.
Errorf
(
"missing query.query"
)
}
}
if
model
.
Query
.
DatasourceId
==
0
{
if
len
(
model
.
Conditions
)
==
0
{
return
nil
,
fmt
.
Errorf
(
"
missing query.datasourceId
"
)
return
nil
,
fmt
.
Errorf
(
"
Alert is missing conditions
"
)
}
}
return
model
,
nil
return
model
,
nil
...
...
pkg/services/alerting/alert_rule_test.go
View file @
20fcffb7
...
@@ -38,26 +38,19 @@ func TestAlertRuleModel(t *testing.T) {
...
@@ -38,26 +38,19 @@ func TestAlertRuleModel(t *testing.T) {
"description": "desc2",
"description": "desc2",
"handler": 0,
"handler": 0,
"enabled": true,
"enabled": true,
"crit": {
"value": 20,
"op": ">"
},
"warn": {
"value": 10,
"op": ">"
},
"frequency": "60s",
"frequency": "60s",
"query": {
"conditions": [
"from": "5m",
{
"refId": "A",
"type": "query",
"to": "now",
"query": {
"query": "aliasByNode(statsd.fakesite.counters.session_start.mobile.count, 4)",
"params": ["A", "5m", "now"],
"datasourceId": 1
"datasourceId": 1,
},
"query": "aliasByNode(statsd.fakesite.counters.session_start.mobile.count, 4)"
"transform": {
},
"type": "avg",
"reducer": {"type": "avg", "params": []},
"name": "aggregation"
"evaluator": {"type": ">", "params": [100]}
}
}
]
}
}
`
`
...
@@ -72,15 +65,11 @@ func TestAlertRuleModel(t *testing.T) {
...
@@ -72,15 +65,11 @@ func TestAlertRuleModel(t *testing.T) {
Settings
:
alertJSON
,
Settings
:
alertJSON
,
}
}
alertRule
,
err
:=
NewAlertRuleFromDBModel
(
alert
)
alertRule
,
err
:=
NewAlertRuleFromDBModel2
(
alert
)
So
(
err
,
ShouldBeNil
)
So
(
err
,
ShouldBeNil
)
So
(
alertRule
.
Warning
.
Operator
,
ShouldEqual
,
">"
)
So
(
alertRule
.
Conditions
,
ShouldHaveLength
,
1
)
So
(
alertRule
.
Warning
.
Value
,
ShouldEqual
,
10
)
So
(
alertRule
.
Critical
.
Operator
,
ShouldEqual
,
">"
)
So
(
alertRule
.
Critical
.
Value
,
ShouldEqual
,
20
)
})
})
})
})
}
}
pkg/services/alerting/conditions.go
0 → 100644
View file @
20fcffb7
package
alerting
import
"github.com/grafana/grafana/pkg/components/simplejson"
type
AlertCondition
interface
{
Eval
()
}
type
QueryCondition
struct
{
Query
AlertQuery
Reducer
AlertReducerModel
Evaluator
AlertEvaluatorModel
}
func
(
c
*
QueryCondition
)
Eval
()
{
}
type
AlertReducerModel
struct
{
Type
string
Params
[]
interface
{}
}
type
AlertEvaluatorModel
struct
{
Type
string
Params
[]
interface
{}
}
func
NewQueryCondition
(
model
*
simplejson
.
Json
)
(
*
QueryCondition
,
error
)
{
condition
:=
QueryCondition
{}
return
&
condition
,
nil
}
pkg/services/alerting/result_handler_test.go
View file @
20fcffb7
...
@@ -38,7 +38,7 @@ func TestAlertResultHandler(t *testing.T) {
...
@@ -38,7 +38,7 @@ func TestAlertResultHandler(t *testing.T) {
Convey
(
"alert state have changed"
,
func
()
{
Convey
(
"alert state have changed"
,
func
()
{
mockAlertState
=
&
m
.
AlertState
{
mockAlertState
=
&
m
.
AlertState
{
New
State
:
alertstates
.
Critical
,
State
:
alertstates
.
Critical
,
}
}
mockResult
.
State
=
alertstates
.
Ok
mockResult
.
State
=
alertstates
.
Ok
So
(
resultHandler
.
shouldUpdateState
(
mockResult
),
ShouldBeTrue
)
So
(
resultHandler
.
shouldUpdateState
(
mockResult
),
ShouldBeTrue
)
...
@@ -47,11 +47,11 @@ func TestAlertResultHandler(t *testing.T) {
...
@@ -47,11 +47,11 @@ func TestAlertResultHandler(t *testing.T) {
Convey
(
"last alert state was 15min ago"
,
func
()
{
Convey
(
"last alert state was 15min ago"
,
func
()
{
now
:=
time
.
Now
()
now
:=
time
.
Now
()
mockAlertState
=
&
m
.
AlertState
{
mockAlertState
=
&
m
.
AlertState
{
NewState
:
alertstates
.
Critical
,
State
:
alertstates
.
Critical
,
Created
:
now
.
Add
(
time
.
Minute
*
-
30
),
Created
:
now
.
Add
(
time
.
Minute
*
-
30
),
}
}
mockResult
.
State
=
alertstates
.
Critical
mockResult
.
State
=
alertstates
.
Critical
mockResult
.
Exeuction
Time
=
time
.
Now
()
mockResult
.
Start
Time
=
time
.
Now
()
So
(
resultHandler
.
shouldUpdateState
(
mockResult
),
ShouldBeTrue
)
So
(
resultHandler
.
shouldUpdateState
(
mockResult
),
ShouldBeTrue
)
})
})
})
})
...
...
public/app/plugins/panel/graph/alert_tab_ctrl.ts
View file @
20fcffb7
...
@@ -38,11 +38,15 @@ export class AlertTabCtrl {
...
@@ -38,11 +38,15 @@ export class AlertTabCtrl {
];
];
alert
:
any
;
alert
:
any
;
conditionModels
:
any
;
conditionModels
:
any
;
levelOpList
=
[
evalFunctions
=
[
{
text
:
'>'
,
value
:
'>'
},
{
text
:
'>'
,
value
:
'>'
},
{
text
:
'<'
,
value
:
'<'
},
{
text
:
'<'
,
value
:
'<'
},
{
text
:
'='
,
value
:
'='
},
];
];
severityLevels
=
[
{
text
:
'Critical'
,
value
:
'critical'
},
{
text
:
'Warning'
,
value
:
'warning'
},
];
/** @ngInject */
/** @ngInject */
constructor
(
$scope
,
private
$timeout
)
{
constructor
(
$scope
,
private
$timeout
)
{
...
@@ -60,21 +64,15 @@ export class AlertTabCtrl {
...
@@ -60,21 +64,15 @@ export class AlertTabCtrl {
});
});
}
}
getThresholdWithDefaults
(
threshold
)
{
threshold
=
threshold
||
{};
threshold
.
op
=
threshold
.
op
||
'>'
;
threshold
.
value
=
threshold
.
value
||
undefined
;
return
threshold
;
}
initModel
()
{
initModel
()
{
var
alert
=
this
.
alert
=
this
.
panel
.
alert
=
this
.
panel
.
alert
||
{};
var
alert
=
this
.
alert
=
this
.
panel
.
alert
=
this
.
panel
.
alert
||
{};
alert
.
conditions
=
alert
.
conditions
||
[];
alert
.
conditions
=
[];
if
(
alert
.
conditions
.
length
===
0
)
{
if
(
alert
.
conditions
.
length
===
0
)
{
alert
.
conditions
.
push
(
this
.
buildDefaultCondition
());
alert
.
conditions
.
push
(
this
.
buildDefaultCondition
());
}
}
alert
.
severity
=
alert
.
severity
||
'critical'
;
alert
.
frequency
=
alert
.
frequency
||
'60s'
;
alert
.
frequency
=
alert
.
frequency
||
'60s'
;
alert
.
handler
=
alert
.
handler
||
1
;
alert
.
handler
=
alert
.
handler
||
1
;
alert
.
notifications
=
alert
.
notifications
||
[];
alert
.
notifications
=
alert
.
notifications
||
[];
...
@@ -95,32 +93,23 @@ export class AlertTabCtrl {
...
@@ -95,32 +93,23 @@ export class AlertTabCtrl {
buildDefaultCondition
()
{
buildDefaultCondition
()
{
return
{
return
{
type
:
'query'
,
type
:
'query'
,
refId
:
'A'
,
query
:
{
params
:
[
'A'
,
'5m'
,
'now'
]},
from
:
'5m'
,
reducer
:
{
type
:
'avg'
,
params
:
[]},
to
:
'now'
,
evaluator
:
{
type
:
'>'
,
params
:
[
null
]},
reducer
:
'avg'
,
reducerParams
:
[],
warn
:
this
.
getThresholdWithDefaults
({}),
crit
:
this
.
getThresholdWithDefaults
({}),
};
};
}
}
buildConditionModel
(
source
)
{
buildConditionModel
(
source
)
{
var
cm
:
any
=
{
source
:
source
,
type
:
source
.
type
};
var
cm
:
any
=
{
source
:
source
,
type
:
source
.
type
};
var
queryPartModel
=
{
cm
.
queryPart
=
new
QueryPart
(
source
.
query
,
alertQueryDef
);
params
:
[
source
.
refId
,
source
.
from
,
source
.
to
]
};
cm
.
queryPart
=
new
QueryPart
(
queryPartModel
,
alertQueryDef
);
cm
.
reducerPart
=
new
QueryPart
({
params
:
[]},
reducerAvgDef
);
cm
.
reducerPart
=
new
QueryPart
({
params
:
[]},
reducerAvgDef
);
cm
.
evaluator
=
source
.
evaluator
;
return
cm
;
return
cm
;
}
}
queryPartUpdated
(
conditionModel
)
{
queryPartUpdated
(
conditionModel
)
{
conditionModel
.
source
.
refId
=
conditionModel
.
queryPart
.
params
[
0
];
conditionModel
.
source
.
from
=
conditionModel
.
queryPart
.
params
[
1
];
conditionModel
.
source
.
to
=
conditionModel
.
queryPart
.
params
[
2
];
}
}
addCondition
(
type
)
{
addCondition
(
type
)
{
...
@@ -138,10 +127,6 @@ export class AlertTabCtrl {
...
@@ -138,10 +127,6 @@ export class AlertTabCtrl {
delete
()
{
delete
()
{
this
.
alert
.
enabled
=
false
;
this
.
alert
.
enabled
=
false
;
this
.
alert
.
warn
.
value
=
undefined
;
this
.
alert
.
crit
.
value
=
undefined
;
// reset model but keep thresholds instance
this
.
initModel
();
this
.
initModel
();
}
}
...
...
public/app/plugins/panel/graph/partials/tab_alerting.html
View file @
20fcffb7
...
@@ -27,31 +27,42 @@
...
@@ -27,31 +27,42 @@
<div
class=
"gf-form-group"
>
<div
class=
"gf-form-group"
>
<h5
class=
"section-heading"
>
Alert Rule
</h5>
<h5
class=
"section-heading"
>
Alert Rule
</h5>
<div
class=
"gf-form-inline"
>
<div
class=
"gf-form-inline"
>
<div
class=
"gf-form"
>
<div
class=
"gf-form
max-width-30
"
>
<span
class=
"gf-form-label width-8"
>
Name
</span>
<span
class=
"gf-form-label width-8"
>
Name
</span>
<input
type=
"text"
class=
"gf-form-input width-22"
ng-model=
"ctrl.alert.name"
>
<input
type=
"text"
class=
"gf-form-input width-22"
ng-model=
"ctrl.alert.name"
>
</div>
</div>
<
div
class=
"gf-form"
>
<
!-- <div class="gf-form"> --
>
<span
class=
"gf-form-label"
>
Handler
</span
>
<!-- <span class="gf-form-label width-6">Handler</span> --
>
<div
class=
"gf-form-select-wrapper"
>
<!-- <div class="gf-form-select-wrapper"> --
>
<select
class=
"gf-form-input"
<!-- <select class="gf-form-input" -->
ng-model=
"ctrl.alert.handler"
<!-- ng-model="ctrl.alert.handler" -->
ng-options=
"f.value as f.text for f in ctrl.handlers"
>
<!-- ng-options="f.value as f.text for f in ctrl.handlers"> --
>
</select
>
<!-- </select> --
>
</div
>
<!-- </div> --
>
<
/div
>
<
!-- </div> --
>
<div
class=
"gf-form"
>
<div
class=
"gf-form"
>
<span
class=
"gf-form-label width-8"
>
Evaluate every
</span>
<span
class=
"gf-form-label width-8"
>
Evaluate every
</span>
<input
class=
"gf-form-input max-width-7"
type=
"text"
ng-model=
"ctrl.alert.frequency"
></input>
<input
class=
"gf-form-input max-width-7"
type=
"text"
ng-model=
"ctrl.alert.frequency"
></input>
</div>
</div>
</div>
</div>
<div
class=
"gf-form"
>
<div
class=
"gf-form-inline"
>
<span
class=
"gf-form-label width-8"
>
Notifications
</span>
<div
class=
"gf-form max-width-30"
>
<input
class=
"gf-form-input max-width-22"
type=
"text"
ng-model=
"ctrl.alert.notify"
></input>
<span
class=
"gf-form-label width-8"
>
Notifications
</span>
<input
class=
"gf-form-input max-width-22"
type=
"text"
ng-model=
"ctrl.alert.notifications"
></input>
</div>
<!--
<!--
<bootstrap-tagsinput ng-model="ctrl.alert.notify" tagclass="label label-tag" placeholder="add tags">
<bootstrap-tagsinput ng-model="ctrl.alert.notify" tagclass="label label-tag" placeholder="add tags">
</bootstrap-tagsinput>
</bootstrap-tagsinput>
-->
-->
<div
class=
"gf-form"
>
<span
class=
"gf-form-label width-8"
>
Severity
</span>
<div
class=
"gf-form-select-wrapper"
>
<select
class=
"gf-form-input"
ng-model=
"ctrl.alert.severity"
ng-options=
"f.value as f.text for f in ctrl.severityLevels"
>
</select>
</div>
</div>
</div>
</div>
</div>
</div>
...
@@ -59,7 +70,7 @@
...
@@ -59,7 +70,7 @@
<h5
class=
"section-heading"
>
Conditions
</h5>
<h5
class=
"section-heading"
>
Conditions
</h5>
<div
class=
"gf-form-inline"
ng-repeat=
"conditionModel in ctrl.conditionModels"
>
<div
class=
"gf-form-inline"
ng-repeat=
"conditionModel in ctrl.conditionModels"
>
<div
class=
"gf-form"
>
<div
class=
"gf-form"
>
<span
class=
"gf-form-label
"
>
{{$index+1}}
</span>
<span
class=
"gf-form-label
query-keyword"
>
AND
</span>
</div>
</div>
<div
class=
"gf-form"
>
<div
class=
"gf-form"
>
<query-part-editor
<query-part-editor
...
@@ -77,21 +88,11 @@
...
@@ -77,21 +88,11 @@
</query-part-editor>
</query-part-editor>
</div>
</div>
<div
class=
"gf-form"
>
<div
class=
"gf-form"
>
<span
class=
"gf-form-label"
>
<span
class=
"gf-form-label"
>
When Value
</span>
<i
class=
"icon-gf icon-gf-warn alert-icon-critical"
></i>
<metric-segment-model
property=
"conditionModel.evaluator.type"
options=
"ctrl.evalFunctions"
custom=
"false"
css-class=
"query-segment-operator"
on-change=
"ctrl.thresholdUpdated()"
></metric-segment-model>
Critcal if
<input
class=
"gf-form-input max-width-7"
type=
"number"
ng-model=
"conditionModel.evaluator.params[0]"
ng-change=
"ctrl.thresholdsUpdated()"
></input>
</span>
<metric-segment-model
property=
"ctrl.alert.crit.op"
options=
"ctrl.operatorList"
custom=
"false"
css-class=
"query-segment-operator"
on-change=
"ctrl.thresholdsUpdated()"
></metric-segment-model>
<input
class=
"gf-form-input max-width-7"
type=
"number"
ng-model=
"ctrl.alert.crit.value"
ng-change=
"ctrl.thresholdsUpdated()"
></input>
</div>
</div>
<div
class=
"gf-form"
>
<div
class=
"gf-form"
>
<span
class=
"gf-form-label"
>
<i
class=
"icon-gf icon-gf-warn alert-icon-warn"
></i>
Warn if
</span>
<metric-segment-model
property=
"ctrl.alert.warn.op"
options=
"ctrl.operatorList"
custom=
"false"
css-class=
"query-segment-operator"
on-change=
"ctrl.thresholdsUpdated()"
></metric-segment-model>
<input
class=
"gf-form-input max-width-7"
type=
"number"
ng-model=
"ctrl.alert.warn.value"
ng-change=
"ctrl.thresholdsUpdated()"
></input>
<label
class=
"gf-form-label"
>
<label
class=
"gf-form-label"
>
<a
class=
"pointer"
tabindex=
"1"
ng-click=
"ctrl.removeCondition($index)"
>
<a
class=
"pointer"
tabindex=
"1"
ng-click=
"ctrl.removeCondition($index)"
>
<i
class=
"fa fa-trash"
></i>
<i
class=
"fa fa-trash"
></i>
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment