/* * Copyright (c) 2016 Ericsson India Global Services Pvt Ltd. and others. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html */ package org.opendaylight.netvirt.vpnmanager; import static org.opendaylight.genius.infra.Datastore.CONFIGURATION; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.SettableFuture; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.Optional; import java.util.concurrent.ExecutionException; import java.util.function.Consumer; import javax.inject.Inject; import javax.inject.Singleton; import org.opendaylight.genius.infra.ManagedNewTransactionRunner; import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl; import org.opendaylight.mdsal.binding.api.DataBroker; import org.opendaylight.netvirt.bgpmanager.api.IBgpManager; import org.opendaylight.netvirt.fibmanager.api.IFibManager; import org.opendaylight.netvirt.fibmanager.api.RouteOrigin; import org.opendaylight.netvirt.vpnmanager.api.IVpnManager; import org.opendaylight.netvirt.vpnmanager.api.intervpnlink.InterVpnLinkCache; import org.opendaylight.netvirt.vpnmanager.api.intervpnlink.InterVpnLinkDataComposite; import org.opendaylight.netvirt.vpnmanager.intervpnlink.InterVpnLinkUtil; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.vrfentries.VrfEntry; 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.vpn.rpc.rev160201.AddStaticRouteInput; 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.AddStaticRouteOutputBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.vpn.rpc.rev160201.ApplyArpConfigInput; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.vpn.rpc.rev160201.ApplyArpConfigOutput; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.vpn.rpc.rev160201.ApplyArpConfigOutputBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.vpn.rpc.rev160201.GenerateVpnLabelInput; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.vpn.rpc.rev160201.GenerateVpnLabelOutput; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.vpn.rpc.rev160201.GenerateVpnLabelOutputBuilder; 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.RemoveStaticRouteOutput; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.vpn.rpc.rev160201.RemoveStaticRouteOutputBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.vpn.rpc.rev160201.RemoveVpnLabelInput; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.vpn.rpc.rev160201.RemoveVpnLabelOutput; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.vpn.rpc.rev160201.RemoveVpnLabelOutputBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.vpn.rpc.rev160201.VpnRpcService; 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.opendaylight.yangtools.yang.common.Uint32; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.slf4j.helpers.FormattingTuple; import org.slf4j.helpers.MessageFormatter; @Singleton public class VpnRpcServiceImpl implements VpnRpcService { private static final Logger LOG = LoggerFactory.getLogger(VpnRpcServiceImpl.class); private final ManagedNewTransactionRunner txRunner; private final IFibManager fibManager; private final IBgpManager bgpManager; private final IVpnManager vpnManager; private final InterVpnLinkCache interVpnLinkCache; private final VpnUtil vpnUtil; private final InterVpnLinkUtil interVpnLinkUtil; @Inject public VpnRpcServiceImpl(final DataBroker dataBroker, final IFibManager fibManager, IBgpManager bgpManager, final IVpnManager vpnManager, final InterVpnLinkCache interVpnLinkCache, VpnUtil vpnUtil, InterVpnLinkUtil interVpnLinkUtil) { this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker); this.fibManager = fibManager; this.bgpManager = bgpManager; this.vpnManager = vpnManager; this.interVpnLinkCache = interVpnLinkCache; this.vpnUtil = vpnUtil; this.interVpnLinkUtil = interVpnLinkUtil; } /** * Generate label for the given ip prefix from the associated VPN. */ @Override public ListenableFuture> generateVpnLabel(GenerateVpnLabelInput input) { String vpnName = input.getVpnName(); String ipPrefix = input.getIpPrefix(); SettableFuture> futureResult = SettableFuture.create(); String rd = vpnUtil.getVpnRd(vpnName); Uint32 label = vpnUtil.getUniqueId(VpnConstants.VPN_IDPOOL_NAME, VpnUtil.getNextHopLabelKey(rd != null ? rd : vpnName, ipPrefix)); if (label == null || label.longValue() == VpnConstants.INVALID_LABEL) { futureResult.set(RpcResultBuilder.failed().withError(ErrorType.APPLICATION, formatAndLog(LOG::error, "Could not retrieve the label for prefix {} in VPN {}", ipPrefix, vpnName)).build()); } else { GenerateVpnLabelOutput output = new GenerateVpnLabelOutputBuilder().setLabel(label).build(); futureResult.set(RpcResultBuilder.success(output).build()); } return futureResult; } /** * Remove label for the given ip prefix from the associated VPN. */ @Override public ListenableFuture> removeVpnLabel(RemoveVpnLabelInput input) { String vpnName = input.getVpnName(); String ipPrefix = input.getIpPrefix(); String rd = vpnUtil.getVpnRd(vpnName); vpnUtil.releaseId(VpnConstants.VPN_IDPOOL_NAME, VpnUtil.getNextHopLabelKey(rd != null ? rd : vpnName, ipPrefix)); return RpcResultBuilder.success(new RemoveVpnLabelOutputBuilder().build()).buildFuture(); } private Collection validateAddStaticRouteInput(AddStaticRouteInput input) { Collection rpcErrors = new ArrayList<>(); String destination = input.getDestination(); String vpnInstanceName = input.getVpnInstanceName(); String nexthop = input.getNexthop(); if (destination == null || destination.isEmpty()) { String message = "destination parameter is mandatory"; rpcErrors.add(RpcResultBuilder.newError(RpcError.ErrorType.PROTOCOL, "addStaticRoute", message)); } if (vpnInstanceName == null || vpnInstanceName.isEmpty()) { String message = "vpnInstanceName parameter is mandatory"; rpcErrors.add(RpcResultBuilder.newError(RpcError.ErrorType.PROTOCOL, "addStaticRoute", message)); } if (nexthop == null || nexthop.isEmpty()) { String message = "nexthop parameter is mandatory"; rpcErrors.add(RpcResultBuilder.newError(RpcError.ErrorType.PROTOCOL, "addStaticRoute", message)); } return rpcErrors; } // TODO Clean up the exception handling @SuppressWarnings("checkstyle:IllegalCatch") @Override public ListenableFuture> addStaticRoute(AddStaticRouteInput input) { SettableFuture> result = SettableFuture.create(); String destination = input.getDestination(); String vpnInstanceName = input.getVpnInstanceName(); String nexthop = input.getNexthop(); Uint32 label = input.getLabel(); LOG.info("Adding static route for Vpn {} with destination {}, nexthop {} and label {}", vpnInstanceName, destination, nexthop, label); Collection rpcErrors = validateAddStaticRouteInput(input); if (!rpcErrors.isEmpty()) { result.set(RpcResultBuilder.failed().withRpcErrors(rpcErrors).build()); return result; } if (label == null || label.longValue() == VpnConstants.INVALID_LABEL) { label = vpnUtil.getUniqueId(VpnConstants.VPN_IDPOOL_NAME, VpnUtil.getNextHopLabelKey(vpnInstanceName, destination)); if (label.longValue() == VpnConstants.INVALID_LABEL) { String message = "Unable to retrieve a new Label for the new Route"; result.set(RpcResultBuilder.failed().withError(RpcError.ErrorType.APPLICATION, message).build()); LOG.error("addStaticRoute: Unable to retrieve label for static route with destination {}, vpninstance" + " {}, nexthop {}", destination, vpnInstanceName, nexthop); return result; } } String vpnRd = vpnUtil.getVpnRd(input.getVpnInstanceName()); VpnInstanceOpDataEntry vpnOpEntry = vpnUtil.getVpnInstanceOpData(vpnRd); Boolean isVxlan = VpnUtil.isL3VpnOverVxLan(vpnOpEntry.getL3vni()); VrfEntry.EncapType encapType = VpnUtil.getEncapType(isVxlan); if (vpnRd == null) { String message = "Could not find Route-Distinguisher for VpnName " + vpnInstanceName; result.set(RpcResultBuilder.failed().withError(RpcError.ErrorType.APPLICATION, message).build()); return result; } Optional optIVpnLink = interVpnLinkCache.getInterVpnLinkByEndpoint(nexthop); if (optIVpnLink.isPresent()) { try { interVpnLinkUtil.handleStaticRoute(optIVpnLink.get(), vpnInstanceName, destination, nexthop, label); } catch (Exception e) { result.set(RpcResultBuilder.failed().withError(ErrorType.APPLICATION, formatAndLog(LOG::warn, "Could not advertise route [vpn={}, prefix={}, label={}, nexthop={}] to BGP: {}", vpnRd, destination, label, nexthop, e.getMessage(), e)).build()); return result; } } else { try { txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, confTx -> vpnManager.addExtraRoute(vpnInstanceName, destination, nexthop, vpnRd, null /* routerId */, vpnOpEntry.getL3vni(), RouteOrigin.STATIC, null /* intfName */, null /*Adjacency*/, encapType, new HashSet<>() /*prefixListForRefreshFib*/,confTx)).get(); } catch (InterruptedException | ExecutionException e) { LOG.error("Error adding static route {}", input, e); result.set(RpcResultBuilder.failed().withError(ErrorType.APPLICATION, "Error adding static route " + input, e).build()); return result; } } AddStaticRouteOutput labelOutput = new AddStaticRouteOutputBuilder().setLabel(label).build(); result.set(RpcResultBuilder.success(labelOutput).build()); return result; } private Collection validateRemoveStaticRouteInput(RemoveStaticRouteInput input) { Collection rpcErrors = new ArrayList<>(); String destination = input.getDestination(); String vpnInstanceName = input.getVpnInstanceName(); String nexthop = input.getNexthop(); if (destination == null || destination.isEmpty()) { String message = "destination parameter is mandatory"; rpcErrors.add(RpcResultBuilder.newError(RpcError.ErrorType.PROTOCOL, "removeStaticRoute", message)); } if (vpnInstanceName == null || vpnInstanceName.isEmpty()) { String message = "vpnInstanceName parameter is mandatory"; rpcErrors.add(RpcResultBuilder.newError(RpcError.ErrorType.PROTOCOL, "removeStaticRoute", message)); } if (nexthop == null || nexthop.isEmpty()) { String message = "nexthop parameter is mandatory"; rpcErrors.add(RpcResultBuilder.newError(RpcError.ErrorType.PROTOCOL, "removeStaticRoute", message)); } return rpcErrors; } @Override public ListenableFuture> removeStaticRoute(RemoveStaticRouteInput input) { SettableFuture> result = SettableFuture.create(); String destination = input.getDestination(); String vpnInstanceName = input.getVpnInstanceName(); String nexthop = input.getNexthop(); LOG.info("Removing static route with destination={}, nexthop={} in VPN={}", destination, nexthop, vpnInstanceName); Collection rpcErrors = validateRemoveStaticRouteInput(input); if (!rpcErrors.isEmpty()) { result.set(RpcResultBuilder.failed().withRpcErrors(rpcErrors).build()); return result; } String vpnRd = vpnUtil.getVpnRd(input.getVpnInstanceName()); if (vpnRd == null) { String message = "Could not find Route-Distinguisher for VpnName " + vpnInstanceName; result.set(RpcResultBuilder.failed() .withError(RpcError.ErrorType.APPLICATION, message).build()); return result; } Optional optVpnLink = interVpnLinkCache.getInterVpnLinkByEndpoint(nexthop); if (optVpnLink.isPresent()) { fibManager.removeOrUpdateFibEntry(vpnRd, destination, nexthop, /*writeTx*/ null); bgpManager.withdrawPrefix(vpnRd, destination); } else { vpnManager.delExtraRoute(vpnInstanceName, destination, nexthop, vpnRd, null /* routerId */, null /* intfName */, null, null); } result.set(RpcResultBuilder.success(new RemoveStaticRouteOutputBuilder().build()).build()); return result; } private String formatAndLog(Consumer logger, String template, Object arg1, Object arg2) { return logAndReturnMessage(logger, MessageFormatter.format(template, arg1, arg2)); } private String formatAndLog(Consumer logger, String template, Object... args) { return logAndReturnMessage(logger, MessageFormatter.arrayFormat(template, args)); } private String logAndReturnMessage(Consumer logger, FormattingTuple tuple) { String message = tuple.getMessage(); logger.accept(message); return message; } @Override public ListenableFuture> applyArpConfig(ApplyArpConfigInput input) { Boolean isArpLearningEnabled = input.isEnableArpLearning(); LOG.info("isArpLearningEnabled {}", isArpLearningEnabled); SettableFuture> result = SettableFuture.create(); ApplyArpConfigOutputBuilder output = new ApplyArpConfigOutputBuilder(); VpnUtil.enableArpLearning(isArpLearningEnabled); output.setEnableArpLearning(VpnUtil.isArpLearningEnabled()); result.set(RpcResultBuilder.success(output).build()); return result; } }