银狐NetDevOps-网络运维python篇之NETCONF(一)华为ncclient指南

科技银狐

前言

银狐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&amp;D Beijing, Huawei Technologies co.,Ltd.</sysContact>
        <sysLocation>Beijing China</sysLocation>
        <sysDesc>Huawei Versatile Routing Platform Software &#13;
VRP (R) software, Version 8.191 (CE6865EI V200R019C10SPC800) &#13;
Copyright (C) 2012-2020 Huawei Technologies Co., Ltd. &#13;
HUAWEI CE6865-48S8CQ-EI &#13;</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搜索,有很多资料。

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

推荐阅读更多精彩内容