blob: a8deff311cfd550ce2c631a189aa2b969a9e8b08 [file] [log] [blame]
/*
* Copyright 2015-present Open Networking Foundation
*
* 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.topo;
import org.onosproject.net.Link;
import org.onosproject.net.LinkKey;
import java.text.DecimalFormat;
import static com.google.common.base.Preconditions.checkArgument;
import static org.onosproject.net.LinkKey.linkKey;
/**
* Utility methods for helping out with formatting data for the Topology View
* in the web client.
*/
public final class TopoUtils {
// explicit decision made to not 'javadoc' these constants
public static final double N_KILO = 1024;
public static final double N_MEGA = 1024 * N_KILO;
public static final double N_GIGA = 1024 * N_MEGA;
public static final String BITS_UNIT = "b";
public static final String BYTES_UNIT = "B";
public static final String PACKETS_UNIT = "p";
private static final DecimalFormat DF2 = new DecimalFormat("#,###.##");
private static final String COMPACT = "%s/%s-%s/%s";
private static final String EMPTY = "";
private static final String SPACE = " ";
private static final String FLOW = "flow";
private static final String FLOWS = "flows";
// non-instantiable
private TopoUtils() { }
/**
* Returns a compact identity for the given link, in the form
* used to identify links in the Topology View on the client.
*
* @param link link
* @return compact link identity
*/
public static String compactLinkString(Link link) {
return String.format(COMPACT, link.src().elementId(), link.src().port(),
link.dst().elementId(), link.dst().port());
}
/**
* Produces a canonical link key, that is, one that will match both a link
* and its inverse.
*
* @param link the link
* @return canonical key
*/
public static LinkKey canonicalLinkKey(Link link) {
String sn = link.src().elementId().toString();
String dn = link.dst().elementId().toString();
return sn.compareTo(dn) < 0 ?
linkKey(link.src(), link.dst()) : linkKey(link.dst(), link.src());
}
/**
* Returns a value representing a count of bytes.
*
* @param bytes number of bytes
* @return value representing bytes
*/
public static ValueLabel formatBytes(long bytes) {
return new ValueLabel(bytes, BYTES_UNIT);
}
/**
* Returns a value representing a count of packets per second.
*
* @param packets number of packets (per second)
* @return value representing packets per second
*/
public static ValueLabel formatPacketRate(long packets) {
return new ValueLabel(packets, PACKETS_UNIT).perSec();
}
/**
* Returns a value representing a count of bits per second,
* (clipped to a maximum of 100 Gbps).
* Note that the input is bytes per second.
*
* @param bytes bytes per second
* @return value representing bits per second
*/
public static ValueLabel formatClippedBitRate(long bytes) {
return new ValueLabel(bytes * 8, BITS_UNIT).perSec().clipG(100.0);
}
/**
* Returns human readable flow count, to be displayed as a label.
*
* @param flows number of flows
* @return formatted flow count
*/
public static String formatFlows(long flows) {
if (flows < 1) {
return EMPTY;
}
return String.valueOf(flows) + SPACE + (flows > 1 ? FLOWS : FLOW);
}
/**
* Enumeration of magnitudes.
*/
public enum Magnitude {
ONE("", 1),
KILO("K", N_KILO),
MEGA("M", N_MEGA),
GIGA("G", N_GIGA);
private final String label;
private final double mult;
Magnitude(String label, double mult) {
this.label = label;
this.mult = mult;
}
@Override
public String toString() {
return label;
}
private double mult() {
return mult;
}
}
/**
* Encapsulates a value to be used as a label.
*/
public static class ValueLabel {
private final long value;
private final String unit;
private double divDown;
private Magnitude mag;
private boolean perSec = false;
private boolean clipped = false;
/**
* Creates a value label with the given base value and unit. For
* example:
* <pre>
* ValueLabel bits = new ValueLabel(2_050, "b");
* ValueLabel bytesPs = new ValueLabel(3_000_000, "B").perSec();
* </pre>
* Generating labels:
* <pre>
* bits.toString() ... "2.00 Kb"
* bytesPs.toString() ... "2.86 MBps"
* </pre>
*
* @param value the base value
* @param unit the value unit
*/
public ValueLabel(long value, String unit) {
this.value = value;
this.unit = unit;
computeAdjusted();
}
private void computeAdjusted() {
if (value >= N_GIGA) {
divDown = value / N_GIGA;
mag = Magnitude.GIGA;
} else if (value >= N_MEGA) {
divDown = value / N_MEGA;
mag = Magnitude.MEGA;
} else if (value >= N_KILO) {
divDown = value / N_KILO;
mag = Magnitude.KILO;
} else {
divDown = value;
mag = Magnitude.ONE;
}
}
/**
* Mark this value to be expressed as a rate. That is, "ps" (per sec)
* will be appended in the string representation.
*
* @return self, for chaining
*/
public ValueLabel perSec() {
perSec = true;
return this;
}
/**
* Clips the (adjusted) value to the given threshold expressed in
* Giga units. That is, if the adjusted value exceeds the threshold,
* it will be set to the threshold value and the clipped flag
* will be set. For example,
* <pre>
* ValueLabel tooMuch = new ValueLabel(12_000_000_000, "b")
* .perSec().clipG(10.0);
*
* tooMuch.toString() ... "10.00 Gbps"
* tooMuch.clipped() ... true
* </pre>
*
* @param threshold the clip threshold (Giga)
* @return self, for chaining
*/
public ValueLabel clipG(double threshold) {
return clip(threshold, Magnitude.GIGA);
}
private ValueLabel clip(double threshold, Magnitude m) {
checkArgument(threshold >= 1.0, "threshold must be 1.0 or more");
double clipAt = threshold * m.mult();
if (value > clipAt) {
divDown = threshold;
mag = m;
clipped = true;
}
return this;
}
/**
* Returns true if this value was clipped to a maximum threshold.
*
* @return true if value was clipped
*/
public boolean clipped() {
return clipped;
}
/**
* Returns the magnitude value.
*
* @return the magnitude
*/
public Magnitude magnitude() {
return mag;
}
@Override
public String toString() {
return DF2.format(divDown) + SPACE + mag + unit + (perSec ? "ps" : "");
}
}
}