blob: 846b0e00eed549c8dc61b612e577d3188163e487 [file] [log] [blame]
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2014-present Open Networking Foundation
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -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 */
Brian O'Connorabafb502014-12-02 22:26:20 -080016package org.onosproject.net.statistic.impl;
alshabiba43aa252014-10-21 21:36:41 -070017
Sho SHIMIZUe3cc0b92014-11-20 16:18:29 -080018import com.google.common.base.MoreObjects;
19import com.google.common.base.Predicate;
20import com.google.common.collect.FluentIterable;
21import com.google.common.collect.ImmutableSet;
Brian O'Connorabafb502014-12-02 22:26:20 -080022import org.onosproject.core.ApplicationId;
23import org.onosproject.core.GroupId;
24import org.onosproject.net.ConnectPoint;
25import org.onosproject.net.Link;
26import org.onosproject.net.Path;
Brian O'Connorabafb502014-12-02 22:26:20 -080027import org.onosproject.net.flow.FlowEntry;
28import org.onosproject.net.flow.FlowRule;
29import org.onosproject.net.flow.FlowRuleEvent;
30import org.onosproject.net.flow.FlowRuleListener;
31import org.onosproject.net.flow.FlowRuleService;
32import org.onosproject.net.statistic.DefaultLoad;
33import org.onosproject.net.statistic.Load;
34import org.onosproject.net.statistic.StatisticService;
35import org.onosproject.net.statistic.StatisticStore;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070036import org.osgi.service.component.annotations.Activate;
37import org.osgi.service.component.annotations.Component;
38import org.osgi.service.component.annotations.Deactivate;
39import org.osgi.service.component.annotations.Reference;
40import org.osgi.service.component.annotations.ReferenceCardinality;
alshabiba43aa252014-10-21 21:36:41 -070041import org.slf4j.Logger;
Sho SHIMIZUe3cc0b92014-11-20 16:18:29 -080042
43import java.util.Collections;
44import java.util.Objects;
45import java.util.Optional;
alshabib3d643ec2014-10-22 18:33:00 -070046import java.util.Set;
47
Sho SHIMIZUe3cc0b92014-11-20 16:18:29 -080048import static com.google.common.base.Preconditions.checkNotNull;
Changhoon Yoon541ef712015-05-23 17:18:34 +090049import static org.onosproject.security.AppGuard.checkPermission;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070050import static org.onosproject.security.AppPermission.Type.STATISTIC_READ;
51import static org.slf4j.LoggerFactory.getLogger;
Changhoon Yoon541ef712015-05-23 17:18:34 +090052
alshabiba43aa252014-10-21 21:36:41 -070053
54/**
55 * Provides an implementation of the Statistic Service.
56 */
Ray Milkeyd84f89b2018-08-17 14:54:17 -070057@Component(immediate = true, service = StatisticService.class)
alshabiba43aa252014-10-21 21:36:41 -070058public class StatisticManager implements StatisticService {
59
60 private final Logger log = getLogger(getClass());
61
Ray Milkeyd84f89b2018-08-17 14:54:17 -070062 @Reference(cardinality = ReferenceCardinality.MANDATORY)
alshabiba43aa252014-10-21 21:36:41 -070063 protected FlowRuleService flowRuleService;
64
Ray Milkeyd84f89b2018-08-17 14:54:17 -070065 @Reference(cardinality = ReferenceCardinality.MANDATORY)
alshabiba43aa252014-10-21 21:36:41 -070066 protected StatisticStore statisticStore;
67
alshabib2374fc92014-10-22 11:03:23 -070068
alshabiba43aa252014-10-21 21:36:41 -070069 private final InternalFlowRuleListener listener = new InternalFlowRuleListener();
70
71 @Activate
72 public void activate() {
73 flowRuleService.addListener(listener);
74 log.info("Started");
alshabib2374fc92014-10-22 11:03:23 -070075
alshabiba43aa252014-10-21 21:36:41 -070076 }
77
78 @Deactivate
79 public void deactivate() {
80 flowRuleService.removeListener(listener);
81 log.info("Stopped");
82 }
83
84 @Override
85 public Load load(Link link) {
Changhoon Yoonb856b812015-08-10 03:47:19 +090086 checkPermission(STATISTIC_READ);
Changhoon Yoon541ef712015-05-23 17:18:34 +090087
88 return load(link.src());
alshabiba43aa252014-10-21 21:36:41 -070089 }
90
91 @Override
Sho SHIMIZUe3cc0b92014-11-20 16:18:29 -080092 public Load load(Link link, ApplicationId appId, Optional<GroupId> groupId) {
Changhoon Yoonb856b812015-08-10 03:47:19 +090093 checkPermission(STATISTIC_READ);
Changhoon Yoon541ef712015-05-23 17:18:34 +090094
Sho SHIMIZUe3cc0b92014-11-20 16:18:29 -080095 Statistics stats = getStatistics(link.src());
96 if (!stats.isValid()) {
97 return new DefaultLoad();
98 }
99
100 ImmutableSet<FlowEntry> current = FluentIterable.from(stats.current())
101 .filter(hasApplicationId(appId))
102 .filter(hasGroupId(groupId))
103 .toSet();
104 ImmutableSet<FlowEntry> previous = FluentIterable.from(stats.previous())
105 .filter(hasApplicationId(appId))
106 .filter(hasGroupId(groupId))
107 .toSet();
108
109 return new DefaultLoad(aggregate(current), aggregate(previous));
110 }
111
112 @Override
alshabiba43aa252014-10-21 21:36:41 -0700113 public Load load(ConnectPoint connectPoint) {
Changhoon Yoonb856b812015-08-10 03:47:19 +0900114 checkPermission(STATISTIC_READ);
Changhoon Yoon541ef712015-05-23 17:18:34 +0900115
alshabib3d643ec2014-10-22 18:33:00 -0700116 return loadInternal(connectPoint);
alshabiba43aa252014-10-21 21:36:41 -0700117 }
118
119 @Override
120 public Link max(Path path) {
Changhoon Yoonb856b812015-08-10 03:47:19 +0900121 checkPermission(STATISTIC_READ);
Changhoon Yoon541ef712015-05-23 17:18:34 +0900122
alshabib46122d82014-10-23 09:05:31 -0700123 if (path.links().isEmpty()) {
124 return null;
125 }
126 Load maxLoad = new DefaultLoad();
127 Link maxLink = null;
128 for (Link link : path.links()) {
129 Load load = loadInternal(link.src());
130 if (load.rate() > maxLoad.rate()) {
131 maxLoad = load;
132 maxLink = link;
133 }
134 }
135 return maxLink;
alshabiba43aa252014-10-21 21:36:41 -0700136 }
137
138 @Override
139 public Link min(Path path) {
Changhoon Yoonb856b812015-08-10 03:47:19 +0900140 checkPermission(STATISTIC_READ);
Changhoon Yoon541ef712015-05-23 17:18:34 +0900141
alshabib46122d82014-10-23 09:05:31 -0700142 if (path.links().isEmpty()) {
143 return null;
144 }
145 Load minLoad = new DefaultLoad();
146 Link minLink = null;
147 for (Link link : path.links()) {
148 Load load = loadInternal(link.src());
149 if (load.rate() < minLoad.rate()) {
150 minLoad = load;
151 minLink = link;
152 }
153 }
154 return minLink;
alshabiba43aa252014-10-21 21:36:41 -0700155 }
156
157 @Override
158 public FlowRule highestHitter(ConnectPoint connectPoint) {
Changhoon Yoonb856b812015-08-10 03:47:19 +0900159 checkPermission(STATISTIC_READ);
Changhoon Yoon541ef712015-05-23 17:18:34 +0900160
alshabib46122d82014-10-23 09:05:31 -0700161 Set<FlowEntry> hitters = statisticStore.getCurrentStatistic(connectPoint);
162 if (hitters.isEmpty()) {
163 return null;
164 }
165
166 FlowEntry max = hitters.iterator().next();
167 for (FlowEntry entry : hitters) {
168 if (entry.bytes() > max.bytes()) {
169 max = entry;
170 }
171 }
172 return max;
alshabiba43aa252014-10-21 21:36:41 -0700173 }
174
alshabib3d643ec2014-10-22 18:33:00 -0700175 private Load loadInternal(ConnectPoint connectPoint) {
Sho SHIMIZUe3cc0b92014-11-20 16:18:29 -0800176 Statistics stats = getStatistics(connectPoint);
177 if (!stats.isValid()) {
178 return new DefaultLoad();
179 }
180
181 return new DefaultLoad(aggregate(stats.current), aggregate(stats.previous));
182 }
183
184 /**
185 * Returns statistics of the specified port.
186 *
187 * @param connectPoint port to query
188 * @return statistics
189 */
190 private Statistics getStatistics(ConnectPoint connectPoint) {
alshabib3d643ec2014-10-22 18:33:00 -0700191 Set<FlowEntry> current;
192 Set<FlowEntry> previous;
193 synchronized (statisticStore) {
Sho SHIMIZUe3cc0b92014-11-20 16:18:29 -0800194 current = getCurrentStatistic(connectPoint);
195 previous = getPreviousStatistic(connectPoint);
alshabib3d643ec2014-10-22 18:33:00 -0700196 }
alshabib3d643ec2014-10-22 18:33:00 -0700197
Sho SHIMIZUe3cc0b92014-11-20 16:18:29 -0800198 return new Statistics(current, previous);
alshabib3d643ec2014-10-22 18:33:00 -0700199 }
200
201 /**
Sho SHIMIZUe3cc0b92014-11-20 16:18:29 -0800202 * Returns the current statistic of the specified port.
203
204 * @param connectPoint port to query
205 * @return set of flow entries
206 */
207 private Set<FlowEntry> getCurrentStatistic(ConnectPoint connectPoint) {
208 Set<FlowEntry> stats = statisticStore.getCurrentStatistic(connectPoint);
209 if (stats == null) {
210 return Collections.emptySet();
211 } else {
212 return stats;
213 }
214 }
215
216 /**
217 * Returns the previous statistic of the specified port.
218 *
219 * @param connectPoint port to query
220 * @return set of flow entries
221 */
222 private Set<FlowEntry> getPreviousStatistic(ConnectPoint connectPoint) {
alshabib507ac372014-11-28 18:12:07 -0500223 Set<FlowEntry> stats = statisticStore.getPreviousStatistic(connectPoint);
Sho SHIMIZUe3cc0b92014-11-20 16:18:29 -0800224 if (stats == null) {
225 return Collections.emptySet();
226 } else {
227 return stats;
228 }
229 }
230
231 // TODO: make aggregation function generic by passing a function
232 // (applying Java 8 Stream API?)
233 /**
alshabib3d643ec2014-10-22 18:33:00 -0700234 * Aggregates a set of values.
235 * @param values the values to aggregate
236 * @return a long value
237 */
238 private long aggregate(Set<FlowEntry> values) {
239 long sum = 0;
240 for (FlowEntry f : values) {
241 sum += f.bytes();
242 }
243 return sum;
244 }
245
alshabiba43aa252014-10-21 21:36:41 -0700246 /**
247 * Internal flow rule event listener.
248 */
249 private class InternalFlowRuleListener implements FlowRuleListener {
250
251 @Override
252 public void event(FlowRuleEvent event) {
alshabib3d643ec2014-10-22 18:33:00 -0700253 FlowRule rule = event.subject();
254 switch (event.type()) {
255 case RULE_ADDED:
256 case RULE_UPDATED:
257 if (rule instanceof FlowEntry) {
258 statisticStore.addOrUpdateStatistic((FlowEntry) rule);
alshabib3d643ec2014-10-22 18:33:00 -0700259 }
260 break;
261 case RULE_ADD_REQUESTED:
alshabib3d643ec2014-10-22 18:33:00 -0700262 statisticStore.prepareForStatistics(rule);
263 break;
264 case RULE_REMOVE_REQUESTED:
alshabib3d643ec2014-10-22 18:33:00 -0700265 statisticStore.removeFromStatistics(rule);
266 break;
267 case RULE_REMOVED:
268 break;
269 default:
270 log.warn("Unknown flow rule event {}", event);
271 }
alshabiba43aa252014-10-21 21:36:41 -0700272 }
273 }
274
Sho SHIMIZUe3cc0b92014-11-20 16:18:29 -0800275 /**
276 * Internal data class holding two set of flow entries.
277 */
278 private static class Statistics {
279 private final ImmutableSet<FlowEntry> current;
280 private final ImmutableSet<FlowEntry> previous;
alshabiba43aa252014-10-21 21:36:41 -0700281
Sho SHIMIZUe3cc0b92014-11-20 16:18:29 -0800282 public Statistics(Set<FlowEntry> current, Set<FlowEntry> previous) {
283 this.current = ImmutableSet.copyOf(checkNotNull(current));
284 this.previous = ImmutableSet.copyOf(checkNotNull(previous));
285 }
286
287 /**
288 * Returns flow entries as the current value.
289 *
290 * @return flow entries as the current value
291 */
292 public ImmutableSet<FlowEntry> current() {
293 return current;
294 }
295
296 /**
297 * Returns flow entries as the previous value.
298 *
299 * @return flow entries as the previous value
300 */
301 public ImmutableSet<FlowEntry> previous() {
302 return previous;
303 }
304
305 /**
306 * Validates values are not empty.
307 *
308 * @return false if either of the sets is empty. Otherwise, true.
309 */
310 public boolean isValid() {
311 return !(current.isEmpty() || previous.isEmpty());
312 }
313
314 @Override
315 public int hashCode() {
316 return Objects.hash(current, previous);
317 }
318
319 @Override
320 public boolean equals(Object obj) {
321 if (this == obj) {
322 return true;
323 }
324 if (!(obj instanceof Statistics)) {
325 return false;
326 }
327 final Statistics other = (Statistics) obj;
328 return Objects.equals(this.current, other.current) && Objects.equals(this.previous, other.previous);
329 }
330
331 @Override
332 public String toString() {
333 return MoreObjects.toStringHelper(this)
334 .add("current", current)
335 .add("previous", previous)
336 .toString();
337 }
338 }
339
340 /**
341 * Creates a predicate that checks the application ID of a flow entry is the same as
342 * the specified application ID.
343 *
344 * @param appId application ID to be checked
345 * @return predicate
346 */
347 private static Predicate<FlowEntry> hasApplicationId(ApplicationId appId) {
Sho SHIMIZU74626412015-09-11 11:46:27 -0700348 return flowEntry -> flowEntry.appId() == appId.id();
Sho SHIMIZUe3cc0b92014-11-20 16:18:29 -0800349 }
350
351 /**
352 * Create a predicate that checks the group ID of a flow entry is the same as
353 * the specified group ID.
354 *
355 * @param groupId group ID to be checked
356 * @return predicate
357 */
358 private static Predicate<FlowEntry> hasGroupId(Optional<GroupId> groupId) {
Sho SHIMIZU74626412015-09-11 11:46:27 -0700359 return flowEntry -> {
360 if (!groupId.isPresent()) {
361 return false;
Sho SHIMIZUe3cc0b92014-11-20 16:18:29 -0800362 }
Sho SHIMIZU74626412015-09-11 11:46:27 -0700363 // FIXME: The left hand type and right hand type don't match
364 // FlowEntry.groupId() still returns a short value, not int.
365 return flowEntry.groupId().equals(groupId.get());
Sho SHIMIZUe3cc0b92014-11-20 16:18:29 -0800366 };
367 }
alshabiba43aa252014-10-21 21:36:41 -0700368}