Sho SHIMIZU
Committed by Gerrit Code Review

Enhance StatisticService to allow filtering by Application ID and Group ID

Resolve ONOS-205

Change-Id: Ie9318a5ade943bd8e47fb92ee856b7ebead5022e
1 +/*
2 + * Copyright 2014 Open Networking Laboratory
3 + *
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 + */
16 +package org.onlab.onos.core;
17 +
18 +import com.google.common.base.MoreObjects;
19 +
20 +import java.util.Objects;
21 +
22 +/**
23 + * Default implementation of {@link GroupId}.
24 + */
25 +public class DefaultGroupId implements GroupId {
26 +
27 + private final int id;
28 +
29 + public DefaultGroupId(int id) {
30 + this.id = id;
31 + }
32 +
33 + // Constructor for serialization
34 + private DefaultGroupId() {
35 + this.id = 0;
36 + }
37 +
38 + @Override
39 + public int id() {
40 + return 0;
41 + }
42 +
43 + @Override
44 + public int hashCode() {
45 + return Objects.hash(id);
46 + }
47 +
48 + @Override
49 + public boolean equals(Object obj) {
50 + if (this == obj) {
51 + return true;
52 + }
53 + if (!(obj instanceof DefaultGroupId)) {
54 + return false;
55 + }
56 + final DefaultGroupId other = (DefaultGroupId) obj;
57 + return Objects.equals(this.id, other.id);
58 + }
59 +
60 + @Override
61 + public String toString() {
62 + return MoreObjects.toStringHelper(this)
63 + .add("id", id)
64 + .toString();
65 + }
66 +}
1 +/*
2 + * Copyright 2014 Open Networking Laboratory
3 + *
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 + */
16 +package org.onlab.onos.core;
17 +
18 +/**
19 + * Group identifier.
20 + */
21 +public interface GroupId {
22 +
23 + /**
24 + * Returns a group ID as short value.
25 + * The method is not intended for use by application developers.
26 + * Return data type may change in the future release.
27 + *
28 + * @return a group ID as short value
29 + */
30 + int id();
31 +}
...@@ -15,11 +15,15 @@ ...@@ -15,11 +15,15 @@
15 */ 15 */
16 package org.onlab.onos.net.statistic; 16 package org.onlab.onos.net.statistic;
17 17
18 +import org.onlab.onos.core.ApplicationId;
19 +import org.onlab.onos.core.GroupId;
18 import org.onlab.onos.net.ConnectPoint; 20 import org.onlab.onos.net.ConnectPoint;
19 import org.onlab.onos.net.Link; 21 import org.onlab.onos.net.Link;
20 import org.onlab.onos.net.Path; 22 import org.onlab.onos.net.Path;
21 import org.onlab.onos.net.flow.FlowRule; 23 import org.onlab.onos.net.flow.FlowRule;
22 24
25 +import java.util.Optional;
26 +
23 /** 27 /**
24 * Service for obtaining statistic information about link in the system. 28 * Service for obtaining statistic information about link in the system.
25 * Statistics are obtained from the FlowRuleService in order to minimize the 29 * Statistics are obtained from the FlowRuleService in order to minimize the
...@@ -68,4 +72,14 @@ public interface StatisticService { ...@@ -68,4 +72,14 @@ public interface StatisticService {
68 */ 72 */
69 FlowRule highestHitter(ConnectPoint connectPoint); 73 FlowRule highestHitter(ConnectPoint connectPoint);
70 74
75 + /**
76 + * Obtain the load for a the ingress to the given link used by
77 + * the specified application ID and group ID.
78 + *
79 + * @param link link to query
80 + * @param appId application ID to filter with
81 + * @param groupId group ID to filter with
82 + * @return {@link Load Load}
83 + */
84 + Load load(Link link, ApplicationId appId, Optional<GroupId> groupId);
71 } 85 }
......
1 +/*
2 + * Copyright 2014 Open Networking Laboratory
3 + *
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 + */
16 +package org.onlab.onos.core;
17 +
18 +import com.google.common.testing.EqualsTester;
19 +import org.junit.Test;
20 +
21 +/**
22 + * Test for DefaultGroupId.
23 + */
24 +public class DefaultGroupIdTest {
25 +
26 + /**
27 + * Tests the equality of the instances.
28 + */
29 + @Test
30 + public void testEquality() {
31 + DefaultGroupId id1 = new DefaultGroupId((short) 1);
32 + DefaultGroupId id2 = new DefaultGroupId((short) 1);
33 + DefaultGroupId id3 = new DefaultGroupId((short) 2);
34 +
35 + new EqualsTester()
36 + .addEqualityGroup(id1, id2)
37 + .addEqualityGroup(id3)
38 + .testEquals();
39 + }
40 +
41 +}
...@@ -15,12 +15,18 @@ ...@@ -15,12 +15,18 @@
15 */ 15 */
16 package org.onlab.onos.net.statistic.impl; 16 package org.onlab.onos.net.statistic.impl;
17 17
18 +import com.google.common.base.MoreObjects;
19 +import com.google.common.base.Predicate;
20 +import com.google.common.collect.FluentIterable;
21 +import com.google.common.collect.ImmutableSet;
18 import org.apache.felix.scr.annotations.Activate; 22 import org.apache.felix.scr.annotations.Activate;
19 import org.apache.felix.scr.annotations.Component; 23 import org.apache.felix.scr.annotations.Component;
20 import org.apache.felix.scr.annotations.Deactivate; 24 import org.apache.felix.scr.annotations.Deactivate;
21 import org.apache.felix.scr.annotations.Reference; 25 import org.apache.felix.scr.annotations.Reference;
22 import org.apache.felix.scr.annotations.ReferenceCardinality; 26 import org.apache.felix.scr.annotations.ReferenceCardinality;
23 import org.apache.felix.scr.annotations.Service; 27 import org.apache.felix.scr.annotations.Service;
28 +import org.onlab.onos.core.ApplicationId;
29 +import org.onlab.onos.core.GroupId;
24 import org.onlab.onos.net.ConnectPoint; 30 import org.onlab.onos.net.ConnectPoint;
25 import org.onlab.onos.net.Link; 31 import org.onlab.onos.net.Link;
26 import org.onlab.onos.net.Path; 32 import org.onlab.onos.net.Path;
...@@ -35,8 +41,13 @@ import org.onlab.onos.net.statistic.Load; ...@@ -35,8 +41,13 @@ import org.onlab.onos.net.statistic.Load;
35 import org.onlab.onos.net.statistic.StatisticService; 41 import org.onlab.onos.net.statistic.StatisticService;
36 import org.onlab.onos.net.statistic.StatisticStore; 42 import org.onlab.onos.net.statistic.StatisticStore;
37 import org.slf4j.Logger; 43 import org.slf4j.Logger;
44 +
45 +import java.util.Collections;
46 +import java.util.Objects;
47 +import java.util.Optional;
38 import java.util.Set; 48 import java.util.Set;
39 49
50 +import static com.google.common.base.Preconditions.checkNotNull;
40 import static org.slf4j.LoggerFactory.getLogger; 51 import static org.slf4j.LoggerFactory.getLogger;
41 52
42 /** 53 /**
...@@ -76,6 +87,25 @@ public class StatisticManager implements StatisticService { ...@@ -76,6 +87,25 @@ public class StatisticManager implements StatisticService {
76 } 87 }
77 88
78 @Override 89 @Override
90 + public Load load(Link link, ApplicationId appId, Optional<GroupId> groupId) {
91 + Statistics stats = getStatistics(link.src());
92 + if (!stats.isValid()) {
93 + return new DefaultLoad();
94 + }
95 +
96 + ImmutableSet<FlowEntry> current = FluentIterable.from(stats.current())
97 + .filter(hasApplicationId(appId))
98 + .filter(hasGroupId(groupId))
99 + .toSet();
100 + ImmutableSet<FlowEntry> previous = FluentIterable.from(stats.previous())
101 + .filter(hasApplicationId(appId))
102 + .filter(hasGroupId(groupId))
103 + .toSet();
104 +
105 + return new DefaultLoad(aggregate(current), aggregate(previous));
106 + }
107 +
108 + @Override
79 public Load load(ConnectPoint connectPoint) { 109 public Load load(ConnectPoint connectPoint) {
80 return loadInternal(connectPoint); 110 return loadInternal(connectPoint);
81 } 111 }
...@@ -131,21 +161,63 @@ public class StatisticManager implements StatisticService { ...@@ -131,21 +161,63 @@ public class StatisticManager implements StatisticService {
131 } 161 }
132 162
133 private Load loadInternal(ConnectPoint connectPoint) { 163 private Load loadInternal(ConnectPoint connectPoint) {
164 + Statistics stats = getStatistics(connectPoint);
165 + if (!stats.isValid()) {
166 + return new DefaultLoad();
167 + }
168 +
169 + return new DefaultLoad(aggregate(stats.current), aggregate(stats.previous));
170 + }
171 +
172 + /**
173 + * Returns statistics of the specified port.
174 + *
175 + * @param connectPoint port to query
176 + * @return statistics
177 + */
178 + private Statistics getStatistics(ConnectPoint connectPoint) {
134 Set<FlowEntry> current; 179 Set<FlowEntry> current;
135 Set<FlowEntry> previous; 180 Set<FlowEntry> previous;
136 synchronized (statisticStore) { 181 synchronized (statisticStore) {
137 - current = statisticStore.getCurrentStatistic(connectPoint); 182 + current = getCurrentStatistic(connectPoint);
138 - previous = statisticStore.getPreviousStatistic(connectPoint); 183 + previous = getPreviousStatistic(connectPoint);
139 } 184 }
140 - if (current == null || previous == null) { 185 +
141 - return new DefaultLoad(); 186 + return new Statistics(current, previous);
187 + }
188 +
189 + /**
190 + * Returns the current statistic of the specified port.
191 +
192 + * @param connectPoint port to query
193 + * @return set of flow entries
194 + */
195 + private Set<FlowEntry> getCurrentStatistic(ConnectPoint connectPoint) {
196 + Set<FlowEntry> stats = statisticStore.getCurrentStatistic(connectPoint);
197 + if (stats == null) {
198 + return Collections.emptySet();
199 + } else {
200 + return stats;
142 } 201 }
143 - long currentAggregate = aggregate(current); 202 + }
144 - long previousAggregate = aggregate(previous);
145 203
146 - return new DefaultLoad(currentAggregate, previousAggregate); 204 + /**
205 + * Returns the previous statistic of the specified port.
206 + *
207 + * @param connectPoint port to query
208 + * @return set of flow entries
209 + */
210 + private Set<FlowEntry> getPreviousStatistic(ConnectPoint connectPoint) {
211 + Set<FlowEntry> stats = statisticStore.getCurrentStatistic(connectPoint);
212 + if (stats == null) {
213 + return Collections.emptySet();
214 + } else {
215 + return stats;
216 + }
147 } 217 }
148 218
219 + // TODO: make aggregation function generic by passing a function
220 + // (applying Java 8 Stream API?)
149 /** 221 /**
150 * Aggregates a set of values. 222 * Aggregates a set of values.
151 * @param values the values to aggregate 223 * @param values the values to aggregate
...@@ -188,5 +260,105 @@ public class StatisticManager implements StatisticService { ...@@ -188,5 +260,105 @@ public class StatisticManager implements StatisticService {
188 } 260 }
189 } 261 }
190 262
263 + /**
264 + * Internal data class holding two set of flow entries.
265 + */
266 + private static class Statistics {
267 + private final ImmutableSet<FlowEntry> current;
268 + private final ImmutableSet<FlowEntry> previous;
191 269
270 + public Statistics(Set<FlowEntry> current, Set<FlowEntry> previous) {
271 + this.current = ImmutableSet.copyOf(checkNotNull(current));
272 + this.previous = ImmutableSet.copyOf(checkNotNull(previous));
273 + }
274 +
275 + /**
276 + * Returns flow entries as the current value.
277 + *
278 + * @return flow entries as the current value
279 + */
280 + public ImmutableSet<FlowEntry> current() {
281 + return current;
282 + }
283 +
284 + /**
285 + * Returns flow entries as the previous value.
286 + *
287 + * @return flow entries as the previous value
288 + */
289 + public ImmutableSet<FlowEntry> previous() {
290 + return previous;
291 + }
292 +
293 + /**
294 + * Validates values are not empty.
295 + *
296 + * @return false if either of the sets is empty. Otherwise, true.
297 + */
298 + public boolean isValid() {
299 + return !(current.isEmpty() || previous.isEmpty());
300 + }
301 +
302 + @Override
303 + public int hashCode() {
304 + return Objects.hash(current, previous);
305 + }
306 +
307 + @Override
308 + public boolean equals(Object obj) {
309 + if (this == obj) {
310 + return true;
311 + }
312 + if (!(obj instanceof Statistics)) {
313 + return false;
314 + }
315 + final Statistics other = (Statistics) obj;
316 + return Objects.equals(this.current, other.current) && Objects.equals(this.previous, other.previous);
317 + }
318 +
319 + @Override
320 + public String toString() {
321 + return MoreObjects.toStringHelper(this)
322 + .add("current", current)
323 + .add("previous", previous)
324 + .toString();
325 + }
326 + }
327 +
328 + /**
329 + * Creates a predicate that checks the application ID of a flow entry is the same as
330 + * the specified application ID.
331 + *
332 + * @param appId application ID to be checked
333 + * @return predicate
334 + */
335 + private static Predicate<FlowEntry> hasApplicationId(ApplicationId appId) {
336 + return new Predicate<FlowEntry>() {
337 + @Override
338 + public boolean apply(FlowEntry flowEntry) {
339 + return flowEntry.appId() == appId.id();
340 + }
341 + };
342 + }
343 +
344 + /**
345 + * Create a predicate that checks the group ID of a flow entry is the same as
346 + * the specified group ID.
347 + *
348 + * @param groupId group ID to be checked
349 + * @return predicate
350 + */
351 + private static Predicate<FlowEntry> hasGroupId(Optional<GroupId> groupId) {
352 + return new Predicate<FlowEntry>() {
353 + @Override
354 + public boolean apply(FlowEntry flowEntry) {
355 + if (!groupId.isPresent()) {
356 + return false;
357 + }
358 + // FIXME: The left hand type and right hand type don't match
359 + // FlowEntry.groupId() still returns a short value, not int.
360 + return flowEntry.groupId() == groupId.get().id();
361 + }
362 + };
363 + }
192 } 364 }
......