blob: a8deff311cfd550ce2c631a189aa2b969a9e8b08 [file] [log] [blame]
Simon Hunta17fa672015-08-19 18:42:22 -07001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2015-present Open Networking Foundation
Simon Hunta17fa672015-08-19 18:42:22 -07003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
Simon Hunta17fa672015-08-19 18:42:22 -070015 */
16
Simon Hunt191c84a2015-08-21 08:24:48 -070017package org.onosproject.ui.topo;
Simon Hunta17fa672015-08-19 18:42:22 -070018
19import org.onosproject.net.Link;
20import org.onosproject.net.LinkKey;
21
22import java.text.DecimalFormat;
Simon Hunta17fa672015-08-19 18:42:22 -070023
Simon Hunt21281fd2017-03-30 22:28:28 -070024import static com.google.common.base.Preconditions.checkArgument;
Simon Hunta17fa672015-08-19 18:42:22 -070025import static org.onosproject.net.LinkKey.linkKey;
26
27/**
Simon Hunt4fc86852015-08-20 17:57:52 -070028 * Utility methods for helping out with formatting data for the Topology View
29 * in the web client.
Simon Hunta17fa672015-08-19 18:42:22 -070030 */
31public final class TopoUtils {
32
Simon Hunt21281fd2017-03-30 22:28:28 -070033 // explicit decision made to not 'javadoc' these constants
34 public static final double N_KILO = 1024;
35 public static final double N_MEGA = 1024 * N_KILO;
36 public static final double N_GIGA = 1024 * N_MEGA;
Simon Hunta17fa672015-08-19 18:42:22 -070037
Simon Hunta17fa672015-08-19 18:42:22 -070038 public static final String BITS_UNIT = "b";
Simon Hunta17fa672015-08-19 18:42:22 -070039 public static final String BYTES_UNIT = "B";
Simon Hunt21281fd2017-03-30 22:28:28 -070040 public static final String PACKETS_UNIT = "p";
Simon Hunta17fa672015-08-19 18:42:22 -070041
42 private static final DecimalFormat DF2 = new DecimalFormat("#,###.##");
43
44 private static final String COMPACT = "%s/%s-%s/%s";
45 private static final String EMPTY = "";
46 private static final String SPACE = " ";
Simon Hunta17fa672015-08-19 18:42:22 -070047 private static final String FLOW = "flow";
48 private static final String FLOWS = "flows";
49
50 // non-instantiable
51 private TopoUtils() { }
52
53 /**
54 * Returns a compact identity for the given link, in the form
55 * used to identify links in the Topology View on the client.
56 *
57 * @param link link
58 * @return compact link identity
59 */
60 public static String compactLinkString(Link link) {
61 return String.format(COMPACT, link.src().elementId(), link.src().port(),
Simon Hunt21281fd2017-03-30 22:28:28 -070062 link.dst().elementId(), link.dst().port());
Simon Hunta17fa672015-08-19 18:42:22 -070063 }
64
65 /**
66 * Produces a canonical link key, that is, one that will match both a link
67 * and its inverse.
68 *
69 * @param link the link
70 * @return canonical key
71 */
72 public static LinkKey canonicalLinkKey(Link link) {
73 String sn = link.src().elementId().toString();
74 String dn = link.dst().elementId().toString();
75 return sn.compareTo(dn) < 0 ?
76 linkKey(link.src(), link.dst()) : linkKey(link.dst(), link.src());
77 }
78
79 /**
Simon Hunt21281fd2017-03-30 22:28:28 -070080 * Returns a value representing a count of bytes.
Simon Hunta17fa672015-08-19 18:42:22 -070081 *
82 * @param bytes number of bytes
Simon Hunt21281fd2017-03-30 22:28:28 -070083 * @return value representing bytes
Simon Hunta17fa672015-08-19 18:42:22 -070084 */
Simon Hunt21281fd2017-03-30 22:28:28 -070085 public static ValueLabel formatBytes(long bytes) {
86 return new ValueLabel(bytes, BYTES_UNIT);
Simon Hunta17fa672015-08-19 18:42:22 -070087 }
88
89 /**
Simon Hunt21281fd2017-03-30 22:28:28 -070090 * Returns a value representing a count of packets per second.
91 *
92 * @param packets number of packets (per second)
93 * @return value representing packets per second
94 */
95 public static ValueLabel formatPacketRate(long packets) {
96 return new ValueLabel(packets, PACKETS_UNIT).perSec();
97 }
98
99
100 /**
101 * Returns a value representing a count of bits per second,
Andrea Campanellad7c93362017-09-13 15:53:04 +0200102 * (clipped to a maximum of 100 Gbps).
Simon Hunt21281fd2017-03-30 22:28:28 -0700103 * Note that the input is bytes per second.
Simon Hunta17fa672015-08-19 18:42:22 -0700104 *
105 * @param bytes bytes per second
Simon Hunt21281fd2017-03-30 22:28:28 -0700106 * @return value representing bits per second
Simon Hunta17fa672015-08-19 18:42:22 -0700107 */
Simon Hunt21281fd2017-03-30 22:28:28 -0700108 public static ValueLabel formatClippedBitRate(long bytes) {
Andrea Campanellad7c93362017-09-13 15:53:04 +0200109 return new ValueLabel(bytes * 8, BITS_UNIT).perSec().clipG(100.0);
Simon Hunta17fa672015-08-19 18:42:22 -0700110 }
111
112 /**
113 * Returns human readable flow count, to be displayed as a label.
114 *
115 * @param flows number of flows
116 * @return formatted flow count
117 */
118 public static String formatFlows(long flows) {
119 if (flows < 1) {
120 return EMPTY;
121 }
122 return String.valueOf(flows) + SPACE + (flows > 1 ? FLOWS : FLOW);
123 }
Simon Hunt21281fd2017-03-30 22:28:28 -0700124
125
126 /**
127 * Enumeration of magnitudes.
128 */
129 public enum Magnitude {
130 ONE("", 1),
131 KILO("K", N_KILO),
132 MEGA("M", N_MEGA),
133 GIGA("G", N_GIGA);
134
135 private final String label;
136 private final double mult;
137
138 Magnitude(String label, double mult) {
139 this.label = label;
140 this.mult = mult;
141 }
142
143 @Override
144 public String toString() {
145 return label;
146 }
147
148 private double mult() {
149 return mult;
150 }
151 }
152
153
154 /**
155 * Encapsulates a value to be used as a label.
156 */
157 public static class ValueLabel {
158 private final long value;
159 private final String unit;
160
161 private double divDown;
162 private Magnitude mag;
163
164 private boolean perSec = false;
165 private boolean clipped = false;
166
167 /**
168 * Creates a value label with the given base value and unit. For
169 * example:
170 * <pre>
171 * ValueLabel bits = new ValueLabel(2_050, "b");
172 * ValueLabel bytesPs = new ValueLabel(3_000_000, "B").perSec();
173 * </pre>
174 * Generating labels:
175 * <pre>
176 * bits.toString() ... "2.00 Kb"
177 * bytesPs.toString() ... "2.86 MBps"
178 * </pre>
179 *
180 * @param value the base value
181 * @param unit the value unit
182 */
183 public ValueLabel(long value, String unit) {
184 this.value = value;
185 this.unit = unit;
186 computeAdjusted();
187 }
188
189 private void computeAdjusted() {
190 if (value >= N_GIGA) {
191 divDown = value / N_GIGA;
192 mag = Magnitude.GIGA;
193 } else if (value >= N_MEGA) {
194 divDown = value / N_MEGA;
195 mag = Magnitude.MEGA;
196 } else if (value >= N_KILO) {
197 divDown = value / N_KILO;
198 mag = Magnitude.KILO;
199 } else {
200 divDown = value;
201 mag = Magnitude.ONE;
202 }
203 }
204
205 /**
206 * Mark this value to be expressed as a rate. That is, "ps" (per sec)
207 * will be appended in the string representation.
208 *
209 * @return self, for chaining
210 */
211 public ValueLabel perSec() {
212 perSec = true;
213 return this;
214 }
215
216 /**
217 * Clips the (adjusted) value to the given threshold expressed in
218 * Giga units. That is, if the adjusted value exceeds the threshold,
219 * it will be set to the threshold value and the clipped flag
220 * will be set. For example,
221 * <pre>
222 * ValueLabel tooMuch = new ValueLabel(12_000_000_000, "b")
223 * .perSec().clipG(10.0);
224 *
225 * tooMuch.toString() ... "10.00 Gbps"
226 * tooMuch.clipped() ... true
227 * </pre>
228 *
229 * @param threshold the clip threshold (Giga)
230 * @return self, for chaining
231 */
232 public ValueLabel clipG(double threshold) {
233 return clip(threshold, Magnitude.GIGA);
234 }
235
236 private ValueLabel clip(double threshold, Magnitude m) {
237 checkArgument(threshold >= 1.0, "threshold must be 1.0 or more");
238 double clipAt = threshold * m.mult();
239 if (value > clipAt) {
240 divDown = threshold;
241 mag = m;
242 clipped = true;
243 }
244 return this;
245 }
246
247 /**
248 * Returns true if this value was clipped to a maximum threshold.
249 *
250 * @return true if value was clipped
251 */
252 public boolean clipped() {
253 return clipped;
254 }
255
256 /**
257 * Returns the magnitude value.
258 *
259 * @return the magnitude
260 */
261 public Magnitude magnitude() {
262 return mag;
263 }
264
265 @Override
266 public String toString() {
267 return DF2.format(divDown) + SPACE + mag + unit + (perSec ? "ps" : "");
268 }
269 }
Simon Hunta17fa672015-08-19 18:42:22 -0700270}