| /* |
| * Copyright 2016-present Open Networking Laboratory |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package org.onosproject.pceweb; |
| |
| import com.fasterxml.jackson.databind.node.ObjectNode; |
| import com.google.common.collect.ImmutableSet; |
| |
| import org.onlab.osgi.ServiceDirectory; |
| import org.onlab.packet.IpAddress; |
| import org.onlab.util.DataRateUnit; |
| import org.onosproject.net.Device; |
| import org.onosproject.net.DeviceId; |
| import org.onosproject.net.ElementId; |
| import org.onosproject.net.HostId; |
| import org.onosproject.net.Link; |
| import org.onosproject.net.Path; |
| import org.onosproject.net.topology.TopologyEvent; |
| import org.onosproject.net.topology.TopologyListener; |
| import org.onosproject.net.topology.TopologyService; |
| import org.onosproject.ui.RequestHandler; |
| import org.onosproject.ui.UiConnection; |
| import org.onosproject.ui.UiMessageHandler; |
| import org.onosproject.ui.topo.DeviceHighlight; |
| import org.onosproject.ui.topo.Highlights; |
| import org.onosproject.ui.topo.LinkHighlight; |
| import org.onosproject.ui.topo.Mod; |
| import org.onosproject.ui.topo.NodeBadge; |
| import org.onosproject.ui.topo.TopoJson; |
| import org.onosproject.ui.topo.TopoUtils; |
| import org.onosproject.net.device.DeviceService; |
| import org.onosproject.net.intent.Constraint; |
| import org.onosproject.pce.pceservice.LspType; |
| import org.onosproject.pce.pceservice.api.PceService; |
| import org.onosproject.pce.pceservice.constraint.CostConstraint; |
| import org.onosproject.net.intent.constraint.BandwidthConstraint; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| import org.onosproject.incubator.net.tunnel.IpTunnelEndPoint; |
| import org.onosproject.incubator.net.tunnel.Tunnel; |
| import org.onosproject.incubator.net.tunnel.TunnelEndPoint; |
| import org.onosproject.incubator.net.tunnel.TunnelEvent; |
| import org.onosproject.incubator.net.tunnel.TunnelId; |
| import org.onosproject.incubator.net.tunnel.TunnelListener; |
| import org.onosproject.incubator.net.tunnel.TunnelService; |
| import static org.onosproject.ui.topo.LinkHighlight.Flavor.*; |
| import static org.onosproject.incubator.net.tunnel.Tunnel.Type.MPLS; |
| |
| import com.fasterxml.jackson.databind.node.ArrayNode; |
| |
| import java.util.Collection; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.Set; |
| |
| /** |
| * ONOS UI PCE WEB Topology-Overlay message handler. |
| */ |
| public class PceWebTopovMessageHandler extends UiMessageHandler { |
| |
| private static final String PCEWEB_CLEAR = "pceTopovClear"; |
| private static final String PCEWEB_SET_PATH = "pceTopovSetMode"; |
| private static final String PCEWEB_UPDATE_PATH_QUERY = "pceTopovUpdateQuery"; |
| private static final String PCEWEB_UPDATE_PATH = "pceTopovUpdate"; |
| private static final String PCEWEB_REMOVE_PATH_QUERY = "pceTopovRemQuery"; |
| private static final String PCEWEB_REMOVE_PATH = "pceTopovRem"; |
| private static final String PCEWEB_QUERY_TUNNELS = "pceTopovTunnelDisplay"; |
| private static final String PCEWEB_SHOW_TUNNEL = "pceTopovShowTunnels"; |
| private static final String PCEWEB_SHOW_TUNNEL_REMOVE = "pceTopovShowTunnelsRem"; |
| private static final String PCEWEB_TUNNEL_UPDATE_INFO = "updatePathmsgInfo"; |
| private static final String PCEWEB_TUNNEL_UPDATE_INFO_REPLY = "pceTopovShowTunnelsUpdate"; |
| private static final String DST = "DST"; |
| private static final String SRC = "SRC"; |
| private static final String BANDWIDTH = "bw"; |
| private static final String BANDWIDTHTYPE = "bwtype"; |
| private static final String COSTTYPE = "ctype"; |
| private static final String LSPTYPE = "lsptype"; |
| private static final String SRCID = "srid"; |
| private static final String DSTID = "dsid"; |
| private static final String TUNNEL_ID = "tunnelid"; |
| private static final String TUNNEL_NAME = "tunnelname"; |
| private static final String COST_TYPE_IGP = "igp"; |
| private static final String COST_TYPE_TE = "te"; |
| private static final String BANDWIDTH_TYPE_KBPS = "kbps"; |
| private static final String BANDWIDTH_TYPE_MBPS = "kbps"; |
| private static final String BUFFER_ARRAY = "a"; |
| private static final String BANDWIDTH_BPS = "BPS"; |
| private static final String LSP_TYPE_CR = "cr"; |
| private static final String LSP_TYPE_SRBE = "srbe"; |
| private static final String LSP_TYPE_SRTE = "srte"; |
| private static final String STRING_NULL = "null"; |
| // Delay for showHighlights event processing on GUI client side to |
| // account for addLink animation. |
| private static final int DELAY_MS = 1100; |
| private static final double BANDWIDTH_KBPS = 1_000; |
| private static final double BANDWIDTH_MBPS = 1_000_000; |
| private static String[] linkColor = {"pCol1", "pCol2", "pCol3", "pCol4", "pCol5", |
| "pCol6", "pCol7", "pCol8", "pCol9", "pCol10", |
| "pCol11", "pCol12", "pCol13", "pCol14", "pCol15"}; |
| private static final int LINK_COLOR_MAX = 15; |
| private Set<Link> allPathLinks; |
| private ElementId src, dst; |
| private List<Path> paths = new LinkedList<>(); |
| private int pathIndex; |
| |
| private final Logger log = LoggerFactory.getLogger(getClass()); |
| private final TopologyListener topologyListener = new InternalTopologyListener(); |
| private final TunnelListener tunnelListener = new InnerPceWebTunnelListener(); |
| |
| protected TopologyService topologyService; |
| protected TunnelService tunnelService; |
| protected PceService pceService; |
| protected DeviceService deviceService; |
| @Override |
| public void init(UiConnection connection, ServiceDirectory directory) { |
| |
| super.init(connection, directory); |
| topologyService = directory.get(TopologyService.class); |
| tunnelService = directory.get(TunnelService.class); |
| pceService = directory.get(PceService.class); |
| deviceService = directory.get(DeviceService.class); |
| topologyService.addListener(topologyListener); |
| tunnelService.addListener(tunnelListener); |
| } |
| |
| @Override |
| protected Collection<RequestHandler> createRequestHandlers() { |
| return ImmutableSet.of( |
| new ClearHandler(), |
| new SetPathHandler(), |
| new UpdatePathQueryHandler(), |
| new UpdatePathHandler(), |
| new RemovePathQueryHandler(), |
| new RemovePathHandler(), |
| new UpdatePathInfoHandler(), |
| new ShowTunnelHandler()); |
| } |
| |
| @Override |
| public void destroy() { |
| topologyService.removeListener(topologyListener); |
| tunnelService.removeListener(tunnelListener); |
| super.destroy(); |
| } |
| |
| // Handler classes |
| /** |
| * Handles the 'clear' event received from the client. |
| */ |
| private final class ClearHandler extends RequestHandler { |
| |
| public ClearHandler() { |
| super(PCEWEB_CLEAR); |
| } |
| |
| @Override |
| public void process(long sid, ObjectNode payload) { |
| src = null; |
| dst = null; |
| sendMessage(TopoJson.highlightsMessage(new Highlights())); |
| } |
| } |
| |
| /** |
| * Handles the 'path calculation' event received from the client. |
| */ |
| private final class SetPathHandler extends RequestHandler { |
| |
| public SetPathHandler() { |
| super(PCEWEB_SET_PATH); |
| } |
| |
| @Override |
| public void process(long sid, ObjectNode payload) { |
| String srcId = string(payload, SRCID); |
| src = elementId(srcId); |
| String dstId = string(payload, DSTID); |
| dst = elementId(dstId); |
| if (src.equals(dst)) { |
| src = null; |
| } |
| |
| String bandWidth = string(payload, BANDWIDTH); |
| String bandWidthType = string(payload, BANDWIDTHTYPE); |
| String costType = string(payload, COSTTYPE); |
| String lspType = string(payload, LSPTYPE); |
| String tunnelName = string(payload, TUNNEL_NAME); |
| |
| if (tunnelName.equals(STRING_NULL)) { |
| log.error("tunnel name should not be empty"); |
| return; |
| } |
| |
| if (pceService == null) { |
| log.error("PCE service is not active"); |
| return; |
| } |
| |
| if (lspType == null) { |
| log.error("PCE setup path is failed."); |
| } |
| |
| if ((src != null) && (dst != null)) { |
| findAndSendPaths(src, dst, bandWidth, bandWidthType, costType, lspType, tunnelName); |
| } |
| } |
| } |
| |
| /** |
| * Handles the 'update path query' event received from the client. |
| */ |
| private final class UpdatePathQueryHandler extends RequestHandler { |
| |
| public UpdatePathQueryHandler() { |
| super(PCEWEB_UPDATE_PATH_QUERY); |
| } |
| |
| @Override |
| public void process(long sid, ObjectNode payload) { |
| String srcId = string(payload, SRCID); |
| ElementId src = elementId(srcId); |
| String dstId = string(payload, DSTID); |
| ElementId dst = elementId(dstId); |
| Device srcDevice = deviceService.getDevice((DeviceId) src); |
| Device dstDevice = deviceService.getDevice((DeviceId) dst); |
| |
| TunnelEndPoint tunSrc = IpTunnelEndPoint.ipTunnelPoint(IpAddress |
| .valueOf(srcDevice.annotations().value("lsrId"))); |
| TunnelEndPoint tunDst = IpTunnelEndPoint.ipTunnelPoint(IpAddress |
| .valueOf(dstDevice.annotations().value("lsrId"))); |
| |
| Collection<Tunnel> tunnelSet = tunnelService.queryTunnel(tunSrc, tunDst); |
| ObjectNode result = objectNode(); |
| ArrayNode arrayNode = arrayNode(); |
| for (Tunnel tunnel : tunnelSet) { |
| if (tunnel.type() == MPLS) { |
| arrayNode.add(tunnel.tunnelId().toString()); |
| } |
| } |
| |
| result.putArray(BUFFER_ARRAY).addAll(arrayNode); |
| sendMessage(PCEWEB_SHOW_TUNNEL, sid, result); |
| } |
| } |
| |
| /** |
| * Handles the 'update path' event received from the client. |
| */ |
| private final class UpdatePathHandler extends RequestHandler { |
| |
| public UpdatePathHandler() { |
| super(PCEWEB_UPDATE_PATH); |
| } |
| |
| @Override |
| public void process(long sid, ObjectNode payload) { |
| String bandWidth = string(payload, BANDWIDTH); |
| String bandWidthType = string(payload, BANDWIDTHTYPE); |
| String costType = string(payload, COSTTYPE); |
| String tunnelId = string(payload, TUNNEL_ID); |
| |
| if (tunnelId == null) { |
| log.error("PCE update path is failed."); |
| } |
| |
| findAndSendPathsUpdate(bandWidth, bandWidthType, costType, tunnelId); |
| } |
| } |
| |
| /** |
| * Handles the 'update path' event received from the client. |
| */ |
| private final class UpdatePathInfoHandler extends RequestHandler { |
| |
| public UpdatePathInfoHandler() { |
| super(PCEWEB_TUNNEL_UPDATE_INFO); |
| } |
| |
| @Override |
| public void process(long sid, ObjectNode payload) { |
| String tunnelIdStr = string(payload, TUNNEL_ID); |
| |
| if (tunnelIdStr == null) { |
| log.error("PCE update path is failed."); |
| } |
| |
| if (tunnelIdStr.equals(STRING_NULL)) { |
| log.error("PCE update path is failed."); |
| return; |
| } |
| |
| if (pceService == null) { |
| log.error("PCE service is not active"); |
| return; |
| } |
| |
| TunnelId tunnelId = TunnelId.valueOf(tunnelIdStr); |
| Tunnel tunnel = tunnelService.queryTunnel(tunnelId); |
| ObjectNode result = objectNode(); |
| ArrayNode arrayNode = arrayNode(); |
| |
| arrayNode.add("Tunnel"); |
| arrayNode.add(tunnelIdStr); |
| arrayNode.add("BandWidth"); |
| arrayNode.add(tunnel.annotations().value("bandwidth")); |
| arrayNode.add("CostType"); |
| arrayNode.add(tunnel.annotations().value("costType")); |
| |
| result.putArray(BUFFER_ARRAY).addAll(arrayNode); |
| sendMessage(PCEWEB_TUNNEL_UPDATE_INFO_REPLY, sid, result); |
| } |
| } |
| |
| /** |
| * Handles the 'remove path query' event received from the client. |
| */ |
| private final class RemovePathQueryHandler extends RequestHandler { |
| |
| public RemovePathQueryHandler() { |
| super(PCEWEB_REMOVE_PATH_QUERY); |
| } |
| |
| @Override |
| public void process(long sid, ObjectNode payload) { |
| String srcId = string(payload, SRCID); |
| ElementId src = elementId(srcId); |
| String dstId = string(payload, DSTID); |
| ElementId dst = elementId(dstId); |
| |
| Device srcDevice = deviceService.getDevice((DeviceId) src); |
| Device dstDevice = deviceService.getDevice((DeviceId) dst); |
| |
| TunnelEndPoint tunSrc = IpTunnelEndPoint.ipTunnelPoint(IpAddress |
| .valueOf(srcDevice.annotations().value("lsrId"))); |
| TunnelEndPoint tunDst = IpTunnelEndPoint.ipTunnelPoint(IpAddress |
| .valueOf(dstDevice.annotations().value("lsrId"))); |
| |
| Collection<Tunnel> tunnelSet = tunnelService.queryTunnel(tunSrc, tunDst); |
| ObjectNode result = objectNode(); |
| ArrayNode arrayNode = arrayNode(); |
| |
| for (Tunnel tunnel : tunnelSet) { |
| if (tunnel.type() == MPLS) { |
| arrayNode.add(tunnel.tunnelId().toString()); |
| } |
| } |
| |
| result.putArray(BUFFER_ARRAY).addAll(arrayNode); |
| sendMessage(PCEWEB_SHOW_TUNNEL_REMOVE, sid, result); |
| } |
| } |
| |
| /** |
| * Handles the 'remove path' event received from the client. |
| */ |
| private final class RemovePathHandler extends RequestHandler { |
| |
| public RemovePathHandler() { |
| super(PCEWEB_REMOVE_PATH); |
| } |
| |
| @Override |
| public void process(long sid, ObjectNode payload) { |
| String tunnelId = string(payload, TUNNEL_ID); |
| |
| if (tunnelId == null) { |
| log.error("PCE update path is failed."); |
| } |
| |
| findAndSendPathsRemove(tunnelId); |
| } |
| } |
| |
| /** |
| * Handles the 'show the existed tunnels' event received from the client. |
| */ |
| private final class ShowTunnelHandler extends RequestHandler { |
| |
| public ShowTunnelHandler() { |
| super(PCEWEB_QUERY_TUNNELS); |
| } |
| |
| @Override |
| public void process(long sid, ObjectNode payload) { |
| findTunnelAndHighlights(); |
| } |
| } |
| |
| /** |
| * provides the element id. |
| */ |
| private ElementId elementId(String id) { |
| try { |
| return DeviceId.deviceId(id); |
| } catch (IllegalArgumentException e) { |
| return HostId.hostId(id); |
| } |
| } |
| |
| /** |
| * Handles the setup path and highlights the path. |
| * |
| * @param bandWidth |
| * @param bandWidthType is the kbps or mbps |
| * @param costType is igp or te |
| * @param lspType is WITH_SIGNALLING,WITHOUT_SIGNALLING_AND_WITHOUT_SR or SR_WITHOUT_SIGNALLING |
| * @param tunnelName tunnel id |
| */ |
| private void findAndSendPaths(ElementId src, ElementId dst, String bandWidth, String bandWidthType, |
| String costType, String lspType, String tunnelName) { |
| log.debug("src={}; dst={};", src, dst); |
| boolean path; |
| List<Constraint> listConstrnt; |
| |
| listConstrnt = addBandwidthCostTypeConstraints(bandWidth, bandWidthType, costType); |
| |
| //LSP type |
| LspType lspTypeVal = null; |
| switch (lspType) { |
| case LSP_TYPE_CR: |
| lspTypeVal = LspType.WITH_SIGNALLING; |
| break; |
| case LSP_TYPE_SRBE: |
| lspTypeVal = LspType.WITHOUT_SIGNALLING_AND_WITHOUT_SR; |
| break; |
| case LSP_TYPE_SRTE: |
| lspTypeVal = LspType.SR_WITHOUT_SIGNALLING; |
| break; |
| default: |
| break; |
| } |
| |
| path = pceService.setupPath((DeviceId) src, (DeviceId) dst, tunnelName, listConstrnt, lspTypeVal); |
| if (!path) { |
| log.error("setup path is failed"); |
| return; |
| } |
| |
| return; |
| } |
| |
| /** |
| * Handles the update path and highlights the path. |
| * |
| * @param bandWidth bandWidth |
| * @param bandWidthType is the kbps or mbps |
| * @param costType is igp or te |
| * @param tunnelName tunnel id |
| */ |
| private void findAndSendPathsUpdate(String bandWidth, String bandWidthType, String costType, String tunnelIdStr) { |
| if (tunnelIdStr != null) { |
| List<Constraint> listConstrnt; |
| |
| if (tunnelIdStr.equals(STRING_NULL)) { |
| log.error("update path is failed"); |
| return; |
| } |
| |
| if (pceService == null) { |
| log.error("PCE service is not active"); |
| return; |
| } |
| |
| listConstrnt = addBandwidthCostTypeConstraints(bandWidth, bandWidthType, costType); |
| TunnelId tunnelId = TunnelId.valueOf(tunnelIdStr); |
| boolean path = pceService.updatePath(tunnelId, listConstrnt); |
| |
| if (!path) { |
| log.error("update path is failed"); |
| return; |
| } |
| } |
| return; |
| } |
| |
| /** |
| * Handles the remove path and highlights the paths if existed. |
| * |
| * @param tunnelIdName tunnelId |
| */ |
| private void findAndSendPathsRemove(String tunnelIdStr) { |
| if (tunnelIdStr != null) { |
| if (pceService == null) { |
| log.error("PCE service is not active"); |
| return; |
| } |
| |
| TunnelId tunnelId = TunnelId.valueOf(tunnelIdStr); |
| boolean path = pceService.releasePath(tunnelId); |
| if (!path) { |
| log.error("remove path is failed"); |
| return; |
| } |
| } |
| return; |
| } |
| |
| private ImmutableSet.Builder<Link> buildPaths(ImmutableSet.Builder<Link> pathBuilder) { |
| paths.forEach(path -> path.links().forEach(pathBuilder::add)); |
| return pathBuilder; |
| } |
| |
| /** |
| * Handles the preparation of constraints list with given bandwidth and cost-type. |
| * |
| * @param bandWidth bandWidth |
| * @param bandWidthType is the kbps or mbps |
| * @param costType is igp or te |
| * @param listConstrnt list of constraints |
| * @return |
| */ |
| private List<Constraint> addBandwidthCostTypeConstraints(String bandWidth, |
| String bandWidthType, |
| String costType) { |
| List<Constraint> listConstrnt = new LinkedList<>(); |
| //bandwidth |
| double bwValue = 0.0; |
| if (!bandWidth.equals(STRING_NULL)) { |
| bwValue = Double.parseDouble(bandWidth); |
| } |
| if (bandWidthType.equals(BANDWIDTH_TYPE_KBPS)) { |
| bwValue = bwValue * BANDWIDTH_KBPS; |
| } else if (bandWidthType.equals(BANDWIDTH_TYPE_MBPS)) { |
| bwValue = bwValue * BANDWIDTH_MBPS; |
| } |
| |
| //Cost type |
| CostConstraint.Type costTypeVal = null; |
| switch (costType) { |
| case COST_TYPE_IGP: |
| costTypeVal = CostConstraint.Type.COST; |
| break; |
| case COST_TYPE_TE: |
| costTypeVal = CostConstraint.Type.TE_COST; |
| break; |
| default: |
| break; |
| } |
| |
| if (bwValue != 0.0) { |
| listConstrnt.add(BandwidthConstraint.of(bwValue, DataRateUnit.valueOf(BANDWIDTH_BPS))); |
| } |
| |
| if (costTypeVal != null) { |
| listConstrnt.add(CostConstraint.of(costTypeVal)); |
| } |
| |
| return listConstrnt; |
| } |
| |
| /** |
| * Handles the highlights of selected path. |
| */ |
| private void hilightAndSendPaths(Highlights highlights) { |
| LinkHighlight lh; |
| int linkclr = 0; |
| for (Path path : paths) { |
| for (Link link : path.links()) { |
| lh = new LinkHighlight(TopoUtils.compactLinkString(link), PRIMARY_HIGHLIGHT) |
| .addMod(new Mod(linkColor[linkclr])); |
| highlights.add(lh); |
| } |
| linkclr = linkclr + 1; |
| if (linkclr == LINK_COLOR_MAX) { |
| linkclr = 0; |
| } |
| } |
| |
| sendMessage(TopoJson.highlightsMessage(highlights)); |
| } |
| |
| /** |
| * Handles the addition of badge and highlights. |
| * |
| * @param highlights highlights |
| * @param elemId device to be add badge |
| * @param src device to be add badge |
| * @return |
| */ |
| private Highlights addBadge(Highlights highlights, |
| String elemId, String src) { |
| highlights = addDeviceBadge(highlights, elemId, src); |
| return highlights; |
| } |
| |
| /** |
| * Handles the badge add and highlights. |
| * |
| * @param h highlights |
| * @param elemId device to be add badge |
| * @param type device badge value |
| * @return highlights |
| */ |
| private Highlights addDeviceBadge(Highlights h, String elemId, String type) { |
| DeviceHighlight dh = new DeviceHighlight(elemId); |
| dh.setBadge(createBadge(type)); |
| h.add(dh); |
| return h; |
| } |
| |
| /** |
| * Handles the node badge add and highlights. |
| * |
| * @param type device badge value |
| * @return badge of given node |
| */ |
| private NodeBadge createBadge(String type) { |
| return NodeBadge.text(type); |
| } |
| |
| /** |
| * Handles the event of topology listeners. |
| */ |
| private class InternalTopologyListener implements TopologyListener { |
| @Override |
| public void event(TopologyEvent event) { |
| findTunnelAndHighlights(); |
| } |
| } |
| |
| /** |
| * Handles the event of tunnel listeners. |
| */ |
| private class InnerPceWebTunnelListener implements TunnelListener { |
| @Override |
| public void event(TunnelEvent event) { |
| Tunnel tunnel = event.subject(); |
| if (tunnel.type() == MPLS) { |
| findTunnelAndHighlights(); |
| } |
| } |
| } |
| |
| /** |
| * Handles the event of topology listeners. |
| */ |
| private void findTunnelAndHighlights() { |
| Collection<Tunnel> tunnelSet = null; |
| Highlights highlights = new Highlights(); |
| paths.removeAll(paths); |
| tunnelSet = tunnelService.queryTunnel(MPLS); |
| if (tunnelSet.size() == 0) { |
| log.warn("Tunnel does not exist"); |
| sendMessage(TopoJson.highlightsMessage(highlights)); |
| return; |
| } |
| |
| for (Tunnel tunnel : tunnelSet) { |
| if (tunnel.path() == null) { |
| log.error("path does not exist"); |
| sendMessage(TopoJson.highlightsMessage(highlights)); |
| return; |
| } |
| Link firstLink = tunnel.path().links().get(0); |
| if (firstLink != null) { |
| if (firstLink.src() != null) { |
| highlights = addBadge(highlights, firstLink.src().deviceId().toString(), SRC); |
| } |
| } |
| Link lastLink = tunnel.path().links().get(tunnel.path().links().size() - 1); |
| if (lastLink != null) { |
| if (lastLink.dst() != null) { |
| highlights = addBadge(highlights, lastLink.dst().deviceId().toString(), DST); |
| } |
| } |
| paths.add(tunnel.path()); |
| } |
| |
| ImmutableSet.Builder<Link> builder = ImmutableSet.builder(); |
| allPathLinks = buildPaths(builder).build(); |
| hilightAndSendPaths(highlights); |
| } |
| } |