RYU实验笔记(一):多RYU控制器连接拓扑以及相关流表操作

关于单RYU连接拓扑和流表操作:

  • https://www.sdnlab.com/11552.html
  • https://www.sdnlab.com/1785.html

在构建这样一个拓扑并让多RYU控制器连接之前需要知道的几个重要信息:

一,RYU控制器有三个端口:

  • OFP_TCP_PORT = 6653,它用于连接向OVS交换机,作为监听端口
  • OFP_SSL_PORT = 6653 ,目前暂无用处
  • WSGI_PORT=8080,它用于进行一系列restAPI的相关操作。

(其中前面两个端口的信息存在ryu/ryu/ofproto/ofproto_common.py中)

RYU实验笔记(一):多RYU控制器连接拓扑以及相关流表操作_第1张图片

二,restAPI的相关操作则可以在ryu/ryu/app/ofctl_rest.py中查看,其中描述流表的增删查看等等操作和具体代码

 

RYU实验笔记(一):多RYU控制器连接拓扑以及相关流表操作_第2张图片RYU实验笔记(一):多RYU控制器连接拓扑以及相关流表操作_第3张图片

三,为使拓扑灵活度更强,特地设定了一种基于参数的拓扑,通过JSON类型的参数设置拓扑(这个JSON文件储存了主机个数IP地址交换机个数和链路等等),相关代码已传至Github,本次使用的只是一个简陋版基于参数的拓扑,以便于把重心放在“多RYU控制器”上,本次拓扑:

 
                 MASTER
                 ___|___   
          _____ /   |   \ _____
     ___ /     /    |    \     \ _____
    /   /     /     |     \     \     \
  c0    c1    c2    c3    c4     c5     c6 
   |    |     |     |     |      |      |
   s0   s1    s2    s3    s4     s5     s6
   /\   /\    /\    /\    /\     /\     /\
 h0 h1 h2 h3 h4 h5 h6 h7 h8 h9 h10 h11 h12 h13

需要:

  • 基于参数的拓扑脚本:拓扑的生成
  • 自定义控制器类脚本:功能的定义
  • 用户(控制器群)类:整个框架的实例化
  • 流表操作脚本:在实例上进行一系列操作
  • 两个终端:一个用于创建mininet,一个用于调用流表操作脚本
  • 环境:mininet,RYU

1:基于参数的拓扑脚本:

先写好基于参数的拓扑脚本,并尝试运行,(脚本懒得改模块了就一并搬过来了)这里的例子是创建了7个控制器,每1个控制器管辖1个交换机,每个交换机连接2个主机,相邻两个交换机之间相连,由图可见由于没有开启RYU控制器所以没有连接上控制器,所以pingall的时候也都ping不通

#!/usr/bin/python
from mininet.net import Mininet
from mininet.node import Controller, RemoteController, OVSSwitch
from mininet.cli import CLI
from mininet.log import setLogLevel, info
from mininet.link import Link, Intf, TCLink
from mininet.topo import Topo
from ryu.app.wsgi import ControllerBase
from ryu.app.wsgi import Response
from ryu.app.wsgi import WSGIApplication
import logging
import os


def multiControllerNet(con_num=7, sw_num=7, host_num=14):
	"Create a network from semi-scratch with multiple controllers."
	controller_list = []
	switch_list = []
	host_list = []
	
	net = Mininet(controller=None, switch=OVSSwitch, link=TCLink)
	
	for i in xrange(con_num):
		name = 'controller%s' % str(i)
		c = net.addController(name, controller=RemoteController,ip='127.0.0.1',port=6661 + i)
		controller_list.append(c)
		print("*** Creating %s" % name)
	
	print("*** Creating switches")
	switch_list = [net.addSwitch('s%d' % n) for n in xrange(sw_num)]
	print(switch_list)
	print("*** Creating hosts")
	
	host_list = [net.addHost('h%d' % n) for n in xrange(host_num)]
	
	print("*** Creating links of host2switch.")
	
	for i in xrange(0, sw_num):
		net.addLink(switch_list[i], host_list[i * 2])
		net.addLink(switch_list[i], host_list[i * 2 + 1])
	
	print("*** Creating interior links of switch2switch.")
	
	
	for i in xrange(0, sw_num, sw_num / con_num):
		for j in xrange(sw_num / con_num):
			for k in xrange(sw_num / con_num):
				if j != k and j > k:
					net.addLink(switch_list[i + j], switch_list[i + k])
	
	print("*** Creating intra links of switch2switch.")
	for i in range(con_num-1):
		net.addLink(switch_list[i],switch_list[i+1])
	
	print("*** Starting network")
	
	net.build()
	for c in controller_list:
		c.start()
	
	_No = 0
	for i in xrange(0, sw_num, sw_num / con_num):
		for j in xrange(sw_num / con_num):
			switch_list[i + j].start([controller_list[_No]])
		_No += 1
	
	# print "*** Testing network"
	# net.pingAll()
	
	print("*** Running CLI")
	
	CLI(net)
	
	print("*** Stopping network")
	
	net.stop()


if __name__ == '__main__':
	setLogLevel('info')  # for CLI output
	multiControllerNet(con_num=7, sw_num=7, host_num=14)

RYU实验笔记(一):多RYU控制器连接拓扑以及相关流表操作_第4张图片

2.自定义控制器类(Mycontroller):

它继承了mininetAPI里的原始控制器类,并调用restAPI的脚本,这里只写了一些些功能并没有完全把ofctl_rest.py里的功能都写出来(其中有几个flow是供来做参考的),其中的match_flow是自己定义的一种匹配方式:事先设定一个你想要的匹配域A,kind=1时把有包含A的流表取出来,kind=2时取其补集

#!/usr/bin/python
import os
import sys
import json
import re
import subprocess
import argparse
import logging
import ast
import parser
import requests
from flask import Flask
from flask import request
from flask import url_for
from flask import jsonify
from ryu.app.wsgi import ControllerBase
from ryu.app.wsgi import Response
from ryu.app.wsgi import WSGIApplication
#from mininet.net import Mininet
#from mininet.node import Switch, OVSSwitch, Ryu,RemoteController,Controller
sw='3'
swag='{"match":{"in_port": 1,"dl_dst":"00:00:00:00:00:02"}}'
flow1='{"dpid":3, "cookie": 1, "cookie_mask": 1,"table_id": 1,"idle_timeout": 300,"hard_timeout": 300,"priority": 35,"flags": 1,"match":{"in_port":1},"actions":[{"type":"OUTPUT","port": 2}]}'
flow2='{"dpid":3, "cookie": 1, "cookie_mask": 1,"table_id": 1,"idle_timeout": 300,"hard_timeout": 300, "priority": 1,"flags": 1,"match":{"in_port":1},"actions":[]}'
flow3='{"dpid":3, "cookie": 1, "cookie_mask": 1,"table_id": 1,"idle_timeout": 320,"hard_timeout": 340, "priority": 1,"flags": 2,"match":{"dl_dst": "00:00:00:00:00:01", "dl_src": "00:00:00:00:00:02", "in_port": 2},"actions":[]}'
flow4='{"dpid":3 ,"cookie": 1,"cookie_mask": 1,"table_id": 0,"idle_timeout": 30,"hard_timeout": 3000,"priority": 11111,"flags": 1,"match":{"dl_src":"00:00:00:00:00:02","dl_dst":"00:00:00:00:00:01"},"actions":[ { "type": "OUTPUT","port": 2}]}'
class MyController():
	def __init__(self,cip,cport):
		self.cip=cip
		self.cport=cport
		self.path='http://' + self.cip + ':' + self.cport
	#get the list of all switche
	
	def get_switches(self):
		item =  self.path+ '/stats/switches'
		tmpres = os.popen('curl %s -s' % item).readlines()
		ss = tmpres[0][:-1]
		ss = ss[1:]
		sl = ss.split(', ')
		return (sl)
	# get the desc of a single switch
	def get_switch_desc(sid):
		if self.findswitch(sid):
			item = self.path + '/stats/desc/' + sid
			tmpres = os.popen('curl  %s -s' % item).readlines()
			return (tmpres)
		else:
			return 0
	# get the flowtable of a single switch
	def get_flow_states(self,sid):
		if self.findswitch(sid):
			item = self.path + '/stats/flow/' + sid
			tmpres = os.popen('curl  %s -s' % item).readlines()
			flow_table  = json.loads(tmpres[0])
			return (flow_table)
		else:
			print('Error:not such a flowtable')
			return 'Error:not such a flowtable'
	# figure out whether sid is useful
	def findswitch(self,sid):
		if sid not in self.get_switches():
			print('Error:not such a switch')
			return 0
		else:
			return 1
        # match the flowtable of a single switch you want
	def match_flow(self, swag, sid, kind):
		if self.findswitch(sid):
			flow_table = self.get_flow_states(sid)
			table = json.loads(swag)
			print(table['match'])
			number = 0;
			location1 = [];
			location2 = [];
			for flow in flow_table[sid]:
				flag = 0;
				for element in flow['match']:
					if element in table['match']:
						if flow['match'][element] == table['match'][element]:
							flag = flag + 1;
						else:
							flag = -20;
				if flag > 0:
					print('flow' + str(number) + 'matchable')
					location2.append(number)
				else:
					print('flow' + str(number) + 'unmatchable')
					location1.append(number);
				number = number + 1;
			if kind == 1:
				flag = 0
				for i in location1:
					del flow_table[sid][i - flag]
					flag = flag + 1
			if kind == 2:
				flag = 0
				for i in location2:
					del flow_table[sid][i - flag]
					flag = flag + 1
			return flow_table
		else:
			return 0
	# add the flow
	def add_flow(self,flow):
		item=self.path+'/stats/flowentry/add'
		tmpres = os.popen('curl -X POST -d \'%s\' %s -s' %(flow,item)).readlines()
		return 'adding flow'
	# delete the flow with match and priority
	def del_flow_mp(self,flow):
		item=self.path+'/stats/flowentry/delete_strict'
		tmpres = os.popen('curl -X POST -d \'%s\' %s -s' %(flow,item)).readlines()
		return 'deleting flow'
	# delete the flow with match
	def del_flow_m(self,flow):
		item=self.path+'/stats/flowentry/delete'
		tmpres = os.popen('curl -X POST -d \'%s\' %s -s' %(flow,item)).readlines()
		return 'deleting flow'
	# delete all the flow
	def del_all_flow(self,sid):
		if self.findswitch(sid):
			item =self.path+'/stats/flowentry/clear/'+sid
			tmptres = os.system('curl -X DELETE %s -s'%item)
			return 'clear!'
		else:
			return 'Error:not such a switch'

3.用户(控制器群)类(master):

它继承了控制器类,通过它管控各个控制器,这里就展示两个最主要的功能,开启控制器和关闭控制器,其中可见开启控制器使用了tcp_listen_port&wsgi_port,为使端口不冲突,特地给每个控制器设置了不一样的端口,在配置端口的时候可事先使用netstats -ap查看端口的使用情况,至于关闭控制器功能则就是将其端口对应的进程所关闭

#!/usr/bin/python
from tf import MyController
from mininet.net import Mininet
from mininet.node import Controller, RemoteController, OVSSwitch
from mininet.cli import CLI
from mininet.log import setLogLevel, info
from mininet.link import Link, Intf, TCLink
from mininet.topo import Topo
from ryu.app.wsgi import ControllerBase
from ryu.app.wsgi import Response
from ryu.app.wsgi import WSGIApplication
import logging
import time
import os
flow4='{"dpid":33 ,"cookie": 1,"cookie_mask": 1,"table_id": 0,"idle_timeout": 30,"hard_timeout": 3000,"priority": 11111,"flags": 1,"match":{},"actions":[ { "type": "OUTPUT","port":"controller"}]}'

class master(MyController):
	def __init__(self,cnum):
		self.cnum=cnum
		self.clist=[]
	def createcontroller(self):
		for i in range(0,self.cnum):
			tcp_port=6661+i;
			lis_port=9090+i;
			tcp_port=str(tcp_port)
			lis_port=str(lis_port)
			name='app'+str(i)
			item='ryu-manager ofctl_rest.py simple_switch_13.py --ofp-tcp-listen-port='+tcp_port+' --wsapi-port '+lis_port+'&'
			os.system(item)
			controller=MyController('0.0.0.0',lis_port)
			self.clist.append(controller)
	def clear(self):
		for i in range(0,self.cnum):
			command='''kill -9 $(netstat -nlp | grep :'''+str(9090+i)+''' | awk '{print $7}' | awk -F"/" '{ print $1 }')'''
			os.system(command)
			os.system("netstat -an|awk '/tcp/ {print $6}'|sort|uniq -c")
	

4.用户的实例化和相关部署和实施

写好一个简易流表操作脚本从而达到通过RYU进行流表操作的目的

#!/usr/bin/python
from rf import master
from tf import MyController
imoprt time
me=master(7)
me.createcontroller()
time.sleep(20)
for i in range(7):
	print(me.clist[i].get_flow_states(str(i)))
time.sleep(200)
for i in range(7):
	print(me.clist[i].get_flow_states(str(i)))
me.clear()

注意到这里有两个休眠期,在开启这个脚本的时候等待20秒是为了开启拓扑脚本,然后将获取到这个拓扑的每个交换机的流表,然后等待两百秒是为了进行pingall,然后再获取一次流表,如果没有错的话,第一次获取到的流表只有连向控制器的流表,而第二次就是有连向其他交换机接口的流表了

4.1开启流表操作脚本:

出现(进程号)wsgi starting up on http://0.0.0.0:wsgi_port就是开启成功了,开启成功后就有流了(0号交换机的编号出了点问题,不过不影响实验)

RYU实验笔记(一):多RYU控制器连接拓扑以及相关流表操作_第5张图片RYU实验笔记(一):多RYU控制器连接拓扑以及相关流表操作_第6张图片

4.2开启拓扑脚本并观察流表

接下来再第一个休眠期里开启拓扑,休眠期过后可见这些控制器获取到的流表,可以看出其流表中的流表项都只有1个,那就是连接上控制器的那一个(u只是unicode码所致)

RYU实验笔记(一):多RYU控制器连接拓扑以及相关流表操作_第7张图片

RYU实验笔记(一):多RYU控制器连接拓扑以及相关流表操作_第8张图片

4.3在mininet上作ping测试,然后观察流表

在第二个休眠期pingall然后再次获取流表,光是1号交换机的流表就让人应接不暇,然后结束

RYU实验笔记(一):多RYU控制器连接拓扑以及相关流表操作_第9张图片RYU实验笔记(一):多RYU控制器连接拓扑以及相关流表操作_第10张图片

RYU实验笔记(一):多RYU控制器连接拓扑以及相关流表操作_第11张图片


总结:

  • 终于实现了多控制器控制拓扑并进行相关流表操作的目标
  • 功能性和灵活度有待完善
  • 后期将结合flask以及拓扑的GUI界面和还有网络测试功能,做到在web端创建基于参数的可视化网络并对它进行控制器控制以及网络测试测量

你可能感兴趣的:(Level.4,SDN实验)