From f5935cd1a3e47578ad5dc2507ed1157697d25f2b Mon Sep 17 00:00:00 2001 From: HsiaoHsuan Date: Tue, 2 Feb 2016 06:50:52 -0800 Subject: [PATCH 01/31] Revise ryu adapter --- adapter/ryu/omniui/omniui.py | 686 +++++++++++++++++++++++++++++------ 1 file changed, 565 insertions(+), 121 deletions(-) diff --git a/adapter/ryu/omniui/omniui.py b/adapter/ryu/omniui/omniui.py index e071ed2..fe9cf0a 100644 --- a/adapter/ryu/omniui/omniui.py +++ b/adapter/ryu/omniui/omniui.py @@ -7,7 +7,7 @@ from ryu.base import app_manager from ryu.controller import ofp_event from ryu.controller import dpset -from ryu.controller.handler import MAIN_DISPATCHER +from ryu.controller.handler import MAIN_DISPATCHER, CONFIG_DISPATCHER, DEAD_DISPATCHER from ryu.controller.handler import set_ev_cls from ryu.lib import dpid as dpid_lib from ryu.ofproto import ofproto_v1_0 @@ -18,8 +18,20 @@ from ryu.lib import ofctl_v1_3 from ryu.lib.dpid import dpid_to_str from ryu.topology.api import get_switch, get_link +import requests +import subprocess +from operator import attrgetter +from ryu.ofproto.ether import ETH_TYPE_LLDP, ETH_TYPE_IPV6 +from ryu.lib import hub +from ryu.lib.packet import * +from ryu.topology import event, switches + +global controllerName +controllerName = 'DEFAULT' class OmniUI(app_manager.RyuApp): + OFP_VERSIONS = [ofproto_v1_0.OFP_VERSION, ofproto_v1_2.OFP_VERSION, ofproto_v1_3.OFP_VERSION] + _EVENTS = [event.EventPortAdd] _CONTEXTS = { 'wsgi': WSGIApplication, 'dpset': dpset.DPSet @@ -33,18 +45,20 @@ def __init__(self, *args, **kwargs): self.data['dpset'] = kwargs['dpset'] self.data['waiters'] = self.waiters self.data['omniui'] = self + self.mac_to_port = {} + self.port_to_feature = {} + self.datapaths = {} + self.monitor_thread = hub.spawn(self.monitor) + self.dpset = self.data['dpset'] mapper = wsgi.mapper wsgi.registory['RestController'] = self.data - mapper.connect('omniui', '/wm/omniui/switch/json', - controller=RestController, action='switches', - conditions=dict(method=['GET'])) - mapper.connect('omniui', '/wm/omniui/link/json', - controller=RestController, action='links', - conditions=dict(method=['GET'])) mapper.connect('omniui', '/wm/omniui/add/json', controller=RestController, action='mod_flow_entry', conditions=dict(method=['POST'])) + mapper.connect('omniui', '/wm/omniui/controller/name', + controller=RestController, action='get_controller_name', + conditions=dict(method=['POST'])) @set_ev_cls([ofp_event.EventOFPFlowStatsReply, ofp_event.EventOFPPortStatsReply], MAIN_DISPATCHER) def stats_reply_handler(self, ev): @@ -71,19 +85,452 @@ def stats_reply_handler(self, ev): del self.waiters[dp.id][msg.xid] lock.set() -class RestController(ControllerBase): - def __init__(self, req, link, data, **config): - super(RestController, self).__init__(req, link, data, **config) - self.omniui = data['omniui'] - self.dpset = data['dpset'] - self.waiters = data['waiters'] + @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 + + match = parser.OFPMatch() + actions = [parser.OFPActionOutput(ofproto.OFPP_CONTROLLER, + ofproto.OFPCML_NO_BUFFER)] + self.add_flow(datapath, 0, match, actions) + + def add_flow(self, datapath, priority, match, actions): + ofproto = datapath.ofproto + parser = datapath.ofproto_parser + + inst = [parser.OFPInstructionActions(ofproto.OFPIT_APPLY_ACTIONS, + actions)] + + mod = parser.OFPFlowMod(datapath=datapath, priority=priority, + match=match, instructions=inst) + datapath.send_msg(mod) + + # + # try post json to core + # + def post_json_to_core(self, url, data): + try: + resp = requests.post(url, data = data, headers = {'Content-Type': 'application/json'}) + print resp + except Exception, e: + print(str(e)) + + # + # handle add switch event + # + @set_ev_cls(event.EventSwitchEnter) + def add_device_handler(self, ev): + switch = ev.switch + print '*****add device*****' + + # format dpid + temp = "%016x" %switch.dp.id + temp = map(str, temp) + for i in range(2, 23, 3): + temp.insert(i, ':') + + addDevice = { + 'dpid': ''.join(temp), + 'controller': controllerName + } + self.port_to_feature[addDevice['dpid']] = {} + + print json.dumps(addDevice) + self.post_json_to_core("http://localhost:5567/publish/adddevice", json.dumps(addDevice)) + + # send add port event + for port in switch.ports: + n_ev = event.EventPortAdd(port) + self.send_event_to_observers(n_ev) + + # + # handle delete switch event + # + @set_ev_cls(event.EventSwitchLeave) + def del_device_handler(self, ev): + switch = ev.switch + print '*****del device*****' + + # format dpid + temp = "%016x" %switch.dp.id + temp = map(str, temp) + for i in range(2, 23, 3): + temp.insert(i, ':') + + delDevice = { + 'dpid': ''.join(temp), + 'controller': controllerName + } + del self.port_to_feature[delDevice['dpid']] + + print json.dumps(delDevice) + self.post_json_to_core("http://localhost:5567/publish/deldevice", json.dumps(delDevice)) + + # + # handle modify port event + # + @set_ev_cls(event.EventPortModify) + def mod_port_handler(self, ev): + port = ev.port + print '*****mod port*****' + + if port.is_down(): + print '***down***' + + # format dpid + temp = "%016x" %port.dpid + temp = map(str, temp) + for i in range(2, 23, 3): + temp.insert(i, ':') + + modPort = { + 'dpid': ''.join(temp), + 'port': str(port.port_no), + 'controller': controllerName + } + del self.port_to_feature[modPort['dpid']][modPort['port']] + + print json.dumps(modPort) + self.post_json_to_core("http://localhost:5567/publish/delport", json.dumps(modPort)) + else: + print '***live***' + + # format dpid + temp = "%016x" %port.dpid + temp = map(str, temp) + for i in range(2, 23, 3): + temp.insert(i, ':') + + modPort = { + 'dpid': ''.join(temp), + 'port': str(port.port_no), + 'controller': controllerName + } + self.port_to_feature[modPort['dpid']][modPort['port']] = str(port.currentFeatures) + + print json.dumps(modPort) + self.post_json_to_core("http://localhost:5567/publish/addport", json.dumps(modPort)) + + # + # handle add port event + # + @set_ev_cls(event.EventPortAdd) + def add_port_handler(self, ev): + port = ev.port + print '*****add port*****' + + # format dpid + temp = "%016x" %port.dpid + temp = map(str, temp) + for i in range(2, 23, 3): + temp.insert(i, ':') + + addPort = { + 'dpid': ''.join(temp), + 'port': str(port.port_no), + 'controller': controllerName + } + self.port_to_feature[addPort['dpid']][addPort['port']] = str(port.currentFeatures) + + print json.dumps(addPort) + self.post_json_to_core("http://localhost:5567/publish/addport", json.dumps(addPort)) + + # + # handle delete port event + # + @set_ev_cls(event.EventPortDelete) + def del_port_handler(self, ev): + port = ev.port + print '*****del port*****' + + # format dpid + temp = "%016x" %port.dpid + temp = map(str, temp) + for i in range(2, 23, 3): + temp.insert(i, ':') + + delPort = { + 'dpid': ''.join(temp), + 'port': str(port.port_no), + 'controller': controllerName + } + del self.port_to_feature[delPort['dpid']][delPort['port']] + + print json.dumps(delPort) + self.post_json_to_core("http://localhost:5567/publish/delport", json.dumps(delPort)) + + # + # handle add link event + # + @set_ev_cls(event.EventLinkAdd) + def add_link_handler(self, ev): + link = ev.link + print '*****add link*****' + + # format src dpid + temp = "%016x" %link.src.dpid + temp = map(str, temp) + for i in range(2, 23, 3): + temp.insert(i, ':') + nodesrc = { + 'dpid': ''.join(temp), + 'port': str(link.src.port_no) + } + + # format dst dpid + temp = "%016x" %link.dst.dpid + temp = map(str, temp) + for i in range(2, 23, 3): + temp.insert(i, ':') + nodedst = { + 'dpid': ''.join(temp), + 'port': str(link.dst.port_no) + } + + addLink = { + 'link': [nodesrc, nodedst], + 'controller': controllerName + } + + print json.dumps(addLink) + self.post_json_to_core("http://localhost:5567/publish/addlink", json.dumps(addLink)) + + # + # handle delete link event + # + @set_ev_cls(event.EventLinkDelete) + def del_link_handler(self, ev): + link = ev.link + print '*****del link*****' + + # format src dpid + temp = "%016x" %link.src.dpid + temp = map(str, temp) + for i in range(2, 23, 3): + temp.insert(i, ':') + nodesrc = { + 'dpid': ''.join(temp), + 'port': str(link.src.port_no) + } + + # format dst dpid + temp = "%016x" %link.dst.dpid + temp = map(str, temp) + for i in range(2, 23, 3): + temp.insert(i, ':') + nodedst = { + 'dpid': ''.join(temp), + 'port': str(link.dst.port_no) + } + + delLink = { + 'link': [nodesrc, nodedst], + 'controller': controllerName + } + + print json.dumps(delLink) + self.post_json_to_core("http://localhost:5567/publish/dellink", json.dumps(delLink)) + + # + # handle add host event + # + @set_ev_cls(event.EventHostAdd) + def add_host_handler(self, ev): + host = ev.host + print '*****add host*****' + + # format dpid + temp = "%016x" %host.port.dpid + temp = map(str, temp) + for i in range(2, 23, 3): + temp.insert(i, ':') + nodeloc = { + 'dpid': ''.join(temp), + 'port': str(host.port.port_no) + } + + addHost = { + 'mac': host.mac, + 'type': 'wired', + 'location': nodeloc, + 'vlan': str(host.vlan[len(host.vlan)-1]) if host.vlan else "0", + 'ip': str(host.ipv4[len(host.ipv4)-1]) if host.ipv4 else "0.0.0.0", + 'controller': controllerName + } + + print json.dumps(addHost) + self.post_json_to_core("http://localhost:5567/publish/addhost", json.dumps(addHost)) + + # + # handle delete host event + # + @set_ev_cls(event.EventHostDelete) + def del_host_handler(self, ev): + host = ev.host + print '*****del host*****' + + delHost = { + 'mac': host.mac, + 'controller': controllerName + } + + print json.dumps(delHost) + self.post_json_to_core("http://localhost:5567/publish/delhost", json.dumps(delHost)) + + # + # handle packet in event + # + @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER) + def packet_in_handler(self, ev): + msg = ev.msg + datapath = msg.datapath + ofproto = datapath.ofproto + parser = datapath.ofproto_parser + + dpid = datapath.id + + if ofproto.OFP_VERSION == ofproto_v1_0.OFP_VERSION: + in_port = msg.in_port + else: + in_port = msg.match['in_port'] + + pkt = packet.Packet(msg.data) + eth = pkt.get_protocols(ethernet.ethernet)[0] - # return dpid of all nodes - def getNodes(self): - return self.dpset.dps.keys() + # ignore lldp packet & ipv6 + if (eth.ethertype == ETH_TYPE_LLDP) | (eth.ethertype == ETH_TYPE_IPV6): + return + + src = eth.src + dst = eth.dst + + self.mac_to_port.setdefault(dpid, {}) + + # learn a mac address to avoid FLOOD next time. + self.mac_to_port[dpid][src] = in_port + + if dst in self.mac_to_port[dpid]: + out_port = self.mac_to_port[dpid][dst] + else: + out_port = ofproto.OFPP_FLOOD + + actions = [parser.OFPActionOutput(out_port)] + + # install a flow to avoid packet_in next time + if out_port != ofproto.OFPP_FLOOD: + match = parser.OFPMatch(in_port=in_port, eth_dst=dst, eth_src=src) + self.add_flow(datapath, 1, match, actions) + + data = None + if msg.buffer_id == ofproto.OFP_NO_BUFFER: + data = msg.data + + out = parser.OFPPacketOut(datapath=datapath, buffer_id=msg.buffer_id, + in_port=in_port, actions=actions, data=data) + datapath.send_msg(out) + + print '*****packet in*****' + + packetIn = {} - # return flow table of specific dpid - def getFlows(self, dpid): + # format dpid + temp = "%016x" %dpid + temp = map(str, temp) + for i in range(2, 23, 3): + temp.insert(i, ':') + packetIn["dpid"] = ''.join(temp) + + packetIn["in_port"] = str(in_port) + + pkt_ethernet = pkt.get_protocol(ethernet.ethernet) + if not pkt_ethernet: + return + else: + packetIn["mac_src"] = pkt_ethernet.src + packetIn["mac_dst"] = pkt_ethernet.dst + packetIn["ether_type"] = 'x0'.join(hex(pkt_ethernet.ethertype).split('x')) if len(hex(pkt_ethernet.ethertype)) < 6 else hex(pkt_ethernet.ethertype) + + for p in pkt.protocols: + if hasattr(p, 'src'): + packetIn["ip_src"] = p.src + packetIn["ip_dst"] = p.dst + + if hasattr(p, 'src_ip'): + packetIn["ip_src"] = p.src_ip + packetIn["ip_dst"] = p.dst_ip + + if hasattr(p, 'proto'): + packetIn["protocol"] = 'x0'.join(hex(p.proto).split('x')) if ((len(hex(p.proto)) < 4) | (len(hex(p.proto)) == 5)) else hex(p.proto) + + if hasattr(p, 'src_port'): + packetIn["port_src"] = str(p.src_port) + packetIn["port_dst"] = str(p.dst_port) + + packetIn["protocol"] = '0' if 'protocol' not in packetIn else packetIn["protocol"] + packetIn["ip_src"] = '0.0.0.0' if 'ip_src' not in packetIn else packetIn["ip_src"] + packetIn["ip_dst"] = '0.0.0.0' if 'ip_dst' not in packetIn else packetIn["ip_dst"] + packetIn["port_src"] = '0' if 'port_src' not in packetIn else packetIn["port_src"] + packetIn["port_dst"] = '0' if 'port_dst' not in packetIn else packetIn["port_dst"] + packetIn["controller"] = controllerName + + print json.dumps(packetIn) + self.post_json_to_core("http://localhost:5567/publish/packet", json.dumps(packetIn)) + + # + # for datapath + # + @set_ev_cls(ofp_event.EventOFPStateChange, [MAIN_DISPATCHER, DEAD_DISPATCHER]) + def state_change_handler(self, ev): + datapath = ev.datapath + if ev.state == MAIN_DISPATCHER: + if not datapath.id in self.datapaths: + self.logger.debug('register datapath: %016x', datapath.id) + self.datapaths[datapath.id] = datapath + elif ev.state == DEAD_DISPATCHER: + if datapath.id in self.datapaths: + self.logger.debug('unregister datapath: %016x', datapath.id) + del self.datapaths[datapath.id] + + # + # for polling + # + def monitor(self): + while True: + for dp in self.datapaths.values(): + tempflow = self.flow_stats_reply_handler_API(dp.id) # from OpenADM + tempport = self.port_stats_reply_handler_API(dp.id) # from OpenADM + self.controller_stats_reply_handler() + + hub.sleep(5) + + # + # polling controller + # + def controller_stats_reply_handler(self): + print '----------------------controller----------------------' + + os = subprocess.check_output("cat /etc/issue".split()) + mem = subprocess.check_output("free -h", shell=True) + cpu = subprocess.check_output("cat /proc/loadavg".split()) + controllerstatsReply = { + 'controller': controllerName, + 'type': 'ryu', + 'os': ' '.join(os.split()), + 'mem_total': mem.split()[7], + 'mem_used': mem.split()[8], + 'mem_free': mem.split()[9], + 'cpu': cpu.split()[0] + } + + print json.dumps(controllerstatsReply) + self.post_json_to_core("http://localhost:5567/publish/controller", json.dumps(controllerstatsReply)) + + # + # polling flows + # + def flow_stats_reply_handler_API(self, dpid): flow = {} dp = self.dpset.get(int(dpid)) if dp is None: @@ -97,10 +544,70 @@ def getFlows(self, dpid): else: LOG.debug('Unsupported OF protocol') return None + + print '----------------------flowAPI----------------------' + + flowstatsReplyAPI = {} + flowstatsReplyAPI["controller"] = controllerName + + for key in flows: + temp = "%016x" %int(key) + temp = map(str, temp) + for i in range(2, 23, 3): + temp.insert(i, ':') + flowstatsReplyAPI["dpid"] = ''.join(temp) + + flowstatsReplyAPI["flows"] = [] + i = 0 + for inflow in flows[key]: + if inflow["priority"] == 1: + flowstatsReplyAPI["flows"].append({}) + flowstatsReplyAPI["flows"][i]["ingressPort"] = str(inflow['match']['in_port']) if 'in_port' in inflow['match'] else "0" + flowstatsReplyAPI["flows"][i]["dstMac"] = inflow['match']['dl_dst'] if 'dl_dst' in inflow['match'] else "0" + flowstatsReplyAPI["flows"][i]["srcMac"] = inflow['match']['dl_src'] if 'dl_src' in inflow['match'] else "0" + flowstatsReplyAPI["flows"][i]["dstIP"] = inflow['match']['nw_dst'] if 'nw_dst' in inflow['match'] else "0.0.0.0" + flowstatsReplyAPI["flows"][i]["dstIPMask"] = "0" # not support in ryu + flowstatsReplyAPI["flows"][i]["srcIP"] = inflow['match']['nw_src'] if 'nw_src' in inflow['match'] else "0.0.0.0" + flowstatsReplyAPI["flows"][i]["srcIPMask"] = "0" # not support in ryu + flowstatsReplyAPI["flows"][i]["netProtocol"] = hex(inflow['match']['nw_proto']) if 'nw_proto' in inflow['match'] else "0" + flowstatsReplyAPI["flows"][i]["dstPort"] = str(inflow['match']['tp_dst']) if 'tp_dst' in inflow['match'] else "0" + flowstatsReplyAPI["flows"][i]["srcPort"] = str(inflow['match']['tp_src']) if 'tp_src' in inflow['match'] else "0" + flowstatsReplyAPI["flows"][i]["vlan"] = str(inflow['match']['dl_vlan']) if 'dl_vlan' in inflow['match'] else "0" + flowstatsReplyAPI["flows"][i]["vlanP"] = str(inflow['match']['dl_vlan_pcp']) if 'dl_vlan_pcp' in inflow['match'] else "0" + flowstatsReplyAPI["flows"][i]["wildcards"] = str(inflow['match']['wildcards']) if 'wildcards' in inflow['match'] else "0" + flowstatsReplyAPI["flows"][i]["tosBits"] = str(inflow['match']['nw_tos']) if 'nw_tos' in inflow['match'] else "0" + flowstatsReplyAPI["flows"][i]["counterByte"] = str(inflow['byte_count']) + flowstatsReplyAPI["flows"][i]["counterPacket"] = str(inflow['packet_count']) + flowstatsReplyAPI["flows"][i]["idleTimeout"] = str(inflow['idle_timeout']) + flowstatsReplyAPI["flows"][i]["hardTimeout"] = str(inflow['hard_timeout']) + flowstatsReplyAPI["flows"][i]["priority"] = str(inflow['priority']) + flowstatsReplyAPI["flows"][i]["duration"] = str(inflow['duration_sec']) + flowstatsReplyAPI["flows"][i]["dlType"] = hex(inflow['match']['dl_type']) if 'dl_type' in inflow['match'] else "0" + + flowstatsReplyAPI["flows"][i]["actions"] = [] + for action in inflow['actions']: + if len(action.split(':')) == 1: + act = { + "value": "0", + "type": action + } + else: + act = { + "value": action.split(':')[1], + "type": action.split(':')[0] + } + flowstatsReplyAPI["flows"][i]["actions"].append(act) + + i += 1 + + print json.dumps(flowstatsReplyAPI) + self.post_json_to_core("http://localhost:5567/publish/flow", json.dumps(flowstatsReplyAPI)) return flows - # return port information of specific dpid - def getPorts(self, dpid): + # + # polling ports + # + def port_stats_reply_handler_API(self, dpid): dp = self.dpset.get(int(dpid)) if dp is None: return None @@ -113,105 +620,46 @@ def getPorts(self, dpid): else: LOG.debug('Unsupported OF protocol') return None + + print '----------------------portAPI----------------------' + + for key in ports: + for port in ports[key]: + portstatsReplyAPI = { + 'controller': controllerName, + 'port': str(port['port_no']), + 'rxbyte': str(port['rx_bytes']), + 'rxpacket': str(port['rx_packets']), + 'txbyte': str(port['tx_bytes']), + 'txpacket': str(port['tx_packets']) + } + + temp = "%016x" %int(key) + temp = map(str, temp) + for i in range(2, 23, 3): + temp.insert(i, ':') + portstatsReplyAPI["dpid"] = ''.join(temp) + portstatsReplyAPI["capacity"] = self.port_to_feature[portstatsReplyAPI['dpid']][portstatsReplyAPI['port']] if portstatsReplyAPI['port'] in self.port_to_feature[portstatsReplyAPI['dpid']] else '0' + + print json.dumps(portstatsReplyAPI) + self.post_json_to_core("http://localhost:5567/publish/port", json.dumps(portstatsReplyAPI)) return ports - # return links in network topology - # notice: --observe-link is needed when running ryu-manager - def getLinks(self): - dpid = None - links = get_link(self.omniui, dpid) - return links - - # repack switch information - def switches(self, req, **kwargs): - result = [] - nodes = self.getNodes() - for node in nodes: - omniNode = { - 'dpid': self.colonDPID(dpid_to_str(node)), - 'flows':[], - 'ports':[] - } - # repack flow information - flows = self.getFlows(node) - for key in flows: - for flow in flows[key]: - omniFlow = { - 'ingressPort': flow['match']['in_port'] if 'in_port' in flow['match'] else 0, - 'srcMac': flow['match']['dl_src'] if 'dl_src' in flow['match'] else 0, - 'dstMac': flow['match']['dl_dst'] if 'dl_dst' in flow['match'] else 0, - 'dstIP': flow['match']['nw_dst'] if 'nw_dst' in flow['match'] else 0, - 'dstIPMask': '-', # not support in ryu - 'netProtocol': flow['match']['nw_proto'] if 'nw_proto' in flow['match'] else 0, - 'srcIP': flow['match']['nw_src'] if 'nw_src' in flow['match'] else 0, - 'srcIPMask': '-', # not support in ryu - 'dstPort': flow['match']['tp_dst'] if 'tp_dst' in flow['match'] else 0, - 'srcPort': flow['match']['tp_src'] if 'tp_src' in flow['match'] else 0, - 'vlan': flow['match']['dl_vlan'] if 'dl_vlan' in flow['match'] else 0, - 'vlanP': flow['match']['dl_vlan_pcp'] if 'dl_vlan_pcp' in flow['match'] else 0, - 'wildcards': flow['match']['wildcards'] if 'wildcards' in flow['match'] else '-', - 'tosBits': flow['match']['nw_tos'] if 'nw_tos' in flow['match'] else 0, - 'counterByte': flow['byte_count'], - 'counterPacket': flow['packet_count'], - 'idleTimeout': flow['idle_timeout'], - 'hardTimeout': flow['hard_timeout'], - 'priority': flow['priority'], - 'duration': flow['duration_sec'], - 'dlType': flow['match']['dl_type'] if 'dl_type' in flow['match'] else 0, - 'actions': [] - } - # repack action field - for action in flow['actions']: - if (len(action.split(':')) == 1): - omniAction = { - 'type': action, - } - omniFlow['actions'].append(omniAction) - else: - omniAction = { - 'type': action.split(':')[0], - 'value': action.split(':')[1] - } - omniFlow['actions'].append(omniAction) - omniNode['flows'].append(omniFlow) - # repack port information - ports = self.getPorts(node) - for key in ports: - for port in ports[key]: - omniPort = { - 'PortNumber': port['port_no'], - 'recvPackets': port['rx_packets'], - 'transmitPackets': port['tx_packets'], - 'recvBytes': port['rx_bytes'], - 'transmitBytes': port['tx_bytes'] - } - omniNode['ports'].append(omniPort) - result.append(omniNode) - body = json.dumps(result) - return Response(content_type='application/json', body=body) - - # repack link information - def links(self, req, **kwargs): - result = [] - links = self.getLinks() - for link in links: - omniLink = { - 'src-switch': self.colonDPID(link.to_dict()['src']['dpid']), - 'dst-switch': self.colonDPID(link.to_dict()['dst']['dpid']), - 'src-port': (int)(link.to_dict()['src']['port_no']), - 'dst-port': (int)(link.to_dict()['dst']['port_no']) - } - # remove bi-direction link - reverse = False - for link in result: - if (link['src-switch'] == omniLink['dst-switch'] and - link['dst-switch'] == omniLink['src-switch'] and - link['src-port'] == omniLink['dst-port'] and - link['dst-port'] == omniLink['src-port']): - reverse = True - result.append(omniLink) if reverse is False else None - body = json.dumps(result) - return Response(content_type='application/json', body=body) + +class RestController(ControllerBase): + def __init__(self, req, link, data, **config): + super(RestController, self).__init__(req, link, data, **config) + self.dpset = data['dpset'] + + def get_controller_name(self, req, **kwargs): + try: + global controllerName + controllerName = req.body + print '*****NAME: ' + controllerName + '*****' + except SyntaxError: + return Response(status=400) + + return Response(status=200) def mod_flow_entry(self, req, **kwargs): try: @@ -550,7 +998,3 @@ def to_action_v1_3(self, dp, dic): # restore Ryu-format dpid def nospaceDPID(self, dpid): return "".join(dpid) - - # repack dpid - def colonDPID(self, dpid): - return ':'.join(a+b for a,b in zip(dpid[::2], dpid[1::2])) From 2bcb1d6005c34fa5bd8738e73dc9c00ce2d91bb6 Mon Sep 17 00:00:00 2001 From: HsiaoHsuan Date: Tue, 2 Feb 2016 07:04:38 -0800 Subject: [PATCH 02/31] Register event for busylink detection --- core/src/floodlight_modules/nwinfo.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/core/src/floodlight_modules/nwinfo.py b/core/src/floodlight_modules/nwinfo.py index db9feb0..400f491 100644 --- a/core/src/floodlight_modules/nwinfo.py +++ b/core/src/floodlight_modules/nwinfo.py @@ -48,6 +48,10 @@ def __registration(self, core): core.registerSSEHandler('delhost', self.delhostHandler) core.registerSSEHandler('port', self.portHandler) core.registerSSEHandler('flow', self.flowHandler) + + # Send link and port info for busylink detection + core.registerEvent('linkbag', self.sendLink, 5) + core.registerEvent('portbag', self.sendPort, 5) # RESTful API for WebUI core.registerRestApi('port', self.getPortCounter) @@ -57,6 +61,12 @@ def __registration(self, core): logger.info('Handlers and RESTful APIs registered') + def sendLink(self): + return self.links + + def sendPort(self): + return self.portstats + def __trigger(self, controller_ip, controller_port, adapter_port, controller_name): """Trigger controller adapter to send events to us From f8b752b4e1f2f125bcd69e0c3ec6ac785767e294 Mon Sep 17 00:00:00 2001 From: HsiaoHsuan Date: Tue, 2 Feb 2016 07:31:00 -0800 Subject: [PATCH 03/31] Revise busylink detection --- .../src/floodlight_modules/busylink_detect.py | 186 +++++++----------- 1 file changed, 68 insertions(+), 118 deletions(-) diff --git a/core/src/floodlight_modules/busylink_detect.py b/core/src/floodlight_modules/busylink_detect.py index 533688f..82b5311 100644 --- a/core/src/floodlight_modules/busylink_detect.py +++ b/core/src/floodlight_modules/busylink_detect.py @@ -6,55 +6,69 @@ class BusyLink_Detect: def __init__(self,core,parm): """ BusyLinkDetect init""" - self.controllerIP = "localhost" - self.controllerPort = "8080" - self.timerInterval = 5 - self.coreIP = "localhost" self.corePort = "5567" self.baseState = 1 self.finalState = 3 - self.threshold = 0.8 + self.threshold = 0.000001 self.statistics = {} self.capacity = {} self.links = {} - self.switches= {} + self.switches = {} self.BLD_result = [] - #load config - if(parm): - if(parm.has_key("ip")): - self.controllerIP = parm["ip"] - if(parm.has_key("port")): - self.controllerPort = parm["port"] - if(parm.has_key("interval")): - self.timerInterval = int(parm["interval"]) - logger.debug('IP =%s port = %s interval = %s' % (self.controllerIP,self.controllerPort,self.timerInterval)) - core.registerEvent("periodicQuery",self.periodicQuery,self.timerInterval) - core.registerEventHandler("periodicQuery", self.busyLinkDetect) - core.registerSSEHandler("busylink", self.busylinkHandler) + core.registerEventHandler("linkbag", self.getLink) + core.registerEventHandler("portbag", self.getPort) + + def getLink(self, linksbag): + #print '----------links----------' + #print json.dumps(linksbag.values()) + + try: + data = linksbag.values() + self.links = {} + for link in data: + tmp = {} + tmp['source'] = link[0]['dpid'] + tmp['target'] = link[1]['dpid'] + tmp['sourcePort'] = int(link[0]['port']) + tmp['targetPort'] = int(link[1]['port']) + id = "dpid %s, port %d -- dpid %s, port %d" % (tmp['source'], tmp['sourcePort'], tmp['target'], tmp['targetPort']) + self.links[id] = tmp + except Exception, e: + logger.error("json parse error for links: "+str(e)) + + def getPort(self, portsbag): + #print '----------ports----------' + #print json.dumps(portsbag.values()) - def busylinkHandler(self): - data = {} - for i in range(len(self.BLD_result)): - data[i] = self.BLD_result[i] - return json.dumps(data) + try: + data = portsbag.values() + self.capacity = {} + self.switches = {} + for port in data: + self.capacity["%s_%d" % (port['dpid'],int(port['port']))] = self.parsePortFeatures(int(port['capacity'])) + if port['dpid'] in self.switches: + self.switches[port['dpid']][int(port['port'])] = int(port['rxbyte']) + else: + self.switches[port['dpid']] = {} + self.switches[port['dpid']][int(port['port'])] = int(port['rxbyte']) + except Exception, e: + logger.error("json parse error for switch and features: "+str(e)) + + self.busyLinkDetect() - def overthreshold(self,link,id): - link['state'] += 1 - if link['state'] >= self.finalState: - link['state'] = self.finalState + def overthreshold(self,id): + self.statistics[id]['state'] += 1 + if self.statistics[id]['state'] >= self.finalState: + self.statistics[id]['state'] = self.finalState self.BLD_result.append(id) - def underthreshold(self,link): - link['state'] -= 1 - if link['state'] < self.baseState: - link['state'] = self.baseState - - def periodicQuery(self): - self.periodicQueryLink() - self.periodicQueryPort() + def underthreshold(self,id): + self.statistics[id]['state'] -= 1 + if self.statistics[id]['state'] < self.baseState: + self.statistics[id]['state'] = self.baseState def parsePortFeatures(self,features): if features == 0: @@ -73,102 +87,32 @@ def parsePortFeatures(self,features): if turn_binary[10] == '1' or turn_binary[11] == '1': return 10*(1024**2)/8.0 #10Mb return 0 - - def queryLinkCapacity(self): - try: - conn = httplib.HTTPConnection(self.controllerIP, int(self.controllerPort)) - conn.request("GET", "/wm/core/switch/all/features/json") - response = conn.getresponse().read() - except Exception, e: - logger.error("connection error for inquiring features: "+str(e)) - return - finally: - conn.close() - try: - data = json.loads(response) - self.capacity = {} - for switch_id in data: - switch = data[switch_id] - ports = switch['ports'] - for port in ports: - result = self.parsePortFeatures(port['currentFeatures']) - self.capacity["%s_%d" % (switch_id,port['portNumber'])] = result - except Exception, e: - logger.error("json parse error for features: "+str(e)) - - def periodicQueryLink(self): - try: - conn = httplib.HTTPConnection(self.controllerIP, int(self.controllerPort)) - conn.request("GET", "/wm/omniui/link/json") - response = conn.getresponse().read() - except Exception, e: - logger.error("connection error for inquiring links: "+str(e)) - return - finally: - conn.close() - try: - data = json.loads(response) - self.links = {} - for link in data: - tmp = {} - tmp['source'] = link['src-switch'] - tmp['target'] = link['dst-switch'] - tmp['sourcePort'] = link['src-port'] - tmp['targetPort'] = link['dst-port'] - id = "dpid %s, port %s -- dpid %s, port %s" % (tmp['source'],tmp['sourcePort'],tmp['target'],tmp['targetPort']) - self.links[id] = tmp - except Exception, e: - logger.error("json parse error for links: "+str(e)) - self.queryLinkCapacity() - - def periodicQueryPort(self): - try: - conn = httplib.HTTPConnection(self.controllerIP, int(self.controllerPort)) - conn.request("GET", "/wm/omniui/switch/json") - response = conn.getresponse().read() - except Exception, e: - logger.error("connection error for inquiring switches: "+str(e)) - return - finally: - conn.close() - try: - data = json.loads(response) - self.switches= {} - for switch in data: - tmp = {} - for port in switch['ports']: - tmp[int(port['port'])] = int(port['rxbyte']) - self.switches[switch['dpid']] = tmp - except Exception, e: - logger.error("json parse error for switch: "+str(e)) - + def busyLinkDetect(self,event): self.BLD_result = [] #calculate link's countBytes and capacity for link_id in self.links: - link = self.links[link_id] - src = link['source'] - srcp = link['sourcePort'] - dest = link['target'] - destp = link['targetPort'] + src = self.links[link_id]['source'] + srcp = self.links[link_id]['sourcePort'] + dest = self.links[link_id]['target'] + destp = self.links[link_id]['targetPort'] total_bytes = self.switches[src][srcp] + self.switches[dest][destp] - link['countBytes'] = total_bytes - link['capacity'] = min(self.capacity["%s_%d" % (src,srcp)],self.capacity["%s_%d" % (dest,destp)]) + self.links[link_id]['countBytes'] = total_bytes + self.links[link_id]['capacity'] = min(self.capacity["%s_%d" % (src,srcp)],self.capacity["%s_%d" % (dest,destp)]) #initialize self.statistics value if len(self.statistics) == 0: self.statistics = dict(self.links) for link_id in self.statistics: - link = self.statistics[link_id] - link['state'] = self.baseState + self.statistics[link_id]['state'] = self.baseState #check threshold for link_id in self.links: if link_id in self.statistics: if (self.links[link_id]['countBytes'] - self.statistics[link_id]['countBytes']) / self.statistics[link_id]['capacity'] >= self.threshold: - self.overthreshold(self.statistics[link_id],link_id) + self.overthreshold(link_id) else: - self.underthreshold(self.statistics[link_id]) + self.underthreshold(link_id) self.statistics[link_id]['countBytes'] = self.links[link_id]['countBytes'] else: self.statistics[link_id] = dict(self.links[link_id]) @@ -179,6 +123,12 @@ def busyLinkDetect(self,event): del self.statistics[link_id] #return result - if len(self.BLD_result)>0: - conn = httplib.HTTPConnection(self.coreIP,self.corePort) - conn.request('POST','/publish/busylink') + if len(self.BLD_result) > 0: + data = {} + for i in range(len(self.BLD_result)): + data[i] = self.BLD_result[i] + + print '*****Busy Link ID*****' + print json.dumps(data) + #conn = httplib.HTTPConnection(self.coreIP,self.corePort) + #conn.request('POST','/publish/busylink') From f130ced8de3a789750e5609e57103d818561b343 Mon Sep 17 00:00:00 2001 From: HsiaoHsuan Date: Tue, 2 Feb 2016 08:00:31 -0800 Subject: [PATCH 04/31] Update event and switches for ryu adapter --- adapter/ryu/event.py | 178 +++++++ adapter/ryu/switches.py | 1096 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 1274 insertions(+) create mode 100644 adapter/ryu/event.py create mode 100644 adapter/ryu/switches.py diff --git a/adapter/ryu/event.py b/adapter/ryu/event.py new file mode 100644 index 0000000..cc6bb30 --- /dev/null +++ b/adapter/ryu/event.py @@ -0,0 +1,178 @@ +# Copyright (C) 2013 Nippon Telegraph and Telephone Corporation. +# +# 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. + +import logging +from ryu.controller import handler +from ryu.controller import event + +LOG = logging.getLogger(__name__) + + +class EventSwitchBase(event.EventBase): + def __init__(self, switch): + super(EventSwitchBase, self).__init__() + self.switch = switch + + def __str__(self): + return '%s' % \ + (self.__class__.__name__, + self.switch.dp.id, len(self.switch.ports)) + + +class EventSwitchEnter(EventSwitchBase): + def __init__(self, switch): + super(EventSwitchEnter, self).__init__(switch) + + +class EventSwitchLeave(EventSwitchBase): + def __init__(self, switch): + super(EventSwitchLeave, self).__init__(switch) + + +class EventSwitchReconnected(EventSwitchBase): + def __init__(self, switch): + super(EventSwitchReconnected, self).__init__(switch) + + +class EventPortBase(event.EventBase): + def __init__(self, port): + super(EventPortBase, self).__init__() + self.port = port + + def __str__(self): + return '%s<%s>' % (self.__class__.__name__, self.port) + + +class EventPortAdd(EventPortBase): + def __init__(self, port): + super(EventPortAdd, self).__init__(port) + + +class EventPortDelete(EventPortBase): + def __init__(self, port): + super(EventPortDelete, self).__init__(port) + + +class EventPortModify(EventPortBase): + def __init__(self, port): + super(EventPortModify, self).__init__(port) + + +class EventSwitchRequest(event.EventRequestBase): + # If dpid is None, reply all list + def __init__(self, dpid=None): + super(EventSwitchRequest, self).__init__() + self.dst = 'switches' + self.dpid = dpid + + def __str__(self): + return 'EventSwitchRequest' % \ + (self.src, self.dpid) + + +class EventSwitchReply(event.EventReplyBase): + def __init__(self, dst, switches): + super(EventSwitchReply, self).__init__(dst) + self.switches = switches + + def __str__(self): + return 'EventSwitchReply' % \ + (self.dst, self.switches) + + +class EventLinkBase(event.EventBase): + def __init__(self, link): + super(EventLinkBase, self).__init__() + self.link = link + + def __str__(self): + return '%s<%s>' % (self.__class__.__name__, self.link) + + +class EventLinkAdd(EventLinkBase): + def __init__(self, link): + super(EventLinkAdd, self).__init__(link) + + +class EventLinkDelete(EventLinkBase): + def __init__(self, link): + super(EventLinkDelete, self).__init__(link) + + +class EventLinkRequest(event.EventRequestBase): + # If dpid is None, reply all list + def __init__(self, dpid=None): + super(EventLinkRequest, self).__init__() + self.dst = 'switches' + self.dpid = dpid + + def __str__(self): + return 'EventLinkRequest' % \ + (self.src, self.dpid) + + +class EventLinkReply(event.EventReplyBase): + def __init__(self, dst, dpid, links): + super(EventLinkReply, self).__init__(dst) + self.dpid = dpid + self.links = links + + def __str__(self): + return 'EventLinkReply' % \ + (self.dst, self.dpid, len(self.links)) + + +class EventHostRequest(event.EventRequestBase): + # if dpid is None, replay all hosts + def __init__(self, dpid=None): + super(EventHostRequest, self).__init__() + self.dst = 'switches' + self.dpid = dpid + + def __str__(self): + return 'EventHostRequest' % \ + (self.src, self.dpid) + + +class EventHostReply(event.EventReplyBase): + def __init__(self, dst, dpid, hosts): + super(EventHostReply, self).__init__(dst) + self.dpid = dpid + self.hosts = hosts + + def __str__(self): + return 'EventHostReply' % \ + (self.dst, self.dpid, len(self.hosts)) + + +class EventHostBase(event.EventBase): + def __init__(self, host): + super(EventHostBase, self).__init__() + self.host = host + + def __str__(self): + return '%s<%s>' % (self.__class__.__name__, self.host) + + +class EventHostAdd(EventHostBase): + def __init__(self, host): + super(EventHostAdd, self).__init__(host) + + +class EventHostDelete(EventHostBase): + def __init__(self, host): + super(EventHostDelete, self).__init__(host) + +handler.register_service('ryu.topology.switches') \ No newline at end of file diff --git a/adapter/ryu/switches.py b/adapter/ryu/switches.py new file mode 100644 index 0000000..0497506 --- /dev/null +++ b/adapter/ryu/switches.py @@ -0,0 +1,1096 @@ +# Copyright (C) 2013 Nippon Telegraph and Telephone Corporation. +# +# 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. + +import logging +import six +import struct +import time +import json +from ryu import cfg + +from ryu.topology import event +from ryu.base import app_manager +from ryu.controller import ofp_event +from ryu.controller.handler import set_ev_cls +from ryu.controller.handler import MAIN_DISPATCHER, DEAD_DISPATCHER +from ryu.exception import RyuException +from ryu.lib import addrconv, hub +from ryu.lib.mac import DONTCARE_STR +from ryu.lib.dpid import dpid_to_str, str_to_dpid +from ryu.lib.port_no import port_no_to_str +from ryu.lib.packet import packet, ethernet +from ryu.lib.packet import lldp, ether_types +from ryu.lib.packet import arp, ipv4, ipv6, vlan +from ryu.ofproto.ether import ETH_TYPE_LLDP +from ryu.ofproto import nx_match +from ryu.ofproto import ofproto_v1_0 +from ryu.ofproto import ofproto_v1_2 +from ryu.ofproto import ofproto_v1_3 +from ryu.ofproto import ofproto_v1_4 + + +LOG = logging.getLogger(__name__) + + +CONF = cfg.CONF + +CONF.register_cli_opts([ + cfg.BoolOpt('observe-links', default=False, + help='observe link discovery events.'), + cfg.BoolOpt('install-lldp-flow', default=True, + help='link discovery: explicitly install flow entry ' + 'to send lldp packet to controller'), + cfg.BoolOpt('explicit-drop', default=True, + help='link discovery: explicitly drop lldp packet in') +]) + + +class Port(object): + # This is data class passed by EventPortXXX + def __init__(self, dpid, ofproto, ofpport): + super(Port, self).__init__() + + self.dpid = dpid + self._ofproto = ofproto + self._config = ofpport.config + self._state = ofpport.state + + self.port_no = ofpport.port_no + self.hw_addr = ofpport.hw_addr + self.name = ofpport.name + self.currentFeatures = ofpport.curr + + def is_reserved(self): + return self.port_no > self._ofproto.OFPP_MAX + + def is_down(self): + return (self._state & self._ofproto.OFPPS_LINK_DOWN) > 0 \ + or (self._config & self._ofproto.OFPPC_PORT_DOWN) > 0 + + def is_live(self): + # NOTE: OF1.2 has OFPPS_LIVE state + # return (self._state & self._ofproto.OFPPS_LIVE) > 0 + return not self.is_down() + + def to_dict(self): + return {'dpid': dpid_to_str(self.dpid), + 'port_no': port_no_to_str(self.port_no), + 'hw_addr': self.hw_addr, + 'name': self.name.rstrip('\0')} + + # for Switch.del_port() + def __eq__(self, other): + return self.dpid == other.dpid and self.port_no == other.port_no + + def __ne__(self, other): + return not self.__eq__(other) + + def __hash__(self): + return hash((self.dpid, self.port_no)) + + def __str__(self): + LIVE_MSG = {False: 'DOWN', True: 'LIVE'} + return 'Port' % \ + (self.dpid, self.port_no, LIVE_MSG[self.is_live()]) + + +class Switch(object): + # This is data class passed by EventSwitchXXX + def __init__(self, dp): + super(Switch, self).__init__() + + self.dp = dp + self.ports = [] + + def add_port(self, ofpport): + port = Port(self.dp.id, self.dp.ofproto, ofpport) + if not port.is_reserved(): + self.ports.append(port) + + def del_port(self, ofpport): + self.ports.remove(Port(ofpport)) + + def to_dict(self): + d = {'dpid': dpid_to_str(self.dp.id), + 'ports': [port.to_dict() for port in self.ports]} + return d + + def __str__(self): + msg = 'Switch Host class + def __init__(self): + super(HostState, self).__init__() + + def add(self, host): + mac = host.mac + self.setdefault(mac, host) + + def update_vlan(self, host, vlan=None): + mac = host.mac + host = None + if mac in self: + host = self[mac] + + if not host: + return + + if vlan != None and vlan not in host.vlan: + host.vlan.append(vlan) + return 'new' + elif vlan != None: + if host.vlan.index(vlan) == (len(host.vlan)-1): + return 'old' + else: + host.vlan.remove(vlan) + host.vlan.append(vlan) + return 'new' + + def update_ip(self, host, ip_v4=None, ip_v6=None): + mac = host.mac + host = None + if mac in self: + host = self[mac] + + if not host: + return + + if ip_v4 != None and ip_v4 not in host.ipv4: + host.ipv4.append(ip_v4) + return 'new' + elif ip_v4 != None: + if host.ipv4.index(ip_v4) == (len(host.ipv4)-1): + return 'old' + else: + host.ipv4.remove(ip_v4) + host.ipv4.append(ip_v4) + return 'new' + + if ip_v6 != None and ip_v6 not in host.ipv6: + host.ipv6.append(ip_v6) + return 'new' + elif ip_v6 != None: + if host.ipv6.index(ip_v6) == (len(host.ipv6)-1): + return 'old' + else: + host.ipv6.remove(ip_v6) + host.ipv6.append(ip_v6) + return 'new' + + def get_by_dpid(self, dpid): + result = [] + + for mac in self: + host = self[mac] + if host.port.dpid == dpid: + result.append(host) + + return result + + +class PortState(dict): + # dict: int port_no -> OFPPort port + # OFPPort is defined in ryu.ofproto.ofproto_v1_X_parser + def __init__(self): + super(PortState, self).__init__() + + def add(self, port_no, port): + self[port_no] = port + + def remove(self, port_no): + del self[port_no] + + def modify(self, port_no, port): + self[port_no] = port + + +class PortData(object): + def __init__(self, is_down, lldp_data): + super(PortData, self).__init__() + self.is_down = is_down + self.lldp_data = lldp_data + self.timestamp = None + self.sent = 0 + + def lldp_sent(self): + self.timestamp = time.time() + self.sent += 1 + + def lldp_received(self): + self.sent = 0 + + def lldp_dropped(self): + return self.sent + + def clear_timestamp(self): + self.timestamp = None + + def set_down(self, is_down): + self.is_down = is_down + + def __str__(self): + return 'PortData' \ + % (not self.is_down, self.timestamp, self.sent) + + +class PortDataState(dict): + # dict: Port class -> PortData class + # slimed down version of OrderedDict as python 2.6 doesn't support it. + _PREV = 0 + _NEXT = 1 + _KEY = 2 + + def __init__(self): + super(PortDataState, self).__init__() + self._root = root = [] # sentinel node + root[:] = [root, root, None] # [_PREV, _NEXT, _KEY] + # doubly linked list + self._map = {} + + def _remove_key(self, key): + link_prev, link_next, key = self._map.pop(key) + link_prev[self._NEXT] = link_next + link_next[self._PREV] = link_prev + + def _append_key(self, key): + root = self._root + last = root[self._PREV] + last[self._NEXT] = root[self._PREV] = self._map[key] = [last, root, + key] + + def _prepend_key(self, key): + root = self._root + first = root[self._NEXT] + first[self._PREV] = root[self._NEXT] = self._map[key] = [root, first, + key] + + def _move_last_key(self, key): + self._remove_key(key) + self._append_key(key) + + def _move_front_key(self, key): + self._remove_key(key) + self._prepend_key(key) + + def add_port(self, port, lldp_data): + if port not in self: + self._prepend_key(port) + self[port] = PortData(port.is_down(), lldp_data) + else: + self[port].is_down = port.is_down() + + def lldp_sent(self, port): + port_data = self[port] + port_data.lldp_sent() + self._move_last_key(port) + return port_data + + def lldp_received(self, port): + self[port].lldp_received() + + def move_front(self, port): + port_data = self.get(port, None) + if port_data is not None: + port_data.clear_timestamp() + self._move_front_key(port) + + def set_down(self, port): + is_down = port.is_down() + port_data = self[port] + port_data.set_down(is_down) + port_data.clear_timestamp() + if not is_down: + self._move_front_key(port) + return is_down + + def get_port(self, port): + return self[port] + + def del_port(self, port): + del self[port] + self._remove_key(port) + + def __iter__(self): + root = self._root + curr = root[self._NEXT] + while curr is not root: + yield curr[self._KEY] + curr = curr[self._NEXT] + + def clear(self): + for node in self._map.values(): + del node[:] + root = self._root + root[:] = [root, root, None] + self._map.clear() + dict.clear(self) + + def items(self): + 'od.items() -> list of (key, value) pairs in od' + return [(key, self[key]) for key in self] + + def iteritems(self): + 'od.iteritems -> an iterator over the (key, value) pairs in od' + for k in self: + yield (k, self[k]) + + +class LinkState(dict): + # dict: Link class -> timestamp + def __init__(self): + super(LinkState, self).__init__() + self._map = {} + + def get_peer(self, src): + return self._map.get(src, None) + + def update_link(self, src, dst): + link = Link(src, dst) + + self[link] = time.time() + self._map[src] = dst + + # return if the reverse link is also up or not + rev_link = Link(dst, src) + return rev_link in self + + def link_down(self, link): + del self[link] + del self._map[link.src] + + def rev_link_set_timestamp(self, rev_link, timestamp): + # rev_link may or may not in LinkSet + if rev_link in self: + self[rev_link] = timestamp + + def port_deleted(self, src): + dst = self.get_peer(src) + if dst is None: + raise KeyError() + + link = Link(src, dst) + rev_link = Link(dst, src) + del self[link] + del self._map[src] + # reverse link might not exist + self.pop(rev_link, None) + rev_link_dst = self._map.pop(dst, None) + + return dst, rev_link_dst + + +class LLDPPacket(object): + # make a LLDP packet for link discovery. + + CHASSIS_ID_PREFIX = 'dpid:' + CHASSIS_ID_PREFIX_LEN = len(CHASSIS_ID_PREFIX) + CHASSIS_ID_FMT = CHASSIS_ID_PREFIX + '%s' + + PORT_ID_STR = '!I' # uint32_t + PORT_ID_SIZE = 4 + + class LLDPUnknownFormat(RyuException): + message = '%(msg)s' + + @staticmethod + def lldp_packet(dpid, port_no, dl_addr, ttl): + pkt = packet.Packet() + + dst = lldp.LLDP_MAC_NEAREST_BRIDGE + src = dl_addr + ethertype = ETH_TYPE_LLDP + eth_pkt = ethernet.ethernet(dst, src, ethertype) + pkt.add_protocol(eth_pkt) + + tlv_chassis_id = lldp.ChassisID( + subtype=lldp.ChassisID.SUB_LOCALLY_ASSIGNED, + chassis_id=(LLDPPacket.CHASSIS_ID_FMT % + dpid_to_str(dpid)).encode('ascii')) + + tlv_port_id = lldp.PortID(subtype=lldp.PortID.SUB_PORT_COMPONENT, + port_id=struct.pack( + LLDPPacket.PORT_ID_STR, + port_no)) + + tlv_ttl = lldp.TTL(ttl=ttl) + tlv_end = lldp.End() + + tlvs = (tlv_chassis_id, tlv_port_id, tlv_ttl, tlv_end) + lldp_pkt = lldp.lldp(tlvs) + pkt.add_protocol(lldp_pkt) + + pkt.serialize() + return pkt.data + + @staticmethod + def lldp_parse(data): + pkt = packet.Packet(data) + i = iter(pkt) + eth_pkt = six.next(i) + assert type(eth_pkt) == ethernet.ethernet + + lldp_pkt = six.next(i) + if type(lldp_pkt) != lldp.lldp: + raise LLDPPacket.LLDPUnknownFormat() + + tlv_chassis_id = lldp_pkt.tlvs[0] + if tlv_chassis_id.subtype != lldp.ChassisID.SUB_LOCALLY_ASSIGNED: + raise LLDPPacket.LLDPUnknownFormat( + msg='unknown chassis id subtype %d' % tlv_chassis_id.subtype) + chassis_id = tlv_chassis_id.chassis_id + if not chassis_id.startswith(LLDPPacket.CHASSIS_ID_PREFIX): + raise LLDPPacket.LLDPUnknownFormat( + msg='unknown chassis id format %s' % chassis_id) + src_dpid = str_to_dpid(chassis_id[LLDPPacket.CHASSIS_ID_PREFIX_LEN:]) + + tlv_port_id = lldp_pkt.tlvs[1] + if tlv_port_id.subtype != lldp.PortID.SUB_PORT_COMPONENT: + raise LLDPPacket.LLDPUnknownFormat( + msg='unknown port id subtype %d' % tlv_port_id.subtype) + port_id = tlv_port_id.port_id + if len(port_id) != LLDPPacket.PORT_ID_SIZE: + raise LLDPPacket.LLDPUnknownFormat( + msg='unknown port id %d' % port_id) + (src_port_no, ) = struct.unpack(LLDPPacket.PORT_ID_STR, port_id) + + return src_dpid, src_port_no + + +class Switches(app_manager.RyuApp): + OFP_VERSIONS = [ofproto_v1_0.OFP_VERSION, ofproto_v1_2.OFP_VERSION, + ofproto_v1_3.OFP_VERSION, ofproto_v1_4.OFP_VERSION] + _EVENTS = [event.EventSwitchEnter, event.EventSwitchLeave, + event.EventSwitchReconnected, + event.EventPortAdd, event.EventPortDelete, + event.EventPortModify, + event.EventLinkAdd, event.EventLinkDelete, + event.EventHostAdd] + + DEFAULT_TTL = 120 # unused. ignored. + LLDP_PACKET_LEN = len(LLDPPacket.lldp_packet(0, 0, DONTCARE_STR, 0)) + + LLDP_SEND_GUARD = .05 + LLDP_SEND_PERIOD_PER_PORT = .9 + TIMEOUT_CHECK_PERIOD = 5. + LINK_TIMEOUT = TIMEOUT_CHECK_PERIOD * 2 + LINK_LLDP_DROP = 5 + + def __init__(self, *args, **kwargs): + super(Switches, self).__init__(*args, **kwargs) + + self.name = 'switches' + self.dps = {} # datapath_id => Datapath class + self.port_state = {} # datapath_id => ports + self.ports = PortDataState() # Port class -> PortData class + self.links = LinkState() # Link class -> timestamp + self.hosts = HostState() # mac address -> Host class list + self.is_active = True + + self.link_discovery = self.CONF.observe_links + if self.link_discovery: + self.install_flow = self.CONF.install_lldp_flow + self.explicit_drop = self.CONF.explicit_drop + self.lldp_event = hub.Event() + self.link_event = hub.Event() + self.threads.append(hub.spawn(self.lldp_loop)) + self.threads.append(hub.spawn(self.link_loop)) + + def close(self): + self.is_active = False + if self.link_discovery: + self.lldp_event.set() + self.link_event.set() + hub.joinall(self.threads) + + def _register(self, dp): + assert dp.id is not None + + self.dps[dp.id] = dp + if dp.id not in self.port_state: + self.port_state[dp.id] = PortState() + for port in dp.ports.values(): + self.port_state[dp.id].add(port.port_no, port) + + def _unregister(self, dp): + if dp.id in self.dps: + if (self.dps[dp.id] == dp): + del self.dps[dp.id] + del self.port_state[dp.id] + + def _get_switch(self, dpid): + if dpid in self.dps: + switch = Switch(self.dps[dpid]) + for ofpport in self.port_state[dpid].values(): + switch.add_port(ofpport) + return switch + + def _get_port(self, dpid, port_no): + switch = self._get_switch(dpid) + if switch: + for p in switch.ports: + if p.port_no == port_no: + return p + + def _port_added(self, port): + lldp_data = LLDPPacket.lldp_packet( + port.dpid, port.port_no, port.hw_addr, self.DEFAULT_TTL) + self.ports.add_port(port, lldp_data) + # LOG.debug('_port_added dpid=%s, port_no=%s, live=%s', + # port.dpid, port.port_no, port.is_live()) + + def _link_down(self, port): + try: + dst, rev_link_dst = self.links.port_deleted(port) + except KeyError: + # LOG.debug('key error. src=%s, dst=%s', + # port, self.links.get_peer(port)) + return + link = Link(port, dst) + self.send_event_to_observers(event.EventLinkDelete(link)) + if rev_link_dst: + rev_link = Link(dst, rev_link_dst) + self.send_event_to_observers(event.EventLinkDelete(rev_link)) + self.ports.move_front(dst) + + def _is_edge_port(self, port): + for link in self.links: + if port == link.src or port == link.dst: + return False + + return True + + @set_ev_cls(ofp_event.EventOFPStateChange, + [MAIN_DISPATCHER, DEAD_DISPATCHER]) + def state_change_handler(self, ev): + dp = ev.datapath + assert dp is not None + LOG.debug(dp) + + if ev.state == MAIN_DISPATCHER: + dp_multiple_conns = False + if dp.id in self.dps: + LOG.warning('Multiple connections from %s', dpid_to_str(dp.id)) + dp_multiple_conns = True + (self.dps[dp.id]).close() + + self._register(dp) + switch = self._get_switch(dp.id) + LOG.debug('register %s', switch) + + if not dp_multiple_conns: + self.send_event_to_observers(event.EventSwitchEnter(switch)) + else: + self.send_event_to_observers(event.EventSwitchReconnected(switch)) + + if not self.link_discovery: + return + + if self.install_flow: + ofproto = dp.ofproto + ofproto_parser = dp.ofproto_parser + + # TODO:XXX need other versions + if ofproto.OFP_VERSION == ofproto_v1_0.OFP_VERSION: + rule = nx_match.ClsRule() + rule.set_dl_dst(addrconv.mac.text_to_bin( + lldp.LLDP_MAC_NEAREST_BRIDGE)) + rule.set_dl_type(ETH_TYPE_LLDP) + actions = [ofproto_parser.OFPActionOutput( + ofproto.OFPP_CONTROLLER, self.LLDP_PACKET_LEN)] + dp.send_flow_mod( + rule=rule, cookie=0, command=ofproto.OFPFC_ADD, + idle_timeout=0, hard_timeout=0, actions=actions, + priority=0xFFFF) + elif ofproto.OFP_VERSION >= ofproto_v1_2.OFP_VERSION: + match = ofproto_parser.OFPMatch( + eth_type=ETH_TYPE_LLDP, + eth_dst=lldp.LLDP_MAC_NEAREST_BRIDGE) + # OFPCML_NO_BUFFER is set so that the LLDP is not + # buffered on switch + parser = ofproto_parser + actions = [parser.OFPActionOutput(ofproto.OFPP_CONTROLLER, + ofproto.OFPCML_NO_BUFFER + )] + inst = [parser.OFPInstructionActions( + ofproto.OFPIT_APPLY_ACTIONS, actions)] + mod = parser.OFPFlowMod(datapath=dp, match=match, + idle_timeout=0, hard_timeout=0, + instructions=inst, + priority=0xFFFF) + dp.send_msg(mod) + else: + LOG.error('cannot install flow. unsupported version. %x', + dp.ofproto.OFP_VERSION) + + # Do not add ports while dp has multiple connections to controller. + if not dp_multiple_conns: + for port in switch.ports: + if not port.is_reserved(): + self._port_added(port) + + self.lldp_event.set() + + elif ev.state == DEAD_DISPATCHER: + # dp.id is None when datapath dies before handshake + if dp.id is None: + return + + # if switch delete + # then delete host on it + hostlist = self.hosts.get_by_dpid(dp.id) + for host in hostlist: + ev = event.EventHostDelete(host) + self.send_event_to_observers(ev) + del self.hosts[host.mac] + + switch = self._get_switch(dp.id) + if switch: + if switch.dp is dp: + self._unregister(dp) + LOG.debug('unregister %s', switch) + + self.send_event_to_observers(event.EventSwitchLeave(switch)) + + if not self.link_discovery: + return + + for port in switch.ports: + if not port.is_reserved(): + self.ports.del_port(port) + self._link_down(port) + self.lldp_event.set() + + @set_ev_cls(ofp_event.EventOFPPortStatus, MAIN_DISPATCHER) + def port_status_handler(self, ev): + msg = ev.msg + reason = msg.reason + dp = msg.datapath + ofpport = msg.desc + + if reason == dp.ofproto.OFPPR_ADD: + # LOG.debug('A port was added.' + + # '(datapath id = %s, port number = %s)', + # dp.id, ofpport.port_no) + self.port_state[dp.id].add(ofpport.port_no, ofpport) + self.send_event_to_observers( + event.EventPortAdd(Port(dp.id, dp.ofproto, ofpport))) + + if not self.link_discovery: + return + + port = self._get_port(dp.id, ofpport.port_no) + if port and not port.is_reserved(): + self._port_added(port) + self.lldp_event.set() + + elif reason == dp.ofproto.OFPPR_DELETE: + # LOG.debug('A port was deleted.' + + # '(datapath id = %s, port number = %s)', + # dp.id, ofpport.port_no) + self.port_state[dp.id].remove(ofpport.port_no) + self.send_event_to_observers( + event.EventPortDelete(Port(dp.id, dp.ofproto, ofpport))) + + if not self.link_discovery: + return + + port = self._get_port(dp.id, ofpport.port_no) + if port and not port.is_reserved(): + # if port delete + # then delete host on it + for host in self.hosts.values(): + if port.__eq__(host.port): + ev = event.EventHostDelete(host) + self.send_event_to_observers(ev) + del self.hosts[host.mac] + break + + self.ports.del_port(port) + self._link_down(port) + self.lldp_event.set() + + else: + assert reason == dp.ofproto.OFPPR_MODIFY + # LOG.debug('A port was modified.' + + # '(datapath id = %s, port number = %s)', + # dp.id, ofpport.port_no) + self.port_state[dp.id].modify(ofpport.port_no, ofpport) + self.send_event_to_observers( + event.EventPortModify(Port(dp.id, dp.ofproto, ofpport))) + + if not self.link_discovery: + return + + port = self._get_port(dp.id, ofpport.port_no) + if port and not port.is_reserved(): + if self.ports.set_down(port): + # if port down + # then delete host on it + for host in self.hosts.values(): + if port.__eq__(host.port): + ev = event.EventHostDelete(host) + self.send_event_to_observers(ev) + del self.hosts[host.mac] + break + + self._link_down(port) + self.lldp_event.set() + + @staticmethod + def _drop_packet(msg): + buffer_id = msg.buffer_id + if buffer_id == msg.datapath.ofproto.OFP_NO_BUFFER: + return + + dp = msg.datapath + # TODO:XXX + if dp.ofproto.OFP_VERSION == ofproto_v1_0.OFP_VERSION: + dp.send_packet_out(buffer_id, msg.in_port, []) + elif dp.ofproto.OFP_VERSION >= ofproto_v1_2.OFP_VERSION: + dp.send_packet_out(buffer_id, msg.match['in_port'], []) + else: + LOG.error('cannot drop_packet. unsupported version. %x', + dp.ofproto.OFP_VERSION) + + @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER) + def lldp_packet_in_handler(self, ev): + if not self.link_discovery: + return + + msg = ev.msg + try: + src_dpid, src_port_no = LLDPPacket.lldp_parse(msg.data) + except LLDPPacket.LLDPUnknownFormat as e: + # This handler can receive all the packets which can be + # not-LLDP packet. Ignore it silently + return + + dst_dpid = msg.datapath.id + if msg.datapath.ofproto.OFP_VERSION == ofproto_v1_0.OFP_VERSION: + dst_port_no = msg.in_port + elif msg.datapath.ofproto.OFP_VERSION >= ofproto_v1_2.OFP_VERSION: + dst_port_no = msg.match['in_port'] + else: + LOG.error('cannot accept LLDP. unsupported version. %x', + msg.datapath.ofproto.OFP_VERSION) + + src = self._get_port(src_dpid, src_port_no) + if not src or src.dpid == dst_dpid: + return + try: + self.ports.lldp_received(src) + except KeyError: + # There are races between EventOFPPacketIn and + # EventDPPortAdd. So packet-in event can happend before + # port add event. In that case key error can happend. + # LOG.debug('lldp_received: KeyError %s', e) + pass + + dst = self._get_port(dst_dpid, dst_port_no) + if not dst: + return + + old_peer = self.links.get_peer(src) + # LOG.debug("Packet-In") + # LOG.debug(" src=%s", src) + # LOG.debug(" dst=%s", dst) + # LOG.debug(" old_peer=%s", old_peer) + if old_peer and old_peer != dst: + old_link = Link(src, old_peer) + del self.links[old_link] + self.send_event_to_observers(event.EventLinkDelete(old_link)) + + link = Link(src, dst) + if link not in self.links: + self.send_event_to_observers(event.EventLinkAdd(link)) + + # remove hosts from edge port + for host in self.hosts.values(): + if not self._is_edge_port(host.port): + del self.hosts[host.mac] + + if not self.links.update_link(src, dst): + # reverse link is not detected yet. + # So schedule the check early because it's very likely it's up + self.ports.move_front(dst) + self.lldp_event.set() + if self.explicit_drop: + self._drop_packet(msg) + + @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER) + def host_discovery_packet_in_handler(self, ev): + msg = ev.msg + pkt = packet.Packet(msg.data) + eth = pkt.get_protocols(ethernet.ethernet)[0] + + # ignore lldp packet + if eth.ethertype == ETH_TYPE_LLDP: + return + + datapath = msg.datapath + dpid = datapath.id + port_no = -1 + + if msg.datapath.ofproto.OFP_VERSION == ofproto_v1_0.OFP_VERSION: + port_no = msg.in_port + else: + port_no = msg.match['in_port'] + + port = self._get_port(dpid, port_no) + + # can't find this port(ex: logic port) + if not port: + return + # ignore switch-to-switch port + if not self._is_edge_port(port): + return + + host_mac = eth.src + host = Host(host_mac, port) + + if host_mac not in self.hosts: + self.hosts.add(host) + ev = event.EventHostAdd(host) + self.send_event_to_observers(ev) + + # vlan packet, update vlan tag + vlan_pkt = pkt.get_protocol(vlan.vlan) + if vlan_pkt: + ans = self.hosts.update_vlan(host, vlan=vlan_pkt.vid) + if ans == 'new': + ev = event.EventHostAdd(host) + self.send_event_to_observers(ev) + + # arp packet, update ip address + arp_pkt = pkt.get_protocol(arp.arp) + if arp_pkt: + ans = self.hosts.update_ip(host, ip_v4=arp_pkt.src_ip) + if ans == 'new': + ev = event.EventHostAdd(host) + self.send_event_to_observers(ev) + + # ipv4 packet, update ipv4 address + ipv4_pkt = pkt.get_protocol(ipv4.ipv4) + if ipv4_pkt: + ans = self.hosts.update_ip(host, ip_v4=ipv4_pkt.src) + if ans == 'new': + ev = event.EventHostAdd(host) + self.send_event_to_observers(ev) + + # ipv6 packet, update ipv6 address + ipv6_pkt = pkt.get_protocol(ipv6.ipv6) + if ipv6_pkt: + self.hosts.update_ip(host, ip_v6=ipv6_pkt.src) + + def send_lldp_packet(self, port): + try: + port_data = self.ports.lldp_sent(port) + except KeyError as e: + # ports can be modified during our sleep in self.lldp_loop() + # LOG.debug('send_lldp: KeyError %s', e) + return + if port_data.is_down: + return + + dp = self.dps.get(port.dpid, None) + if dp is None: + # datapath was already deleted + return + + # LOG.debug('lldp sent dpid=%s, port_no=%d', dp.id, port.port_no) + # TODO:XXX + if dp.ofproto.OFP_VERSION == ofproto_v1_0.OFP_VERSION: + actions = [dp.ofproto_parser.OFPActionOutput(port.port_no)] + dp.send_packet_out(actions=actions, data=port_data.lldp_data) + elif dp.ofproto.OFP_VERSION >= ofproto_v1_2.OFP_VERSION: + actions = [dp.ofproto_parser.OFPActionOutput(port.port_no)] + out = dp.ofproto_parser.OFPPacketOut( + datapath=dp, in_port=dp.ofproto.OFPP_CONTROLLER, + buffer_id=dp.ofproto.OFP_NO_BUFFER, actions=actions, + data=port_data.lldp_data) + dp.send_msg(out) + else: + LOG.error('cannot send lldp packet. unsupported version. %x', + dp.ofproto.OFP_VERSION) + + def lldp_loop(self): + while self.is_active: + self.lldp_event.clear() + + now = time.time() + timeout = None + ports_now = [] + ports = [] + for (key, data) in self.ports.items(): + if data.timestamp is None: + ports_now.append(key) + continue + + expire = data.timestamp + self.LLDP_SEND_PERIOD_PER_PORT + if expire <= now: + ports.append(key) + continue + + timeout = expire - now + break + + for port in ports_now: + self.send_lldp_packet(port) + for port in ports: + self.send_lldp_packet(port) + hub.sleep(self.LLDP_SEND_GUARD) # don't burst + + if timeout is not None and ports: + timeout = 0 # We have already slept + # LOG.debug('lldp sleep %s', timeout) + self.lldp_event.wait(timeout=timeout) + + def link_loop(self): + while self.is_active: + self.link_event.clear() + + now = time.time() + deleted = [] + for (link, timestamp) in self.links.items(): + # LOG.debug('%s timestamp %d (now %d)', link, timestamp, now) + if timestamp + self.LINK_TIMEOUT < now: + src = link.src + if src in self.ports: + port_data = self.ports.get_port(src) + # LOG.debug('port_data %s', port_data) + if port_data.lldp_dropped() > self.LINK_LLDP_DROP: + deleted.append(link) + + for link in deleted: + self.links.link_down(link) + # LOG.debug('delete %s', link) + self.send_event_to_observers(event.EventLinkDelete(link)) + + dst = link.dst + rev_link = Link(dst, link.src) + if rev_link not in deleted: + # It is very likely that the reverse link is also + # disconnected. Check it early. + expire = now - self.LINK_TIMEOUT + self.links.rev_link_set_timestamp(rev_link, expire) + if dst in self.ports: + self.ports.move_front(dst) + self.lldp_event.set() + + self.link_event.wait(timeout=self.TIMEOUT_CHECK_PERIOD) + + @set_ev_cls(event.EventSwitchRequest) + def switch_request_handler(self, req): + # LOG.debug(req) + dpid = req.dpid + + switches = [] + if dpid is None: + # reply all list + for dp in self.dps.values(): + switches.append(self._get_switch(dp.id)) + elif dpid in self.dps: + switches.append(self._get_switch(dpid)) + + rep = event.EventSwitchReply(req.src, switches) + self.reply_to_request(req, rep) + + @set_ev_cls(event.EventLinkRequest) + def link_request_handler(self, req): + # LOG.debug(req) + dpid = req.dpid + + if dpid is None: + links = self.links + else: + links = [link for link in self.links if link.src.dpid == dpid] + rep = event.EventLinkReply(req.src, dpid, links) + self.reply_to_request(req, rep) + + @set_ev_cls(event.EventHostRequest) + def host_request_handler(self, req): + dpid = req.dpid + hosts = [] + if dpid is None: + for mac in self.hosts: + hosts.append(self.hosts[mac]) + else: + hosts = self.hosts.get_by_dpid(dpid) + + rep = event.EventHostReply(req.src, dpid, hosts) + self.reply_to_request(req, rep) \ No newline at end of file From 7a504c9a5c3a6f7f905fc918e45b1a5bfb361698 Mon Sep 17 00:00:00 2001 From: hsiaohsuan1l1l Date: Wed, 3 Feb 2016 00:40:44 +0800 Subject: [PATCH 05/31] Update installation for new version adapter --- adapter/ryu/README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/adapter/ryu/README.md b/adapter/ryu/README.md index ca12acb..471c90b 100644 --- a/adapter/ryu/README.md +++ b/adapter/ryu/README.md @@ -5,7 +5,8 @@ Ryu adapter module for OmniUI ###Installation### 1. Download the Ryu Controller $ `git clone git://github.com/osrg/ryu.git` -$ `cd ryu; sudo python ./setup.py install` +$ `cd OpenADM/adapter/ryu; cp event.py event.py ~/ryu/ryu/topology; cp switches.py switches.py ~/ryu/ryu/topology` +$ `cd ~/ryu; sudo python ./setup.py install` ###Execution### **Ryu 1.0** From 904c8487576751bde255899c2a38a49af796c350 Mon Sep 17 00:00:00 2001 From: hsiaohsuan1l1l Date: Wed, 3 Feb 2016 00:45:10 +0800 Subject: [PATCH 06/31] Revise installation for readability --- adapter/ryu/README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/adapter/ryu/README.md b/adapter/ryu/README.md index 471c90b..f93fe6b 100644 --- a/adapter/ryu/README.md +++ b/adapter/ryu/README.md @@ -5,7 +5,8 @@ Ryu adapter module for OmniUI ###Installation### 1. Download the Ryu Controller $ `git clone git://github.com/osrg/ryu.git` -$ `cd OpenADM/adapter/ryu; cp event.py event.py ~/ryu/ryu/topology; cp switches.py switches.py ~/ryu/ryu/topology` +$ `cd OpenADM/adapter/ryu; cp event.py event.py ~/ryu/ryu/topology` +$ `cp switches.py switches.py ~/ryu/ryu/topology` $ `cd ~/ryu; sudo python ./setup.py install` ###Execution### From d5577d3d9118e12e31c623cdce483e9592c64056 Mon Sep 17 00:00:00 2001 From: hsiaohsuan1l1l Date: Wed, 3 Feb 2016 00:46:23 +0800 Subject: [PATCH 07/31] Revise installation for readability --- adapter/ryu/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/adapter/ryu/README.md b/adapter/ryu/README.md index f93fe6b..09c9ceb 100644 --- a/adapter/ryu/README.md +++ b/adapter/ryu/README.md @@ -5,9 +5,9 @@ Ryu adapter module for OmniUI ###Installation### 1. Download the Ryu Controller $ `git clone git://github.com/osrg/ryu.git` -$ `cd OpenADM/adapter/ryu; cp event.py event.py ~/ryu/ryu/topology` -$ `cp switches.py switches.py ~/ryu/ryu/topology` -$ `cd ~/ryu; sudo python ./setup.py install` +$ `cd OpenADM/adapter/ryu; cp event.py event.py ~/ryu/ryu/topology` +$ `cp switches.py switches.py ~/ryu/ryu/topology` +$ `cd ~/ryu; sudo python ./setup.py install` ###Execution### **Ryu 1.0** From 16765ff8bd6886f0119ed8ff034cba0abdccc5da Mon Sep 17 00:00:00 2001 From: HsiaoHsuan Date: Tue, 2 Feb 2016 09:04:43 -0800 Subject: [PATCH 08/31] Update installation for new version adapter --- adapter/ryu/README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/adapter/ryu/README.md b/adapter/ryu/README.md index ca12acb..09c9ceb 100644 --- a/adapter/ryu/README.md +++ b/adapter/ryu/README.md @@ -5,7 +5,9 @@ Ryu adapter module for OmniUI ###Installation### 1. Download the Ryu Controller $ `git clone git://github.com/osrg/ryu.git` -$ `cd ryu; sudo python ./setup.py install` +$ `cd OpenADM/adapter/ryu; cp event.py event.py ~/ryu/ryu/topology` +$ `cp switches.py switches.py ~/ryu/ryu/topology` +$ `cd ~/ryu; sudo python ./setup.py install` ###Execution### **Ryu 1.0** From dbb84a5c109c199fb39754b0c706f1f680ed08d6 Mon Sep 17 00:00:00 2001 From: HsiaoHsuan Date: Wed, 3 Feb 2016 05:27:20 -0800 Subject: [PATCH 09/31] Revise busylink for race condition --- core/src/floodlight_modules/busylink_detect.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/core/src/floodlight_modules/busylink_detect.py b/core/src/floodlight_modules/busylink_detect.py index 82b5311..a31a015 100644 --- a/core/src/floodlight_modules/busylink_detect.py +++ b/core/src/floodlight_modules/busylink_detect.py @@ -88,7 +88,7 @@ def parsePortFeatures(self,features): return 10*(1024**2)/8.0 #10Mb return 0 - def busyLinkDetect(self,event): + def busyLinkDetect(self): self.BLD_result = [] #calculate link's countBytes and capacity for link_id in self.links: @@ -96,6 +96,14 @@ def busyLinkDetect(self,event): srcp = self.links[link_id]['sourcePort'] dest = self.links[link_id]['target'] destp = self.links[link_id]['targetPort'] + + if (src not in self.switches) | (dest not in self.switches): + print 'Not Ready' + return + elif (srcp not in self.switches[src]) | (destp not in self.switches[dest]): + print 'Not Ready' + return + total_bytes = self.switches[src][srcp] + self.switches[dest][destp] self.links[link_id]['countBytes'] = total_bytes self.links[link_id]['capacity'] = min(self.capacity["%s_%d" % (src,srcp)],self.capacity["%s_%d" % (dest,destp)]) @@ -130,5 +138,7 @@ def busyLinkDetect(self,event): print '*****Busy Link ID*****' print json.dumps(data) + else: + print 'No BusyLink' #conn = httplib.HTTPConnection(self.coreIP,self.corePort) #conn.request('POST','/publish/busylink') From 755e186d918a10f4ba655295763836c2240572a8 Mon Sep 17 00:00:00 2001 From: HsiaoHsuan Date: Wed, 24 Feb 2016 08:03:21 -0800 Subject: [PATCH 10/31] Revise checking of port feature --- adapter/ryu/omniui/omniui.py | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/adapter/ryu/omniui/omniui.py b/adapter/ryu/omniui/omniui.py index fe9cf0a..8ab975c 100644 --- a/adapter/ryu/omniui/omniui.py +++ b/adapter/ryu/omniui/omniui.py @@ -135,6 +135,7 @@ def add_device_handler(self, ev): 'dpid': ''.join(temp), 'controller': controllerName } + self.port_to_feature[addDevice['dpid']] = {} print json.dumps(addDevice) @@ -163,7 +164,9 @@ def del_device_handler(self, ev): 'dpid': ''.join(temp), 'controller': controllerName } - del self.port_to_feature[delDevice['dpid']] + + if delDevice['dpid'] in self.port_to_feature: + del self.port_to_feature[delDevice['dpid']] print json.dumps(delDevice) self.post_json_to_core("http://localhost:5567/publish/deldevice", json.dumps(delDevice)) @@ -190,7 +193,10 @@ def mod_port_handler(self, ev): 'port': str(port.port_no), 'controller': controllerName } - del self.port_to_feature[modPort['dpid']][modPort['port']] + + if modPort['dpid'] in self.port_to_feature: + if modPort['port'] in self.port_to_feature[modPort['dpid']]: + del self.port_to_feature[modPort['dpid']][modPort['port']] print json.dumps(modPort) self.post_json_to_core("http://localhost:5567/publish/delport", json.dumps(modPort)) @@ -208,7 +214,9 @@ def mod_port_handler(self, ev): 'port': str(port.port_no), 'controller': controllerName } - self.port_to_feature[modPort['dpid']][modPort['port']] = str(port.currentFeatures) + + if modPort['dpid'] in self.port_to_feature: + self.port_to_feature[modPort['dpid']][modPort['port']] = str(port.currentFeatures) print json.dumps(modPort) self.post_json_to_core("http://localhost:5567/publish/addport", json.dumps(modPort)) @@ -232,7 +240,9 @@ def add_port_handler(self, ev): 'port': str(port.port_no), 'controller': controllerName } - self.port_to_feature[addPort['dpid']][addPort['port']] = str(port.currentFeatures) + + if addPort['dpid'] in self.port_to_feature: + self.port_to_feature[addPort['dpid']][addPort['port']] = str(port.currentFeatures) print json.dumps(addPort) self.post_json_to_core("http://localhost:5567/publish/addport", json.dumps(addPort)) @@ -256,7 +266,10 @@ def del_port_handler(self, ev): 'port': str(port.port_no), 'controller': controllerName } - del self.port_to_feature[delPort['dpid']][delPort['port']] + + if delPort['dpid'] in self.port_to_feature: + if delPort['port'] in self.port_to_feature[delPort['dpid']]: + del self.port_to_feature[delPort['dpid']][delPort['port']] print json.dumps(delPort) self.post_json_to_core("http://localhost:5567/publish/delport", json.dumps(delPort)) From 69ab42be91a2e035ec30837bfba38a2049b34931 Mon Sep 17 00:00:00 2001 From: HsiaoHsuan Date: Tue, 1 Mar 2016 04:48:21 -0800 Subject: [PATCH 11/31] Change the type of space --- .../src/floodlight_modules/busylink_detect.py | 252 +++++++++--------- 1 file changed, 126 insertions(+), 126 deletions(-) diff --git a/core/src/floodlight_modules/busylink_detect.py b/core/src/floodlight_modules/busylink_detect.py index a31a015..bb35a91 100644 --- a/core/src/floodlight_modules/busylink_detect.py +++ b/core/src/floodlight_modules/busylink_detect.py @@ -4,141 +4,141 @@ logger = logging.getLogger(__name__) class BusyLink_Detect: - def __init__(self,core,parm): - """ BusyLinkDetect init""" - self.coreIP = "localhost" - self.corePort = "5567" + def __init__(self,core,parm): + """ BusyLinkDetect init""" + self.coreIP = "localhost" + self.corePort = "5567" - self.baseState = 1 - self.finalState = 3 - self.threshold = 0.000001 - self.statistics = {} - self.capacity = {} - self.links = {} - self.switches = {} - self.BLD_result = [] + self.baseState = 1 + self.finalState = 3 + self.threshold = 0.000001 + self.statistics = {} + self.capacity = {} + self.links = {} + self.switches = {} + self.BLD_result = [] - core.registerEventHandler("linkbag", self.getLink) - core.registerEventHandler("portbag", self.getPort) + core.registerEventHandler("linkbag", self.getLink) + core.registerEventHandler("portbag", self.getPort) - def getLink(self, linksbag): - #print '----------links----------' - #print json.dumps(linksbag.values()) + def getLink(self, linksbag): + #print '----------links----------' + #print json.dumps(linksbag.values()) - try: - data = linksbag.values() - self.links = {} - for link in data: - tmp = {} - tmp['source'] = link[0]['dpid'] - tmp['target'] = link[1]['dpid'] - tmp['sourcePort'] = int(link[0]['port']) - tmp['targetPort'] = int(link[1]['port']) - id = "dpid %s, port %d -- dpid %s, port %d" % (tmp['source'], tmp['sourcePort'], tmp['target'], tmp['targetPort']) - self.links[id] = tmp - except Exception, e: - logger.error("json parse error for links: "+str(e)) + try: + data = linksbag.values() + self.links = {} + for link in data: + tmp = {} + tmp['source'] = link[0]['dpid'] + tmp['target'] = link[1]['dpid'] + tmp['sourcePort'] = int(link[0]['port']) + tmp['targetPort'] = int(link[1]['port']) + id = "dpid %s, port %d -- dpid %s, port %d" % (tmp['source'], tmp['sourcePort'], tmp['target'], tmp['targetPort']) + self.links[id] = tmp + except Exception, e: + logger.error("json parse error for links: "+str(e)) - def getPort(self, portsbag): - #print '----------ports----------' - #print json.dumps(portsbag.values()) + def getPort(self, portsbag): + #print '----------ports----------' + #print json.dumps(portsbag.values()) - try: - data = portsbag.values() - self.capacity = {} - self.switches = {} - for port in data: - self.capacity["%s_%d" % (port['dpid'],int(port['port']))] = self.parsePortFeatures(int(port['capacity'])) - if port['dpid'] in self.switches: - self.switches[port['dpid']][int(port['port'])] = int(port['rxbyte']) - else: - self.switches[port['dpid']] = {} - self.switches[port['dpid']][int(port['port'])] = int(port['rxbyte']) - except Exception, e: - logger.error("json parse error for switch and features: "+str(e)) + try: + data = portsbag.values() + self.capacity = {} + self.switches = {} + for port in data: + self.capacity["%s_%d" % (port['dpid'],int(port['port']))] = self.parsePortFeatures(int(port['capacity'])) + if port['dpid'] in self.switches: + self.switches[port['dpid']][int(port['port'])] = int(port['rxbyte']) + else: + self.switches[port['dpid']] = {} + self.switches[port['dpid']][int(port['port'])] = int(port['rxbyte']) + except Exception, e: + logger.error("json parse error for switch and features: "+str(e)) - self.busyLinkDetect() + self.busyLinkDetect() - def overthreshold(self,id): - self.statistics[id]['state'] += 1 - if self.statistics[id]['state'] >= self.finalState: - self.statistics[id]['state'] = self.finalState - self.BLD_result.append(id) - - def underthreshold(self,id): - self.statistics[id]['state'] -= 1 - if self.statistics[id]['state'] < self.baseState: - self.statistics[id]['state'] = self.baseState - - def parsePortFeatures(self,features): - if features == 0: - return 0 - turn_binary = bin(features)[2:] - binary_len = len(turn_binary) - if binary_len < 12: - turn_binary = '0'*(12-binary_len) + turn_binary - - if turn_binary[5] == '1': - return 10*(1024**3)/8.0 #10Gb - if turn_binary[6] == '1' or turn_binary[7] == '1': - return 1024**3/8.0 #1Gb - if turn_binary[8] == '1' or turn_binary[9] == '1': - return 100*(1024**2)/8.0 #100Mb - if turn_binary[10] == '1' or turn_binary[11] == '1': - return 10*(1024**2)/8.0 #10Mb - return 0 + def overthreshold(self,id): + self.statistics[id]['state'] += 1 + if self.statistics[id]['state'] >= self.finalState: + self.statistics[id]['state'] = self.finalState + self.BLD_result.append(id) + + def underthreshold(self,id): + self.statistics[id]['state'] -= 1 + if self.statistics[id]['state'] < self.baseState: + self.statistics[id]['state'] = self.baseState + + def parsePortFeatures(self,features): + if features == 0: + return 0 + turn_binary = bin(features)[2:] + binary_len = len(turn_binary) + if binary_len < 12: + turn_binary = '0'*(12-binary_len) + turn_binary + + if turn_binary[5] == '1': + return 10*(1024**3)/8.0 #10Gb + if turn_binary[6] == '1' or turn_binary[7] == '1': + return 1024**3/8.0 #1Gb + if turn_binary[8] == '1' or turn_binary[9] == '1': + return 100*(1024**2)/8.0 #100Mb + if turn_binary[10] == '1' or turn_binary[11] == '1': + return 10*(1024**2)/8.0 #10Mb + return 0 - def busyLinkDetect(self): - self.BLD_result = [] - #calculate link's countBytes and capacity - for link_id in self.links: - src = self.links[link_id]['source'] - srcp = self.links[link_id]['sourcePort'] - dest = self.links[link_id]['target'] - destp = self.links[link_id]['targetPort'] + def busyLinkDetect(self): + self.BLD_result = [] + #calculate link's countBytes and capacity + for link_id in self.links: + src = self.links[link_id]['source'] + srcp = self.links[link_id]['sourcePort'] + dest = self.links[link_id]['target'] + destp = self.links[link_id]['targetPort'] - if (src not in self.switches) | (dest not in self.switches): - print 'Not Ready' - return - elif (srcp not in self.switches[src]) | (destp not in self.switches[dest]): - print 'Not Ready' - return + if (src not in self.switches) | (dest not in self.switches): + print 'Not Ready' + return + elif (srcp not in self.switches[src]) | (destp not in self.switches[dest]): + print 'Not Ready' + return - total_bytes = self.switches[src][srcp] + self.switches[dest][destp] - self.links[link_id]['countBytes'] = total_bytes - self.links[link_id]['capacity'] = min(self.capacity["%s_%d" % (src,srcp)],self.capacity["%s_%d" % (dest,destp)]) - - #initialize self.statistics value - if len(self.statistics) == 0: - self.statistics = dict(self.links) - for link_id in self.statistics: - self.statistics[link_id]['state'] = self.baseState - - #check threshold - for link_id in self.links: - if link_id in self.statistics: - if (self.links[link_id]['countBytes'] - self.statistics[link_id]['countBytes']) / self.statistics[link_id]['capacity'] >= self.threshold: - self.overthreshold(link_id) - else: - self.underthreshold(link_id) - self.statistics[link_id]['countBytes'] = self.links[link_id]['countBytes'] - else: - self.statistics[link_id] = dict(self.links[link_id]) - self.statistics[link_id]['state'] = self.baseState - #remove unexisted link info - for link_id in self.statistics: - if link_id not in self.links: - del self.statistics[link_id] - - #return result - if len(self.BLD_result) > 0: - data = {} - for i in range(len(self.BLD_result)): - data[i] = self.BLD_result[i] + total_bytes = self.switches[src][srcp] + self.switches[dest][destp] + self.links[link_id]['countBytes'] = total_bytes + self.links[link_id]['capacity'] = min(self.capacity["%s_%d" % (src,srcp)],self.capacity["%s_%d" % (dest,destp)]) + + #initialize self.statistics value + if len(self.statistics) == 0: + self.statistics = dict(self.links) + for link_id in self.statistics: + self.statistics[link_id]['state'] = self.baseState + + #check threshold + for link_id in self.links: + if link_id in self.statistics: + if (self.links[link_id]['countBytes'] - self.statistics[link_id]['countBytes']) / self.statistics[link_id]['capacity'] >= self.threshold: + self.overthreshold(link_id) + else: + self.underthreshold(link_id) + self.statistics[link_id]['countBytes'] = self.links[link_id]['countBytes'] + else: + self.statistics[link_id] = dict(self.links[link_id]) + self.statistics[link_id]['state'] = self.baseState + #remove unexisted link info + for link_id in self.statistics: + if link_id not in self.links: + del self.statistics[link_id] + + #return result + if len(self.BLD_result) > 0: + data = {} + for i in range(len(self.BLD_result)): + data[i] = self.BLD_result[i] - print '*****Busy Link ID*****' - print json.dumps(data) - else: - print 'No BusyLink' - #conn = httplib.HTTPConnection(self.coreIP,self.corePort) - #conn.request('POST','/publish/busylink') + print '*****Busy Link ID*****' + print json.dumps(data) + else: + print 'No BusyLink' + #conn = httplib.HTTPConnection(self.coreIP,self.corePort) + #conn.request('POST','/publish/busylink') From fae9f5fb8c00e27bed121da34cf06fdb2e2b91bd Mon Sep 17 00:00:00 2001 From: HsiaoHsuan Date: Tue, 1 Mar 2016 05:06:34 -0800 Subject: [PATCH 12/31] Exclude the strange port --- core/src/floodlight_modules/busylink_detect.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/src/floodlight_modules/busylink_detect.py b/core/src/floodlight_modules/busylink_detect.py index bb35a91..9a05b7d 100644 --- a/core/src/floodlight_modules/busylink_detect.py +++ b/core/src/floodlight_modules/busylink_detect.py @@ -48,6 +48,8 @@ def getPort(self, portsbag): self.capacity = {} self.switches = {} for port in data: + if int(port['capacity']) == 0: + continue self.capacity["%s_%d" % (port['dpid'],int(port['port']))] = self.parsePortFeatures(int(port['capacity'])) if port['dpid'] in self.switches: self.switches[port['dpid']][int(port['port'])] = int(port['rxbyte']) From 4b2e7c57cfb4d1f7603f960026855bf5ca864345 Mon Sep 17 00:00:00 2001 From: HsiaoHsuan Date: Thu, 3 Mar 2016 01:13:54 -0800 Subject: [PATCH 13/31] Update event.py --- adapter/ryu/event.py | 356 +++++++++++++++++++++---------------------- 1 file changed, 178 insertions(+), 178 deletions(-) diff --git a/adapter/ryu/event.py b/adapter/ryu/event.py index cc6bb30..148d31e 100644 --- a/adapter/ryu/event.py +++ b/adapter/ryu/event.py @@ -1,178 +1,178 @@ -# Copyright (C) 2013 Nippon Telegraph and Telephone Corporation. -# -# 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. - -import logging -from ryu.controller import handler -from ryu.controller import event - -LOG = logging.getLogger(__name__) - - -class EventSwitchBase(event.EventBase): - def __init__(self, switch): - super(EventSwitchBase, self).__init__() - self.switch = switch - - def __str__(self): - return '%s' % \ - (self.__class__.__name__, - self.switch.dp.id, len(self.switch.ports)) - - -class EventSwitchEnter(EventSwitchBase): - def __init__(self, switch): - super(EventSwitchEnter, self).__init__(switch) - - -class EventSwitchLeave(EventSwitchBase): - def __init__(self, switch): - super(EventSwitchLeave, self).__init__(switch) - - -class EventSwitchReconnected(EventSwitchBase): - def __init__(self, switch): - super(EventSwitchReconnected, self).__init__(switch) - - -class EventPortBase(event.EventBase): - def __init__(self, port): - super(EventPortBase, self).__init__() - self.port = port - - def __str__(self): - return '%s<%s>' % (self.__class__.__name__, self.port) - - -class EventPortAdd(EventPortBase): - def __init__(self, port): - super(EventPortAdd, self).__init__(port) - - -class EventPortDelete(EventPortBase): - def __init__(self, port): - super(EventPortDelete, self).__init__(port) - - -class EventPortModify(EventPortBase): - def __init__(self, port): - super(EventPortModify, self).__init__(port) - - -class EventSwitchRequest(event.EventRequestBase): - # If dpid is None, reply all list - def __init__(self, dpid=None): - super(EventSwitchRequest, self).__init__() - self.dst = 'switches' - self.dpid = dpid - - def __str__(self): - return 'EventSwitchRequest' % \ - (self.src, self.dpid) - - -class EventSwitchReply(event.EventReplyBase): - def __init__(self, dst, switches): - super(EventSwitchReply, self).__init__(dst) - self.switches = switches - - def __str__(self): - return 'EventSwitchReply' % \ - (self.dst, self.switches) - - -class EventLinkBase(event.EventBase): - def __init__(self, link): - super(EventLinkBase, self).__init__() - self.link = link - - def __str__(self): - return '%s<%s>' % (self.__class__.__name__, self.link) - - -class EventLinkAdd(EventLinkBase): - def __init__(self, link): - super(EventLinkAdd, self).__init__(link) - - -class EventLinkDelete(EventLinkBase): - def __init__(self, link): - super(EventLinkDelete, self).__init__(link) - - -class EventLinkRequest(event.EventRequestBase): - # If dpid is None, reply all list - def __init__(self, dpid=None): - super(EventLinkRequest, self).__init__() - self.dst = 'switches' - self.dpid = dpid - - def __str__(self): - return 'EventLinkRequest' % \ - (self.src, self.dpid) - - -class EventLinkReply(event.EventReplyBase): - def __init__(self, dst, dpid, links): - super(EventLinkReply, self).__init__(dst) - self.dpid = dpid - self.links = links - - def __str__(self): - return 'EventLinkReply' % \ - (self.dst, self.dpid, len(self.links)) - - -class EventHostRequest(event.EventRequestBase): - # if dpid is None, replay all hosts - def __init__(self, dpid=None): - super(EventHostRequest, self).__init__() - self.dst = 'switches' - self.dpid = dpid - - def __str__(self): - return 'EventHostRequest' % \ - (self.src, self.dpid) - - -class EventHostReply(event.EventReplyBase): - def __init__(self, dst, dpid, hosts): - super(EventHostReply, self).__init__(dst) - self.dpid = dpid - self.hosts = hosts - - def __str__(self): - return 'EventHostReply' % \ - (self.dst, self.dpid, len(self.hosts)) - - -class EventHostBase(event.EventBase): - def __init__(self, host): - super(EventHostBase, self).__init__() - self.host = host - - def __str__(self): - return '%s<%s>' % (self.__class__.__name__, self.host) - - -class EventHostAdd(EventHostBase): - def __init__(self, host): - super(EventHostAdd, self).__init__(host) - - -class EventHostDelete(EventHostBase): - def __init__(self, host): - super(EventHostDelete, self).__init__(host) - -handler.register_service('ryu.topology.switches') \ No newline at end of file +# Copyright (C) 2013 Nippon Telegraph and Telephone Corporation. +# +# 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. + +import logging +from ryu.controller import handler +from ryu.controller import event + +LOG = logging.getLogger(__name__) + + +class EventSwitchBase(event.EventBase): + def __init__(self, switch): + super(EventSwitchBase, self).__init__() + self.switch = switch + + def __str__(self): + return '%s' % \ + (self.__class__.__name__, + self.switch.dp.id, len(self.switch.ports)) + + +class EventSwitchEnter(EventSwitchBase): + def __init__(self, switch): + super(EventSwitchEnter, self).__init__(switch) + + +class EventSwitchLeave(EventSwitchBase): + def __init__(self, switch): + super(EventSwitchLeave, self).__init__(switch) + + +class EventSwitchReconnected(EventSwitchBase): + def __init__(self, switch): + super(EventSwitchReconnected, self).__init__(switch) + + +class EventPortBase(event.EventBase): + def __init__(self, port): + super(EventPortBase, self).__init__() + self.port = port + + def __str__(self): + return '%s<%s>' % (self.__class__.__name__, self.port) + + +class EventPortAdd(EventPortBase): + def __init__(self, port): + super(EventPortAdd, self).__init__(port) + + +class EventPortDelete(EventPortBase): + def __init__(self, port): + super(EventPortDelete, self).__init__(port) + + +class EventPortModify(EventPortBase): + def __init__(self, port): + super(EventPortModify, self).__init__(port) + + +class EventSwitchRequest(event.EventRequestBase): + # If dpid is None, reply all list + def __init__(self, dpid=None): + super(EventSwitchRequest, self).__init__() + self.dst = 'switches' + self.dpid = dpid + + def __str__(self): + return 'EventSwitchRequest' % \ + (self.src, self.dpid) + + +class EventSwitchReply(event.EventReplyBase): + def __init__(self, dst, switches): + super(EventSwitchReply, self).__init__(dst) + self.switches = switches + + def __str__(self): + return 'EventSwitchReply' % \ + (self.dst, self.switches) + + +class EventLinkBase(event.EventBase): + def __init__(self, link): + super(EventLinkBase, self).__init__() + self.link = link + + def __str__(self): + return '%s<%s>' % (self.__class__.__name__, self.link) + + +class EventLinkAdd(EventLinkBase): + def __init__(self, link): + super(EventLinkAdd, self).__init__(link) + + +class EventLinkDelete(EventLinkBase): + def __init__(self, link): + super(EventLinkDelete, self).__init__(link) + + +class EventLinkRequest(event.EventRequestBase): + # If dpid is None, reply all list + def __init__(self, dpid=None): + super(EventLinkRequest, self).__init__() + self.dst = 'switches' + self.dpid = dpid + + def __str__(self): + return 'EventLinkRequest' % \ + (self.src, self.dpid) + + +class EventLinkReply(event.EventReplyBase): + def __init__(self, dst, dpid, links): + super(EventLinkReply, self).__init__(dst) + self.dpid = dpid + self.links = links + + def __str__(self): + return 'EventLinkReply' % \ + (self.dst, self.dpid, len(self.links)) + + +class EventHostRequest(event.EventRequestBase): + # if dpid is None, replay all hosts + def __init__(self, dpid=None): + super(EventHostRequest, self).__init__() + self.dst = 'switches' + self.dpid = dpid + + def __str__(self): + return 'EventHostRequest' % \ + (self.src, self.dpid) + + +class EventHostReply(event.EventReplyBase): + def __init__(self, dst, dpid, hosts): + super(EventHostReply, self).__init__(dst) + self.dpid = dpid + self.hosts = hosts + + def __str__(self): + return 'EventHostReply' % \ + (self.dst, self.dpid, len(self.hosts)) + + +class EventHostBase(event.EventBase): + def __init__(self, host): + super(EventHostBase, self).__init__() + self.host = host + + def __str__(self): + return '%s<%s>' % (self.__class__.__name__, self.host) + + +class EventHostAdd(EventHostBase): + def __init__(self, host): + super(EventHostAdd, self).__init__(host) + + +class EventHostDelete(EventHostBase): + def __init__(self, host): + super(EventHostDelete, self).__init__(host) + +handler.register_service('ryu.topology.switches') From 916fe1005add0b94a54c7a9e4a98762108b20624 Mon Sep 17 00:00:00 2001 From: HsiaoHsuan Date: Thu, 3 Mar 2016 01:56:42 -0800 Subject: [PATCH 14/31] Update switches.py --- adapter/ryu/switches.py | 2182 +++++++++++++++++++-------------------- 1 file changed, 1086 insertions(+), 1096 deletions(-) diff --git a/adapter/ryu/switches.py b/adapter/ryu/switches.py index 0497506..5721f37 100644 --- a/adapter/ryu/switches.py +++ b/adapter/ryu/switches.py @@ -1,1096 +1,1086 @@ -# Copyright (C) 2013 Nippon Telegraph and Telephone Corporation. -# -# 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. - -import logging -import six -import struct -import time -import json -from ryu import cfg - -from ryu.topology import event -from ryu.base import app_manager -from ryu.controller import ofp_event -from ryu.controller.handler import set_ev_cls -from ryu.controller.handler import MAIN_DISPATCHER, DEAD_DISPATCHER -from ryu.exception import RyuException -from ryu.lib import addrconv, hub -from ryu.lib.mac import DONTCARE_STR -from ryu.lib.dpid import dpid_to_str, str_to_dpid -from ryu.lib.port_no import port_no_to_str -from ryu.lib.packet import packet, ethernet -from ryu.lib.packet import lldp, ether_types -from ryu.lib.packet import arp, ipv4, ipv6, vlan -from ryu.ofproto.ether import ETH_TYPE_LLDP -from ryu.ofproto import nx_match -from ryu.ofproto import ofproto_v1_0 -from ryu.ofproto import ofproto_v1_2 -from ryu.ofproto import ofproto_v1_3 -from ryu.ofproto import ofproto_v1_4 - - -LOG = logging.getLogger(__name__) - - -CONF = cfg.CONF - -CONF.register_cli_opts([ - cfg.BoolOpt('observe-links', default=False, - help='observe link discovery events.'), - cfg.BoolOpt('install-lldp-flow', default=True, - help='link discovery: explicitly install flow entry ' - 'to send lldp packet to controller'), - cfg.BoolOpt('explicit-drop', default=True, - help='link discovery: explicitly drop lldp packet in') -]) - - -class Port(object): - # This is data class passed by EventPortXXX - def __init__(self, dpid, ofproto, ofpport): - super(Port, self).__init__() - - self.dpid = dpid - self._ofproto = ofproto - self._config = ofpport.config - self._state = ofpport.state - - self.port_no = ofpport.port_no - self.hw_addr = ofpport.hw_addr - self.name = ofpport.name - self.currentFeatures = ofpport.curr - - def is_reserved(self): - return self.port_no > self._ofproto.OFPP_MAX - - def is_down(self): - return (self._state & self._ofproto.OFPPS_LINK_DOWN) > 0 \ - or (self._config & self._ofproto.OFPPC_PORT_DOWN) > 0 - - def is_live(self): - # NOTE: OF1.2 has OFPPS_LIVE state - # return (self._state & self._ofproto.OFPPS_LIVE) > 0 - return not self.is_down() - - def to_dict(self): - return {'dpid': dpid_to_str(self.dpid), - 'port_no': port_no_to_str(self.port_no), - 'hw_addr': self.hw_addr, - 'name': self.name.rstrip('\0')} - - # for Switch.del_port() - def __eq__(self, other): - return self.dpid == other.dpid and self.port_no == other.port_no - - def __ne__(self, other): - return not self.__eq__(other) - - def __hash__(self): - return hash((self.dpid, self.port_no)) - - def __str__(self): - LIVE_MSG = {False: 'DOWN', True: 'LIVE'} - return 'Port' % \ - (self.dpid, self.port_no, LIVE_MSG[self.is_live()]) - - -class Switch(object): - # This is data class passed by EventSwitchXXX - def __init__(self, dp): - super(Switch, self).__init__() - - self.dp = dp - self.ports = [] - - def add_port(self, ofpport): - port = Port(self.dp.id, self.dp.ofproto, ofpport) - if not port.is_reserved(): - self.ports.append(port) - - def del_port(self, ofpport): - self.ports.remove(Port(ofpport)) - - def to_dict(self): - d = {'dpid': dpid_to_str(self.dp.id), - 'ports': [port.to_dict() for port in self.ports]} - return d - - def __str__(self): - msg = 'Switch Host class - def __init__(self): - super(HostState, self).__init__() - - def add(self, host): - mac = host.mac - self.setdefault(mac, host) - - def update_vlan(self, host, vlan=None): - mac = host.mac - host = None - if mac in self: - host = self[mac] - - if not host: - return - - if vlan != None and vlan not in host.vlan: - host.vlan.append(vlan) - return 'new' - elif vlan != None: - if host.vlan.index(vlan) == (len(host.vlan)-1): - return 'old' - else: - host.vlan.remove(vlan) - host.vlan.append(vlan) - return 'new' - - def update_ip(self, host, ip_v4=None, ip_v6=None): - mac = host.mac - host = None - if mac in self: - host = self[mac] - - if not host: - return - - if ip_v4 != None and ip_v4 not in host.ipv4: - host.ipv4.append(ip_v4) - return 'new' - elif ip_v4 != None: - if host.ipv4.index(ip_v4) == (len(host.ipv4)-1): - return 'old' - else: - host.ipv4.remove(ip_v4) - host.ipv4.append(ip_v4) - return 'new' - - if ip_v6 != None and ip_v6 not in host.ipv6: - host.ipv6.append(ip_v6) - return 'new' - elif ip_v6 != None: - if host.ipv6.index(ip_v6) == (len(host.ipv6)-1): - return 'old' - else: - host.ipv6.remove(ip_v6) - host.ipv6.append(ip_v6) - return 'new' - - def get_by_dpid(self, dpid): - result = [] - - for mac in self: - host = self[mac] - if host.port.dpid == dpid: - result.append(host) - - return result - - -class PortState(dict): - # dict: int port_no -> OFPPort port - # OFPPort is defined in ryu.ofproto.ofproto_v1_X_parser - def __init__(self): - super(PortState, self).__init__() - - def add(self, port_no, port): - self[port_no] = port - - def remove(self, port_no): - del self[port_no] - - def modify(self, port_no, port): - self[port_no] = port - - -class PortData(object): - def __init__(self, is_down, lldp_data): - super(PortData, self).__init__() - self.is_down = is_down - self.lldp_data = lldp_data - self.timestamp = None - self.sent = 0 - - def lldp_sent(self): - self.timestamp = time.time() - self.sent += 1 - - def lldp_received(self): - self.sent = 0 - - def lldp_dropped(self): - return self.sent - - def clear_timestamp(self): - self.timestamp = None - - def set_down(self, is_down): - self.is_down = is_down - - def __str__(self): - return 'PortData' \ - % (not self.is_down, self.timestamp, self.sent) - - -class PortDataState(dict): - # dict: Port class -> PortData class - # slimed down version of OrderedDict as python 2.6 doesn't support it. - _PREV = 0 - _NEXT = 1 - _KEY = 2 - - def __init__(self): - super(PortDataState, self).__init__() - self._root = root = [] # sentinel node - root[:] = [root, root, None] # [_PREV, _NEXT, _KEY] - # doubly linked list - self._map = {} - - def _remove_key(self, key): - link_prev, link_next, key = self._map.pop(key) - link_prev[self._NEXT] = link_next - link_next[self._PREV] = link_prev - - def _append_key(self, key): - root = self._root - last = root[self._PREV] - last[self._NEXT] = root[self._PREV] = self._map[key] = [last, root, - key] - - def _prepend_key(self, key): - root = self._root - first = root[self._NEXT] - first[self._PREV] = root[self._NEXT] = self._map[key] = [root, first, - key] - - def _move_last_key(self, key): - self._remove_key(key) - self._append_key(key) - - def _move_front_key(self, key): - self._remove_key(key) - self._prepend_key(key) - - def add_port(self, port, lldp_data): - if port not in self: - self._prepend_key(port) - self[port] = PortData(port.is_down(), lldp_data) - else: - self[port].is_down = port.is_down() - - def lldp_sent(self, port): - port_data = self[port] - port_data.lldp_sent() - self._move_last_key(port) - return port_data - - def lldp_received(self, port): - self[port].lldp_received() - - def move_front(self, port): - port_data = self.get(port, None) - if port_data is not None: - port_data.clear_timestamp() - self._move_front_key(port) - - def set_down(self, port): - is_down = port.is_down() - port_data = self[port] - port_data.set_down(is_down) - port_data.clear_timestamp() - if not is_down: - self._move_front_key(port) - return is_down - - def get_port(self, port): - return self[port] - - def del_port(self, port): - del self[port] - self._remove_key(port) - - def __iter__(self): - root = self._root - curr = root[self._NEXT] - while curr is not root: - yield curr[self._KEY] - curr = curr[self._NEXT] - - def clear(self): - for node in self._map.values(): - del node[:] - root = self._root - root[:] = [root, root, None] - self._map.clear() - dict.clear(self) - - def items(self): - 'od.items() -> list of (key, value) pairs in od' - return [(key, self[key]) for key in self] - - def iteritems(self): - 'od.iteritems -> an iterator over the (key, value) pairs in od' - for k in self: - yield (k, self[k]) - - -class LinkState(dict): - # dict: Link class -> timestamp - def __init__(self): - super(LinkState, self).__init__() - self._map = {} - - def get_peer(self, src): - return self._map.get(src, None) - - def update_link(self, src, dst): - link = Link(src, dst) - - self[link] = time.time() - self._map[src] = dst - - # return if the reverse link is also up or not - rev_link = Link(dst, src) - return rev_link in self - - def link_down(self, link): - del self[link] - del self._map[link.src] - - def rev_link_set_timestamp(self, rev_link, timestamp): - # rev_link may or may not in LinkSet - if rev_link in self: - self[rev_link] = timestamp - - def port_deleted(self, src): - dst = self.get_peer(src) - if dst is None: - raise KeyError() - - link = Link(src, dst) - rev_link = Link(dst, src) - del self[link] - del self._map[src] - # reverse link might not exist - self.pop(rev_link, None) - rev_link_dst = self._map.pop(dst, None) - - return dst, rev_link_dst - - -class LLDPPacket(object): - # make a LLDP packet for link discovery. - - CHASSIS_ID_PREFIX = 'dpid:' - CHASSIS_ID_PREFIX_LEN = len(CHASSIS_ID_PREFIX) - CHASSIS_ID_FMT = CHASSIS_ID_PREFIX + '%s' - - PORT_ID_STR = '!I' # uint32_t - PORT_ID_SIZE = 4 - - class LLDPUnknownFormat(RyuException): - message = '%(msg)s' - - @staticmethod - def lldp_packet(dpid, port_no, dl_addr, ttl): - pkt = packet.Packet() - - dst = lldp.LLDP_MAC_NEAREST_BRIDGE - src = dl_addr - ethertype = ETH_TYPE_LLDP - eth_pkt = ethernet.ethernet(dst, src, ethertype) - pkt.add_protocol(eth_pkt) - - tlv_chassis_id = lldp.ChassisID( - subtype=lldp.ChassisID.SUB_LOCALLY_ASSIGNED, - chassis_id=(LLDPPacket.CHASSIS_ID_FMT % - dpid_to_str(dpid)).encode('ascii')) - - tlv_port_id = lldp.PortID(subtype=lldp.PortID.SUB_PORT_COMPONENT, - port_id=struct.pack( - LLDPPacket.PORT_ID_STR, - port_no)) - - tlv_ttl = lldp.TTL(ttl=ttl) - tlv_end = lldp.End() - - tlvs = (tlv_chassis_id, tlv_port_id, tlv_ttl, tlv_end) - lldp_pkt = lldp.lldp(tlvs) - pkt.add_protocol(lldp_pkt) - - pkt.serialize() - return pkt.data - - @staticmethod - def lldp_parse(data): - pkt = packet.Packet(data) - i = iter(pkt) - eth_pkt = six.next(i) - assert type(eth_pkt) == ethernet.ethernet - - lldp_pkt = six.next(i) - if type(lldp_pkt) != lldp.lldp: - raise LLDPPacket.LLDPUnknownFormat() - - tlv_chassis_id = lldp_pkt.tlvs[0] - if tlv_chassis_id.subtype != lldp.ChassisID.SUB_LOCALLY_ASSIGNED: - raise LLDPPacket.LLDPUnknownFormat( - msg='unknown chassis id subtype %d' % tlv_chassis_id.subtype) - chassis_id = tlv_chassis_id.chassis_id - if not chassis_id.startswith(LLDPPacket.CHASSIS_ID_PREFIX): - raise LLDPPacket.LLDPUnknownFormat( - msg='unknown chassis id format %s' % chassis_id) - src_dpid = str_to_dpid(chassis_id[LLDPPacket.CHASSIS_ID_PREFIX_LEN:]) - - tlv_port_id = lldp_pkt.tlvs[1] - if tlv_port_id.subtype != lldp.PortID.SUB_PORT_COMPONENT: - raise LLDPPacket.LLDPUnknownFormat( - msg='unknown port id subtype %d' % tlv_port_id.subtype) - port_id = tlv_port_id.port_id - if len(port_id) != LLDPPacket.PORT_ID_SIZE: - raise LLDPPacket.LLDPUnknownFormat( - msg='unknown port id %d' % port_id) - (src_port_no, ) = struct.unpack(LLDPPacket.PORT_ID_STR, port_id) - - return src_dpid, src_port_no - - -class Switches(app_manager.RyuApp): - OFP_VERSIONS = [ofproto_v1_0.OFP_VERSION, ofproto_v1_2.OFP_VERSION, - ofproto_v1_3.OFP_VERSION, ofproto_v1_4.OFP_VERSION] - _EVENTS = [event.EventSwitchEnter, event.EventSwitchLeave, - event.EventSwitchReconnected, - event.EventPortAdd, event.EventPortDelete, - event.EventPortModify, - event.EventLinkAdd, event.EventLinkDelete, - event.EventHostAdd] - - DEFAULT_TTL = 120 # unused. ignored. - LLDP_PACKET_LEN = len(LLDPPacket.lldp_packet(0, 0, DONTCARE_STR, 0)) - - LLDP_SEND_GUARD = .05 - LLDP_SEND_PERIOD_PER_PORT = .9 - TIMEOUT_CHECK_PERIOD = 5. - LINK_TIMEOUT = TIMEOUT_CHECK_PERIOD * 2 - LINK_LLDP_DROP = 5 - - def __init__(self, *args, **kwargs): - super(Switches, self).__init__(*args, **kwargs) - - self.name = 'switches' - self.dps = {} # datapath_id => Datapath class - self.port_state = {} # datapath_id => ports - self.ports = PortDataState() # Port class -> PortData class - self.links = LinkState() # Link class -> timestamp - self.hosts = HostState() # mac address -> Host class list - self.is_active = True - - self.link_discovery = self.CONF.observe_links - if self.link_discovery: - self.install_flow = self.CONF.install_lldp_flow - self.explicit_drop = self.CONF.explicit_drop - self.lldp_event = hub.Event() - self.link_event = hub.Event() - self.threads.append(hub.spawn(self.lldp_loop)) - self.threads.append(hub.spawn(self.link_loop)) - - def close(self): - self.is_active = False - if self.link_discovery: - self.lldp_event.set() - self.link_event.set() - hub.joinall(self.threads) - - def _register(self, dp): - assert dp.id is not None - - self.dps[dp.id] = dp - if dp.id not in self.port_state: - self.port_state[dp.id] = PortState() - for port in dp.ports.values(): - self.port_state[dp.id].add(port.port_no, port) - - def _unregister(self, dp): - if dp.id in self.dps: - if (self.dps[dp.id] == dp): - del self.dps[dp.id] - del self.port_state[dp.id] - - def _get_switch(self, dpid): - if dpid in self.dps: - switch = Switch(self.dps[dpid]) - for ofpport in self.port_state[dpid].values(): - switch.add_port(ofpport) - return switch - - def _get_port(self, dpid, port_no): - switch = self._get_switch(dpid) - if switch: - for p in switch.ports: - if p.port_no == port_no: - return p - - def _port_added(self, port): - lldp_data = LLDPPacket.lldp_packet( - port.dpid, port.port_no, port.hw_addr, self.DEFAULT_TTL) - self.ports.add_port(port, lldp_data) - # LOG.debug('_port_added dpid=%s, port_no=%s, live=%s', - # port.dpid, port.port_no, port.is_live()) - - def _link_down(self, port): - try: - dst, rev_link_dst = self.links.port_deleted(port) - except KeyError: - # LOG.debug('key error. src=%s, dst=%s', - # port, self.links.get_peer(port)) - return - link = Link(port, dst) - self.send_event_to_observers(event.EventLinkDelete(link)) - if rev_link_dst: - rev_link = Link(dst, rev_link_dst) - self.send_event_to_observers(event.EventLinkDelete(rev_link)) - self.ports.move_front(dst) - - def _is_edge_port(self, port): - for link in self.links: - if port == link.src or port == link.dst: - return False - - return True - - @set_ev_cls(ofp_event.EventOFPStateChange, - [MAIN_DISPATCHER, DEAD_DISPATCHER]) - def state_change_handler(self, ev): - dp = ev.datapath - assert dp is not None - LOG.debug(dp) - - if ev.state == MAIN_DISPATCHER: - dp_multiple_conns = False - if dp.id in self.dps: - LOG.warning('Multiple connections from %s', dpid_to_str(dp.id)) - dp_multiple_conns = True - (self.dps[dp.id]).close() - - self._register(dp) - switch = self._get_switch(dp.id) - LOG.debug('register %s', switch) - - if not dp_multiple_conns: - self.send_event_to_observers(event.EventSwitchEnter(switch)) - else: - self.send_event_to_observers(event.EventSwitchReconnected(switch)) - - if not self.link_discovery: - return - - if self.install_flow: - ofproto = dp.ofproto - ofproto_parser = dp.ofproto_parser - - # TODO:XXX need other versions - if ofproto.OFP_VERSION == ofproto_v1_0.OFP_VERSION: - rule = nx_match.ClsRule() - rule.set_dl_dst(addrconv.mac.text_to_bin( - lldp.LLDP_MAC_NEAREST_BRIDGE)) - rule.set_dl_type(ETH_TYPE_LLDP) - actions = [ofproto_parser.OFPActionOutput( - ofproto.OFPP_CONTROLLER, self.LLDP_PACKET_LEN)] - dp.send_flow_mod( - rule=rule, cookie=0, command=ofproto.OFPFC_ADD, - idle_timeout=0, hard_timeout=0, actions=actions, - priority=0xFFFF) - elif ofproto.OFP_VERSION >= ofproto_v1_2.OFP_VERSION: - match = ofproto_parser.OFPMatch( - eth_type=ETH_TYPE_LLDP, - eth_dst=lldp.LLDP_MAC_NEAREST_BRIDGE) - # OFPCML_NO_BUFFER is set so that the LLDP is not - # buffered on switch - parser = ofproto_parser - actions = [parser.OFPActionOutput(ofproto.OFPP_CONTROLLER, - ofproto.OFPCML_NO_BUFFER - )] - inst = [parser.OFPInstructionActions( - ofproto.OFPIT_APPLY_ACTIONS, actions)] - mod = parser.OFPFlowMod(datapath=dp, match=match, - idle_timeout=0, hard_timeout=0, - instructions=inst, - priority=0xFFFF) - dp.send_msg(mod) - else: - LOG.error('cannot install flow. unsupported version. %x', - dp.ofproto.OFP_VERSION) - - # Do not add ports while dp has multiple connections to controller. - if not dp_multiple_conns: - for port in switch.ports: - if not port.is_reserved(): - self._port_added(port) - - self.lldp_event.set() - - elif ev.state == DEAD_DISPATCHER: - # dp.id is None when datapath dies before handshake - if dp.id is None: - return - - # if switch delete - # then delete host on it - hostlist = self.hosts.get_by_dpid(dp.id) - for host in hostlist: - ev = event.EventHostDelete(host) - self.send_event_to_observers(ev) - del self.hosts[host.mac] - - switch = self._get_switch(dp.id) - if switch: - if switch.dp is dp: - self._unregister(dp) - LOG.debug('unregister %s', switch) - - self.send_event_to_observers(event.EventSwitchLeave(switch)) - - if not self.link_discovery: - return - - for port in switch.ports: - if not port.is_reserved(): - self.ports.del_port(port) - self._link_down(port) - self.lldp_event.set() - - @set_ev_cls(ofp_event.EventOFPPortStatus, MAIN_DISPATCHER) - def port_status_handler(self, ev): - msg = ev.msg - reason = msg.reason - dp = msg.datapath - ofpport = msg.desc - - if reason == dp.ofproto.OFPPR_ADD: - # LOG.debug('A port was added.' + - # '(datapath id = %s, port number = %s)', - # dp.id, ofpport.port_no) - self.port_state[dp.id].add(ofpport.port_no, ofpport) - self.send_event_to_observers( - event.EventPortAdd(Port(dp.id, dp.ofproto, ofpport))) - - if not self.link_discovery: - return - - port = self._get_port(dp.id, ofpport.port_no) - if port and not port.is_reserved(): - self._port_added(port) - self.lldp_event.set() - - elif reason == dp.ofproto.OFPPR_DELETE: - # LOG.debug('A port was deleted.' + - # '(datapath id = %s, port number = %s)', - # dp.id, ofpport.port_no) - self.port_state[dp.id].remove(ofpport.port_no) - self.send_event_to_observers( - event.EventPortDelete(Port(dp.id, dp.ofproto, ofpport))) - - if not self.link_discovery: - return - - port = self._get_port(dp.id, ofpport.port_no) - if port and not port.is_reserved(): - # if port delete - # then delete host on it - for host in self.hosts.values(): - if port.__eq__(host.port): - ev = event.EventHostDelete(host) - self.send_event_to_observers(ev) - del self.hosts[host.mac] - break - - self.ports.del_port(port) - self._link_down(port) - self.lldp_event.set() - - else: - assert reason == dp.ofproto.OFPPR_MODIFY - # LOG.debug('A port was modified.' + - # '(datapath id = %s, port number = %s)', - # dp.id, ofpport.port_no) - self.port_state[dp.id].modify(ofpport.port_no, ofpport) - self.send_event_to_observers( - event.EventPortModify(Port(dp.id, dp.ofproto, ofpport))) - - if not self.link_discovery: - return - - port = self._get_port(dp.id, ofpport.port_no) - if port and not port.is_reserved(): - if self.ports.set_down(port): - # if port down - # then delete host on it - for host in self.hosts.values(): - if port.__eq__(host.port): - ev = event.EventHostDelete(host) - self.send_event_to_observers(ev) - del self.hosts[host.mac] - break - - self._link_down(port) - self.lldp_event.set() - - @staticmethod - def _drop_packet(msg): - buffer_id = msg.buffer_id - if buffer_id == msg.datapath.ofproto.OFP_NO_BUFFER: - return - - dp = msg.datapath - # TODO:XXX - if dp.ofproto.OFP_VERSION == ofproto_v1_0.OFP_VERSION: - dp.send_packet_out(buffer_id, msg.in_port, []) - elif dp.ofproto.OFP_VERSION >= ofproto_v1_2.OFP_VERSION: - dp.send_packet_out(buffer_id, msg.match['in_port'], []) - else: - LOG.error('cannot drop_packet. unsupported version. %x', - dp.ofproto.OFP_VERSION) - - @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER) - def lldp_packet_in_handler(self, ev): - if not self.link_discovery: - return - - msg = ev.msg - try: - src_dpid, src_port_no = LLDPPacket.lldp_parse(msg.data) - except LLDPPacket.LLDPUnknownFormat as e: - # This handler can receive all the packets which can be - # not-LLDP packet. Ignore it silently - return - - dst_dpid = msg.datapath.id - if msg.datapath.ofproto.OFP_VERSION == ofproto_v1_0.OFP_VERSION: - dst_port_no = msg.in_port - elif msg.datapath.ofproto.OFP_VERSION >= ofproto_v1_2.OFP_VERSION: - dst_port_no = msg.match['in_port'] - else: - LOG.error('cannot accept LLDP. unsupported version. %x', - msg.datapath.ofproto.OFP_VERSION) - - src = self._get_port(src_dpid, src_port_no) - if not src or src.dpid == dst_dpid: - return - try: - self.ports.lldp_received(src) - except KeyError: - # There are races between EventOFPPacketIn and - # EventDPPortAdd. So packet-in event can happend before - # port add event. In that case key error can happend. - # LOG.debug('lldp_received: KeyError %s', e) - pass - - dst = self._get_port(dst_dpid, dst_port_no) - if not dst: - return - - old_peer = self.links.get_peer(src) - # LOG.debug("Packet-In") - # LOG.debug(" src=%s", src) - # LOG.debug(" dst=%s", dst) - # LOG.debug(" old_peer=%s", old_peer) - if old_peer and old_peer != dst: - old_link = Link(src, old_peer) - del self.links[old_link] - self.send_event_to_observers(event.EventLinkDelete(old_link)) - - link = Link(src, dst) - if link not in self.links: - self.send_event_to_observers(event.EventLinkAdd(link)) - - # remove hosts from edge port - for host in self.hosts.values(): - if not self._is_edge_port(host.port): - del self.hosts[host.mac] - - if not self.links.update_link(src, dst): - # reverse link is not detected yet. - # So schedule the check early because it's very likely it's up - self.ports.move_front(dst) - self.lldp_event.set() - if self.explicit_drop: - self._drop_packet(msg) - - @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER) - def host_discovery_packet_in_handler(self, ev): - msg = ev.msg - pkt = packet.Packet(msg.data) - eth = pkt.get_protocols(ethernet.ethernet)[0] - - # ignore lldp packet - if eth.ethertype == ETH_TYPE_LLDP: - return - - datapath = msg.datapath - dpid = datapath.id - port_no = -1 - - if msg.datapath.ofproto.OFP_VERSION == ofproto_v1_0.OFP_VERSION: - port_no = msg.in_port - else: - port_no = msg.match['in_port'] - - port = self._get_port(dpid, port_no) - - # can't find this port(ex: logic port) - if not port: - return - # ignore switch-to-switch port - if not self._is_edge_port(port): - return - - host_mac = eth.src - host = Host(host_mac, port) - - if host_mac not in self.hosts: - self.hosts.add(host) - ev = event.EventHostAdd(host) - self.send_event_to_observers(ev) - - # vlan packet, update vlan tag - vlan_pkt = pkt.get_protocol(vlan.vlan) - if vlan_pkt: - ans = self.hosts.update_vlan(host, vlan=vlan_pkt.vid) - if ans == 'new': - ev = event.EventHostAdd(host) - self.send_event_to_observers(ev) - - # arp packet, update ip address - arp_pkt = pkt.get_protocol(arp.arp) - if arp_pkt: - ans = self.hosts.update_ip(host, ip_v4=arp_pkt.src_ip) - if ans == 'new': - ev = event.EventHostAdd(host) - self.send_event_to_observers(ev) - - # ipv4 packet, update ipv4 address - ipv4_pkt = pkt.get_protocol(ipv4.ipv4) - if ipv4_pkt: - ans = self.hosts.update_ip(host, ip_v4=ipv4_pkt.src) - if ans == 'new': - ev = event.EventHostAdd(host) - self.send_event_to_observers(ev) - - # ipv6 packet, update ipv6 address - ipv6_pkt = pkt.get_protocol(ipv6.ipv6) - if ipv6_pkt: - self.hosts.update_ip(host, ip_v6=ipv6_pkt.src) - - def send_lldp_packet(self, port): - try: - port_data = self.ports.lldp_sent(port) - except KeyError as e: - # ports can be modified during our sleep in self.lldp_loop() - # LOG.debug('send_lldp: KeyError %s', e) - return - if port_data.is_down: - return - - dp = self.dps.get(port.dpid, None) - if dp is None: - # datapath was already deleted - return - - # LOG.debug('lldp sent dpid=%s, port_no=%d', dp.id, port.port_no) - # TODO:XXX - if dp.ofproto.OFP_VERSION == ofproto_v1_0.OFP_VERSION: - actions = [dp.ofproto_parser.OFPActionOutput(port.port_no)] - dp.send_packet_out(actions=actions, data=port_data.lldp_data) - elif dp.ofproto.OFP_VERSION >= ofproto_v1_2.OFP_VERSION: - actions = [dp.ofproto_parser.OFPActionOutput(port.port_no)] - out = dp.ofproto_parser.OFPPacketOut( - datapath=dp, in_port=dp.ofproto.OFPP_CONTROLLER, - buffer_id=dp.ofproto.OFP_NO_BUFFER, actions=actions, - data=port_data.lldp_data) - dp.send_msg(out) - else: - LOG.error('cannot send lldp packet. unsupported version. %x', - dp.ofproto.OFP_VERSION) - - def lldp_loop(self): - while self.is_active: - self.lldp_event.clear() - - now = time.time() - timeout = None - ports_now = [] - ports = [] - for (key, data) in self.ports.items(): - if data.timestamp is None: - ports_now.append(key) - continue - - expire = data.timestamp + self.LLDP_SEND_PERIOD_PER_PORT - if expire <= now: - ports.append(key) - continue - - timeout = expire - now - break - - for port in ports_now: - self.send_lldp_packet(port) - for port in ports: - self.send_lldp_packet(port) - hub.sleep(self.LLDP_SEND_GUARD) # don't burst - - if timeout is not None and ports: - timeout = 0 # We have already slept - # LOG.debug('lldp sleep %s', timeout) - self.lldp_event.wait(timeout=timeout) - - def link_loop(self): - while self.is_active: - self.link_event.clear() - - now = time.time() - deleted = [] - for (link, timestamp) in self.links.items(): - # LOG.debug('%s timestamp %d (now %d)', link, timestamp, now) - if timestamp + self.LINK_TIMEOUT < now: - src = link.src - if src in self.ports: - port_data = self.ports.get_port(src) - # LOG.debug('port_data %s', port_data) - if port_data.lldp_dropped() > self.LINK_LLDP_DROP: - deleted.append(link) - - for link in deleted: - self.links.link_down(link) - # LOG.debug('delete %s', link) - self.send_event_to_observers(event.EventLinkDelete(link)) - - dst = link.dst - rev_link = Link(dst, link.src) - if rev_link not in deleted: - # It is very likely that the reverse link is also - # disconnected. Check it early. - expire = now - self.LINK_TIMEOUT - self.links.rev_link_set_timestamp(rev_link, expire) - if dst in self.ports: - self.ports.move_front(dst) - self.lldp_event.set() - - self.link_event.wait(timeout=self.TIMEOUT_CHECK_PERIOD) - - @set_ev_cls(event.EventSwitchRequest) - def switch_request_handler(self, req): - # LOG.debug(req) - dpid = req.dpid - - switches = [] - if dpid is None: - # reply all list - for dp in self.dps.values(): - switches.append(self._get_switch(dp.id)) - elif dpid in self.dps: - switches.append(self._get_switch(dpid)) - - rep = event.EventSwitchReply(req.src, switches) - self.reply_to_request(req, rep) - - @set_ev_cls(event.EventLinkRequest) - def link_request_handler(self, req): - # LOG.debug(req) - dpid = req.dpid - - if dpid is None: - links = self.links - else: - links = [link for link in self.links if link.src.dpid == dpid] - rep = event.EventLinkReply(req.src, dpid, links) - self.reply_to_request(req, rep) - - @set_ev_cls(event.EventHostRequest) - def host_request_handler(self, req): - dpid = req.dpid - hosts = [] - if dpid is None: - for mac in self.hosts: - hosts.append(self.hosts[mac]) - else: - hosts = self.hosts.get_by_dpid(dpid) - - rep = event.EventHostReply(req.src, dpid, hosts) - self.reply_to_request(req, rep) \ No newline at end of file +# Copyright (C) 2013 Nippon Telegraph and Telephone Corporation. +# +# 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. + +import logging +import six +import struct +import time +import json +from ryu import cfg + +from ryu.topology import event +from ryu.base import app_manager +from ryu.controller import ofp_event +from ryu.controller.handler import set_ev_cls +from ryu.controller.handler import MAIN_DISPATCHER, DEAD_DISPATCHER +from ryu.exception import RyuException +from ryu.lib import addrconv, hub +from ryu.lib.mac import DONTCARE_STR +from ryu.lib.dpid import dpid_to_str, str_to_dpid +from ryu.lib.port_no import port_no_to_str +from ryu.lib.packet import packet, ethernet +from ryu.lib.packet import lldp, ether_types +from ryu.lib.packet import arp, ipv4, ipv6, vlan +from ryu.ofproto.ether import ETH_TYPE_LLDP +from ryu.ofproto import nx_match +from ryu.ofproto import ofproto_v1_0 +from ryu.ofproto import ofproto_v1_2 +from ryu.ofproto import ofproto_v1_3 +from ryu.ofproto import ofproto_v1_4 + + +LOG = logging.getLogger(__name__) + + +CONF = cfg.CONF + +CONF.register_cli_opts([ + cfg.BoolOpt('observe-links', default=False, + help='observe link discovery events.'), + cfg.BoolOpt('install-lldp-flow', default=True, + help='link discovery: explicitly install flow entry ' + 'to send lldp packet to controller'), + cfg.BoolOpt('explicit-drop', default=True, + help='link discovery: explicitly drop lldp packet in') +]) + + +class Port(object): + # This is data class passed by EventPortXXX + def __init__(self, dpid, ofproto, ofpport): + super(Port, self).__init__() + + self.dpid = dpid + self._ofproto = ofproto + self._config = ofpport.config + self._state = ofpport.state + + self.port_no = ofpport.port_no + self.hw_addr = ofpport.hw_addr + self.name = ofpport.name + self.currentFeatures = ofpport.curr + + def is_reserved(self): + return self.port_no > self._ofproto.OFPP_MAX + + def is_down(self): + return (self._state & self._ofproto.OFPPS_LINK_DOWN) > 0 \ + or (self._config & self._ofproto.OFPPC_PORT_DOWN) > 0 + + def is_live(self): + # NOTE: OF1.2 has OFPPS_LIVE state + # return (self._state & self._ofproto.OFPPS_LIVE) > 0 + return not self.is_down() + + def to_dict(self): + return {'dpid': dpid_to_str(self.dpid), + 'port_no': port_no_to_str(self.port_no), + 'hw_addr': self.hw_addr, + 'name': self.name.rstrip('\0')} + + # for Switch.del_port() + def __eq__(self, other): + return self.dpid == other.dpid and self.port_no == other.port_no + + def __ne__(self, other): + return not self.__eq__(other) + + def __hash__(self): + return hash((self.dpid, self.port_no)) + + def __str__(self): + LIVE_MSG = {False: 'DOWN', True: 'LIVE'} + return 'Port' % \ + (self.dpid, self.port_no, LIVE_MSG[self.is_live()]) + + +class Switch(object): + # This is data class passed by EventSwitchXXX + def __init__(self, dp): + super(Switch, self).__init__() + + self.dp = dp + self.ports = [] + + def add_port(self, ofpport): + port = Port(self.dp.id, self.dp.ofproto, ofpport) + if not port.is_reserved(): + self.ports.append(port) + + def del_port(self, ofpport): + self.ports.remove(Port(ofpport)) + + def to_dict(self): + d = {'dpid': dpid_to_str(self.dp.id), + 'ports': [port.to_dict() for port in self.ports]} + return d + + def __str__(self): + msg = 'Switch Host class + def __init__(self): + super(HostState, self).__init__() + + def add(self, host): + mac = host.mac + self.setdefault(mac, host) + + def update_vlan(self, host, vlan=None): + mac = host.mac + host = None + if mac in self: + host = self[mac] + + if not host: + return + + if vlan != None: + if vlan in host.vlan: + if host.vlan.index(vlan) == (len(host.vlan)-1): + return 'old' + host.vlan.remove(vlan) + host.vlan.append(vlan) + return 'new' + + def update_ip(self, host, ip_v4=None, ip_v6=None): + mac = host.mac + host = None + if mac in self: + host = self[mac] + + if not host: + return + + if ip_v4 != None: + if ip_v4 in host.ipv4: + if host.ipv4.index(ip_v4) == (len(host.ipv4)-1): + return 'old' + host.ipv4.remove(ip_v4) + host.ipv4.append(ip_v4) + return 'new' + + if ip_v6 != None: + if ip_v6 in host.ipv6: + if host.ipv6.index(ip_v6) == (len(host.ipv6)-1): + return 'old' + host.ipv6.remove(ip_v6) + host.ipv6.append(ip_v6) + return 'new' + + def get_by_dpid(self, dpid): + result = [] + + for mac in self: + host = self[mac] + if host.port.dpid == dpid: + result.append(host) + + return result + + +class PortState(dict): + # dict: int port_no -> OFPPort port + # OFPPort is defined in ryu.ofproto.ofproto_v1_X_parser + def __init__(self): + super(PortState, self).__init__() + + def add(self, port_no, port): + self[port_no] = port + + def remove(self, port_no): + del self[port_no] + + def modify(self, port_no, port): + self[port_no] = port + + +class PortData(object): + def __init__(self, is_down, lldp_data): + super(PortData, self).__init__() + self.is_down = is_down + self.lldp_data = lldp_data + self.timestamp = None + self.sent = 0 + + def lldp_sent(self): + self.timestamp = time.time() + self.sent += 1 + + def lldp_received(self): + self.sent = 0 + + def lldp_dropped(self): + return self.sent + + def clear_timestamp(self): + self.timestamp = None + + def set_down(self, is_down): + self.is_down = is_down + + def __str__(self): + return 'PortData' \ + % (not self.is_down, self.timestamp, self.sent) + + +class PortDataState(dict): + # dict: Port class -> PortData class + # slimed down version of OrderedDict as python 2.6 doesn't support it. + _PREV = 0 + _NEXT = 1 + _KEY = 2 + + def __init__(self): + super(PortDataState, self).__init__() + self._root = root = [] # sentinel node + root[:] = [root, root, None] # [_PREV, _NEXT, _KEY] + # doubly linked list + self._map = {} + + def _remove_key(self, key): + link_prev, link_next, key = self._map.pop(key) + link_prev[self._NEXT] = link_next + link_next[self._PREV] = link_prev + + def _append_key(self, key): + root = self._root + last = root[self._PREV] + last[self._NEXT] = root[self._PREV] = self._map[key] = [last, root, + key] + + def _prepend_key(self, key): + root = self._root + first = root[self._NEXT] + first[self._PREV] = root[self._NEXT] = self._map[key] = [root, first, + key] + + def _move_last_key(self, key): + self._remove_key(key) + self._append_key(key) + + def _move_front_key(self, key): + self._remove_key(key) + self._prepend_key(key) + + def add_port(self, port, lldp_data): + if port not in self: + self._prepend_key(port) + self[port] = PortData(port.is_down(), lldp_data) + else: + self[port].is_down = port.is_down() + + def lldp_sent(self, port): + port_data = self[port] + port_data.lldp_sent() + self._move_last_key(port) + return port_data + + def lldp_received(self, port): + self[port].lldp_received() + + def move_front(self, port): + port_data = self.get(port, None) + if port_data is not None: + port_data.clear_timestamp() + self._move_front_key(port) + + def set_down(self, port): + is_down = port.is_down() + port_data = self[port] + port_data.set_down(is_down) + port_data.clear_timestamp() + if not is_down: + self._move_front_key(port) + return is_down + + def get_port(self, port): + return self[port] + + def del_port(self, port): + del self[port] + self._remove_key(port) + + def __iter__(self): + root = self._root + curr = root[self._NEXT] + while curr is not root: + yield curr[self._KEY] + curr = curr[self._NEXT] + + def clear(self): + for node in self._map.values(): + del node[:] + root = self._root + root[:] = [root, root, None] + self._map.clear() + dict.clear(self) + + def items(self): + 'od.items() -> list of (key, value) pairs in od' + return [(key, self[key]) for key in self] + + def iteritems(self): + 'od.iteritems -> an iterator over the (key, value) pairs in od' + for k in self: + yield (k, self[k]) + + +class LinkState(dict): + # dict: Link class -> timestamp + def __init__(self): + super(LinkState, self).__init__() + self._map = {} + + def get_peer(self, src): + return self._map.get(src, None) + + def update_link(self, src, dst): + link = Link(src, dst) + + self[link] = time.time() + self._map[src] = dst + + # return if the reverse link is also up or not + rev_link = Link(dst, src) + return rev_link in self + + def link_down(self, link): + del self[link] + del self._map[link.src] + + def rev_link_set_timestamp(self, rev_link, timestamp): + # rev_link may or may not in LinkSet + if rev_link in self: + self[rev_link] = timestamp + + def port_deleted(self, src): + dst = self.get_peer(src) + if dst is None: + raise KeyError() + + link = Link(src, dst) + rev_link = Link(dst, src) + del self[link] + del self._map[src] + # reverse link might not exist + self.pop(rev_link, None) + rev_link_dst = self._map.pop(dst, None) + + return dst, rev_link_dst + + +class LLDPPacket(object): + # make a LLDP packet for link discovery. + + CHASSIS_ID_PREFIX = 'dpid:' + CHASSIS_ID_PREFIX_LEN = len(CHASSIS_ID_PREFIX) + CHASSIS_ID_FMT = CHASSIS_ID_PREFIX + '%s' + + PORT_ID_STR = '!I' # uint32_t + PORT_ID_SIZE = 4 + + class LLDPUnknownFormat(RyuException): + message = '%(msg)s' + + @staticmethod + def lldp_packet(dpid, port_no, dl_addr, ttl): + pkt = packet.Packet() + + dst = lldp.LLDP_MAC_NEAREST_BRIDGE + src = dl_addr + ethertype = ETH_TYPE_LLDP + eth_pkt = ethernet.ethernet(dst, src, ethertype) + pkt.add_protocol(eth_pkt) + + tlv_chassis_id = lldp.ChassisID( + subtype=lldp.ChassisID.SUB_LOCALLY_ASSIGNED, + chassis_id=(LLDPPacket.CHASSIS_ID_FMT % + dpid_to_str(dpid)).encode('ascii')) + + tlv_port_id = lldp.PortID(subtype=lldp.PortID.SUB_PORT_COMPONENT, + port_id=struct.pack( + LLDPPacket.PORT_ID_STR, + port_no)) + + tlv_ttl = lldp.TTL(ttl=ttl) + tlv_end = lldp.End() + + tlvs = (tlv_chassis_id, tlv_port_id, tlv_ttl, tlv_end) + lldp_pkt = lldp.lldp(tlvs) + pkt.add_protocol(lldp_pkt) + + pkt.serialize() + return pkt.data + + @staticmethod + def lldp_parse(data): + pkt = packet.Packet(data) + i = iter(pkt) + eth_pkt = six.next(i) + assert type(eth_pkt) == ethernet.ethernet + + lldp_pkt = six.next(i) + if type(lldp_pkt) != lldp.lldp: + raise LLDPPacket.LLDPUnknownFormat() + + tlv_chassis_id = lldp_pkt.tlvs[0] + if tlv_chassis_id.subtype != lldp.ChassisID.SUB_LOCALLY_ASSIGNED: + raise LLDPPacket.LLDPUnknownFormat( + msg='unknown chassis id subtype %d' % tlv_chassis_id.subtype) + chassis_id = tlv_chassis_id.chassis_id + if not chassis_id.startswith(LLDPPacket.CHASSIS_ID_PREFIX): + raise LLDPPacket.LLDPUnknownFormat( + msg='unknown chassis id format %s' % chassis_id) + src_dpid = str_to_dpid(chassis_id[LLDPPacket.CHASSIS_ID_PREFIX_LEN:]) + + tlv_port_id = lldp_pkt.tlvs[1] + if tlv_port_id.subtype != lldp.PortID.SUB_PORT_COMPONENT: + raise LLDPPacket.LLDPUnknownFormat( + msg='unknown port id subtype %d' % tlv_port_id.subtype) + port_id = tlv_port_id.port_id + if len(port_id) != LLDPPacket.PORT_ID_SIZE: + raise LLDPPacket.LLDPUnknownFormat( + msg='unknown port id %d' % port_id) + (src_port_no, ) = struct.unpack(LLDPPacket.PORT_ID_STR, port_id) + + return src_dpid, src_port_no + + +class Switches(app_manager.RyuApp): + OFP_VERSIONS = [ofproto_v1_0.OFP_VERSION, ofproto_v1_2.OFP_VERSION, + ofproto_v1_3.OFP_VERSION, ofproto_v1_4.OFP_VERSION] + _EVENTS = [event.EventSwitchEnter, event.EventSwitchLeave, + event.EventSwitchReconnected, + event.EventPortAdd, event.EventPortDelete, + event.EventPortModify, + event.EventLinkAdd, event.EventLinkDelete, + event.EventHostAdd] + + DEFAULT_TTL = 120 # unused. ignored. + LLDP_PACKET_LEN = len(LLDPPacket.lldp_packet(0, 0, DONTCARE_STR, 0)) + + LLDP_SEND_GUARD = .05 + LLDP_SEND_PERIOD_PER_PORT = .9 + TIMEOUT_CHECK_PERIOD = 5. + LINK_TIMEOUT = TIMEOUT_CHECK_PERIOD * 2 + LINK_LLDP_DROP = 5 + + def __init__(self, *args, **kwargs): + super(Switches, self).__init__(*args, **kwargs) + + self.name = 'switches' + self.dps = {} # datapath_id => Datapath class + self.port_state = {} # datapath_id => ports + self.ports = PortDataState() # Port class -> PortData class + self.links = LinkState() # Link class -> timestamp + self.hosts = HostState() # mac address -> Host class list + self.is_active = True + + self.link_discovery = self.CONF.observe_links + if self.link_discovery: + self.install_flow = self.CONF.install_lldp_flow + self.explicit_drop = self.CONF.explicit_drop + self.lldp_event = hub.Event() + self.link_event = hub.Event() + self.threads.append(hub.spawn(self.lldp_loop)) + self.threads.append(hub.spawn(self.link_loop)) + + def close(self): + self.is_active = False + if self.link_discovery: + self.lldp_event.set() + self.link_event.set() + hub.joinall(self.threads) + + def _register(self, dp): + assert dp.id is not None + + self.dps[dp.id] = dp + if dp.id not in self.port_state: + self.port_state[dp.id] = PortState() + for port in dp.ports.values(): + self.port_state[dp.id].add(port.port_no, port) + + def _unregister(self, dp): + if dp.id in self.dps: + if (self.dps[dp.id] == dp): + del self.dps[dp.id] + del self.port_state[dp.id] + + def _get_switch(self, dpid): + if dpid in self.dps: + switch = Switch(self.dps[dpid]) + for ofpport in self.port_state[dpid].values(): + switch.add_port(ofpport) + return switch + + def _get_port(self, dpid, port_no): + switch = self._get_switch(dpid) + if switch: + for p in switch.ports: + if p.port_no == port_no: + return p + + def _port_added(self, port): + lldp_data = LLDPPacket.lldp_packet( + port.dpid, port.port_no, port.hw_addr, self.DEFAULT_TTL) + self.ports.add_port(port, lldp_data) + # LOG.debug('_port_added dpid=%s, port_no=%s, live=%s', + # port.dpid, port.port_no, port.is_live()) + + def _link_down(self, port): + try: + dst, rev_link_dst = self.links.port_deleted(port) + except KeyError: + # LOG.debug('key error. src=%s, dst=%s', + # port, self.links.get_peer(port)) + return + link = Link(port, dst) + self.send_event_to_observers(event.EventLinkDelete(link)) + if rev_link_dst: + rev_link = Link(dst, rev_link_dst) + self.send_event_to_observers(event.EventLinkDelete(rev_link)) + self.ports.move_front(dst) + + def _is_edge_port(self, port): + for link in self.links: + if port == link.src or port == link.dst: + return False + + return True + + @set_ev_cls(ofp_event.EventOFPStateChange, + [MAIN_DISPATCHER, DEAD_DISPATCHER]) + def state_change_handler(self, ev): + dp = ev.datapath + assert dp is not None + LOG.debug(dp) + + if ev.state == MAIN_DISPATCHER: + dp_multiple_conns = False + if dp.id in self.dps: + LOG.warning('Multiple connections from %s', dpid_to_str(dp.id)) + dp_multiple_conns = True + (self.dps[dp.id]).close() + + self._register(dp) + switch = self._get_switch(dp.id) + LOG.debug('register %s', switch) + + if not dp_multiple_conns: + self.send_event_to_observers(event.EventSwitchEnter(switch)) + else: + self.send_event_to_observers(event.EventSwitchReconnected(switch)) + + if not self.link_discovery: + return + + if self.install_flow: + ofproto = dp.ofproto + ofproto_parser = dp.ofproto_parser + + # TODO:XXX need other versions + if ofproto.OFP_VERSION == ofproto_v1_0.OFP_VERSION: + rule = nx_match.ClsRule() + rule.set_dl_dst(addrconv.mac.text_to_bin( + lldp.LLDP_MAC_NEAREST_BRIDGE)) + rule.set_dl_type(ETH_TYPE_LLDP) + actions = [ofproto_parser.OFPActionOutput( + ofproto.OFPP_CONTROLLER, self.LLDP_PACKET_LEN)] + dp.send_flow_mod( + rule=rule, cookie=0, command=ofproto.OFPFC_ADD, + idle_timeout=0, hard_timeout=0, actions=actions, + priority=0xFFFF) + elif ofproto.OFP_VERSION >= ofproto_v1_2.OFP_VERSION: + match = ofproto_parser.OFPMatch( + eth_type=ETH_TYPE_LLDP, + eth_dst=lldp.LLDP_MAC_NEAREST_BRIDGE) + # OFPCML_NO_BUFFER is set so that the LLDP is not + # buffered on switch + parser = ofproto_parser + actions = [parser.OFPActionOutput(ofproto.OFPP_CONTROLLER, + ofproto.OFPCML_NO_BUFFER + )] + inst = [parser.OFPInstructionActions( + ofproto.OFPIT_APPLY_ACTIONS, actions)] + mod = parser.OFPFlowMod(datapath=dp, match=match, + idle_timeout=0, hard_timeout=0, + instructions=inst, + priority=0xFFFF) + dp.send_msg(mod) + else: + LOG.error('cannot install flow. unsupported version. %x', + dp.ofproto.OFP_VERSION) + + # Do not add ports while dp has multiple connections to controller. + if not dp_multiple_conns: + for port in switch.ports: + if not port.is_reserved(): + self._port_added(port) + + self.lldp_event.set() + + elif ev.state == DEAD_DISPATCHER: + # dp.id is None when datapath dies before handshake + if dp.id is None: + return + + # if switch delete + # then delete host on it + hostlist = self.hosts.get_by_dpid(dp.id) + for host in hostlist: + ev = event.EventHostDelete(host) + self.send_event_to_observers(ev) + del self.hosts[host.mac] + + switch = self._get_switch(dp.id) + if switch: + if switch.dp is dp: + self._unregister(dp) + LOG.debug('unregister %s', switch) + + self.send_event_to_observers(event.EventSwitchLeave(switch)) + + if not self.link_discovery: + return + + for port in switch.ports: + if not port.is_reserved(): + self.ports.del_port(port) + self._link_down(port) + self.lldp_event.set() + + @set_ev_cls(ofp_event.EventOFPPortStatus, MAIN_DISPATCHER) + def port_status_handler(self, ev): + msg = ev.msg + reason = msg.reason + dp = msg.datapath + ofpport = msg.desc + + if reason == dp.ofproto.OFPPR_ADD: + # LOG.debug('A port was added.' + + # '(datapath id = %s, port number = %s)', + # dp.id, ofpport.port_no) + self.port_state[dp.id].add(ofpport.port_no, ofpport) + self.send_event_to_observers( + event.EventPortAdd(Port(dp.id, dp.ofproto, ofpport))) + + if not self.link_discovery: + return + + port = self._get_port(dp.id, ofpport.port_no) + if port and not port.is_reserved(): + self._port_added(port) + self.lldp_event.set() + + elif reason == dp.ofproto.OFPPR_DELETE: + # LOG.debug('A port was deleted.' + + # '(datapath id = %s, port number = %s)', + # dp.id, ofpport.port_no) + self.port_state[dp.id].remove(ofpport.port_no) + self.send_event_to_observers( + event.EventPortDelete(Port(dp.id, dp.ofproto, ofpport))) + + if not self.link_discovery: + return + + port = self._get_port(dp.id, ofpport.port_no) + if port and not port.is_reserved(): + # if port delete + # then delete host on it + for host in self.hosts.values(): + if port.__eq__(host.port): + ev = event.EventHostDelete(host) + self.send_event_to_observers(ev) + del self.hosts[host.mac] + break + self.ports.del_port(port) + self._link_down(port) + self.lldp_event.set() + + else: + assert reason == dp.ofproto.OFPPR_MODIFY + # LOG.debug('A port was modified.' + + # '(datapath id = %s, port number = %s)', + # dp.id, ofpport.port_no) + self.port_state[dp.id].modify(ofpport.port_no, ofpport) + self.send_event_to_observers( + event.EventPortModify(Port(dp.id, dp.ofproto, ofpport))) + + if not self.link_discovery: + return + + port = self._get_port(dp.id, ofpport.port_no) + if port and not port.is_reserved(): + if self.ports.set_down(port): + # if port down + # then delete host on it + for host in self.hosts.values(): + if port.__eq__(host.port): + ev = event.EventHostDelete(host) + self.send_event_to_observers(ev) + del self.hosts[host.mac] + break + self._link_down(port) + self.lldp_event.set() + + @staticmethod + def _drop_packet(msg): + buffer_id = msg.buffer_id + if buffer_id == msg.datapath.ofproto.OFP_NO_BUFFER: + return + + dp = msg.datapath + # TODO:XXX + if dp.ofproto.OFP_VERSION == ofproto_v1_0.OFP_VERSION: + dp.send_packet_out(buffer_id, msg.in_port, []) + elif dp.ofproto.OFP_VERSION >= ofproto_v1_2.OFP_VERSION: + dp.send_packet_out(buffer_id, msg.match['in_port'], []) + else: + LOG.error('cannot drop_packet. unsupported version. %x', + dp.ofproto.OFP_VERSION) + + @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER) + def lldp_packet_in_handler(self, ev): + if not self.link_discovery: + return + + msg = ev.msg + try: + src_dpid, src_port_no = LLDPPacket.lldp_parse(msg.data) + except LLDPPacket.LLDPUnknownFormat as e: + # This handler can receive all the packets which can be + # not-LLDP packet. Ignore it silently + return + + dst_dpid = msg.datapath.id + if msg.datapath.ofproto.OFP_VERSION == ofproto_v1_0.OFP_VERSION: + dst_port_no = msg.in_port + elif msg.datapath.ofproto.OFP_VERSION >= ofproto_v1_2.OFP_VERSION: + dst_port_no = msg.match['in_port'] + else: + LOG.error('cannot accept LLDP. unsupported version. %x', + msg.datapath.ofproto.OFP_VERSION) + + src = self._get_port(src_dpid, src_port_no) + if not src or src.dpid == dst_dpid: + return + try: + self.ports.lldp_received(src) + except KeyError: + # There are races between EventOFPPacketIn and + # EventDPPortAdd. So packet-in event can happend before + # port add event. In that case key error can happend. + # LOG.debug('lldp_received: KeyError %s', e) + pass + + dst = self._get_port(dst_dpid, dst_port_no) + if not dst: + return + + old_peer = self.links.get_peer(src) + # LOG.debug("Packet-In") + # LOG.debug(" src=%s", src) + # LOG.debug(" dst=%s", dst) + # LOG.debug(" old_peer=%s", old_peer) + if old_peer and old_peer != dst: + old_link = Link(src, old_peer) + del self.links[old_link] + self.send_event_to_observers(event.EventLinkDelete(old_link)) + + link = Link(src, dst) + if link not in self.links: + self.send_event_to_observers(event.EventLinkAdd(link)) + + # remove hosts from edge port + for host in self.hosts.values(): + if not self._is_edge_port(host.port): + del self.hosts[host.mac] + + if not self.links.update_link(src, dst): + # reverse link is not detected yet. + # So schedule the check early because it's very likely it's up + self.ports.move_front(dst) + self.lldp_event.set() + if self.explicit_drop: + self._drop_packet(msg) + + @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER) + def host_discovery_packet_in_handler(self, ev): + msg = ev.msg + pkt = packet.Packet(msg.data) + eth = pkt.get_protocols(ethernet.ethernet)[0] + + # ignore lldp packet + if eth.ethertype == ETH_TYPE_LLDP: + return + + datapath = msg.datapath + dpid = datapath.id + port_no = -1 + + if msg.datapath.ofproto.OFP_VERSION == ofproto_v1_0.OFP_VERSION: + port_no = msg.in_port + else: + port_no = msg.match['in_port'] + + port = self._get_port(dpid, port_no) + + # can't find this port(ex: logic port) + if not port: + return + # ignore switch-to-switch port + if not self._is_edge_port(port): + return + + host_mac = eth.src + host = Host(host_mac, port) + + if host_mac not in self.hosts: + self.hosts.add(host) + ev = event.EventHostAdd(host) + self.send_event_to_observers(ev) + + # vlan packet, update vlan tag + vlan_pkt = pkt.get_protocol(vlan.vlan) + if vlan_pkt: + ans = self.hosts.update_vlan(host, vlan=vlan_pkt.vid) + if ans == 'new': + ev = event.EventHostAdd(host) + self.send_event_to_observers(ev) + + # arp packet, update ip address + if eth.ethertype == ether_types.ETH_TYPE_ARP: + arp_pkt = pkt.get_protocols(arp.arp)[0] + ans = self.hosts.update_ip(host, ip_v4=arp_pkt.src_ip) + if ans == 'new': + ev = event.EventHostAdd(host) + self.send_event_to_observers(ev) + + # ipv4 packet, update ipv4 address + elif eth.ethertype == ether_types.ETH_TYPE_IP: + ipv4_pkt = pkt.get_protocols(ipv4.ipv4)[0] + ans = self.hosts.update_ip(host, ip_v4=ipv4_pkt.src) + if ans == 'new': + ev = event.EventHostAdd(host) + self.send_event_to_observers(ev) + + # ipv6 packet, update ipv6 address + elif eth.ethertype == ether_types.ETH_TYPE_IPV6: + # TODO: need to handle NDP + ipv6_pkt = pkt.get_protocols(ipv6.ipv6)[0] + self.hosts.update_ip(host, ip_v6=ipv6_pkt.src) + + def send_lldp_packet(self, port): + try: + port_data = self.ports.lldp_sent(port) + except KeyError as e: + # ports can be modified during our sleep in self.lldp_loop() + # LOG.debug('send_lldp: KeyError %s', e) + return + if port_data.is_down: + return + + dp = self.dps.get(port.dpid, None) + if dp is None: + # datapath was already deleted + return + + # LOG.debug('lldp sent dpid=%s, port_no=%d', dp.id, port.port_no) + # TODO:XXX + if dp.ofproto.OFP_VERSION == ofproto_v1_0.OFP_VERSION: + actions = [dp.ofproto_parser.OFPActionOutput(port.port_no)] + dp.send_packet_out(actions=actions, data=port_data.lldp_data) + elif dp.ofproto.OFP_VERSION >= ofproto_v1_2.OFP_VERSION: + actions = [dp.ofproto_parser.OFPActionOutput(port.port_no)] + out = dp.ofproto_parser.OFPPacketOut( + datapath=dp, in_port=dp.ofproto.OFPP_CONTROLLER, + buffer_id=dp.ofproto.OFP_NO_BUFFER, actions=actions, + data=port_data.lldp_data) + dp.send_msg(out) + else: + LOG.error('cannot send lldp packet. unsupported version. %x', + dp.ofproto.OFP_VERSION) + + def lldp_loop(self): + while self.is_active: + self.lldp_event.clear() + + now = time.time() + timeout = None + ports_now = [] + ports = [] + for (key, data) in self.ports.items(): + if data.timestamp is None: + ports_now.append(key) + continue + + expire = data.timestamp + self.LLDP_SEND_PERIOD_PER_PORT + if expire <= now: + ports.append(key) + continue + + timeout = expire - now + break + + for port in ports_now: + self.send_lldp_packet(port) + for port in ports: + self.send_lldp_packet(port) + hub.sleep(self.LLDP_SEND_GUARD) # don't burst + + if timeout is not None and ports: + timeout = 0 # We have already slept + # LOG.debug('lldp sleep %s', timeout) + self.lldp_event.wait(timeout=timeout) + + def link_loop(self): + while self.is_active: + self.link_event.clear() + + now = time.time() + deleted = [] + for (link, timestamp) in self.links.items(): + # LOG.debug('%s timestamp %d (now %d)', link, timestamp, now) + if timestamp + self.LINK_TIMEOUT < now: + src = link.src + if src in self.ports: + port_data = self.ports.get_port(src) + # LOG.debug('port_data %s', port_data) + if port_data.lldp_dropped() > self.LINK_LLDP_DROP: + deleted.append(link) + + for link in deleted: + self.links.link_down(link) + # LOG.debug('delete %s', link) + self.send_event_to_observers(event.EventLinkDelete(link)) + + dst = link.dst + rev_link = Link(dst, link.src) + if rev_link not in deleted: + # It is very likely that the reverse link is also + # disconnected. Check it early. + expire = now - self.LINK_TIMEOUT + self.links.rev_link_set_timestamp(rev_link, expire) + if dst in self.ports: + self.ports.move_front(dst) + self.lldp_event.set() + + self.link_event.wait(timeout=self.TIMEOUT_CHECK_PERIOD) + + @set_ev_cls(event.EventSwitchRequest) + def switch_request_handler(self, req): + # LOG.debug(req) + dpid = req.dpid + + switches = [] + if dpid is None: + # reply all list + for dp in self.dps.values(): + switches.append(self._get_switch(dp.id)) + elif dpid in self.dps: + switches.append(self._get_switch(dpid)) + + rep = event.EventSwitchReply(req.src, switches) + self.reply_to_request(req, rep) + + @set_ev_cls(event.EventLinkRequest) + def link_request_handler(self, req): + # LOG.debug(req) + dpid = req.dpid + + if dpid is None: + links = self.links + else: + links = [link for link in self.links if link.src.dpid == dpid] + rep = event.EventLinkReply(req.src, dpid, links) + self.reply_to_request(req, rep) + + @set_ev_cls(event.EventHostRequest) + def host_request_handler(self, req): + dpid = req.dpid + hosts = [] + if dpid is None: + for mac in self.hosts: + hosts.append(self.hosts[mac]) + else: + hosts = self.hosts.get_by_dpid(dpid) + + rep = event.EventHostReply(req.src, dpid, hosts) + self.reply_to_request(req, rep) From f5bc335ee2eb89c0cb4b5ddef491137c53a90a69 Mon Sep 17 00:00:00 2001 From: HsiaoHsuan Date: Thu, 3 Mar 2016 02:43:12 -0800 Subject: [PATCH 15/31] Update switches.py --- adapter/ryu/switches.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/adapter/ryu/switches.py b/adapter/ryu/switches.py index 5721f37..31c24fd 100644 --- a/adapter/ryu/switches.py +++ b/adapter/ryu/switches.py @@ -530,7 +530,7 @@ class Switches(app_manager.RyuApp): event.EventPortAdd, event.EventPortDelete, event.EventPortModify, event.EventLinkAdd, event.EventLinkDelete, - event.EventHostAdd] + event.EventHostAdd, event.EventHostDelete] DEFAULT_TTL = 120 # unused. ignored. LLDP_PACKET_LEN = len(LLDPPacket.lldp_packet(0, 0, DONTCARE_STR, 0)) From eec21b7286c03f6a998e515233f136a6f5e27eaa Mon Sep 17 00:00:00 2001 From: HsiaoHsuan Date: Thu, 3 Mar 2016 03:11:25 -0800 Subject: [PATCH 16/31] Update switches.py --- adapter/ryu/switches.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/adapter/ryu/switches.py b/adapter/ryu/switches.py index 31c24fd..ef7ea6a 100644 --- a/adapter/ryu/switches.py +++ b/adapter/ryu/switches.py @@ -705,9 +705,9 @@ def state_change_handler(self, ev): # then delete host on it hostlist = self.hosts.get_by_dpid(dp.id) for host in hostlist: + del self.hosts[host.mac] ev = event.EventHostDelete(host) self.send_event_to_observers(ev) - del self.hosts[host.mac] switch = self._get_switch(dp.id) if switch: @@ -766,9 +766,9 @@ def port_status_handler(self, ev): # then delete host on it for host in self.hosts.values(): if port.__eq__(host.port): + del self.hosts[host.mac] ev = event.EventHostDelete(host) self.send_event_to_observers(ev) - del self.hosts[host.mac] break self.ports.del_port(port) self._link_down(port) @@ -793,9 +793,9 @@ def port_status_handler(self, ev): # then delete host on it for host in self.hosts.values(): if port.__eq__(host.port): + del self.hosts[host.mac] ev = event.EventHostDelete(host) self.send_event_to_observers(ev) - del self.hosts[host.mac] break self._link_down(port) self.lldp_event.set() From f93a5fb5e71259f06fe4049992ca098af860e864 Mon Sep 17 00:00:00 2001 From: HsiaoHsuan Date: Thu, 3 Mar 2016 03:42:07 -0800 Subject: [PATCH 17/31] Update switches.py --- adapter/ryu/switches.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/adapter/ryu/switches.py b/adapter/ryu/switches.py index ef7ea6a..17a483c 100644 --- a/adapter/ryu/switches.py +++ b/adapter/ryu/switches.py @@ -705,9 +705,9 @@ def state_change_handler(self, ev): # then delete host on it hostlist = self.hosts.get_by_dpid(dp.id) for host in hostlist: - del self.hosts[host.mac] ev = event.EventHostDelete(host) self.send_event_to_observers(ev) + del self.hosts[host.mac] switch = self._get_switch(dp.id) if switch: @@ -766,9 +766,9 @@ def port_status_handler(self, ev): # then delete host on it for host in self.hosts.values(): if port.__eq__(host.port): - del self.hosts[host.mac] ev = event.EventHostDelete(host) self.send_event_to_observers(ev) + del self.hosts[host.mac] break self.ports.del_port(port) self._link_down(port) @@ -793,9 +793,9 @@ def port_status_handler(self, ev): # then delete host on it for host in self.hosts.values(): if port.__eq__(host.port): - del self.hosts[host.mac] ev = event.EventHostDelete(host) self.send_event_to_observers(ev) + del self.hosts[host.mac] break self._link_down(port) self.lldp_event.set() @@ -871,6 +871,8 @@ def lldp_packet_in_handler(self, ev): # remove hosts from edge port for host in self.hosts.values(): if not self._is_edge_port(host.port): + ev = event.EventHostDelete(host) + self.send_event_to_observers(ev) del self.hosts[host.mac] if not self.links.update_link(src, dst): From b4e8ef2638af07d0ad442a066627bfb02d1d2ad6 Mon Sep 17 00:00:00 2001 From: HsiaoHsuan Date: Fri, 4 Mar 2016 19:59:04 -0800 Subject: [PATCH 18/31] Update the mean to calculate capacity --- core/etc/config.json | 6 ++ .../src/floodlight_modules/busylink_detect.py | 69 +++++++++++++++---- 2 files changed, 60 insertions(+), 15 deletions(-) diff --git a/core/etc/config.json b/core/etc/config.json index 1618a53..7ed4795 100644 --- a/core/etc/config.json +++ b/core/etc/config.json @@ -14,5 +14,11 @@ "controller_port": "6633", "controller_name": "foobar", "adapter_port": "8080" + }, + "BusyLink_Detect": { + "ip": "localhost", + "port": "5567", + "interval": "5", + "version": "1.0" } } diff --git a/core/src/floodlight_modules/busylink_detect.py b/core/src/floodlight_modules/busylink_detect.py index 9a05b7d..297e188 100644 --- a/core/src/floodlight_modules/busylink_detect.py +++ b/core/src/floodlight_modules/busylink_detect.py @@ -8,6 +8,7 @@ def __init__(self,core,parm): """ BusyLinkDetect init""" self.coreIP = "localhost" self.corePort = "5567" + self.openflowVersion = "1.0" self.baseState = 1 self.finalState = 3 @@ -18,6 +19,16 @@ def __init__(self,core,parm): self.switches = {} self.BLD_result = [] + # Load config + if parm: + if parm.has_key("ip"): + self.coreIP = parm["ip"] + if parm.has_key("port"): + self.corePort = parm["port"] + if parm.has_key("version"): + self.openflowVersion = parm["version"] + + # Get link and port information from nwinfo core.registerEventHandler("linkbag", self.getLink) core.registerEventHandler("portbag", self.getPort) @@ -50,7 +61,10 @@ def getPort(self, portsbag): for port in data: if int(port['capacity']) == 0: continue - self.capacity["%s_%d" % (port['dpid'],int(port['port']))] = self.parsePortFeatures(int(port['capacity'])) + if self.openflowVersion == "1.3" + self.capacity["%s_%d" % (port['dpid'],int(port['port']))] = self.parsePortFeatures_v1_3(int(port['capacity'])) + else: + self.capacity["%s_%d" % (port['dpid'],int(port['port']))] = self.parsePortFeatures_v1_0(int(port['capacity'])) if port['dpid'] in self.switches: self.switches[port['dpid']][int(port['port'])] = int(port['rxbyte']) else: @@ -66,39 +80,64 @@ def overthreshold(self,id): if self.statistics[id]['state'] >= self.finalState: self.statistics[id]['state'] = self.finalState self.BLD_result.append(id) - + def underthreshold(self,id): self.statistics[id]['state'] -= 1 if self.statistics[id]['state'] < self.baseState: self.statistics[id]['state'] = self.baseState - - def parsePortFeatures(self,features): + + def parsePortFeatures_v1_0(self,features): if features == 0: return 0 turn_binary = bin(features)[2:] binary_len = len(turn_binary) if binary_len < 12: turn_binary = '0'*(12-binary_len) + turn_binary - + if turn_binary[5] == '1': return 10*(1024**3)/8.0 #10Gb if turn_binary[6] == '1' or turn_binary[7] == '1': - return 1024**3/8.0 #1Gb + return 1024**3/8.0 #1Gb if turn_binary[8] == '1' or turn_binary[9] == '1': - return 100*(1024**2)/8.0 #100Mb + return 100*(1024**2)/8.0 #100Mb + if turn_binary[10] == '1' or turn_binary[11] == '1': + return 10*(1024**2)/8.0 #10Mb + return 0 + + def parsePortFeatures_v1_3(self,features): + if features == 0: + return 0 + turn_binary = bin(features)[2:] + binary_len = len(turn_binary) + if binary_len < 12: + turn_binary = '0'*(12-binary_len) + turn_binary + + if turn_binary[2] == '1': + return 1024**4/8.0 #1Tb + if turn_binary[3] == '1': + return 100*(1024**3)/8.0 #100Gb + if turn_binary[4] == '1': + return 40*(1024**3)/8.0 #40Gb + if turn_binary[5] == '1': + return 10*(1024**3)/8.0 #10Gb + if turn_binary[6] == '1' or turn_binary[7] == '1': + return 1024**3/8.0 #1Gb + if turn_binary[8] == '1' or turn_binary[9] == '1': + return 100*(1024**2)/8.0 #100Mb if turn_binary[10] == '1' or turn_binary[11] == '1': return 10*(1024**2)/8.0 #10Mb return 0 def busyLinkDetect(self): self.BLD_result = [] - #calculate link's countBytes and capacity + # Calculate link's countBytes and capacity for link_id in self.links: src = self.links[link_id]['source'] srcp = self.links[link_id]['sourcePort'] dest = self.links[link_id]['target'] destp = self.links[link_id]['targetPort'] + # Check if needed information all arrived if (src not in self.switches) | (dest not in self.switches): print 'Not Ready' return @@ -109,14 +148,14 @@ def busyLinkDetect(self): total_bytes = self.switches[src][srcp] + self.switches[dest][destp] self.links[link_id]['countBytes'] = total_bytes self.links[link_id]['capacity'] = min(self.capacity["%s_%d" % (src,srcp)],self.capacity["%s_%d" % (dest,destp)]) - - #initialize self.statistics value + + # Initialize self.statistics value if len(self.statistics) == 0: self.statistics = dict(self.links) for link_id in self.statistics: self.statistics[link_id]['state'] = self.baseState - - #check threshold + + # Check threshold for link_id in self.links: if link_id in self.statistics: if (self.links[link_id]['countBytes'] - self.statistics[link_id]['countBytes']) / self.statistics[link_id]['capacity'] >= self.threshold: @@ -127,12 +166,12 @@ def busyLinkDetect(self): else: self.statistics[link_id] = dict(self.links[link_id]) self.statistics[link_id]['state'] = self.baseState - #remove unexisted link info + # Remove unexisted link info for link_id in self.statistics: if link_id not in self.links: del self.statistics[link_id] - - #return result + + # Return result if len(self.BLD_result) > 0: data = {} for i in range(len(self.BLD_result)): From 731c6d9d8a3aa64487705ebc9cd8e610f02c5cee Mon Sep 17 00:00:00 2001 From: HsiaoHsuan Date: Fri, 4 Mar 2016 20:10:21 -0800 Subject: [PATCH 19/31] Update busylink --- core/src/floodlight_modules/busylink_detect.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/floodlight_modules/busylink_detect.py b/core/src/floodlight_modules/busylink_detect.py index 297e188..5ee15a7 100644 --- a/core/src/floodlight_modules/busylink_detect.py +++ b/core/src/floodlight_modules/busylink_detect.py @@ -61,7 +61,7 @@ def getPort(self, portsbag): for port in data: if int(port['capacity']) == 0: continue - if self.openflowVersion == "1.3" + if self.openflowVersion == "1.3": self.capacity["%s_%d" % (port['dpid'],int(port['port']))] = self.parsePortFeatures_v1_3(int(port['capacity'])) else: self.capacity["%s_%d" % (port['dpid'],int(port['port']))] = self.parsePortFeatures_v1_0(int(port['capacity'])) From ef945a44d5cdfb3bc1c0e209a411e0b08f0ce78a Mon Sep 17 00:00:00 2001 From: HsiaoHsuan Date: Thu, 17 Mar 2016 02:59:36 -0700 Subject: [PATCH 20/31] Modify the message of busylink --- core/src/floodlight_modules/busylink_detect.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/core/src/floodlight_modules/busylink_detect.py b/core/src/floodlight_modules/busylink_detect.py index 5ee15a7..7142b0a 100644 --- a/core/src/floodlight_modules/busylink_detect.py +++ b/core/src/floodlight_modules/busylink_detect.py @@ -175,7 +175,9 @@ def busyLinkDetect(self): if len(self.BLD_result) > 0: data = {} for i in range(len(self.BLD_result)): - data[i] = self.BLD_result[i] + busylinkName = 'link' + str(i) + data[busylinkName] = [{'dpid': self.links[self.BLD_result[i]]['source'], 'port': self.links[self.BLD_result[i]]['sourcePort']}, + {'dpid': self.links[self.BLD_result[i]]['target'], 'port': self.links[self.BLD_result[i]]['targetPort']}] print '*****Busy Link ID*****' print json.dumps(data) From a0ff7624d8bc935c12aebd12f07cfa5c454c6345 Mon Sep 17 00:00:00 2001 From: HsiaoHsuan Date: Thu, 17 Mar 2016 07:24:33 -0700 Subject: [PATCH 21/31] Modify the message of busylink --- core/src/floodlight_modules/busylink_detect.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/core/src/floodlight_modules/busylink_detect.py b/core/src/floodlight_modules/busylink_detect.py index 7142b0a..6f027d9 100644 --- a/core/src/floodlight_modules/busylink_detect.py +++ b/core/src/floodlight_modules/busylink_detect.py @@ -173,14 +173,12 @@ def busyLinkDetect(self): # Return result if len(self.BLD_result) > 0: - data = {} - for i in range(len(self.BLD_result)): - busylinkName = 'link' + str(i) - data[busylinkName] = [{'dpid': self.links[self.BLD_result[i]]['source'], 'port': self.links[self.BLD_result[i]]['sourcePort']}, - {'dpid': self.links[self.BLD_result[i]]['target'], 'port': self.links[self.BLD_result[i]]['targetPort']}] - print '*****Busy Link ID*****' - print json.dumps(data) + for i in range(len(self.BLD_result)): + data = {'link': []} + data['link'].append({'dpid': self.links[self.BLD_result[i]]['source'], 'port': self.links[self.BLD_result[i]]['sourcePort']}) + data['link'].append({'dpid': self.links[self.BLD_result[i]]['target'], 'port': self.links[self.BLD_result[i]]['targetPort']}) + print json.dumps(data) else: print 'No BusyLink' #conn = httplib.HTTPConnection(self.coreIP,self.corePort) From 1e44c08db9436e0f4ee202913b153cd40286c17c Mon Sep 17 00:00:00 2001 From: HsiaoHsuan Date: Thu, 24 Mar 2016 05:12:41 -0700 Subject: [PATCH 22/31] Add controller name to busylink --- core/etc/config.json | 1 + core/src/floodlight_modules/busylink_detect.py | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/core/etc/config.json b/core/etc/config.json index 7ed4795..f2515e4 100644 --- a/core/etc/config.json +++ b/core/etc/config.json @@ -18,6 +18,7 @@ "BusyLink_Detect": { "ip": "localhost", "port": "5567", + "controller_name": "foobar", "interval": "5", "version": "1.0" } diff --git a/core/src/floodlight_modules/busylink_detect.py b/core/src/floodlight_modules/busylink_detect.py index 6f027d9..26e5373 100644 --- a/core/src/floodlight_modules/busylink_detect.py +++ b/core/src/floodlight_modules/busylink_detect.py @@ -8,6 +8,7 @@ def __init__(self,core,parm): """ BusyLinkDetect init""" self.coreIP = "localhost" self.corePort = "5567" + self.controllerName = "DEFAULT" self.openflowVersion = "1.0" self.baseState = 1 @@ -25,6 +26,8 @@ def __init__(self,core,parm): self.coreIP = parm["ip"] if parm.has_key("port"): self.corePort = parm["port"] + if parm.has_key("controller_name"): + self.controllerName = parm["controller_name"] if parm.has_key("version"): self.openflowVersion = parm["version"] @@ -175,7 +178,7 @@ def busyLinkDetect(self): if len(self.BLD_result) > 0: print '*****Busy Link ID*****' for i in range(len(self.BLD_result)): - data = {'link': []} + data = {'link': [], 'controller': self.controllerName} data['link'].append({'dpid': self.links[self.BLD_result[i]]['source'], 'port': self.links[self.BLD_result[i]]['sourcePort']}) data['link'].append({'dpid': self.links[self.BLD_result[i]]['target'], 'port': self.links[self.BLD_result[i]]['targetPort']}) print json.dumps(data) From f32cbaa08c67f2210dde837d80034ac6de923146 Mon Sep 17 00:00:00 2001 From: hsiaohsuan1l1l Date: Tue, 10 May 2016 12:50:13 +0800 Subject: [PATCH 23/31] Fix duplicated event.py and switches.py --- adapter/ryu/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/adapter/ryu/README.md b/adapter/ryu/README.md index 09c9ceb..e0f1828 100644 --- a/adapter/ryu/README.md +++ b/adapter/ryu/README.md @@ -5,8 +5,8 @@ Ryu adapter module for OmniUI ###Installation### 1. Download the Ryu Controller $ `git clone git://github.com/osrg/ryu.git` -$ `cd OpenADM/adapter/ryu; cp event.py event.py ~/ryu/ryu/topology` -$ `cp switches.py switches.py ~/ryu/ryu/topology` +$ `cd OpenADM/adapter/ryu; cp event.py ~/ryu/ryu/topology` +$ `cp switches.py ~/ryu/ryu/topology` $ `cd ~/ryu; sudo python ./setup.py install` ###Execution### From 3b5a3aaa92d0107e4fafbd2542893b0cc537da30 Mon Sep 17 00:00:00 2001 From: HsiaoHsuan Date: Wed, 11 May 2016 01:28:17 -0700 Subject: [PATCH 24/31] Fix the hard coded coreIP --- adapter/ryu/omniui/omniui.py | 46 +++++++++++++++++++++++++----------- 1 file changed, 32 insertions(+), 14 deletions(-) diff --git a/adapter/ryu/omniui/omniui.py b/adapter/ryu/omniui/omniui.py index 8ab975c..9d0edd7 100644 --- a/adapter/ryu/omniui/omniui.py +++ b/adapter/ryu/omniui/omniui.py @@ -28,6 +28,10 @@ global controllerName controllerName = 'DEFAULT' +global coreIP +coreIP = '127.0.0.1' +global corePort +corePort = '5567' class OmniUI(app_manager.RyuApp): OFP_VERSIONS = [ofproto_v1_0.OFP_VERSION, ofproto_v1_2.OFP_VERSION, ofproto_v1_3.OFP_VERSION] @@ -139,7 +143,8 @@ def add_device_handler(self, ev): self.port_to_feature[addDevice['dpid']] = {} print json.dumps(addDevice) - self.post_json_to_core("http://localhost:5567/publish/adddevice", json.dumps(addDevice)) + tmpIP = "http://" + coreIP + ":" + corePort + "/publish/adddevice" + self.post_json_to_core(tmpIP, json.dumps(addDevice)) # send add port event for port in switch.ports: @@ -169,7 +174,8 @@ def del_device_handler(self, ev): del self.port_to_feature[delDevice['dpid']] print json.dumps(delDevice) - self.post_json_to_core("http://localhost:5567/publish/deldevice", json.dumps(delDevice)) + tmpIP = "http://" + coreIP + ":" + corePort + "/publish/deldevice" + self.post_json_to_core(tmpIP, json.dumps(delDevice)) # # handle modify port event @@ -199,7 +205,8 @@ def mod_port_handler(self, ev): del self.port_to_feature[modPort['dpid']][modPort['port']] print json.dumps(modPort) - self.post_json_to_core("http://localhost:5567/publish/delport", json.dumps(modPort)) + tmpIP = "http://" + coreIP + ":" + corePort + "/publish/delport" + self.post_json_to_core(tmpIP, json.dumps(modPort)) else: print '***live***' @@ -219,7 +226,8 @@ def mod_port_handler(self, ev): self.port_to_feature[modPort['dpid']][modPort['port']] = str(port.currentFeatures) print json.dumps(modPort) - self.post_json_to_core("http://localhost:5567/publish/addport", json.dumps(modPort)) + tmpIP = "http://" + coreIP + ":" + corePort + "/publish/addport" + self.post_json_to_core(tmpIP, json.dumps(modPort)) # # handle add port event @@ -245,7 +253,8 @@ def add_port_handler(self, ev): self.port_to_feature[addPort['dpid']][addPort['port']] = str(port.currentFeatures) print json.dumps(addPort) - self.post_json_to_core("http://localhost:5567/publish/addport", json.dumps(addPort)) + tmpIP = "http://" + coreIP + ":" + corePort + "/publish/addport" + self.post_json_to_core(tmpIP, json.dumps(addPort)) # # handle delete port event @@ -272,7 +281,8 @@ def del_port_handler(self, ev): del self.port_to_feature[delPort['dpid']][delPort['port']] print json.dumps(delPort) - self.post_json_to_core("http://localhost:5567/publish/delport", json.dumps(delPort)) + tmpIP = "http://" + coreIP + ":" + corePort + "/publish/delport" + self.post_json_to_core(tmpIP, json.dumps(delPort)) # # handle add link event @@ -308,7 +318,8 @@ def add_link_handler(self, ev): } print json.dumps(addLink) - self.post_json_to_core("http://localhost:5567/publish/addlink", json.dumps(addLink)) + tmpIP = "http://" + coreIP + ":" + corePort + "/publish/addlink" + self.post_json_to_core(tmpIP, json.dumps(addLink)) # # handle delete link event @@ -344,7 +355,8 @@ def del_link_handler(self, ev): } print json.dumps(delLink) - self.post_json_to_core("http://localhost:5567/publish/dellink", json.dumps(delLink)) + tmpIP = "http://" + coreIP + ":" + corePort + "/publish/dellink" + self.post_json_to_core(tmpIP, json.dumps(delLink)) # # handle add host event @@ -374,7 +386,8 @@ def add_host_handler(self, ev): } print json.dumps(addHost) - self.post_json_to_core("http://localhost:5567/publish/addhost", json.dumps(addHost)) + tmpIP = "http://" + coreIP + ":" + corePort + "/publish/addhost" + self.post_json_to_core(tmpIP, json.dumps(addHost)) # # handle delete host event @@ -390,7 +403,8 @@ def del_host_handler(self, ev): } print json.dumps(delHost) - self.post_json_to_core("http://localhost:5567/publish/delhost", json.dumps(delHost)) + tmpIP = "http://" + coreIP + ":" + corePort + "/publish/delhost" + self.post_json_to_core(tmpIP, json.dumps(delHost)) # # handle packet in event @@ -489,7 +503,8 @@ def packet_in_handler(self, ev): packetIn["controller"] = controllerName print json.dumps(packetIn) - self.post_json_to_core("http://localhost:5567/publish/packet", json.dumps(packetIn)) + tmpIP = "http://" + coreIP + ":" + corePort + "/publish/packet" + self.post_json_to_core(tmpIP, json.dumps(packetIn)) # # for datapath @@ -538,7 +553,8 @@ def controller_stats_reply_handler(self): } print json.dumps(controllerstatsReply) - self.post_json_to_core("http://localhost:5567/publish/controller", json.dumps(controllerstatsReply)) + tmpIP = "http://" + coreIP + ":" + corePort + "/publish/controller" + self.post_json_to_core(tmpIP, json.dumps(controllerstatsReply)) # # polling flows @@ -614,7 +630,8 @@ def flow_stats_reply_handler_API(self, dpid): i += 1 print json.dumps(flowstatsReplyAPI) - self.post_json_to_core("http://localhost:5567/publish/flow", json.dumps(flowstatsReplyAPI)) + tmpIP = "http://" + coreIP + ":" + corePort + "/publish/flow" + self.post_json_to_core(tmpIP, json.dumps(flowstatsReplyAPI)) return flows # @@ -655,7 +672,8 @@ def port_stats_reply_handler_API(self, dpid): portstatsReplyAPI["capacity"] = self.port_to_feature[portstatsReplyAPI['dpid']][portstatsReplyAPI['port']] if portstatsReplyAPI['port'] in self.port_to_feature[portstatsReplyAPI['dpid']] else '0' print json.dumps(portstatsReplyAPI) - self.post_json_to_core("http://localhost:5567/publish/port", json.dumps(portstatsReplyAPI)) + tmpIP = "http://" + coreIP + ":" + corePort + "/publish/port" + self.post_json_to_core(tmpIP, json.dumps(portstatsReplyAPI)) return ports From 5cc15a0efd21a3ad176a71eca740b5a76484ddde Mon Sep 17 00:00:00 2001 From: HsiaoHsuan Date: Wed, 11 May 2016 03:13:52 -0700 Subject: [PATCH 25/31] Fix dict error in busylink_detect --- core/src/floodlight_modules/busylink_detect.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/floodlight_modules/busylink_detect.py b/core/src/floodlight_modules/busylink_detect.py index 26e5373..f245656 100644 --- a/core/src/floodlight_modules/busylink_detect.py +++ b/core/src/floodlight_modules/busylink_detect.py @@ -170,7 +170,7 @@ def busyLinkDetect(self): self.statistics[link_id] = dict(self.links[link_id]) self.statistics[link_id]['state'] = self.baseState # Remove unexisted link info - for link_id in self.statistics: + for link_id in self.statistics.copy(): if link_id not in self.links: del self.statistics[link_id] From 2a2166e6bcc3d4908246ff678599dc2b3e71cf5c Mon Sep 17 00:00:00 2001 From: HsiaoHsuan Date: Sat, 14 May 2016 05:43:03 -0700 Subject: [PATCH 26/31] Fix ryu adapter for OpenFlow1.0 --- adapter/ryu/omniui/omniui.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/adapter/ryu/omniui/omniui.py b/adapter/ryu/omniui/omniui.py index 9d0edd7..d8fb345 100644 --- a/adapter/ryu/omniui/omniui.py +++ b/adapter/ryu/omniui/omniui.py @@ -23,6 +23,7 @@ from operator import attrgetter from ryu.ofproto.ether import ETH_TYPE_LLDP, ETH_TYPE_IPV6 from ryu.lib import hub +from ryu.lib.mac import haddr_to_bin from ryu.lib.packet import * from ryu.topology import event, switches @@ -100,6 +101,14 @@ def switch_features_handler(self, ev): ofproto.OFPCML_NO_BUFFER)] self.add_flow(datapath, 0, match, actions) + def add_flow10(self, datapath, priority, match, actions): + ofproto = datapath.ofproto + parser = datapath.ofproto_parser + + mod = parser.OFPFlowMod(datapath=datapath, match=match, cookie=0, command=ofproto.OFPFC_ADD, idle_timeout=0, + hard_timeout=0, priority=priority, flags=ofproto.OFPFF_SEND_FLOW_REM, actions=actions) + datapath.send_msg(mod) + def add_flow(self, datapath, priority, match, actions): ofproto = datapath.ofproto parser = datapath.ofproto_parser @@ -447,8 +456,12 @@ def packet_in_handler(self, ev): # install a flow to avoid packet_in next time if out_port != ofproto.OFPP_FLOOD: - match = parser.OFPMatch(in_port=in_port, eth_dst=dst, eth_src=src) - self.add_flow(datapath, 1, match, actions) + if ofproto.OFP_VERSION == ofproto_v1_0.OFP_VERSION: + match = parser.OFPMatch(in_port=in_port, dl_dst=haddr_to_bin(dst)) + self.add_flow10(datapath, 1, match, actions) + else: + match = parser.OFPMatch(in_port=in_port, eth_dst=dst, eth_src=src) + self.add_flow(datapath, 1, match, actions) data = None if msg.buffer_id == ofproto.OFP_NO_BUFFER: From 36af1e9d7f919cc1006df6e49a2168cb31e569a9 Mon Sep 17 00:00:00 2001 From: HsiaoHsuan Date: Sat, 14 May 2016 21:02:39 -0700 Subject: [PATCH 27/31] Remove forwarding functionality in ryu adapter --- adapter/ryu/omniui/omniui.py | 63 +----------------------------------- 1 file changed, 1 insertion(+), 62 deletions(-) diff --git a/adapter/ryu/omniui/omniui.py b/adapter/ryu/omniui/omniui.py index d8fb345..4cb576d 100644 --- a/adapter/ryu/omniui/omniui.py +++ b/adapter/ryu/omniui/omniui.py @@ -23,7 +23,6 @@ from operator import attrgetter from ryu.ofproto.ether import ETH_TYPE_LLDP, ETH_TYPE_IPV6 from ryu.lib import hub -from ryu.lib.mac import haddr_to_bin from ryu.lib.packet import * from ryu.topology import event, switches @@ -50,7 +49,6 @@ def __init__(self, *args, **kwargs): self.data['dpset'] = kwargs['dpset'] self.data['waiters'] = self.waiters self.data['omniui'] = self - self.mac_to_port = {} self.port_to_feature = {} self.datapaths = {} self.monitor_thread = hub.spawn(self.monitor) @@ -90,36 +88,6 @@ def stats_reply_handler(self, ev): del self.waiters[dp.id][msg.xid] lock.set() - @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 - - match = parser.OFPMatch() - actions = [parser.OFPActionOutput(ofproto.OFPP_CONTROLLER, - ofproto.OFPCML_NO_BUFFER)] - self.add_flow(datapath, 0, match, actions) - - def add_flow10(self, datapath, priority, match, actions): - ofproto = datapath.ofproto - parser = datapath.ofproto_parser - - mod = parser.OFPFlowMod(datapath=datapath, match=match, cookie=0, command=ofproto.OFPFC_ADD, idle_timeout=0, - hard_timeout=0, priority=priority, flags=ofproto.OFPFF_SEND_FLOW_REM, actions=actions) - datapath.send_msg(mod) - - def add_flow(self, datapath, priority, match, actions): - ofproto = datapath.ofproto - parser = datapath.ofproto_parser - - inst = [parser.OFPInstructionActions(ofproto.OFPIT_APPLY_ACTIONS, - actions)] - - mod = parser.OFPFlowMod(datapath=datapath, priority=priority, - match=match, instructions=inst) - datapath.send_msg(mod) - # # try post json to core # @@ -442,35 +410,6 @@ def packet_in_handler(self, ev): src = eth.src dst = eth.dst - self.mac_to_port.setdefault(dpid, {}) - - # learn a mac address to avoid FLOOD next time. - self.mac_to_port[dpid][src] = in_port - - if dst in self.mac_to_port[dpid]: - out_port = self.mac_to_port[dpid][dst] - else: - out_port = ofproto.OFPP_FLOOD - - actions = [parser.OFPActionOutput(out_port)] - - # install a flow to avoid packet_in next time - if out_port != ofproto.OFPP_FLOOD: - if ofproto.OFP_VERSION == ofproto_v1_0.OFP_VERSION: - match = parser.OFPMatch(in_port=in_port, dl_dst=haddr_to_bin(dst)) - self.add_flow10(datapath, 1, match, actions) - else: - match = parser.OFPMatch(in_port=in_port, eth_dst=dst, eth_src=src) - self.add_flow(datapath, 1, match, actions) - - data = None - if msg.buffer_id == ofproto.OFP_NO_BUFFER: - data = msg.data - - out = parser.OFPPacketOut(datapath=datapath, buffer_id=msg.buffer_id, - in_port=in_port, actions=actions, data=data) - datapath.send_msg(out) - print '*****packet in*****' packetIn = {} @@ -602,7 +541,7 @@ def flow_stats_reply_handler_API(self, dpid): flowstatsReplyAPI["flows"] = [] i = 0 for inflow in flows[key]: - if inflow["priority"] == 1: + if 'priority' in inflow: flowstatsReplyAPI["flows"].append({}) flowstatsReplyAPI["flows"][i]["ingressPort"] = str(inflow['match']['in_port']) if 'in_port' in inflow['match'] else "0" flowstatsReplyAPI["flows"][i]["dstMac"] = inflow['match']['dl_dst'] if 'dl_dst' in inflow['match'] else "0" From 555fb38f50a67cf3a74668cb755cbd04c56f9136 Mon Sep 17 00:00:00 2001 From: HsiaoHsuan Date: Mon, 16 May 2016 05:45:22 -0700 Subject: [PATCH 28/31] Fix type conversion, refer to #102 --- adapter/ryu/omniui/omniui.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/adapter/ryu/omniui/omniui.py b/adapter/ryu/omniui/omniui.py index 4cb576d..9db0c12 100644 --- a/adapter/ryu/omniui/omniui.py +++ b/adapter/ryu/omniui/omniui.py @@ -706,15 +706,15 @@ def ryuFlow_v1_0(self, dp, flows): 'match': { 'wildcards': wildcards, 'in_port': int(flows.get('ingressPort', 0)), - 'dl_src': flows.get('srcMac'), - 'dl_dst': flows.get('dstMac'), + 'dl_src': flows.get('srcMac', '00:00:00:00:00:00'), + 'dl_dst': flows.get('dstMac', '00:00:00:00:00:00'), 'dl_vlan': int(flows.get('vlan', 0)), 'dl_vlan_pcp': int(flows.get('vlanP', 0)), 'dl_type': int(flows.get('dlType', 0)), 'nw_tos': int(flows.get('tosBits', 0)), 'nw_proto': int(flows.get('netProtocol', 0)), - 'nw_src': flows.get('srcIP').split('/')[0], - 'nw_dst': flows.get('dstIP').split('/')[0], + 'nw_src': flows.get('srcIP', '0.0.0.0').split('/')[0], + 'nw_dst': flows.get('dstIP', '0.0.0.0').split('/')[0], 'tp_src': int(flows.get('srcPort', 0)), 'tp_dst': int(flows.get('dstPort', 0)) } @@ -734,18 +734,18 @@ def to_action_v1_0(self, dp, actions): if actions_type == 'OUTPUT': ryuAction = { 'type': actions_type, - 'port': actions.split('=')[1], + 'port': int(actions.split('=')[1]), 'max_len': 0xffe5 } elif actions_type == 'SET_VLAN_VID': ryuAction = { 'type': actions_type, - 'vlan_vid': actions.split('=')[1] + 'vlan_vid': int(actions.split('=')[1]) } elif actions_type == 'SET_VLAN_PCP': ryuAction = { 'type': actions_type, - 'vlan_pcp': actions.split('=')[1] + 'vlan_pcp': int(actions.split('=')[1]) } elif actions_type == 'STRIP_VLAN': ryuAction = { @@ -774,25 +774,25 @@ def to_action_v1_0(self, dp, actions): elif actions_type == 'SET_NW_TOS': ryuAction = { 'type': actions_type, - 'nw_tos': actions.split('=')[1] + 'nw_tos': int(actions.split('=')[1]) } elif actions_type == 'SET_TP_SRC': ryuAction = { 'type': actions_type, - 'tp_src': actions.split('=')[1] + 'tp_src': int(actions.split('=')[1]) } elif actions_type == 'SET_TP_DST': ryuAction = { 'type': actions_type, - 'tp_dst': actions.split('=')[1] + 'tp_dst': int(actions.split('=')[1]) } elif actions_type == 'ENQUEUE': actions_port = actions.split('=')[1].split(':')[0] actions_qid = actions.split('=')[1].split(':')[1] ryuAction = { 'type': actions_type, - 'port': actions_port, - 'queue_id': actions_qid + 'port': int(actions_port), + 'queue_id': int(actions_qid) } else: LOG.debug('Unknown action type') @@ -901,7 +901,7 @@ def to_action_v1_3(self, dp, dic): if action_type == 'OUTPUT': ryuAction = { 'type': action_type, - 'port': dic.split('=')[1] + 'port': int(dic.split('=')[1]) } elif action_type == 'COPY_TTL_OUT': ryuAction = { From 7bfcde9d58c9c33bc56aa68c87d1c4e564d139c9 Mon Sep 17 00:00:00 2001 From: HsiaoHsuan Date: Thu, 19 May 2016 03:39:15 -0700 Subject: [PATCH 29/31] Fix flow deletion functionality, refer to #102 --- adapter/ryu/omniui/omniui.py | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/adapter/ryu/omniui/omniui.py b/adapter/ryu/omniui/omniui.py index 9db0c12..0331eda 100644 --- a/adapter/ryu/omniui/omniui.py +++ b/adapter/ryu/omniui/omniui.py @@ -544,13 +544,13 @@ def flow_stats_reply_handler_API(self, dpid): if 'priority' in inflow: flowstatsReplyAPI["flows"].append({}) flowstatsReplyAPI["flows"][i]["ingressPort"] = str(inflow['match']['in_port']) if 'in_port' in inflow['match'] else "0" - flowstatsReplyAPI["flows"][i]["dstMac"] = inflow['match']['dl_dst'] if 'dl_dst' in inflow['match'] else "0" - flowstatsReplyAPI["flows"][i]["srcMac"] = inflow['match']['dl_src'] if 'dl_src' in inflow['match'] else "0" + flowstatsReplyAPI["flows"][i]["dstMac"] = inflow['match']['dl_dst'] if 'dl_dst' in inflow['match'] else "00:00:00:00:00:00" + flowstatsReplyAPI["flows"][i]["srcMac"] = inflow['match']['dl_src'] if 'dl_src' in inflow['match'] else "00:00:00:00:00:00" flowstatsReplyAPI["flows"][i]["dstIP"] = inflow['match']['nw_dst'] if 'nw_dst' in inflow['match'] else "0.0.0.0" flowstatsReplyAPI["flows"][i]["dstIPMask"] = "0" # not support in ryu flowstatsReplyAPI["flows"][i]["srcIP"] = inflow['match']['nw_src'] if 'nw_src' in inflow['match'] else "0.0.0.0" flowstatsReplyAPI["flows"][i]["srcIPMask"] = "0" # not support in ryu - flowstatsReplyAPI["flows"][i]["netProtocol"] = hex(inflow['match']['nw_proto']) if 'nw_proto' in inflow['match'] else "0" + flowstatsReplyAPI["flows"][i]["netProtocol"] = hex(inflow['match']['nw_proto']) if 'nw_proto' in inflow['match'] else "0x00" flowstatsReplyAPI["flows"][i]["dstPort"] = str(inflow['match']['tp_dst']) if 'tp_dst' in inflow['match'] else "0" flowstatsReplyAPI["flows"][i]["srcPort"] = str(inflow['match']['tp_src']) if 'tp_src' in inflow['match'] else "0" flowstatsReplyAPI["flows"][i]["vlan"] = str(inflow['match']['dl_vlan']) if 'dl_vlan' in inflow['match'] else "0" @@ -563,7 +563,7 @@ def flow_stats_reply_handler_API(self, dpid): flowstatsReplyAPI["flows"][i]["hardTimeout"] = str(inflow['hard_timeout']) flowstatsReplyAPI["flows"][i]["priority"] = str(inflow['priority']) flowstatsReplyAPI["flows"][i]["duration"] = str(inflow['duration_sec']) - flowstatsReplyAPI["flows"][i]["dlType"] = hex(inflow['match']['dl_type']) if 'dl_type' in inflow['match'] else "0" + flowstatsReplyAPI["flows"][i]["dlType"] = hex(inflow['match']['dl_type']) if 'dl_type' in inflow['match'] else "0x0000" flowstatsReplyAPI["flows"][i]["actions"] = [] for action in inflow['actions']: @@ -710,9 +710,9 @@ def ryuFlow_v1_0(self, dp, flows): 'dl_dst': flows.get('dstMac', '00:00:00:00:00:00'), 'dl_vlan': int(flows.get('vlan', 0)), 'dl_vlan_pcp': int(flows.get('vlanP', 0)), - 'dl_type': int(flows.get('dlType', 0)), + 'dl_type': int(flows.get('dlType', '0x0000'), 16), 'nw_tos': int(flows.get('tosBits', 0)), - 'nw_proto': int(flows.get('netProtocol', 0)), + 'nw_proto': int(flows.get('netProtocol', '0x00'), 16), 'nw_src': flows.get('srcIP', '0.0.0.0').split('/')[0], 'nw_dst': flows.get('dstIP', '0.0.0.0').split('/')[0], 'tp_src': int(flows.get('srcPort', 0)), @@ -725,6 +725,9 @@ def ryuFlow_v1_0(self, dp, flows): for act in actions: action = self.to_action_v1_0(dp, act) ryuFlow['actions'].append(action) + for matchfield in ryuFlow['match'].copy(): + if (ryuFlow['match'][matchfield] == 0) | (ryuFlow['match'][matchfield] == '0.0.0.0') | (ryuFlow['match'][matchfield] == '00:00:00:00:00:00'): + del ryuFlow['match'][matchfield] return ryuFlow @@ -842,15 +845,15 @@ def to_match_v1_3(self, dp, omni_key, omniFlow): 'srcMac': ['dl_src', str], 'eth_dst': ['eth_dst', str], 'eth_src': ['eth_src', str], - 'dlType': ['dl_type', int], - 'eth_type': ['eth_type', int], + 'dlType': ['dl_type', int, 16], + 'eth_type': ['eth_type', int, 16], 'vlan': ['dl_vlan', str], 'vlan_vid': ['vlan_vid', str], 'vlanP': ['vlan_pcp', int], 'ip_dscp': ['ip_dscp', int], 'ip_ecn': ['ip_ecn', int], - 'netProtocol': ['nw_proto', int], - 'ip_proto': ['ip_proto', int], + 'netProtocol': ['nw_proto', int, 16], + 'ip_proto': ['ip_proto', int, 16], 'srcIP': ['nw_src', str], 'dstIP': ['nw_dst', str], 'ipv4_src': ['ipv4_src', str], @@ -886,8 +889,15 @@ def to_match_v1_3(self, dp, omni_key, omniFlow): 'ipv6_exthdr': ['ipv6_exthdr', int] } + if (omniFlow.get(omni_key) == '0') | (omniFlow.get(omni_key) == '0.0.0.0') | (omniFlow.get(omni_key) == '00:00:00:00:00:00') | (omniFlow.get(omni_key) == '0x00') | (omniFlow.get(omni_key) == '0x0000'): + return None for key, value in convert.items(): if omni_key == key: + if len(value) == 3: + ryuMatch = { + value[0]: value[1](omniFlow.get(omni_key), value[2]) + } + return ryuMatch ryuMatch = { value[0]: value[1](omniFlow.get(omni_key)) } From 884639f281d4a8cd676e29bbbbdb4b0448cd9a82 Mon Sep 17 00:00:00 2001 From: HsiaoHsuan Date: Thu, 19 May 2016 07:19:45 -0700 Subject: [PATCH 30/31] Fix default value error in ryu adapter, refer to #102 --- adapter/ryu/omniui/omniui.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/adapter/ryu/omniui/omniui.py b/adapter/ryu/omniui/omniui.py index 0331eda..a521c11 100644 --- a/adapter/ryu/omniui/omniui.py +++ b/adapter/ryu/omniui/omniui.py @@ -826,7 +826,7 @@ def ryuFlow_v1_3(self, dp, omniFlow): ryuFlow['match'].update(match) # handle mutiple actions - acts = omniFlow.get('actions').split(',') + acts = omniFlow.get('actions', '').split(',') for a in acts: action = self.to_action_v1_3(dp, a) if action is not None: From 61c10ad8b5140e828a5bddf5a3e9a4dc7b5bdf2f Mon Sep 17 00:00:00 2001 From: HsiaoHsuan Date: Thu, 19 May 2016 09:08:51 -0700 Subject: [PATCH 31/31] Fix the misuse of operator --- adapter/ryu/omniui/omniui.py | 6 +++--- core/src/floodlight_modules/busylink_detect.py | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/adapter/ryu/omniui/omniui.py b/adapter/ryu/omniui/omniui.py index a521c11..7087149 100644 --- a/adapter/ryu/omniui/omniui.py +++ b/adapter/ryu/omniui/omniui.py @@ -441,7 +441,7 @@ def packet_in_handler(self, ev): packetIn["ip_dst"] = p.dst_ip if hasattr(p, 'proto'): - packetIn["protocol"] = 'x0'.join(hex(p.proto).split('x')) if ((len(hex(p.proto)) < 4) | (len(hex(p.proto)) == 5)) else hex(p.proto) + packetIn["protocol"] = 'x0'.join(hex(p.proto).split('x')) if ((len(hex(p.proto)) < 4) or (len(hex(p.proto)) == 5)) else hex(p.proto) if hasattr(p, 'src_port'): packetIn["port_src"] = str(p.src_port) @@ -726,7 +726,7 @@ def ryuFlow_v1_0(self, dp, flows): action = self.to_action_v1_0(dp, act) ryuFlow['actions'].append(action) for matchfield in ryuFlow['match'].copy(): - if (ryuFlow['match'][matchfield] == 0) | (ryuFlow['match'][matchfield] == '0.0.0.0') | (ryuFlow['match'][matchfield] == '00:00:00:00:00:00'): + if (ryuFlow['match'][matchfield] == 0) or (ryuFlow['match'][matchfield] == '0.0.0.0') or (ryuFlow['match'][matchfield] == '00:00:00:00:00:00'): del ryuFlow['match'][matchfield] return ryuFlow @@ -889,7 +889,7 @@ def to_match_v1_3(self, dp, omni_key, omniFlow): 'ipv6_exthdr': ['ipv6_exthdr', int] } - if (omniFlow.get(omni_key) == '0') | (omniFlow.get(omni_key) == '0.0.0.0') | (omniFlow.get(omni_key) == '00:00:00:00:00:00') | (omniFlow.get(omni_key) == '0x00') | (omniFlow.get(omni_key) == '0x0000'): + if (omniFlow.get(omni_key) == '0') or (omniFlow.get(omni_key) == '0.0.0.0') or (omniFlow.get(omni_key) == '00:00:00:00:00:00') or (omniFlow.get(omni_key) == '0x00') or (omniFlow.get(omni_key) == '0x0000'): return None for key, value in convert.items(): if omni_key == key: diff --git a/core/src/floodlight_modules/busylink_detect.py b/core/src/floodlight_modules/busylink_detect.py index f245656..028e05e 100644 --- a/core/src/floodlight_modules/busylink_detect.py +++ b/core/src/floodlight_modules/busylink_detect.py @@ -141,10 +141,10 @@ def busyLinkDetect(self): destp = self.links[link_id]['targetPort'] # Check if needed information all arrived - if (src not in self.switches) | (dest not in self.switches): + if (src not in self.switches) or (dest not in self.switches): print 'Not Ready' return - elif (srcp not in self.switches[src]) | (destp not in self.switches[dest]): + elif (srcp not in self.switches[src]) or (destp not in self.switches[dest]): print 'Not Ready' return