实验环境:
SW1-SW5上的IP地址分别为192.168.56.11、192.168.56.22、192.168.56.33、192.168.56.44、192.168.56.55。并且在其上配置了SSH,保证PC能和五台设备能够通信。现在通过创建一个ip_list.txt文件,在其中配置其上五个IP地址:
在程序中打开此文档,读取IP地址进行连接。并且创建另外一个comm.txt文档,如下:
其中有配置的命令(查看设备版本),后续在程序中打开这个文件,读取命令输入在设备上。
SSH的账号和密码为:prin和Huawei@123。
现在出现了如下情况:
1.SW1的地址进行了改动,从192.168.56.11->192.168.56.12;
2.SW4的SSH密码为Huawei@1234。
在这种情况下,如果我们正常通过python的paramiko连接,在连接第一个设备的时候就会报错,并不会执行后续的操作,这样的情况在实际生产环境中是不可行的。可行的方案是在遇到无法连接的设备的时候,我们自动跳过,并作出提示,再连接后续的设备。最后能够连接的设备配置完成后,再来根据提示的信息集中处理出问题的设备。
我们可以使用异常语句实现此功能,因为捕捉到异常后,编译器print了异常信息后,会继续执行后面的程序。
版本:python3.9
实验代码:
#通过paramiko的方式连接的各个设备
import paramiko
#调用time模块来保证通过python输入脚本的间隔时间
import time
#调用getpass模块来获取密码
import getpass
#设备不可达产生的是socket.error,需要导入socket内置模块
import socket
username = input('Username: ')
password = getpass.getpass('Password: ')
#创建两个列表分别记录那些设备认证错误,哪些设备不可达
switch_with_authentication_issue = []
switch_not_reachable = []
#打开ip_list.txt文件,将结果赋予ip_list变量
ip_list = open('ip_list.txt', 'r')
#通过readlines()方法读取每一行IP地址,并返回一个列表;再通过for循环进行遍历
for line in ip_list.readlines():
#在设备连接和命令配置代码前,进行异常的捕捉,以便于在这部分出现了故障后,依然能够执行后面的代码,连接后续设备
try:
#使用字符串的strip()方法,去除尾部的\n
ip = line.strip()
#使用paramiko连接设备
ssh_client = paramiko.SSHClient()
ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh_client.connect(hostname=ip, username=username, password=password)
print("You have successfully connect to " + ip)
command = ssh_client.invoke_shell()
#打开command.txt文件,读取其中的命令,并输入到设备上
cmd_list = open('command.txt', 'r')
cmd_list.seek(0)
for line in cmd_list.readlines():
command.send(line + "\n")
#休息2s钟再进行再进行后续的代码
time.sleep(2)
cmd_list.close()
#回显在本设备上执行的代码与查看到的内容
output = command.recv(65535)
print(output.decode('ascii'))
#如果检查到了异常,按照是SSH认证失败还是连接故障进行告警,并将出问题设备的IP地址添加到两个列表的尾部
except paramiko.ssh_exception.AuthenticationException:
print("User authentication failed for " + ip + ".")
switch_with_authentication_issue.append(ip)
except socket.error:
print(ip + " is not reachable.")
switch_not_reachable.append(ip)
ip_list.close()
ssh_client.close()
#通过for循环print出所有出现故障的设备
print("\n User authentication failed for below switches: ")
for i in switch_with_authentication_issue:
print(i)
print('\n Below switches are not reachable: ')
for i in switch_not_reachable:
print(i)
实验结果:
可以从程序运行中看到,192.168.56.11无法联通和192.168.56.44认证失败了,但是其他三台设备能够正常输入命名。
最后也以看到认证失败的IP和不可达的IP。
代码复用:
当面对不同型号的设备的设备时,实现同一功能的命令很有可能会不同,特别是在网络环境较大的情况下,如果对于不同的设备都通告command.send()方法写入命令,十分复杂,所以我们可以按照上述的方法对需要完全相同配置的设备创建不同的command.txt文件,然后再代码进行文件操作。但是这种方式也有一定的弊端,也就是每次运行代码的时候需要修改代码中打开的文件,我们可以通过另外一种方式在不修改代码的情况下,打开不同的txt文件进行操作。
具体操作:使用sys.argv[]来记录打开的文件名称。argv[]变量是一个列表,其中argv[0]表示本身.py脚本的文件名,而argv[1]、agrv[2]…则表示的是我们运行此.py脚本后跟随的参数,类型为str。
具体代码:
import sys
#将值赋予ip_file和cmd_file变量,
ip_file = sys.argv[1]
cmd_file = sys.argv[2]
ip_file = sys.argv[1]
cmd_file = sys.argv[2]
#查看变量具体的内容与类型
print(ip_file)
print(type(ip_file))
print(cmd_file)
print(type(cmd_file))
#以只读方式打开文件
ip_list = open(ip_file, 'r')
cmd_list = open(cmd_file, 'r')
在cmd界面运行:
可以通过打印出来的信息进行验证。因此如果我们需要更换管理的设备和相关的命令,可以不需要修改代码,仅需要在调用.py文件的时候跟上需要打开的txt文档即可。
参考资料:《网络工程师的python之路》