RyuBook1.0案例三:REST L
- 2020 年 1 月 19 日
- 筆記
REST Linkage
該小結主要介紹如何添加一個REST Link 函數
RYU本身提供了一個類似WSGI的web伺服器功能。藉助這個功能,我們可以創建一個REST API。 基於創建的REST API,可以快速的將RYU系統與其他系統或者是瀏覽器相連接,非常實用的一個功能。
程式解析
在案例中,實現了兩個類
- SimpleSwitchRest13
- 繼承SimpleSwitch13的功能,即具備父類的三層交換機的基本功能。
- 註冊WSGI服務
- 配置mac_to_port
- SimpleSwitchController
- REST API功能實現的主體類
- 返回指定交換機的mac_table
- 更新指定的mac_table條目
SimpleSwitchRest13類實現
_CONTEXTS = {'wsgi': WSGIApplication}
該成員變數用於指明Ryu的兼容WSGI的web服務對象。
wsgi = kwargs['wsgi'] wsgi.register(SimpleSwitchController, {simple_switch_instance_name: self})
通過上一步設置的_CONTEXTS
成員變數,可以通過kwargs
進行實例化一個WSGIApplication。同時使用register
方法註冊該服務到 controller類上。
@set_ev_cls(ofp_event.EventOFPSwitchFeatures, CONFIG_DISPATCHER) def switch_features_handler(self, ev): super(SimpleSwitchRest13, self).switch_features_handler(ev) datapath = ev.msg.datapath self.switches[datapath.id] = datapath self.mac_to_port.setdefault(datapath.id, {})
重寫父類的switch_features_handler
函數
- 存儲datapath到
switches
- 初始化MAC 地址表
def set_mac_to_port(self, dpid, entry): # 獲取MAC table mac_table = self.mac_to_port.setdefault(dpid, {}) # 獲取datapath,如果為None,證明沒有該交換機 datapath = self.switches.get(dpid) entry_port = entry['port'] entry_mac = entry['mac'] if datapath is not None: parser = datapath.ofproto_parser # 如果entry_port不在mac_table中 if entry_port not in mac_table.values(): # 下發流表 for mac, port in mac_table.items(): # from known device to new device actions = [parser.OFPActionOutput(entry_port)] match = parser.OFPMatch(in_port=port, eth_dst=entry_mac) self.add_flow(datapath, 1, match, actions) # from new device to known device actions = [parser.OFPActionOutput(port)] match = parser.OFPMatch(in_port=entry_port, eth_dst=mac) self.add_flow(datapath, 1, match, actions) # 添加entry_mac, entry_port到mac_table mac_table.update({entry_mac: entry_port}) return mac_table
該方法將MAC地址和埠註冊到指定的交換機。該方法主要被REST API的PUT方法所調用。
SimpleSwitchController類實現
@route('simpleswitch', url, methods=['GET'], requirements={'dpid': dpid_lib.DPID_PATTERN})
藉助route
裝飾器關聯方法和URL。參數如下:
- 第一個參數:任何自定義名稱
- 第二個參數:指明URL
- 第三個參數:指定http方法
- 第四個參數:指明指定位置的格式,URL(/simpleswitch/mactable/{dpid} 匹配
DPID_PATTERN
的描述
當使用GET方式訪問到該REST API介面時,調用list_mac_table函數
def list_mac_table(self, req, **kwargs): simple_switch = self.simple_switch_app # 獲取{dpid} dpid = dpid_lib.str_to_dpid(kwargs['dpid']) # 如果沒有dpid,返回404 if dpid not in simple_switch.mac_to_port: return Response(status=404) # 獲取mac_table mac_table = simple_switch.mac_to_port.get(dpid, {}) body = json.dumps(mac_table) return Response(content_type='application/json', body=body)
# 使用PUT方式設置mac_table @route('simpleswitch', url, methods=['PUT'], requirements={'dpid': dpid_lib.DPID_PATTERN}) def put_mac_table(self, req, **kwargs): simple_switch = self.simple_switch_app dpid = dpid_lib.str_to_dpid(kwargs['dpid']) try: new_entry = req.json if req.body else {} except ValueError: raise Response(status=400) if dpid not in simple_switch.mac_to_port: return Response(status=404) try: mac_table = simple_switch.set_mac_to_port(dpid, new_entry) body = json.dumps(mac_table) return Response(content_type='application/json', body=body) except Exception as e: return Response(status=500)
運行分析
啟動mininet創建網路拓撲圖
sudo mn --topo single,3 --mac --switch ovsk,protocols=OpenFlow13 --controller remote -x
啟動控制器程式,列印log資訊如下:
debugging is available (--enable-debugger option is turned on) loading app SimpleSwitchRest13 loading app ryu.controller.ofp_handler creating context wsgi instantiating app SimpleSwitchRest13 of SimpleSwitchRest13 instantiating app ryu.controller.ofp_handler of OFPHandler BRICK SimpleSwitchRest13 CONSUMES EventOFPPacketIn CONSUMES EventOFPSwitchFeatures BRICK ofp_event PROVIDES EventOFPPacketIn TO {'SimpleSwitchRest13': set(['main'])} PROVIDES EventOFPSwitchFeatures TO {'SimpleSwitchRest13': set(['config'])} CONSUMES EventOFPEchoReply CONSUMES EventOFPSwitchFeatures CONSUMES EventOFPPortDescStatsReply CONSUMES EventOFPHello CONSUMES EventOFPErrorMsg CONSUMES EventOFPEchoRequest CONSUMES EventOFPPortStatus (22238) wsgi starting up on http://0.0.0.0:8080
這裡我們可以看到,wsgi服務已經啟動,埠號為8080
查詢s1的默認MAC table
curl -X GET http://127.0.0.1:8080/simpleswitch/mactable/0000000000000001
返回為{}
在mininet中執行
mininet> h1 ping -c 1 h2
log資訊列印如下資訊:
EVENT ofp_event->SimpleSwitchRest13 EventOFPPacketIn packet in 1 00:00:00:00:00:01 ff:ff:ff:ff:ff:ff 1 EVENT ofp_event->SimpleSwitchRest13 EventOFPPacketIn packet in 1 00:00:00:00:00:02 00:00:00:00:00:01 2 EVENT ofp_event->SimpleSwitchRest13 EventOFPPacketIn packet in 1 00:00:00:00:00:01 00:00:00:00:00:02 1
返回了三條PackageIn消息(ARP過程)
- host1發起的ARP請求,並廣播
- host2發起的ARP請求,MAC地址是host1
- ICMP echo reply request 從host1 發生到host2
此刻按照設計,已經添加了兩條條目到MAC table,再次查詢進行驗證。
curl -X GET http://127.0.0.1:8080/simpleswitch/mactable/0000000000000001
返回如下,證明符合實驗預期:
{"00:00:00:00:00:02": 2, "00:00:00:00:00:01": 1}
測試POST API介面,添加h3到MAC Table
curl -X PUT -d '{"mac" : "00:00:00:00:00:03", "port" : 3}' http://127.0.0.1:8080/simpleswitch/mactable/0000000000000001
返回:
{"00:00:00:00:00:03": 3, "00:00:00:00:00:02": 2, "00:00:00:00:00:01": 1}
證明h3 mac已經被添加到MAC table。因為h3已經被添加到MAC Table中,所以理論上當h1 ping h3時將不再通過ARP查找到h3.
測試:在mininet中執行命令
mininet> h1 ping c1 h3 PING 10.0.0.3 (10.0.0.3) 56(84) bytes of data. 64 bytes from 10.0.0.3: icmp_seq=1 ttl=64 time=2.48 ms --- 10.0.0.3 ping statistics --- 1 packets transmitted, 1 received, 0% packet loss, time 0ms rtt min/avg/max/mdev = 2.480/2.480/2.480/0.000 ms
控制器log資訊如下:
EVENT ofp_event->SimpleSwitchRest13 EventOFPPacketIn packet in 1 00:00:00:00:00:01 ff:ff:ff:ff:ff:ff 1
我們可以看到,只有h1在不知道h3地址的情況下,發起ARP廣播,將PackageIn消息發送到控制器,而不再出現以後的一系列消息。這證明 MAC Table中添加的條目成功生效,符合預期情況。