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

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