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