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