Skip to content
Snippets Groups Projects
Commit 078dccdb authored by Saurav Das's avatar Saurav Das
Browse files

Some changes to counter store

     removed dead code
     fixed bug on handling display of ethtypes with MSB set
     replaced counterkey string processing on every packet-in with tuple keys
     removed collection of traffic type per switch-port
parent 9f24d71e
No related branches found
No related tags found
No related merge requests found
Showing with 280 additions and 766 deletions
/**
* Copyright 2011, Big Switch Networks, Inc.
* Originally created by David Erickson, Stanford University
*
* 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.counter;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import net.floodlightcontroller.counter.CounterValue.CounterType;
/**
* This module needs to be updated with CounterValue.
*
* This is a crumby attempt at a highly concurrent implementation of the Counter interface.
*
* (Help! Help! Someone please re-write me! This will almost certainly break at high loads.)
*
* The gist is that this class, ConcurrentCounter, keeps an internal highly transient buffer that is occasionally flushed
* in to a set of CountBuffers (circular buffers) which store a longer term historical view of the count values at different
* moments in time.
*
* This Counter implementation may be a bit over-engineered... The goal here was to present an implementation that is very
* predictable with respect to memory and CPU time and, at the same time, present a very fast increment() method. The reasoning
* here is that this will be a go-to class when it comes to debugging, particularly in high-load situations where logging
* may introduce so much variability to the system that it foils the results.
*
* @author kyle
*
*/
public class ConcurrentCounter implements ICounter {
protected static final Map<DateSpan, Integer> MAX_HISTORY = new HashMap<DateSpan, Integer>();
static {
MAX_HISTORY.put(DateSpan.REALTIME, new Integer(1));
MAX_HISTORY.put(DateSpan.SECONDS, new Integer(120));
MAX_HISTORY.put(DateSpan.MINUTES, new Integer(60));
MAX_HISTORY.put(DateSpan.HOURS, new Integer(48));
MAX_HISTORY.put(DateSpan.DAYS, new Integer(60));
MAX_HISTORY.put(DateSpan.WEEKS, new Integer(2));
}
protected static Set<ConcurrentCounter> liveCounters;
static {
liveCounters = Collections.newSetFromMap(new ConcurrentHashMap<ConcurrentCounter, Boolean>()); //nifty way to get concurrent hash set
//Set a background thread to flush any liveCounters every 100 milliseconds
Executors.newScheduledThreadPool(1).scheduleAtFixedRate(new Runnable() {
public void run() {
for(ConcurrentCounter c : liveCounters) {
c.flush();
}
}}, 100, 100, TimeUnit.MILLISECONDS);
}
/**
* Very simple data structure to store off a single count entry at a single point in time
* @author kyle
*
*/
protected static final class CountAtom {
protected Date date;
protected Long delta;
protected CountAtom(Date date, Long delta) {
this.date = date;
this.delta = delta;
}
public String toString() {
return "[" + this.date + ": " + this.delta + "]";
}
}
protected Queue<CountAtom> unprocessedCountBuffer;
protected Map<DateSpan, CountBuffer> counts;
protected Date startDate;
/**
* Factory method to create a new counter instance. (Design note -
* use a factory pattern here as it may be necessary to hook in other
* registrations around counter objects as they are created.)
*
* @param startDate
* @return
*/
public static ICounter createCounter(Date startDate) {
ConcurrentCounter cc = new ConcurrentCounter(startDate);
ConcurrentCounter.liveCounters.add(cc);
return cc;
}
/**
* Protected constructor - use createCounter factory method instead
* @param startDate
*/
protected ConcurrentCounter(Date startDate) {
init(startDate);
}
protected void init(Date startDate) {
this.startDate = startDate;
this.unprocessedCountBuffer = new ConcurrentLinkedQueue<CountAtom>();
this.counts = new HashMap<DateSpan, CountBuffer>();
for(DateSpan ds : DateSpan.values()) {
CountBuffer cb = new CountBuffer(startDate, ds, MAX_HISTORY.get(ds));
counts.put(ds, cb);
}
}
/**
* This is the key method that has to be both fast and very thread-safe.
*/
@Override
public void increment() {
this.increment(new Date(), (long)1);
}
@Override
public void increment(Date d, long delta) {
this.unprocessedCountBuffer.add(new CountAtom(d, delta));
}
@Override
public void setCounter(Date d, CounterValue value) {
// To be done later
}
/**
* Reset the value.
*/
@Override
public void reset(Date startDate) {
init(startDate);
}
/**
* Flushes values out of the internal buffer and in to structures
* that can be fetched with a call to snapshot()
*/
public synchronized void flush() {
for(CountAtom c = this.unprocessedCountBuffer.poll(); c != null; c = this.unprocessedCountBuffer.poll()) {
for(DateSpan ds : DateSpan.values()) {
CountBuffer cb = counts.get(ds);
cb.increment(c.date, c.delta);
}
}
}
@Override
public CounterValue getCounterValue() {
// To be done later
//CountSeries cs = counts.get(DateSpan.REALTIME).snapshot();
//return cs.getSeries()[0];
return new CounterValue(CounterType.LONG);
}
@Override
public Date getCounterDate() {
// To be done later
//CountSeries cs = counts.get(DateSpan.REALTIME).snapshot();
//return cs.getSeries()[0];
return new Date();
}
@Override
/**
* This method returns a disconnected copy of the underlying CountSeries corresponding to dateSpan.
*/
public CountSeries snapshot(DateSpan dateSpan) {
flush();
CountSeries cs = counts.get(dateSpan).snapshot();
return cs;
}
}
/**
* Copyright 2011, Big Switch Networks, Inc.
* Originally created by David Erickson, Stanford University
*
* 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.counter;
import java.util.Date;
import net.floodlightcontroller.counter.ICounter.DateSpan;
/**
* Implements a circular buffer to store the last x time-based counter values. This is pretty crumby
* implementation, basically wrapping everything with synchronized blocks, in order to ensure that threads
* which will be updating the series don't result in a thread which is reading the series getting stuck with
* a start date which does not correspond to the count values in getSeries.
*
* This could probably use a re-think...
*
* @author kyle
*
*/
public class CountBuffer {
protected long[] counterValues;
protected Date startDate;
protected DateSpan dateSpan;
protected int currentIndex;
protected int seriesLength;
public CountBuffer(Date startDate, DateSpan dateSpan, int seriesLength) {
this.seriesLength = seriesLength;
this.counterValues = new long[seriesLength];
this.dateSpan = dateSpan;
this.startDate = startDate;
this.currentIndex = 0;
}
/**
* Increment the count associated with Date d, forgetting some of the older count values if necessary to ensure
* that the total span of time covered by this series corresponds to DateSpan * seriesLength (circular buffer).
*
* Note - fails silently if the Date falls prior to the start of the tracked count values.
*
* Note - this should be a reasonably fast method, though it will have to block if there is another thread reading the
* series at the same time.
*
* @param d
* @param delta
*/
public synchronized void increment(Date d, long delta) {
long dsMillis = CountSeries.dateSpanToMilliseconds(this.dateSpan);
Date endDate = new Date(startDate.getTime() + seriesLength * dsMillis - 1);
if(d.getTime() < startDate.getTime()) {
return; //silently fail rather than insert a count at a time older than the history buffer we're keeping
}
else if (d.getTime() >= startDate.getTime() && d.getTime() <= endDate.getTime()) {
int index = (int) (( d.getTime() - startDate.getTime() ) / dsMillis); // java rounds down on long/long
int modIndex = (index + currentIndex) % seriesLength;
long currentValue = counterValues[modIndex];
counterValues[modIndex] = currentValue + delta;
}
else if (d.getTime() > endDate.getTime()) {
//Initialize new buckets
int newBuckets = (int)((d.getTime() - endDate.getTime()) / dsMillis) + 1; // java rounds down on long/long
for(int i = 0; i < newBuckets; i++) {
int modIndex = (i + currentIndex) % seriesLength;
counterValues[modIndex] = 0;
}
//Update internal vars
this.startDate = new Date(startDate.getTime() + dsMillis * newBuckets);
this.currentIndex = (currentIndex + newBuckets) % this.seriesLength;
//Call again (date should be in the range this time)
this.increment(d, delta);
}
}
/**
* Relatively slow method, expected to be called primarily from UI rather than from in-packet-path.
*
* @return the count values associated with each time interval starting with startDate and demarc'ed by dateSpan
*/
public long[] getSeries() { //synchronized here should lock on 'this', implying that it shares the lock with increment
long[] ret = new long[this.seriesLength];
for(int i = 0; i < this.seriesLength; i++) {
int modIndex = (currentIndex + i) % this.seriesLength;
ret[i] = this.counterValues[modIndex];
}
return ret;
}
/**
* Returns an immutable count series that represents a snapshot of this
* series at a specific moment in time.
* @return
*/
public synchronized CountSeries snapshot() {
long[] cvs = new long[this.seriesLength];
for(int i = 0; i < this.seriesLength; i++) {
int modIndex = (this.currentIndex + i) % this.seriesLength;
cvs[i] = this.counterValues[modIndex];
}
return new CountSeries(this.startDate, this.dateSpan, cvs);
}
}
/**
* Copyright 2011, Big Switch Networks, Inc.
* Originally created by David Erickson, Stanford University
*
* 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.counter;
import java.util.Arrays;
import java.util.Date;
import net.floodlightcontroller.counter.ICounter.DateSpan;
/**
* Simple immutable class to store a series of historic counter values
*
* This could probably use a re-think...
*
* @author kyle
*
*/
public class CountSeries {
protected long[] counterValues;
protected Date startDate;
protected DateSpan dateSpan;
public CountSeries(Date startDate, DateSpan dateSpan, long[] counterValues) {
this.counterValues = counterValues.clone();
this.dateSpan = dateSpan;
this.startDate = startDate;
}
public long[] getSeries() { //synchronized here should lock on 'this', implying that it shares the lock with increment
return this.counterValues.clone();
}
/**
* Returns the startDate of this series. The first long in getSeries represents the sum of deltas from increment calls with dates
* that correspond to >= startDate and < startDate + DateSpan.
* @return
*/
public Date getStartDate() {//synchronized here should lock on 'this', implying that it shares the lock with increment
return this.startDate;
}
public String toString() {
String ret = "{start: " + this.startDate + ", span: " + this.dateSpan + ", series: " + Arrays.toString(getSeries()) + "}";
return ret;
}
/**
* Return a long that is the number of milliseconds in a ds (second/minute/hour/day/week). (Utility method.)
*
* @param ds
* @return
*/
public static final long dateSpanToMilliseconds(DateSpan ds) {
long delta = 1;
switch(ds) {
case WEEKS:
delta *= 7;
case DAYS:
delta *= 24;
case HOURS:
delta *= 60;
case MINUTES:
delta *= 60;
case SECONDS:
delta *= 1000;
default:
break;
}
return delta;
}
}
/** /**
* Copyright 2011, Big Switch Networks, Inc. * Copyright 2011, Big Switch Networks, Inc.
* Originally created by David Erickson, Stanford University * Originally created by David Erickson, Stanford University
* *
* Licensed under the Apache License, Version 2.0 (the "License"); you may * 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 * not use this file except in compliance with the License. You may obtain
* a copy of the License at * a copy of the License at
...@@ -28,46 +28,37 @@ import java.util.Date; ...@@ -28,46 +28,37 @@ import java.util.Date;
* *
*/ */
public interface ICounter { public interface ICounter {
/** /**
* Most commonly used method * Most commonly used method
*/ */
public void increment(); public void increment();
/** /**
* Used primarily for flushing thread local updates * Used primarily for flushing thread local updates
*/ */
public void increment(Date d, long delta); public void increment(Date d, long delta);
/** /**
* Counter value setter * Counter value setter
*/ */
public void setCounter(Date d, CounterValue value); public void setCounter(Date d, CounterValue value);
/** /**
* Return the most current value * Return the most current value
*/ */
public Date getCounterDate(); public Date getCounterDate();
/** /**
* Return the most current value * Return the most current value
*/ */
public CounterValue getCounterValue(); public CounterValue getCounterValue();
/** /**
* Reset the value * Reset the value
*/ */
public void reset(Date d); public void reset(Date d);
/**
* Returns a CountSeries that is a snapshot of the counter's values for the given dateSpan. (Further changes
* to this counter won't be reflected in the CountSeries that comes back.)
*
* @param dateSpan
* @return
*/
public CountSeries snapshot(DateSpan dateSpan);
public static enum DateSpan { public static enum DateSpan {
REALTIME, REALTIME,
......
...@@ -35,27 +35,25 @@ public interface ICounterStoreService extends IFloodlightService { ...@@ -35,27 +35,25 @@ public interface ICounterStoreService extends IFloodlightService {
public final static String BROADCAST = "broadcast"; public final static String BROADCAST = "broadcast";
public final static String MULTICAST = "multicast"; public final static String MULTICAST = "multicast";
public final static String UNICAST = "unicast"; public final static String UNICAST = "unicast";
/** L2 EtherType subCategories */ /** L2 EtherType subCategories */
public final static String L3ET_IPV4 = "L3_IPv4"; public final static String L3ET_IPV4 = "L3_IPv4";
/** /**
* Update packetIn counters * Update packetIn counters
* *
* @param sw * @param sw
* @param m * @param m
* @param eth * @param eth
*/ */
public void updatePacketInCounters(IOFSwitch sw, OFMessage m, Ethernet eth);
public void updatePacketInCountersLocal(IOFSwitch sw, OFMessage m, Ethernet eth); public void updatePacketInCountersLocal(IOFSwitch sw, OFMessage m, Ethernet eth);
/** /**
* This method can only be used to update packetOut and flowmod counters * This method can only be used to update packetOut and flowmod counters
* *
* @param sw * @param sw
* @param ofMsg * @param ofMsg
*/ */
public void updatePktOutFMCounterStore(IOFSwitch sw, OFMessage ofMsg);
public void updatePktOutFMCounterStoreLocal(IOFSwitch sw, OFMessage ofMsg); public void updatePktOutFMCounterStoreLocal(IOFSwitch sw, OFMessage ofMsg);
/** /**
...@@ -63,7 +61,7 @@ public interface ICounterStoreService extends IFloodlightService { ...@@ -63,7 +61,7 @@ public interface ICounterStoreService extends IFloodlightService {
* *
*/ */
public void updateFlush(); public void updateFlush();
/** /**
* Retrieve a list of subCategories by counterName. * Retrieve a list of subCategories by counterName.
* null if nothing. * null if nothing.
...@@ -72,9 +70,9 @@ public interface ICounterStoreService extends IFloodlightService { ...@@ -72,9 +70,9 @@ public interface ICounterStoreService extends IFloodlightService {
NetworkLayer layer); NetworkLayer layer);
/** /**
* Create a new ICounter and set the title. Note that the title must be * Create a new ICounter and set the title. Note that the title must be
* unique, otherwise this will throw an IllegalArgumentException. * unique, otherwise this will throw an IllegalArgumentException.
* *
* @param key * @param key
* @param type * @param type
* @return * @return
...@@ -88,7 +86,7 @@ public interface ICounterStoreService extends IFloodlightService { ...@@ -88,7 +86,7 @@ public interface ICounterStoreService extends IFloodlightService {
/** /**
* Returns an immutable map of title:counter with all of the counters in the store. * Returns an immutable map of title:counter with all of the counters in the store.
* *
* (Note - this method may be slow - primarily for debugging/UI) * (Note - this method may be slow - primarily for debugging/UI)
*/ */
public Map<String, ICounter> getAll(); public Map<String, ICounter> getAll();
......
...@@ -47,22 +47,12 @@ public class NullCounterStore implements IFloodlightModule, ...@@ -47,22 +47,12 @@ public class NullCounterStore implements IFloodlightModule,
private ICounter emptyCounter; private ICounter emptyCounter;
private List<String> emptyList; private List<String> emptyList;
private Map<String, ICounter> emptyMap; private Map<String, ICounter> emptyMap;
@Override
public void updatePacketInCounters(IOFSwitch sw, OFMessage m, Ethernet eth) {
// no-op
}
@Override @Override
public void updatePacketInCountersLocal(IOFSwitch sw, OFMessage m, Ethernet eth) { public void updatePacketInCountersLocal(IOFSwitch sw, OFMessage m, Ethernet eth) {
// no-op // no-op
} }
@Override
public void updatePktOutFMCounterStore(IOFSwitch sw, OFMessage ofMsg) {
// no-op
}
@Override @Override
public void updatePktOutFMCounterStoreLocal(IOFSwitch sw, OFMessage ofMsg) { public void updatePktOutFMCounterStoreLocal(IOFSwitch sw, OFMessage ofMsg) {
// no-op // no-op
...@@ -106,7 +96,7 @@ public class NullCounterStore implements IFloodlightModule, ...@@ -106,7 +96,7 @@ public class NullCounterStore implements IFloodlightModule,
public Map<Class<? extends IFloodlightService>, IFloodlightService> public Map<Class<? extends IFloodlightService>, IFloodlightService>
getServiceImpls() { getServiceImpls() {
Map<Class<? extends IFloodlightService>, Map<Class<? extends IFloodlightService>,
IFloodlightService> m = IFloodlightService> m =
new HashMap<Class<? extends IFloodlightService>, new HashMap<Class<? extends IFloodlightService>,
IFloodlightService>(); IFloodlightService>();
m.put(ICounterStoreService.class, this); m.put(ICounterStoreService.class, this);
......
/** /**
* Copyright 2011, Big Switch Networks, Inc. * Copyright 2011, Big Switch Networks, Inc.
* Originally created by David Erickson, Stanford University * Originally created by David Erickson, Stanford University
* *
* Licensed under the Apache License, Version 2.0 (the "License"); you may * 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 * not use this file except in compliance with the License. You may obtain
* a copy of the License at * a copy of the License at
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
**/ **/
/** /**
* *
*/ */
package net.floodlightcontroller.counter; package net.floodlightcontroller.counter;
...@@ -28,7 +28,7 @@ import java.util.Date; ...@@ -28,7 +28,7 @@ import java.util.Date;
* This is a simple counter implementation that doesn't support data series. * This is a simple counter implementation that doesn't support data series.
* The idea is that floodlight only keeps the realtime value for each counter, * The idea is that floodlight only keeps the realtime value for each counter,
* statd, a statistics collection daemon, samples counters at a user-defined interval * statd, a statistics collection daemon, samples counters at a user-defined interval
* and pushes the values to a database, which keeps time-based data series. * and pushes the values to a database, which keeps time-based data series.
* @author Kanzhe * @author Kanzhe
* *
*/ */
...@@ -37,10 +37,10 @@ public class SimpleCounter implements ICounter { ...@@ -37,10 +37,10 @@ public class SimpleCounter implements ICounter {
protected CounterValue counter; protected CounterValue counter;
protected Date samplingTime; protected Date samplingTime;
protected Date startDate; protected Date startDate;
/** /**
* Factory method to create a new counter instance. * Factory method to create a new counter instance.
* *
* @param startDate * @param startDate
* @return * @return
*/ */
...@@ -48,10 +48,10 @@ public class SimpleCounter implements ICounter { ...@@ -48,10 +48,10 @@ public class SimpleCounter implements ICounter {
SimpleCounter cc = new SimpleCounter(startDate, type); SimpleCounter cc = new SimpleCounter(startDate, type);
return cc; return cc;
} }
/** /**
* Factory method to create a copy of a counter instance. * Factory method to create a copy of a counter instance.
* *
* @param startDate * @param startDate
* @return * @return
*/ */
...@@ -67,7 +67,7 @@ public class SimpleCounter implements ICounter { ...@@ -67,7 +67,7 @@ public class SimpleCounter implements ICounter {
cc.setCounter(copy.getCounterDate(), copy.getCounterValue()); cc.setCounter(copy.getCounterDate(), copy.getCounterValue());
return cc; return cc;
} }
/** /**
* Protected constructor - use createCounter factory method instead * Protected constructor - use createCounter factory method instead
* @param startDate * @param startDate
...@@ -75,32 +75,33 @@ public class SimpleCounter implements ICounter { ...@@ -75,32 +75,33 @@ public class SimpleCounter implements ICounter {
protected SimpleCounter(Date startDate, CounterValue.CounterType type) { protected SimpleCounter(Date startDate, CounterValue.CounterType type) {
init(startDate, type); init(startDate, type);
} }
protected void init(Date startDate, CounterValue.CounterType type) { protected void init(Date startDate, CounterValue.CounterType type) {
this.startDate = startDate; this.startDate = startDate;
this.samplingTime = new Date(); this.samplingTime = new Date();
this.counter = new CounterValue(type); this.counter = new CounterValue(type);
} }
/** /**
* This is the key method that has to be both fast and very thread-safe. * This is the key method that has to be both fast and very thread-safe.
*/ */
@Override @Override
synchronized public void increment() { synchronized public void increment() {
this.increment(new Date(), (long)1); this.increment(new Date(), 1);
} }
@Override @Override
synchronized public void increment(Date d, long delta) { synchronized public void increment(Date d, long delta) {
this.samplingTime = d; this.samplingTime = d;
this.counter.increment(delta); this.counter.increment(delta);
} }
synchronized public void setCounter(Date d, CounterValue value) { @Override
synchronized public void setCounter(Date d, CounterValue value) {
this.samplingTime = d; this.samplingTime = d;
this.counter = value; this.counter = value;
} }
/** /**
* This is the method to retrieve the current value. * This is the method to retrieve the current value.
*/ */
...@@ -116,7 +117,7 @@ public class SimpleCounter implements ICounter { ...@@ -116,7 +117,7 @@ public class SimpleCounter implements ICounter {
synchronized public Date getCounterDate() { synchronized public Date getCounterDate() {
return this.samplingTime; return this.samplingTime;
} }
/** /**
* Reset value. * Reset value.
*/ */
...@@ -124,14 +125,5 @@ public class SimpleCounter implements ICounter { ...@@ -124,14 +125,5 @@ public class SimpleCounter implements ICounter {
synchronized public void reset(Date startDate) { synchronized public void reset(Date startDate) {
init(startDate, this.counter.getType()); init(startDate, this.counter.getType());
} }
@Override
/**
* This method only returns the real-time value.
*/
synchronized public CountSeries snapshot(DateSpan dateSpan) {
long[] values = new long[1];
values[0] = this.counter.getLong();
return new CountSeries(this.samplingTime, DateSpan.DAYS, values);
}
} }
/** /**
* Copyright 2011, Big Switch Networks, Inc. * Copyright 2011, Big Switch Networks, Inc.
* Originally created by David Erickson, Stanford University * Originally created by David Erickson, Stanford University
* *
* Licensed under the Apache License, Version 2.0 (the "License"); you may * 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 * not use this file except in compliance with the License. You may obtain
* a copy of the License at * a copy of the License at
...@@ -52,7 +52,6 @@ import net.floodlightcontroller.counter.ICounterStoreService; ...@@ -52,7 +52,6 @@ import net.floodlightcontroller.counter.ICounterStoreService;
import net.floodlightcontroller.packet.Ethernet; import net.floodlightcontroller.packet.Ethernet;
import net.floodlightcontroller.restserver.IRestApiService; import net.floodlightcontroller.restserver.IRestApiService;
import org.openflow.protocol.OFError;
import org.openflow.protocol.OFFlowMod; import org.openflow.protocol.OFFlowMod;
import org.openflow.protocol.OFFlowRemoved; import org.openflow.protocol.OFFlowRemoved;
import org.openflow.protocol.OFMatch; import org.openflow.protocol.OFMatch;
...@@ -68,15 +67,15 @@ import org.openflow.util.LRULinkedHashMap; ...@@ -68,15 +67,15 @@ import org.openflow.util.LRULinkedHashMap;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
public class LearningSwitch public class LearningSwitch
implements IFloodlightModule, ILearningSwitchService, IOFMessageListener { implements IFloodlightModule, ILearningSwitchService, IOFMessageListener {
protected static Logger log = LoggerFactory.getLogger(LearningSwitch.class); protected static Logger log = LoggerFactory.getLogger(LearningSwitch.class);
// Module dependencies // Module dependencies
protected IFloodlightProviderService floodlightProvider; protected IFloodlightProviderService floodlightProvider;
protected ICounterStoreService counterStore; protected ICounterStoreService counterStore;
protected IRestApiService restApi; protected IRestApiService restApi;
// Stores the learned state for each switch // Stores the learned state for each switch
protected Map<IOFSwitch, Map<MacVlanPair,Short>> macVlanToSwitchPortMap; protected Map<IOFSwitch, Map<MacVlanPair,Short>> macVlanToSwitchPortMap;
...@@ -87,25 +86,25 @@ public class LearningSwitch ...@@ -87,25 +86,25 @@ public class LearningSwitch
public static final int APP_ID_BITS = 12; public static final int APP_ID_BITS = 12;
public static final int APP_ID_SHIFT = (64 - APP_ID_BITS); public static final int APP_ID_SHIFT = (64 - APP_ID_BITS);
public static final long LEARNING_SWITCH_COOKIE = (long) (LEARNING_SWITCH_APP_ID & ((1 << APP_ID_BITS) - 1)) << APP_ID_SHIFT; public static final long LEARNING_SWITCH_COOKIE = (long) (LEARNING_SWITCH_APP_ID & ((1 << APP_ID_BITS) - 1)) << APP_ID_SHIFT;
// more flow-mod defaults // more flow-mod defaults
protected static short FLOWMOD_DEFAULT_IDLE_TIMEOUT = 5; // in seconds protected static short FLOWMOD_DEFAULT_IDLE_TIMEOUT = 5; // in seconds
protected static short FLOWMOD_DEFAULT_HARD_TIMEOUT = 0; // infinite protected static short FLOWMOD_DEFAULT_HARD_TIMEOUT = 0; // infinite
protected static short FLOWMOD_PRIORITY = 100; protected static short FLOWMOD_PRIORITY = 100;
// for managing our map sizes // for managing our map sizes
protected static final int MAX_MACS_PER_SWITCH = 1000; protected static final int MAX_MACS_PER_SWITCH = 1000;
// normally, setup reverse flow as well. Disable only for using cbench for comparison with NOX etc. // normally, setup reverse flow as well. Disable only for using cbench for comparison with NOX etc.
protected static final boolean LEARNING_SWITCH_REVERSE_FLOW = true; protected static final boolean LEARNING_SWITCH_REVERSE_FLOW = true;
/** /**
* @param floodlightProvider the floodlightProvider to set * @param floodlightProvider the floodlightProvider to set
*/ */
public void setFloodlightProvider(IFloodlightProviderService floodlightProvider) { public void setFloodlightProvider(IFloodlightProviderService floodlightProvider) {
this.floodlightProvider = floodlightProvider; this.floodlightProvider = floodlightProvider;
} }
@Override @Override
public String getName() { public String getName() {
return "learningswitch"; return "learningswitch";
...@@ -120,13 +119,13 @@ public class LearningSwitch ...@@ -120,13 +119,13 @@ public class LearningSwitch
*/ */
protected void addToPortMap(IOFSwitch sw, long mac, short vlan, short portVal) { protected void addToPortMap(IOFSwitch sw, long mac, short vlan, short portVal) {
Map<MacVlanPair,Short> swMap = macVlanToSwitchPortMap.get(sw); Map<MacVlanPair,Short> swMap = macVlanToSwitchPortMap.get(sw);
if (vlan == (short) 0xffff) { if (vlan == (short) 0xffff) {
// OFMatch.loadFromPacket sets VLAN ID to 0xffff if the packet contains no VLAN tag; // OFMatch.loadFromPacket sets VLAN ID to 0xffff if the packet contains no VLAN tag;
// for our purposes that is equivalent to the default VLAN ID 0 // for our purposes that is equivalent to the default VLAN ID 0
vlan = 0; vlan = 0;
} }
if (swMap == null) { if (swMap == null) {
// May be accessed by REST API so we need to make it thread safe // May be accessed by REST API so we need to make it thread safe
swMap = Collections.synchronizedMap(new LRULinkedHashMap<MacVlanPair,Short>(MAX_MACS_PER_SWITCH)); swMap = Collections.synchronizedMap(new LRULinkedHashMap<MacVlanPair,Short>(MAX_MACS_PER_SWITCH));
...@@ -134,7 +133,7 @@ public class LearningSwitch ...@@ -134,7 +133,7 @@ public class LearningSwitch
} }
swMap.put(new MacVlanPair(mac, vlan), portVal); swMap.put(new MacVlanPair(mac, vlan), portVal);
} }
/** /**
* Removes a host from the MAC/VLAN->SwitchPort mapping * Removes a host from the MAC/VLAN->SwitchPort mapping
* @param sw The switch to remove the mapping from * @param sw The switch to remove the mapping from
...@@ -164,18 +163,18 @@ public class LearningSwitch ...@@ -164,18 +163,18 @@ public class LearningSwitch
Map<MacVlanPair,Short> swMap = macVlanToSwitchPortMap.get(sw); Map<MacVlanPair,Short> swMap = macVlanToSwitchPortMap.get(sw);
if (swMap != null) if (swMap != null)
return swMap.get(new MacVlanPair(mac, vlan)); return swMap.get(new MacVlanPair(mac, vlan));
// if none found // if none found
return null; return null;
} }
/** /**
* Clears the MAC/VLAN -> SwitchPort map for all switches * Clears the MAC/VLAN -> SwitchPort map for all switches
*/ */
public void clearLearnedTable() { public void clearLearnedTable() {
macVlanToSwitchPortMap.clear(); macVlanToSwitchPortMap.clear();
} }
/** /**
* Clears the MAC/VLAN -> SwitchPort map for a single switch * Clears the MAC/VLAN -> SwitchPort map for a single switch
* @param sw The switch to clear the mapping for * @param sw The switch to clear the mapping for
...@@ -185,12 +184,12 @@ public class LearningSwitch ...@@ -185,12 +184,12 @@ public class LearningSwitch
if (swMap != null) if (swMap != null)
swMap.clear(); swMap.clear();
} }
@Override @Override
public synchronized Map<IOFSwitch, Map<MacVlanPair,Short>> getTable() { public synchronized Map<IOFSwitch, Map<MacVlanPair,Short>> getTable() {
return macVlanToSwitchPortMap; return macVlanToSwitchPortMap;
} }
/** /**
* Writes a OFFlowMod to a switch. * Writes a OFFlowMod to a switch.
* @param sw The switch tow rite the flowmod to. * @param sw The switch tow rite the flowmod to.
...@@ -223,7 +222,7 @@ public class LearningSwitch ...@@ -223,7 +222,7 @@ public class LearningSwitch
// from the length field in the // from the length field in the
// header. */ // header. */
// }; // };
OFFlowMod flowMod = (OFFlowMod) floodlightProvider.getOFMessageFactory().getMessage(OFType.FLOW_MOD); OFFlowMod flowMod = (OFFlowMod) floodlightProvider.getOFMessageFactory().getMessage(OFType.FLOW_MOD);
flowMod.setMatch(match); flowMod.setMatch(match);
flowMod.setCookie(LearningSwitch.LEARNING_SWITCH_COOKIE); flowMod.setCookie(LearningSwitch.LEARNING_SWITCH_COOKIE);
...@@ -247,12 +246,12 @@ public class LearningSwitch ...@@ -247,12 +246,12 @@ public class LearningSwitch
flowMod.setLength((short) (OFFlowMod.MINIMUM_LENGTH + OFActionOutput.MINIMUM_LENGTH)); flowMod.setLength((short) (OFFlowMod.MINIMUM_LENGTH + OFActionOutput.MINIMUM_LENGTH));
if (log.isTraceEnabled()) { if (log.isTraceEnabled()) {
log.trace("{} {} flow mod {}", log.trace("{} {} flow mod {}",
new Object[]{ sw, (command == OFFlowMod.OFPFC_DELETE) ? "deleting" : "adding", flowMod }); new Object[]{ sw, (command == OFFlowMod.OFPFC_DELETE) ? "deleting" : "adding", flowMod });
} }
counterStore.updatePktOutFMCounterStore(sw, flowMod); counterStore.updatePktOutFMCounterStoreLocal(sw, flowMod);
// and write it out // and write it out
try { try {
sw.write(flowMod, null); sw.write(flowMod, null);
...@@ -260,11 +259,11 @@ public class LearningSwitch ...@@ -260,11 +259,11 @@ public class LearningSwitch
log.error("Failed to write {} to switch {}", new Object[]{ flowMod, sw }, e); log.error("Failed to write {} to switch {}", new Object[]{ flowMod, sw }, e);
} }
} }
/** /**
* Pushes a packet-out to a switch. The assumption here is that * Pushes a packet-out to a switch. The assumption here is that
* the packet-in was also generated from the same switch. Thus, if the input * the packet-in was also generated from the same switch. Thus, if the input
* port of the packet-in and the outport are the same, the function will not * port of the packet-in and the outport are the same, the function will not
* push the packet-out. * push the packet-out.
* @param sw switch that generated the packet-in, and from which packet-out is sent * @param sw switch that generated the packet-in, and from which packet-out is sent
* @param match OFmatch * @param match OFmatch
...@@ -276,21 +275,21 @@ public class LearningSwitch ...@@ -276,21 +275,21 @@ public class LearningSwitch
return; return;
} }
// The assumption here is (sw) is the switch that generated the // The assumption here is (sw) is the switch that generated the
// packet-in. If the input port is the same as output port, then // packet-in. If the input port is the same as output port, then
// the packet-out should be ignored. // the packet-out should be ignored.
if (pi.getInPort() == outport) { if (pi.getInPort() == outport) {
if (log.isDebugEnabled()) { if (log.isDebugEnabled()) {
log.debug("Attempting to do packet-out to the same " + log.debug("Attempting to do packet-out to the same " +
"interface as packet-in. Dropping packet. " + "interface as packet-in. Dropping packet. " +
" SrcSwitch={}, match = {}, pi={}", " SrcSwitch={}, match = {}, pi={}",
new Object[]{sw, match, pi}); new Object[]{sw, match, pi});
return; return;
} }
} }
if (log.isTraceEnabled()) { if (log.isTraceEnabled()) {
log.trace("PacketOut srcSwitch={} match={} pi={}", log.trace("PacketOut srcSwitch={} match={} pi={}",
new Object[] {sw, match, pi}); new Object[] {sw, match, pi});
} }
...@@ -336,15 +335,15 @@ public class LearningSwitch ...@@ -336,15 +335,15 @@ public class LearningSwitch
log.error("Failure writing packet out", e); log.error("Failure writing packet out", e);
} }
} }
/** /**
* Writes an OFPacketOut message to a switch. * Writes an OFPacketOut message to a switch.
* @param sw The switch to write the PacketOut to. * @param sw The switch to write the PacketOut to.
* @param packetInMessage The corresponding PacketIn. * @param packetInMessage The corresponding PacketIn.
* @param egressPort The switchport to output the PacketOut. * @param egressPort The switchport to output the PacketOut.
*/ */
private void writePacketOutForPacketIn(IOFSwitch sw, private void writePacketOutForPacketIn(IOFSwitch sw,
OFPacketIn packetInMessage, OFPacketIn packetInMessage,
short egressPort) { short egressPort) {
// from openflow 1.0 spec - need to set these on a struct ofp_packet_out: // from openflow 1.0 spec - need to set these on a struct ofp_packet_out:
// uint32_t buffer_id; /* ID assigned by datapath (-1 if none). */ // uint32_t buffer_id; /* ID assigned by datapath (-1 if none). */
...@@ -354,7 +353,7 @@ public class LearningSwitch ...@@ -354,7 +353,7 @@ public class LearningSwitch
/* uint8_t data[0]; */ /* Packet data. The length is inferred /* uint8_t data[0]; */ /* Packet data. The length is inferred
from the length field in the header. from the length field in the header.
(Only meaningful if buffer_id == -1.) */ (Only meaningful if buffer_id == -1.) */
OFPacketOut packetOutMessage = (OFPacketOut) floodlightProvider.getOFMessageFactory().getMessage(OFType.PACKET_OUT); OFPacketOut packetOutMessage = (OFPacketOut) floodlightProvider.getOFMessageFactory().getMessage(OFType.PACKET_OUT);
short packetOutLength = (short)OFPacketOut.MINIMUM_LENGTH; // starting length short packetOutLength = (short)OFPacketOut.MINIMUM_LENGTH; // starting length
...@@ -363,34 +362,34 @@ public class LearningSwitch ...@@ -363,34 +362,34 @@ public class LearningSwitch
packetOutMessage.setInPort(packetInMessage.getInPort()); packetOutMessage.setInPort(packetInMessage.getInPort());
packetOutMessage.setActionsLength((short)OFActionOutput.MINIMUM_LENGTH); packetOutMessage.setActionsLength((short)OFActionOutput.MINIMUM_LENGTH);
packetOutLength += OFActionOutput.MINIMUM_LENGTH; packetOutLength += OFActionOutput.MINIMUM_LENGTH;
// set actions // set actions
List<OFAction> actions = new ArrayList<OFAction>(1); List<OFAction> actions = new ArrayList<OFAction>(1);
actions.add(new OFActionOutput(egressPort, (short) 0)); actions.add(new OFActionOutput(egressPort, (short) 0));
packetOutMessage.setActions(actions); packetOutMessage.setActions(actions);
// set data - only if buffer_id == -1 // set data - only if buffer_id == -1
if (packetInMessage.getBufferId() == OFPacketOut.BUFFER_ID_NONE) { if (packetInMessage.getBufferId() == OFPacketOut.BUFFER_ID_NONE) {
byte[] packetData = packetInMessage.getPacketData(); byte[] packetData = packetInMessage.getPacketData();
packetOutMessage.setPacketData(packetData); packetOutMessage.setPacketData(packetData);
packetOutLength += (short)packetData.length; packetOutLength += (short)packetData.length;
} }
// finally, set the total length // finally, set the total length
packetOutMessage.setLength(packetOutLength); packetOutMessage.setLength(packetOutLength);
// and write it out // and write it out
try { try {
counterStore.updatePktOutFMCounterStore(sw, packetOutMessage); counterStore.updatePktOutFMCounterStoreLocal(sw, packetOutMessage);
sw.write(packetOutMessage, null); sw.write(packetOutMessage, null);
} catch (IOException e) { } catch (IOException e) {
log.error("Failed to write {} to switch {}: {}", new Object[]{ packetOutMessage, sw, e }); log.error("Failed to write {} to switch {}: {}", new Object[]{ packetOutMessage, sw, e });
} }
} }
/** /**
* Processes a OFPacketIn message. If the switch has learned the MAC/VLAN to port mapping * Processes a OFPacketIn message. If the switch has learned the MAC/VLAN to port mapping
* for the pair it will write a FlowMod for. If the mapping has not been learned the * for the pair it will write a FlowMod for. If the mapping has not been learned the
* we will flood the packet. * we will flood the packet.
* @param sw * @param sw
* @param pi * @param pi
...@@ -415,7 +414,7 @@ public class LearningSwitch ...@@ -415,7 +414,7 @@ public class LearningSwitch
// If source MAC is a unicast address, learn the port for this MAC/VLAN // If source MAC is a unicast address, learn the port for this MAC/VLAN
this.addToPortMap(sw, sourceMac, vlan, pi.getInPort()); this.addToPortMap(sw, sourceMac, vlan, pi.getInPort());
} }
// Now output flow-mod and/or packet // Now output flow-mod and/or packet
Short outPort = getFromPortMap(sw, destMac, vlan); Short outPort = getFromPortMap(sw, destMac, vlan);
if (outPort == null) { if (outPort == null) {
...@@ -483,7 +482,7 @@ public class LearningSwitch ...@@ -483,7 +482,7 @@ public class LearningSwitch
// it from the macVlanToPortMap to revert to flooding packets to this device. // it from the macVlanToPortMap to revert to flooding packets to this device.
this.removeFromPortMap(sw, Ethernet.toLong(match.getDataLayerSource()), this.removeFromPortMap(sw, Ethernet.toLong(match.getDataLayerSource()),
match.getDataLayerVirtualLan()); match.getDataLayerVirtualLan());
// Also, if packets keep coming from another device (e.g. from ping), the // Also, if packets keep coming from another device (e.g. from ping), the
// corresponding reverse flow entry will never expire on its own and will // corresponding reverse flow entry will never expire on its own and will
// send the packets to the wrong port (the matching input port of the // send the packets to the wrong port (the matching input port of the
...@@ -501,9 +500,9 @@ public class LearningSwitch ...@@ -501,9 +500,9 @@ public class LearningSwitch
match.getInputPort()); match.getInputPort());
return Command.CONTINUE; return Command.CONTINUE;
} }
// IOFMessageListener // IOFMessageListener
@Override @Override
public Command receive(IOFSwitch sw, OFMessage msg, FloodlightContext cntx) { public Command receive(IOFSwitch sw, OFMessage msg, FloodlightContext cntx) {
switch (msg.getType()) { switch (msg.getType()) {
...@@ -512,7 +511,7 @@ public class LearningSwitch ...@@ -512,7 +511,7 @@ public class LearningSwitch
case FLOW_REMOVED: case FLOW_REMOVED:
return this.processFlowRemovedMessage(sw, (OFFlowRemoved) msg); return this.processFlowRemovedMessage(sw, (OFFlowRemoved) msg);
case ERROR: case ERROR:
log.info("received an error {} from switch {}", (OFError) msg, sw); log.info("received an error {} from switch {}", msg, sw);
return Command.CONTINUE; return Command.CONTINUE;
default: default:
break; break;
...@@ -532,10 +531,10 @@ public class LearningSwitch ...@@ -532,10 +531,10 @@ public class LearningSwitch
} }
// IFloodlightModule // IFloodlightModule
@Override @Override
public Collection<Class<? extends IFloodlightService>> getModuleServices() { public Collection<Class<? extends IFloodlightService>> getModuleServices() {
Collection<Class<? extends IFloodlightService>> l = Collection<Class<? extends IFloodlightService>> l =
new ArrayList<Class<? extends IFloodlightService>>(); new ArrayList<Class<? extends IFloodlightService>>();
l.add(ILearningSwitchService.class); l.add(ILearningSwitchService.class);
return l; return l;
...@@ -545,7 +544,7 @@ public class LearningSwitch ...@@ -545,7 +544,7 @@ public class LearningSwitch
public Map<Class<? extends IFloodlightService>, IFloodlightService> public Map<Class<? extends IFloodlightService>, IFloodlightService>
getServiceImpls() { getServiceImpls() {
Map<Class<? extends IFloodlightService>, Map<Class<? extends IFloodlightService>,
IFloodlightService> m = IFloodlightService> m =
new HashMap<Class<? extends IFloodlightService>, new HashMap<Class<? extends IFloodlightService>,
IFloodlightService>(); IFloodlightService>();
m.put(ILearningSwitchService.class, this); m.put(ILearningSwitchService.class, this);
...@@ -555,7 +554,7 @@ public class LearningSwitch ...@@ -555,7 +554,7 @@ public class LearningSwitch
@Override @Override
public Collection<Class<? extends IFloodlightService>> public Collection<Class<? extends IFloodlightService>>
getModuleDependencies() { getModuleDependencies() {
Collection<Class<? extends IFloodlightService>> l = Collection<Class<? extends IFloodlightService>> l =
new ArrayList<Class<? extends IFloodlightService>>(); new ArrayList<Class<? extends IFloodlightService>>();
l.add(IFloodlightProviderService.class); l.add(IFloodlightProviderService.class);
l.add(ICounterStoreService.class); l.add(ICounterStoreService.class);
...@@ -566,7 +565,7 @@ public class LearningSwitch ...@@ -566,7 +565,7 @@ public class LearningSwitch
@Override @Override
public void init(FloodlightModuleContext context) public void init(FloodlightModuleContext context)
throws FloodlightModuleException { throws FloodlightModuleException {
macVlanToSwitchPortMap = macVlanToSwitchPortMap =
new ConcurrentHashMap<IOFSwitch, Map<MacVlanPair,Short>>(); new ConcurrentHashMap<IOFSwitch, Map<MacVlanPair,Short>>();
floodlightProvider = floodlightProvider =
context.getServiceImpl(IFloodlightProviderService.class); context.getServiceImpl(IFloodlightProviderService.class);
...@@ -582,7 +581,7 @@ public class LearningSwitch ...@@ -582,7 +581,7 @@ public class LearningSwitch
floodlightProvider.addOFMessageListener(OFType.FLOW_REMOVED, this); floodlightProvider.addOFMessageListener(OFType.FLOW_REMOVED, this);
floodlightProvider.addOFMessageListener(OFType.ERROR, this); floodlightProvider.addOFMessageListener(OFType.ERROR, this);
restApi.addRestletRoutable(new LearningSwitchWebRoutable()); restApi.addRestletRoutable(new LearningSwitchWebRoutable());
// read our config options // read our config options
Map<String, String> configOptions = context.getConfigParams(this); Map<String, String> configOptions = context.getConfigParams(this);
try { try {
...@@ -615,11 +614,11 @@ public class LearningSwitch ...@@ -615,11 +614,11 @@ public class LearningSwitch
"using default of {}", "using default of {}",
FLOWMOD_PRIORITY); FLOWMOD_PRIORITY);
} }
log.debug("FlowMod idle timeout set to {} seconds", log.debug("FlowMod idle timeout set to {} seconds",
FLOWMOD_DEFAULT_IDLE_TIMEOUT); FLOWMOD_DEFAULT_IDLE_TIMEOUT);
log.debug("FlowMod hard timeout set to {} seconds", log.debug("FlowMod hard timeout set to {} seconds",
FLOWMOD_DEFAULT_HARD_TIMEOUT); FLOWMOD_DEFAULT_HARD_TIMEOUT);
log.debug("FlowMod priority set to {}", log.debug("FlowMod priority set to {}",
FLOWMOD_PRIORITY); FLOWMOD_PRIORITY);
} }
} }
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment