Merge "Bug 6366 - of-switch-config-pusher - DTCL instead of DTL"
[openflowplugin.git] / applications / lldp-speaker / src / main / java / org / opendaylight / openflowplugin / applications / lldpspeaker / NodeConnectorInventoryEventTranslator.java
1 /*
2  * Copyright (c) 2014 Pacnet 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
9 package org.opendaylight.openflowplugin.applications.lldpspeaker;
10
11 import com.google.common.collect.ImmutableSet;
12 import com.google.common.collect.Iterables;
13 import java.util.Collection;
14 import java.util.HashMap;
15 import java.util.Map;
16 import java.util.Set;
17 import java.util.concurrent.Callable;
18 import javax.annotation.Nonnull;
19 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
20 import org.opendaylight.controller.md.sal.binding.api.DataTreeChangeListener;
21 import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;
22 import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
23 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
24 import org.opendaylight.openflowplugin.common.wait.SimpleTaskRetryLooper;
25 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnector;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnectorBuilder;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.port.rev130925.PortConfig;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.port.rev130925.PortState;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.port.rev130925.flow.capable.port.State;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
33 import org.opendaylight.yangtools.concepts.ListenerRegistration;
34 import org.opendaylight.yangtools.yang.binding.DataObject;
35 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
36 import org.slf4j.Logger;
37 import org.slf4j.LoggerFactory;
38
39 /**
40  * NodeConnectorInventoryEventTranslator is listening for changes in inventory operational DOM tree
41  * and update LLDPSpeaker and topology.
42  */
43 public class NodeConnectorInventoryEventTranslator<T extends DataObject>
44         implements DataTreeChangeListener<T>, AutoCloseable {
45
46     private static final InstanceIdentifier<State> II_TO_STATE
47         = InstanceIdentifier.builder(Nodes.class)
48             .child(Node.class)
49             .child(NodeConnector.class)
50             .augmentation(FlowCapableNodeConnector.class)
51             .child(State.class)
52             .build();
53
54     private static final InstanceIdentifier<FlowCapableNodeConnector> II_TO_FLOW_CAPABLE_NODE_CONNECTOR
55         = InstanceIdentifier.builder(Nodes.class)
56             .child(Node.class)
57             .child(NodeConnector.class)
58             .augmentation(FlowCapableNodeConnector.class)
59             .build();
60
61     private static final long STARTUP_LOOP_TICK = 500L;
62     private static final int STARTUP_LOOP_MAX_RETRIES = 8;
63     private static final Logger LOG = LoggerFactory.getLogger(NodeConnectorInventoryEventTranslator.class);
64
65     private final ListenerRegistration<DataTreeChangeListener> dataChangeListenerRegistration;
66     private final ListenerRegistration<DataTreeChangeListener> listenerOnPortStateRegistration;
67     private final Set<NodeConnectorEventsObserver> observers;
68     private final Map<InstanceIdentifier<?>,FlowCapableNodeConnector> iiToDownFlowCapableNodeConnectors = new HashMap<>();
69
70     public NodeConnectorInventoryEventTranslator(DataBroker dataBroker, NodeConnectorEventsObserver... observers) {
71         this.observers = ImmutableSet.copyOf(observers);
72         final DataTreeIdentifier<T> dtiToNodeConnector =
73                 new DataTreeIdentifier(LogicalDatastoreType.OPERATIONAL, II_TO_FLOW_CAPABLE_NODE_CONNECTOR);
74         final DataTreeIdentifier<T> dtiToNodeConnectorState =
75                 new DataTreeIdentifier(LogicalDatastoreType.OPERATIONAL, II_TO_STATE);
76         final SimpleTaskRetryLooper looper = new SimpleTaskRetryLooper(STARTUP_LOOP_TICK, STARTUP_LOOP_MAX_RETRIES);
77         try {
78             dataChangeListenerRegistration = looper.loopUntilNoException(new Callable<ListenerRegistration<DataTreeChangeListener>>() {
79                 @Override
80                 public ListenerRegistration<DataTreeChangeListener> call() throws Exception {
81                     return dataBroker.registerDataTreeChangeListener(dtiToNodeConnector, NodeConnectorInventoryEventTranslator.this);
82                 }
83             });
84             listenerOnPortStateRegistration = looper.loopUntilNoException(new Callable<ListenerRegistration<DataTreeChangeListener>>() {
85                 @Override
86                 public ListenerRegistration<DataTreeChangeListener> call() throws Exception {
87                     return dataBroker.registerDataTreeChangeListener(dtiToNodeConnectorState, NodeConnectorInventoryEventTranslator.this);
88                 }
89             });
90         } catch (Exception e) {
91             LOG.error("DataTreeChangeListeners registration failed: {}", e);
92             throw new IllegalStateException("NodeConnectorInventoryEventTranslator startup failed!", e);
93         }
94         LOG.info("NodeConnectorInventoryEventTranslator has started.");
95     }
96
97     @Override
98     public void close() {
99         if (dataChangeListenerRegistration != null) {
100             dataChangeListenerRegistration.close();
101         }
102         if (listenerOnPortStateRegistration != null) {
103             listenerOnPortStateRegistration.close();
104         }
105     }
106
107     @Override
108     public void onDataTreeChanged(@Nonnull Collection<DataTreeModification<T>> modifications) {
109         for(DataTreeModification modification : modifications) {
110             LOG.trace("Node connectors in inventory changed -> {}", modification.getRootNode().getModificationType());
111             switch (modification.getRootNode().getModificationType()) {
112                 case WRITE:
113                     processAddedConnector(modification);
114                     break;
115                 case SUBTREE_MODIFIED:
116                     processUpdatedConnector(modification);
117                     break;
118                 case DELETE:
119                     processRemovedConnector(modification);
120                     break;
121                 default:
122                     throw new IllegalArgumentException("Unhandled modification type: {}" +
123                             modification.getRootNode().getModificationType());
124             }
125         }
126     }
127
128     private void processAddedConnector(final DataTreeModification<T> modification) {
129         final InstanceIdentifier<T> identifier = modification.getRootPath().getRootIdentifier();
130         InstanceIdentifier<NodeConnector> nodeConnectorInstanceId =identifier.firstIdentifierOf(NodeConnector.class);
131         if (compareIITail(identifier, II_TO_FLOW_CAPABLE_NODE_CONNECTOR)) {
132             FlowCapableNodeConnector flowConnector = (FlowCapableNodeConnector) modification.getRootNode().getDataAfter();
133             if (!isPortDown(flowConnector)) {
134                 notifyNodeConnectorAppeared(nodeConnectorInstanceId, flowConnector);
135             } else {
136                 iiToDownFlowCapableNodeConnectors.put(nodeConnectorInstanceId, flowConnector);
137             }
138         }
139     }
140
141     private void processUpdatedConnector(final DataTreeModification<T> modification) {
142         final InstanceIdentifier<T> identifier = modification.getRootPath().getRootIdentifier();
143         InstanceIdentifier<NodeConnector> nodeConnectorInstanceId = identifier.firstIdentifierOf(NodeConnector.class);
144         if (compareIITail(identifier, II_TO_FLOW_CAPABLE_NODE_CONNECTOR)) {
145             FlowCapableNodeConnector flowConnector = (FlowCapableNodeConnector) modification.getRootNode().getDataAfter();
146             if (isPortDown(flowConnector)) {
147                 notifyNodeConnectorDisappeared(nodeConnectorInstanceId);
148             } else {
149                 notifyNodeConnectorAppeared(nodeConnectorInstanceId, flowConnector);
150             }
151         } else if (compareIITail(identifier, II_TO_STATE)) {
152             FlowCapableNodeConnector flowNodeConnector = iiToDownFlowCapableNodeConnectors.get(nodeConnectorInstanceId);
153             if (flowNodeConnector != null) {
154                 State state = (State) modification.getRootNode().getDataAfter();
155                 if (!state.isLinkDown()) {
156                     FlowCapableNodeConnectorBuilder flowCapableNodeConnectorBuilder =
157                             new FlowCapableNodeConnectorBuilder(flowNodeConnector);
158                     flowCapableNodeConnectorBuilder.setState(state);
159                     notifyNodeConnectorAppeared(nodeConnectorInstanceId, flowCapableNodeConnectorBuilder.build());
160                     iiToDownFlowCapableNodeConnectors.remove(nodeConnectorInstanceId);
161                 }
162             }
163         }
164     }
165
166     private void processRemovedConnector(final DataTreeModification<T> modification) {
167         final InstanceIdentifier<T> identifier = modification.getRootPath().getRootIdentifier();
168         if (compareIITail(identifier, II_TO_FLOW_CAPABLE_NODE_CONNECTOR)) {
169             InstanceIdentifier<NodeConnector> nodeConnectorInstanceId = identifier.firstIdentifierOf(NodeConnector.class);
170             notifyNodeConnectorDisappeared(nodeConnectorInstanceId);
171         }
172     }
173
174     private boolean compareIITail(final InstanceIdentifier<?> ii1, final InstanceIdentifier<?> ii2) {
175         return Iterables.getLast(ii1.getPathArguments()).equals(Iterables.getLast(ii2.getPathArguments()));
176     }
177
178     private static boolean isPortDown(final FlowCapableNodeConnector flowCapableNodeConnector) {
179         PortState portState = flowCapableNodeConnector.getState();
180         PortConfig portConfig = flowCapableNodeConnector.getConfiguration();
181         return portState != null && portState.isLinkDown()
182                 || portConfig != null && portConfig.isPORTDOWN();
183     }
184
185     private void notifyNodeConnectorAppeared(final InstanceIdentifier<NodeConnector> nodeConnectorInstanceId,
186                                              final FlowCapableNodeConnector flowConnector) {
187         for (NodeConnectorEventsObserver observer : observers) {
188             observer.nodeConnectorAdded(nodeConnectorInstanceId, flowConnector);
189         }
190     }
191
192     private void notifyNodeConnectorDisappeared(final InstanceIdentifier<NodeConnector> nodeConnectorInstanceId) {
193         for (NodeConnectorEventsObserver observer : observers) {
194             observer.nodeConnectorRemoved(nodeConnectorInstanceId);
195         }
196     }
197
198 }