Bug 6717 - Output to external network group entry is not installed on NAPT FIB table...
[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.AsyncDataBroker;
17 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
18 import org.opendaylight.genius.mdsalutil.AbstractDataChangeListener;
19 import org.opendaylight.genius.mdsalutil.BucketInfo;
20 import org.opendaylight.genius.mdsalutil.FlowEntity;
21 import org.opendaylight.genius.mdsalutil.GroupEntity;
22 import org.opendaylight.genius.mdsalutil.MDSALUtil;
23 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
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.ext.routers.Routers;
31 import org.opendaylight.yangtools.concepts.ListenerRegistration;
32 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
33 import org.slf4j.Logger;
34 import org.slf4j.LoggerFactory;
35
36 public class RouterDpnChangeListener extends AbstractDataChangeListener<DpnVpninterfacesList> implements AutoCloseable{
37     private static final Logger LOG = LoggerFactory.getLogger(RouterDpnChangeListener.class);
38     private ListenerRegistration<DataChangeListener> listenerRegistration;
39     private final DataBroker dataBroker;
40     private final IMdsalApiManager mdsalManager;
41     private final SNATDefaultRouteProgrammer snatDefaultRouteProgrammer;
42     private final NaptSwitchHA naptSwitchHA;
43     private final IdManagerService idManager;
44     private final ExternalNetworkGroupInstaller extNetGroupInstaller;
45
46     public RouterDpnChangeListener(final DataBroker dataBroker, final IMdsalApiManager mdsalManager,
47                                    final SNATDefaultRouteProgrammer snatDefaultRouteProgrammer,
48                                    final NaptSwitchHA naptSwitchHA,
49                                    final IdManagerService idManager,
50                                    final ExternalNetworkGroupInstaller extNetGroupInstaller) {
51         super(DpnVpninterfacesList.class);
52         this.dataBroker = dataBroker;
53         this.mdsalManager = mdsalManager;
54         this.snatDefaultRouteProgrammer = snatDefaultRouteProgrammer;
55         this.naptSwitchHA = naptSwitchHA;
56         this.idManager = idManager;
57         this.extNetGroupInstaller = extNetGroupInstaller;
58     }
59
60     public void init() {
61         LOG.info("{} init", getClass().getSimpleName());
62         listenerRegistration = dataBroker.registerDataChangeListener(LogicalDatastoreType.OPERATIONAL,
63                 getWildCardPath(), this, AsyncDataBroker.DataChangeScope.SUBTREE);
64     }
65
66     private InstanceIdentifier<DpnVpninterfacesList> getWildCardPath() {
67         return InstanceIdentifier.create(NeutronRouterDpns.class).child(RouterDpnList.class).child(DpnVpninterfacesList.class);
68     }
69
70     @Override
71     public void close() throws Exception {
72         if (listenerRegistration != null) {
73             listenerRegistration.close();
74             listenerRegistration = null;
75         }
76         LOG.info("{} close", getClass().getSimpleName());
77     }
78
79     @Override
80     protected void add(final InstanceIdentifier<DpnVpninterfacesList> identifier, final DpnVpninterfacesList dpnInfo) {
81         LOG.trace("Add event - key: {}, value: {}", identifier, dpnInfo);
82         final String routerId = identifier.firstKeyOf(RouterDpnList.class).getRouterId();
83         BigInteger dpnId = dpnInfo.getDpnId();
84         //check router is associated to external network
85         InstanceIdentifier<Routers> id = NatUtil.buildRouterIdentifier(routerId);
86         Optional<Routers> routerData = NatUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, id);
87         if (routerData.isPresent()) {
88             Uuid networkId = routerData.get().getNetworkId();
89             if(networkId != null) {
90                 LOG.debug("Router {} is associated with ext nw {}", routerId, networkId);
91                 Uuid vpnName = NatUtil.getVpnForRouter(dataBroker,routerId);
92                 Long vpnId;
93                 if (vpnName == null) {
94                     LOG.debug("Internal vpn associated to router {}",routerId);
95                     vpnId = NatUtil.getVpnId(dataBroker,routerId);
96                     if (vpnId == NatConstants.INVALID_ID) {
97                         LOG.error("Invalid vpnId returned for routerName {}",routerId);
98                         return;
99                     }
100                     LOG.debug("Retrieved vpnId {} for router {}",vpnId,routerId);
101                     //Install default entry in FIB to SNAT table
102                     LOG.debug("Installing default route in FIB on dpn {} for router {} with vpn {}...", dpnId,routerId,vpnId);
103                     snatDefaultRouteProgrammer.installDefNATRouteInDPN(dpnId, vpnId);
104                 } else {
105                     LOG.debug("External BGP vpn associated to router {}",routerId);
106                     vpnId = NatUtil.getVpnId(dataBroker, vpnName.getValue());
107                     if (vpnId == NatConstants.INVALID_ID) {
108                         LOG.error("Invalid vpnId returned for routerName {}", routerId);
109                         return;
110                     }
111                     Long routId = NatUtil.getVpnId(dataBroker, routerId);
112                     if (routId == NatConstants.INVALID_ID) {
113                         LOG.error("Invalid routId returned for routerName {}",routerId);
114                         return;
115                     }
116                     LOG.debug("Retrieved vpnId {} for router {}",vpnId,routerId);
117                     //Install default entry in FIB to SNAT table
118                     LOG.debug("Installing default route in FIB on dpn {} for routerId {} with vpnId {}...", dpnId,routerId,vpnId);
119                     snatDefaultRouteProgrammer.installDefNATRouteInDPN(dpnId, vpnId, routId);
120                 }
121                 extNetGroupInstaller.installExtNetGroupEntries(networkId, dpnId);
122
123                 if (routerData.get().isEnableSnat()) {
124                     LOG.info("SNAT enabled for router {}", routerId);
125                     handleSNATForDPN(dpnId, routerId ,vpnId);
126                 } else {
127                     LOG.info("SNAT is not enabled for router {} to handle addDPN event {}", routerId, dpnId);
128                 }
129             }
130         } else {
131             LOG.debug("Router {} is not associated with External network", routerId);
132         }
133     }
134
135     @Override
136     protected void remove(InstanceIdentifier<DpnVpninterfacesList> identifier, DpnVpninterfacesList dpnInfo) {
137         LOG.trace("Remove event - key: {}, value: {}", identifier, dpnInfo);
138         final String routerId = identifier.firstKeyOf(RouterDpnList.class).getRouterId();
139         BigInteger dpnId = dpnInfo.getDpnId();
140         //check router is associated to external network
141         InstanceIdentifier<Routers> id = NatUtil.buildRouterIdentifier(routerId);
142         Optional<Routers> routerData = NatUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, id);
143         if (routerData.isPresent()) {
144             Uuid networkId = routerData.get().getNetworkId();
145             if (networkId != null) {
146                 LOG.debug("Router {} is associated with ext nw {}", routerId, networkId);
147                 Uuid vpnName = NatUtil.getVpnForRouter(dataBroker, routerId);
148                 Long vpnId;
149                 if (vpnName == null) {
150                     LOG.debug("Internal vpn associated to router {}", routerId);
151                     vpnId = NatUtil.getVpnId(dataBroker, routerId);
152                     if (vpnId == NatConstants.INVALID_ID) {
153                         LOG.error("Invalid vpnId returned for routerName {}", routerId);
154                         return;
155                     }
156                     LOG.debug("Retrieved vpnId {} for router {}",vpnId,routerId);
157                     //Remove default entry in FIB
158                     LOG.debug("Removing default route in FIB on dpn {} for vpn {} ...", dpnId, vpnName);
159                     snatDefaultRouteProgrammer.removeDefNATRouteInDPN(dpnId, vpnId);
160                 } else {
161                     LOG.debug("External vpn associated to router {}", routerId);
162                     vpnId = NatUtil.getVpnId(dataBroker, vpnName.getValue());
163                     if (vpnId == NatConstants.INVALID_ID) {
164                         LOG.error("Invalid vpnId returned for routerName {}", routerId);
165                         return;
166                     }
167                     Long routId = NatUtil.getVpnId(dataBroker, routerId);
168                     if (routId == NatConstants.INVALID_ID) {
169                         LOG.error("Invalid routId returned for routerName {}",routerId);
170                         return;
171                     }
172                     LOG.debug("Retrieved vpnId {} for router {}",vpnId,routerId);
173                     //Remove default entry in FIB
174                     LOG.debug("Removing default route in FIB on dpn {} for vpn {} ...", dpnId, vpnName);
175                     snatDefaultRouteProgrammer.removeDefNATRouteInDPN(dpnId,vpnId,routId);
176                 }
177
178                 if (routerData.get().isEnableSnat()) {
179                     LOG.info("SNAT enabled for router {}", routerId);
180                     removeSNATFromDPN(dpnId, routerId, vpnId);
181                 } else {
182                     LOG.info("SNAT is not enabled for router {} to handle removeDPN event {}", routerId, dpnId);
183                 }
184             }
185         }
186     }
187
188     @Override
189     protected void update(InstanceIdentifier<DpnVpninterfacesList> identifier, DpnVpninterfacesList original, DpnVpninterfacesList update) {
190         LOG.trace("Update event - key: {}, original: {}, update: {}", identifier, original, update);
191     }
192     void handleSNATForDPN(BigInteger dpnId, String routerName,Long routerVpnId) {
193         //Check if primary and secondary switch are selected, If not select the role
194         //Install select group to NAPT switch
195         //Install default miss entry to NAPT switch
196         BigInteger naptSwitch;
197         try {
198             Long routerId = NatUtil.getVpnId(dataBroker, routerName);
199             if (routerId == NatConstants.INVALID_ID) {
200                 LOG.error("Invalid routerId returned for routerName {}", routerName);
201                 return;
202             }
203             BigInteger naptId = NatUtil.getPrimaryNaptfromRouterId(dataBroker, routerId);
204             if (naptId == null || naptId.equals(BigInteger.ZERO) || !naptSwitchHA.getSwitchStatus(naptId)) {
205                 LOG.debug("No NaptSwitch is selected for router {}", routerName);
206
207                 naptSwitch = dpnId;
208                 boolean naptstatus = naptSwitchHA.updateNaptSwitch(routerName, naptSwitch);
209                 if (!naptstatus) {
210                     LOG.error("Failed to update newNaptSwitch {} for routername {}", naptSwitch, routerName);
211                     return;
212                 }
213                 LOG.debug("Switch {} is elected as NaptSwitch for router {}", dpnId, routerName);
214
215                 naptSwitchHA.installSnatFlows(routerName, routerId, naptSwitch, routerVpnId);
216
217                 // Install miss entry (table 26) pointing to table 46
218                 FlowEntity flowEntity = naptSwitchHA.buildSnatFlowEntityForNaptSwitch(dpnId, routerName, routerVpnId, NatConstants.ADD_FLOW);
219                 if (flowEntity == null) {
220                     LOG.debug("Failed to populate flowentity for router {} with dpnId {}", routerName, dpnId);
221                     return;
222                 }
223                 LOG.debug("Successfully installed flow for dpnId {} router {}", dpnId, routerName);
224                 mdsalManager.installFlow(flowEntity);
225                 //Removing primary flows from old napt switch
226                 if (naptId != null && !naptId.equals(BigInteger.ZERO)) {
227                     LOG.debug("Removing primary flows from old napt switch {} for router {}", naptId, routerName);
228                     naptSwitchHA.removeSnatFlowsInOldNaptSwitch(routerName, naptId, null);
229                 }
230             } else if (naptId.equals(dpnId)) {
231                     LOG.debug("NaptSwitch {} gone down during cluster reboot came alive", naptId);
232             } else {
233
234                 LOG.debug("Napt switch with Id {} is already elected for router {}", naptId, routerName);
235                 naptSwitch = naptId;
236
237                 //installing group
238                 List<BucketInfo> bucketInfo = naptSwitchHA.handleGroupInNeighborSwitches(dpnId, routerName, naptSwitch);
239                 if (bucketInfo == null) {
240                     LOG.debug("Failed to populate bucketInfo for dpnId {} routername {} naptSwitch {}", dpnId, routerName,
241                             naptSwitch);
242                     return;
243                 }
244                 naptSwitchHA.installSnatGroupEntry(dpnId, bucketInfo, routerName);
245
246                 // Install miss entry (table 26) pointing to group
247                 long groupId = NatUtil.createGroupId(NatUtil.getGroupIdKey(routerName), idManager);
248                 FlowEntity flowEntity = naptSwitchHA.buildSnatFlowEntity(dpnId, routerName, groupId, routerVpnId, NatConstants.ADD_FLOW);
249                 if (flowEntity == null) {
250                     LOG.debug("Failed to populate flowentity for router {} with dpnId {} groupId {}", routerName, dpnId, groupId);
251                     return;
252                 }
253                 LOG.debug("Successfully installed flow for dpnId {} router {} group {}", dpnId, routerName, groupId);
254                 mdsalManager.installFlow(flowEntity);
255             }
256         } catch (Exception ex) {
257             LOG.error("Exception in handleSNATForDPN method : {}", ex);
258         }
259     }
260
261     void removeSNATFromDPN(BigInteger dpnId, String routerName, long routerVpnId) {
262         //irrespective of naptswitch or non-naptswitch, SNAT default miss entry need to be removed
263         //remove miss entry to NAPT switch
264         //if naptswitch elect new switch and install Snat flows and remove those flows in oldnaptswitch
265
266         //get ExternalIpIn prior
267         List<String> externalIpCache;
268         //HashMap Label
269         HashMap<String,Long> externalIpLabel;
270         Long routerId = NatUtil.getVpnId(dataBroker, routerName);
271         if (routerId == NatConstants.INVALID_ID) {
272             LOG.error("Invalid routerId returned for routerName {}",routerName);
273             return;
274         }
275         externalIpCache = NatUtil.getExternalIpsForRouter(dataBroker,routerId);
276         externalIpLabel = NatUtil.getExternalIpsLabelForRouter(dataBroker,routerId);
277         BigInteger naptSwitch = NatUtil.getPrimaryNaptfromRouterId(dataBroker, routerId);
278         if (naptSwitch == null || naptSwitch.equals(BigInteger.ZERO)) {
279             LOG.debug("No naptSwitch is selected for router {}", routerName);
280             return;
281         }
282         try {
283             boolean naptStatus = naptSwitchHA.isNaptSwitchDown(routerName,dpnId,naptSwitch,routerVpnId,externalIpCache);
284             if (!naptStatus) {
285                 LOG.debug("NaptSwitchDown: Switch with DpnId {} is not naptSwitch for router {}",
286                         dpnId, routerName);
287                 long groupId = NatUtil.createGroupId(NatUtil.getGroupIdKey(routerName), idManager);
288                 FlowEntity flowEntity = null;
289                 try {
290                     flowEntity = naptSwitchHA.buildSnatFlowEntity(dpnId, routerName, groupId, routerVpnId, NatConstants.DEL_FLOW);
291                     if (flowEntity == null) {
292                         LOG.debug("Failed to populate flowentity for router {} with dpnId {} groupIs {}",routerName,dpnId,groupId);
293                         return;
294                     }
295                     LOG.debug("NAT Service : Removing default SNAT miss entry flow entity {}",flowEntity);
296                     mdsalManager.removeFlow(flowEntity);
297
298                 } catch (Exception ex) {
299                     LOG.debug("NAT Service : Failed to remove default SNAT miss entry flow entity {} : {}",flowEntity,ex);
300                     return;
301                 }
302                 LOG.debug("NAT Service : Removed default SNAT miss entry flow for dpnID {} with routername {}", dpnId, routerName);
303
304                 //remove group
305                 GroupEntity groupEntity = null;
306                 try {
307                     groupEntity = MDSALUtil.buildGroupEntity(dpnId, groupId, routerName,
308                             GroupTypes.GroupAll, null);
309                     LOG.info("NAT Service : Removing NAPT GroupEntity:{}", groupEntity);
310                     mdsalManager.removeGroup(groupEntity);
311                 } catch (Exception ex) {
312                     LOG.debug("NAT Service : Failed to remove group entity {} : {}",groupEntity,ex);
313                     return;
314                 }
315                 LOG.debug("NAT Service : Removed default SNAT miss entry flow for dpnID {} with routerName {}", dpnId, routerName);
316             } else {
317                 naptSwitchHA.removeSnatFlowsInOldNaptSwitch(routerName, naptSwitch,externalIpLabel);
318                 //remove table 26 flow ppointing to table46
319                 FlowEntity flowEntity = null;
320                 try {
321                     flowEntity = naptSwitchHA.buildSnatFlowEntityForNaptSwitch(dpnId, routerName, routerVpnId, NatConstants.DEL_FLOW);
322                     if (flowEntity == null) {
323                         LOG.debug("Failed to populate flowentity for router {} with dpnId {}",routerName,dpnId);
324                         return;
325                     }
326                     LOG.debug("NAT Service : Removing default SNAT miss entry flow entity for router {} with dpnId {} in napt switch {}"
327                             ,routerName,dpnId,naptSwitch);
328                     mdsalManager.removeFlow(flowEntity);
329
330                 } catch (Exception ex) {
331                     LOG.debug("NAT Service : Failed to remove default SNAT miss entry flow entity {} : {}",flowEntity,ex);
332                     return;
333                 }
334                 LOG.debug("NAT Service : Removed default SNAT miss entry flow for dpnID {} with routername {}", dpnId, routerName);
335
336                 //best effort to check IntExt model
337                 naptSwitchHA.bestEffortDeletion(routerId,routerName,externalIpLabel);
338             }
339         } catch (Exception ex) {
340             LOG.debug("Exception while handling naptSwitch down for router {} : {}",routerName,ex);
341         }
342     }
343 }