From e3cc0b9f4587eb2c5daae03c3afb4811d87abc87 Mon Sep 17 00:00:00 2001 From: Sho SHIMIZU Date: Thu, 20 Nov 2014 16:18:29 -0800 Subject: [PATCH] Enhance StatisticService to allow filtering by Application ID and Group ID Resolve ONOS-205 Change-Id: Ie9318a5ade943bd8e47fb92ee856b7ebead5022e --- .../org/onlab/onos/core/DefaultGroupId.java | 66 ++++++ .../java/org/onlab/onos/core/GroupId.java | 31 +++ .../onos/net/statistic/StatisticService.java | 14 ++ .../onlab/onos/core/DefaultGroupIdTest.java | 41 ++++ .../net/statistic/impl/StatisticManager.java | 188 +++++++++++++++++- 5 files changed, 332 insertions(+), 8 deletions(-) create mode 100644 core/api/src/main/java/org/onlab/onos/core/DefaultGroupId.java create mode 100644 core/api/src/main/java/org/onlab/onos/core/GroupId.java create mode 100644 core/api/src/test/java/org/onlab/onos/core/DefaultGroupIdTest.java diff --git a/core/api/src/main/java/org/onlab/onos/core/DefaultGroupId.java b/core/api/src/main/java/org/onlab/onos/core/DefaultGroupId.java new file mode 100644 index 0000000000..7e51114649 --- /dev/null +++ b/core/api/src/main/java/org/onlab/onos/core/DefaultGroupId.java @@ -0,0 +1,66 @@ +/* + * Copyright 2014 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onlab.onos.core; + +import com.google.common.base.MoreObjects; + +import java.util.Objects; + +/** + * Default implementation of {@link GroupId}. + */ +public class DefaultGroupId implements GroupId { + + private final int id; + + public DefaultGroupId(int id) { + this.id = id; + } + + // Constructor for serialization + private DefaultGroupId() { + this.id = 0; + } + + @Override + public int id() { + return 0; + } + + @Override + public int hashCode() { + return Objects.hash(id); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof DefaultGroupId)) { + return false; + } + final DefaultGroupId other = (DefaultGroupId) obj; + return Objects.equals(this.id, other.id); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("id", id) + .toString(); + } +} diff --git a/core/api/src/main/java/org/onlab/onos/core/GroupId.java b/core/api/src/main/java/org/onlab/onos/core/GroupId.java new file mode 100644 index 0000000000..6757fcd812 --- /dev/null +++ b/core/api/src/main/java/org/onlab/onos/core/GroupId.java @@ -0,0 +1,31 @@ +/* + * Copyright 2014 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onlab.onos.core; + +/** + * Group identifier. + */ +public interface GroupId { + + /** + * Returns a group ID as short value. + * The method is not intended for use by application developers. + * Return data type may change in the future release. + * + * @return a group ID as short value + */ + int id(); +} diff --git a/core/api/src/main/java/org/onlab/onos/net/statistic/StatisticService.java b/core/api/src/main/java/org/onlab/onos/net/statistic/StatisticService.java index cc974744f3..de598863d1 100644 --- a/core/api/src/main/java/org/onlab/onos/net/statistic/StatisticService.java +++ b/core/api/src/main/java/org/onlab/onos/net/statistic/StatisticService.java @@ -15,11 +15,15 @@ */ package org.onlab.onos.net.statistic; +import org.onlab.onos.core.ApplicationId; +import org.onlab.onos.core.GroupId; import org.onlab.onos.net.ConnectPoint; import org.onlab.onos.net.Link; import org.onlab.onos.net.Path; import org.onlab.onos.net.flow.FlowRule; +import java.util.Optional; + /** * Service for obtaining statistic information about link in the system. * Statistics are obtained from the FlowRuleService in order to minimize the @@ -68,4 +72,14 @@ public interface StatisticService { */ FlowRule highestHitter(ConnectPoint connectPoint); + /** + * Obtain the load for a the ingress to the given link used by + * the specified application ID and group ID. + * + * @param link link to query + * @param appId application ID to filter with + * @param groupId group ID to filter with + * @return {@link Load Load} + */ + Load load(Link link, ApplicationId appId, Optional groupId); } diff --git a/core/api/src/test/java/org/onlab/onos/core/DefaultGroupIdTest.java b/core/api/src/test/java/org/onlab/onos/core/DefaultGroupIdTest.java new file mode 100644 index 0000000000..53b3d2c1b8 --- /dev/null +++ b/core/api/src/test/java/org/onlab/onos/core/DefaultGroupIdTest.java @@ -0,0 +1,41 @@ +/* + * Copyright 2014 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onlab.onos.core; + +import com.google.common.testing.EqualsTester; +import org.junit.Test; + +/** + * Test for DefaultGroupId. + */ +public class DefaultGroupIdTest { + + /** + * Tests the equality of the instances. + */ + @Test + public void testEquality() { + DefaultGroupId id1 = new DefaultGroupId((short) 1); + DefaultGroupId id2 = new DefaultGroupId((short) 1); + DefaultGroupId id3 = new DefaultGroupId((short) 2); + + new EqualsTester() + .addEqualityGroup(id1, id2) + .addEqualityGroup(id3) + .testEquals(); + } + +} diff --git a/core/net/src/main/java/org/onlab/onos/net/statistic/impl/StatisticManager.java b/core/net/src/main/java/org/onlab/onos/net/statistic/impl/StatisticManager.java index 3bff3c5de0..fd4b7411f9 100644 --- a/core/net/src/main/java/org/onlab/onos/net/statistic/impl/StatisticManager.java +++ b/core/net/src/main/java/org/onlab/onos/net/statistic/impl/StatisticManager.java @@ -15,12 +15,18 @@ */ package org.onlab.onos.net.statistic.impl; +import com.google.common.base.MoreObjects; +import com.google.common.base.Predicate; +import com.google.common.collect.FluentIterable; +import com.google.common.collect.ImmutableSet; import org.apache.felix.scr.annotations.Activate; import org.apache.felix.scr.annotations.Component; import org.apache.felix.scr.annotations.Deactivate; import org.apache.felix.scr.annotations.Reference; import org.apache.felix.scr.annotations.ReferenceCardinality; import org.apache.felix.scr.annotations.Service; +import org.onlab.onos.core.ApplicationId; +import org.onlab.onos.core.GroupId; import org.onlab.onos.net.ConnectPoint; import org.onlab.onos.net.Link; import org.onlab.onos.net.Path; @@ -35,8 +41,13 @@ import org.onlab.onos.net.statistic.Load; import org.onlab.onos.net.statistic.StatisticService; import org.onlab.onos.net.statistic.StatisticStore; import org.slf4j.Logger; + +import java.util.Collections; +import java.util.Objects; +import java.util.Optional; import java.util.Set; +import static com.google.common.base.Preconditions.checkNotNull; import static org.slf4j.LoggerFactory.getLogger; /** @@ -75,6 +86,25 @@ public class StatisticManager implements StatisticService { return load(link.src()); } + @Override + public Load load(Link link, ApplicationId appId, Optional groupId) { + Statistics stats = getStatistics(link.src()); + if (!stats.isValid()) { + return new DefaultLoad(); + } + + ImmutableSet current = FluentIterable.from(stats.current()) + .filter(hasApplicationId(appId)) + .filter(hasGroupId(groupId)) + .toSet(); + ImmutableSet previous = FluentIterable.from(stats.previous()) + .filter(hasApplicationId(appId)) + .filter(hasGroupId(groupId)) + .toSet(); + + return new DefaultLoad(aggregate(current), aggregate(previous)); + } + @Override public Load load(ConnectPoint connectPoint) { return loadInternal(connectPoint); @@ -131,21 +161,63 @@ public class StatisticManager implements StatisticService { } private Load loadInternal(ConnectPoint connectPoint) { + Statistics stats = getStatistics(connectPoint); + if (!stats.isValid()) { + return new DefaultLoad(); + } + + return new DefaultLoad(aggregate(stats.current), aggregate(stats.previous)); + } + + /** + * Returns statistics of the specified port. + * + * @param connectPoint port to query + * @return statistics + */ + private Statistics getStatistics(ConnectPoint connectPoint) { Set current; Set previous; synchronized (statisticStore) { - current = statisticStore.getCurrentStatistic(connectPoint); - previous = statisticStore.getPreviousStatistic(connectPoint); + current = getCurrentStatistic(connectPoint); + previous = getPreviousStatistic(connectPoint); } - if (current == null || previous == null) { - return new DefaultLoad(); - } - long currentAggregate = aggregate(current); - long previousAggregate = aggregate(previous); - return new DefaultLoad(currentAggregate, previousAggregate); + return new Statistics(current, previous); } + /** + * Returns the current statistic of the specified port. + + * @param connectPoint port to query + * @return set of flow entries + */ + private Set getCurrentStatistic(ConnectPoint connectPoint) { + Set stats = statisticStore.getCurrentStatistic(connectPoint); + if (stats == null) { + return Collections.emptySet(); + } else { + return stats; + } + } + + /** + * Returns the previous statistic of the specified port. + * + * @param connectPoint port to query + * @return set of flow entries + */ + private Set getPreviousStatistic(ConnectPoint connectPoint) { + Set stats = statisticStore.getCurrentStatistic(connectPoint); + if (stats == null) { + return Collections.emptySet(); + } else { + return stats; + } + } + + // TODO: make aggregation function generic by passing a function + // (applying Java 8 Stream API?) /** * Aggregates a set of values. * @param values the values to aggregate @@ -188,5 +260,105 @@ public class StatisticManager implements StatisticService { } } + /** + * Internal data class holding two set of flow entries. + */ + private static class Statistics { + private final ImmutableSet current; + private final ImmutableSet previous; + public Statistics(Set current, Set previous) { + this.current = ImmutableSet.copyOf(checkNotNull(current)); + this.previous = ImmutableSet.copyOf(checkNotNull(previous)); + } + + /** + * Returns flow entries as the current value. + * + * @return flow entries as the current value + */ + public ImmutableSet current() { + return current; + } + + /** + * Returns flow entries as the previous value. + * + * @return flow entries as the previous value + */ + public ImmutableSet previous() { + return previous; + } + + /** + * Validates values are not empty. + * + * @return false if either of the sets is empty. Otherwise, true. + */ + public boolean isValid() { + return !(current.isEmpty() || previous.isEmpty()); + } + + @Override + public int hashCode() { + return Objects.hash(current, previous); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof Statistics)) { + return false; + } + final Statistics other = (Statistics) obj; + return Objects.equals(this.current, other.current) && Objects.equals(this.previous, other.previous); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("current", current) + .add("previous", previous) + .toString(); + } + } + + /** + * Creates a predicate that checks the application ID of a flow entry is the same as + * the specified application ID. + * + * @param appId application ID to be checked + * @return predicate + */ + private static Predicate hasApplicationId(ApplicationId appId) { + return new Predicate() { + @Override + public boolean apply(FlowEntry flowEntry) { + return flowEntry.appId() == appId.id(); + } + }; + } + + /** + * Create a predicate that checks the group ID of a flow entry is the same as + * the specified group ID. + * + * @param groupId group ID to be checked + * @return predicate + */ + private static Predicate hasGroupId(Optional groupId) { + return new Predicate() { + @Override + public boolean apply(FlowEntry flowEntry) { + if (!groupId.isPresent()) { + return false; + } + // FIXME: The left hand type and right hand type don't match + // FlowEntry.groupId() still returns a short value, not int. + return flowEntry.groupId() == groupId.get().id(); + } + }; + } }