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

Pythonで作るネットワーク自動化基盤:Nornirの活用法

Tags: Python, Nornir, ネットワーク自動化, IaC, 自動化基盤

はじめに

システム開発やクラウドインフラの運用において、自動化は不可欠な要素となっています。Infrastructure as Code(IaC)やCI/CDパイプラインの導入が進む中、ネットワーク機器の設定や状態管理も自動化の対象とすることが求められています。

しかし、ネットワーク機器の操作には特有のプロトコル(SSH/CLI, NETCONF, RESTCONFなど)や設定構文があり、開発経験が豊富でもネットワーク機器に不慣れな方にとってはハードルを感じるかもしれません。また、単一のスクリプトで特定のタスクを自動化することは可能ですが、管理対象機器の増加や自動化タスクの複雑化に伴い、メンテナンスが困難になるケースも少なくありません。

このような課題に対し、Pythonでネットワーク自動化を体系的に行うためのフレームワークとして「Nornir」が注目されています。Nornirは、ネットワーク機器のインベントリ管理、並列実行、プラグインによる拡張性などを提供し、より堅牢で再利用可能な自動化スクリプトの開発を支援します。

本記事では、Pythonスキルを活かしてネットワーク自動化をさらに一歩進めたいと考えるエンジニアの皆様に向けて、Nornirの基本的な考え方、環境構築、そして簡単な活用方法について解説します。

Nornirとは

Nornirは、Pythonで書かれたネットワーク自動化フレームワークです。その最大の特長は、自動化対象となるネットワーク機器の情報を一元管理し、定義されたタスクを柔軟に実行できる構造にあります。単なるライブラリとしてではなく、自動化ワークフロー全体を管理するための「基盤」としての機能を提供します。

Nornirを導入する主なメリットは以下の通りです。

Nornirの基本要素

Nornirは主に以下の要素で構成されます。

  1. Inventory: 管理対象のネットワーク機器に関する情報です。ホスト名、IPアドレス、認証情報、OSタイプ、所属グループなどを定義します。通常、hosts.yaml, groups.yaml, defaults.yamlといったYAMLファイルで構成されますが、データベースなど他のソースから取得するプラグインもあります。
  2. Configuration: Nornir自体の動作設定です。Inventoryファイルの場所、Runnerの種類、プラグイン設定などを定義します。通常はconfig.yamlというファイルを使用します。
  3. Tasks: ネットワーク機器に対して実行したい具体的な処理です。例えば、「特定のコマンドを実行する」「設定ファイルを投入する」「インターフェースの状態を取得する」といった操作がタスクとして定義されます。Nornirのプラグインによって様々なタスクが提供されています。
  4. Runners: 定義したタスクをどのように実行するかを制御します。タスクを直列に実行するか、並列に実行するかなどを指定できます。デフォルトでは並列実行を行うRunnerが使用されます。
  5. Result: タスク実行後の結果オブジェクトです。各機器でのタスクの成否や、取得したデータなどが含まれます。

環境構築と最小構成

Nornirを使い始めるための基本的な環境構築手順を説明します。

まず、Pythonがインストールされていることを確認してください。NornirはPython 3.6以上が必要です。次に、pipを使ってNornirとSSH接続を行うためのnornir-netmikoプラグインをインストールします。

pip install nornir nornir-netmiko pyyaml

pyyamlはNornirの設定ファイルやInventoryファイルを扱うために必要です。

次に、Nornirの実行に必要な設定ファイルとInventoryファイルを作成します。プロジェクトのルートディレクトリに以下の3つのファイルを作成します。

config.yaml:

---
inventory:
  plugin: HostYaml # YAMLファイルからInventoryをロードするプラグインを指定
  options:
    host_file: "hosts.yaml"
    group_file: "groups.yaml"
    defaults_file: "defaults.yaml"

# Runnerの設定(ここではデフォルトの並列Runnerを使用)
# runner:
#   plugin: threaded # 並列実行runner
#   options:
#     num_workers: 20 # 最大並列実行数

# Logging設定(任意)
# logging:
#   log_file: "nornir.log"
#   level: INFO

hosts.yaml:

---
device1: # ホスト名
  hostname: 192.168.1.10 # 接続先IPアドレスまたはホスト名
  platform: cisco_ios # プラットフォームタイプ (Netmikoがサポートするもの)
  groups: # 所属グループ
    - routers
  data: # ホスト固有の追加情報(任意)
    location: "Tokyo"

device2:
  hostname: 192.168.1.20
  platform: juniper_junos
  groups:
    - routers
    - production

# 実際の認証情報はdefaults.yamlまたはgroups.yamlに記述することが推奨されます
# username: your_username
# password: your_password

groups.yaml:

---
routers: # グループ名
  username: your_ssh_username # グループ共通のユーザー名
  password: your_ssh_password # グループ共通のパスワード
  # port: 22 # グループ共通のポート(任意)

production: # 別のグループ
  data:
    environment: "prod"

defaults.yaml:

---
# hosts.yaml や groups.yaml で指定されていない場合に適用されるデフォルト値
username: default_ssh_username
password: default_ssh_password
port: 22
platform: generic # デフォルトのプラットフォーム(必要であれば)
# data:
#   some_default_setting: "value"

これらの設定ファイルにより、Nornirはどの機器に対して、どのような認証情報や設定で接続すればよいかを認識します。認証情報はdefaults.yamlgroups.yamlに記述することで、hosts.yamlに直接書くよりも安全性を高められます。(ただし、本番環境では環境変数やシークレット管理ツールとの連携を検討すべきです。)

Nornirを使った簡単なコマンド実行

環境構築ができたら、実際にNornirを使ってネットワーク機器にコマンドを実行してみましょう。ここでは、すべてのルーター機器に対してshow ip interface briefコマンドを実行する例を示します。

以下のPythonスクリプトを作成します。

run_command.py:

from nornir import InitNornir
from nornir_netmiko.tasks import netmiko_send_command
from nornir.core.filter import F # Filterをインポート

def get_interface_status(task):
    """
    対象ホストに対して 'show ip interface brief' コマンドを実行し、結果を表示するタスク関数
    """
    # netmiko_send_command は nornir-netmiko プラグインによって提供されるタスク
    # task.host は現在のタスクが実行されている対象ホストオブジェクト
    result = task.run(task=netmiko_send_command, command="show ip interface brief")

    # 結果は Result オブジェクトとして返される
    # result[0] は通常、タスク関数内の最初の task.run() の結果
    # .result に実際のコマンド実行結果(文字列)が格納されている
    print(f"--- {task.host.name} のインターフェース状態 ---")
    print(result[0].result)
    print("-" * 30)

def main():
    # config.yaml を読み込んで Nornir オブジェクトを初期化
    # auto_enforce_subset=True で、失敗したホストは後続のタスクから自動的に除外される
    nr = InitNornir(config_file="config.yaml", auto_enforce_subset=True)

    # 特定のグループに属するホストのみを対象とするフィルター
    # F() を使用して条件を指定
    routers = nr.filter(F(groups__contains="routers"))

    # フィルターされたホストに対して、get_interface_status タスク関数を実行
    # task=get_interface_status で、この関数が各ホストに対して並列実行される
    # name は Nornir の結果出力で見やすくするためのタスク名(任意)
    results = routers.run(task=get_interface_status, name="Get Interface Status")

    # タスク全体の実行結果を処理
    # results オブジェクトには各ホストの結果が含まれる
    # for host, result in results.items():
    #     if result.failed:
    #         print(f"--- {host} でタスクが失敗しました ---")
    #         print(result.exception) # 失敗した場合は例外情報などを参照可能
    #         print("-" * 30)

if __name__ == "__main__":
    main()

コードの解説:

このスクリプトを実行するには、前述のconfig.yaml, hosts.yaml, groups.yaml(適切な認証情報に置き換えてください)、そしてこのrun_command.pyを同じディレクトリに置き、以下のコマンドを実行します。

python run_command.py

設定が正しければ、定義したルーター機器にSSHで接続し、コマンドを実行した結果がホストごとに表示されるはずです。

実践的な活用と発展

上記の例は非常にシンプルですが、Nornirはこれを基盤として、より複雑な自動化を実現できます。

まとめ

本記事では、Pythonを使ったネットワーク自動化のための強力なフレームワークであるNornirの基本について解説しました。Nornirを使うことで、ネットワーク機器のInventory管理、タスクの並列実行、プラグインによる多様な機器/プロトコル対応などが可能になり、スケーラブルでメンテナンス性の高い自動化基盤を構築できます。

開発経験が豊富で、ネットワーク機器の操作に慣れていない方も、Pythonコードで直感的に自動化タスクを記述できるNornirは非常に扱いやすいツールです。まずは小規模な環境でInventoryファイルと設定ファイル、簡単なタスクスクリプトを作成し、実際にコマンド実行や情報取得から試してみることをお勧めします。

ネットワーク自動化は、IaCやCI/CDを実現する上でますます重要になります。Nornirを学び、実践することで、システムの自動化領域をさらに広げることが可能となるでしょう。今後は、より具体的なタスク例や他のプラグインの活用方法についてもご紹介していく予定です。