Update to work on Sodium SR1
[l2switch.git] / arphandler / src / main / java / org / opendaylight / l2switch / arphandler / inventory / InventoryReader.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.arphandler.inventory;
9
10 import com.google.common.base.Optional;
11 import java.util.ArrayList;
12 import java.util.Collection;
13 import java.util.HashMap;
14 import java.util.List;
15 import java.util.concurrent.CopyOnWriteArrayList;
16 import java.util.concurrent.ExecutionException;
17 import java.util.concurrent.Executors;
18 import java.util.concurrent.ScheduledExecutorService;
19 import java.util.concurrent.TimeUnit;
20 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
21 import org.opendaylight.controller.md.sal.binding.api.DataTreeChangeListener;
22 import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;
23 import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
24 import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
25 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
26 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.address.tracker.rev140617.AddressCapableNodeConnector;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.address.tracker.rev140617.address.node.connector.Addresses;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRef;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.l2switch.loopremover.rev140714.StpStatus;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.l2switch.loopremover.rev140714.StpStatusAwareNodeConnector;
38 import org.opendaylight.yangtools.concepts.Registration;
39 import org.opendaylight.yangtools.yang.binding.DataObject;
40 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
41 import org.slf4j.Logger;
42 import org.slf4j.LoggerFactory;
43
44 /**
45  * InventoryReader reads the opendaylight-inventory tree in MD-SAL data store.
46  */
47 public class InventoryReader implements DataTreeChangeListener<DataObject> {
48
49     private static final Logger LOG = LoggerFactory.getLogger(InventoryReader.class);
50     private final DataBroker dataService;
51     // Key: SwitchId, Value: NodeConnectorRef that corresponds to NC between
52     // controller & switch
53     private final HashMap<String, NodeConnectorRef> controllerSwitchConnectors;
54     // Key: SwitchId, Value: List of node connectors on this switch
55     private final HashMap<String, List<NodeConnectorRef>> switchNodeConnectors;
56     private final List<Registration> listenerRegistrationList = new CopyOnWriteArrayList<>();
57
58     private volatile boolean refreshData = false;
59     private final long refreshDataDelay = 20L;
60     private volatile boolean refreshDataScheduled = false;
61     private final ScheduledExecutorService nodeConnectorDataChangeEventProcessor = Executors.newScheduledThreadPool(1);
62
63     /**
64      * Construct an InventoryService object with the specified inputs.
65      *
66      * @param dataService
67      *            The DataBrokerService associated with the InventoryService.
68      */
69     public InventoryReader(DataBroker dataService) {
70         this.dataService = dataService;
71         controllerSwitchConnectors = new HashMap<>();
72         switchNodeConnectors = new HashMap<>();
73     }
74
75     public void setRefreshData(boolean refreshData) {
76         this.refreshData = refreshData;
77     }
78
79     @SuppressWarnings({ "unchecked", "rawtypes" })
80     private void registerAsDataChangeListener() {
81         InstanceIdentifier<NodeConnector> nodeConnector = InstanceIdentifier.builder(Nodes.class)
82                 .child(Node.class)
83                 .child(NodeConnector.class)
84                 .build();
85         this.listenerRegistrationList.add(dataService.registerDataTreeChangeListener(
86                 new DataTreeIdentifier<>(LogicalDatastoreType.OPERATIONAL, nodeConnector),
87                     (DataTreeChangeListener)this));
88
89         InstanceIdentifier<StpStatusAwareNodeConnector> stpStatusAwareNodeConnecto =
90             InstanceIdentifier.builder(Nodes.class).child(Node.class).child(NodeConnector.class)
91                 .augmentation(StpStatusAwareNodeConnector.class)
92                 .build();
93         this.listenerRegistrationList.add(dataService.registerDataTreeChangeListener(
94                 new DataTreeIdentifier<>(LogicalDatastoreType.OPERATIONAL, stpStatusAwareNodeConnecto),
95                     (DataTreeChangeListener)this));
96     }
97
98
99     public HashMap<String, NodeConnectorRef> getControllerSwitchConnectors() {
100         return controllerSwitchConnectors;
101     }
102
103     public HashMap<String, List<NodeConnectorRef>> getSwitchNodeConnectors() {
104         return switchNodeConnectors;
105     }
106
107     @Override
108     public void onDataTreeChanged(Collection<DataTreeModification<DataObject>> changes) {
109         if (!refreshDataScheduled) {
110             synchronized (this) {
111                 if (!refreshDataScheduled) {
112                     nodeConnectorDataChangeEventProcessor.schedule(new NodeConnectorDataChangeEventProcessor(),
113                             refreshDataDelay, TimeUnit.MILLISECONDS);
114                     refreshDataScheduled = true;
115                 }
116             }
117         }
118     }
119
120
121     public void close() {
122         listenerRegistrationList.forEach(reg -> reg.close());
123     }
124
125     /**
126      * Read the Inventory data tree to find information about the Nodes and
127      * NodeConnectors. Create the list of NodeConnectors for a given switch.
128      * Also determine the STP status of each NodeConnector.
129      */
130     public void readInventory() {
131         // Only run once for now
132         if (!refreshData) {
133             return;
134         }
135         synchronized (this) {
136             if (!refreshData) {
137                 return;
138             }
139             // Read Inventory
140             InstanceIdentifier.InstanceIdentifierBuilder<Nodes> nodesInsIdBuilder = InstanceIdentifier
141                     .<Nodes>builder(Nodes.class);
142             Nodes nodes = null;
143             try (ReadOnlyTransaction readOnlyTransaction = dataService.newReadOnlyTransaction()) {
144                 Optional<Nodes> dataObjectOptional = readOnlyTransaction
145                         .read(LogicalDatastoreType.OPERATIONAL, nodesInsIdBuilder.build()).get();
146                 if (dataObjectOptional.isPresent()) {
147                     nodes = dataObjectOptional.get();
148                 }
149             } catch (InterruptedException e) {
150                 LOG.error("Failed to read nodes from Operation data store.");
151                 throw new RuntimeException("Failed to read nodes from Operation data store.", e);
152             } catch (ExecutionException e) {
153                 LOG.error("Failed to read nodes from Operation data store.");
154                 throw new RuntimeException("Failed to read nodes from Operation data store.", e);
155             }
156
157             if (nodes != null) {
158                 // Get NodeConnectors for each node
159                 for (Node node : nodes.getNode()) {
160                     ArrayList<NodeConnectorRef> nodeConnectorRefs = new ArrayList<>();
161                     List<NodeConnector> nodeConnectors = node.getNodeConnector();
162                     if (nodeConnectors != null) {
163                         for (NodeConnector nodeConnector : nodeConnectors) {
164                             // Read STP status for this NodeConnector
165                             StpStatusAwareNodeConnector saNodeConnector = nodeConnector
166                                     .augmentation(StpStatusAwareNodeConnector.class);
167                             if (saNodeConnector != null && StpStatus.Discarding.equals(saNodeConnector.getStatus())) {
168                                 continue;
169                             }
170                             if (nodeConnector.key().toString().contains("LOCAL")) {
171                                 continue;
172                             }
173                             NodeConnectorRef ncRef = new NodeConnectorRef(InstanceIdentifier.<Nodes>builder(Nodes.class)
174                                     .<Node, NodeKey>child(Node.class, node.key())
175                                     .<NodeConnector, NodeConnectorKey>child(NodeConnector.class, nodeConnector.key())
176                                     .build());
177                             nodeConnectorRefs.add(ncRef);
178                         }
179                     }
180
181                     switchNodeConnectors.put(node.getId().getValue(), nodeConnectorRefs);
182                     NodeConnectorRef ncRef = new NodeConnectorRef(InstanceIdentifier.<Nodes>builder(Nodes.class)
183                             .<Node, NodeKey>child(Node.class, node.key())
184                             .<NodeConnector, NodeConnectorKey>child(NodeConnector.class,
185                                     new NodeConnectorKey(new NodeConnectorId(node.getId().getValue() + ":LOCAL")))
186                             .build());
187                     LOG.debug("Local port for node {} is {}", node.key(), ncRef);
188                     controllerSwitchConnectors.put(node.getId().getValue(), ncRef);
189                 }
190             }
191
192             refreshData = false;
193
194             if (listenerRegistrationList.isEmpty()) {
195                 registerAsDataChangeListener();
196             }
197         }
198     }
199
200     /**
201      * Get the NodeConnector on the specified node with the specified MacAddress
202      * observation.
203      *
204      * @param nodeInsId
205      *            InstanceIdentifier for the node on which to search for.
206      * @param macAddress
207      *            MacAddress to be searched for.
208      * @return NodeConnectorRef that pertains to the NodeConnector containing
209      *         the MacAddress observation.
210      */
211     public NodeConnectorRef getNodeConnector(InstanceIdentifier<Node> nodeInsId, MacAddress macAddress) {
212         if (nodeInsId == null || macAddress == null) {
213             return null;
214         }
215
216         NodeConnectorRef destNodeConnector = null;
217         long latest = -1;
218         ReadOnlyTransaction readOnlyTransaction = dataService.newReadOnlyTransaction();
219         try {
220             Optional<Node> dataObjectOptional = null;
221             dataObjectOptional = readOnlyTransaction.read(LogicalDatastoreType.OPERATIONAL, nodeInsId).get();
222             if (dataObjectOptional.isPresent()) {
223                 Node node = dataObjectOptional.get();
224                 LOG.debug("Looking address{} in node : {}", macAddress, nodeInsId);
225                 if (node.getNodeConnector() != null) {
226                     for (NodeConnector nc : node.getNodeConnector()) {
227                         // Don't look for mac in discarding node connectors
228                         StpStatusAwareNodeConnector saNodeConnector = nc
229                                 .augmentation(StpStatusAwareNodeConnector.class);
230                         if (saNodeConnector != null && StpStatus.Discarding.equals(saNodeConnector.getStatus())) {
231                             continue;
232                         }
233                         LOG.debug("Looking address{} in nodeconnector : {}", macAddress, nc.key());
234                         AddressCapableNodeConnector acnc = nc.augmentation(AddressCapableNodeConnector.class);
235                         if (acnc != null) {
236                             List<Addresses> addressesList = acnc.getAddresses();
237                             for (Addresses add : addressesList) {
238                                 if (macAddress.equals(add.getMac())) {
239                                     if (add.getLastSeen() > latest) {
240                                         destNodeConnector = new NodeConnectorRef(
241                                                 nodeInsId.child(NodeConnector.class, nc.key()));
242                                         latest = add.getLastSeen();
243                                         LOG.debug("Found address{} in nodeconnector : {}", macAddress, nc.key());
244                                         break;
245                                     }
246                                 }
247                             }
248                         }
249                     }
250                 } else {
251                     LOG.debug("Node connectors data is not present for node {}", node.getId());
252                 }
253             }
254         } catch (InterruptedException e) {
255             LOG.error("Failed to read nodes from Operation data store.");
256             readOnlyTransaction.close();
257             throw new RuntimeException("Failed to read nodes from Operation data store.", e);
258         } catch (ExecutionException e) {
259             LOG.error("Failed to read nodes from Operation data store.");
260             readOnlyTransaction.close();
261             throw new RuntimeException("Failed to read nodes from Operation data store.", e);
262         }
263         readOnlyTransaction.close();
264         return destNodeConnector;
265     }
266
267     private class NodeConnectorDataChangeEventProcessor implements Runnable {
268
269         @Override
270         public void run() {
271             controllerSwitchConnectors.clear();
272             switchNodeConnectors.clear();
273             refreshDataScheduled = false;
274             setRefreshData(true);
275             readInventory();
276         }
277     }
278 }