实验7:基于REST API的SDN北向应用实践

实验7:基于REST API的SDN北向应用实践


基本要求

  1. 编写Python程序,调用OpenDaylight的北向接口实现以下功能
  • (1) 利用Mininet平台搭建下图所示网络拓扑,并连接OpenDaylight;
  • (2) 下发指令删除s1上的流表数据。
    ODLDelete.py
    #!/usr/bin/python
    import requests
    from requests.auth import HTTPBasicAuth
    
    if __name__ == "__main__":
        url = 'http://127.0.0.1:8181/restconf/config/opendaylight-inventory:nodes/node/openflow:1/'
        headers = {'Content-Type': 'application/json'}
        res = requests.delete(url, headers=headers, auth=HTTPBasicAuth('admin', 'admin'))
        print (res.content)
    
  • (3) 下发硬超时流表,实现拓扑内主机h1和h3网络中断20s。
    ODLTimeOut.py
    import requests
    from requests.auth import HTTPBasicAuth
    
    if __name__ == "__main__":
        url = 'http://127.0.0.1:8181/restconf/config/opendaylight-inventory:nodes/node/openflow:1/flow-node-inventory:table/0/flow/1'
        with open("./ODLTimeOut.json") as f:
            jstr = f.read()
        headers = {'Content-Type': 'application/json'}
        res = requests.put(url, jstr, headers=headers, auth=HTTPBasicAuth('admin', 'admin'))
        print (res.content)
    
    ODLTimeOut.json
        {
      "flow": [
        {
          "id": "1",
          "match": {
            "in-port": "1",
            "ethernet-match": {
              "ethernet-type": {
                "type": "0x0800"
              }
            },
            "ipv4-destination": "10.0.0.3/32"
          },
          "instructions": {
            "instruction": [
              {
                "order": "0",
                "apply-actions": {
                  "action": [
                    {
                      "order": "0",
                      "drop-action": {}
                    }
                  ]
                }
              }
            ]
          },
          "flow-name": "flow1",
          "priority": "65535",
          "hard-timeout": "20",
          "cookie": "2",
          "table_id": "0"
        }
      ]
    }
    
  • (4) 获取s1上活动的流表数。
    ODLGetFlow.py
    #!/usr/bin/python
    import requests
    from requests.auth import HTTPBasicAuth
    
    if __name__ == "__main__":
        url = 'http://127.0.0.1:8181/restconf/operational/opendaylight-inventory:nodes/node/openflow:1/flow-node-inventory:table/0/opendaylight-flow-table-statistics:flow-table-statistics'
        headers = {'Content-Type': 'application/json'}
        res = requests.get(url,headers=headers, auth=HTTPBasicAuth('admin', 'admin'))
        print (res.content)
    
  1. 编写Python程序,调用Ryu的北向接口实现以下功能
  • (1) 实现上述OpenDaylight实验拓扑上相同的硬超时流表下发。
    RyuTimeOut.py
    #!/usr/bin/python
    import requests
    
    if __name__ == "__main__":
        url = 'http://127.0.0.1:8080/stats/flowentry/add'
        with open("./RyuTimeOut.json") as f:
            jstr = f.read()
        headers = {'Content-Type': 'application/json'}
        res = requests.post(url, jstr, headers=headers)
        print (res.content)
    
    RyuTimeOut.json
    {
        "dpid": 1,
        "cookie": 1,
        "cookie_mask": 1,
        "table_id": 0,
        "hard_timeout": 20,
        "priority": 65535,
        "flags": 1,
        "match":{
            "in_port":1,
        },
        "actions":[
    
        ]
     }
    
  • (2) 参考Ryu REST API的文档,基于VLAN实验的网络拓扑,编程实现相同的VLAN配置。
    Topo.py
    #Topo.py:建立要求的拓扑
    from mininet.topo import Topo
    
    class MyTopo(Topo):
        def __init__(self):
            # initilaize topology
            Topo.__init__(self)
    
            self.addSwitch("s1")
            self.addSwitch("s2")
    
            self.addHost("h1")
            self.addHost("h2")
            self.addHost("h3")
            self.addHost("h4")
    
            self.addLink("s1", "h1")
            self.addLink("s1", "h2")
            self.addLink("s2", "h3")
            self.addLink("s2", "h4")
            self.addLink("s1", "s2")
    
    topos = {'mytopo': (lambda: MyTopo())}
    
    RyuVlan.py
    import json
    
    import requests
    
    if __name__ == "__main__":
        url = 'http://127.0.0.1:8080/stats/flowentry/add'
        headers = {'Content-Type': 'application/json'}
        flow1 = {
            "dpid": 1,
            "priority": 1,
            "match":{
                "in_port": 1
            },
            "actions":[
                {
                    "type": "PUSH_VLAN",    
                    "ethertype": 33024      
                },
                {
                    "type": "SET_FIELD",
                    "field": "vlan_vid",    
                    "value": 4096           
                },
                {
                    "type": "OUTPUT",
                    "port": 3
                }
            ]
        }
        flow2 = {
            "dpid": 1,
            "priority": 1,
            "match":{
                "in_port": 2
            },
            "actions":[
                {
                    "type": "PUSH_VLAN",     
                    "ethertype": 33024      
                },
                {
                    "type": "SET_FIELD",
                    "field": "vlan_vid",     
                    "value": 4097           
                },
                {
                    "type": "OUTPUT",
                    "port": 3
                }
            ]
        }
        flow3 = {
            "dpid": 1,
            "priority": 1,
            "match":{
                "vlan_vid": 0
            },
            "actions":[
                {
                    "type": "POP_VLAN",    
                    "ethertype": 33024     
                },
                {
                    "type": "OUTPUT",
                    "port": 1
                }
            ]
        }
        flow4 = {
            "dpid": 1,
            "priority": 1,
            "match": {
                "vlan_vid": 1
            },
            "actions": [
                {
                    "type": "POP_VLAN", 
                    "ethertype": 33024  
                },
                {
                    "type": "OUTPUT",
                    "port": 2
                }
            ]
        }
        flow5 = {
            "dpid": 2,
            "priority": 1,
            "match": {
                "in_port": 1
            },
            "actions": [
                {
                    "type": "PUSH_VLAN", 
                    "ethertype": 33024 
                },
                {
                    "type": "SET_FIELD",
                    "field": "vlan_vid", 
                    "value": 4096  
                },
                {
                    "type": "OUTPUT",
                    "port": 3
                }
            ]
        }
        flow6 = {
            "dpid": 2,
            "priority": 1,
            "match": {
                "in_port": 2
            },
            "actions": [
                {
                    "type": "PUSH_VLAN",  
                    "ethertype": 33024  
                },
                {
                    "type": "SET_FIELD",
                    "field": "vlan_vid",  
                    "value": 4097 
                },
                {
                    "type": "OUTPUT",
                    "port": 3
                }
            ]
        }
        flow7 = {
            "dpid": 2,
            "priority": 1,
            "match": {
                "vlan_vid": 0
            },
            "actions": [
                {
                    "type": "POP_VLAN", 
                    "ethertype": 33024  
                },
                {
                    "type": "OUTPUT",
                    "port": 1
                }
            ]
        }
        flow8 = {
            "dpid": 2,
            "priority": 1,
            "match": {
                "vlan_vid": 1
            },
            "actions": [
                {
                    "type": "POP_VLAN", 
                    "ethertype": 33024  
                },
                {
                    "type": "OUTPUT",
                    "port": 2
                }
            ]
        }
        res1 = requests.post(url, json.dumps(flow1), headers=headers)
        res2 = requests.post(url, json.dumps(flow2), headers=headers)
        res3 = requests.post(url, json.dumps(flow3), headers=headers)
        res4 = requests.post(url, json.dumps(flow4), headers=headers)
        res5 = requests.post(url, json.dumps(flow5), headers=headers)
        res6 = requests.post(url, json.dumps(flow6), headers=headers)
        res7 = requests.post(url, json.dumps(flow7), headers=headers)
        res8 = requests.post(url, json.dumps(flow8), headers=headers)
    

进阶要求

  • GetNode.py
    import requests
    import time
    import re
    
    
    class GetNodes:
        def __init__(self, ip):
            self.ip = ip
            
        def get_switch_id(self):
            url = 'http://' + self.ip + '/stats/switches'
            re_switch_id = requests.get(url=url).json()
            switch_id_hex = []
            for i in re_switch_id:
                switch_id_hex.append(hex(i))
    
            return switch_id_hex
    
        def getflow(self):
            url = 'http://' + self.ip + '/stats/flow/%d'
            switch_list = self.get_switch_id()
            ret_flow = []
            for switch in switch_list:
                new_url = format(url % int(switch, 16))
                re_switch_flow = requests.get(url=new_url).json()
                ret_flow.append(re_switch_flow)
            return ret_flow
    
        def show(self):
            flow_list = self.getflow()
            for flow in flow_list:
                for dpid in flow.keys():
                    dp_id = dpid
                    switchnum= '{1}'.format(hex(int(dp_id)), int(dp_id))        
                    print('s'+switchnum,end = " ")
                    switchnum = int(switchnum)
                for list_table in flow.values():
                    for table in list_table:          
                        string1 = str(table)
                        if re.search("'dl_vlan': '(.*?)'", string1) is not None:
                           num = re.search("'dl_vlan': '(.*?)'", string1).group(1);
                           if num == '0' and switchnum == 1:
                              print('h1',end = " ")
                           if num == '1' and switchnum == 1:
                              print('h2',end = " ")
                           if num == '0' and switchnum == 2:
                              print('h3',end = " ")
                           if num == '1' and switchnum == 2:
                              print('h4',end = " ")
            print("")
            flow_list = self.getflow()
            for flow in flow_list:
                for dpid in flow.keys():
                    dp_id = dpid
                    print('switch_name:s{1}'.format(hex(int(dp_id)), int(dp_id)))
                for list_table in flow.values():
                    for table in list_table:
                        print(table)
    s1 = GetNodes("127.0.0.1:8080")
    s1.show()
    

个人总结

  • 实验难度:
    本次的实验难度难度还蛮大的,但总的来说就是需要参考REST API文档然后用python实现特定的网络功能,修改参数、自我缝合,综合应用。
  • 实验过程遇到的困难及解决办法:
    • 困难:在搭建好拓扑的时候,发现pingall全都ping不通,curl删除流表以及sudo mn -c后也无法ping通。
    • 解决办法:在分析后得知,应该先开启odl或ryu后,再建立拓扑。
    • 困难:运行RyuVlan.py时候发现无效。
    • 解决办法:没有打开Ryu,需要输入ryu-manager ryu.app.simple_switch_13 ryu.app.ofctl_rest打开Ryu,以及建立拓扑的格式sudo mn --custom Topo.py --topo mytopo -- controller=remote,ip=127.0.0.1,port=6653 --switch ovsk,protocols=OpenFlow13
  • 个人感想:
    通过本次的实验,我明白了SDN应用层与控制层之间的北向接口REST API。重新回顾PDF文档熟悉了相关控制器的使用,对控制器的启动,下发流表等操作更熟悉了,并且在实践中通过阅读相关文档,查阅资料,了解到了北向接口API其实并没有一个统一的标准,不同的控制器对应着的API也有所区别,体会到了SDN框架的灵活性和可编程性。
posted @ 2022-10-26 14:08  Champion_Wang  阅读(51)  评论(0)    收藏  举报