暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Nornir3 和 Nornir2 的区别

非资深老网工 2021-04-02
1479

题图由 Pexels 在 Pixabay 上发布

------------------------


Nornir3 vs Nornir2,就好比 Python3 vs Python2。虽然都是一个东西,但是又有一些差异,无法直接互操作。


第一个区别,安装 Nornir2 的时候,程序会自动把所有的插件安装好。而 Nornir3 仅默认安装核心插件,例如 SimpleInventory ,threaded。需要使用者自己手工安装插件。


大部分的插件都可以在 nornir.tech 找到:

https://nornir.tech/nornir/plugins/


安装/注册插件有很多种方法,可以写一个 setuptools 或者 poetry 脚本来安装,也可以从 GitHub 上面把插件项目的 Package 复制到本地然后 import,再用 path.to 来注册。好消息是现在基本上所有的插件都可以用 pip(或者 pip3,取决于你的环境变量)来自动化地安装注册。


    pip install nornir_napalm 
    pip install nornir_netmiko
    pip install nornir_utils
    pip install nornir_jinja2


    安装上述几个插件的同时,相关的依赖包也会被自动安装。


    还有一些插件,比如 ncclient,在 Nornir2 被支持,但 Nornir3 没有。


    其实有一个插件 nornir_netconf ,但看上去开发者在 6 个月前停止开发了。下面是项目地址,作者写好了 README,但是还没 merge,也没放到 PyPI 上去。

    https://github.com/nornir-automation/nornir_netconf/tree/first


    但是 Nornir3 可以使用一个更强大的 Netconf connections 插件: nornir_scrapli。安装的时候注意要用中横线,而不是下划线:


      pip install nornir-scrapli


      scrapli 看上去是 netmikoncclient 的组合体,不仅支持 SSH,还支持 Telnet 和 Netconf。


      这些是和 netmiko 对标的功能:



      这些是和 ncclient 对标的功能:



      第 2 个区别,在 config file 里面,对于线程数量的定义的位置变了。


      Nornir 原生支持多线程,默认 20 个线程,所以其性能比 Ansible 高出两个量级。如果要修改 Nornir2 的线程数量,需要在 config file 里面修改 core.num_workers 选项的值。


        ---
        core:
        num_workers: 100


        inventory:
        plugin: nornir.plugins.inventory.simple.SimpleInventory
        options:
        host_file: "inventory/hosts.yaml"
        group_file: "inventory/groups.yaml"
        defaults_file: "inventory/defaults.yaml"


        Nornir3 把 num_workerscore 里面拎出来,在 config file 中新增了 runner 部分,同样默认 20 个线程。下面是多线程的写法:


          ---
          inventory:
          plugin: SimpleInventory
          options:
          host_file: "inventory/hosts.yaml"
          group_file: "inventory/groups.yaml"
          defaults_file: "inventory/defaults.yaml"
          runner:
          plugin: threaded
          options:
          num_workers: 100


          下面是单线程的写法:


            ---
            inventory:
            plugin: SimpleInventory
            options:
            host_file: "inventory/hosts.yaml"
            group_file: "inventory/groups.yaml"
            defaults_file: "inventory/defaults.yaml"
            runner:
                plugin: serial


            可以看到,Inventory plugin 的写法也变得简单了。这算是第 3 个区别吧。


            其他的版本差异:Nornir3 移除了 core.deserializer,config file 不能再直接配置 jinja2 而需要采用 nornir_jinja2 插件,logging.file 改成 logging.log_file,还有一些其他的小改动:

            https://nornir.readthedocs.io/en/v3.1.0/upgrading/2_to_3.html


            接下来,咱们写一个简单的 Nornir3 demo。按我自己的经验,初学者在学习 Nornir 的时候,最大的疑惑是 Inventory 文件怎么写。最关键的是 platformconnection_optionstransport 都应该怎么写。


            比如上一篇《初识Nornir》,我将一台 Nexus 93180YC 的 platform 写成了 nxos_ssh


              sw_93180:
                  hostname: 10.75.**.**
              platform: nxos_ssh
              transport: ssh
              groups:
              - devnet


              那为什么是 nxos_ssh 而不是 nxos 呢?原因是我分别选择了 napalmnetmiko 作为 connection_options


                devnet:
                username: admin
                password: ***
                connection_options:
                napalm:
                extras: {}


                根据 NAPALM 的文档,nxosnxos_ssh 底层所采用的框架是不一样的。nxos 的底层是 pynxos,而 nxos_ssh 的底层是 netmiko



                所以我的经验是,先根据你想要的数据格式和自己的熟悉程度选择合适的 connection_options(即连接设备的工具,napalm / netmiko scrapli);然后再根据相关工具的文档,确定 platformnxos  nxos_ssh ios)以及使用哪种协议 transportssh  netconf telnet)。


                如果选择 Scrapliplatform 有哪些选项呢?通过查询 Scrapli 的源代码,可以看到它支持以下几种 platform,key 和 value 是等价的,用哪个都行。


                  PLATFORM_MAP = {
                  "ios": "cisco_iosxe",
                  "nxos": "cisco_nxos",
                  "iosxr": "cisco_iosxr",
                  "eos": "arista_eos",
                  "junos": "juniper_junos",
                  }


                  写一个 hosts.yaml 文件:


                    ---
                    sw_93180:
                        hostname: 10.5.7.14
                    platform: nxos
                    transport: ssh
                    groups:
                    - devnet


                    csrv:
                        hostname: 10.5.7.24
                    platform: ios
                    transport: ssh
                    groups:
                    - devnet


                    再写一个 groups.yaml 文件:


                      ---
                      devnet:
                      username: admin
                          password: admin
                      connection_options:
                      scrapli:
                      port: 22
                      extras:
                      ssh_config_file: True
                      auth_strict_key: False


                      再写一个 demo.py


                        from nornir import InitNornir
                        from nornir_utils.plugins.functions import print_result
                        from nornir_scrapli.tasks import send_command


                        nr = InitNornir(config_file='config.yaml')
                        lldp_neighbors = nr.run(task=send_command,
                        command='show lldp neighbors')


                        print_result(lldp_neighbors)


                        如果我们用 nornir_utils 插件的 print_result() 来打印 output,会得到一个 string 文本:



                        但如果使用 Scrapli 提供的 print_structured_result() 就可以得到一个结构化数据了(如果可以成功 parse 的话。默认使用 TextFSM,也可以选择 Genie):


                             

                        更妙的是,作为一个 Scrapli 对象,response 可以直接调用函数,这样就不需要 print_result() 了,而且功能也更加丰富。改写 demo.py 如下:


                          from nornir import InitNornir
                          from nornir_scrapli.tasks import send_command


                          from pprint import pprint


                          nr = InitNornir(config_file='config.yaml')
                          lldp_neighbors = nr.run(task=send_command,
                          command='show lldp neighbors')


                          for key in lldp_neighbors.keys():
                          print()
                          print('Host {} 命令执行时间:'.format(key))
                          pprint(lldp_neighbors[key].scrapli_response.elapsed_time)
                          print()
                          print('命令输出结果:')
                          pprint(lldp_neighbors[key].scrapli_response.textfsm_parse_output()[0])
                          print('-' * 80)

                           

                          执行脚本,返回以下结果:


                            Host sw_93180 命令执行时间:
                            0.126486


                            命令输出结果:
                            {'local_interface': 'mgmt0',
                            'neighbor': 'SE_LAB_OTT_10.75.37',
                            'neighbor_interface': 'Gi1/0/36'}
                            --------------------------------------------------------------------------------


                            Host csrv 命令执行时间:
                            0.149602


                            命令输出结果:
                            {'capabilities': 'B',
                            'local_interface': 'Gi1',
                            'neighbor': 'SE_LAB_OTT_10.75.37',
                            'neighbor_interface': 'Gi1/0/3'}
                            --------------------------------------------------------------------------------


                            文章转载自非资深老网工,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

                            评论