class Scheduler(object): """ Scheduling flows """ def __init__(self, topo) : self.topology = topo self.fdb = FlowDatabase() def findNewPath(self, fid) : """ Perform a greedy depth first search for finding new path for fid """ src = self.fdb.getSourceSwitch(fid) dst = self.fdb.getSourceSwitch(fid) def greedyDFS(self, sw, fc, dst, cThres) : """ At switch sw with flow characteristics fc, find a greedy path to dst""" neighbours = self.topology.getSwitchNeighbours(sw) swPairList = [] for n in neighbours : ct = self.fdb.computeCriticalEvent(sw, n, fc) # Computes critical event time if flow of fc is sent from sw to n # Check with cThres, should not even consider events in the same epoch if ct > cThres : swPairList.append([n, ct]) swPairList = sorted(swPairList, key=lambda pair: pair[1], reverse=True) # Sort by critical times # Greedy path search, explore switch with greatest critical time for pair in swPairList : nextsw = pair[0] if nextsw == dst : # Found a path to destination return [sw, dst] updatedfc = 0 # If sw-nextsw link is chosen, find updated fc path = self.greedyDFS(nextsw, updatedfc, dst, cThres) if path <> None : path.insert(sw, 0) return path # if none of the neighbours have permissible values, return None return None
def __init__(self): self.listenTo(core.openflow) core.openflow_discovery.addListeners(self) log.debug("Enabling NetworkMapper Module") # Adjacency map. [sw1][sw2] -> port from sw1 to sw2 self.adjacency = defaultdict(lambda:defaultdict(lambda:None)) self.switchMap = dict() self.switchConnections = dict() self.topology = Topology() self.fdb = FlowDatabase(self.topology, QUEUE_SIZE) self.routeStatus = [] self.startTime = time.time() self.measurementEpoch = 2 self.measurementInfra = Timer(self.measurementEpoch, self._measurement, recurring=True) self.schedulerEpoch = 20 self.scheduler = Timer(self.schedulerEpoch, self._scheduler, recurring=True)
def __init__(self, topo) : self.topology = topo self.fdb = FlowDatabase(self.topology, QUEUE_SIZE) self.flows = dict()
class Simulator(object) : def __init__(self, topo) : self.topology = topo self.fdb = FlowDatabase(self.topology, QUEUE_SIZE) self.flows = dict() def addFlow(self, src, dst, fc, mice) : fid = self.fdb.addFlow([], src, dst, fc) if mice : self.flows[fid] = True # Mice flow else : self.flows[fid] = False # Elephant flow path = self.topology.getPath(src, dst) self.fdb.changePath(fid, path) def schedule(self) : self.fdb.updateCriticalTimes() reroutes = self.fdb.scheduleEpoch() print "Number of reroutes", len(reroutes) return self.computeCriticalFlowCount() def computeCriticalFlowCount(self) : swCount = self.topology.getSwitchCount() criticalFlows = dict() for f in self.flows : criticalFlows[f] = False for sw1 in range(1, swCount + 1) : neighbours = self.topology.getSwitchNeighbours(sw1) flowCaps = dict() # Decide input characteristics for sw0 in neighbours : flows = self.fdb.getSwitchFlows(sw0, sw1) totalCap = 0 for f in flows : if self.flows[f] : totalCap += 1 else : totalCap += 10 if totalCap > 100 : scalingFactor = 100/totalCap else : scalingFactor = 1 for f in flows : if self.flows[f] : flowCaps[f] = 1 * scalingFactor else : flowCaps[f] = 10 * scalingFactor for sw2 in neighbours : flows = self.fdb.getSwitchFlows(sw1, sw2) totalCap = 0 for f in flows : if f in flowCaps : totalCap += flowCaps[f] else : if self.flows[f] : totalCap += 1 else : totalCap += 10 if totalCap > 120 : # Critical switch queue! for f in flows : if self.flows[f] : criticalFlows[f] = True criticalCount = 0 for f in criticalFlows : if criticalFlows[f] : criticalCount += 1 return criticalCount
class Scheduler (EventMixin): def __init__(self): self.listenTo(core.openflow) core.openflow_discovery.addListeners(self) log.debug("Enabling NetworkMapper Module") # Adjacency map. [sw1][sw2] -> port from sw1 to sw2 self.adjacency = defaultdict(lambda:defaultdict(lambda:None)) self.switchMap = dict() self.switchConnections = dict() self.topology = Topology() self.fdb = FlowDatabase(self.topology, QUEUE_SIZE) self.routeStatus = [] self.startTime = time.time() self.measurementEpoch = 2 self.measurementInfra = Timer(self.measurementEpoch, self._measurement, recurring=True) self.schedulerEpoch = 20 self.scheduler = Timer(self.schedulerEpoch, self._scheduler, recurring=True) """This event will be raised each time a switch will connect to the controller""" def _handle_ConnectionUp(self, event): # Use dpid to differentiate between switches (datapath-id) # Each switch has its own flow table. As we'll see in this # example we need to write different rules in different tables. dpid = dpidToStr(event.dpid) switchName = "" for m in event.connection.features.ports: name = m.name.split("-") if switchName == "" : switchName = name[0] if not switchName == name[0] : log.debug("Some Error in mapping name from the OpenFlow Switch Up Message.") swID = self.topology.addSwitch(switchName, []) self.switchMap[switchName] = dpid self.switchConnections[swID] = event.connection # msg = of.ofp_flow_mod() # msg.actions.append(of.ofp_action_output(port = of.OFPP_FLOOD)) # event.connection.send(msg) def findSwitchName(self, dpid) : for name in self.switchMap.iterkeys() : if self.switchMap[name] == dpid : return name def getSwitchMacAddr(self, sw) : if sw == None : return None else : dpid = self.switchMap[sw] mac = dpid.replace("-", ":") return mac def findOutputPort(self, curr, next, prev = None) : #curr and next are not adjacent. Find the next switch. sw = self.findNeighbour(src = curr, dst = next) if sw == prev : return of.OFPP_IN_PORT # send back on the input port. elif not self.adjacency[self.switchMap[curr]][self.switchMap[sw]] == None : return self.adjacency[self.switchMap[curr]][self.switchMap[sw]] else : print "[ERROR] No edge present." return None def _handle_LinkEvent (self, event): l = event.link swdpid1 = dpid_to_str(l.dpid1) swdpid2 = dpid_to_str(l.dpid2) log.debug ("link %s[%d] <-> %s[%d]", swdpid1, l.port1, swdpid2, l.port2) sw1 = self.topology.getSwID(self.findSwitchName(swdpid1)) sw2 = self.topology.getSwID(self.findSwitchName(swdpid2)) self.adjacency[sw1][sw2] = int(l.port1) self.adjacency[sw2][sw1] = int(l.port2) self.topology.addLink(sw1, sw2) def _handle_PacketIn (self, event): """ Handle packet in messages from the switch. """ packet = event.parsed def install_fwdrule(event,srcip,dstip,outport,vlan=0): msg = of.ofp_flow_mod() #Match msg.match = of.ofp_match() msg.match.dl_type = ethernet.IP_TYPE msg.match.set_nw_src(IPAddr(srcip, 32), 32) msg.match.set_nw_dst(IPAddr(dstip, 32), 32) if not vlan == 0 : # Need to set VLAN Tag for isolation of tenant traffic. msg.actions.append(of.ofp_action_vlan_vid(vlan_vid = vlan)) msg.actions.append(of.ofp_action_output(port = outport)) msg.data = event.ofp msg.in_port = event.port event.connection.send(msg) def installFloodRule(event,packet,outport,vlan=0): msg = of.ofp_flow_mod() #Match msg.match = of.ofp_match.from_packet(packet, event.port) msg.actions.append(of.ofp_action_output(port = outport)) msg.data = event.ofp msg.in_port = event.port event.connection.send(msg) def handle_IP_packet (event, packet): ip = packet.find('ipv4') if ip is None: #print "Non IP packet" match = of.ofp_match.from_packet(packet) if match.dl_type == packet.ARP_TYPE and match.nw_proto == arp.REQUEST : ip = match.nw_dst self.respondToARP(ip, packet, match, event) else : #print ip.__str__() if str(ip.srcip) not in hostMap or str(ip.dstip) not in hostMap : return # Ignore packet srcSw = self.topology.getSwID(hostMap[str(ip.srcip)]) dstSw = self.topology.getSwID(hostMap[str(ip.dstip)]) # Find path from srcSw to dstSw and add rules for ip.srcip and ip.dstip self.addForwardingRules(ip.srcip, srcSw, ip.dstip, dstSw) #switch is event.dpid handle_IP_packet(event, packet) # flood and install the flow table entry for the flood def respondToARP(self, ip, packet, match, event): # reply to ARP request r = arp() r.opcode = arp.REPLY r.hwdst = match.dl_src r.protosrc = ip r.protodst = match.nw_src r.hwsrc = EthAddr(macMap[str(ip)]) e = ethernet(type=packet.ARP_TYPE, src=r.hwsrc, dst=r.hwdst) e.set_payload(r) log.debug("%i %i answering ARP for %s" % ( event.dpid, event.port, str(r.protosrc))) msg = of.ofp_packet_out() msg.data = e.pack() msg.actions.append(of.ofp_action_output(port = of.OFPP_IN_PORT)) msg.in_port = event.port event.connection.send(msg) def addForwardingRules(self, srcip, srcSw, dstip, dstSw, path=[]) : if path == [] and self.checkRouteStatus(srcip, dstip) : return print "Adding forwarding rules for", srcip, "->", dstip, ":", srcSw, "->", dstSw if path == [] : path = self.topology.getPath(srcSw, dstSw) self.printPath(path) # Create flow characteristic for flow fc = FlowCharacteristic() fid = self.fdb.addFlow([srcip, dstip], srcSw, dstSw, fc) # Adding flow to flow database #self.fdb.changePath(fid, path) status = [srcip, dstip, path, fc, fid] self.routeStatus.append(status) for i in range(len(path) - 1) : sw1 = path[i] sw2 = path[i + 1] # add rule to go from sw1 to sw2 self.addRule(srcip, dstip, sw1, sw2) # add flooding rule for dst : self.addRule(srcip, dstip, dstSw, None) def addRule(self, srcip, dstip, sw1, sw2) : msg = of.ofp_flow_mod() connection = self.switchConnections[sw1] if sw2 == None : outport = of.OFPP_FLOOD else : outport = self.adjacency[sw1][sw2] #Match msg.match = of.ofp_match() msg.match.dl_type = ethernet.IP_TYPE msg.match.set_nw_src(IPAddr(srcip, 32), 32) msg.match.set_nw_dst(IPAddr(dstip, 32), 32) msg.priority = of.OFP_DEFAULT_PRIORITY msg.actions.append(of.ofp_action_output(port = outport)) connection.send(msg) def deleteRule(self, srcip, dstip, sw1, sw2) : msg = of.ofp_flow_mod() connection = self.switchConnections[sw1] msg.command = of.OFPFC_DELETE #Match msg.match = of.ofp_match() msg.match.dl_type = ethernet.IP_TYPE msg.match.set_nw_src(IPAddr(srcip, 32), 32) msg.match.set_nw_dst(IPAddr(dstip, 32), 32) connection.send(msg) def printPath(self, path) : namedPath = [] for i in range(len(path)) : namedPath.append(self.topology.getSwName(path[i])) print namedPath def checkRouteStatus(self, srcip, dstip) : for stat in self.routeStatus : if stat[0] == srcip and stat[1] == dstip : return True return False def deleteStaleForwardingRules(self,srcip, dstip, path) : """ Delete stale rules for srcip -> dstip on path """ for i in range(len(path) - 1) : sw1 = path[i] sw2 = path[i + 1] # delete rule to go from sw1 to sw2 self.deleteRule(srcip, dstip, sw1, sw2) def _handle_FlowStatsReceived (self, event): #stats = flow_stats_to_list(event.stats) swdpid = dpidToStr(event.dpid) swID = self.topology.getSwID(self.findSwitchName(swdpid)) for f in event.stats: for stat in self.routeStatus : if f.match.nw_src == stat[0] and f.match.nw_dst == stat[1] and stat[2][0] == swID : # Edge switch for flow. Update Flow Characteristics fc = stat[3] val = fc.insert(f.byte_count / 1000, time.time() - self.startTime) # def _handle_PortStatsReceived(self, event) : # print "Port event" # swdpid = dpidToStr(event.dpid) # swID = self.topology.getSwID(self.findSwitchName(swdpid)) # swBytes = 0 # print event.stats # for f in event.stats: # print f.port_no, f.tx_bytes, f.rx_bytes # swBytes += f.rx_bytes - f.tx_bytes # print "Updating Switch ", swID, swBytes # #self.fdb.addSwitchBytes(swID, swBytes) def _measurement (self) : for connection in self.switchConnections.values(): connection.send(of.ofp_stats_request(body=of.ofp_flow_stats_request())) #connection.send(of.ofp_stats_request(body=of.ofp_port_stats_request())) def _scheduler(self) : # Update new and learned flows for stat in self.routeStatus : fid = stat[4] fc = stat[3] path = stat[2] if fc.learned() and len(self.fdb.getPath(fid)) == 0 : # Add this flow to the scheduler self.fdb.changePath(fid, path) # perform scheduling for events in the current epoch. self.fdb.updateCriticalTimes() rerouteDecisions = self.fdb.scheduleEpoch() for dec in rerouteDecisions : fid = dec[0] oldpath = dec[1] newpath = dec[2] self.deleteStaleForwardingRules(srcip=self.fdb.getSourceIP(fid), dstip=self.fdb.getDestinationIP(fid), path=oldpath) self.addForwardingRules(srcip=self.fdb.getSourceIP(fid), srcSw=newpath[0], dstip=self.fdb.getDestinationIP(fid), dstSw=newpath[len(newpath)-1], path=newpath) # update stat for stat in self.routeStatus : if fid == stat[4] : stat[2] = newpath
def __init__(self, topo) : self.topology = topo self.fdb = FlowDatabase()