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/util/EnumBitmaps.java b/src/main/java/net/floodlightcontroller/util/EnumBitmaps.java
new file mode 100644
index 0000000..a7503eb
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/util/EnumBitmaps.java
@@ -0,0 +1,141 @@
+package net.floodlightcontroller.util;
+
+import java.util.EnumSet;
+import java.util.Set;
+
+/**
+ * A utility class to convert between integer based bitmaps for (OpenFlow)
+ * flags and Enum and EnumSet based representations.
+ *
+ * The enum used to represent individual flags needs to implement the
+ * BitmapableEnum interface.
+ *
+ * Example:
+ * {@code
+ * int bitmap = 0x11; // OFPPC_PORT_DOWN | OFPPC_NO_STP
+ * EnumSet<OFPortConfig> s = toEnumSet(OFPortConfig.class, bitmap);
+ * // s will contain OFPPC_PORT_DOWN and OFPPC_NO_STP
+ * }
+ *
+ * {@code
+ * EnumSet<OFPortConfig> s = EnumSet.of(OFPPC_NO_STP, OFPPC_PORT_DOWN);
+ * int bitmap = toBitmap(s); // returns 0x11
+ * }
+ * @author gregor
+ *
+ */
+public class EnumBitmaps {
+ /**
+ * Enums used to represent individual flags needs to implement this
+ * interface
+ */
+ public interface BitmapableEnum {
+ /** Return the value in the bitmap that the enum constant represents.
+ * The returned value must have only a single bit set. E.g.,1<<3
+ */
+ int getValue();
+ }
+
+ /**
+ * Convert an integer bitmap to an EnumSet.
+ *
+ * See class description for example
+ * @param type The Enum class to use. Must implement BitmapableEnum
+ * @param bitmap The integer bitmap
+ * @return A newly allocated EnumSet representing the bits set in the
+ * bitmap
+ * @throws NullPointerException if type is null
+ * @throws IllegalArgumentException if any enum constant from type has
+ * more than one bit set.
+ * @throws IllegalArgumentException if the bitmap has any bits set not
+ * represented by an enum constant.
+ */
+ public static <E extends Enum<E> & BitmapableEnum>
+ EnumSet<E> toEnumSet(Class<E> type, int bitmap) {
+ if (type == null)
+ throw new NullPointerException("Given enum type must not be null");
+ EnumSet<E> s = EnumSet.noneOf(type);
+ // allSetBitmap will eventually have all valid bits for the given
+ // type set.
+ int allSetBitmap = 0;
+ for (E element: type.getEnumConstants()) {
+ if (Integer.bitCount(element.getValue()) != 1) {
+ String msg = String.format("The %s (%x) constant of the " +
+ "enum %s is supposed to represent a bitmap entry but " +
+ "has more than one bit set.",
+ element.toString(), element.getValue(), type.getName());
+ throw new IllegalArgumentException(msg);
+ }
+ allSetBitmap |= element.getValue();
+ if ((bitmap & element.getValue()) != 0)
+ s.add(element);
+ }
+ if (((~allSetBitmap) & bitmap) != 0) {
+ // check if only valid flags are set in the given bitmap
+ String msg = String.format("The bitmap %x for enum %s has " +
+ "bits set that are presented by any enum constant",
+ bitmap, type.getName());
+ throw new IllegalArgumentException(msg);
+ }
+ return s;
+ }
+
+ /**
+ * Return the bitmap mask with all possible bits set. E.g., If a bitmap
+ * has the individual flags 0x1, 0x2, and 0x8 (note the missing 0x4) then
+ * the mask will be 0xb (1011 binary)
+ *
+ * @param type The Enum class to use. Must implement BitmapableEnum
+ * @throws NullPointerException if type is null
+ * @throws IllegalArgumentException if any enum constant from type has
+ * more than one bit set
+ * @return an integer with all possible bits for the given bitmap enum
+ * type set.
+ */
+ public static <E extends Enum<E> & BitmapableEnum>
+ int getMask(Class<E> type) {
+ if (type == null)
+ throw new NullPointerException("Given enum type must not be null");
+ // allSetBitmap will eventually have all valid bits for the given
+ // type set.
+ int allSetBitmap = 0;
+ for (E element: type.getEnumConstants()) {
+ if (Integer.bitCount(element.getValue()) != 1) {
+ String msg = String.format("The %s (%x) constant of the " +
+ "enum %s is supposed to represent a bitmap entry but " +
+ "has more than one bit set.",
+ element.toString(), element.getValue(), type.getName());
+ throw new IllegalArgumentException(msg);
+ }
+ allSetBitmap |= element.getValue();
+ }
+ return allSetBitmap;
+ }
+
+ /**
+ * Convert the given EnumSet to the integer bitmap representation
+ * @param set The EnumSet to convert. The enum must implement
+ * BitmapableEnum
+ * @return the integer bitmap
+ * @throws IllegalArgumentException if an enum constant from the set (!) has
+ * more than one bit set
+ * @throws NullPointerException if the set is null
+ */
+ public static <E extends Enum<E> & BitmapableEnum>
+ int toBitmap(Set<E> set) {
+ if (set == null)
+ throw new NullPointerException("Given set must not be null");
+ int bitmap = 0;
+ for (E element: set) {
+ if (Integer.bitCount(element.getValue()) != 1) {
+ String msg = String.format("The %s (%x) constant in the set " +
+ "is supposed to represent a bitmap entry but " +
+ "has more than one bit set.",
+ element.toString(), element.getValue());
+ throw new IllegalArgumentException(msg);
+ }
+ bitmap |= element.getValue();
+ }
+ return bitmap;
+ }
+}
diff --git a/src/main/java/net/floodlightcontroller/util/LinkedHashSetWrapper.java b/src/main/java/net/floodlightcontroller/util/LinkedHashSetWrapper.java
new file mode 100644
index 0000000..65865c0
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/util/LinkedHashSetWrapper.java
@@ -0,0 +1,33 @@
+package net.floodlightcontroller.util;
+
+import java.util.Collection;
+import java.util.LinkedHashSet;
+
+import com.google.common.collect.ForwardingCollection;
+
+/**
+ * A simple wrapper / forwarder that forwards all calls to a LinkedHashSet.
+ * This wrappers sole reason for existence is to implement the
+ * OrderedCollection marker interface.
+ * @author gregor
+ *
+ */
+public class LinkedHashSetWrapper<E>
+ extends ForwardingCollection<E> implements OrderedCollection<E> {
+ private final Collection<E> delegate;
+
+ public LinkedHashSetWrapper() {
+ super();
+ this.delegate = new LinkedHashSet<E>();
+ }
+
+ public LinkedHashSetWrapper(Collection<? extends E> c) {
+ super();
+ this.delegate = new LinkedHashSet<E>(c);
+ }
+
+ @Override
+ protected Collection<E> delegate() {
+ return this.delegate;
+ }
+}
diff --git a/src/main/java/net/floodlightcontroller/util/LoadMonitor.java b/src/main/java/net/floodlightcontroller/util/LoadMonitor.java
new file mode 100644
index 0000000..5b234cd
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/util/LoadMonitor.java
@@ -0,0 +1,275 @@
+/**
+ * Copyright 2013, Big Switch Networks, Inc.
+ *
+ * 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.util;
+
+import java.io.BufferedReader;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+
+import org.slf4j.Logger;
+
+import net.floodlightcontroller.core.annotations.LogMessageDocs;
+import net.floodlightcontroller.core.annotations.LogMessageDoc;
+
+public class LoadMonitor implements Runnable {
+
+ public enum LoadLevel {
+ OK,
+ HIGH,
+ VERYHIGH,
+ }
+
+ public LoadLevel getLoadLevel() {
+ return loadlevel ;
+ }
+
+ public double getLoad() {
+ return load ;
+ }
+
+ public static final int LOADMONITOR_SAMPLING_INTERVAL = 1000; // mili-sec
+ public static final double THRESHOLD_HIGH = 0.90;
+ public static final double THRESHOLD_VERYHIGH = 0.95;
+ public static final int MAX_LOADED_ITERATIONS = 5;
+ public static final int MAX_LOAD_HISTORY = 5;
+
+ protected volatile double load;
+ protected volatile LoadLevel loadlevel;
+ protected int itersLoaded;
+
+ protected boolean isLinux;
+ protected int numcores;
+ protected int jiffyNanos;
+ protected long[] lastNanos;
+ protected long[] lastIdle;
+ protected Logger log;
+
+ public LoadMonitor(Logger log_) {
+ log = log_;
+ loadlevel = LoadLevel.OK;
+ load = 0.0;
+ itersLoaded = 0;
+
+ lastNanos = new long[MAX_LOAD_HISTORY];
+ lastIdle = new long[MAX_LOAD_HISTORY];
+ for (int i=0 ; i<MAX_LOAD_HISTORY ; i++) {
+ lastNanos[i] = 0L;
+ lastIdle[i] = 0L;
+ }
+
+ isLinux = System.getProperty("os.name").equals("Linux");
+ numcores = 1;
+ jiffyNanos = 10 * 1000 * 1000;
+ if (isLinux) {
+ try {
+ numcores = Integer.parseInt(
+ this.runcmd("/usr/bin/nproc"));
+ jiffyNanos = (1000 * 1000 * 1000) / Integer.parseInt(
+ this.runcmd("/usr/bin/getconf CLK_TCK"));
+ }
+ catch (NumberFormatException ex) {
+ if (log != null) {
+ // Log message documented on runcmd function
+ log.error("Exception in inializing load monitor ", ex);
+ }
+ else {
+ ex.printStackTrace();
+ }
+ }
+ }
+ }
+
+ @Override
+ @LogMessageDocs({
+ @LogMessageDoc(
+ message="System under very heavy load, dropping some packet-ins",
+ explanation="We detcted that the system was under very heavy" +
+ " load, dropping some packet-ins temporarily"),
+ @LogMessageDoc(
+ message="System under heavy load, dropping some new flows",
+ explanation="We detcted that the system was under heavy load," +
+ " dropping some new flows temporarily")
+ })
+ public void run() {
+ if (!isLinux) return;
+
+ long currNanos = System.nanoTime();
+ long currIdle = this.readIdle();
+ for (int i=0 ; i < (MAX_LOAD_HISTORY - 1) ; i++) {
+ lastNanos[i] = lastNanos[i+1];
+ lastIdle[i] = lastIdle[i+1];
+ }
+ lastNanos[MAX_LOAD_HISTORY - 1] = currNanos;
+ lastIdle[MAX_LOAD_HISTORY - 1] = currIdle;
+
+ if (itersLoaded >= MAX_LOADED_ITERATIONS) {
+ loadlevel = LoadLevel.OK;
+ itersLoaded = 0;
+ return;
+ }
+
+ long nanos = lastNanos[MAX_LOAD_HISTORY - 1] - lastNanos[0];
+ long idle = lastIdle[MAX_LOAD_HISTORY - 1] - lastIdle[0];
+ load =
+ 1.0 - ((double)(idle * jiffyNanos) / (double)(nanos * numcores));
+
+ if (load > THRESHOLD_VERYHIGH) {
+ loadlevel = LoadLevel.VERYHIGH;
+ itersLoaded += 1;
+ String msg = "System under very heavy load, dropping packet-ins.";
+
+ if (log != null) {
+ log.error(msg);
+ }
+ else {
+ System.out.println(msg);
+ }
+ return;
+ }
+
+ if (load > THRESHOLD_HIGH) {
+ loadlevel = LoadLevel.HIGH;
+ itersLoaded += 1;
+ String msg = "System under heavy load, dropping new flows.";
+
+ if (log != null) {
+ log.error(msg);
+ }
+ else {
+ System.out.println(msg);
+ }
+ return;
+ }
+
+ loadlevel = LoadLevel.OK;
+ itersLoaded = 0;
+ return;
+ }
+
+ @LogMessageDoc(
+ message="Exception in reading load monitor params, using defaults",
+ explanation="There was an error in inializing load monitor's props," +
+ " using default parameters")
+ protected String runcmd(String cmd) {
+ String line;
+ StringBuilder ret = new StringBuilder();
+ try {
+ Process p = Runtime.getRuntime().exec(cmd);
+ BufferedReader input =
+ new BufferedReader(
+ new InputStreamReader(p.getInputStream()));
+ while ((line = input.readLine()) != null) {
+ ret.append(line);
+ }
+ input.close();
+ p.waitFor();
+ }
+ catch (InterruptedException ex) {
+ if (log != null) {
+ log.error("Exception in inializing load monitor ", ex);
+ }
+ else {
+ ex.printStackTrace();
+ }
+ }
+ catch (IOException ex) {
+ if (log != null) {
+ log.error("Exception in inializing load monitor ", ex);
+ }
+ else {
+ ex.printStackTrace();
+ }
+ }
+ return ret.toString();
+
+ }
+
+ protected long readIdle() {
+ long idle = 0;
+ FileInputStream fs = null;
+ BufferedReader reader = null;
+ try {
+ try {
+ fs = new FileInputStream("/proc/stat");
+ reader = new BufferedReader(new InputStreamReader(fs));
+ String line = reader.readLine();
+ if (line == null) throw new IOException("Empty file");
+ idle = Long.parseLong(line.split("\\s+")[4]);
+ } finally {
+ if (reader != null)
+ reader.close();
+ if (fs != null)
+ fs.close();
+ }
+ } catch (IOException ex) {
+ log.error("Error reading idle time from /proc/stat", ex);
+ }
+ return idle;
+
+ }
+
+ public ScheduledFuture<?> startMonitoring(ScheduledExecutorService ses)
+ {
+ ScheduledFuture<?> monitorTask =
+ ses.scheduleAtFixedRate(
+ this, 0,
+ LOADMONITOR_SAMPLING_INTERVAL, TimeUnit.MILLISECONDS);
+ return monitorTask;
+ }
+
+ /*
+ * For testing
+ */
+ public ScheduledFuture<?> printMonitoring(ScheduledExecutorService ses)
+ {
+ final LoadMonitor mon = this;
+ ScheduledFuture<?> monitorTask =
+ ses.scheduleAtFixedRate(
+ new Runnable() {
+ public void run() {
+ System.out.println(mon.getLoad());
+ }
+ }, LOADMONITOR_SAMPLING_INTERVAL/2,
+ LOADMONITOR_SAMPLING_INTERVAL, TimeUnit.MILLISECONDS);
+ return monitorTask;
+ }
+
+ public static void main(String[] args) {
+ final LoadMonitor monitor = new LoadMonitor(null);
+ final ScheduledExecutorService scheduler =
+ Executors.newScheduledThreadPool(1);
+ final ScheduledFuture<?> monitorTask =
+ monitor.startMonitoring(scheduler);
+ final ScheduledFuture<?> printTask =
+ monitor.printMonitoring(scheduler);
+
+ // Run the tasks for 2 minutes
+ scheduler.schedule(
+ new Runnable() {
+ public void run() {
+ monitorTask.cancel(true);
+ printTask.cancel(true);
+ }
+ }, 5*60, TimeUnit.SECONDS);
+ }
+
+}
diff --git a/src/main/java/net/floodlightcontroller/util/OFMessageDamper.java b/src/main/java/net/floodlightcontroller/util/OFMessageDamper.java
index 5faf38e..90bee76 100644
--- a/src/main/java/net/floodlightcontroller/util/OFMessageDamper.java
+++ b/src/main/java/net/floodlightcontroller/util/OFMessageDamper.java
@@ -133,7 +133,8 @@
FloodlightContext cntx, boolean flush)
throws IOException {
if (!msgTypesToCache.contains(msg.getType())) {
- sw.write(msg, cntx);
+ // XXX S commenting out old message writes
+ //sw.write(msg, cntx);
if (flush) {
sw.flush();
}
@@ -145,7 +146,8 @@
// entry exists in cache. Dampening.
return false;
} else {
- sw.write(msg, cntx);
+ // XXX S commenting out old message writes
+ // sw.write(msg, cntx);
if (flush) {
sw.flush();
}
diff --git a/src/main/java/net/floodlightcontroller/util/OrderedCollection.java b/src/main/java/net/floodlightcontroller/util/OrderedCollection.java
new file mode 100644
index 0000000..e302a72
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/util/OrderedCollection.java
@@ -0,0 +1,15 @@
+package net.floodlightcontroller.util;
+
+import java.util.Collection;
+
+/**
+ * A marker interface indicating that this Collection defines a particular
+ * iteration order. The details about the iteration order are specified by
+ * the concrete implementation.
+ * @author gregor
+ *
+ * @param <E>
+ */
+public interface OrderedCollection<E> extends Collection<E> {
+
+}