GNS3でSONiCを使う

# きっかけ

動作検証のためSONiCがインストールされた複数のスイッチを制御するにあたり、CLIで3つのコンソールを操作するのは手間がかかり効率も悪くミスも多く確認もしづらいので、REST APIを使ってみようと考えました。

が、SONiCのREST APIに関するドキュメントがほとんど見当たりません。実装前に書かれる設計ドキュメントはあるのですが、スキーマなどの情報は載っていません。これは動かして調べてみるしかないな、と思ったわけですが、よく考えるとREST APIの調査であればスイッチ実機で試す必要はなく、ソフトウェア実装の仮想スイッチで試せると気がつきました。

SONiCの仮想スイッチ実装(SONiC-VS)自体は動かしたことがあるのですが、複数のインスタンスを用意してポート間を接続して、それらと通信するREST APIクライアントを用意して、など必要な作業を思い浮かべたとき、それらをいちいち手作業で構築するのも面倒だと感じ、そういえばGNS3があるじゃないか、ということで動かしてみることにしました。

# GNS3とは

名前はGraphical Network Simulator-3の略だそうです。ルータやスイッチ、ホストPCなどのアイコンを図上に配置してケーブルを接続するよう図を描画するとトポロジが構成され、それぞれの機器は仮想マシンとして動作させることができるネットワークシミュレータです。

WindowsやmacOSでも動かせますしLinux版もあります。

# 準備

## SONiC-VSのビルド

ここではソースコードからビルドする手順を説明します。[SONiC公式のJenkinsによるビルド結果](https://sonic-jenkins.westus2.cloudapp.azure.com/job/vs/job/buildimage-vs-all/lastSuccessfulBuild/artifact/)からビルド済みイメージを取得することもできます。

ビルドに必要なストレージは100GBくらいだったと思います。インターネット接続環境が必要です。ファイヤウォールの内側からだとproxyの設定がけっこう面倒だと思います。

[公式のビルド手順](https://github.com/Azure/sonic-buildimage/blob/master/README.md)はいろんなケースに対応するため説明が冗長になっています。シンプルな手順を下記に載せますが、うまくいかなかったり迷ったら公式の説明どおりにやるのがいいと思います。

```
git clone https://github.com/Azure/sonic-buildimage
cd sonic-buildimage
make init
make configure PLATFORM=vs
make target/sonic-vs.img.gz
```

## GNS3 Applianceの作成

SONiC-VSはKVM用イメージファイルとして作成されますが、それをそのままGNS3にインポートすることはできません。appliance file (suffixは`.gns3a`)を作成する必要があるのですが、[作成用のスクリプト](https://sonic-jenkins.westus2.cloudapp.azure.com/job/vs/job/buildimage-vs-all/lastSuccessfulBuild/artifact/platform/vs/sonic-gns3a.sh)がありますので、それを利用します。

```
gzip -dc target/sonic-vs.img.gz > /tmp/sonic-vs.img
cp platform/vs/sonic-gns3a.sh /tmp/
cd /tmp
chmod +x sonic-gns3a.sh
./sonic-gns3a.sh -r 1.1 -b sonic-vs.img
```

以上で`SONiC-1.1.gns3a`が作成されます。`sonic-vs.img`とともに、GNS3を起動してインポートするまで保持しておきます。インポートが終われば削除して構いません。

## GNS3のインストール

[公式サイトの手順](https://docs.gns3.com/1QXVIihk7dsOL7Xr7Bmz4zRzTsJ02wklfImGuHwTlaA4/index.html)に従ってインストールします。SONiCをビルドできる環境であればdockerは導入済みですので、そこはスキップします。こちらの環境はUbuntu 20.04なので、下記でした。

```
sudo add-apt-repository ppa:gns3/ppa
sudo apt update                                
sudo apt install gns3-gui gns3-server```
```

# GNS3の起動とSONiC-VSのインポート

起動は簡単、`gns3 &` です。

![gns3-startup.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/546046/b6ac6d2c-d9e5-6536-bbe2-f4e4e86f27c6.png)

GNS3は起動すると、新規プロジェクトを作成するか既存プロジェクトを開くか問い合わせてきます。そのままOKを押すと、新規プロジェクト`untitled`の編集画面になります。

インポートは左上のルータアイコンをつついて一覧の下にある`New template`をクリック。表示されるダイヤログで`Import an appliance file (.gns3 extension)`を選んで、作成しておいた`/tmp/SONiC-1.1.gns3a`を選んで取り込みます。

# いざ作図!

アイコンをドラッグ&ドロップして、線でつなぐだけなので簡単と言えば簡単です。

![sonic-restapi-test-topology.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/546046/19a6e90b-fdfe-cb7f-a583-dd679c282ca9.png)

線でつなぐときに、SONiCアイコンからは`Ethernet0`〜`Ethernet9`のいずれかのポートから選択するのですが、このあたりに罠が存在します。

あとでそれぞれのSONiC仮想ルータにログインしてIPアドレスを設定などしていくのですが、**GNS3で見えているEthernet0は、SONiC仮想ルータ内でのEthernet0ではない**のです。`Ethernet1`も`Ethernet2`も、SONiC仮想マシン内での名称は違うものになります。

具体的には

|GNS3上のポート|SONiC仮想マシン内でのポート|
|------------|-----------------------|
|Ethernet0   |eth0                   |
|Ethernet1   |Ethernet0              |
|Ethernet2   |Ethernet4              |
|Ethernet3   |Ethernet8              |

といったかんじになります。SONiCにおいて`eth0`はいわゆるマネジメントポートになります。長々といろいろ書いていますが、記事として残すべきもっとも重要な情報はこの表だけです。

※追記: `.gns3a`ファイルに `"first_port_name": "eth0"` を追加してからインポートすると、最初のポートが`eth0`、次から`Ethernet0`,`Ethernet1`となるので多少マシになるという情報をいただきました。SONiC公式が提供する生成スクリプトが洗練されてないようです。

※追記2: スクリプトのパッチを作って[PR](https://github.com/Azure/sonic-buildimage/pull/4694)しました。取り込まれるかは謎。

※追記3: パッチがmasterに入りました。よかった。変更後は下記のようになっています。

|GNS3上のポート|SONiC仮想マシン内でのポート|
|------------|-----------------------|
|eth0        |eth0                   |
|Ethernet0   |Ethernet0              |
|Ethernet1   |Ethernet4              |
|Ethernet2   |Ethernet8              |


# 仮想ルータ、仮想マシンの設定

GNS3がいろいろやってくれるのはトポロジの構築と仮想マシンの起動や終了であって、それぞれの仮想マシン(仮想ルータ)の設定までやってくれるわけではないようです。

まずは仮想マシンの起動ですが、GNS3画面上部にある緑のプレイボタンを押すと、それぞれの仮想マシンが一斉に起動します。起動すると、線の端点の小さな赤い箱が緑色に変化します。

起動後、それぞれのアイコンの右メニューで`Console`を選ぶとSONiCでは端末エミュレータが起動し、ログインプロンプトが出るのでログインします。ビルド時に指定していればそのアカウントとパスワード、SONiCの公式Jenkinsから取得した場合は`admin`, `YourPaSsWoRd`になります。

あとはそれぞれ設定すれば構築はおしまいです。