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());
+ }
+}