2 * Copyright (c) 2016 - 2018 Ericsson India Global Services Pvt Ltd. and others. All rights reserved.
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
8 package org.opendaylight.netvirt.natservice.internal;
10 import static org.opendaylight.genius.infra.Datastore.CONFIGURATION;
11 import static org.opendaylight.genius.infra.Datastore.OPERATIONAL;
13 import com.google.common.base.Optional;
14 import com.google.common.collect.HashBasedTable;
15 import com.google.common.collect.Table;
16 import com.google.common.util.concurrent.FluentFuture;
17 import com.google.common.util.concurrent.ListenableFuture;
18 import com.google.common.util.concurrent.MoreExecutors;
19 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
20 import java.util.ArrayList;
21 import java.util.Collections;
22 import java.util.List;
23 import java.util.concurrent.Callable;
24 import java.util.concurrent.ExecutionException;
25 import java.util.concurrent.locks.ReentrantLock;
26 import javax.inject.Inject;
27 import javax.inject.Singleton;
28 import org.eclipse.jdt.annotation.NonNull;
29 import org.eclipse.jdt.annotation.Nullable;
30 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
31 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
32 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
33 import org.opendaylight.genius.datastoreutils.SingleTransactionDataBroker;
34 import org.opendaylight.genius.infra.Datastore.Operational;
35 import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
36 import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
37 import org.opendaylight.genius.infra.TypedReadWriteTransaction;
38 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
39 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
40 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.OdlInterfaceRpcService;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.router.interfaces.RouterInterface;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.floating.ip.info.RouterPorts;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.floating.ip.info.router.ports.Ports;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.floating.ip.info.router.ports.ports.InternalToExternalPortMap;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.neutron.vip.states.VipState;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.NeutronvpnService;
48 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
49 import org.opendaylight.yangtools.yang.common.Uint64;
50 import org.slf4j.Logger;
51 import org.slf4j.LoggerFactory;
54 public class NatSouthboundEventHandlers {
56 private static final Logger LOG = LoggerFactory.getLogger(NatSouthboundEventHandlers.class);
57 private static final String NAT_DS = "NATDS";
58 private final DataBroker dataBroker;
59 private final ManagedNewTransactionRunner txRunner;
60 private final OdlInterfaceRpcService odlInterfaceRpcService;
61 private final JobCoordinator coordinator;
62 private final FloatingIPListener floatingIPListener;
63 private final NeutronvpnService neutronVpnService;
64 private final IMdsalApiManager mdsalManager;
65 private final NaptManager naptManager;
66 private final VipStateTracker vipStateTracker;
67 Table<Interface.OperStatus, Interface.OperStatus, IntfTransitionState> stateTable = HashBasedTable.create();
69 enum IntfTransitionState {
75 private void initialize() {
76 stateTable.put(Interface.OperStatus.Up, Interface.OperStatus.Down, IntfTransitionState.STATE_DOWN);
77 stateTable.put(Interface.OperStatus.Down, Interface.OperStatus.Up, IntfTransitionState.STATE_UP);
78 stateTable.put(Interface.OperStatus.Unknown, Interface.OperStatus.Up, IntfTransitionState.STATE_UP);
79 stateTable.put(Interface.OperStatus.Unknown, Interface.OperStatus.Down, IntfTransitionState.STATE_DOWN);
80 stateTable.put(Interface.OperStatus.Up, Interface.OperStatus.Unknown, IntfTransitionState.STATE_DOWN);
84 public NatSouthboundEventHandlers(final DataBroker dataBroker,
85 final OdlInterfaceRpcService odlInterfaceRpcService, final JobCoordinator coordinator,
86 final FloatingIPListener floatingIPListener,final NeutronvpnService neutronvpnService,
87 final IMdsalApiManager mdsalManager, final NaptManager naptManager, final VipStateTracker vipStateTracker) {
88 this.dataBroker = dataBroker;
89 this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
90 this.odlInterfaceRpcService = odlInterfaceRpcService;
91 this.coordinator = coordinator;
92 this.floatingIPListener = floatingIPListener;
93 this.neutronVpnService = neutronvpnService;
94 this.mdsalManager = mdsalManager;
95 this.naptManager = naptManager;
96 this.vipStateTracker = vipStateTracker;
100 public void handleAdd(String interfaceName, Uint64 intfDpnId, RouterInterface routerInterface) {
101 handleAdd(interfaceName, intfDpnId, routerInterface, null);
104 public void handleAdd(String interfaceName, Uint64 intfDpnId,
105 RouterInterface routerInterface, @Nullable VipState vipState) {
106 String routerName = routerInterface.getRouterName();
107 if (NatUtil.validateIsIntefacePartofRouter(dataBroker, routerName, interfaceName)) {
108 NatInterfaceStateAddWorker natIfaceStateAddWorker = new NatInterfaceStateAddWorker(
110 intfDpnId, routerName);
111 coordinator.enqueueJob(NAT_DS + "-" + interfaceName, natIfaceStateAddWorker);
113 NatFlowAddWorker natFlowAddWorker = new NatFlowAddWorker(interfaceName, routerName,
114 intfDpnId, vipState);
115 coordinator.enqueueJob(NAT_DS + "-" + interfaceName, natFlowAddWorker,
116 NatConstants.NAT_DJC_MAX_RETRIES);
118 LOG.error("NAT Service : Router {} not valid for interface {} during add. Deleting it from "
119 + "router-interfaces DS", routerName, routerInterface);
121 SingleTransactionDataBroker.syncDelete(dataBroker, LogicalDatastoreType.CONFIGURATION,
122 NatUtil.getRouterInterfaceId(interfaceName));
123 } catch (TransactionCommitFailedException e) {
124 LOG.error("NAT Service : Failed to stale Router Interface entry for interface {}",
130 public void handleRemove(String interfaceName, Uint64 intfDpnId, RouterInterface routerInterface) {
131 String routerName = routerInterface.getRouterName();
132 NatInterfaceStateRemoveWorker natIfaceStateRemoveWorker = new NatInterfaceStateRemoveWorker(interfaceName,
133 intfDpnId, routerName);
134 coordinator.enqueueJob(NAT_DS + "-" + interfaceName, natIfaceStateRemoveWorker);
136 NatFlowRemoveWorker natFlowRemoveWorker = new NatFlowRemoveWorker(interfaceName, intfDpnId, routerName);
137 coordinator.enqueueJob(NAT_DS + "-" + interfaceName, natFlowRemoveWorker,
138 NatConstants.NAT_DJC_MAX_RETRIES);
139 // Validate whether the routerInterface is still part of given router in router-interface-map
140 if (!NatUtil.validateIsIntefacePartofRouter(dataBroker, routerName, interfaceName)) {
141 LOG.error("NAT Service : Router {} not valid for interface {} during remove. "
142 + "Deleting it from router-interfaces DS", routerName, routerInterface);
144 SingleTransactionDataBroker.syncDelete(dataBroker, LogicalDatastoreType.CONFIGURATION,
145 NatUtil.getRouterInterfaceId(interfaceName));
146 } catch (TransactionCommitFailedException e) {
147 LOG.error("NAT Service : Failed to stale Router Interface entry for interface {}",
153 public void handleUpdate(Interface original, Interface update,
154 Uint64 intfDpnId, RouterInterface routerInterface) {
155 String routerName = routerInterface.getRouterName();
156 if (NatUtil.validateIsIntefacePartofRouter(dataBroker, routerName, update.getName())) {
157 NatInterfaceStateUpdateWorker natIfaceStateupdateWorker = new NatInterfaceStateUpdateWorker(
159 update, intfDpnId, routerName);
160 coordinator.enqueueJob(NAT_DS + "-" + update.getName(), natIfaceStateupdateWorker);
161 NatFlowUpdateWorker natFlowUpdateWorker = new NatFlowUpdateWorker(original, update,
163 coordinator.enqueueJob(NAT_DS + "-" + update.getName(), natFlowUpdateWorker,
164 NatConstants.NAT_DJC_MAX_RETRIES);
168 void handleRouterInterfacesUpEvent(String routerName, String interfaceName, Uint64 dpId,
169 TypedReadWriteTransaction<Operational> operTx) throws ExecutionException, InterruptedException {
170 LOG.debug("handleRouterInterfacesUpEvent : Handling UP event for router interface {} in Router {} on Dpn {}",
171 interfaceName, routerName, dpId);
172 NatUtil.addToNeutronRouterDpnsMap(routerName, interfaceName, dpId, operTx);
173 NatUtil.addToDpnRoutersMap(routerName, interfaceName, dpId, operTx);
176 void handleRouterInterfacesDownEvent(String routerName, String interfaceName, Uint64 dpnId,
177 TypedReadWriteTransaction<Operational> operTx)
178 throws ExecutionException, InterruptedException {
179 LOG.debug("handleRouterInterfacesDownEvent : Handling DOWN event for router Interface {} in Router {}",
180 interfaceName, routerName);
181 NatUtil.removeFromNeutronRouterDpnsMap(routerName, dpnId, operTx);
182 NatUtil.removeFromDpnRoutersMap(dataBroker, routerName, interfaceName, dpnId, odlInterfaceRpcService,
186 @SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD",
187 justification = "https://github.com/spotbugs/spotbugs/issues/811")
188 private IntfTransitionState getTransitionState(Interface.OperStatus original , Interface.OperStatus updated) {
189 IntfTransitionState transitionState = stateTable.get(original, updated);
191 if (transitionState == null) {
192 return IntfTransitionState.STATE_IGNORE;
194 return transitionState;
197 private class NatInterfaceStateAddWorker implements Callable<List<ListenableFuture<Void>>> {
198 private final String interfaceName;
199 private final String routerName;
200 private final Uint64 intfDpnId;
202 NatInterfaceStateAddWorker(String interfaceName, Uint64 intfDpnId, String routerName) {
203 this.interfaceName = interfaceName;
204 this.routerName = routerName;
205 this.intfDpnId = intfDpnId;
209 @SuppressWarnings("checkstyle:IllegalCatch")
210 public List<ListenableFuture<Void>> call() {
211 LOG.trace("call : Received interface {} PORT UP OR ADD event ", interfaceName);
212 List<ListenableFuture<Void>> futures = new ArrayList<>();
213 final ReentrantLock lock = NatUtil.lockForNat(intfDpnId);
216 futures.add(txRunner.callWithNewReadWriteTransactionAndSubmit(OPERATIONAL, tx ->
217 handleRouterInterfacesUpEvent(routerName, interfaceName, intfDpnId, tx)));
218 } catch (Exception e) {
219 LOG.error("call : Exception caught in Interface {} Operational State Up event",
228 private class NatInterfaceStateRemoveWorker implements Callable<List<ListenableFuture<Void>>> {
229 private final String interfaceName;
230 private final String routerName;
231 private final Uint64 intfDpnId;
233 NatInterfaceStateRemoveWorker(String interfaceName, Uint64 intfDpnId, String routerName) {
234 this.interfaceName = interfaceName;
235 this.routerName = routerName;
236 this.intfDpnId = intfDpnId;
240 @SuppressWarnings("checkstyle:IllegalCatch")
241 public List<ListenableFuture<Void>> call() {
242 LOG.trace("call : Received interface {} PORT DOWN or REMOVE event", interfaceName);
243 List<ListenableFuture<Void>> futures = new ArrayList<>();
244 final ReentrantLock lock = NatUtil.lockForNat(intfDpnId);
247 futures.add(txRunner.callWithNewReadWriteTransactionAndSubmit(OPERATIONAL, tx ->
248 handleRouterInterfacesDownEvent(routerName, interfaceName, intfDpnId, tx)));
249 } catch (Exception e) {
250 LOG.error("call : Exception observed in handling deletion of VPN Interface {}.", interfaceName, e);
258 private class NatInterfaceStateUpdateWorker implements Callable<List<ListenableFuture<Void>>> {
259 private final Interface original;
260 private final Interface update;
261 private final Uint64 intfDpnId;
262 private final String routerName;
264 NatInterfaceStateUpdateWorker(Interface original, Interface update, Uint64 intfDpnId, String routerName) {
265 this.original = original;
266 this.update = update;
267 this.intfDpnId = intfDpnId;
268 this.routerName = routerName;
272 @SuppressWarnings("checkstyle:IllegalCatch")
273 public List<ListenableFuture<Void>> call() {
274 final String interfaceName = update.getName();
275 LOG.trace("call : Received interface {} state change event", interfaceName);
276 LOG.debug("call : DPN ID {} for the interface {} ", intfDpnId, interfaceName);
278 List<ListenableFuture<Void>> futures = new ArrayList<>();
279 final ReentrantLock lock = NatUtil.lockForNat(intfDpnId);
282 IntfTransitionState state = getTransitionState(original.getOperStatus(), update.getOperStatus());
283 if (state.equals(IntfTransitionState.STATE_IGNORE)) {
284 LOG.info("NAT Service: Interface {} state original {} updated {} not handled",
285 interfaceName, original.getOperStatus(), update.getOperStatus());
288 futures.add(txRunner.callWithNewReadWriteTransactionAndSubmit(OPERATIONAL, tx -> {
289 if (state.equals(IntfTransitionState.STATE_DOWN)) {
290 LOG.debug("call : DPN {} connnected to the interface {} has gone down."
291 + "Hence clearing the dpn-vpninterfaces-list entry from the"
292 + " neutron-router-dpns model in the ODL:L3VPN", intfDpnId, interfaceName);
293 // If the interface state is unknown, it means that the corresponding DPN has gone down.
294 // So remove the dpn-vpninterfaces-list from the neutron-router-dpns model.
295 NatUtil.removeFromNeutronRouterDpnsMap(routerName, interfaceName,
297 } else if (state.equals(IntfTransitionState.STATE_UP)) {
298 LOG.debug("call : DPN {} connnected to the interface {} has come up. Hence adding"
299 + " the dpn-vpninterfaces-list entry from the neutron-router-dpns model"
300 + " in the ODL:L3VPN", intfDpnId, interfaceName);
301 handleRouterInterfacesUpEvent(routerName, interfaceName, intfDpnId, tx);
304 } catch (Exception e) {
305 LOG.error("call : Exception observed in handling updation of VPN Interface {}.", update.getName(), e);
313 @SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD",
314 justification = "https://github.com/spotbugs/spotbugs/issues/811")
315 private void processInterfaceAdded(String portName, String routerId, Uint64 dpnId, VipState vipState) {
316 LOG.trace("processInterfaceAdded : Processing Interface Add Event for interface {}", portName);
317 List<InternalToExternalPortMap> intExtPortMapList = getIntExtPortMapListForPortName(portName, routerId);
318 if (intExtPortMapList.isEmpty()) {
319 LOG.debug("processInterfaceAdded : Ip Mapping list is empty/null for portname {}", portName);
322 InstanceIdentifier<RouterPorts> portIid = NatUtil.buildRouterPortsIdentifier(routerId);
323 FluentFuture<Void> future = txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION, tx -> {
324 for (InternalToExternalPortMap intExtPortMap : intExtPortMapList) {
325 floatingIPListener.createNATFlowEntries(portName, intExtPortMap, portIid, routerId, dpnId, tx);
328 future.transform((ignored) -> {
329 if (vipState != null) {
330 return this.vipStateTracker.writeVipState(vipState);
333 }, MoreExecutors.directExecutor());
337 private List<InternalToExternalPortMap> getIntExtPortMapListForPortName(String portName, String routerId) {
338 InstanceIdentifier<Ports> portToIpMapIdentifier = NatUtil.buildPortToIpMapIdentifier(routerId, portName);
339 Optional<Ports> port =
340 SingleTransactionDataBroker.syncReadOptionalAndTreatReadFailedExceptionAsAbsentOptional(dataBroker,
341 LogicalDatastoreType.CONFIGURATION, portToIpMapIdentifier);
342 if (!port.isPresent()) {
343 LOG.info("getIntExtPortMapListForPortName : Unable to read router port entry for router ID {} "
344 + "and port name {}", routerId, portName);
345 return Collections.emptyList();
347 return port.get().nonnullInternalToExternalPortMap();
350 @SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD",
351 justification = "https://github.com/spotbugs/spotbugs/issues/811")
352 private void processInterfaceRemoved(String portName, Uint64 dpnId, String routerId,
353 List<ListenableFuture<Void>> futures) {
354 LOG.trace("processInterfaceRemoved : Processing Interface Removed Event for interface {} on DPN ID {}",
356 List<InternalToExternalPortMap> intExtPortMapList = getIntExtPortMapListForPortName(portName, routerId);
357 if (intExtPortMapList.isEmpty()) {
358 LOG.debug("processInterfaceRemoved : Ip Mapping list is empty/null for portName {}", portName);
361 InstanceIdentifier<RouterPorts> portIid = NatUtil.buildRouterPortsIdentifier(routerId);
362 ListenableFuture<Void> future = txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION, tx -> {
363 for (InternalToExternalPortMap intExtPortMap : intExtPortMapList) {
364 LOG.trace("processInterfaceRemoved : Removing DNAT Flow entries for dpnId {} ", dpnId);
365 floatingIPListener.removeNATFlowEntries(portName, intExtPortMap, portIid, routerId, dpnId, tx);
371 } catch (InterruptedException | ExecutionException e) {
372 LOG.error("Error processing interface removal", e);
376 private class NatFlowAddWorker implements Callable<List<ListenableFuture<Void>>> {
377 private final String interfaceName;
378 private final String routerName;
379 private final Uint64 dpnId;
380 private final VipState vipState;
382 NatFlowAddWorker(String interfaceName,String routerName, Uint64 dpnId, VipState vipState) {
383 this.interfaceName = interfaceName;
384 this.routerName = routerName;
386 this.vipState = vipState;
390 @SuppressWarnings("checkstyle:IllegalCatch")
391 public List<ListenableFuture<Void>> call() {
392 final List<ListenableFuture<Void>> futures = new ArrayList<>();
393 LOG.trace("call : Interface {} up event received", interfaceName);
395 LOG.trace("call : Port added event received for interface {} ", interfaceName);
396 processInterfaceAdded(interfaceName, routerName, dpnId, vipState);
397 } catch (Exception ex) {
398 LOG.error("call : Exception caught in Interface {} Operational State Up event",
405 private class NatFlowUpdateWorker implements Callable<List<ListenableFuture<Void>>> {
406 private final Interface original;
407 private final Interface update;
408 private final String routerName;
410 NatFlowUpdateWorker(Interface original, Interface update, String routerName) {
411 this.original = original;
412 this.update = update;
413 this.routerName = routerName;
417 @SuppressWarnings("checkstyle:IllegalCatch")
418 public List<ListenableFuture<Void>> call() {
419 final List<ListenableFuture<Void>> futures = new ArrayList<>();
420 String interfaceName = update.getName();
421 IntfTransitionState state = getTransitionState(original.getOperStatus(), update.getOperStatus());
422 if (state.equals(IntfTransitionState.STATE_IGNORE)) {
423 LOG.info("NAT Service: Interface {} state original {} updated {} not handled",
424 interfaceName, original.getOperStatus(), update.getOperStatus());
427 if (state.equals(IntfTransitionState.STATE_UP)) {
428 LOG.debug("call : Port UP event received for interface {} ", interfaceName);
429 } else if (state.equals(IntfTransitionState.STATE_DOWN)) {
430 LOG.debug("call : Port DOWN event received for interface {} ", interfaceName);
432 if (NatUtil.isSnatEnabledForRouterId(dataBroker, routerName)) {
433 NatUtil.removeSnatEntriesForPort(dataBroker, naptManager, mdsalManager, neutronVpnService,
434 interfaceName, routerName);
436 } catch (Exception ex) {
437 LOG.error("call : Exception caught in Interface {} OperationalStateDown", interfaceName, ex);
444 private class NatFlowRemoveWorker implements Callable<List<ListenableFuture<Void>>> {
445 private final String interfaceName;
446 private final String routerName;
447 private final Uint64 intfDpnId;
449 NatFlowRemoveWorker(String interfaceName, Uint64 intfDpnId, String routerName) {
450 this.interfaceName = interfaceName;
451 this.routerName = routerName;
452 this.intfDpnId = intfDpnId;
456 @SuppressWarnings("checkstyle:IllegalCatch")
457 public List<ListenableFuture<Void>> call() {
458 final List<ListenableFuture<Void>> futures = new ArrayList<>();
459 LOG.trace("call : Interface {} removed event received", interfaceName);
461 LOG.trace("call : Port removed event received for interface {} ", interfaceName);
462 processInterfaceRemoved(interfaceName, intfDpnId, routerName, futures);
463 if (NatUtil.isSnatEnabledForRouterId(dataBroker, routerName)) {
464 NatUtil.removeSnatEntriesForPort(dataBroker, naptManager, mdsalManager, neutronVpnService,
465 interfaceName, routerName);
467 } catch (Exception e) {
468 LOG.error("call : Exception caught in Interface {} OperationalStateRemove", interfaceName, e);