OpenFlowのベンチマークプログラムをJavaで書く

昨日に引き続き、OpenFlowネタ。昨日のエントリの最後の方で、OpenFlowコントローラのベンチマークの話に少しだけ触れた。OpenFlowコントローラを作っている過程でOpenFlowコントローラ用のベンチマークプログラムを自作したのもあり、OpenFlowのベンチマークについて書く。

OpenFlowスイッチやコントローラのベンチマークプログラムで一番に思い浮かぶのは、OpenFlowの総本山スタンフォード大学のProf. Nick McKeownのグループが開発しているoflopsである。というか、oflops以外に少なくとも私は聞いたことがない。

oflopsはgitレポジトリから

# git clone git://gitosis.stanford.edu/oflops.git

で、入手可能。正しく説明すると、oflopsはOpenFlowスイッチ用のベンチマークで、同じパッケージに含まれているcbenchがOpenFlowコントローラ用のベンチマークになる。そのため、コントローラのベンチマークが目的な今回はcbenchに限った話である。私はOpenFlowスイッチを作っているわけではないので、oflopsをどのように使うのか調べたことがないのでよく分からないが、恐らくOpen vSwitchのOpenFlow対応部分を作っている人などは使っているのだろうと思われる。

私の不勉強のせいなのか、そもそもLinuxに依存しているのか、はたまた依存ライブラリを入れていないだけなのかよく分からないが、私が使っているMac OS X Snow Leopardではoflopsパッケージ全体のmakeに失敗して、それを解決するのもMacPortsやら何やらが絡むと面倒そうだったので、思い切って自作してみることにした。コントローラと同じ様にJavaで作る。Javaで作れば、ポータビリティが高くなるし、Netty使えばとりあえずパフォーマンスは問題無さそうな気がしたので。

OpenFlowコントローラのベンチマークって?

OpenFlowコントローラのベンチマークって一体何をして負荷をかけるのか?という疑問がまず起きる。素直にcbenchを参考にさせて頂くという方針をとった。cbenchのREADMEによると、

cbench is a benchmarking tool for controllers

Algorithm:
    pretend to be n switches (n=16 is default)
    create n openflow sessions to the controller
    if latency mode (default):
        for each session:
            1) send up a packet in
            2) wait for a matching flow mod to come back
            3) repeat
            4) count how many times #1-3 happen per sec
    else in throughtput mode (i.e., with '-t'):
        for each session:
            while buffer not full:
                queue packet_in's
                count flow_mod's as they come back


NOTE: packet_in messages are the only (?) switch-solicited openflow
packet that should get a response from a controller.  Would be
nice to evaluate more, but this is at least something.

とある。つまり、n個(デフォルトではn=16)のスイッチのふりをして、コントローラに接続する。レイテンシモードでは、各セッション毎に

  1. PACKET_INをコントローラに送り、
  2. コントローラからFLOW_MODが返ってくるのを待ち、
  3. それを繰り返す。

スループットモードでは、各セッション毎に、

  1. バッファがフルになるまでPACKET_INをキューイングし、
  2. FLOW_MODの返信と関係なくPACKET_INを送信し続け
  3. FLOW_MODが返信されたら、カウントする。

という動作をしているとのこと。

さらに、ソースコードの内容の中身を見てみて、動作の詳細を確認。fakeswitch.cの中のvoid fakeswitch_handle_read(struct fakeswitch *fs)を理解すればほぼ内容がつかめる。その動作をおおざっぱに要約すると、

  • セッション確立時にはHELLOを送る
  • FEATURES_REQUESTを受信したら、FEATURES_REPLYを返信
  • ECHO_REQUESTを受信したら、ECHO_REPLYを返信
  • VENDORを受信したら、VENDORを返信
  • GET_CONFIG_REQUESTを受信したら、GET_CONFIG_REPLYを返信
  • PACKET_OUTもしくはFLOW_MODを受信したら、カウンタをカウントアップして、PACKET_INをスイッチに送信

という感じの動作をしている。

Javaでのベンチマークプログラム

Nettyを使って、昨日のコントローラの用にChannelPipelineでframer, decoder, encoder, handlerを構成し、handlerにベンチマーク用の動作を記述すれば良いことになる。framer, decoder, encoderは使い回せる。

massageReceivedの中身は以下の通り。

素直に、必要な動作を記述している。このとき、返信するのに必要なデータの作成と受信したメッセージのカウントはfakeSwitchに委譲している。cbenchでは、FEATURES_REPLY, PACKET_INはwiresharkからキャプチャしたバイト配列がソースコードに直接記述されており、そのデータを送信するようになっているが、自作のベンチマークではFEATURES_REPLYのデータはcbenchのものを拝借したが、PACKET_INのデータは0で埋めたバイト列を送信するように手抜きしている。そのため、コントローラによってはPACKET_INに含まれるデータが有効なパケットデータと見なされずにうまく動作しない可能性もある。ただし、今のところリピータハブやL2スイッチ程度のアプリケーションを動かしたコントローラの動作しか試していないので、その範囲ではこのデータでも問題がなかった。

また、今回は、cbenchでいうレイテンシモードだけを実装になっている。というのは、スループットモードでは、送信バッファ一杯になるまでPACKET_INをキューイングしなければいけないのだが、Nettyを使ってそれをどうやって実現するか分からなかったためである。その実現方法を教えて頂ける方がいらしたら幸いである。

ソースコード

https://github.com/oshothebig/galibier
ソースコードを公開している。作り途中のOpenFlowコントローラの一部として今回のベンチマークプログラムが含まれている形を取っている(org.galibier.benchmarkパッケージがベンチマーク関係)前回同様、openflowjが別途必要である。

Mavenをいまいちちゃんと理解していないので、実行方法があまり美しくないが、以下のコマンドで、ビルドとlocalhostの6633番ポートで待ち受けるコントローラに対してベンチマークが実行が出来る。Mavenのセオリーを勉強しないといけないのだけれども、全容が把握できていないのと、ユースケースとそれを解決するセオリー的なやり方が分かっていない・・・

# git clone git://github.com/oshothebig/galibier.git
# cd galibier
# mvn package
# java -cp target/galibier-controller-0.1.0-devel-jar-with-dependencies.jar org.galibier.benchmark.Main localhost

設定可能なオプションは以下の通り。

 -d (--duration) N : Duration of a loop in milli sec
 -h (--help)       : Print this help
 -l (--loops) N    : Number of loops
 -m (--message) N  : Bytes of the payload of a packet in
 -p (--port) N     : Port number of the controller
 -s (--switch) N   : Number of switches

動作の不具合やソースのおかしな場所を見つけた場合には、ご連絡頂けると幸いです。