Java tutorial
/* * Copyright (c) 2016 Cisco Systems, Inc. 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.vbd.impl; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.concurrent.ExecutionException; import java.util.stream.Collectors; import javax.annotation.Nullable; import org.opendaylight.controller.md.sal.binding.api.DataBroker; import org.opendaylight.controller.md.sal.binding.api.MountPointService; import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction; import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException; import org.opendaylight.vbd.impl.transaction.VbdNetconfTransaction; 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.inet.types.rev130715.Ipv4AddressNoZone; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.Interfaces; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.InterfacesState; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.InterfaceBuilder; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.InterfaceKey; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.Interface2; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces.state._interface.Ipv4; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces.state._interface.ipv4.Address; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.external.reference.rev160129.ExternalReference; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.Loopback; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.Vpp; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.VppInterfaceAugmentation; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.VppInterfaceAugmentationBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.VxlanTunnel; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.VxlanVni; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces._interface.L2; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces._interface.L2Builder; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces._interface.Vxlan; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces._interface.VxlanBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.l2.base.attributes.Interconnection; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.l2.base.attributes.interconnection.BridgeBased; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.l2.base.attributes.interconnection.BridgeBasedBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.vpp.BridgeDomains; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.vpp.bridge.domains.BridgeDomain; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.vpp.bridge.domains.BridgeDomainBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.vpp.bridge.domains.BridgeDomainKey; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vbridge.topology.rev160129.TerminationPointVbridgeAugment; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vbridge.topology.rev160129.TopologyVbridgeAugment; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vbridge.topology.rev160129.TunnelType; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vbridge.topology.rev160129.network.topology.topology.TunnelParameters; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vbridge.topology.rev160129.network.topology.topology.node.termination.point.InterfaceType; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vbridge.topology.rev160129.network.topology.topology.node.termination.point._interface.type.UserInterface; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vbridge.tunnel.vxlan.rev160429.TunnelTypeVxlan; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vbridge.tunnel.vxlan.rev160429.network.topology.topology.tunnel.parameters.VxlanTunnelParameters; import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology; import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TopologyId; import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology; import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey; import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node; import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeKey; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.base.Optional; import com.google.common.base.Preconditions; import com.google.common.util.concurrent.CheckedFuture; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.SettableFuture; /** * Class which is used for manipulation with VPP */ final class VppModifier { private static final Long DEFAULT_ENCAP_VRF_ID = 0L; private static final Short DEFAULT_SHG = 1; private static final Logger LOG = LoggerFactory.getLogger(VppModifier.class); private final DataBroker dataBroker; private final MountPointService mountService; private final String bridgeDomainName; private final VbdBridgeDomain vbdBridgeDomain; private TopologyVbridgeAugment config; private final InstanceIdentifier<BridgeDomain> iiBridgeDomainOnVPP; private Set<NodeKey> nodesWithBridgeDomain = new HashSet<>(); VppModifier(final DataBroker dataBroker, final MountPointService mountService, final String bridgeDomainName, final VbdBridgeDomain vbdBridgeDomain) { this.dataBroker = Preconditions.checkNotNull(dataBroker); this.mountService = Preconditions.checkNotNull(mountService); this.vbdBridgeDomain = Preconditions.checkNotNull(vbdBridgeDomain); this.bridgeDomainName = Preconditions.checkNotNull(bridgeDomainName); this.iiBridgeDomainOnVPP = InstanceIdentifier.create(Vpp.class).child(BridgeDomains.class) .child(BridgeDomain.class, new BridgeDomainKey(bridgeDomainName)); } ListenableFuture<Void> deleteBridgeDomainFromVppNode(final KeyedInstanceIdentifier<Node, NodeKey> iiToVpp) { if (!nodesWithBridgeDomain.contains(iiToVpp.getKey())) { LOG.debug("Bridge domain {} not present on vpp node {} skipping...", vbdBridgeDomain, iiToVpp.getKey().getNodeId()); return Futures.immediateFuture(null); } final DataBroker vppDataBroker = VbdUtil.resolveDataBrokerForMountPoint(iiToVpp, mountService); if (vppDataBroker == null) { LOG.warn("Got null data broker when attempting to delete bridge domain {}", bridgeDomainName); return Futures.immediateFuture(null); } final boolean transactionState = VbdNetconfTransaction.deleteIfExists(vppDataBroker, this.iiBridgeDomainOnVPP, VbdNetconfTransaction.RETRY_COUNT); if (transactionState) { LOG.debug("Successfully deleted bridge domain {} from node {}", bridgeDomainName, PPrint.node(iiToVpp)); nodesWithBridgeDomain.remove(iiToVpp.getKey()); } else { LOG.warn("Failed to delete bridge domain {} from node {}", bridgeDomainName, PPrint.node(iiToVpp)); } return Futures.immediateFuture(null); } private void deleteSupportingInterfaces(final KeyedInstanceIdentifier<Node, NodeKey> iiToVpp, final DataBroker vppDataBroker) { LOG.debug("Deleting interfaces supporting bridge domain {} on node {}", bridgeDomainName, PPrint.node(iiToVpp)); final List<Interface> allInterfaces = getInterfacesFromVpp(vppDataBroker); final List<Interface> supportingInterfaces = getInterfacesSupportingBridgeDomain(iiToVpp, allInterfaces); LOG.debug("There are {} interfaces supporting bridge domain {} on node {}", supportingInterfaces.size(), bridgeDomainName, PPrint.node(iiToVpp)); if (supportingInterfaces.isEmpty()) { LOG.debug( "No interfaces supporting bridge domain {} on node {}. This is expected if this is the last node " + "to be deleted when the bridge domain is shutting down", bridgeDomainName, PPrint.node(iiToVpp)); return; } for (final Interface intf : supportingInterfaces) { deleteInterface(iiToVpp, intf, vppDataBroker); } final Optional<IpAddress> ipOp = getVtepAddress(iiToVpp, supportingInterfaces); if (ipOp.isPresent()) { deletePeerInterfaces(iiToVpp, ipOp.get()); } else { LOG.warn( "Unable to determine VTEP address for node {} in bridge domain {}. VXLAN tunnels facing this node cannot be deleted!", PPrint.node(iiToVpp), bridgeDomainName); } } private void deleteInterface(final KeyedInstanceIdentifier<Node, NodeKey> iiToVpp, final Interface intf, final DataBroker vppDataBroker) { LOG.debug("Deleting interface {} from config DS on vpp {}, it was supporting bridge domain {}", intf.getName(), PPrint.node(iiToVpp), bridgeDomainName); final KeyedInstanceIdentifier<Interface, InterfaceKey> iiToIntf = InstanceIdentifier .create(Interfaces.class).child(Interface.class, intf.getKey()); final boolean transactionState = VbdNetconfTransaction.deleteIfExists(vppDataBroker, iiToIntf, VbdNetconfTransaction.RETRY_COUNT); if (transactionState) { LOG.debug("Successfully deleted interface {}, which was supporting bridge domain {} on node {}", intf.getName(), bridgeDomainName, PPrint.node(iiToVpp)); } else { LOG.warn("Failed to delete interface {}, which was supporting bridge domain {} on node {}", intf.getName(), bridgeDomainName, PPrint.node(iiToVpp)); } } /** * Find the src for the vxlan tunnels for interfaces supporting the deleted node, and assume that is going to * be the dst address for peer interfaces which should be deleted. */ private Optional<IpAddress> getVtepAddress(final KeyedInstanceIdentifier<Node, NodeKey> iiToVpp, final List<Interface> interfaces) { LOG.debug("Finding VTEP address for node {} on bridge domain {}", PPrint.node(iiToVpp), bridgeDomainName); Optional<IpAddress> vtepAddress = Optional.absent(); for (final Interface intf : interfaces) { final VppInterfaceAugmentation intfAug = intf.getAugmentation(VppInterfaceAugmentation.class); if (intfAug == null) { continue; } final IpAddress tunnelSrcAddr = intfAug.getVxlan().getSrc(); if (vtepAddress.isPresent()) { if (!tunnelSrcAddr.equals(vtepAddress.get())) { LOG.warn("Got differing tunnel src addresses for interfaces which are supporting bridge domain " + "{} on node {}. Going to assume the first one encountered ({}) is the real VTEP address. Differing address is {}", bridgeDomainName, PPrint.node(iiToVpp), String.valueOf(vtepAddress.get().getValue()), String.valueOf(tunnelSrcAddr.getValue())); } } else { vtepAddress = Optional.of(tunnelSrcAddr); } } final String vtepIpStr = vtepAddress.isPresent() ? String.valueOf(vtepAddress.get().getValue()) : "UNKNOWN"; LOG.debug("VTEP address for node {} on bridge domain {} is {}", PPrint.node(iiToVpp), bridgeDomainName, vtepIpStr); return vtepAddress; } private void deletePeerInterfaces(final KeyedInstanceIdentifier<Node, NodeKey> iiToVpp, final IpAddress deletedNodeAddress) { LOG.debug("Deleting peer interfaces for node {} in bridge domain {}. Its VTEP address is {}", PPrint.node(iiToVpp), bridgeDomainName, String.valueOf(deletedNodeAddress.getValue())); final List<KeyedInstanceIdentifier<Node, NodeKey>> peerIIDs = vbdBridgeDomain.getNodePeersByIID(iiToVpp); for (final KeyedInstanceIdentifier<Node, NodeKey> peerIID : peerIIDs) { final DataBroker peerDataBroker = VbdUtil.resolveDataBrokerForMountPoint(peerIID, mountService); if (peerDataBroker == null) { LOG.warn( "Got null data broker for peer node {}, will not be able to remove VXLAN tunnel interfaces on this node!", PPrint.node(peerIID)); continue; } getInterfacesFromVpp(peerDataBroker).stream() .filter(peerIntf -> doesInterfacePointTowardAddress(peerIntf, deletedNodeAddress)) .forEach(peerIntf -> deleteInterface(peerIID, peerIntf, peerDataBroker)); } } private boolean doesInterfacePointTowardAddress(final Interface intf, final IpAddress address) { final VppInterfaceAugmentation intfAug = intf.getAugmentation(VppInterfaceAugmentation.class); if (intfAug == null) { return false; } final Vxlan vxlan = intfAug.getVxlan(); if (vxlan == null) { return false; } final IpAddress dst = vxlan.getDst(); return dst != null && dst.equals(address); } private List<Interface> getInterfacesFromVpp(final DataBroker vppDataBroker) { // read interfaces from config DS final Optional<Interfaces> interfacesOptional = VbdNetconfTransaction.read(vppDataBroker, LogicalDatastoreType.CONFIGURATION, InstanceIdentifier.create(Interfaces.class), VbdNetconfTransaction.RETRY_COUNT); if (interfacesOptional.isPresent()) { return interfacesOptional.get().getInterface(); } return Collections.emptyList(); } private List<Interface> getInterfacesSupportingBridgeDomain( final KeyedInstanceIdentifier<Node, NodeKey> iiToVpp, final List<Interface> interfaces) { LOG.debug("Finding interfaces supporting bridge domain {} on node {}", this.bridgeDomainName, PPrint.node(iiToVpp)); return interfaces.stream().filter(this::isInterfaceSupportingBD).collect(Collectors.toList()); } /** * Tries to read ipv4 addresses from all specified {@code iidToVpps } vpps. * * @param iiToVpps collection of instance identifiers which points to concrete mount points. * @return future which contains list of ip addresses in the same order as was specified in {@code iiToVpps} */ @SafeVarargs final List<Optional<Ipv4AddressNoZone>> readIpAddressesFromVpps( final KeyedInstanceIdentifier<Node, NodeKey>... iiToVpps) throws ExecutionException, InterruptedException { final List<Optional<Ipv4AddressNoZone>> ipv4Futures = new ArrayList<>(iiToVpps.length); for (final KeyedInstanceIdentifier<Node, NodeKey> iiToVpp : iiToVpps) { ipv4Futures.add(readIpAddressFromVpp(iiToVpp).get()); } return ipv4Futures; } /** * Passes through interfaces at mount point specified via {@code iiToVpp}. * * When first ipv4 address is found then it is returned. * * @param iiToVpp instance idenfifier which point to mounted vpp * @return if set ipv4 address is found at mounted vpp then it is returned as future. Otherwise absent value is returned * in future or exception which has been thrown */ private ListenableFuture<Optional<Ipv4AddressNoZone>> readIpAddressFromVpp( final KeyedInstanceIdentifier<Node, NodeKey> iiToVpp) { final SettableFuture<Optional<Ipv4AddressNoZone>> resultFuture = SettableFuture.create(); final DataBroker vppDataBroker = VbdUtil.resolveDataBrokerForMountPoint(iiToVpp, mountService); if (vppDataBroker != null) { final Optional<InterfacesState> opInterfaceState = VbdNetconfTransaction.read(vppDataBroker, LogicalDatastoreType.OPERATIONAL, InstanceIdentifier.create(InterfacesState.class), VbdNetconfTransaction.RETRY_COUNT); if (opInterfaceState.isPresent()) { for (org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface intf : opInterfaceState .get().getInterface()) { final Optional<Ipv4AddressNoZone> ipOp = readIpAddressFromInterface(intf, iiToVpp); if (ipOp.isPresent() && !intf.getType().equals(Loopback.class)) { resultFuture.set(ipOp); break; } } } else { LOG.debug("There appear to be no interfaces on node {}.", PPrint.node(iiToVpp)); } // if we got here, we were unable to successfully read an ip address from any of the node's interfaces resultFuture.set(Optional.absent()); } else { LOG.debug("Data broker for vpp {} is missing.", iiToVpp); } return resultFuture; } /** * Read the first available IPv4 address from the given interface. * * @param intf Interface to read an address from * @param iiToVpp Node which contains the given interface * @return An optional which is set to the IPv4 address which was read from the interface. If no IPv4 address could * be read from this interface, the optional will be absent. */ private Optional<Ipv4AddressNoZone> readIpAddressFromInterface( final org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface intf, final KeyedInstanceIdentifier<Node, NodeKey> iiToVpp) { final Interface2 augIntf = intf.getAugmentation(Interface2.class); if (augIntf == null) { LOG.debug("Cannot get Interface2 augmentation for intf {}", intf); return Optional.absent(); } final Ipv4 ipv4 = augIntf.getIpv4(); if (ipv4 == null) { LOG.debug("Ipv4 address for interface {} on node {} is null!", augIntf, PPrint.node(iiToVpp)); return Optional.absent(); } final List<Address> addresses = ipv4.getAddress(); if (addresses == null || addresses.isEmpty()) { LOG.debug("Ipv4 addresses list is empty for interface {} on node {}", augIntf, PPrint.node(iiToVpp)); return Optional.absent(); } final Ipv4AddressNoZone ip = addresses.iterator().next().getIp(); if (ip == null) { LOG.debug("Ipv4AddressNoZone is null for node {}", PPrint.node(iiToVpp)); return Optional.absent(); } LOG.debug("Got ip address {} from interface {} on node {}", ip.getValue(), intf.getName(), PPrint.node(iiToVpp)); return Optional.of(ip); } /** * Create virtual interface and add appropriate bridge domain. If interface already exists * (found by {@link VppModifier#findVxlanTunnelFromIpAddresses(Ipv4AddressNoZone, Ipv4AddressNoZone, DataBroker)}), * only bridge domain is leveraged from input data and written into interface. If interface is missing, it's written * with bridge domain set up. * * @param ipSrc source ip address * @param ipDst destination ip address * @param iidToVpp {@link InstanceIdentifier} to node * @param vxlanTunnelId input id of vxlan tunnel. This id is used only when particular interface does not exist */ ListenableFuture<Void> createVirtualInterfaceOnVpp(final Ipv4AddressNoZone ipSrc, final Ipv4AddressNoZone ipDst, final KeyedInstanceIdentifier<Node, NodeKey> iidToVpp, final Integer vxlanTunnelId) { final DataBroker vppDataBroker = VbdUtil.resolveDataBrokerForMountPoint(iidToVpp, mountService); if (vppDataBroker == null) { LOG.warn("Writing virtual interface {} to VPP {} wasn't successful because data broker is missing", VbdUtil.provideVxlanId(vxlanTunnelId), iidToVpp); return Futures.immediateFuture(null); } final Vxlan vxlanData = prepareVxlan(ipSrc, ipDst); final Integer potentialExistingInterfaceTunnelId = findVxlanTunnelFromIpAddresses(ipSrc, ipDst, vppDataBroker); // Interface exists, add BD only if (potentialExistingInterfaceTunnelId != null) { LOG.debug("Interface with srcIp {} and dstIp {} found, bridge domain will be added.", ipSrc, ipDst); final Interface interfaceData = prepareVirtualInterfaceData(vxlanData, vxlanTunnelId); final L2 l2Data = interfaceData.getAugmentation(VppInterfaceAugmentation.class).getL2(); LOG.trace("L2 data: {}", l2Data); final String vxlanId = VbdUtil.provideVxlanId(potentialExistingInterfaceTunnelId); final InstanceIdentifier<L2> l2Iid = InstanceIdentifier.create(Interfaces.class) .child(Interface.class, new InterfaceKey(vxlanId)).augmentation(VppInterfaceAugmentation.class) .child(L2.class).builder().build(); boolean transactionState = VbdNetconfTransaction.write(vppDataBroker, l2Iid, l2Data, VbdNetconfTransaction.RETRY_COUNT); if (transactionState) { LOG.debug("Writing bridge domain into super virtual interface to {} finished successfully.", PPrint.node(iidToVpp)); } else { LOG.warn("Writing bridge domain into super virtual interface to {} failed.", PPrint.node(iidToVpp)); } } // Interface does not exist, create a new one with BD assigned else { LOG.debug( "Interface with srcIp {} and dstIp {} not found, creating a new one with bridge domain attached.", ipSrc, ipDst); final Interface interfaceData = prepareVirtualInterfaceData(vxlanData, vxlanTunnelId); LOG.trace("Interface data: {}", interfaceData); final KeyedInstanceIdentifier<Interface, InterfaceKey> iidToInterface = InstanceIdentifier .create(Interfaces.class) .child(Interface.class, new InterfaceKey(VbdUtil.provideVxlanId(vxlanTunnelId))); LOG.debug("Submitting new interface to config store..."); boolean transactionState = VbdNetconfTransaction.write(vppDataBroker, iidToInterface, interfaceData, VbdNetconfTransaction.RETRY_COUNT); if (transactionState) { LOG.debug("Writing super virtual interface to {} finished successfully.", PPrint.node(iidToVpp)); } else { LOG.warn("Writing super virtual interface to {} failed.", PPrint.node(iidToVpp)); } } return Futures.immediateFuture(null); } /** * Get all interfaces from vpp, selects vxlan tunnels and finds the one with matching source ip address, destination * ip address and VNI. Returns vxlan tunnel index or null if such a interface is not present on node mountpoint * belongs to * * @param srcIp source ip address * @param dstIp destination ip address * @param mountpoint to access vpp router * @return vxlan tunnel id as an String, null otherwise */ @Nullable private Integer findVxlanTunnelFromIpAddresses(final Ipv4AddressNoZone srcIp, final Ipv4AddressNoZone dstIp, final DataBroker mountpoint) { final InstanceIdentifier<Interfaces> interfacesIid = InstanceIdentifier.create(Interfaces.class); final Optional<Interfaces> optionalInterfaces = VbdNetconfTransaction.read(mountpoint, LogicalDatastoreType.CONFIGURATION, interfacesIid, VbdNetconfTransaction.RETRY_COUNT); if (!optionalInterfaces.isPresent()) { LOG.warn("No interfaces exist for device {}", mountpoint); return null; } final Interfaces availableInterfaces = optionalInterfaces.get(); // Find vxlan tunnels final List<Interface> interfaces = availableInterfaces.getInterface(); if (interfaces == null || interfaces.isEmpty()) { LOG.warn("No vxlan tunnel is available for source ip: {} and destination ip: {}", srcIp, dstIp); return null; } for (Interface potentialVxlan : interfaces) { if (potentialVxlan.getType() != null && potentialVxlan.getType().equals(VxlanTunnel.class)) { // Get augmentation final VppInterfaceAugmentation augmentation = potentialVxlan .getAugmentation(VppInterfaceAugmentation.class); if (augmentation != null && augmentation.getVxlan() != null) { final Vxlan vxlan = augmentation.getVxlan(); // Resolve ip addresses, only Ipv4 addresses are supported final Ipv4AddressNoZone vxlanSrcIp = new Ipv4AddressNoZone(vxlan.getSrc().getIpv4Address()); final Ipv4AddressNoZone vxlanDstIp = new Ipv4AddressNoZone(vxlan.getDst().getIpv4Address()); final VxlanVni vni = vxlan.getVni(); if (srcIp.equals(vxlanSrcIp) && dstIp.equals(vxlanDstIp) && verifyVni(vni, vxlan)) { // Desired vxlan tunnel found, get tunnel id String vxlanName = potentialVxlan.getName(); // Replace any non-digit chars with empty space to get tunnel id return Integer.valueOf(vxlanName.replaceAll("\\D+", "")); } } } } LOG.debug("Vxlan tunnel was not found for src ip: {} and dst ip: {}", srcIp, dstIp); return null; } private boolean verifyVni(final VxlanVni providedVni, final Vxlan vxlan) { if (providedVni != null) { // Read current bridge domain final InstanceIdentifier<Topology> bridgeDomainIid = InstanceIdentifier.create(NetworkTopology.class) .child(Topology.class, new TopologyKey(new TopologyId(bridgeDomainName))).builder().build(); final ReadWriteTransaction rwTx = dataBroker.newReadWriteTransaction(); final CheckedFuture<Optional<Topology>, ReadFailedException> future = rwTx .read(LogicalDatastoreType.CONFIGURATION, bridgeDomainIid); try { final Optional<Topology> optionalTopology = future.get(); if (!optionalTopology.isPresent()) { // This shouldn't happen LOG.warn("Bridge domain {} is not present in datastore", bridgeDomainName); return false; } final Topology bridgeDomain = optionalTopology.get(); final TopologyVbridgeAugment augmentation = bridgeDomain .getAugmentation(TopologyVbridgeAugment.class); if (augmentation == null) { LOG.warn("Bridge domain {} does not contain Vbridge augmentation, vni cannot be verified", bridgeDomainName); return false; } final TunnelParameters parameters = augmentation.getTunnelParameters(); if (parameters instanceof VxlanTunnelParameters) { final VxlanTunnelParameters vxlanParameters = (VxlanTunnelParameters) parameters; final VxlanVni currentVni = vxlanParameters.getVni(); return providedVni.equals(currentVni); } return false; } catch (InterruptedException | ExecutionException e) { LOG.warn("Failed to read bridge domain from data store, vni cannot be verified for vxlan {}", vxlan); return false; } } LOG.warn("Cannot verify VNI for vxlan tunnel {}, provided vni is null", vxlan); return false; } private Interface prepareVirtualInterfaceData(final Vxlan vxlan, Integer vxlanTunnelId) { final InterfaceBuilder interfaceBuilder = new InterfaceBuilder(); //TODO implement tunnel counter interfaceBuilder.setName(VbdUtil.provideVxlanId(vxlanTunnelId)); interfaceBuilder.setType(VxlanTunnel.class); VppInterfaceAugmentationBuilder vppInterfaceAugmentationBuilder = new VppInterfaceAugmentationBuilder(); vppInterfaceAugmentationBuilder.setVxlan(vxlan); vppInterfaceAugmentationBuilder.setL2(prepareL2Data(false, DEFAULT_SHG)); interfaceBuilder.addAugmentation(VppInterfaceAugmentation.class, vppInterfaceAugmentationBuilder.build()); interfaceBuilder.setEnabled(true); return interfaceBuilder.build(); } private Vxlan prepareVxlan(final Ipv4AddressNoZone ipSrc, final Ipv4AddressNoZone ipDst) { final VxlanBuilder vxlanBuilder = new VxlanBuilder(); vxlanBuilder.setSrc(new IpAddress(ipSrc)); vxlanBuilder.setDst(new IpAddress(ipDst)); final TunnelParameters tunnelParameters = config.getTunnelParameters(); final Class<? extends TunnelType> tunnelType = config.getTunnelType(); if (tunnelType.equals(TunnelTypeVxlan.class)) { if (tunnelParameters instanceof VxlanTunnelParameters) { final VxlanTunnelParameters vxlanTunnelParams = (VxlanTunnelParameters) tunnelParameters; final VxlanVni vni = vxlanTunnelParams.getVni(); if (vni != null) { vxlanBuilder.setVni(vni); } else { LOG.warn( "Tunnel type is VXLAN but no VNI parameter was given! Creating new vxlan without VNI, result is undefined!"); } } else { LOG.warn("Tunnel type is vxlan but tunnel parameters are not for vxlan!?!?"); } } vxlanBuilder.setEncapVrfId(DEFAULT_ENCAP_VRF_ID); return vxlanBuilder.build(); } ListenableFuture<Void> addInterfaceToBridgeDomainOnVpp(final DataBroker vppDataBroker, final TerminationPointVbridgeAugment termPointVbridgeAug) { final InterfaceType interfaceType = termPointVbridgeAug.getInterfaceType(); if (interfaceType instanceof UserInterface) { //REMARK: according contract in YANG model this should be URI to data on mount point (according to RESTCONF) //It was much more easier to just await concrete interface name, thus isn't necessary parse it (splitting on '/') final ExternalReference userInterface = ((UserInterface) interfaceType).getUserInterface(); final KeyedInstanceIdentifier<Interface, InterfaceKey> iiToVpp = InstanceIdentifier .create(Interfaces.class).child(Interface.class, new InterfaceKey(userInterface.getValue())); InstanceIdentifier<L2> iiToV3poL2 = iiToVpp.augmentation(VppInterfaceAugmentation.class) .child(L2.class); LOG.debug("Writing L2 data to configuration DS to concrete interface."); VbdNetconfTransaction.write(vppDataBroker, iiToV3poL2, prepareL2Data(false, null), VbdNetconfTransaction.RETRY_COUNT); } return Futures.immediateFuture(null); } ListenableFuture<Void> addVppToBridgeDomain(final KeyedInstanceIdentifier<Node, NodeKey> iiToVpp) { final DataBroker vppDataBroker = VbdUtil.resolveDataBrokerForMountPoint(iiToVpp, mountService); if (vppDataBroker != null) { VbdNetconfTransaction.write(vppDataBroker, iiBridgeDomainOnVPP, prepareNewBridgeDomainData(), VbdNetconfTransaction.RETRY_COUNT); nodesWithBridgeDomain.add(iiToVpp.getKey()); return Futures.immediateFuture(null); } return Futures.immediateFailedFuture(new IllegalStateException("Data broker for vpp is missing")); } private BridgeDomain prepareNewBridgeDomainData() { final BridgeDomainBuilder bridgeDomainBuilder = new BridgeDomainBuilder(config); bridgeDomainBuilder.setName(bridgeDomainName); return bridgeDomainBuilder.build(); } /** * Prepare the L2 interface fields from the VPP interface augmentation in the v3po model. * * @param bridgedVirtualInterface value for the bridged-virtual-interface field * @return the built L2 object */ private L2 prepareL2Data(final boolean bridgedVirtualInterface, final Short splitHgrp) { final L2Builder l2Builder = new L2Builder(); final BridgeBasedBuilder bridgeBasedBuilder = new BridgeBasedBuilder(); bridgeBasedBuilder.setBridgedVirtualInterface(bridgedVirtualInterface); bridgeBasedBuilder.setBridgeDomain(bridgeDomainName); if (splitHgrp != null) { bridgeBasedBuilder.setSplitHorizonGroup(splitHgrp); } l2Builder.setInterconnection(bridgeBasedBuilder.build()); return l2Builder.build(); } public void setConfig(final TopologyVbridgeAugment config) { this.config = config; } private boolean isInterfaceSupportingBD(final Interface intf) { final VppInterfaceAugmentation vppInterface = intf.getAugmentation(VppInterfaceAugmentation.class); if (vppInterface == null) { return false; } final L2 l2Data = vppInterface.getL2(); if (l2Data == null) { return false; } final Interconnection interconnection = l2Data.getInterconnection(); if (!(interconnection instanceof BridgeBased)) { return false; } LOG.debug("Got bridge based VPP interface {}", intf.getName()); return VxlanTunnel.class.equals(intf.getType()) && this.bridgeDomainName.equals(((BridgeBased) interconnection).getBridgeDomain()); } ListenableFuture<Void> deleteVxlanInterface(final Ipv4AddressNoZone srcIp, final Ipv4AddressNoZone dstIp, final KeyedInstanceIdentifier<Node, NodeKey> vppNodeIid) { final DataBroker vppDataBroker = VbdUtil.resolveDataBrokerForMountPoint(vppNodeIid, mountService); if (vppDataBroker == null) { LOG.warn("Mountpoint not found for node {}", vppNodeIid); return Futures.immediateFuture(null); } final Integer tunnelId = findVxlanTunnelFromIpAddresses(srcIp, dstIp, vppDataBroker); if (tunnelId == null) { LOG.debug("Vxlan tunnel with source ip {}, destination ip {} and bridge domain {} not found on node {}", srcIp, dstIp, bridgeDomainName, vppNodeIid); return Futures.immediateFuture(null); } final String vxlanId = VbdUtil.provideVxlanId(tunnelId); final InstanceIdentifier<Interface> interfaceId = InstanceIdentifier.create(Interfaces.class) .child(Interface.class, new InterfaceKey(vxlanId)).builder().build(); LOG.debug("Removing bridge domain from vxlan {} on node {}", vxlanId, vppNodeIid); boolean transactionState = VbdNetconfTransaction.deleteIfExists(vppDataBroker, interfaceId, VbdNetconfTransaction.RETRY_COUNT); if (transactionState) { LOG.debug("Bridge domain successfully removed from vxlan interface on node {}", vppNodeIid); } else { LOG.warn("Failed to remove bridge domain from interface. Node: {}, cause: {}", vppNodeIid); } return Futures.immediateFuture(null); } }