Showing posts with label automation. Show all posts
Showing posts with label automation. Show all posts

Sunday, July 24, 2016

A Script to Control Quagga BGPD Daemon, Implementing Anycast DNS Server

One way to implement Anycast DNS Server throughout the internet is to use BGP protocol and to announce your DNS IP block from multiple locations so that redundancy and low-latency targets are achieved. There are many ways to design such a infrastructure; you could use hardware routers, you could use load-Balancers in front of your servers etc. I would like to share a script here for the sceanario where Quagga BGPD daemon is used as a software router either on the DNS server or any other server purely as a router:
Using python dnspython library, it is easy to perform checks of a DNS servers ability to respond to different type of DNS queries. Based on the DNS servers answer you could stop, start Quagga BGPD daemon.
You could check the examples on http://www.dnspython.org/examples.html to understand how dnspython is used. A simple A type request is as below:
test_domain = "www.test.com"
server_to_test = ["127.0.0.1"] 

answers = anycast_server1.query(test_domain, "A")
anycast_server1 = dns.resolver.Resolver()
anycast_server1.timeout=1.0
anycast_server1.lifetime=1.0
anycast_server1.nameservers = server_to_test
for data in answers: 
 print data
If DNS and BGPD daemon is on the same server, script should forward the query to local server: 127.0.0.1. Below is the script i wrote to check local server with an A type DNS request, and based on the answer act either to start or stop the BGPD daemon. You could download the latest version of the script from https://github.com/ercintorun/dns-check-quagga-act.
# -*- coding: utf-8 -*-
import dns.resolver, psutil, commands, time, logging, datetime

###VARIABLES 
script_run_time = 60 
test_domain = "www.test.com"
server_to_test = ["127.0.0.1"] 

#############
#############
#############
###logging file folder and logging level config
logging.basicConfig(filename='/var/log/dnsscript.log', filemode='a', level=logging.INFO,
                    format='%(asctime)s [%(name)s] %(levelname)s (%(threadName)-10s): %(message)s')

###define dns servers, parameters
anycast_server1 = dns.resolver.Resolver()
anycast_server1.timeout=1.0
anycast_server1.lifetime=1.0
anycast_server1.nameservers = server_to_test 
   
###time to run the script
starttime = time.time()
timeout = time.time()+ script_run_time

###kill process function 
def kill_process(PROCNAME):
 for proc in psutil.process_iter():
  if proc.name() == PROCNAME:
   proc.kill()

###start a loop with an amount of script_run_time value

while True:
 time.sleep(0.8)
 if time.time()> timeout:
  break
 else:
###get all daemon names into list
  daemon_list=[]
  for proc in psutil.process_iter():
   daemon_list.append(proc.name())
###start the control
  if "bgpd" not in daemon_list: 
   try:
    answers = anycast_server1.query(test_domain, "A")
    commands.getoutput ("/etc/init.d/bgpd restart")
    logging.warning("DNS successful, BGP daemon is down, bgpd restarted")
    time.sleep(2) #give bgp 2 second to get up again
   except dns.resolver.NXDOMAIN:
    logging.warning("DNS exception: No such domain, BGP daemon is already down, no change done")
   except dns.resolver.Timeout:
    logging.warning("DNS exception: Timed out while resolving, BGP daemon is already down, no change done ") 
   except dns.exception.DNSException:
    logging.warning("DNS exception: Unhandled exception, BGP daemon is already down, no change done") 
  else:
   try:
    answers = anycast_server1.query(test_domain, "A")
    for data in answers: 
     resolved = data
    logging.info("DNS successful, nothing has been changed, last resolved ip is: "+str(data))
   except dns.resolver.NXDOMAIN:
    kill_process("bgpd")
    logging.warning("DNS exception: No such domain, BGP daemon terminated")
   except dns.resolver.Timeout:
    kill_process("bgpd")
    logging.warning("DNS exception: Timed out while resolving, BGP daemon terminated")
   except dns.exception.DNSException:
    kill_process("bgpd")
    logging.warning("DNS exception: Unhandled exception, BGP daemon terminated")
If you examine the script you could see that it works for 60 seconds. If you add this script to crontab with 1 minute interval it will check DNS server 60-70 times per minute (on local server) continiously. I've used psutil library to fetch active daemons list on Linux to check whether BGPD daemon is active or not. Script,
  • If DNS successful and BGPD is active does nothing 
  • If DNS successful and BGPD is passive, restarts BGPD using "commands" libary 
  • IF DNS unsuccessful and BGPD is active, stops BGPD 
  • IF DNS unsuccessful and BGPD is passive, does nothing
Also you could see that the script logs its actions for each query to "/var/log/dnsscript.log". If you would like to decrease log size, you could change "level=logging.INFO" to "level=logging.WARNING" to not to log no-action-taken checks.

Monday, January 26, 2015

Validating BGP Announcements by Automating Filter Generation with Python: Part2


At first part of the post, i wrote that some tools use “whois”  client to query RIR databases. So it is possible to use a “whois cli tool” to gather the necessary data of your interest.

Whois

You can find whois flags for RIPE on http://www.ripe.net/data-tools/support/documentation/queries-ref-card  to elaborate the whois queries.  Lets pull routes of an AS34984 in Linux command Line:

root@test:~# whois -h whois.ripe.net -i or as34984 | grep route:
route:          151.250.0.0/16
…output omitted

In order to get route data as a variable, to play with, you still need to need to parse the related part of the data from  the output. A better approach, which i generally prefer , is to use a native library, so that your script won’t depend on  a tool,  as a result it would be executable both  on Windows, or or Linux/Mac.  For a native client library check  https://pypi.python.org/pypi/WhoisClient

Restful Web Services API


Even with a native library, you will still need to parse the output data. There is a better way, which is getting the data in a format like XML, YAML, JSON etc  so that it is easier to get related parts systematically.  Fortunately RIPE/ARIN has a RESTFul Web Services API  which is a REST interface to their WhoIS Database which are invoked via http requests. Here are the documentation links:

For ARIN documentation check: 


Querying RIPE  Web API and Processing  XML Input With Python


By looking at the documentation, it can be seen that for AS5400, which is BritishTelecom, url

http://rest.db.ripe.net/search.xml?query-string=as5400&inverse-attribute=origin  could be used to get all routes that has an origin of AS5400. The ony thing to get prefixes for another AS is to change the “as5400” value in the url itelf. If you open the url in your web browser you will see an xml output like below: 



So if we could open this url in python, and get the output, then parse it as xml, everything will be ok. To do so, lets examine the code i wrote, part by part:

Examining the code: 

At first part of the code we need to import libraries, which are: urllib, urllib2 and xml.etree.ElementTree   (you may also use lxml instead of xml.etree.ElementTree)


import urllib,urllib2
try:
    import xml.etree.cElementTree as ET
except ImportError:
    import xml.etree.ElementTree as ET

We determined how to create a URL to pull prefixes that is originated from an AS. Creating an AS variable is logical so that in further usages you can change the AS number or get the value as an input.

as_to_pull_prefixes = "AS5400"
url = "http://rest.db.ripe.net/search.xml?query-string=%s&inverse-attribute=origin" % as_to_pull_prefixes

As url is ready we need to open the url and assign the output to a string variable:

fp = urllib2.urlopen (url)
response = fp.read()

Then we need to parse XML from string into an element:

tree = ET.fromstring(response)
fp.close()

Last part is to get the prefix values from the xml output, which we turned into an element by using xml.etree.ElementTree. First we need to determine the hierarchy so that we can write a path argument. Having a look at the xml output, the hierarchy should be:

  • inside <objects> 
  • inside <object> that has a type value of  "route" (for ipv4 prefixes) 
  • inside <primary-key>
  • every <attribute> which has a name value of "route" 

To write such a path argument, you may wanted to have a look at https://docs.python.org/2/library/xml.etree.elementtree.html#elementtree-xpath. Here is the code:

interested =  tree.findall("./objects/object[@type='route']/primary-key/attribute[@name='route']")

Our job is still not done yet, cause inside the tree we created, which is "interested" variable, we need to take the values of "value": Here is the last piece of the code:

for child in interested:
    print child.get('value')

Trying It Out 

Here is the output when i executed the script:


To download the script, click here, or copy-paste the full code below:

import urllib,urllib2
try:
    import xml.etree.cElementTree as ET
except ImportError:
    import xml.etree.ElementTree as ET
###
###Variables which changes per request
as_to_pull_prefixes = "AS5400"
url = "http://rest.db.ripe.net/search.xml?query-string=%s&inverse-attribute=origin" % as_to_pull_prefixes
###
###Pull Info From IRR(RIPE)and assign it to a variable as string
fp = urllib2.urlopen (url)
response = fp.read()
###
###parse xml from  from string into an element 
tree = ET.fromstring(response)
fp.close()
###
###get interested data from element 
interested =  tree.findall("./objects/object[@type='route']/primary-key/attribute[@name='route']")
for child in interested:
    print child.get('value')
###############
##for more info on how to select interested data from xml
##https://docs.python.org/2/library/xml.etree.elementtree.html#elementtree-xpath
###############

Wednesday, January 21, 2015

Validating BGP Announcements by Automating Filter Generation with Python: Part1


Securing Internet Routing infrastructure has been a hot topic for a long time as hijack events occurs again and again either mistakenly or on purpose. Operators use different  techniques to validate BGP announcements. I will try to explain creating an ip/as prefix-list  by pulling information from a RIR database, in our case RIPE, using python. But first, lets take a look at other methods in Part1: 

RPKI

Probabily  the best  way to prevent BGP/IP hijacks is to use RPKI infrastructure, , which is a  PKI framework (trust anchor mechanism). Unfortunately the method needs legitamate AS/IP owners to register their sources. needless to say that no one is registering their sources, as a result the method is still not applicable. By 21.01.2015 the current validation states for all IPv4 prefixes is only %5.59, You can check validation states from http://rpki.surfnet.nl/ipcomp.html.


RPSL 

For RPKI is not applicable, we need another way to validate the BGP announcements, where IRR RPSL database entries comes into play. RPSL is a language commonly used by ISPs to describe their routing policies. ISP's could store their routing policies either on their own server or on open public whois databases including RIPE, RADB, APNIC etc.  RIPE (RIR for Europe) transformed its database to RPSL format in 2001.  Example entries in RIPE database are:

as-set:          AS-FUNET
descr:           Macro with all ASes exported by FUNET
members:         AS1741
members:         AS1739
members:         AS565
members:         AS15496
members:         AS30754
members:         AS39098
members:         AS39857
members:         AS39662
tech-c:          FH437-RIPE
admin-c:         FA1183-RIPE
mnt-by:          AS1741-MNT
source:          RIPE # Filtered
As-set object which was created by FUNET, and includes their members

aut-num:         AS5400
as-name:         BT
descr:           British Telecommunications plc
org:             ORG-CNS3-RIPE
import:          from AS1741 action pref=20; accept AS-FUNET
British Telecoms RPSL entry which accepts the informations in AS-FUNET  as-set entry.
Route−set: AS4763:RS−ROUTES:AS681
descr:     prefix filter for AS681
members:   130.216.0.0/16, 130.217.0.0/16,
132.181.0.0/16, 138.75.0.0/16, 139.80.0.0/16,
140.200.0.0/16, 156.62.0.0/16, 192.73.21.0/24
tech−c:    JA39
mnt−by:    MAINT−TELSTRA−NZ
changed:   jabley@patho.gen.nz 19991118
source:    RADB
Route-set sample

Using these databases it is possible to get infos for AS-SETs, AS entries, route objects etc. We know that RPKI validation states are really low, so what about the RPSL entries?  Unfortunately Inter Route Registries are not that accurate also. BGPmon analysis which is done at 2010, shows that only %46 of the global routing table entries has a  matching route-object. 


If  you wanted to update a BGP import policy automatically then you should force the facing AS owner to update their IRR entries (route-objects, as-sets…). Some providers area also very strict in the IRR usage , so  why shouldn’t you ? 

Getting  Info From RIR Databases

Tools

There are many great tools that are ready for use which does the whois queries behind, which some of are: 
  • IRR Toolset
  • IRR Power Tools
  • NETİ:IRR
  • Bgpq3
  • Md
  • P2BGPTool
Lets take a loot at one of these tools, namely  Bgpq3, before writing our own python script: 

Bgpq3 Installation and Usage

Download compressed bgpq3 file from http://snar.spb.ru/prog/bgpq3/ , extract and install it (below steps for Ubuntu):

admin@ubuntu:~# wget http://snar.spb.ru/prog/bgpq3/bgpq3-0.1.21.tgz 
admin@ubuntu:~#  tar -xvzf bgpq3-0.1.21.tgz 
admin@ubuntu:~#  cd bgpq3-0.1.21/
admin@ubuntu:~/bgpq3-0.1.21# ./configure
admin@ubuntu:~/bgpq3-0.1.21# sudo make && sudo make install


After installing we could create a prefix-list from command line using the tool. Below is a sample for creating Junos prefix list for as-set: AS-FUNET


You may check bgpq3 man page for  more parameters (cisco, juniper, as-path, ipv4, ipv6)

Definitely command line tools are useful  but if you wanted get data from command line output in  python script it means that you need to parse the data.  There are better ways which you may continue reading in Part 2. 


Monday, January 5, 2015

Controlling Junos with Python & PyEZ, Part 2: Getting Info, RPC on Demand


At first part of this post, i've tried to explain how to install PyEZ module on Windows, and how to get the device name. Surely the power of python, PyEZ framework and XML API of Junos is not limited with that, so let's get into detail.

RPC on Demand


For any command you can execute on Junos CLI, you can invoke it's RPC equivalent. To determine an RPC equivalent of a command, you use the | display xml rpc mechanism as illustrated:

etorun@as34984-lg> show route 1.0.0.0 | display xml rpc 
<rpc-reply xmlns:junos="http://xml.juniper.net/junos/12.3R8/junos">
    <rpc>
        <get-route-information>
                <destination>1.0.0.0</destination>
        </get-route-information>
         </rpc>
        <cli>
            <banner></banner>
        </cli>
    </rpc-reply

The content between the "<rpc>...</rpc>" is the XML RPC command, in this case "get_route_information". Be careful you need to swap dashes (-) to underscores(_) 

show_route_output = testdevice.rpc.get_route_information(destination="1.0.0.0")

If you wanted search active route for 1.0.0.0 in inet.0 table, then you need to add parameters to the same command, but the command you need to execute to get the rpc equivalent does not change.
etorun@as34984-lg> show route 1.0.0.0 table inet.0 active-path | display xml rpc 
<rpc-reply xmlns:junos="http://xml.juniper.net/junos/12.3R8/junos">
    <rpc>
        <get-route-information>
                <destination>1.0.0.0</destination>
                <table>inet.0</table>
                <active-path/>
        </get-route-information>
         </rpc>
        <cli>
            <banner></banner>
        </cli>
    </rpc-reply>

However you need to add these extra parameters to python equivalent

show_route_output = testdevice.rpc.get_route_information(destination="1.0.0.0", table="inet.0", active_path=True) 

The "active-path" parameter does not take any value, so just assign it to True

To determine the  RPC response use the | display xml mechanism

etorun@as34984-lg> show route 1.0.0.0 table inet.0 active-path | display xml        
<rpc-reply xmlns:junos="http://xml.juniper.net/junos/12.3R8/junos">
    <route-information xmlns="http://xml.juniper.net/junos/12.3R8/junos-routing">
        <route-table>
            <table-name>inet.0</table-name>
            <destination-count>584861</destination-count>
            <total-route-count>1169721</total-route-count>
            <active-route-count>577542</active-route-count>
            <holddown-route-count>2</holddown-route-count>
            <hidden-route-count>14636</hidden-route-count>
            <rt junos:style="brief">
                <rt-destination>1.0.0.0/24</rt-destination>
                <rt-entry>
                    <active-tag>*</active-tag>
                    <current-active/>
                    <last-active/>
                    <protocol-name>BGP</protocol-name>
                    <preference>170</preference>
                    <age junos:seconds="791658">1w2d 03:54:18</age>
                    <med>0</med>
                    <local-preference>190</local-preference>
                    <learned-from>92.44.0.248</learned-from>
                    <as-path>15169 I
                    </as-path>
                    <validation-state>unverified</validation-state>
                    <nh>
                        <selected-next-hop/>
                        <to>213.14.125.85</to>
                        <via>ge-1/0/3.100</via>
                    </nh>
                </rt-entry>
            </rt>
        </route-table>
    </route-information>
    <cli>
        <banner></banner>
    </cli>
</rpc-reply>

Trying It Out

Imagine you need to take the active as-path value of 1.0.0.0/24. Then the python script should be:

from jnpr.junos import Device
from jnpr.junos import Device
router = Device(user='admin', host='10.0.0.1', password='logmein')
router.open()
# invoke the RPC equivalent to "show route 1.0.0.0 table inet.0 active-path"
show_route_output = router.rpc.get_route_information(destination="1.0.0.0", table="inet.0", active_path=True) 
# use XPath expressions to extract the data from the Junos/XML response# the :show_route_output: variable is an lxml Element object
print "the as-path value is: %s" % show_route_output.findtext('route-table/rt/rt-entry/as-path')
router.close()

Here is the output when i run the .py file from windows powershell:


If you wanted to have a look, I did benefit from RPC On Demand page of Juniper Tech Wiki when writing this post.

Thursday, January 1, 2015

Controlling Junos with Python & PyEZ, Part 1: Installation


You can manage or automate routers by using python. Having an XML API is the benefit of using a Juniper router as it is easier than other routers to manage it with python. Moreover Juniper wrote a Python library, which is actually a "micro-framework", called PyEZ and it makes easier for non-proggramers to manage a Junos OS router. 

Features: 

I am taking the exact explanation from the "juniper tech wiki":

Junos PyEZ is designed to provide the same capabilities as a user would have on the Junos OS CLI, but in an environment built for automation tasks. These capabilities include, but are not limited to:


  • Provide "facts" about the device such as software version, serial number, and so on
  • Retrieve the "operational" or "run-state" information (think "show" commands) using Tables/Views
  • Retrieve the configuration using Tables/Views
  • Make unstructured configuration changes with "snippets" and "templates"
  • Make structured configuration changes with modeled abstractions
  • Provide common utilities for tasks such as secure copy of files and software updates


Installation Python, PIP and PyEZ (Windows) 


To use PyEz, you must be using a python version 2.7 or later. I am not getting  into details of installing Python on to Windows as it is as easy as installing an .exe file. You may download the latest build from python.org/downloads. When installing python select "pip" and "setuptools" to get installed. Also to register the version which you are installing select "" as seen in the capture below:





You may need to reload Windows inorder for python to work. If PIP is not installed and you do not wanted to install python from scratch then you have the option to only install PIP. (PIP is a package itself which install packages.). Download get-pip.py,  save it as a .py file then, run it from the command prompt "python get-pip.py".

Behind the scenes PyEZ uses lxml, ncclient, paramiko, pycrypto, jinja2, PyYAML, netaddr, ecdsa and markupsafe python packages.  For ease of installation, we will use PyPI, which is a Python package repository. But before  you need to check if  Microsoft Visual C++ Compiler for Python 2.7 is installed on your PC and if not install it before executing the command "pip install junos-eznc" command in powershell/cmd.



Configuring Router 


For PyEz to work, ssh and netconf must be enabled on router which means you must be using "domestic" version of Junos. Basic configuration to enable ssh and netconf: 


Trial 

If everything is right, then you should be able to connect to a router using PyEZ and get some outputs. Here is a basic script which gets version info from a router:

from jnpr.junos import Device

dev = Device(host='10.33.198.221',user='admin',password='password')
dev.open()

print dev.facts['version']
print dev.cli("show configuration system services)

dev.close()

Here is the output:



For more you should visit Junipers PyEZ TechWiki page. You may also have a look at Juniper Python TeckWiki page. 

Happy New Year!!


Tuesday, December 9, 2014

Upgrading IOS-XR Using Python: Cisco AUT

If you have ever upgraded an IOS-XR you definitely are aware that it is a long process which includes so much control steps. Cisco has written a python tool, known as Cisco Automatic  Upgrade Tool, that upgrades an IOS-XR device with pre and post checks. Cisco CPOC video explains nearly everything about this tool which you should watch before start reading this post. Probably the video recorded for an older version of AUT, you will realize that there are little differences between the post and the video. 

Installing The Tool & Prerequisites


The tool works on Linux and Mac cause it uses pexpect module of python so it is not possible to use it on Windows Operating System. Find yourself a Linux/MAC system that is allowed to reach your routers. 


Python Version Check

The script itself does not give any warning about python version if it is old, so be sure that your python version is up to date. I successfully used the tool with python version 2.7.6. I also tried  with version 2.6  and it did not work.

You can check your python version on a Centos system using the below command: 

python -v

Pexpect Module Install

Pexpect python module must be installed on the operating system. Here is the way to install pexpect module on a Centos system:

wget http://pexpect.sourceforge.net/pexpect-2.3.tar.gz
tar xzf pexpect-2.3.tar.gz
cd pexpect-2.3
python ./setup.py install

Cisco AUT Install

You should first download the tool itself. Go the the sourceforge website 
http://sourceforge.net/p/acceleratedupgrade/code/ci/master/tree/ and get the gitclone code. Then: 

python install -d /destination_folder

Cisco AUT


If you have installed all the necessary tools and Cisco AUT , you might start using it by checking which options the script gives, by executing the command below: 

python accelerated_upgrade -h


Below is sample command you might use to upgrade a IOS-XR device.

python accelerated_upgrade --url ssh://admin:yourpassword@11.11.11.11:22  -f packagelist.txt -r tftp://10.0.0.1/ -c  extracommands.txt

As the name reveals itself, packagelist.txt includes the packages we wanted to install on the device. A sample txt file must be like below (based on your needs): 

asr9k-k9sec-px.pie-5.1.3
asr9k-fpd-px.pie-5.1.3
asr9k-mcast-px.pie-5.1.3
asr9k-mpls-px.pie-5.1.3
asr9k-mini-px.pie-5.1.3
asr9k-mgbl-px.pie-5.1.3
asr9k-doc-px.pie-5.1.3


If you wanted to upgrade more than 1 device, you can also create a device.txt file (name it as you want) and the change the command like below: 

python accelerated_upgrade --urls devices.txt -f packagelist.txt -r tftp://10.0.0.1/ -c extracommands.txt

As you already noticed, in the command we gave tftp address where router is going to download the packages from. You may also give ftp link also (including username and password)

Extracommands file, as the name reveals itself again, includes the commands that you might want to execute before upgrade, so that you can compare after the upgrade. Here is a sample file output:

show redundancy summary
show platform
admin show ds
admin show hw-module fpd location all

Pre-checks 

Pre-upgrade steps of the tool are:

  • Node Status Check 
  • Ping Check
  • Disck Space Check
  • Active Package Check 
  • Inactive Package Check 
  • Commited Package Check
  • Redundancy Nde Check 
  • Configuration Backup 
  • Backup CLI Snapshots 
  • Turboboot Check 
You can find detailed information about the pre-check steps in the Cisco CPOC video.

Sample Upgrade 


I upgraded an ASR9010 IOS-XRdevice from 4.2.3 to 5.1.3  using AUT with parameters below:

python accelerated_upgrade --url ssh://admin:yourpassword@11.11.11.11:22  -f packagelist.txt -r tftp://10.0.0.1/ -c extracommands.txt

Unfortunately telnet did not work for me, may be it was related with my router config, but ssh worked. Here are the outputs: 


As you might see in the output, post-check warned me about failed startup config (which is due to changed syntax of MoFRR), and difference between total ospf neighbor count of pre and post upgrade situations. 

FPD Upgrade

In my attempt, script did not do an automatic fpd upgrade, so you need to check and do a fpd upgrade manually. 

Log Files 

Script creates to folders, named "aulogs" and "au_out" in the executed folder, to store logs of the operation. You can see the structure in the screenshot below. 



In aulogs folder, the scripts session to the router is stored. In au_out folder the extracommands (if used) and predetermined commands output, which are taken before upgrade procedure, are stored. 

Thursday, December 4, 2014

Automating Telnet to Cisco/Juniper/Huawei Routers Using Python

When choosing a method for router automation, netconf, ssh, snmp etc are more reliable and stable than telnet but still you may it. I needed a telnet script/library which can execute multiple commands on a router and assign every output to a different variable so that i could. parse related areas.

Telnetlib is a native library in python and it can be used in the same way on all operating systems that support python. You should check the details (like how it is used basically) of the library from https://docs.python.org/2/library/telnetlib.html


In my opinion, a good telnet script for router automatization must support:

  • executing multiple commands in order
  • executing long commands (longer than terminal width)
  • assigning every result to a different variable or print it.
  • JUNOS, IOS-XR, IOS, VRP

SCRIPT

You can download the script files from here if you don't want to read the rest of the post.

We will use class logic in this script. Classes are very useful if you repeat the same tasks again and again. With a class it is possible to create many objects. For example  every telnet session to a different router could be an object of the same class, and every object could have different values. If you don't know much about classes in python, i would suggest you to take a lookout at page Jeff Knupp's page: http://www.jeffknupp.com/blog/2014/06/18/improve-your-python-python-classes-and-object-oriented-programming/

Variables

Login and Password Phrases

When telnetting to a router, different vendors has different login and password phrases. It is possible to wait for these phrases and then send the username or password. The variables  that we will use in the script  to identify these phrases  are:

login_phrase = ["sername:", "ogin:"]
password_phrase = ["assword:"]

You might realize that the first letters are missing, this is done to avoid case sensitivity.

Terminal Length

When you execute a command, a  router will return a limited output cause it has a preconfigured terminal length and if you wanted to see the continuning parts of the result you need to press "enter" key. When using a script we want the router to return an infinite output. To do so every vendor has a different command which we can assign to different variables:

ios_cli_length = " terminal length 0"
junos_cli_length = " set cli screen-length 0"
iosxr_cli_length = " terminal length 0"
vrp_cli_length = " screen-length 0 temporary"

Various Variables

line_break = "\r\n" #need to be send after a command to get it executed like sending "enter" key stroke
timeout_for_reply = 1 #1 second time variable
exclude_start = ("#", "$")

TELNET Class

Init Definition

We need a init definition as we are using a class, so that every object that is created using this class could be identified.

class TELNET(object):
 """connect to hosts"""
 def __init__(self):
  self.connections = []
  self.device_names = []
  self.result_dictionary = {}

You may notice that inside the _init_ definition there is two list and one dictionary variable. We have created these empty  lists and dictionary to pass values between definitions inside the class. You will have a better understanding reading through the other definitions.

Connect Definition

This definition is the longest one in TELNET class. Inside the definition, you may notice that there is a router variable and it is a list. Telnetlib needs router hostname and port number for the connection to get established. And you will also need to enter username and password after getting connected, if it is needed. In "router" the positions of the elements must be like below. Based on your needs, you may enter this variable as a static element in the script file itself or you may make the script read it from a txt/xml file.

router = [ip,software_type,connection_type,port_number,username,password,first_command]
##e.g.
router = [1.2.3.4,ios,telnet,23,admin,password,show ip bgp 8.8.8.8]

 def connect (self, router):
  try:
   connection = telnetlib.Telnet(router[0], router[3])
  except IOError:
   print "IOError, could not open a connection to %s" % router[0]
   return
  """send username"""
  try:
   if router[4] != "":
    connection.expect(login_phrase)
    connection.write(router[4] + line_break)
  except IOError:
   #Send failed
   print "sending username %s failed" % router[4]
   return
  """send password"""
  try:
   if router[5] != "":
    connection.expect(password_phrase)
    connection.write(router[5] + line_break)
  except IOError:
   #Send failed
   print "sending username %s failed" % router[5]
   return

The upper part of the "connect" definition is for getting connected to the router and sending  username and password. The lower part, which you can see below, is for setting the terminal length based on router type and getting the device_name as a variable.

Why we are trying to take the device name as a variable ? Whenever the connection type is telnet, after executing a command you must be sure that you get the whole output. So if there is delay how long should you wait for the output, or how many bytes you should get ? The best way that i could find is to wait for the router_name to be send through the connection. This way even the execution of the command takes long, or there is a delay in network you can be sure that you get the whole output.


  """set terminal length and take device name"""
  try :
   if router[1] == "ios-xr":
    time.sleep(timeout_for_reply)
    connection.write(iosxr_cli_length + line_break)
    device_name = connection.read_until(iosxr_cli_length).split()[-len(iosxr_cli_length.split(' '))]
   elif router[1] == "junos":
    time.sleep(timeout_for_reply)
    connection.write(junos_cli_length + line_break)
    device_name = connection.read_until(">").split()[-1]
   elif router[1] == "vrp":
    time.sleep(timeout_for_reply)
    connection.write(vrp_cli_length + line_break)
    device_name = connection.read_until(vrp_cli_length).split()[-len(vrp_cli_length.split(' '))]
   elif router[1] == "ios":
    time.sleep(timeout_for_reply)
    connection.write(ios_cli_length + line_break)
    device_name = connection.read_until(ios_cli_length).split()[-len(ios_cli_length.split(' '))]
   else:
    print router[1] + " is not an appropriate connection type"
    sys.exit(1)
  except IOError:
   #Send failed
   print "setting terminal length failed"
   return
  self.connections.append(connection)
  self.device_names.append(device_name)

If you examine inside the if confidition for  every router type we wait "timeout_for_reply" second, then send the terminal length command (line break is for "enter" key stroke). And for last we catch the device name by splitting the received data and getting the last value. You may add a "print device_name" statement to see the device name.


At the end of the "connect definition" we created a connections and device_names dictionaries, so that we can pass the active connection to the next definitions

Execute Definition

At this definition the aim is to execute commands, and if needed multiple commands in order. As we get the connection info as a variable from "connect" definition, the definiton is:

 def execute (self, router):
  for conn in self.connections:
   for device in self.device_names:
    conn.write(line_break) #if executing more than one, line break will push device name again and next read_until wont get stuck
    conn.read_until(device)
    conn.write(router[6]+line_break+line_break)
    time.sleep(1)
    catch_end_of_output = [device+" "+line_break, device+line_break]
    self.result_dictionary[router[0]] = conn.expect(catch_end_of_output)[-1]

You may notice that catch_end_of_output is a dictionary and it contains device_name+ line_break elements with and without an empty space character. If you are execu_ing a long command, which is longer than the terminal width, the first part of the command  will be resent back to you from the router including the host name.(you may check this pcap file).It is not enough to wait only for the device name to get the output, as you will get it before executing the command. To solve this problem we can send another line_break after command execution (that is why you see 2 line+breaks for conn.write) and catch device_name+line_break in output.
Why our list has 2 elements with and without space character. This is because some routers return device name after line break with a space character while others send it without a space in order which i realised in various capture files. Close Definition As we get connected to the router with "connect" definition, we also need to close that session. For that purpose the definition is:

 def close(self):
  for conn in self.connections:
   conn.close

Reading Router Info From a Text File


After close definition, the TELNET class is ready to be used. Now we need the script to read connection information from a txt file.

## open routers.txt, clear comments, get all data as "lines" variable
f = open('routers.txt', "r")
lines =  [n for n in f.readlines() if not n.startswith(exclude_start)] #read the lines that does not start with the characters defined in exclude_estart variable
f.close()
total_connections = len(lines) #determine number of routers/connections by counting the lines

#split "lines" variable and turn every line into new variables named as routerx which contains connection infos
for x in range(0, total_connections):
    globals()['router%s' % x] = (lines[x]).split(',')

You may also try getting the data from an xml file. Getting data from an xml file is easier than getting it from a text  cause you wont' need parsing the text file. You may have a look at xml.etree.elementtree python library from https://docs.python.org/2/library/xml.etree.elementtree.html

Connecting, Executing and Printing

It is time to connect and execute the commands as all infos and class are ready. We will create a loop and for every router create an instance of the class (object) so that we session is created and command is executed.

##connect, execute command and print 
##connect, execute command and print  
for x in range(0, total_connections):
 telnet = TELNET()
 telnet.connect(globals()["router"+str(x)])
 telnet.execute(globals()["router"+str(x)])
 result= telnet.result_dictionary[(globals()["router"+str(x)][0])].split("\r\n")
 for line in result:
  print line
 telnet.close

Trial


As everything is ready lets try adding two globally available looking glass and try getting BGP info for 8.8.8.8. The routers.txt file must be in the same folder with the script. Here is the text info:

#Comments here
##
##
route-server.eu.gblx.net,ios,telnet,23,,,show ip bgp 8.8.8.8
route-server.ip.att.net,junos,telnet,23,rviews,rviews,show route protocol bgp 8.8.8.8

Here is the result when we call the script in Windows PowerShell


Monday, November 24, 2014

Cisco ASR9K L2VPN Mac Usage Script - TCL

If you manage a ASR9K router you must be sure that the mac address usage of the linecards do not exceed their capacity, otherwise based on the configuration traffic either going to be dropped or flooded. Trident based old line cards has a capacity of 128k Mac adress, and this limit could be a problem if you terminate too many l2 circuits.In such a scenerio, it is possible to write a TCL to create a syslog entry whenever the usage reaches a certain configurable amount. To do so, i wrote a TCL script, here are the details:

Command to Check Mac Usage
 You can check any of the linecards  resource like below:

RP/0/RSP0/CPU0:xxxx: show l2vpn forwarding resource hardware ingress detail location 0/0/CPU0 | include TOTAL MAC
Mon Nov 33 20:49:01.870 xxx
    TOTAL MAC     -/-         -/-         -/-         -/-         -/-         -/-      4325/2097152
    TOTAL MAC     -/-         -/-         -/-         -/-         -/-         -/-      4325/2097152
    TOTAL MAC     -/-         -/-         -/-         -/-         -/-         -/-      4325/2097152
    TOTAL MAC     -/-         -/-         -/-         -/-         -/-         -/-      4323/2097152
    TOTAL MAC     -/-         -/-         -/-         -/-         -/-         -/-      4323/2097152
    TOTAL MAC     -/-         -/-         -/-         -/-         -/-         -/-      4325/2097152
    TOTAL MAC     -/-         -/-         -/-         -/-         -/-         -/-      4323/2097152
    TOTAL MAC     -/-         -/-         -/-         -/-         -/-         -/-      4323/2097152


Prerequisites
For any script to work, router must have a hostname. Unless it is a lab router, you probably wouldn’t have such a problem, but keep in mind.

Determine a folder  in router  to copy the script, and make that directory.
#cd disk0:
#pwd
Mon Nov 21 15:37:24.393 UTC
disk0:
#mkdir tcl_scripts
Mon Nov 21 15:37:30.098 UTC
Create directory filename [tcl_scripts?
Created dir disk0:/ tcl_scripts


Point your user scripts folder to event manager.
(config)#event manager directory user policy disk0:/tcl_scripts

Configure AAA, so that event manager use locally configured users.
(config)#aaa authorization eventmanager default local

Under “admin” hierarchy create a user to execute scripts.
(config)#admin
(config)#username eem
(config-un)#group root-system
(config-un)#group cisco-support

The Script File:
I am a beginner if the case is writing a TCL script. In the script below i did not use loop/while cyles, as a result some part of the code repeats itself for every line card, moreover anyone experienced in regexps could shorten the code.

If you are not going to do any change, copy the script below into a text file and save it as a file. In my case i named it as “”.

  • Script should work on ASR9010 and ASR9006.
  • Script checks only the cards that are in “IOS-XR RUN” state. So there should not be a problem if linecard slots are empty or  the linecards are off due to any reason. 
  • Do not use tcl operators supported by TCL versions newer than 8.3.4


::cisco::eem::event_register_timer cron name l2maccheck cron_entry $l2maccheck maxrun_sec 240
namespace import ::cisco::eem::*
namespace import ::cisco::lib::*
################################################################################################
#                                                                                              #
#  Revision #          :1.2                                                                    #
#  Last Updated        :01.10.2014                                                             #  
#  Author/Contributor  :Ercin TORUN                                                            #
#                                                                                              #
#  Description         :Script checks if any of the linecards reaches its MAC capacity         #
#                       and creates a syslog message.                                          #
#                                                                                              #
#  Requirements        :                                                                       #
#                                                                                              #                   
#                                                                                              #
#                       Example: event manager environment l2vpnmac_threshold_percent 85       #   
#                                event manager environment l2maccheck 0 * * * *                #
#                                                                                              #
#  Cisco Products tested : ASR9010-9006                                                        #   
#                                                                                              #
#  Cisco IOS-XR Version tested : 4.2.3                                                         #  
#                                                                                              #
################################################################################################
##########################################################
#####################SET VARIABLES########################
##########################################################
# errorInfo gets set by namespace if any of the auto_path directories do not
# contain a valid tclIndex file. It is not an error just left over stuff.
# so we set the errorInfo value to null so that we don't have left
# over errors in it.
#
set errorInfo ""
set USEDMAC_LINECARD0_INGRESS "0"
set TOTALMAC_LINECARD0_INGRESS "0"
set USEDMAC_LINECARD1_INGRESS "0"
set TOTALMAC_LINECARD1_INGRESS "0"
set USEDMAC_LINECARD2_INGRESS "0"
set TOTALMAC_LINECARD2_INGRESS "0"
set USEDMAC_LINECARD3_INGRESS "0"
set TOTALMAC_LINECARD3_INGRESS "0"
set USEDMAC_LINECARD4_INGRESS "0"
set TOTALMAC_LINECARD4_INGRESS "0"
set USEDMAC_LINECARD5_INGRESS "0"
set TOTALMAC_LINECARD5_INGRESS "0"
set USEDMAC_LINECARD7_INGRESS "0"
set TOTALMAC_LINECARD7_INGRESS "0"
set USEDMAC_LINECARD7_INGRESS "0"
set TOTALMAC_LINECARD7_INGRESS "0"
set card0check "0"
set card1check "0"
set card2check "0"
set card3check "0"
set card4check "0"
set card5check "0"
set card6check "0"
set card7check "0"
##########################################################
#Check required environment variable(s) has been defined##
##########################################################
if {![info exists l2vpnmac_threshold_percent]} {
    set result "EEM Policy Error: variable l2vpnmac_threshold_percent has not been set"
    error $result $errorInfo
}
if {![info exists l2maccheck]} {
    set result "EEM Policy Error: variable l2maccheck has not been set"
    error $result $errorInfo
}
##############LINECARD-EXIST-CHECK####################
#------------------- "cli open"-------------------
if [catch {cli_open} result] {
    error $result $errorInfo
} else {
    array set cli $result
}
if [catch {cli_exec $cli(fd) "show platform | i IOS XR RUN "} result] {
    error $result $errorInfo
} else { 
 set line_card_exist_check $result
 regexp {0/0/CPU0} $line_card_exist_check card0check
 regexp {0/1/CPU0} $line_card_exist_check card1check
 regexp {0/2/CPU0} $line_card_exist_check card2check
 regexp {0/3/CPU0} $line_card_exist_check card3check
 regexp {0/4/CPU0} $line_card_exist_check card4check
 regexp {0/5/CPU0} $line_card_exist_check card5check
 regexp {0/6/CPU0} $line_card_exist_check card6check
 regexp {0/7/CPU0} $line_card_exist_check card7check 
 }
##########################################################
############MAC LINECARD0 INGRESS#########################
##########################################################
if { [ string length $card0check ] > 2 } {
if [catch {cli_open} result] {
    error $result $errorInfo
} else {
    array set cli $result
}
if [catch {cli_exec $cli(fd) "show l2vpn forwarding resource hardware ingress detail location 0/0/CPU0 | include TOTAL MAC"} result] { 
    error $result $errorInfo
} else { 
 set linecard0_output $result
 regsub -all { TOTAL MAC } $linecard0_output "USEDMAC" linecard0_output
 regsub -all { -/- } $linecard0_output "" linecard0_output
 regsub -all "\\s" $linecard0_output "" linecard0_output
 regsub -all "/" $linecard0_output "TOTALMAC" linecard0_output
 regexp {USEDMAC([0-9]*)TOTALMAC([0-9]*)} $linecard0_output linecard0_output
 regexp {USEDMAC([0-9]*)} $linecard0_output USEDMAC_LINECARD0_INGRESS
 regexp {TOTALMAC([0-9]*)} $linecard0_output TOTALMAC_LINECARD0_INGRESS
 regsub -all {[A-Z]} $USEDMAC_LINECARD0_INGRESS "" USEDMAC_LINECARD0_INGRESS
 regsub -all {[A-Z]} $TOTALMAC_LINECARD0_INGRESS "" TOTALMAC_LINECARD0_INGRESS
}
 if {$TOTALMAC_LINECARD0_INGRESS > 63999} {
 set linecard0_ingress_percent [ expr ($USEDMAC_LINECARD0_INGRESS*100.0)/($TOTALMAC_LINECARD0_INGRESS)]
}
 if { $linecard0_ingress_percent > $l2vpnmac_threshold_percent } {
    action_syslog msg "LINECARD0 Mac Adress usage is $USEDMAC_LINECARD0_INGRESS and exceeded the threshold % $l2vpnmac_threshold_percent ! If mac address usage on LINECARD0 reaches up to $TOTALMAC_LINECARD0_INGRESS , traffic might get flooded or dropped depending on the l2vpn configuration of the router"
}
if [catch {cli_close $cli(fd) $cli(tty_id)} result] {
    error $result $errorInfo
}
}
##########################################################
############MAC LINECARD1 INGRESS#########################
##########################################################
if { [ string length $card1check ] > 2 } {
if [catch {cli_open} result] {
    error $result $errorInfo
} else {
    array set cli $result
}
if [catch {cli_exec $cli(fd) "show l2vpn forwarding resource hardware ingress detail location 0/1/CPU0 | include TOTAL MAC"} result] { 
    error $result $errorInfo
} else { 
 set linecard1_output $result
 regsub -all { TOTAL MAC } $linecard1_output "USEDMAC" linecard1_output
 regsub -all { -/- } $linecard1_output "" linecard1_output
 regsub -all "\\s" $linecard1_output "" linecard1_output
 regsub -all "/" $linecard1_output "TOTALMAC" linecard1_output
 regexp {USEDMAC([0-9]*)TOTALMAC([0-9]*)} $linecard1_output linecard1_output
 regexp {USEDMAC([0-9]*)} $linecard1_output USEDMAC_LINECARD1_INGRESS
 regexp {TOTALMAC([0-9]*)} $linecard1_output TOTALMAC_LINECARD1_INGRESS
 regsub -all {[A-Z]} $USEDMAC_LINECARD1_INGRESS "" USEDMAC_LINECARD1_INGRESS
 regsub -all {[A-Z]} $TOTALMAC_LINECARD1_INGRESS "" TOTALMAC_LINECARD1_INGRESS
}
 if {$TOTALMAC_LINECARD1_INGRESS > 63999} {
 set linecard1_ingress_percent [ expr ($USEDMAC_LINECARD1_INGRESS*100.0)/($TOTALMAC_LINECARD1_INGRESS)]
}
 if { $linecard1_ingress_percent > $l2vpnmac_threshold_percent } {
    action_syslog msg "LINECARD1 Mac Adress usage is $USEDMAC_LINECARD1_INGRESS and exceeded the threshold % $l2vpnmac_threshold_percent ! If mac address usage on LINECARD1 reaches up to $TOTALMAC_LINECARD1_INGRESS , traffic might get flooded or dropped depending on the l2vpn configuration of the router"
}
if [catch {cli_close $cli(fd) $cli(tty_id)} result] {
    error $result $errorInfo
}
}
##########################################################
############MAC LINECARD2 INGRESS#########################
##########################################################
if { [ string length $card2check ] > 2 } {
if [catch {cli_open} result] {
    error $result $errorInfo
} else {
    array set cli $result
}
if [catch {cli_exec $cli(fd) "show l2vpn forwarding resource hardware ingress detail location 0/2/CPU0 | include TOTAL MAC"} result] { 
    error $result $errorInfo
} else { 
 set linecard2_output $result
 regsub -all { TOTAL MAC } $linecard2_output "USEDMAC" linecard2_output
 regsub -all { -/- } $linecard2_output "" linecard2_output
 regsub -all "\\s" $linecard2_output "" linecard2_output
 regsub -all "/" $linecard2_output "TOTALMAC" linecard2_output
 regexp {USEDMAC([0-9]*)TOTALMAC([0-9]*)} $linecard2_output linecard2_output
 regexp {USEDMAC([0-9]*)} $linecard2_output USEDMAC_LINECARD2_INGRESS
 regexp {TOTALMAC([0-9]*)} $linecard2_output TOTALMAC_LINECARD2_INGRESS
 regsub -all {[A-Z]} $USEDMAC_LINECARD2_INGRESS "" USEDMAC_LINECARD2_INGRESS
 regsub -all {[A-Z]} $TOTALMAC_LINECARD2_INGRESS "" TOTALMAC_LINECARD2_INGRESS
}
 if {$TOTALMAC_LINECARD2_INGRESS > 63999} {
 set linecard2_ingress_percent [ expr ($USEDMAC_LINECARD2_INGRESS*100.0)/($TOTALMAC_LINECARD2_INGRESS)]
}
 if { $linecard2_ingress_percent > $l2vpnmac_threshold_percent } {
    action_syslog msg "LINECARD2 Mac Adress usage is $USEDMAC_LINECARD2_INGRESS and exceeded the threshold % $l2vpnmac_threshold_percent ! If mac address usage on LINECARD2 reaches up to $TOTALMAC_LINECARD2_INGRESS , traffic might get flooded or dropped depending on the l2vpn configuration of the router"
}
if [catch {cli_close $cli(fd) $cli(tty_id)} result] {
    error $result $errorInfo
}
}
##########################################################
############MAC LINECARD3 INGRESS#########################
##########################################################
if { [ string length $card3check ] > 2 } {
if [catch {cli_open} result] {
    error $result $errorInfo
} else {
    array set cli $result
}
if [catch {cli_exec $cli(fd) "show l2vpn forwarding resource hardware ingress detail location 0/3/CPU0 | include TOTAL MAC"} result] { 
    error $result $errorInfo
} else { 
 set linecard3_output $result
 regsub -all { TOTAL MAC } $linecard3_output "USEDMAC" linecard3_output
 regsub -all { -/- } $linecard3_output "" linecard3_output
 regsub -all "\\s" $linecard3_output "" linecard3_output
 regsub -all "/" $linecard3_output "TOTALMAC" linecard3_output
 regexp {USEDMAC([0-9]*)TOTALMAC([0-9]*)} $linecard3_output linecard3_output
 regexp {USEDMAC([0-9]*)} $linecard3_output USEDMAC_LINECARD3_INGRESS
 regexp {TOTALMAC([0-9]*)} $linecard3_output TOTALMAC_LINECARD3_INGRESS
 regsub -all {[A-Z]} $USEDMAC_LINECARD3_INGRESS "" USEDMAC_LINECARD3_INGRESS
 regsub -all {[A-Z]} $TOTALMAC_LINECARD3_INGRESS "" TOTALMAC_LINECARD3_INGRESS
}
 if {$TOTALMAC_LINECARD3_INGRESS > 63999} {
 set linecard3_ingress_percent [ expr ($USEDMAC_LINECARD3_INGRESS*100.0)/($TOTALMAC_LINECARD3_INGRESS)]
}
 if { $linecard3_ingress_percent > $l2vpnmac_threshold_percent } {
    action_syslog msg "LINECARD3 Mac Adress usage is $USEDMAC_LINECARD3_INGRESS and exceeded the threshold % $l2vpnmac_threshold_percent ! If mac address usage on LINECARD3 reaches up to $TOTALMAC_LINECARD3_INGRESS , traffic might get flooded or dropped depending on the l2vpn configuration of the router"
}
if [catch {cli_close $cli(fd) $cli(tty_id)} result] {
    error $result $errorInfo
}
}
##########################################################
############MAC LINECARD4 INGRESS#########################
##########################################################
if { [ string length $card4check ] > 2 } {
if [catch {cli_open} result] {
    error $result $errorInfo
} else {
    array set cli $result
}
if [catch {cli_exec $cli(fd) "show l2vpn forwarding resource hardware ingress detail location 0/4/CPU0 | include TOTAL MAC"} result] { 
    error $result $errorInfo
} else { 
 set linecard4_output $result
 regsub -all { TOTAL MAC } $linecard4_output "USEDMAC" linecard4_output
 regsub -all { -/- } $linecard4_output "" linecard4_output
 regsub -all "\\s" $linecard4_output "" linecard4_output
 regsub -all "/" $linecard4_output "TOTALMAC" linecard4_output
 regexp {USEDMAC([0-9]*)TOTALMAC([0-9]*)} $linecard4_output linecard4_output
 regexp {USEDMAC([0-9]*)} $linecard4_output USEDMAC_LINECARD4_INGRESS
 regexp {TOTALMAC([0-9]*)} $linecard4_output TOTALMAC_LINECARD4_INGRESS
 regsub -all {[A-Z]} $USEDMAC_LINECARD4_INGRESS "" USEDMAC_LINECARD4_INGRESS
 regsub -all {[A-Z]} $TOTALMAC_LINECARD4_INGRESS "" TOTALMAC_LINECARD4_INGRESS
}
 if {$TOTALMAC_LINECARD4_INGRESS > 63999} {
 set linecard4_ingress_percent [ expr ($USEDMAC_LINECARD4_INGRESS*100.0)/($TOTALMAC_LINECARD4_INGRESS)]
}
 if { $linecard4_ingress_percent > $l2vpnmac_threshold_percent } {
    action_syslog msg "LINECARD4 Mac Adress usage is $USEDMAC_LINECARD4_INGRESS and exceeded the threshold % $l2vpnmac_threshold_percent ! If mac address usage on LINECARD4 reaches up to $TOTALMAC_LINECARD4_INGRESS , traffic might get flooded or dropped depending on the l2vpn configuration of the router"
}
if [catch {cli_close $cli(fd) $cli(tty_id)} result] {
    error $result $errorInfo
}
}
##########################################################
############MAC LINECARD5 INGRESS#########################
##########################################################
if { [ string length $card5check ] > 2 } {
if [catch {cli_open} result] {
    error $result $errorInfo
} else {
    array set cli $result
}
if [catch {cli_exec $cli(fd) "show l2vpn forwarding resource hardware ingress detail location 0/5/CPU0 | include TOTAL MAC"} result] { 
    error $result $errorInfo
} else { 
 set linecard5_output $result
 regsub -all { TOTAL MAC } $linecard5_output "USEDMAC" linecard5_output
 regsub -all { -/- } $linecard5_output "" linecard5_output
 regsub -all "\\s" $linecard5_output "" linecard5_output
 regsub -all "/" $linecard5_output "TOTALMAC" linecard5_output
 regexp {USEDMAC([0-9]*)TOTALMAC([0-9]*)} $linecard5_output linecard5_output
 regexp {USEDMAC([0-9]*)} $linecard5_output USEDMAC_LINECARD5_INGRESS
 regexp {TOTALMAC([0-9]*)} $linecard5_output TOTALMAC_LINECARD5_INGRESS
 regsub -all {[A-Z]} $USEDMAC_LINECARD5_INGRESS "" USEDMAC_LINECARD5_INGRESS
 regsub -all {[A-Z]} $TOTALMAC_LINECARD5_INGRESS "" TOTALMAC_LINECARD5_INGRESS
}
 if {$TOTALMAC_LINECARD0_INGRESS > 63999} {
 set linecard5_ingress_percent [ expr ($USEDMAC_LINECARD5_INGRESS*100.0)/($TOTALMAC_LINECARD5_INGRESS)]
}
 if { $linecard5_ingress_percent > $l2vpnmac_threshold_percent } {
    action_syslog msg "LINECARD5 Mac Adress usage is $USEDMAC_LINECARD5_INGRESS and exceeded the threshold % $l2vpnmac_threshold_percent ! If mac address usage on LINECARD5 reaches up to $TOTALMAC_LINECARD5_INGRESS , traffic might get flooded or dropped depending on the l2vpn configuration of the router"
}
if [catch {cli_close $cli(fd) $cli(tty_id)} result] {
    error $result $errorInfo
}
}
##########################################################
############MAC LINECARD6 INGRESS#########################
##########################################################
if { [ string length $card6check ] > 2 } {
if [catch {cli_open} result] {
    error $result $errorInfo
} else {
    array set cli $result
}
if [catch {cli_exec $cli(fd) "show l2vpn forwarding resource hardware ingress detail location 0/6/CPU0 | include TOTAL MAC"} result] { 
    error $result $errorInfo
} else { 
 set linecard6_output $result
 regsub -all { TOTAL MAC } $linecard6_output "USEDMAC" linecard6_output
 regsub -all { -/- } $linecard6_output "" linecard6_output
 regsub -all "\\s" $linecard6_output "" linecard6_output
 regsub -all "/" $linecard6_output "TOTALMAC" linecard6_output
 regexp {USEDMAC([0-9]*)TOTALMAC([0-9]*)} $linecard6_output linecard6_output
 regexp {USEDMAC([0-9]*)} $linecard6_output USEDMAC_LINECARD6_INGRESS
 regexp {TOTALMAC([0-9]*)} $linecard6_output TOTALMAC_LINECARD6_INGRESS
 regsub -all {[A-Z]} $USEDMAC_LINECARD6_INGRESS "" USEDMAC_LINECARD6_INGRESS
 regsub -all {[A-Z]} $TOTALMAC_LINECARD6_INGRESS "" TOTALMAC_LINECARD6_INGRESS
}
 if {$TOTALMAC_LINECARD6_INGRESS > 63999} {
 set linecard6_ingress_percent [ expr ($USEDMAC_LINECARD6_INGRESS*100.0)/($TOTALMAC_LINECARD6_INGRESS)]
}
 if { $linecard6_ingress_percent > $l2vpnmac_threshold_percent } {
    action_syslog msg "LINECARD6 Mac Adress usage is $USEDMAC_LINECARD6_INGRESS and exceeded the threshold % $l2vpnmac_threshold_percent ! If mac address usage on LINECARD6 reaches up to $TOTALMAC_LINECARD6_INGRESS , traffic might get flooded or dropped depending on the l2vpn configuration of the router"
}
if [catch {cli_close $cli(fd) $cli(tty_id)} result] {
    error $result $errorInfo
}
}
##########################################################
############MAC LINECARD7 INGRESS#########################
##########################################################
if { [ string length $card7check ] > 2 } {
if [catch {cli_open} result] {
    error $result $errorInfo
} else {
    array set cli $result
}
if [catch {cli_exec $cli(fd) "show l2vpn forwarding resource hardware ingress detail location 0/7/CPU0 | include TOTAL MAC"} result] { 
    error $result $errorInfo
} else { 
 set linecard7_output $result
 regsub -all { TOTAL MAC } $linecard7_output "USEDMAC" linecard7_output
 regsub -all { -/- } $linecard7_output "" linecard7_output
 regsub -all "\\s" $linecard7_output "" linecard7_output
 regsub -all "/" $linecard7_output "TOTALMAC" linecard7_output
 regexp {USEDMAC([0-9]*)TOTALMAC([0-9]*)} $linecard7_output linecard7_output
 regexp {USEDMAC([0-9]*)} $linecard7_output USEDMAC_LINECARD7_INGRESS
 regexp {TOTALMAC([0-9]*)} $linecard7_output TOTALMAC_LINECARD7_INGRESS
 regsub -all {[A-Z]} $USEDMAC_LINECARD7_INGRESS "" USEDMAC_LINECARD7_INGRESS
 regsub -all {[A-Z]} $TOTALMAC_LINECARD7_INGRESS "" TOTALMAC_LINECARD7_INGRESS
}
 if {$TOTALMAC_LINECARD7_INGRESS > 63999} {
 set linecard7_ingress_percent [ expr ($USEDMAC_LINECARD7_INGRESS*100.0)/($TOTALMAC_LINECARD7_INGRESS)]
}
 if { $linecard7_ingress_percent > $l2vpnmac_threshold_percent } {
    action_syslog msg "LINECARD7 Mac Adress usage is $USEDMAC_LINECARD7_INGRESS and exceeded the threshold % $l2vpnmac_threshold_percent ! If mac address usage on LINECARD7 reaches up to $TOTALMAC_LINECARD7_INGRESS , traffic might get flooded or dropped depending on the l2vpn configuration of the router"
}
if [catch {cli_close $cli(fd) $cli(tty_id)} result] {
    error $result $errorInfo
}
}
############CLOSE#########################

Activating the Script

After copying the script to a folder, you must define the environment variables  For this script, you must define the warning thresold and a cron entry for repetitive tasks.

(config)# event manager environment l2vpnmac_threshold_percent 85   
(config)# event manager environment l2maccheck 0 * * * * 
(config)# event manager policy  asr9010_l2vpn_mac_check username eem type user

NOTE: make a Google search if you are not familiar with cron entries.  Some examples:

0 * * * *            ------->  0. Minute of every hour
15 * * * *          ------->  15. Minute of every hour
*/10 * * * *    ------->  e very 10 minutes

Sunday, November 9, 2014

Python, The Right Language for (non)-Programmer Networkers, Where to Start ?

Network management is changing, nowadays in every network event, the hot topics  are SDN,  Virtualization of network services, Open platforms, efforts on seperating hardware from software etc. Whatever the widespread method will be, there will be more innovation in network management and there will be need for networkers with some programming skills. Thus so many networkers have some motivation to learn some programming language, as i am. 

I think that the right language to start with, for non-programmer networkers, is Python It is efficient, fast & easy and now is the most popular introductory language at American colleges, a recent Association for Computing Machinery study reports. 

I have searched through the internet for right sources, and tried some of them, as a result, here are the sources i think that a non-programmer networker should follow to learn Python:

  • Learn Python the Hard Way is an excellent beginner programmer’s guide to Python. It covers “hello world” from the console to the web.

Once you start and getting through stages of the book, may be you will also do not like how “learn python the hard way” explains class-object logic. If you are then you should also visit Jeff Knupp’s pages: 

On my following blogs, i will keep posting about python hints (like which telnet or python library you may use for automation…) that networkers might benefit. 
 

Internetworking Hints Copyright © 2011 -- Template created by O Pregador -- Powered by Blogger