/*
* Copyright (c) 2014, 2017 Ericsson Inc. and others. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/epl-v10.html
*/
package org.opendaylight.sfc.ofrenderer;
import static org.junit.Assert.assertEquals;
import static org.mockito.Matchers.anyLong;
import static org.mockito.Matchers.anyObject;
import static org.mockito.Matchers.anyShort;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import java.math.BigInteger;
import org.junit.Test;
import org.opendaylight.sfc.ofrenderer.openflow.SfcIpv4PacketInHandler;
import org.opendaylight.sfc.ofrenderer.openflow.SfcOfFlowProgrammerImpl;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.FlowCookie;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRef;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.MetadataBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketReceived;
import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.packet.received.MatchBuilder;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
public class SfcIpv4PacketInHandlerTest {
SfcIpv4PacketInHandler pktInHandler;
SfcOfFlowProgrammerImpl flowProgrammerMock;
public SfcIpv4PacketInHandlerTest() {
this.flowProgrammerMock = mock(SfcOfFlowProgrammerImpl.class);
when(this.flowProgrammerMock.compareClassificationTableCookie((FlowCookie) anyObject())).thenReturn(true);
this.pktInHandler = new SfcIpv4PacketInHandler(this.flowProgrammerMock);
}
@Test
public void nullInvocation() {
this.pktInHandler.onPacketReceived(null);
verifyNoMoreInteractions(this.flowProgrammerMock);
}
@Test
public void pktInFlowsCreated() throws Exception {
this.pktInHandler.onPacketReceived(createPacket());
this.pktInHandler.close();
verify(this.flowProgrammerMock, times(2)).setFlowRspId(anyLong());
verify(this.flowProgrammerMock, times(2)).configurePathMapperAclFlow(anyString(), anyString(), anyString(),
anyShort());
verify(this.flowProgrammerMock, times(1)).compareClassificationTableCookie((FlowCookie) anyObject());
verifyNoMoreInteractions(this.flowProgrammerMock);
}
@Test
public void pktInBuffering() {
this.pktInHandler.setMaxBufferTime(10000); // 10 seconds
this.pktInHandler.setPacketCountPurge(1000);
PacketReceived pkt = createPacket();
this.pktInHandler.onPacketReceived(pkt);
verify(this.flowProgrammerMock, times(2)).setFlowRspId(anyLong());
verify(this.flowProgrammerMock, times(2)).configurePathMapperAclFlow(anyString(), anyString(), anyString(),
anyShort());
verify(this.flowProgrammerMock, times(1)).compareClassificationTableCookie((FlowCookie) anyObject());
verifyNoMoreInteractions(this.flowProgrammerMock);
// When called again, nothing should be called on
// the FlowProgrammer since the pkt is buffered
resetFlowProgrammerMock();
this.pktInHandler.onPacketReceived(pkt);
verify(this.flowProgrammerMock, times(1)).compareClassificationTableCookie((FlowCookie) anyObject());
verifyNoMoreInteractions(this.flowProgrammerMock);
}
@Test
public void pktInBufferingTimeout() throws InterruptedException {
this.pktInHandler.setMaxBufferTime(1); // 1 millisecond
this.pktInHandler.setPacketCountPurge(1000);
PacketReceived pkt = createPacket();
this.pktInHandler.onPacketReceived(pkt);
verify(this.flowProgrammerMock, times(2)).setFlowRspId(anyLong());
verify(this.flowProgrammerMock, times(2)).configurePathMapperAclFlow(anyString(), anyString(), anyString(),
anyShort());
verify(this.flowProgrammerMock, times(1)).compareClassificationTableCookie((FlowCookie) anyObject());
verifyNoMoreInteractions(this.flowProgrammerMock);
// When called again, the packet should be sent to the FlowProgrammer
// again, since the timeout is 1 millisecond
Thread.sleep(10); // sleep 10 milliseconds, to let the buffer time
// expire
resetFlowProgrammerMock();
this.pktInHandler.onPacketReceived(pkt);
verify(this.flowProgrammerMock, times(2)).setFlowRspId(anyLong());
verify(this.flowProgrammerMock, times(2)).configurePathMapperAclFlow(anyString(), anyString(), anyString(),
anyShort());
verify(this.flowProgrammerMock, times(1)).compareClassificationTableCookie((FlowCookie) anyObject());
verifyNoMoreInteractions(this.flowProgrammerMock);
}
@Test
public void pktInPurgeBuffering() throws InterruptedException {
this.pktInHandler.setMaxBufferTime(1); // 1 millisecond
this.pktInHandler.setPacketCountPurge(2);
PacketReceived pkt = createPacket();
assertEquals(this.pktInHandler.getBufferSize(), 0);
this.pktInHandler.onPacketReceived(pkt);
verify(this.flowProgrammerMock, times(2)).setFlowRspId(anyLong());
verify(this.flowProgrammerMock, times(2)).configurePathMapperAclFlow(anyString(), anyString(), anyString(),
anyShort());
verify(this.flowProgrammerMock, times(1)).compareClassificationTableCookie((FlowCookie) anyObject());
verifyNoMoreInteractions(this.flowProgrammerMock);
assertEquals(this.pktInHandler.getBufferSize(), 1);
// When called again, the purgeCount will be exceeded, the buffer will
// be flushed, and this packet will be added back, making a size of 1
Thread.sleep(10); // sleep 10 milliseconds, to let the buffer time
// expire
resetFlowProgrammerMock();
this.pktInHandler.onPacketReceived(pkt);
verify(this.flowProgrammerMock, times(2)).setFlowRspId(anyLong());
verify(this.flowProgrammerMock, times(2)).configurePathMapperAclFlow(anyString(), anyString(), anyString(),
anyShort());
verify(this.flowProgrammerMock, times(1)).compareClassificationTableCookie((FlowCookie) anyObject());
verifyNoMoreInteractions(this.flowProgrammerMock);
assertEquals(this.pktInHandler.getBufferSize(), 1);
}
// When we want to reset the method call counters,
// it also resets the stubs, so do both together
private void resetFlowProgrammerMock() {
reset(this.flowProgrammerMock);
when(this.flowProgrammerMock.compareClassificationTableCookie((FlowCookie) anyObject())).thenReturn(true);
}
private PacketReceived createPacket() {
PacketReceived pktMock = mock(PacketReceived.class);
// Stub the PacketReceived methods
// getPayload()
// We dont need a real payload, just the etherType, IpSrc, and IpDst
// MacSrc=a1a1a1a1a1a1, MacDst=b2b2b2b2b2b2, etherType=0800
// IpHdrStuff=000000000000000000000000, IpSrc=0a0a0001, IpDst=0b0b0001
byte[] payload = hexStringToByteArray(
"a1a1a1a1a1a1b2b2b2b2b2b20800" + "0000000000000000000000000" + "a0a00010b0b0001");
when(pktMock.getPayload()).thenReturn(payload);
// getMatch(), getMatch().getMetadata()
MetadataBuilder metadataBuilder = new MetadataBuilder();
metadataBuilder.setMetadata(new BigInteger("100"));
MatchBuilder matchBuilder = new MatchBuilder();
matchBuilder.setMetadata(metadataBuilder.build());
when(pktMock.getMatch()).thenReturn(matchBuilder.build());
// getIngress()
// .getValue()
// .firstKeyOf(Node.class, NodeKey.class)
// .getId().getValue();
InstanceIdentifier<Node> nodeId = InstanceIdentifier.builder(Nodes.class)
.child(Node.class, new NodeKey(new NodeId("openflow:1"))).build();
NodeConnectorRef nodeConnRef = new NodeConnectorRef(nodeId);
when(pktMock.getIngress()).thenReturn(nodeConnRef);
return pktMock;
}
public static byte[] hexStringToByteArray(String string) {
int len = string.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
data[i / 2] = (byte) ((Character.digit(string.charAt(i), 16) << 4)
+ Character.digit(string.charAt(i + 1), 16));
}
return data;
}
}