diff --git a/build.xml b/build.xml index 19e6d34afd8c0067b77901e65187d754dace4e12..c94be94c1475fafe689bd3811b2e9fbfade75047 100644 --- a/build.xml +++ b/build.xml @@ -35,8 +35,6 @@ <property name="packetstreamer-gen" location="lib/gen-java" /> <property name="packetstreamer-gen-build" location="lib/gen-java-bin"/> <property name="packetstreamer-thrift-jar" value="packetstreamer-thrift.jar"/> - <property name="floodlight-nodeps-jar" location="${target}/floodlight-nodeps.jar"/> - <property name="floodlight-debian-jar" location="${target}/floodlight-debian.jar"/> <property name="floodlight-jar" location="${target}/floodlight.jar"/> <property name="floodlight-test-jar" location="${target}/floodlight-test.jar"/> @@ -58,15 +56,6 @@ <include name="jython-2.5.2.jar"/> <include name="libthrift-0.7.0.jar"/> </patternset> - <!-- Dependencies we can't get from Debian/Ubuntu. --> - <patternset id="debian-lib"> - <include name="org.restlet-2.1-RC1.jar"/> - <include name="org.restlet.ext.jackson-2.1-RC1.jar"/> - <include name="org.restlet.ext.simple-2.1-RC1.jar"/> - <include name="org.restlet.ext.slf4j-2.1-RC1.jar"/> - <include name="libthrift-0.7.0.jar"/> - <include name="simple-4.1.21.jar"/> - </patternset> <patternset id="genlib"> <include name="${packetstreamer-thrift-jar}"/> </patternset> @@ -212,7 +201,7 @@ <target name="coverage" depends="instrument,test,coverage-report"/> <target name="dist" depends="compile,compile-test"> - <jar destfile="${floodlight-nodeps-jar}" filesetmanifest="mergewithoutmain"> + <jar destfile="${floodlight-jar}" filesetmanifest="mergewithoutmain"> <manifest> <attribute name="Main-Class" value="${main-class}"/> <attribute name="Class-Path" value="."/> @@ -222,30 +211,12 @@ <fileset dir="${python-src}"> <include name="**/*.py"/> </fileset> - <zipgroupfileset dir="${target}/lib"> - <patternset refid="genlib"/> - </zipgroupfileset> - </jar> - <jar destfile="${floodlight-jar}"> - <manifest> - <attribute name="Main-Class" value="${main-class}"/> - <attribute name="Class-Path" value="."/> - </manifest> <zipgroupfileset dir="lib"> <patternset refid="lib"/> </zipgroupfileset> - <zipfileset src="${floodlight-nodeps-jar}"/> - </jar> - <jar destfile="${floodlight-debian-jar}"> - <manifest> - <attribute name="Main-Class" value="${main-class}"/> - <!-- JARs under /usr/share/java on Debian/Ubuntu --> - <attribute name="Class-Path" value="args4j.jar slf4j-api.jar logback-core.jar logback-classic.jar jackson-core-asl.jar jackson-mapper-asl.jar easymock.jar netty.jar concurrentlinkedhashmap-lru.jar jython.jar antlr3-runtime.jar asm3.jar asm3-tree.jar ."/> - </manifest> - <zipgroupfileset dir="lib"> - <patternset refid="debian-lib"/> + <zipgroupfileset dir="${target}/lib"> + <patternset refid="genlib"/> </zipgroupfileset> - <zipfileset src="${floodlight-nodeps-jar}"/> </jar> <jar destfile="${floodlight-test-jar}" filesetmanifest="mergewithoutmain"> <manifest> diff --git a/debian/.gitignore b/debian/.gitignore deleted file mode 100644 index 972bbfe651b922d9d33bcc9051a37ab33236252c..0000000000000000000000000000000000000000 --- a/debian/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -/files -/floodlight.debhelper.log -/floodlight.substvars -/floodlight - diff --git a/debian/README b/debian/README deleted file mode 100644 index 8583ef341267838fcc1ac25c84ab1a88a603aa3c..0000000000000000000000000000000000000000 --- a/debian/README +++ /dev/null @@ -1,6 +0,0 @@ -The Debian Package floodlight ----------------------------- - -Comments regarding the Package - - -- Rich Lane <rlane@bigswitch.com> Thu, 10 May 2012 23:02:25 -0700 diff --git a/debian/changelog b/debian/changelog deleted file mode 100644 index b3f5ace5775966ac692d3fb2c5de48e2c21963b5..0000000000000000000000000000000000000000 --- a/debian/changelog +++ /dev/null @@ -1,5 +0,0 @@ -floodlight (0.82) unstable; urgency=low - - * Initial Release. - - -- Rich Lane <rlane@bigswitch.com> Thu, 10 May 2012 23:02:25 -0700 diff --git a/debian/compat b/debian/compat deleted file mode 100644 index 45a4fb75db864000d01701c0f7a51864bd4daabf..0000000000000000000000000000000000000000 --- a/debian/compat +++ /dev/null @@ -1 +0,0 @@ -8 diff --git a/debian/control b/debian/control deleted file mode 100644 index b11688589e622c7655c5fc613f7a18f52f8bb2e3..0000000000000000000000000000000000000000 --- a/debian/control +++ /dev/null @@ -1,16 +0,0 @@ -Source: floodlight -Section: net -Priority: extra -Maintainer: Rich Lane <rlane@bigswitch.com> -Build-Depends: debhelper (>= 8.0.0), javahelper (>= 0.40ubuntu1), liblogback-java (>= 1.0.0-1), libjackson-json-java (>= 1.9.2-1), libeasymock-java (>= 2.4+ds1-6), libslf4j-java (>= 1.6.4-1), libnetty-java (>= 1:3.2.6.Final-2), libargs4j-java (>= 2.0.16-2), libconcurrentlinkedhashmap-java (>= 1.1~jdk5-1), jython (>= 2.5.1-2ubuntu2) -Standards-Version: 3.9.2 -Homepage: http://floodlight.openflowhub.org/ -Vcs-Git: git://github.com/floodlight/floodlight.git -Vcs-Browser: https://github.com/floodlight/floodlight - -Package: floodlight -Architecture: all -Depends: ${java:Depends}, ${misc:Depends} -Description: Java based OpenFlow controller - Floodlight is a Java based OpenFlow controller originally written by David - Erickson at Stanford University. diff --git a/debian/copyright b/debian/copyright deleted file mode 100644 index e139e18b52babbe34fc4fcef6b260f1e501844c2..0000000000000000000000000000000000000000 --- a/debian/copyright +++ /dev/null @@ -1,27 +0,0 @@ -Format: http://dep.debian.net/deps/dep5 -Upstream-Name: floodlight -Source: https://github.com/floodlight/floodlight - -Files: * -Copyright: 2011 Big Switch Networks <rlane@bigswitch.com> -License: Apache-2.0 - -Files: debian/* -Copyright: 2012 Big Switch Networks <rlane@bigswitch.com> -License: Apache-2.0 - -License: Apache-2.0 - 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. - . - On Debian systems, the complete text of the Apache version 2.0 license - can be found in "/usr/share/common-licenses/Apache-2.0". diff --git a/debian/docs b/debian/docs deleted file mode 100644 index 588b138a5aceaf1ecbaf91a1d08172598891cc11..0000000000000000000000000000000000000000 --- a/debian/docs +++ /dev/null @@ -1,2 +0,0 @@ -NOTICE.txt -README.txt diff --git a/debian/floodlight.1 b/debian/floodlight.1 deleted file mode 100644 index 80635073d7d13a860e182ac53853246ddeb9e188..0000000000000000000000000000000000000000 --- a/debian/floodlight.1 +++ /dev/null @@ -1,40 +0,0 @@ -.\" Hey, EMACS: -*- nroff -*- -.\" First parameter, NAME, should be all caps -.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection -.\" other parameters are allowed: see man(7), man(1) -.TH FLOODLIGHT 1 "May 14, 2012" -.\" Please adjust this date whenever revising the manpage. -.\" -.\" Some roff macros, for reference: -.\" .nh disable hyphenation -.\" .hy enable hyphenation -.\" .ad l left justify -.\" .ad b justify to both left and right margins -.\" .nf disable filling -.\" .fi enable filling -.\" .br insert line break -.\" .sp <n> insert n+1 empty lines -.\" for manpage-specific macros, see man(7) -.SH NAME -floodlight \- An Apache licensed, Java based OpenFlow controller -.SH SYNOPSIS -.B floodlight -.RI [ options ] -.SH DESCRIPTION -This manual page documents briefly the -.B floodlight -command. -.PP -.\" TeX users may be more comfortable with the \fB<whatever>\fP and -.\" \fI<whatever>\fP escape sequences to invode bold face and italics, -.\" respectively. -\fBfloodlight\fP is an Apache licensed, Java based OpenFlow controller. -.SH OPTIONS -.TP -.B \-cf, \-\-configFile FILE -Floodlight configuration file. -.SH AUTHOR -floodlight was written by David Erickson and Big Switch Networks. -.PP -This manual page was written by Rich Lane <rlane@bigswitch.com>, -for the Debian project (and may be used by others). diff --git a/debian/floodlight.install b/debian/floodlight.install deleted file mode 100644 index f21b0d6c1f05b46f0c81f2c497c5c56e84fac247..0000000000000000000000000000000000000000 --- a/debian/floodlight.install +++ /dev/null @@ -1,2 +0,0 @@ -target/floodlight-debian.jar usr/share/java -debian/misc/floodlight usr/bin diff --git a/debian/floodlight.manpages b/debian/floodlight.manpages deleted file mode 100644 index bec775d2741b6ae5db26abc89c80c7f41cd13f97..0000000000000000000000000000000000000000 --- a/debian/floodlight.manpages +++ /dev/null @@ -1 +0,0 @@ -debian/floodlight.1 diff --git a/debian/init.d b/debian/init.d deleted file mode 100644 index ecb447884107bb4a9fb40d5b0ff73b1c44f12329..0000000000000000000000000000000000000000 --- a/debian/init.d +++ /dev/null @@ -1,143 +0,0 @@ -#!/bin/sh -### BEGIN INIT INFO -# Provides: floodlight -# Required-Start: $network $local_fs $remote_fs -# Required-Stop: $remote_fs -# Default-Start: 2 3 4 5 -# Default-Stop: 0 1 6 -# Short-Description: An Apache licensed, Java based OpenFlow controller -# Description: An Apache licensed, Java based OpenFlow controller -### END INIT INFO - -# Author: Rich Lane <rlane@bigswitch.com> - -# PATH should only include /usr/* if it runs after the mountnfs.sh script -PATH=/sbin:/usr/sbin:/bin:/usr/bin -DESC=floodlight # Introduce a short description here -NAME=floodlight # Introduce the short server's name here -DAEMON=/usr/bin/floodlight # Introduce the server's location here -DAEMON_ARGS="" # Arguments to run the daemon with -PIDFILE=/var/run/$NAME.pid -SCRIPTNAME=/etc/init.d/$NAME - -# Exit if the package is not installed -[ -x $DAEMON ] || exit 0 - -# Read configuration variable file if it is present -[ -r /etc/default/$NAME ] && . /etc/default/$NAME - -# Load the VERBOSE setting and other rcS variables -. /lib/init/vars.sh - -# Define LSB log_* functions. -# Depend on lsb-base (>= 3.0-6) to ensure that this file is present. -. /lib/lsb/init-functions - -# -# Function that starts the daemon/service -# -do_start() -{ - # Return - # 0 if daemon has been started - # 1 if daemon was already running - # 2 if daemon could not be started - start-stop-daemon --start --quiet --pidfile $PIDFILE --startas $DAEMON --test > /dev/null \ - || return 1 - start-stop-daemon --start --quiet --pidfile $PIDFILE --startas $DAEMON -b -m -- \ - $DAEMON_ARGS \ - || return 2 - # Wait for the server to start. - while ! nc -z localhost 6633; do sleep 1; done -} - -# -# Function that stops the daemon/service -# -do_stop() -{ - # Return - # 0 if daemon has been stopped - # 1 if daemon was already stopped - # 2 if daemon could not be stopped - # other if a failure occurred - start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE - RETVAL="$?" - [ "$RETVAL" = 2 ] && return 2 - # Many daemons don't delete their pidfiles when they exit. - rm -f $PIDFILE - return "$RETVAL" -} - -# -# Function that sends a SIGHUP to the daemon/service -# -do_reload() { - # - # If the daemon can reload its configuration without - # restarting (for example, when it is sent a SIGHUP), - # then implement that here. - # - start-stop-daemon --stop --signal 1 --quiet --pidfile $PIDFILE - return 0 -} - -case "$1" in - start) - [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC " "$NAME" - do_start - case "$?" in - 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; - 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; - esac - ;; - stop) - [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME" - do_stop - case "$?" in - 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; - 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; - esac - ;; - status) - status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $? - ;; - #reload|force-reload) - # - # If do_reload() is not implemented then leave this commented out - # and leave 'force-reload' as an alias for 'restart'. - # - #log_daemon_msg "Reloading $DESC" "$NAME" - #do_reload - #log_end_msg $? - #;; - restart|force-reload) - # - # If the "reload" option is implemented then remove the - # 'force-reload' alias - # - log_daemon_msg "Restarting $DESC" "$NAME" - do_stop - case "$?" in - 0|1) - do_start - case "$?" in - 0) log_end_msg 0 ;; - 1) log_end_msg 1 ;; # Old process is still running - *) log_end_msg 1 ;; # Failed to start - esac - ;; - *) - # Failed to stop - log_end_msg 1 - ;; - esac - ;; - *) - #echo "Usage: $SCRIPTNAME {start|stop|restart|reload|force-reload}" >&2 - echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload}" >&2 - exit 3 - ;; -esac - -: diff --git a/debian/misc/floodlight b/debian/misc/floodlight deleted file mode 100755 index b98a261ac74afb002e4ee8649d5240e76a0114db..0000000000000000000000000000000000000000 --- a/debian/misc/floodlight +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/sh -exec java -jar /usr/share/java/floodlight-debian.jar "$@" diff --git a/debian/rules b/debian/rules deleted file mode 100755 index f35bb0c395caa40b3538262f854a0c49fce71652..0000000000000000000000000000000000000000 --- a/debian/rules +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/make -f -# -*- makefile -*- -# Sample debian/rules that uses debhelper. -# This file was originally written by Joey Hess and Craig Small. -# As a special exception, when this file is copied by dh-make into a -# dh-make output file, you may use that output file without restriction. -# This special exception was added by Craig Small in version 0.37 of dh-make. - -# Uncomment this to turn on verbose mode. -#export DH_VERBOSE=1 - -export JAVA_HOME=/usr/lib/jvm/default-java - -%: - dh $@ --with javahelper diff --git a/debian/source/format b/debian/source/format deleted file mode 100644 index 89ae9db8f88b823b6a7eabf55e203658739da122..0000000000000000000000000000000000000000 --- a/debian/source/format +++ /dev/null @@ -1 +0,0 @@ -3.0 (native) diff --git a/src/main/java/net/floodlightcontroller/core/internal/Controller.java b/src/main/java/net/floodlightcontroller/core/internal/Controller.java index 31f7e0613bd583f9d8fb9b36943765c78305704a..d65972029dc260bb919b783bbd95bb1a53e17861 100644 --- a/src/main/java/net/floodlightcontroller/core/internal/Controller.java +++ b/src/main/java/net/floodlightcontroller/core/internal/Controller.java @@ -166,6 +166,8 @@ public class Controller implements IFloodlightProviderService, // Configuration options protected int openFlowPort = 6633; protected int workerThreads = 0; + // The id for this controller node. Should be unique for each controller + // node in a controller cluster. protected String controllerId = "localhost"; // The current role of the controller. @@ -419,15 +421,42 @@ public class Controller implements IFloodlightProviderService, * @throws IOException */ protected void sendRoleRequest(IOFSwitch sw, Role role) throws IOException { + // There are three cases to consider: + // + // 1) If the controller role at the point the switch connected was + // null/disabled, then we never sent the role request probe to the + // switch and therefore never set the SWITCH_SUPPORTS_NX_ROLE + // attribute for the switch, so supportsNxRole is null. In that + // case since we're now enabling role support for the controller + // we should send out the role request probe/update to the switch. + // + // 2) If supportsNxRole == Boolean.TRUE then that means we've already + // sent the role request probe to the switch and it replied with + // a role reply message, so we know it supports role request + // messages. Now we're changing the role and we want to send + // it another role request message to inform it of the new role + // for the controller. + // + // 3) If supportsNxRole == Boolean.FALSE, then that means we sent the + // role request probe to the switch but it responded with an error + // indicating that it didn't understand the role request message. + // In that case we don't want to send it another role request that + // it (still) doesn't understand. But if the new role of the + // controller is SLAVE, then we don't want the switch to remain + // connected to this controller. It might support the older serial + // failover model for HA support, so we want to terminate the + // connection and get it to initiate a connection with another + // controller in its list of controllers. Eventually (hopefully, if + // things are configured correctly) it will walk down its list of + // controllers and connect to the current master controller. Boolean supportsNxRole = (Boolean) sw.getAttribute(IOFSwitch.SWITCH_SUPPORTS_NX_ROLE); - if ((supportsNxRole != null) && supportsNxRole) { + if ((supportsNxRole == null) || supportsNxRole) { + // Handle cases #1 and #2 sendNxRoleRequest(sw, role); - } else if (supportsNxRole != null && !supportsNxRole) { - // We know switch does not support role-request (and so sw.role is null) - // but we may have just switched roles from MASTER to SLAVE in which - // case we should disconnect switch - if (getRole() == Role.SLAVE && sw.getRole() == null) { + } else { + // Handle case #3 + if (getRole() == Role.SLAVE) { log.error("Disconnecting switch {} that doesn't support " + "role request messages from a controller that went to SLAVE mode"); // Closing the channel should result in a call to @@ -1712,7 +1741,11 @@ public class Controller implements IFloodlightProviderService, roleString = properties.getProperty("floodlight.role"); } catch (IOException exc) { - log.error("Error reading current role value from file: {}", rolePath); + // Don't treat it as an error if the file specified by the + // rolepath property doesn't exist. This lets us enable the + // HA mechanism by just creating/setting the floodlight.role + // property in that file without having to modify the + // floodlight properties. } } } @@ -1862,6 +1895,7 @@ public class Controller implements IFloodlightProviderService, storageSource.createTable(SWITCH_TABLE_NAME, null); storageSource.createTable(PORT_TABLE_NAME, null); storageSource.createTable(CONTROLLER_INTERFACE_TABLE_NAME, null); + storageSource.createTable(SWITCH_CONFIG_TABLE_NAME, null); storageSource.setTablePrimaryKeyName(CONTROLLER_TABLE_NAME, CONTROLLER_ID); storageSource.setTablePrimaryKeyName(SWITCH_TABLE_NAME, diff --git a/src/main/java/net/floodlightcontroller/core/web/RoleResource.java b/src/main/java/net/floodlightcontroller/core/web/ControllerRoleResource.java similarity index 72% rename from src/main/java/net/floodlightcontroller/core/web/RoleResource.java rename to src/main/java/net/floodlightcontroller/core/web/ControllerRoleResource.java index d0bf2f837e62be1d4b915a1f23b60eb36e66d92d..2a1520577c0ebbb581f57b8989e71539acccbaa0 100644 --- a/src/main/java/net/floodlightcontroller/core/web/RoleResource.java +++ b/src/main/java/net/floodlightcontroller/core/web/ControllerRoleResource.java @@ -11,33 +11,10 @@ import org.restlet.resource.Post; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class RoleResource extends ServerResource { +public class ControllerRoleResource extends ServerResource { - protected static Logger log = LoggerFactory.getLogger(RoleResource.class); + protected static Logger log = LoggerFactory.getLogger(ControllerRoleResource.class); - public static class RoleInfo { - protected String role; - - public RoleInfo() { - } - - public RoleInfo(String role) { - setRole(role); - } - - public RoleInfo(Role role) { - this.role = (role != null) ? role.name() : "DISABLED"; - } - - public String getRole() { - return role; - } - - public void setRole(String role) { - this.role = role; - } - } - @Get("json") public RoleInfo getRole() { IFloodlightProviderService floodlightProvider = diff --git a/src/main/java/net/floodlightcontroller/core/web/CoreWebRoutable.java b/src/main/java/net/floodlightcontroller/core/web/CoreWebRoutable.java index 66c462618ab10097892e680a8838d61a287bcde5..009681fb4693fcb83a3292d1babe319058cb3778 100644 --- a/src/main/java/net/floodlightcontroller/core/web/CoreWebRoutable.java +++ b/src/main/java/net/floodlightcontroller/core/web/CoreWebRoutable.java @@ -39,6 +39,7 @@ public class CoreWebRoutable implements RestletRoutable { Router router = new Router(context); router.attach("/module/all/json", ModuleLoaderResource.class); router.attach("/module/loaded/json", LoadedModuleLoaderResource.class); + router.attach("/switch/{switchId}/role/json", SwitchRoleResource.class); router.attach("/switch/all/{statType}/json", AllSwitchStatisticsResource.class); router.attach("/switch/{switchId}/{statType}/json", SwitchStatisticsResource.class); router.attach("/controller/switches/json", ControllerSwitchesResource.class); @@ -56,7 +57,7 @@ public class CoreWebRoutable implements RestletRoutable { EventHistoryTopologyClusterResource.class); router.attach("/storage/tables/json", StorageSourceTablesResource.class); router.attach("/controller/summary/json", ControllerSummaryResource.class); - router.attach("/role/json", RoleResource.class); + router.attach("/role/json", ControllerRoleResource.class); router.attach("/health/json", HealthCheckResource.class); return router; } diff --git a/src/main/java/net/floodlightcontroller/core/web/RoleInfo.java b/src/main/java/net/floodlightcontroller/core/web/RoleInfo.java new file mode 100644 index 0000000000000000000000000000000000000000..e600ea0eb02ada3d8003fb4c4e75b3e8b57f7b16 --- /dev/null +++ b/src/main/java/net/floodlightcontroller/core/web/RoleInfo.java @@ -0,0 +1,26 @@ +package net.floodlightcontroller.core.web; + +import net.floodlightcontroller.core.IFloodlightProviderService.Role; + +public class RoleInfo { + protected String role; + + public RoleInfo() { + } + + public RoleInfo(String role) { + setRole(role); + } + + public RoleInfo(Role role) { + this.role = (role != null) ? role.name() : "DISABLED"; + } + + public String getRole() { + return role; + } + + public void setRole(String role) { + this.role = role; + } +} \ No newline at end of file diff --git a/src/main/java/net/floodlightcontroller/core/web/SwitchRoleResource.java b/src/main/java/net/floodlightcontroller/core/web/SwitchRoleResource.java new file mode 100644 index 0000000000000000000000000000000000000000..0d73f93f12130c1df48fc995b1ccd50ad53ef82e --- /dev/null +++ b/src/main/java/net/floodlightcontroller/core/web/SwitchRoleResource.java @@ -0,0 +1,46 @@ +package net.floodlightcontroller.core.web; + +import java.util.HashMap; + +import org.openflow.util.HexString; +import org.restlet.resource.ServerResource; + +import net.floodlightcontroller.core.IFloodlightProviderService; +import net.floodlightcontroller.core.IOFSwitch; + +import org.restlet.resource.Get; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class SwitchRoleResource extends ServerResource { + + protected static Logger log = LoggerFactory.getLogger(SwitchRoleResource.class); + + @Get("json") + public Object getRole() { + IFloodlightProviderService floodlightProvider = + (IFloodlightProviderService)getContext().getAttributes(). + get(IFloodlightProviderService.class.getCanonicalName()); + + String switchId = (String) getRequestAttributes().get("switchId"); + + RoleInfo roleInfo; + + if (switchId.equalsIgnoreCase("all")) { + HashMap<String,RoleInfo> model = new HashMap<String,RoleInfo>(); + for (IOFSwitch sw: floodlightProvider.getSwitches().values()) { + switchId = sw.getStringId(); + roleInfo = new RoleInfo(sw.getRole()); + model.put(switchId, roleInfo); + } + return model; + } + + Long dpid = HexString.toLong(switchId); + IOFSwitch sw = floodlightProvider.getSwitches().get(dpid); + if (sw == null) + return null; + roleInfo = new RoleInfo(sw.getRole()); + return roleInfo; + } +} diff --git a/src/main/java/net/floodlightcontroller/devicemanager/internal/APBlockedException.java b/src/main/java/net/floodlightcontroller/devicemanager/internal/APBlockedException.java deleted file mode 100644 index eab0ebfc50199529880ed6728a6d6cc98955efa8..0000000000000000000000000000000000000000 --- a/src/main/java/net/floodlightcontroller/devicemanager/internal/APBlockedException.java +++ /dev/null @@ -1,44 +0,0 @@ -/** -* Copyright 2011, Big Switch Networks, Inc. -* Originally created by David Erickson, Stanford University -* -* Licensed under the Apache License, Version 2.0 (the "License"); you may -* not use this file except in compliance with the License. You may obtain -* a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -* License for the specific language governing permissions and limitations -* under the License. -**/ - -package net.floodlightcontroller.devicemanager.internal; - -/** - * Exception thrown when an attachment point is blocked - * @author readams - */ -public class APBlockedException extends Exception { - - private static final long serialVersionUID = 4554154651844610376L; - - public APBlockedException() { - - } - - public APBlockedException(String message) { - super(message); - } - - public APBlockedException(Throwable cause) { - super(cause); - } - - public APBlockedException(String message, Throwable cause) { - super(message, cause); - } - -} diff --git a/src/main/java/net/floodlightcontroller/devicemanager/internal/Device.java b/src/main/java/net/floodlightcontroller/devicemanager/internal/Device.java index 20448def33437f49bf4b3ea2be42462d81cf9c9a..57abf6c532b4e08b9bd7c41cca488abb69b5aa94 100755 --- a/src/main/java/net/floodlightcontroller/devicemanager/internal/Device.java +++ b/src/main/java/net/floodlightcontroller/devicemanager/internal/Device.java @@ -275,7 +275,11 @@ public class Device implements IDevice { if (prev != null && !(dpid.equals(prev.getSwitchDPID()) && - port.equals(prev.getSwitchPort()))) { + port.equals(prev.getSwitchPort())) && + !topology.isInSameBroadcastDomain(dpid.longValue(), + port.shortValue(), + prev.getSwitchDPID().longValue(), + prev.getSwitchPort().shortValue())) { long curActive = deviceManager.apComparator. getEffTS(cur, cur.getActiveSince()); diff --git a/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImpl.java b/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImpl.java index 469b0621697c86c767b3e72d60420217337cc654..80960bbc90436048b7382f50be04c3e93afd1e27 100755 --- a/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImpl.java +++ b/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImpl.java @@ -38,9 +38,11 @@ import java.util.concurrent.TimeUnit; import net.floodlightcontroller.core.FloodlightContext; import net.floodlightcontroller.core.IFloodlightProviderService; +import net.floodlightcontroller.core.IHAListener; import net.floodlightcontroller.core.IInfoProvider; import net.floodlightcontroller.core.IOFMessageListener; import net.floodlightcontroller.core.IOFSwitch; +import net.floodlightcontroller.core.IFloodlightProviderService.Role; import net.floodlightcontroller.core.module.FloodlightModuleContext; import net.floodlightcontroller.core.module.IFloodlightModule; import net.floodlightcontroller.core.module.IFloodlightService; @@ -81,7 +83,7 @@ import org.slf4j.LoggerFactory; public class DeviceManagerImpl implements IDeviceService, IOFMessageListener, IStorageSourceListener, IFloodlightModule, - IInfoProvider { + IInfoProvider, IHAListener { protected static Logger logger = LoggerFactory.getLogger(DeviceManagerImpl.class); @@ -602,6 +604,28 @@ public class DeviceManagerImpl implements logger.error("Could not instantiate REST API"); } } + + // *************** + // IHAListener + // *************** + + @Override + public void roleChanged(Role oldRole, Role newRole) { + switch(newRole) { + case SLAVE: + logger.debug("Resetting device state because of role change"); + startUp(null); + break; + } + } + + @Override + public void controllerNodeIPsChanged( + Map<String, String> curControllerNodeIPs, + Map<String, String> addedControllerNodeIPs, + Map<String, String> removedControllerNodeIPs) { + // no-op + } // **************** // Internal methods diff --git a/src/main/java/net/floodlightcontroller/packet/LLDPOrganizationalTLV.java b/src/main/java/net/floodlightcontroller/packet/LLDPOrganizationalTLV.java new file mode 100644 index 0000000000000000000000000000000000000000..a0930bd0fcf73c977ae778ed6dcaf53cbbe15c24 --- /dev/null +++ b/src/main/java/net/floodlightcontroller/packet/LLDPOrganizationalTLV.java @@ -0,0 +1,181 @@ +/** + * 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.packet; + +import java.nio.ByteBuffer; +import java.nio.charset.Charset; +import java.util.Arrays; + +/** + * The class representing LLDP Organizationally Specific TLV. + * + * @author Sho Shimizu (sho.shimizu@gmail.com) + */ +public class LLDPOrganizationalTLV extends LLDPTLV { + public static final int OUI_LENGTH = 3; + public static final int SUBTYPE_LENGTH = 1; + public static final byte ORGANIZATIONAL_TLV_TYPE = 127; + public static final int MAX_INFOSTRING_LENGTH = 507; + + protected byte[] oui; + protected byte subType; + private byte[] infoString; + + public LLDPOrganizationalTLV() { + type = ORGANIZATIONAL_TLV_TYPE; + } + + /** + * Set the value of OUI. + * @param oui The value of OUI to be set. + * @return This LLDP Organizationally Specific TLV. + */ + public LLDPOrganizationalTLV setOUI(byte[] oui) { + if (oui.length != OUI_LENGTH) { + throw new IllegalArgumentException("The length of OUI must be " + OUI_LENGTH + + ", but it is " + oui.length); + } + this.oui = Arrays.copyOf(oui, oui.length); + return this; + } + + /** + * Returns the value of the OUI. + * @return The value of the OUI . + */ + public byte[] getOUI() { + return Arrays.copyOf(oui, oui.length); + } + + /** + * Set the value of sub type. + * @param subType The value of sub type to be set. + * @return This LLDP Organizationally Specific TLV. + */ + public LLDPOrganizationalTLV setSubType(byte subType) { + this.subType = subType; + return this; + } + + /** + * Returns the value of the sub type. + * @return The value of the sub type. + */ + public byte getSubType() { + return subType; + } + + /** + * Set the value of information string. + * @param infoString the byte array of the value of information string. + * @return This LLDP Organizationally Specific TLV. + */ + public LLDPOrganizationalTLV setInfoString(byte[] infoString) { + if (infoString.length > MAX_INFOSTRING_LENGTH) { + throw new IllegalArgumentException("The length of infoString cannot exceed " + MAX_INFOSTRING_LENGTH); + } + this.infoString = Arrays.copyOf(infoString, infoString.length); + return this; + } + + /** + * Set the value of information string. + * The String value is automatically converted into byte array with UTF-8 encoding. + * @param infoString the String value of information string. + * @return This LLDP Organizationally Specific TLV. + */ + public LLDPOrganizationalTLV setInfoString(String infoString) { + byte[] infoStringBytes = infoString.getBytes(Charset.forName("UTF-8")); + return setInfoString(infoStringBytes); + } + + /** + * Returns the value of information string. + * @return the value of information string. + */ + public byte[] getInfoString() { + return Arrays.copyOf(infoString, infoString.length); + } + + @Override + public byte[] serialize() { + int valueLength = OUI_LENGTH + SUBTYPE_LENGTH + infoString.length; + value = new byte[valueLength]; + ByteBuffer bb = ByteBuffer.wrap(value); + bb.put(oui); + bb.put(subType); + bb.put(infoString); + return super.serialize(); + } + + @Override + public LLDPTLV deserialize(ByteBuffer bb) { + super.deserialize(bb); + ByteBuffer optionalField = ByteBuffer.wrap(value); + + byte[] oui = new byte[OUI_LENGTH]; + optionalField.get(oui); + setOUI(oui); + + setSubType(optionalField.get()); + + byte[] infoString = new byte[getLength() - OUI_LENGTH - SUBTYPE_LENGTH]; + optionalField.get(infoString); + setInfoString(infoString); + return this; + } + + @Override + public int hashCode() { + final int prime = 1423; + int result = 1; + result = prime * result + type; + result = prime * result + length; + result = prime * result + Arrays.hashCode(oui); + result = prime * result + subType; + result = prime * result + Arrays.hashCode(infoString); + return result; + } + + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + + if (!(o instanceof LLDPOrganizationalTLV)) { + return false; + } + + LLDPOrganizationalTLV other = (LLDPOrganizationalTLV)o; + if (this.type != other.type) { + return false; + } + if (this.length != other.length) { + return false; + } + if (!Arrays.equals(this.oui, other.oui)) { + return false; + } + if (this.subType != other.subType) { + return false; + } + if (!Arrays.equals(this.infoString, other.infoString)) { + return false; + } + + return true; + } +} diff --git a/src/main/java/org/openflow/protocol/statistics/OFAggregateStatisticsReply.java b/src/main/java/org/openflow/protocol/statistics/OFAggregateStatisticsReply.java index b5a486c31cee6766fe9a0e1a8afb459ae8c0e391..7dec16b69d8d295985363703585fb82ab08f3da1 100644 --- a/src/main/java/org/openflow/protocol/statistics/OFAggregateStatisticsReply.java +++ b/src/main/java/org/openflow/protocol/statistics/OFAggregateStatisticsReply.java @@ -18,6 +18,7 @@ package org.openflow.protocol.statistics; +import org.codehaus.jackson.annotate.JsonIgnore; import org.jboss.netty.buffer.ChannelBuffer; /** @@ -72,6 +73,7 @@ public class OFAggregateStatisticsReply implements OFStatistics { } @Override + @JsonIgnore public int getLength() { return 24; } diff --git a/src/main/java/org/openflow/protocol/statistics/OFFlowStatisticsReply.java b/src/main/java/org/openflow/protocol/statistics/OFFlowStatisticsReply.java index 420717fb3df42007eb168af167bcaefd85467063..803db8a9167fee4918e7d795cb9b42b665191100 100644 --- a/src/main/java/org/openflow/protocol/statistics/OFFlowStatisticsReply.java +++ b/src/main/java/org/openflow/protocol/statistics/OFFlowStatisticsReply.java @@ -19,6 +19,7 @@ package org.openflow.protocol.statistics; import java.util.List; +import org.codehaus.jackson.annotate.JsonIgnore; import org.jboss.netty.buffer.ChannelBuffer; import org.openflow.protocol.OFMatch; import org.openflow.protocol.action.OFAction; @@ -195,6 +196,7 @@ public class OFFlowStatisticsReply implements OFStatistics, OFActionFactoryAware } @Override + @JsonIgnore public int getLength() { return U16.f(length); } diff --git a/src/main/java/org/openflow/protocol/statistics/OFPortStatisticsReply.java b/src/main/java/org/openflow/protocol/statistics/OFPortStatisticsReply.java index 047ca5425ee6cdc56f18a19091e3f56d8d84f580..87a2465eaef54df954157d05edf53ab5052c95eb 100644 --- a/src/main/java/org/openflow/protocol/statistics/OFPortStatisticsReply.java +++ b/src/main/java/org/openflow/protocol/statistics/OFPortStatisticsReply.java @@ -18,6 +18,7 @@ package org.openflow.protocol.statistics; +import org.codehaus.jackson.annotate.JsonIgnore; import org.jboss.netty.buffer.ChannelBuffer; /** @@ -222,6 +223,7 @@ public class OFPortStatisticsReply implements OFStatistics { } @Override + @JsonIgnore public int getLength() { return 104; } diff --git a/src/main/java/org/openflow/protocol/statistics/OFQueueStatisticsReply.java b/src/main/java/org/openflow/protocol/statistics/OFQueueStatisticsReply.java index 7d0238a3fffcd2fbf9e90450bdb3ef946924b8c6..03cbb9cef0c87b3056a57fef8c309e2ad2d1ed23 100644 --- a/src/main/java/org/openflow/protocol/statistics/OFQueueStatisticsReply.java +++ b/src/main/java/org/openflow/protocol/statistics/OFQueueStatisticsReply.java @@ -18,6 +18,7 @@ package org.openflow.protocol.statistics; +import org.codehaus.jackson.annotate.JsonIgnore; import org.jboss.netty.buffer.ChannelBuffer; /** @@ -102,6 +103,7 @@ public class OFQueueStatisticsReply implements OFStatistics { } @Override + @JsonIgnore public int getLength() { return 32; } diff --git a/src/test/java/net/floodlightcontroller/core/internal/ControllerTest.java b/src/test/java/net/floodlightcontroller/core/internal/ControllerTest.java index b238228f6158301239af76ce459ee07c556e5f2b..1b2b7e9a94b0dfb98e30c1b4a1c665999d3f47b2 100644 --- a/src/test/java/net/floodlightcontroller/core/internal/ControllerTest.java +++ b/src/test/java/net/floodlightcontroller/core/internal/ControllerTest.java @@ -685,7 +685,6 @@ public class ControllerTest extends FloodlightTestCase { // newsw.role is null because the switch does not support // role request messages - expect(newsw.getRole()).andReturn(null); expect(newsw.getAttribute(IOFSwitch.SWITCH_SUPPORTS_NX_ROLE)) .andReturn(false); // switch is connected diff --git a/src/test/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImplTest.java b/src/test/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImplTest.java index 18cc280309fe34ad2a8f9d87012971ba497ad5c6..7db7c6659843b6117297325f8f3597b15f4e11b8 100644 --- a/src/test/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImplTest.java +++ b/src/test/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImplTest.java @@ -415,6 +415,8 @@ public class DeviceManagerImplTest extends FloodlightTestCase { andReturn(10L).anyTimes(); expect(mockTopology.isBroadcastDomainPort(anyLong(), anyShort())). andReturn(false).anyTimes(); + expect(mockTopology.isInSameBroadcastDomain(anyLong(), anyShort(), + anyLong(), anyShort())).andReturn(false).anyTimes(); expect(mockTopology.isInternal(anyLong(), anyShort())).andReturn(false).anyTimes(); @@ -499,6 +501,10 @@ public class DeviceManagerImplTest extends FloodlightTestCase { andReturn(false).anyTimes(); expect(mockTopology.isBroadcastDomainPort(1L, (short)2)). andReturn(true).anyTimes(); + expect(mockTopology.isInSameBroadcastDomain(1L, (short)1, + 1L, (short)2)).andReturn(true).anyTimes(); + expect(mockTopology.isInSameBroadcastDomain(1L, (short)2, + 1L, (short)1)).andReturn(true).anyTimes(); replay(mockTopology); @@ -717,6 +723,8 @@ public class DeviceManagerImplTest extends FloodlightTestCase { expect(mockTopology.isBroadcastDomainPort(anyLong(), anyShort())). andReturn(false).anyTimes(); + expect(mockTopology.isInSameBroadcastDomain(anyLong(), anyShort(), + anyLong(), anyShort())).andReturn(false).anyTimes(); expect(mockTopology.getSwitchClusterId(anyLong())). andReturn(1L).anyTimes(); replay(mockTopology); @@ -809,6 +817,8 @@ public class DeviceManagerImplTest extends FloodlightTestCase { expect(mockTopology.isBroadcastDomainPort(anyLong(), anyShort())). andReturn(false).anyTimes(); + expect(mockTopology.isInSameBroadcastDomain(anyLong(), anyShort(), + anyLong(), anyShort())).andReturn(false).anyTimes(); expect(mockTopology.getSwitchClusterId(1L)). andReturn(1L).anyTimes(); expect(mockTopology.getSwitchClusterId(5L)). diff --git a/src/test/java/net/floodlightcontroller/packet/LLDPOrganizationalTLVTest.java b/src/test/java/net/floodlightcontroller/packet/LLDPOrganizationalTLVTest.java new file mode 100644 index 0000000000000000000000000000000000000000..88fb26f2e75ed8dbf34d85d2f4a7e7b32e1e1e2b --- /dev/null +++ b/src/test/java/net/floodlightcontroller/packet/LLDPOrganizationalTLVTest.java @@ -0,0 +1,95 @@ +/** + * 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.packet; + +import org.junit.Test; + +import java.nio.ByteBuffer; +import java.nio.charset.Charset; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +public class LLDPOrganizationalTLVTest { + private final byte[] expected = new byte[] { + // Type: 127, Length: 13 + (byte) 254, 13, + // OpenFlow OUI: 00-26-E1 + 0x0, 0x26, (byte)0xe1, + // SubType: 12 + 0xc, + // Bytes in "ExtraInfo" + 0x45, 0x78, 0x74, 0x72, 0x61, 0x49, 0x6e, 0x66, 0x6f + }; + + @Test(expected = IllegalArgumentException.class) + public void testShortOUI() { + LLDPOrganizationalTLV tlv = new LLDPOrganizationalTLV(); + tlv.setOUI(new byte[2]); + } + + @Test(expected = IllegalArgumentException.class) + public void testLongOUI() { + LLDPOrganizationalTLV tlv = new LLDPOrganizationalTLV(); + tlv.setOUI(new byte[4]); + } + + @Test(expected = IllegalArgumentException.class) + public void testLongInfoString() { + LLDPOrganizationalTLV tlv = new LLDPOrganizationalTLV(); + tlv.setInfoString(new byte[LLDPOrganizationalTLV.MAX_INFOSTRING_LENGTH + 1]); + } + + @Test + public void testMaxInfoString() { + LLDPOrganizationalTLV tlv = new LLDPOrganizationalTLV(); + tlv.setInfoString(new byte[LLDPOrganizationalTLV.MAX_INFOSTRING_LENGTH]); + } + + @Test + public void testInfoString() { + LLDPOrganizationalTLV tlv = new LLDPOrganizationalTLV(); + tlv.setInfoString("ExtraInfo"); + assertThat(tlv.getInfoString(), is("ExtraInfo".getBytes(Charset.forName("UTF-8")))); + } + + @Test + public void testSerialize() { + LLDPOrganizationalTLV tlv = new LLDPOrganizationalTLV(); + tlv.setLength((short) 13); + // OpenFlow OUI is 00-26-E1 + tlv.setOUI(new byte[] {0x0, 0x26, (byte) 0xe1}); + tlv.setSubType((byte) 12); + tlv.setInfoString("ExtraInfo".getBytes(Charset.forName("UTF-8"))); + + assertThat(tlv.getType(), is((byte)127)); + assertThat(tlv.getLength(), is((short)13)); + assertThat(tlv.getOUI(), is(new byte[] {0x0, 0x26, (byte) 0xe1})); + assertThat(tlv.getSubType(), is((byte)12)); + assertThat(tlv.serialize(), is(expected)); + } + + @Test + public void testDeserialize() { + LLDPOrganizationalTLV tlv = new LLDPOrganizationalTLV(); + tlv.deserialize(ByteBuffer.wrap(expected)); + + assertThat(tlv.getType(), is((byte)127)); + assertThat(tlv.getLength(), is((short)13)); + assertThat(tlv.getOUI(), is(new byte[] {0x0, 0x26, (byte) 0xe1})); + assertThat(tlv.getSubType(), is((byte)12)); + assertThat(tlv.getInfoString(), is("ExtraInfo".getBytes(Charset.forName("UTF-8")))); + } +}