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