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.math.BigInteger;
13 import java.util.ArrayList;
14 import java.util.Arrays;
15 import java.util.HashMap;
16 import java.util.List;
18 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
19 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
20 import org.opendaylight.genius.mdsalutil.MDSALUtil;
21 import org.opendaylight.netvirt.bgpmanager.api.IBgpManager;
22 import org.opendaylight.netvirt.vpnmanager.utilities.InterfaceUtils;
23 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface;
24 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface.OperStatus;
25 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.IdManagerService;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.lockmanager.rev160413.LockManagerService;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.PortOpData;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.SubnetOpData;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.TaskState;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.port.op.data.PortOpDataEntry;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.port.op.data.PortOpDataEntryKey;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.subnet.op.data.SubnetOpDataEntry;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.subnet.op.data.SubnetOpDataEntryBuilder;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.subnet.op.data.SubnetOpDataEntryKey;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.subnet.op.data.subnet.op.data.entry.SubnetToDpn;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ExternalNetworks;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.external.networks.Networks;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.external.networks.NetworksKey;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.NeutronvpnListener;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.PortAddedToSubnet;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.PortRemovedFromSubnet;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.RouterAssociatedToVpn;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.RouterDisassociatedFromVpn;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.SubnetAddedToVpn;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.SubnetAddedToVpnBuilder;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.SubnetDeletedFromVpn;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.SubnetDeletedFromVpnBuilder;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.SubnetUpdatedInVpn;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.Subnetmaps;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.subnetmaps.Subnetmap;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.subnetmaps.SubnetmapKey;
53 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
54 import org.slf4j.Logger;
55 import org.slf4j.LoggerFactory;
57 public class VpnSubnetRouteHandler implements NeutronvpnListener {
58 private static final Logger logger = LoggerFactory.getLogger(VpnSubnetRouteHandler.class);
59 private final DataBroker dataBroker;
60 private final SubnetOpDpnManager subOpDpnManager;
61 private final IBgpManager bgpManager;
62 private final VpnInterfaceManager vpnInterfaceManager;
63 private final IdManagerService idManager;
64 private LockManagerService lockManager;
66 public VpnSubnetRouteHandler(final DataBroker dataBroker, final SubnetOpDpnManager subnetOpDpnManager,
67 final IBgpManager bgpManager, final VpnInterfaceManager vpnIntfManager,
68 final IdManagerService idManager, LockManagerService lockManagerService) {
69 this.dataBroker = dataBroker;
70 this.subOpDpnManager = subnetOpDpnManager;
71 this.bgpManager = bgpManager;
72 this.vpnInterfaceManager = vpnIntfManager;
73 this.idManager = idManager;
74 this.lockManager = lockManagerService;
78 public void onSubnetAddedToVpn(SubnetAddedToVpn notification) {
79 if (!notification.isExternalVpn()) {
83 Uuid subnetId = notification.getSubnetId();
84 String vpnName = notification.getVpnName();
85 String subnetIp = notification.getSubnetIp();
86 Long elanTag = notification.getElanTag();
88 Preconditions.checkNotNull(subnetId, "SubnetId cannot be null or empty!");
89 Preconditions.checkNotNull(subnetIp, "SubnetPrefix cannot be null or empty!");
90 Preconditions.checkNotNull(vpnName, "VpnName cannot be null or empty!");
91 Preconditions.checkNotNull(elanTag, "ElanTag cannot be null or empty!");
93 logger.info("onSubnetAddedToVpn: Subnet " + subnetId.getValue() + " being added to vpn");
94 //TODO(vivek): Change this to use more granularized lock at subnetId level
96 VpnUtil.lockSubnet(lockManager, subnetId.getValue());
98 Subnetmap subMap = null;
100 // Please check if subnetId belongs to an External Network
101 InstanceIdentifier<Subnetmap> subMapid = InstanceIdentifier.builder(Subnetmaps.class).
102 child(Subnetmap.class, new SubnetmapKey(subnetId)).build();
103 Optional<Subnetmap> sm = VpnUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, subMapid);
104 if (!sm.isPresent()) {
105 logger.error("onSubnetAddedToVpn: Unable to retrieve subnetmap entry for subnet : " + subnetId);
109 InstanceIdentifier<Networks> netsIdentifier = InstanceIdentifier.builder(ExternalNetworks.class).
110 child(Networks.class, new NetworksKey(subMap.getNetworkId())).build();
111 Optional<Networks> optionalNets = VpnUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, netsIdentifier);
112 if (optionalNets.isPresent()) {
113 logger.info("onSubnetAddedToVpn: subnet {} is an external subnet on external network {}, so ignoring this for SubnetRoute",
114 subnetId.getValue(), subMap.getNetworkId().getValue());
117 //Create and add SubnetOpDataEntry object for this subnet to the SubnetOpData container
118 InstanceIdentifier<SubnetOpDataEntry> subOpIdentifier = InstanceIdentifier.builder(SubnetOpData.class).
119 child(SubnetOpDataEntry.class, new SubnetOpDataEntryKey(subnetId)).build();
120 Optional<SubnetOpDataEntry> optionalSubs = VpnUtil.read(dataBroker,
121 LogicalDatastoreType.OPERATIONAL,
123 if (optionalSubs.isPresent()) {
124 logger.error("onSubnetAddedToVpn: SubnetOpDataEntry for subnet " + subnetId.getValue() +
125 " already detected to be present");
128 logger.debug("onSubnetAddedToVpn: Creating new SubnetOpDataEntry node for subnet: " + subnetId.getValue());
129 Map<BigInteger, SubnetToDpn> subDpnMap = new HashMap<BigInteger, SubnetToDpn>();
130 SubnetOpDataEntry subOpEntry = null;
131 BigInteger dpnId = null;
132 BigInteger nhDpnId = null;
133 SubnetToDpn subDpn = null;
135 SubnetOpDataEntryBuilder subOpBuilder = new SubnetOpDataEntryBuilder().setKey(new SubnetOpDataEntryKey(subnetId));
136 subOpBuilder.setSubnetId(subnetId);
137 subOpBuilder.setSubnetCidr(subnetIp);
138 String rd = VpnUtil.getVpnRdFromVpnInstanceConfig(dataBroker, vpnName);
140 logger.error("onSubnetAddedToVpn: The VPN Instance name " + notification.getVpnName() + " does not have RD ");
143 subOpBuilder.setVrfId(rd);
144 subOpBuilder.setVpnName(vpnName);
145 subOpBuilder.setSubnetToDpn(new ArrayList<SubnetToDpn>());
146 subOpBuilder.setRouteAdvState(TaskState.Na);
147 subOpBuilder.setElanTag(elanTag);
149 // First recover set of ports available in this subnet
150 List<Uuid> portList = subMap.getPortList();
151 if (portList != null) {
152 for (Uuid port: portList) {
153 Interface intfState = InterfaceUtils.getInterfaceStateFromOperDS(dataBroker,port.getValue());
154 if (intfState != null) {
156 dpnId = InterfaceUtils.getDpIdFromInterface(intfState);
157 } catch (Exception e) {
158 logger.error("onSubnetAddedToVpn: Unable to obtain dpnId for interface {},",
159 " subnetroute inclusion for this interface failed with exception {}",
163 if (dpnId.equals(BigInteger.ZERO)) {
164 logger.info("onSubnetAddedToVpn: Port " + port.getValue() + " is not assigned DPN yet, ignoring ");
167 subOpDpnManager.addPortOpDataEntry(port.getValue(), subnetId, dpnId);
168 if (intfState.getOperStatus() != OperStatus.Up) {
169 logger.info("onSubnetAddedToVpn: Port " + port.getValue() + " is not UP yet, ignoring ");
172 subDpn = subOpDpnManager.addInterfaceToDpn(subnetId, dpnId, port.getValue());
173 if (intfState.getOperStatus() == OperStatus.Up) {
175 subDpnMap.put(dpnId, subDpn);
176 if (nhDpnId == null) {
181 subOpDpnManager.addPortOpDataEntry(port.getValue(), subnetId, null);
184 if (subDpnMap.size() > 0) {
185 subOpBuilder.setSubnetToDpn(new ArrayList<SubnetToDpn>(subDpnMap.values()));
189 if (nhDpnId != null) {
190 logger.info("Next-Hop dpn {} is available for rd {} subnetIp {} vpn {}", nhDpnId, rd, subnetIp, vpnName);
191 subOpBuilder.setNhDpnId(nhDpnId);
194 Write the subnet route entry to the FIB.
195 And also advertise the subnet route entry via BGP.
197 int label = getLabel(rd, subnetIp);
199 logger.error("Unable to fetch label from Id Manager. Bailing out of handling addition of subnet {} to vpn {}", subnetIp, vpnName);
202 addSubnetRouteToFib(rd, subnetIp, nhDpnId, vpnName, elanTag, label);
203 advertiseSubnetRouteToBgp(rd, subnetIp, nhDpnId, vpnName, elanTag, label);
204 subOpBuilder.setRouteAdvState(TaskState.Done);
205 } catch (Exception ex) {
206 logger.error("onSubnetAddedToVpn: FIB rules and Advertising nhDpnId " + nhDpnId +
207 " information for subnet " + subnetId.getValue() + " to BGP failed {}", ex);
208 subOpBuilder.setRouteAdvState(TaskState.Pending);
211 logger.info("Next-Hop dpn is unavailable for rd {} subnetIp {} vpn {}", rd, subnetIp, vpnName);
214 subOpEntry = subOpBuilder.build();
215 MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.OPERATIONAL, subOpIdentifier, subOpEntry);
216 logger.info("onSubnetAddedToVpn: Added subnetopdataentry to OP Datastore for subnet {}",
217 subnetId.getValue());
218 } catch (Exception ex) {
219 logger.error("Creation of SubnetOpDataEntry for subnet " +
220 subnetId.getValue() + " failed {}", ex);
222 VpnUtil.unlockSubnet(lockManager, subnetId.getValue());
224 } catch (Exception e) {
225 logger.error("Unable to handle subnet {} added to vpn {} {}", subnetIp, vpnName, e);
230 public void onSubnetDeletedFromVpn(SubnetDeletedFromVpn notification) {
231 Uuid subnetId = notification.getSubnetId();
233 if (!notification.isExternalVpn()) {
236 logger.info("onSubnetDeletedFromVpn: Subnet " + subnetId.getValue() + " being removed from vpn");
237 //TODO(vivek): Change this to use more granularized lock at subnetId level
239 VpnUtil.lockSubnet(lockManager, subnetId.getValue());
241 InstanceIdentifier<SubnetOpDataEntry> subOpIdentifier = InstanceIdentifier.builder(SubnetOpData.class).
242 child(SubnetOpDataEntry.class, new SubnetOpDataEntryKey(subnetId)).build();
243 logger.trace(" Removing the SubnetOpDataEntry node for subnet: " + subnetId.getValue());
244 Optional<SubnetOpDataEntry> optionalSubs = VpnUtil.read(dataBroker,
245 LogicalDatastoreType.OPERATIONAL,
247 if (!optionalSubs.isPresent()) {
248 logger.error("onSubnetDeletedFromVpn: SubnetOpDataEntry for subnet " + subnetId.getValue() +
249 " not available in datastore");
253 /* If subnet is deleted (or if its removed from VPN), the ports that are DOWN on that subnet
254 * will continue to be stale in portOpData DS, as subDpnList used for portOpData removal will
255 * contain only ports that are UP. So here we explicitly cleanup the ports of the subnet by
256 * going through the list of ports on the subnet
258 InstanceIdentifier<Subnetmap> subMapid = InstanceIdentifier.builder(Subnetmaps.class).
259 child(Subnetmap.class, new SubnetmapKey(subnetId)).build();
260 Optional<Subnetmap> sm = VpnUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, subMapid);
261 if (!sm.isPresent()) {
262 logger.error("Stale ports removal: Unable to retrieve subnetmap entry for subnet : " + subnetId);
264 Subnetmap subMap = sm.get();
265 List<Uuid> portList = subMap.getPortList();
266 if (portList != null) {
267 for (Uuid port : portList) {
268 InstanceIdentifier<PortOpDataEntry> portOpIdentifier = InstanceIdentifier.builder(PortOpData.class).
269 child(PortOpDataEntry.class, new PortOpDataEntryKey(port.getValue())).build();
270 logger.trace("Deleting portOpData entry for port " + port.getValue());
271 MDSALUtil.syncDelete(dataBroker, LogicalDatastoreType.OPERATIONAL, portOpIdentifier);
276 SubnetOpDataEntryBuilder subOpBuilder = new SubnetOpDataEntryBuilder(optionalSubs.get());
277 String rd = subOpBuilder.getVrfId();
278 String subnetIp = subOpBuilder.getSubnetCidr();
279 String vpnName = subOpBuilder.getVpnName();
280 MDSALUtil.syncDelete(dataBroker, LogicalDatastoreType.OPERATIONAL, subOpIdentifier);
281 logger.info("onSubnetDeletedFromVpn: Removed subnetopdataentry for subnet {} successfully from Datastore", subnetId.getValue());
283 //Withdraw the routes for all the interfaces on this subnet
284 //Remove subnet route entry from FIB
285 deleteSubnetRouteFromFib(rd, subnetIp, vpnName);
286 withdrawSubnetRoutefromBgp(rd, subnetIp);
287 } catch (Exception ex) {
288 logger.error("onSubnetAddedToVpn: Withdrawing routes from BGP for subnet " +
289 subnetId.getValue() + " failed {}" + ex);
291 } catch (Exception ex) {
292 logger.error("Removal of SubnetOpDataEntry for subnet " +
293 subnetId.getValue() + " failed {}" + ex);
295 VpnUtil.unlockSubnet(lockManager, subnetId.getValue());
297 } catch (Exception e) {
298 logger.error("Unable to handle subnet {} removed to vpn {} {}", notification.getSubnetIp(), notification.getVpnName(), e);
303 public void onSubnetUpdatedInVpn(SubnetUpdatedInVpn notification) {
304 Uuid subnetId = notification.getSubnetId();
305 String vpnName = notification.getVpnName();
306 String subnetIp = notification.getSubnetIp();
307 Long elanTag = notification.getElanTag();
309 Preconditions.checkNotNull(subnetId, "SubnetId cannot be null or empty!");
310 Preconditions.checkNotNull(subnetIp, "SubnetPrefix cannot be null or empty!");
311 Preconditions.checkNotNull(vpnName, "VpnName cannot be null or empty!");
312 Preconditions.checkNotNull(elanTag, "ElanTag cannot be null or empty!");
314 InstanceIdentifier<SubnetOpDataEntry> subOpIdentifier = InstanceIdentifier.builder(SubnetOpData.class).
315 child(SubnetOpDataEntry.class, new SubnetOpDataEntryKey(subnetId)).build();
316 Optional<SubnetOpDataEntry> optionalSubs = VpnUtil.read(dataBroker,
317 LogicalDatastoreType.OPERATIONAL,
319 if (optionalSubs.isPresent()) {
320 if (!notification.isExternalVpn()) {
321 SubnetDeletedFromVpnBuilder bldr = new SubnetDeletedFromVpnBuilder().setVpnName(vpnName);
322 bldr.setElanTag(elanTag).setExternalVpn(true).setSubnetIp(subnetIp).setSubnetId(subnetId);
323 onSubnetDeletedFromVpn(bldr.build());
325 // TODO(vivek): Something got updated, but we donot know what ?
327 if (notification.isExternalVpn()) {
328 SubnetAddedToVpnBuilder bldr = new SubnetAddedToVpnBuilder().setVpnName(vpnName).setElanTag(elanTag);
329 bldr.setSubnetIp(subnetIp).setSubnetId(subnetId).setExternalVpn(true);;
330 onSubnetAddedToVpn(bldr.build());
332 // TODO(vivek): Something got updated, but we donot know what ?
337 public void onPortAddedToSubnet(PortAddedToSubnet notification) {
338 Uuid subnetId = notification.getSubnetId();
339 Uuid portId = notification.getPortId();
341 logger.info("onPortAddedToSubnet: Port " + portId.getValue() + " being added to subnet " + subnetId.getValue());
342 //TODO(vivek): Change this to use more granularized lock at subnetId level
344 VpnUtil.lockSubnet(lockManager, subnetId.getValue());
346 InstanceIdentifier<SubnetOpDataEntry> subOpIdentifier = InstanceIdentifier.builder(SubnetOpData.class).
347 child(SubnetOpDataEntry.class, new SubnetOpDataEntryKey(subnetId)).build();
349 Optional<SubnetOpDataEntry> optionalSubs = VpnUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL,
351 if (!optionalSubs.isPresent()) {
352 logger.info("onPortAddedToSubnet: Port " + portId.getValue() + " is part of a subnet " + subnetId.getValue() +
353 " that is not in VPN, ignoring");
356 Interface intfState = InterfaceUtils.getInterfaceStateFromOperDS(dataBroker,portId.getValue());
357 if (intfState == null) {
358 // Interface State not yet available
359 subOpDpnManager.addPortOpDataEntry(portId.getValue(), subnetId, null);
362 BigInteger dpnId = BigInteger.ZERO;
364 dpnId = InterfaceUtils.getDpIdFromInterface(intfState);
365 } catch (Exception e) {
366 logger.error("onSubnetAddedToVpn: Unable to obtain dpnId for interface {},",
367 " subnetroute inclusion for this interface failed with exception {}",
368 portId.getValue(), e);
371 if (dpnId.equals(BigInteger.ZERO)) {
372 logger.info("onPortAddedToSubnet: Port " + portId.getValue() + " is not assigned DPN yet, ignoring ");
375 subOpDpnManager.addPortOpDataEntry(portId.getValue(), subnetId, dpnId);
376 if (intfState.getOperStatus() != OperStatus.Up) {
377 logger.info("onPortAddedToSubnet: Port " + portId.getValue() + " is not UP yet, ignoring ");
380 logger.debug("onPortAddedToSubnet: Updating the SubnetOpDataEntry node for subnet: " + subnetId.getValue());
381 SubnetToDpn subDpn = subOpDpnManager.addInterfaceToDpn(subnetId, dpnId, portId.getValue());
382 if (subDpn == null) {
385 SubnetOpDataEntryBuilder subOpBuilder = new SubnetOpDataEntryBuilder(optionalSubs.get());
386 List<SubnetToDpn> subDpnList = subOpBuilder.getSubnetToDpn();
387 subDpnList.add(subDpn);
388 subOpBuilder.setSubnetToDpn(subDpnList);
389 if (subOpBuilder.getNhDpnId() == null) {
390 subOpBuilder.setNhDpnId(dpnId);
392 BigInteger nhDpnId = subOpBuilder.getNhDpnId();
393 String rd = subOpBuilder.getVrfId();
394 String subnetIp = subOpBuilder.getSubnetCidr();
395 String vpnName = subOpBuilder.getVpnName();
396 Long elanTag = subOpBuilder.getElanTag();
397 if ((subOpBuilder.getRouteAdvState() == TaskState.Pending) ||
398 (subOpBuilder.getRouteAdvState() == TaskState.Na)) {
400 // Write the Subnet Route Entry to FIB
401 // Advertise BGP Route here and set route_adv_state to DONE
402 int label = getLabel(rd, subnetIp);
404 logger.error("Unable to fetch label from Id Manager. Bailing out of handling addition of port {} to subnet {} in vpn {}", portId.getValue(), subnetIp, vpnName);
407 addSubnetRouteToFib(rd, subnetIp, nhDpnId, vpnName, elanTag, label);
408 advertiseSubnetRouteToBgp(rd, subnetIp, nhDpnId, vpnName, elanTag, label);
409 subOpBuilder.setRouteAdvState(TaskState.Done);
410 } catch (Exception ex) {
411 logger.error("onPortAddedToSubnet: Advertising NextHopDPN "+ nhDpnId +
412 " information for subnet " + subnetId.getValue() + " to BGP failed {}", ex);
415 SubnetOpDataEntry subOpEntry = subOpBuilder.build();
416 MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.OPERATIONAL, subOpIdentifier, subOpEntry);
417 logger.info("onPortAddedToSubnet: Updated subnetopdataentry to OP Datastore for port " + portId.getValue());
419 } catch (Exception ex) {
420 logger.error("Creation of SubnetOpDataEntry for subnet " +
421 subnetId.getValue() + " failed {}", ex);
423 VpnUtil.unlockSubnet(lockManager, subnetId.getValue());
425 } catch (Exception e) {
426 logger.error("Unable to handle port {} added to subnet {} {}", portId.getValue(), subnetId.getValue(), e);
431 public void onPortRemovedFromSubnet(PortRemovedFromSubnet notification) {
432 Uuid subnetId = notification.getSubnetId();
433 Uuid portId = notification.getPortId();
435 logger.info("onPortRemovedFromSubnet: Port " + portId.getValue() + " being removed from subnet " + subnetId.getValue());
436 //TODO(vivek): Change this to use more granularized lock at subnetId level
438 VpnUtil.lockSubnet(lockManager, subnetId.getValue());
440 PortOpDataEntry portOpEntry = subOpDpnManager.removePortOpDataEntry(portId.getValue());
441 if (portOpEntry == null) {
444 BigInteger dpnId = portOpEntry.getDpnId();
446 logger.debug("onPortRemovedFromSubnet: Port {} does not have a DPNId associated, ignoring", portId.getValue());
449 logger.debug("onPortRemovedFromSubnet: Updating the SubnetOpDataEntry node for subnet: " + subnetId.getValue());
450 boolean last = subOpDpnManager.removeInterfaceFromDpn(subnetId, dpnId, portId.getValue());
451 InstanceIdentifier<SubnetOpDataEntry> subOpIdentifier = InstanceIdentifier.builder(SubnetOpData.class).
452 child(SubnetOpDataEntry.class, new SubnetOpDataEntryKey(subnetId)).build();
453 Optional<SubnetOpDataEntry> optionalSubs = VpnUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL,
455 if (!optionalSubs.isPresent()) {
456 logger.info("onPortRemovedFromSubnet: Port " + portId.getValue() + " is part of a subnet " + subnetId.getValue() +
457 " that is not in VPN, ignoring");
460 SubnetOpDataEntry subOpEntry = null;
461 List<SubnetToDpn> subDpnList = null;
462 SubnetOpDataEntryBuilder subOpBuilder = new SubnetOpDataEntryBuilder(optionalSubs.get());
463 String rd = subOpBuilder.getVrfId();
464 String subnetIp = subOpBuilder.getSubnetCidr();
465 String vpnName = subOpBuilder.getVpnName();
466 Long elanTag = subOpBuilder.getElanTag();
467 BigInteger nhDpnId = subOpBuilder.getNhDpnId();
468 if ((nhDpnId != null) && (nhDpnId.equals(dpnId))) {
469 // select another NhDpnId
471 logger.debug("onPortRemovedFromSubnet: Last port " + portId + " on the subnet: " + subnetId.getValue());
472 // last port on this DPN, so we need to swap the NHDpnId
473 subDpnList = subOpBuilder.getSubnetToDpn();
474 if (subDpnList.isEmpty()) {
475 subOpBuilder.setNhDpnId(null);
477 // withdraw route from BGP
478 deleteSubnetRouteFromFib(rd, subnetIp, vpnName);
479 withdrawSubnetRoutefromBgp(rd, subnetIp);
480 subOpBuilder.setRouteAdvState(TaskState.Na);
481 } catch (Exception ex) {
482 logger.error("onPortRemovedFromSubnet: Withdrawing NextHopDPN " + dpnId + " information for subnet " +
483 subnetId.getValue() + " from BGP failed ", ex);
484 subOpBuilder.setRouteAdvState(TaskState.Pending);
487 nhDpnId = subDpnList.get(0).getDpnId();
488 subOpBuilder.setNhDpnId(nhDpnId);
489 logger.debug("onInterfaceDown: Swapping the Designated DPN to " + nhDpnId + " for subnet " + subnetId.getValue());
491 // Best effort Withdrawal of route from BGP for this subnet
492 // Advertise the new NexthopIP to BGP for this subnet
493 //withdrawSubnetRoutefromBgp(rd, subnetIp);
494 int label = getLabel(rd, subnetIp);
496 logger.error("Unable to fetch label from Id Manager. Bailing out of handling removal of port {} from subnet {} in vpn {}", portId.getValue(), subnetIp, vpnName);
499 addSubnetRouteToFib(rd, subnetIp, nhDpnId, vpnName, elanTag, label);
500 advertiseSubnetRouteToBgp(rd, subnetIp, nhDpnId, vpnName, elanTag, label);
501 subOpBuilder.setRouteAdvState(TaskState.Done);
502 } catch (Exception ex) {
503 logger.error("onPortRemovedFromSubnet: Swapping Withdrawing NextHopDPN " + dpnId +
504 " information for subnet " + subnetId.getValue() +
505 " to BGP failed {}" + ex);
506 subOpBuilder.setRouteAdvState(TaskState.Pending);
511 subOpEntry = subOpBuilder.build();
512 MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.OPERATIONAL, subOpIdentifier, subOpEntry);
513 logger.info("onPortRemovedFromSubnet: Updated subnetopdataentry to OP Datastore removing port " + portId.getValue());
514 } catch (Exception ex) {
515 logger.error("Creation of SubnetOpDataEntry for subnet " +
516 subnetId.getValue() + " failed {}" + ex);
518 VpnUtil.unlockSubnet(lockManager, subnetId.getValue());
520 } catch (Exception e) {
521 logger.error("Unable to handle port {} removed from subnet {} {}", portId.getValue(), subnetId.getValue(), e);
525 public void onInterfaceUp(BigInteger dpnId, String intfName) {
526 logger.info("onInterfaceUp: Port " + intfName);
527 //TODO(vivek): Change this to use more granularized lock at subnetId level
528 SubnetToDpn subDpn = null;
529 PortOpDataEntry portOpEntry = subOpDpnManager.getPortOpDataEntry(intfName);
530 if (portOpEntry == null) {
531 logger.info("onInterfaceUp: Port " + intfName + "is part of a subnet not in VPN, ignoring");
534 if ((dpnId == null) || (dpnId == BigInteger.ZERO)) {
535 dpnId = portOpEntry.getDpnId();
537 logger.error("onInterfaceUp: Unable to determine the DPNID for port " + intfName);
541 Uuid subnetId = portOpEntry.getSubnetId();
543 VpnUtil.lockSubnet(lockManager, subnetId.getValue());
545 InstanceIdentifier<SubnetOpDataEntry> subOpIdentifier = InstanceIdentifier.builder(SubnetOpData.class).
546 child(SubnetOpDataEntry.class, new SubnetOpDataEntryKey(subnetId)).build();
547 Optional<SubnetOpDataEntry> optionalSubs = VpnUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL,
549 if (!optionalSubs.isPresent()) {
550 logger.error("onInterfaceUp: SubnetOpDataEntry for subnet " + subnetId.getValue() +
551 " is not available");
555 SubnetOpDataEntryBuilder subOpBuilder = new SubnetOpDataEntryBuilder(optionalSubs.get());
556 logger.debug("onInterfaceUp: Updating the SubnetOpDataEntry node for subnet: " + subnetId.getValue());
557 subOpDpnManager.addPortOpDataEntry(intfName, subnetId, dpnId);
558 subDpn = subOpDpnManager.addInterfaceToDpn(subnetId, dpnId, intfName);
559 if (subDpn == null) {
562 List<SubnetToDpn> subDpnList = subOpBuilder.getSubnetToDpn();
563 subDpnList.add(subDpn);
564 subOpBuilder.setSubnetToDpn(subDpnList);
565 if (subOpBuilder.getNhDpnId() == null) {
566 subOpBuilder.setNhDpnId(dpnId);
568 BigInteger nhDpnId = subOpBuilder.getNhDpnId();
569 String rd = subOpBuilder.getVrfId();
570 String subnetIp = subOpBuilder.getSubnetCidr();
571 String vpnName = subOpBuilder.getVpnName();
572 Long elanTag = subOpBuilder.getElanTag();
573 if ((subOpBuilder.getRouteAdvState() == TaskState.Pending) || (subOpBuilder.getRouteAdvState() == TaskState.Na)) {
575 // Write the Subnet Route Entry to FIB
576 // Advertise BGP Route here and set route_adv_state to DONE
577 int label = getLabel(rd, subnetIp);
579 logger.error("Unable to fetch label from Id Manager. Bailing out of handling interface up event for port {} for subnet {} in vpn {}", intfName, subnetIp, vpnName);
582 addSubnetRouteToFib(rd, subnetIp, nhDpnId, vpnName, elanTag, label);
583 advertiseSubnetRouteToBgp(rd, subnetIp, nhDpnId, vpnName, elanTag, label);
584 subOpBuilder.setRouteAdvState(TaskState.Done);
585 } catch (Exception ex) {
586 logger.error("onInterfaceUp: Advertising NextHopDPN " + nhDpnId + " information for subnet " +
587 subnetId.getValue() + " to BGP failed {}" + ex);
590 SubnetOpDataEntry subOpEntry = subOpBuilder.build();
591 MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.OPERATIONAL, subOpIdentifier, subOpEntry);
592 logger.info("onInterfaceUp: Updated subnetopdataentry to OP Datastore port up " + intfName);
593 } catch (Exception ex) {
594 logger.error("Creation of SubnetOpDataEntry for subnet " +
595 subnetId.getValue() + " failed {}" + ex);
597 VpnUtil.unlockSubnet(lockManager, subnetId.getValue());
599 } catch (Exception e) {
600 logger.error("Unable to handle interface up event for port {} in subnet {} {}", portOpEntry.getPortId(), subnetId.getValue(), e);
604 public void onInterfaceDown(final BigInteger dpnId, final String interfaceName) {
605 logger.info("onInterfaceDown: Port " + interfaceName);
606 //TODO(vivek): Change this to use more granularized lock at subnetId level
607 PortOpDataEntry portOpEntry = subOpDpnManager.getPortOpDataEntry(interfaceName);
608 if (portOpEntry == null) {
609 logger.info("onInterfaceDown: Port " + interfaceName + "is part of a subnet not in VPN, ignoring");
612 if ((dpnId == null) ||(dpnId == BigInteger.ZERO)) {
613 logger.error("onInterfaceDown: Unable to determine the DPNID for port " + interfaceName);
616 Uuid subnetId = portOpEntry.getSubnetId();
618 VpnUtil.lockSubnet(lockManager, subnetId.getValue());
620 logger.debug("onInterfaceDown: Updating the SubnetOpDataEntry node for subnet: " + subnetId.getValue());
621 boolean last = subOpDpnManager.removeInterfaceFromDpn(subnetId, dpnId, interfaceName);
622 InstanceIdentifier<SubnetOpDataEntry> subOpIdentifier = InstanceIdentifier.builder(SubnetOpData.class).
623 child(SubnetOpDataEntry.class, new SubnetOpDataEntryKey(subnetId)).build();
624 Optional<SubnetOpDataEntry> optionalSubs = VpnUtil.read(dataBroker,
625 LogicalDatastoreType.OPERATIONAL,
627 if (!optionalSubs.isPresent()) {
628 logger.error("onInterfaceDown: SubnetOpDataEntry for subnet " + subnetId.getValue() +
629 " is not available");
632 SubnetOpDataEntry subOpEntry = null;
633 List<SubnetToDpn> subDpnList = null;
634 SubnetOpDataEntryBuilder subOpBuilder = new SubnetOpDataEntryBuilder(optionalSubs.get());
635 String rd = subOpBuilder.getVrfId();
636 String subnetIp = subOpBuilder.getSubnetCidr();
637 String vpnName = subOpBuilder.getVpnName();
638 Long elanTag = subOpBuilder.getElanTag();
639 BigInteger nhDpnId = subOpBuilder.getNhDpnId();
640 if ((nhDpnId != null) && (nhDpnId.equals(dpnId))) {
641 // select another NhDpnId
643 logger.debug("onInterfaceDown: Last active port " + interfaceName + " on the subnet: " + subnetId.getValue());
644 // last port on this DPN, so we need to swap the NHDpnId
645 subDpnList = subOpBuilder.getSubnetToDpn();
646 if (subDpnList.isEmpty()) {
647 subOpBuilder.setNhDpnId(null);
649 // Withdraw route from BGP for this subnet
650 deleteSubnetRouteFromFib(rd, subnetIp, vpnName);
651 withdrawSubnetRoutefromBgp(rd, subnetIp);
652 subOpBuilder.setRouteAdvState(TaskState.Na);
653 } catch (Exception ex) {
654 logger.error("onInterfaceDown: Withdrawing NextHopDPN " + dpnId + " information for subnet " +
655 subnetId.getValue() + " from BGP failed {}" + ex);
656 subOpBuilder.setRouteAdvState(TaskState.Pending);
659 nhDpnId = subDpnList.get(0).getDpnId();
660 subOpBuilder.setNhDpnId(nhDpnId);
661 logger.debug("onInterfaceDown: Swapping the Designated DPN to " + nhDpnId + " for subnet " + subnetId.getValue());
663 // Best effort Withdrawal of route from BGP for this subnet
664 //withdrawSubnetRoutefromBgp(rd, subnetIp);
665 int label = getLabel(rd, subnetIp);
667 logger.error("Unable to fetch label from Id Manager. Bailing out of handling interface down event for port {} in subnet {} for vpn {}", interfaceName, subnetIp, vpnName);
670 addSubnetRouteToFib(rd, subnetIp, nhDpnId, vpnName, elanTag, label);
671 advertiseSubnetRouteToBgp(rd, subnetIp, nhDpnId, vpnName, elanTag, label);
672 subOpBuilder.setRouteAdvState(TaskState.Done);
673 } catch (Exception ex) {
674 logger.error("onInterfaceDown: Swapping Withdrawing NextHopDPN " + dpnId + " information for subnet " +
675 subnetId.getValue() + " to BGP failed {}" + ex);
676 subOpBuilder.setRouteAdvState(TaskState.Pending);
681 subOpEntry = subOpBuilder.build();
682 MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.OPERATIONAL, subOpIdentifier, subOpEntry);
683 logger.info("onInterfaceDown: Updated subnetopdataentry to OP Datastore port down " + interfaceName);
684 } catch (Exception ex) {
685 logger.error("Creation of SubnetOpDataEntry for subnet " +
686 subnetId.getValue() + " failed {}" + ex);
688 VpnUtil.unlockSubnet(lockManager, subnetId.getValue());
690 } catch (Exception e) {
691 logger.error("Unable to handle interface down event for port {} in subnet {} {}", portOpEntry.getPortId(), subnetId.getValue(), e);
696 public void onRouterAssociatedToVpn(RouterAssociatedToVpn notification) {
700 public void onRouterDisassociatedFromVpn(RouterDisassociatedFromVpn notification) {
703 private void addSubnetRouteToFib(String rd, String subnetIp, BigInteger nhDpnId, String vpnName,
704 Long elanTag, int label) {
705 Preconditions.checkNotNull(rd, "RouteDistinguisher cannot be null or empty!");
706 Preconditions.checkNotNull(subnetIp, "SubnetRouteIp cannot be null or empty!");
707 Preconditions.checkNotNull(vpnName, "vpnName cannot be null or empty!");
708 Preconditions.checkNotNull(elanTag, "elanTag cannot be null or empty!");
709 String nexthopIp = InterfaceUtils.getEndpointIpAddressForDPN(dataBroker, nhDpnId);
710 if(nexthopIp != null)
711 vpnInterfaceManager.addSubnetRouteFibEntryToDS(rd, vpnName, subnetIp, nexthopIp, label, elanTag, nhDpnId , null);
713 logger.info("Unable to get nextHop ip address for nextHop DPN {}. Abort adding subnet route to FIB table.", nhDpnId);
716 private int getLabel(String rd, String subnetIp) {
717 int label = VpnUtil.getUniqueId(idManager, VpnConstants.VPN_IDPOOL_NAME,
718 VpnUtil.getNextHopLabelKey(rd, subnetIp));
719 logger.trace("Allocated subnetroute label {} for rd {} prefix {}", label, rd, subnetIp);
723 private void deleteSubnetRouteFromFib(String rd, String subnetIp, String vpnName) {
724 Preconditions.checkNotNull(rd, "RouteDistinguisher cannot be null or empty!");
725 Preconditions.checkNotNull(subnetIp, "SubnetRouteIp cannot be null or empty!");
726 vpnInterfaceManager.deleteSubnetRouteFibEntryFromDS(rd, subnetIp, vpnName);
729 private void advertiseSubnetRouteToBgp(String rd, String subnetIp, BigInteger nhDpnId, String vpnName,
730 Long elanTag, int label) throws Exception {
731 Preconditions.checkNotNull(rd, "RouteDistinguisher cannot be null or empty!");
732 Preconditions.checkNotNull(subnetIp, "SubnetRouteIp cannot be null or empty!");
733 Preconditions.checkNotNull(elanTag, "elanTag cannot be null or empty!");
734 Preconditions.checkNotNull(nhDpnId, "nhDpnId cannot be null or empty!");
735 Preconditions.checkNotNull(vpnName, "vpnName cannot be null or empty!");
736 String nexthopIp = null;
737 nexthopIp = InterfaceUtils.getEndpointIpAddressForDPN(dataBroker, nhDpnId);
738 if (nexthopIp == null) {
739 logger.error("createSubnetRouteInVpn: Unable to obtain endpointIp address for DPNId " + nhDpnId);
740 throw new Exception("Unable to obtain endpointIp address for DPNId " + nhDpnId);
743 // BGPManager (inside ODL) requires a withdraw followed by advertise
744 // due to bugs with ClusterDataChangeListener used by BGPManager.
745 //bgpManager.withdrawPrefix(rd, subnetIp);
746 bgpManager.advertisePrefix(rd, subnetIp, Arrays.asList(nexthopIp), label);
747 } catch (Exception e) {
748 logger.error("Subnet route not advertised for rd " + rd + " failed ", e);
753 private void withdrawSubnetRoutefromBgp(String rd, String subnetIp) throws Exception {
754 Preconditions.checkNotNull(rd, "RouteDistinguisher cannot be null or empty!");
755 Preconditions.checkNotNull(subnetIp, "SubnetIp cannot be null or empty!");
757 bgpManager.withdrawPrefix(rd, subnetIp);
758 } catch (Exception e) {
759 logger.error("Subnet route not advertised for rd " + rd + " failed ", e);