ONOS-4971: Synthetic Link Data -- WIP

- Breaking out UiLink to subclasses for device links, host links, region links, region-device links,
    - (soon, also peer links).
- Augmenting UiLinkId to include regions as endpoints.
- Introduced UiSynthLink to encapsulate synthetic links bound to regions.
- Model Cache now computes synthetic links from the underlying link data.
- Added endPointA/B() and type() methods to UiLink.
- Updated topo2CurrentRegion response to include synth-links for the region.

Change-Id: Ifa62a15fbe0a58b134d92278b201fa7a72cbfa83
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/topo/Topo2Jsonifier.java b/web/gui/src/main/java/org/onosproject/ui/impl/topo/Topo2Jsonifier.java
index 8a26a92..a5a344a 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/topo/Topo2Jsonifier.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/topo/Topo2Jsonifier.java
@@ -16,6 +16,7 @@
 
 package org.onosproject.ui.impl.topo;
 
+import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.node.ArrayNode;
 import com.fasterxml.jackson.databind.node.ObjectNode;
@@ -39,6 +40,7 @@
 import org.onosproject.ui.model.topo.UiLink;
 import org.onosproject.ui.model.topo.UiNode;
 import org.onosproject.ui.model.topo.UiRegion;
+import org.onosproject.ui.model.topo.UiSynthLink;
 import org.onosproject.ui.model.topo.UiTopoLayout;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -180,9 +182,11 @@
      *
      * @param region     the region to transform to JSON
      * @param subRegions the subregions within this region
+     * @param links      the links within this region
      * @return a JSON representation of the data
      */
-    ObjectNode region(UiRegion region, Set<UiRegion> subRegions) {
+    ObjectNode region(UiRegion region, Set<UiRegion> subRegions,
+                      List<UiSynthLink> links) {
         ObjectNode payload = objectNode();
         if (region == null) {
             payload.put("note", "no-region");
@@ -193,14 +197,16 @@
             payload.set("subregions", jsonSubRegions(subRegions));
         }
 
+        if (links != null) {
+            payload.set("links", jsonLinks(links));
+        }
+
         List<String> layerTags = region.layerOrder();
         List<Set<UiNode>> splitDevices = splitByLayer(layerTags, region.devices());
         List<Set<UiNode>> splitHosts = splitByLayer(layerTags, region.hosts());
-        Set<UiLink> links = region.links();
 
         payload.set("devices", jsonGrouped(splitDevices));
         payload.set("hosts", jsonGrouped(splitHosts));
-        payload.set("links", jsonLinks(links));
         payload.set("layerOrder", jsonStrings(layerTags));
 
         return payload;
@@ -208,24 +214,22 @@
 
     private ArrayNode jsonSubRegions(Set<UiRegion> subregions) {
         ArrayNode kids = arrayNode();
-        if (subregions != null) {
-            subregions.forEach(s -> kids.add(jsonClosedRegion(s)));
-        }
+        subregions.forEach(s -> kids.add(jsonClosedRegion(s)));
         return kids;
     }
 
+    private JsonNode jsonLinks(List<UiSynthLink> links) {
+        ArrayNode synthLinks = arrayNode();
+        links.forEach(l -> synthLinks.add(json(l)));
+        return synthLinks;
+    }
+
     private ArrayNode jsonStrings(List<String> strings) {
         ArrayNode array = arrayNode();
         strings.forEach(array::add);
         return array;
     }
 
-    private ArrayNode jsonLinks(Set<UiLink> links) {
-        ArrayNode result = arrayNode();
-        links.forEach(lnk -> result.add(json(lnk)));
-        return result;
-    }
-
     private ArrayNode jsonGrouped(List<Set<UiNode>> groupedNodes) {
         ArrayNode result = arrayNode();
         groupedNodes.forEach(g -> {
@@ -280,11 +284,13 @@
         // TODO: complete host details
     }
 
-
-    private ObjectNode json(UiLink link) {
+    private ObjectNode json(UiSynthLink sLink) {
+        UiLink uLink = sLink.link();
         return objectNode()
-                .put("id", link.idAsString());
-        // TODO: complete link details
+                .put("id", uLink.idAsString())
+                .put("epA", uLink.endPointA())
+                .put("epB", uLink.endPointB())
+                .put("type", uLink.type());
     }
 
 
@@ -305,7 +311,7 @@
      */
     public ArrayNode closedNodes(Set<UiNode> nodes) {
         ArrayNode array = arrayNode();
-        for (UiNode node: nodes) {
+        for (UiNode node : nodes) {
             if (node instanceof UiRegion) {
                 array.add(jsonClosedRegion((UiRegion) node));
             } else if (node instanceof UiDevice) {
@@ -361,20 +367,6 @@
         return array;
     }
 
-    /**
-     * Returns a JSON array representation of a list of links.
-     *
-     * @param links the links
-     * @return a JSON representation of the links
-     */
-    public ArrayNode links(Set<UiLink> links) {
-        ArrayNode array = arrayNode();
-        for (UiLink link : links) {
-            array.add(json(link));
-        }
-        return array;
-    }
-
     // package-private for unit testing
     List<Set<UiNode>> splitByLayer(List<String> layerTags,
                                    Set<? extends UiNode> nodes) {
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/topo/Topo2ViewMessageHandler.java b/web/gui/src/main/java/org/onosproject/ui/impl/topo/Topo2ViewMessageHandler.java
index 7795271..86d54cc 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/topo/Topo2ViewMessageHandler.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/topo/Topo2ViewMessageHandler.java
@@ -26,6 +26,7 @@
 import org.onosproject.ui.model.topo.UiClusterMember;
 import org.onosproject.ui.model.topo.UiNode;
 import org.onosproject.ui.model.topo.UiRegion;
+import org.onosproject.ui.model.topo.UiSynthLink;
 import org.onosproject.ui.model.topo.UiTopoLayout;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -125,7 +126,8 @@
             //   (as well as layer-order hints)
             UiRegion region = topoSession.getRegion(currentLayout);
             Set<UiRegion> kids = topoSession.getSubRegions(currentLayout);
-            sendMessage(CURRENT_REGION, t2json.region(region, kids));
+            List<UiSynthLink> links = topoSession.getLinks(currentLayout);
+            sendMessage(CURRENT_REGION, t2json.region(region, kids, links));
 
             // these are the regions/devices that are siblings to this region
             Set<UiNode> peers = topoSession.getPeerNodes(currentLayout);
@@ -133,6 +135,8 @@
             peersPayload.set("peers", t2json.closedNodes(peers));
             sendMessage(PEER_REGIONS, peersPayload);
 
+            // TODO: send breadcrumb message
+
             // finally, tell the UI that we are done : TODO review / delete??
             sendMessage(TOPO_START_DONE, null);
 
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/topo/UiTopoSession.java b/web/gui/src/main/java/org/onosproject/ui/impl/topo/UiTopoSession.java
index 7127ec1..44ea910 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/topo/UiTopoSession.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/topo/UiTopoSession.java
@@ -25,6 +25,7 @@
 import org.onosproject.ui.model.topo.UiClusterMember;
 import org.onosproject.ui.model.topo.UiNode;
 import org.onosproject.ui.model.topo.UiRegion;
+import org.onosproject.ui.model.topo.UiSynthLink;
 import org.onosproject.ui.model.topo.UiTopoLayout;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -214,6 +215,16 @@
     }
 
     /**
+     * Returns the (synthetic) links of the region in the specified layout.
+     *
+     * @param layout the layout being viewed
+     * @return all links that are contained by this layout's region
+     */
+    public List<UiSynthLink> getLinks(UiTopoLayout layout) {
+        return sharedModel.getSynthLinks(layout.regionId());
+    }
+
+    /**
      * Refreshes the model's internal state.
      */
     public void refreshModel() {
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/topo/cli/ListLinks.java b/web/gui/src/main/java/org/onosproject/ui/impl/topo/cli/ListLinks.java
index 640b37d..245aab3 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/topo/cli/ListLinks.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/topo/cli/ListLinks.java
@@ -29,6 +29,6 @@
     @Override
     protected void execute() {
         UiSharedTopologyModel model = get(UiSharedTopologyModel.class);
-        sorted(model.getLinks()).forEach(l -> print("%s", l));
+        sorted(model.getDeviceLinks()).forEach(l -> print("%s", l));
     }
 }
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/topo/model/ModelCache.java b/web/gui/src/main/java/org/onosproject/ui/impl/topo/model/ModelCache.java
index 781650d..02c21dc 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/topo/model/ModelCache.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/topo/model/ModelCache.java
@@ -33,11 +33,13 @@
 import org.onosproject.ui.model.ServiceBundle;
 import org.onosproject.ui.model.topo.UiClusterMember;
 import org.onosproject.ui.model.topo.UiDevice;
+import org.onosproject.ui.model.topo.UiDeviceLink;
+import org.onosproject.ui.model.topo.UiEdgeLink;
 import org.onosproject.ui.model.topo.UiElement;
 import org.onosproject.ui.model.topo.UiHost;
-import org.onosproject.ui.model.topo.UiLink;
 import org.onosproject.ui.model.topo.UiLinkId;
 import org.onosproject.ui.model.topo.UiRegion;
+import org.onosproject.ui.model.topo.UiSynthLink;
 import org.onosproject.ui.model.topo.UiTopoLayout;
 import org.onosproject.ui.model.topo.UiTopoLayoutId;
 import org.onosproject.ui.model.topo.UiTopology;
@@ -101,7 +103,7 @@
         loadClusterMembers();
         loadRegions();
         loadDevices();
-        loadLinks();
+        loadDeviceLinks();
         loadHosts();
     }
 
@@ -334,66 +336,72 @@
     }
 
 
-    // === LINKS
+    // === LINKS ===
 
-    private UiLink addNewLink(UiLinkId id) {
-        UiLink uiLink = new UiLink(uiTopology, id);
-        uiTopology.add(uiLink);
-        return uiLink;
+    private UiDeviceLink addNewDeviceLink(UiLinkId id) {
+        UiDeviceLink uiDeviceLink = new UiDeviceLink(uiTopology, id);
+        uiTopology.add(uiDeviceLink);
+        return uiDeviceLink;
     }
 
-    private void updateLink(UiLink uiLink, Link link) {
-        uiLink.attachBackingLink(link);
+    private UiEdgeLink addNewEdgeLink(UiLinkId id) {
+        UiEdgeLink uiEdgeLink = new UiEdgeLink(uiTopology, id);
+        uiTopology.add(uiEdgeLink);
+        return uiEdgeLink;
     }
 
-    private void loadLinks() {
+    private void updateDeviceLink(UiDeviceLink uiDeviceLink, Link link) {
+        uiDeviceLink.attachBackingLink(link);
+    }
+
+    private void loadDeviceLinks() {
         for (Link link : services.link().getLinks()) {
             UiLinkId id = uiLinkId(link);
 
-            UiLink uiLink = uiTopology.findLink(id);
-            if (uiLink == null) {
-                uiLink = addNewLink(id);
+            UiDeviceLink uiDeviceLink = uiTopology.findDeviceLink(id);
+            if (uiDeviceLink == null) {
+                uiDeviceLink = addNewDeviceLink(id);
             }
-            updateLink(uiLink, link);
+            updateDeviceLink(uiDeviceLink, link);
         }
     }
 
     // invoked from UiSharedTopologyModel link listener
-    void addOrUpdateLink(Link link) {
+    void addOrUpdateDeviceLink(Link link) {
         UiLinkId id = uiLinkId(link);
-        UiLink uiLink = uiTopology.findLink(id);
-        if (uiLink == null) {
-            uiLink = addNewLink(id);
+        UiDeviceLink uiDeviceLink = uiTopology.findDeviceLink(id);
+        if (uiDeviceLink == null) {
+            uiDeviceLink = addNewDeviceLink(id);
         }
-        updateLink(uiLink, link);
+        updateDeviceLink(uiDeviceLink, link);
 
-        postEvent(LINK_ADDED_OR_UPDATED, uiLink);
+        postEvent(LINK_ADDED_OR_UPDATED, uiDeviceLink);
     }
 
     // package private for unit test access
-    UiLink accessLink(UiLinkId id) {
-        return uiTopology.findLink(id);
+    UiDeviceLink accessDeviceLink(UiLinkId id) {
+        return uiTopology.findDeviceLink(id);
     }
 
     // invoked from UiSharedTopologyModel link listener
-    void removeLink(Link link) {
+    void removeDeviceLink(Link link) {
         UiLinkId id = uiLinkId(link);
-        UiLink uiLink = uiTopology.findLink(id);
-        if (uiLink != null) {
-            boolean remaining = uiLink.detachBackingLink(link);
+        UiDeviceLink uiDeviceLink = uiTopology.findDeviceLink(id);
+        if (uiDeviceLink != null) {
+            boolean remaining = uiDeviceLink.detachBackingLink(link);
             if (remaining) {
-                postEvent(LINK_ADDED_OR_UPDATED, uiLink);
+                postEvent(LINK_ADDED_OR_UPDATED, uiDeviceLink);
             } else {
-                uiTopology.remove(uiLink);
-                postEvent(LINK_REMOVED, uiLink);
+                uiTopology.remove(uiDeviceLink);
+                postEvent(LINK_REMOVED, uiDeviceLink);
             }
         } else {
-            log.warn(E_NO_ELEMENT, "link", id);
+            log.warn(E_NO_ELEMENT, "Device link", id);
         }
     }
 
-    Set<UiLink> getAllLinks() {
-        return uiTopology.allLinks();
+    Set<UiDeviceLink> getAllDeviceLinks() {
+        return uiTopology.allDeviceLinks();
     }
 
     // === HOSTS
@@ -411,20 +419,19 @@
         host.setEdgeLinkId(elinkId);
 
         // add synthesized edge link to the topology
-        UiLink edgeLink = addNewLink(elinkId);
+        UiEdgeLink edgeLink = addNewEdgeLink(elinkId);
         edgeLink.attachEdgeLink(elink);
 
         return host;
     }
 
-    private void insertNewUiLink(UiLinkId id, EdgeLink e) {
-        UiLink newEdgeLink = addNewLink(id);
+    private void insertNewUiEdgeLink(UiLinkId id, EdgeLink e) {
+        UiEdgeLink newEdgeLink = addNewEdgeLink(id);
         newEdgeLink.attachEdgeLink(e);
-
     }
 
     private void updateHost(UiHost uiHost, Host h) {
-        UiLink existing = uiTopology.findLink(uiHost.edgeLinkId());
+        UiEdgeLink existing = uiTopology.findEdgeLink(uiHost.edgeLinkId());
 
         EdgeLink currentElink = synthesizeLink(h);
         UiLinkId currentElinkId = uiLinkId(currentElink);
@@ -432,7 +439,7 @@
         if (existing != null) {
             if (!currentElinkId.equals(existing.id())) {
                 // edge link has changed
-                insertNewUiLink(currentElinkId, currentElink);
+                insertNewUiEdgeLink(currentElinkId, currentElink);
                 uiHost.setEdgeLinkId(currentElinkId);
 
                 uiTopology.remove(existing);
@@ -440,7 +447,7 @@
 
         } else {
             // no previously existing edge link
-            insertNewUiLink(currentElinkId, currentElink);
+            insertNewUiEdgeLink(currentElinkId, currentElink);
             uiHost.setEdgeLinkId(currentElinkId);
 
         }
@@ -489,7 +496,7 @@
         HostId id = host.id();
         UiHost uiHost = uiTopology.findHost(id);
         if (uiHost != null) {
-            UiLink edgeLink = uiTopology.findLink(uiHost.edgeLinkId());
+            UiEdgeLink edgeLink = uiTopology.findEdgeLink(uiHost.edgeLinkId());
             uiTopology.remove(edgeLink);
             uiTopology.remove(uiHost);
             postEvent(HOST_REMOVED, uiHost);
@@ -503,11 +510,17 @@
     }
 
 
+    // === SYNTHETIC LINKS
+
+    List<UiSynthLink> getSynthLinks(RegionId regionId) {
+        return uiTopology.findSynthLinks(regionId);
+    }
+
     /**
      * Refreshes the internal state.
      */
     public void refresh() {
-        // fix up internal linkages if they aren't correct
+        // fix up internal linkages to ensure they are correct
 
         // make sure regions reflect layout containment hierarchy
         fixupContainmentHierarchy(uiTopology.nullRegion());
@@ -542,8 +555,13 @@
         Set<DeviceId> leftOver = new HashSet<>(allDevices.size());
         allDevices.forEach(d -> leftOver.add(d.id()));
         uiTopology.nullRegion().reconcileDevices(leftOver);
+
+        // now that we have correct region hierarchy, and devices are in their
+        //  respective regions, we can compute synthetic links for each region.
+        uiTopology.computeSynthLinks();
     }
 
+
     // === CACHE STATISTICS
 
     /**
@@ -583,12 +601,21 @@
     }
 
     /**
-     * Returns the number of links in the topology.
+     * Returns the number of device links in the topology.
      *
-     * @return number of links
+     * @return number of device links
      */
-    public int linkCount() {
-        return uiTopology.linkCount();
+    public int deviceLinkCount() {
+        return uiTopology.deviceLinkCount();
+    }
+
+    /**
+     * Returns the number of edge links in the topology.
+     *
+     * @return number of edge links
+     */
+    public int edgeLinkCount() {
+        return uiTopology.edgeLinkCount();
     }
 
     /**
@@ -600,4 +627,12 @@
         return uiTopology.hostCount();
     }
 
+    /**
+     * Returns the number of synthetic links in the topology.
+     *
+     * @return the number of synthetic links
+     */
+    public int synthLinkCount() {
+        return uiTopology.synthLinkCount();
+    }
 }
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/topo/model/UiSharedTopologyModel.java b/web/gui/src/main/java/org/onosproject/ui/impl/topo/model/UiSharedTopologyModel.java
index a12e34d..2a66b8e 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/topo/model/UiSharedTopologyModel.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/topo/model/UiSharedTopologyModel.java
@@ -65,9 +65,10 @@
 import org.onosproject.ui.model.ServiceBundle;
 import org.onosproject.ui.model.topo.UiClusterMember;
 import org.onosproject.ui.model.topo.UiDevice;
+import org.onosproject.ui.model.topo.UiDeviceLink;
 import org.onosproject.ui.model.topo.UiHost;
-import org.onosproject.ui.model.topo.UiLink;
 import org.onosproject.ui.model.topo.UiRegion;
+import org.onosproject.ui.model.topo.UiSynthLink;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -269,12 +270,22 @@
     }
 
     /**
-     * Returns the set of links stored in the model cache.
+     * Returns the set of device links stored in the model cache.
      *
-     * @return set of links
+     * @return set of device links
      */
-    public Set<UiLink> getLinks() {
-        return cache.getAllLinks();
+    public Set<UiDeviceLink> getDeviceLinks() {
+        return cache.getAllDeviceLinks();
+    }
+
+    /**
+     * Returns the synthetic links associated with the specified region.
+     *
+     * @param regionId region ID
+     * @return synthetic links for that region
+     */
+    public List<UiSynthLink> getSynthLinks(RegionId regionId) {
+        return cache.getSynthLinks(regionId);
     }
 
     // =====================================================================
@@ -434,11 +445,11 @@
 
                 case LINK_ADDED:
                 case LINK_UPDATED:
-                    cache.addOrUpdateLink(link);
+                    cache.addOrUpdateDeviceLink(link);
                     break;
 
                 case LINK_REMOVED:
-                    cache.removeLink(link);
+                    cache.removeDeviceLink(link);
                     break;
 
                 default:
diff --git a/web/gui/src/test/java/org/onosproject/ui/impl/topo/model/ModelCacheTest.java b/web/gui/src/test/java/org/onosproject/ui/impl/topo/model/ModelCacheTest.java
index 4d409dc..d7ec3b6 100644
--- a/web/gui/src/test/java/org/onosproject/ui/impl/topo/model/ModelCacheTest.java
+++ b/web/gui/src/test/java/org/onosproject/ui/impl/topo/model/ModelCacheTest.java
@@ -29,9 +29,9 @@
 import org.onosproject.ui.impl.topo.model.UiModelEvent.Type;
 import org.onosproject.ui.model.topo.UiClusterMember;
 import org.onosproject.ui.model.topo.UiDevice;
+import org.onosproject.ui.model.topo.UiDeviceLink;
 import org.onosproject.ui.model.topo.UiElement;
 import org.onosproject.ui.model.topo.UiHost;
-import org.onosproject.ui.model.topo.UiLink;
 import org.onosproject.ui.model.topo.UiLinkId;
 import org.onosproject.ui.model.topo.UiRegion;
 
@@ -265,54 +265,54 @@
         // we've established that the ID is the same for both
         UiLinkId linkId = idA2B;
 
-        cache.addOrUpdateLink(link1);
+        cache.addOrUpdateDeviceLink(link1);
         dispatcher.assertLast(Type.LINK_ADDED_OR_UPDATED, linkId.toString());
         dispatcher.assertEventCount(1);
-        assertEquals("unex # links", 1, cache.linkCount());
+        assertEquals("unex # links", 1, cache.deviceLinkCount());
 
-        UiLink link = cache.accessLink(linkId);
+        UiDeviceLink link = cache.accessDeviceLink(linkId);
         assertEquals("dev A not d2", DEVID_2, link.deviceA());
         assertEquals("dev B not d7", DEVID_7, link.deviceB());
         assertEquals("wrong backing link A-B", link1, link.linkAtoB());
         assertEquals("backing link B-A?", null, link.linkBtoA());
 
-        cache.addOrUpdateLink(link2);
+        cache.addOrUpdateDeviceLink(link2);
         dispatcher.assertLast(Type.LINK_ADDED_OR_UPDATED, linkId.toString());
         dispatcher.assertEventCount(2);
         // NOTE: yes! expect 1 UiLink
-        assertEquals("unex # links", 1, cache.linkCount());
+        assertEquals("unex # links", 1, cache.deviceLinkCount());
 
-        link = cache.accessLink(linkId);
+        link = cache.accessDeviceLink(linkId);
         assertEquals("dev A not d2", DEVID_2, link.deviceA());
         assertEquals("dev B not d7", DEVID_7, link.deviceB());
         assertEquals("wrong backing link A-B", link1, link.linkAtoB());
         assertEquals("wrong backing link B-A", link2, link.linkBtoA());
 
         // now remove links one at a time
-        cache.removeLink(link1);
+        cache.removeDeviceLink(link1);
         // NOTE: yes! ADD_OR_UPDATE, since the link was updated
         dispatcher.assertLast(Type.LINK_ADDED_OR_UPDATED, linkId.toString());
         dispatcher.assertEventCount(3);
         // NOTE: yes! expect 1 UiLink (still)
-        assertEquals("unex # links", 1, cache.linkCount());
+        assertEquals("unex # links", 1, cache.deviceLinkCount());
 
-        link = cache.accessLink(linkId);
+        link = cache.accessDeviceLink(linkId);
         assertEquals("dev A not d2", DEVID_2, link.deviceA());
         assertEquals("dev B not d7", DEVID_7, link.deviceB());
         assertEquals("backing link A-B?", null, link.linkAtoB());
         assertEquals("wrong backing link B-A", link2, link.linkBtoA());
 
         // remove final link
-        cache.removeLink(link2);
+        cache.removeDeviceLink(link2);
         dispatcher.assertLast(Type.LINK_REMOVED, linkId.toString());
         dispatcher.assertEventCount(4);
         // NOTE: finally link should be removed from cache
-        assertEquals("unex # links", 0, cache.linkCount());
+        assertEquals("unex # links", 0, cache.deviceLinkCount());
     }
 
     private void assertHostLinkCounts(int nHosts, int nLinks) {
         assertEquals("unex # hosts", nHosts, cache.hostCount());
-        assertEquals("unex # links", nLinks, cache.linkCount());
+        assertEquals("unex # links", nLinks, cache.edgeLinkCount());
     }
 
     private void assertLocation(HostId hid, DeviceId expDev, int expPort) {
@@ -403,6 +403,8 @@
         assertEquals("unex # regions", 3, cache.regionCount());
         assertEquals("unex # devices", 9, cache.deviceCount());
         assertEquals("unex # hosts", 18, cache.hostCount());
-        assertEquals("unex # hosts", 26, cache.linkCount());
+        assertEquals("unex # device-links", 8, cache.deviceLinkCount());
+        assertEquals("unex # edge-links", 18, cache.edgeLinkCount());
+        assertEquals("unex # synth-links", 0, cache.synthLinkCount());
     }
 }