forked from faucetsdn/ryu
-
Notifications
You must be signed in to change notification settings - Fork 176
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
8 changed files
with
1,188 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
##Network Awareness | ||
|
||
Network Awareness is a set of Ryu applications to collecting the basic network information including the topology, link delay, and link free bandwidth. Also, the Shortest\_forwarding.py application can achieve the shortest path forwarding based on HOP, DELAY and BANDWIDTH. You can set model of computing shortest path when starting Ryu by adding "weight" argument. Moreover, you can set "k-paths" argument to support K-Shortest paths computing. Fortunately, our application supports load balance based on dynamic traffic information. | ||
|
||
The detail information of modules shows below. | ||
|
||
* Network Aware is a module for collecting network information. | ||
|
||
* Network Monitor is a module for collecting network traffic information. | ||
|
||
* Network Delay is a module for collecting link delay information. | ||
|
||
* Shortest\_forwarding is a simple application to achieve shortest forwarding based on hop or delay. | ||
|
||
* Setting is the common setting module. | ||
|
||
|
||
|
||
In this version, we take networkx's data structure to store topology. Meanwhile, we also use networkx's function to calculate shortest path. | ||
|
||
|
||
### Download File | ||
|
||
Download files, and add them to ryu directory, for instance, app/network_awareness | ||
|
||
### Make some changes | ||
|
||
To register parsing parameter, you NEED to add code into flags.py, which is in the topo directory of ryu project. | ||
|
||
CONF.register_cli_opts([ | ||
# k_shortest_forwarding | ||
cfg.IntOpt('k-paths', default=1, help='number for k shortest paths'), | ||
cfg.StrOpt('weight', default='hop', | ||
help='weight type of computing shortest path.')]) | ||
|
||
### Reinstall Ryu | ||
|
||
You have to reinstall Ryu, so that you can run the new code. In the top derectory of ryu project. | ||
|
||
sudo python setup.py install | ||
|
||
|
||
### Start | ||
|
||
Go into the directory, and run applications. You are suggested to add arguments when starting Ryu. The example shows below. | ||
|
||
ryu-manager shortest_forwarding.py --observe-links --k-paths=2 --weight=bw | ||
|
||
The last step is to set up a network and connect to Ryu. | ||
|
||
If you need to show collected information, you can set the parameter in setting.py. Also, you can define your personal setting, such as topology discovery period, You will find out the information shown in terninal. | ||
|
||
Enjoy it! Good Luck! | ||
|
||
If you have any question, you can email me. Don't forget to STAR this repository! |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
"For loading module" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,277 @@ | ||
# Copyright (C) 2016 Li Cheng at Beijing University of Posts | ||
# and Telecommunications. www.muzixing.com | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | ||
# implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
|
||
# conding=utf-8 | ||
import logging | ||
import struct | ||
import copy | ||
import networkx as nx | ||
from operator import attrgetter | ||
from ryu import cfg | ||
from ryu.base import app_manager | ||
from ryu.controller import ofp_event | ||
from ryu.controller.handler import MAIN_DISPATCHER, DEAD_DISPATCHER | ||
from ryu.controller.handler import CONFIG_DISPATCHER | ||
from ryu.controller.handler import set_ev_cls | ||
from ryu.ofproto import ofproto_v1_3 | ||
from ryu.lib.packet import packet | ||
from ryu.lib.packet import ethernet | ||
from ryu.lib.packet import ipv4 | ||
from ryu.lib.packet import arp | ||
from ryu.lib import hub | ||
|
||
from ryu.topology import event, switches | ||
from ryu.topology.api import get_switch, get_link | ||
import setting | ||
|
||
|
||
CONF = cfg.CONF | ||
|
||
|
||
class NetworkAwareness(app_manager.RyuApp): | ||
OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION] | ||
|
||
def __init__(self, *args, **kwargs): | ||
super(NetworkAwareness, self).__init__(*args, **kwargs) | ||
self.topology_api_app = self | ||
self.name = "awareness" | ||
self.link_to_port = {} # (src_dpid,dst_dpid)->(src_port,dst_port) | ||
self.access_table = {} # {(sw,port) :[host1_ip]} | ||
self.switch_port_table = {} # dpip->port_num | ||
self.access_ports = {} # dpid->port_num | ||
self.interior_ports = {} # dpid->port_num | ||
|
||
self.graph = nx.DiGraph() | ||
self.pre_graph = nx.DiGraph() | ||
self.pre_access_table = {} | ||
self.pre_link_to_port = {} | ||
self.shortest_paths = None | ||
|
||
self.discover_thread = hub.spawn(self._discover) | ||
|
||
def _discover(self): | ||
i = 0 | ||
while True: | ||
self.show_topology() | ||
if i == 5: | ||
self.get_topology(None) | ||
i = 0 | ||
hub.sleep(setting.DISCOVERY_PERIOD) | ||
i = i + 1 | ||
|
||
@set_ev_cls(ofp_event.EventOFPSwitchFeatures, CONFIG_DISPATCHER) | ||
def switch_features_handler(self, ev): | ||
datapath = ev.msg.datapath | ||
ofproto = datapath.ofproto | ||
parser = datapath.ofproto_parser | ||
msg = ev.msg | ||
self.logger.info("switch:%s connected", datapath.id) | ||
|
||
# install table-miss flow entry | ||
match = parser.OFPMatch() | ||
actions = [parser.OFPActionOutput(ofproto.OFPP_CONTROLLER, | ||
ofproto.OFPCML_NO_BUFFER)] | ||
self.add_flow(datapath, 0, match, actions) | ||
|
||
def add_flow(self, dp, p, match, actions, idle_timeout=0, hard_timeout=0): | ||
ofproto = dp.ofproto | ||
parser = dp.ofproto_parser | ||
|
||
inst = [parser.OFPInstructionActions(ofproto.OFPIT_APPLY_ACTIONS, | ||
actions)] | ||
|
||
mod = parser.OFPFlowMod(datapath=dp, priority=p, | ||
idle_timeout=idle_timeout, | ||
hard_timeout=hard_timeout, | ||
match=match, instructions=inst) | ||
dp.send_msg(mod) | ||
|
||
def get_host_location(self, host_ip): | ||
for key in self.access_table.keys(): | ||
if self.access_table[key][0] == host_ip: | ||
return key | ||
self.logger.info("%s location is not found." % host_ip) | ||
return None | ||
|
||
def get_switches(self): | ||
return self.switches | ||
|
||
def get_links(self): | ||
return self.link_to_port | ||
|
||
# get Adjacency matrix from link_to_port | ||
def get_graph(self, link_list): | ||
for src in self.switches: | ||
for dst in self.switches: | ||
# self.graph.add_edge(src, dst, weight=float('inf')) | ||
if src == dst: | ||
self.graph.add_edge(src, dst, weight=0) | ||
elif (src, dst) in link_list: | ||
self.graph.add_edge(src, dst, weight=1) | ||
return self.graph | ||
|
||
def create_port_map(self, switch_list): | ||
for sw in switch_list: | ||
dpid = sw.dp.id | ||
self.switch_port_table.setdefault(dpid, set()) | ||
self.interior_ports.setdefault(dpid, set()) | ||
self.access_ports.setdefault(dpid, set()) | ||
|
||
for p in sw.ports: | ||
self.switch_port_table[dpid].add(p.port_no) | ||
|
||
# get links`srouce port to dst port from link_list, | ||
# link_to_port:(src_dpid,dst_dpid)->(src_port,dst_port) | ||
def create_interior_links(self, link_list): | ||
for link in link_list: | ||
src = link.src | ||
dst = link.dst | ||
self.link_to_port[ | ||
(src.dpid, dst.dpid)] = (src.port_no, dst.port_no) | ||
|
||
# find the access ports and interiorior ports | ||
if link.src.dpid in self.switches: | ||
self.interior_ports[link.src.dpid].add(link.src.port_no) | ||
if link.dst.dpid in self.switches: | ||
self.interior_ports[link.dst.dpid].add(link.dst.port_no) | ||
|
||
# get ports without link into access_ports | ||
def create_access_ports(self): | ||
for sw in self.switch_port_table: | ||
all_port_table = self.switch_port_table[sw] | ||
interior_port = self.interior_ports[sw] | ||
self.access_ports[sw] = all_port_table - interior_port | ||
|
||
def k_shortest_paths(self, graph, src, dst, weight='weight', k=1): | ||
generator = nx.shortest_simple_paths(graph, source=src, | ||
target=dst, weight=weight) | ||
shortest_paths = [] | ||
try: | ||
for path in generator: | ||
if k <= 0: | ||
break | ||
shortest_paths.append(path) | ||
k -= 1 | ||
return shortest_paths | ||
except: | ||
self.logger.debug("No path between %s and %s" % (src, dst)) | ||
|
||
def all_k_shortest_paths(self, graph, weight='weight', k=1): | ||
_graph = copy.deepcopy(graph) | ||
paths = {} | ||
|
||
# find ksp in graph. | ||
for src in _graph.nodes(): | ||
paths.setdefault(src, {src: [[src] for i in xrange(k)]}) | ||
for dst in _graph.nodes(): | ||
if src == dst: | ||
continue | ||
paths[src].setdefault(dst, []) | ||
paths[src][dst] = self.k_shortest_paths(_graph, src, dst, | ||
weight=weight, k=k) | ||
return paths | ||
|
||
events = [event.EventSwitchEnter, | ||
event.EventSwitchLeave, event.EventPortAdd, | ||
event.EventPortDelete, event.EventPortModify, | ||
event.EventLinkAdd, event.EventLinkDelete] | ||
|
||
@set_ev_cls(events) | ||
def get_topology(self, ev): | ||
switch_list = get_switch(self.topology_api_app, None) | ||
self.create_port_map(switch_list) | ||
self.switches = self.switch_port_table.keys() | ||
links = get_link(self.topology_api_app, None) | ||
self.create_interior_links(links) | ||
self.create_access_ports() | ||
self.get_graph(self.link_to_port.keys()) | ||
self.shortest_paths = self.all_k_shortest_paths( | ||
self.graph, weight='weight', k=CONF.k_paths) | ||
|
||
def register_access_info(self, dpid, in_port, ip, mac): | ||
if in_port in self.access_ports[dpid]: | ||
if (dpid, in_port) in self.access_table: | ||
if self.access_table[(dpid, in_port)] == (ip, mac): | ||
return | ||
else: | ||
self.access_table[(dpid, in_port)] = (ip, mac) | ||
return | ||
else: | ||
self.access_table.setdefault((dpid, in_port), None) | ||
self.access_table[(dpid, in_port)] = (ip, mac) | ||
return | ||
|
||
@set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER) | ||
def _packet_in_handler(self, ev): | ||
msg = ev.msg | ||
datapath = msg.datapath | ||
|
||
parser = datapath.ofproto_parser | ||
in_port = msg.match['in_port'] | ||
pkt = packet.Packet(msg.data) | ||
|
||
eth_type = pkt.get_protocols(ethernet.ethernet)[0].ethertype | ||
arp_pkt = pkt.get_protocol(arp.arp) | ||
ip_pkt = pkt.get_protocol(ipv4.ipv4) | ||
|
||
if arp_pkt: | ||
arp_src_ip = arp_pkt.src_ip | ||
arp_dst_ip = arp_pkt.dst_ip | ||
mac = arp_pkt.src_mac | ||
|
||
# record the access info | ||
self.register_access_info(datapath.id, in_port, arp_src_ip, mac) | ||
|
||
def show_topology(self): | ||
switch_num = len(self.graph.nodes()) | ||
if self.pre_graph != self.graph or setting.TOSHOW: | ||
print "---------------------Topo Link---------------------" | ||
print '%10s' % ("switch"), | ||
for i in xrange(1, switch_num + 1): | ||
print '%10d' % i, | ||
print "" | ||
for i in self.graph.nodes(): | ||
print '%10d' % i, | ||
for j in self.graph[i].values(): | ||
print '%10.0f' % j['weight'], | ||
print "" | ||
self.pre_graph = copy.deepcopy(self.graph) | ||
|
||
if self.pre_link_to_port != self.link_to_port or setting.TOSHOW: | ||
print "---------------------Link Port---------------------" | ||
print '%10s' % ("switch"), | ||
for i in xrange(1, switch_num + 1): | ||
print '%10d' % i, | ||
print "" | ||
for i in xrange(1, switch_num + 1): | ||
print '%10d' % i, | ||
for j in xrange(1, switch_num + 1): | ||
if (i, j) in self.link_to_port.keys(): | ||
print '%10s' % str(self.link_to_port[(i, j)]), | ||
else: | ||
print '%10s' % "No-link", | ||
print "" | ||
self.pre_link_to_port = copy.deepcopy(self.link_to_port) | ||
|
||
if self.pre_access_table != self.access_table or setting.TOSHOW: | ||
print "----------------Access Host-------------------" | ||
print '%10s' % ("switch"), '%12s' % "Host" | ||
if not self.access_table.keys(): | ||
print " NO found host" | ||
else: | ||
for tup in self.access_table: | ||
print '%10d: ' % tup[0], self.access_table[tup] | ||
self.pre_access_table = copy.deepcopy(self.access_table) |
Oops, something went wrong.