Switch to JDT annotations for Nullable and NonNull
[netvirt.git] / vpnmanager / impl / src / main / java / org / opendaylight / netvirt / vpnmanager / VpnSubnetRouteHandler.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.vpnmanager;
9
10 import com.google.common.base.Optional;
11 import com.google.common.base.Preconditions;
12 import java.math.BigInteger;
13 import java.util.ArrayList;
14 import java.util.Arrays;
15 import java.util.Collections;
16 import java.util.HashMap;
17 import java.util.List;
18 import java.util.Map;
19 import java.util.Objects;
20 import javax.inject.Inject;
21 import javax.inject.Singleton;
22 import org.eclipse.jdt.annotation.Nullable;
23 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
24 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
25 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
26 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
27 import org.opendaylight.genius.datastoreutils.SingleTransactionDataBroker;
28 import org.opendaylight.netvirt.bgpmanager.api.IBgpManager;
29 import org.opendaylight.netvirt.fibmanager.api.IFibManager;
30 import org.opendaylight.netvirt.fibmanager.api.RouteOrigin;
31 import org.opendaylight.netvirt.vpnmanager.VpnOpDataSyncer.VpnOpDataType;
32 import org.opendaylight.netvirt.vpnmanager.api.InterfaceUtils;
33 import org.opendaylight.netvirt.vpnmanager.populator.input.L3vpnInput;
34 import org.opendaylight.netvirt.vpnmanager.populator.intfc.VpnPopulator;
35 import org.opendaylight.netvirt.vpnmanager.populator.registry.L3vpnRegistry;
36 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface;
37 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface.OperStatus;
38 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.vrfentries.VrfEntry;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.PortOpData;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.SubnetOpData;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.TaskState;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.port.op.data.PortOpDataEntry;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.port.op.data.PortOpDataEntryKey;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.prefix.to._interface.vpn.ids.Prefixes;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.subnet.op.data.SubnetOpDataEntry;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.subnet.op.data.SubnetOpDataEntryBuilder;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.subnet.op.data.SubnetOpDataEntryKey;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.subnet.op.data.subnet.op.data.entry.SubnetToDpn;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.VpnInstanceOpDataEntry;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ExternalNetworks;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.external.networks.Networks;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.external.networks.NetworksKey;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.Subnetmaps;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.subnetmaps.Subnetmap;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.subnetmaps.SubnetmapKey;
57 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
58 import org.slf4j.Logger;
59 import org.slf4j.LoggerFactory;
60
61 @Singleton
62 public class VpnSubnetRouteHandler {
63     private static final Logger LOG = LoggerFactory.getLogger(VpnSubnetRouteHandler.class);
64     private static final String LOGGING_PREFIX = "SUBNETROUTE:";
65     private final DataBroker dataBroker;
66     private final SubnetOpDpnManager subOpDpnManager;
67     private final IBgpManager bgpManager;
68     private final VpnOpDataSyncer vpnOpDataSyncer;
69     private final VpnNodeListener vpnNodeListener;
70     private final IFibManager fibManager;
71     private final VpnUtil vpnUtil;
72
73     @Inject
74     public VpnSubnetRouteHandler(final DataBroker dataBroker, final SubnetOpDpnManager subnetOpDpnManager,
75             final IBgpManager bgpManager, final VpnOpDataSyncer vpnOpDataSyncer, final VpnNodeListener vpnNodeListener,
76             final IFibManager fibManager, VpnUtil vpnUtil) {
77         this.dataBroker = dataBroker;
78         this.subOpDpnManager = subnetOpDpnManager;
79         this.bgpManager = bgpManager;
80         this.vpnOpDataSyncer = vpnOpDataSyncer;
81         this.vpnNodeListener = vpnNodeListener;
82         this.fibManager = fibManager;
83         this.vpnUtil = vpnUtil;
84     }
85
86     // TODO Clean up the exception handling
87     @SuppressWarnings("checkstyle:IllegalCatch")
88     public void onSubnetAddedToVpn(Subnetmap subnetmap, boolean isBgpVpn, Long elanTag) {
89         Uuid subnetId = subnetmap.getId();
90         String subnetIp = subnetmap.getSubnetIp();
91         Subnetmap subMap = null;
92         SubnetOpDataEntry subOpEntry = null;
93         SubnetOpDataEntryBuilder subOpBuilder = null;
94         InstanceIdentifier<SubnetOpDataEntry> subOpIdentifier = null;
95         Optional<SubnetOpDataEntry> optionalSubs = null;
96
97         Preconditions.checkNotNull(subnetId, LOGGING_PREFIX + " onSubnetAddedToVpn: SubnetId cannot be null or empty!");
98         Preconditions.checkNotNull(subnetIp,
99                 LOGGING_PREFIX + " onSubnetAddedToVpn: SubnetPrefix cannot be null or empty!");
100         Preconditions.checkNotNull(elanTag, LOGGING_PREFIX + " onSubnetAddedToVpn: ElanTag cannot be null or empty!");
101
102         if (subnetmap.getVpnId() == null) {
103             LOG.error("onSubnetAddedToVpn: VpnId {} for subnet {} not found, bailing out", subnetmap.getVpnId(),
104                     subnetId);
105             return;
106         }
107         String vpnName = subnetmap.getVpnId().getValue();
108         long vpnId = waitAndGetVpnIdIfInvalid(vpnName);
109         if (vpnId == VpnConstants.INVALID_ID) {
110             LOG.error(
111                     "{} onSubnetAddedToVpn: VpnInstance to VPNId mapping not yet available for VpnName {} "
112                             + "processing subnet {} with IP {}, bailing out now.",
113                     LOGGING_PREFIX, vpnName, subnetId, subnetIp);
114             return;
115         }
116
117         String primaryRd = vpnUtil.getPrimaryRd(vpnName);
118         VpnInstanceOpDataEntry vpnInstanceOpData = waitAndGetVpnInstanceOpDataIfNull(vpnName, primaryRd);
119         if (vpnInstanceOpData == null) {
120             LOG.error(
121                     "{} onSubnetAddedToVpn: VpnInstanceOpData not yet available for VpnName {} "
122                             + "processing subnet {} with IP {}, bailing out now.",
123                     LOGGING_PREFIX, vpnName, subnetId, subnetIp);
124             return;
125         }
126         LOG.info("{} onSubnetAddedToVpn: Subnet {} with IP {} being added to vpn {}", LOGGING_PREFIX,
127                 subnetId.getValue(), subnetIp, vpnName);
128         //TODO(vivek): Change this to use more granularized lock at subnetId level
129         try {
130             vpnUtil.lockSubnet(subnetId.getValue());
131             // Please check if subnetId belongs to an External Network
132             InstanceIdentifier<Subnetmap> subMapid =
133                     InstanceIdentifier.builder(Subnetmaps.class).child(Subnetmap.class,
134                             new SubnetmapKey(subnetId)).build();
135             Optional<Subnetmap> sm = SingleTransactionDataBroker.syncReadOptional(dataBroker,
136                     LogicalDatastoreType.CONFIGURATION, subMapid);
137             if (!sm.isPresent()) {
138                 LOG.error("{} onSubnetAddedToVpn: Unable to retrieve subnetmap entry for subnet {} IP {}"
139                         + " vpnName {}", LOGGING_PREFIX, subnetId, subnetIp, vpnName);
140                 return;
141             }
142             subMap = sm.get();
143
144             if (isBgpVpn) {
145                 InstanceIdentifier<Networks> netsIdentifier = InstanceIdentifier.builder(ExternalNetworks.class)
146                         .child(Networks.class, new NetworksKey(subMap.getNetworkId())).build();
147                 Optional<Networks> optionalNets = SingleTransactionDataBroker.syncReadOptional(dataBroker,
148                         LogicalDatastoreType.CONFIGURATION, netsIdentifier);
149                 if (optionalNets.isPresent()) {
150                     LOG.info("{} onSubnetAddedToVpn: subnet {} with IP {} is an external subnet on external "
151                                     + "network {}, so ignoring this for SubnetRoute on vpn {}", LOGGING_PREFIX,
152                             subnetId.getValue(), subnetIp, subMap.getNetworkId().getValue(), vpnName);
153                     return;
154                 }
155             }
156             //Create and add SubnetOpDataEntry object for this subnet to the SubnetOpData container
157             subOpIdentifier = InstanceIdentifier.builder(SubnetOpData.class).child(SubnetOpDataEntry.class,
158                     new SubnetOpDataEntryKey(subnetId)).build();
159             optionalSubs = SingleTransactionDataBroker.syncReadOptional(dataBroker, LogicalDatastoreType.OPERATIONAL,
160                     subOpIdentifier);
161             if (optionalSubs.isPresent()) {
162                 LOG.error("{} onSubnetAddedToVpn: SubnetOpDataEntry for subnet {} with ip {} and vpn {} already"
163                         + " detected to be present", LOGGING_PREFIX, subnetId.getValue(), subnetIp, vpnName);
164                 return;
165             }
166             LOG.debug("{} onSubnetAddedToVpn: Creating new SubnetOpDataEntry node for subnet {} subnetIp {} "
167                     + "vpn {}", LOGGING_PREFIX, subnetId.getValue(), subnetIp, vpnName);
168             subOpBuilder = new SubnetOpDataEntryBuilder().withKey(new SubnetOpDataEntryKey(subnetId));
169             subOpBuilder.setSubnetId(subnetId);
170             subOpBuilder.setSubnetCidr(subnetIp);
171
172             if (isBgpVpn && !VpnUtil.isBgpVpn(vpnName, primaryRd)) {
173                 LOG.error("{} onSubnetAddedToVpn: The VPN Instance name {} does not have RD. Bailing out for"
174                         + " subnet {} subnetIp {} ", LOGGING_PREFIX, vpnName, subnetId.getValue(), subnetIp);
175                 return;
176             }
177             subOpBuilder.setVrfId(primaryRd);
178             subOpBuilder.setVpnName(vpnName);
179             subOpBuilder.setSubnetToDpn(new ArrayList<>());
180             subOpBuilder.setRouteAdvState(TaskState.Idle);
181             subOpBuilder.setElanTag(elanTag);
182             Long l3Vni = vpnInstanceOpData.getL3vni();
183             subOpBuilder.setL3vni(l3Vni);
184             subOpEntry = subOpBuilder.build();
185             SingleTransactionDataBroker.syncWrite(dataBroker, LogicalDatastoreType.OPERATIONAL, subOpIdentifier,
186                     subOpEntry, VpnUtil.SINGLE_TRANSACTION_BROKER_NO_RETRY);
187             LOG.info("{} onSubnetAddedToVpn: Added subnetopdataentry to OP Datastore for subnet {}", LOGGING_PREFIX,
188                     subnetId.getValue());
189         } catch (TransactionCommitFailedException e) {
190             LOG.error("{} Creation of SubnetOpDataEntry for subnet {} failed ", LOGGING_PREFIX,
191                     subnetId.getValue(), e);
192             // The second part of this method depends on subMap being non-null so fail fast here.
193             return;
194         } catch (RuntimeException e) { //TODO: Avoid this
195             LOG.error("{} onSubnetAddedToVpn: Unable to handle subnet {} with ip {} added to vpn {}", LOGGING_PREFIX,
196                     subnetId.getValue(), subnetIp, vpnName, e);
197             return;
198         } catch (ReadFailedException e) {
199             LOG.error("{} onSubnetAddedToVpn: Failed to read data store for subnet {} ip {} vpn {}", LOGGING_PREFIX,
200                     subnetId, subnetIp, vpnName);
201             return;
202         } finally {
203             vpnUtil.unlockSubnet(subnetId.getValue());
204         }
205         try {
206             //In second critical section , Port-Op-Data will be updated.
207             vpnUtil.lockSubnet(subnetId.getValue());
208             BigInteger dpnId = null;
209             SubnetToDpn subDpn = null;
210             Map<BigInteger, SubnetToDpn> subDpnMap = new HashMap<>();
211
212             optionalSubs = SingleTransactionDataBroker.syncReadOptional(dataBroker, LogicalDatastoreType.OPERATIONAL,
213                     subOpIdentifier);
214             subOpBuilder = new SubnetOpDataEntryBuilder(optionalSubs.get())
215                     .withKey(new SubnetOpDataEntryKey(subnetId));
216             List<Uuid> portList = subMap.getPortList();
217             if (portList != null) {
218                 for (Uuid port : portList) {
219                     Interface intfState = InterfaceUtils.getInterfaceStateFromOperDS(dataBroker,port.getValue());
220                     if (intfState != null) {
221                         try {
222                             dpnId = InterfaceUtils.getDpIdFromInterface(intfState);
223                         } catch (Exception e) {
224                             LOG.error("{} onSubnetAddedToVpn: Unable to obtain dpnId for interface {},"
225                                             + " subnetroute inclusion for this interface for subnet {} subnetIp {} "
226                                     + "vpn {} failed with exception", LOGGING_PREFIX, port.getValue(),
227                                     subnetId.getValue(), subnetIp, vpnName, e);
228                             continue;
229                         }
230                         if (dpnId.equals(BigInteger.ZERO)) {
231                             LOG.error("{} onSubnetAddedToVpn: Port {} is not assigned DPN yet,"
232                                             + " ignoring subnet {} subnetIP {} vpn {}", LOGGING_PREFIX, port.getValue(),
233                                     subnetId.getValue(), subnetIp, vpnName);
234                             continue;
235                         }
236                         subOpDpnManager.addPortOpDataEntry(port.getValue(), subnetId, dpnId);
237                         if (intfState.getOperStatus() != OperStatus.Up) {
238                             LOG.error("{} onSubnetAddedToVpn: Port {} is not UP yet, ignoring subnet {}"
239                                             + " subnetIp {} vpn {}", LOGGING_PREFIX, port.getValue(),
240                                     subnetId.getValue(), subnetIp, vpnName);
241                             continue;
242                         }
243                         subDpn = subOpDpnManager.addInterfaceToDpn(subnetId, dpnId, port.getValue());
244                         if (intfState.getOperStatus() == OperStatus.Up) {
245                             // port is UP
246                             subDpnMap.put(dpnId, subDpn);
247                         }
248                     } else {
249                         subOpDpnManager.addPortOpDataEntry(port.getValue(), subnetId, null);
250                     }
251                 }
252                 if (subDpnMap.size() > 0) {
253                     subOpBuilder.setSubnetToDpn(new ArrayList<>(subDpnMap.values()));
254                 }
255             }
256             electNewDpnForSubnetRoute(subOpBuilder, null /* oldDpnId */, subnetId,
257                     subMap.getNetworkId(), isBgpVpn);
258             subOpEntry = subOpBuilder.build();
259             SingleTransactionDataBroker.syncUpdate(dataBroker, LogicalDatastoreType.OPERATIONAL, subOpIdentifier,
260                     subOpEntry, VpnUtil.SINGLE_TRANSACTION_BROKER_NO_RETRY);
261             LOG.info("{} onSubnetAddedToVpn: Added PortOpDataEntry and VpnInterfaces to SubnetOpData"
262                             + " for subnet {} subnetIp {} vpn {} TaskState {} lastTaskState {}", LOGGING_PREFIX,
263                     subnetId.getValue(), subnetIp, vpnName, subOpEntry.getRouteAdvState(),
264                     subOpEntry.getLastAdvState());
265         } catch (RuntimeException e) {
266             LOG.error("{} onSubnetAddedToVpn: Unable to handle subnet {} with ip {} added to vpn {}", LOGGING_PREFIX,
267                     subnetId.getValue(), subnetIp, vpnName, e);
268         } catch (ReadFailedException e) {
269             LOG.error("{} onSubnetAddedToVpn: Failed to read data store for subnet {} ip {} vpn {}", LOGGING_PREFIX,
270                     subnetId, subnetIp, vpnName);
271         } catch (TransactionCommitFailedException ex) {
272             LOG.error("{} onSubnetAddedToVpn: Creation of SubnetOpDataEntry for subnet {} subnetIp {} vpn {} failed",
273                     LOGGING_PREFIX, subnetId.getValue(), subnetIp, vpnName, ex);
274         } finally {
275             vpnUtil.unlockSubnet(subnetId.getValue());
276         }
277     }
278
279     private long waitAndGetVpnIdIfInvalid(String vpnName) {
280         long vpnId = vpnUtil.getVpnId(vpnName);
281         if (vpnId == VpnConstants.INVALID_ID) {
282             LOG.debug("VpnId is invalid, waiting to fetch again: vpnName={}, vpnId={}", vpnName, vpnId);
283             vpnOpDataSyncer.waitForVpnDataReady(VpnOpDataType.vpnInstanceToId, vpnName,
284                     VpnConstants.PER_VPN_INSTANCE_MAX_WAIT_TIME_IN_MILLISECONDS);
285             vpnId = vpnUtil.getVpnId(vpnName);
286         }
287         return vpnId;
288     }
289
290     private VpnInstanceOpDataEntry waitAndGetVpnInstanceOpDataIfNull(String vpnName, String primaryRd) {
291         VpnInstanceOpDataEntry vpnInstanceOpData = vpnUtil.getVpnInstanceOpData(primaryRd);
292         if (vpnInstanceOpData == null) {
293             LOG.debug("vpnInstanceOpData is null, waiting to fetch again: vpnName={}", vpnName);
294             vpnOpDataSyncer.waitForVpnDataReady(VpnOpDataType.vpnOpData, vpnName,
295                     VpnConstants.PER_VPN_INSTANCE_OPDATA_MAX_WAIT_TIME_IN_MILLISECONDS);
296             vpnInstanceOpData = vpnUtil.getVpnInstanceOpData(primaryRd);
297         }
298         return vpnInstanceOpData;
299     }
300
301     // TODO Clean up the exception handling
302     @SuppressWarnings("checkstyle:IllegalCatch")
303     public void onSubnetDeletedFromVpn(Subnetmap subnetmap, boolean isBgpVpn) {
304         Uuid subnetId = subnetmap.getId();
305         LOG.info("{} onSubnetDeletedFromVpn: Subnet {} with ip {} being removed from vpnId {}", LOGGING_PREFIX,
306                 subnetId, subnetmap.getSubnetIp(), subnetmap.getVpnId());
307         //TODO(vivek): Change this to use more granularized lock at subnetId level
308         try {
309             vpnUtil.lockSubnet(subnetId.getValue());
310             InstanceIdentifier<SubnetOpDataEntry> subOpIdentifier =
311                     InstanceIdentifier.builder(SubnetOpData.class).child(SubnetOpDataEntry.class,
312                             new SubnetOpDataEntryKey(subnetId)).build();
313             Optional<SubnetOpDataEntry> optionalSubs = SingleTransactionDataBroker.syncReadOptional(dataBroker,
314                     LogicalDatastoreType.OPERATIONAL,
315                     subOpIdentifier);
316             if (!optionalSubs.isPresent()) {
317                 LOG.error("{} onSubnetDeletedFromVpn: SubnetOpDataEntry for subnet {} subnetIp {} vpn {}"
318                                 + " not available in datastore", LOGGING_PREFIX, subnetId.getValue(),
319                         subnetId.getValue(), subnetmap.getVpnId());
320                 return;
321             }
322             LOG.trace("{} onSubnetDeletedFromVpn: Removing the SubnetOpDataEntry node for subnet {} subnetIp {}"
323                             + " vpnName {} rd {} TaskState {}", LOGGING_PREFIX, subnetId.getValue(),
324                     optionalSubs.get().getSubnetCidr(), optionalSubs.get().getVpnName(),
325                     optionalSubs.get().getVrfId(), optionalSubs.get().getRouteAdvState());
326                 /* If subnet is deleted (or if its removed from VPN), the ports that are DOWN on that subnet
327                  * will continue to be stale in portOpData DS, as subDpnList used for portOpData removal will
328                  * contain only ports that are UP. So here we explicitly cleanup the ports of the subnet by
329                  * going through the list of ports on the subnet
330                  */
331             InstanceIdentifier<Subnetmap> subMapid =
332                     InstanceIdentifier.builder(Subnetmaps.class).child(Subnetmap.class,
333                             new SubnetmapKey(subnetId)).build();
334             Optional<Subnetmap> sm = SingleTransactionDataBroker.syncReadOptional(dataBroker,
335                     LogicalDatastoreType.CONFIGURATION, subMapid);
336             if (!sm.isPresent()) {
337                 LOG.error("{} onSubnetDeletedFromVpn: Stale ports removal: Unable to retrieve subnetmap entry"
338                                 + " for subnet {} subnetIp {} vpnName {}", LOGGING_PREFIX, subnetId.getValue(),
339                         optionalSubs.get().getSubnetCidr(), optionalSubs.get().getVpnName());
340             } else {
341                 Subnetmap subMap = sm.get();
342                 List<Uuid> portList = subMap.getPortList();
343                 if (portList != null) {
344                     for (Uuid port : portList) {
345                         InstanceIdentifier<PortOpDataEntry> portOpIdentifier =
346                                 InstanceIdentifier.builder(PortOpData.class).child(PortOpDataEntry.class,
347                                         new PortOpDataEntryKey(port.getValue())).build();
348                         LOG.trace("{} onSubnetDeletedFromVpn: Deleting portOpData entry for port {}"
349                                         + " from subnet {} subnetIp {} vpnName {} TaskState {}",
350                                 LOGGING_PREFIX, port.getValue(), subnetId.getValue(),
351                                 optionalSubs.get().getSubnetCidr(), optionalSubs.get().getVpnName(),
352                                 optionalSubs.get().getRouteAdvState());
353                         SingleTransactionDataBroker.syncDelete(dataBroker, LogicalDatastoreType.OPERATIONAL,
354                                 portOpIdentifier, VpnUtil.SINGLE_TRANSACTION_BROKER_NO_RETRY);
355                     }
356                 }
357             }
358
359             SubnetOpDataEntryBuilder subOpBuilder = new SubnetOpDataEntryBuilder(optionalSubs.get());
360             String rd = subOpBuilder.getVrfId();
361             String subnetIp = subOpBuilder.getSubnetCidr();
362             String vpnName = subOpBuilder.getVpnName();
363             //Withdraw the routes for all the interfaces on this subnet
364             //Remove subnet route entry from FIB
365             deleteSubnetRouteFromFib(rd, subnetIp, vpnName, isBgpVpn);
366             SingleTransactionDataBroker.syncDelete(dataBroker, LogicalDatastoreType.OPERATIONAL, subOpIdentifier,
367                     VpnUtil.SINGLE_TRANSACTION_BROKER_NO_RETRY);
368             LOG.info("{} onSubnetDeletedFromVpn: Removed subnetopdataentry successfully from Datastore"
369                             + " for subnet {} subnetIp {} vpnName {} rd {}", LOGGING_PREFIX, subnetId.getValue(),
370                     subnetIp, vpnName, rd);
371         } catch (RuntimeException e) { //TODO: Avoid this
372             LOG.error("{} onSubnetDeletedFromVpn: Unable to handle subnet {} with Ip {} removed from vpn {}",
373                     LOGGING_PREFIX, subnetId.getValue(), subnetmap.getSubnetIp(), subnetmap.getVpnId(), e);
374         } catch (TransactionCommitFailedException ex) {
375             LOG.error("{} onSubnetDeletedFromVpn: Removal of SubnetOpDataEntry for subnet {} subnetIp {}"
376                             + " vpnId {} failed", LOGGING_PREFIX, subnetId.getValue(), subnetmap.getSubnetIp(),
377                     subnetmap.getVpnId(), ex);
378         } catch (ReadFailedException e) {
379             LOG.error("{} onSubnetDeletedFromVpn: Failed to read data store for subnet {} ip {} vpn {}",
380                     LOGGING_PREFIX, subnetId, subnetmap.getSubnetIp(), subnetmap.getVpnId());
381         } finally {
382             vpnUtil.unlockSubnet(subnetId.getValue());
383         }
384     }
385
386     public void onSubnetUpdatedInVpn(Subnetmap subnetmap, Long elanTag) {
387         Uuid subnetId = subnetmap.getId();
388         String vpnName = subnetmap.getVpnId().getValue();
389         String subnetIp = subnetmap.getSubnetIp();
390
391         Preconditions.checkNotNull(subnetId,
392                 LOGGING_PREFIX + " onSubnetUpdatedInVpn: SubnetId cannot be null or empty!");
393         Preconditions.checkNotNull(subnetIp,
394                 LOGGING_PREFIX + " onSubnetUpdatedInVpn: SubnetPrefix cannot be null or empty!");
395         Preconditions.checkNotNull(vpnName, LOGGING_PREFIX + " onSubnetUpdatedInVpn: VpnName cannot be null or empty!");
396         Preconditions.checkNotNull(elanTag, LOGGING_PREFIX + " onSubnetUpdatedInVpn: ElanTag cannot be null or empty!");
397         try {
398             InstanceIdentifier<SubnetOpDataEntry> subOpIdentifier =
399                     InstanceIdentifier.builder(SubnetOpData.class).child(SubnetOpDataEntry.class,
400                             new SubnetOpDataEntryKey(subnetId)).build();
401             Optional<SubnetOpDataEntry> optionalSubs =
402                     SingleTransactionDataBroker.syncReadOptional(dataBroker, LogicalDatastoreType.OPERATIONAL,
403                             subOpIdentifier);
404             if (optionalSubs.isPresent()) {
405                 onSubnetDeletedFromVpn(subnetmap, true);
406             } else {
407                 onSubnetAddedToVpn(subnetmap, true, elanTag);
408             }
409             LOG.info("{} onSubnetUpdatedInVpn: subnet {} with Ip {} updated successfully for vpn {}", LOGGING_PREFIX,
410                     subnetId.getValue(), subnetIp, vpnName);
411         } catch (ReadFailedException e) {
412             LOG.error("onSubnetUpdatedInVpn: Failed to read data store for subnet{} ip {} elanTag {} vpn {}",subnetId,
413                     subnetIp, elanTag, vpnName);
414         }
415     }
416
417     // TODO Clean up the exception handling
418     @SuppressWarnings("checkstyle:IllegalCatch")
419     public void onPortAddedToSubnet(Subnetmap subnetmap, Uuid portId) {
420         Uuid subnetId = subnetmap.getId();
421         LOG.info("{} onPortAddedToSubnet: Port {} being added to subnet {}", LOGGING_PREFIX, portId.getValue(),
422                 subnetId.getValue());
423         //TODO(vivek): Change this to use more granularized lock at subnetId level
424         try {
425             vpnUtil.lockSubnet(subnetId.getValue());
426             InstanceIdentifier<SubnetOpDataEntry> subOpIdentifier =
427                     InstanceIdentifier.builder(SubnetOpData.class).child(SubnetOpDataEntry.class,
428                             new SubnetOpDataEntryKey(subnetId)).build();
429
430             Optional<SubnetOpDataEntry> optionalSubs = SingleTransactionDataBroker.syncReadOptional(dataBroker,
431                     LogicalDatastoreType.OPERATIONAL, subOpIdentifier);
432             if (!optionalSubs.isPresent()) {
433                 LOG.info("{} onPortAddedToSubnet: Port {} is part of a subnet {} that is not in VPN, ignoring",
434                         LOGGING_PREFIX, portId.getValue(), subnetId.getValue());
435                 return;
436             }
437             String vpnName = optionalSubs.get().getVpnName();
438             String subnetIp = optionalSubs.get().getSubnetCidr();
439             String rd = optionalSubs.get().getVrfId();
440             String routeAdvState = optionalSubs.get().getRouteAdvState().toString();
441             LOG.info("{} onPortAddedToSubnet: Port {} being added to subnet {} subnetIp {} vpnName {} rd {} "
442                             + "TaskState {}", LOGGING_PREFIX, portId.getValue(), subnetId.getValue(), subnetIp,
443                     vpnName, rd, routeAdvState);
444             subOpDpnManager.addPortOpDataEntry(portId.getValue(), subnetId, null);
445             Interface intfState = InterfaceUtils.getInterfaceStateFromOperDS(dataBroker,portId.getValue());
446             if (intfState == null) {
447                 // Interface State not yet available
448                 return;
449             }
450             final BigInteger dpnId;
451             try {
452                 dpnId = InterfaceUtils.getDpIdFromInterface(intfState);
453             } catch (Exception e) {
454                 LOG.error("{} onPortAddedToSubnet: Unable to obtain dpnId for interface {}. subnetroute inclusion"
455                                 + " for this interface failed for subnet {} subnetIp {} vpn {} rd {}",
456                         LOGGING_PREFIX, portId.getValue(), subnetId.getValue(), subnetIp, vpnName, rd, e);
457                 return;
458             }
459             if (dpnId.equals(BigInteger.ZERO)) {
460                 LOG.error("{} onPortAddedToSubnet: Port {} is not assigned DPN yet, ignoring subnetRoute "
461                                 + "inclusion for the interface into subnet {} subnetIp {} vpnName {} rd {}",
462                         LOGGING_PREFIX, portId.getValue(), subnetId.getValue(), subnetIp, vpnName, rd);
463                 return;
464             }
465             subOpDpnManager.addPortOpDataEntry(portId.getValue(), subnetId, dpnId);
466             if (intfState.getOperStatus() != OperStatus.Up) {
467                 LOG.error("{} onPortAddedToSubnet: Port {} is not UP yet, ignoring subnetRoute inclusion for "
468                                 + "the interface into subnet {} subnetIp {} vpnName {} rd {}", LOGGING_PREFIX,
469                         portId.getValue(), subnetId.getValue(), subnetIp, vpnName, rd);
470                 return;
471             }
472             LOG.debug("{} onPortAddedToSubnet: Port {} added. Updating the SubnetOpDataEntry node for subnet {} "
473                             + "subnetIp {} vpnName {} rd {} TaskState {}", LOGGING_PREFIX, portId.getValue(),
474                     subnetId.getValue(), subnetIp, vpnName, rd, routeAdvState);
475             SubnetToDpn subDpn = subOpDpnManager.addInterfaceToDpn(subnetId, dpnId, portId.getValue());
476             if (subDpn == null) {
477                 LOG.error("{} onPortAddedToSubnet: subnet-to-dpn list is null for subnetId {}. portId {}, "
478                                 + "vpnName {}, rd {}, subnetIp {}", LOGGING_PREFIX, subnetId.getValue(),
479                         portId.getValue(), vpnName, rd, subnetIp);
480                 return;
481             }
482             SubnetOpDataEntry subnetOpDataEntry = optionalSubs.get();
483             SubnetOpDataEntryBuilder subOpBuilder = new SubnetOpDataEntryBuilder(subnetOpDataEntry);
484             List<SubnetToDpn> subDpnList = subOpBuilder.getSubnetToDpn();
485             subDpnList.add(subDpn);
486             subOpBuilder.setSubnetToDpn(subDpnList);
487             if (subOpBuilder.getRouteAdvState() != TaskState.Advertised) {
488                 if (subOpBuilder.getNhDpnId() == null) {
489                     // No nexthop selected yet, elect one now
490                     electNewDpnForSubnetRoute(subOpBuilder, null /* oldDpnId */, subnetId,
491                             subnetmap.getNetworkId(), true);
492                 } else if (!VpnUtil.isExternalSubnetVpn(subnetOpDataEntry.getVpnName(), subnetId.getValue())) {
493                     // Already nexthop has been selected, only publishing to bgp required, so publish to bgp
494                     getNexthopTepAndPublishRoute(subOpBuilder, subnetId);
495                 }
496             }
497             SubnetOpDataEntry subOpEntry = subOpBuilder.build();
498             SingleTransactionDataBroker.syncWrite(dataBroker, LogicalDatastoreType.OPERATIONAL, subOpIdentifier,
499                     subOpEntry, VpnUtil.SINGLE_TRANSACTION_BROKER_NO_RETRY);
500             LOG.info("{} onPortAddedToSubnet: Updated subnetopdataentry to OP Datastore for port {} subnet {}"
501                             + " subnetIp {} vpnName {} rd {} TaskState {} lastTaskState {}", LOGGING_PREFIX,
502                     portId.getValue(), subnetId.getValue(), subOpEntry.getSubnetCidr(), subOpEntry.getVpnName(),
503                     subOpBuilder.getVrfId(), subOpEntry.getRouteAdvState(), subOpEntry.getLastAdvState());
504         } catch (RuntimeException e) { //TODO: Avoid this
505             LOG.error("{} onPortAddedToSubnet: Unable to handle port {} added to subnet {}", LOGGING_PREFIX,
506                     portId.getValue(), subnetId.getValue(), e);
507         } catch (ReadFailedException e) {
508             LOG.error("{} onPortAddedToSubnet: Failed to read data store for port {} subnet {}", LOGGING_PREFIX,
509                     portId, subnetId);
510         } catch (TransactionCommitFailedException e) {
511             LOG.error("{} onPortAddedToSubnet: Updation of subnetOpEntry for port {} subnet {} falied",
512                     LOGGING_PREFIX, portId.getValue(), subnetId.getValue(), e);
513         } finally {
514             vpnUtil.unlockSubnet(subnetId.getValue());
515         }
516     }
517
518     // TODO Clean up the exception handling
519     @SuppressWarnings("checkstyle:IllegalCatch")
520     public void onPortRemovedFromSubnet(Subnetmap subnetmap, Uuid portId) {
521         Uuid subnetId = subnetmap.getId();
522         //TODO(vivek): Change this to use more granularized lock at subnetId level
523         try {
524             vpnUtil.lockSubnet(subnetId.getValue());
525             PortOpDataEntry portOpEntry = subOpDpnManager.removePortOpDataEntry(portId.getValue(),
526                     subnetmap.getId());
527             if (portOpEntry == null) {
528                 return;
529             }
530             BigInteger dpnId = portOpEntry.getDpnId();
531             if (dpnId == null) {
532                 LOG.error("{} onPortRemovedFromSubnet:  Port {} does not have a DPNId associated,"
533                                 + " ignoring removal from subnet {}", LOGGING_PREFIX, portId.getValue(),
534                         subnetId.getValue());
535                 return;
536             }
537             boolean last = subOpDpnManager.removeInterfaceFromDpn(subnetId, dpnId, portId.getValue());
538             InstanceIdentifier<SubnetOpDataEntry> subOpIdentifier =
539                     InstanceIdentifier.builder(SubnetOpData.class).child(SubnetOpDataEntry.class,
540                             new SubnetOpDataEntryKey(subnetId)).build();
541             Optional<SubnetOpDataEntry> optionalSubs = SingleTransactionDataBroker.syncReadOptional(dataBroker,
542                     LogicalDatastoreType.OPERATIONAL, subOpIdentifier);
543             if (!optionalSubs.isPresent()) {
544                 LOG.info("{} onPortRemovedFromSubnet: Port {} is part of a subnet {} that is not in VPN,"
545                         + " ignoring", LOGGING_PREFIX, portId.getValue(), subnetId.getValue());
546                 return;
547             }
548             LOG.info("{} onPortRemovedFromSubnet: Port {} being removed. Updating the SubnetOpDataEntry"
549                             + " for subnet {} subnetIp {} vpnName {} rd {} TaskState {} lastTaskState {}",
550                     LOGGING_PREFIX, portId.getValue(), subnetId.getValue(), optionalSubs.get().getSubnetCidr(),
551                     optionalSubs.get().getVpnName(), optionalSubs.get().getVrfId(),
552                     optionalSubs.get().getRouteAdvState(), optionalSubs.get().getLastAdvState());
553             SubnetOpDataEntry subnetOpDataEntry = optionalSubs.get();
554             SubnetOpDataEntryBuilder subOpBuilder = new SubnetOpDataEntryBuilder(subnetOpDataEntry);
555             BigInteger nhDpnId = subOpBuilder.getNhDpnId();
556             if (nhDpnId != null && nhDpnId.equals(dpnId)) {
557                 // select another NhDpnId
558                 if (last) {
559                     LOG.debug("{} onPortRemovedFromSubnet: Last port {} being removed from subnet {} subnetIp {}"
560                                     + " vpnName {} rd {}", LOGGING_PREFIX, portId.getValue(), subnetId.getValue(),
561                             subOpBuilder.getSubnetCidr(), subOpBuilder.getVpnName(), subOpBuilder.getVrfId());
562                     // last port on this DPN, so we need to elect the new NHDpnId
563                     electNewDpnForSubnetRoute(subOpBuilder, nhDpnId, subnetId, subnetmap.getNetworkId(),
564                             !VpnUtil.isExternalSubnetVpn(subnetOpDataEntry.getVpnName(), subnetId.getValue()));
565                     SingleTransactionDataBroker.syncWrite(dataBroker, LogicalDatastoreType.OPERATIONAL,
566                             subOpIdentifier, subOpBuilder.build(), VpnUtil.SINGLE_TRANSACTION_BROKER_NO_RETRY);
567                     LOG.info("{} onPortRemovedFromSubnet: Updated subnetopdataentry to OP Datastore"
568                                     + " removing port {} from subnet {} subnetIp {} vpnName {} rd {}", LOGGING_PREFIX,
569                             portId.getValue(), subnetId.getValue(), subOpBuilder.getSubnetCidr(),
570                             subOpBuilder.getVpnName(), subOpBuilder.getVrfId());
571                 }
572             }
573         } catch (RuntimeException e) {
574             LOG.error("{} onPortRemovedFromSubnet: Unable to handle port {} removed from subnet {}", LOGGING_PREFIX,
575                     portId.getValue(), subnetId.getValue(), e);
576         } catch (ReadFailedException e) {
577             LOG.error("{} onPortRemovedFromSubnet: Failed to read data store for port {} subnet {}", LOGGING_PREFIX,
578                     portId, subnetId);
579         } catch (TransactionCommitFailedException e) {
580             LOG.error("{} onPortRemovedFromSubnet: Removal of portOp for {} from subnet {} failed", LOGGING_PREFIX,
581                     portId.getValue(), subnetId.getValue(), e);
582         } finally {
583             vpnUtil.unlockSubnet(subnetId.getValue());
584         }
585     }
586
587     // TODO Clean up the exception handling
588     @SuppressWarnings("checkstyle:IllegalCatch")
589     public void onInterfaceUp(BigInteger dpnId, String intfName, Uuid subnetId) {
590         //TODO(vivek): Change this to use more granularized lock at subnetId level
591         SubnetToDpn subDpn = null;
592         if (dpnId == null || Objects.equals(dpnId, BigInteger.ZERO)) {
593             LOG.error("{} onInterfaceUp: Unable to determine the DPNID for port {} on subnet {}", LOGGING_PREFIX,
594                     intfName, subnetId.getValue());
595             return;
596         }
597         try {
598             vpnUtil.lockSubnet(subnetId.getValue());
599             InstanceIdentifier<SubnetOpDataEntry> subOpIdentifier =
600                     InstanceIdentifier.builder(SubnetOpData.class).child(SubnetOpDataEntry.class,
601                             new SubnetOpDataEntryKey(subnetId)).build();
602             Optional<SubnetOpDataEntry> optionalSubs = SingleTransactionDataBroker.syncReadOptional(dataBroker,
603                     LogicalDatastoreType.OPERATIONAL, subOpIdentifier);
604             if (!optionalSubs.isPresent()) {
605                 LOG.trace("{} onInterfaceUp: SubnetOpDataEntry for subnet {} is not available."
606                         + " Ignoring interfaceUp for port{}", LOGGING_PREFIX, subnetId.getValue(), intfName);
607                 return;
608             }
609             subOpDpnManager.addPortOpDataEntry(intfName, subnetId, dpnId);
610             subDpn = subOpDpnManager.addInterfaceToDpn(subnetId, dpnId, intfName);
611             if (subDpn == null) {
612                 return;
613             }
614             SubnetOpDataEntry subnetOpDataEntry = optionalSubs.get();
615             SubnetOpDataEntryBuilder subOpBuilder = new SubnetOpDataEntryBuilder(subnetOpDataEntry);
616             LOG.info("{} onInterfaceUp: Updating the SubnetOpDataEntry node for subnet {} subnetIp {} vpn {}"
617                             + " rd {} TaskState {} lastTaskState {}" , LOGGING_PREFIX, subnetId.getValue(),
618                     subOpBuilder.getSubnetCidr(), subOpBuilder.getVpnName(), subOpBuilder.getVrfId(),
619                     subOpBuilder.getRouteAdvState(), subOpBuilder.getLastAdvState());
620             boolean isExternalSubnetVpn = VpnUtil.isExternalSubnetVpn(subnetOpDataEntry.getVpnName(),
621                     subnetId.getValue());
622             List<SubnetToDpn> subDpnList = subOpBuilder.getSubnetToDpn();
623             subDpnList.add(subDpn);
624             subOpBuilder.setSubnetToDpn(subDpnList);
625             if (subOpBuilder.getRouteAdvState() != TaskState.Advertised) {
626                 if (subOpBuilder.getNhDpnId() == null) {
627                     // No nexthop selected yet, elect one now
628                     electNewDpnForSubnetRoute(subOpBuilder, null /* oldDpnId */, subnetId,
629                             null /*networkId*/, !isExternalSubnetVpn);
630                 } else if (!isExternalSubnetVpn) {
631                     // Already nexthop has been selected, only publishing to bgp required, so publish to bgp
632                     getNexthopTepAndPublishRoute(subOpBuilder, subnetId);
633                 }
634             }
635             SubnetOpDataEntry subOpEntry = subOpBuilder.build();
636             SingleTransactionDataBroker.syncWrite(dataBroker, LogicalDatastoreType.OPERATIONAL, subOpIdentifier,
637                     subOpEntry, VpnUtil.SINGLE_TRANSACTION_BROKER_NO_RETRY);
638             LOG.info("{} onInterfaceUp: Updated subnetopdataentry to OP Datastore port {} up for subnet {}"
639                             + " subnetIp {} vpnName {} rd {} TaskState {} lastTaskState {} ", LOGGING_PREFIX, intfName,
640                     subnetId.getValue(), subOpEntry.getSubnetCidr(), subOpEntry.getVpnName(),
641                     subOpEntry.getVrfId(), subOpEntry.getRouteAdvState(), subOpEntry.getLastAdvState());
642         } catch (RuntimeException e) {
643             LOG.error("{} onInterfaceUp: Unable to handle interface up event for port {} in subnet {}",
644                     LOGGING_PREFIX, intfName, subnetId.getValue(), e);
645         } catch (ReadFailedException e) {
646             LOG.error("{} onInterfaceUp: Failed to read data store for interface {} dpn {} subnet {}", LOGGING_PREFIX,
647                     intfName, dpnId, subnetId);
648         } catch (TransactionCommitFailedException e) {
649             LOG.error("{} onInterfaceUp: Updation of SubnetOpDataEntry for subnet {} on port {} up failed",
650                     LOGGING_PREFIX, subnetId.getValue(), intfName, e);
651         } finally {
652             vpnUtil.unlockSubnet(subnetId.getValue());
653         }
654     }
655
656     // TODO Clean up the exception handling
657     @SuppressWarnings("checkstyle:IllegalCatch")
658     public void onInterfaceDown(final BigInteger dpnId, final String interfaceName, Uuid subnetId) {
659         if (dpnId == null || Objects.equals(dpnId, BigInteger.ZERO)) {
660             LOG.error("{} onInterfaceDown: Unable to determine the DPNID for port {} on subnet {}", LOGGING_PREFIX,
661                     interfaceName, subnetId.getValue());
662             return;
663         }
664         try {
665             vpnUtil.lockSubnet(subnetId.getValue());
666             boolean last = subOpDpnManager.removeInterfaceFromDpn(subnetId, dpnId, interfaceName);
667             InstanceIdentifier<SubnetOpDataEntry> subOpIdentifier =
668                     InstanceIdentifier.builder(SubnetOpData.class).child(SubnetOpDataEntry.class,
669                             new SubnetOpDataEntryKey(subnetId)).build();
670             Optional<SubnetOpDataEntry> optionalSubs = SingleTransactionDataBroker.syncReadOptional(dataBroker,
671                     LogicalDatastoreType.OPERATIONAL,
672                     subOpIdentifier);
673             if (!optionalSubs.isPresent()) {
674                 LOG.info("{} onInterfaceDown: SubnetOpDataEntry for subnet {} is not available."
675                         + " Ignoring port {} down event.", LOGGING_PREFIX, subnetId.getValue(), interfaceName);
676                 return;
677             }
678             SubnetOpDataEntry subnetOpDataEntry = optionalSubs.get();
679             SubnetOpDataEntryBuilder subOpBuilder = new SubnetOpDataEntryBuilder(subnetOpDataEntry);
680             LOG.info("{} onInterfaceDown: Updating the SubnetOpDataEntry node for subnet {} subnetIp {}"
681                             + " vpnName {} rd {} TaskState {} lastTaskState {} on port {} down", LOGGING_PREFIX,
682                     subnetId.getValue(), subOpBuilder.getSubnetCidr(), subOpBuilder.getVpnName(),
683                     subOpBuilder.getVrfId(), subOpBuilder.getRouteAdvState(), subOpBuilder.getLastAdvState(),
684                     interfaceName);
685             BigInteger nhDpnId = subOpBuilder.getNhDpnId();
686             if (nhDpnId != null && nhDpnId.equals(dpnId)) {
687                 // select another NhDpnId
688                 if (last) {
689                     LOG.debug("{} onInterfaceDown: Last active port {} on the subnet {} subnetIp {} vpn {}"
690                                     + " rd {}", LOGGING_PREFIX, interfaceName, subnetId.getValue(),
691                             subOpBuilder.getSubnetCidr(), subOpBuilder.getVpnName(), subOpBuilder.getVrfId());
692                     // last port on this DPN, so we need to elect the new NHDpnId
693                     electNewDpnForSubnetRoute(subOpBuilder, dpnId, subnetId, null /*networkId*/,
694                             !VpnUtil.isExternalSubnetVpn(subnetOpDataEntry.getVpnName(), subnetId.getValue()));
695                     SingleTransactionDataBroker.syncWrite(dataBroker, LogicalDatastoreType.OPERATIONAL,
696                             subOpIdentifier, subOpBuilder.build(), VpnUtil.SINGLE_TRANSACTION_BROKER_NO_RETRY);
697                     LOG.info("{} onInterfaceDown: Updated subnetopdataentry for subnet {} subnetIp {} vpnName {}"
698                                     + " rd {} to OP Datastore on port {} down ", LOGGING_PREFIX, subnetId.getValue(),
699                             subOpBuilder.getSubnetCidr(), subOpBuilder.getVpnName(), subOpBuilder.getVrfId(),
700                             interfaceName);
701                 }
702             }
703         } catch (RuntimeException e) { //TODO: Remove RuntimeException
704             LOG.error("{} onInterfaceDown: Unable to handle interface down event for port {} in subnet {}",
705                     LOGGING_PREFIX, interfaceName, subnetId.getValue(), e);
706         } catch (ReadFailedException e) {
707             LOG.error("{} onInterfaceDown: Failed to read data store for interface {} dpn {} subnet {}",
708                     LOGGING_PREFIX, interfaceName, dpnId, subnetId.getValue(), e);
709         } catch (TransactionCommitFailedException ex) {
710             LOG.error("{} onInterfaceDown: SubnetOpDataEntry update on interface {} down event for subnet {} failed",
711                     LOGGING_PREFIX, interfaceName, subnetId.getValue(), ex);
712         } finally {
713             vpnUtil.unlockSubnet(subnetId.getValue());
714         }
715     }
716
717     // TODO Clean up the exception handling
718     @SuppressWarnings("checkstyle:IllegalCatch")
719     public void updateSubnetRouteOnTunnelUpEvent(Uuid subnetId, BigInteger dpnId) {
720         LOG.info("{} updateSubnetRouteOnTunnelUpEvent: Subnet {} Dpn {}", LOGGING_PREFIX, subnetId.getValue(),
721                 dpnId.toString());
722         try {
723             vpnUtil.lockSubnet(subnetId.getValue());
724             InstanceIdentifier<SubnetOpDataEntry> subOpIdentifier =
725                     InstanceIdentifier.builder(SubnetOpData.class).child(SubnetOpDataEntry.class,
726                             new SubnetOpDataEntryKey(subnetId)).build();
727             Optional<SubnetOpDataEntry> optionalSubs = SingleTransactionDataBroker.syncReadOptional(dataBroker,
728                     LogicalDatastoreType.OPERATIONAL, subOpIdentifier);
729             if (!optionalSubs.isPresent()) {
730                 LOG.error("{} updateSubnetRouteOnTunnelUpEvent: SubnetOpDataEntry for subnet {} is not available",
731                         LOGGING_PREFIX, subnetId.getValue());
732                 return;
733             }
734             LOG.info("{} updateSubnetRouteOnTunnelUpEvent: Subnet {} subnetIp {} vpnName {} rd {} TaskState {}"
735                             + " lastTaskState {} Dpn {}", LOGGING_PREFIX, subnetId.getValue(),
736                     optionalSubs.get().getSubnetCidr(), optionalSubs.get().getVpnName(),
737                     optionalSubs.get().getVrfId(), optionalSubs.get().getRouteAdvState(),
738                     optionalSubs.get().getLastAdvState(), dpnId.toString());
739             SubnetOpDataEntry subOpEntry = optionalSubs.get();
740             SubnetOpDataEntryBuilder subOpBuilder = new SubnetOpDataEntryBuilder(subOpEntry);
741             boolean isExternalSubnetVpn = VpnUtil.isExternalSubnetVpn(subOpEntry.getVpnName(), subnetId.getValue());
742             if (subOpBuilder.getRouteAdvState() != TaskState.Advertised) {
743                 if (subOpBuilder.getNhDpnId() == null) {
744                     // No nexthop selected yet, elect one now
745                     electNewDpnForSubnetRoute(subOpBuilder, null /* oldDpnId */, subnetId,
746                             null /*networkId*/, !isExternalSubnetVpn);
747                 } else if (!isExternalSubnetVpn) {
748                     // Already nexthop has been selected, only publishing to bgp required, so publish to bgp
749                     getNexthopTepAndPublishRoute(subOpBuilder, subnetId);
750                 }
751             }
752             subOpEntry = subOpBuilder.build();
753             SingleTransactionDataBroker.syncWrite(dataBroker, LogicalDatastoreType.OPERATIONAL, subOpIdentifier,
754                     subOpEntry, VpnUtil.SINGLE_TRANSACTION_BROKER_NO_RETRY);
755             LOG.info("{} updateSubnetRouteOnTunnelUpEvent: Updated subnetopdataentry to OP Datastore tunnel up"
756                             + " on dpn {} for subnet {} subnetIp {} vpnName {} rd {} TaskState {} lastTaskState {}",
757                     LOGGING_PREFIX, dpnId.toString(), subnetId.getValue(), subOpEntry.getSubnetCidr(),
758                     subOpEntry.getVpnName(), subOpEntry.getVrfId(), subOpEntry.getRouteAdvState(),
759                     subOpEntry.getLastAdvState());
760         } catch (RuntimeException e) { //TODO: lockSubnet() throws this exception. Rectify lockSubnet()
761             LOG.error("{} updateSubnetRouteOnTunnelUpEvent: Unable to handle tunnel up event for subnetId {} dpnId {}",
762                     LOGGING_PREFIX, subnetId.getValue(), dpnId.toString(), e);
763         } catch (TransactionCommitFailedException ex) {
764             LOG.error("{} updateSubnetRouteOnTunnelUpEvent: Failed to update subnetRoute for subnet {} on dpn {}",
765                     LOGGING_PREFIX, subnetId.getValue(), dpnId.toString(), ex);
766         } catch (ReadFailedException e) {
767             LOG.error("{} updateSubnetRouteOnTunnelUpEvent: Failed to read data store for subnet {} on dpn {}",
768                     LOGGING_PREFIX, subnetId.getValue(), dpnId.toString(), e);
769         }
770         finally {
771             vpnUtil.unlockSubnet(subnetId.getValue());
772         }
773     }
774
775     // TODO Clean up the exception handling
776     @SuppressWarnings("checkstyle:IllegalCatch")
777     public void updateSubnetRouteOnTunnelDownEvent(Uuid subnetId, BigInteger dpnId) {
778         LOG.info("updateSubnetRouteOnTunnelDownEvent: Subnet {} Dpn {}", subnetId.getValue(), dpnId.toString());
779         //TODO(vivek): Change this to use more granularized lock at subnetId level
780         try {
781             vpnUtil.lockSubnet(subnetId.getValue());
782             InstanceIdentifier<SubnetOpDataEntry> subOpIdentifier =
783                     InstanceIdentifier.builder(SubnetOpData.class).child(SubnetOpDataEntry.class,
784                             new SubnetOpDataEntryKey(subnetId)).build();
785             Optional<SubnetOpDataEntry> optionalSubs = SingleTransactionDataBroker.syncReadOptional(dataBroker,
786                     LogicalDatastoreType.OPERATIONAL, subOpIdentifier);
787             if (!optionalSubs.isPresent()) {
788                 LOG.error("{} updateSubnetRouteOnTunnelDownEvent: SubnetOpDataEntry for subnet {}"
789                         + " is not available", LOGGING_PREFIX, subnetId.getValue());
790                 return;
791             }
792             LOG.debug("{} updateSubnetRouteOnTunnelDownEvent: Dpn {} Subnet {} subnetIp {} vpnName {} rd {}"
793                             + " TaskState {} lastTaskState {}", LOGGING_PREFIX, dpnId.toString(), subnetId.getValue(),
794                     optionalSubs.get().getSubnetCidr(), optionalSubs.get().getVpnName(),
795                     optionalSubs.get().getVrfId(), optionalSubs.get().getRouteAdvState(),
796                     optionalSubs.get().getLastAdvState());
797             SubnetOpDataEntry subOpEntry = null;
798             SubnetOpDataEntryBuilder subOpBuilder = new SubnetOpDataEntryBuilder(optionalSubs.get());
799             BigInteger nhDpnId = subOpBuilder.getNhDpnId();
800             if (nhDpnId != null && nhDpnId.equals(dpnId)) {
801                 electNewDpnForSubnetRoute(subOpBuilder, dpnId, subnetId, null /*networkId*/, true);
802                 subOpEntry = subOpBuilder.build();
803                 SingleTransactionDataBroker.syncWrite(dataBroker, LogicalDatastoreType.OPERATIONAL, subOpIdentifier,
804                         subOpEntry, VpnUtil.SINGLE_TRANSACTION_BROKER_NO_RETRY);
805                 LOG.info("{} updateSubnetRouteOnTunnelDownEvent: Subnet {} Dpn {} subnetIp {} vpnName {} rd {}"
806                                 + " TaskState {} lastTaskState {}", LOGGING_PREFIX, subnetId.getValue(),
807                         dpnId.toString(), optionalSubs.get().getSubnetCidr(), optionalSubs.get().getVpnName(),
808                         optionalSubs.get().getVrfId(), optionalSubs.get().getRouteAdvState(),
809                         optionalSubs.get().getLastAdvState());
810             }
811         } catch (RuntimeException e) {
812             LOG.error("{} updateSubnetRouteOnTunnelDownEvent: Unable to handle tunnel down event for subnetId {}"
813                     + " dpnId {}", LOGGING_PREFIX, subnetId.getValue(), dpnId.toString(), e);
814         } catch (ReadFailedException e) {
815             LOG.error("{} Failed to read data store for subnet {} dpn {}", LOGGING_PREFIX, subnetId, dpnId);
816         } catch (TransactionCommitFailedException e) {
817             LOG.error("{} updateSubnetRouteOnTunnelDownEvent: Updation of SubnetOpDataEntry for subnet {}"
818                     + " on dpn {} failed", LOGGING_PREFIX, subnetId.getValue(), dpnId, e);
819         } finally {
820             vpnUtil.unlockSubnet(subnetId.getValue());
821         }
822     }
823
824     @SuppressWarnings("checkstyle:IllegalCatch")
825     private void publishSubnetRouteToBgp(SubnetOpDataEntryBuilder subOpBuilder, String nextHopIp) {
826         try {
827             //BGP manager will handle withdraw and advertise internally if prefix
828             //already exist
829             long label = 0;
830             long l3vni = 0;
831
832             VrfEntry.EncapType encapType =  VpnUtil.getEncapType(VpnUtil.isL3VpnOverVxLan(l3vni));
833             if (encapType.equals(VrfEntry.EncapType.Vxlan)) {
834                 l3vni = subOpBuilder.getL3vni();
835             } else {
836                 label = subOpBuilder.getLabel();
837             }
838             bgpManager.advertisePrefix(subOpBuilder.getVrfId(), null /*macAddress*/, subOpBuilder.getSubnetCidr(),
839                     Arrays.asList(nextHopIp), encapType,  label, l3vni,
840                     0 /*l2vni*/, null /*gatewayMacAddress*/);
841             subOpBuilder.setLastAdvState(subOpBuilder.getRouteAdvState()).setRouteAdvState(TaskState.Advertised);
842         } catch (Exception e) {
843             LOG.error("{} publishSubnetRouteToBgp: Subnet route not advertised for subnet {} subnetIp {} vpn {} rd {}"
844                     + " with dpnid {}", LOGGING_PREFIX, subOpBuilder.getSubnetId().getValue(),
845                     subOpBuilder.getSubnetCidr(), subOpBuilder.getVpnName(), subOpBuilder.getVrfId(), nextHopIp, e);
846         }
847     }
848
849     private void getNexthopTepAndPublishRoute(SubnetOpDataEntryBuilder subOpBuilder, Uuid subnetId) {
850         String nhTepIp = InterfaceUtils.getEndpointIpAddressForDPN(dataBroker,
851                 subOpBuilder.getNhDpnId());
852         if (nhTepIp != null) {
853             publishSubnetRouteToBgp(subOpBuilder, nhTepIp);
854         } else {
855             LOG.warn("Unable to find nexthopip for rd {} subnetroute subnetip {} for dpnid {}",
856                     subOpBuilder.getVrfId(), subOpBuilder.getSubnetCidr(),
857                     subOpBuilder.getNhDpnId().toString());
858             electNewDpnForSubnetRoute(subOpBuilder, null /* oldDpnId */, subnetId, null /*networkId*/, true);
859         }
860     }
861
862     // TODO Clean up the exception handling
863     @SuppressWarnings("checkstyle:IllegalCatch")
864     private boolean addSubnetRouteToFib(String rd, String subnetIp, BigInteger nhDpnId, String nextHopIp,
865                                         String vpnName, Long elanTag, long label, long l3vni,
866                                         Uuid subnetId, boolean isBgpVpn, String networkName) {
867
868         Preconditions.checkNotNull(rd,
869                 LOGGING_PREFIX + " addSubnetRouteToFib: RouteDistinguisher cannot be null or empty!");
870         Preconditions.checkNotNull(subnetIp,
871                 LOGGING_PREFIX + " addSubnetRouteToFib: SubnetRouteIp cannot be null or empty!");
872         Preconditions.checkNotNull(vpnName, LOGGING_PREFIX + " addSubnetRouteToFib: vpnName cannot be null or empty!");
873         Preconditions.checkNotNull(elanTag, LOGGING_PREFIX + " addSubnetRouteToFib: elanTag cannot be null or empty!");
874         Preconditions.checkNotNull(label, LOGGING_PREFIX + " addSubnetRouteToFib: label cannot be null or empty!");
875         VrfEntry.EncapType encapType = VpnUtil.getEncapType(VpnUtil.isL3VpnOverVxLan(l3vni));
876         VpnPopulator vpnPopulator = L3vpnRegistry.getRegisteredPopulator(encapType);
877         LOG.info("{} addSubnetRouteToFib: Adding SubnetRoute fib entry for vpnName {}, subnetIP {}, elanTag {}",
878                 LOGGING_PREFIX, vpnName, subnetIp, elanTag);
879         L3vpnInput input = new L3vpnInput().setRouteOrigin(RouteOrigin.CONNECTED).setRd(rd).setVpnName(vpnName)
880                 .setSubnetIp(subnetIp).setNextHopIp(nextHopIp).setL3vni(l3vni).setLabel(label).setElanTag(elanTag)
881                 .setDpnId(nhDpnId).setEncapType(encapType).setNetworkName(networkName).setPrimaryRd(rd);
882         if (!isBgpVpn) {
883             vpnPopulator.populateFib(input, null /*writeCfgTxn*/);
884             return true;
885         }
886         Preconditions.checkNotNull(nextHopIp, LOGGING_PREFIX + "NextHopIp cannot be null or empty!");
887         vpnUtil.syncWrite(LogicalDatastoreType.OPERATIONAL, VpnUtil
888                 .getPrefixToInterfaceIdentifier(vpnUtil.getVpnId(vpnName), subnetIp), VpnUtil
889                 .getPrefixToInterface(nhDpnId, subnetId.getValue(), subnetIp, Prefixes.PrefixCue.SubnetRoute));
890         vpnPopulator.populateFib(input, null /*writeCfgTxn*/);
891         try {
892             // BGP manager will handle withdraw and advertise internally if prefix
893             // already exist
894             bgpManager.advertisePrefix(rd, null /*macAddress*/, subnetIp, Collections.singletonList(nextHopIp),
895                     encapType, label, l3vni, 0 /*l2vni*/, null /*gatewayMacAddress*/);
896         } catch (Exception e) {
897             LOG.error("{} addSubnetRouteToFib: Subnet route not advertised for subnet {} subnetIp {} vpnName {} rd {} "
898                     + "with dpnid {}", LOGGING_PREFIX, subnetId.getValue(), subnetIp, vpnName, rd, nhDpnId, e);
899             return false;
900         }
901         return true;
902     }
903
904     private int getLabel(String rd, String subnetIp) {
905         int label = vpnUtil.getUniqueId(VpnConstants.VPN_IDPOOL_NAME, VpnUtil.getNextHopLabelKey(rd, subnetIp));
906         LOG.trace("{} getLabel: Allocated subnetroute label {} for rd {} prefix {}", LOGGING_PREFIX, label, rd,
907                 subnetIp);
908         return label;
909     }
910
911     // TODO Clean up the exception handling
912     @SuppressWarnings("checkstyle:IllegalCatch")
913     private boolean deleteSubnetRouteFromFib(String rd, String subnetIp, String vpnName, boolean isBgpVpn) {
914         Preconditions.checkNotNull(rd,
915                 LOGGING_PREFIX + " deleteSubnetRouteFromFib: RouteDistinguisher cannot be null or empty!");
916         Preconditions.checkNotNull(subnetIp,
917                 LOGGING_PREFIX +  " deleteSubnetRouteFromFib: SubnetRouteIp cannot be null or empty!");
918         deleteSubnetRouteFibEntryFromDS(rd, subnetIp, vpnName);
919         if (isBgpVpn) {
920             try {
921                 bgpManager.withdrawPrefix(rd, subnetIp);
922             } catch (Exception e) {
923                 LOG.error("{} deleteSubnetRouteFromFib: Subnet route not withdrawn for subnetIp {} vpn {} rd {}",
924                         LOGGING_PREFIX, subnetIp, vpnName, rd, e);
925                 return false;
926             }
927         }
928         return true;
929     }
930
931     public void deleteSubnetRouteFibEntryFromDS(String rd, String prefix, String vpnName) {
932         fibManager.removeFibEntry(rd, prefix, null);
933         List<VpnInstanceOpDataEntry> vpnsToImportRoute = vpnUtil.getVpnsImportingMyRoute(vpnName);
934         for (VpnInstanceOpDataEntry vpnInstance : vpnsToImportRoute) {
935             String importingRd = vpnInstance.getVrfId();
936             fibManager.removeFibEntry(importingRd, prefix, null);
937             LOG.info("SUBNETROUTE: deleteSubnetRouteFibEntryFromDS: Deleted imported subnet route rd {} prefix {}"
938                     + " from vpn {} importingRd {}", rd, prefix, vpnInstance.getVpnInstanceName(), importingRd);
939         }
940         LOG.info("SUBNETROUTE: deleteSubnetRouteFibEntryFromDS: Removed subnetroute FIB for prefix {} rd {}"
941                 + " vpnName {}", prefix, rd, vpnName);
942     }
943
944     // TODO Clean up the exception handling
945     @SuppressWarnings("checkstyle:IllegalCatch")
946     private void electNewDpnForSubnetRoute(SubnetOpDataEntryBuilder subOpBuilder, @Nullable BigInteger oldDpnId,
947                                            Uuid subnetId, Uuid networkId, boolean isBgpVpn) {
948         List<SubnetToDpn> subDpnList = null;
949         boolean isRouteAdvertised = false;
950         subDpnList = subOpBuilder.getSubnetToDpn();
951         String rd = subOpBuilder.getVrfId();
952         String subnetIp = subOpBuilder.getSubnetCidr();
953         String vpnName = subOpBuilder.getVpnName();
954         long elanTag = subOpBuilder.getElanTag();
955         boolean isAlternateDpnSelected = false;
956         long l3vni = 0;
957         long label = 0;
958         String networkName = networkId != null ? networkId.getValue() : null;
959
960         LOG.info("{} electNewDpnForSubnetRoute: Handling subnet {} subnetIp {} vpn {} rd {} TaskState {}"
961                 + " lastTaskState {}", LOGGING_PREFIX, subnetId.getValue(), subnetIp, subOpBuilder.getVpnName(),
962                 subOpBuilder.getVrfId(), subOpBuilder.getRouteAdvState(), subOpBuilder.getLastAdvState());
963         if (!isBgpVpn) {
964             // Non-BGPVPN as it stands here represents use-case of External Subnets of VLAN-Provider-Network
965             //  TODO(Tomer):  Pulling in both external and internal VLAN-Provider-Network need to be
966             // blended more better into this design.
967             if (VpnUtil.isL3VpnOverVxLan(subOpBuilder.getL3vni())) {
968                 l3vni = subOpBuilder.getL3vni();
969             } else {
970                 label = getLabel(rd, subnetIp);
971                 subOpBuilder.setLabel(label);
972             }
973             isRouteAdvertised = addSubnetRouteToFib(rd, subnetIp, null /* nhDpnId */, null /* nhTepIp */,
974                     vpnName, elanTag, label, l3vni, subnetId, isBgpVpn, networkName);
975             if (isRouteAdvertised) {
976                 subOpBuilder.setRouteAdvState(TaskState.Advertised);
977             } else {
978                 LOG.error("{} electNewDpnForSubnetRoute: Unable to find TepIp for subnet {} subnetip {} vpnName {}"
979                     + " rd {}, attempt next dpn", LOGGING_PREFIX, subnetId.getValue(), subnetIp,
980                     vpnName, rd);
981                 subOpBuilder.setRouteAdvState(TaskState.PendingAdvertise);
982             }
983             return;
984         }
985
986         String nhTepIp = null;
987         BigInteger nhDpnId = null;
988         for (SubnetToDpn subnetToDpn : subDpnList) {
989             if (subnetToDpn.getDpnId().equals(oldDpnId)) {
990                 // Is this same is as input dpnId, then ignore it
991                 continue;
992             }
993             nhDpnId = subnetToDpn.getDpnId();
994             if (vpnNodeListener.isConnectedNode(nhDpnId)) {
995                 // selected dpnId is connected to ODL
996                 // but does it have a TEP configured at all?
997                 try {
998                     nhTepIp = InterfaceUtils.getEndpointIpAddressForDPN(dataBroker, nhDpnId);
999                     if (nhTepIp != null) {
1000                         isAlternateDpnSelected = true;
1001                         break;
1002                     }
1003                 } catch (Exception e) {
1004                     LOG.warn("{} electNewDpnForSubnetRoute: Unable to find TepIp for rd {} subnetroute subnetip {}"
1005                             + " for dpnid {}, attempt next", LOGGING_PREFIX, rd, subnetIp, nhDpnId.toString(), e);
1006                 }
1007             }
1008         }
1009         if (!isAlternateDpnSelected) {
1010             //If no alternate Dpn is selected as nextHopDpn, withdraw the subnetroute if it had a nextHop already.
1011             if (isRouteAdvertised(subOpBuilder) && oldDpnId != null) {
1012                 LOG.info("{} electNewDpnForSubnetRoute: No alternate DPN available for subnet {} subnetIp {} vpn {}"
1013                         + " rd {} Prefix withdrawn from BGP", LOGGING_PREFIX, subnetId.getValue(), subnetIp, vpnName,
1014                         rd);
1015                 // Withdraw route from BGP for this subnet
1016                 boolean routeWithdrawn = deleteSubnetRouteFromFib(rd, subnetIp, vpnName, isBgpVpn);
1017                 subOpBuilder.setNhDpnId(null);
1018                 subOpBuilder.setLastAdvState(subOpBuilder.getRouteAdvState());
1019                 if (routeWithdrawn) {
1020                     subOpBuilder.setRouteAdvState(TaskState.Withdrawn);
1021                 } else {
1022                     LOG.error("{} electNewDpnForSubnetRoute: Withdrawing NextHopDPN {} for subnet {} subnetIp {}"
1023                         + " vpn {} rd {} from BGP failed", LOGGING_PREFIX, oldDpnId, subnetId.getValue(),
1024                         subnetIp, vpnName, rd);
1025                     subOpBuilder.setRouteAdvState(TaskState.PendingWithdraw);
1026                 }
1027             }
1028         } else {
1029             //If alternate Dpn is selected as nextHopDpn, use that for subnetroute.
1030             subOpBuilder.setNhDpnId(nhDpnId);
1031             if (VpnUtil.isL3VpnOverVxLan(subOpBuilder.getL3vni())) {
1032                 l3vni = subOpBuilder.getL3vni();
1033             } else {
1034                 label = getLabel(rd, subnetIp);
1035                 subOpBuilder.setLabel(label);
1036             }
1037             //update the VRF entry for the subnetroute.
1038             isRouteAdvertised = addSubnetRouteToFib(rd, subnetIp, nhDpnId, nhTepIp,
1039                     vpnName, elanTag, label, l3vni, subnetId, isBgpVpn, networkName);
1040             subOpBuilder.setLastAdvState(subOpBuilder.getRouteAdvState());
1041             if (isRouteAdvertised) {
1042                 subOpBuilder.setRouteAdvState(TaskState.Advertised);
1043             } else {
1044                 LOG.error("{} electNewDpnForSubnetRoute: Swapping to add new NextHopDpn {} for subnet {} subnetIp {}"
1045                         + " vpn {} rd {} failed", LOGGING_PREFIX, nhDpnId, subnetId.getValue(), subnetIp, vpnName, rd);
1046                 subOpBuilder.setRouteAdvState(TaskState.PendingAdvertise);
1047             }
1048         }
1049     }
1050
1051     private boolean isRouteAdvertised(SubnetOpDataEntryBuilder subOpBuilder) {
1052         return subOpBuilder.getRouteAdvState() == TaskState.Advertised
1053                 || subOpBuilder.getRouteAdvState() == TaskState.PendingAdvertise;
1054     }
1055 }
1056