2 * Copyright (c) 2016 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;
13 import java.math.BigInteger;
14 import java.util.ArrayList;
15 import java.util.Collection;
16 import java.util.Collections;
17 import java.util.List;
19 import javax.annotation.PostConstruct;
20 import javax.inject.Inject;
21 import javax.inject.Singleton;
23 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
24 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
25 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
26 import org.opendaylight.genius.datastoreutils.AsyncDataTreeChangeListenerBase;
27 import org.opendaylight.genius.datastoreutils.SingleTransactionDataBroker;
28 import org.opendaylight.genius.mdsalutil.BucketInfo;
29 import org.opendaylight.genius.mdsalutil.FlowEntity;
30 import org.opendaylight.genius.mdsalutil.GroupEntity;
31 import org.opendaylight.genius.mdsalutil.MDSALUtil;
32 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
33 import org.opendaylight.netvirt.elanmanager.api.IElanService;
34 import org.opendaylight.netvirt.natservice.api.SnatServiceManager;
35 import org.opendaylight.netvirt.neutronvpn.interfaces.INeutronVpnManager;
36 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.IdManagerService;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.GroupTypes;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.NeutronRouterDpns;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.neutron.router.dpns.RouterDpnList;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.neutron.router.dpns.router.dpn.list.DpnVpninterfacesList;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.config.rev170206.NatserviceConfig;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.config.rev170206.NatserviceConfig.NatMode;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ProviderTypes;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ext.routers.Routers;
46 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
47 import org.slf4j.Logger;
48 import org.slf4j.LoggerFactory;
51 public class RouterDpnChangeListener
52 extends AsyncDataTreeChangeListenerBase<DpnVpninterfacesList, RouterDpnChangeListener>
53 implements AutoCloseable {
55 private static final Logger LOG = LoggerFactory.getLogger(RouterDpnChangeListener.class);
56 private final DataBroker dataBroker;
57 private final IMdsalApiManager mdsalManager;
58 private final SNATDefaultRouteProgrammer snatDefaultRouteProgrammer;
59 private final NaptSwitchHA naptSwitchHA;
60 private final IdManagerService idManager;
61 private final INeutronVpnManager nvpnManager;
62 private final ExternalNetworkGroupInstaller extNetGroupInstaller;
63 private final IElanService elanManager;
64 private SnatServiceManager natServiceManager;
65 private NatMode natMode = NatMode.Controller;
68 public RouterDpnChangeListener(final DataBroker dataBroker, final IMdsalApiManager mdsalManager,
69 final SNATDefaultRouteProgrammer snatDefaultRouteProgrammer,
70 final NaptSwitchHA naptSwitchHA,
71 final IdManagerService idManager,
72 final ExternalNetworkGroupInstaller extNetGroupInstaller,
73 final INeutronVpnManager nvpnManager,
74 final SnatServiceManager natServiceManager,
75 final NatserviceConfig config,
76 final IElanService elanManager) {
77 super(DpnVpninterfacesList.class, RouterDpnChangeListener.class);
78 this.dataBroker = dataBroker;
79 this.mdsalManager = mdsalManager;
80 this.snatDefaultRouteProgrammer = snatDefaultRouteProgrammer;
81 this.naptSwitchHA = naptSwitchHA;
82 this.idManager = idManager;
83 this.extNetGroupInstaller = extNetGroupInstaller;
84 this.nvpnManager = nvpnManager;
85 this.elanManager = elanManager;
86 this.natServiceManager = natServiceManager;
88 this.natMode = config.getNatMode();
95 LOG.info("{} init", getClass().getSimpleName());
96 registerListener(LogicalDatastoreType.OPERATIONAL, dataBroker);
100 protected RouterDpnChangeListener getDataTreeChangeListener() {
101 return RouterDpnChangeListener.this;
105 protected InstanceIdentifier<DpnVpninterfacesList> getWildCardPath() {
106 return InstanceIdentifier.create(NeutronRouterDpns.class).child(RouterDpnList.class)
107 .child(DpnVpninterfacesList.class);
111 protected void add(final InstanceIdentifier<DpnVpninterfacesList> identifier, final DpnVpninterfacesList dpnInfo) {
112 LOG.trace("add : key: {}, value: {}", dpnInfo.getKey(), dpnInfo);
113 final String routerId = identifier.firstKeyOf(RouterDpnList.class).getRouterId();
114 BigInteger dpnId = dpnInfo.getDpnId();
115 //check router is associated to external network
116 InstanceIdentifier<Routers> id = NatUtil.buildRouterIdentifier(routerId);
117 Optional<Routers> routerData =
118 SingleTransactionDataBroker.syncReadOptionalAndTreatReadFailedExceptionAsAbsentOptional(dataBroker,
119 LogicalDatastoreType.CONFIGURATION, id);
120 if (routerData.isPresent()) {
121 Routers router = routerData.get();
122 Uuid networkId = router.getNetworkId();
123 if (networkId != null) {
124 if (natMode == NatMode.Conntrack) {
125 BigInteger naptSwitch = NatUtil.getPrimaryNaptfromRouterName(dataBroker, router.getRouterName());
126 if (naptSwitch == null || naptSwitch.equals(BigInteger.ZERO)) {
127 LOG.warn("add : NAPT switch is not selected.");
130 natServiceManager.notify(router, naptSwitch, dpnId,
131 SnatServiceManager.Action.SNAT_ROUTER_ENBL);
133 WriteTransaction writeFlowInvTx = dataBroker.newWriteOnlyTransaction();
134 WriteTransaction removeFlowInvTx = dataBroker.newWriteOnlyTransaction();
135 LOG.debug("add : Router {} is associated with ext nw {}", routerId, networkId);
136 Uuid vpnName = NatUtil.getVpnForRouter(dataBroker, routerId);
138 if (vpnName == null) {
139 LOG.debug("add : Internal vpn associated to router {}", routerId);
140 vpnId = NatUtil.getVpnId(dataBroker, routerId);
141 if (vpnId == NatConstants.INVALID_ID) {
142 LOG.error("add : Invalid vpnId returned for routerName {}", routerId);
145 LOG.debug("add : Retrieved vpnId {} for router {}", vpnId, routerId);
146 //Install default entry in FIB to SNAT table
147 LOG.info("add : Installing default route in FIB on dpn {} for router {} with vpn {}",
148 dpnId, routerId, vpnId);
149 installDefaultNatRouteForRouterExternalSubnets(dpnId,
150 NatUtil.getExternalSubnetIdsFromExternalIps(router.getExternalIps()));
151 snatDefaultRouteProgrammer.installDefNATRouteInDPN(dpnId, vpnId, writeFlowInvTx);
153 LOG.debug("add : External BGP vpn associated to router {}", routerId);
154 vpnId = NatUtil.getVpnId(dataBroker, vpnName.getValue());
155 if (vpnId == NatConstants.INVALID_ID) {
156 LOG.error("add : Invalid vpnId returned for routerName {}", routerId);
159 Long routId = NatUtil.getVpnId(dataBroker, routerId);
160 if (routId == NatConstants.INVALID_ID) {
161 LOG.error("add : Invalid routId returned for routerName {}", routerId);
164 LOG.debug("add : Retrieved vpnId {} for router {}", vpnId, routerId);
165 //Install default entry in FIB to SNAT table
166 LOG.debug("add : Installing default route in FIB on dpn {} for routerId {} with vpnId {}...",
167 dpnId, routerId, vpnId);
168 installDefaultNatRouteForRouterExternalSubnets(dpnId,
169 NatUtil.getExternalSubnetIdsFromExternalIps(router.getExternalIps()));
170 snatDefaultRouteProgrammer.installDefNATRouteInDPN(dpnId, vpnId, routId, writeFlowInvTx);
172 extNetGroupInstaller.installExtNetGroupEntries(networkId, dpnId);
174 if (router.isEnableSnat()) {
175 LOG.info("add : SNAT enabled for router {}", routerId);
176 handleSNATForDPN(dpnId, routerId, vpnId, writeFlowInvTx, removeFlowInvTx);
178 LOG.info("add : SNAT is not enabled for router {} to handle addDPN event {}", routerId, dpnId);
180 List<ListenableFuture<Void>> futures = new ArrayList<>();
181 futures.add(NatUtil.waitForTransactionToComplete(writeFlowInvTx));
182 futures.add(NatUtil.waitForTransactionToComplete(removeFlowInvTx));
183 } // end of controller based SNAT
186 LOG.debug("add : Router {} is not associated with External network", routerId);
191 protected void remove(InstanceIdentifier<DpnVpninterfacesList> identifier, DpnVpninterfacesList dpnInfo) {
192 LOG.trace("remove : key: {}, value: {}", dpnInfo.getKey(), dpnInfo);
193 final String routerId = identifier.firstKeyOf(RouterDpnList.class).getRouterId();
194 BigInteger dpnId = dpnInfo.getDpnId();
195 //check router is associated to external network
196 InstanceIdentifier<Routers> id = NatUtil.buildRouterIdentifier(routerId);
197 Optional<Routers> routerData =
198 SingleTransactionDataBroker.syncReadOptionalAndTreatReadFailedExceptionAsAbsentOptional(dataBroker,
199 LogicalDatastoreType.CONFIGURATION, id);
200 if (routerData.isPresent()) {
201 Routers router = routerData.get();
202 Uuid networkId = router.getNetworkId();
203 if (networkId != null) {
204 if (natMode == NatMode.Conntrack) {
205 BigInteger naptSwitch = NatUtil.getPrimaryNaptfromRouterName(dataBroker, router.getRouterName());
206 if (naptSwitch == null || naptSwitch.equals(BigInteger.ZERO)) {
207 LOG.warn("remove : NAPT switch is not selected.");
210 natServiceManager.notify(router, naptSwitch, dpnId,
211 SnatServiceManager.Action.SNAT_ROUTER_DISBL);
213 WriteTransaction removeFlowInvTx = dataBroker.newWriteOnlyTransaction();
214 LOG.debug("remove : Router {} is associated with ext nw {}", routerId, networkId);
215 Uuid vpnName = NatUtil.getVpnForRouter(dataBroker, routerId);
217 if (vpnName == null) {
218 LOG.debug("remove : Internal vpn associated to router {}", routerId);
219 vpnId = NatUtil.getVpnId(dataBroker, routerId);
220 if (vpnId == NatConstants.INVALID_ID) {
221 LOG.error("remove : Invalid vpnId returned for routerName {}", routerId);
224 LOG.debug("remove : Retrieved vpnId {} for router {}", vpnId, routerId);
225 //Remove default entry in FIB
226 LOG.debug("remove : Removing default route in FIB on dpn {} for vpn {} ...", dpnId, vpnName);
227 snatDefaultRouteProgrammer.removeDefNATRouteInDPN(dpnId, vpnId, removeFlowInvTx);
229 LOG.debug("remove : External vpn associated to router {}", routerId);
230 vpnId = NatUtil.getVpnId(dataBroker, vpnName.getValue());
231 if (vpnId == NatConstants.INVALID_ID) {
232 LOG.error("remove : Invalid vpnId returned for routerName {}", routerId);
235 Long routId = NatUtil.getVpnId(dataBroker, routerId);
236 if (routId == NatConstants.INVALID_ID) {
237 LOG.error("remove : Invalid routId returned for routerName {}", routerId);
240 LOG.debug("remove : Retrieved vpnId {} for router {}", vpnId, routerId);
241 //Remove default entry in FIB
242 LOG.debug("remove : Removing default route in FIB on dpn {} for vpn {} ...", dpnId, vpnName);
243 snatDefaultRouteProgrammer.removeDefNATRouteInDPN(dpnId, vpnId, routId, removeFlowInvTx);
246 if (router.isEnableSnat()) {
247 LOG.info("remove : SNAT enabled for router {}", routerId);
248 removeSNATFromDPN(dpnId, routerId, vpnId, removeFlowInvTx);
250 LOG.info("remove : SNAT is not enabled for router {} to handle removeDPN event {}",
253 List<ListenableFuture<Void>> futures = new ArrayList<>();
254 futures.add(NatUtil.waitForTransactionToComplete(removeFlowInvTx));
255 } // end of controller based SNAT
261 protected void update(InstanceIdentifier<DpnVpninterfacesList> identifier, DpnVpninterfacesList original,
262 DpnVpninterfacesList update) {
263 LOG.trace("Update key: {}, original: {}, update: {}", update.getKey(), original, update);
266 // TODO Clean up the exception handling
267 @SuppressWarnings("checkstyle:IllegalCatch")
268 void handleSNATForDPN(BigInteger dpnId, String routerName, Long routerVpnId, WriteTransaction writeFlowInvTx,
269 WriteTransaction removeFlowInvTx) {
270 //Check if primary and secondary switch are selected, If not select the role
271 //Install select group to NAPT switch
272 //Install default miss entry to NAPT switch
273 BigInteger naptSwitch;
275 Long routerId = NatUtil.getVpnId(dataBroker, routerName);
276 if (routerId == NatConstants.INVALID_ID) {
277 LOG.error("handleSNATForDPN : Invalid routerId returned for routerName {}", routerName);
280 BigInteger naptId = NatUtil.getPrimaryNaptfromRouterName(dataBroker, routerName);
281 if (naptId == null || naptId.equals(BigInteger.ZERO) || !naptSwitchHA.getSwitchStatus(naptId)) {
282 LOG.debug("handleSNATForDPN : No NaptSwitch is selected for router {}", routerName);
284 boolean naptstatus = naptSwitchHA.updateNaptSwitch(routerName, naptSwitch);
286 LOG.error("handleSNATForDPN : Failed to update newNaptSwitch {} for routername {}",
287 naptSwitch, routerName);
290 LOG.debug("handleSNATForDPN : Switch {} is elected as NaptSwitch for router {}", dpnId, routerName);
292 // When NAPT switch is elected during first VM comes up for the given Router
293 if (elanManager.isOpenStackVniSemanticsEnforced()) {
294 NatOverVxlanUtil.validateAndCreateVxlanVniPool(dataBroker, nvpnManager,
295 idManager, NatConstants.ODL_VNI_POOL_NAME);
298 Routers extRouters = NatUtil.getRoutersFromConfigDS(dataBroker, routerName);
299 if (extRouters != null) {
300 NatUtil.createRouterIdsConfigDS(dataBroker, routerName);
301 naptSwitchHA.subnetRegisterMapping(extRouters, routerId);
304 naptSwitchHA.installSnatFlows(routerName, routerId, naptSwitch, routerVpnId, writeFlowInvTx);
306 // Install miss entry (table 26) pointing to table 46
307 FlowEntity flowEntity = naptSwitchHA.buildSnatFlowEntityForNaptSwitch(dpnId, routerName,
308 routerVpnId, NatConstants.ADD_FLOW);
309 if (flowEntity == null) {
310 LOG.error("handleSNATForDPN : Failed to populate flowentity for router {} with dpnId {}",
314 LOG.debug("handleSNATForDPN : Successfully installed flow for dpnId {} router {}", dpnId, routerName);
315 mdsalManager.addFlowToTx(flowEntity, writeFlowInvTx);
316 //Removing primary flows from old napt switch
317 if (naptId != null && !naptId.equals(BigInteger.ZERO)) {
318 LOG.debug("handleSNATForDPN : Removing primary flows from old napt switch {} for router {}",
320 naptSwitchHA.removeSnatFlowsInOldNaptSwitch(routerName, naptId, null, removeFlowInvTx);
322 } else if (naptId.equals(dpnId)) {
323 LOG.debug("handleSNATForDPN : NaptSwitch {} gone down during cluster reboot came alive", naptId);
326 LOG.debug("handleSNATForDPN : Napt switch with Id {} is already elected for router {}",
330 List<BucketInfo> bucketInfo = naptSwitchHA.handleGroupInNeighborSwitches(dpnId,
331 routerName, naptSwitch);
332 if (bucketInfo == null) {
333 LOG.error("handleSNATForDPN:Failed to populate bucketInfo for dpnId {},routername {},naptSwitch {}",
334 dpnId, routerName, naptSwitch);
337 naptSwitchHA.installSnatGroupEntry(dpnId, bucketInfo, routerName);
339 // Install miss entry (table 26) pointing to group
340 long groupId = NatUtil.createGroupId(NatUtil.getGroupIdKey(routerName), idManager);
341 FlowEntity flowEntity =
342 naptSwitchHA.buildSnatFlowEntity(dpnId, routerName, groupId,
343 routerVpnId, NatConstants.ADD_FLOW);
344 if (flowEntity == null) {
345 LOG.error("handleSNATForDPN : Failed to populate flowentity for router {} with dpnId {} groupId {}",
346 routerName, dpnId, groupId);
349 LOG.debug("handleSNATForDPN : Successfully installed flow for dpnId {} router {} group {}",
350 dpnId, routerName, groupId);
351 mdsalManager.addFlowToTx(flowEntity, writeFlowInvTx);
354 } catch (Exception ex) {
355 LOG.error("handleSNATForDPN : Exception in handleSNATForDPN", ex);
359 // TODO Clean up the exception handling
360 @SuppressWarnings("checkstyle:IllegalCatch")
361 void removeSNATFromDPN(BigInteger dpnId, String routerName, long routerVpnId, WriteTransaction removeFlowInvTx) {
362 //irrespective of naptswitch or non-naptswitch, SNAT default miss entry need to be removed
363 //remove miss entry to NAPT switch
364 //if naptswitch elect new switch and install Snat flows and remove those flows in oldnaptswitch
366 Long routerId = NatUtil.getVpnId(dataBroker, routerName);
367 if (routerId == NatConstants.INVALID_ID) {
368 LOG.error("removeSNATFromDPN : Invalid routerId returned for routerName {}", routerName);
371 Collection<String> externalIpCache = NatUtil.getExternalIpsForRouter(dataBroker, routerId);
372 ProviderTypes extNwProvType = NatEvpnUtil.getExtNwProvTypeFromRouterName(dataBroker, routerName);
373 if (extNwProvType == null) {
376 //Get the external IP labels other than VXLAN provider type. Since label is not applicable for VXLAN
377 Map<String, Long> externalIpLabel;
378 if (extNwProvType == ProviderTypes.VXLAN) {
379 externalIpLabel = null;
381 externalIpLabel = NatUtil.getExternalIpsLabelForRouter(dataBroker, routerId);
383 BigInteger naptSwitch = NatUtil.getPrimaryNaptfromRouterName(dataBroker, routerName);
384 if (naptSwitch == null || naptSwitch.equals(BigInteger.ZERO)) {
385 LOG.error("removeSNATFromDPN : No naptSwitch is selected for router {}", routerName);
390 naptSwitchHA.isNaptSwitchDown(routerName, dpnId, naptSwitch, routerVpnId, externalIpCache,
393 LOG.debug("removeSNATFromDPN: Switch with DpnId {} is not naptSwitch for router {}",
395 long groupId = NatUtil.createGroupId(NatUtil.getGroupIdKey(routerName), idManager);
396 FlowEntity flowEntity = null;
398 flowEntity = naptSwitchHA.buildSnatFlowEntity(dpnId, routerName, groupId, routerVpnId,
399 NatConstants.DEL_FLOW);
400 if (flowEntity == null) {
401 LOG.error("removeSNATFromDPN : Failed to populate flowentity for router:{} "
402 + "with dpnId:{} groupId:{}", routerName, dpnId, groupId);
405 LOG.debug("removeSNATFromDPN : Removing default SNAT miss entry flow entity {}", flowEntity);
406 mdsalManager.removeFlowToTx(flowEntity, removeFlowInvTx);
408 } catch (Exception ex) {
409 LOG.error("removeSNATFromDPN : Failed to remove default SNAT miss entry flow entity {}",
413 LOG.debug("removeSNATFromDPN : Removed default SNAT miss entry flow for dpnID {} with routername {}",
417 GroupEntity groupEntity = null;
419 groupEntity = MDSALUtil.buildGroupEntity(dpnId, groupId, routerName,
420 GroupTypes.GroupAll, Collections.emptyList() /*listBucketInfo*/);
421 LOG.info("removeSNATFromDPN : Removing NAPT GroupEntity:{}", groupEntity);
422 mdsalManager.removeGroup(groupEntity);
423 } catch (Exception ex) {
424 LOG.error("removeSNATFromDPN : Failed to remove group entity {}", groupEntity, ex);
427 LOG.debug("removeSNATFromDPN : Removed default SNAT miss entry flow for dpnID {} with routerName {}",
430 naptSwitchHA.removeSnatFlowsInOldNaptSwitch(routerName, naptSwitch, externalIpLabel, removeFlowInvTx);
431 //remove table 26 flow ppointing to table46
432 FlowEntity flowEntity = null;
434 flowEntity = naptSwitchHA.buildSnatFlowEntityForNaptSwitch(dpnId, routerName, routerVpnId,
435 NatConstants.DEL_FLOW);
436 if (flowEntity == null) {
437 LOG.error("removeSNATFromDPN : Failed to populate flowentity for router {} with dpnId {}",
441 LOG.debug("removeSNATFromDPN : Removing default SNAT miss entry flow entity for router {} with "
442 + "dpnId {} in napt switch {}", routerName, dpnId, naptSwitch);
443 mdsalManager.removeFlowToTx(flowEntity, removeFlowInvTx);
445 } catch (Exception ex) {
446 LOG.error("removeSNATFromDPN : Failed to remove default SNAT miss entry flow entity {}",
450 LOG.debug("removeSNATFromDPN : Removed default SNAT miss entry flow for dpnID {} with routername {}",
453 //best effort to check IntExt model
454 naptSwitchHA.bestEffortDeletion(routerId, routerName, externalIpLabel, removeFlowInvTx);
456 } catch (Exception ex) {
457 LOG.error("removeSNATFromDPN : Exception while handling naptSwitch down for router {}", routerName, ex);
461 private void installDefaultNatRouteForRouterExternalSubnets(BigInteger dpnId, Collection<Uuid> externalSubnetIds) {
462 if (externalSubnetIds == null) {
463 LOG.error("installDefaultNatRouteForRouterExternalSubnets : No external subnets for router");
467 for (Uuid subnetId : externalSubnetIds) {
468 long vpnIdForSubnet = NatUtil.getExternalSubnetVpnId(dataBroker, subnetId);
469 if (vpnIdForSubnet != NatConstants.INVALID_ID) {
470 LOG.info("installDefaultNatRouteForRouterExternalSubnets : Installing default routes in FIB on dpn {} "
471 + "for subnetId {} with vpnId {}", dpnId, subnetId, vpnIdForSubnet);
472 snatDefaultRouteProgrammer.installDefNATRouteInDPN(dpnId, vpnIdForSubnet, subnetId.getValue(),
475 LOG.debug("installDefaultNatRouteForRouterExternalSubnets : No vpnID for subnet {} found", subnetId);