blob: 02a80832ae061dd650fc98df3596e2aafe210f9f [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 Hunt05eba352017-05-11 20:17:08 -070022import org.onosproject.ui.model.topo.UiLinkId;
Simon Hunt191c84a2015-08-21 08:24:48 -070023import org.onosproject.ui.topo.BiLink;
Simon Hunt4fc86852015-08-20 17:57:52 -070024import org.onosproject.ui.topo.LinkHighlight;
25import org.onosproject.ui.topo.LinkHighlight.Flavor;
Andrea Campanellaaf934682017-03-05 11:06:40 +010026import org.onosproject.ui.topo.Mod;
Simon Hunt21281fd2017-03-30 22:28:28 -070027import org.onosproject.ui.topo.TopoUtils.Magnitude;
28import org.onosproject.ui.topo.TopoUtils.ValueLabel;
Simon Hunt4fc86852015-08-20 17:57:52 -070029
Andrea Campanellaaf934682017-03-05 11:06:40 +010030import java.util.HashSet;
31import java.util.Set;
32
Simon Hunt0e161092017-05-08 17:41:38 -070033import static com.google.common.base.MoreObjects.toStringHelper;
Simon Hunt21281fd2017-03-30 22:28:28 -070034import static org.onosproject.ui.topo.LinkHighlight.Flavor.NO_HIGHLIGHT;
35import static org.onosproject.ui.topo.LinkHighlight.Flavor.PRIMARY_HIGHLIGHT;
36import static org.onosproject.ui.topo.LinkHighlight.Flavor.SECONDARY_HIGHLIGHT;
37import static org.onosproject.ui.topo.TopoUtils.formatBytes;
38import static org.onosproject.ui.topo.TopoUtils.formatClippedBitRate;
39import static org.onosproject.ui.topo.TopoUtils.formatFlows;
40import static org.onosproject.ui.topo.TopoUtils.formatPacketRate;
Simon Hunt4fc86852015-08-20 17:57:52 -070041
42/**
43 * Representation of a link and its inverse, and associated traffic data.
44 * This class understands how to generate the appropriate
45 * {@link LinkHighlight}s for showing traffic data on the topology view.
46 */
47public class TrafficLink extends BiLink {
Simon Hunt21281fd2017-03-30 22:28:28 -070048 private static final Mod PORT_TRAFFIC_GREEN = new Mod("port-traffic-green");
49 private static final Mod PORT_TRAFFIC_YELLOW = new Mod("port-traffic-yellow");
50 private static final Mod PORT_TRAFFIC_ORANGE = new Mod("port-traffic-orange");
51 private static final Mod PORT_TRAFFIC_RED = new Mod("port-traffic-red");
Simon Hunt4fc86852015-08-20 17:57:52 -070052
53 private static final String EMPTY = "";
Simon Hunt4fc86852015-08-20 17:57:52 -070054
55 private long bytes = 0;
56 private long rate = 0;
57 private long flows = 0;
58 private Flavor taggedFlavor = NO_HIGHLIGHT;
59 private boolean hasTraffic = false;
60 private boolean isOptical = false;
61 private boolean antMarch = false;
Andrea Campanellaaf934682017-03-05 11:06:40 +010062 private Set<Mod> mods = new HashSet<>();
Simon Hunt4fc86852015-08-20 17:57:52 -070063
64 /**
65 * Constructs a traffic link for the given key and initial link.
66 *
67 * @param key canonical key for this traffic link
68 * @param link first link
69 */
70 public TrafficLink(LinkKey key, Link link) {
71 super(key, link);
72 }
73
Simon Hunt05eba352017-05-11 20:17:08 -070074
Simon Hunt4fc86852015-08-20 17:57:52 -070075 /**
Simon Hunt05eba352017-05-11 20:17:08 -070076 * Returns an "empty" traffic link (one with no underlying links or stats)
77 * with the given identifier. This is useful when we want to aggregate
78 * stats from other links into a single entity (such as a region-region
79 * link reporting the stats for the links that compose it).
Simon Hunt0e161092017-05-08 17:41:38 -070080 *
Simon Hunt05eba352017-05-11 20:17:08 -070081 * @param id the link identifier
Simon Hunt0e161092017-05-08 17:41:38 -070082 */
Simon Hunt05eba352017-05-11 20:17:08 -070083 public TrafficLink(UiLinkId id) {
84 super(id);
Simon Hunt0e161092017-05-08 17:41:38 -070085 }
86
87 @Override
88 public boolean equals(Object o) {
89 if (this == o) {
90 return true;
91 }
92 if (o == null || getClass() != o.getClass()) {
93 return false;
94 }
95
96 TrafficLink that = (TrafficLink) o;
97
98 return bytes == that.bytes && rate == that.rate &&
99 flows == that.flows && hasTraffic == that.hasTraffic &&
100 isOptical == that.isOptical && antMarch == that.antMarch &&
101 taggedFlavor == that.taggedFlavor && mods.equals(that.mods);
102 }
103
104 @Override
105 public int hashCode() {
106 int result = (int) (bytes ^ (bytes >>> 32));
107 result = 31 * result + (int) (rate ^ (rate >>> 32));
108 result = 31 * result + (int) (flows ^ (flows >>> 32));
109 result = 31 * result + taggedFlavor.hashCode();
110 result = 31 * result + (hasTraffic ? 1 : 0);
111 result = 31 * result + (isOptical ? 1 : 0);
112 result = 31 * result + (antMarch ? 1 : 0);
113 result = 31 * result + mods.hashCode();
114 return result;
115 }
116
117 @Override
118 public String toString() {
119 return toStringHelper(this)
Simon Hunt05eba352017-05-11 20:17:08 -0700120 .add("linkId", linkId())
Simon Hunt0e161092017-05-08 17:41:38 -0700121 .add("bytes", bytes)
122 .add("rate", rate)
123 .add("flows", flows)
124 .toString();
125 }
126
127 /**
128 * Returns the count of bytes.
129 *
130 * @return the byte count
131 */
132 public long bytes() {
133 return bytes;
134 }
135
136 /**
137 * Returns the rate.
138 *
139 * @return the rate
140 */
141 public long rate() {
142 return rate;
143 }
144
145 /**
146 * Returns the flows.
147 *
148 * @return flow count
149 */
150 public long flows() {
151 return flows;
152 }
153
154 /**
Simon Hunt4fc86852015-08-20 17:57:52 -0700155 * Sets the optical flag to the given value.
156 *
157 * @param b true if an optical link
158 * @return self, for chaining
159 */
160 public TrafficLink optical(boolean b) {
161 isOptical = b;
162 return this;
163 }
164
165 /**
166 * Sets the ant march flag to the given value.
167 *
168 * @param b true if marching ants required
169 * @return self, for chaining
170 */
171 public TrafficLink antMarch(boolean b) {
172 antMarch = b;
173 return this;
174 }
175
176 /**
177 * Tags this traffic link with the flavor to be used in visual rendering.
178 *
179 * @param flavor the flavor to tag
180 * @return self, for chaining
181 */
182 public TrafficLink tagFlavor(Flavor flavor) {
Simon Hunt21281fd2017-03-30 22:28:28 -0700183 taggedFlavor = flavor;
Simon Hunt4fc86852015-08-20 17:57:52 -0700184 return this;
185 }
186
187 /**
Andrea Campanellaaf934682017-03-05 11:06:40 +0100188 * Tags this traffic link with the mods to be used in visual rendering.
189 *
190 * @param mods the mods to tag on this link
191 * @return self, for chaining
192 */
193 public TrafficLink tagMods(Set<Mod> mods) {
194 if (mods != null) {
195 this.mods.addAll(mods);
196 }
197 return this;
198 }
199
200 /**
Simon Hunt4fc86852015-08-20 17:57:52 -0700201 * Adds load statistics, marks the traffic link as having traffic.
202 *
203 * @param load load to add
204 */
205 public void addLoad(Load load) {
206 addLoad(load, 0);
207 }
208
209 /**
210 * Adds load statistics, marks the traffic link as having traffic, if the
211 * load {@link Load#rate rate} is greater than the given threshold
212 * (expressed in bytes per second).
213 *
Andrea Campanellaaf934682017-03-05 11:06:40 +0100214 * @param load load to add
Simon Hunt4fc86852015-08-20 17:57:52 -0700215 * @param threshold threshold to register traffic
216 */
217 public void addLoad(Load load, double threshold) {
218 if (load != null) {
219 this.hasTraffic = hasTraffic || load.rate() > threshold;
220 this.bytes += load.latest();
221 this.rate += load.rate();
222 }
223 }
224
225 /**
226 * Adds the given count of flows to this traffic link.
227 *
228 * @param count count of flows
229 */
230 public void addFlows(int count) {
231 this.flows += count;
232 }
233
Simon Hunt0e161092017-05-08 17:41:38 -0700234 /**
235 * Merges the load recorded on the given traffic link into this one.
236 *
237 * @param other the other traffic link
238 */
239 public void mergeStats(TrafficLink other) {
240 this.bytes += other.bytes;
241 this.rate += other.rate;
242 this.flows += other.flows;
243 }
244
245
Simon Hunt4fc86852015-08-20 17:57:52 -0700246 @Override
247 public LinkHighlight highlight(Enum<?> type) {
248 StatsType statsType = (StatsType) type;
249 switch (statsType) {
250 case FLOW_COUNT:
Simon Hunt21281fd2017-03-30 22:28:28 -0700251 return highlightForFlowCount();
Simon Hunt4fc86852015-08-20 17:57:52 -0700252
253 case FLOW_STATS:
254 case PORT_STATS:
Simon Hunt21281fd2017-03-30 22:28:28 -0700255 case PORT_PACKET_STATS:
Simon Hunt4fc86852015-08-20 17:57:52 -0700256 return highlightForStats(statsType);
257
258 case TAGGED:
Simon Hunt21281fd2017-03-30 22:28:28 -0700259 return highlightForTagging();
Simon Hunt4fc86852015-08-20 17:57:52 -0700260
261 default:
262 throw new IllegalStateException("unexpected case: " + statsType);
263 }
264 }
265
266 private LinkHighlight highlightForStats(StatsType type) {
Simon Hunt21281fd2017-03-30 22:28:28 -0700267 ValueLabel vl = null;
268 Mod m = null;
269
270 // based on the type of stats, need to determine the label and "color"...
271 switch (type) {
272 case FLOW_STATS:
273 vl = formatBytes(bytes);
274 // default to "secondary highlighting" of link
275 break;
276
277 case PORT_STATS:
278 vl = formatClippedBitRate(rate);
279
280 // set color based on bits per second...
281 if (vl.magnitude() == Magnitude.ONE ||
282 vl.magnitude() == Magnitude.KILO) {
283 m = PORT_TRAFFIC_GREEN;
284
285 } else if (vl.magnitude() == Magnitude.MEGA) {
286 m = PORT_TRAFFIC_YELLOW;
287
288 } else if (vl.magnitude() == Magnitude.GIGA) {
289 m = vl.clipped() ? PORT_TRAFFIC_RED : PORT_TRAFFIC_ORANGE;
290 }
291 break;
292
293 case PORT_PACKET_STATS:
294 vl = formatPacketRate(rate);
295
Thomas Vachuska605134d2017-03-31 11:15:39 -0700296 // FIXME: Provisional color threshold parameters for packets
297 // set color based on bits per second...
298 if (rate < 10) {
299 m = PORT_TRAFFIC_GREEN;
300
301 } else if (rate < 1000) {
302 m = PORT_TRAFFIC_YELLOW;
303
304 } else if (rate < 100000) {
305 m = PORT_TRAFFIC_ORANGE;
306 } else {
307 m = PORT_TRAFFIC_RED;
308 }
Simon Hunt21281fd2017-03-30 22:28:28 -0700309 break;
310
311 default:
312 break;
Andrea Campanellaaf934682017-03-05 11:06:40 +0100313 }
Simon Hunt21281fd2017-03-30 22:28:28 -0700314
315 LinkHighlight hlite = new LinkHighlight(linkId(), SECONDARY_HIGHLIGHT);
316 if (vl != null) {
317 hlite.setLabel(vl.toString());
318 }
319 if (m != null) {
320 hlite.addMod(m);
321 }
322
323 return addCustomMods(hlite);
Simon Hunt4fc86852015-08-20 17:57:52 -0700324 }
325
Simon Hunt21281fd2017-03-30 22:28:28 -0700326 private LinkHighlight highlightForFlowCount() {
Simon Hunt4fc86852015-08-20 17:57:52 -0700327 Flavor flavor = flows > 0 ? PRIMARY_HIGHLIGHT : SECONDARY_HIGHLIGHT;
Andrea Campanellaaf934682017-03-05 11:06:40 +0100328 LinkHighlight hlite = new LinkHighlight(linkId(), flavor)
Simon Hunt21281fd2017-03-30 22:28:28 -0700329 .setLabel(formatFlows(flows));
Andrea Campanellaaf934682017-03-05 11:06:40 +0100330
Simon Hunt21281fd2017-03-30 22:28:28 -0700331 return addCustomMods(hlite);
Simon Hunt4fc86852015-08-20 17:57:52 -0700332 }
333
Simon Hunt21281fd2017-03-30 22:28:28 -0700334 private LinkHighlight highlightForTagging() {
Simon Hunt4fc86852015-08-20 17:57:52 -0700335 LinkHighlight hlite = new LinkHighlight(linkId(), taggedFlavor)
Simon Hunt21281fd2017-03-30 22:28:28 -0700336 .setLabel(hasTraffic ? formatBytes(bytes).toString() : EMPTY);
337
Simon Hunt4fc86852015-08-20 17:57:52 -0700338 if (isOptical) {
339 hlite.addMod(LinkHighlight.MOD_OPTICAL);
340 }
341 if (antMarch) {
342 hlite.addMod(LinkHighlight.MOD_ANIMATED);
343 }
Simon Hunt21281fd2017-03-30 22:28:28 -0700344 return addCustomMods(hlite);
345 }
346
347 private LinkHighlight addCustomMods(LinkHighlight hlite) {
Andrea Campanellaaf934682017-03-05 11:06:40 +0100348 if (!mods.isEmpty()) {
349 mods.forEach(hlite::addMod);
350 }
Simon Hunt4fc86852015-08-20 17:57:52 -0700351 return hlite;
352 }
353
Simon Hunt4fc86852015-08-20 17:57:52 -0700354 /**
355 * Returns true if this link has been deemed to have enough traffic
356 * to register on the topology view in the web UI.
357 *
358 * @return true if this link has displayable traffic
359 */
360 public boolean hasTraffic() {
361 return hasTraffic;
362 }
363
364 /**
365 * Designates type of traffic statistics to report on a highlighted link.
366 */
367 public enum StatsType {
368 /**
369 * Number of flows.
370 */
371 FLOW_COUNT,
372
373 /**
374 * Number of bytes.
375 */
376 FLOW_STATS,
377
378 /**
379 * Number of bits per second.
380 */
381 PORT_STATS,
382
383 /**
Thomas Vachuska0932ac52017-03-30 13:28:49 -0700384 * Number of packets per second.
385 */
386 PORT_PACKET_STATS,
387
388 /**
Simon Hunt4fc86852015-08-20 17:57:52 -0700389 * Custom tagged information.
390 */
391 TAGGED
392 }
393}