Monitoring email content using Zabbix

Posted: January 1, 2019 in Linux

In this example we’ll use python script for extracting Job name from email body, moving parsed emails to “Processed” folder,create Zabbix item from that job and create LLD discovery:

Put this script under /usr/lib/zabbix/externalscripts folder

#!/usr/bin/python

import email, imaplib, re, sys, json, base64

#read previously encrypted password from file

with open('/opt/an_sys/output.txt', 'r') as myfile:
    data=myfile.read()

#connect to mailbox,switch to "SNAP" folder and serach for emails with "failed"
#in subject, sent from snapuploader@brevanhoward.com

user = 'monitoring@email.com'
pwd = base64.b64decode(data)

conn = imaplib.IMAP4_SSL("outlook.office365.com")
conn.login(user,pwd)
conn.select("ZABBIX")

#resp, items = conn.uid("search",None, 'All')
resp, items = conn.uid("search" ,None, '(FROM "email@domain.com")')

#f = open('output.txt','w')

#sys.stdout = f

tdata=[]

items = items[0].split()
for emailid in items:
    resp, data = conn.uid("fetch",emailid, "(RFC822)")
    if resp == 'OK':
        email_body = data[0][1].decode('utf-8')
        mail = email.message_from_string(email_body)
        if mail["Subject"].find("failed") > 0:
          #print mail["Subject"]
          regex1=r'Snap:\s*(.+?)\s+failed'
          a=re.findall(regex1 ,mail["Subject"], re.DOTALL)

          #regex2 = r'Job finished'
          #c=re.findall(regex2, email_body, re.IGNORECASE)

          #format string by removing "'","\r\n"," ","|",".","-","__" and "Processor_"

          if a:
           a=[item.replace("'","") for item in a]
           a=[item.replace("\r\n","") for item in a]
           a=[item.replace(" ","_") for item in a]
           a=[item.replace("|","_") for item in a]
           a=[item.replace(".","_") for item in a]
           a=[item.replace("-","") for item in a]
           a=[item.replace("__","_") for item in a]
           a=[item.replace("Processor_","") for item in a]
           seen = set()
           result = []

           for item in a:
		       #remove "_for_" and all after it
			   c = item.split("_for_")[0]
			   #remove digits
               c = ''.join([i for i in c if not i.isdigit()])
			   #limit strings to 36 characters (in order to create zabbix items)
               s = c[:36]
			   #if string ends with "_",remove it
			   s = re.sub("_$", "", s)
			   #replace "__" with empty space
               s = c.replace("__","")
               s = s[:36]
               s = re.sub("_$","",s)
               if s not in seen:
                seen.add(s)

                result.append(s)
                output = " ".join(result)
                output.join(result)

                #create LLD JSON output
                tdata.append({'{#JOB}':output,'{#NAME}':item})
print json.dumps({"data": tdata}, indent=4)

Discovery rule

If it takes some time for items to be created in Zabbix, try reducing update interval,if it doesn’t help try decreasing configuration cache.Configuration cache  contains information on hosts and items to be monitored. It re-creates this cache by default every 60 seconds. This period can be customised by configuration parameter CacheUpdateFrequency (/etc/zabbix/zabbix_server.conf), try setting value between 20-60 seconds, if using zabbix proxy edit ConfigFrequency (/etc/zabbix/zabbix_proxy.conf),restart zabbix service.

Capture.PNG

Item property

1.PNG

Items will be sent by zabbix trapper, if item still doesn’t exit on Zabbix server, email will be left in “Zabbix” folder as long as key for that job is created. This script will be run every 2 minutes

#!/usr/local/bin/python3
from subprocess import run, PIPE
import email
import imaplib
import re
import sys
import logging
import base64
import argparse
import os
logging.basicConfig(stream=sys.stdout, level=logging.INFO)

# function to send items to Zabbix server using trapper

def zabbix_sender(key, output):
    """
    Sends a message to the Zabbix monitoring server to update the given key
    with the given output. This is designed to be only called whenever
    the service encounters an error.
    Zabbix should be configured with an Zabbix Trapper
    item for any key passed in, and a trigger for any instance where the output
    value of the message has a string length greater than 0. Since this method
    should only be called when something goes wrong, the Zabbix setup for
    listening for this key should be "any news is bad news"
    @param key
    The item key to use when communicating with Zabbix. This should match a key
    configured on the Zabbix monitoring server.
    @param output
    The data to display in Zabbix notifying TechOps of a problem.
    """
    # When I actually did this at work, I had the server and hostname set in an
    # external configuration file. That's probably how you want to do this as
    # opposed to hard-coding it into the script.
    server = "192.168.10.19"
    hostname = "zabbix_host"
    cmd = ["zabbix_sender", "-z", server, "-s",  hostname,
        "-k", key, "-o",  output]
    result = run(cmd, stdout=PIPE, stderr=PIPE, universal_newlines=True, check=True)
    return result.stdout, result.stderr
# read previously encrypted password

# log in to mailbox 

parser = argparse.ArgumentParser()
parser.add_argument('-p', '-password', dest='pwd', help='The password for authentication.')
args = parser.parse_args()

user = 'monitoring@domain.com'
pwd = args.pwd

conn = imaplib.IMAP4_SSL("outlook.office365.com")
conn.login(user, pwd)

conn.select("ZABBIX")

# resp, items = conn.uid("search",None, 'All')
resp, items = conn.uid("search", None, '(FROM "email1@domain.com")')
items = items[0].split()
for emailid in items:
    res, data = conn.uid("fetch", emailid, "(RFC822)")
    if resp == 'OK':
        email_body = data[0][1].decode('utf-8')
        mail = email.message_from_string(email_body)
        # search for emails with "failed" word in subject
        if mail["Subject"].find("failed") > 0:
                 # and get job name from subject, that string will be used as Zabbix item
         regex1 = r'Snap:\s*(.+?)\s+failed'
         a=re.findall(regex1, mail["Subject"], re.DOTALL)
         # regex2 = r'Job finished'
         # c=re.findall(regex2, email_body, re.IGNORECASE)
         # format job name (remove "'","\r\n","|",".","-","__" and "Processor"
         if a:
           a = [item.replace("'", "") for item in a]
           a = [item.replace("\r\n", "") for item in a]
           a = [item.replace(" ", "_") for item in a]
           a = [item.replace("|", "_") for item in a]
           a = [item.replace(".", "_") for item in a]
           a = [item.replace("-", "") for item in a]
           a = [item.replace("__", "_") for item in a]
           a = [item.replace("Processor_", "") for item in a]
           seen = set()
           result = []

           for item in a:
          # remove all after "_for_" (including "_for_")
               c = item.split("_for_")[0]
              # remove digits from string
               c = ''.join([i for i in c if not i.isdigit()])
              # had to remove string lenghts to 36 (Zabbix item can't be too long)
               s = c[:36]
               s = re.sub("_$", "", s)
               s = c.replace("__", "")
               s = s[:36]
               # if string ends with "_", remove it,(zabbix item can't end with special characters)
               s = re.sub("_$", "", s)
               # put all strings in list
               if s not in seen:
                seen.add(s)
                result.append(s)
                out = " ".join(result)
                # create Zabbix key from strings (email subjects)
                key = "an.snap[" + out + ",an]"
                # send values to Zabbix with value "failed"
                try:
                  r = zabbix_sender (key, "failed")
                  k = "".join(r)
                  if k.find("failed: 0") > 0:

                 # copy all email from "zabbix" folder to "Proceesed" folder
                   result = conn.uid('COPY', emailid, "Processed")
                   if result[0] == 'OK':
                   # clear "SNAP" folder
                      result = mov, data = conn.uid('STORE', emailid, '+FLAGS', '(\Deleted Items)')
                      # conn.expunge()
                except:
                      continue
        else:
                 # if mail subjects don't contain word "failed" move it to "Ignored" folder
         result = conn.uid('COPY', emailid, "Ignored")
          # print result
         if result[0] == 'OK':
                    # clean up "zabbix folder" folder
            result = mov, data = conn.uid('STORE', emailid, '+FLAGS', '(\Deleted Items)')
           # conn.expunge()

#Disconnect from mailbox
conn.close()
conn.logout()

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s