How to build Batfish

## はじめに

Batfish : [https://www.batfish.org/](https://www.batfish.org/) 使ってますか? 使っている方はおそらく batfish project が配布しているコンテナイメージを持ってきて動かしていると思います。docker pull するだけでつかえますしね。

使っていると「あーこのコンフィグのこの設定読んでくれないのかー!!」 みたいになったことありませんか? そういうとき、Batfish のパーサーをいじって読めるようにできないかなー、できたらいいのになーと思いますよね。

パーサーのお試し修正をちょっと試してみたんですが、今回はその前段階・事前準備として batfish のビルドの方法の話をしようと思います。ビルドができないとコード修正したとして試しようがないですしね。じゃあその次はパーサーの修正方法の話があるのか? と期待するかもしれませんがすみませんちょっとそこまではたどり着かないかも…。

…というのも、Batfish は java で書かれているんですが、java のコードって書いたことがないし、パーサー (ANTLR) とか java project で使用されるツールセットとかの話も知識がないのです。なので今回の話も見様見真似の域を出ていません。ちゃんと java の知識経験がある人から見ると怪しい内容があるかもしれませんがそういうのはご指摘いただけるとありがたいです。

## 参照

本体のビルドについてはドキュメントがありますが、コンテナイメージのビルドについては細かい話はほとんどない…ということもあって今回はそのあたりを取り上げます。

- [Building and running Batfish and its tests](https://github.com/batfish/batfish/blob/master/docs/building_and_running/README.md)

## ビルド環境のセットアップ

Ubuntu 22.04 で試しています。

```
$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 22.04.1 LTS
Release:        22.04
Codename:       jammy
```

いれるもの

- OpenJDK 11
- Bazelisk
    - Batfish はビルドに bazel というツールを使っています。
    - Bazel を複数のプロジェクトで使い分けるためのツールとして Bazelisk があります。Bazelisk を使うと、プロジェクトで指定されている bazel version を自動でダウンロードして使ってくれます。プロジェクトでの bazel version は `.bazelversion` というファイルで設定されます。

JDK のインストール

```bash
sudo apt install openjdk-11-jdk
```

Bazelisk のインストール

- 手元の環境には golang がインストールしてあったので、ここでは `go install` でインストールしました
- Golang がない場合、バイナリを持ってきてインストールできます ([マニュアル参照](https://github.com/batfish/batfish/blob/master/docs/building_and_running/README.md#ubuntu))

```bash
go install github.com/bazelbuild/bazelisk@latest
exec $SHELL -l
```

## コードの取得

Github Batfish organization から2つのリポジトリを使用します。

- [batfish/batfish](https://github.com/batfish/batfish) : Batfish 本体のコードです
- [batfish/docker](https://github.com/batfish/docker) : Batfish コンテナイメージを作るためのコードです
    - Batfish 本体をビルドしてローカルで直接 batfish process を実行することもできますが、ほとんどの人はコンテナとして batfish を使用していると思います。利用形態に合わせるため、コンテナイメージの作成もやります。

以降の操作は、どちらのリポジトリについても、2022-09-18 時点の main head を使用しています。

```bash
cd ~/batfish/
git clone https://github.com/batfish/batfish.git
git clone https://github.com/batfish/docker.git
```

## Batfish本体の修正〜実行

以降 batfish repository ディレクトリ (`~/batfish/batfish` ) で操作します。

### コードの修正

今回 Batfish のコード修正についてはあまりどうこう言えるほど理解していないのでここはパスです。
batfish/docs に開発者向けドキュメントがありますが、2022-09-18 時点では概要レベルの話で細かいことはあまり書かれていません。

- [Batfish developer documentation](https://github.com/batfish/batfish/tree/master/docs)

### テスト

既存の機能への影響がないかどうかを確認しておきます。

```bash
bazelisk test //...
```

### ビルド

コードを修正したらローカルで動かして動作確認をしてみます。ビルドと実行をまとめてやってくれるスクリプトがあるのでそれを使用します。

```bash
tools/bazel_run.sh
```

ビルドだけをする場合

```bash
bazelisk build //projects/allinone:allinone_main
```

これで、 `bazel-bin/projects/allinone/allinone_main.jar` ができているはずです。ビルド済みのバイナリから起動だけをやる場合このようにします。

```
./bazel-bin/projects/allinone/allinone_main --jvm_flag=-Xmx12g -runclient false -coordinatorargs "-templatedirs ./questions"
```

コンテナイメージを作る場合、コンテナに入れるための jar ファイルを作っておきます。

```bash
bazelisk build //projects/allinone:allinone_main_deploy.jar
```

### 動作確認

ビルドしてローカルに batfish を起動できたら、実際に pybatfish をつかってクエリを投げてみましょう。[pybatfish のインストール等については前の記事を参照してください](https://qiita.com/corestate55/items/17555e92ebc2026b2d7c)。例えばこんな感じで対話的にクエリを実行することができます。

```bash
python -i
```

```python
from pybatfish.client.session import Session
import pandas as pd

bf = Session() # localhost
bf.list_networks()
bf.set_network('network-name')
bf.list_snapshots()

# load snapshot
bf.init_snapshot('/path/to/configs', name="snapshot-name", overwrite=True)
bf.list_snapshots()

# query: e.g. IP address list of all nodes
bf.q.ipOwners().answer().frame()
```

## Batfishコンテナイメージの作成

以降 docker repository ディレクトリ (`~/batfish/docker` ) で操作します。

### 余談: コンテナイメージビルド方法の変化

コンテナイメージのビルドについては、実は 2022/8 月中に大きく変わりました。実はビルド手順については最初、8月頭くらいに調べていたのですが、1ヶ月ちょっとあけて git pull してもう一度試したら動かず。調べるとごっそり変わっていたという状況です。[master のコミットログ](https://github.com/batfish/docker/commits/master) 見てもらうとわかるのですが、8月くらいから Github Actions でのビルドに移行が進んでいて、最終的には前の方式 (ビルド用のコンテナイメージを作ってビルドする・buildkite というツールを使う) が削除されています ([PR#109](https://github.com/batfish/docker/pull/109))。

どの時点の何をビルドしたいかで使うものや方法が変わりうるので注意してください。

### 参照

- [Building and running Batfish and its tests](https://github.com/batfish/batfish/blob/master/docs/building_and_running/README.md)
    - 前述の通りコンテナイメージの作成については情報がほとんどないです
- [docker/reusable-precommit.yml at master · batfish/docker](https://github.com/batfish/docker/blob/master/.github/workflows/reusable-precommit.yml)
    - Batfish 本体のビルド〜コンテナイメージ作成までを Github Actions で実行するようになっています。コンテナイメージ作成にあたって何をしたらいいのか書いてある資料とかは特になさそう。

### ビルドした jar ファイルと questions の取得

Batfish コンテナイメージのビルドは Github Actions で自動化されてるんですが、自前で修正加える場合はまず手元で作って動かしたいと思うので、Actions の中の手順をベースに手動でやるならこうなるだろう、という形に書き下してみます。

batfish repository ディレクトリから、ビルドされたもの = コンテナイメージに含める jar ファイルと questions を持ってきます。

```bash
# ~/batfish/docker

# collect "artifacts"
mkdir -p assets
cp ../batfish/bazel-bin/projects/allinone/allinone_main_deploy.jar assets/allinone-bundle.jar
cp -r ../batfish/questions/ assets/
cp log4j2.yaml assets/

# build container image
BF_SHA=$(cd ../batfish; git rev-parse --short HEAD)
BF_VERSION=$(date +'%Y.%m.%d').$BF_SHA.selfbuild
docker build --build-arg ASSETS=assets -f batfish.dockerfile -t batfish/batfish:$BF_VERSION .

```

- `ASSETS` で指定したディレクトリにおいてあるものがコンテナイメージに導入されます。
- とりあえず、いつ時点のビルドなのかを明示するために、タグは日付 + Batfish本体のコミットハッシュ + `selfbuild` としてみました。

```
$ docker image ls | grep selfbuild
batfish/batfish                          2022.09.19.0250aa7ac2.selfbuild   b1e4a6adca48   16 seconds ago   330MB

```

あとはこれまで使っていたオフィシャルのイメージの代わりに `batfish/batfish:2022.09.19.0250aa7ac2.selfbuild` を使用するだけです。

## まとめ

Batfish を自前でビルドする方法・コンテナイメージにする方法についてまとめました。一式作るだけならどちらもそれほど難しい手順は必要ではありませんが、ドキュメントが薄いのでリポジトリ内のコード等から必要なものを抽出するのがちょっと大変でしたね。

本当は Batfish 本体コードの修正、コードの中身の話ができるといいんですが、いかんせん java についての知識がないので難しく…。このへんもうちょっと開発者向けのドキュメントを出してもらえるとありがたいんですけどねえ。