net.floodlightcontroller.flowcache.FlowCacheDB.java Source code

Java tutorial

Introduction

Here is the source code for net.floodlightcontroller.flowcache.FlowCacheDB.java

Source

package net.floodlightcontroller.flowcache;

/*
* Copyright (c) 2013, California Institute of Technology
* ALL RIGHTS RESERVED.
* Based on Government Sponsored Research DE-SC0007346
* Author Michael Bredel <michael.bredel@cern.ch>
* 
* 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
* 
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
* WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
* 
* Neither the name of the California Institute of Technology
* (Caltech) nor the names of its contributors may be used to endorse
* or promote products derived from this software without specific prior
* written permission.
*/

import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import org.bouncycastle.util.Arrays;
import org.openflow.util.HexString;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * A very generic flow cache database. Hence, it is also a very slow
 * database with respect to queries.
 * 
 * @author Michael Bredel <michael.bredel@cern.ch>
 */
public class FlowCacheDB implements IFlowCacheDB {
    /** The logger. */
    protected static Logger log = LoggerFactory.getLogger(FlowCacheDB.class);

    /** The name of the application that uses this flow cache database. */
    protected String appName;
    /** SwitchId -> MatchHash -> FlowCacheObj. */
    protected ConcurrentHashMap<Long, ConcurrentHashMap<Integer, FlowCacheObj>> flowCacheMatchHashMap;

    /**
     * Default constructor instantiates this flow cache 
     * database for a given application. 
     * 
     * @param appName
     */
    public FlowCacheDB(String appName) {
        this.appName = appName;
        this.flowCacheMatchHashMap = new ConcurrentHashMap<Long, ConcurrentHashMap<Integer, FlowCacheObj>>();

        if (log.isDebugEnabled()) {
            log.debug("New flow cache database created for {}", appName);
        }
    }

    @Override
    public synchronized boolean storeEntry(long switchId, FlowCacheObj fco, boolean overrideEntries) {
        /* Get the hash of the match object taking its wildcards into account. */
        int matchHash = fco.getId();

        // Add new switch to flow the flowCacheMatchHashMap.
        if (!flowCacheMatchHashMap.containsKey(switchId)) {
            flowCacheMatchHashMap.put(switchId, new ConcurrentHashMap<Integer, FlowCacheObj>());
        }

        // Get switch's flow cache map.
        ConcurrentHashMap<Integer, FlowCacheObj> switchFlowCacheMap = flowCacheMatchHashMap.get(switchId);

        // Add flow match to flow cache: matchHash -> FlowCacheObj
        if (!switchFlowCacheMap.containsKey(matchHash)) {
            switchFlowCacheMap.put(matchHash, fco);
            return true;
        } else {
            // To be sure: Check the identity of flow cache objects.
            if (!switchFlowCacheMap.get(matchHash).equals(fco)) {
                if (log.isDebugEnabled()) {
                    log.debug(
                            "storeEntry: Flow cache objects for switch {} are not identical, although there hashes are!",
                            HexString.toHexString(switchId));
                }
                if (overrideEntries) {
                    switchFlowCacheMap.put(matchHash, fco);
                    if (log.isDebugEnabled()) {
                        log.debug("storeEntry: Overriding exsting entry.");
                    }
                    return true;
                } else {
                    return false;
                }
            } else {
                return true;
            }
        }
    }

    @Override
    public synchronized FlowCacheObj getEntry(long switchId, FlowCacheObj fco) {
        return this.getEntry(switchId, fco.getId());
    }

    /**
    * Simple getter for flow cache object entries
    * 
    * @param switchId The switchID that is associated with this flow cache object.
    * @param matchHash The flow cache object hash of the flow to get.
    * @return <b>FlowCacheObj</b> The flow cache entry.
    */
    protected synchronized FlowCacheObj getEntry(long switchId, int matchHash) {
        /* The removed flow cache object. */
        FlowCacheObj getFco = null;

        // Get switch's flow cache map.
        ConcurrentHashMap<Integer, FlowCacheObj> switchFlowCacheMap = flowCacheMatchHashMap.get(switchId);
        if (switchFlowCacheMap == null) {
            if (log.isDebugEnabled()) {
                log.debug("getEntry: No flow cache map found for switch {}", HexString.toHexString(switchId));
            }
            // Return null.
            return getFco;
        }

        // Remove flow cache object. TODO: improve performance by object reuse.
        if (switchFlowCacheMap.containsKey(matchHash)) {
            getFco = switchFlowCacheMap.get(matchHash);
        } else {
            if (log.isDebugEnabled()) {
                log.debug("getEntry: No flow cache object found for switch {}",
                        HexString.toHexString(switchId) + ": hash " + matchHash);
            }
            // Return null.
            return getFco;
        }

        return getFco;
    }

    @Override
    public synchronized FlowCacheObj removeEntry(long switchId, FlowCacheObj fco) {
        /* Get the hash of the match object taking its wildcards into account. */
        int matchHash = fco.getId();
        /* The removed flow cache object. */
        FlowCacheObj removedFco = null;

        // Get switch's flow cache map.
        ConcurrentHashMap<Integer, FlowCacheObj> switchFlowCacheMap = flowCacheMatchHashMap.get(switchId);
        if (switchFlowCacheMap == null) {
            if (log.isWarnEnabled()) {
                log.warn(
                        "removeEntry: Could not remove flow cache object {} for switch {} from standard match hash db. Object not found. ",
                        fco, HexString.toHexString(switchId));
            }
            // Return null.
            return removedFco;
        }

        // Remove flow cache object. TODO: improve performance by object reuse.
        if (switchFlowCacheMap.containsKey(matchHash)) {
            removedFco = switchFlowCacheMap.remove(matchHash);
        } else {
            if (log.isWarnEnabled()) {
                log.warn(
                        "removeEntry: Could not remove flow cache object {} for switch {} from standard match hash db. Object not found. ",
                        fco, HexString.toHexString(switchId));
            }
            // Return null.
            return removedFco;
        }

        // Check for empty maps and remove them. TODO: improve performance by object reuse.
        if (switchFlowCacheMap.isEmpty()) {
            flowCacheMatchHashMap.remove(switchId);
        }
        return removedFco;
    }

    @Override
    public boolean hasEntry(long switchId, FlowCacheObj fco) {
        return this.hasEntry(switchId, fco.getId());
    }

    /**
    * Checks if the database has a specific flow cache object.
    * 
    * @param switchId The switchID that is associated with this flow cache object.
    * @param matchHash The flow cache object hash of the object to be checked.
    * @return <b>boolean</b> True iff the object is in the flow cache database.
    */
    protected boolean hasEntry(long switchId, int matchHash) {
        if (!flowCacheMatchHashMap.containsKey(switchId))
            return false;

        // Set of match hashes of flows installed on the switch.
        Set<Integer> hashSet = flowCacheMatchHashMap.get(switchId).keySet();

        if (hashSet != null && hashSet.contains(matchHash)) {
            return true;
        } else {
            return false;
        }
    }

    @Override
    public boolean isEmpty() {
        return flowCacheMatchHashMap.isEmpty();
    }

    @Override
    public Map<Long, Set<FlowCacheObj>> getAllEntries() {
        /* New HashMap that contains all flow cache objects. */
        HashMap<Long, Set<FlowCacheObj>> resultMap = new HashMap<Long, Set<FlowCacheObj>>();

        for (Long switchId : this.flowCacheMatchHashMap.keySet()) {
            if (!resultMap.containsKey(switchId)) {
                resultMap.put(switchId, new HashSet<FlowCacheObj>());
            }
            resultMap.get(switchId).addAll(this.flowCacheMatchHashMap.get(switchId).values());
        }

        return resultMap;
    }

    @Override
    public synchronized Map<Long, Set<FlowCacheObj>> queryDB(long switchId, FlowCacheQuery query) {
        /* New HashMap that contains all flow cache objects that match the query: switchId -> SetOf FlowCacheObj. */
        HashMap<Long, Set<FlowCacheObj>> resultMap = new HashMap<Long, Set<FlowCacheObj>>();

        if (!this.flowCacheMatchHashMap.containsKey(switchId)) {
            if (log.isDebugEnabled()) {
                log.debug("Switch ID {} not found.", HexString.toHexString(switchId));
            }
            return null;
        }

        // Add all flow cache objects of the switch to the resulting map.
        if (!resultMap.containsKey(switchId)) {
            resultMap.put(switchId, new HashSet<FlowCacheObj>());
        }
        resultMap.get(switchId).addAll(this.flowCacheMatchHashMap.get(switchId).values());

        // Remove unwanted entries - according to the flow cache query.
        for (Iterator<FlowCacheObj> iter = resultMap.get(switchId).iterator(); iter.hasNext();) {
            // Get the flow cache object.
            FlowCacheObj fco = iter.next();

            // Cookie
            if (query.cookie != 0 && query.cookie != fco.getCookie()) {
                iter.remove();
                continue;
            }
            // priority
            if (query.priority != 0 && query.priority != fco.getPriority()) {
                iter.remove();
                continue;
            }
            // OutPort
            if (query.outPort != 0 && (fco.getOutPorts() == null || !fco.getOutPorts().contains(query.outPort))) {
                iter.remove();
                continue;
            }
            // InPort
            if (query.inPort != 0 && (fco.getMatch() == null || query.inPort != fco.getMatch().getInputPort())) {
                iter.remove();
                continue;
            }
            // DL_DST
            if (query.dataLayerDestination != null
                    && !Arrays.areEqual(query.dataLayerDestination, fco.getMatch().getDataLayerDestination())) {
                iter.remove();
                continue;
            }
            // DL_SRC
            if (query.dataLayerSource != null
                    && !Arrays.areEqual(query.dataLayerSource, fco.getMatch().getDataLayerSource())) {
                iter.remove();
                continue;
            }
            // DL_VLAN
            if (query.dataLayerVirtualLan != 0
                    && query.dataLayerVirtualLan != fco.getMatch().getDataLayerVirtualLan()) {
                iter.remove();
                continue;
            }
            // DL_VLAN_PCP
            if (query.dataLayerVirtualLanPriorityCodePoint != 0 && query.dataLayerVirtualLanPriorityCodePoint != fco
                    .getMatch().getDataLayerVirtualLanPriorityCodePoint()) {
                iter.remove();
                continue;
            }
            // DL_TYPE
            if (query.dataLayerType != 0 && query.dataLayerType != fco.getMatch().getDataLayerType()) {
                iter.remove();
                continue;
            }
            // NW_PROTO
            if (query.networkProtocol != 0 && query.networkProtocol != fco.getMatch().getNetworkProtocol()) {
                iter.remove();
                continue;
            }
            // NW_TOS
            if (query.networkTypeOfService != 0
                    && query.networkTypeOfService != fco.getMatch().getNetworkTypeOfService()) {
                iter.remove();
                continue;
            }
            // NW_SRC
            if (query.networkSource != 0 && query.networkSource != fco.getMatch().getNetworkSource()) {
                iter.remove();
                continue;
            }
            // NW_DST
            if (query.networkDestination != 0
                    && query.networkDestination != fco.getMatch().getNetworkDestination()) {
                iter.remove();
                continue;
            }
            // TP_SRC
            if (query.transportSource != 0 && query.transportSource != fco.getMatch().getTransportSource()) {
                iter.remove();
                continue;
            }
            // TP_DST
            if (query.transportDestination != 0
                    && query.transportDestination != fco.getMatch().getTransportDestination()) {
                iter.remove();
                continue;
            }

            // Path ID
            if (query.pathId > 0 && query.pathId != fco.getPathId()) {
                iter.remove();
                continue;
            }
        }

        //      // TESTING AND DEBUGGING
        //      System.out.println("--------------------------");
        //      for (long swId : this.flowCacheMatchHashMap.keySet()) {
        //         System.out.println("SwitchID: " + HexString.toHexString(swId));
        //         for (int hash : this.flowCacheMatchHashMap.get(swId).keySet()) {
        //            System.out.println("   " + hash + ": " + this.flowCacheMatchHashMap.get(swId).get(hash));
        //         }
        //      }

        // Remove empty SetOf FlowCacheObj and corresponding switch IDs.
        for (long swId : resultMap.keySet()) {
            if (resultMap.get(swId) == null || resultMap.get(swId).isEmpty()) {
                resultMap.remove(swId);
            }
        }

        // Return new result map, or null if result map is empty.
        return (!resultMap.isEmpty()) ? resultMap : null;
    }

    @Override
    public void clear() {
        for (long switchId : flowCacheMatchHashMap.keySet()) {
            for (int hash : flowCacheMatchHashMap.get(switchId).keySet()) {
                flowCacheMatchHashMap.get(switchId).remove(hash);
            }
            if (flowCacheMatchHashMap.get(switchId).isEmpty()) {
                flowCacheMatchHashMap.remove(switchId);
            }
        }
    }

    @Override
    public String toString() {
        /* The string builder. */
        StringBuilder sb = new StringBuilder();

        sb.append("FlowCacheDB [");
        sb.append("appName=" + this.appName);
        sb.append("]");

        return sb.toString();
    }

}