ONOS-885: Group store interface definition and in-memory store implementation
Change-Id: I1125fbc23f1e58bcb8aaf5f67c02da610fa7ef25
ONOS-885: Group store interface definition and in-memory store implementation
Change-Id: Id3794bed63785e10ed86c0b5d90bf875d127224c
diff --git a/core/api/src/main/java/org/onosproject/net/group/DefaultGroup.java b/core/api/src/main/java/org/onosproject/net/group/DefaultGroup.java
new file mode 100644
index 0000000..0e4cac1
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/net/group/DefaultGroup.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright 2015 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.onosproject.net.group;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+import org.onosproject.core.GroupId;
+import org.slf4j.Logger;
+
+/**
+ * ONOS implementation of default group that is stored in the system.
+ */
+public class DefaultGroup extends DefaultGroupDescription
+ implements Group, StoredGroupEntry {
+
+ private final Logger log = getLogger(getClass());
+
+ private GroupState state;
+ private long life;
+ private long packets;
+ private long bytes;
+ private GroupId id;
+
+ /**
+ * Default group object constructor with the parameters.
+ *
+ * @param id group identifier
+ * @param groupDesc group description parameters
+ */
+ public DefaultGroup(GroupId id, GroupDescription groupDesc) {
+ super(groupDesc);
+ this.id = id;
+ this.state = GroupState.PENDING_ADD;
+ this.life = 0;
+ this.packets = 0;
+ this.bytes = 0;
+ }
+
+ /**
+ * Returns group identifier associated with a group object.
+ *
+ * @return GroupId Group Identifier
+ */
+ @Override
+ public GroupId id() {
+ return this.id;
+ }
+
+ /**
+ * Returns current state of a group object.
+ *
+ * @return GroupState Group State
+ */
+ @Override
+ public GroupState state() {
+ return this.state;
+ }
+
+ /**
+ * Returns the number of milliseconds this group has been alive.
+ *
+ * @return number of millis
+ */
+ @Override
+ public long life() {
+ return this.life;
+ }
+
+ /**
+ * Returns the number of packets processed by this group.
+ *
+ * @return number of packets
+ */
+ @Override
+ public long packets() {
+ return this.packets;
+ }
+
+ /**
+ * Returns the number of bytes processed by this group.
+ *
+ * @return number of bytes
+ */
+ @Override
+ public long bytes() {
+ return this.bytes;
+ }
+
+ /**
+ * Sets the new state for this entry.
+ *
+ * @param newState new group entry state.
+ */
+ @Override
+ public void setState(Group.GroupState newState) {
+ this.state = newState;
+ }
+
+ /**
+ * Sets how long this entry has been entered in the system.
+ *
+ * @param life epoch time
+ */
+ @Override
+ public void setLife(long life) {
+ this.life = life;
+ }
+
+ /**
+ * Sets number of packets processed by this group entry.
+ *
+ * @param packets a long value
+ */
+ @Override
+ public void setPackets(long packets) {
+ this.packets = packets;
+ }
+
+ /**
+ * Sets number of bytes processed by this group entry.
+ *
+ * @param bytes a long value
+ */
+ @Override
+ public void setBytes(long bytes) {
+ this.bytes = bytes;
+ }
+
+}
diff --git a/core/api/src/main/java/org/onosproject/net/group/DefaultGroupBucket.java b/core/api/src/main/java/org/onosproject/net/group/DefaultGroupBucket.java
index 8c6eb63..3fab387 100644
--- a/core/api/src/main/java/org/onosproject/net/group/DefaultGroupBucket.java
+++ b/core/api/src/main/java/org/onosproject/net/group/DefaultGroupBucket.java
@@ -15,12 +15,12 @@
*/
package org.onosproject.net.group;
-import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import org.onosproject.core.GroupId;
import org.onosproject.net.PortNumber;
import org.onosproject.net.flow.TrafficTreatment;
-import org.onosproject.core.GroupId;
/**
* Group bucket implementation. A group bucket is collection of
@@ -65,7 +65,6 @@
* Creates indirect group bucket.
*
* @param treatment traffic treatment associated with group bucket
- *
* @return indirect group bucket object
*/
public static GroupBucket createIndirectGroupBucket(
@@ -81,7 +80,6 @@
* Creates select group bucket with weight as 1.
*
* @param treatment traffic treatment associated with group bucket
- *
* @return select group bucket object
*/
public static GroupBucket createSelectGroupBucket(
@@ -98,7 +96,6 @@
*
* @param treatment traffic treatment associated with group bucket
* @param weight weight associated with group bucket
- *
* @return select group bucket object
*/
public static GroupBucket createSelectGroupBucket(
@@ -121,7 +118,6 @@
* @param treatment traffic treatment associated with group bucket
* @param watchPort port that determines the liveness of group bucket
* @param watchGroup group that determines the liveness of group bucket
- *
* @return failover group bucket object
*/
public static GroupBucket createFailoverGroupBucket(
@@ -142,7 +138,7 @@
}
/**
- * Return list of Traffic instructions that are part of the bucket.
+ * Returns list of Traffic instructions that are part of the bucket.
*
* @return TrafficTreatment Traffic instruction list
*/
@@ -152,7 +148,7 @@
}
/**
- * Return weight of select group bucket.
+ * Returns weight of select group bucket.
*
* @return short weight associated with a bucket
*/
@@ -162,7 +158,7 @@
}
/**
- * Return port number used for liveness detection for a
+ * Returns port number used for liveness detection for a
* failover bucket.
*
* @return PortNumber port number used for liveness detection
@@ -173,7 +169,7 @@
}
/**
- * Return group identifier used for liveness detection for a
+ * Returns group identifier used for liveness detection for a
* failover bucket.
*
* @return GroupId group identifier to be used for liveness detection
diff --git a/core/api/src/main/java/org/onosproject/net/group/DefaultGroupDescription.java b/core/api/src/main/java/org/onosproject/net/group/DefaultGroupDescription.java
index 46ccafd..25af506 100644
--- a/core/api/src/main/java/org/onosproject/net/group/DefaultGroupDescription.java
+++ b/core/api/src/main/java/org/onosproject/net/group/DefaultGroupDescription.java
@@ -20,6 +20,9 @@
import org.onosproject.core.ApplicationId;
import org.onosproject.net.DeviceId;
+/**
+ * Default implementation of group description interface.
+ */
public class DefaultGroupDescription implements GroupDescription {
private final GroupDescription.Type type;
private final GroupBuckets buckets;
@@ -28,15 +31,15 @@
private final DeviceId deviceId;
/**
+ * Constructor to be used by north bound applications.
+ * NOTE: The caller of this subsystem MUST ensure the appCookie
+ * provided in this API is immutable
*
* @param deviceId device identifier
* @param type type of the group
* @param buckets immutable list of group bucket
* @param appCookie immutable application cookie to be associated with the group
* @param appId application id
- *
- * NOTE: The caller of this subsystem MUST ensure the appCookie
- * provided in this API is immutable
*/
public DefaultGroupDescription(DeviceId deviceId,
GroupDescription.Type type,
@@ -51,7 +54,22 @@
}
/**
- * Return type of a group object.
+ * Constructor to be used by group subsystem internal components.
+ * Creates group description object from another object of same type.
+ *
+ * @param groupDesc group description object
+ *
+ */
+ public DefaultGroupDescription(GroupDescription groupDesc) {
+ this.type = checkNotNull(groupDesc.type());
+ this.deviceId = checkNotNull(groupDesc.deviceId());
+ this.buckets = checkNotNull(groupDesc.buckets());
+ this.appCookie = checkNotNull(groupDesc.appCookie());
+ this.appId = checkNotNull(groupDesc.appId());
+ }
+
+ /**
+ * Returns type of a group object.
*
* @return GroupType group type
*/
@@ -61,7 +79,7 @@
}
/**
- * Return device identifier on which this group object is created.
+ * Returns device identifier on which this group object is created.
*
* @return DeviceId device identifier
*/
@@ -71,7 +89,7 @@
}
/**
- * Return application identifier that has created this group object.
+ * Returns application identifier that has created this group object.
*
* @return ApplicationId application identifier
*/
@@ -81,7 +99,7 @@
}
/**
- * Return application cookie associated with a group object.
+ * Returns application cookie associated with a group object.
*
* @return GroupKey application cookie
*/
@@ -91,7 +109,7 @@
}
/**
- * Return group buckets of a group.
+ * Returns group buckets of a group.
*
* @return GroupBuckets immutable list of group bucket
*/
diff --git a/core/api/src/main/java/org/onosproject/net/group/Group.java b/core/api/src/main/java/org/onosproject/net/group/Group.java
index 33a7969..f7fa507 100644
--- a/core/api/src/main/java/org/onosproject/net/group/Group.java
+++ b/core/api/src/main/java/org/onosproject/net/group/Group.java
@@ -23,35 +23,43 @@
public interface Group extends GroupDescription {
/**
* State of the group object in ONOS.
- * PENDING_ADD: group create request is processed by ONOS and
- * not yet received the confirmation from data plane
- * ADDED: group is created in the data plane
- * PENDING_UPDATE: group update request is processed by ONOS and
- * not received the confirmation from data plane post which state
- * moves to ADDED state
- * PENDING_DELETE: group delete request is processed by ONOS and
- * not received the confirmation from data plane
*/
public enum GroupState {
+ /**
+ * Group create request is processed by ONOS and not yet
+ * received the confirmation from data plane.
+ */
PENDING_ADD,
+ /**
+ * Group is created in the data plane.
+ */
ADDED,
+ /**
+ * Group update request is processed by ONOS and not
+ * received the confirmation from data plane post which
+ * state moves to ADDED state.
+ */
PENDING_UPDATE,
+ /**
+ * Group delete request is processed by ONOS and not
+ * received the confirmation from data plane.
+ */
PENDING_DELETE
}
/**
- * Return group identifier associated with a group object.
+ * Returns group identifier associated with a group object.
*
* @return GroupId Group Identifier
*/
- public GroupId id();
+ GroupId id();
/**
- * Return current state of a group object.
+ * Returns current state of a group object.
*
* @return GroupState Group State
*/
- public GroupState state();
+ GroupState state();
/**
* Returns the number of milliseconds this group has been alive.
diff --git a/core/api/src/main/java/org/onosproject/net/group/GroupBucket.java b/core/api/src/main/java/org/onosproject/net/group/GroupBucket.java
index 3c2e1ec..2bf8882 100644
--- a/core/api/src/main/java/org/onosproject/net/group/GroupBucket.java
+++ b/core/api/src/main/java/org/onosproject/net/group/GroupBucket.java
@@ -15,11 +15,12 @@
*/
package org.onosproject.net.group;
+import org.onosproject.core.GroupId;
import org.onosproject.net.PortNumber;
import org.onosproject.net.flow.TrafficTreatment;
-import org.onosproject.core.GroupId;
-/* Group Bucket definition. A default group Bucket is collection of
+/**
+ * Group Bucket definition. A default group Bucket is collection of
* Instructions that can be performed on a traffic flow. A failover
* group bucket is associated with a specific port or group that
* controls its liveness. A select group bucket contains optional
@@ -27,28 +28,28 @@
*/
public interface GroupBucket {
/**
- * Return group type of the bucket.
+ * Returns group type of the bucket.
*
* @return GroupType group type
*/
public GroupDescription.Type type();
/**
- * Return list of Traffic instructions that are part of the bucket.
+ * Returns list of Traffic instructions that are part of the bucket.
*
* @return TrafficTreatment traffic instruction list
*/
public TrafficTreatment treatment();
/**
- * Return weight of select group bucket.
+ * Returns weight of select group bucket.
*
* @return short weight associated with a bucket
*/
public short weight();
/**
- * Return port number used for liveness detection for a
+ * Returns port number used for liveness detection for a
* failover bucket.
*
* @return PortNumber port number used for liveness detection
@@ -56,7 +57,7 @@
public PortNumber watchPort();
/**
- * Return group identifier used for liveness detection for a
+ * Returns group identifier used for liveness detection for a
* failover bucket.
*
* @return GroupId group identifier to be used for liveness detection
diff --git a/core/api/src/main/java/org/onosproject/net/group/GroupBucketEntry.java b/core/api/src/main/java/org/onosproject/net/group/GroupBucketEntry.java
index c8805ad..f7a6617 100644
--- a/core/api/src/main/java/org/onosproject/net/group/GroupBucketEntry.java
+++ b/core/api/src/main/java/org/onosproject/net/group/GroupBucketEntry.java
@@ -15,20 +15,21 @@
*/
package org.onosproject.net.group;
-/* Generic group bucket entry representation that is stored in a
+/**
+ * Generic group bucket entry representation that is stored in a
* group object. A group bucket entry provides additional info of
* group bucket like statistics...etc
*/
public interface GroupBucketEntry extends GroupBucket {
/**
- * Return Number of packets processed by bucket.
+ * Returns Number of packets processed by bucket.
*
* @return long
*/
public long packets();
/**
- * Return Number of bytes processed by bucket.
+ * Returns Number of bytes processed by bucket.
*
* @return long
*/
diff --git a/core/api/src/main/java/org/onosproject/net/group/GroupBuckets.java b/core/api/src/main/java/org/onosproject/net/group/GroupBuckets.java
index c4aaad2..10f4eca 100644
--- a/core/api/src/main/java/org/onosproject/net/group/GroupBuckets.java
+++ b/core/api/src/main/java/org/onosproject/net/group/GroupBuckets.java
@@ -19,9 +19,11 @@
import java.util.List;
-
import com.google.common.collect.ImmutableList;
+/**
+ * Immutable collection of group bucket.
+ */
public final class GroupBuckets {
private final List<GroupBucket> buckets;
@@ -35,7 +37,7 @@
}
/**
- * Immutable list of group buckets.
+ * Returns immutable list of group buckets.
*
* @return list of group bucket
*/
diff --git a/core/api/src/main/java/org/onosproject/net/group/GroupDescription.java b/core/api/src/main/java/org/onosproject/net/group/GroupDescription.java
index e4ae7ed..74dcb16 100644
--- a/core/api/src/main/java/org/onosproject/net/group/GroupDescription.java
+++ b/core/api/src/main/java/org/onosproject/net/group/GroupDescription.java
@@ -47,35 +47,35 @@
}
/**
- * Return type of a group object.
+ * Returns type of a group object.
*
* @return GroupType group type
*/
public Type type();
/**
- * Return device identifier on which this group object is created.
+ * Returns device identifier on which this group object is created.
*
* @return DeviceId device identifier
*/
public DeviceId deviceId();
/**
- * Return application identifier that has created this group object.
+ * Returns application identifier that has created this group object.
*
* @return ApplicationId application identifier
*/
public ApplicationId appId();
/**
- * Return application cookie associated with a group object.
+ * Returns application cookie associated with a group object.
*
* @return GroupKey application cookie
*/
public GroupKey appCookie();
/**
- * Return group buckets of a group.
+ * Returns group buckets of a group.
*
* @return GroupBuckets immutable list of group bucket
*/
diff --git a/core/api/src/main/java/org/onosproject/net/group/GroupEvent.java b/core/api/src/main/java/org/onosproject/net/group/GroupEvent.java
index 8bbe742..d9d8a97 100644
--- a/core/api/src/main/java/org/onosproject/net/group/GroupEvent.java
+++ b/core/api/src/main/java/org/onosproject/net/group/GroupEvent.java
@@ -18,7 +18,7 @@
import org.onosproject.event.AbstractEvent;
/**
- * Describes flow rule event.
+ * Describes group events.
*/
public class GroupEvent extends AbstractEvent<GroupEvent.Type, Group> {
@@ -48,6 +48,10 @@
*/
GROUP_ADD_REQUESTED,
/*
+ * Signifies that a request to update Group has been added to the store.
+ */
+ GROUP_UPDATE_REQUESTED,
+ /*
* Signifies that a request to delete Group has been added to the store.
*/
GROUP_REMOVE_REQUESTED,
diff --git a/core/api/src/main/java/org/onosproject/net/group/GroupKey.java b/core/api/src/main/java/org/onosproject/net/group/GroupKey.java
index 17cb560..da8d449 100644
--- a/core/api/src/main/java/org/onosproject/net/group/GroupKey.java
+++ b/core/api/src/main/java/org/onosproject/net/group/GroupKey.java
@@ -15,8 +15,9 @@
*/
package org.onosproject.net.group;
-/* Representation of generalized Key that would be used to store
- * groups in <Key, Value> store. Implementation of this interface
+/**
+ * Representation of generalized Key that would be used to store
+ * groups in < Key, Value > store. Implementation of this interface
* MUST override "equals()" and "hashcode()" methods.
*/
public interface GroupKey {
diff --git a/core/api/src/main/java/org/onosproject/net/group/GroupOperation.java b/core/api/src/main/java/org/onosproject/net/group/GroupOperation.java
index a99ed07..44d7e88 100644
--- a/core/api/src/main/java/org/onosproject/net/group/GroupOperation.java
+++ b/core/api/src/main/java/org/onosproject/net/group/GroupOperation.java
@@ -19,6 +19,11 @@
import org.onosproject.core.GroupId;
+/**
+ * Group operation definition to be used between core and provider
+ * layers of group subsystem.
+ *
+ */
public final class GroupOperation {
private final Type opType;
private final GroupId groupId;
@@ -64,7 +69,6 @@
* @param groupId group Identifier
* @param groupType type of the group
* @param buckets immutable list of group buckets to be part of group
- *
* @return add group operation object
*/
public static GroupOperation createAddGroupOperation(GroupId groupId,
@@ -80,7 +84,6 @@
* @param groupId group Identifier
* @param groupType type of the group
* @param buckets immutable list of group buckets to be part of group
- *
* @return modify group operation object
*/
public static GroupOperation createModifyGroupOperation(GroupId groupId,
@@ -96,7 +99,6 @@
*
* @param groupId group Identifier
* @param groupType type of the group
- *
* @return delete group operation object
*/
public static GroupOperation createDeleteGroupOperation(GroupId groupId,
@@ -106,7 +108,7 @@
}
/**
- * Return group operation type.
+ * Returns group operation type.
*
* @return GroupOpType group operation type
*/
@@ -115,7 +117,7 @@
}
/**
- * Return group identifier attribute of the operation.
+ * Returns group identifier attribute of the operation.
*
* @return GroupId group identifier
*/
@@ -124,7 +126,7 @@
}
/**
- * Return group type attribute of the operation.
+ * Returns group type attribute of the operation.
*
* @return GroupType group type
*/
@@ -133,7 +135,7 @@
}
/**
- * Return group buckets associated with the operation.
+ * Returns group buckets associated with the operation.
*
* @return GroupBuckets group buckets
*/
diff --git a/core/api/src/main/java/org/onosproject/net/group/GroupOperations.java b/core/api/src/main/java/org/onosproject/net/group/GroupOperations.java
index 629ecd3..bc03628 100644
--- a/core/api/src/main/java/org/onosproject/net/group/GroupOperations.java
+++ b/core/api/src/main/java/org/onosproject/net/group/GroupOperations.java
@@ -19,9 +19,13 @@
import java.util.List;
-
import com.google.common.collect.ImmutableList;
+/**
+ * Immutable collection of group operation to be used between
+ * core and provider layers of group subsystem.
+ *
+ */
public final class GroupOperations {
private final List<GroupOperation> operations;
@@ -35,7 +39,7 @@
}
/**
- * Immutable list of group operation.
+ * Returns immutable list of group operation.
*
* @return list of group operation
*/
diff --git a/core/api/src/main/java/org/onosproject/net/group/GroupProvider.java b/core/api/src/main/java/org/onosproject/net/group/GroupProvider.java
index 35d949f..6757e66 100644
--- a/core/api/src/main/java/org/onosproject/net/group/GroupProvider.java
+++ b/core/api/src/main/java/org/onosproject/net/group/GroupProvider.java
@@ -19,12 +19,12 @@
import org.onosproject.net.provider.Provider;
/**
- * Abstraction of a flow rule provider.
+ * Abstraction of group provider.
*/
public interface GroupProvider extends Provider {
/**
- * Perform a group operation in the specified device with the
+ * Performs a batch of group operation in the specified device with the
* specified parameters.
*
* @param deviceId device identifier on which the batch of group
diff --git a/core/api/src/main/java/org/onosproject/net/group/GroupProviderService.java b/core/api/src/main/java/org/onosproject/net/group/GroupProviderService.java
index bfa0522..26c7bb1 100644
--- a/core/api/src/main/java/org/onosproject/net/group/GroupProviderService.java
+++ b/core/api/src/main/java/org/onosproject/net/group/GroupProviderService.java
@@ -26,6 +26,11 @@
*/
public interface GroupProviderService extends ProviderService<GroupProvider> {
+ /**
+ * Notifies core if any failure from data plane during group operations.
+ *
+ * @param operation offended group operation
+ */
void groupOperationFailed(GroupOperation operation);
/**
diff --git a/core/api/src/main/java/org/onosproject/net/group/GroupService.java b/core/api/src/main/java/org/onosproject/net/group/GroupService.java
index eb733e1..1fd9984 100644
--- a/core/api/src/main/java/org/onosproject/net/group/GroupService.java
+++ b/core/api/src/main/java/org/onosproject/net/group/GroupService.java
@@ -36,7 +36,7 @@
public interface GroupService {
/**
- * Create a group in the specified device with the provided buckets.
+ * Creates a group in the specified device with the provided buckets.
* This API provides an option for application to associate a cookie
* while creating a group, so that applications can look-up the
* groups based on the cookies. These Groups will be retained by
@@ -55,7 +55,7 @@
void addGroup(GroupDescription groupDesc);
/**
- * Return a group object associated to an application cookie.
+ * Returns a group object associated to an application cookie.
*
* NOTE1: The presence of group object in the system does not
* guarantee that the "group" is actually created in device.
@@ -64,14 +64,13 @@
*
* @param deviceId device identifier
* @param appCookie application cookie to be used for lookup
- *
* @return group associated with the application cookie or
* NULL if Group is not found for the provided cookie
*/
Group getGroup(DeviceId deviceId, GroupKey appCookie);
/**
- * Append buckets to existing group. The caller can optionally
+ * Appends buckets to existing group. The caller can optionally
* associate a new cookie during this updation. GROUP_UPDATED or
* GROUP_UPDATE_FAILED notifications would be provided along with
* cookie depending on the result of the operation on the device
@@ -89,7 +88,7 @@
ApplicationId appId);
/**
- * Remove buckets from existing group. The caller can optionally
+ * Removes buckets from existing group. The caller can optionally
* associate a new cookie during this updation. GROUP_UPDATED or
* GROUP_UPDATE_FAILED notifications would be provided along with
* cookie depending on the result of the operation on the device
@@ -107,7 +106,7 @@
ApplicationId appId);
/**
- * Delete a group associated to an application cookie.
+ * Deletes a group associated to an application cookie.
* GROUP_DELETED or GROUP_DELETE_FAILED notifications would be
* provided along with cookie depending on the result of the
* operation on the device
@@ -119,12 +118,11 @@
void removeGroup(Device deviceId, GroupKey appCookie, ApplicationId appId);
/**
- * Retrieve all groups created by an application in the specified device
+ * Retrieves all groups created by an application in the specified device
* as seen by current controller instance.
*
* @param deviceId device identifier
* @param appId application id
- *
* @return collection of immutable group objects created by the application
*/
Iterable<Group> getGroups(Device deviceId, ApplicationId appId);
diff --git a/core/api/src/main/java/org/onosproject/net/group/GroupStore.java b/core/api/src/main/java/org/onosproject/net/group/GroupStore.java
new file mode 100644
index 0000000..22914f9
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/net/group/GroupStore.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2015 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.onosproject.net.group;
+
+import org.onosproject.net.DeviceId;
+import org.onosproject.store.Store;
+
+/**
+ * Manages inventory of groups per device; not intended for direct use.
+ */
+public interface GroupStore extends Store<GroupEvent, GroupStoreDelegate> {
+
+ public enum UpdateType {
+ /**
+ * Modify existing group entry by adding provided information.
+ */
+ ADD,
+ /**
+ * Modify existing group by removing provided information from it.
+ */
+ REMOVE
+ }
+
+ /**
+ * Returns the number of groups for the specified device in the store.
+ *
+ * @param deviceId the device ID
+ * @return number of groups for the specified device
+ */
+ int getGroupCount(DeviceId deviceId);
+
+ /**
+ * Returns the groups associated with a device.
+ *
+ * @param deviceId the device ID
+ * @return the group entries
+ */
+ Iterable<Group> getGroups(DeviceId deviceId);
+
+ /**
+ * Returns the stored group entry.
+ *
+ * @param deviceId the device ID
+ * @param appCookie the group key
+ * @return a group associated with the key
+ */
+ Group getGroup(DeviceId deviceId, GroupKey appCookie);
+
+ /**
+ * Stores a new group entry using the information from group description.
+ *
+ * @param groupDesc group description to be used to store group entry
+ */
+ void storeGroupDescription(GroupDescription groupDesc);
+
+ /**
+ * Updates the existing group entry with the information
+ * from group description.
+ *
+ * @param deviceId the device ID
+ * @param oldAppCookie the current group key
+ * @param type update type
+ * @param newGroupDesc group description with updates
+ */
+ void updateGroupDescription(DeviceId deviceId,
+ GroupKey oldAppCookie,
+ UpdateType type,
+ GroupDescription newGroupDesc);
+
+ /**
+ * Triggers deleting the existing group entry.
+ *
+ * @param deviceId the device ID
+ * @param appCookie the group key
+ */
+ void deleteGroupDescription(DeviceId deviceId,
+ GroupKey appCookie);
+
+ /**
+ * Stores a new group entry, or updates an existing entry.
+ *
+ * @param group group entry
+ */
+ void addOrUpdateGroupEntry(Group group);
+
+ /**
+ * Removes the group entry from store.
+ *
+ * @param group group entry
+ */
+ void removeGroupEntry(Group group);
+}
diff --git a/core/api/src/main/java/org/onosproject/net/group/GroupStoreDelegate.java b/core/api/src/main/java/org/onosproject/net/group/GroupStoreDelegate.java
new file mode 100644
index 0000000..308ebb1
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/net/group/GroupStoreDelegate.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2015 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.onosproject.net.group;
+
+import org.onosproject.store.StoreDelegate;
+
+/**
+ * Group store delegate abstraction.
+ */
+public interface GroupStoreDelegate extends StoreDelegate<GroupEvent> {
+}
diff --git a/core/api/src/main/java/org/onosproject/net/group/StoredGroupEntry.java b/core/api/src/main/java/org/onosproject/net/group/StoredGroupEntry.java
new file mode 100644
index 0000000..b3557b4
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/net/group/StoredGroupEntry.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2015 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.onosproject.net.group;
+
+/**
+ * Interface that defines set methods for a group entry
+ * that is stored in the system.
+ */
+public interface StoredGroupEntry extends Group {
+
+ /**
+ * Sets the new state for this entry.
+ *
+ * @param newState new group entry state.
+ */
+ void setState(Group.GroupState newState);
+
+ /**
+ * Sets how long this entry has been entered in the system.
+ *
+ * @param life epoch time
+ */
+ void setLife(long life);
+
+ /**
+ * Sets number of packets processed by this group entry.
+ *
+ * @param packets a long value
+ */
+ void setPackets(long packets);
+
+ /**
+ * Sets number of bytes processed by this group entry.
+ *
+ * @param bytes a long value
+ */
+ void setBytes(long bytes);
+
+}
diff --git a/core/store/dist/src/main/java/org/onosproject/store/group/impl/package-info.java b/core/store/dist/src/main/java/org/onosproject/store/group/impl/package-info.java
new file mode 100644
index 0000000..603712f
--- /dev/null
+++ b/core/store/dist/src/main/java/org/onosproject/store/group/impl/package-info.java
@@ -0,0 +1,19 @@
+/*
+ * 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.
+ */
+/**
+ * Implementation of the group store.
+ */
+package org.onosproject.store.group.impl;
\ No newline at end of file
diff --git a/core/store/trivial/src/main/java/org/onosproject/store/trivial/impl/SimpleGroupStore.java b/core/store/trivial/src/main/java/org/onosproject/store/trivial/impl/SimpleGroupStore.java
new file mode 100644
index 0000000..c82ebc2
--- /dev/null
+++ b/core/store/trivial/src/main/java/org/onosproject/store/trivial/impl/SimpleGroupStore.java
@@ -0,0 +1,365 @@
+/*
+ * Copyright 2015 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.onosproject.store.trivial.impl;
+
+import static org.apache.commons.lang3.concurrent.ConcurrentUtils.createIfAbsentUnchecked;
+import static org.slf4j.LoggerFactory.getLogger;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.atomic.AtomicInteger;
+
+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.Service;
+import org.onlab.util.NewConcurrentHashMap;
+import org.onosproject.core.DefaultGroupId;
+import org.onosproject.core.GroupId;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.group.DefaultGroup;
+import org.onosproject.net.group.DefaultGroupDescription;
+import org.onosproject.net.group.Group;
+import org.onosproject.net.group.Group.GroupState;
+import org.onosproject.net.group.GroupBucket;
+import org.onosproject.net.group.GroupBuckets;
+import org.onosproject.net.group.GroupDescription;
+import org.onosproject.net.group.GroupEvent;
+import org.onosproject.net.group.GroupEvent.Type;
+import org.onosproject.net.group.GroupKey;
+import org.onosproject.net.group.GroupStore;
+import org.onosproject.net.group.GroupStoreDelegate;
+import org.onosproject.net.group.StoredGroupEntry;
+import org.onosproject.store.AbstractStore;
+import org.slf4j.Logger;
+
+import com.google.common.base.Function;
+import com.google.common.collect.FluentIterable;
+
+/**
+ * Manages inventory of group entries using trivial in-memory implementation.
+ */
+@Component(immediate = true)
+@Service
+public class SimpleGroupStore
+ extends AbstractStore<GroupEvent, GroupStoreDelegate>
+ implements GroupStore {
+
+ private final Logger log = getLogger(getClass());
+
+ // inner Map is per device group table
+ private final ConcurrentMap<DeviceId, ConcurrentMap<GroupKey, StoredGroupEntry>>
+ groupEntriesByKey = new ConcurrentHashMap<>();
+ private final ConcurrentMap<DeviceId, ConcurrentMap<GroupId, StoredGroupEntry>>
+ groupEntriesById = new ConcurrentHashMap<>();
+
+ private final AtomicInteger groupIdGen = new AtomicInteger();
+
+ @Activate
+ public void activate() {
+ log.info("Started");
+ }
+
+ @Deactivate
+ public void deactivate() {
+ groupEntriesByKey.clear();
+ groupEntriesById.clear();
+ log.info("Stopped");
+ }
+
+ private static NewConcurrentHashMap<GroupKey, StoredGroupEntry> lazyEmptyGroupKeyTable() {
+ return NewConcurrentHashMap.<GroupKey, StoredGroupEntry>ifNeeded();
+ }
+
+ private static NewConcurrentHashMap<GroupId, StoredGroupEntry> lazyEmptyGroupIdTable() {
+ return NewConcurrentHashMap.<GroupId, StoredGroupEntry>ifNeeded();
+ }
+
+ /**
+ * Returns the group key table for specified device.
+ *
+ * @param deviceId identifier of the device
+ * @return Map representing group key table of given device.
+ */
+ private ConcurrentMap<GroupKey, StoredGroupEntry> getGroupKeyTable(DeviceId deviceId) {
+ return createIfAbsentUnchecked(groupEntriesByKey,
+ deviceId, lazyEmptyGroupKeyTable());
+ }
+
+ /**
+ * Returns the group id table for specified device.
+ *
+ * @param deviceId identifier of the device
+ * @return Map representing group key table of given device.
+ */
+ private ConcurrentMap<GroupId, StoredGroupEntry> getGroupIdTable(DeviceId deviceId) {
+ return createIfAbsentUnchecked(groupEntriesById,
+ deviceId, lazyEmptyGroupIdTable());
+ }
+
+ /**
+ * Returns the number of groups for the specified device in the store.
+ *
+ * @return number of groups for the specified device
+ */
+ @Override
+ public int getGroupCount(DeviceId deviceId) {
+ return (groupEntriesByKey.get(deviceId) != null) ?
+ groupEntriesByKey.get(deviceId).size() : 0;
+ }
+
+ /**
+ * Returns the groups associated with a device.
+ *
+ * @param deviceId the device ID
+ *
+ * @return the group entries
+ */
+ @Override
+ public Iterable<Group> getGroups(DeviceId deviceId) {
+ // flatten and make iterator unmodifiable
+ if (groupEntriesByKey.get(deviceId) != null) {
+ return FluentIterable.from(groupEntriesByKey.get(deviceId).values())
+ .transform(
+ new Function<StoredGroupEntry, Group>() {
+
+ @Override
+ public Group apply(
+ StoredGroupEntry input) {
+ return input;
+ }
+ });
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Returns the stored group entry.
+ *
+ * @param deviceId the device ID
+ * @param appCookie the group key
+ *
+ * @return a group associated with the key
+ */
+ @Override
+ public Group getGroup(DeviceId deviceId, GroupKey appCookie) {
+ return (groupEntriesByKey.get(deviceId) != null) ?
+ groupEntriesByKey.get(deviceId).get(appCookie) :
+ null;
+ }
+
+ /**
+ * Stores a new group entry using the information from group description.
+ *
+ * @param groupDesc group description to be used to create group entry
+ */
+ @Override
+ public void storeGroupDescription(GroupDescription groupDesc) {
+ /* Check if a group is existing with the same key */
+ if (getGroup(groupDesc.deviceId(), groupDesc.appCookie()) != null) {
+ return;
+ }
+
+ /* Get a new group identifier */
+ GroupId id = new DefaultGroupId(groupIdGen.incrementAndGet());
+ /* Create a group entry object */
+ StoredGroupEntry group = new DefaultGroup(id, groupDesc);
+ /* Insert the newly created group entry into concurrent key and id maps */
+ ConcurrentMap<GroupKey, StoredGroupEntry> keyTable =
+ getGroupKeyTable(groupDesc.deviceId());
+ keyTable.put(groupDesc.appCookie(), group);
+ ConcurrentMap<GroupId, StoredGroupEntry> idTable =
+ getGroupIdTable(groupDesc.deviceId());
+ idTable.put(id, group);
+ notifyDelegate(new GroupEvent(GroupEvent.Type.GROUP_ADD_REQUESTED,
+ group));
+ }
+
+ /**
+ * Updates the existing group entry with the information
+ * from group description.
+ *
+ * @param deviceId the device ID
+ * @param oldAppCookie the current group key
+ * @param type update type
+ * @param newGroupDesc group description with updates
+ */
+ @Override
+ public void updateGroupDescription(DeviceId deviceId,
+ GroupKey oldAppCookie,
+ UpdateType type,
+ GroupDescription newGroupDesc) {
+ /* Check if a group is existing with the provided key */
+ Group oldGroup = getGroup(deviceId, oldAppCookie);
+ if (oldGroup == null) {
+ return;
+ }
+
+ List<GroupBucket> newBucketList = getUpdatedBucketList(oldGroup,
+ type,
+ newGroupDesc.buckets());
+ if (newBucketList != null) {
+ /* Create a new group object from the old group */
+ GroupBuckets updatedBuckets = new GroupBuckets(newBucketList);
+ GroupDescription updatedGroupDesc = new DefaultGroupDescription(
+ oldGroup.deviceId(),
+ oldGroup.type(),
+ updatedBuckets,
+ newGroupDesc.appCookie(),
+ oldGroup.appId());
+ StoredGroupEntry newGroup = new DefaultGroup(oldGroup.id(),
+ updatedGroupDesc);
+ newGroup.setState(GroupState.PENDING_UPDATE);
+ newGroup.setLife(oldGroup.life());
+ newGroup.setPackets(oldGroup.packets());
+ newGroup.setBytes(oldGroup.bytes());
+ /* Remove the old entry from maps and add new entry
+ * using new key
+ */
+ ConcurrentMap<GroupKey, StoredGroupEntry> keyTable =
+ getGroupKeyTable(oldGroup.deviceId());
+ ConcurrentMap<GroupId, StoredGroupEntry> idTable =
+ getGroupIdTable(oldGroup.deviceId());
+ keyTable.remove(oldGroup.appCookie());
+ idTable.remove(oldGroup.id());
+ keyTable.put(newGroup.appCookie(), newGroup);
+ idTable.put(newGroup.id(), newGroup);
+ notifyDelegate(new GroupEvent(Type.GROUP_UPDATE_REQUESTED, newGroup));
+ }
+ }
+
+ private List<GroupBucket> getUpdatedBucketList(Group oldGroup,
+ UpdateType type,
+ GroupBuckets buckets) {
+ GroupBuckets oldBuckets = oldGroup.buckets();
+ List<GroupBucket> newBucketList = new ArrayList<GroupBucket>(
+ oldBuckets.buckets());
+ boolean groupDescUpdated = false;
+
+ if (type == UpdateType.ADD) {
+ /* Check if the any of the new buckets are part of the
+ * old bucket list
+ */
+ for (GroupBucket addBucket:buckets.buckets()) {
+ if (!newBucketList.contains(addBucket)) {
+ newBucketList.add(addBucket);
+ groupDescUpdated = true;
+ }
+ }
+ } else if (type == UpdateType.REMOVE) {
+ /* Check if the to be removed buckets are part of the
+ * old bucket list
+ */
+ for (GroupBucket removeBucket:buckets.buckets()) {
+ if (newBucketList.contains(removeBucket)) {
+ newBucketList.remove(removeBucket);
+ groupDescUpdated = true;
+ }
+ }
+ }
+
+ if (groupDescUpdated) {
+ return newBucketList;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Triggers deleting the existing group entry.
+ *
+ * @param deviceId the device ID
+ * @param appCookie the group key
+ */
+ @Override
+ public void deleteGroupDescription(DeviceId deviceId,
+ GroupKey appCookie) {
+ /* Check if a group is existing with the provided key */
+ StoredGroupEntry existing = (groupEntriesByKey.get(deviceId) != null) ?
+ groupEntriesByKey.get(deviceId).get(appCookie) :
+ null;
+ if (existing == null) {
+ return;
+ }
+
+ synchronized (existing) {
+ existing.setState(GroupState.PENDING_DELETE);
+ }
+ notifyDelegate(new GroupEvent(Type.GROUP_REMOVE_REQUESTED, existing));
+ }
+
+ /**
+ * Stores a new group entry, or updates an existing entry.
+ *
+ * @param group group entry
+ */
+ @Override
+ public void addOrUpdateGroupEntry(Group group) {
+ // check if this new entry is an update to an existing entry
+ StoredGroupEntry existing = (groupEntriesById.get(
+ group.deviceId()) != null) ?
+ groupEntriesById.get(group.deviceId()).get(group.id()) :
+ null;
+ GroupEvent event = null;
+
+ if (existing != null) {
+ synchronized (existing) {
+ existing.setLife(group.life());
+ existing.setPackets(group.packets());
+ existing.setBytes(group.bytes());
+ if (existing.state() == GroupState.PENDING_ADD) {
+ existing.setState(GroupState.ADDED);
+ event = new GroupEvent(Type.GROUP_ADDED, existing);
+ } else {
+ if (existing.state() == GroupState.PENDING_UPDATE) {
+ existing.setState(GroupState.PENDING_UPDATE);
+ }
+ event = new GroupEvent(Type.GROUP_UPDATED, existing);
+ }
+ }
+ }
+
+ if (event != null) {
+ notifyDelegate(event);
+ }
+ }
+
+ /**
+ * Removes the group entry from store.
+ *
+ * @param group group entry
+ */
+ @Override
+ public void removeGroupEntry(Group group) {
+ StoredGroupEntry existing = (groupEntriesById.get(
+ group.deviceId()) != null) ?
+ groupEntriesById.get(group.deviceId()).get(group.id()) :
+ null;
+
+ if (existing != null) {
+ ConcurrentMap<GroupKey, StoredGroupEntry> keyTable =
+ getGroupKeyTable(existing.deviceId());
+ ConcurrentMap<GroupId, StoredGroupEntry> idTable =
+ getGroupIdTable(existing.deviceId());
+ idTable.remove(existing.id());
+ keyTable.remove(existing.appCookie());
+ notifyDelegate(new GroupEvent(Type.GROUP_REMOVED, existing));
+ }
+ }
+}
diff --git a/core/store/trivial/src/test/java/org/onosproject/store/trivial/impl/SimpleGroupStoreTest.java b/core/store/trivial/src/test/java/org/onosproject/store/trivial/impl/SimpleGroupStoreTest.java
new file mode 100644
index 0000000..b1f03f4
--- /dev/null
+++ b/core/store/trivial/src/test/java/org/onosproject/store/trivial/impl/SimpleGroupStoreTest.java
@@ -0,0 +1,314 @@
+/*
+ * 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.onosproject.store.trivial.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.onosproject.net.DeviceId.deviceId;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.packet.MacAddress;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.DefaultApplicationId;
+import org.onosproject.core.GroupId;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.group.DefaultGroupBucket;
+import org.onosproject.net.group.DefaultGroupDescription;
+import org.onosproject.net.group.Group;
+import org.onosproject.net.group.GroupBucket;
+import org.onosproject.net.group.GroupBuckets;
+import org.onosproject.net.group.GroupDescription;
+import org.onosproject.net.group.GroupEvent;
+import org.onosproject.net.group.GroupKey;
+import org.onosproject.net.group.GroupStoreDelegate;
+import org.onosproject.net.group.GroupStore.UpdateType;
+
+/**
+ * Test of the simple DeviceStore implementation.
+ */
+public class SimpleGroupStoreTest {
+
+ private SimpleGroupStore simpleGroupStore;
+
+ public static final DeviceId D1 = deviceId("of:1");
+
+ @Before
+ public void setUp() throws Exception {
+ simpleGroupStore = new SimpleGroupStore();
+ simpleGroupStore.activate();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ simpleGroupStore.deactivate();
+ }
+
+ public class TestGroupKey implements GroupKey {
+ private String groupId;
+
+ public TestGroupKey(String id) {
+ this.groupId = id;
+ }
+
+ public String id() {
+ return this.groupId;
+ }
+
+ @Override
+ public int hashCode() {
+ return groupId.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof TestGroupKey) {
+ return this.groupId.equals(((TestGroupKey) obj).id());
+ }
+ return false;
+ }
+ }
+
+ private class InternalGroupStoreDelegate
+ implements GroupStoreDelegate {
+ private GroupId createdGroupId = null;
+ private GroupKey createdGroupKey;
+ private GroupBuckets createdBuckets;
+ private GroupEvent.Type expectedEvent;
+
+ public InternalGroupStoreDelegate(GroupKey key,
+ GroupBuckets buckets,
+ GroupEvent.Type expectedEvent) {
+ this.createdBuckets = buckets;
+ this.createdGroupKey = key;
+ this.expectedEvent = expectedEvent;
+ }
+ @Override
+ public void notify(GroupEvent event) {
+ assertEquals(expectedEvent, event.type());
+ assertEquals(Group.Type.SELECT, event.subject().type());
+ assertEquals(D1, event.subject().deviceId());
+ assertEquals(createdGroupKey, event.subject().appCookie());
+ assertEquals(createdBuckets.buckets(), event.subject().buckets().buckets());
+ if (expectedEvent == GroupEvent.Type.GROUP_ADD_REQUESTED) {
+ createdGroupId = event.subject().id();
+ assertEquals(Group.GroupState.PENDING_ADD,
+ event.subject().state());
+ } else if (expectedEvent == GroupEvent.Type.GROUP_ADDED) {
+ createdGroupId = event.subject().id();
+ assertEquals(Group.GroupState.ADDED,
+ event.subject().state());
+ } else if (expectedEvent == GroupEvent.Type.GROUP_UPDATE_REQUESTED) {
+ assertEquals(Group.GroupState.PENDING_UPDATE,
+ event.subject().state());
+ } else if (expectedEvent == GroupEvent.Type.GROUP_REMOVE_REQUESTED) {
+ assertEquals(Group.GroupState.PENDING_DELETE,
+ event.subject().state());
+ } else if (expectedEvent == GroupEvent.Type.GROUP_REMOVED) {
+ createdGroupId = event.subject().id();
+ assertEquals(Group.GroupState.PENDING_DELETE,
+ event.subject().state());
+ }
+ }
+
+ public void verifyGroupId(GroupId id) {
+ assertEquals(createdGroupId, id);
+ }
+ }
+
+ @Test
+ public void testGroupStoreOperations() {
+ ApplicationId appId =
+ new DefaultApplicationId(2, "org.groupstore.test");
+ TestGroupKey key = new TestGroupKey("group1");
+ PortNumber[] ports = {PortNumber.portNumber(31),
+ PortNumber.portNumber(32)};
+ List<PortNumber> outPorts = new ArrayList<PortNumber>();
+ outPorts.add(ports[0]);
+ outPorts.add(ports[1]);
+
+ List<GroupBucket> buckets = new ArrayList<GroupBucket>();
+ for (PortNumber portNumber: outPorts) {
+ TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
+ tBuilder.setOutput(portNumber)
+ .setEthDst(MacAddress.valueOf("00:00:00:00:00:02"))
+ .setEthSrc(MacAddress.valueOf("00:00:00:00:00:01"))
+ .pushMpls()
+ .setMpls(106);
+ buckets.add(DefaultGroupBucket.createSelectGroupBucket(
+ tBuilder.build()));
+ }
+ GroupBuckets groupBuckets = new GroupBuckets(buckets);
+ GroupDescription groupDesc = new DefaultGroupDescription(
+ D1,
+ Group.Type.SELECT,
+ groupBuckets,
+ key,
+ appId);
+ InternalGroupStoreDelegate checkStoreGroupDelegate =
+ new InternalGroupStoreDelegate(key,
+ groupBuckets,
+ GroupEvent.Type.GROUP_ADD_REQUESTED);
+ simpleGroupStore.setDelegate(checkStoreGroupDelegate);
+ /* Testing storeGroup operation */
+ simpleGroupStore.storeGroupDescription(groupDesc);
+
+ /* Testing getGroupCount operation */
+ assertEquals(1, simpleGroupStore.getGroupCount(D1));
+
+ /* Testing getGroup operation */
+ Group createdGroup = simpleGroupStore.getGroup(D1, key);
+ checkStoreGroupDelegate.verifyGroupId(createdGroup.id());
+
+ /* Testing getGroups operation */
+ Iterable<Group> createdGroups = simpleGroupStore.getGroups(D1);
+ int groupCount = 0;
+ for (Group group:createdGroups) {
+ checkStoreGroupDelegate.verifyGroupId(group.id());
+ groupCount++;
+ }
+ assertEquals(1, groupCount);
+ simpleGroupStore.unsetDelegate(checkStoreGroupDelegate);
+
+ /* Testing addOrUpdateGroupEntry operation from southbound */
+ InternalGroupStoreDelegate addGroupEntryDelegate =
+ new InternalGroupStoreDelegate(key,
+ groupBuckets,
+ GroupEvent.Type.GROUP_ADDED);
+ simpleGroupStore.setDelegate(addGroupEntryDelegate);
+ simpleGroupStore.addOrUpdateGroupEntry(createdGroup);
+ simpleGroupStore.unsetDelegate(addGroupEntryDelegate);
+
+ /* Testing updateGroupDescription for ADD operation from northbound */
+ TestGroupKey addKey = new TestGroupKey("group1AddBuckets");
+ PortNumber[] newNeighborPorts = {PortNumber.portNumber(41),
+ PortNumber.portNumber(42)};
+ List<PortNumber> newOutPorts = new ArrayList<PortNumber>();
+ newOutPorts.add(newNeighborPorts[0]);
+ newOutPorts.add(newNeighborPorts[1]);
+
+ List<GroupBucket> toAddBuckets = new ArrayList<GroupBucket>();
+ for (PortNumber portNumber: newOutPorts) {
+ TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
+ tBuilder.setOutput(portNumber)
+ .setEthDst(MacAddress.valueOf("00:00:00:00:00:03"))
+ .setEthSrc(MacAddress.valueOf("00:00:00:00:00:01"))
+ .pushMpls()
+ .setMpls(106);
+ toAddBuckets.add(DefaultGroupBucket.createSelectGroupBucket(
+ tBuilder.build()));
+ }
+ GroupBuckets toAddGroupBuckets = new GroupBuckets(toAddBuckets);
+ buckets.addAll(toAddBuckets);
+ GroupBuckets updatedGroupBuckets = new GroupBuckets(buckets);
+ InternalGroupStoreDelegate updateGroupDescDelegate =
+ new InternalGroupStoreDelegate(addKey,
+ updatedGroupBuckets,
+ GroupEvent.Type.GROUP_UPDATE_REQUESTED);
+ simpleGroupStore.setDelegate(updateGroupDescDelegate);
+ GroupDescription newGroupDesc = new DefaultGroupDescription(
+ D1,
+ Group.Type.SELECT,
+ toAddGroupBuckets,
+ addKey,
+ appId);
+ simpleGroupStore.updateGroupDescription(D1,
+ key,
+ UpdateType.ADD,
+ newGroupDesc);
+ simpleGroupStore.unsetDelegate(updateGroupDescDelegate);
+
+ /* Testing updateGroupDescription for REMOVE operation from northbound */
+ TestGroupKey removeKey = new TestGroupKey("group1RemoveBuckets");
+ List<GroupBucket> toRemoveBuckets = new ArrayList<GroupBucket>();
+ toRemoveBuckets.add(updatedGroupBuckets.buckets().get(0));
+ toRemoveBuckets.add(updatedGroupBuckets.buckets().get(1));
+ GroupBuckets toRemoveGroupBuckets = new GroupBuckets(toRemoveBuckets);
+ List<GroupBucket> remainingBuckets = new ArrayList<GroupBucket>();
+ remainingBuckets.add(updatedGroupBuckets.buckets().get(2));
+ remainingBuckets.add(updatedGroupBuckets.buckets().get(3));
+ GroupBuckets remainingGroupBuckets = new GroupBuckets(remainingBuckets);
+ InternalGroupStoreDelegate removeGroupDescDelegate =
+ new InternalGroupStoreDelegate(removeKey,
+ remainingGroupBuckets,
+ GroupEvent.Type.GROUP_UPDATE_REQUESTED);
+ simpleGroupStore.setDelegate(removeGroupDescDelegate);
+ GroupDescription removeGroupDesc = new DefaultGroupDescription(
+ D1,
+ Group.Type.SELECT,
+ toRemoveGroupBuckets,
+ removeKey,
+ appId);
+ simpleGroupStore.updateGroupDescription(D1,
+ addKey,
+ UpdateType.REMOVE,
+ removeGroupDesc);
+ simpleGroupStore.unsetDelegate(removeGroupDescDelegate);
+
+ /* Testing getGroup operation */
+ Group existingGroup = simpleGroupStore.getGroup(D1, removeKey);
+ checkStoreGroupDelegate.verifyGroupId(existingGroup.id());
+
+ /* Testing addOrUpdateGroupEntry operation from southbound */
+ InternalGroupStoreDelegate updateGroupEntryDelegate =
+ new InternalGroupStoreDelegate(removeKey,
+ remainingGroupBuckets,
+ GroupEvent.Type.GROUP_UPDATED);
+ simpleGroupStore.setDelegate(updateGroupEntryDelegate);
+ simpleGroupStore.addOrUpdateGroupEntry(existingGroup);
+ simpleGroupStore.unsetDelegate(updateGroupEntryDelegate);
+
+ /* Testing deleteGroupDescription operation from northbound */
+ InternalGroupStoreDelegate deleteGroupDescDelegate =
+ new InternalGroupStoreDelegate(removeKey,
+ remainingGroupBuckets,
+ GroupEvent.Type.GROUP_REMOVE_REQUESTED);
+ simpleGroupStore.setDelegate(deleteGroupDescDelegate);
+ simpleGroupStore.deleteGroupDescription(D1, removeKey);
+ simpleGroupStore.unsetDelegate(deleteGroupDescDelegate);
+
+ /* Testing removeGroupEntry operation from southbound */
+ InternalGroupStoreDelegate removeGroupEntryDelegate =
+ new InternalGroupStoreDelegate(removeKey,
+ remainingGroupBuckets,
+ GroupEvent.Type.GROUP_REMOVED);
+ simpleGroupStore.setDelegate(removeGroupEntryDelegate);
+ simpleGroupStore.removeGroupEntry(existingGroup);
+
+ /* Testing getGroup operation */
+ existingGroup = simpleGroupStore.getGroup(D1, removeKey);
+ assertEquals(null, existingGroup);
+ Iterable<Group> existingGroups = simpleGroupStore.getGroups(D1);
+ groupCount = 0;
+ for (Group tmp:existingGroups) {
+ /* To avoid warning */
+ assertEquals(null, tmp);
+ groupCount++;
+ }
+ assertEquals(0, groupCount);
+ assertEquals(0, simpleGroupStore.getGroupCount(D1));
+
+ simpleGroupStore.unsetDelegate(removeGroupEntryDelegate);
+
+ }
+}
+