mirror of
https://github.com/opennetworkinglab/onos.git
synced 2025-10-22 12:51:09 +02:00
ONOS-895: Group manager implementation
Change-Id: Ie183f722fa39012f8de056961715c325e2388e63
This commit is contained in:
parent
0599d51603
commit
45c27c8c62
@ -37,7 +37,7 @@ public class DefaultGroupId implements GroupId {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int id() {
|
public int id() {
|
||||||
return 0;
|
return this.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -17,7 +17,10 @@ package org.onosproject.net.group;
|
|||||||
|
|
||||||
import static org.slf4j.LoggerFactory.getLogger;
|
import static org.slf4j.LoggerFactory.getLogger;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
import org.onosproject.core.GroupId;
|
import org.onosproject.core.GroupId;
|
||||||
|
import org.onosproject.net.DeviceId;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -32,6 +35,7 @@ public class DefaultGroup extends DefaultGroupDescription
|
|||||||
private long life;
|
private long life;
|
||||||
private long packets;
|
private long packets;
|
||||||
private long bytes;
|
private long bytes;
|
||||||
|
private long referenceCount;
|
||||||
private GroupId id;
|
private GroupId id;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -47,6 +51,29 @@ public class DefaultGroup extends DefaultGroupDescription
|
|||||||
this.life = 0;
|
this.life = 0;
|
||||||
this.packets = 0;
|
this.packets = 0;
|
||||||
this.bytes = 0;
|
this.bytes = 0;
|
||||||
|
this.referenceCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default group object constructor with the available information
|
||||||
|
* from data plane.
|
||||||
|
*
|
||||||
|
* @param id group identifier
|
||||||
|
* @param deviceId device identifier
|
||||||
|
* @param type type of the group
|
||||||
|
* @param buckets immutable list of group bucket
|
||||||
|
*/
|
||||||
|
public DefaultGroup(GroupId id,
|
||||||
|
DeviceId deviceId,
|
||||||
|
GroupDescription.Type type,
|
||||||
|
GroupBuckets buckets) {
|
||||||
|
super(deviceId, type, buckets);
|
||||||
|
this.id = id;
|
||||||
|
this.state = GroupState.PENDING_ADD;
|
||||||
|
this.life = 0;
|
||||||
|
this.packets = 0;
|
||||||
|
this.bytes = 0;
|
||||||
|
this.referenceCount = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -139,4 +166,43 @@ public class DefaultGroup extends DefaultGroupDescription
|
|||||||
this.bytes = bytes;
|
this.bytes = bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setReferenceCount(long referenceCount) {
|
||||||
|
this.referenceCount = referenceCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long referenceCount() {
|
||||||
|
return referenceCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The deviceId, type and buckets are used for hash.
|
||||||
|
*
|
||||||
|
* (non-Javadoc)
|
||||||
|
* @see java.lang.Object#equals(java.lang.Object)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return super.hashCode() + Objects.hash(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The deviceId, groupId, type and buckets should be same.
|
||||||
|
*
|
||||||
|
* (non-Javadoc)
|
||||||
|
* @see java.lang.Object#equals(java.lang.Object)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (obj instanceof DefaultGroup) {
|
||||||
|
DefaultGroup that = (DefaultGroup) obj;
|
||||||
|
return super.equals(obj) &&
|
||||||
|
Objects.equals(id, that.id);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,8 @@ package org.onosproject.net.group;
|
|||||||
import static com.google.common.base.Preconditions.checkArgument;
|
import static com.google.common.base.Preconditions.checkArgument;
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
import org.onosproject.core.GroupId;
|
import org.onosproject.core.GroupId;
|
||||||
import org.onosproject.net.PortNumber;
|
import org.onosproject.net.PortNumber;
|
||||||
import org.onosproject.net.flow.TrafficTreatment;
|
import org.onosproject.net.flow.TrafficTreatment;
|
||||||
@ -178,4 +180,36 @@ public final class DefaultGroupBucket implements GroupBucket {
|
|||||||
public GroupId watchGroup() {
|
public GroupId watchGroup() {
|
||||||
return watchGroup;
|
return watchGroup;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The type and treatment can change on a given bucket
|
||||||
|
*
|
||||||
|
* (non-Javadoc)
|
||||||
|
* @see java.lang.Object#equals(java.lang.Object)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(type, treatment);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The priority and statistics can change on a given treatment and selector
|
||||||
|
*
|
||||||
|
* (non-Javadoc)
|
||||||
|
* @see java.lang.Object#equals(java.lang.Object)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (obj instanceof DefaultGroupBucket) {
|
||||||
|
DefaultGroupBucket that = (DefaultGroupBucket) obj;
|
||||||
|
return Objects.equals(type, that.type) &&
|
||||||
|
this.treatment.instructions().containsAll(that.treatment.instructions()) &&
|
||||||
|
that.treatment.instructions().containsAll(this.treatment.instructions());
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,8 @@ package org.onosproject.net.group;
|
|||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
import org.onosproject.core.ApplicationId;
|
import org.onosproject.core.ApplicationId;
|
||||||
import org.onosproject.net.DeviceId;
|
import org.onosproject.net.DeviceId;
|
||||||
|
|
||||||
@ -49,8 +51,8 @@ public class DefaultGroupDescription implements GroupDescription {
|
|||||||
this.type = checkNotNull(type);
|
this.type = checkNotNull(type);
|
||||||
this.deviceId = checkNotNull(deviceId);
|
this.deviceId = checkNotNull(deviceId);
|
||||||
this.buckets = checkNotNull(buckets);
|
this.buckets = checkNotNull(buckets);
|
||||||
this.appCookie = checkNotNull(appCookie);
|
this.appCookie = appCookie;
|
||||||
this.appId = checkNotNull(appId);
|
this.appId = appId;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -61,11 +63,27 @@ public class DefaultGroupDescription implements GroupDescription {
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public DefaultGroupDescription(GroupDescription groupDesc) {
|
public DefaultGroupDescription(GroupDescription groupDesc) {
|
||||||
this.type = checkNotNull(groupDesc.type());
|
this.type = groupDesc.type();
|
||||||
this.deviceId = checkNotNull(groupDesc.deviceId());
|
this.deviceId = groupDesc.deviceId();
|
||||||
this.buckets = checkNotNull(groupDesc.buckets());
|
this.buckets = groupDesc.buckets();
|
||||||
this.appCookie = checkNotNull(groupDesc.appCookie());
|
this.appCookie = groupDesc.appCookie();
|
||||||
this.appId = checkNotNull(groupDesc.appId());
|
this.appId = groupDesc.appId();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor to be used by group subsystem internal components.
|
||||||
|
* Creates group description object from the information retrieved
|
||||||
|
* from data plane.
|
||||||
|
*
|
||||||
|
* @param deviceId device identifier
|
||||||
|
* @param type type of the group
|
||||||
|
* @param buckets immutable list of group bucket
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public DefaultGroupDescription(DeviceId deviceId,
|
||||||
|
GroupDescription.Type type,
|
||||||
|
GroupBuckets buckets) {
|
||||||
|
this(deviceId, type, buckets, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -118,4 +136,36 @@ public class DefaultGroupDescription implements GroupDescription {
|
|||||||
return this.buckets;
|
return this.buckets;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
/*
|
||||||
|
* The deviceId, type and buckets are used for hash.
|
||||||
|
*
|
||||||
|
* (non-Javadoc)
|
||||||
|
* @see java.lang.Object#equals(java.lang.Object)
|
||||||
|
*/
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(deviceId, type, buckets);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
/*
|
||||||
|
* The deviceId, type and buckets should be same.
|
||||||
|
*
|
||||||
|
* (non-Javadoc)
|
||||||
|
* @see java.lang.Object#equals(java.lang.Object)
|
||||||
|
*/
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (obj instanceof DefaultGroupDescription) {
|
||||||
|
DefaultGroupDescription that = (DefaultGroupDescription) obj;
|
||||||
|
return Objects.equals(deviceId, that.deviceId) &&
|
||||||
|
Objects.equals(type, that.type) &&
|
||||||
|
Objects.equals(buckets, that.buckets);
|
||||||
|
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -25,6 +25,10 @@ public interface Group extends GroupDescription {
|
|||||||
* State of the group object in ONOS.
|
* State of the group object in ONOS.
|
||||||
*/
|
*/
|
||||||
public enum GroupState {
|
public enum GroupState {
|
||||||
|
/**
|
||||||
|
* Group create request is queued as group AUDIT is in progress.
|
||||||
|
*/
|
||||||
|
WAITING_AUDIT_COMPLETE,
|
||||||
/**
|
/**
|
||||||
* Group create request is processed by ONOS and not yet
|
* Group create request is processed by ONOS and not yet
|
||||||
* received the confirmation from data plane.
|
* received the confirmation from data plane.
|
||||||
@ -81,4 +85,11 @@ public interface Group extends GroupDescription {
|
|||||||
* @return number of bytes
|
* @return number of bytes
|
||||||
*/
|
*/
|
||||||
long bytes();
|
long bytes();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the number of flow rules or other groups reference this group.
|
||||||
|
*
|
||||||
|
* @return number of flow rules or other groups pointing to this group
|
||||||
|
*/
|
||||||
|
long referenceCount();
|
||||||
}
|
}
|
||||||
|
@ -45,4 +45,25 @@ public final class GroupBuckets {
|
|||||||
return buckets;
|
return buckets;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
int result = 17;
|
||||||
|
int combinedHash = 0;
|
||||||
|
for (GroupBucket bucket:buckets) {
|
||||||
|
combinedHash = combinedHash + bucket.hashCode();
|
||||||
|
}
|
||||||
|
result = 31 * result + combinedHash;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (obj instanceof GroupBuckets) {
|
||||||
|
return (this.buckets.containsAll(((GroupBuckets) obj).buckets) &&
|
||||||
|
((GroupBuckets) obj).buckets.containsAll(this.buckets));
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -17,6 +17,8 @@ package org.onosproject.net.group;
|
|||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
import org.onosproject.core.GroupId;
|
import org.onosproject.core.GroupId;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -142,4 +144,37 @@ public final class GroupOperation {
|
|||||||
public GroupBuckets buckets() {
|
public GroupBuckets buckets() {
|
||||||
return this.buckets;
|
return this.buckets;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
/*
|
||||||
|
* The deviceId, type and buckets are used for hash.
|
||||||
|
*
|
||||||
|
* (non-Javadoc)
|
||||||
|
* @see java.lang.Object#equals(java.lang.Object)
|
||||||
|
*/
|
||||||
|
public int hashCode() {
|
||||||
|
return (buckets != null) ? Objects.hash(groupId, opType, buckets) :
|
||||||
|
Objects.hash(groupId, opType);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
/*
|
||||||
|
* The deviceId, type and buckets should be same.
|
||||||
|
*
|
||||||
|
* (non-Javadoc)
|
||||||
|
* @see java.lang.Object#equals(java.lang.Object)
|
||||||
|
*/
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (obj instanceof GroupOperation) {
|
||||||
|
GroupOperation that = (GroupOperation) obj;
|
||||||
|
return Objects.equals(groupId, that.groupId) &&
|
||||||
|
Objects.equals(opType, that.opType) &&
|
||||||
|
Objects.equals(buckets, that.buckets);
|
||||||
|
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
/*
|
||||||
|
* 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.provider.ProviderRegistry;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstraction for a group provider registry.
|
||||||
|
*/
|
||||||
|
public interface GroupProviderRegistry
|
||||||
|
extends ProviderRegistry<GroupProvider, GroupProviderService> {
|
||||||
|
}
|
@ -16,7 +16,6 @@
|
|||||||
package org.onosproject.net.group;
|
package org.onosproject.net.group;
|
||||||
|
|
||||||
import org.onosproject.core.ApplicationId;
|
import org.onosproject.core.ApplicationId;
|
||||||
import org.onosproject.net.Device;
|
|
||||||
import org.onosproject.net.DeviceId;
|
import org.onosproject.net.DeviceId;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -27,7 +26,7 @@ import org.onosproject.net.DeviceId;
|
|||||||
* specified in a group.
|
* specified in a group.
|
||||||
* "group" can also be used for grouping common actions of different flows,
|
* "group" can also be used for grouping common actions of different flows,
|
||||||
* so that in some scenarios only one group entry required to be modified
|
* so that in some scenarios only one group entry required to be modified
|
||||||
* for all the referencing flow entries instead of modifying all of them
|
* for all the referencing flow entries instead of modifying all of them.
|
||||||
*
|
*
|
||||||
* This implements semantics of a distributed authoritative group store
|
* This implements semantics of a distributed authoritative group store
|
||||||
* where the master copy of the groups lies with the controller and
|
* where the master copy of the groups lies with the controller and
|
||||||
@ -60,7 +59,7 @@ public interface GroupService {
|
|||||||
* NOTE1: The presence of group object in the system does not
|
* NOTE1: The presence of group object in the system does not
|
||||||
* guarantee that the "group" is actually created in device.
|
* guarantee that the "group" is actually created in device.
|
||||||
* GROUP_ADDED notification would confirm the creation of
|
* GROUP_ADDED notification would confirm the creation of
|
||||||
* this group in data plane
|
* this group in data plane.
|
||||||
*
|
*
|
||||||
* @param deviceId device identifier
|
* @param deviceId device identifier
|
||||||
* @param appCookie application cookie to be used for lookup
|
* @param appCookie application cookie to be used for lookup
|
||||||
@ -73,7 +72,7 @@ public interface GroupService {
|
|||||||
* Appends 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
|
* associate a new cookie during this updation. GROUP_UPDATED or
|
||||||
* GROUP_UPDATE_FAILED notifications would be provided along with
|
* GROUP_UPDATE_FAILED notifications would be provided along with
|
||||||
* cookie depending on the result of the operation on the device
|
* cookie depending on the result of the operation on the device.
|
||||||
*
|
*
|
||||||
* @param deviceId device identifier
|
* @param deviceId device identifier
|
||||||
* @param oldCookie cookie to be used to retrieve the existing group
|
* @param oldCookie cookie to be used to retrieve the existing group
|
||||||
@ -91,7 +90,7 @@ public interface GroupService {
|
|||||||
* Removes 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
|
* associate a new cookie during this updation. GROUP_UPDATED or
|
||||||
* GROUP_UPDATE_FAILED notifications would be provided along with
|
* GROUP_UPDATE_FAILED notifications would be provided along with
|
||||||
* cookie depending on the result of the operation on the device
|
* cookie depending on the result of the operation on the device.
|
||||||
*
|
*
|
||||||
* @param deviceId device identifier
|
* @param deviceId device identifier
|
||||||
* @param oldCookie cookie to be used to retrieve the existing group
|
* @param oldCookie cookie to be used to retrieve the existing group
|
||||||
@ -99,7 +98,7 @@ public interface GroupService {
|
|||||||
* @param newCookie immutable cookie to be used post update operation
|
* @param newCookie immutable cookie to be used post update operation
|
||||||
* @param appId Application Id
|
* @param appId Application Id
|
||||||
*/
|
*/
|
||||||
void removeBucketsFromGroup(Device deviceId,
|
void removeBucketsFromGroup(DeviceId deviceId,
|
||||||
GroupKey oldCookie,
|
GroupKey oldCookie,
|
||||||
GroupBuckets buckets,
|
GroupBuckets buckets,
|
||||||
GroupKey newCookie,
|
GroupKey newCookie,
|
||||||
@ -109,13 +108,13 @@ public interface GroupService {
|
|||||||
* Deletes a group associated to an application cookie.
|
* Deletes a group associated to an application cookie.
|
||||||
* GROUP_DELETED or GROUP_DELETE_FAILED notifications would be
|
* GROUP_DELETED or GROUP_DELETE_FAILED notifications would be
|
||||||
* provided along with cookie depending on the result of the
|
* provided along with cookie depending on the result of the
|
||||||
* operation on the device
|
* operation on the device.
|
||||||
*
|
*
|
||||||
* @param deviceId device identifier
|
* @param deviceId device identifier
|
||||||
* @param appCookie application cookie to be used for lookup
|
* @param appCookie application cookie to be used for lookup
|
||||||
* @param appId Application Id
|
* @param appId Application Id
|
||||||
*/
|
*/
|
||||||
void removeGroup(Device deviceId, GroupKey appCookie, ApplicationId appId);
|
void removeGroup(DeviceId deviceId, GroupKey appCookie, ApplicationId appId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves all groups created by an application in the specified device
|
* Retrieves all groups created by an application in the specified device
|
||||||
@ -125,7 +124,7 @@ public interface GroupService {
|
|||||||
* @param appId application id
|
* @param appId application id
|
||||||
* @return collection of immutable group objects created by the application
|
* @return collection of immutable group objects created by the application
|
||||||
*/
|
*/
|
||||||
Iterable<Group> getGroups(Device deviceId, ApplicationId appId);
|
Iterable<Group> getGroups(DeviceId deviceId, ApplicationId appId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds the specified group listener.
|
* Adds the specified group listener.
|
||||||
|
@ -73,12 +73,14 @@ public interface GroupStore extends Store<GroupEvent, GroupStoreDelegate> {
|
|||||||
* @param deviceId the device ID
|
* @param deviceId the device ID
|
||||||
* @param oldAppCookie the current group key
|
* @param oldAppCookie the current group key
|
||||||
* @param type update type
|
* @param type update type
|
||||||
* @param newGroupDesc group description with updates
|
* @param newBuckets group buckets for updates
|
||||||
|
* @param newAppCookie optional new group key
|
||||||
*/
|
*/
|
||||||
void updateGroupDescription(DeviceId deviceId,
|
void updateGroupDescription(DeviceId deviceId,
|
||||||
GroupKey oldAppCookie,
|
GroupKey oldAppCookie,
|
||||||
UpdateType type,
|
UpdateType type,
|
||||||
GroupDescription newGroupDesc);
|
GroupBuckets newBuckets,
|
||||||
|
GroupKey newAppCookie);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Triggers deleting the existing group entry.
|
* Triggers deleting the existing group entry.
|
||||||
@ -102,4 +104,43 @@ public interface GroupStore extends Store<GroupEvent, GroupStoreDelegate> {
|
|||||||
* @param group group entry
|
* @param group group entry
|
||||||
*/
|
*/
|
||||||
void removeGroupEntry(Group group);
|
void removeGroupEntry(Group group);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A group entry that is present in switch but not in the store.
|
||||||
|
*
|
||||||
|
* @param group group entry
|
||||||
|
*/
|
||||||
|
void addOrUpdateExtraneousGroupEntry(Group group);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the group entry from extraneous database.
|
||||||
|
*
|
||||||
|
* @param group group entry
|
||||||
|
*/
|
||||||
|
void removeExtraneousGroupEntry(Group group);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the extraneous groups associated with a device.
|
||||||
|
*
|
||||||
|
* @param deviceId the device ID
|
||||||
|
*
|
||||||
|
* @return the extraneous group entries
|
||||||
|
*/
|
||||||
|
Iterable<Group> getExtraneousGroups(DeviceId deviceId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates the first group audit is completed.
|
||||||
|
*
|
||||||
|
* @param deviceId the device ID
|
||||||
|
*/
|
||||||
|
void deviceInitialAuditCompleted(DeviceId deviceId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the initial group audit status for a device.
|
||||||
|
*
|
||||||
|
* @param deviceId the device ID
|
||||||
|
*
|
||||||
|
* @return initial group audit status
|
||||||
|
*/
|
||||||
|
boolean deviceInitialAuditStatus(DeviceId deviceId);
|
||||||
}
|
}
|
||||||
|
@ -49,4 +49,10 @@ public interface StoredGroupEntry extends Group {
|
|||||||
*/
|
*/
|
||||||
void setBytes(long bytes);
|
void setBytes(long bytes);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets number of flow rules or groups referencing this group entry.
|
||||||
|
*
|
||||||
|
* @param referenceCount reference count
|
||||||
|
*/
|
||||||
|
void setReferenceCount(long referenceCount);
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,366 @@
|
|||||||
|
/*
|
||||||
|
* 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.impl;
|
||||||
|
|
||||||
|
import static org.slf4j.LoggerFactory.getLogger;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
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.onosproject.core.ApplicationId;
|
||||||
|
import org.onosproject.event.AbstractListenerRegistry;
|
||||||
|
import org.onosproject.event.EventDeliveryService;
|
||||||
|
import org.onosproject.net.DeviceId;
|
||||||
|
import org.onosproject.net.group.Group;
|
||||||
|
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.GroupListener;
|
||||||
|
import org.onosproject.net.group.GroupOperation;
|
||||||
|
import org.onosproject.net.group.GroupOperations;
|
||||||
|
import org.onosproject.net.group.GroupProvider;
|
||||||
|
import org.onosproject.net.group.GroupProviderRegistry;
|
||||||
|
import org.onosproject.net.group.GroupProviderService;
|
||||||
|
import org.onosproject.net.group.GroupService;
|
||||||
|
import org.onosproject.net.group.GroupStore;
|
||||||
|
import org.onosproject.net.group.GroupStore.UpdateType;
|
||||||
|
import org.onosproject.net.group.GroupStoreDelegate;
|
||||||
|
import org.onosproject.net.provider.AbstractProviderRegistry;
|
||||||
|
import org.onosproject.net.provider.AbstractProviderService;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
|
||||||
|
import com.google.common.collect.Sets;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides implementation of the group service APIs.
|
||||||
|
*/
|
||||||
|
@Component(immediate = true)
|
||||||
|
@Service
|
||||||
|
public class GroupManager
|
||||||
|
extends AbstractProviderRegistry<GroupProvider, GroupProviderService>
|
||||||
|
implements GroupService, GroupProviderRegistry {
|
||||||
|
|
||||||
|
private final Logger log = getLogger(getClass());
|
||||||
|
|
||||||
|
private final AbstractListenerRegistry<GroupEvent, GroupListener>
|
||||||
|
listenerRegistry = new AbstractListenerRegistry<>();
|
||||||
|
private final GroupStoreDelegate delegate = new InternalGroupStoreDelegate();
|
||||||
|
|
||||||
|
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
|
||||||
|
protected GroupStore store;
|
||||||
|
|
||||||
|
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
|
||||||
|
protected EventDeliveryService eventDispatcher;
|
||||||
|
|
||||||
|
@Activate
|
||||||
|
public void activate() {
|
||||||
|
store.setDelegate(delegate);
|
||||||
|
eventDispatcher.addSink(GroupEvent.class, listenerRegistry);
|
||||||
|
log.info("Started");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deactivate
|
||||||
|
public void deactivate() {
|
||||||
|
store.unsetDelegate(delegate);
|
||||||
|
eventDispatcher.removeSink(GroupEvent.class);
|
||||||
|
log.info("Stopped");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a group in the specified device with the provided parameters.
|
||||||
|
*
|
||||||
|
* @param groupDesc group creation parameters
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void addGroup(GroupDescription groupDesc) {
|
||||||
|
store.storeGroupDescription(groupDesc);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return 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.
|
||||||
|
* GROUP_ADDED notification would confirm the creation of
|
||||||
|
* this group in data plane.
|
||||||
|
*
|
||||||
|
* @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
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Group getGroup(DeviceId deviceId, GroupKey appCookie) {
|
||||||
|
return store.getGroup(deviceId, appCookie);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Append 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.
|
||||||
|
*
|
||||||
|
* @param deviceId device identifier
|
||||||
|
* @param oldCookie cookie to be used to retrieve the existing group
|
||||||
|
* @param buckets immutable list of group bucket to be added
|
||||||
|
* @param newCookie immutable cookie to be used post update operation
|
||||||
|
* @param appId Application Id
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void addBucketsToGroup(DeviceId deviceId,
|
||||||
|
GroupKey oldCookie,
|
||||||
|
GroupBuckets buckets,
|
||||||
|
GroupKey newCookie,
|
||||||
|
ApplicationId appId) {
|
||||||
|
store.updateGroupDescription(deviceId,
|
||||||
|
oldCookie,
|
||||||
|
UpdateType.ADD,
|
||||||
|
buckets,
|
||||||
|
newCookie);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove 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.
|
||||||
|
*
|
||||||
|
* @param deviceId device identifier
|
||||||
|
* @param oldCookie cookie to be used to retrieve the existing group
|
||||||
|
* @param buckets immutable list of group bucket to be removed
|
||||||
|
* @param newCookie immutable cookie to be used post update operation
|
||||||
|
* @param appId Application Id
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void removeBucketsFromGroup(DeviceId deviceId,
|
||||||
|
GroupKey oldCookie,
|
||||||
|
GroupBuckets buckets,
|
||||||
|
GroupKey newCookie,
|
||||||
|
ApplicationId appId) {
|
||||||
|
store.updateGroupDescription(deviceId,
|
||||||
|
oldCookie,
|
||||||
|
UpdateType.REMOVE,
|
||||||
|
buckets,
|
||||||
|
newCookie);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete 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.
|
||||||
|
*
|
||||||
|
* @param deviceId device identifier
|
||||||
|
* @param appCookie application cookie to be used for lookup
|
||||||
|
* @param appId Application Id
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void removeGroup(DeviceId deviceId,
|
||||||
|
GroupKey appCookie,
|
||||||
|
ApplicationId appId) {
|
||||||
|
store.deleteGroupDescription(deviceId, appCookie);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve 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
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Iterable<Group> getGroups(DeviceId deviceId,
|
||||||
|
ApplicationId appId) {
|
||||||
|
return store.getGroups(deviceId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the specified group listener.
|
||||||
|
*
|
||||||
|
* @param listener group listener
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void addListener(GroupListener listener) {
|
||||||
|
listenerRegistry.addListener(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the specified group listener.
|
||||||
|
*
|
||||||
|
* @param listener group listener
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void removeListener(GroupListener listener) {
|
||||||
|
listenerRegistry.removeListener(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected GroupProviderService createProviderService(GroupProvider provider) {
|
||||||
|
return new InternalGroupProviderService(provider);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class InternalGroupStoreDelegate implements GroupStoreDelegate {
|
||||||
|
@Override
|
||||||
|
public void notify(GroupEvent event) {
|
||||||
|
final Group group = event.subject();
|
||||||
|
GroupProvider groupProvider =
|
||||||
|
getProvider(group.deviceId());
|
||||||
|
GroupOperations groupOps = null;
|
||||||
|
switch (event.type()) {
|
||||||
|
case GROUP_ADD_REQUESTED:
|
||||||
|
GroupOperation groupAddOp = GroupOperation.
|
||||||
|
createAddGroupOperation(group.id(),
|
||||||
|
group.type(),
|
||||||
|
group.buckets());
|
||||||
|
groupOps = new GroupOperations(
|
||||||
|
Arrays.asList(groupAddOp));
|
||||||
|
groupProvider.performGroupOperation(group.deviceId(), groupOps);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GROUP_UPDATE_REQUESTED:
|
||||||
|
GroupOperation groupModifyOp = GroupOperation.
|
||||||
|
createModifyGroupOperation(group.id(),
|
||||||
|
group.type(),
|
||||||
|
group.buckets());
|
||||||
|
groupOps = new GroupOperations(
|
||||||
|
Arrays.asList(groupModifyOp));
|
||||||
|
groupProvider.performGroupOperation(group.deviceId(), groupOps);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GROUP_REMOVE_REQUESTED:
|
||||||
|
GroupOperation groupDeleteOp = GroupOperation.
|
||||||
|
createDeleteGroupOperation(group.id(),
|
||||||
|
group.type());
|
||||||
|
groupOps = new GroupOperations(
|
||||||
|
Arrays.asList(groupDeleteOp));
|
||||||
|
groupProvider.performGroupOperation(group.deviceId(), groupOps);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GROUP_ADDED:
|
||||||
|
case GROUP_UPDATED:
|
||||||
|
case GROUP_REMOVED:
|
||||||
|
eventDispatcher.post(event);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class InternalGroupProviderService
|
||||||
|
extends AbstractProviderService<GroupProvider>
|
||||||
|
implements GroupProviderService {
|
||||||
|
|
||||||
|
protected InternalGroupProviderService(GroupProvider provider) {
|
||||||
|
super(provider);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void groupOperationFailed(GroupOperation operation) {
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void groupMissing(Group group) {
|
||||||
|
checkValidity();
|
||||||
|
GroupProvider gp = getProvider(group.deviceId());
|
||||||
|
switch (group.state()) {
|
||||||
|
case PENDING_DELETE:
|
||||||
|
store.removeGroupEntry(group);
|
||||||
|
break;
|
||||||
|
case ADDED:
|
||||||
|
case PENDING_ADD:
|
||||||
|
GroupOperation groupAddOp = GroupOperation.
|
||||||
|
createAddGroupOperation(group.id(),
|
||||||
|
group.type(),
|
||||||
|
group.buckets());
|
||||||
|
GroupOperations groupOps = new GroupOperations(
|
||||||
|
Arrays.asList(groupAddOp));
|
||||||
|
gp.performGroupOperation(group.deviceId(), groupOps);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
log.debug("Group {} has not been installed.", group);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void extraneousGroup(Group group) {
|
||||||
|
log.debug("Group {} is on switch but not in store.", group);
|
||||||
|
checkValidity();
|
||||||
|
store.addOrUpdateExtraneousGroupEntry(group);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void groupAdded(Group group) {
|
||||||
|
checkValidity();
|
||||||
|
|
||||||
|
log.trace("Group {}", group);
|
||||||
|
store.addOrUpdateGroupEntry(group);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void pushGroupMetrics(DeviceId deviceId,
|
||||||
|
Collection<Group> groupEntries) {
|
||||||
|
boolean deviceInitialAuditStatus =
|
||||||
|
store.deviceInitialAuditStatus(deviceId);
|
||||||
|
Set<Group> southboundGroupEntries =
|
||||||
|
Sets.newHashSet(groupEntries);
|
||||||
|
Set<Group> storedGroupEntries =
|
||||||
|
Sets.newHashSet(store.getGroups(deviceId));
|
||||||
|
Set<Group> extraneousStoredEntries =
|
||||||
|
Sets.newHashSet(store.getExtraneousGroups(deviceId));
|
||||||
|
|
||||||
|
for (Iterator<Group> it = southboundGroupEntries.iterator(); it.hasNext();) {
|
||||||
|
Group group = it.next();
|
||||||
|
if (storedGroupEntries.remove(group)) {
|
||||||
|
// we both have the group, let's update some info then.
|
||||||
|
groupAdded(group);
|
||||||
|
it.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (Group group : southboundGroupEntries) {
|
||||||
|
// there are groups in the switch that aren't in the store
|
||||||
|
extraneousStoredEntries.remove(group);
|
||||||
|
extraneousGroup(group);
|
||||||
|
}
|
||||||
|
for (Group group : storedGroupEntries) {
|
||||||
|
// there are groups in the store that aren't in the switch
|
||||||
|
groupMissing(group);
|
||||||
|
}
|
||||||
|
for (Group group : extraneousStoredEntries) {
|
||||||
|
// there are groups in the extraneous store that
|
||||||
|
// aren't in the switch
|
||||||
|
store.removeExtraneousGroupEntry(group);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!deviceInitialAuditStatus) {
|
||||||
|
store.deviceInitialAuditCompleted(deviceId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Core subsystem for group state.
|
||||||
|
*/
|
||||||
|
package org.onosproject.net.group.impl;
|
@ -0,0 +1,400 @@
|
|||||||
|
/*
|
||||||
|
* 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.impl;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.junit.Assert.assertNotEquals;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
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.DefaultGroupId;
|
||||||
|
import org.onosproject.core.GroupId;
|
||||||
|
import org.onosproject.event.impl.TestEventDispatcher;
|
||||||
|
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.DefaultGroup;
|
||||||
|
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.GroupListener;
|
||||||
|
import org.onosproject.net.group.GroupOperation;
|
||||||
|
import org.onosproject.net.group.GroupOperations;
|
||||||
|
import org.onosproject.net.group.GroupProvider;
|
||||||
|
import org.onosproject.net.group.GroupProviderRegistry;
|
||||||
|
import org.onosproject.net.group.GroupProviderService;
|
||||||
|
import org.onosproject.net.group.GroupService;
|
||||||
|
import org.onosproject.net.group.StoredGroupEntry;
|
||||||
|
import org.onosproject.net.provider.AbstractProvider;
|
||||||
|
import org.onosproject.net.provider.ProviderId;
|
||||||
|
import org.onosproject.store.trivial.impl.SimpleGroupStore;
|
||||||
|
|
||||||
|
import com.google.common.collect.Iterables;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test codifying the group service & group provider service contracts.
|
||||||
|
*/
|
||||||
|
public class GroupManagerTest {
|
||||||
|
|
||||||
|
private static final ProviderId PID = new ProviderId("of", "groupfoo");
|
||||||
|
private static final DeviceId DID = DeviceId.deviceId("of:001");
|
||||||
|
|
||||||
|
private GroupManager mgr;
|
||||||
|
private GroupService groupService;
|
||||||
|
private GroupProviderRegistry providerRegistry;
|
||||||
|
private TestGroupListener internalListener = new TestGroupListener();
|
||||||
|
private GroupListener listener = internalListener;
|
||||||
|
private TestGroupProvider internalProvider;
|
||||||
|
private GroupProvider provider;
|
||||||
|
private GroupProviderService providerService;
|
||||||
|
private ApplicationId appId;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
mgr = new GroupManager();
|
||||||
|
groupService = mgr;
|
||||||
|
mgr.store = new SimpleGroupStore();
|
||||||
|
mgr.eventDispatcher = new TestEventDispatcher();
|
||||||
|
providerRegistry = mgr;
|
||||||
|
|
||||||
|
mgr.activate();
|
||||||
|
mgr.addListener(listener);
|
||||||
|
|
||||||
|
internalProvider = new TestGroupProvider(PID);
|
||||||
|
provider = internalProvider;
|
||||||
|
providerService = providerRegistry.register(provider);
|
||||||
|
appId = new DefaultApplicationId(2, "org.groupmanager.test");
|
||||||
|
assertTrue("provider should be registered",
|
||||||
|
providerRegistry.getProviders().contains(provider.id()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void tearDown() {
|
||||||
|
providerRegistry.unregister(provider);
|
||||||
|
assertFalse("provider should not be registered",
|
||||||
|
providerRegistry.getProviders().contains(provider.id()));
|
||||||
|
mgr.removeListener(listener);
|
||||||
|
mgr.deactivate();
|
||||||
|
mgr.eventDispatcher = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests group service north bound and south bound interfaces.
|
||||||
|
* The following operations are tested:
|
||||||
|
* a)Tests group creation before the device group AUDIT completes
|
||||||
|
* b)Tests initial device group AUDIT process
|
||||||
|
* c)Tests deletion process of any extraneous groups
|
||||||
|
* d)Tests execution of any pending group creation requests
|
||||||
|
* after the device group AUDIT completes
|
||||||
|
* e)Tests re-apply process of any missing groups
|
||||||
|
* f)Tests event notifications after receiving confirmation for
|
||||||
|
* any operations from data plane
|
||||||
|
* g)Tests group bucket modifications (additions and deletions)
|
||||||
|
* h)Tests group deletion
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testGroupService() {
|
||||||
|
PortNumber[] ports1 = {PortNumber.portNumber(31),
|
||||||
|
PortNumber.portNumber(32)};
|
||||||
|
PortNumber[] ports2 = {PortNumber.portNumber(41),
|
||||||
|
PortNumber.portNumber(42)};
|
||||||
|
// Test Group creation before AUDIT process
|
||||||
|
TestGroupKey key = new TestGroupKey("group1BeforeAudit");
|
||||||
|
List<GroupBucket> buckets = new ArrayList<GroupBucket>();
|
||||||
|
List<PortNumber> outPorts = new ArrayList<PortNumber>();
|
||||||
|
outPorts.addAll(Arrays.asList(ports1));
|
||||||
|
outPorts.addAll(Arrays.asList(ports2));
|
||||||
|
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 newGroupDesc = new DefaultGroupDescription(DID,
|
||||||
|
Group.Type.SELECT,
|
||||||
|
groupBuckets,
|
||||||
|
key,
|
||||||
|
appId);
|
||||||
|
groupService.addGroup(newGroupDesc);
|
||||||
|
internalProvider.validate(DID, null);
|
||||||
|
assertEquals(null, groupService.getGroup(DID, key));
|
||||||
|
assertEquals(0, Iterables.size(groupService.getGroups(DID, appId)));
|
||||||
|
|
||||||
|
// Test initial group audit process
|
||||||
|
GroupId gId1 = new DefaultGroupId(1);
|
||||||
|
Group group1 = createSouthboundGroupEntry(gId1,
|
||||||
|
Arrays.asList(ports1),
|
||||||
|
0);
|
||||||
|
GroupId gId2 = new DefaultGroupId(2);
|
||||||
|
// Non zero reference count will make the group manager to queue
|
||||||
|
// the extraneous groups until reference count is zero.
|
||||||
|
Group group2 = createSouthboundGroupEntry(gId2,
|
||||||
|
Arrays.asList(ports2),
|
||||||
|
2);
|
||||||
|
List<Group> groupEntries = Arrays.asList(group1, group2);
|
||||||
|
providerService.pushGroupMetrics(DID, groupEntries);
|
||||||
|
// First group metrics would trigger the device audit completion
|
||||||
|
// post which all pending group requests are also executed.
|
||||||
|
Group createdGroup = groupService.getGroup(DID, key);
|
||||||
|
int createdGroupId = createdGroup.id().id();
|
||||||
|
assertNotEquals(gId1.id(), createdGroupId);
|
||||||
|
assertNotEquals(gId2.id(), createdGroupId);
|
||||||
|
List<GroupOperation> expectedGroupOps = Arrays.asList(
|
||||||
|
GroupOperation.createDeleteGroupOperation(gId1,
|
||||||
|
Group.Type.SELECT),
|
||||||
|
GroupOperation.createAddGroupOperation(
|
||||||
|
createdGroup.id(),
|
||||||
|
Group.Type.SELECT,
|
||||||
|
groupBuckets));
|
||||||
|
internalProvider.validate(DID, expectedGroupOps);
|
||||||
|
|
||||||
|
group1 = createSouthboundGroupEntry(gId1,
|
||||||
|
Arrays.asList(ports1),
|
||||||
|
0);
|
||||||
|
group2 = createSouthboundGroupEntry(gId2,
|
||||||
|
Arrays.asList(ports2),
|
||||||
|
0);
|
||||||
|
groupEntries = Arrays.asList(group1, group2);
|
||||||
|
providerService.pushGroupMetrics(DID, groupEntries);
|
||||||
|
expectedGroupOps = Arrays.asList(
|
||||||
|
GroupOperation.createDeleteGroupOperation(gId1,
|
||||||
|
Group.Type.SELECT),
|
||||||
|
GroupOperation.createDeleteGroupOperation(gId2,
|
||||||
|
Group.Type.SELECT),
|
||||||
|
GroupOperation.createAddGroupOperation(createdGroup.id(),
|
||||||
|
Group.Type.SELECT,
|
||||||
|
groupBuckets));
|
||||||
|
internalProvider.validate(DID, expectedGroupOps);
|
||||||
|
|
||||||
|
createdGroup = new DefaultGroup(createdGroup.id(),
|
||||||
|
DID,
|
||||||
|
Group.Type.SELECT,
|
||||||
|
groupBuckets);
|
||||||
|
groupEntries = Arrays.asList(createdGroup);
|
||||||
|
providerService.pushGroupMetrics(DID, groupEntries);
|
||||||
|
internalListener.validateEvent(Arrays.asList(GroupEvent.Type.GROUP_ADDED));
|
||||||
|
|
||||||
|
// Test group add bucket operations
|
||||||
|
TestGroupKey addKey = new TestGroupKey("group1AddBuckets");
|
||||||
|
PortNumber[] addPorts = {PortNumber.portNumber(51),
|
||||||
|
PortNumber.portNumber(52)};
|
||||||
|
outPorts.clear();
|
||||||
|
outPorts.addAll(Arrays.asList(addPorts));
|
||||||
|
List<GroupBucket> addBuckets = 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);
|
||||||
|
addBuckets.add(DefaultGroupBucket.createSelectGroupBucket(
|
||||||
|
tBuilder.build()));
|
||||||
|
buckets.add(DefaultGroupBucket.createSelectGroupBucket(
|
||||||
|
tBuilder.build()));
|
||||||
|
}
|
||||||
|
GroupBuckets groupAddBuckets = new GroupBuckets(addBuckets);
|
||||||
|
groupService.addBucketsToGroup(DID,
|
||||||
|
key,
|
||||||
|
groupAddBuckets,
|
||||||
|
addKey,
|
||||||
|
appId);
|
||||||
|
GroupBuckets updatedBuckets = new GroupBuckets(buckets);
|
||||||
|
expectedGroupOps = Arrays.asList(
|
||||||
|
GroupOperation.createModifyGroupOperation(createdGroup.id(),
|
||||||
|
Group.Type.SELECT,
|
||||||
|
updatedBuckets));
|
||||||
|
internalProvider.validate(DID, expectedGroupOps);
|
||||||
|
Group existingGroup = groupService.getGroup(DID, addKey);
|
||||||
|
groupEntries = Arrays.asList(existingGroup);
|
||||||
|
providerService.pushGroupMetrics(DID, groupEntries);
|
||||||
|
internalListener.validateEvent(Arrays.asList(GroupEvent.Type.GROUP_UPDATED));
|
||||||
|
|
||||||
|
// Test group remove bucket operations
|
||||||
|
TestGroupKey removeKey = new TestGroupKey("group1RemoveBuckets");
|
||||||
|
PortNumber[] removePorts = {PortNumber.portNumber(31),
|
||||||
|
PortNumber.portNumber(32)};
|
||||||
|
outPorts.clear();
|
||||||
|
outPorts.addAll(Arrays.asList(removePorts));
|
||||||
|
List<GroupBucket> removeBuckets = 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);
|
||||||
|
removeBuckets.add(DefaultGroupBucket.createSelectGroupBucket(
|
||||||
|
tBuilder.build()));
|
||||||
|
buckets.remove(DefaultGroupBucket.createSelectGroupBucket(
|
||||||
|
tBuilder.build()));
|
||||||
|
}
|
||||||
|
GroupBuckets groupRemoveBuckets = new GroupBuckets(removeBuckets);
|
||||||
|
groupService.removeBucketsFromGroup(DID,
|
||||||
|
addKey,
|
||||||
|
groupRemoveBuckets,
|
||||||
|
removeKey,
|
||||||
|
appId);
|
||||||
|
updatedBuckets = new GroupBuckets(buckets);
|
||||||
|
expectedGroupOps = Arrays.asList(
|
||||||
|
GroupOperation.createModifyGroupOperation(createdGroup.id(),
|
||||||
|
Group.Type.SELECT,
|
||||||
|
updatedBuckets));
|
||||||
|
internalProvider.validate(DID, expectedGroupOps);
|
||||||
|
existingGroup = groupService.getGroup(DID, removeKey);
|
||||||
|
groupEntries = Arrays.asList(existingGroup);
|
||||||
|
providerService.pushGroupMetrics(DID, groupEntries);
|
||||||
|
internalListener.validateEvent(Arrays.asList(GroupEvent.Type.GROUP_UPDATED));
|
||||||
|
|
||||||
|
// Test group remove operations
|
||||||
|
groupService.removeGroup(DID, removeKey, appId);
|
||||||
|
expectedGroupOps = Arrays.asList(
|
||||||
|
GroupOperation.createDeleteGroupOperation(createdGroup.id(),
|
||||||
|
Group.Type.SELECT));
|
||||||
|
internalProvider.validate(DID, expectedGroupOps);
|
||||||
|
groupEntries = Collections.emptyList();
|
||||||
|
providerService.pushGroupMetrics(DID, groupEntries);
|
||||||
|
internalListener.validateEvent(Arrays.asList(GroupEvent.Type.GROUP_REMOVED));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Group createSouthboundGroupEntry(GroupId gId,
|
||||||
|
List<PortNumber> ports,
|
||||||
|
long referenceCount) {
|
||||||
|
List<PortNumber> outPorts = new ArrayList<PortNumber>();
|
||||||
|
outPorts.addAll(ports);
|
||||||
|
|
||||||
|
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);
|
||||||
|
StoredGroupEntry group = new DefaultGroup(
|
||||||
|
gId, DID, Group.Type.SELECT, groupBuckets);
|
||||||
|
group.setReferenceCount(referenceCount);
|
||||||
|
return group;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class TestGroupListener implements GroupListener {
|
||||||
|
final List<GroupEvent> events = new ArrayList<>();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void event(GroupEvent event) {
|
||||||
|
events.add(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void validateEvent(List<GroupEvent.Type> expectedEvents) {
|
||||||
|
int i = 0;
|
||||||
|
System.err.println("events :" + events);
|
||||||
|
for (GroupEvent e : events) {
|
||||||
|
assertEquals("unexpected event", expectedEvents.get(i), e.type());
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
assertEquals("mispredicted number of events",
|
||||||
|
expectedEvents.size(), events.size());
|
||||||
|
events.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestGroupProvider
|
||||||
|
extends AbstractProvider implements GroupProvider {
|
||||||
|
DeviceId lastDeviceId;
|
||||||
|
List<GroupOperation> groupOperations = new ArrayList<GroupOperation>();
|
||||||
|
|
||||||
|
protected TestGroupProvider(ProviderId id) {
|
||||||
|
super(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void performGroupOperation(DeviceId deviceId,
|
||||||
|
GroupOperations groupOps) {
|
||||||
|
lastDeviceId = deviceId;
|
||||||
|
groupOperations.addAll(groupOps.operations());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void validate(DeviceId expectedDeviceId,
|
||||||
|
List<GroupOperation> expectedGroupOps) {
|
||||||
|
if (expectedGroupOps == null) {
|
||||||
|
assertTrue("events generated", groupOperations.isEmpty());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
assertEquals(lastDeviceId, expectedDeviceId);
|
||||||
|
assertTrue((this.groupOperations.containsAll(expectedGroupOps) &&
|
||||||
|
expectedGroupOps.containsAll(groupOperations)));
|
||||||
|
|
||||||
|
groupOperations.clear();
|
||||||
|
lastDeviceId = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -19,6 +19,7 @@ import static org.apache.commons.lang3.concurrent.ConcurrentUtils.createIfAbsent
|
|||||||
import static org.slf4j.LoggerFactory.getLogger;
|
import static org.slf4j.LoggerFactory.getLogger;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.ConcurrentMap;
|
import java.util.concurrent.ConcurrentMap;
|
||||||
@ -62,11 +63,21 @@ public class SimpleGroupStore
|
|||||||
|
|
||||||
private final Logger log = getLogger(getClass());
|
private final Logger log = getLogger(getClass());
|
||||||
|
|
||||||
|
private final int dummyId = 0xffffffff;
|
||||||
|
private final GroupId dummyGroupId = new DefaultGroupId(dummyId);
|
||||||
|
|
||||||
// inner Map is per device group table
|
// inner Map is per device group table
|
||||||
private final ConcurrentMap<DeviceId, ConcurrentMap<GroupKey, StoredGroupEntry>>
|
private final ConcurrentMap<DeviceId, ConcurrentMap<GroupKey, StoredGroupEntry>>
|
||||||
groupEntriesByKey = new ConcurrentHashMap<>();
|
groupEntriesByKey = new ConcurrentHashMap<>();
|
||||||
private final ConcurrentMap<DeviceId, ConcurrentMap<GroupId, StoredGroupEntry>>
|
private final ConcurrentMap<DeviceId, ConcurrentMap<GroupId, StoredGroupEntry>>
|
||||||
groupEntriesById = new ConcurrentHashMap<>();
|
groupEntriesById = new ConcurrentHashMap<>();
|
||||||
|
private final ConcurrentMap<DeviceId, ConcurrentMap<GroupKey, StoredGroupEntry>>
|
||||||
|
pendingGroupEntriesByKey = new ConcurrentHashMap<>();
|
||||||
|
private final ConcurrentMap<DeviceId, ConcurrentMap<GroupId, Group>>
|
||||||
|
extraneousGroupEntriesById = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
private final HashMap<DeviceId, Boolean> deviceAuditStatus =
|
||||||
|
new HashMap<DeviceId, Boolean>();
|
||||||
|
|
||||||
private final AtomicInteger groupIdGen = new AtomicInteger();
|
private final AtomicInteger groupIdGen = new AtomicInteger();
|
||||||
|
|
||||||
@ -82,14 +93,26 @@ public class SimpleGroupStore
|
|||||||
log.info("Stopped");
|
log.info("Stopped");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static NewConcurrentHashMap<GroupKey, StoredGroupEntry> lazyEmptyGroupKeyTable() {
|
private static NewConcurrentHashMap<GroupKey, StoredGroupEntry>
|
||||||
|
lazyEmptyGroupKeyTable() {
|
||||||
return NewConcurrentHashMap.<GroupKey, StoredGroupEntry>ifNeeded();
|
return NewConcurrentHashMap.<GroupKey, StoredGroupEntry>ifNeeded();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static NewConcurrentHashMap<GroupId, StoredGroupEntry> lazyEmptyGroupIdTable() {
|
private static NewConcurrentHashMap<GroupId, StoredGroupEntry>
|
||||||
|
lazyEmptyGroupIdTable() {
|
||||||
return NewConcurrentHashMap.<GroupId, StoredGroupEntry>ifNeeded();
|
return NewConcurrentHashMap.<GroupId, StoredGroupEntry>ifNeeded();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static NewConcurrentHashMap<GroupKey, StoredGroupEntry>
|
||||||
|
lazyEmptyPendingGroupKeyTable() {
|
||||||
|
return NewConcurrentHashMap.<GroupKey, StoredGroupEntry>ifNeeded();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static NewConcurrentHashMap<GroupId, Group>
|
||||||
|
lazyEmptyExtraneousGroupIdTable() {
|
||||||
|
return NewConcurrentHashMap.<GroupId, Group>ifNeeded();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the group key table for specified device.
|
* Returns the group key table for specified device.
|
||||||
*
|
*
|
||||||
@ -112,6 +135,31 @@ public class SimpleGroupStore
|
|||||||
deviceId, lazyEmptyGroupIdTable());
|
deviceId, lazyEmptyGroupIdTable());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the pending 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>
|
||||||
|
getPendingGroupKeyTable(DeviceId deviceId) {
|
||||||
|
return createIfAbsentUnchecked(pendingGroupEntriesByKey,
|
||||||
|
deviceId, lazyEmptyPendingGroupKeyTable());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the extraneous group id table for specified device.
|
||||||
|
*
|
||||||
|
* @param deviceId identifier of the device
|
||||||
|
* @return Map representing group key table of given device.
|
||||||
|
*/
|
||||||
|
private ConcurrentMap<GroupId, Group>
|
||||||
|
getExtraneousGroupIdTable(DeviceId deviceId) {
|
||||||
|
return createIfAbsentUnchecked(extraneousGroupEntriesById,
|
||||||
|
deviceId,
|
||||||
|
lazyEmptyExtraneousGroupIdTable());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the number of groups for the specified device in the store.
|
* Returns the number of groups for the specified device in the store.
|
||||||
*
|
*
|
||||||
@ -133,20 +181,16 @@ public class SimpleGroupStore
|
|||||||
@Override
|
@Override
|
||||||
public Iterable<Group> getGroups(DeviceId deviceId) {
|
public Iterable<Group> getGroups(DeviceId deviceId) {
|
||||||
// flatten and make iterator unmodifiable
|
// flatten and make iterator unmodifiable
|
||||||
if (groupEntriesByKey.get(deviceId) != null) {
|
return FluentIterable.from(getGroupKeyTable(deviceId).values())
|
||||||
return FluentIterable.from(groupEntriesByKey.get(deviceId).values())
|
.transform(
|
||||||
.transform(
|
new Function<StoredGroupEntry, Group>() {
|
||||||
new Function<StoredGroupEntry, Group>() {
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Group apply(
|
public Group apply(
|
||||||
StoredGroupEntry input) {
|
StoredGroupEntry input) {
|
||||||
return input;
|
return input;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -164,6 +208,30 @@ public class SimpleGroupStore
|
|||||||
null;
|
null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private int getFreeGroupIdValue(DeviceId deviceId) {
|
||||||
|
int freeId = groupIdGen.incrementAndGet();
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
Group existing = (
|
||||||
|
groupEntriesById.get(deviceId) != null) ?
|
||||||
|
groupEntriesById.get(deviceId).get(new DefaultGroupId(freeId)) :
|
||||||
|
null;
|
||||||
|
if (existing == null) {
|
||||||
|
existing = (
|
||||||
|
extraneousGroupEntriesById.get(deviceId) != null) ?
|
||||||
|
extraneousGroupEntriesById.get(deviceId).
|
||||||
|
get(new DefaultGroupId(freeId)) :
|
||||||
|
null;
|
||||||
|
}
|
||||||
|
if (existing != null) {
|
||||||
|
freeId = groupIdGen.incrementAndGet();
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return freeId;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stores a new group entry using the information from group description.
|
* Stores a new group entry using the information from group description.
|
||||||
*
|
*
|
||||||
@ -171,16 +239,32 @@ public class SimpleGroupStore
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void storeGroupDescription(GroupDescription groupDesc) {
|
public void storeGroupDescription(GroupDescription groupDesc) {
|
||||||
/* Check if a group is existing with the same key */
|
// Check if a group is existing with the same key
|
||||||
if (getGroup(groupDesc.deviceId(), groupDesc.appCookie()) != null) {
|
if (getGroup(groupDesc.deviceId(), groupDesc.appCookie()) != null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Get a new group identifier */
|
if (deviceAuditStatus.get(groupDesc.deviceId()) == null) {
|
||||||
GroupId id = new DefaultGroupId(groupIdGen.incrementAndGet());
|
// Device group audit has not completed yet
|
||||||
/* Create a group entry object */
|
// Add this group description to pending group key table
|
||||||
|
// Create a group entry object with Dummy Group ID
|
||||||
|
StoredGroupEntry group = new DefaultGroup(dummyGroupId, groupDesc);
|
||||||
|
group.setState(GroupState.WAITING_AUDIT_COMPLETE);
|
||||||
|
ConcurrentMap<GroupKey, StoredGroupEntry> pendingKeyTable =
|
||||||
|
getPendingGroupKeyTable(groupDesc.deviceId());
|
||||||
|
pendingKeyTable.put(groupDesc.appCookie(), group);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
storeGroupDescriptionInternal(groupDesc);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void storeGroupDescriptionInternal(GroupDescription groupDesc) {
|
||||||
|
// Get a new group identifier
|
||||||
|
GroupId id = new DefaultGroupId(getFreeGroupIdValue(groupDesc.deviceId()));
|
||||||
|
// Create a group entry object
|
||||||
StoredGroupEntry group = new DefaultGroup(id, groupDesc);
|
StoredGroupEntry group = new DefaultGroup(id, groupDesc);
|
||||||
/* Insert the newly created group entry into concurrent key and id maps */
|
// Insert the newly created group entry into concurrent key and id maps
|
||||||
ConcurrentMap<GroupKey, StoredGroupEntry> keyTable =
|
ConcurrentMap<GroupKey, StoredGroupEntry> keyTable =
|
||||||
getGroupKeyTable(groupDesc.deviceId());
|
getGroupKeyTable(groupDesc.deviceId());
|
||||||
keyTable.put(groupDesc.appCookie(), group);
|
keyTable.put(groupDesc.appCookie(), group);
|
||||||
@ -198,14 +282,16 @@ public class SimpleGroupStore
|
|||||||
* @param deviceId the device ID
|
* @param deviceId the device ID
|
||||||
* @param oldAppCookie the current group key
|
* @param oldAppCookie the current group key
|
||||||
* @param type update type
|
* @param type update type
|
||||||
* @param newGroupDesc group description with updates
|
* @param newBuckets group buckets for updates
|
||||||
|
* @param newAppCookie optional new group key
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void updateGroupDescription(DeviceId deviceId,
|
public void updateGroupDescription(DeviceId deviceId,
|
||||||
GroupKey oldAppCookie,
|
GroupKey oldAppCookie,
|
||||||
UpdateType type,
|
UpdateType type,
|
||||||
GroupDescription newGroupDesc) {
|
GroupBuckets newBuckets,
|
||||||
/* Check if a group is existing with the provided key */
|
GroupKey newAppCookie) {
|
||||||
|
// Check if a group is existing with the provided key
|
||||||
Group oldGroup = getGroup(deviceId, oldAppCookie);
|
Group oldGroup = getGroup(deviceId, oldAppCookie);
|
||||||
if (oldGroup == null) {
|
if (oldGroup == null) {
|
||||||
return;
|
return;
|
||||||
@ -213,15 +299,16 @@ public class SimpleGroupStore
|
|||||||
|
|
||||||
List<GroupBucket> newBucketList = getUpdatedBucketList(oldGroup,
|
List<GroupBucket> newBucketList = getUpdatedBucketList(oldGroup,
|
||||||
type,
|
type,
|
||||||
newGroupDesc.buckets());
|
newBuckets);
|
||||||
if (newBucketList != null) {
|
if (newBucketList != null) {
|
||||||
/* Create a new group object from the old group */
|
// Create a new group object from the old group
|
||||||
GroupBuckets updatedBuckets = new GroupBuckets(newBucketList);
|
GroupBuckets updatedBuckets = new GroupBuckets(newBucketList);
|
||||||
|
GroupKey newCookie = (newAppCookie != null) ? newAppCookie : oldAppCookie;
|
||||||
GroupDescription updatedGroupDesc = new DefaultGroupDescription(
|
GroupDescription updatedGroupDesc = new DefaultGroupDescription(
|
||||||
oldGroup.deviceId(),
|
oldGroup.deviceId(),
|
||||||
oldGroup.type(),
|
oldGroup.type(),
|
||||||
updatedBuckets,
|
updatedBuckets,
|
||||||
newGroupDesc.appCookie(),
|
newCookie,
|
||||||
oldGroup.appId());
|
oldGroup.appId());
|
||||||
StoredGroupEntry newGroup = new DefaultGroup(oldGroup.id(),
|
StoredGroupEntry newGroup = new DefaultGroup(oldGroup.id(),
|
||||||
updatedGroupDesc);
|
updatedGroupDesc);
|
||||||
@ -229,9 +316,7 @@ public class SimpleGroupStore
|
|||||||
newGroup.setLife(oldGroup.life());
|
newGroup.setLife(oldGroup.life());
|
||||||
newGroup.setPackets(oldGroup.packets());
|
newGroup.setPackets(oldGroup.packets());
|
||||||
newGroup.setBytes(oldGroup.bytes());
|
newGroup.setBytes(oldGroup.bytes());
|
||||||
/* Remove the old entry from maps and add new entry
|
// Remove the old entry from maps and add new entry using new key
|
||||||
* using new key
|
|
||||||
*/
|
|
||||||
ConcurrentMap<GroupKey, StoredGroupEntry> keyTable =
|
ConcurrentMap<GroupKey, StoredGroupEntry> keyTable =
|
||||||
getGroupKeyTable(oldGroup.deviceId());
|
getGroupKeyTable(oldGroup.deviceId());
|
||||||
ConcurrentMap<GroupId, StoredGroupEntry> idTable =
|
ConcurrentMap<GroupId, StoredGroupEntry> idTable =
|
||||||
@ -253,9 +338,8 @@ public class SimpleGroupStore
|
|||||||
boolean groupDescUpdated = false;
|
boolean groupDescUpdated = false;
|
||||||
|
|
||||||
if (type == UpdateType.ADD) {
|
if (type == UpdateType.ADD) {
|
||||||
/* Check if the any of the new buckets are part of the
|
// Check if the any of the new buckets are part of
|
||||||
* old bucket list
|
// the old bucket list
|
||||||
*/
|
|
||||||
for (GroupBucket addBucket:buckets.buckets()) {
|
for (GroupBucket addBucket:buckets.buckets()) {
|
||||||
if (!newBucketList.contains(addBucket)) {
|
if (!newBucketList.contains(addBucket)) {
|
||||||
newBucketList.add(addBucket);
|
newBucketList.add(addBucket);
|
||||||
@ -263,9 +347,8 @@ public class SimpleGroupStore
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (type == UpdateType.REMOVE) {
|
} else if (type == UpdateType.REMOVE) {
|
||||||
/* Check if the to be removed buckets are part of the
|
// Check if the to be removed buckets are part of the
|
||||||
* old bucket list
|
// old bucket list
|
||||||
*/
|
|
||||||
for (GroupBucket removeBucket:buckets.buckets()) {
|
for (GroupBucket removeBucket:buckets.buckets()) {
|
||||||
if (newBucketList.contains(removeBucket)) {
|
if (newBucketList.contains(removeBucket)) {
|
||||||
newBucketList.remove(removeBucket);
|
newBucketList.remove(removeBucket);
|
||||||
@ -290,7 +373,7 @@ public class SimpleGroupStore
|
|||||||
@Override
|
@Override
|
||||||
public void deleteGroupDescription(DeviceId deviceId,
|
public void deleteGroupDescription(DeviceId deviceId,
|
||||||
GroupKey appCookie) {
|
GroupKey appCookie) {
|
||||||
/* Check if a group is existing with the provided key */
|
// Check if a group is existing with the provided key
|
||||||
StoredGroupEntry existing = (groupEntriesByKey.get(deviceId) != null) ?
|
StoredGroupEntry existing = (groupEntriesByKey.get(deviceId) != null) ?
|
||||||
groupEntriesByKey.get(deviceId).get(appCookie) :
|
groupEntriesByKey.get(deviceId).get(appCookie) :
|
||||||
null;
|
null;
|
||||||
@ -362,4 +445,56 @@ public class SimpleGroupStore
|
|||||||
notifyDelegate(new GroupEvent(Type.GROUP_REMOVED, existing));
|
notifyDelegate(new GroupEvent(Type.GROUP_REMOVED, existing));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void deviceInitialAuditCompleted(DeviceId deviceId) {
|
||||||
|
synchronized (deviceAuditStatus) {
|
||||||
|
deviceAuditStatus.putIfAbsent(deviceId, true);
|
||||||
|
// Execute all pending group requests
|
||||||
|
ConcurrentMap<GroupKey, StoredGroupEntry> pendingGroupRequests =
|
||||||
|
getPendingGroupKeyTable(deviceId);
|
||||||
|
for (Group group:pendingGroupRequests.values()) {
|
||||||
|
GroupDescription tmp = new DefaultGroupDescription(
|
||||||
|
group.deviceId(),
|
||||||
|
group.type(),
|
||||||
|
group.buckets(),
|
||||||
|
group.appCookie(),
|
||||||
|
group.appId());
|
||||||
|
storeGroupDescriptionInternal(tmp);
|
||||||
|
}
|
||||||
|
getPendingGroupKeyTable(deviceId).clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean deviceInitialAuditStatus(DeviceId deviceId) {
|
||||||
|
synchronized (deviceAuditStatus) {
|
||||||
|
return (deviceAuditStatus.get(deviceId) != null) ? true : false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addOrUpdateExtraneousGroupEntry(Group group) {
|
||||||
|
ConcurrentMap<GroupId, Group> extraneousIdTable =
|
||||||
|
getExtraneousGroupIdTable(group.deviceId());
|
||||||
|
extraneousIdTable.put(group.id(), group);
|
||||||
|
// Check the reference counter
|
||||||
|
if (group.referenceCount() == 0) {
|
||||||
|
notifyDelegate(new GroupEvent(Type.GROUP_REMOVE_REQUESTED, group));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeExtraneousGroupEntry(Group group) {
|
||||||
|
ConcurrentMap<GroupId, Group> extraneousIdTable =
|
||||||
|
getExtraneousGroupIdTable(group.deviceId());
|
||||||
|
extraneousIdTable.remove(group.id());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterable<Group> getExtraneousGroups(DeviceId deviceId) {
|
||||||
|
// flatten and make iterator unmodifiable
|
||||||
|
return FluentIterable.from(
|
||||||
|
getExtraneousGroupIdTable(deviceId).values());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,8 +40,10 @@ import org.onosproject.net.group.GroupBuckets;
|
|||||||
import org.onosproject.net.group.GroupDescription;
|
import org.onosproject.net.group.GroupDescription;
|
||||||
import org.onosproject.net.group.GroupEvent;
|
import org.onosproject.net.group.GroupEvent;
|
||||||
import org.onosproject.net.group.GroupKey;
|
import org.onosproject.net.group.GroupKey;
|
||||||
import org.onosproject.net.group.GroupStoreDelegate;
|
|
||||||
import org.onosproject.net.group.GroupStore.UpdateType;
|
import org.onosproject.net.group.GroupStore.UpdateType;
|
||||||
|
import org.onosproject.net.group.GroupStoreDelegate;
|
||||||
|
|
||||||
|
import com.google.common.collect.Iterables;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test of the simple DeviceStore implementation.
|
* Test of the simple DeviceStore implementation.
|
||||||
@ -135,8 +137,24 @@ public class SimpleGroupStoreTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests group store operations. The following operations are tested:
|
||||||
|
* a)Tests device group audit completion status change
|
||||||
|
* b)Tests storeGroup operation
|
||||||
|
* c)Tests getGroupCount operation
|
||||||
|
* d)Tests getGroup operation
|
||||||
|
* e)Tests getGroups operation
|
||||||
|
* f)Tests addOrUpdateGroupEntry operation from southbound
|
||||||
|
* g)Tests updateGroupDescription for ADD operation from northbound
|
||||||
|
* h)Tests updateGroupDescription for REMOVE operation from northbound
|
||||||
|
* i)Tests deleteGroupDescription operation from northbound
|
||||||
|
* j)Tests removeGroupEntry operation from southbound
|
||||||
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testGroupStoreOperations() {
|
public void testGroupStoreOperations() {
|
||||||
|
// Set the Device AUDIT completed in the store
|
||||||
|
simpleGroupStore.deviceInitialAuditCompleted(D1);
|
||||||
|
|
||||||
ApplicationId appId =
|
ApplicationId appId =
|
||||||
new DefaultApplicationId(2, "org.groupstore.test");
|
new DefaultApplicationId(2, "org.groupstore.test");
|
||||||
TestGroupKey key = new TestGroupKey("group1");
|
TestGroupKey key = new TestGroupKey("group1");
|
||||||
@ -169,17 +187,17 @@ public class SimpleGroupStoreTest {
|
|||||||
groupBuckets,
|
groupBuckets,
|
||||||
GroupEvent.Type.GROUP_ADD_REQUESTED);
|
GroupEvent.Type.GROUP_ADD_REQUESTED);
|
||||||
simpleGroupStore.setDelegate(checkStoreGroupDelegate);
|
simpleGroupStore.setDelegate(checkStoreGroupDelegate);
|
||||||
/* Testing storeGroup operation */
|
// Testing storeGroup operation
|
||||||
simpleGroupStore.storeGroupDescription(groupDesc);
|
simpleGroupStore.storeGroupDescription(groupDesc);
|
||||||
|
|
||||||
/* Testing getGroupCount operation */
|
// Testing getGroupCount operation
|
||||||
assertEquals(1, simpleGroupStore.getGroupCount(D1));
|
assertEquals(1, simpleGroupStore.getGroupCount(D1));
|
||||||
|
|
||||||
/* Testing getGroup operation */
|
// Testing getGroup operation
|
||||||
Group createdGroup = simpleGroupStore.getGroup(D1, key);
|
Group createdGroup = simpleGroupStore.getGroup(D1, key);
|
||||||
checkStoreGroupDelegate.verifyGroupId(createdGroup.id());
|
checkStoreGroupDelegate.verifyGroupId(createdGroup.id());
|
||||||
|
|
||||||
/* Testing getGroups operation */
|
// Testing getGroups operation
|
||||||
Iterable<Group> createdGroups = simpleGroupStore.getGroups(D1);
|
Iterable<Group> createdGroups = simpleGroupStore.getGroups(D1);
|
||||||
int groupCount = 0;
|
int groupCount = 0;
|
||||||
for (Group group:createdGroups) {
|
for (Group group:createdGroups) {
|
||||||
@ -189,7 +207,7 @@ public class SimpleGroupStoreTest {
|
|||||||
assertEquals(1, groupCount);
|
assertEquals(1, groupCount);
|
||||||
simpleGroupStore.unsetDelegate(checkStoreGroupDelegate);
|
simpleGroupStore.unsetDelegate(checkStoreGroupDelegate);
|
||||||
|
|
||||||
/* Testing addOrUpdateGroupEntry operation from southbound */
|
// Testing addOrUpdateGroupEntry operation from southbound
|
||||||
InternalGroupStoreDelegate addGroupEntryDelegate =
|
InternalGroupStoreDelegate addGroupEntryDelegate =
|
||||||
new InternalGroupStoreDelegate(key,
|
new InternalGroupStoreDelegate(key,
|
||||||
groupBuckets,
|
groupBuckets,
|
||||||
@ -198,7 +216,7 @@ public class SimpleGroupStoreTest {
|
|||||||
simpleGroupStore.addOrUpdateGroupEntry(createdGroup);
|
simpleGroupStore.addOrUpdateGroupEntry(createdGroup);
|
||||||
simpleGroupStore.unsetDelegate(addGroupEntryDelegate);
|
simpleGroupStore.unsetDelegate(addGroupEntryDelegate);
|
||||||
|
|
||||||
/* Testing updateGroupDescription for ADD operation from northbound */
|
// Testing updateGroupDescription for ADD operation from northbound
|
||||||
TestGroupKey addKey = new TestGroupKey("group1AddBuckets");
|
TestGroupKey addKey = new TestGroupKey("group1AddBuckets");
|
||||||
PortNumber[] newNeighborPorts = {PortNumber.portNumber(41),
|
PortNumber[] newNeighborPorts = {PortNumber.portNumber(41),
|
||||||
PortNumber.portNumber(42)};
|
PortNumber.portNumber(42)};
|
||||||
@ -225,19 +243,14 @@ public class SimpleGroupStoreTest {
|
|||||||
updatedGroupBuckets,
|
updatedGroupBuckets,
|
||||||
GroupEvent.Type.GROUP_UPDATE_REQUESTED);
|
GroupEvent.Type.GROUP_UPDATE_REQUESTED);
|
||||||
simpleGroupStore.setDelegate(updateGroupDescDelegate);
|
simpleGroupStore.setDelegate(updateGroupDescDelegate);
|
||||||
GroupDescription newGroupDesc = new DefaultGroupDescription(
|
|
||||||
D1,
|
|
||||||
Group.Type.SELECT,
|
|
||||||
toAddGroupBuckets,
|
|
||||||
addKey,
|
|
||||||
appId);
|
|
||||||
simpleGroupStore.updateGroupDescription(D1,
|
simpleGroupStore.updateGroupDescription(D1,
|
||||||
key,
|
key,
|
||||||
UpdateType.ADD,
|
UpdateType.ADD,
|
||||||
newGroupDesc);
|
toAddGroupBuckets,
|
||||||
|
addKey);
|
||||||
simpleGroupStore.unsetDelegate(updateGroupDescDelegate);
|
simpleGroupStore.unsetDelegate(updateGroupDescDelegate);
|
||||||
|
|
||||||
/* Testing updateGroupDescription for REMOVE operation from northbound */
|
// Testing updateGroupDescription for REMOVE operation from northbound
|
||||||
TestGroupKey removeKey = new TestGroupKey("group1RemoveBuckets");
|
TestGroupKey removeKey = new TestGroupKey("group1RemoveBuckets");
|
||||||
List<GroupBucket> toRemoveBuckets = new ArrayList<GroupBucket>();
|
List<GroupBucket> toRemoveBuckets = new ArrayList<GroupBucket>();
|
||||||
toRemoveBuckets.add(updatedGroupBuckets.buckets().get(0));
|
toRemoveBuckets.add(updatedGroupBuckets.buckets().get(0));
|
||||||
@ -252,23 +265,18 @@ public class SimpleGroupStoreTest {
|
|||||||
remainingGroupBuckets,
|
remainingGroupBuckets,
|
||||||
GroupEvent.Type.GROUP_UPDATE_REQUESTED);
|
GroupEvent.Type.GROUP_UPDATE_REQUESTED);
|
||||||
simpleGroupStore.setDelegate(removeGroupDescDelegate);
|
simpleGroupStore.setDelegate(removeGroupDescDelegate);
|
||||||
GroupDescription removeGroupDesc = new DefaultGroupDescription(
|
|
||||||
D1,
|
|
||||||
Group.Type.SELECT,
|
|
||||||
toRemoveGroupBuckets,
|
|
||||||
removeKey,
|
|
||||||
appId);
|
|
||||||
simpleGroupStore.updateGroupDescription(D1,
|
simpleGroupStore.updateGroupDescription(D1,
|
||||||
addKey,
|
addKey,
|
||||||
UpdateType.REMOVE,
|
UpdateType.REMOVE,
|
||||||
removeGroupDesc);
|
toRemoveGroupBuckets,
|
||||||
|
removeKey);
|
||||||
simpleGroupStore.unsetDelegate(removeGroupDescDelegate);
|
simpleGroupStore.unsetDelegate(removeGroupDescDelegate);
|
||||||
|
|
||||||
/* Testing getGroup operation */
|
// Testing getGroup operation
|
||||||
Group existingGroup = simpleGroupStore.getGroup(D1, removeKey);
|
Group existingGroup = simpleGroupStore.getGroup(D1, removeKey);
|
||||||
checkStoreGroupDelegate.verifyGroupId(existingGroup.id());
|
checkStoreGroupDelegate.verifyGroupId(existingGroup.id());
|
||||||
|
|
||||||
/* Testing addOrUpdateGroupEntry operation from southbound */
|
// Testing addOrUpdateGroupEntry operation from southbound
|
||||||
InternalGroupStoreDelegate updateGroupEntryDelegate =
|
InternalGroupStoreDelegate updateGroupEntryDelegate =
|
||||||
new InternalGroupStoreDelegate(removeKey,
|
new InternalGroupStoreDelegate(removeKey,
|
||||||
remainingGroupBuckets,
|
remainingGroupBuckets,
|
||||||
@ -277,7 +285,7 @@ public class SimpleGroupStoreTest {
|
|||||||
simpleGroupStore.addOrUpdateGroupEntry(existingGroup);
|
simpleGroupStore.addOrUpdateGroupEntry(existingGroup);
|
||||||
simpleGroupStore.unsetDelegate(updateGroupEntryDelegate);
|
simpleGroupStore.unsetDelegate(updateGroupEntryDelegate);
|
||||||
|
|
||||||
/* Testing deleteGroupDescription operation from northbound */
|
// Testing deleteGroupDescription operation from northbound
|
||||||
InternalGroupStoreDelegate deleteGroupDescDelegate =
|
InternalGroupStoreDelegate deleteGroupDescDelegate =
|
||||||
new InternalGroupStoreDelegate(removeKey,
|
new InternalGroupStoreDelegate(removeKey,
|
||||||
remainingGroupBuckets,
|
remainingGroupBuckets,
|
||||||
@ -286,7 +294,7 @@ public class SimpleGroupStoreTest {
|
|||||||
simpleGroupStore.deleteGroupDescription(D1, removeKey);
|
simpleGroupStore.deleteGroupDescription(D1, removeKey);
|
||||||
simpleGroupStore.unsetDelegate(deleteGroupDescDelegate);
|
simpleGroupStore.unsetDelegate(deleteGroupDescDelegate);
|
||||||
|
|
||||||
/* Testing removeGroupEntry operation from southbound */
|
// Testing removeGroupEntry operation from southbound
|
||||||
InternalGroupStoreDelegate removeGroupEntryDelegate =
|
InternalGroupStoreDelegate removeGroupEntryDelegate =
|
||||||
new InternalGroupStoreDelegate(removeKey,
|
new InternalGroupStoreDelegate(removeKey,
|
||||||
remainingGroupBuckets,
|
remainingGroupBuckets,
|
||||||
@ -294,17 +302,10 @@ public class SimpleGroupStoreTest {
|
|||||||
simpleGroupStore.setDelegate(removeGroupEntryDelegate);
|
simpleGroupStore.setDelegate(removeGroupEntryDelegate);
|
||||||
simpleGroupStore.removeGroupEntry(existingGroup);
|
simpleGroupStore.removeGroupEntry(existingGroup);
|
||||||
|
|
||||||
/* Testing getGroup operation */
|
// Testing getGroup operation
|
||||||
existingGroup = simpleGroupStore.getGroup(D1, removeKey);
|
existingGroup = simpleGroupStore.getGroup(D1, removeKey);
|
||||||
assertEquals(null, existingGroup);
|
assertEquals(null, existingGroup);
|
||||||
Iterable<Group> existingGroups = simpleGroupStore.getGroups(D1);
|
assertEquals(0, Iterables.size(simpleGroupStore.getGroups(D1)));
|
||||||
groupCount = 0;
|
|
||||||
for (Group tmp:existingGroups) {
|
|
||||||
/* To avoid warning */
|
|
||||||
assertEquals(null, tmp);
|
|
||||||
groupCount++;
|
|
||||||
}
|
|
||||||
assertEquals(0, groupCount);
|
|
||||||
assertEquals(0, simpleGroupStore.getGroupCount(D1));
|
assertEquals(0, simpleGroupStore.getGroupCount(D1));
|
||||||
|
|
||||||
simpleGroupStore.unsetDelegate(removeGroupEntryDelegate);
|
simpleGroupStore.unsetDelegate(removeGroupEntryDelegate);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user