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