mirror of
https://github.com/opennetworkinglab/onos.git
synced 2025-10-22 21:01:00 +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
|
||||
public int id() {
|
||||
return 0;
|
||||
return this.id;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -17,7 +17,10 @@ package org.onosproject.net.group;
|
||||
|
||||
import static org.slf4j.LoggerFactory.getLogger;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import org.onosproject.core.GroupId;
|
||||
import org.onosproject.net.DeviceId;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
/**
|
||||
@ -32,6 +35,7 @@ public class DefaultGroup extends DefaultGroupDescription
|
||||
private long life;
|
||||
private long packets;
|
||||
private long bytes;
|
||||
private long referenceCount;
|
||||
private GroupId id;
|
||||
|
||||
/**
|
||||
@ -47,6 +51,29 @@ public class DefaultGroup extends DefaultGroupDescription
|
||||
this.life = 0;
|
||||
this.packets = 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;
|
||||
}
|
||||
|
||||
@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.checkNotNull;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import org.onosproject.core.GroupId;
|
||||
import org.onosproject.net.PortNumber;
|
||||
import org.onosproject.net.flow.TrafficTreatment;
|
||||
@ -178,4 +180,36 @@ public final class DefaultGroupBucket implements GroupBucket {
|
||||
public GroupId 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 java.util.Objects;
|
||||
|
||||
import org.onosproject.core.ApplicationId;
|
||||
import org.onosproject.net.DeviceId;
|
||||
|
||||
@ -49,8 +51,8 @@ public class DefaultGroupDescription implements GroupDescription {
|
||||
this.type = checkNotNull(type);
|
||||
this.deviceId = checkNotNull(deviceId);
|
||||
this.buckets = checkNotNull(buckets);
|
||||
this.appCookie = checkNotNull(appCookie);
|
||||
this.appId = checkNotNull(appId);
|
||||
this.appCookie = appCookie;
|
||||
this.appId = appId;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -61,11 +63,27 @@ public class DefaultGroupDescription implements GroupDescription {
|
||||
*
|
||||
*/
|
||||
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());
|
||||
this.type = groupDesc.type();
|
||||
this.deviceId = groupDesc.deviceId();
|
||||
this.buckets = groupDesc.buckets();
|
||||
this.appCookie = groupDesc.appCookie();
|
||||
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;
|
||||
}
|
||||
|
||||
@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.
|
||||
*/
|
||||
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
|
||||
* received the confirmation from data plane.
|
||||
@ -81,4 +85,11 @@ public interface Group extends GroupDescription {
|
||||
* @return number of 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;
|
||||
}
|
||||
|
||||
@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 java.util.Objects;
|
||||
|
||||
import org.onosproject.core.GroupId;
|
||||
|
||||
/**
|
||||
@ -142,4 +144,37 @@ public final class GroupOperation {
|
||||
public GroupBuckets 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;
|
||||
|
||||
import org.onosproject.core.ApplicationId;
|
||||
import org.onosproject.net.Device;
|
||||
import org.onosproject.net.DeviceId;
|
||||
|
||||
/**
|
||||
@ -27,7 +26,7 @@ import org.onosproject.net.DeviceId;
|
||||
* specified in a group.
|
||||
* "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
|
||||
* 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
|
||||
* 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
|
||||
* guarantee that the "group" is actually created in device.
|
||||
* GROUP_ADDED notification would confirm the creation of
|
||||
* this group in data plane
|
||||
* this group in data plane.
|
||||
*
|
||||
* @param deviceId device identifier
|
||||
* @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
|
||||
* 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
|
||||
* 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
|
||||
@ -91,7 +90,7 @@ public interface GroupService {
|
||||
* 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
|
||||
* 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
|
||||
@ -99,7 +98,7 @@ public interface GroupService {
|
||||
* @param newCookie immutable cookie to be used post update operation
|
||||
* @param appId Application Id
|
||||
*/
|
||||
void removeBucketsFromGroup(Device deviceId,
|
||||
void removeBucketsFromGroup(DeviceId deviceId,
|
||||
GroupKey oldCookie,
|
||||
GroupBuckets buckets,
|
||||
GroupKey newCookie,
|
||||
@ -109,13 +108,13 @@ public interface GroupService {
|
||||
* 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
|
||||
* operation on the device.
|
||||
*
|
||||
* @param deviceId device identifier
|
||||
* @param appCookie application cookie to be used for lookup
|
||||
* @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
|
||||
@ -125,7 +124,7 @@ public interface GroupService {
|
||||
* @param appId application id
|
||||
* @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.
|
||||
|
@ -73,12 +73,14 @@ public interface GroupStore extends Store<GroupEvent, GroupStoreDelegate> {
|
||||
* @param deviceId the device ID
|
||||
* @param oldAppCookie the current group key
|
||||
* @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,
|
||||
GroupKey oldAppCookie,
|
||||
UpdateType type,
|
||||
GroupDescription newGroupDesc);
|
||||
GroupBuckets newBuckets,
|
||||
GroupKey newAppCookie);
|
||||
|
||||
/**
|
||||
* Triggers deleting the existing group entry.
|
||||
@ -102,4 +104,43 @@ public interface GroupStore extends Store<GroupEvent, GroupStoreDelegate> {
|
||||
* @param group group entry
|
||||
*/
|
||||
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);
|
||||
|
||||
/**
|
||||
* 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 java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
@ -62,11 +63,21 @@ public class SimpleGroupStore
|
||||
|
||||
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
|
||||
private final ConcurrentMap<DeviceId, ConcurrentMap<GroupKey, StoredGroupEntry>>
|
||||
groupEntriesByKey = new ConcurrentHashMap<>();
|
||||
private final ConcurrentMap<DeviceId, ConcurrentMap<GroupId, StoredGroupEntry>>
|
||||
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();
|
||||
|
||||
@ -82,14 +93,26 @@ public class SimpleGroupStore
|
||||
log.info("Stopped");
|
||||
}
|
||||
|
||||
private static NewConcurrentHashMap<GroupKey, StoredGroupEntry> lazyEmptyGroupKeyTable() {
|
||||
private static NewConcurrentHashMap<GroupKey, StoredGroupEntry>
|
||||
lazyEmptyGroupKeyTable() {
|
||||
return NewConcurrentHashMap.<GroupKey, StoredGroupEntry>ifNeeded();
|
||||
}
|
||||
|
||||
private static NewConcurrentHashMap<GroupId, StoredGroupEntry> lazyEmptyGroupIdTable() {
|
||||
private static NewConcurrentHashMap<GroupId, StoredGroupEntry>
|
||||
lazyEmptyGroupIdTable() {
|
||||
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.
|
||||
*
|
||||
@ -112,6 +135,31 @@ public class SimpleGroupStore
|
||||
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.
|
||||
*
|
||||
@ -133,8 +181,7 @@ public class SimpleGroupStore
|
||||
@Override
|
||||
public Iterable<Group> getGroups(DeviceId deviceId) {
|
||||
// flatten and make iterator unmodifiable
|
||||
if (groupEntriesByKey.get(deviceId) != null) {
|
||||
return FluentIterable.from(groupEntriesByKey.get(deviceId).values())
|
||||
return FluentIterable.from(getGroupKeyTable(deviceId).values())
|
||||
.transform(
|
||||
new Function<StoredGroupEntry, Group>() {
|
||||
|
||||
@ -144,9 +191,6 @@ public class SimpleGroupStore
|
||||
return input;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -164,6 +208,30 @@ public class SimpleGroupStore
|
||||
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.
|
||||
*
|
||||
@ -171,16 +239,32 @@ public class SimpleGroupStore
|
||||
*/
|
||||
@Override
|
||||
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) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Get a new group identifier */
|
||||
GroupId id = new DefaultGroupId(groupIdGen.incrementAndGet());
|
||||
/* Create a group entry object */
|
||||
if (deviceAuditStatus.get(groupDesc.deviceId()) == null) {
|
||||
// Device group audit has not completed yet
|
||||
// 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);
|
||||
/* 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 =
|
||||
getGroupKeyTable(groupDesc.deviceId());
|
||||
keyTable.put(groupDesc.appCookie(), group);
|
||||
@ -198,14 +282,16 @@ public class SimpleGroupStore
|
||||
* @param deviceId the device ID
|
||||
* @param oldAppCookie the current group key
|
||||
* @param type update type
|
||||
* @param newGroupDesc group description with updates
|
||||
* @param newBuckets group buckets for updates
|
||||
* @param newAppCookie optional new group key
|
||||
*/
|
||||
@Override
|
||||
public void updateGroupDescription(DeviceId deviceId,
|
||||
GroupKey oldAppCookie,
|
||||
UpdateType type,
|
||||
GroupDescription newGroupDesc) {
|
||||
/* Check if a group is existing with the provided key */
|
||||
GroupBuckets newBuckets,
|
||||
GroupKey newAppCookie) {
|
||||
// Check if a group is existing with the provided key
|
||||
Group oldGroup = getGroup(deviceId, oldAppCookie);
|
||||
if (oldGroup == null) {
|
||||
return;
|
||||
@ -213,15 +299,16 @@ public class SimpleGroupStore
|
||||
|
||||
List<GroupBucket> newBucketList = getUpdatedBucketList(oldGroup,
|
||||
type,
|
||||
newGroupDesc.buckets());
|
||||
newBuckets);
|
||||
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);
|
||||
GroupKey newCookie = (newAppCookie != null) ? newAppCookie : oldAppCookie;
|
||||
GroupDescription updatedGroupDesc = new DefaultGroupDescription(
|
||||
oldGroup.deviceId(),
|
||||
oldGroup.type(),
|
||||
updatedBuckets,
|
||||
newGroupDesc.appCookie(),
|
||||
newCookie,
|
||||
oldGroup.appId());
|
||||
StoredGroupEntry newGroup = new DefaultGroup(oldGroup.id(),
|
||||
updatedGroupDesc);
|
||||
@ -229,9 +316,7 @@ public class SimpleGroupStore
|
||||
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
|
||||
*/
|
||||
// Remove the old entry from maps and add new entry using new key
|
||||
ConcurrentMap<GroupKey, StoredGroupEntry> keyTable =
|
||||
getGroupKeyTable(oldGroup.deviceId());
|
||||
ConcurrentMap<GroupId, StoredGroupEntry> idTable =
|
||||
@ -253,9 +338,8 @@ public class SimpleGroupStore
|
||||
boolean groupDescUpdated = false;
|
||||
|
||||
if (type == UpdateType.ADD) {
|
||||
/* Check if the any of the new buckets are part of the
|
||||
* old bucket list
|
||||
*/
|
||||
// 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);
|
||||
@ -263,9 +347,8 @@ public class SimpleGroupStore
|
||||
}
|
||||
}
|
||||
} else if (type == UpdateType.REMOVE) {
|
||||
/* Check if the to be removed buckets are part of the
|
||||
* old bucket list
|
||||
*/
|
||||
// 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);
|
||||
@ -290,7 +373,7 @@ public class SimpleGroupStore
|
||||
@Override
|
||||
public void deleteGroupDescription(DeviceId deviceId,
|
||||
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) ?
|
||||
groupEntriesByKey.get(deviceId).get(appCookie) :
|
||||
null;
|
||||
@ -362,4 +445,56 @@ public class SimpleGroupStore
|
||||
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.GroupEvent;
|
||||
import org.onosproject.net.group.GroupKey;
|
||||
import org.onosproject.net.group.GroupStoreDelegate;
|
||||
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.
|
||||
@ -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
|
||||
public void testGroupStoreOperations() {
|
||||
// Set the Device AUDIT completed in the store
|
||||
simpleGroupStore.deviceInitialAuditCompleted(D1);
|
||||
|
||||
ApplicationId appId =
|
||||
new DefaultApplicationId(2, "org.groupstore.test");
|
||||
TestGroupKey key = new TestGroupKey("group1");
|
||||
@ -169,17 +187,17 @@ public class SimpleGroupStoreTest {
|
||||
groupBuckets,
|
||||
GroupEvent.Type.GROUP_ADD_REQUESTED);
|
||||
simpleGroupStore.setDelegate(checkStoreGroupDelegate);
|
||||
/* Testing storeGroup operation */
|
||||
// Testing storeGroup operation
|
||||
simpleGroupStore.storeGroupDescription(groupDesc);
|
||||
|
||||
/* Testing getGroupCount operation */
|
||||
// Testing getGroupCount operation
|
||||
assertEquals(1, simpleGroupStore.getGroupCount(D1));
|
||||
|
||||
/* Testing getGroup operation */
|
||||
// Testing getGroup operation
|
||||
Group createdGroup = simpleGroupStore.getGroup(D1, key);
|
||||
checkStoreGroupDelegate.verifyGroupId(createdGroup.id());
|
||||
|
||||
/* Testing getGroups operation */
|
||||
// Testing getGroups operation
|
||||
Iterable<Group> createdGroups = simpleGroupStore.getGroups(D1);
|
||||
int groupCount = 0;
|
||||
for (Group group:createdGroups) {
|
||||
@ -189,7 +207,7 @@ public class SimpleGroupStoreTest {
|
||||
assertEquals(1, groupCount);
|
||||
simpleGroupStore.unsetDelegate(checkStoreGroupDelegate);
|
||||
|
||||
/* Testing addOrUpdateGroupEntry operation from southbound */
|
||||
// Testing addOrUpdateGroupEntry operation from southbound
|
||||
InternalGroupStoreDelegate addGroupEntryDelegate =
|
||||
new InternalGroupStoreDelegate(key,
|
||||
groupBuckets,
|
||||
@ -198,7 +216,7 @@ public class SimpleGroupStoreTest {
|
||||
simpleGroupStore.addOrUpdateGroupEntry(createdGroup);
|
||||
simpleGroupStore.unsetDelegate(addGroupEntryDelegate);
|
||||
|
||||
/* Testing updateGroupDescription for ADD operation from northbound */
|
||||
// Testing updateGroupDescription for ADD operation from northbound
|
||||
TestGroupKey addKey = new TestGroupKey("group1AddBuckets");
|
||||
PortNumber[] newNeighborPorts = {PortNumber.portNumber(41),
|
||||
PortNumber.portNumber(42)};
|
||||
@ -225,19 +243,14 @@ public class SimpleGroupStoreTest {
|
||||
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);
|
||||
toAddGroupBuckets,
|
||||
addKey);
|
||||
simpleGroupStore.unsetDelegate(updateGroupDescDelegate);
|
||||
|
||||
/* Testing updateGroupDescription for REMOVE operation from northbound */
|
||||
// Testing updateGroupDescription for REMOVE operation from northbound
|
||||
TestGroupKey removeKey = new TestGroupKey("group1RemoveBuckets");
|
||||
List<GroupBucket> toRemoveBuckets = new ArrayList<GroupBucket>();
|
||||
toRemoveBuckets.add(updatedGroupBuckets.buckets().get(0));
|
||||
@ -252,23 +265,18 @@ public class SimpleGroupStoreTest {
|
||||
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);
|
||||
toRemoveGroupBuckets,
|
||||
removeKey);
|
||||
simpleGroupStore.unsetDelegate(removeGroupDescDelegate);
|
||||
|
||||
/* Testing getGroup operation */
|
||||
// Testing getGroup operation
|
||||
Group existingGroup = simpleGroupStore.getGroup(D1, removeKey);
|
||||
checkStoreGroupDelegate.verifyGroupId(existingGroup.id());
|
||||
|
||||
/* Testing addOrUpdateGroupEntry operation from southbound */
|
||||
// Testing addOrUpdateGroupEntry operation from southbound
|
||||
InternalGroupStoreDelegate updateGroupEntryDelegate =
|
||||
new InternalGroupStoreDelegate(removeKey,
|
||||
remainingGroupBuckets,
|
||||
@ -277,7 +285,7 @@ public class SimpleGroupStoreTest {
|
||||
simpleGroupStore.addOrUpdateGroupEntry(existingGroup);
|
||||
simpleGroupStore.unsetDelegate(updateGroupEntryDelegate);
|
||||
|
||||
/* Testing deleteGroupDescription operation from northbound */
|
||||
// Testing deleteGroupDescription operation from northbound
|
||||
InternalGroupStoreDelegate deleteGroupDescDelegate =
|
||||
new InternalGroupStoreDelegate(removeKey,
|
||||
remainingGroupBuckets,
|
||||
@ -286,7 +294,7 @@ public class SimpleGroupStoreTest {
|
||||
simpleGroupStore.deleteGroupDescription(D1, removeKey);
|
||||
simpleGroupStore.unsetDelegate(deleteGroupDescDelegate);
|
||||
|
||||
/* Testing removeGroupEntry operation from southbound */
|
||||
// Testing removeGroupEntry operation from southbound
|
||||
InternalGroupStoreDelegate removeGroupEntryDelegate =
|
||||
new InternalGroupStoreDelegate(removeKey,
|
||||
remainingGroupBuckets,
|
||||
@ -294,17 +302,10 @@ public class SimpleGroupStoreTest {
|
||||
simpleGroupStore.setDelegate(removeGroupEntryDelegate);
|
||||
simpleGroupStore.removeGroupEntry(existingGroup);
|
||||
|
||||
/* Testing getGroup operation */
|
||||
// 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, Iterables.size(simpleGroupStore.getGroups(D1)));
|
||||
assertEquals(0, simpleGroupStore.getGroupCount(D1));
|
||||
|
||||
simpleGroupStore.unsetDelegate(removeGroupEntryDelegate);
|
||||
|
Loading…
x
Reference in New Issue
Block a user