Package net.floodlightcontroller.staticflowentry

Source Code of net.floodlightcontroller.staticflowentry.StaticFlowEntryPusher$HAListenerDelegate

/**
*    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;
        }
    }
}
TOP

Related Classes of net.floodlightcontroller.staticflowentry.StaticFlowEntryPusher$HAListenerDelegate

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.