Router VNI as tun_id from Non-NAPT to NAPT
[netvirt.git] / vpnservice / natservice / 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 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;
36
37 public class RouterDpnChangeListener
38         extends AsyncDataTreeChangeListenerBase<DpnVpninterfacesList, RouterDpnChangeListener>
39         implements AutoCloseable {
40
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;
50
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;
65     }
66
67     @Override
68     public void init() {
69         LOG.info("{} init", getClass().getSimpleName());
70         registerListener(LogicalDatastoreType.OPERATIONAL, dataBroker);
71     }
72
73     @Override
74     protected RouterDpnChangeListener getDataTreeChangeListener() {
75         return RouterDpnChangeListener.this;
76     }
77
78     @Override
79     protected InstanceIdentifier<DpnVpninterfacesList> getWildCardPath() {
80         return InstanceIdentifier.create(NeutronRouterDpns.class).child(RouterDpnList.class)
81             .child(DpnVpninterfacesList.class);
82     }
83
84     @Override
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);
98                 Long vpnId;
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);
104                         return;
105                     }
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);
113                 } else {
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);
118                         return;
119                     }
120                     Long routId = NatUtil.getVpnId(dataBroker, routerId);
121                     if (routId == NatConstants.INVALID_ID) {
122                         LOG.error("Invalid routId returned for routerName {}", routerId);
123                         return;
124                     }
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);
132                 }
133                 extNetGroupInstaller.installExtNetGroupEntries(networkId, dpnId);
134
135                 if (router.isEnableSnat()) {
136                     LOG.info("On add - SNAT enabled for router {}", routerId);
137                     handleSNATForDPN(dpnId, routerId, vpnId);
138                 } else {
139                     LOG.info("SNAT is not enabled for router {} to handle addDPN event {}", routerId, dpnId);
140                 }
141             }
142         } else {
143             LOG.debug("Router {} is not associated with External network", routerId);
144         }
145     }
146
147     @Override
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);
161                 Long vpnId;
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);
167                         return;
168                     }
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);
173                 } else {
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);
178                         return;
179                     }
180                     Long routId = NatUtil.getVpnId(dataBroker, routerId);
181                     if (routId == NatConstants.INVALID_ID) {
182                         LOG.error("Invalid routId returned for routerName {}", routerId);
183                         return;
184                     }
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);
189                 }
190
191                 if (router.isEnableSnat()) {
192                     LOG.info("On remove - SNAT enabled for router {}", routerId);
193                     removeSNATFromDPN(dpnId, routerId, vpnId);
194                 } else {
195                     LOG.info("SNAT is not enabled for router {} to handle removeDPN event {}", routerId, dpnId);
196                 }
197             }
198         }
199     }
200
201     @Override
202     protected void update(InstanceIdentifier<DpnVpninterfacesList> identifier, DpnVpninterfacesList original,
203                           DpnVpninterfacesList update) {
204         LOG.trace("Update event - key: {}, original: {}, update: {}", identifier, original, update);
205     }
206
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;
214         try {
215             Long routerId = NatUtil.getVpnId(dataBroker, routerName);
216             if (routerId == NatConstants.INVALID_ID) {
217                 LOG.error("Invalid routerId returned for routerName {}", routerName);
218                 return;
219             }
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);
223
224                 naptSwitch = dpnId;
225                 boolean naptstatus = naptSwitchHA.updateNaptSwitch(routerName, naptSwitch);
226                 if (!naptstatus) {
227                     LOG.error("Failed to update newNaptSwitch {} for routername {}", naptSwitch, routerName);
228                     return;
229                 }
230                 LOG.debug("Switch {} is elected as NaptSwitch for router {}", dpnId, routerName);
231
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);
236                 }
237
238                 Routers extRouters = NatUtil.getRoutersFromConfigDS(dataBroker, routerName);
239                 if (extRouters != null) {
240                     NatUtil.createRouterIdsConfigDS(dataBroker, routerName);
241                     naptSwitchHA.subnetRegisterMapping(extRouters, routerId);
242                 }
243                 naptSwitchHA.installSnatFlows(routerName, routerId, naptSwitch, routerVpnId);
244
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);
250                     return;
251                 }
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);
258                 }
259             } else if (naptId.equals(dpnId)) {
260                 LOG.debug("NaptSwitch {} gone down during cluster reboot came alive", naptId);
261             } else {
262
263                 LOG.debug("Napt switch with Id {} is already elected for router {}", naptId, routerName);
264                 naptSwitch = naptId;
265
266                 //installing group
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);
271                     return;
272                 }
273                 naptSwitchHA.installSnatGroupEntry(dpnId, bucketInfo, routerName);
274
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);
282                     return;
283                 }
284                 LOG.debug("Successfully installed flow for dpnId {} router {} group {}", dpnId, routerName, groupId);
285                 mdsalManager.installFlow(flowEntity);
286             }
287         } catch (Exception ex) {
288             LOG.error("Exception in handleSNATForDPN method : {}", ex);
289         }
290     }
291
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
298
299         //get ExternalIpIn prior
300         List<String> externalIpCache;
301         //HashMap Label
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);
306             return;
307         }
308         externalIpCache = NatUtil.getExternalIpsForRouter(dataBroker, routerId);
309         ProviderTypes extNwProvType = NatEvpnUtil.getExtNwProvTypeFromRouterName(dataBroker, routerName);
310         if (extNwProvType == null) {
311             return;
312         }
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;
316         } else {
317             externalIpLabel = NatUtil.getExternalIpsLabelForRouter(dataBroker, routerId);
318         }
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);
322             return;
323         }
324         try {
325             boolean naptStatus =
326                 naptSwitchHA.isNaptSwitchDown(routerName, dpnId, naptSwitch, routerVpnId, externalIpCache);
327             if (!naptStatus) {
328                 LOG.debug("NaptSwitchDown: Switch with DpnId {} is not naptSwitch for router {}",
329                     dpnId, routerName);
330                 long groupId = NatUtil.createGroupId(NatUtil.getGroupIdKey(routerName), idManager);
331                 FlowEntity flowEntity = null;
332                 try {
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);
338                         return;
339                     }
340                     LOG.debug("NAT Service : Removing default SNAT miss entry flow entity {}", flowEntity);
341                     mdsalManager.removeFlow(flowEntity);
342
343                 } catch (Exception ex) {
344                     LOG.debug("NAT Service : Failed to remove default SNAT miss entry flow entity {} : {}",
345                         flowEntity, ex);
346                     return;
347                 }
348                 LOG.debug("NAT Service : Removed default SNAT miss entry flow for dpnID {} with routername {}",
349                     dpnId, routerName);
350
351                 //remove group
352                 GroupEntity groupEntity = null;
353                 try {
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);
360                     return;
361                 }
362                 LOG.debug("NAT Service : Removed default SNAT miss entry flow for dpnID {} with routerName {}",
363                     dpnId, routerName);
364             } else {
365                 naptSwitchHA.removeSnatFlowsInOldNaptSwitch(routerName, naptSwitch, externalIpLabel);
366                 //remove table 26 flow ppointing to table46
367                 FlowEntity flowEntity = null;
368                 try {
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);
373                         return;
374                     }
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);
378
379                 } catch (Exception ex) {
380                     LOG.debug("NAT Service : Failed to remove default SNAT miss entry flow entity {} : {}",
381                         flowEntity, ex);
382                     return;
383                 }
384                 LOG.debug("NAT Service : Removed default SNAT miss entry flow for dpnID {} with routername {}",
385                     dpnId, routerName);
386
387                 //best effort to check IntExt model
388                 naptSwitchHA.bestEffortDeletion(routerId, routerName, externalIpLabel);
389             }
390         } catch (Exception ex) {
391             LOG.debug("Exception while handling naptSwitch down for router {} : {}", routerName, ex);
392         }
393     }
394
395     private void installDefaultNatRouteForRouterExternalSubnets(BigInteger dpnId, List<Uuid> externalSubnetIds) {
396         if (externalSubnetIds == null) {
397             LOG.debug("NAT Service : No external subnets for router");
398             return;
399         }
400
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(),
407                         idManager);
408             } else {
409                 LOG.debug("NAT Service : No vpnID for subnet {} found", subnetId);
410             }
411         }
412     }
413 }