[toc]

NAPALM概述

官网链接

其余drive链接(国人)

NAPALM全称为Network Automation and Programmability Abstraction Layer with Multivendor support,翻译起来就是反对"多厂商"网络自动化和可编程的形象层.

NAPALM是一个python开源的第三方模块,截至2021.11月,反对的厂商如下所示:

尽管反对厂商不是很多,临时没有反对国内厂商,但性能都是很弱小的。

Cisco IOSCisco NX-OSCisco IOS-XRJuniper JUNOSArista EOS

NX-API support on the Nexus 5k, 6k and 7k families was introduced in version 7.2

  • 依赖netmiko,本机装置即可.

反对的设施

通用反对模型

_EOSJunosIOS-XR (NETCONF)IOS-XR (XML-Agent)NX-OSNX-OS SSHIOS
Driver Nameeosjunosiosxr_netconfiosxrnxosnxos_sshios
Structured dataYesYesYesNoYesNoNo
Minimum version4.15.0F12.17.05.1.06.1 [1]12.4(20)T6.3.2
Backend librarypyeapijunos-ezncncclientpyIOSXRpynxosnetmikonetmiko
CaveatsEOSIOS-XR (NETCONF)NXOSNXOSIOS

阐明:

  • driver name:前面咱们写代码须要填写的,如get_network_driver(ios)
  • structured data:反对的结构化数据
  • Minimum version:这里还有最低的版本要求;

配置反对模型

反对配置替换、合并、提交、比照、原子配置、回滚等。
_EOSJunosIOS-XR (NETCONF)IOS-XR (XML-Agent)NX-OSIOS
Config. replaceYesYesYesYesYesYes
Config. mergeYesYesYesYesYesYes
Commit ConfirmYesYesNoNoNoNo
Compare configYesYesYesYes [2]Yes [4]Yes
Atomic ChangesYesYesYesYesYes/No [5]Yes/No [5]
RollbackYes [3]YesYesYesYes/No [5]Yes

[2] Hand-crafted by the API as the device doesn't support the feature.
[3] Not supported but emulated. Check caveats.
[4] For merges, the diff is very simplistic. See caveats.
[5] (1, 2, 3) No for merges. See caveats.
[6] NAPALM requires Junos OS >= 14.1 for commit-confirm functionality.

Getters反对的模型

该表官网会不定期自动更新。
EOSIOSIOSXRIOSXR_NETCONFJUNOSNXOSNXOS_SSH
get_arp_table
get_bgp_config
get_bgp_neighbors
get_bgp_neighbors_detail
get_config
get_environment
get_facts
get_firewall_policies
get_interfaces
get_interfaces_counters
get_interfaces_ip
get_ipv6_neighbors_table
get_lldp_neighbors
get_lldp_neighbors_detail
get_mac_address_table
get_network_instances
get_ntp_peers
get_ntp_servers
get_ntp_stats
get_optics
get_probes_config
get_probes_results
get_route_to
get_snmp_information
get_users
get_vlans
is_alive
ping
traceroute
  • ✅ - supported
  • ❌ - not supported
  • ☠ - broken

通过Getters类,比方get_config就能够失去设施的配置(running和startup)、get_facts获取设施的根本信息、get_interfaces获取接口信息等等,也是十分不便的。

其余办法

_EOSJunosIOS-XR (NETCONF)IOS-XRNX-OSIOS
load_template
ping
traceroute

可用的配置模板

  • set_hostname (JunOS, IOS-XR, IOS) - Configures the hostname of the device.
  • set_ntp_peers (JunOS, IOS-XR, EOS, NXOS, IOS) - Configures NTP peers of the device.
  • delete_ntp_peers (JunOS, IOS-XR, EOS, NXOS, IOS): Removes NTP peers from device’s configuration.
  • set_probes (JunOS, IOS-XR): Configures RPM/SLA probes.
  • schedule_probes (IOS-XR): On Cisco devices, after defining the SLA probes, it is mandatory to schedule them. Defined also for JunOS as empty template, for consistency reasons.
  • delete_probes (JunOS, IOS-XR): Removes RPM/SLA probes.

以上办法,大家能够在试验环境测试下。

如何装置

须要确保netmiko曾经被装置,依赖与netmiko的。

  • 通过pip3装置
python -m pip install napalm

备注:从版本3.0.0之后,NAPALM仅反对Python 3.6+.

如何降级

pip install napalm -U

备注:如果napalm有降级了,能够通过该办法进行降级.

实操示例

视频中演示的代码,都在这里了.

根本的设施连贯

from napalm import get_network_driverimport pprintpp = pprint.PrettyPrinter(indent=2)# drive是一个类class,传入drive_namedrive = get_network_driver('ios')# 类实例化conn = drive(hostname='192.168.0.20',             username='cisco',             password='cisco',             optional_args={'port': 22})# 建设连贯会话conn.open()# 应用其中一个办法,获取接口ip地址ouput = conn.get_interfaces_ip()# 打印后果pp.pprint(ouput)# 敞开连贯conn.close()

执行脚本后,后果如下所示:

备份配置

from napalm import get_network_driverimport pprintimport ospp = pprint.PrettyPrinter(indent=2)host = "192.168.0.20"# drive是一个类classdrive = get_network_driver('ios')# 类实例化conn = drive(hostname= host,             username= 'cisco',             password= 'cisco',             optional_args= {'port': 22})# 建设连贯会话conn.open()# 获取设施配置信息output = conn.get_config()# 打印后果pp.pprint(output)running_config = output['running']# 写入文件with open(os.path.join('LOG', f'{host}-running.conf'), 'w+') as f:    f.writelines(running_config)# 敞开连贯conn.close()

执行完脚本,后果如下所示:

默认蕴含了3个备份配置:

  • running配置:就是以后运行配置.
  • startup配置:就是启动配置了.
  • candidate配置,这个个别为空.

这里,我保留了running.config配置文件.

获取设施根底信息

# drive是一个类classdrive = get_network_driver('ios')# 类实例化conn = drive(hostname='192.168.0.20',             username='cisco',             password='cisco',             optional_args={'port': 22})# 建设连贯会话conn.open()# 获取接口ip地址output = conn.get_facts()# 打印后果pp.pprint(ouput)# 敞开连贯conn.close()

执行脚本后,后果如下所示:

get_facts()办法采集了如下字段:

  • 域名、主机名称、接口列表、型号、版本号、序列号、运行工夫、厂商.

发送命令

# drive是一个类classdrive = get_network_driver('ios')# 类实例化conn = drive(hostname='192.168.0.20',             username='cisco',             password='cisco',             optional_args={'port': 22, 'secret': 'cisco'})# 建设连贯会话conn.open()# 发送命令output = conn.cli(['show ip int brief', 'show arp'])pp.pprint(output)# 敞开连贯conn.close()

执行脚本后,后果如下所示:

阐明:这里要注意下如果用户权限不够或者须要进入enable的,须要optional_args减少secret参数,表明须要进入特权模式了.

如果执行的命令权限不容许,就会报如下谬误:

ValueError: Failed to enter enable mode. Please ensure you pass the 'secret' argument to ConnectHandler.

配置类

通过scp协定下发配置,它不须要enable明码.

前提条件:

  • 设施要开启scp服务, ip scp server enable .
  • 开启archive服务,备份配置到本地flash上;

    archive path flash:R1.conf write-memory

合并配置

阐明:

  • load_merge_candidate():加载配置文件.
  • commit_config():提交配置.
  • revert_in=30:如果平台反对,能够设置工夫挂起配置提交,超时没commit就复原配置。

    根底玩法

    模板文件门路:templates\ios_logging_config.cfg

    logging host 10.1.1.2
from napalm import get_network_driverimport pprintpp = pprint.PrettyPrinter(indent=2)# drive是一个类classdrive = get_network_driver('ios')# 类实例化conn = drive(hostname='192.168.0.20',             username='cisco',             password='cisco',             optional_args={'port': 22})# 建设连贯会话conn.open()try:    conn.load_merge_candidate(filename='templates/ios_logging_config.cfg')    conn.commit_config()except Exception as e:    print(e)    

如果设施上没开启scp服务就会报错:

SCP file transfers are not enabled. Configure 'ip scp server enable' on the device.

高级一点玩法:

模板文件门路:templates\ios_logging_config.cfg

def merge_config(vendor, devices, template_file):    try:        # drive是一个类class        drive = get_network_driver(vendor)        # 类实例化        conn = drive(**devices)        # 建设连贯会话        conn.open()    except Exception as e:        print("连贯谬误:{}".format(e))        return    try:        conn.load_merge_candidate(filename=template_file)        new_config = conn.compare_config()        if new_config:            print('预推送的配置如下:')            print('='*80)            print(new_config)            print('=' * 80)            choice = input("你要开始推送这些配置嘛, [Y/N]: ")            if choice.lower() == 'y':                print('开始提交配置,请稍后...')                conn.commit_config()                rollback = input("是否须要回滚配置,输出Y:回滚, 输出N,不回滚. [Y/N]")                if rollback.lower() == 'y':                    print('开始回滚配置,请稍后...')                    conn.rollback()                    print('配置已回滚,请查看配置.')                else:                    print('配置曾经下发胜利.')            else:                conn.discard_config()                print('本次预配置没有推送.')        else:            print('无需反复配置.')    except Exception as e:        print(e)    finally:        conn.close()if __name__ == '__main__':    vendor = 'ios'    devices = {'hostname': '192.168.0.20',               'username': 'cisco',               'password': 'cisco',               'optional_args':{'port': 22}               }    tmp = 'templates/ios_logging_config.cfg'    merge_config(vendor, devices, template_file=tmp)

配置替换

办法:load_replace_candidate()

def replace_config(vendor, devices, template_file):    try:        # drive是一个类class        drive = get_network_driver(vendor)        # 类实例化        conn = drive(**devices)        # 建设连贯会话        conn.open()    except Exception as e:        print("连贯谬误:{}".format(e))        return    try:        if not (os.path.exists(template_file) and os.path.isfile(template_file)):            msg = '文件不存在或文件类型不可用.'            raise ValueError(msg)        print("开始加载候选替换配置...")        # 这个还未加载到设施上的        conn.load_replace_candidate(template_file)        # 配置比拟        print("\n预览配置比照:")        print(">"*80)        compare_result = conn.compare_config()        print(compare_result)        print(">" * 80)        # You can commit or discard the candidate changes.        if compare_result:            try:                choice = input("\nWould you like to commit these changes? [yN]: ").lower()            except NameError:                choice = input("\nWould you like to commit these changes? [yN]: ").lower()            if choice == "y":                print("Committing ...")                conn.commit_config()            else:                print("Discarding ...")                conn.discard_config()        else:            print('没有新的配置.')            conn.discard_config()        conn.close()        print("Done...")    except Exception as e:        print(e)if __name__ == '__main__':    vendor = 'ios'    devices = {'hostname': '192.168.0.20',               'username': 'cisco',               'password': 'cisco',               'optional_args':{'port': 22}               }    tmp = 'LOG/192.168.0.20-running.conf'    replace_config(vendor, devices, template_file=tmp)

阐明:这里我用思科的设施,留神的是,如果running的配置里应用了banner,我是都提前把它删除了,不然会报错,这块我临时没测试进去,如果哪位小伙伴试出来了,请告知我,谢谢。

Jinja2模板

  • 装置jinja2

    python -m pip install jinja2
  • jiaja2模板配置
    模板文件门路:templates/ssh-acl.tpl

    {% for host in hosts -%}    access-list 20 permit host {{ host }}{% endfor -%}line vty 0 4    access-class 20 in
  • 下发配置

    from napalm import get_network_driverfrom jinja2 import FileSystemLoader, Environmentimport pprintimport loggingpp = pprint.PrettyPrinter(indent=2)def connect_device(vendor, devices, hosts):    try:        # drive是一个类class        drive = get_network_driver(vendor)        # 类实例化        conn = drive(**devices)        # 建设连贯会话        conn.open()    except Exception as e:        print("连贯谬误:{}".format(e))        return    try:        loader = FileSystemLoader('templates')        env = Environment(loader=loader)        tpl = env.get_template('ssh-acl.tpl')        config_tpl = tpl.render({'hosts': hosts})        # print(config_tpl)        conn.load_merge_candidate(config=config_tpl)        conn.commit_config()    except Exception as e:        print(e)    finally:        conn.close()if __name__ == '__main__':    vendor = 'ios'    devices = {'hostname': '192.168.0.20',               'username': 'cisco',               'password': 'cisco',               'optional_args':{'secret': 'cisco', 'port': 22}  # 如果enable须要明码,退出'secret'参数               }    hosts = ['192.168.0.1', '192.168.0.254']    connect_device(vendor, devices, hosts)

    这里能够打印下config_tpl,看看下发的配置是啥:

    # print(config_tpl)的输入后果如下所示:access-list 20 permit host 192.168.0.1access-list 20 permit host 192.168.0.254line vty 0 4    access-class 20 in

配置更改回滚

conn.rollback()

备注:见后面章节演示.

验证部署

验证设施的状态是否是你冀望的.
  • 定义一个yaml文件,定义预期想要的状态

    模板文件门路:templates/napalm_t.yaml

    ---- get_facts:    'serial_number': '99CBK1M35C217V5Z7ABWQ'- get_interfaces_ip:    GigabitEthernet0/0:      ipv4: 192.168.0.20
  • compliance_report办法

    import pprintfrom napalm import get_network_driverpp = pprint.PrettyPrinter(indent=2)def connect_device(vendor, devices, hosts):    try:        # drive是一个类class        drive = get_network_driver(vendor)        # 类实例化        conn = drive(**devices)        # 建设连贯会话        conn.open()    except Exception as e:        print("连贯谬误:{}".format(e))        return    try:        out = conn.compliance_report('templates/napalm_t.yaml')        pp.pprint(out)    except Exception as e:        print(e)    finally:        conn.close()if __name__ == '__main__':    vendor = 'ios'    devices = {'hostname': '192.168.0.20',               'username': 'cisco',               'password': 'cisco',               'optional_args':{'port': 22}               }    hosts = ['192.168.0.1', '192.168.0.254']    connect_device(vendor, devices, hosts)

    执行后果如下所示:

    { 'complies': True,  'get_facts': { 'complies': True,                 'extra': [],                 'missing': [],                 'present': { 'serial_number': { 'complies': True,                                                 'nested': False}}},  'get_interfaces_ip': { 'complies': True,                         'extra': [],                         'missing': [],                         'present': { 'GigabitEthernet0/0': { 'complies': True,                                                              'nested': True}}},  'skipped': []}

    阐明:complies的value值为True阐明失常,如果False示意和预期构想不太统一。

反对上下文治理

通过上下文治理就不须要调用open()和close()办法了.
# 类实例化drive = get_network_driver('ios')# with上下文治理with drive(hostname='192.168.0.20',username='cisco',password='cisco',optional_args={'port': 22}) as conn:    output = conn.get_facts()    # 打印后果    pp.pprint(ouput)

ping办法

默认格局:ping x.x.x.x timeout 2 size 100 repeat 5

#!/usr/bin/env python3#-*- coding:UTF-8 -*-from napalm import get_network_driverimport pprintimport logging# logging.basicConfig(filename='debug.log', level=logging.DEBUG)# logger = logging.getLogger('napalm')pp = pprint.PrettyPrinter(indent=2)def connect_device(vendor, devices, dst_ip):    try:        # drive是一个类class        drive = get_network_driver(vendor)        # 类实例化        conn = drive(**devices)        # 建设连贯会话        conn.open()    except Exception as e:        print("连贯谬误:{}".format(e))        return    try:        for ip in dst_ip:            output = conn.ping(ip)            pp.pprint(output)    except Exception as e:        print(e)    finally:        conn.close()if __name__ == '__main__':    vendor = 'ios'    devices = {'hostname': '192.168.0.20',               'username': 'cisco',               'password': 'cisco',               'optional_args':{'port': 22}               }    dst_ip = ['192.168.0.19', '192.168.0.20']    connect_device(vendor, devices, dst_ip)

回显的后果:

{ 'success': { 'packet_loss': 0,               'probes_sent': 5,               'results': [ {'ip_address': '192.168.0.19', 'rtt': 0.0},                            {'ip_address': '192.168.0.19', 'rtt': 0.0},                            {'ip_address': '192.168.0.19', 'rtt': 0.0},                            {'ip_address': '192.168.0.19', 'rtt': 0.0},                            {'ip_address': '192.168.0.19', 'rtt': 0.0}],               'rtt_avg': 2.0,               'rtt_max': 3.0,               'rtt_min': 2.0,               'rtt_stddev': 0.0}}{ 'success': { 'packet_loss': 0,               'probes_sent': 5,               'results': [ {'ip_address': '192.168.0.20', 'rtt': 0.0},                            {'ip_address': '192.168.0.20', 'rtt': 0.0},                            {'ip_address': '192.168.0.20', 'rtt': 0.0},                            {'ip_address': '192.168.0.20', 'rtt': 0.0},                            {'ip_address': '192.168.0.20', 'rtt': 0.0}],               'rtt_avg': 1.0,               'rtt_max': 2.0,               'rtt_min': 1.0,               'rtt_stddev': 0.0}}    

好了,文章的解说就到此结束了,各个性能曾经给大家演示了,各位小伙伴如实操一遍,置信就能把握了并依据需要进行改写了。

如果须要更具体的信息,请挪动官网进行查阅。

别忘了,动动手分享下,点击、珍藏、三连击! 哈哈...

如果喜爱的我的文章,欢送关注我的公众号:点滴技术,扫码关注,不定期分享