#!/usr/bin/env python3
#
# mail-alarm: uses ssmtp to send a mail message, to pool:other_config:mail-destination
#
# If /etc/mail-alarm.conf exists then it is used as the ssmtp config.
# However, this script first replaces any macros with keys from pool:other-config.
# For example, if /etc/mail-alarm.conf contains the text @MYMACRO@ then it will
# be replaced by pool:other-config:ssmtp-mymacro
#
# If /etc/mail-alarm.conf does not exist the default_config string below is used and
# the only thing that needs be set is pool:other-config:ssmtp-mailhub

from __future__ import print_function

import json
import os
import re
import subprocess
import sys
import syslog
import tempfile
import traceback
from socket import getfqdn
from xml.dom import minidom  # pytype: disable=pyi-error

import XenAPI
from xcp import branding

# Go read man ssmtp.conf
default_config = """
mailhub=@MAILHUB@
FromLineOverride=YES
"""


mail_language_pack_path = "/etc/xapi.d/mail-languages/"


def log_err(err):
    print(err, file=sys.stderr)
    syslog.syslog(syslog.LOG_USER | syslog.LOG_ERR, "%s: %s" % (sys.argv[0], err))


def get_pool_name(session):
    opaque_ref = session.xenapi.pool.get_all()[0]
    pool_name = session.xenapi.pool.get_name_label(opaque_ref)
    if pool_name == "":
        master_ref = session.xenapi.pool.get_master(opaque_ref)
        master_name = session.xenapi.host.get_name_label(master_ref)
        return master_name
    else:
        return pool_name


def get_sr_name_by_uuid(sr_uuid, session):
    opaque_ref = session.xenapi.SR.get_by_uuid(sr_uuid)
    sr_name = session.xenapi.SR.get_name_label(opaque_ref)
    return sr_name


def get_pool_other_config(session):
    opaque_ref = session.xenapi.pool.get_all()[0]
    return session.xenapi.pool.get_other_config(opaque_ref)


def get_VM_params(uuid, session):
    try:
        opaque_ref = session.xenapi.VM.get_by_uuid(uuid)
        return session.xenapi.VM.get_record(opaque_ref)
    except:
        return {}


def get_host_params(uuid, session):
    try:
        opaque_ref = session.xenapi.host.get_by_uuid(uuid)
        return session.xenapi.host.get_record(opaque_ref)
    except:
        return {}


def get_search_replace(other_config):
    sr = []
    for key in other_config:
        if key.startswith("ssmtp-"):
            replacement_text = other_config[key]
            search_text = "@" + key[6:].upper() + "@"
            sr.append((search_text, replacement_text))
    return sr


def get_destination(other_config):
    if "mail-destination" in other_config:
        return other_config["mail-destination"]


def get_sender(other_config):
    if "mail-sender" in other_config:
        return other_config["mail-sender"]


def get_mail_language(other_config):
    if "mail-language" in other_config:
        return other_config["mail-language"]
    else:
        return "en-US"


def get_config_file():
    try:
        with open("/etc/mail-alarm.conf", "r") as file:
            return file.read()
    except:
        return default_config


def load_mail_language(mail_language):
    mail_language_file = ""
    try:
        mail_language_file = os.path.join(
            mail_language_pack_path, mail_language + ".json"
        )

        with open(mail_language_file, encoding="utf-8") as fileh:
            return json.load(fileh)

    except IOError:
        log_err('Read mail language pack error:["%s"]' % (mail_language_file))
        return None
    except ValueError:
        log_err('Decode JSON string error:["%s"]' % (mail_language_file))
        return None
    except Exception as exn:
        log_err(
            'Unknown Error from Decode ["%s"] to JSON object: "%s"'
            % (mail_language_file, str(exn))
        )
        return None


class EmailTextGenerator(object):
    def __init__(self, mail_language, mail_language_component):
        self.mail_language = mail_language
        self.mail_language_component = mail_language_component

    def mail_err(self, key):
        log_err(
            "[%s] error in looking for (%s:%s). Mail language pack:(%s)."
            % (
                self.__class__.__name__,
                self.mail_language_component,
                key,
                self.mail_language,
            )
        )

    def mail_subject(self):
        try:
            return self.mail_language["mail-alarms"][self.mail_language_component][
                "subject"
            ]
        except KeyError:
            self.mail_err("subject")
            return None
        except TypeError:
            self.mail_err("subject")
            return None
        except Exception as err:
            log_err("[%s] unexpected failure: %s" % (self.__class__.__name__, str(err)))
            raise

    def mail_body(self):
        try:
            return self.mail_language["mail-alarms"][self.mail_language_component][
                "body"
            ]
        except KeyError:
            self.mail_err("body")
            return None
        except TypeError:
            self.mail_err("body")
            return None
        except Exception as err:
            log_err("[%s] unexpected failure: %s" % (self.__class__.__name__, str(err)))
            raise


class CpuUsageAlarmETG(EmailTextGenerator):
    def __init__(
        self,
        cls,
        obj_uuid,
        value,
        alarm_trigger_period,
        alarm_trigger_level,
        mail_language,
        session,
    ):
        super(CpuUsageAlarmETG, self).__init__(mail_language, "cpu_usage_alarm")
        if alarm_trigger_period is None:
            alarm_trigger_period = 60
        if cls == "Host":
            self.params = get_host_params(obj_uuid, session)
        elif cls == "VM":
            self.params = get_VM_params(obj_uuid, session)
        else:
            raise Exception(
                "programmer error - this alarm should only be available for Hosts and VMs"
            )
        self.cls = cls
        self.value = value
        self.alarm_trigger_period = alarm_trigger_period
        self.alarm_trigger_level = alarm_trigger_level
        self.session = session

    def generate_subject(self):
        self.pool_name = get_pool_name(self.session)
        subject_pattern = self.mail_subject()
        if subject_pattern is None:
            subject_pattern = (
                '[{pool_name}] {product_brand} Alarm: CPU usage on {cls} "{name_label}"'
            )

        return subject_pattern.format(
            pool_name=self.pool_name,
            product_brand=branding.PRODUCT_BRAND,
            cls=self.cls,
            name_label=self.params["name_label"],
        )

    def generate_body(self):
        body_pattern = self.mail_body()
        if body_pattern is None:
            body_pattern = (
                'CPU usage on {cls} "{name_label}" has been on average {value}% for the last {period} seconds.\n'
                "This alarm is set to be triggered when CPU usage is more than {level}%.\n"
                "\n"
                'For Alarm Settings, please log into your {brand_console} Console and click on "{cls_name}"->\n'
                '"Properties"->"Alerts"\n'
            )

        return body_pattern.format(
            cls=self.cls,
            name_label=self.params["name_label"],
            value="%.1f" % (self.value * 100.0),
            period="%d" % self.alarm_trigger_period,
            level="%.1f" % (self.alarm_trigger_level * 100.0),
            brand_console=branding.BRAND_CONSOLE,
            cls_name=(self.cls == "Host" or self.params["is_control_domain"])
            and "Server"
            or "VM",
        )


class NetworkUsageAlarmETG(EmailTextGenerator):
    def __init__(
        self,
        cls,
        obj_uuid,
        value,
        alarm_trigger_period,
        alarm_trigger_level,
        mail_language,
        session,
    ):
        super(NetworkUsageAlarmETG, self).__init__(mail_language, "network_usage_alarm")
        if alarm_trigger_period is None:
            alarm_trigger_period = 60
        if cls == "Host":
            self.params = get_host_params(obj_uuid, session)
        elif cls == "VM":
            self.params = get_VM_params(obj_uuid, session)
        else:
            raise Exception(
                "programmer error - this alarm should only be available for Hosts and VMs"
            )
        self.cls = cls
        self.value = value
        self.alarm_trigger_period = alarm_trigger_period
        self.alarm_trigger_level = alarm_trigger_level
        self.session = session

    def generate_subject(self):
        self.pool_name = get_pool_name(self.session)
        subject_pattern = self.mail_subject()
        if subject_pattern is None:
            subject_pattern = '[{pool_name}] {product_brand} Alarm: Network usage on {cls} "{name_label}"'

        return subject_pattern.format(
            pool_name=self.pool_name,
            product_brand=branding.PRODUCT_BRAND,
            cls=self.cls,
            name_label=self.params["name_label"],
        )

    def generate_body(self):
        body_pattern = self.mail_body()
        if body_pattern is None:
            body_pattern = (
                'Network usage on {cls} "{name_label}" has been on average {value} B/s for the last {period} seconds.\n'
                "This alarm is set to be triggered when Network usage is more than {level} B/s.\n\n"
                'For Alarm Settings, please log into your {brand_console} Console and click on "{cls_name}"->\n'
                '"Properties"->"Alerts"\n'
            )

        return body_pattern.format(
            cls=self.cls,
            name_label=self.params["name_label"],
            value="%d" % self.value,
            period="%d" % self.alarm_trigger_period,
            level="%d" % self.alarm_trigger_level,
            brand_console=branding.BRAND_CONSOLE,
            cls_name=(self.cls == "Host") and "Server" or "VM",
        )


class MemoryUsageAlarmETG(EmailTextGenerator):
    def __init__(
        self,
        cls,
        obj_uuid,
        value,
        alarm_trigger_period,
        alarm_trigger_level,
        mail_language,
        session,
    ):
        super(MemoryUsageAlarmETG, self).__init__(mail_language, "memory_usage_alarm")
        if alarm_trigger_period is None:
            alarm_trigger_period = 60
        if cls == "Host":
            self.params = get_host_params(obj_uuid, session)
        elif cls == "VM":
            self.params = get_VM_params(obj_uuid, session)
        else:
            raise Exception(
                "programmer error - this alarm should only be available for hosts and VMs"
            )
        self.cls = cls
        self.value = value
        self.alarm_trigger_period = alarm_trigger_period
        self.alarm_trigger_level = alarm_trigger_level
        self.session = session

    def generate_subject(self):
        self.pool_name = get_pool_name(self.session)
        subject_pattern = self.mail_subject()
        if subject_pattern is None:
            subject_pattern = '[{pool_name}] {product_brand} Alarm: Memory usage on {cls} "{name_label}"'

        return subject_pattern.format(
            pool_name=self.pool_name,
            product_brand=branding.PRODUCT_BRAND,
            cls=self.cls,
            name_label=self.params["name_label"],
        )

    def generate_body(self):
        body_pattern = self.mail_body()
        if body_pattern is None:
            body_pattern = (
                'Free memory on {cls} "{name_label}" has been on average {value} KiB for the last {period} seconds.\n'
                "This alarm is set to be triggered when free memory is less than {level} KiB.\n\n"
                'For Alarm Settings, please log into your {brand_console} Console and click on "{cls_name}"->\n'
                '"Properties"->"Alerts"\n'
            )

        return body_pattern.format(
            cls=self.cls,
            name_label=self.params["name_label"],
            value="%d" % self.value,
            period="%d" % self.alarm_trigger_period,
            level="%d" % self.alarm_trigger_level,
            brand_console=branding.BRAND_CONSOLE,
            cls_name=(self.cls == "Host" or self.params["is_control_domain"])
            and "Server"
            or "VM",
        )


class DiskUsageAlarmETG(EmailTextGenerator):
    def __init__(
        self,
        cls,
        obj_uuid,
        value,
        alarm_trigger_period,
        alarm_trigger_level,
        mail_language,
        session,
    ):
        super(DiskUsageAlarmETG, self).__init__(mail_language, "disk_usage_alarm")
        if alarm_trigger_period is None:
            alarm_trigger_period = 60
        if cls != "VM":
            raise Exception(
                "programmer error - this alarm should only be available for VMs"
            )
        self.params = get_VM_params(obj_uuid, session)
        self.cls = cls
        self.value = value
        self.alarm_trigger_period = alarm_trigger_period
        self.alarm_trigger_level = alarm_trigger_level
        self.session = session

    def generate_subject(self):
        self.pool_name = get_pool_name(self.session)
        subject_pattern = self.mail_subject()
        if subject_pattern is None:
            subject_pattern = (
                '[{pool_name}] {product_brand} Alarm: Disk usage on VM "{name_label}"'
            )

        return subject_pattern.format(
            pool_name=self.pool_name,
            product_brand=branding.PRODUCT_BRAND,
            name_label=self.params["name_label"],
        )

    def generate_body(self):
        body_pattern = self.mail_body()
        if body_pattern is None:
            body_pattern = (
                'Disk usage on VM "{name_label}" has been on average {value} B/s for the last {period} seconds.\n'
                "This alarm is set to be triggered when Disk usage is more than {level} B/s.\n\n"
                'For Alarm Settings, please log into your {brand_console} Console and click on "VM"->\n'
                '"Properties"->"Alerts"\n'
            )

        return body_pattern.format(
            name_label=self.params["name_label"],
            value="%d" % self.value,
            period="%d" % self.alarm_trigger_period,
            level="%d" % self.alarm_trigger_level,
            brand_console=branding.BRAND_CONSOLE,
        )


class Dom0FSUsageAlarmETG(EmailTextGenerator):
    def __init__(
        self, cls, obj_uuid, value, alarm_trigger_level, mail_language, session
    ):
        super(Dom0FSUsageAlarmETG, self).__init__(mail_language, "dom0fs_usage_alarm")
        if alarm_trigger_level is None:
            alarm_trigger_level = 0.9
        if cls != "VM":
            raise Exception(
                "programmer error - this alarm should only be available for control domain VM"
            )
        self.params = get_VM_params(obj_uuid, session)
        self.cls = cls
        self.value = value
        self.alarm_trigger_level = alarm_trigger_level
        self.session = session

    def generate_subject(self):
        self.pool_name = get_pool_name(self.session)
        subject_pattern = self.mail_subject()
        if subject_pattern is None:
            subject_pattern = '[{pool_name}] {product_brand} Alarm: Filesystem nearly full on "{name_label}"'

        return subject_pattern.format(
            pool_name=self.pool_name,
            product_brand=branding.PRODUCT_BRAND,
            name_label=self.params["name_label"],
        )

    def generate_body(self):
        body_pattern = self.mail_body()
        if body_pattern is None:
            body_pattern = (
                'The filesystem usage on "{name_label}" is at {value}%.\n'
                "This alarm is set to be triggered when filesystem usage is more than {level}%.\n\n"
            )

        return body_pattern.format(
            name_label=self.params["name_label"],
            value="%.1f" % (self.value * 100.0),
            level="%.1f" % (self.alarm_trigger_level * 100.0),
        )


class Dom0LogFSUsageAlarmETG(EmailTextGenerator):
    def __init__(
        self, cls, obj_uuid, value, alarm_trigger_level, mail_language, session
    ):
        super(Dom0LogFSUsageAlarmETG, self).__init__(
            mail_language, "dom0logfs_usage_alarm"
        )
        if alarm_trigger_level is None:
            alarm_trigger_level = 0.9
        if cls != "VM":
            raise Exception(
                "programmer error - this alarm should only be available for control domain VM"
            )
        self.params = get_VM_params(obj_uuid, session)
        self.cls = cls
        self.value = value
        self.alarm_trigger_level = alarm_trigger_level
        self.session = session

    def generate_subject(self):
        self.pool_name = get_pool_name(self.session)
        subject_pattern = self.mail_subject()
        if subject_pattern is None:
            subject_pattern = '[{pool_name}] {product_brand} Alarm: Log partition nearly full on "{name_label}"'

        return subject_pattern.format(
            pool_name=self.pool_name,
            product_brand=branding.PRODUCT_BRAND,
            name_label=self.params["name_label"],
        )

    def generate_body(self):
        body_pattern = self.mail_body()
        if body_pattern is None:
            body_pattern = (
                'The log partition usage on "{name_label}" is at {value}%.\n'
                "This alarm is set to be triggered when log partition usage is more than {level}%.\n\n"
            )

        return body_pattern.format(
            name_label=self.params["name_label"],
            value="%.1f" % (self.value * 100.0),
            level="%.1f" % (self.alarm_trigger_level * 100.0),
        )


class Dom0MemUsageAlarmETG(EmailTextGenerator):
    def __init__(
        self, cls, obj_uuid, value, alarm_trigger_level, mail_language, session
    ):
        super(Dom0MemUsageAlarmETG, self).__init__(
            mail_language, "dom0memory_usage_alarm"
        )
        if alarm_trigger_level is None:
            alarm_trigger_level = 0.95
        if cls != "VM":
            raise Exception(
                "programmer error - this alarm should only be available for control domain VM"
            )
        self.params = get_VM_params(obj_uuid, session)
        self.cls = cls
        self.value = value
        self.alarm_trigger_level = alarm_trigger_level
        self.session = session

    def generate_subject(self):
        self.pool_name = get_pool_name(self.session)
        subject_pattern = self.mail_subject()
        if subject_pattern is None:
            subject_pattern = '[{pool_name}] {product_brand} Alarm: Dom0 memory demand is high on "{name_label}"'

        return subject_pattern.format(
            pool_name=self.pool_name,
            product_brand=branding.PRODUCT_BRAND,
            name_label=self.params["name_label"],
        )

    def generate_body(self):
        body_pattern = self.mail_body()
        if body_pattern is None:
            body_pattern = (
                'The memory required by the control domain on "{name_label}" is about {value}% of its allocated memory. '
                "Occasional performance degradation can be expected when memory swapping is forced to happen.\n"
                "This alarm is set to be triggered when the memory required by the control domain is above {level}% of its allocated memory.\n\n"
            )

        return body_pattern.format(
            name_label=self.params["name_label"],
            value="%.1f" % (self.value * 100.0),
            level="%.1f" % (self.alarm_trigger_level * 100.0),
        )


class WlbConsultationFailure(EmailTextGenerator):
    def __init__(self, cls, obj_uuid, mail_language, session):
        super(WlbConsultationFailure, self).__init__(
            mail_language, "wlb_consultation_failure"
        )
        self.cls = cls
        self.params = get_VM_params(obj_uuid, session)
        self.session = session

    def generate_subject(self):
        self.pool_name = get_pool_name(self.session)
        subject_pattern = self.mail_subject()
        if subject_pattern is None:
            subject_pattern = '[{pool_name}] {product_brand} Alarm: Attempt to consult WLB for VM "{name_label}" failed'

        return subject_pattern.format(
            pool_name=self.pool_name,
            product_brand=branding.PRODUCT_BRAND,
            name_label=self.params["name_label"],
        )

    def generate_body(self):
        body_pattern = self.mail_body()
        if body_pattern is None:
            body_pattern = (
                "A workload balancing consultation for VM {name_label} failed.\n"
                "The operation was completed using the default algorithm instead of a workload balancing recommendation.\n\n"
            )

        return body_pattern.format(name_label=self.params["name_label"])


class WlbOptimizationAlert(EmailTextGenerator):
    def __init__(self, optimization_mode, severity, mail_language, session):
        super(WlbOptimizationAlert, self).__init__(
            mail_language, "wlb_optimization_alert"
        )
        self.optimization_mode = optimization_mode
        self.severity = severity
        self.pool_name = get_pool_name(session)

    def generate_subject(self):
        subject_pattern = self.mail_subject()
        if subject_pattern is None:
            subject_pattern = (
                "Workload Balancing Alert: Optimization alert from pool {pool_name}"
            )

        return subject_pattern.format(pool_name=self.pool_name)

    def generate_body(self):
        body_pattern = self.mail_body()
        if body_pattern is None:
            body_pattern = (
                "The Workload Balancing Server has reported that pool {pool_name} is in need of optimization.\n"
                "{pool_name} is in optimization mode {optimization_mode} and is in a {severity} state.\n\n"
            )

        return body_pattern.format(
            pool_name=self.pool_name,
            optimization_mode=self.optimization_mode,
            severity=self.severity,
        )


class HAHostFailedETG(EmailTextGenerator):
    def __init__(self, text, mail_language, session):
        super(HAHostFailedETG, self).__init__(mail_language, "ha_host_failed")
        self.text = text
        self.session = session

    def generate_subject(self):
        self.pool_name = get_pool_name(self.session)
        subject_pattern = self.mail_subject()
        if subject_pattern is None:
            subject_pattern = "[{pool_name}] {product_brand} HA Alarm: {text}"

        return subject_pattern.format(
            pool_name=self.pool_name,
            product_brand=branding.PRODUCT_BRAND,
            text=self.text,
        )

    def generate_body(self):
        body_pattern = self.mail_body()
        if body_pattern is None:
            body_pattern = (
                "{text}\n\n"
                "This alarm is set to be triggered when a host belonging to a high availability pool fails.\n"
            )

        return body_pattern.format(text=self.text)


class SRIOThroughputTotalAlertETG(EmailTextGenerator):
    def __init__(
        self,
        cls,
        obj_uuid,
        value,
        alarm_trigger_period,
        alarm_trigger_level,
        sr_uuid,
        mail_language,
        session,
    ):
        super(SRIOThroughputTotalAlertETG, self).__init__(
            mail_language, "sr_io_throughput_total_alert"
        )
        if alarm_trigger_period is None:
            alarm_trigger_period = 60
        if cls == "Host":
            self.params = get_host_params(obj_uuid, session)
        else:
            raise Exception(
                "programmer error - this alarm should only be available for Hosts"
            )
        self.value = value
        self.alarm_trigger_period = alarm_trigger_period
        self.alarm_trigger_level = alarm_trigger_level
        self.sr_name = get_sr_name_by_uuid(sr_uuid, session)
        self.session = session

    def generate_subject(self):
        self.pool_name = get_pool_name(self.session)
        subject_pattern = self.mail_subject()
        if subject_pattern is None:
            subject_pattern = '[{pool_name}] Storage Throughput Alarm: The total IO throughput on "{sr_name}"'

        return subject_pattern.format(pool_name=self.pool_name, sr_name=self.sr_name)

    def generate_body(self):
        body_pattern = self.mail_body()
        if body_pattern is None:
            body_pattern = (
                'The total read and write throughput of server "{name_label}" on storage repository '
                '"{sr_name}" has been {value} MB/s for the last {period} seconds.\n'
                "This alarm is set to be triggered when the total throughput exceeds {level} KB/s.\n\n"
                'For Alarm Settings, please log into your {brand_console} Console and click on "Storage"->\n'
                '"Properties"->"Alerts"\n'
            )

        return body_pattern.format(
            name_label=self.params["name_label"],
            sr_name=self.sr_name,
            value="%.1f" % self.value,
            period="%d" % self.alarm_trigger_period,
            level="%d" % (self.alarm_trigger_level * 1024),
            brand_console=branding.BRAND_CONSOLE,
        )


class XapiMessage:
    def __init__(self, xml, mail_language, session):
        "Parse message XML"
        try:
            xmldoc = minidom.parseString(xml)

            def get_text(tag):
                text = xmldoc.getElementsByTagName(tag)[0].firstChild
                if text is None:
                    raise ValueError("Get text failed with tag <{}>".format(tag))
                return text.toxml()

            self.name = get_text("name")
            self.priority = get_text("priority")
            self.cls = get_text("cls")
            self.obj_uuid = get_text("obj_uuid")
            self.timestamp = get_text("timestamp")
            self.uuid = get_text("uuid")
        except:
            log_err("Badly formatted XML, or missing field")
            sys.exit(1)
        try:
            self.body = get_text("body")
        except:
            self.body = ""
        self.pool_name = get_pool_name(session)
        self.mail_language = load_mail_language(mail_language)
        self.session = session

    def get_priority(self):
        return int(self.priority)

    def get_cls(self):
        return self.cls

    def get_obj_uuid(self):
        return self.obj_uuid

    def get_message(self, msg):
        # Extract the current level of the variable
        # (this will raise an exception if the 1st line of <body> is not in the correct format, namely "value: %f\n")
        value_line = msg.split("\n", 2)[0]
        key, val = value_line.split(":", 2)
        assert key == "value"
        value = float(val)

        # Extract a few key config elements
        config_xml_escaped = msg.split("config:")[1]
        config_xml = (
            config_xml_escaped.replace("&gt;", ">")
            .replace("&lt;", "<")
            .replace("&quot;", '"')
        )
        config_xmldoc = minidom.parseString(config_xml)

        def get_alarm_config(tag, cast):
            try:
                return cast(
                    config_xmldoc.getElementsByTagName(tag)[0].getAttribute("value")
                )
            except:
                return None

        def get_alarm_uuid(tag, cast):
            try:
                return cast(
                    config_xmldoc.getElementsByTagName(tag)[0].getAttribute("uuid")
                )
            except:
                return None

        name = get_alarm_config("name", str)
        alarm_trigger_level = get_alarm_config("alarm_trigger_level", float)
        alarm_trigger_period = get_alarm_config("alarm_trigger_period", int)
        uuid = get_alarm_uuid("configured_on", str)

        return (value, name, uuid, alarm_trigger_level, alarm_trigger_period)

    def __get_email_text_generator(self):
        """Returns an EmailTextGenerator object appropriate to this XapiMessage or None if none found"""
        if hasattr(self, "cached_etg"):
            return self.cached_etg

        if self.name == "ALARM":
            (
                value,
                name,
                uuid,
                alarm_trigger_level,
                alarm_trigger_period,
            ) = self.get_message(self.body)

            # Set the alarm text generator
            if name == "cpu_usage":
                etg = CpuUsageAlarmETG(
                    self.cls,
                    self.obj_uuid,
                    value,
                    alarm_trigger_period,
                    alarm_trigger_level,
                    self.mail_language,
                    self.session,
                )
            elif name == "network_usage":
                etg = NetworkUsageAlarmETG(
                    self.cls,
                    self.obj_uuid,
                    value,
                    alarm_trigger_period,
                    alarm_trigger_level,
                    self.mail_language,
                    self.session,
                )
            elif name in [
                "memory_free_kib",  # for Host
                "memory_internal_free",  # for VM
            ]:
                etg = MemoryUsageAlarmETG(
                    self.cls,
                    self.obj_uuid,
                    value,
                    alarm_trigger_period,
                    alarm_trigger_level,
                    self.mail_language,
                    self.session,
                )
            elif name == "disk_usage":
                etg = DiskUsageAlarmETG(
                    self.cls,
                    self.obj_uuid,
                    value,
                    alarm_trigger_period,
                    alarm_trigger_level,
                    self.mail_language,
                    self.session,
                )
            elif name == "fs_usage":
                etg = Dom0FSUsageAlarmETG(
                    self.cls,
                    self.obj_uuid,
                    value,
                    alarm_trigger_level,
                    self.mail_language,
                    self.session,
                )
            elif name == "log_fs_usage":
                etg = Dom0LogFSUsageAlarmETG(
                    self.cls,
                    self.obj_uuid,
                    value,
                    alarm_trigger_level,
                    self.mail_language,
                    self.session,
                )
            elif name == "mem_usage":
                etg = Dom0MemUsageAlarmETG(
                    self.cls,
                    self.obj_uuid,
                    value,
                    alarm_trigger_level,
                    self.mail_language,
                    self.session,
                )
            elif name and re.match("sr_io_throughput_total_[0-9a-f]{8}$", name):
                etg = SRIOThroughputTotalAlertETG(
                    self.cls,
                    self.obj_uuid,
                    value,
                    alarm_trigger_period,
                    alarm_trigger_level,
                    uuid,
                    self.mail_language,
                    self.session,
                )
            else:
                etg = None
        elif self.name == "HA_HOST_FAILED":
            etg = HAHostFailedETG(self.body, self.mail_language, self.session)
        elif self.name == "WLB_CONSULTATION_FAILED":
            etg = WlbConsultationFailure(
                self.cls, self.obj_uuid, self.mail_language, self.session
            )
        elif self.name == "WLB_OPTIMIZATION_ALERT":
            severity_line = self.body.split()[0]
            severity = str(severity_line.split("severity:")[1])
            mode_line = self.body.split()[1]
            optimization_mode = str(mode_line.split("mode:")[1])
            etg = WlbOptimizationAlert(
                optimization_mode, severity, self.mail_language, self.session
            )
        else:
            etg = None

        self.cached_etg = etg
        return etg

    def generate_email_subject(self):
        generator = self.__get_email_text_generator()
        if generator:
            return generator.generate_subject()
        else:
            return "[%s] %s Message: %s %s %s" % (
                self.pool_name,
                branding.PRODUCT_BRAND,
                self.cls,
                self.obj_uuid,
                self.name,
            )

    def generate_email_body(self):
        generator = self.__get_email_text_generator()
        if generator:
            return generator.generate_body()
        else:
            try:
                (
                    value,
                    name,
                    _,
                    alarm_trigger_level,
                    alarm_trigger_period,
                ) = self.get_message(self.body)
                return (
                    "Field\t\tValue\n-----\t\t-----\nName:\t\t%s\nPriority:\t%s\nClass:\t\t%s\n"
                    "Object UUID:\t%s\nTimestamp:\t%s\nMessage UUID:\t%s\nPool name:\t%s\nBody:\n"
                    "Parameter=%s\nValue=%f\nAlarm trigger value=%s\nAlarm trigger period=%s"
                    % (
                        self.name,
                        self.priority,
                        self.cls,
                        self.obj_uuid,
                        self.timestamp,
                        self.uuid,
                        self.pool_name,
                        name,
                        value,
                        alarm_trigger_level,
                        alarm_trigger_period,
                    )
                )
            except:
                msg = (
                    self.body.replace("&gt;", ">")
                    .replace("&lt;", "<")
                    .replace("&quot;", '"')
                )
                return (
                    "Field\t\tValue\n-----\t\t-----\nName:\t\t%s\nPriority:\t%s\nClass:\t\t%s\n"
                    "Object UUID:\t%s\nTimestamp:\t%s\nMessage UUID:\t%s\nPool name:\t%s\nBody:\t%s\n"
                    % (
                        self.name,
                        self.priority,
                        self.cls,
                        self.obj_uuid,
                        self.timestamp,
                        self.uuid,
                        self.pool_name,
                        msg,
                    )
                )


def main():
    if len(sys.argv) < 2:
        log_err(
            'Expected at least 1 argument but got none: ["%s"].' % (" ".join(sys.argv))
        )
        raise Exception("Insufficient arguments")

    session = XenAPI.xapi_local()
    ma_username = "__dom0__mail_alarm"
    session.xenapi.login_with_password(
        ma_username, "", "1.0", "xen-api-scripts-mail-alarm"
    )

    try:
        other_config = get_pool_other_config(session)
        if "mail-min-priority" in other_config:
            min_priority = int(other_config["mail-min-priority"])
        else:
            min_priority = 3

        charset = other_config.get("mail-charset", "utf-8")
        mail_language = get_mail_language(other_config)
        msg = XapiMessage(sys.argv[1], mail_language, session)

        # We only mail messages with priority lower than or equal to max_priority
        if msg.get_priority() > min_priority:
            return 0

        config = get_config_file()

        search_replace = get_search_replace(other_config)
        destination = get_destination(other_config)
        sender = get_sender(other_config)

        if not destination:
            log_err("pool:other-config:mail-destination not specified")
            return 1

        if not sender:
            sender = "noreply@%s" % getfqdn()

        # Replace macros in config file using search_replace list
        for s, r in search_replace:
            config = config.replace(s, r)

        # Write out a temporary file containing the new config
        temp_file_path = ""
        try:
            with tempfile.NamedTemporaryFile(
                prefix="mail-", dir="/tmp", delete=False
            ) as temp_file:
                temp_file.write(config.encode())
                temp_file_path = temp_file.name

            # Run ssmtp to send mail
            with subprocess.Popen(
                ["/usr/sbin/ssmtp", "-C%s" % temp_file_path, destination],
                stdin=subprocess.PIPE,
                stdout=subprocess.PIPE,
            ) as proc:
                input_data = (
                    "From: %s\n"
                    'Content-Type: text/plain; charset="%s"\n'
                    "To: %s\n"
                    "Subject: %s\n"
                    "\n"
                    "%s"
                ) % (
                    sender,
                    charset,
                    destination,
                    msg.generate_email_subject(),
                    msg.generate_email_body(),
                )
                proc.communicate(input=input_data.encode(charset))
        finally:
            os.remove(temp_file_path)
    finally:
        session.xenapi.session.logout()


if __name__ == "__main__":
    rc = 1
    try:
        rc = main()
    except:
        ex = sys.exc_info()
        err = traceback.format_exception(*ex)
        for exline in err:
            log_err(exline)

    sys.exit(rc)
