Check Vlan404 Occurrences

Summary

Info

Network information were modified to protect the privacy of the organization

Since there were issues with flapping Vlan in my workplace's DMVPN topology, I wrote a script that gets run at 7:45am through crontab. The script checks for occurrences of Vlan404 by regular expression in the log through a list of known sites that has Vlan404. After the script parses through the list of sites, then it would email the team whether or not it found any occurrences of Vlan404 mentioned in the log.

Since the no autostate command was implemented in all sites, then theoretically there should not be any site that would have the Vlan404 flapping in its' log. However, new sites come up daily with terminating DMVPN Tunnel and the Vlan404 network that lives in that topology so this script keeps the site in monitoring and in check for possible deviation from the standard.

Crontab Schedule

Email Results

Python Source Code

#!/usr/bin/python3.7

from netmiko import ConnectHandler
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
import netmiko
import paramiko
import re
from time import gmtime, strftime
from datetime import date

from datetime import datetime, timezone
from random import randrange
'''
Function: send_Email(sender, receiver, message)

This function sends an email notification with the provided message.

Args:
    sender (str): Email address of the sender.
    receiver (list): List of email addresses of the recipients.
    message (str): The message content to be sent in the email.
'''
def send_Email(sender, receiver, message):

    msg = MIMEMultipart('alternative')
    msg['Subject'] = "VLAN404 Flapping Check"
    msg['From']    = "Server@localhost"
    msg['To']      = "receivers@domain.com"
        
    htmlmsg = MIMEText(message, 'html')

    msg.attach(htmlmsg)

    try:
        smtpObj = smtplib.SMTP("emailrelayserver.domain.com", 25)
        smtpObj.sendmail(sender, receivers, msg.as_string())
        
    except smtplib.SMTPException:
        print("Unable to send email")


# Get the current date and time
today = datetime.now()

# Extract the month abbreviation and day number from the current date
month_abbr = today.strftime("%b")

# Build the command to search for "Vlan404" in logs
command = "show log | i " + month_abbr + "  " + str(today.day)

# Initialize variables to store message and flag
myMsg = ""
found = False

# Create a regular expression object to match "Vlan404"
regex = re.compile(r"\bVlan404\b")

# Define sender and receiver email addresses
sender = "Server@localhost"
receivers = ["receivers@domain.com"]

# List to store unreachable routers during the script execution
nonconnected_routers = []

'''
This code block opens a file containing a list of possible VLAN404 sites (router hostnames or IP addresses).
It iterates over each line in the file and attempts to connect to the corresponding router.
'''
with open("List of Possible Vlan404 Sites.txt", "r") as f:

  # Iterate over the lines of the file.
  for line in f:
    router = {
    'device_type': 'cisco_ios',
    'host':   str(line),
    'username': 'cisco',
    'password': 'cisco123',
    }

    try:
        router_connected = ConnectHandler(**router)
        output = router_connected.send_command(command)
        prompt = router_connected.find_prompt()

        hostname = prompt.split()[-1]

        # Find all occurrences of "Vlan404" in the output using the regular expression
        matches = regex.findall(output)

         # If occurrences are found, update the message and set the found flag
        if len(matches) > 0:
            myMsg = myMsg + str(hostname) + " has " + str(len(matches)) + " of occurrences of Vlan404 " + "<br/>"
            found = True

    except paramiko.ssh_exception.AuthenticationException as e:
        # Handle the exception.
        print('Error connecting to device: {}'.format(e))
        nonconnected_routers.append(str(line))
        continue
    except paramiko.ssh_exception.NoValidConnectionsError as e2:
        print('Error connecting to device: {}'.format(e2))
        nonconnected_routers.append(str(line))
        continue
    except netmiko.NetmikoAuthenticationException:
        # Print a message and skip the device                        
        print("Authentication failed for device - %s" %(str(line)))  
        nonconnected_routers.append(str(line))
        continue
    except netmiko.NetmikoTimeoutException:
        # Print a message and skip the device
        print("Connection timed out for device - %s " %(str(line))) 
        nonconnected_routers.append(str(line))
        continue

    finally:
        # Close the connection to the device.
        router_connected.disconnect()
        pass

if found is False: 
    myMsg = "No occurrences happened on " + str(month_abbr) + ", " + str(today.day) + " " + str(today.year) 

# Create a string listing all the routers that cannot be connected
if len(nonconnected_routers) > 0:
    myMsg = myMsg + "<br/> <br/>"
    for ip in nonconnected_routers: 
        if len(ip) > 0: 
            myMsg = myMsg + "Unable to connected to " + ip + " <br/>"

send_Email(sender, receivers, myMsg)