RYU 滅龍戰 fourth day (2)
前言
之前試過在ODL調用他們的rest api,一直想自己寫一個基于ODL的rest api,結果還是無果而終。這個小目标卻在RYU身上實作了。今日說法,為你帶來,基于RYU的北向rest api開發
目的
-
mac位址表擷取 API
取得基于RYU 滅龍戰 third day實驗的mac位址表内容。即 對應的mac位址和連接配接端口 以JSON的形式回傳
-
mac位址表注冊 API
向mac位址表加入新的mac位址和端口号,同時加到交換機的流表中
實驗方案
附錄源碼
# Copyright (C) 2016 Nippon Telegraph and Telephone Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import json
from ryu.app import simple_switch_13
from ryu.controller import ofp_event
from ryu.controller.handler import CONFIG_DISPATCHER
from ryu.controller.handler import set_ev_cls
from ryu.app.wsgi import ControllerBase
from ryu.app.wsgi import Response
from ryu.app.wsgi import route
from ryu.app.wsgi import WSGIApplication
from ryu.lib import dpid as dpid_lib
simple_switch_instance_name = 'simple_switch_api_app'
url = '/simpleswitch/mactable/{dpid}'
#SimpleSwitchRest13用來擴充實驗一的功能,讓它可以更新mac位址表,其中switch_features_handler方法由于需要更新mac位址表,是以這個地方繼承原方法,進行重寫
class SimpleSwitchRest13(simple_switch_13.SimpleSwitch13):
#指定RYU使用的為WSGI網頁伺服器
_CONTEXTS = {'wsgi': WSGIApplication}
def __init__(self, *args, **kwargs):
super(SimpleSwitchRest13, self).__init__(*args, **kwargs)
#已連接配接交換機集合
self.switches = {}
wsgi = kwargs['wsgi']
wsgi.register(SimpleSwitchController,
{simple_switch_instance_name: self})
#接收消息為OPFSwitchFeatures,交換機狀态為接收SwitchFeatures消息
@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, {})
def set_mac_to_port(self, dpid, entry):
mac_table = self.mac_to_port.setdefault(dpid, {})
datapath = self.switches.get(dpid)
#entry用來存儲已經注冊的mac位址和端口
entry_port = entry['port']
entry_mac = entry['mac']
if datapath is not None:
parser = datapath.ofproto_parser
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)
mac_table.update({entry_mac: entry_port})
return mac_table
#SimpleSwitchController用來實作收到HTTP請求時所需要回應的方法
class SimpleSwitchController(ControllerBase):
def __init__(self, req, link, data, **config):
super(SimpleSwitchController, self).__init__(req, link, data, **config)
self.simple_switch_app = data[simple_switch_instance_name]
#參數說明,第一個參數任意名稱,第二個參數url,指定url,使得對應的url為http://<IP>:8080/simpleswitch/mactable/<datapath ID>,第三個參數為GET方法,
# 第四個參數為指定的URL形式,即simpleswitch/mactable/<datapath ID>的<datapapath ID>要和目标檔案的值相對應
@route('simpleswitch', url, methods=['GET'],
requirements={'dpid': dpid_lib.DPID_PATTERN})
def list_mac_table(self, req, **kwargs):
simple_switch = self.simple_switch_app
dpid = dpid_lib.str_to_dpid(kwargs['dpid'])
#如果dpid不在表中的話,就會傳回404
if dpid not in simple_switch.mac_to_port:
return Response(status=404)
#把對應的dpid對應的mac位址表用json的形式傳回
mac_table = simple_switch.mac_to_port.get(dpid, {})
body = json.dumps(mac_table)
return Response(content_type='application/json', body=body)
#同上
@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)
#調用set_mac_to_port方法,注冊相應的mac,port,并下發流表
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
- 另外一個終端,目錄為ryu/app
ryu-manager --verbose ./simple_switch_rest_13.py
- mininet端,讓h1 ping h2 ,看RYU端的變化
h1 ping -c1 h2
- 使用curl調用rest api進行擷取mac位址表
- 使用curl調用rest api進行mac位址表的注冊
- 在OVS上檢視流表
可以看得到剛剛PUT的請求,也轉化為流表的形式下發了
總結
學習到這裡,基本了解一些RYU的一些大概架構和結構,知道如何相應事件去做相應的處理。接下來我将
- 學一下RYU對LLDP封包的機制
- 對該機制進行這3天來的實驗彙總,功能包括對LLDP封包做相應處理,并在這基礎上增加rest api的調用