Fixed bug of inventory management. 49/17049/2
authorShigeru Yasuda <s-yasuda@da.jp.nec.com>
Tue, 24 Mar 2015 11:44:37 +0000 (20:44 +0900)
committerShigeru Yasuda <s-yasuda@da.jp.nec.com>
Tue, 24 Mar 2015 11:52:13 +0000 (20:52 +0900)
  * Fixed incorrect data change scope for inventory change listeners.
  * Fixed bug that ignored inter-swtich links affected by a removed
    node were not removed.
  * Enable integration tests again.

Change-Id: I4249cd34e985b85c0546b2381f60dd08ddfcad30
Signed-off-by: Shigeru Yasuda <s-yasuda@da.jp.nec.com>
23 files changed:
manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/inventory/InventoryMaintainer.java
manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/inventory/InventoryUpdateTask.java [new file with mode: 0644]
manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/inventory/LinkUpdateTask.java [new file with mode: 0644]
manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/inventory/NodeConnectorEventContext.java [deleted file]
manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/inventory/NodeConnectorListener.java
manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/inventory/NodeEventContext.java [deleted file]
manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/inventory/NodeListener.java
manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/inventory/NodeUpdateTask.java [new file with mode: 0644]
manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/inventory/PortUpdateTask.java [new file with mode: 0644]
manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/inventory/TopologyEventContext.java [deleted file]
manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/inventory/TopologyListener.java
manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/util/MiscUtils.java
manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/util/inventory/InventoryUtils.java
manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/util/inventory/LinkEdge.java
manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/util/inventory/InventoryUtilsTest.java
manager/it/ofmock/src/main/java/org/opendaylight/vtn/manager/it/ofmock/impl/AdSalInventory.java
manager/it/ofmock/src/main/java/org/opendaylight/vtn/manager/it/ofmock/impl/MdNodeListener.java [new file with mode: 0644]
manager/it/ofmock/src/main/java/org/opendaylight/vtn/manager/it/ofmock/impl/MdPortListener.java [new file with mode: 0644]
manager/it/ofmock/src/main/java/org/opendaylight/vtn/manager/it/ofmock/impl/OfMockProvider.java
manager/it/ofmock/src/main/java/org/opendaylight/vtn/manager/it/ofmock/impl/ServiceWaiter.java
manager/it/ofmock/src/main/java/org/opendaylight/vtn/manager/it/ofmock/impl/VtnNodeListener.java
manager/it/ofmock/src/main/java/org/opendaylight/vtn/manager/it/ofmock/impl/VtnPortListener.java
manager/it/pom.xml

index d826db01221387a6d4e84962b6d22040ee99593a..d59c809c992a22546046640959d97e7eb5de2374 100644 (file)
@@ -9,44 +9,15 @@
 
 package org.opendaylight.vtn.manager.internal.inventory;
 
-import java.util.List;
-
-import com.google.common.base.Optional;
-import com.google.common.util.concurrent.CheckedFuture;
-
-import org.slf4j.Logger;
-
-import org.opendaylight.vtn.manager.VTNException;
-
-import org.opendaylight.vtn.manager.internal.TxContext;
 import org.opendaylight.vtn.manager.internal.TxQueue;
 import org.opendaylight.vtn.manager.internal.TxTask;
 import org.opendaylight.vtn.manager.internal.util.DataStoreListener;
-import org.opendaylight.vtn.manager.internal.util.DataStoreUtils;
-import org.opendaylight.vtn.manager.internal.util.inventory.InventoryReader;
-import org.opendaylight.vtn.manager.internal.util.inventory.InventoryUtils;
-import org.opendaylight.vtn.manager.internal.util.inventory.SalNode;
-import org.opendaylight.vtn.manager.internal.util.inventory.SalPort;
 
 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
-import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
-import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
 
 import org.opendaylight.yangtools.yang.binding.DataObject;
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
-
-import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.impl.inventory.rev150209.vtn.node.info.VtnPort;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.impl.inventory.rev150209.vtn.port.info.PortLink;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.impl.topology.rev150209.IgnoredLinks;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.impl.topology.rev150209.VtnTopology;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.impl.topology.rev150209.ignored.links.IgnoredLink;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.impl.topology.rev150209.vtn.topology.VtnLink;
-
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
-
-import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.LinkId;
 
 /**
  * Base class for MD-SAL data change listeners that maintain the VTN inventory
@@ -97,298 +68,4 @@ public abstract class InventoryMaintainer<T extends DataObject, C>
     protected final void submitInitial(TxTask<?> task) {
         txQueue.postFirst(task);
     }
-
-    /**
-     * Read the specified VTN port asynchronously.
-     *
-     * @param tx     A {@link ReadWriteTransaction} instance.
-     * @param sport  A {@link SalPort} instance corresponding to the VTN port.
-     * @return  A future associated with a MD-SAL datastore read transaction.
-     */
-    protected final CheckedFuture<Optional<VtnPort>, ReadFailedException> read(
-        ReadWriteTransaction tx, SalPort sport) {
-        InstanceIdentifier<VtnPort> path = sport.getVtnPortIdentifier();
-        return tx.read(LogicalDatastoreType.OPERATIONAL, path);
-    }
-
-    /**
-     * Determine whether the given VTN port is present or not.
-     *
-     * @param tx     A {@link ReadWriteTransaction} instance.
-     * @param sport  A {@link SalPort} instance corresponding to the VTN port.
-     * @return  {@code true} only if the specified VTN port is present.
-     * @throws VTNException
-     *    An error occurred.
-     */
-    protected final boolean isPresent(ReadWriteTransaction tx, SalPort sport)
-        throws VTNException {
-        InstanceIdentifier<VtnPort> path = sport.getVtnPortIdentifier();
-        LogicalDatastoreType oper = LogicalDatastoreType.OPERATIONAL;
-        return DataStoreUtils.read(tx, oper, path).isPresent();
-    }
-
-    /**
-     * Add the given inter-switch link information into the VTN inventory data.
-     *
-     * @param tx   A {@link ReadWriteTransaction} instance.
-     * @param lid  The identifier of the created link.
-     * @param src  A {@link SalPort} instance corresponding to the source
-     *             of the created link.
-     * @param dst  A {@link SalPort} instance corresponding to the destination
-     *             of the created link.
-     * @return  {@code true} if the link was added to the vtn-topology list.
-     *          {@code false} if the link was added to the ignored-links list.
-     * @throws VTNException
-     *    An error occurred.
-     */
-    protected final boolean addVtnLink(ReadWriteTransaction tx, LinkId lid,
-                                       SalPort src, SalPort dst)
-        throws VTNException {
-        // Determine whether the VTN port for both termination points are
-        // present or not.
-        CheckedFuture<Optional<VtnPort>, ReadFailedException> sf =
-            read(tx, src);
-        CheckedFuture<Optional<VtnPort>, ReadFailedException> df =
-            read(tx, dst);
-        boolean srcPresent = DataStoreUtils.read(sf).isPresent();
-        boolean dstPresent = DataStoreUtils.read(df).isPresent();
-
-        boolean ret;
-        if (srcPresent && dstPresent) {
-            // Create link information.
-            createVtnLink(tx, lid, src, dst);
-            ret = true;
-        } else {
-            // Put link information into ignored link list.
-            InstanceIdentifier<IgnoredLink> key =
-                InventoryUtils.toIgnoredLinkIdentifier(lid);
-            IgnoredLink ilink =
-                InventoryUtils.toIgnoredLinkBuilder(lid, src, dst).build();
-            tx.merge(LogicalDatastoreType.OPERATIONAL, key, ilink, true);
-            ret = false;
-        }
-
-        return ret;
-    }
-
-    /**
-     * Remove all VTN links affected by the the removed VTN node.
-     *
-     * @param tx     A {@link ReadWriteTransaction} instance.
-     * @param snode  A {@link SalNode} instance corresponding to the removed
-     *               VTN node.
-     * @throws VTNException
-     *    An error occurred.
-     */
-    protected final void removeVtnLink(ReadWriteTransaction tx, SalNode snode)
-        throws VTNException {
-        removeVtnTopologyLink(tx, snode);
-        removeIgnoredLink(tx, snode);
-    }
-
-    /**
-     * Remove all VTN links in vtn-topology affected by the removed VTN node.
-     *
-     * @param tx     A {@link ReadWriteTransaction} instance.
-     * @param snode  A {@link SalNode} instance corresponding to the removed
-     *               VTN node.
-     * @throws VTNException
-     *    An error occurred.
-     */
-    protected final void removeVtnTopologyLink(ReadWriteTransaction tx,
-                                               SalNode snode)
-        throws VTNException {
-        InstanceIdentifier<VtnTopology> topoPath =
-            InstanceIdentifier.create(VtnTopology.class);
-        LogicalDatastoreType oper = LogicalDatastoreType.OPERATIONAL;
-        VtnTopology topology = DataStoreUtils.read(tx, oper, topoPath).
-            orNull();
-        if (topology == null) {
-            return;
-        }
-
-        List<VtnLink> links = topology.getVtnLink();
-        if (links == null) {
-            return;
-        }
-
-        long dpid = snode.getNodeNumber();
-        for (VtnLink vlink: links) {
-            LinkId lid = vlink.getLinkId();
-            SalPort src = SalPort.create(vlink.getSource());
-            SalPort dst = SalPort.create(vlink.getDestination());
-            long srcDpid = src.getNodeNumber();
-            long dstDpid = dst.getNodeNumber();
-
-            boolean rmLink = false;
-            if (srcDpid == dpid) {
-                rmLink = true;
-                if (dstDpid != dpid) {
-                    removePortLink(tx, dst, lid);
-                }
-            } else if (dstDpid == dpid) {
-                rmLink = true;
-                removePortLink(tx, src, lid);
-            }
-
-            if (rmLink) {
-                InstanceIdentifier<VtnLink> lpath =
-                    InventoryUtils.toVtnLinkIdentifier(lid);
-                tx.delete(oper, lpath);
-            }
-        }
-    }
-
-    /**
-     * Remove all VTN links in ignored-links affected by the removed VTN node.
-     *
-     * @param tx     A {@link ReadWriteTransaction} instance.
-     * @param snode  A {@link SalNode} instance corresponding to the removed
-     *               VTN node.
-     * @throws VTNException
-     *    An error occurred.
-     */
-    protected final void removeIgnoredLink(ReadWriteTransaction tx,
-                                           SalNode snode)
-        throws VTNException {
-        InstanceIdentifier<IgnoredLinks> igPath =
-            InstanceIdentifier.create(IgnoredLinks.class);
-        LogicalDatastoreType oper = LogicalDatastoreType.OPERATIONAL;
-        IgnoredLinks igList = DataStoreUtils.read(tx, oper, igPath).orNull();
-        if (igList == null) {
-            return;
-        }
-
-        List<IgnoredLink> links = igList.getIgnoredLink();
-        if (links == null) {
-            return;
-        }
-
-        long dpid = snode.getNodeNumber();
-        for (IgnoredLink vlink: links) {
-            LinkId lid = vlink.getLinkId();
-            SalPort src = SalPort.create(vlink.getSource());
-            SalPort dst = SalPort.create(vlink.getDestination());
-            long srcDpid = src.getNodeNumber();
-            long dstDpid = dst.getNodeNumber();
-
-            if (srcDpid == dpid || dstDpid == dpid) {
-                InstanceIdentifier<VtnLink> lpath =
-                    InventoryUtils.toVtnLinkIdentifier(lid);
-                tx.delete(LogicalDatastoreType.OPERATIONAL, lpath);
-            }
-        }
-    }
-
-    /**
-     * Remove all VTN links affected by the removed VTN port.
-     *
-     * @param tx     A {@link ReadWriteTransaction} instance.
-     * @param vport  A {@link VtnPort} instance corresponding to the removed
-     *               VTN port.
-     */
-    protected final void removeVtnLink(ReadWriteTransaction tx,
-                                       VtnPort vport) {
-        List<PortLink> links = vport.getPortLink();
-        if (links == null) {
-            return;
-        }
-
-        for (PortLink plink: links) {
-            LinkId lid = plink.getLinkId();
-            NodeConnectorId peer = plink.getPeer();
-            SalPort p = SalPort.create(peer);
-            removePortLink(tx, p, lid);
-
-            InstanceIdentifier<VtnLink> lpath =
-                InventoryUtils.toVtnLinkIdentifier(lid);
-            tx.delete(LogicalDatastoreType.OPERATIONAL, lpath);
-        }
-    }
-
-    /**
-     * Remove the specified port link.
-     *
-     * @param tx     A {@link ReadWriteTransaction} instance.
-     * @param sport  A {@link SalPort} instance corresponding to a VTN port.
-     * @param lid    A {@link LinkId} that specifies inter-switch link to be
-     *               removed.
-     */
-    protected final void removePortLink(ReadWriteTransaction tx, SalPort sport,
-                                        LinkId lid) {
-        InstanceIdentifier<PortLink> path = sport.getPortLinkIdentifier(lid);
-        tx.delete(LogicalDatastoreType.OPERATIONAL, path);
-    }
-
-    /**
-     * Try to resolve ignored inter-switch links.
-     *
-     * @param ctx     A runtime context for transaction task.
-     * @param logger  A {@link Logger} instance.
-     * @throws VTNException
-     *    An error occurred.
-     */
-    protected final void resolveIgnoredLinks(TxContext ctx, Logger logger)
-        throws VTNException {
-        // Read all ignored inter-switch links.
-        InstanceIdentifier<IgnoredLinks> igPath =
-            InstanceIdentifier.create(IgnoredLinks.class);
-        ReadWriteTransaction tx = ctx.getReadWriteTransaction();
-        LogicalDatastoreType oper = LogicalDatastoreType.OPERATIONAL;
-        IgnoredLinks igLinks = DataStoreUtils.read(tx, oper, igPath).orNull();
-        if (igLinks == null) {
-            return;
-        }
-
-        List<IgnoredLink> igList = igLinks.getIgnoredLink();
-        if (igList == null) {
-            return;
-        }
-
-        InventoryReader reader = ctx.getInventoryReader();
-        for (IgnoredLink ignored: igList) {
-            SalPort src = SalPort.create(ignored.getSource());
-            SalPort dst = SalPort.create(ignored.getDestination());
-            if (reader.get(src) != null && reader.get(dst) != null) {
-                // Move this link to vtn-topology.
-                LinkId lid = ignored.getLinkId();
-                InstanceIdentifier<IgnoredLink> ipath =
-                    InventoryUtils.toIgnoredLinkIdentifier(lid);
-                tx.delete(oper, ipath);
-                createVtnLink(tx, lid, src, dst);
-                logger.info("Inter-switch link has been resolved: {}: {} -> {}",
-                            lid.getValue(), src, dst);
-            }
-        }
-    }
-
-    /**
-     * Create a VTN link information.
-     *
-     * @param tx   A {@link ReadWriteTransaction} instance.
-     * @param lid  The identifier of the created link.
-     * @param src  A {@link SalPort} instance corresponding to the source
-     *             of the created link.
-     * @param dst  A {@link SalPort} instance corresponding to the destination
-     *             of the created link.
-     */
-    private void createVtnLink(ReadWriteTransaction tx, LinkId lid,
-                               SalPort src, SalPort dst) {
-        // Put the link information into vtn-topology list.
-        InstanceIdentifier<VtnLink> key =
-            InventoryUtils.toVtnLinkIdentifier(lid);
-        VtnLink vlink = InventoryUtils.toVtnLinkBuilder(lid, src, dst).build();
-        LogicalDatastoreType oper = LogicalDatastoreType.OPERATIONAL;
-        tx.merge(oper, key, vlink, true);
-
-        // Create source port link.
-        InstanceIdentifier<PortLink> pkey = src.getPortLinkIdentifier(lid);
-        PortLink plink = InventoryUtils.toPortLinkBuilder(lid, dst).build();
-        tx.merge(oper, pkey, plink, true);
-
-        // Create destination port link.
-        pkey = dst.getPortLinkIdentifier(lid);
-        plink = InventoryUtils.toPortLinkBuilder(lid, src).build();
-        tx.merge(oper, pkey, plink, true);
-    }
 }
diff --git a/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/inventory/InventoryUpdateTask.java b/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/inventory/InventoryUpdateTask.java
new file mode 100644 (file)
index 0000000..06988f8
--- /dev/null
@@ -0,0 +1,158 @@
+/*
+ * Copyright (c) 2015 NEC Corporation
+ * 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.vtn.manager.internal.inventory;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import com.google.common.base.Optional;
+
+import org.slf4j.Logger;
+
+import org.opendaylight.vtn.manager.VTNException;
+
+import org.opendaylight.vtn.manager.internal.TxContext;
+import org.opendaylight.vtn.manager.internal.util.DataStoreUtils;
+import org.opendaylight.vtn.manager.internal.util.tx.AbstractTxTask;
+
+import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ * Base class for tasks to update VTN inventory information.
+ *
+ * @param <T>  The type of MD-SAL inventory to listen.
+ * @param <L>  The type of instance which represents the location of the
+ *             target inventory.
+ */
+abstract class InventoryUpdateTask<T extends DataObject, L>
+    extends AbstractTxTask<Void> {
+    /**
+     * A map that keeps locations of notified inventories.
+     */
+    private final Map<L, InstanceIdentifier<T>>  updated = new HashMap<>();
+
+    /**
+     * A logger instance.
+     */
+    private final Logger  logger;
+
+    /**
+     * Construct a new instance.
+     *
+     * @param log  A {@link Logger} instance.
+     */
+    InventoryUpdateTask(Logger log) {
+        logger = log;
+    }
+
+    /**
+     * Add an inventory information notified by a data change event.
+     *
+     * @param path  Path to the target instance.
+     * @param loc   An object that represents the location of the target
+     *              inventory.
+     */
+    final void addUpdated(InstanceIdentifier<T> path, L loc) {
+        updated.put(loc, path);
+    }
+
+    /**
+     * Determine whether this task contains at least one notification or not.
+     *
+     * @return  {@code true} only if this instance contains at least one
+     *          notification.
+     */
+    final boolean hasUpdates() {
+        return !updated.isEmpty();
+    }
+
+    /**
+     * Return a {@link Logger} instance.
+     *
+     * @return  A {@link Logger} instance.
+     */
+    final Logger getLogger() {
+        return logger;
+    }
+
+    /**
+     * Add an inventory information notified by a data change event.
+     *
+     * @param ctx   A {@link TxContext} instance.
+     * @param tx    A {@link ReadWriteTransaction} instance.
+     * @param loc   An object that represents the location of the target
+     *              inventory.
+     * @param path  Path to the target inventory in the MD-SAL datastore.
+     * @param data  An inventory data in the MD-SAL datastore.
+     * @throws VTNException  An error occurred.
+     */
+    protected abstract void add(TxContext ctx, ReadWriteTransaction tx,
+                                L loc, InstanceIdentifier<T> path, T data)
+        throws VTNException;
+
+    /**
+     * Remove an inventory information notified by a data change event.
+     *
+     * @param ctx   A {@link TxContext} instance.
+     * @param tx    A {@link ReadWriteTransaction} instance.
+     * @param loc   An object that represents the location of the target
+     *              inventory.
+     * @throws VTNException  An error occurred.
+     */
+    protected abstract void remove(TxContext ctx, ReadWriteTransaction tx,
+                                   L loc)
+        throws VTNException;
+
+    /**
+     * A post-event hook which will be invoked after event processing.
+     *
+     * @param ctx    A {@link TxContext} instance.
+     * @param added  {@code true} is passed if at least one inventory
+     *               information has been created.
+     * @throws VTNException  An error occurred.
+     */
+    protected abstract void fixUp(TxContext ctx, boolean added)
+        throws VTNException;
+
+    // TxTask
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public final Void execute(TxContext ctx) throws VTNException {
+        ReadWriteTransaction tx = ctx.getReadWriteTransaction();
+        LogicalDatastoreType oper = LogicalDatastoreType.OPERATIONAL;
+        boolean added = false;
+        for (Map.Entry<L, InstanceIdentifier<T>> entry: updated.entrySet()) {
+            // Read the notified inventory.
+            L loc = entry.getKey();
+            InstanceIdentifier<T> path = entry.getValue();
+            Optional<T> opt = DataStoreUtils.read(tx, oper, path);
+            if (opt.isPresent()) {
+                // Add the inventory information corresponding to the target
+                // inventory.
+                add(ctx, tx, loc, path, opt.get());
+                added = true;
+            } else {
+                // Remove the inventory information corresponding to the target
+                // inventory.
+                remove(ctx, tx, loc);
+            }
+        }
+
+        fixUp(ctx, added);
+        return null;
+    }
+}
diff --git a/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/inventory/LinkUpdateTask.java b/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/inventory/LinkUpdateTask.java
new file mode 100644 (file)
index 0000000..ca0890d
--- /dev/null
@@ -0,0 +1,183 @@
+/*
+ * Copyright (c) 2015 NEC Corporation
+ * 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.vtn.manager.internal.inventory;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import com.google.common.base.Optional;
+
+import org.slf4j.Logger;
+
+import org.opendaylight.vtn.manager.VTNException;
+
+import org.opendaylight.vtn.manager.internal.TxContext;
+import org.opendaylight.vtn.manager.internal.VTNManagerProvider;
+import org.opendaylight.vtn.manager.internal.util.DataStoreUtils;
+import org.opendaylight.vtn.manager.internal.util.inventory.InventoryReader;
+import org.opendaylight.vtn.manager.internal.util.inventory.InventoryUtils;
+import org.opendaylight.vtn.manager.internal.util.inventory.LinkEdge;
+import org.opendaylight.vtn.manager.internal.util.inventory.SalPort;
+import org.opendaylight.vtn.manager.internal.util.tx.AbstractTxTask;
+
+import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.impl.topology.rev150209.ignored.links.IgnoredLink;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.impl.topology.rev150209.vtn.topology.VtnLink;
+
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.LinkId;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Link;
+
+/**
+ * A MD-SAL datastore transaction task that updates VTN topology information.
+ */
+final class LinkUpdateTask extends AbstractTxTask<Void> {
+    /**
+     * A set of paths to updated inter-switch links.
+     */
+    private final Set<InstanceIdentifier<Link>>  updated = new HashSet<>();
+
+    /**
+     * A logger instance.
+     */
+    private final Logger  logger;
+
+    /**
+     * Construct a new instance.
+     *
+     * @param log  A {@link Logger} instance.
+     */
+    LinkUpdateTask(Logger log) {
+        logger = log;
+    }
+
+    /**
+     * Add a link information notified by a data change event.
+     *
+     * @param path  Path to the target link.
+     */
+    void addUpdated(InstanceIdentifier<Link> path) {
+        updated.add(path);
+    }
+
+    /**
+     * Determine whether this task contains at least one notification or not.
+     *
+     * @return  {@code true} only if this instance contains at least one
+     *          notification.
+     */
+    boolean hasUpdates() {
+        return !updated.isEmpty();
+    }
+
+    /**
+     * Add a VTN link information corresponding to the given MD-SAL
+     * inter-switch link.
+     *
+     * @param tx      A {@link ReadWriteTransaction} instance.
+     * @param reader  An {@link InventoryReader} instance.
+     * @param link    A {@link Link} instance.
+     * @throws VTNException  An error occurred.
+     */
+    private void add(ReadWriteTransaction tx, InventoryReader reader,
+                     Link link) throws VTNException {
+        LinkEdge le;
+        try {
+            le = new LinkEdge(link);
+        } catch (RuntimeException e) {
+            logger.debug("Ignore unsupported inter-switch link: " + link, e);
+            return;
+        }
+
+        LinkId lid = link.getLinkId();
+        SalPort src = le.getSourcePort();
+        SalPort dst = le.getDestinationPort();
+        if (!InventoryUtils.addVtnLink(tx, reader, lid, src, dst)) {
+            logger.warn("Ignore inter-switch link: {}: {} -> {}",
+                        lid.getValue(), src, dst);
+        }
+    }
+
+    /**
+     * Remove a VTN link information corresponding to the given MD-SAL
+     * inter-switch link.
+     *
+     * @param tx    A {@link ReadWriteTransaction} instance.
+     * @param path  Path to the removed inter-switch link.
+     * @throws VTNException  An error occurred.
+     */
+    private void remove(ReadWriteTransaction tx, InstanceIdentifier<Link> path)
+        throws VTNException {
+        LinkId lid = InventoryUtils.getLinkId(path);
+        if (lid == null) {
+            logger.warn("Ignore invalid inter-switch link path: {}", path);
+            return;
+        }
+
+        // Read the VTN link information corresponding to the given link.
+        LogicalDatastoreType oper = LogicalDatastoreType.OPERATIONAL;
+        InstanceIdentifier<VtnLink> lpath =
+            InventoryUtils.toVtnLinkIdentifier(lid);
+        Optional<VtnLink> opt = DataStoreUtils.read(tx, oper, lpath);
+        if (opt.isPresent()) {
+            VtnLink vlink = opt.get();
+            SalPort src = SalPort.create(vlink.getSource());
+            SalPort dst = SalPort.create(vlink.getDestination());
+
+            // Remove the link from vtn-topology list.
+            tx.delete(oper, lpath);
+
+            // Remove port links.
+            InventoryUtils.removePortLink(tx, src, lid);
+            InventoryUtils.removePortLink(tx, dst, lid);
+        } else {
+            // Remove the link from ignored-links list.
+            InstanceIdentifier<IgnoredLink> ipath =
+                InventoryUtils.toIgnoredLinkIdentifier(lid);
+            DataStoreUtils.delete(tx, oper, ipath);
+        }
+    }
+
+    // TxTask
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public Void execute(TxContext ctx) throws VTNException {
+        ReadWriteTransaction tx = ctx.getReadWriteTransaction();
+        LogicalDatastoreType oper = LogicalDatastoreType.OPERATIONAL;
+        InventoryReader reader = ctx.getInventoryReader();
+        for (InstanceIdentifier<Link> path: updated) {
+            // Read the notified link.
+            Optional<Link> opt = DataStoreUtils.read(tx, oper, path);
+            if (opt.isPresent()) {
+                // Add the inter-switch link information.
+                add(tx, reader, opt.get());
+            } else {
+                // Remove the inter-swtich link information.
+                remove(tx, path);
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void onFailure(VTNManagerProvider provider, Throwable t) {
+        logger.error("Failed to update VTN link information.", t);
+    }
+}
diff --git a/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/inventory/NodeConnectorEventContext.java b/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/inventory/NodeConnectorEventContext.java
deleted file mode 100644 (file)
index a2860f8..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (c) 2015 NEC Corporation
- * 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.vtn.manager.internal.inventory;
-
-import org.opendaylight.vtn.manager.internal.TxTask;
-
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
-
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector;
-
-/**
- * An event context used to handle node connector events.
- */
-interface NodeConnectorEventContext extends TxTask<Void> {
-    /**
-     * Add the given node to the node map to be updated.
-     *
-     * @param path   The identifier of the node connector to be added.
-     * @param nc     A {@link NodeConnector} instance to be added
-     * @param label  A label usd only for logging.
-     */
-    void addUpdated(InstanceIdentifier<NodeConnector> path, NodeConnector nc,
-                    String label);
-
-    /**
-     * Add the given node to the node map to be removed.
-     *
-     * @param path  The identifier of the node connector to be added.
-     * @param nc    A {@link NodeConnector} instance to be added.
-     */
-    void addRemoved(InstanceIdentifier<NodeConnector> path, NodeConnector nc);
-
-    /**
-     * Determine whether this instance contains at least one port information
-     * to be updated or removed.
-     *
-     * @return  {@code true} only if this instance contains at least one port
-     *          information to be updated or removed.
-     */
-    boolean hasPort();
-}
index ae44a44a5864be7f37c67ae30de223eca1de93a8..ab2e32de8f9e1be215b146abd164329e28b11038 100644 (file)
@@ -9,40 +9,26 @@
 
 package org.opendaylight.vtn.manager.internal.inventory;
 
-import java.util.HashMap;
-import java.util.Map;
 import java.util.Set;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import org.opendaylight.vtn.manager.VTNException;
-
-import org.opendaylight.vtn.manager.internal.TxContext;
 import org.opendaylight.vtn.manager.internal.TxQueue;
-import org.opendaylight.vtn.manager.internal.VTNManagerProvider;
-import org.opendaylight.vtn.manager.internal.util.DataStoreUtils;
-import org.opendaylight.vtn.manager.internal.util.inventory.InventoryReader;
+import org.opendaylight.vtn.manager.internal.util.MiscUtils;
 import org.opendaylight.vtn.manager.internal.util.inventory.InventoryUtils;
-import org.opendaylight.vtn.manager.internal.util.inventory.SalNode;
 import org.opendaylight.vtn.manager.internal.util.inventory.SalPort;
-import org.opendaylight.vtn.manager.internal.util.tx.AbstractTxTask;
 
 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
-import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
-import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
 
 import org.opendaylight.yangtools.yang.binding.DataObject;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 
-import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.impl.inventory.rev150209.VtnOpenflowVersion;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.impl.inventory.rev150209.vtn.node.info.VtnPort;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.impl.inventory.rev150209.vtn.nodes.VtnNode;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.impl.inventory.rev150209.vtn.nodes.VtnNodeBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.types.rev150209.VtnUpdateType;
 
+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;
@@ -52,199 +38,13 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.N
  * Listener class that listens the change of MD-SAL node connectors.
  */
 public final class NodeConnectorListener
-    extends InventoryMaintainer<NodeConnector, NodeConnectorEventContext> {
+    extends InventoryMaintainer<FlowCapableNodeConnector, PortUpdateTask> {
     /**
      * Logger instance.
      */
     private static final Logger  LOG =
         LoggerFactory.getLogger(NodeConnectorListener.class);
 
-    /**
-     * MD-SAL transaction task that updates the VTN port.
-     */
-    private class PortUpdatedTask extends AbstractTxTask<Void>
-        implements NodeConnectorEventContext {
-        /**
-         * A map that keeps created or updated MD-SAL node connectors.
-         */
-        private final Map<InstanceIdentifier<NodeConnector>, SalPort> updated =
-            new HashMap<>();
-
-        /**
-         * A map that keeps removed MD-SAL node connectors.
-         p*/
-        private final Map<InstanceIdentifier<NodeConnector>, SalPort> removed =
-            new HashMap<>();
-
-        /**
-         * Update the given node connector in the VTN inventory datastore.
-         *
-         * @param ctx    A MD-SAL datastore transaction context.
-         * @param tx     A {@link ReadWriteTransaction} instance.
-         * @param path   Instance identifier of the updated MD-SAL node
-         *               connector.
-         * @param sport  A {@link SalPort} instance corresponding to the
-         *               updated MD-SAL node connector.
-         * @throws VTNException  An error occurred.
-         */
-        private void add(TxContext ctx, ReadWriteTransaction tx,
-                         InstanceIdentifier<NodeConnector> path,
-                         SalPort sport) throws VTNException {
-            // Read MD-SAL node connector from the datastore.
-            LogicalDatastoreType oper = LogicalDatastoreType.OPERATIONAL;
-            NodeConnector nc = DataStoreUtils.read(tx, oper, path).orNull();
-            if (nc == null) {
-                LOG.debug("Ignore bogus event: {}", sport);
-                return;
-            }
-
-            // Read VTN node from the datastore.
-            SalNode snode = sport.getSalNode();
-            InstanceIdentifier<VtnNode> vnpath = snode.getVtnNodeIdentifier();
-            VtnNode vnode = DataStoreUtils.read(tx, oper, vnpath).orNull();
-            if (vnode != null) {
-                // Create or update a VTN port.
-                VtnPort vport = InventoryUtils.toVtnPortBuilder(nc).build();
-                tx.merge(oper, sport.getVtnPortIdentifier(), vport, true);
-
-                if (vnode.getOpenflowVersion() == null) {
-                    // Estimate protocol version.
-                    VtnOpenflowVersion v =
-                        InventoryUtils.getOpenflowVersion(nc);
-                    VtnNode vn = new VtnNodeBuilder().setId(snode.getNodeId()).
-                        setOpenflowVersion(v).build();
-                    tx.merge(oper, vnpath, vn, true);
-                }
-
-                // Cache this port into the inventory reader.
-                InventoryReader reader = ctx.getInventoryReader();
-                reader.prefetch(snode, vnode);
-                resolveIgnoredLinks(ctx, LOG);
-            } else {
-                LOG.debug("Ignore port because VTN node is not present: {}",
-                          sport);
-            }
-        }
-
-        /**
-         * Remove the given node connector from the VTN inventory datastore.
-         *
-         * @param tx     A {@link ReadWriteTransaction} instance.
-         * @param path   Instance identifier of the updated MD-SAL node
-         *               connector.
-         * @param sport  A {@link SalPort} instance corresponding to the
-         *               updated MD-SAL node connector.
-         * @throws VTNException  An error occurred.
-         */
-        private void remove(ReadWriteTransaction tx,
-                            InstanceIdentifier<NodeConnector> path,
-                            SalPort sport) throws VTNException {
-            LogicalDatastoreType oper = LogicalDatastoreType.OPERATIONAL;
-            InstanceIdentifier<VtnPort> vpath = sport.getVtnPortIdentifier();
-            VtnPort vport = DataStoreUtils.read(tx, oper, vpath).orNull();
-            if (vport != null) {
-                // Remove VTN links affected by the removed port.
-                removeVtnLink(tx, vport);
-
-                // Remove a VTN port.
-                tx.delete(oper, vpath);
-            }
-        }
-
-        /**
-         * Convert the given MD-SAL node connector into a {@link SalPort}
-         * instance.
-         *
-         * @param nc     A {@link NodeConnector} instance.
-         * @param label  A label used only for logging.
-         * @return  A {@link SalPort} instance on success.
-         *          {@code null} on failure.
-         */
-        private SalPort toSalPort(NodeConnector nc, String label) {
-            NodeConnectorId id = nc.getId();
-            SalPort sport = SalPort.create(id);
-
-            // Ignore notification on logical port completely.
-            if (sport == null && LOG.isDebugEnabled() &&
-                !SalPort.isLogicalPort(id)) {
-                LOG.debug("{}: Ignore unsupported node connector: {}",
-                          label, id);
-            }
-            return sport;
-        }
-
-        /**
-         * {@inheritDoc}
-         */
-        @Override
-        public Void execute(TxContext ctx) throws VTNException {
-            // Process node connector deletion events.
-            ReadWriteTransaction tx = ctx.getReadWriteTransaction();
-            for (Map.Entry<InstanceIdentifier<NodeConnector>, SalPort> entry:
-                     removed.entrySet()) {
-                InstanceIdentifier<NodeConnector> path = entry.getKey();
-                SalPort sport = entry.getValue();
-                remove(tx, path, sport);
-            }
-
-            // Process node connector update events.
-            for (Map.Entry<InstanceIdentifier<NodeConnector>, SalPort> entry:
-                     updated.entrySet()) {
-                InstanceIdentifier<NodeConnector> path = entry.getKey();
-                SalPort sport = entry.getValue();
-                add(ctx, tx, path, sport);
-            }
-
-            if (!updated.isEmpty()) {
-                resolveIgnoredLinks(ctx, LOG);
-            }
-
-            return null;
-        }
-
-        /**
-         * {@inheritDoc}
-         */
-        @Override
-        public void onFailure(VTNManagerProvider provider, Throwable t) {
-            LOG.error("Failed to update VTN port information.", t);
-        }
-
-        // NodeConnectorEventContext
-
-        /**
-         * {@inheritDoc}
-         */
-        @Override
-        public void addUpdated(InstanceIdentifier<NodeConnector> path,
-                               NodeConnector nc, String label) {
-            SalPort sport = toSalPort(nc, label);
-            if (sport != null) {
-                updated.put(path, sport);
-            }
-        }
-
-        /**
-         * {@inheritDoc}
-         */
-        @Override
-        public void addRemoved(InstanceIdentifier<NodeConnector> path,
-                               NodeConnector nc) {
-            SalPort sport = toSalPort(nc, "onRemoved");
-            if (sport != null) {
-                removed.put(path, sport);
-            }
-        }
-
-        /**
-         * {@inheritDoc}
-         */
-        @Override
-        public boolean hasPort() {
-            return !(updated.isEmpty() && removed.isEmpty());
-        }
-    }
-
     /**
      * Construct a new instance.
      *
@@ -253,7 +53,34 @@ public final class NodeConnectorListener
      * @param broker  A {@link DataBroker} service instance.
      */
     public NodeConnectorListener(TxQueue queue, DataBroker broker) {
-        super(queue, broker, NodeConnector.class, DataChangeScope.BASE);
+        super(queue, broker, FlowCapableNodeConnector.class,
+              DataChangeScope.SUBTREE);
+    }
+
+    /**
+     * Add the given node connector information to the port update task.
+     *
+     * @param ectx  A {@link PortUpdateTask} instance.
+     * @param path  Path to the flow-capable-node-connector.
+     * @param type  A {@link VtnUpdateType} instance which indicates the type
+     *              of event.
+     */
+    private void addUpdated(PortUpdateTask ectx,
+                            InstanceIdentifier<FlowCapableNodeConnector> path,
+                            VtnUpdateType type) {
+        NodeConnectorId id = InventoryUtils.getNodeConnectorId(path);
+        SalPort sport = SalPort.create(id);
+
+        if (sport == null) {
+            Logger log = (SalPort.isLogicalPort(id))
+                ? MiscUtils.VERBOSE_LOG : LOG;
+            if (log.isDebugEnabled()) {
+                log.debug("{}: Ignore unsupported node connector event: {}",
+                          type, path);
+            }
+        } else {
+            ectx.addUpdated(path, sport);
+        }
     }
 
     // DataStoreListener
@@ -262,17 +89,17 @@ public final class NodeConnectorListener
      * {@inheritDoc}
      */
     @Override
-    protected NodeConnectorEventContext enterEvent(
+    protected PortUpdateTask enterEvent(
         AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> ev) {
-        return new PortUpdatedTask();
+        return new PortUpdateTask(LOG);
     }
 
     /**
      * {@inheritDoc}
      */
     @Override
-    protected void exitEvent(NodeConnectorEventContext ectx) {
-        if (ectx.hasPort()) {
+    protected void exitEvent(PortUpdateTask ectx) {
+        if (ectx.hasUpdates()) {
             submit(ectx);
         }
     }
@@ -281,39 +108,41 @@ public final class NodeConnectorListener
      * {@inheritDoc}
      */
     @Override
-    protected void onCreated(NodeConnectorEventContext ectx,
-                             InstanceIdentifier<NodeConnector> key,
-                             NodeConnector value) {
-        ectx.addUpdated(key, value, "onCreated");
+    protected void onCreated(PortUpdateTask ectx,
+                             InstanceIdentifier<FlowCapableNodeConnector> key,
+                             FlowCapableNodeConnector value) {
+        addUpdated(ectx, key, VtnUpdateType.CREATED);
     }
 
     /**
      * {@inheritDoc}
      */
     @Override
-    protected void onUpdated(NodeConnectorEventContext ectx,
-                             InstanceIdentifier<NodeConnector> key,
-                             NodeConnector oldValue, NodeConnector newValue) {
-        ectx.addUpdated(key, newValue, "onUpdated");
+    protected void onUpdated(PortUpdateTask ectx,
+                             InstanceIdentifier<FlowCapableNodeConnector> key,
+                             FlowCapableNodeConnector oldValue,
+                             FlowCapableNodeConnector newValue) {
+        addUpdated(ectx, key, VtnUpdateType.CHANGED);
     }
 
     /**
      * {@inheritDoc}
      */
     @Override
-    protected void onRemoved(NodeConnectorEventContext ectx,
-                             InstanceIdentifier<NodeConnector> key,
-                             NodeConnector value) {
-        ectx.addRemoved(key, value);
+    protected void onRemoved(PortUpdateTask ectx,
+                             InstanceIdentifier<FlowCapableNodeConnector> key,
+                             FlowCapableNodeConnector value) {
+        addUpdated(ectx, key, VtnUpdateType.REMOVED);
     }
 
     /**
      * {@inheritDoc}
      */
     @Override
-    protected InstanceIdentifier<NodeConnector> getWildcardPath() {
+    protected InstanceIdentifier<FlowCapableNodeConnector> getWildcardPath() {
         return InstanceIdentifier.builder(Nodes.class).child(Node.class).
-            child(NodeConnector.class).build();
+            child(NodeConnector.class).
+            augmentation(FlowCapableNodeConnector.class).build();
     }
 
     /**
diff --git a/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/inventory/NodeEventContext.java b/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/inventory/NodeEventContext.java
deleted file mode 100644 (file)
index 2dd634b..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (c) 2015 NEC Corporation
- * 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.vtn.manager.internal.inventory;
-
-import org.opendaylight.vtn.manager.internal.TxTask;
-
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
-
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
-
-/**
- * An event context used to handle node events.
- */
-interface NodeEventContext extends TxTask<Void> {
-    /**
-     * Add the given node to the node map to be created.
-     *
-     * @param path   The identifier of the node to be added.
-     * @param node  A {@link Node} instance to be added.
-     */
-    void addCreated(InstanceIdentifier<Node> path, Node node);
-
-    /**
-     * Add the given node to the node map to be removed.
-     *
-     * @param path  The identifier of the node to be added.
-     * @param node  A {@link Node} instance to be added.
-     */
-    void addRemoved(InstanceIdentifier<Node> path, Node node);
-
-    /**
-     * Determine whether this instance contains nodes to be created or
-     * removed.
-     *
-     * @return  {@code true} only if this instance contains at least one
-     *          node to be created or removed.
-     */
-    boolean hasNode();
-}
index a14310aab1b6a36bdde757752b42c4c64129993d..2997871239b6621406f27c909317110e26016215 100644 (file)
@@ -12,9 +12,7 @@ package org.opendaylight.vtn.manager.internal.inventory;
 import java.util.Collections;
 import java.util.ArrayList;
 import java.util.EnumSet;
-import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
 import java.util.Set;
 
 import org.slf4j.Logger;
@@ -26,7 +24,6 @@ import org.opendaylight.vtn.manager.internal.TxContext;
 import org.opendaylight.vtn.manager.internal.TxQueue;
 import org.opendaylight.vtn.manager.internal.VTNManagerProvider;
 import org.opendaylight.vtn.manager.internal.util.DataStoreUtils;
-import org.opendaylight.vtn.manager.internal.util.inventory.InventoryReader;
 import org.opendaylight.vtn.manager.internal.util.inventory.InventoryUtils;
 import org.opendaylight.vtn.manager.internal.util.inventory.SalNode;
 import org.opendaylight.vtn.manager.internal.util.tx.AbstractTxTask;
@@ -45,6 +42,7 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.impl.inventory.rev15020
 import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.impl.inventory.rev150209.vtn.nodes.VtnNode;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.types.rev150209.VtnUpdateType;
 
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
@@ -53,7 +51,7 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.N
  * Listener class that listens the change of MD-SAL nodes.
  */
 public final class NodeListener
-    extends InventoryMaintainer<Node, NodeEventContext> {
+    extends InventoryMaintainer<FlowCapableNode, NodeUpdateTask> {
     /**
      * Logger instance.
      */
@@ -137,149 +135,6 @@ public final class NodeListener
         }
     }
 
-    /**
-     * MD-SAL transaction task that creates the VTN node.
-     */
-    private class NodeUpdatedTask extends AbstractTxTask<Void>
-        implements NodeEventContext {
-        /**
-         * A map that keeps created MD-SAL nodes.
-         */
-        private final Map<InstanceIdentifier<Node>, SalNode>  created =
-            new HashMap<>();
-
-        /**
-         * A map that keeps removed list of removed MD-SAL nodes.
-         */
-        private final Map<InstanceIdentifier<Node>, SalNode>  removed =
-            new HashMap<>();
-
-        /**
-         * Add the given node to the VTN inventory datastore.
-         *
-         * @param ctx    A MD-SAL datastore transaction context.
-         * @param tx     A {@link ReadWriteTransaction} instance.
-         * @param path   Instance identifier of the created MD-SAL node.
-         * @param snode  A {@link SalNode} instance corresponding to the
-         *               created MD-SAL node.
-         * @throws VTNException  An error occurred.
-         */
-        private void add(TxContext ctx, ReadWriteTransaction tx,
-                         InstanceIdentifier<Node> path, SalNode snode)
-            throws VTNException {
-            // Read MD-SAL node from the datastore.
-            LogicalDatastoreType oper = LogicalDatastoreType.OPERATIONAL;
-            Node node = DataStoreUtils.read(tx, oper, path).orNull();
-            if (node == null) {
-                LOG.debug("Ignore bogus creation event: {}", snode);
-            } else {
-                // Create a VTN node.
-                VtnNode vnode = InventoryUtils.toVtnNodeBuilder(node).build();
-                tx.merge(oper, snode.getVtnNodeIdentifier(), vnode, true);
-
-                // Cache this node into the inventory reader.
-                InventoryReader reader = ctx.getInventoryReader();
-                reader.prefetch(snode, vnode);
-            }
-        }
-
-        /**
-         * Remove the given node from the VTN inventory datastore.
-         *
-         * @param tx     A {@link ReadWriteTransaction} instance.
-         * @param path   Instance identifier of the removed MD-SAL node.
-         * @param snode  A {@link SalNode} instance corresponding to the
-         *               removed MD-SAL node.
-         * @throws VTNException  An error occurred.
-         */
-        private void remove(ReadWriteTransaction tx,
-                            InstanceIdentifier<Node> path, SalNode snode)
-            throws VTNException {
-            // Remove VTN links affected by the removed node.
-            removeVtnLink(tx, snode);
-
-            // Delete a VTN node associated with the given MD-SAL node.
-            DataStoreUtils.delete(tx, LogicalDatastoreType.OPERATIONAL,
-                                  snode.getVtnNodeIdentifier());
-        }
-
-        /**
-         * {@inheritDoc}
-         */
-        @Override
-        public Void execute(TxContext ctx) throws VTNException {
-            // Process node deletion events.
-            ReadWriteTransaction tx = ctx.getReadWriteTransaction();
-            for (Map.Entry<InstanceIdentifier<Node>, SalNode> entry:
-                     removed.entrySet()) {
-                InstanceIdentifier<Node> path = entry.getKey();
-                SalNode snode = entry.getValue();
-                remove(tx, path, snode);
-            }
-
-            // Process node creation events.
-            for (Map.Entry<InstanceIdentifier<Node>, SalNode> entry:
-                     created.entrySet()) {
-                InstanceIdentifier<Node> path = entry.getKey();
-                SalNode snode = entry.getValue();
-                add(ctx, tx, path, snode);
-            }
-
-            if (!created.isEmpty()) {
-                // Try to resolve ignored inter-switch links.
-                resolveIgnoredLinks(ctx, LOG);
-            }
-
-            return null;
-        }
-
-        /**
-         * {@inheritDoc}
-         */
-        @Override
-        public void onFailure(VTNManagerProvider provider, Throwable t) {
-            LOG.error("Failed to update VTN node information.", t);
-        }
-
-        // NodeEventContext
-
-        /**
-         * {@inheritDoc}
-         */
-        @Override
-        public void addCreated(InstanceIdentifier<Node> path, Node node) {
-            NodeId id = node.getId();
-            SalNode snode = SalNode.create(id);
-            if (snode == null) {
-                LOG.debug("Ignore unsupported node creation: {}", id);
-            } else {
-                created.put(path, snode);
-            }
-        }
-
-        /**
-         * {@inheritDoc}
-         */
-        @Override
-        public void addRemoved(InstanceIdentifier<Node> path, Node node) {
-            NodeId id = node.getId();
-            SalNode snode = SalNode.create(id);
-            if (snode == null) {
-                LOG.debug("Ignore unsupported node deletion: {}", id);
-            } else {
-                removed.put(path, snode);
-            }
-        }
-
-        /**
-         * {@inheritDoc}
-         */
-        @Override
-        public boolean hasNode() {
-            return !(created.isEmpty() && removed.isEmpty());
-        }
-    }
-
     /**
      * Construct a new instance.
      *
@@ -288,27 +143,47 @@ public final class NodeListener
      * @param broker  A {@link DataBroker} service instance.
      */
     public NodeListener(TxQueue queue, DataBroker broker) {
-        super(queue, broker, Node.class, DataChangeScope.BASE);
+        super(queue, broker, FlowCapableNode.class, DataChangeScope.SUBTREE);
         submitInitial(new NodesInitTask());
     }
 
+    /**
+     * Add the given node information to the node update task.
+     *
+     * @param ectx  A {@link NodeUpdateTask} instance.
+     * @param path  Path to the flow-capable-node.
+     * @param type  A {@link VtnUpdateType} instance which indicates the type
+     *              of event.
+     */
+    private void addUpdated(NodeUpdateTask ectx,
+                            InstanceIdentifier<FlowCapableNode> path,
+                            VtnUpdateType type) {
+        NodeId nid = InventoryUtils.getNodeId(path);
+        SalNode snode = SalNode.create(nid);
+        if (snode == null) {
+            LOG.debug("{}: Ignore unsupported node event: {}", type, path);
+        } else {
+            ectx.addUpdated(path, snode);
+        }
+    }
+
     // DataStoreListener
 
     /**
      * {@inheritDoc}
      */
     @Override
-    protected NodeEventContext enterEvent(
+    protected NodeUpdateTask enterEvent(
         AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> ev) {
-        return new NodeUpdatedTask();
+        return new NodeUpdateTask(LOG);
     }
 
     /**
      * {@inheritDoc}
      */
     @Override
-    protected void exitEvent(NodeEventContext ectx) {
-        if (ectx.hasNode()) {
+    protected void exitEvent(NodeUpdateTask ectx) {
+        if (ectx.hasUpdates()) {
             submit(ectx);
         }
     }
@@ -317,18 +192,20 @@ public final class NodeListener
      * {@inheritDoc}
      */
     @Override
-    protected void onCreated(NodeEventContext ectx,
-                             InstanceIdentifier<Node> key, Node value) {
-        ectx.addCreated(key, value);
+    protected void onCreated(NodeUpdateTask ectx,
+                             InstanceIdentifier<FlowCapableNode> key,
+                             FlowCapableNode value) {
+        addUpdated(ectx, key, VtnUpdateType.CREATED);
     }
 
     /**
      * {@inheritDoc}
      */
     @Override
-    protected void onUpdated(NodeEventContext ectx,
-                             InstanceIdentifier<Node> key, Node oldValue,
-                             Node newValue) {
+    protected void onUpdated(NodeUpdateTask ectx,
+                             InstanceIdentifier<FlowCapableNode> key,
+                             FlowCapableNode oldValue,
+                             FlowCapableNode newValue) {
         throw new IllegalStateException("Should never be called.");
     }
 
@@ -336,18 +213,19 @@ public final class NodeListener
      * {@inheritDoc}
      */
     @Override
-    protected void onRemoved(NodeEventContext ectx,
-                             InstanceIdentifier<Node> key, Node value) {
-        ectx.addRemoved(key, value);
+    protected void onRemoved(NodeUpdateTask ectx,
+                             InstanceIdentifier<FlowCapableNode> key,
+                             FlowCapableNode value) {
+        addUpdated(ectx, key, VtnUpdateType.REMOVED);
     }
 
     /**
      * {@inheritDoc}
      */
     @Override
-    protected InstanceIdentifier<Node> getWildcardPath() {
+    protected InstanceIdentifier<FlowCapableNode> getWildcardPath() {
         return InstanceIdentifier.builder(Nodes.class).child(Node.class).
-            build();
+            augmentation(FlowCapableNode.class).build();
     }
 
     /**
diff --git a/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/inventory/NodeUpdateTask.java b/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/inventory/NodeUpdateTask.java
new file mode 100644 (file)
index 0000000..6cc15b7
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2015 NEC Corporation
+ * 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.vtn.manager.internal.inventory;
+
+import org.slf4j.Logger;
+
+import org.opendaylight.vtn.manager.VTNException;
+
+import org.opendaylight.vtn.manager.internal.TxContext;
+import org.opendaylight.vtn.manager.internal.VTNManagerProvider;
+import org.opendaylight.vtn.manager.internal.util.DataStoreUtils;
+import org.opendaylight.vtn.manager.internal.util.inventory.InventoryUtils;
+import org.opendaylight.vtn.manager.internal.util.inventory.SalNode;
+
+import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.impl.inventory.rev150209.vtn.nodes.VtnNode;
+
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
+
+/**
+ * A MD-SAL datastore transaction task that updates VTN node information.
+ */
+final class NodeUpdateTask
+    extends InventoryUpdateTask<FlowCapableNode, SalNode> {
+    /**
+     * Construct a new instance.
+     *
+     * @param log  A {@link Logger} instance.
+     */
+    NodeUpdateTask(Logger log) {
+        super(log);
+    }
+
+    // InventoryUpdateTask
+
+    /**
+     * Add a VTN node information corresponding to the given MD-SAL node.
+     *
+     * @param ctx    A {@link TxContext} instance.
+     * @param tx     A {@link ReadWriteTransaction} instance.
+     * @param snode  A {@link SalNode} instance which indicates the location
+     *               of the node.
+     * @param path   Path to the {@link FlowCapableNode} instance in the
+     *               MD-SAL datastore.
+     * @param fcn    A {@link FlowCapableNode} instance.
+     * @throws VTNException  An error occurred.
+     */
+    @Override
+    protected void add(TxContext ctx, ReadWriteTransaction tx, SalNode snode,
+                       InstanceIdentifier<FlowCapableNode> path,
+                       FlowCapableNode fcn) throws VTNException {
+        // Create a VTN node.
+        VtnNode vnode = InventoryUtils.toVtnNodeBuilder(snode.getNodeId()).
+            build();
+        tx.merge(LogicalDatastoreType.OPERATIONAL,
+                 snode.getVtnNodeIdentifier(), vnode, true);
+    }
+
+    /**
+     * Remove a VTN node information corresponding to the given MD-SAL node.
+     *
+     * @param ctx    A {@link TxContext} instance.
+     * @param tx     A {@link ReadWriteTransaction} instance.
+     * @param snode  A {@link SalNode} instance which indicates the location
+     *               of the node.
+     * @throws VTNException  An error occurred.
+     */
+    @Override
+    protected void remove(TxContext ctx, ReadWriteTransaction tx,
+                          SalNode snode)throws VTNException {
+        // Remove VTN links affected by the removed node.
+        InventoryUtils.removeVtnLink(tx, snode);
+
+        // Delete a VTN node associated with the given MD-SAL node.
+        DataStoreUtils.delete(tx, LogicalDatastoreType.OPERATIONAL,
+                              snode.getVtnNodeIdentifier());
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected void fixUp(TxContext ctx, boolean added) {
+        // Nothing to do here.
+    }
+
+    // TxTask
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void onFailure(VTNManagerProvider provider, Throwable t) {
+        getLogger().error("Failed to update VTN node information.", t);
+    }
+}
diff --git a/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/inventory/PortUpdateTask.java b/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/inventory/PortUpdateTask.java
new file mode 100644 (file)
index 0000000..1c034b5
--- /dev/null
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 2015 NEC Corporation
+ * 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.vtn.manager.internal.inventory;
+
+import org.slf4j.Logger;
+
+import org.opendaylight.vtn.manager.VTNException;
+
+import org.opendaylight.vtn.manager.internal.TxContext;
+import org.opendaylight.vtn.manager.internal.VTNManagerProvider;
+import org.opendaylight.vtn.manager.internal.util.DataStoreUtils;
+import org.opendaylight.vtn.manager.internal.util.inventory.InventoryReader;
+import org.opendaylight.vtn.manager.internal.util.inventory.InventoryUtils;
+import org.opendaylight.vtn.manager.internal.util.inventory.SalNode;
+import org.opendaylight.vtn.manager.internal.util.inventory.SalPort;
+
+import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.impl.inventory.rev150209.VtnOpenflowVersion;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.impl.inventory.rev150209.vtn.node.info.VtnPort;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.impl.inventory.rev150209.vtn.nodes.VtnNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.impl.inventory.rev150209.vtn.nodes.VtnNodeBuilder;
+
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnector;
+
+/**
+ * A MD-SAL datastore transaction task that updates VTN port information.
+ */
+final class PortUpdateTask
+    extends InventoryUpdateTask<FlowCapableNodeConnector, SalPort> {
+    /**
+     * Construct a new instance.
+     *
+     * @param log  A {@link Logger} instance.
+     */
+    PortUpdateTask(Logger log) {
+        super(log);
+    }
+
+    // InventoryUpdateTask
+    /**
+     * Add a VTN port information corresponding to the given MD-SAL node
+     * connector.
+     *
+     * @param ctx    A {@link TxContext} instance.
+     * @param tx     A {@link ReadWriteTransaction} instance.
+     * @param sport  A {@link SalPort} instance which indicates the location
+     *               of the node connector.
+     * @param path   Path to the {@link FlowCapableNodeConnector} instance in
+     *               the MD-SAL datastore.
+     * @param fcnc   A {@link FlowCapableNodeConnector} instance.
+     * @throws VTNException  An error occurred.
+     */
+    @Override
+    protected void add(TxContext ctx, ReadWriteTransaction tx, SalPort sport,
+                       InstanceIdentifier<FlowCapableNodeConnector> path,
+                       FlowCapableNodeConnector fcnc) throws VTNException {
+        // Create or update a VTN port.
+        LogicalDatastoreType oper = LogicalDatastoreType.OPERATIONAL;
+        VtnPort vport = InventoryUtils.
+            toVtnPortBuilder(sport.getNodeConnectorId(), fcnc).build();
+        tx.merge(oper, sport.getVtnPortIdentifier(), vport, true);
+
+        // Read VTN node from the datastore.
+        SalNode snode = sport.getSalNode();
+        InstanceIdentifier<VtnNode> vnpath = snode.getVtnNodeIdentifier();
+        VtnNode vnode = DataStoreUtils.read(tx, oper, vnpath).orNull();
+        if (vnode == null || vnode.getOpenflowVersion() == null) {
+            // Estimate protocol version.
+            VtnOpenflowVersion v = InventoryUtils.getOpenflowVersion(fcnc);
+            VtnNode vn = new VtnNodeBuilder().setId(snode.getNodeId()).
+                setOpenflowVersion(v).build();
+            tx.merge(oper, vnpath, vn, true);
+        }
+
+        // Cache this port into the inventory reader.
+        InventoryReader reader = ctx.getInventoryReader();
+        reader.prefetch(sport, vport);
+    }
+
+    /**
+     * Remove a VTN port information corresponding to the given MD-SAL node
+     * connector.
+     *
+     * @param ctx    A {@link TxContext} instance.
+     * @param tx     A {@link ReadWriteTransaction} instance.
+     * @param sport  A {@link SalPort} instance which indicates the location
+     *               of the node connector.
+     * @throws VTNException  An error occurred.
+     */
+    @Override
+    protected void remove(TxContext ctx, ReadWriteTransaction tx,
+                          SalPort sport) throws VTNException {
+        LogicalDatastoreType oper = LogicalDatastoreType.OPERATIONAL;
+        InstanceIdentifier<VtnPort> vpath = sport.getVtnPortIdentifier();
+        VtnPort vport = DataStoreUtils.read(tx, oper, vpath).orNull();
+        if (vport != null) {
+            // Remove VTN links affected by the removed port.
+            InventoryUtils.removeVtnLink(tx, vport);
+
+            // Remove a VTN port.
+            tx.delete(oper, vpath);
+        }
+
+        // Add negative cache for the removed port.
+        InventoryReader reader = ctx.getInventoryReader();
+        reader.prefetch(sport, (VtnPort)null);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected void fixUp(TxContext ctx, boolean added) throws VTNException {
+        if (added) {
+            // Resolve ignored inter-switch links.
+            ReadWriteTransaction tx = ctx.getReadWriteTransaction();
+            InventoryReader reader = ctx.getInventoryReader();
+            InventoryUtils.resolveIgnoredLinks(tx, reader, getLogger());
+        }
+    }
+
+    // TxTask
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void onFailure(VTNManagerProvider provider, Throwable t) {
+        getLogger().error("Failed to update VTN port information.", t);
+    }
+}
diff --git a/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/inventory/TopologyEventContext.java b/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/inventory/TopologyEventContext.java
deleted file mode 100644 (file)
index 92716c1..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (c) 2015 NEC Corporation
- * 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.vtn.manager.internal.inventory;
-
-import org.opendaylight.vtn.manager.internal.TxTask;
-
-import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Link;
-
-/**
- * An event context used to handle topology events.
- */
-interface TopologyEventContext extends TxTask<Void> {
-    /**
-     * Add the given link to the link map to be created.
-     *
-     * @param link  A {@link Link} instance to be added.
-     */
-    void addCreated(Link link);
-
-    /**
-     * Add the given link to the link map to be removed.
-     *
-     * @param link  A {@link Link} instance to be added.
-     */
-    void addRemoved(Link link);
-
-    /**
-     * Determine whether this instance contains at least one link information
-     * to be created or removed.
-     *
-     * @return  {@code true} only if this instance contains at least one
-     *          link information to be created or removed.
-     */
-    boolean hasLink();
-}
index 6f1d26fe3eb422bdab2cea59875ba491999da23a..10a61d788dea1bcf0bad4e09b8f1ba97dfd8b41c 100644 (file)
@@ -11,9 +11,7 @@ package org.opendaylight.vtn.manager.internal.inventory;
 
 import java.util.Collections;
 import java.util.EnumSet;
-import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
 import java.util.Set;
 
 import org.slf4j.Logger;
@@ -25,8 +23,8 @@ import org.opendaylight.vtn.manager.internal.TxContext;
 import org.opendaylight.vtn.manager.internal.TxQueue;
 import org.opendaylight.vtn.manager.internal.VTNManagerProvider;
 import org.opendaylight.vtn.manager.internal.util.DataStoreUtils;
+import org.opendaylight.vtn.manager.internal.util.inventory.InventoryReader;
 import org.opendaylight.vtn.manager.internal.util.inventory.InventoryUtils;
-import org.opendaylight.vtn.manager.internal.util.inventory.LinkEdge;
 import org.opendaylight.vtn.manager.internal.util.inventory.SalPort;
 import org.opendaylight.vtn.manager.internal.util.tx.AbstractTxTask;
 
@@ -43,8 +41,6 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.impl.topology.rev150209
 import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.impl.topology.rev150209.IgnoredLinksBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.impl.topology.rev150209.VtnTopology;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.impl.topology.rev150209.VtnTopologyBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.impl.topology.rev150209.ignored.links.IgnoredLink;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.impl.topology.rev150209.vtn.topology.VtnLink;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.types.rev150209.VtnUpdateType;
 
 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.LinkId;
@@ -58,7 +54,7 @@ import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.
  * Listener class that listens the change of MD-SAL topology datastore.
  */
 public final class TopologyListener
-    extends InventoryMaintainer<Link, TopologyEventContext> {
+    extends InventoryMaintainer<Link, LinkUpdateTask> {
     /**
      * Logger instance.
      */
@@ -80,16 +76,18 @@ public final class TopologyListener
     /**
      * MD-SAL transaction task that initializes the VTN topology tree.
      */
-    private class TopologyInitTask extends AbstractTxTask<Void> {
+    private static class TopologyInitTask extends AbstractTxTask<Void> {
         /**
          * Initialize VTN network topology.
          *
          * @param tx        A {@link ReadWriteTransaction} instance.
+         * @param reader    An {@link InventoryReader} instance.
          * @param topology  MD-SAL network topology.
          * @throws VTNException
          *    An error occurred.
          */
-        private void initLinks(ReadWriteTransaction tx, Topology topology)
+        private void initLinks(ReadWriteTransaction tx, InventoryReader reader,
+                               Topology topology)
             throws VTNException {
             if (topology == null) {
                 return;
@@ -110,7 +108,7 @@ public final class TopologyListener
                 }
 
                 LinkId lid = link.getLinkId();
-                if (!addVtnLink(tx, lid, src, dst)) {
+                if (!InventoryUtils.addVtnLink(tx, reader, lid, src, dst)) {
                     LOG.warn("Ignore inter-switch link: {}: {} -> {}",
                              lid.getValue(), src, dst);
                 }
@@ -141,7 +139,7 @@ public final class TopologyListener
             tx.delete(oper, igPath);
             tx.merge(oper, vtPath, new VtnTopologyBuilder().build(), true);
             tx.merge(oper, igPath, new IgnoredLinksBuilder().build(), true);
-            initLinks(tx, topology);
+            initLinks(tx, ctx.getInventoryReader(), topology);
 
             return null;
         }
@@ -155,118 +153,6 @@ public final class TopologyListener
         }
     }
 
-    /**
-     * MD-SAL transaction task that updates the VTN link.
-     */
-    private class LinkUpdatedTask extends AbstractTxTask<Void>
-        implements TopologyEventContext {
-        /**
-         * A map that keeps created links.
-         */
-        private final Map<LinkId, LinkEdge>  created = new HashMap<>();
-
-        /**
-         * A map that keeps removed links.
-         */
-        private final Map<LinkId, LinkEdge>  removed = new HashMap<>();
-
-        /**
-         * Remove the given link from the VTN network topology.
-         *
-         * @param tx   A {@link ReadWriteTransaction} instance.
-         * @param lid  A {@link LinkId} instance.
-         * @param le   A {@link LinkEdge} instance.
-         * @throws VTNException  An error occurred.
-         */
-        private void remove(ReadWriteTransaction tx, LinkId lid, LinkEdge le)
-            throws VTNException {
-            // Remove the link from vtn-topology list.
-            SalPort src = le.getSourcePort();
-            SalPort dst = le.getDestinationPort();
-            LogicalDatastoreType oper = LogicalDatastoreType.OPERATIONAL;
-            InstanceIdentifier<VtnLink> lpath =
-                InventoryUtils.toVtnLinkIdentifier(lid);
-            if (DataStoreUtils.read(tx, oper, lpath).isPresent()) {
-                tx.delete(oper, lpath);
-
-                // Remove port links.
-                removePortLink(tx, src, lid);
-                removePortLink(tx, dst, lid);
-            } else {
-                // Remove the link from ignored-links list.
-                InstanceIdentifier<IgnoredLink> ipath =
-                    InventoryUtils.toIgnoredLinkIdentifier(lid);
-                DataStoreUtils.delete(tx, oper, ipath);
-            }
-        }
-
-        /**
-         * {@inheritDoc}
-         */
-        @Override
-        public Void execute(TxContext ctx) throws VTNException {
-            // Process link deletion events.
-            ReadWriteTransaction tx = ctx.getReadWriteTransaction();
-            for (Map.Entry<LinkId, LinkEdge> entry: removed.entrySet()) {
-                LinkId lid = entry.getKey();
-                LinkEdge le = entry.getValue();
-                remove(tx, lid, le);
-            }
-
-            // Process link creation events.
-            for (Map.Entry<LinkId, LinkEdge> entry: created.entrySet()) {
-                LinkId lid = entry.getKey();
-                LinkEdge le = entry.getValue();
-                SalPort src = le.getSourcePort();
-                SalPort dst = le.getDestinationPort();
-                if (!addVtnLink(tx, lid, src, dst)) {
-                    LOG.warn("onCreated: Ignore inter-switch link: {}: " +
-                             "{} -> {}", lid.getValue(), src, dst);
-                }
-            }
-
-            return null;
-        }
-
-        /**
-         * {@inheritDoc}
-         */
-        @Override
-        public void onFailure(VTNManagerProvider provider, Throwable t) {
-            LOG.error("Failed to update VTN link.", t);
-        }
-
-        // TopologyEventContext
-
-        /**
-         * {@inheritDoc}
-         */
-        @Override
-        public void addCreated(Link link) {
-            LinkId lid = link.getLinkId();
-            LinkEdge le = new LinkEdge(link);
-            created.put(lid, le);
-        }
-
-        /**
-         * {@inheritDoc}
-         */
-        @Override
-        public void addRemoved(Link link) {
-            LinkId lid = link.getLinkId();
-            LinkEdge le = new LinkEdge(link);
-            removed.put(lid, le);
-        }
-
-        /**
-         * {@inheritDoc}
-         */
-        @Override
-        public boolean hasLink() {
-            return !(created.isEmpty() && removed.isEmpty());
-        }
-    }
-
     /**
      * Construct a new instance.
      *
@@ -285,17 +171,17 @@ public final class TopologyListener
      * {@inheritDoc}
      */
     @Override
-    protected TopologyEventContext enterEvent(
+    protected LinkUpdateTask enterEvent(
         AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> ev) {
-        return new LinkUpdatedTask();
+        return new LinkUpdateTask(LOG);
     }
 
     /**
      * {@inheritDoc}
      */
     @Override
-    protected void exitEvent(TopologyEventContext ectx) {
-        if (ectx.hasLink()) {
+    protected void exitEvent(LinkUpdateTask ectx) {
+        if (ectx.hasUpdates()) {
             submit(ectx);
         }
     }
@@ -304,23 +190,17 @@ public final class TopologyListener
      * {@inheritDoc}
      */
     @Override
-    protected void onCreated(TopologyEventContext ectx,
-                             InstanceIdentifier<Link> key, Link value) {
-        try {
-            ectx.addCreated(value);
-        } catch (IllegalArgumentException e) {
-            LOG.debug("Ignore unsupported inter-switch link creation: " +
-                      value, e);
-        }
+    protected void onCreated(LinkUpdateTask ectx, InstanceIdentifier<Link> key,
+                             Link value) {
+        ectx.addUpdated(key);
     }
 
     /**
      * {@inheritDoc}
      */
     @Override
-    protected void onUpdated(TopologyEventContext ectx,
-                             InstanceIdentifier<Link> key, Link oldValue,
-                             Link newValue) {
+    protected void onUpdated(LinkUpdateTask ectx, InstanceIdentifier<Link> key,
+                             Link oldValue, Link newValue) {
         throw new IllegalStateException("Should never be called.");
     }
 
@@ -328,14 +208,9 @@ public final class TopologyListener
      * {@inheritDoc}
      */
     @Override
-    protected void onRemoved(TopologyEventContext ectx,
-                             InstanceIdentifier<Link> key, Link value) {
-        try {
-            ectx.addRemoved(value);
-        } catch (IllegalArgumentException e) {
-            LOG.debug("Ignore unsupported inter-switch link deletion: " +
-                      value, e);
-        }
+    protected void onRemoved(LinkUpdateTask ectx, InstanceIdentifier<Link> key,
+                             Link value) {
+        ectx.addUpdated(key);
     }
 
     /**
index 74b2904e81221f73479737153c8d547abf7542d7..bc56bd7f1f6bdb6131f76a3a55c366225b7935f2 100644 (file)
@@ -14,6 +14,7 @@ import java.net.InetAddress;
 import java.util.Collection;
 
 import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import org.opendaylight.vtn.manager.VTNException;
 import org.opendaylight.vtn.manager.util.EtherAddress;
@@ -35,6 +36,17 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.types.rev150209.VtnUpda
  * methods.
  */
 public final class MiscUtils {
+    /**
+     * A logger for extra verbose logging.
+     */
+    public static final Logger  VERBOSE_LOG =
+        LoggerFactory.getLogger("ODL-VTN-Manager-verbose");
+
+    /**
+     * A prime number used to calculate hash code.
+     */
+    public static final int  HASH_PRIME = 31;
+
     /**
      * Private constructor that protects this class from instantiating.
      */
index 9d65fbee08fab476ce97456c210b74fbcd3c5f4b..b66374201722bf8b6cb472f64c0afe0acba4a51d 100644 (file)
@@ -12,6 +12,17 @@ package org.opendaylight.vtn.manager.internal.util.inventory;
 import java.util.ArrayList;
 import java.util.List;
 
+import com.google.common.base.Optional;
+
+import org.slf4j.Logger;
+
+import org.opendaylight.vtn.manager.VTNException;
+
+import org.opendaylight.vtn.manager.internal.util.DataStoreUtils;
+
+import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 
 import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.impl.inventory.rev150209.VtnOpenflowVersion;
@@ -35,9 +46,14 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.port.rev130925.P
 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.port.rev130925.PortFeatures;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.port.rev130925.flow.capable.port.State;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.LinkId;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Link;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.LinkKey;
 
 /**
  * {@code InventoryUtils} class is a collection of utility class methods
@@ -248,44 +264,38 @@ public final class InventoryUtils {
         return builder.setId(node.getId());
     }
 
+    /**
+     * Create a VTN node builder associated with the given MD-SAL node ID.
+     *
+     * @param nid  A MD-SAL node ID.
+     * @return  A VTN node builder.
+     */
+    public static VtnNodeBuilder toVtnNodeBuilder(NodeId nid) {
+        return new VtnNodeBuilder().setId(nid);
+    }
+
     /**
      * Create a VTN port builder associated with the given MD-SAL node
      * connector ID.
      *
-     * @param nc  A {@link NodeConnector} instance.
+     * @param ncid  A MD-SAL node connector ID.
+     * @param fcnc  A {@link FlowCapableNodeConnector} instance.
      * @return  A VTN port builder.
      */
-    public static VtnPortBuilder toVtnPortBuilder(NodeConnector nc) {
-        VtnPortBuilder builder = new VtnPortBuilder();
-        builder.setId(nc.getId());
+    public static VtnPortBuilder toVtnPortBuilder(
+        NodeConnectorId ncid, FlowCapableNodeConnector fcnc) {
+        VtnPortBuilder builder = new VtnPortBuilder().setId(ncid);
+        String name = fcnc.getName();
+        PortFeatures pf = fcnc.getCurrentFeature();
+        Long curSpeed = fcnc.getCurrentSpeed();
+        PortConfig pcfg = fcnc.getConfiguration();
+        Boolean portDown = (pcfg == null) ? null : pcfg.isPORTDOWN();
+        State state = fcnc.getState();
+        Boolean linkDown = (state == null) ? null : state.isLinkDown();
 
-        FlowCapableNodeConnector fcnc =
-            nc.getAugmentation(FlowCapableNodeConnector.class);
-        String name = null;
         boolean enabled = false;
-        PortFeatures pf = null;
-        Long curSpeed = null;
-        if (fcnc != null) {
-            name = fcnc.getName();
-            PortConfig pcfg = fcnc.getConfiguration();
-            Boolean portDown = null;
-            if (pcfg != null) {
-                portDown = pcfg.isPORTDOWN();
-            }
-
-            State state = fcnc.getState();
-            Boolean linkDown = null;
-            if (state != null) {
-                linkDown = state.isLinkDown();
-            }
-
-            if (Boolean.FALSE.equals(portDown) &&
-                Boolean.FALSE.equals(linkDown)) {
-                enabled = true;
-            }
-
-            pf = fcnc.getCurrentFeature();
-            curSpeed = fcnc.getCurrentSpeed();
+        if (Boolean.FALSE.equals(portDown) && Boolean.FALSE.equals(linkDown)) {
+            enabled = true;
         }
 
         // Determine the cost of the link from the link speed.
@@ -307,7 +317,7 @@ public final class InventoryUtils {
         if (name == null) {
             // Port name is unavailable.
             // Use node-connector-id instead.
-            name = nc.getId().getValue();
+            name = ncid.getValue();
         }
 
         builder.setName(name).setEnabled(Boolean.valueOf(enabled)).
@@ -391,11 +401,17 @@ public final class InventoryUtils {
         for (NodeConnector nc: connectors) {
             NodeConnectorId id = nc.getId();
             SalPort sport = SalPort.create(id);
-            if (sport != null) {
-                VtnPort vport = toVtnPortBuilder(nc).build();
+            if (sport == null) {
+                continue;
+            }
+
+            FlowCapableNodeConnector fcnc =
+                nc.getAugmentation(FlowCapableNodeConnector.class);
+            if (fcnc != null) {
+                VtnPort vport = toVtnPortBuilder(id, fcnc).build();
                 list.add(vport);
                 if (version == null) {
-                    version = getOpenflowVersion(nc);
+                    version = getOpenflowVersion(fcnc);
                 }
             }
         }
@@ -407,18 +423,15 @@ public final class InventoryUtils {
      * Estimate the OpenFlow protocol version from the given
      * {@link NodeConnector} instance.
      *
-     * @param nc  A {@link NodeConnector} instance.
+     * @param fcnc  A {@link FlowCapableNodeConnector} instance.
      * @return  Estimated OpenFlow protocol version number.
      */
-    public static VtnOpenflowVersion getOpenflowVersion(NodeConnector nc) {
-        FlowCapableNodeConnector fcnc =
-            nc.getAugmentation(FlowCapableNodeConnector.class);
-        if (fcnc != null) {
-            if (fcnc.getCurrentSpeed() != null) {
-                // OpenFlow 1.3 PORT_STATUS message contains the current
-                // link speed of the port, but OpenFlow 1.0 does not.
-                return VtnOpenflowVersion.OF13;
-            }
+    public static VtnOpenflowVersion getOpenflowVersion(
+        FlowCapableNodeConnector fcnc) {
+        if (fcnc != null && fcnc.getCurrentSpeed() != null) {
+            // OpenFlow 1.3 PORT_STATUS message contains the current
+            // link speed of the port, but OpenFlow 1.0 does not.
+            return VtnOpenflowVersion.OF13;
         }
 
         return VtnOpenflowVersion.OF10;
@@ -462,4 +475,293 @@ public final class InventoryUtils {
         // Use 1Gbps as default.
         return LINK_SPEED_1G;
     }
+
+    /**
+     * Return a MD-SAL node ID in the given instance identifier.
+     *
+     * @param path  An {@link InstanceIdentifier} instance.
+     * @return  A MD-SAL node ID if found.
+     *          {@code null} if not found.
+     */
+    public static NodeId getNodeId(InstanceIdentifier<?> path) {
+        NodeKey key = path.firstKeyOf(Node.class, NodeKey.class);
+        return (key == null) ? null : key.getId();
+    }
+
+    /**
+     * Return a MD-SAL node connector ID in the given instance identifier.
+     *
+     * @param path  An {@link InstanceIdentifier} instance.
+     * @return  A MD-SAL node connector ID if found.
+     *          {@code null} if not found.
+     */
+    public static NodeConnectorId getNodeConnectorId(
+        InstanceIdentifier<?> path) {
+        NodeConnectorKey key =
+            path.firstKeyOf(NodeConnector.class, NodeConnectorKey.class);
+        return (key == null) ? null : key.getId();
+    }
+
+    /**
+     * Return a MD-SAL inter-switch link ID in the given instance identifier.
+     *
+     * @param path  An {@link InstanceIdentifier} instance.
+     * @return  A MD-SAL inter-switch link ID if found.
+     *          {@code null} if not found.
+     */
+    public static LinkId getLinkId(InstanceIdentifier<?> path) {
+        LinkKey key = path.firstKeyOf(Link.class, LinkKey.class);
+        return (key == null) ? null : key.getLinkId();
+    }
+
+    /**
+     * Add the given inter-switch link information into the VTN inventory data.
+     *
+     * @param tx      A {@link ReadWriteTransaction} instance.
+     * @param reader  An {@link InventoryReader} instance.
+     * @param lid     The identifier of the created link.
+     * @param src     A {@link SalPort} instance corresponding to the source
+     *                of the created link.
+     * @param dst     A {@link SalPort} instance corresponding to the
+     *                destination of the created link.
+     * @return  {@code true} if the link was added to the vtn-topology list.
+     *          {@code false} if the link was added to the ignored-links list.
+     * @throws VTNException  An error occurred.
+     */
+    public static boolean addVtnLink(ReadWriteTransaction tx,
+                                     InventoryReader reader, LinkId lid,
+                                     SalPort src, SalPort dst)
+        throws VTNException {
+        // Determine whether the VTN port for both termination points are
+        // present or not.
+        boolean ret;
+        if (reader.get(src) != null && reader.get(dst) != null) {
+            // Create link information.
+            createVtnLink(tx, lid, src, dst);
+            ret = true;
+        } else {
+            // Put link information into ignored link list.
+            InstanceIdentifier<IgnoredLink> key = toIgnoredLinkIdentifier(lid);
+            IgnoredLink ilink = toIgnoredLinkBuilder(lid, src, dst).build();
+            tx.merge(LogicalDatastoreType.OPERATIONAL, key, ilink, true);
+            ret = false;
+        }
+
+        return ret;
+    }
+
+
+    /**
+     * Create a VTN link information, and put it into the MD-SAL datastore.
+     *
+     * @param tx   A {@link ReadWriteTransaction} instance.
+     * @param lid  The identifier of the created link.
+     * @param src  A {@link SalPort} instance corresponding to the source
+     *             of the created link.
+     * @param dst  A {@link SalPort} instance corresponding to the destination
+     *             of the created link.
+     */
+    public static void createVtnLink(ReadWriteTransaction tx, LinkId lid,
+                                     SalPort src, SalPort dst) {
+        // Put the link information into vtn-topology list.
+        InstanceIdentifier<VtnLink> key = toVtnLinkIdentifier(lid);
+        VtnLink vlink = toVtnLinkBuilder(lid, src, dst).build();
+        LogicalDatastoreType oper = LogicalDatastoreType.OPERATIONAL;
+        tx.merge(oper, key, vlink, true);
+
+        // Create source port link.
+        InstanceIdentifier<PortLink> pkey = src.getPortLinkIdentifier(lid);
+        PortLink plink = toPortLinkBuilder(lid, dst).build();
+        tx.merge(oper, pkey, plink, true);
+
+        // Create destination port link.
+        pkey = dst.getPortLinkIdentifier(lid);
+        plink = toPortLinkBuilder(lid, src).build();
+        tx.merge(oper, pkey, plink, true);
+    }
+
+    /**
+     * Remove all VTN links affected by the the removed VTN node.
+     *
+     * @param tx     A {@link ReadWriteTransaction} instance.
+     * @param snode  A {@link SalNode} instance corresponding to the removed
+     *               VTN node.
+     * @throws VTNException  An error occurred.
+     */
+    public static void removeVtnLink(ReadWriteTransaction tx, SalNode snode)
+        throws VTNException {
+        removeVtnTopologyLink(tx, snode);
+        removeIgnoredLink(tx, snode);
+    }
+
+    /**
+     * Remove all VTN links in vtn-topology affected by the removed VTN node.
+     *
+     * @param tx     A {@link ReadWriteTransaction} instance.
+     * @param snode  A {@link SalNode} instance corresponding to the removed
+     *               VTN node.
+     * @throws VTNException  An error occurred.
+     */
+    public static void removeVtnTopologyLink(ReadWriteTransaction tx,
+                                             SalNode snode)
+        throws VTNException {
+        InstanceIdentifier<VtnTopology> topoPath =
+            InstanceIdentifier.create(VtnTopology.class);
+        LogicalDatastoreType oper = LogicalDatastoreType.OPERATIONAL;
+        Optional<VtnTopology> opt = DataStoreUtils.read(tx, oper, topoPath);
+        if (!opt.isPresent()) {
+            return;
+        }
+
+        List<VtnLink> links = opt.get().getVtnLink();
+        if (links == null) {
+            return;
+        }
+
+        long dpid = snode.getNodeNumber();
+        for (VtnLink vlink: links) {
+            LinkId lid = vlink.getLinkId();
+            SalPort src = SalPort.create(vlink.getSource());
+            SalPort dst = SalPort.create(vlink.getDestination());
+            long srcDpid = src.getNodeNumber();
+            long dstDpid = dst.getNodeNumber();
+
+            boolean rmLink = false;
+            if (srcDpid == dpid) {
+                rmLink = true;
+                if (dstDpid != dpid) {
+                    removePortLink(tx, dst, lid);
+                }
+            } else if (dstDpid == dpid) {
+                rmLink = true;
+                removePortLink(tx, src, lid);
+            }
+
+            if (rmLink) {
+                InstanceIdentifier<VtnLink> lpath = toVtnLinkIdentifier(lid);
+                tx.delete(oper, lpath);
+            }
+        }
+    }
+
+    /**
+     * Remove all VTN links in ignored-links affected by the removed VTN node.
+     *
+     * @param tx     A {@link ReadWriteTransaction} instance.
+     * @param snode  A {@link SalNode} instance corresponding to the removed
+     *               VTN node.
+     * @throws VTNException  An error occurred.
+     */
+    public static void removeIgnoredLink(ReadWriteTransaction tx,
+                                         SalNode snode) throws VTNException {
+        InstanceIdentifier<IgnoredLinks> igPath =
+            InstanceIdentifier.create(IgnoredLinks.class);
+        LogicalDatastoreType oper = LogicalDatastoreType.OPERATIONAL;
+        Optional<IgnoredLinks> opt = DataStoreUtils.read(tx, oper, igPath);
+        if (!opt.isPresent()) {
+            return;
+        }
+
+        List<IgnoredLink> links = opt.get().getIgnoredLink();
+        if (links == null) {
+            return;
+        }
+
+        long dpid = snode.getNodeNumber();
+        for (IgnoredLink vlink: links) {
+            LinkId lid = vlink.getLinkId();
+            SalPort src = SalPort.create(vlink.getSource());
+            SalPort dst = SalPort.create(vlink.getDestination());
+            long srcDpid = src.getNodeNumber();
+            long dstDpid = dst.getNodeNumber();
+
+            if (srcDpid == dpid || dstDpid == dpid) {
+                InstanceIdentifier<IgnoredLink> lpath =
+                    InventoryUtils.toIgnoredLinkIdentifier(lid);
+                tx.delete(LogicalDatastoreType.OPERATIONAL, lpath);
+            }
+        }
+    }
+
+    /**
+     * Remove all VTN links affected by the removed VTN port.
+     *
+     * @param tx     A {@link ReadWriteTransaction} instance.
+     * @param vport  A {@link VtnPort} instance corresponding to the removed
+     *               VTN port.
+     * @throws VTNException  An error occurred.
+     */
+    public static void removeVtnLink(ReadWriteTransaction tx, VtnPort vport)
+        throws VTNException {
+        List<PortLink> links = vport.getPortLink();
+        if (links == null) {
+            return;
+        }
+
+        for (PortLink plink: links) {
+            LinkId lid = plink.getLinkId();
+            NodeConnectorId peer = plink.getPeer();
+            SalPort p = SalPort.create(peer);
+            removePortLink(tx, p, lid);
+
+            InstanceIdentifier<VtnLink> lpath = toVtnLinkIdentifier(lid);
+            DataStoreUtils.delete(tx, LogicalDatastoreType.OPERATIONAL, lpath);
+        }
+    }
+
+    /**
+     * Remove the specified port link.
+     *
+     * @param tx     A {@link ReadWriteTransaction} instance.
+     * @param sport  A {@link SalPort} instance corresponding to a VTN port.
+     * @param lid    A {@link LinkId} that specifies inter-switch link to be
+     *               removed.
+     * @throws VTNException  An error occurred.
+     */
+    public static void removePortLink(ReadWriteTransaction tx, SalPort sport,
+                                      LinkId lid) throws VTNException {
+        InstanceIdentifier<PortLink> path = sport.getPortLinkIdentifier(lid);
+        DataStoreUtils.delete(tx, LogicalDatastoreType.OPERATIONAL, path);
+    }
+
+    /**
+     * Try to resolve ignored inter-switch links.
+     *
+     * @param tx      A {@link ReadWriteTransaction} instance.
+     * @param reader  An {@link InventoryReader} instance.
+     * @param log     A {@link Logger} instance.
+     * @throws VTNException  An error occurred.
+     */
+    public static void resolveIgnoredLinks(ReadWriteTransaction tx,
+                                           InventoryReader reader,
+                                           Logger log) throws VTNException {
+        // Read all ignored inter-switch links.
+        InstanceIdentifier<IgnoredLinks> igPath =
+            InstanceIdentifier.create(IgnoredLinks.class);
+        LogicalDatastoreType oper = LogicalDatastoreType.OPERATIONAL;
+        Optional<IgnoredLinks> opt  = DataStoreUtils.read(tx, oper, igPath);
+        if (!opt.isPresent()) {
+            return;
+        }
+
+        List<IgnoredLink> links = opt.get().getIgnoredLink();
+        if (links == null) {
+            return;
+        }
+
+        for (IgnoredLink ignored: links) {
+            SalPort src = SalPort.create(ignored.getSource());
+            SalPort dst = SalPort.create(ignored.getDestination());
+            if (reader.get(src) != null && reader.get(dst) != null) {
+                // Move this link to vtn-topology.
+                LinkId lid = ignored.getLinkId();
+                InstanceIdentifier<IgnoredLink> ipath =
+                    toIgnoredLinkIdentifier(lid);
+                tx.delete(oper, ipath);
+                createVtnLink(tx, lid, src, dst);
+                log.info("Inter-switch link has been resolved: {}: {} -> {}",
+                         lid.getValue(), src, dst);
+            }
+        }
+    }
 }
index ffa7eaa8e2ffc174593876ce20753be69749d180..0e395cf07ada7f0a47ff26a9ee510f75c3fec466 100644 (file)
@@ -9,6 +9,8 @@
 
 package org.opendaylight.vtn.manager.internal.util.inventory;
 
+import org.opendaylight.vtn.manager.internal.util.MiscUtils;
+
 import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.impl.topology.rev150209.vtn.topology.VtnLink;
 
 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Link;
@@ -120,7 +122,8 @@ public final class LinkEdge {
      */
     @Override
     public int hashCode() {
-        return sourcePort.hashCode() + destinationPort.hashCode() * 31;
+        return sourcePort.hashCode() +
+            destinationPort.hashCode() * MiscUtils.HASH_PRIME;
     }
 
     /**
index 017efa2bb5825a9b2aa33a544339a44acbd4d23d..4b680eecdd1dcdbade2c3bc2075fb766734806a8 100644 (file)
 package org.opendaylight.vtn.manager.internal.util.inventory;
 
 import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.slf4j.Logger;
 
 import org.junit.Test;
 
+import org.mockito.Mockito;
+
 import org.opendaylight.vtn.manager.internal.TestBase;
 
+import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.IdentifiableItem;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.Item;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.impl.inventory.rev150209.VtnNodes;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.impl.inventory.rev150209.VtnOpenflowVersion;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.impl.inventory.rev150209.vtn.node.info.VtnPort;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.impl.inventory.rev150209.vtn.node.info.VtnPortBuilder;
@@ -30,8 +43,11 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.impl.inventory.rev15020
 import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.impl.inventory.rev150209.vtn.nodes.VtnNodeBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.impl.inventory.rev150209.vtn.port.info.PortLink;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.impl.inventory.rev150209.vtn.port.info.PortLinkBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.impl.inventory.rev150209.vtn.port.info.PortLinkKey;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.impl.topology.rev150209.IgnoredLinks;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.impl.topology.rev150209.IgnoredLinksBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.impl.topology.rev150209.VtnTopology;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.impl.topology.rev150209.VtnTopologyBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.impl.topology.rev150209.ignored.links.IgnoredLink;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.impl.topology.rev150209.ignored.links.IgnoredLinkBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.impl.topology.rev150209.ignored.links.IgnoredLinkKey;
@@ -47,16 +63,31 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.port.rev130925.f
 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.port.rev130925.flow.capable.port.StateBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
+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.node.NodeConnectorBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
+
 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.LinkId;
+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.TopologyId;
+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.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.LinkKey;
 
 /**
  * JUnit test for {@link InventoryUtils}.
  */
 public class InventoryUtilsTest extends TestBase {
+    /**
+     * Network topology identifier.
+     */
+    private static final String TOPOLOGY_ID = "flow:1";
+
     /**
      * Test case for the following methods.
      *
@@ -354,8 +385,13 @@ public class InventoryUtilsTest extends TestBase {
     }
 
     /**
-     * Test case for {@link InventoryUtils#toVtnNodeBuilder(Node)} and
-     * {@link InventoryUtils#getVtnPorts(List, Node)}.
+     * Test case for the following methods.
+     *
+     * <ul>
+     *   <li>{@link InventoryUtils#toVtnNodeBuilder(Node)}</li>
+     *   <li>{@link InventoryUtils#toVtnNodeBuilder(NodeId)}</li>
+     *   <li>{@link InventoryUtils#getVtnPorts(List, Node)}</li>
+     * </ul>
      */
     @Test
     public void testToVtnNodeBuilder() {
@@ -406,6 +442,11 @@ public class InventoryUtilsTest extends TestBase {
             NodeId nodeId = new NodeId(nid);
             builder.setId(nodeId);
 
+            vbuilder = InventoryUtils.toVtnNodeBuilder(nodeId);
+            assertEquals(nodeId, vbuilder.getId());
+            assertEquals(null, vbuilder.getVtnPort());
+            assertEquals(null, vbuilder.getOpenflowVersion());
+
             List<NodeConnector> nclist10 = new ArrayList<>(badNcList);
             List<NodeConnector> nclist13 = new ArrayList<>(badNcList);
             List<VtnPort> vports10 = new ArrayList<>();
@@ -467,7 +508,8 @@ public class InventoryUtilsTest extends TestBase {
     }
 
     /**
-     * Test case for {@link InventoryUtils#toVtnPortBuilder(NodeConnector)}.
+     * Test case for
+     * {@link InventoryUtils#toVtnPortBuilder(NodeConnectorId, FlowCapableNodeConnector)}.
      */
     @Test
     public void testToVtnPortBuilder() {
@@ -478,24 +520,12 @@ public class InventoryUtilsTest extends TestBase {
                 String pid = "openflow:" + dpid + ":" + port;
                 NodeConnectorId ncid = new NodeConnectorId(pid);
                 VtnPortKey key = new VtnPortKey(ncid);
-                NodeConnectorBuilder ncBuilder = new NodeConnectorBuilder().
-                    setId(ncid);
-                NodeConnector nc = ncBuilder.build();
-                VtnPort vport = InventoryUtils.toVtnPortBuilder(nc).build();
-                assertEquals(ncid, vport.getId());
-                assertEquals(key, vport.getKey());
-                assertEquals(pid, vport.getName());
-                assertEquals(null, vport.getPortLink());
-                assertEquals(Boolean.FALSE, vport.isEnabled());
-                assertEquals(defCost, vport.getCost());
 
                 FlowCapableNodeConnectorBuilder fcb =
                     new FlowCapableNodeConnectorBuilder();
                 FlowCapableNodeConnector fcnc = fcb.build();
-                nc = ncBuilder.
-                    addAugmentation(FlowCapableNodeConnector.class, fcnc).
+                VtnPort vport = InventoryUtils.toVtnPortBuilder(ncid, fcnc).
                     build();
-                vport = InventoryUtils.toVtnPortBuilder(nc).build();
                 assertEquals(ncid, vport.getId());
                 assertEquals(key, vport.getKey());
                 assertEquals(pid, vport.getName());
@@ -506,10 +536,7 @@ public class InventoryUtilsTest extends TestBase {
                 // Set port name.
                 String name = "switch-" + dpid + "-port-" + port;
                 fcnc = fcb.setName(name).build();
-                nc = ncBuilder.
-                    addAugmentation(FlowCapableNodeConnector.class, fcnc).
-                    build();
-                vport = InventoryUtils.toVtnPortBuilder(nc).build();
+                vport = InventoryUtils.toVtnPortBuilder(ncid, fcnc).build();
                 assertEquals(ncid, vport.getId());
                 assertEquals(key, vport.getKey());
                 assertEquals(name, vport.getName());
@@ -521,10 +548,7 @@ public class InventoryUtilsTest extends TestBase {
                 PortConfigBuilder pcfb = new PortConfigBuilder();
                 PortConfig pcf = pcfb.build();
                 fcnc = fcb.setConfiguration(pcf).build();
-                nc = ncBuilder.
-                    addAugmentation(FlowCapableNodeConnector.class, fcnc).
-                    build();
-                vport = InventoryUtils.toVtnPortBuilder(nc).build();
+                vport = InventoryUtils.toVtnPortBuilder(ncid, fcnc).build();
                 assertEquals(ncid, vport.getId());
                 assertEquals(key, vport.getKey());
                 assertEquals(name, vport.getName());
@@ -535,10 +559,7 @@ public class InventoryUtilsTest extends TestBase {
                 pcf = pcfb.setNoFwd(Boolean.TRUE).setNoPacketIn(Boolean.TRUE).
                     setNoRecv(Boolean.TRUE).setPortDown(Boolean.TRUE).build();
                 fcnc = fcb.setConfiguration(pcf).build();
-                nc = ncBuilder.
-                    addAugmentation(FlowCapableNodeConnector.class, fcnc).
-                    build();
-                vport = InventoryUtils.toVtnPortBuilder(nc).build();
+                vport = InventoryUtils.toVtnPortBuilder(ncid, fcnc).build();
                 assertEquals(ncid, vport.getId());
                 assertEquals(key, vport.getKey());
                 assertEquals(name, vport.getName());
@@ -549,10 +570,7 @@ public class InventoryUtilsTest extends TestBase {
                 StateBuilder stb = new StateBuilder();
                 State st = stb.build();
                 fcnc = fcb.setState(st).build();
-                nc = ncBuilder.
-                    addAugmentation(FlowCapableNodeConnector.class, fcnc).
-                    build();
-                vport = InventoryUtils.toVtnPortBuilder(nc).build();
+                vport = InventoryUtils.toVtnPortBuilder(ncid, fcnc).build();
                 assertEquals(ncid, vport.getId());
                 assertEquals(key, vport.getKey());
                 assertEquals(name, vport.getName());
@@ -563,10 +581,7 @@ public class InventoryUtilsTest extends TestBase {
                 st = stb.setBlocked(Boolean.TRUE).setLive(Boolean.TRUE).
                     setLinkDown(Boolean.TRUE).build();
                 fcnc = fcb.setState(st).build();
-                nc = ncBuilder.
-                    addAugmentation(FlowCapableNodeConnector.class, fcnc).
-                    build();
-                vport = InventoryUtils.toVtnPortBuilder(nc).build();
+                vport = InventoryUtils.toVtnPortBuilder(ncid, fcnc).build();
                 assertEquals(ncid, vport.getId());
                 assertEquals(key, vport.getKey());
                 assertEquals(name, vport.getName());
@@ -576,10 +591,7 @@ public class InventoryUtilsTest extends TestBase {
 
                 st = stb.setLinkDown(Boolean.FALSE).build();
                 fcnc = fcb.setState(st).build();
-                nc = ncBuilder.
-                    addAugmentation(FlowCapableNodeConnector.class, fcnc).
-                    build();
-                vport = InventoryUtils.toVtnPortBuilder(nc).build();
+                vport = InventoryUtils.toVtnPortBuilder(ncid, fcnc).build();
                 assertEquals(ncid, vport.getId());
                 assertEquals(key, vport.getKey());
                 assertEquals(name, vport.getName());
@@ -590,10 +602,7 @@ public class InventoryUtilsTest extends TestBase {
                 pcf = pcfb.setPortDown(Boolean.FALSE).build();
                 st = stb.setLinkDown(Boolean.TRUE).build();
                 fcnc = fcb.setState(st).setConfiguration(pcf).build();
-                nc = ncBuilder.
-                    addAugmentation(FlowCapableNodeConnector.class, fcnc).
-                    build();
-                vport = InventoryUtils.toVtnPortBuilder(nc).build();
+                vport = InventoryUtils.toVtnPortBuilder(ncid, fcnc).build();
                 assertEquals(ncid, vport.getId());
                 assertEquals(key, vport.getKey());
                 assertEquals(name, vport.getName());
@@ -603,10 +612,7 @@ public class InventoryUtilsTest extends TestBase {
 
                 st = stb.setLinkDown(Boolean.FALSE).build();
                 fcnc = fcb.setState(st).build();
-                nc = ncBuilder.
-                    addAugmentation(FlowCapableNodeConnector.class, fcnc).
-                    build();
-                vport = InventoryUtils.toVtnPortBuilder(nc).build();
+                vport = InventoryUtils.toVtnPortBuilder(ncid, fcnc).build();
                 assertEquals(ncid, vport.getId());
                 assertEquals(key, vport.getKey());
                 assertEquals(name, vport.getName());
@@ -619,10 +625,7 @@ public class InventoryUtilsTest extends TestBase {
                 PortFeatures pf = pfb.setTenMbHd(Boolean.TRUE).build();
                 Long cost = Long.valueOf(base / 10000000L);
                 fcnc = fcb.setCurrentFeature(pf).build();
-                nc = ncBuilder.
-                    addAugmentation(FlowCapableNodeConnector.class, fcnc).
-                    build();
-                vport = InventoryUtils.toVtnPortBuilder(nc).build();
+                vport = InventoryUtils.toVtnPortBuilder(ncid, fcnc).build();
                 assertEquals(ncid, vport.getId());
                 assertEquals(key, vport.getKey());
                 assertEquals(name, vport.getName());
@@ -634,10 +637,7 @@ public class InventoryUtilsTest extends TestBase {
                     build();
                 cost = Long.valueOf(base / 10000000000L);
                 fcnc = fcb.setCurrentFeature(pf).build();
-                nc = ncBuilder.
-                    addAugmentation(FlowCapableNodeConnector.class, fcnc).
-                    build();
-                vport = InventoryUtils.toVtnPortBuilder(nc).build();
+                vport = InventoryUtils.toVtnPortBuilder(ncid, fcnc).build();
                 assertEquals(ncid, vport.getId());
                 assertEquals(key, vport.getKey());
                 assertEquals(name, vport.getName());
@@ -649,10 +649,7 @@ public class InventoryUtilsTest extends TestBase {
                     build();
                 cost = Long.valueOf(base / 1000000000000L);
                 fcnc = fcb.setCurrentFeature(pf).build();
-                nc = ncBuilder.
-                    addAugmentation(FlowCapableNodeConnector.class, fcnc).
-                    build();
-                vport = InventoryUtils.toVtnPortBuilder(nc).build();
+                vport = InventoryUtils.toVtnPortBuilder(ncid, fcnc).build();
                 assertEquals(ncid, vport.getId());
                 assertEquals(key, vport.getKey());
                 assertEquals(name, vport.getName());
@@ -664,10 +661,7 @@ public class InventoryUtilsTest extends TestBase {
                 long speed = 100000000L;
                 cost = Long.valueOf(base / speed);
                 fcnc = fcb.setCurrentSpeed(speed / 1000L).build();
-                nc = ncBuilder.
-                    addAugmentation(FlowCapableNodeConnector.class, fcnc).
-                    build();
-                vport = InventoryUtils.toVtnPortBuilder(nc).build();
+                vport = InventoryUtils.toVtnPortBuilder(ncid, fcnc).build();
                 assertEquals(ncid, vport.getId());
                 assertEquals(key, vport.getKey());
                 assertEquals(name, vport.getName());
@@ -678,10 +672,7 @@ public class InventoryUtilsTest extends TestBase {
                 speed = (long)0xffffffffL;
                 cost = Long.valueOf(base / (speed * 1000L));
                 fcnc = fcb.setCurrentSpeed(speed).build();
-                nc = ncBuilder.
-                    addAugmentation(FlowCapableNodeConnector.class, fcnc).
-                    build();
-                vport = InventoryUtils.toVtnPortBuilder(nc).build();
+                vport = InventoryUtils.toVtnPortBuilder(ncid, fcnc).build();
                 assertEquals(ncid, vport.getId());
                 assertEquals(key, vport.getKey());
                 assertEquals(name, vport.getName());
@@ -761,28 +752,19 @@ public class InventoryUtilsTest extends TestBase {
         for (long dpid = 1L; dpid <= 10L; dpid++) {
             for (long port = 1L; port <= 10L; port++) {
                 String pid = "openflow:" + dpid + ":" + port;
-                NodeConnectorId ncid = new NodeConnectorId(pid);
-                NodeConnectorBuilder ncBuilder = new NodeConnectorBuilder().
-                    setId(ncid);
-                NodeConnector nc = ncBuilder.build();
+                FlowCapableNodeConnector fcnc = null;
                 assertEquals(VtnOpenflowVersion.OF10,
-                             InventoryUtils.getOpenflowVersion(nc));
+                             InventoryUtils.getOpenflowVersion(fcnc));
 
                 FlowCapableNodeConnectorBuilder fcb =
                     new FlowCapableNodeConnectorBuilder();
-                FlowCapableNodeConnector fcnc = fcb.build();
-                nc = ncBuilder.
-                    addAugmentation(FlowCapableNodeConnector.class, fcnc).
-                    build();
+                fcnc = fcb.build();
                 assertEquals(VtnOpenflowVersion.OF10,
-                             InventoryUtils.getOpenflowVersion(nc));
+                             InventoryUtils.getOpenflowVersion(fcnc));
 
                 fcnc = fcb.setCurrentSpeed(100000L).build();
-                nc = ncBuilder.
-                    addAugmentation(FlowCapableNodeConnector.class, fcnc).
-                    build();
                 assertEquals(VtnOpenflowVersion.OF13,
-                             InventoryUtils.getOpenflowVersion(nc));
+                             InventoryUtils.getOpenflowVersion(fcnc));
             }
         }
     }
@@ -846,4 +828,1016 @@ public class InventoryUtilsTest extends TestBase {
             build();
         assertEquals(spd1t, InventoryUtils.getLinkSpeed(pf));
     }
+
+    /**
+     * Test case for the following methods.
+     *
+     * <ul>
+     *   <li>{@link InventoryUtils#getNodeId(InstanceIdentifier)}</li>
+     *   <li>{@link InventoryUtils#getNodeConnectorId(InstanceIdentifier)}</li>
+     *   <li>{@link InventoryUtils#getLinkId(InstanceIdentifier)}</li>
+     * </ul>
+     */
+    @Test
+    public void testGetKey() {
+        TopologyKey topoKey = new TopologyKey(new TopologyId(TOPOLOGY_ID));
+        for (long dpid = 1L; dpid <= 5L; dpid++) {
+            String nid = "openflow:" + dpid;
+            NodeId nodeId = new NodeId(nid);
+            NodeKey nodeKey = new NodeKey(nodeId);
+            InstanceIdentifier<Node> path = InstanceIdentifier.
+                builder(Nodes.class).
+                child(Node.class, nodeKey).build();
+            assertEquals(nodeId, InventoryUtils.getNodeId(path));
+            assertEquals(null, InventoryUtils.getNodeConnectorId(path));
+            assertEquals(null, InventoryUtils.getLinkId(path));
+
+            for (long port = 1L; port <= 5L; port++) {
+                String pid = nid + ";" + port;
+                NodeConnectorId ncId = new NodeConnectorId(pid);
+                NodeConnectorKey ncKey = new NodeConnectorKey(ncId);
+                InstanceIdentifier<NodeConnector> ncPath = InstanceIdentifier.
+                    builder(Nodes.class).
+                    child(Node.class, nodeKey).
+                    child(NodeConnector.class, ncKey).build();
+                assertEquals(nodeId, InventoryUtils.getNodeId(ncPath));
+                assertEquals(ncId, InventoryUtils.getNodeConnectorId(ncPath));
+                assertEquals(null, InventoryUtils.getLinkId(ncPath));
+
+                LinkId lid = new LinkId(pid);
+                LinkKey lkey = new LinkKey(lid);
+                InstanceIdentifier<Link> lpath = InstanceIdentifier.
+                    builder(NetworkTopology.class).
+                    child(Topology.class, topoKey).
+                    child(Link.class, lkey).build();
+                assertEquals(null, InventoryUtils.getNodeId(lpath));
+                assertEquals(null, InventoryUtils.getNodeConnectorId(lpath));
+                assertEquals(lid, InventoryUtils.getLinkId(lpath));
+            }
+        }
+    }
+
+    /**
+     * Test case for the following methods.
+     *
+     * <ul>
+     *   <li>{@link InventoryUtils#addVtnLink(ReadWriteTransaction,InventoryReader,LinkId,SalPort,SalPort)}</li>
+     *   <li>{@link InventoryUtils#createVtnLink(ReadWriteTransaction,LinkId,SalPort,SalPort)}</li>
+     * </ul>
+     *
+     * @throws Exception  An error occurred.
+     */
+    @Test
+    public void testAddVtnLink() throws Exception {
+        ReadWriteTransaction tx = Mockito.mock(ReadWriteTransaction.class);
+        InventoryReader reader = new InventoryReader(tx);
+        LogicalDatastoreType oper = LogicalDatastoreType.OPERATIONAL;
+        SalPort src = new SalPort(1L, 2L);
+        SalPort dst = new SalPort(10L, 20L);
+        LinkId lid = new LinkId(src.toString());
+        InstanceIdentifier<IgnoredLink> ipath = InstanceIdentifier.
+            builder(IgnoredLinks.class).
+            child(IgnoredLink.class, new IgnoredLinkKey(lid)).build();
+        IgnoredLink ignored = new IgnoredLinkBuilder().
+            setLinkId(lid).setSource(src.getNodeConnectorId()).
+            setDestination(dst.getNodeConnectorId()).build();
+        InstanceIdentifier<VtnLink> vpath = InstanceIdentifier.
+            builder(VtnTopology.class).
+            child(VtnLink.class, new VtnLinkKey(lid)).build();
+        VtnLink vlink = new VtnLinkBuilder().
+            setLinkId(lid).setSource(src.getNodeConnectorId()).
+            setDestination(dst.getNodeConnectorId()).build();
+        InstanceIdentifier<PortLink> srcPath = InstanceIdentifier.
+            builder(VtnNodes.class).
+            child(VtnNode.class, src.getVtnNodeKey()).
+            child(VtnPort.class, src.getVtnPortKey()).
+            child(PortLink.class, new PortLinkKey(lid)).build();
+        PortLink srcLink = new PortLinkBuilder().
+            setLinkId(lid).setPeer(dst.getNodeConnectorId()).build();
+        InstanceIdentifier<PortLink> dstPath = InstanceIdentifier.
+            builder(VtnNodes.class).
+            child(VtnNode.class, dst.getVtnNodeKey()).
+            child(VtnPort.class, dst.getVtnPortKey()).
+            child(PortLink.class, new PortLinkKey(lid)).build();
+        PortLink dstLink = new PortLinkBuilder().
+            setLinkId(lid).setPeer(src.getNodeConnectorId()).build();
+
+        // Both source and destination ports are not present.
+        reader.prefetch(src, (VtnPort)null);
+        reader.prefetch(dst, (VtnPort)null);
+        assertEquals(false,
+                     InventoryUtils.addVtnLink(tx, reader, lid, src, dst));
+        Mockito.verify(tx).merge(oper, ipath, ignored, true);
+        Mockito.verify(tx, Mockito.never()).
+            merge(Mockito.eq(oper), Mockito.eq(vpath), Mockito.eq(vlink),
+                  Mockito.anyBoolean());
+        Mockito.verify(tx, Mockito.never()).
+            merge(Mockito.eq(oper), Mockito.eq(srcPath), Mockito.eq(srcLink),
+                  Mockito.anyBoolean());
+        Mockito.verify(tx, Mockito.never()).
+            merge(Mockito.eq(oper), Mockito.eq(dstPath), Mockito.eq(dstLink),
+                  Mockito.anyBoolean());
+        Mockito.reset(tx);
+
+        // Source port is not present.
+        VtnPort dstPort = new VtnPortBuilder().setId(dst.getNodeConnectorId()).
+            build();
+        reader.prefetch(dst, dstPort);
+        assertEquals(false,
+                     InventoryUtils.addVtnLink(tx, reader, lid, src, dst));
+        Mockito.verify(tx).merge(oper, ipath, ignored, true);
+        Mockito.verify(tx, Mockito.never()).
+            merge(Mockito.eq(oper), Mockito.eq(vpath), Mockito.eq(vlink),
+                  Mockito.anyBoolean());
+        Mockito.verify(tx, Mockito.never()).
+            merge(Mockito.eq(oper), Mockito.eq(srcPath), Mockito.eq(srcLink),
+                  Mockito.anyBoolean());
+        Mockito.verify(tx, Mockito.never()).
+            merge(Mockito.eq(oper), Mockito.eq(dstPath), Mockito.eq(dstLink),
+                  Mockito.anyBoolean());
+        Mockito.reset(tx);
+
+        // Destination port is not present.
+        VtnPort srcPort = new VtnPortBuilder().setId(src.getNodeConnectorId()).
+            build();
+        reader.prefetch(src, srcPort);
+        reader.prefetch(dst, (VtnPort)null);
+        assertEquals(false,
+                     InventoryUtils.addVtnLink(tx, reader, lid, src, dst));
+        Mockito.verify(tx).merge(oper, ipath, ignored, true);
+        Mockito.verify(tx, Mockito.never()).
+            merge(Mockito.eq(oper), Mockito.eq(vpath), Mockito.eq(vlink),
+                  Mockito.anyBoolean());
+        Mockito.verify(tx, Mockito.never()).
+            merge(Mockito.eq(oper), Mockito.eq(srcPath), Mockito.eq(srcLink),
+                  Mockito.anyBoolean());
+        Mockito.verify(tx, Mockito.never()).
+            merge(Mockito.eq(oper), Mockito.eq(dstPath), Mockito.eq(dstLink),
+                  Mockito.anyBoolean());
+        Mockito.reset(tx);
+
+        // Both ports are present.
+        reader.prefetch(dst, dstPort);
+        assertEquals(true,
+                     InventoryUtils.addVtnLink(tx, reader, lid, src, dst));
+        Mockito.verify(tx, Mockito.never()).
+            merge(Mockito.eq(oper), Mockito.eq(ipath), Mockito.eq(ignored),
+                  Mockito.anyBoolean());
+        Mockito.verify(tx).merge(oper, vpath, vlink, true);
+        Mockito.verify(tx).merge(oper, srcPath, srcLink, true);
+        Mockito.verify(tx).merge(oper, dstPath, dstLink, true);
+    }
+
+    /**
+     * VTN topology for test.
+     */
+    private final class TopologyLists {
+        /**
+         * A list of {@link VtnLink} instances.
+         */
+        private final List<VtnLink>  vtnLinks = new ArrayList<>();
+
+        /**
+         * A list of {@link IgnoredLink} instances.
+         */
+        private final List<IgnoredLink>  ignoredLinks = new ArrayList<>();
+
+        /**
+         * A map that keeps paths to {@link VtnLink} instances.
+         *
+         * <p>
+         *   {@link Boolean.TRUE} means the VTN link to be removed.
+         * </p>
+         */
+        private Map<InstanceIdentifier<VtnLink>, Boolean> vtnLinkPaths;
+
+        /**
+         * A map that keeps paths to {@link PortLink} intances.
+         *
+         * <p>
+         *   {@link Boolean.TRUE} means the port link to be removed.
+         * </p>
+         */
+        private Map<InstanceIdentifier<PortLink>, Boolean>  portLinkPaths;
+
+        /**
+         * A map that keeps paths to {@link IgnoredLink} instances.
+         *
+         * <p>
+         *   {@link Boolean.TRUE} means the ignored link to be removed.
+         * </p>
+         */
+        private Map<InstanceIdentifier<IgnoredLink>, Boolean> ignoredLinkPaths;
+
+        /**
+         * Return a list of {@link VtnLink} instances.
+         *
+         * @return  A list of {@link VtnLink} instances.
+         */
+        private List<VtnLink> getVtnLinks() {
+            return vtnLinks;
+        }
+
+        /**
+         * Return a list of {@link IgnoredLink} instances.
+         *
+         * @return  A list of {@link IgnoredLink} instances.
+         */
+        private List<IgnoredLink> getIgnoredLinks() {
+            return ignoredLinks;
+        }
+
+        /**
+         * Add a {@link VtnLink} instance.
+         *
+         * @param src  The source port.
+         * @param dst  The destination port.
+         */
+        private void addVtnLink(SalPort src, SalPort dst) {
+            LinkId lid = new LinkId(src.toString());
+            VtnLink vlink = new VtnLinkBuilder().
+                setLinkId(lid).setSource(src.getNodeConnectorId()).
+                setDestination(dst.getNodeConnectorId()).build();
+            vtnLinks.add(vlink);
+        }
+
+        /**
+         * Add an {@link IgnoredLink} instance.
+         *
+         * @param src  The source port.
+         * @param dst  The destination port.
+         */
+        private void addIgnoredLink(SalPort src, SalPort dst) {
+            LinkId lid = new LinkId(src.toString());
+            IgnoredLink ilink = new IgnoredLinkBuilder().
+                setLinkId(lid).setSource(src.getNodeConnectorId()).
+                setDestination(dst.getNodeConnectorId()).build();
+            ignoredLinks.add(ilink);
+        }
+
+        /**
+         * Set up the mock-up of MD-SAL datastore transaction.
+         *
+         * @param tx     A mock-up of {@link ReadWriteTransaction}.
+         * @param snode  A {@link SalNode} to be removed.
+         */
+        private void setUp(ReadWriteTransaction tx, SalNode snode) {
+            Map<InstanceIdentifier<PortLink>, Boolean> plinks =
+                new HashMap<>();
+            Map<InstanceIdentifier<VtnLink>, Boolean> rmLinks =
+                new HashMap<>();
+            LogicalDatastoreType oper = LogicalDatastoreType.OPERATIONAL;
+            long dpid = snode.getNodeNumber();
+            for (VtnLink vlink: vtnLinks) {
+                SalPort src = SalPort.create(vlink.getSource());
+                SalPort dst = SalPort.create(vlink.getDestination());
+                LinkId lid = vlink.getLinkId();
+                boolean rmLink = false;
+                boolean rmSrc = false;
+                boolean rmDst = false;
+                if (src.getNodeNumber() == dpid) {
+                    rmLink = true;
+                    if (dst.getNodeNumber() != dpid) {
+                        rmDst = true;
+                    }
+                } else if (dst.getNodeNumber() == dpid) {
+                    rmLink = true;
+                    rmSrc = true;
+                }
+
+                InstanceIdentifier<VtnLink> vpath = InstanceIdentifier.
+                    builder(VtnTopology.class).
+                    child(VtnLink.class, new VtnLinkKey(lid)).build();
+                assertEquals(null, rmLinks.put(vpath, rmLink));
+
+                InstanceIdentifier<PortLink> ppath = InstanceIdentifier.
+                    builder(VtnNodes.class).
+                    child(VtnNode.class, src.getVtnNodeKey()).
+                    child(VtnPort.class, src.getVtnPortKey()).
+                    child(PortLink.class, new PortLinkKey(lid)).build();
+                assertEquals(null, plinks.put(ppath, rmSrc));
+                if (rmSrc) {
+                    PortLink plink = new PortLinkBuilder().
+                        setLinkId(lid).setPeer(dst.getNodeConnectorId()).
+                        build();
+                    Mockito.when(tx.read(oper, ppath)).
+                        thenReturn(getReadResult(plink));
+                }
+
+                ppath = InstanceIdentifier.builder(VtnNodes.class).
+                    child(VtnNode.class, dst.getVtnNodeKey()).
+                    child(VtnPort.class, dst.getVtnPortKey()).
+                    child(PortLink.class, new PortLinkKey(lid)).build();
+                assertEquals(null, plinks.put(ppath, rmDst));
+                if (rmDst) {
+                    PortLink plink = new PortLinkBuilder().
+                        setLinkId(lid).setPeer(src.getNodeConnectorId()).
+                        build();
+                    Mockito.when(tx.read(oper, ppath)).
+                        thenReturn(getReadResult(plink));
+                }
+            }
+
+            InstanceIdentifier<VtnTopology> rpath =
+                InstanceIdentifier.create(VtnTopology.class);
+            VtnTopology root = new VtnTopologyBuilder().
+                setVtnLink(vtnLinks).build();
+            Mockito.when(tx.read(oper, rpath)).thenReturn(getReadResult(root));
+
+            vtnLinkPaths = rmLinks;
+            portLinkPaths = plinks;
+
+            Map<InstanceIdentifier<IgnoredLink>, Boolean> ilinks =
+                new HashMap<>();
+            for (IgnoredLink ilink: ignoredLinks) {
+                SalPort src = SalPort.create(ilink.getSource());
+                SalPort dst = SalPort.create(ilink.getDestination());
+                LinkId lid = ilink.getLinkId();
+                boolean rmLink = (src.getNodeNumber() == dpid ||
+                                  dst.getNodeNumber() == dpid);
+                InstanceIdentifier<IgnoredLink> ipath = InstanceIdentifier.
+                    builder(IgnoredLinks.class).
+                    child(IgnoredLink.class, new IgnoredLinkKey(lid)).build();
+                assertEquals(null, ilinks.put(ipath, rmLink));
+            }
+
+            InstanceIdentifier<IgnoredLinks> irpath =
+                InstanceIdentifier.create(IgnoredLinks.class);
+            IgnoredLinks iroot = new IgnoredLinksBuilder().
+                setIgnoredLink(ignoredLinks).build();
+            Mockito.when(tx.read(oper, irpath)).
+                thenReturn(getReadResult(iroot));
+
+            ignoredLinkPaths = ilinks;
+        }
+
+        /**
+         * Verify results of
+         * {@link InventoryUtils#removeVtnTopologyLink(ReadWriteTransaction,SalNode)}.
+         *
+         * @param tx       A mock-up of {@link ReadWriteTransaction}.
+         * @param invoked  {@code true} means that the method was invoked.
+         */
+        private void verifyVtnLink(ReadWriteTransaction tx, boolean invoked) {
+            LogicalDatastoreType oper = LogicalDatastoreType.OPERATIONAL;
+            InstanceIdentifier<VtnTopology> rpath =
+                InstanceIdentifier.create(VtnTopology.class);
+            if (invoked) {
+                Mockito.verify(tx).read(oper, rpath);
+            } else {
+                Mockito.verify(tx, Mockito.never()).read(oper, rpath);
+            }
+
+            for (Map.Entry<InstanceIdentifier<VtnLink>, Boolean> entry:
+                     vtnLinkPaths.entrySet()) {
+                InstanceIdentifier<VtnLink> vpath = entry.getKey();
+                Mockito.verify(tx, Mockito.never()).read(oper, vpath);
+
+                Boolean removed = entry.getValue();
+                if (invoked && Boolean.TRUE.equals(removed)) {
+                    Mockito.verify(tx).delete(oper, vpath);
+                } else {
+                    Mockito.verify(tx, Mockito.never()).delete(oper, vpath);
+                }
+            }
+
+            for (Map.Entry<InstanceIdentifier<PortLink>, Boolean> entry:
+                     portLinkPaths.entrySet()) {
+                InstanceIdentifier<PortLink> ppath = entry.getKey();
+                Boolean removed = entry.getValue();
+                if (invoked && Boolean.TRUE.equals(removed)) {
+                    Mockito.verify(tx).read(oper, ppath);
+                    Mockito.verify(tx).delete(oper, ppath);
+                } else {
+                    Mockito.verify(tx, Mockito.never()).read(oper, ppath);
+                    Mockito.verify(tx, Mockito.never()).delete(oper, ppath);
+                }
+            }
+        }
+
+        /**
+         * Verify results of
+         * {@link InventoryUtils#removeIgnoredLink(ReadWriteTransaction,SalNode)}.
+         *
+         * @param tx     A mock-up of {@link ReadWriteTransaction}.
+         * @param invoked  {@code true} means that the method was invoked.
+         */
+        private void verifyIgnoredLink(ReadWriteTransaction tx,
+                                       boolean invoked) {
+            LogicalDatastoreType oper = LogicalDatastoreType.OPERATIONAL;
+            InstanceIdentifier<IgnoredLinks> irpath =
+                InstanceIdentifier.create(IgnoredLinks.class);
+            if (invoked) {
+                Mockito.verify(tx).read(oper, irpath);
+            } else {
+                Mockito.verify(tx, Mockito.never()).read(oper, irpath);
+            }
+
+            for (Map.Entry<InstanceIdentifier<IgnoredLink>, Boolean> entry:
+                     ignoredLinkPaths.entrySet()) {
+                InstanceIdentifier<IgnoredLink> ipath = entry.getKey();
+                Mockito.verify(tx, Mockito.never()).read(oper, ipath);
+
+                Boolean removed = entry.getValue();
+                if (invoked && Boolean.TRUE.equals(removed)) {
+                    Mockito.verify(tx).delete(oper, ipath);
+                } else {
+                    Mockito.verify(tx, Mockito.never()).delete(oper, ipath);
+                }
+            }
+        }
+    }
+
+    /**
+     * Generator of {@link SalPort} instances.
+     */
+    private static final class SalPortGenerator {
+        /**
+         * Node number.
+         */
+        private final long  nodeNumber;
+
+        /**
+         * Port number.
+         */
+        private long  portNumber;
+
+        /**
+         * Construct a new instance.
+         *
+         * @param dpid  A node number.
+         */
+        private SalPortGenerator(long dpid) {
+            nodeNumber = dpid;
+        }
+
+        /**
+         * Construct a new {@link SalPort} instance.
+         *
+         * @return  A {@link SalPort} instance.
+         */
+        private SalPort newInstance() {
+            return new SalPort(nodeNumber, ++portNumber);
+        }
+    }
+
+    /**
+     * Test case for the following methods.
+     *
+     * <ul>
+     *   <li>{@link InventoryUtils#removeVtnLink(ReadWriteTransaction,SalNode)}</li>
+     *   <li>{@link InventoryUtils#removeVtnTopologyLink(ReadWriteTransaction,SalNode)}</li>
+     *   <li>{@link InventoryUtils#removeIgnoredLink(ReadWriteTransaction,SalNode)}</li>
+     * </ul>
+     *
+     * @throws Exception  An error occurred.
+     */
+    @Test
+    public void testRemoveVtnLinkByNode() throws Exception {
+        ReadWriteTransaction tx = Mockito.mock(ReadWriteTransaction.class);
+        LogicalDatastoreType oper = LogicalDatastoreType.OPERATIONAL;
+
+        // Root containers are not present.
+        long dpid = 10L;
+        SalNode snode = new SalNode(dpid);
+        InstanceIdentifier<VtnTopology> rpath =
+            InstanceIdentifier.create(VtnTopology.class);
+        InstanceIdentifier<IgnoredLinks> irpath =
+            InstanceIdentifier.create(IgnoredLinks.class);
+        VtnTopology root = null;
+        IgnoredLinks iroot = null;
+        Mockito.when(tx.read(oper, rpath)).thenReturn(getReadResult(root));
+        Mockito.when(tx.read(oper, irpath)).thenReturn(getReadResult(iroot));
+        InventoryUtils.removeVtnLink(tx, snode);
+        Mockito.verify(tx).read(oper, rpath);
+        Mockito.verify(tx).read(oper, irpath);
+
+        InventoryUtils.removeVtnTopologyLink(tx, snode);
+        Mockito.verify(tx, Mockito.times(2)).read(oper, rpath);
+        Mockito.verify(tx).read(oper, irpath);
+
+        InventoryUtils.removeIgnoredLink(tx, snode);
+        Mockito.verify(tx, Mockito.times(2)).read(oper, rpath);
+        Mockito.verify(tx, Mockito.times(2)).read(oper, irpath);
+        Mockito.reset(tx);
+
+        // Root containers contain null list.
+        root = new VtnTopologyBuilder().build();
+        iroot = new IgnoredLinksBuilder().build();
+        Mockito.when(tx.read(oper, rpath)).thenReturn(getReadResult(root));
+        Mockito.when(tx.read(oper, irpath)).thenReturn(getReadResult(iroot));
+        InventoryUtils.removeVtnLink(tx, snode);
+        Mockito.verify(tx).read(oper, rpath);
+        Mockito.verify(tx).read(oper, irpath);
+
+        InventoryUtils.removeVtnTopologyLink(tx, snode);
+        Mockito.verify(tx, Mockito.times(2)).read(oper, rpath);
+        Mockito.verify(tx).read(oper, irpath);
+
+        InventoryUtils.removeIgnoredLink(tx, snode);
+        Mockito.verify(tx, Mockito.times(2)).read(oper, rpath);
+        Mockito.verify(tx, Mockito.times(2)).read(oper, irpath);
+        Mockito.reset(tx);
+
+        // Root containers contain empty list.
+        List<VtnLink> vlinks = new ArrayList<>();
+        List<IgnoredLink> ilinks = new ArrayList<>();
+        root = new VtnTopologyBuilder().setVtnLink(vlinks).build();
+        iroot = new IgnoredLinksBuilder().setIgnoredLink(ilinks).build();
+        Mockito.when(tx.read(oper, rpath)).thenReturn(getReadResult(root));
+        Mockito.when(tx.read(oper, irpath)).thenReturn(getReadResult(iroot));
+        InventoryUtils.removeVtnLink(tx, snode);
+        Mockito.verify(tx).read(oper, rpath);
+        Mockito.verify(tx).read(oper, irpath);
+
+        InventoryUtils.removeVtnTopologyLink(tx, snode);
+        Mockito.verify(tx, Mockito.times(2)).read(oper, rpath);
+        Mockito.verify(tx).read(oper, irpath);
+
+        InventoryUtils.removeIgnoredLink(tx, snode);
+        Mockito.verify(tx, Mockito.times(2)).read(oper, rpath);
+        Mockito.verify(tx, Mockito.times(2)).read(oper, irpath);
+        Mockito.reset(tx);
+
+        SalPortGenerator gen = new SalPortGenerator(dpid);
+        SalPortGenerator gen1 = new SalPortGenerator(dpid + 1L);
+        SalPortGenerator gen2 = new SalPortGenerator(dpid + 2L);
+        SalPortGenerator[] peers = {
+            gen1, gen2,
+        };
+        TopologyLists topo = new TopologyLists();
+        for (int i = 0; i < 5; i++) {
+            SalPort src = gen.newInstance();
+            SalPort dst = gen.newInstance();
+            topo.addVtnLink(src, dst);
+
+            src = gen.newInstance();
+            dst = gen.newInstance();
+            topo.addIgnoredLink(src, dst);
+
+            for (SalPortGenerator pgen: peers) {
+                src = gen.newInstance();
+                dst = pgen.newInstance();
+                topo.addVtnLink(src, dst);
+
+                src = gen.newInstance();
+                dst = pgen.newInstance();
+                topo.addIgnoredLink(src, dst);
+
+                src = pgen.newInstance();
+                dst = gen.newInstance();
+                topo.addVtnLink(src, dst);
+
+                src = pgen.newInstance();
+                dst = gen.newInstance();
+                topo.addIgnoredLink(src, dst);
+            }
+
+            src = gen1.newInstance();
+            dst = gen2.newInstance();
+            topo.addVtnLink(src, dst);
+
+            src = gen1.newInstance();
+            dst = gen2.newInstance();
+            topo.addIgnoredLink(src, dst);
+
+            src = gen2.newInstance();
+            dst = gen1.newInstance();
+            topo.addVtnLink(src, dst);
+
+            src = gen2.newInstance();
+            dst = gen1.newInstance();
+            topo.addIgnoredLink(src, dst);
+        }
+
+        topo.setUp(tx, snode);
+        InventoryUtils.removeVtnLink(tx, snode);
+        topo.verifyVtnLink(tx, true);
+        topo.verifyIgnoredLink(tx, true);
+        Mockito.reset(tx);
+
+        topo.setUp(tx, snode);
+        InventoryUtils.removeVtnTopologyLink(tx, snode);
+        topo.verifyVtnLink(tx, true);
+        topo.verifyIgnoredLink(tx, false);
+        Mockito.reset(tx);
+
+        topo.setUp(tx, snode);
+        InventoryUtils.removeIgnoredLink(tx, snode);
+        topo.verifyVtnLink(tx, false);
+        topo.verifyIgnoredLink(tx, true);
+        Mockito.reset(tx);
+    }
+
+    /**
+     * Test case for the following methods.
+     *
+     * <ul>
+     *   <li>{@link InventoryUtils#removeVtnLink(ReadWriteTransaction,VtnPort)}</li>
+     *   <li>{@link InventoryUtils#removePortLink(ReadWriteTransaction,SalPort,LinkId)}</li>
+     * </ul>
+     *
+     * @throws Exception  An error occurred.
+     */
+    @Test
+    public void testRemoveVtnLinkByPort() throws Exception {
+        ReadWriteTransaction tx = Mockito.mock(ReadWriteTransaction.class);
+        LogicalDatastoreType oper = LogicalDatastoreType.OPERATIONAL;
+
+        // Port link is null.
+        VtnPort vport = new VtnPortBuilder().build();
+        InventoryUtils.removeVtnLink(tx, vport);
+        Mockito.verifyZeroInteractions(tx);
+
+        // Port link is empty.
+        List<PortLink> plist = new ArrayList<>();
+        vport = new VtnPortBuilder().setPortLink(plist).build();
+        InventoryUtils.removeVtnLink(tx, vport);
+        Mockito.verifyZeroInteractions(tx);
+
+        Map<InstanceIdentifier<VtnLink>, VtnLink> vtnLinks = new HashMap<>();
+        Map<InstanceIdentifier<PortLink>, PortLink> peerLinks =
+            new HashMap<>();
+        Set<InstanceIdentifier<PortLink>> portLinks = new HashSet<>();
+        long dpid = 10L;
+        final SalPort sport = new SalPort(dpid, 3L);
+        final SalPort peer = new SalPort(dpid + 10L, 1L);
+
+        LinkId lid1 = new LinkId(sport.toString());
+        InstanceIdentifier<PortLink> portPath1 = InstanceIdentifier.
+            builder(VtnNodes.class).
+            child(VtnNode.class, sport.getVtnNodeKey()).
+            child(VtnPort.class, sport.getVtnPortKey()).
+            child(PortLink.class, new PortLinkKey(lid1)).build();
+        PortLink portLink1 = new PortLinkBuilder().
+            setLinkId(lid1).setPeer(peer.getNodeConnectorId()).build();
+        InstanceIdentifier<PortLink> peerPath1 = InstanceIdentifier.
+            builder(VtnNodes.class).
+            child(VtnNode.class, peer.getVtnNodeKey()).
+            child(VtnPort.class, peer.getVtnPortKey()).
+            child(PortLink.class, new PortLinkKey(lid1)).build();
+        InstanceIdentifier<VtnLink> vpath1 = InstanceIdentifier.
+            builder(VtnTopology.class).
+            child(VtnLink.class, new VtnLinkKey(lid1)).build();
+
+        LinkId lid2 = new LinkId(peer.toString());
+        InstanceIdentifier<PortLink> portPath2 = InstanceIdentifier.
+            builder(VtnNodes.class).
+            child(VtnNode.class, sport.getVtnNodeKey()).
+            child(VtnPort.class, sport.getVtnPortKey()).
+            child(PortLink.class, new PortLinkKey(lid2)).build();
+        PortLink portLink2 = new PortLinkBuilder().
+            setLinkId(lid2).setPeer(peer.getNodeConnectorId()).build();
+        InstanceIdentifier<PortLink> peerPath2 = InstanceIdentifier.
+            builder(VtnNodes.class).
+            child(VtnNode.class, peer.getVtnNodeKey()).
+            child(VtnPort.class, peer.getVtnPortKey()).
+            child(PortLink.class, new PortLinkKey(lid2)).build();
+        InstanceIdentifier<VtnLink> vpath2 = InstanceIdentifier.
+            builder(VtnTopology.class).
+            child(VtnLink.class, new VtnLinkKey(lid2)).build();
+        List<PortLink> plinks = new ArrayList<>();
+        Collections.addAll(plinks, portLink1, portLink2);
+        vport = new VtnPortBuilder().setId(sport.getNodeConnectorId()).
+            setPortLink(plinks).build();
+
+        // No link information is present.
+        Mockito.when(tx.read(oper, peerPath1)).
+            thenReturn(getReadResult((PortLink)null));
+        Mockito.when(tx.read(oper, peerPath2)).
+            thenReturn(getReadResult((PortLink)null));
+        Mockito.when(tx.read(oper, vpath1)).
+            thenReturn(getReadResult((VtnLink)null));
+        Mockito.when(tx.read(oper, vpath2)).
+            thenReturn(getReadResult((VtnLink)null));
+
+        InventoryUtils.removeVtnLink(tx, vport);
+        Mockito.verify(tx).read(oper, peerPath1);
+        Mockito.verify(tx).read(oper, peerPath2);
+        Mockito.verify(tx).read(oper, vpath1);
+        Mockito.verify(tx).read(oper, vpath2);
+        Mockito.verify(tx, Mockito.never()).delete(oper, peerPath1);
+        Mockito.verify(tx, Mockito.never()).delete(oper, peerPath2);
+        Mockito.verify(tx, Mockito.never()).delete(oper, vpath1);
+        Mockito.verify(tx, Mockito.never()).delete(oper, vpath2);
+        Mockito.reset(tx);
+
+        // Link information is present.
+        PortLink peerLink1 = new PortLinkBuilder().
+            setLinkId(lid1).setPeer(sport.getNodeConnectorId()).build();
+        PortLink peerLink2 = new PortLinkBuilder().
+            setLinkId(lid2).setPeer(sport.getNodeConnectorId()).build();
+        VtnLink vlink1 = new VtnLinkBuilder().
+            setLinkId(lid1).setSource(sport.getNodeConnectorId()).
+            setDestination(peer.getNodeConnectorId()).build();
+        VtnLink vlink2 = new VtnLinkBuilder().
+            setLinkId(lid2).setSource(peer.getNodeConnectorId()).
+            setDestination(sport.getNodeConnectorId()).build();
+        Mockito.when(tx.read(oper, peerPath1)).
+            thenReturn(getReadResult(peerLink1));
+        Mockito.when(tx.read(oper, peerPath2)).
+            thenReturn(getReadResult(peerLink2));
+        Mockito.when(tx.read(oper, vpath1)).thenReturn(getReadResult(vlink1));
+        Mockito.when(tx.read(oper, vpath2)).thenReturn(getReadResult(vlink2));
+
+        InventoryUtils.removeVtnLink(tx, vport);
+        Mockito.verify(tx).read(oper, peerPath1);
+        Mockito.verify(tx).read(oper, peerPath2);
+        Mockito.verify(tx).read(oper, vpath1);
+        Mockito.verify(tx).read(oper, vpath2);
+        Mockito.verify(tx).delete(oper, peerPath1);
+        Mockito.verify(tx).delete(oper, peerPath2);
+        Mockito.verify(tx).delete(oper, vpath1);
+        Mockito.verify(tx).delete(oper, vpath2);
+    }
+
+    /**
+     * Test environment for {@link #testResolveIgnoredLinks()}.
+     */
+    private final class ResolveLinkEnv {
+        /**
+         * The mock-up of MD-SAL datastore transaction.
+         */
+        private final ReadWriteTransaction  transaction =
+            Mockito.mock(ReadWriteTransaction.class);
+
+        /**
+         * The mock-up of logger instance.
+         */
+        private final Logger  logger = Mockito.mock(Logger.class);
+
+        /**
+         * Inventory reader.
+         */
+        private final InventoryReader  reader =
+            new InventoryReader(transaction);
+
+        /**
+         * A list of {@link IgnoredLink} instances.
+         */
+        private final List<IgnoredLink>  ignoredLinks = new ArrayList<>();
+
+        /**
+         * VTN ignored links to be removed.
+         */
+        private final Set<InstanceIdentifier<IgnoredLink>> removedLinks =
+            new HashSet<>();
+
+        /**
+         * VTN links to be created.
+         */
+        private final Map<InstanceIdentifier<VtnLink>, VtnLink> vtnLinks =
+            new HashMap<>();
+
+        /**
+         * VTN port links to be created.
+         */
+        private final Map<InstanceIdentifier<PortLink>, PortLink> portLinks =
+            new HashMap<>();
+
+        /**
+         * Return the mock-up of MD-SAL transaction.
+         *
+         * @return  A {@link ReadWriteTransaction} instance.
+         */
+        private ReadWriteTransaction getTransaction() {
+            return transaction;
+        }
+
+        /**
+         * Return the mock-up of logger instance.
+         *
+         * @return  A {@link Logger} instance.
+         */
+        private Logger getLogger() {
+            return logger;
+        }
+
+        /**
+         * Return the inventory reader for test.
+         *
+         * @return  An {@link InventoryReader} instance.
+         */
+        private InventoryReader getInventoryReader() {
+            return reader;
+        }
+
+        /**
+         * Add the given link information.
+         *
+         * @param src         The source port.
+         * @param srcPresent  {@code true} means that the source port is
+         *                    present.
+         * @param dst         The destination port.
+         * @param dstPresent  {@code true} means that the destination port is
+         *                    present.
+         */
+        private void add(SalPort src, boolean srcPresent, SalPort dst,
+                         boolean dstPresent) {
+            LinkId lid = new LinkId(src.toString());
+            IgnoredLink ilink = new IgnoredLinkBuilder().
+                setLinkId(lid).setSource(src.getNodeConnectorId()).
+                setDestination(dst.getNodeConnectorId()).build();
+            ignoredLinks.add(ilink);
+
+            VtnPort vport;
+            if (srcPresent) {
+                vport = new VtnPortBuilder().
+                    setId(src.getNodeConnectorId()).build();
+            } else {
+                vport = null;
+            }
+            reader.prefetch(src, vport);
+
+            if (dstPresent) {
+                vport = new VtnPortBuilder().
+                    setId(dst.getNodeConnectorId()).build();
+            } else {
+                vport = null;
+            }
+            reader.prefetch(dst, vport);
+
+            InstanceIdentifier<IgnoredLink> ipath = InstanceIdentifier.
+                builder(IgnoredLinks.class).
+                child(IgnoredLink.class, new IgnoredLinkKey(lid)).build();
+            InstanceIdentifier<VtnLink> vpath = InstanceIdentifier.
+                builder(VtnTopology.class).
+                child(VtnLink.class, new VtnLinkKey(lid)).build();
+            InstanceIdentifier<PortLink> spath = InstanceIdentifier.
+                builder(VtnNodes.class).
+                child(VtnNode.class, src.getVtnNodeKey()).
+                child(VtnPort.class, src.getVtnPortKey()).
+                child(PortLink.class, new PortLinkKey(lid)).build();
+            InstanceIdentifier<PortLink> dpath = InstanceIdentifier.
+                builder(VtnNodes.class).
+                child(VtnNode.class, dst.getVtnNodeKey()).
+                child(VtnPort.class, dst.getVtnPortKey()).
+                child(PortLink.class, new PortLinkKey(lid)).build();
+            assertEquals(false, vtnLinks.containsKey(vpath));
+            assertEquals(false, portLinks.containsKey(spath));
+            assertEquals(false, portLinks.containsKey(dpath));
+
+            VtnLink vlink = null;
+            PortLink splink = null;
+            PortLink dplink = null;
+            if (srcPresent && dstPresent) {
+                assertEquals(true, removedLinks.add(ipath));
+                NodeConnectorId sncid = src.getNodeConnectorId();
+                NodeConnectorId dncid = dst.getNodeConnectorId();
+                vlink = new VtnLinkBuilder().setLinkId(lid).
+                    setSource(sncid).setDestination(dncid).build();
+                splink = new PortLinkBuilder().setLinkId(lid).setPeer(dncid).
+                    build();
+                dplink = new PortLinkBuilder().setLinkId(lid).setPeer(sncid).
+                    build();
+            }
+
+            assertEquals(null, vtnLinks.put(vpath, vlink));
+            assertEquals(null, portLinks.put(spath, splink));
+            assertEquals(null, portLinks.put(dpath, dplink));
+        }
+
+        /**
+         * Run the test.
+         *
+         * @throws Exception  An error occurred.
+         */
+        private void runTest() throws Exception {
+            ReadWriteTransaction tx = transaction;
+            LogicalDatastoreType oper = LogicalDatastoreType.OPERATIONAL;
+            InstanceIdentifier<IgnoredLinks> irpath =
+                InstanceIdentifier.create(IgnoredLinks.class);
+            IgnoredLinks iroot = new IgnoredLinksBuilder().
+                setIgnoredLink(ignoredLinks).build();
+            Mockito.when(tx.read(oper, irpath)).
+                thenReturn(getReadResult(iroot));
+            InventoryUtils.resolveIgnoredLinks(tx, reader, logger);
+
+            for (IgnoredLink ilink: ignoredLinks) {
+                LinkId lid = ilink.getLinkId();
+                InstanceIdentifier<IgnoredLink> ipath = InstanceIdentifier.
+                    builder(IgnoredLinks.class).
+                    child(IgnoredLink.class, new IgnoredLinkKey(lid)).build();
+                String linkId = lid.getValue();
+                SalPort src = SalPort.create(ilink.getSource());
+                SalPort dst = SalPort.create(ilink.getDestination());
+                String msg =
+                    "Inter-switch link has been resolved: {}: {} -> {}";
+                if (removedLinks.contains(ipath)) {
+                    Mockito.verify(tx).delete(oper, ipath);
+                    Mockito.verify(logger).info(msg, linkId, src, dst);
+                } else {
+                    Mockito.verify(tx, Mockito.never()).delete(oper, ipath);
+                    Mockito.verify(logger, Mockito.never()).
+                        info(msg, linkId, src, dst);
+                }
+            }
+
+            for (Map.Entry<InstanceIdentifier<VtnLink>, VtnLink> entry:
+                     vtnLinks.entrySet()) {
+                InstanceIdentifier<VtnLink> vpath = entry.getKey();
+                VtnLink vlink = entry.getValue();
+                if (vlink == null) {
+                    Mockito.verify(tx, Mockito.never()).
+                        merge(Mockito.any(LogicalDatastoreType.class),
+                              Mockito.eq(vpath), Mockito.any(VtnLink.class),
+                              Mockito.anyBoolean());
+                } else {
+                    Mockito.verify(tx).merge(oper, vpath, vlink, true);
+                }
+            }
+
+            for (Map.Entry<InstanceIdentifier<PortLink>, PortLink> entry:
+                     portLinks.entrySet()) {
+                InstanceIdentifier<PortLink> ppath = entry.getKey();
+                PortLink plink = entry.getValue();
+                if (plink == null) {
+                    Mockito.verify(tx, Mockito.never()).
+                        merge(Mockito.any(LogicalDatastoreType.class),
+                              Mockito.eq(ppath), Mockito.any(PortLink.class),
+                              Mockito.anyBoolean());
+                } else {
+                    Mockito.verify(tx).merge(oper, ppath, plink, true);
+                }
+            }
+        }
+    }
+
+    /**
+     * Test case for
+     * {@link InventoryUtils#resolveIgnoredLinks(ReadWriteTransaction,InventoryReader,Logger)}.
+     *
+     * @throws Exception  An error occurred.
+     */
+    @Test
+    public void testResolveIgnoredLinks() throws Exception {
+        ResolveLinkEnv env = new ResolveLinkEnv();
+        ReadWriteTransaction tx = env.getTransaction();
+        Logger log = env.getLogger();
+        InventoryReader reader = env.getInventoryReader();
+        LogicalDatastoreType oper = LogicalDatastoreType.OPERATIONAL;
+
+        // Root container is not present.
+        InstanceIdentifier<IgnoredLinks> irpath =
+            InstanceIdentifier.create(IgnoredLinks.class);
+        IgnoredLinks iroot = null;
+        Mockito.when(tx.read(oper, irpath)).thenReturn(getReadResult(iroot));
+        InventoryUtils.resolveIgnoredLinks(tx, reader, log);
+        Mockito.verify(tx).read(oper, irpath);
+        Mockito.verifyZeroInteractions(log);
+        Mockito.reset(tx, log);
+
+        // Ignored link list is null.
+        iroot = new IgnoredLinksBuilder().build();
+        Mockito.when(tx.read(oper, irpath)).thenReturn(getReadResult(iroot));
+        InventoryUtils.resolveIgnoredLinks(tx, reader, log);
+        Mockito.verify(tx).read(oper, irpath);
+        Mockito.verifyZeroInteractions(log);
+        Mockito.reset(tx, log);
+
+        // Ignored link list is empty.
+        List<IgnoredLink> ilinks = new ArrayList<>();
+        iroot = new IgnoredLinksBuilder().setIgnoredLink(ilinks).build();
+        Mockito.when(tx.read(oper, irpath)).thenReturn(getReadResult(iroot));
+        InventoryUtils.resolveIgnoredLinks(tx, reader, log);
+        Mockito.verify(tx).read(oper, irpath);
+        Mockito.verifyZeroInteractions(log);
+        Mockito.reset(tx, log);
+
+        SalPortGenerator[] generators = {
+            new SalPortGenerator(10L),
+            new SalPortGenerator(11L),
+            new SalPortGenerator(20L),
+        };
+
+        for (SalPortGenerator gen1: generators) {
+            for (SalPortGenerator gen2: generators) {
+                if (gen1.equals(gen2)) {
+                    continue;
+                }
+
+                // Both source and destination ports are not present.
+                SalPort src = gen1.newInstance();
+                SalPort dst = gen2.newInstance();
+                env.add(src, false, dst, false);
+
+                // Only source port is present.
+                src = gen1.newInstance();
+                dst = gen2.newInstance();
+                env.add(src, true, dst, false);
+
+                // Only destination port is present.
+                src = gen1.newInstance();
+                dst = gen2.newInstance();
+                env.add(src, false, dst, true);
+
+                // Both source and destination ports are present.
+                src = gen1.newInstance();
+                dst = gen2.newInstance();
+                env.add(src, true, dst, true);
+            }
+        }
+
+        env.runTest();
+    }
 }
index 96ec5bf9a20b2704eb7614ae979d9e49e230e80c..7062d84004187c57c0b152eedb581e564274c352 100644 (file)
 package org.opendaylight.vtn.manager.it.ofmock.impl;
 
 import java.math.BigInteger;
+import java.util.Collections;
 import java.util.Dictionary;
 import java.util.HashSet;
 import java.util.Hashtable;
 import java.util.Iterator;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.Timer;
+import java.util.TimerTask;
 import java.util.concurrent.atomic.AtomicReference;
 
 import org.slf4j.Logger;
@@ -26,47 +30,97 @@ import org.osgi.framework.BundleContext;
 import org.osgi.framework.FrameworkUtil;
 import org.osgi.framework.ServiceRegistration;
 
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+
 import org.opendaylight.controller.connectionmanager.IConnectionManager;
 import org.opendaylight.controller.sal.connection.ConnectionLocality;
+import org.opendaylight.controller.sal.core.AdvertisedBandwidth;
+import org.opendaylight.controller.sal.core.Bandwidth;
+import org.opendaylight.controller.sal.core.Buffers;
+import org.opendaylight.controller.sal.core.Capabilities;
+import org.opendaylight.controller.sal.core.Config;
+import org.opendaylight.controller.sal.core.MacAddress;
+import org.opendaylight.controller.sal.core.Name;
 import org.opendaylight.controller.sal.core.Node;
 import org.opendaylight.controller.sal.core.NodeConnector;
+import org.opendaylight.controller.sal.core.PeerBandwidth;
 import org.opendaylight.controller.sal.core.Property;
+import org.opendaylight.controller.sal.core.SupportedBandwidth;
+import org.opendaylight.controller.sal.core.Tables;
+import org.opendaylight.controller.sal.core.TimeStamp;
 import org.opendaylight.controller.sal.core.UpdateType;
 import org.opendaylight.controller.sal.flowprogrammer.IPluginInFlowProgrammerService;
 import org.opendaylight.controller.sal.inventory.IListenInventoryUpdates;
 import org.opendaylight.controller.sal.inventory.IPluginInInventoryService;
+import org.opendaylight.controller.sal.inventory.IPluginOutInventoryService;
 import org.opendaylight.controller.sal.utils.GlobalConstants;
 import org.opendaylight.controller.sal.utils.NodeConnectorCreator;
 import org.opendaylight.controller.sal.utils.NodeCreator;
 import org.opendaylight.controller.switchmanager.IInventoryListener;
 import org.opendaylight.controller.switchmanager.ISwitchManager;
 
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FeatureCapability;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowFeatureCapabilityFlowStats;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.flow.node.SwitchFeatures;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.port.rev130925.FlowCapablePort;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.port.rev130925.PortConfig;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.port.rev130925.PortFeatures;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.port.rev130925.flow.capable.port.State;
+
 /**
  * AD-SAL inventory management.
  */
-public final class AdSalInventory implements IInventoryListener {
+public final class AdSalInventory
+    implements IInventoryListener, AutoCloseable {
     /**
      * Logger instance.
      */
     private static final Logger  LOG =
         LoggerFactory.getLogger(AdSalInventory.class);
 
+    /**
+     * The number of bytes in a MAC address.
+     */
+    private static final int  MAC_ADDRESS_LENGTH = 6;
+
     /**
      * The number of milliseconds to wait for AD-SAL inventory to be
      * synchronized.
      */
     private static final long  SYNC_TIMEOUT = 30000L;
 
+    /**
+     * The number of milliseconds to wait for sal-compatibility to update
+     * the AD-SAL inventory.
+     */
+    private static final long  COMPAT_TIMEOUT = 2000L;
+
     /**
      * The number of milliseconds between polls.
      */
     private static final long  POLL_INTERVAL = 500L;
 
+    /**
+     * AD-SAL switch manager service.
+     */
+    private final ISwitchManager  switchManager;
+
     /**
      * AD-SAL connection manager service.
      */
     private final IConnectionManager  connectionManager;
 
+    /**
+     * AD-SAL inventory output service.
+     */
+    private final IPluginOutInventoryService  inventoryOutGlobal;
+
+    /**
+     * AD-SAL inventory output service for the default container.
+     */
+    private final IPluginOutInventoryService  inventoryOut;
+
     /**
      * An OSGi bundle context for this OSGi bundle.
      */
@@ -88,6 +142,29 @@ public final class AdSalInventory implements IInventoryListener {
     private final AtomicReference<ServiceRegistration<?>> inventoryListener =
         new AtomicReference<>();
 
+    /**
+     * A set of MD-SAL listeners.
+     */
+    private final AtomicReference<Set<DataStoreListener<?, ?>>> dataListeners =
+        new AtomicReference<>();
+
+    /**
+     * A map that keeps current AD-SAL nodes.
+     */
+    private final Map<Node, Set<Property>>  adNodes = new Hashtable<>();
+
+    /**
+     * A map that keeps current AD-SAL node connectors.
+     */
+    private final Map<NodeConnector, Set<Property>> adNodeConnectors =
+        new Hashtable<>();
+
+    /**
+     * Timer thread used to maintain AD-SAL inventory.
+     */
+    private final AtomicReference<Timer>  inventoryTimer =
+        new AtomicReference<>();
+
     /**
      * Convert the given MD-SAL node identifier into AD-SAL node.
      *
@@ -132,13 +209,182 @@ public final class AdSalInventory implements IInventoryListener {
             "Invalid node connector identifier: " + pid);
     }
 
+    /**
+     * Convert the given MD-SAL flow-node into a set of AD-SAL node properties.
+
+     * @param node  A {@link Node} instance.
+     * @param fn    A {@link FlowNode} instance.
+     * @return  A set of AD-SAL node properties.
+     */
+    public static Set<Property> toProperties(Node node, FlowNode fn) {
+        Set<Property> props = new HashSet<>();
+        Object id = node.getID();
+        long dpid = (id instanceof Number) ? ((Number)id).longValue() : 0;
+        byte[] mac = new byte[MAC_ADDRESS_LENGTH];
+        for (int i = 1; i <= mac.length; i++) {
+            mac[mac.length - i] = (byte)dpid;
+            dpid >>>= Byte.SIZE;
+        }
+        props.add(new MacAddress(mac));
+        props.add(new TimeStamp(System.currentTimeMillis(), "connectedSince"));
+
+        SwitchFeatures swf = fn.getSwitchFeatures();
+        if (swf != null) {
+            Short tables = swf.getMaxTables();
+            if (tables != null) {
+                props.add(new Tables(tables.byteValue()));
+            }
+
+            Long buffers = swf.getMaxBuffers();
+            if (buffers != null) {
+                props.add(new Buffers(buffers.intValue()));
+            }
+
+            setNodeCapabilities(props, swf.getCapabilities());
+        }
+
+        return Collections.unmodifiableSet(props);
+    }
+
+    /**
+     * Convert the given MD-SAL flow-capable-port into a set of AD-SAL node
+     * connector properties.
+     *
+     * @param fcp  A {@link FlowCapablePort} instance.
+     * @return  A set of AD-SAL node connector properties.
+     */
+    public static Set<Property> toProperties(FlowCapablePort fcp) {
+        Set<Property> props = toBandwidthProps(fcp);
+        String name = fcp.getName();
+        if (name != null) {
+            props.add(new Name(name));
+        }
+
+        PortConfig pc = fcp.getConfiguration();
+        if (pc != null) {
+            short cfg = (pc.isPORTDOWN())
+                ? Config.ADMIN_DOWN : Config.ADMIN_UP;
+            props.add(new Config(cfg));
+        }
+
+        State state = fcp.getState();
+        if (state != null) {
+            short st = (state.isLinkDown())
+                ? org.opendaylight.controller.sal.core.State.EDGE_DOWN
+                : org.opendaylight.controller.sal.core.State.EDGE_UP;
+            props.add(new org.opendaylight.controller.sal.core.State(st));
+        }
+
+        return Collections.unmodifiableSet(props);
+    }
+
+    /**
+     * Set AD-SAL node properties from the given capability list.
+     *
+     * @param props  A set of AD-SAL node properties.
+     * @param fclist A list of MD-SAL node capability classes.
+     */
+    public static void setNodeCapabilities(
+        Set<Property> props, List<Class<? extends FeatureCapability>> fclist) {
+        if (fclist != null) {
+            int bits = 0;
+            for (Class<? extends FeatureCapability> fc: fclist) {
+                if (fc.equals(FlowFeatureCapabilityFlowStats.class)) {
+                    bits |= Capabilities.CapabilitiesType.
+                        FLOW_STATS_CAPABILITY.getValue();
+                }
+            }
+
+            props.add(new Capabilities(bits));
+        }
+    }
+
+    /**
+     * Create AD-SAL node connector properties related to bandwidth.
+     *
+     * @param fcp    A {@link FlowCapablePort} instance.
+     * @return  A set of AD-SAL node connector properties.
+     */
+    public static Set<Property> toBandwidthProps(FlowCapablePort fcp) {
+        Set<Property> props = new HashSet<>();
+        PortFeatures pf = fcp.getCurrentFeature();
+        if (pf != null) {
+            long bw = toBandwidth(pf);
+            if (bw != Bandwidth.BWUNK) {
+                props.add(new Bandwidth(bw));
+            }
+        }
+
+        pf = fcp.getAdvertisedFeatures();
+        if (pf != null) {
+            long bw = toBandwidth(pf);
+            if (bw != Bandwidth.BWUNK) {
+                props.add(new AdvertisedBandwidth(bw));
+            }
+        }
+
+        pf = fcp.getSupported();
+        if (pf != null) {
+            long bw = toBandwidth(pf);
+            if (bw != Bandwidth.BWUNK) {
+                props.add(new SupportedBandwidth(bw));
+            }
+        }
+
+        pf = fcp.getPeerFeatures();
+        if (pf != null) {
+            long bw = toBandwidth(pf);
+            if (bw != Bandwidth.BWUNK) {
+                props.add(new PeerBandwidth(bw));
+            }
+        }
+
+        return props;
+    }
+
+    /**
+     * Convert the given MD-SAL port features into AD-SAL bandwidth value.
+     *
+     * @param pf  A {@link PortFeatures} instance.
+     * @return  An AD-SAL bandwidth value.
+     */
+    public static long toBandwidth(PortFeatures pf) {
+        if (pf.isTenMbHd() || pf.isTenMbFd()) {
+            return Bandwidth.BW10Mbps;
+        }
+        if (pf.isHundredMbHd() || pf.isHundredMbFd()) {
+            return Bandwidth.BW100Mbps;
+        }
+        if (pf.isOneGbHd() || pf.isOneGbFd()) {
+            return Bandwidth.BW1Gbps;
+        }
+        if (pf.isOneGbFd()) {
+            return Bandwidth.BW10Gbps;
+        }
+        if (pf.isTenGbFd()) {
+            return Bandwidth.BW10Gbps;
+        }
+        if (pf.isFortyGbFd()) {
+            return Bandwidth.BW40Gbps;
+        }
+        if (pf.isHundredGbFd()) {
+            return Bandwidth.BW100Gbps;
+        }
+        if (pf.isOneTbFd()) {
+            return Bandwidth.BW1Tbps;
+        }
+
+        return Bandwidth.BWUNK;
+    }
+
     /**
      * Construct a new instance.
      *
+     * @param broker  Data broker service.
      * @throws InterruptedException
      *    The calling thread was interrupted.
      */
-    public AdSalInventory() throws InterruptedException {
+    public AdSalInventory(DataBroker broker) throws InterruptedException {
         Bundle bundle = FrameworkUtil.getBundle(AdSalInventory.class);
         if (bundle == null) {
             // This should never happen.
@@ -154,10 +400,20 @@ public final class AdSalInventory implements IInventoryListener {
 
         // Wait for mandatory OSGi services to be registered.
         String container = GlobalConstants.DEFAULT.toString();
-        new ServiceWaiter<ISwitchManager>(bc, ISwitchManager.class, container).
-            await();
+        switchManager = new ServiceWaiter<ISwitchManager>(
+            bc, ISwitchManager.class, container).await();
         connectionManager = new ServiceWaiter<IConnectionManager>(
-            bc, IConnectionManager.class).await();
+            bc, IConnectionManager.class).
+            setFilter(ServiceWaiter.PROP_SCOPE, ServiceWaiter.SCOPE_GLOBAL).
+            await();
+        inventoryOut = new ServiceWaiter<IPluginOutInventoryService>(
+            bc, IPluginOutInventoryService.class, container).await();
+        inventoryOutGlobal = new ServiceWaiter<IPluginOutInventoryService>(
+            bc, IPluginOutInventoryService.class).
+            setFilter(ServiceWaiter.PROP_SCOPE, ServiceWaiter.SCOPE_GLOBAL).
+            await();
+
+        inventoryTimer.set(new Timer("AD-SAL inventory timer"));
 
         // Register AD-SAL inventory listener.
         Dictionary<String, Object> props = new Hashtable<>();
@@ -177,12 +433,111 @@ public final class AdSalInventory implements IInventoryListener {
         // Wait for sal-compatibility services to be registered.
         new ServiceWaiter<IPluginInInventoryService>(
             bc, IPluginInInventoryService.class, container).await();
+        new ServiceWaiter<IPluginInInventoryService>(
+            bc, IPluginInInventoryService.class).await();
         new ServiceWaiter<IPluginInFlowProgrammerService>(
             bc, IPluginInFlowProgrammerService.class).await();
 
+        // Register MD-SAL inventory listeners.
+        Set<DataStoreListener<?, ?>> mdSet = new HashSet<>();
+        dataListeners.set(mdSet);
+        mdSet.add(new MdNodeListener(broker, this));
+        mdSet.add(new MdPortListener(broker, this));
+
         LOG.debug("AD-SAL inventory management has been initialized.");
     }
 
+    /**
+     * Notify that the given node has been updated.
+     *
+     * @param nid   The MD-SAL node identifier.
+     * @param fn    A {@link FlowNode} instance.
+     * @param type  An {@link UpdateType} instance which indicates the type of
+     *              the event.
+     */
+    public void notifyNodeUpdated(String nid, FlowNode fn, UpdateType type) {
+        final Node node = toAdNode(nid);
+        Set<Property> props = toProperties(node, fn);
+        adNodes.put(node, props);
+
+        if (type == UpdateType.ADDED) {
+            Timer timer = inventoryTimer.get();
+            if (timer != null) {
+                TimerTask task = new TimerTask() {
+                    @Override
+                    public void run() {
+                        publishNodeAdded(node);
+                    }
+                };
+                timer.schedule(task, COMPAT_TIMEOUT);
+            }
+        } else {
+            inventoryOutGlobal.updateNode(node, type, props);
+            inventoryOut.updateNode(node, type, props);
+        }
+    }
+
+    /**
+     * Notify that the given node has been removed.
+     *
+     * @param nid  The MD-SAL node identifier.
+     */
+    public void notifyNodeRemoved(String nid) {
+        Node node = toAdNode(nid);
+        adNodes.remove(node);
+
+        Set<Property> props = Collections.<Property>emptySet();
+        UpdateType type = UpdateType.REMOVED;
+        inventoryOutGlobal.updateNode(node, type, props);
+        inventoryOut.updateNode(node, type, props);
+    }
+
+    /**
+     * Notify that the given node connector has been updated.
+     *
+     * @param pid   The MD-SAL port identifier.
+     * @param fcp   A {@link FlowCapablePort} instance.
+     * @param type  An {@link UpdateType} instance which indicates the type of
+     *              the event.
+     */
+    public void notifyPortUpdated(String pid, FlowCapablePort fcp,
+                                  UpdateType type) {
+        final NodeConnector nc = toAdNodeConnector(pid);
+        Set<Property> props = toProperties(fcp);
+        adNodeConnectors.put(nc, props);
+
+        if (type == UpdateType.ADDED) {
+            Timer timer = inventoryTimer.get();
+            if (timer != null) {
+                TimerTask task = new TimerTask() {
+                    @Override
+                    public void run() {
+                        publishPortAdded(nc);
+                    }
+                };
+                timer.schedule(task, COMPAT_TIMEOUT);
+            }
+        } else {
+            inventoryOutGlobal.updateNodeConnector(nc, type, props);
+            inventoryOut.updateNodeConnector(nc, type, props);
+        }
+    }
+
+    /**
+     * Notify that the given node connector has been removed.
+     *
+     * @param pid  The MD-SAL node connector identifier.
+     */
+    public void notifyPortRemoved(String pid) {
+        NodeConnector nc = toAdNodeConnector(pid);
+        adNodeConnectors.remove(nc);
+
+        Set<Property> props = Collections.<Property>emptySet();
+        UpdateType type = UpdateType.REMOVED;
+        inventoryOutGlobal.updateNodeConnector(nc, type, props);
+        inventoryOut.updateNodeConnector(nc, type, props);
+    }
+
     /**
      * Wait for the AD-SAL switch manager to detect the given node.
      *
@@ -261,20 +616,6 @@ public final class AdSalInventory implements IInventoryListener {
         }
     }
 
-    /**
-     * Stop the service.
-     */
-    public void close() {
-        ServiceRegistration<?> reg = inventoryListener.getAndSet(null);
-        if (reg != null) {
-            try {
-                reg.unregister();
-            } catch (RuntimeException e) {
-                LOG.warn("Failed to unregister IInventoryListener.", e);
-            }
-        }
-    }
-
     /**
      * Wait for the given AD-SAL node to be created.
      *
@@ -300,6 +641,43 @@ public final class AdSalInventory implements IInventoryListener {
         throw new IllegalStateException(msg);
     }
 
+    /**
+     * Publish AD-SAL node creation event by force.
+     *
+     * @param node  A {@link Node} instance.
+     */
+    private void publishNodeAdded(Node node) {
+        Set<Property> props = adNodes.get(node);
+        if (props != null && !switchManager.getNodes().contains(node)) {
+            LOG.warn("Notifying AD-SAL node creation: {}", node);
+            UpdateType type = UpdateType.ADDED;
+            inventoryOutGlobal.updateNode(node, type, props);
+            inventoryOut.updateNode(node, type, props);
+        }
+    }
+
+    /**
+     * Publish AD-SAL node connector creation event by force.
+     *
+     * @param nc  A {@link NodeConnector} instance.
+     */
+    private void publishPortAdded(NodeConnector nc) {
+        Set<Property> props = adNodeConnectors.get(nc);
+        if (props == null) {
+            // The target node connector is already removed.
+            return;
+        }
+
+        Node node = nc.getNode();
+        Set<NodeConnector> ncSet = switchManager.getNodeConnectors(node);
+        if (ncSet == null || !ncSet.contains(nc)) {
+            LOG.warn("Notifying AD-SAL node connector creation: {}", nc);
+            UpdateType type = UpdateType.ADDED;
+            inventoryOutGlobal.updateNodeConnector(nc, type, props);
+            inventoryOut.updateNodeConnector(nc, type, props);
+        }
+    }
+
     // IInventoryListener
 
     /**
@@ -373,4 +751,33 @@ public final class AdSalInventory implements IInventoryListener {
             notifyAll();
         }
     }
+
+    // AutoCloseable
+
+    /**
+     * Close this instance.
+     */
+    @Override
+    public void close() {
+        Timer timer = inventoryTimer.getAndSet(null);
+        if (timer != null) {
+            timer.cancel();
+        }
+
+        ServiceRegistration<?> reg = inventoryListener.getAndSet(null);
+        if (reg != null) {
+            try {
+                reg.unregister();
+            } catch (RuntimeException e) {
+                LOG.debug("Failed to unregister IInventoryListener.", e);
+            }
+        }
+
+        Set<DataStoreListener<?, ?>> mdSet = dataListeners.getAndSet(null);
+        if (mdSet != null) {
+            for (DataStoreListener<?, ?> dsl: mdSet) {
+                dsl.close();
+            }
+        }
+    }
 }
diff --git a/manager/it/ofmock/src/main/java/org/opendaylight/vtn/manager/it/ofmock/impl/MdNodeListener.java b/manager/it/ofmock/src/main/java/org/opendaylight/vtn/manager/it/ofmock/impl/MdNodeListener.java
new file mode 100644 (file)
index 0000000..3caebbe
--- /dev/null
@@ -0,0 +1,192 @@
+/*
+ * Copyright (c) 2015 NEC Corporation
+ * 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.vtn.manager.it.ofmock.impl;
+
+import java.util.Set;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+
+import org.opendaylight.controller.sal.core.UpdateType;
+
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.types.rev150209.VtnUpdateType;
+
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
+
+/**
+ * {@code MdNodeListener} listens changes to the MD-SAL node information.
+ */
+public final class MdNodeListener
+    extends DataStoreListener<FlowCapableNode, Void> {
+    /**
+     * Logger instance.
+     */
+    private static final Logger  LOG =
+        LoggerFactory.getLogger(MdNodeListener.class);
+
+    /**
+     * AD-SAL inventory manager.
+     */
+    private final AdSalInventory  adInventory;
+
+    /**
+     * Construct a new instance.
+     *
+     * @param broker  Data broker service.
+     * @param adsal   AD-SAL inventory manager.
+     */
+    public MdNodeListener(DataBroker broker, AdSalInventory adsal) {
+        super(FlowCapableNode.class);
+        adInventory = adsal;
+        registerListener(broker, LogicalDatastoreType.OPERATIONAL,
+                         DataChangeScope.SUBTREE);
+    }
+
+    /**
+     * Return a MD-SAL node identifier in the given instance identifier.
+     *
+     * @param path  An {@link InstanceIdentifier} instance.
+     * @return  A MD-SAL node identifier if found.
+     *          {@code null} if not found.
+     */
+    private String getNodeIdentifier(InstanceIdentifier<?> path) {
+        if (path == null) {
+            return null;
+        }
+
+        NodeKey key = path.firstKeyOf(Node.class, NodeKey.class);
+        if (key == null) {
+            return null;
+        }
+
+        NodeId nodeId = key.getId();
+        return (nodeId == null) ? null : nodeId.getValue();
+    }
+
+    /**
+     * Invoked when a MD-SAL node has been updated.
+     *
+     * @param path  Path to the flow-capable-node.
+     * @param fcn   A {@link FlowCapableNode} instance.
+     * @param type  An {@link UpdateType} instance which indicates the type of
+     *              the event.
+     */
+    private void nodeUpdated(InstanceIdentifier<FlowCapableNode> path,
+                             FlowCapableNode fcn, UpdateType type) {
+        if (fcn == null) {
+            return;
+        }
+
+        String nid = getNodeIdentifier(path);
+        if (nid != null) {
+            LOG.trace("{}: MD-SAL node has been updated: nid={}, fcn={}",
+                      type, nid, fcn);
+            adInventory.notifyNodeUpdated(nid, fcn, type);
+        }
+    }
+
+    /**
+     * Invoked when a MD-SAL node has been removed.
+     *
+     * @param path  Path to the flow-capable-node.
+     */
+    private void nodeRemoved(InstanceIdentifier<FlowCapableNode> path) {
+        String nid = getNodeIdentifier(path);
+        if (nid != null) {
+            LOG.trace("REMOVED: MD-SAL node has been removed: {}", nid);
+            adInventory.notifyNodeRemoved(nid);
+        }
+    }
+
+    // DataStoreListener
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected Void enterEvent(
+        AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> ev) {
+        return null;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected void exitEvent(Void ectx) {
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected void onCreated(Void ectx,
+                             InstanceIdentifier<FlowCapableNode> path,
+                             FlowCapableNode data) {
+        nodeUpdated(path, data, UpdateType.ADDED);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected void onUpdated(Void ectx,
+                             InstanceIdentifier<FlowCapableNode> path,
+                             FlowCapableNode oldData, FlowCapableNode newData) {
+        nodeUpdated(path, newData, UpdateType.CHANGED);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected void onRemoved(Void ectx,
+                             InstanceIdentifier<FlowCapableNode> path,
+                             FlowCapableNode data) {
+        nodeRemoved(path);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected InstanceIdentifier<FlowCapableNode> getWildcardPath() {
+        return InstanceIdentifier.builder(Nodes.class).child(Node.class).
+            augmentation(FlowCapableNode.class).build();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected Logger getLogger() {
+        return LOG;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected Set<VtnUpdateType> getRequiredEvents() {
+        return null;
+    }
+}
diff --git a/manager/it/ofmock/src/main/java/org/opendaylight/vtn/manager/it/ofmock/impl/MdPortListener.java b/manager/it/ofmock/src/main/java/org/opendaylight/vtn/manager/it/ofmock/impl/MdPortListener.java
new file mode 100644 (file)
index 0000000..29e4447
--- /dev/null
@@ -0,0 +1,200 @@
+/*
+ * Copyright (c) 2015 NEC Corporation
+ * 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.vtn.manager.it.ofmock.impl;
+
+import java.util.Set;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+
+import org.opendaylight.controller.sal.core.UpdateType;
+
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.types.rev150209.VtnUpdateType;
+
+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.node.NodeConnectorKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+
+/**
+ * {@code MdPortListener} listens changes to the MD-SAL node connector
+ * information.
+ */
+public final class MdPortListener
+    extends DataStoreListener<FlowCapableNodeConnector, Void> {
+    /**
+     * Logger instance.
+     */
+    private static final Logger  LOG =
+        LoggerFactory.getLogger(MdPortListener.class);
+
+    /**
+     * AD-SAL inventory manager.
+     */
+    private final AdSalInventory  adInventory;
+
+    /**
+     * Construct a new instance.
+     *
+     * @param broker  Data broker service.
+     * @param adsal   AD-SAL inventory manager.
+     */
+    public MdPortListener(DataBroker broker, AdSalInventory adsal) {
+        super(FlowCapableNodeConnector.class);
+        adInventory = adsal;
+        registerListener(broker, LogicalDatastoreType.OPERATIONAL,
+                         DataChangeScope.SUBTREE);
+    }
+
+    /**
+     * Return a MD-SAL node connector identifier in the given instance
+     * identifier.
+     *
+     * @param path  An {@link InstanceIdentifier} instance.
+     * @return  A MD-SAL node connector identifier if found.
+     *          {@code null} if not found.
+     */
+    private String getPortIdentifier(InstanceIdentifier<?> path) {
+        if (path == null) {
+            return null;
+        }
+
+        NodeConnectorKey key =
+            path.firstKeyOf(NodeConnector.class, NodeConnectorKey.class);
+        if (key == null) {
+            return null;
+        }
+
+        NodeConnectorId ncId = key.getId();
+        return (ncId == null) ? null : ncId.getValue();
+    }
+
+    /**
+     * Invoked when a MD-SAL node connector has been updated.
+     *
+     * @param path  Path to the flow-capable-node-connector.
+     * @param fcnc  A {@link FlowCapableNodeConnector} instance.
+     * @param type  An {@link UpdateType} instance which indicates the type of
+     *              the event.
+     */
+    private void portUpdated(InstanceIdentifier<FlowCapableNodeConnector> path,
+                             FlowCapableNodeConnector fcnc, UpdateType type) {
+        if (fcnc == null) {
+            return;
+        }
+
+        String pid = getPortIdentifier(path);
+        if (pid != null) {
+            LOG.trace("{}: MD-SAL node connector has been updated: pid={}, " +
+                      "fcnc={}", type, pid, fcnc);
+            adInventory.notifyPortUpdated(pid, fcnc, type);
+        }
+    }
+
+    /**
+     * Invoked when a MD-SAL node connector has been removed.
+     *
+     * @param path  Path to the flow-capable-node-capable.
+     */
+    private void portRemoved(
+        InstanceIdentifier<FlowCapableNodeConnector> path) {
+        String pid = getPortIdentifier(path);
+        if (pid != null) {
+            LOG.trace("REMOVED: MD-SAL node connector has been removed: {}",
+                      pid);
+            adInventory.notifyPortRemoved(pid);
+        }
+    }
+
+    // DataStoreListener
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected Void enterEvent(
+        AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> ev) {
+        return null;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected void exitEvent(Void ectx) {
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected void onCreated(Void ectx,
+                             InstanceIdentifier<FlowCapableNodeConnector> path,
+                             FlowCapableNodeConnector data) {
+        portUpdated(path, data, UpdateType.ADDED);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected void onUpdated(Void ectx,
+                             InstanceIdentifier<FlowCapableNodeConnector> path,
+                             FlowCapableNodeConnector oldData,
+                             FlowCapableNodeConnector newData) {
+        portUpdated(path, newData, UpdateType.CHANGED);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected void onRemoved(Void ectx,
+                             InstanceIdentifier<FlowCapableNodeConnector> path,
+                             FlowCapableNodeConnector data) {
+        portRemoved(path);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected InstanceIdentifier<FlowCapableNodeConnector> getWildcardPath() {
+        return InstanceIdentifier.builder(Nodes.class).child(Node.class).
+            child(NodeConnector.class).
+            augmentation(FlowCapableNodeConnector.class).build();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected Logger getLogger() {
+        return LOG;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected Set<VtnUpdateType> getRequiredEvents() {
+        return null;
+    }
+}
index a6961153ff46e3e0cc40ba582926d17413d77ed5..72d4d1d2fd1b0dab3bbe4c2317172335157d7410 100644 (file)
@@ -564,7 +564,7 @@ public class OfMockProvider implements AutoCloseable, Executor, OfMockService {
     public void initialize() throws InterruptedException {
         if (adSalInventory == null) {
             try {
-                adSalInventory = new AdSalInventory();
+                adSalInventory = new AdSalInventory(dataBroker);
             } catch (RuntimeException e) {
                 String msg = "Failed to initialize AD-SAL inventory: " +
                     e.getMessage();
index df2ccdcc0b788b23c581e8ebd6e7b75f6af80727..379ae785d5d8d07cde187051d013030c55f5b374 100644 (file)
@@ -10,7 +10,9 @@
 package org.opendaylight.vtn.manager.it.ofmock.impl;
 
 import java.util.Collection;
+import java.util.HashMap;
 import java.util.Iterator;
+import java.util.Map;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -40,6 +42,18 @@ public final class ServiceWaiter<T> implements ServiceListener {
      */
     static final String  PROP_CONTAINER = "containerName";
 
+    /**
+     * The name of the OSGi service property which specifies the scope of the
+     * service.
+     */
+    static final String  PROP_SCOPE = "scope";
+
+    /**
+     * The value of the OSGi service property which specifies the global scope.
+     * service.
+     */
+    static final String  SCOPE_GLOBAL = "Global";
+
     /**
      * An OSGi bundle context for this OSGi bundle.
      */
@@ -51,9 +65,9 @@ public final class ServiceWaiter<T> implements ServiceListener {
     private final Class<T>  serviceType;
 
     /**
-     * OSGi service filter for retrieving.
+     * A set of OSGi service filters for retrieving.
      */
-    private final String  serviceFilter;
+    private final Map<String, String>  serviceFilters = new HashMap<>();
 
     /**
      * OSGI service implementation to be returned.
@@ -83,22 +97,24 @@ public final class ServiceWaiter<T> implements ServiceListener {
     public ServiceWaiter(BundleContext bc, Class<T> type, String cname) {
         bundleContext = bc;
         serviceType = type;
+        setFilter(Constants.OBJECTCLASS, type.getName()).
+            setFilter(PROP_CONTAINER, cname);
+    }
 
-        // Construct a OSGi service filter.
-        String objFilter = new StringBuilder("(").
-            append(Constants.OBJECTCLASS).append('=').append(type.getName()).
-            append(')').toString();
-        if (cname == null) {
-            serviceFilter = objFilter;
+    /**
+     * Set OSGi service filters for retrieving.
+     *
+     * @param name   The name of the filter.
+     * @param value  The value to be associated with the given filter name.
+     * @return  This instance.
+     */
+    public ServiceWaiter<T> setFilter(String name, String value) {
+        if (value == null) {
+            serviceFilters.remove(name);
         } else {
-            StringBuilder builder = new StringBuilder("(&").
-                append(objFilter).append('(').append(PROP_CONTAINER).
-                append('=').append(cname).append("))");
-            serviceFilter = builder.toString();
+            serviceFilters.put(name, value);
         }
-
-        // Try to get service instance.
-        getService();
+        return this;
     }
 
     /**
@@ -109,24 +125,31 @@ public final class ServiceWaiter<T> implements ServiceListener {
      *    The calling thread was interrupted.
      */
     public T await() throws InterruptedException {
-        synchronized (this) {
-            if (serviceInstance != null) {
-                return serviceInstance;
-            }
+        // Construct a OSGi service filter.
+        StringBuilder builder = new StringBuilder("(&");
+        for (Map.Entry<String, String> entry: serviceFilters.entrySet()) {
+            builder.append('(').append(entry.getKey()).append('=').
+                append(entry.getValue()).append(')');
         }
 
+        String filter = builder.append(')').toString();
+        LOG.trace("Searching for {} service: filter={}",
+                  serviceType.getSimpleName(), filter);
+
         try {
-            bundleContext.addServiceListener(this, serviceFilter);
+            bundleContext.addServiceListener(this, filter);
         } catch (Exception e) {
             throw new IllegalStateException(
                 "Failed to add OSGi service listener.", e);
         }
 
         try {
-            // We need to check the service again in order to avoid race
-            // condition with service event.
-            T impl = getService();
+            // We need to check the service in order to avoid race condition
+            // with service event.
+            T impl = getService(filter);
             if (impl == null) {
+                LOG.trace("Waiting for {} service to be registered: filter={}",
+                          serviceType.getSimpleName(), filter);
                 impl = awaitImpl();
             }
 
@@ -144,9 +167,6 @@ public final class ServiceWaiter<T> implements ServiceListener {
      *    The calling thread was interrupted.
      */
     private synchronized T awaitImpl() throws InterruptedException {
-        LOG.trace("Waiting for {} service to be registered.",
-                  serviceType.getSimpleName());
-
         long timeout = OfMockProvider.TASK_TIMEOUT;
         long deadline = System.currentTimeMillis() + timeout;
         do {
@@ -165,10 +185,11 @@ public final class ServiceWaiter<T> implements ServiceListener {
     /**
      * Search for the target OSGi service implementation.
      *
+     * @param filter  An OSGi service filter.
      * @return  An implementation of the target OSGi service on success.
      *          {@code null} on failure.
      */
-    private T getService() {
+    private T getService(String filter) {
         synchronized (this) {
             if (serviceInstance != null) {
                 return serviceInstance;
@@ -177,7 +198,7 @@ public final class ServiceWaiter<T> implements ServiceListener {
 
         Collection<ServiceReference<T>> c;
         try {
-            c = bundleContext.getServiceReferences(serviceType, serviceFilter);
+            c = bundleContext.getServiceReferences(serviceType, filter);
         } catch (Exception e) {
             String msg = "Failed to get OSGi service reference: " +
                 serviceType.getSimpleName();
index 0dcf687dcea3ceec46c26d82b2b88da2f6a9c05f..b44c77e6be7c05ba0110360be59394763035698c 100644 (file)
@@ -94,7 +94,7 @@ public final class VtnNodeListener extends DataStoreListener<VtnNode, Void> {
     public VtnNodeListener(DataBroker broker) {
         super(VtnNode.class);
         registerListener(broker, LogicalDatastoreType.OPERATIONAL,
-                         DataChangeScope.BASE);
+                         DataChangeScope.ONE);
     }
 
     /**
index e701af2b50ddb5510fb553664bf9e96481b6d529..baa92f8e2722741e3934cf9e8b7845fe86938d11 100644 (file)
@@ -128,7 +128,7 @@ public final class VtnPortListener extends DataStoreListener<VtnPort, Void> {
     public VtnPortListener(DataBroker broker) {
         super(VtnPort.class);
         registerListener(broker, LogicalDatastoreType.OPERATIONAL,
-                         DataChangeScope.BASE);
+                         DataChangeScope.SUBTREE);
     }
 
     /**
index 68c78113b6fb0b96e4ebe3504a4e8d27f8ca3571..ad0d80e8796cf4621ad755d8ef38f5fc36e24333 100644 (file)
@@ -40,9 +40,7 @@
     <module>option</module>
     <module>util</module>
     <module>common</module>
-    <!--
     <module>core</module>
     <module>northbound</module>
-    -->
   </modules>
 </project>