マルチベンダーなネットワーク機器を制御出来るPythonライブラリ「NAPALM」を試してみた

NANOG 69で「Saltstack + NAPALM」を使ったネットワーク・オートメーションに関するプレゼンがありました。

1_Ulinic_Network_Automation_At_v1.pdf

Saltstackとは

Saltstackは、ChefやPuppet、Ansibleなどと同じ構成管理ツールです。日本でのSaltstackの知名度はイマイチですが、Ansible並に簡単に導入できて、Chef以上に機能が豊富なことがウリのようです。
ただし、まだまだマイナーなためコミュニティも弱く、日本語の情報も少ない点がデメリットです。

SaltStack automation for CloudOps, ITOps & DevOps at scale

NAPALMとは

NAPALM(Network Automation and Programmability Abstraction Layer with Multivendor support)はネットワーク機器の設定や情報を取得するためのオープンなPythonライブラリです。NAPALMを使うことでマルチベンダー製品を統一化されたインタフェースで制御することが可能です。動作イメージとしては、AristaのeAPIやJuniperのPyEZなどの公式APIやNETCONF、netmikoなどのコミュニティが開発したPythonライブラリなどの様々なAPIやライブラリを裏で動かして、その上にキャップを被せたツールがNAPALMになります。
SaltStackだけでなく、Ansibleとも連携が可能です。

昨今のネットワークはマルチベンダーで組む場合が増えてきていますので、そのような運用者にとっては便利なライブラリだと思います。

Welcome to NAPALM’s documentation! — NAPALM 1 documentation

napalm-automation/napalm: Network Automation and Programmability Abstraction Layer with Multivendor support

今回はNAPALMを使って、実際にいくつかのネットワーク機器を制御してみます。

NAPALMがサポートするネットワーク機器

現時点(2017年2月)でNAPALMがサポートするネットワークには以下のようなものがあります。

  • Arista EOS
  • Cisco IOS
  • Cisco IOS-XR
  • Cisco NX-OS
  • Fortinet Fortios
  • IBM
  • Juniper JunOS
  • Mikrotik RouterOS
  • Palo Alto NOS
  • Pluribus
  • Vyos

NAPALMがサポートしている機能

NAPALMが制御可能な機能には以下の様なものがあります。

  • get_arp_table
    APRテーブルの取得
  • get_bgp_config
    BGP設定の取得
  • get_bgp_neighbors
    BGPネイバーの取得
  • get_bgp_neighbors_detail
    詳細なBGPネイバーの取得
  • get_config
    設定の取得
  • get_environment
    環境情報の取得
  • get_facts
    装置情報の取得
  • get_firewall_policies
    FWポリシーの取得
  • get_interfaces
    インタフェース情報の取得
  • get_interfaces_counters
    インタフェースカウンターの取得
  • get_interfaces_ip
    インタフェースに設定しているIPアドレスの取得
  • get_lldp_neighbors
    LLDPネイバー情報の取得
  • get_lldp_neighbors_detail
    詳細なLLDPネイバー情報の取得
  • get_mac_address_table
    MACアドレステーブルの取得
  • get_network_instances
    ネットワーク情報の取得
  • get_ntp_peers
    NTP情報の取得
  • get_ntp_servers
    NTP情報の取得
  • get_ntp_stats
    NTP情報の取得
  • get_optics
    光出力情報の取得
  • get_probes_config
    プローブ情報の取得
  • get_probes_results
    プローブ情報の取得
  • get_route_to
    ルーティング情報の取得
  • get_snmp_information
    SNMP情報の取得
  • get_users
    ユーザー情報の取得
  • is_alive
    接続状態の取得
  • ping
    Ping情報の取得
  • traceroute
    Traceroute情報の取得

上記機能はベンダーごとにサポート有無が異なります。詳細は以下のURL参照。

Supported Devices — NAPALM 1 documentation

NAPALMを試してみる

インストール

今回はOSX 10.12.3上で試してみます。pipを使用してインストールします。

% pip install napalm

% pip list | grep napalm
napalm (1.1.0)
napalm-base (0.23.0)
napalm-eos (0.5.3)
napalm-fortios (0.3.1)
napalm-ibm (0.1.7)
napalm-ios (0.6.1)
napalm-iosxr (0.4.8)
napalm-junos (0.6.4)
napalm-nxos (0.5.0)
napalm-panos (0.4.0)
napalm-pluribus (0.5.1)

% pip show napalm
Name: napalm
Version: 1.1.0
Summary: Network Automation and Programmability Abstraction Layer with Multivendor support
Home-page: https://github.com/napalm-automation/napalm
Author: David Barroso
Author-email: dbarrosop@dravetech.com
License: UNKNOWN
Location: /Users/itbook/.pyenv/versions/2.7.8/lib/python2.7/site-packages
Requires: napalm-iosxr, napalm-ibm, napalm-eos, napalm-nxos, napalm-fortios, napalm-panos, napalm-junos, napalm-base, napalm-ios, napalm-pluribus

Arista

続いて情報を取得するルータを準備します。今回はAristaとJuniperのルータを使用してみます。

Arista EOSの設定は以下の通り

EOS-1#sh run
! Command: show running-config
! device: EOS-1 (CVX, EOS-4.15.7M)
!
! boot system flash:/EOS.swi
!
transceiver qsfp default-mode 4x10G
!
hostname EOS-1
!
spanning-tree mode mstp
!
no aaa root
!
username test secret 5 $1$m8/K6n.m$AD0jR8r07N7wYRMLmIAPs/
!
interface Management1
   ip address 172.16.41.2/24
!
no ip routing
!
management api http-commands
   protocol http
   no shutdown
!
!
end
EOS-1#show version
Arista CVX
Hardware version:
Serial number:
System MAC address:  000c.29f1.b416

Software image version: 4.15.7M
Architecture:           i386
Internal build version: 4.15.7M-3284043.4157M
Internal build ID:      b0b0dff8-c9ca-40cc-a625-7fd3c8c76ebd

Uptime:                 5 hours and 25 minutes
Total memory:           2513796 kB
Free memory:            179944 kB

Aristaから情報を取得するにはeAPIを使用しますので、eAPIを有効にしています。

Juniper

Juniperの設定は以下の通り

srx1@srx1> show configuration
## Last commit: 2016-11-30 16:49:26 JST by root
version 15.1X49-D60.7;
groups {
    node0 {
        system {
            host-name srx1;
        }
        interfaces {
            fxp0 {
                unit 0 {
                    family inet {
                        address 172.16.41.10/24;
                    }
                }
            }
        }
    }
    node1 {
        system {
            host-name srx2;
        }
        interfaces {
            fxp0 {
                unit 0 {
                    family inet {
                        address 172.16.41.11/24;
                    }
                }
            }
        }
    }
}
apply-groups "${node}";
system {
    host-name srx1;
    time-zone Asia/Tokyo;
    root-authentication {
        encrypted-password "$5$LL2YpvKd$OFdFNzb/f/9/K0az5IRtxKK3rZgwZYZCozLDA14v9/D"; ## SECRET-DATA
    }
    login {
        user srx1 {
            uid 2000;
            class super-user;
            authentication {
                encrypted-password "$5$qasOhBQ7$s7QyQgvvDdNT5GKyCx1coLop5F9yXLtoeuJUidEy4O7"; ## SECRET-DATA
            }
        }
    }
    services {
        ssh {
            protocol-version v2;
        }
        web-management {
            http {
                interface fxp0.0;
            }
        }
    }
    syslog {
        user * {
            any emergency;
        }
        file messages {
            any any;
            authorization info;
        }
        file interactive-commands {
            interactive-commands any;
        }
    }
    license {
        autoupdate {
            url https://ae1.juniper.net/junos/key_retrieval;
        }
    }
}
security {
    screen {
        ids-option untrust-screen {
            icmp {
                ping-death;
            }
            ip {
                source-route-option;
                tear-drop;
            }
            tcp {
                syn-flood {
                    alarm-threshold 1024;
                    attack-threshold 200;
                    source-threshold 1024;
                    destination-threshold 2048;
                    queue-size 2000; ## Warning: 'queue-size' is deprecated
                    timeout 20;
                }
                land;
            }
        }
    }
    policies {
        from-zone trust to-zone trust {
            policy default-permit {
                match {
                    source-address any;
                    destination-address any;
                    application any;
                }
                then {
                    permit;
                }
            }
        }
        from-zone trust to-zone untrust {
            policy default-permit {
                match {
                    source-address any;
                    destination-address any;
                    application any;
                }
                then {
                    permit;
                }
            }
        }
    }
    zones {
        security-zone trust {
            tcp-rst;
        }
        security-zone untrust {
            screen untrust-screen;
        }
    }
}
interfaces {
    fxp0 {
        unit 0;
    }
}
srx1@srx1> show version
node0:
--------------------------------------------------------------------------
Hostname: srx1
Model: vsrx
Junos: 15.1X49-D60.7
JUNOS Software Release [15.1X49-D60.7]

NAPALMを使ったコンフィグの取得

準備が整いましたので、NAPALMを使用してコンフィグを取得してみます。
コンフィグ取得はget_config()を使用します。

Arista

% more get_conf_arista.py
#! /usr/bin/env python
# -*- coding: utf-8 -*-

import napalm

driver = napalm.get_network_driver('eos')
device = driver(
    hostname='172.16.41.2',
    username='test',
    password='test' )

print 'Device Opening ...',
device.open()
print 'OK\n'

result = device.get_config()

print result[u'running']

print 'Device Closing ...',
device.close()
print 'Done'

実行結果は以下の通りです。

% python get_conf_arista.py
Device Opening ...
OK
! Command: show running-config
! device: EOS-1 (CVX, EOS-4.15.7M)
!
! boot system flash:/EOS.swi
!
transceiver qsfp default-mode 4x10G
!
hostname EOS-1
!
spanning-tree mode mstp
!
no aaa root
!
username test secret 5 $1$m8/K6n.m$AD0jR8r07N7wYRMLmIAPs/
!
interface Management1
   ip address 172.16.41.2/24
!
no ip routing
!
management api http-commands
   protocol http
   no shutdown
!
!
end

Device Closing ... Done

Juniper

% more get_conf_juniper.py
#! /usr/bin/env python
# -*- coding: utf-8 -*-

import napalm

driver = napalm.get_network_driver('junos')
device = driver(
    hostname='172.16.41.10',
    username='srx1',
    password='test' )

print 'Device Opening ...',
device.open()
print 'OK\n'

result = device.get_config()

print result[u'running']

print 'Device Closing ...',
device.close()
print 'Done'

実行結果は以下の通り。

% python get_conf_juniper.py
Device Opening ... OK


## Last commit: 2016-11-30 16:49:26 JST by root
version 15.1X49-D60.7;
groups {
    node0 {
        system {
            host-name srx1;
        }
        interfaces {
            fxp0 {
                unit 0 {
                    family inet {
                        address 172.16.41.10/24;
                    }
                }
            }
        }
    }
    node1 {
        system {
            host-name srx2;
        }
        interfaces {
            fxp0 {
                unit 0 {
                    family inet {
                        address 172.16.41.11/24;
                    }
                }
            }
        }
    }
}
apply-groups "${node}";
system {
    host-name srx1;
    time-zone Asia/Tokyo;
    root-authentication {
        encrypted-password "$5$LL2YpvKd$OFdFNzb/f/9/K0az5IRtxKK3rZgwZYZCozLDA14v9/D";
    }
    login {
        user srx1 {
            uid 2000;
            class super-user;
            authentication {
                encrypted-password "$5$qasOhBQ7$s7QyQgvvDdNT5GKyCx1coLop5F9yXLtoeuJUidEy4O7";
            }
        }
    }
    services {
        ssh {
            protocol-version v2;
        }
        web-management {
            http {
                interface fxp0.0;
            }
        }
    }
    syslog {
        user * {
            any emergency;
        }
        file messages {
            any any;
            authorization info;
        }
        file interactive-commands {
            interactive-commands any;
        }
    }
    license {
        autoupdate {
            url https://ae1.juniper.net/junos/key_retrieval;
        }
    }
}
security {
    screen {
        ids-option untrust-screen {
            icmp {
                ping-death;
            }
            ip {
                source-route-option;
                tear-drop;
            }
            tcp {
                syn-flood {
                    alarm-threshold 1024;
                    attack-threshold 200;
                    source-threshold 1024;
                    destination-threshold 2048;
                    queue-size 2000;
                    timeout 20;
                }
                land;
            }
        }
    }
    policies {
        from-zone trust to-zone trust {
            policy default-permit {
                match {
                    source-address any;
                    destination-address any;
                    application any;
                }
                then {
                    permit;
                }
            }
        }
        from-zone trust to-zone untrust {
            policy default-permit {
                match {
                    source-address any;
                    destination-address any;
                    application any;
                }
                then {
                    permit;
                }
            }
        }
    }
    zones {
        security-zone trust {
            tcp-rst;
        }
        security-zone untrust {
            screen untrust-screen;
        }
    }
}
interfaces {
    fxp0 {
        unit 0;
    }
}

Device Closing ... Done

NAPALMを使ったARPテーブルの取得

ARPテーブル情報を取得するには、get_arp_table()関数を利用します。

Arista

% more get_arp_arista.py
#! /usr/bin/env python
# -*- coding: utf-8 -*-

import napalm

driver = napalm.get_network_driver('eos')
device = driver(
    hostname='172.16.41.2',
    username='test',
    password='test' )

print 'Device Opening ...',
device.open()
print 'OK\n'

result = device.get_arp_table()

print result

print 'Device Closing ...',
device.close()
print 'Done'

実行結果は以下の通り。

% python get_arp_arista.py
Device Opening ... OK

[{u'interface': u'Management1', u'ip': u'172.16.41.1', u'mac': u'00:50:56:C0:00:01', u'age': 0.0}]
Device Closing ... Done

実機での確認結果は以下の通り。

EOS-1#show arp
Address         Age (min)  Hardware Addr   Interface
172.16.41.1             0  0050.56c0.0001  Management1

Juniper

% more get_arp_juniper.py
#! /usr/bin/env python
# -*- coding: utf-8 -*-

import napalm

driver = napalm.get_network_driver('junos')
device = driver(
    hostname='172.16.41.10',
    username='srx1',
    password='test' )

print 'Device Opening ...',
device.open()
print 'OK\n'

result = device.get_arp_table()

print result

print 'Device Closing ...',
device.close()
print 'Done'

実行結果は以下の通り。

% python get_arp_juniper.py
Device Opening ... OK

[{'interface': u'fab0.0', 'ip': u'30.17.0.2', 'mac': u'4C:96:14:23:62:B0', 'age': None}, {'interface': u'em0.0', 'ip': u'129.16.0.16', 'mac': u'00:50:56:20:7C:04', 'age': 1090.0}, {'interface': u'fxp0.0', 'ip': u'172.16.41.1', 'mac': u'00:50:56:C0:00:01', 'age': 981.0}, {'interface': u'em1.32768', 'ip': u'192.168.1.1', 'mac': u'AA:BB:CC:DD:EE:FF', 'age': 558.0}]
Device Closing ... Done

実機での確認結果は以下の通り。

srx1@srx1> show arp
MAC Address       Address         Name                      Interface               Flags
4c:96:14:23:62:b0 30.17.0.2       30.17.0.2                 fab0.0                  permanent
00:50:56:20:7c:04 129.16.0.16     129.16.0.16               em0.0                   none
00:50:56:c0:00:01 172.16.41.1     172.16.41.1               fxp0.0                  none
aa:bb:cc:dd:ee:ff 192.168.1.1     192.168.1.1               em1.32768               none
Total entries: 4

まとめ

NAPALMを使用することで、コマンドを意識せずにマルチベンダー環境における自動化が行えるのは、種類が多ければ多いほど運用工数を削減できそうです。調べて見ると、NAPALMは現在も活発に開発が続けられているので、今後も機能は追加されていくと思います。

次回は、「Ansible + NAPALM」か「Saltstack + NAPALM」を試してみようと思います。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)