前言
本月上旬到海南出差,现在回来终于可以继续乱入SDN了。本次学习的是如何使用RYU提供的REST API接口对交换机的流表进行控制。对流表的控制无外呼就是增删改查了。本节先使用POSTMAN进行实验,体验一下效果。下一节将使用写一个可视化工具封装一下REST API,方便后续的调试与开发。
实验拓扑
控制器:RYU
交换机:s1,s2
主机:h1,h2,h3,h3
联通性(直连):
h1<->s1;h2<->s1
h3<->s2;h4<->s2
s1<->s2
拓扑代码如下:
__author__ = 'jmh081701'
from mininet.topo import Topo
class MyTopo(Topo):
def __init__(self):
Topo.__init__(self)
left=[]
left.append(self.addHost("h1"))
left.append(self.addHost("h2"))
right=[]
right.append(self.addHost("h3"))
right.append(self.addHost("h4"))
switchs=[]
switchs.append(self.addSwitch("s1"))
switchs.append(self.addSwitch("s2"))
self.addLink(left[0],switchs[0])
self.addLink(left[1],switchs[0])
self.addLink(right[0],switchs[1])
self.addLink(right[1],switchs[1])
self.addLink(switchs[0],switchs[1])
topos={'mytopo':(lambda : MyTopo())}
启动ryu
cd RYUPATH/ryu/app/ #首先进入到RYU的安装目录的app目录下,里面有相应的模块
sudo ryu-manager ofctl_rest.py simple_switch.py
# 启动ofctl_rest.py模块以及simple_switch.py交换机,这个是openflow1.0的交换机
启动mininet
sudo mn --controller=remote,ip=192.168.1.197,port=6653 --custom 1.py --topo mytopo
其中 1.py是刚刚定义的拓扑python文件, mytopo是 最后两行
topos={'mytopo':(lambda : MyTopo())} 中指定的拓扑名
注意 这里面有一个坑!!!
加载自定义拓扑文件时 是使用 –custom .py 格式,而不是– custom=.py 格式 –topo 也是类似。
mn –help 出来的结果是:
--topo=TOPO linear|minimal|reversed|single|tree[,param=value...]
-c, --clean clean and exit
--custom=CUSTOM read custom topo and node params from .py
然而 – custom=*.py 是不能加载的
实验细节
1. 在mininet 检查网络联通性
mininet> pingall
*** Ping: testing ping reachability
h1 -> h2 h3 h4
h2 -> h1 h3 h4
h3 -> h1 h2 h4
h4 -> h1 h2 h3
*** Results: 0% dropped (12/12 received)
结论:4台主机 两两互通
2. 分析得到各网络节点的地址(不知道是不是MAC地址,因为交换机居然也有这个地址)
h1-eth0: fe:3b:25:cc:04:97
h2-eth0: d2:3e:55:89:f3:a1
h3-eth0: 02:28:7c:93:27:af
h4-eth0: ba:94:88:a1:55:63
3. 流表”查”
打开chrome的扩展工具 postman
3.1 得到所有交换机列表:
URL: http://192.168.1.197:8080/stats/switches
使用GET方法
结果:
[
1,
2
]
得到一个json list,里面有两个元素:1和2,表示共有两台交换机。
这是他们的DPID,datapath id
3.2 得到某台交换机状态信息
URL : http://192.168.1.197:8080:/stats/desc/
方法:get
其中 是可变的,由3.1 所得填入即可
比如得到交换机1的基本信息:
URL http://192.168.1.197:8080/stats/desc/1
结果:
{
"1":(DPID)
{
"dp_desc": "None",(datapath的描述信息)
"sw_desc": "2.0.2",(软件机的描述信息)
"hw_desc": "Open vSwitch",(交换机描述信息)
"serial_num": "None",(序列号)
"mfr_desc": "Nicira, Inc."(生产商信息)
}
}
3.3 得到指定交换机的所有flow的状态信息
URL: http://192.168.1.197:8080/stats/flow/
方法:GET
如得到交换机1的所有流表项:
{
"1": [
{
"actions": [
"OUTPUT:3"
],(动作,转发到3 号端口)
"idle_timeout": 0,(空闲后存活时间)
"cookie": 0,
"packet_count": 2,(包计数)
"hard_timeout": 0,(存活时间)
"byte_count": 140,(比特计数)
"duration_nsec": 111000000,
"priority": 32768,(优先级)
"duration_sec": 985,(已经存活时间)
"table_id": 0,(在流表1)
"match": (匹配字段)
{
"dl_dst": "02:28:7c:93:27:af",(过滤目的地址为02:28:7c:93:27:af的包,就是去主机3的包)
"in_port": 2(从2号口子来的)
}
(作用:从2号口子来的,要到h3的报文都从3号口子出去哈)
},
{
"actions": [
"OUTPUT:2"
],
"idle_timeout": 0,
"cookie": 0,
"packet_count": 7,
"hard_timeout": 0,
"byte_count": 518,
"duration_nsec": 113000000,
"priority": 32768,
"duration_sec": 985,
"table_id": 0,
"match": {
"dl_dst": "d2:3e:55:89:f3:a1",
"in_port": 3
}
(作用:从3号口子来的,发往h2的包都从2号口子出去哈)
},
{
"actions": [
"OUTPUT:3"
],
"idle_timeout": 0,
"cookie": 0,
"packet_count": 2,
"hard_timeout": 0,
"byte_count": 140,
"duration_nsec": 155000000,
"priority": 32768,
"duration_sec": 985,
"table_id": 0,
"match": {
"dl_dst": "02:28:7c:93:27:af",
"in_port": 1
}
(作用:从1号口子来的,发往h3的的包都从3号口子出去哈
},
{
"actions": [
"OUTPUT:1"
],
"idle_timeout": 0,
"cookie": 0,
"packet_count": 3,
"hard_timeout": 0,
"byte_count": 238,
"duration_nsec": 171000000,
"priority": 32768,
"duration_sec": 985,
"table_id": 0,
"match": {
"dl_dst": "fe:3b:25:cc:04:97",
"in_port": 2
}
(作用:从2号口子来的,发往h1的包都从1号口子出去哈)
},
{
"actions": [
"OUTPUT:2"
],
"idle_timeout": 0,
"cookie": 0,
"packet_count": 2,
"hard_timeout": 0,
"byte_count": 140,
"duration_nsec": 169000000,
"priority": 32768,
"duration_sec": 985,
"table_id": 0,
"match": {
"dl_dst": "d2:3e:55:89:f3:a1",
"in_port": 1
}
(从1号口子来的,发给h2的包都从2号口子出去哈
},
{
"actions": [
"OUTPUT:3"
],
"idle_timeout": 0,
"cookie": 0,
"packet_count": 2,
"hard_timeout": 0,
"byte_count": 140,
"duration_nsec": 137000000,
"priority": 32768,
"duration_sec": 985,
"table_id": 0,
"match": {
"dl_dst": "ba:94:88:a1:55:63",
"in_port": 1
}
(从1号口子来的,发往h4的,从3号口子出去哈)
},
{
"actions": [
"OUTPUT:1"
],
"idle_timeout": 0,
"cookie": 0,
"packet_count": 7,
"hard_timeout": 0,
"byte_count": 518,
"duration_nsec": 157000000,
"priority": 32768,
"duration_sec": 985,
"table_id": 0,
"match": {
"dl_dst": "fe:3b:25:cc:04:97",
"in_port": 3
}
(作用:从3号口子来的,发给h1的包都从1号口子出去哈)
},
{
"actions": [
"OUTPUT:3"
],
"idle_timeout": 0,
"cookie": 0,
"packet_count": 2,
"hard_timeout": 0,
"byte_count": 140,
"duration_nsec": 92000000,
"priority": 32768,
"duration_sec": 985,
"table_id": 0,
"match": {
"dl_dst": "ba:94:88:a1:55:63",
"in_port": 2
}
(从2号口子来的,发给h4的都从3号口子出去哈)
}
]
}
由上面,我们可以分析出:
第一个交换机一个有3个端口
端口1与h1直连
端口2与h2直连
端口3负责与另外一个交换机直连
另一个交换机也是类似的作法
3.3 有条件地查询某台交换机的流表
URL: http://192.168.1.197:8080/stats/flow/
方法:POST
其实就是通过POST一些过滤条件来返回结果,类似于SQL里面的where语句
支持的过滤字段有:
table_id:流表ID
out_port:出端口号
out_group:出组号
cookie:
cookie_mask:
match:匹配字段
prority:优先级
举个栗子:
我们想获取所以目的地为h1的流表项:只要过滤那些匹配域中目的为h1即可:
在BODY里面填:
{
"match":
{
"dl_dst": "fe:3b:25:cc:04:97"
}
}
结果:
{
"1": [
{
"actions": [
"OUTPUT:1"
],
"idle_timeout": 0,
"cookie": 0,
"packet_count": 3,
"hard_timeout": 0,
"byte_count": 238,
"duration_nsec": 426000000,
"priority": 32768,
"duration_sec": 3084,
"table_id": 0,
"match": {
"dl_dst": "fe:3b:25:cc:04:97",
"in_port": 2
}
},
{
"actions": [
"OUTPUT:1"
],
"idle_timeout": 0,
"cookie": 0,
"packet_count": 7,
"hard_timeout": 0,
"byte_count": 518,
"duration_nsec": 412000000,
"priority": 32768,
"duration_sec": 3084,
"table_id": 0,
"match": {
"dl_dst": "fe:3b:25:cc:04:97",
"in_port": 3
}
}
]
}
把所有发给h1的流表项都过滤出来了
3.4 查询指定交换机的全局统计流表的字段
URL: http://192.168.1.197:8080/stats/aggregateflow/
方式:get
查交换机1的:
{
"1": [
{
"packet_count": 27,
"byte_count": 1974,
"flow_count": 8
}
]
}
可以看出是交换机所有流表项的全局性统计
3.5 有条件的查询指定交换机的全局统计流表的字段
与3.3方法类似,也是通过POST方法来过滤掉一些结果
过滤字段一样
3.6 查询指定交换机上所有流表的统计信息
URL: http://192.168.1.197:8080/stats/table/
方法:GET
比如查交换机1的流表相关信息
{
"1": [
{
"wildcards": (通配符)
[
"IN_PORT",
"DL_VLAN",
"DL_SRC",
"NW_DST_BITS",
"NW_SRC_SHIFT",
"NW_DST_SHIFT",
"DL_TYPE"
],
"matched_count": 12911,(已匹配计数)
"name": "classifier",
"active_count": 17,(有效的流表项)
"max_entries": 1000000,
"lookup_count": 13974,(查询计数)
"table_id": 0(流表号)
}
]
}
得到端口的统计信息
URL: http://192.168.1.197:8080/stats/port//[/]
方法:GET
得到指定交换机下的某端口统计信息(如果没有指定端口则获取所有端口)
如得到交换机1下端口1的:
http://192.168.1.197:8080/stats/port/1/1
结果:
{
"1": [
{
"tx_dropped": 1,
"rx_packets": 18,
"rx_crc_err": 0,
"tx_bytes": 219719,
"rx_dropped": 0,
"port_no": 1,
"rx_over_err": 0,
"rx_frame_err": 0,
"rx_bytes": 1308,
"tx_errors": 0,
"collisions": 0,
"rx_errors": 0,
"tx_packets": 1176
}
]
}
得到端口配置信息:
URL: http://192.168.1.197:8080/stats/portdesc/1
方法:GET
得到交换机1的结果:
{
"1": [
{
"hw_addr": "1e:4f:eb:10:65:1c",
"state": 0,
"curr": 192,
"name": "s1-eth1",
"advertised": 0,
"peer": 0,
"supported": 0,
"config": 0,
"port_no": 1
},
{
"hw_addr": "ee:09:8f:0d:35:5c",
"state": 0,
"curr": 192,
"name": "s1-eth2",
"advertised": 0,
"peer": 0,
"supported": 0,
"config": 0,
"port_no": 2
},
{
"hw_addr": "96:e1:f6:ae:21:78",
"state": 0,
"curr": 192,
"name": "s1-eth3",
"advertised": 0,
"peer": 0,
"supported": 0,
"config": 0,
"port_no": 3
},
{
"hw_addr": "2e:48:15:67:7f:43",
"state": 0,
"curr": 0,
"name": "s1",
"advertised": 0,
"peer": 0,
"supported": 0,
"config": 0,
"port_no": "LOCAL"
}
]
}