/* * Copyright © 2015, 2018 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.netvirt.neutronvpn; import static java.util.Collections.singletonList; import static org.opendaylight.genius.datastoreutils.SingleTransactionDataBroker.syncReadOptional; import static org.opendaylight.genius.infra.Datastore.CONFIGURATION; import com.google.common.base.Optional; import com.google.common.base.Preconditions; import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.JdkFutureAdapters; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.MoreExecutors; import com.google.common.util.concurrent.SettableFuture; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.EventListener; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Objects; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReentrantLock; import java.util.function.Consumer; import javax.annotation.Nonnull; import javax.annotation.Nullable; import javax.annotation.PreDestroy; import javax.inject.Inject; import javax.inject.Singleton; import org.opendaylight.controller.md.sal.binding.api.DataBroker; import org.opendaylight.controller.md.sal.binding.api.NotificationPublishService; import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; import org.opendaylight.controller.md.sal.common.api.data.OptimisticLockFailedException; import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException; import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException; import org.opendaylight.genius.datastoreutils.SingleTransactionDataBroker; import org.opendaylight.genius.infra.Datastore.Configuration; import org.opendaylight.genius.infra.ManagedNewTransactionRunner; import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl; import org.opendaylight.genius.infra.TypedWriteTransaction; import org.opendaylight.genius.mdsalutil.NwConstants; import org.opendaylight.genius.utils.JvmGlobalLocks; import org.opendaylight.infrautils.jobcoordinator.JobCoordinator; import org.opendaylight.infrautils.utils.concurrent.KeyedLocks; import org.opendaylight.infrautils.utils.concurrent.ListenableFutures; import org.opendaylight.netvirt.alarm.NeutronvpnAlarms; import org.opendaylight.netvirt.elanmanager.api.IElanService; import org.opendaylight.netvirt.fibmanager.api.FibHelper; import org.opendaylight.netvirt.neutronvpn.api.enums.IpVersionChoice; import org.opendaylight.netvirt.neutronvpn.api.utils.NeutronConstants; import org.opendaylight.netvirt.neutronvpn.evpn.manager.NeutronEvpnManager; import org.opendaylight.netvirt.neutronvpn.evpn.utils.NeutronEvpnUtils; import org.opendaylight.netvirt.vpnmanager.api.IVpnManager; import org.opendaylight.netvirt.vpnmanager.api.VpnHelper; import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.VpnInstances; import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.VpnInterfaces; import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.af.config.VpnTargets; import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.af.config.VpnTargetsBuilder; import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.af.config.vpntargets.VpnTarget; import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.af.config.vpntargets.VpnTargetBuilder; import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.af.config.vpntargets.VpnTargetKey; import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.instances.VpnInstance; import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.instances.VpnInstanceBuilder; import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.instances.VpnInstanceKey; import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.instances.vpn.instance.Ipv4FamilyBuilder; import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.instances.vpn.instance.Ipv6FamilyBuilder; import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.VpnInterface; import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.VpnInterfaceBuilder; import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.VpnInterfaceKey; import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.vpn._interface.VpnInstanceNames; import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.vpn._interface.VpnInstanceNames.AssociatedSubnetType; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress; 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.netvirt.l3vpn.rev130911.Adjacencies; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.AdjacenciesBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.adjacency.list.Adjacency; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.adjacency.list.Adjacency.AdjacencyType; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.adjacency.list.AdjacencyBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.adjacency.list.AdjacencyKey; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.learnt.vpn.vip.to.port.data.LearntVpnVipToPort; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.VpnInstanceOpDataEntry; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.VpnInstanceOpDataEntry.BgpvpnType; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ExternalNetworks; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ProviderTypes; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.external.networks.Networks; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.external.networks.NetworksBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.external.networks.NetworksKey; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.config.rev160806.NeutronvpnConfig; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.AssociateNetworksInput; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.AssociateNetworksOutput; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.AssociateNetworksOutputBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.AssociateRouterInput; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.AssociateRouterOutput; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.AssociateRouterOutputBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.CreateEVPNInput; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.CreateEVPNOutput; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.CreateL3VPNInput; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.CreateL3VPNOutput; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.CreateL3VPNOutputBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.DeleteEVPNInput; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.DeleteEVPNOutput; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.DeleteL3VPNInput; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.DeleteL3VPNOutput; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.DeleteL3VPNOutputBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.DissociateNetworksInput; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.DissociateNetworksOutput; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.DissociateNetworksOutputBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.DissociateRouterInput; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.DissociateRouterOutput; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.DissociateRouterOutputBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.GetEVPNInput; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.GetEVPNOutput; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.GetFixedIPsForNeutronPortInput; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.GetFixedIPsForNeutronPortOutput; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.GetFixedIPsForNeutronPortOutputBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.GetL3VPNInput; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.GetL3VPNInputBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.GetL3VPNOutput; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.GetL3VPNOutputBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.NetworkAttributes; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.NeutronvpnService; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.RouterInterfacesMap; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.Subnetmaps; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.VpnMaps; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.createl3vpn.input.L3vpn; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.getl3vpn.output.L3vpnInstances; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.getl3vpn.output.L3vpnInstancesBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.neutron.vpn.portip.port.data.VpnPortipToPort; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.router.interfaces.map.RouterInterfaces; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.router.interfaces.map.RouterInterfacesKey; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.router.interfaces.map.router.interfaces.Interfaces; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.router.interfaces.map.router.interfaces.InterfacesBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.router.interfaces.map.router.interfaces.InterfacesKey; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.subnetmaps.Subnetmap; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.subnetmaps.SubnetmapBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.subnetmaps.SubnetmapKey; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.vpnmaps.VpnMap; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.vpnmaps.VpnMapBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.vpnmaps.VpnMapKey; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.vpnmaps.vpnmap.RouterIds; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.vpnmaps.vpnmap.RouterIdsBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.vpn.rpc.rev160201.AddStaticRouteInput; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.vpn.rpc.rev160201.AddStaticRouteInputBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.vpn.rpc.rev160201.AddStaticRouteOutput; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.vpn.rpc.rev160201.RemoveStaticRouteInput; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.vpn.rpc.rev160201.RemoveStaticRouteInputBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.vpn.rpc.rev160201.VpnRpcService; import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.extensions.rev160617.BgpvpnVni; import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.extensions.rev160617.OperationalPortStatus; import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.extensions.rev160617.service.provider.features.attributes.Features; import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.extensions.rev160617.service.provider.features.attributes.features.Feature; import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.extensions.rev160617.service.provider.features.attributes.features.FeatureBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.extensions.rev160617.service.provider.features.attributes.features.FeatureKey; import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l3.rev150712.l3.attributes.Routes; import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l3.rev150712.routers.attributes.routers.Router; import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.networks.rev150712.networks.attributes.networks.Network; import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.port.attributes.FixedIps; import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.Ports; import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.ports.Port; import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.provider.ext.rev150712.NetworkProviderExtension; import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.rev150712.Neutron; import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.subnets.rev150712.subnets.attributes.subnets.Subnet; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netvirt.inter.vpn.link.rev160311.inter.vpn.links.InterVpnLink; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; import org.opendaylight.yangtools.yang.common.RpcError; import org.opendaylight.yangtools.yang.common.RpcError.ErrorType; import org.opendaylight.yangtools.yang.common.RpcResult; import org.opendaylight.yangtools.yang.common.RpcResultBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.slf4j.helpers.FormattingTuple; import org.slf4j.helpers.MessageFormatter; @Singleton public class NeutronvpnManager implements NeutronvpnService, AutoCloseable, EventListener { private static final Logger LOG = LoggerFactory.getLogger(NeutronvpnManager.class); private static long LOCK_WAIT_TIME = 10L; private final DataBroker dataBroker; private final ManagedNewTransactionRunner txRunner; private final NotificationPublishService notificationPublishService; private final VpnRpcService vpnRpcService; private final NeutronFloatingToFixedIpMappingChangeListener floatingIpMapListener; private final IElanService elanService; private final NeutronvpnConfig neutronvpnConfig; private final NeutronEvpnManager neutronEvpnManager; private final NeutronEvpnUtils neutronEvpnUtils; private final JobCoordinator jobCoordinator; private final NeutronvpnUtils neutronvpnUtils; private final IVpnManager vpnManager; private final ConcurrentHashMap unprocessedPortsMap = new ConcurrentHashMap<>(); private final NeutronvpnAlarms neutronvpnAlarm = new NeutronvpnAlarms(); private final KeyedLocks vpnLock = new KeyedLocks<>(); private final KeyedLocks interfaceLock = new KeyedLocks<>(); @Inject public NeutronvpnManager( final DataBroker dataBroker, final NotificationPublishService notiPublishService, final VpnRpcService vpnRpcSrv, final IElanService elanService, final NeutronFloatingToFixedIpMappingChangeListener neutronFloatingToFixedIpMappingChangeListener, final NeutronvpnConfig neutronvpnConfig, final IVpnManager vpnManager, final JobCoordinator jobCoordinator, final NeutronvpnUtils neutronvpnUtils) throws TransactionCommitFailedException { this.dataBroker = dataBroker; this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker); notificationPublishService = notiPublishService; vpnRpcService = vpnRpcSrv; this.elanService = elanService; floatingIpMapListener = neutronFloatingToFixedIpMappingChangeListener; this.neutronvpnConfig = neutronvpnConfig; neutronEvpnManager = new NeutronEvpnManager(dataBroker, this, neutronvpnUtils); neutronEvpnUtils = new NeutronEvpnUtils(dataBroker, vpnManager, jobCoordinator); this.jobCoordinator = jobCoordinator; this.neutronvpnUtils = neutronvpnUtils; this.vpnManager = vpnManager; configureFeatures(); } @Override @PreDestroy public void close() { LOG.info("{} close", getClass().getSimpleName()); } private void configureFeatures() throws TransactionCommitFailedException { InstanceIdentifier iid = InstanceIdentifier.builder( Neutron.class).child(Features.class).child( Feature.class, new FeatureKey(OperationalPortStatus.class)).build(); Feature feature = new FeatureBuilder().withKey(new FeatureKey(OperationalPortStatus.class)).build(); try { SingleTransactionDataBroker.syncWrite(dataBroker, LogicalDatastoreType.OPERATIONAL, iid, feature); } catch (OptimisticLockFailedException e) { LOG.debug("Optimistic lock failed exception while configuring feature {}", feature, e); } InstanceIdentifier bgpvpnVniIid = InstanceIdentifier.builder( Neutron.class).child(Features.class).child( Feature.class, new FeatureKey(BgpvpnVni.class)).build(); Feature bgpvpnVniFeature = new FeatureBuilder().withKey(new FeatureKey(BgpvpnVni.class)).build(); try { SingleTransactionDataBroker.syncWrite( dataBroker, LogicalDatastoreType.OPERATIONAL, bgpvpnVniIid, bgpvpnVniFeature); } catch (OptimisticLockFailedException e) { LOG.debug("Optimistic lock failed exception while configuring feature {}", bgpvpnVniFeature, e); } } public String getOpenDaylightVniRangesConfig() { return neutronvpnConfig.getOpendaylightVniRanges(); } // TODO Clean up the exception handling @SuppressWarnings("checkstyle:IllegalCatch") protected void createSubnetmapNode(Uuid subnetId, String subnetIp, Uuid tenantId, Uuid networkId, @Nullable NetworkAttributes.NetworkType networkType, long segmentationId) { try { InstanceIdentifier subnetMapIdentifier = NeutronvpnUtils.buildSubnetMapIdentifier(subnetId); final ReentrantLock lock = lockForUuid(subnetId); lock.lock(); try { LOG.info("createSubnetmapNode: subnet ID {}", subnetId.toString()); Optional sn = SingleTransactionDataBroker.syncReadOptional(dataBroker, LogicalDatastoreType.CONFIGURATION, subnetMapIdentifier); if (sn.isPresent()) { LOG.error("createSubnetmapNode: Subnetmap node for subnet ID {} already exists, returning", subnetId.getValue()); return; } SubnetmapBuilder subnetmapBuilder = new SubnetmapBuilder().withKey(new SubnetmapKey(subnetId)) .setId(subnetId).setSubnetIp(subnetIp).setTenantId(tenantId).setNetworkId(networkId) .setNetworkType(networkType).setSegmentationId(segmentationId); LOG.debug("createSubnetmapNode: Adding a new subnet node in Subnetmaps DS for subnet {}", subnetId.getValue()); SingleTransactionDataBroker.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION, subnetMapIdentifier, subnetmapBuilder.build()); } finally { lock.unlock(); } } catch (TransactionCommitFailedException | ReadFailedException e) { LOG.error("createSubnetmapNode: Creating subnetmap node failed for subnet {}", subnetId.getValue()); } // check if there are ports to update for already created Subnetmap node LOG.debug("createSubnetmapNode: Update created Subnetmap for subnet {} with ports", subnetId.getValue()); for (Map.Entry entry : unprocessedPortsMap.entrySet()) { if (entry.getValue().getValue().equals(subnetId.getValue())) { updateSubnetmapNodeWithPorts(subnetId, entry.getKey(), null); unprocessedPortsMap.remove(entry.getKey()); } } } @Nullable protected Subnetmap updateSubnetNode(Uuid subnetId, @Nullable Uuid routerId, Uuid vpnId, @Nullable Uuid internetvpnId) { InstanceIdentifier id = InstanceIdentifier.builder(Subnetmaps.class) .child(Subnetmap.class, new SubnetmapKey(subnetId)) .build(); final ReentrantLock lock = lockForUuid(subnetId); lock.lock(); try { Optional sn = SingleTransactionDataBroker.syncReadOptional(dataBroker, LogicalDatastoreType.CONFIGURATION, id); if (!sn.isPresent()) { LOG.error("subnetmap node for subnet {} does not exist, returning", subnetId.getValue()); return null; } LOG.debug("updating existing subnetmap node for subnet ID {}", subnetId.getValue()); SubnetmapBuilder builder = new SubnetmapBuilder(sn.get()); if (routerId != null) { builder.setRouterId(routerId); } if (vpnId != null) { builder.setVpnId(vpnId); } if (NeutronvpnUtils.getIpVersionFromString(sn.get().getSubnetIp()) == IpVersionChoice.IPV6) { builder.setInternetVpnId(internetvpnId); } Subnetmap subnetmap = builder.build(); LOG.debug("Creating/Updating subnetMap node: {} ", subnetId.getValue()); SingleTransactionDataBroker.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION, id, subnetmap); return subnetmap; } catch (ReadFailedException | TransactionCommitFailedException e) { LOG.error("Subnet map update failed for node {}", subnetId.getValue(), e); return null; } finally { lock.unlock(); } } protected void updateSubnetNodeWithFixedIp(Uuid subnetId, @Nullable Uuid routerId, @Nullable Uuid routerInterfacePortId, @Nullable String fixedIp, @Nullable String routerIntfMacAddress, @Nullable Uuid vpnId) { InstanceIdentifier id = InstanceIdentifier.builder(Subnetmaps.class).child(Subnetmap.class, new SubnetmapKey(subnetId)).build(); final ReentrantLock lock = lockForUuid(subnetId); lock.lock(); try { Optional sn = SingleTransactionDataBroker.syncReadOptional(dataBroker, LogicalDatastoreType.CONFIGURATION, id); if (!sn.isPresent()) { LOG.error("WithRouterFixedIP: subnetmap node for subnet {} does not exist, returning ", subnetId.getValue()); return; } LOG.debug("WithRouterFixedIP: Updating existing subnetmap node for subnet ID {}", subnetId.getValue()); SubnetmapBuilder builder = new SubnetmapBuilder(sn.get()); builder.setRouterId(routerId); builder.setRouterInterfacePortId(routerInterfacePortId); builder.setRouterIntfMacAddress(routerIntfMacAddress); builder.setRouterInterfaceFixedIp(fixedIp); if (vpnId != null) { builder.setVpnId(vpnId); } Subnetmap subnetmap = builder.build(); LOG.debug("WithRouterFixedIP Creating/Updating subnetMap node for Router FixedIp: {} ", subnetId.getValue()); SingleTransactionDataBroker.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION, id, subnetmap); } catch (ReadFailedException | TransactionCommitFailedException e) { LOG.error("WithRouterFixedIP: subnet map for Router FixedIp failed for node {}", subnetId.getValue(), e); } finally { lock.unlock(); } } protected Subnetmap updateSubnetmapNodeWithPorts(Uuid subnetId, @Nullable Uuid portId, @Nullable Uuid directPortId) { Subnetmap subnetmap = null; InstanceIdentifier id = InstanceIdentifier.builder(Subnetmaps.class).child(Subnetmap.class, new SubnetmapKey(subnetId)).build(); LOG.info("updateSubnetmapNodeWithPorts : subnetId {}, subnetMapId {}", subnetId.toString(), id.toString()); final ReentrantLock lock = lockForUuid(subnetId); lock.lock(); try { Optional sn = SingleTransactionDataBroker.syncReadOptional(dataBroker, LogicalDatastoreType.CONFIGURATION, id); if (sn.isPresent()) { SubnetmapBuilder builder = new SubnetmapBuilder(sn.get()); if (null != portId) { List portList = builder.getPortList(); if (null == portList) { portList = new ArrayList<>(); } portList.add(portId); builder.setPortList(portList); LOG.debug("updateSubnetmapNodeWithPorts: Updating existing subnetmap node {} with port {}", subnetId.getValue(), portId.getValue()); } if (null != directPortId) { List directPortList = builder.getDirectPortList(); if (null == directPortList) { directPortList = new ArrayList<>(); } directPortList.add(directPortId); builder.setDirectPortList(directPortList); LOG.debug("Updating existing subnetmap node {} with port {}", subnetId.getValue(), directPortId.getValue()); } subnetmap = builder.build(); SingleTransactionDataBroker.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION, id, subnetmap); } else { LOG.info("updateSubnetmapNodeWithPorts: Subnetmap node is not ready {}, put port {} in unprocessed " + "cache ", subnetId.getValue(), portId.getValue()); unprocessedPortsMap.put(portId, subnetId); } } catch (ReadFailedException | TransactionCommitFailedException e) { LOG.error("Updating port list of a given subnetMap failed for node: {}", subnetId.getValue(), e); } finally { lock.unlock(); } return subnetmap; } protected Subnetmap removeFromSubnetNode(Uuid subnetId, @Nullable Uuid networkId, @Nullable Uuid routerId, Uuid vpnId, @Nullable Uuid portId) { Subnetmap subnetmap = null; InstanceIdentifier id = InstanceIdentifier.builder(Subnetmaps.class) .child(Subnetmap.class, new SubnetmapKey(subnetId)) .build(); final ReentrantLock lock = lockForUuid(subnetId); lock.lock(); try { Optional sn = SingleTransactionDataBroker.syncReadOptional(dataBroker, LogicalDatastoreType.CONFIGURATION, id); if (sn.isPresent()) { SubnetmapBuilder builder = new SubnetmapBuilder(sn.get()); if (routerId != null) { builder.setRouterId(null); } if (networkId != null) { builder.setNetworkId(null); } if (vpnId != null) { builder.setVpnId(null); } builder.setInternetVpnId(null); if (portId != null && builder.getPortList() != null) { List portList = builder.getPortList(); portList.remove(portId); builder.setPortList(portList); } subnetmap = builder.build(); LOG.debug("Removing from existing subnetmap node: {} ", subnetId.getValue()); SingleTransactionDataBroker.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION, id, subnetmap); } else { LOG.warn("removing from non-existing subnetmap node: {} ", subnetId.getValue()); } } catch (ReadFailedException | TransactionCommitFailedException e) { LOG.error("Removal from subnetmap failed for node: {}", subnetId.getValue()); } finally { lock.unlock(); } return subnetmap; } @Nullable protected Subnetmap removePortsFromSubnetmapNode(Uuid subnetId, @Nullable Uuid portId, @Nullable Uuid directPortId) { Subnetmap subnetmap = null; InstanceIdentifier id = InstanceIdentifier.builder(Subnetmaps.class).child(Subnetmap.class, new SubnetmapKey(subnetId)).build(); final ReentrantLock lock = lockForUuid(subnetId); lock.lock(); try { Optional sn = SingleTransactionDataBroker.syncReadOptional(dataBroker, LogicalDatastoreType.CONFIGURATION, id); if (sn.isPresent()) { SubnetmapBuilder builder = new SubnetmapBuilder(sn.get()); if (null != portId && null != builder.getPortList()) { List portList = builder.getPortList(); portList.remove(portId); builder.setPortList(portList); LOG.debug("Removing port {} from existing subnetmap node: {} ", portId.getValue(), subnetId.getValue()); } if (null != directPortId && null != builder.getDirectPortList()) { List directPortList = builder.getDirectPortList(); directPortList.remove(directPortId); builder.setDirectPortList(directPortList); LOG.debug("Removing direct port {} from existing subnetmap node: {} ", directPortId .getValue(), subnetId.getValue()); } subnetmap = builder.build(); SingleTransactionDataBroker.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION, id, subnetmap); } else { LOG.info("Trying to remove port from non-existing subnetmap node {}", subnetId.getValue()); } } catch (ReadFailedException | TransactionCommitFailedException e) { LOG.error("Removing a port from port list of a subnetmap failed for node: {}", subnetId.getValue(), e); } finally { lock.unlock(); } return subnetmap; } // TODO Clean up the exception handling @SuppressWarnings("checkstyle:IllegalCatch") protected void deleteSubnetMapNode(Uuid subnetId) { InstanceIdentifier subnetMapIdentifier = InstanceIdentifier.builder(Subnetmaps.class).child(Subnetmap.class, new SubnetmapKey(subnetId)).build(); LOG.debug("removing subnetMap node: {} ", subnetId.getValue()); final ReentrantLock lock = lockForUuid(subnetId); lock.lock(); try { SingleTransactionDataBroker.syncDelete(dataBroker, LogicalDatastoreType.CONFIGURATION, subnetMapIdentifier); } catch (TransactionCommitFailedException e) { LOG.error("Delete subnetMap node failed for subnet : {} ", subnetId.getValue()); } finally { lock.unlock(); } } public void updateVpnInstanceWithRDs(String vpnInstanceId, final List rds) { InstanceIdentifier vpnIdentifier = InstanceIdentifier.builder(VpnInstances.class) .child(VpnInstance.class, new VpnInstanceKey(vpnInstanceId)).build(); try { Optional vpnInstanceConfig = SingleTransactionDataBroker.syncReadOptional(dataBroker, LogicalDatastoreType.CONFIGURATION, vpnIdentifier); if (!vpnInstanceConfig.isPresent()) { LOG.debug("No VpnInstance present under config vpnInstance:{}", vpnInstanceId); return; } VpnInstance vpnInstance = vpnInstanceConfig.get(); VpnInstanceBuilder updateVpnInstanceBuilder = new VpnInstanceBuilder(vpnInstance); if (vpnInstance.getIpv4Family() != null) { Ipv4FamilyBuilder ipv4FamilyBuilder = new Ipv4FamilyBuilder(vpnInstance.getIpv4Family()); updateVpnInstanceBuilder.setIpv4Family(ipv4FamilyBuilder.setRouteDistinguisher(rds).build()); } if (vpnInstance.getIpv6Family() != null) { Ipv6FamilyBuilder ipv6FamilyBuilder = new Ipv6FamilyBuilder(vpnInstance.getIpv6Family()); updateVpnInstanceBuilder.setIpv6Family(ipv6FamilyBuilder.setRouteDistinguisher(rds).build()); } LOG.debug("Updating Config vpn-instance: {} with the list of RDs: {}", vpnInstanceId, rds); SingleTransactionDataBroker.syncUpdate(dataBroker, LogicalDatastoreType.CONFIGURATION, vpnIdentifier, updateVpnInstanceBuilder.build()); } catch (ReadFailedException | TransactionCommitFailedException ex) { LOG.warn("Error configuring feature ", ex); } } private void updateVpnInstanceNode(Uuid vpnId, List rd, List irt, List ert, VpnInstance.Type type, long l3vni, IpVersionChoice ipVersion) { String vpnName = vpnId.getValue(); VpnInstanceBuilder builder = null; List vpnTargetList = new ArrayList<>(); boolean isLockAcquired = false; InstanceIdentifier vpnIdentifier = InstanceIdentifier.builder(VpnInstances.class) .child(VpnInstance.class, new VpnInstanceKey(vpnName)).build(); try { Optional optionalVpn = SingleTransactionDataBroker.syncReadOptional(dataBroker, LogicalDatastoreType.CONFIGURATION, vpnIdentifier); LOG.debug("Creating/Updating a new vpn-instance node: {} ", vpnName); if (optionalVpn.isPresent()) { builder = new VpnInstanceBuilder(optionalVpn.get()); LOG.debug("updating existing vpninstance node"); } else { builder = new VpnInstanceBuilder().withKey(new VpnInstanceKey(vpnName)).setVpnInstanceName(vpnName) .setType(type).setL3vni(l3vni); } if (irt != null && !irt.isEmpty()) { if (ert != null && !ert.isEmpty()) { List commonRT = new ArrayList<>(irt); commonRT.retainAll(ert); for (String common : commonRT) { irt.remove(common); ert.remove(common); VpnTarget vpnTarget = new VpnTargetBuilder().withKey(new VpnTargetKey(common)).setVrfRTValue(common) .setVrfRTType(VpnTarget.VrfRTType.Both).build(); vpnTargetList.add(vpnTarget); } } for (String importRT : irt) { VpnTarget vpnTarget = new VpnTargetBuilder().withKey(new VpnTargetKey(importRT)).setVrfRTValue(importRT) .setVrfRTType(VpnTarget.VrfRTType.ImportExtcommunity).build(); vpnTargetList.add(vpnTarget); } } if (ert != null && !ert.isEmpty()) { for (String exportRT : ert) { VpnTarget vpnTarget = new VpnTargetBuilder().withKey(new VpnTargetKey(exportRT)).setVrfRTValue(exportRT) .setVrfRTType(VpnTarget.VrfRTType.ExportExtcommunity).build(); vpnTargetList.add(vpnTarget); } } VpnTargets vpnTargets = new VpnTargetsBuilder().setVpnTarget(vpnTargetList).build(); Ipv4FamilyBuilder ipv4vpnBuilder = new Ipv4FamilyBuilder().setVpnTargets(vpnTargets); Ipv6FamilyBuilder ipv6vpnBuilder = new Ipv6FamilyBuilder().setVpnTargets(vpnTargets); if (rd != null && !rd.isEmpty()) { ipv4vpnBuilder.setRouteDistinguisher(rd); ipv6vpnBuilder.setRouteDistinguisher(rd); } if (ipVersion != null && ipVersion.isIpVersionChosen(IpVersionChoice.IPV4)) { builder.setIpv4Family(ipv4vpnBuilder.build()); } if (ipVersion != null && ipVersion.isIpVersionChosen(IpVersionChoice.IPV6)) { builder.setIpv6Family(ipv6vpnBuilder.build()); } if (ipVersion != null && ipVersion.isIpVersionChosen(IpVersionChoice.UNDEFINED)) { builder.setIpv4Family(ipv4vpnBuilder.build()); } VpnInstance newVpn = builder.build(); isLockAcquired = vpnLock.tryLock(vpnId, LOCK_WAIT_TIME, TimeUnit.SECONDS); LOG.debug("Creating/Updating vpn-instance for {} ", vpnName); SingleTransactionDataBroker.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION, vpnIdentifier, newVpn); } catch (ReadFailedException | TransactionCommitFailedException e) { LOG.error("Update VPN Instance node failed for node: {} {} {} {}", vpnName, rd, irt, ert); } finally { if (isLockAcquired) { vpnLock.unlock(vpnId); } } } private void deleteVpnMapsNode(Uuid vpnId) { boolean isLockAcquired = false; InstanceIdentifier vpnMapIdentifier = InstanceIdentifier.builder(VpnMaps.class) .child(VpnMap.class, new VpnMapKey(vpnId)) .build(); LOG.debug("removing vpnMaps node: {} ", vpnId.getValue()); try { isLockAcquired = vpnLock.tryLock(vpnId, LOCK_WAIT_TIME, TimeUnit.SECONDS); SingleTransactionDataBroker.syncDelete(dataBroker, LogicalDatastoreType.CONFIGURATION, vpnMapIdentifier); } catch (TransactionCommitFailedException e) { LOG.error("Delete vpnMaps node failed for vpn : {} ", vpnId.getValue()); } finally { if (isLockAcquired) { vpnLock.unlock(vpnId); } } } protected void updateVpnMaps(Uuid vpnId, @Nullable String name, @Nullable Uuid router, @Nullable Uuid tenantId, @Nullable List networks) { VpnMapBuilder builder; boolean isLockAcquired = false; InstanceIdentifier vpnMapIdentifier = InstanceIdentifier.builder(VpnMaps.class) .child(VpnMap.class, new VpnMapKey(vpnId)) .build(); try { Optional optionalVpnMap = SingleTransactionDataBroker.syncReadOptional(dataBroker, LogicalDatastoreType.CONFIGURATION, vpnMapIdentifier); if (optionalVpnMap.isPresent()) { builder = new VpnMapBuilder(optionalVpnMap.get()); } else { builder = new VpnMapBuilder().withKey(new VpnMapKey(vpnId)).setVpnId(vpnId); } if (name != null) { builder.setName(name); } if (tenantId != null) { builder.setTenantId(tenantId); } if (router != null) { RouterIds vpnRouterId = new RouterIdsBuilder().setRouterId(router).build(); List rtrIds = builder.getRouterIds(); if (rtrIds == null) { rtrIds = Collections.singletonList(vpnRouterId); } else { rtrIds.add(vpnRouterId); } builder.setRouterIds(rtrIds); } if (networks != null) { List nwList = builder.getNetworkIds(); if (nwList == null) { nwList = new ArrayList<>(); } nwList.addAll(networks); builder.setNetworkIds(nwList); } isLockAcquired = vpnLock.tryLock(vpnId, LOCK_WAIT_TIME, TimeUnit.SECONDS); LOG.debug("Creating/Updating vpnMaps node: {} ", vpnId.getValue()); SingleTransactionDataBroker.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION, vpnMapIdentifier, builder.build()); LOG.debug("VPNMaps DS updated for VPN {} ", vpnId.getValue()); } catch (ReadFailedException | TransactionCommitFailedException e) { LOG.error("UpdateVpnMaps failed for node: {} ", vpnId.getValue()); } finally { if (isLockAcquired) { vpnLock.unlock(vpnId); } } } private void clearFromVpnMaps(Uuid vpnId, @Nullable Uuid routerId, @Nullable List networkIds) { boolean isLockAcquired = false; InstanceIdentifier vpnMapIdentifier = InstanceIdentifier.builder(VpnMaps.class) .child(VpnMap.class, new VpnMapKey(vpnId)) .build(); Optional optionalVpnMap; try { optionalVpnMap = SingleTransactionDataBroker.syncReadOptional(dataBroker, LogicalDatastoreType.CONFIGURATION, vpnMapIdentifier); } catch (ReadFailedException e) { LOG.error("Error reading the VPN map for {}", vpnMapIdentifier, e); return; } if (optionalVpnMap.isPresent()) { VpnMap vpnMap = optionalVpnMap.get(); VpnMapBuilder vpnMapBuilder = new VpnMapBuilder(vpnMap); List rtrIds = vpnMap.getRouterIds(); if (rtrIds == null) { rtrIds = new ArrayList<>(); } if (routerId != null) { if (vpnMap.getNetworkIds() == null && routerId.equals(vpnMap.getVpnId())) { rtrIds.add(new RouterIdsBuilder().setRouterId(routerId).build()); vpnMapBuilder.setRouterIds(rtrIds); try { // remove entire node in case of internal VPN isLockAcquired = vpnLock.tryLock(vpnId, LOCK_WAIT_TIME, TimeUnit.SECONDS); LOG.debug("removing vpnMaps node: {} ", vpnId); SingleTransactionDataBroker.syncDelete(dataBroker, LogicalDatastoreType.CONFIGURATION, vpnMapIdentifier); } catch (TransactionCommitFailedException e) { LOG.error("Deletion of vpnMaps node failed for vpn {}", vpnId.getValue()); } finally { if (isLockAcquired) { vpnLock.unlock(vpnId); } } return; } else if (vpnMap.getNetworkIds() == null && !routerId.equals(vpnMap.getVpnId())) { rtrIds.remove(new RouterIdsBuilder().setRouterId(routerId).build()); vpnMapBuilder.setRouterIds(rtrIds); LOG.debug("Removing routerId {} in vpnMaps for the vpn {}", routerId, vpnId.getValue()); } } if (networkIds != null) { List vpnNw = vpnMap.getNetworkIds(); vpnNw.removeAll(networkIds); if (vpnNw.isEmpty()) { LOG.debug("setting networks null in vpnMaps node: {} ", vpnId.getValue()); vpnMapBuilder.setNetworkIds(null); } else { vpnMapBuilder.setNetworkIds(vpnNw); } } try { isLockAcquired = vpnLock.tryLock(vpnId, LOCK_WAIT_TIME, TimeUnit.SECONDS); LOG.debug("clearing from vpnMaps node: {} ", vpnId.getValue()); SingleTransactionDataBroker.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION, vpnMapIdentifier, vpnMapBuilder.build()); } catch (TransactionCommitFailedException e) { LOG.error("Clearing from vpnMaps node failed for vpn {}", vpnId.getValue()); } finally { if (isLockAcquired) { vpnLock.unlock(vpnId); } } } else { LOG.error("VPN : {} not found", vpnId.getValue()); } LOG.debug("Clear from VPNMaps DS successful for VPN {} ", vpnId.getValue()); } private void deleteVpnInstance(Uuid vpnId) { boolean isLockAcquired = false; InstanceIdentifier vpnIdentifier = InstanceIdentifier.builder(VpnInstances.class) .child(VpnInstance.class, new VpnInstanceKey(vpnId.getValue())) .build(); try { isLockAcquired = vpnLock.tryLock(vpnId, LOCK_WAIT_TIME, TimeUnit.SECONDS); LOG.debug("Deleting vpnInstance {}", vpnId.getValue()); SingleTransactionDataBroker.syncDelete(dataBroker, LogicalDatastoreType.CONFIGURATION, vpnIdentifier); } catch (TransactionCommitFailedException e) { LOG.error("Deletion of VPNInstance node failed for VPN {}", vpnId.getValue()); } finally { if (isLockAcquired) { vpnLock.unlock(vpnId); } } } protected Adjacencies createPortIpAdjacencies(Port port, Boolean isRouterInterface, TypedWriteTransaction wrtConfigTxn, @Nullable Subnetmap sn, @Nullable VpnInterface vpnIface) { List adjList = new ArrayList<>(); if (vpnIface != null) { adjList = vpnIface.augmentation(Adjacencies.class).getAdjacency(); } String infName = port.getUuid().getValue(); LOG.trace("neutronVpnManager: create config adjacencies for Port: {}", infName); for (FixedIps ip : port.nonnullFixedIps()) { String ipValue = ip.getIpAddress().stringValue(); String ipPrefix = ip.getIpAddress().getIpv4Address() != null ? ipValue + "/32" : ipValue + "/128"; Subnetmap snTemp = neutronvpnUtils.getSubnetmap(ip.getSubnetId()); if (snTemp != null && !FibHelper.doesPrefixBelongToSubnet(ipPrefix, snTemp.getSubnetIp(), false)) { continue; } Uuid vpnId = snTemp != null ? snTemp.getVpnId() : null; if (vpnId != null) { neutronvpnUtils.createVpnPortFixedIpToPort(vpnId.getValue(), ipValue, infName, port.getMacAddress().getValue(), isRouterInterface, wrtConfigTxn); //Create Neutron port adjacency if VPN presence is existing for subnet Adjacency vmAdj = new AdjacencyBuilder().withKey(new AdjacencyKey(ipPrefix)).setIpAddress(ipPrefix) .setMacAddress(port.getMacAddress().getValue()).setAdjacencyType(AdjacencyType.PrimaryAdjacency) .setSubnetId(ip.getSubnetId()).build(); if (!adjList.contains(vmAdj)) { adjList.add(vmAdj); } } Uuid routerId = snTemp != null ? snTemp.getRouterId() : null; if (snTemp != null && snTemp.getInternetVpnId() != null) { neutronvpnUtils.createVpnPortFixedIpToPort(snTemp.getInternetVpnId().getValue(), ipValue, infName, port.getMacAddress().getValue(), isRouterInterface, wrtConfigTxn); } if (routerId != null) { Router rtr = neutronvpnUtils.getNeutronRouter(routerId); if (rtr != null && rtr.getRoutes() != null) { List routeList = rtr.getRoutes(); // create extraroute Adjacence for each ipValue, // because router can have IPv4 and IPv6 subnet ports, or can have // more that one IPv4 subnet port or more than one IPv6 subnet port List erAdjList = getAdjacencyforExtraRoute(routeList, ipValue); if (!erAdjList.isEmpty()) { adjList.addAll(erAdjList); } } } } return new AdjacenciesBuilder().setAdjacency(adjList).build(); } protected void createVpnInterface(Collection vpnIds, Port port, @Nullable TypedWriteTransaction wrtConfigTxn) { boolean isRouterInterface = false; if (port.getDeviceOwner() != null) { isRouterInterface = NeutronConstants.DEVICE_OWNER_ROUTER_INF.equals(port.getDeviceOwner()); } String infName = port.getUuid().getValue(); // Handling cluster reboot scenario where VpnInterface already exists in datastore. VpnInterface vpnIface = VpnHelper.getVpnInterface(dataBroker, infName); Adjacencies adjs = createPortIpAdjacencies(port, isRouterInterface, wrtConfigTxn, null, vpnIface); LOG.trace("createVpnInterface for Port: {}, isRouterInterface: {}", infName, isRouterInterface); writeVpnInterfaceToDs(vpnIds, infName, adjs, port.getNetworkId(), isRouterInterface, wrtConfigTxn); } protected void withdrawPortIpFromVpnIface(Uuid vpnId, Uuid internetVpnId, Port port, Subnetmap sn, TypedWriteTransaction wrtConfigTxn) { String infName = port.getUuid().getValue(); InstanceIdentifier vpnIfIdentifier = NeutronvpnUtils.buildVpnInterfaceIdentifier(infName); Optional optionalVpnInterface = null; LOG.debug("withdrawPortIpFromVpnIface vpn {} internetVpn {} Port {}", vpnId, internetVpnId, infName); try { optionalVpnInterface = SingleTransactionDataBroker .syncReadOptional(dataBroker, LogicalDatastoreType .CONFIGURATION, vpnIfIdentifier); } catch (ReadFailedException e) { LOG.error("withdrawPortIpFromVpnIface: Error reading the VPN interface for {}", vpnIfIdentifier, e); return; } if (!optionalVpnInterface.isPresent()) { return; } LOG.trace("withdraw adjacencies for Port: {} subnet {}", port.getUuid().getValue(), sn != null ? sn.getSubnetIp() : "null"); List vpnAdjsList = optionalVpnInterface.get().augmentation(Adjacencies.class).nonnullAdjacency(); List updatedAdjsList = new ArrayList<>(); boolean isIpFromAnotherSubnet = false; for (Adjacency adj : vpnAdjsList) { String adjString = FibHelper.getIpFromPrefix(adj.getIpAddress()); if (sn == null || !Objects.equals(adj.getSubnetId(), sn.getId())) { if (adj.getAdjacencyType() == AdjacencyType.PrimaryAdjacency) { isIpFromAnotherSubnet = true; } updatedAdjsList.add(adj); continue; } if (adj.getAdjacencyType() == AdjacencyType.PrimaryAdjacency) { LOG.error("withdrawPortIpFromVpnIface: suppressing primaryAdjacency {} FixedIp for vpnId {}", adjString, vpnId); if (vpnId != null) { neutronvpnUtils.removeVpnPortFixedIpToPort(vpnId.getValue(), String.valueOf(adjString), wrtConfigTxn); } if (internetVpnId != null) { neutronvpnUtils.removeVpnPortFixedIpToPort(internetVpnId.getValue(), String.valueOf(adjString), wrtConfigTxn); } } else { if (NeutronConstants.DEVICE_OWNER_ROUTER_INF.equals(port.getDeviceOwner()) && sn.getRouterId() != null) { Router rtr = neutronvpnUtils.getNeutronRouter(sn.getRouterId()); if (rtr != null && rtr.getRoutes() != null) { List extraRoutesToRemove = new ArrayList<>(); for (Routes rt: rtr.getRoutes()) { if (rt.getNexthop().toString().equals(adjString)) { extraRoutesToRemove.add(rt); } } if (vpnId != null) { LOG.error("withdrawPortIpFromVpnIface: suppressing extraRoute {} for vpnId {}", extraRoutesToRemove, vpnId); removeAdjacencyforExtraRoute(vpnId, extraRoutesToRemove); } /* removeAdjacencyforExtraRoute done also for internet-vpn-id, in previous call */ } } } } Adjacencies adjacencies = new AdjacenciesBuilder().setAdjacency(updatedAdjsList).build(); if (vpnId != null) { updateVpnInterfaceWithAdjacencies(vpnId, infName, adjacencies, wrtConfigTxn); } if (!isIpFromAnotherSubnet) { // no more subnetworks for neutron port if (sn != null && sn.getRouterId() != null) { removeFromNeutronRouterInterfacesMap(sn.getRouterId(), port.getUuid().getValue()); } deleteVpnInterface(infName, null /* vpn-id */, wrtConfigTxn); return; } return; } // TODO Clean up the exception handling @SuppressWarnings("checkstyle:IllegalCatch") protected void deleteVpnInterface(String infName, @Nullable String vpnId, @Nullable TypedWriteTransaction wrtConfigTxn) { if (wrtConfigTxn == null) { ListenableFutures.addErrorLogging( txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> deleteVpnInterface(infName, vpnId, tx)), LOG, "Error deleting VPN interface {} {}", infName, vpnId); return; } InstanceIdentifier vpnIfIdentifier = NeutronvpnUtils.buildVpnInterfaceIdentifier(infName); Optional optionalVpnInterface; try { optionalVpnInterface = SingleTransactionDataBroker.syncReadOptional(dataBroker, LogicalDatastoreType.CONFIGURATION, vpnIfIdentifier); } catch (ReadFailedException ex) { LOG.error("Error during deletion of vpninterface {}", infName, ex); return; } if (!optionalVpnInterface.isPresent()) { LOG.warn("Deletion of vpninterface {}, optionalVpnInterface is not present()", infName); return; } if (vpnId != null) { VpnInterface vpnInterface = optionalVpnInterface.get(); List vpnList = vpnInterface.getVpnInstanceNames(); if (vpnList != null && VpnHelper.doesVpnInterfaceBelongToVpnInstance(vpnId, vpnList)) { VpnHelper.removeVpnInterfaceVpnInstanceNamesFromList(vpnId, vpnList); if (!vpnList.isEmpty()) { LOG.debug("Deleting vpn interface {} not immediately since vpnInstanceName " + "List not empty", infName); return; } VpnInterfaceBuilder vpnIfBuilder = new VpnInterfaceBuilder(optionalVpnInterface.get()) .setVpnInstanceNames(vpnList); wrtConfigTxn.put(vpnIfIdentifier, vpnIfBuilder .build()); } } LOG.debug("Deleting vpn interface {}", infName); wrtConfigTxn.delete(vpnIfIdentifier); } protected void removeInternetVpnFromVpnInterface(Uuid vpnId, Port port, TypedWriteTransaction writeConfigTxn, Subnetmap sm) { if (vpnId == null || port == null) { return; } String infName = port.getUuid().getValue(); InstanceIdentifier vpnIfIdentifier = NeutronvpnUtils.buildVpnInterfaceIdentifier(infName); try { Optional optionalVpnInterface = SingleTransactionDataBroker .syncReadOptional(dataBroker, LogicalDatastoreType .CONFIGURATION, vpnIfIdentifier); if (optionalVpnInterface.isPresent()) { List listVpn = optionalVpnInterface.get().getVpnInstanceNames(); if (listVpn != null && VpnHelper.doesVpnInterfaceBelongToVpnInstance(vpnId.getValue(), listVpn)) { VpnHelper.removeVpnInterfaceVpnInstanceNamesFromList(vpnId.getValue(), listVpn); } VpnInterfaceBuilder vpnIfBuilder = new VpnInterfaceBuilder(optionalVpnInterface.get()) .setVpnInstanceNames(listVpn); Adjacencies adjs = vpnIfBuilder.augmentation(Adjacencies.class); LOG.debug("Updating vpn interface {}", infName); List adjacencyList = adjs != null ? adjs.getAdjacency() : new ArrayList<>(); Iterator adjacencyIter = adjacencyList.iterator(); while (adjacencyIter.hasNext()) { Adjacency adjacency = adjacencyIter.next(); if (adjacency.getAdjacencyType() == AdjacencyType.PrimaryAdjacency) { continue; } String mipToQuery = adjacency.getIpAddress().split("/")[0]; InstanceIdentifier id = NeutronvpnUtils.buildLearntVpnVipToPortIdentifier(vpnId.getValue(), mipToQuery); Optional optionalVpnVipToPort = SingleTransactionDataBroker.syncReadOptional(dataBroker, LogicalDatastoreType.OPERATIONAL, id); if (optionalVpnVipToPort.isPresent()) { LOG.trace("Removing adjacencies from vpninterface {} upon dissociation of router {}", infName, vpnId); if (listVpn == null || listVpn.isEmpty()) { adjacencyIter.remove(); } neutronvpnUtils.removeLearntVpnVipToPort(vpnId.getValue(), mipToQuery); LOG.trace("Entry for fixedIP {} for port {} on VPN {} removed from VpnPortFixedIPToPortData", mipToQuery, infName, vpnId.getValue()); } } for (FixedIps ip : port.nonnullFixedIps()) { String ipValue = ip.getIpAddress().stringValue(); //skip IPv4 address if (!NeutronvpnUtils.getIpVersionFromString(ipValue).isIpVersionChosen(IpVersionChoice.IPV6)) { continue; } neutronvpnUtils.removeVpnPortFixedIpToPort(vpnId.getValue(), ipValue, writeConfigTxn); } if (listVpn == null || listVpn.isEmpty()) { if (sm != null && sm.getRouterId() != null) { removeFromNeutronRouterInterfacesMap(sm.getRouterId(), port.getUuid().getValue()); } deleteVpnInterface(port.getUuid().getValue(), null /* vpn-id */, writeConfigTxn); } else { writeConfigTxn.put(vpnIfIdentifier, vpnIfBuilder.build()); } } else { LOG.info("removeVpnFromVpnInterface: VPN Interface {} not found", infName); } } catch (ReadFailedException ex) { LOG.error("Update of vpninterface {} failed", infName, ex); } } protected void updateVpnInterface(Uuid vpnId, @Nullable Uuid oldVpnId, Port port, boolean isBeingAssociated, boolean isSubnetIp, TypedWriteTransaction writeConfigTxn, boolean isInternetVpn) { if (vpnId == null || port == null) { return; } boolean isLockAcquired = false; String infName = port.getUuid().getValue(); InstanceIdentifier vpnIfIdentifier = NeutronvpnUtils.buildVpnInterfaceIdentifier(infName); try { isLockAcquired = interfaceLock.tryLock(infName, LOCK_WAIT_TIME, TimeUnit.SECONDS); Optional optionalVpnInterface = SingleTransactionDataBroker.syncReadOptional(dataBroker, LogicalDatastoreType.CONFIGURATION, vpnIfIdentifier); if (optionalVpnInterface.isPresent()) { VpnInstanceNames vpnInstance = VpnHelper .getVpnInterfaceVpnInstanceNames(vpnId.getValue(), AssociatedSubnetType.V4AndV6Subnets); List listVpn = new ArrayList<>(optionalVpnInterface .get().getVpnInstanceNames()); if (oldVpnId != null && VpnHelper.doesVpnInterfaceBelongToVpnInstance(oldVpnId.getValue(), listVpn)) { VpnHelper.removeVpnInterfaceVpnInstanceNamesFromList(oldVpnId.getValue(), listVpn); } if (vpnId.getValue() != null && !VpnHelper.doesVpnInterfaceBelongToVpnInstance(vpnId.getValue(), listVpn)) { listVpn.add(vpnInstance); } VpnInterfaceBuilder vpnIfBuilder = new VpnInterfaceBuilder(optionalVpnInterface.get()) .setVpnInstanceNames(listVpn); LOG.debug("Updating vpn interface {}", infName); if (!isBeingAssociated) { Adjacencies adjs = vpnIfBuilder.augmentation(Adjacencies.class); List adjacencyList = adjs != null ? adjs.getAdjacency() : new ArrayList<>(); Iterator adjacencyIter = adjacencyList.iterator(); while (adjacencyIter.hasNext()) { Adjacency adjacency = adjacencyIter.next(); String mipToQuery = adjacency.getIpAddress().split("/")[0]; InstanceIdentifier id = NeutronvpnUtils.buildLearntVpnVipToPortIdentifier(oldVpnId.getValue(), mipToQuery); Optional optionalVpnVipToPort = SingleTransactionDataBroker.syncReadOptional(dataBroker, LogicalDatastoreType.OPERATIONAL, id); if (optionalVpnVipToPort.isPresent() && optionalVpnVipToPort.get().getPortName().equals(infName)) { LOG.trace("Removing adjacencies from vpninterface {} upon dissociation of router {} " + "from VPN {}", infName, vpnId, oldVpnId); adjacencyIter.remove(); neutronvpnUtils.removeLearntVpnVipToPort(oldVpnId.getValue(), mipToQuery); LOG.trace( "Entry for fixedIP {} for port {} on VPN {} removed from LearntVpnVipToPort", mipToQuery, infName, vpnId.getValue()); } InstanceIdentifier build = neutronvpnUtils.buildVpnPortipToPortIdentifier(oldVpnId.getValue(), mipToQuery); Optional persistedIp = SingleTransactionDataBroker.syncReadOptional(dataBroker, LogicalDatastoreType.OPERATIONAL, build); if (persistedIp.isPresent() && persistedIp.get().getPortName().equals(infName)) { neutronvpnUtils.removeVpnPortFixedIpToPort(oldVpnId.getValue(), mipToQuery, null); LOG.trace("Entry for fixedIP {} for port {} on VPN {} removed from VpnPortipToPort", mipToQuery, infName, vpnId.getValue()); } } Adjacencies adjacencies = new AdjacenciesBuilder().setAdjacency(adjacencyList).build(); vpnIfBuilder.addAugmentation(Adjacencies.class, adjacencies); } for (FixedIps ip : port.nonnullFixedIps()) { String ipValue = ip.getIpAddress().stringValue(); if (oldVpnId != null) { neutronvpnUtils.removeVpnPortFixedIpToPort(oldVpnId.getValue(), ipValue, writeConfigTxn); } if ((NeutronvpnUtils.getIpVersionFromString(ipValue) != IpVersionChoice.IPV6) && (isInternetVpn == true)) { continue; } neutronvpnUtils.createVpnPortFixedIpToPort(vpnId.getValue(), ipValue, infName, port .getMacAddress().getValue(), isSubnetIp, writeConfigTxn); } writeConfigTxn.put(vpnIfIdentifier, vpnIfBuilder.build()); } else { LOG.error("VPN Interface {} not found", infName); } } catch (ReadFailedException ex) { LOG.error("Updation of vpninterface {} failed", infName, ex); } finally { if (isLockAcquired) { interfaceLock.unlock(infName); } } } public void createL3InternalVpn(Uuid vpn, @Nullable String name, @Nullable Uuid tenant, @Nullable List rd, @Nullable List irt, @Nullable List ert, @Nullable Uuid router, @Nullable List networks) { IpVersionChoice ipVersChoices = neutronvpnUtils.getIpVersionChoicesFromRouterUuid(router); // Update VPN Instance node updateVpnInstanceNode(vpn, rd, irt, ert, VpnInstance.Type.L3, 0 /*l3vni*/, ipVersChoices); // Update local vpn-subnet DS updateVpnMaps(vpn, name, router, tenant, networks); if (router != null) { Uuid existingVpn = neutronvpnUtils.getVpnForRouter(router, true); if (existingVpn != null) { // use case when a cluster is rebooted and router add DCN is received, triggering #createL3InternalVpn // if before reboot, router was already associated to VPN, should not proceed associating router to // internal VPN. Adding to RouterInterfacesMap is also not needed since it's a config DS and will be // preserved upon reboot. // For a non-reboot case #associateRouterToInternalVPN already takes care of adding to // RouterInterfacesMap via #createVPNInterface call. LOG.info("Associating router to Internal VPN skipped for VPN {} due to router {} already associated " + "to external VPN {}", vpn.getValue(), router.getValue(), existingVpn.getValue()); return; } associateRouterToInternalVpn(vpn, router); } } /** * Performs the creation of a Neutron L3VPN, associating the new VPN to the * specified Neutron Networks and Routers. * * @param vpnId Uuid of the VPN tp be created * @param name Representative name of the new VPN * @param tenantId Uuid of the Tenant under which the VPN is going to be created * @param rdList Route-distinguisher for the VPN * @param irtList A list of Import Route Targets * @param ertList A list of Export Route Targets * @param routerIdsList ist of neutron router Id to associate with created VPN * @param networkList UUID of the neutron network the VPN may be associated to * @param type Type of the VPN Instance * @param l3vni L3VNI for the VPN Instance using VxLAN as the underlay * @throws Exception if association of L3VPN failed */ public void createVpn(Uuid vpnId, String name, Uuid tenantId, List rdList, List irtList, List ertList, @Nullable List routerIdsList, @Nullable List networkList, VpnInstance.Type type, long l3vni) throws Exception { IpVersionChoice ipVersChoices = IpVersionChoice.UNDEFINED; if (routerIdsList != null && !routerIdsList.isEmpty()) { for (Uuid routerId : routerIdsList) { IpVersionChoice vers = neutronvpnUtils.getIpVersionChoicesFromRouterUuid(routerId); ipVersChoices = ipVersChoices.addVersion(vers); } } updateVpnInstanceNode(vpnId, rdList, irtList, ertList, type, l3vni, ipVersChoices); // Please note that router and networks will be filled into VPNMaps // by subsequent calls here to associateRouterToVpn and // associateNetworksToVpn updateVpnMaps(vpnId, name, null, tenantId, null); LOG.debug("Created L3VPN with ID {}, name {}, tenantID {}, RDList {}, iRTList {}, eRTList{}, routerIdsList {}, " + "networkList {}", vpnId.getValue(), name, tenantId, rdList, irtList, ertList, routerIdsList, networkList); if (routerIdsList != null && !routerIdsList.isEmpty()) { for (Uuid routerId : routerIdsList) { associateRouterToVpn(vpnId, routerId); } } if (networkList != null) { List failStrings = associateNetworksToVpn(vpnId, networkList); if (!failStrings.isEmpty()) { LOG.error("VPN {} association to networks failed for networks: {}. ", vpnId.getValue(), failStrings.toString()); throw new Exception(failStrings.toString()); } } } /** * It handles the invocations to the createVPN RPC method. */ @Override // TODO Clean up the exception handling @SuppressWarnings("checkstyle:IllegalCatch") public ListenableFuture> createL3VPN(CreateL3VPNInput input) { CreateL3VPNOutputBuilder opBuilder = new CreateL3VPNOutputBuilder(); SettableFuture> result = SettableFuture.create(); List errorList = new ArrayList<>(); int failurecount = 0; int warningcount = 0; List vpns = input.getL3vpn(); if (vpns == null) { vpns = Collections.emptyList(); } for (L3vpn vpn : vpns) { if (neutronvpnUtils.doesVpnExist(vpn.getId())) { errorList.add(RpcResultBuilder.newWarning(ErrorType.PROTOCOL, "invalid-input", formatAndLog(LOG::warn, "Creation of L3VPN failed for VPN {} due to VPN with the same ID already present", vpn.getId().getValue()))); warningcount++; continue; } if (vpn.getRouteDistinguisher() == null || vpn.getImportRT() == null || vpn.getExportRT() == null) { errorList.add(RpcResultBuilder.newWarning(ErrorType.PROTOCOL, "invalid-input", formatAndLog(LOG::warn, "Creation of L3VPN failed for VPN {} due to absence of RD/iRT/eRT input", vpn.getId().getValue()))); warningcount++; continue; } VpnInstance.Type vpnInstanceType = VpnInstance.Type.L3; long l3vni = 0; if (vpn.getL3vni() != null) { l3vni = vpn.getL3vni(); } List existingRDs = neutronvpnUtils.getExistingRDs(); if (existingRDs.contains(vpn.getRouteDistinguisher().get(0))) { errorList.add(RpcResultBuilder.newWarning(ErrorType.PROTOCOL, "invalid-input", formatAndLog(LOG::warn, "Creation of L3VPN failed for VPN {} as another VPN with the same RD {} " + "is already configured", vpn.getId().getValue(), vpn.getRouteDistinguisher().get(0)))); warningcount++; continue; } Optional operationalVpn = getExistingOperationalVpn(vpn.getRouteDistinguisher().get(0)); if (operationalVpn.isPresent()) { errorList.add(RpcResultBuilder.newError(ErrorType.APPLICATION, "application-error", formatAndLog(LOG::error, "Creation of L3VPN failed for VPN {} as another VPN {} with the same RD {} " + "is still available. Please retry creation of a new vpn with the same RD" + " after a couple of minutes.", vpn.getId().getValue(), operationalVpn.get(), vpn.getRouteDistinguisher().get(0)))); warningcount++; continue; } if (vpn.getRouterIds() != null && !vpn.getRouterIds().isEmpty()) { List routerIdsList = vpn.getRouterIds(); for (org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.vpn.instance.RouterIds routerId : routerIdsList) { if (neutronvpnUtils.getNeutronRouter(routerId.getRouterId()) == null) { errorList.add(RpcResultBuilder.newWarning(ErrorType.PROTOCOL, "invalid-input", formatAndLog(LOG::warn, "Creation of L3VPN failed for VPN {} due to absense of routers" + "{}", vpn.getId(), routerId.getRouterId()))); warningcount++; continue; } Uuid vpnId = neutronvpnUtils.getVpnForRouter(routerId.getRouterId(), true); if (vpnId != null) { errorList.add(RpcResultBuilder.newWarning(ErrorType.PROTOCOL, "invalid-input", formatAndLog(LOG::warn, "Creation of L3VPN failed for VPN {} due to router {} already " + "associated to another VPN {}", vpn.getId(), routerId.getRouterId(), vpnId.getValue()))); warningcount++; continue; } } } if (vpn.getNetworkIds() != null) { int initialWarningCount = warningcount; for (Uuid nw : vpn.getNetworkIds()) { Network network = neutronvpnUtils.getNeutronNetwork(nw); Uuid vpnId = neutronvpnUtils.getVpnForNetwork(nw); if (network == null) { errorList.add(RpcResultBuilder.newWarning(ErrorType.PROTOCOL, "invalid-input", formatAndLog(LOG::warn, "Creation of L3VPN failed for VPN {} due to network not found {}", vpn.getId().getValue(), nw.getValue()))); warningcount++; } else if (vpnId != null) { errorList.add(RpcResultBuilder.newWarning(ErrorType.PROTOCOL, "invalid-input", formatAndLog(LOG::warn, "Creation of L3VPN failed for VPN {} due to network {} already associated" + " to another VPN {}", vpn.getId().getValue(), nw.getValue(), vpnId.getValue()))); warningcount++; } } if (warningcount != initialWarningCount) { continue; } } List rtrIdsList = new ArrayList<>(); if (vpn.getRouterIds() != null && !vpn.getRouterIds().isEmpty()) { for (org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.vpn.instance.RouterIds rtrId : vpn.getRouterIds()) { rtrIdsList.add(rtrId.getRouterId()); } } try { LOG.debug("L3VPN add RPC: VpnID {}, name {}, tenantID {}, RDList {}, iRTList {}, eRTList{}, " + "routerIdList {}, networksList {}", vpn.getId().getValue(), vpn.getName(), vpn.getTenantId(), vpn.getRouteDistinguisher().toString(), vpn.getImportRT().toString(), vpn.getExportRT().toString(), rtrIdsList, vpn.getNetworkIds()); createVpn(vpn.getId(), vpn.getName(), vpn.getTenantId(), vpn.getRouteDistinguisher(), vpn.getImportRT(), vpn.getExportRT(), rtrIdsList, vpn.getNetworkIds(), vpnInstanceType, l3vni); } catch (Exception ex) { errorList.add(RpcResultBuilder.newError(ErrorType.APPLICATION, formatAndLog(LOG::error, "Creation of VPN failed for VPN {}", vpn.getId().getValue(), ex), ex.getMessage())); failurecount++; } } // if at least one succeeds; result is success // if none succeeds; result is failure if (failurecount + warningcount == vpns.size()) { result.set(RpcResultBuilder.failed().withRpcErrors(errorList).build()); } else { List errorResponseList = new ArrayList<>(); if (!errorList.isEmpty()) { for (RpcError rpcError : errorList) { errorResponseList.add("ErrorType: " + rpcError.getErrorType() + ", ErrorTag: " + rpcError.getTag() + ", ErrorMessage: " + rpcError.getMessage()); } } else { errorResponseList.add("Operation successful with no errors"); } opBuilder.setResponse(errorResponseList); result.set(RpcResultBuilder.success().withResult(opBuilder.build()).build()); } return result; } /** * It handles the invocations to the neutronvpn:getL3VPN RPC method. */ @Override public ListenableFuture> getL3VPN(GetL3VPNInput input) { GetL3VPNOutputBuilder opBuilder = new GetL3VPNOutputBuilder(); SettableFuture> result = SettableFuture.create(); Uuid inputVpnId = input.getId(); List vpns = new ArrayList<>(); List l3vpnList = new ArrayList<>(); try { if (inputVpnId == null) { // get all vpns InstanceIdentifier vpnsIdentifier = InstanceIdentifier.builder(VpnInstances.class) .build(); Optional optionalVpns = SingleTransactionDataBroker.syncReadOptional(dataBroker, LogicalDatastoreType.CONFIGURATION, vpnsIdentifier); if (optionalVpns.isPresent() && !optionalVpns.get().getVpnInstance().isEmpty()) { for (VpnInstance vpn : optionalVpns.get().nonnullVpnInstance()) { // eliminating implicitly created (router and VLAN provider external network specific) VPNs // from getL3VPN output if (vpn.getIpv4Family().getRouteDistinguisher() != null) { vpns.add(vpn); } if (vpn.getIpv6Family().getRouteDistinguisher() != null) { vpns.add(vpn); } } } else { // No VPN present opBuilder.setL3vpnInstances(l3vpnList); result.set(RpcResultBuilder.success().withResult(opBuilder.build()).build()); return result; } } else { String name = inputVpnId.getValue(); InstanceIdentifier vpnIdentifier = InstanceIdentifier.builder(VpnInstances.class) .child(VpnInstance.class, new VpnInstanceKey(name)).build(); // read VpnInstance Info Optional optionalVpn = SingleTransactionDataBroker.syncReadOptional(dataBroker, LogicalDatastoreType.CONFIGURATION, vpnIdentifier); // eliminating implicitly created (router or VLAN provider external network specific) VPN from // getL3VPN output if (optionalVpn.isPresent() && optionalVpn.get().getIpv4Family().getRouteDistinguisher() != null || optionalVpn.get().getIpv6Family().getRouteDistinguisher() != null) { vpns.add(optionalVpn.get()); } else { result.set( RpcResultBuilder.failed().withWarning(ErrorType.PROTOCOL, "invalid-value", formatAndLog(LOG::error, "GetL3VPN failed because VPN {} is not present", name)).build()); } } for (VpnInstance vpnInstance : vpns) { Uuid vpnId = new Uuid(vpnInstance.getVpnInstanceName()); // create VpnMaps id L3vpnInstancesBuilder l3vpn = new L3vpnInstancesBuilder(); List rd = Collections.EMPTY_LIST; if (vpnInstance.getIpv4Family().getRouteDistinguisher() != null) { rd = vpnInstance.getIpv4Family().getRouteDistinguisher(); } else if (vpnInstance.getIpv6Family().getRouteDistinguisher() != null) { rd = vpnInstance.getIpv6Family().getRouteDistinguisher(); } List ertList = new ArrayList<>(); List irtList = new ArrayList<>(); if (vpnInstance.getIpv4Family().getVpnTargets() != null || vpnInstance.getIpv6Family().getVpnTargets() != null) { List vpnTargetList = Collections.EMPTY_LIST; if (!vpnInstance.getIpv4Family().getVpnTargets().getVpnTarget().isEmpty()) { vpnTargetList = vpnInstance.getIpv4Family().getVpnTargets().getVpnTarget(); } else if (!vpnInstance.getIpv6Family().getVpnTargets().getVpnTarget().isEmpty()) { vpnTargetList = vpnInstance.getIpv6Family().getVpnTargets().getVpnTarget(); } if (!vpnTargetList.isEmpty()) { for (VpnTarget vpnTarget : vpnTargetList) { if (vpnTarget.getVrfRTType() == VpnTarget.VrfRTType.ExportExtcommunity) { ertList.add(vpnTarget.getVrfRTValue()); } if (vpnTarget.getVrfRTType() == VpnTarget.VrfRTType.ImportExtcommunity) { irtList.add(vpnTarget.getVrfRTValue()); } if (vpnTarget.getVrfRTType() == VpnTarget.VrfRTType.Both) { ertList.add(vpnTarget.getVrfRTValue()); irtList.add(vpnTarget.getVrfRTValue()); } } } } l3vpn.setId(vpnId).setRouteDistinguisher(rd).setImportRT(irtList).setExportRT(ertList); if (vpnInstance.getL3vni() != null) { l3vpn.setL3vni(vpnInstance.getL3vni()); } InstanceIdentifier vpnMapIdentifier = InstanceIdentifier.builder(VpnMaps.class).child(VpnMap .class, new VpnMapKey(vpnId)).build(); Optional optionalVpnMap = SingleTransactionDataBroker.syncReadOptional(dataBroker, LogicalDatastoreType.CONFIGURATION, vpnMapIdentifier); if (optionalVpnMap.isPresent()) { VpnMap vpnMap = optionalVpnMap.get(); List rtrIds = new ArrayList<>(); if (vpnMap.getRouterIds() != null && !vpnMap.getRouterIds().isEmpty()) { for (RouterIds rtrId : vpnMap.getRouterIds()) { rtrIds.add(rtrId.getRouterId()); } } l3vpn.setRouterIds(NeutronvpnUtils.getVpnInstanceRouterIdsList(rtrIds)) .setNetworkIds(vpnMap.getNetworkIds()).setTenantId(vpnMap.getTenantId()) .setName(vpnMap.getName()); } l3vpnList.add(l3vpn.build()); } opBuilder.setL3vpnInstances(l3vpnList); result.set(RpcResultBuilder.success().withResult(opBuilder.build()).build()); } catch (ReadFailedException ex) { result.set(RpcResultBuilder.failed().withError(ErrorType.APPLICATION, formatAndLog(LOG::error, "GetVPN failed due to {}", ex.getMessage())).build()); } return result; } /** * It handles the invocations to the neutronvpn:deleteL3VPN RPC method. */ @Override public ListenableFuture> deleteL3VPN(DeleteL3VPNInput input) { DeleteL3VPNOutputBuilder opBuilder = new DeleteL3VPNOutputBuilder(); SettableFuture> result = SettableFuture.create(); List errorList = new ArrayList<>(); int failurecount = 0; int warningcount = 0; List vpns = input.getId() != null ? input.getId() : Collections.emptyList(); for (Uuid vpn : vpns) { try { LOG.debug("L3VPN delete RPC: VpnID {}", vpn.getValue()); InstanceIdentifier vpnIdentifier = InstanceIdentifier.builder(VpnInstances.class) .child(VpnInstance.class, new VpnInstanceKey(vpn.getValue())).build(); Optional optionalVpn = SingleTransactionDataBroker.syncReadOptional(dataBroker, LogicalDatastoreType.CONFIGURATION, vpnIdentifier); if (optionalVpn.isPresent()) { removeVpn(vpn); } else { errorList.add(RpcResultBuilder.newWarning(ErrorType.PROTOCOL, "invalid-value", formatAndLog(LOG::warn, "VPN with vpnid: {} does not exist", vpn.getValue()))); warningcount++; } } catch (ReadFailedException ex) { errorList.add(RpcResultBuilder.newError(ErrorType.APPLICATION, formatAndLog(LOG::error, "Deletion of L3VPN failed when deleting for uuid {}", vpn.getValue()), ex.getMessage())); failurecount++; } } // if at least one succeeds; result is success // if none succeeds; result is failure if (failurecount + warningcount == vpns.size()) { result.set(RpcResultBuilder.failed().withRpcErrors(errorList).build()); } else { List errorResponseList = new ArrayList<>(); if (!errorList.isEmpty()) { for (RpcError rpcError : errorList) { errorResponseList.add("ErrorType: " + rpcError.getErrorType() + ", ErrorTag: " + rpcError.getTag() + ", ErrorMessage: " + rpcError.getMessage()); } } else { errorResponseList.add("Operation successful with no errors"); } opBuilder.setResponse(errorResponseList); result.set(RpcResultBuilder.success().withResult(opBuilder.build()).build()); } return result; } public void createVpnInstanceForSubnet(Uuid subnetId) { LOG.debug("Creating/Updating L3 internalVPN for subnetID {} ", subnetId); createL3InternalVpn(subnetId, subnetId.getValue(), null, null, null, null, null, null); } public void removeVpnInstanceForSubnet(Uuid subnetId) { LOG.debug("Removing vpn-instance for subnetID {} ", subnetId); removeVpn(subnetId); } protected void addSubnetToVpn(@Nullable final Uuid vpnId, Uuid subnet, @Nullable final Uuid internetVpnId) { LOG.debug("addSubnetToVpn: Adding subnet {} to vpn {}", subnet.getValue(), vpnId != null ? vpnId.getValue() : internetVpnId.getValue()); Subnetmap sn = updateSubnetNode(subnet, null, vpnId, internetVpnId); if (sn == null) { LOG.error("addSubnetToVpn: subnetmap is null, cannot add subnet {} to VPN {}", subnet.getValue(), vpnId != null ? vpnId.getValue() : internetVpnId.getValue()); return; } if (vpnId != null) { VpnMap vpnMap = neutronvpnUtils.getVpnMap(vpnId); if (vpnMap == null) { LOG.error("addSubnetToVpn: No vpnMap for vpnId {}," + " cannot add subnet {} to VPN", vpnId.getValue(), subnet.getValue()); return; } final VpnInstance vpnInstance = VpnHelper.getVpnInstance(dataBroker, vpnId.getValue()); LOG.debug("addSubnetToVpn: VpnInstance {}", vpnInstance.toString()); if (isVpnOfTypeL2(vpnInstance)) { neutronEvpnUtils.updateElanAndVpn(vpnInstance, sn.getNetworkId().getValue(), NeutronEvpnUtils.Operation.ADD); } } if (internetVpnId != null) { VpnMap vpnMap = neutronvpnUtils.getVpnMap(internetVpnId); if (vpnMap == null) { LOG.error("addSubnetToVpn: No vpnMap for InternetVpnId {}, cannot add " + "subnet {} to VPN", internetVpnId.getValue(), subnet.getValue()); return; } } final Uuid internetId = internetVpnId; // Check if there are ports on this subnet and add corresponding vpn-interfaces List portList = sn.getPortList(); if (portList != null) { for (final Uuid portId : portList) { String vpnInfName = portId.getValue(); VpnInterface vpnIface = VpnHelper.getVpnInterface(dataBroker, vpnInfName); Port port = neutronvpnUtils.getNeutronPort(portId); if (port == null) { LOG.error("addSubnetToVpn: Cannot proceed with addSubnetToVpn for port {} in subnet {} " + "since port is absent in Neutron config DS", portId.getValue(), subnet.getValue()); continue; } final Boolean isRouterInterface = port.getDeviceOwner() .equals(NeutronConstants.DEVICE_OWNER_ROUTER_INF) ? true : false; jobCoordinator.enqueueJob("PORT-" + portId.getValue(), () -> singletonList( txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, wrtConfigTxn -> { Adjacencies portAdj = createPortIpAdjacencies(port, isRouterInterface, wrtConfigTxn, sn, vpnIface); if (vpnIface == null) { LOG.trace("addSubnetToVpn: create new VpnInterface for Port {}", vpnInfName); Set listVpn = new HashSet<>(); if (vpnId != null) { listVpn.add(vpnId); } if (internetId != null) { listVpn.add(internetId); } writeVpnInterfaceToDs(listVpn, vpnInfName, portAdj, port.getNetworkId(), isRouterInterface, wrtConfigTxn); if (sn.getRouterId() != null) { addToNeutronRouterInterfacesMap(sn.getRouterId(), portId.getValue()); } } else { LOG.trace("update VpnInterface for Port {} with adj {}", vpnInfName, portAdj); if (vpnId != null) { updateVpnInterfaceWithAdjacencies(vpnId, vpnInfName, portAdj, wrtConfigTxn); } if (internetId != null) { updateVpnInterfaceWithAdjacencies(internetId, vpnInfName, portAdj, wrtConfigTxn); } } })) ); } } } protected void removeSubnetFromVpn(final Uuid vpnId, Uuid subnet, @Nullable Uuid internetVpnId) { Preconditions.checkArgument(vpnId != null || internetVpnId != null, "removeSubnetFromVpn: at least one VPN must be not null"); LOG.debug("Removing subnet {} from vpn {}/{}", subnet.getValue(), vpnId, internetVpnId); Subnetmap sn = neutronvpnUtils.getSubnetmap(subnet); if (sn == null) { LOG.error("removeSubnetFromVpn: Subnetmap for subnet {} not found", subnet.getValue()); return; } VpnMap vpnMap = null; VpnInstance vpnInstance = null; if (vpnId != null) { vpnMap = neutronvpnUtils.getVpnMap(vpnId); if (vpnMap == null) { LOG.error("No vpnMap for vpnId {}, cannot remove subnet {} from VPN", vpnId.getValue(), subnet.getValue()); return; } vpnInstance = VpnHelper.getVpnInstance(dataBroker, vpnId.getValue()); } if (internetVpnId == null) { internetVpnId = sn.getInternetVpnId(); } if (internetVpnId != null) { vpnMap = neutronvpnUtils.getVpnMap(internetVpnId); if (vpnMap == null) { LOG.error("No vpnMap for vpnId {}, cannot remove subnet {}" + " from Internet VPN", internetVpnId.getValue(), subnet.getValue()); return; } } if (vpnInstance != null && isVpnOfTypeL2(vpnInstance)) { neutronEvpnUtils.updateElanAndVpn(vpnInstance, sn.getNetworkId().getValue(), NeutronEvpnUtils.Operation.DELETE); } boolean subnetVpnAssociation = false; if (vpnId != null && sn.getVpnId() != null && sn.getVpnId().getValue().equals(vpnId.getValue())) { subnetVpnAssociation = true; } else if (internetVpnId != null && sn.getInternetVpnId() != null && sn.getInternetVpnId().getValue().matches(internetVpnId.getValue())) { subnetVpnAssociation = true; } if (subnetVpnAssociation == false) { LOG.error("Removing subnet : Subnetmap is not in VPN {}/{}, owns {} and {}", vpnId, internetVpnId, sn.getVpnId(), sn.getInternetVpnId()); return; } // Check if there are ports on this subnet; remove corresponding vpn-interfaces List portList = sn.getPortList(); final Uuid internetId = internetVpnId; if (portList != null) { for (final Uuid portId : portList) { LOG.debug("withdrawing subnet IP {} from vpn-interface {}", sn.getSubnetIp(), portId.getValue()); final Port port = neutronvpnUtils.getNeutronPort(portId); jobCoordinator.enqueueJob("PORT-" + portId.getValue(), () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit( CONFIGURATION, tx -> { if (port != null) { withdrawPortIpFromVpnIface(vpnId, internetId, port, sn, tx); } else { LOG.warn( "Cannot proceed with withdrawPortIpFromVpnIface for port {} in subnet {} since " + "port is absent in Neutron config DS", portId.getValue(), subnet.getValue()); } }))); } } //update subnet-vpn association removeFromSubnetNode(subnet, null, null, vpnId, null); } protected void updateVpnInternetForSubnet(Subnetmap sm, Uuid vpn, boolean isBeingAssociated) { LOG.debug("updateVpnInternetForSubnet: {} subnet {} with BGPVPN Internet {} ", isBeingAssociated ? "associating" : "dissociating", sm.getSubnetIp(), vpn.getValue()); Uuid internalVpnId = sm.getVpnId(); if (internalVpnId == null) { LOG.error("updateVpnInternetForSubnet: can not find Internal or BGPVPN Id for subnet {}, bailing out", sm.getId().getValue()); return; } if (isBeingAssociated) { updateSubnetNode(sm.getId(), null, sm.getVpnId(), vpn); } else { updateSubnetNode(sm.getId(), null, sm.getVpnId(), null); } jobCoordinator.enqueueJob("VPN-" + vpn.getValue(), () -> singletonList( txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, wrtConfigTxn -> { if (isBeingAssociated) { updateVpnInterface(vpn, null, neutronvpnUtils.getNeutronPort( sm.getRouterInterfacePortId()), true, true, wrtConfigTxn, true); } else { removeInternetVpnFromVpnInterface(vpn, neutronvpnUtils.getNeutronPort(sm.getRouterInterfacePortId()), wrtConfigTxn, sm); } } ))); // Check for ports on this subnet and update association of // corresponding vpn-interfaces to internet vpn List portList = sm.getPortList(); if (portList != null) { for (Uuid port : portList) { LOG.debug("Updating vpn-interface for port {} isBeingAssociated {}", port.getValue(), isBeingAssociated); jobCoordinator.enqueueJob("PORT-" + port.getValue(), () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> { if (isBeingAssociated) { updateVpnInterface(vpn, null, neutronvpnUtils.getNeutronPort(port), true, false, tx, true); } else { removeInternetVpnFromVpnInterface(vpn, neutronvpnUtils.getNeutronPort(port), tx, sm); } }))); } } } @Nullable private Subnetmap updateVpnForSubnet(Uuid oldVpnId, Uuid newVpnId, Uuid subnet, boolean isBeingAssociated) { LOG.debug("Moving subnet {} from oldVpn {} to newVpn {} ", subnet.getValue(), oldVpnId.getValue(), newVpnId.getValue()); Uuid networkUuid = neutronvpnUtils.getSubnetmap(subnet).getNetworkId(); Network network = neutronvpnUtils.getNeutronNetwork(networkUuid); boolean netIsExternal = NeutronvpnUtils.getIsExternal(network); Uuid vpnExtUuid = netIsExternal ? null : neutronvpnUtils.getInternetvpnUuidBoundToSubnetRouter(subnet); Subnetmap sn = updateSubnetNode(subnet, null, newVpnId, vpnExtUuid); if (sn == null) { LOG.error("Updating subnet {} with newVpn {} failed", subnet.getValue(), newVpnId.getValue()); return sn; } /* vpnExtUuid will contain the value only on if the subnet is V6 and it is already been * associated with internet BGP-VPN. */ if (vpnExtUuid != null) { /* Update V6 Internet default route match with new VPN metadata. * isBeingAssociated = true means oldVpnId is same as routerId * isBeingAssociated = false means newVpnId is same as routerId */ if (isBeingAssociated) { neutronvpnUtils.updateVpnInstanceWithFallback(oldVpnId, vpnExtUuid, true); } else { neutronvpnUtils.updateVpnInstanceWithFallback(newVpnId, vpnExtUuid, true); } } //Update Router Interface first synchronously. //CAUTION: Please DONOT make the router interface VPN Movement as an asynchronous commit again ! ListenableFuture future = txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> updateVpnInterface(newVpnId, oldVpnId, neutronvpnUtils.getNeutronPort(sn.getRouterInterfacePortId()), isBeingAssociated, true, tx, false)); Futures.addCallback(future, new FutureCallback() { @Override public void onSuccess(Void result) { // Check for ports on this subnet and update association of // corresponding vpn-interfaces to external vpn List portList = sn.getPortList(); if (portList != null) { for (Uuid port : portList) { LOG.debug("Updating vpn-interface for port {} isBeingAssociated {}", port.getValue(), isBeingAssociated); jobCoordinator.enqueueJob("PORT-" + port.getValue(), () -> Collections.singletonList( txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> updateVpnInterface(newVpnId, oldVpnId, neutronvpnUtils.getNeutronPort(port), isBeingAssociated, false, tx, false)))); } } } @Override public void onFailure(Throwable throwable) { LOG.error( "Failed to update router interface {} in subnet {} from oldVpnId {} to newVpnId {}, " + "returning", sn.getRouterInterfacePortId().getValue(), subnet.getValue(), oldVpnId, newVpnId, throwable); } }, MoreExecutors.directExecutor()); return sn; } public InstanceIdentifier getRouterInterfacesId(Uuid routerId) { return InstanceIdentifier.builder(RouterInterfacesMap.class) .child(RouterInterfaces.class, new RouterInterfacesKey(routerId)).build(); } protected void addToNeutronRouterInterfacesMap(Uuid routerId, String interfaceName) { final InstanceIdentifier routerInterfacesId = getRouterInterfacesId(routerId); final ReentrantLock lock = lockForUuid(routerId); lock.lock(); try { Optional optRouterInterfaces = SingleTransactionDataBroker.syncReadOptional(dataBroker, LogicalDatastoreType.CONFIGURATION, routerInterfacesId); Interfaces routerInterface = new InterfacesBuilder().withKey(new InterfacesKey(interfaceName)) .setInterfaceId(interfaceName).build(); if (optRouterInterfaces.isPresent()) { SingleTransactionDataBroker.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION, routerInterfacesId.child(Interfaces.class, new InterfacesKey(interfaceName)), routerInterface); } else { // TODO Shouldn't we be doing something with builder and interfaces? // RouterInterfacesBuilder builder = new RouterInterfacesBuilder().setRouterId(routerId); // List interfaces = new ArrayList<>(); // interfaces.add(routerInterface); SingleTransactionDataBroker.syncUpdate(dataBroker, LogicalDatastoreType.CONFIGURATION, routerInterfacesId.child(Interfaces.class, new InterfacesKey(interfaceName)), routerInterface); } } catch (ReadFailedException | TransactionCommitFailedException e) { LOG.error("Error reading router interfaces for {}", routerInterfacesId, e); } finally { lock.unlock(); } } protected void removeFromNeutronRouterInterfacesMap(Uuid routerId, String interfaceName) { final InstanceIdentifier routerInterfacesId = getRouterInterfacesId(routerId); final ReentrantLock lock = lockForUuid(routerId); lock.lock(); try { Optional optRouterInterfaces = SingleTransactionDataBroker.syncReadOptional(dataBroker, LogicalDatastoreType.CONFIGURATION, routerInterfacesId); Interfaces routerInterface = new InterfacesBuilder().withKey(new InterfacesKey(interfaceName)) .setInterfaceId(interfaceName).build(); if (optRouterInterfaces.isPresent()) { RouterInterfaces routerInterfaces = optRouterInterfaces.get(); List interfaces = routerInterfaces.getInterfaces(); if (interfaces != null && interfaces.remove(routerInterface)) { if (interfaces.isEmpty()) { SingleTransactionDataBroker.syncDelete(dataBroker, LogicalDatastoreType.CONFIGURATION, routerInterfacesId); } else { SingleTransactionDataBroker.syncDelete(dataBroker, LogicalDatastoreType.CONFIGURATION, routerInterfacesId.child(Interfaces.class, new InterfacesKey(interfaceName))); } } } } catch (ReadFailedException | TransactionCommitFailedException e) { LOG.error("Error reading the router interfaces for {}", routerInterfacesId, e); } finally { lock.unlock(); } } /** * Creates the corresponding static routes in the specified VPN. These static routes must be point to an * InterVpnLink endpoint and the specified VPN must be the other end of the InterVpnLink. Otherwise the * route will be ignored. * * @param vpnName the VPN identifier * @param interVpnLinkRoutes The list of static routes * @param nexthopsXinterVpnLinks A Map with the correspondence nextHop-InterVpnLink */ public void addInterVpnRoutes(Uuid vpnName, List interVpnLinkRoutes, HashMap nexthopsXinterVpnLinks) { for (Routes route : interVpnLinkRoutes) { String nexthop = route.getNexthop().stringValue(); String destination = route.getDestination().stringValue(); InterVpnLink interVpnLink = nexthopsXinterVpnLinks.get(nexthop); if (isNexthopTheOtherVpnLinkEndpoint(nexthop, vpnName.getValue(), interVpnLink)) { AddStaticRouteInput rpcInput = new AddStaticRouteInputBuilder().setDestination(destination).setNexthop(nexthop) .setVpnInstanceName(vpnName.getValue()) .build(); Future> labelOuputFtr = vpnRpcService.addStaticRoute(rpcInput); RpcResult rpcResult; try { rpcResult = labelOuputFtr.get(); if (rpcResult.isSuccessful()) { LOG.debug("Label generated for destination {} is: {}", destination, rpcResult.getResult().getLabel()); } else { LOG.error("RPC call to add a static Route to {} with nexthop {} returned with errors {}", destination, nexthop, rpcResult.getErrors()); } } catch (InterruptedException | ExecutionException e) { LOG.error("Error happened while invoking addStaticRoute RPC for nexthop {} with destination {} " + "for VPN {}", nexthop, destination, vpnName.getValue(), e); } } else { // Any other case is a fault. LOG.warn("route with destination {} and nexthop {} does not apply to any InterVpnLink", route.getDestination().stringValue(), nexthop); continue; } } } /** * Removes the corresponding static routes from the specified VPN. These static routes point to an * InterVpnLink endpoint and the specified VPN must be the other end of the InterVpnLink. * * @param vpnName the VPN identifier * @param interVpnLinkRoutes The list of static routes * @param nexthopsXinterVpnLinks A Map with the correspondence nextHop-InterVpnLink */ public void removeInterVpnRoutes(Uuid vpnName, List interVpnLinkRoutes, HashMap nexthopsXinterVpnLinks) { for (Routes route : interVpnLinkRoutes) { String nexthop = route.getNexthop().stringValue(); String destination = route.getDestination().stringValue(); InterVpnLink interVpnLink = nexthopsXinterVpnLinks.get(nexthop); if (isNexthopTheOtherVpnLinkEndpoint(nexthop, vpnName.getValue(), interVpnLink)) { RemoveStaticRouteInput rpcInput = new RemoveStaticRouteInputBuilder().setDestination(destination).setNexthop(nexthop) .setVpnInstanceName(vpnName.getValue()) .build(); ListenableFutures.addErrorLogging(JdkFutureAdapters.listenInPoolThread( vpnRpcService.removeStaticRoute(rpcInput)), LOG, "Remove VPN routes"); } else { // Any other case is a fault. LOG.warn("route with destination {} and nexthop {} does not apply to any InterVpnLink", route.getDestination().stringValue(), nexthop); continue; } } } /* * Returns true if the specified nexthop is the other endpoint in an * InterVpnLink, regarding one of the VPN's point of view. */ private boolean isNexthopTheOtherVpnLinkEndpoint(String nexthop, String thisVpnUuid, InterVpnLink interVpnLink) { return interVpnLink != null && (interVpnLink.getFirstEndpoint().getVpnUuid().getValue().equals(thisVpnUuid) && interVpnLink.getSecondEndpoint().getIpAddress().getValue().equals(nexthop) || interVpnLink.getSecondEndpoint().getVpnUuid().getValue().equals(thisVpnUuid) && interVpnLink.getFirstEndpoint().getIpAddress().getValue().equals(nexthop)); } @Nonnull protected List getAdjacencyforExtraRoute(List routeList, String fixedIp) { List adjList = new ArrayList<>(); Map> adjMap = new HashMap<>(); for (Routes route : routeList) { if (route == null || route.getNexthop() == null || route.getDestination() == null) { LOG.error("Incorrect input received for extra route. {}", route); } else { String nextHop = route.getNexthop().stringValue(); String destination = route.getDestination().stringValue(); if (!nextHop.equals(fixedIp)) { LOG.trace("FixedIP {} is not extra route nexthop for destination {}", fixedIp, destination); continue; } LOG.trace("Adding extra route for destination {} with nexthop {} ", destination, nextHop); List hops = adjMap.computeIfAbsent(destination, k -> new ArrayList<>()); if (!hops.contains(nextHop)) { hops.add(nextHop); } } } for (Entry> entry : adjMap.entrySet()) { final String destination = entry.getKey(); final List ipList = entry.getValue(); Adjacency erAdj = new AdjacencyBuilder().setIpAddress(destination) .setAdjacencyType(AdjacencyType.ExtraRoute).setNextHopIpList(ipList) .withKey(new AdjacencyKey(destination)).build(); adjList.add(erAdj); } return adjList; } protected void updateVpnInterfaceWithExtraRouteAdjacency(Uuid vpnId, List routeList) { checkAlarmExtraRoutes(vpnId, routeList); for (Routes route : routeList) { if (route == null || route.getNexthop() == null || route.getDestination() == null) { LOG.error("Incorrect input received for extra route. {}", route); } else { String nextHop = route.getNexthop().stringValue(); String destination = route.getDestination().stringValue(); String infName = neutronvpnUtils.getNeutronPortNameFromVpnPortFixedIp(vpnId.getValue(), nextHop); if (infName != null) { LOG.trace("Updating extra route for destination {} onto vpn {} with nexthop {} and infName {}", destination, vpnId.getValue(), nextHop, infName); boolean isLockAcquired = false; try { InstanceIdentifier identifier = InstanceIdentifier.builder(VpnInterfaces.class) .child(VpnInterface.class, new VpnInterfaceKey(infName)).build(); InstanceIdentifier path = identifier.augmentation(Adjacencies.class) .child(Adjacency.class, new AdjacencyKey(destination)); Optional existingAdjacency = SingleTransactionDataBroker.syncReadOptional(dataBroker, LogicalDatastoreType.CONFIGURATION, path); if (existingAdjacency.isPresent() && existingAdjacency.get().getAdjacencyType() == AdjacencyType.PrimaryAdjacency) { LOG.error("The route with destination {} nextHop {} is already present as" + " a primary adjacency for interface {}. Skipping adjacency addition.", destination, nextHop, infName); continue; } Adjacency erAdj = new AdjacencyBuilder().setIpAddress(destination) .setNextHopIpList(Collections.singletonList(nextHop)).withKey(new AdjacencyKey(destination)) .setAdjacencyType(AdjacencyType.ExtraRoute).build(); isLockAcquired = interfaceLock.tryLock(infName, LOCK_WAIT_TIME, TimeUnit.SECONDS); SingleTransactionDataBroker.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION, path, erAdj); } catch (TransactionCommitFailedException e) { LOG.error("exception in adding extra route with destination: {}, next hop: {}", destination, nextHop, e); } catch (ReadFailedException e) { LOG.error("Exception on reading data-store ", e); } finally { if (isLockAcquired) { interfaceLock.unlock(infName); } } } else { LOG.error("Unable to find VPN NextHop interface to apply extra-route destination {} on VPN {} " + "with nexthop {}", destination, vpnId.getValue(), nextHop); } } } } /** * This method setup or down an alarm about extra route fault. * When extra routes are configured, through a router, if the number of nexthops is greater than the number of * available RDs, then an alarm and an error is generated.
* Be careful the routeList could be changed. * * @param vpnId the vpnId of vpn to control. * @param routeList the list of router to check, it could be modified. */ private void checkAlarmExtraRoutes(Uuid vpnId, List routeList) { if (!neutronvpnAlarm.isAlarmEnabled()) { LOG.debug("checkAlarmExtraRoutes is not enable for vpnId {} routeList {}", vpnId, routeList); return; } VpnInstance vpnInstance = neutronvpnUtils.getVpnInstance(dataBroker, vpnId); if (vpnInstance == null || routeList == null || routeList.isEmpty() || !neutronvpnAlarm.isAlarmEnabled()) { LOG.debug("checkAlarmExtraRoutes have args null as following : vpnId {} routeList {}", vpnId, routeList); return; } String primaryRd = neutronvpnUtils.getVpnRd(vpnId.getValue()); if (primaryRd == null || primaryRd.equals(vpnId.getValue())) { LOG.debug("checkAlarmExtraRoutes. vpn {} is not a BGPVPN. cancel checkExtraRoute", vpnId); return; } for (Routes route : routeList) { // count the number of nexthops for each same route.getDestingation().getValue() String destination = route.getDestination().stringValue(); String nextHop = route.getNexthop().stringValue(); List nextHopList = new ArrayList<>(); nextHopList.add(nextHop); int nbNextHops = 0; for (Routes routeTmp : routeList) { String routeDest = routeTmp.getDestination().stringValue(); if (!destination.equals(routeDest)) { continue; } String routeNextH = routeTmp.getNexthop().stringValue(); if (nextHop.equals(routeNextH)) { continue; } nbNextHops++; nextHopList.add(routeTmp.getNexthop().stringValue()); } final List rdList = new ArrayList<>(); if (vpnInstance.getIpv4Family() != null && vpnInstance.getIpv4Family().getRouteDistinguisher() != null) { vpnInstance.getIpv4Family().getRouteDistinguisher().forEach(rd -> { if (rd != null) { rdList.add(rd); } }); } if (vpnInstance.getIpv6Family() != null && vpnInstance.getIpv6Family().getRouteDistinguisher() != null) { vpnInstance.getIpv6Family().getRouteDistinguisher().forEach(rd -> { if (rd != null && !rdList.contains(rd)) { rdList.add(rd); } }); } // 1. VPN Instance Name String typeAlarm = "for vpnId: " + vpnId + " have exceeded next hops for prefixe"; // 2. Router ID List routerUuidList = neutronvpnUtils.getRouterIdListforVpn(vpnId); Uuid routerUuid = routerUuidList.get(0); StringBuilder detailsAlarm = new StringBuilder("routerUuid: "); detailsAlarm.append(routerUuid == null ? vpnId.toString() : routerUuid.getValue()); // 3. List of RDs associated with the VPN detailsAlarm.append(" List of RDs associated with the VPN: "); for (String s : rdList) { detailsAlarm.append(s); detailsAlarm.append(", "); } // 4. Prefix in question detailsAlarm.append(" for prefix: "); detailsAlarm.append(route.getDestination().stringValue()); // 5. List of NHs for the prefix detailsAlarm.append(" for nextHops: "); for (String s : nextHopList) { detailsAlarm.append(s); detailsAlarm.append(", "); } if (rdList.size() < nbNextHops) { neutronvpnAlarm.raiseNeutronvpnAlarm(typeAlarm, detailsAlarm.toString()); } else { neutronvpnAlarm.clearNeutronvpnAlarm(typeAlarm, detailsAlarm.toString()); } } } // TODO Clean up the exception handling @SuppressWarnings("checkstyle:IllegalCatch") protected void removeAdjacencyforExtraRoute(Uuid vpnId, List routeList) { for (Routes route : routeList) { if (route != null && route.getNexthop() != null && route.getDestination() != null) { boolean isLockAcquired = false; String nextHop = route.getNexthop().stringValue(); String destination = route.getDestination().stringValue(); String infName = neutronvpnUtils.getNeutronPortNameFromVpnPortFixedIp(vpnId.getValue(), nextHop); if (infName == null) { LOG.error("Unable to find VPN NextHop interface to remove extra-route destination {} on VPN {} " + "with nexthop {}", destination, vpnId.getValue(), nextHop); // Proceed to remove the next extra-route continue; } LOG.trace("Removing extra route for destination {} on vpn {} with nexthop {} and infName {}", destination, vpnId.getValue(), nextHop, infName); InstanceIdentifier adjacencyIdentifier = InstanceIdentifier.builder(VpnInterfaces.class) .child(VpnInterface.class, new VpnInterfaceKey(infName)) .augmentation(Adjacencies.class) .child(Adjacency.class, new AdjacencyKey(destination)) .build(); try { // Looking for existing prefix in MDSAL database Optional adjacency = SingleTransactionDataBroker.syncReadOptional(dataBroker, LogicalDatastoreType.CONFIGURATION, adjacencyIdentifier); boolean updateNextHops = false; List nextHopList = new ArrayList<>(); if (adjacency.isPresent()) { List nhListRead = adjacency.get().getNextHopIpList(); if (nhListRead.size() > 1) { // ECMP case for (String nextHopRead : nhListRead) { if (nextHopRead.equals(nextHop)) { updateNextHops = true; } else { nextHopList.add(nextHopRead); } } } } isLockAcquired = interfaceLock.tryLock(infName, LOCK_WAIT_TIME, TimeUnit.SECONDS); if (updateNextHops) { // An update must be done, not including the current next hop InstanceIdentifier vpnIfIdentifier = InstanceIdentifier.builder( VpnInterfaces.class).child(VpnInterface.class, new VpnInterfaceKey(infName)).build(); Adjacency newAdj = new AdjacencyBuilder(adjacency.get()).setIpAddress(destination) .setNextHopIpList(nextHopList) .withKey(new AdjacencyKey(destination)) .build(); Adjacencies erAdjs = new AdjacenciesBuilder().setAdjacency(Collections.singletonList(newAdj)).build(); VpnInterface vpnIf = new VpnInterfaceBuilder().withKey(new VpnInterfaceKey(infName)) .addAugmentation(Adjacencies.class, erAdjs).build(); SingleTransactionDataBroker.syncUpdate(dataBroker, LogicalDatastoreType.CONFIGURATION, vpnIfIdentifier, vpnIf); } else { // Remove the whole route SingleTransactionDataBroker.syncDelete(dataBroker, LogicalDatastoreType.CONFIGURATION, adjacencyIdentifier); LOG.trace("extra route {} deleted successfully", route); } } catch (TransactionCommitFailedException | ReadFailedException e) { LOG.error("exception in deleting extra route with destination {} for interface {}", destination, infName, e); } finally { if (isLockAcquired) { interfaceLock.unlock(infName); } } } else { LOG.error("Incorrect input received for extra route: {}", route); } } } public void removeVpn(Uuid vpnId) { // read VPNMaps VpnMap vpnMap = neutronvpnUtils.getVpnMap(vpnId); List routerIdsList = vpnMap != null ? vpnMap.getRouterIds() : null; List routerUuidList = new ArrayList<>(); // dissociate router if (routerIdsList != null && !routerIdsList.isEmpty()) { for (RouterIds router : routerIdsList) { Uuid routerId = router.getRouterId(); routerUuidList.add(routerId); dissociateRouterFromVpn(vpnId, routerId); } } if (!routerUuidList.contains(vpnId) && vpnMap.getNetworkIds() != null) { dissociateNetworksFromVpn(vpnId, vpnMap.getNetworkIds()); } // remove entire vpnMaps node deleteVpnMapsNode(vpnId); // remove vpn-instance deleteVpnInstance(vpnId); LOG.debug("Deleted L3VPN with ID {}", vpnId.getValue()); } private boolean isVpnOfTypeL2(VpnInstance vpnInstance) { return vpnInstance != null && vpnInstance.getType() == VpnInstance.Type.L2; } // TODO Clean up the exception handling @SuppressWarnings("checkstyle:IllegalCatch") protected void associateRouterToVpn(Uuid vpnId, Uuid routerId) { updateVpnMaps(vpnId, null, routerId, null, null); LOG.debug("associateRouterToVpn: Updating association of subnets to external vpn {}", vpnId.getValue()); List subMapList = neutronvpnUtils.getNeutronRouterSubnetMapList(routerId); IpVersionChoice ipVersion = IpVersionChoice.UNDEFINED; for (Subnetmap sn : subMapList) { IpVersionChoice ipVers = neutronvpnUtils.getIpVersionFromString(sn.getSubnetIp()); if (!ipVersion.isIpVersionChosen(ipVers)) { ipVersion = ipVersion.addVersion(ipVers); } } if (ipVersion != IpVersionChoice.UNDEFINED) { LOG.debug("associateRouterToVpn: Updating vpnInstanceOpDataEntrywith ip address family {} for VPN {} ", ipVersion, vpnId); neutronvpnUtils.updateVpnInstanceWithIpFamily(vpnId.getValue(), ipVersion, true); } for (Subnetmap sn : subMapList) { updateVpnForSubnet(routerId, vpnId, sn.getId(), true); } } protected void associateRouterToInternalVpn(Uuid vpnId, Uuid routerId) { List routerSubnets = neutronvpnUtils.getNeutronRouterSubnetIds(routerId); Uuid internetVpnId = neutronvpnUtils.getInternetvpnUuidBoundToRouterId(routerId); LOG.debug("Adding subnets to internal vpn {}", vpnId.getValue()); for (Uuid subnet : routerSubnets) { IpVersionChoice version = NeutronvpnUtils .getIpVersionFromSubnet(neutronvpnUtils.getSubnetmap(subnet)); if (version.isIpVersionChosen(IpVersionChoice.IPV4)) { addSubnetToVpn(vpnId, subnet, null); } else { addSubnetToVpn(vpnId, subnet, internetVpnId); } } } // TODO Clean up the exception handling @SuppressWarnings("checkstyle:IllegalCatch") protected void dissociateRouterFromVpn(Uuid vpnId, Uuid routerId) { clearFromVpnMaps(vpnId, routerId, null); List subMapList = neutronvpnUtils.getNeutronRouterSubnetMapList(routerId); IpVersionChoice ipVersion = IpVersionChoice.UNDEFINED; for (Subnetmap sn : subMapList) { IpVersionChoice ipVers = NeutronvpnUtils.getIpVersionFromString(sn.getSubnetIp()); if (ipVersion.isIpVersionChosen(ipVers)) { ipVersion = ipVersion.addVersion(ipVers); } LOG.debug("dissociateRouterFromVpn: Updating association of subnets to internal vpn {}", routerId.getValue()); updateVpnForSubnet(vpnId, routerId, sn.getId(), false); } if (ipVersion != IpVersionChoice.UNDEFINED) { LOG.debug("dissociateRouterFromVpn; Updating vpnInstanceOpDataEntry with ip address family {} for VPN {} ", ipVersion, vpnId); neutronvpnUtils.updateVpnInstanceWithIpFamily(vpnId.getValue(), ipVersion, false); } } /** * Parses and associates networks list with given VPN. * * @param vpnId Uuid of given VPN. * @param networkList List list of network Ids (Uuid), which will be associated. * @return list of formatted strings with detailed error messages. */ @Nonnull protected List associateNetworksToVpn(@Nonnull Uuid vpnId, @Nonnull List networkList) { List failedNwList = new ArrayList<>(); HashSet passedNwList = new HashSet<>(); boolean isExternalNetwork = false; if (networkList.isEmpty()) { LOG.error("associateNetworksToVpn: Failed as given networks list is empty, VPN Id: {}", vpnId.getValue()); failedNwList.add(String.format("Failed to associate networks with VPN %s as given networks list is empty", vpnId.getValue())); return failedNwList; } VpnInstance vpnInstance = VpnHelper.getVpnInstance(dataBroker, vpnId.getValue()); if (vpnInstance == null) { LOG.error("associateNetworksToVpn: Can not find vpnInstance for VPN {} in ConfigDS", vpnId.getValue()); failedNwList.add(String.format("Failed to associate network: can not found vpnInstance for VPN %s " + "in ConfigDS", vpnId.getValue())); return failedNwList; } try { if (isVpnOfTypeL2(vpnInstance) && neutronEvpnUtils.isVpnAssociatedWithNetwork(vpnInstance)) { LOG.error("associateNetworksToVpn: EVPN {} supports only one network to be associated with", vpnId.getValue()); failedNwList.add(String.format("Failed to associate network: EVPN %s supports only one network to be " + "associated with", vpnId.getValue())); return failedNwList; } Set routeTargets = vpnManager.getRtListForVpn(vpnId.getValue()); for (Uuid nw : networkList) { Network network = neutronvpnUtils.getNeutronNetwork(nw); if (network == null) { LOG.error("associateNetworksToVpn: Network {} not found in ConfigDS", nw.getValue()); failedNwList.add(String.format("Failed to associate network: network %s not found in ConfigDS", nw.getValue())); continue; } NetworkProviderExtension providerExtension = network.augmentation(NetworkProviderExtension.class); if (providerExtension.getSegments() != null && providerExtension.getSegments().size() > 1) { LOG.error("associateNetworksToVpn: MultiSegmented network {} not supported in BGPVPN {}", nw.getValue(), vpnId.getValue()); failedNwList.add(String.format("Failed to associate multisegmented network %s with BGPVPN %s", nw.getValue(), vpnId.getValue())); continue; } Uuid networkVpnId = neutronvpnUtils.getVpnForNetwork(nw); if (networkVpnId != null) { LOG.error("associateNetworksToVpn: Network {} already associated with another VPN {}", nw.getValue(), networkVpnId.getValue()); failedNwList.add(String.format("Failed to associate network %s as it is already associated to " + "another VPN %s", nw.getValue(), networkVpnId.getValue())); continue; } if (NeutronvpnUtils.getIsExternal(network) && !associateExtNetworkToVpn(vpnId, network)) { LOG.error("associateNetworksToVpn: Failed to associate Provider Network {} with VPN {}", nw.getValue(), vpnId.getValue()); failedNwList.add(String.format("Failed to associate Provider Network %s with VPN %s", nw.getValue(), vpnId.getValue())); continue; } if (NeutronvpnUtils.getIsExternal(network)) { isExternalNetwork = true; } List subnetmapList = neutronvpnUtils.getSubnetmapListFromNetworkId(nw); if (subnetmapList == null || subnetmapList.isEmpty()) { passedNwList.add(nw); continue; } if (vpnManager.checkForOverlappingSubnets(nw, subnetmapList, vpnId, routeTargets, failedNwList)) { continue; } IpVersionChoice ipVersion = IpVersionChoice.UNDEFINED; for (Subnetmap subnetmap : subnetmapList) { IpVersionChoice ipVers = neutronvpnUtils.getIpVersionFromString(subnetmap.getSubnetIp()); if (!ipVersion.isIpVersionChosen(ipVers)) { ipVersion = ipVersion.addVersion(ipVers); } } if (ipVersion != IpVersionChoice.UNDEFINED) { LOG.debug("associateNetworksToVpn: Updating vpnInstanceOpDataEntry with ip address family {}" + " for VPN {} ", ipVersion, vpnId); neutronvpnUtils.updateVpnInstanceWithIpFamily(vpnId.getValue(), ipVersion, true); } for (Subnetmap subnetmap : subnetmapList) { Uuid subnetId = subnetmap.getId(); Uuid subnetVpnId = neutronvpnUtils.getVpnForSubnet(subnetId); if (subnetVpnId != null) { LOG.error("associateNetworksToVpn: Failed to associate subnet {} with VPN {}" + " as it is already associated", subnetId.getValue(), subnetVpnId.getValue()); failedNwList.add(String.format("Failed to associate subnet %s with VPN %s" + " as it is already associated", subnetId.getValue(), vpnId.getValue())); continue; } if (!NeutronvpnUtils.getIsExternal(network)) { LOG.debug("associateNetworksToVpn: Add subnet {} to VPN {}", subnetId.getValue(), vpnId.getValue()); addSubnetToVpn(vpnId, subnetId, null); vpnManager.updateRouteTargetsToSubnetAssociation(routeTargets, subnetmap.getSubnetIp(), vpnId.getValue()); passedNwList.add(nw); } } passedNwList.add(nw); } } catch (ReadFailedException e) { LOG.error("associateNetworksToVpn: Failed to associate VPN {} with networks {}: ", vpnId.getValue(), networkList, e); failedNwList.add(String.format("Failed to associate VPN %s with networks %s: %s", vpnId.getValue(), networkList, e)); } //VpnMap update for ext-nw is already done in associateExtNetworkToVpn() method. if (!isExternalNetwork) { updateVpnMaps(vpnId, null, null, null, new ArrayList<>(passedNwList)); } LOG.info("Network(s) {} associated to L3VPN {} successfully", passedNwList.toString(), vpnId.getValue()); return failedNwList; } private boolean associateExtNetworkToVpn(@Nonnull Uuid vpnId, @Nonnull Network extNet) { if (!addExternalNetworkToVpn(extNet, vpnId)) { return false; } VpnInstanceOpDataEntry vpnOpDataEntry = neutronvpnUtils.getVpnInstanceOpDataEntryFromVpnId(vpnId.getValue()); if (vpnOpDataEntry == null) { LOG.error("associateExtNetworkToVpn: can not find VpnOpDataEntry for VPN {}", vpnId.getValue()); return false; } if (!vpnOpDataEntry.getBgpvpnType().equals(BgpvpnType.BGPVPNInternet)) { LOG.info("associateExtNetworkToVpn: set type {} for VPN {}", BgpvpnType.BGPVPNInternet, vpnId.getValue()); neutronvpnUtils.updateVpnInstanceOpWithType(BgpvpnType.BGPVPNInternet, vpnId); } //Update VpnMap with ext-nw is needed first before processing V6 internet default fallback flows List extNwList = Collections.singletonList(extNet.key().getUuid()); updateVpnMaps(vpnId, null, null, null, extNwList); IpVersionChoice ipVersion = IpVersionChoice.UNDEFINED; for (Uuid snId: neutronvpnUtils.getPrivateSubnetsToExport(extNet, vpnId)) { Subnetmap sm = neutronvpnUtils.getSubnetmap(snId); if (sm == null) { LOG.error("associateExtNetworkToVpn: can not find subnet with Id {} in ConfigDS", snId.getValue()); continue; } IpVersionChoice ipVers = NeutronvpnUtils.getIpVersionFromString(sm.getSubnetIp()); if (ipVers.isIpVersionChosen(IpVersionChoice.IPV4)) { continue; } if (ipVers.isIpVersionChosen(IpVersionChoice.IPV6)) { updateVpnInternetForSubnet(sm, vpnId, true); } if (!ipVersion.isIpVersionChosen(ipVers)) { ipVersion = ipVersion.addVersion(ipVers); } } if (ipVersion != IpVersionChoice.UNDEFINED) { neutronvpnUtils.updateVpnInstanceWithIpFamily(vpnId.getValue(), IpVersionChoice.IPV6, true); LOG.info("associateExtNetworkToVpn: add IPv6 Internet default route in VPN {}", vpnId.getValue()); neutronvpnUtils.updateVpnInstanceWithFallback(/*routerId*/ null, vpnId, true); } return true; } /** * Parses and disassociates networks list from given VPN. * * @param vpnId Uuid of given VPN. * @param networkList List list of network Ids (Uuid), which will be disassociated. * @return list of formatted strings with detailed error messages. */ @Nonnull protected List dissociateNetworksFromVpn(@Nonnull Uuid vpnId, @Nonnull List networkList) { List failedNwList = new ArrayList<>(); HashSet passedNwList = new HashSet<>(); if (networkList.isEmpty()) { LOG.error("dissociateNetworksFromVpn: Failed as networks list is empty"); failedNwList.add(String.format("Failed to disassociate networks from VPN %s as networks list is empty", vpnId.getValue())); return failedNwList; } for (Uuid nw : networkList) { List networkSubnets = neutronvpnUtils.getSubnetIdsFromNetworkId(nw); if (networkSubnets == null) { passedNwList.add(nw); continue; } Network network = neutronvpnUtils.getNeutronNetwork(nw); if (network == null) { LOG.error("dissociateNetworksFromVpn: Network {} not found in ConfigDS"); failedNwList.add(String.format("Failed to disassociate network %s as is not found in ConfigDS", nw.getValue())); continue; } Uuid networkVpnId = neutronvpnUtils.getVpnForNetwork(nw); if (networkVpnId == null) { LOG.error("dissociateNetworksFromVpn: Network {} is not associated to any VPN", nw.getValue()); failedNwList.add(String.format("Failed to disassociate network %s as is not associated to any VPN", nw.getValue())); continue; } if (!vpnId.equals(networkVpnId)) { LOG.error("dissociateNetworksFromVpn: Network {} is associated to another VPN {} instead of given {}", nw.getValue(), networkVpnId.getValue(), vpnId.getValue()); failedNwList.add(String.format("Failed to disassociate network %s as it is associated to another " + "vpn %s instead of given %s", nw.getValue(), networkVpnId.getValue(), vpnId.getValue())); continue; } if (NeutronvpnUtils.getIsExternal(network)) { if (disassociateExtNetworkFromVpn(vpnId, network)) { passedNwList.add(nw); } else { LOG.error("dissociateNetworksFromVpn: Failed to withdraw Provider Network {} from VPN {}", nw.getValue(), vpnId.getValue()); failedNwList.add(String.format("Failed to withdraw Provider Network %s from VPN %s", nw.getValue(), vpnId.getValue())); continue; } } IpVersionChoice ipVersion = IpVersionChoice.UNDEFINED; for (Uuid subnet : networkSubnets) { Subnetmap subnetmap = neutronvpnUtils.getSubnetmap(subnet); IpVersionChoice ipVers = NeutronvpnUtils.getIpVersionFromString(subnetmap.getSubnetIp()); if (!ipVersion.isIpVersionChosen(ipVers)) { ipVersion = ipVersion.addVersion(ipVers); } if (!NeutronvpnUtils.getIsExternal(network)) { LOG.debug("dissociateNetworksFromVpn: Withdraw subnet {} from VPN {}", subnet.getValue(), vpnId.getValue()); removeSubnetFromVpn(vpnId, subnet, null); Set routeTargets = vpnManager.getRtListForVpn(vpnId.getValue()); vpnManager.removeRouteTargetsToSubnetAssociation(routeTargets, subnetmap.getSubnetIp(), vpnId.getValue()); passedNwList.add(nw); } } if (ipVersion != IpVersionChoice.UNDEFINED) { LOG.debug("dissociateNetworksFromVpn: Updating vpnInstanceOpDataEntryupdate with ip address family {}" + " for VPN {}", ipVersion, vpnId); neutronvpnUtils.updateVpnInstanceWithIpFamily(vpnId.getValue(), ipVersion, false); } } clearFromVpnMaps(vpnId, null, new ArrayList<>(passedNwList)); LOG.info("dissociateNetworksFromVpn: Network(s) {} disassociated from L3VPN {} successfully", passedNwList.toString(), vpnId.getValue()); return failedNwList; } private boolean disassociateExtNetworkFromVpn(@Nonnull Uuid vpnId, @Nonnull Network extNet) { if (!removeExternalNetworkFromVpn(extNet)) { return false; } // check, if there is another Provider Networks associated with given VPN List vpnNets = getNetworksForVpn(vpnId); if (vpnNets != null) { //Remove currently disassociated network from the list vpnNets.remove(extNet.getUuid()); for (Uuid netId : vpnNets) { if (NeutronvpnUtils.getIsExternal(getNeutronNetwork(netId))) { LOG.error("dissociateExtNetworkFromVpn: Internet VPN {} is still associated with Provider Network " + "{}", vpnId.getValue(), netId.getValue()); return true; } } } //Set VPN Type is BGPVPNExternal from BGPVPNInternet LOG.info("disassociateExtNetworkFromVpn: set type {} for VPN {}", VpnInstanceOpDataEntry.BgpvpnType.BGPVPNExternal, vpnId.getValue()); neutronvpnUtils.updateVpnInstanceOpWithType(VpnInstanceOpDataEntry.BgpvpnType.BGPVPNExternal, vpnId); IpVersionChoice ipVersion = IpVersionChoice.UNDEFINED; for (Uuid snId : neutronvpnUtils.getPrivateSubnetsToExport(extNet, vpnId)) { Subnetmap sm = neutronvpnUtils.getSubnetmap(snId); if (sm == null) { LOG.error("disassociateExtNetworkFromVpn: can not find subnet with Id {} in ConfigDS", snId.getValue()); continue; } IpVersionChoice ipVers = NeutronvpnUtils.getIpVersionFromString(sm.getSubnetIp()); if (ipVers.isIpVersionChosen(IpVersionChoice.IPV4)) { continue; } if (ipVers.isIpVersionChosen(IpVersionChoice.IPV6)) { updateVpnInternetForSubnet(sm, vpnId, false); } if (!ipVersion.isIpVersionChosen(ipVers)) { ipVersion = ipVersion.addVersion(ipVers); } } if (ipVersion != IpVersionChoice.UNDEFINED) { neutronvpnUtils.updateVpnInstanceWithIpFamily(vpnId.getValue(), IpVersionChoice.IPV6, false); LOG.info("disassociateExtNetworkFromVpn: withdraw IPv6 Internet default route from VPN {}", vpnId.getValue()); neutronvpnUtils.updateVpnInstanceWithFallback(/*routerId*/ null, vpnId, false); } return true; } /** * It handles the invocations to the neutronvpn:associateNetworks RPC method. */ @Override // TODO Clean up the exception handling @SuppressWarnings("checkstyle:IllegalCatch") public ListenableFuture> associateNetworks(AssociateNetworksInput input) { AssociateNetworksOutputBuilder opBuilder = new AssociateNetworksOutputBuilder(); SettableFuture> result = SettableFuture.create(); StringBuilder returnMsg = new StringBuilder(); Uuid vpnId = input.getVpnId(); try { if (neutronvpnUtils.getVpnMap(vpnId) != null) { LOG.debug("associateNetworks RPC: VpnId {}, networkList {}", vpnId.getValue(), input.getNetworkId().toString()); List netIds = input.getNetworkId(); if (netIds != null && !netIds.isEmpty()) { List failed = associateNetworksToVpn(vpnId, netIds); if (!failed.isEmpty()) { returnMsg.append(failed); } } } else { returnMsg.append("VPN not found : ").append(vpnId.getValue()); } if (returnMsg.length() != 0) { opBuilder.setResponse( "ErrorType: PROTOCOL, ErrorTag: invalid-value, ErrorMessage: " + formatAndLog(LOG::error, "associate Networks to vpn {} failed due to {}", vpnId.getValue(), returnMsg)); result.set(RpcResultBuilder.success().withResult(opBuilder.build()).build()); } else { result.set(RpcResultBuilder.success().build()); } } catch (Exception ex) { result.set(RpcResultBuilder.failed().withError(ErrorType.APPLICATION, formatAndLog(LOG::error, "associate Networks to vpn {} failed due to {}", input.getVpnId().getValue(), ex.getMessage(), ex)).build()); } LOG.debug("associateNetworks returns.."); return result; } /** * It handles the invocations to the neutronvpn:associateRouter RPC method. */ @Override public ListenableFuture> associateRouter(AssociateRouterInput input) { SettableFuture> result = SettableFuture.create(); LOG.debug("associateRouter {}", input); StringBuilder returnMsg = new StringBuilder(); Uuid vpnId = input.getVpnId(); List routerIds = input.getRouterIds(); Preconditions.checkArgument(!routerIds.isEmpty(), "associateRouter: RouterIds list is empty!"); Preconditions.checkNotNull(vpnId, "associateRouter; VpnId not found!"); Preconditions.checkNotNull(vpnId, "associateRouter; RouterIds not found!"); for (org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.associaterouter.input .RouterIds routerId : routerIds) { VpnMap vpnMap = neutronvpnUtils.getVpnMap(vpnId); Router rtr = neutronvpnUtils.getNeutronRouter(routerId.getRouterId()); if (vpnMap != null) { if (rtr != null) { Uuid extVpnId = neutronvpnUtils.getVpnForRouter(routerId.getRouterId(), true); if (vpnMap.getRouterIds() != null && vpnMap.getRouterIds().size() > 1) { returnMsg.append("vpn ").append(vpnId.getValue()).append(" already associated to router ") .append(routerId.getRouterId()); } else if (extVpnId != null) { returnMsg.append("router ").append(routerId.getRouterId()).append(" already associated to " + "another VPN ").append(extVpnId.getValue()); } else { LOG.debug("associateRouter RPC: VpnId {}, routerId {}", vpnId.getValue(), routerId.getRouterId()); associateRouterToVpn(vpnId, routerId.getRouterId()); } } else { returnMsg.append("router not found : ").append(routerId.getRouterId()); } } else { returnMsg.append("VPN not found : ").append(vpnId.getValue()); } if (returnMsg.length() != 0) { result.set(RpcResultBuilder.failed().withWarning(ErrorType.PROTOCOL, "invalid-value", formatAndLog(LOG::error, "associate router to vpn {} failed " + "due to {}", routerId.getRouterId(), returnMsg)).build()); } else { result.set(RpcResultBuilder.success(new AssociateRouterOutputBuilder().build()).build()); } } LOG.debug("associateRouter returns.."); return result; } /** * It handles the invocations to the neutronvpn:getFixedIPsForNeutronPort RPC method. */ @Override // TODO Clean up the exception handling @SuppressWarnings("checkstyle:IllegalCatch") public ListenableFuture> getFixedIPsForNeutronPort( GetFixedIPsForNeutronPortInput input) { GetFixedIPsForNeutronPortOutputBuilder opBuilder = new GetFixedIPsForNeutronPortOutputBuilder(); SettableFuture> result = SettableFuture.create(); Uuid portId = input.getPortId(); StringBuilder returnMsg = new StringBuilder(); try { List fixedIPList = new ArrayList<>(); Port port = neutronvpnUtils.getNeutronPort(portId); if (port != null) { for (FixedIps ip : port.nonnullFixedIps()) { fixedIPList.add(ip.getIpAddress().stringValue()); } } else { returnMsg.append("neutron port: ").append(portId.getValue()).append(" not found"); } if (returnMsg.length() != 0) { result.set(RpcResultBuilder.failed().withWarning(ErrorType.PROTOCOL, "invalid-value", formatAndLog(LOG::error, "Retrieval of FixedIPList for neutron port failed due to {}", returnMsg)).build()); } else { opBuilder.setFixedIPs(fixedIPList); result.set(RpcResultBuilder.success().withResult(opBuilder.build()) .build()); result.set(RpcResultBuilder.success().build()); } } catch (Exception ex) { result.set(RpcResultBuilder.failed().withError(ErrorType.APPLICATION, formatAndLog(LOG::error, "Retrieval of FixedIPList for neutron port {} failed due to {}", portId.getValue(), ex.getMessage(), ex)).build()); } return result; } /** * It handles the invocations to the neutronvpn:dissociateNetworks RPC method. */ @Override // TODO Clean up the exception handling @SuppressWarnings("checkstyle:IllegalCatch") public ListenableFuture> dissociateNetworks(DissociateNetworksInput input) { DissociateNetworksOutputBuilder opBuilder = new DissociateNetworksOutputBuilder(); SettableFuture> result = SettableFuture.create(); LOG.debug("dissociateNetworks {}", input); StringBuilder returnMsg = new StringBuilder(); Uuid vpnId = input.getVpnId(); try { if (neutronvpnUtils.getVpnMap(vpnId) != null) { LOG.debug("dissociateNetworks RPC: VpnId {}, networkList {}", vpnId.getValue(), input.getNetworkId().toString()); List netIds = input.getNetworkId(); if (netIds != null && !netIds.isEmpty()) { List failed = dissociateNetworksFromVpn(vpnId, netIds); if (!failed.isEmpty()) { returnMsg.append(failed); } } } else { returnMsg.append("VPN not found : ").append(vpnId.getValue()); } if (returnMsg.length() != 0) { opBuilder.setResponse( "ErrorType: PROTOCOL, ErrorTag: invalid-value, ErrorMessage: " + formatAndLog(LOG::error, "dissociate Networks to vpn {} failed due to {}", vpnId.getValue(), returnMsg)); result.set(RpcResultBuilder.success().withResult(opBuilder.build()).build()); } else { result.set(RpcResultBuilder.success().build()); } } catch (Exception ex) { result.set(RpcResultBuilder.failed().withError(ErrorType.APPLICATION, formatAndLog(LOG::error, "dissociate Networks to vpn {} failed due to {}", input.getVpnId().getValue(), ex.getMessage(), ex)).build()); } LOG.debug("dissociateNetworks returns.."); return result; } /** * It handles the invocations to the neutronvpn:dissociateRouter RPC method. */ @Override // TODO Clean up the exception handling @SuppressWarnings("checkstyle:IllegalCatch") public ListenableFuture> dissociateRouter(DissociateRouterInput input) { SettableFuture> result = SettableFuture.create(); LOG.debug("dissociateRouter {}", input); StringBuilder returnMsg = new StringBuilder(); Uuid vpnId = input.getVpnId(); List routerIdList = input.getRouterIds(); String routerIdsString = ""; Preconditions.checkArgument(!routerIdList.isEmpty(), "dissociateRouter: RouterIds list is empty!"); Preconditions.checkNotNull(vpnId, "dissociateRouter: vpnId not found!"); Preconditions.checkNotNull(routerIdList, "dissociateRouter: routerIdList not found!"); if (neutronvpnUtils.getVpnMap(vpnId) != null) { for (org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.dissociaterouter.input .RouterIds routerId : routerIdList) { try { if (routerId != null) { routerIdsString += routerId.getRouterId() + ", "; Router rtr = neutronvpnUtils.getNeutronRouter(routerId.getRouterId()); if (rtr != null) { Uuid routerVpnId = neutronvpnUtils.getVpnForRouter(routerId.getRouterId(), true); if (routerVpnId == null) { returnMsg.append("input router ").append(routerId.getRouterId()) .append(" not associated to any vpn yet"); } else if (vpnId.equals(routerVpnId)) { dissociateRouterFromVpn(vpnId, routerId.getRouterId()); } else { returnMsg.append("input router ").append(routerId.getRouterId()) .append(" associated to vpn ") .append(routerVpnId.getValue()).append("instead of the vpn given as input"); } } else { returnMsg.append("router not found : ").append(routerId.getRouterId()); } } if (returnMsg.length() != 0) { result.set(RpcResultBuilder.failed().withWarning(ErrorType.PROTOCOL, "invalid-value", formatAndLog(LOG::error, "dissociate router {} to " + "vpn {} failed due to {}", routerId.getRouterId(), vpnId.getValue(), returnMsg)).build()); } else { result.set(RpcResultBuilder.success(new DissociateRouterOutputBuilder().build()).build()); } } catch (Exception ex) { result.set(RpcResultBuilder.failed().withError(ErrorType.APPLICATION, formatAndLog(LOG::error, "disssociate router {} to vpn {} failed due to {}", routerId.getRouterId(), vpnId.getValue(), ex.getMessage(), ex)).build()); } } } else { returnMsg.append("VPN not found : ").append(vpnId.getValue()); } LOG.debug("dissociateRouter returns.."); return result; } protected void handleNeutronRouterDeleted(Uuid routerId, List routerSubnetIds) { // check if the router is associated to some VPN Uuid vpnId = neutronvpnUtils.getVpnForRouter(routerId, true); Uuid internetVpnId = neutronvpnUtils.getInternetvpnUuidBoundToRouterId(routerId); if (vpnId != null) { // remove existing external vpn interfaces for (Uuid subnetId : routerSubnetIds) { removeSubnetFromVpn(vpnId, subnetId, internetVpnId); } clearFromVpnMaps(vpnId, routerId, null); } else { // remove existing internal vpn interfaces for (Uuid subnetId : routerSubnetIds) { removeSubnetFromVpn(routerId, subnetId, internetVpnId); } } // delete entire vpnMaps node for internal VPN deleteVpnMapsNode(routerId); // delete vpn-instance for internal VPN deleteVpnInstance(routerId); } protected Subnet getNeutronSubnet(Uuid subnetId) { return neutronvpnUtils.getNeutronSubnet(subnetId); } @Nullable protected IpAddress getNeutronSubnetGateway(Uuid subnetId) { Subnet sn = neutronvpnUtils.getNeutronSubnet(subnetId); if (null != sn) { return sn.getGatewayIp(); } return null; } protected Network getNeutronNetwork(Uuid networkId) { return neutronvpnUtils.getNeutronNetwork(networkId); } protected Port getNeutronPort(String name) { return neutronvpnUtils.getNeutronPort(new Uuid(name)); } protected Port getNeutronPort(Uuid portId) { return neutronvpnUtils.getNeutronPort(portId); } protected Uuid getNetworkForSubnet(Uuid subnetId) { return neutronvpnUtils.getNetworkForSubnet(subnetId); } protected List getNetworksForVpn(Uuid vpnId) { return neutronvpnUtils.getNetworksForVpn(vpnId); } /** * Implementation of the "vpnservice:neutron-ports-show" Karaf CLI command. * * @return a List of String to be printed on screen * @throws ReadFailedException if there was a problem reading from the data store */ public List showNeutronPortsCLI() throws ReadFailedException { List result = new ArrayList<>(); result.add(String.format(" %-36s %-19s %-13s %-20s ", "Port ID", "Mac Address", "Prefix Length", "IP Address")); result.add("-------------------------------------------------------------------------------------------"); InstanceIdentifier portidentifier = InstanceIdentifier.create(Neutron.class).child(Ports.class); Optional ports = syncReadOptional(dataBroker, LogicalDatastoreType.CONFIGURATION, portidentifier); if (ports.isPresent() && ports.get().getPort() != null) { for (Port port : ports.get().nonnullPort()) { List fixedIPs = port.getFixedIps(); if (fixedIPs != null && !fixedIPs.isEmpty()) { List ipList = new ArrayList<>(); for (FixedIps fixedIp : fixedIPs) { IpAddress ipAddress = fixedIp.getIpAddress(); if (ipAddress.getIpv4Address() != null) { ipList.add(ipAddress.getIpv4Address().getValue()); } else { ipList.add(ipAddress.getIpv6Address().getValue()); } } result.add(String.format(" %-36s %-19s %-13s %-20s ", port.getUuid().getValue(), port .getMacAddress().getValue(), neutronvpnUtils.getIPPrefixFromPort(port), ipList.toString())); } else { result.add(String.format(" %-36s %-19s %-13s %-20s ", port.getUuid().getValue(), port .getMacAddress().getValue(), "Not Assigned", "Not Assigned")); } } } return result; } /** * Implementation of the "vpnservice:l3vpn-config-show" karaf CLI command. * * @param vpnuuid Uuid of the VPN whose config must be shown * @return formatted output list * @throws InterruptedException if there was a thread related problem getting the data to display * @throws ExecutionException if there was any other problem getting the data to display */ public List showVpnConfigCLI(Uuid vpnuuid) throws InterruptedException, ExecutionException { List result = new ArrayList<>(); if (vpnuuid == null) { result.add(""); result.add("Displaying VPN config for all VPNs"); result.add("To display VPN config for a particular VPN, use the following syntax"); result.add(getshowVpnConfigCLIHelp()); } RpcResult rpcResult = getL3VPN(new GetL3VPNInputBuilder().setId(vpnuuid).build()).get(); if (rpcResult.isSuccessful()) { result.add(""); result.add(String.format(" %-37s %-37s %-7s ", "VPN ID", "Tenant ID", "RD")); result.add(""); result.add(String.format(" %-80s ", "Import-RTs")); result.add(""); result.add(String.format(" %-80s ", "Export-RTs")); result.add(""); result.add(String.format(" %-76s ", "Subnet IDs")); result.add(""); result.add("------------------------------------------------------------------------------------"); result.add(""); for (org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.VpnInstance vpn : rpcResult.getResult().nonnullL3vpnInstances()) { String tenantId = vpn.getTenantId() != null ? vpn.getTenantId().getValue() : "\" " + " \""; result.add(String.format(" %-37s %-37s %-7s ", vpn.getId().getValue(), tenantId, vpn.getRouteDistinguisher())); result.add(""); result.add(String.format(" %-80s ", vpn.getImportRT())); result.add(""); result.add(String.format(" %-80s ", vpn.getExportRT())); result.add(""); Uuid vpnid = vpn.getId(); List subnetList = neutronvpnUtils.getSubnetsforVpn(vpnid); if (!subnetList.isEmpty()) { for (Uuid subnetuuid : subnetList) { result.add(String.format(" %-76s ", subnetuuid.getValue())); } } else { result.add(String.format(" %-76s ", "\" \"")); } result.add(""); result.add("----------------------------------------"); result.add(""); } } else { String errortag = rpcResult.getErrors().iterator().next().getTag(); if (Objects.equals(errortag, "")) { result.add(""); result.add("No VPN has been configured yet"); } else if (Objects.equals(errortag, "invalid-value")) { result.add(""); result.add("VPN " + vpnuuid.getValue() + " is not present"); } else { result.add("error getting VPN info : " + rpcResult.getErrors()); result.add(getshowVpnConfigCLIHelp()); } } return result; } protected void createExternalVpnInterfaces(Uuid extNetId) { if (extNetId == null) { LOG.error("createExternalVpnInterfaces: external network is null"); return; } Collection extElanInterfaces = elanService.getExternalElanInterfaces(extNetId.getValue()); if (extElanInterfaces == null || extElanInterfaces.isEmpty()) { LOG.error("No external ports attached to external network {}", extNetId.getValue()); return; } ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> { for (String elanInterface : extElanInterfaces) { createExternalVpnInterface(extNetId, elanInterface, tx); } }), LOG, "Error creating external VPN interfaces for {}", extNetId); } // TODO Clean up the exception handling @SuppressWarnings("checkstyle:IllegalCatch") protected void removeExternalVpnInterfaces(Uuid extNetId) { Collection extElanInterfaces = elanService.getExternalElanInterfaces(extNetId.getValue()); if (extElanInterfaces == null || extElanInterfaces.isEmpty()) { LOG.error("No external ports attached for external network {}", extNetId.getValue()); return; } ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> { for (String elanInterface : extElanInterfaces) { InstanceIdentifier vpnIfIdentifier = NeutronvpnUtils .buildVpnInterfaceIdentifier(elanInterface); LOG.info("Removing vpn interface {}", elanInterface); tx.delete(vpnIfIdentifier); } }), LOG, "Error removing external VPN interfaces for {}", extNetId); } private void createExternalVpnInterface(Uuid vpnId, String infName, TypedWriteTransaction wrtConfigTxn) { writeVpnInterfaceToDs(Collections.singletonList(vpnId), infName, null, vpnId /* external network id */, false /* not a router iface */, wrtConfigTxn); } // TODO Clean up the exception handling @SuppressWarnings("checkstyle:IllegalCatch") private void writeVpnInterfaceToDs(@Nonnull Collection vpnIdList, String infName, @Nullable Adjacencies adjacencies, Uuid networkUuid, Boolean isRouterInterface, TypedWriteTransaction wrtConfigTxn) { if (vpnIdList.isEmpty() || infName == null) { LOG.error("vpnid is empty or interface({}) is null", infName); return; } if (wrtConfigTxn == null) { ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> writeVpnInterfaceToDs(vpnIdList, infName, adjacencies, networkUuid, isRouterInterface, tx)), LOG, "Error writing VPN interface"); return; } List vpnIdListStruct = new ArrayList<>(); for (Uuid vpnId: vpnIdList) { VpnInstanceNames vpnInstance = VpnHelper.getVpnInterfaceVpnInstanceNames(vpnId.getValue(), AssociatedSubnetType.V4AndV6Subnets); vpnIdListStruct.add(vpnInstance); } InstanceIdentifier vpnIfIdentifier = NeutronvpnUtils.buildVpnInterfaceIdentifier(infName); VpnInterfaceBuilder vpnb = new VpnInterfaceBuilder().withKey(new VpnInterfaceKey(infName)) .setName(infName) .setVpnInstanceNames(vpnIdListStruct) .setRouterInterface(isRouterInterface); LOG.info("Network Id is {}", networkUuid); if (networkUuid != null) { Network portNetwork = neutronvpnUtils.getNeutronNetwork(networkUuid); ProviderTypes providerType = NeutronvpnUtils.getProviderNetworkType(portNetwork); NetworkAttributes.NetworkType networkType = providerType != null ? NetworkAttributes.NetworkType.valueOf(providerType.getName()) : null; String segmentationId = NeutronvpnUtils.getSegmentationIdFromNeutronNetwork(portNetwork); vpnb.setNetworkId(networkUuid).setNetworkType(networkType) .setSegmentationId(segmentationId != null ? Long.parseLong(segmentationId) : 0L); } if (adjacencies != null) { vpnb.addAugmentation(Adjacencies.class, adjacencies); } VpnInterface vpnIf = vpnb.build(); try { LOG.info("Creating vpn interface {}", vpnIf); wrtConfigTxn.put(vpnIfIdentifier, vpnIf); } catch (Exception ex) { LOG.error("Creation of vpninterface {} failed", infName, ex); } } private void updateVpnInterfaceWithAdjacencies(Uuid vpnId, String infName, Adjacencies adjacencies, TypedWriteTransaction wrtConfigTxn) { if (vpnId == null || infName == null) { LOG.error("vpn id or interface is null"); return; } if (wrtConfigTxn == null) { LOG.error("updateVpnInterfaceWithAdjancies called with wrtConfigTxn as null"); ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> { updateVpnInterfaceWithAdjacencies(vpnId, infName, adjacencies, tx); }), LOG, "Error updating VPN interface with adjacencies"); return; } InstanceIdentifier vpnIfIdentifier = NeutronvpnUtils.buildVpnInterfaceIdentifier(infName); boolean isLockAcquired = false; try { isLockAcquired = interfaceLock.tryLock(infName, LOCK_WAIT_TIME, TimeUnit.SECONDS); Optional optionalVpnInterface = SingleTransactionDataBroker .syncReadOptional(dataBroker, LogicalDatastoreType .CONFIGURATION, vpnIfIdentifier); if (optionalVpnInterface.isPresent()) { VpnInterfaceBuilder vpnIfBuilder = new VpnInterfaceBuilder(optionalVpnInterface.get()); LOG.debug("Updating vpn interface {} with new adjacencies", infName); if (adjacencies == null) { if (isLockAcquired) { interfaceLock.unlock(infName); } return; } vpnIfBuilder.addAugmentation(Adjacencies.class, adjacencies); if (optionalVpnInterface.get().getVpnInstanceNames() != null) { List listVpnInstances = new ArrayList<>( optionalVpnInterface.get().getVpnInstanceNames()); if (listVpnInstances.isEmpty() || !VpnHelper .doesVpnInterfaceBelongToVpnInstance(vpnId.getValue(), listVpnInstances)) { VpnInstanceNames vpnInstance = VpnHelper .getVpnInterfaceVpnInstanceNames(vpnId.getValue(), AssociatedSubnetType.V4AndV6Subnets); listVpnInstances.add(vpnInstance); vpnIfBuilder.setVpnInstanceNames(listVpnInstances); } } else { VpnInstanceNames vpnInstance = VpnHelper .getVpnInterfaceVpnInstanceNames(vpnId.getValue(), AssociatedSubnetType.V4AndV6Subnets); List listVpnInstances = new ArrayList<>(); listVpnInstances.add(vpnInstance); vpnIfBuilder.setVpnInstanceNames(listVpnInstances); } LOG.info("Updating vpn interface {} with new adjacencies", infName); wrtConfigTxn.put(vpnIfIdentifier, vpnIfBuilder.build()); } } catch (IllegalStateException | ReadFailedException ex) { LOG.error("Update of vpninterface {} failed", infName, ex); } finally { if (isLockAcquired) { interfaceLock.unlock(infName); } } } private String getshowVpnConfigCLIHelp() { StringBuilder help = new StringBuilder("Usage:"); help.append("display vpn-config [-vid/--vpnid ]"); return help.toString(); } protected void dissociatefixedIPFromFloatingIP(String fixedNeutronPortName) { floatingIpMapListener.dissociatefixedIPFromFloatingIP(fixedNeutronPortName); } @Override public ListenableFuture> createEVPN(CreateEVPNInput input) { return neutronEvpnManager.createEVPN(input); } @Override public ListenableFuture> getEVPN(GetEVPNInput input) { return neutronEvpnManager.getEVPN(input); } @Override public ListenableFuture> deleteEVPN(DeleteEVPNInput input) { return neutronEvpnManager.deleteEVPN(input); } private boolean addExternalNetworkToVpn(Network extNet, Uuid vpnId) { Uuid extNetId = extNet.getUuid(); InstanceIdentifier extNetIdentifier = InstanceIdentifier.builder(ExternalNetworks.class) .child(Networks.class, new NetworksKey(extNetId)).build(); try { Optional optionalExtNets = SingleTransactionDataBroker.syncReadOptional(dataBroker, LogicalDatastoreType.CONFIGURATION, extNetIdentifier); if (!optionalExtNets.isPresent()) { LOG.error("addExternalNetworkToVpn: Provider Network {} is not present in ConfigDS", extNetId.getValue()); return false; } NetworksBuilder builder = new NetworksBuilder(optionalExtNets.get()); builder.setVpnid(vpnId); Networks networks = builder.build(); // Add Networks object to the ExternalNetworks list LOG.trace("addExternalNetworkToVpn: Set VPN Id {} for Provider Network {}", vpnId.getValue(), extNetId.getValue()); SingleTransactionDataBroker.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION, extNetIdentifier, networks); return true; } catch (TransactionCommitFailedException | ReadFailedException ex) { LOG.error("addExternalNetworkToVpn: Failed to set VPN Id {} to Provider Network {}: ", vpnId.getValue(), extNetId.getValue(), ex); } return false; } private boolean removeExternalNetworkFromVpn(Network extNet) { Uuid extNetId = extNet.getUuid(); InstanceIdentifier extNetsId = InstanceIdentifier.builder(ExternalNetworks.class) .child(Networks.class, new NetworksKey(extNetId)).build(); try { Optional optionalNets = SingleTransactionDataBroker.syncReadOptional(dataBroker, LogicalDatastoreType.CONFIGURATION, extNetsId); NetworksBuilder builder = null; if (optionalNets.isPresent()) { builder = new NetworksBuilder(optionalNets.get()); } else { LOG.error("removeExternalNetworkFromVpn: Provider Network {} is not present in the ConfigDS", extNetId.getValue()); return false; } builder.setVpnid(null); Networks networks = builder.build(); LOG.info("removeExternalNetworkFromVpn: Withdraw VPN Id from Provider Network {} node", extNetId.getValue()); SingleTransactionDataBroker.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION, extNetsId, networks); return true; } catch (TransactionCommitFailedException | ReadFailedException ex) { LOG.error("removeExternalNetworkFromVpn: Failed to withdraw VPN Id from Provider Network node {}: ", extNetId.getValue(), ex); } return false; } private Optional getExistingOperationalVpn(String primaryRd) { Optional existingVpnName = Optional.of(primaryRd); Optional vpnInstanceOpDataOptional; try { vpnInstanceOpDataOptional = SingleTransactionDataBroker .syncReadOptional(dataBroker, LogicalDatastoreType.OPERATIONAL, neutronvpnUtils.getVpnOpDataIdentifier(primaryRd)); } catch (ReadFailedException e) { LOG.error("getExistingOperationalVpn: Exception while checking operational status of vpn with rd {}", primaryRd, e); /*Read failed. We don't know if a VPN exists or not. * Return primaryRd to halt caller execution, to be safe.*/ return existingVpnName; } if (vpnInstanceOpDataOptional.isPresent()) { existingVpnName = Optional.of(vpnInstanceOpDataOptional.get().getVpnInstanceName()); } else { existingVpnName = Optional.absent(); } return existingVpnName; } private static String formatAndLog(Consumer logger, String template, Object arg) { return logAndReturnMessage(logger, MessageFormatter.format(template, arg)); } private static String formatAndLog(Consumer logger, String template, Object arg1, Object arg2) { return logAndReturnMessage(logger, MessageFormatter.format(template, arg1, arg2)); } private static String formatAndLog(Consumer logger, String template, Object... args) { return logAndReturnMessage(logger, MessageFormatter.arrayFormat(template, args)); } private static String logAndReturnMessage(Consumer logger, FormattingTuple tuple) { String message = tuple.getMessage(); logger.accept(message); return message; } protected void addV6PrivateSubnetToExtNetwork(@Nonnull Uuid routerId, @Nonnull Uuid internetVpnId, @Nonnull Subnetmap subnetMap) { updateVpnInternetForSubnet(subnetMap, internetVpnId, true); neutronvpnUtils.updateVpnInstanceWithFallback(routerId, internetVpnId, true); if (neutronvpnUtils.shouldVpnHandleIpVersionChoiceChange(IpVersionChoice.IPV6, routerId, true)) { neutronvpnUtils.updateVpnInstanceWithIpFamily(internetVpnId.getValue(), IpVersionChoice.IPV6, true); LOG.info("addV6PrivateSubnetToExtNetwork: Advertise IPv6 Private Subnet {} to Internet VPN {}", subnetMap.getId().getValue(), internetVpnId.getValue()); } } protected void removeV6PrivateSubnetToExtNetwork(@Nonnull Uuid routerId, @Nonnull Uuid internetVpnId, @Nonnull Subnetmap subnetMap) { updateVpnInternetForSubnet(subnetMap, internetVpnId, false); neutronvpnUtils.updateVpnInstanceWithFallback(routerId, internetVpnId, false); } protected void programV6InternetFallbackFlow(Uuid routerId, Uuid internetVpnId, int addOrRemove) { if (neutronvpnUtils.isV6SubnetPartOfRouter(routerId)) { LOG.debug("processV6InternetFlowsForRtr: Successfully {} V6 internet vpn {} default fallback rule " + "for the router {}", addOrRemove == NwConstants.ADD_FLOW ? "added" : "removed", internetVpnId.getValue(), routerId.getValue()); neutronvpnUtils.updateVpnInstanceWithFallback(routerId, internetVpnId, addOrRemove == NwConstants.ADD_FLOW ? true : false); } } private static ReentrantLock lockForUuid(Uuid uuid) { // FIXME: prove that this locks only on Uuids and not some other entity or create a separate lock domain return JvmGlobalLocks.getLockForString(uuid.getValue()); } }