L3 handling for COE services. 79/80079/4
authorFaseela K <faseela.k@ericsson.com>
Fri, 1 Feb 2019 10:20:51 +0000 (15:50 +0530)
committerFaseela K <faseela.k@ericsson.com>
Thu, 7 Feb 2019 05:14:54 +0000 (10:44 +0530)
Current implementation of services is handled
via ELAN pipeline, which is not an efficient solution.
The proposal here makes each service IP an extraroute
on the service-gateway port.

Change-Id: I3f8d022124e118cf08eb13ccac19185f81a6736e
Signed-off-by: Faseela K <faseela.k@ericsson.com>
coe/api/src/main/yang/service-meta.yang [new file with mode: 0644]
coe/impl/src/main/java/org/opendaylight/netvirt/coe/listeners/ServiceListener.java
coe/impl/src/main/java/org/opendaylight/netvirt/coe/listeners/TerminationPointStateListener.java
coe/impl/src/main/java/org/opendaylight/netvirt/coe/utils/CoeUtils.java

diff --git a/coe/api/src/main/yang/service-meta.yang b/coe/api/src/main/yang/service-meta.yang
new file mode 100644 (file)
index 0000000..83537b9
--- /dev/null
@@ -0,0 +1,34 @@
+module service-meta {
+    namespace "urn:opendaylight:netvirt:coe:service-meta";
+    prefix "coe-service-meta";
+
+    import ietf-yang-types {
+        prefix yang;
+        revision-date "2013-07-15";
+    }
+
+    revision "2019-01-23" {
+        description "Coe Service Meta Information";
+    }
+
+    container service-gateway-info {
+        description "Contains the list of services to poduuid mapping";
+
+        list service-gateway {
+            key gateway-pod-name;
+            leaf gateway-pod-name {
+                type string;
+            }
+
+            leaf gateway-pod-ip-address {
+                type string;
+                description "ip address of the service gateway";
+            }
+
+            leaf gateway-pod-mac-address {
+                type string;
+                description "MAC address of the associated port.";
+            }
+        }
+    }
+}
\ No newline at end of file
index 7f81e93c8f0f314a9eea31500ddef1c69b7ce279..e9c34d6f87378b8a00b059c1249f59a602a26794 100644 (file)
@@ -8,6 +8,8 @@
 
 package org.opendaylight.netvirt.coe.listeners;
 
+import static org.opendaylight.genius.infra.Datastore.CONFIGURATION;
+
 import java.util.Collection;
 import javax.annotation.Nonnull;
 import javax.annotation.PreDestroy;
@@ -20,6 +22,9 @@ import org.opendaylight.controller.md.sal.binding.api.DataTreeChangeListener;
 import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;
 import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
+import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
+import org.opendaylight.netvirt.coe.utils.CoeUtils;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.coe.northbound.service.rev170611.ServiceInformation;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.coe.northbound.service.rev170611.service.information.Services;
 import org.opendaylight.yangtools.concepts.ListenerRegistration;
@@ -31,11 +36,17 @@ import org.slf4j.LoggerFactory;
 public class ServiceListener implements DataTreeChangeListener<Services> {
 
     private static final Logger LOG = LoggerFactory.getLogger(ServiceListener.class);
+
+    private final CoeUtils coeUtils;
+    private final ManagedNewTransactionRunner txRunner;
+
     private ListenerRegistration<ServiceListener> listenerRegistration;
 
     @Inject
-    public ServiceListener(@Reference final DataBroker dataBroker) {
+    public ServiceListener(@Reference final DataBroker dataBroker, final CoeUtils coeUtils) {
         registerListener(LogicalDatastoreType.CONFIGURATION, dataBroker);
+        this.coeUtils = coeUtils;
+        this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
     }
 
     protected InstanceIdentifier<Services> getWildCardPath() {
@@ -69,6 +80,7 @@ public class ServiceListener implements DataTreeChangeListener<Services> {
                     break;
                 case SUBTREE_MODIFIED:
                     LOG.info("Service updated old : {}, new : {}", mod.getDataBefore(), mod.getDataAfter());
+                    add(mod.getDataAfter());
                     break;
                 case WRITE:
                     if (mod.getDataBefore() == null) {
@@ -76,6 +88,7 @@ public class ServiceListener implements DataTreeChangeListener<Services> {
                     } else {
                         LOG.info("Service updated old : {}, new : {}", mod.getDataBefore(), mod.getDataAfter());
                     }
+                    add(mod.getDataAfter());
                     break;
                 default:
                     // FIXME: May be not a good idea to throw.
@@ -83,4 +96,9 @@ public class ServiceListener implements DataTreeChangeListener<Services> {
             }
         }
     }
+
+    void add(Services services) {
+        txRunner.callWithNewReadWriteTransactionAndSubmit(
+                CONFIGURATION, tx -> coeUtils.updateVpnInterfaceWithExtraRouteAdjacency(tx, services));
+    }
 }
index 3195634516db0091606e7be8498134f5345f48ef..f48629d95b9dc606830362031a507940d0471f7d 100644 (file)
@@ -109,11 +109,15 @@ public class TerminationPointStateListener extends
                                             pods.getHostIpAddress().stringValue(), clusterId);
                                     coeUtils.updateElanInterfaceWithStaticMac(macAddress, podIpAddress,
                                             interfaceName, elanInstanceName, tx);
-                                    if (!isServiceGateway) {
-                                        coeUtils.createVpnInterface(clusterId, pods, interfaceName,
-                                                macAddress,false, tx);
-                                        LOG.debug("Bind Kube Proxy Service for {}", interfaceName);
-                                        bindKubeProxyService(tx, interfaceName);
+                                    coeUtils.createVpnInterface(pods.getClusterId().getValue(), pods, interfaceName,
+                                            macAddress,false, tx);
+                                    LOG.debug("Bind Kube Proxy Service for {}", interfaceName);
+                                    bindKubeProxyService(tx, interfaceName);
+                                    if (isServiceGateway) {
+                                        String ipValue = podIpAddress.getIpv4Address() != null
+                                                ? podIpAddress.getIpv4Address().getValue() :
+                                                podIpAddress.getIpv6Address().getValue();
+                                        coeUtils.updateServiceGatewayList(tx, interfaceName, ipValue, macAddress);
                                     }
                                 }
                             }), LOG, "Error handling pod configuration for termination-point");
index 6304cb8163f046519584f2fcbea3adaf3c696c6e..e00898e3957ff1118e95d2300017cadcd30869b8 100644 (file)
@@ -8,11 +8,13 @@
 
 package org.opendaylight.netvirt.coe.utils;
 
+import com.google.common.base.Optional;
 import com.google.common.collect.ImmutableBiMap;
 
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
 import java.util.UUID;
@@ -60,6 +62,7 @@ import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.coe.northbound.pod.rev170611.NetworkAttributes;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.coe.northbound.pod.rev170611.coe.Pods;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.coe.northbound.service.rev170611.service.information.Services;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.IfL2vlan;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.IfL2vlanBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.ServiceBindings;
@@ -72,6 +75,10 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.coe.meta.rev180118.
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.coe.meta.rev180118.podidentifier.info.PodIdentifier;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.coe.meta.rev180118.podidentifier.info.PodIdentifierBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.coe.meta.rev180118.podidentifier.info.PodIdentifierKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.coe.service.meta.rev190123.ServiceGatewayInfo;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.coe.service.meta.rev190123.service.gateway.info.ServiceGateway;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.coe.service.meta.rev190123.service.gateway.info.ServiceGatewayBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.coe.service.meta.rev190123.service.gateway.info.ServiceGatewayKey;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.ElanInstances;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.ElanInterfaces;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.SegmentTypeBase;
@@ -462,21 +469,63 @@ public final class CoeUtils {
         if (!adjList.contains(vmAdj)) {
             adjList.add(vmAdj);
         }
-
-        //if (isRouterInterface) {
-            // TODO
-            // create extraroute Adjacence for each ipValue,
-            // because router can have IPv4 and IPv6 subnet ports, or can have
-            // more that one IPv4 subnet port or more than one IPv6 subnet port
-            //List<Adjacency> erAdjList = getAdjacencyforExtraRoute(vpnId, routeList, ipValue);
-            //if (!erAdjList.isEmpty()) {
-            //    adjList.addAll(erAdjList);
-            //}
-        //}
         return new AdjacenciesBuilder().setAdjacency(adjList).build();
     }
 
     public void unbindKubeProxyService(String interfaceName, TypedWriteTransaction<Datastore.Configuration> tx) {
         tx.delete(buildKubeProxyServicesIId(interfaceName));
     }
+
+    public void updateServiceGatewayList(TypedWriteTransaction<Datastore.Configuration> tx, String serviceGatewayPod,
+                                         String serviceGatewayIp,  String serviceGatewayMac) {
+        InstanceIdentifier<ServiceGateway> serviceGatewayInstanceIdentifier =
+                InstanceIdentifier.builder(ServiceGatewayInfo.class).child(
+                        ServiceGateway.class, new ServiceGatewayKey(serviceGatewayPod)).build();
+        ServiceGateway serviceGateway = new ServiceGatewayBuilder().setGatewayPodName(serviceGatewayPod)
+                .setGatewayPodIpAddress(serviceGatewayIp).setGatewayPodMacAddress(serviceGatewayMac).build();
+        tx.put(serviceGatewayInstanceIdentifier, serviceGateway);
+    }
+
+    private InstanceIdentifier<ServiceGatewayInfo> buildServiceGatewayInstanceIndentifier() {
+        return InstanceIdentifier.builder(ServiceGatewayInfo.class).build();
+    }
+
+    public void updateVpnInterfaceWithExtraRouteAdjacency(TypedReadWriteTransaction<Datastore.Configuration> tx,
+                                                          Services services) throws ExecutionException,
+            InterruptedException {
+        if (services.getClusterIpAddress() == null) {
+            LOG.error("Incorrect input received for extra route. {}", services.getName());
+        } else {
+            LOG.info("update vpn-interface with extra route adjacencies for {}", services.getName());
+            Optional<ServiceGatewayInfo> serviceGatewayList = tx.read(buildServiceGatewayInstanceIndentifier()).get();
+            if (serviceGatewayList.isPresent() && serviceGatewayList.get() != null) {
+                for (ServiceGateway serviceGateway : serviceGatewayList.get().nonnullServiceGateway()) {
+                    String nextHop = serviceGateway.getGatewayPodIpAddress();
+                    IpAddress destination = services.getClusterIpAddress();
+                    String destinationIpValue = destination.getIpv4Address() != null
+                            ? destination.getIpv4Address().getValue() :
+                            destination.getIpv6Address().getValue();
+                    String destinationIpPrefix = destination.getIpv4Address() != null ? destinationIpValue + "/32"
+                            : destinationIpValue + "/128";
+                    String infName = serviceGateway.getGatewayPodName();
+                    if (infName != null) {
+                        LOG.info("Updating extra route for destination {} onto vpn {} with nexthop {} and infName {}",
+                                destination, services.getClusterId(), nextHop, infName);
+                        InstanceIdentifier<Adjacency> path = InstanceIdentifier.builder(VpnInterfaces.class)
+                                .child(VpnInterface.class, new VpnInterfaceKey(infName)).build()
+                                .augmentation(Adjacencies.class).child(Adjacency.class,
+                                        new AdjacencyKey(destinationIpPrefix));
+                        Adjacency erAdj = new AdjacencyBuilder().setIpAddress(destinationIpPrefix)
+                                .setNextHopIpList(Collections.singletonList(nextHop))
+                                .setAdjacencyType(Adjacency.AdjacencyType.ExtraRoute).build();
+                        tx.put(path, erAdj);
+
+                    } else {
+                        LOG.error("Unable to find VPN NextHop interface to apply extra-route destination {} on VPN {} "
+                                + "with nexthop {}", destination, services.getClusterId(), nextHop);
+                    }
+                }
+            }
+        }
+    }
 }