Merge remote-tracking branch 'origin/master'
diff --git a/apps/optical/src/main/java/org/onlab/onos/optical/cfg/OpticalConfigProvider.java b/apps/optical/src/main/java/org/onlab/onos/optical/cfg/OpticalConfigProvider.java
index b2e273f..cfdeb1f 100644
--- a/apps/optical/src/main/java/org/onlab/onos/optical/cfg/OpticalConfigProvider.java
+++ b/apps/optical/src/main/java/org/onlab/onos/optical/cfg/OpticalConfigProvider.java
@@ -284,7 +284,7 @@
             DefaultLinkDescription linkDescription =
                     new DefaultLinkDescription(srcPoint,
                                                  snkPoint,
-                                                 Link.Type.DIRECT,
+                                                 Link.Type.OPTICAL,
                                                  extendedAttributes);
 
             linkProviderService.linkDetected(linkDescription);
@@ -315,7 +315,7 @@
             DefaultLinkDescription linkDescription =
                     new DefaultLinkDescription(srcPoint,
                                                  snkPoint,
-                                                 Link.Type.DIRECT,
+                                                 Link.Type.OPTICAL,
                                                  extendedAttributes);
 
             linkProviderService.linkDetected(linkDescription);
diff --git a/apps/optical/src/main/resources/demo-3-roadm-2-ps.json b/apps/optical/src/main/resources/demo-3-roadm-2-ps.json
index 6f2c2f5..20b7db2 100644
--- a/apps/optical/src/main/resources/demo-3-roadm-2-ps.json
+++ b/apps/optical/src/main/resources/demo-3-roadm-2-ps.json
@@ -1,5 +1,5 @@
 {
-        "opticalSwitches": [
+    "opticalSwitches": [
         {
             "allowed": true,
             "latitude": 37.6,
@@ -12,7 +12,7 @@
             "type": "Roadm"
         },
 
-	{
+        {
             "allowed": true,
             "latitude": 37.3,
             "longitude": 121.9,
@@ -22,9 +22,9 @@
                 "numRegen": 0
             },
             "type": "Roadm"
-         },
+        },
 
- 	{
+        {
             "allowed": true,
             "latitude": 33.9,
             "longitude": 118.4,
@@ -34,10 +34,10 @@
                 "numRegen": 2
             },
             "type": "Roadm"
-         }
+        }
     ],
 
-        "opticalLinks": [
+    "opticalLinks": [
         {
             "allowed": true,
             "nodeDpid1": "00:00:ff:ff:ff:ff:ff:01",
@@ -51,10 +51,38 @@
                 "port2": 30
             },
             "type": "wdmLink"
-         },
-       
-       {
-	"allowed": true,
+        },
+        {
+            "allowed": true,
+            "nodeDpid1": "00:00:ff:ff:ff:ff:ff:03",
+            "nodeDpid2": "00:00:ff:ff:ff:ff:ff:01",
+            "params": {
+                "distKms": 1000,
+                "nodeName1": "ROADM3",
+                "nodeName2": "ROADM1",
+                "numWaves": 80,
+                "port1": 30,
+                "port2": 10
+            },
+            "type": "wdmLink"
+        },
+
+        {
+            "allowed": true,
+            "nodeDpid1": "00:00:ff:ff:ff:ff:ff:02",
+            "nodeDpid2": "00:00:ff:ff:ff:ff:ff:03",
+            "params": {
+                "distKms": 2000,
+                "nodeName1": "ROADM2",
+                "nodeName2": "ROADM3",
+                "numWaves": 80,
+                "port1": 20,
+                "port2": 31
+            },
+            "type": "wdmLink"
+        },
+        {
+            "allowed": true,
             "nodeDpid1": "00:00:ff:ff:ff:ff:ff:03",
             "nodeDpid2": "00:00:ff:ff:ff:ff:ff:02",
             "params": {
@@ -66,10 +94,9 @@
                 "port2": 20
             },
             "type": "wdmLink"
-       },
+        },
 
-   
-      {
+        {
             "allowed": true,
             "nodeDpid1": "00:00:ff:ff:ff:ff:00:01",
             "nodeDpid2": "00:00:ff:ff:ff:ff:ff:01",
@@ -82,8 +109,21 @@
             },
             "type": "pktOptLink"
         },
+        {
+            "allowed": true,
+            "nodeDpid1": "00:00:ff:ff:ff:ff:ff:01",
+            "nodeDpid2": "00:00:ff:ff:ff:ff:00:01",
+            "params": {
+                "nodeName1": "ROADM1",
+                "nodeName2": "ROUTER1",
+                "bandWidth": 100000,
+                "port1": 11,
+                "port2": 10
+            },
+            "type": "pktOptLink"
+        },
 
-       {
+        {
             "allowed": true,
             "nodeDpid1": "00:00:ff:ff:ff:ff:00:02",
             "nodeDpid2": "00:00:ff:ff:ff:ff:ff:02",
@@ -95,7 +135,20 @@
                 "port2": 21
             },
             "type": "pktOptLink"
-        } 
+        },
+        {
+            "allowed": true,
+            "nodeDpid1": "00:00:ff:ff:ff:ff:ff:02",
+            "nodeDpid2": "00:00:ff:ff:ff:ff:00:02",
+            "params": {
+                "nodeName1": "ROADM2",
+                "nodeName2": "ROUTER2",
+                "bandWidth": 100000,
+                "port1": 21,
+                "port2": 10
+            },
+            "type": "pktOptLink"
+        }
 
     ]
 }
diff --git a/cli/src/main/java/org/onlab/onos/cli/net/TopologyCommand.java b/cli/src/main/java/org/onlab/onos/cli/net/TopologyCommand.java
index ce3dc59..903b352 100644
--- a/cli/src/main/java/org/onlab/onos/cli/net/TopologyCommand.java
+++ b/cli/src/main/java/org/onlab/onos/cli/net/TopologyCommand.java
@@ -20,8 +20,10 @@
 
 import com.fasterxml.jackson.databind.ObjectMapper;
 import org.apache.karaf.shell.commands.Command;
+import org.apache.karaf.shell.commands.Option;
 import org.onlab.onos.cli.AbstractShellCommand;
 import org.onlab.onos.net.topology.Topology;
+import org.onlab.onos.net.topology.TopologyProvider;
 import org.onlab.onos.net.topology.TopologyService;
 
 /**
@@ -35,6 +37,10 @@
     private static final String FMT =
             "time=%s, devices=%d, links=%d, clusters=%d, paths=%d";
 
+    @Option(name = "-r", aliases = "--recompute", description = "Trigger topology re-computation",
+            required = false, multiValued = false)
+    private boolean recompute = false;
+
     protected TopologyService service;
     protected Topology topology;
 
@@ -49,7 +55,10 @@
     @Override
     protected void execute() {
         init();
-        if (outputJson()) {
+        if (recompute) {
+            get(TopologyProvider.class).triggerRecompute();
+
+        } else if (outputJson()) {
             print("%s", new ObjectMapper().createObjectNode()
                     .put("time", topology.time())
                     .put("deviceCount", topology.deviceCount())
diff --git a/core/api/src/main/java/org/onlab/onos/net/Link.java b/core/api/src/main/java/org/onlab/onos/net/Link.java
index 1ae5b9d..3e23dc1 100644
--- a/core/api/src/main/java/org/onlab/onos/net/Link.java
+++ b/core/api/src/main/java/org/onlab/onos/net/Link.java
@@ -25,7 +25,18 @@
         /**
          * Signifies that this link is an edge, i.e. host link.
          */
-        EDGE
+        EDGE,
+
+        /**
+         * Signifies that this link represents a logical link backed by
+         * some form of a tunnel.
+         */
+        TUNNEL,
+
+        /**
+         * Signifies that this link is realized by optical connection.
+         */
+        OPTICAL
     }
 
     /**
@@ -49,6 +60,4 @@
      */
     Type type();
 
-    // LinkInfo info(); // Additional link information / decorations
-
 }
diff --git a/core/api/src/main/java/org/onlab/onos/net/topology/TopologyProvider.java b/core/api/src/main/java/org/onlab/onos/net/topology/TopologyProvider.java
index 7ad8cc0..312e154 100644
--- a/core/api/src/main/java/org/onlab/onos/net/topology/TopologyProvider.java
+++ b/core/api/src/main/java/org/onlab/onos/net/topology/TopologyProvider.java
@@ -7,4 +7,9 @@
  */
 public interface TopologyProvider extends Provider {
 
+    /**
+     * Triggers topology recomputation.
+     */
+    void triggerRecompute();
+
 }
diff --git a/core/net/src/main/java/org/onlab/onos/net/link/impl/LinkManager.java b/core/net/src/main/java/org/onlab/onos/net/link/impl/LinkManager.java
index 835c47e..e59eb9f 100644
--- a/core/net/src/main/java/org/onlab/onos/net/link/impl/LinkManager.java
+++ b/core/net/src/main/java/org/onlab/onos/net/link/impl/LinkManager.java
@@ -208,7 +208,7 @@
             LinkEvent event = store.createOrUpdateLink(provider().id(),
                                                        linkDescription);
             if (event != null) {
-                log.debug("Link {} detected", linkDescription);
+                log.info("Link {} detected", linkDescription);
                 post(event);
             }
         }
diff --git a/core/net/src/main/java/org/onlab/onos/net/topology/impl/DefaultTopologyProvider.java b/core/net/src/main/java/org/onlab/onos/net/topology/impl/DefaultTopologyProvider.java
index 9631c66..0efd08b 100644
--- a/core/net/src/main/java/org/onlab/onos/net/topology/impl/DefaultTopologyProvider.java
+++ b/core/net/src/main/java/org/onlab/onos/net/topology/impl/DefaultTopologyProvider.java
@@ -5,6 +5,7 @@
 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.onlab.onos.event.AbstractEventAccumulator;
 import org.onlab.onos.event.Event;
 import org.onlab.onos.event.EventAccumulator;
@@ -39,6 +40,7 @@
  * new topology snapshots.
  */
 @Component(immediate = true)
+@Service
 public class DefaultTopologyProvider extends AbstractProvider
         implements TopologyProvider {
 
@@ -89,7 +91,7 @@
         linkService.addListener(linkListener);
 
         isStarted = true;
-        triggerTopologyBuild(Collections.<Event>emptyList());
+        triggerRecompute();
         log.info("Started");
     }
 
@@ -108,6 +110,11 @@
         log.info("Stopped");
     }
 
+    @Override
+    public void triggerRecompute() {
+        triggerTopologyBuild(Collections.<Event>emptyList());
+    }
+
     /**
      * Triggers assembly of topology data citing the specified events as the
      * reason.
@@ -177,7 +184,11 @@
 
         @Override
         public void run() {
-            buildTopology(reasons);
+            try {
+                buildTopology(reasons);
+            } catch (Exception e) {
+                log.warn("Unable to compute topology due to: {}", e.getMessage());
+            }
         }
     }
 
diff --git a/core/net/src/test/java/org/onlab/onos/net/topology/impl/TopologyManagerTest.java b/core/net/src/test/java/org/onlab/onos/net/topology/impl/TopologyManagerTest.java
index d369073..ada7e78 100644
--- a/core/net/src/test/java/org/onlab/onos/net/topology/impl/TopologyManagerTest.java
+++ b/core/net/src/test/java/org/onlab/onos/net/topology/impl/TopologyManagerTest.java
@@ -195,6 +195,10 @@
         public TestProvider() {
             super(PID);
         }
+
+        @Override
+        public void triggerRecompute() {
+        }
     }
 
     private static class TestListener implements TopologyListener {
diff --git a/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleLinkStore.java b/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleLinkStore.java
index 5c87921..09d6a62 100644
--- a/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleLinkStore.java
+++ b/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleLinkStore.java
@@ -192,14 +192,6 @@
     // Creates and stores the link and returns the appropriate event.
     // Guarded by linkDescs value (=locking each Link)
     private LinkEvent createLink(LinkKey key, Link newLink) {
-
-        if (newLink.providerId().isAncillary()) {
-            // TODO: revisit ancillary only Link handling
-
-            // currently treating ancillary only as down (not visible outside)
-            return null;
-        }
-
         links.put(key, newLink);
         srcLinks.put(newLink.src().deviceId(), key);
         dstLinks.put(newLink.dst().deviceId(), key);
@@ -209,10 +201,8 @@
     // Updates, if necessary the specified link and returns the appropriate event.
     // Guarded by linkDescs value (=locking each Link)
     private LinkEvent updateLink(LinkKey key, Link oldLink, Link newLink) {
-
         if (newLink.providerId().isAncillary()) {
             // TODO: revisit ancillary only Link handling
-
             // currently treating ancillary only as down (not visible outside)
             return null;
         }
diff --git a/core/store/trivial/src/test/java/org/onlab/onos/store/trivial/impl/SimpleLinkStoreTest.java b/core/store/trivial/src/test/java/org/onlab/onos/store/trivial/impl/SimpleLinkStoreTest.java
index 02cb411..a7f40ac 100644
--- a/core/store/trivial/src/test/java/org/onlab/onos/store/trivial/impl/SimpleLinkStoreTest.java
+++ b/core/store/trivial/src/test/java/org/onlab/onos/store/trivial/impl/SimpleLinkStoreTest.java
@@ -1,18 +1,6 @@
 package org.onlab.onos.store.trivial.impl;
 
-import static org.junit.Assert.*;
-import static org.onlab.onos.net.DeviceId.deviceId;
-import static org.onlab.onos.net.Link.Type.*;
-import static org.onlab.onos.net.link.LinkEvent.Type.*;
-import static org.onlab.onos.store.trivial.impl.SimpleDeviceStoreTest.assertAnnotationsEquals;
-
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
+import com.google.common.collect.Iterables;
 import org.junit.After;
 import org.junit.AfterClass;
 import org.junit.Before;
@@ -23,17 +11,27 @@
 import org.onlab.onos.net.DefaultAnnotations;
 import org.onlab.onos.net.DeviceId;
 import org.onlab.onos.net.Link;
+import org.onlab.onos.net.Link.Type;
 import org.onlab.onos.net.LinkKey;
 import org.onlab.onos.net.PortNumber;
 import org.onlab.onos.net.SparseAnnotations;
-import org.onlab.onos.net.Link.Type;
 import org.onlab.onos.net.link.DefaultLinkDescription;
 import org.onlab.onos.net.link.LinkEvent;
 import org.onlab.onos.net.link.LinkStore;
 import org.onlab.onos.net.link.LinkStoreDelegate;
 import org.onlab.onos.net.provider.ProviderId;
 
-import com.google.common.collect.Iterables;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import static org.junit.Assert.*;
+import static org.onlab.onos.net.DeviceId.deviceId;
+import static org.onlab.onos.net.Link.Type.*;
+import static org.onlab.onos.net.link.LinkEvent.Type.*;
+import static org.onlab.onos.store.trivial.impl.SimpleDeviceStoreTest.assertAnnotationsEquals;
 
 /**
  * Test of the simple LinkStore implementation.
@@ -301,7 +299,7 @@
         LinkEvent event = linkStore.createOrUpdateLink(PIDA,
                     new DefaultLinkDescription(src, dst, INDIRECT, A1));
 
-        assertNull("Ancillary only link is ignored", event);
+        assertNotNull("Ancillary only link is ignored", event);
 
         // add Primary link
         LinkEvent event2 = linkStore.createOrUpdateLink(PID,
@@ -309,7 +307,7 @@
 
         assertLink(DID1, P1, DID2, P2, INDIRECT, event2.subject());
         assertAnnotationsEquals(event2.subject().annotations(), A2, A1);
-        assertEquals(LINK_ADDED, event2.type());
+        assertEquals(LINK_UPDATED, event2.type());
 
         // update link type
         LinkEvent event3 = linkStore.createOrUpdateLink(PID,
@@ -375,7 +373,7 @@
     }
 
     @Test
-    public final void testAncillaryOnlyNotVisible() {
+    public final void testAncillaryVisible() {
         ConnectPoint src = new ConnectPoint(DID1, P1);
         ConnectPoint dst = new ConnectPoint(DID2, P2);
 
@@ -384,18 +382,8 @@
                     new DefaultLinkDescription(src, dst, INDIRECT, A1));
 
         // Ancillary only link should not be visible
-        assertEquals(0, linkStore.getLinkCount());
-
-        assertTrue(Iterables.isEmpty(linkStore.getLinks()));
-
-        assertNull(linkStore.getLink(src, dst));
-
-        assertEquals(Collections.emptySet(), linkStore.getIngressLinks(dst));
-
-        assertEquals(Collections.emptySet(), linkStore.getEgressLinks(src));
-
-        assertEquals(Collections.emptySet(), linkStore.getDeviceEgressLinks(DID1));
-        assertEquals(Collections.emptySet(), linkStore.getDeviceIngressLinks(DID2));
+        assertEquals(1, linkStore.getLinkCount());
+        assertNotNull(linkStore.getLink(src, dst));
     }
 
     // If Delegates should be called only on remote events,