Decompose RPC implementation classes
[openflowplugin.git] / openflowplugin-impl / src / main / java / org / opendaylight / openflowplugin / impl / services / sal / UpdateFlowImpl.java
1 /*
2  * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
3  * Copyright (c) 2024 PANTHEON.tech, s.r.o.
4  *
5  * This program and the accompanying materials are made available under the
6  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
7  * and is available at http://www.eclipse.org/legal/epl-v10.html
8  */
9 package org.opendaylight.openflowplugin.impl.services.sal;
10
11 import com.google.common.util.concurrent.FutureCallback;
12 import com.google.common.util.concurrent.Futures;
13 import com.google.common.util.concurrent.ListenableFuture;
14 import com.google.common.util.concurrent.MoreExecutors;
15 import com.google.common.util.concurrent.SettableFuture;
16 import java.util.ArrayList;
17 import java.util.List;
18 import org.opendaylight.openflowplugin.api.OFConstants;
19 import org.opendaylight.openflowplugin.api.openflow.FlowGroupStatus;
20 import org.opendaylight.openflowplugin.api.openflow.device.DeviceContext;
21 import org.opendaylight.openflowplugin.api.openflow.device.RequestContextStack;
22 import org.opendaylight.openflowplugin.api.openflow.registry.flow.DeviceFlowRegistry;
23 import org.opendaylight.openflowplugin.api.openflow.registry.flow.FlowDescriptor;
24 import org.opendaylight.openflowplugin.api.openflow.registry.flow.FlowRegistryKey;
25 import org.opendaylight.openflowplugin.impl.registry.flow.FlowDescriptorFactory;
26 import org.opendaylight.openflowplugin.impl.registry.flow.FlowRegistryKeyFactory;
27 import org.opendaylight.openflowplugin.impl.util.FlowCreatorUtil;
28 import org.opendaylight.openflowplugin.openflow.md.core.sal.convertor.ConvertorExecutor;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlowInputBuilder;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.RemoveFlowInputBuilder;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.UpdateFlow;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.UpdateFlowInput;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.UpdateFlowOutput;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.flow.update.OriginalFlow;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.flow.update.UpdatedFlow;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.FlowRef;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.FlowModInputBuilder;
40 import org.opendaylight.yangtools.yang.common.ErrorType;
41 import org.opendaylight.yangtools.yang.common.RpcError;
42 import org.opendaylight.yangtools.yang.common.RpcResult;
43 import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
44 import org.opendaylight.yangtools.yang.common.Uint8;
45 import org.slf4j.Logger;
46 import org.slf4j.LoggerFactory;
47
48 public final class UpdateFlowImpl extends AbstractFlowRpc<UpdateFlowOutput> implements UpdateFlow {
49     private static final Logger LOG = LoggerFactory.getLogger(UpdateFlowImpl.class);
50
51     public UpdateFlowImpl(final RequestContextStack requestContextStack, final DeviceContext deviceContext,
52             final ConvertorExecutor convertorExecutor) {
53         super(requestContextStack, deviceContext, convertorExecutor, UpdateFlowOutput.class);
54     }
55
56     @Override
57     public ListenableFuture<RpcResult<UpdateFlowOutput>> invoke(final UpdateFlowInput input) {
58         final var updated = input.getUpdatedFlow();
59         final var original = input.getOriginalFlow();
60         final var allFlowMods = new ArrayList<FlowModInputBuilder>();
61         final List<FlowModInputBuilder> ofFlowModInputs;
62
63         ListenableFuture<RpcResult<UpdateFlowOutput>> future;
64         if (single.canUseSingleLayerSerialization()) {
65
66             if (!FlowCreatorUtil.canModifyFlow(original, updated, single.getVersion())) {
67                 final var objectSettableFuture = SettableFuture.<RpcResult<UpdateFlowOutput>>create();
68                 final var listListenableFuture = Futures.successfulAsList(
69                     single.handleServiceCall(input.getOriginalFlow()),
70                     single.handleServiceCall(input.getUpdatedFlow()));
71
72                 Futures.addCallback(listListenableFuture, new FutureCallback<>() {
73                     @Override
74                     public void onSuccess(final List<RpcResult<UpdateFlowOutput>> results) {
75                         final var errors = new ArrayList<RpcError>();
76                         for (var flowModResult : results) {
77                             if (flowModResult == null) {
78                                 errors.add(RpcResultBuilder.newError(
79                                         ErrorType.PROTOCOL, OFConstants.APPLICATION_TAG,
80                                         "unexpected flowMod result (null) occurred"));
81                             } else if (!flowModResult.isSuccessful()) {
82                                 errors.addAll(flowModResult.getErrors());
83                             }
84                         }
85
86                         final var rpcResultBuilder = errors.isEmpty() ? RpcResultBuilder.<UpdateFlowOutput>success()
87                             : RpcResultBuilder.<UpdateFlowOutput>failed().withRpcErrors(errors);
88
89                         objectSettableFuture.set(rpcResultBuilder.build());
90                     }
91
92                     @Override
93                     public void onFailure(final Throwable throwable) {
94                         objectSettableFuture.set(RpcResultBuilder.<UpdateFlowOutput>failed().build());
95                     }
96                 }, MoreExecutors.directExecutor());
97
98                 future = objectSettableFuture;
99             } else {
100                 future = single.handleServiceCall(input.getUpdatedFlow());
101             }
102         } else {
103             if (!FlowCreatorUtil.canModifyFlow(original, updated, multi.getVersion())) {
104                 // We would need to remove original and add updated.
105
106                 // remove flow
107                 final RemoveFlowInputBuilder removeflow = new RemoveFlowInputBuilder(original);
108                 final List<FlowModInputBuilder> ofFlowRemoveInput = multi.toFlowModInputs(removeflow.build());
109                 // remove flow should be the first
110                 allFlowMods.addAll(ofFlowRemoveInput);
111                 final AddFlowInputBuilder addFlowInputBuilder = new AddFlowInputBuilder(updated);
112                 ofFlowModInputs = multi.toFlowModInputs(addFlowInputBuilder.build());
113             } else {
114                 ofFlowModInputs = multi.toFlowModInputs(updated);
115             }
116
117             allFlowMods.addAll(ofFlowModInputs);
118
119             future = multi.processFlowModInputBuilders(allFlowMods);
120         }
121
122         Futures.addCallback(future, new UpdateFlowCallback(input), MoreExecutors.directExecutor());
123         return future;
124     }
125
126
127     private final class UpdateFlowCallback implements FutureCallback<RpcResult<UpdateFlowOutput>> {
128         private final UpdateFlowInput input;
129
130         private UpdateFlowCallback(final UpdateFlowInput input) {
131             this.input = input;
132         }
133
134         @Override
135         public void onSuccess(final RpcResult<UpdateFlowOutput> updateFlowOutputRpcResult) {
136             final DeviceFlowRegistry deviceFlowRegistry = deviceContext.getDeviceFlowRegistry();
137             final UpdatedFlow updated = input.getUpdatedFlow();
138             final OriginalFlow original = input.getOriginalFlow();
139             final FlowRegistryKey origFlowRegistryKey =
140                     FlowRegistryKeyFactory.create(deviceContext.getDeviceInfo().getVersion(), original);
141             final FlowRegistryKey updatedFlowRegistryKey =
142                     FlowRegistryKeyFactory.create(deviceContext.getDeviceInfo().getVersion(), updated);
143             final FlowDescriptor origFlowDescriptor = deviceFlowRegistry.retrieveDescriptor(origFlowRegistryKey);
144
145             final boolean isUpdate = origFlowDescriptor != null;
146             final FlowDescriptor updatedFlowDescriptor;
147             final FlowRef flowRef = input.getFlowRef();
148             if (flowRef != null) {
149                 final Uint8 tableId = updated.getTableId();
150                 final FlowId flowId = flowRef.getValue().firstKeyOf(Flow.class).getId();
151                 // FIXME: this does not look right, we probably want better integration
152                 deviceFlowRegistry.appendHistoryFlow(flowId, tableId, FlowGroupStatus.MODIFIED);
153
154                 updatedFlowDescriptor = FlowDescriptorFactory.create(tableId, flowId);
155             } else if (isUpdate) {
156                 updatedFlowDescriptor = origFlowDescriptor;
157             } else {
158                 deviceFlowRegistry.store(updatedFlowRegistryKey);
159                 updatedFlowDescriptor = deviceFlowRegistry.retrieveDescriptor(updatedFlowRegistryKey);
160             }
161
162             if (isUpdate) {
163                 deviceFlowRegistry.addMark(origFlowRegistryKey);
164                 deviceFlowRegistry.storeDescriptor(updatedFlowRegistryKey, updatedFlowDescriptor);
165             }
166         }
167
168         @Override
169         public void onFailure(final Throwable throwable) {
170             LOG.warn("Service call for updating flow={} failed", input, throwable);
171         }
172     }
173 }