はじめに
ホワイトボックススイッチ用OSのSONiCですが、Barefoot, Broadcom, Mellanox等さまざまなスイッチベンダーのASICに対応するために、内部ではASICに依存しない共通レイヤーが用意されています。Switch Abstration Interface (SAI)と呼ばれるC言語のAPIです。本稿では、SONiCがどのようにして抽象度の高い指示(e.g. CLI)をSAIにマッピングしているか、どうやってSAIのAPIを呼び出しているか、またその副産物について解説したいと思います。
SAI
本家はこちら。
URLを見ていただければわかるとおり、Open Compute Project (OCP)によって仕様が策定されています。
どういうものを仕様としてまとめているか大まかに説明すると、スイッチの機能を各社のスイッチASICがそれぞれのSDKで扱える単位まで分解したオブジェクトを定義し、オブジェクトに対しての操作も定義したもの、となります。操作にはオブジェクトの作成、削除、パラメータ設定、パラメータや状態の参照と行ったものがあり、パラメータは属性(attribute)として指定します。
SAIの仕様はOCPによって管理されていますが、実装はそれぞれのスイッチASICベンダーが提供します。ベンダーによってはソースコードが公開されているようですが、ソースコード非公開でSAIのライブラリはバイナリ提供としているベンダーが多いようです。
ふわっとまとめると、SONiCはSAIのAPIを呼び出すことでスイッチASICを制御している、と言えます。
オブジェクトや操作ですが、具体的に並べると下記のようなものです。
- オブジェクト
- Switch
- Virtual Router
- Port
- Router Interface
- Nrighbor
- Nexthop
- Route entry
- 等
- 操作
- create
- remove
- set
- get
- オブジェクト独自の操作
属性はオブジェクトの種類によって様々で多岐にわたります。たとえば、PortはどのVirtual Routerとつながってるか、ポートスピード、リンク状態、といった属性を持っています。
これらの他、SAIの初期化だったりAPIの呼び出しエントリ取得だったりといった機能が用意されています。
なお、仮想マシン版SONiC (SONiC-VS) では、SAIの実装(libsaivs
; src/sonic-sairedis/vslib
)の中身はほとんどなにもしないスタブ実装です。主にユーザープロセス(bgpd
等)のために用意されるHost Interfaceオブジェクトを作る部分が実装されていて、その他についてはSAIが提供するよりも抽象度の高いレイヤーで処理されています(たとえばIPアドレスを追加する場合、Linuxのip address add
コマンドが呼び出されます)。
おおまかな処理フロー
SONiC内部での設定処理フローは、機能によって多少違いがあるのですがおおよそ下記のようになっています。
-
config
コマンド等がCONFIG_DBに書き込む -
cfgmgr
の各プロセスがCONFIG_DBの内容を読み取り、APPL_DB用の内容に翻訳して書き込む -
orchagent
プロセスが、APPL_DBの内容をSAIの機能単位に分解し、ASIC_DBに書き込む -
syncd
がASIC_DBの内容を読み取り、SAIを呼び出す - SAIの実装の中でASICを制御する
前出の「Linuxコマンドの呼び出し」は主にcfgmgr
の役割になります。
CLIからSAIへ
IPアドレス設定を例に、流れを解説します。
1.CLIでconfig
コマンドを入力
sudo config interface ip add Ethernet0 192.168.2.1/24
2.CONFIG_DBに設定が加わる
sonic-db-dump -n CONFIG_DB
で見ると下記のような感じになります。
"INTERFACE|Ethernet0|192.168.2.1/24": {
"type": "hash",
"value": {
"NULL": "NULL"
},
"ttl": -0.001,
"expireat": 1670442233.688389
},
3.cfgmgr/intfmgrd
がCONFIG_DBを読んでAPPL_DBに書く
intfmgrd
は、インタフェース関連の情報を扱うプログラムです。
sonic-db-dump -n APPL_DB
でみると下記。
"INTF_TABLE:Ethernet0:102.168.2.1/24": {
"type": "hash",
"value": {
"scope": "global",
"family": "IPv4"
},
"ttl": -0.001,
"expireat": 1670442372.047751
},
4.orchagent
がAPPL_DBを読んでASIC_DBに書く
5. syncd
がASIC_DBから情報を読み取ってSAIコールする
どうもSAIコールのために情報を読み出した後消してしまうようで、設定後にASIC_DBの中をダンプしてもIPアドレス情報は拾えませんでした。
syncd
syncd
は、SONiCが動作する上で不可欠なプロセスです。なぜなら、syncd
はSAIのオブジェクト操作の手続きを一手に引き受けているからです。異常終了時にご丁寧にスイッチオブジェクトを削除するので、syncd
が止まったらSONiCは動作を停止します。show services
でsyncd dockerが見当たらなかったら、そのSONiCは正常動作していません。
syncdのコマンドライン
通常syncd
は自動的に起動されるので、コマンドラインについて知る必要はないかもしれません。が、指定されているパラメータから読み取れるものがあるかもしれませんので、少し見てみることにします。
ps
というでみると、下記のようなコマンドラインで呼ばれているのがわかります。
/usr/bin/syncd --diag -u -s -p /etc/sai.d/sai.profile -b /tmp/break_before_make_objects
パラメータについて見てみます。
- --diag
- -u
- -s
- -p /etc/sai.d/sai.profile
- -b /tmp/break_before_make_objects
ヘルプに解説がありました。
admin@sonic:~$ docker exec -it syncd bash
root@sonic:/# syncd --help
Usage: syncd [-d] [-p profile] [-t type] [-u] [-S] [-U] [-C] [-s] [-z mode] [-l] [-g idx] [-x contextConfig] [-b breakConfig] [-h]
-d --diag
Enable diagnostic shell
-p --profile profile
Provide profile map file
-t --startType type
Specify start type (cold|warm|fast|fastfast)
-u --useTempView
Use temporary view between init and apply
-S --disableExitSleep
Disable sleep when syncd crashes
-U --enableUnittests
Metadata enable unittests
-C --enableConsistencyCheck
Enable consisteny check DB vs ASIC after comparison logic
-s --syncMode
Enable synchronous mode (depreacated, use -z)
-z --redisCommunicationMode
Redis communication mode (redis_async|redis_sync|zmq_sync), default: redis_async
-l --enableBulk
Enable SAI Bulk support
-g --globalContext
Global context index to load from context config file
-x --contextConfig
Context configuration file
-b --breakConfig
Comparison logic 'break before make' configuration file
-h --help
Print out this message
なにやら、Redisを使わずにZMQで情報をやり取りするモードもあるようです。大容量向けでしょうか?
syncdの内部動作
大雑把に説明すると、syncd
は「ASIC_DBの更新情報を読み取って、内容のとおりにSAIのAPIをコールする」を繰り返すのが中心的な動作になります。その他としては、定期的に状態参照のSAI APIコールを実行し、得られた情報をSTATE_DBに書き込むという動作があります。
/var/log/swss/sairedis.rec
では実際にSONiCはどのようなSAI呼び出しをしているのでしょうか。実はそれを確かめる手段が存在します。それが/var/log/swss/sairedis.rec
です。
実機でも仮想マシンでもかまいません、SONiCを起動させ、ログインできたら/var/log/swss/sairedis.rec
を開いてみてください。テキストファイルですので、ページャやエディタで開けます。
2022-12-02.20:41:06.551657|#|recording on: /var/log/swss/sairedis.rec
2022-12-02.20:41:06.551731|a|INIT_VIEW
2022-12-02.20:41:06.554573|A|SAI_STATUS_SUCCESS
2022-12-02.20:41:06.557623|c|SAI_OBJECT_TYPE_SWITCH:oid:0x21000000000000|SAI_SWITCH_ATTR_INIT_SWITCH=true|SAI_SWITCH_ATTR_FDB_EVENT_NOTIFY=0x559d788ca6a0|SAI_SWITCH_ATTR_PORT_STATE_CHANGE_NOTIFY=0x559d788ca6b0|SAI_SWITCH_ATTR_SWITCH_SHUTDOWN_REQUEST_NOTIFY=0x559d788ca6d0|SAI_SWITCH_ATTR_SRC_MAC_ADDRESS=52:54:00:12:01:00
2022-12-02.20:41:06.660269|g|SAI_OBJECT_TYPE_SWITCH:oid:0x21000000000000|SAI_SWITCH_ATTR_DEFAULT_VIRTUAL_ROUTER_ID=oid:0x0
2022-12-02.20:41:06.661564|G|SAI_STATUS_SUCCESS|SAI_SWITCH_ATTR_DEFAULT_VIRTUAL_ROUTER_ID=oid:0x3000000000004
2022-12-02.20:41:06.662205|c|SAI_OBJECT_TYPE_ROUTER_INTERFACE:oid:0x6000000000081|SAI_ROUTER_INTERFACE_ATTR_VIRTUAL_ROUTER_ID=oid:0x3000000000004|SAI_ROUTER_INTERFACE_ATTR_TYPE=SAI_ROUTER_INTERFACE_TYPE_LOOPBACK|SAI_ROUTER_INTERFACE_ATTR_MTU=9100
2022-12-02.20:41:06.667048|q|attribute_capability|SAI_OBJECT_TYPE_SWITCH:oid:0x21000000000000|OBJECT_TYPE=SAI_OBJECT_TYPE_QUEUE|ATTR_ID=SAI_QUEUE_ATTR_PFC_DLR_INIT
2022-12-02.20:41:06.667409|Q|attribute_capability|SAI_STATUS_SUCCESS|OBJECT_TYPE=SAI_OBJECT_TYPE_QUEUE|ATTR_ID=SAI_QUEUE_ATTR_PFC_DLR_INIT|CREATE_IMP=true|SET_IMP=true|GET_IMP=true
このように、日時|コール種別|オブジェクト種別:オブジェクトID|属性...
という形式で、SAIのAPIコールと応答が記録されています。コール種別はおおよそ下記です。
-
|#|
コメント -
|c|
create -
|C|
create (bulk) -
|s|
set -
|S|
set (bulk) -
|g|
get -
|G|
get responce -
|r|
remove -
|R|
remove (bulk) -
|q|
query - ~|Q|` query response
たとえば |c|SAI_OBJECT_TYPE_SWITCH:oid:0x21000000000000|
は、スイッチオブジェクトの作成を意味します。
同じディレクトリにswss.rec
というAPPL_DBの情報を記録したものもありまして、なんか動かないなというときこのあたりのファイルを見るとASICに設定が投入されてるかを確認できたりします。
saiplayer
saiplayerは、sairedis.rec
の内容を読んで、そのとおりにSAIコールを実行するプログラムです。syncd dockerの中にあります。
公式wikiの説明はこちら。
不具合発生時の緊急回避手段として使われることを想定しているようです。
$ docker exec -it syncd saiplayer --help
Usage: saiplayer [-u] [-i] [-C] [-d] [-s] [-m] [-z mode] [-r] [-p profile] [-x contextConfig] [-h] recordfile
-u --useTempView:
Enable temporary view between init and apply
-i --inspectAsic:
Inspect ASIC by ASIC DB
-C --skipNotifySyncd:
Will not send notify init/apply view to syncd
-d --enableDebug:
Enable syslog debug messages
-s --sleep:
Sleep after success reply, to notice any switch notifications
-m --syncMode:
Enable synchronous mode (depreacated, use -z)
-z --redisCommunicationMode
Redis communication mode (redis_async|redis_sync|zmq_sync), default: redis_async
-r --enableRecording:
Enable sairedis recording
-p --profile profile
Provide profile map file
-x --contextConfig
Context configuration file
-h --help:
Print out this message
「これを使えばテキストでSAIコールを並べて直接制御できるのでは?」と思ったのですが、oidどうすんのよってところがよくわかりません。ひとまず、同様の操作をしているVLANオブジェクトのoidの値をコピペして、試しにVLANを生やそうとしてみます。
2022-12-01.06:14:56.004974|c|SAI_OBJECT_TYPE_VLAN:oid:0x26000000000616|SAI_VLAN_ATTR_VLAN_ID=222
2022-12-01.06:14:56.022152|c|SAI_OBJECT_TYPE_VLAN:oid:0x26000000000617|SAI_VLAN_ATTR_VLAN_ID=444
これをsaiplayer
に食わせてみます。
admin@sonic:~$ docker cp vlan.rec syncd:.
admin@sonic:~$ docker exec -it syncd saiplayer vlan.rec
terminate called after throwing an instance of 'std::runtime_error'
what(): :- translate_local_to_redis: failed to translate local RID oid:0x21000000000000
使われてない値ならいけるかなと思ってoid:0x26000000000619
等を試してみましたが、同じエラー。なるほど、動かん。
公式の説明通り、SONiC内部では設定できているつもりなのにASICに設定が入っていないときに、設定を再度ASICに流し込むという用途に限定されたプログラムのようでした。
おわりに
つらつらと書いてみましたが、「sairedis.erc
を見るといい」みたいな話はきっとデバッグするときくらいしか役に立たない気がします。
ちょっとしたSONiC内部探検みたいな記事になってしまいましたが、少しでもお楽しみいただければ幸いです。