ONOS-303 Added ability to add mult-source intent from GUI.
Fixed treatment of selection & hover modes. Change-Id: Idf47b6a15b56ea96b9edaeeb034fad0f205af6e3
Showing
4 changed files
with
163 additions
and
88 deletions
| ... | @@ -15,6 +15,7 @@ | ... | @@ -15,6 +15,7 @@ |
| 15 | */ | 15 | */ |
| 16 | package org.onlab.onos.gui; | 16 | package org.onlab.onos.gui; |
| 17 | 17 | ||
| 18 | +import com.fasterxml.jackson.databind.JsonNode; | ||
| 18 | import com.fasterxml.jackson.databind.node.ArrayNode; | 19 | import com.fasterxml.jackson.databind.node.ArrayNode; |
| 19 | import com.fasterxml.jackson.databind.node.ObjectNode; | 20 | import com.fasterxml.jackson.databind.node.ObjectNode; |
| 20 | import org.eclipse.jetty.websocket.WebSocket; | 21 | import org.eclipse.jetty.websocket.WebSocket; |
| ... | @@ -23,20 +24,25 @@ import org.onlab.onos.cluster.ClusterEventListener; | ... | @@ -23,20 +24,25 @@ import org.onlab.onos.cluster.ClusterEventListener; |
| 23 | import org.onlab.onos.cluster.ControllerNode; | 24 | import org.onlab.onos.cluster.ControllerNode; |
| 24 | import org.onlab.onos.core.ApplicationId; | 25 | import org.onlab.onos.core.ApplicationId; |
| 25 | import org.onlab.onos.core.CoreService; | 26 | import org.onlab.onos.core.CoreService; |
| 27 | +import org.onlab.onos.net.ConnectPoint; | ||
| 26 | import org.onlab.onos.net.Device; | 28 | import org.onlab.onos.net.Device; |
| 27 | import org.onlab.onos.net.Host; | 29 | import org.onlab.onos.net.Host; |
| 28 | import org.onlab.onos.net.HostId; | 30 | import org.onlab.onos.net.HostId; |
| 31 | +import org.onlab.onos.net.HostLocation; | ||
| 29 | import org.onlab.onos.net.Link; | 32 | import org.onlab.onos.net.Link; |
| 30 | import org.onlab.onos.net.device.DeviceEvent; | 33 | import org.onlab.onos.net.device.DeviceEvent; |
| 31 | import org.onlab.onos.net.device.DeviceListener; | 34 | import org.onlab.onos.net.device.DeviceListener; |
| 32 | import org.onlab.onos.net.flow.DefaultTrafficSelector; | 35 | import org.onlab.onos.net.flow.DefaultTrafficSelector; |
| 33 | import org.onlab.onos.net.flow.DefaultTrafficTreatment; | 36 | import org.onlab.onos.net.flow.DefaultTrafficTreatment; |
| 37 | +import org.onlab.onos.net.flow.TrafficSelector; | ||
| 38 | +import org.onlab.onos.net.flow.TrafficTreatment; | ||
| 34 | import org.onlab.onos.net.host.HostEvent; | 39 | import org.onlab.onos.net.host.HostEvent; |
| 35 | import org.onlab.onos.net.host.HostListener; | 40 | import org.onlab.onos.net.host.HostListener; |
| 36 | import org.onlab.onos.net.intent.HostToHostIntent; | 41 | import org.onlab.onos.net.intent.HostToHostIntent; |
| 37 | import org.onlab.onos.net.intent.Intent; | 42 | import org.onlab.onos.net.intent.Intent; |
| 38 | import org.onlab.onos.net.intent.IntentEvent; | 43 | import org.onlab.onos.net.intent.IntentEvent; |
| 39 | import org.onlab.onos.net.intent.IntentListener; | 44 | import org.onlab.onos.net.intent.IntentListener; |
| 45 | +import org.onlab.onos.net.intent.MultiPointToSinglePointIntent; | ||
| 40 | import org.onlab.onos.net.link.LinkEvent; | 46 | import org.onlab.onos.net.link.LinkEvent; |
| 41 | import org.onlab.onos.net.link.LinkListener; | 47 | import org.onlab.onos.net.link.LinkListener; |
| 42 | import org.onlab.osgi.ServiceDirectory; | 48 | import org.onlab.osgi.ServiceDirectory; |
| ... | @@ -119,7 +125,7 @@ public class TopologyViewWebSocket | ... | @@ -119,7 +125,7 @@ public class TopologyViewWebSocket |
| 119 | super(directory); | 125 | super(directory); |
| 120 | 126 | ||
| 121 | intentFilter = new TopologyViewIntentFilter(intentService, deviceService, | 127 | intentFilter = new TopologyViewIntentFilter(intentService, deviceService, |
| 122 | - hostService, linkService); | 128 | + hostService, linkService); |
| 123 | appId = directory.get(CoreService.class).registerApplication(APP_ID); | 129 | appId = directory.get(CoreService.class).registerApplication(APP_ID); |
| 124 | } | 130 | } |
| 125 | 131 | ||
| ... | @@ -195,8 +201,11 @@ public class TopologyViewWebSocket | ... | @@ -195,8 +201,11 @@ public class TopologyViewWebSocket |
| 195 | requestDetails(event); | 201 | requestDetails(event); |
| 196 | } else if (type.equals("updateMeta")) { | 202 | } else if (type.equals("updateMeta")) { |
| 197 | updateMetaUi(event); | 203 | updateMetaUi(event); |
| 204 | + | ||
| 198 | } else if (type.equals("addHostIntent")) { | 205 | } else if (type.equals("addHostIntent")) { |
| 199 | createHostIntent(event); | 206 | createHostIntent(event); |
| 207 | + } else if (type.equals("addMultiSourceIntent")) { | ||
| 208 | + createMultiSourceIntent(event); | ||
| 200 | 209 | ||
| 201 | } else if (type.equals("requestTraffic")) { | 210 | } else if (type.equals("requestTraffic")) { |
| 202 | requestTraffic(event); | 211 | requestTraffic(event); |
| ... | @@ -268,6 +277,7 @@ public class TopologyViewWebSocket | ... | @@ -268,6 +277,7 @@ public class TopologyViewWebSocket |
| 268 | } | 277 | } |
| 269 | } | 278 | } |
| 270 | 279 | ||
| 280 | + | ||
| 271 | // Creates host-to-host intent. | 281 | // Creates host-to-host intent. |
| 272 | private void createHostIntent(ObjectNode event) { | 282 | private void createHostIntent(ObjectNode event) { |
| 273 | ObjectNode payload = payload(event); | 283 | ObjectNode payload = payload(event); |
| ... | @@ -276,19 +286,66 @@ public class TopologyViewWebSocket | ... | @@ -276,19 +286,66 @@ public class TopologyViewWebSocket |
| 276 | HostId one = hostId(string(payload, "one")); | 286 | HostId one = hostId(string(payload, "one")); |
| 277 | HostId two = hostId(string(payload, "two")); | 287 | HostId two = hostId(string(payload, "two")); |
| 278 | 288 | ||
| 279 | - HostToHostIntent hostIntent = new HostToHostIntent(appId, one, two, | 289 | + HostToHostIntent intent = |
| 280 | - DefaultTrafficSelector.builder().build(), | 290 | + new HostToHostIntent(appId, one, two, |
| 281 | - DefaultTrafficTreatment.builder().build()); | 291 | + DefaultTrafficSelector.builder().build(), |
| 292 | + DefaultTrafficTreatment.builder().build()); | ||
| 293 | + startMonitoring(event); | ||
| 294 | + intentService.submit(intent); | ||
| 295 | + } | ||
| 296 | + | ||
| 297 | + // Creates multi-source-to-single-dest intent. | ||
| 298 | + private void createMultiSourceIntent(ObjectNode event) { | ||
| 299 | + ObjectNode payload = payload(event); | ||
| 300 | + long id = number(event, "sid"); | ||
| 301 | + // TODO: add protection against device ids and non-existent hosts. | ||
| 302 | + Set<HostId> src = getHostIds((ArrayNode) payload.path("src")); | ||
| 303 | + HostId dst = hostId(string(payload, "dst")); | ||
| 304 | + Host dstHost = hostService.getHost(dst); | ||
| 305 | + | ||
| 306 | + Set<ConnectPoint> ingressPoints = getHostLocations(src); | ||
| 307 | + | ||
| 308 | + // FIXME: clearly, this is not enough | ||
| 309 | + TrafficSelector selector = DefaultTrafficSelector.builder() | ||
| 310 | + .matchEthDst(dstHost.mac()).build(); | ||
| 311 | + TrafficTreatment treatment = DefaultTrafficTreatment.builder().build(); | ||
| 312 | + | ||
| 313 | + MultiPointToSinglePointIntent intent = | ||
| 314 | + new MultiPointToSinglePointIntent(appId, selector, treatment, | ||
| 315 | + ingressPoints, dstHost.location()); | ||
| 282 | trafficEvent = event; | 316 | trafficEvent = event; |
| 283 | - intentService.submit(hostIntent); | 317 | + intentService.submit(intent); |
| 284 | } | 318 | } |
| 285 | 319 | ||
| 320 | + private Set<ConnectPoint> getHostLocations(Set<HostId> hostIds) { | ||
| 321 | + Set<ConnectPoint> points = new HashSet<>(); | ||
| 322 | + for (HostId hostId : hostIds) { | ||
| 323 | + points.add(getHostLocation(hostId)); | ||
| 324 | + } | ||
| 325 | + return points; | ||
| 326 | + } | ||
| 327 | + | ||
| 328 | + private HostLocation getHostLocation(HostId hostId) { | ||
| 329 | + return hostService.getHost(hostId).location(); | ||
| 330 | + } | ||
| 331 | + | ||
| 332 | + // Produces a list of host ids from the specified JSON array. | ||
| 333 | + private Set<HostId> getHostIds(ArrayNode ids) { | ||
| 334 | + Set<HostId> hostIds = new HashSet<>(); | ||
| 335 | + for (JsonNode id : ids) { | ||
| 336 | + hostIds.add(hostId(id.asText())); | ||
| 337 | + } | ||
| 338 | + return hostIds; | ||
| 339 | + } | ||
| 340 | + | ||
| 341 | + | ||
| 286 | private synchronized long startMonitoring(ObjectNode event) { | 342 | private synchronized long startMonitoring(ObjectNode event) { |
| 287 | - if (trafficTask == null) { | 343 | + if (trafficTask != null) { |
| 288 | - trafficEvent = event; | 344 | + stopMonitoring(); |
| 289 | - trafficTask = new TrafficMonitor(); | ||
| 290 | - timer.schedule(trafficTask, TRAFFIC_FREQUENCY_SEC, TRAFFIC_FREQUENCY_SEC); | ||
| 291 | } | 345 | } |
| 346 | + trafficEvent = event; | ||
| 347 | + trafficTask = new TrafficMonitor(); | ||
| 348 | + timer.schedule(trafficTask, TRAFFIC_FREQUENCY_SEC, TRAFFIC_FREQUENCY_SEC); | ||
| 292 | return number(event, "sid"); | 349 | return number(event, "sid"); |
| 293 | } | 350 | } |
| 294 | 351 | ... | ... |
| ... | @@ -132,11 +132,11 @@ svg .node.host circle { | ... | @@ -132,11 +132,11 @@ svg .node.host circle { |
| 132 | /* LINKS */ | 132 | /* LINKS */ |
| 133 | 133 | ||
| 134 | #topo svg .link { | 134 | #topo svg .link { |
| 135 | - opacity: .7; | 135 | + opacity: .9; |
| 136 | } | 136 | } |
| 137 | 137 | ||
| 138 | #topo svg .link.inactive { | 138 | #topo svg .link.inactive { |
| 139 | - opacity: .2; | 139 | + opacity: .5; |
| 140 | stroke-dasharray: 8 4; | 140 | stroke-dasharray: 8 4; |
| 141 | } | 141 | } |
| 142 | 142 | ||
| ... | @@ -285,7 +285,7 @@ svg .node.host circle { | ... | @@ -285,7 +285,7 @@ svg .node.host circle { |
| 285 | padding: 2px 6px; | 285 | padding: 2px 6px; |
| 286 | font-size: 9pt; | 286 | font-size: 9pt; |
| 287 | cursor: pointer; | 287 | cursor: pointer; |
| 288 | - width: 50%; | 288 | + width: 200px; |
| 289 | text-align: center; | 289 | text-align: center; |
| 290 | 290 | ||
| 291 | /* theme specific... */ | 291 | /* theme specific... */ | ... | ... |
| ... | @@ -72,9 +72,9 @@ | ... | @@ -72,9 +72,9 @@ |
| 72 | topo: { | 72 | topo: { |
| 73 | linkBaseColor: '#666', | 73 | linkBaseColor: '#666', |
| 74 | linkInColor: '#66f', | 74 | linkInColor: '#66f', |
| 75 | - linkInWidth: 14, | 75 | + linkInWidth: 12, |
| 76 | linkOutColor: '#f00', | 76 | linkOutColor: '#f00', |
| 77 | - linkOutWidth: 14 | 77 | + linkOutWidth: 10 |
| 78 | }, | 78 | }, |
| 79 | icons: { | 79 | icons: { |
| 80 | w: 30, | 80 | w: 30, |
| ... | @@ -148,8 +148,7 @@ | ... | @@ -148,8 +148,7 @@ |
| 148 | P: togglePorts, | 148 | P: togglePorts, |
| 149 | U: [unpin, 'Unpin node'], | 149 | U: [unpin, 'Unpin node'], |
| 150 | R: [resetZoomPan, 'Reset zoom/pan'], | 150 | R: [resetZoomPan, 'Reset zoom/pan'], |
| 151 | - H: [cycleHoverMode, 'Cycle hover mode'], | 151 | + V: [showTrafficAction, 'Show related traffic'], |
| 152 | - V: [showTrafficAction, 'Show traffic'], | ||
| 153 | A: [showAllTrafficAction, 'Show all traffic'], | 152 | A: [showAllTrafficAction, 'Show all traffic'], |
| 154 | F: [showDeviceLinkFlowsAction, 'Show device link flows'], | 153 | F: [showDeviceLinkFlowsAction, 'Show device link flows'], |
| 155 | esc: handleEscape | 154 | esc: handleEscape |
| ... | @@ -191,10 +190,13 @@ | ... | @@ -191,10 +190,13 @@ |
| 191 | onosOrder = [], | 190 | onosOrder = [], |
| 192 | oiBox, | 191 | oiBox, |
| 193 | oiShowMaster = false, | 192 | oiShowMaster = false, |
| 194 | - hoverModes = [ 'none', 'intents', 'flows'], | ||
| 195 | - hoverMode = 0, | ||
| 196 | portLabelsOn = false; | 193 | portLabelsOn = false; |
| 197 | 194 | ||
| 195 | + var hoverModeAll = 1, | ||
| 196 | + hoverModeFlows = 2, | ||
| 197 | + hoverModeIntents = 3, | ||
| 198 | + hoverMode = hoverModeFlows; | ||
| 199 | + | ||
| 198 | // D3 selections | 200 | // D3 selections |
| 199 | var svg, | 201 | var svg, |
| 200 | zoomPanContainer, | 202 | zoomPanContainer, |
| ... | @@ -327,14 +329,6 @@ | ... | @@ -327,14 +329,6 @@ |
| 327 | }); | 329 | }); |
| 328 | } | 330 | } |
| 329 | 331 | ||
| 330 | - function cycleHoverMode(view) { | ||
| 331 | - hoverMode++; | ||
| 332 | - if (hoverMode === hoverModes.length) { | ||
| 333 | - hoverMode = 0; | ||
| 334 | - } | ||
| 335 | - view.flash('Mode: ' + hoverModes[hoverMode]); | ||
| 336 | - } | ||
| 337 | - | ||
| 338 | function togglePorts(view) { | 332 | function togglePorts(view) { |
| 339 | view.alert('togglePorts() callback') | 333 | view.alert('togglePorts() callback') |
| 340 | } | 334 | } |
| ... | @@ -829,6 +823,14 @@ | ... | @@ -829,6 +823,14 @@ |
| 829 | function getSelId(idx) { | 823 | function getSelId(idx) { |
| 830 | return getSel(idx).obj.id; | 824 | return getSel(idx).obj.id; |
| 831 | } | 825 | } |
| 826 | + function getSelIds(start, endOffset) { | ||
| 827 | + var end = selectOrder.length - endOffset; | ||
| 828 | + var ids = []; | ||
| 829 | + selectOrder.slice(start, end).forEach(function (d) { | ||
| 830 | + ids.push(getSelId(d)); | ||
| 831 | + }); | ||
| 832 | + return ids; | ||
| 833 | + } | ||
| 832 | function allSelectionsClass(cls) { | 834 | function allSelectionsClass(cls) { |
| 833 | for (var i=0, n=nSel(); i<n; i++) { | 835 | for (var i=0, n=nSel(); i<n; i++) { |
| 834 | if (getSel(i).obj.class !== cls) { | 836 | if (getSel(i).obj.class !== cls) { |
| ... | @@ -876,69 +878,92 @@ | ... | @@ -876,69 +878,92 @@ |
| 876 | sendMessage('requestDetails', payload); | 878 | sendMessage('requestDetails', payload); |
| 877 | } | 879 | } |
| 878 | 880 | ||
| 879 | - function addIntentAction() { | 881 | + function addHostIntentAction() { |
| 880 | sendMessage('addHostIntent', { | 882 | sendMessage('addHostIntent', { |
| 881 | - one: getSelId(0), | 883 | + one: selectOrder[0], |
| 882 | - two: getSelId(1), | 884 | + two: selectOrder[1], |
| 883 | - ids: [ getSelId(0), getSelId(1) ] | 885 | + ids: selectOrder |
| 884 | }); | 886 | }); |
| 885 | - network.view.flash('Host-to-Host connectivity added'); | 887 | + network.view.flash('Host-to-Host flow added'); |
| 886 | } | 888 | } |
| 887 | 889 | ||
| 888 | - function showTrafficAction() { | 890 | + function addMultiSourceIntentAction() { |
| 889 | - cancelTraffic(); | 891 | + sendMessage('addMultiSourceIntent', { |
| 890 | - hoverMode = 1; | 892 | + src: selectOrder.slice(0, selectOrder.length - 1), |
| 891 | - showSelectTraffic(); | 893 | + dst: selectOrder[selectOrder.length - 1], |
| 892 | - network.view.flash('Related Traffic'); | 894 | + ids: selectOrder |
| 895 | + }); | ||
| 896 | + network.view.flash('Multi-Source flow added'); | ||
| 893 | } | 897 | } |
| 894 | 898 | ||
| 899 | + | ||
| 895 | function cancelTraffic() { | 900 | function cancelTraffic() { |
| 896 | sendMessage('cancelTraffic', {}); | 901 | sendMessage('cancelTraffic', {}); |
| 897 | } | 902 | } |
| 898 | 903 | ||
| 899 | - function showSelectTraffic() { | 904 | + function requestTrafficForMode() { |
| 900 | - // if nothing is hovered over, and nothing selected, send cancel request | 905 | + if (hoverMode === hoverModeAll) { |
| 901 | - if (!hovered && nSel() === 0) { | 906 | + requestAllTraffic(); |
| 902 | - cancelTraffic(); | 907 | + } else if (hoverMode === hoverModeFlows) { |
| 903 | - return; | 908 | + requestDeviceLinkFlows(); |
| 909 | + } else if (hoverMode === hoverModeIntents) { | ||
| 910 | + requestSelectTraffic(); | ||
| 904 | } | 911 | } |
| 912 | + } | ||
| 905 | 913 | ||
| 906 | - // NOTE: hover is only populated if "show traffic on hover" is | 914 | + function showTrafficAction() { |
| 907 | - // toggled on, and the item hovered is a host or a device... | 915 | + hoverMode = hoverModeIntents; |
| 908 | - var hoverId = (trafficHover() && hovered && | 916 | + requestSelectTraffic(); |
| 909 | - (hovered.class === 'host' || hovered.class === 'device')) | 917 | + network.view.flash('Related Traffic'); |
| 918 | + } | ||
| 919 | + | ||
| 920 | + function requestSelectTraffic() { | ||
| 921 | + if (validateSelectionContext()) { | ||
| 922 | + var hoverId = (hoverMode === hoverModeIntents && hovered && | ||
| 923 | + (hovered.class === 'host' || hovered.class === 'device')) | ||
| 910 | ? hovered.id : ''; | 924 | ? hovered.id : ''; |
| 911 | - sendMessage('requestTraffic', { | 925 | + sendMessage('requestTraffic', { |
| 912 | - ids: selectOrder, | 926 | + ids: selectOrder, |
| 913 | - hover: hoverId | 927 | + hover: hoverId |
| 914 | - }); | 928 | + }); |
| 929 | + } | ||
| 915 | } | 930 | } |
| 916 | 931 | ||
| 932 | + | ||
| 933 | + function showDeviceLinkFlowsAction() { | ||
| 934 | + hoverMode = hoverModeFlows; | ||
| 935 | + requestDeviceLinkFlows(); | ||
| 936 | + network.view.flash('Device Flows'); | ||
| 937 | + } | ||
| 938 | + | ||
| 939 | + function requestDeviceLinkFlows() { | ||
| 940 | + if (validateSelectionContext()) { | ||
| 941 | + var hoverId = (hoverMode === hoverModeFlows && hovered && | ||
| 942 | + (hovered.class === 'device')) ? hovered.id : ''; | ||
| 943 | + sendMessage('requestDeviceLinkFlows', { | ||
| 944 | + ids: selectOrder, | ||
| 945 | + hover: hoverId | ||
| 946 | + }); | ||
| 947 | + } | ||
| 948 | + } | ||
| 949 | + | ||
| 950 | + | ||
| 917 | function showAllTrafficAction() { | 951 | function showAllTrafficAction() { |
| 918 | - cancelTraffic(); | 952 | + hoverMode = hoverModeAll; |
| 919 | - sendMessage('requestAllTraffic', {}); | 953 | + requestAllTraffic(); |
| 920 | network.view.flash('All Traffic'); | 954 | network.view.flash('All Traffic'); |
| 921 | } | 955 | } |
| 922 | 956 | ||
| 923 | - function showDeviceLinkFlowsAction() { | 957 | + function requestAllTraffic() { |
| 924 | - cancelTraffic(); | 958 | + sendMessage('requestAllTraffic', {}); |
| 925 | - hoverMode = 2; | ||
| 926 | - showDeviceLinkFlows(); | ||
| 927 | - network.view.flash('Device Flows'); | ||
| 928 | } | 959 | } |
| 929 | 960 | ||
| 930 | - function showDeviceLinkFlows() { | 961 | + function validateSelectionContext() { |
| 931 | - // if nothing is hovered over, and nothing selected, send cancel request | ||
| 932 | if (!hovered && nSel() === 0) { | 962 | if (!hovered && nSel() === 0) { |
| 933 | cancelTraffic(); | 963 | cancelTraffic(); |
| 934 | - return; | 964 | + return false; |
| 935 | } | 965 | } |
| 936 | - var hoverId = (flowsHover() && hovered && hovered.class === 'device') ? | 966 | + return true; |
| 937 | - hovered.id : ''; | ||
| 938 | - sendMessage('requestDeviceLinkFlows', { | ||
| 939 | - ids: selectOrder, | ||
| 940 | - hover: hoverId | ||
| 941 | - }); | ||
| 942 | } | 967 | } |
| 943 | 968 | ||
| 944 | // TODO: these should be moved out to utility module. | 969 | // TODO: these should be moved out to utility module. |
| ... | @@ -1547,20 +1572,12 @@ | ... | @@ -1547,20 +1572,12 @@ |
| 1547 | 1572 | ||
| 1548 | function nodeMouseOver(d) { | 1573 | function nodeMouseOver(d) { |
| 1549 | hovered = d; | 1574 | hovered = d; |
| 1550 | - if (trafficHover() && (d.class === 'host' || d.class === 'device')) { | 1575 | + requestTrafficForMode(); |
| 1551 | - showSelectTraffic(); | ||
| 1552 | - } else if (flowsHover() && (d.class === 'device')) { | ||
| 1553 | - showDeviceLinkFlows(); | ||
| 1554 | - } | ||
| 1555 | } | 1576 | } |
| 1556 | 1577 | ||
| 1557 | function nodeMouseOut(d) { | 1578 | function nodeMouseOut(d) { |
| 1558 | hovered = null; | 1579 | hovered = null; |
| 1559 | - if (trafficHover() && (d.class === 'host' || d.class === 'device')) { | 1580 | + requestTrafficForMode(); |
| 1560 | - showSelectTraffic(); | ||
| 1561 | - } else if (flowsHover() && (d.class === 'device')) { | ||
| 1562 | - showDeviceLinkFlows(); | ||
| 1563 | - } | ||
| 1564 | } | 1581 | } |
| 1565 | 1582 | ||
| 1566 | function addHostIcon(node, radius, iid) { | 1583 | function addHostIcon(node, radius, iid) { |
| ... | @@ -2002,22 +2019,29 @@ | ... | @@ -2002,22 +2019,29 @@ |
| 2002 | function updateDetailPane() { | 2019 | function updateDetailPane() { |
| 2003 | var nSel = selectOrder.length; | 2020 | var nSel = selectOrder.length; |
| 2004 | if (!nSel) { | 2021 | if (!nSel) { |
| 2005 | - detailPane.hide(); | 2022 | + emptySelect(); |
| 2006 | - cancelTraffic(); | ||
| 2007 | } else if (nSel === 1) { | 2023 | } else if (nSel === 1) { |
| 2008 | singleSelect(); | 2024 | singleSelect(); |
| 2025 | + requestTrafficForMode(); | ||
| 2009 | } else { | 2026 | } else { |
| 2010 | multiSelect(); | 2027 | multiSelect(); |
| 2011 | } | 2028 | } |
| 2012 | } | 2029 | } |
| 2013 | 2030 | ||
| 2031 | + function emptySelect() { | ||
| 2032 | + detailPane.hide(); | ||
| 2033 | + cancelTraffic(); | ||
| 2034 | + } | ||
| 2035 | + | ||
| 2014 | function singleSelect() { | 2036 | function singleSelect() { |
| 2037 | + // NOTE: detail is shown from showDetails event callback | ||
| 2015 | requestDetails(); | 2038 | requestDetails(); |
| 2016 | - // NOTE: detail pane will be shown from showDetails event callback | 2039 | + requestTrafficForMode(); |
| 2017 | } | 2040 | } |
| 2018 | 2041 | ||
| 2019 | function multiSelect() { | 2042 | function multiSelect() { |
| 2020 | populateMultiSelect(); | 2043 | populateMultiSelect(); |
| 2044 | + requestTrafficForMode(); | ||
| 2021 | } | 2045 | } |
| 2022 | 2046 | ||
| 2023 | function addSep(tbody) { | 2047 | function addSep(tbody) { |
| ... | @@ -2127,7 +2151,9 @@ | ... | @@ -2127,7 +2151,9 @@ |
| 2127 | addAction(detailPane, 'Show Related Traffic', showTrafficAction); | 2151 | addAction(detailPane, 'Show Related Traffic', showTrafficAction); |
| 2128 | // if exactly two hosts are selected, also want 'add host intent' | 2152 | // if exactly two hosts are selected, also want 'add host intent' |
| 2129 | if (nSel() === 2 && allSelectionsClass('host')) { | 2153 | if (nSel() === 2 && allSelectionsClass('host')) { |
| 2130 | - addAction(detailPane, 'Add Host-to-Host Intent', addIntentAction); | 2154 | + addAction(detailPane, 'Create Host-to-Host Flow', addHostIntentAction); |
| 2155 | + } else if (nSel() >= 2 && allSelectionsClass('host')) { | ||
| 2156 | + addAction(detailPane, 'Create Multi-Source Flow', addMultiSourceIntentAction); | ||
| 2131 | } | 2157 | } |
| 2132 | } | 2158 | } |
| 2133 | 2159 | ||
| ... | @@ -2239,14 +2265,6 @@ | ... | @@ -2239,14 +2265,6 @@ |
| 2239 | return false; | 2265 | return false; |
| 2240 | } | 2266 | } |
| 2241 | 2267 | ||
| 2242 | - function trafficHover() { | ||
| 2243 | - return hoverModes[hoverMode] === 'intents'; | ||
| 2244 | - } | ||
| 2245 | - | ||
| 2246 | - function flowsHover() { | ||
| 2247 | - return hoverModes[hoverMode] === 'flows'; | ||
| 2248 | - } | ||
| 2249 | - | ||
| 2250 | function loadGlyphs(svg) { | 2268 | function loadGlyphs(svg) { |
| 2251 | var defs = svg.append('defs'); | 2269 | var defs = svg.append('defs'); |
| 2252 | gly.defBird(defs); | 2270 | gly.defBird(defs); | ... | ... |
-
Please register or login to post a comment