##// END OF EJS Templates
events: do not fail when handling events for removed applications
ergo -
Show More
@@ -1,160 +1,165 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 sqlalchemy as sa
17 import sqlalchemy as sa
18 import logging
18 import logging
19
19
20 from datetime import datetime
20 from datetime import datetime
21 from appenlight.models import Base, get_db_session
21 from appenlight.models import Base, get_db_session
22 from appenlight.models.services.report_stat import ReportStatService
22 from appenlight.models.services.report_stat import ReportStatService
23 from appenlight.models.resource import Resource
23 from appenlight.models.resource import Resource
24 from appenlight.models.integrations import IntegrationException
24 from appenlight.models.integrations import IntegrationException
25 from pyramid.threadlocal import get_current_request
25 from pyramid.threadlocal import get_current_request
26 from sqlalchemy.dialects.postgresql import JSON
26 from sqlalchemy.dialects.postgresql import JSON
27 from ziggurat_foundations.models.base import BaseModel
27 from ziggurat_foundations.models.base import BaseModel
28
28
29 log = logging.getLogger(__name__)
29 log = logging.getLogger(__name__)
30
30
31
31
32 class Event(Base, BaseModel):
32 class Event(Base, BaseModel):
33 __tablename__ = 'events'
33 __tablename__ = 'events'
34
34
35 types = {'error_report_alert': 1,
35 types = {'error_report_alert': 1,
36 'slow_report_alert': 3,
36 'slow_report_alert': 3,
37 'comment': 5,
37 'comment': 5,
38 'assignment': 6,
38 'assignment': 6,
39 'uptime_alert': 7,
39 'uptime_alert': 7,
40 'chart_alert': 9}
40 'chart_alert': 9}
41
41
42 statuses = {'active': 1,
42 statuses = {'active': 1,
43 'closed': 0}
43 'closed': 0}
44
44
45 id = sa.Column(sa.Integer, primary_key=True)
45 id = sa.Column(sa.Integer, primary_key=True)
46 start_date = sa.Column(sa.DateTime, default=datetime.utcnow)
46 start_date = sa.Column(sa.DateTime, default=datetime.utcnow)
47 end_date = sa.Column(sa.DateTime)
47 end_date = sa.Column(sa.DateTime)
48 status = sa.Column(sa.Integer, default=1)
48 status = sa.Column(sa.Integer, default=1)
49 event_type = sa.Column(sa.Integer, default=1)
49 event_type = sa.Column(sa.Integer, default=1)
50 origin_user_id = sa.Column(sa.Integer(), sa.ForeignKey('users.id'),
50 origin_user_id = sa.Column(sa.Integer(), sa.ForeignKey('users.id'),
51 nullable=True)
51 nullable=True)
52 target_user_id = sa.Column(sa.Integer(), sa.ForeignKey('users.id'),
52 target_user_id = sa.Column(sa.Integer(), sa.ForeignKey('users.id'),
53 nullable=True)
53 nullable=True)
54 resource_id = sa.Column(sa.Integer(),
54 resource_id = sa.Column(sa.Integer(),
55 sa.ForeignKey('resources.resource_id'),
55 sa.ForeignKey('resources.resource_id'),
56 nullable=True)
56 nullable=True)
57 target_id = sa.Column(sa.Integer)
57 target_id = sa.Column(sa.Integer)
58 target_uuid = sa.Column(sa.Unicode(40))
58 target_uuid = sa.Column(sa.Unicode(40))
59 text = sa.Column(sa.UnicodeText())
59 text = sa.Column(sa.UnicodeText())
60 values = sa.Column(JSON(), nullable=False, default=None)
60 values = sa.Column(JSON(), nullable=False, default=None)
61
61
62 def __repr__(self):
62 def __repr__(self):
63 return '<Event %s, app:%s, %s>' % (self.unified_alert_name(),
63 return '<Event %s, app:%s, %s>' % (self.unified_alert_name(),
64 self.resource_id,
64 self.resource_id,
65 self.unified_alert_action())
65 self.unified_alert_action())
66
66
67 @property
67 @property
68 def reverse_types(self):
68 def reverse_types(self):
69 return dict([(v, k) for k, v in self.types.items()])
69 return dict([(v, k) for k, v in self.types.items()])
70
70
71 def unified_alert_name(self):
71 def unified_alert_name(self):
72 return self.reverse_types[self.event_type]
72 return self.reverse_types[self.event_type]
73
73
74 def unified_alert_action(self):
74 def unified_alert_action(self):
75 event_name = self.reverse_types[self.event_type]
75 event_name = self.reverse_types[self.event_type]
76 if self.status == Event.statuses['closed']:
76 if self.status == Event.statuses['closed']:
77 return "CLOSE"
77 return "CLOSE"
78 if self.status != Event.statuses['closed']:
78 if self.status != Event.statuses['closed']:
79 return "OPEN"
79 return "OPEN"
80 return event_name
80 return event_name
81
81
82 def send_alerts(self, request=None, resource=None, db_session=None):
82 def send_alerts(self, request=None, resource=None, db_session=None):
83 """" Sends alerts to applicable channels """
83 """" Sends alerts to applicable channels """
84 db_session = get_db_session(db_session)
84 db_session = get_db_session(db_session)
85 db_session.flush()
85 db_session.flush()
86 if not resource:
86 if not resource:
87 resource = Resource.by_resource_id(self.resource_id)
87 resource = Resource.by_resource_id(self.resource_id)
88 if not request:
88 if not request:
89 request = get_current_request()
89 request = get_current_request()
90 if not resource:
90 if not resource:
91 return
91 return
92 users = set([p.user for p in resource.users_for_perm('view')])
92 users = set([p.user for p in resource.users_for_perm('view')])
93 for user in users:
93 for user in users:
94 for channel in user.alert_channels:
94 for channel in user.alert_channels:
95 matches_resource = not channel.resources or resource in [r.resource_id for r in channel.resources]
95 matches_resource = not channel.resources or resource in [r.resource_id for r in channel.resources]
96 if (
96 if (
97 not channel.channel_validated or
97 not channel.channel_validated or
98 not channel.send_alerts or
98 not channel.send_alerts or
99 not matches_resource
99 not matches_resource
100 ):
100 ):
101 continue
101 continue
102 else:
102 else:
103 try:
103 try:
104 channel.notify_alert(resource=resource,
104 channel.notify_alert(resource=resource,
105 event=self,
105 event=self,
106 user=user,
106 user=user,
107 request=request)
107 request=request)
108 except IntegrationException as e:
108 except IntegrationException as e:
109 log.warning('%s' % e)
109 log.warning('%s' % e)
110
110
111 def validate_or_close(self, since_when, db_session=None):
111 def validate_or_close(self, since_when, db_session=None):
112 """ Checks if alerts should stay open or it's time to close them.
112 """ Checks if alerts should stay open or it's time to close them.
113 Generates close alert event if alerts get closed """
113 Generates close alert event if alerts get closed """
114 event_types = [Event.types['error_report_alert'],
114 event_types = [Event.types['error_report_alert'],
115 Event.types['slow_report_alert']]
115 Event.types['slow_report_alert']]
116 app = Resource.by_resource_id(self.resource_id)
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 if self.event_type in event_types:
122 if self.event_type in event_types:
118 total = ReportStatService.count_by_type(
123 total = ReportStatService.count_by_type(
119 self.event_type, self.resource_id, since_when)
124 self.event_type, self.resource_id, since_when)
120 if Event.types['error_report_alert'] == self.event_type:
125 if Event.types['error_report_alert'] == self.event_type:
121 threshold = app.error_report_threshold
126 threshold = app.error_report_threshold
122 if Event.types['slow_report_alert'] == self.event_type:
127 if Event.types['slow_report_alert'] == self.event_type:
123 threshold = app.slow_report_threshold
128 threshold = app.slow_report_threshold
124
129
125 if total < threshold:
130 if total < threshold:
126 self.close()
131 self.close()
127
132
128 def close(self, db_session=None):
133 def close(self, db_session=None):
129 """
134 """
130 Closes an event and sends notification to affected users
135 Closes an event and sends notification to affected users
131 """
136 """
132 self.end_date = datetime.utcnow()
137 self.end_date = datetime.utcnow()
133 self.status = Event.statuses['closed']
138 self.status = Event.statuses['closed']
134 log.warning('ALERT: CLOSE: %s' % self)
139 log.warning('ALERT: CLOSE: %s' % self)
135 self.send_alerts()
140 self.send_alerts()
136
141
137 def text_representation(self):
142 def text_representation(self):
138 alert_type = self.unified_alert_name()
143 alert_type = self.unified_alert_name()
139 text = ''
144 text = ''
140 if 'slow_report' in alert_type:
145 if 'slow_report' in alert_type:
141 text += 'Slow report alert'
146 text += 'Slow report alert'
142 if 'error_report' in alert_type:
147 if 'error_report' in alert_type:
143 text += 'Exception report alert'
148 text += 'Exception report alert'
144 if 'uptime_alert' in alert_type:
149 if 'uptime_alert' in alert_type:
145 text += 'Uptime alert'
150 text += 'Uptime alert'
146 if 'chart_alert' in alert_type:
151 if 'chart_alert' in alert_type:
147 text += 'Metrics value alert'
152 text += 'Metrics value alert'
148
153
149 alert_action = self.unified_alert_action()
154 alert_action = self.unified_alert_action()
150 if alert_action == 'OPEN':
155 if alert_action == 'OPEN':
151 text += ' got opened.'
156 text += ' got opened.'
152 if alert_action == 'CLOSE':
157 if alert_action == 'CLOSE':
153 text += ' got closed.'
158 text += ' got closed.'
154 return text
159 return text
155
160
156 def get_dict(self, request=None):
161 def get_dict(self, request=None):
157 dict_data = super(Event, self).get_dict()
162 dict_data = super(Event, self).get_dict()
158 dict_data['text'] = self.text_representation()
163 dict_data['text'] = self.text_representation()
159 dict_data['resource_name'] = self.resource.resource_name
164 dict_data['resource_name'] = self.resource.resource_name
160 return dict_data
165 return dict_data
General Comments 1
Approved

approved

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