3e2e67c9a775959a37494594ee6491bb8ca786b1
[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 javax.annotation.Nonnull;
18 import org.opendaylight.controller.md.sal.binding.api.ClusteredDataTreeChangeListener;
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 ClusteredDataTreeChangeListener<T>, AutoCloseable {
45     private static final Logger LOG = LoggerFactory.getLogger(NodeConnectorInventoryEventTranslator.class);
46
47     private static final InstanceIdentifier<State> II_TO_STATE = InstanceIdentifier.builder(Nodes.class)
48             .child(Node.class).child(NodeConnector.class).augmentation(FlowCapableNodeConnector.class)
49             .child(State.class).build();
50
51     private static final InstanceIdentifier<FlowCapableNodeConnector> II_TO_FLOW_CAPABLE_NODE_CONNECTOR
52             = InstanceIdentifier.builder(Nodes.class).child(Node.class).child(NodeConnector.class)
53             .augmentation(FlowCapableNodeConnector.class).build();
54
55     private static final long STARTUP_LOOP_TICK = 500L;
56     private static final int STARTUP_LOOP_MAX_RETRIES = 8;
57
58     private final ListenerRegistration<DataTreeChangeListener> listenerOnPortRegistration;
59     private final ListenerRegistration<DataTreeChangeListener> listenerOnPortStateRegistration;
60     private final Set<NodeConnectorEventsObserver> observers;
61     private final Map<InstanceIdentifier<?>, FlowCapableNodeConnector> iiToDownFlowCapableNodeConnectors
62             = new HashMap<>();
63
64     @SuppressWarnings("IllegalCatch")
65     public NodeConnectorInventoryEventTranslator(DataBroker dataBroker, NodeConnectorEventsObserver... observers) {
66         this.observers = ImmutableSet.copyOf(observers);
67         final DataTreeIdentifier<T> dtiToNodeConnector = new DataTreeIdentifier(LogicalDatastoreType.OPERATIONAL,
68                                                                                 II_TO_FLOW_CAPABLE_NODE_CONNECTOR);
69         final DataTreeIdentifier<T> dtiToNodeConnectorState = new DataTreeIdentifier(LogicalDatastoreType.OPERATIONAL,
70                                                                                      II_TO_STATE);
71         final SimpleTaskRetryLooper looper = new SimpleTaskRetryLooper(STARTUP_LOOP_TICK, STARTUP_LOOP_MAX_RETRIES);
72         try {
73             listenerOnPortRegistration = looper.loopUntilNoException(() ->
74                     dataBroker.registerDataTreeChangeListener(dtiToNodeConnector,
75                             NodeConnectorInventoryEventTranslator.this));
76             listenerOnPortStateRegistration = looper.loopUntilNoException(() ->
77                     dataBroker.registerDataTreeChangeListener(dtiToNodeConnectorState,
78                             NodeConnectorInventoryEventTranslator.this));
79         } catch (Exception e) {
80             LOG.error("DataTreeChangeListeners registration failed: {}", e);
81             throw new IllegalStateException("NodeConnectorInventoryEventTranslator startup failed!", e);
82         }
83         LOG.info("NodeConnectorInventoryEventTranslator has started.");
84     }
85
86     @Override
87     public void close() {
88         if (listenerOnPortRegistration != null) {
89             listenerOnPortRegistration.close();
90         }
91         if (listenerOnPortStateRegistration != null) {
92             listenerOnPortStateRegistration.close();
93         }
94     }
95
96     @Override
97     public void onDataTreeChanged(@Nonnull Collection<DataTreeModification<T>> modifications) {
98         for (DataTreeModification modification : modifications) {
99             LOG.trace("Node connectors in inventory changed -> {}", modification.getRootNode().getModificationType());
100             switch (modification.getRootNode().getModificationType()) {
101                 case WRITE:
102                     processAddedConnector(modification);
103                     break;
104                 case SUBTREE_MODIFIED:
105                     processUpdatedConnector(modification);
106                     break;
107                 case DELETE:
108                     processRemovedConnector(modification);
109                     break;
110                 default:
111                     throw new IllegalArgumentException(
112                             "Unhandled modification type: {}" + modification.getRootNode().getModificationType());
113             }
114         }
115     }
116
117     private void processAddedConnector(final DataTreeModification<T> modification) {
118         final InstanceIdentifier<T> identifier = modification.getRootPath().getRootIdentifier();
119         InstanceIdentifier<NodeConnector> nodeConnectorInstanceId = identifier.firstIdentifierOf(NodeConnector.class);
120         if (compareIITail(identifier, II_TO_FLOW_CAPABLE_NODE_CONNECTOR)) {
121             FlowCapableNodeConnector flowConnector = (FlowCapableNodeConnector) modification.getRootNode()
122                     .getDataAfter();
123             if (!isPortDown(flowConnector)) {
124                 notifyNodeConnectorAppeared(nodeConnectorInstanceId, flowConnector);
125             } else {
126                 iiToDownFlowCapableNodeConnectors.put(nodeConnectorInstanceId, flowConnector);
127             }
128         }
129     }
130
131     private void processUpdatedConnector(final DataTreeModification<T> modification) {
132         final InstanceIdentifier<T> identifier = modification.getRootPath().getRootIdentifier();
133         InstanceIdentifier<NodeConnector> nodeConnectorInstanceId = identifier.firstIdentifierOf(NodeConnector.class);
134         if (compareIITail(identifier, II_TO_FLOW_CAPABLE_NODE_CONNECTOR)) {
135             FlowCapableNodeConnector flowConnector = (FlowCapableNodeConnector) modification.getRootNode()
136                     .getDataAfter();
137             if (isPortDown(flowConnector)) {
138                 notifyNodeConnectorDisappeared(nodeConnectorInstanceId);
139             } else {
140                 notifyNodeConnectorAppeared(nodeConnectorInstanceId, flowConnector);
141             }
142         } else if (compareIITail(identifier, II_TO_STATE)) {
143             FlowCapableNodeConnector flowNodeConnector = iiToDownFlowCapableNodeConnectors.get(nodeConnectorInstanceId);
144             if (flowNodeConnector != null) {
145                 State state = (State) modification.getRootNode().getDataAfter();
146                 if (!state.isLinkDown()) {
147                     FlowCapableNodeConnectorBuilder flowCapableNodeConnectorBuilder
148                             = new FlowCapableNodeConnectorBuilder(flowNodeConnector);
149                     flowCapableNodeConnectorBuilder.setState(state);
150                     notifyNodeConnectorAppeared(nodeConnectorInstanceId, flowCapableNodeConnectorBuilder.build());
151                     iiToDownFlowCapableNodeConnectors.remove(nodeConnectorInstanceId);
152                 }
153             }
154         }
155     }
156
157     private void processRemovedConnector(final DataTreeModification<T> modification) {
158         final InstanceIdentifier<T> identifier = modification.getRootPath().getRootIdentifier();
159         if (compareIITail(identifier, II_TO_FLOW_CAPABLE_NODE_CONNECTOR)) {
160             InstanceIdentifier<NodeConnector> nodeConnectorInstanceId = identifier
161                     .firstIdentifierOf(NodeConnector.class);
162             notifyNodeConnectorDisappeared(nodeConnectorInstanceId);
163         }
164     }
165
166     private boolean compareIITail(final InstanceIdentifier<?> ii1, final InstanceIdentifier<?> ii2) {
167         return Iterables.getLast(ii1.getPathArguments()).equals(Iterables.getLast(ii2.getPathArguments()));
168     }
169
170     private static boolean isPortDown(final FlowCapableNodeConnector flowCapableNodeConnector) {
171         PortState portState = flowCapableNodeConnector.getState();
172         PortConfig portConfig = flowCapableNodeConnector.getConfiguration();
173         return portState != null && portState.isLinkDown() || portConfig != null && portConfig.isPORTDOWN();
174     }
175
176     private void notifyNodeConnectorAppeared(final InstanceIdentifier<NodeConnector> nodeConnectorInstanceId,
177                                              final FlowCapableNodeConnector flowConnector) {
178         for (NodeConnectorEventsObserver observer : observers) {
179             observer.nodeConnectorAdded(nodeConnectorInstanceId, flowConnector);
180         }
181     }
182
183     private void notifyNodeConnectorDisappeared(final InstanceIdentifier<NodeConnector> nodeConnectorInstanceId) {
184         for (NodeConnectorEventsObserver observer : observers) {
185             observer.nodeConnectorRemoved(nodeConnectorInstanceId);
186         }
187     }
188 }