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

一、实验目的

  • 能够编写程序调用OpenDaylight REST API实现特定网络功能;
  • 能够编写程序调用Ryu REST API实现特定网络功能。

二、实验环境

  • 下载虚拟机软件Oracle VisualBox或VMware;
  • 在虚拟机中安装Ubuntu 20.04 Desktop amd64,并完整安装Mininet、OpenDaylight(Carbon版本)、Postman和Ryu;

三、实验要求

(一)基本要求

编写Python程序,调用OpenDaylight的北向接口实现以下功能

(1) 利用Mininet平台搭建下图所示网络拓扑,并连接OpenDaylight;

 

 

(2) 下发指令删除s1上的流表数据。

 

1
2
3
4
5
6
7
8
# delete.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/'
    headers = {'Content-Type''application/json'}
    res = requests.delete(url, headers=headers, auth=HTTPBasicAuth('admin''admin'))
    print (res.content)

(3) 下发硬超时流表,实现拓扑内主机h1和h3网络中断20s。

1
2
3
4
5
6
7
8
9
10
# timeout.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("./timeout.json") as file:
        str = file.read()
    headers = {'Content-Type''application/json'}
    res = requests.put(url, str, headers=headers, auth=HTTPBasicAuth('admin''admin'))
    print (res.content)

  

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# timeout.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""flow",
        "priority""65535",
        "hard-timeout""20",
        "cookie""2",
        "table_id""0"
      }
    ]
  }

(4) 获取s1上活动的流表数。

 

1
2
3
4
5
6
7
8
# get.py
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)

  

编写Python程序,调用Ryu的北向接口实现以下功能

(1) 实现上述OpenDaylight实验拓扑上相同的硬超时流表下发。

(1) 实现上述OpenDaylight实验拓扑上相同的硬超时流表下发。

  • 关闭ODL控制器,关闭上次的拓扑并清除拓扑后

  • 使用命令ryu-manager ryu.app.simple_switch_13 ryu.app.ofctl_rest打开Ryu控制器


  •  

    • 使用命令sudo mn --topo=single,3 --mac --controller=remote,ip=127.0.0.1,port=6633 --switch ovsk,protocols=OpenFlow13创建拓扑

       

    • h1 ping h3,同时运行ryu硬超时运行ryu_timeout,h1 ping h3中断20秒

    •  

    • 1
      2
      3
      4
      5
      6
      7
      8
      9
      # ryu_timeout.py
      import requests
      if __name__ == "__main__":
          url = 'http://127.0.0.1:8080/stats/flowentry/add'
          with open("./ryu_timeout.json") as file:
              str = file.read()
          headers = {'Content-Type''application/json'}
          res = requests.post(url, str, headers=headers)
          print (res.content)
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      # ryu_timeout.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配置。
      提示:拓扑生成后需连接Ryu,且Ryu应能够提供REST API服务

    • 1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      #ryu_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())}
      • 关闭控制器,关闭上一次实验的拓扑并清除拓扑

      • 使用命令ryu-manager ryu.app.simple_switch_13 ryu.app.ofctl_rest打开Ryu控制器

      • 使用命令sudo mn --custom topo.py --topo mytopo --mac --controller=remote,ip=127.0.0.1,port=6633 --switch ovsk,protocols=OpenFlow13创建拓扑

      • 1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
        34
        35
        36
        37
        38
        39
        40
        41
        42
        43
        44
        45
        46
        47
        48
        49
        50
        51
        52
        53
        54
        55
        56
        57
        58
        59
        60
        61
        62
        63
        64
        65
        66
        67
        68
        69
        70
        71
        72
        73
        74
        75
        76
        77
        78
        79
        80
        81
        82
        83
        84
        85
        86
        87
        88
        89
        90
        91
        92
        93
        94
        95
        96
        97
        98
        99
        100
        101
        102
        103
        104
        105
        106
        107
        108
        109
        110
        111
        112
        113
        114
        115
        116
        117
        118
        119
        120
        121
        122
        123
        124
        125
        126
        127
        128
        129
        130
        131
        132
        133
        134
        135
        136
        137
        138
        139
        140
        141
        142
        143
        144
        145
        146
        147
        148
        149
        150
        151
        152
        153
        154
        155
        156
        157
        158
        159
        160
        161
        162
        163
        164
        165
        166
        167
        168
        169
        170
        171
        172
        # ryu_vlan.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)

          

         

      •  

         

        (二)进阶要求

        OpenDaylight或Ryu任选其一,编程实现查看前序VLAN实验拓扑中所有节点(含交换机、主机)的名称,以及显示每台交换机的所有流表项。

      • 1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
        34
        35
        36
        37
        38
        39
        40
        41
        42
        43
        44
        45
        46
        47
        48
        49
        50
        51
        52
        53
        54
        55
        56
        57
        58
        59
        60
        61
        #getnodes.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 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()

          

        四、个人总结

      • 通过本次实验,学习了使用OpenDaylight REST API和Ryu REST API实现调用网络功能,除此之外还进一步学习了python的request库。本次实验感觉比较难,综合了前几次的实验。在实验中要记得先把流表清空。还有curl的下载,按照百度的方法利用wget来下载,下了好几次都不行,最后用命令行里的提示,利用命令sudo apt install curl才下载成功。删除流表时,需要授权使用sudo curl -X DELETE http://localhost:8080/stats/flowentry/clear/1。
posted @ 2022-11-03 10:51  lllllp  阅读(34)  评论(0)    收藏  举报