27eb42e580f7e2865dc40a7726622d100ecf796c
[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.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
24 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.IdManagerService;
25 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.GroupTypes;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.NeutronRouterDpns;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.neutron.router.dpns.RouterDpnList;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.neutron.router.dpns.router.dpn.list.DpnVpninterfacesList;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ExternalSubnets;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ext.routers.Routers;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.external.subnets.Subnets;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.external.subnets.SubnetsKey;
33 import org.opendaylight.yangtools.concepts.ListenerRegistration;
34 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
35 import org.slf4j.Logger;
36 import org.slf4j.LoggerFactory;
37
38 public class RouterDpnChangeListener
39     extends AsyncDataTreeChangeListenerBase<DpnVpninterfacesList, RouterDpnChangeListener>
40     implements AutoCloseable {
41
42     private static final Logger LOG = LoggerFactory.getLogger(RouterDpnChangeListener.class);
43     private ListenerRegistration<DataChangeListener> listenerRegistration;
44     private final DataBroker dataBroker;
45     private final IMdsalApiManager mdsalManager;
46     private final SNATDefaultRouteProgrammer snatDefaultRouteProgrammer;
47     private final NaptSwitchHA naptSwitchHA;
48     private final IdManagerService idManager;
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         super(DpnVpninterfacesList.class, RouterDpnChangeListener.class);
57         this.dataBroker = dataBroker;
58         this.mdsalManager = mdsalManager;
59         this.snatDefaultRouteProgrammer = snatDefaultRouteProgrammer;
60         this.naptSwitchHA = naptSwitchHA;
61         this.idManager = idManager;
62         this.extNetGroupInstaller = extNetGroupInstaller;
63     }
64
65     @Override
66     public void init() {
67         LOG.info("{} init", getClass().getSimpleName());
68         registerListener(LogicalDatastoreType.OPERATIONAL, dataBroker);
69     }
70
71     @Override
72     protected RouterDpnChangeListener getDataTreeChangeListener() {
73         return RouterDpnChangeListener.this;
74     }
75
76     @Override
77     protected InstanceIdentifier<DpnVpninterfacesList> getWildCardPath() {
78         return InstanceIdentifier.create(NeutronRouterDpns.class).child(RouterDpnList.class)
79             .child(DpnVpninterfacesList.class);
80     }
81
82     @Override
83     protected void add(final InstanceIdentifier<DpnVpninterfacesList> identifier, final DpnVpninterfacesList dpnInfo) {
84         LOG.trace("Add event - key: {}, value: {}", identifier, dpnInfo);
85         final String routerId = identifier.firstKeyOf(RouterDpnList.class).getRouterId();
86         BigInteger dpnId = dpnInfo.getDpnId();
87         //check router is associated to external network
88         InstanceIdentifier<Routers> id = NatUtil.buildRouterIdentifier(routerId);
89         Optional<Routers> routerData = NatUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, id);
90         if (routerData.isPresent()) {
91             Routers router = routerData.get();
92             Uuid networkId = router.getNetworkId();
93             if (networkId != null) {
94                 LOG.debug("Router {} is associated with ext nw {}", routerId, networkId);
95                 Uuid vpnName = NatUtil.getVpnForRouter(dataBroker, routerId);
96                 Long vpnId;
97                 if (vpnName == null) {
98                     LOG.debug("Internal vpn associated to router {}", routerId);
99                     vpnId = NatUtil.getVpnId(dataBroker, routerId);
100                     if (vpnId == NatConstants.INVALID_ID) {
101                         LOG.error("Invalid vpnId returned for routerName {}", routerId);
102                         return;
103                     }
104                     LOG.debug("Retrieved vpnId {} for router {}", vpnId, routerId);
105                     //Install default entry in FIB to SNAT table
106                     LOG.debug("Installing default route in FIB on dpn {} for router {} with vpn {}...",
107                         dpnId, routerId, vpnId);
108                     installDefaultNatRouteForRouterExternalSubnets(dpnId, router.getExternalSubnetIds());
109                     snatDefaultRouteProgrammer.installDefNATRouteInDPN(dpnId, vpnId);
110                 } else {
111                     LOG.debug("External BGP vpn associated to router {}", routerId);
112                     vpnId = NatUtil.getVpnId(dataBroker, vpnName.getValue());
113                     if (vpnId == NatConstants.INVALID_ID) {
114                         LOG.error("Invalid vpnId returned for routerName {}", routerId);
115                         return;
116                     }
117                     Long routId = NatUtil.getVpnId(dataBroker, routerId);
118                     if (routId == NatConstants.INVALID_ID) {
119                         LOG.error("Invalid routId returned for routerName {}", routerId);
120                         return;
121                     }
122                     LOG.debug("Retrieved vpnId {} for router {}", vpnId, routerId);
123                     //Install default entry in FIB to SNAT table
124                     LOG.debug("Installing default route in FIB on dpn {} for routerId {} with vpnId {}...",
125                         dpnId, routerId, vpnId);
126                     installDefaultNatRouteForRouterExternalSubnets(dpnId, router.getExternalSubnetIds());
127                     snatDefaultRouteProgrammer.installDefNATRouteInDPN(dpnId, vpnId, routId);
128                 }
129                 extNetGroupInstaller.installExtNetGroupEntries(networkId, dpnId);
130
131                 if (router.isEnableSnat()) {
132                     LOG.info("SNAT enabled for router {}", routerId);
133                     handleSNATForDPN(dpnId, routerId, vpnId);
134                 } else {
135                     LOG.info("SNAT is not enabled for router {} to handle addDPN event {}", routerId, dpnId);
136                 }
137             }
138         } else {
139             LOG.debug("Router {} is not associated with External network", routerId);
140         }
141     }
142
143     @Override
144     protected void remove(InstanceIdentifier<DpnVpninterfacesList> identifier, DpnVpninterfacesList dpnInfo) {
145         LOG.trace("Remove event - key: {}, value: {}", identifier, dpnInfo);
146         final String routerId = identifier.firstKeyOf(RouterDpnList.class).getRouterId();
147         BigInteger dpnId = dpnInfo.getDpnId();
148         //check router is associated to external network
149         InstanceIdentifier<Routers> id = NatUtil.buildRouterIdentifier(routerId);
150         Optional<Routers> routerData = NatUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, id);
151         if (routerData.isPresent()) {
152             Routers router = routerData.get();
153             Uuid networkId = router.getNetworkId();
154             if (networkId != null) {
155                 LOG.debug("Router {} is associated with ext nw {}", routerId, networkId);
156                 Uuid vpnName = NatUtil.getVpnForRouter(dataBroker, routerId);
157                 Long vpnId;
158                 if (vpnName == null) {
159                     LOG.debug("Internal vpn associated to router {}", routerId);
160                     vpnId = NatUtil.getVpnId(dataBroker, routerId);
161                     if (vpnId == NatConstants.INVALID_ID) {
162                         LOG.error("Invalid vpnId returned for routerName {}", routerId);
163                         return;
164                     }
165                     LOG.debug("Retrieved vpnId {} for router {}", vpnId, routerId);
166                     //Remove default entry in FIB
167                     LOG.debug("Removing default route in FIB on dpn {} for vpn {} ...", dpnId, vpnName);
168                     snatDefaultRouteProgrammer.removeDefNATRouteInDPN(dpnId, vpnId);
169                 } else {
170                     LOG.debug("External vpn associated to router {}", routerId);
171                     vpnId = NatUtil.getVpnId(dataBroker, vpnName.getValue());
172                     if (vpnId == NatConstants.INVALID_ID) {
173                         LOG.error("Invalid vpnId returned for routerName {}", routerId);
174                         return;
175                     }
176                     Long routId = NatUtil.getVpnId(dataBroker, routerId);
177                     if (routId == NatConstants.INVALID_ID) {
178                         LOG.error("Invalid routId returned for routerName {}", routerId);
179                         return;
180                     }
181                     LOG.debug("Retrieved vpnId {} for router {}", vpnId, routerId);
182                     //Remove default entry in FIB
183                     LOG.debug("Removing default route in FIB on dpn {} for vpn {} ...", dpnId, vpnName);
184                     snatDefaultRouteProgrammer.removeDefNATRouteInDPN(dpnId, vpnId, routId);
185                 }
186
187                 if (router.isEnableSnat()) {
188                     LOG.info("SNAT enabled for router {}", routerId);
189                     removeSNATFromDPN(dpnId, routerId, vpnId);
190                 } else {
191                     LOG.info("SNAT is not enabled for router {} to handle removeDPN event {}", routerId, dpnId);
192                 }
193             }
194         }
195     }
196
197     @Override
198     protected void update(InstanceIdentifier<DpnVpninterfacesList> identifier, DpnVpninterfacesList original,
199                           DpnVpninterfacesList update) {
200         LOG.trace("Update event - key: {}, original: {}, update: {}", identifier, original, update);
201     }
202
203     // TODO Clean up the exception handling
204     @SuppressWarnings("checkstyle:IllegalCatch")
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.getPrimaryNaptfromRouterName(dataBroker, routerName);
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                 Routers extRouters = NatUtil.getRoutersFromConfigDS(dataBroker, routerName);
229                 if (extRouters != null) {
230                     NatUtil.createRouterIdsConfigDS(dataBroker, routerName);
231                     naptSwitchHA.subnetRegisterMapping(extRouters, routerId);
232                 }
233                 naptSwitchHA.installSnatFlows(routerName, routerId, naptSwitch, routerVpnId);
234
235                 // Install miss entry (table 26) pointing to table 46
236                 FlowEntity flowEntity = naptSwitchHA.buildSnatFlowEntityForNaptSwitch(dpnId, routerName,
237                     routerVpnId, NatConstants.ADD_FLOW);
238                 if (flowEntity == null) {
239                     LOG.debug("Failed to populate flowentity for router {} with dpnId {}", routerName, dpnId);
240                     return;
241                 }
242                 LOG.debug("Successfully installed flow for dpnId {} router {}", dpnId, routerName);
243                 mdsalManager.installFlow(flowEntity);
244                 //Removing primary flows from old napt switch
245                 if (naptId != null && !naptId.equals(BigInteger.ZERO)) {
246                     LOG.debug("Removing primary flows from old napt switch {} for router {}", naptId, routerName);
247                     naptSwitchHA.removeSnatFlowsInOldNaptSwitch(routerName, naptId, null);
248                 }
249             } else if (naptId.equals(dpnId)) {
250                 LOG.debug("NaptSwitch {} gone down during cluster reboot came alive", naptId);
251             } else {
252
253                 LOG.debug("Napt switch with Id {} is already elected for router {}", naptId, routerName);
254                 naptSwitch = naptId;
255
256                 //installing group
257                 List<BucketInfo> bucketInfo = naptSwitchHA.handleGroupInNeighborSwitches(dpnId, routerName, naptSwitch);
258                 if (bucketInfo == null) {
259                     LOG.debug("Failed to populate bucketInfo for dpnId {} routername {} naptSwitch {}",
260                         dpnId, routerName, naptSwitch);
261                     return;
262                 }
263                 naptSwitchHA.installSnatGroupEntry(dpnId, bucketInfo, routerName);
264
265                 // Install miss entry (table 26) pointing to group
266                 long groupId = NatUtil.createGroupId(NatUtil.getGroupIdKey(routerName), idManager);
267                 FlowEntity flowEntity =
268                     naptSwitchHA.buildSnatFlowEntity(dpnId, routerName, groupId, routerVpnId, NatConstants.ADD_FLOW);
269                 if (flowEntity == null) {
270                     LOG.debug("Failed to populate flowentity for router {} with dpnId {} groupId {}",
271                         routerName, dpnId, groupId);
272                     return;
273                 }
274                 LOG.debug("Successfully installed flow for dpnId {} router {} group {}", dpnId, routerName, groupId);
275                 mdsalManager.installFlow(flowEntity);
276             }
277         } catch (Exception ex) {
278             LOG.error("Exception in handleSNATForDPN method : {}", ex);
279         }
280     }
281
282     // TODO Clean up the exception handling
283     @SuppressWarnings("checkstyle:IllegalCatch")
284     void removeSNATFromDPN(BigInteger dpnId, String routerName, long routerVpnId) {
285         //irrespective of naptswitch or non-naptswitch, SNAT default miss entry need to be removed
286         //remove miss entry to NAPT switch
287         //if naptswitch elect new switch and install Snat flows and remove those flows in oldnaptswitch
288
289         //get ExternalIpIn prior
290         List<String> externalIpCache;
291         //HashMap Label
292         HashMap<String, Long> externalIpLabel;
293         Long routerId = NatUtil.getVpnId(dataBroker, routerName);
294         if (routerId == NatConstants.INVALID_ID) {
295             LOG.error("Invalid routerId returned for routerName {}", routerName);
296             return;
297         }
298         externalIpCache = NatUtil.getExternalIpsForRouter(dataBroker, routerId);
299         externalIpLabel = NatUtil.getExternalIpsLabelForRouter(dataBroker, routerId);
300         BigInteger naptSwitch = NatUtil.getPrimaryNaptfromRouterName(dataBroker, routerName);
301         if (naptSwitch == null || naptSwitch.equals(BigInteger.ZERO)) {
302             LOG.debug("No naptSwitch is selected for router {}", routerName);
303             return;
304         }
305         try {
306             boolean naptStatus =
307                 naptSwitchHA.isNaptSwitchDown(routerName, dpnId, naptSwitch, routerVpnId, externalIpCache);
308             if (!naptStatus) {
309                 LOG.debug("NaptSwitchDown: Switch with DpnId {} is not naptSwitch for router {}",
310                     dpnId, routerName);
311                 long groupId = NatUtil.createGroupId(NatUtil.getGroupIdKey(routerName), idManager);
312                 FlowEntity flowEntity = null;
313                 try {
314                     flowEntity = naptSwitchHA.buildSnatFlowEntity(dpnId, routerName, groupId, routerVpnId,
315                         NatConstants.DEL_FLOW);
316                     if (flowEntity == null) {
317                         LOG.debug("Failed to populate flowentity for router {} with dpnId {} groupIs {}",
318                             routerName, dpnId, groupId);
319                         return;
320                     }
321                     LOG.debug("NAT Service : Removing default SNAT miss entry flow entity {}", flowEntity);
322                     mdsalManager.removeFlow(flowEntity);
323
324                 } catch (Exception ex) {
325                     LOG.debug("NAT Service : Failed to remove default SNAT miss entry flow entity {} : {}",
326                         flowEntity, ex);
327                     return;
328                 }
329                 LOG.debug("NAT Service : Removed default SNAT miss entry flow for dpnID {} with routername {}",
330                     dpnId, routerName);
331
332                 //remove group
333                 GroupEntity groupEntity = null;
334                 try {
335                     groupEntity = MDSALUtil.buildGroupEntity(dpnId, groupId, routerName,
336                         GroupTypes.GroupAll, null);
337                     LOG.info("NAT Service : Removing NAPT GroupEntity:{}", groupEntity);
338                     mdsalManager.removeGroup(groupEntity);
339                 } catch (Exception ex) {
340                     LOG.debug("NAT Service : Failed to remove group entity {} : {}", groupEntity, ex);
341                     return;
342                 }
343                 LOG.debug("NAT Service : Removed default SNAT miss entry flow for dpnID {} with routerName {}",
344                     dpnId, routerName);
345             } else {
346                 naptSwitchHA.removeSnatFlowsInOldNaptSwitch(routerName, naptSwitch, externalIpLabel);
347                 //remove table 26 flow ppointing to table46
348                 FlowEntity flowEntity = null;
349                 try {
350                     flowEntity = naptSwitchHA.buildSnatFlowEntityForNaptSwitch(dpnId, routerName, routerVpnId,
351                         NatConstants.DEL_FLOW);
352                     if (flowEntity == null) {
353                         LOG.debug("Failed to populate flowentity for router {} with dpnId {}", routerName, dpnId);
354                         return;
355                     }
356                     LOG.debug("NAT Service : Removing default SNAT miss entry flow entity for router {} with "
357                         + "dpnId {} in napt switch {}", routerName, dpnId, naptSwitch);
358                     mdsalManager.removeFlow(flowEntity);
359
360                 } catch (Exception ex) {
361                     LOG.debug("NAT Service : Failed to remove default SNAT miss entry flow entity {} : {}",
362                         flowEntity, ex);
363                     return;
364                 }
365                 LOG.debug("NAT Service : Removed default SNAT miss entry flow for dpnID {} with routername {}",
366                     dpnId, routerName);
367
368                 //best effort to check IntExt model
369                 naptSwitchHA.bestEffortDeletion(routerId, routerName, externalIpLabel);
370             }
371         } catch (Exception ex) {
372             LOG.debug("Exception while handling naptSwitch down for router {} : {}", routerName, ex);
373         }
374     }
375
376     private void installDefaultNatRouteForRouterExternalSubnets(BigInteger dpnId, List<Uuid> externalSubnetIds) {
377         if (externalSubnetIds == null) {
378             LOG.debug("NAT Service : No external subnets for router");
379             return;
380         }
381
382         for (Uuid subnetId : externalSubnetIds) {
383             InstanceIdentifier<Subnets> subnetsIdentifier = InstanceIdentifier.create(ExternalSubnets.class)
384                     .child(Subnets.class, new SubnetsKey(subnetId));
385             Optional<Subnets> externalSubnet = NatUtil.read(dataBroker,
386                     LogicalDatastoreType.CONFIGURATION, subnetsIdentifier);
387             if (externalSubnet.isPresent()) {
388                 long vpnIdForSubnet = NatUtil.getVpnId(dataBroker, externalSubnet.get().getVpnId().getValue());
389                 if (vpnIdForSubnet != NatConstants.INVALID_ID) {
390                     LOG.debug("NAT Service : Installing default routes in FIB on dpn {} for subnetId {} with vpnId {}",
391                             dpnId, subnetId, vpnIdForSubnet);
392                     snatDefaultRouteProgrammer.installDefNATRouteInDPN(dpnId, vpnIdForSubnet,
393                             subnetId.getValue(), idManager);
394                 } else {
395                     LOG.debug("NAT Service : No VPN ID for subnetId {}, cannot installing default routes flows in FIB",
396                             subnetId);
397                 }
398             } else {
399                 LOG.warn("NAT Service : No external subnet found for Uuid {}", subnetId);
400             }
401         }
402     }
403 }