/* * 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.JsonNode; import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Lists; import org.onlab.osgi.ServiceDirectory; import org.onlab.packet.IpAddress; import org.onlab.util.DataRateUnit; 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 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.device.DeviceService; import org.onosproject.net.intent.Constraint; import org.onosproject.net.topology.TopologyService; import org.onosproject.pce.pceservice.LspType; import org.onosproject.pce.pceservice.api.PceService; import org.onosproject.pce.pceservice.constraint.CostConstraint; import org.onosproject.pce.pceservice.constraint.PceBandwidthConstraint; 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.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Arrays; import java.util.Collection; import java.util.LinkedList; import java.util.List; import java.util.Set; import static org.onosproject.incubator.net.tunnel.Tunnel.State.ACTIVE; import static org.onosproject.incubator.net.tunnel.Tunnel.Type.MPLS; import static org.onosproject.ui.topo.LinkHighlight.Flavor.PRIMARY_HIGHLIGHT; import static org.onosproject.ui.topo.TopoJson.highlightsMessage; /** * 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 PCEWEB_TUNNEL_QUERY_INFO = "pceTopovShowTunnelsQuery"; private static final String PCEWEB_TUNNEL_QUERY_INFO_SHOW = "pceTopovshowTunnelHighlightMsg"; private static final String DST = "DST"; private static final String SRC = "SRC"; private static final String BANDWIDTH = "bw"; private static final String LOADBALANCING = "lb"; 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 = "mbps"; 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 = 1_100; private static final double BANDWIDTH_KBPS = 1_000; private static final double BANDWIDTH_MBPS = 1_000_000; private static final String[] LINK_COLOR = { "pCol1", "pCol2", "pCol3", "pCol4", "pCol5", "pCol6", "pCol7", "pCol8", "pCol9", "pCol10", "pCol11", "pCol12", "pCol13", "pCol14", "pCol15" }; private static final int LINK_COLOR_MAX = LINK_COLOR.length; 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 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); tunnelService = directory.get(TunnelService.class); pceService = directory.get(PceService.class); deviceService = directory.get(DeviceService.class); 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(), new ShowTunnelHighlight()); } @Override public void destroy() { 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(ObjectNode payload) { src = null; dst = null; sendMessage(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(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 loadBalancing = string(payload, LOADBALANCING); String lspType = string(payload, LSPTYPE); String tunnelName = string(payload, TUNNEL_NAME); if (tunnelName == null || tunnelName.equals(STRING_NULL)) { log.error("tunnel name should not be empty"); return; } //Validating tunnel name, duplicated tunnel names not allowed Collection<Tunnel> existingTunnels = tunnelService.queryTunnel(Tunnel.Type.MPLS); if (existingTunnels != null) { for (Tunnel t : existingTunnels) { if (t.tunnelName().toString().equals(tunnelName)) { log.error("Path creation failed, Tunnel name already exists"); return; } } } if (pceService == null) { log.error("PCE service is not active"); return; } if (lspType == null || lspType.equals(STRING_NULL)) { log.error("PCE setup path is failed as LSP type is mandatory"); } if ((src != null) && (dst != null)) { findAndSendPaths(src, dst, bandWidth, bandWidthType, costType, lspType, tunnelName, loadBalancing); } } } /** * 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(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) { if (tunnel.state().equals(ACTIVE)) { arrayNode.add(tunnel.tunnelId().toString()); arrayNode.add(tunnel.tunnelName().toString()); } } } result.putArray(BUFFER_ARRAY).addAll(arrayNode); sendMessage(PCEWEB_SHOW_TUNNEL, 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(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(ObjectNode payload) { String tunnelIdStr = string(payload, TUNNEL_ID); if (tunnelIdStr == null) { log.error("PCE update path is failed."); return; } 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, 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(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) { if (tunnel.state().equals(ACTIVE)) { if (tunnel.annotations().value("loadBalancingPathName") != null) { boolean present = false; if (!arrayNode.isNull()) { for (JsonNode node : arrayNode) { if (node.asText().equals(tunnel.annotations().value("loadBalancingPathName"))) { present = true; break; } } if (!present) { arrayNode.add(""); arrayNode.add(tunnel.annotations().value("loadBalancingPathName")); } } } else { arrayNode.add(tunnel.tunnelId().toString()); arrayNode.add(tunnel.tunnelName().toString()); } } } } result.putArray(BUFFER_ARRAY).addAll(arrayNode); sendMessage(PCEWEB_SHOW_TUNNEL_REMOVE, 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(ObjectNode payload) { String tunnelId = string(payload, TUNNEL_ID); String pathName = string(payload, "tunnelname"); //TODO: if tunnel id null it is load banlanced path if (tunnelId.equals("") && pathName != null) { findAndSendPathsRemove(STRING_NULL, pathName); /* List<TunnelId> tunnelIds = pceService.getLoadBalancedPath(pathName); for (TunnelId id : tunnelIds) { Tunnel tunnel = tunnelService.queryTunnel(id); if (tunnel != null) { } }*/ } else { findAndSendPathsRemove(tunnelId, null); } } } /** * 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(ObjectNode payload) { ObjectNode result = objectNode(); ArrayNode arrayNode = arrayNode(); Collection<Tunnel> tunnelSet = null; tunnelSet = tunnelService.queryTunnel(MPLS); for (Tunnel tunnel : tunnelSet) { if (tunnel.state().equals(ACTIVE)) { //TODO: if it is load balancing need to check whether to send tunnel ID as null or some negative //TODO;value if (tunnel.annotations().value("loadBalancingPathName") != null) { boolean present = false; if (!arrayNode.isNull()) { for (JsonNode node : arrayNode) { if (node.asText().equals(tunnel.annotations().value("loadBalancingPathName"))) { present = true; break; } } if (!present) { arrayNode.add(""); arrayNode.add(tunnel.annotations().value("loadBalancingPathName")); } } } else { arrayNode.add(tunnel.tunnelId().toString()); arrayNode.add(tunnel.tunnelName().toString()); } } } result.putArray(BUFFER_ARRAY).addAll(arrayNode); sendMessage(PCEWEB_TUNNEL_QUERY_INFO, result); } } /** * Handles the 'show the existed tunnels' event received from the client. */ private final class ShowTunnelHighlight extends RequestHandler { public ShowTunnelHighlight() { super(PCEWEB_TUNNEL_QUERY_INFO_SHOW); } @Override public void process(ObjectNode payload) { String tunnelIdStr = string(payload, TUNNEL_ID); String pathName = string(payload, "tunnelname"); if (tunnelIdStr.equals("")) { List<Tunnel> tunnels = Lists.newLinkedList(); List<TunnelId> tunnelIds = pceService.queryLoadBalancingPath(pathName); for (TunnelId id : tunnelIds) { Tunnel t = tunnelService.queryTunnel(id); tunnels.add(t); } if (!tunnels.isEmpty()) { highlightsForTunnel(tunnels); } } else { TunnelId tunnelId = TunnelId.valueOf(tunnelIdStr); Tunnel t = tunnelService.queryTunnel(tunnelId); if (t != null) { highlightsForTunnel(t); } } } } /** * 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 src ID of source * @param dst ID of destination * @param bandWidth 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, String loadBalancing) { 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; } boolean loadBalancingOpt = Boolean.parseBoolean(loadBalancing); //TODO: need to get explicit paths [temporarily using null as the value] if (loadBalancingOpt) { path = pceService.setupPath((DeviceId) src, (DeviceId) dst, tunnelName, listConstrnt, lspTypeVal, loadBalancingOpt); } else { path = pceService.setupPath((DeviceId) src, (DeviceId) dst, tunnelName, listConstrnt, lspTypeVal, null); } if (!path) { log.error("setup path is failed"); } } /** * 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 tunnelIdStr 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"); } } } /** * Handles the remove path and highlights the paths if existed. * * @param tunnelIdStr tunnelId */ private void findAndSendPathsRemove(String tunnelIdStr, String pathName) { if (pceService == null) { log.error("PCE service is not active"); return; } boolean path; if (tunnelIdStr.equals(STRING_NULL) && !pathName.equals(STRING_NULL)) { path = pceService.releasePath(pathName); } else { TunnelId tunnelId = TunnelId.valueOf(tunnelIdStr); path = pceService.releasePath(tunnelId); } if (!path) { log.error("remove path is failed"); } } 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 * @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(PceBandwidthConstraint.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(LINK_COLOR[linkclr])); highlights.add(lh); } linkclr = linkclr + 1; if (linkclr == LINK_COLOR_MAX) { linkclr = 0; } } sendMessage(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 tunnel listeners. */ private class InnerPceWebTunnelListener implements TunnelListener { @Override public void event(TunnelEvent event) { Tunnel tunnel = event.subject(); if (tunnel.type() == MPLS) { highlightsForTunnel(tunnel); } } } /** * 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(highlightsMessage(highlights)); return; } for (Tunnel tunnel : tunnelSet) { if (tunnel.path() == null) { log.error("path does not exist"); sendMessage(highlightsMessage(highlights)); return; } if (!tunnel.state().equals(ACTIVE)) { log.debug("Tunnel state is not active"); sendMessage(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); } private void highlightsForTunnel(Tunnel... tunnels) { highlightsForTunnel(Arrays.asList(tunnels)); } /** * Handles the event of topology listeners. */ private void highlightsForTunnel(List<Tunnel> tunnels) { Highlights highlights = new Highlights(); paths.removeAll(paths); if (tunnels.isEmpty()) { log.error("path does not exist"); sendMessage(TopoJson.highlightsMessage(highlights)); return; } for (Tunnel tunnel : tunnels) { if (tunnel.path() == null) { log.error("path does not exist"); sendMessage(highlightsMessage(highlights)); return; } if (!tunnel.state().equals(ACTIVE)) { log.debug("Tunnel state is not active"); sendMessage(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); } }