/**
* 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.*;
import static org.junit.Assert.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import net.floodlightcontroller.core.FloodlightContext;
import net.floodlightcontroller.core.HAListenerTypeMarker;
import net.floodlightcontroller.core.IFloodlightProviderService;
import net.floodlightcontroller.core.IFloodlightProviderService.Role;
import net.floodlightcontroller.core.IHAListener;
import net.floodlightcontroller.core.IListener;
import net.floodlightcontroller.core.IListener.Command;
import net.floodlightcontroller.core.IOFMessageFilterManagerService;
import net.floodlightcontroller.core.IOFMessageListener;
import net.floodlightcontroller.core.IOFSwitch;
import net.floodlightcontroller.core.IOFSwitch.PortChangeType;
import net.floodlightcontroller.core.IOFSwitchDriver;
import net.floodlightcontroller.core.IOFSwitchListener;
import net.floodlightcontroller.core.IReadyForReconcileListener;
import net.floodlightcontroller.core.ImmutablePort;
import net.floodlightcontroller.core.OFMessageFilterManager;
import net.floodlightcontroller.core.RoleInfo;
import net.floodlightcontroller.core.SwitchSyncRepresentation;
import net.floodlightcontroller.core.module.FloodlightModuleContext;
import net.floodlightcontroller.core.test.MockFloodlightProvider;
import net.floodlightcontroller.core.test.MockThreadPoolService;
import net.floodlightcontroller.counter.CounterStore;
import net.floodlightcontroller.counter.ICounterStoreService;
import net.floodlightcontroller.debugcounter.DebugCounter;
import net.floodlightcontroller.debugcounter.IDebugCounterService;
import net.floodlightcontroller.debugevent.DebugEvent;
import net.floodlightcontroller.debugevent.IDebugEventService;
import net.floodlightcontroller.packet.ARP;
import net.floodlightcontroller.packet.Ethernet;
import net.floodlightcontroller.packet.IPacket;
import net.floodlightcontroller.packet.IPv4;
import net.floodlightcontroller.perfmon.IPktInProcessingTimeService;
import net.floodlightcontroller.perfmon.PktInProcessingTime;
import net.floodlightcontroller.restserver.IRestApiService;
import net.floodlightcontroller.restserver.RestApiServer;
import net.floodlightcontroller.storage.IStorageSourceService;
import net.floodlightcontroller.storage.memory.MemoryStorageSource;
import net.floodlightcontroller.test.FloodlightTestCase;
import net.floodlightcontroller.threadpool.IThreadPoolService;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.openflow.protocol.OFFeaturesReply;
import org.openflow.protocol.OFFlowMod;
import org.openflow.protocol.OFMessage;
import org.openflow.protocol.OFPacketIn;
import org.openflow.protocol.OFPacketIn.OFPacketInReason;
import org.openflow.protocol.OFPacketOut;
import org.openflow.protocol.OFPhysicalPort;
import org.openflow.protocol.OFPort;
import org.openflow.protocol.OFType;
import org.openflow.protocol.action.OFAction;
import org.openflow.protocol.action.OFActionOutput;
import org.openflow.protocol.factory.BasicFactory;
import org.openflow.protocol.statistics.OFDescriptionStatistics;
import org.openflow.util.HexString;
import org.sdnplatform.sync.IStoreClient;
import org.sdnplatform.sync.ISyncService;
import org.sdnplatform.sync.IStoreListener.UpdateType;
import org.sdnplatform.sync.test.MockSyncService;
public class ControllerTest extends FloodlightTestCase {
private Controller controller;
private MockThreadPoolService tp;
private MockSyncService syncService;
private IStoreClient<Long, SwitchSyncRepresentation> storeClient;
private IPacket testPacket;
private OFPacketIn pi;
@Override
@Before
public void setUp() throws Exception {
doSetUp(Role.MASTER);
}
public void doSetUp(Role role) throws Exception {
super.setUp();
FloodlightModuleContext fmc = new FloodlightModuleContext();
FloodlightProvider cm = new FloodlightProvider();
fmc.addConfigParam(cm, "role", role.toString());
controller = (Controller)cm.getServiceImpls().get(IFloodlightProviderService.class);
fmc.addService(IFloodlightProviderService.class, controller);
MemoryStorageSource memstorage = new MemoryStorageSource();
fmc.addService(IStorageSourceService.class, memstorage);
RestApiServer restApi = new RestApiServer();
fmc.addService(IRestApiService.class, restApi);
CounterStore cs = new CounterStore();
fmc.addService(ICounterStoreService.class, cs);
PktInProcessingTime ppt = new PktInProcessingTime();
fmc.addService(IPktInProcessingTimeService.class, ppt);
// TODO: should mock IDebugCounterService and make sure
// the expected counters are updated.
DebugCounter debugCounterService = new DebugCounter();
fmc.addService(IDebugCounterService.class, debugCounterService);
DebugEvent debugEventService = new DebugEvent();
fmc.addService(IDebugEventService.class, debugEventService);
tp = new MockThreadPoolService();
fmc.addService(IThreadPoolService.class, tp);
syncService = new MockSyncService();
fmc.addService(ISyncService.class, syncService);
ppt.init(fmc);
restApi.init(fmc);
memstorage.init(fmc);
tp.init(fmc);
debugCounterService.init(fmc);
debugEventService.init(fmc);
syncService.init(fmc);
cm.init(fmc);
ppt.startUp(fmc);
restApi.startUp(fmc);
memstorage.startUp(fmc);
tp.startUp(fmc);
debugCounterService.startUp(fmc);
debugEventService.startUp(fmc);
syncService.startUp(fmc);
cm.startUp(fmc);
storeClient =
syncService.getStoreClient(Controller.SWITCH_SYNC_STORE_NAME,
Long.class,
SwitchSyncRepresentation.class);
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();
pi = ((OFPacketIn) BasicFactory.getInstance().getMessage(OFType.PACKET_IN))
.setBufferId(-1)
.setInPort((short) 1)
.setPacketData(testPacketSerialized)
.setReason(OFPacketInReason.NO_MATCH)
.setTotalLength((short) testPacketSerialized.length);
}
@Override
@After
public void tearDown() {
tp.getScheduledExecutor().shutdownNow();
// Make sure thare are not left over updates in the queue
assertTrue("Updates left in controller update queue",
controller.isUpdateQueueEmptyForTesting());
}
public Controller getController() {
return controller;
}
private static OFDescriptionStatistics createOFDescriptionStatistics() {
OFDescriptionStatistics desc = new OFDescriptionStatistics();
desc.setDatapathDescription("");
desc.setHardwareDescription("");
desc.setManufacturerDescription("");
desc.setSerialNumber("");
desc.setSoftwareDescription("");
return desc;
}
private static OFFeaturesReply createOFFeaturesReply() {
OFFeaturesReply fr = new OFFeaturesReply();
fr.setPorts(Collections.<OFPhysicalPort>emptyList());
return fr;
}
/** Set the mock expectations for sw when sw is passed to addSwitch
* The same expectations can be used when a new SwitchSyncRepresentation
* is created from the given mocked switch */
protected void setupSwitchForAddSwitch(IOFSwitch sw, long dpid,
OFDescriptionStatistics desc,
OFFeaturesReply featuresReply) {
String dpidString = HexString.toHexString(dpid);
if (desc == null) {
desc = createOFDescriptionStatistics();
}
if (featuresReply == null) {
featuresReply = createOFFeaturesReply();
featuresReply.setDatapathId(dpid);
}
List<ImmutablePort> ports =
ImmutablePort.immutablePortListOf(featuresReply.getPorts());
expect(sw.getId()).andReturn(dpid).anyTimes();
expect(sw.getStringId()).andReturn(dpidString).anyTimes();
expect(sw.getDescriptionStatistics()) .andReturn(desc).atLeastOnce();
expect(sw.getBuffers())
.andReturn(featuresReply.getBuffers()).atLeastOnce();
expect(sw.getTables())
.andReturn(featuresReply.getTables()).atLeastOnce();
expect(sw.getCapabilities())
.andReturn(featuresReply.getCapabilities()).atLeastOnce();
expect(sw.getActions())
.andReturn(featuresReply.getActions()).atLeastOnce();
expect(sw.getPorts())
.andReturn(ports).atLeastOnce();
expect(sw.attributeEquals(IOFSwitch.SWITCH_SUPPORTS_NX_ROLE, true))
.andReturn(false).anyTimes();
expect(sw.getInetAddress()).andReturn(null).anyTimes();
}
@SuppressWarnings("unchecked")
private <T> void setupListenerOrdering(IListener<T> listener) {
listener.isCallbackOrderingPostreq((T)anyObject(),
anyObject(String.class));
expectLastCall().andReturn(false).anyTimes();
listener.isCallbackOrderingPrereq((T)anyObject(),
anyObject(String.class));
expectLastCall().andReturn(false).anyTimes();
}
@Test
public void testHandleMessagesNoListeners() throws Exception {
IOFSwitch sw = createMock(IOFSwitch.class);
expect(sw.getId()).andReturn(0L).anyTimes();
expect(sw.getStringId()).andReturn("00:00:00:00:00:00:00").anyTimes();
replay(sw);
controller.handleMessage(sw, pi, null);
verify(sw);
}
/**
* Test message dispatching to OFMessageListeners. Test ordering of
* listeners for different types (we do this implicitly by using
* STOP and CONTINUE and making sure the processing stops at the right
* place)
* Verify that a listener that throws an exception halts further
* execution, and verify that the Commands STOP and CONTINUE are honored.
* @throws Exception
*/
@Test
public void testHandleMessages() throws Exception {
controller.removeOFMessageListeners(OFType.PACKET_IN);
IOFSwitch sw = createMock(IOFSwitch.class);
expect(sw.getId()).andReturn(0L).anyTimes();
expect(sw.getStringId()).andReturn("00:00:00:00:00:00:00").anyTimes();
// Setup listener orderings
IOFMessageListener test1 = createMock(IOFMessageListener.class);
expect(test1.getName()).andReturn("test1").anyTimes();
setupListenerOrdering(test1);
IOFMessageListener test2 = createMock(IOFMessageListener.class);
expect(test2.getName()).andReturn("test2").anyTimes();
// using a postreq and a prereq ordering here
expect(test2.isCallbackOrderingPrereq(OFType.PACKET_IN, "test1"))
.andReturn(true).atLeastOnce();
expect(test2.isCallbackOrderingPostreq(OFType.FLOW_MOD, "test1"))
.andReturn(true).atLeastOnce();
setupListenerOrdering(test2);
IOFMessageListener test3 = createMock(IOFMessageListener.class);
expect(test3.getName()).andReturn("test3").anyTimes();
expect(test3.isCallbackOrderingPrereq((OFType)anyObject(), eq("test1")))
.andReturn(true).atLeastOnce();
expect(test3.isCallbackOrderingPrereq((OFType)anyObject(), eq("test2")))
.andReturn(true).atLeastOnce();
setupListenerOrdering(test3);
// Ordering: PacketIn: test1 -> test2 -> test3
// FlowMod: test2 -> test1
replay(test1, test2, test3);
controller.addOFMessageListener(OFType.PACKET_IN, test1);
controller.addOFMessageListener(OFType.PACKET_IN, test3);
controller.addOFMessageListener(OFType.PACKET_IN, test2);
controller.addOFMessageListener(OFType.FLOW_MOD, test1);
controller.addOFMessageListener(OFType.FLOW_MOD, test2);
verify(test1);
verify(test2);
verify(test3);
replay(sw);
//------------------
// Test PacketIn handling: all listeners return CONTINUE
reset(test1, test2, test3);
expect(test1.receive(eq(sw), eq(pi), isA(FloodlightContext.class)))
.andReturn(Command.CONTINUE);
expect(test2.receive(eq(sw), eq(pi), isA(FloodlightContext.class)))
.andReturn(Command.CONTINUE);
expect(test3.receive(eq(sw), eq(pi), isA(FloodlightContext.class)))
.andReturn(Command.CONTINUE);
replay(test1, test2, test3);
controller.handleMessage(sw, pi, null);
verify(test1);
verify(test2);
verify(test3);
//------------------
// Test PacketIn handling: with a thrown exception.
reset(test1, test2, test3);
expect(test1.receive(eq(sw), eq(pi), isA(FloodlightContext.class)))
.andReturn(Command.CONTINUE);
expect(test2.receive(eq(sw), eq(pi), isA(FloodlightContext.class)))
.andThrow(new RuntimeException("This is NOT an error! We " +
"are testing exception catching."));
// expect no calls to test3.receive() since test2.receive throws
// an exception
replay(test1, test2, test3);
try {
controller.handleMessage(sw, pi, null);
fail("Expected exception was not thrown!");
} catch (RuntimeException e) {
assertTrue("The caught exception was not the expected one",
e.getMessage().startsWith("This is NOT an error!"));
}
verify(test1);
verify(test2);
verify(test3);
//------------------
// Test PacketIn handling: test1 return Command.STOP
reset(test1, test2, test3);
expect(test1.receive(eq(sw), eq(pi), isA(FloodlightContext.class)))
.andReturn(Command.STOP);
// expect no calls to test3.receive() and test2.receive since
// test1.receive returns STOP
replay(test1, test2, test3);
controller.handleMessage(sw, pi, null);
verify(test1);
verify(test2);
verify(test3);
OFFlowMod fm = (OFFlowMod)
BasicFactory.getInstance().getMessage(OFType.FLOW_MOD);
//------------------
// Test FlowMod handling: all listeners return CONTINUE
reset(test1, test2, test3);
expect(test1.receive(eq(sw), eq(fm), isA(FloodlightContext.class)))
.andReturn(Command.CONTINUE);
expect(test2.receive(eq(sw), eq(fm), isA(FloodlightContext.class)))
.andReturn(Command.CONTINUE);
// test3 is not a listener for FlowMod
replay(test1, test2, test3);
controller.handleMessage(sw, fm, null);
verify(test1);
verify(test2);
verify(test3);
//------------------
// Test FlowMod handling: test2 (first listener) return STOP
reset(test1, test2, test3);
expect(test2.receive(eq(sw), eq(fm), isA(FloodlightContext.class)))
.andReturn(Command.STOP);
// test2 will not be called
// test3 is not a listener for FlowMod
replay(test1, test2, test3);
controller.handleMessage(sw, fm, null);
verify(test1);
verify(test2);
verify(test3);
verify(sw);
}
@Test
public void testHandleMessagesSlave() throws Exception {
doSetUp(Role.SLAVE);
IOFSwitch sw = createMock(IOFSwitch.class);
expect(sw.getId()).andReturn(0L).anyTimes();
expect(sw.getStringId()).andReturn("00:00:00:00:00:00:00").anyTimes();
IOFMessageListener test1 = createMock(IOFMessageListener.class);
expect(test1.getName()).andReturn("test1").atLeastOnce();
expect(test1.isCallbackOrderingPrereq((OFType)anyObject(),
(String)anyObject()))
.andReturn(false).atLeastOnce();
expect(test1.isCallbackOrderingPostreq((OFType)anyObject(),
(String)anyObject()))
.andReturn(false).atLeastOnce();
replay(test1, sw);
controller.addOFMessageListener(OFType.PACKET_IN, test1);
// message should not be dispatched
controller.handleMessage(sw, pi, null);
verify(test1);
//---------------------------------
// transition to Master
//--------------------------------
controller.setRole(Role.MASTER, "FooBar");
// transitioned but HA listeneres not yet notified.
// message should not be dispatched
reset(test1);
replay(test1);
controller.handleMessage(sw, pi, null);
verify(test1);
// notify HA listeners
controller.processUpdateQueueForTesting();
// no message should be dispatched
reset(test1);
expect(test1.receive(eq(sw), eq(pi), isA(FloodlightContext.class))).andReturn(Command.STOP);
replay(test1);
controller.handleMessage(sw, pi, null);
verify(test1);
verify(sw);
}
@Test
public void testHandleMessageWithContext() throws Exception {
IOFSwitch sw = createMock(IOFSwitch.class);
expect(sw.getId()).andReturn(0L).anyTimes();
expect(sw.getStringId()).andReturn("00:00:00:00:00:00:00").anyTimes();
IOFMessageListener test1 = createMock(IOFMessageListener.class);
expect(test1.getName()).andReturn("test1").anyTimes();
expect(test1.isCallbackOrderingPrereq((OFType)anyObject(),
(String)anyObject()))
.andReturn(false).anyTimes();
expect(test1.isCallbackOrderingPostreq((OFType)anyObject(),
(String)anyObject()))
.andReturn(false).anyTimes();
FloodlightContext cntx = new FloodlightContext();
expect(test1.receive(same(sw), same(pi) , same(cntx)))
.andReturn(Command.CONTINUE);
IOFMessageListener test2 = createMock(IOFMessageListener.class);
expect(test2.getName()).andReturn("test2").anyTimes();
expect(test2.isCallbackOrderingPrereq((OFType)anyObject(),
(String)anyObject()))
.andReturn(false).anyTimes();
expect(test2.isCallbackOrderingPostreq((OFType)anyObject(),
(String)anyObject()))
.andReturn(false).anyTimes();
// 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(testPacket.serialize(), eth.serialize());
}
/**
* Test injectMessage and also do some more tests for listener ordering
* and handling of Command.STOP
* @throws Exception
*/
@Test
public void testInjectMessage() throws Exception {
FloodlightContext cntx = new FloodlightContext();
IOFSwitch sw = createMock(IOFSwitch.class);
expect(sw.getId()).andReturn(0L).anyTimes();
expect(sw.getStringId()).andReturn("00:00:00:00:00:00:00").anyTimes();
// Add listeners
IOFMessageListener test1 = createMock(IOFMessageListener.class);
expect(test1.getName()).andReturn("test1").anyTimes();
setupListenerOrdering(test1);
IOFMessageListener test2 = createMock(IOFMessageListener.class);
expect(test2.getName()).andReturn("test2").anyTimes();
test2.isCallbackOrderingPostreq(OFType.PACKET_IN, "test1");
expectLastCall().andReturn(true).atLeastOnce();
setupListenerOrdering(test2);
replay(test1, test2);
controller.addOFMessageListener(OFType.PACKET_IN, test1);
controller.addOFMessageListener(OFType.PACKET_IN, test2);
verify(test1);
verify(test2);
// Test inject with null switch and no message. Should not work.
reset(test1, test2);
replay(test1, test2, sw);
try {
controller.injectOfMessage(null, pi);
fail("InjectOfMessage should have thrown a NPE");
} catch (NullPointerException e) {
// expected
}
try {
controller.injectOfMessage(null, pi, cntx);
fail("InjectOfMessage should have thrown a NPE");
} catch (NullPointerException e) {
// expected
}
try {
controller.injectOfMessage(sw, null);
fail("InjectOfMessage should have thrown a NPE");
} catch (NullPointerException e) {
// expected
}
try {
controller.injectOfMessage(sw, null, cntx);
fail("InjectOfMessage should have thrown a NPE");
} catch (NullPointerException e) {
// expected
}
verify(test1);
verify(test2);
verify(sw);
//
// Test inject with inActive switch. Should not work.
reset(test1, test2, sw);
expect(sw.getId()).andReturn(0L).anyTimes();
expect(sw.getStringId()).andReturn("00:00:00:00:00:00:00").anyTimes();
expect(sw.isActive()).andReturn(false).atLeastOnce();
replay(test1, test2, sw);
assertFalse("Inject should have failed",
controller.injectOfMessage(sw, pi));
assertFalse("Inject should have failed",
controller.injectOfMessage(sw, pi, cntx));
verify(test1);
verify(test2);
verify(sw);
// Test inject in the "normal" case without context
reset(test1, test2, sw);
expect(sw.getId()).andReturn(0L).anyTimes();
expect(sw.getStringId()).andReturn("00:00:00:00:00:00:00").anyTimes();
expect(sw.isActive()).andReturn(true).atLeastOnce();
expect(test2.receive(same(sw), same(pi) , isA(FloodlightContext.class)))
.andReturn(Command.STOP);
// test1 will not receive any message!
replay(test1, test2, sw);
assertTrue("Inject should have worked",
controller.injectOfMessage(sw, pi));
verify(test1);
verify(test2);
verify(sw);
// Test inject in the "normal" case with context
reset(test1, test2, sw);
expect(sw.getId()).andReturn(0L).anyTimes();
expect(sw.getStringId()).andReturn("00:00:00:00:00:00:00").anyTimes();
expect(sw.isActive()).andReturn(true).atLeastOnce();
expect(test2.receive(same(sw), same(pi) , same(cntx)))
.andReturn(Command.STOP);
// test1 will not receive any message!
replay(test1, test2, sw);
assertTrue("Inject should have worked",
controller.injectOfMessage(sw, pi, cntx));
verify(test1);
verify(test2);
verify(sw);
Ethernet eth = IFloodlightProviderService.bcStore.get(cntx,
IFloodlightProviderService.CONTEXT_PI_PAYLOAD);
assertArrayEquals(testPacket.serialize(), eth.serialize());
}
/**
* Test handleOutgoingMessage and also test listener ordering
* @throws Exception
*/
@Test
public void testHandleOutgoingMessage() throws Exception {
OFMessage m = BasicFactory.getInstance().getMessage(OFType.ECHO_REQUEST);
FloodlightContext cntx = new FloodlightContext();
IOFSwitch sw = createMock(IOFSwitch.class);
expect(sw.getId()).andReturn(0L).anyTimes();
expect(sw.getStringId()).andReturn("00:00:00:00:00:00:00").anyTimes();
// Add listeners
IOFMessageListener test1 = createMock(IOFMessageListener.class);
expect(test1.getName()).andReturn("test1").anyTimes();
setupListenerOrdering(test1);
IOFMessageListener test2 = createMock(IOFMessageListener.class);
expect(test2.getName()).andReturn("test2").anyTimes();
test2.isCallbackOrderingPostreq(OFType.ECHO_REQUEST, "test1");
expectLastCall().andReturn(true).atLeastOnce();
setupListenerOrdering(test2);
IOFMessageListener test3 = createMock(IOFMessageListener.class);
expect(test3.getName()).andReturn("test3").anyTimes();
test3.isCallbackOrderingPostreq(OFType.ECHO_REQUEST, "test2");
expectLastCall().andReturn(true).atLeastOnce();
setupListenerOrdering(test3);
// expected ordering is test3, test2, test1
replay(test1, test2, test3);
controller.addOFMessageListener(OFType.ECHO_REQUEST, test1);
controller.addOFMessageListener(OFType.ECHO_REQUEST, test3);
controller.addOFMessageListener(OFType.ECHO_REQUEST, test2);
verify(test1);
verify(test2);
verify(test3);
// Test inject with null switch and no message. Should not work.
reset(test1, test2, test3);
replay(test1, test2, test3, sw);
try {
controller.handleOutgoingMessage(null, pi, cntx);
fail("handleOutgoindMessage should have thrown a NPE");
} catch (NullPointerException e) {
// expected
}
try {
controller.handleOutgoingMessage(sw, null, cntx);
fail("handleOutgoingMessage should have thrown a NPE");
} catch (NullPointerException e) {
// expected
}
verify(test1);
verify(test2);
verify(test3);
verify(sw);
// Test the handleOutgoingMessage
reset(test1, test2, test3, sw);
expect(sw.getId()).andReturn(0L).anyTimes();
expect(sw.getStringId()).andReturn("00:00:00:00:00:00:00").anyTimes();
expect(test2.receive(same(sw), same(m) , same(cntx)))
.andReturn(Command.STOP);
expect(test3.receive(same(sw), same(m) , same(cntx)))
.andReturn(Command.CONTINUE);
// test1 will not receive any message!
replay(test1, test2, test3, sw);
controller.handleOutgoingMessage(sw, m, cntx);
verify(test1);
verify(test2);
verify(test3);
verify(sw);
// Test the handleOutgoingMessage with null context
reset(test1, test2, test3, sw);
expect(sw.getId()).andReturn(0L).anyTimes();
expect(sw.getStringId()).andReturn("00:00:00:00:00:00:00").anyTimes();
expect(test2.receive(same(sw), same(m) , isA(FloodlightContext.class)))
.andReturn(Command.STOP);
expect(test3.receive(same(sw), same(m) , isA(FloodlightContext.class)))
.andReturn(Command.CONTINUE);
// test1 will not receive any message!
replay(test1, test2, test3, sw);
controller.handleOutgoingMessage(sw, m, null);
verify(test1);
verify(test2);
verify(test3);
verify(sw);
// Test for message without listeners
reset(test1, test2, test3, sw);
replay(test1, test2, test3, sw);
m = BasicFactory.getInstance().getMessage(OFType.ECHO_REPLY);
controller.handleOutgoingMessage(sw, m, cntx);
verify(test1);
verify(test2);
verify(test3);
verify(sw);
}
@Test
public void testMessageFilterManager() throws Exception {
class MyOFMessageFilterManager extends OFMessageFilterManager {
public MyOFMessageFilterManager(int timer_interval) {
super();
TIMER_INTERVAL = timer_interval;
}
}
FloodlightModuleContext fmCntx = new FloodlightModuleContext();
MockFloodlightProvider mfp = new MockFloodlightProvider();
OFMessageFilterManager mfm = new MyOFMessageFilterManager(100);
MockThreadPoolService mtp = new MockThreadPoolService();
fmCntx.addService(IOFMessageFilterManagerService.class, mfm);
fmCntx.addService(IFloodlightProviderService.class, mfp);
fmCntx.addService(IThreadPoolService.class, mtp);
String sid = null;
mfm.init(fmCntx);
mfm.startUp(fmCntx);
ConcurrentHashMap <String, String> filter;
int i;
//Adding the filter works -- adds up to the maximum filter size.
for(i=mfm.getMaxFilterSize(); i > 0; --i) {
filter = new ConcurrentHashMap<String,String>();
filter.put("mac", String.format("00:11:22:33:44:%d%d", i,i));
sid = mfm.setupFilter(null, filter, 60);
assertTrue(mfm.getNumberOfFilters() == mfm.getMaxFilterSize() - i +1);
}
// Add one more to see if you can't
filter = new ConcurrentHashMap<String,String>();
filter.put("mac", "mac2");
mfm.setupFilter(null, filter, 10*1000);
assertTrue(mfm.getNumberOfFilters() == mfm.getMaxFilterSize());
// Deleting the filter works.
mfm.setupFilter(sid, null, -1);
assertTrue(mfm.getNumberOfFilters() == mfm.getMaxFilterSize()-1);
// Creating mock switch to which we will send packet out and
IOFSwitch sw = createMock(IOFSwitch.class);
expect(sw.getId()).andReturn(new Long(0));
// Mock Packet-in
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();
// Build the PacketIn
OFPacketIn pi = ((OFPacketIn) BasicFactory.getInstance().getMessage(OFType.PACKET_IN))
.setBufferId(-1)
.setInPort((short) 1)
.setPacketData(testPacketSerialized)
.setReason(OFPacketInReason.NO_MATCH)
.setTotalLength((short) testPacketSerialized.length);
// Mock Packet-out
OFPacketOut packetOut =
(OFPacketOut) mockFloodlightProvider.getOFMessageFactory().getMessage(OFType.PACKET_OUT);
packetOut.setBufferId(pi.getBufferId())
.setInPort(pi.getInPort());
List<OFAction> poactions = new ArrayList<OFAction>();
poactions.add(new OFActionOutput(OFPort.OFPP_TABLE.getValue(), (short) 0));
packetOut.setActions(poactions)
.setActionsLength((short) OFActionOutput.MINIMUM_LENGTH)
.setPacketData(testPacketSerialized)
.setLengthU(OFPacketOut.MINIMUM_LENGTH+packetOut.getActionsLength()+testPacketSerialized.length);
FloodlightContext cntx = new FloodlightContext();
IFloodlightProviderService.bcStore.put(cntx, IFloodlightProviderService.CONTEXT_PI_PAYLOAD, (Ethernet) testPacket);
// Let's check the listeners.
List <IOFMessageListener> lm;
// Check to see if all the listeners are active.
lm = mfp.getListeners().get(OFType.PACKET_OUT);
assertTrue(lm.size() == 1);
assertTrue(lm.get(0).equals(mfm));
lm = mfp.getListeners().get(OFType.FLOW_MOD);
assertTrue(lm.size() == 1);
assertTrue(lm.get(0).equals(mfm));
lm = mfp.getListeners().get(OFType.PACKET_IN);
assertTrue(lm.size() == 1);
assertTrue(lm.get(0).equals(mfm));
HashSet<String> matchedFilters;
// Send a packet in and check if it matches a filter.
matchedFilters = mfm.getMatchedFilters(pi, cntx);
assertTrue(matchedFilters.size() == 1);
// Send a packet out and check if it matches a filter
matchedFilters = mfm.getMatchedFilters(packetOut, cntx);
assertTrue(matchedFilters.size() == 1);
// Wait for all filters to be timed out.
Thread.sleep(150);
assertEquals(0, mfm.getNumberOfFilters());
}
@Test
public void testGetRoleInfoDefault() {
RoleInfo info = controller.getRoleInfo();
assertEquals(Role.MASTER.toString(), info.getRole());
assertNotNull(info.getRoleChangeDescription());
assertEquals(Role.MASTER, controller.getRole());
// FIXME: RoleInfo's date. but the format is kinda broken
}
/**
* Test interaction with OFChannelHandler when the current role is
* master.
*/
@Test
public void testChannelHandlerMaster() {
OFChannelHandler h = createMock(OFChannelHandler.class);
// Add the handler. The controller should call sendRoleRequest
h.sendRoleRequest(Role.MASTER);
expectLastCall().once();
replay(h);
controller.addSwitchChannelAndSendInitialRole(h);
verify(h);
// Reassert the role.
reset(h);
h.sendRoleRequestIfNotPending(Role.MASTER);
replay(h);
controller.reassertRole(h, Role.MASTER);
verify(h);
// reassert a different role: no-op
reset(h);
replay(h);
controller.reassertRole(h, Role.SLAVE);
verify(h);
}
/**
* Start as SLAVE then set role to MASTER
* Tests normal role change transition. Check that connected channels
* receive a setRole request
*/
@Test
public void testSetRole() throws Exception {
doSetUp(Role.SLAVE);
RoleInfo info = controller.getRoleInfo();
assertEquals(Role.SLAVE.toString(), info.getRole());
assertEquals(Role.SLAVE, controller.getRole());
OFChannelHandler h = createMock(OFChannelHandler.class);
// Add the channel handler. The controller should call sendRoleRequest
h.sendRoleRequest(Role.SLAVE);
expectLastCall().once();
replay(h);
controller.addSwitchChannelAndSendInitialRole(h);
verify(h);
// Reassert the role.
reset(h);
h.sendRoleRequestIfNotPending(Role.SLAVE);
replay(h);
controller.reassertRole(h, Role.SLAVE);
verify(h);
// reassert a different role: no-op
reset(h);
replay(h);
controller.reassertRole(h, Role.MASTER);
verify(h);
// Change role to MASTER
reset(h);
h.sendRoleRequest(Role.MASTER);
expectLastCall().once();
IHAListener listener = createMock(IHAListener.class);
expect(listener.getName()).andReturn("foo").anyTimes();
setupListenerOrdering(listener);
listener.transitionToMaster();
expectLastCall().once();
replay(listener);
replay(h);
controller.addHAListener(listener);
controller.setRole(Role.MASTER, "FooBar");
controller.processUpdateQueueForTesting();
verify(h);
verify(listener);
info = controller.getRoleInfo();
assertEquals(Role.MASTER.toString(), info.getRole());
assertEquals("FooBar", info.getRoleChangeDescription());
assertEquals(Role.MASTER, controller.getRole());
}
/** Test other setRole cases: re-setting role to the current role,
* setting role to equal, etc.
*/
@Test
public void testSetRoleOthercases() throws Exception {
doSetUp(Role.SLAVE);
OFChannelHandler h = createMock(OFChannelHandler.class);
// Add the channel handler. The controller should call sendRoleRequest
h.sendRoleRequest(Role.SLAVE);
expectLastCall().once();
replay(h);
controller.addSwitchChannelAndSendInitialRole(h);
verify(h);
// remove the channel. Nothing should
reset(h);
replay(h);
controller.removeSwitchChannel(h);
// Create and add the HA listener
IHAListener listener = createMock(IHAListener.class);
expect(listener.getName()).andReturn("foo").anyTimes();
setupListenerOrdering(listener);
replay(listener);
controller.addHAListener(listener);
// Set role to slave again. Nothing should happen
controller.setRole(Role.SLAVE, "FooBar");
controller.processUpdateQueueForTesting();
verify(listener);
reset(listener);
listener.transitionToMaster();
expectLastCall().once();
replay(listener);
// set role to equal. Should set to master internally
controller.setRole(Role.EQUAL, "ToEqual");
controller.processUpdateQueueForTesting();
verify(listener);
RoleInfo info = controller.getRoleInfo();
assertEquals(Role.MASTER.toString(), info.getRole());
assertEquals("ToEqual", info.getRoleChangeDescription());
assertEquals(Role.MASTER, controller.getRole());
verify(h); // no calls should have happened on h
}
@Test
public void testSetRoleNPE() {
try {
controller.setRole(null, "");
fail("Should have thrown an Exception");
}
catch (NullPointerException e) {
//exptected
}
try {
controller.setRole(Role.MASTER, null);
fail("Should have thrown an Exception");
}
catch (NullPointerException e) {
//exptected
}
}
@Test
/**
* Test switchActivated for a new switch, i.e., a switch that was not
* previously known to the controller cluser. We expect that all
* flow mods are cleared and we expect a switchAdded
*/
public void testNewSwitchActivated() throws Exception {
// We set AlwaysClearFlowsOnSwActivate to false but we still
// expect a clearAllFlowMods() because the AlwaysClearFlowsOnSwActivate
// is only relevant if a switch that was previously known is activated!!
controller.setAlwaysClearFlowsOnSwActivate(false);
IOFSwitch sw = createMock(IOFSwitch.class);
setupSwitchForAddSwitch(sw, 0L, null, null);
sw.clearAllFlowMods();
expectLastCall().once();
// strict mock. Order of events matters!
IOFSwitchListener listener = createStrictMock(IOFSwitchListener.class);
listener.switchAdded(0L);
expectLastCall().once();
listener.switchActivated(0L);
expectLastCall().once();
replay(listener);
controller.addOFSwitchListener(listener);
replay(sw);
controller.switchActivated(sw);
verify(sw);
assertEquals(sw, controller.getSwitch(0L));
controller.processUpdateQueueForTesting();
verify(listener);
SwitchSyncRepresentation storedSwitch = storeClient.getValue(0L);
assertEquals(createOFFeaturesReply(), storedSwitch.getFeaturesReply());
assertEquals(createOFDescriptionStatistics(),
storedSwitch.getDescription());
}
/**
* Test switchActivated for a new switch while in slave: a no-op
*/
@Test
public void testNewSwitchActivatedWhileSlave() throws Exception {
doSetUp(Role.SLAVE);
IOFSwitch sw = createMock(IOFSwitch.class);
IOFSwitchListener listener = createMock(IOFSwitchListener.class);
controller.addOFSwitchListener(listener);
replay(sw, listener); // nothing recorded
controller.switchActivated(sw);
verify(sw);
verify(listener);
}
/**
* Create and activate a switch, either completely new or reconnected
* The mocked switch instance will be returned. It wil be reset.
*/
private IOFSwitch doActivateSwitchInt(long dpid,
OFDescriptionStatistics desc,
OFFeaturesReply featuresReply,
boolean clearFlows)
throws Exception {
controller.setAlwaysClearFlowsOnSwActivate(true);
IOFSwitch sw = createMock(IOFSwitch.class);
if (featuresReply == null) {
featuresReply = createOFFeaturesReply();
featuresReply.setDatapathId(dpid);
}
if (desc == null) {
desc = createOFDescriptionStatistics();
}
setupSwitchForAddSwitch(sw, dpid, desc, featuresReply);
if (clearFlows) {
sw.clearAllFlowMods();
expectLastCall().once();
}
replay(sw);
controller.switchActivated(sw);
verify(sw);
assertEquals(sw, controller.getSwitch(dpid));
// drain updates and ignore
controller.processUpdateQueueForTesting();
SwitchSyncRepresentation storedSwitch = storeClient.getValue(dpid);
assertEquals(featuresReply, storedSwitch.getFeaturesReply());
assertEquals(desc, storedSwitch.getDescription());
reset(sw);
return sw;
}
/**
* Create and activate a new switch with the given dpid, features reply
* and description. If description and/or features reply are null we'll
* allocate the default one
* The mocked switch instance will be returned. It wil be reset.
*/
private IOFSwitch doActivateNewSwitch(long dpid,
OFDescriptionStatistics desc,
OFFeaturesReply featuresReply)
throws Exception {
return doActivateSwitchInt(dpid, desc, featuresReply, true);
}
/**
* Create and activate a switch that's just been disconnected.
* The mocked switch instance will be returned. It wil be reset.
*/
private IOFSwitch doActivateOldSwitch(long dpid,
OFDescriptionStatistics desc,
OFFeaturesReply featuresReply)
throws Exception {
return doActivateSwitchInt(dpid, desc, featuresReply, false);
}
/**
* Create a switch sync representation and add it to the store and
* notify the store listener.
* If the description and/or features reply are null, we'll allocate
* the default one
*/
public void doAddSwitchToStore(long dpid,
OFDescriptionStatistics desc,
OFFeaturesReply featuresReply)
throws Exception {
if (featuresReply == null) {
featuresReply = createOFFeaturesReply();
featuresReply.setDatapathId(dpid);
}
if (desc == null) {
desc = createOFDescriptionStatistics();
}
SwitchSyncRepresentation ssr =
new SwitchSyncRepresentation(featuresReply, desc);
storeClient.put(dpid, ssr);
Iterator<Long> keysToNotify = Collections.singletonList(dpid).iterator();
controller.getStoreListener().keysModified(keysToNotify,
UpdateType.REMOTE);
}
/**
* Remove a switch from the sync store and
* notify the store listener.
*/
public void doRemoveSwitchFromStore(long dpid) throws Exception {
storeClient.delete(dpid);
Iterator<Long> keysToNotify = Collections.singletonList(dpid).iterator();
controller.getStoreListener().keysModified(keysToNotify,
UpdateType.REMOTE);
}
/** (remotely) add switch to store and then remove while master. no-op */
@Test
public void testAddSwitchToStoreMaster() throws Exception {
IOFSwitchListener listener = createMock(IOFSwitchListener.class);
controller.addOFSwitchListener(listener);
replay(listener);
//--------------
// add switch
doAddSwitchToStore(1L, null, null);
controller.processUpdateQueueForTesting();
IOFSwitch sw = controller.getSwitch(1L);
assertNull("There shouldn't be a switch", sw);
verify(listener);
//--------------
// add a real switch
controller.setAlwaysClearFlowsOnSwActivate(true);
sw = createMock(IOFSwitch.class);
setupSwitchForAddSwitch(sw, 1L, null, null);
sw.clearAllFlowMods();
expectLastCall().once();
reset(listener);
listener.switchAdded(1L);
expectLastCall().once();
listener.switchActivated(1L);
expectLastCall().once();
replay(listener);
replay(sw);
controller.switchActivated(sw);
verify(sw);
assertEquals(sw, controller.getSwitch(1L));
controller.processUpdateQueueForTesting();
verify(listener);
//-----------
// remove switch from store.
reset(listener);
replay(listener);
doRemoveSwitchFromStore(1L);
controller.processUpdateQueueForTesting();
verify(listener);
assertEquals(sw, controller.getSwitch(1L));
}
/**
* add switch to store then remove it again while slave.
* should get notification and switch should be added and then removed
*/
@Test
public void testAddSwitchRemoveSwitchStoreSlave() throws Exception {
doSetUp(Role.SLAVE);
IOFSwitchListener listener = createMock(IOFSwitchListener.class);
controller.addOFSwitchListener(listener);
//------
// Add switch
listener.switchAdded(1L);
expectLastCall().once();
replay(listener);
OFDescriptionStatistics desc = createOFDescriptionStatistics();
desc.setDatapathDescription("The Switch");
doAddSwitchToStore(1L, desc, null);
controller.processUpdateQueueForTesting();
verify(listener);
IOFSwitch sw = controller.getSwitch(1L);
assertNotNull("Switch should be present", sw);
assertEquals(1L, sw.getId());
assertFalse("Switch should be inactive", sw.isActive());
assertEquals("The Switch",
sw.getDescriptionStatistics().getDatapathDescription());
//------
// remove switch
reset(listener);
listener.switchRemoved(1L);
replay(listener);
doRemoveSwitchFromStore(1L);
controller.processUpdateQueueForTesting();
verify(listener);
assertNull("Switch should not exist anymore", controller.getSwitch(1L));
}
/** Add switch to store with inconsistent DPID
* @throws Exception
*/
@Test
public void testInconsistentStoreDpid() throws Exception {
doSetUp(Role.SLAVE);
IOFSwitchListener listener = createMock(IOFSwitchListener.class);
controller.addOFSwitchListener(listener);
replay(listener);
OFFeaturesReply featuresReply = createOFFeaturesReply();
featuresReply.setDatapathId(42L);
OFDescriptionStatistics desc = createOFDescriptionStatistics();
SwitchSyncRepresentation ssr =
new SwitchSyncRepresentation(featuresReply, desc);
storeClient.put(1L, ssr);
Iterator<Long> keysToNotify = Collections.singletonList(1L).iterator();
controller.getStoreListener().keysModified(keysToNotify,
UpdateType.REMOTE);
controller.processUpdateQueueForTesting();
verify(listener);
assertNull("Switch should not have been added",
controller.getSwitch(1L));
assertNull("Switch should not have been added",
controller.getSwitch(42L));
}
/**
* This test goes through the SLAVE->MASTER program flow. We'll start as
* SLAVE. Add switches to the store while slave, update these switches
* then transition to master, make most (but not all switches) "connect"
* We also check correct behavior of getAllSwitchDpids() and
* getAllSwitchMap()
*
* We also change ports to verify that we receive port changed notifications
* if ports are changes in the sync store or when we transition from
* inactive to active
*/
@Test
public void testSwitchAddWithRoleChangeSomeReconnect() throws Exception {
int consolidateStoreDelayMs = 50;
doSetUp(Role.SLAVE);
// Add HA Listener
IHAListener haListener = createMock(IHAListener.class);
expect(haListener.getName()).andReturn("foo").anyTimes();
setupListenerOrdering(haListener);
replay(haListener);
controller.addHAListener(haListener);
verify(haListener);
reset(haListener);
// Add switch listener
IOFSwitchListener switchListener = createMock(IOFSwitchListener.class);
controller.addOFSwitchListener(switchListener);
// Add readyForReconcile listener
IReadyForReconcileListener readyForReconcileListener =
createMock(IReadyForReconcileListener.class);
controller.addReadyForReconcileListener(readyForReconcileListener);
//---------------------------------------
// Initialization
//---------------------------------------
// Switch 1
// no actual IOFSwitch here because we simply add features reply
// and desc stats to store
OFFeaturesReply fr1a = createOFFeaturesReply();
fr1a.setDatapathId(1L);
OFPhysicalPort p = createOFPhysicalPort("P1", 1);
ImmutablePort sw1p1 = ImmutablePort.fromOFPhysicalPort(p);
List<OFPhysicalPort> ports1a = Collections.singletonList(p);
fr1a.setPorts(ports1a);
List<ImmutablePort> ports1aImmutable =
ImmutablePort.immutablePortListOf(ports1a);
// an alternative featuers reply
OFFeaturesReply fr1b = createOFFeaturesReply();
fr1b.setDatapathId(1L);
p = new OFPhysicalPort();
p = createOFPhysicalPort("P1", 1); // same port as above
List<OFPhysicalPort> ports1b = new ArrayList<OFPhysicalPort>();
ports1b.add(p);
p = createOFPhysicalPort("P2", 42000);
ImmutablePort sw1p2 = ImmutablePort.fromOFPhysicalPort(p);
ports1b.add(p);
fr1b.setPorts(ports1b);
List<ImmutablePort> ports1bImmutable =
ImmutablePort.immutablePortListOf(ports1b);
// Switch 2
// no actual IOFSwitch here because we simply add features reply
// and desc stats to store
OFFeaturesReply fr2a = createOFFeaturesReply();
fr2a.setDatapathId(2L);
ImmutablePort sw2p1 = sw1p1;
List<OFPhysicalPort> ports2a = new ArrayList<OFPhysicalPort>(ports1a);
fr2a.setPorts(ports2a);
List<ImmutablePort> ports2aImmutable =
ImmutablePort.immutablePortListOf(ports2a);
// an alternative features reply
OFFeaturesReply fr2b = createOFFeaturesReply();
fr2b.setDatapathId(2L);
p = new OFPhysicalPort();
p = createOFPhysicalPort("P1", 2); // port number changed
ImmutablePort sw2p1Changed = ImmutablePort.fromOFPhysicalPort(p);
List<OFPhysicalPort> ports2b = Collections.singletonList(p);
fr2b.setPorts(ports2b);
// Switches 3 and 4 are create with default features reply and desc
// so nothing to do here
//---------------------------------------
// Adding switches to store
//---------------------------------------
replay(haListener); // nothing should happen to haListener
replay(readyForReconcileListener); // nothing should happen to
// readyForReconcileListener
// add switch1 with fr1a to store
reset(switchListener);
switchListener.switchAdded(1L);
expectLastCall().once();
replay(switchListener);
doAddSwitchToStore(1L, null, fr1a);
controller.processUpdateQueueForTesting();
verify(switchListener);
reset(switchListener);
IOFSwitch sw = controller.getSwitch(1L);
assertNotNull("Switch should be present", sw);
assertEquals(1L, sw.getId());
assertFalse("Switch should be inactive", sw.isActive());
assertEquals(new HashSet<ImmutablePort>(ports1aImmutable),
new HashSet<ImmutablePort>(sw.getPorts()));
// add switch 2 with fr2a to store
reset(switchListener);
switchListener.switchAdded(2L);
expectLastCall().once();
replay(switchListener);
doAddSwitchToStore(2L, null, fr2a);
controller.processUpdateQueueForTesting();
verify(switchListener);
reset(switchListener);
sw = controller.getSwitch(2L);
assertNotNull("Switch should be present", sw);
assertEquals(2L, sw.getId());
assertFalse("Switch should be inactive", sw.isActive());
assertEquals(new HashSet<ImmutablePort>(ports2aImmutable),
new HashSet<ImmutablePort>(sw.getPorts()));
// add switch 3 to store
reset(switchListener);
switchListener.switchAdded(3L);
expectLastCall().once();
replay(switchListener);
doAddSwitchToStore(3L, null, null);
controller.processUpdateQueueForTesting();
verify(switchListener);
reset(switchListener);
sw = controller.getSwitch(3L);
assertNotNull("Switch should be present", sw);
assertEquals(3L, sw.getId());
assertFalse("Switch should be inactive", sw.isActive());
// add switch 4 to store
reset(switchListener);
switchListener.switchAdded(4L);
expectLastCall().once();
replay(switchListener);
doAddSwitchToStore(4L, null, null);
controller.processUpdateQueueForTesting();
verify(switchListener);
reset(switchListener);
sw = controller.getSwitch(4L);
assertNotNull("Switch should be present", sw);
assertEquals(4L, sw.getId());
assertFalse("Switch should be inactive", sw.isActive());
// update switch 1 with fr1b
reset(switchListener);
switchListener.switchPortChanged(1L, sw1p2, PortChangeType.ADD);
expectLastCall().once();
replay(switchListener);
doAddSwitchToStore(1L, null, fr1b);
controller.processUpdateQueueForTesting();
verify(switchListener);
reset(switchListener);
sw = controller.getSwitch(1L);
assertNotNull("Switch should be present", sw);
assertEquals(1L, sw.getId());
assertFalse("Switch should be inactive", sw.isActive());
assertEquals(new HashSet<ImmutablePort>(ports1bImmutable),
new HashSet<ImmutablePort>(sw.getPorts()));
// Check getAllSwitchDpids() and getAllSwitchMap()
Set<Long> expectedDpids = new HashSet<Long>();
expectedDpids.add(1L);
expectedDpids.add(2L);
expectedDpids.add(3L);
expectedDpids.add(4L);
assertEquals(expectedDpids, controller.getAllSwitchDpids());
Map<Long, IOFSwitch> expectedSwitchMap = new HashMap<Long, IOFSwitch>();
expectedSwitchMap.put(1L, controller.getSwitch(1L));
expectedSwitchMap.put(2L, controller.getSwitch(2L));
expectedSwitchMap.put(3L, controller.getSwitch(3L));
expectedSwitchMap.put(4L, controller.getSwitch(4L));
assertEquals(expectedSwitchMap, controller.getAllSwitchMap());
verify(haListener);
//--------------------------------------
// Transition to master
//--------------------------------------
reset(haListener);
haListener.transitionToMaster();
expectLastCall().once();
replay(haListener);
controller.setConsolidateStoreTaskDelay(consolidateStoreDelayMs);
controller.setRole(Role.MASTER, "FooBar");
controller.processUpdateQueueForTesting();
verify(haListener);
reset(haListener);
replay(haListener);
//--------------------------------------
// Activate switches
//--------------------------------------
// Activate switch 1
IOFSwitch sw1 = createMock(IOFSwitch.class);
setupSwitchForAddSwitch(sw1, 1L, null, fr1b);
reset(switchListener);
switchListener.switchActivated(1L);
expectLastCall().once();
replay(sw1);
replay(switchListener);
controller.switchActivated(sw1);
controller.processUpdateQueueForTesting();
verify(switchListener);
verify(sw1);
sw = controller.getSwitch(1L);
assertNotNull("Switch should be present", sw);
assertSame(sw1, sw); // the mock switch should be returned
// Activate switch 2 with different features reply
// should get portChanged
// also set alwaysClearFlorModOnSwAcitvate to true;
controller.setAlwaysClearFlowsOnSwActivate(true);
IOFSwitch sw2 = createMock(IOFSwitch.class);
setupSwitchForAddSwitch(sw2, 2L, null, fr2b);
sw2.clearAllFlowMods();
expectLastCall().once();
reset(switchListener);
switchListener.switchActivated(2L);
expectLastCall().once();
switchListener.switchPortChanged(2L, sw2p1, PortChangeType.DELETE);
switchListener.switchPortChanged(2L, sw2p1Changed, PortChangeType.ADD);
expectLastCall().once();
replay(sw2);
replay(switchListener);
controller.switchActivated(sw2);
controller.processUpdateQueueForTesting();
verify(switchListener);
verify(sw2);
sw = controller.getSwitch(2L);
assertNotNull("Switch should be present", sw);
assertSame(sw2, sw); // the mock switch should be returned
// Do not activate switch 3, but it should still be present
sw = controller.getSwitch(3L);
IOFSwitch sw3 = sw;
assertNotNull("Switch should be present", sw);
assertEquals(3L, sw.getId());
assertFalse("Switch should be inactive", sw.isActive());
// Do not activate switch 4, but it should still be present
sw = controller.getSwitch(4L);
IOFSwitch sw4 = sw;
assertNotNull("Switch should be present", sw);
assertEquals(4L, sw.getId());
assertFalse("Switch should be inactive", sw.isActive());
// Check getAllSwitchDpids() and getAllSwitchMap()
expectedDpids = new HashSet<Long>();
expectedDpids.add(1L);
expectedDpids.add(2L);
expectedDpids.add(3L);
expectedDpids.add(4L);
assertEquals(expectedDpids, controller.getAllSwitchDpids());
expectedSwitchMap = new HashMap<Long, IOFSwitch>();
expectedSwitchMap.put(1L, sw1);
expectedSwitchMap.put(2L, sw2);
expectedSwitchMap.put(3L, sw3);
expectedSwitchMap.put(4L, sw4);
assertEquals(expectedSwitchMap, controller.getAllSwitchMap());
// silently remove switch 4 from the store and notify the
// store listener. Since the controller is MASTER it will ignore
// this notification.
reset(switchListener);
replay(switchListener);
doRemoveSwitchFromStore(4L);
controller.processUpdateQueueForTesting();
verify(switchListener);
// Switch should still be queryable
sw = controller.getSwitch(4L);
assertNotNull("Switch should be present", sw);
assertEquals(4L, sw.getId());
assertFalse("Switch should be inactive", sw.isActive());
//--------------------------------
// Wait for consolidateStore
//--------------------------------
verify(readyForReconcileListener);
reset(readyForReconcileListener);
readyForReconcileListener.readyForReconcile();
replay(readyForReconcileListener);
reset(switchListener);
switchListener.switchRemoved(3L);
switchListener.switchRemoved(4L);
replay(switchListener);
Thread.sleep(2*consolidateStoreDelayMs);
controller.processUpdateQueueForTesting();
verify(switchListener);
verify(readyForReconcileListener);
reset(readyForReconcileListener);
replay(readyForReconcileListener);
// Verify the expected switches are all there. no more no less
sw = controller.getSwitch(1L);
assertNotNull("Switch should be present", sw);
assertSame(sw1, sw); // the mock switch should be returned
sw = controller.getSwitch(2L);
assertNotNull("Switch should be present", sw);
assertSame(sw2, sw); // the mock switch should be returned
// Do not activate switch 3, but it should still be present
sw = controller.getSwitch(3L);
assertNull("Switch should NOT be present", sw);
// Check getAllSwitchDpids() and getAllSwitchMap()
expectedDpids = new HashSet<Long>();
expectedDpids.add(1L);
expectedDpids.add(2L);
assertEquals(expectedDpids, controller.getAllSwitchDpids());
expectedSwitchMap = new HashMap<Long, IOFSwitch>();
expectedSwitchMap.put(1L, sw1);
expectedSwitchMap.put(2L, sw2);
assertEquals(expectedSwitchMap, controller.getAllSwitchMap());
verify(haListener);
verify(readyForReconcileListener);
}
/**
* This test goes through the SLAVE->MASTER program flow. We'll start as
* SLAVE. Add switches to the store while slave, update these switches
* then transition to master, make all "connect"
*
* Supplements testSwitchAddWithRoleChangeSomeReconnect() and thus does
* less extensive testing. We are really only interested in verifying
* that we get the readyForReconciliation event before
* consolidateStore runs.
*/
@Test
public void testSwitchAddWithRoleChangeAllReconnect() throws Exception {
int consolidateStoreDelayMs = 50;
doSetUp(Role.SLAVE);
// Add HA Listener
IHAListener haListener = createMock(IHAListener.class);
expect(haListener.getName()).andReturn("foo").anyTimes();
setupListenerOrdering(haListener);
replay(haListener);
controller.addHAListener(haListener);
verify(haListener);
reset(haListener);
// Add switch listener
IOFSwitchListener switchListener = createMock(IOFSwitchListener.class);
controller.addOFSwitchListener(switchListener);
// Add readyForReconcile listener
IReadyForReconcileListener readyForReconcileListener =
createMock(IReadyForReconcileListener.class);
controller.addReadyForReconcileListener(readyForReconcileListener);
//---------------------------------------
// Adding switches to store
//---------------------------------------
replay(haListener); // nothing should happen to haListener
replay(readyForReconcileListener); // nothing should happen to
// readyForReconcileListener
// add switch 1 to store
reset(switchListener);
switchListener.switchAdded(1L);
expectLastCall().once();
replay(switchListener);
doAddSwitchToStore(1L, null, null);
controller.processUpdateQueueForTesting();
verify(switchListener);
reset(switchListener);
IOFSwitch sw = controller.getSwitch(1L);
assertNotNull("Switch should be present", sw);
assertEquals(1L, sw.getId());
assertFalse("Switch should be inactive", sw.isActive());
// add switch 2 to store
reset(switchListener);
switchListener.switchAdded(2L);
expectLastCall().once();
replay(switchListener);
doAddSwitchToStore(2L, null, null);
controller.processUpdateQueueForTesting();
verify(switchListener);
reset(switchListener);
sw = controller.getSwitch(2L);
assertNotNull("Switch should be present", sw);
assertEquals(2L, sw.getId());
assertFalse("Switch should be inactive", sw.isActive());
// Check getAllSwitchDpids() and getAllSwitchMap()
Set<Long> expectedDpids = new HashSet<Long>();
expectedDpids.add(1L);
expectedDpids.add(2L);
assertEquals(expectedDpids, controller.getAllSwitchDpids());
Map<Long, IOFSwitch> expectedSwitchMap = new HashMap<Long, IOFSwitch>();
expectedSwitchMap.put(1L, controller.getSwitch(1L));
expectedSwitchMap.put(2L, controller.getSwitch(2L));
assertEquals(expectedSwitchMap, controller.getAllSwitchMap());
verify(haListener);
//--------------------------------------
// Transition to master
//--------------------------------------
reset(haListener);
haListener.transitionToMaster();
expectLastCall().once();
replay(haListener);
controller.setConsolidateStoreTaskDelay(consolidateStoreDelayMs);
controller.setRole(Role.MASTER, "FooBar");
controller.processUpdateQueueForTesting();
verify(haListener);
reset(haListener);
replay(haListener);
//--------------------------------------
// Activate switches
//--------------------------------------
// Activate switch 1
IOFSwitch sw1 = createMock(IOFSwitch.class);
setupSwitchForAddSwitch(sw1, 1L, null, null);
reset(switchListener);
switchListener.switchActivated(1L);
expectLastCall().once();
replay(sw1);
replay(switchListener);
controller.switchActivated(sw1);
controller.processUpdateQueueForTesting();
verify(switchListener);
verify(sw1);
sw = controller.getSwitch(1L);
assertNotNull("Switch should be present", sw);
assertSame(sw1, sw); // the mock switch should be returned
// Activate switch 2
// Since this is the last inactive switch to activate we should
// get the readyForReconcile notifiction
verify(readyForReconcileListener);
reset(readyForReconcileListener);
readyForReconcileListener.readyForReconcile();
replay(readyForReconcileListener);
controller.setAlwaysClearFlowsOnSwActivate(true);
IOFSwitch sw2 = createMock(IOFSwitch.class);
setupSwitchForAddSwitch(sw2, 2L, null, null);
sw2.clearAllFlowMods();
expectLastCall().once();
reset(switchListener);
switchListener.switchActivated(2L);
expectLastCall().once();
replay(sw2);
replay(switchListener);
controller.switchActivated(sw2);
controller.processUpdateQueueForTesting();
verify(switchListener);
verify(sw2);
verify(readyForReconcileListener);
sw = controller.getSwitch(2L);
assertNotNull("Switch should be present", sw);
assertSame(sw2, sw); // the mock switch should be returned
// Check getAllSwitchDpids() and getAllSwitchMap()
expectedDpids = new HashSet<Long>();
expectedDpids.add(1L);
expectedDpids.add(2L);
assertEquals(expectedDpids, controller.getAllSwitchDpids());
expectedSwitchMap = new HashMap<Long, IOFSwitch>();
expectedSwitchMap.put(1L, sw1);
expectedSwitchMap.put(2L, sw2);
assertEquals(expectedSwitchMap, controller.getAllSwitchMap());
//--------------------------------
// Wait for consolidateStore: a no-op
//--------------------------------
reset(switchListener);
replay(switchListener);
reset(readyForReconcileListener);
replay(readyForReconcileListener);
Thread.sleep(2*consolidateStoreDelayMs);
controller.processUpdateQueueForTesting();
verify(switchListener);
verify(readyForReconcileListener);
// Verify the expected switches are all there. no more no less
sw = controller.getSwitch(1L);
assertNotNull("Switch should be present", sw);
assertSame(sw1, sw); // the mock switch should be returned
sw = controller.getSwitch(2L);
assertNotNull("Switch should be present", sw);
assertSame(sw2, sw); // the mock switch should be returned
// Check getAllSwitchDpids() and getAllSwitchMap()
expectedDpids = new HashSet<Long>();
expectedDpids.add(1L);
expectedDpids.add(2L);
assertEquals(expectedDpids, controller.getAllSwitchDpids());
expectedSwitchMap = new HashMap<Long, IOFSwitch>();
expectedSwitchMap.put(1L, sw1);
expectedSwitchMap.put(2L, sw2);
assertEquals(expectedSwitchMap, controller.getAllSwitchMap());
verify(haListener);
}
/**
* Disconnect a switch. normal program flow
*/
@Test
private void doTestSwitchConnectReconnect(boolean reconnect)
throws Exception {
IOFSwitch sw = doActivateNewSwitch(1L, null, null);
expect(sw.getId()).andReturn(1L).anyTimes();
expect(sw.getStringId()).andReturn(HexString.toHexString(1L)).anyTimes();
sw.cancelAllStatisticsReplies();
expectLastCall().once();
IOFSwitchListener listener = createMock(IOFSwitchListener.class);
listener.switchRemoved(1L);
expectLastCall().once();
controller.addOFSwitchListener(listener);
replay(sw, listener);
controller.switchDisconnected(sw);
controller.processUpdateQueueForTesting();
verify(sw, listener);
assertNull(controller.getSwitch(1L));
assertNull(storeClient.getValue(1L));
if (reconnect) {
controller.removeOFSwitchListener(listener);
sw = doActivateOldSwitch(1L, null, null);
}
}
@Test
public void testSwitchDisconnected() throws Exception {
doTestSwitchConnectReconnect(false);
}
/**
* Disconnect a switch and reconnect, verify no clearAllFlowmods()
*/
@Test
public void testSwitchReconnect() throws Exception {
doTestSwitchConnectReconnect(true);
}
/**
* Remove a nonexisting switch. should be ignored
*/
@Test
public void testNonexistingSwitchDisconnected() throws Exception {
IOFSwitch sw = createMock(IOFSwitch.class);
expect(sw.getId()).andReturn(1L).anyTimes();
expect(sw.getStringId()).andReturn(HexString.toHexString(1L)).anyTimes();
IOFSwitchListener listener = createMock(IOFSwitchListener.class);
controller.addOFSwitchListener(listener);
replay(sw, listener);
controller.switchDisconnected(sw);
controller.processUpdateQueueForTesting();
verify(sw, listener);
assertNull(controller.getSwitch(1L));
assertNull(storeClient.getValue(1L));
}
/**
* Try to remove a switch that's different from what's in the active
* switch map. Should be ignored
*/
@Test
public void testSwitchDisconnectedOther() throws Exception {
IOFSwitch origSw = doActivateNewSwitch(1L, null, null);
// create a new mock switch
IOFSwitch sw = createMock(IOFSwitch.class);
expect(sw.getId()).andReturn(1L).anyTimes();
expect(sw.getStringId()).andReturn(HexString.toHexString(1L)).anyTimes();
IOFSwitchListener listener = createMock(IOFSwitchListener.class);
controller.addOFSwitchListener(listener);
replay(sw, listener);
controller.switchDisconnected(sw);
controller.processUpdateQueueForTesting();
verify(sw, listener);
assertSame(origSw, controller.getSwitch(1L));
assertNotNull(storeClient.getValue(1L));
}
/**
* Try to activate a switch that's already active (which can happen if
* two different switches have the same DPIP or if a switch reconnects
* while the old TCP connection is still alive
*/
@Test
public void testSwitchActivatedWithAlreadyActiveSwitch() throws Exception {
OFDescriptionStatistics oldDesc = createOFDescriptionStatistics();
oldDesc.setDatapathDescription("Ye Olde Switch");
OFDescriptionStatistics newDesc = createOFDescriptionStatistics();
newDesc.setDatapathDescription("The new Switch");
OFFeaturesReply featuresReply = createOFFeaturesReply();
// Setup: add a switch to the controller
IOFSwitch oldsw = createMock(IOFSwitch.class);
setupSwitchForAddSwitch(oldsw, 0L, oldDesc, featuresReply);
oldsw.clearAllFlowMods();
expectLastCall().once();
replay(oldsw);
controller.switchActivated(oldsw);
verify(oldsw);
// drain the queue, we don't care what's in it
controller.processUpdateQueueForTesting();
assertEquals(oldsw, controller.getSwitch(0L));
// Now the actual test: add a new switch with the same dpid to
// the controller
reset(oldsw);
expect(oldsw.getId()).andReturn(0L).anyTimes();
oldsw.cancelAllStatisticsReplies();
expectLastCall().once();
oldsw.disconnectOutputStream();
expectLastCall().once();
IOFSwitch newsw = createMock(IOFSwitch.class);
setupSwitchForAddSwitch(newsw, 0L, newDesc, featuresReply);
newsw.clearAllFlowMods();
expectLastCall().once();
// Strict mock. We need to get the removed notification before the
// add notification
IOFSwitchListener listener = createStrictMock(IOFSwitchListener.class);
listener.switchRemoved(0L);
listener.switchAdded(0L);
listener.switchActivated(0L);
replay(listener);
controller.addOFSwitchListener(listener);
replay(newsw, oldsw);
controller.switchActivated(newsw);
verify(newsw, oldsw);
assertEquals(newsw, controller.getSwitch(0L));
controller.processUpdateQueueForTesting();
verify(listener);
}
/**
* 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() {
IOFSwitch sw = createNiceMock(IOFSwitch.class);
setupSwitchForAddSwitch(sw, 1L, null, null);
replay(sw);
getController().switchActivated(sw);
assertEquals(sw, getController().getSwitch(1L));
getController().getAllSwitchMap().remove(1L);
assertEquals(sw, getController().getSwitch(1L));
verify(sw);
// we don't care for updates. drain queue.
controller.processUpdateQueueForTesting();
}
/**
* Test that notifyPortChanged() results in an IOFSwitchListener
* update and that its arguments are passed through to
* the listener call
*/
@Test
public void testNotifySwitchPoArtChanged() throws Exception {
long dpid = 42L;
OFFeaturesReply fr1 = createOFFeaturesReply();
fr1.setDatapathId(dpid);
OFPhysicalPort p1 = createOFPhysicalPort("Port1", 1);
fr1.setPorts(Collections.singletonList(p1));
OFFeaturesReply fr2 = createOFFeaturesReply();
fr1.setDatapathId(dpid);
OFPhysicalPort p2 = createOFPhysicalPort("Port1", 1);
p2.setAdvertisedFeatures(0x2); // just some bogus values
fr2.setPorts(Collections.singletonList(p2));
OFDescriptionStatistics desc = createOFDescriptionStatistics();
// activate switch
IOFSwitch sw = doActivateNewSwitch(dpid, desc, fr1);
// check the store
SwitchSyncRepresentation ssr = storeClient.getValue(dpid);
assertNotNull(ssr);
assertEquals(dpid, ssr.getDpid());
assertEquals(1, ssr.getPorts().size());
assertEquals(p1, ssr.getPorts().get(0).toOFPhysicalPort());
IOFSwitchListener listener = createMock(IOFSwitchListener.class);
controller.addOFSwitchListener(listener);
// setup switch with the new, second features reply (and thus ports)
setupSwitchForAddSwitch(sw, dpid, desc, fr2);
listener.switchPortChanged(dpid, ImmutablePort.fromOFPhysicalPort(p2),
PortChangeType.OTHER_UPDATE);
expectLastCall().once();
replay(listener);
replay(sw);
controller.notifyPortChanged(sw, ImmutablePort.fromOFPhysicalPort(p2),
PortChangeType.OTHER_UPDATE);
controller.processUpdateQueueForTesting();
verify(listener);
verify(sw);
// check the store
ssr = storeClient.getValue(dpid);
assertNotNull(ssr);
assertEquals(dpid, ssr.getDpid());
assertEquals(1, ssr.getPorts().size());
assertEquals(p2, ssr.getPorts().get(0).toOFPhysicalPort());
}
private Map<String,Object> getFakeControllerIPRow(String id, String controllerId,
String type, int number, String discoveredIP ) {
HashMap<String, Object> row = new HashMap<String,Object>();
row.put(Controller.CONTROLLER_INTERFACE_ID, id);
row.put(Controller.CONTROLLER_INTERFACE_CONTROLLER_ID, controllerId);
row.put(Controller.CONTROLLER_INTERFACE_TYPE, type);
row.put(Controller.CONTROLLER_INTERFACE_NUMBER, number);
row.put(Controller.CONTROLLER_INTERFACE_DISCOVERED_IP, discoveredIP);
return row;
}
/**
* Test notifications for controller node IP changes. This requires
* synchronization between the main test thread and another thread
* that runs Controller's main loop and takes / handles updates. We
* synchronize with wait(timeout) / notifyAll(). We check for the
* expected condition after the wait returns. However, if wait returns
* due to the timeout (or due to spurious awaking) and the check fails we
* might just not have waited long enough. Using a long enough timeout
* mitigates this but we cannot get rid of the fundamental "issue".
*
* @throws Exception
*/
@Test
public void testControllerNodeIPChanges() throws Exception {
class DummyHAListener implements IHAListener {
public Map<String, String> curControllerNodeIPs;
public Map<String, String> addedControllerNodeIPs;
public Map<String, String> removedControllerNodeIPs;
public int nCalled;
public DummyHAListener() {
this.nCalled = 0;
}
@Override
public synchronized void controllerNodeIPsChanged(
Map<String, String> curControllerNodeIPs,
Map<String, String> addedControllerNodeIPs,
Map<String, String> removedControllerNodeIPs) {
this.curControllerNodeIPs = curControllerNodeIPs;
this.addedControllerNodeIPs = addedControllerNodeIPs;
this.removedControllerNodeIPs = removedControllerNodeIPs;
this.nCalled++;
notifyAll();
}
public void do_assert(int nCalled,
Map<String, String> curControllerNodeIPs,
Map<String, String> addedControllerNodeIPs,
Map<String, String> removedControllerNodeIPs) {
assertEquals("nCalled is not as expected", nCalled, this.nCalled);
assertEquals("curControllerNodeIPs is not as expected",
curControllerNodeIPs, this.curControllerNodeIPs);
assertEquals("addedControllerNodeIPs is not as expected",
addedControllerNodeIPs, this.addedControllerNodeIPs);
assertEquals("removedControllerNodeIPs is not as expected",
removedControllerNodeIPs, this.removedControllerNodeIPs);
}
@Override
public String getName() {
return null;
}
@Override
public boolean
isCallbackOrderingPrereq(HAListenerTypeMarker type,
String name) {
return false;
}
@Override
public boolean
isCallbackOrderingPostreq(HAListenerTypeMarker type,
String name) {
return false;
}
@Override
public void transitionToMaster() {
}
}
DummyHAListener listener = new DummyHAListener();
HashMap<String,String> expectedCurMap = new HashMap<String, String>();
HashMap<String,String> expectedAddedMap = new HashMap<String, String>();
HashMap<String,String> expectedRemovedMap = new HashMap<String, String>();
controller.addHAListener(listener);
synchronized(listener) {
// Insert a first entry
controller.getStorageSourceService()
.insertRow(Controller.CONTROLLER_INTERFACE_TABLE_NAME,
getFakeControllerIPRow("row1", "c1", "Ethernet", 0, "1.1.1.1"));
expectedCurMap.clear();
expectedAddedMap.clear();
expectedRemovedMap.clear();
expectedCurMap.put("c1", "1.1.1.1");
expectedAddedMap.put("c1", "1.1.1.1");
controller.processUpdateQueueForTesting();
listener.do_assert(1, expectedCurMap, expectedAddedMap, expectedRemovedMap);
// Add an interface that we want to ignore.
controller.getStorageSourceService()
.insertRow(Controller.CONTROLLER_INTERFACE_TABLE_NAME,
getFakeControllerIPRow("row2", "c1", "Ethernet", 1, "1.1.1.2"));
// TODO: do a different check. This call will have to wait for the timeout
controller.processUpdateQueueForTesting();
assertTrue("controllerNodeIPsChanged() should not have been called here",
listener.nCalled == 1);
// Add another entry
controller.getStorageSourceService()
.insertRow(Controller.CONTROLLER_INTERFACE_TABLE_NAME,
getFakeControllerIPRow("row3", "c2", "Ethernet", 0, "2.2.2.2"));
expectedCurMap.clear();
expectedAddedMap.clear();
expectedRemovedMap.clear();
expectedCurMap.put("c1", "1.1.1.1");
expectedCurMap.put("c2", "2.2.2.2");
expectedAddedMap.put("c2", "2.2.2.2");
controller.processUpdateQueueForTesting();
listener.do_assert(2, expectedCurMap, expectedAddedMap, expectedRemovedMap);
// Update an entry
controller.getStorageSourceService()
.updateRow(Controller.CONTROLLER_INTERFACE_TABLE_NAME,
"row3", getFakeControllerIPRow("row3", "c2", "Ethernet", 0, "2.2.2.3"));
expectedCurMap.clear();
expectedAddedMap.clear();
expectedRemovedMap.clear();
expectedCurMap.put("c1", "1.1.1.1");
expectedCurMap.put("c2", "2.2.2.3");
expectedAddedMap.put("c2", "2.2.2.3");
expectedRemovedMap.put("c2", "2.2.2.2");
controller.processUpdateQueueForTesting();
listener.do_assert(3, expectedCurMap, expectedAddedMap, expectedRemovedMap);
// Delete an entry
controller.getStorageSourceService()
.deleteRow(Controller.CONTROLLER_INTERFACE_TABLE_NAME, "row3");
expectedCurMap.clear();
expectedAddedMap.clear();
expectedRemovedMap.clear();
expectedCurMap.put("c1", "1.1.1.1");
expectedRemovedMap.put("c2", "2.2.2.3");
controller.processUpdateQueueForTesting();
listener.do_assert(4, expectedCurMap, expectedAddedMap, expectedRemovedMap);
}
}
@Test
public void testGetControllerNodeIPs() {
HashMap<String,String> expectedCurMap = new HashMap<String, String>();
controller.getStorageSourceService()
.insertRow(Controller.CONTROLLER_INTERFACE_TABLE_NAME,
getFakeControllerIPRow("row1", "c1", "Ethernet", 0, "1.1.1.1"));
controller.getStorageSourceService()
.insertRow(Controller.CONTROLLER_INTERFACE_TABLE_NAME,
getFakeControllerIPRow("row2", "c1", "Ethernet", 1, "1.1.1.2"));
controller.getStorageSourceService()
.insertRow(Controller.CONTROLLER_INTERFACE_TABLE_NAME,
getFakeControllerIPRow("row3", "c2", "Ethernet", 0, "2.2.2.2"));
expectedCurMap.put("c1", "1.1.1.1");
expectedCurMap.put("c2", "2.2.2.2");
assertEquals("expectedControllerNodeIPs is not as expected",
expectedCurMap, controller.getControllerNodeIPs());
// we don't care for updates. drain update queue
controller.processUpdateQueueForTesting();
}
/**
* Test the driver registry: test the bind order
*/
@Test
public void testSwitchDriverRegistryBindOrder() {
IOFSwitchDriver driver1 = createMock(IOFSwitchDriver.class);
IOFSwitchDriver driver2 = createMock(IOFSwitchDriver.class);
IOFSwitchDriver driver3 = createMock(IOFSwitchDriver.class);
IOFSwitch returnedSwitch = null;
IOFSwitch mockSwitch = createMock(IOFSwitch.class);
controller.addOFSwitchDriver("", driver3);
controller.addOFSwitchDriver("test switch", driver1);
controller.addOFSwitchDriver("test", driver2);
replay(driver1);
replay(driver2);
replay(driver3);
replay(mockSwitch);
OFDescriptionStatistics desc = createOFDescriptionStatistics();
desc.setManufacturerDescription("test switch");
desc.setHardwareDescription("version 0.9");
reset(driver1);
reset(driver2);
reset(driver3);
reset(mockSwitch);
mockSwitch.setSwitchProperties(desc);
expectLastCall().once();
expect(driver1.getOFSwitchImpl(desc)).andReturn(mockSwitch).once();
replay(driver1);
replay(driver2);
replay(driver3);
replay(mockSwitch);
returnedSwitch = controller.getOFSwitchInstance(desc);
assertSame(mockSwitch, returnedSwitch);
verify(driver1);
verify(driver2);
verify(driver3);
verify(mockSwitch);
desc = createOFDescriptionStatistics();
desc.setManufacturerDescription("testFooBar");
desc.setHardwareDescription("version 0.9");
reset(driver1);
reset(driver2);
reset(driver3);
reset(mockSwitch);
mockSwitch.setSwitchProperties(desc);
expectLastCall().once();
expect(driver2.getOFSwitchImpl(desc)).andReturn(mockSwitch).once();
replay(driver1);
replay(driver2);
replay(driver3);
replay(mockSwitch);
returnedSwitch = controller.getOFSwitchInstance(desc);
assertSame(mockSwitch, returnedSwitch);
verify(driver1);
verify(driver2);
verify(driver3);
verify(mockSwitch);
desc = createOFDescriptionStatistics();
desc.setManufacturerDescription("FooBar");
desc.setHardwareDescription("version 0.9");
reset(driver1);
reset(driver2);
reset(driver3);
reset(mockSwitch);
mockSwitch.setSwitchProperties(desc);
expectLastCall().once();
expect(driver3.getOFSwitchImpl(desc)).andReturn(mockSwitch).once();
replay(driver1);
replay(driver2);
replay(driver3);
replay(mockSwitch);
returnedSwitch = controller.getOFSwitchInstance(desc);
assertSame(mockSwitch, returnedSwitch);
verify(driver1);
verify(driver2);
verify(driver3);
verify(mockSwitch);
}
/**
* Test SwitchDriverRegistry
* Test fallback to default if no switch driver is registered for a
* particular prefix
*/
@Test
public void testSwitchDriverRegistryNoDriver() {
IOFSwitchDriver driver = createMock(IOFSwitchDriver.class);
IOFSwitch returnedSwitch = null;
IOFSwitch mockSwitch = createMock(IOFSwitch.class);
controller.addOFSwitchDriver("test switch", driver);
replay(driver);
replay(mockSwitch);
OFDescriptionStatistics desc = createOFDescriptionStatistics();
desc.setManufacturerDescription("test switch");
desc.setHardwareDescription("version 0.9");
reset(driver);
reset(mockSwitch);
mockSwitch.setSwitchProperties(desc);
expectLastCall().once();
expect(driver.getOFSwitchImpl(desc)).andReturn(mockSwitch).once();
replay(driver);
replay(mockSwitch);
returnedSwitch = controller.getOFSwitchInstance(desc);
assertSame(mockSwitch, returnedSwitch);
verify(driver);
verify(mockSwitch);
desc = createOFDescriptionStatistics();
desc.setManufacturerDescription("Foo Bar test switch");
desc.setHardwareDescription("version 0.9");
reset(driver);
reset(mockSwitch);
replay(driver);
replay(mockSwitch);
returnedSwitch = controller.getOFSwitchInstance(desc);
assertNotNull(returnedSwitch);
assertTrue("Returned switch should be OFSwitchImpl",
returnedSwitch instanceof OFSwitchImpl);
assertEquals(desc, returnedSwitch.getDescriptionStatistics());
verify(driver);
verify(mockSwitch);
}
/**
*
*/
@Test
public void testDriverRegistryExceptions() {
IOFSwitchDriver driver = createMock(IOFSwitchDriver.class);
IOFSwitchDriver driver2 = createMock(IOFSwitchDriver.class);
replay(driver, driver2); // no calls expected on driver
//---------------
// Test exception handling when registering driver
try {
controller.addOFSwitchDriver("foobar", null);
fail("Expected NullPointerException not thrown");
} catch (NullPointerException e) {
//expected
}
try {
controller.addOFSwitchDriver(null, driver);
fail("Expected NullPointerException not thrown");
} catch (NullPointerException e) {
//expected
}
// test that we can register each prefix only once!
controller.addOFSwitchDriver("foobar", driver);
try {
controller.addOFSwitchDriver("foobar", driver);
fail("Expected IllegalStateException not thrown");
} catch (IllegalStateException e) {
//expected
}
try {
controller.addOFSwitchDriver("foobar", driver2);
fail("Expected IllegalStateException not thrown");
} catch (IllegalStateException e) {
//expected
}
OFDescriptionStatistics desc = createOFDescriptionStatistics();
desc.setDatapathDescription(null);
try {
controller.getOFSwitchInstance(desc);
fail("Expected NullPointerException not thrown");
} catch (NullPointerException e) {
//expected
}
desc.setHardwareDescription(null);
try {
controller.getOFSwitchInstance(desc);
fail("Expected NullPointerException not thrown");
} catch (NullPointerException e) {
//expected
}
desc.setManufacturerDescription(null);
try {
controller.getOFSwitchInstance(desc);
fail("Expected NullPointerException not thrown");
} catch (NullPointerException e) {
//expected
}
desc.setSerialNumber(null);
try {
controller.getOFSwitchInstance(desc);
fail("Expected NullPointerException not thrown");
} catch (NullPointerException e) {
//expected
}
desc.setSoftwareDescription(null);
try {
controller.getOFSwitchInstance(desc);
fail("Expected NullPointerException not thrown");
} catch (NullPointerException e) {
//expected
}
verify(driver, driver2);
}
}