BUG 3548 - LLDP speaker doesn't start send LLDP packet on port up.
[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.Iterables;
12
13 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnectorBuilder;
14 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.port.rev130925.flow.capable.port.State;
15 import java.util.HashMap;
16 import com.google.common.collect.ImmutableSet;
17 import java.util.Map;
18 import java.util.Set;
19 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
20 import org.opendaylight.controller.md.sal.binding.api.DataChangeListener;
21 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker;
22 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
23 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
24 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnector;
25 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.port.rev130925.PortConfig;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.port.rev130925.PortState;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
30 import org.opendaylight.yangtools.concepts.ListenerRegistration;
31 import org.opendaylight.yangtools.yang.binding.DataObject;
32 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
33 import org.slf4j.Logger;
34 import org.slf4j.LoggerFactory;
35
36
37 /**
38  * NodeConnectorInventoryEventTranslator is listening for changes in inventory operational DOM tree
39  * and update LLDPSpeaker and topology.
40  */
41 public class NodeConnectorInventoryEventTranslator implements DataChangeListener, AutoCloseable {
42     /**
43      *
44      */
45     private static final InstanceIdentifier<State> II_TO_STATE 
46         = InstanceIdentifier.builder(Nodes.class)
47             .child(Node.class)
48             .child(NodeConnector.class)
49             .augmentation(FlowCapableNodeConnector.class)
50             .child(State.class)
51             .build();
52
53     private static final InstanceIdentifier<FlowCapableNodeConnector> II_TO_FLOW_CAPABLE_NODE_CONNECTOR
54         = InstanceIdentifier.builder(Nodes.class)
55             .child(Node.class)
56             .child(NodeConnector.class)
57             .augmentation(FlowCapableNodeConnector.class)
58             .build();
59
60     private static final Logger LOG = LoggerFactory.getLogger(NodeConnectorInventoryEventTranslator.class);
61
62     private final ListenerRegistration<DataChangeListener> dataChangeListenerRegistration;
63     private final ListenerRegistration<DataChangeListener> listenerOnPortStateRegistration;
64     private final Set<NodeConnectorEventsObserver> observers;
65     private final Map<InstanceIdentifier<?>,FlowCapableNodeConnector> iiToDownFlowCapableNodeConnectors = new HashMap<>();
66
67     public NodeConnectorInventoryEventTranslator(DataBroker dataBroker, NodeConnectorEventsObserver... observers) {
68         this.observers = ImmutableSet.copyOf(observers);
69         dataChangeListenerRegistration = dataBroker.registerDataChangeListener(
70                 LogicalDatastoreType.OPERATIONAL,
71                 II_TO_FLOW_CAPABLE_NODE_CONNECTOR,
72                 this, AsyncDataBroker.DataChangeScope.BASE);
73         listenerOnPortStateRegistration = dataBroker.registerDataChangeListener(
74                 LogicalDatastoreType.OPERATIONAL,
75                 II_TO_STATE,
76                 this, AsyncDataBroker.DataChangeScope.SUBTREE);
77     }
78
79     @Override
80     public void close() {
81         dataChangeListenerRegistration.close();
82         listenerOnPortStateRegistration.close();
83     }
84
85     @Override
86     public void onDataChanged(AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> change) {
87         LOG.trace("Node connectors in inventory changed: {} created, {} updated, {} removed",
88                 change.getCreatedData().size(), change.getUpdatedData().size(), change.getRemovedPaths().size());
89
90         // Iterate over created node connectors
91         for (Map.Entry<InstanceIdentifier<?>, DataObject> entry : change.getCreatedData().entrySet()) {
92             InstanceIdentifier<NodeConnector> nodeConnectorInstanceId =
93                     entry.getKey().firstIdentifierOf(NodeConnector.class);
94             if (compareIITail(entry.getKey(),II_TO_FLOW_CAPABLE_NODE_CONNECTOR)) {
95                 FlowCapableNodeConnector flowConnector = (FlowCapableNodeConnector) entry.getValue();
96                 if (!isPortDown(flowConnector)) {
97                     notifyNodeConnectorAppeared(nodeConnectorInstanceId, flowConnector);
98                 } else {
99                     iiToDownFlowCapableNodeConnectors.put(nodeConnectorInstanceId, flowConnector);
100                 }
101             }
102         }
103
104         // Iterate over updated node connectors (port down state may change)
105         for (Map.Entry<InstanceIdentifier<?>, DataObject> entry : change.getUpdatedData().entrySet()) {
106             InstanceIdentifier<NodeConnector> nodeConnectorInstanceId =
107                     entry.getKey().firstIdentifierOf(NodeConnector.class);
108             if (compareIITail(entry.getKey(),II_TO_FLOW_CAPABLE_NODE_CONNECTOR)) {
109                 FlowCapableNodeConnector flowConnector = (FlowCapableNodeConnector) entry.getValue();
110                 if (isPortDown(flowConnector)) {
111                     notifyNodeConnectorDisappeared(nodeConnectorInstanceId);
112                 } else {
113                     notifyNodeConnectorAppeared(nodeConnectorInstanceId, flowConnector);
114                 }
115             } else if (compareIITail(entry.getKey(),II_TO_STATE)) {
116                 FlowCapableNodeConnector flowNodeConnector = iiToDownFlowCapableNodeConnectors.get(nodeConnectorInstanceId);
117                 if (flowNodeConnector != null) {
118                     State state = (State)entry.getValue();
119                     if (!state.isLinkDown()) {
120                         FlowCapableNodeConnectorBuilder flowCapableNodeConnectorBuilder = new FlowCapableNodeConnectorBuilder(flowNodeConnector);
121                         flowCapableNodeConnectorBuilder.setState(state);
122                         notifyNodeConnectorAppeared(nodeConnectorInstanceId, flowCapableNodeConnectorBuilder.build());
123                         iiToDownFlowCapableNodeConnectors.remove(nodeConnectorInstanceId);
124                     }
125                 }
126             }
127         }
128
129         // Iterate over removed node connectors
130         for (InstanceIdentifier<?> removed : change.getRemovedPaths()) {
131             if (compareIITail(removed,II_TO_FLOW_CAPABLE_NODE_CONNECTOR)) {
132                 InstanceIdentifier<NodeConnector> nodeConnectorInstanceId = removed.firstIdentifierOf(NodeConnector.class);
133                 notifyNodeConnectorDisappeared(nodeConnectorInstanceId);
134             }
135         }
136     }
137
138     /**
139      * @param key
140      * @param iiToFlowCapableNodeConnector
141      * @return
142      */
143     private boolean compareIITail(InstanceIdentifier<?> ii1,
144             InstanceIdentifier<?> ii2) {
145         return Iterables.getLast(ii1.getPathArguments()).equals(Iterables.getLast(ii2.getPathArguments()));
146     }
147
148     private static boolean isPortDown(FlowCapableNodeConnector flowCapableNodeConnector) {
149         PortState portState = flowCapableNodeConnector.getState();
150         PortConfig portConfig = flowCapableNodeConnector.getConfiguration();
151         return portState != null && portState.isLinkDown() ||
152                 portConfig != null && portConfig.isPORTDOWN();
153     }
154
155     private void notifyNodeConnectorAppeared(InstanceIdentifier<NodeConnector> nodeConnectorInstanceId,
156                                              FlowCapableNodeConnector flowConnector) {
157         for (NodeConnectorEventsObserver observer : observers) {
158             observer.nodeConnectorAdded(nodeConnectorInstanceId, flowConnector);
159         }
160     }
161
162     private void notifyNodeConnectorDisappeared(InstanceIdentifier<NodeConnector> nodeConnectorInstanceId) {
163         for (NodeConnectorEventsObserver observer : observers) {
164             observer.nodeConnectorRemoved(nodeConnectorInstanceId);
165         }
166     }
167 }