RyuBook1.0案例三:REST Linkage

NinWoo發表於2018-08-24

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過程)

  1. host1發起的ARP請求,並廣播
  2. host2發起的ARP請求,MAC地址是host1
  3. 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中新增的條目成功生效,符合預期情況。

相關文章