银狐NetDevOps-网络运维python篇之NETCONF(
前言
银狐DevNet系列会持续将网络运维工作中python的应用进行场景化的分享,因为每个单独的模块网上都有详细的教学,这里就不深入讲解模块基础了,内容主要以思路和示例为主,并将碰到的问题汇总提出注意事项。
主要是因为网络工程师和网络运维工作者编程基础不强,加上网上对于这个领域的python资料又少,传统的分享方式(每个章节仅单纯分享一个知识点)对于很多网工来说各个知识点相对独立且割裂的,很难进行一个知识的融合,现实工作中也很难直接应用,大家学习的难度就会很大,也会导致大部分人刚入门就放弃。所以我将这些内容进行场景化,根据特定场景由浅入深不断优化,从而带出更多知识点,希望对大家有所帮助。
这些分享都是我本人真实的学习路径,一方面是帮助自己梳理网络自动化相关知识,另一方面也希望可以通过分享我微不足道的学习过程和实战经验,帮助更多想要了解NetDevOps而苦于没有资料和环境被劝退的人,进而找到同行之人互相交流、互相提升。
1、场景分析:
NETCONF协议对于传统网工来说是噩梦,看到XML编码就直接劝退了,但对于普通网工来说依然有适用的场景。平时我们批量操作无非就是增删改查,针对设备增删改,个人建议直接使用python的netmiko模块,非常方便,也符合传统网工输入CLI的习惯。但对于查就需要区分场景了。
当我们需要查询设备某些配置或者状态时,常规做法就是输入CLI,然后在返回结果中查看想要的内容。使用python进行跑批操作时也是通过paramiko、netmiko这类模块直接SSH上设备,然后输入CLI命令,在根据回显内容获取想要的数据。这里分几种情况,如果我们想获取设备配置文件,直接通过netmiko传递show config命令,其实是没什么问题的,因为这个回显的内容是不需要做任何处理的。
那我们换一个场景,1000台网络设备,需要批量获取所有设备的设备名称、型号、版本号、补丁,并将内容存储起来时该怎么办呢?这里有3中方法。
方法一:
使用paramiko或者netmiko批量登录网络设备,并输入CLI(disp ver),将如下回显内容自行处理。
Huawei Versatile Routing Platform Software
VRP (R) software, Version 8.180 (CE6855HI V200R005C10SPC800)
Copyright (C) 2012-2018 Huawei Technologies Co., Ltd.
HUAWEI CE6855-48S6Q-HI uptime is 421 days, 13 hours, 24 minutes
Patch Version: V200R005SPH016
CE6855-48S6Q-HI(Master) 1 : uptime is 421 days, 13 hours, 22 minutes
StartupTime 2020/03/24 18:35:47+08:00
Memory Size : 2048 M bytes
Flash Size : 1024 M bytes
CE6855-48S6Q-HI version information
1\. PCB Version : CEM48S6QP05 VER B
2\. MAB Version : 1
3\. Board Type : CE6855-48S6Q-HI
4\. CPLD1 Version : 102
5\. BIOS Version : 433
显然,处理以上数据会非常麻烦
方法二:
使用netmiko这类SSH模块配合textFSM模板(结合ntc-template)进行操作(方法以后讲解),直接回显如下列表内容。
[{'model': 'CE6855-48S6Q-HI',
'product_version': 'CE6855HI V200R005C10SPC800',
'uptime': '421 days, 9 hours, 24 minutes ',
'vrp_version': '8.180'}]
可以看到,非常方便。
但问题就是这方面的模板cisco和juniper等国外厂商比较充足,基本涵盖了常见需求,国内华为只有4个模板,如下所示。
-rw-rw-r-- 1 yydd yydd 483 May 8 10:21 huawei_vrp_display_interface_brief.textfsm
-rw-rw-r-- 1 yydd yydd 1811 May 8 10:21 huawei_vrp_display_lldp_neighbor.textfsm
-rw-rw-r-- 1 yydd yydd 390 May 8 10:21 huawei_vrp_display_temperature.textfsm
-rw-rw-r-- 1 yydd yydd 217 May 8 10:21 huawei_vrp_display_version.textfsm
也就是只有4个命令回显可以直接返回处理好的列表,显然是不够用的。(模板可自己编写,难度比较大)
方法三:
使用NETCONF协议,结合标准的XML编码格式,返回标准化格式的内容。而ncclient就是一个用于NETCONF客户端的Python库。
2、操作环境:
操作系统:Linux CentOS 7.4
python版本:python 3.8
网络设备:华为CloudEngine 6865
编辑器:vscode
安装ncclient:
pip3 install ncclient
3、代码示例
#!/usr/bin/env python
#coding: utf-8
from pprint import pprint
import xmltodict
from ncclient import manager
from ncclient import operations
from ncclient.transport.errors import SSHError
from ncclient.transport.errors import AuthenticationError
#构造待配置的YANG内容,此处为系统信息。
#具体可参考华为官网二次开发文档《CloudEngine 8800, 7800, 6800HI, 6880EI, 6875EI, 6870EI, 6865EI, 6860EI, 6857EI, 5880EI V200R005C10 NETCONF Schema API参考》
devinfo_FILTER = '''
<system xmlns="<http://www.huawei.com/netconf/vrp>" content-version="1.0" format-version="1.0">
<systemInfo>
</systemInfo>
</system>
'''
# Fill the device information and establish a NETCONF session
def huawei_connect(host, port, user, password):
return manager.connect(host=host,
port=port,
username=user,
password=password,
hostkey_verify = False,
device_params={'name': "huawei"},
allow_agent = False,
look_for_keys = False)
def nc_get(host, port, user, password):
with huawei_connect(host, port=port, user=user, password=password) as m:
return m.get(("subtree", devinfo_FILTER))
def main():
nc_res = nc_get("172.26.32.7",22,"user","pass")
xml_rep = nc_res.data_xml
print(xml_rep)
if __name__ == '__main__':
main()
执行结果
<?xml version="1.0" encoding="UTF-8"?><data xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
<system xmlns="<http://www.huawei.com/netconf/vrp>" format-version="1.0" content-version="1.0">
<systemInfo>
<sysName>SZTL-POD2-Leaf-H6865-1</sysName>
<sysContact>R&D Beijing, Huawei Technologies co.,Ltd.</sysContact>
<sysLocation>Beijing China</sysLocation>
<sysDesc>Huawei Versatile Routing Platform Software
VRP (R) software, Version 8.191 (CE6865EI V200R019C10SPC800)
Copyright (C) 2012-2020 Huawei Technologies Co., Ltd.
HUAWEI CE6865-48S8CQ-EI </sysDesc>
<sysObjectId>1.3.6.1.4.1.2011.2.239.49</sysObjectId>
<sysGmtTime>1621568735</sysGmtTime>
<sysUpTime>4125375</sysUpTime>
<sysService>78</sysService>
<platformName>VRP</platformName>
<platformVer>V800R019C16SPC599</platformVer>
<productName>CE6865EI</productName>
<productVer>V200R019C10SPC800</productVer>
<patchVer>V200R019SPH007</patchVer>
<esn>2102351RFD10MC111521</esn>
<mac>9017-3FF0-1CB1</mac>
<lsRole>admin</lsRole>
<authenFlag>false</authenFlag>
<softwareName/>
</systemInfo>
</system>
</data>
可以看到回显内容结构非常清晰,需要什么数据直接从XML中获取即可。后面就以示例为基础,完成开头提到的需求。
4、ncclient实验
4.1 需求
获取设备的设备名称、型号、版本号、补丁、SN
4.2 代码
#!/usr/bin/env python
#coding: utf-8
from pprint import pprint
import xmltodict
from ncclient import manager
from ncclient import operations
from ncclient.transport.errors import SSHError
from ncclient.transport.errors import AuthenticationError
#构造YANG
devinfo_FILTER = '''
<system xmlns="<http://www.huawei.com/netconf/vrp>" content-version="1.0" format-version="1.0">
<systemInfo>
<sysName></sysName>
<productName></productName>
<productVer></productVer>
<patchVer></patchVer>
<esn></esn>
</systemInfo>
</system>
'''
#建立NETCONF会话
def huawei_connect(host, port, user, password):
return manager.connect(host=host,
port=port,
username=user,
password=password,
hostkey_verify = False,
device_params={'name': "huawei"},
allow_agent = False,
look_for_keys = False)
#构造<get>报文
def nc_get(host, port, user, password):
with huawei_connect(host, port=port, user=user, password=password) as m:
return m.get(("subtree", devinfo_FILTER))
#主函数
def main():
nc_res = nc_get("172.26.32.7",22,"user","pass")
xml_rep = nc_res.data_xml
print(xml_rep)
xml_dict = xmltodict.parse(xml_rep)
for k,y in xml_dict.items():
print(k,y)
print("="*70)
for k,y in xml_dict['data']['system']['systemInfo'].items():
print(k,":",y)
if __name__ == '__main__':
main()
执行结果
<?xml version="1.0" encoding="UTF-8"?><data xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
<system xmlns="<http://www.huawei.com/netconf/vrp>" format-version="1.0" content-version="1.0">
<systemInfo>
<sysName>SZTL-POD2-Leaf-H6865-1</sysName>
<productName>CE6865EI</productName>
<productVer>V200R019C10SPC800</productVer>
<patchVer>V200R019SPH007</patchVer>
<esn>2102351RFD10MC111521</esn>
</systemInfo>
</system>
</data>
data OrderedDict([('@xmlns', 'urn:ietf:params:xml:ns:netconf:base:1.0'), ('system', OrderedDict([('@xmlns', '<http://www.huawei.com/netconf/vrp>'), ('@format-version', '1.0'), ('@content-version', '1.0'), ('systemInfo', OrderedDict([('sysName', 'SZTL-POD2-Leaf-H6865-1'), ('productName', 'CE6865EI'), ('productVer', 'V200R019C10SPC800'), ('patchVer', 'V200R019SPH007'), ('esn', '2102351RFD10MC111521')]))]))])
======================================================================
sysName : SZTL-POD2-Leaf-H6865-1
productName : CE6865EI
productVer : V200R019C10SPC800
patchVer : V200R019SPH007
esn : 2102351RFD10MC111521
4.3 详解
from pprint import pprint
#有缩进的打印,回显更直观,建议使用
import xmltodict
#用于xml转换为dict
#导入ncclient常用模块
from ncclient import manager
from ncclient import operations
from ncclient.transport.errors import SSHError
from ncclient.transport.errors import AuthenticationError
devinfo_FILTER = '''
<system xmlns="<http://www.huawei.com/netconf/vrp>" content-version="1.0" format-version="1.0">
<systemInfo>
<sysName></sysName>
<productName></productName>
<productVer></productVer>
<patchVer></patchVer>
<esn></esn>
</systemInfo>
</system>
'''
构造待配置的YANG内容,此处为系统信息。 具体可参考华为官网二次开发文档《CloudEngine 8800, 7800, 6800HI, 6880EI, 6875EI, 6870EI, 6865EI, 6860EI, 6857EI, 5880EI V200R005C10 NETCONF Schema API参考》。
如果我们只需要systemInfo下的部分信息,直接在<systemInfo></systemInfo>中间加入明细内容即可,可参考官方文档。
关于XML基础知识,可以访问https://www.w3school.com.cn/xml/xml_intro.asp查看。
def huawei_connect(host, port, user, password):
return manager.connect(host=host,
port=port,
username=user,
password=password,
hostkey_verify = False,
device_params={'name': "huawei"},
allow_agent = False,
look_for_keys = False)
定义NETCONF session的函数,调用manager.connect建立连接。
def nc_get(host, port, user, password):
with huawei_connect(host, port=port, user=user, password=password) as m:
return m.get(("subtree", devinfo_FILTER))
定义get函数,构造<get>报文,并返回rpc-reply的结果。
因为我们不希望get过多内容,所以要加入filter功能,devinfo_FILTER就是我们自己构建的XML,<filter>元素可以包含“type”属性,NETCONF中的默认过滤机制被称为子树过滤(“type”属性值为“subtree”)。
打印结果一:
#主函数
def main():
nc_res = nc_get("172.26.32.7",22,"user","pass")
xml_rep = nc_res.data_xml #返回XML标准格式的字符串
print(xml_rep)
#执行结果
<?xml version="1.0" encoding="UTF-8"?><data xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
<system xmlns="<http://www.huawei.com/netconf/vrp>" format-version="1.0" content-version="1.0">
<systemInfo>
<sysName>SZTL-POD2-Leaf-H6865-1</sysName>
<productName>CE6865EI</productName>
<productVer>V200R019C10SPC800</productVer>
<patchVer>V200R019SPH007</patchVer>
<esn>2102351RFD10MC111521</esn>
</systemInfo>
</system>
</data>
打印结果二:
xml_dict = xmltodict.parse(xml_rep)
#此方法会将xml_rep解析为orderdict,和传统dict区别就是orderdict是有顺序的
for k,y in xml_dict.items():
#遍历字典,items() 方法让字典以列表形式返回可遍历的(键, 值) 元组数组
print(k,y)
#执行结果
data OrderedDict([('@xmlns', 'urn:ietf:params:xml:ns:netconf:base:1.0'), ('system', OrderedDict([('@xmlns', '<http://www.huawei.com/netconf/vrp>'), ('@format-version', '1.0'), ('@content-version', '1.0'), ('systemInfo', OrderedDict([('sysName', 'SZTL-POD2-Leaf-H6865-1'), ('productName', 'CE6865EI'), ('productVer', 'V200R019C10SPC800'), ('patchVer', 'V200R019SPH007'), ('esn', '2102351RFD10MC111521')]))]))])
打印结果三:
#访问xml_dict['data']['system']['systemInfo']的value
#OrderedDict([('sysName', 'SZTL-POD2-Leaf-H6865-1'),
# ('productName', 'CE6865EI'),
# ('productVer', 'V200R019C10SPC800'),
# ('patchVer', 'V200R019SPH007'),
# ('esn', '2102351RFD10MC111521')])
#在进行遍历操作
for k,y in xml_dict['data']['system']['systemInfo'].items():
print(k,":",y)
#打印结果
sysName : SZTL-POD2-Leaf-H6865-1
productName : CE6865EI
productVer : V200R019C10SPC800
patchVer : V200R019SPH007
esn : 2102351RFD10MC111521
上面的逐步打印,是为了让大家更清晰的看到每一步数据处理的区别。
核心还是提供思路,详细的基础知识如果有不懂的,直接在Google或者baidu搜索,有很多资料。