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