diff --git a/core/net/src/main/java/org/onlab/onos/net/topology/impl/DefaultTopologyProvider.java b/core/net/src/main/java/org/onlab/onos/net/topology/impl/DefaultTopologyProvider.java index 44aa9f7567..3b1a4a3a23 100644 --- a/core/net/src/main/java/org/onlab/onos/net/topology/impl/DefaultTopologyProvider.java +++ b/core/net/src/main/java/org/onlab/onos/net/topology/impl/DefaultTopologyProvider.java @@ -66,7 +66,7 @@ import static org.slf4j.LoggerFactory.getLogger; public class DefaultTopologyProvider extends AbstractProvider implements TopologyProvider { - private static final int MAX_THREADS = 8; + private static final int MAX_THREADS = 32; private static final int DEFAULT_MAX_EVENTS = 1000; private static final int DEFAULT_MAX_IDLE_MS = 10; private static final int DEFAULT_MAX_BATCH_MS = 50; diff --git a/tools/test/topos/topo-200sw-linkalarm.py b/tools/test/topos/topo-200sw-linkalarm.py new file mode 100644 index 0000000000..bb964e2b25 --- /dev/null +++ b/tools/test/topos/topo-200sw-linkalarm.py @@ -0,0 +1,20 @@ + +from mininet.topo import Topo + +class MyTopo( Topo ): + "10 'floating' switch topology" + + def __init__( self ): + # Initialize topology + Topo.__init__( self ) + + sw_list = [] + swC = self.addSwitch('sc', dpid = 'ffffffff00000001') + + for i in range(1, 201): + switch=self.addSwitch('s'+str(i), dpid = str(i).zfill(16)) + self.addLink(switch,swC) + + sw_list.append(switch) + +topos = { 'mytopo': ( lambda: MyTopo() ) } diff --git a/web/gui/src/main/java/org/onlab/onos/gui/TopologyViewIntentFilter.java b/web/gui/src/main/java/org/onlab/onos/gui/TopologyViewIntentFilter.java index 2585b84e67..e5c92492b3 100644 --- a/web/gui/src/main/java/org/onlab/onos/gui/TopologyViewIntentFilter.java +++ b/web/gui/src/main/java/org/onlab/onos/gui/TopologyViewIntentFilter.java @@ -33,6 +33,7 @@ import org.onlab.onos.net.intent.PathIntent; import org.onlab.onos.net.intent.PointToPointIntent; import org.onlab.onos.net.link.LinkService; +import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -71,17 +72,19 @@ public class TopologyViewIntentFilter { * Finds all path (host-to-host or point-to-point) intents that pertains * to the given hosts. * - * @param hosts set of hosts to query by - * @param devices set of devices to query by + * @param hosts set of hosts to query by + * @param devices set of devices to query by + * @param sourceIntents collection of intents to search * @return set of intents that 'match' all hosts and devices given */ - Set findPathIntents(Set hosts, Set devices) { + List findPathIntents(Set hosts, Set devices, + Iterable sourceIntents) { // Derive from this the set of edge connect points. Set edgePoints = getEdgePoints(hosts); // Iterate over all intents and produce a set that contains only those // intents that target all selected hosts or derived edge connect points. - return getIntents(hosts, devices, edgePoints); + return getIntents(hosts, devices, edgePoints, sourceIntents); } @@ -94,10 +97,11 @@ public class TopologyViewIntentFilter { return edgePoints; } - // Produces a set of intents that target all selected hosts, devices or connect points. - private Set getIntents(Set hosts, Set devices, - Set edgePoints) { - Set intents = new HashSet<>(); + // Produces a list of intents that target all selected hosts, devices or connect points. + private List getIntents(Set hosts, Set devices, + Set edgePoints, + Iterable sourceIntents) { + List intents = new ArrayList<>(); if (hosts.isEmpty() && devices.isEmpty()) { return intents; } @@ -105,7 +109,7 @@ public class TopologyViewIntentFilter { Set opticalIntents = new HashSet<>(); // Search through all intents and see if they are relevant to our search. - for (Intent intent : intentService.getIntents()) { + for (Intent intent : sourceIntents) { if (intentService.getIntentState(intent.id()) == INSTALLED) { boolean isRelevant = false; if (intent instanceof HostToHostIntent) { @@ -140,7 +144,7 @@ public class TopologyViewIntentFilter { } // Indicates whether the specified intent involves all of the given hosts. - private boolean isIntentRelevantToHosts(HostToHostIntent intent, Set hosts) { + private boolean isIntentRelevantToHosts(HostToHostIntent intent, Iterable hosts) { for (Host host : hosts) { HostId id = host.id(); // Bail if intent does not involve this host. @@ -152,7 +156,7 @@ public class TopologyViewIntentFilter { } // Indicates whether the specified intent involves all of the given devices. - private boolean isIntentRelevantToDevices(Intent intent, Set devices) { + private boolean isIntentRelevantToDevices(Intent intent, Iterable devices) { List installables = intentService.getInstallableIntents(intent.id()); for (Device device : devices) { if (!isIntentRelevantToDevice(installables, device)) { @@ -192,7 +196,8 @@ public class TopologyViewIntentFilter { return false; } - private boolean isIntentRelevant(PointToPointIntent intent, Set edgePoints) { + private boolean isIntentRelevant(PointToPointIntent intent, + Iterable edgePoints) { for (ConnectPoint point : edgePoints) { // Bail if intent does not involve this edge point. if (!point.equals(intent.egressPoint()) && @@ -205,7 +210,7 @@ public class TopologyViewIntentFilter { // Indicates whether the specified intent involves all of the given edge points. private boolean isIntentRelevant(MultiPointToSinglePointIntent intent, - Set edgePoints) { + Iterable edgePoints) { for (ConnectPoint point : edgePoints) { // Bail if intent does not involve this edge point. if (!point.equals(intent.egressPoint()) && @@ -218,7 +223,7 @@ public class TopologyViewIntentFilter { // Indicates whether the specified intent involves all of the given edge points. private boolean isIntentRelevant(OpticalConnectivityIntent opticalIntent, - Set intents) { + Iterable intents) { Link ccSrc = getFirstLink(opticalIntent.getSrc(), false); Link ccDst = getFirstLink(opticalIntent.getDst(), true); diff --git a/web/gui/src/main/java/org/onlab/onos/gui/TopologyViewMessages.java b/web/gui/src/main/java/org/onlab/onos/gui/TopologyViewMessages.java index c49fbeea1b..486e375f7e 100644 --- a/web/gui/src/main/java/org/onlab/onos/gui/TopologyViewMessages.java +++ b/web/gui/src/main/java/org/onlab/onos/gui/TopologyViewMessages.java @@ -676,13 +676,16 @@ public abstract class TopologyViewMessages { List installables = intentService.getInstallableIntents(intent.id()); if (installables != null) { for (Intent installable : installables) { - String cls = isOptical ? trafficClass.type + " optical" : trafficClass.type; + String type = isOptical ? trafficClass.type + " optical" : trafficClass.type; if (installable instanceof PathIntent) { - classifyLinks(cls, biLinks, ((PathIntent) installable).path().links()); + classifyLinks(type, biLinks, trafficClass.showTraffic, + ((PathIntent) installable).path().links()); } else if (installable instanceof LinkCollectionIntent) { - classifyLinks(cls, biLinks, ((LinkCollectionIntent) installable).links()); + classifyLinks(type, biLinks, trafficClass.showTraffic, + ((LinkCollectionIntent) installable).links()); } else if (installable instanceof OpticalPathIntent) { - classifyLinks(cls, biLinks, ((OpticalPathIntent) installable).path().links()); + classifyLinks(type, biLinks, trafficClass.showTraffic, + ((OpticalPathIntent) installable).path().links()); } } } @@ -695,12 +698,14 @@ public abstract class TopologyViewMessages { // Adds the link segments (path or tree) associated with the specified // connectivity intent private void classifyLinks(String type, Map biLinks, - Iterable links) { + boolean showTraffic, Iterable links) { if (links != null) { for (Link link : links) { BiLink biLink = addLink(biLinks, link); if (isInfrastructureEgress(link)) { - biLink.addLoad(statService.load(link)); + if (showTraffic) { + biLink.addLoad(statService.load(link)); + } biLink.addClass(type); } } @@ -862,12 +867,18 @@ public abstract class TopologyViewMessages { // Auxiliary carrier of data for requesting traffic message. protected class TrafficClass { + public final boolean showTraffic; public final String type; - public final Set intents; + public final Iterable intents; - TrafficClass(String type, Set intents) { + TrafficClass(String type, Iterable intents) { + this(type, intents, false); + } + + TrafficClass(String type, Iterable intents, boolean showTraffic) { this.type = type; this.intents = intents; + this.showTraffic = showTraffic; } } diff --git a/web/gui/src/main/java/org/onlab/onos/gui/TopologyViewWebSocket.java b/web/gui/src/main/java/org/onlab/onos/gui/TopologyViewWebSocket.java index 9e46da33e3..bd0206598f 100644 --- a/web/gui/src/main/java/org/onlab/onos/gui/TopologyViewWebSocket.java +++ b/web/gui/src/main/java/org/onlab/onos/gui/TopologyViewWebSocket.java @@ -90,7 +90,8 @@ public class TopologyViewWebSocket private static final String APP_ID = "org.onlab.onos.gui"; - private static final long TRAFFIC_FREQUENCY_SEC = 2000; + private static final long TRAFFIC_FREQUENCY = 2000; + private static final long SUMMARY_FREQUENCY = 30000; private static final Comparator NODE_COMPARATOR = new Comparator() { @@ -103,9 +104,9 @@ public class TopologyViewWebSocket private final Timer timer = new Timer("topology-view"); - private static final int MAX_EVENTS = 500; - private static final int MAX_BATCH_MS = 1000; - private static final int MAX_IDLE_MS = 500; + private static final int MAX_EVENTS = 1000; + private static final int MAX_BATCH_MS = 5000; + private static final int MAX_IDLE_MS = 1000; private final ApplicationId appId; @@ -122,15 +123,23 @@ public class TopologyViewWebSocket private final EventAccumulator eventAccummulator = new InternalEventAccummulator(); - private boolean summaryEnabled = true; private TimerTask trafficTask; private ObjectNode trafficEvent; + private TimerTask summaryTask; + private ObjectNode summaryEvent; + private long lastActive = System.currentTimeMillis(); private boolean listenersRemoved = false; private TopologyViewIntentFilter intentFilter; + // Current selection context + private Set selectedHosts; + private Set selectedDevices; + private List selectedIntents; + private int currentIntentIndex = -1; + /** * Creates a new web-socket for serving data to GUI topology view. * @@ -204,7 +213,6 @@ public class TopologyViewWebSocket processMessage((ObjectNode) mapper.reader().readTree(data)); } catch (Exception e) { log.warn("Unable to parse GUI request {} due to {}", data, e); - log.warn("Boom!!!!", e); } } @@ -221,19 +229,29 @@ public class TopologyViewWebSocket } else if (type.equals("addMultiSourceIntent")) { createMultiSourceIntent(event); - } else if (type.equals("requestTraffic")) { - requestTraffic(event); + } else if (type.equals("requestRelatedIntents")) { + requestRelatedIntents(event); + } else if (type.equals("requestNextRelatedIntent")) { + requestNextRelatedIntent(event); + } else if (type.equals("requestSelectedIntentTraffic")) { + requestSelectedIntentTraffic(event); + } else if (type.equals("requestAllTraffic")) { requestAllTraffic(event); + startTrafficMonitoring(event); + } else if (type.equals("requestDeviceLinkFlows")) { requestDeviceLinkFlows(event); + startTrafficMonitoring(event); + } else if (type.equals("cancelTraffic")) { cancelTraffic(event); } else if (type.equals("requestSummary")) { requestSummary(event); + startSummaryMonitoring(event); } else if (type.equals("cancelSummary")) { - cancelSummary(event); + stopSummaryMonitoring(); } else if (type.equals("equalizeMasters")) { equalizeMasters(event); @@ -324,8 +342,9 @@ public class TopologyViewWebSocket new HostToHostIntent(appId, one, two, DefaultTrafficSelector.builder().build(), DefaultTrafficTreatment.builder().build()); - startMonitoring(event); + intentService.submit(intent); + startMonitoringIntent(event, intent); } // Creates multi-source-to-single-dest intent. @@ -348,10 +367,24 @@ public class TopologyViewWebSocket MultiPointToSinglePointIntent intent = new MultiPointToSinglePointIntent(appId, selector, treatment, ingressPoints, dstHost.location()); - startMonitoring(event); + intentService.submit(intent); + startMonitoringIntent(event, intent); } + + private synchronized void startMonitoringIntent(ObjectNode event, Intent intent) { + selectedHosts = new HashSet<>(); + selectedDevices = new HashSet<>(); + selectedIntents = new ArrayList<>(); + selectedIntents.add(intent); + currentIntentIndex = -1; + requestNextRelatedIntent(event); + requestSelectedIntentTraffic(event); + } + + + private Set getHostLocations(Set hostIds) { Set points = new HashSet<>(); for (HostId hostId : hostIds) { @@ -374,17 +407,15 @@ public class TopologyViewWebSocket } - private synchronized long startMonitoring(ObjectNode event) { - if (trafficTask != null) { - stopMonitoring(); - } + private synchronized long startTrafficMonitoring(ObjectNode event) { + stopTrafficMonitoring(); trafficEvent = event; trafficTask = new TrafficMonitor(); - timer.schedule(trafficTask, TRAFFIC_FREQUENCY_SEC, TRAFFIC_FREQUENCY_SEC); + timer.schedule(trafficTask, TRAFFIC_FREQUENCY, TRAFFIC_FREQUENCY); return number(event, "sid"); } - private synchronized void stopMonitoring() { + private synchronized void stopTrafficMonitoring() { if (trafficTask != null) { trafficTask.cancel(); trafficTask = null; @@ -394,13 +425,13 @@ public class TopologyViewWebSocket // Subscribes for host traffic messages. private synchronized void requestAllTraffic(ObjectNode event) { - long sid = startMonitoring(event); + long sid = startTrafficMonitoring(event); sendMessage(trafficSummaryMessage(sid)); } private void requestDeviceLinkFlows(ObjectNode event) { ObjectNode payload = payload(event); - long sid = startMonitoring(event); + long sid = startTrafficMonitoring(event); // Get the set of selected hosts and their intents. ArrayNode ids = (ArrayNode) payload.path("ids"); @@ -416,58 +447,122 @@ public class TopologyViewWebSocket } - // Subscribes for host traffic messages. - private synchronized void requestTraffic(ObjectNode event) { + // Requests related intents message. + private synchronized void requestRelatedIntents(ObjectNode event) { ObjectNode payload = payload(event); if (!payload.has("ids")) { return; } - long sid = startMonitoring(event); + long sid = number(event, "sid"); - // Get the set of selected hosts and their intents. - ArrayNode ids = (ArrayNode) payload.path("ids"); - Set hosts = getHosts(ids); - Set devices = getDevices(ids); - Set intents = intentFilter.findPathIntents(hosts, devices); + // Cancel any other traffic monitoring mode. + stopTrafficMonitoring(); - // If there is a hover node, include it in the hosts and find intents. String hover = string(payload, "hover"); - Set hoverIntents; + if (haveSelectedIntents()) { + // Get the set of selected hosts and their intents. + ArrayNode ids = (ArrayNode) payload.path("ids"); + selectedHosts = getHosts(ids); + selectedDevices = getDevices(ids); + selectedIntents = intentFilter.findPathIntents(selectedHosts, selectedDevices, + intentService.getIntents()); + currentIntentIndex = -1; + + // Send a message to highlight all links of all monitored intents. + sendMessage(trafficMessage(sid, new TrafficClass("primary", selectedIntents))); + } + if (!isNullOrEmpty(hover)) { - addHover(hosts, devices, hover); - hoverIntents = intentFilter.findPathIntents(hosts, devices); - intents.removeAll(hoverIntents); + // If there is a hover node, include it in the selection and find intents. + processExtendedSelection(sid, hover); + } + } - // Send an initial message to highlight all links of all monitored intents. - sendMessage(trafficMessage(sid, - new TrafficClass("primary", hoverIntents), - new TrafficClass("secondary", intents))); + private boolean haveSelectedIntents() { + return selectedIntents != null && !selectedIntents.isEmpty(); + } - } else { - // Send an initial message to highlight all links of all monitored intents. - sendMessage(trafficMessage(sid, new TrafficClass("primary", intents))); + private void processExtendedSelection(long sid, String hover) { + Set hoverSelHosts = new HashSet<>(selectedHosts); + Set hoverSelDevices = new HashSet<>(selectedDevices); + addHover(hoverSelHosts, hoverSelDevices, hover); + + List primary = + intentFilter.findPathIntents(hoverSelHosts, hoverSelDevices, + selectedIntents); + Set secondary = new HashSet<>(selectedIntents); + secondary.removeAll(primary); + + // Send a message to highlight all links of all monitored intents. + sendMessage(trafficMessage(sid, new TrafficClass("primary", primary), + new TrafficClass("secondary", secondary))); + } + + // Requests next of the related intents. + private void requestNextRelatedIntent(ObjectNode event) { + if (haveSelectedIntents()) { + currentIntentIndex = (currentIntentIndex + 1) % selectedIntents.size(); + Intent selectedIntent = selectedIntents.get(currentIntentIndex); + log.info("Requested next intent {}", selectedIntent.id()); + + Set primary = new HashSet<>(); + primary.add(selectedIntent); + + Set secondary = new HashSet<>(selectedIntents); + secondary.remove(selectedIntent); + + // Send a message to highlight all links of the selected intent. + sendMessage(trafficMessage(number(event, "sid"), + new TrafficClass("primary", primary), + new TrafficClass("secondary", secondary))); + } + } + + // Requests monitoring of traffic for the selected intent. + private void requestSelectedIntentTraffic(ObjectNode event) { + if (haveSelectedIntents()) { + Intent selectedIntent = selectedIntents.get(currentIntentIndex); + log.info("Requested traffic for selected {}", selectedIntent.id()); + + Set primary = new HashSet<>(); + primary.add(selectedIntent); + + // Send a message to highlight all links of the selected intent. + sendMessage(trafficMessage(number(event, "sid"), + new TrafficClass("primary", primary, true))); } } // Cancels sending traffic messages. private void cancelTraffic(ObjectNode event) { + selectedIntents = null; sendMessage(trafficMessage(number(event, "sid"))); - stopMonitoring(); + stopTrafficMonitoring(); } + private synchronized long startSummaryMonitoring(ObjectNode event) { + stopSummaryMonitoring(); + summaryEvent = event; + summaryTask = new SummaryMonitor(); + timer.schedule(summaryTask, SUMMARY_FREQUENCY, SUMMARY_FREQUENCY); + return number(event, "sid"); + } + + private synchronized void stopSummaryMonitoring() { + if (summaryEvent != null) { + summaryTask.cancel(); + summaryTask = null; + summaryEvent = null; + } + } + // Subscribes for summary messages. private synchronized void requestSummary(ObjectNode event) { - summaryEnabled = true; sendMessage(summmaryMessage(number(event, "sid"))); } - // Cancels sending summary messages. - private synchronized void cancelSummary(ObjectNode event) { - summaryEnabled = false; - } - // Forces mastership role rebalancing. private void equalizeMasters(ObjectNode event) { @@ -550,7 +645,7 @@ public class TopologyViewWebSocket @Override public void event(IntentEvent event) { if (trafficEvent != null) { - requestTraffic(trafficEvent); + requestSelectedIntentTraffic(trafficEvent); } eventAccummulator.add(event); } @@ -564,6 +659,7 @@ public class TopologyViewWebSocket } } + // Periodic update of the traffic information private class TrafficMonitor extends TimerTask { @Override public void run() { @@ -574,8 +670,8 @@ public class TopologyViewWebSocket requestAllTraffic(trafficEvent); } else if (type.equals("requestDeviceLinkFlows")) { requestDeviceLinkFlows(trafficEvent); - } else { - requestTraffic(trafficEvent); + } else if (type.equals("requestSelectedIntentTraffic")) { + requestSelectedIntentTraffic(trafficEvent); } } } catch (Exception e) { @@ -585,6 +681,20 @@ public class TopologyViewWebSocket } } + // Periodic update of the summary information + private class SummaryMonitor extends TimerTask { + @Override + public void run() { + try { + if (summaryEvent != null) { + requestSummary(summaryEvent); + } + } catch (Exception e) { + log.warn("Unable to handle summary request due to {}", e.getMessage()); + } + } + } + // Accumulates events to drive methodic update of the summary pane. private class InternalEventAccummulator extends AbstractEventAccumulator { protected InternalEventAccummulator() { @@ -594,7 +704,7 @@ public class TopologyViewWebSocket @Override public void processEvents(List events) { try { - if (summaryEnabled) { + if (summaryEvent != null) { sendMessage(summmaryMessage(0)); } } catch (Exception e) { diff --git a/web/gui/src/main/webapp/topo.js b/web/gui/src/main/webapp/topo.js index f52b0c91d6..ea0d731156 100644 --- a/web/gui/src/main/webapp/topo.js +++ b/web/gui/src/main/webapp/topo.js @@ -145,8 +145,10 @@ P: togglePorts, U: [unpin, 'Unpin node (hover mouse over)'], R: [resetPanZoom, 'Reset pan / zoom'], - V: [showTrafficAction, 'Show related traffic'], - A: [showAllTrafficAction, 'Show all traffic'], + V: [showRelatedIntentsAction, 'Show all related intents'], + N: [showNextIntentAction, 'Show next related intent'], + W: [showSelectedIntentTrafficAction, 'Monitor traffic of selected intent'], + A: [showAllTrafficAction, 'Monitor all traffic'], F: [showDeviceLinkFlowsAction, 'Show device link flows'], X: [toggleNodeLock, 'Lock / unlock node positions'], Z: [toggleOblique, 'Toggle oblique view (Experimental)'], @@ -209,10 +211,11 @@ oblique = false; // constants - var hoverModeAll = 1, + var hoverModeNone = 0, + hoverModeAll = 1, hoverModeFlows = 2, hoverModeIntents = 3, - hoverMode = hoverModeFlows; + hoverMode = hoverModeNone; // D3 selections var svg, @@ -394,7 +397,7 @@ cancelSummary(); stopAntTimer(); } else { - hoverMode = hoverModeFlows; + hoverMode = hoverModeNone; } } @@ -1219,22 +1222,20 @@ } function requestTrafficForMode() { - if (hoverMode === hoverModeAll) { - requestAllTraffic(); - } else if (hoverMode === hoverModeFlows) { + if (hoverMode === hoverModeFlows) { requestDeviceLinkFlows(); } else if (hoverMode === hoverModeIntents) { - requestSelectTraffic(); + requestRelatedIntents(); } } - function showTrafficAction() { + function showRelatedIntentsAction() { hoverMode = hoverModeIntents; - requestSelectTraffic(); - flash('Related Traffic'); + requestRelatedIntents(); + flash('Related intents'); } - function requestSelectTraffic() { + function requestRelatedIntents() { function hoverValid() { return hoverMode === hoverModeIntents && hovered && @@ -1242,13 +1243,24 @@ } if (validateSelectionContext()) { - sendMessage('requestTraffic', { + sendMessage('requestRelatedIntents', { ids: selectOrder, hover: hoverValid() ? hovered.id : '' }); } } + function showNextIntentAction() { + hoverMode = hoverModeNone; + sendMessage('requestNextRelatedIntent', {}); + flash('Next related intent'); + } + + function showSelectedIntentTrafficAction() { + hoverMode = hoverModeNone; + sendMessage('requestSelectedIntentTraffic', {}); + flash('Monitoring selected intent'); + } function showDeviceLinkFlowsAction() { hoverMode = hoverModeFlows; @@ -2010,13 +2022,17 @@ } function nodeMouseOver(d) { - hovered = d; - requestTrafficForMode(); + if (hovered != d) { + hovered = d; + requestTrafficForMode(); + } } function nodeMouseOut(d) { - hovered = null; - requestTrafficForMode(); + if (hovered != null) { + hovered = null; + requestTrafficForMode(); + } } function addHostIcon(node, radius, iid) { @@ -2498,7 +2514,7 @@ wsTrace('rx', msg); } function wsTrace(rxtx, msg) { - console.log('[' + rxtx + '] ' + msg); + // console.log('[' + rxtx + '] ' + msg); } // NOTE: Temporary hardcoded example for showing detail pane @@ -2620,7 +2636,6 @@ emptySelect(); } else if (nSel === 1) { singleSelect(); - requestTrafficForMode(); } else { multiSelect(); } @@ -2635,12 +2650,14 @@ function singleSelect() { // NOTE: detail is shown from showDetails event callback requestDetails(); + cancelTraffic(); requestTrafficForMode(); } function multiSelect() { haveDetails = true; populateMultiSelect(); + cancelTraffic(); requestTrafficForMode(); } @@ -2738,7 +2755,7 @@ function addSingleSelectActions(data) { detailPane.append('hr'); // always want to allow 'show traffic' - addAction(detailPane, 'Show Related Traffic', showTrafficAction); + addAction(detailPane, 'Show Related Traffic', showRelatedIntentsAction); if (data.type === 'switch') { addAction(detailPane, 'Show Device Flows', showDeviceLinkFlowsAction); @@ -2748,7 +2765,7 @@ function addMultiSelectActions() { detailPane.append('hr'); // always want to allow 'show traffic' - addAction(detailPane, 'Show Related Traffic', showTrafficAction); + addAction(detailPane, 'Show Related Traffic', showRelatedIntentsAction); // if exactly two hosts are selected, also want 'add host intent' if (nSel() === 2 && allSelectionsClass('host')) { addAction(detailPane, 'Create Host-to-Host Flow', addHostIntentAction);