Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions PortFlap_email_alert/EEM_configuration.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Catalyst_Switch#sh run | sec event manager
event manager applet config_change
event syslog pattern "GigabitEthernet1/0/5, changed state to down"
action 0 cli command "enable"
action 1 cli command "guestshell run python port_flap_email_alert.py"
21 changes: 21 additions & 0 deletions PortFlap_email_alert/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# port_flap_email_alert script will send an email alert if an interface is flapping

If an interface has flapped 5 times in last 5 minutes this script will shutdown that interface. You can change the flapping count to whatever you want. Then the script will wait for 5 mins and enable the shutdown interface. If that interface still flapping at least once script will shutdown the interface and will send an email alert.

## Setup

This script requires the IOS-XE guestshell feature. To enable guestshell, configure

```
iox
```

Then type the following in EXEC mode:

```
guestshell enable
```

Save the script port_flap_email_alert.py in guestshell.

Next, configure the EEM environment using EEM_configuration file. Embedded Event Manager (EEM) is a distributed and customized approach to event detection and recovery. EEM offers the ability to monitor events and take informational, corrective, or any desired EEM action when the monitored events occur or when a threshold is reached.
192 changes: 192 additions & 0 deletions PortFlap_email_alert/port_flap_email_alert.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
#
# Copyright (c) 2018 Krishna Kotha <krkotha@cisco.com>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
#
# PURPOSE of this SCRIPT
# The purpose of this script is if a interface flapped 5 times in last 5 minutes it will shutdown that interface. You can change that flapping count to what ever you want.
# Then it will wait for 5 mins and enable that interface.
# If still flapping atleast once it will shutdown that interface and will send an email alert.
#
#
# This script monitors the interface GigabitEthernet1/0/5, change the interface ID based on your requirement.
#
#
# This script requires the following variables to be defined:
# FROM_ADDR
# TO_ADDR
# name-server ip address
# http_proxy and https_proxy commands
#

# importing necessary modules
import os
import sys
import cli
import time
import difflib
from datetime import datetime
from time import strptime
from time import mktime
import smtplib
import shutil
from email.MIMEMultipart import MIMEMultipart
from email.MIMEText import MIMEText
import subprocess


def checkPortFlap(port_flaps_cnt,interface):

# calculate current time using time module
currTime = time.time()
print("curr time is: %s" % (currTime))

# retrieve the show log output
arr_output = cli.cli("show log").split("\n")

for line in arr_output:

# In this example I am monitoring the Gi 1/0/5 interface, based on your requirement change the interface ID.
if(line.find("GigabitEthernet1/0/5, changed state to down") != -1):
print("The line %s indicates a port flap action" % (line))
line_arr = line.split("%")

# seperate the date from the log lines and convert the matching line to list objects and strings
date_in_log = line_arr[0]
print("the entry date is: %s" % (date_in_log))
line_arr = line.split(",")
line_arr_1 = line_arr[0].split(" ")
str_line_arr_0=''.join(line_arr_1[0])

# remove * and : from the strings
str_line_arr_0= str_line_arr_0[1:]
str_line_arr_1=''.join(line_arr_1[3])
str_line_arr_1=str_line_arr_1[:-1]

# parse a string to represent a time according to a format
log_time = time.strptime(str_line_arr_0+" "+line_arr_1[1]+" "+line_arr_1[2]+" "+str_line_arr_1, "%b %d %Y %H:%M:%S")

# calculate the log time in seconds
log_time_secs = mktime(log_time)

# calculate the difference from current time to log time
delta=currTime-log_time_secs
interface = line_arr_1[len(line_arr_1) - 1]
print("the interface is: %s" % (interface))

# if delta is less than 5 minutes that log will be counted.
if delta <= 300:
port_flaps_cnt = port_flaps_cnt + 1
return port_flaps_cnt, interface



def send_e_mail(subject):
"""
send an e mail from the Cisco network to anyone -
Note: This example uses from address as a Cisco server, Change the domain to your SMTP server to send email from your domain.
"""

# retrieve the hostname using in-built cli module
host_name = cli.cli("show running-config | include hostname")
FROM_ADDR = 'xxxx@cisco.com'
TO_ADDR = 'xxxx@gmail.com'

# create the message
msg = MIMEMultipart()
msg['From'] = FROM_ADDR
msg['To'] = TO_ADDR
msg['Subject'] = "%s - %s" % (subject, host_name)
text = "This is an automated e mail message:\n===========================\nAn on-Box Python script running on a Cisco Polaris guestshell device and detected that the %s %s \n===========================\n\n\n===========================\n\n" % (subject, host_name)
msg.attach(MIMEText(text, 'plain'))

# connect to server and send
server = smtplib.SMTP('outbound.your_company_name.com', 25)
server.sendmail(FROM_ADDR, TO_ADDR, msg.as_string())
server.quit()


def set_device_to_role_as_cisco_mail_server():
"""
Manipulates the /etc/resolv.conf and set it to a Cisco mail server. The commands are known to the average network Linux admin...
"""
commands_to_run_on_gs = [
"sudo rm /etc/resolv.conf",
"sudo touch /etc/resolv.conf",
"echo \"nameserver x.x.x.x\" | sudo tee -a /etc/resolv.conf",
"echo \"domain x.x.x.x.com\" | sudo tee -a /etc/resolv.conf",
"echo \"export http_proxy=http://x.x.x.x:80/\" | sudo tee -a /etc/bashrc",
"echo \"export https_proxy=http://x.x.x.x:80/\" | sudo tee -a /etc/bashrc",
"echo \"export ftp_proxy=http://x.x.x.x:80/\" | sudo tee -a /etc/bashrc",
"echo \"export no_proxy=.x.x.x.x.com\" | sudo tee -a /etc/bashrc",
"echo \"export HTTP_PROXY=http://x.x.x.x:80/\" | sudo tee -a /etc/bashrc",
"echo \"export HTTPS_PROXY=http://x.x.x.x:80/\" | sudo tee -a /etc/bashrc",
"echo \"export FTP_PROXY=http://x.x.x.x:80/\" | sudo tee -a /etc/bashrc"
]
for command in commands_to_run_on_gs:
p = subprocess.Popen(command, stdout=subprocess.PIPE, shell=True)
(output, err) = p.communicate()
print "Linux command output is", output


# main code:
if __name__ == '__main__':

# these commands are need to remove msec and add year in the show log command
cli.configurep("service timestamps log datetime")
cli.configurep("service timestamps log datetime year")


port_flaps_cnt = 0
interface = None
port_flaps = checkPortFlap(port_flaps_cnt,interface)

if(port_flaps[0] == 10):
if(port_flaps[1] is not None):
print("shutting down interface %s" % (port_flaps[1]))

# shut down the interface using cli module
#cli(["interface %s" % (port_flaps[1]), "shut", "end"])
cli.configurep(["interface %s" % (port_flaps[1]), "shut", "end"])

# wait for 5 mins to monitor the interface flap again
time.sleep(300)
print("Waited for 5 mins Enabling the interface %s" % (port_flaps[1]))

# enable the interface
#cli("cont f", "interfact %s" % (port_flaps[1]), "no shut", "end")
cli.configurep(["interface %s" % (port_flaps[1]), "no shut", "end"])
port_flaps_cnt = 0
interface = None

# re check the interface is flapping or not
port_flaps = checkPortFlap(port_flaps_cnt,interface)

# if still flapping shut down the interface and send email
if(port_flaps[0] >= 2):
if(port_flaps[1] is not None):
print("shutting down interface %s" % (port_flaps[1]))
#cli("cont f", "interfact %s" % (port_flaps[1]), "shut", "end")
cli.configurep(["interface %s" % (port_flaps[1]), "shut", "end"])
set_device_to_role_as_cisco_mail_server()
send_e_mail("interface %s is flapping and did admin shutdown" % (port_flaps[1]))
51 changes: 51 additions & 0 deletions PortFlap_email_alert/sample_log_file.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
Catalyst_Switch#sh log
Syslog logging: enabled (0 messages dropped, 11 messages rate-limited, 0 flushes, 0 overruns, xml disabled, filtering disabled)

No Active Message Discriminator.



No Inactive Message Discriminator.


Console logging: level debugging, 169945 messages logged, xml disabled,
filtering disabled
Monitor logging: level debugging, 39 messages logged, xml disabled,
filtering disabled
Buffer logging: level debugging, 169955 messages logged, xml disabled,
filtering disabled
Exception Logging: size (4096 bytes)
Count and timestamp logging messages: disabled
File logging: disabled
Persistent logging: disabled

No active filter modules.

Trap logging: level informational, 169933 message lines logged
Logging Source-Interface: VRF Name:

Log Buffer (4096 bytes):

*Apr 19 2018 18:00:06: %LINEPROTO-5-UPDOWN: Line protocol on Interface GigabitEthernet1/0/5, changed state to down
*Apr 19 2018 18:00:07: %LINK-3-UPDOWN: Interface GigabitEthernet1/0/5, changed state to down
*Apr 19 2018 18:00:14: %LINK-3-UPDOWN: Interface GigabitEthernet1/0/5, changed state to up
*Apr 19 2018 18:00:15: %LINEPROTO-5-UPDOWN: Line protocol on Interface GigabitEthernet1/0/5, changed state to up
*Apr 19 2018 18:00:19: %LINEPROTO-5-UPDOWN: Line protocol on Interface GigabitEthernet1/0/5, changed state to down
*Apr 19 2018 18:00:20: %LINK-3-UPDOWN: Interface GigabitEthernet1/0/5, changed state to down
*Apr 19 2018 18:00:29: %LINK-3-UPDOWN: Interface GigabitEthernet1/0/5, changed state to up
*Apr 19 2018 18:00:30: %LINEPROTO-5-UPDOWN: Line protocol on Interface GigabitEthernet1/0/5, changed state to up
*Apr 19 2018 18:00:53: %LINEPROTO-5-UPDOWN: Line protocol on Interface GigabitEthernet1/0/5, changed state to down
*Apr 19 2018 18:00:54: %LINK-3-UPDOWN: Interface GigabitEthernet1/0/5, changed state to down
*Apr 19 2018 18:01:01: %LINK-3-UPDOWN: Interface GigabitEthernet1/0/5, changed state to up
*Apr 19 2018 18:01:02: %LINEPROTO-5-UPDOWN: Line protocol on Interface GigabitEthernet1/0/5, changed state to up
*Apr 19 2018 18:01:07: %LINEPROTO-5-UPDOWN: Line protocol on Interface GigabitEthernet1/0/5, changed state to down
*Apr 19 2018 18:01:08: %LINK-3-UPDOWN: Interface GigabitEthernet1/0/5, changed state to down
*Apr 19 2018 18:01:27: %LINK-3-UPDOWN: Interface GigabitEthernet1/0/5, changed state to up
*Apr 19 2018 18:01:29: %LINK-3-UPDOWN: Interface GigabitEthernet1/0/5, changed state to down
*Apr 19 2018 18:01:50: %LINK-3-UPDOWN: Interface GigabitEthernet1/0/5, changed state to up
*Apr 19 2018 18:01:51: %LINEPROTO-5-UPDOWN: Line protocol on Interface GigabitEthernet1/0/5, changed state to up
*Apr 19 2018 18:02:07: %UTIL-6-RANDOM: A pseudo-random number was generated twice in succession
*Apr 19 2018 18:02:08: %LINEPROTO-5-UPDOWN: Line protocol on Interface GigabitEthernet1/0/5, changed state to down
*Apr 19 2018 18:02:09: %LINK-3-UPDOWN: Interface GigabitEthernet1/0/5, changed state to down
*Apr 19 2018 18:02:16: %LINK-5-CHANGED: Interface GigabitEthernet1/0/5, changed state to administratively down <<<<<<<<<< Flapped 5 times in last 5 mins EEM script ran and did admin shutdown on the gi 1/0/5 interface.
Catalyst_Switch#
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ Many Cisco switches and routers provide an on-box Python Interpreter that can be
| [EEM Config Changes to Spark](/eem_configdiff_to_spark) | In this example, the EEM library is used to monitor for configuration changes. When one occurs a message is sent to a Cisco Spark Room. |
| [Python with Eventing Example](/EEM-interface-move-routes) | Use the EEM and Python together to script based on local events. |
| [EEM + Python + Spark ChatOps](/spark_checkin) | Use the EEM to monitor for config changes and send a Spark Message |

| [EEM + Python + Email alert](/PortFlap_email_alert) | This example leverages the CLI library and using the EEM feature to monitor for interface flapping and send an email alert |


## Off-Box Examples
Expand Down
2 changes: 1 addition & 1 deletion RESTCONF/delete-Ip-address.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ def main():
# RESTCONF media types for REST API headers
headers = {'Content-Type': 'application/yang-data+json',
'Accept': 'application/yang-data+json'}
# this statement performs a GET on the specified url
# this statement performs a DELETE on the specified url
response = requests.request("DELETE",url, auth=(USER, PASS),
headers=headers, verify=False)
# print the json that is returned
Expand Down
2 changes: 1 addition & 1 deletion RESTCONF/patch-Ip-address-config.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ def main():
# RESTCONF media types for REST API headers
headers = {'Content-Type': 'application/yang-data+json',
'Accept': 'application/yang-data+json'}
# this statement performs a GET on the specified url
# this statement performs a PATCH on the specified url
response = requests.request("PATCH",url, auth=(USER, PASS),
data=payload, headers=headers, verify=False)

Expand Down
2 changes: 1 addition & 1 deletion RESTCONF/post-ipdomain.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ def main():
# RESTCONF media types for REST API headers
headers = {'Content-Type': 'application/yang-data+json',
'Accept': 'application/yang-data+json'}
# this statement performs a GET on the specified url
# this statement performs a POST on the specified url
response = requests.request("POST",url, auth=(USER, PASS),
data=payload, headers=headers, verify=False)

Expand Down
2 changes: 1 addition & 1 deletion RESTCONF/put-hostname-config.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ def main():
# RESTCONF media types for REST API headers
headers = {'Content-Type': 'application/yang-data+json',
'Accept': 'application/yang-data+json'}
# this statement performs a GET on the specified url
# this statement performs a PUT on the specified url
response = requests.request("PUT",url, auth=(USER, PASS),
data=payload, headers=headers, verify=False)

Expand Down