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 java.math.BigInteger;
12 import java.util.HashMap;
13 import java.util.List;
14 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
15 import org.opendaylight.controller.md.sal.binding.api.DataChangeListener;
16 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
17 import org.opendaylight.genius.datastoreutils.AsyncDataTreeChangeListenerBase;
18 import org.opendaylight.genius.mdsalutil.BucketInfo;
19 import org.opendaylight.genius.mdsalutil.FlowEntity;
20 import org.opendaylight.genius.mdsalutil.GroupEntity;
21 import org.opendaylight.genius.mdsalutil.MDSALUtil;
22 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
23 import org.opendaylight.netvirt.neutronvpn.interfaces.INeutronVpnManager;
24 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
25 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.IdManagerService;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.GroupTypes;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.NeutronRouterDpns;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.neutron.router.dpns.RouterDpnList;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.neutron.router.dpns.router.dpn.list.DpnVpninterfacesList;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ProviderTypes;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ext.routers.Routers;
32 import org.opendaylight.yangtools.concepts.ListenerRegistration;
33 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
34 import org.slf4j.Logger;
35 import org.slf4j.LoggerFactory;
37 public class RouterDpnChangeListener
38 extends AsyncDataTreeChangeListenerBase<DpnVpninterfacesList, RouterDpnChangeListener>
39 implements AutoCloseable {
41 private static final Logger LOG = LoggerFactory.getLogger(RouterDpnChangeListener.class);
42 private ListenerRegistration<DataChangeListener> listenerRegistration;
43 private final DataBroker dataBroker;
44 private final IMdsalApiManager mdsalManager;
45 private final SNATDefaultRouteProgrammer snatDefaultRouteProgrammer;
46 private final NaptSwitchHA naptSwitchHA;
47 private final IdManagerService idManager;
48 private final INeutronVpnManager nvpnManager;
49 private final ExternalNetworkGroupInstaller extNetGroupInstaller;
51 public RouterDpnChangeListener(final DataBroker dataBroker, final IMdsalApiManager mdsalManager,
52 final SNATDefaultRouteProgrammer snatDefaultRouteProgrammer,
53 final NaptSwitchHA naptSwitchHA,
54 final IdManagerService idManager,
55 final ExternalNetworkGroupInstaller extNetGroupInstaller,
56 final INeutronVpnManager nvpnManager) {
57 super(DpnVpninterfacesList.class, RouterDpnChangeListener.class);
58 this.dataBroker = dataBroker;
59 this.mdsalManager = mdsalManager;
60 this.snatDefaultRouteProgrammer = snatDefaultRouteProgrammer;
61 this.naptSwitchHA = naptSwitchHA;
62 this.idManager = idManager;
63 this.extNetGroupInstaller = extNetGroupInstaller;
64 this.nvpnManager = nvpnManager;
69 LOG.info("{} init", getClass().getSimpleName());
70 registerListener(LogicalDatastoreType.OPERATIONAL, dataBroker);
74 protected RouterDpnChangeListener getDataTreeChangeListener() {
75 return RouterDpnChangeListener.this;
79 protected InstanceIdentifier<DpnVpninterfacesList> getWildCardPath() {
80 return InstanceIdentifier.create(NeutronRouterDpns.class).child(RouterDpnList.class)
81 .child(DpnVpninterfacesList.class);
85 protected void add(final InstanceIdentifier<DpnVpninterfacesList> identifier, final DpnVpninterfacesList dpnInfo) {
86 LOG.trace("Add event - key: {}, value: {}", identifier, dpnInfo);
87 final String routerId = identifier.firstKeyOf(RouterDpnList.class).getRouterId();
88 BigInteger dpnId = dpnInfo.getDpnId();
89 //check router is associated to external network
90 InstanceIdentifier<Routers> id = NatUtil.buildRouterIdentifier(routerId);
91 Optional<Routers> routerData = NatUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, id);
92 if (routerData.isPresent()) {
93 Routers router = routerData.get();
94 Uuid networkId = router.getNetworkId();
95 if (networkId != null) {
96 LOG.debug("Router {} is associated with ext nw {}", routerId, networkId);
97 Uuid vpnName = NatUtil.getVpnForRouter(dataBroker, routerId);
99 if (vpnName == null) {
100 LOG.debug("Internal vpn associated to router {}", routerId);
101 vpnId = NatUtil.getVpnId(dataBroker, routerId);
102 if (vpnId == NatConstants.INVALID_ID) {
103 LOG.error("Invalid vpnId returned for routerName {}", routerId);
106 LOG.debug("Retrieved vpnId {} for router {}", vpnId, routerId);
107 //Install default entry in FIB to SNAT table
108 LOG.info("Installing default route in FIB on dpn {} for router {} with vpn {}",
109 dpnId, routerId, vpnId);
110 installDefaultNatRouteForRouterExternalSubnets(dpnId,
111 NatUtil.getExternalSubnetIdsFromExternalIps(router.getExternalIps()));
112 snatDefaultRouteProgrammer.installDefNATRouteInDPN(dpnId, vpnId);
114 LOG.debug("External BGP vpn associated to router {}", routerId);
115 vpnId = NatUtil.getVpnId(dataBroker, vpnName.getValue());
116 if (vpnId == NatConstants.INVALID_ID) {
117 LOG.error("Invalid vpnId returned for routerName {}", routerId);
120 Long routId = NatUtil.getVpnId(dataBroker, routerId);
121 if (routId == NatConstants.INVALID_ID) {
122 LOG.error("Invalid routId returned for routerName {}", routerId);
125 LOG.debug("Retrieved vpnId {} for router {}", vpnId, routerId);
126 //Install default entry in FIB to SNAT table
127 LOG.debug("Installing default route in FIB on dpn {} for routerId {} with vpnId {}...",
128 dpnId, routerId, vpnId);
129 installDefaultNatRouteForRouterExternalSubnets(dpnId,
130 NatUtil.getExternalSubnetIdsFromExternalIps(router.getExternalIps()));
131 snatDefaultRouteProgrammer.installDefNATRouteInDPN(dpnId, vpnId, routId);
133 extNetGroupInstaller.installExtNetGroupEntries(networkId, dpnId);
135 if (router.isEnableSnat()) {
136 LOG.info("On add - SNAT enabled for router {}", routerId);
137 handleSNATForDPN(dpnId, routerId, vpnId);
139 LOG.info("SNAT is not enabled for router {} to handle addDPN event {}", routerId, dpnId);
143 LOG.debug("Router {} is not associated with External network", routerId);
148 protected void remove(InstanceIdentifier<DpnVpninterfacesList> identifier, DpnVpninterfacesList dpnInfo) {
149 LOG.trace("Remove event - key: {}, value: {}", identifier, dpnInfo);
150 final String routerId = identifier.firstKeyOf(RouterDpnList.class).getRouterId();
151 BigInteger dpnId = dpnInfo.getDpnId();
152 //check router is associated to external network
153 InstanceIdentifier<Routers> id = NatUtil.buildRouterIdentifier(routerId);
154 Optional<Routers> routerData = NatUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, id);
155 if (routerData.isPresent()) {
156 Routers router = routerData.get();
157 Uuid networkId = router.getNetworkId();
158 if (networkId != null) {
159 LOG.debug("Router {} is associated with ext nw {}", routerId, networkId);
160 Uuid vpnName = NatUtil.getVpnForRouter(dataBroker, routerId);
162 if (vpnName == null) {
163 LOG.debug("Internal vpn associated to router {}", routerId);
164 vpnId = NatUtil.getVpnId(dataBroker, routerId);
165 if (vpnId == NatConstants.INVALID_ID) {
166 LOG.error("Invalid vpnId returned for routerName {}", routerId);
169 LOG.debug("Retrieved vpnId {} for router {}", vpnId, routerId);
170 //Remove default entry in FIB
171 LOG.debug("Removing default route in FIB on dpn {} for vpn {} ...", dpnId, vpnName);
172 snatDefaultRouteProgrammer.removeDefNATRouteInDPN(dpnId, vpnId);
174 LOG.debug("External vpn associated to router {}", routerId);
175 vpnId = NatUtil.getVpnId(dataBroker, vpnName.getValue());
176 if (vpnId == NatConstants.INVALID_ID) {
177 LOG.error("Invalid vpnId returned for routerName {}", routerId);
180 Long routId = NatUtil.getVpnId(dataBroker, routerId);
181 if (routId == NatConstants.INVALID_ID) {
182 LOG.error("Invalid routId returned for routerName {}", routerId);
185 LOG.debug("Retrieved vpnId {} for router {}", vpnId, routerId);
186 //Remove default entry in FIB
187 LOG.debug("Removing default route in FIB on dpn {} for vpn {} ...", dpnId, vpnName);
188 snatDefaultRouteProgrammer.removeDefNATRouteInDPN(dpnId, vpnId, routId);
191 if (router.isEnableSnat()) {
192 LOG.info("On remove - SNAT enabled for router {}", routerId);
193 removeSNATFromDPN(dpnId, routerId, vpnId);
195 LOG.info("SNAT is not enabled for router {} to handle removeDPN event {}", routerId, dpnId);
202 protected void update(InstanceIdentifier<DpnVpninterfacesList> identifier, DpnVpninterfacesList original,
203 DpnVpninterfacesList update) {
204 LOG.trace("Update event - key: {}, original: {}, update: {}", identifier, original, update);
207 // TODO Clean up the exception handling
208 @SuppressWarnings("checkstyle:IllegalCatch")
209 void handleSNATForDPN(BigInteger dpnId, String routerName, Long routerVpnId) {
210 //Check if primary and secondary switch are selected, If not select the role
211 //Install select group to NAPT switch
212 //Install default miss entry to NAPT switch
213 BigInteger naptSwitch;
215 Long routerId = NatUtil.getVpnId(dataBroker, routerName);
216 if (routerId == NatConstants.INVALID_ID) {
217 LOG.error("Invalid routerId returned for routerName {}", routerName);
220 BigInteger naptId = NatUtil.getPrimaryNaptfromRouterName(dataBroker, routerName);
221 if (naptId == null || naptId.equals(BigInteger.ZERO) || !naptSwitchHA.getSwitchStatus(naptId)) {
222 LOG.debug("No NaptSwitch is selected for router {}", routerName);
225 boolean naptstatus = naptSwitchHA.updateNaptSwitch(routerName, naptSwitch);
227 LOG.error("Failed to update newNaptSwitch {} for routername {}", naptSwitch, routerName);
230 LOG.debug("Switch {} is elected as NaptSwitch for router {}", dpnId, routerName);
232 // When NAPT switch is elected during first VM comes up for the given Router
233 if (nvpnManager.getEnforceOpenstackSemanticsConfig()) {
234 NatOverVxlanUtil.validateAndCreateVxlanVniPool(dataBroker, nvpnManager,
235 idManager, NatConstants.ODL_VNI_POOL_NAME);
238 Routers extRouters = NatUtil.getRoutersFromConfigDS(dataBroker, routerName);
239 if (extRouters != null) {
240 NatUtil.createRouterIdsConfigDS(dataBroker, routerName);
241 naptSwitchHA.subnetRegisterMapping(extRouters, routerId);
243 naptSwitchHA.installSnatFlows(routerName, routerId, naptSwitch, routerVpnId);
245 // Install miss entry (table 26) pointing to table 46
246 FlowEntity flowEntity = naptSwitchHA.buildSnatFlowEntityForNaptSwitch(dpnId, routerName,
247 routerVpnId, NatConstants.ADD_FLOW);
248 if (flowEntity == null) {
249 LOG.debug("Failed to populate flowentity for router {} with dpnId {}", routerName, dpnId);
252 LOG.debug("Successfully installed flow for dpnId {} router {}", dpnId, routerName);
253 mdsalManager.installFlow(flowEntity);
254 //Removing primary flows from old napt switch
255 if (naptId != null && !naptId.equals(BigInteger.ZERO)) {
256 LOG.debug("Removing primary flows from old napt switch {} for router {}", naptId, routerName);
257 naptSwitchHA.removeSnatFlowsInOldNaptSwitch(routerName, naptId, null);
259 } else if (naptId.equals(dpnId)) {
260 LOG.debug("NaptSwitch {} gone down during cluster reboot came alive", naptId);
263 LOG.debug("Napt switch with Id {} is already elected for router {}", naptId, routerName);
267 List<BucketInfo> bucketInfo = naptSwitchHA.handleGroupInNeighborSwitches(dpnId, routerName, naptSwitch);
268 if (bucketInfo == null) {
269 LOG.debug("Failed to populate bucketInfo for dpnId {} routername {} naptSwitch {}",
270 dpnId, routerName, naptSwitch);
273 naptSwitchHA.installSnatGroupEntry(dpnId, bucketInfo, routerName);
275 // Install miss entry (table 26) pointing to group
276 long groupId = NatUtil.createGroupId(NatUtil.getGroupIdKey(routerName), idManager);
277 FlowEntity flowEntity =
278 naptSwitchHA.buildSnatFlowEntity(dpnId, routerName, groupId, routerVpnId, NatConstants.ADD_FLOW);
279 if (flowEntity == null) {
280 LOG.debug("Failed to populate flowentity for router {} with dpnId {} groupId {}",
281 routerName, dpnId, groupId);
284 LOG.debug("Successfully installed flow for dpnId {} router {} group {}", dpnId, routerName, groupId);
285 mdsalManager.installFlow(flowEntity);
287 } catch (Exception ex) {
288 LOG.error("Exception in handleSNATForDPN method : {}", ex);
292 // TODO Clean up the exception handling
293 @SuppressWarnings("checkstyle:IllegalCatch")
294 void removeSNATFromDPN(BigInteger dpnId, String routerName, long routerVpnId) {
295 //irrespective of naptswitch or non-naptswitch, SNAT default miss entry need to be removed
296 //remove miss entry to NAPT switch
297 //if naptswitch elect new switch and install Snat flows and remove those flows in oldnaptswitch
299 //get ExternalIpIn prior
300 List<String> externalIpCache;
302 HashMap<String, Long> externalIpLabel;
303 Long routerId = NatUtil.getVpnId(dataBroker, routerName);
304 if (routerId == NatConstants.INVALID_ID) {
305 LOG.error("Invalid routerId returned for routerName {}", routerName);
308 externalIpCache = NatUtil.getExternalIpsForRouter(dataBroker, routerId);
309 ProviderTypes extNwProvType = NatEvpnUtil.getExtNwProvTypeFromRouterName(dataBroker, routerName);
310 if (extNwProvType == null) {
313 //Get the external IP labels other than VXLAN provider type. Since label is not applicable for VXLAN
314 if (extNwProvType == ProviderTypes.VXLAN) {
315 externalIpLabel = null;
317 externalIpLabel = NatUtil.getExternalIpsLabelForRouter(dataBroker, routerId);
319 BigInteger naptSwitch = NatUtil.getPrimaryNaptfromRouterName(dataBroker, routerName);
320 if (naptSwitch == null || naptSwitch.equals(BigInteger.ZERO)) {
321 LOG.debug("No naptSwitch is selected for router {}", routerName);
326 naptSwitchHA.isNaptSwitchDown(routerName, dpnId, naptSwitch, routerVpnId, externalIpCache);
328 LOG.debug("NaptSwitchDown: Switch with DpnId {} is not naptSwitch for router {}",
330 long groupId = NatUtil.createGroupId(NatUtil.getGroupIdKey(routerName), idManager);
331 FlowEntity flowEntity = null;
333 flowEntity = naptSwitchHA.buildSnatFlowEntity(dpnId, routerName, groupId, routerVpnId,
334 NatConstants.DEL_FLOW);
335 if (flowEntity == null) {
336 LOG.debug("Failed to populate flowentity for router {} with dpnId {} groupIs {}",
337 routerName, dpnId, groupId);
340 LOG.debug("NAT Service : Removing default SNAT miss entry flow entity {}", flowEntity);
341 mdsalManager.removeFlow(flowEntity);
343 } catch (Exception ex) {
344 LOG.debug("NAT Service : Failed to remove default SNAT miss entry flow entity {} : {}",
348 LOG.debug("NAT Service : Removed default SNAT miss entry flow for dpnID {} with routername {}",
352 GroupEntity groupEntity = null;
354 groupEntity = MDSALUtil.buildGroupEntity(dpnId, groupId, routerName,
355 GroupTypes.GroupAll, null);
356 LOG.info("NAT Service : Removing NAPT GroupEntity:{}", groupEntity);
357 mdsalManager.removeGroup(groupEntity);
358 } catch (Exception ex) {
359 LOG.debug("NAT Service : Failed to remove group entity {} : {}", groupEntity, ex);
362 LOG.debug("NAT Service : Removed default SNAT miss entry flow for dpnID {} with routerName {}",
365 naptSwitchHA.removeSnatFlowsInOldNaptSwitch(routerName, naptSwitch, externalIpLabel);
366 //remove table 26 flow ppointing to table46
367 FlowEntity flowEntity = null;
369 flowEntity = naptSwitchHA.buildSnatFlowEntityForNaptSwitch(dpnId, routerName, routerVpnId,
370 NatConstants.DEL_FLOW);
371 if (flowEntity == null) {
372 LOG.debug("Failed to populate flowentity for router {} with dpnId {}", routerName, dpnId);
375 LOG.debug("NAT Service : Removing default SNAT miss entry flow entity for router {} with "
376 + "dpnId {} in napt switch {}", routerName, dpnId, naptSwitch);
377 mdsalManager.removeFlow(flowEntity);
379 } catch (Exception ex) {
380 LOG.debug("NAT Service : Failed to remove default SNAT miss entry flow entity {} : {}",
384 LOG.debug("NAT Service : Removed default SNAT miss entry flow for dpnID {} with routername {}",
387 //best effort to check IntExt model
388 naptSwitchHA.bestEffortDeletion(routerId, routerName, externalIpLabel);
390 } catch (Exception ex) {
391 LOG.debug("Exception while handling naptSwitch down for router {} : {}", routerName, ex);
395 private void installDefaultNatRouteForRouterExternalSubnets(BigInteger dpnId, List<Uuid> externalSubnetIds) {
396 if (externalSubnetIds == null) {
397 LOG.debug("NAT Service : No external subnets for router");
401 for (Uuid subnetId : externalSubnetIds) {
402 long vpnIdForSubnet = NatUtil.getExternalSubnetVpnId(dataBroker, subnetId);
403 if (vpnIdForSubnet != NatConstants.INVALID_ID) {
404 LOG.info("NAT Service : Installing default routes in FIB on dpn {} for subnetId {} with vpnId {}",
405 dpnId, subnetId, vpnIdForSubnet);
406 snatDefaultRouteProgrammer.installDefNATRouteInDPN(dpnId, vpnIdForSubnet, subnetId.getValue(),
409 LOG.debug("NAT Service : No vpnID for subnet {} found", subnetId);