Merge "L2GW: Updated to support dynamic port additions to TOR and stale entry cleanup"
[netvirt.git] / vpnservice / elanmanager / elanmanager-impl / src / main / java / org / opendaylight / netvirt / elan / l2gw / listeners / HwvtepTerminationPointListener.java
1 /*
2  * Copyright (c) 2016 Ericsson India Global Services Pvt Ltd. 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.netvirt.elan.l2gw.listeners;
9
10 import com.google.common.collect.Lists;
11 import com.google.common.util.concurrent.ListenableFuture;
12
13 import org.opendaylight.controller.md.sal.binding.api.ClusteredDataChangeListener;
14 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
15 import org.opendaylight.controller.md.sal.binding.api.DataChangeListener;
16 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
17 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
18 import org.opendaylight.genius.datastoreutils.AsyncClusteredDataChangeListenerBase;
19 import org.opendaylight.netvirt.elan.l2gw.utils.ElanL2GatewayUtils;
20 import org.opendaylight.netvirt.elan.l2gw.utils.L2GatewayConnectionUtils;
21 import org.opendaylight.netvirt.elan.utils.ElanClusterUtils;
22 import org.opendaylight.netvirt.neutronvpn.api.l2gw.L2GatewayDevice;
23 import org.opendaylight.netvirt.neutronvpn.api.l2gw.utils.L2GatewayCacheUtils;
24 import org.opendaylight.genius.utils.hwvtep.HwvtepSouthboundConstants;
25 import org.opendaylight.genius.utils.hwvtep.HwvtepSouthboundUtils;
26 import org.opendaylight.genius.utils.hwvtep.HwvtepUtils;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l2gateways.rev150712.l2gateway.attributes.Devices;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l2gateways.rev150712.l2gateway.attributes.devices.Interfaces;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l2gateways.rev150712.l2gateway.connections.attributes.l2gatewayconnections.L2gatewayConnection;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l2gateways.rev150712.l2gateways.attributes.l2gateways.L2gateway;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.HwvtepPhysicalPortAugmentation;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.PhysicalSwitchAugmentation;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.physical.port.attributes.VlanBindings;
34 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;
35 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
36 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
37 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey;
38 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
39 import org.opendaylight.yangtools.concepts.ListenerRegistration;
40 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
41 import org.slf4j.Logger;
42 import org.slf4j.LoggerFactory;
43 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPoint;
44
45 import java.util.ArrayList;
46 import java.util.Collections;
47 import java.util.List;
48 import java.util.Map;
49 import java.util.concurrent.Callable;
50 import java.util.concurrent.ConcurrentHashMap;
51
52
53 /**
54  * Listener for physical locator presence in operational datastore
55  *
56  *
57  *
58  */
59 public class HwvtepTerminationPointListener extends
60         AsyncClusteredDataChangeListenerBase<TerminationPoint, HwvtepTerminationPointListener> implements AutoCloseable {
61
62     private DataBroker broker;
63     private ListenerRegistration<DataChangeListener> lstnerRegistration;
64
65     private static final Logger logger = LoggerFactory.getLogger(HwvtepTerminationPointListener.class);
66
67     public HwvtepTerminationPointListener(DataBroker broker) {
68         super(TerminationPoint.class, HwvtepTerminationPointListener.class);
69
70         this.broker = broker;
71         registerListener();
72         logger.debug("created HwvtepTerminationPointListener");
73     }
74
75     static Map<InstanceIdentifier<TerminationPoint>, List<Runnable>> waitingJobsList = new ConcurrentHashMap<>();
76     static Map<InstanceIdentifier<TerminationPoint>, Boolean> teps = new ConcurrentHashMap<>();
77
78     public static void runJobAfterPhysicalLocatorIsAvialable(InstanceIdentifier<TerminationPoint> key, Runnable runnable) {
79         if (teps.get(key) != null) {
80             logger.debug("physical locator already available {} running job ", key);
81             runnable.run();
82             return;
83         }
84         synchronized (HwvtepTerminationPointListener.class) {
85             List<Runnable> list = waitingJobsList.get(key);
86             if (list == null) {
87                 waitingJobsList.put(key, Lists.newArrayList(runnable));
88             } else {
89                 list.add(runnable);
90             }
91             logger.debug("added the job to wait list of physical locator {}", key);
92         }
93     }
94
95     protected void registerListener() {
96         try {
97             lstnerRegistration = this.broker.registerDataChangeListener(LogicalDatastoreType.OPERATIONAL,
98                     InstanceIdentifier.create(NetworkTopology.class).child(Topology.class,
99                             new TopologyKey(HwvtepSouthboundConstants.HWVTEP_TOPOLOGY_ID)).child(Node.class).
100                     child(TerminationPoint.class), this, DataChangeScope.BASE);
101         } catch (final Exception e) {
102             logger.error("Hwvtep LocalUcasMacs DataChange listener registration failed !", e);
103             throw new IllegalStateException("Hwvtep LocalUcasMacs DataChange listener registration failed .", e);
104         }
105     }
106
107     @Override
108     public void close() throws Exception {
109         if (lstnerRegistration != null) {
110             try {
111                 lstnerRegistration.close();
112             } catch (final Exception e) {
113                 logger.error("Error when cleaning up DataChangeListener.", e);
114             }
115             lstnerRegistration = null;
116         }
117     }
118
119     @Override
120     protected void remove(InstanceIdentifier<TerminationPoint> identifier, TerminationPoint del) {
121         logger.trace("physical locator removed {}", identifier);
122         teps.remove(identifier);
123     }
124
125     @Override
126     protected void update(InstanceIdentifier<TerminationPoint> identifier, TerminationPoint original, TerminationPoint update) {
127         logger.trace("physical locator available {}", identifier);
128     }
129
130     @Override
131     protected void add(InstanceIdentifier<TerminationPoint> identifier, final TerminationPoint add) {
132         final HwvtepPhysicalPortAugmentation portAugmentation =
133                 add.getAugmentation(HwvtepPhysicalPortAugmentation.class);
134         if (portAugmentation != null) {
135             final NodeId nodeId = identifier.firstIdentifierOf(Node.class).firstKeyOf(Node.class).getNodeId();
136             ElanClusterUtils.runOnlyInLeaderNode(HwvtepSouthboundConstants.ELAN_ENTITY_NAME,
137                     "handling Physical Switch add", new Callable<List<ListenableFuture<Void>>>() {
138                         @Override
139                         public List<ListenableFuture<Void>> call() throws Exception {
140                             return handlePortAdded(portAugmentation, add, nodeId);
141                         }
142                     });
143             return;
144         }
145
146         logger.trace("physical locator available {}", identifier);
147         teps.put(identifier, true);
148         List<Runnable> runnableList = null;
149         synchronized (HwvtepTerminationPointListener.class) {
150             runnableList = waitingJobsList.get(identifier);
151             waitingJobsList.remove(identifier);
152         }
153         if (runnableList != null) {
154             logger.debug("physical locator available {} running jobs ", identifier);
155             for (Runnable r : runnableList) {
156                 r.run();
157             }
158         } else {
159             logger.debug("no jobs are waiting for physical locator {}", identifier);
160         }
161     }
162
163     @Override
164     protected InstanceIdentifier<TerminationPoint> getWildCardPath() {
165         return InstanceIdentifier.create(NetworkTopology.class).child(Topology.class).child(Node.class).
166                 child(TerminationPoint.class);
167     }
168
169     @Override
170     protected ClusteredDataChangeListener getDataChangeListener() {
171         return HwvtepTerminationPointListener.this;
172     }
173
174     @Override
175     protected DataChangeScope getDataChangeScope() {
176         return DataChangeScope.BASE;
177     }
178
179     private List<ListenableFuture<Void>> handlePortAdded(HwvtepPhysicalPortAugmentation portAugmentation,
180             TerminationPoint portAdded, NodeId psNodeId) {
181         Node psNode = HwvtepUtils.getHwVtepNode(broker, LogicalDatastoreType.OPERATIONAL, psNodeId);
182         if (psNode != null) {
183             String psName = psNode.getAugmentation(PhysicalSwitchAugmentation.class).getHwvtepNodeName().getValue();
184             L2GatewayDevice l2GwDevice = L2GatewayCacheUtils.getL2DeviceFromCache(psName);
185             if (l2GwDevice != null) {
186                 if (isL2GatewayConfigured(l2GwDevice)) {
187                     List<L2gatewayConnection> l2GwConns = L2GatewayConnectionUtils.getAssociatedL2GwConnections(broker,
188                             l2GwDevice.getL2GatewayIds());
189                     if (l2GwConns != null) {
190                         String newPortId = portAdded.getTpId().getValue();
191                         NodeId hwvtepNodeId = new NodeId(l2GwDevice.getHwvtepNodeId());
192                         List<VlanBindings> vlanBindings = getVlanBindings(l2GwConns, hwvtepNodeId, psName, newPortId);
193                         List<ListenableFuture<Void>> futures = new ArrayList<>();
194                         futures.add(ElanL2GatewayUtils.updateVlanBindingsInL2GatewayDevice(hwvtepNodeId, psName,
195                                 newPortId, vlanBindings));
196                         return futures;
197                     }
198                 }
199             } else {
200                 logger.error("{} details are not present in L2Gateway Cache", psName);
201             }
202         } else {
203             logger.error("{} entry not in config datastore", psNodeId);
204         }
205         return Collections.emptyList();
206     }
207
208     private List<VlanBindings> getVlanBindings(List<L2gatewayConnection> l2GwConns, NodeId hwvtepNodeId, String psName,
209             String newPortId) {
210         List<VlanBindings> vlanBindings = new ArrayList<>();
211         for (L2gatewayConnection l2GwConn : l2GwConns) {
212             L2gateway l2Gateway = L2GatewayConnectionUtils.getNeutronL2gateway(broker, l2GwConn.getL2gatewayId());
213             if (l2Gateway == null) {
214                 logger.error("L2Gateway with id {} is not present", l2GwConn.getL2gatewayId().getValue());
215             } else {
216                 String logicalSwitchName = ElanL2GatewayUtils.getLogicalSwitchFromElan(
217                         l2GwConn.getNetworkId().getValue());
218                 List<Devices> l2Devices = l2Gateway.getDevices();
219                 for (Devices l2Device : l2Devices) {
220                     String l2DeviceName = l2Device.getDeviceName();
221                     if (l2DeviceName != null && l2DeviceName.equals(psName)) {
222                         for (Interfaces deviceInterface : l2Device.getInterfaces()) {
223                             if (deviceInterface.getInterfaceName().equals(newPortId)) {
224                                 if (deviceInterface.getSegmentationIds() != null &&
225                                         !deviceInterface.getSegmentationIds().isEmpty()) {
226                                     for (Integer vlanId : deviceInterface.getSegmentationIds()) {
227                                         vlanBindings.add(HwvtepSouthboundUtils.createVlanBinding(hwvtepNodeId, vlanId,
228                                                 logicalSwitchName));
229                                     }
230                                 } else {
231                                     // Use defaultVlanId (specified in L2GatewayConnection) if Vlan
232                                     // ID not specified at interface level.
233                                     Integer segmentationId = l2GwConn.getSegmentId();
234                                     int defaultVlanId = (segmentationId != null) ? segmentationId : 0;
235                                     vlanBindings.add(HwvtepSouthboundUtils.createVlanBinding(hwvtepNodeId,
236                                             defaultVlanId, logicalSwitchName));
237                                 }
238                             }
239                         }
240                     }
241                 }
242             }
243         }
244         return vlanBindings;
245     }
246
247     private boolean isL2GatewayConfigured(L2GatewayDevice l2GwDevice) {
248         return (l2GwDevice.getHwvtepNodeId() != null && l2GwDevice.isConnected() &&
249                 l2GwDevice.getL2GatewayIds().size() > 0 && l2GwDevice.getTunnelIp() != null);
250     }
251 }