def send_pgp_alerts(self, node_desc, receiver_desc, notification_settings): fakeevent = OD() fakeevent.type = u'pgp_expiration_alert' fakeevent.node_info = node_desc fakeevent.context_info = None fakeevent.steps_info = None fakeevent.receiver_info = receiver_desc fakeevent.tip_info = None fakeevent.subevent_info = None body = Templating().format_template( notification_settings['pgp_alert_mail_template'], fakeevent) title = Templating().format_template( notification_settings['pgp_alert_mail_title'], fakeevent) to_address = receiver_desc['mail_address'] message = MIME_mail_build(GLSetting.memory_copy.notif_source_name, GLSetting.memory_copy.notif_source_email, to_address, to_address, title, body) yield sendmail(authentication_username=GLSetting.memory_copy.notif_username, authentication_password=GLSetting.memory_copy.notif_password, from_address=GLSetting.memory_copy.notif_source_email, to_address=to_address, message_file=message, smtp_host=GLSetting.memory_copy.notif_server, smtp_port=GLSetting.memory_copy.notif_port, security=GLSetting.memory_copy.notif_security, event=None)
def ping_mail_flush(self, notification_settings, receivers_synthesis): """ TODO This function should be implemented as a clean and testable plugin in the way defined in plugin/base.py and plugin/notification.py, and/or is the opportunity to review these classes, at the moment is a simplified version that just create a ping email and send it via sendmail. """ for _, data in receivers_synthesis.iteritems(): receiver_dict, winks = data receiver_name = receiver_dict['name'] receiver_email = receiver_dict['ping_mail_address'] fakeevent = OD() fakeevent.type = u'ping_mail' fakeevent.node_info = None fakeevent.context_info = None fakeevent.receiver_info = receiver_dict fakeevent.tip_info = None fakeevent.subevent_info = {'counter': winks} body = Templating().format_template( notification_settings['ping_mail_template'], fakeevent) title = Templating().format_template( notification_settings['ping_mail_title'], fakeevent) # so comfortable for a developer!! :) source_mail_name = GLSettings.developer_name if GLSettings.devel_mode \ else GLSettings.memory_copy.notif_source_name message = MIME_mail_build(source_mail_name, GLSettings.memory_copy.notif_source_email, receiver_name, receiver_email, title, body) fakeevent2 = OD() fakeevent2.type = "Ping mail for %s (%d info)" % (receiver_email, winks) return sendmail(authentication_username=GLSettings.memory_copy.notif_username, authentication_password=GLSettings.memory_copy.notif_password, from_address= GLSettings.memory_copy.notif_source_email, to_address= [receiver_email], message_file=message, smtp_host=GLSettings.memory_copy.notif_server, smtp_port=GLSettings.memory_copy.notif_port, security=GLSettings.memory_copy.notif_security, event=fakeevent2)
def do_notify(self, event): if event.type == 'digest': body = event.tip_info['body'] title = event.tip_info['title'] else: body, title = self.get_mail_body_and_title(event) if not self.validate_admin_opt(event.notification_settings): log.err('Invalid Mail Settings, no mail can be deliver') return None # If the receiver has encryption enabled (for notification), encrypt the mail body if event.receiver_info['pgp_key_status'] == u'enabled': gpob = GLBPGP() try: gpob.load_key(event.receiver_info['pgp_key_public']) body = gpob.encrypt_message(event.receiver_info['pgp_key_fingerprint'], body) except Exception as excep: log.err("Error in PGP interface object (for %s: %s)! (notification+encryption)" % (event.receiver_info['username'], str(excep))) # On this condition (PGP enabled but key invalid) the only # thing to do is to return None; # It will be duty of the PGP check schedule will disable the key # and advise the user and the admin about that action. return None finally: # the finally statement is always called also if # except contains a return or a raise gpob.destroy_environment() receiver_mail = event.receiver_info['mail_address'] message = MIME_mail_build(GLSettings.memory_copy.notif_source_name, GLSettings.memory_copy.notif_source_email, event.receiver_info['name'], receiver_mail, title, body) return self.mail_flush(event.notification_settings['source_email'], [receiver_mail], message, event)
def do_notify(self, event): # check if exists the conf if not self.validate_admin_opt(event.notification_settings): log.info('invalid configuration for admin email!') return None # At the moment the language used is a system language, not # Receiver preferences language ? if event.type == u'encrypted_tip': body = Templating().format_template( event.notification_settings['encrypted_tip_template'], event) title = Templating().format_template( event.notification_settings['encrypted_tip_mail_title'], event) elif event.type == u'plaintext_tip': body = Templating().format_template( event.notification_settings['plaintext_tip_template'], event) title = Templating().format_template( event.notification_settings['plaintext_tip_mail_title'], event) elif event.type == u'encrypted_file': body = Templating().format_template( event.notification_settings['encrypted_file_template'], event) title = Templating().format_template( event.notification_settings['encrypted_file_mail_title'], event) elif event.type == u'plaintext_file': body = Templating().format_template( event.notification_settings['plaintext_file_template'], event) title = Templating().format_template( event.notification_settings['plaintext_file_mail_title'], event) elif event.type == u'encrypted_comment': body = Templating().format_template( event.notification_settings['encrypted_comment_template'], event) title = Templating().format_template( event.notification_settings['encrypted_comment_mail_title'], event) elif event.type == u'plaintext_comment': body = Templating().format_template( event.notification_settings['plaintext_comment_template'], event) title = Templating().format_template( event.notification_settings['plaintext_comment_mail_title'], event) elif event.type == u'encrypted_message': body = Templating().format_template( event.notification_settings['encrypted_message_template'], event) title = Templating().format_template( event.notification_settings['encrypted_message_mail_title'], event) elif event.type == u'plaintext_message': body = Templating().format_template( event.notification_settings['plaintext_message_template'], event) title = Templating().format_template( event.notification_settings['plaintext_message_mail_title'], event) else: raise NotImplementedError("At the moment, only Tip expected") # If the receiver has encryption enabled (for notification), encrypt the mail body if event.receiver_info['gpg_key_status'] == Receiver._gpg_types[1] and \ event.receiver_info['gpg_enable_notification']: try: gpob = GLBGPG(event.receiver_info) if not gpob.validate_key(event.receiver_info['gpg_key_armor']): log.err("unable to validated GPG key for receiver %s" % event.receiver_info['username']) return None body = gpob.encrypt_message(body) gpob.destroy_environment() except Exception as excep: log.err( "Error in GPG interface object (for %s: %s)! (notification+encryption)" % (event.receiver_info['username'], str(excep))) return None receiver_mail = event.receiver_info['mail_address'] # XXX here can be catch the subject (may change if encrypted or whatever) message = MIME_mail_build(GLSetting.memory_copy.notif_source_name, GLSetting.memory_copy.notif_source_email, event.receiver_info['name'], receiver_mail, title, body) if not message: log.err("Unable to format (and then notify!) email for %s" % receiver_mail) log.debug(body) return None self.finished = self.mail_flush( event.notification_settings['source_email'], [receiver_mail], message, event) return self.finished
def admin_alarm_generate_mail(event_matrix): """ This function put a mail in queue for the Admin, if the Admin notification is disable or if another Anomaly has been raised in the last 15 minutes, email is not send. """ do_not_stress_admin_with_more_than_an_email_every_minutes = 120 # if emergency is set to True, the previous time check is ignored. emergency_notification = False @transact_ro def _get_node_admin_email(store): node = store.find(models.Node).one() return node.email @transact_ro def _get_admin_user_language(store): admin_user = store.find(models.User, models.User.username == u'admin').one() return admin_user.language # THE THREE FUNCTIONS BELOW ARE POORLY SUBOPTIMAL, # AND THIS IS BAD: REFACTOR TO BE DONE ON THIS SUBJECT @transact_ro def _get_message_template(store): admin_user = store.find(models.User, models.User.username == u'admin').one() notif = store.find(models.Notification).one() template = notif.admin_anomaly_mail_template if admin_user.language in template: localized_template = template[admin_user.language] elif GLSettings.memory_copy.default_language in template: localized_template = template[ GLSettings.memory_copy.default_language] else: raise Exception( "Cannot find any language for admin notification") return localized_template @transact_ro def _disk_anomaly_detail(store): # This happen all the time anomalies are present but disk is ok if Alarm.stress_levels['disk_space'] == 0: return u'' admin_user = store.find(models.User, models.User.username == u'admin').one() notif = store.find(models.Notification).one() if Alarm.stress_levels['disk_space'] == 1: template = notif.admin_anomaly_disk_low elif Alarm.stress_levels['disk_space'] == 2: template = notif.admin_anomaly_disk_medium elif Alarm.stress_levels['disk_space'] == 3: template = notif.admin_anomaly_disk_high else: raise Exception("Invalid disk stess level %d" % Alarm.stress_levels['disk_space']) if admin_user.language in template: localized_template = template[admin_user.language] elif GLSettings.memory_copy.default_language in template: localized_template = template[ GLSettings.memory_copy.default_language] else: raise Exception( "Cannot find any language for Admin disk alarm (level %d)" % Alarm.stress_levels['disk_space']) return localized_template @transact_ro def _activities_anomaly_detail(store): # This happen all the time there is not anomalous traffic if Alarm.stress_levels['activity'] == 0: return u'' admin_user = store.find(models.User, models.User.username == u'admin').one() notif = store.find(models.Notification).one() template = notif.admin_anomaly_activities if admin_user.language in template: localized_template = template[admin_user.language] elif GLSettings.memory_copy.default_language in template: localized_template = template[ GLSettings.memory_copy.default_language] else: raise Exception( "Cannot find any language for admin notification") return localized_template # END OF THE SUB-OPTIMAL SECTION OF CODE THAT HAS TO BE RESTRUCTURED def _activity_alarm_level(): return "%s" % Alarm.stress_levels['activity'] def _activity_dump(): retstr = "" for event, amount in event_matrix.iteritems(): if not amount: continue retstr = "%s%s%d\n%s" % \ (event, (25 - len(event)) * " ", amount, retstr) return retstr @transact_ro def _node_name(store): node = store.find(models.Node).one() return unicode(node.name) def _free_disk_space(): return "%s" % bytes_to_pretty_str(Alarm.latest_measured_freespace) def _total_disk_space(): return "%s" % bytes_to_pretty_str(Alarm.latest_measured_totalspace) def _notification_suppressed(): if Alarm.stress_levels['notification'] == []: return u'' emergency_notification = True return "** %s **" % Alarm.stress_levels['notification'] KeyWordTemplate = { "%AnomalyDetailDisk%": _disk_anomaly_detail, "%AnomalyDetailActivities%": _activities_anomaly_detail, "%ActivityAlarmLevel%": _activity_alarm_level, "%ActivityDump%": _activity_dump, "%NotificationSuppressed%": _notification_suppressed, "%NodeName%": _node_name, "%FreeMemory%": _free_disk_space, "%TotalMemory%": _total_disk_space, } # ------------------------------------------------------------------ # Independently from the event_matrix, the status of the stress level can # be in non-0 value. # Here start the Anomaly Notification code, before checking if we have to send email if not (Alarm.stress_levels['activity'] or Alarm.stress_levels['disk_space'] or Alarm.stress_levels['notification']): # lucky, no stress activities recorded: no mail needed defer.returnValue(None) if GLSettings.memory_copy.disable_admin_notification_emails: # event_matrix is {} if we are here only for disk log.debug( "Anomaly to be reported %s, but Admin has Notification disabled" % "[%s]" % event_matrix if event_matrix else "") defer.returnValue(None) if Alarm.last_alarm_email and not emergency_notification: if not is_expired( Alarm.last_alarm_email, minutes= do_not_stress_admin_with_more_than_an_email_every_minutes): defer.returnValue(None) # This is skipped then: log.debug( "Alert [%s] want be sent, but the threshold of %d minutes still unexpired %s" % (Alarm.stress_levels, do_not_stress_admin_with_more_than_an_email_every_minutes, datetime_to_ISO8601(Alarm.last_alarm_email))) admin_email = yield _get_node_admin_email() admin_language = yield _get_admin_user_language() notification_settings = yield get_notification(admin_language) # and now, processing the template message = yield _get_message_template() message_title = notification_settings['admin_anomaly_mail_title'] recursion_time = 2 # since the ActivityDetails, we've to manage recursion while recursion_time: recursion_time -= 1 for keyword, templ_funct in KeyWordTemplate.iteritems(): where = message.find(keyword) if where == -1: continue # based on the type of templ_funct, we've to use 'yield' or not # cause some returns a deferred. if isinstance(templ_funct, type(sendmail)): content = templ_funct() else: content = yield templ_funct() message = "%s%s%s" % (message[:where], content, message[where + len(keyword):]) # message title, we can't put the loop together at the moment for keyword, templ_funct in KeyWordTemplate.iteritems(): where = message_title.find(keyword) if where == -1: continue if isinstance(templ_funct, type(sendmail)): content = templ_funct() else: content = yield templ_funct() message_title = "%s%s%s" % (message_title[:where], content, message_title[where + len(keyword):]) message = MIME_mail_build(GLSettings.memory_copy.notif_source_name, GLSettings.memory_copy.notif_source_email, admin_email, admin_email, message_title, message) log.debug( 'Alarm Email generated for Admin (%s): connecting to [%s:%d], ' 'the next mail should be in %d minutes' % (event_matrix, GLSettings.memory_copy.notif_server, GLSettings.memory_copy.notif_port, do_not_stress_admin_with_more_than_an_email_every_minutes)) defer.returnValue({ 'admin_email': admin_email, 'message': message, })
def admin_alarm_notification(event_matrix): """ This function put a mail in queue for the Admin, if the configured threshold has been reached for Alarm notification. TODO put a GLSetting + Admin configuration variable, now is hardcoded to notice at >= 1 """ @transact_ro def _get_node_name(store): node = store.find(models.Node).one() return node.email @transact_ro def _get_admin_email(store): node = store.find(models.Node).one() return node.email node_name = yield _get_node_name() admin_email = yield _get_admin_email() @transact_ro def _get_message_template(store): admin_user = store.find(models.User, models.User.username == u'admin').one() notif = store.find(models.Notification).one() template = notif.admin_anomaly_template if admin_user.language in template: return template[admin_user.language] elif GLSetting.memory_copy.language in template: return template[GLSetting.memory_copy.language] else: raise Exception( "Cannot find any language for admin notification") def _aal(): return "%s" % Alarm.stress_levels['activity'] def _ad(): retstr = "" for event, amount in event_matrix.iteritems(): retstr = "%s: %d\n%s" % (event, amount, retstr) return retstr def _dal(): return "%s" % Alarm.stress_levels['disk_space'] def _dd(): return "%s" % bytes_to_pretty_str(Alarm.latest_measured_freespace) def _nn(): return "%s" % node_name message_required = False if Alarm.stress_levels['activity'] >= 1: message_required = True if Alarm.stress_levels['disk_space'] >= 1: message_required = True if not message_required: # luckly, no mail needed return KeyWordTemplate = { "%ActivityAlarmLevel%": _aal, "%ActivityDump%": _ad, "%DiskAlarmLevel%": _dal, "%DiskDump%": _dd, "%NodeSignature%": _nn } message = yield _get_message_template() for keyword, function in KeyWordTemplate.iteritems(): where = message.find(keyword) message = "%s%s%s" % (message[:where], function(), message[where + len(keyword):]) if Alarm.last_alarm_email: if not is_expired(Alarm.last_alarm_email, minutes=10): log.debug( "Alert email want be send, but the threshold of 10 minutes is not yet reached since %s" % datetime_to_ISO8601(Alarm.last_alarm_email)) return message = MIME_mail_build(GLSetting.memory_copy.notif_source_name, GLSetting.memory_copy.notif_source_email, "Admin", admin_email, "ALERT: Anomaly detection", message) log.debug('Alarm Email for admin: connecting to [%s:%d]' % (GLSetting.memory_copy.notif_server, GLSetting.memory_copy.notif_port)) Alarm.last_alarm_email = datetime_now() yield sendmail( authentication_username=GLSetting.memory_copy.notif_username, authentication_password=GLSetting.memory_copy.notif_password, from_address=GLSetting.memory_copy.notif_source_email, to_address=admin_email, message_file=message, smtp_host=GLSetting.memory_copy.notif_server, smtp_port=GLSetting.memory_copy.notif_port, security=GLSetting.memory_copy.notif_security, event=None)
def do_notify(self, event): if not self.validate_admin_opt(event.notification_settings): log.info('invalid mail settings for admin') return None # At the moment the language used is a system language, not # Receiver preferences language ? if event.type == u'encrypted_tip': body = Templating().format_template( event.notification_settings['encrypted_tip_template'], event) title = Templating().format_template( event.notification_settings['encrypted_tip_mail_title'], event) elif event.type == u'plaintext_tip': body = Templating().format_template( event.notification_settings['plaintext_tip_template'], event) title = Templating().format_template( event.notification_settings['plaintext_tip_mail_title'], event) elif event.type == u'encrypted_file': body = Templating().format_template( event.notification_settings['encrypted_file_template'], event) title = Templating().format_template( event.notification_settings['encrypted_file_mail_title'], event) elif event.type == u'plaintext_file': body = Templating().format_template( event.notification_settings['plaintext_file_template'], event) title = Templating().format_template( event.notification_settings['plaintext_file_mail_title'], event) elif event.type == u'encrypted_comment': body = Templating().format_template( event.notification_settings['encrypted_comment_template'], event) title = Templating().format_template( event.notification_settings['encrypted_comment_mail_title'], event) elif event.type == u'plaintext_comment': body = Templating().format_template( event.notification_settings['plaintext_comment_template'], event) title = Templating().format_template( event.notification_settings['plaintext_comment_mail_title'], event) elif event.type == u'encrypted_message': body = Templating().format_template( event.notification_settings['encrypted_message_template'], event) title = Templating().format_template( event.notification_settings['encrypted_message_mail_title'], event) elif event.type == u'plaintext_message': body = Templating().format_template( event.notification_settings['plaintext_message_template'], event) title = Templating().format_template( event.notification_settings['plaintext_message_mail_title'], event) else: raise NotImplementedError("At the moment, only Tip expected") # If the receiver has encryption enabled (for notification), encrypt the mail body if event.receiver_info['gpg_key_status'] == u'enabled': gpob = GLBGPG() try: gpob.load_key(event.receiver_info['gpg_key_armor']) body = gpob.encrypt_message(event.receiver_info['gpg_key_fingerprint'], body) except Exception as excep: log.err("Error in GPG interface object (for %s: %s)! (notification+encryption)" % (event.receiver_info['username'], str(excep) )) return None # We return None and the mail will be delayed # If GPG is enabled and the key is invalid this # is the only possiibly thing to do. # The PGP check schedule will disable the key # and alert the user and the admin finally: # the finally statement is always called also if # except contains a return or a raise gpob.destroy_environment() receiver_mail = event.receiver_info['mail_address'] # XXX here can be catch the subject (may change if encrypted or whatever) message = MIME_mail_build(GLSetting.memory_copy.notif_source_name, GLSetting.memory_copy.notif_source_email, event.receiver_info['name'], receiver_mail, title, body) return self.mail_flush(event.notification_settings['source_email'], [receiver_mail], message, event)