| /** |
| * 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.IListener.Command; |
| 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.packet.Ethernet; |
| import net.onrc.onos.core.packet.OnosLldp; |
| 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.OFPacketIn; |
| 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(); |
| expect(mockSwitch.portEnabled(EasyMock.anyShort())).andReturn(true).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()); |
| } |
| |
| @Test |
| public void testHandlePortStatusForDeletePort() { |
| byte[] macAddress = new byte[] {0x0, 0x0, 0x0, 0x0, 0x0, 0x1}; |
| |
| LinkDiscoveryManager linkDiscovery = getTopology(); |
| |
| // Add a link that we can delete later during the test |
| Link lt = new Link(1L, 1, 2L, 2); |
| LinkInfo info = new LinkInfo(System.currentTimeMillis(), |
| System.currentTimeMillis(), 0, 0); |
| linkDiscovery.addOrUpdateLink(lt, info); |
| |
| short portNum = 1; |
| int srcPortState = 1; |
| OFPhysicalPort srcPort = new OFPhysicalPort(); |
| srcPort.setPortNumber(portNum); |
| srcPort.setHardwareAddress(macAddress); |
| srcPort.setState(srcPortState); |
| |
| OFPortStatus srcPortStatus = new OFPortStatus(); |
| srcPortStatus.setDesc(srcPort); |
| srcPortStatus.setReason((byte) OFPortReason.OFPPR_DELETE.ordinal()); |
| |
| assertNotNull(linkDiscovery.getLinks().get(lt)); |
| |
| // Send a delete port status for the source port, which should result |
| // in the link being deleted |
| linkDiscovery.handlePortStatus( |
| getMockFloodlightProvider().getSwitches().get(1L), srcPortStatus); |
| |
| assertNull(linkDiscovery.getLinks().get(lt)); |
| } |
| |
| @Test |
| public void testReceive() { |
| byte[] macAddress = new byte[] {0x0, 0x0, 0x0, 0x0, 0x0, 0x1}; |
| |
| OnosLldp lldpPacket = new OnosLldp(); |
| lldpPacket.setPort((short) 1); |
| lldpPacket.setSwitch(1L); |
| lldpPacket.setReverse(false); |
| |
| Ethernet ethPacket = new Ethernet(); |
| ethPacket.setEtherType(Ethernet.TYPE_LLDP); |
| ethPacket.setSourceMACAddress(macAddress); |
| ethPacket.setDestinationMACAddress( |
| LinkDiscoveryManager.LLDP_STANDARD_DST_MAC_STRING); |
| ethPacket.setPayload(lldpPacket); |
| ethPacket.setPad(true); |
| |
| OFPacketIn pi = new OFPacketIn(); |
| pi.setInPort((short) 2); |
| pi.setPacketData(ethPacket.serialize()); |
| |
| LinkDiscoveryManager linkDiscovery = getTopology(); |
| |
| Link expectedLink = new Link(1L, 1, 2L, 2); |
| |
| assertNull(linkDiscovery.links.get(expectedLink)); |
| |
| // Sending in the LLDP packet should cause the link to be created |
| Command command = linkDiscovery.handleLldp(lldpPacket, 2L, pi); |
| |
| assertEquals(Command.STOP, command); |
| assertNotNull(linkDiscovery.links.get(expectedLink)); |
| } |
| } |