MRI version bumpup for Aluminium
[netvirt.git] / natservice / impl / src / main / java / org / opendaylight / netvirt / natservice / internal / RouterDpnChangeListener.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.natservice.internal;
9
10 import static org.opendaylight.genius.infra.Datastore.CONFIGURATION;
11
12 import java.util.ArrayList;
13 import java.util.Collection;
14 import java.util.Collections;
15 import java.util.Optional;
16 import javax.annotation.PreDestroy;
17 import javax.inject.Inject;
18 import javax.inject.Singleton;
19 import org.opendaylight.genius.datastoreutils.SingleTransactionDataBroker;
20 import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
21 import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
22 import org.opendaylight.genius.mdsalutil.NwConstants;
23 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
24 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
25 import org.opendaylight.infrautils.utils.concurrent.Executors;
26 import org.opendaylight.infrautils.utils.concurrent.ListenableFutures;
27 import org.opendaylight.mdsal.binding.api.DataBroker;
28 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
29 import org.opendaylight.netvirt.natservice.api.SnatServiceManager;
30 import org.opendaylight.netvirt.neutronvpn.interfaces.INeutronVpnManager;
31 import org.opendaylight.serviceutils.tools.listener.AbstractAsyncDataTreeChangeListener;
32 import org.opendaylight.serviceutils.upgrade.UpgradeState;
33 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.IdManagerService;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.NeutronRouterDpns;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.neutron.router.dpns.RouterDpnList;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.neutron.router.dpns.router.dpn.list.DpnVpninterfacesList;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.config.rev170206.NatserviceConfig;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.config.rev170206.NatserviceConfig.NatMode;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ProviderTypes;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ext.routers.Routers;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ext.routers.routers.ExternalIps;
43 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
44 import org.opendaylight.yangtools.yang.common.Uint32;
45 import org.opendaylight.yangtools.yang.common.Uint64;
46 import org.slf4j.Logger;
47 import org.slf4j.LoggerFactory;
48
49 @Singleton
50 public class RouterDpnChangeListener extends AbstractAsyncDataTreeChangeListener<DpnVpninterfacesList> {
51
52     private static final Logger LOG = LoggerFactory.getLogger(RouterDpnChangeListener.class);
53     private final DataBroker dataBroker;
54     private final ManagedNewTransactionRunner txRunner;
55     private final IMdsalApiManager mdsalManager;
56     private final SNATDefaultRouteProgrammer snatDefaultRouteProgrammer;
57     private final NaptSwitchHA naptSwitchHA;
58     private final IdManagerService idManager;
59     private final INeutronVpnManager nvpnManager;
60     private final ExternalNetworkGroupInstaller extNetGroupInstaller;
61     private final JobCoordinator coordinator;
62     private final SnatServiceManager natServiceManager;
63     private final NatMode natMode;
64     private final UpgradeState upgradeState;
65
66     @Inject
67     public RouterDpnChangeListener(final DataBroker dataBroker, final IMdsalApiManager mdsalManager,
68                                    final SNATDefaultRouteProgrammer snatDefaultRouteProgrammer,
69                                    final NaptSwitchHA naptSwitchHA,
70                                    final IdManagerService idManager,
71                                    final ExternalNetworkGroupInstaller extNetGroupInstaller,
72                                    final INeutronVpnManager nvpnManager,
73                                    final SnatServiceManager natServiceManager,
74                                    final NatserviceConfig config,
75                                    final JobCoordinator coordinator,
76                                    final UpgradeState upgradeState) {
77         super(dataBroker, LogicalDatastoreType.OPERATIONAL, InstanceIdentifier.create(NeutronRouterDpns.class)
78                 .child(RouterDpnList.class).child(DpnVpninterfacesList.class),
79                 Executors.newListeningSingleThreadExecutor("RouterDpnChangeListener", LOG));
80         this.dataBroker = dataBroker;
81         this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
82         this.mdsalManager = mdsalManager;
83         this.snatDefaultRouteProgrammer = snatDefaultRouteProgrammer;
84         this.naptSwitchHA = naptSwitchHA;
85         this.idManager = idManager;
86         this.extNetGroupInstaller = extNetGroupInstaller;
87         this.nvpnManager = nvpnManager;
88         this.natServiceManager = natServiceManager;
89         this.coordinator = coordinator;
90         this.natMode = config != null ? config.getNatMode() : NatMode.Controller;
91         this.upgradeState = upgradeState;
92     }
93
94     public void init() {
95         LOG.info("{} init", getClass().getSimpleName());
96     }
97
98     @Override
99     @PreDestroy
100     public void close() {
101         super.close();
102         Executors.shutdownAndAwaitTermination(getExecutorService());
103     }
104
105     @Override
106     public void add(final InstanceIdentifier<DpnVpninterfacesList> identifier, final DpnVpninterfacesList dpnInfo) {
107         LOG.trace("add : key: {}, value: {}", dpnInfo.key(), dpnInfo);
108         final String routerUuid = identifier.firstKeyOf(RouterDpnList.class).getRouterId();
109         Uint64 dpnId = dpnInfo.getDpnId();
110         //check router is associated to external network
111         InstanceIdentifier<Routers> id = NatUtil.buildRouterIdentifier(routerUuid);
112         Optional<Routers> routerData =
113                 SingleTransactionDataBroker.syncReadOptionalAndTreatReadFailedExceptionAsAbsentOptional(dataBroker,
114                         LogicalDatastoreType.CONFIGURATION, id);
115         if (routerData.isPresent()) {
116             Routers router = routerData.get();
117             Uuid networkId = router.getNetworkId();
118             if (networkId != null) {
119                 if (natMode == NatMode.Conntrack) {
120                     Uint64 naptSwitch = NatUtil.getPrimaryNaptfromRouterName(dataBroker, router.getRouterName());
121                     if (naptSwitch == null || naptSwitch.equals(Uint64.ZERO)) {
122                         LOG.warn("add : NAPT switch is not selected.");
123                         return;
124                     }
125                     //If it is for NAPT switch skip as the flows would be already programmed.
126                     if (naptSwitch.equals(dpnId)) {
127                         LOG.debug("Skipping the notification recived for NAPT switch {}", routerUuid);
128                         return;
129                     }
130                     ListenableFutures.addErrorLogging(txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION,
131                         confTx -> {
132                             natServiceManager.notify(confTx, router, null, naptSwitch, dpnId,
133                                     SnatServiceManager.Action.CNT_ROUTER_ENBL);
134                             if (router.isEnableSnat()) {
135                                 natServiceManager.notify(confTx, router, null, naptSwitch, naptSwitch,
136                                         SnatServiceManager.Action.SNAT_ROUTER_ENBL);
137                             }
138                         }), LOG, "Error notifying NAT service manager");
139                 } else {
140                     Uint32 routerId = NatUtil.getVpnId(dataBroker, routerUuid);
141                     if (routerId == NatConstants.INVALID_ID) {
142                         LOG.error("add : Invalid routerId returned for routerName {}", routerUuid);
143                         return;
144                     }
145                     ProviderTypes extNwProvType = NatEvpnUtil.getExtNwProvTypeFromRouterName(dataBroker,
146                             routerUuid, networkId);
147                     if (extNwProvType == ProviderTypes.FLAT || extNwProvType == ProviderTypes.VLAN) {
148                         coordinator.enqueueJob(NatConstants.NAT_DJC_PREFIX + networkId, () -> {
149                             extNetGroupInstaller.installExtNetGroupEntries(networkId, dpnId);
150                             installDefaultNatRouteForRouterExternalSubnets(dpnId,
151                                     NatUtil.getExternalSubnetIdsFromExternalIps(
152                                             new ArrayList<ExternalIps>(router.getExternalIps().values())));
153                             return Collections.emptyList();
154                         });
155                     }
156                     coordinator.enqueueJob(NatConstants.NAT_DJC_PREFIX + router.getRouterName(), () -> {
157                         LOG.debug("add : Router {} is associated with ext nw {}", routerUuid, networkId);
158                         Uuid vpnName = NatUtil.getVpnForRouter(dataBroker, routerUuid);
159                         return Collections.singletonList(
160                             txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION, confTx -> {
161                                 Uint32 vpnId;
162                                 if (vpnName == null) {
163                                     LOG.debug("add : Internal vpn associated to router {}", routerUuid);
164                                     vpnId = routerId;
165                                     if (vpnId == NatConstants.INVALID_ID) {
166                                         LOG.error("add : Invalid vpnId returned for routerName {}", routerUuid);
167                                         return;
168                                     }
169                                     LOG.debug("add : Retrieved vpnId {} for router {}", vpnId, routerUuid);
170                                     //Install default entry in FIB to SNAT table
171                                     LOG.info(
172                                         "add : Installing default route in FIB on dpn {} for router {} with vpn {}",
173                                         dpnId, routerUuid, vpnId);
174                                     snatDefaultRouteProgrammer.installDefNATRouteInDPN(dpnId, vpnId, confTx);
175                                 } else {
176                                     LOG.debug("add : External BGP vpn associated to router {}", routerUuid);
177                                     vpnId = NatUtil.getVpnId(dataBroker, vpnName.getValue());
178                                     if (vpnId == NatConstants.INVALID_ID) {
179                                         LOG.error("add : Invalid vpnId returned for routerName {}", routerUuid);
180                                         return;
181                                     }
182                                     LOG.debug("add : Retrieved vpnId {} for router {}", vpnId, routerUuid);
183                                     //Install default entry in FIB to SNAT table
184                                     LOG.debug("add : Installing default route in FIB on dpn {} for routerId {} with "
185                                         + "vpnId {}...", dpnId, routerUuid, vpnId);
186                                     snatDefaultRouteProgrammer.installDefNATRouteInDPN(dpnId, vpnId, routerId, confTx);
187                                 }
188                                 /* install V6 internet default fallback rule in FIB_TABLE if router
189                                  * is having V6 subnet
190                                  */
191                                 nvpnManager.programV6InternetFallbackFlow(new Uuid(routerUuid),
192                                         NatUtil.getVpnIdfromNetworkId(dataBroker, networkId), NwConstants.ADD_FLOW);
193                                 if (router.isEnableSnat()) {
194                                     LOG.info("add : SNAT enabled for router {}", routerUuid);
195                                     if (extNwProvType == null) {
196                                         LOG.error("add : External Network Provider Type missing");
197                                         return;
198                                     }
199                                     NatUtil.handleSNATForDPN(dataBroker, mdsalManager, idManager, naptSwitchHA,
200                                         dpnId, router, routerId, vpnId, confTx, extNwProvType, upgradeState);
201                                 } else {
202                                     LOG.info("add : SNAT is not enabled for router {} to handle addDPN event {}",
203                                         routerUuid, dpnId);
204                                 }
205                             }));
206                     }, NatConstants.NAT_DJC_MAX_RETRIES);
207                 } // end of controller based SNAT
208             }
209         } else {
210             LOG.debug("add : Router {} is not associated with External network", routerUuid);
211         }
212     }
213
214     @Override
215     public void remove(InstanceIdentifier<DpnVpninterfacesList> identifier, DpnVpninterfacesList dpnInfo) {
216         LOG.trace("remove : key: {}, value: {}", dpnInfo.key(), dpnInfo);
217         final String routerUuid = identifier.firstKeyOf(RouterDpnList.class).getRouterId();
218         Uint32 routerId = NatUtil.getVpnId(dataBroker, routerUuid);
219         if (routerId == NatConstants.INVALID_ID) {
220             LOG.error("REMOVE: Invalid routId returned for routerName {}",routerUuid);
221             return;
222         }
223         Uint64 dpnId = dpnInfo.getDpnId();
224         //check router is associated to external network
225         InstanceIdentifier<Routers> id = NatUtil.buildRouterIdentifier(routerUuid);
226         Optional<Routers> routerData =
227                 SingleTransactionDataBroker.syncReadOptionalAndTreatReadFailedExceptionAsAbsentOptional(dataBroker,
228                         LogicalDatastoreType.CONFIGURATION, id);
229         if (routerData.isPresent()) {
230             Routers router = routerData.get();
231             Uuid networkId = router.getNetworkId();
232             if (networkId != null) {
233                 if (natMode == NatMode.Conntrack) {
234                     Uint64 naptSwitch = NatUtil.getPrimaryNaptfromRouterName(dataBroker, router.getRouterName());
235                     if (naptSwitch == null || naptSwitch.equals(Uint64.ZERO)) {
236                         LOG.warn("remove : NAPT switch is not selected.");
237                         return;
238                     }
239                     //If it is for NAPT switch skip as the flows would be already programmed.
240                     if (naptSwitch.equals(dpnId)) {
241                         LOG.debug("Skipping the notification recived for NAPT switch {}", routerUuid);
242                         return;
243                     }
244                     ListenableFutures.addErrorLogging(txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION,
245                         confTx -> {
246                             natServiceManager.notify(confTx, router, null, naptSwitch, dpnId,
247                                     SnatServiceManager.Action.CNT_ROUTER_DISBL);
248                             if (router.isEnableSnat()) {
249                                 natServiceManager.notify(confTx, router, null, naptSwitch, naptSwitch,
250                                         SnatServiceManager.Action.SNAT_ROUTER_DISBL);
251                             }
252                         }), LOG, "Error notifying NAT service manager");
253                 } else {
254                     coordinator.enqueueJob(NatConstants.NAT_DJC_PREFIX + routerUuid, () -> {
255                         LOG.debug("remove : Router {} is associated with ext nw {}", routerUuid, networkId);
256                         Uuid vpnName = NatUtil.getVpnForRouter(dataBroker, routerUuid);
257                         return Collections.singletonList(
258                             txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION, confTx -> {
259                                 Uint32 vpnId;
260                                 if (vpnName == null) {
261                                     LOG.debug("remove : Internal vpn associated to router {}", routerUuid);
262                                     vpnId = routerId;
263                                     if (vpnId == NatConstants.INVALID_ID) {
264                                         LOG.error("remove : Invalid vpnId returned for routerName {}", routerUuid);
265                                         return;
266                                     }
267                                     LOG.debug("remove : Retrieved vpnId {} for router {}", vpnId, routerUuid);
268                                     //Remove default entry in FIB
269                                     LOG.debug("remove : Removing default route in FIB on dpn {} ...", dpnId);
270                                     snatDefaultRouteProgrammer.removeDefNATRouteInDPN(dpnId, vpnId, confTx);
271                                 } else {
272                                     LOG.debug("remove : External vpn associated to router {}", routerUuid);
273                                     vpnId = NatUtil.getVpnId(dataBroker, vpnName.getValue());
274                                     if (vpnId == NatConstants.INVALID_ID) {
275                                         LOG.error("remove : Invalid vpnId returned for routerName {}", routerUuid);
276                                         return;
277                                     }
278                                     LOG.debug("remove : Retrieved vpnId {} for router {}", vpnId, routerUuid);
279                                     //Remove default entry in FIB
280                                     LOG.debug("remove : Removing default route in FIB on dpn {} for vpn {} ...", dpnId,
281                                         vpnName);
282                                     snatDefaultRouteProgrammer.removeDefNATRouteInDPN(dpnId, vpnId, routerId, confTx);
283                                 }
284                                 /* remove V6 internet default fallback rule in FIB_TABLE if router
285                                  * is having V6 subnet
286                                  */
287                                 nvpnManager.programV6InternetFallbackFlow(new Uuid(routerUuid),
288                                         NatUtil.getVpnIdfromNetworkId(dataBroker, networkId), NwConstants.DEL_FLOW);
289                                 if (router.isEnableSnat()) {
290                                     ProviderTypes extNwProvType = NatEvpnUtil.getExtNwProvTypeFromRouterName(dataBroker,
291                                         routerUuid, networkId);
292                                     if (extNwProvType == null) {
293                                         return;
294                                     }
295                                     LOG.info("remove : SNAT enabled for router {}", routerUuid);
296                                     String externalVpnName = NatUtil.getAssociatedVPN(dataBroker,
297                                         routerData.get().getNetworkId());
298                                     NatUtil.removeSNATFromDPN(dataBroker, mdsalManager, idManager, naptSwitchHA, dpnId,
299                                         router, routerId, vpnId, externalVpnName, extNwProvType, confTx);
300                                 } else {
301                                     LOG.info("remove : SNAT is not enabled for router {} to handle removeDPN event {}",
302                                         routerUuid, dpnId);
303                                 }
304                             }));
305                     }, NatConstants.NAT_DJC_MAX_RETRIES);
306                 } // end of controller based SNAT
307             }
308         }
309     }
310
311     @Override
312     public void update(InstanceIdentifier<DpnVpninterfacesList> identifier, DpnVpninterfacesList original,
313                           DpnVpninterfacesList update) {
314         LOG.trace("Update key: {}, original: {}, update: {}", update.key(), original, update);
315     }
316
317     private void installDefaultNatRouteForRouterExternalSubnets(Uint64 dpnId, Collection<Uuid> externalSubnetIds) {
318         if (externalSubnetIds == null) {
319             LOG.error("installDefaultNatRouteForRouterExternalSubnets : No external subnets for router");
320             return;
321         }
322
323         for (Uuid subnetId : externalSubnetIds) {
324             Uint32 vpnIdForSubnet = NatUtil.getExternalSubnetVpnId(dataBroker, subnetId);
325             if (vpnIdForSubnet != NatConstants.INVALID_ID) {
326                 LOG.info("installDefaultNatRouteForRouterExternalSubnets : Installing default routes in FIB on dpn {} "
327                         + "for subnetId {} with vpnId {}", dpnId, subnetId, vpnIdForSubnet);
328                 snatDefaultRouteProgrammer.installDefNATRouteInDPN(dpnId, vpnIdForSubnet, subnetId.getValue());
329             } else {
330                 LOG.debug("installDefaultNatRouteForRouterExternalSubnets : No vpnID for subnet {} found", subnetId);
331             }
332         }
333     }
334 }