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