Check_MK mit SMS Notifikation über SMSEagle

Das Python Skript dient dem Versenden von SMS Meldungen von Check_MK mittels SMSEagle Geräten. Eine SMS wird nur dann versendet, wenn der Benutzer eine gültige Telefonnummer als Pager-Adresse eingetragen hat und sie auf die PHONE_REGEX passt. Vorsicht beim Kopieren von Python-Code, da bei dieser Skriptsprache die Logik aufgrund von Texteinzügen basiert, was anfällig ist. Auch ändern sich Syntax und Bibliotheksnamen mit verschiedenen Python-Versionen. Das war mein erstes Python-Skript um mich mit dieser Sprache vertraut zu machen, von der heutzutage jeder zu schwärmen scheint.

Mehr Details zu Check_MK und SMSEagle:

Skript

Aktuellste Version aus dem Github Repository: github.com/nies-ch/smseagle-check_mk

#!/usr/bin/env python3

import urllib.request
import urllib.parse
import smtplib
import re
import sys
import socket
from os import environ
from email.mime.text import MIMEText

### Configuration Parameters

EAGLE_HOSTS    = [ '1.2.3.4', '5.6.7.8', '9.10.11.12' ]
EAGLE_LOGIN    = 'eagleuser'
EAGLE_PASSWORD = 'eaglepassword'
EAGLE_TIMEOUT  = 10
PHONE_REGEX    = '^(\+|00)41\d+$'
MAIL_FROM      = 'check_mk@example.com'
MAIL_TO        = 'my-email@example.com'
MAIL_HOSTS     = [ 'localhost', 'smtp1.example.com', 'smtp2.example.com' ]
MAIL_PORT      = 25


def complain(error):
    """
    Complain about errors via email. If that fails, just print the error
    message to stdout.
    """
    print(error)
    msg = MIMEText(error)
    msg['To'] = MAIL_TO
    msg['From'] = MAIL_FROM
    msg['Subject'] = 'Check_MK: SMS Notification Error'
    message_sent  = False

    for host in MAIL_HOSTS:
        if not message_sent:
            try:
                server = smtplib.SMTP(host, MAIL_PORT)
                server.sendmail(MAIL_FROM, MAIL_TO, msg.as_string())
                server.quit()
            except socket.error as e:
                print("Could not connect to " + host + ": " + str(e))
            except smtplib.SMTPException as e:
                print("Sending email failed: " + str(e))
            except:
                print("Unknown error:", sys.exc_info()[0])
            else:
                message_sent = True
                break
    return


def format_message():
    """
    Format the SMS message using the environment variables given by Check_MK.
    See https://mathias-kettner.com/cms_notifications.html for details.
    """ 
    if 'NOTIFY_WHAT' in environ:
        if environ['NOTIFY_WHAT'] == 'HOST':
            msg = "%s %s: %s (%s)" % (
                  environ['NOTIFY_NOTIFICATIONTYPE'], 
                  environ['NOTIFY_HOSTNAME'],
                  environ['NOTIFY_HOSTOUTPUT'],
                  environ['NOTIFY_SHORTDATETIME'])
        elif environ['NOTIFY_WHAT'] == 'SERVICE':
            msg = "%s %s: %s %s (%s)" % (
                  environ['NOTIFY_NOTIFICATIONTYPE'],
                  environ['NOTIFY_HOSTNAME'],
                  environ['NOTIFY_SERVICEDESC'],
                  environ['NOTIFY_SERVICEOUTPUT'],
                  environ['NOTIFY_SHORTDATETIME'])
        else:
            msg = "Unknown notification method: " + environ['NOTIFY_WHAT']
    else:
        msg = "Environment variable NOTIFY_WHAT not defined."
    return msg


def send_eagle_sms(to,message):
    """
    Sending SMS via SMSEagle HTTP API. Uses hosts defined in list EAGLE_HOSTS.
    Upon failure an email with the error is sent and next host is used.
    """
    query_args   = { 'login':EAGLE_LOGIN, 'pass':EAGLE_PASSWORD, 'to':to,
                     'message':message }
    encoded_args = urllib.parse.urlencode(query_args)
    message_sent  = False

    for host in EAGLE_HOSTS:
        url =  'http://%s/index.php/http_api/send_sms?%s' % (host, encoded_args)
        if not message_sent:
            try:
                result = urllib.request.urlopen(url,None,EAGLE_TIMEOUT).read()
            except urllib.request.HTTPError as e:
                complain('Sending SMS via SMSEagle %s failed: %s' 
                          % (host, str(e.code))) 
            except urllib.request.URLError as e:
                complain('Sending SMS via SMSEagle %s failed: %s'
                          % (host, str(e.args)))
            else:    
                if result.startswith('OK;'):
                    message_sent = True
                    print('SMS successfully sent via %s to %s.' % (host, to))
                    break
                else:
                    message_sent = False
                    complain('Sending SMS via SMSEagle %s failed: %s' 
                             % (host, result.rstrip()))
            
    return


if not 'NOTIFY_CONTACTPAGER' in environ:
    complain('Environment variable NOTIFY_CONTACTPAGER missing')
else:
    phone_number = environ['NOTIFY_CONTACTPAGER']
    if re.match(PHONE_REGEX, phone_number):
        eagle_message = format_message()
        send_eagle_sms(phone_number, eagle_message)