ONOS-3633 - Adding intent event listener to PointToPointIntent virtual network
provider. Intent events will either set the virtual link state to ACTIVE or
INACTIVE.

Change-Id: I34b65b2bfff29b791e7b2eb4d7cefb2ec4e88672
diff --git a/cli/src/main/java/org/onosproject/cli/net/vnet/VirtualLinkListCommand.java b/cli/src/main/java/org/onosproject/cli/net/vnet/VirtualLinkListCommand.java
index 9f47437..d606059 100644
--- a/cli/src/main/java/org/onosproject/cli/net/vnet/VirtualLinkListCommand.java
+++ b/cli/src/main/java/org/onosproject/cli/net/vnet/VirtualLinkListCommand.java
@@ -34,7 +34,7 @@
 public class VirtualLinkListCommand extends AbstractShellCommand {
 
     private static final String FMT_VIRTUAL_LINK =
-            "src=%s, dst=%s, tunnelId=%s";
+            "src=%s, dst=%s, state=%s, tunnelId=%s";
 
     @Argument(index = 0, name = "networkId", description = "Network ID",
             required = true, multiValued = false)
@@ -66,6 +66,7 @@
      */
     private void printVirtualLink(VirtualLink virtualLink) {
         print(FMT_VIRTUAL_LINK, virtualLink.src().toString(), virtualLink.dst().toString(),
+              virtualLink.state(),
               virtualLink.tunnelId() == null ? null : virtualLink.tunnelId().toString());
     }
 }
diff --git a/core/api/src/main/java/org/onosproject/net/DefaultEdgeLink.java b/core/api/src/main/java/org/onosproject/net/DefaultEdgeLink.java
index e08f9fc..7bafeda 100644
--- a/core/api/src/main/java/org/onosproject/net/DefaultEdgeLink.java
+++ b/core/api/src/main/java/org/onosproject/net/DefaultEdgeLink.java
@@ -42,7 +42,7 @@
                            HostLocation hostLocation, boolean isIngress,
                            Annotations... annotations) {
         super(providerId, isIngress ? hostPoint : hostLocation,
-              isIngress ? hostLocation : hostPoint, Type.EDGE, annotations);
+              isIngress ? hostLocation : hostPoint, Type.EDGE, State.ACTIVE, annotations);
         checkArgument(hostPoint.elementId() instanceof HostId,
                       "Host point does not refer to a host ID");
         this.hostId = (HostId) hostPoint.elementId();
diff --git a/core/api/src/main/java/org/onosproject/net/DefaultLink.java b/core/api/src/main/java/org/onosproject/net/DefaultLink.java
index 9aed9c9..628d785 100644
--- a/core/api/src/main/java/org/onosproject/net/DefaultLink.java
+++ b/core/api/src/main/java/org/onosproject/net/DefaultLink.java
@@ -36,17 +36,18 @@
     private final boolean isExpected;
 
     /**
-     * Creates an active infrastructure link using the supplied information.
+     * Creates an infrastructure link using the supplied information.
      *
      * @param providerId  provider identity
      * @param src         link source
      * @param dst         link destination
      * @param type        link type
+     * @param state       link state
      * @param annotations optional key/value annotations
      */
     protected DefaultLink(ProviderId providerId, ConnectPoint src, ConnectPoint dst,
-                       Type type, Annotations... annotations) {
-        this(providerId, src, dst, type, ACTIVE, false, annotations);
+                       Type type, State state, Annotations... annotations) {
+        this(providerId, src, dst, type, state, false, annotations);
     }
 
     /**
diff --git a/core/api/src/main/java/org/onosproject/net/DefaultPath.java b/core/api/src/main/java/org/onosproject/net/DefaultPath.java
index 9f0480d..e4c3d8c 100644
--- a/core/api/src/main/java/org/onosproject/net/DefaultPath.java
+++ b/core/api/src/main/java/org/onosproject/net/DefaultPath.java
@@ -44,7 +44,7 @@
      */
     public DefaultPath(ProviderId providerId, List<Link> links, double cost,
                        Annotations... annotations) {
-        super(providerId, source(links), destination(links), Type.INDIRECT, annotations);
+        super(providerId, source(links), destination(links), Type.INDIRECT, State.ACTIVE, annotations);
         this.links = ImmutableList.copyOf(links);
         this.cost = cost;
     }
diff --git a/core/api/src/test/java/org/onosproject/net/DefaultLinkTest.java b/core/api/src/test/java/org/onosproject/net/DefaultLinkTest.java
index 33af473..7bb7a5c 100644
--- a/core/api/src/test/java/org/onosproject/net/DefaultLinkTest.java
+++ b/core/api/src/test/java/org/onosproject/net/DefaultLinkTest.java
@@ -42,11 +42,11 @@
 
     @Test
     public void testEquality() {
-        Link l1 = new DefaultLink(PID, cp(DID1, P1), cp(DID2, P2), DIRECT);
-        Link l2 = new DefaultLink(PID, cp(DID1, P1), cp(DID2, P2), DIRECT);
-        Link l3 = new DefaultLink(PID, cp(DID1, P2), cp(DID2, P2), DIRECT);
-        Link l4 = new DefaultLink(PID, cp(DID1, P2), cp(DID2, P2), DIRECT);
-        Link l5 = new DefaultLink(PID, cp(DID1, P2), cp(DID2, P2), INDIRECT);
+        Link l1 = new DefaultLink(PID, cp(DID1, P1), cp(DID2, P2), DIRECT, Link.State.ACTIVE);
+        Link l2 = new DefaultLink(PID, cp(DID1, P1), cp(DID2, P2), DIRECT, Link.State.ACTIVE);
+        Link l3 = new DefaultLink(PID, cp(DID1, P2), cp(DID2, P2), DIRECT, Link.State.ACTIVE);
+        Link l4 = new DefaultLink(PID, cp(DID1, P2), cp(DID2, P2), DIRECT, Link.State.ACTIVE);
+        Link l5 = new DefaultLink(PID, cp(DID1, P2), cp(DID2, P2), INDIRECT, Link.State.ACTIVE);
 
         new EqualsTester().addEqualityGroup(l1, l2)
                 .addEqualityGroup(l3, l4)
@@ -56,7 +56,7 @@
 
     @Test
     public void basics() {
-        Link link = new DefaultLink(PID, cp(DID1, P1), cp(DID2, P2), DIRECT);
+        Link link = new DefaultLink(PID, cp(DID1, P1), cp(DID2, P2), DIRECT, Link.State.ACTIVE);
         assertEquals("incorrect src", cp(DID1, P1), link.src());
         assertEquals("incorrect dst", cp(DID2, P2), link.dst());
         assertEquals("incorrect type", DIRECT, link.type());
diff --git a/core/api/src/test/java/org/onosproject/net/NetTestTools.java b/core/api/src/test/java/org/onosproject/net/NetTestTools.java
index 010c2cd..8898008 100644
--- a/core/api/src/test/java/org/onosproject/net/NetTestTools.java
+++ b/core/api/src/test/java/org/onosproject/net/NetTestTools.java
@@ -81,7 +81,7 @@
         return new DefaultLink(PID,
                                connectPoint(src, sp),
                                connectPoint(dst, dp),
-                               Link.Type.DIRECT);
+                               Link.Type.DIRECT, Link.State.ACTIVE);
     }
 
     // Creates a path that leads through the given devices.
diff --git a/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/DefaultVirtualLink.java b/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/DefaultVirtualLink.java
index ff42dcd..0daa8b5 100644
--- a/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/DefaultVirtualLink.java
+++ b/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/DefaultVirtualLink.java
@@ -44,10 +44,12 @@
      * @param networkId network identifier
      * @param src       source connection point
      * @param dst       destination connection point
+     * @param state     link state
      * @param tunnelId  tunnel identifier
      */
-    private DefaultVirtualLink(NetworkId networkId, ConnectPoint src, ConnectPoint dst, TunnelId tunnelId) {
-        super(PID, src, dst, Type.VIRTUAL, DefaultAnnotations.builder().build());
+    private DefaultVirtualLink(NetworkId networkId, ConnectPoint src, ConnectPoint dst,
+                               State state, TunnelId tunnelId) {
+        super(PID, src, dst, Type.VIRTUAL, state, DefaultAnnotations.builder().build());
         this.networkId = networkId;
         this.tunnelId = tunnelId;
     }
@@ -107,6 +109,7 @@
         private ConnectPoint src;
         private ConnectPoint dst;
         private TunnelId tunnelId;
+        private State state;
 
         private Builder() {
             // Hide constructor
@@ -157,6 +160,17 @@
         }
 
         /**
+         * Sets the link state to be used by the builder.
+         *
+         * @param state link state
+         * @return self
+         */
+        public Builder state(State state) {
+            this.state = state;
+            return this;
+        }
+
+        /**
          * Builds a default virtual link object from the accumulated parameters.
          *
          * @return default virtual link object
@@ -166,7 +180,7 @@
             checkNotNull(dst, "Destination connect point cannot be null");
             checkNotNull(networkId, "Network Id cannot be null");
 
-            return new DefaultVirtualLink(networkId, src, dst, tunnelId);
+            return new DefaultVirtualLink(networkId, src, dst, state, tunnelId);
         }
     }
 }
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 6cc4ad8..5d63c7d 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
@@ -15,6 +15,7 @@
  */
 package org.onosproject.incubator.net.virtual;
 
+import org.onosproject.incubator.net.tunnel.TunnelId;
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.provider.ProviderService;
 
@@ -30,8 +31,9 @@
      * @param networkId network identifier
      * @param src       source connection point
      * @param dst       destination connection point
+     * @param tunnelId  tunnel identifier
      */
-    void tunnelUp(NetworkId networkId, ConnectPoint src, ConnectPoint dst);
+    void tunnelUp(NetworkId networkId, ConnectPoint src, ConnectPoint dst, TunnelId tunnelId);
 
     /**
      * This method is used to notify the VirtualNetwork service that a tunnel is now
@@ -40,7 +42,8 @@
      * @param networkId network identifier
      * @param src       source connection point
      * @param dst       destination connection point
+     * @param tunnelId  tunnel identifier
      */
-    void tunnelDown(NetworkId networkId, ConnectPoint src, ConnectPoint dst);
+    void tunnelDown(NetworkId networkId, ConnectPoint src, ConnectPoint dst, TunnelId tunnelId);
 
 }
diff --git a/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkService.java b/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkService.java
index 6357aee..ebe0b69 100644
--- a/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkService.java
+++ b/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkService.java
@@ -60,7 +60,9 @@
     Set<VirtualLink> getVirtualLinks(NetworkId networkId);
 
     /**
-     * Returns list of all virtual ports of the specified device.
+     * Returns list of all virtual ports of the specified device. If the
+     * device identifier is null then all of the virtual ports in the specified
+     * network will be returned.
      *
      * @param networkId network identifier
      * @param deviceId  device identifier
diff --git a/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkStore.java b/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkStore.java
index 35fb0c3..663c208 100644
--- a/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkStore.java
+++ b/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkStore.java
@@ -18,6 +18,7 @@
 import org.onosproject.incubator.net.tunnel.TunnelId;
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.DeviceId;
+import org.onosproject.net.Link;
 import org.onosproject.net.Port;
 import org.onosproject.net.PortNumber;
 import org.onosproject.store.Store;
@@ -89,18 +90,20 @@
      * @param networkId  network identifier
      * @param src        source end-point of the link
      * @param dst        destination end-point of the link
+     * @param state      link state
      * @param realizedBy underlying tunnel identifier using which this link is realized
      * @return the virtual link
      */
-    VirtualLink addLink(NetworkId networkId, ConnectPoint src, ConnectPoint dst, TunnelId realizedBy);
+    VirtualLink addLink(NetworkId networkId, ConnectPoint src, ConnectPoint dst, Link.State state, TunnelId realizedBy);
 
     /**
      * Updates the tunnelId in the virtual link.
      *
-     * @param virtualLink  virtual link
-     * @param tunnelId tunnel identifier
+     * @param virtualLink virtual link
+     * @param tunnelId    tunnel identifier
+     * @param state       link state
      */
-    void updateLink(VirtualLink virtualLink, TunnelId tunnelId);
+    void updateLink(VirtualLink virtualLink, TunnelId tunnelId, Link.State state);
 
     /**
      * Removes the specified link from the store.
@@ -158,10 +161,21 @@
     Set<VirtualLink> getLinks(NetworkId networkId);
 
     /**
+     * Returns the virtual link matching the network identifier, source connect point,
+     * and destination connect point.
+     *
+     * @param networkId network identifier
+     * @param src       source connect point
+     * @param dst       destination connect point
+     * @return virtual link
+     */
+    VirtualLink getLink(NetworkId networkId, ConnectPoint src, ConnectPoint dst);
+
+    /**
      * Returns the list of ports of the specified virtual device.
      *
      * @param networkId network identifier
-     * @param deviceId   device identifier
+     * @param deviceId  device identifier
      * @return set of virtual networks
      */
     Set<VirtualPort> getPorts(NetworkId networkId, DeviceId deviceId);
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 7cd09ae..00d203e 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
@@ -35,8 +35,9 @@
 import org.onosproject.net.EncapsulationType;
 import org.onosproject.net.intent.Constraint;
 import org.onosproject.net.intent.Intent;
+import org.onosproject.net.intent.IntentEvent;
+import org.onosproject.net.intent.IntentListener;
 import org.onosproject.net.intent.IntentService;
-import org.onosproject.net.intent.IntentState;
 import org.onosproject.net.intent.Key;
 import org.onosproject.net.intent.PointToPointIntent;
 import org.onosproject.net.intent.constraint.EncapsulationConstraint;
@@ -45,9 +46,9 @@
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.StringTokenizer;
 
 import static com.google.common.base.Preconditions.checkNotNull;
-import static java.lang.Thread.sleep;
 import static org.slf4j.LoggerFactory.getLogger;
 
 /**
@@ -55,13 +56,15 @@
  */
 @Component(immediate = true)
 @Service
-public class PtToPtIntentVirtualNetworkProvider extends AbstractProvider implements VirtualNetworkProvider {
+public class PtToPtIntentVirtualNetworkProvider extends AbstractProvider
+        implements VirtualNetworkProvider {
 
     private final Logger log = getLogger(PtToPtIntentVirtualNetworkProvider.class);
     private static final String NETWORK_ID_NULL = "Network ID cannot be null";
     private static final String CONNECT_POINT_NULL = "Connect Point cannot be null";
     private static final String INTENT_NULL = "Intent cannot be null";
-    protected static final String KEY_FORMAT = "networkId=%s src=%s dst=%s";
+    private static final String NETWORK_ID = "networkId=";
+    protected static final String KEY_FORMAT = NETWORK_ID + "%s, src=%s, dst=%s";
     private static final int MAX_WAIT_COUNT = 30;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
@@ -72,6 +75,8 @@
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected IntentService intentService;
 
+    private final InternalPtPtIntentListener intentListener = new InternalPtPtIntentListener();
+
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected CoreService coreService;
 
@@ -90,11 +95,13 @@
         providerService = providerRegistry.register(this);
         appId = coreService.registerApplication(PTPT_INTENT_APPID);
 
+        intentService.addListener(intentListener);
         log.info("Started");
     }
 
     @Deactivate
     public void deactivate() {
+        intentService.removeListener(intentListener);
         providerRegistry.unregister(this);
         providerService = null;
         log.info("Stopped");
@@ -105,8 +112,7 @@
         checkNotNull(networkId, NETWORK_ID_NULL);
         checkNotNull(src, CONNECT_POINT_NULL);
         checkNotNull(dst, CONNECT_POINT_NULL);
-        String key = String.format(KEY_FORMAT, networkId.toString(), src.toString(), dst.toString());
-        Key intentKey = Key.of(key, appId);
+        Key intentKey = encodeKey(networkId, src, dst);
 
         List<Constraint> constraints = new ArrayList<>();
         constraints.add(new EncapsulationConstraint(EncapsulationType.VLAN));
@@ -123,7 +129,7 @@
         intentService.submit(intent);
 
         // construct tunnelId from the key
-        return TunnelId.valueOf(key);
+        return TunnelId.valueOf(intentKey.toString());
     }
 
     @Override
@@ -133,20 +139,64 @@
         Intent intent = intentService.getIntent(intentKey);
         checkNotNull(intent, INTENT_NULL);
         intentService.withdraw(intent);
-        try {
-            int count = 0;
-            // Loop waiting for the intent to go into a withdrawn or failed state
-            // before attempting to purge it.
-            while (++count <= MAX_WAIT_COUNT) {
-                IntentState state = intentService.getIntentState(intentKey);
-                if ((state == IntentState.FAILED) || (state == IntentState.WITHDRAWN)) {
-                    intentService.purge(intent);
-                    break;
-                }
-                sleep(1000);
+    }
+
+    private NetworkId decodeNetworkIdFromKey(Key intentKey) {
+        // Extract the network identifier from the intent key
+        StringTokenizer tokenizer = new StringTokenizer(intentKey.toString(), ",");
+        String networkIdString = tokenizer.nextToken().substring(NETWORK_ID.length());
+        return NetworkId.networkId(Integer.valueOf(networkIdString));
+    }
+
+    private Key encodeKey(NetworkId networkId, ConnectPoint src, ConnectPoint dst) {
+        String key = String.format(KEY_FORMAT, networkId, src, dst);
+        return Key.of(key, appId);
+    }
+
+    private class InternalPtPtIntentListener implements IntentListener {
+        @Override
+        public void event(IntentEvent event) {
+            PointToPointIntent intent = (PointToPointIntent) event.subject();
+            Key intentKey = intent.key();
+
+            // Ignore intent events that are not relevant.
+            if (!isRelevant(event)) {
+                return;
             }
-        } catch (Exception e) {
-            log.error("Exception: " + e);
+
+            NetworkId networkId = decodeNetworkIdFromKey(intentKey);
+            ConnectPoint src = intent.ingressPoint();
+            ConnectPoint dst = intent.egressPoint();
+
+            switch (event.type()) {
+                case INSTALLED:
+                    providerService.tunnelUp(networkId, src, dst, TunnelId.valueOf(intentKey.toString()));
+                    break;
+                case WITHDRAWN:
+                    intentService.purge(intent);
+                    // Fall through and notify the provider service that the tunnel is down
+                    // for both WITHDRAWN and FAILED intent event types.
+                case FAILED:
+                    providerService.tunnelDown(networkId, src, dst, TunnelId.valueOf(intentKey.toString()));
+                    break;
+                case INSTALL_REQ:
+                case CORRUPT:
+                case PURGED:
+                    break; // Not sure what do with these events, ignore for now.
+                default:
+                    break;
+            }
+        }
+
+        @Override
+        public boolean isRelevant(IntentEvent event) {
+            PointToPointIntent intent = (PointToPointIntent) event.subject();
+
+            // Only events that are for this appId are relevent.
+            if (intent.appId().equals(appId)) {
+                return true;
+            }
+            return false;
         }
     }
 }
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 012b250..ee1e01c 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
@@ -39,6 +39,7 @@
 import org.onosproject.incubator.net.virtual.VirtualPort;
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.DeviceId;
+import org.onosproject.net.Link;
 import org.onosproject.net.Port;
 import org.onosproject.net.PortNumber;
 import org.onosproject.net.provider.AbstractListenerProviderRegistry;
@@ -140,7 +141,7 @@
         checkNotNull(networkId, NETWORK_NULL);
         checkNotNull(src, LINK_POINT_NULL);
         checkNotNull(dst, LINK_POINT_NULL);
-        VirtualLink virtualLink = store.addLink(networkId, src, dst, null);
+        VirtualLink virtualLink = store.addLink(networkId, src, dst, Link.State.INACTIVE, null);
         checkNotNull(virtualLink, VIRTUAL_LINK_NULL);
 
         if (virtualLink.providerId() != null) {
@@ -148,7 +149,7 @@
             if (provider != null) {
                 TunnelId tunnelId = provider.createTunnel(networkId, mapVirtualToPhysicalPort(networkId, src),
                                                           mapVirtualToPhysicalPort(networkId, dst));
-                store.updateLink(virtualLink, tunnelId);
+                store.updateLink(virtualLink, tunnelId, Link.State.INACTIVE);
             }
         }
         return virtualLink;
@@ -173,6 +174,25 @@
         return null;
     }
 
+    /**
+     * Maps the physical connect point to a virtual connect point.
+     *
+     * @param networkId network identifier
+     * @param physicalCp physical connect point
+     * @return virtual connect point
+     */
+    private ConnectPoint mapPhysicalToVirtualToPort(NetworkId networkId,
+                                                  ConnectPoint physicalCp) {
+        Set<VirtualPort> ports = store.getPorts(networkId, null);
+        for (VirtualPort port : ports) {
+            if (port.realizedBy().element().id().equals(physicalCp.elementId()) &&
+                    port.realizedBy().number().equals(physicalCp.port())) {
+                return new ConnectPoint(port.element().id(), port.number());
+            }
+        }
+        return null;
+    }
+
     @Override
     public void removeVirtualLink(NetworkId networkId, ConnectPoint src, ConnectPoint dst) {
         checkNotNull(networkId, NETWORK_NULL);
@@ -226,7 +246,6 @@
     @Override
     public Set<VirtualPort> getVirtualPorts(NetworkId networkId, DeviceId deviceId) {
         checkNotNull(networkId, NETWORK_NULL);
-        checkNotNull(deviceId, DEVICE_NULL);
         return store.getPorts(networkId, deviceId);
     }
 
@@ -251,13 +270,32 @@
         }
 
         @Override
-        public void tunnelUp(NetworkId networkId, ConnectPoint src, ConnectPoint dst) {
+        public void tunnelUp(NetworkId networkId, ConnectPoint src, ConnectPoint dst, TunnelId tunnelId) {
 
+            ConnectPoint srcVirtualCp = mapPhysicalToVirtualToPort(networkId, src);
+            ConnectPoint dstVirtualCp = mapPhysicalToVirtualToPort(networkId, dst);
+            if ((srcVirtualCp == null) || (dstVirtualCp == null)) {
+                log.error("Src or dst virtual connection point was not found.");
+            }
+
+            VirtualLink virtualLink = store.getLink(networkId, srcVirtualCp, dstVirtualCp);
+            if (virtualLink != null) {
+                store.updateLink(virtualLink, tunnelId, Link.State.ACTIVE);
+            }
         }
 
         @Override
-        public void tunnelDown(NetworkId networkId, ConnectPoint src, ConnectPoint dst) {
+        public void tunnelDown(NetworkId networkId, ConnectPoint src, ConnectPoint dst, TunnelId tunnelId) {
+            ConnectPoint srcVirtualCp = mapPhysicalToVirtualToPort(networkId, src);
+            ConnectPoint dstVirtualCp = mapPhysicalToVirtualToPort(networkId, dst);
+            if ((srcVirtualCp == null) || (dstVirtualCp == null)) {
+                log.error("Src or dst virtual connection point was not found.");
+            }
 
+            VirtualLink virtualLink = store.getLink(networkId, srcVirtualCp, dstVirtualCp);
+            if (virtualLink != null) {
+                store.updateLink(virtualLink, tunnelId, Link.State.INACTIVE);
+            }
         }
     }
 
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 4bc96d5..15dcaf8 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
@@ -16,7 +16,7 @@
 
 package org.onosproject.incubator.net.virtual.impl;
 
-import com.google.common.collect.Sets;
+import com.google.common.collect.Lists;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -33,21 +33,29 @@
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.PortNumber;
+import org.onosproject.net.intent.FakeIntentManager;
+import org.onosproject.net.intent.FlowRuleIntent;
 import org.onosproject.net.intent.Intent;
-import org.onosproject.net.intent.IntentService;
-import org.onosproject.net.intent.IntentServiceAdapter;
-import org.onosproject.net.intent.IntentState;
-import org.onosproject.net.intent.Key;
+import org.onosproject.net.intent.IntentCompiler;
+import org.onosproject.net.intent.IntentEvent;
+import org.onosproject.net.intent.IntentExtensionService;
+import org.onosproject.net.intent.IntentListener;
+import org.onosproject.net.intent.IntentTestsMocks;
 import org.onosproject.net.intent.MockIdGenerator;
+import org.onosproject.net.intent.PointToPointIntent;
+import org.onosproject.net.intent.TestableIntentService;
 import org.onosproject.net.provider.AbstractProviderService;
 import org.onosproject.net.provider.ProviderId;
 
+import java.util.Collections;
+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.easymock.EasyMock.*;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.*;
 
 /**
  * Junit tests for PtToPtIntentVirtualNetworkProvider.
@@ -58,12 +66,19 @@
     private VirtualNetworkProviderRegistry providerRegistry;
 
     private final VirtualNetworkRegistryAdapter virtualNetworkRegistry = new VirtualNetworkRegistryAdapter();
-    private IntentService intentService;
+    private TestableIntentService intentService = new FakeIntentManager();
+    private TestListener listener = new TestListener();
+    protected TestIntentCompiler compiler = new TestIntentCompiler();
+    private IntentExtensionService intentExtensionService;
 
     private static final ApplicationId APP_ID =
             TestApplicationId.create(PtToPtIntentVirtualNetworkProvider.PTPT_INTENT_APPID);
 
     private IdGenerator idGenerator = new MockIdGenerator();
+    private static final int MAX_WAIT_TIME = 5;
+    private static final int MAX_PERMITS = 2;
+    private static Semaphore created;
+    private static Semaphore removed;
 
     @Before
     public void setUp() {
@@ -77,18 +92,28 @@
         Intent.unbindIdGenerator(idGenerator);
         Intent.bindIdGenerator(idGenerator);
 
-        intentService = new TestIntentService();
+        intentService.addListener(listener);
         provider.intentService = intentService;
+
+        // Register a compiler and an installer both setup for success.
+        intentExtensionService = intentService;
+        intentExtensionService.registerCompiler(PointToPointIntent.class, compiler);
+
         provider.activate();
+        created = new Semaphore(0, true);
+        removed = new Semaphore(0, true);
     }
 
     @After
     public void tearDown() {
+        Intent.unbindIdGenerator(idGenerator);
+        intentService.removeListener(listener);
         provider.deactivate();
         provider.providerRegistry = null;
         provider.coreService = null;
         provider.intentService = null;
-        Intent.unbindIdGenerator(idGenerator);
+        created = null;
+        removed = null;
     }
 
     @Test
@@ -134,11 +159,30 @@
         ConnectPoint dst = new ConnectPoint(DeviceId.deviceId("device2"), PortNumber.portNumber(2));
 
         TunnelId tunnelId = provider.createTunnel(networkId, src, dst);
+
+        // Wait for the tunnel to go into an INSTALLED state, and that the tunnelUp method was called.
+        try {
+            if (!created.tryAcquire(MAX_PERMITS, MAX_WAIT_TIME, TimeUnit.SECONDS)) {
+                fail("Failed to wait for tunnel to get installed.");
+            }
+        } catch (InterruptedException e) {
+            fail("Semaphore exception during tunnel installation." + e.getMessage());
+        }
+
         String key = String.format(PtToPtIntentVirtualNetworkProvider.KEY_FORMAT,
                                    networkId.toString(), src.toString(), dst.toString());
 
         assertEquals("TunnelId does not match as expected.", key, tunnelId.toString());
         provider.destroyTunnel(networkId, tunnelId);
+
+        // Wait for the tunnel to go into a WITHDRAWN state, and that the tunnelDown method was called.
+        try {
+            if (!removed.tryAcquire(MAX_PERMITS, MAX_WAIT_TIME, TimeUnit.SECONDS)) {
+                fail("Failed to wait for tunnel to get removed.");
+            }
+        } catch (InterruptedException e) {
+            fail("Semaphore exception during tunnel removal." + e.getMessage());
+        }
     }
 
     /**
@@ -176,16 +220,36 @@
         }
 
         @Override
-        public void tunnelUp(NetworkId networkId, ConnectPoint src, ConnectPoint dst) {
-
+        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();
         }
 
         @Override
-        public void tunnelDown(NetworkId networkId, ConnectPoint src, ConnectPoint dst) {
-
+        public void tunnelDown(NetworkId networkId, ConnectPoint src, ConnectPoint dst, TunnelId tunnelId) {
+            // Release one permit on the removed semaphore since the tunnelDown method was called.
+            removed.release();
         }
     }
 
+    private static class TestListener implements IntentListener {
+
+        @Override
+        public void event(IntentEvent event) {
+            switch (event.type()) {
+                case INSTALLED:
+                    // Release one permit on the created semaphore since the Intent event was received.
+                    created.release();
+                    break;
+                case WITHDRAWN:
+                    // Release one permit on the removed semaphore since the Intent event was received.
+                    removed.release();
+                    break;
+                default:
+                    break;
+            }
+        }
+    }
 
     /**
      * Core service test class.
@@ -205,55 +269,17 @@
         }
     }
 
-    /**
-     * Represents a fake IntentService class that easily allows to store and
-     * retrieve intents without implementing the IntentService logic.
-     */
-    private class TestIntentService extends IntentServiceAdapter {
-
-        private Set<Intent> intents;
-
-        public TestIntentService() {
-            intents = Sets.newHashSet();
-        }
-
+    private static class TestIntentCompiler implements IntentCompiler<PointToPointIntent> {
         @Override
-        public void submit(Intent intent) {
-            intents.add(intent);
+        public List<Intent> compile(PointToPointIntent intent, List<Intent> installable) {
+            return Lists.newArrayList(new MockInstallableIntent());
         }
+    }
 
-        @Override
-        public void withdraw(Intent intent) {
-        }
+    private static class MockInstallableIntent extends FlowRuleIntent {
 
-        @Override
-        public IntentState getIntentState(Key intentKey) {
-            return IntentState.WITHDRAWN;
-        }
-
-        @Override
-        public void purge(Intent intent) {
-            intents.remove(intent);
-        }
-
-        @Override
-        public long getIntentCount() {
-            return intents.size();
-        }
-
-        @Override
-        public Iterable<Intent> getIntents() {
-            return intents;
-        }
-
-        @Override
-        public Intent getIntent(Key intentKey) {
-            for (Intent intent : intents) {
-                if (intent.key().equals(intentKey)) {
-                    return intent;
-                }
-            }
-            return null;
+        public MockInstallableIntent() {
+            super(APP_ID, Collections.singletonList(new IntentTestsMocks.MockFlowRule(100)), Collections.emptyList());
         }
     }
 }
diff --git a/incubator/store/src/main/java/org/onosproject/incubator/store/virtual/impl/DistributedVirtualNetworkStore.java b/incubator/store/src/main/java/org/onosproject/incubator/store/virtual/impl/DistributedVirtualNetworkStore.java
index 800ecfd..7ceeb1b 100644
--- a/incubator/store/src/main/java/org/onosproject/incubator/store/virtual/impl/DistributedVirtualNetworkStore.java
+++ b/incubator/store/src/main/java/org/onosproject/incubator/store/virtual/impl/DistributedVirtualNetworkStore.java
@@ -44,6 +44,7 @@
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.Device;
 import org.onosproject.net.DeviceId;
+import org.onosproject.net.Link;
 import org.onosproject.net.Port;
 import org.onosproject.net.PortNumber;
 import org.onosproject.store.AbstractStore;
@@ -295,6 +296,7 @@
      * @return true if the network identifier exists, false otherwise.
      */
     private boolean networkExists(NetworkId networkId) {
+        checkNotNull(networkId, "The network identifier cannot be null.");
         return (networkIdVirtualNetworkMap.containsKey(networkId));
     }
 
@@ -339,7 +341,8 @@
     }
 
     @Override
-    public VirtualLink addLink(NetworkId networkId, ConnectPoint src, ConnectPoint dst, TunnelId realizedBy) {
+    public VirtualLink addLink(NetworkId networkId, ConnectPoint src, ConnectPoint dst,
+                               Link.State state, TunnelId realizedBy) {
         checkState(networkExists(networkId), "The network has not been added.");
         Set<VirtualLink> virtualLinkSet = networkIdVirtualLinkSetMap.get(networkId);
         if (virtualLinkSet == null) {
@@ -352,6 +355,7 @@
                 .networkId(networkId)
                 .src(src)
                 .dst(dst)
+                .state(state)
                 .tunnelId(realizedBy)
                 .build();
 
@@ -361,7 +365,7 @@
     }
 
     @Override
-    public void updateLink(VirtualLink virtualLink, TunnelId tunnelId) {
+    public void updateLink(VirtualLink virtualLink, TunnelId tunnelId, Link.State state) {
         checkState(networkExists(virtualLink.networkId()), "The network has not been added.");
         Set<VirtualLink> virtualLinkSet = networkIdVirtualLinkSetMap.get(virtualLink.networkId());
         if (virtualLinkSet == null) {
@@ -374,6 +378,7 @@
                 .src(virtualLink.src())
                 .dst(virtualLink.dst())
                 .tunnelId(tunnelId)
+                .state(state)
                 .build();
 
         virtualLinkSet.add(newVirtualLink);
@@ -471,16 +476,8 @@
         return ImmutableSet.copyOf(virtualLinkSet);
     }
 
-    /**
-     * Returns the virtual link matching the network identifier, source connect point,
-     * and destination connect point.
-     *
-     * @param networkId network identifier
-     * @param src       source connect point
-     * @param dst       destination connect point
-     * @return virtual link
-     */
-    private VirtualLink getLink(NetworkId networkId, ConnectPoint src, ConnectPoint dst) {
+    @Override
+    public VirtualLink getLink(NetworkId networkId, ConnectPoint src, ConnectPoint dst) {
         Set<VirtualLink> virtualLinkSet = networkIdVirtualLinkSetMap.get(networkId);
         if (virtualLinkSet == null) {
             return null;
@@ -504,6 +501,10 @@
             virtualPortSet = new HashSet<>();
         }
 
+        if (deviceId == null) {
+            return ImmutableSet.copyOf(virtualPortSet);
+        }
+
         Set<VirtualPort> portSet = new HashSet<>();
         virtualPortSet.forEach(virtualPort -> {
             if (virtualPort.element().id().equals(deviceId)) {