Blogs

Scripting How-To: Pings between two GRE endpoints and toggles the GRE interface status

By Erdem posted 11-19-2015 11:27

  
This applies to SLAX version 1.0 and higher.
 

Overview

 

A combined commit and event script that pings between two GRE endpoints and toggles the GRE interface status.

 

Description

 

This script uses RPM to monitor each GRE interface. The ping probes run and check if the SA and DA pair can be pinged. If not, an error is sent to the syslog daemon. Based on the event log configuration, the toggle-interface.slax file is called. The GRE interface is set to disable. The probe runs again and if the SA+DA pair are reachable, toggle-interface.slax file is called to delete the disable parameter.

Source Code

 

GitHub Links

 

The source code below is also available from GitHub at the following locations:

 

Example Configuration

 

Here are the instructions.

1. Put the attached toggle-interface.slax file into /var/db/scripts/op 

2. Put the attached create-rpms.slax file into /var/db/scripts/commit 

3. Merge the configuration in the config-changes.txt file into the router configuration

It works like this:

1. create-rpms.slax runs every time there is a commit done. 
It looks for all GRE interfaces on the router and creates a RPM pim probe tests for each GRE interface. The GRE source and destination addresses are used as the probe source and destinations of the test

2. The change is made as a transient change, so can't be seen in the configuration unless you pipe show configuration | display commit-scripts. This is done because it lets that part of the config be dynamically created based on configuration of the GRE interfaces

3. The probe name has the name name as the GRE interface.

4. Every 15 seconds, the ping probes run and check if the SA and DA pair can be pinged. If not, an error is sent to the syslog daemon. Based on the event log configuration, the toggle-interface.slax file is called. The GRE interface is set to disable.

5. The probe runs again and if the SA+DA pair are reachable, toggle-interface.slax file is called to delete the disable parameter.

6. In every RPM probe test, even if the SA+DA pair continue to be reachable, the toggle-interface.slax script is still called to read the configuration and check if the interface is enabled or disabled. This is done because there isn't any state kept between script runs.



system {
    scripts {
        commit {
            allow-transients;
            file create-rpms.slax;
        }
        op {
            file ping-test.slax;
            file toggle-interface.slax;
        }
    }
}
event-options {
    policy disable-interface-on-ping-failure {
        events ping_test_failed;
        attributes-match {
            ping_test_failed.test-owner matches .*script.*;
        }
        then {
            event-script toggle-interface.slax {
                arguments {
                    silent 2;
                    interface "{$$.test-name}";
                    new_intf_state disable;
                }
            }
        }
    }
    policy enable-interface-on-ping-success {
        events ping_test_completed;
        attributes-match {
            ping_test_completed.test-owner matches .*script.*;
        }           
        then {      
            event-script toggle-interface.slax {
                arguments {
                    silent 2;
                    interface "{$$.test-name}";
                    new_intf_state enable;
                }   
            }      
        }           
    }               
}                  

 

SLAX Script Contents

 

version 1.0;

ns junos = "http://xml.juniper.net/junos/*/junos";
ns xnm = "http://xml.juniper.net/xnm/1.1/xnm";
ns jcs = "http://xml.juniper.net/junos/commit-scripts/1.0";

import "../import/junos.xsl";

/*
* Version: 1.0
* Author: Juniper Networks
*/

/*
* Script toggles interface up or down based on passed args.
* It's designed to work with RPM scripts and be called as part
* of a JUNOS event on RPM failure or sucess. No state is
* kept between RPM probes on interface state, so script
* needs to read config for each time a probe passes or failes
* to check current interface state and new one passed in arg list
*
*/

var $arguments = {
   <argument> {
      <name> "silent";
      <description> "Decides where the output will go, 0 -> stdout, 1 -> syslog, 2 -> none";
   }
   <argument> {
      <name> "interface";
      <description> "Interface to deactivate";
   }
   <argument> {
      <name> "new_intf_state";
      <description> "New state of the interface disable -> disable interface, 1 -> enable interface";
   }

}


match / {

   /*
    * Parse interface to get IFD and IFL
    */
   var $split = jcs:split("\\.", $interface);
   var $ifd = $split[1];
   var $unit = $split[2];

   var $rpc-config-req = <get-configuration database="committed" inherit="inherit">;
   var $configuration = jcs:invoke($rpc-config-req);
   if ($new_intf_state == "enable") {
      /*
       * If interface is already disabled and new interface state is enable,
       * populate the $xml var below to enable interface
       */
      if ($configuration/interfaces/interface[name=$ifd]/unit[name=$unit]/disable) {
         var $xml = {
            <configuration> {
               <interfaces> {
                  <interface> {
                     <name> $ifd;
                     <unit> {
                        <name> $unit;
                        <disable delete="disable">;
                     } /* end unit */
                  }  /* end <interface> */
               } /* end <interfaces> */
            } /* end <configuration> */
         } /* end var $xml */

         call doConfigChange($xml);
      }
   } else if ($new_intf_state == "disable") {
      /* 
       * If interface is enabled and new interface state is disable,
       * populate the $xml var below to disable interface
       */
      if (not($configuration/interfaces/interface[name=$ifd]/unit[name=$unit]/disable)) {
         var $xml = {
            <configuration> {
               <interfaces> {
                  <interface> {
                     <name> $ifd;
                     <unit> {
                        <name> $unit;
                        <disable>;
                     } /* end unit */
                  }  /* end <interface> */
               } /* end <interfaces> */
            } /* end <configuration> */
         } /* end var $xml */

         call doConfigChange($xml);
      } 
   } else {
      /*
       * unrecognised new interface state.
       */
       call emit-error($message = "unrecognised new intf state");
   }
}

template doConfigChange($xml) {
   /*
    * Open connection with mgd
    */
    var $con = jcs:open();

    if (not($con)) {
       call emit-error($message = "Not able to connect to local mgd");
    }
    var $config-private = <open-configuration> {
       <private>;
    }
    var $private-results = jcs:execute($con, $config-private);

    var $load-configuration = <load-configuration> {
       copy-of $xml;
    }

   var $load-results = jcs:execute($con, $load-configuration);
  /*
   * Use load-configuration template defined in junos.xsl to load and
   * commit the configuration
   */

   var $commit-configuration = <commit-configuration>;

   var $commit-results = jcs:execute($con, $commit-configuration);

   var $close-private = <close-configuration>;

   var $close-configuration-results = jcs:execute($con, $close-private);
   var $close-results = jcs:close($con);

   /*
    * Emit warnings
    */
   for-each ($commit-results//xnm:warning) {
      call emit-warn($message = message);
   }

   if ($commit-results//xnm:error) {
      for-each ($results//xnm:error) {
         call emit-error($message = message);
      }
   } else {
      call emit-success($message = "Deactivated interface");
   }

   if (not($silent)) {
      <op-script-results> {
         copy-of $results;
      }
   }
}

template emit-success($message) {

   if ($silent == 0) {
      expr jcs:output($message);
   } else if ($silent == 1) {
      expr jcs:syslog("user.info","toggle-interface.slax[Success]: ", $message);
   }
}
 
template emit-error($message) {

   if ($silent == 0) {
      expr jcs:output($message);
   } else if ($silent == 1) {
      expr jcs:syslog("user.error", "disable-interface.slax[Error]: ", $message);
  }
}

template emit-warn($message) {

   if ($silent == 0) {
      expr jcs:output($message);
   } else if ($silent == 1) {
      expr jcs:syslog("user.warning", "disable-interface.slax[Warning]: ", $message);
   }
}

XML Script Contents

<?xml version="1.0"?>
<script>
<title>toggle-interface.slax</title>
<author>chellberg</author>
<synopsis>
A combined commit and event script that pings between two GRE endpoints and toggle the GRE interface status
</synopsis>
<coe>event</coe>
<type>interfaces</type>

<description>
This script uses RPM to monitor each GRE interface. The ping probes run and check if the SA and DA pair can be pinged. If not, an error is sent to the syslog daemon. Based on the event log configuration, the toggle-interface.slax file is called. The GRE interface is set to disable. The probe runs again and if the SA+DA pair are reachable, toggle-interface.slax file is called to delete the disable parameter.
</description>

 <example>
 <title>Example</title>
 <config>example-1.conf</config>
 </example>

<xhtml:script xmlns:xhtml="http://www.w3.org/1999/xhtml"
src="../../../../../web/leaf.js" 
type="text/javascript"/>
</script>

 

Required Utility Script

 

version 1.0;

ns junos = "http://xml.juniper.net/junos/*/junos";
ns xnm = "http://xml.juniper.net/xnm/1.1/xnm";
ns jcs = "http://xml.juniper.net/junos/commit-scripts/1.0";

import "../import/junos.xsl";

version 1.0;

ns junos = "http://xml.juniper.net/junos/*/junos";
ns xnm = "http://xml.juniper.net/xnm/1.1/xnm";
ns jcs = "http://xml.juniper.net/junos/commit-scripts/1.0";

import "../import/junos.xsl";

/* Version: 1.0   
* Author: Juniper Networks
*/

/* This script parses the configuration for any GRE interfaces.
* It extracts the source and destination  addresses from the tunnels
* and creates RPM probe interfaces with a test interval of 15s.
* The owner of all probes is "script" and all test names are the
* same as the GRE interface. Changing these values should be
* avoided because they are referenced in other OP scripts
*/

match configuration {
   var $top = .;
   for-each (interfaces/interface[starts-with(name, "gr-")]) {
      var $ifd = name;
      for-each (unit) {
         var $unit = name;
         var $t_source_ip = tunnel/source;
         var $t_dest_ip = tunnel/destination;
         if ($t_source_ip && $t_dest_ip) {
            <transient-change> {
               <services> {
                  <rpm> {
                     <probe> {
                        <name> "script";
                        <test>  {
                           <name> $ifd _ "." _ $unit;
                           <probe-type>
                              "icmp-ping";
                              <target> {
                                 <address> $t_dest_ip;
                               }
                               <test-interval> 15;
                               <source-address> $t_source_ip;
                        } /* end test */
                     } /* end probe type */
                  } /* end rpm */
               } /* end services */
            }  /* end transient change */
         } /* ensure SA and DA are present */
      } /* end for-each unit loop */
   } /* end for-each IFD loop */
} /* match configuration */

#commitscript
#SLAXscript
#How-To
#Slax