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