/**
* 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 io.fd.honeycomb.vbd.impl;
import com.google.common.base.Optional;
import com.google.common.util.concurrent.CheckedFuture;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.SettableFuture;
import java.util.ArrayList;
import java.util.List;
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.ReadOnlyTransaction;
import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
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.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.Interface1;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces._interface.Ipv4;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces._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.rev150105.Vpp;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.VppInterfaceAugmentation;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.VppInterfaceAugmentationBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.VxlanTunnel;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces._interface.L2;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces._interface.L2Builder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces._interface.Vxlan;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces._interface.VxlanBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces._interface.l2.interconnection.BridgeBasedBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.vpp.BridgeDomains;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.vpp.bridge.domains.BridgeDomainBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.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.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.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;
/**
* Class which is used for manipulation with VPP
*/
public class VppModifier {
private static final Long DEFAULT_ENCAP_VRF_ID = 0L;
private static final Logger LOG = LoggerFactory.getLogger(BridgeDomain.class);
private final MountPointService mountService;
private final String bridgeDomainName;
private TopologyVbridgeAugment config;
private final InstanceIdentifier iiBridgeDomainOnVPP;
public VppModifier(final MountPointService mountService, final String bridgeDomainName) {
this.mountService = mountService;
this.bridgeDomainName = bridgeDomainName;
this.iiBridgeDomainOnVPP = InstanceIdentifier.create(Vpp.class)
.child(BridgeDomains.class)
.child(org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.vpp.bridge.domains.BridgeDomain.class, new BridgeDomainKey(bridgeDomainName));
}
/**
* Tryies to read ipv4 addresses from all specified {@code iiToVpps } vpps.
*
* @param iiToVpps collection of instance identifiers which points to concrete mount points.
* @return future which contains list of ip addreases in the same order as was specified in {@code iiToVpps}
*/
ListenableFuture>> readIpAddressesFromVpps(final KeyedInstanceIdentifier... iiToVpps) {
final List>> ipv4Futures = new ArrayList<>();
for (final KeyedInstanceIdentifier iiToVpp : iiToVpps) {
ipv4Futures.add(readIpAddressFromVpp(iiToVpp));
}
return Futures.successfulAsList(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> readIpAddressFromVpp(final KeyedInstanceIdentifier iiToVpp) {
final SettableFuture> resultFuture = SettableFuture.create();
final DataBroker vppDataBroker = VbdUtil.resolveDataBrokerForMountPoint(iiToVpp, mountService);
if (vppDataBroker != null) {
final ReadOnlyTransaction rTx = vppDataBroker.newReadOnlyTransaction();
final CheckedFuture, ReadFailedException> interfaceStateFuture
= rTx.read(LogicalDatastoreType.CONFIGURATION, InstanceIdentifier.create(Interfaces.class));
Futures.addCallback(interfaceStateFuture, new FutureCallback>() {
@Override
public void onSuccess(final Optional optInterfaces) {
if (optInterfaces.isPresent()) {
for (org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface intf : optInterfaces.get().getInterface()) {
final Interface1 interface1 = intf.getAugmentation(Interface1.class);
if (interface1 != null) {
final Ipv4 ipv4 = interface1.getIpv4();
if (ipv4 != null) {
final List addresses = ipv4.getAddress();
if (!addresses.isEmpty()) {
final Ipv4AddressNoZone ip = addresses.iterator().next().getIp();
if (ip != null) {
resultFuture.set(Optional.of(ip));
break;
}
}
}
}
}
} else {
LOG.debug("There is no inferface with ipv4 address set at VPP {}.", iiToVpp);
resultFuture.set(Optional.absent());
}
}
@Override
public void onFailure(Throwable t) {
resultFuture.setException(t);
}
});
} else {
LOG.debug("Data broker for vpp {} is missing.", iiToVpp);
resultFuture.set(Optional.absent());
}
return resultFuture;
}
void createVirtualInterfaceOnVpp(final Ipv4AddressNoZone ipSrc, final Ipv4AddressNoZone ipDst, final KeyedInstanceIdentifier iiToVpp) {
final Vxlan vxlanData = prepareVxlan(ipSrc, ipDst);
final Interface intfData = prepareVirtualInterfaceData(vxlanData);
final DataBroker vppDataBroker = VbdUtil.resolveDataBrokerForMountPoint(iiToVpp, mountService);
if (vppDataBroker != null) {
final WriteTransaction wTx = vppDataBroker.newWriteOnlyTransaction();
final KeyedInstanceIdentifier iiToInterface
= InstanceIdentifier.create(Interfaces.class).child(Interface.class, new InterfaceKey(BridgeDomain.TUNNEL_ID_DEMO));
wTx.put(LogicalDatastoreType.CONFIGURATION, iiToInterface, intfData);
final CheckedFuture submitFuture = wTx.submit();
Futures.addCallback(submitFuture, new FutureCallback() {
@Override
public void onSuccess(@Nullable Void result) {
LOG.debug("Writing super virtual interface to {} finished successfully.",iiToVpp.getKey().getNodeId());
}
@Override
public void onFailure(Throwable t) {
LOG.debug("Writing super virtual interface to {} failed.", iiToVpp.getKey().getNodeId());
}
});
} else {
LOG.debug("Writing virtual interface {} to VPP {} wasn't successfull because missing data broker.", BridgeDomain.TUNNEL_ID_DEMO, iiToVpp);
}
}
private Interface prepareVirtualInterfaceData(final Vxlan vxlan) {
final InterfaceBuilder interfaceBuilder = new InterfaceBuilder();
//TODO implement tunnel counter
interfaceBuilder.setName(BridgeDomain.TUNNEL_ID_DEMO);
interfaceBuilder.setType(VxlanTunnel.class);
VppInterfaceAugmentationBuilder vppInterfaceAugmentationBuilder = new VppInterfaceAugmentationBuilder();
vppInterfaceAugmentationBuilder.setVxlan(vxlan);
interfaceBuilder.addAugmentation(VppInterfaceAugmentation.class, vppInterfaceAugmentationBuilder.build());
return interfaceBuilder.build();
}
private Vxlan prepareVxlan(final Ipv4AddressNoZone ipSrc, final Ipv4AddressNoZone ipDst) {
final VxlanBuilder vxlanBuilder = new VxlanBuilder();
vxlanBuilder.setSrc(ipSrc);
vxlanBuilder.setDst(ipDst);
final TunnelParameters tunnelParameters = config.getTunnelParameters();
if (tunnelParameters instanceof org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vbridge.topology.rev160129.network.topology.topology.tunnel.parameters.Vxlan) {
org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vbridge.topology.rev160129.network.topology.topology.tunnel.parameters.Vxlan vxlan =
(org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vbridge.topology.rev160129.network.topology.topology.tunnel.parameters.Vxlan) tunnelParameters;
//TODO: handle NPE
vxlanBuilder.setVni(vxlan.getVxlan().getVni());
}
vxlanBuilder.setEncapVrfId(DEFAULT_ENCAP_VRF_ID);
return vxlanBuilder.build();
}
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 (accroding 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 iiToVpp =
InstanceIdentifier.create(Interfaces.class)
.child(Interface.class, new InterfaceKey(userInterface.getValue()));
InstanceIdentifier iiToV3poL2 = iiToVpp.augmentation(VppInterfaceAugmentation.class).child(L2.class);
LOG.debug("Writing L2 data to configuration DS to concrete interface.");
final WriteTransaction wTx = vppDataBroker.newWriteOnlyTransaction();
wTx.put(LogicalDatastoreType.CONFIGURATION, iiToV3poL2, prepareL2Data());
wTx.submit();
}
}
ListenableFuture addVppToBridgeDomain(final KeyedInstanceIdentifier iiToVpp, final Node node) {
final DataBroker vppDataBroker = VbdUtil.resolveDataBrokerForMountPoint(iiToVpp, mountService);
if (vppDataBroker != null) {
final WriteTransaction wTx = vppDataBroker.newWriteOnlyTransaction();
wTx.put(LogicalDatastoreType.CONFIGURATION, iiBridgeDomainOnVPP, prepareNewBridgeDomainData());
return wTx.submit();
}
return Futures.immediateFailedFuture(new IllegalStateException("Data broker for vpp is missing"));
}
private org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.vpp.bridge.domains.BridgeDomain
prepareNewBridgeDomainData() {
final BridgeDomainBuilder bridgeDomainBuilder = new BridgeDomainBuilder(config);
bridgeDomainBuilder.setName(bridgeDomainName);
return bridgeDomainBuilder.build();
}
private L2 prepareL2Data() {
final L2Builder l2Builder = new L2Builder();
final BridgeBasedBuilder bridgeBasedBuilder = new BridgeBasedBuilder();
bridgeBasedBuilder.setSplitHorizonGroup((short) 0);
bridgeBasedBuilder.setBridgedVirtualInterface(false);
bridgeBasedBuilder.setBridgeDomain(bridgeDomainName);
l2Builder.setInterconnection(bridgeBasedBuilder.build());
return l2Builder.build();
}
public void setConfig(final TopologyVbridgeAugment config) {
this.config = config;
}
}