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