[PYTHON] Cisco IOS-XE captures changes in the routing table and posts them to external services

What is this?

Using EEM (Embedded Event Manager) and on-box python supported by Cisco routers and switches, we can see that the routing table has changed. A sample that notifies an external service (Cisco Spark) as a trigger.

The content to be notified is the changed network, the changed content (addition / deletion, etc.), and the changed cause (OSPF, EIGRP, BGP, CONNECTED, etc.), and these are provided by EEM. EEM script is written in Python. The device used was the Catalyst 3850 that I had at hand.

Demo video (youtube opens) IOS-XE EEM Routing ED with Python Script and notify to Cisco Spark

※reference [Python and Bash in Cisco Catalyst IOS-XE](http://qiita.com/kikuta1978/items/1739d7d7063a20b1736f#eem%E3%81%8B%E3%82%89%E5%AE%9F%E8%A1 % 8C) Running Tcl Scripts Using Cisco IOS EEM (http://qiita.com/kikuta1978/items/5d7d92e7f35d0a03922b) Share device events from container on Catalyst switch to Cisco Spark

environment

IOS-XE version

Cat3850-3#sh ver | i .bin
System image file is "flash:cat3k_caa-universalk9.16.06.01.SPA.bin"

Setting

The EEM policy written in Python script is installed in Flash of Catalyst.

routewatch.py


Cat3850-3#dir | i routewatch.py
54679  -rw-             1043  Aug 29 2017 13:28:46 +09:00  routewatch.py

The IOS command settings are on the following two lines. Specify the Path of the EEM script and register the actual script (described later). Execute the second line below to compile the script and run it in memory.

event manager directory user policy "flash:/"
event manager policy routewatch.py type user

EEM Python script

routewatch.py


::cisco::eem::event_register_routing network 1.1.1.0/24 type all ge 24
#::cisco::eem::event_register_routing network 1.1.1.0/24 type all ge 24 ratelimit 60

import requests
import sys
import eem

ACCESS_TOKEN = "<my_access_token>"
ROOM_ID = "<my_room_id>"

#Header creation
def setHeaders():
    accessToken_hdr = 'Bearer ' + ACCESS_TOKEN
    spark_header = {'Authorization': accessToken_hdr, 'Content-Type': 'application/json; charset=utf-8'}
    return spark_header

#Post a message to the Spark room
def postMsg(the_header,roomId,message):
    message = '{"roomId":"' + roomId + '","text":"' + message +'"}'
    uri = 'https://api.ciscospark.com/v1/messages'
    resp = requests.post(uri, data=message, headers=the_header)
    print resp

#Get the information provided internally for each EEM event and compose a message
event = eem.event_reqinfo()
message = '!!! RoutingTable Change Detected by EEM: !!! -> ' + event['network'] + '-' + event['type'] + '-BY-' + event['protocol']
          
header=setHeaders()
postMsg(header,ROOM_ID,message)

Below are some points.

--First line: Cisco EEM notation. Uses routing event detection. 1.1.1.0/24 and ge (great equal) match path changes longer than the 24-bit mask. 1.1.1.X matches, 2.2.2.2 does not. --type all detects all add / remove / modify. --It is realistically necessary to round events within a certain period of time so as not to capture all continuously occurring states such as root flaps (an example of setting ratelimit to 60 seconds is also shown, not set this time) .. --By default, script operation is killed from IOS after 20 seconds (MAXRUN Timer). If it is assumed that the processing time is long in advance and there is no problem, expand the description at the end of the first line (example: MAXRUN 300). --You can use the library provided by the IOS EEM function with import eem-> eem.event_reqinfo () --By event_reqinfo (), an array is automatically generated with the contents generated for each event as internal variables. In this example, it is used for event ['network'], event ['type'], event ['protocol']. --See here for posting to Cisco Spark.

Operation check

1. Initial state-> Added 1.1.1.200 with OSPF

Before


Cat3850-3#sh ip route
Codes: L - local, C - connected, S - static, R - RIP, M - mobile, B - BGP
       D - EIGRP, EX - EIGRP external, O - OSPF, IA - OSPF inter area 
       N1 - OSPF NSSA external type 1, N2 - OSPF NSSA external type 2
       E1 - OSPF external type 1, E2 - OSPF external type 2
       i - IS-IS, su - IS-IS summary, L1 - IS-IS level-1, L2 - IS-IS level-2
       ia - IS-IS inter area, * - candidate default, U - per-user static route
       o - ODR, P - periodic downloaded static route, H - NHRP, l - LISP
       a - application route
       + - replicated route, % - next hop override, p - overrides from PfR

Gateway of last resort is not set

      1.0.0.0/32 is subnetted, 2 subnets
C        1.1.1.1 is directly connected, Loopback100
O        1.1.1.100 [110/2] via 192.168.1.2, 01:10:05, Vlan1
      192.168.1.0/24 is variably subnetted, 2 subnets, 2 masks
C        192.168.1.0/24 is directly connected, Vlan1
L        192.168.1.1/32 is directly connected, Vlan1

After1


Cat3850-3#sh ip route
Codes: L - local, C - connected, S - static, R - RIP, M - mobile, B - BGP
       D - EIGRP, EX - EIGRP external, O - OSPF, IA - OSPF inter area 
       N1 - OSPF NSSA external type 1, N2 - OSPF NSSA external type 2
       E1 - OSPF external type 1, E2 - OSPF external type 2
       i - IS-IS, su - IS-IS summary, L1 - IS-IS level-1, L2 - IS-IS level-2
       ia - IS-IS inter area, * - candidate default, U - per-user static route
       o - ODR, P - periodic downloaded static route, H - NHRP, l - LISP
       a - application route
       + - replicated route, % - next hop override, p - overrides from PfR

Gateway of last resort is not set

      1.0.0.0/32 is subnetted, 3 subnets
C        1.1.1.1 is directly connected, Loopback100
O        1.1.1.100 [110/2] via 192.168.1.2, 01:11:55, Vlan1
O        1.1.1.200 [110/2] via 192.168.1.2, 00:00:34, Vlan1 <---★★ Added
      192.168.1.0/24 is variably subnetted, 2 subnets, 2 masks
C        192.168.1.0/24 is directly connected, Vlan1
L        192.168.1.1/32 is directly connected, Vlan1

Posted on Spark:

--Changed route: 1.1.1.200 --Type: add --Protocol: OSPF

SS 2017-08-29 15.40.18.png

2. OSPF update causes 1.1.1.100 to disappear from table

After2


Cat3850-3#sh ip route
Codes: L - local, C - connected, S - static, R - RIP, M - mobile, B - BGP
       D - EIGRP, EX - EIGRP external, O - OSPF, IA - OSPF inter area 
       N1 - OSPF NSSA external type 1, N2 - OSPF NSSA external type 2
       E1 - OSPF external type 1, E2 - OSPF external type 2
       i - IS-IS, su - IS-IS summary, L1 - IS-IS level-1, L2 - IS-IS level-2
       ia - IS-IS inter area, * - candidate default, U - per-user static route
       o - ODR, P - periodic downloaded static route, H - NHRP, l - LISP
       a - application route
       + - replicated route, % - next hop override, p - overrides from PfR

Gateway of last resort is not set

      1.0.0.0/32 is subnetted, 2 subnets
C        1.1.1.1 is directly connected, Loopback100
O        1.1.1.200 [110/2] via 192.168.1.2, 00:03:21, Vlan1
      192.168.1.0/24 is variably subnetted, 2 subnets, 2 masks
C        192.168.1.0/24 is directly connected, Vlan1
L        192.168.1.1/32 is directly connected, Vlan1

Posted on Spark:

--Changed route: 1.1.1.100 --Type: remove --Protocol: OSPF

SS 2017-08-29 15.42.19.png

3. Add 1.1.1.30 to the table by adding Loopback setting on the device

After3


Cat3850-3#sh ip route
Codes: L - local, C - connected, S - static, R - RIP, M - mobile, B - BGP
       D - EIGRP, EX - EIGRP external, O - OSPF, IA - OSPF inter area 
       N1 - OSPF NSSA external type 1, N2 - OSPF NSSA external type 2
       E1 - OSPF external type 1, E2 - OSPF external type 2
       i - IS-IS, su - IS-IS summary, L1 - IS-IS level-1, L2 - IS-IS level-2
       ia - IS-IS inter area, * - candidate default, U - per-user static route
       o - ODR, P - periodic downloaded static route, H - NHRP, l - LISP
       a - application route
       + - replicated route, % - next hop override, p - overrides from PfR

Gateway of last resort is not set

      1.0.0.0/32 is subnetted, 3 subnets
C        1.1.1.1 is directly connected, Loopback100
C        1.1.1.30 is directly connected, Loopback300 <---★★ Added
O        1.1.1.200 [110/2] via 192.168.1.2, 00:05:57, Vlan1
      192.168.1.0/24 is variably subnetted, 2 subnets, 2 masks
C        192.168.1.0/24 is directly connected, Vlan1
L        192.168.1.1/32 is directly connected, Vlan1

Posted on Spark:

--Changed route: 1.1.1.30 --Type: add --Protocol: connected

SS 2017-08-29 15.44.52.png

Setting example with EEM applet (CLI)

You can do something similar with the CLI. The following is an example of how to write an event that detects any route change and output the variables generated by the event in Syslog.

IOS-Config


event manager applet routewatch-applet
 event routing network 0.0.0.0/0 type all
 action 101 syslog msg "_event_type_string is $_event_type_string"
 action 102 syslog msg "_routing_network is $_routing_network"
 action 103 syslog msg "_routing_protocol is $_routing_protocol"
 action 104 syslog msg "_routing_type is $_routing_type"

Operation example


Cat3850-3#
Aug 29 16:07:09.331: %HA_EM-6-LOG: routewatch-applet: _event_type_string is routing
Aug 29 16:07:09.331: %HA_EM-6-LOG: routewatch-applet: _routing_network is 1.1.1.100
Aug 29 16:07:09.331: %HA_EM-6-LOG: routewatch-applet: _routing_protocol is OSPF
Aug 29 16:07:09.331: %HA_EM-6-LOG: routewatch-applet: _routing_type is add

What is event_reqinfo ()?

IOS EEM can detect various events and take actions, but it is convenient because it not only captures routing changes as in this example, but also provides the changed contents internally.

There is a convenient command to check the variables (array) generated for each event (more convenient than the online manual). In the command output below, you can see the Tcl event_reqinfo Array in addition to the EEM event notation, but you can also use it in a Python script like this sample code.

Of course, you can also retrieve values with an applet (CLI), send them via Syslog or email, write to internal files, and perform small branching and loop processing.

Cat3850-3#show event manager detector routing detailed 
No.  Name                Version   Node        Type    
1    routing             03.00     node0/0     RP      

	Tcl Configuration Syntax: 
	::cisco::eem::event_register_routing
		 [tag <tag-val>] 
		 network <network>/<length> 
		 [ge <ge-length>]
		 [le <le-length>]
		 [ne <ne-length>]
		 [type {add | remove | modify | all}]
		 [protocol <protocol-val>]
		 [queue_priority {normal | low | high | last}] 
		 [maxrun <sec.msec>]
		 [ratelimit <sec.msec>]
		 [vrf {all | default | name=regex}]
		 [nice {0 | 1}]

	Tcl event_reqinfo Array Names: 
	event_id
	job_id
	event_type
	event_type_string
	event_pub_time
        event_pub_sec
	event_pub_msec
	event_trigger_num
	event_severity
	network 
	mask 
	prefix_len 
	protocol 
	type 
	lastgateway 
	distance 
	time 
	time_sec 
	time_msec 
	metric 
	afi 
	lastinterface 

	Applet Configuration Syntax: 
	[ no ] event [tag <tag-val>] routing 
		 network <network>/<length> 
		 [ge <ge-length>]
		 [le <le-length>]
		 [ne <ne-length>]
                 [type {add | remove | modify | all}]
		 [protocol <protocol-val>]
		 [maxrun <sec.msec>]
		 [ratelimit <sec.msec>]
		 [vrf {all | default | name <regex>}]

	Applet Built-in Environment Variables: 
	$_event_id
	$_job_id
	$_event_type
	$_event_type_string
	$_event_pub_time
	$_event_pub_sec
	$_event_pub_msec
	$_event_severity
	$_routing_network 
	$_routing_mask 
	$_routing_prefix_len 
	$_routing_protocol 
	$_routing_type 
	$_routing_tag_name 
	$_routing_vrf_name 
	$_routing_topo_name 
	$_routing_lastgateway 
        $_routing_distance 
	$_routing_time 
	$_routing_time_sec 
	$_routing_time_msec 
	$_routing_metric 
	$_routing_lastinterface 
	$_routing_afi 

Below, you can check event_reqinfo for each event that can be used in IOS-XE 16.6 of Cat3850, so if you look at something, your imagination will expand.

Cat3850-3#show event manager detector ?                
  all                 All available event detectors
  application         Application event detector
  cli                 CLI event detector
  config              Config event detector
  counter             Counter event detector
  env                 Environmental event detector
  generic             Generic event detector
  gold                GOLD event detector
  identity            Identity event detector
  interface           Interface event detector
  ioswdsysmon         Ioswdsysmon event detector
  ipsla               IPSLA event detector
  mat                 mac-address-table event detector
  neighbor-discovery  neighbor discovery event detector
  nf                  NF event detector
  none                None event detector
  oir                 OIR event detector
  rf                  RF event detector
  routing             Routing event detector
  rpc                 RPC event detector
  snmp                Snmp event detector
  snmp-notification   Snmp notification event detector
  snmp-object         Snmp Object event detector
  syslog              Syslog event detector
  test                Test event detector
  timer               Timer event detector

Monitoring NetFlow cache and MAC address table, detecting connection by CDP / LLDP, etc. seems to be fun.

Cat3850-3#show event manager detector nf detailed 
No.  Name                Version   Node        Type    
1    nf                  01.00     node0/0     RP      

	Tcl Configuration Syntax: 
	::cisco::eem::event_register_nf 
		 [tag <tag-val>] 
		 monitor_name <monitor-name value>
		 event_type <create|update|delete>
		 exit_event_type <create|update|delete>
		 event1-event4 <subevent-description>
		 [maxrun <sec.msec>]
		 [ratelimit <sec.msec>]
		 [nice {0 | 1}]

		 where <subevent-description> can be 
		 field <field value>
		 rate_interval <rate interval value> (event1 only)
		 entry_value <entry value>
		 entry_op {eq|ge|gt|le|lt|wc}
		 [exit_value <exit value>]
		 [exit_op {eq|ge|gt|le|lt|wc}]
		 [exit_rate_interval <exit rate interval value>](event1 only)

	Tcl event_reqinfo Array Names: 
	event_id
	job_id
	event_type
	event_type_string
	event_pub_time
	event_pub_sec
	event_pub_msec
	event_trigger_num
	event_severity
	monitor_name 
        event_type
	ip_protocol
	source_address
	source_port
	dest_address
	dest_port
	app_name
	event[1-4]_field
	event[1-4]_value

	Applet Configuration Syntax: 
	 [ no ] event [tag <tag-val>] nf 
		 monitor-name <monitor-name value>
		 event-type <create|update|delete>
		 exit-event-type <create|update|delete>
		 event1-event4 <subevent-description>
		 [maxrun <sec.msec>]
		 [ratelimit <sec.msec>]

		 where <subevent-description> can be 
		 field <field value>
		 rate-interval <rate interval value> (event1 only)
		 entry-value <entry value>
		 entry-op {eq|ge|gt|le|lt|wc}
		 [exit-value <exit value>]
		 [exit-op {eq|ge|gt|le|lt|wc}]
		 [exit-rate-interval <exit rate interval value>](event1 only)

	Applet Built-in Environment Variables: 
	$_event_id
	$_job_id
	$_event_type
	$_event_type_string
	$_event_pub_time
        $_event_pub_sec
	$_event_pub_msec
	$_event_severity
	$_nf_monitor_name 
	$_nf_event_type
	$_nf_ip_protocol
	$_nf_source_address
	$_nf_source_port
	$_nf_dest_address
	$_nf_dest_port
	$_nf_app_name
	$_nf_event[1-4]_field
	$_nf_event[1-4]_value

Summary

It's been a long time, but in summary, it was an article that it was fun to look at event_reqinfo () in Cisco IOS. I hope that On-box programmability can complement the troublesome things that can be done with Off-box. I think you can try EEM and event_reqinfo () on Cisco 1812J and 892J. If you want to write EEM in Python, you need IOS-XE16.5 or higher, so for now you need a device such as Cat3850 or ISR4000 / ASR1000.

reference

Embedded Event Manager Configuration Guide, Cisco IOS Release 15M&T Programmability Configuration Guide, Cisco IOS XE Everest 16.6.1

Recommended Posts

Cisco IOS-XE captures changes in the routing table and posts them to external services
Memorandum (in openpyxl ① copy and paste from another book ② refer to the comparison table)
Steps to change table and column names in your Django model at the same time
Scraping the member stores of Go To EAT in Osaka Prefecture and converting them to CSV