| package net.floodlightcontroller.staticflowentry; |
| |
| import java.io.IOException; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.Comparator; |
| import java.util.Iterator; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Map.Entry; |
| import java.util.Set; |
| import java.util.concurrent.ConcurrentHashMap; |
| |
| import net.floodlightcontroller.core.FloodlightContext; |
| import net.floodlightcontroller.core.IFloodlightProviderService; |
| import net.floodlightcontroller.core.IFloodlightProviderService.Role; |
| import net.floodlightcontroller.core.IHAListener; |
| import net.floodlightcontroller.core.IOFMessageListener; |
| import net.floodlightcontroller.core.IOFSwitch; |
| import net.floodlightcontroller.core.IOFSwitchListener; |
| import net.floodlightcontroller.core.annotations.LogMessageCategory; |
| import net.floodlightcontroller.core.annotations.LogMessageDoc; |
| import net.floodlightcontroller.core.module.FloodlightModuleContext; |
| import net.floodlightcontroller.core.module.FloodlightModuleException; |
| import net.floodlightcontroller.core.module.IFloodlightModule; |
| 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; |
| |
| import net.floodlightcontroller.storage.StorageException; |
| import org.openflow.protocol.OFFlowMod; |
| import org.openflow.protocol.OFFlowRemoved; |
| import org.openflow.protocol.OFMatch; |
| import org.openflow.protocol.OFMessage; |
| import org.openflow.protocol.OFType; |
| import org.openflow.protocol.factory.BasicFactory; |
| import org.openflow.util.HexString; |
| import org.openflow.util.U16; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| @LogMessageCategory("Static Flow Pusher") |
| /** |
| * This module is responsible for maintaining a set of static flows on |
| * switches. This is just a big 'ol dumb list of flows and something external |
| * is responsible for ensuring they make sense for the network. |
| */ |
| public class StaticFlowEntryPusher |
| implements IOFSwitchListener, IFloodlightModule, IStaticFlowEntryPusherService, |
| IStorageSourceListener, IOFMessageListener, IHAListener { |
| protected static Logger log = LoggerFactory.getLogger(StaticFlowEntryPusher.class); |
| public static final String StaticFlowName = "staticflowentry"; |
| |
| public static final int STATIC_FLOW_APP_ID = 10; |
| |
| public static final String TABLE_NAME = "controller_staticflowtableentry"; |
| public static final String COLUMN_NAME = "name"; |
| public static final String COLUMN_SWITCH = "switch_id"; |
| public static final String COLUMN_ACTIVE = "active"; |
| public static final String COLUMN_IDLE_TIMEOUT = "idle_timeout"; |
| public static final String COLUMN_HARD_TIMEOUT = "hard_timeout"; |
| public static final String COLUMN_PRIORITY = "priority"; |
| public static final String COLUMN_COOKIE = "cookie"; |
| public static final String COLUMN_WILDCARD = "wildcards"; |
| public static final String COLUMN_IN_PORT = "in_port"; |
| public static final String COLUMN_DL_SRC = "dl_src"; |
| public static final String COLUMN_DL_DST = "dl_dst"; |
| public static final String COLUMN_DL_VLAN = "dl_vlan"; |
| public static final String COLUMN_DL_VLAN_PCP = "dl_vlan_pcp"; |
| public static final String COLUMN_DL_TYPE = "dl_type"; |
| public static final String COLUMN_NW_TOS = "nw_tos"; |
| public static final String COLUMN_NW_PROTO = "nw_proto"; |
| public static final String COLUMN_NW_SRC = "nw_src"; // includes CIDR-style |
| // netmask, e.g. |
| // "128.8.128.0/24" |
| public static final String COLUMN_NW_DST = "nw_dst"; |
| public static final String COLUMN_TP_DST = "tp_dst"; |
| public static final String COLUMN_TP_SRC = "tp_src"; |
| public static final String COLUMN_ACTIONS = "actions"; |
| public static String ColumnNames[] = { COLUMN_NAME, COLUMN_SWITCH, |
| COLUMN_ACTIVE, COLUMN_IDLE_TIMEOUT, COLUMN_HARD_TIMEOUT, |
| COLUMN_PRIORITY, COLUMN_COOKIE, COLUMN_WILDCARD, COLUMN_IN_PORT, |
| COLUMN_DL_SRC, COLUMN_DL_DST, COLUMN_DL_VLAN, COLUMN_DL_VLAN_PCP, |
| COLUMN_DL_TYPE, COLUMN_NW_TOS, COLUMN_NW_PROTO, COLUMN_NW_SRC, |
| COLUMN_NW_DST, COLUMN_TP_DST, COLUMN_TP_SRC, COLUMN_ACTIONS }; |
| |
| |
| protected IFloodlightProviderService floodlightProvider; |
| protected IStorageSourceService storageSource; |
| protected IRestApiService restApi; |
| |
| // Map<DPID, Map<Name, FlowMod>> ; FlowMod can be null to indicate non-active |
| protected Map<String, Map<String, OFFlowMod>> entriesFromStorage; |
| // Entry Name -> DPID of Switch it's on |
| protected Map<String, String> entry2dpid; |
| |
| private BasicFactory ofMessageFactory; |
| |
| // Class to sort FlowMod's by priority, from lowest to highest |
| class FlowModSorter implements Comparator<String> { |
| private String dpid; |
| public FlowModSorter(String dpid) { |
| this.dpid = dpid; |
| } |
| @Override |
| public int compare(String o1, String o2) { |
| OFFlowMod f1 = entriesFromStorage.get(dpid).get(o1); |
| OFFlowMod f2 = entriesFromStorage.get(dpid).get(o2); |
| if (f1 == null || f2 == null) // sort active=false flows by key |
| return o1.compareTo(o2); |
| return U16.f(f1.getPriority()) - U16.f(f2.getPriority()); |
| } |
| }; |
| |
| /** |
| * 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) |
| return 0; |
| for (String ofswitch : entriesFromStorage.keySet()) |
| size += entriesFromStorage.get(ofswitch).size(); |
| return size; |
| } |
| |
| public IFloodlightProviderService getFloodlightProvider() { |
| return floodlightProvider; |
| } |
| |
| public void setFloodlightProvider(IFloodlightProviderService floodlightProvider) { |
| this.floodlightProvider = floodlightProvider; |
| } |
| |
| public void setStorageSource(IStorageSourceService storageSource) { |
| this.storageSource = storageSource; |
| } |
| |
| /** |
| * Reads from our entriesFromStorage for the specified switch and |
| * sends the FlowMods down to the controller in <b>sorted</b> order. |
| * |
| * Sorted is important to maintain correctness of the switch: |
| * if a packet would match both a lower and a higher priority |
| * rule, then we want it to match the higher priority or nothing, |
| * but never just the lower priority one. Inserting from high to |
| * low priority fixes this. |
| * |
| * TODO consider adding a "block all" flow mod and then removing it |
| * while starting up. |
| * |
| * @param sw The switch to send entries to |
| */ |
| protected void sendEntriesToSwitch(IOFSwitch sw) { |
| String dpid = sw.getStringId(); |
| |
| if ((entriesFromStorage != null) && (entriesFromStorage.containsKey(dpid))) { |
| Map<String, OFFlowMod> entries = entriesFromStorage.get(dpid); |
| List<String> sortedList = new ArrayList<String>(entries.keySet()); |
| // weird that Collections.sort() returns void |
| Collections.sort( sortedList, new FlowModSorter(dpid)); |
| for (String entryName : sortedList) { |
| OFFlowMod flowMod = entries.get(entryName); |
| if (flowMod != null) { |
| if (log.isDebugEnabled()) { |
| log.debug("Pushing static entry {} for {}", dpid, entryName); |
| } |
| writeFlowModToSwitch(sw, flowMod); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Used only for bundle-local indexing |
| * |
| * @param map |
| * @return |
| */ |
| |
| protected Map<String, String> computeEntry2DpidMap( |
| Map<String, Map<String, OFFlowMod>> map) { |
| Map<String, String> ret = new HashMap<String, String>(); |
| for(String dpid : map.keySet()) { |
| for( String entry: map.get(dpid).keySet()) |
| ret.put(entry, dpid); |
| } |
| return ret; |
| } |
| |
| /** |
| * Read entries from storageSource, and store them in a hash |
| * |
| * @return |
| */ |
| @LogMessageDoc(level="ERROR", |
| message="failed to access storage: {reason}", |
| explanation="Could not retrieve static flows from the system " + |
| "database", |
| recommendation=LogMessageDoc.CHECK_CONTROLLER) |
| private Map<String, Map<String, OFFlowMod>> readEntriesFromStorage() { |
| Map<String, Map<String, OFFlowMod>> entries = new ConcurrentHashMap<String, Map<String, OFFlowMod>>(); |
| try { |
| Map<String, Object> row; |
| // null1=no predicate, null2=no ordering |
| IResultSet resultSet = storageSource.executeQuery(TABLE_NAME, |
| ColumnNames, null, null); |
| for (Iterator<IResultSet> it = resultSet.iterator(); it.hasNext();) { |
| row = it.next().getRow(); |
| parseRow(row, entries); |
| } |
| } catch (StorageException e) { |
| log.error("failed to access storage: {}", e.getMessage()); |
| // if the table doesn't exist, then wait to populate later via |
| // setStorageSource() |
| } |
| return entries; |
| } |
| |
| /** |
| * Take a single row, turn it into a flowMod, and add it to the |
| * entries{$dpid}.{$entryName}=FlowMod |
| * |
| * IF an entry is in active, mark it with FlowMod = null |
| * |
| * @param row |
| * @param entries |
| */ |
| |
| void parseRow(Map<String, Object> row, |
| Map<String, Map<String, OFFlowMod>> entries) { |
| String switchName = null; |
| String entryName = null; |
| |
| StringBuffer matchString = new StringBuffer(); |
| if (ofMessageFactory == null) // lazy init |
| ofMessageFactory = new BasicFactory(); |
| |
| OFFlowMod flowMod = (OFFlowMod) ofMessageFactory |
| .getMessage(OFType.FLOW_MOD); |
| |
| if (!row.containsKey(COLUMN_SWITCH) || !row.containsKey(COLUMN_NAME)) { |
| log.debug( |
| "skipping entry with missing required 'switch' or 'name' entry: {}", |
| row); |
| return; |
| } |
| // most error checking done with ClassCastException |
| try { |
| // first, snag the required entries, for debugging info |
| switchName = (String) row.get(COLUMN_SWITCH); |
| entryName = (String) row.get(COLUMN_NAME); |
| if (!entries.containsKey(switchName)) |
| entries.put(switchName, new HashMap<String, OFFlowMod>()); |
| StaticFlowEntries.initDefaultFlowMod(flowMod, entryName); |
| |
| for (String key : row.keySet()) { |
| if (row.get(key) == null) |
| continue; |
| if ( key.equals(COLUMN_SWITCH) || key.equals(COLUMN_NAME) |
| || key.equals("id")) |
| continue; // already handled |
| // explicitly ignore timeouts and wildcards |
| if ( key.equals(COLUMN_HARD_TIMEOUT) || key.equals(COLUMN_IDLE_TIMEOUT) || |
| key.equals(COLUMN_WILDCARD)) |
| continue; |
| if ( key.equals(COLUMN_ACTIVE)) { |
| if (! Boolean.valueOf((String) row.get(COLUMN_ACTIVE))) { |
| log.debug("skipping inactive entry {} for switch {}", |
| entryName, switchName); |
| entries.get(switchName).put(entryName, null); // mark this an inactive |
| return; |
| } |
| } else if ( key.equals(COLUMN_ACTIONS)){ |
| StaticFlowEntries.parseActionString(flowMod, (String) row.get(COLUMN_ACTIONS), log); |
| } else if ( key.equals(COLUMN_COOKIE)) { |
| flowMod.setCookie( |
| StaticFlowEntries.computeEntryCookie(flowMod, |
| Integer.valueOf((String) row.get(COLUMN_COOKIE)), |
| entryName) |
| ); |
| } else if ( key.equals(COLUMN_PRIORITY)) { |
| flowMod.setPriority(U16.t(Integer.valueOf((String) row.get(COLUMN_PRIORITY)))); |
| } else { // the rest of the keys are for OFMatch().fromString() |
| if (matchString.length() > 0) |
| matchString.append(","); |
| matchString.append(key + "=" + row.get(key).toString()); |
| } |
| } |
| } catch (ClassCastException e) { |
| if (entryName != null && switchName != null) |
| log.debug( |
| "skipping entry {} on switch {} with bad data : " |
| + e.getMessage(), entryName, switchName); |
| else |
| log.debug("skipping entry with bad data: {} :: {} ", |
| e.getMessage(), e.getStackTrace()); |
| } |
| |
| OFMatch ofMatch = new OFMatch(); |
| String match = matchString.toString(); |
| try { |
| ofMatch.fromString(match); |
| } catch (IllegalArgumentException e) { |
| log.debug( |
| "ignoring flow entry {} on switch {} with illegal OFMatch() key: " |
| + match, entryName, switchName); |
| return; |
| } |
| flowMod.setMatch(ofMatch); |
| |
| entries.get(switchName).put(entryName, flowMod); |
| } |
| |
| @Override |
| public void addedSwitch(IOFSwitch sw) { |
| log.debug("addedSwitch {}; processing its static entries", sw); |
| sendEntriesToSwitch(sw); |
| } |
| |
| @Override |
| public void removedSwitch(IOFSwitch sw) { |
| log.debug("removedSwitch {}", sw); |
| // do NOT delete from our internal state; we're tracking the rules, |
| // not the switches |
| } |
| |
| @Override |
| public void switchPortChanged(Long switchId) { |
| // no-op |
| } |
| |
| /** |
| * This handles both rowInsert() and rowUpdate() |
| */ |
| |
| @Override |
| public void rowsModified(String tableName, Set<Object> rowKeys) { |
| log.debug("Modifying Table {}", tableName); |
| |
| HashMap<String, Map<String, OFFlowMod>> entriesToAdd = |
| new HashMap<String, Map<String, OFFlowMod>>(); |
| // build up list of what was added |
| for(Object key: rowKeys) { |
| IResultSet resultSet = storageSource.getRow(tableName, key); |
| for (Iterator<IResultSet> it = resultSet.iterator(); it.hasNext();) { |
| Map<String, Object> row = it.next().getRow(); |
| parseRow(row, entriesToAdd); |
| } |
| } |
| // batch updates by switch and blast them out |
| for (String dpid : entriesToAdd.keySet()) { |
| if (!entriesFromStorage.containsKey(dpid)) |
| entriesFromStorage.put(dpid, new HashMap<String, OFFlowMod>()); |
| List<OFMessage> outQueue = new ArrayList<OFMessage>(); |
| 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) { |
| entriesFromStorage.get(dpid).put(entry, newFlowMod); |
| outQueue.add(newFlowMod); |
| entry2dpid.put(entry, dpid); |
| } else { |
| entriesFromStorage.get(dpid).remove(entry); |
| entry2dpid.remove(entry); |
| } |
| } |
| |
| writeOFMessagesToSwitch(HexString.toLong(dpid), outQueue); |
| } |
| } |
| |
| @Override |
| public void rowsDeleted(String tableName, Set<Object> rowKeys) { |
| if (log.isDebugEnabled()) { |
| log.debug("deleting from Table {}", tableName); |
| } |
| |
| for(Object obj : rowKeys) { |
| if (!(obj instanceof String)) { |
| log.debug("tried to delete non-string key {}; ignoring", obj); |
| continue; |
| } |
| deleteStaticFlowEntry((String) obj); |
| } |
| } |
| |
| @LogMessageDoc(level="ERROR", |
| message="inconsistent internal state: no switch has rule {rule}", |
| explanation="Inconsistent internat state discovered while " + |
| "deleting a static flow rule", |
| recommendation=LogMessageDoc.REPORT_CONTROLLER_BUG) |
| 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.debug("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. |
| */ |
| @LogMessageDoc(level="ERROR", |
| message="Tried to write to switch {switch} but got {error}", |
| explanation="An I/O error occured while trying to write a " + |
| "static flow to a switch", |
| recommendation=LogMessageDoc.CHECK_SWITCH) |
| private void writeOFMessagesToSwitch(long dpid, List<OFMessage> messages) { |
| IOFSwitch ofswitch = floodlightProvider.getSwitches().get(dpid); |
| if (ofswitch != null) { // is the switch connected |
| try { |
| 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 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 |
| */ |
| @LogMessageDoc(level="ERROR", |
| message="Tried to write OFFlowMod to {switch} but got {error}", |
| explanation="An I/O error occured while trying to write a " + |
| "static flow to a switch", |
| recommendation=LogMessageDoc.CHECK_SWITCH) |
| 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() { |
| return StaticFlowName; |
| } |
| |
| @Override |
| @LogMessageDoc(level="ERROR", |
| message="Got a FlowRemove message for a infinite " + |
| "timeout flow: {flow} from switch {switch}", |
| explanation="Flows with infinite timeouts should not expire. " + |
| "The switch has expired the flow anyway.", |
| recommendation=LogMessageDoc.REPORT_SWITCH_BUG) |
| public Command receive(IOFSwitch sw, OFMessage msg, FloodlightContext cntx) { |
| switch (msg.getType()) { |
| case FLOW_REMOVED: |
| break; |
| default: |
| return Command.CONTINUE; |
| } |
| OFFlowRemoved flowRemoved = (OFFlowRemoved) msg; |
| long cookie = flowRemoved.getCookie(); |
| /** |
| * This is just to sanity check our assumption that static flows |
| * never expire. |
| */ |
| if( AppCookie.extractApp(cookie) == STATIC_FLOW_APP_ID) { |
| if (flowRemoved.getReason() != |
| OFFlowRemoved.OFFlowRemovedReason.OFPRR_DELETE) |
| log.error("Got a FlowRemove message for a infinite " + |
| "timeout flow: {} from switch {}", msg, sw); |
| return Command.STOP; // only for us |
| } else |
| return Command.CONTINUE; |
| } |
| |
| @Override |
| public boolean isCallbackOrderingPrereq(OFType type, String name) { |
| return false; // no dependency for non-packet in |
| } |
| |
| @Override |
| public boolean isCallbackOrderingPostreq(OFType type, String name) { |
| return false; // no dependency for non-packet in |
| } |
| |
| // IFloodlightModule |
| |
| @Override |
| public Collection<Class<? extends IFloodlightService>> getModuleServices() { |
| Collection<Class<? extends IFloodlightService>> l = |
| new ArrayList<Class<? extends IFloodlightService>>(); |
| l.add(IStaticFlowEntryPusherService.class); |
| return l; |
| } |
| |
| @Override |
| public Map<Class<? extends IFloodlightService>, IFloodlightService> getServiceImpls() { |
| Map<Class<? extends IFloodlightService>, |
| IFloodlightService> m = |
| new HashMap<Class<? extends IFloodlightService>, |
| IFloodlightService>(); |
| m.put(IStaticFlowEntryPusherService.class, this); |
| return m; |
| } |
| |
| @Override |
| public Collection<Class<? extends IFloodlightService>> getModuleDependencies() { |
| Collection<Class<? extends IFloodlightService>> l = |
| new ArrayList<Class<? extends IFloodlightService>>(); |
| l.add(IFloodlightProviderService.class); |
| l.add(IStorageSourceService.class); |
| l.add(IRestApiService.class); |
| return l; |
| } |
| |
| @Override |
| public void init(FloodlightModuleContext context) |
| throws FloodlightModuleException { |
| floodlightProvider = |
| context.getServiceImpl(IFloodlightProviderService.class); |
| storageSource = |
| context.getServiceImpl(IStorageSourceService.class); |
| restApi = |
| context.getServiceImpl(IRestApiService.class); |
| } |
| |
| @Override |
| public void startUp(FloodlightModuleContext context) { |
| floodlightProvider.addOFMessageListener(OFType.FLOW_REMOVED, this); |
| floodlightProvider.addOFSwitchListener(this); |
| floodlightProvider.addHAListener(this); |
| |
| // assumes no switches connected at startup() |
| storageSource.createTable(TABLE_NAME, null); |
| storageSource.setTablePrimaryKeyName(TABLE_NAME, COLUMN_NAME); |
| storageSource.addListener(TABLE_NAME, this); |
| entriesFromStorage = readEntriesFromStorage(); |
| 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); |
| } |
| } |
| |
| @Override |
| public void deleteFlowsForSwitch(long dpid) { |
| String sDpid = HexString.toHexString(dpid); |
| |
| for (Entry<String, String> e : entry2dpid.entrySet()) { |
| if (e.getValue().equals(sDpid)) |
| deleteFlow(e.getKey()); |
| } |
| } |
| |
| @Override |
| public Map<String, Map<String, OFFlowMod>> getFlows() { |
| return entriesFromStorage; |
| } |
| |
| @Override |
| public Map<String, OFFlowMod> getFlows(String dpid) { |
| return entriesFromStorage.get(dpid); |
| } |
| |
| |
| // IHAListener |
| |
| @Override |
| public void roleChanged(Role oldRole, Role newRole) { |
| switch(newRole) { |
| case MASTER: |
| if (oldRole == Role.SLAVE) { |
| log.debug("Re-reading static flows from storage due " + |
| "to HA change from SLAVE->MASTER"); |
| entriesFromStorage = readEntriesFromStorage(); |
| entry2dpid = computeEntry2DpidMap(entriesFromStorage); |
| } |
| break; |
| case SLAVE: |
| log.debug("Clearing in-memory flows due to " + |
| "HA change to SLAVE"); |
| entry2dpid.clear(); |
| entriesFromStorage.clear(); |
| break; |
| default: |
| break; |
| } |
| } |
| |
| @Override |
| public void controllerNodeIPsChanged( |
| Map<String, String> curControllerNodeIPs, |
| Map<String, String> addedControllerNodeIPs, |
| Map<String, String> removedControllerNodeIPs) { |
| // ignore |
| } |
| |
| } |