##// END OF EJS Templates
alert_channels: allow binding to resources
ergo -
Show More
@@ -1,305 +1,305 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright 2010 - 2017 RhodeCode GmbH and the AppEnlight project authors
3 # Copyright 2010 - 2017 RhodeCode GmbH and the AppEnlight project authors
4 #
4 #
5 # Licensed under the Apache License, Version 2.0 (the "License");
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with 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
7 # You may obtain a copy of the License at
8 #
8 #
9 # http://www.apache.org/licenses/LICENSE-2.0
9 # http://www.apache.org/licenses/LICENSE-2.0
10 #
10 #
11 # Unless required by applicable law or agreed to in writing, software
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
15 # limitations under the License.
16
16
17 import logging
17 import logging
18 import sqlalchemy as sa
18 import sqlalchemy as sa
19 import urllib.request, urllib.parse, urllib.error
19 import urllib.request, urllib.parse, urllib.error
20 from datetime import timedelta
20 from datetime import timedelta
21 from appenlight.models import Base
21 from appenlight.models import Base
22 from appenlight.lib.utils.date_utils import convert_date
22 from appenlight.lib.utils.date_utils import convert_date
23 from sqlalchemy.dialects.postgresql import JSON
23 from sqlalchemy.dialects.postgresql import JSON
24 from ziggurat_foundations.models.base import BaseModel
24 from ziggurat_foundations.models.base import BaseModel
25
25
26 log = logging.getLogger(__name__)
26 log = logging.getLogger(__name__)
27
27
28 #
28 #
29 channel_rules_m2m_table = sa.Table(
29 channel_rules_m2m_table = sa.Table(
30 'channels_actions', Base.metadata,
30 'channels_actions', Base.metadata,
31 sa.Column('channel_pkey', sa.Integer,
31 sa.Column('channel_pkey', sa.Integer,
32 sa.ForeignKey('alert_channels.pkey')),
32 sa.ForeignKey('alert_channels.pkey')),
33 sa.Column('action_pkey', sa.Integer,
33 sa.Column('action_pkey', sa.Integer,
34 sa.ForeignKey('alert_channels_actions.pkey'))
34 sa.ForeignKey('alert_channels_actions.pkey'))
35 )
35 )
36
36
37 channel_resources_m2m_table = sa.Table(
37 channel_resources_m2m_table = sa.Table(
38 'channels_resources', Base.metadata,
38 'channels_resources', Base.metadata,
39 sa.Column('channel_pkey', sa.Integer,
39 sa.Column('channel_pkey', sa.Integer,
40 sa.ForeignKey('alert_channels.pkey')),
40 sa.ForeignKey('alert_channels.pkey')),
41 sa.Column('resource_id', sa.Integer,
41 sa.Column('resource_id', sa.Integer,
42 sa.ForeignKey('resources.resource_id'))
42 sa.ForeignKey('resources.resource_id'))
43 )
43 )
44
44
45 DATE_FRMT = '%Y-%m-%dT%H:%M'
45 DATE_FRMT = '%Y-%m-%dT%H:%M'
46
46
47
47
48 class AlertChannel(Base, BaseModel):
48 class AlertChannel(Base, BaseModel):
49 """
49 """
50 Stores information about possible alerting options
50 Stores information about possible alerting options
51 """
51 """
52 __tablename__ = 'alert_channels'
52 __tablename__ = 'alert_channels'
53 __possible_channel_names__ = ['email']
53 __possible_channel_names__ = ['email']
54 __mapper_args__ = {
54 __mapper_args__ = {
55 'polymorphic_on': 'channel_name',
55 'polymorphic_on': 'channel_name',
56 'polymorphic_identity': 'integration'
56 'polymorphic_identity': 'integration'
57 }
57 }
58
58
59 owner_id = sa.Column(sa.Unicode(30),
59 owner_id = sa.Column(sa.Unicode(30),
60 sa.ForeignKey('users.id', onupdate='CASCADE',
60 sa.ForeignKey('users.id', onupdate='CASCADE',
61 ondelete='CASCADE'))
61 ondelete='CASCADE'))
62 channel_name = sa.Column(sa.Unicode(25), nullable=False)
62 channel_name = sa.Column(sa.Unicode(25), nullable=False)
63 channel_value = sa.Column(sa.Unicode(80), nullable=False, default='')
63 channel_value = sa.Column(sa.Unicode(80), nullable=False, default='')
64 channel_json_conf = sa.Column(JSON(), nullable=False, default='')
64 channel_json_conf = sa.Column(JSON(), nullable=False, default='')
65 channel_validated = sa.Column(sa.Boolean, nullable=False,
65 channel_validated = sa.Column(sa.Boolean, nullable=False,
66 default=False)
66 default=False)
67 send_alerts = sa.Column(sa.Boolean, nullable=False,
67 send_alerts = sa.Column(sa.Boolean, nullable=False,
68 default=True)
68 default=True)
69 daily_digest = sa.Column(sa.Boolean, nullable=False,
69 daily_digest = sa.Column(sa.Boolean, nullable=False,
70 default=True)
70 default=True)
71 integration_id = sa.Column(sa.Integer, sa.ForeignKey('integrations.id'),
71 integration_id = sa.Column(sa.Integer, sa.ForeignKey('integrations.id'),
72 nullable=True)
72 nullable=True)
73 pkey = sa.Column(sa.Integer(), nullable=False, primary_key=True)
73 pkey = sa.Column(sa.Integer(), nullable=False, primary_key=True)
74
74
75 channel_actions = sa.orm.relationship('AlertChannelAction',
75 channel_actions = sa.orm.relationship('AlertChannelAction',
76 cascade="all",
76 cascade="all",
77 passive_deletes=True,
77 passive_deletes=True,
78 passive_updates=True,
78 passive_updates=True,
79 secondary=channel_rules_m2m_table,
79 secondary=channel_rules_m2m_table,
80 backref='channels')
80 backref='channels')
81 resources = sa.orm.relationship('Resource',
81 resources = sa.orm.relationship('Resource',
82 cascade="all, delete-orphan",
82 cascade="all",
83 passive_deletes=True,
83 passive_deletes=True,
84 passive_updates=True,
84 passive_updates=True,
85 secondary=channel_resources_m2m_table,
85 secondary=channel_resources_m2m_table,
86 backref='resources')
86 backref='resources')
87
87
88 @property
88 @property
89 def channel_visible_value(self):
89 def channel_visible_value(self):
90 if self.integration:
90 if self.integration:
91 return '{}: {}'.format(
91 return '{}: {}'.format(
92 self.channel_name,
92 self.channel_name,
93 self.integration.resource.resource_name
93 self.integration.resource.resource_name
94 )
94 )
95
95
96 return '{}: {}'.format(
96 return '{}: {}'.format(
97 self.channel_name,
97 self.channel_name,
98 self.channel_value
98 self.channel_value
99 )
99 )
100
100
101 def get_dict(self, exclude_keys=None, include_keys=None,
101 def get_dict(self, exclude_keys=None, include_keys=None,
102 extended_info=True):
102 extended_info=True):
103 """
103 """
104 Returns dictionary with required information that will be consumed by
104 Returns dictionary with required information that will be consumed by
105 angular
105 angular
106 """
106 """
107 instance_dict = super(AlertChannel, self).get_dict(exclude_keys,
107 instance_dict = super(AlertChannel, self).get_dict(exclude_keys,
108 include_keys)
108 include_keys)
109 exclude_keys_list = exclude_keys or []
109 exclude_keys_list = exclude_keys or []
110 include_keys_list = include_keys or []
110 include_keys_list = include_keys or []
111
111
112 instance_dict['supports_report_alerting'] = True
112 instance_dict['supports_report_alerting'] = True
113 instance_dict['channel_visible_value'] = self.channel_visible_value
113 instance_dict['channel_visible_value'] = self.channel_visible_value
114
114
115 if extended_info:
115 if extended_info:
116 instance_dict['actions'] = [
116 instance_dict['actions'] = [
117 rule.get_dict(extended_info=True) for
117 rule.get_dict(extended_info=True) for
118 rule in self.channel_actions]
118 rule in self.channel_actions]
119
119
120 del instance_dict['channel_json_conf']
120 del instance_dict['channel_json_conf']
121
121
122 if self.integration:
122 if self.integration:
123 instance_dict[
123 instance_dict[
124 'supports_report_alerting'] = \
124 'supports_report_alerting'] = \
125 self.integration.supports_report_alerting
125 self.integration.supports_report_alerting
126 d = {}
126 d = {}
127 for k in instance_dict.keys():
127 for k in instance_dict.keys():
128 if (k not in exclude_keys_list and
128 if (k not in exclude_keys_list and
129 (k in include_keys_list or not include_keys)):
129 (k in include_keys_list or not include_keys)):
130 d[k] = instance_dict[k]
130 d[k] = instance_dict[k]
131 return d
131 return d
132
132
133 def __repr__(self):
133 def __repr__(self):
134 return '<AlertChannel: (%s,%s), user:%s>' % (self.channel_name,
134 return '<AlertChannel: (%s,%s), user:%s>' % (self.channel_name,
135 self.channel_value,
135 self.channel_value,
136 self.user_name,)
136 self.user_name,)
137
137
138 def send_digest(self, **kwargs):
138 def send_digest(self, **kwargs):
139 """
139 """
140 This should implement daily top error report notifications
140 This should implement daily top error report notifications
141 """
141 """
142 log.warning('send_digest NOT IMPLEMENTED')
142 log.warning('send_digest NOT IMPLEMENTED')
143
143
144 def notify_reports(self, **kwargs):
144 def notify_reports(self, **kwargs):
145 """
145 """
146 This should implement notification of reports that occured in 1 min
146 This should implement notification of reports that occured in 1 min
147 interval
147 interval
148 """
148 """
149 log.warning('notify_reports NOT IMPLEMENTED')
149 log.warning('notify_reports NOT IMPLEMENTED')
150
150
151 def notify_alert(self, **kwargs):
151 def notify_alert(self, **kwargs):
152 """
152 """
153 Notify user of report/uptime/chart threshold events based on events alert
153 Notify user of report/uptime/chart threshold events based on events alert
154 type
154 type
155
155
156 Kwargs:
156 Kwargs:
157 application: application that the event applies for,
157 application: application that the event applies for,
158 event: event that is notified,
158 event: event that is notified,
159 user: user that should be notified
159 user: user that should be notified
160 request: request object
160 request: request object
161
161
162 """
162 """
163 alert_name = kwargs['event'].unified_alert_name()
163 alert_name = kwargs['event'].unified_alert_name()
164 if alert_name in ['slow_report_alert', 'error_report_alert']:
164 if alert_name in ['slow_report_alert', 'error_report_alert']:
165 self.notify_report_alert(**kwargs)
165 self.notify_report_alert(**kwargs)
166 elif alert_name == 'uptime_alert':
166 elif alert_name == 'uptime_alert':
167 self.notify_uptime_alert(**kwargs)
167 self.notify_uptime_alert(**kwargs)
168 elif alert_name == 'chart_alert':
168 elif alert_name == 'chart_alert':
169 self.notify_chart_alert(**kwargs)
169 self.notify_chart_alert(**kwargs)
170
170
171 def notify_chart_alert(self, **kwargs):
171 def notify_chart_alert(self, **kwargs):
172 """
172 """
173 This should implement report open/close alerts notifications
173 This should implement report open/close alerts notifications
174 """
174 """
175 log.warning('notify_chart_alert NOT IMPLEMENTED')
175 log.warning('notify_chart_alert NOT IMPLEMENTED')
176
176
177 def notify_report_alert(self, **kwargs):
177 def notify_report_alert(self, **kwargs):
178 """
178 """
179 This should implement report open/close alerts notifications
179 This should implement report open/close alerts notifications
180 """
180 """
181 log.warning('notify_report_alert NOT IMPLEMENTED')
181 log.warning('notify_report_alert NOT IMPLEMENTED')
182
182
183 def notify_uptime_alert(self, **kwargs):
183 def notify_uptime_alert(self, **kwargs):
184 """
184 """
185 This should implement uptime open/close alerts notifications
185 This should implement uptime open/close alerts notifications
186 """
186 """
187 log.warning('notify_uptime_alert NOT IMPLEMENTED')
187 log.warning('notify_uptime_alert NOT IMPLEMENTED')
188
188
189 def get_notification_basic_vars(self, kwargs):
189 def get_notification_basic_vars(self, kwargs):
190 """
190 """
191 Sets most common variables used later for rendering notifications for
191 Sets most common variables used later for rendering notifications for
192 channel
192 channel
193 """
193 """
194 if 'event' in kwargs:
194 if 'event' in kwargs:
195 kwargs['since_when'] = kwargs['event'].start_date
195 kwargs['since_when'] = kwargs['event'].start_date
196
196
197 url_start_date = kwargs.get('since_when') - timedelta(minutes=1)
197 url_start_date = kwargs.get('since_when') - timedelta(minutes=1)
198 url_end_date = kwargs.get('since_when') + timedelta(minutes=4)
198 url_end_date = kwargs.get('since_when') + timedelta(minutes=4)
199 tmpl_vars = {
199 tmpl_vars = {
200 "timestamp": kwargs['since_when'],
200 "timestamp": kwargs['since_when'],
201 "user": kwargs['user'],
201 "user": kwargs['user'],
202 "since_when": kwargs.get('since_when'),
202 "since_when": kwargs.get('since_when'),
203 "url_start_date": url_start_date,
203 "url_start_date": url_start_date,
204 "url_end_date": url_end_date
204 "url_end_date": url_end_date
205 }
205 }
206 tmpl_vars["resource_name"] = kwargs['resource'].resource_name
206 tmpl_vars["resource_name"] = kwargs['resource'].resource_name
207 tmpl_vars["resource"] = kwargs['resource']
207 tmpl_vars["resource"] = kwargs['resource']
208
208
209 if 'event' in kwargs:
209 if 'event' in kwargs:
210 tmpl_vars['event_values'] = kwargs['event'].values
210 tmpl_vars['event_values'] = kwargs['event'].values
211 tmpl_vars['alert_type'] = kwargs['event'].unified_alert_name()
211 tmpl_vars['alert_type'] = kwargs['event'].unified_alert_name()
212 tmpl_vars['alert_action'] = kwargs['event'].unified_alert_action()
212 tmpl_vars['alert_action'] = kwargs['event'].unified_alert_action()
213 return tmpl_vars
213 return tmpl_vars
214
214
215 def report_alert_notification_vars(self, kwargs):
215 def report_alert_notification_vars(self, kwargs):
216 tmpl_vars = self.get_notification_basic_vars(kwargs)
216 tmpl_vars = self.get_notification_basic_vars(kwargs)
217 reports = kwargs.get('reports', [])
217 reports = kwargs.get('reports', [])
218 tmpl_vars["reports"] = reports
218 tmpl_vars["reports"] = reports
219 tmpl_vars["confirmed_total"] = len(reports)
219 tmpl_vars["confirmed_total"] = len(reports)
220
220
221 tmpl_vars["report_type"] = "error reports"
221 tmpl_vars["report_type"] = "error reports"
222 tmpl_vars["url_report_type"] = 'report/list'
222 tmpl_vars["url_report_type"] = 'report/list'
223
223
224 alert_type = tmpl_vars.get('alert_type', '')
224 alert_type = tmpl_vars.get('alert_type', '')
225 if 'slow_report' in alert_type:
225 if 'slow_report' in alert_type:
226 tmpl_vars["report_type"] = "slow reports"
226 tmpl_vars["report_type"] = "slow reports"
227 tmpl_vars["url_report_type"] = 'report/list_slow'
227 tmpl_vars["url_report_type"] = 'report/list_slow'
228
228
229 app_url = kwargs['request'].registry.settings['_mail_url']
229 app_url = kwargs['request'].registry.settings['_mail_url']
230
230
231 destination_url = kwargs['request'].route_url('/',
231 destination_url = kwargs['request'].route_url('/',
232 _app_url=app_url)
232 _app_url=app_url)
233 if alert_type:
233 if alert_type:
234 destination_url += 'ui/{}?resource={}&start_date={}&end_date={}'.format(
234 destination_url += 'ui/{}?resource={}&start_date={}&end_date={}'.format(
235 tmpl_vars["url_report_type"],
235 tmpl_vars["url_report_type"],
236 tmpl_vars['resource'].resource_id,
236 tmpl_vars['resource'].resource_id,
237 tmpl_vars['url_start_date'].strftime(DATE_FRMT),
237 tmpl_vars['url_start_date'].strftime(DATE_FRMT),
238 tmpl_vars['url_end_date'].strftime(DATE_FRMT)
238 tmpl_vars['url_end_date'].strftime(DATE_FRMT)
239 )
239 )
240 else:
240 else:
241 destination_url += 'ui/{}?resource={}'.format(
241 destination_url += 'ui/{}?resource={}'.format(
242 tmpl_vars["url_report_type"],
242 tmpl_vars["url_report_type"],
243 tmpl_vars['resource'].resource_id
243 tmpl_vars['resource'].resource_id
244 )
244 )
245 tmpl_vars["destination_url"] = destination_url
245 tmpl_vars["destination_url"] = destination_url
246
246
247 return tmpl_vars
247 return tmpl_vars
248
248
249 def uptime_alert_notification_vars(self, kwargs):
249 def uptime_alert_notification_vars(self, kwargs):
250 tmpl_vars = self.get_notification_basic_vars(kwargs)
250 tmpl_vars = self.get_notification_basic_vars(kwargs)
251 app_url = kwargs['request'].registry.settings['_mail_url']
251 app_url = kwargs['request'].registry.settings['_mail_url']
252 destination_url = kwargs['request'].route_url('/', _app_url=app_url)
252 destination_url = kwargs['request'].route_url('/', _app_url=app_url)
253 destination_url += 'ui/{}?resource={}'.format(
253 destination_url += 'ui/{}?resource={}'.format(
254 'uptime',
254 'uptime',
255 tmpl_vars['resource'].resource_id)
255 tmpl_vars['resource'].resource_id)
256 tmpl_vars['destination_url'] = destination_url
256 tmpl_vars['destination_url'] = destination_url
257
257
258 reason = ''
258 reason = ''
259 e_values = tmpl_vars.get('event_values')
259 e_values = tmpl_vars.get('event_values')
260
260
261 if e_values and e_values.get('response_time') == 0:
261 if e_values and e_values.get('response_time') == 0:
262 reason += ' Response time was slower than 20 seconds.'
262 reason += ' Response time was slower than 20 seconds.'
263 elif e_values:
263 elif e_values:
264 code = e_values.get('status_code')
264 code = e_values.get('status_code')
265 reason += ' Response status code: %s.' % code
265 reason += ' Response status code: %s.' % code
266
266
267 tmpl_vars['reason'] = reason
267 tmpl_vars['reason'] = reason
268 return tmpl_vars
268 return tmpl_vars
269
269
270 def chart_alert_notification_vars(self, kwargs):
270 def chart_alert_notification_vars(self, kwargs):
271 tmpl_vars = self.get_notification_basic_vars(kwargs)
271 tmpl_vars = self.get_notification_basic_vars(kwargs)
272 tmpl_vars['chart_name'] = tmpl_vars['event_values']['chart_name']
272 tmpl_vars['chart_name'] = tmpl_vars['event_values']['chart_name']
273 tmpl_vars['action_name'] = tmpl_vars['event_values'].get(
273 tmpl_vars['action_name'] = tmpl_vars['event_values'].get(
274 'action_name') or ''
274 'action_name') or ''
275 matched_values = tmpl_vars['event_values']['matched_step_values']
275 matched_values = tmpl_vars['event_values']['matched_step_values']
276 tmpl_vars['readable_values'] = []
276 tmpl_vars['readable_values'] = []
277 for key, value in list(matched_values['values'].items()):
277 for key, value in list(matched_values['values'].items()):
278 matched_label = matched_values['labels'].get(key)
278 matched_label = matched_values['labels'].get(key)
279 if matched_label:
279 if matched_label:
280 tmpl_vars['readable_values'].append({
280 tmpl_vars['readable_values'].append({
281 'label': matched_label['human_label'],
281 'label': matched_label['human_label'],
282 'value': value
282 'value': value
283 })
283 })
284 tmpl_vars['readable_values'] = sorted(tmpl_vars['readable_values'],
284 tmpl_vars['readable_values'] = sorted(tmpl_vars['readable_values'],
285 key=lambda x: x['label'])
285 key=lambda x: x['label'])
286 start_date = convert_date(tmpl_vars['event_values']['start_interval'])
286 start_date = convert_date(tmpl_vars['event_values']['start_interval'])
287 end_date = None
287 end_date = None
288 if tmpl_vars['event_values'].get('end_interval'):
288 if tmpl_vars['event_values'].get('end_interval'):
289 end_date = convert_date(tmpl_vars['event_values']['end_interval'])
289 end_date = convert_date(tmpl_vars['event_values']['end_interval'])
290
290
291 app_url = kwargs['request'].registry.settings['_mail_url']
291 app_url = kwargs['request'].registry.settings['_mail_url']
292 destination_url = kwargs['request'].route_url('/', _app_url=app_url)
292 destination_url = kwargs['request'].route_url('/', _app_url=app_url)
293 to_encode = {
293 to_encode = {
294 'resource': tmpl_vars['event_values']['resource'],
294 'resource': tmpl_vars['event_values']['resource'],
295 'start_date': start_date.strftime(DATE_FRMT),
295 'start_date': start_date.strftime(DATE_FRMT),
296 }
296 }
297 if end_date:
297 if end_date:
298 to_encode['end_date'] = end_date.strftime(DATE_FRMT)
298 to_encode['end_date'] = end_date.strftime(DATE_FRMT)
299
299
300 destination_url += 'ui/{}?{}'.format(
300 destination_url += 'ui/{}?{}'.format(
301 'logs',
301 'logs',
302 urllib.parse.urlencode(to_encode)
302 urllib.parse.urlencode(to_encode)
303 )
303 )
304 tmpl_vars['destination_url'] = destination_url
304 tmpl_vars['destination_url'] = destination_url
305 return tmpl_vars
305 return tmpl_vars
General Comments 2
Under Review
author

Auto status change to "Under Review"

Rejected

Please use: https://github.com/Appenlight/appenlight to contribute :) Thanks !

You need to be logged in to leave comments. Login now