ONOS-4326: TopoRegions: Implement basic structure of response to 'topo2Start' event.
- this is WIP: still need to extract data from model cache. Change-Id: I5ab843a1c352275a8da89964c886b660e3b8b616
Showing
6 changed files
with
463 additions
and
50 deletions
| ... | @@ -19,7 +19,7 @@ package org.onosproject.ui.model.topo; | ... | @@ -19,7 +19,7 @@ package org.onosproject.ui.model.topo; |
| 19 | /** | 19 | /** |
| 20 | * Represents a node drawn on the topology view (region, device, host). | 20 | * Represents a node drawn on the topology view (region, device, host). |
| 21 | */ | 21 | */ |
| 22 | -abstract class UiNode extends UiElement { | 22 | +public abstract class UiNode extends UiElement { |
| 23 | 23 | ||
| 24 | /** | 24 | /** |
| 25 | * Default "layer" tag. | 25 | * Default "layer" tag. | ... | ... |
| ... | @@ -37,12 +37,19 @@ import org.onosproject.ui.model.topo.UiClusterMember; | ... | @@ -37,12 +37,19 @@ import org.onosproject.ui.model.topo.UiClusterMember; |
| 37 | import org.onosproject.ui.model.topo.UiDevice; | 37 | import org.onosproject.ui.model.topo.UiDevice; |
| 38 | import org.onosproject.ui.model.topo.UiHost; | 38 | import org.onosproject.ui.model.topo.UiHost; |
| 39 | import org.onosproject.ui.model.topo.UiLink; | 39 | import org.onosproject.ui.model.topo.UiLink; |
| 40 | +import org.onosproject.ui.model.topo.UiNode; | ||
| 40 | import org.onosproject.ui.model.topo.UiRegion; | 41 | import org.onosproject.ui.model.topo.UiRegion; |
| 41 | import org.onosproject.ui.model.topo.UiTopoLayout; | 42 | import org.onosproject.ui.model.topo.UiTopoLayout; |
| 42 | 43 | ||
| 44 | +import java.util.ArrayList; | ||
| 45 | +import java.util.HashMap; | ||
| 46 | +import java.util.HashSet; | ||
| 43 | import java.util.List; | 47 | import java.util.List; |
| 48 | +import java.util.Map; | ||
| 49 | +import java.util.Set; | ||
| 44 | 50 | ||
| 45 | import static com.google.common.base.Preconditions.checkNotNull; | 51 | import static com.google.common.base.Preconditions.checkNotNull; |
| 52 | +import static org.onosproject.ui.model.topo.UiNode.LAYER_DEFAULT; | ||
| 46 | 53 | ||
| 47 | /** | 54 | /** |
| 48 | * Facility for creating JSON messages to send to the topology view in the | 55 | * Facility for creating JSON messages to send to the topology view in the |
| ... | @@ -50,6 +57,11 @@ import static com.google.common.base.Preconditions.checkNotNull; | ... | @@ -50,6 +57,11 @@ import static com.google.common.base.Preconditions.checkNotNull; |
| 50 | */ | 57 | */ |
| 51 | class Topo2Jsonifier { | 58 | class Topo2Jsonifier { |
| 52 | 59 | ||
| 60 | + private static final String E_DEF_NOT_LAST = | ||
| 61 | + "UiNode.LAYER_DEFAULT not last in layer list"; | ||
| 62 | + private static final String E_UNKNOWN_UI_NODE = | ||
| 63 | + "Unknown subclass of UiNode: "; | ||
| 64 | + | ||
| 53 | private final ObjectMapper mapper = new ObjectMapper(); | 65 | private final ObjectMapper mapper = new ObjectMapper(); |
| 54 | 66 | ||
| 55 | private ServiceDirectory directory; | 67 | private ServiceDirectory directory; |
| ... | @@ -87,7 +99,10 @@ class Topo2Jsonifier { | ... | @@ -87,7 +99,10 @@ class Topo2Jsonifier { |
| 87 | portStatsService = directory.get(PortStatisticsService.class); | 99 | portStatsService = directory.get(PortStatisticsService.class); |
| 88 | topologyService = directory.get(TopologyService.class); | 100 | topologyService = directory.get(TopologyService.class); |
| 89 | tunnelService = directory.get(TunnelService.class); | 101 | tunnelService = directory.get(TunnelService.class); |
| 102 | + } | ||
| 90 | 103 | ||
| 104 | + // for unit testing | ||
| 105 | + Topo2Jsonifier() { | ||
| 91 | } | 106 | } |
| 92 | 107 | ||
| 93 | private ObjectNode objectNode() { | 108 | private ObjectNode objectNode() { |
| ... | @@ -155,47 +170,104 @@ class Topo2Jsonifier { | ... | @@ -155,47 +170,104 @@ class Topo2Jsonifier { |
| 155 | * Returns a JSON representation of the region to display in the topology | 170 | * Returns a JSON representation of the region to display in the topology |
| 156 | * view. | 171 | * view. |
| 157 | * | 172 | * |
| 158 | - * @param region the region to transform to JSON | 173 | + * @param region the region to transform to JSON |
| 174 | + * @param subRegions the subregions within this region | ||
| 159 | * @return a JSON representation of the data | 175 | * @return a JSON representation of the data |
| 160 | */ | 176 | */ |
| 161 | - ObjectNode region(UiRegion region) { | 177 | + ObjectNode region(UiRegion region, Set<UiRegion> subRegions) { |
| 162 | ObjectNode payload = objectNode(); | 178 | ObjectNode payload = objectNode(); |
| 163 | - | ||
| 164 | if (region == null) { | 179 | if (region == null) { |
| 165 | payload.put("note", "no-region"); | 180 | payload.put("note", "no-region"); |
| 166 | return payload; | 181 | return payload; |
| 167 | } | 182 | } |
| 183 | + payload.put("id", region.idAsString()); | ||
| 184 | + payload.set("subregions", jsonSubRegions(subRegions)); | ||
| 168 | 185 | ||
| 169 | - payload.put("id", region.id().toString()); | 186 | + List<String> layerTags = region.layerOrder(); |
| 187 | + List<Set<UiNode>> splitDevices = splitByLayer(layerTags, region.devices()); | ||
| 188 | + List<Set<UiNode>> splitHosts = splitByLayer(layerTags, region.hosts()); | ||
| 189 | + Set<UiLink> links = region.links(); | ||
| 170 | 190 | ||
| 171 | - ArrayNode layerOrder = arrayNode(); | 191 | + payload.set("devices", jsonGrouped(splitDevices)); |
| 172 | - payload.set("layerOrder", layerOrder); | 192 | + payload.set("hosts", jsonGrouped(splitHosts)); |
| 173 | - region.layerOrder().forEach(layerOrder::add); | 193 | + payload.set("links", jsonLinks(links)); |
| 194 | + payload.set("layerOrder", jsonStrings(layerTags)); | ||
| 174 | 195 | ||
| 175 | - ArrayNode devices = arrayNode(); | 196 | + return payload; |
| 176 | - payload.set("devices", devices); | 197 | + } |
| 177 | - for (UiDevice device : region.devices()) { | ||
| 178 | - devices.add(json(device)); | ||
| 179 | - } | ||
| 180 | 198 | ||
| 181 | - ArrayNode hosts = arrayNode(); | 199 | + private ArrayNode jsonSubRegions(Set<UiRegion> subregions) { |
| 182 | - payload.set("hosts", hosts); | 200 | + ArrayNode kids = arrayNode(); |
| 183 | - for (UiHost host : region.hosts()) { | 201 | + if (subregions != null) { |
| 184 | - hosts.add(json(host)); | 202 | + subregions.forEach(s -> kids.add(jsonClosedRegion(s))); |
| 185 | } | 203 | } |
| 204 | + return kids; | ||
| 205 | + } | ||
| 186 | 206 | ||
| 187 | - ArrayNode links = arrayNode(); | 207 | + private ArrayNode jsonStrings(List<String> strings) { |
| 188 | - payload.set("links", links); | 208 | + ArrayNode array = arrayNode(); |
| 189 | - for (UiLink link : region.links()) { | 209 | + strings.forEach(array::add); |
| 190 | - links.add(json(link)); | 210 | + return array; |
| 191 | - } | 211 | + } |
| 212 | + | ||
| 213 | + private ArrayNode jsonLinks(Set<UiLink> links) { | ||
| 214 | + ArrayNode result = arrayNode(); | ||
| 215 | + links.forEach(lnk -> result.add(json(lnk))); | ||
| 216 | + return result; | ||
| 217 | + } | ||
| 218 | + | ||
| 219 | + private ArrayNode jsonGrouped(List<Set<UiNode>> groupedNodes) { | ||
| 220 | + ArrayNode result = arrayNode(); | ||
| 221 | + groupedNodes.forEach(g -> { | ||
| 222 | + ArrayNode subset = arrayNode(); | ||
| 223 | + g.forEach(n -> subset.add(json(n))); | ||
| 224 | + result.add(subset); | ||
| 225 | + }); | ||
| 226 | + return result; | ||
| 227 | + } | ||
| 228 | + | ||
| 229 | + /** | ||
| 230 | + * Returns a JSON payload that encapsulates the devices, hosts, links that | ||
| 231 | + * do not belong to any region. | ||
| 232 | + * | ||
| 233 | + * @param oDevices orphan devices | ||
| 234 | + * @param oHosts orphan hosts | ||
| 235 | + * @param oLinks orphan links | ||
| 236 | + * @param layerTags layer tags | ||
| 237 | + * @return a JSON representation of the data | ||
| 238 | + */ | ||
| 239 | + ObjectNode orphans(Set<UiDevice> oDevices, Set<UiHost> oHosts, | ||
| 240 | + Set<UiLink> oLinks, List<String> layerTags) { | ||
| 241 | + | ||
| 242 | + ObjectNode payload = objectNode(); | ||
| 243 | + | ||
| 244 | + List<Set<UiNode>> splitDevices = splitByLayer(layerTags, oDevices); | ||
| 245 | + List<Set<UiNode>> splitHosts = splitByLayer(layerTags, oHosts); | ||
| 246 | + | ||
| 247 | + payload.set("devices", jsonGrouped(splitDevices)); | ||
| 248 | + payload.set("hosts", jsonGrouped(splitHosts)); | ||
| 249 | + payload.set("links", jsonLinks(oLinks)); | ||
| 250 | + payload.set("layerOrder", jsonStrings(layerTags)); | ||
| 192 | 251 | ||
| 193 | return payload; | 252 | return payload; |
| 194 | } | 253 | } |
| 195 | 254 | ||
| 255 | + private ObjectNode json(UiNode node) { | ||
| 256 | + if (node instanceof UiRegion) { | ||
| 257 | + return jsonClosedRegion((UiRegion) node); | ||
| 258 | + } | ||
| 259 | + if (node instanceof UiDevice) { | ||
| 260 | + return json((UiDevice) node); | ||
| 261 | + } | ||
| 262 | + if (node instanceof UiHost) { | ||
| 263 | + return json((UiHost) node); | ||
| 264 | + } | ||
| 265 | + throw new IllegalStateException(E_UNKNOWN_UI_NODE + node.getClass()); | ||
| 266 | + } | ||
| 267 | + | ||
| 196 | private ObjectNode json(UiDevice device) { | 268 | private ObjectNode json(UiDevice device) { |
| 197 | ObjectNode node = objectNode() | 269 | ObjectNode node = objectNode() |
| 198 | - .put("id", device.id().toString()) | 270 | + .put("id", device.idAsString()) |
| 199 | .put("type", device.type()) | 271 | .put("type", device.type()) |
| 200 | .put("online", device.isOnline()) | 272 | .put("online", device.isOnline()) |
| 201 | .put("master", device.master().toString()) | 273 | .put("master", device.master().toString()) |
| ... | @@ -216,7 +288,7 @@ class Topo2Jsonifier { | ... | @@ -216,7 +288,7 @@ class Topo2Jsonifier { |
| 216 | 288 | ||
| 217 | private ObjectNode json(UiHost host) { | 289 | private ObjectNode json(UiHost host) { |
| 218 | return objectNode() | 290 | return objectNode() |
| 219 | - .put("id", host.id().toString()) | 291 | + .put("id", host.idAsString()) |
| 220 | .put("layer", host.layer()); | 292 | .put("layer", host.layer()); |
| 221 | // TODO: complete host details | 293 | // TODO: complete host details |
| 222 | } | 294 | } |
| ... | @@ -224,9 +296,101 @@ class Topo2Jsonifier { | ... | @@ -224,9 +296,101 @@ class Topo2Jsonifier { |
| 224 | 296 | ||
| 225 | private ObjectNode json(UiLink link) { | 297 | private ObjectNode json(UiLink link) { |
| 226 | return objectNode() | 298 | return objectNode() |
| 227 | - .put("id", link.id().toString()); | 299 | + .put("id", link.idAsString()); |
| 228 | // TODO: complete link details | 300 | // TODO: complete link details |
| 229 | } | 301 | } |
| 230 | 302 | ||
| 231 | 303 | ||
| 304 | + private ObjectNode jsonClosedRegion(UiRegion region) { | ||
| 305 | + return objectNode() | ||
| 306 | + .put("id", region.idAsString()); | ||
| 307 | + // TODO: complete closed-region details | ||
| 308 | + } | ||
| 309 | + | ||
| 310 | + | ||
| 311 | + /** | ||
| 312 | + * Returns a JSON array representation of a list of regions. Note that the | ||
| 313 | + * information about each region is limited to what needs to be used to | ||
| 314 | + * show the regions as nodes on the view. | ||
| 315 | + * | ||
| 316 | + * @param regions the regions | ||
| 317 | + * @return a JSON representation of the minimal region information | ||
| 318 | + */ | ||
| 319 | + public ArrayNode closedRegions(Set<UiRegion> regions) { | ||
| 320 | + ArrayNode array = arrayNode(); | ||
| 321 | + for (UiRegion r : regions) { | ||
| 322 | + array.add(jsonClosedRegion(r)); | ||
| 323 | + } | ||
| 324 | + return array; | ||
| 325 | + } | ||
| 326 | + | ||
| 327 | + /** | ||
| 328 | + * Returns a JSON array representation of a list of devices. | ||
| 329 | + * | ||
| 330 | + * @param devices the devices | ||
| 331 | + * @return a JSON representation of the devices | ||
| 332 | + */ | ||
| 333 | + public ArrayNode devices(Set<UiDevice> devices) { | ||
| 334 | + ArrayNode array = arrayNode(); | ||
| 335 | + for (UiDevice device : devices) { | ||
| 336 | + array.add(json(device)); | ||
| 337 | + } | ||
| 338 | + return array; | ||
| 339 | + } | ||
| 340 | + | ||
| 341 | + /** | ||
| 342 | + * Returns a JSON array representation of a list of hosts. | ||
| 343 | + * | ||
| 344 | + * @param hosts the hosts | ||
| 345 | + * @return a JSON representation of the hosts | ||
| 346 | + */ | ||
| 347 | + public ArrayNode hosts(Set<UiHost> hosts) { | ||
| 348 | + ArrayNode array = arrayNode(); | ||
| 349 | + for (UiHost host : hosts) { | ||
| 350 | + array.add(json(host)); | ||
| 351 | + } | ||
| 352 | + return array; | ||
| 353 | + } | ||
| 354 | + | ||
| 355 | + /** | ||
| 356 | + * Returns a JSON array representation of a list of links. | ||
| 357 | + * | ||
| 358 | + * @param links the links | ||
| 359 | + * @return a JSON representation of the links | ||
| 360 | + */ | ||
| 361 | + public ArrayNode links(Set<UiLink> links) { | ||
| 362 | + ArrayNode array = arrayNode(); | ||
| 363 | + for (UiLink link : links) { | ||
| 364 | + array.add(json(link)); | ||
| 365 | + } | ||
| 366 | + return array; | ||
| 367 | + } | ||
| 368 | + | ||
| 369 | + // package-private for unit testing | ||
| 370 | + List<Set<UiNode>> splitByLayer(List<String> layerTags, | ||
| 371 | + Set<? extends UiNode> nodes) { | ||
| 372 | + final int nLayers = layerTags.size(); | ||
| 373 | + if (!layerTags.get(nLayers - 1).equals(LAYER_DEFAULT)) { | ||
| 374 | + throw new IllegalArgumentException(E_DEF_NOT_LAST); | ||
| 375 | + } | ||
| 376 | + | ||
| 377 | + List<Set<UiNode>> splitList = new ArrayList<>(layerTags.size()); | ||
| 378 | + Map<String, Set<UiNode>> byLayer = new HashMap<>(layerTags.size()); | ||
| 379 | + | ||
| 380 | + for (String tag : layerTags) { | ||
| 381 | + Set<UiNode> set = new HashSet<>(); | ||
| 382 | + byLayer.put(tag, set); | ||
| 383 | + splitList.add(set); | ||
| 384 | + } | ||
| 385 | + | ||
| 386 | + for (UiNode n : nodes) { | ||
| 387 | + String which = n.layer(); | ||
| 388 | + if (!layerTags.contains(which)) { | ||
| 389 | + which = LAYER_DEFAULT; | ||
| 390 | + } | ||
| 391 | + byLayer.get(which).add(n); | ||
| 392 | + } | ||
| 393 | + | ||
| 394 | + return splitList; | ||
| 395 | + } | ||
| 232 | } | 396 | } | ... | ... |
| ... | @@ -17,6 +17,7 @@ | ... | @@ -17,6 +17,7 @@ |
| 17 | package org.onosproject.ui.impl.topo; | 17 | package org.onosproject.ui.impl.topo; |
| 18 | 18 | ||
| 19 | import com.fasterxml.jackson.databind.node.ObjectNode; | 19 | import com.fasterxml.jackson.databind.node.ObjectNode; |
| 20 | +import com.google.common.collect.ImmutableList; | ||
| 20 | import com.google.common.collect.ImmutableSet; | 21 | import com.google.common.collect.ImmutableSet; |
| 21 | import org.onlab.osgi.ServiceDirectory; | 22 | import org.onlab.osgi.ServiceDirectory; |
| 22 | import org.onosproject.ui.RequestHandler; | 23 | import org.onosproject.ui.RequestHandler; |
| ... | @@ -24,6 +25,9 @@ import org.onosproject.ui.UiConnection; | ... | @@ -24,6 +25,9 @@ import org.onosproject.ui.UiConnection; |
| 24 | import org.onosproject.ui.UiMessageHandler; | 25 | import org.onosproject.ui.UiMessageHandler; |
| 25 | import org.onosproject.ui.impl.UiWebSocket; | 26 | import org.onosproject.ui.impl.UiWebSocket; |
| 26 | import org.onosproject.ui.model.topo.UiClusterMember; | 27 | import org.onosproject.ui.model.topo.UiClusterMember; |
| 28 | +import org.onosproject.ui.model.topo.UiDevice; | ||
| 29 | +import org.onosproject.ui.model.topo.UiHost; | ||
| 30 | +import org.onosproject.ui.model.topo.UiLink; | ||
| 27 | import org.onosproject.ui.model.topo.UiRegion; | 31 | import org.onosproject.ui.model.topo.UiRegion; |
| 28 | import org.onosproject.ui.model.topo.UiTopoLayout; | 32 | import org.onosproject.ui.model.topo.UiTopoLayout; |
| 29 | import org.slf4j.Logger; | 33 | import org.slf4j.Logger; |
| ... | @@ -31,6 +35,9 @@ import org.slf4j.LoggerFactory; | ... | @@ -31,6 +35,9 @@ import org.slf4j.LoggerFactory; |
| 31 | 35 | ||
| 32 | import java.util.Collection; | 36 | import java.util.Collection; |
| 33 | import java.util.List; | 37 | import java.util.List; |
| 38 | +import java.util.Set; | ||
| 39 | + | ||
| 40 | +import static org.onosproject.ui.model.topo.UiNode.LAYER_DEFAULT; | ||
| 34 | 41 | ||
| 35 | /* | 42 | /* |
| 36 | NOTES: | 43 | NOTES: |
| ... | @@ -58,11 +65,14 @@ public class Topo2ViewMessageHandler extends UiMessageHandler { | ... | @@ -58,11 +65,14 @@ public class Topo2ViewMessageHandler extends UiMessageHandler { |
| 58 | private static final String TOPO2_STOP = "topo2Stop"; | 65 | private static final String TOPO2_STOP = "topo2Stop"; |
| 59 | 66 | ||
| 60 | // === Outbound event identifiers | 67 | // === Outbound event identifiers |
| 68 | + private static final String ALL_INSTANCES = "topo2AllInstances"; | ||
| 61 | private static final String CURRENT_LAYOUT = "topo2CurrentLayout"; | 69 | private static final String CURRENT_LAYOUT = "topo2CurrentLayout"; |
| 62 | private static final String CURRENT_REGION = "topo2CurrentRegion"; | 70 | private static final String CURRENT_REGION = "topo2CurrentRegion"; |
| 63 | - private static final String ALL_INSTANCES = "topo2AllInstances"; | 71 | + private static final String PEER_REGIONS = "topo2PeerRegions"; |
| 72 | + private static final String ORPHANS = "topo2Orphans"; | ||
| 64 | private static final String TOPO_START_DONE = "topo2StartDone"; | 73 | private static final String TOPO_START_DONE = "topo2StartDone"; |
| 65 | 74 | ||
| 75 | + | ||
| 66 | private UiTopoSession topoSession; | 76 | private UiTopoSession topoSession; |
| 67 | private Topo2Jsonifier t2json; | 77 | private Topo2Jsonifier t2json; |
| 68 | 78 | ||
| ... | @@ -99,18 +109,36 @@ public class Topo2ViewMessageHandler extends UiMessageHandler { | ... | @@ -99,18 +109,36 @@ public class Topo2ViewMessageHandler extends UiMessageHandler { |
| 99 | 109 | ||
| 100 | log.debug("topo2Start: {}", payload); | 110 | log.debug("topo2Start: {}", payload); |
| 101 | 111 | ||
| 112 | + // this is the list of ONOS cluster members | ||
| 102 | List<UiClusterMember> instances = topoSession.getAllInstances(); | 113 | List<UiClusterMember> instances = topoSession.getAllInstances(); |
| 103 | sendMessage(ALL_INSTANCES, t2json.instances(instances)); | 114 | sendMessage(ALL_INSTANCES, t2json.instances(instances)); |
| 104 | 115 | ||
| 116 | + // this is the layout that the user has chosen to display | ||
| 105 | UiTopoLayout currentLayout = topoSession.currentLayout(); | 117 | UiTopoLayout currentLayout = topoSession.currentLayout(); |
| 106 | sendMessage(CURRENT_LAYOUT, t2json.layout(currentLayout)); | 118 | sendMessage(CURRENT_LAYOUT, t2json.layout(currentLayout)); |
| 107 | 119 | ||
| 120 | + // this is the region that is associated with the current layout | ||
| 121 | + // this message includes details of the sub-regions, devices, | ||
| 122 | + // hosts, and links within the region | ||
| 123 | + // (as well as layer-order hints) | ||
| 108 | UiRegion region = topoSession.getRegion(currentLayout); | 124 | UiRegion region = topoSession.getRegion(currentLayout); |
| 109 | - sendMessage(CURRENT_REGION, t2json.region(region)); | 125 | + Set<UiRegion> kids = topoSession.getSubRegions(currentLayout); |
| 110 | - | 126 | + sendMessage(CURRENT_REGION, t2json.region(region, kids)); |
| 111 | - // TODO: send information about devices/hosts/links in non-region | 127 | + |
| 112 | - // TODO: send information about "linked, peer" regions | 128 | + // these are the regions that are siblings to this one |
| 113 | - | 129 | + Set<UiRegion> peers = topoSession.getPeerRegions(currentLayout); |
| 130 | + ObjectNode peersPayload = objectNode(); | ||
| 131 | + peersPayload.set("peers", t2json.closedRegions(peers)); | ||
| 132 | + sendMessage(PEER_REGIONS, peersPayload); | ||
| 133 | + | ||
| 134 | + // return devices, hosts, links belonging to no region | ||
| 135 | + Set<UiDevice> oDevices = topoSession.getOrphanDevices(); | ||
| 136 | + Set<UiHost> oHosts = topoSession.getOrphanHosts(); | ||
| 137 | + Set<UiLink> oLinks = topoSession.getOrphanLinks(); | ||
| 138 | + List<String> oLayers = getOrphanLayerOrder(); | ||
| 139 | + sendMessage(ORPHANS, t2json.orphans(oDevices, oHosts, oLinks, oLayers)); | ||
| 140 | + | ||
| 141 | + // finally, tell the UI that we are done | ||
| 114 | sendMessage(TOPO_START_DONE, null); | 142 | sendMessage(TOPO_START_DONE, null); |
| 115 | 143 | ||
| 116 | 144 | ||
| ... | @@ -122,6 +150,16 @@ public class Topo2ViewMessageHandler extends UiMessageHandler { | ... | @@ -122,6 +150,16 @@ public class Topo2ViewMessageHandler extends UiMessageHandler { |
| 122 | // sendAllHosts(); | 150 | // sendAllHosts(); |
| 123 | // sendTopoStartDone(); | 151 | // sendTopoStartDone(); |
| 124 | } | 152 | } |
| 153 | + | ||
| 154 | + | ||
| 155 | + } | ||
| 156 | + | ||
| 157 | + // TODO: we need to decide on how this should really get populated. | ||
| 158 | + // For example, to be "backward compatible", this should really be | ||
| 159 | + // [ LAYER_OPTICAL, LAYER_PACKET, LAYER_DEFAULT ] | ||
| 160 | + private List<String> getOrphanLayerOrder() { | ||
| 161 | + // NOTE that LAYER_DEFAULT must always be last in the array | ||
| 162 | + return ImmutableList.of(LAYER_DEFAULT); | ||
| 125 | } | 163 | } |
| 126 | 164 | ||
| 127 | private final class Topo2Stop extends RequestHandler { | 165 | private final class Topo2Stop extends RequestHandler { | ... | ... |
| ... | @@ -22,12 +22,17 @@ import org.onosproject.ui.impl.topo.model.UiModelEvent; | ... | @@ -22,12 +22,17 @@ import org.onosproject.ui.impl.topo.model.UiModelEvent; |
| 22 | import org.onosproject.ui.impl.topo.model.UiModelListener; | 22 | import org.onosproject.ui.impl.topo.model.UiModelListener; |
| 23 | import org.onosproject.ui.impl.topo.model.UiSharedTopologyModel; | 23 | import org.onosproject.ui.impl.topo.model.UiSharedTopologyModel; |
| 24 | import org.onosproject.ui.model.topo.UiClusterMember; | 24 | import org.onosproject.ui.model.topo.UiClusterMember; |
| 25 | +import org.onosproject.ui.model.topo.UiDevice; | ||
| 26 | +import org.onosproject.ui.model.topo.UiHost; | ||
| 27 | +import org.onosproject.ui.model.topo.UiLink; | ||
| 25 | import org.onosproject.ui.model.topo.UiRegion; | 28 | import org.onosproject.ui.model.topo.UiRegion; |
| 26 | import org.onosproject.ui.model.topo.UiTopoLayout; | 29 | import org.onosproject.ui.model.topo.UiTopoLayout; |
| 27 | import org.slf4j.Logger; | 30 | import org.slf4j.Logger; |
| 28 | import org.slf4j.LoggerFactory; | 31 | import org.slf4j.LoggerFactory; |
| 29 | 32 | ||
| 33 | +import java.util.Collections; | ||
| 30 | import java.util.List; | 34 | import java.util.List; |
| 35 | +import java.util.Set; | ||
| 31 | 36 | ||
| 32 | /** | 37 | /** |
| 33 | * Coordinates with the {@link UiTopoLayoutService} to access | 38 | * Coordinates with the {@link UiTopoLayoutService} to access |
| ... | @@ -44,6 +49,7 @@ import java.util.List; | ... | @@ -44,6 +49,7 @@ import java.util.List; |
| 44 | * interact with it when topo-related events come in from the client. | 49 | * interact with it when topo-related events come in from the client. |
| 45 | */ | 50 | */ |
| 46 | public class UiTopoSession implements UiModelListener { | 51 | public class UiTopoSession implements UiModelListener { |
| 52 | + | ||
| 47 | private final Logger log = LoggerFactory.getLogger(getClass()); | 53 | private final Logger log = LoggerFactory.getLogger(getClass()); |
| 48 | 54 | ||
| 49 | private final UiWebSocket webSocket; | 55 | private final UiWebSocket webSocket; |
| ... | @@ -73,6 +79,13 @@ public class UiTopoSession implements UiModelListener { | ... | @@ -73,6 +79,13 @@ public class UiTopoSession implements UiModelListener { |
| 73 | this.layoutService = layoutService; | 79 | this.layoutService = layoutService; |
| 74 | } | 80 | } |
| 75 | 81 | ||
| 82 | + // constructs a neutered instance, for unit testing | ||
| 83 | + UiTopoSession() { | ||
| 84 | + webSocket = null; | ||
| 85 | + username = null; | ||
| 86 | + sharedModel = null; | ||
| 87 | + } | ||
| 88 | + | ||
| 76 | /** | 89 | /** |
| 77 | * Initializes the session; registering with the shared model. | 90 | * Initializes the session; registering with the shared model. |
| 78 | */ | 91 | */ |
| ... | @@ -154,6 +167,68 @@ public class UiTopoSession implements UiModelListener { | ... | @@ -154,6 +167,68 @@ public class UiTopoSession implements UiModelListener { |
| 154 | * @return region that the layout is based upon | 167 | * @return region that the layout is based upon |
| 155 | */ | 168 | */ |
| 156 | public UiRegion getRegion(UiTopoLayout layout) { | 169 | public UiRegion getRegion(UiTopoLayout layout) { |
| 157 | - return sharedModel.getRegion(layout); | 170 | + return sharedModel.getRegion(layout.regionId()); |
| 171 | + } | ||
| 172 | + | ||
| 173 | + /** | ||
| 174 | + * Returns the regions that are "peers" to this region. That is, based on | ||
| 175 | + * the layout the user is viewing, all the regions that are associated with | ||
| 176 | + * layouts that are children of the parent layout to this layout. | ||
| 177 | + * | ||
| 178 | + * @param layout the layout being viewed | ||
| 179 | + * @return all regions that are "siblings" to this layout's region | ||
| 180 | + */ | ||
| 181 | + public Set<UiRegion> getPeerRegions(UiTopoLayout layout) { | ||
| 182 | + UiRegion currentRegion = getRegion(layout); | ||
| 183 | + | ||
| 184 | + // TODO: consult topo layout service to get hierarchy info... | ||
| 185 | + // TODO: then consult shared model to get regions | ||
| 186 | + return Collections.emptySet(); | ||
| 187 | + } | ||
| 188 | + | ||
| 189 | + /** | ||
| 190 | + * Returns the subregions of the region in the specified layout. | ||
| 191 | + * | ||
| 192 | + * @param layout the layout being viewed | ||
| 193 | + * @return all regions that are "contained within" this layout's region | ||
| 194 | + */ | ||
| 195 | + public Set<UiRegion> getSubRegions(UiTopoLayout layout) { | ||
| 196 | + UiRegion currentRegion = getRegion(layout); | ||
| 197 | + | ||
| 198 | + // TODO: consult topo layout service to get child layouts... | ||
| 199 | + // TODO: then consult shared model to get regions | ||
| 200 | + return Collections.emptySet(); | ||
| 201 | + } | ||
| 202 | + | ||
| 203 | + | ||
| 204 | + /** | ||
| 205 | + * Returns all devices that are not in a region. | ||
| 206 | + * | ||
| 207 | + * @return all devices not in a region | ||
| 208 | + */ | ||
| 209 | + public Set<UiDevice> getOrphanDevices() { | ||
| 210 | + // TODO: get devices with no region | ||
| 211 | + return Collections.emptySet(); | ||
| 212 | + } | ||
| 213 | + | ||
| 214 | + /** | ||
| 215 | + * Returns all hosts that are not in a region. | ||
| 216 | + * | ||
| 217 | + * @return all hosts not in a region | ||
| 218 | + */ | ||
| 219 | + public Set<UiHost> getOrphanHosts() { | ||
| 220 | + // TODO: get hosts with no region | ||
| 221 | + return Collections.emptySet(); | ||
| 222 | + } | ||
| 223 | + | ||
| 224 | + /** | ||
| 225 | + * Returns all links that are not in a region. | ||
| 226 | + * | ||
| 227 | + * @return all links not in a region | ||
| 228 | + */ | ||
| 229 | + public Set<UiLink> getOrphanLinks() { | ||
| 230 | + // TODO: get links with no region | ||
| 231 | + return Collections.emptySet(); | ||
| 158 | } | 232 | } |
| 233 | + | ||
| 159 | } | 234 | } | ... | ... |
| ... | @@ -55,6 +55,7 @@ import org.onosproject.net.link.LinkListener; | ... | @@ -55,6 +55,7 @@ import org.onosproject.net.link.LinkListener; |
| 55 | import org.onosproject.net.link.LinkService; | 55 | import org.onosproject.net.link.LinkService; |
| 56 | import org.onosproject.net.region.Region; | 56 | import org.onosproject.net.region.Region; |
| 57 | import org.onosproject.net.region.RegionEvent; | 57 | import org.onosproject.net.region.RegionEvent; |
| 58 | +import org.onosproject.net.region.RegionId; | ||
| 58 | import org.onosproject.net.region.RegionListener; | 59 | import org.onosproject.net.region.RegionListener; |
| 59 | import org.onosproject.net.region.RegionService; | 60 | import org.onosproject.net.region.RegionService; |
| 60 | import org.onosproject.net.statistic.StatisticService; | 61 | import org.onosproject.net.statistic.StatisticService; |
| ... | @@ -62,15 +63,11 @@ import org.onosproject.net.topology.TopologyService; | ... | @@ -62,15 +63,11 @@ import org.onosproject.net.topology.TopologyService; |
| 62 | import org.onosproject.ui.impl.topo.UiTopoSession; | 63 | import org.onosproject.ui.impl.topo.UiTopoSession; |
| 63 | import org.onosproject.ui.model.ServiceBundle; | 64 | import org.onosproject.ui.model.ServiceBundle; |
| 64 | import org.onosproject.ui.model.topo.UiClusterMember; | 65 | import org.onosproject.ui.model.topo.UiClusterMember; |
| 65 | -import org.onosproject.ui.model.topo.UiElement; | ||
| 66 | import org.onosproject.ui.model.topo.UiRegion; | 66 | import org.onosproject.ui.model.topo.UiRegion; |
| 67 | -import org.onosproject.ui.model.topo.UiTopoLayout; | ||
| 68 | import org.slf4j.Logger; | 67 | import org.slf4j.Logger; |
| 69 | import org.slf4j.LoggerFactory; | 68 | import org.slf4j.LoggerFactory; |
| 70 | 69 | ||
| 71 | -import java.util.HashSet; | ||
| 72 | import java.util.List; | 70 | import java.util.List; |
| 73 | -import java.util.Set; | ||
| 74 | import java.util.concurrent.ExecutorService; | 71 | import java.util.concurrent.ExecutorService; |
| 75 | import java.util.concurrent.Executors; | 72 | import java.util.concurrent.Executors; |
| 76 | 73 | ||
| ... | @@ -210,23 +207,14 @@ public final class UiSharedTopologyModel | ... | @@ -210,23 +207,14 @@ public final class UiSharedTopologyModel |
| 210 | return cache.getAllClusterMembers(); | 207 | return cache.getAllClusterMembers(); |
| 211 | } | 208 | } |
| 212 | 209 | ||
| 213 | - public Set<UiElement> getElements(UiTopoLayout layout) { | ||
| 214 | - Set<UiElement> results = new HashSet<>(); | ||
| 215 | - | ||
| 216 | - // TODO: figure out how to extract the appropriate nodes | ||
| 217 | - // from the cache, for the given layout. | ||
| 218 | - | ||
| 219 | - return results; | ||
| 220 | - } | ||
| 221 | - | ||
| 222 | /** | 210 | /** |
| 223 | - * Returns the region for the given layout. | 211 | + * Returns the region for the given identifier. |
| 224 | * | 212 | * |
| 225 | - * @param layout layout filter | 213 | + * @param id region identifier |
| 226 | - * @return the region the layout is based upon | 214 | + * @return the region |
| 227 | */ | 215 | */ |
| 228 | - public UiRegion getRegion(UiTopoLayout layout) { | 216 | + public UiRegion getRegion(RegionId id) { |
| 229 | - return cache.accessRegion(layout.regionId()); | 217 | + return cache.accessRegion(id); |
| 230 | } | 218 | } |
| 231 | 219 | ||
| 232 | // ===================================================================== | 220 | // ===================================================================== | ... | ... |
| 1 | +/* | ||
| 2 | + * Copyright 2016-present Open Networking Laboratory | ||
| 3 | + * | ||
| 4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 5 | + * you may not use this file except in compliance with the License. | ||
| 6 | + * You may obtain a copy of the License at | ||
| 7 | + * | ||
| 8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
| 9 | + * | ||
| 10 | + * Unless required by applicable law or agreed to in writing, software | ||
| 11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
| 12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 13 | + * See the License for the specific language governing permissions and | ||
| 14 | + * limitations under the License. | ||
| 15 | + */ | ||
| 16 | + | ||
| 17 | +package org.onosproject.ui.impl.topo; | ||
| 18 | + | ||
| 19 | +import com.google.common.collect.ImmutableList; | ||
| 20 | +import com.google.common.collect.ImmutableSet; | ||
| 21 | +import org.junit.Test; | ||
| 22 | +import org.onosproject.ui.impl.AbstractUiImplTest; | ||
| 23 | +import org.onosproject.ui.model.topo.UiNode; | ||
| 24 | + | ||
| 25 | +import java.util.List; | ||
| 26 | +import java.util.Set; | ||
| 27 | + | ||
| 28 | +import static org.junit.Assert.assertEquals; | ||
| 29 | +import static org.onosproject.ui.model.topo.UiNode.LAYER_DEFAULT; | ||
| 30 | +import static org.onosproject.ui.model.topo.UiNode.LAYER_OPTICAL; | ||
| 31 | +import static org.onosproject.ui.model.topo.UiNode.LAYER_PACKET; | ||
| 32 | + | ||
| 33 | +/** | ||
| 34 | + * Unit tests for {@link Topo2ViewMessageHandler}. | ||
| 35 | + */ | ||
| 36 | +public class Topo2JsonifierTest extends AbstractUiImplTest { | ||
| 37 | + | ||
| 38 | + // mock node class for testing | ||
| 39 | + private static class MockNode extends UiNode { | ||
| 40 | + private final String id; | ||
| 41 | + | ||
| 42 | + MockNode(String id, String layer) { | ||
| 43 | + this.id = id; | ||
| 44 | + setLayer(layer); | ||
| 45 | + } | ||
| 46 | + | ||
| 47 | + @Override | ||
| 48 | + public String idAsString() { | ||
| 49 | + return id; | ||
| 50 | + } | ||
| 51 | + | ||
| 52 | + @Override | ||
| 53 | + public String toString() { | ||
| 54 | + return id; | ||
| 55 | + } | ||
| 56 | + } | ||
| 57 | + | ||
| 58 | + private static final List<String> ALL_TAGS = ImmutableList.of( | ||
| 59 | + LAYER_OPTICAL, LAYER_PACKET, LAYER_DEFAULT | ||
| 60 | + ); | ||
| 61 | + | ||
| 62 | + private static final List<String> PKT_DEF_TAGS = ImmutableList.of( | ||
| 63 | + LAYER_PACKET, LAYER_DEFAULT | ||
| 64 | + ); | ||
| 65 | + | ||
| 66 | + private static final List<String> DEF_TAG_ONLY = ImmutableList.of( | ||
| 67 | + LAYER_DEFAULT | ||
| 68 | + ); | ||
| 69 | + | ||
| 70 | + private static final MockNode NODE_A = new MockNode("A-O", LAYER_OPTICAL); | ||
| 71 | + private static final MockNode NODE_B = new MockNode("B-P", LAYER_PACKET); | ||
| 72 | + private static final MockNode NODE_C = new MockNode("C-O", LAYER_OPTICAL); | ||
| 73 | + private static final MockNode NODE_D = new MockNode("D-D", LAYER_DEFAULT); | ||
| 74 | + private static final MockNode NODE_E = new MockNode("E-P", LAYER_PACKET); | ||
| 75 | + private static final MockNode NODE_F = new MockNode("F-r", "random"); | ||
| 76 | + | ||
| 77 | + private static final Set<MockNode> NODES = ImmutableSet.of( | ||
| 78 | + NODE_A, NODE_B, NODE_C, NODE_D, NODE_E, NODE_F | ||
| 79 | + ); | ||
| 80 | + | ||
| 81 | + private Topo2Jsonifier t2 = new Topo2Jsonifier(); | ||
| 82 | + | ||
| 83 | + @Test | ||
| 84 | + public void threeLayers() { | ||
| 85 | + print("threeLayers()"); | ||
| 86 | + | ||
| 87 | + List<Set<UiNode>> result = t2.splitByLayer(ALL_TAGS, NODES); | ||
| 88 | + print(result); | ||
| 89 | + | ||
| 90 | + assertEquals("wrong split size", 3, result.size()); | ||
| 91 | + Set<UiNode> opt = result.get(0); | ||
| 92 | + Set<UiNode> pkt = result.get(1); | ||
| 93 | + Set<UiNode> def = result.get(2); | ||
| 94 | + | ||
| 95 | + assertEquals("opt bad size", 2, opt.size()); | ||
| 96 | + assertEquals("missing node A", true, opt.contains(NODE_A)); | ||
| 97 | + assertEquals("missing node C", true, opt.contains(NODE_C)); | ||
| 98 | + | ||
| 99 | + assertEquals("pkt bad size", 2, pkt.size()); | ||
| 100 | + assertEquals("missing node B", true, pkt.contains(NODE_B)); | ||
| 101 | + assertEquals("missing node E", true, pkt.contains(NODE_E)); | ||
| 102 | + | ||
| 103 | + assertEquals("def bad size", 2, def.size()); | ||
| 104 | + assertEquals("missing node D", true, def.contains(NODE_D)); | ||
| 105 | + assertEquals("missing node F", true, def.contains(NODE_F)); | ||
| 106 | + } | ||
| 107 | + | ||
| 108 | + @Test | ||
| 109 | + public void twoLayers() { | ||
| 110 | + print("twoLayers()"); | ||
| 111 | + | ||
| 112 | + List<Set<UiNode>> result = t2.splitByLayer(PKT_DEF_TAGS, NODES); | ||
| 113 | + print(result); | ||
| 114 | + | ||
| 115 | + assertEquals("wrong split size", 2, result.size()); | ||
| 116 | + Set<UiNode> pkt = result.get(0); | ||
| 117 | + Set<UiNode> def = result.get(1); | ||
| 118 | + | ||
| 119 | + assertEquals("pkt bad size", 2, pkt.size()); | ||
| 120 | + assertEquals("missing node B", true, pkt.contains(NODE_B)); | ||
| 121 | + assertEquals("missing node E", true, pkt.contains(NODE_E)); | ||
| 122 | + | ||
| 123 | + assertEquals("def bad size", 4, def.size()); | ||
| 124 | + assertEquals("missing node D", true, def.contains(NODE_D)); | ||
| 125 | + assertEquals("missing node F", true, def.contains(NODE_F)); | ||
| 126 | + assertEquals("missing node A", true, def.contains(NODE_A)); | ||
| 127 | + assertEquals("missing node C", true, def.contains(NODE_C)); | ||
| 128 | + } | ||
| 129 | + | ||
| 130 | + @Test | ||
| 131 | + public void oneLayer() { | ||
| 132 | + print("oneLayer()"); | ||
| 133 | + | ||
| 134 | + List<Set<UiNode>> result = t2.splitByLayer(DEF_TAG_ONLY, NODES); | ||
| 135 | + print(result); | ||
| 136 | + | ||
| 137 | + assertEquals("wrong split size", 1, result.size()); | ||
| 138 | + Set<UiNode> def = result.get(0); | ||
| 139 | + | ||
| 140 | + assertEquals("def bad size", 6, def.size()); | ||
| 141 | + assertEquals("missing node D", true, def.contains(NODE_D)); | ||
| 142 | + assertEquals("missing node F", true, def.contains(NODE_F)); | ||
| 143 | + assertEquals("missing node A", true, def.contains(NODE_A)); | ||
| 144 | + assertEquals("missing node C", true, def.contains(NODE_C)); | ||
| 145 | + assertEquals("missing node B", true, def.contains(NODE_B)); | ||
| 146 | + assertEquals("missing node E", true, def.contains(NODE_E)); | ||
| 147 | + } | ||
| 148 | +} |
-
Please register or login to post a comment