/**
* 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.core.internal;
import static org.easymock.EasyMock.anyObject;
import static org.easymock.EasyMock.createMock;
import static org.easymock.EasyMock.createStrictMock;
import static org.easymock.EasyMock.eq;
import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.expectLastCall;
import static org.easymock.EasyMock.isA;
import static org.easymock.EasyMock.replay;
import static org.easymock.EasyMock.reset;
import static org.easymock.EasyMock.same;
import static org.easymock.EasyMock.verify;
import static org.junit.Assert.assertArrayEquals;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import net.floodlightcontroller.core.FloodlightContext;
import net.floodlightcontroller.core.FloodlightProvider;
import net.floodlightcontroller.core.IFloodlightProviderService;
import net.floodlightcontroller.core.IListener;
import net.floodlightcontroller.core.IListener.Command;
import net.floodlightcontroller.core.IOFMessageListener;
import net.floodlightcontroller.core.IOFSwitch;
import net.floodlightcontroller.core.IOFSwitch.PortChangeType;
import net.floodlightcontroller.core.IOFSwitchListener;
import net.floodlightcontroller.core.IUpdate;
import net.floodlightcontroller.core.internal.Controller.SwitchUpdate;
import net.floodlightcontroller.core.internal.Controller.SwitchUpdateType;
import net.floodlightcontroller.core.module.FloodlightModuleContext;
import net.floodlightcontroller.core.test.MockThreadPoolService;
import net.floodlightcontroller.debugcounter.DebugCounter;
import net.floodlightcontroller.debugcounter.IDebugCounterService;
import net.floodlightcontroller.restserver.IRestApiService;
import net.floodlightcontroller.restserver.RestApiServer;
import net.floodlightcontroller.test.FloodlightTestCase;
import net.floodlightcontroller.threadpool.IThreadPoolService;
import net.onrc.onos.core.linkdiscovery.ILinkDiscoveryService;
import net.onrc.onos.core.linkdiscovery.LinkDiscoveryManager;
import net.onrc.onos.core.packet.ARP;
import net.onrc.onos.core.packet.Ethernet;
import net.onrc.onos.core.packet.IPacket;
import net.onrc.onos.core.packet.IPv4;
import net.onrc.onos.core.registry.IControllerRegistryService;
import org.easymock.EasyMock;
import org.junit.Before;
import org.junit.Test;
import org.projectfloodlight.openflow.protocol.OFFactories;
import org.projectfloodlight.openflow.protocol.OFFactory;
import org.projectfloodlight.openflow.protocol.OFFlowStatsEntry;
import org.projectfloodlight.openflow.protocol.OFFlowStatsReply;
import org.projectfloodlight.openflow.protocol.OFPacketIn;
import org.projectfloodlight.openflow.protocol.OFPacketIn.Builder;
import org.projectfloodlight.openflow.protocol.OFPacketInReason;
import org.projectfloodlight.openflow.protocol.OFPortDesc;
import org.projectfloodlight.openflow.protocol.OFStatsReply;
import org.projectfloodlight.openflow.protocol.OFStatsReplyFlags;
import org.projectfloodlight.openflow.protocol.OFStatsType;
import org.projectfloodlight.openflow.protocol.OFType;
import org.projectfloodlight.openflow.protocol.OFVersion;
import org.projectfloodlight.openflow.protocol.match.Match;
import org.projectfloodlight.openflow.protocol.match.MatchField;
import org.projectfloodlight.openflow.types.OFBufferId;
import org.projectfloodlight.openflow.types.OFPort;
import org.projectfloodlight.openflow.util.HexString;
import com.google.common.collect.HashMultiset;
import com.google.common.collect.Multiset;
/**
* Unit tests for the Controller class.
*/
public class ControllerTest extends FloodlightTestCase {
private Controller controller;
private MockThreadPoolService threadPool;
@Override
@Before
public void setUp() throws Exception {
super.setUp();
FloodlightModuleContext fmc = new FloodlightModuleContext();
FloodlightProvider floodlightProvider = new FloodlightProvider();
controller = (Controller) floodlightProvider.getServiceImpls().get(
IFloodlightProviderService.class);
fmc.addService(IFloodlightProviderService.class, controller);
RestApiServer restApi = new RestApiServer();
fmc.addService(IRestApiService.class, restApi);
DebugCounter counterService = new DebugCounter();
fmc.addService(IDebugCounterService.class, counterService);
threadPool = new MockThreadPoolService();
fmc.addService(IThreadPoolService.class, threadPool);
IControllerRegistryService registry =
createMock(IControllerRegistryService.class);
fmc.addService(IControllerRegistryService.class, registry);
LinkDiscoveryManager linkDiscovery = new LinkDiscoveryManager();
fmc.addService(ILinkDiscoveryService.class, linkDiscovery);
restApi.init(fmc);
floodlightProvider.init(fmc);
threadPool.init(fmc);
linkDiscovery.init(fmc);
restApi.startUp(fmc);
floodlightProvider.startUp(fmc);
threadPool.startUp(fmc);
}
private OFPacketIn buildPacketIn(short inPort, OFVersion version) {
IPacket testPacket = new Ethernet()
.setSourceMACAddress("00:44:33:22:11:00")
.setDestinationMACAddress("00:11:22:33:44:55")
.setEtherType(Ethernet.TYPE_ARP)
.setPayload(
new ARP()
.setHardwareType(ARP.HW_TYPE_ETHERNET)
.setProtocolType(ARP.PROTO_TYPE_IP)
.setHardwareAddressLength((byte) 6)
.setProtocolAddressLength((byte) 4)
.setOpCode(ARP.OP_REPLY)
.setSenderHardwareAddress(
Ethernet.toMACAddress("00:44:33:22:11:00"))
.setSenderProtocolAddress(
IPv4.toIPv4AddressBytes("192.168.1.1"))
.setTargetHardwareAddress(
Ethernet.toMACAddress("00:11:22:33:44:55"))
.setTargetProtocolAddress(
IPv4.toIPv4AddressBytes("192.168.1.2")));
byte[] testPacketSerialized = testPacket.serialize();
OFFactory factory = OFFactories.getFactory(version);
Builder piBuilder = factory.buildPacketIn()
.setBufferId(OFBufferId.NO_BUFFER)
.setData(testPacketSerialized)
.setReason(OFPacketInReason.NO_MATCH);
if (version == OFVersion.OF_10) {
piBuilder.setInPort(OFPort.of(inPort));
} else {
Match match = factory.buildMatch()
.setExact(MatchField.IN_PORT, OFPort.ofShort(inPort))
.build();
piBuilder.setMatch(match);
}
return piBuilder.build();
}
public Controller getController() {
return controller;
}
private OFStatsReply getStatisticsReply(int transactionId,
int count, boolean moreReplies, OFVersion version) {
OFFactory factory = OFFactories.getFactory(version);
List<OFFlowStatsEntry> statistics = new ArrayList<OFFlowStatsEntry>();
for (int i = 0; i < count; ++i) {
statistics.add(factory.buildFlowStatsEntry().build());
}
assertEquals(statistics.size(), count);
org.projectfloodlight.openflow.protocol.OFStatsReply.Builder
statsReplyBuilder = factory.buildFlowStatsReply()
.setXid(transactionId)
.setEntries(statistics);
if (moreReplies) {
statsReplyBuilder.setFlags(
Collections.singleton(OFStatsReplyFlags.REPLY_MORE));
}
return statsReplyBuilder.build();
}
private IOFSwitch createMockSwitch(long dpid, OFVersion version) {
IOFSwitch sw = createMock(IOFSwitch.class);
expect(sw.getId()).andReturn(dpid).anyTimes();
expect(sw.getStringId()).andReturn(HexString.toHexString(dpid)).anyTimes();
expect(sw.getPorts()).andReturn(
Collections.<OFPortDesc>emptySet()).anyTimes();
expect(sw.getOFVersion()).andReturn(version).anyTimes();
return sw;
}
/**
* Set up expectations for the callback ordering methods for mocked
* listener classes.
*
* @param listener the mock listener to set up expectations for
*/
private void setUpNoOrderingRequirements(IListener<OFType> listener) {
listener.isCallbackOrderingPostreq(EasyMock.<OFType>anyObject(),
anyObject(String.class));
expectLastCall().andReturn(false).anyTimes();
listener.isCallbackOrderingPrereq(EasyMock.<OFType>anyObject(),
anyObject(String.class));
expectLastCall().andReturn(false).anyTimes();
}
/**
* Run the controller's main loop so that updates are processed
*/
protected class ControllerRunThread extends Thread {
@Override
public void run() {
controller.openFlowPort = 0; // Don't listen
controller.run();
}
}
/**
* Verifies that a listener that throws an exception halts further
* execution, and verifies that the Commands STOP and CONTINUE are honored.
*
* @throws Exception
*/
@Test
public void testHandleMessages() throws Exception {
Controller controller = getController();
controller.removeOFMessageListeners(OFType.PACKET_IN);
// Just test 1.0 here. We test with 1.3 in testHandleMessageWithContext.
OFVersion version = OFVersion.OF_10;
IOFSwitch sw = createMockSwitch(1L, version);
OFPacketIn pi = buildPacketIn((short) 1, version);
IOFMessageListener test1 = createMock(IOFMessageListener.class);
expect(test1.getName()).andReturn("test1").anyTimes();
setUpNoOrderingRequirements(test1);
expect(test1.receive(eq(sw), eq(pi), isA(FloodlightContext.class)))
.andThrow(new RuntimeException(
"This is NOT an error! We are testing exception catching."));
IOFMessageListener test2 = createMock(IOFMessageListener.class);
expect(test2.getName()).andReturn("test2").anyTimes();
setUpNoOrderingRequirements(test2);
// expect no calls to test2.receive() since test1.receive() threw an
// exception
replay(test1, test2, sw);
controller.addOFMessageListener(OFType.PACKET_IN, test1);
controller.addOFMessageListener(OFType.PACKET_IN, test2);
try {
controller.handleMessage(sw, pi, null);
} catch (RuntimeException e) {
assertEquals(e.getMessage().startsWith("This is NOT an error!"), true);
}
verify(test1, test2);
// verify STOP works
reset(test1, test2);
expect(test1.receive(eq(sw), eq(pi), isA(FloodlightContext.class)))
.andReturn(Command.STOP);
replay(test1, test2);
controller.handleMessage(sw, pi, null);
verify(test1, test2);
}
/**
* Tests message handling when providing a FloodlightContext object.
* Checks that the correct values are set in the context before the message
* is dispatched to the listeners.
*
* @throws Exception
*/
@Test
public void testHandleMessageWithContext() throws Exception {
doTestHandleMessageWithContext(OFVersion.OF_10);
doTestHandleMessageWithContext(OFVersion.OF_13);
}
private void doTestHandleMessageWithContext(OFVersion version)
throws IOException {
controller.messageListeners.clear();
short inPort = (short) 1;
FloodlightContext cntx = new FloodlightContext();
IOFSwitch sw = createMockSwitch(1L, version);
OFPacketIn pi = buildPacketIn(inPort, version);
IOFMessageListener test1 = createMock(IOFMessageListener.class);
expect(test1.getName()).andReturn("test1").anyTimes();
setUpNoOrderingRequirements(test1);
expect(test1.receive(same(sw), same(pi), same(cntx)))
.andReturn(Command.CONTINUE);
IOFMessageListener test2 = createMock(IOFMessageListener.class);
expect(test2.getName()).andReturn("test2").anyTimes();
setUpNoOrderingRequirements(test2);
// test2 will not receive any message!
replay(test1, test2, sw);
controller.addOFMessageListener(OFType.PACKET_IN, test1);
controller.addOFMessageListener(OFType.ERROR, test2);
controller.handleMessage(sw, pi, cntx);
verify(test1, test2, sw);
Ethernet eth = IFloodlightProviderService.bcStore.get(cntx,
IFloodlightProviderService.CONTEXT_PI_PAYLOAD);
assertArrayEquals(pi.getData(), eth.serialize());
short actualInPort = (short) cntx.getStorage()
.get(IFloodlightProviderService.CONTEXT_PI_INPORT);
assertEquals(inPort, actualInPort);
}
/**
* Task used to get the value from a Future in a different thread.
*
* @param <E> the type of value returned by the Future
*/
public class FutureFetcher<E> implements Runnable {
public E value;
public Future<E> future;
public FutureFetcher(Future<E> future) {
this.future = future;
}
@Override
public void run() {
try {
value = future.get();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* Gets the value from the Future.
*
* @return the value
*/
public E getValue() {
return value;
}
/**
* Gets the Future.
*
* @return the future
*/
public Future<E> getFuture() {
return future;
}
}
/**
* Test that the OFStatisticsFuture correctly returns statistics replies.
*
* @throws Exception
*/
@Test
public void testOFStatisticsFuture() throws Exception {
// Test both OF 1.0 and 1.3 stats messages
doTestOFStatisticsFuture(OFVersion.OF_10);
doTestOFStatisticsFuture(OFVersion.OF_13);
}
private void doTestOFStatisticsFuture(OFVersion version) throws Exception {
// Test for a single stats reply
IOFSwitch sw = createMock(IOFSwitch.class);
sw.cancelStatisticsReply(1);
OFStatisticsFuture sf = new OFStatisticsFuture(threadPool, sw, 1);
replay(sw);
List<OFStatsReply> stats;
FutureFetcher<List<OFStatsReply>> ff =
new FutureFetcher<List<OFStatsReply>>(sf);
Thread t = new Thread(ff);
t.start();
sf.deliverFuture(sw, getStatisticsReply(1, 10, false, version));
t.join();
stats = ff.getValue();
verify(sw);
// We expect 1 flow stats reply message containing 10 flow stats entries
assertEquals(1, stats.size());
assertEquals(OFStatsType.FLOW, stats.get(0).getStatsType());
assertEquals(Collections.EMPTY_SET, stats.get(0).getFlags());
OFFlowStatsReply flowStatsReply = (OFFlowStatsReply) stats.get(0);
assertEquals(10, flowStatsReply.getEntries().size());
// Test multiple stats replies
reset(sw);
sw.cancelStatisticsReply(1);
sf = new OFStatisticsFuture(threadPool, sw, 1);
replay(sw);
ff = new FutureFetcher<List<OFStatsReply>>(sf);
t = new Thread(ff);
t.start();
sf.deliverFuture(sw, getStatisticsReply(1, 10, true, version));
sf.deliverFuture(sw, getStatisticsReply(1, 5, false, version));
t.join();
stats = sf.get();
verify(sw);
// We expect 2 flow stats replies. The first one has 10 entries and has
// the REPLY_MORE flag set, and the second one has 5 entries with no flag.
assertEquals(2, stats.size());
assertEquals(OFStatsType.FLOW, stats.get(0).getStatsType());
assertEquals(Collections.singleton(OFStatsReplyFlags.REPLY_MORE),
stats.get(0).getFlags());
assertEquals(OFStatsType.FLOW, stats.get(1).getStatsType());
assertEquals(Collections.EMPTY_SET, stats.get(1).getFlags());
OFFlowStatsReply flowStatsReply2 = (OFFlowStatsReply) stats.get(0);
assertEquals(10, flowStatsReply2.getEntries().size());
OFFlowStatsReply flowStatsReply3 = (OFFlowStatsReply) stats.get(1);
assertEquals(5, flowStatsReply3.getEntries().size());
// Test cancellation
reset(sw);
sw.cancelStatisticsReply(1);
sf = new OFStatisticsFuture(threadPool, sw, 1);
replay(sw);
ff = new FutureFetcher<List<OFStatsReply>>(sf);
t = new Thread(ff);
t.start();
sf.cancel(true);
t.join();
stats = sf.get();
verify(sw);
assertEquals(0, stats.size());
// Test self timeout
reset(sw);
sw.cancelStatisticsReply(1);
sf = new OFStatisticsFuture(threadPool, sw, 1, 75, TimeUnit.MILLISECONDS);
replay(sw);
ff = new FutureFetcher<List<OFStatsReply>>(sf);
t = new Thread(ff);
t.start();
t.join(2000);
stats = sf.get();
verify(sw);
assertEquals(0, stats.size());
}
/**
* Test adding a new switch in the MASTER role.
* We expect a switchActivatedMaster event fired to the switch listeners.
*/
@Test
public void testSwitchActivatedMaster() throws Exception {
long dpid = 1L;
controller.setAlwaysClearFlowsOnSwActivate(false);
controller.setAlwaysClearFlowsOnSwAdd(false);
// Create a 1.0 switch. There's no difference between 1.0 and 1.3 here.
IOFSwitch sw = createMockSwitch(dpid, OFVersion.OF_10);
// strict mock. Order of events matters!
IOFSwitchListener listener = createStrictMock(IOFSwitchListener.class);
listener.switchActivatedMaster(dpid);
expectLastCall().once();
replay(listener);
controller.addOFSwitchListener(listener);
replay(sw);
controller.addConnectedSwitch(dpid, new OFChannelHandler(controller));
controller.addActivatedMasterSwitch(dpid, sw);
verify(sw);
assertEquals(sw, controller.getMasterSwitch(dpid));
controller.processUpdateQueueForTesting();
verify(listener);
}
/**
* Test adding a new switch in the EQUAL role.
* We expect a switchActivatedEqual event fired to the switch listeners.
*/
@Test
public void testSwitchActivatedEqual() throws Exception {
long dpid = 1L;
// Create a 1.0 switch. There's no difference between 1.0 and 1.3 here.
IOFSwitch sw = createMockSwitch(dpid, OFVersion.OF_10);
IOFSwitchListener listener = createStrictMock(IOFSwitchListener.class);
controller.addOFSwitchListener(listener);
listener.switchActivatedEqual(dpid);
replay(sw, listener); // nothing recorded
controller.addConnectedSwitch(dpid, new OFChannelHandler(controller));
controller.addActivatedEqualSwitch(dpid, sw);
verify(sw);
controller.processUpdateQueueForTesting();
verify(listener);
}
/**
* Disconnect a switch which was connected in the MASTER role.
* Check the correct cleanup methods are called on the switch, that the
* switch is removed from the Controller data structures, and that the
* correct switch listener method is called.
*/
@Test
public void testDisconnectMasterSwitch() {
long dpid = 1L;
// Create a 1.0 switch. There's no difference between 1.0 and 1.3 here.
IOFSwitch sw = createMockSwitch(dpid, OFVersion.OF_10);
replay(sw);
// Add switch to controller as MASTER
controller.addConnectedSwitch(dpid, new OFChannelHandler(controller));
controller.addActivatedMasterSwitch(dpid, sw);
// Check the switch is in the controller's lists
assertEquals(sw, controller.getMasterSwitch(dpid));
IOFSwitchListener listener = createStrictMock(IOFSwitchListener.class);
listener.switchDisconnected(dpid);
expectLastCall().once();
replay(listener);
// Drain the update queue
controller.processUpdateQueueForTesting();
// Add the listener
controller.addOFSwitchListener(listener);
reset(sw);
sw.cancelAllStatisticsReplies();
expectLastCall().once();
sw.setConnected(false);
expectLastCall().once();
replay(sw);
// Disconnect switch
controller.removeConnectedSwitch(dpid);
assertNull(controller.getMasterSwitch(dpid));
controller.processUpdateQueueForTesting();
verify(listener, sw);
}
/**
* Disconnect a switch which was connected in the EQUAL role.
* Check the correct cleanup methods are called on the switch, that the
* switch is removed from the Controller data structures, and that the
* correct switch listener method is called.
*/
@Test
public void testDisconnectEqualSwitch() {
long dpid = 1L;
// Create a 1.0 switch. There's no difference between 1.0 and 1.3 here.
IOFSwitch sw = createMockSwitch(dpid, OFVersion.OF_10);
replay(sw);
// Add switch to controller as EQUAL
controller.addConnectedSwitch(dpid, new OFChannelHandler(controller));
controller.addActivatedEqualSwitch(dpid, sw);
// Check the switch is in the controller's lists
assertEquals(sw, controller.getEqualSwitch(dpid));
IOFSwitchListener listener = createStrictMock(IOFSwitchListener.class);
listener.switchDisconnected(dpid);
expectLastCall().once();
replay(listener);
// Drain the update queue
controller.processUpdateQueueForTesting();
// Add the listener
controller.addOFSwitchListener(listener);
reset(sw);
sw.cancelAllStatisticsReplies();
expectLastCall().once();
sw.setConnected(false);
expectLastCall().once();
replay(sw);
// Disconnect switch
controller.removeConnectedSwitch(dpid);
assertNull(controller.getEqualSwitch(dpid));
controller.processUpdateQueueForTesting();
verify(listener, sw);
}
/**
* Remove a nonexistent switch.
* Check the switch listeners don't receive a disconnected event.
*/
@Test
public void testNonexistingSwitchDisconnected() throws Exception {
long dpid = 1L;
// Create a 1.0 switch. There's no difference between 1.0 and 1.3 here.
IOFSwitch sw = createMockSwitch(1L, OFVersion.OF_10);
IOFSwitchListener listener = createStrictMock(IOFSwitchListener.class);
controller.addOFSwitchListener(listener);
replay(sw, listener);
controller.removeConnectedSwitch(dpid);
controller.processUpdateQueueForTesting();
verify(sw, listener);
assertNull(controller.getSwitch(dpid));
}
/**
* Try to connect a switch with the same DPID as an already active switch.
* This could happen if two different switches have the same DPID or if a
* switch reconnects while the old TCP connection is still alive.
* Check that {@link Controller#addConnectedSwitch(long, OFChannelHandler)}
* returns false and that no modification is made to the connectedSwitches
* map.
*/
@Test
public void testConnectSwitchWithSameDpid() {
long dpid = 1L;
// Setup: add a switch to the controller
// Create a 1.0 switch. There's no difference between 1.0 and 1.3 here.
IOFSwitch oldsw = createMockSwitch(dpid, OFVersion.OF_10);
replay(oldsw);
OFChannelHandler oldChannel = new OFChannelHandler(controller);
assertTrue(controller.addConnectedSwitch(dpid, oldChannel));
controller.addActivatedMasterSwitch(dpid, oldsw);
assertSame(oldChannel, controller.connectedSwitches.get(dpid));
assertEquals(oldsw, controller.getSwitch(dpid));
// Now try to add a new switch (OFChannelHandler) with the same dpid to
// the controller. #addConnectedSwitch should return false and no
// modification should be made to the connected switches map.
assertFalse(controller.addConnectedSwitch(
dpid, new OFChannelHandler(controller)));
assertSame(oldChannel, controller.connectedSwitches.get(dpid));
}
/**
* Tests that you can't remove a switch from the map returned by
* getSwitches() (because getSwitches should return an unmodifiable map)
*/
@Test
public void testRemoveActiveSwitch() {
long dpid = 1L;
// Create a 1.0 switch. There's no difference between 1.0 and 1.3 here.
IOFSwitch sw = createMockSwitch(dpid, OFVersion.OF_10);
replay(sw);
controller.addConnectedSwitch(1L, new OFChannelHandler(controller));
controller.addActivatedMasterSwitch(1L, sw);
assertEquals(sw, getController().getSwitch(1L));
controller.getAllSwitchDpids().remove(1L);
assertEquals(sw, getController().getSwitch(1L));
verify(sw);
}
/**
* Implementation of an IOFSwitchListener that counts the number of events
* it has received of each different event type.
*/
private static class DummySwitchListener implements IOFSwitchListener {
// The multisets will record a count of the number of times each update
// has been seen by the listener
private Multiset<SwitchUpdateType> updateCount = HashMultiset.create();
private Multiset<PortChangeType> portUpdateCount = HashMultiset.create();
/**
* Gets the number of times a switch update event of the specified type
* has been received.
*
* @param type SwitchUpdateType to get the count for
* @return number of times the event has been received
*/
public int getSwitchUpdateCount(SwitchUpdateType type) {
return updateCount.count(type);
}
/**
* Gets the number of times a port update event of the specified type
* has been received.
*
* @param type PortChangeType to get the count for
* @return number of times the event has been received
*/
public int getPortUpdateCount(PortChangeType type) {
return portUpdateCount.count(type);
}
@Override
public String getName() {
return "dummy";
}
@Override
public synchronized void switchActivatedMaster(long swId) {
updateCount.add(SwitchUpdateType.ACTIVATED_MASTER);
notifyAll();
}
@Override
public synchronized void switchActivatedEqual(long swId) {
updateCount.add(SwitchUpdateType.ACTIVATED_EQUAL);
notifyAll();
}
@Override
public synchronized void switchMasterToEqual(long swId) {
updateCount.add(SwitchUpdateType.MASTER_TO_EQUAL);
notifyAll();
}
@Override
public synchronized void switchEqualToMaster(long swId) {
updateCount.add(SwitchUpdateType.EQUAL_TO_MASTER);
notifyAll();
}
@Override
public synchronized void switchDisconnected(long swId) {
updateCount.add(SwitchUpdateType.DISCONNECTED);
notifyAll();
}
@Override
public synchronized void switchPortChanged(long swId, OFPortDesc port,
PortChangeType changeType) {
portUpdateCount.add(changeType);
notifyAll();
}
}
/**
* Tests that updates sent into the Controller updates queue are dispatched
* to the listeners correctly.
*
* @throws InterruptedException
*/
@Test
public void testUpdateQueue() throws InterruptedException {
// No difference between OpenFlow versions here
OFVersion version = OFVersion.OF_10;
OFPortDesc port = OFFactories.getFactory(version)
.buildPortDesc().build();
long dpid = 1L;
DummySwitchListener switchListener = new DummySwitchListener();
IOFSwitch sw = createMockSwitch(dpid, version);
replay(sw);
ControllerRunThread t = new ControllerRunThread();
t.start();
controller.addOFSwitchListener(switchListener);
// Switch updates
doTestUpdateQueueWithUpdate(dpid, SwitchUpdateType.ACTIVATED_MASTER,
switchListener);
doTestUpdateQueueWithUpdate(dpid, SwitchUpdateType.ACTIVATED_EQUAL,
switchListener);
doTestUpdateQueueWithUpdate(dpid, SwitchUpdateType.EQUAL_TO_MASTER,
switchListener);
doTestUpdateQueueWithUpdate(dpid, SwitchUpdateType.MASTER_TO_EQUAL,
switchListener);
doTestUpdateQueueWithUpdate(dpid, SwitchUpdateType.DISCONNECTED,
switchListener);
// Port updates
doTestUpdateQueueWithPortUpdate(dpid, port, PortChangeType.ADD,
switchListener);
doTestUpdateQueueWithPortUpdate(dpid, port, PortChangeType.OTHER_UPDATE,
switchListener);
doTestUpdateQueueWithPortUpdate(dpid, port, PortChangeType.DELETE,
switchListener);
doTestUpdateQueueWithPortUpdate(dpid, port, PortChangeType.UP,
switchListener);
doTestUpdateQueueWithPortUpdate(dpid, port, PortChangeType.DOWN,
switchListener);
}
private void doTestUpdateQueueWithUpdate(long dpid, SwitchUpdateType type,
DummySwitchListener listener) throws InterruptedException {
controller.updates.put(controller.new SwitchUpdate(dpid, type));
synchronized (listener) {
listener.wait(500);
}
// Test that the update was seen by the listener 1 time
assertEquals(1, listener.getSwitchUpdateCount(type));
}
private void doTestUpdateQueueWithPortUpdate(long dpid, OFPortDesc port,
PortChangeType type,
DummySwitchListener listener) throws InterruptedException {
controller.updates.put(controller.new SwitchUpdate(dpid,
SwitchUpdateType.PORTCHANGED, port, type));
synchronized (listener) {
listener.wait(500);
}
// Test that the update was seen by the listener 1 time
assertEquals(1, listener.getPortUpdateCount(type));
}
/**
* Test the method to notify the controller of a port change.
* The controller should send out an update to the switch listeners.
*
* @throws InterruptedException
*/
@Test
public void testNotifyPortChanged() throws InterruptedException {
long dpid = 1L;
// No difference between OpenFlow versions here
OFVersion version = OFVersion.OF_10;
IOFSwitch sw = createMockSwitch(dpid, version);
OFPortDesc port = OFFactories.getFactory(version).buildPortDesc()
.setName("myPortName1")
.setPortNo(OFPort.of(42))
.build();
replay(sw);
controller.connectedSwitches.put(1L, new OFChannelHandler(controller));
controller.activeMasterSwitches.put(1L, sw);
doTestNotifyPortChanged(dpid, port, PortChangeType.ADD);
doTestNotifyPortChanged(dpid, port, PortChangeType.OTHER_UPDATE);
doTestNotifyPortChanged(dpid, port, PortChangeType.DELETE);
doTestNotifyPortChanged(dpid, port, PortChangeType.UP);
doTestNotifyPortChanged(dpid, port, PortChangeType.DOWN);
}
private void doTestNotifyPortChanged(long dpid, OFPortDesc port,
PortChangeType changeType) throws InterruptedException {
controller.notifyPortChanged(dpid, port, changeType);
assertEquals(1, controller.updates.size());
IUpdate update = controller.updates.take();
assertEquals(true, update instanceof SwitchUpdate);
SwitchUpdate swUpdate = (SwitchUpdate) update;
assertEquals(dpid, swUpdate.getSwId());
assertEquals(SwitchUpdateType.PORTCHANGED, swUpdate.getSwitchUpdateType());
assertEquals(changeType, swUpdate.getPortChangeType());
}
}