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.arphandler.inventory;
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;
45 * InventoryReader reads the opendaylight-inventory tree in MD-SAL data store.
47 public class InventoryReader implements DataTreeChangeListener<DataObject> {
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<>();
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);
64 * Construct an InventoryService object with the specified inputs.
67 * The DataBrokerService associated with the InventoryService.
69 public InventoryReader(DataBroker dataService) {
70 this.dataService = dataService;
71 controllerSwitchConnectors = new HashMap<>();
72 switchNodeConnectors = new HashMap<>();
75 public void setRefreshData(boolean refreshData) {
76 this.refreshData = refreshData;
79 @SuppressWarnings({ "unchecked", "rawtypes" })
80 private void registerAsDataChangeListener() {
81 InstanceIdentifier<NodeConnector> nodeConnector = InstanceIdentifier.builder(Nodes.class)
83 .child(NodeConnector.class)
85 this.listenerRegistrationList.add(dataService.registerDataTreeChangeListener(
86 new DataTreeIdentifier<>(LogicalDatastoreType.OPERATIONAL, nodeConnector),
87 (DataTreeChangeListener)this));
89 InstanceIdentifier<StpStatusAwareNodeConnector> stpStatusAwareNodeConnecto =
90 InstanceIdentifier.builder(Nodes.class).child(Node.class).child(NodeConnector.class)
91 .augmentation(StpStatusAwareNodeConnector.class)
93 this.listenerRegistrationList.add(dataService.registerDataTreeChangeListener(
94 new DataTreeIdentifier<>(LogicalDatastoreType.OPERATIONAL, stpStatusAwareNodeConnecto),
95 (DataTreeChangeListener)this));
99 public HashMap<String, NodeConnectorRef> getControllerSwitchConnectors() {
100 return controllerSwitchConnectors;
103 public HashMap<String, List<NodeConnectorRef>> getSwitchNodeConnectors() {
104 return switchNodeConnectors;
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;
121 public void close() {
122 listenerRegistrationList.forEach(reg -> reg.close());
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.
130 public void readInventory() {
131 // Only run once for now
135 synchronized (this) {
140 InstanceIdentifier.InstanceIdentifierBuilder<Nodes> nodesInsIdBuilder = InstanceIdentifier
141 .<Nodes>builder(Nodes.class);
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();
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);
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())) {
170 if (nodeConnector.key().toString().contains("LOCAL")) {
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())
177 nodeConnectorRefs.add(ncRef);
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")))
187 LOG.debug("Local port for node {} is {}", node.key(), ncRef);
188 controllerSwitchConnectors.put(node.getId().getValue(), ncRef);
194 if (listenerRegistrationList.isEmpty()) {
195 registerAsDataChangeListener();
201 * Get the NodeConnector on the specified node with the specified MacAddress
205 * InstanceIdentifier for the node on which to search for.
207 * MacAddress to be searched for.
208 * @return NodeConnectorRef that pertains to the NodeConnector containing
209 * the MacAddress observation.
211 public NodeConnectorRef getNodeConnector(InstanceIdentifier<Node> nodeInsId, MacAddress macAddress) {
212 if (nodeInsId == null || macAddress == null) {
216 NodeConnectorRef destNodeConnector = null;
218 ReadOnlyTransaction readOnlyTransaction = dataService.newReadOnlyTransaction();
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())) {
233 LOG.debug("Looking address{} in nodeconnector : {}", macAddress, nc.key());
234 AddressCapableNodeConnector acnc = nc.augmentation(AddressCapableNodeConnector.class);
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());
251 LOG.debug("Node connectors data is not present for node {}", node.getId());
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);
263 readOnlyTransaction.close();
264 return destNodeConnector;
267 private class NodeConnectorDataChangeEventProcessor implements Runnable {
271 controllerSwitchConnectors.clear();
272 switchNodeConnectors.clear();
273 refreshDataScheduled = false;
274 setRefreshData(true);