/**
* Copyright 2013, Big Switch Networks, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
**/
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.HashMap;
import java.util.Iterator;
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.HAListenerTypeMarker;
import net.floodlightcontroller.core.IFloodlightProviderService;
import net.floodlightcontroller.core.IHAListener;
import net.floodlightcontroller.core.IOFMessageListener;
import net.floodlightcontroller.core.IOFSwitch;
import net.floodlightcontroller.core.IOFSwitchListener;
import net.floodlightcontroller.core.ImmutablePort;
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.storage.IResultSet;
import net.floodlightcontroller.storage.IStorageSourceListener;
import net.floodlightcontroller.storage.IStorageSourceService;
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.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 {
protected static Logger log = LoggerFactory.getLogger(StaticFlowEntryPusher.class);
public static final String StaticFlowName = "staticflowentry";
public static final int STATIC_FLOW_APP_ID = 10;
static {
AppCookie.registerApp(STATIC_FLOW_APP_ID, StaticFlowName);
}
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;
private IHAListener haListener;
// 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;
// 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(long switchId) {
IOFSwitch sw = floodlightProvider.getSwitch(switchId);
if (sw == null)
return;
String stringId = sw.getStringId();
if ((entriesFromStorage != null) && (entriesFromStorage.containsKey(stringId))) {
Map<String, OFFlowMod> entries = entriesFromStorage.get(stringId);
List<String> sortedList = new ArrayList<String>(entries.keySet());
// weird that Collections.sort() returns void
Collections.sort( sortedList, new FlowModSorter(stringId));
for (String entryName : sortedList) {
OFFlowMod flowMod = entries.get(entryName);
if (flowMod != null) {
if (log.isDebugEnabled()) {
log.debug("Pushing static entry {} for {}", stringId, 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 ConcurrentHashMap<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();
OFFlowMod flowMod = (OFFlowMod) floodlightProvider.getOFMessageFactory()
.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.warn(
"Skipping entry {} on switch {} with bad data : "
+ e.getMessage(), entryName, switchName);
} else {
log.warn("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 switchAdded(long switchId) {
log.debug("Switch {} connected; processing its static entries",
HexString.toHexString(switchId));
sendEntriesToSwitch(switchId);
}
@Override
public void switchRemoved(long switchId) {
// do NOT delete from our internal state; we're tracking the rules,
// not the switches
}
@Override
public void switchActivated(long switchId) {
// no-op
}
@Override
public void switchChanged(long switchId) {
// no-op
}
@Override
public void switchPortChanged(long switchId,
ImmutablePort port,
IOFSwitch.PortChangeType type) {
// no-op
}
@Override
public void rowsModified(String tableName, Set<Object> rowKeys) {
// This handles both rowInsert() and rowUpdate()
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);
Iterator<IResultSet> it = resultSet.iterator();
while (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);
OFFlowMod oldFlowMod = null;
String dpidOldFlowMod = entry2dpid.get(entry);
if (dpidOldFlowMod != null) {
oldFlowMod = entriesFromStorage.get(dpidOldFlowMod).remove(entry);
}
if (oldFlowMod != null && newFlowMod != null) {
// set the new flow mod to modify a pre-existing rule if these fields match
if(oldFlowMod.getMatch().equals(newFlowMod.getMatch())
&& oldFlowMod.getCookie() == newFlowMod.getCookie()
&& oldFlowMod.getPriority() == newFlowMod.getPriority()){
newFlowMod.setCommand(OFFlowMod.OFPFC_MODIFY_STRICT);
// if they don't match delete the old flow
} else{
oldFlowMod.setCommand(OFFlowMod.OFPFC_DELETE_STRICT);
if (dpidOldFlowMod.equals(dpid)) {
outQueue.add(oldFlowMod);
} else {
writeOFMessageToSwitch(HexString.toLong(dpidOldFlowMod), oldFlowMod);
}
}
}
// write the new flow
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 void deleteStaticFlowEntry(String entryName) {
String dpid = entry2dpid.remove(entryName);
if (dpid == null) {
// assume state has been cleared by deleteFlowsForSwitch() or
// deleteAllFlows()
return;
}
if (log.isDebugEnabled()) {
log.debug("Sending delete flow mod for flow {} for switch {}", entryName, dpid);
}
// 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;
}
writeFlowModToSwitch(HexString.toLong(dpid), flowMod);
return;
}
/**
* 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.getSwitch(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 a single OFMessage to a switch
* @param dpid The datapath ID of the switch to write to
* @param message The OFMessage 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 writeOFMessageToSwitch(long dpid, OFMessage message) {
IOFSwitch ofswitch = floodlightProvider.getSwitch(dpid);
if (ofswitch != null) { // is the switch connected
try {
if (log.isDebugEnabled()) {
log.debug("Sending 1 new entries to {}", HexString.toHexString(dpid));
}
ofswitch.write(message, 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) {
IOFSwitch ofSwitch = floodlightProvider.getSwitch(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;
}
/**
* Handles a flow removed message from a switch. If the flow was removed
* and we did not explicitly delete it we re-install it. If we explicitly
* removed the flow we stop the processing of the flow removed message.
* @param sw The switch that sent the flow removed message.
* @param msg The flow removed message.
* @param cntx The associated context.
* @return Whether to continue processing this message.
*/
public Command handleFlowRemoved(IOFSwitch sw, OFFlowRemoved msg, FloodlightContext cntx) {
long cookie = msg.getCookie();
/**
* This is just to sanity check our assumption that static flows
* never expire.
*/
if (AppCookie.extractApp(cookie) == STATIC_FLOW_APP_ID) {
if (msg.getReason() != OFFlowRemoved.OFFlowRemovedReason.OFPRR_DELETE)
log.error("Got a FlowRemove message for a infinite " +
"timeout flow: {} from switch {}", msg, sw);
// Stop the processing chain since we sent the delete.
return Command.STOP;
}
return Command.CONTINUE;
}
@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:
return handleFlowRemoved(sw, (OFFlowRemoved) msg, cntx);
default:
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);
haListener = new HAListenerDelegate();
}
@Override
public void startUp(FloodlightModuleContext context) {
floodlightProvider.addOFMessageListener(OFType.FLOW_REMOVED, this);
floodlightProvider.addOFSwitchListener(this);
floodlightProvider.addHAListener(this.haListener);
// 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);
storageSource.insertRowAsync(TABLE_NAME, fmMap);
}
@Override
public void deleteFlow(String name) {
storageSource.deleteRowAsync(TABLE_NAME, name);
}
@Override
public void deleteAllFlows() {
for (String entry : entry2dpid.keySet()) {
deleteFlow(entry);
}
/*
FIXME: Since the OF spec 1.0 is not clear on how
to match on cookies. Once all switches come to a
common implementation we can possibly re-enable this
fix.
// Send a delete for each switch
Set<String> swSet = new HashSet<String>();
for (String dpid : entry2dpid.values()) {
// Avoid sending duplicate deletes
if (!swSet.contains(dpid)) {
swSet.add(dpid);
sendDeleteByCookie(HexString.toLong(dpid));
}
}
// Clear our map
entry2dpid.clear();
// Clear our book keeping map
for (Map<String, OFFlowMod> eMap : entriesFromStorage.values()) {
eMap.clear();
}
// Reset our DB
storageSource.deleteMatchingRowsAsync(TABLE_NAME, null);
*/
}
@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());
}
/*
FIXME: Since the OF spec 1.0 is not clear on how
to match on cookies. Once all switches come to a
common implementation we can possibly re-enable this
fix.
//sendDeleteByCookie(dpid);
String sDpid = HexString.toHexString(dpid);
// Clear all internal flows for this switch
Map<String, OFFlowMod> sMap = entriesFromStorage.get(sDpid);
if (sMap != null) {
for (String entryName : sMap.keySet()) {
entry2dpid.remove(entryName);
// Delete from DB
deleteFlow(entryName);
}
sMap.clear();
} else {
log.warn("Map of storage entries for switch {} was null", sDpid);
}
*/
}
/**
* Deletes all flows installed by static flow pusher on a given switch.
* We send a delete flow mod with the static flow pusher app ID in the cookie.
* Since OF1.0 doesn't support masking based on the cookie we have to
* disable having flow specific cookies.
* @param dpid The DPID of the switch to clear all it's flows.
*/
/*
FIXME: Since the OF spec 1.0 is not clear on how
to match on cookies. Once all switches come to a
common implementation we can possibly re-enable this
fix.
private void sendDeleteByCookie(long dpid) {
if (log.isDebugEnabled())
log.debug("Deleting all static flows on switch {}", HexString.toHexString(dpid));
IOFSwitch sw = floodlightProvider.getSwitch(dpid);
if (sw == null) {
log.warn("Tried to delete static flows for non-existant switch {}",
HexString.toHexString(dpid));
return;
}
OFFlowMod fm = (OFFlowMod) floodlightProvider.getOFMessageFactory().
getMessage(OFType.FLOW_MOD);
OFMatch ofm = new OFMatch();
fm.setMatch(ofm);
fm.setCookie(AppCookie.makeCookie(StaticFlowEntryPusher.STATIC_FLOW_APP_ID, 0));
fm.setCommand(OFFlowMod.OFPFC_DELETE);
fm.setOutPort(OFPort.OFPP_NONE);
try {
sw.write(fm, null);
sw.flush();
} catch (IOException e1) {
log.error("Error deleting all flows for switch {}:\n {}",
HexString.toHexString(dpid), e1.getMessage());
return;
}
}
*/
@Override
public Map<String, Map<String, OFFlowMod>> getFlows() {
return entriesFromStorage;
}
@Override
public Map<String, OFFlowMod> getFlows(String dpid) {
return entriesFromStorage.get(dpid);
}
// IHAListener
private class HAListenerDelegate implements IHAListener {
@Override
public void transitionToMaster() {
log.debug("Re-reading static flows from storage due " +
"to HA change from SLAVE->MASTER");
entriesFromStorage = readEntriesFromStorage();
entry2dpid = computeEntry2DpidMap(entriesFromStorage);
}
@Override
public void controllerNodeIPsChanged(
Map<String, String> curControllerNodeIPs,
Map<String, String> addedControllerNodeIPs,
Map<String, String> removedControllerNodeIPs) {
// ignore
}
@Override
public String getName() {
return StaticFlowEntryPusher.this.getName();
}
@Override
public boolean isCallbackOrderingPrereq(HAListenerTypeMarker type,
String name) {
// TODO Auto-generated method stub
return false;
}
@Override
public boolean isCallbackOrderingPostreq(HAListenerTypeMarker type,
String name) {
// TODO Auto-generated method stub
return false;
}
}
}