<Huawei>sys
[Huawei]sys R2
[R2]int g0/0/0
[R2-GigabitEthernet0/0/0]ip add 192.168.250.2 24
[R2]stelnet server enable
[R2]aaa
[R2-aaa]local-user admin privilege level 3 password cipher Huawei@123
[R2-aaa]local-user admin service-type ssh
[R2-aaa]q
[R2]user-interface vty 0 4
[R2-ui-vty0-4]authentication-mode aaa
[R2-ui-vty0-4]protocol inbound ssh
import paramiko
import time
username = 'admin'
password = 'Huawei@123'
ip = '192.168.250.2'
def ensp_exec_command(command):
# 创建SSH对象
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
# 连接服务器
ssh.connect(hostname=ip, port=22, username=username, password=password)
# 执行命令
print("Sucessfully login to ", ip)
command = ssh.invoke_shell() # 激活terminal
command.send("sys\n")
command.send("int g0/0/0\n")
command.send("ip address 2.2.2.2 255.255.255.0\n")
time.sleep(5) # 如果程序执行的太快,没有等到返回足够的信息,chan.recv(65535)不能得到想要的结果
output = command.recv(65535)
print(output.decode().strip())
ssh.close()
if __name__ == '__main__':
ensp_exec_command("dis cu")
结果:
Sucessfully login to 192.168.250.2
-----------------------------------------------------------------------------
User last login information:
-----------------------------------------------------------------------------
Access Type: SSH
IP-Address : 192.168.250.10 ssh
Time : 2020-05-24 00:03:22-08:00
-----------------------------------------------------------------------------
<R2>sys
Enter system view, return user view with Ctrl+Z.
[R2]int g0/0/0
[R2-GigabitEthernet0/0/0]
<Huawei>sys
[Huawei]sys SW2
[SW2]
[SW2]int vlanif 1
[SW2-Vlanif1]ip add 192.168.250.2 24
SW3:
interface Vlanif1
ip address 192.168.250.3 255.255.255.0
SW4:
interface Vlanif1
ip address 192.168.250.4 255.255.255.0
SW2:
[SW2]stelnet server enable
[SW2]aaa
[SW2-aaa]local-user admin privilege level 3 password cipher Huawei@123
[SW2-aaa]local-user admin service-type ssh
[SW2-aaa]q
[SW2]user-interface vty 0 4
[SW2-ui-vty0-4]authentication-mode aaa
[SW2-ui-vty0-4]protocol inbound ssh
SW3\SW4:
stelnet server enable
#
aaa
local-user admin password cipher Huawei@123
local-user admin privilege level 3
local-user admin service-type ssh
#
user-interface vty 0 4
authentication-mode aaa
protocol inbound ssh
#
我发觉配完之后,ssh登不上,the connection is closed by SSH server,看来交换机和路由器的ssh有点区别,然后查找官方交换机手册发现了这个,而且两种方案只能选择其一
方案1:aaa配了之后,只配ssh authentication-type default password;
方案2:aaa配了之后,配置具体的用户,用户的服务类型和认证方式
例如:ssh user xx;
ssh user xx service-type stelnet;
ssh user xx authentication-type password;
方案一:
[SW2]ssh authentication-type default
方案二:
[SW2]ssh user admin
Info: Succeeded in adding a new SSH user.
[SW2]ssh user admin service-type stelnet
[SW2]ssh user admin authentication-type password
import paramiko
import time
#import getpass
username = input('Username: ')
password = input('Password: ')
# password = getpass.getpass('Password: ') #在PyCharm中,运行该命令是无效的会卡在那。需要在terminal中运行。
for i in range(2, 5):
ip = "192.168.250." + str(i)
ssh_client = paramiko.SSHClient()
ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh_client.connect(hostname=ip,port=22,username=username,password=password)
print("\033[31;1m[+] Successfully connect to \033[0m", ip)
command = ssh_client.invoke_shell()
command.send("system-view\n")
for n in range (10, 21):
print("\033[32;1m[*]Creating VLAN[*]\033[0m" + str(n))
command.send("vlan " + str(n) + "\n")
time.sleep(0.5)
command.send("return\n")
time.sleep(2)
output = command.recv(65535)
print(output.decode().strip())
ssh_client.close()
和实验二的网络拓扑和配置一样的,唯独就是地址变成192.168.250.44
import paramiko
import time
import getpass
username = input('Username: ')
password = input('Password: ')
# password = getpass.getpass('Password: ') #在PyCharm中,运行该命令是无效的会卡在那。需要在terminal中运行。
f = open("ip_list.txt","r")
for line in f.readlines():
ip = line.strip()
ssh_client = paramiko.SSHClient()
ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh_client.connect(hostname=ip,port=22,username=username,password=password)
print("\033[31;1m[+] Successfully connect to \033[0m", ip)
command = ssh_client.invoke_shell()
# 配置trunk口
command.send("system-view\n")
command.send("int g0/0/1\n")
command.send("port link-type trunk\n")
command.send("port trunk allow-pass vlan all\n")
time.sleep(2)
output = command.recv(65535)
print(output.decode().strip())
print("\033[32;1m[*]配置结束[*]\033[0m")
f.close()
ssh_client.close()
在只有几台设备的实验环境中常用。但是在有成千上万台网络设备需要管理的生产环境中,实验三这种方法显得很笨拙,缺乏灵活性。举例来说,假设你的生产环境中有不同型号,不同操作系统,不同命令格式的设备各1000台
比如华为防火墙的5500和6000
你必须反复修改command.send()这个部分的代码,如果只是简单数条命令还好办,一旦遇到大规模的配置,那这种方法的效率会很低下。
解决这个问题的思路是分别创建两个文本文件,一个用来存放配置5500要用到的命令集,另一个用来存放配置6000要用到的命令集,然后在Python脚本里通过for循环加open()来读取两个文件里的内容以达到分别给所有5500和6000 防火墙 配置的目的,这样做的好处是无须修改command.send()这个部分的代码,因为所有的命令行已经在文本文件里预先设置好了。
不过这时新的问题又来了,每次配备不同型号的设备,我们必须手动修改open()函数所打开的配置文本文件,比如在给5500做配置的时候,我们要open(‘command_5500.txt’), 给6000做配置的时候,我们又要open(‘command_6000.txt’),这样一来二去修改配置脚本的做法大大缺乏灵活性。
如果说只有两种不同型号,不同命令格式的设备还能应付的话,那么当你的生产环境中有多种型号的设备,甚至其他厂商的设备,而你又接到任务要对所有这些设备同时修改某个共有的配置(比如网络新添加了某台服务器,要统一给所有设备修改AAA配置,亦或者网络新添加了某台NMS系统,要统一给所有设备修改SNMP配置),因为不同OS的配置命令完全不同,这时你就能体会到痛苦了。这时可以用到sys.argv来解决这个问题。
# -- coding: UTF-8 --
import paramiko
import time
# import getpass
import sys
username = input('Username: ')
password = input('Password: ')
# password = getpass.getpass('password: ')
ip_file = sys.argv[1]
cmd_file = sys.argv[2]
iplist = open(ip_file, 'r')
for line in iplist.readlines():
ip = line.strip()
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()
# 读取命令
cmdlist = open(cmd_file, 'r')
cmdlist.seek(0)
for line in cmdlist.readlines():
command.send(line + "\n")
time.sleep(2)
cmdlist.close()
output = command.recv(65535)
print(output.decode().strip())
print("\033[32;1m[*]配置结束[*]\033[0m")
iplist.close()
ssh_client.close
举个例子,可以在终端运行
$ python lab4.py ip_3750.txt cmd_3750.txt
这时argv = ['lab4.py', 'ip_3750.txt', 'cmd_3750.txt']
我们代码里的:
ip_file = sys.argv[1],此时也就等同于ip_file = ip_3750.txt
cmd_file = sys.argv[2], 此时也就等同于cmd_file = cmd_3750.txt
同理,如果这时我们在linux执行命令:
$ python lab4.py ip_3850.txt cmd_3850.txt
那么此时ip_file = ip_3850.txt, cmd_file = cmd_3850.txt
在网络设备数量超过千台甚至上万台的大型企业网中,难免会遇到某些设备管理IP不通,SSH连接失败的情况,设备数量越多,这种情况发生的几率越大。这个时候如果你想用Python批量配置所有的设备,就一定要注意这种情况,很有可能你的脚本跑了还不到一半就因为中间某一个连接不通的设备而停止了。比如说你有3000台交换机需要统一更改本地用户名和密码,前500台交换机的连通性都没问题,第501台交换机因为某个网络问题导致管理IP不可达,SSH连不上了,这个时候Python会返回一个socket.error:
socket.error: [Errno 10060] A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has
同样的问题也会发生在当你输入了错误的交换机用户名和密码之后,或者某些交换机和其他大部分交换机用户名和密码不一致的时候(因为我们只能输入一次用户名和密码,用户名/密码不一致会导致个别交换机无法登陆的情况发生),也许你会问大型企业网不都是统一配置AAA配合TACACS或者RADIUS做用户访问管理吗,怎么还会出现登陆账号和密码不一样的问题?历史服务器遗留下来的问题,这就出现了两套账号和密码的情况,后果就是使用paramiko来SSH登陆网络设备的python会返回认证异常
paramiko.ssh_exception.AuthenticationException:
要解决上述两个问题也很简单,这里我们可以用到Python中的try, except异常处理语句,来让你的脚本在遇到上述出现设备连通性故障以及用户认证无法通过的情况时,继续给后面的设备做配置,而不会因为某个故障的设备而停下来。
import paramiko
import time
import getpass
import sys
import socket
username = input('Username: ')
password = input('Password: ')
# password = getpass.getpass('password: ')
ip_file = sys.argv[1]
cmd_file = sys.argv[2]
switch_with_authentication_issue = []
switch_not_reachable = []
iplist = open(ip_file, 'r')
for line in iplist.readlines():
try:
ip = line.strip()
ssh_client = paramiko.SSHClient()
ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh_client.connect(hostname=ip, username=username, password=password, look_for_keys=False)
print("You have successfully connect to ", ip)
command = ssh_client.invoke_shell()
cmdlist = open(cmd_file, 'r')
cmdlist.seek(0)
for line in cmdlist.readlines():
command.send(line + "\n")
time.sleep(2)
cmdlist.close()
output = command.recv(65535)
print(output.decode().strip())
print("\033[32;1m[*]配置结束[*]\033[0m")
except paramiko.ssh_exception.AuthenticationException:
print("\033[31;1mUser authentication failed for \033[0m" + ip + ".")
switch_with_authentication_issue.append(ip)
except socket.error:
print(ip + "\033[31;1m is not reachable.\033[0m")
switch_not_reachable.append(ip)
iplist.close()
ssh_client.close()
print('\n\033[31;1m[+]User authentication failed for below switches: \033[0m')
for i in switch_with_authentication_issue:
print(i)
print('\n\033[31;1m[+]Below switches are not reachable: \033[0m')
for i in switch_not_reachable:
print(i)
像dis cu这种一个屏幕显示不了的,需要按回车才能显示全的操作
# 定义一个集合,每一屏的信息都追加到集合中
result_list = []
# 判断是否有--More--
command = "dis cu\n"
tn.write(command.encode('ascii'))
time.sleep(2)
while (True):
# 获取返回的结果
command_result = tn.read_very_eager().decode("ascii")
# 追加到集合中
result_list.append(command_result)
# 判断当前这一页是否 --More-- 结束
if ' ---- More ----' in command_result.strip():
tn.write(b"\n")
time.sleep(0.5)
else:
break
# 拼接集合
result_str = "\n".join(result_list)
print(result_str)
tn.close()
print("测试完毕")