Merge "Switch to using lfdocs-conf to pull in theme"
[openflowplugin.git] / applications / forwardingrules-manager / src / main / java / org / opendaylight / openflowplugin / applications / frm / impl / FlowForwarder.java
1 /**
2  * Copyright (c) 2014, 2017 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.openflowplugin.applications.frm.impl;
9
10 import com.google.common.base.Preconditions;
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.concurrent.Future;
17 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
18 import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;
19 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
20 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
21 import org.opendaylight.infrautils.utils.concurrent.JdkFutures;
22 import org.opendaylight.openflowplugin.applications.frm.ForwardingRulesManager;
23 import org.opendaylight.openflowplugin.common.wait.SimpleTaskRetryLooper;
24 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Uri;
25 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableKey;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.StaleFlow;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.StaleFlowBuilder;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.StaleFlowKey;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlowInputBuilder;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlowOutput;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.FlowTableRef;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.RemoveFlowInputBuilder;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.RemoveFlowOutput;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.UpdateFlowInputBuilder;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.UpdateFlowOutput;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.flow.update.OriginalFlowBuilder;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.flow.update.UpdatedFlowBuilder;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.FlowRef;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
46 import org.opendaylight.yangtools.concepts.ListenerRegistration;
47 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
48 import org.opendaylight.yangtools.yang.common.RpcResult;
49 import org.slf4j.Logger;
50 import org.slf4j.LoggerFactory;
51
52 /**
53  * FlowForwarder It implements
54  * {@link org.opendaylight.controller.md.sal.binding.api.DataTreeChangeListener}
55  * for WildCardedPath to {@link Flow} and ForwardingRulesCommiter interface for
56  * methods: add, update and remove {@link Flow} processing for
57  * {@link org.opendaylight.controller.md.sal.binding.api.DataTreeModification}.
58  */
59 public class FlowForwarder extends AbstractListeningCommiter<Flow> {
60
61     private static final Logger LOG = LoggerFactory.getLogger(FlowForwarder.class);
62     private final DataBroker dataBroker;
63     private ListenerRegistration<FlowForwarder> listenerRegistration;
64
65     public FlowForwarder(final ForwardingRulesManager manager, final DataBroker db) {
66         super(manager);
67         dataBroker = Preconditions.checkNotNull(db, "DataBroker can not be null!");
68         registrationListener(db);
69     }
70
71     @SuppressWarnings("IllegalCatch")
72     private void registrationListener(final DataBroker db) {
73         final DataTreeIdentifier<Flow> treeId = new DataTreeIdentifier<>(LogicalDatastoreType.CONFIGURATION,
74                 getWildCardPath());
75         try {
76             SimpleTaskRetryLooper looper = new SimpleTaskRetryLooper(ForwardingRulesManagerImpl.STARTUP_LOOP_TICK,
77                     ForwardingRulesManagerImpl.STARTUP_LOOP_MAX_RETRIES);
78             listenerRegistration = looper
79                     .loopUntilNoException(() -> db.registerDataTreeChangeListener(treeId, FlowForwarder.this));
80         } catch (final Exception e) {
81             LOG.warn("FRM Flow DataTreeChange listener registration fail!");
82             LOG.debug("FRM Flow DataTreeChange listener registration fail ..", e);
83             throw new IllegalStateException("FlowForwarder startup fail! System needs restart.", e);
84         }
85     }
86
87     @Override
88     public void close() {
89         if (listenerRegistration != null) {
90             listenerRegistration.close();
91             listenerRegistration = null;
92         }
93     }
94
95     @Override
96     public void remove(final InstanceIdentifier<Flow> identifier, final Flow removeDataObj,
97             final InstanceIdentifier<FlowCapableNode> nodeIdent) {
98
99         final TableKey tableKey = identifier.firstKeyOf(Table.class, TableKey.class);
100         if (tableIdValidationPrecondition(tableKey, removeDataObj)) {
101             final RemoveFlowInputBuilder builder = new RemoveFlowInputBuilder(removeDataObj);
102             builder.setFlowRef(new FlowRef(identifier));
103             builder.setNode(new NodeRef(nodeIdent.firstIdentifierOf(Node.class)));
104             builder.setFlowTable(new FlowTableRef(nodeIdent.child(Table.class, tableKey)));
105
106             // This method is called only when a given flow object has been
107             // removed from datastore. So FRM always needs to set strict flag
108             // into remove-flow input so that only a flow entry associated with
109             // a given flow object is removed.
110             builder.setTransactionUri(new Uri(provider.getNewTransactionId())).setStrict(Boolean.TRUE);
111             final Future<RpcResult<RemoveFlowOutput>> resultFuture =
112                     provider.getSalFlowService().removeFlow(builder.build());
113             JdkFutures.addErrorLogging(resultFuture, LOG, "removeFlow");
114         }
115     }
116
117     // TODO: Pull this into ForwardingRulesCommiter and override it here
118
119     @Override
120     public Future<RpcResult<RemoveFlowOutput>> removeWithResult(final InstanceIdentifier<Flow> identifier,
121             final Flow removeDataObj, final InstanceIdentifier<FlowCapableNode> nodeIdent) {
122
123         Future<RpcResult<RemoveFlowOutput>> resultFuture = SettableFuture.create();
124         final TableKey tableKey = identifier.firstKeyOf(Table.class, TableKey.class);
125         if (tableIdValidationPrecondition(tableKey, removeDataObj)) {
126             final RemoveFlowInputBuilder builder = new RemoveFlowInputBuilder(removeDataObj);
127             builder.setFlowRef(new FlowRef(identifier));
128             builder.setNode(new NodeRef(nodeIdent.firstIdentifierOf(Node.class)));
129             builder.setFlowTable(new FlowTableRef(nodeIdent.child(Table.class, tableKey)));
130
131             // This method is called only when a given flow object has been
132             // removed from datastore. So FRM always needs to set strict flag
133             // into remove-flow input so that only a flow entry associated with
134             // a given flow object is removed.
135             builder.setTransactionUri(new Uri(provider.getNewTransactionId())).setStrict(Boolean.TRUE);
136             resultFuture = provider.getSalFlowService().removeFlow(builder.build());
137         }
138
139         return resultFuture;
140     }
141
142     @Override
143     public void update(final InstanceIdentifier<Flow> identifier, final Flow original, final Flow update,
144             final InstanceIdentifier<FlowCapableNode> nodeIdent) {
145
146         final TableKey tableKey = identifier.firstKeyOf(Table.class, TableKey.class);
147         if (tableIdValidationPrecondition(tableKey, update)) {
148             final UpdateFlowInputBuilder builder = new UpdateFlowInputBuilder();
149
150             builder.setNode(new NodeRef(nodeIdent.firstIdentifierOf(Node.class)));
151             builder.setFlowRef(new FlowRef(identifier));
152             builder.setTransactionUri(new Uri(provider.getNewTransactionId()));
153
154             // This method is called only when a given flow object in datastore
155             // has been updated. So FRM always needs to set strict flag into
156             // update-flow input so that only a flow entry associated with
157             // a given flow object is updated.
158             builder.setUpdatedFlow(new UpdatedFlowBuilder(update).setStrict(Boolean.TRUE).build());
159             builder.setOriginalFlow(new OriginalFlowBuilder(original).setStrict(Boolean.TRUE).build());
160
161             final Future<RpcResult<UpdateFlowOutput>> resultFuture =
162                     provider.getSalFlowService().updateFlow(builder.build());
163             JdkFutures.addErrorLogging(resultFuture, LOG, "updateFlow");
164         }
165     }
166
167     @Override
168     public Future<RpcResult<AddFlowOutput>> add(final InstanceIdentifier<Flow> identifier, final Flow addDataObj,
169             final InstanceIdentifier<FlowCapableNode> nodeIdent) {
170
171         Future<RpcResult<AddFlowOutput>> future;
172         final TableKey tableKey = identifier.firstKeyOf(Table.class, TableKey.class);
173         if (tableIdValidationPrecondition(tableKey, addDataObj)) {
174             final AddFlowInputBuilder builder = new AddFlowInputBuilder(addDataObj);
175
176             builder.setNode(new NodeRef(nodeIdent.firstIdentifierOf(Node.class)));
177             builder.setFlowRef(new FlowRef(identifier));
178             builder.setFlowTable(new FlowTableRef(nodeIdent.child(Table.class, tableKey)));
179             builder.setTransactionUri(new Uri(provider.getNewTransactionId()));
180             future = provider.getSalFlowService().addFlow(builder.build());
181         } else {
182             future = Futures.<RpcResult<AddFlowOutput>>immediateFuture(null);
183         }
184
185         return future;
186     }
187
188     @Override
189     public void createStaleMarkEntity(InstanceIdentifier<Flow> identifier, Flow del,
190             InstanceIdentifier<FlowCapableNode> nodeIdent) {
191         LOG.debug("Creating Stale-Mark entry for the switch {} for flow {} ", nodeIdent.toString(), del.toString());
192
193         StaleFlow staleFlow = makeStaleFlow(identifier, del, nodeIdent);
194         persistStaleFlow(staleFlow, nodeIdent);
195
196     }
197
198     @Override
199     protected InstanceIdentifier<Flow> getWildCardPath() {
200         return InstanceIdentifier.create(Nodes.class).child(Node.class).augmentation(FlowCapableNode.class)
201                 .child(Table.class).child(Flow.class);
202     }
203
204     private static boolean tableIdValidationPrecondition(final TableKey tableKey, final Flow flow) {
205         Preconditions.checkNotNull(tableKey, "TableKey can not be null or empty!");
206         Preconditions.checkNotNull(flow, "Flow can not be null or empty!");
207         if (!tableKey.getId().equals(flow.getTableId())) {
208             LOG.warn("TableID in URI tableId={} and in palyload tableId={} is not same.", flow.getTableId(),
209                     tableKey.getId());
210             return false;
211         }
212         return true;
213     }
214
215     private StaleFlow makeStaleFlow(InstanceIdentifier<Flow> identifier, Flow del,
216             InstanceIdentifier<FlowCapableNode> nodeIdent) {
217         StaleFlowBuilder staleFlowBuilder = new StaleFlowBuilder(del);
218         return staleFlowBuilder.setId(del.getId()).build();
219     }
220
221     private void persistStaleFlow(StaleFlow staleFlow, InstanceIdentifier<FlowCapableNode> nodeIdent) {
222         WriteTransaction writeTransaction = dataBroker.newWriteOnlyTransaction();
223         writeTransaction.put(LogicalDatastoreType.CONFIGURATION, getStaleFlowInstanceIdentifier(staleFlow, nodeIdent),
224                 staleFlow, false);
225
226         ListenableFuture<Void> submitFuture = writeTransaction.submit();
227         handleStaleFlowResultFuture(submitFuture);
228     }
229
230     private void handleStaleFlowResultFuture(ListenableFuture<Void> submitFuture) {
231         Futures.addCallback(submitFuture, new FutureCallback<Void>() {
232             @Override
233             public void onSuccess(Void result) {
234                 LOG.debug("Stale Flow creation success");
235             }
236
237             @Override
238             public void onFailure(Throwable throwable) {
239                 LOG.error("Stale Flow creation failed {}", throwable);
240             }
241         }, MoreExecutors.directExecutor());
242
243     }
244
245     private InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight
246         .flow.inventory.rev130819.tables.table.StaleFlow> getStaleFlowInstanceIdentifier(
247             StaleFlow staleFlow, InstanceIdentifier<FlowCapableNode> nodeIdent) {
248         return nodeIdent.child(Table.class, new TableKey(staleFlow.getTableId())).child(
249                 org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.StaleFlow.class,
250                 new StaleFlowKey(new FlowId(staleFlow.getId())));
251     }
252 }