RYU 灭龙战 fourth day (2)

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的调用
posted @ 2017-03-03 10:48  考拉小无  阅读(438)  评论(0编辑  收藏  举报