2 * Copyright (c) 2014 Cisco Systems, Inc. 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.l2switch.loopremover.topology;
10 import com.google.common.base.Optional;
11 import com.google.common.base.Preconditions;
12 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
13 import org.opendaylight.controller.md.sal.binding.api.DataChangeListener;
14 import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
15 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker;
16 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
17 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
18 import org.opendaylight.l2switch.loopremover.util.InstanceIdentifierUtils;
19 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRef;
20 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
21 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodesBuilder;
22 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector;
23 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorBuilder;
24 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey;
25 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeBuilder;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.l2switch.loopremover.rev140714.StpStatus;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.l2switch.loopremover.rev140714.StpStatusAwareNodeConnector;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.l2switch.loopremover.rev140714.StpStatusAwareNodeConnectorBuilder;
31 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;
32 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TopologyId;
33 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
34 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey;
35 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Link;
36 import org.opendaylight.yangtools.concepts.ListenerRegistration;
37 import org.opendaylight.yangtools.yang.binding.DataObject;
38 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
39 import org.slf4j.Logger;
40 import org.slf4j.LoggerFactory;
42 import java.util.ArrayList;
43 import java.util.List;
46 import java.util.concurrent.ExecutorService;
47 import java.util.concurrent.Executors;
50 * Listens to data change events on topology links
51 * {@link org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Link}
52 * and maintains a topology graph using provided NetworkGraphService
53 * {@link org.opendaylight.l2switch.loopremover.topology.NetworkGraphService}.
54 * It refreshes the graph after a delay(default 10 sec) to accommodate burst of change events if they come in bulk.
55 * This is to avoid continuous refresh of graph on a series of change events in short time.
57 public class TopologyLinkDataChangeHandler implements DataChangeListener {
58 private static final Logger _logger = LoggerFactory.getLogger(TopologyLinkDataChangeHandler.class);
59 private static final String DEFAULT_TOPOLOGY_ID = "flow:1";
61 private final ExecutorService topologyDataChangeEventProcessor = Executors.newCachedThreadPool();
63 private final NetworkGraphService networkGraphService;
64 private final DataBroker dataBroker;
65 boolean doneOnce = false;
68 * Uses default delay to refresh topology graph if this constructor is used.
71 * @param networkGraphService
73 public TopologyLinkDataChangeHandler(DataBroker dataBroker, NetworkGraphService networkGraphService) {
74 Preconditions.checkNotNull(dataBroker, "dataBroker should not be null.");
75 Preconditions.checkNotNull(networkGraphService, "networkGraphService should not be null.");
76 this.dataBroker = dataBroker;
77 this.networkGraphService = networkGraphService;
81 * Registers as a data listener to receive changes done to
82 * {@link org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Link}
83 * under {@link org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology}
84 * operation data root.
87 public ListenerRegistration<DataChangeListener> registerAsDataChangeListener() {
88 InstanceIdentifier<Link> linkInstance = InstanceIdentifier.builder(NetworkTopology.class)
89 .child(Topology.class, new TopologyKey(new TopologyId(DEFAULT_TOPOLOGY_ID))).child(Link.class).toInstance();
90 return dataBroker.registerDataChangeListener(LogicalDatastoreType.OPERATIONAL, linkInstance, this, AsyncDataBroker.DataChangeScope.BASE);
94 public void onDataChanged(AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> instanceIdentifierDataObjectAsyncDataChangeEvent) {
95 if(instanceIdentifierDataObjectAsyncDataChangeEvent == null) {
99 topologyDataChangeEventProcessor.submit(new TopologyDataChangeEventProcessor(instanceIdentifierDataObjectAsyncDataChangeEvent));
100 _logger.info("************After topologyDataChangeEventProcessor called ");
106 private class TopologyDataChangeEventProcessor implements Runnable {
107 AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> instanceIdentifierDataObjectAsyncDataChangeEvent;
110 public TopologyDataChangeEventProcessor(AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> dataChangeEvent) {
111 this.instanceIdentifierDataObjectAsyncDataChangeEvent = dataChangeEvent;
117 if(instanceIdentifierDataObjectAsyncDataChangeEvent == null) {
120 Map<InstanceIdentifier<?>, DataObject> createdData = instanceIdentifierDataObjectAsyncDataChangeEvent.getCreatedData();
121 Set<InstanceIdentifier<?>> removedPaths = instanceIdentifierDataObjectAsyncDataChangeEvent.getRemovedPaths();
122 Map<InstanceIdentifier<?>, DataObject> originalData = instanceIdentifierDataObjectAsyncDataChangeEvent.getOriginalData();
123 boolean isGraphUpdated = false;
124 ReadWriteTransaction readWriteTransaction = null;
126 _logger.info("Topology Event Started********************** ");
127 if(createdData != null && !createdData.isEmpty()) {
128 List<Link> links = new ArrayList<>();
129 for(InstanceIdentifier<?> instanceId : createdData.keySet()) {
130 if(Link.class.isAssignableFrom(instanceId.getTargetType())) {
131 links.add((Link) createdData.get(instanceId));
134 if(!links.isEmpty()) {
135 networkGraphService.addLinks(links);
136 isGraphUpdated = true;
140 List<Link> removedLinks = null;
141 if(removedPaths != null && !removedPaths.isEmpty() && originalData != null && !originalData.isEmpty()) {
142 removedLinks = new ArrayList<>();
143 for(InstanceIdentifier<?> instanceId : removedPaths) {
144 if(Link.class.isAssignableFrom(instanceId.getTargetType())) {
145 Link link = (Link) originalData.get(instanceId);
146 removedLinks.add(link);
149 if(!removedLinks.isEmpty()) {
150 networkGraphService.removeLinks(removedLinks);
151 isGraphUpdated = true;
156 readWriteTransaction = dataBroker.newReadWriteTransaction();
157 if(removedLinks != null && !removedLinks.isEmpty()) {
158 for(Link link : removedLinks) {
159 updateNodeConnector(readWriteTransaction, getSourceNodeConnectorRef(link), StpStatus.Discarding);
160 updateNodeConnector(readWriteTransaction, getDestNodeConnectorRef(link), StpStatus.Discarding);
163 updateNodeConnectorStatus(readWriteTransaction);
164 readWriteTransaction.commit();
169 * @param readWriteTransaction
171 private void updateNodeConnectorStatus(ReadWriteTransaction readWriteTransaction) {
172 List<Link> allLinks = networkGraphService.getAllLinks();
173 if(allLinks == null || allLinks.isEmpty()) {
177 List<Link> mstLinks = networkGraphService.getLinksInMst();
178 for(Link link : allLinks) {
179 if(mstLinks != null && !mstLinks.isEmpty() && mstLinks.contains(link)) {
180 updateNodeConnector(readWriteTransaction, getSourceNodeConnectorRef(link), StpStatus.Forwarding);
181 updateNodeConnector(readWriteTransaction, getDestNodeConnectorRef(link), StpStatus.Forwarding);
183 updateNodeConnector(readWriteTransaction, getSourceNodeConnectorRef(link), StpStatus.Discarding);
184 updateNodeConnector(readWriteTransaction, getDestNodeConnectorRef(link), StpStatus.Discarding);
193 private NodeConnectorRef getSourceNodeConnectorRef(Link link) {
194 InstanceIdentifier<NodeConnector> nodeConnectorInstanceIdentifier
195 = InstanceIdentifierUtils.createNodeConnectorIdentifier(
196 link.getSource().getSourceNode().getValue(),
197 link.getSource().getSourceTp().getValue());
198 return new NodeConnectorRef(nodeConnectorInstanceIdentifier);
205 private NodeConnectorRef getDestNodeConnectorRef(Link link) {
206 InstanceIdentifier<NodeConnector> nodeConnectorInstanceIdentifier
207 = InstanceIdentifierUtils.createNodeConnectorIdentifier(
208 link.getDestination().getDestNode().getValue(),
209 link.getDestination().getDestTp().getValue());
211 return new NodeConnectorRef(nodeConnectorInstanceIdentifier);
215 * @param readWriteTransaction
216 * @param nodeConnectorRef
219 private void updateNodeConnector(ReadWriteTransaction readWriteTransaction, NodeConnectorRef nodeConnectorRef, StpStatus stpStatus) {
220 StpStatusAwareNodeConnectorBuilder stpStatusAwareNodeConnectorBuilder = new StpStatusAwareNodeConnectorBuilder()
221 .setStatus(stpStatus);
222 checkIfExistAndUpdateNodeConnector(readWriteTransaction, nodeConnectorRef, stpStatusAwareNodeConnectorBuilder.build());
226 * @param readWriteTransaction
227 * @param nodeConnectorRef
228 * @param stpStatusAwareNodeConnector
230 private void checkIfExistAndUpdateNodeConnector(ReadWriteTransaction readWriteTransaction, NodeConnectorRef nodeConnectorRef, StpStatusAwareNodeConnector stpStatusAwareNodeConnector) {
231 NodeConnector nc = null;
233 Optional<NodeConnector> dataObjectOptional = readWriteTransaction.read(LogicalDatastoreType.CONFIGURATION, (InstanceIdentifier<NodeConnector>)nodeConnectorRef.getValue()).get();
234 if(dataObjectOptional.isPresent())
235 nc = (NodeConnector) dataObjectOptional.get();
236 } catch(Exception e) {
237 _logger.error("Error reading node connector {}", nodeConnectorRef.getValue());
238 readWriteTransaction.commit();
239 throw new RuntimeException("Error reading from configuration store, node connector : " + nodeConnectorRef, e);
241 NodeConnectorBuilder nodeConnectorBuilder;
243 if(sameStatusPresent(nc.getAugmentation(StpStatusAwareNodeConnector.class), stpStatusAwareNodeConnector.getStatus())) {
246 nodeConnectorBuilder = new NodeConnectorBuilder(nc)
248 .addAugmentation(StpStatusAwareNodeConnector.class, stpStatusAwareNodeConnector);
249 readWriteTransaction.put(LogicalDatastoreType.CONFIGURATION, nodeConnectorRef.getValue(), nodeConnectorBuilder.build());
250 _logger.info("Updated node connector in configuration {}", nodeConnectorRef);
253 NodeConnectorKey nodeConnectorKey = InstanceIdentifierUtils.getNodeConnectorKey(nodeConnectorRef.getValue());
254 nodeConnectorBuilder = new NodeConnectorBuilder()
255 .setKey(nodeConnectorKey)
256 .setId(nodeConnectorKey.getId())
257 .addAugmentation(StpStatusAwareNodeConnector.class, stpStatusAwareNodeConnector);
258 nc = nodeConnectorBuilder.build();
259 checkIfExistAndUpdateNode(readWriteTransaction, nodeConnectorRef, nc);
264 * @param stpStatusAwareNodeConnector
267 private boolean sameStatusPresent(StpStatusAwareNodeConnector stpStatusAwareNodeConnector, StpStatus stpStatus) {
269 if(stpStatusAwareNodeConnector == null)
272 if(stpStatusAwareNodeConnector.getStatus() == null)
275 if(stpStatus.getIntValue() != stpStatusAwareNodeConnector.getStatus().getIntValue())
282 * @param readWriteTransaction
283 * @param nodeConnectorRef
286 private void checkIfExistAndUpdateNode(ReadWriteTransaction readWriteTransaction, NodeConnectorRef nodeConnectorRef, NodeConnector nc) {
288 InstanceIdentifier<Node> nodeInstanceIdentifier = InstanceIdentifierUtils.generateNodeInstanceIdentifier(nodeConnectorRef);
290 Optional<Node> dataObjectOptional = readWriteTransaction.read(LogicalDatastoreType.CONFIGURATION, nodeInstanceIdentifier).get();
291 if(dataObjectOptional.isPresent())
292 node = (Node) dataObjectOptional.get();
293 } catch(Exception e) {
294 _logger.error("Error reading node {}", nodeInstanceIdentifier);
295 readWriteTransaction.commit();
296 throw new RuntimeException("Error reading from configuration store, node : " + nodeInstanceIdentifier, e);
299 List<NodeConnector> nodeConnectors = node.getNodeConnector();
300 if(nodeConnectors == null) {
301 nodeConnectors = new ArrayList<>();
303 nodeConnectors.add(nc);
304 NodeBuilder nodeBuilder = new NodeBuilder(node)
305 .setNodeConnector(nodeConnectors);
306 node = nodeBuilder.build();
307 readWriteTransaction.put(LogicalDatastoreType.CONFIGURATION, nodeInstanceIdentifier, node);
308 _logger.info("Updated node {} in configuration store with node id {}", node, nodeInstanceIdentifier);
310 NodeKey nodeKey = nodeConnectorRef.getValue().firstKeyOf(Node.class, NodeKey.class);
311 List<NodeConnector> nodeConnectors = new ArrayList<>();
312 nodeConnectors.add(nc);
313 NodeBuilder nodeBuilder = new NodeBuilder()
315 .setId(nodeKey.getId())
316 .setNodeConnector(nodeConnectors);
317 node = nodeBuilder.build();
319 checkIfExistsAndUpdateNodes(readWriteTransaction, nodeConnectorRef, node);
324 * @param readWriteTransaction
325 * @param nodeConnectorRef
328 private void checkIfExistsAndUpdateNodes(ReadWriteTransaction readWriteTransaction, NodeConnectorRef nodeConnectorRef, Node node) {
330 List<Node> nodesList = null;
333 InstanceIdentifier<Nodes> nodesInstanceIdentifier = nodeConnectorRef.getValue().firstIdentifierOf(Nodes.class);
335 Optional<Nodes> dataObjectOptional = readWriteTransaction.read(LogicalDatastoreType.CONFIGURATION, nodesInstanceIdentifier).get();
336 if(dataObjectOptional.isPresent())
337 nodes = (Nodes) dataObjectOptional.get();
338 } catch(Exception e) {
339 _logger.error("Error reading nodes {}", nodesInstanceIdentifier);
340 readWriteTransaction.commit();
341 throw new RuntimeException("Error reading from configuration store, nodes : " + nodesInstanceIdentifier, e);
344 nodesList = nodes.getNode();
346 if(nodesList == null) {
347 nodesList = new ArrayList<>();
350 NodesBuilder nodesBuilder = new NodesBuilder()
352 nodes = nodesBuilder.build();
353 readWriteTransaction.put(LogicalDatastoreType.CONFIGURATION, nodesInstanceIdentifier, nodes);
354 _logger.info("Updated nodes {} in configuration store with nodes id {}", nodes, nodesInstanceIdentifier);