From 39596493afa217727b680308fd4fe2f0111eeedd Mon Sep 17 00:00:00 2001
From: David Trattnig <david.trattnig@o94.at>
Date: Fri, 8 May 2020 09:04:38 +0200
Subject: [PATCH] Mail notifications, healthy state ressurection.

---
 modules/core/engine.py  |  4 +--
 modules/core/monitor.py | 58 +++++++++++++++++++++++++++++++++++++----
 2 files changed, 55 insertions(+), 7 deletions(-)

diff --git a/modules/core/engine.py b/modules/core/engine.py
index 58a88e22..3279bd22 100644
--- a/modules/core/engine.py
+++ b/modules/core/engine.py
@@ -78,9 +78,9 @@ class SoundSystem():
 
         self.client = LiquidSoapPlayerClient(config, "engine.sock")
         # self.lqcr = LiquidSoapRecorderClient(config, "record.sock")
-        self.monitoring = Monitoring(config, self)
-
         self.auramailer = AuraMailer(self.config)
+        self.monitoring = Monitoring(config, self, self.auramailer)
+        
         self.is_active()
 
         # Initialize Default Channels
diff --git a/modules/core/monitor.py b/modules/core/monitor.py
index 5a270cc5..f4bbce0d 100644
--- a/modules/core/monitor.py
+++ b/modules/core/monitor.py
@@ -18,21 +18,24 @@
 
 
 
+import meta
+
 import sys
 import urllib
 import logging
 import json
 import os.path
 import threading
+import platform
 
 from os import path
 from enum import Enum
-from socket import socket, AF_INET, SOCK_DGRAM
 from time import time, ctime, sleep
+from socket import socket, AF_INET, SOCK_DGRAM, SOL_SOCKET, SO_BROADCAST 
 
-import meta
-from modules.communication.redis.adapter import ClientRedisAdapter
 
+from modules.communication.redis.adapter import ClientRedisAdapter
+from modules.base.utils import SimpleUtil
 
 
 class MonitorResponseCode(Enum):
@@ -52,7 +55,10 @@ class Monitoring:
     """
     logger = None
     soundsystem = None
+    mailer = None
     status = None
+    already_invalid = None
+    engine_id = None
 
     heartbeat_server = None
     heartbeat_port = None
@@ -61,13 +67,14 @@ class Monitoring:
     heartbeat_running = None
 
 
-    def __init__(self, config, soundsystem):
+    def __init__(self, config, soundsystem, mailer):
         """
         Initialize Monitoring
         """
         self.logger = logging.getLogger("AuraEngine")
         self.config = config
         self.soundsystem = soundsystem
+        self.mailer = mailer
         self.status = dict()
         self.status["engine"] = dict()
         self.status["soundsystem"] = dict()
@@ -76,6 +83,7 @@ class Monitoring:
         self.status["api"]["steering"] = dict()
         self.status["api"]["tank"] = dict()
         self.status["api"]["engine"] = dict()
+        self.already_invalid = False
 
         # Heartbeat settings
         self.heartbeat_running = False
@@ -84,7 +92,8 @@ class Monitoring:
         self.heartbeat_frequency = config.get("heartbeat_frequency")
         self.heartbeat_socket = socket(AF_INET, SOCK_DGRAM)
 
-
+        self.engine_id = self.get_engine_id()
+        
 
     #
     # PUBLIC METHODS
@@ -194,6 +203,24 @@ class Monitoring:
         """
         if self.has_valid_status(True):
             self.heartbeat_socket.sendto(str.encode("OK"), (self.heartbeat_server, self.heartbeat_port))
+            
+            # Engine resurrected into normal state
+            if self.already_invalid:
+                self.already_invalid = False
+                status = json.dumps(self.get_status())
+                self.logger.info(SimpleUtil.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))
+        else:
+            # Engine turned into invalid state
+            if not self.already_invalid:
+                self.already_invalid = True
+                status = json.dumps(self.get_status())
+                self.logger.critical(SimpleUtil.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))
 
         threading.Timer(self.config.get("heartbeat_frequency"), self.heartbeat).start()
 
@@ -280,3 +307,24 @@ class Monitoring:
             self.logger.error("Error while connecting to URL '%s' - %s" % (url, e))
 
         return MonitorResponseCode.INVALID_STATE.value
+
+
+
+    def get_engine_id(self):
+        """
+        Retrieves a String identifier consisting of IP and Hostname to differentiate
+        the engine in mails and status broadcasts.
+        """
+        host = platform.node()
+        return "%s (%s)" % (self.get_ip(), host)
+
+
+
+    def get_ip(self):
+        """
+        Returns the IP of the Engine instance.
+        """
+        s = socket(AF_INET, SOCK_DGRAM)
+        s.setsockopt(SOL_SOCKET, SO_BROADCAST, 1)
+        s.connect(('<broadcast>', 0))
+        return s.getsockname()[0]
-- 
GitLab