Saurav Das
Committed by Gerrit Code Review

Group handling changes:

      Edge case fix in group store where extraneous group buckets are updated so the group can be reused
      Bug fix for broadcast group in OF-DPA as there can only be one l2flood group per vlan
      Group operation failure codes are now reported by the provider to the core
      Group ids are displayed in hex in a couple of places

Change-Id: Ib48d21aa96e0400b77999a1b45839c28f678e524
......@@ -61,7 +61,7 @@ public class DefaultGroupId implements GroupId {
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("id", id)
.add("id", "0x" + Integer.toHexString(id))
.toString();
}
}
......
......@@ -576,7 +576,7 @@ public final class Instructions {
@Override
public String toString() {
return type().toString() + SEPARATOR + Integer.toHexString(groupId.id());
return type().toString() + SEPARATOR + "0x" + Integer.toHexString(groupId.id());
}
@Override
......
......@@ -31,6 +31,7 @@ public final class GroupOperation {
private final GroupId groupId;
private final GroupDescription.Type groupType;
private final GroupBuckets buckets;
private final GroupMsgErrorCode failureCode;
public enum Type {
/**
......@@ -48,6 +49,28 @@ public final class GroupOperation {
}
/**
* Possible error codes for a failure of a group operation.
*
*/
public enum GroupMsgErrorCode {
GROUP_EXISTS,
INVALID_GROUP,
WEIGHT_UNSUPPORTED,
OUT_OF_GROUPS,
OUT_OF_BUCKETS,
CHAINING_UNSUPPORTED,
WATCH_UNSUPPORTED,
LOOP,
UNKNOWN_GROUP,
CHAINED_GROUP,
BAD_TYPE,
BAD_COMMAND,
BAD_BUCKET,
BAD_WATCH,
EPERM;
}
/**
* Group operation constructor with the parameters.
*
* @param opType group operation type
......@@ -63,6 +86,23 @@ public final class GroupOperation {
this.groupId = checkNotNull(groupId);
this.groupType = checkNotNull(groupType);
this.buckets = buckets;
this.failureCode = null;
}
/**
* Group operation copy-constructor with additional field to set failure code.
* Typically used by provider to return information to the core about
* the failure reason for a group operation.
*
* @param groupOp the original group operation
* @param failureCode failure code for a failed group operation
*/
private GroupOperation(GroupOperation groupOp, GroupMsgErrorCode failureCode) {
this.opType = groupOp.opType;
this.groupId = groupOp.groupId;
this.groupType = groupOp.groupType;
this.buckets = groupOp.buckets;
this.failureCode = failureCode;
}
/**
......@@ -109,6 +149,12 @@ public final class GroupOperation {
}
public static GroupOperation createFailedGroupOperation(GroupOperation groupOperation,
GroupMsgErrorCode failureCode) {
checkNotNull(groupOperation);
return new GroupOperation(groupOperation, failureCode);
}
/**
* Returns group operation type.
*
......@@ -145,6 +191,15 @@ public final class GroupOperation {
return this.buckets;
}
/**
* Returns the failure code representing the failure of a group operation.
*
* @return error code for failure of group operation
*/
public GroupMsgErrorCode failureCode() {
return failureCode;
}
@Override
/*
* The deviceId, type and buckets are used for hash.
......
......@@ -88,7 +88,8 @@ import static org.onlab.util.Tools.groupedThreads;
import static org.slf4j.LoggerFactory.getLogger;
/**
* Manages inventory of group entries using trivial in-memory implementation.
* Manages inventory of group entries using distributed group stores from the
* storage service.
*/
@Component(immediate = true)
@Service
......@@ -450,16 +451,18 @@ public class DistributedGroupStore
matchingExtraneousGroup = getMatchingExtraneousGroupbyId(
groupDesc.deviceId(), groupDesc.givenGroupId());
if (matchingExtraneousGroup != null) {
log.debug("storeGroupDescriptionInternal: Matching extraneous group found in Device {} for group id {}",
log.debug("storeGroupDescriptionInternal: Matching extraneous group "
+ "found in Device {} for group id 0x{}",
groupDesc.deviceId(),
groupDesc.givenGroupId());
Integer.toHexString(groupDesc.givenGroupId()));
//Check if the group buckets matches with user provided buckets
if (matchingExtraneousGroup.buckets().equals(groupDesc.buckets())) {
//Group is already existing with the same buckets and Id
// Create a group entry object
log.debug("storeGroupDescriptionInternal: Buckets also matching in Device {} for group id {}",
log.debug("storeGroupDescriptionInternal: Buckets also matching "
+ "in Device {} for group id 0x{}",
groupDesc.deviceId(),
groupDesc.givenGroupId());
Integer.toHexString(groupDesc.givenGroupId()));
StoredGroupEntry group = new DefaultGroup(
matchingExtraneousGroup.id(), groupDesc);
// Insert the newly created group entry into key and id maps
......@@ -476,10 +479,27 @@ public class DistributedGroupStore
} else {
//Group buckets are not matching. Update group
//with user provided buckets.
//TODO
log.debug("storeGroupDescriptionInternal: Buckets are not matching in Device {} for group id {}",
log.debug("storeGroupDescriptionInternal: Buckets are not "
+ "matching in Device {} for group id 0x{}",
groupDesc.deviceId(),
groupDesc.givenGroupId());
Integer.toHexString(groupDesc.givenGroupId()));
StoredGroupEntry modifiedGroup = new DefaultGroup(
matchingExtraneousGroup.id(), groupDesc);
modifiedGroup.setState(GroupState.PENDING_UPDATE);
getGroupStoreKeyMap().
put(new GroupStoreKeyMapKey(groupDesc.deviceId(),
groupDesc.appCookie()), modifiedGroup);
// Ensure it also inserted into group id based table to
// avoid any chances of duplication in group id generation
getGroupIdTable(groupDesc.deviceId()).
put(matchingExtraneousGroup.id(), modifiedGroup);
removeExtraneousGroupEntry(matchingExtraneousGroup);
log.debug("storeGroupDescriptionInternal: Triggering Group "
+ "UPDATE request for {} in device {}",
matchingExtraneousGroup.id(),
groupDesc.deviceId());
notifyDelegate(new GroupEvent(Type.GROUP_UPDATE_REQUESTED, modifiedGroup));
return;
}
}
} else {
......@@ -754,7 +774,7 @@ public class DistributedGroupStore
GroupEvent event = null;
if (existing != null) {
log.debug("addOrUpdateGroupEntry: updating group entry {} in device {}",
log.trace("addOrUpdateGroupEntry: updating group entry {} in device {}",
group.id(),
group.deviceId());
synchronized (existing) {
......@@ -779,7 +799,7 @@ public class DistributedGroupStore
existing.setBytes(group.bytes());
if ((existing.state() == GroupState.PENDING_ADD) ||
(existing.state() == GroupState.PENDING_ADD_RETRY)) {
log.debug("addOrUpdateGroupEntry: group entry {} in device {} moving from {} to ADDED",
log.trace("addOrUpdateGroupEntry: group entry {} in device {} moving from {} to ADDED",
existing.id(),
existing.deviceId(),
existing.state());
......@@ -787,7 +807,7 @@ public class DistributedGroupStore
existing.setIsGroupStateAddedFirstTime(true);
event = new GroupEvent(Type.GROUP_ADDED, existing);
} else {
log.debug("addOrUpdateGroupEntry: group entry {} in device {} moving from {} to ADDED",
log.trace("addOrUpdateGroupEntry: group entry {} in device {} moving from {} to ADDED",
existing.id(),
existing.deviceId(),
GroupState.PENDING_UPDATE);
......@@ -911,18 +931,19 @@ public class DistributedGroupStore
}
log.warn("groupOperationFailed: group operation {} failed"
+ "for group {} in device {}",
+ "for group {} in device {} with code {}",
operation.opType(),
existing.id(),
existing.deviceId());
existing.deviceId(),
operation.failureCode());
if (operation.failureCode() == GroupOperation.GroupMsgErrorCode.GROUP_EXISTS) {
log.warn("Current extraneous groups in device:{} are: {}",
deviceId,
getExtraneousGroups(deviceId));
}
switch (operation.opType()) {
case ADD:
if (existing.state() == GroupState.PENDING_ADD) {
//TODO: Need to add support for passing the group
//operation failure reason from group provider.
//If the error type is anything other than GROUP_EXISTS,
//then the GROUP_ADD_FAILED event should be raised even
//in PENDING_ADD_RETRY state also.
notifyDelegate(new GroupEvent(Type.GROUP_ADD_FAILED, existing));
log.warn("groupOperationFailed: cleaningup "
+ "group {} from store in device {}....",
......@@ -1220,13 +1241,13 @@ public class DistributedGroupStore
for (Group group : extraneousStoredEntries) {
// there are groups in the extraneous store that
// aren't in the switch
log.debug("Group AUDIT: clearing extransoeus group {} from store for device {}",
log.debug("Group AUDIT: clearing extraneous group {} from store for device {}",
group.id(), deviceId);
removeExtraneousGroupEntry(group);
}
if (!deviceInitialAuditStatus) {
log.debug("Group AUDIT: Setting device {} initial AUDIT completed",
log.info("Group AUDIT: Setting device {} initial AUDIT completed",
deviceId);
deviceInitialAuditCompleted(deviceId, true);
}
......@@ -1266,7 +1287,7 @@ public class DistributedGroupStore
}
private void extraneousGroup(Group group) {
log.debug("Group {} is on device {} but not in store.",
log.trace("Group {} is on device {} but not in store.",
group, group.deviceId());
addOrUpdateExtraneousGroupEntry(group);
}
......
......@@ -556,7 +556,8 @@ public class OFDPA2GroupHandler {
}
// assemble info for l2 flood group
Integer l2floodgroupId = L2_FLOOD_TYPE | (vlanId.toShort() << 16) | nextObj.id();
// since there can be only one flood group for a vlan, its index is always the same - 0
Integer l2floodgroupId = L2_FLOOD_TYPE | (vlanId.toShort() << 16);
int l2floodgk = L2_FLOOD_TYPE | nextObj.id() << 12;
final GroupKey l2floodgroupkey = new DefaultGroupKey(OFDPA2Pipeline.appKryo.serialize(l2floodgk));
// collection of group buckets pointing to all the l2 interface groups
......@@ -1007,7 +1008,7 @@ public class OFDPA2GroupHandler {
if (gceSet != null) {
for (GroupChainElem gce : gceSet) {
log.info("Group service {} group key {} in device {}. "
+ "Processing next group in group chain with group id {}",
+ "Processing next group in group chain with group id 0x{}",
(added) ? "ADDED" : "processed",
key, deviceId,
Integer.toHexString(gce.groupDescription.givenGroupId()));
......@@ -1020,7 +1021,7 @@ public class OFDPA2GroupHandler {
pendingNextObjectives.invalidate(key);
nextGrpList.forEach(nextGrp -> {
log.info("Group service {} group key {} in device:{}. "
+ "Done implementing next objective: {} <<-->> gid:{}",
+ "Done implementing next objective: {} <<-->> gid:0x{}",
(added) ? "ADDED" : "processed",
key, deviceId, nextGrp.nextObjective().id(),
Integer.toHexString(groupService.getGroup(deviceId, key)
......@@ -1116,7 +1117,6 @@ public class OFDPA2GroupHandler {
this.nextObj = nextObj;
}
@SuppressWarnings("unused")
public List<Deque<GroupKey>> groupKey() {
return gkeys;
}
......
......@@ -37,6 +37,7 @@ import org.onosproject.net.group.Group;
import org.onosproject.net.group.GroupBuckets;
import org.onosproject.net.group.GroupDescription;
import org.onosproject.net.group.GroupOperation;
import org.onosproject.net.group.GroupOperation.GroupMsgErrorCode;
import org.onosproject.net.group.GroupOperations;
import org.onosproject.net.group.GroupProvider;
import org.onosproject.net.group.GroupProviderRegistry;
......@@ -56,6 +57,7 @@ import org.projectfloodlight.openflow.protocol.OFErrorType;
import org.projectfloodlight.openflow.protocol.OFGroupDescStatsEntry;
import org.projectfloodlight.openflow.protocol.OFGroupDescStatsReply;
import org.projectfloodlight.openflow.protocol.OFGroupMod;
import org.projectfloodlight.openflow.protocol.OFGroupModFailedCode;
import org.projectfloodlight.openflow.protocol.OFGroupStatsEntry;
import org.projectfloodlight.openflow.protocol.OFGroupStatsReply;
import org.projectfloodlight.openflow.protocol.OFGroupType;
......@@ -64,6 +66,7 @@ import org.projectfloodlight.openflow.protocol.OFPortStatus;
import org.projectfloodlight.openflow.protocol.OFStatsReply;
import org.projectfloodlight.openflow.protocol.OFStatsType;
import org.projectfloodlight.openflow.protocol.OFVersion;
import org.projectfloodlight.openflow.protocol.errormsg.OFGroupModFailedErrorMsg;
import org.slf4j.Logger;
import com.google.common.collect.Maps;
......@@ -135,7 +138,6 @@ public class OpenFlowGroupProvider extends AbstractProvider implements GroupProv
@Override
public void performGroupOperation(DeviceId deviceId, GroupOperations groupOps) {
Map<OFGroupMod, OpenFlowSwitch> mods = Maps.newIdentityHashMap();
final Dpid dpid = Dpid.dpid(deviceId.uri());
OpenFlowSwitch sw = controller.getSwitch(dpid);
for (GroupOperation groupOperation: groupOps.operations()) {
......@@ -329,11 +331,17 @@ public class OpenFlowGroupProvider extends AbstractProvider implements GroupProv
pendingGroupOperations.get(pendingGroupId);
DeviceId deviceId = DeviceId.deviceId(Dpid.uri(dpid));
if (operation != null) {
OFGroupModFailedCode code =
((OFGroupModFailedErrorMsg) errorMsg).getCode();
GroupMsgErrorCode failureCode =
GroupMsgErrorCode.values()[(code.ordinal())];
GroupOperation failedOperation = GroupOperation
.createFailedGroupOperation(operation, failureCode);
providerService.groupOperationFailed(deviceId,
operation);
failedOperation);
pendingGroupOperations.remove(pendingGroupId);
pendingXidMaps.remove(pendingGroupId);
log.warn("Received an group mod error {}", msg);
log.warn("Received a group mod error {}", msg);
} else {
log.error("Cannot find pending group operation with group ID: {}",
pendingGroupId);
......