引言:對(duì)于OpenFlow交換機(jī)碌廓,流表下發(fā)速率是一個(gè)非常重要的指標(biāo)分蓖。雖然OpenFlow交換機(jī)的Spec定義了barrier_request和barrier_reply機(jī)制來查詢交換機(jī)是否操作完畢念祭,但是可信度是存疑的镰官。最有說服力的方法是用匹配流表的報(bào)文來做檢測湾盗。此處提供一種使用OFTest套件來測試流表下發(fā)速率的方法供參考你辣。
環(huán)境搭建
- 一臺(tái)待測的OpenFlow交換機(jī),配置好controller的IP和port信息拦盹,保證OFTest運(yùn)行時(shí)能夠正確連接
- 安裝了OFTest測試套件的PC鹃祖,需要有兩個(gè)網(wǎng)口連接到交換機(jī)上(eth1->port1,eth2->port2)
- 一臺(tái)發(fā)包測試儀,例如Ixia掌敬,用來持續(xù)發(fā)探測包(ixia->port8)
Paste_Image.png
組包發(fā)包
- 使用發(fā)包測試儀組以太報(bào)文,并在運(yùn)行OFTest腳本前就開始持續(xù)發(fā)包
- dest mac固定為0000:ffff:ffff
- source mac從0000:0000:0001遞增池磁,step=1奔害,repeat=4000(后面添加flow條目數(shù))
- 線速發(fā)包,報(bào)文長度固定為64bytes,那么發(fā)包速率是1488095pps,意味著每秒每種報(bào)文會(huì)發(fā)1488095/4000=372.02次
- 那么一種報(bào)文循環(huán)周期的時(shí)間就是1/372.02=0.0027s,所以在后面腳本中在端口poll的等待周期需要超過0.0027秒
測試思路
- 首先在交換機(jī)上加一條priority為0的flow地熄,將從port8進(jìn)來的所有流量都轉(zhuǎn)發(fā)到port1
- 提前組好4000個(gè)add_flow的message华临,這些message會(huì)逐個(gè)匹配不同source mac,并轉(zhuǎn)發(fā)到port2
- 開始計(jì)時(shí)端考,并將上一步準(zhǔn)備好的message一次性發(fā)給交換機(jī)
- 立即開始polling交換機(jī)的port1雅潭,直到在一秒的周期內(nèi)沒有dest mac為0000:ffff:ffff的報(bào)文再轉(zhuǎn)發(fā)出來揭厚,說明后加的4000條flow全部生效了
- 立即再次計(jì)時(shí)
- 4000條flow除以兩次計(jì)時(shí)之差再減去等待時(shí)間,即為每秒真正下發(fā)的流表數(shù)
- 刪除所有流表扶供,并需要抓包驗(yàn)證真正刪除了
- 多次重復(fù)上述步驟筛圆,取得平均值,即為最后 測試結(jié)果
腳本實(shí)現(xiàn)
- 在OFTest中椿浓,一個(gè)class就是一個(gè)case太援,繼承自base_tests.SimpleDataPlane即可
class FlowAddRate(base_tests.SimpleDataPlane):
- 在class內(nèi)部定義函數(shù)timekeeping,用來計(jì)算單次的流表下發(fā)速率
def timekeeping(self,requests):
detect_pkt = simple_tcp_packet(pktlen=100,eth_dst="00:00:ff:ff:ff:ff" )
ignore_list = [(6,99)]
start_time = time.time()
for request in requests:
self.controller.message_send(request)
while True:
(rcv_port,rcv_pkt,pkt_time) = self.dataplane.poll_ignore_parts(port_number=self.out_port1,
timeout=1,exp_pkt=str(detect_pkt),ignore_list=ignore_list)
if rcv_pkt is not None: #說明還有flow沒有生效扳碍,重新poll
continue
else: #在等待的1秒時(shí)間內(nèi)提岔,沒有報(bào)文再從port1轉(zhuǎn)發(fā)出來,說明4000條flow全部生效
break
end_time = time.time()
return TEST_FLOW_NUM / (end_time - start_time-1) #這里需要減去多等待的1秒
- 定義函數(shù)rateVerify笋敞,用來提前組好4000個(gè)添加flow的報(bào)文碱蒙,存儲(chǔ)在一個(gè)list里,并調(diào)用timekeeping返回單次測試結(jié)果
def rateVerify(self):
requests = []
for flow_num in range(1,TEST_FLOW_NUM+1):
match = ofp.match([
ofp.oxm.in_port(ofp_port_ixia_mac_inc),
ofp.oxm.eth_src([0x00, 0x00, 0x00, 0x00,flow_num/256,flow_num%256])
])
actions = [ofp.action.output(self.out_port2)]
request = ofp.message.flow_add(
table_id=test_param_get("table", 0),
match=match,
instructions=[
ofp.instruction.apply_actions(actions)],
buffer_id=ofp.OFP_NO_BUFFER,
priority=1000)
requests.append(request)
return self.timekeeping(requests)
- 定義函數(shù)checkNoForward夯巷,用來清除流表后確認(rèn)指定端口沒有報(bào)文轉(zhuǎn)發(fā)出來赛惩,說明所有流表都被真正刪除了
def checkNoForward(self,chk_port):
detect_pkt = simple_tcp_packet(pktlen=100,eth_dst="00:00:ff:ff:ff:ff" )
ignore_list = [(6,99)]
while True:
(rcv_port,rcv_pkt,pkt_time) = self.dataplane.poll_ignore_parts(port_number=chk_port,
timeout=1,exp_pkt=str(detect_pkt),ignore_list=ignore_list)
if rcv_pkt is not None:
continue
else:
return
- 入口函數(shù)runTest中,可以設(shè)定測試次數(shù)test_time鞭莽,并把每次測試結(jié)果和最終平均速率寫到log中
def runTest(self):
self.out_port1, self.out_port2= openflow_ports(2)
test_time = 10
#test priority increase
record = []
for i in range(test_time):
delete_all_flows(self.controller,timeout=20)
self.checkNoForward(self.out_port2) #確保沒有報(bào)文轉(zhuǎn)發(fā)出來
self.checkNoForward(self.out_port1)
match = ofp.match([ofp.oxm.in_port(ofp_port_ixia_mac_inc), ])
actions = [ofp.action.output(self.out_port1)]
request = ofp.message.flow_add(
table_id=test_param_get("table", 0),
match=match,
instructions=[
ofp.instruction.apply_actions(actions)],
buffer_id=ofp.OFP_NO_BUFFER,
priority=0)
self.controller.message_send(request) #添加priority=0的flow坊秸,將流量都轉(zhuǎn)發(fā)到port1
(rcv_port,rcv_pkt,pkt_time) = self.dataplane.poll(port_number=self.out_port1,exp_pkt=None)
self.assertTrue(rcv_pkt is not None,"Please keep transmitting source mac increase flow before test!")
s=self.rateVerify()
record.append(s) #將每次測試結(jié)果存在列表中
logging.info("~~~~~ veirfy priority fix~~~~~~~" )
for s in record:
logging.info("%f flows per second" % s)
logging.info("!!!avarage=%f flows per second" % (sum(record)/test_time))
logging.info("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" )
- 運(yùn)行腳本然后查看結(jié)果
centec@centec-28:~/workshop/oftest-new$ sudo ./oft -p 6653 -P remote -V 1.3 --test-dir tests-1.3-advance performance_validation.FLOW_ADD_RATE --log-file=flow_add_rate.txt
......
centec@centec-28:~/workshop/oftest-new$ more flow_add_rate.txt
17:43:34.908 root : INFO : ~~~~~ veirfy priority fix~~~~~~~
17:43:34.908 root : INFO : 1030.014116 flows per second
17:43:34.908 root : INFO : 973.554838 flows per second
17:43:34.909 root : INFO : 976.370872 flows per second
17:43:34.909 root : INFO : 1074.302799 flows per second
17:43:34.909 root : INFO : 1013.381452 flows per second
17:43:34.909 root : INFO : 1023.900947 flows per second
17:43:34.909 root : INFO : 1018.519737 flows per second
17:43:34.909 root : INFO : 957.920226 flows per second
17:43:34.910 root : INFO : 1025.500571 flows per second
17:43:34.910 root : INFO : 969.328993 flows per second
17:43:34.910 root : INFO : !!!avarage=1006.279455 flows per second
17:43:34.910 root : INFO : ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
擴(kuò)展
不同priority的排列
- 上面舉例中,rateVerify中定義的所有流表的priority是固定的
- 如果需要priority遞增:priority=flow_num
- 如果需要priority遞減:priority=6000-flow_num
- 如果需要priority隨機(jī):priority=flow_num ,然后將組好的request打亂順序 random.shuffle(requests)
刪除流表速率
- 根據(jù)上面的思路澎怒,很容易設(shè)計(jì)出按照不同priority的順序(升序/降序/隨機(jī))刪除流表的測試速率的方法褒搔。
本文首發(fā)于SDNLAB http://www.sdnlab.com/17103.html