7
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

いまからはじめるpybatfish

Last updated at Posted at 2022-01-22

はじめに

Batfish というネットワーク機器のコンフィグレーションファイルを解析してくれる便利ツールがあります。batfish 自体はクライアント・サーバ型で使うようになっていて、API は REST なんですが、その python wrapper として batfish/pybatfish があります。ここでは batfish の基本的な「管理単位」とそれを pybatfish から扱うための方法を step-by-step に見ていきます。

batfish および pybatfish については過去何回か記事を書いているのですが、私が書いているスクリプトがちょっと古くてですね。ちゃんと現時点版でのあれこれをまとめておこうという意図もあります。

セットアップ

オフィシャルのチュートリアルでは all-in-one 版のコンテナを持ってきて、同梱されている jupyter notebook 上で操作しよう、という流れになっています。それはそれでいいのですが、今回は python script で batfish 操作していくための土台として考えているので、もうちょっとベタな方法で見ていきます。(と言ってもたいした違いはないですが)

all-in-one 版ではなく通常版のコンテナを落とします。jupyter とかが同梱されていないので軽量です。

docker pull batfish/batfish

2022-01-22 時点 latests ではこれくらいサイズが違います。

$ docker image ls | grep batfish
batfish/allinone          latest    5ba3c111cc7c   5 weeks ago     1.08GB
batfish/batfish           latest    bb872fd73111   5 weeks ago     403MB

pybatfish をインストールします。これは pip でインストールできます。(ここでは python/3.8.3, pip/21.3.8 を使っています)

pip install pybatfish

pybatfish 2021.11.4.1095 です

$ pip list | grep batfish
pybatfish         2021.11.4.1095

pybatfish version に関する余談

このあと Welcome to the Batfish documentation! — pybatfish 0.36.0 documentation ってドキュメントを参照しながら書いてるんですが、この title のところに "0.36.0" ってありますよね。

pip install したバージョンと違うので何だろうというので探して見るんですが、pybatfish · PyPI の Release history も Releases · batfish/pybatfish も YYYY.MM.DD な日付ベースのバージョン設定なんですよ。じゃあ 0.36.0 ってどっから来たバージョンなんだと。おそらくこの __init__.pyで定義してあるやつじゃないかと思うんですが (python package 作ったことないのでよくわかってない)、このファイルの last commit が “15 Oct 2019”…。

ということで、この 0.36.0 っていうのがあまり信用なりません。このバージョン番号を指定してもインストールはできるみたいなんですが、もし日付ベースのバージョンになっていなければ、いったん消して入れ直すほうが良いかもしれません。

$ pip install pybatfish==2021.11.4.1095
...省略...
Successfully installed pybatfish-2021.11.4.1095
$ pip list | grep batfish
pybatfish         2021.11.4.1095

分析対象の準備

あらかじめ、batfish で解析したいネットワーク機器の設定ファイルを用意します。batfish は決まったディレクトリ構成に基づいてデータを読むのと、”snapshot” という考え方があるので、まずはそれを押さえておきましょう。

Snapshot

Batfish が分析する対象は、単一の NW 機器コンフィグファイルではなく、複数 (環境全体) の NW 機器コンフィグファイルです。「環境全体」を把握するために、NW 機器コンフィグファイル以外にも、物理 (L1) トポロジ情報、エンドポイント情報等のデータを同梱できます。これらのデータ一式をまとめて “snapshot” と呼びます。

1つの環境(ネットワーク)に対して、例えば設定変更作業の前後や日次など、複数の snapshot を設定できます。後ほど出てきますが、Batfish は下の図のような考え方で “snapshot” を持っています。

  • batfish は複数の network を持つ
  • network は複数の snapshot を持つ
  • Batfish は 1 つの snapshot を指定して分析を実行する

batfish_basics.png

ディレクトリ構成

snapshot ごとにディレクトリを用意し、その中に特定の名前でファイルやディレクトリを設置します。

  • snapshot-directory/
    • configs/ (必須)
    • hosts/
      • ホスト (endpoint) 設定ファイル
    • iptables/
      • ホスト (endpoint) の iptables 設定ファイル
    • batfish/
      • layer1_topology.json : L1トポロジ情報
      • isp_config.json : ISP (インターネット接続) 情報
      • runtime_data.json : インターフェスの実行時状態 (runtime state) 情報
      • external_bgp_announcements.json : 外部 AS との BGP 情報

configs 以外のディレクトリはオプショナルです。そこで設置するファイルについては Format of vendor and supplemental data を参照してください。(わたしも全部を使ったことがあるわけではないのであまり細かく説明できません…)

Sample snapshots

…と、いうのを踏まえて今回はサンプルのコンフィグを使います。

pybatfish step-by-step

ref: Interacting with the Batfish service — pybatfish 0.36.0 documentation

Batfish (server) の起動

Batfishコンテナを起動します。

$ docker run -d --rm --name batfish -p 9996:9996 -p 9997:9997 batfish/batfish
59cf4ad5ca6dc67ee38e4a5e2f501949c8eca197db5b12e86f3a6cf14aa23dc7
$ docker container ls
CONTAINER ID   IMAGE             COMMAND                  CREATED          STATUS         PORTS                                                           NAMES
59cf4ad5ca6d   batfish/batfish   "java -XX:-UseCompre…"   10 seconds ago   Up 9 seconds   0.0.0.0:9996-9997->9996-9997/tcp, :::9996-9997->9996-9997/tcp   batfish

pybatfish (client) の実行

corestate55/batfish-test-topology を clone して、そのディレクトリに移動して作業していきます。

$ git clone https://github.com/corestate55/batfish-test-topology.git
$ cd batfish-test-topology

python を対話モードで起動します。

$ python -i
Python 3.8.3 (default, Jan  1 2021, 16:39:20) 
[GCC 9.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>>

pybatfish Session を作ります

>>> from pybatfish.client.session import Session
>>> bf = Session()
>>>

Session の引数を何も指定しないと localhost に接続します。別途 batfish service を起動している場合は host 引数を指定します。(引数なしの場合は Session(host="localhost") と同等)

snapshot の登録

次は分析したい NW機器コンフィグファイルを登録していくのですが、先に、snapshot を束ねる単位である network を設定しておきます。

>>> # ネットワーク一覧を出してみるけどまだ何もない
>>> bf.list_networks()
[]
>>> # ネットワークを登録する
>>> bf.set_network("batfish-test-topology")
'batfish-test-topology'
>>> bf.list_networks()
['batfish-test-topology']
>>>
>>> # ネットワークの中に snapshot が...まだ入っていない
>>> bf.list_snapshots()
[]

では snapshot を登録しましょう。batfish-test-topology にはいくつかの snapshot が入っています。そのうちの1つを登録してみます。

>>> bf.init_snapshot('./l2/sample3', name="l2_sample3")
'l2_sample3'
>>> bf.list_snapshots()
['l2_sample3']

登録できました。

分析 (query) の実行

snapshot が登録できれば、対象とする snapshot を選択して分析 (batfish query) を実行できます。

ここでは L1 edges (物理リンク情報) を取得してみます。

>>> bf.list_snapshots()
['l2_sample3']
>>> bf.set_snapshot('l2_sample3')
'l2_sample3'
>>> bf.q.edges(edgeType='layer1').answer().frame()
                        Interface               Remote_Interface
0                    host13[eth0]  switch2[GigabitEthernet1/0/3]
1                    host14[eth0]  switch2[GigabitEthernet1/0/4]
2                    host22[eth0]  switch1[GigabitEthernet1/0/6]
3   switch2[GigabitEthernet1/0/4]                   host14[eth0]
4          switch1[Port-channel1]         switch2[Port-channel1]
5          switch2[Port-channel1]         switch1[Port-channel1]
6                    host23[eth0]  switch2[GigabitEthernet1/0/7]
7   switch2[GigabitEthernet1/0/8]                   host24[eth0]
8   switch1[GigabitEthernet1/0/6]                   host22[eth0]
9                    host21[eth0]  switch1[GigabitEthernet1/0/5]
10  switch2[GigabitEthernet1/0/7]                   host23[eth0]
11                   host11[eth0]  switch1[GigabitEthernet1/0/1]
12                   host12[eth0]  switch1[GigabitEthernet1/0/2]
13                   host24[eth0]  switch2[GigabitEthernet1/0/8]
14  switch1[GigabitEthernet1/0/5]                   host21[eth0]
15  switch2[GigabitEthernet1/0/3]                   host13[eth0]
16  switch1[GigabitEthernet1/0/1]                   host11[eth0]
17  switch1[GigabitEthernet1/0/2]                   host12[eth0]

この query では pandas dataframe として結果を取得しています(frame())。query によっては異なるオブジェクトで返ってきます。それらのデータオブジェクトを扱う際は対応するデータモデルをインポートしておく必要があります。

from pybatfish.datamodel import *
from pybatfish.datamodel.answer import *
from pybatfish.datamodel.flow import *

異なる snapshot の登録・使い分け

l2/sample3/batfish ディレクトリの layer1_topology.jsonを編集して、host11-switch1 間のリンクを消します。(片方向ずつ 2 本セットで双方向になっている点に注意)

diff --git a/l2/sample3/batfish/layer1_topology.json b/l2/sample3/batfish/layer1_topology.json
index 5f482be..5260579 100644
--- a/l2/sample3/batfish/layer1_topology.json
+++ b/l2/sample3/batfish/layer1_topology.json
@@ -1,25 +1,5 @@
 {
   "edges": [
-    {
-      "node1": {
-        "hostname": "switch1",
-        "interfaceName": "GigabitEthernet1/0/1"
-      },
-      "node2": {
-        "hostname": "host11",
-        "interfaceName": "eth0"
-      }
-    },
-    {
-      "node1": {
-        "hostname": "host11",
-        "interfaceName": "eth0"
-      },
-      "node2": {
-        "hostname": "switch1",
-        "interfaceName": "GigabitEthernet1/0/1"
-      }
-    },
     {
       "node1": {
         "hostname": "switch1",

この状態で、別な snapshot 名をつけて登録してみましょう。

>>> bf.init_snapshot('./l2/sample3', name="l2_sample3_sw1_gi101_off")
'l2_sample3_sw1_gi101_off'
>>> bf.list_snapshots()
['l2_sample3_sw1_gi101_off', 'l2_sample3']

同様に L1 edges を取得してみます。

>>> bf.set_snapshot('l2_sample3_sw1_gi101_off')
'l2_sample3_sw1_gi101_off'
>>> bf.q.edges(edgeType='layer1').answer().frame()
                        Interface               Remote_Interface
0                    host13[eth0]  switch2[GigabitEthernet1/0/3]
1                    host14[eth0]  switch2[GigabitEthernet1/0/4]
2                    host22[eth0]  switch1[GigabitEthernet1/0/6]
3   switch2[GigabitEthernet1/0/4]                   host14[eth0]
4          switch1[Port-channel1]         switch2[Port-channel1]
5          switch2[Port-channel1]         switch1[Port-channel1]
6                    host23[eth0]  switch2[GigabitEthernet1/0/7]
7   switch2[GigabitEthernet1/0/8]                   host24[eth0]
8   switch1[GigabitEthernet1/0/6]                   host22[eth0]
9                    host21[eth0]  switch1[GigabitEthernet1/0/5]
10  switch2[GigabitEthernet1/0/7]                   host23[eth0]
11                   host12[eth0]  switch1[GigabitEthernet1/0/2]
12                   host24[eth0]  switch2[GigabitEthernet1/0/8]
13  switch1[GigabitEthernet1/0/5]                   host21[eth0]
14  switch2[GigabitEthernet1/0/3]                   host13[eth0]
15  switch1[GigabitEthernet1/0/2]                   host12[eth0]
>>>

layer1_topology.json で指定したとおり、host11-switch1 間のリンクが 2 本 (片方向ずつ、双方向) 見えなくなりました。snapshot l2_sample3 と同じNW機器コンフィグファイルを使用していますが、L1 topology が違う別な snapshot として扱うことができています。

おわりに

pybatfish の基本的な使い方をまとめてみました。pybatfish v2021.11.04 tag のリリースノートに “examples and documentation have been updated to use Sessions” とあるので、おそらくこのへんから Session 中心の書き方に変わっています。

古い書き方は legacy_docs の方に残してあるみたいなんですが、Session が明示的に出てこなかったりします。ディレクトリ構成も追加データの json file が batfish ディレクトリじゃなくて snapshot ディレクトリ直下にあったりするかも。混乱しないように注意してください。

このあたりの管理方法等は変わってきているので、これから使う方はいま一度ドキュメントを参照するようにしてください。

7
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?