Take a Break, I got this! – An EEM Post


Lately we have been looking at various automation topics like Python where we can do some configuration or grab some output on a few boxes. Today we are going to kick the tires with Cisco’s Embedded Event Manager (EEM), the neat difference with this feature is that it runs directly on the Cisco device so it can run based on events like a command being entered or the router getting a new CDP neighbor.

The Basics

Most of the configuration we’ll be looking at today is under the event manager applet We can call the script anything we want so of course I’ll use MEOWCAT, once instead the config we have 3 main options that we care about:

  • EVENT – This is the “event” that causes the script to run
  • ACTION – This is what the actual script that runs
  • TRIGGER – This is optional but lets you adjust when the event is triggered.

The number of events varies depending on your platform and version but EEM is pretty much supported across the board.

R01(config-applet)#event ?
 application Application specific event
 cli CLI event
 config Configuration policy event
 counter Counter event
 env Environmental event
 gold GOLD event
 interface Interface event
 ioswdsysmon IOS WDSysMon event
 ipsla IPSLA Event
 mat MAC address table event
 neighbor-discovery Neighbor Discovery event
 none Manually run policy event
 oir OIR event
 resource Resource event
 rf Redundancy Facility event
 routing Routing event
 rpc Remote Procedure Call event
 snmp SNMP event
 snmp-notification SNMP Notification Event
 snmp-object SNMP object event
 syslog Syslog event
 tag event tag identifier
 timer Timer event
 track Tracking object event

Likewise the number of actions depends on your platform and version.

R01(config-applet)#action 10 ?
 add Add
 append Append to a variable
 break Break out of a conditional loop
 cli Execute a CLI command
 cns-event Send a CNS event
 comment add comment
 context Save or retrieve context information
 continue Continue to next loop iteration
 counter Modify a counter value
 decrement Decrement a variable
 divide Divide
 else else conditional
 elseif elseif conditional
 end end conditional block
 exit Exit from applet run
 file file operations
 force-switchover Force a software switchover
 foreach foreach loop
 gets get line of input from active tty
 handle-error On error action
 help Read/Set parser help buffer
 if if conditional
 increment Increment a variable
 info Obtain system specific information
 mail Send an e-mail
 multiply Multiply
 policy Run a pre-registered policy
 publish-event Publish an application specific event
 puts print data to active tty
 regexp regular expression match
 reload Reload system
 set Set a variable
 snmp-object-value Specify value for the SNMP get request
 snmp-trap Send an SNMP trap
 string string commands
 subtract Subtract
 syslog Log a syslog message
 track Read/Set a tracking object
 wait Wait for a specified amount of time
 while while loop

Actions are run from lowest number to highest number and IOS will automatically sort the actions when you exit, it is a good practice to use consistent numbering since going from 1 digit actions to 2 digits might mix things in a way you don’t want.

R01(config-applet)# action 1 syslog msg "TEST03"
 R01(config-applet)#action 08 syslog msg TEST02"
 R01(config-applet)#action 11 syslog msg "TEST04"
 R01(config)#do sh run | s TEST
 event manager applet TEST
 event none
 action 08 syslog msg "TEST02""
 action 1 syslog msg "TEST03"
 action 10 syslog msg "TEST01"
 action 11 syslog msg "TEST04"

CLI Example

Let’s look at a simple example that can stop some headaches in your network. We have all heard of a horror story where a junior accidentally causes an outage by typing switchport trunk allowed vlan 100 instead of switchport trunk allowed vlan add 100

We will stop that by preventing the evil command from running and then have the switch remind the junior!

First we name the script

SW01(config-if)#event manager applet ANTI-NOOB

Then we look for a CLI event that looks for switchport trunk allowed vlan followed by any number, the sync yes tells the router to run the script instead of the command. We can also have both the script and command run etc.

SW01(config-applet)# event cli pattern "switchport trunk allowed vlan [0-9*]" sync yes

Next we just have the switch display our educational message then we exit, just like with vlans we need to exit for the config to become live.

SW01(config-applet)# action 100 puts "NO! BAD JUNIOR! NO! Use the Add keyword"

Now if we try to run the evil command we get our message instead and we can see the config didn’t apply.

SW01(config)#int g1/0
 SW01(config-if)#switchport trunk allowed vlan none
 SW01(config-if)#switchport trunk allowed vlan 100
 NO! BAD JUNIOR! NO! Use the Add keyword

SW01(config-if)#do sh run int g1/0 | in allowed
 switchport trunk allowed vlan none

But we can still use the add keyword like normal.

SW01(config-if)#switchport trunk allowed vlan add 100
 SW01(config-if)#do sh run int g1/0 | in allowed
 switchport trunk allowed vlan 100

Routing Example

Lets do another simple example where EEM makes and advertises a new Loopback if it detects a certain OSPF route in the routing table.

First we make an event that that matches the OSPF route we want to look for.

R01(config)# event manager applet ROUTE
 R01(config-applet)# event routing network type add protocol OSPF

Next we have our actions add the loopback and advertise it

Note: EEM’s process starts as unprivileged so we need to enter enable mode though we don’t need to specify the actual password. We also need to go to config mode if we are changing things.

R01(config-applet)# action 10 cli command "enable"
 R01(config-applet)# action 11 cli command "conf t"
 R01(config-applet)# action 12 cli command "interface l1"
 R01(config-applet)# action 13 cli command "ip add"
 R01(config-applet)# action 14 cli command "ip ospf 1 area 100"

Lastly we will add a syslog message so we know the script ran.

R01(config-applet)# action 20 syslog msg "Added New Loopback!"

Lets test this out by making a new loopback with the address and add it to OSPF

R03(config)#int l33
 R03(config-if)#ip add
 R03(config-if)#ip ospf 1 area 300

On R01 we see that our loopback is created.

Sep 2 22:00:26.131: %LINEPROTO-5-UPDOWN: Line protocol on Interface Loopback1, changed state to up
 Sep 2 22:00:26.519: %HA_EM-6-LOG: ROUTE: Added New Loopback!
 R01(config)#do sh run int l1
 Building configuration...

Current configuration : 83 bytes
 interface Loopback1
 ip address
 ip ospf 1 area 100
On R03 we see the route!

 R03(config-if)#do sh ip route ospf | in IA
 D - EIGRP, EX - EIGRP external, O - OSPF, IA - OSPF inter area
 O IA [110/3] via, 00:00:30, GigabitEthernet2.23

Destructive Change Example

One simple but effective use of EEM is scripting out destructive changes, say you have to change a IP and default gateway on a router through SSH – You can’t simply paste in the commands because you’ll lose connectivity when you change the IP or remove the gateway. With EEM we can have the router run all the commands even though it loses connectivity.

Lets see how R02 is configured now.

R02#show ip int br | ex unass
 Interface IP-Address OK? Method Status Protocol
 GigabitEthernet2.12 YES manual up up
 GigabitEthernet2.23 YES manual up up
 Loopback0 YES manual up up

R02#show run | in ip route
 ip route

Then we’ll SSH into the router from R01 to keep things authentic

R01#ssh -l cisco

 R02#conf t
 Enter configuration commands, one per line. End with CNTL/Z.

We will use event none for this which means we will have to manually call the script when we are ready.

R02(config)#event manager applet CHANGEIP
 R02(config-applet)#event none

Then we just add all the commands we want to run, one things to note is that there is no ? or validation for the IOS commands in this section so make sure you don’t do a typo.

R02(config-applet)#action 10 cli command "enable"
 R02(config-applet)#action 11 cli command "conf t"
 R02(config-applet)#action 12 cli command "interface g2.12"
 R02(config-applet)#action 13 cli command "ip add"
 R02(config-applet)#action 14 cli command "no ip route"
 R02(config-applet)#action 15 cli command "ip route"

Then we run the script from privileged mode.

R02#event manager run CHANGEIP

We can see the changes worked!!!

R02#show ip int br | ex unass
 Interface IP-Address OK? Method Status Protocol
 GigabitEthernet2.12 YES manual up up
 GigabitEthernet2.23 YES manual up up
 Loopback0 YES manual up up

 .Sep 2 22:20:58.183: %OSPF-5-ADJCHG: Process 1, Nbr on GigabitEthernet2.12 from LOADING to FULL, Loading Done

R02#show run | in ^ip route
 ip route

CDP Example

We’ll close this out by looking a more complex script that Cisco put out on their site, this will use set the interface description by using CDP info. We’ll also do some manipulations to make the output a bit nicer.

First we will make an event that runs when a new CDP neighbor is discovered.

SW01(config)# event manager applet auto-update-port-description authorization bypass
 SW01(config-applet)# description "Auto-update port-description based on CDP neighbors info"
 SW01(config-applet)# event neighbor-discovery interface regexp .*GigabitEthernet.* cdp add

Next we strip the domain-name from the CDP output

SW01(config-applet)# action 0.0 comment "Event line regexp: Deside which interface to auto-update description on"
 SW01(config-applet)# action 1.0 comment "Trim domain name"
 SW01(config-applet)# action 1.1 string trimright "$_nd_cdp_entry_name" ".testlab.com"
 SW01(config-applet)# action 1.2 set _host "$_string_result"
 SW01(config-applet)# action 1.3 set _host "$_string_result"

Then we do some manipulations to shorten the port name from GigabitEthernet to Gi etc.

SW01(config-applet)# action 2.0 comment "Convert long interface name to short"
 SW01(config-applet)# action 2.1 string first "Ethernet" "$_nd_port_id"
 SW01(config-applet)# action 2.2 if $_string_result eq "7"
 SW01(config-applet)# action 2.21 string replace "$_nd_port_id" 0 14 "Gi"
 SW01(config-applet)# action 2.3 elseif $_string_result eq 10
 SW01(config-applet)# action 2.31 string replace "$_nd_port_id" 0 17 "Te"
 SW01(config-applet)# action 2.4 elseif $_string_result eq 4
 SW01(config-applet)# action 2.41 string replace "$_nd_port_id" 0 11 "Fa"
 SW01(config-applet)# action 2.5 end
 SW01(config-applet)# action 2.6 set _int "$_string_result"

Lastly we set the description based on the CDP information

SW01(config-applet)# action 3.0 comment "Actual config of port description"
 SW01(config-applet)# action 3.1 cli command "enable"
 SW01(config-applet)# action 3.2 cli command "config t"
 SW01(config-applet)# action 3.3 cli command "interface $_nd_local_intf_name"
 SW01(config-applet)# action 3.4 cli command "description Connected to $_host Port $_int"
 SW01(config-applet)# action 3.5 cli command "do write"
 SW01(config-applet)# action 4.0 syslog msg "EEM script updated description on $_nd_local_intf_name and saved config"

Let’s test this out!!!!

First we’ll have a look at what neighbors we have on SW01

SW01#show cdp ne
 Capability Codes: R - Router, T - Trans Bridge, B - Source Route Bridge
 S - Switch, H - Host, I - IGMP, r - Repeater, P - Phone,
 D - Remote, C - CVTA, M - Two-port Mac Relay

Device ID Local Intrfce Holdtme Capability Platform Port ID
 SW02.testlab.com Gig 3/0 134 R S I Gig 3/0
 SW02.testlab.com Gig 3/1 164 R S I Gig 3/1
 SW03.testlab.com Gig 3/3 145 R S I Gig 3/3
 SW03.testlab.com Gig 3/2 165 R S I Gig 3/2
 R01.testlab.com Gig 0/1 170 R I CSR1000V Gig 2

Total cdp entries displayed : 5

Since the script works when CDP discovers a new neighbor we need to clear the CDP table. Then we’ll see the syslog messages as CDP discovers each neighbor.

SW01#clear cdp table

*Sep 2 21:59:55.954: %GRUB-5-CONFIG_WRITING: GRUB configuration is being updated on disk. Please wait...
 *Sep 2 21:59:56.880: %GRUB-5-CONFIG_WRITTEN: GRUB configuration was written to disk successfully.
 *Sep 2 21:59:56.923: %HA_EM-6-LOG: auto-update-port-description: EEM script updated description on GigabitEthernet3/1 and saved config
 *Sep 2 22:00:01.275: %GRUB-5-CONFIG_WRITING: GRUB configuration is being updated on disk. Please wait...
 *Sep 2 22:00:02.083: %GRUB-5-CONFIG_WRITTEN: GRUB configuration was written to disk successfully.
 *Sep 2 22:00:02.155: %HA_EM-6-LOG: auto-update-port-description: EEM script updated description on GigabitEthernet3/2 and saved config
 *Sep 2 22:00:14.658: %GRUB-5-CONFIG_WRITING: GRUB configuration is being updated on disk. Please wait...
 *Sep 2 22:00:15.440: %GRUB-5-CONFIG_WRITTEN: GRUB configuration was written to disk successfully.
 *Sep 2 22:00:15.481: %HA_EM-6-LOG: auto-update-port-description: EEM script updated description on GigabitEthernet3/0 and saved config
 *Sep 2 22:00:20.310: %GRUB-5-CONFIG_WRITING: GRUB configuration is being updated on disk. Please wait...
 *Sep 2 22:00:21.110: %GRUB-5-CONFIG_WRITTEN: GRUB configuration was written to disk successfully.
 *Sep 2 22:00:21.185: %HA_EM-6-LOG: auto-update-port-description: EEM script updated description on GigabitEthernet0/1 and saved config
 *Sep 2 22:00:28.503: %GRUB-5-CONFIG_WRITING: GRUB configuration is being updated on disk. Please wait...
 *Sep 2 22:00:29.358: %GRUB-5-CONFIG_WRITTEN: GRUB configuration was written to disk successfully.
 *Sep 2 22:00:29.428: %HA_EM-6-LOG: auto-update-port-description: EEM script updated description on GigabitEthernet3/3 and saved config

Now if we look at the interface descriptions we can see “Connected to Port

SW01#show interface description | in Gi
 Gi0/0 up up OOB management
 Gi0/1 up up Connected to R01 Port Gi2
 Gi0/2 up up to R02
 Gi0/3 up up to R03
 Gi1/0 up up to S01
 Gi1/1 up up
 Gi1/2 up up
 Gi1/3 up up
 Gi2/0 up up to RM01
 Gi2/1 up up
 Gi2/2 up up
 Gi2/3 up up
 Gi3/0 up up Connected to SW02 Port Gi3/0
 Gi3/1 up up Connected to SW02 Port Gi3/1
 Gi3/2 up up Connected to SW03 Port Gi3/2
 Gi3/3 up up Connected to SW03 Port Gi3/3

Wrapping up

That is all for today, hope y’all found it interesting.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.