Following Fixes are added
[l2switch.git] / loopremover / implementation / src / main / java / org / opendaylight / l2switch / loopremover / topology / TopologyLinkDataChangeHandler.java
1 /*
2  * Copyright (c) 2014 Cisco Systems, Inc. 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.l2switch.loopremover.topology;
9
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;
41
42 import java.util.ArrayList;
43 import java.util.List;
44 import java.util.Map;
45 import java.util.Set;
46 import java.util.concurrent.ExecutorService;
47 import java.util.concurrent.Executors;
48
49 /**
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.
56  */
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";
60
61   private final ExecutorService topologyDataChangeEventProcessor = Executors.newCachedThreadPool();
62
63   private final NetworkGraphService networkGraphService;
64   private final DataBroker dataBroker;
65   boolean doneOnce = false;
66
67   /**
68    * Uses default delay to refresh topology graph if this constructor is used.
69    *
70    * @param dataBroker
71    * @param networkGraphService
72    */
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;
78   }
79
80   /**
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.
85    */
86
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);
91   }
92
93   @Override
94   public void onDataChanged(AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> instanceIdentifierDataObjectAsyncDataChangeEvent) {
95     if(instanceIdentifierDataObjectAsyncDataChangeEvent == null) {
96       return;
97     }
98
99     topologyDataChangeEventProcessor.submit(new TopologyDataChangeEventProcessor(instanceIdentifierDataObjectAsyncDataChangeEvent));
100     _logger.info("************After topologyDataChangeEventProcessor called ");
101   }
102
103   /**
104    *
105    */
106   private class TopologyDataChangeEventProcessor implements Runnable {
107     AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> instanceIdentifierDataObjectAsyncDataChangeEvent;
108
109
110     public TopologyDataChangeEventProcessor(AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> dataChangeEvent) {
111       this.instanceIdentifierDataObjectAsyncDataChangeEvent = dataChangeEvent;
112     }
113
114     @Override
115     public void run() {
116
117       if(instanceIdentifierDataObjectAsyncDataChangeEvent == null) {
118         return;
119       }
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;
125
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));
132           }
133         }
134         if(!links.isEmpty()) {
135           networkGraphService.addLinks(links);
136           isGraphUpdated = true;
137         }
138       }
139
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);
147           }
148         }
149         if(!removedLinks.isEmpty()) {
150           networkGraphService.removeLinks(removedLinks);
151           isGraphUpdated = true;
152         }
153       }
154
155       if(isGraphUpdated) {
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);
161           }
162         }
163         updateNodeConnectorStatus(readWriteTransaction);
164         readWriteTransaction.commit();
165       }
166     }
167
168     /**
169      * @param readWriteTransaction
170      */
171     private void updateNodeConnectorStatus(ReadWriteTransaction readWriteTransaction) {
172       List<Link> allLinks = networkGraphService.getAllLinks();
173       if(allLinks == null || allLinks.isEmpty()) {
174         return;
175       }
176
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);
182         } else {
183           updateNodeConnector(readWriteTransaction, getSourceNodeConnectorRef(link), StpStatus.Discarding);
184           updateNodeConnector(readWriteTransaction, getDestNodeConnectorRef(link), StpStatus.Discarding);
185         }
186       }
187     }
188
189     /**
190      * @param link
191      * @return
192      */
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);
199     }
200
201     /**
202      * @param link
203      * @return
204      */
205     private NodeConnectorRef getDestNodeConnectorRef(Link link) {
206       InstanceIdentifier<NodeConnector> nodeConnectorInstanceIdentifier
207           = InstanceIdentifierUtils.createNodeConnectorIdentifier(
208           link.getDestination().getDestNode().getValue(),
209           link.getDestination().getDestTp().getValue());
210
211       return new NodeConnectorRef(nodeConnectorInstanceIdentifier);
212     }
213
214     /**
215      * @param readWriteTransaction
216      * @param nodeConnectorRef
217      * @param stpStatus
218      */
219     private void updateNodeConnector(ReadWriteTransaction readWriteTransaction, NodeConnectorRef nodeConnectorRef, StpStatus stpStatus) {
220       StpStatusAwareNodeConnectorBuilder stpStatusAwareNodeConnectorBuilder = new StpStatusAwareNodeConnectorBuilder()
221           .setStatus(stpStatus);
222       checkIfExistAndUpdateNodeConnector(readWriteTransaction, nodeConnectorRef, stpStatusAwareNodeConnectorBuilder.build());
223     }
224
225     /**
226      * @param readWriteTransaction
227      * @param nodeConnectorRef
228      * @param stpStatusAwareNodeConnector
229      */
230     private void checkIfExistAndUpdateNodeConnector(ReadWriteTransaction readWriteTransaction, NodeConnectorRef nodeConnectorRef, StpStatusAwareNodeConnector stpStatusAwareNodeConnector) {
231       NodeConnector nc = null;
232       try {
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);
240       }
241       NodeConnectorBuilder nodeConnectorBuilder;
242       if(nc != null) {
243         if(sameStatusPresent(nc.getAugmentation(StpStatusAwareNodeConnector.class), stpStatusAwareNodeConnector.getStatus())) {
244           return;
245         }
246         nodeConnectorBuilder = new NodeConnectorBuilder(nc)
247             .setKey(nc.getKey())
248             .addAugmentation(StpStatusAwareNodeConnector.class, stpStatusAwareNodeConnector);
249         readWriteTransaction.put(LogicalDatastoreType.CONFIGURATION, nodeConnectorRef.getValue(), nodeConnectorBuilder.build());
250         _logger.info("Updated node connector in configuration {}", nodeConnectorRef);
251       } else {
252
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);
260       }
261     }
262
263     /**
264      * @param stpStatusAwareNodeConnector
265      * @return
266      */
267     private boolean sameStatusPresent(StpStatusAwareNodeConnector stpStatusAwareNodeConnector, StpStatus stpStatus) {
268
269       if(stpStatusAwareNodeConnector == null)
270         return false;
271
272       if(stpStatusAwareNodeConnector.getStatus() == null)
273         return false;
274
275       if(stpStatus.getIntValue() != stpStatusAwareNodeConnector.getStatus().getIntValue())
276         return false;
277
278       return true;
279     }
280
281     /**
282      * @param readWriteTransaction
283      * @param nodeConnectorRef
284      * @param nc
285      */
286     private void checkIfExistAndUpdateNode(ReadWriteTransaction readWriteTransaction, NodeConnectorRef nodeConnectorRef, NodeConnector nc) {
287       Node node = null;
288       InstanceIdentifier<Node> nodeInstanceIdentifier = InstanceIdentifierUtils.generateNodeInstanceIdentifier(nodeConnectorRef);
289       try {
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);
297       }
298       if(node != null) {
299         List<NodeConnector> nodeConnectors = node.getNodeConnector();
300         if(nodeConnectors == null) {
301           nodeConnectors = new ArrayList<>();
302         }
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);
309       } else {
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()
314             .setKey(nodeKey)
315             .setId(nodeKey.getId())
316             .setNodeConnector(nodeConnectors);
317         node = nodeBuilder.build();
318
319         checkIfExistsAndUpdateNodes(readWriteTransaction, nodeConnectorRef, node);
320       }
321     }
322
323     /**
324      * @param readWriteTransaction
325      * @param nodeConnectorRef
326      * @param node
327      */
328     private void checkIfExistsAndUpdateNodes(ReadWriteTransaction readWriteTransaction, NodeConnectorRef nodeConnectorRef, Node node) {
329
330       List<Node> nodesList = null;
331
332       Nodes nodes = null;
333       InstanceIdentifier<Nodes> nodesInstanceIdentifier = nodeConnectorRef.getValue().firstIdentifierOf(Nodes.class);
334       try {
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);
342       }
343       if(nodes != null) {
344         nodesList = nodes.getNode();
345       }
346       if(nodesList == null) {
347         nodesList = new ArrayList<>();
348       }
349       nodesList.add(node);
350       NodesBuilder nodesBuilder = new NodesBuilder()
351           .setNode(nodesList);
352       nodes = nodesBuilder.build();
353       readWriteTransaction.put(LogicalDatastoreType.CONFIGURATION, nodesInstanceIdentifier, nodes);
354       _logger.info("Updated nodes {}  in configuration store with nodes id {}", nodes, nodesInstanceIdentifier);
355     }
356   }
357 }