--- /dev/null
+/*
+ * 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<SubnetOpDataEntry> subOpIdentifier = InstanceIdentifier.builder(SubnetOpData.class).
+ child(SubnetOpDataEntry.class, new SubnetOpDataEntryKey(subnetId)).build();
+ Optional<SubnetOpDataEntry> 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<BigInteger, SubnetToDpn> subDpnMap = new HashMap<BigInteger, SubnetToDpn>();
+ 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.getVpnRd(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<SubnetToDpn>());
+ subOpBuilder.setRouteAdvState(TaskState.Na);
+ subOpBuilder.setElanTag(elanTag);
+
+ // First recover set of ports available in this subnet
+ InstanceIdentifier<Subnetmap> subMapid = InstanceIdentifier.builder(Subnetmaps.class).
+ child(Subnetmap.class, new SubnetmapKey(subnetId)).build();
+ Optional<Subnetmap> 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<Uuid> 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<SubnetToDpn>(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<SubnetOpDataEntry> subOpIdentifier = InstanceIdentifier.builder(SubnetOpData.class).
+ child(SubnetOpDataEntry.class, new SubnetOpDataEntryKey(subnetId)).build();
+ logger.trace(" Removing the SubnetOpDataEntry node for subnet: " + subnetId.getValue());
+ Optional<SubnetOpDataEntry> 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<SubnetToDpn> subDpnList = subOpBuilder.getSubnetToDpn();
+ for (SubnetToDpn subDpn: subDpnList) {
+ List<VpnInterfaces> vpnIntfList = subDpn.getVpnInterfaces();
+ for (VpnInterfaces vpnIntf: vpnIntfList) {
+ subOpDpnManager.removePortOpDataEntry(vpnIntf.getInterfaceName());
+ }
+ }
+ //Removing Stale Ports in portOpData
+ InstanceIdentifier<Subnetmap> subMapid = InstanceIdentifier.builder(Subnetmaps.class).
+ child(Subnetmap.class, new SubnetmapKey(subnetId)).build();
+ Optional<Subnetmap> 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<Uuid> portList = subMap.getPortList();
+ if(portList!=null){
+ InstanceIdentifier<PortOpData> portOpIdentifier = InstanceIdentifier.builder(PortOpData.class).build();
+ Optional<PortOpData> 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<PortOpDataEntry> 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<SubnetOpDataEntry> subOpIdentifier = InstanceIdentifier.builder(SubnetOpData.class).
+ child(SubnetOpDataEntry.class, new SubnetOpDataEntryKey(subnetId)).build();
+ Optional<SubnetOpDataEntry> 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<SubnetOpDataEntry> subOpIdentifier = InstanceIdentifier.builder(SubnetOpData.class).
+ child(SubnetOpDataEntry.class, new SubnetOpDataEntryKey(subnetId)).build();
+
+ Optional<SubnetOpDataEntry> 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<SubnetToDpn> 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<SubnetOpDataEntry> subOpIdentifier = InstanceIdentifier.builder(SubnetOpData.class).
+ child(SubnetOpDataEntry.class, new SubnetOpDataEntryKey(subnetId)).build();
+ Optional<SubnetOpDataEntry> 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<SubnetToDpn> 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<SubnetOpDataEntry> subOpIdentifier = InstanceIdentifier.builder(SubnetOpData.class).
+ child(SubnetOpDataEntry.class, new SubnetOpDataEntryKey(subnetId)).build();
+ Optional<SubnetOpDataEntry> 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<SubnetToDpn> 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<SubnetOpDataEntry> subOpIdentifier = InstanceIdentifier.builder(SubnetOpData.class).
+ child(SubnetOpDataEntry.class, new SubnetOpDataEntryKey(subnetId)).build();
+ Optional<SubnetOpDataEntry> 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<SubnetToDpn> 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<RdToElanOpEntry> rdIdentifier = InstanceIdentifier.builder(RdToElanOp.class).
+ child(RdToElanOpEntry.class, new RdToElanOpEntryKey(rd, subnetIp)).build();
+ Optional<RdToElanOpEntry> 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;
+ }
+ }
+}
+