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