写在前面
本想着自动登录一台路由器, 去执行查询配置指令, 接着运用正则表达式进行分析, 从而获取那台路由器的接口连接关系。
此刻, 鉴于网络方面存在问题, 致使没办法直接连接路由器, 仅仅能够借助别的方式去获取配置文件, 就像读取本地的文件之类的。
由于时间关系,本教程修改如下:
第一部分通过模块测试登陆、执行指令,登陆设备改为MME;
第二部分析依旧是之前的配置文件, 只不过配置文件源自本地已经下载的配置文件。
与此同时, 大家还能够去钻研别的登录网元之类的自动获取信息的办法, 像是指令平台等等。
存在诸多第三方库, 于自动运维范畴, 能辅助我们去自动处理ssh/设备相关事宜, 还能够去执行指令, 且可以对结果加以分析, 的, 的。
互联网公司应用自动运维更多, 这是在通常情况下, 由于他们维护的设备主要都是类似的linux服务器, 且操作统一。
而我们面对的, 是不同厂家提供的不同设备的CT设备, 其自动运维是相对较少的, 虽如此, 现在已然是ICT融合这般状况了, 当前所引入的NFV技术, 有着大量的IT设备, 在这种情形下, 自动运维能够起到更多的作用。
本教程旨在借助解决一个实际问题, 来分享自动运维的简单知识, 至于更多的内容, 目前我仍处于学习之中。
问题需求
日常之中, 我们对大量设备予以维护, 其中每个设备均设有账号密码, 那这账号密码是否已然过期? 设备可否能够正常登陆? 与此同时, 我们还需执行大量指令, 进而开展分析工作, 而后判断设备状态是否正常。
今日项我们的职责是, 给出某设备 IP 地址, 用户名以及密码, 达成以下事务:
模拟/ssh设备,可以测试账号可用性,设备连通性
执行指令,返回结果,如查询配置文件
解析配置文档, 像是留意拓扑资讯, 能够研习正则表达式的基础要点。
代码实现
模拟/ssh
存在着数量不少的第三方库, 它们能够达成ssh、ftp等命令的交互, 像、等这样, 然而这些我一直都没有抽出时间来认真地进行研究, 所以就运用自带的去实现最为简单的功能, 后续要是出现了需求再进一步地去学习。
模拟的代码如下:
主机等于 “x.x.x.x”, 这是设备的 IP, 未来能够从一个地址池里进行读取。
user =b 'x'#用户名密码
=b 'x'
finsh = b"#"#结束符号
tn = .(host) #创建连接
tn.(b"login: ") #读取到给定字符串
tn进行写入操作, 写入的内容是, 用户再加字节类型的换行符, 此操作是为了输入用户名。
tn.(b": ")
tn.write( + b"\n")
tn.(finsh)
留意于网管网进行更换, 使之能够以 IP 展开测试, 能够瞧见, 正处于模拟人工登录的状态, 我秉持这样的观点:
那个被称作(host)的, 没有出现报错的情况, 这表明设备的网络是畅通的, 并且还开放了端口。
该任务编号(完成), 表明账号密码处于正常状态, 致使能够正常进行登录操作。
执行指令并返回结果
我们在正常设备之后, 准备好去执行指令, 我们去查询与爱立信MME相关的指标, 指令是这样的:
| tail -10
我们使用执行指令,代码如下:
=b " | tail -10" #待执行指令
执行指令, 进行模拟人工输入, 之后进行回车操作, 具体是通过tn.write( + b"\n")来达成。
res = tn.(finsh) #读取指令结果
能够理解成, 从执行上条指令起始, 一致持续读取, 直至 finsh(此处为#, 于不同平台间或许存在差异, 像~)终结为此。
我们可以查看res结果如下,说明已经正确完成指令结果读取。
image.png
分析本地配置文件
只因路由器当下没办法直接进行, 所以只能借助对本地配置文件予以分析。实际上, 针对上述MME的指令结果而言, 同样能够持续展开分析, 只不过以下这些内容在很早之前就已完成, 并且达成的思路都是相同的。
分析指令结果
配置文件能够针对诸多内容展开剖析, 此处我们仅仅聚焦于对路由器的接口数据予以分析, 这即意味着要去探究究竟配置了何方接口, 与之相对的另一端各自是哪些网元, 以及接口的 IP 具体数值是多少?
我们看下接口配置样例。
/0/8.1055
vlan-type dot1q 1055
To--
MHRD|2|4|11|B|1
-1G
ip vpn-
ip x.x.x.149 255.255.255.252
我们可以通过正则表达式匹配这段内容,并提取相应信息。
正则表达式re
关于正则表达式的知识非常多,可以写一本书,显然我写不出来。
有一份正则表达式指南教程, 我曾看过, 它不错, 我当时把它从 # 转来的, 是可以去研究一番的。
那个被称为正则表达式的东西, 是要先给出一个模式字符串表达式, 接着再逐个去匹配字符串, 就如同我们所熟知的, *代表着任何的事物一般。
正则表达式示例,我想在一句英文中匹配是否存在hello。
re
# 将正则表达式编译成对象
= pile(r'hello')
s等于一个点, 后面跟着括号, 括号里是单引号, 单引号里是hello world! , 这是在调用函数, 进行查找。
if s: #匹配成功为真
print(s.group())
#以上是输出
hello
以编译好的正则表达式作为对象, 该对象存在诸多可用于使用的方法, 像match()方法, 还有其他未明确写出的方法, 比如()方法, 以及()另一种方法。
今日, 我们借助()去匹配设备接口信息, 其会以列表的形式, 将全部匹配的字串代码如下返回:
In: as pd
f被打开, 其要打开的文件路径为u'-配置备份文件保存.txt', 使用的读取模式是'r'并有符号。
conf = f.read()
ints = p_int.(conf)
#转换为,以便后续输出报错
ints = pd.(ints, =
'',''
ints
''
''
In: ints.head()
Out:
0 /0/10TO--
LSW01-GE0/0/13
_1G
1 /0/23TO-xxx-1G-
2 /0/1 TO-
GDHIZ-xxx-
-GE2/0/1-1G
3 /0/2 TO-
GDHIZ-xxx-
-GE2/0/2-1G
4 /0/3To--
MHRD|1|2|11|B|1
-1G
其中正则表达式" (.?)\n.? (.*?)\n",
以表示起头, 先去匹配任意字符, 一直到换行符\n为止;第一个括号当中的就是接口号;接着再去忽略某些字符.*?, 找到, 之后对应着的就是对端名称, 以换行符\n来结束匹配。
此种写法, 会直接返回, 在()里匹配上的内容, 举个例子, 像是端口号、desc之类的, 然而呢, 对于那些没有desc的端口而言, 就没办法匹配命中了。
整个配置文件()后可以全部匹配想要的接口号及对端信息。
更多接口信息
上面仅仅匹配了存在对端的接口信息, 实际上, 路由具备诸多接口类型, 像是vlan, 以及子接口等等, 接下来尝试编写一个更为全面的功能, 此功能能够剖析所有接口信息。
#给定配置文件conf,返回全部接口信息
def (conf):
P_int,等于, 通过, pile函数, 以, r'#\n(.*?)\n#为参数, 并且, re.S为标志, 来进行, 相关操作。
ints = p_int.(conf)
cols =
'','','','','vpn'与'vlan'各有不同,各有不同。
rows =
for i in ints:
把i通过按空格进行拆分运算, 之后按照换行符对得到的结果再进行拆分运算, 最后将所得结果赋予name。
如果, “NULL”不在名为name之中, 并且, 空字符串也不在名为name之中。
if ' ' in i:
先将i通过按空格进行分割, 之后再对分割后的结果按换行符进行分割, 所得到的结果赋值给desc。
else:
desc = ''
if 'ip ' in i:
我们使用`i`, 对其进行以`'ip '`为分隔的拆分操作,之后再对拆分后的结果进行以`'\n'`为分隔的拆分操作。
else:
= ''
if 'ip vpn- ' in i:
第1步: i进行以'ip vpn-'为分隔的拆分, 第2步: 对第1步拆分后的结果进行以'\n'为分隔的拆分, 最终结果等于vpn。
else:
vpn = ''
如果, 在“port vlan ”之中, 存在于“i”里面, 有这种情况。
vlan等于, i通过分割字符串port vlan得到的结果, 再次通过分割字符串\n得到的东西。
else:
vlan = ''
if 'undo ' in i:
= 'undo '
elif '' in i:
= ''
else:
= '' #为空表示没有这个字段
row =
name,,desc,,vpn,vlan
rows.(row)
= pd.(rows, = cols)
于接口类型繁杂多样, 难以借助统一正则表达式予以匹配, 若诸位有那般兴致, 可尝试运用正则进行编写, 此处则是采用了极为简单直接的if判断方式。
经由(conf)能够取得涵盖所有的接口信息, 随后能够针对这些接口信息予以处理, 比如说唯保留处于使用状态的端口, 将IP地址以子网的形式予以呈现等。
#只保留在用端口
=
~
''
.isin(
''
因对端描述并非特别规范, 提取真实网元名称需一些判断逻辑, 这并不麻烦, 与此同时, 若知晓全部IP地址信息, 可用子网进行匹配, 如此更为准确。
最终代码
将上面的代码进行整理, 汇总呈现如下, 大家能够新建.py, 把以下代码予以复制, 测试其是否正常。(此需要处于内网环境, 并且能够连接上网元, 目前所能连接的也仅仅是部分网元)
re
as pd
#在指定网元上执行指令,并返回指令结果
def (host,user,,finsh,):
tn = .(host)
tn.(b"login: ")
tn.write(user +b "\n")
tn.(b": ")
tn.write( + b"\n")
tn.(finsh)
print(host+"登陆成功!")
res =
#可以执行多条指令
for in :
tn.write( +b "\n")
res.(tn.(finsh))
tn.close()
res
#给定配置文件conf,返回全部接口信息
def (conf):
p_int = pile(r'#\n( .*?)\n#',re.S)
ints = p_int.(conf)
cols =
'','','','','vpn-','vlan'
rows =
for i in ints:
name = i.split(' ').split('\n')
if ('NULL' not in name) & ('' not in name):
if ' ' in i:
desc = i.split(' ').split('\n')
else:
desc = ''
if 'ip ' in i:
= i.split('ip ').split('\n')
else:
= ''
if 'ip vpn- ' in i:
vpn = i.split('ip vpn- ').split('\n')
else:
vpn = ''
if 'port vlan ' in i:
vlan = i.split('port vlan ').split('\n')
else:
vlan = ''
if 'undo ' in i:
= 'undo '
elif '' in i:
= ''
else:
= '' #为空表示没有这个字段
row =
name,,desc,,vpn,vlan