/* * Copyright (c) 2016 Ericsson India Global Services Pvt Ltd. and others. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html */ package org.opendaylight.vpnservice; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.HashMap; import com.google.common.base.Preconditions; import org.opendaylight.bgpmanager.api.IBgpManager; import org.opendaylight.controller.md.sal.binding.api.DataBroker; import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; import org.opendaylight.vpnservice.mdsalutil.MDSALUtil; import org.opendaylight.vpnservice.utilities.InterfaceUtils; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface.OperStatus; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid; import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.*; import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.port.op.data.PortOpDataEntry; import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.port.op.data.PortOpDataEntryKey; import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.rd.to.elan.op.RdToElanOpEntry; import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.rd.to.elan.op.RdToElanOpEntryBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.rd.to.elan.op.RdToElanOpEntryKey; import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.subnet.op.data.SubnetOpDataEntry; import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.subnet.op.data.SubnetOpDataEntryKey; import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.subnet.op.data.SubnetOpDataEntryBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.IdManagerService; import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.neutronvpn.rev150602.*; import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.neutronvpn.rev150602.subnetmaps.Subnetmap; import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.neutronvpn.rev150602.subnetmaps.SubnetmapKey; import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.subnet.op.data.subnet.op.data.entry.SubnetToDpn; import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.subnet.op.data.subnet.op.data.entry.subnet.to.dpn.VpnInterfaces; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; import java.math.BigInteger; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.base.Optional; public class VpnSubnetRouteHandler implements NeutronvpnListener { private static final Logger logger = LoggerFactory.getLogger(VpnSubnetRouteHandler.class); private final DataBroker broker; private SubnetOpDpnManager subOpDpnManager; private final IBgpManager bgpManager; private IdManagerService idManager; private VpnInterfaceManager vpnInterfaceManager; public VpnSubnetRouteHandler(final DataBroker db, IBgpManager bgpManager, VpnInterfaceManager vpnIntfManager) { broker = db; subOpDpnManager = new SubnetOpDpnManager(broker); this.bgpManager = bgpManager; this.vpnInterfaceManager = vpnIntfManager; } public void setIdManager(IdManagerService idManager) { this.idManager = idManager; } @Override public void onSubnetAddedToVpn(SubnetAddedToVpn notification) { if (!notification.isExternalVpn()) { return; } Uuid subnetId = notification.getSubnetId(); String vpnName = notification.getVpnName(); String subnetIp = notification.getSubnetIp(); Long elanTag = notification.getElanTag(); Preconditions.checkNotNull(subnetId, "SubnetId cannot be null or empty!"); Preconditions.checkNotNull(subnetIp, "SubnetPrefix cannot be null or empty!"); Preconditions.checkNotNull(vpnName, "VpnName cannot be null or empty!"); Preconditions.checkNotNull(elanTag, "ElanTag cannot be null or empty!"); logger.info("onSubnetAddedToVpn: Subnet" + subnetId.getValue() + " being added to vpn"); //TODO(vivek): Change this to use more granularized lock at subnetId level synchronized (this) { try { //Create and add SubnetOpDataEntry object for this subnet to the SubnetOpData container InstanceIdentifier subOpIdentifier = InstanceIdentifier.builder(SubnetOpData.class). child(SubnetOpDataEntry.class, new SubnetOpDataEntryKey(subnetId)).build(); Optional optionalSubs = VpnUtil.read(broker, LogicalDatastoreType.OPERATIONAL, subOpIdentifier); if (optionalSubs.isPresent()) { logger.error("onSubnetAddedToVpn: SubnetOpDataEntry for subnet " + subnetId.getValue() + " already detected to be present"); return; } logger.debug("onSubnetAddedToVpn: Creating new SubnetOpDataEntry node for subnet: " + subnetId.getValue()); Map subDpnMap = new HashMap(); SubnetOpDataEntry subOpEntry = null; BigInteger dpnId = null; BigInteger nhDpnId = null; SubnetToDpn subDpn = null; SubnetOpDataEntryBuilder subOpBuilder = new SubnetOpDataEntryBuilder().setKey(new SubnetOpDataEntryKey(subnetId)); subOpBuilder.setSubnetId(subnetId); subOpBuilder.setSubnetCidr(subnetIp); String rd = VpnUtil.getVpnRdFromVpnInstanceConfig(broker, vpnName); if (rd == null) { logger.error("onSubnetAddedToVpn: The VPN Instance name " + notification.getVpnName() + " does not have RD "); return; } subOpBuilder.setVrfId(rd); subOpBuilder.setVpnName(vpnName); subOpBuilder.setSubnetToDpn(new ArrayList()); subOpBuilder.setRouteAdvState(TaskState.Na); subOpBuilder.setElanTag(elanTag); // First recover set of ports available in this subnet InstanceIdentifier subMapid = InstanceIdentifier.builder(Subnetmaps.class). child(Subnetmap.class, new SubnetmapKey(subnetId)).build(); Optional sm = VpnUtil.read(broker, LogicalDatastoreType.CONFIGURATION, subMapid); if (!sm.isPresent()) { logger.error("onSubnetAddedToVpn: Unable to retrieve subnetmap entry for subnet : " + subnetId); return; } Subnetmap subMap = sm.get(); List portList = subMap.getPortList(); if (portList != null) { for (Uuid port: portList) { Interface intfState = InterfaceUtils.getInterfaceStateFromOperDS(broker,port.getValue()); if (intfState != null) { dpnId = InterfaceUtils.getDpIdFromInterface(intfState); if (dpnId == null) { logger.info("onSubnetAddedToVpn: Port " + port.getValue() + " is not assigned DPN yet, ignoring "); continue; } subOpDpnManager.addPortOpDataEntry(port.getValue(), subnetId, dpnId); if (intfState.getOperStatus() != OperStatus.Up) { logger.info("onSubnetAddedToVpn: Port " + port.getValue() + " is not UP yet, ignoring "); continue; } subDpn = subOpDpnManager.addInterfaceToDpn(subnetId, dpnId, port.getValue()); if (intfState.getOperStatus() == OperStatus.Up) { // port is UP subDpnMap.put(dpnId, subDpn); if (nhDpnId == null) { nhDpnId = dpnId; } } } else { subOpDpnManager.addPortOpDataEntry(port.getValue(), subnetId, null); } } if (subDpnMap.size() > 0) { subOpBuilder.setSubnetToDpn(new ArrayList(subDpnMap.values())); } } if (nhDpnId != null) { subOpBuilder.setNhDpnId(nhDpnId); try { /* Write the subnet route entry to the FIB. And also advertise the subnet route entry via BGP. */ addSubnetRouteToFib(rd, subnetIp, nhDpnId, vpnName, elanTag); advertiseSubnetRouteToBgp(rd, subnetIp, nhDpnId, vpnName, elanTag); subOpBuilder.setRouteAdvState(TaskState.Done); } catch (Exception ex) { logger.error("onSubnetAddedToVpn: FIB rules and Advertising nhDpnId " + nhDpnId + " information for subnet " + subnetId.getValue() + " to BGP failed {}", ex); subOpBuilder.setRouteAdvState(TaskState.Pending); } } else { try { /* Write the subnet route entry to the FIB. NOTE: Will not advertise to BGP as NextHopDPN is not available yet. */ addSubnetRouteToFib(rd, subnetIp, null, vpnName, elanTag); } catch (Exception ex) { logger.error("onSubnetAddedToVpn: FIB rules writing for subnet {} with exception {} " + subnetId.getValue(), ex); subOpBuilder.setRouteAdvState(TaskState.Pending); } } subOpEntry = subOpBuilder.build(); MDSALUtil.syncWrite(broker, LogicalDatastoreType.OPERATIONAL, subOpIdentifier, subOpEntry); logger.info("onSubnetAddedToVpn: Added subnetopdataentry to OP Datastore for subnet " + subnetId.getValue()); } catch (Exception ex) { logger.error("Creation of SubnetOpDataEntry for subnet " + subnetId.getValue() + " failed {}", ex); } finally { } } } @Override public void onSubnetDeletedFromVpn(SubnetDeletedFromVpn notification) { Uuid subnetId = notification.getSubnetId(); if (!notification.isExternalVpn()) { return; } logger.info("onSubnetDeletedFromVpn: Subnet" + subnetId.getValue() + " being removed to vpn"); //TODO(vivek): Change this to use more granularized lock at subnetId level synchronized (this) { try { InstanceIdentifier subOpIdentifier = InstanceIdentifier.builder(SubnetOpData.class). child(SubnetOpDataEntry.class, new SubnetOpDataEntryKey(subnetId)).build(); logger.trace(" Removing the SubnetOpDataEntry node for subnet: " + subnetId.getValue()); Optional optionalSubs = VpnUtil.read(broker, LogicalDatastoreType.OPERATIONAL, subOpIdentifier); if (!optionalSubs.isPresent()) { logger.error("onSubnetDeletedFromVpn: SubnetOpDataEntry for subnet " + subnetId.getValue() + " not available in datastore"); return; } SubnetOpDataEntryBuilder subOpBuilder = new SubnetOpDataEntryBuilder(optionalSubs.get()); List subDpnList = subOpBuilder.getSubnetToDpn(); for (SubnetToDpn subDpn: subDpnList) { List vpnIntfList = subDpn.getVpnInterfaces(); for (VpnInterfaces vpnIntf: vpnIntfList) { subOpDpnManager.removePortOpDataEntry(vpnIntf.getInterfaceName()); } } //Removing Stale Ports in portOpData InstanceIdentifier subMapid = InstanceIdentifier.builder(Subnetmaps.class). child(Subnetmap.class, new SubnetmapKey(subnetId)).build(); Optional sm = VpnUtil.read(broker, LogicalDatastoreType.CONFIGURATION, subMapid); if (!sm.isPresent()) { logger.error("Stale ports removal: Unable to retrieve subnetmap entry for subnet : " + subnetId); } Subnetmap subMap = sm.get(); List portList = subMap.getPortList(); if(portList!=null){ InstanceIdentifier portOpIdentifier = InstanceIdentifier.builder(PortOpData.class).build(); Optional optionalPortOp = VpnUtil.read(broker, LogicalDatastoreType.OPERATIONAL, portOpIdentifier); if(!optionalPortOp.isPresent()){ logger.error("Stale ports removal: Cannot delete port. Not available in data store"); return; } else{ PortOpData portOpData = optionalPortOp.get(); List portOpDataList = portOpData.getPortOpDataEntry(); if(portOpDataList!=null){ for(PortOpDataEntry portOpDataListEntry : portOpDataList){ if(portList.contains(new Uuid(portOpDataListEntry.getPortId()))){ logger.trace("Removing stale port: " + portOpDataListEntry + "for dissociated subnetId: " + subnetId); MDSALUtil.syncDelete(broker, LogicalDatastoreType.OPERATIONAL, portOpIdentifier. child(PortOpDataEntry.class, new PortOpDataEntryKey(portOpDataListEntry.getKey()))); } } } } } String rd = subOpBuilder.getVrfId(); String subnetIp = subOpBuilder.getSubnetCidr(); BigInteger nhDpnId = subOpBuilder.getNhDpnId(); MDSALUtil.syncDelete(broker, LogicalDatastoreType.OPERATIONAL, subOpIdentifier); logger.trace("Removed subnetopdataentry successfully to CONFIG Datastore"); try { //Withdraw the routes for all the interfaces on this subnet //Remove subnet route entry from FIB withdrawSubnetRoutefromBgp(rd, subnetIp); deleteSubnetRouteFromFib(rd, subnetIp); } catch (Exception ex) { logger.error("onSubnetAddedToVpn: Withdrawing routes from BGP for subnet " + subnetId.getValue() + " failed {}" + ex); } } catch (Exception ex) { logger.error("Removal of SubnetOpDataEntry for subnet " + subnetId.getValue() + " failed {}" + ex); } finally { } } } @Override public void onSubnetUpdatedInVpn(SubnetUpdatedInVpn notification) { Uuid subnetId = notification.getSubnetId(); String vpnName = notification.getVpnName(); String subnetIp = notification.getSubnetIp(); Long elanTag = notification.getElanTag(); Preconditions.checkNotNull(subnetId, "SubnetId cannot be null or empty!"); Preconditions.checkNotNull(subnetIp, "SubnetPrefix cannot be null or empty!"); Preconditions.checkNotNull(vpnName, "VpnName cannot be null or empty!"); Preconditions.checkNotNull(elanTag, "ElanTag cannot be null or empty!"); InstanceIdentifier subOpIdentifier = InstanceIdentifier.builder(SubnetOpData.class). child(SubnetOpDataEntry.class, new SubnetOpDataEntryKey(subnetId)).build(); Optional optionalSubs = VpnUtil.read(broker, LogicalDatastoreType.OPERATIONAL, subOpIdentifier); if (optionalSubs.isPresent()) { if (!notification.isExternalVpn()) { SubnetDeletedFromVpnBuilder bldr = new SubnetDeletedFromVpnBuilder().setVpnName(vpnName); bldr.setElanTag(elanTag).setExternalVpn(true).setSubnetIp(subnetIp).setSubnetId(subnetId); onSubnetDeletedFromVpn(bldr.build()); } // TODO(vivek): Something got updated, but we donot know what ? } else { if (notification.isExternalVpn()) { SubnetAddedToVpnBuilder bldr = new SubnetAddedToVpnBuilder().setVpnName(vpnName).setElanTag(elanTag); bldr.setSubnetIp(subnetIp).setSubnetId(subnetId).setExternalVpn(true);; onSubnetAddedToVpn(bldr.build()); } // TODO(vivek): Something got updated, but we donot know what ? } } @Override public void onPortAddedToSubnet(PortAddedToSubnet notification) { Uuid subnetId = notification.getSubnetId(); Uuid portId = notification.getPortId(); logger.info("onPortAddedToSubnet: Port " + portId.getValue() + " being added to subnet " + subnetId.getValue()); //TODO(vivek): Change this to use more granularized lock at subnetId level synchronized (this) { try { InstanceIdentifier subOpIdentifier = InstanceIdentifier.builder(SubnetOpData.class). child(SubnetOpDataEntry.class, new SubnetOpDataEntryKey(subnetId)).build(); Optional optionalSubs = VpnUtil.read(broker, LogicalDatastoreType.OPERATIONAL, subOpIdentifier); if (!optionalSubs.isPresent()) { logger.info("onPortAddedToSubnet: Port " + portId.getValue() + " is part of a subnet " + subnetId.getValue() + " that is not in VPN, ignoring"); return; } Interface intfState = InterfaceUtils.getInterfaceStateFromOperDS(broker,portId.getValue()); if (intfState == null) { // Interface State not yet available subOpDpnManager.addPortOpDataEntry(portId.getValue(), subnetId, null); return; } BigInteger dpnId = InterfaceUtils.getDpIdFromInterface(intfState); if (dpnId == null) { logger.info("onPortAddedToSubnet: Port " + portId.getValue() + " is not assigned DPN yet, ignoring "); return; } subOpDpnManager.addPortOpDataEntry(portId.getValue(), subnetId, dpnId); if (intfState.getOperStatus() != OperStatus.Up) { logger.info("onPortAddedToSubnet: Port " + portId.getValue() + " is not UP yet, ignoring "); return; } logger.debug("onPortAddedToSubnet: Updating the SubnetOpDataEntry node for subnet: " + subnetId.getValue()); SubnetToDpn subDpn = subOpDpnManager.addInterfaceToDpn(subnetId, dpnId, portId.getValue()); if (subDpn == null) { return; } SubnetOpDataEntryBuilder subOpBuilder = new SubnetOpDataEntryBuilder(optionalSubs.get()); List subDpnList = subOpBuilder.getSubnetToDpn(); subDpnList.add(subDpn); subOpBuilder.setSubnetToDpn(subDpnList); if (subOpBuilder.getNhDpnId() == null) { subOpBuilder.setNhDpnId(dpnId); } BigInteger nhDpnId = subOpBuilder.getNhDpnId(); String rd = subOpBuilder.getVrfId(); String subnetIp = subOpBuilder.getSubnetCidr(); String vpnName = subOpBuilder.getVpnName(); Long elanTag = subOpBuilder.getElanTag(); if ((subOpBuilder.getRouteAdvState() == TaskState.Pending) || (subOpBuilder.getRouteAdvState() == TaskState.Na)) { try { // Write the Subnet Route Entry to FIB // Advertise BGP Route here and set route_adv_state to DONE addSubnetRouteToFib(rd, subnetIp, nhDpnId, vpnName, elanTag); advertiseSubnetRouteToBgp(rd, subnetIp, nhDpnId, vpnName, elanTag); subOpBuilder.setRouteAdvState(TaskState.Done); } catch (Exception ex) { logger.error("onPortAddedToSubnet: Advertising NextHopDPN "+ nhDpnId + " information for subnet " + subnetId.getValue() + " to BGP failed {}", ex); } } SubnetOpDataEntry subOpEntry = subOpBuilder.build(); MDSALUtil.syncWrite(broker, LogicalDatastoreType.OPERATIONAL, subOpIdentifier, subOpEntry); logger.info("onPortAddedToSubnet: Updated subnetopdataentry to OP Datastore for port " + portId.getValue()); } catch (Exception ex) { logger.error("Creation of SubnetOpDataEntry for subnet " + subnetId.getValue() + " failed {}", ex); } finally { } } } @Override public void onPortRemovedFromSubnet(PortRemovedFromSubnet notification) { Uuid subnetId = notification.getSubnetId(); Uuid portId = notification.getPortId(); logger.info("onPortRemovedFromSubnet: Port " + portId.getValue() + " being removed from subnet " + subnetId.getValue()); //TODO(vivek): Change this to use more granularized lock at subnetId level synchronized (this) { try { PortOpDataEntry portOpEntry = subOpDpnManager.removePortOpDataEntry(portId.getValue()); if (portOpEntry == null) { return; } BigInteger dpnId = portOpEntry.getDpnId(); if (dpnId == null) { logger.debug("onPortRemovedFromSubnet: Port {} does not have a DPNId associated, ignoring", portId.getValue()); return; } logger.debug("onPortRemovedFromSubnet: Updating the SubnetOpDataEntry node for subnet: " + subnetId.getValue()); boolean last = subOpDpnManager.removeInterfaceFromDpn(subnetId, dpnId, portId.getValue()); InstanceIdentifier subOpIdentifier = InstanceIdentifier.builder(SubnetOpData.class). child(SubnetOpDataEntry.class, new SubnetOpDataEntryKey(subnetId)).build(); Optional optionalSubs = VpnUtil.read(broker, LogicalDatastoreType.OPERATIONAL, subOpIdentifier); if (!optionalSubs.isPresent()) { logger.info("onPortRemovedFromSubnet: Port " + portId.getValue() + " is part of a subnet " + subnetId.getValue() + " that is not in VPN, ignoring"); return; } SubnetOpDataEntry subOpEntry = null; List subDpnList = null; SubnetOpDataEntryBuilder subOpBuilder = new SubnetOpDataEntryBuilder(optionalSubs.get()); String rd = subOpBuilder.getVrfId(); String subnetIp = subOpBuilder.getSubnetCidr(); String vpnName = subOpBuilder.getVpnName(); Long elanTag = subOpBuilder.getElanTag(); BigInteger nhDpnId = subOpBuilder.getNhDpnId(); if ((nhDpnId != null) && (nhDpnId.equals(dpnId))) { // select another NhDpnId if (last) { logger.debug("onPortRemovedFromSubnet: Last port " + portId + " on the subnet: " + subnetId.getValue()); // last port on this DPN, so we need to swap the NHDpnId subDpnList = subOpBuilder.getSubnetToDpn(); if (subDpnList.isEmpty()) { subOpBuilder.setNhDpnId(null); try { // withdraw route from BGP deleteSubnetRouteFromFib(rd, subnetIp); withdrawSubnetRoutefromBgp(rd, subnetIp); subOpBuilder.setRouteAdvState(TaskState.Na); } catch (Exception ex) { logger.error("onPortRemovedFromSubnet: Withdrawing NextHopDPN " + dpnId + " information for subnet " + subnetId.getValue() + " from BGP failed ", ex); subOpBuilder.setRouteAdvState(TaskState.Pending); } } else { nhDpnId = subDpnList.get(0).getDpnId(); subOpBuilder.setNhDpnId(nhDpnId); logger.debug("onInterfaceDown: Swapping the Designated DPN to " + nhDpnId + " for subnet " + subnetId.getValue()); try { // Best effort Withdrawal of route from BGP for this subnet // Advertise the new NexthopIP to BGP for this subnet withdrawSubnetRoutefromBgp(rd, subnetIp); addSubnetRouteToFib(rd, subnetIp, nhDpnId, vpnName, elanTag); advertiseSubnetRouteToBgp(rd, subnetIp, nhDpnId, vpnName, elanTag); subOpBuilder.setRouteAdvState(TaskState.Done); } catch (Exception ex) { logger.error("onPortRemovedFromSubnet: Swapping Withdrawing NextHopDPN " + dpnId + " information for subnet " + subnetId.getValue() + " to BGP failed {}" + ex); subOpBuilder.setRouteAdvState(TaskState.Pending); } } } } subOpEntry = subOpBuilder.build(); MDSALUtil.syncWrite(broker, LogicalDatastoreType.OPERATIONAL, subOpIdentifier, subOpEntry); logger.info("onPortRemovedFromSubnet: Updated subnetopdataentry to OP Datastore removing port " + portId.getValue()); } catch (Exception ex) { logger.error("Creation of SubnetOpDataEntry for subnet " + subnetId.getValue() + " failed {}" + ex); } finally { } } } public void onInterfaceUp(Interface intfState) { logger.info("onInterfaceUp: Port " + intfState.getName()); //TODO(vivek): Change this to use more granularized lock at subnetId level synchronized (this) { SubnetToDpn subDpn = null; String intfName = intfState.getName(); PortOpDataEntry portOpEntry = subOpDpnManager.getPortOpDataEntry(intfName); if (portOpEntry == null) { logger.info("onInterfaceUp: Port " + intfState.getName() + "is part of a subnet not in VPN, ignoring"); return; } BigInteger dpnId = portOpEntry.getDpnId(); if (dpnId == null) { dpnId = InterfaceUtils.getDpIdFromInterface(intfState); if (dpnId == null) { logger.error("onInterfaceUp: Unable to determine the DPNID for port " + intfName); return; } } Uuid subnetId = portOpEntry.getSubnetId(); try { InstanceIdentifier subOpIdentifier = InstanceIdentifier.builder(SubnetOpData.class). child(SubnetOpDataEntry.class, new SubnetOpDataEntryKey(subnetId)).build(); Optional optionalSubs = VpnUtil.read(broker, LogicalDatastoreType.OPERATIONAL, subOpIdentifier); if (!optionalSubs.isPresent()) { logger.error("onInterfaceUp: SubnetOpDataEntry for subnet " + subnetId.getValue() + " is not available"); return; } SubnetOpDataEntryBuilder subOpBuilder = new SubnetOpDataEntryBuilder(optionalSubs.get()); logger.debug("onInterfaceUp: Updating the SubnetOpDataEntry node for subnet: " + subnetId.getValue()); subOpDpnManager.addPortOpDataEntry(intfName, subnetId, dpnId); subDpn = subOpDpnManager.addInterfaceToDpn(subnetId, dpnId, intfName); if (subDpn == null) { return; } List subDpnList = subOpBuilder.getSubnetToDpn(); subDpnList.add(subDpn); subOpBuilder.setSubnetToDpn(subDpnList); if (subOpBuilder.getNhDpnId() == null) { subOpBuilder.setNhDpnId(dpnId); } BigInteger nhDpnId = subOpBuilder.getNhDpnId(); String rd = subOpBuilder.getVrfId(); String subnetIp = subOpBuilder.getSubnetCidr(); String vpnName = subOpBuilder.getVpnName(); Long elanTag = subOpBuilder.getElanTag(); if ((subOpBuilder.getRouteAdvState() == TaskState.Pending) || (subOpBuilder.getRouteAdvState() == TaskState.Na)) { try { // Write the Subnet Route Entry to FIB // Advertise BGP Route here and set route_adv_state to DONE addSubnetRouteToFib(rd, subnetIp, nhDpnId, vpnName, elanTag); advertiseSubnetRouteToBgp(rd, subnetIp, nhDpnId, vpnName, elanTag); subOpBuilder.setRouteAdvState(TaskState.Done); } catch (Exception ex) { logger.error("onInterfaceUp: Advertising NextHopDPN " + nhDpnId + " information for subnet " + subnetId.getValue() + " to BGP failed {}" + ex); } } SubnetOpDataEntry subOpEntry = subOpBuilder.build(); MDSALUtil.syncWrite(broker, LogicalDatastoreType.OPERATIONAL, subOpIdentifier, subOpEntry); logger.info("onInterfaceUp: Updated subnetopdataentry to OP Datastore port up " + intfName); } catch (Exception ex) { logger.error("Creation of SubnetOpDataEntry for subnet " + subnetId.getValue() + " failed {}" + ex); } finally { } } } public void onInterfaceDown(Interface intfState) { logger.info("onInterfaceDown: Port " + intfState.getName()); //TODO(vivek): Change this to use more granularized lock at subnetId level synchronized (this) { String intfName = intfState.getName(); PortOpDataEntry portOpEntry = subOpDpnManager.getPortOpDataEntry(intfName); if (portOpEntry == null) { logger.info("onInterfaceDown: Port " + intfState.getName() + "is part of a subnet not in VPN, ignoring"); return; } BigInteger dpnId = portOpEntry.getDpnId(); if (dpnId == null) { dpnId = InterfaceUtils.getDpIdFromInterface(intfState); if (dpnId == null) { logger.error("onInterfaceDown: Unable to determine the DPNID for port " + intfName); return; } } Uuid subnetId = portOpEntry.getSubnetId(); try { logger.debug("onInterfaceDown: Updating the SubnetOpDataEntry node for subnet: " + subnetId.getValue()); boolean last = subOpDpnManager.removeInterfaceFromDpn(subnetId, dpnId, intfName); InstanceIdentifier subOpIdentifier = InstanceIdentifier.builder(SubnetOpData.class). child(SubnetOpDataEntry.class, new SubnetOpDataEntryKey(subnetId)).build(); Optional optionalSubs = VpnUtil.read(broker, LogicalDatastoreType.OPERATIONAL, subOpIdentifier); if (!optionalSubs.isPresent()) { logger.error("onInterfaceDown: SubnetOpDataEntry for subnet " + subnetId.getValue() + " is not available"); return; } SubnetOpDataEntry subOpEntry = null; List subDpnList = null; SubnetOpDataEntryBuilder subOpBuilder = new SubnetOpDataEntryBuilder(optionalSubs.get()); String rd = subOpBuilder.getVrfId(); String subnetIp = subOpBuilder.getSubnetCidr(); String vpnName = subOpBuilder.getVpnName(); Long elanTag = subOpBuilder.getElanTag(); BigInteger nhDpnId = subOpBuilder.getNhDpnId(); if ((nhDpnId != null) && (nhDpnId.equals(dpnId))) { // select another NhDpnId if (last) { logger.debug("onInterfaceDown: Last active port " + intfState.getName() + " on the subnet: " + subnetId.getValue()); // last port on this DPN, so we need to swap the NHDpnId subDpnList = subOpBuilder.getSubnetToDpn(); if (subDpnList.isEmpty()) { subOpBuilder.setNhDpnId(null); try { // Withdraw route from BGP for this subnet deleteSubnetRouteFromFib(rd, subnetIp); withdrawSubnetRoutefromBgp(rd, subnetIp); subOpBuilder.setRouteAdvState(TaskState.Na); } catch (Exception ex) { logger.error("onInterfaceDown: Withdrawing NextHopDPN " + dpnId + " information for subnet " + subnetId.getValue() + " from BGP failed {}" + ex); subOpBuilder.setRouteAdvState(TaskState.Pending); } } else { nhDpnId = subDpnList.get(0).getDpnId(); subOpBuilder.setNhDpnId(nhDpnId); logger.debug("onInterfaceDown: Swapping the Designated DPN to " + nhDpnId + " for subnet " + subnetId.getValue()); try { // Best effort Withdrawal of route from BGP for this subnet withdrawSubnetRoutefromBgp(rd, subnetIp); addSubnetRouteToFib(rd, subnetIp, nhDpnId, vpnName, elanTag); advertiseSubnetRouteToBgp(rd, subnetIp, nhDpnId, vpnName, elanTag); subOpBuilder.setRouteAdvState(TaskState.Done); } catch (Exception ex) { logger.error("onInterfaceDown: Swapping Withdrawing NextHopDPN " + dpnId + " information for subnet " + subnetId.getValue() + " to BGP failed {}" + ex); subOpBuilder.setRouteAdvState(TaskState.Pending); } } } } subOpEntry = subOpBuilder.build(); MDSALUtil.syncWrite(broker, LogicalDatastoreType.OPERATIONAL, subOpIdentifier, subOpEntry); logger.info("onInterfaceDown: Updated subnetopdataentry to OP Datastore port down " + intfName); } catch (Exception ex) { logger.error("Creation of SubnetOpDataEntry for subnet " + subnetId.getValue() + " failed {}" + ex); } finally { } } } private static void setRdToElanOpEntry(DataBroker broker, String rd, String subnetIp, String nextHopIp, String vpnName, Long elanTag) { RdToElanOpEntryBuilder rdElanBuilder = null; RdToElanOpEntry rdElan = null; try { InstanceIdentifier rdIdentifier = InstanceIdentifier.builder(RdToElanOp.class). child(RdToElanOpEntry.class, new RdToElanOpEntryKey(rd, subnetIp)).build(); Optional optionalRd = VpnUtil.read(broker, LogicalDatastoreType.OPERATIONAL, rdIdentifier); if (!optionalRd.isPresent()) { // Create PortOpDataEntry only if not present rdElanBuilder = new RdToElanOpEntryBuilder().setKey(new RdToElanOpEntryKey(rd,subnetIp)); rdElanBuilder.setRd(rd).setSubnetIp(subnetIp).setNextHopIp(nextHopIp); rdElanBuilder.setElanTag(elanTag); rdElanBuilder.setVpnName(vpnName); rdElan = rdElanBuilder.build(); } else { rdElanBuilder = new RdToElanOpEntryBuilder(optionalRd.get()); rdElanBuilder.setRd(rd).setSubnetIp(subnetIp).setNextHopIp(nextHopIp); rdElanBuilder.setElanTag(elanTag); rdElanBuilder.setVpnName(vpnName); rdElan = rdElanBuilder.build(); } MDSALUtil.syncWrite(broker, LogicalDatastoreType.OPERATIONAL, rdIdentifier, rdElan); logger.info("Creating RdToElan entry Rd {} SubnetIp {} NextHopIp {} Elan {} " ,rd, subnetIp, nextHopIp, elanTag); } catch (Exception ex) { logger.error("Exception when creating RdToElan entry {}" + ex); } finally { } } private void addSubnetRouteToFib(String rd, String subnetIp, BigInteger nhDpnId, String vpnName, Long elanTag) { Preconditions.checkNotNull(rd, "RouteDistinguisher cannot be null or empty!"); Preconditions.checkNotNull(subnetIp, "SubnetRouteIp cannot be null or empty!"); Preconditions.checkNotNull(vpnName, "vpnName cannot be null or empty!"); Preconditions.checkNotNull(elanTag, "elanTag cannot be null or empty!"); String nexthopIp = null; if (nhDpnId != null) { nexthopIp = InterfaceUtils.getEndpointIpAddressForDPN(broker, nhDpnId); } int label = VpnUtil.getUniqueId(idManager, VpnConstants.VPN_IDPOOL_NAME, VpnUtil.getNextHopLabelKey(rd, subnetIp)); setRdToElanOpEntry(broker, rd, subnetIp, nexthopIp, vpnName, elanTag); vpnInterfaceManager.addFibEntryToDS(rd, subnetIp, nexthopIp, label); } private void deleteSubnetRouteFromFib(String rd, String subnetIp) { Preconditions.checkNotNull(rd, "RouteDistinguisher cannot be null or empty!"); Preconditions.checkNotNull(subnetIp, "SubnetRouteIp cannot be null or empty!"); vpnInterfaceManager.removeFibEntryFromDS(rd, subnetIp); } private void advertiseSubnetRouteToBgp(String rd, String subnetIp, BigInteger nhDpnId, String vpnName, Long elanTag) throws Exception { Preconditions.checkNotNull(rd, "RouteDistinguisher cannot be null or empty!"); Preconditions.checkNotNull(subnetIp, "SubnetRouteIp cannot be null or empty!"); Preconditions.checkNotNull(elanTag, "elanTag cannot be null or empty!"); Preconditions.checkNotNull(nhDpnId, "nhDpnId cannot be null or empty!"); Preconditions.checkNotNull(vpnName, "vpnName cannot be null or empty!"); String nexthopIp = null; nexthopIp = InterfaceUtils.getEndpointIpAddressForDPN(broker, nhDpnId); if (nexthopIp == null) { logger.error("createSubnetRouteInVpn: Unable to obtain endpointIp address for DPNId " + nhDpnId); throw new Exception("Unable to obtain endpointIp address for DPNId " + nhDpnId); } int label = VpnUtil.getUniqueId(idManager, VpnConstants.VPN_IDPOOL_NAME, VpnUtil.getNextHopLabelKey(rd, subnetIp)); setRdToElanOpEntry(broker, rd, subnetIp, nexthopIp, vpnName, elanTag); try { bgpManager.advertisePrefix(rd, subnetIp, nexthopIp, label); } catch (Exception e) { logger.error("Subnet route not advertised for rd " + rd + " failed ", e); throw e; } } private void withdrawSubnetRoutefromBgp(String rd, String subnetIp) throws Exception { Preconditions.checkNotNull(rd, "RouteDistinguisher cannot be null or empty!"); Preconditions.checkNotNull(subnetIp, "SubnetIp cannot be null or empty!"); try { bgpManager.withdrawPrefix(rd, subnetIp); } catch (Exception e) { logger.error("Subnet route not advertised for rd " + rd + " failed ", e); throw e; } } }