简明Python开发教程(4):网络自动化运维的曙光

写在前面

本打算自动登陆一台路由器,执行查询配置指令,然后用正则表达式分析,获取该路由器的接口连接关系。
现在由于网络问题,导致无法直接telnet路由器,只能通过其他方式获取配置文件,如读取本地文件等。
由于时间关系,本教程修改如下:
第一部分通过telnet模块测试登陆、执行指令,登陆设备改为MME;
第二部分还是之前的配置文件分析,只是配置文件来源于本地已下载的配置文件。
同时大家也可以研究其他登陆网元等自动获取信息的方法,如指令平台等。


Python在自动运维方面也有很多第三方库,可以帮助我们自动ssh/telent设备、执行指令,分析结果等。
通常情况下Python自动运维在互联网公司应用更多,因为他们维护设备主要都是类似的linux服务器,操作统一。
而我们所面临的不同厂家不同设备的CT设备,Python自动运维相对较少,不过现在已经是ICT融合,目前引入的NFV技术有大量IT设备,Python自动运维可以发挥更多作用。
本教程还是想通过解决一个实际问题,分享Python自动运维的简单知识,更多的我也在学习中。

问题需求

日常我们维护着大量的设备,每个设备都有账号密码,账号密码是否过期?设备能否正常登陆?同时我们还需要执行大量指令并进行分析,判断设备是否正常。
今天我们的任务是,给定某设备IP地址,用户名和密码,完成以下任务:

  • 模拟telnet/ssh设备,可以测试账号可用性,设备连通性
  • 执行指令,返回结果,如查询配置文件
  • 分析配置文件,比如关注拓扑信息,可以学习Python正则表达式的基本知识;

代码实现

模拟telnet/ssh

Python有很多第三方库,实现ssh、ftp、telnet等命令交互,如pexpectparamiko等,不过这些我都还没时间认真研究,就用Python自带的telnetlib实现最简单的telnet功能,后续如果有需求再进一步学习。
模拟telnet的代码如下:

import telnetlib
host = "x.x.x.x"    #设备IP,将来可以从一个地址池中读取
user =b 'x'     #用户名密码
password =b 'x'
finsh = b"#"     #结束符号
tn = telnetlib.Telnet(host) #创建telnet连接
tn.read_until(b"login: ") #读取到给定字符串
tn.write(user + b"\n")  #输入用户名
tn.read_until(b"Password: ")
tn.write(password + b"\n")
tn.read_until(finsh)

注意在网管网换成可以IP测试,可以看到,python正在模拟人工登陆,我认为:

  • telnetlib.Telnet(host) 没有报错,说明设备网络通的,也开放了telnet端口;
  • tn.read_until(finsh) 执行完毕,说明账号密码正常,可以正常登陆

执行指令并返回结果

正常telnet设备后,我们准备执行指令,我们查询爱立信MME attach相关指标,指令如下:
attach | tail -10
我们使用Python执行指令,代码如下:

command =b "attach | tail -10" #待执行指令
tn.write(command + b"\n") #Python执行指令,模拟人工输入、回车
res = tn.read_until(finsh)  #python读取指令结果

tn.read_until(finsh) 可以理解为从执行上条指令开始,一直读取到finsh(这里是#,不同平台可能不一样,如~)结束。
我们可以查看res结果如下,说明已经正确完成指令结果读取。

image.png

分析本地配置文件

由于路由器暂时无法直接telnet,只能通过分析本地配置文件。其实对于上述MME的指令结果也可以继续分析,只是以下内容很早已经完成,实现思路都是一样的。

分析指令结果

配置文件可以分析很多东西,这里我们只分析路由器的接口数据,也就是配置了哪些接口?对端分别是哪些网元,接口IP是多少?
我们看下接口配置样例。

interface GigabitEthernet2/0/8.1055
 vlan-type dot1q 1055
 description To-[HZMxx]-[MHRD|2|4|11|B|1]-1G
 ip binding vpn-instance xx_xx_Media
 ip address x.x.x.149 255.255.255.252
 statistic enable

我们可以通过正则表达式匹配这段内容,并提取相应信息。

Python正则表达式re

关于Python正则表达式的知识非常多,可以写一本书,显然我写不出来。
我曾经看过一份不错的教程Python正则表达式指南,转自# AstralWind,可以研究下。
所谓正则表达式,就是给定一个模式字符串表达式patten,然后逐一匹配字符串,正如我们熟悉的*表示任何东西一样。
Python正则表达式示例,我想在一句英文中匹配是否存在hello。

import re  
# 将正则表达式编译成Pattern对象 
pattern = re.compile(r'hello')
s = pattern.search('hello world!') #调用search函数,查找
if s: #匹配成功为真
    print(s.group())
#以上是输出    
hello

Pattern对象是一个编译好的正则表达式,提供了很多方法使用,如match()、search()、findall()。
今天我们用findall()匹配设备接口信息,会以列表形式返回全部匹配的字串代码如下:

In[227]: import pandas as pd 
f = open(u'XXXCE06-HWNE40E_配置备份文件保存.txt','r')
conf = f.read() 
p_int= re.compile(r"interface GigabitEthernet(.*?)\n.*?description (.*?)\n")
ints = p_int.findall(conf)
#转换为DataFrame,以便后续输出报错
ints = pd.DataFrame(ints,columns = ['interface','description'])
ints['interface'] = ['GigabitEthernet'+i for i in list(ints['interface'])]
In[228]: ints.head()
Out[228]: 
               interface                             description
0  GigabitEthernet1/0/10          TO-[xxx]-[LSW01-GE0/0/13]_1G
1  GigabitEthernet1/0/23                     TO-xxx-1G-WANGGUAN
2   GigabitEthernet2/0/1  TO-[GDHIZ-xxx-HWNE40E]-GE2/0/1-1G
3   GigabitEthernet2/0/2  TO-[GDHIZ-xxx-HWNE40E]-GE2/0/2-1G
4   GigabitEthernet2/0/3         To-[HZMxx]-[MHRD|1|2|11|B|1]-1G

其中正则表达式"interface GigabitEthernet(.?)\n.?description (.*?)\n",
表示以interface GigabitEthernet开头,先匹配任意字符,直到换行\n;第一个括号内就是接口号interface;然后再忽略一些字符. *?,找到description ,后面对应对端名称,以换行符\n结束匹配。
这个写法直接返回()中匹配上的内容,如端口号、desc等,但是对于没有desc的端口则无法匹配命中。
整个配置文件findall()后可以全部匹配想要的接口号及对端信息。

更多接口信息

上面只匹配了有对端的GigabitEthernet接口信息,其实路由有很多接口类型,如vlan,还有子接口等,下面尝试写一个更全面的功能,能够分析所有接口信息。

#给定配置文件conf,返回全部接口信息
def get_intferaces(conf):
    p_int = re.compile(r'#\n(interface .*?)\n#',re.S)
    ints = p_int.findall(conf)
    cols = ['interface','is_shutdown','description','ipaddress','vpn-instance','vlan']
    rows = []
    for i in ints:
        name = i.split('interface ')[1].split('\n')[0]
        if ('NULL' not in name) & ('Virtual' not in name):
            if 'description ' in i:
                desc = i.split('description ')[1].split('\n')[0]
            else:
                desc = ''
            if 'ip address ' in i:
                ipaddress = i.split('ip address ')[1].split('\n')[0]
            else:
                ipaddress = ''     
            if 'ip binding vpn-instance ' in i:
                vpn = i.split('ip binding vpn-instance ')[1].split('\n')[0]
            else:
                vpn = ''  
            if 'port default vlan ' in i:
                vlan = i.split('port default vlan ')[1].split('\n')[0]
            else:
                vlan = ''
            if 'undo shutdown' in i:
                is_shutdow = 'undo shutdown'
            elif 'shutdown' in i:
                is_shutdow = 'shutdown'
            else:
                is_shutdow = '' #为空表示没有这个字段    
            row = [name,is_shutdow,desc,ipaddress,vpn,vlan]
            rows.append(row)
    interfaces = pd.DataFrame(rows,columns = cols)
    return interfaces

由于接口类型很多,不好用统一的正则表达式匹配,大家有兴趣可以用正则写,这里傻瓜式的if判断。


通过get_intferaces(conf)可以获取到全部接口信息。然后可以对这些接口信息进行处理,比如只保留在用端口,IP地址用子网表示等。

#只保留在用端口
interfaces = interfaces[~interfaces['is_shutdown'].isin(['shutdown'])]

由于对端描述不是特别规范,提取真实网元名称需要一些判断逻辑,不麻烦,同时如果知道全部IP地址信息,可以用子网进行匹配,更准确。


最终代码

整理上面代码,汇总如下,大家可以新建automain_demo.py,复制以下代码,测试是否正常。(需要内网环境,能够连上网元,目前也只能连上部分网元)

import telnetlib
import re
import pandas as pd

#在指定网元上执行指令,并返回指令结果
def doComd(host,user,password,finsh,commands):
    tn = telnetlib.Telnet(host)
    tn.read_until(b"login: ")
    tn.write(user +b "\n")
    tn.read_until(b"Password: ")
    tn.write(password + b"\n")    
    tn.read_until(finsh)   
    print(host+"登陆成功!")
    res = []
    #可以执行多条指令
    for command in commands:
        tn.write(command +b "\n")    
        res.append(tn.read_until(finsh))
    tn.close()
    return res
#给定配置文件conf,返回全部接口信息
def get_intferaces(conf):
    p_int = re.compile(r'#\n(interface .*?)\n#',re.S)
    ints = p_int.findall(conf)
    cols = ['interface','is_shutdown','description','ipaddress','vpn-instance','vlan']
    rows = []
    for i in ints:
        name = i.split('interface ')[1].split('\n')[0]
        if ('NULL' not in name) & ('Virtual' not in name):
            if 'description ' in i:
                desc = i.split('description ')[1].split('\n')[0]
            else:
                desc = ''
            if 'ip address ' in i:
                ipaddress = i.split('ip address ')[1].split('\n')[0]
            else:
                ipaddress = ''     
            if 'ip binding vpn-instance ' in i:
                vpn = i.split('ip binding vpn-instance ')[1].split('\n')[0]
            else:
                vpn = ''  
            if 'port default vlan ' in i:
                vlan = i.split('port default vlan ')[1].split('\n')[0]
            else:
                vlan = ''
            if 'undo shutdown' in i:
                is_shutdow = 'undo shutdown'
            elif 'shutdown' in i:
                is_shutdow = 'shutdown'
            else:
                is_shutdow = '' #为空表示没有这个字段    
            row = [name,is_shutdow,desc,ipaddress,vpn,vlan]
            rows.append(row)
    interfaces = pd.DataFrame(rows,columns = cols)
    interfaces = interfaces[~interfaces['is_shutdown'].isin(['shutdown'])]
    return interfaces
if __name__ == "__main__":  
    host = "x.x.x.x"    #设备IP,将来可以从一个地址池中读取
    user = b'x'     #用户名密码
    password =b 'x'
    finsh = b"#"     #结束符号
    command = ["hostname","dispaly current-config"]
    #result = doComd(host,user,password,finsh,command)
    f = open(u'GDHIZ-NGN-CE06-HWNE40E_配置备份文件保存.txt','r')
    result = f.read()
    #hostname = result[0]
    hostname = "test"
    conf = result[1]
    interface = get_intferaces(conf)
    interface.to_csv(hostname+'.csv',encoding = 'utf8',index = False)

注意换成真实的IP、用户名和密码既可,然后在命令行中运行测试。

image.png

可以发现,运行脚本后,新增test.csv文件:


image.png

上面这个脚本只是基础,还可以扩展出很多功能,如稍微修改了,就可以遍历一个IP地址池,大家可以试试。
通过配置文件分析的信息更全更准确,可以有效帮助提升工作效率,同时还可以执行其他指令,并借助Python强大的分析能力进行处理。
Python也可以实现网络自动运维,而且非常简单易学,让我们能够便捷维护海量设备。

这篇写的很赶,这方面内容以前掌握比较少,今天在这里只是抛砖引玉,说与神器Python是可以帮我们自动运维的,希望和大家一起学习进步。
下一篇,我想分享下Python在网络爬虫方面的简单应用——简明Python开发教程(5):用爬虫实现个性化搜索引擎

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 201,681评论 5 474
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,710评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 148,623评论 0 334
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,202评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,232评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,368评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,795评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,461评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,647评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,476评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,525评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,226评论 3 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,785评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,857评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,090评论 1 258
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,647评论 2 348
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,215评论 2 341

推荐阅读更多精彩内容