現場で役立つPython:ネットワーク機器の依存関係とトポロジを自動収集
はじめに
ネットワークインフラの構成情報は、運用・保守、トラブルシューティング、変更管理など、あらゆるフェーズで不可欠です。特に機器間の接続関係や依存関係を正確に把握することは、障害発生時の影響範囲特定や、変更作業時のリスク評価において非常に重要となります。
しかし、ネットワーク構成は絶えず変化しうるものであり、手作業で最新の情報を維持することは困難を伴います。ドキュメントはすぐに陳腐化し、実際の接続状態と乖離してしまうことが少なくありません。
システムエンジニアやインフラエンジニアの皆様にとって、Pythonを活用してネットワーク機器の操作や管理を自動化するスキルは、既に必須のものとなりつつあります。この記事では、Pythonスキルを活かし、ネットワーク機器から直接、機器間の依存関係やトポロジ情報を自動的に収集するための具体的な手法をご紹介します。CLIコマンドの実行と結果のパースを中心に、実践的なスクリプト例を交えながら解説を進めます。
なぜネットワークの依存関係・トポロジを自動収集するのか
ネットワーク機器の依存関係(例:どの機器がどの機器にL2/L3で接続されているか)や全体のトポロジを手作業で把握するには、以下のような課題があります。
- 手作業の限界: 大規模・複雑なネットワークでは、機器一台一台にログインして情報を収集し、関連性を紐づける作業は膨大かつ非効率です。
- ドキュメントの陳腐化: ネットワーク構成は日常的な運用や障害対応で変化する可能性があり、手動でドキュメントを更新し続けることは非現実的です。結果として、古い情報に基づく判断は誤りを招くリスクがあります。
- 障害対応の遅延: 障害発生時、影響範囲や根本原因の特定には正確なトポロジ情報が不可欠ですが、最新の情報がすぐに得られない場合、復旧が遅れる要因となります。
- 変更管理のリスク: 設定変更を行う際に、その影響がどの機器に及ぶかを把握できていないと、予期せぬ障害を引き起こす可能性があります。
Pythonによる自動収集は、これらの課題を解決し、常に最新のネットワーク構成情報を迅速かつ正確に取得することを可能にします。
ネットワーク依存関係・トポロジ収集のアプローチ
ネットワーク機器から依存関係やトポロジ情報を得るための主なアプローチはいくつか存在します。
-
CLIコマンドの利用:
- 多くのネットワーク機器は、近隣機器情報を表示するCLIコマンドを提供しています(例: Ciscoの
show cdp neighbor
, マルチベンダーで標準的なshow lldp neighbor
)。 - また、ルーティングテーブル (
show ip route
) やMACアドレステーブル (show mac address-table
), ARPテーブル (show ip arp
), スパニングツリー情報 (show spanning-tree
) などからも、L2/L3の接続性や依存関係の手がかりを得ることができます。 - Pythonからは、NetmikoやParamikoといったSSHライブラリを使ってこれらのコマンドを実行し、結果を文字列として取得します。取得した文字列は、後述のパース処理で構造化します。
- 多くのネットワーク機器は、近隣機器情報を表示するCLIコマンドを提供しています(例: Ciscoの
-
SNMPの利用:
- SNMP (Simple Network Management Protocol) は、ネットワーク機器の状態監視によく用いられますが、MIB (Management Information Base) オブジェクトの中には、機器のインターフェース情報や接続情報を格納しているものがあります(例: LLDP MIB)。
- PythonのPysnmpライブラリなどを使って、SNMPエージェントからこれらの情報を直接取得することも可能です。CLIに比べて構造化されたデータを直接取得できる利点がありますが、SNMPエージェントの設定やMIBの理解が必要になります。
-
APIの利用:
- 比較的新しいネットワーク機器やSDN環境では、RESTConfやNETConfといった標準的なAPIが提供されている場合があります。
- APIを利用することで、構造化されたデータ(XMLやJSON形式)を直接取得でき、パースの手間が省けます。Pythonの
requests
ライブラリ(RESTConf向け)やncclient
ライブラリ(NETConf向け)を活用します。 - ただし、すべての機器がAPIに対応しているわけではありません。
この記事では、最も一般的で多くの既存機器に適用可能な 「CLIコマンドの利用」 に焦点を当てて解説します。
PythonとCLIコマンドで依存関係を収集する
CLIコマンドを使ってネットワーク機器の隣接情報を取得する典型的な方法は、CDP (Cisco Discovery Protocol) や LLDP (Link Layer Discovery Protocol) といったプロトコルによって機器間で交換される情報を利用することです。これらのプロトコルは、直接接続された隣接機器のデバイスID、インターフェース名、プラットフォームなどの情報を通知します。
Pythonでこの情報を収集する基本的な手順は以下の通りです。
- 収集対象の機器リストを用意する。
- 各機器に対し、SSHで接続する。
- 隣接情報を表示するCLIコマンド(例:
show cdp neighbor detail
やshow lldp neighbor detail
)を実行する。 - コマンドの実行結果(文字列)を取得する。
- 取得した文字列から、隣接機器のホスト名、接続元インターフェース、接続先インターフェースなどの必要な情報をパース(構造化)する。
- 収集した隣接関係データを整理し、トポロジデータとして構築する。
- 必要に応じて、収集データを保存したり、グラフ化したりする。
Netmikoを使ったCLIコマンド実行と結果取得
SSH接続とCLIコマンド実行には、多くのネットワーク機器ベンダーに対応したNetmikoライブラリが便利です。
まず、必要なライブラリをインストールします。
pip install netmiko textfsm
TextFSMは、構造化されていないCLI出力から特定の情報を抽出するためのGoogleが開発したテンプレートベースのパースツールです。NetmikoはTextFSMとの連携機能を持っています。
以下は、Netmikoを使ってCisco IOSデバイスに接続し、show cdp neighbor
コマンドを実行して隣接情報を収集する簡単なスクリプト例です。
import json
from netmiko import Netmiko
from netmiko.exceptions import NetmikoTimeoutException, NetmikoAuthenticationException
# 認証情報とデバイス情報
device = {
'device_type': 'cisco_ios',
'host': 'YOUR_DEVICE_IP', # 対象機器のIPアドレスまたはホスト名
'username': 'YOUR_USERNAME',
'password': 'YOUR_PASSWORD',
'secret': 'YOUR_ENABLE_PASSWORD', # enableパスワードが必要な場合
'port': 22 # 通常SSHは22番ポート
}
def get_cdp_neighbors(device_info):
"""
指定されたデバイスからCDP隣接情報を取得する関数
"""
neighbors_list = []
try:
print(f"Connecting to {device_info['host']}...")
# TextFSMで結果を構造化するために use_textfsm=True を指定
with Netmiko(**device_info) as net_connect:
# enableモードへの移行が必要な場合
if 'secret' in device_info:
net_connect.enable()
print("Executing 'show cdp neighbor'...")
# TextFSMテンプレートを使ってパース
# Cisco IOS用のCDPテンプレートがnetmikoに内蔵されている
output = net_connect.send_command("show cdp neighbor", use_textfsm=True)
# 結果はリスト形式で取得される
# 例: [{'DEVICE_ID': 'SW2', 'LOCAL_INTERFACE': 'GigabitEthernet1/0/1', ...}, ...]
neighbors_list = output
print(f"Successfully retrieved {len(neighbors_list)} CDP neighbors from {device_info['host']}")
except (NetmikoTimeoutException, NetmikoAuthenticationException) as e:
print(f"Connection error to {device_info['host']}: {e}")
except Exception as e:
print(f"An error occurred while processing {device_info['host']}: {e}")
return neighbors_list
# スクリプト実行部分
if __name__ == "__main__":
# 実際のデバイス情報に置き換えてください
# 例: 複数の機器を処理する場合、このリストに追加
devices_to_collect = [
{
'device_type': 'cisco_ios',
'host': '192.168.1.10',
'username': 'your_user',
'password': 'your_password',
'secret': 'your_enable_password',
},
# {
# 'device_type': 'cisco_ios',
# 'host': '192.168.1.11',
# 'username': 'your_user',
# 'password': 'your_password',
# 'secret': 'your_enable_password',
# },
# 必要に応じて他の機器情報を追加
]
all_neighbors_data = {} # { 'hostname': [neighbor_info, ...], ... }
for dev in devices_to_collect:
hostname = dev.get('host') # ホスト名をキーとする
neighbor_info = get_cdp_neighbors(dev)
if neighbor_info is not None:
all_neighbors_data[hostname] = neighbor_info
# 収集したデータを表示(ここではJSON形式で見やすく出力)
print("\n--- Collected Neighbor Data ---")
print(json.dumps(all_neighbors_data, indent=4))
# このデータを基にトポロジを構築・可視化する処理を追加できます
# 例:NetworkXライブラリでグラフを構築するなど
コードの解説:
device
辞書に接続に必要な情報を設定します。device_type
はNetmikoが対応するデバイスタイプを指定します。get_cdp_neighbors
関数内で、Netmiko(**device_info)
で接続オブジェクトを作成します。use_textfsm=True
をsend_command
メソッドに指定すると、Netmikoは内蔵または指定されたTextFSMテンプレートを使ってCLI出力を自動的にパースし、構造化されたリスト(辞書のリスト)として返します。これにより、文字列パースの手間を大幅に省けます。if __name__ == "__main__":
ブロック内で、複数の機器に対してこの関数を実行し、結果をall_neighbors_data
辞書にまとめています。
TextFSMによるパース(カスタマイズが必要な場合)
Netmikoに内蔵されていないコマンドや、特定のベンダー・OSバージョンに対応するテンプレートがない場合は、独自のTextFSMテンプレートを作成する必要があります。
TextFSMテンプレートは正規表現と変数の定義を使って、CLI出力のどの部分を抽出し、どの変数に割り当てるかを定義します。
例えば、show lldp neighbor
の簡単な出力例をパースするテンプレートは以下のようになります(実際のテンプレートはより複雑になります)。
CLI出力例:
Device ID Local Intf Holdtime Capability Port ID
SW2 Eth1/1 120 B, R Eth1/5
Router1 Eth1/2 90 R GigabitEthernet0/0
TextFSMテンプレート (lldp_neighbor.textfsm
):
Value Required DEVICE_ID (\S+)
Value LOCAL_INTF (\S+)
Value HOLDTIME (\d+)
Value CAPABILITY (.*)
Value PORT_ID (\S+)
Start
^\s*${DEVICE_ID}\s+${LOCAL_INTF}\s+${HOLDTIME}\s+${CAPABILITY}\s+${PORT_ID} -> Record
このテンプレートファイルを使ってNetmikoでパースするには、send_command
の代わりに send_command_timing
や send_command_expect
などを使用し、取得した文字列に対してTextFSMを直接適用するか、Netmikoのカスタムテンプレート機能を活用します。ただし、use_textfsm=True
は内蔵テンプレートを利用するため最も手軽です。
収集データの加工とトポロジ構築
all_neighbors_data
には、各機器から見た隣接情報が収集されています。このデータは、グラフ構造としてネットワークトポロジを構築するための元情報となります。
例えば、PythonのNetworkXライブラリを使うと、ノード(機器)とエッジ(接続)を持つグラフオブジェクトを構築し、分析や可視化を行うことができます。
import networkx as nx
# 可視化にはmatplotlibやgraphvizなどが必要になる場合があります
# import matplotlib.pyplot as plt
# 前のスクリプトで収集した all_neighbors_data を使用
# グラフオブジェクトの作成
G = nx.Graph()
# 収集データからノードとエッジを追加
# CDPやLLDPは隣接情報なので、双方向のエッジとして扱う
for local_device, neighbors in all_neighbors_data.items():
# ローカルデバイスをノードとして追加
G.add_node(local_device)
for neighbor in neighbors:
remote_device = neighbor.get('DEVICE_ID')
local_intf = neighbor.get('LOCAL_INTERFACE')
remote_intf = neighbor.get('PORT_ID')
# リモートデバイスをノードとして追加
G.add_node(remote_device)
# ローカルデバイスとリモートデバイス間にエッジを追加
# エッジには接続インターフェース情報などの属性を持たせることが可能
if local_intf and remote_intf:
# エッジが存在しない場合のみ追加(重複防止)
if not G.has_edge(local_device, remote_device):
G.add_edge(local_device, remote_device,
local_interface=local_intf,
remote_interface=remote_intf)
# 既存のエッジに属性を追加・更新する場合の考慮も必要
# 構築されたグラフの情報を表示
print("\n--- Network Topology Graph ---")
print(f"Nodes: {list(G.nodes)}")
print(f"Edges: {list(G.edges(data=True))}")
# NetworkXを使ってグラフの可視化を行うことも可能
# 例: nx.draw(G, with_labels=True)
# plt.show() # 可視化ライブラリの設定が必要
このNetworkXを使った例では、CDP/LLDP情報から抽出した機器間の接続関係をグラフ構造に落とし込んでいます。これにより、ネットワークの論理的な接続状態をデータとして表現できます。
実践的な考慮事項
実際の運用環境でネットワークトポロジ自動収集スクリプトを利用する際には、いくつかの考慮点があります。
- 複数ベンダー対応: ベンダーによってCLIコマンドの出力形式やCDP/LLDPの挙動が異なる場合があります。TextFSMテンプレートの追加や、
device_type
に応じたコマンド分岐が必要です。Nornirのような自動化フレームワークは、このような複数ベンダー対応をより効率的に行う機能を提供します。 - 認証情報の管理: スクリプト内に直接パスワードを記述するのはセキュリティリスクがあります。環境変数、設定ファイル、Vaultのような認証情報管理システムなどを利用して、安全に認証情報を扱う必要があります。
- エラーハンドリング: 機器への接続失敗、コマンド実行エラー、パース失敗など、様々なエラーが発生し得ます。try-exceptブロックを使って適切にエラーを捕捉し、処理を継続または中断するロジックが必要です。
- 大規模ネットワークへの対応: 数百、数千台規模の機器から情報を収集する場合、逐次処理では時間がかかりすぎます。Pythonの
concurrent.futures
モジュールや、Nornirのような並列処理機能を内蔵したフレームワークを活用して、処理を並列化することで効率を高めることができます。 - 収集データの活用: 収集したトポロジデータをどう活用するかが重要です。
- 可視化: Graphvizなどと連携して物理/論理トポロジマップを生成する。
- ドキュメント自動生成: 収集データから構成表や接続図を自動作成する。
- CMDB連携: CMDB (Configuration Management Database) にネットワーク機器間の接続情報を自動登録・更新する。
- 構成管理: 設定変更時の影響範囲分析や、設定と実機の比較に利用する。
- 障害解析: 障害発生機器から接続されている機器を迅速に特定する。
まとめ
この記事では、PythonとNetmiko、TextFSMといったライブラリを活用し、ネットワーク機器のCLIコマンドから機器間の依存関係(隣接情報)やトポロジを自動収集する基本的な手法をご紹介しました。手作業に頼りがちなトポロジ情報の収集・維持を自動化することで、運用効率の向上、ドキュメントの正確性維持、そして障害対応や変更管理におけるリスク低減が期待できます。
CLIコマンドによるパースは強力ですが、出力形式の微妙な違いやベンダー差分への対応が必要となります。より堅牢で拡張性の高い自動化を目指す場合は、Nornirのようなフレームワークや、可能であればSNMPやAPIを活用することも検討されると良いでしょう。
収集したトポロジデータを基に、ネットワークの健全性チェック、構成のコンプライアンスチェック、キャパシティプランニングなど、さらに高度な自動化へと発展させる道も開けます。ぜひ、現場の課題に合わせて本記事の手法を応用し、実践的なネットワーク自動化を進めていただければ幸いです。