/** * Copyright 2013, Big Switch Networks, Inc. * * 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.loadbalancer; import static org.easymock.EasyMock.anyLong; import static org.easymock.EasyMock.anyShort; import static org.easymock.EasyMock.capture; import static org.easymock.EasyMock.createMock; import static org.easymock.EasyMock.expect; import static org.easymock.EasyMock.expectLastCall; import static org.easymock.EasyMock.replay; import static org.easymock.EasyMock.reset; import static org.easymock.EasyMock.verify; import static org.junit.Assert.*; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.easymock.Capture; import org.easymock.CaptureType; import org.easymock.EasyMock; import org.junit.Before; import org.junit.Test; import org.projectfloodlight.openflow.protocol.OFFactory; import org.projectfloodlight.openflow.protocol.OFFactories; import org.projectfloodlight.openflow.protocol.OFMessage; import org.projectfloodlight.openflow.protocol.OFPacketIn; import org.projectfloodlight.openflow.protocol.OFPacketOut; import org.projectfloodlight.openflow.protocol.OFVersion; import org.projectfloodlight.openflow.types.EthType; import org.projectfloodlight.openflow.types.IPv4Address; import org.projectfloodlight.openflow.types.IPv6Address; import org.projectfloodlight.openflow.types.IpProtocol; import org.projectfloodlight.openflow.types.MacAddress; import org.projectfloodlight.openflow.types.OFBufferId; import org.projectfloodlight.openflow.types.OFPort; import org.projectfloodlight.openflow.types.VlanVid; import org.projectfloodlight.openflow.protocol.OFPacketInReason; import org.projectfloodlight.openflow.protocol.action.OFAction; import org.projectfloodlight.openflow.protocol.match.MatchField; import org.projectfloodlight.openflow.types.DatapathId; import org.sdnplatform.sync.ISyncService; import org.sdnplatform.sync.test.MockSyncService; import net.floodlightcontroller.core.FloodlightContext; import net.floodlightcontroller.core.IFloodlightProviderService; import net.floodlightcontroller.core.IOFSwitch; import net.floodlightcontroller.core.internal.IOFSwitchService; import net.floodlightcontroller.core.module.FloodlightModuleContext; import net.floodlightcontroller.core.test.MockThreadPoolService; import net.floodlightcontroller.core.types.NodePortTuple; import net.floodlightcontroller.debugcounter.IDebugCounterService; import net.floodlightcontroller.debugcounter.MockDebugCounterService; import net.floodlightcontroller.devicemanager.IDeviceService; import net.floodlightcontroller.devicemanager.IEntityClassifierService; import net.floodlightcontroller.devicemanager.internal.DefaultEntityClassifier; import net.floodlightcontroller.devicemanager.test.MockDeviceManager; import net.floodlightcontroller.packet.ARP; import net.floodlightcontroller.packet.Ethernet; import net.floodlightcontroller.packet.ICMP; import net.floodlightcontroller.packet.IPacket; import net.floodlightcontroller.packet.IPv4; import net.floodlightcontroller.restserver.IRestApiService; import net.floodlightcontroller.restserver.RestApiServer; import net.floodlightcontroller.routing.IRoutingService; import net.floodlightcontroller.routing.Path; import net.floodlightcontroller.staticentry.IStaticEntryPusherService; import net.floodlightcontroller.staticentry.StaticEntryPusher; import net.floodlightcontroller.storage.IStorageSourceService; import net.floodlightcontroller.storage.memory.MemoryStorageSource; import net.floodlightcontroller.test.FloodlightTestCase; import net.floodlightcontroller.threadpool.IThreadPoolService; import net.floodlightcontroller.topology.ITopologyService; import net.floodlightcontroller.util.OFMessageUtils; public class LoadBalancerTest extends FloodlightTestCase { protected LoadBalancer lb; protected FloodlightContext cntx; protected FloodlightModuleContext fmc; protected MockDeviceManager deviceManager; protected MockThreadPoolService tps; protected DefaultEntityClassifier entityClassifier; protected IRoutingService routingEngine; protected ITopologyService topology; protected StaticEntryPusher sfp; protected MemoryStorageSource storage; protected RestApiServer restApi; protected VipsResource vipsResource; protected PoolsResource poolsResource; protected MembersResource membersResource; private MockSyncService mockSyncService; protected IDebugCounterService debugCounterService; protected LBVip vip1, vip2; protected LBPool pool1, pool2, pool3; protected LBMember member1, member2, member3, member4; private OFFactory factory; @Override @Before public void setUp() throws Exception { super.setUp(); factory = OFFactories.getFactory(OFVersion.OF_13); lb = new LoadBalancer(); cntx = new FloodlightContext(); fmc = new FloodlightModuleContext(); entityClassifier = new DefaultEntityClassifier(); // dependency for device manager tps = new MockThreadPoolService(); //dependency for device manager deviceManager = new MockDeviceManager(); topology = createMock(ITopologyService.class); routingEngine = createMock(IRoutingService.class); restApi = new RestApiServer(); sfp = new StaticEntryPusher(); storage = new MemoryStorageSource(); //dependency for sfp mockSyncService = new MockSyncService(); debugCounterService = new MockDebugCounterService(); fmc.addService(IRestApiService.class, restApi); fmc.addService(IFloodlightProviderService.class, getMockFloodlightProvider()); fmc.addService(IEntityClassifierService.class, entityClassifier); fmc.addService(IThreadPoolService.class, tps); fmc.addService(IDeviceService.class, deviceManager); fmc.addService(ITopologyService.class, topology); fmc.addService(IRoutingService.class, routingEngine); fmc.addService(IStaticEntryPusherService.class, sfp); fmc.addService(ILoadBalancerService.class, lb); fmc.addService(IStorageSourceService.class, storage); fmc.addService(ISyncService.class, mockSyncService); fmc.addService(IDebugCounterService.class, debugCounterService); fmc.addService(IOFSwitchService.class, getMockSwitchService()); lb.init(fmc); //getMockFloodlightProvider().init(fmc); entityClassifier.init(fmc); tps.init(fmc); mockSyncService.init(fmc); deviceManager.init(fmc); restApi.init(fmc); sfp.init(fmc); storage.init(fmc); topology.addListener(deviceManager); expectLastCall().times(1); replay(topology); lb.startUp(fmc); //getMockFloodlightProvider().startUp(fmc); entityClassifier.startUp(fmc); tps.startUp(fmc); mockSyncService.startUp(fmc); deviceManager.startUp(fmc); restApi.startUp(fmc); sfp.startUp(fmc); storage.startUp(fmc); verify(topology); vipsResource = new VipsResource(); poolsResource = new PoolsResource(); membersResource = new MembersResource(); vip1=null; vip2=null; pool1=null; pool2=null; pool3=null; member1=null; member2=null; member3=null; member4=null; } @Test public void testCreateVip() { String postData1, postData2; IOException error = null; postData1 = "{\"id\":\"1\",\"name\":\"vip1\",\"protocol\":\"icmp\",\"address\":\"10.0.0.100\",\"port\":\"8\"}"; postData2 = "{\"id\":\"2\",\"name\":\"vip2\",\"protocol\":\"tcp\",\"address\":\"10.0.0.200\",\"port\":\"100\"}"; try { vip1 = vipsResource.jsonToVip(postData1); } catch (IOException e) { error = e; } try { vip2 = vipsResource.jsonToVip(postData2); } catch (IOException e) { error = e; } // verify correct parsing assertFalse(vip1==null); assertFalse(vip2==null); assertTrue(error==null); lb.createVip(vip1); lb.createVip(vip2); // verify correct creation assertTrue(lb.vips.containsKey(vip1.id)); assertTrue(lb.vips.containsKey(vip2.id)); } @Test public void testRemoveVip() { testCreateVip(); // verify correct initial condition assertFalse(vip1==null); assertFalse(vip2==null); lb.removeVip(vip1.id); lb.removeVip(vip2.id); // verify correct removal assertFalse(lb.vips.containsKey(vip1.id)); assertFalse(lb.vips.containsKey(vip2.id)); } @Test public void testCreatePool() { String postData1, postData2, postData3; IOException error = null; testCreateVip(); postData1 = "{\"id\":\"1\",\"name\":\"pool1\",\"protocol\":\"icmp\",\"vip_id\":\"1\"}"; postData2 = "{\"id\":\"2\",\"name\":\"pool2\",\"protocol\":\"tcp\",\"vip_id\":\"2\"}"; postData3 = "{\"id\":\"3\",\"name\":\"pool3\",\"protocol\":\"udp\",\"vip_id\":\"3\"}"; try { pool1 = poolsResource.jsonToPool(postData1); } catch (IOException e) { error = e; } try { pool2 = poolsResource.jsonToPool(postData2); } catch (IOException e) { error = e; } try { pool3 = poolsResource.jsonToPool(postData3); } catch (IOException e) { error = e; } // verify correct parsing assertFalse(pool1==null); assertFalse(pool2==null); assertFalse(pool3==null); assertTrue(error==null); lb.createPool(pool1); lb.createPool(pool2); lb.createPool(pool3); // verify successful creates; two registered with vips and one not assertTrue(lb.pools.containsKey(pool1.id)); assertTrue(lb.vips.get(pool1.vipId).pools.contains(pool1.id)); assertTrue(lb.pools.containsKey(pool2.id)); assertTrue(lb.vips.get(pool2.vipId).pools.contains(pool2.id)); assertTrue(lb.pools.containsKey(pool3.id)); assertFalse(lb.vips.containsKey(pool3.vipId)); } @Test public void testRemovePool() { testCreateVip(); testCreatePool(); // verify correct initial condition assertFalse(vip1==null); assertFalse(vip2==null); assertFalse(pool1==null); assertFalse(pool2==null); assertFalse(pool3==null); lb.removePool(pool1.id); lb.removePool(pool2.id); lb.removePool(pool3.id); // verify correct removal assertFalse(lb.pools.containsKey(pool1.id)); assertFalse(lb.pools.containsKey(pool2.id)); assertFalse(lb.pools.containsKey(pool3.id)); //verify pool cleanup from vip assertFalse(lb.vips.get(pool1.vipId).pools.contains(pool1.id)); assertFalse(lb.vips.get(pool2.vipId).pools.contains(pool2.id)); } @Test public void testCreateMember() { String postData1, postData2, postData3, postData4; IOException error = null; testCreateVip(); testCreatePool(); postData1 = "{\"id\":\"1\",\"address\":\"10.0.0.3\",\"port\":\"8\",\"pool_id\":\"1\"}"; postData2 = "{\"id\":\"2\",\"address\":\"10.0.0.4\",\"port\":\"8\",\"pool_id\":\"1\"}"; postData3 = "{\"id\":\"3\",\"address\":\"10.0.0.5\",\"port\":\"100\",\"pool_id\":\"2\"}"; postData4 = "{\"id\":\"4\",\"address\":\"10.0.0.6\",\"port\":\"100\",\"pool_id\":\"2\"}"; try { member1 = membersResource.jsonToMember(postData1); } catch (IOException e) { error = e; } try { member2 = membersResource.jsonToMember(postData2); } catch (IOException e) { error = e; } try { member3 = membersResource.jsonToMember(postData3); } catch (IOException e) { error = e; } try { member4 = membersResource.jsonToMember(postData4); } catch (IOException e) { error = e; } // verify correct parsing assertFalse(member1==null); assertFalse(member2==null); assertFalse(member3==null); assertFalse(member4==null); assertTrue(error==null); lb.createMember(member1); lb.createMember(member2); lb.createMember(member3); lb.createMember(member4); // add the same server a second time lb.createMember(member1); // verify successful creates assertTrue(lb.members.containsKey(member1.id)); assertTrue(lb.members.containsKey(member2.id)); assertTrue(lb.members.containsKey(member3.id)); assertTrue(lb.members.containsKey(member4.id)); assertTrue(lb.pools.get(member1.poolId).members.size()==2); assertTrue(lb.pools.get(member3.poolId).members.size()==2); // member1 should inherit valid vipId from pool assertTrue(lb.vips.get(member1.vipId)!=null); } @Test public void testRemoveMember() { testCreateVip(); testCreatePool(); testCreateMember(); // verify correct initial condition assertFalse(vip1==null); assertFalse(vip2==null); assertFalse(pool1==null); assertFalse(pool2==null); assertFalse(pool3==null); assertFalse(member1==null); assertFalse(member2==null); assertFalse(member3==null); assertFalse(member4==null); lb.removeMember(member1.id); lb.removeMember(member2.id); lb.removeMember(member3.id); lb.removeMember(member4.id); // verify correct removal assertFalse(lb.members.containsKey(member1.id)); assertFalse(lb.members.containsKey(member2.id)); assertFalse(lb.members.containsKey(member3.id)); assertFalse(lb.members.containsKey(member4.id)); //verify member cleanup from pool assertFalse(lb.pools.get(member1.poolId).members.contains(member1.id)); assertFalse(lb.pools.get(member2.poolId).members.contains(member2.id)); assertFalse(lb.pools.get(member3.poolId).members.contains(member3.id)); assertFalse(lb.pools.get(member4.poolId).members.contains(member4.id)); } @Test public void testTwoSubsequentIcmpRequests() throws Exception { testCreateVip(); testCreatePool(); testCreateMember(); IOFSwitch sw1; IPacket arpRequest1, arpReply1, icmpPacket1, icmpPacket2; byte[] arpRequest1Serialized; byte[] arpReply1Serialized; byte[] icmpPacket1Serialized, icmpPacket2Serialized; OFPacketIn arpRequestPacketIn1; OFPacketIn icmpPacketIn1, icmpPacketIn2; OFPacketOut arpReplyPacketOut1; Capture<OFMessage> wc1 = EasyMock.newCapture(CaptureType.ALL); sw1 = EasyMock.createNiceMock(IOFSwitch.class); expect(sw1.getId()).andReturn(DatapathId.of(1L)).anyTimes(); expect(sw1.hasAttribute(IOFSwitch.PROP_SUPPORTS_OFPP_TABLE)).andReturn(true).anyTimes(); expect(sw1.getOFFactory()).andReturn(factory).anyTimes(); expect(sw1.write(capture(wc1))).andReturn(true).anyTimes(); replay(sw1); sfp.switchAdded(DatapathId.of(1L)); verify(sw1); /* Test plan: * - two clients and two servers on sw1 port 1, 2, 3, 4 * - mock arp request received towards vip1 from (1L, 1) * - proxy arp got pushed out to (1L, 1)- check sw1 getting the packetout * - mock icmp request received towards vip1 from (1L, 1) * - device manager list of devices queried to identify source and dest devices * - routing engine queried to get inbound and outbound routes * - check getRoute calls and responses * - sfp called to install flows * - check sfp calls */ // Build topology reset(topology); expect(topology.isBroadcastAllowed(DatapathId.of(anyLong()), OFPort.of(anyShort()))).andReturn(true).anyTimes(); expect(topology.getClusterId(DatapathId.of(1L))).andReturn(DatapathId.of(1L)).anyTimes(); expect(topology.isAttachmentPointPort(DatapathId.of(1L), OFPort.of(1))).andReturn(true).anyTimes(); expect(topology.isAttachmentPointPort(DatapathId.of(1L), OFPort.of(2))).andReturn(true).anyTimes(); expect(topology.isAttachmentPointPort(DatapathId.of(1L), OFPort.of(3))).andReturn(true).anyTimes(); expect(topology.isAttachmentPointPort(DatapathId.of(1L), OFPort.of(4))).andReturn(true).anyTimes(); replay(topology); // Build arp packets arpRequest1 = new Ethernet() .setSourceMACAddress("00:00:00:00:00:01") .setDestinationMACAddress("ff:ff:ff:ff:ff:ff") .setEtherType(EthType.ARP) .setVlanID((short) 0) .setPriorityCode((byte) 0) .setPayload( new ARP() .setHardwareType(ARP.HW_TYPE_ETHERNET) .setProtocolType(ARP.PROTO_TYPE_IP) .setHardwareAddressLength((byte) 6) .setProtocolAddressLength((byte) 4) .setOpCode(ARP.OP_REQUEST) .setSenderHardwareAddress(MacAddress.of("00:00:00:00:00:01")) .setSenderProtocolAddress(IPv4Address.of("10.0.0.1")) .setTargetHardwareAddress(MacAddress.of("00:00:00:00:00:00")) .setTargetProtocolAddress(IPv4Address.of("10.0.0.100"))); arpRequest1Serialized = arpRequest1.serialize(); arpRequestPacketIn1 = factory.buildPacketIn() .setMatch(factory.buildMatch().setExact(MatchField.IN_PORT, OFPort.of(1)).build()) .setBufferId(OFBufferId.NO_BUFFER) .setData(arpRequest1Serialized) .setReason(OFPacketInReason.NO_MATCH) .build(); IFloodlightProviderService.bcStore.put(cntx, IFloodlightProviderService.CONTEXT_PI_PAYLOAD, (Ethernet) arpRequest1); // Mock proxy arp packet-out arpReply1 = new Ethernet() .setSourceMACAddress(LBVip.LB_PROXY_MAC) .setDestinationMACAddress(MacAddress.of("00:00:00:00:00:01")) .setEtherType(EthType.ARP) .setVlanID((short) 0) .setPriorityCode((byte) 0) .setPayload( new ARP() .setHardwareType(ARP.HW_TYPE_ETHERNET) .setProtocolType(ARP.PROTO_TYPE_IP) .setHardwareAddressLength((byte) 6) .setProtocolAddressLength((byte) 4) .setOpCode(ARP.OP_REPLY) .setSenderHardwareAddress(MacAddress.of(LBVip.LB_PROXY_MAC)) .setSenderProtocolAddress(IPv4Address.of("10.0.0.100")) .setTargetHardwareAddress(MacAddress.of("00:00:00:00:00:01")) .setTargetProtocolAddress(IPv4Address.of("10.0.0.1"))); arpReply1Serialized = arpReply1.serialize(); List<OFAction> poactions = new ArrayList<OFAction>(); poactions.add(factory.actions().output(arpRequestPacketIn1.getMatch().get(MatchField.IN_PORT), Integer.MAX_VALUE)); arpReplyPacketOut1 = factory.buildPacketOut() .setBufferId(OFBufferId.NO_BUFFER) .setInPort(OFPort.ANY) .setActions(poactions) .setData(arpReply1Serialized) .setXid(22) .build(); sw1.write(arpReplyPacketOut1); lb.receive(sw1, arpRequestPacketIn1, cntx); verify(sw1, topology); assertTrue(wc1.hasCaptured()); // wc1 should get packetout List<OFMessage> msglist1 = wc1.getValues(); for (OFMessage m: msglist1) { if (m instanceof OFPacketOut) assertEquals(OFMessageUtils.OFMessageIgnoreXid.of(arpReplyPacketOut1), OFMessageUtils.OFMessageIgnoreXid.of(m)); else assertTrue(false); // unexpected message } // // Skip arpRequest2 test - in reality this will happen, but for unit test the same logic // is already validated with arpRequest1 test above // // Keep the StaticFlowEntryPusher happy with a switch in the switch service Map<DatapathId, IOFSwitch> switches = new HashMap<DatapathId, IOFSwitch>(1); switches.put(DatapathId.of(1), sw1); getMockSwitchService().setSwitches(switches); // Build icmp packets icmpPacket1 = new Ethernet() .setSourceMACAddress("00:00:00:00:00:01") .setDestinationMACAddress(LBVip.LB_PROXY_MAC) .setEtherType(EthType.IPv4) .setVlanID((short) 0) .setPriorityCode((byte) 0) .setPayload( new IPv4() .setSourceAddress("10.0.0.1") .setDestinationAddress("10.0.0.100") .setProtocol(IpProtocol.ICMP) .setPayload(new ICMP() .setIcmpCode((byte) 0) .setIcmpType((byte) 0))); icmpPacket1Serialized = icmpPacket1.serialize(); icmpPacketIn1 = OFFactories.getFactory(OFVersion.OF_13).buildPacketIn() .setBufferId(OFBufferId.NO_BUFFER) .setMatch(OFFactories.getFactory(OFVersion.OF_13).buildMatch().setExact(MatchField.IN_PORT, OFPort.of(1)).build()) .setData(icmpPacket1Serialized) .setReason(OFPacketInReason.NO_MATCH) .build(); icmpPacket2 = new Ethernet() .setSourceMACAddress("00:00:00:00:00:02") .setDestinationMACAddress(LBVip.LB_PROXY_MAC) .setEtherType(EthType.IPv4) .setVlanID((short) 0) .setPriorityCode((byte) 0) .setPayload( new IPv4() .setSourceAddress("10.0.0.2") .setDestinationAddress("10.0.0.100") .setProtocol(IpProtocol.ICMP) .setPayload(new ICMP() .setIcmpCode((byte) 0) .setIcmpType((byte) 0))); icmpPacket2Serialized = icmpPacket2.serialize(); icmpPacketIn2 = OFFactories.getFactory(OFVersion.OF_13).buildPacketIn() .setBufferId(OFBufferId.NO_BUFFER) .setMatch(OFFactories.getFactory(OFVersion.OF_13).buildMatch().setExact(MatchField.IN_PORT, OFPort.of(2)).build()) .setData(icmpPacket2Serialized) .setReason(OFPacketInReason.NO_MATCH) .build(); MacAddress dataLayerSource1 = ((Ethernet)icmpPacket1).getSourceMACAddress(); IPv4Address networkSource1 = ((IPv4)((Ethernet)icmpPacket1).getPayload()).getSourceAddress(); MacAddress dataLayerDest1 = MacAddress.of("00:00:00:00:00:03"); IPv4Address networkDest1 = IPv4Address.of("10.0.0.3"); MacAddress dataLayerSource2 = ((Ethernet)icmpPacket2).getSourceMACAddress(); IPv4Address networkSource2 = ((IPv4)((Ethernet)icmpPacket2).getPayload()).getSourceAddress(); MacAddress dataLayerDest2 = MacAddress.of("00:00:00:00:00:04"); IPv4Address networkDest2 = IPv4Address.of("10.0.0.4"); deviceManager.learnEntity(dataLayerSource1, VlanVid.ZERO, networkSource1, IPv6Address.NONE, DatapathId.of(1), OFPort.of(1)); deviceManager.learnEntity(dataLayerSource2, VlanVid.ZERO, networkSource2, IPv6Address.NONE, DatapathId.of(1), OFPort.of(2)); deviceManager.learnEntity(dataLayerDest1, VlanVid.ZERO, networkDest1, IPv6Address.NONE, DatapathId.of(1), OFPort.of(3)); deviceManager.learnEntity(dataLayerDest2, VlanVid.ZERO, networkDest2, IPv6Address.NONE, DatapathId.of(1), OFPort.of(4)); // in bound #1 Path route1 = new Path(DatapathId.of(1L), DatapathId.of(1L)); List<NodePortTuple> nptList1 = new ArrayList<NodePortTuple>(); nptList1.add(new NodePortTuple(DatapathId.of(1L), OFPort.of(1))); nptList1.add(new NodePortTuple(DatapathId.of(1L), OFPort.of(3))); route1.setPath(nptList1); expect(routingEngine.getPath(DatapathId.of(1L), OFPort.of(1), DatapathId.of(1L), OFPort.of(3))).andReturn(route1).atLeastOnce(); // outbound #1 Path route2 = new Path(DatapathId.of(1L), DatapathId.of(1L)); List<NodePortTuple> nptList2 = new ArrayList<NodePortTuple>(); nptList2.add(new NodePortTuple(DatapathId.of(1L), OFPort.of(3))); nptList2.add(new NodePortTuple(DatapathId.of(1L), OFPort.of(1))); route2.setPath(nptList2); expect(routingEngine.getPath(DatapathId.of(1L), OFPort.of(3), DatapathId.of(1L), OFPort.of(1))).andReturn(route2).atLeastOnce(); // inbound #2 Path route3 = new Path(DatapathId.of(1L), DatapathId.of(1L)); List<NodePortTuple> nptList3 = new ArrayList<NodePortTuple>(); nptList3.add(new NodePortTuple(DatapathId.of(1L), OFPort.of(2))); nptList3.add(new NodePortTuple(DatapathId.of(1L), OFPort.of(4))); route3.setPath(nptList3); expect(routingEngine.getPath(DatapathId.of(1L), OFPort.of(2), DatapathId.of(1L), OFPort.of(4))).andReturn(route3).atLeastOnce(); // outbound #2 Path route4 = new Path(DatapathId.of(1L), DatapathId.of(1L)); List<NodePortTuple> nptList4 = new ArrayList<NodePortTuple>(); nptList4.add(new NodePortTuple(DatapathId.of(1L), OFPort.of(4))); nptList4.add(new NodePortTuple(DatapathId.of(1L), OFPort.of(2))); route4.setPath(nptList3); expect(routingEngine.getPath(DatapathId.of(1L), OFPort.of(4), DatapathId.of(1L), OFPort.of(2))).andReturn(route4).atLeastOnce(); replay(routingEngine); wc1.reset(); IFloodlightProviderService.bcStore.put(cntx, IFloodlightProviderService.CONTEXT_PI_PAYLOAD, (Ethernet) icmpPacket1); lb.receive(sw1, icmpPacketIn1, cntx); IFloodlightProviderService.bcStore.put(cntx, IFloodlightProviderService.CONTEXT_PI_PAYLOAD, (Ethernet) icmpPacket2); lb.receive(sw1, icmpPacketIn2, cntx); assertTrue(wc1.hasCaptured()); // wc1 should get packetout List<OFMessage> msglist2 = wc1.getValues(); assertTrue(msglist2.size()==2); // has inbound and outbound packetouts // TODO: not seeing flowmods yet ... Map<String, OFMessage> map = sfp.getEntries(DatapathId.of(1L)); assertTrue(map.size()==4); } }