From ccf246e0386f7b54bb0366eef2ca88afc7f41cab Mon Sep 17 00:00:00 2001
From: David Trattnig <david.trattnig@o94.at>
Date: Fri, 30 Oct 2020 21:20:47 +0100
Subject: [PATCH] Mailer as event handler. #38

---
 src/core/events.py                      | 11 ++--
 src/{base/mail.py => plugins/mailer.py} | 86 ++++++++++++++++++++-----
 src/plugins/monitor.py                  | 12 +---
 src/scheduling/fallback.py              |  3 -
 4 files changed, 77 insertions(+), 35 deletions(-)
 rename src/{base/mail.py => plugins/mailer.py} (62%)

diff --git a/src/core/events.py b/src/core/events.py
index 4ab14c63..8c75fac7 100644
--- a/src/core/events.py
+++ b/src/core/events.py
@@ -24,7 +24,7 @@ from threading                  import Thread
 
 from src.base.config            import AuraConfig
 from src.base.utils             import SimpleUtil as SU
-from src.base.mail              import AuraMailer
+from src.plugins.mailer         import AuraMailer
 from src.plugins.monitor        import AuraMonitor
 from src.plugins.trackservice   import TrackServiceHandler
 
@@ -76,7 +76,6 @@ class EngineEventDispatcher():
     config = None
 
     subscriber_registry = None
-    mailer = None
     engine = None
     scheduler = None
     monitor = None
@@ -89,9 +88,13 @@ class EngineEventDispatcher():
         self.subscriber_registry = dict()
         self.logger = logging.getLogger("AuraEngine")
         self.config = AuraConfig.config()
-        self.mailer = AuraMailer(self.config)
         self.engine = engine
         
+        binding = self.attach(AuraMailer)
+        binding.subscribe("on_critical")
+        binding.subscribe("on_sick")
+        binding.subscribe("on_resurrect")
+
         binding = self.attach(AuraMonitor)
         binding.subscribe("on_boot")
         binding.subscribe("on_sick")
@@ -314,8 +317,6 @@ class EngineEventDispatcher():
         """
         def func(self, subject, message, data):        
             self.logger.debug("on_critical(..)")
-            if not data: data = ""
-            self.mailer.send_admin_mail(subject, message + "\n\n" + str(data))
             self.call_event("on_critical", (subject, message, data))
 
         thread = Thread(target = func, args = (self, subject, message, data))
diff --git a/src/base/mail.py b/src/plugins/mailer.py
similarity index 62%
rename from src/base/mail.py
rename to src/plugins/mailer.py
index e32c9090..fba65338 100644
--- a/src/base/mail.py
+++ b/src/plugins/mailer.py
@@ -18,8 +18,10 @@
 
 
 import smtplib
+
 from email.message import EmailMessage
 
+from src.base.config import AuraConfig
 
 
 class MailingException(Exception):
@@ -31,47 +33,97 @@ class MailingException(Exception):
 
 
 class AuraMailer():
+    """
+    Event handler to send emails to Aura administrators and programme coordinators.
+
+
+    """
+    engine = None
+    mail = None
+
+    def __init__(self, engine):
+        """
+        Constructor
+
+        Args:
+        engine (Engine):    The Engine
+        """
+        self.engine = engine
+        self.mail = MailService()
+
+
+    #
+    #   METHODS
+    #
+
+
+    def on_sick(self, data):
+        """
+        Called when the engine is in some unhealthy state.
+        """
+        subject = "ERROR - Engine turned into some INVALID STATE!"
+        message = "There's an issue with your AURA Engine '%s':\n\n%s" % (data.get("engine_id"), data.get("status"))
+        self.mail.send_admin(subject, message)
+            
+
+
+    def on_resurrect(self, data):
+        """
+        Called when the engine turned healthy again after being sick.
+        """
+        subject = "OK - Engine became healthy again"
+        message = "Good news, things seem fine again with your AURA Engine '%s':\n\n%s" % (data.get("engine_id"), data.get("status"))
+        self.mail.send_admin(subject, message)
+
+
+
+    def on_critical(self, subject, message, data=None):
+        """
+        Callend when some critical event occurs
+        """
+        if not data: data = ""
+        self.mail.send_admin(subject, message + "\n\n" + str(data))
+
+
+
+
+class MailService():
     """
     Service to send emails to Aura administrators.
     """
     config = None
+    admin_mails = None
 
 
-    def __init__(self, config):
+    def __init__(self):
         """
-        Constructor to initialize service with Aura `config`.
-
-        Args:
-            config (AuraConfig):    The configuration with the mail server details
+        Constructor
         """
-        self.config = config
-        self.admin_mails = config.get("admin_mail")
+        self.config = AuraConfig.config()
+        self.admin_mails = self.config.get("admin_mail")
 
 
     #
-    #   PUBLIC METHODS
+    #   METHODS
     #
 
-    def send_admin_mail(self, subject, body):
+
+    def send_admin(self, subject, body):
         """
         Sends an email to the administrator as defined in the configuration.
 
         Args:
-            subject (String):   The email's subject
-            body (String):      The email's body text
+            subject (String):   The email subject
+            body (String):      The email body text
         """
         admin_mails = self.admin_mails.split()
 
         for mail_to in admin_mails:
-            self.__send(mail_to, subject, body)
-
+            self.send(mail_to, subject, body)
 
-    #
-    #   PRIVATE METHODS
-    #
 
 
-    def __send(self, mail_to, subject, body):
+    def send(self, mail_to, subject, body):
         """
         Sends an email to the given address.
 
diff --git a/src/plugins/monitor.py b/src/plugins/monitor.py
index 86759993..5c5bf769 100644
--- a/src/plugins/monitor.py
+++ b/src/plugins/monitor.py
@@ -33,7 +33,6 @@ import meta
 
 from src.base.config    import AuraConfig
 from src.base.utils     import SimpleUtil as SU
-from src.base.mail      import AuraMailer
 
 
 
@@ -81,7 +80,6 @@ class AuraMonitor:
         self.logger = logging.getLogger("AuraEngine")
         self.config = AuraConfig.config()
         self.engine = engine
-        self.mailer = AuraMailer(self.config)
         self.status = dict()
         self.status["engine"] = dict()
         self.status["lqs"] = dict()
@@ -267,22 +265,16 @@ class AuraMonitor:
                 self.already_invalid = False
                 status = json.dumps(self.get_status())
                 self.logger.info(SU.green("OK - Engine turned back into some healthy state!")+"\n"+str(status))
-                self.mailer.send_admin_mail( \
-                    "OK - Engine turned back into some HEALTHY STATE!", \
-                    "Things seem fine again at '%s':\n\n%s" % (self.engine_id, status))
                 # Route call of event via event dispatcher to provide ability for additional hooks
-                self.engine.event_dispatcher.on_resurrect(status)                    
+                self.engine.event_dispatcher.on_resurrect({"engine_id": self.engine_id, "status": status})                    
         else:
             # Engine turned into invalid state
             if not self.already_invalid:
                 self.already_invalid = True
                 status = json.dumps(self.get_status())
                 self.logger.critical(SU.red("Engine turned into some INVALID STATE!")+"\n"+str(status))
-                self.mailer.send_admin_mail( \
-                    "ERROR - Engine turned into some INVALID STATE!", \
-                    "There's an issue with Aura Engine '%s':\n\n%s" % (self.engine_id, status))
                 # Route call of event via event dispatcher to provide ability for additional hooks
-                self.engine.event_dispatcher.on_sick(status)
+                self.engine.event_dispatcher.on_sick({"engine_id": self.engine_id, "status": status})
 
         threading.Timer(self.config.get("heartbeat_frequency"), self.heartbeat).start()
 
diff --git a/src/scheduling/fallback.py b/src/scheduling/fallback.py
index 3ad56091..a8375e78 100644
--- a/src/scheduling/fallback.py
+++ b/src/scheduling/fallback.py
@@ -26,7 +26,6 @@ from datetime               import timedelta
 
 from src.base.config        import AuraConfig
 from src.base.utils         import SimpleUtil as SU
-from src.base.mail          import AuraMailer
 from src.core.resources     import ResourceClass
 from src.core.channels      import Channel
 from src.core.control       import EngineExecutor
@@ -64,7 +63,6 @@ class FallbackManager:
     """    
     config = None
     logger = None
-    mailer = None
     scheduler = None
 
     
@@ -77,7 +75,6 @@ class FallbackManager:
         """
         self.config = AuraConfig.config()
         self.logger = logging.getLogger("AuraEngine")
-        self.mailer = AuraMailer(self.config)
         self.scheduler = scheduler
 
 
-- 
GitLab