Updates to COE ELAN integration 87/66287/5
authorFaseela K <faseela.k@ericsson.com>
Wed, 6 Dec 2017 18:05:36 +0000 (23:35 +0530)
committerFaseela K <faseela.k@ericsson.com>
Thu, 4 Jan 2018 11:51:33 +0000 (17:21 +0530)
- All pods of same namespace within the same node
  should be part of same ELAN.
- Pods of same namepsace, but under different node
  will not be part of same ELAN.
- Ietf interface as well as elan-interface names
  should have a format namespace:podName to match
  with the external id coming from CNI plugin

Change-Id: Ia33506190299ffb4cc6f2ff47209282ed437cf76
Signed-off-by: Faseela K <faseela.k@ericsson.com>
vpnservice/coe/impl/src/main/java/org/opendaylight/netvirt/coe/listeners/PodListener.java
vpnservice/coe/impl/src/main/java/org/opendaylight/netvirt/coe/utils/CoeUtils.java

index a300ff73abad68c6dad10686a71ea2c55dc071e0..a1d5165187c22797ca711f5bbf72808714229915 100644 (file)
@@ -12,6 +12,7 @@ import com.google.common.util.concurrent.ListenableFuture;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
+import java.util.Objects;
 import java.util.concurrent.Callable;
 import javax.annotation.Nonnull;
 import javax.annotation.PreDestroy;
@@ -29,6 +30,7 @@ import org.opendaylight.netvirt.coe.utils.CoeUtils;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.coe.northbound.pod.rev170611.Coe;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.coe.northbound.pod.rev170611.coe.Pods;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.coe.northbound.pod.rev170611.pod_attributes.Interface;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.instances.ElanInstance;
 import org.opendaylight.yangtools.concepts.ListenerRegistration;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 import org.slf4j.Logger;
@@ -70,72 +72,129 @@ public class PodListener implements DataTreeChangeListener<Pods> {
     }
 
     @Override
-    public void onDataTreeChanged(@Nonnull Collection<DataTreeModification<Pods>> changes) {
-        for (DataTreeModification<Pods> change : changes) {
-            final DataObjectModification<Pods> mod = change.getRootNode();
-
-            switch (mod.getModificationType()) {
-                case DELETE:
-                    delete(mod.getDataBefore());
-                    break;
-                case SUBTREE_MODIFIED:
-                    update(mod.getDataBefore(), mod.getDataAfter());
-                    break;
-                case WRITE:
-                    if (mod.getDataBefore() == null) {
-                        add(mod.getDataAfter());
-                    } else {
-                        update(mod.getDataBefore(), mod.getDataAfter());
-                    }
-                    break;
-                default:
-                    LOG.error("Unhandled modification type " + mod.getModificationType());
-                    break;
-            }
+    public void onDataTreeChanged(@Nonnull Collection<DataTreeModification<Pods>> collection) {
+        collection.stream().forEach(podsDataTreeModification -> {
+            podsDataTreeModification.getRootNode().getModifiedChildren().stream().filter(
+                dataObjectModification -> dataObjectModification.getDataType().equals(Interface.class))
+                    .forEach(dataObjectModification -> onPodInterfacesChanged(
+                            (DataObjectModification<Interface>) dataObjectModification,
+                            podsDataTreeModification.getRootPath().getRootIdentifier(),
+                            podsDataTreeModification.getRootNode()));
         }
+        );
     }
 
-    private void add(Pods podsNew) {
-        Interface podInterface = podsNew.getInterface().get(0);
+    public void onPodInterfacesChanged(final DataObjectModification<Interface> dataObjectModification,
+                                       final InstanceIdentifier<Pods> rootIdentifier,
+                                       DataObjectModification<Pods> rootNode) {
+        Pods pods = rootNode.getDataAfter();
+        Pods podsBefore = rootNode.getDataBefore();
+        Interface podInterfaceBefore = dataObjectModification.getDataBefore();
+        Interface podInterfaceAfter = dataObjectModification.getDataAfter();
+        switch (dataObjectModification.getModificationType()) {
+            case DELETE:
+                remove(pods, podInterfaceBefore);
+                break;
+            case SUBTREE_MODIFIED:
+                update(pods, podsBefore, podInterfaceBefore, podInterfaceAfter);
+                break;
+            case WRITE:
+                if (podInterfaceBefore == null) {
+                    add(pods, podInterfaceAfter);
+                } else {
+                    update(pods, podsBefore, podInterfaceBefore, podInterfaceAfter);
+                }
+                break;
+            default:
+                LOG.error("Unhandled Modificiation Type{} for {}", dataObjectModification.getModificationType(),
+                        rootIdentifier);
+        }
+    }
 
-        String network = podInterface.getNetworkId().getValue();
-        if (network == null) {
-            LOG.warn("pod {} added without a valid network id", podInterface.getUid().getValue());
+    private void add(Pods pods, Interface podInterface) {
+        LOG.trace("Pod added {}",pods);
+        if (pods.getNetworkNS() == null || pods.getHostIpAddress() == null) {
+            LOG.warn("pod {} added with insufficient information to process", pods.getName());
             return;
         }
         // TODO use infrautils caching mechanism to add this info to cache.
 
-        String podInterfaceName = podInterface.getUid().getValue();
-        jobCoordinator.enqueueJob(podInterfaceName, new RendererConfigAddWorker(podInterfaceName, podInterface));
-
+        jobCoordinator.enqueueJob(pods.getName(), new PodConfigAddWorker(dataBroker, pods, podInterface));
     }
 
-    private void update(Pods podsOld, Pods podsNew) {
-        // TODO
+    private void update(Pods podsAfter, Pods podsBefore, Interface podInterfaceBefore, Interface podInterfaceAfter) {
+        LOG.trace("Pod updated before :{}, after :{}",podsBefore, podsAfter);
+        if (!Objects.equals(podsAfter.getNetworkNS(), podsBefore.getNetworkNS())
+                || !Objects.equals(podsAfter.getHostIpAddress(), podsBefore.getHostIpAddress())) {
+            if (podsBefore.getNetworkNS() != null || podsBefore.getHostIpAddress() != null) {
+                // Case where pod is moving from one namespace to another
+                // issue a delete of all previous configuration, and add the new one.
+                jobCoordinator.enqueueJob(podsAfter.getName(), new PodConfigRemoveWorker(dataBroker, podsBefore));
+            }
+            jobCoordinator.enqueueJob(podsAfter.getName(), new PodConfigAddWorker(dataBroker, podsAfter,
+                    podInterfaceAfter));
+        }
+        // TODO use infrautils caching mechanism to add this info to cache.
     }
 
-    private void delete(Pods podsOld) {
-         // TODO
+    private void remove(Pods pods, Interface podInterface) {
+        LOG.trace("Pod reomved {}", pods);
+        if (pods.getNetworkNS() == null || pods.getHostIpAddress() == null) {
+            LOG.warn("pod {} deletion without a valid network id {}", podInterface.getUid().getValue());
+            return;
+        }
+
+        jobCoordinator.enqueueJob(pods.getName(), new PodConfigRemoveWorker(dataBroker, pods));
     }
 
-    private class RendererConfigAddWorker implements Callable<List<ListenableFuture<Void>>> {
-        String podInterfaceName;
-        Interface podInterface;
+    private static class PodConfigAddWorker implements Callable<List<ListenableFuture<Void>>> {
+        private final Pods pods;
+        private final Interface podInterface;
+        private final DataBroker dataBroker;
 
-        RendererConfigAddWorker(String podInterfaceName, Interface podInterface) {
-            this.podInterfaceName = podInterfaceName;
+        PodConfigAddWorker(DataBroker dataBroker, Pods pods, Interface podInterface) {
+            this.pods = pods;
             this.podInterface = podInterface;
+            this.dataBroker = dataBroker;
         }
 
         @Override
         public List<ListenableFuture<Void>> call() {
             LOG.trace("Adding Pod : {}", podInterface);
+            String interfaceName = CoeUtils.buildInterfaceName(pods.getNetworkNS(), pods.getName());
+            WriteTransaction wrtConfigTxn = dataBroker.newWriteOnlyTransaction();
+            String nodeIp = String.valueOf(pods.getHostIpAddress().getValue());
+            ElanInstance elanInstance = CoeUtils.createElanInstanceForTheFirstPodInTheNetwork(
+                    pods.getNetworkNS(), nodeIp, podInterface, wrtConfigTxn, dataBroker);
+            LOG.info("interface creation for pod {}", interfaceName);
+            String portInterfaceName = CoeUtils.createOfPortInterface(interfaceName, podInterface, wrtConfigTxn);
+            LOG.debug("Creating ELAN Interface for pod {}", interfaceName);
+            CoeUtils.createElanInterface(podInterface, portInterfaceName,
+                    elanInstance.getElanInstanceName(), wrtConfigTxn);
+            return Collections.singletonList(wrtConfigTxn.submit());
+        }
+    }
+
+    private static class PodConfigRemoveWorker implements Callable<List<ListenableFuture<Void>>> {
+        private final Pods pods;
+        private final DataBroker dataBroker;
+
+        PodConfigRemoveWorker(DataBroker dataBroker, Pods pods) {
+            this.pods = pods;
+            this.dataBroker = dataBroker;
+        }
+
+        @Override
+        public List<ListenableFuture<Void>> call() {
+            String podInterfaceName = CoeUtils.buildInterfaceName(pods.getNetworkNS(), pods.getName());
+            LOG.trace("Deleting Pod : {}", podInterfaceName);
             WriteTransaction wrtConfigTxn = dataBroker.newWriteOnlyTransaction();
-            CoeUtils.createElanInstanceForTheFirstPodInTheNetwork(podInterface, wrtConfigTxn, dataBroker);
-            LOG.info("interface creation for pod {}", podInterfaceName);
-            String portInterfaceName = CoeUtils.createOfPortInterface(podInterface, wrtConfigTxn, dataBroker);
-            LOG.debug("Creating ELAN Interface for pod {}", podInterfaceName);
-            CoeUtils.createElanInterface(podInterface, portInterfaceName, wrtConfigTxn);
+            LOG.debug("Deleting ELAN Interface for pod {}", podInterfaceName);
+            CoeUtils.deleteElanInterface(podInterfaceName, wrtConfigTxn);
+            LOG.info("interface deletion for pod {}", podInterfaceName);
+            CoeUtils.deleteOfPortInterface(podInterfaceName, wrtConfigTxn);
+            // TODO delete elan-instance if this is the last pod in the network
+            // TODO use infrautils cache to maintain this mapping and to decide on elan-instance deletion
             return Collections.singletonList(wrtConfigTxn.submit());
         }
     }
index 8c6bcf33c446443fc998a0056d144d8e9d3f6570..cd3dbf943cd3563b4dcc9feb0b7d261f02257503 100644 (file)
@@ -41,6 +41,8 @@ import org.slf4j.LoggerFactory;
 
 public final class CoeUtils {
     private static final Logger LOG = LoggerFactory.getLogger(CoeUtils.class);
+
+    private static final String SEPARATOR = ":";
     public static final ImmutableBiMap<NetworkAttributes.NetworkType, Class<? extends SegmentTypeBase>>
             NETWORK_MAP =
             new ImmutableBiMap.Builder<NetworkAttributes.NetworkType, Class<? extends SegmentTypeBase>>()
@@ -60,10 +62,12 @@ public final class CoeUtils {
         return id;
     }
 
+    public static String buildInterfaceName(String networkNS, String podName) {
+        return new StringBuilder().append(networkNS).append(SEPARATOR).append(podName).toString();
+    }
+
     static org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces
-            .Interface buildInterface(org.opendaylight.yang.gen.v1.urn.opendaylight.coe.northbound.pod.rev170611
-                                                         .pod_attributes.Interface podInterface) {
-        String interfaceName = podInterface.getUid().getValue();
+            .Interface buildInterface(String interfaceName) {
         IfL2vlan.L2vlanMode l2VlanMode = IfL2vlan.L2vlanMode.Trunk;
         InterfaceBuilder interfaceBuilder = new InterfaceBuilder();
         IfL2vlanBuilder ifL2vlanBuilder = new IfL2vlanBuilder();
@@ -93,34 +97,43 @@ public final class CoeUtils {
 
     public static void createElanInterface(org.opendaylight.yang.gen.v1.urn.opendaylight.coe
                                                    .northbound.pod.rev170611.pod_attributes.Interface podInterface,
-                                           String name, WriteTransaction wrtConfigTxn) {
-        String elanInstanceName = podInterface.getNetworkId().getValue();
+                                           String elanInterfaceName, String elanInstanceName,
+                                           WriteTransaction wrtConfigTxn) {
         InstanceIdentifier<ElanInterface> id = InstanceIdentifier.builder(ElanInterfaces.class).child(ElanInterface
-                .class, new ElanInterfaceKey(name)).build();
+                .class, new ElanInterfaceKey(elanInterfaceName)).build();
+        // TODO set static mac entries based on pod interface mac
         ElanInterface elanInterface = new ElanInterfaceBuilder().setElanInstanceName(elanInstanceName)
-                .setName(name).setKey(new ElanInterfaceKey(name)).build();
+                .setName(elanInterfaceName).setKey(new ElanInterfaceKey(elanInterfaceName)).build();
         wrtConfigTxn.put(LogicalDatastoreType.CONFIGURATION, id, elanInterface);
         LOG.debug("Creating new ELAN Interface {}", elanInterface);
     }
 
-    public static String createOfPortInterface(org.opendaylight.yang.gen.v1.urn.opendaylight.coe
+    public static void deleteElanInterface(String elanInterfaceName, WriteTransaction wrtConfigTxn) {
+        InstanceIdentifier<ElanInterface> id = InstanceIdentifier.builder(ElanInterfaces.class).child(ElanInterface
+                .class, new ElanInterfaceKey(elanInterfaceName)).build();
+        wrtConfigTxn.delete(LogicalDatastoreType.CONFIGURATION, id);
+        LOG.debug("Deleting ELAN Interface {}", elanInterfaceName);
+    }
+
+    public static String createOfPortInterface(String interfaceName,
+                                               org.opendaylight.yang.gen.v1.urn.opendaylight.coe
                                                        .northbound.pod.rev170611.pod_attributes.Interface podInterface,
-                                               WriteTransaction wrtConfigTxn, DataBroker dataBroker) {
+                                               WriteTransaction wrtConfigTxn) {
         org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface inf =
-                buildInterface(podInterface);
+                buildInterface(interfaceName);
         String infName = inf.getName();
-        LOG.debug("Creating OFPort Interface {}", infName);
+        LOG.info("Creating OFPort Interface {}", infName);
         InstanceIdentifier interfaceIdentifier = CoeUtils.buildVlanInterfaceIdentifier(infName);
-        Optional<Interface> optionalInf = MDSALUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION,
-                interfaceIdentifier);
-        if (!optionalInf.isPresent()) {
-            wrtConfigTxn.put(LogicalDatastoreType.CONFIGURATION, interfaceIdentifier, inf);
-        } else {
-            LOG.warn("Interface {} is already present", infName);
-        }
+        wrtConfigTxn.put(LogicalDatastoreType.CONFIGURATION, interfaceIdentifier, inf);
         return infName;
     }
 
+    public static void deleteOfPortInterface(String infName, WriteTransaction wrtConfigTxn) {
+        LOG.debug("Deleting OFPort Interface {}", infName);
+        InstanceIdentifier interfaceIdentifier = CoeUtils.buildVlanInterfaceIdentifier(infName);
+        wrtConfigTxn.delete(LogicalDatastoreType.CONFIGURATION, interfaceIdentifier);
+    }
+
     static InstanceIdentifier<ElanInstance> createElanInstanceIdentifier(String elanInstanceName) {
         InstanceIdentifier<ElanInstance> id = InstanceIdentifier.builder(ElanInstances.class).child(ElanInstance.class,
                 new ElanInstanceKey(elanInstanceName)).build();
@@ -133,10 +146,15 @@ public final class CoeUtils {
         return CoeUtils.NETWORK_MAP.get(elanInterface.getNetworkType());
     }
 
-    public static ElanInstance createElanInstanceForTheFirstPodInTheNetwork(
-            org.opendaylight.yang.gen.v1.urn.opendaylight.coe.northbound.pod.rev170611.pod_attributes.Interface
-                    podInterface, WriteTransaction wrtConfigTxn, DataBroker dataBroker) {
-        String elanInstanceName = podInterface.getNetworkId().getValue();
+    public static String buildElanInstanceName(String nodeIp, String networkNS) {
+        return new StringBuilder().append(nodeIp).append(SEPARATOR).append(networkNS).toString();
+    }
+
+    public static ElanInstance createElanInstanceForTheFirstPodInTheNetwork(String networkNS, String nodeIp,
+                                                 org.opendaylight.yang.gen.v1.urn.opendaylight.coe.northbound.pod
+                                                         .rev170611.pod_attributes.Interface podInterface,
+                                                 WriteTransaction wrtConfigTxn, DataBroker dataBroker) {
+        String elanInstanceName = buildElanInstanceName(nodeIp, networkNS);
         InstanceIdentifier<ElanInstance> id = createElanInstanceIdentifier(elanInstanceName);
         Optional<ElanInstance> existingElanInstance = MDSALUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION,
                 id);