Monday, July 1, 2019

Cisco IP SLA with EEM Email Alerts on NX-OS

IP SLAs on Cisco routers can be used for a lot of reasons. I like to use them for dual ISP configurations, together with tracking objects and an EEM script to send email alerts whenever a failover has occurred. When I tried to carry over my basic config to NX-OS, I ran into a few issues, main one being that NX-OS does not support EEM "mail server". After a bit of research I discovered that you will need call a Python script to handle the email portion.

For this example, I'm going to be using the EEM script to remove some static routes on a Nexus 7k and 9k as part of a site-to-site failover mechanism between an VPN tunnel and a MPLS link that connect the two together. The static routes override the dynamically learned routes to DC2 over the MPLS by pointing traffic to the 

1.  Enables the IP SLAs operation feature

    feature sla sender

2.  Create two IP SLAs, I'll be using two internal IPs for testing

    ip sla 10
     icmp-echo 192.168.20.1 source-ip 192.168.10.1
     frequency 10
    ip sla schedule 10 life forever start-time now
    !
    ip sla 20
     icmp-echo 192.168.21.1 source-ip 192.168.10.1
     frequency 10
    ip sla schedule 20 life forever start-time now

3. Since I want the IP SLAs to test connectivity between the two locations using the VPN tunnel as the primary link, I'll create a static route pointing them to the VPN link

    ip route 192.168.20.1 255.255.255.255 192.168.10.254 permanent name SLA_Tracking
    ip route 192.168.21.1 255.255.255.255 192.168.10.254 permanent name SLA_Tracking

4. Now that our IP SLAs are testing our connection, lets setup some tracking objects to track the status of the IP SLAs. These two tracking objects will track their respective IP SLA and transition to a down state after the IP SLA has failed for over 30 seconds. This will help prevent route flapping and general network chaos.

    track 1 ip sla 10 reachability
     delay down 30
    !
    track 2 ip sla 20 reachability
     delay down 30

5. To ensure that the VPN link has truly failed, I bundled the two individual tracking objects into one overall tracking object. This tracking object will only transition to a down state if BOTH tracking objects fail. This is handled by a Boolean OR and prevents primary/backup ISP failover if just one IP SLA is failing.

    track 10 list boolean or
     object 1
     object 2

6. Setup an EEM script that sends an email whenever Track 10 changes its state UP or DOWN

    -Nexus 7k-
    event manager applet TRACK_SLA_10_DOWN
     event track 10 state down
      action 1 event-default
      action 2 cli command source N7K_EEM_TRACK_SLA_10_DOWN.py
    !
    event manager applet TRACK_SLA_10_UP
     event track 10 state up
      action 1 event-default
      action 2 cli command source N7K_EEM_TRACK_SLA_10_UP.py


    -Nexus 9k-
    event manager applet TRACK_SLA_10_DOWN
     event track 10 state down
      action 1 event-default
      action 2 cli command source N9K_EEM_TRACK_SLA_10_DOWN.py
    !
    event manager applet TRACK_SLA_10_UP
     event track 10 state up
      action 1 event-default
      action 2 cli command source N9K_EEM_TRACK_SLA_10_UP.py


7. Create the following Python (.py) files and upload them to the switch's bootflash:, the customizable parts are in italics

    -N7K_EEM_TRACK_SLA_10_DOWN.py-
    #!/bin/env python
    import smtplib
    import cisco
    from email.mime.multipart import MIMEMultipart
    from email.mime.text import MIMEText
    # set_vrf('default')
    # me == my email address
    # you == recipient's email address
    me = "eem-alerts@company.com"
    you = "noc@company.com"
    # Create message container - the correct MIME type is multipart/alternative.
    msg = MIMEMultipart('alternative')
    msg['Subject'] = "DC Interconnect Switching to MPLS"
    msg['From'] = me
    msg['To'] = you
    # Create the body of the message (a plain-text and an HTML version).
    hostname = cli('show hostname').rstrip()
    track = cli('show track brief')
    text = hostname + " IP SLA Tracking Object 10 is DOWN, switching to MPLS for DC Interconnectivity.\n\n" + track
    # Record the MIME types of both parts - text/plain and text/html.
    part1 = MIMEText(text, 'plain')
    # Attach parts into message container.
    # According to RFC 2046, the last part of a multipart message, in this case
    # the HTML message, is best and preferred.
    msg.attach(part1)
    # Send the message via local SMTP server.
    s = smtplib.SMTP('192.168.10.125', 25)
    # sendmail function takes 3 arguments: sender's address, recipient's address
    # and message to send - here it is sent as one string.
    s.sendmail(me, you, msg.as_string())
    s.quit()

    -N7K_EEM_TRACK_SLA_10_UP.py-
    #!/bin/env python
    import smtplib
    import cisco
    from email.mime.multipart import MIMEMultipart
    from email.mime.text import MIMEText
    # set_vrf('default')
    # me == my email address
    # you == recipient's email address
    me = "eem-alerts@company.com"
    you = "noc@company.com"
    # Create message container - the correct MIME type is multipart/alternative.
    msg = MIMEMultipart('alternative')
    msg['Subject'] = "DC Interconnect Switching to VPN"
    msg['From'] = me
    msg['To'] = you
    # Create the body of the message (a plain-text and an HTML version).
    hostname = cli('show hostname').rstrip()
    track = cli('show track brief')
    text = hostname + " IP SLA Tracking Object 10 is UP, switching to VPN for DC Interconnectivity.\n\n" + track
    # Record the MIME types of both parts - text/plain and text/html.
    part1 = MIMEText(text, 'plain')
    # Attach parts into message container.
    # According to RFC 2046, the last part of a multipart message, in this case
    # the HTML message, is best and preferred.
    msg.attach(part1)
    # Send the message via local SMTP server.
    s = smtplib.SMTP('192.168.10.125', 25)
    # sendmail function takes 3 arguments: sender's address, recipient's address
    # and message to send - here it is sent as one string.
    s.sendmail(me, you, msg.as_string())
    s.quit()

    -N9K_EEM_TRACK_SLA_10_DOWN.py-
    #!/bin/env python
    import smtplib
    import cisco
    from cli import cli
    from email.mime.multipart import MIMEMultipart
    from email.mime.text import MIMEText
    # set_vrf('default')
    # me == my email address
    # you == recipient's email address
    me = "eem-alerts@company.com"
    you = "noc@company.com"
    # Create message container - the correct MIME type is multipart/alternative.
    msg = MIMEMultipart('alternative')
    msg['Subject'] = "DC Interconnect Switching to MPLS"
    msg['From'] = me
    msg['To'] = you
    # Create the body of the message (a plain-text and an HTML version).
    hostname = cli('show hostname').rstrip()
    track = cli('show track brief')
    text = hostname + " IP SLA Tracking Object 10 is DOWN, switching to MPLS for DC Interconnectivity.\n\n" + track
    # Record the MIME types of both parts - text/plain and text/html.
    part1 = MIMEText(text, 'plain')
    # Attach parts into message container.
    # According to RFC 2046, the last part of a multipart message, in this case
    # the HTML message, is best and preferred.
    msg.attach(part1)
    # Send the message via local SMTP server.
    s = smtplib.SMTP('192.168.10.125', 25)
    # sendmail function takes 3 arguments: sender's address, recipient's address
    # and message to send - here it is sent as one string.
    s.sendmail(me, you, msg.as_string())
    s.quit()

    -N9K_EEM_TRACK_SLA_10_UP.py-
    #!/bin/env python
    import smtplib
    import cisco
    from cli import cli
    from email.mime.multipart import MIMEMultipart
    from email.mime.text import MIMEText
    # set_vrf('default')
    # me == my email address
    # you == recipient's email address
    me = "eem-alerts@company.com"
    you = "noc@company.com"
    # Create message container - the correct MIME type is multipart/alternative.
    msg = MIMEMultipart('alternative')
    msg['Subject'] = "DC Interconnect Switching to VPN"
    msg['From'] = me
    msg['To'] = you
    # Create the body of the message (a plain-text and an HTML version).
    hostname = cli('show hostname').rstrip()
    track = cli('show track brief')
    text = hostname + " IP SLA Tracking Object 10 is UP, switching to VPN for DC Interconnectivity.\n\n" + track
    # Record the MIME types of both parts - text/plain and text/html.
    part1 = MIMEText(text, 'plain')
    # Attach parts into message container.
    # According to RFC 2046, the last part of a multipart message, in this case
    # the HTML message, is best and preferred.
    msg.attach(part1)
    # Send the message via local SMTP server.
    s = smtplib.SMTP('192.168.10.125', 25)
    # sendmail function takes 3 arguments: sender's address, recipient's address
    # and message to send - here it is sent as one string.
    s.sendmail(me, you, msg.as_string())
    s.quit()

Once the Python scripts have been uploaded to the bootflash: on the switch, you can test the script by running the following command from the CLI:

    source N7K_EEM_TRACK_SLA_10_DOWN.py
    source N7K_EEM_TRACK_SLA_10_UP.py

    source N9K_EEM_TRACK_SLA_10_DOWN.py
    source N9K_EEM_TRACK_SLA_10_UP.py


This should trigger the email to be sent from the respective script.

8. Finally, associate the tracking objects to the two static routes pointing DC2 traffic out the VPN link. If these are removed, routes to DC2 will be learned dynamically through a routing protocol used by the MPLS router to advertise these routes to the core switch.

    ip route 192.168.20.0 255.255.255.0 192.168.10.254 track 10
    ip route 192.168.21.0 255.255.255.0 192.168.10.254 track 10