乐趣区

关于python:NAPALM介绍与实操

[toc]

NAPALM 概述

官网链接

其余 drive 链接(国人)

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

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

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

Cisco IOS
Cisco NX-OS
Cisco IOS-XR
Juniper JUNOS
Arista EOS

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

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

反对的设施

通用反对模型

_ EOS Junos IOS-XR (NETCONF) IOS-XR (XML-Agent) NX-OS NX-OS SSH IOS
Driver Name eos junos iosxr_netconf iosxr nxos nxos_ssh ios
Structured data Yes Yes Yes No Yes No No
Minimum version 4.15.0F 12.1 7.0 5.1.0 6.1 [1] 12.4(20)T 6.3.2
Backend library pyeapi junos-eznc ncclient pyIOSXR pynxos netmiko netmiko
Caveats EOS IOS-XR (NETCONF) NXOS NXOS IOS

阐明:

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

配置反对模型

反对配置替换、合并、提交、比照、原子配置、回滚等。

_ EOS Junos IOS-XR (NETCONF) IOS-XR (XML-Agent) NX-OS IOS
Config. replace Yes Yes Yes Yes Yes Yes
Config. merge Yes Yes Yes Yes Yes Yes
Commit Confirm Yes Yes No No No No
Compare config Yes Yes Yes Yes [2] Yes [4] Yes
Atomic Changes Yes Yes Yes Yes Yes/No [5] Yes/No [5]
Rollback Yes [3] Yes Yes Yes Yes/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 反对的模型

该表官网会不定期自动更新。

EOS IOS IOSXR IOSXR_NETCONF JUNOS NXOS NXOS_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获取接口信息等等,也是十分不便的。

其余办法

_ EOS Junos IOS-XR (NETCONF) IOS-XR NX-OS IOS
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_driver
import pprint

pp = pprint.PrettyPrinter(indent=2)

# drive 是一个类 class,传入 drive_name
drive = 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_driver

import pprint
import os

pp = pprint.PrettyPrinter(indent=2)

host = "192.168.0.20"

# drive 是一个类 class
drive = 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 是一个类 class
drive = 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 是一个类 class
drive = 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_driver
import pprint

pp = pprint.PrettyPrinter(indent=2)

# drive 是一个类 class
drive = 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_driver
    from jinja2 import FileSystemLoader, Environment
    
    import pprint
    import logging
    
    pp = 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.1
    access-list 20 permit host 192.168.0.254
    line 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 pprint
    from napalm import get_network_driver
    
    pp = 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_driver
import pprint
import 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}}    

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

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

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

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

退出移动版