| /* |
| * Copyright 2017-present Open Networking Foundation |
| * |
| * 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.pi.impl; |
| |
| import com.google.common.collect.Sets; |
| import org.onosproject.net.Device; |
| import org.onosproject.net.group.Group; |
| import org.onosproject.net.group.GroupBucket; |
| import org.onosproject.net.group.GroupDescription; |
| import org.onosproject.net.pi.model.PiActionProfileId; |
| import org.onosproject.net.pi.model.PiActionProfileModel; |
| import org.onosproject.net.pi.model.PiPipeconf; |
| import org.onosproject.net.pi.model.PiPipelineInterpreter; |
| import org.onosproject.net.pi.runtime.PiAction; |
| import org.onosproject.net.pi.runtime.PiActionProfileGroup; |
| import org.onosproject.net.pi.runtime.PiActionProfileGroupId; |
| import org.onosproject.net.pi.runtime.PiActionProfileMember; |
| import org.onosproject.net.pi.runtime.PiActionProfileMemberId; |
| import org.onosproject.net.pi.runtime.PiGroupKey; |
| import org.onosproject.net.pi.runtime.PiTableAction; |
| import org.onosproject.net.pi.service.PiTranslationException; |
| |
| import java.nio.ByteBuffer; |
| import java.util.Set; |
| |
| import static java.lang.String.format; |
| import static org.onosproject.net.pi.impl.PiFlowRuleTranslatorImpl.translateTreatment; |
| import static org.onosproject.net.pi.impl.PiUtils.getInterpreterOrNull; |
| import static org.onosproject.net.pi.runtime.PiTableAction.Type.ACTION; |
| |
| /** |
| * Implementation of group translation logic. |
| */ |
| final class PiGroupTranslatorImpl { |
| |
| private static final Set<GroupDescription.Type> SUPPORTED_GROUP_TYPES = |
| Sets.immutableEnumSet( |
| GroupDescription.Type.SELECT, |
| GroupDescription.Type.INDIRECT); |
| |
| private PiGroupTranslatorImpl() { |
| // Hides constructor. |
| } |
| |
| /** |
| * Returns a PI action profile group equivalent to the given group, for the |
| * given pipeconf and device. |
| * |
| * @param group group |
| * @param pipeconf pipeconf |
| * @param device device |
| * @return PI action profile group |
| * @throws PiTranslationException if the group cannot be translated |
| */ |
| static PiActionProfileGroup translate(Group group, PiPipeconf pipeconf, Device device) |
| throws PiTranslationException { |
| |
| if (!SUPPORTED_GROUP_TYPES.contains(group.type())) { |
| throw new PiTranslationException(format( |
| "group type %s not supported", group.type())); |
| } |
| |
| // Get action profile from group key. |
| // TODO: define proper field in group class. |
| if (!(group.appCookie() instanceof PiGroupKey)) { |
| throw new PiTranslationException( |
| "group app cookie is not PI (class should be PiGroupKey)"); |
| } |
| final PiGroupKey groupKey = (PiGroupKey) group.appCookie(); |
| final PiActionProfileId actionProfileId = groupKey.actionProfileId(); |
| |
| // Check validity of action profile against pipeconf. |
| final PiActionProfileModel actionProfileModel = pipeconf.pipelineModel() |
| .actionProfiles(actionProfileId) |
| .orElseThrow(() -> new PiTranslationException(format( |
| "no such action profile '%s'", actionProfileId))); |
| if (!actionProfileModel.hasSelector()) { |
| throw new PiTranslationException(format( |
| "action profile '%s' does not support dynamic selection", |
| actionProfileId)); |
| } |
| |
| // Check group validity. |
| if (actionProfileModel.maxGroupSize() > 0 |
| && group.buckets().buckets().size() > actionProfileModel.maxGroupSize()) { |
| throw new PiTranslationException(format( |
| "too many buckets, max group size for action profile '%s' is %d", |
| actionProfileId, actionProfileModel.maxGroupSize())); |
| } |
| |
| final PiActionProfileGroup.Builder piActionGroupBuilder = PiActionProfileGroup.builder() |
| .withId(PiActionProfileGroupId.of(group.id().id())) |
| .withActionProfileId(groupKey.actionProfileId()) |
| // We set the maximum group size as specified in the model, |
| // however this might be highly inefficient for some HW targets |
| // which pre-allocate resources for the whole group. |
| .withMaxSize(actionProfileModel.maxGroupSize()); |
| |
| // Translate group buckets to PI group members |
| final PiPipelineInterpreter interpreter = getInterpreterOrNull(device, pipeconf); |
| short bucketIdx = 0; |
| for (GroupBucket bucket : group.buckets().buckets()) { |
| /* |
| FIXME: the way member IDs are computed can cause collisions! |
| Problem: In P4Runtime action profile members, i.e. action buckets, |
| are associated to a numeric ID chosen at member insertion time. This |
| ID must be unique for the whole action profile (i.e. the group table |
| in OpenFlow). In ONOS, GroupBucket doesn't specify any ID. |
| |
| Solutions: |
| - Change GroupBucket API to force application wanting to perform |
| group operations to specify a member id. |
| - Maintain state to dynamically allocate/deallocate member IDs, e.g. |
| in a dedicated service, or in a P4Runtime Group Provider. |
| |
| Hack: Statically derive member ID by combining groupId and position |
| of the bucket in the list. |
| */ |
| final ByteBuffer bb = ByteBuffer.allocate(4) |
| .putShort((short) (group.id().id() & 0xffff)) |
| .putShort(bucketIdx); |
| bb.rewind(); |
| final int memberId = bb.getInt(); |
| bucketIdx++; |
| |
| final PiTableAction tableAction = translateTreatment( |
| bucket.treatment(), interpreter, |
| groupKey.tableId(), pipeconf.pipelineModel()); |
| if (tableAction == null) { |
| throw new PiTranslationException( |
| "bucket treatment translator returned null"); |
| } |
| |
| if (tableAction.type() != ACTION) { |
| throw new PiTranslationException(format( |
| "action of type '%s' cannot be used in action profile members", |
| tableAction.type())); |
| } |
| |
| final PiActionProfileMember member = PiActionProfileMember.builder() |
| .forActionProfile(groupKey.actionProfileId()) |
| .withId(PiActionProfileMemberId.of(memberId)) |
| .withAction((PiAction) tableAction) |
| .build(); |
| |
| piActionGroupBuilder.addMember(member, bucket.weight()); |
| } |
| |
| return piActionGroupBuilder.build(); |
| } |
| } |