-
Notifications
You must be signed in to change notification settings - Fork 1
/
loudml_plugin_slack.py
174 lines (136 loc) · 4.62 KB
/
loudml_plugin_slack.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
import logging
import json
from slacker import Slacker
from loudml.api import (
Hook,
Plugin,
)
from voluptuous import (
All,
Any,
Invalid,
Optional,
Required,
Schema,
)
class SlackPlugin(Plugin):
"""
LoudML Slack plug-in
"""
# Optional class to be defined if the plug-in requires an initialization
# on LoudML start up. If no initialization is needed, get rid of it.
CONFIG_SCHEMA = Schema({
Required('slack'): Schema({
Required('token'): str,
Required('channel'): str,
}),
})
def __init__(self, name, config_dir, *args, **kwargs):
# Load and validate plug-in configuration from
# /etc/loudml/plugins.d/<plugin name>.yml
super().__init__(name, config_dir, *args, **kwargs)
# ... put here additional initialization actions ...
self.slack = Slacker(self.config['slack']['token'])
@classmethod
def validate(cls, config):
"""
Validate and sanitize plug-in static configuration
"""
# Validate configuration against the schema
config = super().validate(config)
# ... put here additional validation if needed ...
# Return sanitized configuration
return config
class SlackHook(Hook):
TEMPLATES = {
'anomaly_start': {
'color': '#FF0000',
'content': 'Anomaly detected! date={date} score={score} reason={reason} predicted={predicted} observed={observed}',
},
'anomaly_end': {
'color': '#0000FF',
'content': 'Anomaly end date={date} score={score}',
},
}
CONFIG_SCHEMA = Schema({
Optional('templates', default=TEMPLATES): Schema({
Optional('anomaly_start', default=TEMPLATES['anomaly_start']): {
Optional('color', default=TEMPLATES['anomaly_start']['color']): str,
Optional('content', default=TEMPLATES['anomaly_start']['content']): str,
},
Optional('anomaly_end', default=TEMPLATES['anomaly_end']): {
Optional('color', default=TEMPLATES['anomaly_end']['color']): str,
Optional('content', default=TEMPLATES['anomaly_end']['content']): str,
},
}),
})
@classmethod
def validate(cls, config):
"""
Validate and sanitize hook configuration
"""
# Validate configuration against the schema
config = super().validate(config)
# ... put here additional validation if needed ...
# Return sanitized configuration
return config
def send_msg(self, template_name, *args, **kwargs):
"""
Build slack message from template and send it
"""
plugin_cfg = SlackPlugin.instance.config
slack_instance = SlackPlugin.instance.slack
if plugin_cfg is None:
logging.error("slack plug-in is not configured")
return
if slack_instance is None:
logging.error("slack plug-in is not configured")
return
channel_cfg = plugin_cfg['slack']['channel']
template = self.config['templates'][template_name]
try:
slack_instance.chat.post_message(channel_cfg, self.model['name'],
icon_emoji=':echobot:', as_user=False, link_names=True, username='LoudML Bot', attachments= '[{"color": "' + template['color'] + '", "text": "'+ template['content'].strip().format(**kwargs) +'"}]')
logging.info("sending alert to %s", channel_cfg)
except Exception as e:
logging.error("cannot send alert to %s, because %s", channel_cfg, str(e))
def on_anomaly_start(
self,
dt,
score,
predicted,
observed,
anomalies,
*args,
**kwargs
):
# Deal with anomaly notification here
try:
self.send_msg(
'anomaly_start',
date=str(dt.astimezone()),
score=score,
predicted=str(predicted),
observed=str(observed),
reason="\n".join(ano_desc),
**kwargs
)
except Exception as e:
logging.error("Error in anomaly_start.")
def on_anomaly_end(
self,
dt,
score,
*args,
**kwargs
):
# Deal with anomaly notification here
try:
self.send_msg(
'anomaly_end',
date=str(dt.astimezone()),
score=score,
**kwargs
)
except Exception as e:
logging.error("Error in anomaly_end.")