Package net.floodlightcontroller.core.internal

Source Code of net.floodlightcontroller.core.internal.DummyHAListener

/**
*    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);
    }

}
TOP

Related Classes of net.floodlightcontroller.core.internal.DummyHAListener

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.
reate', 'UA-20639858-1', 'auto'); ga('send', 'pageview');