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