8284dda1aab53a18160f0b86907c4d5ca7666748
[netvirt.git] /
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,
109                             NatUtil.getExternalSubnetIdsFromExternalIps(router.getExternalIps()));
110                     snatDefaultRouteProgrammer.installDefNATRouteInDPN(dpnId, vpnId);
111                 } else {
112                     LOG.debug("External BGP vpn associated to router {}", routerId);
113                     vpnId = NatUtil.getVpnId(dataBroker, vpnName.getValue());
114                     if (vpnId == NatConstants.INVALID_ID) {
115                         LOG.error("Invalid vpnId returned for routerName {}", routerId);
116                         return;
117                     }
118                     Long routId = NatUtil.getVpnId(dataBroker, routerId);
119                     if (routId == NatConstants.INVALID_ID) {
120                         LOG.error("Invalid routId returned for routerName {}", routerId);
121                         return;
122                     }
123                     LOG.debug("Retrieved vpnId {} for router {}", vpnId, routerId);
124                     //Install default entry in FIB to SNAT table
125                     LOG.debug("Installing default route in FIB on dpn {} for routerId {} with vpnId {}...",
126                         dpnId, routerId, vpnId);
127                     installDefaultNatRouteForRouterExternalSubnets(dpnId,
128                             NatUtil.getExternalSubnetIdsFromExternalIps(router.getExternalIps()));
129                     snatDefaultRouteProgrammer.installDefNATRouteInDPN(dpnId, vpnId, routId);
130                 }
131                 extNetGroupInstaller.installExtNetGroupEntries(networkId, dpnId);
132
133                 if (router.isEnableSnat()) {
134                     LOG.info("SNAT enabled for router {}", routerId);
135                     handleSNATForDPN(dpnId, routerId, vpnId);
136                 } else {
137                     LOG.info("SNAT is not enabled for router {} to handle addDPN event {}", routerId, dpnId);
138                 }
139             }
140         } else {
141             LOG.debug("Router {} is not associated with External network", routerId);
142         }
143     }
144
145     @Override
146     protected void remove(InstanceIdentifier<DpnVpninterfacesList> identifier, DpnVpninterfacesList dpnInfo) {
147         LOG.trace("Remove event - key: {}, value: {}", identifier, dpnInfo);
148         final String routerId = identifier.firstKeyOf(RouterDpnList.class).getRouterId();
149         BigInteger dpnId = dpnInfo.getDpnId();
150         //check router is associated to external network
151         InstanceIdentifier<Routers> id = NatUtil.buildRouterIdentifier(routerId);
152         Optional<Routers> routerData = NatUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, id);
153         if (routerData.isPresent()) {
154             Routers router = routerData.get();
155             Uuid networkId = router.getNetworkId();
156             if (networkId != null) {
157                 LOG.debug("Router {} is associated with ext nw {}", routerId, networkId);
158                 Uuid vpnName = NatUtil.getVpnForRouter(dataBroker, routerId);
159                 Long vpnId;
160                 if (vpnName == null) {
161                     LOG.debug("Internal vpn associated to router {}", routerId);
162                     vpnId = NatUtil.getVpnId(dataBroker, routerId);
163                     if (vpnId == NatConstants.INVALID_ID) {
164                         LOG.error("Invalid vpnId returned for routerName {}", routerId);
165                         return;
166                     }
167                     LOG.debug("Retrieved vpnId {} for router {}", vpnId, routerId);
168                     //Remove default entry in FIB
169                     LOG.debug("Removing default route in FIB on dpn {} for vpn {} ...", dpnId, vpnName);
170                     snatDefaultRouteProgrammer.removeDefNATRouteInDPN(dpnId, vpnId);
171                 } else {
172                     LOG.debug("External vpn associated to router {}", routerId);
173                     vpnId = NatUtil.getVpnId(dataBroker, vpnName.getValue());
174                     if (vpnId == NatConstants.INVALID_ID) {
175                         LOG.error("Invalid vpnId returned for routerName {}", routerId);
176                         return;
177                     }
178                     Long routId = NatUtil.getVpnId(dataBroker, routerId);
179                     if (routId == NatConstants.INVALID_ID) {
180                         LOG.error("Invalid routId returned for routerName {}", routerId);
181                         return;
182                     }
183                     LOG.debug("Retrieved vpnId {} for router {}", vpnId, routerId);
184                     //Remove default entry in FIB
185                     LOG.debug("Removing default route in FIB on dpn {} for vpn {} ...", dpnId, vpnName);
186                     snatDefaultRouteProgrammer.removeDefNATRouteInDPN(dpnId, vpnId, routId);
187                 }
188
189                 if (router.isEnableSnat()) {
190                     LOG.info("SNAT enabled for router {}", routerId);
191                     removeSNATFromDPN(dpnId, routerId, vpnId);
192                 } else {
193                     LOG.info("SNAT is not enabled for router {} to handle removeDPN event {}", routerId, dpnId);
194                 }
195             }
196         }
197     }
198
199     @Override
200     protected void update(InstanceIdentifier<DpnVpninterfacesList> identifier, DpnVpninterfacesList original,
201                           DpnVpninterfacesList update) {
202         LOG.trace("Update event - key: {}, original: {}, update: {}", identifier, original, update);
203     }
204
205     // TODO Clean up the exception handling
206     @SuppressWarnings("checkstyle:IllegalCatch")
207     void handleSNATForDPN(BigInteger dpnId, String routerName, Long routerVpnId) {
208         //Check if primary and secondary switch are selected, If not select the role
209         //Install select group to NAPT switch
210         //Install default miss entry to NAPT switch
211         BigInteger naptSwitch;
212         try {
213             Long routerId = NatUtil.getVpnId(dataBroker, routerName);
214             if (routerId == NatConstants.INVALID_ID) {
215                 LOG.error("Invalid routerId returned for routerName {}", routerName);
216                 return;
217             }
218             BigInteger naptId = NatUtil.getPrimaryNaptfromRouterName(dataBroker, routerName);
219             if (naptId == null || naptId.equals(BigInteger.ZERO) || !naptSwitchHA.getSwitchStatus(naptId)) {
220                 LOG.debug("No NaptSwitch is selected for router {}", routerName);
221
222                 naptSwitch = dpnId;
223                 boolean naptstatus = naptSwitchHA.updateNaptSwitch(routerName, naptSwitch);
224                 if (!naptstatus) {
225                     LOG.error("Failed to update newNaptSwitch {} for routername {}", naptSwitch, routerName);
226                     return;
227                 }
228                 LOG.debug("Switch {} is elected as NaptSwitch for router {}", dpnId, routerName);
229
230                 Routers extRouters = NatUtil.getRoutersFromConfigDS(dataBroker, routerName);
231                 if (extRouters != null) {
232                     NatUtil.createRouterIdsConfigDS(dataBroker, routerName);
233                     naptSwitchHA.subnetRegisterMapping(extRouters, routerId);
234                 }
235                 naptSwitchHA.installSnatFlows(routerName, routerId, naptSwitch, routerVpnId);
236
237                 // Install miss entry (table 26) pointing to table 46
238                 FlowEntity flowEntity = naptSwitchHA.buildSnatFlowEntityForNaptSwitch(dpnId, routerName,
239                     routerVpnId, NatConstants.ADD_FLOW);
240                 if (flowEntity == null) {
241                     LOG.debug("Failed to populate flowentity for router {} with dpnId {}", routerName, dpnId);
242                     return;
243                 }
244                 LOG.debug("Successfully installed flow for dpnId {} router {}", dpnId, routerName);
245                 mdsalManager.installFlow(flowEntity);
246                 //Removing primary flows from old napt switch
247                 if (naptId != null && !naptId.equals(BigInteger.ZERO)) {
248                     LOG.debug("Removing primary flows from old napt switch {} for router {}", naptId, routerName);
249                     naptSwitchHA.removeSnatFlowsInOldNaptSwitch(routerName, naptId, null);
250                 }
251             } else if (naptId.equals(dpnId)) {
252                 LOG.debug("NaptSwitch {} gone down during cluster reboot came alive", naptId);
253             } else {
254
255                 LOG.debug("Napt switch with Id {} is already elected for router {}", naptId, routerName);
256                 naptSwitch = naptId;
257
258                 //installing group
259                 List<BucketInfo> bucketInfo = naptSwitchHA.handleGroupInNeighborSwitches(dpnId, routerName, naptSwitch);
260                 if (bucketInfo == null) {
261                     LOG.debug("Failed to populate bucketInfo for dpnId {} routername {} naptSwitch {}",
262                         dpnId, routerName, naptSwitch);
263                     return;
264                 }
265                 naptSwitchHA.installSnatGroupEntry(dpnId, bucketInfo, routerName);
266
267                 // Install miss entry (table 26) pointing to group
268                 long groupId = NatUtil.createGroupId(NatUtil.getGroupIdKey(routerName), idManager);
269                 FlowEntity flowEntity =
270                     naptSwitchHA.buildSnatFlowEntity(dpnId, routerName, groupId, routerVpnId, NatConstants.ADD_FLOW);
271                 if (flowEntity == null) {
272                     LOG.debug("Failed to populate flowentity for router {} with dpnId {} groupId {}",
273                         routerName, dpnId, groupId);
274                     return;
275                 }
276                 LOG.debug("Successfully installed flow for dpnId {} router {} group {}", dpnId, routerName, groupId);
277                 mdsalManager.installFlow(flowEntity);
278             }
279         } catch (Exception ex) {
280             LOG.error("Exception in handleSNATForDPN method : {}", ex);
281         }
282     }
283
284     // TODO Clean up the exception handling
285     @SuppressWarnings("checkstyle:IllegalCatch")
286     void removeSNATFromDPN(BigInteger dpnId, String routerName, long routerVpnId) {
287         //irrespective of naptswitch or non-naptswitch, SNAT default miss entry need to be removed
288         //remove miss entry to NAPT switch
289         //if naptswitch elect new switch and install Snat flows and remove those flows in oldnaptswitch
290
291         //get ExternalIpIn prior
292         List<String> externalIpCache;
293         //HashMap Label
294         HashMap<String, Long> externalIpLabel;
295         Long routerId = NatUtil.getVpnId(dataBroker, routerName);
296         if (routerId == NatConstants.INVALID_ID) {
297             LOG.error("Invalid routerId returned for routerName {}", routerName);
298             return;
299         }
300         externalIpCache = NatUtil.getExternalIpsForRouter(dataBroker, routerId);
301         externalIpLabel = NatUtil.getExternalIpsLabelForRouter(dataBroker, routerId);
302         BigInteger naptSwitch = NatUtil.getPrimaryNaptfromRouterName(dataBroker, routerName);
303         if (naptSwitch == null || naptSwitch.equals(BigInteger.ZERO)) {
304             LOG.debug("No naptSwitch is selected for router {}", routerName);
305             return;
306         }
307         try {
308             boolean naptStatus =
309                 naptSwitchHA.isNaptSwitchDown(routerName, dpnId, naptSwitch, routerVpnId, externalIpCache);
310             if (!naptStatus) {
311                 LOG.debug("NaptSwitchDown: Switch with DpnId {} is not naptSwitch for router {}",
312                     dpnId, routerName);
313                 long groupId = NatUtil.createGroupId(NatUtil.getGroupIdKey(routerName), idManager);
314                 FlowEntity flowEntity = null;
315                 try {
316                     flowEntity = naptSwitchHA.buildSnatFlowEntity(dpnId, routerName, groupId, routerVpnId,
317                         NatConstants.DEL_FLOW);
318                     if (flowEntity == null) {
319                         LOG.debug("Failed to populate flowentity for router {} with dpnId {} groupIs {}",
320                             routerName, dpnId, groupId);
321                         return;
322                     }
323                     LOG.debug("NAT Service : Removing default SNAT miss entry flow entity {}", flowEntity);
324                     mdsalManager.removeFlow(flowEntity);
325
326                 } catch (Exception ex) {
327                     LOG.debug("NAT Service : Failed to remove default SNAT miss entry flow entity {} : {}",
328                         flowEntity, ex);
329                     return;
330                 }
331                 LOG.debug("NAT Service : Removed default SNAT miss entry flow for dpnID {} with routername {}",
332                     dpnId, routerName);
333
334                 //remove group
335                 GroupEntity groupEntity = null;
336                 try {
337                     groupEntity = MDSALUtil.buildGroupEntity(dpnId, groupId, routerName,
338                         GroupTypes.GroupAll, null);
339                     LOG.info("NAT Service : Removing NAPT GroupEntity:{}", groupEntity);
340                     mdsalManager.removeGroup(groupEntity);
341                 } catch (Exception ex) {
342                     LOG.debug("NAT Service : Failed to remove group entity {} : {}", groupEntity, ex);
343                     return;
344                 }
345                 LOG.debug("NAT Service : Removed default SNAT miss entry flow for dpnID {} with routerName {}",
346                     dpnId, routerName);
347             } else {
348                 naptSwitchHA.removeSnatFlowsInOldNaptSwitch(routerName, naptSwitch, externalIpLabel);
349                 //remove table 26 flow ppointing to table46
350                 FlowEntity flowEntity = null;
351                 try {
352                     flowEntity = naptSwitchHA.buildSnatFlowEntityForNaptSwitch(dpnId, routerName, routerVpnId,
353                         NatConstants.DEL_FLOW);
354                     if (flowEntity == null) {
355                         LOG.debug("Failed to populate flowentity for router {} with dpnId {}", routerName, dpnId);
356                         return;
357                     }
358                     LOG.debug("NAT Service : Removing default SNAT miss entry flow entity for router {} with "
359                         + "dpnId {} in napt switch {}", routerName, dpnId, naptSwitch);
360                     mdsalManager.removeFlow(flowEntity);
361
362                 } catch (Exception ex) {
363                     LOG.debug("NAT Service : Failed to remove default SNAT miss entry flow entity {} : {}",
364                         flowEntity, ex);
365                     return;
366                 }
367                 LOG.debug("NAT Service : Removed default SNAT miss entry flow for dpnID {} with routername {}",
368                     dpnId, routerName);
369
370                 //best effort to check IntExt model
371                 naptSwitchHA.bestEffortDeletion(routerId, routerName, externalIpLabel);
372             }
373         } catch (Exception ex) {
374             LOG.debug("Exception while handling naptSwitch down for router {} : {}", routerName, ex);
375         }
376     }
377
378     private void installDefaultNatRouteForRouterExternalSubnets(BigInteger dpnId, List<Uuid> externalSubnetIds) {
379         if (externalSubnetIds == null) {
380             LOG.debug("NAT Service : No external subnets for router");
381             return;
382         }
383
384         for (Uuid subnetId : externalSubnetIds) {
385             InstanceIdentifier<Subnets> subnetsIdentifier = InstanceIdentifier.create(ExternalSubnets.class)
386                     .child(Subnets.class, new SubnetsKey(subnetId));
387             Optional<Subnets> externalSubnet = NatUtil.read(dataBroker,
388                     LogicalDatastoreType.CONFIGURATION, subnetsIdentifier);
389             if (externalSubnet.isPresent()) {
390                 long vpnIdForSubnet = NatUtil.getVpnId(dataBroker, externalSubnet.get().getVpnId().getValue());
391                 if (vpnIdForSubnet != NatConstants.INVALID_ID) {
392                     LOG.debug("NAT Service : Installing default routes in FIB on dpn {} for subnetId {} with vpnId {}",
393                             dpnId, subnetId, vpnIdForSubnet);
394                     snatDefaultRouteProgrammer.installDefNATRouteInDPN(dpnId, vpnIdForSubnet,
395                             subnetId.getValue(), idManager);
396                 } else {
397                     LOG.debug("NAT Service : No VPN ID for subnetId {}, cannot installing default routes flows in FIB",
398                             subnetId);
399                 }
400             } else {
401                 LOG.warn("NAT Service : No external subnet found for Uuid {}", subnetId);
402             }
403         }
404     }
405 }