マルチベンダーなネットワーク機器を制御出来る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を使って、実際にいくつかのネットワーク機器を制御してみます。
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」を試してみようと思います。