Skip to content

Commit f2e3459

Browse files
authored
Merge pull request #7 from krishna426426/master
just updated the comments
2 parents 633c0c1 + 95a98e6 commit f2e3459

File tree

9 files changed

+274
-5
lines changed

9 files changed

+274
-5
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Catalyst_Switch#sh run | sec event manager
2+
event manager applet config_change
3+
event syslog pattern "GigabitEthernet1/0/5, changed state to down"
4+
action 0 cli command "enable"
5+
action 1 cli command "guestshell run python port_flap_email_alert.py"

PortFlap_email_alert/README.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# port_flap_email_alert script will send an email alert if an interface is flapping
2+
3+
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.
4+
5+
## Setup
6+
7+
This script requires the IOS-XE guestshell feature. To enable guestshell, configure
8+
9+
```
10+
iox
11+
```
12+
13+
Then type the following in EXEC mode:
14+
15+
```
16+
guestshell enable
17+
```
18+
19+
Save the script port_flap_email_alert.py in guestshell.
20+
21+
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.
Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
#
2+
# Copyright (c) 2018 Krishna Kotha <[email protected]>
3+
# All rights reserved.
4+
#
5+
# Redistribution and use in source and binary forms, with or without
6+
# modification, are permitted provided that the following conditions
7+
# are met:
8+
# 1. Redistributions of source code must retain the above copyright
9+
# notice, this list of conditions and the following disclaimer.
10+
# 2. Redistributions in binary form must reproduce the above copyright
11+
# notice, this list of conditions and the following disclaimer in the
12+
# documentation and/or other materials provided with the distribution.
13+
#
14+
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15+
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17+
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18+
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19+
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20+
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21+
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22+
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23+
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24+
# SUCH DAMAGE.
25+
#
26+
# PURPOSE of this SCRIPT
27+
# 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.
28+
# Then it will wait for 5 mins and enable that interface.
29+
# If still flapping atleast once it will shutdown that interface and will send an email alert.
30+
#
31+
#
32+
# This script monitors the interface GigabitEthernet1/0/5, change the interface ID based on your requirement.
33+
#
34+
#
35+
# This script requires the following variables to be defined:
36+
# FROM_ADDR
37+
# TO_ADDR
38+
# name-server ip address
39+
# http_proxy and https_proxy commands
40+
#
41+
42+
# importing necessary modules
43+
import os
44+
import sys
45+
import cli
46+
import time
47+
import difflib
48+
from datetime import datetime
49+
from time import strptime
50+
from time import mktime
51+
import smtplib
52+
import shutil
53+
from email.MIMEMultipart import MIMEMultipart
54+
from email.MIMEText import MIMEText
55+
import subprocess
56+
57+
58+
def checkPortFlap(port_flaps_cnt,interface):
59+
60+
# calculate current time using time module
61+
currTime = time.time()
62+
print("curr time is: %s" % (currTime))
63+
64+
# retrieve the show log output
65+
arr_output = cli.cli("show log").split("\n")
66+
67+
for line in arr_output:
68+
69+
# In this example I am monitoring the Gi 1/0/5 interface, based on your requirement change the interface ID.
70+
if(line.find("GigabitEthernet1/0/5, changed state to down") != -1):
71+
print("The line %s indicates a port flap action" % (line))
72+
line_arr = line.split("%")
73+
74+
# seperate the date from the log lines and convert the matching line to list objects and strings
75+
date_in_log = line_arr[0]
76+
print("the entry date is: %s" % (date_in_log))
77+
line_arr = line.split(",")
78+
line_arr_1 = line_arr[0].split(" ")
79+
str_line_arr_0=''.join(line_arr_1[0])
80+
81+
# remove * and : from the strings
82+
str_line_arr_0= str_line_arr_0[1:]
83+
str_line_arr_1=''.join(line_arr_1[3])
84+
str_line_arr_1=str_line_arr_1[:-1]
85+
86+
# parse a string to represent a time according to a format
87+
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")
88+
89+
# calculate the log time in seconds
90+
log_time_secs = mktime(log_time)
91+
92+
# calculate the difference from current time to log time
93+
delta=currTime-log_time_secs
94+
interface = line_arr_1[len(line_arr_1) - 1]
95+
print("the interface is: %s" % (interface))
96+
97+
# if delta is less than 5 minutes that log will be counted.
98+
if delta <= 300:
99+
port_flaps_cnt = port_flaps_cnt + 1
100+
return port_flaps_cnt, interface
101+
102+
103+
104+
def send_e_mail(subject):
105+
"""
106+
send an e mail from the Cisco network to anyone -
107+
Note: This example uses from address as a Cisco server, Change the domain to your SMTP server to send email from your domain.
108+
"""
109+
110+
# retrieve the hostname using in-built cli module
111+
host_name = cli.cli("show running-config | include hostname")
112+
FROM_ADDR = '[email protected]'
113+
TO_ADDR = '[email protected]'
114+
115+
# create the message
116+
msg = MIMEMultipart()
117+
msg['From'] = FROM_ADDR
118+
msg['To'] = TO_ADDR
119+
msg['Subject'] = "%s - %s" % (subject, host_name)
120+
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)
121+
msg.attach(MIMEText(text, 'plain'))
122+
123+
# connect to server and send
124+
server = smtplib.SMTP('outbound.your_company_name.com', 25)
125+
server.sendmail(FROM_ADDR, TO_ADDR, msg.as_string())
126+
server.quit()
127+
128+
129+
def set_device_to_role_as_cisco_mail_server():
130+
"""
131+
Manipulates the /etc/resolv.conf and set it to a Cisco mail server. The commands are known to the average network Linux admin...
132+
"""
133+
commands_to_run_on_gs = [
134+
"sudo rm /etc/resolv.conf",
135+
"sudo touch /etc/resolv.conf",
136+
"echo \"nameserver x.x.x.x\" | sudo tee -a /etc/resolv.conf",
137+
"echo \"domain x.x.x.x.com\" | sudo tee -a /etc/resolv.conf",
138+
"echo \"export http_proxy=http://x.x.x.x:80/\" | sudo tee -a /etc/bashrc",
139+
"echo \"export https_proxy=http://x.x.x.x:80/\" | sudo tee -a /etc/bashrc",
140+
"echo \"export ftp_proxy=http://x.x.x.x:80/\" | sudo tee -a /etc/bashrc",
141+
"echo \"export no_proxy=.x.x.x.x.com\" | sudo tee -a /etc/bashrc",
142+
"echo \"export HTTP_PROXY=http://x.x.x.x:80/\" | sudo tee -a /etc/bashrc",
143+
"echo \"export HTTPS_PROXY=http://x.x.x.x:80/\" | sudo tee -a /etc/bashrc",
144+
"echo \"export FTP_PROXY=http://x.x.x.x:80/\" | sudo tee -a /etc/bashrc"
145+
]
146+
for command in commands_to_run_on_gs:
147+
p = subprocess.Popen(command, stdout=subprocess.PIPE, shell=True)
148+
(output, err) = p.communicate()
149+
print "Linux command output is", output
150+
151+
152+
# main code:
153+
if __name__ == '__main__':
154+
155+
# these commands are need to remove msec and add year in the show log command
156+
cli.configurep("service timestamps log datetime")
157+
cli.configurep("service timestamps log datetime year")
158+
159+
160+
port_flaps_cnt = 0
161+
interface = None
162+
port_flaps = checkPortFlap(port_flaps_cnt,interface)
163+
164+
if(port_flaps[0] == 10):
165+
if(port_flaps[1] is not None):
166+
print("shutting down interface %s" % (port_flaps[1]))
167+
168+
# shut down the interface using cli module
169+
#cli(["interface %s" % (port_flaps[1]), "shut", "end"])
170+
cli.configurep(["interface %s" % (port_flaps[1]), "shut", "end"])
171+
172+
# wait for 5 mins to monitor the interface flap again
173+
time.sleep(300)
174+
print("Waited for 5 mins Enabling the interface %s" % (port_flaps[1]))
175+
176+
# enable the interface
177+
#cli("cont f", "interfact %s" % (port_flaps[1]), "no shut", "end")
178+
cli.configurep(["interface %s" % (port_flaps[1]), "no shut", "end"])
179+
port_flaps_cnt = 0
180+
interface = None
181+
182+
# re check the interface is flapping or not
183+
port_flaps = checkPortFlap(port_flaps_cnt,interface)
184+
185+
# if still flapping shut down the interface and send email
186+
if(port_flaps[0] >= 2):
187+
if(port_flaps[1] is not None):
188+
print("shutting down interface %s" % (port_flaps[1]))
189+
#cli("cont f", "interfact %s" % (port_flaps[1]), "shut", "end")
190+
cli.configurep(["interface %s" % (port_flaps[1]), "shut", "end"])
191+
set_device_to_role_as_cisco_mail_server()
192+
send_e_mail("interface %s is flapping and did admin shutdown" % (port_flaps[1]))
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
Catalyst_Switch#sh log
2+
Syslog logging: enabled (0 messages dropped, 11 messages rate-limited, 0 flushes, 0 overruns, xml disabled, filtering disabled)
3+
4+
No Active Message Discriminator.
5+
6+
7+
8+
No Inactive Message Discriminator.
9+
10+
11+
Console logging: level debugging, 169945 messages logged, xml disabled,
12+
filtering disabled
13+
Monitor logging: level debugging, 39 messages logged, xml disabled,
14+
filtering disabled
15+
Buffer logging: level debugging, 169955 messages logged, xml disabled,
16+
filtering disabled
17+
Exception Logging: size (4096 bytes)
18+
Count and timestamp logging messages: disabled
19+
File logging: disabled
20+
Persistent logging: disabled
21+
22+
No active filter modules.
23+
24+
Trap logging: level informational, 169933 message lines logged
25+
Logging Source-Interface: VRF Name:
26+
27+
Log Buffer (4096 bytes):
28+
29+
*Apr 19 2018 18:00:06: %LINEPROTO-5-UPDOWN: Line protocol on Interface GigabitEthernet1/0/5, changed state to down
30+
*Apr 19 2018 18:00:07: %LINK-3-UPDOWN: Interface GigabitEthernet1/0/5, changed state to down
31+
*Apr 19 2018 18:00:14: %LINK-3-UPDOWN: Interface GigabitEthernet1/0/5, changed state to up
32+
*Apr 19 2018 18:00:15: %LINEPROTO-5-UPDOWN: Line protocol on Interface GigabitEthernet1/0/5, changed state to up
33+
*Apr 19 2018 18:00:19: %LINEPROTO-5-UPDOWN: Line protocol on Interface GigabitEthernet1/0/5, changed state to down
34+
*Apr 19 2018 18:00:20: %LINK-3-UPDOWN: Interface GigabitEthernet1/0/5, changed state to down
35+
*Apr 19 2018 18:00:29: %LINK-3-UPDOWN: Interface GigabitEthernet1/0/5, changed state to up
36+
*Apr 19 2018 18:00:30: %LINEPROTO-5-UPDOWN: Line protocol on Interface GigabitEthernet1/0/5, changed state to up
37+
*Apr 19 2018 18:00:53: %LINEPROTO-5-UPDOWN: Line protocol on Interface GigabitEthernet1/0/5, changed state to down
38+
*Apr 19 2018 18:00:54: %LINK-3-UPDOWN: Interface GigabitEthernet1/0/5, changed state to down
39+
*Apr 19 2018 18:01:01: %LINK-3-UPDOWN: Interface GigabitEthernet1/0/5, changed state to up
40+
*Apr 19 2018 18:01:02: %LINEPROTO-5-UPDOWN: Line protocol on Interface GigabitEthernet1/0/5, changed state to up
41+
*Apr 19 2018 18:01:07: %LINEPROTO-5-UPDOWN: Line protocol on Interface GigabitEthernet1/0/5, changed state to down
42+
*Apr 19 2018 18:01:08: %LINK-3-UPDOWN: Interface GigabitEthernet1/0/5, changed state to down
43+
*Apr 19 2018 18:01:27: %LINK-3-UPDOWN: Interface GigabitEthernet1/0/5, changed state to up
44+
*Apr 19 2018 18:01:29: %LINK-3-UPDOWN: Interface GigabitEthernet1/0/5, changed state to down
45+
*Apr 19 2018 18:01:50: %LINK-3-UPDOWN: Interface GigabitEthernet1/0/5, changed state to up
46+
*Apr 19 2018 18:01:51: %LINEPROTO-5-UPDOWN: Line protocol on Interface GigabitEthernet1/0/5, changed state to up
47+
*Apr 19 2018 18:02:07: %UTIL-6-RANDOM: A pseudo-random number was generated twice in succession
48+
*Apr 19 2018 18:02:08: %LINEPROTO-5-UPDOWN: Line protocol on Interface GigabitEthernet1/0/5, changed state to down
49+
*Apr 19 2018 18:02:09: %LINK-3-UPDOWN: Interface GigabitEthernet1/0/5, changed state to down
50+
*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.
51+
Catalyst_Switch#

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ Many Cisco switches and routers provide an on-box Python Interpreter that can be
1515
| [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. |
1616
| [Python with Eventing Example](/EEM-interface-move-routes) | Use the EEM and Python together to script based on local events. |
1717
| [EEM + Python + Spark ChatOps](/spark_checkin) | Use the EEM to monitor for config changes and send a Spark Message |
18-
18+
| [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 |
1919

2020

2121
## Off-Box Examples

RESTCONF/delete-Ip-address.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ def main():
5151
# RESTCONF media types for REST API headers
5252
headers = {'Content-Type': 'application/yang-data+json',
5353
'Accept': 'application/yang-data+json'}
54-
# this statement performs a GET on the specified url
54+
# this statement performs a DELETE on the specified url
5555
response = requests.request("DELETE",url, auth=(USER, PASS),
5656
headers=headers, verify=False)
5757
# print the json that is returned

RESTCONF/patch-Ip-address-config.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ def main():
5252
# RESTCONF media types for REST API headers
5353
headers = {'Content-Type': 'application/yang-data+json',
5454
'Accept': 'application/yang-data+json'}
55-
# this statement performs a GET on the specified url
55+
# this statement performs a PATCH on the specified url
5656
response = requests.request("PATCH",url, auth=(USER, PASS),
5757
data=payload, headers=headers, verify=False)
5858

RESTCONF/post-ipdomain.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ def main():
5252
# RESTCONF media types for REST API headers
5353
headers = {'Content-Type': 'application/yang-data+json',
5454
'Accept': 'application/yang-data+json'}
55-
# this statement performs a GET on the specified url
55+
# this statement performs a POST on the specified url
5656
response = requests.request("POST",url, auth=(USER, PASS),
5757
data=payload, headers=headers, verify=False)
5858

RESTCONF/put-hostname-config.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ def main():
5252
# RESTCONF media types for REST API headers
5353
headers = {'Content-Type': 'application/yang-data+json',
5454
'Accept': 'application/yang-data+json'}
55-
# this statement performs a GET on the specified url
55+
# this statement performs a PUT on the specified url
5656
response = requests.request("PUT",url, auth=(USER, PASS),
5757
data=payload, headers=headers, verify=False)
5858

0 commit comments

Comments
 (0)