前言
好久没有更新博客了,最近私人的事情比较多,所以一直没有时间来更新。正好十一假期后比较空闲,所以又继续自学Python。前段时间在工作中遇到网络设备配置丢失的问题,还好之前有备份的配置,正好这次研究下使用Python备份网络设备的配置信息,实现定期备份,这样就不怕配置丢失的问题了。
pexpect模块
首先肯定要使用到pexpect模块了,这样只要登录网络设备,执行显示配置信息命令,就可以将配置信息统统抓取下来了。
一个简单的pexpect模块示例:
# This connects to the openbsd ftp site and
# downloads the recursive directory listing.
import pexpect
child = pexpect.spawn('ftp ftp.openbsd.org')
child.expect('Name .*: ')
child.sendline('anonymous')
child.expect('Password:')
child.sendline('noah@example.com')
child.expect('ftp> ')
child.sendline('lcd /tmp')
child.expect('ftp> ')
child.sendline('cd pub/OpenBSD')
child.expect('ftp> ')
child.sendline('get README')
child.expect('ftp> ')
child.sendline('bye')
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# This connects to the openbsd ftp site and
# downloads the recursive directory listing.
importpexpect
child=pexpect.spawn('ftp ftp.openbsd.org')
child.expect('Name .*: ')
child.sendline('anonymous')
child.expect('Password:')
child.sendline('noah@example.com')
child.expect('ftp> ')
child.sendline('lcd /tmp')
child.expect('ftp> ')
child.sendline('cd pub/OpenBSD')
child.expect('ftp> ')
child.sendline('get README')
child.expect('ftp> ')
child.sendline('bye')
通过上面的示例可以看到pexpect模块的基本使用方法:
基本使用方法
1、首先需要import一下pexpect模块:
import pexpect
2、通过spawn()类来新建一个你需要执行远程命令的连接:
child = pexpect.spawn('CMD#这里是你需要执行的交互式命令')
3、通过expect()方法来捕获需要匹配的交互式信息:(如:提示需要输入用户名“login:”)
child.expect('Name .*: ')
4、通过sendline()方法来执行交互式窗口需要输入的信息:(如:输入用户名或执行退出命令)
child.sendline('anonymous') or child.sendline('bye')
以上为基本的一些基本的方法,还有一些更高级的方法和类,如:run方法。这里就不过多的说明了,有兴趣的化可以看官方的说明文档。传送门
抓取配置信息
上面说了怎么使用pexpect模块,下面就通过几段代码来说明,我是获取设备的配置信息。(这里因为我测试环境中绝大部分的设备都是H3C的,所以查看配置信息命令就默认使用了H3C的命令,如果不是同一厂商,则需要对设备厂商进行辨别,并执行相应的命令。)
针对Telnet方式的配置抓取代码:
def telnet(name,ip,passwd):
child = pexpect.spawn('telnet %s' % ip)
fout = open('/home/zxw/backup_conf/' + today + '/' + '%s_%s.txt' % (name, ip), 'wb+')
child.logfile = fout
child.expect(['login:', 'Username'])
child.sendline('admin')
child.expect('(?i)ssword:')
child.sendline('%s' % passwd)
child.expect('' % name)
child.sendline('display current-configuration')
while index(child) == 0:
child.send(' ')
child.sendline('quit')
1
2
3
4
5
6
7
8
9
10
11
12
13
deftelnet(name,ip,passwd):
child=pexpect.spawn('telnet %s'%ip)
fout=open('/home/zxw/backup_conf/'+today+'/'+'%s_%s.txt'%(name,ip),'wb+')
child.logfile=fout
child.expect(['login:','Username'])
child.sendline('admin')
child.expect('(?i)ssword:')
child.sendline('%s'%passwd)
child.expect(''%name)
child.sendline('display current-configuration')
whileindex(child)==0:
child.send(' ')
child.sendline('quit')
针对SSH方式的配置抓取代码:
def ssh(name,ip,passwd):
child = pexpect.spawn('ssh admin@%s' % ip)
fout = open('/home/zxw/backup_conf/' + today + '/' + '%s_%s.txt' % (name, ip), 'wb+')
child.logfile = fout
child.expect('admin@%s\'s password:' % ip)
child.sendline('%s' % passwd)
child.expect('' % name)
child.sendline('display current-configuration')
while index(child) == 0:
child.send(' ')
child.sendline('quit')
1
2
3
4
5
6
7
8
9
10
11
defssh(name,ip,passwd):
child=pexpect.spawn('ssh admin@%s'%ip)
fout=open('/home/zxw/backup_conf/'+today+'/'+'%s_%s.txt'%(name,ip),'wb+')
child.logfile=fout
child.expect('admin@%s\'s password:'%ip)
child.sendline('%s'%passwd)
child.expect(''%name)
child.sendline('display current-configuration')
whileindex(child)==0:
child.send(' ')
child.sendline('quit')
上面两段基本一样,只是pexpect.spawn这里区分了远程的方式。这里还用到了一个自定义的函数index()来对一般远程登录时需要手动下翻的“—-more—-”来进行自动翻页,附上index()函数代码:
def index(child):
name1 = '---- More ----'
index = child.expect([name1, '' % sw_name])
return index
1
2
3
4
defindex(child):
name1='---- More ----'
index=child.expect([name1,''%sw_name])
returnindex
获取设备登录信息
上面说过了怎么获取设备的配置信息,但是既然需要实现自动备份功能,而且在工作中我们肯定也管理着众多的网络设备,不可能针对每个设备都执行下上面的命令,所以批量的执行是必须的。我这里采用了读取配置文件的方式进行获取设备的登录信息,这样只要一个for...in循环就可以依次执行完成备份操作了。
先是配置文件的格式:
[标签] #用于获取总的设备列表,便于后面使用for...in循环
name = 设备名称
ip = 设备ip地址
passwd = 登录密码
remote = 设备远程登录方式 telnet或ssh
1
2
3
4
5
[标签]#用于获取总的设备列表,便于后面使用for...in循环
name=设备名称
ip=设备ip地址
passwd=登录密码
remote=设备远程登录方式telnet或ssh
如:
[FW10508]
name = S10508_FireWall
ip = 10.10.10.1
passwd = password
remote = ssh
[SW10508]
name = SW_S10508
ip = 10.10.10.2
passwd = password
remote = ssh
[12508]
name = SW-S12508X
ip = 10.10.10.3
passwd = password
remote = telnet
[YW6800]
name = 6800-YeWu
ip = 10.10.10.4
passwd = password
remote = telnet
[WAN5100]
name = WAN_Conn_Switch
ip = 10.10.10.5
passwd = password
remote = telnet
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
[FW10508]
name=S10508_FireWall
ip=10.10.10.1
passwd=password
remote=ssh
[SW10508]
name=SW_S10508
ip=10.10.10.2
passwd=password
remote=ssh
[12508]
name=SW-S12508X
ip=10.10.10.3
passwd=password
remote=telnet
[YW6800]
name=6800-YeWu
ip=10.10.10.4
passwd=password
remote=telnet
[WAN5100]
name=WAN_Conn_Switch
ip=10.10.10.5
passwd=password
remote=telnet
获取设备登录信息:
def get_cfg(sw_tag):
global sw_name, sw_ip, sw_passwd, sw_remote
#config = configparser.ConfigParser(allow_no_value = True)
#config.read(ini_file)
sw_name = config.get(sw_tag, 'name')
sw_ip = config.get(sw_tag, 'ip')
sw_passwd = config.get(sw_tag, 'passwd')
sw_remote = config.get(sw_tag, 'remote')
return sw_name, sw_ip, sw_passwd, sw_remote
config_path = './config.ini'
if os.path.exists(config_path): #判断配置文件是否存在
config = configparser.ConfigParser(allow_no_value = True)
config.read(config_path)
dev_list = config.sections() #获取设备列表
else:
print('Missing configuration files')
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
defget_cfg(sw_tag):
globalsw_name,sw_ip,sw_passwd,sw_remote
#config = configparser.ConfigParser(allow_no_value = True)
#config.read(ini_file)
sw_name=config.get(sw_tag,'name')
sw_ip=config.get(sw_tag,'ip')
sw_passwd=config.get(sw_tag,'passwd')
sw_remote=config.get(sw_tag,'remote')
returnsw_name,sw_ip,sw_passwd,sw_remote
config_path='./config.ini'
ifos.path.exists(config_path):#判断配置文件是否存在
config=configparser.ConfigParser(allow_no_value=True)
config.read(config_path)
dev_list=config.sections()#获取设备列表
else:
print('Missing configuration files')
批量执行备份
实现上面两个功能后,就可以对设备配置信息进行批量备份了:
for device in dev_list:
get_cfg(device)
if sw_remote == 'ssh':
if test_port(sw_ip,22) == 0:
ssh(sw_name,sw_ip,sw_passwd)
print(': finished' % sw_name)
else:
print('can not connect deivce:' % sw_name)
elif sw_remote == 'telnet':
if test_port(sw_ip,23) == 0:
telnet(sw_name,sw_ip,sw_passwd)
print(': finished' % sw_name)
else:
print('can not connect deivce:' % sw_name)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
fordeviceindev_list:
get_cfg(device)
ifsw_remote=='ssh':
iftest_port(sw_ip,22)==0:
ssh(sw_name,sw_ip,sw_passwd)
print(': finished'%sw_name)
else:
print('can not connect deivce:'%sw_name)
elifsw_remote=='telnet':
iftest_port(sw_ip,23)==0:
telnet(sw_name,sw_ip,sw_passwd)
print(': finished'%sw_name)
else:
print('can not connect deivce:'%sw_name)
这里我还对设备是否能够远程连接进行了测试,避免因为配置文件中某些设备无法远程连接导致其他设备备份失败:
def test_port(ip,port):
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.settimeout(3)
host = (ip,port)
try:
s.connect(host)
s.close()
return 0
except Exception as e:
s.close()
return 1
1
2
3
4
5
6
7
8
9
10
11
deftest_port(ip,port):
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.settimeout(3)
host=(ip,port)
try:
s.connect(host)
s.close()
return0
exceptExceptionase:
s.close()
return1
压缩备份
定期备份配置文件带来最明显的问题就是磁盘空间的增长,所以压缩是最好的选择。
def zip_dir(dir_name, zipfile_name):
f = zipfile.ZipFile(zipfile_name, 'w', zipfile.ZIP_DEFLATED)
for root, dirs, files in os.walk(dir_name):
for name in files:
f.write(os.path.join(root, name))
os.remove(os.path.join(root, name))
f.close()
os.rmdir(path)
zipfile_name = path + '.zip'
try:
zip_dir(path, zipfile_name)
print('compress the backup files successd.')
except IOError as e:
print(e)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
defzip_dir(dir_name,zipfile_name):
f=zipfile.ZipFile(zipfile_name,'w',zipfile.ZIP_DEFLATED)
forroot,dirs,filesinos.walk(dir_name):
fornameinfiles:
f.write(os.path.join(root,name))
os.remove(os.path.join(root,name))
f.close()
os.rmdir(path)
zipfile_name=path+'.zip'
try:
zip_dir(path,zipfile_name)
print('compress the backup files successd.')
exceptIOErrorase:
print(e)
这里在将文件加入压缩文件后,就直接进行删除操作,将备份目录及目录下文件全部删除。
实现效果
执行成功显示:
--------------------2018-10-23--------------------
start backup task ...
: finished.
: finished.
: finished.
: finished.
: finished.
: finished.
compress the backup files successd.
finished backup task.
1
2
3
4
5
6
7
8
9
10
--------------------2018-10-23--------------------
startbackuptask...
:finished.
:finished.
:finished.
:finished.
:finished.
:finished.
compressthebackupfilessuccessd.
finishedbackuptask.
查看备份目录:
[root@localhost backup_conf]# ll -h
total 28K
dr----x--t. 2 root root 194 Oct 23 14:14 20181012
dr----x--t. 2 root root 194 Oct 15 17:27 20181015
-rw-r--r--. 1 root root 27K Oct 23 15:48 20181023.zip #这里为新创建的备份压缩文件
1
2
3
4
5
[root@localhostbackup_conf]# ll -h
total28K
dr----x--t.2rootroot194Oct2314:1420181012
dr----x--t.2rootroot194Oct1517:2720181015
-rw-r--r--.1rootroot27KOct2315:4820181023.zip#这里为新创建的备份压缩文件
还有一个重要的配置,就是将脚本加入crontab中,定期执行:
1 0 * * 6 root python3 /home/zxw/pytest/backup_conf/backup_swconfig.py >> output.log
1
10**6rootpython3/home/zxw/pytest/backup_conf/backup_swconfig.py>>output.log
查看log文件:
--------------------2018-10-23--------------------
start backup task ...
: finished.
: finished.
: finished.
: finished.
: finished.
: finished.
compress the backup files successd.
finished backup task.
1
2
3
4
5
6
7
8
9
10
--------------------2018-10-23--------------------
startbackuptask...
:finished.
:finished.
:finished.
:finished.
:finished.
:finished.
compressthebackupfilessuccessd.
finishedbackuptask.
总结
编辑前
这里也只是实现了一些我目前需要的基本功能,后面也可能基于这个代码再增加一些更高级而且实用的功能,比如说跨厂商的。还有就是对备份文件的压缩打包,节省存储空间。
这里我实现了对备份文件的压缩打包,节省了存储空间。
完整代码
附上完整的代码:
#!/usr/bin/python
#coding:utf-8
# @Author: zhangxw
# @Blog: www.conum.cn
import pexpect
import sys
import datetime
import os
import configparser
import socket
import zipfile
def get_cfg(sw_tag):
global sw_name, sw_ip, sw_passwd, sw_remote
#config = configparser.ConfigParser(allow_no_value = True)
#config.read(ini_file)
sw_name = config.get(sw_tag, 'name')
sw_ip = config.get(sw_tag, 'ip')
sw_passwd = config.get(sw_tag, 'passwd')
sw_remote = config.get(sw_tag, 'remote')
return sw_name, sw_ip, sw_passwd, sw_remote
def index(child):
name1 = '---- More ----'
index = child.expect([name1, '' % sw_name])
return index
def telnet(name,ip,passwd):
#try:
child = pexpect.spawn('telnet %s' % ip)
fout = open('/home/zxw/backup_conf/' + today + '/' + '%s_%s.txt' % (name, ip), 'wb+')
child.logfile = fout
child.expect(['login:', 'Username'])
child.sendline('admin')
child.expect('(?i)ssword:')
child.sendline('%s' % passwd)
child.expect('' % name)
child.sendline('display current-configuration')
while index(child) == 0:
child.send(' ')
child.sendline('quit')
def ssh(name,ip,passwd):
#try:
child = pexpect.spawn('ssh admin@%s' % ip)
fout = open('/home/zxw/backup_conf/' + today + '/' + '%s_%s.txt' % (name, ip), 'wb+')
child.logfile = fout
child.expect('admin@%s\'s password:' % ip)
child.sendline('%s' % passwd)
child.expect('' % name)
child.sendline('display current-configuration')
while index(child) == 0:
child.send(' ')
child.sendline('quit')
def test_port(ip,port):
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.settimeout(3)
host = (ip,port)
try:
s.connect(host)
s.close()
return 0
except Exception as e:
s.close()
return 1
def zip_dir(dir_name, zipfile_name):
f = zipfile.ZipFile(zipfile_name, 'w', zipfile.ZIP_DEFLATED)
for root, dirs, files in os.walk(dir_name):
for name in files:
f.write(os.path.join(root, name))
os.remove(os.path.join(root, name))
f.close()
os.rmdir(path)
today = datetime.date.today().strftime('%Y%m%d')
print('--------------------' + datetime.date.today().strftime('%Y-%m-%d') + '--------------------')
print('start backup task ... ')
path = '/home/zxw/backup_conf/' + today
if os.path.exists(path) != True:
os.mkdir(path,777)
config_path = './config.ini'
if os.path.exists(config_path):
config = configparser.ConfigParser(allow_no_value = True)
config.read(config_path)
dev_list = config.sections()
else:
print('Missing configuration files')
for device in dev_list:
get_cfg(device)
if sw_remote == 'ssh':
if test_port(sw_ip,22) == 0:
ssh(sw_name,sw_ip,sw_passwd)
print(': finished.' % sw_name)
else:
print('can not connect deivce:.' % sw_name)
elif sw_remote == 'telnet':
if test_port(sw_ip,23) == 0:
telnet(sw_name,sw_ip,sw_passwd)
print(': finished.' % sw_name)
else:
print('can not connect deivce:.' % sw_name)
zipfile_name = path + '.zip'
try:
zip_dir(path, zipfile_name)
print('compress the backup files successd.')
except IOError as e:
print(e)
print('finished backup task.')
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
#!/usr/bin/python
#coding:utf-8
# @Author: zhangxw
# @Blog: www.conum.cn
importpexpect
importsys
importdatetime
importos
importconfigparser
importsocket
importzipfile
defget_cfg(sw_tag):
globalsw_name,sw_ip,sw_passwd,sw_remote
#config = configparser.ConfigParser(allow_no_value = True)
#config.read(ini_file)
sw_name=config.get(sw_tag,'name')
sw_ip=config.get(sw_tag,'ip')
sw_passwd=config.get(sw_tag,'passwd')
sw_remote=config.get(sw_tag,'remote')
returnsw_name,sw_ip,sw_passwd,sw_remote
defindex(child):
name1='---- More ----'
index=child.expect([name1,''%sw_name])
returnindex
deftelnet(name,ip,passwd):
#try:
child=pexpect.spawn('telnet %s'%ip)
fout=open('/home/zxw/backup_conf/'+today+'/'+'%s_%s.txt'%(name,ip),'wb+')
child.logfile=fout
child.expect(['login:','Username'])
child.sendline('admin')
child.expect('(?i)ssword:')
child.sendline('%s'%passwd)
child.expect(''%name)
child.sendline('display current-configuration')
whileindex(child)==0:
child.send(' ')
child.sendline('quit')
defssh(name,ip,passwd):
#try:
child=pexpect.spawn('ssh admin@%s'%ip)
fout=open('/home/zxw/backup_conf/'+today+'/'+'%s_%s.txt'%(name,ip),'wb+')
child.logfile=fout
child.expect('admin@%s\'s password:'%ip)
child.sendline('%s'%passwd)
child.expect(''%name)
child.sendline('display current-configuration')
whileindex(child)==0:
child.send(' ')
child.sendline('quit')
deftest_port(ip,port):
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.settimeout(3)
host=(ip,port)
try:
s.connect(host)
s.close()
return0
exceptExceptionase:
s.close()
return1
defzip_dir(dir_name,zipfile_name):
f=zipfile.ZipFile(zipfile_name,'w',zipfile.ZIP_DEFLATED)
forroot,dirs,filesinos.walk(dir_name):
fornameinfiles:
f.write(os.path.join(root,name))
os.remove(os.path.join(root,name))
f.close()
os.rmdir(path)
today=datetime.date.today().strftime('%Y%m%d')
print('--------------------'+datetime.date.today().strftime('%Y-%m-%d')+'--------------------')
print('start backup task ... ')
path='/home/zxw/backup_conf/'+today
ifos.path.exists(path)!=True:
os.mkdir(path,777)
config_path='./config.ini'
ifos.path.exists(config_path):
config=configparser.ConfigParser(allow_no_value=True)
config.read(config_path)
dev_list=config.sections()
else:
print('Missing configuration files')
fordeviceindev_list:
get_cfg(device)
ifsw_remote=='ssh':
iftest_port(sw_ip,22)==0:
ssh(sw_name,sw_ip,sw_passwd)
print(': finished.'%sw_name)
else:
print('can not connect deivce:.'%sw_name)
elifsw_remote=='telnet':
iftest_port(sw_ip,23)==0:
telnet(sw_name,sw_ip,sw_passwd)
print(': finished.'%sw_name)
else:
print('can not connect deivce:.'%sw_name)
zipfile_name=path+'.zip'
try:
zip_dir(path,zipfile_name)
print('compress the backup files successd.')
exceptIOErrorase:
print(e)
print('finished backup task.')