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中新增的條目成功生效,符合預期情況。