Bash Script
This Bash script is required to execute the Python script.
# © 2022 by Cloudera, Inc. All rights reserved. # Scripts and sample code are licensed under the Apache License, # Version 2.0 #!/bin/bash python /bin/script_test/main.py $1
You can use this sample script to respond to an alert by sending emails based on the content of the alert.
The Sample Custom Alert Script provided below provides an example of how custom alert scripts can be implemented. This example uses a Python script, which includes multiple routing rules called Alert Templates. Once the script is called, it parses the alert JSON file using a path provided by the Alert Publisher. Based on the Alert Templates, it sends out alerts to different email addresses based on the content of the alert.
The script requires an SMTP server (defined by its host and port) and an email address to send out the emails. You need to set these parameters in the script.
There are multiple Alert Templates in the sample script as examples. Pick the ones you need and delete the rest, or create new templates.
Python should be always available on your hosts because hosts managed by Cloudera Manager require Python for the Cloudera Manager Agent to run. Because the Alert Publisher is not able to execute python scripts, a small Bash script is needed to execute the main Python script.
This Bash script is required to execute the Python script.
# © 2022 by Cloudera, Inc. All rights reserved. # Scripts and sample code are licensed under the Apache License, # Version 2.0 #!/bin/bash python /bin/script_test/main.py $1
To use the Python script, you will need to provide the values described below.
host = "mailserver.mycompany.com"
port = 25
sender_address = "noreply@mycompany.com"
AlertTemplate(email, attribute_key,
attribute_value, remove_after)
method:AlertTemplate("admin@mycompany.com", "SEVERITY", "CRITICAL", False)
AlertTemplate("admin@mycompany.com, admin2@mycompany.com", "SEVERITY", "CRITICAL", False)
attribute_key
and
attribute_value
containing the rules that
your alert should match for the email to be
sent:AlertTemplate("admin@mycompany.com", "SEVERITY", "CRITICAL", False),To use Regular Expression matching for your alerts, use
“REGEX”
as the
attribute_key
:
AlertTemplate("admin@mycompany.com", "REGEX", '.*content": "This is a test alert that was generated.*', False)
A None attribute_key
and
attribute_value
pair indicates a default
template, which should be applied to all alerts. You can use
this when there's no matching
AlertTemplate
, but you still do not want to
lose the Alert.
remove_after
: True
if you want the alert to not be
sent, even if further Alert Templates match the alert:
AlertTemplate("admin@mycompany.com", "SEVERITY", "CRITICAL", True),
You can copy this script and modify it for your deployment.
# -*- coding: utf-8 -*- # © 2022 by Cloudera, Inc. All rights reserved. # Scripts and sample code are licensed under the Apache License, # Version 2.0 #!/usr/bin/env python2 from __future__ import print_function import sys import smtplib import json import re from email.mime.text import MIMEText from email.mime.multipart import MIMEMultipart class AlertReader(object): def __init__(self, file_path): self.alerts = [] self.file_path = file_path def read_alert(self): alert_file = open(self.file_path, "r") data = json.load(alert_file) for alert in data: self.alerts.append(alert["body"]["alert"]) print("Done reading " + self.file_path) def print_alerts(self): print(self.alerts) def alert_as_string(alert): return json.dumps(alert) class AlertTemplate(object): def __init__(self, email, attribute_key, attribute_value, remove_after): self.email = email self.attribute_key = attribute_key self.attribute_value = attribute_value self.remove_after = remove_after """ A None attribute_key and attribute_value pair indicates a default template, which should be applied to all alerts. You can use this when there's no matching AlertTemplate, but you still don't want to lose the Alert. Put these AlertTemplates to the end of your AlertTemplate list to catch all remaining alerts. But if only one of the attribute_key or attribute_value is None, that is an invalid state. """ if (self.attribute_key is None) != (self.attribute_value is None): raise ValueError("AlertTemplate is in invalid state, " "one of attribute_key or attribute_value is None.") def does_apply(self, alert): if self.attribute_key == "REGEX": alert_string = alert_as_string(alert) search_result = re.search(self.attribute_value, alert_string) return search_result is not None if self.attribute_key == "CONTENT": if self.attribute_value in alert["content"]: return True return False if self.attribute_key is None and self.attribute_value is None: return True try: if self.attribute_value in alert["attributes"][self.attribute_key]: return True except KeyError: print("There's no match for attribute_key: " + self.attribute_key) return False def to_string(self): return "AlertTemplate[" + \ str(self.attribute_key) + ": " + \ str(self.attribute_value) + ", " + \ str(self.email) + "]" class EmailSender(object): def __init__(self, smtp_host, smtp_port, sender): self.server = smtplib.SMTP(smtp_host, smtp_port) self.sender = sender def send_email_to_recipients(self, alerts, alert_templates): print("Processing alerts to send emails.") for alert in alerts: for alert_template in alert_templates: if alert_template.does_apply(alert): """ With a template with no email address, you usually want to exclude a type of alert from the processed alert list. Use None to email address with remove_after = True if you want an alert to be removed from the list of alerts sent. These templates should be in the beginning of the template list. """ if alert_template.email is not None: self.send_email(alert_template, alert) print(create_message_text(alert_template, alert)) print("Sending alert based on " + alert_template.to_string()) if alert_template.remove_after: break print("All alerts are processed.") def send_email(self, alert_template, alert): message = MIMEMultipart("alternative") message["Subject"] = "Cloudera Alert" message["From"] = self.sender message["To"] = alert_template.email message.attach(MIMEText(create_message_text(alert_template, alert), "plain")) self.server.sendmail(self.sender, alert_template.email.split(","), message.as_string()) def create_message_text(alert_template, alert): out = alert_template.to_string() out += " firing for: \n" out += alert_as_string(alert) return out if __name__ == '__main__': """ SMTP server parameters: - email where the emails sent from - file name, where the alerts are coming from. This is provided by the script interface. Alert templates: - email address to send email to when the alert contains the specified key with the specified value. - Set remove_after to True if you don't want the alert sent to other recipients, even if the template is matching. Because of this, it is required that the recipients list is in a specific order. """ host = "mailserver.mycompany.com" # SMTP server host name port = 25 # SMTP Server port sender_address = "noreply@"MyCompanyMailServer" # Email address to send emails from # The file's name which contains the alerts is provided by the script framework if len(sys.argv) < 2: print("Please provide a file path for alerts. Usually this comes from the script framework.") exit(2) file_name = sys.argv[1] alert_reader = AlertReader(file_name) recipients = [] try: recipients = [AlertTemplate(None, None, None, True), # This template matches every alert # This template matches to all alerts which matches to the given regex pattern AlertTemplate("admin@mycompany.com", "REGEX", '.*content": "This is a test alert that was generated.*', False), # This template matches only for issues with critical severity AlertTemplate("admin@mycompany.com", "SEVERITY", "CRITICAL", False), # This template matches only for Kafka related issues AlertTemplate("kafka_admin@mycompany.com", "SERVICE_TYPE", "KAFKA", True), # This template matches only for Kafka Broker related issues AlertTemplate("user1@gmail.com", "ROLE_TYPE", "KAFKA_BROKER", True), # This template matches only for a specific host's alerts AlertTemplate("admin2@mycompany.com", "HOSTS", "myhost", True), # This template matches for a specific cluster's alerts AlertTemplate("admin3@mycompany.com", "CLUSTER", "Cluster 1", True), # This template matches for a specific health test message AlertTemplate("admin@mycompany.com", "CONTENT", "This is a test alert that was generated by user request from " "Cloudera Manager", True)] except ValueError as error: print(error) exit(2) alert_reader.read_alert() alert_reader.print_alerts() email_sender = EmailSender(host, port, sender_address) email_sender.send_email_to_recipients(alert_reader.alerts, recipients)