From 89f2dc1cdd79700dfdd055061b24c105e8430959 Mon Sep 17 00:00:00 2001 From: Alex Reimers <alex@bigswitch.com> Date: Wed, 4 Apr 2012 21:47:22 -0700 Subject: [PATCH] Make static flow pusher into a service (IStaticFlowEntryPusherService). This will allow other Floodlight modules to push static flows without using the REST API. --- .../IStaticFlowEntryPusherService.java | 26 ++ .../staticflowentry/StaticFlowEntries.java | 195 +++++++++++--- .../StaticFlowEntryPusher.java | 246 ++++++++++-------- .../web/ClearStaticFlowEntriesResource.java | 21 ++ .../web/StaticFlowEntryWebRoutable.java | 1 + .../protocol/action/OFActionType.java | 2 +- .../action/OFActionVirtualLanIdentifier.java | 2 +- 7 files changed, 350 insertions(+), 143 deletions(-) create mode 100644 src/main/java/net/floodlightcontroller/staticflowentry/IStaticFlowEntryPusherService.java create mode 100644 src/main/java/net/floodlightcontroller/staticflowentry/web/ClearStaticFlowEntriesResource.java diff --git a/src/main/java/net/floodlightcontroller/staticflowentry/IStaticFlowEntryPusherService.java b/src/main/java/net/floodlightcontroller/staticflowentry/IStaticFlowEntryPusherService.java new file mode 100644 index 000000000..10e65678a --- /dev/null +++ b/src/main/java/net/floodlightcontroller/staticflowentry/IStaticFlowEntryPusherService.java @@ -0,0 +1,26 @@ +package net.floodlightcontroller.staticflowentry; + +import org.openflow.protocol.OFFlowMod; + +import net.floodlightcontroller.core.module.IFloodlightService; + +public interface IStaticFlowEntryPusherService extends IFloodlightService { + /** + * Adds a static flow. + * @param name Name of the flow mod. Must be unique. + * @param fm The flow to push. + * @param swDpid The switch DPID to push it to. + */ + public void addFlow(String name, OFFlowMod fm, String swDpid); + + /** + * Deletes a static flow + * @param name The name of the static flow to delete. + */ + public void deleteFlow(String name); + + /** + * Deletes all flows. + */ + public void deleteAllFlows(); +} diff --git a/src/main/java/net/floodlightcontroller/staticflowentry/StaticFlowEntries.java b/src/main/java/net/floodlightcontroller/staticflowentry/StaticFlowEntries.java index 5a2b52e0e..7359ff0a9 100644 --- a/src/main/java/net/floodlightcontroller/staticflowentry/StaticFlowEntries.java +++ b/src/main/java/net/floodlightcontroller/staticflowentry/StaticFlowEntries.java @@ -7,7 +7,12 @@ import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; + +import net.floodlightcontroller.core.util.AppCookie; +import net.floodlightcontroller.packet.IPv4; + import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.codehaus.jackson.JsonParseException; @@ -15,6 +20,8 @@ import org.codehaus.jackson.JsonParser; import org.codehaus.jackson.JsonToken; import org.codehaus.jackson.map.MappingJsonFactory; import org.openflow.protocol.OFFlowMod; +import org.openflow.protocol.OFMatch; +import org.openflow.protocol.OFPacketOut; import org.openflow.protocol.OFPort; import org.openflow.protocol.action.OFAction; import org.openflow.protocol.action.OFActionDataLayerDestination; @@ -29,14 +36,57 @@ import org.openflow.protocol.action.OFActionTransportLayerDestination; import org.openflow.protocol.action.OFActionTransportLayerSource; import org.openflow.protocol.action.OFActionVirtualLanIdentifier; import org.openflow.protocol.action.OFActionVirtualLanPriorityCodePoint; +import org.openflow.util.HexString; public class StaticFlowEntries { + protected static Logger log = LoggerFactory.getLogger(StaticFlowEntries.class); private static class SubActionStruct { OFAction action; int len; } + + /** + * This function generates a random hash for the bottom half of the cookie + * + * @param fm + * @param userCookie + * @param name + * @return A cookie that encodes the application ID and a hash + */ + public static long computeEntryCookie(OFFlowMod fm, int userCookie, String name) { + // flow-specific hash is next 20 bits LOOK! who knows if this + int prime = 211; + int flowHash = 2311; + for (int i=0; i < name.length(); i++) + flowHash = flowHash * prime + (int)name.charAt(i); + + return AppCookie.makeCookie(StaticFlowEntryPusher.STATIC_FLOW_APP_ID, flowHash); + } + + /** + * Sets defaults for an OFFlowMod + * @param fm The OFFlowMod to set defaults for + * @param entryName The name of the entry. Used to compute the cookie. + */ + public static void initDefaultFlowMod(OFFlowMod fm, String entryName) { + fm.setIdleTimeout((short) 0); // infinite + fm.setHardTimeout((short) 0); // infinite + fm.setBufferId(OFPacketOut.BUFFER_ID_NONE); + fm.setCommand((short) 0); + fm.setFlags((short) 0); + fm.setOutPort(OFPort.OFPP_NONE.getValue()); + fm.setCookie(computeEntryCookie(fm, 0, entryName)); + fm.setPriority(Short.MAX_VALUE); + } + + /** + * Gets the entry name of a flow mod + * @param fmJson The OFFlowMod in a JSON representation + * @return The name of the OFFlowMod, null if not found + * @throws IOException If ther was an error parsing the JSON + */ public static String getEntryNameFromJson(String fmJson) throws IOException{ MappingJsonFactory f = new MappingJsonFactory(); JsonParser jp; @@ -69,6 +119,98 @@ public class StaticFlowEntries { return null; } + /** + * Parses an OFFlowMod (and it's inner OFMatch) to the storage entry format. + * @param fm The FlowMod to parse + * @param sw The switch the FlowMod is going to be installed on + * @param name The name of this static flow entry + * @return A Map representation of the storage entry + */ + public static Map<String, Object> flowModToStorageEntry(OFFlowMod fm, String sw, String name) { + Map<String, Object> entry = new HashMap<String, Object>(); + OFMatch match = fm.getMatch(); + entry.put(StaticFlowEntryPusher.COLUMN_NAME, name); + entry.put(StaticFlowEntryPusher.COLUMN_SWITCH, sw); + entry.put(StaticFlowEntryPusher.COLUMN_ACTIONS, StaticFlowEntries.flowModActionsToString(fm.getActions())); + entry.put(StaticFlowEntryPusher.COLUMN_PRIORITY, Short.toString(fm.getPriority())); + entry.put(StaticFlowEntryPusher.COLUMN_ACTIVE, Boolean.toString(true)); + entry.put(StaticFlowEntryPusher.COLUMN_WILDCARD, Integer.toString(match.getWildcards())); + entry.put(StaticFlowEntryPusher.COLUMN_IN_PORT, Short.toString(match.getInputPort())); + entry.put(StaticFlowEntryPusher.COLUMN_DL_SRC, HexString.toHexString(match.getDataLayerSource())); + entry.put(StaticFlowEntryPusher.COLUMN_DL_DST, HexString.toHexString(match.getDataLayerDestination())); + entry.put(StaticFlowEntryPusher.COLUMN_DL_VLAN, Short.toString(match.getDataLayerVirtualLan())); + entry.put(StaticFlowEntryPusher.COLUMN_DL_VLAN_PCP, Short.toString(match.getDataLayerVirtualLanPriorityCodePoint())); + entry.put(StaticFlowEntryPusher.COLUMN_DL_TYPE, Short.toString(match.getDataLayerType())); + entry.put(StaticFlowEntryPusher.COLUMN_NW_TOS, Short.toString(match.getNetworkTypeOfService())); + entry.put(StaticFlowEntryPusher.COLUMN_NW_PROTO, Short.toString(match.getNetworkProtocol())); + entry.put(StaticFlowEntryPusher.COLUMN_NW_SRC, IPv4.fromIPv4Address(match.getNetworkSource())); + entry.put(StaticFlowEntryPusher.COLUMN_NW_DST, IPv4.fromIPv4Address(match.getNetworkDestination())); + entry.put(StaticFlowEntryPusher.COLUMN_TP_SRC, Short.toString(match.getTransportSource())); + entry.put(StaticFlowEntryPusher.COLUMN_TP_DST, Short.toString(match.getTransportDestination())); + return entry; + } + + /** + * Returns a + * @param fmActions A list of OFActions to encode into one string + * @return A string of the actions encoded for our database + */ + private static String flowModActionsToString(List<OFAction> fmActions) { + // TODO commas?, make a string array then join with commas in between + // TODO - some values may be in hex....i.e. ethertype + String actions = ""; + for (OFAction a : fmActions) { + switch(a.getType()) { + case OUTPUT: + actions += "output=" + Short.toString(((OFActionOutput)a).getPort()); + break; + case OPAQUE_ENQUEUE: + actions += "enqueue=" + Integer.toString(((OFActionEnqueue)a).getQueueId()); + break; + case SET_VLAN_ID: + actions += "set-vlan-id=" + + Short.toString(((OFActionVirtualLanIdentifier)a).getVirtualLanIdentifier()); + break; + case SET_VLAN_PCP: + actions += "set-vlan-priority=" + + Byte.toString(((OFActionVirtualLanPriorityCodePoint)a).getVirtualLanPriorityCodePoint()); + break; + case SET_DL_SRC: + actions += "set-src-mac=" + + HexString.toHexString(((OFActionDataLayerSource)a).getDataLayerAddress()); + break; + case SET_DL_DST: + actions += "set-dst-mac=" + + HexString.toHexString(((OFActionDataLayerDestination)a).getDataLayerAddress()); + break; + case SET_NW_TOS: + actions += "set-tos-bits=" + + Byte.toString(((OFActionNetworkTypeOfService)a).getNetworkTypeOfService()); + break; + case SET_NW_SRC: + actions += "set-nw-src=" + + IPv4.fromIPv4Address(((OFActionNetworkLayerSource)a).getNetworkAddress()); + break; + case SET_NW_DST: + actions += "set-nw-dst=" + + IPv4.fromIPv4Address(((OFActionNetworkLayerDestination)a).getNetworkAddress()); + break; + case SET_TP_SRC: + actions += "set-src-port=" + + Short.toString(((OFActionTransportLayerSource)a).getTransportPort()); + break; + case SET_TP_DST: + actions += "set-dst-port=" + + Short.toString(((OFActionTransportLayerDestination)a).getTransportPort()); + break; + default: + + } + + } + return actions; + } + /** * Turns a JSON formatted Static Flow Pusher string into a storage entry * Expects a string in JSON along the lines of: @@ -151,8 +293,13 @@ public class StaticFlowEntries { return entry; } - public static void parseActionString(String entryName, String switchName, - OFFlowMod flowMod, String actionstr, Logger log) { + /** + * Parses OFFlowMod actions from a database + * @param flowMod The OFFlowMod to set the actions for + * @param actionstr The string containing all the actions + * @param log A logger to log for errors. + */ + public static void parseActionString(OFFlowMod flowMod, String actionstr, Logger log) { List<OFAction> actions = new LinkedList<OFAction>(); int actionsLength = 0; if (actionstr != null) { @@ -304,9 +451,8 @@ public class StaticFlowEntries { private static SubActionStruct decode_strip_vlan(String subaction, Logger log) { SubActionStruct sa = null; - Matcher n; - - n = Pattern.compile("strip-vlan").matcher(subaction); + Matcher n = Pattern.compile("strip-vlan").matcher(subaction); + if (n.matches()) { OFActionStripVirtualLan action = new OFActionStripVirtualLan(); log.debug(" action {}", action); @@ -319,16 +465,14 @@ public class StaticFlowEntries { log.debug(" Invalid action: '{}'", subaction); return null; } - return sa; } private static SubActionStruct decode_set_vlan_id(String subaction, Logger log) { SubActionStruct sa = null; - Matcher n; + Matcher n = Pattern.compile("set-vlan-id=((?:0x)?\\d+)").matcher(subaction); - n = Pattern.compile("set-vlan-id=((?:0x)?\\d+)").matcher(subaction); if (n.matches()) { if (n.group(1) != null) { try { @@ -357,9 +501,8 @@ public class StaticFlowEntries { private static SubActionStruct decode_set_vlan_priority(String subaction, Logger log) { SubActionStruct sa = null; - Matcher n; + Matcher n = Pattern.compile("set-vlan-priority=((?:0x)?\\d+)").matcher(subaction); - n = Pattern.compile("set-vlan-priority=((?:0x)?\\d+)").matcher(subaction); if (n.matches()) { if (n.group(1) != null) { try { @@ -388,9 +531,8 @@ public class StaticFlowEntries { private static SubActionStruct decode_set_src_mac(String subaction, Logger log) { SubActionStruct sa = null; - Matcher n; - - n = Pattern.compile("set-src-mac=(?:(\\p{XDigit}+)\\:(\\p{XDigit}+)\\:(\\p{XDigit}+)\\:(\\p{XDigit}+)\\:(\\p{XDigit}+)\\:(\\p{XDigit}+))").matcher(subaction); + Matcher n = Pattern.compile("set-src-mac=(?:(\\p{XDigit}+)\\:(\\p{XDigit}+)\\:(\\p{XDigit}+)\\:(\\p{XDigit}+)\\:(\\p{XDigit}+)\\:(\\p{XDigit}+))").matcher(subaction); + if (n.matches()) { byte[] macaddr = get_mac_addr(n, subaction, log); if (macaddr != null) { @@ -413,9 +555,8 @@ public class StaticFlowEntries { private static SubActionStruct decode_set_dst_mac(String subaction, Logger log) { SubActionStruct sa = null; - Matcher n; + Matcher n = Pattern.compile("set-dst-mac=(?:(\\p{XDigit}+)\\:(\\p{XDigit}+)\\:(\\p{XDigit}+)\\:(\\p{XDigit}+)\\:(\\p{XDigit}+)\\:(\\p{XDigit}+))").matcher(subaction); - n = Pattern.compile("set-dst-mac=(?:(\\p{XDigit}+)\\:(\\p{XDigit}+)\\:(\\p{XDigit}+)\\:(\\p{XDigit}+)\\:(\\p{XDigit}+)\\:(\\p{XDigit}+))").matcher(subaction); if (n.matches()) { byte[] macaddr = get_mac_addr(n, subaction, log); if (macaddr != null) { @@ -438,9 +579,8 @@ public class StaticFlowEntries { private static SubActionStruct decode_set_tos_bits(String subaction, Logger log) { SubActionStruct sa = null; - Matcher n; + Matcher n = Pattern.compile("set-tos-bits=((?:0x)?\\d+)").matcher(subaction); - n = Pattern.compile("set-tos-bits=((?:0x)?\\d+)").matcher(subaction); if (n.matches()) { if (n.group(1) != null) { try { @@ -469,9 +609,8 @@ public class StaticFlowEntries { private static SubActionStruct decode_set_src_ip(String subaction, Logger log) { SubActionStruct sa = null; - Matcher n; + Matcher n = Pattern.compile("set-src-ip=(?:(\\d+)\\.(\\d+)\\.(\\d+)\\.(\\d+))").matcher(subaction); - n = Pattern.compile("set-src-ip=(?:(\\d+)\\.(\\d+)\\.(\\d+)\\.(\\d+))").matcher(subaction); if (n.matches()) { int ipaddr = get_ip_addr(n, subaction, log); OFActionNetworkLayerSource action = new OFActionNetworkLayerSource(); @@ -492,9 +631,8 @@ public class StaticFlowEntries { private static SubActionStruct decode_set_dst_ip(String subaction, Logger log) { SubActionStruct sa = null; - Matcher n; + Matcher n = Pattern.compile("set-dst-ip=(?:(\\d+)\\.(\\d+)\\.(\\d+)\\.(\\d+))").matcher(subaction); - n = Pattern.compile("set-dst-ip=(?:(\\d+)\\.(\\d+)\\.(\\d+)\\.(\\d+))").matcher(subaction); if (n.matches()) { int ipaddr = get_ip_addr(n, subaction, log); OFActionNetworkLayerDestination action = new OFActionNetworkLayerDestination(); @@ -515,9 +653,8 @@ public class StaticFlowEntries { private static SubActionStruct decode_set_src_port(String subaction, Logger log) { SubActionStruct sa = null; - Matcher n; + Matcher n = Pattern.compile("set-src-port=((?:0x)?\\d+)").matcher(subaction); - n = Pattern.compile("set-src-port=((?:0x)?\\d+)").matcher(subaction); if (n.matches()) { if (n.group(1) != null) { try { @@ -546,9 +683,8 @@ public class StaticFlowEntries { private static SubActionStruct decode_set_dst_port(String subaction, Logger log) { SubActionStruct sa = null; - Matcher n; + Matcher n = Pattern.compile("set-dst-port=((?:0x)?\\d+)").matcher(subaction); - n = Pattern.compile("set-dst-port=((?:0x)?\\d+)").matcher(subaction); if (n.matches()) { if (n.group(1) != null) { try { @@ -621,20 +757,17 @@ public class StaticFlowEntries { } // Parse int as decimal, hex (start with 0x or #) or octal (starts with 0) - private static int get_int(String str) - { + private static int get_int(String str) { return (int)Integer.decode(str); } // Parse short as decimal, hex (start with 0x or #) or octal (starts with 0) - private static short get_short(String str) - { + private static short get_short(String str) { return (short)(int)Integer.decode(str); } // Parse byte as decimal, hex (start with 0x or #) or octal (starts with 0) - private static byte get_byte(String str) - { + private static byte get_byte(String str) { return Integer.decode(str).byteValue(); } diff --git a/src/main/java/net/floodlightcontroller/staticflowentry/StaticFlowEntryPusher.java b/src/main/java/net/floodlightcontroller/staticflowentry/StaticFlowEntryPusher.java index 4d75aa8aa..6e83f5a49 100644 --- a/src/main/java/net/floodlightcontroller/staticflowentry/StaticFlowEntryPusher.java +++ b/src/main/java/net/floodlightcontroller/staticflowentry/StaticFlowEntryPusher.java @@ -23,6 +23,7 @@ import net.floodlightcontroller.core.module.IFloodlightService; import net.floodlightcontroller.core.util.AppCookie; import net.floodlightcontroller.restserver.IRestApiService; import net.floodlightcontroller.staticflowentry.web.StaticFlowEntryWebRoutable; +import net.floodlightcontroller.staticflowentry.IStaticFlowEntryPusherService; import net.floodlightcontroller.storage.IResultSet; import net.floodlightcontroller.storage.IStorageSourceService; import net.floodlightcontroller.storage.IStorageSourceListener; @@ -32,7 +33,6 @@ import org.openflow.protocol.OFFlowMod; import org.openflow.protocol.OFFlowRemoved; import org.openflow.protocol.OFMatch; import org.openflow.protocol.OFMessage; -import org.openflow.protocol.OFPort; import org.openflow.protocol.OFType; import org.openflow.protocol.factory.BasicFactory; import org.openflow.util.HexString; @@ -41,7 +41,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class StaticFlowEntryPusher - implements IOFSwitchListener, IFloodlightModule, + implements IOFSwitchListener, IFloodlightModule, IStaticFlowEntryPusherService, IStorageSourceListener, IOFMessageListener { protected static Logger log = LoggerFactory.getLogger(StaticFlowEntryPusher.class); public static final String StaticFlowName = "staticflowentry"; @@ -84,8 +84,9 @@ public class StaticFlowEntryPusher protected IStorageSourceService storageSource; protected IRestApiService restApi; - // entriesFromStorage[$dpid]->[$entryName = FlowMod] ; FlowMod can be null to indicate non-active + // Map<DPID, Map<Name, FlowMod>> ; FlowMod can be null to indicate non-active protected HashMap<String, Map<String, OFFlowMod>> entriesFromStorage; + // Entry Name -> DPID of Switch it's on protected Map<String, String> entry2dpid; private BasicFactory ofMessageFactory; @@ -110,7 +111,6 @@ public class StaticFlowEntryPusher * used for debugging and unittests * @return the number of static flow entries as cached from storage */ - public int countEntries() { int size = 0; if (entriesFromStorage == null) @@ -132,17 +132,6 @@ public class StaticFlowEntryPusher this.storageSource = storageSource; } - protected void initDefaultFlowMod(OFFlowMod fm, String entryName) { - fm.setIdleTimeout((short) 0); // infinite - fm.setHardTimeout((short) 0); // infinite - fm.setBufferId(-1); - fm.setCommand((short) 0); - fm.setFlags((short) 0); - fm.setOutPort(OFPort.OFPP_NONE.getValue()); - fm.setCookie(computeEntryCookie(fm, 0, entryName)); - fm.setPriority(Short.MAX_VALUE); - } - /** * Reads from our entriesFromStorage for the specified switch and * sends the FlowMods down to the controller in <b>sorted</b> order. @@ -156,9 +145,8 @@ public class StaticFlowEntryPusher * TODO consider adding a "block all" flow mod and then removing it * while starting up. * - * @param sw + * @param sw The switch to send entries to */ - protected void sendEntriesToSwitch(IOFSwitch sw) { String dpid = sw.getStringId(); @@ -169,16 +157,11 @@ public class StaticFlowEntryPusher Collections.sort( sortedList, new FlowModSorter(dpid)); for (String entryName : sortedList) { OFFlowMod flowMod = entries.get(entryName); - try { - if ( flowMod != null) { - log.debug(" push static entry {} for {}", dpid, entryName); - sw.write(flowMod, null); - sw.flush(); - // Srini: FlowMod Sent to Switches + if (flowMod != null) { + if (log.isDebugEnabled()) { + log.debug("Pushing static entry {} for {}", dpid, entryName); } - } catch (IOException e) { - log.error("Failed to push static flow entry for {}: {}", - dpid, e.getMessage()); + writeFlowModToSwitch(sw, flowMod); } } } @@ -261,7 +244,7 @@ public class StaticFlowEntryPusher entryName = (String) row.get(COLUMN_NAME); if (!entries.containsKey(switchName)) entries.put(switchName, new HashMap<String, OFFlowMod>()); - initDefaultFlowMod(flowMod, entryName); + StaticFlowEntries.initDefaultFlowMod(flowMod, entryName); for (String key : row.keySet()) { if (row.get(key) == null) @@ -281,12 +264,11 @@ public class StaticFlowEntryPusher return; } } else if ( key.equals(COLUMN_ACTIONS)){ - StaticFlowEntries.parseActionString(entryName, switchName, flowMod, - (String) row.get(COLUMN_ACTIONS), log); + StaticFlowEntries.parseActionString(flowMod, (String) row.get(COLUMN_ACTIONS), log); } else if ( key.equals(COLUMN_COOKIE)) { flowMod.setCookie( - computeEntryCookie(flowMod, - Integer.valueOf( (String) row.get(COLUMN_COOKIE)), + StaticFlowEntries.computeEntryCookie(flowMod, + Integer.valueOf((String) row.get(COLUMN_COOKIE)), entryName) ); } else if ( key.equals(COLUMN_PRIORITY)) { @@ -322,28 +304,6 @@ public class StaticFlowEntryPusher entries.get(switchName).put(entryName, flowMod); } - /** - * This function generates a random hash for the bottom half of the cookie - * - * FIXME Decide if we really want this... it's actually very likely that this was - * a misunderstanding that cookies needed to be unique, which they definitely don't. - * - * @param fm - * @param userCookie - * @param name - * @return A cookie that encodes the Application ID and a hash - */ - - public static long computeEntryCookie(OFFlowMod fm, int userCookie, String name) { - // flow-specific hash is next 20 bits LOOK! who knows if this - int prime = 211; - int flowHash = 2311; - for (int i=0; i < name.length(); i++) - flowHash = flowHash * prime + (int)name.charAt(i); - - return AppCookie.makeCookie(STATIC_FLOW_APP_ID, flowHash); - } - @Override public void addedSwitch(IOFSwitch sw) { log.debug("addedSwitch {}; processing its static entries", sw); @@ -380,14 +340,14 @@ public class StaticFlowEntryPusher if (!entriesFromStorage.containsKey(dpid)) entriesFromStorage.put(dpid, new HashMap<String, OFFlowMod>()); List<OFMessage> outQueue = new ArrayList<OFMessage>(); - for(String entry: entriesToAdd.get(dpid).keySet()) { + for(String entry : entriesToAdd.get(dpid).keySet()) { OFFlowMod newFlowMod = entriesToAdd.get(dpid).get(entry); OFFlowMod oldFlowMod = entriesFromStorage.get(dpid).get(entry); if (oldFlowMod != null) { // remove any pre-existing rule oldFlowMod.setCommand(OFFlowMod.OFPFC_DELETE_STRICT); outQueue.add(oldFlowMod); } - if ( newFlowMod != null) { + if (newFlowMod != null) { entriesFromStorage.get(dpid).put(entry, newFlowMod); outQueue.add(newFlowMod); entry2dpid.put(entry, dpid); @@ -396,19 +356,8 @@ public class StaticFlowEntryPusher entry2dpid.remove(entry); } } - IOFSwitch ofswitch = floodlightProvider.getSwitches().get(HexString.toLong(dpid)); - if (ofswitch != null) { // is the switch connected - try { - if (log.isDebugEnabled()) { - log.debug("Sending {} new entries to {}", outQueue.size(), dpid); - } - ofswitch.write(outQueue, null); - ofswitch.flush(); - } catch (IOException e) { - log.error("writed to write to switch {} but got {}", - dpid, e.getMessage()); - } - } + + writeOFMessagesToSwitch(HexString.toLong(dpid), outQueue); } } @@ -423,40 +372,91 @@ public class StaticFlowEntryPusher log.error("tried to delete non-string key {}; ignoring", obj); continue; } - String entryName = (String) obj; - String dpid = entry2dpid.get(entryName); - if (dpid == null) { - log.error("inconsistent internal state: no switch has rule {}", - entryName); - continue; - } - // send flow_mod delete - OFFlowMod flowMod = entriesFromStorage.get(dpid).get(entryName); - flowMod.setCommand(OFFlowMod.OFPFC_DELETE_STRICT); - log.debug("deleting flow {} for switch {}", entryName, dpid); - Map<Long,IOFSwitch> switches = floodlightProvider.getSwitches(); - - if ( entriesFromStorage.containsKey(dpid) && - entriesFromStorage.get(dpid).containsKey(entryName)) - entriesFromStorage.get(dpid).remove(entryName); - else - log.error("tried to delete non-existent entry {} for switch {}", - entryName, dpid); - - IOFSwitch ofSwitch = switches.get(HexString.toLong(dpid)); - if ( ofSwitch == null) { - log.debug("Not deleting key {} :: switch {} not connected", dpid); - continue; - } + deleteStaticFlowEntry((String) obj); + } + } + + private boolean deleteStaticFlowEntry(String entryName) { + String dpid = entry2dpid.get(entryName); + if (log.isDebugEnabled()) { + log.debug("Deleting flow {} for switch {}", entryName, dpid); + } + if (dpid == null) { + log.error("inconsistent internal state: no switch has rule {}", + entryName); + return false; + } + + // send flow_mod delete + OFFlowMod flowMod = entriesFromStorage.get(dpid).get(entryName); + flowMod.setCommand(OFFlowMod.OFPFC_DELETE_STRICT); + + if (entriesFromStorage.containsKey(dpid) && + entriesFromStorage.get(dpid).containsKey(entryName)) { + entriesFromStorage.get(dpid).remove(entryName); + } else { + log.error("Tried to delete non-existent entry {} for switch {}", + entryName, dpid); + return false; + } + + writeFlowModToSwitch(HexString.toLong(dpid), flowMod); + return true; + } + + /** + * Writes a list of OFMessages to a switch + * @param dpid The datapath ID of the switch to write to + * @param messages The list of OFMessages to write. + */ + private void writeOFMessagesToSwitch(long dpid, List<OFMessage> messages) { + IOFSwitch ofswitch = floodlightProvider.getSwitches().get(dpid); + if (ofswitch != null) { // is the switch connected try { - ofSwitch.write(flowMod, null); - ofSwitch.flush(); + if (log.isDebugEnabled()) { + log.debug("Sending {} new entries to {}", messages.size(), dpid); + } + ofswitch.write(messages, null); + ofswitch.flush(); } catch (IOException e) { - log.error("tried to write flow_mod delete to {} but failed: {}", - dpid, e.getMessage()); + log.error("writed to write to switch {} but got {}", dpid, e.getMessage()); } } } + + /** + * Writes an OFFlowMod to a switch. It checks to make sure the switch + * exists before it sends + * @param dpid The data to write the flow mod to + * @param flowMod The OFFlowMod to write + */ + private void writeFlowModToSwitch(long dpid, OFFlowMod flowMod) { + Map<Long,IOFSwitch> switches = floodlightProvider.getSwitches(); + IOFSwitch ofSwitch = switches.get(dpid); + if (ofSwitch == null) { + if (log.isDebugEnabled()) { + log.debug("Not deleting key {} :: switch {} not connected", + dpid); + } + return; + } + writeFlowModToSwitch(ofSwitch, flowMod); + } + + /** + * Writes an OFFlowMod to a switch + * @param sw The IOFSwitch to write to + * @param flowMod The OFFlowMod to write + */ + private void writeFlowModToSwitch(IOFSwitch sw, OFFlowMod flowMod) { + try { + sw.write(flowMod, null); + sw.flush(); + } catch (IOException e) { + log.error("Tried to write OFFlowMod to {} but failed: {}", + HexString.toHexString(sw.getId()), e.getMessage()); + } + } @Override public String getName() { @@ -479,7 +479,7 @@ public class StaticFlowEntryPusher * never expire. */ if( AppCookie.extractApp(cookie) == STATIC_FLOW_APP_ID) { - if ( flowRemoved.getReason() != OFFlowRemoved.OFFlowRemovedReason.OFPRR_DELETE) + if (flowRemoved.getReason() != OFFlowRemoved.OFFlowRemovedReason.OFPRR_DELETE) log.error("PANIC -- got a FlowRemove message for a infinite timeout flow: {} from switch {}", msg, sw); return Command.STOP; // only for us @@ -489,19 +489,11 @@ public class StaticFlowEntryPusher @Override public boolean isCallbackOrderingPrereq(OFType type, String name) { - // packet in listener goes after topology - if (type == OFType.PACKET_IN && "topology".equals(name)) { - return true; - } return false; // no dependency for non-packet in } @Override public boolean isCallbackOrderingPostreq(OFType type, String name) { - // packet in listener goes before devicemanager - if (type == OFType.PACKET_IN && "devicemanager".equals(name)) { - return true; - } return false; // no dependency for non-packet in } @@ -509,14 +501,20 @@ public class StaticFlowEntryPusher @Override public Collection<Class<? extends IFloodlightService>> getModuleServices() { - // We don't have any services - return null; + Collection<Class<? extends IFloodlightService>> l = + new ArrayList<Class<? extends IFloodlightService>>(); + l.add(IStaticFlowEntryPusherService.class); + return l; } @Override public Map<Class<? extends IFloodlightService>, IFloodlightService> getServiceImpls() { - // We don't have any services - return null; + Map<Class<? extends IFloodlightService>, + IFloodlightService> m = + new HashMap<Class<? extends IFloodlightService>, + IFloodlightService>(); + m.put(IStaticFlowEntryPusherService.class, this); + return m; } @Override @@ -553,4 +551,32 @@ public class StaticFlowEntryPusher entry2dpid = computeEntry2DpidMap(entriesFromStorage); restApi.addRestletRoutable(new StaticFlowEntryWebRoutable()); } + + // IStaticFlowEntryPusherService methods + + @Override + public void addFlow(String name, OFFlowMod fm, String swDpid) { + Map<String, Object> fmMap = StaticFlowEntries.flowModToStorageEntry(fm, swDpid, name); + entry2dpid.put(name, swDpid); + Map<String, OFFlowMod> switchEntries = entriesFromStorage.get(swDpid); + if (switchEntries == null) { + switchEntries = new HashMap<String, OFFlowMod>(); + entriesFromStorage.put(swDpid, switchEntries); + } + switchEntries.put(name, fm); + storageSource.insertRowAsync(TABLE_NAME, fmMap); + } + + @Override + public void deleteFlow(String name) { + storageSource.deleteRowAsync(TABLE_NAME, name); + // TODO - What if there is a delay in storage? + } + + @Override + public void deleteAllFlows() { + for (String entry : entry2dpid.keySet()) { + deleteFlow(entry); + } + } } diff --git a/src/main/java/net/floodlightcontroller/staticflowentry/web/ClearStaticFlowEntriesResource.java b/src/main/java/net/floodlightcontroller/staticflowentry/web/ClearStaticFlowEntriesResource.java new file mode 100644 index 000000000..43474c5a2 --- /dev/null +++ b/src/main/java/net/floodlightcontroller/staticflowentry/web/ClearStaticFlowEntriesResource.java @@ -0,0 +1,21 @@ +package net.floodlightcontroller.staticflowentry.web; + +import net.floodlightcontroller.staticflowentry.IStaticFlowEntryPusherService; + +import org.restlet.resource.Get; +import org.restlet.resource.ServerResource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ClearStaticFlowEntriesResource extends ServerResource { + protected static Logger log = LoggerFactory.getLogger(ClearStaticFlowEntriesResource.class); + + @Get + public void IStaticFlowEntryPusherService() { + IStaticFlowEntryPusherService sfpService = + (IStaticFlowEntryPusherService)getContext().getAttributes(). + get(IStaticFlowEntryPusherService.class.getCanonicalName()); + log.debug("Clearing all static flow entires"); + sfpService.deleteAllFlows(); + } +} diff --git a/src/main/java/net/floodlightcontroller/staticflowentry/web/StaticFlowEntryWebRoutable.java b/src/main/java/net/floodlightcontroller/staticflowentry/web/StaticFlowEntryWebRoutable.java index badd9d926..d8eb0537a 100644 --- a/src/main/java/net/floodlightcontroller/staticflowentry/web/StaticFlowEntryWebRoutable.java +++ b/src/main/java/net/floodlightcontroller/staticflowentry/web/StaticFlowEntryWebRoutable.java @@ -14,6 +14,7 @@ public class StaticFlowEntryWebRoutable implements RestletRoutable { public Restlet getRestlet(Context context) { Router router = new Router(context); router.attach("/json", StaticFlowEntryPusherResource.class); + router.attach("/clear/json", ClearStaticFlowEntriesResource.class); return router; } diff --git a/src/main/java/org/openflow/protocol/action/OFActionType.java b/src/main/java/org/openflow/protocol/action/OFActionType.java index 78353dde1..b0c2c47d9 100644 --- a/src/main/java/org/openflow/protocol/action/OFActionType.java +++ b/src/main/java/org/openflow/protocol/action/OFActionType.java @@ -36,7 +36,7 @@ public enum OFActionType { public OFAction instantiate() { return new OFActionOutput(); }}), - SET_VLAN_VID (1, OFActionVirtualLanIdentifier.class, new Instantiable<OFAction>() { + SET_VLAN_ID (1, OFActionVirtualLanIdentifier.class, new Instantiable<OFAction>() { @Override public OFAction instantiate() { return new OFActionVirtualLanIdentifier(); diff --git a/src/main/java/org/openflow/protocol/action/OFActionVirtualLanIdentifier.java b/src/main/java/org/openflow/protocol/action/OFActionVirtualLanIdentifier.java index 325372fc3..dc3949630 100644 --- a/src/main/java/org/openflow/protocol/action/OFActionVirtualLanIdentifier.java +++ b/src/main/java/org/openflow/protocol/action/OFActionVirtualLanIdentifier.java @@ -33,7 +33,7 @@ public class OFActionVirtualLanIdentifier extends OFAction { protected short virtualLanIdentifier; public OFActionVirtualLanIdentifier() { - super.setType(OFActionType.SET_VLAN_VID); + super.setType(OFActionType.SET_VLAN_ID); super.setLength((short) MINIMUM_LENGTH); } -- GitLab