Adding support for link state to GUI.

Change-Id: I6476cf04810f7bc7e88af629a36ce671b42748d0
diff --git a/cli/src/main/java/org/onlab/onos/cli/net/DevicesListCommand.java b/cli/src/main/java/org/onlab/onos/cli/net/DevicesListCommand.java
index ca946d0..f981411 100644
--- a/cli/src/main/java/org/onlab/onos/cli/net/DevicesListCommand.java
+++ b/cli/src/main/java/org/onlab/onos/cli/net/DevicesListCommand.java
@@ -82,6 +82,7 @@
         if (device != null) {
             result.put("id", device.id().toString())
                     .put("available", service.isAvailable(device.id()))
+                    .put("type", device.type().toString())
                     .put("role", service.getRole(device.id()).toString())
                     .put("mfr", device.manufacturer())
                     .put("hw", device.hwVersion())
diff --git a/cli/src/main/java/org/onlab/onos/cli/net/LinksListCommand.java b/cli/src/main/java/org/onlab/onos/cli/net/LinksListCommand.java
index 24f5c7b..7c32129 100644
--- a/cli/src/main/java/org/onlab/onos/cli/net/LinksListCommand.java
+++ b/cli/src/main/java/org/onlab/onos/cli/net/LinksListCommand.java
@@ -35,7 +35,7 @@
          description = "Lists all infrastructure links")
 public class LinksListCommand extends AbstractShellCommand {
 
-    private static final String FMT = "src=%s/%s, dst=%s/%s, type=%s%s";
+    private static final String FMT = "src=%s/%s, dst=%s/%s, type=%s, state=%s%s";
     private static final String COMPACT = "%s/%s-%s/%s";
 
     @Argument(index = 0, name = "uri", description = "Device ID",
@@ -82,6 +82,8 @@
         ObjectNode result = mapper.createObjectNode();
         result.set("src", json(mapper, link.src()));
         result.set("dst", json(mapper, link.dst()));
+        result.put("type", link.type().toString());
+        result.put("state", link.state().toString());
         result.set("annotations", annotations(mapper, link.annotations()));
         return result;
     }
@@ -107,7 +109,8 @@
      */
     public static String linkString(Link link) {
         return String.format(FMT, link.src().deviceId(), link.src().port(),
-                             link.dst().deviceId(), link.dst().port(), link.type(),
+                             link.dst().deviceId(), link.dst().port(),
+                             link.type(), link.state(),
                              annotations(link.annotations()));
     }
 
diff --git a/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleHostStore.java b/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleHostStore.java
index 2f8fbd2..ba1cafb 100644
--- a/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleHostStore.java
+++ b/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleHostStore.java
@@ -15,6 +15,7 @@
  */
 package org.onlab.onos.store.trivial.impl;
 
+import static org.onlab.onos.net.DefaultAnnotations.merge;
 import static org.onlab.onos.net.host.HostEvent.Type.HOST_ADDED;
 import static org.onlab.onos.net.host.HostEvent.Type.HOST_MOVED;
 import static org.onlab.onos.net.host.HostEvent.Type.HOST_REMOVED;
@@ -33,6 +34,7 @@
 import org.apache.felix.scr.annotations.Service;
 import org.onlab.onos.net.Annotations;
 import org.onlab.onos.net.ConnectPoint;
+import org.onlab.onos.net.DefaultAnnotations;
 import org.onlab.onos.net.DefaultHost;
 import org.onlab.onos.net.DeviceId;
 import org.onlab.onos.net.Host;
@@ -106,7 +108,8 @@
                                             descr.hwAddress(),
                                             descr.vlan(),
                                             descr.location(),
-                                            ImmutableSet.copyOf(descr.ipAddress()));
+                                            ImmutableSet.copyOf(descr.ipAddress()),
+                                            descr.annotations());
         synchronized (this) {
             hosts.put(hostId, newhost);
             locations.put(descr.location(), newhost);
@@ -123,15 +126,19 @@
             return new HostEvent(HOST_MOVED, host);
         }
 
-        if (host.ipAddresses().containsAll(descr.ipAddress())) {
+        if (host.ipAddresses().containsAll(descr.ipAddress()) &&
+                descr.annotations().keys().isEmpty()) {
             return null;
         }
 
         Set<IpAddress> addresses = new HashSet<>(host.ipAddresses());
         addresses.addAll(descr.ipAddress());
+        Annotations annotations = merge((DefaultAnnotations) host.annotations(),
+                                        descr.annotations());
         StoredHost updated = new StoredHost(providerId, host.id(),
                                             host.mac(), host.vlan(),
-                                            descr.location(), addresses);
+                                            descr.location(), addresses,
+                                            annotations);
         event = new HostEvent(HOST_UPDATED, updated);
         synchronized (this) {
             hosts.put(host.id(), updated);
diff --git a/tools/test/topos/att-mpls.json b/tools/test/topos/att-mpls.json
new file mode 100644
index 0000000..ccf9332
--- /dev/null
+++ b/tools/test/topos/att-mpls.json
@@ -0,0 +1,58 @@
+{
+  "devices": [
+    { "name": "CMBR", "alias":  "s1", "uri": "of:0000000000000001", "mac": "000000000001", "annotations": { "latitude": 42.373730, "longitude": -71.109734  }, "type": "SWITCH" },
+    { "name": "CHCG", "alias":  "s2", "uri": "of:0000000000000002", "mac": "000000000002", "annotations": { "latitude": 41.877461, "longitude": -87.642892  }, "type": "SWITCH" },
+    { "name": "CLEV", "alias":  "s3", "uri": "of:0000000000000003", "mac": "000000000003", "annotations": { "latitude": 41.498928, "longitude": -81.695217  }, "type": "SWITCH" },
+    { "name": "RLGH", "alias":  "s4", "uri": "of:0000000000000004", "mac": "000000000004", "annotations": { "latitude": 35.780150, "longitude": -78.644026  }, "type": "SWITCH" },
+    { "name": "ATLN", "alias":  "s5", "uri": "of:0000000000000005", "mac": "000000000005", "annotations": { "latitude": 33.749017, "longitude": -84.394168  }, "type": "SWITCH" },
+    { "name": "PHLA", "alias":  "s6", "uri": "of:0000000000000006", "mac": "000000000006", "annotations": { "latitude": 39.952906, "longitude": -75.172278  }, "type": "SWITCH" },
+    { "name": "WASH", "alias":  "s7", "uri": "of:0000000000000007", "mac": "000000000007", "annotations": { "latitude": 38.906696, "longitude": -77.035509  }, "type": "SWITCH" },
+    { "name": "NSVL", "alias":  "s8", "uri": "of:0000000000000008", "mac": "000000000008", "annotations": { "latitude": 36.166410, "longitude": -86.787305  }, "type": "SWITCH" },
+    { "name": "STLS", "alias":  "s9", "uri": "of:0000000000000009", "mac": "000000000009", "annotations": { "latitude": 38.626418, "longitude": -90.198143  }, "type": "SWITCH" },
+    { "name": "NWOR", "alias": "s10", "uri": "of:000000000000000a", "mac": "00000000000a", "annotations": { "Xlatitude": 0.0,      "Xlongitude": 0.00       }, "type": "SWITCH" },
+    { "name": "HSTN", "alias": "s11", "uri": "of:000000000000000b", "mac": "00000000000b", "annotations": { "latitude": 29.763249, "longitude": -95.368332  }, "type": "SWITCH" },
+    { "name": "SNAN", "alias": "s12", "uri": "of:000000000000000c", "mac": "00000000000c", "annotations": { "latitude": 29.424331, "longitude": -98.491745  }, "type": "SWITCH" },
+    { "name": "DLLS", "alias": "s13", "uri": "of:000000000000000d", "mac": "00000000000d", "annotations": { "latitude": 32.777665, "longitude": -96.802064  }, "type": "SWITCH" },
+    { "name": "ORLD", "alias": "s14", "uri": "of:000000000000000e", "mac": "00000000000e", "annotations": { "latitude": 28.538641, "longitude": -81.381110  }, "type": "SWITCH" },
+    { "name": "DNVR", "alias": "s15", "uri": "of:000000000000000f", "mac": "00000000000f", "annotations": { "latitude": 39.736623, "longitude": -104.984887 }, "type": "SWITCH" },
+    { "name": "KSCY", "alias": "s16", "uri": "of:0000000000000010", "mac": "000000000010", "annotations": { "latitude": 39.100725, "longitude": -94.581228  }, "type": "SWITCH" },
+    { "name": "SNFN", "alias": "s17", "uri": "of:0000000000000011", "mac": "000000000011", "annotations": { "latitude": 37.779751, "longitude": -122.409791 }, "type": "SWITCH" },
+    { "name": "SCRM", "alias": "s18", "uri": "of:0000000000000012", "mac": "000000000012", "annotations": { "latitude": 38.581001, "longitude": -121.497844 }, "type": "SWITCH" },
+    { "name": "PTLD", "alias": "s19", "uri": "of:0000000000000013", "mac": "000000000013", "annotations": { "latitude": 45.523317, "longitude": -122.677768 }, "type": "SWITCH" },
+    { "name": "STTL", "alias": "s20", "uri": "of:0000000000000014", "mac": "000000000014", "annotations": { "latitude": 47.607326, "longitude": -122.331786 }, "type": "SWITCH" },
+    { "name": "SLKC", "alias": "s21", "uri": "of:0000000000000015", "mac": "000000000015", "annotations": { "latitude": 40.759577, "longitude": -111.895079 }, "type": "SWITCH" },
+    { "name": "LA03", "alias": "s22", "uri": "of:0000000000000016", "mac": "000000000016", "annotations": { "latitude": 34.056346, "longitude": -118.235951 }, "type": "SWITCH" },
+    { "name": "SNDG", "alias": "s23", "uri": "of:0000000000000017", "mac": "000000000017", "annotations": { "latitude": 32.714564, "longitude": -117.153528 }, "type": "SWITCH" },
+    { "name": "PHNX", "alias": "s24", "uri": "of:0000000000000018", "mac": "000000000018", "annotations": { "latitude": 33.448289, "longitude": -112.076299 }, "type": "SWITCH" },
+    { "name": "NY54", "alias": "s25", "uri": "of:0000000000000019", "mac": "000000000019", "annotations": { "latitude": 40.728270, "longitude": -73.994483  }, "type": "SWITCH" }
+  ],
+
+  "hosts": [
+    { "name": "CMBR", "alias":  "h1", "mac": "00:00:00:00:00:01", "vlan": -1, "location": "of:0000000000000001/1", "ip": "10.0.0.1",  "annotations": { "latitude": 44.373730, "longitude": -71.109734  } },
+    { "name": "CHCG", "alias":  "h2", "mac": "00:00:00:00:00:02", "vlan": -1, "location": "of:0000000000000002/1", "ip": "10.0.0.2",  "annotations": { "latitude": 43.877461, "longitude": -87.642892  } },
+    { "name": "CLEV", "alias":  "h3", "mac": "00:00:00:00:00:03", "vlan": -1, "location": "of:0000000000000003/1", "ip": "10.0.0.3",  "annotations": { "latitude": 43.498928, "longitude": -81.695217  } },
+    { "name": "RLGH", "alias":  "h4", "mac": "00:00:00:00:00:04", "vlan": -1, "location": "of:0000000000000004/1", "ip": "10.0.0.4",  "annotations": { "latitude": 37.780150, "longitude": -78.644026  } },
+    { "name": "ATLN", "alias":  "h5", "mac": "00:00:00:00:00:05", "vlan": -1, "location": "of:0000000000000005/1", "ip": "10.0.0.5",  "annotations": { "latitude": 35.749017, "longitude": -84.394168  } },
+    { "name": "PHLA", "alias":  "h6", "mac": "00:00:00:00:00:06", "vlan": -1, "location": "of:0000000000000006/1", "ip": "10.0.0.6",  "annotations": { "latitude": 41.952906, "longitude": -75.172278  } },
+    { "name": "WASH", "alias":  "h7", "mac": "00:00:00:00:00:07", "vlan": -1, "location": "of:0000000000000007/1", "ip": "10.0.0.7",  "annotations": { "latitude": 40.906696, "longitude": -77.035509  } },
+    { "name": "NSVL", "alias":  "h8", "mac": "00:00:00:00:00:08", "vlan": -1, "location": "of:0000000000000008/1", "ip": "10.0.0.8",  "annotations": { "latitude": 38.166410, "longitude": -86.787305  } },
+    { "name": "STLS", "alias":  "h9", "mac": "00:00:00:00:00:09", "vlan": -1, "location": "of:0000000000000009/1", "ip": "10.0.0.9",  "annotations": { "latitude": 40.626418, "longitude": -90.198143  } },
+    { "name": "NWOR", "alias": "h10", "mac": "00:00:00:00:00:0a", "vlan": -1, "location": "of:000000000000000a/1", "ip": "10.0.0.10", "annotations": { "Xlatitude": 0.0,      "Xlongitude": 0.00       } },
+    { "name": "HSTN", "alias": "h11", "mac": "00:00:00:00:00:0b", "vlan": -1, "location": "of:000000000000000b/1", "ip": "10.0.0.11", "annotations": { "latitude": 31.763249, "longitude": -95.368332  } },
+    { "name": "SNAN", "alias": "h12", "mac": "00:00:00:00:00:0c", "vlan": -1, "location": "of:000000000000000c/1", "ip": "10.0.0.12", "annotations": { "latitude": 31.424331, "longitude": -98.491745  } },
+    { "name": "DLLS", "alias": "h13", "mac": "00:00:00:00:00:0d", "vlan": -1, "location": "of:000000000000000d/1", "ip": "10.0.0.13", "annotations": { "latitude": 34.777665, "longitude": -96.802064  } },
+    { "name": "ORLD", "alias": "h14", "mac": "00:00:00:00:00:0e", "vlan": -1, "location": "of:000000000000000e/1", "ip": "10.0.0.14", "annotations": { "latitude": 31.538641, "longitude": -81.381110  } },
+    { "name": "DNVR", "alias": "h15", "mac": "00:00:00:00:00:0f", "vlan": -1, "location": "of:000000000000000f/1", "ip": "10.0.0.15", "annotations": { "latitude": 41.736623, "longitude": -104.984887 } },
+    { "name": "KSCY", "alias": "h16", "mac": "00:00:00:00:00:10", "vlan": -1, "location": "of:0000000000000010/1", "ip": "10.0.0.16", "annotations": { "latitude": 41.100725, "longitude": -94.581228  } },
+    { "name": "SNFN", "alias": "h17", "mac": "00:00:00:00:00:11", "vlan": -1, "location": "of:0000000000000011/1", "ip": "10.0.0.17", "annotations": { "latitude": 39.779751, "longitude": -122.409791 } },
+    { "name": "SCRM", "alias": "h18", "mac": "00:00:00:00:00:12", "vlan": -1, "location": "of:0000000000000012/1", "ip": "10.0.0.18", "annotations": { "latitude": 40.581001, "longitude": -121.497844 } },
+    { "name": "PTLD", "alias": "h19", "mac": "00:00:00:00:00:13", "vlan": -1, "location": "of:0000000000000013/1", "ip": "10.0.0.19", "annotations": { "latitude": 47.523317, "longitude": -122.677768 } },
+    { "name": "STTL", "alias": "h20", "mac": "00:00:00:00:00:14", "vlan": -1, "location": "of:0000000000000014/1", "ip": "10.0.0.20", "annotations": { "latitude": 49.607326, "longitude": -122.331786 } },
+    { "name": "SLKC", "alias": "h21", "mac": "00:00:00:00:00:15", "vlan": -1, "location": "of:0000000000000015/1", "ip": "10.0.0.21", "annotations": { "latitude": 42.759577, "longitude": -111.895079 } },
+    { "name": "LA03", "alias": "h22", "mac": "00:00:00:00:00:16", "vlan": -1, "location": "of:0000000000000016/1", "ip": "10.0.0.22", "annotations": { "latitude": 36.056346, "longitude": -118.235951 } },
+    { "name": "SNDG", "alias": "h23", "mac": "00:00:00:00:00:17", "vlan": -1, "location": "of:0000000000000017/1", "ip": "10.0.0.23", "annotations": { "latitude": 34.714564, "longitude": -117.153528 } },
+    { "name": "PHNX", "alias": "h24", "mac": "00:00:00:00:00:18", "vlan": -1, "location": "of:0000000000000018/1", "ip": "10.0.0.24", "annotations": { "latitude": 35.448289, "longitude": -112.076299 } },
+    { "name": "NY54", "alias": "h25", "mac": "00:00:00:00:00:19", "vlan": -1, "location": "of:0000000000000019/1", "ip": "10.0.0.25", "annotations": { "latitude": 42.728270, "longitude": -73.994483  } }
+  ]
+
+}
diff --git a/tools/test/topos/att-mpls.py b/tools/test/topos/att-mpls.py
new file mode 100644
index 0000000..3620620
--- /dev/null
+++ b/tools/test/topos/att-mpls.py
@@ -0,0 +1,164 @@
+#!/usr/bin/env python
+
+"""
+"""
+from mininet.topo import Topo
+from mininet.net import Mininet
+from mininet.node import RemoteController
+from mininet.node import Node
+from mininet.node import CPULimitedHost
+from mininet.link import TCLink
+from mininet.cli import CLI
+from mininet.log import setLogLevel
+from mininet.util import dumpNodeConnections
+
+class AttMplsTopo( Topo ):
+    "Internet Topology Zoo Specimen."
+
+    def __init__( self ):
+        "Create a topology."
+
+        # Initialize Topology
+        Topo.__init__( self )
+
+        # add nodes, switches first...
+        NY54 = self.addSwitch( 's25' ) // 40.728270, -73.994483
+        CMBR = self.addSwitch( 's1' )  // 42.373730, -71.109734
+        CHCG = self.addSwitch( 's2' )  // 41.877461, -87.642892
+        CLEV = self.addSwitch( 's3' )  // 41.498928, -81.695217
+        RLGH = self.addSwitch( 's4' )  // 35.780150, -78.644026
+        ATLN = self.addSwitch( 's5' )  // 33.749017, -84.394168
+        PHLA = self.addSwitch( 's6' )  // 39.952906, -75.172278
+        WASH = self.addSwitch( 's7' )  // 38.906696, -77.035509
+        NSVL = self.addSwitch( 's8' )  // 36.166410, -86.787305
+        STLS = self.addSwitch( 's9' )  // 38.626418, -90.198143
+        NWOR = self.addSwitch( 's10' ) //
+        HSTN = self.addSwitch( 's11' ) // 29.763249, -95.368332
+        SNAN = self.addSwitch( 's12' ) // 29.424331, -98.491745
+        DLLS = self.addSwitch( 's13' ) // 32.777665, -96.802064
+        ORLD = self.addSwitch( 's14' ) // 28.538641, -81.381110
+        DNVR = self.addSwitch( 's15' ) // 39.736623, -104.984887
+        KSCY = self.addSwitch( 's16' ) // 39.100725, -94.581228
+        SNFN = self.addSwitch( 's17' ) // 37.779751, -122.409791
+        SCRM = self.addSwitch( 's18' ) // 38.581001, -121.497844
+        PTLD = self.addSwitch( 's19' ) // 45.523317, -122.677768
+        STTL = self.addSwitch( 's20' ) // 47.607326, -122.331786
+        SLKC = self.addSwitch( 's21' ) // 40.759577, -111.895079
+        LA03 = self.addSwitch( 's22' ) // 34.056346, -118.235951
+        SNDG = self.addSwitch( 's23' ) // 32.714564, -117.153528
+        PHNX = self.addSwitch( 's24' ) // 33.448289, -112.076299
+
+        # ... and now hosts
+        NY54_host = self.addHost( 'h25' )
+        CMBR_host = self.addHost( 'h1' )
+        CHCG_host = self.addHost( 'h2' )
+        CLEV_host = self.addHost( 'h3' )
+        RLGH_host = self.addHost( 'h4' )
+        ATLN_host = self.addHost( 'h5' )
+        PHLA_host = self.addHost( 'h6' )
+        WASH_host = self.addHost( 'h7' )
+        NSVL_host = self.addHost( 'h8' )
+        STLS_host = self.addHost( 'h9' )
+        NWOR_host = self.addHost( 'h10' )
+        HSTN_host = self.addHost( 'h11' )
+        SNAN_host = self.addHost( 'h12' )
+        DLLS_host = self.addHost( 'h13' )
+        ORLD_host = self.addHost( 'h14' )
+        DNVR_host = self.addHost( 'h15' )
+        KSCY_host = self.addHost( 'h16' )
+        SNFN_host = self.addHost( 'h17' )
+        SCRM_host = self.addHost( 'h18' )
+        PTLD_host = self.addHost( 'h19' )
+        STTL_host = self.addHost( 'h20' )
+        SLKC_host = self.addHost( 'h21' )
+        LA03_host = self.addHost( 'h22' )
+        SNDG_host = self.addHost( 'h23' )
+        PHNX_host = self.addHost( 'h24' )
+
+        # add edges between switch and corresponding host
+        self.addLink( NY54 , NY54_host )
+        self.addLink( CMBR , CMBR_host )
+        self.addLink( CHCG , CHCG_host )
+        self.addLink( CLEV , CLEV_host )
+        self.addLink( RLGH , RLGH_host )
+        self.addLink( ATLN , ATLN_host )
+        self.addLink( PHLA , PHLA_host )
+        self.addLink( WASH , WASH_host )
+        self.addLink( NSVL , NSVL_host )
+        self.addLink( STLS , STLS_host )
+        self.addLink( NWOR , NWOR_host )
+        self.addLink( HSTN , HSTN_host )
+        self.addLink( SNAN , SNAN_host )
+        self.addLink( DLLS , DLLS_host )
+        self.addLink( ORLD , ORLD_host )
+        self.addLink( DNVR , DNVR_host )
+        self.addLink( KSCY , KSCY_host )
+        self.addLink( SNFN , SNFN_host )
+        self.addLink( SCRM , SCRM_host )
+        self.addLink( PTLD , PTLD_host )
+        self.addLink( STTL , STTL_host )
+        self.addLink( SLKC , SLKC_host )
+        self.addLink( LA03 , LA03_host )
+        self.addLink( SNDG , SNDG_host )
+        self.addLink( PHNX , PHNX_host )
+
+        # add edges between switches
+        self.addLink( NY54 , CMBR, bw=10, delay='0.979030824185ms')
+        self.addLink( NY54 , CHCG, bw=10, delay='0.806374975652ms')
+        self.addLink( NY54 , PHLA, bw=10, delay='0.686192970166ms')
+        self.addLink( NY54 , WASH, bw=10, delay='0.605826192092ms')
+        self.addLink( CMBR , PHLA, bw=10, delay='1.4018238197ms')
+        self.addLink( CHCG , CLEV, bw=10, delay='0.232315346482ms')
+        self.addLink( CHCG , PHLA, bw=10, delay='1.07297714274ms')
+        self.addLink( CHCG , STLS, bw=10, delay='1.12827896944ms')
+        self.addLink( CHCG , DNVR, bw=10, delay='1.35964770335ms')
+        self.addLink( CHCG , KSCY, bw=10, delay='1.5199778541ms')
+        self.addLink( CHCG , SNFN, bw=10, delay='0.620743405435ms')
+        self.addLink( CHCG , STTL, bw=10, delay='0.93027212534ms')
+        self.addLink( CHCG , SLKC, bw=10, delay='0.735621751348ms')
+        self.addLink( CLEV , NSVL, bw=10, delay='0.523419372248ms')
+        self.addLink( CLEV , STLS, bw=10, delay='1.00360290845ms')
+        self.addLink( CLEV , PHLA, bw=10, delay='0.882912133249ms')
+        self.addLink( RLGH , ATLN, bw=10, delay='1.1644489729ms')
+        self.addLink( RLGH , WASH, bw=10, delay='1.48176810502ms')
+        self.addLink( ATLN , WASH, bw=10, delay='0.557636936322ms')
+        self.addLink( ATLN , NSVL, bw=10, delay='1.32869749865ms')
+        self.addLink( ATLN , STLS, bw=10, delay='0.767705554748ms')
+        self.addLink( ATLN , DLLS, bw=10, delay='0.544782086448ms')
+        self.addLink( ATLN , ORLD, bw=10, delay='1.46119152532ms')
+        self.addLink( PHLA , WASH, bw=10, delay='0.372209320106ms')
+        self.addLink( NSVL , STLS, bw=10, delay='1.43250491305ms')
+        self.addLink( NSVL , DLLS, bw=10, delay='1.67698215288ms')
+        self.addLink( STLS , DLLS, bw=10, delay='0.256389964194ms')
+        self.addLink( STLS , KSCY, bw=10, delay='0.395511571791ms')
+        self.addLink( STLS , LA03, bw=10, delay='0.257085227363ms')
+        self.addLink( NWOR , HSTN, bw=10, delay='0.0952906633914ms')
+        self.addLink( NWOR , DLLS, bw=10, delay='1.60231329739ms')
+        self.addLink( NWOR , ORLD, bw=10, delay='0.692731063896ms')
+        self.addLink( HSTN , SNAN, bw=10, delay='0.284150653798ms')
+        self.addLink( HSTN , DLLS, bw=10, delay='1.65690128332ms')
+        self.addLink( HSTN , ORLD, bw=10, delay='0.731886304782ms')
+        self.addLink( SNAN , PHNX, bw=10, delay='1.34258627257ms')
+        self.addLink( SNAN , DLLS, bw=10, delay='1.50063532341ms')
+        self.addLink( DLLS , DNVR, bw=10, delay='0.251471593235ms')
+        self.addLink( DLLS , KSCY, bw=10, delay='0.18026026737ms')
+        self.addLink( DLLS , SNFN, bw=10, delay='0.74304274592ms')
+        self.addLink( DLLS , LA03, bw=10, delay='0.506439293357ms')
+        self.addLink( DNVR , KSCY, bw=10, delay='0.223328790403ms')
+        self.addLink( DNVR , SNFN, bw=10, delay='0.889017541903ms')
+        self.addLink( DNVR , SLKC, bw=10, delay='0.631898982721ms')
+        self.addLink( KSCY , SNFN, bw=10, delay='0.922778522233ms')
+        self.addLink( SNFN , SCRM, bw=10, delay='0.630352278097ms')
+        self.addLink( SNFN , PTLD, bw=10, delay='0.828572513655ms')
+        self.addLink( SNFN , STTL, bw=10, delay='1.54076081649ms')
+        self.addLink( SNFN , SLKC, bw=10, delay='0.621507502625ms')
+        self.addLink( SNFN , LA03, bw=10, delay='0.602936230151ms')
+        self.addLink( SCRM , SLKC, bw=10, delay='0.461350343644ms')
+        self.addLink( PTLD , STTL, bw=10, delay='1.17591515181ms')
+        self.addLink( SLKC , LA03, bw=10, delay='0.243225267023ms')
+        self.addLink( LA03 , SNDG, bw=10, delay='0.681264950821ms')
+        self.addLink( LA03 , PHNX, bw=10, delay='0.343709457969ms')
+        # self.addLink( LA03 , PHNX, bw=10, delay='0.343709457969ms')
+        self.addLink( SNDG , PHNX, bw=10, delay='0.345064487693ms')
+
+topos = { 'att': ( lambda: AttMplsTopo() ) }
diff --git a/tools/test/topos/attmpls b/tools/test/topos/attmpls
new file mode 100755
index 0000000..e309f9c
--- /dev/null
+++ b/tools/test/topos/attmpls
@@ -0,0 +1,3 @@
+#!/bin/bash
+cd ~/topos && sudo mn --custom att-mpls.py --topo att --link tc --controller remote,${1:-192.168.56.101} --mac \
+	--switch ovsk,protocols=OpenFlow10
diff --git a/tools/test/topos/oe-linear-3.json b/tools/test/topos/oe-linear-3.json
index f35cc98..71addcb 100644
--- a/tools/test/topos/oe-linear-3.json
+++ b/tools/test/topos/oe-linear-3.json
@@ -34,11 +34,11 @@
     ],
 
     "links" : [
-        { "src": "of:0000ffffffffff01/20", "dst": "of:0000ffffffffff03/30", "type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "durable": "true" } },
-        { "src": "of:0000ffffffffff02/21", "dst": "of:0000ffffffffff03/31", "type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "durable": "true" } },
+        { "src": "of:0000ffffffffff01/20", "dst": "of:0000ffffffffff03/30", "type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "durable": "true", "inactive": "true" } },
+        { "src": "of:0000ffffffffff02/21", "dst": "of:0000ffffffffff03/31", "type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "durable": "true", "inactive": "true" } },
 
-        { "src": "of:0000ffffffff0001/2", "dst": "of:0000ffffffffff01/10", "type": "OPTICAL", "annotations": { "bandwidth": 100000, "optical.type": "cross-connect", "durable": "true" } },
-        { "src": "of:0000ffffffff0002/2", "dst": "of:0000ffffffffff02/11", "type": "OPTICAL", "annotations": { "bandwidth": 100000, "optical.type": "cross-connect", "durable": "true" } }
+        { "src": "of:0000ffffffff0001/2", "dst": "of:0000ffffffffff01/10", "type": "OPTICAL", "annotations": { "bandwidth": 100000, "optical.type": "cross-connect", "durable": "true", "inactive": "true" } },
+        { "src": "of:0000ffffffff0002/2", "dst": "of:0000ffffffffff02/11", "type": "OPTICAL", "annotations": { "bandwidth": 100000, "optical.type": "cross-connect", "durable": "true", "inactive": "true" } }
     ],
 
     "hosts" : [
diff --git a/tools/test/topos/oe-nonlinear-10.json b/tools/test/topos/oe-nonlinear-10.json
index aa98deb..d208bff 100644
--- a/tools/test/topos/oe-nonlinear-10.json
+++ b/tools/test/topos/oe-nonlinear-10.json
@@ -124,26 +124,26 @@
     ],
 
     "links" : [
-        { "src": "of:0000ffffffffff01/50", "dst": "of:0000ffffffffff02/30","type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 48, "durable": "true" } },
-        { "src": "of:0000ffffffffff02/50", "dst": "of:0000ffffffffff03/30","type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 493, "durable": "true" } },
-        { "src": "of:0000ffffffffff03/50", "dst": "of:0000ffffffffff04/50","type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 171, "durable": "true" } },
-        { "src": "of:0000ffffffffff01/20", "dst": "of:0000ffffffffff05/50","type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 2555, "durable": "true" } },
-        { "src": "of:0000ffffffffff02/20", "dst": "of:0000ffffffffff05/20","type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 2539, "durable": "true" } },
-        { "src": "of:0000ffffffffff03/20", "dst": "of:0000ffffffffff06/50","type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 1979, "durable": "true" } },
-        { "src": "of:0000ffffffffff04/20", "dst": "of:0000ffffffffff06/20","type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 1867, "durable": "true" } },
-        { "src": "of:0000ffffffffff05/30", "dst": "of:0000ffffffffff06/40","type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 1378, "durable": "true" } },
-        { "src": "of:0000ffffffffff05/40", "dst": "of:0000ffffffffff07/50", "type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 2200, "durable": "true" } },
-        { "src": "of:0000ffffffffff06/30", "dst": "of:0000ffffffffff08/50", "type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 1918, "durable": "true" } },
-        { "src": "of:0000ffffffffff07/20", "dst": "of:0000ffffffffff08/30", "type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 3625, "durable": "true" } },
-        { "src": "of:0000ffffffffff07/30", "dst": "of:0000ffffffffff09/50", "type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 3880, "durable": "true" } },
-        { "src": "of:0000ffffffffff08/20", "dst": "of:0000ffffffffff0A/50", "type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 838, "durable": "true" } },
-        { "src": "of:0000ffffffffff09/20", "dst": "of:0000ffffffffff0A/20","type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 1245, "durable": "true" } },
+        { "src": "of:0000ffffffffff01/50", "dst": "of:0000ffffffffff02/30","type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 48, "durable": "true", "inactive": "true" } },
+        { "src": "of:0000ffffffffff02/50", "dst": "of:0000ffffffffff03/30","type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 493, "durable": "true", "inactive": "true" } },
+        { "src": "of:0000ffffffffff03/50", "dst": "of:0000ffffffffff04/50","type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 171, "durable": "true", "inactive": "true" } },
+        { "src": "of:0000ffffffffff01/20", "dst": "of:0000ffffffffff05/50","type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 2555, "durable": "true", "inactive": "true" } },
+        { "src": "of:0000ffffffffff02/20", "dst": "of:0000ffffffffff05/20","type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 2539, "durable": "true", "inactive": "true" } },
+        { "src": "of:0000ffffffffff03/20", "dst": "of:0000ffffffffff06/50","type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 1979, "durable": "true", "inactive": "true" } },
+        { "src": "of:0000ffffffffff04/20", "dst": "of:0000ffffffffff06/20","type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 1867, "durable": "true", "inactive": "true" } },
+        { "src": "of:0000ffffffffff05/30", "dst": "of:0000ffffffffff06/40","type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 1378, "durable": "true", "inactive": "true" } },
+        { "src": "of:0000ffffffffff05/40", "dst": "of:0000ffffffffff07/50", "type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 2200, "durable": "true", "inactive": "true" } },
+        { "src": "of:0000ffffffffff06/30", "dst": "of:0000ffffffffff08/50", "type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 1918, "durable": "true", "inactive": "true" } },
+        { "src": "of:0000ffffffffff07/20", "dst": "of:0000ffffffffff08/30", "type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 3625, "durable": "true", "inactive": "true" } },
+        { "src": "of:0000ffffffffff07/30", "dst": "of:0000ffffffffff09/50", "type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 3880, "durable": "true", "inactive": "true" } },
+        { "src": "of:0000ffffffffff08/20", "dst": "of:0000ffffffffff0A/50", "type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 838, "durable": "true", "inactive": "true" } },
+        { "src": "of:0000ffffffffff09/20", "dst": "of:0000ffffffffff0A/20","type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 1245, "durable": "true", "inactive": "true" } },
 
-        { "src": "of:0000ffffffff0001/2", "dst": "of:0000ffffffffff01/10", "type": "OPTICAL", "annotations": { "bandwidth": 100000, "optical.type": "cross-connect", "durable": "true" } },
-        { "src": "of:0000ffffffff0002/2", "dst": "of:0000ffffffffff04/10", "type": "OPTICAL", "annotations": { "bandwidth": 100000, "optical.type": "cross-connect", "durable": "true" } },
-        { "src": "of:0000ffffffff0003/2", "dst": "of:0000ffffffffff06/10", "type": "OPTICAL", "annotations": { "bandwidth": 100000, "optical.type": "cross-connect", "durable": "true" } },
-        { "src": "of:0000ffffffff0004/2", "dst": "of:0000ffffffffff07/10", "type": "OPTICAL", "annotations": { "bandwidth": 100000, "optical.type": "cross-connect", "durable": "true" } },
-        { "src": "of:0000ffffffff0005/2", "dst": "of:0000ffffffffff09/10", "type": "OPTICAL", "annotations": { "bandwidth": 100000, "optical.type": "cross-connect", "durable": "true" } },
-        { "src": "of:0000ffffffff0006/2", "dst": "of:0000ffffffffff0A/10",  "type": "OPTICAL", "annotations": { "bandwidth": 100000, "optical.type": "cross-connect", "durable": "true" } }
+        { "src": "of:0000ffffffff0001/2", "dst": "of:0000ffffffffff01/10", "type": "OPTICAL", "annotations": { "bandwidth": 100000, "optical.type": "cross-connect", "durable": "true", "inactive": "true" } },
+        { "src": "of:0000ffffffff0002/2", "dst": "of:0000ffffffffff04/10", "type": "OPTICAL", "annotations": { "bandwidth": 100000, "optical.type": "cross-connect", "durable": "true", "inactive": "true" } },
+        { "src": "of:0000ffffffff0003/2", "dst": "of:0000ffffffffff06/10", "type": "OPTICAL", "annotations": { "bandwidth": 100000, "optical.type": "cross-connect", "durable": "true", "inactive": "true" } },
+        { "src": "of:0000ffffffff0004/2", "dst": "of:0000ffffffffff07/10", "type": "OPTICAL", "annotations": { "bandwidth": 100000, "optical.type": "cross-connect", "durable": "true", "inactive": "true" } },
+        { "src": "of:0000ffffffff0005/2", "dst": "of:0000ffffffffff09/10", "type": "OPTICAL", "annotations": { "bandwidth": 100000, "optical.type": "cross-connect", "durable": "true", "inactive": "true" } },
+        { "src": "of:0000ffffffff0006/2", "dst": "of:0000ffffffffff0A/10",  "type": "OPTICAL", "annotations": { "bandwidth": 100000, "optical.type": "cross-connect", "durable": "true", "inactive": "true" } }
     ]
 }
diff --git a/tools/test/topos/oe-nonlinear-4.json b/tools/test/topos/oe-nonlinear-4.json
index 7082047..3a0d18c 100644
--- a/tools/test/topos/oe-nonlinear-4.json
+++ b/tools/test/topos/oe-nonlinear-4.json
@@ -39,13 +39,13 @@
     ],
 
     "links" : [
-        { "src": "of:0000ffffffffff01/20", "dst": "of:0000ffffffffff03/30", "type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "durable": "true" } },
-        { "src": "of:0000ffffffffff02/21", "dst": "of:0000ffffffffff03/31", "type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "durable": "true" } },
-        { "src": "of:0000ffffffffff01/22", "dst": "of:0000ffffffffff04/30", "type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "durable": "true" } },
-        { "src": "of:0000ffffffffff04/31", "dst": "of:0000ffffffffff02/22", "type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "durable": "true" } },
+        { "src": "of:0000ffffffffff01/20", "dst": "of:0000ffffffffff03/30", "type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "durable": "true", "inactive": "true" } },
+        { "src": "of:0000ffffffffff02/21", "dst": "of:0000ffffffffff03/31", "type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "durable": "true", "inactive": "true" } },
+        { "src": "of:0000ffffffffff01/22", "dst": "of:0000ffffffffff04/30", "type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "durable": "true", "inactive": "true" } },
+        { "src": "of:0000ffffffffff04/31", "dst": "of:0000ffffffffff02/22", "type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "durable": "true", "inactive": "true" } },
 
-        { "src": "of:0000ffffffff0001/2", "dst": "of:0000ffffffffff01/10", "type": "OPTICAL", "annotations": { "bandwidth": 100000, "optical.type": "cross-connect", "durable": "true" } },
-        { "src": "of:0000ffffffff0002/2", "dst": "of:0000ffffffffff02/11", "type": "OPTICAL", "annotations": { "bandwidth": 100000, "optical.type": "cross-connect", "durable": "true" } }
+        { "src": "of:0000ffffffff0001/2", "dst": "of:0000ffffffffff01/10", "type": "OPTICAL", "annotations": { "bandwidth": 100000, "optical.type": "cross-connect", "durable": "true", "inactive": "true" } },
+        { "src": "of:0000ffffffff0002/2", "dst": "of:0000ffffffffff02/11", "type": "OPTICAL", "annotations": { "bandwidth": 100000, "optical.type": "cross-connect", "durable": "true", "inactive": "true" } }
     ]
 
 }
diff --git a/tools/test/topos/tower b/tools/test/topos/tower
new file mode 100755
index 0000000..8244bae
--- /dev/null
+++ b/tools/test/topos/tower
@@ -0,0 +1,2 @@
+#!/bin/bash
+cd ~/topos && sudo mn --custom tower.py --topo tower --controller remote,${1:-192.168.56.101} --mac
diff --git a/web/gui/src/main/java/org/onlab/onos/gui/TopologyViewMessages.java b/web/gui/src/main/java/org/onlab/onos/gui/TopologyViewMessages.java
index dcc2355..fdc8d06 100644
--- a/web/gui/src/main/java/org/onlab/onos/gui/TopologyViewMessages.java
+++ b/web/gui/src/main/java/org/onlab/onos/gui/TopologyViewMessages.java
@@ -188,7 +188,8 @@
         ControllerNode node = event.subject();
         ObjectNode payload = mapper.createObjectNode()
                 .put("id", node.id().toString())
-                .put("online", clusterService.getState(node.id()) == ACTIVE);
+                .put("online", clusterService.getState(node.id()) == ACTIVE)
+                .put("uiAttached", event.subject().equals(clusterService.getLocalNode()));
 
         ArrayNode labels = mapper.createArrayNode();
         labels.add(node.id().toString());
@@ -236,7 +237,7 @@
         ObjectNode payload = mapper.createObjectNode()
                 .put("id", compactLinkString(link))
                 .put("type", link.type().toString().toLowerCase())
-                .put("online", true) // link.state()) TODO: add link state field
+                .put("online", link.state() == Link.State.ACTIVE)
                 .put("linkWidth", 2)
                 .put("src", link.src().deviceId().toString())
                 .put("srcPort", link.src().port().toString())
diff --git a/web/gui/src/main/webapp/mast2.js b/web/gui/src/main/webapp/mast2.js
index 237c511..bf8694a 100644
--- a/web/gui/src/main/webapp/mast2.js
+++ b/web/gui/src/main/webapp/mast2.js
@@ -30,7 +30,7 @@
     var api = onos.api;
 
     // Config variables
-    var guiTitle = 'Open Networking Operating System';
+    var guiTitle = 'Open Network Operating System';
 
     // DOM elements and the like
     var mast = d3.select('#mast');
diff --git a/web/gui/src/main/webapp/topo2.css b/web/gui/src/main/webapp/topo2.css
index 438f265..2eb6a8e 100644
--- a/web/gui/src/main/webapp/topo2.css
+++ b/web/gui/src/main/webapp/topo2.css
@@ -108,6 +108,11 @@
     opacity: .7;
 }
 
+#topo svg .link.inactive {
+    opacity: .2;
+    stroke-dasharray: 8 4;
+}
+
 #topo svg .link.primary {
     stroke: #f11;
     stroke-width: 6px;
diff --git a/web/gui/src/main/webapp/topo2.js b/web/gui/src/main/webapp/topo2.js
index f34f247..5329545 100644
--- a/web/gui/src/main/webapp/topo2.js
+++ b/web/gui/src/main/webapp/topo2.js
@@ -826,7 +826,7 @@
         // merge in remaining data
         $.extend(lnk, link, {
             class: 'link',
-            svgClass: type ? 'link ' + type : 'link'
+            svgClass: (type ? 'link ' + type : 'link') + ' ' + (link.online ? 'active' : 'inactive')
         });
         return lnk;
     }