General link discovery module cleanup.

* Removed all events we don't use from the event system (switch and port
  events, link updates)
* Refactored event interface to two separate methods to match the other event
  interfaces
* Removed the LDUpdate class and moved all the event enums and classes to be
  internal to the LinkDiscoveryManager
* Removed all LinkTypes we no longer used and moved the one remaining type
  to the ILinkDiscoveryService. After this the ILinkDiscovery interface is
  no longer needed.
* Made Link immutable
* Removed the linkdiscovery.internal package as it only contained one class
* Readability improvements to LinkDiscoveryManager

Change-Id: Ifae97879aadc49b70a7b3d2294dcc540538c2cfc
diff --git a/src/test/java/net/onrc/onos/core/linkdiscovery/LinkDiscoveryManagerTest.java b/src/test/java/net/onrc/onos/core/linkdiscovery/LinkDiscoveryManagerTest.java
new file mode 100644
index 0000000..d6c0043
--- /dev/null
+++ b/src/test/java/net/onrc/onos/core/linkdiscovery/LinkDiscoveryManagerTest.java
@@ -0,0 +1,433 @@
+/**
+ *    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.onrc.onos.core.linkdiscovery;
+
+import static org.easymock.EasyMock.createNiceMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.verify;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import net.floodlightcontroller.core.FloodlightContext;
+import net.floodlightcontroller.core.IFloodlightProviderService;
+import net.floodlightcontroller.core.IOFSwitch;
+import net.floodlightcontroller.core.module.FloodlightModuleContext;
+import net.floodlightcontroller.core.test.MockThreadPoolService;
+import net.floodlightcontroller.restserver.IRestApiService;
+import net.floodlightcontroller.restserver.RestApiServer;
+import net.floodlightcontroller.test.FloodlightTestCase;
+import net.floodlightcontroller.threadpool.IThreadPoolService;
+import net.onrc.onos.core.registry.IControllerRegistryService;
+
+import org.easymock.EasyMock;
+import org.junit.Before;
+import org.junit.Test;
+import org.openflow.protocol.OFMessage;
+import org.openflow.protocol.OFPhysicalPort;
+import org.openflow.protocol.OFPortStatus;
+import org.openflow.protocol.OFPortStatus.OFPortReason;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+// CHECKSTYLE IGNORE WriteTag FOR NEXT 2 LINES
+/**
+ * @author David Erickson (daviderickson@cs.stanford.edu)
+ */
+public class LinkDiscoveryManagerTest extends FloodlightTestCase {
+
+    private TestLinkDiscoveryManager ldm;
+    protected static final Logger log = LoggerFactory.getLogger(LinkDiscoveryManagerTest.class);
+
+    public class TestLinkDiscoveryManager extends LinkDiscoveryManager {
+        public boolean isSendLLDPsCalled = false;
+        public boolean isClearLinksCalled = false;
+
+        @Override
+        protected void discoverOnAllPorts() {
+            isSendLLDPsCalled = true;
+            super.discoverOnAllPorts();
+        }
+
+        public void reset() {
+            isSendLLDPsCalled = false;
+            isClearLinksCalled = false;
+        }
+    }
+
+    public LinkDiscoveryManager getTopology() {
+        return ldm;
+    }
+
+    public IOFSwitch createMockSwitch(Long id) {
+        IOFSwitch mockSwitch = createNiceMock(IOFSwitch.class);
+        expect(mockSwitch.getId()).andReturn(id).anyTimes();
+        return mockSwitch;
+    }
+
+    @Override
+    @Before
+    public void setUp() throws Exception {
+        super.setUp();
+        FloodlightModuleContext cntx = new FloodlightModuleContext();
+        ldm = new TestLinkDiscoveryManager();
+        //ldm.linkDiscoveryAware = new ArrayList<ILinkDiscoveryListener>();
+        MockThreadPoolService tp = new MockThreadPoolService();
+        RestApiServer restApi = new RestApiServer();
+        IControllerRegistryService registry =
+                EasyMock.createMock(IControllerRegistryService.class);
+        expect(registry.hasControl(EasyMock.anyLong())).andReturn(true).anyTimes();
+        replay(registry);
+        cntx.addService(IControllerRegistryService.class, registry);
+        cntx.addService(IRestApiService.class, restApi);
+        cntx.addService(IThreadPoolService.class, tp);
+        cntx.addService(ILinkDiscoveryService.class, ldm);
+        cntx.addService(IFloodlightProviderService.class, getMockFloodlightProvider());
+        restApi.init(cntx);
+        tp.init(cntx);
+        ldm.init(cntx);
+        restApi.startUp(cntx);
+        tp.startUp(cntx);
+        ldm.startUp(cntx);
+
+        IOFSwitch sw1 = createMockSwitch(1L);
+        IOFSwitch sw2 = createMockSwitch(2L);
+        Map<Long, IOFSwitch> switches = new HashMap<Long, IOFSwitch>();
+        switches.put(1L, sw1);
+        switches.put(2L, sw2);
+        getMockFloodlightProvider().setSwitches(switches);
+        replay(sw1, sw2);
+    }
+
+    @Test
+    public void testAddOrUpdateLink() throws Exception {
+        LinkDiscoveryManager topology = getTopology();
+
+        Link lt = new Link(1L, 2, 2L, 1);
+        long firstSeenTime = System.currentTimeMillis();
+        LinkInfo info = new LinkInfo(firstSeenTime,
+                System.currentTimeMillis(), 0, 0);
+        topology.addOrUpdateLink(lt, info);
+
+
+        NodePortTuple srcNpt = new NodePortTuple(1L, 2);
+        NodePortTuple dstNpt = new NodePortTuple(2L, 1);
+
+        // check invariants hold
+        assertNotNull(topology.switchLinks.get(lt.getSrc()));
+        assertTrue(topology.switchLinks.get(lt.getSrc()).contains(lt));
+        assertNotNull(topology.portLinks.get(srcNpt));
+        assertTrue(topology.portLinks.get(srcNpt).contains(lt));
+        assertNotNull(topology.portLinks.get(dstNpt));
+        assertTrue(topology.portLinks.get(dstNpt).contains(lt));
+        assertTrue(topology.links.containsKey(lt));
+
+        LinkInfo infoToVerify = topology.links.get(lt);
+        assertEquals(firstSeenTime, infoToVerify.getFirstSeenTime());
+        assertEquals(0, infoToVerify.getSrcPortState());
+        assertEquals(0, infoToVerify.getDstPortState());
+
+        // Arbitrary new port states to verify that the port state is updated
+        final int newSrcPortState = 1;
+        final int newDstPortState = 2;
+
+        // Update the last received probe timestamp and the port states
+        LinkInfo infoWithStateChange = new LinkInfo(System.currentTimeMillis(),
+                System.currentTimeMillis(), newSrcPortState, newDstPortState);
+
+        topology.addOrUpdateLink(lt, infoWithStateChange);
+
+        assertNotNull(topology.links.get(lt));
+        infoToVerify = topology.links.get(lt);
+        // First seen time should be the original time, not the second update time
+        assertEquals(firstSeenTime, infoToVerify.getFirstSeenTime());
+        // Both port states should have been updated
+        assertEquals(newSrcPortState, infoToVerify.getSrcPortState());
+        assertEquals(newDstPortState, infoToVerify.getDstPortState());
+    }
+
+    @Test
+    public void testDeleteLink() throws Exception {
+        LinkDiscoveryManager topology = getTopology();
+
+        Link lt = new Link(1L, 2, 2L, 1);
+        LinkInfo info = new LinkInfo(System.currentTimeMillis(),
+                System.currentTimeMillis(), 0, 0);
+        topology.addOrUpdateLink(lt, info);
+        topology.deleteLinks(Collections.singletonList(lt));
+
+        // check invariants hold
+        assertNull(topology.switchLinks.get(lt.getSrc()));
+        assertNull(topology.switchLinks.get(lt.getDst()));
+        assertNull(topology.portLinks.get(lt.getSrc()));
+        assertNull(topology.portLinks.get(lt.getDst()));
+        assertTrue(topology.links.isEmpty());
+    }
+
+    @Test
+    public void testAddOrUpdateLinkToSelf() throws Exception {
+        LinkDiscoveryManager topology = getTopology();
+
+        Link lt = new Link(1L, 2, 2L, 3);
+        NodePortTuple srcNpt = new NodePortTuple(1L, 2);
+        NodePortTuple dstNpt = new NodePortTuple(2L, 3);
+
+        LinkInfo info = new LinkInfo(System.currentTimeMillis(),
+                System.currentTimeMillis(), 0, 0);
+        topology.addOrUpdateLink(lt, info);
+
+        // check invariants hold
+        assertNotNull(topology.switchLinks.get(lt.getSrc()));
+        assertTrue(topology.switchLinks.get(lt.getSrc()).contains(lt));
+        assertNotNull(topology.portLinks.get(srcNpt));
+        assertTrue(topology.portLinks.get(srcNpt).contains(lt));
+        assertNotNull(topology.portLinks.get(dstNpt));
+        assertTrue(topology.portLinks.get(dstNpt).contains(lt));
+        assertTrue(topology.links.containsKey(lt));
+    }
+
+    @Test
+    public void testDeleteLinkToSelf() throws Exception {
+        LinkDiscoveryManager topology = getTopology();
+
+        Link lt = new Link(1L, 2, 1L, 3);
+        NodePortTuple srcNpt = new NodePortTuple(1L, 2);
+        NodePortTuple dstNpt = new NodePortTuple(2L, 3);
+
+        LinkInfo info = new LinkInfo(System.currentTimeMillis(),
+                System.currentTimeMillis(), 0, 0);
+        topology.addOrUpdateLink(lt, info);
+        topology.deleteLinks(Collections.singletonList(lt));
+
+        // check invariants hold
+        assertNull(topology.switchLinks.get(lt.getSrc()));
+        assertNull(topology.switchLinks.get(lt.getDst()));
+        assertNull(topology.portLinks.get(srcNpt));
+        assertNull(topology.portLinks.get(dstNpt));
+        assertTrue(topology.links.isEmpty());
+    }
+
+    @Test
+    public void testRemovedSwitch() {
+        LinkDiscoveryManager topology = getTopology();
+
+        Link lt = new Link(1L, 2, 2L, 1);
+        NodePortTuple srcNpt = new NodePortTuple(1L, 2);
+        NodePortTuple dstNpt = new NodePortTuple(2L, 1);
+        LinkInfo info = new LinkInfo(System.currentTimeMillis(),
+                System.currentTimeMillis(), 0, 0);
+        topology.addOrUpdateLink(lt, info);
+
+        IOFSwitch sw1 = getMockFloodlightProvider().getSwitches().get(1L);
+        IOFSwitch sw2 = getMockFloodlightProvider().getSwitches().get(2L);
+        // Mock up our expected behavior
+        topology.removedSwitch(sw1);
+        verify(sw1, sw2);
+
+        // check invariants hold
+        assertNull(topology.switchLinks.get(lt.getSrc()));
+        assertNull(topology.switchLinks.get(lt.getDst()));
+        assertNull(topology.portLinks.get(srcNpt));
+        assertNull(topology.portLinks.get(dstNpt));
+        assertTrue(topology.links.isEmpty());
+    }
+
+    @Test
+    public void testRemovedSwitchSelf() {
+        LinkDiscoveryManager topology = getTopology();
+        IOFSwitch sw1 = createMockSwitch(1L);
+        replay(sw1);
+        Link lt = new Link(1L, 2, 1L, 3);
+        LinkInfo info = new LinkInfo(System.currentTimeMillis(),
+                System.currentTimeMillis(), 0, 0);
+        topology.addOrUpdateLink(lt, info);
+
+        // Mock up our expected behavior
+        topology.removedSwitch(sw1);
+
+        verify(sw1);
+        // check invariants hold
+        assertNull(topology.switchLinks.get(lt.getSrc()));
+        assertNull(topology.portLinks.get(lt.getSrc()));
+        assertNull(topology.portLinks.get(lt.getDst()));
+        assertTrue(topology.links.isEmpty());
+    }
+
+    @Test
+    public void testAddUpdateLinks() throws Exception {
+        LinkDiscoveryManager topology = getTopology();
+
+        Link lt = new Link(1L, 1, 2L, 1);
+        NodePortTuple srcNpt = new NodePortTuple(1L, 1);
+        NodePortTuple dstNpt = new NodePortTuple(2L, 1);
+
+        LinkInfo info;
+
+        info = new LinkInfo(System.currentTimeMillis() - 40000,
+                System.currentTimeMillis() - 40000, 0, 0);
+        topology.addOrUpdateLink(lt, info);
+
+        // check invariants hold
+        assertNotNull(topology.switchLinks.get(lt.getSrc()));
+        assertTrue(topology.switchLinks.get(lt.getSrc()).contains(lt));
+        assertNotNull(topology.portLinks.get(srcNpt));
+        assertTrue(topology.portLinks.get(srcNpt).contains(lt));
+        assertNotNull(topology.portLinks.get(dstNpt));
+        assertTrue(topology.portLinks.get(dstNpt).contains(lt));
+        assertTrue(topology.links.containsKey(lt));
+
+        topology.timeOutLinks();
+
+        // Add a link info based on info that would be obtained from unicast LLDP
+        // Setting the unicast LLDP reception time to be 40 seconds old, so we can use
+        // this to test timeout after this test.
+        info = new LinkInfo(System.currentTimeMillis() - 40000,
+                System.currentTimeMillis() - 40000, 0, 0);
+        topology.addOrUpdateLink(lt, info);
+
+        // Expect to timeout the unicast Valid Time, so the link should disappear
+        topology.timeOutLinks();
+        assertTrue(topology.links.get(lt) == null);
+    }
+
+    /**
+     * This test case verifies that LinkDiscoveryManager.sendDiscoveryMessage()
+     * performs "write" operation on the specified IOFSwitch object
+     * with a LLDP packet.
+     *
+     * @throws IOException
+     */
+    @Test
+    public void testSendDiscoveryMessage() throws IOException {
+        byte[] macAddress = new byte[] {0x0, 0x0, 0x0, 0x0, 0x0, 0x1};
+
+        LinkDiscoveryManager topology = getTopology();
+
+        // Mock up our expected behavior
+        IOFSwitch swTest = createMockSwitch(3L);
+        getMockFloodlightProvider().getSwitches().put(3L, swTest);
+
+        short portNum = 1;
+        OFPhysicalPort ofpPort = new OFPhysicalPort();
+        ofpPort.setPortNumber(portNum);
+        ofpPort.setHardwareAddress(macAddress);
+
+        /* sendDiscoverMessage() should perform the following actions on
+         * IOFSwitch object
+         * - getPort() with argument as "1"
+         * - write() with OFPacketOut
+         * - flush()
+         */
+        expect(swTest.getPort(portNum)).andReturn(ofpPort).atLeastOnce();
+        swTest.write(EasyMock.anyObject(OFMessage.class), EasyMock.anyObject(FloodlightContext.class));
+        EasyMock.expectLastCall().times(1);
+        swTest.flush();
+        EasyMock.expectLastCall().once();
+        replay(swTest);
+
+        topology.sendDiscoveryMessage(3L, portNum, false);
+
+        verify(swTest);
+    }
+
+    @Test
+    public void testHandlePortStatusForNewPort() throws IOException {
+        byte[] macAddress = new byte[] {0x0, 0x0, 0x0, 0x0, 0x0, 0x1};
+
+        LinkDiscoveryManager linkDiscovery = getTopology();
+
+        long dpid = 3L;
+        IOFSwitch sw = createMockSwitch(dpid);
+        getMockFloodlightProvider().getSwitches().put(dpid, sw);
+
+        short portNum = 1;
+        OFPhysicalPort ofpPort = new OFPhysicalPort();
+        ofpPort.setPortNumber(portNum);
+        ofpPort.setHardwareAddress(macAddress);
+
+        OFPortStatus portStatus = new OFPortStatus();
+        portStatus.setDesc(ofpPort);
+        portStatus.setReason((byte) OFPortReason.OFPPR_ADD.ordinal());
+
+        expect(sw.getPort(portNum)).andReturn(ofpPort).anyTimes();
+        sw.write(EasyMock.anyObject(OFMessage.class),
+                EasyMock.anyObject(FloodlightContext.class));
+        sw.flush();
+
+        replay(sw);
+
+        linkDiscovery.handlePortStatus(sw, portStatus);
+
+        verify(sw);
+    }
+
+    @Test
+    public void testHandlePortStatusForExistingPort() {
+        byte[] macAddress = new byte[] {0x0, 0x0, 0x0, 0x0, 0x0, 0x1};
+
+        LinkDiscoveryManager linkDiscovery = getTopology();
+
+        // Add a link that we can update later during the test
+        Link lt = new Link(1L, 1, 2L, 1);
+        LinkInfo info = new LinkInfo(System.currentTimeMillis(),
+                System.currentTimeMillis(), 0, 0);
+        linkDiscovery.addOrUpdateLink(lt, info);
+
+        short portNum = 1;
+        // src port
+        int srcPortState = 2;
+        OFPhysicalPort srcPort = new OFPhysicalPort();
+        srcPort.setPortNumber(portNum);
+        srcPort.setHardwareAddress(macAddress);
+        srcPort.setState(srcPortState);
+
+        // dst port
+        int dstPortState = 4;
+        OFPhysicalPort dstPort = new OFPhysicalPort();
+        dstPort.setPortNumber(portNum);
+        dstPort.setHardwareAddress(macAddress);
+        dstPort.setState(dstPortState);
+
+        OFPortStatus srcPortStatus = new OFPortStatus();
+        srcPortStatus.setDesc(srcPort);
+        srcPortStatus.setReason((byte) OFPortReason.OFPPR_MODIFY.ordinal());
+
+        OFPortStatus dstPortStatus = new OFPortStatus();
+        dstPortStatus.setDesc(dstPort);
+        dstPortStatus.setReason((byte) OFPortReason.OFPPR_MODIFY.ordinal());
+
+        linkDiscovery.handlePortStatus(
+                getMockFloodlightProvider().getSwitches().get(1L), srcPortStatus);
+
+
+        LinkInfo newInfo = linkDiscovery.links.get(lt);
+        assertEquals(srcPortState, newInfo.getSrcPortState());
+        assertEquals(0, newInfo.getDstPortState());
+
+
+        linkDiscovery.handlePortStatus(
+                getMockFloodlightProvider().getSwitches().get(2L), dstPortStatus);
+
+        newInfo = linkDiscovery.links.get(lt);
+        assertEquals(srcPortState, newInfo.getSrcPortState());
+        assertEquals(dstPortState, newInfo.getDstPortState());
+    }
+}