##// END OF EJS Templates
events: do not fail when handling events for removed applications
ergo -
Show More
@@ -1,160 +1,165 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 sqlalchemy as sa
18 18 import logging
19 19
20 20 from datetime import datetime
21 21 from appenlight.models import Base, get_db_session
22 22 from appenlight.models.services.report_stat import ReportStatService
23 23 from appenlight.models.resource import Resource
24 24 from appenlight.models.integrations import IntegrationException
25 25 from pyramid.threadlocal import get_current_request
26 26 from sqlalchemy.dialects.postgresql import JSON
27 27 from ziggurat_foundations.models.base import BaseModel
28 28
29 29 log = logging.getLogger(__name__)
30 30
31 31
32 32 class Event(Base, BaseModel):
33 33 __tablename__ = 'events'
34 34
35 35 types = {'error_report_alert': 1,
36 36 'slow_report_alert': 3,
37 37 'comment': 5,
38 38 'assignment': 6,
39 39 'uptime_alert': 7,
40 40 'chart_alert': 9}
41 41
42 42 statuses = {'active': 1,
43 43 'closed': 0}
44 44
45 45 id = sa.Column(sa.Integer, primary_key=True)
46 46 start_date = sa.Column(sa.DateTime, default=datetime.utcnow)
47 47 end_date = sa.Column(sa.DateTime)
48 48 status = sa.Column(sa.Integer, default=1)
49 49 event_type = sa.Column(sa.Integer, default=1)
50 50 origin_user_id = sa.Column(sa.Integer(), sa.ForeignKey('users.id'),
51 51 nullable=True)
52 52 target_user_id = sa.Column(sa.Integer(), sa.ForeignKey('users.id'),
53 53 nullable=True)
54 54 resource_id = sa.Column(sa.Integer(),
55 55 sa.ForeignKey('resources.resource_id'),
56 56 nullable=True)
57 57 target_id = sa.Column(sa.Integer)
58 58 target_uuid = sa.Column(sa.Unicode(40))
59 59 text = sa.Column(sa.UnicodeText())
60 60 values = sa.Column(JSON(), nullable=False, default=None)
61 61
62 62 def __repr__(self):
63 63 return '<Event %s, app:%s, %s>' % (self.unified_alert_name(),
64 64 self.resource_id,
65 65 self.unified_alert_action())
66 66
67 67 @property
68 68 def reverse_types(self):
69 69 return dict([(v, k) for k, v in self.types.items()])
70 70
71 71 def unified_alert_name(self):
72 72 return self.reverse_types[self.event_type]
73 73
74 74 def unified_alert_action(self):
75 75 event_name = self.reverse_types[self.event_type]
76 76 if self.status == Event.statuses['closed']:
77 77 return "CLOSE"
78 78 if self.status != Event.statuses['closed']:
79 79 return "OPEN"
80 80 return event_name
81 81
82 82 def send_alerts(self, request=None, resource=None, db_session=None):
83 83 """" Sends alerts to applicable channels """
84 84 db_session = get_db_session(db_session)
85 85 db_session.flush()
86 86 if not resource:
87 87 resource = Resource.by_resource_id(self.resource_id)
88 88 if not request:
89 89 request = get_current_request()
90 90 if not resource:
91 91 return
92 92 users = set([p.user for p in resource.users_for_perm('view')])
93 93 for user in users:
94 94 for channel in user.alert_channels:
95 95 matches_resource = not channel.resources or resource in [r.resource_id for r in channel.resources]
96 96 if (
97 97 not channel.channel_validated or
98 98 not channel.send_alerts or
99 99 not matches_resource
100 100 ):
101 101 continue
102 102 else:
103 103 try:
104 104 channel.notify_alert(resource=resource,
105 105 event=self,
106 106 user=user,
107 107 request=request)
108 108 except IntegrationException as e:
109 109 log.warning('%s' % e)
110 110
111 111 def validate_or_close(self, since_when, db_session=None):
112 112 """ Checks if alerts should stay open or it's time to close them.
113 113 Generates close alert event if alerts get closed """
114 114 event_types = [Event.types['error_report_alert'],
115 115 Event.types['slow_report_alert']]
116 116 app = Resource.by_resource_id(self.resource_id)
117 # if app was deleted close instantly

test

aadf

118 if not app:
119 self.close()
120 return
121
117 122 if self.event_type in event_types:
118 123 total = ReportStatService.count_by_type(
119 124 self.event_type, self.resource_id, since_when)
120 125 if Event.types['error_report_alert'] == self.event_type:
121 126 threshold = app.error_report_threshold
122 127 if Event.types['slow_report_alert'] == self.event_type:
123 128 threshold = app.slow_report_threshold
124 129
125 130 if total < threshold:
126 131 self.close()
127 132
128 133 def close(self, db_session=None):
129 134 """
130 135 Closes an event and sends notification to affected users
131 136 """
132 137 self.end_date = datetime.utcnow()
133 138 self.status = Event.statuses['closed']
134 139 log.warning('ALERT: CLOSE: %s' % self)
135 140 self.send_alerts()
136 141
137 142 def text_representation(self):
138 143 alert_type = self.unified_alert_name()
139 144 text = ''
140 145 if 'slow_report' in alert_type:
141 146 text += 'Slow report alert'
142 147 if 'error_report' in alert_type:
143 148 text += 'Exception report alert'
144 149 if 'uptime_alert' in alert_type:
145 150 text += 'Uptime alert'
146 151 if 'chart_alert' in alert_type:
147 152 text += 'Metrics value alert'
148 153
149 154 alert_action = self.unified_alert_action()
150 155 if alert_action == 'OPEN':
151 156 text += ' got opened.'
152 157 if alert_action == 'CLOSE':
153 158 text += ' got closed.'
154 159 return text
155 160
156 161 def get_dict(self, request=None):
157 162 dict_data = super(Event, self).get_dict()
158 163 dict_data['text'] = self.text_representation()
159 164 dict_data['resource_name'] = self.resource.resource_name
160 165 return dict_data
General Comments 1
Approved

approved

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