/* * Copyright (c) 2016 Cisco Systems. 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.routing; import com.google.common.base.Optional; import com.google.common.base.Strings; import org.opendaylight.controller.md.sal.binding.api.DataBroker; import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; import org.opendaylight.groupbasedpolicy.renderer.vpp.commands.RoutingCommand; import org.opendaylight.groupbasedpolicy.renderer.vpp.util.GbpNetconfTransaction; import org.opendaylight.groupbasedpolicy.renderer.vpp.util.General; import org.opendaylight.groupbasedpolicy.renderer.vpp.util.MountedDataBrokerProvider; import org.opendaylight.groupbasedpolicy.util.DataStoreHelper; 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.Ipv4Address; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Prefix; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ipv4.unicast.routing.rev140524.routing.routing.instance.routing.protocols.routing.protocol._static.routes.ipv4.Route; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ipv4.unicast.routing.rev140524.routing.routing.instance.routing.protocols.routing.protocol._static.routes.ipv4.RouteBuilder; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ipv4.unicast.routing.rev140524.routing.routing.instance.routing.protocols.routing.protocol._static.routes.ipv4.route.next.hop.options.SimpleNextHopBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.forwarding.l2_l3.rev160427.SubnetAugmentRenderer; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.forwarding.l2_l3.rev160427.has.subnet.subnet.Gateways; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.forwarding.l2_l3.rev160427.has.subnet.subnet.gateways.Prefixes; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.renderer.nodes.RendererNode; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.renderer.policy.configuration.renderer.forwarding.RendererForwardingByTenant; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.renderer.policy.configuration.renderer.forwarding.renderer.forwarding.by.tenant.RendererNetworkDomain; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.renderers.renderer.renderer.nodes.renderer.node.PhysicalInterface; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.annotation.Nonnull; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; public class RoutingManager { private static final Logger LOG = LoggerFactory.getLogger(RoutingManager.class); private static final long DEFAULT_TABLE = 0L; private final DataBroker dataBroker; private final MountedDataBrokerProvider mountDataProvider; public RoutingManager(DataBroker dataBroker, MountedDataBrokerProvider mountDataProvider) { this.dataBroker = dataBroker; this.mountDataProvider = mountDataProvider; } public Map<InstanceIdentifier<?>, RoutingCommand> createRouting( @Nonnull RendererForwardingByTenant forwardingByTenant, List<InstanceIdentifier<PhysicalInterface>> physIntIids, General.Operations operation) { Map<InstanceIdentifier<?>, RoutingCommand> routingCommands = new HashMap<>(); getExternalGateways(forwardingByTenant.getRendererNetworkDomain()).forEach((gateways, virtualRouterIp) -> { LOG.trace("Creating routing for Tenant {}, gateway {}, virtualRouterIp {}.", forwardingByTenant.getTenantId(), gateways, virtualRouterIp); List<Route> ipv4Routes = new ArrayList<>(); PhysicalInterface outboundInterface = resolveOutboundInterface(virtualRouterIp, physIntIids); InstanceIdentifier<?> node = resolveOutboundNode(virtualRouterIp, physIntIids); String outboundIntName = outboundInterface != null ? outboundInterface.getInterfaceName() : null; if (Strings.isNullOrEmpty(outboundIntName)) { LOG.trace("Route skipped, no physical interface for gateway found {} in interfaces {}", gateways, physIntIids); } else { Long routeIndex = 0L; IpAddress externalGwIp = gateways.getGateway(); for (Prefixes prefixes : gateways.getPrefixes()) { routeIndex++; Route ipv4Route = buildIpv4Route(outboundIntName, routeIndex, externalGwIp, prefixes); //todo add support for ipv6 LOG.trace("Adding new route {}.", ipv4Route); ipv4Routes.add(ipv4Route); } } if (!ipv4Routes.isEmpty()) { RoutingCommand command = routingCommands.put(node, new RoutingCommand.RoutingCommandBuilder() .setOperation(operation) .setRouterProtocol(RoutingCommand.DEFAULT_ROUTING_PROTOCOL) .setRoutes(ipv4Routes) //todo in multi-tenant environment we need to use different vrfID for each tenant .setVrfId(DEFAULT_TABLE) .build()); LOG.trace("Creating of routing successful, routing command: {}.", command); } }); return routingCommands; } private Route buildIpv4Route(String outboundIntName, Long routeIndex, IpAddress externalGwIp, Prefixes prefixes) { return new RouteBuilder().setId(routeIndex) .setDestinationPrefix(new Ipv4Prefix(prefixes.getPrefix().getIpv4Prefix())) .setNextHopOptions(new SimpleNextHopBuilder().setNextHop(new Ipv4Address(externalGwIp.getIpv4Address())) .setOutgoingInterface(outboundIntName) .build()) .build(); } /** * Used to extract external gateways from network domains if they contain any gateways * @param rendererNetworkDomain list of network domains from which we extract external gateways * @return map of extracted gateways by virtual router IP from network domain list. */ private Map<Gateways, IpAddress> getExternalGateways(List<RendererNetworkDomain> rendererNetworkDomain){ Map<Gateways, IpAddress> gateways = new HashMap<>(); for (RendererNetworkDomain domain : rendererNetworkDomain) { SubnetAugmentRenderer subnet = domain.getAugmentation(SubnetAugmentRenderer.class); IpAddress virtualRouterIp = subnet.getSubnet().getVirtualRouterIp(); if (virtualRouterIp == null){ continue; } List<Gateways> gatewaysList = subnet.getSubnet().getGateways(); if (gatewaysList != null) { for (Gateways gateway : gatewaysList) { gateways.put(gateway, virtualRouterIp); LOG.trace("Found external Gateway {}", gateway); } } } return gateways; } private PhysicalInterface resolveOutboundInterface(@Nonnull IpAddress extIfaceIp, List<InstanceIdentifier<PhysicalInterface>> physIntIids) { LOG.trace("Resolving External interface {} from interfaces {}.", extIfaceIp, physIntIids); for (InstanceIdentifier<PhysicalInterface> identifier : physIntIids) { Optional<PhysicalInterface> physicalInterfaceOptional = DataStoreHelper.readFromDs( LogicalDatastoreType.OPERATIONAL, identifier, dataBroker.newReadOnlyTransaction()); if (!physicalInterfaceOptional.isPresent()) { continue; } if (physicalInterfaceOptional.get().isExternal()) { return physicalInterfaceOptional.get(); } if (physicalInterfaceOptional.get().getAddress().contains(extIfaceIp)){ return physicalInterfaceOptional.get(); } } return null; } private InstanceIdentifier<?> resolveOutboundNode(@Nonnull IpAddress extIfaceIp, List<InstanceIdentifier<PhysicalInterface>> physIntIids) { for (InstanceIdentifier<PhysicalInterface> identifier : physIntIids) { Optional<PhysicalInterface> physicalInterfaceOptional = DataStoreHelper.readFromDs( LogicalDatastoreType.OPERATIONAL, identifier, dataBroker.newReadOnlyTransaction()); if (!physicalInterfaceOptional.isPresent()){ continue; } if (physicalInterfaceOptional.get().isExternal()) { return identifier.firstKeyOf(RendererNode.class).getNodePath(); } if (physicalInterfaceOptional.get().getAddress().contains(extIfaceIp)){ return identifier.firstKeyOf(RendererNode.class).getNodePath(); } } return null; } public boolean submitRouting(@Nonnull RoutingCommand routing, InstanceIdentifier<?> nodeIid) { if (nodeIid == null) { LOG.info("NodeId is null Cannot create routing. RoutingCommand: {}", routing); return false; } LOG.trace("Submitting routing for routing command: {}, nodeId: {}", routing, nodeIid); Optional<DataBroker> mountPointDataBroker = mountDataProvider.getDataBrokerForMountPoint(nodeIid); if (!mountPointDataBroker.isPresent()) { throw new IllegalStateException("Cannot find data broker for mount point " + nodeIid); } LOG.info("Routing was created for forwarding. Routing: {}, for node: {}", routing, nodeIid); if (routing.getOperation() == General.Operations.PUT){ return GbpNetconfTransaction.netconfSyncedWrite(mountPointDataBroker.get(), routing, GbpNetconfTransaction.RETRY_COUNT); } else if (routing.getOperation() == General.Operations.DELETE){ return GbpNetconfTransaction.netconfSyncedDelete(mountPointDataBroker.get(), routing, GbpNetconfTransaction.RETRY_COUNT); } return false; } }