def run(self): """Main loop """ while self.running: if (len(self._events) == 0): #Sleep for some time if nothing to do time.sleep(self.sleep) continue else: while (self._events.has_event_ready()): (t, event) = self._events.get_next() if ((t-time.time()) > self.tolerance): output.warn("Event "+event.name+" scheduled for "+str(t)+\ " is being run at time "+str(time.time()), self.__class__.__name__) else: output.vvdbg("Event "+event.name+" scheduled for "+str(t)+\ " is being run at time "+str(time.time()), self.__class__.__name__) self._dispatch_event(event) ntime = self.sleep if (len(self._events) > 0): ntime = self._events.get_next_time() - time.time() time.sleep(min(ntime, self.sleep))
def _process_outbound(self, pktin, intfs, iport, lointf): """Event handler for outbound ARP packets @param pktin packet in event to handle @param intfs dictionary of interfaces (with ip range) @param iport port no of local interface @param lointf local interface address (ip, mac) @return false """ for portno, ipr in intfs.items(): if (ipr[0] & ipr[1]) == (pktin.match.nw_dst & ipr[1]): flow = flows.exact_entry(pktin.match) if flow.buffer_id != pktin.pktin.buffer_id: output.warn("Buffered outbound ARP packet (cannot be rewritten properly)!", self.__class__.__name__) else: flow.add_output(portno) ofpkt.dl_rewrite(pktin.dpkt, True, ipr[2]) ofpkt.nw_rewrite(pktin.dpkt, True, ipr[0]) self.get_conn().send(flow.get_packet_out().pack() + pktin.dpkt.pack()) output.vdbg( "ARP to " + pu.ip_val2string(pktin.match.nw_dst) + " sent to port " + str(portno), self.__class__.__name__, ) return False output.warn("ARP for " + pu.ip_val2string(pktin.match.nw_dst) + " unknown subnet", self.__class__.__name__) return False
def dohandshake(self, msg): """Function to carry out handshake Switch (hello) => hello + feature request Switch (feature reply) => DONE """ if (msg.header.type == pyopenflow.OFPT_HELLO): sendmsg = pyopenflow.ofp_hello() self.send(sendmsg.pack()) sendmsg = pyopenflow.ofp_header() sendmsg.type = pyopenflow.OFPT_FEATURES_REQUEST self.send(sendmsg.pack()) elif (msg.header.type == pyopenflow.OFPT_FEATURES_REPLY): switch_feature = pyopenflow.ofp_switch_features() switch_feature.unpack(msg.message) self.dpid = switch_feature.datapath_id output.info("Connected to switch %x" % self.dpid, self.__class__.__name__) self.handshake = True else: output.warn("Handshake should not handle message type "+\ pyopenflow.ofp_type[msg.header.type], self.__class__.__name__)
def __init__(self, sock, msg, stats_reply=None, reply=None): """Initialize @param sock reference to socket @param msg message """ ofcomm.message.__init__(self, sock, msg) ##Stats reply header self.stats_reply = stats_reply remaining = reply if (self.stats_reply == None) or (remaining == None): self.stats_reply = pyof.ofp_stats_reply() remaining = self.stats_reply.unpack(msg) ##Flow stats of individual flows self.flows = [] while (len(remaining) >= pyof.OFP_FLOW_STATS_BYTES): flow = pyof.ofp_flow_stats() flow.unpack(remaining) self.unpack_actions(remaining[pyof.OFP_FLOW_STATS_BYTES:flow.length], flow.actions) remaining = remaining[flow.length:] self.flows.append(flow) if (len(remaining) > 0): output.warn("Flow stats reply is of irregular length with "+\ str(len(remaining))+" bytes remaining.", self.__class__.__name__) output.vdbg("Received "+str(len(self.flows))+" flow stats.", self.__class__.__name__)
def _process_inbound(self, pktin, intfs, iport, lointf): """Event handler for inbound ARP packets @param pktin packet in event to handle @param intfs dictionary of interfaces (with ip range) @param iport port no of local interface @param lointf local interface address (ip, mac) @return false """ try: ipr = intfs[pktin.match.in_port] except KeyError: output.vdbg("ARP packet received on unknown/uninitialized interface", self.__class__.__name__) return False flow = flows.exact_entry(pktin.match) if flow.buffer_id != pktin.pktin.buffer_id: output.warn("Buffered inbound ARP packet (cannot be rewritten properly)!", self.__class__.__name__) elif flow.match.nw_dst != ipr[0]: output.vdbg("Inbound ARP packet not destined for this host, ignored.", self.__class__.__name__) else: flow.add_output(iport) if pu.array2val(pktin.match.dl_dst) != 0xFFFFFFFFFFFF: ofpkt.dl_rewrite(pktin.dpkt, False, lointf[1]) ofpkt.nw_rewrite(pktin.dpkt, False, lointf[0]) self.get_conn().send(flow.get_packet_out().pack() + pktin.dpkt.pack()) return False
def __init__(self, sock, msg): """Initialize @param sock reference to socket @param msg message """ ofcomm.message.__init__(self, sock, msg) ##Features struct self.features = None if (self.header.type == pyof.OFPT_FEATURES_REPLY): self.features = pyof.ofp_switch_features() r = self.features.unpack(msg) while (len(r) >= pyof.OFP_PHY_PORT_BYTES): p = pyof.ofp_phy_port() r = p.unpack(r) self.features.ports.append(p) if (len(r) > 0): output.warn("Features reply is of irregular length with "+\ str(len(r))+" bytes remaining.", self.__class__.__name__) output.dbg("Received switch features:\n"+\ self.features.show("\t"), self.__class__.__name__)
def dns_select_intf(self, intfs): """Get which interface to send @return port no to send flow on and None if nothing to choose from """ if len(intfs) == 0: return None if self.coin == None: output.warn( "No COIN server reference provided. Default to random choice of interface", self.__class__.__name__ ) return self.select_nth_intf(intfs, 1) if self.coin.config["dns_select_interface"] == "random": return self.random_select_intf(intfs) elif self.coin.config["dns_select_interface"] == "round_robin": return self.round_robin_select_intf(intfs) elif self.coin.config["dns_select_interface"] == "bandwidth": return self.bandwidth_select_intf(intfs) elif isinstance(self.coin.config["dns_select_interface"], int): return self.select_nth_intf(intfs, self.coin.config["dns_select_interface"]) else: value = 1 try: value = int(self.coin.config["dns_select_interface"]) self.coin.set_config("dns_select_interface", value) except ValueError: output.warn("Unknown selection configuration!", self.__class__.__name__) return self.select_nth_intf(intfs, value)
def processevent(self, event): """Process event @param event event to process @return True """ if (isinstance(event, yapc.priv_callback)): lastr = self.lastresult r = self.get_stat() output.vdbg(r) if (lastr == None): self._server.post_event(yapc.priv_callback(self), self.interval) return True for k,v in r.items(): for k2 in ["transmit", "receive"]: try: v[k2]["bps"] = (float(v[k2]["bytes"] - lastr[k][k2]["bytes"])*8.0/ (v["timestamp"]-lastr[k]["timestamp"])) v[k2]["pps"] = (float(v[k2]["packets"] - lastr[k][k2]["packets"])/ (v["timestamp"]-lastr[k]["timestamp"])) except KeyError: output.warn("Interface "+str(k)+" is new or removed", self.__class__.__name__) self._server.post_event(yapc.priv_callback(self), self.interval) return True
def order_cleanup(self, earlier_handler, later_handler): """Order handler for cleanup Do so by moving earlier handler to be just before the later handler @param earlier_handler handler that should process event earlier @param later_handler handler that should process event later """ eindex = -1 if (earlier_handler in self.cleanups): eindex = self.cleanups.index(earlier_handler) else: output.warn(earlier_handler.__class__.__name__+\ " did not register for a cleanup". self.__class__.__name__) return lindex = -1 if (later_handler in self.cleanups): lindex = self.cleanups.index(later_handler) else: output.warn(laterr_handler.__class__.__name__+\ " did not register for a cleanup", self.__class__.__name__) return #Reorder if (lindex < eindex): self.cleanups.insert(lindex, self.cleanups.pop(eindex))
def __process_switch_json(self, event): """Process JSON messages for switch @param event JSON message event for switch """ reply = {} reply["type"] = "coin" reply["subtype"] = "ovs" if event.message["command"] == "add_if": self.add_if(event.message["name"]) reply["executed"] = True elif event.message["command"] == "del_if": self.datapaths[COIN_DP_NAME].del_if(event.message["name"]) reply["executed"] = True elif event.message["command"] == "get_interfaces": dpidsl = mc.get(swstate.dp_features.DP_SOCK_LIST) if dpidsl != None: reply["interfaces"] = [] if len(dpidsl) > 1: output.warn(str(len(dpidsl)) + " datapaths connected to COIN", self.__class__.__name__) f = mc.get(dpidsl[0]) output.dbg("Updated switch features:\n" + f.show("\t"), self.__class__.__name__) for p in f.ports: reply["interfaces"].append(p.name) else: output.warn("No datapaths connected to COIN", self.__class__.__name__) reply["error"] = "No datapath connected" else: reply["error"] = "Unknown command" return reply
def eventname(self): """Provide name for event used to publish data @return event name """ output.warn("eventname should overloaded", self.__class__.__name__) return None
def send(self, sock, msg): """Send message on socket """ try: self.db[sock].send(msg) except KeyError: output.warn("Message dropped because socket is already closed", self.__class__.__name__)
def send(self, msg): """Send dictionary as JSON message """ try: self.sock.send(simplejson.dumps(msg)) output.dbg("Send message " + simplejson.dumps(msg), self.__class__.__name__) except socket.error: output.warn(str(self.sock) + " is broken, message is dropped!", self.__class__.__name__)
def processevent(self, event): """Process OpenFlow message for switch status @param event OpenFlow message event to process @return True """ if isinstance(event, ofevents.features_reply): # Maintain list of datapath socket key = self.get_key(event.sock) dpidsl = mc.get(dp_features.DP_SOCK_LIST) if dpidsl == None: dpidsl = [] if key not in dpidsl: dpidsl.append(key) mc.set(dp_features.DP_SOCK_LIST, dpidsl) # Maintain dp features in memcache mc.set(key, event.features) elif isinstance(event, ofevents.port_status): # Port status if event.header.type == pyof.OFPT_PORT_STATUS: key = self.get_key(event.sock) sw = mc.get(key) if sw == None: output.warn("Port status from unknown datapath", self.__class__.__name__) else: output.dbg("Received port status:\n" + event.port.show("\t"), self.__class__.__name__) if event.port.reason == pyof.OFPPR_DELETE or event.port.reason == pyof.OFPPR_MODIFY: for p in sw.ports: if p.port_no == event.port.desc.port_no: sw.ports.remove(p) if event.port.reason == pyof.OFPPR_ADD or event.port.reason == pyof.OFPPR_MODIFY: sw.ports.append(event.port.desc) output.dbg("Updated switch features:\n" + sw.show("\t"), self.__class__.__name__) mc.set(key, sw) elif isinstance(event, comm.event): # Socket close if event.event == comm.event.SOCK_CLOSE: key = self.get_key(event.sock) sw = mc.get(key) if sw != None: output.info("Datapath %x leaves" % sw.datapath_id, self.__class__.__name__) # Maintain list of datapath socket dpidsl = mc.get(dp_features.DP_SOCK_LIST) if dpidsl != None: if key in dpidsl: dpidsl.remove(key) mc.set(dp_features.DP_SOCK_LIST, dpidsl) # Remove features mc.delete(key) return True
def parse(self, entry_line): """Parse line """ i = entry_line.split() if (len(i) == 3): pass #Incomplete entry elif (len(i) != 5): output.warn("ARP entry line should have 5 items but "+str(len(i))+" found", self.__class__.__name__) else: self.ip = i[0] self.mac = i[2] self.iface = i[4]
def get_conn(self): """Get connection @return the one and only connection to send messages (None otherwise) """ if (len(self.conn.db) > 1): output.warn("More than one connection to COIN!", self.__class__.__name__) for s,c in self.conn.db.items(): return c return None
def del_if(self, intf): """Remove interface to datapath @param intf name of interface @return command's exit status """ if (intf in self.interfaces): self.interfaces.remove(intf) return cmd.run_cmd(DPCTL+" del-if "+self.name+" "+intf, self.__class__.__name__) else: output.warn("Interface "+intf+" do not exist for deletion", self.__class__.__name__)
def get_sw_feature(self): """Get switch description @return switch features else None """ dpidsl = mc.get(swstate.dp_features.DP_SOCK_LIST) if dpidsl != None: if len(dpidsl) > 1: output.warn(str(len(dpidsl)) + " datapaths connected to COIN", self.__class__.__name__) f = mc.get(dpidsl[0]) return f else: return None
def add_row(self, row): """Add data to the table @param row is a tuple of data """ if (len(row) == len(self.columns)): output.vdbg("Add row :"+str(row)) self.data_cache.append(row) if (len(self.data_cache) >= self.cache_size): self.flush_cache() else: output.warn("Adding row "+str(row)+" with "+len(row)+" items"+\ " when there is "+len(self.columns)+" columns", self.__class__.__name__)
def set(name, value, timeout=0): """Set key and value @param name key name @param value value to set key to @param timeout timeout to expire data """ global memcache_mode if memcache_mode == MEMCACHE_MODE["OFF"]: return None if name.find(" ") != -1: output.warn("Memcache key cannot contain spaces", "memcache util") return memcache_client.set(name, value, timeout)
def flush_cache(self, close=True): """Flush data in cache in database """ if (self.db == None): output.warn("No database to flush data into", self.__class__.__name__) return output.vdbg("Flush cache of "+str(len(self.data_cache))+" items", self.__class__.__name__) stmt = "?,"*len(self.columns) self.db.executemany("INSERT INTO "+self.name+\ " VALUES ("+stmt[:-1]+")", self.data_cache, close) self.data_cache = []
def registereventhandler(self, eventname, handler): """Register handler for event Event should be registered in order of calling @param eventname name of event @param handler handler function """ if (not isinstance(eventname, str)): output.warn("Event name "+str(eventname)+" is not a string", self.__class__.__name__) return #Register handler if (eventname not in self._processors): self._processors[eventname] = [] self._processors[eventname].append(handler)
def send(self, msg): """Send OpenFlow message """ header = pyopenflow.ofp_header() if (len(msg) < len(header)): output.warn("Cannot send OpenFlow of length "+str(len(msg))) else: remain = header.unpack(msg) header.length = len(msg) output.vdbg("Send message "+header.show().strip().replace("\n",";"), self.__class__.__name__) try: self.sock.send(header.pack()+remain) except socket.error: output.warn("Broken pipe, message not sent", self.__class__.__name__)
def update_sw_feature(self): """Update switch feature in memcache """ sf = self.switch.get_sw_feature() if sf == None: output.warn("No switch features!!!", self.__class__.__name__) else: output.dbg("Set switch feature as " + sf.show(), self.__class__.__name__) mc.set(bridge.SW_FEATURE, sf) iport = self.switch.if_name2dpid_port_mac(self.loif.switch_intf)[1] if iport == None: output.warn("No inner port!!!", self.__class__.__name__) else: output.dbg("Set inner port as " + str(iport), self.__class__.__name__) mc.set(bridge.SW_INNER_PORT, iport)
def processevent(self, event): """Process OpenFlow and JSON messages @param event event to handle @return True """ if isinstance(event, ofevents.error): #OpenFlow error output.warn("Error of type "+str(event.error.type)+\ " code "+str(event.error.code), self.__class__.__name__) elif isinstance(event, jsoncomm.message): #JSON messages self.__processjson(event) return True
def post_event(self, event, timedelta = 0): """Post event @param event event to post @param timedelta to wait before posting event @return if successful """ #Check event is an event if (not isinstance(event, yapc.event)): output.warn(str(event)+"is not an event", self.__class__.__name__) if (timedelta == 0): return self.__scheduler.post_event(event) else: return self.__timedscheduler.post_event(event, timedelta+time.time())
def parse(self, entry_line): """Parse line """ i = entry_line.split() if (len(i) != 8): output.warn("Route entry line should have 8 items but "+str(len(i))+" found", self.__class__.__name__) return else: self.destination = i[0] self.gateway = i[1] self.mask = i[2] self.flags.parse(i[3]) self.metric = int(i[4]) self.ref = int(i[5]) self.use = int(i[6]) self.iface = i[7]
def set_priority(self, priority): """Set priority of flow entry Priority can be expressed as number or one of the following string expressed in yapc.openflowutil @param priority expression of priority @return success """ if isinstance(priority, int): self.priority = priority elif priority in ofutil.PRIORITY: self.priority = ofutil.PRIORITY[priority] else: output.warn("Unknown expression of priority " + str(priority), self.__class__.__name__) return False return True
def post_event(self, event, clock): """Post event @param event event to post @param clock time to post event @return success status """ if (clock > (time.time()+self.sleep)): self._events.add(event, clock) output.vvdbg("Added event "+event.name+" for time "+str(clock), self.__class__.__name__) return True else: output.warn("Cannot add event "+event.name+\ " shorter than "+str(self.sleep)+\ " before execution time", self.__class__.__name__) return False
def unpack_actions(self, string, actions=None): """Unpack actions @param string string with actions encoded @param actions list to add actions into @return list of actions """ remaining = string if (actions == None): actions = [] while (len(remaining) >= pyof.OFP_ACTION_HEADER_BYTES): (action, remaining) = self.unpack_action(remaining) actions.append(action) if (len(remaining) > 0): output.warn("Action array is of irregular length with "+\ str(len(remaining))+" bytes remaining.", self.__class__.__name__) return actions