2 * Copyright (c) 2016 - 2017 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 com.google.common.base.Optional;
11 import com.google.common.util.concurrent.ListenableFuture;
12 import java.math.BigInteger;
13 import java.util.ArrayList;
14 import java.util.List;
15 import java.util.concurrent.Callable;
16 import java.util.concurrent.ExecutionException;
17 import java.util.concurrent.Future;
18 import javax.annotation.PostConstruct;
19 import javax.inject.Inject;
20 import javax.inject.Singleton;
21 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
22 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
23 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
24 import org.opendaylight.genius.datastoreutils.AsyncDataTreeChangeListenerBase;
25 import org.opendaylight.genius.datastoreutils.SingleTransactionDataBroker;
26 import org.opendaylight.genius.mdsalutil.FlowEntity;
27 import org.opendaylight.genius.mdsalutil.NwConstants;
28 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
29 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
30 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.VpnInterface;
31 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.vpn._interface.VpnInstanceNames;
32 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana._if.type.rev140508.L2vlan;
33 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.InterfacesState;
34 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface;
35 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn._interface.op.data.VpnInterfaceOpDataEntry;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.NaptSwitches;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ProtocolTypes;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.floating.ip.info.RouterPorts;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.floating.ip.info.router.ports.Ports;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.floating.ip.info.router.ports.ports.InternalToExternalPortMap;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.intext.ip.port.map.ip.port.mapping.intext.ip.protocol.type.ip.port.map.IpPortExternal;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.napt.switches.RouterToNaptSwitch;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.napt.switches.RouterToNaptSwitchKey;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.snatint.ip.port.map.intip.port.map.IpPort;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.snatint.ip.port.map.intip.port.map.ip.port.IntIpProtoType;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.GetFixedIPsForNeutronPortInputBuilder;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.GetFixedIPsForNeutronPortOutput;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.NeutronvpnService;
50 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
51 import org.opendaylight.yangtools.yang.common.RpcResult;
52 import org.slf4j.Logger;
53 import org.slf4j.LoggerFactory;
56 public class InterfaceStateEventListener
57 extends AsyncDataTreeChangeListenerBase<Interface, InterfaceStateEventListener> {
59 private static final Logger LOG = LoggerFactory.getLogger(InterfaceStateEventListener.class);
61 private static final String NAT_FLOW = "NATFLOW";
63 private final DataBroker dataBroker;
64 private final IMdsalApiManager mdsalManager;
65 private final FloatingIPListener floatingIPListener;
66 private final NaptManager naptManager;
67 private final NeutronvpnService neutronVpnService;
68 private final JobCoordinator coordinator;
71 public InterfaceStateEventListener(final DataBroker dataBroker, final IMdsalApiManager mdsalManager,
72 final FloatingIPListener floatingIPListener,
73 final NaptManager naptManager,
74 final NeutronvpnService neutronvpnService,
75 final JobCoordinator coordinator) {
76 super(Interface.class, InterfaceStateEventListener.class);
77 this.dataBroker = dataBroker;
78 this.mdsalManager = mdsalManager;
79 this.floatingIPListener = floatingIPListener;
80 this.naptManager = naptManager;
81 this.neutronVpnService = neutronvpnService;
82 this.coordinator = coordinator;
88 LOG.info("{} init", getClass().getSimpleName());
89 registerListener(LogicalDatastoreType.OPERATIONAL, dataBroker);
93 protected InstanceIdentifier<Interface> getWildCardPath() {
94 return InstanceIdentifier.create(InterfacesState.class).child(Interface.class);
98 protected InterfaceStateEventListener getDataTreeChangeListener() {
99 return InterfaceStateEventListener.this;
103 // TODO Clean up the exception handling
104 @SuppressWarnings("checkstyle:IllegalCatch")
105 protected void remove(InstanceIdentifier<Interface> identifier, Interface delintrf) {
106 LOG.trace("remove : Interface {} removed event received", delintrf);
107 if (!L2vlan.class.equals(delintrf.getType())) {
108 LOG.debug("remove : Interface {} is a not type Vlan.Ignoring", delintrf.getName());
112 NatFlowRemoveWorker natFlowRemoveWorker =
113 new NatFlowRemoveWorker(delintrf);
114 coordinator.enqueueJob(NAT_FLOW + "-" + delintrf.getName(), natFlowRemoveWorker,
115 NatConstants.NAT_DJC_MAX_RETRIES);
119 // TODO Clean up the exception handling
120 @SuppressWarnings("checkstyle:IllegalCatch")
121 protected void update(InstanceIdentifier<Interface> identifier, Interface original, Interface update) {
122 LOG.trace("update : Operation Interface update event - Old: {}, New: {}", original, update);
123 if (!L2vlan.class.equals(update.getType())) {
124 LOG.debug("update : Interface {} is not type Vlan.Ignoring", update.getName());
128 NatFlowUpdateWorker natFlowUpdateWorker =
129 new NatFlowUpdateWorker(update);
130 coordinator.enqueueJob(NAT_FLOW + "-" + update.getName(), natFlowUpdateWorker,
131 NatConstants.NAT_DJC_MAX_RETRIES);
136 // TODO Clean up the exception handling
137 @SuppressWarnings("checkstyle:IllegalCatch")
138 protected void add(InstanceIdentifier<Interface> identifier, Interface intrf) {
139 LOG.trace("add : Interface {} up event received", intrf);
140 if (!L2vlan.class.equals(intrf.getType())) {
141 LOG.debug("add : Interface {} is not type vlan.Ignoring", intrf.getName());
145 NatFlowAddWorker natFlowAddWorker =
146 new NatFlowAddWorker(intrf);
147 coordinator.enqueueJob(NAT_FLOW + "-" + intrf.getName(), natFlowAddWorker,
148 NatConstants.NAT_DJC_MAX_RETRIES);
151 // TODO Clean up the exception handling
152 @SuppressWarnings("checkstyle:IllegalCatch")
153 private void removeSnatEntriesForPort(String interfaceName, String routerName) {
154 Long routerId = NatUtil.getVpnId(dataBroker, routerName);
155 if (routerId == NatConstants.INVALID_ID) {
156 LOG.error("removeSnatEntriesForPort : routerId not found for routername {}", routerName);
159 BigInteger naptSwitch = getNaptSwitchforRouter(dataBroker, routerName);
160 if (naptSwitch == null || naptSwitch.equals(BigInteger.ZERO)) {
161 LOG.error("removeSnatEntriesForPort : NaptSwitch is not elected for router {} with Id {}",
162 routerName, routerId);
165 //getInternalIp for port
166 List<String> fixedIps = getFixedIpsForPort(interfaceName);
167 if (fixedIps == null) {
168 LOG.warn("removeSnatEntriesForPort : Internal Ips not found for InterfaceName {} in router {} with id {}",
169 interfaceName, routerName, routerId);
173 for (String internalIp : fixedIps) {
174 LOG.debug("removeSnatEntriesForPort : Internal Ip retrieved for interface {} is {} in router with Id {}",
175 interfaceName, internalIp, routerId);
176 IpPort ipPort = NatUtil.getInternalIpPortInfo(dataBroker, routerId, internalIp);
177 if (ipPort == null) {
178 LOG.debug("removeSnatEntriesForPort : no snatint-ip-port-map found for ip:{}", internalIp);
182 for (IntIpProtoType protoType: ipPort.getIntIpProtoType()) {
183 ProtocolTypes protocol = protoType.getProtocol();
184 for (Integer portnum : protoType.getPorts()) {
185 //build and remove the flow in outbound table
187 removeNatFlow(naptSwitch, NwConstants.OUTBOUND_NAPT_TABLE, routerId, internalIp, portnum);
188 } catch (Exception ex) {
189 LOG.error("removeSnatEntriesForPort : Failed to remove snat flow for internalIP {} with "
190 + "Port {} protocol {} for routerId {} in OUTBOUNDTABLE of NaptSwitch {}: {}",
191 internalIp, portnum, protocol, routerId, naptSwitch, ex);
193 //Get the external IP address and the port from the model
194 NAPTEntryEvent.Protocol proto = protocol.toString().equals(ProtocolTypes.TCP.toString())
195 ? NAPTEntryEvent.Protocol.TCP : NAPTEntryEvent.Protocol.UDP;
196 IpPortExternal ipPortExternal = NatUtil.getExternalIpPortMap(dataBroker, routerId,
197 internalIp, String.valueOf(portnum), proto);
198 if (ipPortExternal == null) {
199 LOG.error("removeSnatEntriesForPort : Mapping for internalIp {} with port {} is not found in "
200 + "router with Id {}", internalIp, portnum, routerId);
203 String externalIpAddress = ipPortExternal.getIpAddress();
204 Integer portNumber = ipPortExternal.getPortNum();
206 //build and remove the flow in inboundtable
208 removeNatFlow(naptSwitch, NwConstants.INBOUND_NAPT_TABLE, routerId,
209 externalIpAddress, portNumber);
210 } catch (Exception ex) {
211 LOG.error("removeSnatEntriesForPort : Failed to remove snat flow internalIP {} with "
212 + "Port {} protocol {} for routerId {} in INBOUNDTABLE of naptSwitch {}",
213 externalIpAddress, portNumber, protocol, routerId, naptSwitch, ex);
216 String internalIpPort = internalIp + ":" + portnum;
217 // delete the entry from IntExtIpPortMap DS
219 naptManager.removeFromIpPortMapDS(routerId, internalIpPort, proto);
220 naptManager.removePortFromPool(internalIpPort, externalIpAddress);
221 } catch (Exception ex) {
222 LOG.error("removeSnatEntriesForPort : releaseIpExtPortMapping failed, Removal of "
223 + "ipportmap {} for router {} failed {}", internalIpPort, routerId, ex);
227 // delete the entry from SnatIntIpPortMap DS
228 LOG.debug("removeSnatEntriesForPort : Removing InternalIp:{} on router {}", internalIp, routerId);
229 naptManager.removeFromSnatIpPortDS(routerId, internalIp);
233 // TODO Clean up the exception handling
234 @SuppressWarnings("checkstyle:IllegalCatch")
235 private String getRouterIdForPort(String interfaceName) {
236 String routerName = null;
237 VpnInterface vpnInterface = null;
239 vpnInterface = NatUtil.getConfiguredVpnInterface(dataBroker, interfaceName);
240 } catch (Exception ex) {
241 LOG.error("getRouterIdForPort : Unable to process for interface {} as it is not configured",
244 if (vpnInterface != null) {
246 if (vpnInterface.getVpnInstanceNames() == null) {
247 LOG.debug("getRouterIdForPort : vpnName not found for vpnInterface {} of port {}",
248 vpnInterface, interfaceName);
250 for (VpnInstanceNames vpnInstance : vpnInterface.getVpnInstanceNames()) {
251 String vpnName = vpnInstance.getVpnName();
253 routerName = NatUtil.getRouterIdfromVpnInstance(dataBroker, vpnName);
254 } catch (Exception e) {
255 LOG.error("getRouterIdForPort : Unable to get routerId for vpnName {}", vpnName, e);
257 if (routerName != null) {
258 //check router is associated to external network
259 if (NatUtil.isSnatEnabledForRouterId(dataBroker, routerName)) {
260 LOG.debug("getRouterIdForPort : Retreived Router Id {} for vpnname {} "
261 + "associated to interface {}", routerName, vpnName, interfaceName);
264 LOG.warn("getRouterIdForPort : Interface {} associated to routerId {} is not "
265 + "associated to external network", interfaceName, routerName);
268 LOG.warn("getRouterIdForPort : Router is not associated to vpnname {} for interface {}",
269 vpnName, interfaceName);
274 LOG.debug("getRouterIdForPort : Interface {} is not a vpninterface", interfaceName);
279 private BigInteger getNaptSwitchforRouter(DataBroker broker, String routerName) {
280 InstanceIdentifier<RouterToNaptSwitch> rtrNaptSw = InstanceIdentifier.builder(NaptSwitches.class)
281 .child(RouterToNaptSwitch.class, new RouterToNaptSwitchKey(routerName)).build();
282 Optional<RouterToNaptSwitch> routerToNaptSwitchData =
283 SingleTransactionDataBroker.syncReadOptionalAndTreatReadFailedExceptionAsAbsentOptional(broker,
284 LogicalDatastoreType.CONFIGURATION, rtrNaptSw);
285 if (routerToNaptSwitchData.isPresent()) {
286 RouterToNaptSwitch routerToNaptSwitchInstance = routerToNaptSwitchData.get();
287 return routerToNaptSwitchInstance.getPrimarySwitchId();
292 private void removeNatFlow(BigInteger dpnId, short tableId, Long routerId, String ipAddress, int ipPort) {
294 String switchFlowRef = NatUtil.getNaptFlowRef(dpnId, tableId, String.valueOf(routerId), ipAddress, ipPort);
295 FlowEntity snatFlowEntity = NatUtil.buildFlowEntity(dpnId, tableId, switchFlowRef);
297 mdsalManager.removeFlow(snatFlowEntity);
298 LOG.debug("removeNatFlow : Removed the flow in table {} for the switch with the DPN ID {} for "
299 + "router {} ip {} port {}", tableId, dpnId, routerId, ipAddress, ipPort);
302 private void processInterfaceAdded(String portName, String routerId, List<ListenableFuture<Void>> futures) {
303 LOG.trace("processInterfaceAdded : Processing Interface Add Event for interface {}", portName);
304 List<InternalToExternalPortMap> intExtPortMapList = getIntExtPortMapListForPortName(portName, routerId);
305 if (intExtPortMapList == null || intExtPortMapList.isEmpty()) {
306 LOG.debug("processInterfaceAdded : Ip Mapping list is empty/null for portname {}", portName);
309 InstanceIdentifier<RouterPorts> portIid = NatUtil.buildRouterPortsIdentifier(routerId);
310 WriteTransaction installFlowInvTx = dataBroker.newWriteOnlyTransaction();
311 for (InternalToExternalPortMap intExtPortMap : intExtPortMapList) {
312 floatingIPListener.createNATFlowEntries(portName, intExtPortMap, portIid, routerId, installFlowInvTx);
314 //final submit call for installFlowInvTx
315 futures.add(NatUtil.waitForTransactionToComplete(installFlowInvTx));
318 private void processInterfaceRemoved(String portName, BigInteger dpnId, String routerId,
319 List<ListenableFuture<Void>> futures) {
320 LOG.trace("processInterfaceRemoved : Processing Interface Removed Event for interface {} on DPN ID {}",
322 List<InternalToExternalPortMap> intExtPortMapList = getIntExtPortMapListForPortName(portName, routerId);
323 if (intExtPortMapList == null || intExtPortMapList.isEmpty()) {
324 LOG.debug("processInterfaceRemoved : Ip Mapping list is empty/null for portName {}", portName);
327 InstanceIdentifier<RouterPorts> portIid = NatUtil.buildRouterPortsIdentifier(routerId);
328 WriteTransaction removeFlowInvTx = dataBroker.newWriteOnlyTransaction();
329 for (InternalToExternalPortMap intExtPortMap : intExtPortMapList) {
330 LOG.trace("processInterfaceRemoved : Removing DNAT Flow entries for dpnId {} ", dpnId);
331 floatingIPListener.removeNATFlowEntries(portName, intExtPortMap, portIid, routerId, dpnId, removeFlowInvTx);
333 //final submit call for removeFlowInvTx
334 futures.add(NatUtil.waitForTransactionToComplete(removeFlowInvTx));
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);
347 return port.get().getInternalToExternalPortMap();
350 private List<String> getFixedIpsForPort(String interfname) {
351 LOG.debug("getFixedIpsForPort : getFixedIpsForPort method is called for interface {}", interfname);
353 Future<RpcResult<GetFixedIPsForNeutronPortOutput>> result =
354 neutronVpnService.getFixedIPsForNeutronPort(new GetFixedIPsForNeutronPortInputBuilder()
355 .setPortId(new Uuid(interfname)).build());
357 RpcResult<GetFixedIPsForNeutronPortOutput> rpcResult = result.get();
358 if (!rpcResult.isSuccessful()) {
359 LOG.error("getFixedIpsForPort : RPC Call to GetFixedIPsForNeutronPortOutput returned with Errors {}",
360 rpcResult.getErrors());
362 return rpcResult.getResult().getFixedIPs();
364 } catch (InterruptedException | ExecutionException | NullPointerException ex) {
365 LOG.error("getFixedIpsForPort : Exception while receiving fixedIps for port {}", interfname, ex);
370 private class NatFlowAddWorker implements Callable<List<ListenableFuture<Void>>> {
373 NatFlowAddWorker(Interface iface) {
378 @SuppressWarnings("checkstyle:IllegalCatch")
379 public List<ListenableFuture<Void>> call() {
380 final List<ListenableFuture<Void>> futures = new ArrayList<>();
381 LOG.trace("call : Interface {} up event received", iface);
382 String interfaceName = iface.getName();
384 LOG.trace("call : Port added event received for interface {} ", interfaceName);
385 String routerId = getRouterIdForPort(interfaceName);
386 if (routerId != null) {
387 processInterfaceAdded(interfaceName, routerId, futures);
389 } catch (Exception ex) {
390 LOG.error("call : Exception caught in Interface {} Operational State Up event",
397 private class NatFlowRemoveWorker implements Callable<List<ListenableFuture<Void>>> {
400 NatFlowRemoveWorker(Interface delintrf) {
401 this.delintrf = delintrf;
405 @SuppressWarnings("checkstyle:IllegalCatch")
406 public List<ListenableFuture<Void>> call() {
407 final List<ListenableFuture<Void>> futures = new ArrayList<>();
408 final String interfaceName = delintrf.getName();
409 LOG.trace("call : Interface {} removed event received", delintrf);
411 String routerName = getRouterIdForPort(interfaceName);
412 if (routerName != null) {
413 LOG.trace("call : Port removed event received for interface {} ", interfaceName);
415 BigInteger dpId = null;
417 dpId = NatUtil.getDpIdFromInterface(delintrf);
418 } catch (Exception e) {
420 "call: Unable to retrieve DPNID from Interface operational data store for"
421 + " Interface {}. Fetching from VPN Interface op data store. ",
423 InstanceIdentifier<VpnInterface> id = NatUtil.getVpnInterfaceIdentifier(interfaceName);
424 Optional<VpnInterface> cfgVpnInterface =
425 SingleTransactionDataBroker.syncReadOptionalAndTreatReadFailedExceptionAsAbsentOptional(
426 dataBroker, LogicalDatastoreType.CONFIGURATION, id);
427 if (!cfgVpnInterface.isPresent()) {
428 LOG.debug("call : Interface {} is not a VPN Interface, ignoring.", interfaceName);
431 for (VpnInstanceNames vpnInstance : cfgVpnInterface.get().getVpnInstanceNames()) {
432 String vpnName = vpnInstance.getVpnName();
433 InstanceIdentifier<VpnInterfaceOpDataEntry> idOper = NatUtil
434 .getVpnInterfaceOpDataEntryIdentifier(interfaceName, vpnName);
435 Optional<VpnInterfaceOpDataEntry> optVpnInterface =
436 SingleTransactionDataBroker.syncReadOptionalAndTreatReadFailedExceptionAsAbsentOptional(
437 dataBroker, LogicalDatastoreType.OPERATIONAL, idOper);
438 if (optVpnInterface.isPresent()) {
439 dpId = optVpnInterface.get().getDpnId();
444 if (dpId == null || dpId.equals(BigInteger.ZERO)) {
445 LOG.error("call : Unable to get DPN ID for the Interface {}", interfaceName);
448 processInterfaceRemoved(interfaceName, dpId, routerName, futures);
449 removeSnatEntriesForPort(interfaceName, routerName);
451 LOG.debug("call : PORT_REMOVE: Router Id is null either Interface {} is not associated "
452 + "to router or failed to retrieve routerId due to exception", interfaceName);
454 } catch (Exception e) {
455 LOG.error("call : Exception caught in Interface {} OperationalStateRemove", interfaceName, e);
461 private class NatFlowUpdateWorker implements Callable<List<ListenableFuture<Void>>> {
464 NatFlowUpdateWorker(Interface update) {
465 this.update = update;
469 @SuppressWarnings("checkstyle:IllegalCatch")
470 public List<ListenableFuture<Void>> call() {
471 final List<ListenableFuture<Void>> futures = new ArrayList<>();
472 String interfaceName = update.getName();
473 if (update.getOperStatus().equals(Interface.OperStatus.Up)) {
474 LOG.debug("call : Port UP event received for interface {} ", interfaceName);
475 } else if (update.getOperStatus().equals(Interface.OperStatus.Down)) {
476 LOG.debug("call : Port DOWN event received for interface {} ", interfaceName);
478 String routerName = getRouterIdForPort(interfaceName);
479 if (routerName != null) {
480 removeSnatEntriesForPort(interfaceName, routerName);
483 "call : PORT_DOWN: Router Id is null, either Interface {} is not associated "
484 + "to a router or failed to retrieve routerId due to exception", interfaceName);
486 } catch (Exception ex) {
487 LOG.error("call : Exception caught in Interface {} OperationalStateDown", interfaceName, ex);