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/debugevent/DebugEvent.java b/src/main/java/net/floodlightcontroller/debugevent/DebugEvent.java
new file mode 100644
index 0000000..e167b26
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/debugevent/DebugEvent.java
@@ -0,0 +1,506 @@
+package net.floodlightcontroller.debugevent;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+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.debugevent.web.DebugEventRoutable;
+import net.floodlightcontroller.restserver.IRestApiService;
+
+import com.google.common.collect.Sets;
+/**
+ * This class implements a central store for all events used for debugging the
+ * system. The basic idea is that given the functionality provided by this class,
+ * it should be unnecessary to resort to scraping through system DEBUG/TRACE logs
+ * to understand behavior in a running system.
+ *
+ * @author saurav
+ */
+public class DebugEvent implements IFloodlightModule, IDebugEventService {
+ protected static Logger log = LoggerFactory.getLogger(DebugEvent.class);
+
+ /**
+ * Every registered event type gets an event id, the value for which is obtained
+ * while holding the lock.
+ */
+ protected int eventIdCounter = 0;
+ protected Object eventIdLock = new Object();
+
+ private static final int PCT_LOCAL_CAP = 10; // % of global capacity
+ private static final int MIN_LOCAL_CAPACITY = 10; //elements
+
+ /**
+ * Event Information
+ */
+ public class EventInfo {
+ int eventId;
+ boolean enabled;
+ int bufferCapacity;
+ EventType etype;
+ String eventDesc;
+ String eventName;
+ String moduleName;
+ String moduleEventName;
+ Class<?> eventClass;
+ String[] metaData;
+
+ public EventInfo(int eventId, boolean enabled, int bufferCapacity,
+ EventType etype, Class<?> eventClass, String eventDesc,
+ String eventName, String moduleName, String... metaData) {
+ this.enabled = enabled;
+ this.eventId = eventId;
+ this.bufferCapacity = bufferCapacity;
+ this.etype = etype;
+ this.eventClass = eventClass;
+ this.eventDesc = eventDesc;
+ this.eventName = eventName;
+ this.moduleName = moduleName;
+ this.moduleEventName = moduleName + "/" + eventName;
+ this.metaData = metaData;
+ }
+
+ public int getEventId() { return eventId; }
+ public boolean isEnabled() { return enabled; }
+ public int getBufferCapacity() { return bufferCapacity; }
+ public EventType getEtype() { return etype; }
+ public String getEventDesc() { return eventDesc; }
+ public String getEventName() { return eventName; }
+ public String getModuleName() { return moduleName; }
+ public String getModuleEventName() { return moduleEventName; }
+ public String[] getMetaData() { return metaData; }
+ }
+
+ //******************
+ // Global stores
+ //******************
+
+ /**
+ * Event history for a particular event-id is stored in a circular buffer
+ */
+ protected class DebugEventHistory {
+ EventInfo einfo;
+ CircularBuffer<Event> eventBuffer;
+
+ public DebugEventHistory(EventInfo einfo, int capacity) {
+ this.einfo = einfo;
+ this.eventBuffer = new CircularBuffer<Event>(capacity);
+ }
+ }
+
+ /**
+ * Global storage for all event types and their corresponding event buffers.
+ * A particular event type is accessed by directly indexing into the array
+ * with the corresponding event-id.
+ */
+ protected DebugEventHistory[] allEvents =
+ new DebugEventHistory[MAX_EVENTS];
+
+ /**
+ * Global storage for all event ids registered for a module. The map is indexed
+ * by the module name and event name and returns the event-ids that correspond to the
+ * event types registered by that module (for example module 'linkdiscovery'
+ * may register events that have ids 0 and 1 that correspond to link up/down
+ * events, and receiving malformed LLDP packets, respectively).
+ */
+ protected ConcurrentHashMap<String, ConcurrentHashMap<String, Integer>>
+ moduleEvents = new ConcurrentHashMap<String,
+ ConcurrentHashMap<String, Integer>>();
+
+ /**
+ * A collection of event ids that are currently enabled for logging
+ */
+ protected Set<Integer> currentEvents = Collections.newSetFromMap(
+ new ConcurrentHashMap<Integer,Boolean>());
+
+ //******************
+ // Thread local stores
+ //******************
+
+ /**
+ * Thread local storage for events
+ */
+ protected class LocalEventHistory {
+ int nextIndex;
+ int maxCapacity;
+ boolean enabled;
+ ArrayList<Event> eventList;
+
+ public LocalEventHistory(boolean enabled, int maxCapacity) {
+ this.nextIndex = 0;
+ this.maxCapacity = maxCapacity;
+ this.enabled = enabled;
+ this.eventList = new ArrayList<Event>();
+ }
+ }
+
+ /**
+ * Thread local event buffers used for maintaining event history local to
+ * a thread. Eventually this locally maintained information is flushed
+ * into the global event buffers.
+ */
+ protected final ThreadLocal<LocalEventHistory[]> threadlocalEvents =
+ new ThreadLocal<LocalEventHistory[]>() {
+ @Override
+ protected LocalEventHistory[] initialValue() {
+ return new LocalEventHistory[MAX_EVENTS];
+ }
+ };
+
+ /**
+ * Thread local cache for event-ids that are currently active.
+ */
+ protected final ThreadLocal<Set<Integer>> threadlocalCurrentEvents =
+ new ThreadLocal<Set<Integer>>() {
+ @Override
+ protected Set<Integer> initialValue() {
+ return new HashSet<Integer>();
+ }
+ };
+
+ //*******************************
+ // IEventUpdater
+ //*******************************
+
+ protected class EventUpdaterImpl<T> implements IEventUpdater<T> {
+ private final int eventId;
+
+ public EventUpdaterImpl(int evId) {
+ this.eventId = evId;
+ }
+
+ @Override
+ public void updateEventNoFlush(Object event) {
+ if (!validEventId()) return;
+ updateEvent(eventId, false, event);
+ }
+
+ @Override
+ public void updateEventWithFlush(Object event) {
+ if (!validEventId()) return;
+ updateEvent(eventId, true, event);
+ }
+
+ private boolean validEventId() {
+ if (eventId < 0 || eventId >= MAX_EVENTS) {
+ throw new IllegalStateException();
+ }
+ return true;
+ }
+
+ }
+
+ //*******************************
+ // IDebugEventService
+ //*******************************
+
+ @Override
+ public <T> IEventUpdater<T> registerEvent(String moduleName, String eventName,
+ String eventDescription, EventType et,
+ Class<T> eventClass, int bufferCapacity,
+ String... metaData) throws MaxEventsRegistered {
+ int eventId = -1;
+ synchronized (eventIdLock) {
+ eventId = Integer.valueOf(eventIdCounter++);
+ }
+ if (eventId > MAX_EVENTS-1) {
+ throw new MaxEventsRegistered();
+ }
+
+ // register event id for moduleName
+ if (!moduleEvents.containsKey(moduleName)) {
+ moduleEvents.put(moduleName, new ConcurrentHashMap<String, Integer>());
+ }
+ if (!moduleEvents.get(moduleName).containsKey(eventName)) {
+ moduleEvents.get(moduleName).put(eventName, new Integer(eventId));
+ } else {
+ int existingEventId = moduleEvents.get(moduleName).get(eventName);
+ log.error("Duplicate event registration for moduleName {} eventName {}",
+ moduleName, eventName);
+ return new EventUpdaterImpl<T>(existingEventId);
+ }
+
+ // create storage for event-type
+ boolean enabled = (et == EventType.ALWAYS_LOG) ? true : false;
+ EventInfo ei = new EventInfo(eventId, enabled, bufferCapacity,
+ et, eventClass, eventDescription, eventName,
+ moduleName, metaData);
+ allEvents[eventId] = new DebugEventHistory(ei, bufferCapacity);
+ if (enabled) {
+ currentEvents.add(eventId);
+ }
+
+ return new EventUpdaterImpl<T>(eventId);
+ }
+
+ private void updateEvent(int eventId, boolean flushNow, Object eventData) {
+ if (eventId < 0 || eventId > MAX_EVENTS-1) return;
+
+ LocalEventHistory[] thishist = this.threadlocalEvents.get();
+ if (thishist[eventId] == null) {
+ // seeing this event for the first time in this thread - create local
+ // store by consulting global store
+ DebugEventHistory de = allEvents[eventId];
+ if (de != null) {
+ boolean enabled = de.einfo.enabled;
+ int localCapacity = de.einfo.bufferCapacity * PCT_LOCAL_CAP/ 100;
+ if (localCapacity < 10) localCapacity = MIN_LOCAL_CAPACITY;
+ thishist[eventId] = new LocalEventHistory(enabled, localCapacity);
+ if (enabled) {
+ Set<Integer> thisset = this.threadlocalCurrentEvents.get();
+ thisset.add(eventId);
+ }
+ } else {
+ log.error("updateEvent seen locally for event {} but no global"
+ + "storage exists for it yet .. not updating", eventId);
+ return;
+ }
+ }
+
+ // update local store if enabled locally for updating
+ LocalEventHistory le = thishist[eventId];
+ if (le.enabled) {
+ long timestamp = System.currentTimeMillis();
+ long thisthread = Thread.currentThread().getId();
+ String thisthreadname = Thread.currentThread().getName();
+ if (le.nextIndex < le.eventList.size()) {
+ if (le.eventList.get(le.nextIndex) == null) {
+ le.eventList.set(le.nextIndex, new Event(timestamp, thisthread,
+ thisthreadname,
+ eventData));
+ } else {
+ Event e = le.eventList.get(le.nextIndex);
+ e.timestamp = timestamp;
+ e.threadId = thisthread;
+ e.threadName = thisthreadname;
+ e.eventData = eventData;
+ e.nullifyCachedFormattedEvent();
+ }
+ } else {
+ le.eventList.add(new Event(timestamp, thisthread, thisthreadname, eventData));
+ }
+ le.nextIndex++;
+
+ if (le.nextIndex >= le.maxCapacity || flushNow) {
+ // flush this buffer now
+ DebugEventHistory de = allEvents[eventId];
+ if (de.einfo.enabled) {
+ le.eventList = de.eventBuffer.addAll(le.eventList, le.nextIndex);
+ } else {
+ // global buffer is disabled - don't flush, disable locally
+ le.enabled = false;
+ Set<Integer> thisset = this.threadlocalCurrentEvents.get();
+ thisset.remove(eventId);
+ }
+ le.nextIndex = 0;
+ }
+ }
+ }
+
+ @Override
+ public void flushEvents() {
+ LocalEventHistory[] thishist = this.threadlocalEvents.get();
+ Set<Integer> thisset = this.threadlocalCurrentEvents.get();
+ ArrayList<Integer> temp = new ArrayList<Integer>();
+
+ for (int eventId : thisset) {
+ LocalEventHistory le = thishist[eventId];
+ if (le != null && le.nextIndex > 0) {
+ // flush this buffer now
+ DebugEventHistory de = allEvents[eventId];
+ if (de.einfo.enabled) {
+ le.eventList = de.eventBuffer.addAll(le.eventList, le.nextIndex);
+ } else {
+ // global buffer is disabled - don't flush, disable locally
+ le.enabled = false;
+ temp.add(eventId);
+ }
+ le.nextIndex = 0;
+ }
+ }
+ for (int eId : temp)
+ thisset.remove(eId);
+
+ // sync thread local currently enabled set of eventIds with global set.
+ Sets.SetView<Integer> sv = Sets.difference(currentEvents, thisset);
+ for (int eventId : sv) {
+ if (thishist[eventId] != null) {
+ thishist[eventId].enabled = true;
+ thisset.add(eventId);
+ }
+ }
+
+ }
+
+ @Override
+ public boolean containsModuleEventName(String moduleName, String eventName) {
+ if (!moduleEvents.containsKey(moduleName)) return false;
+ if (moduleEvents.get(moduleName).containsKey(eventName)) return true;
+ return false;
+ }
+
+ @Override
+ public boolean containsModuleName(String moduleName) {
+ return moduleEvents.containsKey(moduleName);
+ }
+
+ @Override
+ public List<DebugEventInfo> getAllEventHistory() {
+ List<DebugEventInfo> moduleEventList = new ArrayList<DebugEventInfo>();
+ for (Map<String, Integer> modev : moduleEvents.values()) {
+ for (int eventId : modev.values()) {
+ DebugEventHistory de = allEvents[eventId];
+ if (de != null) {
+ List<Map<String,String>> ret = new ArrayList<Map<String,String>>();
+ for (Event e : de.eventBuffer) {
+ ret.add(e.getFormattedEvent(de.einfo.eventClass,
+ de.einfo.moduleEventName));
+ }
+ moduleEventList.add(new DebugEventInfo(de.einfo, ret));
+ }
+ }
+ }
+ return moduleEventList;
+ }
+
+ @Override
+ public List<DebugEventInfo> getModuleEventHistory(String moduleName) {
+ if (!moduleEvents.containsKey(moduleName)) return Collections.emptyList();
+ List<DebugEventInfo> moduleEventList = new ArrayList<DebugEventInfo>();
+ for (int eventId : moduleEvents.get(moduleName).values()) {
+ DebugEventHistory de = allEvents[eventId];
+ if (de != null) {
+ List<Map<String,String>> ret = new ArrayList<Map<String,String>>();
+ for (Event e : de.eventBuffer) {
+ ret.add(e.getFormattedEvent(de.einfo.eventClass,
+ de.einfo.moduleEventName));
+ }
+ moduleEventList.add(new DebugEventInfo(de.einfo, ret));
+ }
+ }
+ return moduleEventList;
+ }
+
+ @Override
+ public DebugEventInfo getSingleEventHistory(String moduleName, String eventName,
+ int last) {
+ if (!moduleEvents.containsKey(moduleName)) return null;
+ Integer eventId = moduleEvents.get(moduleName).get(eventName);
+ if (eventId == null) return null;
+ DebugEventHistory de = allEvents[eventId];
+ if (de != null) {
+ int num = 1;
+ List<Map<String,String>> ret = new ArrayList<Map<String,String>>();
+ for (Event e : de.eventBuffer) {
+ if (num > last)
+ break;
+ Map<String, String> temp = e.getFormattedEvent(de.einfo.eventClass,
+ de.einfo.moduleEventName);
+ temp.put("#", String.valueOf(num++));
+ ret.add(temp);
+ }
+ return new DebugEventInfo(de.einfo, ret);
+ }
+ return null;
+ }
+
+ @Override
+ public void resetAllEvents() {
+ for (Map<String, Integer> eventMap : moduleEvents.values()) {
+ for (Integer evId : eventMap.values()) {
+ allEvents[evId].eventBuffer.clear();
+ }
+ }
+ }
+
+ @Override
+ public void resetAllModuleEvents(String moduleName) {
+ if (!moduleEvents.containsKey(moduleName)) return;
+ Map<String, Integer> modEvents = moduleEvents.get(moduleName);
+ for (Integer evId : modEvents.values()) {
+ allEvents[evId].eventBuffer.clear();
+ }
+ }
+
+ @Override
+ public void resetSingleEvent(String moduleName, String eventName) {
+ if (!moduleEvents.containsKey(moduleName)) return;
+ Integer eventId = moduleEvents.get(moduleName).get(eventName);
+ if (eventId == null) return;
+ DebugEventHistory de = allEvents[eventId];
+ if (de != null) {
+ de.eventBuffer.clear();
+ }
+ }
+
+ @Override
+ public List<String> getModuleList() {
+ List<String> el = new ArrayList<String>();
+ el.addAll(moduleEvents.keySet());
+ return el;
+ }
+
+ @Override
+ public List<String> getModuleEventList(String moduleName) {
+ if (!moduleEvents.containsKey(moduleName))
+ return Collections.emptyList();
+ List<String> el = new ArrayList<String>();
+ el.addAll(moduleEvents.get(moduleName).keySet());
+ return el;
+ }
+
+ //*******************************
+ // IFloodlightModule
+ //*******************************
+
+ @Override
+ public Collection<Class<? extends IFloodlightService>> getModuleServices() {
+ Collection<Class<? extends IFloodlightService>> l =
+ new ArrayList<Class<? extends IFloodlightService>>();
+ l.add(IDebugEventService.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(IDebugEventService.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)
+ throws FloodlightModuleException {
+ IRestApiService restService =
+ context.getServiceImpl(IRestApiService.class);
+ restService.addRestletRoutable(new DebugEventRoutable());
+ DebugEventAppender.setDebugEventServiceImpl(this);
+ }
+
+}