実践ネット自動化スクリプト集

現場で役立つPython:ネットワーク機器の依存関係とトポロジを自動収集

Tags: Python, ネットワーク自動化, トポロジ, Netmiko, TextFSM, CLI自動化

はじめに

ネットワークインフラの構成情報は、運用・保守、トラブルシューティング、変更管理など、あらゆるフェーズで不可欠です。特に機器間の接続関係や依存関係を正確に把握することは、障害発生時の影響範囲特定や、変更作業時のリスク評価において非常に重要となります。

しかし、ネットワーク構成は絶えず変化しうるものであり、手作業で最新の情報を維持することは困難を伴います。ドキュメントはすぐに陳腐化し、実際の接続状態と乖離してしまうことが少なくありません。

システムエンジニアやインフラエンジニアの皆様にとって、Pythonを活用してネットワーク機器の操作や管理を自動化するスキルは、既に必須のものとなりつつあります。この記事では、Pythonスキルを活かし、ネットワーク機器から直接、機器間の依存関係やトポロジ情報を自動的に収集するための具体的な手法をご紹介します。CLIコマンドの実行と結果のパースを中心に、実践的なスクリプト例を交えながら解説を進めます。

なぜネットワークの依存関係・トポロジを自動収集するのか

ネットワーク機器の依存関係(例:どの機器がどの機器にL2/L3で接続されているか)や全体のトポロジを手作業で把握するには、以下のような課題があります。

Pythonによる自動収集は、これらの課題を解決し、常に最新のネットワーク構成情報を迅速かつ正確に取得することを可能にします。

ネットワーク依存関係・トポロジ収集のアプローチ

ネットワーク機器から依存関係やトポロジ情報を得るための主なアプローチはいくつか存在します。

  1. 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ライブラリを使ってこれらのコマンドを実行し、結果を文字列として取得します。取得した文字列は、後述のパース処理で構造化します。
  2. SNMPの利用:

    • SNMP (Simple Network Management Protocol) は、ネットワーク機器の状態監視によく用いられますが、MIB (Management Information Base) オブジェクトの中には、機器のインターフェース情報や接続情報を格納しているものがあります(例: LLDP MIB)。
    • PythonのPysnmpライブラリなどを使って、SNMPエージェントからこれらの情報を直接取得することも可能です。CLIに比べて構造化されたデータを直接取得できる利点がありますが、SNMPエージェントの設定やMIBの理解が必要になります。
  3. 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でこの情報を収集する基本的な手順は以下の通りです。

  1. 収集対象の機器リストを用意する。
  2. 各機器に対し、SSHで接続する。
  3. 隣接情報を表示するCLIコマンド(例: show cdp neighbor detailshow lldp neighbor detail)を実行する。
  4. コマンドの実行結果(文字列)を取得する。
  5. 取得した文字列から、隣接機器のホスト名、接続元インターフェース、接続先インターフェースなどの必要な情報をパース(構造化)する。
  6. 収集した隣接関係データを整理し、トポロジデータとして構築する。
  7. 必要に応じて、収集データを保存したり、グラフ化したりする。

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ライブラリでグラフを構築するなど

コードの解説:

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_timingsend_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情報から抽出した機器間の接続関係をグラフ構造に落とし込んでいます。これにより、ネットワークの論理的な接続状態をデータとして表現できます。

実践的な考慮事項

実際の運用環境でネットワークトポロジ自動収集スクリプトを利用する際には、いくつかの考慮点があります。

まとめ

この記事では、PythonとNetmiko、TextFSMといったライブラリを活用し、ネットワーク機器のCLIコマンドから機器間の依存関係(隣接情報)やトポロジを自動収集する基本的な手法をご紹介しました。手作業に頼りがちなトポロジ情報の収集・維持を自動化することで、運用効率の向上、ドキュメントの正確性維持、そして障害対応や変更管理におけるリスク低減が期待できます。

CLIコマンドによるパースは強力ですが、出力形式の微妙な違いやベンダー差分への対応が必要となります。より堅牢で拡張性の高い自動化を目指す場合は、Nornirのようなフレームワークや、可能であればSNMPやAPIを活用することも検討されると良いでしょう。

収集したトポロジデータを基に、ネットワークの健全性チェック、構成のコンプライアンスチェック、キャパシティプランニングなど、さらに高度な自動化へと発展させる道も開けます。ぜひ、現場の課題に合わせて本記事の手法を応用し、実践的なネットワーク自動化を進めていただければ幸いです。