from __future__ import division
from smtplib import SMTP
# @added 20220203 - Feature #4416: settings - additional SMTP_OPTS
from smtplib import SMTP_SSL
import sys
python_version = int(sys.version_info[0])
if python_version == 2:
from email.MIMEMultipart import MIMEMultipart
from email.MIMEText import MIMEText
from email.MIMEImage import MIMEImage
if python_version == 3:
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.image import MIMEImage
import negaters
try:
import urllib2
except ImportError:
import urllib.request
import urllib.error
import os.path
sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), os.pardir))
sys.path.insert(0, os.path.dirname(__file__))
import settings
# @added 20220203 - Feature #4416: settings - additional SMTP_OPTS
from functions.smtp.determine_smtp_server import determine_smtp_server
# from skyline import settings
"""
Create any alerter you want here. The function will be invoked from trigger_alert.
Two arguments will be passed, both of them tuples: alert and metric.
alert: the tuple specified in your settings:
alert[0]: The matched substring of the anomalous metric
alert[1]: the name of the strategy being used to alert
alert[2]: The timeout of the alert that was triggered
alert[3]: The SECOND_ORDER_RESOLUTION_HOURS
metric: information about the anomaly itself
metric[0]: the anomalous value
metric[1]: The full name of the anomalous metric
"""
[docs]def negate_analyzer_alert(alert, metric, second_order_resolution_seconds, metric_value):
# FULL_DURATION to hours so that mirage can surface the relevant timeseries data
# for analyzer comparison in the graph
full_duration_in_hours = int(settings.FULL_DURATION) / 3600
# SECOND_ORDER_RESOLUTION_SECONDS to hours so that mirage surfaces the
# relevant timeseries data in the graph
second_order_resolution_in_hours = int(second_order_resolution_seconds) / 3600
# For backwards compatibility
if '@' in alert[1]:
sender = settings.ALERT_SENDER
recipient = alert[1]
else:
sender = settings.SMTP_OPTS['sender']
recipients = settings.SMTP_OPTS['recipients'][alert[0]]
# Backwards compatibility
if type(recipients) is str:
recipients = [recipients]
graph_title = '&title=skyline%%20mirage%%20negation%%20at%%20%s%%20hours%%0A%s%%20-%%20%s' % (second_order_resolution_in_hours, metric[1], metric[0])
analyzer_graph_title = '&title=skyline%%20analyzer%%20alert%%20at%%20%s%%20hours%%0A%s%%20-%%20%s' % (full_duration_in_hours, metric[1], metric_value)
if settings.GRAPHITE_PORT != '':
# @modified 20200417 - Task #3294: py3 - handle system parameter in Graphite cactiStyle
# link = '%s://%s:%s/render/?from=-%shour&target=cactiStyle(%s)%s%s&colorList=purple' % (settings.GRAPHITE_PROTOCOL, settings.GRAPHITE_HOST, settings.GRAPHITE_PORT, second_order_resolution_in_hours, metric[1], settings.GRAPHITE_GRAPH_SETTINGS, graph_title)
# analyzer_link = '%s://%s:%s/render/?from=-%shour&target=cactiStyle(%s)%s%s&colorList=orange' % (settings.GRAPHITE_PROTOCOL, settings.GRAPHITE_HOST, settings.GRAPHITE_PORT, full_duration_in_hours, metric[1], settings.GRAPHITE_GRAPH_SETTINGS, analyzer_graph_title)
link = '%s://%s:%s/render/?from=-%shour&target=cactiStyle(%s,%%27si%%27)%s%s&colorList=purple' % (settings.GRAPHITE_PROTOCOL, settings.GRAPHITE_HOST, settings.GRAPHITE_PORT, second_order_resolution_in_hours, metric[1], settings.GRAPHITE_GRAPH_SETTINGS, graph_title)
analyzer_link = '%s://%s:%s/render/?from=-%shour&target=cactiStyle(%s,%%27si%%27)%s%s&colorList=orange' % (settings.GRAPHITE_PROTOCOL, settings.GRAPHITE_HOST, settings.GRAPHITE_PORT, full_duration_in_hours, metric[1], settings.GRAPHITE_GRAPH_SETTINGS, analyzer_graph_title)
else:
# @modified 20200417 - Task #3294: py3 - handle system parameter in Graphite cactiStyle
# link = '%s://%s/render/?from=-%shour&target=cactiStyle(%s)%s%s&colorList=purple' % (settings.GRAPHITE_PROTOCOL, settings.GRAPHITE_HOST, second_order_resolution_in_hours, metric[1], settings.GRAPHITE_GRAPH_SETTINGS, graph_title)
# analyzer_link = '%s://%s/render/?from=-%shour&target=cactiStyle(%s)%s%s&colorList=orange' % (settings.GRAPHITE_PROTOCOL, settings.GRAPHITE_HOST, full_duration_in_hours, metric[1], settings.GRAPHITE_GRAPH_SETTINGS, analyzer_graph_title)
link = '%s://%s/render/?from=-%shour&target=cactiStyle(%s,%%27si%%27)%s%s&colorList=purple' % (settings.GRAPHITE_PROTOCOL, settings.GRAPHITE_HOST, second_order_resolution_in_hours, metric[1], settings.GRAPHITE_GRAPH_SETTINGS, graph_title)
analyzer_link = '%s://%s/render/?from=-%shour&target=cactiStyle(%s,%%27si%%27)%s%s&colorList=orange' % (settings.GRAPHITE_PROTOCOL, settings.GRAPHITE_HOST, full_duration_in_hours, metric[1], settings.GRAPHITE_GRAPH_SETTINGS, analyzer_graph_title)
content_id = metric[1]
image_data = None
if settings.SMTP_OPTS.get('embed-images'):
try:
# @modified 20170913 - Task #2160: Test skyline with bandit
# Added nosec to exclude from bandit tests
image_data = urllib2.urlopen(link).read() # nosec
except urllib2.URLError:
image_data = None
# If we failed to get the image or if it was explicitly disabled,
# use the image URL instead of the content.
if image_data is None:
img_tag = '<img src="%s"/>' % link
else:
img_tag = '<img src="cid:%s"/>' % content_id
analyzer_content_id = 'analyzer'
analyzer_image_data = None
if settings.SMTP_OPTS.get('embed-images'):
try:
# @modified 20170913 - Task #2160: Test skyline with bandit
# Added nosec to exclude from bandit tests
analyzer_image_data = urllib2.urlopen(analyzer_link).read() # nosec
except urllib2.URLError:
analyzer_image_data = None
# If we failed to get the image or if it was explicitly disabled,
# use the image URL instead of the content.
if analyzer_image_data is None:
analyzer_img_tag = '<img src="%s"/>' % analyzer_link
else:
analyzer_img_tag = '<img src="cid:%s"/>' % analyzer_content_id
mirage_body = 'mirage alert NEGATION <br> analyzer reported %s as anomalous at %s over %s hours <br> <br> This metric is NOT anomalous at %s hours <br> <a href="%s">%s</a>' % (metric[1], metric_value, full_duration_in_hours, second_order_resolution_in_hours, link, img_tag)
analyzer_body = ' <br> <br> <br> Analyzer graph at %s hours for anomalous value: %s<br> <a href="%s">%s</a>' % (full_duration_in_hours, metric_value, analyzer_link, analyzer_img_tag)
if analyzer_image_data is not None:
body = mirage_body + analyzer_body
else:
body = mirage_body
# @added 20220203 - Feature #4416: settings - additional SMTP_OPTS
smtp_server = None
try:
smtp_server = determine_smtp_server()
except:
return
for recipient in recipients:
msg = MIMEMultipart('alternative')
msg['Subject'] = 'mirage alert NEGATION - ' + metric[1]
msg['From'] = sender
msg['To'] = recipient
msg.attach(MIMEText(body, 'html'))
if image_data is not None:
msg_attachment = MIMEImage(image_data)
msg_attachment.add_header('Content-ID', '<%s>' % content_id)
msg.attach(msg_attachment)
if analyzer_image_data is not None:
analyzer_msg_attachment = MIMEImage(analyzer_image_data)
analyzer_msg_attachment.add_header('Content-ID', '<%s>' % analyzer_content_id)
msg.attach(analyzer_msg_attachment)
# @modified 20220203 - Feature #4416: settings - additional SMTP_OPTS
# s = SMTP('127.0.0.1')
if not smtp_server['ssl']:
s = SMTP(smtp_server['host'], smtp_server['port'])
else:
s = SMTP_SSL(smtp_server['host'], smtp_server['port'])
if smtp_server['user']:
s.login(smtp_server['user'], smtp_server['password'])
s.sendmail(sender, recipient, msg.as_string())
s.quit()
[docs]def negate_hipchat(alert, metric, second_order_resolution_seconds, metric_value):
# SECOND_ORDER_RESOLUTION_SECONDS to hours so that mirage surfaces the
# relevant timeseries data in the graph
second_order_resolution_in_hours = int(second_order_resolution_seconds) / 3600
# not really required
[docs]def negate_syslog(alert, metric, second_order_resolution_seconds, metric_value):
import sys
import syslog
# FULL_DURATION to hours so that mirage can surface the relevant timeseries data
# for analyzer comparison in the graph
full_duration_in_hours = int(settings.FULL_DURATION) / 3600
# SECOND_ORDER_RESOLUTION_SECONDS to hours so that mirage surfaces the
# relevant timeseries data in the graph
second_order_resolution_in_hours = int(second_order_resolution_seconds) / 3600
syslog_ident = settings.SYSLOG_OPTS['ident'] + '-mirage'
message = str("not anomalous at %s hours: %s (%s hours value: %s) - (%s hours value: %s)" % (second_order_resolution_in_hours, metric[1], full_duration_in_hours, metric_value, second_order_resolution_in_hours, metric[0]))
if sys.version_info[:2] == (2, 6):
syslog.openlog(syslog_ident, syslog.LOG_PID, syslog.LOG_LOCAL4)
elif sys.version_info[:2] == (2, 7):
syslog.openlog(ident="skyline", logoption=syslog.LOG_PID, facility=syslog.LOG_LOCAL4)
elif sys.version_info[:1] == (3):
syslog.openlog(ident="skyline", logoption=syslog.LOG_PID, facility=syslog.LOG_LOCAL4)
else:
syslog.openlog(syslog_ident, syslog.LOG_PID, syslog.LOG_LOCAL4)
syslog.syslog(4, message)
[docs]def trigger_negater(alert, metric, second_order_resolution_seconds, metric_value):
if alert[1] == 'smtp':
strategy = 'negate_analyzer_alert'
else:
strategy = 'negate_' + alert[1]
getattr(negaters, strategy)(alert, metric, second_order_resolution_seconds, metric_value)