Skip to content

Commit fd621a1

Browse files
committed
Base client for Hawkular Alerts, can create and list triggers, also added groups support.
1 parent 27f95d8 commit fd621a1

4 files changed

Lines changed: 593 additions & 1 deletion

File tree

hawkular/__init__.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,19 @@
11
from .metrics import HawkularMetricsClient, MetricType, Availability
2+
from .alerts import HawkularAlertsClient, Trigger, FullTrigger, Condition, Dampening
23

3-
__all__ = ['HawkularMetricsClient', 'MetricType', 'Availability']
4+
__all__ = ['HawkularMetricsClient',
5+
'MetricType',
6+
'Availability',
7+
'HawkularAlertsClient',
8+
'Trigger',
9+
'Condition',
10+
'Dampening'
11+
'FullTrigger',
12+
'GroupMemberInfo'
13+
'GroupConditionsInfo',
14+
'TriggerType',
15+
'TriggerMode',
16+
'DampeningType',
17+
'ConditionType',
18+
'Operator',
19+
'Severity']

hawkular/alerts.py

Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
"""
2+
Copyright 2015-2016 Red Hat, Inc. and/or its affiliates
3+
and other contributors.
4+
5+
Licensed under the Apache License, Version 2.0 (the "License");
6+
you may not use this file except in compliance with the License.
7+
You may obtain a copy of the License at
8+
9+
http://www.apache.org/licenses/LICENSE-2.0
10+
11+
Unless required by applicable law or agreed to in writing, software
12+
distributed under the License is distributed on an "AS IS" BASIS,
13+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
See the License for the specific language governing permissions and
15+
limitations under the License.
16+
"""
17+
18+
from client import ApiOject, HawkularBaseClient
19+
20+
21+
class Trigger(ApiOject):
22+
__slots__ = [
23+
'id', 'name', 'description', 'type', 'event_type', 'event_category',
24+
'event_text', 'event_category', 'event_text', 'severity', 'context',
25+
'tags', 'actions', 'auto_disable', 'auto_enable', 'auto_resolve',
26+
'auto_resolve_alerts', 'auto_resolve_match', 'data_id_map', 'member_of',
27+
'enabled', 'firing_match', 'source'
28+
]
29+
30+
31+
class Condition(ApiOject):
32+
__slots__ = [
33+
'trigger_id', 'trigger_mode', 'type', 'condition_set_size',
34+
'condition_set_index', 'condition_id', 'context', 'data_id',
35+
'operator', 'data2_id', 'data2_multiplier', 'pattern', 'ignore_case',
36+
'threshold', 'operator_low', 'operator_high', 'threshold_low', 'threshold_high',
37+
'in_range', 'alerter_id', 'expression', 'direction', 'period', 'interval'
38+
]
39+
40+
41+
class Dampening(ApiOject):
42+
__slots__ = [
43+
'trigger_id', 'trigger_mode', 'type', 'eval_true_setting',
44+
'eval_total_setting', 'eval_time_setting', 'dampening_id'
45+
]
46+
47+
48+
class FullTrigger(ApiOject):
49+
defaults = {
50+
'conditions': [],
51+
'dampenings': []
52+
}
53+
__slots__ = [
54+
'trigger', 'dampenings', 'conditions'
55+
]
56+
57+
def __init__(self, dictionary=dict()):
58+
udict = FullTrigger.transform_dict_to_underscore(dictionary)
59+
self.trigger = Trigger(udict.get('trigger'))
60+
self.dampenings = Dampening.list_to_object_list(udict.get('dampenings'))
61+
self.conditions = Dampening.list_to_object_list(udict.get('conditions'))
62+
63+
64+
class GroupMemberInfo(ApiOject):
65+
__slots__ = [
66+
'group_id', 'member_id', 'member_name', 'member_description', 'member_context',
67+
'member_tags', 'data_id_map'
68+
]
69+
70+
71+
class GroupConditionsInfo(ApiOject):
72+
__slots__ = [
73+
'conditions', 'data_id_member_map'
74+
]
75+
76+
defaults = {
77+
'conditions': []
78+
}
79+
80+
def addCondition(self, c):
81+
self.conditions.append(c)
82+
83+
84+
class TriggerType:
85+
STANDARD = 'STANDARD'
86+
GROUP = 'GROUP'
87+
DATA_DRIVEN_GROUP = 'DATA_DRIVEN_GROUP'
88+
MEMBER = 'MEMBER'
89+
ORPHAN = 'ORPHAN'
90+
91+
92+
class TriggerMode:
93+
FIRING = 'FIRING'
94+
AUTORESOLVE = 'AUTORESOLVE'
95+
96+
97+
class DampeningType:
98+
STRICT = 'STRICT'
99+
RELAXED_COUNT = 'RELAXED_COUNT'
100+
RELAXED_TIME = 'RELAXED_TIME'
101+
STRICT_TIME = 'STRICT_TIME'
102+
STRICT_TIMEOUT = 'STRICT_TIMEOUT'
103+
104+
105+
class ConditionType:
106+
AVAILABILITY = 'AVAILABILITY'
107+
COMPARE = 'COMPARE'
108+
STRING = 'STRING'
109+
THRESHOLD = 'THRESHOLD'
110+
RANGE = 'RANGE'
111+
EXTERNAL = 'EXTERNAL'
112+
EVENT = 'EVENT'
113+
RATE = 'RATE'
114+
MISSING = 'MISSING'
115+
116+
117+
class Operator:
118+
LT = 'LT'
119+
GT = 'GT'
120+
LTE = 'LTE'
121+
GTE = 'GTE'
122+
123+
124+
class Severity:
125+
LOW = 'LOW'
126+
MEDIUM = 'MEDIUM'
127+
HIGH = 'HIGH'
128+
CRITICAL = 'CRITICAL'
129+
130+
131+
class HawkularAlertsClient(HawkularBaseClient):
132+
def list_triggers(self, ids=[], tags=[]):
133+
ids = ','.join(ids)
134+
tags = ','.join(tags)
135+
url = self._service_url('triggers', {'tags': tags, 'ids': ids})
136+
triggers_dict = self._get(url)
137+
return Trigger.list_to_object_list(triggers_dict)
138+
139+
def create_trigger(self, trigger):
140+
data = self._serialize_object(trigger)
141+
if isinstance(trigger, FullTrigger):
142+
returned_dict = self._post(self._service_url(['triggers', 'trigger']), data)
143+
return FullTrigger(returned_dict)
144+
else:
145+
returned_dict = self._post(self._service_url('triggers'), data)
146+
return Trigger(returned_dict)
147+
148+
def get_trigger(self, trigger_id, full=False):
149+
if full:
150+
returned_dict = self._get(self._service_url(['triggers', 'trigger', trigger_id]))
151+
return FullTrigger(returned_dict)
152+
else:
153+
returned_dict = self._get(self._service_url(['triggers', trigger_id]))
154+
return Trigger(returned_dict)
155+
156+
def create_group_trigger(self, trigger):
157+
data = self._serialize_object(trigger)
158+
return Trigger(self._post(self._service_url(['triggers', 'groups']), data))
159+
160+
def create_group_member(self, member):
161+
data = self._serialize_object(member)
162+
return Trigger(self._post(self._service_url(['triggers', 'groups', 'members']), data))
163+
164+
def create_group_conditions(self, group_id, trigger_mode, conditions):
165+
data = self._serialize_object(conditions)
166+
url = self._service_url(['triggers', 'groups', group_id, 'conditions', trigger_mode])
167+
response = self._put(url, data)
168+
return Condition.list_to_object_list(response)
169+
170+
def list_dampenings(self, trigger_id):
171+
url = self._service_url(['triggers', trigger_id, 'dampenings'])
172+
data = self._get(url)
173+
return Dampening.list_to_object_list(data)

hawkular/alerts_test.py

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
"""
2+
Copyright 2015-2016 Red Hat, Inc. and/or its affiliates
3+
and other contributors.
4+
5+
Licensed under the Apache License, Version 2.0 (the "License");
6+
you may not use this file except in compliance with the License.
7+
You may obtain a copy of the License at
8+
9+
http://www.apache.org/licenses/LICENSE-2.0
10+
11+
Unless required by applicable law or agreed to in writing, software
12+
distributed under the License is distributed on an "AS IS" BASIS,
13+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
See the License for the specific language governing permissions and
15+
limitations under the License.
16+
"""
17+
from __future__ import unicode_literals
18+
19+
import unittest
20+
import uuid
21+
from alerts import *
22+
23+
24+
class TestAlertsFunctionsBase(unittest.TestCase):
25+
def setUp(self):
26+
self.maxDiff = None
27+
self.test_tenant = str(uuid.uuid4())
28+
self.client = HawkularAlertsClient(tenant_id=self.test_tenant,
29+
port=8080,
30+
username='jdoe',
31+
password='password')
32+
33+
34+
class MetricsTestCase(TestAlertsFunctionsBase):
35+
def test_trigger_creation(self):
36+
trigger = Trigger()
37+
trigger.id = 'id_1'
38+
trigger.name = 'test_trigger'
39+
created_trigger = self.client.create_trigger(trigger)
40+
self.assertEqual(trigger.id, created_trigger.id)
41+
triggers = self.client.list_triggers()
42+
self.assertEqual(1, len(triggers))
43+
self.assertEqual(triggers[0].id, trigger.id)
44+
45+
def test_full_trigger_creation(self):
46+
trigger = Trigger()
47+
trigger.id = 'id_1'
48+
trigger.name = 'test_trigger'
49+
full_trigger = FullTrigger()
50+
full_trigger.trigger = trigger
51+
52+
# Creating dampening using object attributes.
53+
54+
dampening1 = Dampening()
55+
dampening1.dampening_id = 'damp_1'
56+
dampening1.trigger_mode = TriggerMode.FIRING
57+
dampening1.type = DampeningType.STRICT
58+
59+
# Creating dampening using a hash.
60+
61+
dampening2 = Dampening({
62+
'dampening_id': 'damp_2',
63+
'trigger_mode': TriggerMode.AUTORESOLVE,
64+
'type': DampeningType.RELAXED_COUNT
65+
})
66+
67+
condition1 = Condition()
68+
condition1.trigger_mode = TriggerMode.AUTORESOLVE
69+
condition1.type = ConditionType.THRESHOLD
70+
condition1.data_id = 'did1'
71+
condition1.threshold = 5
72+
condition1.operator = Operator.LT
73+
74+
condition2 = Condition()
75+
condition2.trigger_mode = TriggerMode.AUTORESOLVE
76+
condition2.type = ConditionType.THRESHOLD
77+
condition2.data_id = 'did2'
78+
condition2.threshold = 5
79+
condition2.operator = Operator.GT
80+
81+
condition3 = Condition()
82+
condition3.trigger_mode = TriggerMode.AUTORESOLVE
83+
condition3.type = ConditionType.THRESHOLD
84+
condition3.data_id = 'did3'
85+
condition3.threshold = 5
86+
condition3.operator = Operator.GTE
87+
88+
full_trigger.dampenings.append(dampening1)
89+
full_trigger.dampenings.append(dampening2)
90+
full_trigger.conditions = [condition1, condition2, condition3]
91+
92+
created_trigger = self.client.create_trigger(full_trigger)
93+
self.assertEqual(trigger.id, created_trigger.trigger.id)
94+
95+
# Check if the trigger appears on the list.
96+
97+
triggers = self.client.list_triggers()
98+
self.assertEqual(1, len(triggers))
99+
self.assertEqual(triggers[0].id, trigger.id)
100+
101+
# Check if it is possible to get the full trigger.
102+
103+
created_full_trigger = self.client.get_trigger('id_1', True)
104+
105+
self.assertTrue(isinstance(created_full_trigger, FullTrigger))
106+
self.assertEqual(created_full_trigger.trigger.id, trigger.id)
107+
self.assertEqual(len(created_full_trigger.dampenings), 2)
108+
self.assertEqual(len(created_full_trigger.conditions), 3)
109+
110+
# Check for dampenings.
111+
112+
dampenings = self.client.list_dampenings('id_1')
113+
self.assertEqual(2, len(dampenings))
114+
self.assertEqual(dampenings[0].trigger_id, trigger.id)
115+
self.assertEqual(dampenings[1].trigger_id, trigger.id)
116+
117+
def test_create_group_trigger(self):
118+
trigger = Trigger()
119+
trigger.id = 'group_trigger_1'
120+
trigger.name = 'group_trigger_test'
121+
self.client.create_group_trigger(trigger)
122+
created_group_trigger = created_full_trigger = self.client.get_trigger('group_trigger_1')
123+
self.assertEqual(created_group_trigger.id, trigger.id)
124+
self.assertEqual(created_group_trigger.name, trigger.name)
125+
126+
def test_create_groups(self):
127+
# Create a group trigger
128+
t = Trigger()
129+
t.enabled = False
130+
t.id = 'a-group-trigger'
131+
t.name = 'A Group Trigger'
132+
t.severity = Severity.HIGH
133+
t.description = 'A Group Trigger generated from test'
134+
135+
# Create a condition
136+
c = Condition()
137+
c.trigger_mode = TriggerMode.FIRING
138+
c.type = ConditionType.THRESHOLD
139+
c.data_id = 'my-metric-id'
140+
c.operator = Operator.LT
141+
c.threshold = 5
142+
143+
gc = GroupConditionsInfo()
144+
gc.addCondition(c)
145+
146+
# Create a member
147+
m1 = GroupMemberInfo()
148+
m1.group_id = 'a-group-trigger'
149+
m1.member_id = 'member1'
150+
m1.member_name = 'Member One'
151+
m1.data_id_map = {'my-metric-id': 'my-metric-id-member1'}
152+
153+
tc = self.client.create_group_trigger(t)
154+
self.assertEqual(tc.type, TriggerType.GROUP)
155+
gcc = self.client.create_group_conditions(t.id, TriggerMode.FIRING, gc)
156+
self.assertEqual(len(gcc), 1)
157+
t_m1c = self.client.create_group_member(m1)
158+
self.assertEqual(t_m1c.type, TriggerType.MEMBER)

0 commit comments

Comments
 (0)