Giant patch of changes to support OpenFlow 1.3
The following people have contributed to this patch:
- Ali Al-Shabibi <alshabibi.ali@gmail.com>
- Ayaka Koshibe <ayaka@onlab.us>
- Brian O'Connor <bocon@onlab.us>
- Jonathan Hart <jono@onlab.us>
- Matteo Gerola <mgerola@create-net.org>
- Michele Santuari <michele.santuari@create-net.org>
- Pavlin Radoslavov <pavlin@onlab.us>
- Saurav Das <sauravdas@alumni.stanford.edu>
- Toshio Koide <t-koide@onlab.us>
- Yuta HIGUCHI <y-higuchi@onlab.us>
The patch includes the following changes:
- New Floodlight I/O loop / state machine
- New switch/port handling
- New role management (incl. Role.EQUAL)
- Added Floodlight debug framework
- Updates to Controller.java
- Move to Loxigen's OpenflowJ library
- Added OF1.3 support
- Added support for different switches (via DriverManager)
- Updated ONOS modules to use new APIs
- Added and updated unit tests
Change-Id: Ic70a8d50f7136946193d2ba2e4dc0b4bfac5f599
diff --git a/src/main/java/net/floodlightcontroller/debugcounter/DebugCounter.java b/src/main/java/net/floodlightcontroller/debugcounter/DebugCounter.java
new file mode 100644
index 0000000..b7c17b2
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/debugcounter/DebugCounter.java
@@ -0,0 +1,741 @@
+package net.floodlightcontroller.debugcounter;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.collect.Sets;
+
+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.debugcounter.web.DebugCounterRoutable;
+import net.floodlightcontroller.restserver.IRestApiService;
+
+/**
+ * This class implements a central store for all counters used for debugging the
+ * system. For counters based on traffic-type, see ICounterStoreService.
+ *
+ * @author saurav
+ */
+public class DebugCounter implements IFloodlightModule, IDebugCounterService {
+ protected static Logger log = LoggerFactory.getLogger(DebugCounter.class);
+
+ /**
+ * registered counters need a counter id
+ */
+ protected AtomicInteger counterIdCounter = new AtomicInteger();
+
+ /**
+ * The counter value
+ */
+ protected class MutableLong {
+ long value = 0;
+ public void increment() { value += 1; }
+ public void increment(long incr) { value += incr; }
+ public long get() { return value; }
+ public void set(long val) { value = val; }
+ }
+
+ /**
+ * protected class to store counter information
+ */
+ public static class CounterInfo {
+ String moduleCounterHierarchy;
+ String counterDesc;
+ CounterType ctype;
+ String moduleName;
+ String counterHierarchy;
+ int counterId;
+ boolean enabled;
+ String[] metaData;
+
+ public CounterInfo(int counterId, boolean enabled,
+ String moduleName, String counterHierarchy,
+ String desc, CounterType ctype, String... metaData) {
+ this.moduleCounterHierarchy = moduleName + "/" + counterHierarchy;
+ this.moduleName = moduleName;
+ this.counterHierarchy = counterHierarchy;
+ this.counterDesc = desc;
+ this.ctype = ctype;
+ this.counterId = counterId;
+ this.enabled = enabled;
+ this.metaData = metaData;
+ }
+
+ public String getModuleCounterHierarchy() { return moduleCounterHierarchy; }
+ public String getCounterDesc() { return counterDesc; }
+ public CounterType getCtype() { return ctype; }
+ public String getModuleName() { return moduleName; }
+ public String getCounterHierarchy() { return counterHierarchy; }
+ public int getCounterId() { return counterId; }
+ public boolean isEnabled() { return enabled; }
+ public String[] getMetaData() { return metaData; }
+ }
+
+ //******************
+ // Global stores
+ //******************
+
+ /**
+ * Counter info for a debug counter
+ */
+ public class DebugCounterInfo {
+ CounterInfo cinfo;
+ AtomicLong cvalue;
+
+ public DebugCounterInfo(CounterInfo cinfo) {
+ this.cinfo = cinfo;
+ this.cvalue = new AtomicLong();
+ }
+ public CounterInfo getCounterInfo() {
+ return cinfo;
+ }
+ public Long getCounterValue() {
+ return cvalue.get();
+ }
+ }
+
+ /**
+ * Global debug-counter storage across all threads. These are
+ * updated from the local per thread counters by the flush counters method.
+ */
+ protected static DebugCounterInfo[] allCounters =
+ new DebugCounterInfo[MAX_COUNTERS];
+
+
+ /**
+ * per module counters, indexed by the module name and storing three levels
+ * of Counter information in the form of CounterIndexStore
+ */
+ protected ConcurrentHashMap<String, ConcurrentHashMap<String, CounterIndexStore>>
+ moduleCounters = new ConcurrentHashMap<String,
+ ConcurrentHashMap<String,
+ CounterIndexStore>>();
+
+ protected class CounterIndexStore {
+ int index;
+ Map<String, CounterIndexStore> nextLevel;
+
+ public CounterIndexStore(int index, Map<String,CounterIndexStore> cis) {
+ this.index = index;
+ this.nextLevel = cis;
+ }
+ }
+
+ /**
+ * fast global cache for counter ids that are currently active
+ */
+ protected Set<Integer> currentCounters = Collections.newSetFromMap(
+ new ConcurrentHashMap<Integer,Boolean>());
+
+ //******************
+ // Thread local stores
+ //******************
+
+ /**
+ * Thread local storage of counter info
+ */
+ protected class LocalCounterInfo {
+ boolean enabled;
+ MutableLong cvalue;
+
+ public LocalCounterInfo(boolean enabled) {
+ this.enabled = enabled;
+ this.cvalue = new MutableLong();
+ }
+ }
+
+ /**
+ * Thread local debug counters used for maintaining counters local to a thread.
+ */
+ protected final ThreadLocal<LocalCounterInfo[]> threadlocalCounters =
+ new ThreadLocal<LocalCounterInfo[]>() {
+ @Override
+ protected LocalCounterInfo[] initialValue() {
+ return new LocalCounterInfo[MAX_COUNTERS];
+ }
+ };
+
+ /**
+ * Thread local cache for counter ids that are currently active.
+ */
+ protected final ThreadLocal<Set<Integer>> threadlocalCurrentCounters =
+ new ThreadLocal<Set<Integer>>() {
+ @Override
+ protected Set<Integer> initialValue() {
+ return new HashSet<Integer>();
+ }
+ };
+
+ //*******************************
+ // IDebugCounter
+ //*******************************
+
+ protected class CounterImpl implements IDebugCounter {
+ private final int counterId;
+
+ public CounterImpl(int counterId) {
+ this.counterId = counterId;
+ }
+
+ @Override
+ public void updateCounterWithFlush() {
+ if (!validCounterId()) return;
+ updateCounter(counterId, 1, true);
+ }
+
+ @Override
+ public void updateCounterNoFlush() {
+ if (!validCounterId()) return;
+ updateCounter(counterId, 1, false);
+ }
+
+ @Override
+ public void updateCounterWithFlush(int incr) {
+ if (!validCounterId()) return;
+ updateCounter(counterId, incr, true);
+ }
+
+ @Override
+ public void updateCounterNoFlush(int incr) {
+ if (!validCounterId()) return;
+ updateCounter(counterId, incr, false);
+ }
+
+ @Override
+ public long getCounterValue() {
+ if (!validCounterId()) return -1;
+ return allCounters[counterId].cvalue.get();
+ }
+
+ private boolean validCounterId() {
+ if (counterId < 0 || counterId >= MAX_COUNTERS) {
+ log.error("Invalid counterId invoked");
+ return false;
+ }
+ return true;
+ }
+
+ }
+
+ //*******************************
+ // IDebugCounterService
+ //*******************************
+
+ @Override
+ public IDebugCounter registerCounter(String moduleName, String counterHierarchy,
+ String counterDescription, CounterType counterType,
+ String... metaData)
+ throws MaxCountersRegistered, MaxHierarchyRegistered,
+ MissingHierarchicalLevel {
+ // check if counter already exists
+ if (!moduleCounters.containsKey(moduleName)) {
+ moduleCounters.putIfAbsent(moduleName,
+ new ConcurrentHashMap<String, CounterIndexStore>());
+ }
+ RetCtrInfo rci = getCounterId(moduleName, counterHierarchy);
+ if (rci.allLevelsFound) {
+ // counter exists
+ log.info("Counter exists for {}/{} -- resetting counters", moduleName,
+ counterHierarchy);
+ resetCounterHierarchy(moduleName, counterHierarchy);
+ return new CounterImpl(rci.ctrIds[rci.foundUptoLevel-1]);
+ }
+ // check for validity of counter
+ if (rci.levels.length > MAX_HIERARCHY) {
+ String err = "Registry of counterHierarchy " + counterHierarchy +
+ " exceeds max hierachy " + MAX_HIERARCHY + ".. aborting";
+ throw new MaxHierarchyRegistered(err);
+ }
+ if (rci.foundUptoLevel < rci.levels.length-1) {
+ String needToRegister = "";
+ for (int i=0; i<=rci.foundUptoLevel; i++) {
+ needToRegister += rci.levels[i];
+ }
+ String err = "Attempting to register hierarchical counterHierarchy " +
+ counterHierarchy + " but parts of hierarchy missing. " +
+ "Please register " + needToRegister + " first";
+ throw new MissingHierarchicalLevel(err);
+ }
+
+ // get a new counter id
+ int counterId = counterIdCounter.getAndIncrement();
+ if (counterId >= MAX_COUNTERS) {
+ throw new MaxCountersRegistered("max counters reached");
+ }
+ // create storage for counter
+ boolean enabled = (counterType == CounterType.ALWAYS_COUNT) ? true : false;
+ CounterInfo ci = new CounterInfo(counterId, enabled, moduleName,
+ counterHierarchy, counterDescription,
+ counterType, metaData);
+ allCounters[counterId] = new DebugCounterInfo(ci);
+
+ // account for the new counter in the module counter hierarchy
+ addToModuleCounterHierarchy(moduleName, counterId, rci);
+
+ // finally add to active counters
+ if (enabled) {
+ currentCounters.add(counterId);
+ }
+ return new CounterImpl(counterId);
+ }
+
+ private void updateCounter(int counterId, int incr, boolean flushNow) {
+ if (counterId < 0 || counterId >= MAX_COUNTERS) return;
+
+ LocalCounterInfo[] thiscounters = this.threadlocalCounters.get();
+ if (thiscounters[counterId] == null) {
+ // seeing this counter for the first time in this thread - create local
+ // store by consulting global store
+ DebugCounterInfo dc = allCounters[counterId];
+ if (dc != null) {
+ thiscounters[counterId] = new LocalCounterInfo(dc.cinfo.enabled);
+ if (dc.cinfo.enabled) {
+ Set<Integer> thisset = this.threadlocalCurrentCounters.get();
+ thisset.add(counterId);
+ }
+ } else {
+ log.error("updateCounter seen locally for counter {} but no global"
+ + "storage exists for it yet .. not updating", counterId);
+ return;
+ }
+ }
+
+ // update local store if enabled locally for updating
+ LocalCounterInfo lc = thiscounters[counterId];
+ if (lc.enabled) {
+ lc.cvalue.increment(incr);
+ if (flushNow) {
+ DebugCounterInfo dc = allCounters[counterId];
+ if (dc.cinfo.enabled) {
+ // globally enabled - flush now
+ dc.cvalue.addAndGet(lc.cvalue.get());
+ lc.cvalue.set(0);
+ } else {
+ // global counter is disabled - don't flush, disable locally
+ lc.enabled = false;
+ Set<Integer> thisset = this.threadlocalCurrentCounters.get();
+ thisset.remove(counterId);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void flushCounters() {
+ LocalCounterInfo[] thiscounters = this.threadlocalCounters.get();
+ Set<Integer> thisset = this.threadlocalCurrentCounters.get();
+ ArrayList<Integer> temp = new ArrayList<Integer>();
+
+ for (int counterId : thisset) {
+ LocalCounterInfo lc = thiscounters[counterId];
+ if (lc.cvalue.get() > 0) {
+ DebugCounterInfo dc = allCounters[counterId];
+ if (dc.cinfo.enabled) {
+ // globally enabled - flush now
+ dc.cvalue.addAndGet(lc.cvalue.get());
+ lc.cvalue.set(0);
+ } else {
+ // global counter is disabled - don't flush, disable locally
+ lc.enabled = false;
+ temp.add(counterId);
+ }
+ }
+ }
+ for (int cId : temp) {
+ thisset.remove(cId);
+ }
+
+ // At this point it is possible that the thread-local set does not
+ // include a counter that has been enabled and is present in the global set.
+ // We need to sync thread-local currently enabled set of counterIds with
+ // the global set.
+ Sets.SetView<Integer> sv = Sets.difference(currentCounters, thisset);
+ for (int counterId : sv) {
+ if (thiscounters[counterId] != null) {
+ thiscounters[counterId].enabled = true;
+ thisset.add(counterId);
+ }
+ }
+ }
+
+ @Override
+ public void resetCounterHierarchy(String moduleName, String counterHierarchy) {
+ RetCtrInfo rci = getCounterId(moduleName, counterHierarchy);
+ if (!rci.allLevelsFound) {
+ String missing = rci.levels[rci.foundUptoLevel];
+ log.error("Cannot reset counter hierarchy - missing counter {}", missing);
+ return;
+ }
+ // reset at this level
+ allCounters[rci.ctrIds[rci.foundUptoLevel-1]].cvalue.set(0);
+ // reset all levels below
+ ArrayList<Integer> resetIds = getHierarchyBelow(moduleName, rci);
+ for (int index : resetIds) {
+ allCounters[index].cvalue.set(0);
+ }
+ }
+
+ @Override
+ public void resetAllCounters() {
+ RetCtrInfo rci = new RetCtrInfo();
+ rci.levels = "".split("/");
+ for (String moduleName : moduleCounters.keySet()) {
+ ArrayList<Integer> resetIds = getHierarchyBelow(moduleName, rci);
+ for (int index : resetIds) {
+ allCounters[index].cvalue.set(0);
+ }
+ }
+ }
+
+ @Override
+ public void resetAllModuleCounters(String moduleName) {
+ Map<String, CounterIndexStore> target = moduleCounters.get(moduleName);
+ RetCtrInfo rci = new RetCtrInfo();
+ rci.levels = "".split("/");
+
+ if (target != null) {
+ ArrayList<Integer> resetIds = getHierarchyBelow(moduleName, rci);
+ for (int index : resetIds) {
+ allCounters[index].cvalue.set(0);
+ }
+ } else {
+ if (log.isDebugEnabled())
+ log.debug("No module found with name {}", moduleName);
+ }
+ }
+
+ @Override
+ public void enableCtrOnDemand(String moduleName, String counterHierarchy) {
+ RetCtrInfo rci = getCounterId(moduleName, counterHierarchy);
+ if (!rci.allLevelsFound) {
+ String missing = rci.levels[rci.foundUptoLevel];
+ log.error("Cannot enable counter - counter not found {}", missing);
+ return;
+ }
+ // enable specific counter
+ DebugCounterInfo dc = allCounters[rci.ctrIds[rci.foundUptoLevel-1]];
+ dc.cinfo.enabled = true;
+ currentCounters.add(dc.cinfo.counterId);
+ }
+
+ @Override
+ public void disableCtrOnDemand(String moduleName, String counterHierarchy) {
+ RetCtrInfo rci = getCounterId(moduleName, counterHierarchy);
+ if (!rci.allLevelsFound) {
+ String missing = rci.levels[rci.foundUptoLevel];
+ log.error("Cannot disable counter - counter not found {}", missing);
+ return;
+ }
+ // disable specific counter
+ DebugCounterInfo dc = allCounters[rci.ctrIds[rci.foundUptoLevel-1]];
+ if (dc.cinfo.ctype == CounterType.COUNT_ON_DEMAND) {
+ dc.cinfo.enabled = false;
+ dc.cvalue.set(0);
+ currentCounters.remove(dc.cinfo.counterId);
+ }
+ }
+
+ @Override
+ public List<DebugCounterInfo> getCounterHierarchy(String moduleName,
+ String counterHierarchy) {
+ RetCtrInfo rci = getCounterId(moduleName, counterHierarchy);
+ if (!rci.allLevelsFound) {
+ String missing = rci.levels[rci.foundUptoLevel];
+ log.error("Cannot fetch counter - counter not found {}", missing);
+ return Collections.emptyList();
+ }
+ ArrayList<DebugCounterInfo> dcilist = new ArrayList<DebugCounterInfo>();
+ // get counter and all below it
+ DebugCounterInfo dc = allCounters[rci.ctrIds[rci.foundUptoLevel-1]];
+ dcilist.add(dc);
+ ArrayList<Integer> belowIds = getHierarchyBelow(moduleName, rci);
+ for (int index : belowIds) {
+ dcilist.add(allCounters[index]);
+ }
+ return dcilist;
+ }
+
+ @Override
+ public List<DebugCounterInfo> getAllCounterValues() {
+ List<DebugCounterInfo> dcilist = new ArrayList<DebugCounterInfo>();
+ RetCtrInfo rci = new RetCtrInfo();
+ rci.levels = "".split("/");
+
+ for (String moduleName : moduleCounters.keySet()) {
+ ArrayList<Integer> resetIds = getHierarchyBelow(moduleName, rci);
+ for (int index : resetIds) {
+ dcilist.add(allCounters[index]);
+ }
+ }
+ return dcilist;
+ }
+
+ @Override
+ public List<DebugCounterInfo> getModuleCounterValues(String moduleName) {
+ List<DebugCounterInfo> dcilist = new ArrayList<DebugCounterInfo>();
+ RetCtrInfo rci = new RetCtrInfo();
+ rci.levels = "".split("/");
+
+ if (moduleCounters.containsKey(moduleName)) {
+ ArrayList<Integer> resetIds = getHierarchyBelow(moduleName, rci);
+ for (int index : resetIds) {
+ dcilist.add(allCounters[index]);
+ }
+ }
+ return dcilist;
+ }
+
+ @Override
+ public boolean containsModuleCounterHierarchy(String moduleName,
+ String counterHierarchy) {
+ if (!moduleCounters.containsKey(moduleName)) return false;
+ RetCtrInfo rci = getCounterId(moduleName, counterHierarchy);
+ return rci.allLevelsFound;
+ }
+
+ @Override
+ public boolean containsModuleName(String moduleName) {
+ return (moduleCounters.containsKey(moduleName)) ? true : false;
+ }
+
+ @Override
+ public List<String> getModuleList() {
+ List<String> retval = new ArrayList<String>();
+ retval.addAll(moduleCounters.keySet());
+ return retval;
+ }
+
+ @Override
+ public List<String> getModuleCounterList(String moduleName) {
+ if (!moduleCounters.containsKey(moduleName))
+ return Collections.emptyList();
+
+ List<String> retval = new ArrayList<String>();
+ RetCtrInfo rci = new RetCtrInfo();
+ rci.levels = "".split("/");
+
+ ArrayList<Integer> cids = getHierarchyBelow(moduleName, rci);
+ for (int index : cids) {
+ retval.add(allCounters[index].cinfo.counterHierarchy);
+ }
+ return retval;
+ }
+
+ //*******************************
+ // Internal Methods
+ //*******************************
+
+ protected class RetCtrInfo {
+ boolean allLevelsFound; // counter indices found all the way down the hierarchy
+ boolean hierarchical; // true if counterHierarchy is hierarchical
+ int foundUptoLevel;
+ int[] ctrIds;
+ String[] levels;
+
+ public RetCtrInfo() {
+ ctrIds = new int[MAX_HIERARCHY];
+ for (int i=0; i<MAX_HIERARCHY; i++) {
+ ctrIds[i] = -1;
+ }
+ }
+
+ @Override
+ public boolean equals(Object oth) {
+ if (!(oth instanceof RetCtrInfo)) return false;
+ RetCtrInfo other = (RetCtrInfo)oth;
+ if (other.allLevelsFound != this.allLevelsFound) return false;
+ if (other.hierarchical != this.hierarchical) return false;
+ if (other.foundUptoLevel != this.foundUptoLevel) return false;
+ if (!Arrays.equals(other.ctrIds, this.ctrIds)) return false;
+ if (!Arrays.equals(other.levels, this.levels)) return false;
+ return true;
+ }
+
+ }
+
+ protected RetCtrInfo getCounterId(String moduleName, String counterHierarchy) {
+ RetCtrInfo rci = new RetCtrInfo();
+ Map<String, CounterIndexStore> templevel = moduleCounters.get(moduleName);
+ rci.levels = counterHierarchy.split("/");
+ if (rci.levels.length > 1) rci.hierarchical = true;
+ if (templevel == null) {
+ log.error("moduleName {} does not exist in debugCounters", moduleName);
+ return rci;
+ }
+
+ /*
+ if (rci.levels.length > MAX_HIERARCHY) {
+ // chop off all array elems greater that MAX_HIERARCHY
+ String[] temp = new String[MAX_HIERARCHY];
+ System.arraycopy(rci.levels, 0, temp, 0, MAX_HIERARCHY);
+ rci.levels = temp;
+ }
+ */
+ for (int i=0; i<rci.levels.length; i++) {
+ if (templevel != null) {
+ CounterIndexStore cis = templevel.get(rci.levels[i]) ;
+ if (cis == null) {
+ // could not find counterHierarchy part at this level
+ break;
+ } else {
+ rci.ctrIds[i] = cis.index;
+ templevel = cis.nextLevel;
+ rci.foundUptoLevel++;
+ if (i == rci.levels.length-1) {
+ rci.allLevelsFound = true;
+ }
+ }
+ } else {
+ // there are no more levels, which means that some part of the
+ // counterHierarchy has no corresponding map
+ break;
+ }
+ }
+ return rci;
+ }
+
+ protected void addToModuleCounterHierarchy(String moduleName, int counterId,
+ RetCtrInfo rci) {
+ Map<String, CounterIndexStore> target = moduleCounters.get(moduleName);
+ if (target == null) return;
+ CounterIndexStore cis = null;
+
+ for (int i=0; i<rci.foundUptoLevel; i++) {
+ cis = target.get(rci.levels[i]);
+ target = cis.nextLevel;
+ }
+ if (cis != null) {
+ if (cis.nextLevel == null)
+ cis.nextLevel = new ConcurrentHashMap<String, CounterIndexStore>();
+ cis.nextLevel.put(rci.levels[rci.foundUptoLevel],
+ new CounterIndexStore(counterId, null));
+ } else {
+ target.put(rci.levels[rci.foundUptoLevel],
+ new CounterIndexStore(counterId, null));
+ }
+ }
+
+ // given a partial hierarchical counter, return the rest of the hierarchy
+ protected ArrayList<Integer> getHierarchyBelow(String moduleName, RetCtrInfo rci) {
+ Map<String, CounterIndexStore> target = moduleCounters.get(moduleName);
+ CounterIndexStore cis = null;
+ ArrayList<Integer> retval = new ArrayList<Integer>();
+ if (target == null) return retval;
+
+ // get to the level given
+ for (int i=0; i<rci.foundUptoLevel; i++) {
+ cis = target.get(rci.levels[i]);
+ target = cis.nextLevel;
+ }
+
+ if (target == null || rci.foundUptoLevel == MAX_HIERARCHY) {
+ // no more levels
+ return retval;
+ } else {
+ // recursively get all ids
+ getIdsAtLevel(target, retval, rci.foundUptoLevel+1);
+ }
+
+ return retval;
+ }
+
+ protected void getIdsAtLevel(Map<String, CounterIndexStore> hcy,
+ ArrayList<Integer> retval, int level) {
+ if (level > MAX_HIERARCHY) return;
+ if (hcy == null || retval == null) return;
+
+ // Can return the counter names as well but for now ids are enough.
+ for (CounterIndexStore cistemp : hcy.values()) {
+ retval.add(cistemp.index); // value at this level
+ if (cistemp.nextLevel != null) {
+ getIdsAtLevel(cistemp.nextLevel, retval, level+1);
+ }
+ }
+ }
+
+ protected void printAllCounterIds() {
+ log.info("<moduleCounterHierarchy>");
+ Set<String> keys = moduleCounters.keySet();
+ for (String key : keys) {
+ log.info("ModuleName: {}", key);
+ Map<String, CounterIndexStore> lev1 = moduleCounters.get(key);
+ for (String key1 : lev1.keySet()) {
+ CounterIndexStore cis1 = lev1.get(key1);
+ log.info(" L1 {}:{}", key1, new Object[] {cis1.index, cis1.nextLevel});
+ if (cis1.nextLevel != null) {
+ Map<String, CounterIndexStore> lev2 = cis1.nextLevel;
+ for (String key2 : lev2.keySet()) {
+ CounterIndexStore cis2 = lev2.get(key2);
+ log.info(" L2 {}:{}", key2, new Object[] {cis2.index,
+ cis2.nextLevel});
+ if (cis2.nextLevel != null) {
+ Map<String, CounterIndexStore> lev3 = cis2.nextLevel;
+ for (String key3 : lev3.keySet()) {
+ CounterIndexStore cis3 = lev3.get(key3);
+ log.info(" L3 {}:{}", key3, new Object[] {cis3.index,
+ cis3.nextLevel});
+ }
+ }
+ }
+ }
+ }
+ }
+ log.info("<\\moduleCounterHierarchy>");
+ }
+
+ //*******************************
+ // IFloodlightModule
+ //*******************************
+
+ @Override
+ public Collection<Class<? extends IFloodlightService>> getModuleServices() {
+ Collection<Class<? extends IFloodlightService>> l =
+ new ArrayList<Class<? extends IFloodlightService>>();
+ l.add(IDebugCounterService.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(IDebugCounterService.class, this);
+ return m;
+ }
+
+ @Override
+ public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
+ ArrayList<Class<? extends IFloodlightService>> deps =
+ new ArrayList<Class<? extends IFloodlightService>>();
+ deps.add(IRestApiService.class);
+ return deps;
+ }
+
+ @Override
+ public void init(FloodlightModuleContext context) throws FloodlightModuleException {
+
+ }
+
+ @Override
+ public void startUp(FloodlightModuleContext context) {
+ IRestApiService restService =
+ context.getServiceImpl(IRestApiService.class);
+ restService.addRestletRoutable(new DebugCounterRoutable());
+ }
+
+}
diff --git a/src/main/java/net/floodlightcontroller/debugcounter/IDebugCounter.java b/src/main/java/net/floodlightcontroller/debugcounter/IDebugCounter.java
new file mode 100644
index 0000000..dbde185
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/debugcounter/IDebugCounter.java
@@ -0,0 +1,38 @@
+package net.floodlightcontroller.debugcounter;
+
+public interface IDebugCounter {
+ /**
+ * Increments the counter by 1 thread-locally, and immediately flushes to
+ * the global counter storage. This method should be used for counters that
+ * are updated outside the OF message processing pipeline.
+ */
+ void updateCounterWithFlush();
+
+ /**
+ * Increments the counter by 1 thread-locally. Flushing to the global
+ * counter storage is delayed (happens with flushCounters() in IDebugCounterService),
+ * resulting in higher performance. This method should be used for counters
+ * updated in the OF message processing pipeline.
+ */
+ void updateCounterNoFlush();
+
+ /**
+ * Increments the counter thread-locally by the 'incr' specified, and immediately
+ * flushes to the global counter storage. This method should be used for counters
+ * that are updated outside the OF message processing pipeline.
+ */
+ void updateCounterWithFlush(int incr);
+
+ /**
+ * Increments the counter thread-locally by the 'incr' specified. Flushing to the global
+ * counter storage is delayed (happens with flushCounters() in IDebugCounterService),
+ * resulting in higher performance. This method should be used for counters
+ * updated in the OF message processing pipeline.
+ */
+ void updateCounterNoFlush(int incr);
+
+ /**
+ * Retrieve the value of the counter from the global counter store
+ */
+ long getCounterValue();
+}
diff --git a/src/main/java/net/floodlightcontroller/debugcounter/IDebugCounterService.java b/src/main/java/net/floodlightcontroller/debugcounter/IDebugCounterService.java
new file mode 100644
index 0000000..f613e7b
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/debugcounter/IDebugCounterService.java
@@ -0,0 +1,260 @@
+package net.floodlightcontroller.debugcounter;
+
+import net.floodlightcontroller.core.module.IFloodlightService;
+import net.floodlightcontroller.debugcounter.DebugCounter.DebugCounterInfo;
+
+import java.util.List;
+
+public interface IDebugCounterService extends IFloodlightService {
+
+ /**
+ * Different counter types. Counters that are meant to be counted-on-demand
+ * need to be separately enabled/disabled.
+ */
+ public enum CounterType {
+ ALWAYS_COUNT,
+ COUNT_ON_DEMAND
+ }
+
+ /**
+ * Debug Counter Qualifiers
+ */
+ public static final String CTR_MDATA_WARN = "warn";
+ public static final String CTR_MDATA_ERROR = "error";
+ public static final String CTR_MDATA_DROP = "drop";
+
+ /**
+ * A limit on the maximum number of counters that can be created
+ */
+ public static final int MAX_COUNTERS = 5000;
+
+ /**
+ * Exception thrown when MAX_COUNTERS have been registered
+ */
+ public class MaxCountersRegistered extends CounterException {
+ private static final long serialVersionUID = 3173747663719376745L;
+ String errormsg;
+ public MaxCountersRegistered(String errormsg) {
+ this.errormsg = errormsg;
+ }
+ @Override
+ public String getMessage() {
+ return this.errormsg;
+ }
+ }
+ /**
+ * Exception thrown when MAX_HIERARCHY has been reached
+ */
+ public class MaxHierarchyRegistered extends CounterException {
+ private static final long serialVersionUID = 967431358683523871L;
+ String errormsg;
+ public MaxHierarchyRegistered(String errormsg) {
+ this.errormsg = errormsg;
+ }
+ @Override
+ public String getMessage() {
+ return this.errormsg;
+ }
+ }
+ /**
+ * Exception thrown when attempting to register a hierarchical counter
+ * where higher levels of the hierarchy have not been pre-registered
+ */
+ public class MissingHierarchicalLevel extends CounterException {
+ private static final long serialVersionUID = 517315311533995739L;
+ String errormsg;
+ public MissingHierarchicalLevel(String errormsg) {
+ this.errormsg = errormsg;
+ }
+ @Override
+ public String getMessage() {
+ return this.errormsg;
+ }
+ }
+
+ public class CounterException extends Exception {
+ private static final long serialVersionUID = 2219781500857866035L;
+ }
+
+ /**
+ * maximum levels of hierarchy
+ * Example of moduleName/counterHierarchy:
+ * switch/00:00:00:00:01:02:03:04/pktin/drops where
+ * moduleName ==> "switch" and
+ * counterHierarchy of 3 ==> "00:00:00:00:01:02:03:04/pktin/drops"
+ */
+ public static final int MAX_HIERARCHY = 3;
+
+ /**
+ * All modules that wish to have the DebugCounterService count for them, must
+ * register their counters by making this call (typically from that module's
+ * 'startUp' method). The counter can then be updated, displayed, reset etc.
+ * using the registered moduleName and counterHierarchy.
+ *
+ * @param moduleName the name of the module which is registering the
+ * counter eg. linkdiscovery or controller or switch
+ * @param counterHierarchy the hierarchical counter name specifying all
+ * the hierarchical levels that come above it.
+ * For example: to register a drop counter for
+ * packet-ins from a switch, the counterHierarchy
+ * can be "00:00:00:00:01:02:03:04/pktin/drops"
+ * It is necessary that counters in hierarchical levels
+ * above have already been pre-registered - in this
+ * example: "00:00:00:00:01:02:03:04/pktin" and
+ * "00:00:00:00:01:02:03:04"
+ * @param counterDescription a descriptive string that gives more information
+ * of what the counter is measuring. For example,
+ * "Measures the number of incoming packets seen by
+ * this module".
+ * @param counterType One of CounterType. On-demand counter types
+ * need to be explicitly enabled/disabled using other
+ * methods in this API -- i.e. registering them is
+ * not enough to start counting.
+ * @param metaData variable arguments that qualify a counter
+ * eg. warn, error etc.
+ * @return IDebugCounter with update methods that can be
+ * used to update a counter.
+ * @throws MaxCountersRegistered
+ * @throws MaxHierarchyRegistered
+ * @throws MissingHierarchicalLevel
+ */
+ public IDebugCounter registerCounter(String moduleName, String counterHierarchy,
+ String counterDescription, CounterType counterType,
+ String... metaData)
+ throws MaxCountersRegistered, MaxHierarchyRegistered,
+ MissingHierarchicalLevel;
+
+ /**
+ * Flush all thread-local counter values (from the current thread)
+ * to the global counter store. This method is not intended for use by any
+ * module. It's typical usage is from floodlight core and it is meant
+ * to flush those counters that are updated in the packet-processing pipeline,
+ * typically with the 'updateCounterNoFlush" methods in IDebugCounter.
+ */
+ public void flushCounters();
+
+ /**
+ * Resets the value of counters in the hierarchy to zero. Note that the reset
+ * applies to the level of counter hierarchy specified AND ALL LEVELS BELOW it
+ * in the hierarchy.
+ * For example: If a hierarchy exists like "00:00:00:00:01:02:03:04/pktin/drops"
+ * specifying a reset hierarchy: "00:00:00:00:01:02:03:04"
+ * will reset all counters for the switch dpid specified;
+ * while specifying a reset hierarchy: ""00:00:00:00:01:02:03:04/pktin"
+ * will reset the pktin counter and all levels below it (like drops)
+ * for the switch dpid specified.
+ */
+ void resetCounterHierarchy(String moduleName, String counterHierarchy);
+
+ /**
+ * Resets the values of all counters in the system.
+ */
+ public void resetAllCounters();
+
+ /**
+ * Resets the values of all counters belonging
+ * to a module with the given 'moduleName'.
+ */
+ public void resetAllModuleCounters(String moduleName);
+
+ /**
+ * This method applies only to CounterType.COUNT_ON_DEMAND. It is used to
+ * enable counting on the counter. Note that this step is necessary to start
+ * counting for these counter types - merely registering the counter is not
+ * enough (as is the case for CounterType.ALWAYS_COUNT). Newly
+ * enabled counters start from an initial value of zero.
+ *
+ * Enabling a counter in a counterHierarchy enables only THAT counter. It
+ * does not enable any other part of the counterHierarchy. For example, if
+ * a hierarchy exists like "00:00:00:00:01:02:03:04/pktin/drops", where the
+ * 'pktin' and 'drops' counters are CounterType.COUNT_ON_DEMAND, then enabling
+ * the 'pktin' counter by specifying the counterHierarchy as
+ * "00:00:00:00:01:02:03:04/pktin" does NOT enable the 'drops' counter.
+ */
+ public void enableCtrOnDemand(String moduleName, String counterHierarchy);
+
+ /**
+ * This method applies only to CounterType.COUNT_ON_DEMAND. It is used to
+ * enable counting on the counter. Note that disabling a counter results in a loss
+ * of the counter value. When re-enabled the counter will restart from zero.
+ *
+ * Disabling a counter in a counterHierarchy disables only THAT counter. It
+ * does not disable any other part of the counterHierarchy. For example, if
+ * a hierarchy exists like "00:00:00:00:01:02:03:04/pktin/drops", where the
+ * 'pktin' and 'drops' counters are CounterType.COUNT_ON_DEMAND, then disabling
+ * the 'pktin' counter by specifying the counterHierarchy as
+ * "00:00:00:00:01:02:03:04/pktin" does NOT disable the 'drops' counter.
+ */
+ public void disableCtrOnDemand(String moduleName, String counterHierarchy);
+
+ /**
+ * Get counter value and associated information for the specified counterHierarchy.
+ * Note that information on the level of counter hierarchy specified
+ * AND ALL LEVELS BELOW it in the hierarchy will be returned.
+ *
+ * For example,
+ * if a hierarchy exists like "00:00:00:00:01:02:03:04/pktin/drops", then
+ * specifying a counterHierarchy of "00:00:00:00:01:02:03:04/pktin" in the
+ * get call will return information on the 'pktin' as well as the 'drops'
+ * counters for the switch dpid specified.
+ *
+ * @return A list of DebugCounterInfo or an empty list if the counter
+ * could not be found
+ */
+ public List<DebugCounterInfo> getCounterHierarchy(String moduleName,
+ String counterHierarchy);
+
+ /**
+ * Get counter values and associated information for all counters in the
+ * system
+ *
+ * @return the list of values/info or an empty list
+ */
+ public List<DebugCounterInfo> getAllCounterValues();
+
+ /**
+ * Get counter values and associated information for all counters associated
+ * with a module.
+ *
+ * @param moduleName
+ * @return the list of values/info or an empty list
+ */
+ public List<DebugCounterInfo> getModuleCounterValues(String moduleName);
+
+ /**
+ * Convenience method to figure out if the the given 'counterHierarchy' corresponds
+ * to a registered counterHierarchy for 'moduleName'. Note that the counter may or
+ * may not be enabled for counting, but if it is registered the method will
+ * return true.
+ *
+ * @param param
+ * @return false if moduleCounterHierarchy is not a registered counter
+ */
+ public boolean containsModuleCounterHierarchy(String moduleName,
+ String counterHierarchy);
+
+ /**
+ * Convenience method to figure out if the the given 'moduleName' corresponds
+ * to a registered moduleName or not. Note that the module may or may not have
+ * a counter enabled for counting, but if it is registered the method will
+ * return true.
+ *
+ * @param param
+ * @return false if moduleName is not a registered counter
+ */
+ public boolean containsModuleName(String moduleName);
+
+ /**
+ * Returns a list of moduleNames registered for debug counters or an empty
+ * list if no counters have been registered in the system
+ */
+ public List<String> getModuleList();
+
+ /**
+ * Returns a list of all counters registered for a specific moduleName
+ * or a empty list
+ */
+ public List<String> getModuleCounterList(String moduleName);
+
+
+}
diff --git a/src/main/java/net/floodlightcontroller/debugcounter/NullDebugCounter.java b/src/main/java/net/floodlightcontroller/debugcounter/NullDebugCounter.java
new file mode 100644
index 0000000..e9081a8
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/debugcounter/NullDebugCounter.java
@@ -0,0 +1,163 @@
+package net.floodlightcontroller.debugcounter;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+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.debugcounter.DebugCounter.DebugCounterInfo;
+
+public class NullDebugCounter implements IFloodlightModule, IDebugCounterService {
+
+
+ @Override
+ public Collection<Class<? extends IFloodlightService>>
+ getModuleServices() {
+ Collection<Class<? extends IFloodlightService>> services =
+ new ArrayList<Class<? extends IFloodlightService>>(1);
+ services.add(IDebugCounterService.class);
+ return services;
+ }
+
+ @Override
+ public Map<Class<? extends IFloodlightService>, IFloodlightService>
+ getServiceImpls() {
+ Map<Class<? extends IFloodlightService>,
+ IFloodlightService> m =
+ new HashMap<Class<? extends IFloodlightService>,
+ IFloodlightService>();
+ m.put(IDebugCounterService.class, this);
+ return m;
+ }
+
+ @Override
+ public Collection<Class<? extends IFloodlightService>>
+ getModuleDependencies() {
+ return null;
+ }
+
+ @Override
+ public void init(FloodlightModuleContext context)
+ throws FloodlightModuleException {
+
+ }
+
+ @Override
+ public void startUp(FloodlightModuleContext context) {
+
+ }
+
+
+ @Override
+ public void flushCounters() {
+
+ }
+
+ @Override
+ public void resetAllCounters() {
+
+ }
+
+ @Override
+ public void resetAllModuleCounters(String moduleName) {
+
+ }
+
+
+ @Override
+ public void resetCounterHierarchy(String moduleName, String counterHierarchy) {
+
+ }
+
+ @Override
+ public void enableCtrOnDemand(String moduleName, String counterHierarchy) {
+
+ }
+
+ @Override
+ public void disableCtrOnDemand(String moduleName, String counterHierarchy) {
+
+ }
+
+ @Override
+ public List<DebugCounterInfo> getCounterHierarchy(String moduleName,
+ String counterHierarchy) {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public List<DebugCounterInfo> getAllCounterValues() {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public List<DebugCounterInfo> getModuleCounterValues(String moduleName) {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public boolean containsModuleCounterHierarchy(String moduleName,
+ String counterHierarchy) {
+ return false;
+ }
+
+ @Override
+ public boolean containsModuleName(String moduleName) {
+ return false;
+ }
+
+ @Override
+ public
+ IDebugCounter
+ registerCounter(String moduleName, String counterHierarchy,
+ String counterDescription,
+ CounterType counterType, String... metaData)
+ throws MaxCountersRegistered {
+ return new NullCounterImpl();
+ }
+
+ @Override
+ public List<String> getModuleList() {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public List<String> getModuleCounterList(String moduleName) {
+ return Collections.emptyList();
+ }
+
+ public class NullCounterImpl implements IDebugCounter {
+
+ @Override
+ public void updateCounterWithFlush() {
+
+ }
+
+ @Override
+ public void updateCounterNoFlush() {
+
+ }
+
+ @Override
+ public void updateCounterWithFlush(int incr) {
+ }
+
+ @Override
+ public void updateCounterNoFlush(int incr) {
+
+ }
+
+ @Override
+ public long getCounterValue() {
+ return -1;
+ }
+
+ }
+
+}
diff --git a/src/main/java/net/floodlightcontroller/debugcounter/web/DebugCounterResource.java b/src/main/java/net/floodlightcontroller/debugcounter/web/DebugCounterResource.java
new file mode 100644
index 0000000..3dd35bc
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/debugcounter/web/DebugCounterResource.java
@@ -0,0 +1,382 @@
+package net.floodlightcontroller.debugcounter.web;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+
+import org.restlet.resource.Get;
+import org.restlet.resource.Post;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import net.floodlightcontroller.debugcounter.DebugCounter.DebugCounterInfo;
+import net.floodlightcontroller.debugcounter.IDebugCounterService.CounterType;
+
+/**
+ * Web interface for Debug Counters
+ *
+ * @author Saurav
+ */
+public class DebugCounterResource extends DebugCounterResourceBase {
+ protected static Logger logger =
+ LoggerFactory.getLogger(DebugCounterResource.class);
+
+ /**
+ * The output JSON model that contains the counter information
+ */
+ public class DebugCounterInfoOutput {
+ protected class DCInfo {
+ private final Long counterValue;
+ private final CounterType counterType;
+ private final String counterDesc;
+ private final boolean enabled;
+ private final String counterHierarchy;
+ private final String moduleName;
+ private final String[] metaData;
+
+ DCInfo(DebugCounterInfo dci) {
+ this.moduleName = dci.getCounterInfo().getModuleName();
+ this.counterHierarchy = dci.getCounterInfo().getCounterHierarchy();
+ this.counterDesc = dci.getCounterInfo().getCounterDesc();
+ this.metaData = dci.getCounterInfo().getMetaData();
+ this.enabled = dci.getCounterInfo().isEnabled();
+ this.counterType = dci.getCounterInfo().getCtype();
+ this.counterValue = dci.getCounterValue();
+ }
+
+ public Long getCounterValue() {
+ return counterValue;
+ }
+ public CounterType getCounterType() {
+ return counterType;
+ }
+
+ public String getCounterDesc() {
+ return counterDesc;
+ }
+
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ public String getCounterHierarchy() {
+ return counterHierarchy;
+ }
+
+ public String getModuleName() {
+ return moduleName;
+ }
+
+ public String[] getMetaData() {
+ return metaData;
+ }
+
+ }
+ // complete counter information - null if only names are requested or
+ // if an error occurs
+ public Map<String, DCInfo> counterMap;
+ // list of names could be just moduleNames or counter hierarchical names
+ // for a specific module
+ public List<String> names;
+
+ public String error;
+
+ DebugCounterInfoOutput(boolean getList) {
+ if (!getList) {
+ counterMap = new HashMap<String, DCInfo>();
+ }
+ error = null;
+ }
+ public Map<String, DCInfo> getCounterMap() {
+ return counterMap;
+ }
+
+ public String getError() {
+ return error;
+ }
+
+ public List<String> getNames() {
+ return names;
+ }
+
+ }
+
+ public enum Option {
+ ALL, ONE_MODULE, MODULE_COUNTER_HIERARCHY, ERROR_BAD_MODULE_NAME,
+ ERROR_BAD_PARAM,
+ ERROR_BAD_MODULE_COUNTER_NAME
+ }
+
+ public static class CounterPost {
+ public Boolean reset;
+ public Boolean enable;
+
+ public Boolean getReset() {
+ return reset;
+ }
+ public void setReset(Boolean reset) {
+ this.reset = reset;
+ }
+ public Boolean getEnable() {
+ return enable;
+ }
+ public void setEnable(Boolean enable) {
+ this.enable = enable;
+ }
+ }
+
+ public static class ResetOutput {
+ String error = null;
+
+ public String getError() {
+ return error;
+ }
+ public void setError(String error) {
+ this.error = error;
+ }
+ }
+
+ /**
+ * Reset or enable/disable counters
+ *
+ * If using curl:
+ * curl -X POST -d DATA -H "Content-Type: application/json" URL
+ * where DATA must be one of the following:
+ * {\"reset\":true} to reset counters
+ * {\"enable\":true} to enable counter
+ * {\"enable\":false} to disable counter
+ * and URL must be in one of the following forms:
+ * "http://{controller-hostname}:8080/wm/debugcounter/{param1}/{param2}/{param3}/{param4}
+ *
+ * {param1} can be null, 'all' or the name of a module {moduleName}.
+ * {param2}/{param3}/{param4} refer to hierarchical counter names.
+ *
+ * The Reset command will reset the counter specified as well as all counters
+ * in the hierarchical levels below. For example, if a counter hierarchy exists
+ * as switch/00:00:00:00:01:02:03:04/pktin/drops, then a reset command with just
+ * the moduleName (param1=switch) and counterHierarchy (param2=00:00:00:00:01:02:03:04)
+ * will reset all counters for that switch. Continuing the example -
+ * for a counterHierarchy (param2=00:00:00:00:01:02:03:04 and param3=pktin), the reset
+ * command will remove all pktin counters for that switch.
+ *
+ * The enable/disable command will ONLY disable a specific counter (and only if
+ * that counter is of CounterType.ON_DEMAND)
+ * It will not enable/disable counters at any other hierarchical level.
+ *
+ */
+ @Post
+ public ResetOutput postHandler(CounterPost postData) {
+ ResetOutput output = new ResetOutput();
+ Option choice = Option.ERROR_BAD_PARAM;
+ String param1 = (String)getRequestAttributes().get("param1");
+ String param2 = (String)getRequestAttributes().get("param2");
+ String param3 = (String)getRequestAttributes().get("param3");
+ String param4 = (String)getRequestAttributes().get("param4");
+ String moduleName = "";
+
+ if (param1 == null) {
+ moduleName = "all";
+ choice = Option.ALL;
+ } else if (param1.equals("all")) {
+ moduleName = "all";
+ choice = Option.ALL;
+ } else {
+ moduleName = param1;
+ }
+
+ String counterHierarchy = "";
+ if (param2 != null) {
+ counterHierarchy += param2;
+ if (param3 != null) {
+ counterHierarchy += "/" + param3;
+ if (param4 != null) {
+ counterHierarchy += "/" + param4;
+ }
+ }
+ }
+
+ if (!moduleName.equals("all") && counterHierarchy.equals("")) {
+ // only module name specified
+ boolean isRegistered = debugCounter.containsModuleName(param1);
+ if (isRegistered) {
+ choice = Option.ONE_MODULE;
+ } else {
+ choice = Option.ERROR_BAD_MODULE_NAME;
+ }
+ } else if (!moduleName.equals("all") && !counterHierarchy.equals("")) {
+ // both module and counter names specified
+ boolean isRegistered = debugCounter.
+ containsModuleCounterHierarchy(moduleName, counterHierarchy);
+ if (isRegistered) {
+ choice = Option.MODULE_COUNTER_HIERARCHY;
+ } else {
+ choice = Option.ERROR_BAD_MODULE_COUNTER_NAME;
+ }
+ }
+
+ boolean reset = false;
+ boolean turnOnOff = false;
+ if (postData.getReset() != null && postData.getReset()) {
+ reset = true;
+ }
+ if (postData.getEnable() != null) {
+ turnOnOff = true;
+ }
+
+ switch (choice) {
+ case ALL:
+ if (reset) debugCounter.resetAllCounters();
+ break;
+ case ONE_MODULE:
+ if (reset) debugCounter.resetAllModuleCounters(moduleName);
+ break;
+ case MODULE_COUNTER_HIERARCHY:
+ if (reset)
+ debugCounter.resetCounterHierarchy(moduleName, counterHierarchy);
+ else if (turnOnOff && postData.getEnable())
+ debugCounter.enableCtrOnDemand(moduleName, counterHierarchy);
+ else if (turnOnOff && !postData.getEnable())
+ debugCounter.disableCtrOnDemand(moduleName, counterHierarchy);
+ break;
+ case ERROR_BAD_MODULE_NAME:
+ output.error = "Module name has no corresponding registered counters";
+ break;
+ case ERROR_BAD_MODULE_COUNTER_NAME:
+ output.error = "Counter not registered";
+ break;
+ case ERROR_BAD_PARAM:
+ output.error = "Bad param";
+ }
+
+ return output;
+ }
+
+ /**
+ * Return the debug counter data for the get rest-api call
+ *
+ * URI must be in one of the following forms:
+ * "http://{controller-hostname}:8080/wm/debugcounter/{param1}/{param2}/{param3}/{param4}"
+ *
+ * where {param1} must be one of (no quotes):
+ * null if nothing is given then by default all
+ * module names are returned for which counters
+ * have been registered
+ * "all" returns value/info on all counters.
+ * "{moduleName}" returns value/info on all counters for
+ * the specified module 'moduelName'.
+ * & {param2}/{param3}/{param4} refer to hierarchical counter names.
+ * eg. 00:00:00:00:01:02:03:04/pktin/drops
+ * where leaving out any of the params returns
+ * all counters in the hierarchical level below.
+ * So giving just the switch dpid will fetch
+ * all counters for that switch.
+ * A special case => if param2 is null, then
+ * all hierarchical counterNames are returned
+ * for the given moduleName (in param1)
+ */
+ @Get
+ public DebugCounterInfoOutput handleCounterInfoQuery() {
+ DebugCounterInfoOutput output;
+ Option choice = Option.ERROR_BAD_PARAM;
+ String param1 = (String)getRequestAttributes().get("param1");
+ String param2 = (String)getRequestAttributes().get("param2");
+ String param3 = (String)getRequestAttributes().get("param3");
+ String param4 = (String)getRequestAttributes().get("param4");
+
+ if (param1 == null) {
+ output = new DebugCounterInfoOutput(true);
+ return listCounters(output);
+ } else if (param1.equals("all")) {
+ output = new DebugCounterInfoOutput(false);
+ populateCounters(debugCounter.getAllCounterValues(), output);
+ return output;
+ }
+
+ output = new DebugCounterInfoOutput(false);
+ String counterHierarchy = "";
+ if (param2 == null) {
+ // param2 is null -- return list of counternames for param1
+ boolean isRegistered = debugCounter.containsModuleName(param1);
+ output = new DebugCounterInfoOutput(true);
+ if (isRegistered) {
+ return listCounters(param1, output);
+ } else {
+ choice = Option.ERROR_BAD_MODULE_NAME;
+ }
+ } else if (param2.equals("all")) {
+ // get all counter info for a single module
+ boolean isRegistered = debugCounter.containsModuleName(param1);
+ if (isRegistered) {
+ choice = Option.ONE_MODULE;
+ } else {
+ choice = Option.ERROR_BAD_MODULE_NAME;
+ }
+ } else {
+ counterHierarchy += param2;
+ if (param3 != null) {
+ counterHierarchy += "/" + param3;
+ if (param4 != null) {
+ counterHierarchy += "/" + param4;
+ }
+ }
+ boolean isRegistered = debugCounter.
+ containsModuleCounterHierarchy(param1, counterHierarchy);
+ if (isRegistered) {
+ choice = Option.MODULE_COUNTER_HIERARCHY;
+ } else {
+ choice = Option.ERROR_BAD_MODULE_COUNTER_NAME;
+ }
+ }
+
+ switch (choice) {
+ case ONE_MODULE:
+ populateCounters(debugCounter.getModuleCounterValues(param1), output);
+ break;
+ case MODULE_COUNTER_HIERARCHY:
+ populateCounters(debugCounter.getCounterHierarchy(param1, counterHierarchy),
+ output);
+ break;
+ case ERROR_BAD_MODULE_NAME:
+ output.error = "Module name is not registered for debug-counters";
+ break;
+ case ERROR_BAD_MODULE_COUNTER_NAME:
+ output.error = "Counter not registered";
+ break;
+ case ERROR_BAD_PARAM:
+ default:
+ output.error = "Bad param";
+ }
+
+ return output;
+ }
+
+ private DebugCounterInfoOutput listCounters(String moduleName,
+ DebugCounterInfoOutput output) {
+ output.names = debugCounter.getModuleCounterList(moduleName);
+ return output;
+ }
+
+ private DebugCounterInfoOutput listCounters(DebugCounterInfoOutput output) {
+ output.names = debugCounter.getModuleList();
+ return output;
+ }
+
+ private void populateSingleCounter(DebugCounterInfo debugCounterInfo,
+ DebugCounterInfoOutput output) {
+ if (debugCounterInfo != null)
+ output.counterMap.put(debugCounterInfo.getCounterInfo().
+ getModuleCounterHierarchy(),
+ output.new DCInfo(debugCounterInfo));
+ }
+
+ private void populateCounters(List<DebugCounterInfo> counterValues,
+ DebugCounterInfoOutput output) {
+ for (DebugCounterInfo dci : counterValues) {
+ populateSingleCounter(dci, output);
+ }
+ }
+
+
+
+}
diff --git a/src/main/java/net/floodlightcontroller/debugcounter/web/DebugCounterResourceBase.java b/src/main/java/net/floodlightcontroller/debugcounter/web/DebugCounterResourceBase.java
new file mode 100644
index 0000000..48e469b
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/debugcounter/web/DebugCounterResourceBase.java
@@ -0,0 +1,18 @@
+package net.floodlightcontroller.debugcounter.web;
+
+import net.floodlightcontroller.debugcounter.IDebugCounterService;
+
+import org.restlet.resource.ResourceException;
+import org.restlet.resource.ServerResource;
+
+public class DebugCounterResourceBase extends ServerResource {
+
+ protected IDebugCounterService debugCounter;
+
+ @Override
+ protected void doInit() throws ResourceException {
+ super.doInit();
+ debugCounter = (IDebugCounterService)getContext().getAttributes().
+ get(IDebugCounterService.class.getCanonicalName());
+ }
+}
diff --git a/src/main/java/net/floodlightcontroller/debugcounter/web/DebugCounterRoutable.java b/src/main/java/net/floodlightcontroller/debugcounter/web/DebugCounterRoutable.java
new file mode 100644
index 0000000..55ba5ba
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/debugcounter/web/DebugCounterRoutable.java
@@ -0,0 +1,31 @@
+package net.floodlightcontroller.debugcounter.web;
+
+import org.restlet.Context;
+import org.restlet.Restlet;
+import org.restlet.routing.Router;
+
+import net.floodlightcontroller.restserver.RestletRoutable;
+
+public class DebugCounterRoutable implements RestletRoutable {
+
+ @Override
+ public String basePath() {
+ return "/wm/debugcounter";
+ }
+
+ @Override
+ public Restlet getRestlet(Context context) {
+ Router router = new Router(context);
+ router.attach("/{param1}/{param2}/{param3}/{param4}/", DebugCounterResource.class);
+ router.attach("/{param1}/{param2}/{param3}/{param4}", DebugCounterResource.class);
+ router.attach("/{param1}/{param2}/{param3}/", DebugCounterResource.class);
+ router.attach("/{param1}/{param2}/{param3}", DebugCounterResource.class);
+ router.attach("/{param1}/{param2}/", DebugCounterResource.class);
+ router.attach("/{param1}/{param2}", DebugCounterResource.class);
+ router.attach("/{param1}/", DebugCounterResource.class);
+ router.attach("/{param1}", DebugCounterResource.class);
+ router.attach("/", DebugCounterResource.class);
+ router.attach("", DebugCounterResource.class);
+ return router;
+ }
+}