Reference post.....I needed to match a cisco format mac address, not the best, but it works...
String: 0000.0c07.ac01
Matching regex: ([0-9A-Fa-f]){4}\.([0-9A-Fa-f]){4}\.([0-9A-Fa-f]){4} String: 00:00:0c:07:ac:01
Matching regex: ([0-9A-Fa-f]){2}\:([0-9A-Fa-f]){2}\:([0-9A-Fa-f]){2}\:([0-9A-Fa-f]){2}\:([0-9A-Fa-f]){2}\:([0-9A-Fa-f]){2}
I have a small lab I put together to help bash around new ideas, it consists of:
- 2 Cisco 2611s
- 2 Cisco 3500 XLs
Recently I've been jumping between devices using a single console cable... and that got old pretty quick. I was able to pickup a NM-16A Async module which provides 16 more lines to my 2600. To use this card I also needed a Cisco octal cable. 
Steps: 1) After inserting the card and screwing in the octal cable you can verify that there is added line capacity: Router_A#show version | i line 16 terminal line(s)
2) Before you proceed you will need to find out what line numbers your new NM-16A uses, in my case it was 33-48: Router_A#show line Tty Typ Tx/Rx A Modem Roty AccO AccI Uses Noise Overruns Int * 0 CTY - - - - - 6 0 0/0 - 33 TTY 9600/9600 - - - - - 2 1 35/106 - 34 TTY 9600/9600 - - - - - 4 19 0/0 - 35 TTY 9600/9600 - - - - - 0 0 0/0 - 36 TTY 9600/9600 - - - - - 0 0 0/0 - 37 TTY 9600/9600 - - - - - 0 0 0/0 - 38 TTY 9600/9600 - - - - - 0 0 0/0 - 39 TTY 9600/9600 - - - - - 0 0 0/0 - 40 TTY 9600/9600 - - - - - 0 0 0/0 - 41 TTY 9600/9600 - - - - - 0 0 0/0 - 42 TTY 9600/9600 - - - - - 0 0 0/0 - 43 TTY 9600/9600 - - - - - 0 0 0/0 - 44 TTY 9600/9600 - - - - - 0 0 0/0 - 45 TTY 9600/9600 - - - - - 0 0 0/0 - 46 TTY 9600/9600 - - - - - 0 0 0/0 - 47 TTY 9600/9600 - - - - - 0 0 0/0 - 48 TTY 9600/9600 - - - - - 0 0 0/0 - 65 AUX 9600/9600 - - - - - 0 0 0/0 -
3) You need to create a loopback to be used to telnet to each of the lines: interface Loopback0 ip address 126.0.0.1 255.255.255.255 no ip redirects end
4) Next you need to configure the lines of this card with your preferred settings: line 33 48 no flush-at-activation transport preferred telnet transport input all stopbits 1 flowcontrol hardware
5) Finally, it's nice to add ip hosts on the router for ease of use. Note that the tcp port number for our new lines are named with 20xx where "xx" is the actual line number. For example, our first line is "33" so you need to connect to port "2033." After this is done plug the rollover cable coming from the octal cable labeled "1" into the console port of the device you want to manage. In my scenario, cable "1" == line 33 == port 2033, cable "2" == line 34 == port 2034, etc... ip host r2 2033 126.0.0.1 ip host s1 2034 126.0.0.1
You should now have a console server in which you can "reverse" telnet into other devices connected through the octal cable. You should be able to issue commands such as "telnet r2" and be dropped into a console session. To return to the router press "control + shift + 6 +x." To disconnect the session type "disconnect <connection number>" or "disconnect <hostname> ." Here is the relevant configuration parameters: ip host r2 2033 126.0.0.1 ip host s1 2034 126.0.0.1 interface Loopback0 ip address 126.0.0.1 255.255.255.255 no ip redirects line 33 48 exec-timeout 0 0 no flush-at-activation logging synchronous no exec notify transport preferred telnet transport input all stopbits 1 flowcontrol hardware
A big thank you to Gerry Murray for initial guidance and introducing me to this.
A few months ago I decided to write a web application that would essentially run like RANCID, I named it "tratto." Since then I decided that it would be better to nail down a basic python framework first, then integrate it into say.. a django application. In all of my years as an engineer I had never leveraged expect to accomplish simple and repetitive tasks. I am a recent python "convert" and wanted to write a simple app that could be used as a framework for managing and monitoring network connected devices and hosts. Tratto uses pexpectto connect and parse ssh and telnet sessions. This framework provides an easy way to connect to remote devices and issue commands and store the output. I also wanted an easy way to "extend" this framework and be able to add ways to connect to any operating system (or at least use default shell behavior as a baseline). I manage a wide variety of devices and I wanted to support at least the default implementations of Cisco IOS, OpenBSD, Mac OS X, and Aruba OS. Here is how you would add an operating system's parameters to Tratto (Systems.py):
class ArubaOS(OperatingSystem):
'''aruba configs'''
PROMPTLINE ='#'
PAGINATES =True
DISABLE_PAGINATION = 'terminal length 0'
GET_CONFIG ="show run"
There are 3 files included in Tratto right now:
- Connectivity.py -- This is a class which manages sessions using pexpect
- Systems.py -- This is the class which manages all the operating parameters
- driver.py -- This is an example file of how to use Tratto to fetch whatever info you want
Here is an example of how to use the framework to connect to devices and issue commands:
#!/usr/bin/env python
import Connectivity
import Systems
#telnet to a cisco switch
m = Systems.OperatingSystems['IOS']
s = Connectivity.Session("192.168.6.1",23,telnet,m)
s.login("akonkol", "mypass")
s.sendcommand("show ver")
s.sendcommand("show clock")
s.sendcommand("show run")
s.logout()
#ssh to a apple machine
m = Systems.OperatingSystems['OSX']
s = Connectivity.Session("127.0.0.1",22,"ssh",m)
s.login("akonkol", "mypass")
#sendcommand will echo response by default, you can store that
#response in a variable if you wish
result = s.sendcommand("df -h")
print result
s.getversion()
s.logout()
#ssh to openbsd box
m = Systems.OperatingSystems['OBSD']
s = Connectivity.Session("192.168.5.1",22,"ssh",m)
s.login("akonkol", "mypass")
print s.sendcommand("cat /etc/passwd")
print s.sendcommand("arp -a")
s.logout()
The Future
With Tratto you can technically pull information from any networked device and use that data for whatever you please. Current ideas are integrating Tratto into
- a config repository application with a web frontend (like RANCID)
- a network mapping application using cdp neighbors
- monitoring platform which performs different commands based on certain scenarios ("show interfaces" when IP SLA shows latency)
This is my first attempt at releasing python software, so if you think something could be better let me know. Download Tratto
Two years ago I wrote a script that you could use to send commands to telnet/ssh enabled devices called tratto. Since then I have changed jobs and with new jobs comes new enviornments. I can no longer send commands like "show run" without sending the enable command (enable is like the 'su' of unix). I re-visited my code... which is always an entertaining. Anyway, I added an "escalateprivileges" command and added a string to the Systems object so you can store what the escalation command is for different operating systems.
You can download it here or via github
Connectivity.py
def escalateprivileges(self, escalated_password=None):
escalated_password = escalated_password
if self.connected:
self.connection.sendline(self.operatingsystem.ESCALATE_COMMAND)
i = self.connection.expect(r"(?i)password[\s:]+")
if i==0:
self.connection.sendline(escalated_password)
i = self.connection.expect(self.operatingsystem.PROMPTLINE)
if i==0:
if("denied" in self.connection.before):
print "***Escalation FAILED***"
print self.connection.before
else:
print "***Escalation Successful***"
else:
raise SessionError("***Not Connected***")
Systems.py
class OperatingSystem(object):
ESCALATE_COMMAND=''
PAGINATES =False
VERSION =''
PROMPTLINE =''
..
class CiscoIOS(OperatingSystem):
'''cisco ios'''
PROMPTLINE = r'[-\w]+[>#]'
GET_CONFIG ='show running-config'
PAGINATES =True
VERSION ='show version'
DISABLE_PAGINATION = 'terminal length 0'
ESCALATE_COMMAND='enable'
...
Example usage:
#!/usr/bin/env python
import Connectivity
import Systems
#telnet to a cisco switch
m = Systems.OperatingSystems['IOS']
s = Connectivity.Session("10.10.1.1",23,"telnet",m)
s.login("akonkol", "mypassword")
s.escalateprivileges('myenablepassword')
#s.sendcommand("show ver")
s.sendcommand("show clock")
s.sendcommand("show run")
s.sendcommand("show start")
s.logout()
I wrote a network config management tool and wanted to create my own syntax highlighting for cisco configs. It's pretty easy to create your own highlighter styles using Alex Gorbatchev's SyntaxHighlighter.
You can download my cisco brush here
shBrushCisco.js
SyntaxHighlighter.brushes.Cisco = function()
{
var interface = 'interface FastEthernet';
var keywords = 'ip snmp-server tacacs-server ntp line logging'+
'boot hostname banner end clock udld power ';
var security = 'crypto';
this.regexList = [
{ regex: new RegExp('description.*$', 'gm'), css: 'comments' },
{ regex: new RegExp('!.*$', 'gm'), css: 'comments' },
{ regex: new RegExp('^( no|no).*$', 'gm'), css: 'comments' },
{ regex: new RegExp('^version.*$', 'gm'), css: 'color2' },
{ regex: new RegExp('^service.*$', 'gm'), css: 'color2' },
{ regex: new RegExp('^aaa.*$', 'gm'), css: 'color3' },
{ regex: new RegExp('^interface.*$', 'gm'), css: 'color2' },
{ regex: new RegExp('^access-list.*$', 'gm'), css: 'color3' },
{ regex: new RegExp('^username.*$', 'gm'), css: 'color2' },
{ regex: new RegExp('^enable.*$', 'gm'), css: 'color2' },
{ regex: SyntaxHighlighter.regexLib.multiLineCComments, css: 'comments' }, // multiline comments
{ regex: SyntaxHighlighter.regexLib.doubleQuotedString, css: 'string' }, // strings
{ regex: SyntaxHighlighter.regexLib.singleQuotedString, css: 'string' }, // strings
{ regex: /^\s*#.*/gm, css: 'preprocessor' },
{ regex: new RegExp(this.getKeywords(keywords), 'gm'), css: 'color1' }, // c# keyword
{ regex: new RegExp(this.getKeywords(interface),'gm'), css: 'color2' }, // c# keyword
{ regex: new RegExp(this.getKeywords(security), 'gm'), css: 'color3' } // c# keyword
];
this.forHtmlScript(SyntaxHighlighter.regexLib.aspScriptTags);
};
SyntaxHighlighter.brushes.Cisco.prototype = new SyntaxHighlighter.Highlighter();
SyntaxHighlighter.brushes.Cisco.aliases = ['cisco','network'];
some html file:
<head>
<link rel="stylesheet" type="text/css" href="path_to/shCore.css" />
<link rel="stylesheet" type="text/css" href="path_to/shThemeDefault.css"/>
<script type="text/javascript" src="/static/syntax-highlighter/shCore.js"></script>
<script type="text/javascript" src="/static/syntax-highlighter/shBrushCisco.js"></script>
<script type="text/javascript">
SyntaxHighlighter.all()
</script>
</head>
<body>
<pre class="brush: cisco">
interface GigabitEthernet0/0
description to corp_lan
ip address 192.168.1.10 255.255.0.0
ip access-group FW_ACL_GigabitEthernet0/0 in
no ip redirects
no ip proxy-arp
ip flow ingress
ip nat inside
ip virtual-reassembly in
duplex full
speed 100
service-policy input GI0/0-COS-IN
</pre>
</body>

I've been developing a web front-end for tratto. This front end is called spur and allows you to run commands on any ssh/telnet available device and store the outputs.
- Web configured cron jobs
- Any command set
- Define your own operating systems
- Run diff's on command outputs
- Since you can run any command you can do things like backup configurations from cisco routers and switches
- Get alerts on outputs, diffs, and failures
- Synatx highlighting
I'm looking to release this for free via github in the near future. Meanwhile here are some screenshots:



Spur is a web based network configuration manager. It allows you to ssh or telnet to any device and run commands through a web front-end.The results of these commands are stored and can be diff'd. You can get alerted whenever a diff is found. You can extend Spur's capabilities by writing your own modules for different types of equipment.
Spur is comprised of:
- Tratto - a ssh/telenet framework built on-top of pexpect
- A custom written "cron" which allows you to create and schedule repetitive tasks
- Customized django skin
- Cisco syntax highlighting via syntax-highlighter
This project is in beta, I need people to help test. I have not used spur to configure any network equipment as of right now.
System Requirements
- Python
- Django 1.4+
- Sqlite, mysql, posgres
which python # find out if you have python installed
#django installation
wget http://www.djangoproject.com/m/releases/1.4/Django-1.4.1.tar.gz
tar xzvf Django-1.4.1.tar.gz
cd Django-1.4.1
sudo python setup.py install
#sqlite installation
sudo apt-get install sqlite
Python Requirements
- django-mptt
- pexpect
- croniter
- pytz
sudo easy_install django-mptt
sudo easy_install pexpect
sudo easy_install croniter
sudo easy_install pytz
Spur Installation
git clone https://github.com/akonkol/spur.git
cd spur
#edit this file to match your enviornment
vi spur/spur_settings.py
#create an admin user
python manage.py syncdb
python manage.py runserver 0.0.0.0:8000
#Create a cronjob for spur
crontab -e
* * * * * /path/to/spur/manage.py spur-cron
Browse to spur http://your_fqdn_or_ip:8000
Thanks for checking it out.
Count the number of IP addresses found in a command output:
#Create regex for a single octet
set OCTET_PAT {(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])}
#Create regex pattern for a full ip address
set IP_PAT "[join [list $OCTET_PAT $OCTET_PAT $OCTET_PAT $OCTET_PAT] {.}]"
#Execute a command and look for regex matches and get count (-all)
set num_ospf_nbrs [regexp -all -nocase -line $IP_PAT [ exec "sho ipv6 ospf neigh | ex OSPF"] all ]
puts "Number of ipv6 ospf neighbors: $num_ospf_nbrs"
I wrote hatch a few years ago to make cookie cutter network config templates. One thing I've wanted to implement for a while is a DSL for use in hatch. I've experimented with jscc and more recently with peg.js. While writing grammar for peg.js I said to myself "it would be great if I could used Django's template system and filters without re-inventing the wheel."
So I started experimenting and found out its pretty easy to do. A little more tinkering and I could possibly replace hatch's mechanics with Django's builtin template, tag, and filter code.
#!/usr/bin/python
from django.conf import settings
from django import template
settings.configure(DEBUG=True, TEMPLATE_DEBUG=True)
#https://github.com/django/django/blob/master/django/template/__init__.py
s = u'conf t\n ip address {{ip_address}} {{subnet_mask}} \n end \n write'
t = template.Template(s)
c = template.Context({'ip_address': '192.168.1.1','subnet_mask':'255.255.255.0'})
rendered = t.render(c)
print rendered
print "----------------------------------"
s = u'conf t\n ip address 10.{{site_code}}.1.1 {{subnet_mask}} \n end \n write'
t = template.Template(s)
c = template.Context({'site_code': '66','subnet_mask':'255.255.255.0'})
rendered = t.render(c)
print rendered
print "----------------------------------"
s = u'conf t\n {% if site_code == 66 %}snmp-server location Sydney{% endif%}'
t = template.Template(s)
c = template.Context({'site_code': 66})
rendered = t.render(c)
print rendered
akonkol@dev:~/Code/messy$ ./temp_hack.py
conf t
ip address 192.168.1.1 255.255.255.0
end
write
----------------------------------
conf t
ip address 10.66.1.1 255.255.255.0
end
write
----------------------------------
conf t
snmp-server location Sydney
Traceroute looks up the PTR dns records for each hop. A lot of providers create ptr records that describe the layer 3 interface of a given router:
akonkol@use:~$ traceroute 8.8.8.8
traceroute to 8.8.8.8 (8.8.8.8), 30 hops max, 60 byte packets
1 router1-dal.linode.com (67.18.7.161) 0.451 ms 0.568 ms 0.698 ms
2 ae2.car01.dllstx2.networklayer.com (67.18.7.89) 0.184 ms 0.215 ms 24.803 ms
3 po101.dsr01.dllstx2.networklayer.com (70.87.254.73) 0.667 ms 0.726 ms 1.026 ms
4 po21.dsr01.dllstx3.networklayer.com (70.87.255.65) 0.793 ms 0.860 ms 0.911 ms
5 ae16.bbr02.eq01.dal03.networklayer.com (173.192.18.228) 0.470 ms 0.451 ms 0.456 ms
6 ae7.bbr01.eq01.dal03.networklayer.com (173.192.18.208) 0.440 ms 0.437 ms 0.417 ms
7 50.97.16.37 (50.97.16.37) 0.486 ms 0.512 ms 0.491 ms
8 72.14.233.85 (72.14.233.85) 0.555 ms 72.14.233.77 (72.14.233.77) 10.054 ms 10.044 ms
9 64.233.175.148 (64.233.175.148) 7.835 ms 72.14.237.219 (72.14.237.219) 3.690 ms 3.676 ms
10 209.85.249.69 (209.85.249.69) 7.489 ms 72.14.237.123 (72.14.237.123) 7.450 ms 209.85.249.66 (209.85.249.66) 7.409 ms
11 216.239.46.39 (216.239.46.39) 7.366 ms 7.332 ms 216.239.46.63 (216.239.46.63) 7.357 ms
12 * * *
13 google-public-dns-a.google.com (8.8.8.8) 7.410 ms 7.436 ms 7.418 ms
Depending on how much equipment you manage you could have hundreds or even thousands of layer 3 interfaces. This problem intrigued me so I wrote a script that leverages tratto; a framework that I built back in 2012 to ssh/telnet to devices. The script is called l3toptr.py which gets layer 3 interface information and constructs a fqdn.
Connection Code
The following snippet shows a few things: setting up the ssh session, issuing a "show ip interface brief" storing the results in "ip_ints."
#connection setup
import Connectiviy, Systems
device = args['device']
username = args['username']
os_type = Systems.OperatingSystems['IOS']
session = Connectivity.Session(device,port,transport,os_type)
session.login(username, password)
ip_ints = session.sendcommand("sho ip int br")
session.logout()
Parsing Code
We take ip_ints and for each line that contains an ip address, we split the line by "whitespace" which allows us to access the interface name and number separately allowing us to assign these values to int_name and int_ip. Based on the interface name we come up with an abbreviation: GE for gigabit interfaces, FE for fast ethernet interfaces,etc... Since DNS records do now allow for slashes, we replace those with hyphens and assign it to clean_suffix.
#for each line of sho ip int brief
for line in ip_ints.split('\r\n'):
contains_ip = re.findall( r'[0-9]+(?:\.[0-9]+){3}', line )
if contains_ip:
line_chunks = line.split()
#[0]= gigabitethernet1/2
int_name = line_chunks[0]
#[1] = 10.a.b.c
int_ip = line_chunks[1]
if "GigabitEthernet" in int_name:
prefix = "GE"
suffix = int_name[15:]
if "FastEthernet" in int_name:
prefix = "FE"
suffix = int_name[12:]
if "Loopback" in int_name:
prefix ="LO"
suffix = int_name[8:]
if "Vlan" in int_name:
prefix="VL"
suffix=int_name[4:]
if "Tunnel" in int_name:
prefix="TU"
suffix= int_name[6:]
#ignore NVIs
if "NVI" in int_name:
break
#replace interface number slash with hyphen
clean_suffix = re.sub('\/','-',suffix)
Schema and Return Code
The remaining code checks for any user defined schema. There are three variables you can specify in this "schema" (or format string): interface_name, interface_number, hostname, and ip_address. You would specify something like this to use a custom fqdn format: ./l3toPTR.py -u akonkol -d 10.24.2.1 -s "interface_name.hostname.mycompany.com" and interface_name and hostname would be replaced with the dynamically learned information for device living at 10.24.2.1.
if args['schema']:
schema = args['schema']
dns_line = schema.replace('interface_name',prefix)
dns_line = dns_line.replace('interface_number',clean_suffix)
dns_line = dns_line.replace('hostname',hostname)
dns_line = dns_line.replace('ip_address',int_ip)
print dns_line
else:
print prefix + clean_suffix + "." + hostname + domainname + "," + int_ip
Some Examples:
akonkol@echo:~/Code$ ./l3toPTR.py -u akonkol -d 10.45.1.2
Password:
GE0-0.chicagorouter02.network.exampleco.com,10.45.1.2
GE0-1.chicagorouter02.network.exampleco.com,24.140.215.81
LO10.chicagorouter02.network.exampleco.com,10.0.45.2
LO99.chicagorouter02.network.exampleco.com,24.140.215.82
akonkol@echo:~/Code$ ./l3toPTR.py -u akonkol -d 10.45.1.2 -dn konkol.com
Password:
GE0-0.chicagorouter02.konkol.com,10.45.1.2
GE0-1.chicagorouter02.konkol.com,24.140.215.81
LO10.chicagorouter02.konkol.com,10.0.45.2
LO99.chicagorouter02.konkol.com,24.140.215.82
akonkol@echo:~/Code$ ./l3toPTR.py -u akonkol -d 10.45.1.2 -s "interface_name-interface_number.mycompany.com,ip_address"
Password:
GE-0-0.mycompany.com,10.45.1.2
GE-0-1.mycompany.com,24.140.215.81
LO-10.mycompany.com,10.0.45.2
LO-99.mycompany.com,24.140.215.82
Wait a second, this isn't actually creating the DNS entries! You would be correct, and I'm willing to bet that if you've read this far that you can figure how to do that... There are a few ways to do this and it all depends on what you are using for your DNS server (bind, windows, etc). The above script generates a CSV output which you could loop through and use to create dns records. Here is a great article on creating ptr records in bulk on a windows dns server DNS Bulk PTR records creation.