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;
12 import java.math.BigInteger;
13 import java.util.Collections;
14 import java.util.HashMap;
15 import java.util.List;
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.mdsalutil.BucketInfo;
24 import org.opendaylight.genius.mdsalutil.FlowEntity;
25 import org.opendaylight.genius.mdsalutil.GroupEntity;
26 import org.opendaylight.genius.mdsalutil.MDSALUtil;
27 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
28 import org.opendaylight.netvirt.elanmanager.api.IElanService;
29 import org.opendaylight.netvirt.natservice.api.SnatServiceManager;
30 import org.opendaylight.netvirt.neutronvpn.interfaces.INeutronVpnManager;
31 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.IdManagerService;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.GroupTypes;
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;
46 public class RouterDpnChangeListener
47 extends AsyncDataTreeChangeListenerBase<DpnVpninterfacesList, RouterDpnChangeListener>
48 implements AutoCloseable {
50 private static final Logger LOG = LoggerFactory.getLogger(RouterDpnChangeListener.class);
51 private final DataBroker dataBroker;
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 IElanService elanManager;
59 private SnatServiceManager natServiceManager;
60 private NatMode natMode = NatMode.Controller;
63 public RouterDpnChangeListener(final DataBroker dataBroker, final IMdsalApiManager mdsalManager,
64 final SNATDefaultRouteProgrammer snatDefaultRouteProgrammer,
65 final NaptSwitchHA naptSwitchHA,
66 final IdManagerService idManager,
67 final ExternalNetworkGroupInstaller extNetGroupInstaller,
68 final INeutronVpnManager nvpnManager,
69 final SnatServiceManager natServiceManager,
70 final NatserviceConfig config,
71 final IElanService elanManager) {
72 super(DpnVpninterfacesList.class, RouterDpnChangeListener.class);
73 this.dataBroker = dataBroker;
74 this.mdsalManager = mdsalManager;
75 this.snatDefaultRouteProgrammer = snatDefaultRouteProgrammer;
76 this.naptSwitchHA = naptSwitchHA;
77 this.idManager = idManager;
78 this.extNetGroupInstaller = extNetGroupInstaller;
79 this.nvpnManager = nvpnManager;
80 this.elanManager = elanManager;
81 this.natServiceManager = natServiceManager;
83 this.natMode = config.getNatMode();
90 LOG.info("{} init", getClass().getSimpleName());
91 registerListener(LogicalDatastoreType.OPERATIONAL, dataBroker);
95 protected RouterDpnChangeListener getDataTreeChangeListener() {
96 return RouterDpnChangeListener.this;
100 protected InstanceIdentifier<DpnVpninterfacesList> getWildCardPath() {
101 return InstanceIdentifier.create(NeutronRouterDpns.class).child(RouterDpnList.class)
102 .child(DpnVpninterfacesList.class);
106 protected void add(final InstanceIdentifier<DpnVpninterfacesList> identifier, final DpnVpninterfacesList dpnInfo) {
107 LOG.trace("Add event - key: {}, value: {}", identifier, dpnInfo);
108 final String routerId = identifier.firstKeyOf(RouterDpnList.class).getRouterId();
109 BigInteger dpnId = dpnInfo.getDpnId();
110 //check router is associated to external network
111 InstanceIdentifier<Routers> id = NatUtil.buildRouterIdentifier(routerId);
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 BigInteger naptSwitch = NatUtil.getPrimaryNaptfromRouterName(dataBroker, router.getRouterName());
121 if (naptSwitch == null || naptSwitch.equals(BigInteger.ZERO)) {
122 LOG.debug("NAPT switch is not selected.");
125 natServiceManager.notify(router, naptSwitch, dpnId,
126 SnatServiceManager.Action.SNAT_ROUTER_ENBL);
128 LOG.debug("Router {} is associated with ext nw {}", routerId, networkId);
129 Uuid vpnName = NatUtil.getVpnForRouter(dataBroker, routerId);
131 if (vpnName == null) {
132 LOG.debug("Internal vpn associated to router {}", routerId);
133 vpnId = NatUtil.getVpnId(dataBroker, routerId);
134 if (vpnId == NatConstants.INVALID_ID) {
135 LOG.error("Invalid vpnId returned for routerName {}", routerId);
138 LOG.debug("Retrieved vpnId {} for router {}", vpnId, routerId);
139 //Install default entry in FIB to SNAT table
140 LOG.info("Installing default route in FIB on dpn {} for router {} with vpn {}",
141 dpnId, routerId, vpnId);
142 installDefaultNatRouteForRouterExternalSubnets(dpnId,
143 NatUtil.getExternalSubnetIdsFromExternalIps(router.getExternalIps()));
144 snatDefaultRouteProgrammer.installDefNATRouteInDPN(dpnId, vpnId);
146 LOG.debug("External BGP vpn associated to router {}", routerId);
147 vpnId = NatUtil.getVpnId(dataBroker, vpnName.getValue());
148 if (vpnId == NatConstants.INVALID_ID) {
149 LOG.error("Invalid vpnId returned for routerName {}", routerId);
152 Long routId = NatUtil.getVpnId(dataBroker, routerId);
153 if (routId == NatConstants.INVALID_ID) {
154 LOG.error("Invalid routId returned for routerName {}", routerId);
157 LOG.debug("Retrieved vpnId {} for router {}", vpnId, routerId);
158 //Install default entry in FIB to SNAT table
159 LOG.debug("Installing default route in FIB on dpn {} for routerId {} with vpnId {}...",
160 dpnId, routerId, vpnId);
161 installDefaultNatRouteForRouterExternalSubnets(dpnId,
162 NatUtil.getExternalSubnetIdsFromExternalIps(router.getExternalIps()));
163 snatDefaultRouteProgrammer.installDefNATRouteInDPN(dpnId, vpnId, routId);
165 extNetGroupInstaller.installExtNetGroupEntries(networkId, dpnId);
167 if (router.isEnableSnat()) {
168 LOG.info("On add - SNAT enabled for router {}", routerId);
169 handleSNATForDPN(dpnId, routerId, vpnId);
171 LOG.info("SNAT is not enabled for router {} to handle addDPN event {}", routerId, dpnId);
176 LOG.debug("Router {} is not associated with External network", routerId);
181 protected void remove(InstanceIdentifier<DpnVpninterfacesList> identifier, DpnVpninterfacesList dpnInfo) {
182 LOG.trace("Remove event - key: {}, value: {}", identifier, dpnInfo);
183 final String routerId = identifier.firstKeyOf(RouterDpnList.class).getRouterId();
184 BigInteger dpnId = dpnInfo.getDpnId();
185 //check router is associated to external network
186 InstanceIdentifier<Routers> id = NatUtil.buildRouterIdentifier(routerId);
187 Optional<Routers> routerData =
188 SingleTransactionDataBroker.syncReadOptionalAndTreatReadFailedExceptionAsAbsentOptional(dataBroker,
189 LogicalDatastoreType.CONFIGURATION, id);
190 if (routerData.isPresent()) {
191 Routers router = routerData.get();
192 Uuid networkId = router.getNetworkId();
193 if (networkId != null) {
194 if (natMode == NatMode.Conntrack) {
195 BigInteger naptSwitch = NatUtil.getPrimaryNaptfromRouterName(dataBroker, router.getRouterName());
196 if (naptSwitch == null || naptSwitch.equals(BigInteger.ZERO)) {
197 LOG.debug("NAPT switch is not selected.");
200 natServiceManager.notify(router, naptSwitch, dpnId,
201 SnatServiceManager.Action.SNAT_ROUTER_DISBL);
203 LOG.debug("Router {} is associated with ext nw {}", routerId, networkId);
204 Uuid vpnName = NatUtil.getVpnForRouter(dataBroker, routerId);
206 if (vpnName == null) {
207 LOG.debug("Internal vpn associated to router {}", routerId);
208 vpnId = NatUtil.getVpnId(dataBroker, routerId);
209 if (vpnId == NatConstants.INVALID_ID) {
210 LOG.error("Invalid vpnId returned for routerName {}", routerId);
213 LOG.debug("Retrieved vpnId {} for router {}", vpnId, routerId);
214 //Remove default entry in FIB
215 LOG.debug("Removing default route in FIB on dpn {} for vpn {} ...", dpnId, vpnName);
216 snatDefaultRouteProgrammer.removeDefNATRouteInDPN(dpnId, vpnId);
218 LOG.debug("External vpn associated to router {}", routerId);
219 vpnId = NatUtil.getVpnId(dataBroker, vpnName.getValue());
220 if (vpnId == NatConstants.INVALID_ID) {
221 LOG.error("Invalid vpnId returned for routerName {}", routerId);
224 Long routId = NatUtil.getVpnId(dataBroker, routerId);
225 if (routId == NatConstants.INVALID_ID) {
226 LOG.error("Invalid routId returned for routerName {}", routerId);
229 LOG.debug("Retrieved vpnId {} for router {}", vpnId, routerId);
230 //Remove default entry in FIB
231 LOG.debug("Removing default route in FIB on dpn {} for vpn {} ...", dpnId, vpnName);
232 snatDefaultRouteProgrammer.removeDefNATRouteInDPN(dpnId, vpnId, routId);
235 if (router.isEnableSnat()) {
236 LOG.info("On remove - SNAT enabled for router {}", routerId);
237 removeSNATFromDPN(dpnId, routerId, vpnId);
239 LOG.info("SNAT is not enabled for router {} to handle removeDPN event {}", routerId, dpnId);
247 protected void update(InstanceIdentifier<DpnVpninterfacesList> identifier, DpnVpninterfacesList original,
248 DpnVpninterfacesList update) {
249 LOG.trace("Update event - key: {}, original: {}, update: {}", identifier, original, update);
252 // TODO Clean up the exception handling
253 @SuppressWarnings("checkstyle:IllegalCatch")
254 void handleSNATForDPN(BigInteger dpnId, String routerName, Long routerVpnId) {
255 //Check if primary and secondary switch are selected, If not select the role
256 //Install select group to NAPT switch
257 //Install default miss entry to NAPT switch
258 BigInteger naptSwitch;
260 Long routerId = NatUtil.getVpnId(dataBroker, routerName);
261 if (routerId == NatConstants.INVALID_ID) {
262 LOG.error("Invalid routerId returned for routerName {}", routerName);
265 BigInteger naptId = NatUtil.getPrimaryNaptfromRouterName(dataBroker, routerName);
266 if (naptId == null || naptId.equals(BigInteger.ZERO) || !naptSwitchHA.getSwitchStatus(naptId)) {
267 LOG.debug("No NaptSwitch is selected for router {}", routerName);
269 boolean naptstatus = naptSwitchHA.updateNaptSwitch(routerName, naptSwitch);
271 LOG.error("Failed to update newNaptSwitch {} for routername {}", naptSwitch, routerName);
274 LOG.debug("Switch {} is elected as NaptSwitch for router {}", dpnId, routerName);
276 // When NAPT switch is elected during first VM comes up for the given Router
277 if (elanManager.isOpenStackVniSemanticsEnforced()) {
278 NatOverVxlanUtil.validateAndCreateVxlanVniPool(dataBroker, nvpnManager,
279 idManager, NatConstants.ODL_VNI_POOL_NAME);
282 Routers extRouters = NatUtil.getRoutersFromConfigDS(dataBroker, routerName);
283 if (extRouters != null) {
284 NatUtil.createRouterIdsConfigDS(dataBroker, routerName);
285 naptSwitchHA.subnetRegisterMapping(extRouters, routerId);
288 naptSwitchHA.installSnatFlows(routerName, routerId, naptSwitch, routerVpnId);
290 // Install miss entry (table 26) pointing to table 46
291 FlowEntity flowEntity = naptSwitchHA.buildSnatFlowEntityForNaptSwitch(dpnId, routerName,
292 routerVpnId, NatConstants.ADD_FLOW);
293 if (flowEntity == null) {
294 LOG.debug("Failed to populate flowentity for router {} with dpnId {}", routerName, dpnId);
297 LOG.debug("Successfully installed flow for dpnId {} router {}", dpnId, routerName);
298 mdsalManager.installFlow(flowEntity);
299 //Removing primary flows from old napt switch
300 if (naptId != null && !naptId.equals(BigInteger.ZERO)) {
301 LOG.debug("Removing primary flows from old napt switch {} for router {}", naptId, routerName);
302 naptSwitchHA.removeSnatFlowsInOldNaptSwitch(routerName, naptId, null);
304 } else if (naptId.equals(dpnId)) {
305 LOG.debug("NaptSwitch {} gone down during cluster reboot came alive", naptId);
308 LOG.debug("Napt switch with Id {} is already elected for router {}", naptId, routerName);
312 List<BucketInfo> bucketInfo = naptSwitchHA.handleGroupInNeighborSwitches(dpnId,
313 routerName, naptSwitch);
314 if (bucketInfo == null) {
315 LOG.debug("Failed to populate bucketInfo for dpnId {} routername {} naptSwitch {}",
316 dpnId, routerName, naptSwitch);
319 naptSwitchHA.installSnatGroupEntry(dpnId, bucketInfo, routerName);
321 // Install miss entry (table 26) pointing to group
322 long groupId = NatUtil.createGroupId(NatUtil.getGroupIdKey(routerName), idManager);
323 FlowEntity flowEntity =
324 naptSwitchHA.buildSnatFlowEntity(dpnId, routerName, groupId,
325 routerVpnId, NatConstants.ADD_FLOW);
326 if (flowEntity == null) {
327 LOG.debug("Failed to populate flowentity for router {} with dpnId {} groupId {}",
328 routerName, dpnId, groupId);
331 LOG.debug("Successfully installed flow for dpnId {} router {} group {}",
332 dpnId, routerName, groupId);
333 mdsalManager.installFlow(flowEntity);
336 } catch (Exception ex) {
337 LOG.error("Exception in handleSNATForDPN method : {}", ex);
341 // TODO Clean up the exception handling
342 @SuppressWarnings("checkstyle:IllegalCatch")
343 void removeSNATFromDPN(BigInteger dpnId, String routerName, long routerVpnId) {
344 //irrespective of naptswitch or non-naptswitch, SNAT default miss entry need to be removed
345 //remove miss entry to NAPT switch
346 //if naptswitch elect new switch and install Snat flows and remove those flows in oldnaptswitch
348 //get ExternalIpIn prior
349 List<String> externalIpCache;
351 HashMap<String, Long> externalIpLabel;
352 Long routerId = NatUtil.getVpnId(dataBroker, routerName);
353 if (routerId == NatConstants.INVALID_ID) {
354 LOG.error("Invalid routerId returned for routerName {}", routerName);
357 externalIpCache = NatUtil.getExternalIpsForRouter(dataBroker, routerId);
358 ProviderTypes extNwProvType = NatEvpnUtil.getExtNwProvTypeFromRouterName(dataBroker, routerName);
359 if (extNwProvType == null) {
362 //Get the external IP labels other than VXLAN provider type. Since label is not applicable for VXLAN
363 if (extNwProvType == ProviderTypes.VXLAN) {
364 externalIpLabel = null;
366 externalIpLabel = NatUtil.getExternalIpsLabelForRouter(dataBroker, routerId);
368 BigInteger naptSwitch = NatUtil.getPrimaryNaptfromRouterName(dataBroker, routerName);
369 if (naptSwitch == null || naptSwitch.equals(BigInteger.ZERO)) {
370 LOG.debug("No naptSwitch is selected for router {}", routerName);
375 naptSwitchHA.isNaptSwitchDown(routerName, dpnId, naptSwitch, routerVpnId, externalIpCache);
377 LOG.debug("NaptSwitchDown: Switch with DpnId {} is not naptSwitch for router {}",
379 long groupId = NatUtil.createGroupId(NatUtil.getGroupIdKey(routerName), idManager);
380 FlowEntity flowEntity = null;
382 flowEntity = naptSwitchHA.buildSnatFlowEntity(dpnId, routerName, groupId, routerVpnId,
383 NatConstants.DEL_FLOW);
384 if (flowEntity == null) {
385 LOG.debug("Failed to populate flowentity for router {} with dpnId {} groupIs {}",
386 routerName, dpnId, groupId);
389 LOG.debug("NAT Service : Removing default SNAT miss entry flow entity {}", flowEntity);
390 mdsalManager.removeFlow(flowEntity);
392 } catch (Exception ex) {
393 LOG.debug("NAT Service : Failed to remove default SNAT miss entry flow entity {} : {}",
397 LOG.debug("NAT Service : Removed default SNAT miss entry flow for dpnID {} with routername {}",
401 GroupEntity groupEntity = null;
403 groupEntity = MDSALUtil.buildGroupEntity(dpnId, groupId, routerName,
404 GroupTypes.GroupAll, Collections.emptyList() /*listBucketInfo*/);
405 LOG.info("NAT Service : Removing NAPT GroupEntity:{}", groupEntity);
406 mdsalManager.removeGroup(groupEntity);
407 } catch (Exception ex) {
408 LOG.debug("NAT Service : Failed to remove group entity {} : {}", groupEntity, ex);
411 LOG.debug("NAT Service : Removed default SNAT miss entry flow for dpnID {} with routerName {}",
414 naptSwitchHA.removeSnatFlowsInOldNaptSwitch(routerName, naptSwitch, externalIpLabel);
415 //remove table 26 flow ppointing to table46
416 FlowEntity flowEntity = null;
418 flowEntity = naptSwitchHA.buildSnatFlowEntityForNaptSwitch(dpnId, routerName, routerVpnId,
419 NatConstants.DEL_FLOW);
420 if (flowEntity == null) {
421 LOG.debug("Failed to populate flowentity for router {} with dpnId {}", routerName, dpnId);
424 LOG.debug("NAT Service : Removing default SNAT miss entry flow entity for router {} with "
425 + "dpnId {} in napt switch {}", routerName, dpnId, naptSwitch);
426 mdsalManager.removeFlow(flowEntity);
428 } catch (Exception ex) {
429 LOG.debug("NAT Service : Failed to remove default SNAT miss entry flow entity {} : {}",
433 LOG.debug("NAT Service : Removed default SNAT miss entry flow for dpnID {} with routername {}",
436 //best effort to check IntExt model
437 naptSwitchHA.bestEffortDeletion(routerId, routerName, externalIpLabel);
439 } catch (Exception ex) {
440 LOG.debug("Exception while handling naptSwitch down for router {} : {}", routerName, ex);
444 private void installDefaultNatRouteForRouterExternalSubnets(BigInteger dpnId, List<Uuid> externalSubnetIds) {
445 if (externalSubnetIds == null) {
446 LOG.debug("NAT Service : No external subnets for router");
450 for (Uuid subnetId : externalSubnetIds) {
451 long vpnIdForSubnet = NatUtil.getExternalSubnetVpnId(dataBroker, subnetId);
452 if (vpnIdForSubnet != NatConstants.INVALID_ID) {
453 LOG.info("NAT Service : Installing default routes in FIB on dpn {} for subnetId {} with vpnId {}",
454 dpnId, subnetId, vpnIdForSubnet);
455 snatDefaultRouteProgrammer.installDefNATRouteInDPN(dpnId, vpnIdForSubnet, subnetId.getValue(),
458 LOG.debug("NAT Service : No vpnID for subnet {} found", subnetId);