/* * 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.groupbasedpolicy.renderer.vpp.adapter; import java.util.ArrayList; import java.util.List; import java.util.concurrent.Future; import java.util.stream.Collectors; import javax.annotation.Nonnull; import org.opendaylight.controller.config.yang.config.vpp_provider.impl.VppRenderer; import org.opendaylight.controller.md.sal.binding.api.DataBroker; 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.groupbasedpolicy.renderer.vpp.api.BridgeDomainManager; import org.opendaylight.groupbasedpolicy.renderer.vpp.commands.interfaces.ConfigCommand; import org.opendaylight.groupbasedpolicy.renderer.vpp.commands.TapPortCommand; import org.opendaylight.groupbasedpolicy.renderer.vpp.commands.VhostUserCommand; import org.opendaylight.groupbasedpolicy.renderer.vpp.commands.VhostUserCommand.VhostUserCommandBuilder; import org.opendaylight.groupbasedpolicy.renderer.vpp.iface.InterfaceManager; import org.opendaylight.groupbasedpolicy.renderer.vpp.util.General.Operations; import org.opendaylight.groupbasedpolicy.renderer.vpp.util.MountedDataBrokerProvider; import org.opendaylight.groupbasedpolicy.renderer.vpp.util.VppIidFactory; 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.InterfaceKey; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_adapter.rev161201.AddInterfaceToBridgeDomainInput; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_adapter.rev161201.CreateInterfaceOnNodeInput; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_adapter.rev161201.CreateVirtualBridgeDomainOnNodesInput; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_adapter.rev161201.DelInterfaceFromBridgeDomainInput; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_adapter.rev161201.DeleteInterfaceFromNodeInput; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_adapter.rev161201.DeleteVirtualBridgeDomainFromNodesInput; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_adapter.rev161201.CloneVirtualBridgeDomainOnNodesInput; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_adapter.rev161201.VppAdapterService; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_adapter.rev161201.bridge.domain.attributes.tunnel.type.Vlan; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_adapter.rev161201.bridge.domain.attributes.tunnel.type.Vxlan; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425._interface.attributes.InterfaceTypeChoice; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425._interface.attributes._interface.type.choice.TapCase; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425._interface.attributes._interface.type.choice.VhostUserCase; import org.opendaylight.yang.gen.v1.urn.opendaylight.l2.types.rev130827.VlanId; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev170315.VhostUserRole; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev170315.VxlanVni; 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.tunnel.vlan.rev170327.network.topology.topology.tunnel.parameters.VlanNetworkParameters; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vbridge.tunnel.vxlan.rev170327.network.topology.topology.tunnel.parameters.VxlanTunnelParameters; import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId; 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.yangtools.yang.binding.InstanceIdentifier; 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 com.google.common.base.Optional; import com.google.common.util.concurrent.AsyncFunction; import com.google.common.util.concurrent.CheckedFuture; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; public class VppRpcServiceImpl implements VppAdapterService, AutoCloseable { private static final Logger LOG = LoggerFactory.getLogger(VppRpcServiceImpl.class); private final DataBroker dataBroker; private final BridgeDomainManager bridgeDomainManager; private final InterfaceManager interfaceManager; private final MountedDataBrokerProvider mountDataProvider; public VppRpcServiceImpl(@Nonnull DataBroker dataBroker, @Nonnull VppRenderer renderer) { this.dataBroker = dataBroker; this.bridgeDomainManager = renderer.getBridgeDomainManager(); this.interfaceManager = renderer.getInterfaceManager(); this.mountDataProvider = renderer.getMountedDataBroker(); } public Future<RpcResult<Void>> createVirtualBridgeDomainOnNodes(CreateVirtualBridgeDomainOnNodesInput input) { LOG.info("Processing a remote call for creating bridge domain {}", input.getId()); if (input.getTunnelType() == null) { return Futures.immediateFuture(RpcResultBuilder.<Void>failed() .withError(ErrorType.RPC, "Failed to create bridge domain" + input.getId() + "." + "Tunnel type not specified") .build()); } List<ListenableFuture<Void>> futures = new ArrayList<>(); List<NodeId> nodeIds = (input.getPhysicalLocationRef() == null) ? new ArrayList<>() : input .getPhysicalLocationRef().stream().map(locationRef -> locationRef.getNodeId()).collect(Collectors.toList()); LOG.trace("Corresponding nodes for bridge-domain {}", input.getPhysicalLocationRef()); if (input.getTunnelType() instanceof Vxlan) { LOG.trace("Detected VXLAN type for bridge domain {}", input.getId()); Vxlan tunnelType = (Vxlan) input.getTunnelType(); VxlanVni vxlanVni = new VxlanVni(tunnelType.getVni().getValue()); nodeIds.forEach(nodeId -> { futures.add(bridgeDomainManager.createVxlanBridgeDomainOnVppNode(input.getId(), vxlanVni, nodeId)); }); } else if (input.getTunnelType() instanceof Vlan) { LOG.trace("Detected VLAN type for bridge domain {}", input.getId()); Vlan vlan = (Vlan) input.getTunnelType(); VlanId vlanId = new VlanId(vlan.getVlanId().getValue()); nodeIds.forEach(nodeId -> { futures.add(bridgeDomainManager.createVlanBridgeDomainOnVppNode(input.getId(), vlanId, nodeId)); }); } return Futures.transform(Futures.allAsList(futures), voidsToRpcResult()); } public Future<RpcResult<Void>> deleteVirtualBridgeDomainFromNodes(DeleteVirtualBridgeDomainFromNodesInput input) { LOG.info("Processing a remote call for removing bridge domain {}", input.getBridgeDomainId()); List<ListenableFuture<Void>> futures = new ArrayList<>(); input.getBridgeDomainNode().forEach(nodeId -> { futures.add(bridgeDomainManager.removeBridgeDomainFromVppNode(input.getBridgeDomainId(), nodeId)); }); return Futures.transform(Futures.allAsList(futures), voidsToRpcResult()); } public ListenableFuture<RpcResult<Void>> cloneVirtualBridgeDomainOnNodes(CloneVirtualBridgeDomainOnNodesInput input) { LOG.info("Processing a remote call for clonning bridge domain {}", input.getBridgeDomainId()); List<ListenableFuture<Void>> futures = new ArrayList<>(); ReadOnlyTransaction rTx = dataBroker.newReadOnlyTransaction(); InstanceIdentifier<Topology> topologyIid = VppIidFactory.getTopologyIid(new TopologyKey(new TopologyId( input.getBridgeDomainId()))); return Futures.transform(rTx.read(LogicalDatastoreType.CONFIGURATION, topologyIid), new AsyncFunction<Optional<Topology>, RpcResult<Void>>() { @Override public ListenableFuture<RpcResult<Void>> apply(Optional<Topology> optTopology) throws Exception { if (!optTopology.isPresent()) { return Futures.immediateFuture(RpcResultBuilder.<Void>failed() .withError( ErrorType.RPC, "Failed to clone bridge domain. Bridge domain " + input.getBridgeDomainId() + " does not exist.") .build()); } TopologyVbridgeAugment vBridgeAug = optTopology.get().getAugmentation(TopologyVbridgeAugment.class); if (vBridgeAug == null) { return Futures.immediateFuture(RpcResultBuilder.<Void>failed() .withError( ErrorType.RPC, "Failed to clone bridge domain. Topology " + input.getBridgeDomainId() + " is not bridge domain type.") .build()); } if (vBridgeAug.getTunnelParameters() instanceof VxlanTunnelParameters) { LOG.debug("Clonning VXLAN type bridge domain {} on nodes {}", input.getBridgeDomainId(), input.getBridgeDomainNode()); VxlanTunnelParameters vxlanTunnelParams = (VxlanTunnelParameters) vBridgeAug.getTunnelParameters(); VxlanVni vni = vxlanTunnelParams.getVni(); input.getBridgeDomainNode().forEach( nodeId -> { futures.add(bridgeDomainManager.createVxlanBridgeDomainOnVppNode( input.getBridgeDomainId(), vni, nodeId)); }); } else if (vBridgeAug.getTunnelParameters() instanceof VlanNetworkParameters) { LOG.debug("Clonning VLAN type bridge domain {} on nodes {}", input.getBridgeDomainId(), input.getBridgeDomainNode()); VlanNetworkParameters vlanTunnelParams = (VlanNetworkParameters) vBridgeAug.getTunnelParameters(); VlanId vlanId = vlanTunnelParams.getVlanId(); input.getBridgeDomainNode().forEach( nodeId -> { futures.add(bridgeDomainManager.createVlanBridgeDomainOnVppNode( input.getBridgeDomainId(), vlanId, nodeId)); }); } return Futures.transform(Futures.allAsList(futures), voidsToRpcResult()); } }); } public ListenableFuture<RpcResult<Void>> createInterfaceOnNode(CreateInterfaceOnNodeInput input) { LOG.info("Processing a remote call for creating interface {} on node {}", input.getVppInterfaceName(), input.getVppNodeId()); InterfaceTypeChoice interfaceType = input.getInterfaceTypeChoice(); ConfigCommand ifaceCommand = null; if (interfaceType instanceof VhostUserCase) { VhostUserCommandBuilder vhostBuilder = VhostUserCommand.builder(); vhostBuilder.setName(input.getVppInterfaceName()); VhostUserCase vhostCase = (VhostUserCase) input.getInterfaceTypeChoice(); vhostBuilder.setSocket(vhostCase.getSocket()); vhostBuilder.setRole(VhostUserRole.Client); vhostBuilder.setDescription(input.getDescription()); vhostBuilder.setOperation(Operations.PUT); ifaceCommand = vhostBuilder.build(); } if (interfaceType instanceof TapCase) { TapPortCommand.TapPortCommandBuilder tapBuilder = TapPortCommand.builder(); TapCase tapIface = (TapCase) input.getInterfaceTypeChoice(); tapBuilder.setTapName(tapIface.getName()); tapBuilder.setPhysAddress(tapIface.getPhysicalAddress()); tapBuilder.setInterfaceName(input.getVppInterfaceName()); tapBuilder.setDescription(input.getDescription()); tapBuilder.setOperation(Operations.PUT); ifaceCommand = tapBuilder.build(); } InstanceIdentifier<Node> vppNodeIid = VppIidFactory.getNetconfNodeIid(input.getVppNodeId()); Optional<DataBroker> optDataBroker = mountDataProvider.getDataBrokerForMountPoint(vppNodeIid); if (!optDataBroker.isPresent()) { return Futures.immediateFuture(RpcResultBuilder.<Void>failed() .withError(ErrorType.RPC, "Cannot find data broker for mount point " + vppNodeIid) .build()); } return Futures.transform(interfaceManager.createInterfaceOnVpp(ifaceCommand, optDataBroker.get()), voidToRpcResult()); } public ListenableFuture<RpcResult<Void>> deleteInterfaceFromNode(DeleteInterfaceFromNodeInput input) { LOG.info("Processing a remote call for removing interface {} from node {}", input.getVppInterfaceName(), input.getVppNodeId()); InstanceIdentifier<Node> vppNodeIid = VppIidFactory.getNetconfNodeIid(input.getVppNodeId()); return Futures.transform(readInterface(vppNodeIid, input.getVppInterfaceName()), new AsyncFunction<Optional<Interface>, RpcResult<Void>>() { @Override public ListenableFuture<RpcResult<Void>> apply(Optional<Interface> optIface) throws Exception { InterfaceKey iKey = new InterfaceKey(input.getVppInterfaceName()); if (!optIface.isPresent()) { return Futures.immediateFuture(RpcResultBuilder.<Void>failed() .withError( ErrorType.RPC, "Cannot delete interface " + iKey + " on node " + vppNodeIid + ". Not found or already deleted.") .build()); } Optional<DataBroker> dataBroker = mountDataProvider.getDataBrokerForMountPoint(vppNodeIid); WriteTransaction wTx = dataBroker.get().newWriteOnlyTransaction(); wTx.delete(LogicalDatastoreType.CONFIGURATION, VppIidFactory.getInterfaceIID(iKey)); return Futures.transform(wTx.submit(), voidToRpcResult()); } }); } public ListenableFuture<RpcResult<Void>> addInterfaceToBridgeDomain(AddInterfaceToBridgeDomainInput input) { LOG.info("Processing a remote call for adding interface {} to bridge domain {}", input.getVppInterfaceName(), input.getBridgeDomainId()); InstanceIdentifier<Node> vppNodeIid = VppIidFactory.getNetconfNodeIid(input.getVppNodeId()); return Futures.transform(readInterface(vppNodeIid, input.getVppInterfaceName()), new AsyncFunction<Optional<Interface>, RpcResult<Void>>() { @Override public ListenableFuture<RpcResult<Void>> apply(Optional<Interface> optIface) throws Exception { InterfaceKey iKey = new InterfaceKey(input.getVppInterfaceName()); if (!optIface.isPresent()) { return Futures.immediateFuture(RpcResultBuilder.<Void>failed() .withError( ErrorType.RPC, "Cannot add interface " + iKey + " to bridge domain on node " + vppNodeIid + ". Not found or deleted.") .build()); } Optional<DataBroker> dataBroker = mountDataProvider.getDataBrokerForMountPoint(vppNodeIid); return Futures.transform(interfaceManager.configureInterface(dataBroker.get(), iKey, input.getBridgeDomainId(), null), voidToRpcResult()); } }); } public ListenableFuture<RpcResult<Void>> delInterfaceFromBridgeDomain(DelInterfaceFromBridgeDomainInput input) { LOG.info("Processing a remote call for removing interface {} from bridge domain.", input.getVppInterfaceName()); InstanceIdentifier<Node> vppNodeIid = VppIidFactory.getNetconfNodeIid(input.getVppNodeId()); return Futures.transform(readInterface(vppNodeIid, input.getVppInterfaceName()), new AsyncFunction<Optional<Interface>, RpcResult<Void>>() { @Override public ListenableFuture<RpcResult<Void>> apply(Optional<Interface> optIface) throws Exception { if (!optIface.isPresent()) { return Futures.immediateFuture(RpcResultBuilder.<Void>failed() .withError( ErrorType.RPC, "Cannot remove interface " + input.getVppInterfaceName() + " from bridge domain on node " + vppNodeIid + ". Not found or deleted.") .build()); } Optional<DataBroker> dataBroker = mountDataProvider.getDataBrokerForMountPoint(vppNodeIid); return Futures.transform(interfaceManager.removeInterfaceFromBridgeDomain(dataBroker.get(), optIface.get().getKey()), voidToRpcResult()); } }); } private CheckedFuture<Optional<Interface>, ReadFailedException> readInterface(InstanceIdentifier<?> nodeIid, String interfaceName) { Optional<DataBroker> optDataBroker = mountDataProvider.getDataBrokerForMountPoint(nodeIid); if (!optDataBroker.isPresent()) { LOG.error("Cannot find data broker for node {}", nodeIid); return Futures.immediateCheckedFuture(Optional.absent()); } ReadOnlyTransaction rwTx = optDataBroker.get().newReadOnlyTransaction(); InterfaceKey iKey = new InterfaceKey(interfaceName); InstanceIdentifier<Interface> interfaceIID = VppIidFactory.getInterfaceIID(iKey); CheckedFuture<Optional<Interface>, ReadFailedException> readInterface = rwTx.read( LogicalDatastoreType.CONFIGURATION, interfaceIID); rwTx.close(); return readInterface; } private AsyncFunction<Void, RpcResult<Void>> voidToRpcResult() { return new AsyncFunction<Void, RpcResult<Void>>() { @Override public ListenableFuture<RpcResult<Void>> apply(Void input) throws Exception { return Futures.immediateFuture(RpcResultBuilder.<Void>success().build()); } }; } private AsyncFunction<List<Void>, RpcResult<Void>> voidsToRpcResult() { return new AsyncFunction<List<Void>, RpcResult<Void>>() { @Override public ListenableFuture<RpcResult<Void>> apply(List<Void> input) throws Exception { return Futures.immediateFuture(RpcResultBuilder.<Void>success().build()); } }; } @Override public void close() throws Exception { // NOOP } }