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.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;
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.
56 public class BridgeConfigReconciliationTask extends ReconciliationTask {
57 private static final Logger LOG = LoggerFactory.getLogger(BridgeConfigReconciliationTask.class);
59 private final OvsdbConnectionInstance connectionInstance;
60 private final InstanceIdentifierCodec instanceIdentifierCodec;
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;
71 public boolean reconcileConfiguration(final OvsdbConnectionManager connectionManagerOfDevice) {
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());
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.
92 Boolean reconcileAllBridges = Boolean.FALSE;
93 if (bridgeReconcileIncludeList.isEmpty() && bridgeReconcileExcludeList.isEmpty()
94 || bridgeReconcileIncludeList.isEmpty() && !bridgeReconcileExcludeList.isEmpty()) {
96 reconcileAllBridges = Boolean.TRUE;
97 } else if (!bridgeReconcileIncludeList.isEmpty() && !bridgeReconcileExcludeList.isEmpty()) {
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;
106 List<Node> bridgeNodeList = new ArrayList<>();
108 if (reconcileAllBridges) {
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);
121 readTopologyFuture.addCallback(new FutureCallback<Optional<Topology>>() {
123 public void onSuccess(@Nullable final Optional<Topology> optionalTopology) {
124 if (optionalTopology != null && optionalTopology.isPresent()) {
125 Map<NodeKey, Node> nodes = optionalTopology.orElseThrow().getNode();
127 for (Node node : nodes.values()) {
128 String bridgeNodeIid = node.getNodeId().getValue();
129 LOG.trace("bridgeNodeIid : {}", bridgeNodeIid);
130 if (bridgeReconcileExcludeList.contains(bridgeNodeIid)) {
132 "Ignoring reconcilation on bridge:{} as its part of exclusion list",
136 bridgeNodeList.add(node);
143 public void onFailure(final Throwable throwable) {
144 LOG.warn("Read Config/DS for Topology failed! {}", nodeIid, throwable);
147 }, MoreExecutors.directExecutor());
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);
159 readNodeFuture.addCallback(new FutureCallback<Optional<Node>>() {
161 public void onSuccess(@Nullable final Optional<Node> optionalTopology) {
162 if (optionalTopology != null && optionalTopology.isPresent()) {
163 Node node = optionalTopology.orElseThrow();
165 bridgeNodeList.add(node);
168 LOG.info("Reconciliation of bridge {} missing in network-topology config DataStore",
174 public void onFailure(final Throwable throwable) {
175 LOG.warn("Read Config/DS for Topology failed! {}", bridgeNodeIid, throwable);
177 }, MoreExecutors.directExecutor());
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));
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
197 LOG.trace("Ignoring Reconcilation of Bridge: {}", node.key().getNodeId().getValue());
202 if (!brChanges.isEmpty()) {
203 reconcileBridgeConfigurations(brChanges);
205 if (!tpChanges.isEmpty()) {
206 reconciliationManager.reconcileTerminationPoints(
207 connectionManagerOfDevice, connectionInstance, tpChanges);
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);
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);
230 throw new IllegalArgumentException("Unknown protocol " + protocol.getProtocol());
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);
248 void reconcileBridgeConfigurations(final Map<InstanceIdentifier<?>, DataObject> changes) {
249 DataChangeEvent changeEvents = new DataChangeEvent() {
251 public Map<InstanceIdentifier<?>, DataObject> getCreatedData() {
256 public Map<InstanceIdentifier<?>, DataObject> getUpdatedData() {
257 return Collections.emptyMap();
261 public Map<InstanceIdentifier<?>, DataObject> getOriginalData() {
262 return Collections.emptyMap();
266 public Set<InstanceIdentifier<?>> getRemovedPaths() {
267 return Collections.emptySet();
271 connectionInstance.transact(new TransactCommandAggregator(),
272 new BridgeOperationalState(reconciliationManager.getDb(), changeEvents),
273 new DataChangesManagedByOvsdbNodeEvent(
274 reconciliationManager.getDb(),
275 connectionInstance.getInstanceIdentifier(),
276 changeEvents), instanceIdentifierCodec);
280 public void doRetry(final boolean wasPreviousAttemptSuccessful) {
284 public void checkReadinessAndProcess() {
288 public long retryDelayInMills() {
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);
300 return nodeIdBridgeList;