一、实验目的
- 能够编写程序调用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
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()
四、个人总结
- 通过本次实验,学习了使用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。
-
-