Martin Sivák has uploaded a new change for review.

Change subject: [WIP] Add email notifications (only state transitions atm)
......................................................................

[WIP] Add email notifications (only state transitions atm)

Change-Id: Ibb466b8b130b75705f3cc9f817e377e79b3e81ba
Signed-off-by: Martin Sivak <msi...@redhat.com>
---
M ovirt_hosted_engine_ha/agent/hosted_engine.py
M ovirt_hosted_engine_ha/broker/listener.py
A ovirt_hosted_engine_ha/broker/notifications.py
M ovirt_hosted_engine_ha/env/Makefile.am
M ovirt_hosted_engine_ha/env/constants.py.in
A ovirt_hosted_engine_ha/env/notifications.conf
A ovirt_hosted_engine_ha/env/notifications/Makefile.am
A ovirt_hosted_engine_ha/env/notifications/state_transition.txt
M ovirt_hosted_engine_ha/lib/brokerlink.py
9 files changed, 157 insertions(+), 1 deletion(-)


  git pull ssh://gerrit.ovirt.org:29418/ovirt-hosted-engine-ha 
refs/changes/42/21042/1

diff --git a/ovirt_hosted_engine_ha/agent/hosted_engine.py 
b/ovirt_hosted_engine_ha/agent/hosted_engine.py
index f547315..8468477 100644
--- a/ovirt_hosted_engine_ha/agent/hosted_engine.py
+++ b/ovirt_hosted_engine_ha/agent/hosted_engine.py
@@ -779,11 +779,15 @@
         self._rinfo.update(rinfo)
 
         yield_ = False
+        old_state = "<initial_state>"
         # Process the states until it's time to sleep, indicated by the
         # state handler returning yield_ as True.
         while not yield_:
             self._log.debug("Processing engine state %s",
                             self._rinfo['current-state'])
+            self._broker.notify(brokerlink.NotifyEvents.STATE_TRANSITION,
+                                "%s-%s" % (old_state,
+                                           self._rinfo['current-state']))
             self._rinfo['current-state'], yield_ \
                 = self._vm_state_actions[self._rinfo['current-state']]()
 
diff --git a/ovirt_hosted_engine_ha/broker/listener.py 
b/ovirt_hosted_engine_ha/broker/listener.py
index 80e888e..727b04f 100644
--- a/ovirt_hosted_engine_ha/broker/listener.py
+++ b/ovirt_hosted_engine_ha/broker/listener.py
@@ -20,6 +20,7 @@
 import errno
 import logging
 import os
+import shlex
 import socket
 import SocketServer
 import threading
@@ -28,6 +29,7 @@
 from ..lib import util
 from ..lib.exceptions import DisconnectionError
 from ..lib.exceptions import RequestError
+import notifications
 
 
 class Listener(object):
@@ -201,7 +203,7 @@
         On success, output is returned as a string.
         On failure, a RequestError exception is raised (often by dispatchees).
         """
-        tokens = data.split(' ')
+        tokens = shlex.split(data)
         type = tokens.pop(0)
         self._log.debug("Request type %s", type)
 
@@ -241,6 +243,9 @@
                 self.server.sp_listener.storage_broker_instance \
                     .put_stats(**options)
             return "ok"
+        elif type == 'notify':
+            options = self._get_options(tokens)
+            notifications.notify(options)
         else:
             self._log.error("Unrecognized request: %s", data)
             raise RequestError("unrecognized request")
diff --git a/ovirt_hosted_engine_ha/broker/notifications.py 
b/ovirt_hosted_engine_ha/broker/notifications.py
new file mode 100644
index 0000000..d243a01
--- /dev/null
+++ b/ovirt_hosted_engine_ha/broker/notifications.py
@@ -0,0 +1,72 @@
+__author__ = 'msivak'
+
+import smtplib
+import ConfigParser
+import re
+import os
+import logging
+
+from ..env import constants
+
+
+def send_email(cfg, message):
+    """Send email."""
+
+    try:
+        server = smtplib.SMTP(cfg["smtp-server"], port=cfg["smtp-port"])
+        server.set_debuglevel(1)
+        server.sendmail(cfg["email-from"], cfg["email-to"], message)
+        server.quit()
+        return True
+    except smtplib.SMTPException as e:
+        logging.exception(e)
+        return False
+
+
+def notify(**kwargs):
+    """Try sending a notification to the configured addresses. If the
+    configuration does not contain a matching rule, do nothing.
+
+    The configuration is refreshed with every call of this method.
+    """
+
+    assert "type" in kwargs
+    type = kwargs["type"]
+
+    cfg = ConfigParser.SafeConfigParser()
+    cfg.read(constants.NOTIFY_CONF_FILE)
+
+    detail = kwargs.get("detail", "")
+
+    try:
+        rules = cfg.get("rules", type)
+        # only send emails for messages we want
+        if not re.search(rules, detail):
+            return False
+    except (ConfigParser.NoOptionError, ConfigParser.NoSectionError):
+        return False
+
+    template_path = os.path.join(constants.NOTIFY_TEMPLATES, type+".txt")
+    template = open(template_path).read()
+
+    # default SMTP configuration
+    smtp_config = {
+        "email-from": "root@localhost",
+        "email-to": "root@localhost",
+        "smtp-server": "localhost",
+        "smtp-port": 25
+    }
+
+    # read SMTP configuration from the notification config file
+    try:
+        smtp_config.update(cfg.items("email"))
+    except (ConfigParser.NoOptionError, ConfigParser.NoSectionError):
+        pass
+
+    # pass SMTP configuration to the formatting dictionary
+    # so we can use email addresses in the templates
+    for k, v in smtp_config.iteritems():
+        kwargs.setdefault(k, v)
+
+    email_body = template.format(**kwargs)
+    return send_email(smtp_config, email_body)
diff --git a/ovirt_hosted_engine_ha/env/Makefile.am 
b/ovirt_hosted_engine_ha/env/Makefile.am
index c7c4e4d..6f93e99 100644
--- a/ovirt_hosted_engine_ha/env/Makefile.am
+++ b/ovirt_hosted_engine_ha/env/Makefile.am
@@ -27,6 +27,10 @@
        constants.py \
        $(NULL)
 
+SUBDIRS = \
+       notifications \
+       $(NULL)
+
 haenvdir = $(engine_ha_libdir)/env
 
 dist_haenv_PYTHON = \
@@ -45,6 +49,12 @@
        ha.conf \
        $(NULL)
 
+hanotifyconfdir = $(engine_ha_confdir)
+
+dist_hanotifyconf_DATA = \
+       notifications.conf \
+       $(NULL)
+
 EXTRA_DIST = \
        constants.py.in \
        $(NULL)
diff --git a/ovirt_hosted_engine_ha/env/constants.py.in 
b/ovirt_hosted_engine_ha/env/constants.py.in
index 73a2ba9..7cddeed 100644
--- a/ovirt_hosted_engine_ha/env/constants.py.in
+++ b/ovirt_hosted_engine_ha/env/constants.py.in
@@ -36,6 +36,8 @@
 ENGINE_SETUP_CONF_FILE = '/etc/ovirt-hosted-engine/hosted-engine.conf'
 VM_CONF_FILE = '/etc/ovirt-hosted-engine/vm.conf'
 HA_AGENT_CONF_FILE = '@ENGINE_HA_STATEDIR@/ha.conf'
+NOTIFY_CONF_FILE = '/etc/ovirt-hosted-engine/notifications.conf'
+NOTIFY_TEMPLATES = '/etc/ovirt-hosted-engine/notifications.d'
 
 SD_MOUNT_PARENT = '/rhev/data-center/mnt'
 SD_METADATA_DIR = 'ha_agent'
diff --git a/ovirt_hosted_engine_ha/env/notifications.conf 
b/ovirt_hosted_engine_ha/env/notifications.conf
new file mode 100644
index 0000000..1ffb49f
--- /dev/null
+++ b/ovirt_hosted_engine_ha/env/notifications.conf
@@ -0,0 +1,7 @@
+[email]
+smtp_server=localhost
+smtp_port=25
+destination_email=root@localhost
+
+[notify]
+state_transition=maintenance|start|stop|migrate
diff --git a/ovirt_hosted_engine_ha/env/notifications/Makefile.am 
b/ovirt_hosted_engine_ha/env/notifications/Makefile.am
new file mode 100644
index 0000000..ec2bcd5
--- /dev/null
+++ b/ovirt_hosted_engine_ha/env/notifications/Makefile.am
@@ -0,0 +1,31 @@
+#
+# ovirt-hosted-engine-ha -- ovirt hosted engine high availability
+# Copyright (C) 2012-2013 Red Hat, Inc.
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+
+include $(top_srcdir)/build/python.inc
+include $(top_srcdir)/build/var_subst.inc
+
+MAINTAINERCLEANFILES = \
+       $(srcdir)/Makefile.in \
+       $(NULL)
+
+hanotifydir = $(engine_ha_confdir)/notifications
+
+dist_hanotify_DATA = \
+       state_transition.txt \
+       $(NULL)
diff --git a/ovirt_hosted_engine_ha/env/notifications/state_transition.txt 
b/ovirt_hosted_engine_ha/env/notifications/state_transition.txt
new file mode 100644
index 0000000..9ce6bdf
--- /dev/null
+++ b/ovirt_hosted_engine_ha/env/notifications/state_transition.txt
@@ -0,0 +1,5 @@
+From: {email-from}
+To: {email-to}
+Subject: ovirt-hosted-engine state transition {detail}
+
+The state machine changed state.
\ No newline at end of file
diff --git a/ovirt_hosted_engine_ha/lib/brokerlink.py 
b/ovirt_hosted_engine_ha/lib/brokerlink.py
index f53371b..f3dc9ae 100644
--- a/ovirt_hosted_engine_ha/lib/brokerlink.py
+++ b/ovirt_hosted_engine_ha/lib/brokerlink.py
@@ -29,6 +29,10 @@
 from ..lib import util
 
 
+class NotifyEvents(object):
+    STATE_TRANSITION = "state_transition"
+
+
 class BrokerLink(object):
     def __init__(self):
         self._log = logging.getLogger("BrokerLink")
@@ -94,6 +98,22 @@
         if not was_connected:
             self.disconnect()
 
+    def notify(self, event_type, detail, **options):
+        request = ["notify time={0} type={1} detail={2}"
+                   .format(time.time(), event_type, detail, "description")]
+        for k, v in options:
+            request.append("{0}={1}".format(k, repr(v)))
+        request = " ".join(request)
+
+        try:
+            response = self._checked_communicate(request)
+        except Exception as e:
+            raise RequestError("Failed to start monitor {0}, options {1}: {2}"
+                               .format(type, options, e))
+
+        self._log.info("Success, notification sent? %s", response)
+        return response
+
     def start_monitor(self, type, options):
         """
         Starts a monitor of the specified type in the ha broker using the


-- 
To view, visit http://gerrit.ovirt.org/21042
To unsubscribe, visit http://gerrit.ovirt.org/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: Ibb466b8b130b75705f3cc9f817e377e79b3e81ba
Gerrit-PatchSet: 1
Gerrit-Project: ovirt-hosted-engine-ha
Gerrit-Branch: master
Gerrit-Owner: Martin Sivák <msi...@redhat.com>
_______________________________________________
Engine-patches mailing list
Engine-patches@ovirt.org
http://lists.ovirt.org/mailman/listinfo/engine-patches

Reply via email to