[PYTHON] A script that pings the registered server and sends an email with Gmail a certain number of times when it fails

Ping to multiple recipients, and if 90% is NG, determine an error and send an email (Gmail). If ping is OK for the specified number of times in a row, recovery is judged.

The first source is multithreaded, but I don't really understand why it works, so I'm not sure why it works. It's also the wrong source as it definitely has bugs.

The program that should have no bugs is described in the second half. It is not multithreaded, but multiprocessed in the second half. 2016/12/29 Fixed: There was a bug, so I fixed it. Please refer to the comments in the source for details of the latter half of the program.

Then it is the explanation of the wrong source. For the time being, it is import.

import pandas as pd
import smtplib
import os, platform
import threading

Next is the Gmail sending function. When sending Gmail, you need to enable POP access in your account to allow access to insecure apps as well. If you search, you will find a kind commentary with a GUI screen attached, so I will leave the details to you.

def sendGmail(text):
    user = '****@gmail.com'  # username
    password = '******'  # password
    to = ['****@gmail.com'] # your favorite mail address
    sub = 'mail title'
    msg = '''From: %s\nTo: %s\nSubject: %s\n\n%s
    ''' % (user, ','.join(to), sub, text)

    server = smtplib.SMTP_SSL("smtp.gmail.com", 465)
    server.ehlo()
    server.login(user, password)
    server.sendmail(user, to, msg)
    server.close()

Next is the ping function. log is a DataFrame. It doesn't have to be a data frame, but I'm used to it because I'm used to it. There are a lot of arguments to save the ping result, and this is the part I think is dirty.

def ping(address, current_position, log):
    ping_str = '-n 1' if platform.system().lower() == 'windows' else '-c 1'
    response = os.system('ping ' + ping_str + ' ' + address)

    if response == 1:
        log[address][current_position] = 1

Next is the main function. Sequentially, the result of ping is written in the data frame called log, and each time, when 700 points are traced back from the latest log, if there are 600 or more ping errors, the mail is skipped.

if __name__ == '__main__':

    domain_list = ['***address***'] # your favorite address

    log = pd.DataFrame(0, index=range(60 * 60), columns=domain_list)
    #For the time being, create a large DataFrame.
    current_position = 0
    sum_of_seconds = 0

    sendGmail('tool has started.')

    while True:
        text = []
        for address in domain_list:
            p = threading.Thread(target=ping, args=(address, current_position, log))
            p.start()

            sum_error = 0

            for i in range(700):#I feel this is inefficient
                 sum_error += log.ix[current_position - i if current_position >= i else 60 * 15 - (i - current_position), address]

            if sum_error > 600:
                text.append(address)

        if (sum_of_seconds % 600 == 0) and (text != []):
            sendGmail(text)

        current_position += 1
        if current_position >= 60 * 15:
            current_position = 0

        sum_of_seconds += 1
        time.sleep(1)

The day after I wrote the above source, I thought that multiprocessing would be better, so I rewrote it. I found the dict type useful for the first time.

First, the Gmail sending part is the same. Next, regarding Ping transmission, it has been revised as follows.

import multiprocessing as mp

def ping(address, result):
 ping_str = '-n 1' if platform.system().lower() == 'windows' else '-c 1'
 response = subprocess.call('ping '+ ping_str + ' '+address)

 if response = 1:
  result[address] = 1
 else:
  result[address] = 0

result is of type dict. Later, we will do multiprocessing. As for ping, subprocess.call seems to be better than os.system, so I am rewriting it without understanding.

Next, the main function is as follows.

if __name__ == '__main__':
    manager = mp.Manager()
 
    current_position = 0
    bottom = 60*60
    evaluate_range = 5 # x5
    recover_range = 2 #10
    evaluate_ratio = 0.9
    before_result = pd.Series()
    act_num = 0
 
    domain_list = ['***', '****']
 
    log = pd.DataFrame(0, index=range(bottom), columns=domain_list)
    #It defines a DataFrame that saves the result after pinging.
 
    while True:
        proc = [] #For multiprocessing
        result = manager.dict() #Define a dict to share between processes
        for address in domain_list:
            p1 = mp.Process(target=ping, args=(address, result,))
            proc.append(p1)
 
        for p1 in proc:
            p1.start()
 
        for p1 in proc:
            p1.join()
 
        #Copy the ping result from the dict type result to the log of the DataFrame.
        for address in domain_list:
            log.ix[current_position, address] = result[address]

       #Since the log will continue to be used by spinning around, the current position that contains the latest data
       #Depending on the value, dataframe.sum()The range to be aggregated is changed in.
        value = log.ix[current_position-evaluate_range:current_position, :].sum() if current_position >= evaluate_range else \
            log.ix[0:current_position, :].sum() + log.ix[bottom-(evaluate_range-current_position):bottom,:].sum()

        #Similar to the above value, recover with a slightly shorter check range in order to reflect the judgment at the time of ping OK as soon as possible._Calculate value
       recover_value = log.ix[current_position-recover_range:current_position, :].sum() if current_position >= recover_range else \
            log.ix[0:current_position, :].sum() + log.ix[bottom-(recover_range-current_position):bottom,:].sum()
        result = value[(value > evaluate_range*evaluate_ratio) & (recover_value != 0)]

        #I try to call sendGmail only when the result changes so that the notification email does not fly too much.
        #Since the design does not look at the details of the number of ping NGs, only the index is extracted and compared.
        #.difference()Does not diff check in both directions, so I connect one-way check with or.
        if not pd.Series().index.equals(result.index.difference(before_result.index)) or not pd.Series().index.equals(before_result.index.difference(result.index)):
            if result.size != 0:
                sendGmail(result.index.str.cat(sep='\n'))
                before_result = result
            else:
                sendGmail('All server are normally operating.')
                before_result = result
 
        current_position += 1
        if current_position > bottom:
            current_position = 0
        time.sleep(1)

I use pandas because I like pandas, which I'm used to, for log aggregation. For multiprocessing, we needed some sharable data, so we do result = manager.dict () and pass it to the ping function.

Recommended Posts

A script that pings the registered server and sends an email with Gmail a certain number of times when it fails
A server that returns the number of people in front of the camera with bottle.py and OpenCV
[Shell art] Only when it is a multiple of 3 and a number with 3 becomes stupid
A simple mock server that simply embeds the HTTP request header in the body of the response and returns it.
[Python] Precautions when finding the maximum and minimum values in a numpy array with a small number of elements
Try to find the probability that it is a multiple of 3 and not a multiple of 5 when one is removed from a card with natural numbers 1 to 100 using Ruby and Python.
I made a system with Raspberry Pi that regularly measures the discomfort index of the room and sends a LINE notification if it is a dangerous value
I managed to do it because the custom of attaching a zip with a password to an email and saying "I will send you the password separately" is troublesome.
A python script that gets the number of jobs for a specified condition from indeed.com
A script that can perform stress tests according to the number of CPU cores
A simple system that automatically shoots with object detection and sends it to LINE
The procedure from generating and saving a learning model by machine learning, making it an API server, and communicating with JSON from a browser
I don't like to be frustrated with the release of Pokemon Go, so I made a script to detect the release and tweet it
PGM that takes the difference of the specified rectangular area from the camera and makes a sound when the rate of change exceeds a certain rate
Two solutions to the problem that it is hard to see the vector field when writing a vector field with quiver () of matplotlib pyplot
When accessing a URL containing Japanese (Japanese URL) with python3, it will be encoded in html without permission and an error will occur, so make a note of the workaround.
[Python] A program that counts the number of valleys
A script that takes a snapshot of an EBS volume
Script that changes the length of the sound with REAPER
Get the matched string with a regular expression and reuse it when replacing on Python3
About the contents of wscript when building a D language environment like that with Waf
[Python] Programming to find the number of a in a character string that repeats a specified number of times.
A Python script that allows you to check the status of the server from your browser
[Python] A program to find the number of apples and oranges that can be harvested