マルチレイヤなNWトポロジ情報から検証環境を作ってみる(3) - 検討

## はじめに

[前々回 (1)](https://qiita.com/corestate55/items/ba966cc1c73e877f5bee)・[前回 (2)](https://qiita.com/corestate55/items/02f6ce5c6b0af47220dc) とでトポロジ情報を元にした検証環境構築・その実装上の検討点について取り上げました。今回 (3) はこれまでの話の中で個別にあがってきた課題に対しての全体的な検討、それらを受けて次のステップとして考えられることについて見ていきます。

## データモデルをどう考えるか

### データモデルとソフトウェアの構造

いま対象としているトポロジ情報には大きく L3/OSPF/BGP のレイヤが含まれています。単純なネットワーク機能の面から見ても複数の見方ができます。

- ルーティングプロセス的な観点 : OSPF のプロセスと BGP のプロセスは独立している : L3-OSPF, L3-BGP
- ネットワーク全体の経路制御の観点 : EGP である BGP は IGP である OSPF に依存している : L3-OSPF-BGP

![viewpoint of layer dependency](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154230/cd02c2f4-f544-2703-3a93-7350d7edf4f2.png)

トポロジ情報を作る上では、BGP process が特定の OSPF process に依存しているかというとそんなことはないので、前者 (上図左) の観点でレイヤ設定していました。tinet config を出力するところでも同様で、レイヤごとに動作確認できた方がいい。でも、最終的に L3 + OSPF + BGP のコンフィグを出力する必要があって、そうなると後者 (上図右) のような作りの方が都合がいい。両立させようとすると多重継承みたいな形になるので、いまは  mix-in で機能とクラス構造の付替えをやってたりします。下図、"All" が 上図右相当です。(作りながらいじっているのでもうちょっときれいな形になりそうな気はする…。)

![class/module structure](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154230/d4723ae8-e159-100f-20c9-5b8855c1c194.png)

などといろいろまとめを書きつつふと思ったんだけど、これ 1-device : 1-ospf process : 1-bgp process だって前提がずれるとまたずれるか……?

### 既存データモデル (YANG) の拡張

いまのところ OSPF, BGP については RFC8346 (Layer3) のデータモデル定義の flag とかにムリヤリ設定情報を埋め込む形にしてしまっているけど、本当は RFC8345/8346 の定義済みのモデルを拡張して、独自のデータモデルを YANG で定義してそれを元に作っていかないといけない。YANG ベースにすることによって、[各種データ変換・XMLツールでの支援が受けられるようになる](https://qiita.com/corestate55/items/2d226b745d1c452c0a25)……んだけど、正直 YANG 書かないといけないのしんどい。

あと、拡張するとしても、もうちょっと考えないといけない。あまりに全部オレオレ定義にしちゃうと結局ロックイン問題が発生してしまう。OpenConfig とか既存のベンダ netconf とか見てみて、よさそうな OSPF, BGP 設定情報のもたせ方を考えたいところなんだけどこのあたりはまだこれから。トポロジ情報として組む場合、neighbor の表現はノード間リンクで表現できたり…etc があるので、単純なデバイス設定のデータ構造と必ずしも同じにはならない/同じにする必要がないだろうなと思ってます。

本当は実働している環境 (検証環境でも実環境でも) のステータス情報とかを図にマッピングして、「いまどう動いているのか」を可視化したりしたいんだけどね。そういうのを含めてデータモデル考えていかないといけないかな。

### わざわざトポロジ情報に組みなおす必要があるのか?

今回の例だと、config file (Cisco IOS, 元環境) → CSV → RFC8345 json → config file (tinet/FRR, 検証環境) という流れです。それで、いろいろデータをいじるわけですが、間の CSV → RFC8345 のところは最終的に必要な情報としては同じものがあって、RFC8345 ではノード間・ネットワーク(レイヤ)間の関係性に注目したデータの作りに変換しているだけ。単純に検証環境のコンフィグをつくることに目的を限定して考えれば、あえて RFC8345 を挟まずに 元 config → CSV → 検証用 config にもできるはず。 

あえてトポロジ情報 (RFC8345) として組みなおす理由は何かとなると、やっぱり人が「トポロジ情報」を読み取るとしてわかりやすいのはどちらか……ではないかなと思います。トポロジ情報にすることで、[過去記事で上げたような可視化の話](https://qiita.com/corestate55/items/8a39af553785fd77c20a)が組み合わせられる。いまは単純にデータ変換するだけの話をしていますが、[1 回目のはじめに書いたように](https://qiita.com/corestate55/items/ba966cc1c73e877f5bee)、構成(トポロジ)情報を基にした操作をしたいわけです。つまりこんな感じにしたい :

* 実環境がいまどうなっているのかを実働している情報を基に構成情報を作る
* 構成情報を可視化して、変更箇所・変更前後の構造の確認をする
* 変更後の構成情報を基に検証環境を構築する
* 検証環境で動作を確認し、問題があればまた構成情報を修正する
* 検証環境で確認済みの構成情報を基に、実環境へのオペレーションを出力する

それに RFC8345 が適しているかどうかはまた考えないといけないですが、少なくとも独自に拡張していく余地のある、プレーンな枠組みだとはいえるかな。

### データモデルのバランス

データを集めて可視化することを考えていた時は、コンフィグ → データ → 可視化 の流れだったので、見たいもの・見方を軸にデータモデルを考えていました。が、今回これに対して、そこから「実体 (模擬環境・NW機器コンフィグ)」を起こす観点を足しています。そのため、コンフィグとかを起こすためには、「図」やそこに含まれる属性としてこういう情報を定義すべきだ、という点が出てくる。

今回、OSPF/BGP などについては対象ネットワークを絞って限られた機能だけを定義してやっていましたが、それでも片方向 (コンフィグ → データ → 可視化) の流れだけだといろいろ足りていないものがあるのが見えてきました。それに応じてレイヤの切り方・依存関係の考え方・属性として必要な情報などを設定してやらないといけない。OSPFでは、設定起こすだけなら 1area-1network (layer) として定義したほうが楽だけど、「図」としてみたいとなったときにエリアごとに断片化しちゃうよりは OSPF「面」としてまとめてみたい、みたいなことがあるのではないか…とか。人がどう捉えるか (どう表現すると理解しやすいか) と、機械で処理するときの使いやすさをどうバランスさせていくかを考える必要がありそうです。

![balance](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154230/77a89791-b0b2-6d37-c598-fba8fec6d2d2.png)

## ネットワークの "コンパイラ"

やや余談なんですが、今回やったデータ変換はコンパイラの構造に似ています。コンパイラは、入力 (ソースコード) を字句解析して構文解析して、構文木をつくってそれに応じて対応する処理を吐き出していく。今やっているデータ変換では対応するものを考えてみるとこうなります :

- ソースコード : Cisco IOS config
- 字句解析 : Batfish が必要なパラメタ読み取って CSV にしてくれる
- 構文解析 : Batfish が出力した CSV から RFC8345 json を作るスクリプト
- 構文木 : RFC8345 json
- 構文木をもとに処理をする : 今回作った、RFC8345 json をもとに tinet & FRR config を出力するスクリプト
    - 最終的に、Cisco で組んでたネットワークを、PC上の FRR のネットワークとして再構成 ("コンパイル") している……機器コンフィグ + 環境全体の構造(トポロジ) を含めて。

知識やノウハウが入ってくるのは構文解析のところです。対象とするネットワークで何が使われているか、どういう情報が取得されるか、それらをどのようにつなぎ合わせてトポロジ情報に変換していくか。NWの知識だけではなくて環境・設計・制約についての知識なんかも含めてかな。それらをつなぎ合わせたものが構文木としてのトポロジ情報 (RFC8345 json)で、今回試している構文木を元にした処理のところではそれを解釈していくだけ。これは構文解析のときの「意図」: 何のためにどのデータをつなぎ合わせたのかを解釈して対応する出力を作る処理になります。

こんな感じの流れなので、最終段の構文木をもとに処理するところを替えれば、ほかのベンダ機器コンフィグやほかのネットワークエミュレータ用のデータ等に置き換えていくことができるはずです。今のコードは FRR 用のコマンドがコード中に埋め込まれちゃってるので汎用性がないんですよね……。みたいな課題についてコンパイラのアナロジーで考えれば、既存のコンパイル済みモジュールと結合していくリンカの処理なんかがはいってくるわけです。それに相当するものを考えると、例えばコンフィグ生成用のテンプレートエンジンみたいなものとの組み合わせなんかが考えられそうです。

## テストをどう考えるか

トポロジデータを読んで検証環境を作るフローについてテストを考えると、大きく二つの観点があると思います。

- データ変換 (トポロジデータを読んで検証環境および各ルータのコンフィグに変換) するプログラムの入出力の正しさをテストすること。
    - 単純に、インプットに対して適切な(想定した)アウトプットが出力されているかどうか、データ処理プログラムとしてのテストの観点 : 入出力を見て静的にテストができる。
    - tinet の spec.yaml についていえば、spec.yaml の中に入れたコンフィグやパラメータが元の RFC8345 トポロジデータを正しく反映したものになっているかどうか。
- でき上った検証環境のなかで、作りたかったネットワークができているか・想定される状態になっているか・想定される動作が確認できるか。
    - 実環境(本番環境)の構成を模擬する検証環境を作る、そもそもやりたかったことができているかどうかの観点 : 実際に出来上がったネットワーク内で、各種情報を確認したり実際に動かして状態変化を確認したり、やりたかったことに応じて動的に確認していくところ。
    - tinet の spec.yaml の、`test`セクションの部分

従来のように、実環境の構成・状態・設計書を基に手作業で環境を構築するようなケースを考えると、まず静的なテストを入れられることがメリットになるだろうと思っています。NW (機器) のパラメータって、自分単独で決まるものではなくて、対向・隣接・接続先など関係性に応じて決まるので、図 + 複数の資料をまたがって追いかけることが多いですよね。変更前後で何がどう変わるか、その時に "環境全体での" パラメータ対応関係の整合性が取れているかどうかなど、「図を見ながら」複数の資料やデータを突き合わせてチェックするようなものは、トポロジデータを中心に自動テストできるようになるんじゃないでしょうか。(参考 : [モデルベースのNW図で差分を可視化する](https://qiita.com/corestate55/items/8c50b4f6cbee4caa0cbc))

そのうえで従来も行っていたような、環境内で実際の動作や状態変化を見ていくような動的なテストがあります。このあたりはこれまでの手順書などを流用できるでしょう。今回やったように、トポロジ情報を基にテストケースを自動生成したりもできます。Tinet のように仮想環境として起動して複数のノード操作をまとめてできれば、手作業でやっていた操作やデータ収集・確認作業のを動化して作りこんでいくこともいろいろ考えられそうです。(ノード単品・ノード内部の設定変更だけではなくて、トポロジの操作など環境の構造自体が操作できるかどうかがポイント)

動的なテストをしてみて想定していた動きをしていなければ、それをフィードバックしてまた修正していくことになります。本来望んでいた動作 → そのための設定・コンフィグ等はなにか → それらをトポロジデータの中でどう定義するか → トポロジデータの解釈・検証環境設定への出力への変換処理をどう変えるか、みたいなフローですね。遠回りに思えるかもしれませんが、重要なのはこれらの経緯がすべてコードとして蓄積・改良していけることだと思います。

## 次のステップとして考えられること

こんなことができたらいいなリストは現時点だとこんなところでしょうか。

- データモデルの拡張
    - bgp/ospf 設定出力のための情報をちゃんと定義して持たせるようにする (YANG 拡張)
- 特定ツールへの依存性を減らす
    - 検証環境全体のコントロール (tinet), 個々のプロダクト (FRR) についてのあれこれがハードコードされている。
    - このあたりを上手く切り離せるような仕組みをどう考えるか?
- 検証環境→実環境への変換
    - 入力を FRR config, 出力を Cisco IOS config としてやれば、検証環境でコンフィグ等修正→本番(実環境)操作用のコンフィグ、というのができるはず。このあたりは Batfish に FRR config 食わせたときの確認ができれば、後は出力するコンフィグを替えるだけで同様の流れで作れるのでは。(技術的には同等)
    - ただ、これをやるなら本当は、トポロジ情報を編集 → tinet/FRR あるいは Cisco IOS config をつくる方向、つまり「構成図」を書いたら検証環境あるいは必要なコンフィグを出力できるものを考えるのが良いのではなかろうか。トポロジ情報 (「構成図」) をうまいこと編集する方法と、それを即時検証環境に反映させる方法、みたいなのを考えたいところ。
- トポロジ情報をうまく操作するためのインタフェース
    - 環境全体の構成を宣言的に扱いたい。でも、ちょっとした修正とか個別にオペレーションを見れば命令的に操作したい。CLIインタフェースで構成情報をちょっとずつ修正するような何かがあってもいいかもしれない
        - 検証環境で直接実機にコマンド入れたものをフィードバックさせるのはややこしくないか?
        - 構成・構造・あるべき姿に関する情報と動的な状態とで分けて考えないといけないかな
    - だとすると、最終的に環境に反映される前に差分の確認とか, candidate/running でどう変わるか確認して commit → (検証)環境への反映みたいな操作が、データモデル (text) + ビュー (「構成図」) の双方でやれないか
    - 図を操作した際、そのデータを実機コンフィグに落とすのは "コンパイラ" がやってくれるのであれば、実は L3, 各プロトコルの基礎知識があれば個々の機器コンフィグについての知識がなくてもネットワークをコントロールできるようになったりする……?
- 実物の情報マッピング
    - どうにかして現物の構成情報およびステータス情報をとってくる
    - それをビュー(可視化した図)のうえにマッピングする
    - それができるだけリアルタイムに近い状態でやれないか、ステータス → 図へのマッピングをうまいことやれる枠組みがつくれないか

## おわりに

これといってオチがあるわけでもないんですがここまでです。こういう話を考えているので興味のある方は声かけてもらえればお話しできると思います。あるいは、ちょっと動かしてみたいんだけどわかんねーよとか、気になるところとかがあったらそれはそれで連絡ください。