blob: f2643c697834235ef6470bd805b2a040e4c60857 [file] [log] [blame]
Simon Hunt4fc86852015-08-20 17:57:52 -07001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2016-present Open Networking Laboratory
Simon Hunt4fc86852015-08-20 17:57:52 -07003 *
Simon Hunted804d52016-03-30 09:51:40 -07004 * 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
Simon Hunt4fc86852015-08-20 17:57:52 -07007 *
Simon Hunted804d52016-03-30 09:51:40 -07008 * http://www.apache.org/licenses/LICENSE-2.0
Simon Hunt4fc86852015-08-20 17:57:52 -07009 *
Simon Hunted804d52016-03-30 09:51:40 -070010 * 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 Hunt4fc86852015-08-20 17:57:52 -070015 */
16
Simon Hunted804d52016-03-30 09:51:40 -070017package org.onosproject.ui.impl.topo.util;
Simon Hunt4fc86852015-08-20 17:57:52 -070018
19import org.onosproject.net.Link;
20import org.onosproject.net.LinkKey;
21import org.onosproject.net.statistic.Load;
Simon Hunt191c84a2015-08-21 08:24:48 -070022import org.onosproject.ui.topo.BiLink;
Simon Hunt4fc86852015-08-20 17:57:52 -070023import org.onosproject.ui.topo.LinkHighlight;
24import org.onosproject.ui.topo.LinkHighlight.Flavor;
Andrea Campanellaaf934682017-03-05 11:06:40 +010025import org.onosproject.ui.topo.Mod;
Simon Hunt21281fd2017-03-30 22:28:28 -070026import org.onosproject.ui.topo.TopoUtils.Magnitude;
27import org.onosproject.ui.topo.TopoUtils.ValueLabel;
Simon Hunt4fc86852015-08-20 17:57:52 -070028
Andrea Campanellaaf934682017-03-05 11:06:40 +010029import java.util.HashSet;
30import java.util.Set;
31
Simon Hunt0e161092017-05-08 17:41:38 -070032import static com.google.common.base.MoreObjects.toStringHelper;
Simon Hunt21281fd2017-03-30 22:28:28 -070033import static org.onosproject.ui.topo.LinkHighlight.Flavor.NO_HIGHLIGHT;
34import static org.onosproject.ui.topo.LinkHighlight.Flavor.PRIMARY_HIGHLIGHT;
35import static org.onosproject.ui.topo.LinkHighlight.Flavor.SECONDARY_HIGHLIGHT;
36import static org.onosproject.ui.topo.TopoUtils.formatBytes;
37import static org.onosproject.ui.topo.TopoUtils.formatClippedBitRate;
38import static org.onosproject.ui.topo.TopoUtils.formatFlows;
39import static org.onosproject.ui.topo.TopoUtils.formatPacketRate;
Simon Hunt4fc86852015-08-20 17:57:52 -070040
41/**
42 * Representation of a link and its inverse, and associated traffic data.
43 * This class understands how to generate the appropriate
44 * {@link LinkHighlight}s for showing traffic data on the topology view.
45 */
46public class TrafficLink extends BiLink {
Simon Hunt21281fd2017-03-30 22:28:28 -070047 private static final Mod PORT_TRAFFIC_GREEN = new Mod("port-traffic-green");
48 private static final Mod PORT_TRAFFIC_YELLOW = new Mod("port-traffic-yellow");
49 private static final Mod PORT_TRAFFIC_ORANGE = new Mod("port-traffic-orange");
50 private static final Mod PORT_TRAFFIC_RED = new Mod("port-traffic-red");
Simon Hunt4fc86852015-08-20 17:57:52 -070051
52 private static final String EMPTY = "";
Simon Hunt4fc86852015-08-20 17:57:52 -070053
54 private long bytes = 0;
55 private long rate = 0;
56 private long flows = 0;
57 private Flavor taggedFlavor = NO_HIGHLIGHT;
58 private boolean hasTraffic = false;
59 private boolean isOptical = false;
60 private boolean antMarch = false;
Andrea Campanellaaf934682017-03-05 11:06:40 +010061 private Set<Mod> mods = new HashSet<>();
Simon Hunt4fc86852015-08-20 17:57:52 -070062
63 /**
64 * Constructs a traffic link for the given key and initial link.
65 *
66 * @param key canonical key for this traffic link
67 * @param link first link
68 */
69 public TrafficLink(LinkKey key, Link link) {
70 super(key, link);
71 }
72
73 /**
Simon Hunt0e161092017-05-08 17:41:38 -070074 * Copy constructor.
75 *
76 * @param copy the instance to copy
77 */
78 public TrafficLink(TrafficLink copy) {
79 super(copy.key(), copy.one());
80 setOther(copy.two());
81 bytes = copy.bytes;
82 rate = copy.rate;
83 flows = copy.flows;
84 taggedFlavor = copy.taggedFlavor;
85 hasTraffic = copy.hasTraffic;
86 isOptical = copy.isOptical;
87 antMarch = copy.antMarch;
88 mods.addAll(copy.mods);
89 }
90
91 @Override
92 public boolean equals(Object o) {
93 if (this == o) {
94 return true;
95 }
96 if (o == null || getClass() != o.getClass()) {
97 return false;
98 }
99
100 TrafficLink that = (TrafficLink) o;
101
102 return bytes == that.bytes && rate == that.rate &&
103 flows == that.flows && hasTraffic == that.hasTraffic &&
104 isOptical == that.isOptical && antMarch == that.antMarch &&
105 taggedFlavor == that.taggedFlavor && mods.equals(that.mods);
106 }
107
108 @Override
109 public int hashCode() {
110 int result = (int) (bytes ^ (bytes >>> 32));
111 result = 31 * result + (int) (rate ^ (rate >>> 32));
112 result = 31 * result + (int) (flows ^ (flows >>> 32));
113 result = 31 * result + taggedFlavor.hashCode();
114 result = 31 * result + (hasTraffic ? 1 : 0);
115 result = 31 * result + (isOptical ? 1 : 0);
116 result = 31 * result + (antMarch ? 1 : 0);
117 result = 31 * result + mods.hashCode();
118 return result;
119 }
120
121 @Override
122 public String toString() {
123 return toStringHelper(this)
124 .add("key", key())
125 .add("bytes", bytes)
126 .add("rate", rate)
127 .add("flows", flows)
128 .toString();
129 }
130
131 /**
132 * Returns the count of bytes.
133 *
134 * @return the byte count
135 */
136 public long bytes() {
137 return bytes;
138 }
139
140 /**
141 * Returns the rate.
142 *
143 * @return the rate
144 */
145 public long rate() {
146 return rate;
147 }
148
149 /**
150 * Returns the flows.
151 *
152 * @return flow count
153 */
154 public long flows() {
155 return flows;
156 }
157
158 /**
Simon Hunt4fc86852015-08-20 17:57:52 -0700159 * Sets the optical flag to the given value.
160 *
161 * @param b true if an optical link
162 * @return self, for chaining
163 */
164 public TrafficLink optical(boolean b) {
165 isOptical = b;
166 return this;
167 }
168
169 /**
170 * Sets the ant march flag to the given value.
171 *
172 * @param b true if marching ants required
173 * @return self, for chaining
174 */
175 public TrafficLink antMarch(boolean b) {
176 antMarch = b;
177 return this;
178 }
179
180 /**
181 * Tags this traffic link with the flavor to be used in visual rendering.
182 *
183 * @param flavor the flavor to tag
184 * @return self, for chaining
185 */
186 public TrafficLink tagFlavor(Flavor flavor) {
Simon Hunt21281fd2017-03-30 22:28:28 -0700187 taggedFlavor = flavor;
Simon Hunt4fc86852015-08-20 17:57:52 -0700188 return this;
189 }
190
191 /**
Andrea Campanellaaf934682017-03-05 11:06:40 +0100192 * Tags this traffic link with the mods to be used in visual rendering.
193 *
194 * @param mods the mods to tag on this link
195 * @return self, for chaining
196 */
197 public TrafficLink tagMods(Set<Mod> mods) {
198 if (mods != null) {
199 this.mods.addAll(mods);
200 }
201 return this;
202 }
203
204 /**
Simon Hunt4fc86852015-08-20 17:57:52 -0700205 * Adds load statistics, marks the traffic link as having traffic.
206 *
207 * @param load load to add
208 */
209 public void addLoad(Load load) {
210 addLoad(load, 0);
211 }
212
213 /**
214 * Adds load statistics, marks the traffic link as having traffic, if the
215 * load {@link Load#rate rate} is greater than the given threshold
216 * (expressed in bytes per second).
217 *
Andrea Campanellaaf934682017-03-05 11:06:40 +0100218 * @param load load to add
Simon Hunt4fc86852015-08-20 17:57:52 -0700219 * @param threshold threshold to register traffic
220 */
221 public void addLoad(Load load, double threshold) {
222 if (load != null) {
223 this.hasTraffic = hasTraffic || load.rate() > threshold;
224 this.bytes += load.latest();
225 this.rate += load.rate();
226 }
227 }
228
229 /**
230 * Adds the given count of flows to this traffic link.
231 *
232 * @param count count of flows
233 */
234 public void addFlows(int count) {
235 this.flows += count;
236 }
237
Simon Hunt0e161092017-05-08 17:41:38 -0700238 /**
239 * Merges the load recorded on the given traffic link into this one.
240 *
241 * @param other the other traffic link
242 */
243 public void mergeStats(TrafficLink other) {
244 this.bytes += other.bytes;
245 this.rate += other.rate;
246 this.flows += other.flows;
247 }
248
249
Simon Hunt4fc86852015-08-20 17:57:52 -0700250 @Override
251 public LinkHighlight highlight(Enum<?> type) {
252 StatsType statsType = (StatsType) type;
253 switch (statsType) {
254 case FLOW_COUNT:
Simon Hunt21281fd2017-03-30 22:28:28 -0700255 return highlightForFlowCount();
Simon Hunt4fc86852015-08-20 17:57:52 -0700256
257 case FLOW_STATS:
258 case PORT_STATS:
Simon Hunt21281fd2017-03-30 22:28:28 -0700259 case PORT_PACKET_STATS:
Simon Hunt4fc86852015-08-20 17:57:52 -0700260 return highlightForStats(statsType);
261
262 case TAGGED:
Simon Hunt21281fd2017-03-30 22:28:28 -0700263 return highlightForTagging();
Simon Hunt4fc86852015-08-20 17:57:52 -0700264
265 default:
266 throw new IllegalStateException("unexpected case: " + statsType);
267 }
268 }
269
270 private LinkHighlight highlightForStats(StatsType type) {
Simon Hunt21281fd2017-03-30 22:28:28 -0700271 ValueLabel vl = null;
272 Mod m = null;
273
274 // based on the type of stats, need to determine the label and "color"...
275 switch (type) {
276 case FLOW_STATS:
277 vl = formatBytes(bytes);
278 // default to "secondary highlighting" of link
279 break;
280
281 case PORT_STATS:
282 vl = formatClippedBitRate(rate);
283
284 // set color based on bits per second...
285 if (vl.magnitude() == Magnitude.ONE ||
286 vl.magnitude() == Magnitude.KILO) {
287 m = PORT_TRAFFIC_GREEN;
288
289 } else if (vl.magnitude() == Magnitude.MEGA) {
290 m = PORT_TRAFFIC_YELLOW;
291
292 } else if (vl.magnitude() == Magnitude.GIGA) {
293 m = vl.clipped() ? PORT_TRAFFIC_RED : PORT_TRAFFIC_ORANGE;
294 }
295 break;
296
297 case PORT_PACKET_STATS:
298 vl = formatPacketRate(rate);
299
Thomas Vachuska605134d2017-03-31 11:15:39 -0700300 // FIXME: Provisional color threshold parameters for packets
301 // set color based on bits per second...
302 if (rate < 10) {
303 m = PORT_TRAFFIC_GREEN;
304
305 } else if (rate < 1000) {
306 m = PORT_TRAFFIC_YELLOW;
307
308 } else if (rate < 100000) {
309 m = PORT_TRAFFIC_ORANGE;
310 } else {
311 m = PORT_TRAFFIC_RED;
312 }
Simon Hunt21281fd2017-03-30 22:28:28 -0700313 break;
314
315 default:
316 break;
Andrea Campanellaaf934682017-03-05 11:06:40 +0100317 }
Simon Hunt21281fd2017-03-30 22:28:28 -0700318
319 LinkHighlight hlite = new LinkHighlight(linkId(), SECONDARY_HIGHLIGHT);
320 if (vl != null) {
321 hlite.setLabel(vl.toString());
322 }
323 if (m != null) {
324 hlite.addMod(m);
325 }
326
327 return addCustomMods(hlite);
Simon Hunt4fc86852015-08-20 17:57:52 -0700328 }
329
Simon Hunt21281fd2017-03-30 22:28:28 -0700330 private LinkHighlight highlightForFlowCount() {
Simon Hunt4fc86852015-08-20 17:57:52 -0700331 Flavor flavor = flows > 0 ? PRIMARY_HIGHLIGHT : SECONDARY_HIGHLIGHT;
Andrea Campanellaaf934682017-03-05 11:06:40 +0100332 LinkHighlight hlite = new LinkHighlight(linkId(), flavor)
Simon Hunt21281fd2017-03-30 22:28:28 -0700333 .setLabel(formatFlows(flows));
Andrea Campanellaaf934682017-03-05 11:06:40 +0100334
Simon Hunt21281fd2017-03-30 22:28:28 -0700335 return addCustomMods(hlite);
Simon Hunt4fc86852015-08-20 17:57:52 -0700336 }
337
Simon Hunt21281fd2017-03-30 22:28:28 -0700338 private LinkHighlight highlightForTagging() {
Simon Hunt4fc86852015-08-20 17:57:52 -0700339 LinkHighlight hlite = new LinkHighlight(linkId(), taggedFlavor)
Simon Hunt21281fd2017-03-30 22:28:28 -0700340 .setLabel(hasTraffic ? formatBytes(bytes).toString() : EMPTY);
341
Simon Hunt4fc86852015-08-20 17:57:52 -0700342 if (isOptical) {
343 hlite.addMod(LinkHighlight.MOD_OPTICAL);
344 }
345 if (antMarch) {
346 hlite.addMod(LinkHighlight.MOD_ANIMATED);
347 }
Simon Hunt21281fd2017-03-30 22:28:28 -0700348 return addCustomMods(hlite);
349 }
350
351 private LinkHighlight addCustomMods(LinkHighlight hlite) {
Andrea Campanellaaf934682017-03-05 11:06:40 +0100352 if (!mods.isEmpty()) {
353 mods.forEach(hlite::addMod);
354 }
Simon Hunt4fc86852015-08-20 17:57:52 -0700355 return hlite;
356 }
357
Simon Hunt4fc86852015-08-20 17:57:52 -0700358 /**
359 * Returns true if this link has been deemed to have enough traffic
360 * to register on the topology view in the web UI.
361 *
362 * @return true if this link has displayable traffic
363 */
364 public boolean hasTraffic() {
365 return hasTraffic;
366 }
367
368 /**
369 * Designates type of traffic statistics to report on a highlighted link.
370 */
371 public enum StatsType {
372 /**
373 * Number of flows.
374 */
375 FLOW_COUNT,
376
377 /**
378 * Number of bytes.
379 */
380 FLOW_STATS,
381
382 /**
383 * Number of bits per second.
384 */
385 PORT_STATS,
386
387 /**
Thomas Vachuska0932ac52017-03-30 13:28:49 -0700388 * Number of packets per second.
389 */
390 PORT_PACKET_STATS,
391
392 /**
Simon Hunt4fc86852015-08-20 17:57:52 -0700393 * Custom tagged information.
394 */
395 TAGGED
396 }
397}