引言:对于OpenFlow交换机,流表下发速率是一个非常重要的指标。虽然OpenFlow交换机的Spec定义了barrier_request和barrier_reply机制来查询交换机是否操作完毕,但是可信度是存疑的。最有说服力的方法是用匹配流表的报文来做检测。此处提供一种使用OFTest套件来测试流表下发速率的方法供参考。
环境搭建
- 一台待测的OpenFlow交换机,配置好controller的IP和port信息,保证OFTest运行时能够正确连接
- 安装了OFTest测试套件的PC,需要有两个网口连接到交换机上(eth1->port1,eth2->port2)
- 一台发包测试仪,例如Ixia,用来持续发探测包(ixia->port8)
组包发包
- 使用发包测试仪组以太报文,并在运行OFTest脚本前就开始持续发包
- dest mac固定为0000:ffff:ffff
- source mac从0000:0000:0001递增,step=1,repeat=4000(后面添加flow条目数)
- 线速发包,报文长度固定为64bytes,那么发包速率是1488095pps,意味着每秒每种报文会发1488095/4000=372.02次
- 那么一种报文循环周期的时间就是1/372.02=0.0027s,所以在后面脚本中在端口poll的等待周期需要超过0.0027秒
测试思路
- 首先在交换机上加一条priority为0的flow,将从port8进来的所有流量都转发到port1
- 提前组好4000个add_flow的message,这些message会逐个匹配不同source mac,并转发到port2
- 开始计时,并将上一步准备好的message一次性发给交换机
- 立即开始polling交换机的port1,直到在一秒的周期内没有dest mac为0000:ffff:ffff的报文再转发出来,说明后加的4000条flow全部生效了
- 立即再次计时
- 4000条flow除以两次计时之差再减去等待时间,即为每秒真正下发的流表数
- 删除所有流表,并需要抓包验证真正删除了
- 多次重复上述步骤,取得平均值,即为最后 测试结果
脚本实现
- 在OFTest中,一个class就是一个case,继承自base_tests.SimpleDataPlane即可
class FlowAddRate(base_tests.SimpleDataPlane):
- 在class内部定义函数timekeeping,用来计算单次的流表下发速率
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秒时间内,没有报文再从port1转发出来,说明4000条flow全部生效
break
end_time = time.time()
return TEST_FLOW_NUM / (end_time - start_time-1) #这里需要减去多等待的1秒
- 定义函数rateVerify,用来提前组好4000个添加flow的报文,存储在一个list里,并调用timekeeping返回单次测试结果
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)
- 定义函数checkNoForward,用来清除流表后确认指定端口没有报文转发出来,说明所有流表都被真正删除了
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
- 入口函数runTest中,可以设定测试次数test_time,并把每次测试结果和最终平均速率写到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) #确保没有报文转发出来
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,将流量都转发到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) #将每次测试结果存在列表中
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("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" )
- 运行脚本然后查看结果
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 : ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
扩展
不同priority的排列
- 上面举例中,rateVerify中定义的所有流表的priority是固定的
- 如果需要priority递增:priority=flow_num
- 如果需要priority递减:priority=6000-flow_num
- 如果需要priority随机:priority=flow_num ,然后将组好的request打乱顺序 random.shuffle(requests)
删除流表速率
- 根据上面的思路,很容易设计出按照不同priority的顺序(升序/降序/随机)删除流表的测试速率的方法。
本文首发于SDNLAB http://www.sdnlab.com/17103.html