Update MRI projects for Aluminium
[ovsdb.git] / southbound / southbound-impl / src / main / java / org / opendaylight / ovsdb / southbound / reconciliation / configuration / BridgeConfigReconciliationTask.java
1 /*
2  * Copyright (c) 2016, 2017 NEC Corporation 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.ovsdb.southbound.reconciliation.configuration;
9
10 import com.google.common.annotations.VisibleForTesting;
11 import com.google.common.util.concurrent.FluentFuture;
12 import com.google.common.util.concurrent.FutureCallback;
13 import com.google.common.util.concurrent.MoreExecutors;
14 import java.util.ArrayList;
15 import java.util.Collections;
16 import java.util.HashMap;
17 import java.util.List;
18 import java.util.Map;
19 import java.util.Optional;
20 import java.util.Set;
21 import org.eclipse.jdt.annotation.Nullable;
22 import org.opendaylight.mdsal.binding.api.ReadTransaction;
23 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
24 import org.opendaylight.ovsdb.southbound.InstanceIdentifierCodec;
25 import org.opendaylight.ovsdb.southbound.OvsdbConnectionInstance;
26 import org.opendaylight.ovsdb.southbound.OvsdbConnectionManager;
27 import org.opendaylight.ovsdb.southbound.SouthboundConstants;
28 import org.opendaylight.ovsdb.southbound.SouthboundMapper;
29 import org.opendaylight.ovsdb.southbound.SouthboundProvider;
30 import org.opendaylight.ovsdb.southbound.ovsdb.transact.BridgeOperationalState;
31 import org.opendaylight.ovsdb.southbound.ovsdb.transact.DataChangeEvent;
32 import org.opendaylight.ovsdb.southbound.ovsdb.transact.DataChangesManagedByOvsdbNodeEvent;
33 import org.opendaylight.ovsdb.southbound.ovsdb.transact.TransactCommandAggregator;
34 import org.opendaylight.ovsdb.southbound.reconciliation.ReconciliationManager;
35 import org.opendaylight.ovsdb.southbound.reconciliation.ReconciliationTask;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbBridgeAugmentation;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.bridge.attributes.ControllerEntry;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.bridge.attributes.ControllerEntryKey;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.bridge.attributes.ProtocolEntry;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.bridge.attributes.ProtocolEntryKey;
41 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
42 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
43 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
44 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeKey;
45 import org.opendaylight.yangtools.yang.binding.DataObject;
46 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
47 import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
48 import org.slf4j.Logger;
49 import org.slf4j.LoggerFactory;
50
51 /**
52  * Configuration Reconciliation task to reconcile existing bridge configurations in the config datastore and the
53  * switch when the latter is up and connected to the controller.
54  * Created by Vinh Nguyen (vinh.nguyen@hcl.com) on 3/21/16.
55  */
56 public class BridgeConfigReconciliationTask extends ReconciliationTask {
57     private static final Logger LOG = LoggerFactory.getLogger(BridgeConfigReconciliationTask.class);
58
59     private final OvsdbConnectionInstance connectionInstance;
60     private final InstanceIdentifierCodec instanceIdentifierCodec;
61
62     public BridgeConfigReconciliationTask(final ReconciliationManager reconciliationManager,
63             final OvsdbConnectionManager connectionManager, final InstanceIdentifier<Node> nodeIid,
64             final OvsdbConnectionInstance connectionInstance, final InstanceIdentifierCodec instanceIdentifierCodec) {
65         super(reconciliationManager, connectionManager, nodeIid, null);
66         this.connectionInstance = connectionInstance;
67         this.instanceIdentifierCodec = instanceIdentifierCodec;
68     }
69
70     @Override
71     public boolean reconcileConfiguration(final OvsdbConnectionManager connectionManagerOfDevice) {
72
73         String nodeIdVal = nodeIid.firstKeyOf(Node.class).getNodeId().getValue();
74         List<String> bridgeReconcileIncludeList = getNodeIdForBridges(nodeIdVal,
75             SouthboundProvider.getBridgesReconciliationInclusionList());
76         List<String> bridgeReconcileExcludeList = getNodeIdForBridges(nodeIdVal,
77             SouthboundProvider.getBridgesReconciliationExclusionList());
78
79         LOG.trace("bridgeReconcileIncludeList : {}", bridgeReconcileIncludeList);
80         LOG.trace("bridgeReconcileExcludeList : {}", bridgeReconcileExcludeList);
81         // (1) Both "bridge-reconciliation-inclusion-list" and "bridge-reconciliation-exclusion-list" are empty.
82         // it means it will keep the default behavior of reconciling on all bridges.
83         // (2) Only "bridge-reconciliation-inclusion-list" has list of bridge.
84         // than plugin will only reconcile specified bridges.
85         // (3) Only "bridge-reconciliation-exclusion-list" has list of bridge.
86         // than plugin will reconcile all the bridge, except excluding the specified bridges.
87         // (4) Both bridge-reconciliation-inclusion-list and bridge-reconciliation-exclusion-list has bridges specified.
88         // this is invalid scenario, so it should log the warning saying this is not valid configuration,
89         // but plugin will give priority to "bridge-reconciliation-exclusion-list" and reconcile all the bridges
90         // except the one specified in the exclusion-list.
91
92         Boolean reconcileAllBridges = Boolean.FALSE;
93         if (bridgeReconcileIncludeList.isEmpty() && bridgeReconcileExcludeList.isEmpty()
94             || bridgeReconcileIncludeList.isEmpty() && !bridgeReconcileExcludeList.isEmpty()) {
95             // Case 1 & 3
96             reconcileAllBridges = Boolean.TRUE;
97         } else if (!bridgeReconcileIncludeList.isEmpty() && !bridgeReconcileExcludeList.isEmpty()) {
98             // Case 4
99             LOG.warn(
100                 "Not a valid case of having both inclusion list : {} and exclusion list : {} for reconcile."
101                     + "OvsDb Plugin will reconcile all the bridge excluding exclusion list bridges",
102                 bridgeReconcileIncludeList, bridgeReconcileExcludeList);
103             reconcileAllBridges = Boolean.TRUE;
104         }
105
106         List<Node> bridgeNodeList = new ArrayList<>();
107
108         if (reconcileAllBridges) {
109             // case 1, 3 & 4
110             LOG.trace("Reconciling all bridges with exclusion list {}", bridgeReconcileExcludeList);
111             FluentFuture<Optional<Topology>> readTopologyFuture;
112             InstanceIdentifier<Topology> topologyInstanceIdentifier = SouthboundMapper
113                 .createTopologyInstanceIdentifier();
114             try (ReadTransaction tx = reconciliationManager.getDb().newReadOnlyTransaction()) {
115                 // find all bridges of the specific device in the config data store
116                 // TODO: this query is not efficient. It retrieves all the Nodes in the datastore, loop over them and
117                 // look for the bridges of specific device. It is mre efficient if MDSAL allows query nodes using
118                 // wildcard on node id (ie: ovsdb://uuid/<device uuid>/bridge/*) r attributes
119                 readTopologyFuture = tx.read(LogicalDatastoreType.CONFIGURATION, topologyInstanceIdentifier);
120             }
121             readTopologyFuture.addCallback(new FutureCallback<Optional<Topology>>() {
122                 @Override
123                 public void onSuccess(@Nullable final Optional<Topology> optionalTopology) {
124                     if (optionalTopology != null && optionalTopology.isPresent()) {
125                         Map<NodeKey, Node> nodes = optionalTopology.get().getNode();
126                         if (nodes != null) {
127                             for (Node node : nodes.values()) {
128                                 String bridgeNodeIid = node.getNodeId().getValue();
129                                 LOG.trace("bridgeNodeIid : {}", bridgeNodeIid);
130                                 if (bridgeReconcileExcludeList.contains(bridgeNodeIid)) {
131                                     LOG.trace(
132                                         "Ignoring reconcilation on bridge:{} as its part of exclusion list",
133                                         bridgeNodeIid);
134                                     continue;
135                                 }
136                                 bridgeNodeList.add(node);
137                             }
138                         }
139                     }
140                 }
141
142                 @Override
143                 public void onFailure(final Throwable throwable) {
144                     LOG.warn("Read Config/DS for Topology failed! {}", nodeIid, throwable);
145                 }
146
147             }, MoreExecutors.directExecutor());
148         } else {
149             // Case 3
150             // Reconciling Specific set of bridges in order to avoid full Topology Read.
151             FluentFuture<Optional<Node>> readNodeFuture;
152             LOG.trace("Reconcile Bridge from InclusionList {} only", bridgeReconcileIncludeList);
153             for (String bridgeNodeIid : bridgeReconcileIncludeList) {
154                 try (ReadTransaction tx = reconciliationManager.getDb().newReadOnlyTransaction()) {
155                     InstanceIdentifier<Node> nodeInstanceIdentifier =
156                         SouthboundMapper.createInstanceIdentifier(new NodeId(bridgeNodeIid));
157                     readNodeFuture = tx.read(LogicalDatastoreType.CONFIGURATION, nodeInstanceIdentifier);
158                 }
159                 readNodeFuture.addCallback(new FutureCallback<Optional<Node>>() {
160                     @Override
161                     public void onSuccess(@Nullable final Optional<Node> optionalTopology) {
162                         if (optionalTopology != null && optionalTopology.isPresent()) {
163                             Node node = optionalTopology.get();
164                             if (node != null) {
165                                 bridgeNodeList.add(node);
166                             }
167                         } else {
168                             LOG.info("Reconciliation of bridge {} missing in network-topology config DataStore",
169                                 bridgeNodeIid);
170                         }
171                     }
172
173                     @Override
174                     public void onFailure(final Throwable throwable) {
175                         LOG.warn("Read Config/DS for Topology failed! {}", bridgeNodeIid, throwable);
176                     }
177                 }, MoreExecutors.directExecutor());
178             }
179         }
180
181         final Map<InstanceIdentifier<?>, DataObject> brChanges = new HashMap<>();
182         final List<Node> tpChanges = new ArrayList<>();
183         for (Node node : bridgeNodeList) {
184             InstanceIdentifier<Node> ndIid = (InstanceIdentifier<Node>) nodeIid;
185             OvsdbBridgeAugmentation bridge = node.augmentation(OvsdbBridgeAugmentation.class);
186             if (bridge != null && bridge.getManagedBy() != null
187                 && bridge.getManagedBy().getValue().equals(ndIid)) {
188                 brChanges.putAll(extractBridgeConfigurationChanges(node, bridge));
189                 tpChanges.add(node);
190             } else if (node.key().getNodeId().getValue().startsWith(
191                 nodeIid.firstKeyOf(Node.class).getNodeId().getValue())) {
192                 //&& node.getTerminationPoint() != null && !node.getTerminationPoint().isEmpty()) {
193                 // Above check removed to handle delete reconciliation with ManagedBy
194                 // param not set in config DS
195                 tpChanges.add(node);
196             } else {
197                 LOG.trace("Ignoring Reconcilation of Bridge: {}", node.key().getNodeId().getValue());
198
199             }
200         }
201
202         if (!brChanges.isEmpty()) {
203             reconcileBridgeConfigurations(brChanges);
204         }
205         if (!tpChanges.isEmpty()) {
206             reconciliationManager.reconcileTerminationPoints(
207                     connectionManagerOfDevice, connectionInstance, tpChanges);
208         }
209         return true;
210     }
211
212     private static Map<InstanceIdentifier<?>, DataObject> extractBridgeConfigurationChanges(
213             final Node bridgeNode, final OvsdbBridgeAugmentation ovsdbBridge) {
214         Map<InstanceIdentifier<?>, DataObject> changes = new HashMap<>();
215         final InstanceIdentifier<Node> bridgeNodeIid =
216                 SouthboundMapper.createInstanceIdentifier(bridgeNode.getNodeId());
217         final InstanceIdentifier<OvsdbBridgeAugmentation> ovsdbBridgeIid =
218                 bridgeNodeIid.builder().augmentation(OvsdbBridgeAugmentation.class).build();
219         changes.put(bridgeNodeIid, bridgeNode);
220         changes.put(ovsdbBridgeIid, ovsdbBridge);
221
222         final Map<ProtocolEntryKey, ProtocolEntry> protocols = ovsdbBridge.getProtocolEntry();
223         if (protocols != null) {
224             for (ProtocolEntry protocol : protocols.values()) {
225                 if (SouthboundConstants.OVSDB_PROTOCOL_MAP.get(protocol.getProtocol()) != null) {
226                     KeyedInstanceIdentifier<ProtocolEntry, ProtocolEntryKey> protocolIid =
227                             ovsdbBridgeIid.child(ProtocolEntry.class, protocol.key());
228                     changes.put(protocolIid, protocol);
229                 } else {
230                     throw new IllegalArgumentException("Unknown protocol " + protocol.getProtocol());
231                 }
232             }
233         }
234
235         final Map<ControllerEntryKey, ControllerEntry> controllers = ovsdbBridge.getControllerEntry();
236         if (controllers != null) {
237             for (ControllerEntry controller : controllers.values()) {
238                 KeyedInstanceIdentifier<ControllerEntry, ControllerEntryKey> controllerIid =
239                         ovsdbBridgeIid.child(ControllerEntry.class, controller.key());
240                 changes.put(controllerIid, controller);
241             }
242         }
243
244         return changes;
245     }
246
247     @VisibleForTesting
248     void reconcileBridgeConfigurations(final Map<InstanceIdentifier<?>, DataObject> changes) {
249         DataChangeEvent changeEvents = new DataChangeEvent() {
250             @Override
251             public Map<InstanceIdentifier<?>, DataObject> getCreatedData() {
252                 return changes;
253             }
254
255             @Override
256             public Map<InstanceIdentifier<?>, DataObject> getUpdatedData() {
257                 return Collections.emptyMap();
258             }
259
260             @Override
261             public Map<InstanceIdentifier<?>, DataObject> getOriginalData() {
262                 return Collections.emptyMap();
263             }
264
265             @Override
266             public Set<InstanceIdentifier<?>> getRemovedPaths() {
267                 return Collections.emptySet();
268             }
269         };
270
271         connectionInstance.transact(new TransactCommandAggregator(),
272                 new BridgeOperationalState(reconciliationManager.getDb(), changeEvents),
273                 new DataChangesManagedByOvsdbNodeEvent(
274                         reconciliationManager.getDb(),
275                         connectionInstance.getInstanceIdentifier(),
276                         changeEvents), instanceIdentifierCodec);
277     }
278
279     @Override
280     public void doRetry(final boolean wasPreviousAttemptSuccessful) {
281     }
282
283     @Override
284     public void checkReadinessAndProcess() {
285     }
286
287     @Override
288     public long retryDelayInMills() {
289         return 0;
290     }
291
292     private static List<String> getNodeIdForBridges(final String nodeIdVal, final List<String> bridgeList) {
293         List<String> nodeIdBridgeList = new ArrayList<>();
294         for (String bridge : bridgeList) {
295             String bridgeNodeIid = new StringBuilder().append(nodeIdVal)
296                 .append(SouthboundConstants.URI_SEPERATOR).append(SouthboundConstants.BRIDGE_URI_PREFIX)
297                 .append(SouthboundConstants.URI_SEPERATOR).append(bridge).toString();
298             nodeIdBridgeList.add(bridgeNodeIid);
299         }
300         return nodeIdBridgeList;
301     }
302 }