ONOS-2184 - Implementation of virtual network topology provider.

Change-Id: I846ba56c138187c6e5435692798e709b74a78020
diff --git a/core/api/src/test/java/org/onosproject/net/TestDeviceParams.java b/core/api/src/test/java/org/onosproject/net/TestDeviceParams.java
index 8116f05..2de364a 100644
--- a/core/api/src/test/java/org/onosproject/net/TestDeviceParams.java
+++ b/core/api/src/test/java/org/onosproject/net/TestDeviceParams.java
@@ -37,6 +37,7 @@
     protected static final DeviceId DID2 = deviceId("of:bar");
     protected static final DeviceId DID3 = deviceId("of:who");
     protected static final DeviceId DID4 = deviceId("of:what");
+    protected static final DeviceId DID5 = deviceId("of:when");
     protected static final MacAddress MAC1 = MacAddress.valueOf("00:11:00:00:00:01");
     protected static final MacAddress MAC2 = MacAddress.valueOf("00:22:00:00:00:02");
     protected static final VlanId VLAN1 = VlanId.vlanId((short) 11);
diff --git a/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/VirtualNetworkIntentCompiler.java b/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/VirtualNetworkIntentCompiler.java
index 04f42e5..1fdebe1 100644
--- a/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/VirtualNetworkIntentCompiler.java
+++ b/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/VirtualNetworkIntentCompiler.java
@@ -31,15 +31,12 @@
 import org.onosproject.incubator.net.virtual.VirtualNetworkStore;
 import org.onosproject.incubator.net.virtual.VirtualPort;
 import org.onosproject.net.ConnectPoint;
-import org.onosproject.net.EncapsulationType;
 import org.onosproject.net.Link;
 import org.onosproject.net.Path;
-import org.onosproject.net.intent.Constraint;
 import org.onosproject.net.intent.Intent;
 import org.onosproject.net.intent.IntentService;
 import org.onosproject.net.intent.Key;
 import org.onosproject.net.intent.PointToPointIntent;
-import org.onosproject.net.intent.constraint.EncapsulationConstraint;
 import org.onosproject.net.intent.impl.IntentCompilationException;
 import org.onosproject.net.topology.TopologyService;
 import org.slf4j.Logger;
@@ -93,12 +90,32 @@
         Optional<Path> path = getPaths(intent).stream()
                 .findFirst();
         if (path != null && path.isPresent()) {
-            path.get().links().forEach(link -> {
-                Intent physicalIntent = createPtPtIntent(intent, link);
-                intents.add(physicalIntent);
+            List<Link> links = path.get().links();
 
-                // store the virtual intent to physical intent tunnelId mapping
-                store.addTunnelId(intent, TunnelId.valueOf(physicalIntent.key().toString()));
+            // First create an intent between the intent ingress CP and the first link source CP,
+            // only if the two CPs are not the same.
+            Link firstLink = links.get(0);
+            if (!intent.ingressPoint().equals(firstLink.src())) {
+                intents.add(createPtPtIntent(intent, intent.ingressPoint(), firstLink.src()));
+            }
+
+            // Next create an intent between the intent egress CP and the last link destination CP,
+            // only if the two CPs are not the same.
+            Link lastLink = links.get(links.size() - 1);
+            if (!intent.egressPoint().equals(lastLink.dst())) {
+                intents.add(createPtPtIntent(intent, lastLink.dst(), intent.egressPoint()));
+            }
+
+            // Now loop through all of the virtual links in the path and create an intent.
+            // An intent is also created connecting two virtual links.
+            final int[] index = {0};
+            links.forEach(link -> {
+                intents.add(createPtPtIntent(intent, link.src(), link.dst()));
+                if (index[0] > 0) {
+                    Link previousLink = links.get(index[0] - 1);
+                    intents.add(createPtPtIntent(intent, previousLink.dst(), link.src()));
+                }
+                index[0]++;
             });
         } else {
             throw new IntentCompilationException("Unable to find a path for intent " + intent);
@@ -124,7 +141,7 @@
     }
 
     /**
-     * Encodes the key using the network identifier, application identifer, source and destination
+     * Encodes the key using the network identifier, application identifier, source and destination
      * connect points.
      *
      * @param networkId     virtual network identifier
@@ -133,26 +150,26 @@
      * @param dst           destination connect point
      * @return encoded key
      */
+
     private static Key encodeKey(NetworkId networkId, ApplicationId applicationId, ConnectPoint src, ConnectPoint dst) {
         String key = String.format(KEY_FORMAT, networkId, src, dst);
         return Key.of(key, applicationId);
     }
 
     /**
-     * Creates a point-to-point intent from the virtual network intent and virtual link.
+     * Creates a point-to-point intent using the virtual network intent between the source and destination
+     * connect point.
      *
      * @param intent virtual network intent
-     * @param link   virtual link
+     * @param src    source connect point
+     * @param dst    destination connect point
      * @return point to point intent
      */
-    private Intent createPtPtIntent(VirtualNetworkIntent intent, Link link) {
-        ConnectPoint ingressPoint = mapVirtualToPhysicalPort(intent.networkId(), link.src());
-        ConnectPoint egressPoint = mapVirtualToPhysicalPort(intent.networkId(), link.dst());
+    private Intent createPtPtIntent(VirtualNetworkIntent intent, ConnectPoint src, ConnectPoint dst) {
+        ConnectPoint ingressPoint = mapVirtualToPhysicalPort(intent.networkId(), src);
+        ConnectPoint egressPoint = mapVirtualToPhysicalPort(intent.networkId(), dst);
         Key intentKey = encodeKey(intent.networkId(), intent.appId(), ingressPoint, egressPoint);
 
-        List<Constraint> constraints = new ArrayList<>();
-        constraints.add(new EncapsulationConstraint(EncapsulationType.VLAN));
-
         // TODO Currently there can only be one intent between the ingress and egress across
         // all virtual networks. We may want to support multiple intents between the same src/dst pairs.
         PointToPointIntent physicalIntent = PointToPointIntent.builder()
@@ -160,11 +177,16 @@
                 .appId(intent.appId())
                 .ingressPoint(ingressPoint)
                 .egressPoint(egressPoint)
-                .constraints(constraints)
+                .constraints(intent.constraints())
+                .selector(intent.selector())
+                .treatment(intent.treatment())
                 .build();
         log.debug("Submitting physical intent: " + physicalIntent);
         intentService.submit(physicalIntent);
 
+        // Store the physical intent against this virtual intent.
+        store.addTunnelId(intent, TunnelId.valueOf(physicalIntent.key().toString()));
+
         return physicalIntent;
     }
 
diff --git a/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/VirtualNetworkIntentCompilerTest.java b/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/VirtualNetworkIntentCompilerTest.java
index 137d2e0..8b3387a 100644
--- a/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/VirtualNetworkIntentCompilerTest.java
+++ b/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/VirtualNetworkIntentCompilerTest.java
@@ -39,13 +39,11 @@
 import org.onosproject.incubator.store.virtual.impl.DistributedVirtualNetworkStore;
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.DefaultPort;
-import org.onosproject.net.EncapsulationType;
 import org.onosproject.net.Link;
 import org.onosproject.net.NetTestTools;
 import org.onosproject.net.Port;
 import org.onosproject.net.PortNumber;
 import org.onosproject.net.TestDeviceParams;
-import org.onosproject.net.intent.Constraint;
 import org.onosproject.net.intent.FakeIntentManager;
 import org.onosproject.net.intent.Intent;
 import org.onosproject.net.intent.IntentExtensionService;
@@ -53,10 +51,8 @@
 import org.onosproject.net.intent.Key;
 import org.onosproject.net.intent.MockIdGenerator;
 import org.onosproject.net.intent.TestableIntentService;
-import org.onosproject.net.intent.constraint.EncapsulationConstraint;
 import org.onosproject.store.service.TestStorageService;
 
-import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 import java.util.concurrent.atomic.AtomicLong;
@@ -192,6 +188,9 @@
         return virtualNetwork;
     }
 
+    /**
+     * Tests the virtual network intent compiler.
+     */
     @Test
     public void testCompiler() {
         compiler.activate();
@@ -199,20 +198,16 @@
 
         Key intentKey = Key.of("test", APP_ID);
 
-        List<Constraint> constraints = new ArrayList<>();
-        constraints.add(new EncapsulationConstraint(EncapsulationType.VLAN));
-
         VirtualNetworkIntent virtualIntent = VirtualNetworkIntent.builder()
                 .networkId(virtualNetwork.id())
                 .key(intentKey)
                 .appId(APP_ID)
-                .ingressPoint(cp1)
-                .egressPoint(cp5)
-                .constraints(constraints)
+                .ingressPoint(cp2)
+                .egressPoint(cp6)
                 .build();
 
         List<Intent> compiled = compiler.compile(virtualIntent, Collections.emptyList());
-        assertEquals("The virtual intents size is not as expected.", 2, compiled.size());
+        assertEquals("The virtual intents size is not as expected.", 5, compiled.size());
 
         compiler.deactivate();
     }
diff --git a/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkProvider.java b/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkProvider.java
index 1ec0536..104c9d3 100644
--- a/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkProvider.java
+++ b/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkProvider.java
@@ -26,6 +26,18 @@
 public interface VirtualNetworkProvider extends Provider {
 
     /**
+     * Indicates whether or not the specified connect points on the underlying
+     * network are traversable/reachable.
+     *
+     * @param src source connection point
+     * @param dst destination connection point
+     * @return true if the destination is reachable from the source
+     */
+    boolean isTraversable(ConnectPoint src, ConnectPoint dst);
+
+    // TODO: Further enhance this interface to support the virtual intent programming across this boundary.
+
+    /**
      * Creates a network tunnel for all traffic from the specified source
      * connection point to the indicated destination connection point.
      *
diff --git a/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkProviderService.java b/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkProviderService.java
index 5d63c7d..50f0fc3 100644
--- a/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkProviderService.java
+++ b/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkProviderService.java
@@ -19,6 +19,8 @@
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.provider.ProviderService;
 
+import java.util.Set;
+
 /**
  * Service through which virtual network providers can inject information into
  * the core.
@@ -26,6 +28,18 @@
 public interface VirtualNetworkProviderService extends ProviderService<VirtualNetworkProvider> {
 
     /**
+     * Set of separate topology clusters expressed in terms of connect points which
+     * belong to the same SCC of the underlying topology.
+     *
+     * @param clusters set of sets of mutually reachable connection points;
+     *                 the outer sets are not mutually reachable
+     */
+    void topologyChanged(Set<Set<ConnectPoint>> clusters);
+
+    // TBD: Is the above sufficient to determine health/viability of virtual entities based on
+    // clustering (SCC) of the physical ones?
+
+    /**
      * This method is used to notify the VirtualNetwork service that a tunnel is now ACTIVE.
      *
      * @param networkId network identifier
diff --git a/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/PtToPtIntentVirtualNetworkProvider.java b/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/PtToPtIntentVirtualNetworkProvider.java
index 1b8a69e..d5b049c 100644
--- a/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/PtToPtIntentVirtualNetworkProvider.java
+++ b/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/PtToPtIntentVirtualNetworkProvider.java
@@ -108,6 +108,11 @@
     }
 
     @Override
+    public boolean isTraversable(ConnectPoint src, ConnectPoint dst) {
+        return false;
+    }
+
+    @Override
     public TunnelId createTunnel(NetworkId networkId, ConnectPoint src, ConnectPoint dst) {
         checkNotNull(networkId, NETWORK_ID_NULL);
         checkNotNull(src, CONNECT_POINT_NULL);
diff --git a/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkIntentService.java b/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkIntentService.java
index 6c0b34a..753b2e3 100644
--- a/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkIntentService.java
+++ b/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkIntentService.java
@@ -46,7 +46,7 @@
 import static com.google.common.base.Preconditions.*;
 
 /**
- * intent service implementation built on the virtual network service.
+ * Intent service implementation built on the virtual network service.
  */
 public class VirtualNetworkIntentService extends AbstractListenerManager<IntentEvent, IntentListener>
         implements IntentService, VnetService {
@@ -148,18 +148,17 @@
     public void withdraw(Intent intent) {
         checkNotNull(intent, INTENT_NULL);
         // Withdraws the physical intents created due to the virtual intents.
-        store.getTunnelIds(intent)
-                .forEach(tunnelId -> {
-                    Key intentKey = Key.of(tunnelId.id(), intent.appId());
-                    Intent physicalIntent = intentService.getIntent(intentKey);
-                    checkNotNull(physicalIntent, INTENT_NULL);
+        store.getTunnelIds(intent).forEach(tunnelId -> {
+            Key intentKey = Key.of(tunnelId.id(), intent.appId());
+            Intent physicalIntent = intentService.getIntent(intentKey);
+            checkNotNull(physicalIntent, INTENT_NULL);
 
-                    // Withdraw the physical intent(s)
-                    log.info("Withdrawing pt-pt intent: " + physicalIntent);
-                    intentService.withdraw(physicalIntent);
-                });
+            // Withdraw the physical intent(s)
+            log.debug("Withdrawing pt-pt intent: " + physicalIntent);
+            intentService.withdraw(physicalIntent);
+        });
         // Now withdraw the virtual intent
-        log.info("Withdrawing virtual intent: " + intent);
+        log.debug("Withdrawing virtual intent: " + intent);
         intentService.withdraw(intent);
     }
 
diff --git a/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkManager.java b/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkManager.java
index 71d73f9..5330833 100644
--- a/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkManager.java
+++ b/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkManager.java
@@ -27,6 +27,7 @@
 import org.onlab.packet.MacAddress;
 import org.onlab.packet.VlanId;
 import org.onosproject.incubator.net.tunnel.TunnelId;
+import org.onosproject.incubator.net.virtual.DefaultVirtualLink;
 import org.onosproject.incubator.net.virtual.NetworkId;
 import org.onosproject.incubator.net.virtual.TenantId;
 import org.onosproject.incubator.net.virtual.VirtualDevice;
@@ -76,7 +77,7 @@
 @Service
 public class VirtualNetworkManager
         extends AbstractListenerProviderRegistry<VirtualNetworkEvent, VirtualNetworkListener,
-                                                 VirtualNetworkProvider, VirtualNetworkProviderService>
+        VirtualNetworkProvider, VirtualNetworkProviderService>
         implements VirtualNetworkService, VirtualNetworkAdminService, VirtualNetworkProviderRegistry {
 
     private final Logger log = LoggerFactory.getLogger(getClass());
@@ -197,7 +198,36 @@
         checkNotNull(networkId, NETWORK_NULL);
         checkNotNull(src, LINK_POINT_NULL);
         checkNotNull(dst, LINK_POINT_NULL);
-        return store.addLink(networkId, src, dst, Link.State.ACTIVE, null);
+        ConnectPoint physicalSrc = mapVirtualToPhysicalPort(networkId, src);
+        checkNotNull(physicalSrc, LINK_POINT_NULL);
+        ConnectPoint physicalDst = mapVirtualToPhysicalPort(networkId, dst);
+        checkNotNull(physicalDst, LINK_POINT_NULL);
+
+        VirtualNetworkProvider provider = getProvider(DefaultVirtualLink.PID);
+        Link.State state = Link.State.INACTIVE;
+        if (provider != null) {
+            boolean traversable = provider.isTraversable(physicalSrc, physicalDst);
+            state = traversable ? Link.State.ACTIVE : Link.State.INACTIVE;
+        }
+        return store.addLink(networkId, src, dst, state, null);
+    }
+
+    /**
+     * Maps the virtual connect point to a physical connect point.
+     *
+     * @param networkId network identifier
+     * @param virtualCp virtual connect point
+     * @return physical connect point
+     */
+    private ConnectPoint mapVirtualToPhysicalPort(NetworkId networkId,
+                                                  ConnectPoint virtualCp) {
+        Set<VirtualPort> ports = store.getPorts(networkId, virtualCp.deviceId());
+        for (VirtualPort port : ports) {
+            if (port.number().equals(virtualCp.port())) {
+                return new ConnectPoint(port.realizedBy().element().id(), port.realizedBy().number());
+            }
+        }
+        return null;
     }
 
     /**
@@ -441,16 +471,73 @@
         return new InternalVirtualNetworkProviderService(provider);
     }
 
-    // Service issued to registered virtual network providers so that they
-    // can interact with the core.
+    /**
+     * Service issued to registered virtual network providers so that they
+     * can interact with the core.
+     */
     private class InternalVirtualNetworkProviderService
             extends AbstractProviderService<VirtualNetworkProvider>
             implements VirtualNetworkProviderService {
+        /**
+         * Constructor.
+         * @param provider virtual network provider
+         */
         InternalVirtualNetworkProviderService(VirtualNetworkProvider provider) {
             super(provider);
         }
 
         @Override
+        public void topologyChanged(Set<Set<ConnectPoint>> clusters) {
+
+            Set<TenantId> tenantIds = getTenantIds();
+            tenantIds.forEach(tenantId -> {
+                Set<VirtualNetwork> virtualNetworks = getVirtualNetworks(tenantId);
+
+                virtualNetworks.forEach(virtualNetwork -> {
+                    Set<VirtualLink> virtualLinks = getVirtualLinks(virtualNetwork.id());
+
+                    virtualLinks.forEach(virtualLink -> {
+                        if (isVirtualLinkInCluster(virtualNetwork.id(), virtualLink, clusters)) {
+                            store.updateLink(virtualLink, virtualLink.tunnelId(), Link.State.ACTIVE);
+                        } else {
+                            store.updateLink(virtualLink, virtualLink.tunnelId(), Link.State.INACTIVE);
+                        }
+                    });
+                });
+            });
+        }
+
+        /**
+         * Determines if the virtual link (both source and destination connect point) is in a cluster.
+         *
+         * @param networkId   virtual network identifier
+         * @param virtualLink virtual link
+         * @param clusters    topology clusters
+         * @return true if the virtual link is in a cluster.
+         */
+        private boolean isVirtualLinkInCluster(NetworkId networkId, VirtualLink virtualLink,
+                                               Set<Set<ConnectPoint>> clusters) {
+            ConnectPoint srcPhysicalCp = mapVirtualToPhysicalPort(networkId, virtualLink.src());
+            ConnectPoint dstPhysicalCp = mapVirtualToPhysicalPort(networkId, virtualLink.dst());
+
+            final boolean[] foundSrc = {false};
+            final boolean[] foundDst = {false};
+            clusters.forEach(connectPoints -> {
+                connectPoints.forEach(connectPoint -> {
+                    if (connectPoint.equals(srcPhysicalCp)) {
+                        foundSrc[0] = true;
+                    } else if (connectPoint.equals(dstPhysicalCp)) {
+                        foundDst[0] = true;
+                    }
+                });
+                if (foundSrc[0] && foundDst[0]) {
+                    return;
+                }
+            });
+            return foundSrc[0] && foundDst[0];
+        }
+
+        @Override
         public void tunnelUp(NetworkId networkId, ConnectPoint src, ConnectPoint dst, TunnelId tunnelId) {
 
             ConnectPoint srcVirtualCp = mapPhysicalToVirtualToPort(networkId, src);
diff --git a/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkTopologyProvider.java b/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkTopologyProvider.java
new file mode 100644
index 0000000..efbaf91
--- /dev/null
+++ b/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkTopologyProvider.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright 2016-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.incubator.net.virtual.impl;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.Service;
+import org.onosproject.incubator.net.tunnel.TunnelId;
+import org.onosproject.incubator.net.virtual.DefaultVirtualLink;
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.incubator.net.virtual.VirtualNetworkProvider;
+import org.onosproject.incubator.net.virtual.VirtualNetworkProviderRegistry;
+import org.onosproject.incubator.net.virtual.VirtualNetworkProviderService;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.Link;
+import org.onosproject.net.Path;
+import org.onosproject.net.link.LinkEvent;
+import org.onosproject.net.provider.AbstractProvider;
+import org.onosproject.net.topology.Topology;
+import org.onosproject.net.topology.TopologyCluster;
+import org.onosproject.net.topology.TopologyEvent;
+import org.onosproject.net.topology.TopologyListener;
+import org.onosproject.net.topology.TopologyService;
+import org.slf4j.Logger;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Virtual network topology provider.
+ */
+@Component(immediate = true)
+@Service
+public class VirtualNetworkTopologyProvider extends AbstractProvider implements VirtualNetworkProvider {
+
+    private final Logger log = getLogger(VirtualNetworkTopologyProvider.class);
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected VirtualNetworkProviderRegistry providerRegistry;
+
+    private VirtualNetworkProviderService providerService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected TopologyService topologyService;
+
+    protected TopologyListener topologyListener = new InternalTopologyListener();
+
+    /**
+     * Default constructor.
+     */
+    public VirtualNetworkTopologyProvider() {
+        super(DefaultVirtualLink.PID);
+    }
+
+    @Activate
+    public void activate() {
+        providerService = providerRegistry.register(this);
+        topologyService.addListener(topologyListener);
+
+        log.info("Started");
+    }
+
+    @Deactivate
+    public void deactivate() {
+        topologyService.removeListener(topologyListener);
+        providerRegistry.unregister(this);
+        providerService = null;
+        log.info("Stopped");
+    }
+
+    @Override
+    public boolean isTraversable(ConnectPoint src, ConnectPoint dst) {
+        final boolean[] foundSrc = new boolean[1];
+        final boolean[] foundDst = new boolean[1];
+        Topology topology = topologyService.currentTopology();
+        Set<Path> paths = topologyService.getPaths(topology, src.deviceId(), dst.deviceId());
+        paths.forEach(path -> {
+            foundDst[0] = false;
+            foundSrc[0] = false;
+            // Traverse the links in each path to determine if both the src and dst connection
+            // point are in the path, if so then this src/dst pair are traversable.
+            path.links().forEach(link -> {
+                if (link.src().equals(src)) {
+                    foundSrc[0] = true;
+                }
+                if (link.dst().equals(dst)) {
+                    foundDst[0] = true;
+                }
+            });
+            if (foundSrc[0] && foundDst[0]) {
+                return;
+            }
+        });
+        return foundSrc[0] && foundDst[0];
+    }
+
+    @Override
+    public TunnelId createTunnel(NetworkId networkId, ConnectPoint src, ConnectPoint dst) {
+        return null;
+    }
+
+    @Override
+    public void destroyTunnel(NetworkId networkId, TunnelId tunnelId) {
+
+    }
+
+    /**
+     * Returns a set of set of interconnected connect points in the default topology.
+     * The inner set represents the interconnected connect points, and the outerset
+     * represents separate clusters.
+     *
+     * @param topology the default topology
+     * @return set of set of interconnected connect points.
+     */
+    protected Set<Set<ConnectPoint>> getConnectPoints(Topology topology) {
+        Set<Set<ConnectPoint>> clusters = new HashSet<>();
+        Set<TopologyCluster> topologyClusters = topologyService.getClusters(topology);
+        topologyClusters.forEach(topologyCluster -> {
+            Set<ConnectPoint> connectPointSet = new HashSet<>();
+            Set<Link> clusterLinks = topologyService.getClusterLinks(topology, topologyCluster);
+            clusterLinks.forEach(link -> {
+                connectPointSet.add(link.src());
+                connectPointSet.add(link.dst());
+            });
+            if (!connectPointSet.isEmpty()) {
+                clusters.add(connectPointSet);
+            }
+        });
+        return clusters;
+    }
+
+    /**
+     * Topology event listener.
+     */
+    private class InternalTopologyListener implements TopologyListener {
+        @Override
+        public void event(TopologyEvent event) {
+            if (!isRelevant(event)) {
+                return;
+            }
+
+            Topology topology = event.subject();
+            providerService.topologyChanged(getConnectPoints(topology));
+        }
+
+        @Override
+        public boolean isRelevant(TopologyEvent event) {
+            final boolean[] relevant = {false};
+            if (event.type().equals(TopologyEvent.Type.TOPOLOGY_CHANGED)) {
+                event.reasons().forEach(event1 -> {
+                    // Only LinkEvents are relevant events, ie: DeviceEvents and others are ignored.
+                    if (event1 instanceof LinkEvent) {
+                        relevant[0] = true;
+                        return;
+                    }
+                });
+            }
+            return relevant[0];
+        }
+    }
+}
diff --git a/incubator/net/src/test/java/org/onosproject/incubator/net/virtual/impl/PtToPtIntentVirtualNetworkProviderTest.java b/incubator/net/src/test/java/org/onosproject/incubator/net/virtual/impl/PtToPtIntentVirtualNetworkProviderTest.java
index 15dcaf8..dd965f8 100644
--- a/incubator/net/src/test/java/org/onosproject/incubator/net/virtual/impl/PtToPtIntentVirtualNetworkProviderTest.java
+++ b/incubator/net/src/test/java/org/onosproject/incubator/net/virtual/impl/PtToPtIntentVirtualNetworkProviderTest.java
@@ -220,6 +220,11 @@
         }
 
         @Override
+        public void topologyChanged(Set<Set<ConnectPoint>> clusters) {
+
+        }
+
+        @Override
         public void tunnelUp(NetworkId networkId, ConnectPoint src, ConnectPoint dst, TunnelId tunnelId) {
             // Release one permit on the created semaphore since the tunnelUp method was called.
             created.release();
diff --git a/incubator/net/src/test/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkLinkServiceTest.java b/incubator/net/src/test/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkLinkServiceTest.java
index ee5a5dd..82c7044 100644
--- a/incubator/net/src/test/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkLinkServiceTest.java
+++ b/incubator/net/src/test/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkLinkServiceTest.java
@@ -31,6 +31,7 @@
 import org.onosproject.incubator.net.virtual.VirtualNetwork;
 import org.onosproject.incubator.store.virtual.impl.DistributedVirtualNetworkStore;
 import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DefaultPort;
 import org.onosproject.net.Link;
 import org.onosproject.net.NetTestTools;
 import org.onosproject.net.PortNumber;
@@ -97,7 +98,13 @@
         VirtualDevice dstVirtualDevice =
                 manager.createVirtualDevice(virtualNetwork.id(), DID2);
         ConnectPoint src = new ConnectPoint(srcVirtualDevice.id(), PortNumber.portNumber(1));
+        manager.createVirtualPort(virtualNetwork.id(), src.deviceId(), src.port(),
+                                  new DefaultPort(srcVirtualDevice, src.port(), true));
+
         ConnectPoint dst = new ConnectPoint(dstVirtualDevice.id(), PortNumber.portNumber(2));
+        manager.createVirtualPort(virtualNetwork.id(), dst.deviceId(), dst.port(),
+                                  new DefaultPort(dstVirtualDevice, dst.port(), true));
+
         VirtualLink link1 = manager.createVirtualLink(virtualNetwork.id(), src, dst);
         VirtualLink link2 = manager.createVirtualLink(virtualNetwork.id(), dst, src);
 
@@ -107,12 +114,12 @@
         Iterator<Link> it = linkService.getLinks().iterator();
         assertEquals("The link set size did not match.", 2, Iterators.size(it));
 
-        // test the getActiveLinks() method where all links are ACTIVE
+        // test the getActiveLinks() method where all links are INACTIVE
         Iterator<Link> it2 = linkService.getActiveLinks().iterator();
-        assertEquals("The link set size did not match.", 2, Iterators.size(it2));
+        assertEquals("The link set size did not match.", 0, Iterators.size(it2));
 
         // test the getActiveLinks() method where one link is ACTIVE
-        virtualNetworkManagerStore.updateLink(link1, link1.tunnelId(), Link.State.INACTIVE);
+        virtualNetworkManagerStore.updateLink(link1, link1.tunnelId(), Link.State.ACTIVE);
         Iterator<Link> it3 = linkService.getActiveLinks().iterator();
         assertEquals("The link set size did not match.", 1, Iterators.size(it3));
 
diff --git a/incubator/net/src/test/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkManagerTest.java b/incubator/net/src/test/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkManagerTest.java
index 2834881..644ba0c 100644
--- a/incubator/net/src/test/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkManagerTest.java
+++ b/incubator/net/src/test/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkManagerTest.java
@@ -38,12 +38,13 @@
 import org.onosproject.incubator.net.virtual.VirtualNetworkEvent;
 import org.onosproject.incubator.net.virtual.VirtualNetworkIntent;
 import org.onosproject.incubator.net.virtual.VirtualNetworkListener;
-import org.onosproject.incubator.net.virtual.VirtualNetworkService;
+import org.onosproject.incubator.net.virtual.VirtualNetworkProviderService;
 import org.onosproject.incubator.net.virtual.VirtualPort;
 import org.onosproject.incubator.store.virtual.impl.DistributedVirtualNetworkStore;
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.DefaultPort;
 import org.onosproject.net.DeviceId;
+import org.onosproject.net.Link;
 import org.onosproject.net.NetTestTools;
 import org.onosproject.net.Port;
 import org.onosproject.net.PortNumber;
@@ -54,8 +55,11 @@
 import org.onosproject.net.intent.Key;
 import org.onosproject.net.intent.MockIdGenerator;
 import org.onosproject.net.intent.TestableIntentService;
+import org.onosproject.net.topology.Topology;
+import org.onosproject.net.topology.TopologyService;
 import org.onosproject.store.service.TestStorageService;
 
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
 import java.util.Set;
@@ -72,13 +76,17 @@
     private final String tenantIdValue2 = "TENANT_ID2";
 
     private VirtualNetworkManager manager;
-    private VirtualNetworkService virtualNetworkManagerService;
+    private VirtualNetworkTopologyProvider topologyProvider;
     private DistributedVirtualNetworkStore virtualNetworkManagerStore;
     private CoreService coreService;
     private TestListener listener = new TestListener();
     private TestableIntentService intentService = new FakeIntentManager();
+    private TopologyService topologyService;
     private IdGenerator idGenerator = new MockIdGenerator();
 
+    private ConnectPoint cp6;
+    private ConnectPoint cp7;
+
     @Before
     public void setUp() throws Exception {
         virtualNetworkManagerStore = new DistributedVirtualNetworkStore();
@@ -96,7 +104,6 @@
         manager.intentService = intentService;
         NetTestTools.injectEventDispatcher(manager, new TestEventDispatcher());
         manager.activate();
-        virtualNetworkManagerService = manager;
     }
 
     @After
@@ -313,7 +320,13 @@
         VirtualDevice dstVirtualDevice =
                 manager.createVirtualDevice(virtualNetwork1.id(), DID2);
         ConnectPoint src = new ConnectPoint(srcVirtualDevice.id(), PortNumber.portNumber(1));
+        manager.createVirtualPort(virtualNetwork1.id(), src.deviceId(), src.port(),
+                                  new DefaultPort(srcVirtualDevice, src.port(), true));
+
         ConnectPoint dst = new ConnectPoint(dstVirtualDevice.id(), PortNumber.portNumber(2));
+        manager.createVirtualPort(virtualNetwork1.id(), dst.deviceId(), dst.port(),
+                                  new DefaultPort(dstVirtualDevice, dst.port(), true));
+
         manager.createVirtualLink(virtualNetwork1.id(), src, dst);
         manager.createVirtualLink(virtualNetwork1.id(), dst, src);
 
@@ -348,7 +361,13 @@
         VirtualDevice dstVirtualDevice =
                 manager.createVirtualDevice(virtualNetwork1.id(), DID2);
         ConnectPoint src = new ConnectPoint(srcVirtualDevice.id(), PortNumber.portNumber(1));
+        manager.createVirtualPort(virtualNetwork1.id(), src.deviceId(), src.port(),
+                                  new DefaultPort(srcVirtualDevice, src.port(), true));
+
         ConnectPoint dst = new ConnectPoint(dstVirtualDevice.id(), PortNumber.portNumber(2));
+        manager.createVirtualPort(virtualNetwork1.id(), dst.deviceId(), dst.port(),
+                                  new DefaultPort(dstVirtualDevice, dst.port(), true));
+
         manager.createVirtualLink(virtualNetwork1.id(), src, dst);
         manager.createVirtualLink(virtualNetwork1.id(), src, dst);
     }
@@ -479,6 +498,126 @@
         assertTrue("The tunnels should be empty.", manager.store.getTunnelIds(virtualIntent).isEmpty());
     }
 
+
+    /**
+     * Method to create the virtual network for further testing.
+     **/
+    private VirtualNetwork setupVirtualNetworkTopology() {
+        manager.registerTenantId(TenantId.tenantId(tenantIdValue1));
+        VirtualNetwork virtualNetwork = manager.createVirtualNetwork(TenantId.tenantId(tenantIdValue1));
+
+        VirtualDevice virtualDevice1 =
+                manager.createVirtualDevice(virtualNetwork.id(), DID1);
+        VirtualDevice virtualDevice2 =
+                manager.createVirtualDevice(virtualNetwork.id(), DID2);
+        VirtualDevice virtualDevice3 =
+                manager.createVirtualDevice(virtualNetwork.id(), DID3);
+        VirtualDevice virtualDevice4 =
+                manager.createVirtualDevice(virtualNetwork.id(), DID4);
+        VirtualDevice virtualDevice5 =
+                manager.createVirtualDevice(virtualNetwork.id(), DID5);
+
+        ConnectPoint cp1 = new ConnectPoint(virtualDevice1.id(), PortNumber.portNumber(1));
+        manager.createVirtualPort(virtualNetwork.id(), virtualDevice1.id(),
+                                  PortNumber.portNumber(1),
+                                  new DefaultPort(virtualDevice1, PortNumber.portNumber(1), true));
+
+        ConnectPoint cp2 = new ConnectPoint(virtualDevice1.id(), PortNumber.portNumber(2));
+        manager.createVirtualPort(virtualNetwork.id(), virtualDevice1.id(),
+                                  PortNumber.portNumber(2),
+                                  new DefaultPort(virtualDevice1, PortNumber.portNumber(2), true));
+
+        ConnectPoint cp3 = new ConnectPoint(virtualDevice2.id(), PortNumber.portNumber(3));
+        manager.createVirtualPort(virtualNetwork.id(), virtualDevice2.id(),
+                                  PortNumber.portNumber(3),
+                                  new DefaultPort(virtualDevice2, PortNumber.portNumber(3), true));
+
+        ConnectPoint cp4 = new ConnectPoint(virtualDevice2.id(), PortNumber.portNumber(4));
+        manager.createVirtualPort(virtualNetwork.id(), virtualDevice2.id(),
+                                  PortNumber.portNumber(4),
+                                  new DefaultPort(virtualDevice2, PortNumber.portNumber(4), true));
+
+        ConnectPoint cp5 = new ConnectPoint(virtualDevice3.id(), PortNumber.portNumber(5));
+        manager.createVirtualPort(virtualNetwork.id(), virtualDevice3.id(),
+                                  PortNumber.portNumber(5),
+                                  new DefaultPort(virtualDevice3, PortNumber.portNumber(5), true));
+
+        cp6 = new ConnectPoint(virtualDevice3.id(), PortNumber.portNumber(6));
+        manager.createVirtualPort(virtualNetwork.id(), virtualDevice3.id(),
+                                  PortNumber.portNumber(6),
+                                  new DefaultPort(virtualDevice3, PortNumber.portNumber(6), true));
+
+        cp7 = new ConnectPoint(virtualDevice4.id(), PortNumber.portNumber(7));
+        manager.createVirtualPort(virtualNetwork.id(), virtualDevice4.id(),
+                                  PortNumber.portNumber(7),
+                                  new DefaultPort(virtualDevice4, PortNumber.portNumber(7), true));
+
+        ConnectPoint cp8 = new ConnectPoint(virtualDevice4.id(), PortNumber.portNumber(8));
+        manager.createVirtualPort(virtualNetwork.id(), virtualDevice4.id(),
+                                  PortNumber.portNumber(8),
+                                  new DefaultPort(virtualDevice4, PortNumber.portNumber(8), true));
+
+        ConnectPoint cp9 = new ConnectPoint(virtualDevice5.id(), PortNumber.portNumber(9));
+        manager.createVirtualPort(virtualNetwork.id(), virtualDevice5.id(),
+                                  PortNumber.portNumber(9),
+                                  new DefaultPort(virtualDevice5, PortNumber.portNumber(9), true));
+
+        VirtualLink link1 = manager.createVirtualLink(virtualNetwork.id(), cp1, cp3);
+        virtualNetworkManagerStore.updateLink(link1, link1.tunnelId(), Link.State.ACTIVE);
+        VirtualLink link2 = manager.createVirtualLink(virtualNetwork.id(), cp3, cp1);
+        virtualNetworkManagerStore.updateLink(link2, link2.tunnelId(), Link.State.ACTIVE);
+        VirtualLink link3 = manager.createVirtualLink(virtualNetwork.id(), cp4, cp5);
+        virtualNetworkManagerStore.updateLink(link3, link3.tunnelId(), Link.State.ACTIVE);
+        VirtualLink link4 = manager.createVirtualLink(virtualNetwork.id(), cp5, cp4);
+        virtualNetworkManagerStore.updateLink(link4, link4.tunnelId(), Link.State.ACTIVE);
+        VirtualLink link5 = manager.createVirtualLink(virtualNetwork.id(), cp8, cp9);
+        virtualNetworkManagerStore.updateLink(link5, link5.tunnelId(), Link.State.ACTIVE);
+        VirtualLink link6 = manager.createVirtualLink(virtualNetwork.id(), cp9, cp8);
+        virtualNetworkManagerStore.updateLink(link6, link6.tunnelId(), Link.State.ACTIVE);
+
+        topologyService = manager.get(virtualNetwork.id(), TopologyService.class);
+        topologyProvider = new VirtualNetworkTopologyProvider();
+        topologyProvider.topologyService = topologyService;
+
+        return virtualNetwork;
+    }
+
+    /**
+     * Test the topologyChanged() method.
+     */
+    @Test
+    public void testTopologyChanged() {
+        VirtualNetwork virtualNetwork = setupVirtualNetworkTopology();
+        VirtualNetworkProviderService providerService = manager.createProviderService(topologyProvider);
+
+        // Initial setup is two clusters of devices/links.
+        assertEquals("The cluster count did not match.", 2, topologyService.currentTopology().clusterCount());
+
+        // Adding this link will join the two clusters together.
+        List<Event> reasons = new ArrayList<>();
+        VirtualLink link = manager.createVirtualLink(virtualNetwork.id(), cp6, cp7);
+        virtualNetworkManagerStore.updateLink(link, link.tunnelId(), Link.State.ACTIVE);
+        VirtualLink link2 = manager.createVirtualLink(virtualNetwork.id(), cp7, cp6);
+        virtualNetworkManagerStore.updateLink(link2, link2.tunnelId(), Link.State.ACTIVE);
+
+        Topology topology = topologyService.currentTopology();
+        providerService.topologyChanged(topologyProvider.getConnectPoints(topology));
+
+        // Validate that all links are still active.
+        manager.getVirtualLinks(virtualNetwork.id()).forEach(virtualLink -> {
+            assertTrue("The virtual link should be active.", virtualLink.state().equals(Link.State.ACTIVE));
+        });
+
+        virtualNetworkManagerStore.updateLink(link, link.tunnelId(), Link.State.INACTIVE);
+        virtualNetworkManagerStore.updateLink(link2, link2.tunnelId(), Link.State.INACTIVE);
+        providerService.topologyChanged(topologyProvider.getConnectPoints(topology));
+
+        // Validate that all links are active again.
+        manager.getVirtualLinks(virtualNetwork.id()).forEach(virtualLink -> {
+            assertTrue("The virtual link should be active.", virtualLink.state().equals(Link.State.ACTIVE));
+        });
+    }
+
     /**
      * Method to validate that the actual versus expected virtual network events were
      * received correctly.
diff --git a/incubator/net/src/test/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkTopologyProviderTest.java b/incubator/net/src/test/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkTopologyProviderTest.java
new file mode 100644
index 0000000..6f53387
--- /dev/null
+++ b/incubator/net/src/test/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkTopologyProviderTest.java
@@ -0,0 +1,388 @@
+/*
+ * Copyright 2016-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.incubator.net.virtual.impl;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.junit.TestUtils;
+import org.onosproject.common.event.impl.TestEventDispatcher;
+import org.onosproject.core.CoreService;
+import org.onosproject.core.CoreServiceAdapter;
+import org.onosproject.core.IdGenerator;
+import org.onosproject.event.Event;
+import org.onosproject.incubator.net.tunnel.TunnelId;
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.incubator.net.virtual.TenantId;
+import org.onosproject.incubator.net.virtual.VirtualDevice;
+import org.onosproject.incubator.net.virtual.VirtualLink;
+import org.onosproject.incubator.net.virtual.VirtualNetwork;
+import org.onosproject.incubator.net.virtual.VirtualNetworkProvider;
+import org.onosproject.incubator.net.virtual.VirtualNetworkProviderRegistry;
+import org.onosproject.incubator.net.virtual.VirtualNetworkProviderService;
+import org.onosproject.incubator.store.virtual.impl.DistributedVirtualNetworkStore;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DefaultPort;
+import org.onosproject.net.Link;
+import org.onosproject.net.NetTestTools;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.TestDeviceParams;
+import org.onosproject.net.intent.FakeIntentManager;
+import org.onosproject.net.intent.TestableIntentService;
+import org.onosproject.net.link.LinkEvent;
+import org.onosproject.net.provider.AbstractProviderService;
+import org.onosproject.net.provider.ProviderId;
+import org.onosproject.net.topology.TopologyEvent;
+import org.onosproject.net.topology.TopologyService;
+import org.onosproject.store.service.TestStorageService;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicLong;
+
+import static org.junit.Assert.*;
+
+/**
+ * Junit tests for VirtualNetworkTopologyProvider.
+ */
+public class VirtualNetworkTopologyProviderTest extends TestDeviceParams {
+
+    private final String tenantIdValue1 = "TENANT_ID1";
+
+    private VirtualNetwork virtualNetwork;
+    private VirtualDevice virtualDevice1;
+    private VirtualDevice virtualDevice2;
+    private VirtualDevice virtualDevice3;
+    private VirtualDevice virtualDevice4;
+    private VirtualDevice virtualDevice5;
+    private ConnectPoint cp1;
+    private ConnectPoint cp2;
+    private ConnectPoint cp3;
+    private ConnectPoint cp4;
+    private ConnectPoint cp5;
+    private ConnectPoint cp6;
+    private ConnectPoint cp7;
+    private ConnectPoint cp8;
+    private ConnectPoint cp9;
+
+    private VirtualNetworkManager manager;
+    private DistributedVirtualNetworkStore virtualNetworkManagerStore;
+    private CoreService coreService;
+    private VirtualNetworkTopologyProvider topologyProvider;
+    private TopologyService topologyService;
+    private TestableIntentService intentService = new FakeIntentManager();
+    private final VirtualNetworkRegistryAdapter virtualNetworkRegistry = new VirtualNetworkRegistryAdapter();
+
+    private static final int MAX_WAIT_TIME = 5;
+    private static final int MAX_PERMITS = 1;
+    private static Semaphore changed;
+
+    private Set<Set<ConnectPoint>> clusters;
+
+    @Before
+    public void setUp() throws Exception {
+
+        virtualNetworkManagerStore = new DistributedVirtualNetworkStore();
+
+        coreService = new VirtualNetworkTopologyProviderTest.TestCoreService();
+
+        virtualNetworkManagerStore.setCoreService(coreService);
+        TestUtils.setField(coreService, "coreService", new VirtualNetworkTopologyProviderTest.TestCoreService());
+        TestUtils.setField(virtualNetworkManagerStore, "storageService", new TestStorageService());
+        virtualNetworkManagerStore.activate();
+
+        manager = new VirtualNetworkManager();
+        manager.store = virtualNetworkManagerStore;
+        manager.intentService = intentService;
+        NetTestTools.injectEventDispatcher(manager, new TestEventDispatcher());
+        manager.activate();
+
+        manager.registerTenantId(TenantId.tenantId(tenantIdValue1));
+        virtualNetwork = manager.createVirtualNetwork(TenantId.tenantId(tenantIdValue1));
+
+        topologyService = manager.get(virtualNetwork.id(), TopologyService.class);
+        topologyProvider = new VirtualNetworkTopologyProvider();
+        topologyProvider.topologyService = topologyService;
+        topologyProvider.providerRegistry = virtualNetworkRegistry;
+        topologyProvider.activate();
+
+        setupVirtualNetworkTopology();
+        changed = new Semaphore(0, true);
+    }
+
+    @After
+    public void tearDown() {
+        topologyProvider.deactivate();
+        virtualNetworkManagerStore.deactivate();
+        manager.deactivate();
+        NetTestTools.injectEventDispatcher(manager, null);
+    }
+
+    /**
+     * Method to create the virtual network for further testing.
+     **/
+    private void setupVirtualNetworkTopology() {
+        virtualDevice1 =
+                manager.createVirtualDevice(virtualNetwork.id(), DID1);
+        virtualDevice2 =
+                manager.createVirtualDevice(virtualNetwork.id(), DID2);
+        virtualDevice3 =
+                manager.createVirtualDevice(virtualNetwork.id(), DID3);
+        virtualDevice4 =
+                manager.createVirtualDevice(virtualNetwork.id(), DID4);
+        virtualDevice5 =
+                manager.createVirtualDevice(virtualNetwork.id(), DID5);
+
+        cp1 = new ConnectPoint(virtualDevice1.id(), PortNumber.portNumber(1));
+        manager.createVirtualPort(virtualNetwork.id(), virtualDevice1.id(),
+                                  PortNumber.portNumber(1),
+                                  new DefaultPort(virtualDevice1, PortNumber.portNumber(1), true));
+
+        cp2 = new ConnectPoint(virtualDevice1.id(), PortNumber.portNumber(2));
+        manager.createVirtualPort(virtualNetwork.id(), virtualDevice1.id(),
+                                  PortNumber.portNumber(2),
+                                  new DefaultPort(virtualDevice1, PortNumber.portNumber(2), true));
+
+        cp3 = new ConnectPoint(virtualDevice2.id(), PortNumber.portNumber(3));
+        manager.createVirtualPort(virtualNetwork.id(), virtualDevice2.id(),
+                                  PortNumber.portNumber(3),
+                                  new DefaultPort(virtualDevice2, PortNumber.portNumber(3), true));
+
+        cp4 = new ConnectPoint(virtualDevice2.id(), PortNumber.portNumber(4));
+        manager.createVirtualPort(virtualNetwork.id(), virtualDevice2.id(),
+                                  PortNumber.portNumber(4),
+                                  new DefaultPort(virtualDevice2, PortNumber.portNumber(4), true));
+
+        cp5 = new ConnectPoint(virtualDevice3.id(), PortNumber.portNumber(5));
+        manager.createVirtualPort(virtualNetwork.id(), virtualDevice3.id(),
+                                  PortNumber.portNumber(5),
+                                  new DefaultPort(virtualDevice3, PortNumber.portNumber(5), true));
+
+        cp6 = new ConnectPoint(virtualDevice3.id(), PortNumber.portNumber(6));
+        manager.createVirtualPort(virtualNetwork.id(), virtualDevice3.id(),
+                                  PortNumber.portNumber(6),
+                                  new DefaultPort(virtualDevice3, PortNumber.portNumber(6), true));
+
+        cp7 = new ConnectPoint(virtualDevice4.id(), PortNumber.portNumber(7));
+        manager.createVirtualPort(virtualNetwork.id(), virtualDevice4.id(),
+                                  PortNumber.portNumber(7),
+                                  new DefaultPort(virtualDevice4, PortNumber.portNumber(7), true));
+
+        cp8 = new ConnectPoint(virtualDevice4.id(), PortNumber.portNumber(8));
+        manager.createVirtualPort(virtualNetwork.id(), virtualDevice4.id(),
+                                  PortNumber.portNumber(8),
+                                  new DefaultPort(virtualDevice4, PortNumber.portNumber(8), true));
+
+        cp9 = new ConnectPoint(virtualDevice5.id(), PortNumber.portNumber(9));
+        manager.createVirtualPort(virtualNetwork.id(), virtualDevice5.id(),
+                                  PortNumber.portNumber(9),
+                                  new DefaultPort(virtualDevice5, PortNumber.portNumber(9), true));
+
+        VirtualLink link1 = manager.createVirtualLink(virtualNetwork.id(), cp1, cp3);
+        virtualNetworkManagerStore.updateLink(link1, link1.tunnelId(), Link.State.ACTIVE);
+        VirtualLink link2 = manager.createVirtualLink(virtualNetwork.id(), cp3, cp1);
+        virtualNetworkManagerStore.updateLink(link2, link2.tunnelId(), Link.State.ACTIVE);
+        VirtualLink link3 = manager.createVirtualLink(virtualNetwork.id(), cp4, cp5);
+        virtualNetworkManagerStore.updateLink(link3, link3.tunnelId(), Link.State.ACTIVE);
+        VirtualLink link4 = manager.createVirtualLink(virtualNetwork.id(), cp5, cp4);
+        virtualNetworkManagerStore.updateLink(link4, link4.tunnelId(), Link.State.ACTIVE);
+        VirtualLink link5 = manager.createVirtualLink(virtualNetwork.id(), cp8, cp9);
+        virtualNetworkManagerStore.updateLink(link5, link5.tunnelId(), Link.State.ACTIVE);
+        VirtualLink link6 = manager.createVirtualLink(virtualNetwork.id(), cp9, cp8);
+        virtualNetworkManagerStore.updateLink(link6, link6.tunnelId(), Link.State.ACTIVE);
+
+        clusters = null;
+    }
+
+    /**
+     * Test isTraversable() method using a null source connect point.
+     */
+    @Test(expected = NullPointerException.class)
+    public void testIsTraversableNullSrc() {
+        // test the isTraversable() method with a null source connect point.
+        topologyProvider.isTraversable(null, cp3);
+    }
+
+    /**
+     * Test isTraversable() method using a null destination connect point.
+     */
+    @Test(expected = NullPointerException.class)
+    public void testIsTraversableNullDst() {
+        // test the isTraversable() method with a null destination connect point.
+        topologyProvider.isTraversable(cp1, null);
+    }
+
+    /**
+     * Test isTraversable() method.
+     */
+    @Test
+    public void testIsTraversable() {
+        // test the isTraversable() method.
+        assertTrue("These two connect points should be traversable.",
+                   topologyProvider.isTraversable(new ConnectPoint(cp1.elementId(), cp1.port()),
+                                                  new ConnectPoint(cp3.elementId(), cp3.port())));
+        assertTrue("These two connect points should be traversable.",
+                   topologyProvider.isTraversable(new ConnectPoint(cp1.elementId(), cp1.port()),
+                                                  new ConnectPoint(cp5.elementId(), cp5.port())));
+        assertFalse("These two connect points should not be traversable.",
+                    topologyProvider.isTraversable(new ConnectPoint(virtualDevice1.id(), PortNumber.portNumber(1)),
+                                                   new ConnectPoint(virtualDevice4.id(), PortNumber.portNumber(6))));
+    }
+
+    /**
+     * Test the topologyChanged() method.
+     */
+    @Test
+    public void testTopologyChanged() {
+        // Initial setup is two clusters of devices/links.
+        assertEquals("The cluster count did not match.", 2, topologyService.currentTopology().clusterCount());
+
+        // Adding this link will join the two clusters together.
+        List<Event> reasons = new ArrayList<>();
+        VirtualLink link = manager.createVirtualLink(virtualNetwork.id(), cp6, cp7);
+        virtualNetworkManagerStore.updateLink(link, link.tunnelId(), Link.State.ACTIVE);
+        VirtualLink link2 = manager.createVirtualLink(virtualNetwork.id(), cp7, cp6);
+        virtualNetworkManagerStore.updateLink(link2, link2.tunnelId(), Link.State.ACTIVE);
+
+        reasons.add(new LinkEvent(LinkEvent.Type.LINK_ADDED, link));
+        reasons.add(new LinkEvent(LinkEvent.Type.LINK_ADDED, link2));
+        TopologyEvent event = new TopologyEvent(
+                TopologyEvent.Type.TOPOLOGY_CHANGED,
+                topologyService.currentTopology(),
+                reasons);
+
+        topologyProvider.topologyListener.event(event);
+
+        // Wait for the topology changed event, and that the topologyChanged method was called.
+        try {
+            if (!changed.tryAcquire(MAX_PERMITS, MAX_WAIT_TIME, TimeUnit.SECONDS)) {
+                fail("Failed to wait for topology changed event.");
+            }
+        } catch (InterruptedException e) {
+            fail("Semaphore exception." + e.getMessage());
+        }
+
+        // Validate that the topology changed method received a single cluster of connect points.
+        // This means that the two previous clusters have now joined into a single cluster.
+        assertEquals("The cluster count did not match.", 1, this.clusters.size());
+        assertEquals("The cluster count did not match.", 1, topologyService.currentTopology().clusterCount());
+
+        // Now remove the virtual link to split it back into two clusters.
+        manager.removeVirtualLink(virtualNetwork.id(), link.src(), link.dst());
+        manager.removeVirtualLink(virtualNetwork.id(), link2.src(), link2.dst());
+        assertEquals("The cluster count did not match.", 2, topologyService.currentTopology().clusterCount());
+
+        reasons = new ArrayList<>();
+        reasons.add(new LinkEvent(LinkEvent.Type.LINK_REMOVED, link));
+        reasons.add(new LinkEvent(LinkEvent.Type.LINK_REMOVED, link2));
+        event = new TopologyEvent(
+                TopologyEvent.Type.TOPOLOGY_CHANGED,
+                topologyService.currentTopology(),
+                reasons);
+
+        topologyProvider.topologyListener.event(event);
+
+        // Wait for the topology changed event, and that the topologyChanged method was called.
+        try {
+            if (!changed.tryAcquire(MAX_PERMITS, MAX_WAIT_TIME, TimeUnit.SECONDS)) {
+                fail("Failed to wait for topology changed event.");
+            }
+        } catch (InterruptedException e) {
+            fail("Semaphore exception." + e.getMessage());
+        }
+
+        // Validate that the topology changed method received two clusters of connect points.
+        // This means that the single previous clusters has now split into two clusters.
+        assertEquals("The cluster count did not match.", 2, this.clusters.size());
+    }
+
+    /**
+     * Virtual network registry implementation for this test class.
+     */
+    private class VirtualNetworkRegistryAdapter implements VirtualNetworkProviderRegistry {
+        private VirtualNetworkProvider provider;
+
+        @Override
+        public VirtualNetworkProviderService register(VirtualNetworkProvider theProvider) {
+            this.provider = theProvider;
+            return new TestVirtualNetworkProviderService(theProvider);
+        }
+
+        @Override
+        public void unregister(VirtualNetworkProvider theProvider) {
+            this.provider = null;
+        }
+
+        @Override
+        public Set<ProviderId> getProviders() {
+            return null;
+        }
+    }
+
+
+    /**
+     * Virtual network provider service implementation for this test class.
+     */
+    private class TestVirtualNetworkProviderService
+            extends AbstractProviderService<VirtualNetworkProvider>
+            implements VirtualNetworkProviderService {
+
+        /**
+         * Constructor.
+         *
+         * @param provider virtual network test provider
+         */
+        protected TestVirtualNetworkProviderService(VirtualNetworkProvider provider) {
+            super(provider);
+        }
+
+        @Override
+        public void topologyChanged(Set<Set<ConnectPoint>> theClusters) {
+            clusters = theClusters;
+            changed.release();
+        }
+
+        @Override
+        public void tunnelUp(NetworkId networkId, ConnectPoint src, ConnectPoint dst, TunnelId tunnelId) {
+        }
+
+        @Override
+        public void tunnelDown(NetworkId networkId, ConnectPoint src, ConnectPoint dst, TunnelId tunnelId) {
+        }
+    }
+
+    /**
+     * Core service test class.
+     */
+    private class TestCoreService extends CoreServiceAdapter {
+
+        @Override
+        public IdGenerator getIdGenerator(String topic) {
+            return new IdGenerator() {
+                private AtomicLong counter = new AtomicLong(0);
+
+                @Override
+                public long getNewId() {
+                    return counter.getAndIncrement();
+                }
+            };
+        }
+    }
+}
diff --git a/incubator/net/src/test/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkTopologyServiceTest.java b/incubator/net/src/test/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkTopologyServiceTest.java
index 802bc0f..267e012 100644
--- a/incubator/net/src/test/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkTopologyServiceTest.java
+++ b/incubator/net/src/test/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkTopologyServiceTest.java
@@ -31,6 +31,7 @@
 import org.onosproject.incubator.net.virtual.VirtualNetwork;
 import org.onosproject.incubator.store.virtual.impl.DistributedVirtualNetworkStore;
 import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DefaultPort;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.DisjointPath;
 import org.onosproject.net.Link;
@@ -108,11 +109,29 @@
                 manager.createVirtualDevice(virtualNetwork.id(), DID4);
 
         ConnectPoint cp1 = new ConnectPoint(virtualDevice1.id(), PortNumber.portNumber(1));
+        manager.createVirtualPort(virtualNetwork.id(), cp1.deviceId(), cp1.port(),
+                                  new DefaultPort(virtualDevice1, cp1.port(), true));
+
         ConnectPoint cp2 = new ConnectPoint(virtualDevice1.id(), PortNumber.portNumber(2));
+        manager.createVirtualPort(virtualNetwork.id(), cp2.deviceId(), cp2.port(),
+                                  new DefaultPort(virtualDevice1, cp2.port(), true));
+
         ConnectPoint cp3 = new ConnectPoint(virtualDevice2.id(), PortNumber.portNumber(3));
+        manager.createVirtualPort(virtualNetwork.id(), cp3.deviceId(), cp3.port(),
+                                  new DefaultPort(virtualDevice2, cp3.port(), true));
+
         ConnectPoint cp4 = new ConnectPoint(virtualDevice2.id(), PortNumber.portNumber(4));
+        manager.createVirtualPort(virtualNetwork.id(), cp4.deviceId(), cp4.port(),
+                                  new DefaultPort(virtualDevice2, cp4.port(), true));
+
         ConnectPoint cp5 = new ConnectPoint(virtualDevice3.id(), PortNumber.portNumber(5));
+        manager.createVirtualPort(virtualNetwork.id(), cp5.deviceId(), cp5.port(),
+                                  new DefaultPort(virtualDevice3, cp5.port(), true));
+
         ConnectPoint cp6 = new ConnectPoint(virtualDevice3.id(), PortNumber.portNumber(6));
+        manager.createVirtualPort(virtualNetwork.id(), cp6.deviceId(), cp6.port(),
+                                  new DefaultPort(virtualDevice3, cp6.port(), true));
+
         VirtualLink link1 = manager.createVirtualLink(virtualNetwork.id(), cp1, cp3);
         virtualNetworkManagerStore.updateLink(link1, link1.tunnelId(), Link.State.ACTIVE);
         VirtualLink link2 = manager.createVirtualLink(virtualNetwork.id(), cp3, cp1);
@@ -177,7 +196,12 @@
 
         topology = topologyService.currentTopology();
         ConnectPoint src = new ConnectPoint(srcVirtualDevice.id(), PortNumber.portNumber(1));
+        manager.createVirtualPort(virtualNetwork.id(), src.deviceId(), src.port(),
+                                  new DefaultPort(srcVirtualDevice, src.port(), true));
+
         ConnectPoint dst = new ConnectPoint(dstVirtualDevice.id(), PortNumber.portNumber(2));
+        manager.createVirtualPort(virtualNetwork.id(), dst.deviceId(), dst.port(),
+                                  new DefaultPort(dstVirtualDevice, dst.port(), true));
         VirtualLink link1 = manager.createVirtualLink(virtualNetwork.id(), src, dst);
 
         // test the isLatest() method where a new link has been added to the current topology.