Python3。 多供应商网络设备配置自动化

前言


祝大家有美好的一天!

我是一家大型电信运营商的网络工程师,在我的控制下,整个动物园遍布各种网络设备,但我们将讨论接入交换机。

本文不是行动指南,也不是唯一的解决方案,并且显然也不假装是年度提名的剧本,但我想分享这个创意,也许对某人有用。

本文将在扰流板下提供一个代码块,并在下面进行剪辑和说明,并说明其原因和用途。

挑战赛


专门使用python3。 该脚本应该能够从列表中转到交换机,确定哪种供应商,给出所需的命令,进行日志。

其实我怎么来的


面对在大量交换机上更改配置的小问题,我将立即对我们拥有的网络设备的集中管理系统保留一下,我的同事也有许多以脚本形式进行开发以方便手动工作的工具,主要是bash,perl。

但是对我来说,做点不同的事情很重要,因为 我最近开始学习python,我需要提高编程技能,并且我的工具应该看起来像锤子(易于使用且易于维护)。 如果仍然有兴趣,那么我要猫。

谷歌搜索并没有找到合适的解决方案(好像在论坛上提出了这个问题,但那里一切都错了),我决定开始编写自己的脚本。

我们有以下开关:

  • Raisecom
  • Qtech版本 1.0
  • Qtech版本 2.0(语法差异)
  • Eltex
  • D链接
  • 用Qtech版本1.0的枪口产生弗兰肯斯坦开关,而Raisecom的铁杆我们称之为ROS

首先,导入所需的库:

import telnetlib import time import os import sys import getpass import pexpect from telnetlib import Telnet import datetime import subprocess 

最初,我们描述供应商定义功能,因为 决定使用两种方法:

1)假设它是哪种类型的供应商(应邀输入登录名),因为我注意到每个人都不同:Qtech(登录:),Raisecom和ROS(登录:),Eltex(用户名:),D-link(用户名)。

2)登录后-确保第一项均已正确完成,并能更好地识别我们所处的开关。

功能介绍
 def who_is(): #   global ver_status global sab_versus global versus sab_versus='' ver_status='' versus='' #  a = 'Serial No.:1405' # #qtech rev1 d = 'Command: show switch' # #d-link f = 'Raisecom Operating System Software' # #raisecom h = 'QOS' # #raisecom-qtech j = 'MES1124M' # #eltex k = 'eltex' # #eltex n = 'Build245' #qtech2.0 parser=open('servers_&_log/ver.txt', 'r') #  for line in parser.readlines(): line1 = line.find(a) line4 = line.find(d) line5 = line.find(f) line6 = line.find(h) line7 = line.find(j) line8 = line.find(k) line10 = line.find(n) if line1 != -1: ver_status='qtech_rev_1.0' if line4 != -1: ver_status='d-link' if line5 != -1: ver_status='raisecom' if line6 != -1: ver_status='raisecom-qtech' sab_versus=1 if line7 !=-1: ver_status='eltex' if line8 !=-1: ver_status='eltex' if line10 != -1: ver_status = 'qtech_rev_2.0' time.sleep(0.1) parser.close() os.remove('servers_&_log/ver.txt') return ver_status,sab_versus 


整个功能在扰流板之下。 我们声明对整个脚本可见的全局变量:

 global ver_status global sab_versus global versus 

变量a,d,f,h,j,k,n存储关键字,通过这些关键字我们随后将确定开关的模型。

 a = 'Serial No.:1405' # #qtech rev1 d = 'Command: show switch' # #d-link f = 'Raisecom Operating System Software' # #raisecom h = 'QOS' # #raisecom-qtech j = 'MES1124M' # #eltex k = 'eltex' # #eltex n = 'Build245' #qtech2.0 

打开ver.txt文件后,我们逐行读取并检查关键字输入,因为 如果此结果为负,则find()函数返回-1,我们将建立分支。

 parser=open('servers_&_log/ver.txt', 'r') #  for line in parser.readlines(): line1 = line.find(a) line4 = line.find(d) line5 = line.find(f) line6 = line.find(h) line7 = line.find(j) line8 = line.find(k) line10 = line.find(n) if line1 != -1: ver_status='qtech_rev_1.0' 

...

我给出了代码的示例部分,其余部分在上面的扰流器下。

该文件的内容将在下面描述。 在完成所有操作并定义了供应商之后,删除ver.txt文件。

主变量声明,主循环主体
 user='user' password='password' komm=open('servers_&_log/komm.txt') log=open('servers_&_log/log.txt','a') ######### counter_komm=0 counter_dlink=0 counter_qtech=0 counter_eltex=0 counter_raisecom=0 counter_ROS=0 counter_qtech1_0=0 counter_qtech2_0=0 ########## for host in komm.readlines(): print('connect....',host) vend = '' response = os.system('ping -c 1 ' + host) verinfo = open('servers_&_log/ver.txt', 'w') ver_status = '' sab_versus = '' if response == 0: telnet = pexpect.spawn('telnet ' + host,timeout=40) vend = telnet.expect(['login:', 'Login:', 'User Name:', 'Username']) telnet.close() tn = Telnet(host.replace('\n', ''), 23,30) try: print('Ok'+'\n') tn.read_until(b':') tn.write((user +'\n').encode('ascii')) tn.read_until(b':') tn.write((password + '\n').encode('ascii')) time.sleep(3) tn.read_until(b'#',timeout=20) except: print('connection refused' + '\n') f = open('servers_&_log/log.txt', 'a') print(host, 'connection refused', file=log) print('#' * 100, host, file=log) ###   ######################################################### if vend == 0:#    Qtech tn.write(('show ver' + '\n').encode('ascii')) print((tn.read_until(b'#').decode('ascii')), file=verinfo) verinfo.close() who_is() ... 


我决定不打扰,并在正文中使用用户名和密码设置变量,我知道这不是安全的,我正在努力。

打开日志文件和开关列表

 komm=open('servers_&_log/komm.txt') log=open('servers_&_log/log.txt','a') 

在使用for循环之后,读取带有交换机ip地址的行

 for host in komm.readlines(): print('connect....',host) vend = '' 

我们检查它的可用性

 response = os.system('ping -c 1 ' + host) 

如果可以使用pexpect库访问它,我们将在此迭代中尝试通过telnet进行连接,并在邀请时进行第一次检查,这是我在开始时写到的。

 if response == 0: telnet = pexpect.spawn('telnet ' + host,timeout=40) vend = telnet.expect(['login:', 'Login:', 'User Name:', 'Username']) telnet.close() tn = Telnet(host.replace('\n', ''), 23,30) 

vend变量的值将介于0到3之间(含0和3),并且根据看到的登录提示,将形成另一个分支。

从这段代码中,细心的读者可能会注意到我正在与交换机建立连接并立即关闭连接,这并非没有道理。 我尝试仅使用telnetlib库,但是在第一次测试中,该脚本会定期卡住,并因超时而掉落,这对我们很有帮助。

关闭连接后,我们仅使用telnetlib库进行重新连接。

为了避免出错,我们已经在上面检查了该开关是否可访问,并排除了由于一个空闲开关而在操​​作期间导致脚本中断的问题,请尝试将所有内容包装在try除块之外。

反复出现的情况是,在100台交换机中,有1台不让自己进入。

 try: print('Ok'+'\n') tn.read_until(b':') tn.write((user +'\n').encode('ascii')) tn.read_until(b':') tn.write((password + '\n').encode('ascii')) time.sleep(3) tn.read_until(b'#',timeout=20) except: print('connection refused' + '\n') f = open('servers_&_log/log.txt', 'a') print(host, 'connection refused', file=log) print('#' * 100, host, file=log) 

...
如果一切正常,并且我们已建立连接,那么我们需要输入用户名和密码,
我们肯定知道任何冒号提示都使用冒号。

所以我们在等他

 tn.read_until(b':') 

输入登录名后

 tn.write((user +'\n').encode('ascii')) 

等待密码冒号

 tn.read_until(b':') 

输入密码

 tn.write((password + '\n').encode('ascii')) 

然后我们等待3秒(尽管如此,否则我们做了很多工作)

 time.sleep(3) 

等待邀请后

 tn.read_until(b'#',timeout=20) 

在此阶段,我们进入第二级以检查供应商。

 if vend == 0:#    Qtech tn.write(('show ver' + '\n').encode('ascii')) print((tn.read_until(b'#').decode('ascii')), file=verinfo) verinfo.close() who_is() 

因为 我们以为我们到达了Qtech,如果您仔细阅读,则在我们的动物园中有两种版本的qtech语法不同,我们仍然需要调和。

因此,我们给出show ver命令,将所有输出放入ver.txt文件
并调用如上所述的who_is()过程。 表演团队对所有Qtech,Raisecom,Eltex,

不幸的是,D-link并不理解,他需要说点swich,但是我们很聪明,并没有白费力气地使用假定的供应商定义进行迭代。

 if vend == 3:#   D-link tn.write(('show sw' + '\n').encode('ascii')) tn.write(('a' + '\n').encode('ascii')) print((tn.read_until(b'#').decode('ascii')), file=verinfo) verinfo.close() who_is() 

因此,立即在输入show swich后打了一个小字,该开关显示不完整的信息,并等待用户按任意键以进一步输出,因此,我们然后发送“ a”字符以显示完整的信息。

 tn.write(('a' + '\n').encode('ascii')) 

为避免这种情况,您可以关闭clipadding

这是供应商验证结束的地方,我们可以以99%的概率假定我们已经正确识别了交换机的型号,然后可以继续进行配置。

对于每个开关,我们都有一个单独的文件,其中包含一组命令。

配置块
 ... elif ver_status == 'qtech_rev_1.0': tn.write(('show ver' + '\n').encode('ascii')) print((tn.read_until(b'#').decode('ascii')), file=log) counter_qtech1_0+=1 komand_qtech1_0=open('servers_&_log/komand_qtech_ver1.0.txt') for kommand in komand_qtech1_0.readlines(): tn.write((kommand.replace('\n', '') + '\n').encode('ascii')) tn.write(('exit' + '\n').encode('ascii')) print(tn.read_all().decode('ascii'), file=log) print('   qtech1.0') print('Qtech rev1.0 ' + host, file=log) print('#' * 100, file=log) ################################################################################ elif ver_status == 'qtech_rev_2.0': tn.write(('show ver' + '\n').encode('ascii')) print((tn.read_until(b'#').decode('ascii')), file=log) counter_qtech2_0+=1 komand_qtech2_0=open('servers_&_log/komand_qtech_ver2.0.txt') print('   qtech2.0') for kommand in komand_qtech2_0.readlines(): tn.write((kommand.replace('\n', '') + '\n').encode('ascii')) print((tn.read_until(b'#').decode('ascii')), file=log) print('Qtech rev2.0 ' + host, file=log) print('#' * 100, file=log) ... 


函数计算完并返回ver_status变量后,我们可以继续进行分支工作,因为 我们确切知道当前正在使用哪个开关。
首先,我们给switch命令show ver,并将输出写入日志(关于d-link,记住我们给它命令sh sw)

 tn.write(('show ver' + '\n').encode('ascii')) print((tn.read_until(b'#').decode('ascii')), file=log) 

确保保留计数器以识别不符合项。
 counter_qtech1_0+=1 

我们用命令打开一个文件
 komand_qtech1_0=open('servers_&_log/komand_qtech_ver1.0.txt') 

文件中命令的顺序必须与管理员手动输入的顺序相同

一个例子:

conf
vlan 2525
name SPD
int ethe 1/1
sw mode access
sw acc vl 2525
exit
exit
save
y

然后一切都取决于场景-我们读取文件直到行结束并执行它们

 for kommand in komand_qtech1_0.readlines(): tn.write((kommand.replace('\n', '') + '\n').encode('ascii')) 

退出循环后,我们告诉交换机退出并将所有输出读取到文件中,因此我们仅使用qtech1.0,因为 有时,脚本会预料到某些事情,并且这种切换就是这样,以免大惊小怪,这种解决方案在我看来似乎更优雅。

配置完交换机后,我们将总计数器增加一。

 counter_komm+=1 

然后,我们重新开始,使用开关从文件中读取下一行,确定模型并进行配置。

退出主要周期后,您需要总结已完成的工作,
在日志末尾,我们输入所有处理过的模型,并且日期是必填项,那么,如果没有日期,该怎么办。

 print('\n\n\n : ', counter_komm,file=log) print('\n\nD-link:', counter_dlink,'\nQtech ver1.0:', counter_qtech1_0,'\nROS:', counter_ROS,'\nRaisecom:',counter_raisecom,'\nEltex:', counter_eltex,'\nQtech ver2.0 :', counter_qtech2_0,file=log) print('\n: ', datetime.datetime.now().isoformat(),'\n', '#'*100,'\n\n\n\n\n',file=log) verinfo.close() log.close() 

该脚本已被重复处理,并且该过程不会在那里停止。
谢谢大家的关注。 欢迎进行建设性的批评。

所有代码都在扰流器下。

源代码
 #!/usr/bin/python3 #22/06/2017 17:17 import telnetlib import time import os import sys import getpass import pexpect from telnetlib import Telnet import datetime import subprocess def who_is(): #     ,      . global ver_status global sab_versus global versus sab_versus='' ver_status='' versus='' a = 'Serial No.:1405' # #qtech rev1 #b = 'Serial No.:3922' # #qtech rev2 #c = 'Serial No.:5436' # #qtech rev2 #l = 'Serial No.:16101' # #qtech rev2 d = 'Command: show switch' # #d-link f = 'Raisecom Operating System Software' # #raisecom h = 'QOS' # #raisecom-qtech j = 'MES1124M' # #eltex k = 'eltex' # #eltex n = 'Build245' #qtech2.0 parser=open('servers_&_log/ver.txt', 'r') for line in parser.readlines(): line1 = line.find(a) #line2 = line.find(b) #line3 = line.find(c) line4 = line.find(d) line5 = line.find(f) line6 = line.find(h) line7 = line.find(j) line8 = line.find(k) #line9 = line.find(l) line10 = line.find(n) if line1 != -1: ver_status='qtech_rev_1.0' #if line2 != -1 or line3 != -1 or line9 !=-1: #ver_status='qtech_rev_2.0' if line4 != -1: ver_status='d-link' if line5 != -1: ver_status='raisecom' if line6 != -1: ver_status='raisecom-qtech' sab_versus=1 if line7 !=-1: ver_status='eltex' if line8 !=-1: ver_status='eltex' if line10 != -1: ver_status = 'qtech_rev_2.0' time.sleep(0.1) parser.close() os.remove('servers_&_log/ver.txt') return ver_status,sab_versus user='user' password='password' komm=open('servers_&_log/komm.txt') log=open('servers_&_log/log.txt','a') counter_komm=0 counter_dlink=0 counter_qtech=0 counter_eltex=0 counter_raisecom=0 counter_ROS=0 # counter_qtech1_0=0 counter_qtech2_0=0 for host in komm.readlines(): print('connect....',host) vend = '' #tn = Telnet(host.replace('\n', ''), 23, 20) response = os.system('ping -c 1 ' + host) verinfo = open('servers_&_log/ver.txt', 'w') ver_status = '' sab_versus = '' if response == 0: telnet = pexpect.spawn('telnet ' + host,timeout=40) vend = telnet.expect(['login:', 'Login:', 'User Name:', 'Username']) telnet.close() tn = Telnet(host.replace('\n', ''), 23,30) try: print('Ok'+'\n') tn.read_until(b':') tn.write((user +'\n').encode('ascii')) tn.read_until(b':') tn.write((password + '\n').encode('ascii')) time.sleep(3) tn.read_until(b'#',timeout=20) except: print('connection refused' + '\n') f = open('servers_&_log/log.txt', 'a') print(host, 'connection refused', file=log) print('#' * 100, host, file=log) ###   ################################################################################ if vend == 0:#    Qtech tn.write(('show ver' + '\n').encode('ascii')) print((tn.read_until(b'#').decode('ascii')), file=verinfo) verinfo.close() who_is() ################################################################################ if vend == 1: #   Raisecom,Raisecom-Qtech tn.write(('show ver' + '\n').encode('ascii')) print((tn.read_until(b'#').decode('ascii')), file=verinfo) verinfo.close() who_is() ################################################################################ if vend == 2:#   Eltex tn.write(('show system' + '\n').encode('ascii')) tn.write(('show ver' + '\n').encode('ascii')) print((tn.read_until(b'#').decode('ascii')), file=verinfo) verinfo.close() who_is() ################################################################################ if vend == 3:#   D-link tn.write(('show sw' + '\n').encode('ascii')) tn.write(('a' + '\n').encode('ascii')) print((tn.read_until(b'#').decode('ascii')), file=verinfo) verinfo.close() who_is() # ###  # ################################################################################ if ver_status == 'd-link': tn.read_until(b'#', timeout=20) tn.write(('show sw' + '\n').encode('ascii')) tn.write(('a' + '\n').encode('ascii')) print((tn.read_until(b'#').decode('ascii')), file=log) print('D-link ' + host,file=log) print('#'*100,file=log) counter_dlink+=1 ################################################################################ elif ver_status == 'qtech_rev_1.0': tn.write(('show ver' + '\n').encode('ascii')) print((tn.read_until(b'#').decode('ascii')), file=log) counter_qtech1_0+=1 komand_qtech1_0=open('servers_&_log/komand_qtech_ver1.0.txt') for kommand in komand_qtech1_0.readlines(): tn.write((kommand.replace('\n', '') + '\n').encode('ascii')) #print((tn.read_until(b'#').decode('ascii')), file=log) tn.write(('exit' + '\n').encode('ascii')) print(tn.read_all().decode('ascii'), file=log) print('Qtech rev1.0 ' + host, file=log) print('#' * 100, file=log) ################################################################################ elif ver_status == 'qtech_rev_2.0': tn.write(('show ver' + '\n').encode('ascii')) print((tn.read_until(b'#').decode('ascii')), file=log) counter_qtech2_0+=1 komand_qtech2_0=open('servers_&_log/komand_qtech_ver2.0.txt') for kommand in komand_qtech2_0.readlines(): tn.write((kommand.replace('\n', '') + '\n').encode('ascii')) print((tn.read_until(b'#').decode('ascii')), file=log) time.sleep(1) print('Qtech rev2.0 ' + host, file=log) print('#' * 100, file=log) ################################################################################ elif ver_status == 'raisecom-qtech': tn.write(('show ver' + '\n').encode('ascii')) print((tn.read_until(b'#').decode('ascii')), file=log) raisecom_command = open('servers_&_log/komand_raisecom.txt') for komand in raisecom_command.readlines(): tn.write((komand.replace('\n', '') + '\n').encode('ascii')) print((tn.read_until(b'#').decode('ascii')), file=log) print('ROS '+ host,file=log) print('#'*100,file=log) counter_ROS+=1 ################################################################################ elif ver_status == 'raisecom': tn.write(('show ver' + '\n').encode('ascii')) print((tn.read_until(b'#').decode('ascii')), file=log) counter_raisecom+=1 raisecom_command = open('servers_&_log/komand_raisecom.txt') for komand in raisecom_command.readlines(): tn.write((komand.replace('\n', '') + '\n').encode('ascii')) print((tn.read_until(b'#').decode('ascii')), file=log) print(host, ':', 'RAISECOM', file=log) print('#' * 100, host, file=log) ################################################################################ elif ver_status == 'eltex': tn.write(('show ver' + '\n').encode('ascii')) print((tn.read_until(b'#').decode('ascii')), file=log) eltex_command=open('servers_&_log/komand_eltex.txt') for komand in eltex_command.readlines(): tn.write((komand.replace('\n', '') + '\n').encode('ascii')) print((tn.read_until(b'#').decode('ascii')), file=log) print('Eltex ' + host, file=log) print('#' * 100, file=log) counter_eltex+=1 else: print('no ping') counter_komm+=1 print('\n\n\n : ', counter_komm,file=log) print('\n\nD-link:', counter_dlink,'\nQtech ver1.0:', counter_qtech1_0,'\nROS:', counter_ROS,'\nRaisecom:',counter_raisecom,'\nEltex:', counter_eltex,'\nQtech ver2.0 :', counter_qtech2_0,file=log) print('\n: ', datetime.datetime.now().isoformat(),'\n', '#'*100,'\n\n\n\n\n',file=log) verinfo.close() log.close() 

Source: https://habr.com/ru/post/zh-CN415453/


All Articles