--- /dev/null
+/**
+ * Copyright (c) 2017 Pantheon Technologies s.r.o. 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.openflowplugin.impl.services;
+
+import org.opendaylight.openflowplugin.api.OFConstants;
+import org.opendaylight.openflowplugin.api.openflow.device.DeviceContext;
+import org.opendaylight.openflowplugin.api.openflow.device.RequestContextStack;
+import org.opendaylight.openflowplugin.api.openflow.device.Xid;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlowInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.RemoveFlowInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.flow.update.OriginalFlow;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.flow.update.UpdatedFlow;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.Flow;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.FlowMessageBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.common.types.rev130731.FlowModCommand;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.OfHeader;
+import org.opendaylight.yangtools.yang.binding.DataContainer;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+
+final class FlowMessageService<O extends DataObject> extends AbstractMessageService<Flow, FlowMessageBuilder, O> {
+ protected FlowMessageService(final RequestContextStack requestContextStack, final DeviceContext deviceContext, final Class<O> clazz) {
+ super(requestContextStack, deviceContext, clazz);
+ }
+
+ @Override
+ protected OfHeader buildRequest(final Xid xid, final Flow input) throws ServiceException {
+ final FlowMessageBuilder flowMessageBuilder = new FlowMessageBuilder(input);
+ final Class<? extends DataContainer> clazz = input.getImplementedInterface();
+
+ if (clazz.equals(AddFlowInput.class)
+ || clazz.equals(UpdatedFlow.class)) {
+ flowMessageBuilder.setCommand(FlowModCommand.OFPFCADD);
+ } else if (clazz.equals(RemoveFlowInput.class)
+ || clazz.equals(OriginalFlow.class)) {
+ flowMessageBuilder.setCommand(Boolean.TRUE.equals(input.isStrict())
+ ? FlowModCommand.OFPFCDELETESTRICT
+ : FlowModCommand.OFPFCDELETE);
+ }
+
+ return flowMessageBuilder
+ .setVersion(getVersion())
+ .setXid(xid.getValue())
+ .build();
+ }
+
+ @Override
+ public boolean isSupported() {
+ return super.isSupported() && getVersion() >= OFConstants.OFP_VERSION_1_3;
+ }
+
+}
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.SettableFuture;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.Future;
import javax.annotation.Nullable;
+import org.opendaylight.openflowplugin.api.OFConstants;
import org.opendaylight.openflowplugin.api.openflow.device.DeviceContext;
import org.opendaylight.openflowplugin.api.openflow.device.RequestContextStack;
import org.opendaylight.openflowplugin.api.openflow.registry.flow.DeviceFlowRegistry;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.FlowModInputBuilder;
import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.RpcError;
import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
private final FlowService<UpdateFlowOutput> flowUpdate;
private final FlowService<AddFlowOutput> flowAdd;
private final FlowService<RemoveFlowOutput> flowRemove;
+ private final FlowMessageService<AddFlowOutput> flowAddMessage;
+ private final FlowMessageService<UpdateFlowOutput> flowUpdateMessage;
+ private final FlowMessageService<RemoveFlowOutput> flowRemoveMessage;
private final DeviceContext deviceContext;
private ItemLifecycleListener itemLifecycleListener;
flowRemove = new FlowService<>(requestContextStack, deviceContext, RemoveFlowOutput.class, convertorExecutor);
flowAdd = new FlowService<>(requestContextStack, deviceContext, AddFlowOutput.class, convertorExecutor);
flowUpdate = new FlowService<>(requestContextStack, deviceContext, UpdateFlowOutput.class, convertorExecutor);
+ flowAddMessage = new FlowMessageService<>(requestContextStack, deviceContext, AddFlowOutput.class);
+ flowUpdateMessage = new FlowMessageService<>(requestContextStack, deviceContext, UpdateFlowOutput.class);
+ flowRemoveMessage= new FlowMessageService<>(requestContextStack, deviceContext, RemoveFlowOutput.class);
}
@Override
@Override
public Future<RpcResult<AddFlowOutput>> addFlow(final AddFlowInput input) {
final FlowRegistryKey flowRegistryKey = FlowRegistryKeyFactory.create(deviceContext.getDeviceInfo().getVersion(), input);
- final ListenableFuture<RpcResult<AddFlowOutput>> future =
- flowAdd.processFlowModInputBuilders(flowAdd.toFlowModInputs(input));
- Futures.addCallback(future, new AddFlowCallback(input, flowRegistryKey));
+ final ListenableFuture<RpcResult<AddFlowOutput>> future;
+
+ if (flowAddMessage.isSupported()) {
+ future = flowAddMessage.handleServiceCall(input);
+ Futures.addCallback(future, new AddFlowCallback(input, flowRegistryKey));
+ } else {
+ future = flowAdd.processFlowModInputBuilders(flowAdd.toFlowModInputs(input));
+ Futures.addCallback(future, new AddFlowCallback(input, flowRegistryKey));
+
+ }
return future;
}
@Override
public Future<RpcResult<RemoveFlowOutput>> removeFlow(final RemoveFlowInput input) {
- final ListenableFuture<RpcResult<RemoveFlowOutput>> future =
- flowRemove.processFlowModInputBuilders(flowRemove.toFlowModInputs(input));
- Futures.addCallback(future, new RemoveFlowCallback(input));
+ final ListenableFuture<RpcResult<RemoveFlowOutput>> future;
+
+ if (flowRemoveMessage.isSupported()) {
+ future = flowRemoveMessage.handleServiceCall(input);
+ Futures.addCallback(future, new RemoveFlowCallback(input));
+
+ } else {
+ future = flowRemove.processFlowModInputBuilders(flowRemove.toFlowModInputs(input));
+ Futures.addCallback(future, new RemoveFlowCallback(input));
+ }
+
return future;
}
final List<FlowModInputBuilder> allFlowMods = new ArrayList<>();
final List<FlowModInputBuilder> ofFlowModInputs;
- if (!FlowCreatorUtil.canModifyFlow(original, updated, flowUpdate.getVersion())) {
- // We would need to remove original and add updated.
+ ListenableFuture<RpcResult<UpdateFlowOutput>> future;
+ if (flowUpdateMessage.isSupported()) {
+
+ if (!FlowCreatorUtil.canModifyFlow(original, updated, flowUpdateMessage.getVersion())) {
+ final SettableFuture<RpcResult<UpdateFlowOutput>> objectSettableFuture = SettableFuture.create();
+
+ final ListenableFuture<List<RpcResult<UpdateFlowOutput>>> listListenableFuture = Futures.successfulAsList(
+ flowUpdateMessage.handleServiceCall(input.getOriginalFlow()),
+ flowUpdateMessage.handleServiceCall(input.getUpdatedFlow()));
+
+ Futures.addCallback(listListenableFuture, new FutureCallback<List<RpcResult<UpdateFlowOutput>>>() {
+ @Override
+ public void onSuccess(final List<RpcResult<UpdateFlowOutput>> results) {
+ final ArrayList<RpcError> errors = new ArrayList();
+ for (RpcResult<UpdateFlowOutput> flowModResult : results) {
+ if (flowModResult == null) {
+ errors.add(RpcResultBuilder.newError(
+ RpcError.ErrorType.PROTOCOL, OFConstants.APPLICATION_TAG,
+ "unexpected flowMod result (null) occurred"));
+ } else if (!flowModResult.isSuccessful()) {
+ errors.addAll(flowModResult.getErrors());
+ }
+ }
+
+ final RpcResultBuilder<UpdateFlowOutput> rpcResultBuilder;
+ if (errors.isEmpty()) {
+ rpcResultBuilder = RpcResultBuilder.success();
+ } else {
+ rpcResultBuilder = RpcResultBuilder.<UpdateFlowOutput>failed().withRpcErrors(errors);
+ }
+
+ objectSettableFuture.set(rpcResultBuilder.build());
+ }
+
+ @Override
+ public void onFailure(final Throwable t) {
+ RpcResultBuilder<UpdateFlowOutput> rpcResultBuilder = RpcResultBuilder.failed();
+ objectSettableFuture.set(rpcResultBuilder.build());
+ }
+ });
- // remove flow
- final RemoveFlowInputBuilder removeflow = new RemoveFlowInputBuilder(original);
- final List<FlowModInputBuilder> ofFlowRemoveInput = flowUpdate.toFlowModInputs(removeflow.build());
- // remove flow should be the first
- allFlowMods.addAll(ofFlowRemoveInput);
- final AddFlowInputBuilder addFlowInputBuilder = new AddFlowInputBuilder(updated);
- ofFlowModInputs = flowUpdate.toFlowModInputs(addFlowInputBuilder.build());
+ future = objectSettableFuture;
+ } else {
+ future = flowUpdateMessage.handleServiceCall(input.getUpdatedFlow());
+ }
} else {
- ofFlowModInputs = flowUpdate.toFlowModInputs(updated);
+ if (!FlowCreatorUtil.canModifyFlow(original, updated, flowUpdate.getVersion())) {
+ // We would need to remove original and add updated.
+
+ // remove flow
+ final RemoveFlowInputBuilder removeflow = new RemoveFlowInputBuilder(original);
+ final List<FlowModInputBuilder> ofFlowRemoveInput = flowUpdate.toFlowModInputs(removeflow.build());
+ // remove flow should be the first
+ allFlowMods.addAll(ofFlowRemoveInput);
+ final AddFlowInputBuilder addFlowInputBuilder = new AddFlowInputBuilder(updated);
+ ofFlowModInputs = flowUpdate.toFlowModInputs(addFlowInputBuilder.build());
+ } else {
+ ofFlowModInputs = flowUpdate.toFlowModInputs(updated);
+ }
+
+ allFlowMods.addAll(ofFlowModInputs);
+
+ future = flowUpdate.processFlowModInputBuilders(allFlowMods);
}
- allFlowMods.addAll(ofFlowModInputs);
- ListenableFuture<RpcResult<UpdateFlowOutput>> future = flowUpdate.processFlowModInputBuilders(allFlowMods);
Futures.addCallback(future, new UpdateFlowCallback(input));
return future;
}
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.when;
import com.google.common.util.concurrent.Futures;
public class SalFlowServiceImplTest extends TestCase {
private static final BigInteger DUMMY_DATAPATH_ID = new BigInteger("444");
- private static final Short DUMMY_VERSION = OFConstants.OFP_VERSION_1_3;
private static final String DUMMY_NODE_ID = "dummyNodeID";
private static final String DUMMY_FLOW_ID = "dummyFlowID";
private static final Short DUMMY_TABLE_ID = (short) 0;
private OutboundQueue outboundQueue;
@Mock
private Match match;
- private SalFlowServiceImpl salFlowService;
@Mock
private DeviceState mockedDeviceState;
@Before
public void initialization() {
when(mockedFeatures.getDatapathId()).thenReturn(DUMMY_DATAPATH_ID);
- when(mockedFeatures.getVersion()).thenReturn(DUMMY_VERSION);
when(mockedFeaturesOutput.getDatapathId()).thenReturn(DUMMY_DATAPATH_ID);
- when(mockedFeaturesOutput.getVersion()).thenReturn(DUMMY_VERSION);
when(mockedPrimConnectionContext.getFeatures()).thenReturn(mockedFeatures);
when(mockedPrimConnectionContext.getConnectionAdapter()).thenReturn(mockedConnectionAdapter);
when(mockedPrimConnectionContext.getOutboundQueueProvider()).thenReturn(outboundQueue);
when(mockedDeviceContext.getPrimaryConnectionContext()).thenReturn(mockedPrimConnectionContext);
-
when(mockedDeviceContext.getMessageSpy()).thenReturn(mockedMessagSpy);
when(mockedDeviceContext.getDeviceFlowRegistry()).thenReturn(deviceFlowRegistry);
- when(mockedRequestContextStack.createRequestContext()).thenReturn(requestContext);
when(requestContext.getXid()).thenReturn(new Xid(84L));
when(requestContext.getFuture()).thenReturn(RpcResultBuilder.success().buildFuture());
+ when(mockedRequestContextStack.createRequestContext()).thenReturn(requestContext);
when(mockedDeviceInfo.getNodeInstanceIdentifier()).thenReturn(NODE_II);
when(mockedDeviceInfo.getDatapathId()).thenReturn(DUMMY_DATAPATH_ID);
- when(mockedDeviceInfo.getVersion()).thenReturn(DUMMY_VERSION);
+
when(mockedDeviceContext.getDeviceState()).thenReturn(mockedDeviceState);
when(mockedDeviceContext.getDeviceInfo()).thenReturn(mockedDeviceInfo);
+ }
+
+ private SalFlowServiceImpl mockSalFlowService(final short version) {
+ when(mockedFeatures.getVersion()).thenReturn(version);
+ when(mockedFeaturesOutput.getVersion()).thenReturn(version);
+ when(mockedDeviceInfo.getVersion()).thenReturn(version);
+
+ if (OFConstants.OFP_VERSION_1_3 >= version) {
+ when(mockedDeviceContext.isUseSingleLayerSerialization()).thenReturn(true);
+ }
final ConvertorManager convertorManager = ConvertorManagerFactory.createDefaultManager();
- salFlowService = new SalFlowServiceImpl(mockedRequestContextStack, mockedDeviceContext, convertorManager);
+ return new SalFlowServiceImpl(mockedRequestContextStack, mockedDeviceContext, convertorManager);
}
@Test
public void testAddFlow() throws Exception {
- addFlow(null);
+ addFlow(null, OFConstants.OFP_VERSION_1_0);
+ addFlow(null, OFConstants.OFP_VERSION_1_3);
}
@Test
public void testAddFlowFailCallback() throws Exception {
+ addFlowFailCallback(OFConstants.OFP_VERSION_1_0);
+ }
+
+ @Test
+ public void testAddFlowFailCallback1() throws Exception {
+ addFlowFailCallback(OFConstants.OFP_VERSION_1_3);
+ }
+
+ private void addFlowFailCallback(short version) throws InterruptedException, ExecutionException {
AddFlowInput mockedAddFlowInput = new AddFlowInputBuilder()
.setMatch(match)
.setTableId((short)1)
.when(requestContext).getFuture();
mockingFlowRegistryLookup();
- final Future<RpcResult<AddFlowOutput>> rpcResultFuture = salFlowService.addFlow(mockedAddFlowInput);
+ final Future<RpcResult<AddFlowOutput>> rpcResultFuture = mockSalFlowService(version).addFlow(mockedAddFlowInput);
assertNotNull(rpcResultFuture);
final RpcResult<?> addFlowOutputRpcResult = rpcResultFuture.get();
@Test
public void testRemoveFlowFailCallback() throws Exception {
+ removeFlowFailCallback(OFConstants.OFP_VERSION_1_0);
+ }
+
+ @Test
+ public void testRemoveFlowFailCallback1() throws Exception {
+ removeFlowFailCallback(OFConstants.OFP_VERSION_1_3);
+ }
+
+ private void removeFlowFailCallback(short version) throws InterruptedException, ExecutionException {
RemoveFlowInput mockedRemoveFlowInput = new RemoveFlowInputBuilder()
.setMatch(match)
.build();
Mockito.doReturn(Futures.<RequestContext<Object>>immediateFailedFuture(new Exception("ut-failed-response")))
.when(requestContext).getFuture();
- final Future<RpcResult<RemoveFlowOutput>> rpcResultFuture = salFlowService.removeFlow(mockedRemoveFlowInput);
+ final Future<RpcResult<RemoveFlowOutput>> rpcResultFuture = mockSalFlowService(version).removeFlow(mockedRemoveFlowInput);
assertNotNull(rpcResultFuture);
final RpcResult<?> removeFlowOutputRpcResult = rpcResultFuture.get();
@Test
public void testAddFlowWithItemLifecycle() throws Exception {
- addFlow(mock(ItemLifecycleListener.class));
+ addFlow(mock(ItemLifecycleListener.class), OFConstants.OFP_VERSION_1_0);
+ addFlow(mock(ItemLifecycleListener.class), OFConstants.OFP_VERSION_1_3);
}
- private void addFlow(final ItemLifecycleListener itemLifecycleListener) throws ExecutionException, InterruptedException {
+ private void addFlow(final ItemLifecycleListener itemLifecycleListener, short version) throws ExecutionException, InterruptedException {
AddFlowInput mockedAddFlowInput = new AddFlowInputBuilder()
.setMatch(match)
.setTableId((short)1)
.build();
-
+ SalFlowServiceImpl salFlowService = mockSalFlowService(version);
salFlowService.setItemLifecycleListener(itemLifecycleListener);
mockingFlowRegistryLookup();
@Test
public void testRemoveFlow() throws Exception {
- removeFlow(null);
+ removeFlow(null, OFConstants.OFP_VERSION_1_0);
+ removeFlow(null, OFConstants.OFP_VERSION_1_3);
}
@Test
public void testRemoveFlowWithItemLifecycle() throws Exception {
- removeFlow(mock(ItemLifecycleListener.class));
+ removeFlow(mock(ItemLifecycleListener.class), OFConstants.OFP_VERSION_1_0);
+ removeFlow(mock(ItemLifecycleListener.class), OFConstants.OFP_VERSION_1_3);
}
- private void removeFlow(final ItemLifecycleListener itemLifecycleListener) throws Exception {
+ private void removeFlow(final ItemLifecycleListener itemLifecycleListener, short version) throws Exception {
RemoveFlowInput mockedRemoveFlowInput = new RemoveFlowInputBuilder()
.setMatch(match)
.setTableId((short)1)
.build();
+ SalFlowServiceImpl salFlowService = mockSalFlowService(version);
if (itemLifecycleListener != null) {
salFlowService.setItemLifecycleListener(itemLifecycleListener);
mockingFlowRegistryLookup();
@Test
public void testUpdateFlow() throws Exception {
- updateFlow(null);
+ updateFlow(null, OFConstants.OFP_VERSION_1_0);
+ updateFlow(null, OFConstants.OFP_VERSION_1_3);
}
@Test
public void testUpdateFlowWithItemLifecycle() throws Exception {
- updateFlow(mock(ItemLifecycleListener.class));
+ updateFlow(mock(ItemLifecycleListener.class), OFConstants.OFP_VERSION_1_0);
+ updateFlow(mock(ItemLifecycleListener.class), OFConstants.OFP_VERSION_1_3);
}
- private void updateFlow(final ItemLifecycleListener itemLifecycleListener) throws Exception {
+ private void updateFlow(final ItemLifecycleListener itemLifecycleListener, short version) throws Exception {
UpdateFlowInput mockedUpdateFlowInput = mock(UpdateFlowInput.class);
+ UpdateFlowInput mockedUpdateFlowInput1 = mock(UpdateFlowInput.class);
UpdatedFlow mockedUpdateFlow = new UpdatedFlowBuilder()
.setMatch(match)
.setTableId((short)1)
.build();
+ UpdatedFlow mockedUpdateFlow1 = new UpdatedFlowBuilder()
+ .setMatch(match)
+ .setTableId((short)1)
+ .setPriority(Integer.valueOf(1))
+ .build();
+
when(mockedUpdateFlowInput.getUpdatedFlow()).thenReturn(mockedUpdateFlow);
+ when(mockedUpdateFlowInput1.getUpdatedFlow()).thenReturn(mockedUpdateFlow1);
FlowRef mockedFlowRef = mock(FlowRef.class);
Mockito.doReturn(TABLE_II.child(Flow.class, new FlowKey(new FlowId(DUMMY_FLOW_ID)))).when(mockedFlowRef).getValue();
when(mockedUpdateFlowInput.getFlowRef()).thenReturn(mockedFlowRef);
+ when(mockedUpdateFlowInput1.getFlowRef()).thenReturn(mockedFlowRef);
OriginalFlow mockedOriginalFlow = new OriginalFlowBuilder()
.setMatch(match)
.setTableId((short)1)
.build();
+ OriginalFlow mockedOriginalFlow1 = new OriginalFlowBuilder()
+ .setMatch(match)
+ .setTableId((short)1)
+ .setPriority(Integer.valueOf(2))
+ .build();
+
when(mockedUpdateFlowInput.getOriginalFlow()).thenReturn(mockedOriginalFlow);
+ when(mockedUpdateFlowInput1.getOriginalFlow()).thenReturn(mockedOriginalFlow1);
+ SalFlowServiceImpl salFlowService = mockSalFlowService(version);
if (itemLifecycleListener != null) {
salFlowService.setItemLifecycleListener(itemLifecycleListener);
mockingFlowRegistryLookup();
}
verifyOutput(salFlowService.updateFlow(mockedUpdateFlowInput));
+ verifyOutput(salFlowService.updateFlow(mockedUpdateFlowInput1));
if (itemLifecycleListener != null) {
- Mockito.verify(itemLifecycleListener).onUpdated(Matchers.<KeyedInstanceIdentifier<Flow, FlowKey>>any(), Matchers.<Flow>any());
+ Mockito.verify(itemLifecycleListener, times(2)).onUpdated(Matchers.<KeyedInstanceIdentifier<Flow, FlowKey>>any(), Matchers.<Flow>any());
}
}