# -*- coding: utf-8 -*- # Copyright 2010 - 2017 RhodeCode GmbH and the AppEnlight project authors # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import sqlalchemy as sa import logging from datetime import datetime from appenlight.models import Base, get_db_session from appenlight.models.services.report_stat import ReportStatService from appenlight.models.integrations import IntegrationException from pyramid.threadlocal import get_current_request from sqlalchemy.dialects.postgresql import JSON from ziggurat_foundations.models.base import BaseModel from ziggurat_foundations.models.services.resource import ResourceService log = logging.getLogger(__name__) class Event(Base, BaseModel): __tablename__ = "events" types = { "error_report_alert": 1, "slow_report_alert": 3, "comment": 5, "assignment": 6, "uptime_alert": 7, "chart_alert": 9, } statuses = {"active": 1, "closed": 0} id = sa.Column(sa.Integer, primary_key=True) start_date = sa.Column(sa.DateTime, default=datetime.utcnow) end_date = sa.Column(sa.DateTime) status = sa.Column(sa.Integer, default=1) event_type = sa.Column(sa.Integer, default=1) origin_user_id = sa.Column(sa.Integer(), sa.ForeignKey("users.id"), nullable=True) target_user_id = sa.Column(sa.Integer(), sa.ForeignKey("users.id"), nullable=True) resource_id = sa.Column( sa.Integer(), sa.ForeignKey("resources.resource_id"), nullable=True ) target_id = sa.Column(sa.Integer) target_uuid = sa.Column(sa.Unicode(40)) text = sa.Column(sa.UnicodeText()) values = sa.Column(JSON(), nullable=False, default=None) def __repr__(self): return "" % ( self.unified_alert_name(), self.resource_id, self.unified_alert_action(), ) @property def reverse_types(self): return dict([(v, k) for k, v in self.types.items()]) def unified_alert_name(self): return self.reverse_types[self.event_type] def unified_alert_action(self): event_name = self.reverse_types[self.event_type] if self.status == Event.statuses["closed"]: return "CLOSE" if self.status != Event.statuses["closed"]: return "OPEN" return event_name def send_alerts(self, request=None, resource=None, db_session=None): """" Sends alerts to applicable channels """ db_session = get_db_session(db_session) db_session.flush() if not resource: resource = ResourceService.by_resource_id(self.resource_id) if not request: request = get_current_request() if not resource: return users = set([p.user for p in ResourceService.users_for_perm(resource, "view")]) for user in users: for channel in user.alert_channels: matches_resource = not channel.resources or resource in [ r.resource_id for r in channel.resources ] if ( not channel.channel_validated or not channel.send_alerts or not matches_resource ): continue else: try: channel.notify_alert( resource=resource, event=self, user=user, request=request ) except IntegrationException as e: log.warning("%s" % e) def validate_or_close(self, since_when, db_session=None): """ Checks if alerts should stay open or it's time to close them. Generates close alert event if alerts get closed """ event_types = [ Event.types["error_report_alert"], Event.types["slow_report_alert"], ] app = ResourceService.by_resource_id(self.resource_id) # if app was deleted close instantly if not app: self.close() return if self.event_type in event_types: total = ReportStatService.count_by_type( self.event_type, self.resource_id, since_when ) if Event.types["error_report_alert"] == self.event_type: threshold = app.error_report_threshold if Event.types["slow_report_alert"] == self.event_type: threshold = app.slow_report_threshold if total < threshold: self.close() def close(self, db_session=None): """ Closes an event and sends notification to affected users """ self.end_date = datetime.utcnow() self.status = Event.statuses["closed"] log.warning("ALERT: CLOSE: %s" % self) self.send_alerts() def text_representation(self): alert_type = self.unified_alert_name() text = "" if "slow_report" in alert_type: text += "Slow report alert" if "error_report" in alert_type: text += "Exception report alert" if "uptime_alert" in alert_type: text += "Uptime alert" if "chart_alert" in alert_type: text += "Metrics value alert" alert_action = self.unified_alert_action() if alert_action == "OPEN": text += " got opened." if alert_action == "CLOSE": text += " got closed." return text def get_dict(self, request=None): dict_data = super(Event, self).get_dict() dict_data["text"] = self.text_representation() dict_data["resource_name"] = self.resource.resource_name return dict_data