Simon Hunt

ONOS-4971: Synthetic Link Data -- WIP, merge anyway

- created temp Topology2 View (topoX) to "process" and display topology data.
- made root layout parent of itself (just like /.. = /) to simplify layout hierarchy operations.
- added nodeType property to JSON rep of regions/devices/hosts.
- augmented peers to include devices.
- added skeleton topo2NavRegion event.

Change-Id: I8219125d7dfe33d211350ae27111a3d9de6eb4ca
...@@ -63,7 +63,7 @@ public interface UiTopoLayoutService { ...@@ -63,7 +63,7 @@ public interface UiTopoLayoutService {
63 * @param layoutId layout identifier 63 * @param layoutId layout identifier
64 * @return set of peer layouts; empty set if layout has no peers 64 * @return set of peer layouts; empty set if layout has no peers
65 */ 65 */
66 - Set<UiTopoLayout> getPeers(UiTopoLayoutId layoutId); 66 + Set<UiTopoLayout> getPeerLayouts(UiTopoLayoutId layoutId);
67 67
68 /** 68 /**
69 * Returns the set of the child layouts of the specified layout. 69 * Returns the set of the child layouts of the specified layout.
......
...@@ -39,7 +39,8 @@ public class UiTopoLayout { ...@@ -39,7 +39,8 @@ public class UiTopoLayout {
39 public UiTopoLayout(UiTopoLayoutId id, Region region, UiTopoLayoutId parent) { 39 public UiTopoLayout(UiTopoLayoutId id, Region region, UiTopoLayoutId parent) {
40 this.id = id; 40 this.id = id;
41 this.region = region; 41 this.region = region;
42 - this.parent = parent; 42 + // NOTE: root layout is its own parent...
43 + this.parent = parent != null ? parent : this.id;
43 } 44 }
44 45
45 @Override 46 @Override
...@@ -85,5 +86,13 @@ public class UiTopoLayout { ...@@ -85,5 +86,13 @@ public class UiTopoLayout {
85 return parent; 86 return parent;
86 } 87 }
87 88
88 - // TODO: additional properties pertinent to the layout 89 + /**
90 + * Returns true if this layout instance is at the top of the
91 + * hierarchy tree.
92 + *
93 + * @return true if this is the root layout
94 + */
95 + public boolean isRoot() {
96 + return id.equals(parent);
97 + }
89 } 98 }
......
...@@ -134,6 +134,7 @@ public class UiExtensionManager ...@@ -134,6 +134,7 @@ public class UiExtensionManager
134 134
135 // FIXME: leave commented out for now, while still under development 135 // FIXME: leave commented out for now, while still under development
136 // new UiView(NETWORK, "topo2", "New-Topo"), 136 // new UiView(NETWORK, "topo2", "New-Topo"),
137 +// new UiView(NETWORK, "topoX", "Topo-X"),
137 138
138 new UiView(NETWORK, "device", "Devices", "nav_devs"), 139 new UiView(NETWORK, "device", "Devices", "nav_devs"),
139 new UiViewHidden("flow"), 140 new UiViewHidden("flow"),
......
...@@ -40,6 +40,8 @@ import org.onosproject.ui.model.topo.UiLink; ...@@ -40,6 +40,8 @@ import org.onosproject.ui.model.topo.UiLink;
40 import org.onosproject.ui.model.topo.UiNode; 40 import org.onosproject.ui.model.topo.UiNode;
41 import org.onosproject.ui.model.topo.UiRegion; 41 import org.onosproject.ui.model.topo.UiRegion;
42 import org.onosproject.ui.model.topo.UiTopoLayout; 42 import org.onosproject.ui.model.topo.UiTopoLayout;
43 +import org.slf4j.Logger;
44 +import org.slf4j.LoggerFactory;
43 45
44 import java.util.ArrayList; 46 import java.util.ArrayList;
45 import java.util.HashMap; 47 import java.util.HashMap;
...@@ -62,6 +64,12 @@ class Topo2Jsonifier { ...@@ -62,6 +64,12 @@ class Topo2Jsonifier {
62 private static final String E_UNKNOWN_UI_NODE = 64 private static final String E_UNKNOWN_UI_NODE =
63 "Unknown subclass of UiNode: "; 65 "Unknown subclass of UiNode: ";
64 66
67 + private static final String REGION = "region";
68 + private static final String DEVICE = "device";
69 + private static final String HOST = "host";
70 +
71 + private final Logger log = LoggerFactory.getLogger(getClass());
72 +
65 private final ObjectMapper mapper = new ObjectMapper(); 73 private final ObjectMapper mapper = new ObjectMapper();
66 74
67 private ServiceDirectory directory; 75 private ServiceDirectory directory;
...@@ -245,6 +253,7 @@ class Topo2Jsonifier { ...@@ -245,6 +253,7 @@ class Topo2Jsonifier {
245 private ObjectNode json(UiDevice device) { 253 private ObjectNode json(UiDevice device) {
246 ObjectNode node = objectNode() 254 ObjectNode node = objectNode()
247 .put("id", device.idAsString()) 255 .put("id", device.idAsString())
256 + .put("nodeType", DEVICE)
248 .put("type", device.type()) 257 .put("type", device.type())
249 .put("online", device.isOnline()) 258 .put("online", device.isOnline())
250 .put("master", nullIsEmpty(device.master())) 259 .put("master", nullIsEmpty(device.master()))
...@@ -266,6 +275,7 @@ class Topo2Jsonifier { ...@@ -266,6 +275,7 @@ class Topo2Jsonifier {
266 private ObjectNode json(UiHost host) { 275 private ObjectNode json(UiHost host) {
267 return objectNode() 276 return objectNode()
268 .put("id", host.idAsString()) 277 .put("id", host.idAsString())
278 + .put("nodeType", HOST)
269 .put("layer", host.layer()); 279 .put("layer", host.layer());
270 // TODO: complete host details 280 // TODO: complete host details
271 } 281 }
...@@ -281,10 +291,31 @@ class Topo2Jsonifier { ...@@ -281,10 +291,31 @@ class Topo2Jsonifier {
281 private ObjectNode jsonClosedRegion(UiRegion region) { 291 private ObjectNode jsonClosedRegion(UiRegion region) {
282 return objectNode() 292 return objectNode()
283 .put("id", region.idAsString()) 293 .put("id", region.idAsString())
294 + .put("nodeType", REGION)
284 .put("nDevs", region.deviceCount()); 295 .put("nDevs", region.deviceCount());
285 // TODO: complete closed-region details 296 // TODO: complete closed-region details
286 } 297 }
287 298
299 + /**
300 + * Returns a JSON array representation of a set of regions/devices. Note
301 + * that the information is sufficient for showing regions as nodes.
302 + *
303 + * @param nodes the nodes
304 + * @return a JSON representation of the nodes
305 + */
306 + public ArrayNode closedNodes(Set<UiNode> nodes) {
307 + ArrayNode array = arrayNode();
308 + for (UiNode node: nodes) {
309 + if (node instanceof UiRegion) {
310 + array.add(jsonClosedRegion((UiRegion) node));
311 + } else if (node instanceof UiDevice) {
312 + array.add(json((UiDevice) node));
313 + } else {
314 + log.warn("Unexpected node instance: {}", node.getClass());
315 + }
316 + }
317 + return array;
318 + }
288 319
289 /** 320 /**
290 * Returns a JSON array representation of a list of regions. Note that the 321 * Returns a JSON array representation of a list of regions. Note that the
......
...@@ -24,6 +24,7 @@ import org.onosproject.ui.UiConnection; ...@@ -24,6 +24,7 @@ import org.onosproject.ui.UiConnection;
24 import org.onosproject.ui.UiMessageHandler; 24 import org.onosproject.ui.UiMessageHandler;
25 import org.onosproject.ui.impl.UiWebSocket; 25 import org.onosproject.ui.impl.UiWebSocket;
26 import org.onosproject.ui.model.topo.UiClusterMember; 26 import org.onosproject.ui.model.topo.UiClusterMember;
27 +import org.onosproject.ui.model.topo.UiNode;
27 import org.onosproject.ui.model.topo.UiRegion; 28 import org.onosproject.ui.model.topo.UiRegion;
28 import org.onosproject.ui.model.topo.UiTopoLayout; 29 import org.onosproject.ui.model.topo.UiTopoLayout;
29 import org.slf4j.Logger; 30 import org.slf4j.Logger;
...@@ -55,8 +56,9 @@ public class Topo2ViewMessageHandler extends UiMessageHandler { ...@@ -55,8 +56,9 @@ public class Topo2ViewMessageHandler extends UiMessageHandler {
55 private final Logger log = LoggerFactory.getLogger(getClass()); 56 private final Logger log = LoggerFactory.getLogger(getClass());
56 57
57 // === Inbound event identifiers 58 // === Inbound event identifiers
58 - private static final String TOPO2_START = "topo2Start"; 59 + private static final String START = "topo2Start";
59 - private static final String TOPO2_STOP = "topo2Stop"; 60 + private static final String NAV_REGION = "topo2navRegion";
61 + private static final String STOP = "topo2Stop";
60 62
61 // === Outbound event identifiers 63 // === Outbound event identifiers
62 private static final String ALL_INSTANCES = "topo2AllInstances"; 64 private static final String ALL_INSTANCES = "topo2AllInstances";
...@@ -83,6 +85,7 @@ public class Topo2ViewMessageHandler extends UiMessageHandler { ...@@ -83,6 +85,7 @@ public class Topo2ViewMessageHandler extends UiMessageHandler {
83 protected Collection<RequestHandler> createRequestHandlers() { 85 protected Collection<RequestHandler> createRequestHandlers() {
84 return ImmutableSet.of( 86 return ImmutableSet.of(
85 new Topo2Start(), 87 new Topo2Start(),
88 + new Topo2NavRegion(),
86 new Topo2Stop() 89 new Topo2Stop()
87 ); 90 );
88 } 91 }
...@@ -92,7 +95,7 @@ public class Topo2ViewMessageHandler extends UiMessageHandler { ...@@ -92,7 +95,7 @@ public class Topo2ViewMessageHandler extends UiMessageHandler {
92 95
93 private final class Topo2Start extends RequestHandler { 96 private final class Topo2Start extends RequestHandler {
94 private Topo2Start() { 97 private Topo2Start() {
95 - super(TOPO2_START); 98 + super(START);
96 } 99 }
97 100
98 @Override 101 @Override
...@@ -124,10 +127,10 @@ public class Topo2ViewMessageHandler extends UiMessageHandler { ...@@ -124,10 +127,10 @@ public class Topo2ViewMessageHandler extends UiMessageHandler {
124 Set<UiRegion> kids = topoSession.getSubRegions(currentLayout); 127 Set<UiRegion> kids = topoSession.getSubRegions(currentLayout);
125 sendMessage(CURRENT_REGION, t2json.region(region, kids)); 128 sendMessage(CURRENT_REGION, t2json.region(region, kids));
126 129
127 - // these are the regions that are siblings to this one 130 + // these are the regions/devices that are siblings to this region
128 - Set<UiRegion> peers = topoSession.getPeerRegions(currentLayout); 131 + Set<UiNode> peers = topoSession.getPeerNodes(currentLayout);
129 ObjectNode peersPayload = objectNode(); 132 ObjectNode peersPayload = objectNode();
130 - peersPayload.set("peers", t2json.closedRegions(peers)); 133 + peersPayload.set("peers", t2json.closedNodes(peers));
131 sendMessage(PEER_REGIONS, peersPayload); 134 sendMessage(PEER_REGIONS, peersPayload);
132 135
133 // finally, tell the UI that we are done : TODO review / delete?? 136 // finally, tell the UI that we are done : TODO review / delete??
...@@ -146,9 +149,22 @@ public class Topo2ViewMessageHandler extends UiMessageHandler { ...@@ -146,9 +149,22 @@ public class Topo2ViewMessageHandler extends UiMessageHandler {
146 149
147 } 150 }
148 151
152 + private final class Topo2NavRegion extends RequestHandler {
153 + private Topo2NavRegion() {
154 + super(NAV_REGION);
155 + }
156 +
157 + @Override
158 + public void process(long sid, ObjectNode payload) {
159 + String dir = string(payload, "dir");
160 + String rid = string(payload, "rid");
161 + log.debug("NavRegion: dir={}, rid={}", dir, rid);
162 + }
163 + }
164 +
149 private final class Topo2Stop extends RequestHandler { 165 private final class Topo2Stop extends RequestHandler {
150 private Topo2Stop() { 166 private Topo2Stop() {
151 - super(TOPO2_STOP); 167 + super(STOP);
152 } 168 }
153 169
154 @Override 170 @Override
......
...@@ -111,15 +111,17 @@ public class UiTopoLayoutManager implements UiTopoLayoutService { ...@@ -111,15 +111,17 @@ public class UiTopoLayoutManager implements UiTopoLayoutService {
111 } 111 }
112 112
113 @Override 113 @Override
114 - public Set<UiTopoLayout> getPeers(UiTopoLayoutId layoutId) { 114 + public Set<UiTopoLayout> getPeerLayouts(UiTopoLayoutId layoutId) {
115 checkNotNull(layoutId, ID_NULL); 115 checkNotNull(layoutId, ID_NULL);
116 +
116 UiTopoLayout layout = layoutMap.get(layoutId); 117 UiTopoLayout layout = layoutMap.get(layoutId);
117 - if (layout == null) { 118 + if (layout == null || layout.isRoot()) {
118 return Collections.emptySet(); 119 return Collections.emptySet();
119 } 120 }
120 121
121 UiTopoLayoutId parentId = layout.parent(); 122 UiTopoLayoutId parentId = layout.parent();
122 return layoutMap.values().stream() 123 return layoutMap.values().stream()
124 + // all layouts who are NOT me and who share my parent...
123 .filter(l -> !Objects.equals(l.id(), layoutId) && 125 .filter(l -> !Objects.equals(l.id(), layoutId) &&
124 Objects.equals(l.parent(), parentId)) 126 Objects.equals(l.parent(), parentId))
125 .collect(Collectors.toSet()); 127 .collect(Collectors.toSet());
...@@ -129,7 +131,7 @@ public class UiTopoLayoutManager implements UiTopoLayoutService { ...@@ -129,7 +131,7 @@ public class UiTopoLayoutManager implements UiTopoLayoutService {
129 public Set<UiTopoLayout> getChildren(UiTopoLayoutId layoutId) { 131 public Set<UiTopoLayout> getChildren(UiTopoLayoutId layoutId) {
130 checkNotNull(layoutId, ID_NULL); 132 checkNotNull(layoutId, ID_NULL);
131 return layoutMap.values().stream() 133 return layoutMap.values().stream()
132 - .filter(l -> Objects.equals(l.parent(), layoutId)) 134 + .filter(l -> !l.isRoot() && Objects.equals(l.parent(), layoutId))
133 .collect(Collectors.toSet()); 135 .collect(Collectors.toSet());
134 } 136 }
135 137
......
...@@ -23,6 +23,7 @@ import org.onosproject.ui.impl.topo.model.UiModelEvent; ...@@ -23,6 +23,7 @@ import org.onosproject.ui.impl.topo.model.UiModelEvent;
23 import org.onosproject.ui.impl.topo.model.UiModelListener; 23 import org.onosproject.ui.impl.topo.model.UiModelListener;
24 import org.onosproject.ui.impl.topo.model.UiSharedTopologyModel; 24 import org.onosproject.ui.impl.topo.model.UiSharedTopologyModel;
25 import org.onosproject.ui.model.topo.UiClusterMember; 25 import org.onosproject.ui.model.topo.UiClusterMember;
26 +import org.onosproject.ui.model.topo.UiNode;
26 import org.onosproject.ui.model.topo.UiRegion; 27 import org.onosproject.ui.model.topo.UiRegion;
27 import org.onosproject.ui.model.topo.UiTopoLayout; 28 import org.onosproject.ui.model.topo.UiTopoLayout;
28 import org.slf4j.Logger; 29 import org.slf4j.Logger;
...@@ -170,17 +171,32 @@ public class UiTopoSession implements UiModelListener { ...@@ -170,17 +171,32 @@ public class UiTopoSession implements UiModelListener {
170 } 171 }
171 172
172 /** 173 /**
173 - * Returns the regions that are "peers" to this region. That is, based on 174 + * Returns the regions/devices that are "peers" to this region. That is,
174 - * the layout the user is viewing, all the regions that are associated with 175 + * based on the layout the user is viewing, all the regions/devices that
175 - * layouts that share the same parent layout as this layout. 176 + * are associated with layouts that share the same parent layout as this
177 + * layout, AND that are linked to an element within this region.
176 * 178 *
177 * @param layout the layout being viewed 179 * @param layout the layout being viewed
178 - * @return all regions that are "siblings" to this layout's region 180 + * @return all regions/devices that are "siblings" to this layout's region
179 */ 181 */
180 - public Set<UiRegion> getPeerRegions(UiTopoLayout layout) { 182 + public Set<UiNode> getPeerNodes(UiTopoLayout layout) {
181 - Set<UiTopoLayout> peerLayouts = layoutService.getPeers(layout.id()); 183 + Set<UiNode> peers = new HashSet<>();
182 - Set<UiRegion> peers = new HashSet<>(); 184 +
183 - peerLayouts.forEach(l -> peers.add(sharedModel.getRegion(l.regionId()))); 185 + // first, get the peer regions
186 + Set<UiTopoLayout> peerLayouts = layoutService.getPeerLayouts(layout.id());
187 + peerLayouts.forEach(l -> {
188 + RegionId peerRegion = l.regionId();
189 + peers.add(sharedModel.getRegion(peerRegion));
190 + });
191 +
192 + // now add the devices that reside in the parent region
193 + if (!layout.isRoot()) {
194 + UiTopoLayout parentLayout = layoutService.getLayout(layout.parent());
195 + getRegion(parentLayout).devices().forEach(peers::add);
196 + }
197 +
198 + // TODO: Finally, filter out regions / devices that are not connected
199 + // directly to this region by an implicit link
184 return peers; 200 return peers;
185 } 201 }
186 202
......
...@@ -25,3 +25,28 @@ ...@@ -25,3 +25,28 @@
25 /* prevents the little cut/copy/paste square that would appear on iPad */ 25 /* prevents the little cut/copy/paste square that would appear on iPad */
26 -webkit-user-select: none; 26 -webkit-user-select: none;
27 } 27 }
28 +
29 +/* -- TEMPORARY CSS (to be deleted) -- */
30 +#topo2tmp div {
31 + padding: 8px 24px;
32 + margin: 8px;
33 + background-color: #ddddff;
34 +}
35 +#topo2tmp div div {
36 + padding: 4px 10px;
37 +}
38 +
39 +#topo2tmp h4 {
40 + margin: 0
41 +}
42 +#topo2tmp p {
43 + margin: 0
44 +}
45 +#topo2tmp .nav-me:hover {
46 + background-color: #bbbbdd;
47 +}
48 +#topo2tmp .nav-me {
49 + font-weight: bold;
50 + text-decoration: underline;
51 + cursor: pointer;
52 +}
......
1 <!-- Topology View partial HTML --> 1 <!-- Topology View partial HTML -->
2 <div id="ov-topo2"> 2 <div id="ov-topo2">
3 + <div id="topo2tmp">
4 + <div class="parentRegion">
5 + Parent Region: <span> - </span>
6 + </div>
7 + <div class="thisRegion">
8 + This Region: <span> - </span>
9 + </div>
10 + <div class="subRegions">
11 + <h4>Subregions</h4>
12 + <div></div>
13 + </div>
14 + <div class="devices">
15 + <h4>Devices</h4>
16 + <div></div>
17 + </div>
18 + <div class="hosts">
19 + <h4>Hosts</h4>
20 + <div></div>
21 + </div>
22 + <div class="links">
23 + <h4>Links</h4>
24 + <div></div>
25 + </div>
26 + <div class="peers">
27 + <h4>Peers</h4>
28 + <div></div>
29 + </div>
30 + </div>
31 +
32 + <!-- Below here is good; Above here is temporary, for debugging -->
33 +
3 <svg viewBox="0 0 1000 1000" 34 <svg viewBox="0 0 1000 1000"
4 resize offset-height="56" offset-width="12" 35 resize offset-height="56" offset-width="12"
5 notifier="notifyResize()"> 36 notifier="notifyResize()">
......
...@@ -42,6 +42,7 @@ ...@@ -42,6 +42,7 @@
42 topo2AllInstances: t2fs, 42 topo2AllInstances: t2fs,
43 topo2CurrentLayout: t2fs, 43 topo2CurrentLayout: t2fs,
44 topo2CurrentRegion: t2fs, 44 topo2CurrentRegion: t2fs,
45 + topo2PeerRegions: t2fs,
45 topo2StartDone: t2fs 46 topo2StartDone: t2fs
46 47
47 // Add further event names / module references as needed 48 // Add further event names / module references as needed
......
...@@ -35,10 +35,79 @@ ...@@ -35,10 +35,79 @@
35 $log.debug('Destroy topo force layout'); 35 $log.debug('Destroy topo force layout');
36 } 36 }
37 37
38 + // ========================== Temporary Code (to be deleted later)
39 +
40 + function request(dir, rid) {
41 + wss.sendEvent('topo2navRegion', {
42 + dir: dir,
43 + rid: rid
44 + });
45 + }
46 +
47 + function doTmpCurrentLayout(data) {
48 + var topdiv = d3.select('#topo2tmp');
49 + var parentRegion = data.parent;
50 + var span = topdiv.select('.parentRegion').select('span');
51 + span.text(parentRegion || '[no parent]');
52 + span.classed('nav-me', !!parentRegion);
53 + }
54 +
55 + function doTmpCurrentRegion(data) {
56 + var topdiv = d3.select('#topo2tmp');
57 + var span = topdiv.select('.thisRegion').select('span');
58 + var div;
59 +
60 + span.text(data.id);
61 +
62 + div = topdiv.select('.subRegions').select('div');
63 + data.subregions.forEach(function (r) {
64 +
65 + function nav() {
66 + request('down', r.id);
67 + }
68 +
69 + div.append('p')
70 + .classed('nav-me', true)
71 + .text(r.id)
72 + .on('click', nav);
73 + });
74 +
75 + div = topdiv.select('.devices').select('div');
76 + data.layerOrder.forEach(function (tag, idx) {
77 + var devs = data.devices[idx];
78 + devs.forEach(function (d) {
79 + div.append('p')
80 + .text('[' + tag + '] ' + d.id);
81 + });
82 +
83 + });
84 +
85 + div = topdiv.select('.hosts').select('div');
86 + data.layerOrder.forEach(function (tag, idx) {
87 + var hosts = data.hosts[idx];
88 + hosts.forEach(function (h) {
89 + div.append('p')
90 + .text('[' + tag + '] ' + h.id);
91 + });
92 + });
93 +
94 + div = topdiv.select('.links').select('div');
95 + var links = data.links;
96 + links.forEach(function (lnk) {
97 + div.append('p')
98 + .text(lnk.id);
99 + });
100 + }
101 +
102 + function doTmpPeerRegions(data) {
103 +
104 + }
105 +
38 // ========================== Event Handlers 106 // ========================== Event Handlers
39 107
40 function allInstances(data) { 108 function allInstances(data) {
41 $log.debug('>> topo2AllInstances event:', data) 109 $log.debug('>> topo2AllInstances event:', data)
110 + doTmpCurrentLayout(data);
42 } 111 }
43 112
44 function currentLayout(data) { 113 function currentLayout(data) {
...@@ -47,6 +116,16 @@ ...@@ -47,6 +116,16 @@
47 116
48 function currentRegion(data) { 117 function currentRegion(data) {
49 $log.debug('>> topo2CurrentRegion event:', data) 118 $log.debug('>> topo2CurrentRegion event:', data)
119 + doTmpCurrentRegion(data);
120 + }
121 +
122 + function topo2PeerRegions(data) {
123 + $log.debug('>> topo2PeerRegions event:', data)
124 + doTmpPeerRegions(data);
125 + }
126 +
127 + function topo2PeerRegions(data) {
128 + $log.debug('>> topo2PeerRegions event:', data)
50 } 129 }
51 130
52 function startDone(data) { 131 function startDone(data) {
...@@ -69,6 +148,7 @@ ...@@ -69,6 +148,7 @@
69 topo2AllInstances: allInstances, 148 topo2AllInstances: allInstances,
70 topo2CurrentLayout: currentLayout, 149 topo2CurrentLayout: currentLayout,
71 topo2CurrentRegion: currentRegion, 150 topo2CurrentRegion: currentRegion,
151 + topo2PeerRegions: topo2PeerRegions,
72 topo2StartDone: startDone 152 topo2StartDone: startDone
73 }; 153 };
74 }]); 154 }]);
......
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 +/*
18 + * Copyright 2016-present Open Networking Laboratory
19 + *
20 + * Licensed under the Apache License, Version 2.0 (the "License");
21 + * you may not use this file except in compliance with the License.
22 + * You may obtain a copy of the License at
23 + *
24 + * http://www.apache.org/licenses/LICENSE-2.0
25 + *
26 + * Unless required by applicable law or agreed to in writing, software
27 + * distributed under the License is distributed on an "AS IS" BASIS,
28 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
29 + * See the License for the specific language governing permissions and
30 + * limitations under the License.
31 + */
32 +
33 +
34 +/*
35 + ONOS GUI -- Topology View (theme) -- CSS file
36 + */
37 +
38 +/* --- Base SVG Layer --- */
39 +
40 +#ov-topoX svg {
41 + /*background-color: #f4f4f4;*/
42 + background-color: goldenrod; /* just for testing */
43 +}
44 +
45 +/* --- "No Devices" Layer --- */
46 +
47 +#ov-topoX svg .noDevsBird {
48 + fill: #db7773;
49 +}
50 +
51 +#ov-topoX svg #topoX-noDevsLayer text {
52 + fill: #7e9aa8;
53 +}
54 +
55 +/* --- Topo Map --- */
56 +
57 +#ov-topoX svg #topoX-map {
58 + stroke-width: 2px;
59 + stroke: #f4f4f4;
60 + fill: #e5e5e6;
61 +}
62 +
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 +
18 +/*
19 + ONOS GUI -- Topology View (layout) -- CSS file
20 + */
21 +
22 +/* --- Base SVG Layer --- */
23 +
24 +#ov-topoX svg {
25 + /* prevents the little cut/copy/paste square that would appear on iPad */
26 + -webkit-user-select: none;
27 +}
28 +
29 +/* -- TEMPORARY CSS (to be deleted) -- */
30 +#topoXtmp div {
31 + padding: 8px 24px;
32 + margin: 8px;
33 + background-color: #ddddff;
34 +}
35 +#topoXtmp div div {
36 + padding: 4px 10px;
37 +}
38 +
39 +#topoXtmp h4 {
40 + margin: 0
41 +}
42 +#topoXtmp p {
43 + margin: 0
44 +}
45 +#topoXtmp .nav-me:hover {
46 + background-color: #bbbbdd;
47 +}
48 +#topoXtmp .nav-me {
49 + font-weight: bold;
50 + text-decoration: underline;
51 + cursor: pointer;
52 +}
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 +<!-- Topology View partial HTML -->
18 +<div id="ov-topoX">
19 + <div id="topoXtmp">
20 + <div class="parentRegion">
21 + Parent Region: <span> - </span>
22 + </div>
23 + <div class="thisRegion">
24 + This Region: <span> - </span>
25 + </div>
26 + <div class="subRegions">
27 + <h4>Subregions</h4>
28 + <div></div>
29 + </div>
30 + <div class="devices">
31 + <h4>Devices</h4>
32 + <div></div>
33 + </div>
34 + <div class="hosts">
35 + <h4>Hosts</h4>
36 + <div></div>
37 + </div>
38 + <div class="links">
39 + <h4>Links</h4>
40 + <div></div>
41 + </div>
42 + <div class="peers">
43 + <h4>Peers</h4>
44 + <div></div>
45 + </div>
46 + </div>
47 +</div>
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 +/*
18 + * Copyright 2016-present Open Networking Laboratory
19 + *
20 + * Licensed under the Apache License, Version 2.0 (the "License");
21 + * you may not use this file except in compliance with the License.
22 + * You may obtain a copy of the License at
23 + *
24 + * http://www.apache.org/licenses/LICENSE-2.0
25 + *
26 + * Unless required by applicable law or agreed to in writing, software
27 + * distributed under the License is distributed on an "AS IS" BASIS,
28 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
29 + * See the License for the specific language governing permissions and
30 + * limitations under the License.
31 + */
32 +
33 +/*
34 + ONOS GUI -- Topology View Module
35 +
36 + NOTE: currently under development to support Regions.
37 + */
38 +
39 +(function () {
40 + 'use strict';
41 +
42 + // references to injected services
43 + var $scope, $log, $loc,
44 + fs, mast, ks, zs,
45 + gs, ms, sus, flash,
46 + wss, ps, th,
47 + tXes, tXfs;
48 +
49 + // DOM elements
50 + var ovtopoX, svg, defs, zoomLayer, mapG, spriteG, forceG, noDevsLayer;
51 +
52 + // Internal state
53 + var zoomer, actionMap;
54 +
55 +
56 + // === Helper Functions
57 +
58 + // callback invoked when the SVG view has been resized..
59 + function svgResized(s) {
60 + $log.debug("topoX view resized", s);
61 + }
62 +
63 + function setUpKeys(overlayKeys) {
64 + $log.debug('topoX: set up keys....');
65 + }
66 +
67 + // === Controller Definition -----------------------------------------
68 +
69 + angular.module('ovTopoX', ['onosUtil', 'onosSvg', 'onosRemote'])
70 + .controller('OvTopoXCtrl',
71 + ['$scope', '$log', '$location',
72 + 'FnService', 'MastService', 'KeyService', 'ZoomService',
73 + 'GlyphService', 'MapService', 'SvgUtilService', 'FlashService',
74 + 'WebSocketService', 'PrefsService', 'ThemeService',
75 + 'TopoXEventService', 'TopoXForceService',
76 +
77 + function (_$scope_, _$log_, _$loc_,
78 + _fs_, _mast_, _ks_, _zs_,
79 + _gs_, _ms_, _sus_, _flash_,
80 + _wss_, _ps_, _th_,
81 + _tXes_, _tXfs_) {
82 +
83 + var params = _$loc_.search(),
84 + projection,
85 + dim,
86 + wh,
87 + uplink = {
88 + // provides function calls back into this space
89 + // showNoDevs: showNoDevs,
90 + // projection: function () { return projection; },
91 + // zoomLayer: function () { return zoomLayer; },
92 + // zoomer: function () { return zoomer; },
93 + // opacifyMap: opacifyMap,
94 + // topoStartDone: topoStartDone
95 + };
96 +
97 + $scope = _$scope_;
98 + $log = _$log_;
99 + $loc = _$loc_;
100 +
101 + fs = _fs_;
102 + mast = _mast_;
103 + ks = _ks_;
104 + zs = _zs_;
105 +
106 + gs = _gs_;
107 + ms = _ms_;
108 + sus = _sus_;
109 + flash = _flash_;
110 +
111 + wss = _wss_;
112 + ps = _ps_;
113 + th = _th_;
114 +
115 + tXes = _tXes_;
116 + tXfs = _tXfs_;
117 +
118 + // capture selected intent parameters (if they are set in the
119 + // query string) so that the traffic overlay can highlight
120 + // the path for that intent
121 + if (params.intentKey && params.intentAppId && params.intentAppName) {
122 + $scope.intentData = {
123 + key: params.intentKey,
124 + appId: params.intentAppId,
125 + appName: params.intentAppName
126 + };
127 + }
128 +
129 + $scope.notifyResize = function () {
130 + svgResized(fs.windowSize(mast.mastHeight()));
131 + };
132 +
133 + // Cleanup on destroyed scope..
134 + $scope.$on('$destroy', function () {
135 + $log.log('OvTopoXCtrl is saying Buh-Bye!');
136 + tXes.stop();
137 + ks.unbindKeys();
138 + tXfs.destroy();
139 + });
140 +
141 + // svg layer and initialization of components
142 + ovtopoX = d3.select('#ov-topoX');
143 + svg = ovtopoX.select('svg');
144 + // set the svg size to match that of the window, less the masthead
145 + wh = fs.windowSize(mast.mastHeight());
146 + $log.debug('setting topo SVG size to', wh);
147 + svg.attr(wh);
148 + dim = [wh.width, wh.height];
149 +
150 +
151 + // set up our keyboard shortcut bindings
152 + setUpKeys();
153 +
154 + // make sure we can respond to topology events from the server
155 + tXes.bindHandlers();
156 +
157 + // initialize the force layout, ready to render the topology
158 + tXfs.init();
159 +
160 +
161 + // =-=-=-=-=-=-=-=-
162 + // TODO: in future, we will load background map data
163 + // asynchronously (hence the promise) and then chain off
164 + // there to send the topoXstart event to the server.
165 + // For now, we'll send the event inline...
166 + tXes.start();
167 +
168 +
169 + // === ORIGINAL CODE ===
170 +
171 + // setUpKeys();
172 + // setUpToolbar();
173 + // setUpDefs();
174 + // setUpZoom();
175 + // setUpNoDevs();
176 + /*
177 + setUpMap().then(
178 + function (proj) {
179 + var z = ps.getPrefs('topo_zoom', { tx:0, ty:0, sc:1 });
180 + zoomer.panZoom([z.tx, z.ty], z.sc);
181 + $log.debug('** Zoom restored:', z);
182 +
183 + projection = proj;
184 + $log.debug('** We installed the projection:', proj);
185 + flash.enable(false);
186 + toggleMap(prefsState.bg);
187 + flash.enable(true);
188 + mapShader(true);
189 +
190 + // now we have the map projection, we are ready for
191 + // the server to send us device/host data...
192 + tes.start();
193 + // need to do the following so we immediately get
194 + // the summary panel data back from the server
195 + restoreSummaryFromPrefs();
196 + }
197 + );
198 + */
199 + // tes.bindHandlers();
200 + // setUpSprites();
201 +
202 + // forceG = zoomLayer.append('g').attr('id', 'topo-force');
203 + // tfs.initForce(svg, forceG, uplink, dim);
204 + // tis.initInst({ showMastership: tfs.showMastership });
205 + // tps.initPanels();
206 +
207 + // restoreConfigFromPrefs();
208 + // ttbs.setDefaultOverlay(prefsState.ovidx);
209 +
210 + // $log.debug('registered overlays...', tov.list());
211 +
212 + $log.log('OvTopoXCtrl has been created');
213 + }]);
214 +}());
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 +/*
18 + ONOS GUI -- Topology Event Module.
19 +
20 + Defines the conduit between the client and the server:
21 + - provides a clean API for sending events to the server
22 + - dispatches incoming events from the server to the appropriate sub-module
23 +
24 + */
25 +
26 +(function () {
27 + 'use strict';
28 +
29 + // injected refs
30 + var $log, wss, tXfs;
31 +
32 + // internal state
33 + var handlerMap,
34 + openListener;
35 +
36 + // ========================== Helper Functions
37 +
38 + function createHandlerMap() {
39 + handlerMap = {
40 + topo2AllInstances: tXfs,
41 + topo2CurrentLayout: tXfs,
42 + topo2CurrentRegion: tXfs,
43 + topo2PeerRegions: tXfs,
44 + topo2StartDone: tXfs
45 +
46 + // Add further event names / module references as needed
47 + };
48 + }
49 +
50 + function wsOpen(host, url) {
51 + $log.debug('topoXEvent: WSopen - cluster node:', host, 'URL:', url);
52 + // tell the server we are ready to receive topo events
53 + wss.sendEvent('topo2Start');
54 + }
55 +
56 + // bind our event handlers to the web socket service, so that our
57 + // callbacks get invoked for incoming events
58 + function bindHandlers() {
59 + wss.bindHandlers(handlerMap);
60 + $log.debug('topoX event handlers bound');
61 + }
62 +
63 + // tell the server we are ready to receive topology events
64 + function start() {
65 + // in case we fail over to a new server,
66 + // listen for wsock-open events
67 + openListener = wss.addOpenListener(wsOpen);
68 + wss.sendEvent('topo2Start');
69 + $log.debug('topoX comms started');
70 + }
71 +
72 + // tell the server we no longer wish to receive topology events
73 + function stop() {
74 + wss.sendEvent('topo2Stop');
75 + wss.unbindHandlers(handlerMap);
76 + wss.removeOpenListener(openListener);
77 + openListener = null;
78 + $log.debug('topoX comms stopped');
79 + }
80 +
81 + // ========================== Main Service Definition
82 +
83 + angular.module('ovTopoX')
84 + .factory('TopoXEventService',
85 + ['$log', 'WebSocketService', 'TopoXForceService',
86 +
87 + function (_$log_, _wss_, _tXfs_) {
88 + $log = _$log_;
89 + wss = _wss_;
90 + tXfs = _tXfs_;
91 +
92 + // deferred creation of handler map, so module references are good
93 + createHandlerMap();
94 +
95 + return {
96 + bindHandlers: bindHandlers,
97 + start: start,
98 + stop: stop
99 + };
100 + }]);
101 +}());
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 +/*
18 + ONOS GUI -- Topology Force Module.
19 + Visualization of the topology in an SVG layer, using a D3 Force Layout.
20 + */
21 +
22 +(function () {
23 + 'use strict';
24 +
25 + // injected refs
26 + var $log, wss;
27 +
28 + // ========================== Helper Functions
29 +
30 + function init() {
31 + $log.debug('Initialize topo force layout');
32 + }
33 +
34 + function destroy() {
35 + $log.debug('Destroy topo force layout');
36 + }
37 +
38 + // ========================== Temporary Code (to be deleted later)
39 +
40 + function request(dir, rid) {
41 + wss.sendEvent('topo2navRegion', {
42 + dir: dir,
43 + rid: rid
44 + });
45 + }
46 +
47 + function doTmpCurrentLayout(data) {
48 + var topdiv = d3.select('#topoXtmp');
49 + var parentRegion = data.parent;
50 + var span = topdiv.select('.parentRegion').select('span');
51 + span.text(parentRegion || '[no parent]');
52 + span.classed('nav-me', !!parentRegion);
53 + }
54 +
55 + function doTmpCurrentRegion(data) {
56 + var topdiv = d3.select('#topoXtmp');
57 + var span = topdiv.select('.thisRegion').select('span');
58 + var div;
59 +
60 + span.text(data.id);
61 +
62 + div = topdiv.select('.subRegions').select('div');
63 + data.subregions.forEach(function (r) {
64 +
65 + function nav() {
66 + request('down', r.id);
67 + }
68 +
69 + div.append('p')
70 + .classed('nav-me', true)
71 + .text(r.id)
72 + .on('click', nav);
73 + });
74 +
75 + div = topdiv.select('.devices').select('div');
76 + data.layerOrder.forEach(function (tag, idx) {
77 + var devs = data.devices[idx];
78 + devs.forEach(function (d) {
79 + div.append('p')
80 + .text('[' + tag + '] ' + d.id);
81 + });
82 +
83 + });
84 +
85 + div = topdiv.select('.hosts').select('div');
86 + data.layerOrder.forEach(function (tag, idx) {
87 + var hosts = data.hosts[idx];
88 + hosts.forEach(function (h) {
89 + div.append('p')
90 + .text('[' + tag + '] ' + h.id);
91 + });
92 + });
93 +
94 + div = topdiv.select('.links').select('div');
95 + var links = data.links;
96 + links.forEach(function (lnk) {
97 + div.append('p')
98 + .text(lnk.id);
99 + });
100 + }
101 +
102 + function doTmpPeerRegions(data) {
103 +
104 + }
105 +
106 + // ========================== Event Handlers
107 +
108 + function allInstances(data) {
109 + $log.debug('>> topo2AllInstances event:', data)
110 + doTmpCurrentLayout(data);
111 + }
112 +
113 + function currentLayout(data) {
114 + $log.debug('>> topo2CurrentLayout event:', data)
115 + }
116 +
117 + function currentRegion(data) {
118 + $log.debug('>> topo2CurrentRegion event:', data)
119 + doTmpCurrentRegion(data);
120 + }
121 +
122 + function peerRegions(data) {
123 + $log.debug('>> topo2PeerRegions event:', data)
124 + doTmpPeerRegions(data);
125 + }
126 +
127 + function startDone(data) {
128 + $log.debug('>> topo2StartDone event:', data)
129 + }
130 +
131 + // ========================== Main Service Definition
132 +
133 + angular.module('ovTopoX')
134 + .factory('TopoXForceService',
135 + ['$log', 'WebSocketService',
136 +
137 + function (_$log_, _wss_) {
138 + $log = _$log_;
139 + wss = _wss_;
140 +
141 + return {
142 + init: init,
143 + destroy: destroy,
144 + topo2AllInstances: allInstances,
145 + topo2CurrentLayout: currentLayout,
146 + topo2CurrentRegion: currentRegion,
147 + topo2PeerRegions: peerRegions,
148 + topo2StartDone: startDone
149 + };
150 + }]);
151 +}());
...@@ -133,6 +133,12 @@ ...@@ -133,6 +133,12 @@
133 <link rel="stylesheet" href="app/view/topo2/topo2.css"> 133 <link rel="stylesheet" href="app/view/topo2/topo2.css">
134 <link rel="stylesheet" href="app/view/topo2/topo2-theme.css"> 134 <link rel="stylesheet" href="app/view/topo2/topo2-theme.css">
135 135
136 + <script src="app/view/topoX/topoX.js"></script>
137 + <script src="app/view/topoX/topoXEvent.js"></script>
138 + <script src="app/view/topoX/topoXForce.js"></script>
139 + <link rel="stylesheet" href="app/view/topoX/topoX.css">
140 + <link rel="stylesheet" href="app/view/topoX/topoX-theme.css">
141 +
136 <!-- Builtin views javascript. --> 142 <!-- Builtin views javascript. -->
137 <script src="app/view/topo/topo.js"></script> 143 <script src="app/view/topo/topo.js"></script>
138 <script src="app/view/topo/topoD3.js"></script> 144 <script src="app/view/topo/topoD3.js"></script>
......
...@@ -4,9 +4,11 @@ ...@@ -4,9 +4,11 @@
4 "id": "<null-region>", 4 "id": "<null-region>",
5 "subregions": [{ 5 "subregions": [{
6 "id": "r2", 6 "id": "r2",
7 + "nodeType":"region",
7 "nDevs": 2 8 "nDevs": 2
8 }, { 9 }, {
9 "id": "r1", 10 "id": "r1",
11 + "nodeType":"region",
10 "nDevs": 3 12 "nDevs": 3
11 }], 13 }],
12 "devices": [ 14 "devices": [
...@@ -14,6 +16,7 @@ ...@@ -14,6 +16,7 @@
14 [], 16 [],
15 [{ 17 [{
16 "id": "null:0000000000000001", 18 "id": "null:0000000000000001",
19 + "nodeType":"device",
17 "type": "switch", 20 "type": "switch",
18 "online": false, 21 "online": false,
19 "master": "", 22 "master": "",
......