Listen to node connections and add unis for them 34/42634/4
authorYakir Dorani <yakir.dorani@hpe.com>
Thu, 21 Jul 2016 11:01:54 +0000 (14:01 +0300)
committerYakir Dorani <yakir.dorani@hpe.com>
Thu, 28 Jul 2016 12:19:21 +0000 (15:19 +0300)
Change-Id: I3c807f52db2d2d09d736a1b9a3969bede9c699e9
Signed-off-by: Yakir Dorani <yakir.dorani@hpe.com>
features/pom.xml
netvirt/src/main/java/org/opendaylight/unimgr/mef/netvirt/EvcListener.java
netvirt/src/main/java/org/opendaylight/unimgr/mef/netvirt/EvcUniListener.java [new file with mode: 0644]
netvirt/src/main/java/org/opendaylight/unimgr/mef/netvirt/MdsalUtils.java
netvirt/src/main/java/org/opendaylight/unimgr/mef/netvirt/MefUtils.java [new file with mode: 0644]
netvirt/src/main/java/org/opendaylight/unimgr/mef/netvirt/NetvirtUtils.java
netvirt/src/main/java/org/opendaylight/unimgr/mef/netvirt/NodeConnectorListener.java [new file with mode: 0644]
netvirt/src/main/resources/org/opendaylight/blueprint/netvirt-driver.xml
netvirt/src/test/java/org/opendaylight/unimgr/mef/netvirt/EvcUniListenerTest.java [new file with mode: 0644]
netvirt/src/test/java/org/opendaylight/unimgr/mef/netvirt/TestHelper.java [new file with mode: 0644]

index fc9351e0ae93cb081143693e09869877abbbb921..f56d58b93d64a155bfff06892bab77bfdd384fca 100644 (file)
@@ -30,6 +30,7 @@
     <mdsal.model.version>0.9.0-SNAPSHOT</mdsal.model.version>
     <netconf.version>1.1.0-SNAPSHOT</netconf.version>
     <genius.version>0.1.0-SNAPSHOT</genius.version>
+    <tenantutil.version>0.1.0-SNAPSHOT</tenantutil.version>
     <configfile.directory>etc/opendaylight/karaf</configfile.directory>
   </properties>
   <dependencyManagement>
index 9cd73a4faba611b6f4d8b112f56e363181d9b6f1..9fb71b8d5168c9d54529cf9585847d932aa253ee 100644 (file)
@@ -14,25 +14,14 @@ import java.util.List;
 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
 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.AsyncDataBroker.DataChangeScope;
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
-import org.opendaylight.genius.mdsalutil.AbstractDataChangeListener;
-import org.opendaylight.genius.mdsalutil.MDSALUtil;
 import org.opendaylight.unimgr.api.UnimgrDataTreeChangeListener;
-import org.opendaylight.unimgr.command.EvcAddCommand;
-import org.opendaylight.unimgr.command.EvcRemoveCommand;
-import org.opendaylight.unimgr.command.EvcUpdateCommand;
-import org.opendaylight.unimgr.impl.UnimgrConstants;
 import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.services.rev150526.MefServices;
 import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.services.rev150526.mef.services.MefService;
 import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.services.rev150526.mef.services.mef.service.Evc;
 import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.services.rev150526.mef.services.mef.service.evc.unis.Uni;
 import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.services.rev150526.mef.services.mef.service.evc.unis.uni.EvcUniCeVlans;
 import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.services.rev150526.mef.services.mef.service.evc.unis.uni.evc.uni.ce.vlans.EvcUniCeVlan;
-import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;
-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.Link;
 import org.opendaylight.yangtools.concepts.ListenerRegistration;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 import org.slf4j.Logger;
diff --git a/netvirt/src/main/java/org/opendaylight/unimgr/mef/netvirt/EvcUniListener.java b/netvirt/src/main/java/org/opendaylight/unimgr/mef/netvirt/EvcUniListener.java
new file mode 100644 (file)
index 0000000..28b1eab
--- /dev/null
@@ -0,0 +1,275 @@
+/*
+ * Copyright (c) 2016 Hewlett Packard Enterprise, Co. 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.unimgr.mef.netvirt;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
+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.TransactionCommitFailedException;
+import org.opendaylight.genius.interfacemanager.globals.IfmConstants;
+import org.opendaylight.unimgr.api.UnimgrDataTreeChangeListener;
+import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.interfaces.rev150526.mef.interfaces.unis.uni.PhysicalLayers;
+import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.interfaces.rev150526.mef.interfaces.unis.uni.physical.layers.Links;
+import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.interfaces.rev150526.mef.interfaces.unis.uni.physical.layers.links.Link;
+import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.services.rev150526.MefServices;
+import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.services.rev150526.mef.services.MefService;
+import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.services.rev150526.mef.services.mef.service.Evc;
+import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.services.rev150526.mef.services.mef.service.evc.Unis;
+import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.services.rev150526.mef.services.mef.service.evc.unis.Uni;
+import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.services.rev150526.mef.services.mef.service.evc.unis.uni.EvcUniCeVlans;
+import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.services.rev150526.mef.services.mef.service.evc.unis.uni.evc.uni.ce.vlans.EvcUniCeVlan;
+import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.types.rev150526.VlanIdType;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.Interfaces;
+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.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Objects;
+import com.google.common.base.Optional;
+import com.google.common.collect.Sets;
+import com.google.common.util.concurrent.CheckedFuture;
+
+public class EvcUniListener extends UnimgrDataTreeChangeListener<Uni> {
+    private static final Logger logger = LoggerFactory.getLogger(EvcUniListener.class);
+
+    private ListenerRegistration<EvcUniListener> uniListenerRegistration;
+
+    public EvcUniListener(final DataBroker dataBroker) {
+        super(dataBroker);
+
+        registerListener();
+    }
+
+    @Override
+    public void add(DataTreeModification<Uni> newDataObject) {
+        if (newDataObject.getRootPath() != null && newDataObject.getRootNode() != null) {
+            logger.info("uni {} created", newDataObject.getRootNode().getIdentifier());
+            addUni(newDataObject);
+        }
+    }
+
+    @Override
+    public void remove(DataTreeModification<Uni> removedDataObject) {
+        if (removedDataObject.getRootPath() != null && removedDataObject.getRootNode() != null) {
+            logger.info("uni {} deleted", removedDataObject.getRootNode().getIdentifier());
+            removeUni(removedDataObject);
+        }
+    }
+
+    @Override
+    public void update(DataTreeModification<Uni> modifiedDataObject) {
+        if (modifiedDataObject.getRootPath() != null && modifiedDataObject.getRootNode() != null) {
+            logger.info("uni {} updated", modifiedDataObject.getRootNode().getIdentifier());
+            updateUni(modifiedDataObject);
+        }
+    }
+
+    protected void removeUni(DataTreeModification<Uni> removedDataObject) {
+        try {
+            Uni data = removedDataObject.getRootNode().getDataBefore();
+
+            String uniId = data.getUniId().getValue();
+            WriteTransaction tx = createTransaction();
+            logger.info("Removing trunk {}", uniId);
+            delete(uniId, tx);
+
+            Optional<List<EvcUniCeVlan>> ceVlansOptional = getCeVlans(data);
+            if (!ceVlansOptional.isPresent()) {
+                return;
+            }
+
+            removeTrunkMemberInterfaces(uniId, ceVlansOptional.get(), tx);
+            commitTransaction(tx);
+        } catch (final Exception e) {
+            logger.error("Remove uni failed !", e);
+        }
+    }
+
+    protected void updateUni(DataTreeModification<Uni> modifiedDataObject) {
+        try {
+            Uni original = modifiedDataObject.getRootNode().getDataBefore();
+            Uni update = modifiedDataObject.getRootNode().getDataAfter();
+
+            String uniId = update.getUniId().getValue();
+            WriteTransaction tx = createTransaction();
+            String origTrunkParentName = getTrunkParentName(original);
+            String updatedTrunkParentName = getTrunkParentName(update);
+
+            if (!Objects.equal(origTrunkParentName, updatedTrunkParentName)) {
+                addTrunkInterface(uniId, updatedTrunkParentName, tx);
+            }
+
+            Set<EvcUniCeVlan> origCeVlans = Sets.newHashSet(getCeVlans(original).or(Collections.emptyList()));
+            Set<EvcUniCeVlan> updatedCeVlans = Sets.newHashSet(getCeVlans(update).or(Collections.emptyList()));
+            Iterable<EvcUniCeVlan> removedCeVlans = Sets.difference(origCeVlans, updatedCeVlans);
+            Iterable<EvcUniCeVlan> addedCeVlans = Sets.difference(updatedCeVlans, origCeVlans);
+            removeTrunkMemberInterfaces(uniId, removedCeVlans, tx);
+            addTrunkMemberInterfaces(uniId, addedCeVlans, tx);
+            commitTransaction(tx);
+        } catch (final Exception e) {
+            logger.error("Update uni failed !", e);
+        }
+
+    }
+
+    protected void addUni(DataTreeModification<Uni> newDataObject) {
+        try {
+            Uni data = newDataObject.getRootNode().getDataAfter();
+
+            String uniId = data.getUniId().getValue();
+            WriteTransaction tx = createTransaction();
+            addTrunkInterface(uniId, getTrunkParentName(data), tx);
+
+            Optional<List<EvcUniCeVlan>> ceVlansOptional = getCeVlans(data);
+            if (ceVlansOptional.isPresent()) {
+                addTrunkMemberInterfaces(uniId, ceVlansOptional.get(), tx);
+            }
+
+            commitTransaction(tx);
+        } catch (final Exception e) {
+            logger.error("Add uni failed !", e);
+        }
+
+    }
+
+    private void addTrunkInterface(String interfaceName, String parentInterfaceName, WriteTransaction tx) {
+        logger.info("Adding VLAN trunk {} ParentRef {}", interfaceName, parentInterfaceName);
+        Interface trunkInterface = NetvirtUtils.createTrunkInterface(interfaceName, parentInterfaceName);
+        write(trunkInterface, tx);
+    }
+
+    private void addTrunkMemberInterfaces(String parentInterfaceName, Iterable<EvcUniCeVlan> ceVlans,
+            WriteTransaction tx) {
+        for (EvcUniCeVlan ceVlan : ceVlans) {
+            Long vlanId = ((VlanIdType) ceVlan.getVid()).getValue();
+            String interfaceName = NetvirtUtils.getInterfaceNameForVlan(parentInterfaceName, vlanId.toString());
+            logger.info("Adding VLAN trunk-member {} ParentRef {}", interfaceName, parentInterfaceName);
+            Interface trunkMemberInterface = NetvirtUtils.createTrunkMemberInterface(interfaceName, parentInterfaceName,
+                    vlanId.intValue());
+            write(trunkMemberInterface, tx);
+        }
+    }
+
+    private void removeTrunkMemberInterfaces(String parentInterfaceName, Iterable<EvcUniCeVlan> ceVlans,
+            WriteTransaction tx) {
+        for (EvcUniCeVlan ceVlan : ceVlans) {
+            Long vlanId = ((VlanIdType) ceVlan.getVid()).getValue();
+            String interfaceName = NetvirtUtils.getInterfaceNameForVlan(parentInterfaceName, vlanId.toString());
+            logger.info("Removing VLAN trunk-member {}", interfaceName);
+            delete(interfaceName, tx);
+        }
+    }
+
+    private InstanceIdentifier<Interface> createInterfaceIdentifier(String interfaceName) {
+        return InstanceIdentifier.builder(Interfaces.class).child(Interface.class, new InterfaceKey(interfaceName))
+                .build();
+    }
+
+    private WriteTransaction createTransaction() {
+        WriteTransaction tx = dataBroker.newWriteOnlyTransaction();
+        return tx;
+    }
+
+    private void commitTransaction(WriteTransaction tx) {
+        try {
+            CheckedFuture<Void, TransactionCommitFailedException> futures = tx.submit();
+            futures.get();
+        } catch (Exception e) {
+            logger.error("failed to commit transaction due to exception ", e);
+        }
+    }
+
+    private void write(Interface iface, WriteTransaction tx) {
+        String interfaceName = iface.getName();
+        InstanceIdentifier<Interface> interfaceIdentifier = createInterfaceIdentifier(interfaceName);
+        tx.put(LogicalDatastoreType.CONFIGURATION, interfaceIdentifier, iface, true);
+    }
+
+    private void delete(String interfaceName, WriteTransaction tx) {
+        InstanceIdentifier<Interface> interfaceIdentifier = createInterfaceIdentifier(interfaceName);
+        tx.delete(LogicalDatastoreType.CONFIGURATION, interfaceIdentifier);
+    }
+
+    private String getTrunkParentName(Uni evcUni) {
+
+        Optional<org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.interfaces.rev150526.mef.interfaces.unis.Uni> optional = MdsalUtils
+                .read(dataBroker, LogicalDatastoreType.CONFIGURATION,
+                        MefUtils.getUniInstanceIdentifier(evcUni.getUniId().getValue()));
+
+        if (!optional.isPresent()) {
+            logger.error("A matching Uni doesn't exist for EvcUni {}", evcUni.getUniId());
+            return null;
+        }
+
+        org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.interfaces.rev150526.mef.interfaces.unis.Uni uni = optional
+                .get();
+
+        PhysicalLayers physicalLayers = uni.getPhysicalLayers();
+        if (physicalLayers == null) {
+            logger.warn("Uni {} is missing PhysicalLayers", evcUni.getUniId());
+            return null;
+        }
+
+        Links links = physicalLayers.getLinks();
+        if (links == null || links.getLink() == null) {
+            logger.warn("Uni {} is has no links", evcUni.getUniId());
+            return null;
+        }
+
+        Link link = links.getLink().get(0);
+        String deviceName = link.getDevice().getValue();
+        String interfaceName = link.getInterface().toString();
+        return getDeviceInterfaceName(deviceName, interfaceName);
+    }
+
+    private String getDeviceInterfaceName(String deviceName, String interfaceName) {
+        return deviceName + IfmConstants.OF_URI_SEPARATOR + interfaceName;
+    }
+
+    private Optional<List<EvcUniCeVlan>> getCeVlans(Uni uni) {
+        EvcUniCeVlans ceVlans = uni.getEvcUniCeVlans();
+        if (ceVlans == null) {
+            return Optional.absent();
+        }
+
+        return Optional.fromNullable(ceVlans.getEvcUniCeVlan());
+    }
+
+    private void registerListener() {
+        try {
+            final DataTreeIdentifier<Uni> dataTreeIid = new DataTreeIdentifier<>(LogicalDatastoreType.CONFIGURATION,
+                    getUniTopologyPath());
+            uniListenerRegistration = dataBroker.registerDataTreeChangeListener(dataTreeIid, this);
+            logger.info("UniDataTreeChangeListener created and registered");
+        } catch (final Exception e) {
+            logger.error("Uni DataChange listener registration failed !", e);
+            throw new IllegalStateException("Uni registration Listener failed.", e);
+        }
+    }
+
+    private InstanceIdentifier<Uni> getUniTopologyPath() {
+        return InstanceIdentifier.create(MefServices.class).child(MefService.class).child(Evc.class).child(Unis.class)
+                .child(Uni.class);
+    }
+
+    @Override
+    public void close() throws Exception {
+        // TODO Auto-generated method stub
+
+    }
+}
\ No newline at end of file
index c712adc545488e5a7bf12f83ebb970203652fce1..f32df0408221b0d9a6545adb1257092e1846e13e 100644 (file)
@@ -11,14 +11,17 @@ package org.opendaylight.unimgr.mef.netvirt;
 import java.util.concurrent.ExecutionException;
 
 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.controller.md.sal.common.api.data.TransactionCommitFailedException;
 import org.opendaylight.yangtools.yang.binding.DataObject;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.base.Optional;
 import com.google.common.util.concurrent.CheckedFuture;
 
 public class MdsalUtils {
@@ -82,4 +85,17 @@ public class MdsalUtils {
         return futures;
     }
 
+    public static <T extends DataObject> Optional<T> read(DataBroker broker, LogicalDatastoreType datastoreType,
+            InstanceIdentifier<T> path) {
+        ReadOnlyTransaction tx = broker.newReadOnlyTransaction();
+        Optional<T> result = Optional.absent();
+        try {
+            CheckedFuture<Optional<T>, ReadFailedException> checkedFuture = tx.read(datastoreType, path);
+            result = checkedFuture.get();
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+
+        return result;
+    }
 }
diff --git a/netvirt/src/main/java/org/opendaylight/unimgr/mef/netvirt/MefUtils.java b/netvirt/src/main/java/org/opendaylight/unimgr/mef/netvirt/MefUtils.java
new file mode 100644 (file)
index 0000000..c8ab72d
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2016 Hewlett Packard Enterprise, Co. 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.unimgr.mef.netvirt;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+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.TransactionCommitFailedException;
+import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.interfaces.rev150526.MefInterfaces;
+import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.interfaces.rev150526.mef.interfaces.Unis;
+import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.interfaces.rev150526.mef.interfaces.unis.Uni;
+import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.interfaces.rev150526.mef.interfaces.unis.UniBuilder;
+import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.interfaces.rev150526.mef.interfaces.unis.UniKey;
+import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.interfaces.rev150526.mef.interfaces.unis.uni.PhysicalLayers;
+import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.interfaces.rev150526.mef.interfaces.unis.uni.PhysicalLayersBuilder;
+import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.interfaces.rev150526.mef.interfaces.unis.uni.physical.layers.Links;
+import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.interfaces.rev150526.mef.interfaces.unis.uni.physical.layers.LinksBuilder;
+import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.interfaces.rev150526.mef.interfaces.unis.uni.physical.layers.links.Link;
+import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.interfaces.rev150526.mef.interfaces.unis.uni.physical.layers.links.LinkBuilder;
+import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.interfaces.rev150526.mef.interfaces.unis.uni.physical.layers.links.LinkKey;
+import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.services.rev150526.MefServices;
+import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.services.rev150526.mef.services.MefService;
+import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.services.rev150526.mef.services.mef.service.Evc;
+import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.topology.rev150526.MefTopology;
+import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.topology.rev150526.mef.topology.Devices;
+import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.topology.rev150526.mef.topology.devices.Device;
+import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.topology.rev150526.mef.topology.devices.DeviceBuilder;
+import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.topology.rev150526.mef.topology.devices.DeviceKey;
+import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.topology.rev150526.mef.topology.devices.device.Interfaces;
+import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.topology.rev150526.mef.topology.devices.device.InterfacesBuilder;
+import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.topology.rev150526.mef.topology.devices.device.interfaces.Interface;
+import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.topology.rev150526.mef.topology.devices.device.interfaces.InterfaceBuilder;
+import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.topology.rev150526.mef.topology.devices.device.interfaces.InterfaceKey;
+import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.types.rev150526.DeviceRole;
+import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.types.rev150526.Identifier45;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnector;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.port.statistics.rev131214.node.connector.statistics.and.port.number.map.NodeConnectorStatisticsAndPortNumberMap;
+import org.opendaylight.yangtools.yang.binding.Augmentation;
+import org.opendaylight.yangtools.yang.binding.DataContainer;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.util.concurrent.CheckedFuture;
+
+public final class MefUtils {
+    private static final Logger logger = LoggerFactory.getLogger(MefUtils.class);
+    
+    public static InstanceIdentifier getDeviceInterfaceInstanceIdentifier(String deviceId, String interfaceId) {
+        return InstanceIdentifier.builder(MefTopology.class).child(Devices.class)
+                .child(Device.class, new DeviceKey(new Identifier45(deviceId))).child(Interfaces.class)
+                .child(Interface.class, new InterfaceKey(new Identifier45(interfaceId))).build();
+    }
+
+    public static InstanceIdentifier<Uni> getUniInstanceIdentifier(String uniId) {
+        return InstanceIdentifier.builder(MefInterfaces.class).child(Unis.class)
+                .child(Uni.class, new UniKey(new Identifier45(uniId))).build();
+    }
+
+    public static InstanceIdentifier<org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.services.rev150526.mef.services.mef.service.evc.unis.Uni> getEvcUniInstanceIdentifier(
+            String uniId) {
+        return InstanceIdentifier.builder(MefServices.class).child(MefService.class).child(Evc.class)
+                .child(org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.services.rev150526.mef.services.mef.service.evc.Unis.class)
+                .child(org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.services.rev150526.mef.services.mef.service.evc.unis.Uni.class,
+                        new org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.services.rev150526.mef.services.mef.service.evc.unis.UniKey(
+                                new Identifier45(uniId))).build();
+
+    }
+
+    public static InstanceIdentifier getUniLinkInstanceIdentifier(String uniId, String deviceId, String interfaceId) {
+        return InstanceIdentifier.builder(MefInterfaces.class).child(Unis.class)
+                .child(Uni.class, new UniKey(new Identifier45(uniId))).child(PhysicalLayers.class).child(Links.class)
+                .child(Link.class, new LinkKey(new Identifier45(deviceId), interfaceId)).build();
+    }
+}
index 6fa3b26d600947c65d4e9a5f8a0ae44ea5b82c3d..00d8ef8746087ffe43d97779767d139152f450aa 100644 (file)
@@ -115,9 +115,29 @@ public class NetvirtUtils {
         MdsalUtils.delete(dataBroker, LogicalDatastoreType.CONFIGURATION,
                 getElanInterfaceInstanceIdentifier(interfaceName));
     }
+    
+    public static Interface createTrunkInterface(String interfaceName, String parentIfaceName) {
+        IfL2vlanBuilder ifL2vlanBuilder = new IfL2vlanBuilder();
+        ifL2vlanBuilder.setL2vlanMode(IfL2vlan.L2vlanMode.Trunk);
+        return createInterface(interfaceName, parentIfaceName, ifL2vlanBuilder.build());
+    }
+
+    public static Interface createTrunkMemberInterface(String interfaceName, String parentIfaceName, int vlanId) {
+        IfL2vlanBuilder ifL2vlanBuilder = new IfL2vlanBuilder();
+        ifL2vlanBuilder.setL2vlanMode(IfL2vlan.L2vlanMode.TrunkMember).setVlanId(new VlanId(vlanId));
+        return createInterface(interfaceName, parentIfaceName, ifL2vlanBuilder.build());
+    }
+
+    private static Interface createInterface(String interfaceName, String parentIfaceName, IfL2vlan ifL2vlan) {
+        InterfaceBuilder interfaceBuilder = new InterfaceBuilder();
+        ParentRefsBuilder parentRefsBuilder = new ParentRefsBuilder().setParentInterface(parentIfaceName);
+        interfaceBuilder.setEnabled(true).setName(interfaceName).setType(L2vlan.class).addAugmentation(IfL2vlan
+                .class, ifL2vlan).addAugmentation(ParentRefs.class, parentRefsBuilder.build());
+        return interfaceBuilder.build();
+    }    
 
     public static String getInterfaceNameForVlan(String uniId, String vlanId) {
-        return uniId + "#" + vlanId;
+        return uniId + "." + vlanId;              
     }
 
     private static ElanInstanceBuilder createElanInstance(String instanceName) {
@@ -147,5 +167,5 @@ public class NetvirtUtils {
     private static InstanceIdentifier getUniInterfaceInstanceIdentifier(String interfaceName) {
         return InstanceIdentifier.builder(MefInterfaces.class).child(Unis.class)
                 .child(Uni.class, new UniKey(new Identifier45(interfaceName))).build();
-    }
+    }    
 }
diff --git a/netvirt/src/main/java/org/opendaylight/unimgr/mef/netvirt/NodeConnectorListener.java b/netvirt/src/main/java/org/opendaylight/unimgr/mef/netvirt/NodeConnectorListener.java
new file mode 100644 (file)
index 0000000..6a30869
--- /dev/null
@@ -0,0 +1,215 @@
+/*
+ * Copyright (c) 2016 Hewlett Packard Enterprise, Co. 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.unimgr.mef.netvirt;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
+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.TransactionCommitFailedException;
+import org.opendaylight.unimgr.api.UnimgrDataTreeChangeListener;
+import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.interfaces.rev150526.mef.interfaces.unis.UniBuilder;
+import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.interfaces.rev150526.mef.interfaces.unis.uni.PhysicalLayersBuilder;
+import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.interfaces.rev150526.mef.interfaces.unis.uni.physical.layers.LinksBuilder;
+import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.interfaces.rev150526.mef.interfaces.unis.uni.physical.layers.links.Link;
+import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.interfaces.rev150526.mef.interfaces.unis.uni.physical.layers.links.LinkBuilder;
+import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.topology.rev150526.mef.topology.devices.Device;
+import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.topology.rev150526.mef.topology.devices.DeviceBuilder;
+import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.topology.rev150526.mef.topology.devices.device.interfaces.InterfaceBuilder;
+import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.types.rev150526.Identifier45;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnector;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.util.concurrent.CheckedFuture;
+
+public class NodeConnectorListener extends UnimgrDataTreeChangeListener<FlowCapableNodeConnector> {
+
+    private static final String OF_URI_SEPARATOR = ":";
+    private static final Logger log = LoggerFactory.getLogger(NodeConnectorListener.class);
+    private static final Logger logger = LoggerFactory.getLogger(NodeConnectorListener.class);
+    private static boolean handleRemovedNodeConnectors = false;
+    private ListenerRegistration<NodeConnectorListener> evcListenerRegistration;
+
+    public NodeConnectorListener(final DataBroker dataBroker) {
+        super(dataBroker);
+
+        registerListener();
+    }
+
+    public void registerListener() {
+        try {
+            final DataTreeIdentifier<FlowCapableNodeConnector> dataTreeIid = new DataTreeIdentifier<>(
+                    LogicalDatastoreType.OPERATIONAL, getInstanceIdentifier());
+            evcListenerRegistration = dataBroker.registerDataTreeChangeListener(dataTreeIid, this);
+            log.info("NodeConnectorListener created and registered");
+        } catch (final Exception e) {
+            log.error("Node connector listener registration failed !", e);
+            throw new IllegalStateException("Node connector listener registration failed.", e);
+        }
+    }
+
+    private InstanceIdentifier<FlowCapableNodeConnector> getInstanceIdentifier() {
+        return InstanceIdentifier.create(Nodes.class).child(Node.class).child(NodeConnector.class)
+                .augmentation(FlowCapableNodeConnector.class);
+    }
+
+    @Override
+    public void close() throws Exception {
+        evcListenerRegistration.close();
+    }
+
+    @Override
+    public void add(DataTreeModification<FlowCapableNodeConnector> newDataObject) {
+        if (newDataObject.getRootPath() != null && newDataObject.getRootNode() != null) {
+            log.info("node connector {} created", newDataObject.getRootNode().getIdentifier());
+            addFlowCapableNodeConnector(newDataObject);
+        }
+    }
+
+    @Override
+    public void remove(DataTreeModification<FlowCapableNodeConnector> removedDataObject) {
+        if (removedDataObject.getRootPath() != null && removedDataObject.getRootNode() != null) {
+            log.info("node connector {} deleted", removedDataObject.getRootNode().getIdentifier());
+            removeFlowCapableNodeConnector(removedDataObject);
+        }
+    }
+
+    @Override
+    public void update(DataTreeModification<FlowCapableNodeConnector> modifiedDataObject) {
+        if (modifiedDataObject.getRootPath() != null && modifiedDataObject.getRootNode() != null) {
+            log.info("node connector {} updated", modifiedDataObject.getRootNode().getIdentifier());
+            updateFlowCapableNodeConnector(modifiedDataObject);
+        }
+    }
+
+    private void addFlowCapableNodeConnector(DataTreeModification<FlowCapableNodeConnector> newDataObject) {
+        try {
+            FlowCapableNodeConnector data = newDataObject.getRootNode().getDataAfter();
+
+            String dpnFromNodeConnectorId = getDpnIdFromNodeConnector(newDataObject);
+
+            handleNodeConnectorAdded(dataBroker, dpnFromNodeConnectorId, data);
+        } catch (final Exception e) {
+            log.error("Add node connector failed !", e);
+        }
+    }
+
+    private void removeFlowCapableNodeConnector(DataTreeModification<FlowCapableNodeConnector> removedDataObject) {
+        try {
+            FlowCapableNodeConnector data = removedDataObject.getRootNode().getDataBefore();
+
+            String dpnFromNodeConnectorId = getDpnIdFromNodeConnector(removedDataObject);
+
+            handleNodeConnectorRemoved(dataBroker, dpnFromNodeConnectorId, data);
+        } catch (final Exception e) {
+            log.error("Remove node connector failed !", e);
+        }
+    }
+
+    private void updateFlowCapableNodeConnector(DataTreeModification<FlowCapableNodeConnector> modifiedDataObject) {
+        try {
+            FlowCapableNodeConnector original = modifiedDataObject.getRootNode().getDataBefore();
+            FlowCapableNodeConnector update = modifiedDataObject.getRootNode().getDataAfter();
+            
+            String dpnFromNodeConnectorId = getDpnIdFromNodeConnector(modifiedDataObject);
+
+            handleNodeConnectorUpdated(dataBroker, dpnFromNodeConnectorId, original,update);
+        } catch (final Exception e) {
+            log.error("Update node connector failed !", e);
+        }
+    }
+
+    private String getDpnIdFromNodeConnector(DataTreeModification<FlowCapableNodeConnector> newDataObject) {
+        InstanceIdentifier<FlowCapableNodeConnector> key = newDataObject.getRootPath().getRootIdentifier();
+        NodeConnectorId nodeConnectorId = InstanceIdentifier.keyOf(key.firstIdentifierOf(NodeConnector.class)).getId();
+
+        String dpnFromNodeConnectorId = getDpnFromNodeConnectorId(nodeConnectorId);
+        return dpnFromNodeConnectorId;
+    }
+
+    private static String getDpnFromNodeConnectorId(NodeConnectorId portId) {
+        /*
+         * NodeConnectorId is of form 'openflow:dpnid:portnum'
+         */
+        String[] split = portId.getValue().split(OF_URI_SEPARATOR);
+        return split[1];
+    }
+    
+    private void handleNodeConnectorAdded(DataBroker dataBroker, String dpnId,
+            FlowCapableNodeConnector nodeConnector) {
+
+        WriteTransaction tx = dataBroker.newWriteOnlyTransaction();
+
+        InstanceIdentifier interfacePath = MefUtils.getDeviceInterfaceInstanceIdentifier(dpnId, nodeConnector.getName());
+        InterfaceBuilder interfaceBuilder = new InterfaceBuilder();
+        interfaceBuilder.setPhy(new Identifier45(nodeConnector.getName()));
+        DataObject deviceInterface = interfaceBuilder.build();
+
+        tx.merge(LogicalDatastoreType.CONFIGURATION, interfacePath, deviceInterface, true);
+
+        InstanceIdentifier uniPath = MefUtils.getUniInstanceIdentifier(nodeConnector.getName());
+        UniBuilder uniBuilder = new UniBuilder();
+        uniBuilder.setUniId(new Identifier45(nodeConnector.getName()));
+
+        PhysicalLayersBuilder physicalLayersBuilder = new PhysicalLayersBuilder();
+        LinksBuilder linksBuilder = new LinksBuilder();
+        List<Link> links = new ArrayList();
+        LinkBuilder linkBuilder = new LinkBuilder();
+        linkBuilder.setDevice(new Identifier45(dpnId));
+        linkBuilder.setInterface(nodeConnector.getName());
+        links.add(linkBuilder.build());
+        linksBuilder.setLink(links);
+        physicalLayersBuilder.setLinks(linksBuilder.build());
+        uniBuilder.setPhysicalLayers(physicalLayersBuilder.build());
+        DataObject uni = uniBuilder.build();
+
+        tx.merge(LogicalDatastoreType.CONFIGURATION, uniPath, uni, true);
+        CheckedFuture<Void, TransactionCommitFailedException> futures = tx.submit();
+
+        try {
+            futures.get();
+        } catch (InterruptedException | ExecutionException e) {
+            logger.error("Error writing to datastore (path, data) : ({}, {}), ({}, {})", interfacePath, deviceInterface,
+                    uniPath, uni);
+            throw new RuntimeException(e.getMessage());
+        }
+    }
+
+    private void handleNodeConnectorRemoved(DataBroker dataBroker, String dpnId,
+            FlowCapableNodeConnector nodeConnector) {
+
+        if (!handleRemovedNodeConnectors) {
+            return;
+        }
+
+        MdsalUtils.syncDelete(dataBroker, LogicalDatastoreType.CONFIGURATION,
+                MefUtils.getDeviceInterfaceInstanceIdentifier(dpnId, nodeConnector.getName()));
+
+        MdsalUtils.syncDelete(dataBroker, LogicalDatastoreType.CONFIGURATION,
+                MefUtils.getUniLinkInstanceIdentifier(nodeConnector.getName(), dpnId, nodeConnector.getName()));
+    }
+
+    private void handleNodeConnectorUpdated(DataBroker dataBroker, String dpnFromNodeConnectorId,
+            FlowCapableNodeConnector original, FlowCapableNodeConnector update) {
+
+    }
+}
index 4b763f195748b8b76ad07c1bcd7bb4972d71859b..e1a19eb4fe5fadd535e7f06962ff0b44d9271ab0 100644 (file)
     <argument index="0" ref="dataBroker" />
   </bean>
 
+  <bean class="org.opendaylight.unimgr.mef.netvirt.EvcUniListener">
+    <argument index="0" ref="dataBroker" />
+  </bean>
+
+  <bean class="org.opendaylight.unimgr.mef.netvirt.NodeConnectorListener">
+    <argument index="0" ref="dataBroker" />
+  </bean>
+
 </blueprint>
\ No newline at end of file
diff --git a/netvirt/src/test/java/org/opendaylight/unimgr/mef/netvirt/EvcUniListenerTest.java b/netvirt/src/test/java/org/opendaylight/unimgr/mef/netvirt/EvcUniListenerTest.java
new file mode 100644 (file)
index 0000000..9a105b7
--- /dev/null
@@ -0,0 +1,313 @@
+/*
+ * Copyright (c) 2016 Hewlett Packard Enterprise, Co. 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.unimgr.mef.netvirt;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.Spy;
+import org.mockito.internal.stubbing.answers.Returns;
+import org.mockito.internal.stubbing.answers.ReturnsArgumentAt;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+import org.mockito.stubbing.OngoingStubbing;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.DataObjectModification.ModificationType;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
+import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
+import org.opendaylight.controller.md.sal.binding.api.ReadTransaction;
+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.controller.md.sal.common.api.data.TransactionCommitFailedException;
+import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.interfaces.rev150526.mef.interfaces.unis.uni.CeVlans;
+import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.interfaces.rev150526.mef.interfaces.unis.uni.PhysicalLayers;
+import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.interfaces.rev150526.mef.interfaces.unis.uni.PhysicalLayersBuilder;
+import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.interfaces.rev150526.mef.interfaces.unis.uni.physical.layers.Links;
+import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.interfaces.rev150526.mef.interfaces.unis.uni.physical.layers.LinksBuilder;
+import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.interfaces.rev150526.mef.interfaces.unis.uni.physical.layers.links.Link;
+import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.interfaces.rev150526.mef.interfaces.unis.uni.physical.layers.links.LinkBuilder;
+import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.services.rev150526.mef.services.mef.service.evc.unis.Uni;
+import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.services.rev150526.mef.services.mef.service.evc.unis.UniBuilder;
+import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.services.rev150526.mef.services.mef.service.evc.unis.uni.EvcUniCeVlans;
+import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.services.rev150526.mef.services.mef.service.evc.unis.uni.EvcUniCeVlansBuilder;
+import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.services.rev150526.mef.services.mef.service.evc.unis.uni.evc.uni.ce.vlans.EvcUniCeVlan;
+import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.services.rev150526.mef.services.mef.service.evc.unis.uni.evc.uni.ce.vlans.EvcUniCeVlanBuilder;
+import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.types.rev150526.Identifier45;
+import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.types.rev150526.VlanIdType;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.Interfaces;
+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.yangtools.yang.binding.InstanceIdentifier;
+import org.powermock.core.classloader.annotations.PrepareForTest;
+import org.powermock.modules.junit4.PowerMockRunner;
+
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.CheckedFuture;
+
+import scala.collection.TraversableOnce.OnceCanBuildFrom;
+
+@RunWith(PowerMockRunner.class)
+@PrepareForTest({ LogicalDatastoreType.class, EvcUniListener.class })
+public class EvcUniListenerTest {
+
+    @Mock
+    private DataBroker dataBroker;
+    @Mock
+    private WriteTransaction transaction;
+    private EvcUniListener uniListener;
+
+    @Before
+    public void setUp() {
+        dataBroker = mock(DataBroker.class);
+        uniListener = new EvcUniListener(dataBroker);
+    }
+
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testUniAdded() throws Exception {
+        String uniId = "MMPOP1-ce0-Slot0-Port1";
+        String deviceName = "ce0";
+        String interfaceName = "GigabitEthernet-0-1";
+        Uni uni = evcUni(uniId, deviceName, interfaceName);
+
+        prepareWriteTransaction();
+
+        Collection<DataTreeModification<Uni>> collection = new ArrayList();
+        collection.add(TestHelper.getUni(null, uni, ModificationType.WRITE));
+
+        prepareReadTransaction(uniId, deviceName, interfaceName);
+
+        uniListener.onDataTreeChanged(collection);              
+
+        Interface trunkInterface = NetvirtUtils.createTrunkInterface(uniId, getParentIfaceName(deviceName, interfaceName));
+
+        verifyWriteInterface(trunkInterface);
+    }
+
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testUniWithVlansAdded() throws Exception {
+        String uniId = "MMPOP1-ce0-Slot1-Port1";
+        String deviceName = "ce0";
+        String interfaceName = "GigabitEthernet-1-1";
+        Uni uni = evcUni(uniId, deviceName, interfaceName, 3, 4);
+        prepareWriteTransaction();
+        prepareReadTransaction(uniId, deviceName, interfaceName);
+
+        Collection<DataTreeModification<Uni>> collection = new ArrayList();
+        collection.add(TestHelper.getUni(null, uni, ModificationType.WRITE));
+        uniListener.onDataTreeChanged(collection);
+
+        Interface trunkInterface = NetvirtUtils.createTrunkInterface(uniId, getParentIfaceName(deviceName, interfaceName));
+        verifyWriteInterface(trunkInterface);
+        Interface vlan3Interface = NetvirtUtils.createTrunkMemberInterface(uniId + ".3", uniId, 3);
+        verifyWriteInterface(vlan3Interface);
+        Interface vlan4Interface = NetvirtUtils.createTrunkMemberInterface(uniId + ".4", uniId, 4);
+        verifyWriteInterface(vlan4Interface);
+    }
+
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testUniRemoved() throws Exception {
+        String uniId = "MMPOP1-ce0-Slot0-Port1";
+        String deviceName = "ce0";
+        String interfaceName = "GigabitEthernet-0-1";
+        Uni uni = evcUni(uniId, deviceName, interfaceName);
+
+        prepareWriteTransaction();
+        prepareReadTransaction(uniId, deviceName, interfaceName);
+
+        Collection<DataTreeModification<Uni>> collection = new ArrayList();
+        collection.add(TestHelper.getUni(uni, null, ModificationType.DELETE));
+
+        uniListener.onDataTreeChanged(collection);
+
+        verifyDeleteInterface(uniId);
+    }
+
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testUniWithVlansRemoved() throws Exception {
+        String uniId = "MMPOP1-ce0-Slot1-Port1";
+        String deviceName = "ce0";
+        String interfaceName = "GigabitEthernet-1-1";
+        Uni uni = evcUni(uniId, deviceName, interfaceName, 3, 4);
+        prepareWriteTransaction();
+        prepareReadTransaction(uniId, deviceName, interfaceName);
+
+        Collection<DataTreeModification<Uni>> collection = new ArrayList();
+        collection.add(TestHelper.getUni(uni, null, ModificationType.DELETE));
+
+        uniListener.onDataTreeChanged(collection);
+
+        verifyDeleteInterface(uniId);
+        verifyDeleteInterface(uniId + ".3");
+        verifyDeleteInterface(uniId + ".4");
+    }
+
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testUniInterfaceUpdated() throws Exception {
+        String uniId = "MMPOP1-ce0-Slot0-Port1";
+        String deviceName = "ce0";
+        String origInterfaceName = "GigabitEthernet-0-1";
+        String updatedInterfaceName = "GigabitEthernet-1-1";
+        Uni origUni = evcUni(uniId, deviceName, origInterfaceName);
+        Uni updatedUni = evcUni(uniId, deviceName, updatedInterfaceName);
+        prepareWriteTransaction();
+        prepareReadTransaction(uniId, deviceName, origInterfaceName, updatedInterfaceName);
+
+        Collection<DataTreeModification<Uni>> collection = new ArrayList();
+        collection.add(TestHelper.getUni(origUni, updatedUni, ModificationType.SUBTREE_MODIFIED));
+
+        uniListener.onDataTreeChanged(collection);
+
+        Interface trunkInterface = NetvirtUtils.createTrunkInterface(uniId, getParentIfaceName(deviceName, updatedInterfaceName));
+        verifyWriteInterface(trunkInterface);
+    }
+
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testUniWithVlanInterfaceUpdated() throws Exception {
+        String uniId = "MMPOP1-ce0-Slot0-Port1";
+        String deviceName = "ce0";
+        String origInterfaceName = "GigabitEthernet-0-1";
+        String updatedInterfaceName = "GigabitEthernet-1-1";
+        Uni origUni = evcUni(uniId, deviceName, origInterfaceName, 3, 4);
+        Uni updatedUni = evcUni(uniId, deviceName, updatedInterfaceName, 3, 4);
+        prepareWriteTransaction();
+        prepareReadTransaction(uniId, deviceName, origInterfaceName, updatedInterfaceName);
+
+        Collection<DataTreeModification<Uni>> collection = new ArrayList();
+        collection.add(TestHelper.getUni(origUni, updatedUni, ModificationType.SUBTREE_MODIFIED));
+
+        uniListener.onDataTreeChanged(collection);
+
+        Interface trunkInterface = NetvirtUtils.createTrunkInterface(uniId, getParentIfaceName(deviceName, updatedInterfaceName));
+        verifyWriteInterface(trunkInterface);
+    }
+
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testUniVlanUpdated() throws Exception {
+        String uniId = "MMPOP1-ce0-Slot0-Port1";
+        String deviceName = "ce0";
+        String interfaceName = "GigabitEthernet-1-1";
+        Uni origUni = evcUni(uniId, deviceName, interfaceName, 1, 2, 3);
+        Uni updatedUni = evcUni(uniId, deviceName, interfaceName, 2, 3, 4, 5);
+        prepareWriteTransaction();
+        prepareReadTransaction(uniId, deviceName, interfaceName);
+
+        Collection<DataTreeModification<Uni>> collection = new ArrayList();
+        collection.add(TestHelper.getUni(origUni, updatedUni, ModificationType.SUBTREE_MODIFIED));
+
+        uniListener.onDataTreeChanged(collection);
+
+        Interface vlan4Interface = NetvirtUtils.createTrunkMemberInterface(uniId + ".4", uniId, 4);
+        verifyWriteInterface(vlan4Interface);
+        Interface vlan5Interface = NetvirtUtils.createTrunkMemberInterface(uniId + ".5", uniId, 5);
+        verifyWriteInterface(vlan5Interface);
+        verifyDeleteInterface(uniId + ".1");
+
+    }
+
+    private void verifyWriteInterface(Interface iface) {
+        verify(transaction).put(LogicalDatastoreType.CONFIGURATION, interfaceInstanceIdentifier(iface.getName()), iface,
+                true);
+        verify(transaction).submit();
+    }
+
+    private void verifyDeleteInterface(String interfaceName) {
+        verify(transaction).delete(LogicalDatastoreType.CONFIGURATION, interfaceInstanceIdentifier(interfaceName));
+        verify(transaction).submit();
+    }
+
+    @SuppressWarnings("unchecked")
+    private void prepareWriteTransaction() {
+        transaction = mock(WriteTransaction.class);
+        when(dataBroker.newWriteOnlyTransaction()).thenReturn(transaction);
+        CheckedFuture<Void, TransactionCommitFailedException> future = mock(CheckedFuture.class);
+        when(transaction.submit()).thenReturn(future);
+    }
+
+    @SuppressWarnings("unchecked")
+    private void prepareReadTransaction(String uniId, String deviceName, String... interfaceNames)
+            throws InterruptedException, ExecutionException {
+
+        ReadOnlyTransaction readTransaction = mock(ReadOnlyTransaction.class);
+        when(dataBroker.newReadOnlyTransaction()).thenReturn(readTransaction);
+
+        CheckedFuture<Optional<org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.interfaces.rev150526.mef.interfaces.unis.Uni>, ReadFailedException> future = mock(
+                CheckedFuture.class);
+
+        OngoingStubbing<Optional<org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.interfaces.rev150526.mef.interfaces.unis.Uni>> ongoingStubbing = when(future.get()); 
+        
+        for (String interfaceName : interfaceNames)
+        {
+            ongoingStubbing = ongoingStubbing.thenReturn(Optional.of(uni(uniId, deviceName, interfaceName)));            
+        }        
+
+        when(readTransaction.read(LogicalDatastoreType.CONFIGURATION, MefUtils.getUniInstanceIdentifier(uniId)))
+                .thenReturn(future);
+    }
+
+    private InstanceIdentifier<Interface> interfaceInstanceIdentifier(String interfaceName) {
+        return InstanceIdentifier.builder(Interfaces.class).child(Interface.class, new InterfaceKey(interfaceName))
+                .build();
+    }
+
+    private org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.interfaces.rev150526.mef.interfaces.unis.Uni uni(
+            String uniId, String deviceName, String interfaceName) {
+        Link link = new LinkBuilder().setDevice(new Identifier45(deviceName)).setInterface(interfaceName).build();
+        Links links = new LinksBuilder().setLink(Arrays.asList(link)).build();
+        PhysicalLayers physicalLayers = new PhysicalLayersBuilder().setLinks(links).build();
+        org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.interfaces.rev150526.mef.interfaces.unis.UniBuilder uniBuilder = new org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.interfaces.rev150526.mef.interfaces.unis.UniBuilder()
+                .setUniId(new Identifier45(uniId)).setPhysicalLayers(physicalLayers);
+
+        return uniBuilder.build();
+    }
+
+    private Uni evcUni(String uniId, String deviceName, String interfaceName, long... vlans) {
+        UniBuilder uniBuilder = new UniBuilder().setUniId(new Identifier45(uniId));
+
+        if (vlans != null) {
+            List<EvcUniCeVlan> vlanList = new ArrayList<>();
+            for (long vlan : vlans) {
+                vlanList.add(ceVlan(vlan));
+            }
+            EvcUniCeVlans ceVlans = new EvcUniCeVlansBuilder().setEvcUniCeVlan(vlanList).build();
+            uniBuilder.setEvcUniCeVlans(ceVlans);
+        }
+
+        return uniBuilder.build();
+    }
+
+    private EvcUniCeVlan ceVlan(long vlanId) {
+        return new EvcUniCeVlanBuilder().setVid(new VlanIdType(vlanId)).build();
+    }
+
+    private String getParentIfaceName(String deviceName, String interfaceName) {
+        return deviceName + ":" + interfaceName;
+    }    
+}
diff --git a/netvirt/src/test/java/org/opendaylight/unimgr/mef/netvirt/TestHelper.java b/netvirt/src/test/java/org/opendaylight/unimgr/mef/netvirt/TestHelper.java
new file mode 100644 (file)
index 0000000..4be3699
--- /dev/null
@@ -0,0 +1,84 @@
+package org.opendaylight.unimgr.mef.netvirt;
+
+import java.util.Collection;
+
+import org.opendaylight.controller.md.sal.binding.api.DataObjectModification;
+import org.opendaylight.controller.md.sal.binding.api.DataObjectModification.ModificationType;
+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.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.services.rev150526.mef.services.mef.service.evc.unis.Uni;
+import org.opendaylight.yangtools.yang.binding.Augmentation;
+import org.opendaylight.yangtools.yang.binding.ChildOf;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.Identifiable;
+import org.opendaylight.yangtools.yang.binding.Identifier;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument;
+
+public class TestHelper {
+    public static final DataTreeModification<Uni> getUni(final Uni before, final Uni after, ModificationType modificationType) {
+        final DataTreeIdentifier<Uni> uniDataTreeIid = new DataTreeIdentifier<>(LogicalDatastoreType.CONFIGURATION, MefUtils.getEvcUniInstanceIdentifier("testUni"));
+        final DataObjectModification<Uni> uniDataTreeObj = new DataObjectModification<Uni>() {
+            @Override
+            public Collection<DataObjectModification<? extends DataObject>> getModifiedChildren() {
+                // TODO Auto-generated method stub
+                return null;
+            }
+            @Override
+            public <C extends Identifiable<K> & ChildOf<? super Uni>, K extends Identifier<C>> DataObjectModification<C> getModifiedChildListItem(
+                    Class<C> arg0, K arg1) {
+                // TODO Auto-generated method stub
+                return null;
+            }
+            @Override
+            public <C extends ChildOf<? super Uni>> DataObjectModification<C> getModifiedChildContainer(Class<C> arg0) {
+                // TODO Auto-generated method stub
+                return null;
+            }
+            @Override
+            public DataObjectModification<? extends DataObject> getModifiedChild(PathArgument arg0) {
+                // TODO Auto-generated method stub
+                return null;
+            }
+            @Override
+            public <C extends Augmentation<Uni> & DataObject> DataObjectModification<C> getModifiedAugmentation(
+                    Class<C> arg0) {
+                // TODO Auto-generated method stub
+                return null;
+            }
+            @Override
+            public ModificationType getModificationType() {
+                return modificationType;
+            }
+            @Override
+            public PathArgument getIdentifier() {
+                // TODO Auto-generated method stub
+                return null;
+            }
+            @Override
+            public Class<Uni> getDataType() {
+                // TODO Auto-generated method stub
+                return null;
+            }
+            @Override
+            public Uni getDataBefore() {
+                return before;
+            }
+            @Override
+            public Uni getDataAfter() {
+                return after;
+            }
+        };
+        DataTreeModification<Uni> uniUni = new DataTreeModification<Uni>() {
+            @Override
+            public DataTreeIdentifier<Uni> getRootPath() {
+                return uniDataTreeIid;
+            }
+            @Override
+            public DataObjectModification<Uni> getRootNode() {
+                return uniDataTreeObj;
+            }
+        };
+        return uniUni;
+    }
+}