2 * Copyright (c) 2016, 2017 NEC Corporation and others. All rights reserved.
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
8 package org.opendaylight.ovsdb.southbound.reconciliation.configuration;
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;
19 import java.util.Optional;
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;
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.
55 public class BridgeConfigReconciliationTask extends ReconciliationTask {
56 private static final Logger LOG = LoggerFactory.getLogger(BridgeConfigReconciliationTask.class);
58 private final OvsdbConnectionInstance connectionInstance;
59 private final InstanceIdentifierCodec instanceIdentifierCodec;
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;
70 public boolean reconcileConfiguration(final OvsdbConnectionManager connectionManagerOfDevice) {
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());
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.
91 Boolean reconcileAllBridges = Boolean.FALSE;
92 if (bridgeReconcileIncludeList.isEmpty() && bridgeReconcileExcludeList.isEmpty()
93 || bridgeReconcileIncludeList.isEmpty() && !bridgeReconcileExcludeList.isEmpty()) {
95 reconcileAllBridges = Boolean.TRUE;
96 } else if (!bridgeReconcileIncludeList.isEmpty() && !bridgeReconcileExcludeList.isEmpty()) {
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;
105 List<Node> bridgeNodeList = new ArrayList<>();
107 if (reconcileAllBridges) {
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);
120 readTopologyFuture.addCallback(new FutureCallback<Optional<Topology>>() {
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)) {
131 "Ignoring reconcilation on bridge:{} as its part of exclusion list",
135 bridgeNodeList.add(node);
142 public void onFailure(final Throwable throwable) {
143 LOG.warn("Read Config/DS for Topology failed! {}", nodeIid, throwable);
146 }, MoreExecutors.directExecutor());
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);
158 readNodeFuture.addCallback(new FutureCallback<Optional<Node>>() {
160 public void onSuccess(@Nullable final Optional<Node> optionalTopology) {
161 if (optionalTopology != null && optionalTopology.isPresent()) {
162 Node node = optionalTopology.get();
164 bridgeNodeList.add(node);
167 LOG.info("Reconciliation of bridge {} missing in network-topology config DataStore",
173 public void onFailure(final Throwable throwable) {
174 LOG.warn("Read Config/DS for Topology failed! {}", bridgeNodeIid, throwable);
176 }, MoreExecutors.directExecutor());
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));
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
196 LOG.trace("Ignoring Reconcilation of Bridge: {}", node.key().getNodeId().getValue());
201 if (!brChanges.isEmpty()) {
202 reconcileBridgeConfigurations(brChanges);
204 if (!tpChanges.isEmpty()) {
205 reconciliationManager.reconcileTerminationPoints(
206 connectionManagerOfDevice, connectionInstance, tpChanges);
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);
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);
228 throw new IllegalArgumentException("Unknown protocol " + protocol.getProtocol());
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);
245 void reconcileBridgeConfigurations(final Map<InstanceIdentifier<?>, DataObject> changes) {
246 DataChangeEvent changeEvents = new DataChangeEvent() {
248 public Map<InstanceIdentifier<?>, DataObject> getCreatedData() {
253 public Map<InstanceIdentifier<?>, DataObject> getUpdatedData() {
254 return Collections.emptyMap();
258 public Map<InstanceIdentifier<?>, DataObject> getOriginalData() {
259 return Collections.emptyMap();
263 public Set<InstanceIdentifier<?>> getRemovedPaths() {
264 return Collections.emptySet();
268 connectionInstance.transact(new TransactCommandAggregator(),
269 new BridgeOperationalState(reconciliationManager.getDb(), changeEvents),
270 new DataChangesManagedByOvsdbNodeEvent(
271 reconciliationManager.getDb(),
272 connectionInstance.getInstanceIdentifier(),
273 changeEvents), instanceIdentifierCodec);
277 public void doRetry(final boolean wasPreviousAttemptSuccessful) {
281 public void checkReadinessAndProcess() {
285 public long retryDelayInMills() {
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);
297 return nodeIdBridgeList;