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