読者です 読者をやめる 読者になる 読者になる

SDN開発エンジニアを目指した活動ブログ

〜SDNなオープンソース製品を実際に使って試してみる〜

Ryu SDN Frameworkを活用した簡易ルータ機能の作成(3) 〜Ryuアプリのユニットテスト編〜

前回、双方のPC端末間での初歩的なIPルーティングをRyuアプリとして実装してみました。
通常のプログラミングでは、ユニットテストを想定しながら実装を行っていくと思います。OpenFlowプログラミングでもユニットテストを意識しながら実装を進めていくべきです。
ところで、実際のOpenFlowスイッチ動作によるPacket-inなどイベントドリブンな挙動をユニットテストで確認するには、どうしたらよいのでしょう?
この疑問を解決するために、Ryu SDN Frameworkの内部を調査した上で、OpenFlowプログラミングのユニットテストを実施してみました。

◆Ryu SDN Frameworkの内部調査

Ryu SDN Frameworkを確認してみて、わかったことは、どうやら、RyuコントローラとOpenFlowスイッチ間でOpenFlowチャネルが開設された時点で、Datapathオブジェクトが生成され、Ryuアプリは、このDatapathオブジェクトとのやりとりを通じてOpenFlowプロトコルを制御するみたいです。
特に、OpenFlowスイッチからPacket-in等を受信した際には、DatapathオブジェクトからEventMessageを受信することにより、必要な情報授受が行われることが理解できました。
ということで、ユニットテストの実装コードにて、(通常は、Ryuアプリ動作で自動生成される)Datapathオブジェクトを、制御してあげればよいみたいです。

f:id:ttsubo:20140117222415j:plain

◆Ryuアプリ「簡易ルータ(コード名;simpleForward)」のテストシナリオ

前回のブログ記事「Ryu SDN Frameworkを活用した簡易ルータ機能の作成(2) 〜IPルーティングの初歩編〜」で動作させたRyuアプリ「簡易ルータ(コード名;simpleForward)」をユニットテストの対象としました。

TestCase1:

HOST1〜HOST2間での通信シーケンスその1(HOST1:ARP学習前)において、HOST1からARP Requestを受信した場合
f:id:ttsubo:20140117222433j:plain

TestCase2:

HOST1〜HOST2間での通信シーケンスその1(HOST1:ARP学習前)において、HOST1からICMP Echoを受信した場合
f:id:ttsubo:20140117222451j:plain

TestCase3:

HOST1〜HOST2間での通信シーケンスその1(HOST1:ARP学習前)において、HOST2からARP Replyを受信した場合
f:id:ttsubo:20140117222505j:plain

TestCase4:

HOST1〜HOST2間での通信シーケンスその2(HOST1:ARP学習済)において、HOST2から想定外のIPパケットを受信した場合

前回、HOST1〜HOST2間での通信シーケンスその2の動作を確認したところ、双方のPC端末(HOST1,HOST2)は"91.189.89.22"宛にTCP SYNパケットを送信しており、OpenFlowコントローラは、OpenFlowスイッチによるPacket-inにより同TCP SYNパケットを受信しておりました。このようなRyuアプリ作成の当初には想定していないOpenFlowコントローラ動作が判明したので、本件も、ユニットテスト化しました。
f:id:ttsubo:20140117231133j:plain

ユニットテスト実装コード

Ryuアプリ「簡易ルータ(コード名;simpleForward)」のユニットテスト実装コードは、こちらになります。
simpleRouter/test_SimpleForward.py at master · ttsubo/simpleRouter · GitHub

◆実際に動かしてみた

想定とおり、動作できました。ちなみに、ユニットテスト実行中のDEBUG文は、Ryuアプリ「simpleForward」で記述したものです。

$ python test_SimpleForward.py 
.*** Case1: HOST1からARP Request受信 ***
DEBUG:SimpleForward:receive ARP Request 52:54:00:75:4e:57 => ff:ff:ff:ff:ff:ff (port1)
DEBUG:SimpleForward:---------------------------------------
DEBUG:SimpleForward:eth_dst_address :ff:ff:ff:ff:ff:ff
DEBUG:SimpleForward:eth_src_address :52:54:00:75:4e:57
DEBUG:SimpleForward:eth_ethertype :0x0806
DEBUG:SimpleForward:---------------------------------------
DEBUG:SimpleForward:arp_hwtype :1
DEBUG:SimpleForward:arp_proto :0x0800
DEBUG:SimpleForward:arp_hlen :6
DEBUG:SimpleForward:arp_plen :4
DEBUG:SimpleForward:arp_opcode :1
DEBUG:SimpleForward:arp_src_mac :52:54:00:75:4e:57
DEBUG:SimpleForward:arp_src_ip :192.168.0.1
DEBUG:SimpleForward:arp_dst_mac :ff:ff:ff:ff:ff:ff
DEBUG:SimpleForward:arp_dst_ip :192.168.0.10
DEBUG:SimpleForward:---------------------------------------
DEBUG:SimpleForward:send ARP reply 00:00:00:00:00:01 => 52:54:00:75:4e:57 (port1)

.*** Case2: HOST2のMAC未学習の時、HOST1からICMP Echoを受信 ***
DEBUG:SimpleForward:receive IP packet 52:54:00:75:4e:57 => 00:00:00:00:00:01 (port1)
DEBUG:SimpleForward:---------------------------------------
DEBUG:SimpleForward:eth_dst_address :00:00:00:00:00:01
DEBUG:SimpleForward:eth_src_address :52:54:00:75:4e:57
DEBUG:SimpleForward:eth_ethertype :0x0800
DEBUG:SimpleForward:---------------------------------------
DEBUG:SimpleForward:ip_version :4
DEBUG:SimpleForward:ip_header_length :5
DEBUG:SimpleForward:ip_tos :0
DEBUG:SimpleForward:ip_total_length :38
DEBUG:SimpleForward:ip_identification:0
DEBUG:SimpleForward:ip_flags :2
DEBUG:SimpleForward:ip_offset :0
DEBUG:SimpleForward:ip_ttl :64
DEBUG:SimpleForward:ip_proto :1
DEBUG:SimpleForward:ip_csum :47236
DEBUG:SimpleForward:ip_src :192.168.0.1
DEBUG:SimpleForward:ip_dst :192.168.1.1
DEBUG:SimpleForward:---------------------------------------
DEBUG:SimpleForward:Drop packet
DEBUG:SimpleForward:send ARP request 00:00:00:00:00:02 => ff:ff:ff:ff:ff:ff (port2)

.*** Case3: HOST1のMAC学習済の時、HOST2からARP Replyを受信 ***
DEBUG:SimpleForward:receive ARP Reply 52:54:00:0b:d0:48 => 00:00:00:00:00:02 (port2)
DEBUG:SimpleForward:---------------------------------------
DEBUG:SimpleForward:eth_dst_address :00:00:00:00:00:02
DEBUG:SimpleForward:eth_src_address :52:54:00:0b:d0:48
DEBUG:SimpleForward:eth_ethertype :0x0806
DEBUG:SimpleForward:---------------------------------------
DEBUG:SimpleForward:arp_hwtype :1
DEBUG:SimpleForward:arp_proto :0x0800
DEBUG:SimpleForward:arp_hlen :6
DEBUG:SimpleForward:arp_plen :4
DEBUG:SimpleForward:arp_opcode :2
DEBUG:SimpleForward:arp_src_mac :52:54:00:0b:d0:48
DEBUG:SimpleForward:arp_src_ip :192.168.1.1
DEBUG:SimpleForward:arp_dst_mac :00:00:00:00:00:02
DEBUG:SimpleForward:arp_dst_ip :192.168.1.10
DEBUG:SimpleForward:---------------------------------------
DEBUG:SimpleForward:Send Flow_mod packet for 192.168.1.1
DEBUG:SimpleForward:Send Flow_mod packet for 192.168.0.1

.*** Case4: HOST2からHOST1以外の宛先IPへのIPパケットを受信 ***
DEBUG:SimpleForward:receive IP packet 52:54:00:0b:d0:48 => 00:00:00:00:00:02 (port2)
DEBUG:SimpleForward:---------------------------------------
DEBUG:SimpleForward:eth_dst_address :00:00:00:00:00:02
DEBUG:SimpleForward:eth_src_address :52:54:00:0b:d0:48
DEBUG:SimpleForward:eth_ethertype :0x0800
DEBUG:SimpleForward:---------------------------------------
DEBUG:SimpleForward:ip_version :4
DEBUG:SimpleForward:ip_header_length :5
DEBUG:SimpleForward:ip_tos :0
DEBUG:SimpleForward:ip_total_length :20
DEBUG:SimpleForward:ip_identification:0
DEBUG:SimpleForward:ip_flags :2
DEBUG:SimpleForward:ip_offset :0
DEBUG:SimpleForward:ip_ttl :64
DEBUG:SimpleForward:ip_proto :1
DEBUG:SimpleForward:ip_csum :50284
DEBUG:SimpleForward:ip_src :192.168.1.1
DEBUG:SimpleForward:ip_dst :91.189.89.22
DEBUG:SimpleForward:---------------------------------------
DEBUG:SimpleForward:Drop packet
DEBUG:SimpleForward:unknown ip received !

.
----------------------------------------------------------------------
Ran 5 tests in 0.013s

OK

Github/Jenkins連携

継続的インテグレーションの予備知識を学ぶため、Github/Jenkinsと連携したユニットテストの自動化環境を構築してみました。コード・カバレッジも自動で計測できて感激しました。
http://ttsubo.github.io/simpleRouter/tree/linuxBox/tests/coverage/code_coverage.html

◆終わりに

今回のユニットテスト手法を活用すれば、わざわざ、OpenFlowスイッチを起動せずとも、Ryuアプリの途中動作を確認することが可能になりますね。次回は、簡易ルータをNorthbound APIに対応させたいと思います。