Network Automation using Python: Mini ISP Configurator
Today one of the most popular topics in networking world is automation and I would like share my thoughts about it. So what is network automation? Well, it really depends on what you are trying to achive. Smells like SDN, right? :)
To give some exampes, you can get your device configs everyday at 3 am, or you can ugrade software of your hundereds of routers simultaneously with one click or you can even push configs to thousands of switches whenever you need. But not limited to these, of course. It looks like automation is only needed when you have lots of devices that you can not manage/operate one by one. But you may still need some degree of automation to make your life easier even you have a few devices. Especially if you have some recurring tasks or scheduled routines which can be automated effectively.
So, Python is a common language to write scripts to automate things. You can just send your configuration commands with Python and expect it finds the target. Or one step further you can read the output and verify if it did what you aimed to do. Another approach can be reading the user input or device logs and use it in the script with some if-else conditions to achive the action/configuration you need to enforce. All this methods can also be merged to accomplish your goal of automation. These variations and methods can be used to provide an appropriate solution to your automation needs.
One can prefer the script to be interactive and able to do the right things whatever the condition is. This makes it a little bit complex, where you need to define what is the action and in which case it should be taken and you need to cover probable cases. But another one can say keep it simple and effective as much as possible. Both are okay as long as it meets the requirements.
By the way, I must say that I am not a programmer or Python expert. I’m just trying to make a point on how Python can help automating some networking tasks, in my view of course.
The last Python script that I have wrote is “Mini ISP Configurator”. It is based on the idea of discovering each XRv box and it’s neighbors via LLDP. Then configure each box’s interface IP addressing according to box’s number. Add OSPF and enable LDP on OSPF enabled interfaces to provide end to end basic LSP paths between nodes. And finally configure BGP Vpnv4 peering with Route Reflectors which are also configured using the information from user input partially.
The coding in this script is not the best way of doing it, but as I said I’m not necesseraly a programmer and this code works good enough for me to accomplish my goals. So do not get stuck on writing the best code, just focus on what your code does.
Here is the topology that I used while writing the script. Script can easily be adopted to higher number of nodes and route reflectors with some minor modifications. I used paramiko and regular expression and time libraries as you may see from the code itself..
My prerequsites, restrictions and information about the script are listed below, they are also in the script and shown when it runs:
"This python script can used be for configuration of your XRv P and PE routers and IOS Route Reflector Routers to provide basic connectivity for MPLS/VPN services. The following prerequsites are needed, please read carefully:
1. SSHv2 and LLDP should be enabled on all routers.
2. XRv and IOS routers need to have contiguos management IP address, i.e if XRv-1 is 192.168.1.5, then XRv-2 should be 192.168.1.6. Same order applies for IOS routers as well.
3. Router hostname should be in 'XRv-X' and 'RR-X' format where X corresponds to host number and starts from 1.
4. The interface ip addressing schema between XRv interfaces is 10.10.XY.X/24, where X is local host number and Y is neighbor host number. In case X>Y then 3.octet will be '.YX.'. Due to ip addressing schema, script may fail if there are more then 9 XRvs in place.
5. The interface ip addressing schema between XRv and RR is 10.XY.10.X/24, where X is local host number and Y is neighbor host number. Same restriction on 4 applies to this one as well.
6. The loopback interface ip addressing schema on XRv's is 10.X.X.X/32 where X is the local host number. On RR's, Loopback is X.X.X.X/32 where X is the local host number.
7. OSPF with process id 1 configured on all routers.
8. BGP with ASN will be configured on PE XRvs and RRs. PEs will peer to all RRs for afi vpnv4.RRs will be in redundant pairs, each RR in pair will have same cluster-id.
9. MPLS LDP will be enabled on all OSPF interfaces.
10. Please insert your entries when you are asked to do, in requested format, otherwise script will fail.
11. This script is designed to be used in lab or test environments, please do not use it on prodcution environments. Use it responsibly...
I also copy the script here, so one can try it on the lab or improve/modify it according to his/her needs. Enjoy!
My final comments on network automation is as follows:
1. Do not think automation is only needed when you have many devices to be operated. You may ease your operation by half, even on a single device.
2. Automating is not just backing up device configs or pushing configuration. You can read what your device/user tells you and take action accordingly.
3. Do not try to write the best code. Focus on your needs and any type of code that does the job is more than enough. Do not get stuck in code perfection.
4. Automate your daily routines or recurring processes as much as possible, so you mitigate the risk of human errors. And you can have time to focus on different things.
5. Test your code before taking it into production, a single white space “ “ can hurt more than you can imagine.
Thank you for reading!
Serdar Kut
CCIE 18130(RS,SP) – Emeritus
from netmiko import ConnectHandler
from datetime import datetime
import re
import time
print("---------------------Welcome to Mini ISP Configurator---------------------")
print("This python script can be used for configuration of your XRv P and PE routers and IOS Route Reflector Routers to provide basic connectivity for MPLS/VPN services. The following prerequsites are needed, please read carefully: ")
print("1. SSHv2 and LLDP should be enabled on all routers. \n2. XRv and IOS routers need to have contiguos management IP address, i.e if XRv-1 is 192.168.1.5, then XRv-2 should be 192.168.1.6. Same order applies for IOS routers as well. \n3. Router hostname should be in 'XRv-X' and 'RR-X' format where X corresponds to host number and starts from 1.")
print("4. The interface ip addressing schema between XRv interfaces is 10.10.XY.X/24, where X is local host number and Y is neighbor host number. In case X>Y then 3.octet will be '.YX.'. Due to ip addressing schema, script may fail if there are more then 9 XRvs in place.")
print("5. The interface ip addressing schema between XRv and RR is 10.XY.10.X/24, where X is local host number and Y is neighbor host number. Same restriction on 4 applies to this one as well.")
print("6. The loopback interface ip addressing schema on XRv's is 10.X.X.X/32 where X is the local host number. On RR's, Loopback is X.X.X.X/32 where X is the local host number.")
print("7. OSPF with process id 1 configured on all routers. \n8. BGP with ASN will be configured on PE XRvs and RRs. PEs will peer to all RRs for afi vpnv4.RRs will be in redundant pairs, each RR in pair will have same cluster-id. \n9. MPLS LDP will be enabled on all OSPF interfaces.")
print("10. Please insert your entries when you are asked to do, in requested format, otherwise script will fail.")
print("11. This script is designed to be used in lab or test environments, please do not use it on prodcution environments. Use it responsibly...")
choice = input("12. If you agree on the steps above, please press Y, otherwise press Q to exit the script: ")
if choice == "Y":
xrv_ip_range = input("Please enter the IP addresses of first and the last XRv with ',' in between: ").split(",")
rr_ip_range = input("Please enter the IP addresses of first and the last RR with ',' in between: ").split(",")
print(xrv_ip_range)
print(rr_ip_range)
xrv_host_range_f = xrv_ip_range[0].split(".")
xrv_host_range_l = xrv_ip_range[1].split(".")
xrv_hf = xrv_host_range_f[3]
xrv_hl = xrv_host_range_l[3]
rr_host_range_f = rr_ip_range[0].split(".")
rr_host_range_l = rr_ip_range[1].split(".")
rr_hf = rr_host_range_f[3]
rr_hl = rr_host_range_l[3]
start_time = datetime.now()
print("LLDP discovery and interface/ip configuration has just begun... You will see applied configuration commands as we proceed... Please be patient...")
for i in range (int(xrv_hf),int(xrv_hl)+1):
cisco_xrv = {
'device_type': 'cisco_xr',
'host': xrv_host_range_f[0]+ '.'+xrv_host_range_f[1]+'.'+xrv_host_range_f[2]+'.' + str(i),
'username': 'admin',
'password': 'admin',
}
loop_ip = i - int(xrv_hf)+1
loop_and_ospf_and_mpls = ['interface Loopback 0', 'description XRv-' + str(loop_ip), 'ipv4 address 10.'+ str(loop_ip)+ '.' + str(loop_ip) + '.' + str(loop_ip) + ' ' + '255.255.255.255','mpls ldp', 'router ospf 1',
'router-id 10.'+ str(loop_ip)+ '.' + str(loop_ip) + '.' + str(loop_ip), 'log adjacency changes', 'mpls ldp auto-config', 'address-family ipv4 unicast', 'area 0', 'interface Loopback 0', 'commit']
net_connect = ConnectHandler(**cisco_xrv)
foutput = net_connect.send_config_set(loop_and_ospf_and_mpls)
bgp_config = ['router bgp 1', 'bgp router-id 10.' + str(loop_ip)+ '.' + str(loop_ip) + '.' + str(loop_ip), 'bgp log neighbor changes detail', 'address-family vpnv4 unicast', 'neighbor-group RR', 'remote-as 1',
'update-source Loopback0', ' address-family vpnv4 unicast', 'commit', 'end']
foutput1 = net_connect.send_config_set(bgp_config)
print(foutput)
print(foutput1)
net_connect.disconnect()
time.sleep(3)
net_connect = ConnectHandler(**cisco_xrv)
output = net_connect.send_command("show lldp neig | i XR " )
print(output)
xr_hosts = re.findall(r'XRv-.', output, re.IGNORECASE)
xrs= re.findall(r'\d+', str(xr_hosts))
if_count = len(xr_hosts)
u = 2
nn = 0
ipp = i - int(xrv_hf)+1
while nn < if_count:
node = xrs[nn]
intf = list(output.split("\n"))[u].split()
# print(intf)
net_connect = ConnectHandler(**cisco_xrv)
if ipp < int(node):
config_commands = ['interface ' + str(intf[1]), 'description link to XRv-' + str(node), 'ipv4 address 10.10.'+str(ipp)+str(node)+'.' + str(ipp) +' ' + '255.255.255.0', 'router ospf 1', 'area 0', 'interface ' + str(intf[1]), 'commit']
output_c = net_connect.send_config_set(config_commands)
print(output_c)
net_connect.disconnect()
else :
config_commands = ['interface ' + str(intf[1]), 'description link to XRv-' + str(node), 'ipv4 address 10.10.'+str(node)+str(ipp)+'.' + str(ipp) +' ' + '255.255.255.0', 'router ospf 1', 'area 0', 'interface ' + str(intf[1]), 'commit']
output_c = net_connect.send_config_set(config_commands)
print(output_c)
net_connect.disconnect()
u += 1
nn += 1
net_connect.disconnect()
time.sleep(3)
net_connect.disconnect()
print("Discovery and IP configuration between XRs boxes has just finished... Now Route-Reflector discovery and configuration begins...")
for i in range (int(xrv_hf),int(xrv_hl)+1):
cisco_xrv = {
'device_type': 'cisco_xr',
'host': xrv_host_range_f[0]+ '.'+xrv_host_range_f[1]+'.'+xrv_host_range_f[2]+'.' + str(i),
'username': 'admin',
'password': 'admin',
}
net_connect = ConnectHandler(**cisco_xrv)
output = net_connect.send_command("show lldp neig | i RR " )
print(output)
rr = re.findall(r'RR-.', output, re.IGNORECASE)
rrs = re.findall(r'\d+', str(rr))
if_count_rr = len(rr)
u = 2
nn = 0
ipp = i - int(xrv_hf)+1
while nn < if_count_rr:
r_intf = list(output.split("\n"))[u].split()
rnode = rrs[nn]
net_connect = ConnectHandler(**cisco_xrv)
rr_loopback = (rnode+'.'+rnode+'.'+rnode+'.'+rnode)
if ipp < int(rnode):
config_commands = ['interface ' + r_intf[1], 'description link to RR-' + rnode, 'ipv4 address 10.'+str(ipp)+rnode+'.10.' + str(ipp) +' ' + '255.255.255.0', 'router ospf 1', 'area 0', 'interface ' + r_intf[1],
'network point-to-point', 'commit']
output_i = net_connect.send_config_set(config_commands)
print(output_i)
net_connect.disconnect()
else :
config_commands = ['interface ' + r_intf[1], 'description link to RR-' + rnode, 'ipv4 address 10.'+rnode+str(ipp)+'.10.' + str(ipp) +' ' + '255.255.255.0', 'router ospf 1', 'area 0', 'interface ' + r_intf[1],
'network point-to-point', 'commit']
output_i = net_connect.send_config_set(config_commands)
print(output_i)
net_connect.disconnect()
u += 1
nn += 1
net_connect.disconnect()
net_connect.disconnect()
print('XR configs has finished')
pe_id = input('Now, please enter Node Number of XRs which will be used as PE (with space in between, etc 5 6): ').split(" ")
for k in range (int(rr_hf),int(rr_hl)+1):
cisco_rr = {
'device_type': 'cisco_ios',
'host': rr_host_range_f[0]+'.'+rr_host_range_f[1]+'.'+rr_host_range_f[2]+'.' + str(k),
'username': 'admin',
'password': 'Cisco_123',
}
rloop_ip = k - int(rr_hf)+1
r_id = (str(rloop_ip) + '.' + str(rloop_ip)+ '.' + str(rloop_ip) + '.' + str(rloop_ip))
net_connect = ConnectHandler(**cisco_rr)
if rloop_ip % 2 > 0 :
rloop_and_ospf = ['interface Loopback 0', 'description RR-' + str(rloop_ip), 'ip address ' + r_id + ' 255.255.255.255', 'ip ospf network point-to-point', 'router ospf 1', 'router-id ' + r_id,
'mpls ldp autoconfig area 0', 'network 0.0.0.0 0.0.0.0 area 0']
bgp_config_rr = ['router bgp 1', 'template peer-policy Clients', 'route-reflector-client', 'send-community both', 'template peer-session Clients', 'remote-as 1', 'cluster-id '+ str(rloop_ip)+'.'+str(rloop_ip+1)+'.'
+ str(rloop_ip)+'.'+str(rloop_ip+1), 'update-source Loopback 0', 'bgp router-id '+ r_id, 'bgp log-neighbor-changes', 'no bgp default ipv4-unicast', 'end']
else:
rloop_and_ospf = ['interface Loopback 0', 'description RR-' + str(rloop_ip), 'ip address ' + r_id + ' 255.255.255.255', 'ip ospf network point-to-point', 'router ospf 1', 'router-id ' + r_id,
'mpls ldp autoconfig area 0', 'network 0.0.0.0 0.0.0.0 area 0']
bgp_config_rr = ['router bgp 1', 'template peer-policy Clients', 'route-reflector-client', 'send-community both', 'template peer-session Clients', 'remote-as 1', 'cluster-id '+ str(rloop_ip-1)+'.'+str(rloop_ip)+'.'
+ str(rloop_ip-1)+'.'+str(rloop_ip), 'update-source Loopback 0', 'bgp router-id '+ r_id, 'bgp log-neighbor-changes', 'no bgp default ipv4-unicast', 'end']
foutput = net_connect.send_config_set(rloop_and_ospf)
foutput_b = net_connect.send_config_set(bgp_config_rr)
print(foutput)
print(foutput_b)
h = 0
while h < len(pe_id):
net_connect = ConnectHandler(**cisco_rr)
xr_rid = ('10.'+pe_id[h]+'.'+pe_id[h]+'.'+pe_id[h])
bgp_config_rr = ['router bgp 1', 'neighbor ' + xr_rid+' inherit peer-session Clients', 'address-family vpnv4', 'neighbor '+xr_rid+' activate', 'neighbor '+xr_rid+' inherit peer-policy Clients', 'end']
foutput1 = net_connect.send_config_set(bgp_config_rr)
print(foutput1)
net_connect.disconnect()
hx = int(pe_id[h])+ int(xrv_hf) - 1
cisco_pe = {
'device_type': 'cisco_xr',
'host': xrv_host_range_f[0]+ '.'+xrv_host_range_f[1]+'.'+xrv_host_range_f[2]+'.'+ str(hx),
'username': 'admin',
'password': 'admin',
}
net_connect = ConnectHandler(**cisco_pe)
bgp_config_xr = ['router bgp 1', 'neighbor '+r_id, 'use neighbor-group RR', 'address-family vpnv4 unicast', 'commit', 'end']
foutput2 = net_connect.send_config_set(bgp_config_xr)
print(foutput2)
net_connect.disconnect()
time.sleep(2)
h +=1
net_connect.disconnect()
time.sleep(5)
net_connect = ConnectHandler(**cisco_rr)
output = net_connect.send_command("show lldp neig | i XR " )
print(output)
m = re.findall(r'XRv-.', output, re.IGNORECASE)
xrs = re.findall(r'\d+', str(m))
#output_if = output.split(" ")
if_count = len(m)
u = 0
nn = 0
ipp = k - int(rr_hf)+1
while nn < if_count:
intf = list(output.split("\n"))[u].split()
node = xrs[nn]
net_connect = ConnectHandler(**cisco_rr)
if ipp < int(node):
config_commands = ['interface ' + intf[1], 'description link to XRv-' + node, 'ip address 10.'+str(ipp)+node+'.10.2' + str(ipp) +' ' + '255.255.255.0', 'ip ospf network point-to-point']
output_r1 = net_connect.send_config_set(config_commands)
print(output_r1)
net_connect.disconnect()
else :
config_commands = ['interface ' + intf[1], 'description link to XRv-' + node, 'ip address 10.'+node+str(ipp)+'.10.2' + str(ipp) +' ' + '255.255.255.0', 'ip ospf network point-to-point']
output_r1 = net_connect.send_config_set(config_commands)
print(output_r1)
net_connect.disconnect()
u += 1
nn += 1
net_connect.disconnect()
net_connect.disconnect()
print("Setup finished, now checking IP reachability from Loopback to Loopback with in 30 seconds: ")
time.sleep( 30 )
for i in range (int(xrv_hf),int(xrv_hl)+1):
cisco_xrv = {
'device_type': 'cisco_xr',
'host': xrv_host_range_f[0]+ '.'+xrv_host_range_f[1]+'.'+xrv_host_range_f[2]+'.' + str(i),
'username': 'admin',
'password': 'admin',
}
loop_ip = i - int(xrv_hf)+1
print("Pinging from XRv-"+str(loop_ip))
net_connect = ConnectHandler(**cisco_xrv)
for loop_ip in range (1,int(xrv_hl)-int(xrv_hf)+2):
ping_output = net_connect.send_command("ping 10." + str(loop_ip) + "." + str(loop_ip) + "." + str(loop_ip)+ " source loopback0")
print(ping_output)
ping_result = re.findall(r'!!!!!', ping_output, re.IGNORECASE)
if ping_result == ["!!!!!"]:
print("Ping successfull")
else:
print("Ping failed please check configs")
net_connect.disconnect()
for k in range (int(rr_hf),int(rr_hl)+1):
cisco_rr = {
'device_type': 'cisco_ios',
'host': rr_host_range_f[0]+'.'+rr_host_range_f[1]+'.'+rr_host_range_f[2]+'.' + str(k),
'username': 'admin',
'password': 'Cisco_123',
}
rloop_ip = k - int(rr_hf)+1
print("Pinging XRv's from RR-"+str(rloop_ip))
net_connect = ConnectHandler(**cisco_rr)
for loop_ip in range (1,int(xrv_hl)-int(xrv_hf)+2):
ping_output = net_connect.send_command("ping 10." + str(loop_ip) + "." + str(loop_ip) + "." + str(loop_ip)+ " source loopback0")
print(ping_output)
ping_result = re.findall(r'!!!!!', ping_output, re.IGNORECASE)
if ping_result == ["!!!!!"]:
print("Ping successfull")
else:
print("Ping failed please check configs")
net_connect.disconnect()
net_connect.disconnect()
print("!!!Basic IP reachability has been established!!!")
end_time = datetime.now()
total_time = end_time - start_time
print("The configuration and verification progress took " + str(total_time))
else:
print("Thank you for participating in this script and have a nice day!")
Çok teşekkür ederim :) , çok karşılıyorum ozellikle su sira herhangi birşey arayıp tararken , ilgimi çekti o yüzden sormak istedim. İnceleyeyim .. Kodlama değil (zaten o kadar bilgim de yok ) ama uygulama açısından bireysel olarak bir şeyler kapabilirsem diye kurcalamak istiyorum mevzuyu ... sevgiler , saygılar , selamlar 👌
Sevgili kuzenim , Phyton kullanımının Kimya Mühendisliği uygulamaları için düşüncelerini öğrenebilir miyim ? Matlabdan daha kullanışlıysa öğrenmeye çalışacağım önerirsen ,yani en azından deneyebilirim .. olur mu dersin ?