こんな夜中にOpenFlowでネットワークをプログラミング!

第9回 【Trema編】テストファーストでアジャイルに

この記事を読むのに必要な時間:およそ 6 分

はじめに

初期ファイナル・ファンタジーの伝説的プログラマ,ナーシャ・ジベリの「早撃ち」エピソードを知っていますか? 彼はヒーローのようにさっそうと現れ,どんなプログラムでも電光石火で書いてしまいます。⁠見てくれの悪さは気にしねえ。誰よりも早くやってやるぜ」⁠やがていくつかの伝説を残し,彼もプロジェクトを去るときがやってきました。残った同僚たちは困りました。彼の超絶プログラムは彼にしか理解できず,バグがあっても修正できないのです。それに,変更しようとすると動かなくなってしまいます。⁠ナーシャ,カムバック!」でも彼はもう戻ってきません……。

こうした悲劇を防ぐ方法の1つがソフトウェアテストです。OpenFlowコントローラのように動作シーケンスが複雑なソフトウェアが壊れていないことを確認するためには,ソフトウェアにより自動化されたテストが有効です。それに,きちんとしたテスト一式があればプログラム本体の理解もしやすく,修正も簡単です。

TremaはOpenFlowコントローラをテストするためのテストツールが充実しています。今回はこれを使って,簡単なコントローラ(リピータハブ)をテストファースト形式で実装していきます。

ではさっそく実際の例を見ていきましょう。

リピータハブの設計

まずは,リピータハブがどのように動作するかを説明しましょう。ホスト3台のネットワークを考えてください。あるホストからパケットを送信すると,リピータハブは入ってきたパケットを複製してほかのすべてのホストにばらまきます。

OpenFlowプロトコル的に何が起こっているかを図1に示します。host1がパケットを送信すると,スイッチからコントローラにpacket_inが起こります。ここでコントローラは「今後は同様のパケットをほかの全ポートへばらまけ(FLOOD)⁠というflow_modを打ちます。また,packet_inを起こした最初のパケットをほかのすべてのホスト(host2 とhost3)に明示的にpacket_outで届けます。

図1 ホスト3台をつなげたリピータハブの動作

図1 ホスト3台をつなげたリピータハブの動作

最初のテスト

ではさっそく,リピータハブのテストコードを書いていきましょう。TremaのテストフレームワークはRubyのユニットテストツールRSpecと統合されています。まだインストールしていない人は,⁠geminstall rspec」でインストールしてください。また,TremaのAPIについては,こちらを参照してください。

テストコードの最初のバージョンはリスト1のとおりです(spec/repeater-hub_spec.rb)⁠最初の行は,テストに必要なTremaのライブラリを読み込みます。describeで始まるdo…endブロックはテストの本体で,RepeaterHubコントローラのふるまいをここに記述(describe)する,という意味です。

リスト1 リピータハブのテストのひな形

require File.join(File.dirname(__FILE__), "spec_helper")

describe RepeaterHub do
end

RepeaterHubを定義していないのでエラーになることはわかりきっていますが,テストを実行してみましょう。次のコマンドを実行すると,Tremaを起動したうえでspec/repeater-hub_spec.rb(リスト1)のテストを実行します。

$ rspec -fs -c ./spec/repeater-hub_spec.rb
.../spec/repeater-hub_spec.rb:3: uninitialized constant
RepeaterHub (Name Error)

予想どおり,定数RepeaterHubが未定義というエラーで失敗しました。エラーを修正するために,RepeaterHubクラスの定義を追加してみましょうリスト2)⁠

リスト2 空のRepeaterHubクラスを追加してNameErrorを修正

require File.join(File.dirname(__FILE__), "spec_helper")

class RepeaterHub < Controller ← 空のクラスを追加した
end

describe RepeaterHub do
end

本来,コントローラクラスは独立した.rbファイルに書きますが,今回は簡便さを優先し,テストコード上に直接書いているので注意してください。それでは実行してみましょう。今度はパスするはずです。

$ rspec -fs -c spec/repeater-hub_spec.rb
No examples found.
Finished in 0.00003 seconds 0 examples, 0 failures

やった! これで最初のテストにパスしました。

このようにテストファーストでは,最初にテストを書き,わざとエラーを起こしてからそれを直すためのコードをちょっとだけ追加します。テストを実行した結果からのフィードバックを得ながら「テスト書く,コード書く」を何度もくりかえしつつ最終的な完成形に近づけていくのです。

著者プロフィール

高宮安仁(たかみややすひと)

Trema開発チーム自称リーダー。東京の大学で計算科学を学び,並列プログラミング用ライブラリや,大規模クラスタ管理ツールの開発,とくに東工大のスパコンTSUBAME,分散クラスタInTriggerの管理基盤などなど主に分散・大規模システムのミドルウェア開発者・研究者として活動。現在は企業の研究者として,OpenFlow用プログラミングフレームワークTremaのメイン開発者。モットーは何がなんでも定時に帰ること。コンピューターはもちろん映画や音楽も好きで,スクラッチDJとしても活動しています。DJの仕事ください。


鈴木一哉(すずきかずや)

Trema開発チームのメンバー。Ascend,古河電工,ヤマハ等のネットワーク機器を扱うエンジニアを経て,現在は経路制御の研究に従事。ホエールズ時代からのベイスターズファンで,98年の優勝時にはもちろん横浜スタジアムへ駆けつけました。野球のない時期には,もっぱらSFを読んでいます。会社ではランニングサークルに入っており,先日も多摩川の駅伝に参加しましたが,練習不足で全然走れませんでした。ちゃんと練習して,来年こそはしっかり走るぞ!

コメント

  • ご指摘ありがとうございます。

    該当部分と関連部分を修正いたしました。
    よろしくお願いいたします。
    少し間が空いてしまいましたが、お礼申し上げます。

    Commented : #2  担当 (2012/06/12, 15:50)

  • リスト8 フローエントリのテスト

    switch("switch").should have(1).flows
    switch("switch").flows.first.actions.should == "FLOOD"

    vswitch("switch").should have(1).flows
    vswitch("switch").flows.first.actions.should == "FLOOD"
    の誤りですか?

    私の環境ではswitchだとそんなメソッド無いYOって怒られます

    Commented : #1  鉄 (2012/05/22, 17:48)

コメントの記入