Skip to content

Commit baee5f9

Browse files
committed
feat: add grafana v10 AlertRule support
1 parent bc9e6e3 commit baee5f9

File tree

2 files changed

+109
-6
lines changed

2 files changed

+109
-6
lines changed

grafanalib/core.py

+68-6
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,7 @@ def to_json_data(self):
195195
EXP_TYPE_REDUCE = 'reduce'
196196
EXP_TYPE_RESAMPLE = 'resample'
197197
EXP_TYPE_MATH = 'math'
198+
EXP_TYPE_THRESHOLD = 'threshold'
198199

199200
# Alert Expression Reducer Function
200201
EXP_REDUCER_FUNC_MIN = 'min'
@@ -1418,6 +1419,23 @@ def to_json_data(self):
14181419

14191420
return expression
14201421

1422+
@attr.s
1423+
class AlertExpressionv10(AlertExpression):
1424+
"""
1425+
Specific to Grafana v10.x, adds support for threshold expression type
1426+
"""
1427+
1428+
expressionType = attr.ib(
1429+
default=EXP_TYPE_CLASSIC,
1430+
validator=in_([
1431+
EXP_TYPE_CLASSIC,
1432+
EXP_TYPE_REDUCE,
1433+
EXP_TYPE_RESAMPLE,
1434+
EXP_TYPE_MATH,
1435+
EXP_TYPE_THRESHOLD
1436+
])
1437+
)
1438+
14211439

14221440
@attr.s
14231441
class Alert(object):
@@ -1510,6 +1528,16 @@ def is_valid_triggersv9(instance, attribute, value):
15101528
is_valid_target(instance, "alert trigger target", trigger)
15111529

15121530

1531+
def is_valid_triggersv10(instance, attribute, value):
1532+
"""Validator for AlertRule triggers for Grafana v9"""
1533+
for trigger in value:
1534+
if not (isinstance(trigger, Target) or isinstance(trigger, AlertExpressionv10)):
1535+
raise ValueError(f"{attribute.name} must either be a Target or AlertExpressionV10")
1536+
1537+
if isinstance(trigger, Target):
1538+
is_valid_target(instance, "alert trigger target", trigger)
1539+
1540+
15131541
@attr.s
15141542
class AlertRulev8(object):
15151543
"""
@@ -1620,10 +1648,8 @@ def to_json_data(self):
16201648

16211649

16221650
@attr.s
1623-
class AlertRulev9(object):
1651+
class _BaseAlertRule(object):
16241652
"""
1625-
Create a Grafana 9.x+ Alert Rule
1626-
16271653
:param title: The alert's title, must be unique per folder
16281654
:param triggers: A list of Targets and AlertConditions.
16291655
The Target specifies the query, and the AlertCondition specifies how this is used to alert.
@@ -1650,7 +1676,6 @@ class AlertRulev9(object):
16501676
"""
16511677

16521678
title = attr.ib()
1653-
triggers = attr.ib(factory=list, validator=is_valid_triggersv9)
16541679
annotations = attr.ib(factory=dict, validator=instance_of(dict))
16551680
labels = attr.ib(factory=dict, validator=instance_of(dict))
16561681

@@ -1678,7 +1703,7 @@ class AlertRulev9(object):
16781703
dashboard_uid = attr.ib(default="", validator=instance_of(str))
16791704
panel_id = attr.ib(default=0, validator=instance_of(int))
16801705

1681-
def to_json_data(self):
1706+
def _render_triggers(self):
16821707
data = []
16831708

16841709
for trigger in self.triggers:
@@ -1696,6 +1721,21 @@ def to_json_data(self):
16961721
else:
16971722
data += [trigger.to_json_data()]
16981723

1724+
return data
1725+
1726+
def to_json_data(self):
1727+
pass
1728+
1729+
1730+
@attr.s
1731+
class AlertRulev9(_BaseAlertRule):
1732+
"""
1733+
Create a Grafana 9.x+ Alert Rule
1734+
"""
1735+
1736+
triggers = attr.ib(factory=list, validator=is_valid_triggersv9)
1737+
1738+
def to_json_data(self):
16991739
return {
17001740
"uid": self.uid,
17011741
"for": self.evaluateFor,
@@ -1704,13 +1744,35 @@ def to_json_data(self):
17041744
"grafana_alert": {
17051745
"title": self.title,
17061746
"condition": self.condition,
1707-
"data": data,
1747+
"data": self._render_triggers(),
17081748
"no_data_state": self.noDataAlertState,
17091749
"exec_err_state": self.errorAlertState,
17101750
},
17111751
}
17121752

17131753

1754+
@attr.s
1755+
class AlertRulev10(_BaseAlertRule):
1756+
"""
1757+
Create a Grafana 10.x+ Alert Rule
1758+
"""
1759+
1760+
triggers = attr.ib(factory=list, validator=is_valid_triggersv10)
1761+
1762+
def to_json_data(self):
1763+
return {
1764+
"uid": self.uid,
1765+
"for": self.evaluateFor,
1766+
"labels": self.labels,
1767+
"annotations": self.annotations,
1768+
"title": self.title,
1769+
"condition": self.condition,
1770+
"data": self._render_triggers(),
1771+
"noDataState": self.noDataAlertState,
1772+
"execErrState": self.errorAlertState,
1773+
}
1774+
1775+
17141776
@attr.s
17151777
class AlertFileBasedProvisioning(object):
17161778
"""

grafanalib/tests/test_core.py

+41
Original file line numberDiff line numberDiff line change
@@ -908,6 +908,47 @@ def test_alertrulev9():
908908
assert data['grafana_alert']['condition'] == condition
909909

910910

911+
def test_alertrulev10():
912+
title = "My Important Alert!"
913+
annotations = {"summary": "this alert fires when prod is down!!!"}
914+
labels = {"severity": "serious"}
915+
condition = 'C'
916+
rule = G.AlertRulev10(
917+
title=title,
918+
uid='alert1',
919+
condition=condition,
920+
triggers=[
921+
G.Target(
922+
expr='query',
923+
refId='A',
924+
datasource='Prometheus',
925+
),
926+
G.AlertExpression(
927+
refId='B',
928+
expressionType=G.EXP_TYPE_CLASSIC,
929+
expression='A',
930+
conditions=[
931+
G.AlertCondition(
932+
evaluator=G.GreaterThan(3),
933+
operator=G.OP_AND,
934+
reducerType=G.RTYPE_LAST
935+
)
936+
]
937+
),
938+
],
939+
annotations=annotations,
940+
labels=labels,
941+
evaluateFor="3m",
942+
)
943+
944+
data = rule.to_json_data()
945+
assert data['annotations'] == annotations
946+
assert data['labels'] == labels
947+
assert data['for'] == "3m"
948+
assert data['title'] == title
949+
assert data['condition'] == condition
950+
951+
911952
def test_alertexpression():
912953
refId = 'D'
913954
expression = 'C'

0 commit comments

Comments
 (0)