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/Event.java b/src/main/java/net/floodlightcontroller/debugevent/Event.java
new file mode 100644
index 0000000..b3e284e
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/debugevent/Event.java
@@ -0,0 +1,233 @@
+package net.floodlightcontroller.debugevent;
+
+import java.lang.ref.SoftReference;
+import java.lang.reflect.Field;
+import java.text.SimpleDateFormat;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import net.floodlightcontroller.debugevent.IDebugEventService.EventColumn;
+import net.onrc.onos.core.packet.IPv4;
+import net.onrc.onos.core.util.SwitchPort;
+
+import org.openflow.protocol.OFFlowMod;
+import org.openflow.util.HexString;
+
+public class Event {
+ long timestamp;
+ long threadId;
+ String threadName;
+ Object eventData;
+ private Map<String, String> returnMap;
+
+ public Event(long timestamp, long threadId, String threadName, Object eventData) {
+ super();
+ this.timestamp = timestamp;
+ this.threadId = threadId;
+ this.threadName = threadName;
+ this.eventData = eventData;
+ }
+
+ public long getTimestamp() {
+ return timestamp;
+ }
+
+ public void setTimestamp(long timestamp) {
+ this.timestamp = timestamp;
+ }
+
+ public long getThreadId() {
+ return threadId;
+ }
+
+ public void setThreadId(long threadId) {
+ this.threadId = threadId;
+ }
+
+ public String getThreadName() {
+ return threadName;
+ }
+
+ public void setThreadName(String threadName) {
+ this.threadName = threadName;
+ }
+
+ public Object geteventData() {
+ return eventData;
+ }
+
+ public void seteventData(Object eventData) {
+ this.eventData = eventData;
+ }
+
+ /**
+ * If an old event (eg. popped from a circular buffer) is being re-used for
+ * storing a new event, it is very important to clear the cached formatted
+ * event, so that the formatting can be redone with the new event data.
+ * Otherwise it will appear as if the circular buffer is not getting updated
+ * at all as old (cached) formatted event is delivered to the user.
+ */
+ public void nullifyCachedFormattedEvent() {
+ this.returnMap = null;
+ }
+
+ @Override
+ public String toString() {
+ return "Event [timestamp=" + timestamp + ", threadId=" + threadId
+ + ", eventData=" + eventData.toString() + "]";
+ }
+
+ public Map<String, String> getFormattedEvent(Class<?> eventClass, String moduleEventName) {
+ if (eventClass == null || !eventClass.equals(eventData.getClass())) {
+ returnMap = new HashMap<String, String>();
+ returnMap.put("Error", "null event data or event-class does not match event-data");
+ return returnMap;
+ }
+ // return cached value if there is one
+ if (returnMap != null)
+ return returnMap;
+
+ returnMap = new HashMap<String, String>();
+ returnMap.put("Timestamp", new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ")
+ .format(timestamp));
+ returnMap.put("Thread Id", String.valueOf(threadId));
+ returnMap.put("Thread Name", String.valueOf(threadName));
+ customFormat(eventClass, eventData, returnMap);
+ return returnMap;
+ }
+
+ private void customFormat(Class<?> clazz, Object eventData,
+ Map<String, String> retMap) {
+ for (Field f : clazz.getDeclaredFields()) {
+ EventColumn ec = f.getAnnotation(EventColumn.class);
+ if (ec == null) continue;
+ f.setAccessible(true);
+ try {
+ Object obj = f.get(eventData);
+
+ switch(ec.description()) {
+ case DPID:
+ retMap.put(ec.name(), HexString.toHexString((Long) obj));
+ break;
+ case MAC:
+ retMap.put(ec.name(), HexString.toHexString((Long) obj, 6));
+ break;
+ case IPv4:
+ retMap.put(ec.name(), net.onrc.onos.core.packet.IPv4.fromIPv4Address((Integer) obj));
+ break;
+ case FLOW_MOD_FLAGS:
+ int flags = (Integer)obj;
+ StringBuilder builder = new StringBuilder();
+ if (flags == 0) {
+ builder.append("None");
+ }
+ else {
+ if ((flags & OFFlowMod.OFPFF_SEND_FLOW_REM) != 0) {
+ builder.append("SEND_FLOW_REM ");
+ }
+ if ((flags & OFFlowMod.OFPFF_CHECK_OVERLAP) != 0) {
+ builder.append("CHECK_OVERLAP ");
+ }
+ if ((flags & OFFlowMod.OFPFF_EMERG) != 0) {
+ builder.append("EMERG ");
+ }
+ }
+ retMap.put(ec.name(), builder.toString());
+ break;
+ case LIST_IPV4:
+ @SuppressWarnings("unchecked")
+ List<Integer> ipv4Addresses = (List<Integer>)obj;
+ StringBuilder ipv4AddressesStr = new StringBuilder();
+ if (ipv4Addresses.size() == 0) {
+ ipv4AddressesStr.append("--");
+ } else {
+ for (Integer ipv4Addr : ipv4Addresses) {
+ ipv4AddressesStr.append(IPv4.fromIPv4Address(ipv4Addr.intValue()));
+ ipv4AddressesStr.append(" ");
+ }
+ }
+ retMap.put(ec.name(), ipv4AddressesStr.toString());
+ break;
+ case LIST_ATTACHMENT_POINT:
+ @SuppressWarnings("unchecked")
+ List<SwitchPort> aps = (List<SwitchPort>)obj;
+ StringBuilder apsStr = new StringBuilder();
+ if (aps.size() == 0) {
+ apsStr.append("--");
+ } else {
+ for (SwitchPort ap : aps) {
+ apsStr.append(HexString.toHexString(ap.dpid().value()));
+ apsStr.append("/");
+ apsStr.append(ap.port().value());
+ apsStr.append(" ");
+ }
+ }
+ retMap.put(ec.name(), apsStr.toString());
+ break;
+ case LIST_OBJECT:
+ @SuppressWarnings("unchecked")
+ List<Object> obl = (List<Object>)obj;
+ StringBuilder sbldr = new StringBuilder();
+ if (obl.size() == 0) {
+ sbldr.append("--");
+ } else {
+ for (Object o : obl) {
+ sbldr.append(o.toString());
+ sbldr.append(" ");
+ }
+ }
+ retMap.put(ec.name(), sbldr.toString());
+ break;
+ case SREF_LIST_OBJECT:
+ @SuppressWarnings("unchecked")
+ SoftReference<List<Object>> srefListObj =
+ (SoftReference<List<Object>>)obj;
+ List<Object> ol = srefListObj.get();
+ if (ol != null) {
+ StringBuilder sb = new StringBuilder();
+ if (ol.size() == 0) {
+ sb.append("--");
+ } else {
+ for (Object o : ol) {
+ sb.append(o.toString());
+ sb.append(" ");
+ }
+ }
+ retMap.put(ec.name(), sb.toString());
+ } else {
+ retMap.put(ec.name(), "-- reference not available --");
+ }
+ break;
+ case SREF_OBJECT:
+ @SuppressWarnings("unchecked")
+ SoftReference<Object> srefObj = (SoftReference<Object>)obj;
+ if (srefObj == null) {
+ retMap.put(ec.name(), "--");
+ } else {
+ Object o = srefObj.get();
+ if (o != null) {
+ retMap.put(ec.name(), o.toString());
+ } else {
+ retMap.put(ec.name(),
+ "-- reference not available --");
+ }
+ }
+ break;
+ case STRING:
+ case OBJECT:
+ case PRIMITIVE:
+ default:
+ retMap.put(ec.name(), obj.toString());
+ }
+ } catch (ClassCastException e) {
+ retMap.put("Error", e.getMessage());
+ } catch (IllegalArgumentException e) {
+ retMap.put("Error", e.getMessage());
+ } catch (IllegalAccessException e) {
+ retMap.put("Error", e.getMessage());
+ }
+ }
+ }
+
+}