/*
* Copyright (c) 2013 Big Switch Networks, Inc.
*
* Licensed under the Eclipse Public License, Version 1.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.eclipse.org/legal/epl-v10.html
*
* 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 org.sdnplatform.flowcache;
import static org.easymock.EasyMock.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
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.After;
import org.junit.Before;
import org.junit.Test;
import org.openflow.protocol.OFFlowMod;
import org.openflow.protocol.OFMatch;
import org.openflow.protocol.OFMatchWithSwDpid;
import org.openflow.protocol.OFMessage;
import org.openflow.protocol.OFPacketIn;
import org.openflow.protocol.OFPacketOut;
import org.openflow.protocol.OFStatisticsReply;
import org.openflow.protocol.OFStatisticsRequest;
import org.openflow.protocol.OFType;
import org.openflow.protocol.OFPacketIn.OFPacketInReason;
import org.openflow.protocol.action.OFAction;
import org.openflow.protocol.action.OFActionOutput;
import org.openflow.protocol.statistics.OFFlowStatisticsReply;
import org.openflow.protocol.statistics.OFFlowStatisticsRequest;
import org.openflow.protocol.statistics.OFStatistics;
import org.openflow.protocol.statistics.OFStatisticsType;
import org.sdnplatform.core.ListenerContext;
import org.sdnplatform.core.IControllerService;
import org.sdnplatform.core.IOFSwitch;
import org.sdnplatform.core.module.ModuleContext;
import org.sdnplatform.core.test.MockThreadPoolService;
import org.sdnplatform.core.util.AppCookie;
import org.sdnplatform.counter.CounterStore;
import org.sdnplatform.counter.ICounterStoreService;
import org.sdnplatform.devicemanager.IDevice;
import org.sdnplatform.devicemanager.IDeviceService;
import org.sdnplatform.devicemanager.IEntityClassifierService;
import org.sdnplatform.devicemanager.SwitchPort;
import org.sdnplatform.devicemanager.internal.DefaultEntityClassifier;
import org.sdnplatform.devicemanager.internal.MockBetterDeviceManager;
import org.sdnplatform.flowcache.BetterFlowCache;
import org.sdnplatform.flowcache.BetterFlowReconcileManager;
import org.sdnplatform.flowcache.FCQueryObj;
import org.sdnplatform.flowcache.FlowCacheObj;
import org.sdnplatform.flowcache.FlowCacheQueryResp;
import org.sdnplatform.flowcache.IFlowCacheService;
import org.sdnplatform.flowcache.IFlowReconcileService;
import org.sdnplatform.flowcache.QRFlowCacheObj;
import org.sdnplatform.flowcache.IFlowCacheService.FCQueryEvType;
import org.sdnplatform.forwarding.Forwarding;
import org.sdnplatform.forwarding.IForwardingService;
import org.sdnplatform.forwarding.IRewriteService;
import org.sdnplatform.linkdiscovery.ILinkDiscoveryService;
import org.sdnplatform.netvirt.core.VNS;
import org.sdnplatform.netvirt.core.VNSInterface;
import org.sdnplatform.netvirt.manager.INetVirtManagerService;
import org.sdnplatform.netvirt.manager.internal.NetVirtManagerImpl;
import org.sdnplatform.netvirt.virtualrouting.IVirtualRoutingService;
import org.sdnplatform.netvirt.virtualrouting.internal.VirtualRouting;
import org.sdnplatform.packet.Data;
import org.sdnplatform.packet.Ethernet;
import org.sdnplatform.packet.IPacket;
import org.sdnplatform.packet.IPv4;
import org.sdnplatform.packet.UDP;
import org.sdnplatform.restserver.IRestApiService;
import org.sdnplatform.restserver.RestApiServer;
import org.sdnplatform.routing.ForwardingBase;
import org.sdnplatform.routing.IRoutingDecision;
import org.sdnplatform.routing.IRoutingService;
import org.sdnplatform.routing.Route;
import org.sdnplatform.storage.IStorageSourceService;
import org.sdnplatform.storage.memory.MemoryStorageSource;
import org.sdnplatform.tagmanager.ITagManagerService;
import org.sdnplatform.test.PlatformTestCase;
import org.sdnplatform.threadpool.IThreadPoolService;
import org.sdnplatform.topology.ITopologyService;
import org.sdnplatform.topology.NodePortTuple;
import org.sdnplatform.tunnelmanager.ITunnelManagerService;
public class BetterFlowReconcileMgrTest extends PlatformTestCase {
protected ListenerContext cntx12, cntx14;
protected Forwarding forwarding;
protected BetterFlowReconcileManager flowReconcileMgr;
protected NetVirtManagerImpl netVirtManager;
protected VirtualRouting virtualRouting;
protected MockBetterDeviceManager betterDeviceManager;
protected IRewriteService rewriteService;
protected BetterFlowCache betterFlowCacheMgr;
protected MockThreadPoolService tp;
protected DefaultEntityClassifier entityClassifier;
protected MemoryStorageSource storageSource;
protected RestApiServer restApi;
protected CounterStore counterStore;
// Mocked modules
protected IRoutingService routingEngine;
protected ITopologyService topology;
protected ITunnelManagerService tunnelManager;
protected ILinkDiscoveryService linkDiscovery;
protected IOFSwitch sw1, sw2, sw3;
protected IDevice device1, device2, device2alt, device4;
protected OFPacketIn packetIn12, packetIn14;
protected OFPacketOut packetOut12, packetOut14;
protected IPacket testPacket12, testPacket21, testPacket14;
protected byte[] testPacketSerialized12;
protected byte[] testPacketSerialized21;
protected byte[] testPacketSerialized14;
protected IRoutingDecision decision12, decision14;
@Override
@Before
public void setUp() throws Exception {
super.setUp();
ModuleContext fmc = new ModuleContext();
// Mock context
cntx12 = new ListenerContext();
cntx14 = new ListenerContext();
forwarding = new Forwarding();
flowReconcileMgr = new BetterFlowReconcileManager();
netVirtManager = new NetVirtManagerImpl();
virtualRouting = new VirtualRouting();
betterDeviceManager = new MockBetterDeviceManager();
rewriteService = createMock(IRewriteService.class);
betterFlowCacheMgr = new BetterFlowCache();
tp = new MockThreadPoolService();
entityClassifier = new DefaultEntityClassifier();
storageSource = new MemoryStorageSource();
restApi = new RestApiServer();
counterStore = new CounterStore();
mockControllerProvider = getMockControllerProvider();
routingEngine = createMock(IRoutingService.class);
topology = createMock(ITopologyService.class);
tunnelManager = createMock(ITunnelManagerService.class);
linkDiscovery = createMock(ILinkDiscoveryService.class);
fmc.addService(IForwardingService.class, forwarding);
fmc.addService(IFlowReconcileService.class, flowReconcileMgr);
fmc.addService(INetVirtManagerService.class, netVirtManager);
fmc.addService(IVirtualRoutingService.class, virtualRouting);
fmc.addService(IDeviceService.class, betterDeviceManager);
fmc.addService(ITagManagerService.class, betterDeviceManager);
fmc.addService(IRewriteService.class, rewriteService);
fmc.addService(IFlowCacheService.class, betterFlowCacheMgr);
fmc.addService(IThreadPoolService.class, tp);
fmc.addService(IEntityClassifierService.class, entityClassifier);
fmc.addService(IStorageSourceService.class, storageSource);
fmc.addService(IRestApiService.class, restApi);
fmc.addService(ICounterStoreService.class, counterStore);
fmc.addService(IControllerService.class,
mockControllerProvider);
fmc.addService(IRoutingService.class, routingEngine);
fmc.addService(ITopologyService.class, topology);
fmc.addService(ITunnelManagerService.class, tunnelManager);
fmc.addService(ILinkDiscoveryService.class, linkDiscovery);
forwarding.init(fmc);
flowReconcileMgr.init(fmc);
netVirtManager.init(fmc);
virtualRouting.init(fmc);
betterDeviceManager.init(fmc);
betterFlowCacheMgr.init(fmc);
tp.init(fmc);
entityClassifier.init(fmc);
storageSource.init(fmc);
restApi.init(fmc);
counterStore.init(fmc);
topology.addListener(flowReconcileMgr);
expectLastCall().times(1);
topology.addListener(betterDeviceManager);
expectLastCall().times(1);
replay(topology);
forwarding.startUp(fmc);
flowReconcileMgr.startUp(fmc);
netVirtManager.startUp(fmc);
virtualRouting.startUp(fmc);
betterDeviceManager.startUp(fmc);
betterFlowCacheMgr.startUp(fmc);
tp.startUp(fmc);
entityClassifier.startUp(fmc);
storageSource.startUp(fmc);
restApi.startUp(fmc);
counterStore.startUp(fmc);
reset(topology);
betterFlowCacheMgr.setAppName("netVirt");
betterFlowCacheMgr.periodicSwScanInitDelayMsec = 0;
betterFlowCacheMgr.periodicSwScanIntervalMsec = 1000; // 1 second
// Mock tunnel manager
expect(tunnelManager.isTunnelEndpoint(anyObject(IDevice.class)))
.andReturn(false).anyTimes();
expect(tunnelManager.isTunnelEndpoint(null)).andReturn(false).anyTimes();
expect(tunnelManager.getTunnelPortNumber(EasyMock.anyLong())).andReturn(null).anyTimes();
expect(tunnelManager.getTunnelLoopbackPort(EasyMock.anyLong())).andReturn(null).anyTimes();
replay(tunnelManager);
// Mock rewrite service
resetToNice(rewriteService);
rewriteService.getSwitchPortVlanMode(anyObject(SwitchPort.class),
anyObject(String.class),
anyShort(),
anyBoolean());
expectLastCall().andReturn(Ethernet.VLAN_UNTAGGED).anyTimes();
replay(rewriteService);
// Mock switches
sw1 = EasyMock.createNiceMock(IOFSwitch.class);
expect(sw1.getId()).andReturn(1L).anyTimes();
expect(sw1.isConnected()).andReturn(true).anyTimes();
expect(sw1.getStringId()).andReturn("00:00:00:00:00:00:00:01").
anyTimes();
expect(topology.getL2DomainId(1L)).andReturn(1L).anyTimes();
sw2 = EasyMock.createNiceMock(IOFSwitch.class);
expect(sw2.getId()).andReturn(2L).anyTimes();
expect(sw2.isConnected()).andReturn(true).anyTimes();
expect(sw2.getStringId()).andReturn("00:00:00:00:00:00:00:02").
anyTimes();
expect(topology.getL2DomainId(2L)).andReturn(1L).anyTimes();
sw3 = EasyMock.createNiceMock(IOFSwitch.class);
expect(sw3.getId()).andReturn(3L).anyTimes();
expect(sw3.isConnected()).andReturn(true).anyTimes();
expect(sw3.getStringId()).andReturn("00:00:00:00:00:00:00:03").
anyTimes();
//switch 3 belongs to cluster 3. as it is on its own.
expect(topology.getL2DomainId(3L)).andReturn(1L).anyTimes();
// Load the switch map
Map<Long, IOFSwitch> switches = new HashMap<Long, IOFSwitch>();
switches.put(1L, sw1);
switches.put(2L, sw2);
switches.put(3L, sw3);
mockControllerProvider.setSwitches(switches);
assertEquals(switches, mockControllerProvider.getSwitches());
// Build test packets
testPacket12 = new Ethernet()
.setDestinationMACAddress("00:00:00:00:00:02")
.setSourceMACAddress("00:00:00:00:00:01")
.setEtherType(Ethernet.TYPE_IPv4)
.setPayload(
new IPv4()
.setTtl((byte) 128)
.setDestinationAddress("192.168.1.2")
.setSourceAddress("192.168.1.1")
.setPayload(new UDP()
.setSourcePort((short) 5000)
.setDestinationPort((short) 5001)
.setPayload(new Data(new byte[] {0x01}))));
testPacketSerialized12 = testPacket12.serialize();
testPacket21 = new Ethernet()
.setDestinationMACAddress("00:00:00:00:00:01")
.setSourceMACAddress("00:00:00:00:00:02")
.setEtherType(Ethernet.TYPE_IPv4)
.setPayload(
new IPv4()
.setTtl((byte) 128)
.setDestinationAddress("192.168.1.1")
.setSourceAddress("192.168.1.2")
.setPayload(new UDP()
.setSourcePort((short) 5000)
.setDestinationPort((short) 5001)
.setPayload(new Data(new byte[] {0x01}))));
testPacketSerialized21= testPacket21.serialize();
testPacket14 = new Ethernet()
.setDestinationMACAddress("00:00:00:00:00:04")
.setSourceMACAddress("00:00:00:00:00:01")
.setEtherType(Ethernet.TYPE_IPv4)
.setPayload(
new IPv4()
.setTtl((byte) 128)
.setDestinationAddress("192.168.1.4")
.setSourceAddress("192.168.1.1")
.setPayload(new UDP()
.setSourcePort((short) 5000)
.setDestinationPort((short) 5001)
.setPayload(new Data(new byte[] {0x01}))));
testPacketSerialized14 = testPacket14.serialize();
expect(topology.isAttachmentPointPort(EasyMock.anyLong(),
EasyMock.anyShort()))
.andReturn(true).anyTimes();
replay(topology);
// Build src and dest devices
byte[] dataLayerDevice1 = ((Ethernet)testPacket12).
getSourceMACAddress();
byte[] dataLayerDevice2 = ((Ethernet)testPacket21).
getSourceMACAddress();
byte[] dataLayerDevice4 =
((Ethernet)testPacket14).getDestinationMACAddress();
int networkSource1 = ((IPv4)((Ethernet)testPacket12).getPayload()).
getSourceAddress();
int networkSource2 = ((IPv4)((Ethernet)testPacket21).getPayload()).
getSourceAddress();
int networkSource4 = ((IPv4)((Ethernet)testPacket14).getPayload()).
getDestinationAddress();
device1 = betterDeviceManager.learnEntity(
Ethernet.toLong(dataLayerDevice1),
null,
networkSource1,
1L, 1, false);
device2 = betterDeviceManager.learnEntity(
Ethernet.toLong(dataLayerDevice2),
null,
networkSource2,
1L, 2, false);
device4 = betterDeviceManager.learnEntity(
Ethernet.toLong(dataLayerDevice4),
null,
networkSource4,
3L, 1, false);
// Mock Packet-in
packetIn12 = ((OFPacketIn) mockControllerProvider.getOFMessageFactory().
getMessage(OFType.PACKET_IN))
.setBufferId(-1)
.setInPort((short) 1)
.setPacketData(testPacketSerialized12)
.setReason(OFPacketInReason.NO_MATCH)
.setTotalLength((short) testPacketSerialized12.length);
packetIn14 = ((OFPacketIn) mockControllerProvider.
getOFMessageFactory().getMessage(OFType.PACKET_IN))
.setBufferId(-1)
.setInPort((short) 1)
.setPacketData(testPacketSerialized14)
.setReason(OFPacketInReason.NO_MATCH)
.setTotalLength((short) testPacketSerialized14.length);
// Mock Packet-out
packetOut12 =
(OFPacketOut) mockControllerProvider.
getOFMessageFactory().getMessage(OFType.PACKET_OUT);
packetOut12.setBufferId(this.packetIn12.getBufferId())
.setInPort(this.packetIn12.getInPort());
List<OFAction> poactions12 = new ArrayList<OFAction>();
poactions12.add(new OFActionOutput((short)2, (short) 0xffff));
packetOut12.setActions(poactions12)
.setActionsLength((short) OFActionOutput.MINIMUM_LENGTH)
.setPacketData(testPacketSerialized12)
.setLengthU(OFPacketOut.MINIMUM_LENGTH+packetOut12.
getActionsLength()+testPacketSerialized12.length);
packetOut14 =
(OFPacketOut) mockControllerProvider.getOFMessageFactory().
getMessage(OFType.PACKET_OUT);
packetOut14.setBufferId(this.packetIn14.getBufferId())
.setInPort(this.packetIn14.getInPort());
List<OFAction> poactions14 = new ArrayList<OFAction>();
poactions14.add(new OFActionOutput((short)3, (short) 0xffff));
packetOut14.setActions(poactions14)
.setActionsLength((short) OFActionOutput.MINIMUM_LENGTH)
.setPacketData(testPacketSerialized14)
.setLengthU(OFPacketOut.MINIMUM_LENGTH +
packetOut14.getActionsLength( ) +
testPacketSerialized14.length);
// Mock decision12
decision12 = createMock(IRoutingDecision.class);
expect(decision12.getSourceDevice()).andReturn(device1).atLeastOnce();
expect(decision12.getSourcePort()).andReturn(
new SwitchPort(1L, (short)1)).atLeastOnce();
ArrayList<IDevice> dstDevices12 = new ArrayList<IDevice>();
dstDevices12.add(device2);
expect(decision12.getDestinationDevices()).andReturn(dstDevices12).
atLeastOnce();
IRoutingDecision.rtStore.put(
cntx12, IRoutingDecision.CONTEXT_DECISION, decision12);
// set decision.getRoutingAction() based on test case
// Set SRC_IFACCES in context
List<VNSInterface> srcIfaces1 = new ArrayList<VNSInterface>();
String netVirtName = "default|DefaultEntityClass-default";
VNS netVirt1 = new VNS(netVirtName);
srcIfaces1.add(new VNSInterface("testSrcIface1", netVirt1, null, null));
INetVirtManagerService.bcStore.put(
cntx12, INetVirtManagerService.CONTEXT_SRC_IFACES, srcIfaces1);
IFlowCacheService.fcStore.put(
cntx12, IFlowCacheService.FLOWCACHE_APP_NAME, netVirtName);
IFlowCacheService.fcStore.put(
cntx12, IFlowCacheService.FLOWCACHE_APP_INSTANCE_NAME,
netVirt1.getName());
// Mock decision14
decision14 = createMock(IRoutingDecision.class);
expect(decision14.getSourceDevice()).andReturn(device1).atLeastOnce();
expect(decision14.getSourcePort()).andReturn(
new SwitchPort(1L, (short)1)).atLeastOnce();
ArrayList<IDevice> dstDevices14 = new ArrayList<IDevice>();
dstDevices14.add(device4);
expect(decision14.getDestinationDevices()).
andReturn(dstDevices14).atLeastOnce();
IRoutingDecision.rtStore.put(
cntx14, IRoutingDecision.CONTEXT_DECISION, decision14);
// set decision.getRoutingAction() based on test case
// Set SRC_IFACCES in context
List<VNSInterface> srcIfaces5 = new ArrayList<VNSInterface>();
netVirtName = "default|DefaultEntityClass-default";
VNS netVirt5 = new VNS(netVirtName);
srcIfaces5.add(new VNSInterface("testSrcIface5", netVirt5, null, null));
INetVirtManagerService.bcStore.put(
cntx14, INetVirtManagerService.CONTEXT_SRC_IFACES, srcIfaces5);
IFlowCacheService.fcStore.put(
cntx14, IFlowCacheService.FLOWCACHE_APP_NAME, netVirtName);
IFlowCacheService.fcStore.put(
cntx14, IFlowCacheService.FLOWCACHE_APP_INSTANCE_NAME,
netVirt5.getName());
}
@Override
@After
public void tearDown() {
verify(tunnelManager);
}
// Test host move handling:
// There are two types of host moves
// (a) Host move with port status change: Here host was directly connected
// to port P1 on openflow switch SW1. Then host moves from Port P1 to
// to Port P2 of switch SW2. Here the controller would get two
// port status change message. Down for P1 and up for P2
// (b) Host move with no port-status change: Here host was connected to
// a switch that was not connected by the controller and moves to
// another port on a switch which was also not connected to the
// controller. Here the controller would get an attachment-point change
// event for the host. Both of these cases are tested here.
// In each of these cases the flows that were *destined* to the moved host
// would queried from the flow-cache and they would be reprogrammed to
// towards the new attachment point of the moved host.
// Switch device1 ---Port 1---> SW1 ---Port 2 ---> device 2
// Trigger: device 2 moves from Port 2 to Port 4
@Test
public void testFlowCacheHostMove() throws Exception {
// Mock decision
expect(decision12.getRoutingAction()).andReturn(
IRoutingDecision.RoutingAction.FORWARD).atLeastOnce();
expect(decision12.getWildcards()).andReturn(null).atLeastOnce();
expect(decision12.getHardTimeout()).
andReturn(ForwardingBase.FLOWMOD_DEFAULT_HARD_TIMEOUT).atLeastOnce();
// Set destination as local and Mock route
Route route1 = new Route(1L, 1L);
route1.getPath().add(new NodePortTuple(1L, (short)1));
route1.getPath().add(new NodePortTuple(1L, (short)2));
long routeCookie12 = forwarding.getHashByMac(((Ethernet) testPacket12).getDestinationMAC().toLong());
expect(routingEngine.getRoute(1L, (short)1, 1L, (short)2, routeCookie12, true))
.andReturn(route1).atLeastOnce();
// Expected Flow-mods
OFMatch match = new OFMatch();
// Packet 1 to 2 from sw1, input port 1
match.loadFromPacket(testPacketSerialized12, (short) 1);
OFActionOutput action = new OFActionOutput((short)2, (short)0xffff);
List<OFAction> actions = new ArrayList<OFAction>();
actions.add(action);
OFFlowMod fm1 = (OFFlowMod) mockControllerProvider.
getOFMessageFactory().getMessage(OFType.FLOW_MOD);
Long cookie = 2L << 52;
fm1.setIdleTimeout((short)5)
.setPriority(forwarding.getAccessPriority())
.setMatch(match.clone()
.setWildcards(VirtualRouting.DEFAULT_HINT))
.setActions(actions)
.setBufferId(OFPacketOut.BUFFER_ID_NONE)
.setCookie(cookie)
.setFlags((short)1)
.setLengthU(OFFlowMod.MINIMUM_LENGTH+
OFActionOutput.MINIMUM_LENGTH);
// Record expected packet-outs/flow-mods
sw1.write(fm1, cntx12);
sw1.write(packetOut12, cntx12);
// Reset mocks, trigger the packet in, and validate results
reset(topology);
expect(topology.getL2DomainId(1L)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(2L)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(3L)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(1L, true)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(2L, true)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(3L, true)).andReturn(1L).anyTimes();
expect(topology.isAttachmentPointPort(EasyMock.anyLong(),
EasyMock.anyShort()))
.andReturn(true).anyTimes();
expect(topology.isAttachmentPointPort(1L, (short)1, true))
.andReturn(true).anyTimes();
expect(topology.getOutgoingSwitchPort(1, (short)1,
1, (short)2, true))
.andReturn(new NodePortTuple(1, (short)2)).anyTimes();
expect(topology.getIncomingSwitchPort(1, (short)1,
1, (short)2, true))
.andReturn(new NodePortTuple(1, (short)1)).anyTimes();
expect(topology.isAllowed(EasyMock.anyLong(), EasyMock.anyShort()))
.andReturn(true).anyTimes();
replay(sw1, routingEngine, decision12, topology);
forwarding.receive(sw1, this.packetIn12, cntx12);
betterFlowCacheMgr.updateFlush();
verify(sw1, routingEngine, decision12, topology);
// Check that the flow was added to flow-cache
assertEquals(1, betterFlowCacheMgr.getBfcCore().getActiveCnt());
assertEquals(0, betterFlowCacheMgr.getBfcCore().getInactiveCnt());
assertEquals(1, betterFlowCacheMgr.getBfcCore().getAddCnt());
assertEquals(0, betterFlowCacheMgr.getBfcCore().getCacheHitCnt());
assertEquals(0, betterFlowCacheMgr.getBfcCore().getNotDampenedCnt());
// Set flowReconcileManager as a DeviceListener
betterDeviceManager.addListener(flowReconcileMgr.deviceListener);
// Create a new flow-mod that is expected to be programmed
fm1.getActions().remove(0);
OFActionOutput actionNew = new OFActionOutput((short)4, (short)0xffff);
List<OFAction> actionsNew = new ArrayList<OFAction>();
actionsNew.add(actionNew);
fm1.setActions(actionsNew);
fm1.setPriority(forwarding.getAccessPriority());
fm1.setCommand(OFFlowMod.OFPFC_MODIFY);
reset(topology, routingEngine);
sw1.write(fm1, cntx12);
Route route2 = new Route(1L, 1L);
route2.getPath().add(new NodePortTuple(1L, (short)1));
route2.getPath().add(new NodePortTuple(1L, (short)4));
expect(routingEngine.getRoute(1L, (short)1, 1L, (short)4,routeCookie12, true))
.andReturn(route2).atLeastOnce();
expect(topology.getL2DomainId(1L)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(2L)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(3L)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(1L, true)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(2L, true)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(3L, true)).andReturn(1L).anyTimes();
expect(topology.getOutgoingSwitchPort(1, (short)1,
1, (short)4, true))
.andReturn(new NodePortTuple(1, (short)4)).anyTimes();
expect(topology.getOutgoingSwitchPort(1, (short)1,
1, (short)2, true))
.andReturn(new NodePortTuple(1, (short)4)).anyTimes();
expect(topology.getIncomingSwitchPort(1, (short)1,
1, (short)4, true))
.andReturn(new NodePortTuple(1, (short)1)).anyTimes();
expect(topology.getIncomingSwitchPort(1, (short)1,
1, (short)2, true))
.andReturn(new NodePortTuple(1, (short)1)).anyTimes();
expect(topology.isConsistent(EasyMock.anyLong(), EasyMock.anyShort(),
EasyMock.anyLong(), EasyMock.anyShort()))
.andReturn(false).anyTimes();
expect(topology.isBroadcastDomainPort(EasyMock.anyLong(),
EasyMock.anyShort()))
.andReturn(false).anyTimes();
expect(topology.isAttachmentPointPort(EasyMock.anyLong(),
EasyMock.anyShort()))
.andReturn(true).anyTimes();
expect(topology.isAttachmentPointPort(1L, (short)1, true))
.andReturn(true).anyTimes();
expect(topology.isInSameBroadcastDomain(EasyMock.anyLong(),
EasyMock.anyShort(),
EasyMock.anyLong(),
EasyMock.anyShort()))
.andReturn(false).anyTimes();
replay(topology, routingEngine);
// Now trigger Device Move with port status change
int pre_count = flowReconcileMgr.flowReconcileThreadRunCount.get();
device2alt = betterDeviceManager.
learnEntity(Ethernet.toLong(((Ethernet)testPacket21)
.getSourceMACAddress()),
null,
((IPv4)((Ethernet)testPacket21).getPayload()).
getSourceAddress(),
1L, 4, true);
Date startTime = new Date();
while (flowReconcileMgr.flowReconcileThreadRunCount.get() == pre_count) {
Date curTime = new Date();
assertTrue((curTime.getTime() - startTime.getTime()) < 5000);
}
FlowCacheQueryResp bfcQR = flowReconcileMgr.lastFCQueryResp;
assertEquals(FCQueryEvType.DEVICE_MOVED, bfcQR.queryObj.evType);
assertEquals(false, bfcQR.moreFlag);
assertEquals(1, bfcQR.qrFlowCacheObjList.size());
QRFlowCacheObj qrFcObj = bfcQR.qrFlowCacheObjList.get(0);
assertEquals(1L, qrFcObj.ofmWithSwDpid.getSwitchDataPathId());
assertEquals(1, qrFcObj.ofmWithSwDpid.getOfMatch().getInputPort());
assertEquals(Ethernet.toLong(qrFcObj.ofmWithSwDpid.getOfMatch()
.getDataLayerSource()),
device1.getMACAddress());
assertEquals(Ethernet.toLong(qrFcObj.ofmWithSwDpid.getOfMatch()
.getDataLayerDestination()),
device2.getMACAddress());
assertEquals(FlowCacheObj.FCActionPERMIT, qrFcObj.action);
verify(topology, routingEngine);
assertEquals(1, betterFlowCacheMgr.getBfcCore().getActiveCnt());
assertEquals(0, betterFlowCacheMgr.getBfcCore().getInactiveCnt());
assertEquals(2, betterFlowCacheMgr.getBfcCore().getAddCnt());
assertEquals(1, betterFlowCacheMgr.getBfcCore().getCacheHitCnt());
}
/* Test link down handling:
* When a link does down, FlowReconcile Manager , being linkDiscovery-aware
* gets a linkDiscoveryUp triggers a redeployment of all the flows that
* were routed over that failed link.
* If there is an alternate route then those flows would be
* re-established via an alternate path.
*/
@Test
public void testFlowCacheLinkDown() throws Exception {
// Mock decision
expect(decision14.getRoutingAction()).andReturn(
IRoutingDecision.RoutingAction.FORWARD).atLeastOnce();
expect(decision14.getWildcards()).andReturn(null).atLeastOnce();
expect(decision14.getHardTimeout()).
andReturn(ForwardingBase.FLOWMOD_DEFAULT_HARD_TIMEOUT).atLeastOnce();
// Set destination as on sw3 and Mock route
Route route = new Route(1L, 3L);
route.setPath(new ArrayList<NodePortTuple>());
/* Route: device1--[P1SW1P5]--[P5SW2P7]--[P7SW3P1]--device4 */
route.getPath().add(new NodePortTuple(1L, (short)1));
route.getPath().add(new NodePortTuple(1L, (short)7));
route.getPath().add(new NodePortTuple(2L, (short)7));
route.getPath().add(new NodePortTuple(2L, (short)5));
route.getPath().add(new NodePortTuple(3L, (short)5));
route.getPath().add(new NodePortTuple(3L, (short)1));
long routeCookie14 = forwarding.getHashByMac(((Ethernet) testPacket14).getDestinationMAC().toLong());
expect(routingEngine.getRoute(1L, (short)1, 3L, (short)1, routeCookie14, true))
.andReturn(route).atLeastOnce();
// Expected Flow-mods
OFMatch match = new OFMatch();
// Packet 1 to 4 from sw1, input port 1
match.loadFromPacket(testPacketSerialized14, (short) 1);
OFActionOutput action = new OFActionOutput((short)7, (short)0xffff);
List<OFAction> actions = new ArrayList<OFAction>();
actions.add(action);
OFFlowMod fm1 = (OFFlowMod) mockControllerProvider.
getOFMessageFactory().getMessage(OFType.FLOW_MOD);
Long cookie = 2L << 52;
fm1.setIdleTimeout((short)5)
.setPriority(forwarding.getAccessPriority())
.setMatch(match.clone()
.setWildcards(VirtualRouting.DEFAULT_HINT))
.setActions(actions)
.setBufferId(OFPacketOut.BUFFER_ID_NONE)
.setCookie(cookie)
.setFlags((short)1)
.setLengthU(OFFlowMod.MINIMUM_LENGTH+OFActionOutput.MINIMUM_LENGTH);
// Record expected packet-outs/flow-mods
Capture<OFMessage> wc1 = new Capture<OFMessage>(CaptureType.ALL);
Capture<ListenerContext> bc1 =
new Capture<ListenerContext>(CaptureType.ALL);
sw1.write(capture(wc1), capture(bc1));
// Reset mocks, trigger the packet in, and validate results
reset(topology);
expect(topology.getL2DomainId(1L)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(2L)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(3L)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(1L, true)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(2L, true)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(3L, true)).andReturn(1L).anyTimes();
expect(topology.getOutgoingSwitchPort(1, (short)1,
3, (short)1, true))
.andReturn(new NodePortTuple(3, (short)1)).anyTimes();
expect(topology.getIncomingSwitchPort(1, (short)1,
3, (short)1, true))
.andReturn(new NodePortTuple(1, (short)1)).anyTimes();
expect(topology.isAttachmentPointPort(1L, (short)1))
.andReturn(true).anyTimes();
expect(topology.isAttachmentPointPort(3L, (short)1))
.andReturn(true).anyTimes();
expect(topology.isAttachmentPointPort(1L, (short)1, true))
.andReturn(true).anyTimes();
expect(topology.isAllowed(EasyMock.anyLong(), EasyMock.anyShort()))
.andReturn(true).anyTimes();
replay(sw1, sw2, sw3, routingEngine, decision14, topology);
forwarding.receive(sw1, this.packetIn14, cntx14);
betterFlowCacheMgr.updateFlush();
verify(sw1, sw2, sw3, routingEngine, decision14, topology);
OFMessage m = wc1.getValues().get(0);
assertEquals(fm1, m);
// Check that the flow was added to flow-cache
assertEquals(1, betterFlowCacheMgr.getBfcCore().getActiveCnt());
assertEquals(0, betterFlowCacheMgr.getBfcCore().getInactiveCnt());
assertEquals(1, betterFlowCacheMgr.getBfcCore().getAddCnt());
assertEquals(0, betterFlowCacheMgr.getBfcCore().getCacheHitCnt());
assertEquals(0, betterFlowCacheMgr.getBfcCore().getNotDampenedCnt());
/* Create a new route bypassing the link */
/* ----- Reset the mocks ------------- */
reset(sw1, sw2, sw3, decision14, routingEngine, topology);
expect(sw1.getId()).andReturn(1L).anyTimes();
expect(sw2.getId()).andReturn(2L).anyTimes();
expect(sw3.getId()).andReturn(3L).anyTimes();
expect(sw1.isConnected()).andReturn(true).atLeastOnce();
expect(sw1.getStringId()).andReturn("00:00:00:00:00:00:00:01").
anyTimes();
expect(sw2.getStringId()).andReturn("00:00:00:00:00:00:00:02").
anyTimes();
expect(topology.getL2DomainId(1L)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(2L)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(3L)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(1L, true)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(2L, true)).andReturn(1L).anyTimes();
expect(topology.getL2DomainId(3L, true)).andReturn(1L).anyTimes();
expect(topology.getOutgoingSwitchPort(1, (short)1,
3, (short)1, true))
.andReturn(new NodePortTuple(3, (short)1)).anyTimes();
expect(topology.getIncomingSwitchPort(1, (short)1,
3, (short)1, true))
.andReturn(new NodePortTuple(1, (short)1)).anyTimes();
expect(topology.isAttachmentPointPort(1L, (short)1))
.andReturn(true).anyTimes();
expect(topology.isAttachmentPointPort(1L, (short)1, true))
.andReturn(true).anyTimes();
expect(topology.isAttachmentPointPort(3L, (short)1))
.andReturn(true).anyTimes();
expect(topology.isInSameBroadcastDomain(1L, (short)7, 2L, (short)7))
.andReturn(false).anyTimes();
OFStatisticsRequest req = new OFStatisticsRequest();
req.setStatisticType(OFStatisticsType.FLOW);
int requestLength = req.getLengthU();
OFFlowStatisticsRequest specificReq = new OFFlowStatisticsRequest();
OFMatch matchReq = new OFMatch();
match.setWildcards(0xffffffff);
specificReq.setMatch(matchReq);
specificReq.setOutPort((short) 7);
specificReq.setTableId((byte) 0xff);
req.setStatistics(Collections.singletonList((OFStatistics)specificReq));
requestLength += specificReq.getLength();
req.setLengthU(requestLength);
Route routeNew = new Route(1L, 3L);
routeNew.setPath(new ArrayList<NodePortTuple>());
/* Route: device1--[P1SW1P8]--[P8SW3P1]--device4 */
routeNew.getPath().add(new NodePortTuple(1L, (short)1));
routeNew.getPath().add(new NodePortTuple(1L, (short)8));
routeNew.getPath().add(new NodePortTuple(3L, (short)8));
routeNew.getPath().add(new NodePortTuple(3L, (short)1));
expect(routingEngine.getRoute(1L, (short)1, 3L, (short)1, routeCookie14, true)).
andReturn(routeNew).atLeastOnce();
// Create a new flow-mod that is expected to be programmed
fm1.getActions().remove(0);
OFActionOutput actionNew = new OFActionOutput((short)8, (short)0xffff);
List<OFAction> actionsNew = new ArrayList<OFAction>();
actionsNew.add(actionNew);
fm1.setActions(actionsNew);
fm1.setCommand(OFFlowMod.OFPFC_MODIFY);
// Record expected flow mods
sw1.write(capture(wc1), capture(bc1));
replay(sw1, sw2, sw3, routingEngine, decision14, topology);
// Now bring the link down between switch SW1 and SW2
flowReconcileMgr.removedLink(sw1.getId(), (short)7,
sw2.getId(), (short)7);
OFStatisticsReply msg = new OFStatisticsReply();
msg.setType(OFType.STATS_REPLY);
List<OFStatistics> statsList = new ArrayList<OFStatistics>();
msg.setStatistics(statsList);
msg.setXid(0);
OFFlowStatisticsReply oneStats = new OFFlowStatisticsReply();
OFMatch matchResp = new OFMatch();
matchResp.setDataLayerSource("00:00:00:00:00:01");
matchResp.setDataLayerDestination("00:00:00:00:00:04");
matchResp.setWildcards(FlowCacheObj.WILD_MATCH_INP_VLAN_DLADRS);
oneStats.setCookie(
AppCookie.makeCookie(Forwarding.FORWARDING_APP_ID, 0));
oneStats.setMatch(matchResp);
statsList.add(oneStats);
int pre_count = flowReconcileMgr.flowReconcileThreadRunCount.get();
Date startTime = new Date();
flowReconcileMgr.receive(sw1, msg, null);
while (flowReconcileMgr.flowReconcileThreadRunCount.get() == pre_count) {
Date curTime = new Date();
assertTrue((curTime.getTime() - startTime.getTime()) < 5000);
}
verify (sw1, sw2, sw3, routingEngine, decision14, topology);
assertEquals(pre_count+1, flowReconcileMgr.switchQueryRespHandlerCallCount.get());
assertEquals(2, betterFlowCacheMgr.getBfcCore().getAddCnt());
assertEquals(1, betterFlowCacheMgr.getBfcCore().getCacheHitCnt());
assertEquals(1, betterFlowCacheMgr.getBfcCore().getActiveCnt());
assertEquals(0, betterFlowCacheMgr.getBfcCore().getInactiveCnt());
m = wc1.getValues().get(1);
assert (m instanceof OFFlowMod);
assertEquals(fm1, m);
}
/* Test flow reconciliation upon NetVirt config change
*
*/
@Test
public void testFlowReconcileUponNetVirtCfgChange() throws Exception {
/* Add a flow to flow cache */
betterFlowCacheMgr.clearFlowCache();
assertEquals(0, betterFlowCacheMgr.getBfcCore().getActiveCnt());
assertEquals(0, betterFlowCacheMgr.getBfcCore().getInactiveCnt());
String netVirtName = "testNetVirt1";
OFMatch ofm = new OFMatch();
ofm.setDataLayerSource(Ethernet.toByteArray(device1.getMACAddress()));
ofm.setDataLayerDestination(
Ethernet.toByteArray(device2.getMACAddress()));
ofm.setWildcards(FlowCacheObj.WILD_MATCH_INP_VLAN_DLADRS_ET);
OFMatchWithSwDpid ofmWithSwDpid = new OFMatchWithSwDpid(ofm, 1L);
/* device1 is connected to sw1 */
betterFlowCacheMgr.addFlow(netVirtName, ofmWithSwDpid, 2L, 1L, (short)1,
(short)0, FlowCacheObj.FCActionPERMIT);
betterFlowCacheMgr.updateFlush();
assertEquals(1, betterFlowCacheMgr.getBfcCore().getActiveCnt());
assertEquals(0, betterFlowCacheMgr.getBfcCore().getInactiveCnt());
int pre_count = flowReconcileMgr.flowReconcileThreadRunCount.get();
Date startTime = new Date();
/* Simulate that there is a config change in "testNetVirt1" causing
* NetVirtManagerImpl to submit a flow cache query.
* NetVirt Manager triggers the flow reconciliation by submitting
* flow query to the flow cache.
*/
FCQueryObj fcQueryObj = new FCQueryObj(
netVirtManager,
netVirtName,
null, // null vlan
null, // null srcDevice
null, // null destDevice
getName(),
FCQueryEvType.APP_CONFIG_CHANGED,
null);
/* Do the replay */
replay(sw1);
betterFlowCacheMgr.submitFlowCacheQuery(fcQueryObj);
while (flowReconcileMgr.flowReconcileThreadRunCount.get() == pre_count) {
Date curTime = new Date();
assertTrue((curTime.getTime() - startTime.getTime()) < 1000);
}
verify(sw1);
/* Now netVirtManager module should get callback with the flow that
* we inserted above.
*/
assertEquals(1, netVirtManager.getFlowQueryRespHandlerCallCount());
assertEquals(1,
netVirtManager.getLastFCQueryResp().qrFlowCacheObjList.size());
String newNetVirtName = "default|DefaultEntityClass-default";
FlowCacheObj fco = betterFlowCacheMgr.
getAllFlowsByApplInstVlanSrcDestDevicesInternal(
newNetVirtName, (short)-1,
device1.getMACAddress(),
device2.getMACAddress());
/* Confirm that the flow cache now has entry under the default netVirt */
assertNotNull(fco);
assertEquals(FlowCacheObj.FCActionPERMIT, fco.fce.getAction());
assertEquals(null, fco.fceList);
assertEquals(1, betterFlowCacheMgr.getBfcCore().getActiveCnt());
assertEquals(0, betterFlowCacheMgr.getBfcCore().getInactiveCnt());
/* **** Now simulate that device1 and device2 are in the same NetVirt */
betterFlowCacheMgr.submitFlowCacheQuery(fcQueryObj);
/* Now virtual routing module should get callback with the flow that
* we inserted above.
*/
startTime = new Date();
while (netVirtManager.getFlowQueryRespHandlerCallCount() == 1) {
Date curTime = new Date();
assertTrue((curTime.getTime() - startTime.getTime()) < 1000);
}
assertEquals(2, netVirtManager.getFlowQueryRespHandlerCallCount());
assertEquals(0,
netVirtManager.getLastFCQueryResp().qrFlowCacheObjList.size());
fco = betterFlowCacheMgr.
getAllFlowsByApplInstVlanSrcDestDevicesInternal(
netVirtName, (short)-1,
device1.getMACAddress(),
device2.getMACAddress());
/* Confirm that the flow cache is now empty as the delete entry would
* be deleted */
assertNull(fco);
assertEquals(1, betterFlowCacheMgr.getBfcCore().getActiveCnt());
assertEquals(0, betterFlowCacheMgr.getBfcCore().getInactiveCnt());
}
}