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