RyuBook1.0案例三:REST L

发布时间:2019-03-03 10:30:29编辑:auto阅读(2030)

    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中添加的条目成功生效,符合预期情况。

关键字