/* * 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.ui.impl.topo.util; import org.onosproject.net.Link; import org.onosproject.net.LinkKey; import org.onosproject.net.statistic.Load; import org.onosproject.ui.model.topo.UiLinkId; import org.onosproject.ui.topo.BiLink; import org.onosproject.ui.topo.LinkHighlight; import org.onosproject.ui.topo.LinkHighlight.Flavor; import org.onosproject.ui.topo.Mod; import org.onosproject.ui.topo.TopoUtils.Magnitude; import org.onosproject.ui.topo.TopoUtils.ValueLabel; import java.util.HashSet; import java.util.Set; import static com.google.common.base.MoreObjects.toStringHelper; import static org.onosproject.ui.topo.LinkHighlight.Flavor.NO_HIGHLIGHT; import static org.onosproject.ui.topo.LinkHighlight.Flavor.PRIMARY_HIGHLIGHT; import static org.onosproject.ui.topo.LinkHighlight.Flavor.SECONDARY_HIGHLIGHT; import static org.onosproject.ui.topo.TopoUtils.formatBytes; import static org.onosproject.ui.topo.TopoUtils.formatClippedBitRate; import static org.onosproject.ui.topo.TopoUtils.formatFlows; import static org.onosproject.ui.topo.TopoUtils.formatPacketRate; /** * Representation of a link and its inverse, and associated traffic data. * This class understands how to generate the appropriate * {@link LinkHighlight}s for showing traffic data on the topology view. */ public class TrafficLink extends BiLink { private static final Mod PORT_TRAFFIC_GREEN = new Mod("port-traffic-green"); private static final Mod PORT_TRAFFIC_YELLOW = new Mod("port-traffic-yellow"); private static final Mod PORT_TRAFFIC_ORANGE = new Mod("port-traffic-orange"); private static final Mod PORT_TRAFFIC_RED = new Mod("port-traffic-red"); private static final String EMPTY = ""; private long bytes = 0; private long rate = 0; private long flows = 0; private Flavor taggedFlavor = NO_HIGHLIGHT; private boolean hasTraffic = false; private boolean isOptical = false; private boolean antMarch = false; private Set<Mod> mods = new HashSet<>(); /** * Constructs a traffic link for the given key and initial link. * * @param key canonical key for this traffic link * @param link first link */ public TrafficLink(LinkKey key, Link link) { super(key, link); } /** * Returns an "empty" traffic link (one with no underlying links or stats) * with the given identifier. This is useful when we want to aggregate * stats from other links into a single entity (such as a region-region * link reporting the stats for the links that compose it). * * @param id the link identifier */ public TrafficLink(UiLinkId id) { super(id); } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } TrafficLink that = (TrafficLink) o; return bytes == that.bytes && rate == that.rate && flows == that.flows && hasTraffic == that.hasTraffic && isOptical == that.isOptical && antMarch == that.antMarch && taggedFlavor == that.taggedFlavor && mods.equals(that.mods); } @Override public int hashCode() { int result = (int) (bytes ^ (bytes >>> 32)); result = 31 * result + (int) (rate ^ (rate >>> 32)); result = 31 * result + (int) (flows ^ (flows >>> 32)); result = 31 * result + taggedFlavor.hashCode(); result = 31 * result + (hasTraffic ? 1 : 0); result = 31 * result + (isOptical ? 1 : 0); result = 31 * result + (antMarch ? 1 : 0); result = 31 * result + mods.hashCode(); return result; } @Override public String toString() { return toStringHelper(this) .add("linkId", linkId()) .add("bytes", bytes) .add("rate", rate) .add("flows", flows) .toString(); } /** * Returns the count of bytes. * * @return the byte count */ public long bytes() { return bytes; } /** * Returns the rate. * * @return the rate */ public long rate() { return rate; } /** * Returns the flows. * * @return flow count */ public long flows() { return flows; } /** * Sets the optical flag to the given value. * * @param b true if an optical link * @return self, for chaining */ public TrafficLink optical(boolean b) { isOptical = b; return this; } /** * Sets the ant march flag to the given value. * * @param b true if marching ants required * @return self, for chaining */ public TrafficLink antMarch(boolean b) { antMarch = b; return this; } /** * Tags this traffic link with the flavor to be used in visual rendering. * * @param flavor the flavor to tag * @return self, for chaining */ public TrafficLink tagFlavor(Flavor flavor) { taggedFlavor = flavor; return this; } /** * Tags this traffic link with the mods to be used in visual rendering. * * @param mods the mods to tag on this link * @return self, for chaining */ public TrafficLink tagMods(Set<Mod> mods) { if (mods != null) { this.mods.addAll(mods); } return this; } /** * Adds load statistics, marks the traffic link as having traffic. * * @param load load to add */ public void addLoad(Load load) { addLoad(load, 0); } /** * Adds load statistics, marks the traffic link as having traffic, if the * load {@link Load#rate rate} is greater than the given threshold * (expressed in bytes per second). * * @param load load to add * @param threshold threshold to register traffic */ public void addLoad(Load load, double threshold) { if (load != null) { this.hasTraffic = hasTraffic || load.rate() > threshold; this.bytes += load.latest(); this.rate += load.rate(); } } /** * Adds the given count of flows to this traffic link. * * @param count count of flows */ public void addFlows(int count) { this.flows += count; } /** * Merges the load recorded on the given traffic link into this one. * * @param other the other traffic link */ public void mergeStats(TrafficLink other) { this.bytes += other.bytes; this.rate += other.rate; this.flows += other.flows; } @Override public LinkHighlight highlight(Enum<?> type) { StatsType statsType = (StatsType) type; switch (statsType) { case FLOW_COUNT: return highlightForFlowCount(); case FLOW_STATS: case PORT_STATS: case PORT_PACKET_STATS: return highlightForStats(statsType); case TAGGED: return highlightForTagging(); default: throw new IllegalStateException("unexpected case: " + statsType); } } private LinkHighlight highlightForStats(StatsType type) { ValueLabel vl = null; Mod m = null; // based on the type of stats, need to determine the label and "color"... switch (type) { case FLOW_STATS: vl = formatBytes(bytes); // default to "secondary highlighting" of link break; case PORT_STATS: vl = formatClippedBitRate(rate); // set color based on bits per second... if (vl.magnitude() == Magnitude.ONE || vl.magnitude() == Magnitude.KILO) { m = PORT_TRAFFIC_GREEN; } else if (vl.magnitude() == Magnitude.MEGA) { m = PORT_TRAFFIC_YELLOW; } else if (vl.magnitude() == Magnitude.GIGA) { m = vl.clipped() ? PORT_TRAFFIC_RED : PORT_TRAFFIC_ORANGE; } break; case PORT_PACKET_STATS: vl = formatPacketRate(rate); // FIXME: Provisional color threshold parameters for packets // set color based on bits per second... if (rate < 10) { m = PORT_TRAFFIC_GREEN; } else if (rate < 1000) { m = PORT_TRAFFIC_YELLOW; } else if (rate < 100000) { m = PORT_TRAFFIC_ORANGE; } else { m = PORT_TRAFFIC_RED; } break; default: break; } LinkHighlight hlite = new LinkHighlight(linkId(), SECONDARY_HIGHLIGHT); if (vl != null) { hlite.setLabel(vl.toString()); } if (m != null) { hlite.addMod(m); } return addCustomMods(hlite); } private LinkHighlight highlightForFlowCount() { Flavor flavor = flows > 0 ? PRIMARY_HIGHLIGHT : SECONDARY_HIGHLIGHT; LinkHighlight hlite = new LinkHighlight(linkId(), flavor) .setLabel(formatFlows(flows)); return addCustomMods(hlite); } private LinkHighlight highlightForTagging() { LinkHighlight hlite = new LinkHighlight(linkId(), taggedFlavor) .setLabel(hasTraffic ? formatBytes(bytes).toString() : EMPTY); if (isOptical) { hlite.addMod(LinkHighlight.MOD_OPTICAL); } if (antMarch) { hlite.addMod(LinkHighlight.MOD_ANIMATED); } return addCustomMods(hlite); } private LinkHighlight addCustomMods(LinkHighlight hlite) { if (!mods.isEmpty()) { mods.forEach(hlite::addMod); } return hlite; } /** * Returns true if this link has been deemed to have enough traffic * to register on the topology view in the web UI. * * @return true if this link has displayable traffic */ public boolean hasTraffic() { return hasTraffic; } /** * Designates type of traffic statistics to report on a highlighted link. */ public enum StatsType { /** * Number of flows. */ FLOW_COUNT, /** * Number of bytes. */ FLOW_STATS, /** * Number of bits per second. */ PORT_STATS, /** * Number of packets per second. */ PORT_PACKET_STATS, /** * Custom tagged information. */ TAGGED } }