モデルベースのNW図で差分を可視化する

## はじめに

### まちがいさがし

突然ですが、まちがいさがしをしてみましょう。

左が変更前の構成図、右が変更後の構成図です。何がどう変更されたか、パッとわかりますか? (この図はテスト用に作った図なのであまり意味のある図ではありません。なので「そもそも NW 自体がおかしい」とかそのへんは気にしないで、前後で変わってるところを探してみてください……)

![problem](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154230/afaa2616-1695-250f-8f97-39f39face6f2.png)

### こたえ

こたえは下の図のとおり。

![answer](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154230/4eae096d-cd4d-504c-85a6-519fa6204372.png)

* 赤線: 変更前にはあったけど変更後ではない (削除された) ところ
* 緑線: 変更前にはなかったけど変更後にはある (追加された) ところ

単純に NW 図に直接描かれるもの (ノードとかポートとか) だけでも何がどう変わったかをちゃんと追いかけるのは結構面倒です……たとえそれがこのくらいの小さなネットワークだとしても。実際には、直接図の構成要素として描かれない変更 (構成要素としては同じだけど設定が違う……例えばポートの IP が変更されたとか) もあり、もっと面倒なチェックをすることになります。

今回考えたいのは、こういった面倒な作業をもうちょっとどうにかできないのか? というところ。

### モデルベースのNW図差分チェック

これまで  [Batfish を使ってネットワーク構成を可視化してみよう \(1\)](https://qiita.com/corestate55/items/8a39af553785fd77c20a)  [\(2\)](https://qiita.com/corestate55/items/9d8023eb19637f9bbd1e)  [\(3\)](https://qiita.com/corestate55/items/10673ef74c33a24a0389) と、「モデルベースにネットワーク構成を可視化する」という話をしてきました。今回は、その流れで、上に書いたような「まちがいさがし」をモデルベースなやりかたではどう扱えるだろうか、という話をします。

## 元データ(NW構成図)を作る

[このスクリプト](https://github.com/corestate55/netomox-examples/blob/develop/model_defs/model_diff_test/multiple.rb) で変更前後の NW 構成を定義しています。実行すると変更前・変更後の NW 構成データを出力します。

* [test_multiple1.json](https://gist.github.com/corestate55/3dc31adc24e2352a23cb71f28472c409#file-test_multiple1-json) : 変更前の NW 構成データ
* [test_multiple2.json](https://gist.github.com/corestate55/3dc31adc24e2352a23cb71f28472c409#file-test_multiple2-json) : 変更後の NW 構成データ

```

hagiwara@exp01:~/nwmodel/netomox-examples$ bundle exec ruby model_defs/model_diff_test/multiple.rb
hagiwara@exp01:~/nwmodel/netomox-examples$ ls model_defs/model
test_multiple1.json  test_multiple2.json
hagiwara@exp01:~/nwmodel/netomox-examples$
```

## CLIで差分を見てみる

### json-diff

単純に前後のデータ (json) に対して行単位の diff とったってしんどいだけなので、もうちょっと違うツールを使いましょう。ためしに [json\-diff](https://www.npmjs.com/package/json-diff) で差分を出してみます。 [結果はこんな感じ](https://gist.github.com/corestate55/3dc31adc24e2352a23cb71f28472c409#file-json-diff-result) になります。この後の自製ツールとの比較をするにあたって気になるところをピックアップしてみます。

ひとつ困るのは構成要素の順序依存があるところですね。汎用ツールだと、(同じ要素でも)順序の変化があればそれを差分として出しますが、意図のあるデータフォーマットなので「同じインスタンスとして扱うべきもの」は順序によらず同じインスタンス同士で比較をしたいわけです。 ……という話をチェックするために `seg_c` はあえて前後 (test_multiple1/2)  で定義順序変えてあるんですよね。[6-81 行目](https://gist.github.com/corestate55/3dc31adc24e2352a23cb71f28472c409#file-json-diff-result-L6)、`seg_c` は本当はポートがひとつ増えるだけなのにノード全体に変更があったかのように見えています。このように、比較するデータの中で定義されている位置が変わっていると、変わっていなくても変化があるとか、変更されているところよりも広く変化があるように見えてしまいます。

```diff
       {
         node: [
+          {
+            node-id: "seg_c"
+            ietf-network-topology:termination-point: [
+              {
+                tp-id: "p0"
+              }
+              {
+                tp-id: "p1"
+              }
+            ]
+            supporting-node: [
+              {
+                network-ref: "layer1"
+                node-ref: "sv2"
+              }
+            ]
+            ietf-l3-unicast-topology:l3-node-attributes: {
+              flag: [
+              ]
+              router-id: [
+              ]
+              prefix: [
+                {
+                  prefix: "192.168.30.0/24"
+                  metric: 100
+                  flag: [
+                  ]
+                }
+              ]
+            }
+          }
           {
             ietf-network-topology:termination-point: [
               ...
-              {
-                tp-id: "p1"
-                supporting-termination-point: [
-                  {
-                    network-ref: "layer1"
-                    node-ref: "sw1"
-                    tp-ref: "gi0"
-                  }
-                ]
-              }
             ]
           }
           ...
-          {
-            node-id: "seg_c"
-            ietf-network-topology:termination-point: [
-              {
-                tp-id: "p0"
-              }
-            ]
-            supporting-node: [
-              {
-                network-ref: "layer1"
-                node-ref: "sv2"
-              }
-            ]
-            ietf-l3-unicast-topology:l3-node-attributes: {
-              flag: [
-              ]
-              router-id: [
-              ]
-              prefix: [
-                {
-                  prefix: "192.168.30.0/24"
-                  metric: 100
-                  flag: [
-                  ]
-                }
-              ]
-            }
-          }
```

もうひとつのサンプルとしてはリンク、たとえば [235-242 行目](https://gist.github.com/corestate55/3dc31adc24e2352a23cb71f28472c409#file-json-diff-result-L235) とかでしょうか。同様に、`vm2,eth1,seg_b,p0` と `vm1,eth1,seg_b,p0` は本当は異なるリンク (異なるインスタンス) なのに、同じインスタンスで属性値が違うだけ、という見え方になっています。

```diff
           {
-            link-id: "vm2,eth1,seg_b,p0"
+            link-id: "vm1,eth1,seg_b,p0"
             source: {
-              source-node: "vm2"
+              source-node: "vm1"
             }
           }
```

### netomox diff

汎用ツールは RFC8345 ベースのデータでどの要素を同一とみなすか……なんて知っているわけがないので、順序依存がある・異なる要素(インスタンス)が同じ要素であるかのように扱われてしまう。当たり前のことですね。なのでソレ (RFC8345 ベース JSON) 用の diff ツールを試しに作ってみました。

```

hagiwara@exp01:~/nwmodel/netomox-examples$ bundle exec netomox diff -c model_defs/model/test_multiple1.json model_defs/model/test_multiple2.json
```

[この結果はこんな感じになります](https://gist.github.com/corestate55/3dc31adc24e2352a23cb71f28472c409#file-netomox-diff-result-txt)。テキスト貼り付けの都合で地味になってしまいますが、実際には下の画像のように色 (`-c/--color`) をつけて出せます。色と行頭文字の意味は以下の通り。

* 行頭 `-` (赤): 削除されたもの
* 行頭 `+` (緑): 追加されたもの
* 行頭 `.` (黄): それ自体は増減していないが内部の情報に変化があったもの

![netomox-diff view for terminal](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154230/b8055cca-15cc-4e7b-fbd3-7b298108d114.png)

では `json-diff` の例でピックアップしたのと同じところを出してみましょう。

`seg_c` ([30-40行目](https://gist.github.com/corestate55/3dc31adc24e2352a23cb71f28472c409#file-netomox-diff-result-txt-L30) : インスタンスの ID (`node-id`) で同一の要素を比較しているので順序依存がありません。

```diff
.           {
.             node-id: seg_c,
.             ietf-network-topology:termination-point: [
+               {
+                 tp-id: p1
+               }
.             ],
              supporting-node: [

              ]
.           },
```

同じ要素が変更されたかのように見えていたふたつのリンクについては、下記のように異なる要素として扱われています。

* [189-199行目](https://gist.github.com/corestate55/3dc31adc24e2352a23cb71f28472c409#file-netomox-diff-result-txt-L189)

```diff
-           {
-             link-id: vm2,eth1,seg_b,p0,
-             source: {
-               source-node: vm2,
-               source-tp: eth1
-             },
-             destination: {
-               dest-node: seg_b,
-               dest-tp: p0
-             }
-           },
```

* [211-221行目](https://gist.github.com/corestate55/3dc31adc24e2352a23cb71f28472c409#file-netomox-diff-result-txt-L211)

```diff
+           {
+             link-id: vm1,eth1,seg_b,p0,
+             source: {
+               source-node: vm1,
+               source-tp: eth1
+             },
+             destination: {
+               dest-node: seg_b,
+               dest-tp: p0
+             }
+           },
```

## 差分情報の可視化

さて、モデルベースに差分をとれたとして、それをどう可視化するかを考えます。上の CLI の例 (`netomox diff`) は、モデルベースの差分情報を terminal 向けに表示(表現)するツールになっています。データはそのままでフロントエンドをブラウザに変更し、差分情報をブラウザ上でグラフィカルに表示するようにします。

まずは、差分情報を含んだモデルデータをファイルに保存します。 [出力結果はここで見れます。](https://github.com/corestate55/netoviz/blob/develop/public/model/diff_test.json)

```

hagiwara@exp01:~/nwmodel/netomox-examples$ bundle exec netomox diff -o ~/nwmodel/netoviz/public/model/diff_test.json model_defs/model/test_multiple1.json model_defs/model/test_multiple2.json
```

データとしては、「RFC8345 ベースの JSON」なのは既存のままです。ただ、そこに構成要素 (node, term-point, link etc) の差分情報が埋め込んであります。元のデータ構造は変えていないので、いままで作っていた可視化ツールは原則そのままでよくて、そのうえで「差分情報」を追加表示する、という形にしました。

では、最初にあげた「まちがいさがし」の差分を表示してみましょう。

![netomox-diff view for browser](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154230/f74e79e8-94f8-fdc2-f694-07f05b96c2ec.png)

ここで線の色は以下の意味を持ちます。(CLI 版 (`netomox diff`) の色と同じです。)

* 赤: 削除されたもの
* 緑: 追加されたもの
* 黄: 増減はないけど変化があったもの

![netomox-diff view animation](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154230/1cc327e6-ef11-0407-7835-4d7b52ca213a.gif)

なお、上記のようにアニメーション [^heroku] できるようにしてみました。変更前後の構成を切り替えて表示するイメージですね。

[^heroku]: [heroku にデプロイしてあるのでそちらで試せます](https://netoviz.herokuapp.com/) : " Topology Data" selection box で "diff viewer test data" を選択してください。なお、今回のエントリでは [可視化してみよう \(2\)](https://qiita.com/corestate55/items/9d8023eb19637f9bbd1e) で解説している "nested view" だけ載せていますが、[可視化してみよう \(1\)](https://qiita.com/corestate55/items/8a39af553785fd77c20a) で説明したほかの view (topology, dependency) でもスタイル合わせて差分情報を見せられるようにしてたので、そっちも見てもらえるとよいと思います。

## おわりに

今回は、NW 構成をモデルデータで表現したときに、変更前後の差分をとって、(1) CLI (2) ブラウザ上の図で表示(可視化)してみました。手書き NW 構成図を見比べるやり方と比べてどうでしょうか?

「図」の変更チェックって面倒なんですよね。構成の増減があればレイアウトも変わるし、変化があるから変わっているかというとそうでもなかったりして。結局ひとつひとつ図中の構成要素を確認・突き合わせしていったりとか、前後の図を印刷して重ねて光に透かしてみたりとか……。構成図をデータとして起こすことができれば、構成要素の増減とか、要素が持っている属性の変化とか、そういった話を機械的に追いかけてチェックすることができます。さらに、今回みたいなやり方で、手描き・紙印刷ではできない表現 (presentation) をしてみるとか、工夫もいろいろできるようになるんじゃないでしょうか。