diff --git a/build.xml b/build.xml index c94be94c1475fafe689bd3811b2e9fbfade75047..19e6d34afd8c0067b77901e65187d754dace4e12 100644 --- a/build.xml +++ b/build.xml @@ -35,6 +35,8 @@ <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"/> @@ -56,6 +58,15 @@ <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> @@ -201,7 +212,7 @@ <target name="coverage" depends="instrument,test,coverage-report"/> <target name="dist" depends="compile,compile-test"> - <jar destfile="${floodlight-jar}" filesetmanifest="mergewithoutmain"> + <jar destfile="${floodlight-nodeps-jar}" filesetmanifest="mergewithoutmain"> <manifest> <attribute name="Main-Class" value="${main-class}"/> <attribute name="Class-Path" value="."/> @@ -211,12 +222,30 @@ <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> - <zipgroupfileset dir="${target}/lib"> - <patternset refid="genlib"/> + <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> + <zipfileset src="${floodlight-nodeps-jar}"/> </jar> <jar destfile="${floodlight-test-jar}" filesetmanifest="mergewithoutmain"> <manifest> diff --git a/debian/control b/debian/control index a3bebb37e2808cba875956ff5d8cc1ea74d45387..f36652fae07e437469a3954a8105e932a89aebf1 100644 --- a/debian/control +++ b/debian/control @@ -2,7 +2,7 @@ Source: floodlight Section: net Priority: extra Maintainer: Rich Lane <rlane@bigswitch.com> -Build-Depends: debhelper (>= 8.0.0), javahelper (>= 0.40ubuntu1) +Build-Depends: debhelper (>= 8.0.0), javahelper (>= 0.40ubuntu1), liblogback-java, libjackson-json-java, libeasymock-java, libslf4j-java, libsimple-xml-java, libnetty-java, libargs4j-java, libconcurrentlinkedhashmap-java, jython Standards-Version: 3.9.2 Homepage: http://floodlight.openflowhub.org/ Vcs-Git: git://github.com/floodlight/floodlight.git diff --git a/debian/floodlight.install b/debian/floodlight.install index 900beb94f874761c9f97066c911096e554ef3bc9..f21b0d6c1f05b46f0c81f2c497c5c56e84fac247 100644 --- a/debian/floodlight.install +++ b/debian/floodlight.install @@ -1,2 +1,2 @@ -target/floodlight.jar usr/share/java +target/floodlight-debian.jar usr/share/java debian/misc/floodlight usr/bin diff --git a/debian/misc/floodlight b/debian/misc/floodlight index 6036594a4b32028ff7de01e423e354a38bdb9b88..b98a261ac74afb002e4ee8649d5240e76a0114db 100755 --- a/debian/misc/floodlight +++ b/debian/misc/floodlight @@ -1,2 +1,2 @@ #!/bin/sh -exec java -jar /usr/share/java/floodlight.jar "$@" +exec java -jar /usr/share/java/floodlight-debian.jar "$@" diff --git a/src/main/java/net/floodlightcontroller/core/internal/Controller.java b/src/main/java/net/floodlightcontroller/core/internal/Controller.java index f0e4bcd833266ea8c204881962baf6c18d1cdd7c..09186efa25294e4f334b3197dd4c5c33ca7787ff 100644 --- a/src/main/java/net/floodlightcontroller/core/internal/Controller.java +++ b/src/main/java/net/floodlightcontroller/core/internal/Controller.java @@ -334,6 +334,17 @@ public class Controller implements IFloodlightProviderService { sw.getAttribute(IOFSwitch.SWITCH_SUPPORTS_NX_ROLE); if ((supportsNxRole != null) && supportsNxRole) { 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) { + 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 + // channelDisconnect which updates all state + sw.getChannel().close(); + } } } @@ -587,14 +598,14 @@ public class Controller implements IFloodlightProviderService { role); state.nxRoleRequestXid = sendNxRoleRequest(sw, role); } else { - // The hasNxRole field is just a flag that's checked before - // advancing the handshake state to READY. In this case, if role - // support isn't enabled for the controller, then we're not - // sending the role request probe to the switch so we don't need - // to wait for a reply/error before transitioning to the READY - // state. + // if role support isn't enabled for the controller, then we're + // not sending the role request probe to the switch log.info("This controllers role is null - not sending role-" + - "request-msg"); + "request-msg"); + // The hasNxRole field is just a flag that's checked before + // advancing the handshake state to READY. In this case, + // we set the flag, so we don't need to wait for a + // reply/error before transitioning to the READY state. state.hasNxRoleReply = true; } } @@ -606,7 +617,7 @@ public class Controller implements IFloodlightProviderService { state.hsState = HandshakeState.READY; if (getRole() == Role.SLAVE && sw.getRole() == null) { - // When the controller is currently in the slave role and + // When the controller is in the slave role and // the switch doesn't understand the role request message - // we disconnect the switch! The expected behavior is that // the switch will probably try to reconnect repeatedly @@ -614,8 +625,9 @@ public class Controller implements IFloodlightProviderService { // while will give-up and move on to the next controller-IP // configured on the switch. This is the serial failover // mechanism from OpenFlow spec v1.0. - log.error("Disconnecting switch {} that doesn't support " + - "role request messages from a slave controller"); + log.error("Disconnecting switch from SLAVE controller." + + " Switch {} doesn't support role request messages", + sw.getId()); sw.setConnected(false); connectedSwitches.remove(sw.getId()); sw.getChannel().close(); diff --git a/src/test/java/net/floodlightcontroller/core/internal/ControllerTest.java b/src/test/java/net/floodlightcontroller/core/internal/ControllerTest.java index 58b92ea9bf97b6d58cd8283654383f1a10d34b59..3ba8bdd90d622068fcdbddfbc9a90119c24d1398 100644 --- a/src/test/java/net/floodlightcontroller/core/internal/ControllerTest.java +++ b/src/test/java/net/floodlightcontroller/core/internal/ControllerTest.java @@ -19,6 +19,8 @@ package net.floodlightcontroller.core.internal; import static org.easymock.EasyMock.*; +import java.net.InetSocketAddress; +import java.net.SocketAddress; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; @@ -31,6 +33,7 @@ import java.util.concurrent.TimeUnit; import net.floodlightcontroller.core.FloodlightProvider; import net.floodlightcontroller.core.FloodlightContext; import net.floodlightcontroller.core.IFloodlightProviderService; +import net.floodlightcontroller.core.IFloodlightProviderService.Role; import net.floodlightcontroller.core.IOFMessageFilterManagerService; import net.floodlightcontroller.core.IOFMessageListener; import net.floodlightcontroller.core.IOFMessageListener.Command; @@ -55,14 +58,19 @@ import net.floodlightcontroller.test.FloodlightTestCase; import net.floodlightcontroller.threadpool.IThreadPoolService; import org.jboss.netty.channel.Channel; +import org.jboss.netty.channel.ChannelStateEvent; import org.junit.Test; +import org.openflow.protocol.OFError; import org.openflow.protocol.OFFeaturesReply; +import org.openflow.protocol.OFMessage; import org.openflow.protocol.OFPacketIn; import org.openflow.protocol.OFPacketOut; import org.openflow.protocol.OFPhysicalPort; import org.openflow.protocol.OFPort; import org.openflow.protocol.OFStatisticsReply; import org.openflow.protocol.OFType; +import org.openflow.protocol.OFError.OFBadRequestCode; +import org.openflow.protocol.OFError.OFErrorType; import org.openflow.protocol.OFPacketIn.OFPacketInReason; import org.openflow.protocol.action.OFAction; import org.openflow.protocol.action.OFActionOutput; @@ -464,7 +472,6 @@ public class ControllerTest extends FloodlightTestCase { expect(channel2.getRemoteAddress()).andReturn(null); expect(newsw.getFeaturesReply()).andReturn(new OFFeaturesReply()).anyTimes(); expect(newsw.getPorts()).andReturn(new HashMap<Short,OFPhysicalPort>()); - controller.activeSwitches.put(0L, oldsw); replay(newsw, channel, channel2); @@ -472,4 +479,67 @@ public class ControllerTest extends FloodlightTestCase { verify(newsw, channel, channel2); } + + + @Test + public void testRoleChangeForSerialFailoverSwitch() throws Exception { + IOFSwitch newsw = createMock(IOFSwitch.class); + expect(newsw.getId()).andReturn(0L).anyTimes(); + expect(newsw.getStringId()).andReturn("00:00:00:00:00:00:00").anyTimes(); + Channel channel2 = createMock(Channel.class); + expect(newsw.getChannel()).andReturn(channel2); + + // 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 + controller.connectedSwitches.put(0L, newsw); + + // the switch should get disconnected when role is changed to SLAVE + expect(channel2.close()).andReturn(null); + + replay(newsw, channel2); + controller.setRole(Role.SLAVE); + verify(newsw, channel2); + } + + @Test + public void testSlaveRoleHandshakeForSerialFailoverSwitch() + throws Exception { + controller.role = Role.SLAVE; + OFFeaturesReply featuresReply = new OFFeaturesReply(); + featuresReply.setDatapathId(0L); + featuresReply.setPorts(new ArrayList<OFPhysicalPort>()); + OFChannelState state = new OFChannelState(); + state.hsState = OFChannelState.HandshakeState.FEATURES_REPLY; + state.hasDescription = true; + state.hasGetConfigReply = true; + Controller.OFChannelHandler chdlr = controller.new OFChannelHandler(state); + + Channel ch = createMock(Channel.class); + ChannelStateEvent e = createMock(ChannelStateEvent.class); + expect(e.getChannel()).andReturn(ch).anyTimes(); + SocketAddress sa = new InetSocketAddress(45454); + expect(ch.getRemoteAddress()).andReturn(sa); + expect(ch.write(anyObject())).andReturn(null); + + // the error returned when role request message is not supported by sw + OFMessage msg = new OFError(); + msg.setType(OFType.ERROR); + ((OFError) msg).setErrorType(OFErrorType.OFPET_BAD_REQUEST); + ((OFError) msg).setErrorCode(OFBadRequestCode.OFPBRC_BAD_VENDOR); + + // the switch connection should get disconnected when the controller is + // in SLAVE mode and the switch does not support role-request messages + expect(ch.close()).andReturn(null); + + replay(ch, e); + chdlr.channelConnected(null, e); + chdlr.sw.setFeaturesReply(featuresReply); + chdlr.processOFMessage(msg); + verify(ch, e); + + } }