blob: fa7df866763c4d48a97f0d982ecb6b011058063f [file] [log] [blame]
Thomas Vachuskaf0397b52015-05-29 13:50:17 -07001/*
Thomas Vachuska52f2cd12018-11-08 21:20:04 -08002 * Copyright 2018-present Open Networking Foundation
Thomas Vachuskaf0397b52015-05-29 13:50:17 -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.
15 */
Thomas Vachuska52f2cd12018-11-08 21:20:04 -080016package org.onosproject.net.statistic.impl;
Thomas Vachuskaf0397b52015-05-29 13:50:17 -070017
18import com.google.common.collect.Maps;
Thomas Vachuska52f2cd12018-11-08 21:20:04 -080019import org.onosproject.net.statistic.PortStatisticsService;
Thomas Vachuskaf0397b52015-05-29 13:50:17 -070020import org.onosproject.net.ConnectPoint;
21import org.onosproject.net.DeviceId;
22import org.onosproject.net.device.DeviceEvent;
23import org.onosproject.net.device.DeviceListener;
24import org.onosproject.net.device.DeviceService;
25import org.onosproject.net.device.PortStatistics;
26import org.onosproject.net.statistic.DefaultLoad;
27import org.onosproject.net.statistic.Load;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070028import org.osgi.service.component.annotations.Activate;
29import org.osgi.service.component.annotations.Component;
30import org.osgi.service.component.annotations.Deactivate;
31import org.osgi.service.component.annotations.Reference;
32import org.osgi.service.component.annotations.ReferenceCardinality;
Thomas Vachuskaf0397b52015-05-29 13:50:17 -070033import org.slf4j.Logger;
34
35import java.util.Map;
36import java.util.stream.Collectors;
37
Ray Milkeyd84f89b2018-08-17 14:54:17 -070038import static org.onosproject.net.device.DeviceEvent.Type.DEVICE_AVAILABILITY_CHANGED;
39import static org.onosproject.net.device.DeviceEvent.Type.DEVICE_REMOVED;
40import static org.onosproject.net.device.DeviceEvent.Type.PORT_STATS_UPDATED;
Thomas Vachuskaf0397b52015-05-29 13:50:17 -070041import static org.slf4j.LoggerFactory.getLogger;
42
43/**
44 * Implementation of the port statistics service.
45 */
Ray Milkeyd84f89b2018-08-17 14:54:17 -070046@Component(immediate = true, service = PortStatisticsService.class)
Thomas Vachuskaf0397b52015-05-29 13:50:17 -070047public class PortStatisticsManager implements PortStatisticsService {
Thomas Vachuskad910a5c2015-05-29 17:09:59 -070048
Thomas Vachuskaf0397b52015-05-29 13:50:17 -070049 private final Logger log = getLogger(getClass());
50
Thomas Vachuska204cb6c2015-06-04 00:03:06 -070051 private static final long POLL_FREQUENCY = 10_000; // milliseconds
52 private static final long STALE_LIMIT = (long) (1.5 * POLL_FREQUENCY);
Srikanth Vavilapalli78baf582015-06-05 11:40:14 -070053 private static final int SECOND = 1_000; // milliseconds
Thomas Vachuskad910a5c2015-05-29 17:09:59 -070054
Ray Milkeyd84f89b2018-08-17 14:54:17 -070055 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Thomas Vachuskaf0397b52015-05-29 13:50:17 -070056 protected DeviceService deviceService;
57
58 private final DeviceListener deviceListener = new InternalDeviceListener();
59
60 private Map<ConnectPoint, DataPoint> current = Maps.newConcurrentMap();
61 private Map<ConnectPoint, DataPoint> previous = Maps.newConcurrentMap();
62
63 @Activate
64 public void activate() {
65 deviceService.addListener(deviceListener);
66 log.info("Started");
Thomas Vachuskaf0397b52015-05-29 13:50:17 -070067 }
68
69 @Deactivate
70 public void deactivate() {
71 deviceService.removeListener(deviceListener);
72 log.info("Stopped");
73 }
74
75 @Override
76 public Load load(ConnectPoint connectPoint) {
Thomas Vachuska0932ac52017-03-30 13:28:49 -070077 return load(connectPoint, MetricType.BYTES);
78 }
79
80 @Override
81 public Load load(ConnectPoint connectPoint, MetricType metricType) {
Thomas Vachuskaf0397b52015-05-29 13:50:17 -070082 DataPoint c = current.get(connectPoint);
83 DataPoint p = previous.get(connectPoint);
Thomas Vachuska028ddb92015-06-03 17:36:32 -070084 long now = System.currentTimeMillis();
Thomas Vachuska204cb6c2015-06-04 00:03:06 -070085
86 if (c != null && p != null && (now - c.time < STALE_LIMIT)) {
Srikanth Vavilapalli8c1ccca2015-06-08 19:23:24 -070087 if (c.time > p.time + SECOND) {
Thomas Vachuska0932ac52017-03-30 13:28:49 -070088 long cve = getEgressValue(c.stats, metricType);
89 long cvi = getIngressValue(c.stats, metricType);
90 long pve = getEgressValue(p.stats, metricType);
91 long pvi = getIngressValue(p.stats, metricType);
Srikanth Vavilapalli8c1ccca2015-06-08 19:23:24 -070092 //Use max of either Tx or Rx load as the total load of a port
93 Load load = null;
Thomas Vachuska0932ac52017-03-30 13:28:49 -070094 if (cve >= pve) {
95 load = new DefaultLoad(cve, pve, (int) (c.time - p.time) / SECOND);
Srikanth Vavilapalli8c1ccca2015-06-08 19:23:24 -070096 }
Thomas Vachuska0932ac52017-03-30 13:28:49 -070097 if (cvi >= pvi) {
98 Load rcvLoad = new DefaultLoad(cvi, pvi, (int) (c.time - p.time) / SECOND);
Srikanth Vavilapalli8c1ccca2015-06-08 19:23:24 -070099 load = ((load == null) || (rcvLoad.rate() > load.rate())) ? rcvLoad : load;
100 }
101 return load;
Thomas Vachuska204cb6c2015-06-04 00:03:06 -0700102 }
Thomas Vachuskaf0397b52015-05-29 13:50:17 -0700103 }
104 return null;
105 }
106
Thomas Vachuska0932ac52017-03-30 13:28:49 -0700107 private long getEgressValue(PortStatistics stats, MetricType metricType) {
108 return metricType == MetricType.BYTES ? stats.bytesSent() : stats.packetsSent();
109 }
110
111 private long getIngressValue(PortStatistics stats, MetricType metricType) {
112 return metricType == MetricType.BYTES ? stats.bytesReceived() : stats.packetsReceived();
113 }
114
Thomas Vachuskaf0397b52015-05-29 13:50:17 -0700115 // Monitors port stats update messages.
116 private class InternalDeviceListener implements DeviceListener {
117 @Override
118 public void event(DeviceEvent event) {
119 DeviceEvent.Type type = event.type();
120 DeviceId deviceId = event.subject().id();
121 if (type == PORT_STATS_UPDATED) {
122 // Update port load
123 updateDeviceData(deviceId);
124
125 } else if (type == DEVICE_REMOVED ||
126 (type == DEVICE_AVAILABILITY_CHANGED &&
127 !deviceService.isAvailable(deviceId))) {
128 // Clean-up all port loads
129 pruneDeviceData(deviceId);
130 }
131 }
132 }
133
134 // Updates the port stats for the specified device
135 private void updateDeviceData(DeviceId deviceId) {
136 deviceService.getPortStatistics(deviceId)
137 .forEach(stats -> updatePortData(deviceId, stats));
138 }
139
140 // Updates the port stats for the specified port
141 private void updatePortData(DeviceId deviceId, PortStatistics stats) {
Ray Milkey5ec42082019-02-13 09:56:07 -0800142 ConnectPoint cp = new ConnectPoint(deviceId, stats.portNumber());
Thomas Vachuskaf0397b52015-05-29 13:50:17 -0700143 DataPoint c = current.get(cp);
Thomas Vachuskaf0397b52015-05-29 13:50:17 -0700144
145 // Create a new data point and make it the current one
146 current.put(cp, new DataPoint(stats));
Thomas Vachuska204cb6c2015-06-04 00:03:06 -0700147
148 // If we have a current data point, demote it to previous
149 if (c != null) {
150 previous.put(cp, c);
151 }
Thomas Vachuskaf0397b52015-05-29 13:50:17 -0700152 }
153
154 // Cleans all port loads for the specified device
155 private void pruneDeviceData(DeviceId deviceId) {
156 pruneMap(current, deviceId);
157 pruneMap(previous, deviceId);
158 }
159
160 private void pruneMap(Map<ConnectPoint, DataPoint> map, DeviceId deviceId) {
161 map.keySet().stream().filter(cp -> deviceId.equals(cp.deviceId()))
162 .collect(Collectors.toSet()).forEach(map::remove);
163 }
164
165 // Auxiliary data point to track when we receive different samples.
166 private class DataPoint {
167 long time;
168 PortStatistics stats;
169
170 DataPoint(PortStatistics stats) {
171 time = System.currentTimeMillis();
172 this.stats = stats;
173 }
174 }
175
176}