RYU實驗筆記(一):多RYU控制器連接拓撲以及相關流表操作

關於單RYU連接拓撲和流表操作:


在構建這樣一個拓撲並讓多RYU控制器連接之前需要知道的幾個重要信息:

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

  • OFP_TCP_PORT = 6653,它用於連接向OVS交換機,作爲監聽端口
  • OFP_SSL_PORT = 6653 ,目前暫無用處
  • WSGI_PORT=8080,它用於進行一系列restAPI的相關操作。

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

二,restAPI的相關操作則可以在ryu/ryu/app/ofctl_rest.py中查看,其中描述流表的增刪查看等等操作和具體代碼

三,爲使拓撲靈活度更強,特地設定了一種基於參數的拓撲,通過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)

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號交換機的編號出了點問題,不過不影響實驗)

4.2開啓拓撲腳本並觀察流表

接下來再第一個休眠期裏開啓拓撲,休眠期過後可見這些控制器獲取到的流表,可以看出其流表中的流表項都只有1個,那就是連接上控制器的那一個(u只是unicode碼所致)

4.3在mininet上作ping測試,然後觀察流表

在第二個休眠期pingall然後再次獲取流表,光是1號交換機的流表就讓人應接不暇,然後結束


總結:

  • 終於實現了多控制器控制拓撲並進行相關流表操作的目標
  • 功能性和靈活度有待完善
  • 後期將結合flask以及拓撲的GUI界面和還有網絡測試功能,做到在web端創建基於參數的可視化網絡並對它進行控制器控制以及網絡測試測量
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章