Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Flexible templates and fixes #14

Merged
merged 10 commits into from
Dec 5, 2023
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.pytest_cache
__pycache__
94 changes: 65 additions & 29 deletions src/notify-email.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,13 @@

log_local = get_logger('email')

EMAIL_TEMPLATE_FILE = 'templates/base.html'
EMAIL_TEMPLATE = Template('')
EMAIL_TEMPLATE_DEFAULT_FILE = 'templates/default.html'
EMAIL_TEMPLATE_APP_PUB_FILE = 'templates/app-pub.html'

EMAIL_TEMPLATES = {
'default': Template('dummy'),
'app-pub': Template('dummy'),
}

NUVLA_API_LOCAL = 'http://api:8200'

Expand All @@ -41,10 +46,10 @@ def get_nuvla_config():
return resp.json()


def set_smpt_params():
def set_smtp_params():
global SMTP_HOST, SMTP_USER, SMTP_PASSWORD, SMTP_PORT, SMTP_SSL
try:
if 'SMTP_HOST' in os.environ:
if os.environ.get('SMTP_HOST') and len(os.environ.get('SMTP_HOST')) > 0:
SMTP_HOST = os.environ['SMTP_HOST']
SMTP_USER = os.environ['SMTP_USER']
SMTP_PASSWORD = os.environ['SMTP_PASSWORD']
Expand Down Expand Up @@ -84,35 +89,56 @@ def get_smtp_server(debug_level=0) -> smtplib.SMTP:
return server


def html_content(values: dict):
subs_config_link = f'<a href="{NUVLA_ENDPOINT}/ui/notifications">Notification configuration</a>'
def get_email_template(msg_params: dict) -> Template:
if 'TEMPLATE' not in msg_params:
return EMAIL_TEMPLATES['default']
tmpl_name = msg_params.get('TEMPLATE', 'default')
template = EMAIL_TEMPLATES.get(tmpl_name)
if template is None:
log_local.warning('Failed to find email template: %s', tmpl_name)
template = EMAIL_TEMPLATES['default']
return template


def html_content(msg_params: dict):
r_uri = msg_params.get('RESOURCE_URI')
link_text = msg_params.get('RESOURCE_NAME') or r_uri
component_link = f'<a href="{NUVLA_ENDPOINT}/ui/{r_uri}">{link_text}</a>'

r_uri = values.get('RESOURCE_URI')
r_name = values.get('RESOURCE_NAME')
component_link = f'<a href="{NUVLA_ENDPOINT}/ui/{r_uri}">{r_name or r_uri}</a>'
metric = values.get('METRIC')
condition = values.get('CONDITION')
if values.get('RECOVERY', False):
if msg_params.get('RECOVERY', False):
img_alert = IMG_ALERT_OK
title = f"[OK] {values.get('SUBS_NAME')}"
notif_title = f"[OK] {msg_params.get('SUBS_NAME')}"
else:
img_alert = IMG_ALERT_NOK
title = f"[Alert] {values.get('SUBS_NAME')}"
notif_title = f"[Alert] {msg_params.get('SUBS_NAME')}"

subs_name = 'Notification configuration'
subs_config_link = f'<a href="{NUVLA_ENDPOINT}/ui/notifications">{subs_name}</a>'

params = {
'title': title,
'subs_description': values.get('SUBS_DESCRIPTION'),
'title': notif_title,
'subs_description': msg_params.get('SUBS_DESCRIPTION'),
'component_link': component_link,
'metric': metric,
'condition': condition.upper(),
'timestamp': timestamp_convert(values.get('TIMESTAMP')),
'metric': msg_params.get('METRIC'),
'timestamp': timestamp_convert(msg_params.get('TIMESTAMP')),
'subs_config_link': subs_config_link,
'header_img': f'{NUVLA_ENDPOINT}/{img_alert}',
'current_year': str(datetime.now().year)
}
if values.get('VALUE'):
params['condition'] = f"{values.get('CONDITION')} {values.get('CONDITION_VALUE')}"
params['value'] = values.get('VALUE')
return EMAIL_TEMPLATE.render(**params)

if 'TRIGGER_RESOURCE_PATH' in msg_params:
resource_path = msg_params.get('TRIGGER_RESOURCE_PATH')
resource_name = msg_params.get('TRIGGER_RESOURCE_NAME')
params['trigger_link'] = \
f'<a href="{NUVLA_ENDPOINT}/ui/{resource_path}">{resource_name}</a>'

if msg_params.get('CONDITION'):
params['condition'] = msg_params.get('CONDITION')
if 'VALUE' in msg_params:
params['condition'] = f"{msg_params.get('CONDITION')} {msg_params.get('CONDITION_VALUE')}"
params['value'] = msg_params.get('VALUE')

return get_email_template(msg_params).render(**params)


def send(server: smtplib.SMTP, recipients, subject, html, attempts=SEND_EMAIL_ATTEMPTS):
Expand All @@ -125,9 +151,9 @@ def send(server: smtplib.SMTP, recipients, subject, html, attempts=SEND_EMAIL_AT
if i > 0:
log_local.warning(f'Failed sending email: retry {i}')
time.sleep(.5)
log_local.warning(f'Reconnecting to SMTP server...')
log_local.warning('Reconnecting to SMTP server...')
server = get_smtp_server()
log_local.warning(f'Reconnecting to SMTP server... done.')
log_local.warning('Reconnecting to SMTP server... done.')
try:
resp = server.sendmail(server.user, recipients, msg.as_string())
if resp:
Expand Down Expand Up @@ -160,14 +186,24 @@ def worker(workq: multiprocessing.Queue):
log_local.info(f'sent: {msg} to {recipients}')
except smtplib.SMTPException as ex:
log_local.error(f'Failed sending email due to SMTP error: {ex}')
log_local.warning(f'Reconnecting to SMTP server...')
log_local.warning('Reconnecting to SMTP server...')
smtp_server = get_smtp_server()
log_local.warning(f'Reconnecting to SMTP server... done.')
log_local.warning('Reconnecting to SMTP server... done.')
except Exception as ex:
log_local.error(f'Failed sending email: {ex}')


def email_template(template_file=EMAIL_TEMPLATE_DEFAULT_FILE):
return Template(open(template_file).read())


def init_email_templates(default=EMAIL_TEMPLATE_DEFAULT_FILE,
app_pub=EMAIL_TEMPLATE_APP_PUB_FILE):
EMAIL_TEMPLATES['default'] = email_template(default)
EMAIL_TEMPLATES['app-pub'] = email_template(app_pub)


if __name__ == "__main__":
EMAIL_TEMPLATE = Template(open(EMAIL_TEMPLATE_FILE).read())
set_smpt_params()
init_email_templates()
set_smtp_params()
main(worker, KAFKA_TOPIC, KAFKA_GROUP_ID)
144 changes: 82 additions & 62 deletions src/notify-slack.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,73 +26,93 @@ def now_timestamp():
return datetime.now().timestamp()


def message_content(values: dict):
subs_name = lt.sub('&lt;', gt.sub('&gt;', values.get('SUBS_NAME', '')))
subs_config_txt = f'<{NUVLA_ENDPOINT}/ui/notifications|{subs_name}>'

metric = values.get('METRIC')
if values.get('VALUE'):
cond_value = values.get('CONDITION_VALUE')
condition = f"{values.get('CONDITION')}"
criteria = f'_{metric}_ {gt.sub("&gt;", lt.sub("&lt;", condition))} *{cond_value}*'
value = f"*{values.get('VALUE')}*"
else:
condition = values.get('CONDITION', '').upper()
criteria = f'_{metric}_'
value = f'*{condition}*'

r_uri = values.get('RESOURCE_URI')
r_name = values.get('RESOURCE_NAME')
link_text = r_name or r_uri
component = f'<{NUVLA_ENDPOINT}/ui/{r_uri}|{link_text}>'

ts = timestamp_convert(values.get('TIMESTAMP'))
def message_content(msg_params: dict):
r_uri = msg_params.get('RESOURCE_URI')
link_text = msg_params.get('RESOURCE_NAME') or r_uri
component_link = f'<{NUVLA_ENDPOINT}/ui/{r_uri}|{link_text}>'

if values.get('RECOVERY', False):
if msg_params.get('RECOVERY', False):
color = COLOR_OK
notif_title = "[OK] Notification"
notif_title = f"[OK] {msg_params.get('SUBS_NAME')}"
else:
color = COLOR_NOK
notif_title = "[Alert] Notification"

return {
"attachments": [
notif_title = f"[Alert] {msg_params.get('SUBS_NAME')}"

subs_config_link = f'<{NUVLA_ENDPOINT}/ui/notifications|Notification configuration>'

# Order of the fields defines the layout of the message.

fields = [
{
'title': notif_title,
'value': subs_config_link,
'short': True
}
]

if 'TRIGGER_RESOURCE_PATH' in msg_params:
resource_path = msg_params.get('TRIGGER_RESOURCE_PATH')
resource_name = msg_params.get('TRIGGER_RESOURCE_NAME')
trigger_link = \
f'<{NUVLA_ENDPOINT}/ui/{resource_path}|{resource_name}>'
fields.append({
'title': 'Application was published',
'value': trigger_link,
'short': True
})

fields.append({
'title': 'Affected resource(s)',
'value': component_link,
'short': True
})

if msg_params.get('CONDITION'):
metric = msg_params.get('METRIC')
if 'VALUE' in msg_params:
cond_value = msg_params.get('CONDITION_VALUE')
condition = f"{msg_params.get('CONDITION')}"
criteria = f'_{metric}_ {gt.sub("&gt;", lt.sub("&lt;", condition))} *{cond_value}*'
value = f"*{msg_params.get('VALUE')}*"
else:
condition = msg_params.get('CONDITION', '').upper()
criteria = f'_{metric}_'
value = f'*{condition}*'

fields.extend([
{
'title': 'Criteria',
'value': criteria,
'short': True
},
{
"color": color,
"author_name": "Nuvla",
"author_link": "https://sixsq.com",
"author_icon": "https://sixsq.com/img/logo/logo_sixsq.png",
"fields": [
{
"title": notif_title,
"value": subs_config_txt,
"short": True
},
{
"title": "Affected resource",
"value": component,
"short": True
},
{
"title": "Criteria",
"value": criteria,
"short": True
},
{
"title": "Value",
"value": value,
"short": True
},
{
"title": "Event Timestamp",
"value": ts,
"short": True
}
],
"ts": now_timestamp()
}
]
}
'title': 'Value',
'value': value,
'short': True
}]
)

fields.append(
{
'title': 'Event Timestamp',
'value': timestamp_convert(msg_params.get('TIMESTAMP')),
'short': True
}
)

attachments = [{
'color': color,
'author_name': 'Nuvla.io',
'author_link': 'https://nuvla.io',
'author_icon': 'https://sixsq.com/assets/img/logo-sixsq.svg',
'fields': fields,
'footer': 'https://sixsq.com',
'footer_icon': 'https://sixsq.com/assets/img/logo-sixsq.svg',
'ts': now_timestamp()
}
]

return {'attachments': attachments}


def send_message(dest, message):
Expand Down
2 changes: 1 addition & 1 deletion src/notify_deps.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ def kafka_consumer(topic, bootstrap_servers, group_id, auto_offset_reset='latest

def timestamp_convert(ts):
return datetime.strptime(ts, '%Y-%m-%dT%H:%M:%SZ'). \
strftime('%a %d, %Y %H:%M:%S UTC')
strftime('%Y-%m-%d %H:%M:%S UTC')


def main(worker, kafka_topic, group_id):
Expand Down
Loading
Loading