blob: 14e2b5c7cb83135cd156687f74c7728e608947fd [file] [log] [blame]
Carmelo Cascone87b9b392017-10-02 18:33:20 +02001/*
2 * Copyright 2017-present Open Networking Foundation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package org.onosproject.net.pi.impl;
18
Carmelo Casconea73f1062018-07-16 22:49:46 +020019import com.google.common.collect.Sets;
Carmelo Cascone87b9b392017-10-02 18:33:20 +020020import org.onosproject.net.Device;
21import org.onosproject.net.group.Group;
22import org.onosproject.net.group.GroupBucket;
Carmelo Casconea73f1062018-07-16 22:49:46 +020023import org.onosproject.net.group.GroupDescription;
Carmelo Cascone99c59db2019-01-17 15:39:35 -080024import org.onosproject.net.pi.model.PiActionProfileId;
25import org.onosproject.net.pi.model.PiActionProfileModel;
Carmelo Cascone87b9b392017-10-02 18:33:20 +020026import org.onosproject.net.pi.model.PiPipeconf;
27import org.onosproject.net.pi.model.PiPipelineInterpreter;
28import org.onosproject.net.pi.runtime.PiAction;
Carmelo Casconecb4327a2018-09-11 15:17:23 -070029import org.onosproject.net.pi.runtime.PiActionProfileGroup;
30import org.onosproject.net.pi.runtime.PiActionProfileGroupId;
31import org.onosproject.net.pi.runtime.PiActionProfileMember;
32import org.onosproject.net.pi.runtime.PiActionProfileMemberId;
Carmelo Cascone87b9b392017-10-02 18:33:20 +020033import org.onosproject.net.pi.runtime.PiGroupKey;
34import org.onosproject.net.pi.runtime.PiTableAction;
Carmelo Cascone326ad2d2017-11-28 18:09:13 -080035import org.onosproject.net.pi.service.PiTranslationException;
Carmelo Cascone87b9b392017-10-02 18:33:20 +020036
37import java.nio.ByteBuffer;
Carmelo Casconea73f1062018-07-16 22:49:46 +020038import java.util.Set;
Carmelo Cascone87b9b392017-10-02 18:33:20 +020039
40import static java.lang.String.format;
Carmelo Cascone326ad2d2017-11-28 18:09:13 -080041import static org.onosproject.net.pi.impl.PiFlowRuleTranslatorImpl.translateTreatment;
Carmelo Cascone87b9b392017-10-02 18:33:20 +020042import static org.onosproject.net.pi.impl.PiUtils.getInterpreterOrNull;
43import static org.onosproject.net.pi.runtime.PiTableAction.Type.ACTION;
44
45/**
46 * Implementation of group translation logic.
47 */
Carmelo Cascone326ad2d2017-11-28 18:09:13 -080048final class PiGroupTranslatorImpl {
Carmelo Cascone87b9b392017-10-02 18:33:20 +020049
Carmelo Casconea73f1062018-07-16 22:49:46 +020050 private static final Set<GroupDescription.Type> SUPPORTED_GROUP_TYPES =
51 Sets.immutableEnumSet(
52 GroupDescription.Type.SELECT,
53 GroupDescription.Type.INDIRECT);
54
Carmelo Cascone326ad2d2017-11-28 18:09:13 -080055 private PiGroupTranslatorImpl() {
Carmelo Cascone87b9b392017-10-02 18:33:20 +020056 // Hides constructor.
57 }
58
59 /**
Carmelo Cascone99c59db2019-01-17 15:39:35 -080060 * Returns a PI action profile group equivalent to the given group, for the
61 * given pipeconf and device.
Carmelo Cascone87b9b392017-10-02 18:33:20 +020062 *
63 * @param group group
64 * @param pipeconf pipeconf
65 * @param device device
Carmelo Casconecb4327a2018-09-11 15:17:23 -070066 * @return PI action profile group
Carmelo Cascone87b9b392017-10-02 18:33:20 +020067 * @throws PiTranslationException if the group cannot be translated
68 */
Carmelo Casconecb4327a2018-09-11 15:17:23 -070069 static PiActionProfileGroup translate(Group group, PiPipeconf pipeconf, Device device)
70 throws PiTranslationException {
Carmelo Cascone87b9b392017-10-02 18:33:20 +020071
Carmelo Casconea73f1062018-07-16 22:49:46 +020072 if (!SUPPORTED_GROUP_TYPES.contains(group.type())) {
73 throw new PiTranslationException(format(
74 "group type %s not supported", group.type()));
75 }
76
Carmelo Cascone99c59db2019-01-17 15:39:35 -080077 // Get action profile from group key.
78 // TODO: define proper field in group class.
Carmelo Cascone87b9b392017-10-02 18:33:20 +020079 if (!(group.appCookie() instanceof PiGroupKey)) {
Carmelo Cascone99c59db2019-01-17 15:39:35 -080080 throw new PiTranslationException(
81 "group app cookie is not PI (class should be PiGroupKey)");
Carmelo Cascone87b9b392017-10-02 18:33:20 +020082 }
83 final PiGroupKey groupKey = (PiGroupKey) group.appCookie();
Carmelo Cascone99c59db2019-01-17 15:39:35 -080084 final PiActionProfileId actionProfileId = groupKey.actionProfileId();
Carmelo Cascone87b9b392017-10-02 18:33:20 +020085
Carmelo Cascone99c59db2019-01-17 15:39:35 -080086 // Check validity of action profile against pipeconf.
87 final PiActionProfileModel actionProfileModel = pipeconf.pipelineModel()
88 .actionProfiles(actionProfileId)
89 .orElseThrow(() -> new PiTranslationException(format(
90 "no such action profile '%s'", actionProfileId)));
91 if (!actionProfileModel.hasSelector()) {
92 throw new PiTranslationException(format(
93 "action profile '%s' does not support dynamic selection",
94 actionProfileId));
95 }
96
97 // Check group validity.
98 if (actionProfileModel.maxGroupSize() > 0
99 && group.buckets().buckets().size() > actionProfileModel.maxGroupSize()) {
100 throw new PiTranslationException(format(
101 "too many buckets, max group size for action profile '%s' is %d",
102 actionProfileId, actionProfileModel.maxGroupSize()));
103 }
104
Carmelo Cascone4f73fac2019-03-19 15:57:59 -0700105 // If not INDIRECT, we set the maximum group size as specified in the
106 // model, however this might be highly inefficient for some HW targets
107 // which pre-allocate resources for the whole group.
108 final int maxGroupSize = group.type() == GroupDescription.Type.INDIRECT
Carmelo Casconea7e60c62019-03-21 17:58:12 -0700109 ? 1 : actionProfileModel.maxGroupSize();
Carmelo Cascone4f73fac2019-03-19 15:57:59 -0700110
Carmelo Cascone99c59db2019-01-17 15:39:35 -0800111 final PiActionProfileGroup.Builder piActionGroupBuilder = PiActionProfileGroup.builder()
112 .withId(PiActionProfileGroupId.of(group.id().id()))
113 .withActionProfileId(groupKey.actionProfileId())
Carmelo Cascone4f73fac2019-03-19 15:57:59 -0700114 .withMaxSize(maxGroupSize);
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200115
116 // Translate group buckets to PI group members
Carmelo Cascone99c59db2019-01-17 15:39:35 -0800117 final PiPipelineInterpreter interpreter = getInterpreterOrNull(device, pipeconf);
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200118 short bucketIdx = 0;
119 for (GroupBucket bucket : group.buckets().buckets()) {
120 /*
121 FIXME: the way member IDs are computed can cause collisions!
Carmelo Cascone99c59db2019-01-17 15:39:35 -0800122 Problem: In P4Runtime action profile members, i.e. action buckets,
123 are associated to a numeric ID chosen at member insertion time. This
124 ID must be unique for the whole action profile (i.e. the group table
125 in OpenFlow). In ONOS, GroupBucket doesn't specify any ID.
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200126
127 Solutions:
Carmelo Cascone99c59db2019-01-17 15:39:35 -0800128 - Change GroupBucket API to force application wanting to perform
129 group operations to specify a member id.
130 - Maintain state to dynamically allocate/deallocate member IDs, e.g.
131 in a dedicated service, or in a P4Runtime Group Provider.
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200132
Carmelo Cascone99c59db2019-01-17 15:39:35 -0800133 Hack: Statically derive member ID by combining groupId and position
134 of the bucket in the list.
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200135 */
Carmelo Cascone99c59db2019-01-17 15:39:35 -0800136 final ByteBuffer bb = ByteBuffer.allocate(4)
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200137 .putShort((short) (group.id().id() & 0xffff))
138 .putShort(bucketIdx);
139 bb.rewind();
Carmelo Cascone99c59db2019-01-17 15:39:35 -0800140 final int memberId = bb.getInt();
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200141 bucketIdx++;
142
Carmelo Cascone99c59db2019-01-17 15:39:35 -0800143 final PiTableAction tableAction = translateTreatment(
144 bucket.treatment(), interpreter,
145 groupKey.tableId(), pipeconf.pipelineModel());
wu66460c92018-10-23 11:36:30 +0800146 if (tableAction == null) {
Carmelo Cascone99c59db2019-01-17 15:39:35 -0800147 throw new PiTranslationException(
148 "bucket treatment translator returned null");
wu66460c92018-10-23 11:36:30 +0800149 }
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200150
151 if (tableAction.type() != ACTION) {
152 throw new PiTranslationException(format(
Carmelo Cascone99c59db2019-01-17 15:39:35 -0800153 "action of type '%s' cannot be used in action profile members",
154 tableAction.type()));
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200155 }
156
Carmelo Cascone99c59db2019-01-17 15:39:35 -0800157 final PiActionProfileMember member = PiActionProfileMember.builder()
158 .forActionProfile(groupKey.actionProfileId())
159 .withId(PiActionProfileMemberId.of(memberId))
160 .withAction((PiAction) tableAction)
161 .build();
162
163 piActionGroupBuilder.addMember(member, bucket.weight());
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200164 }
165
166 return piActionGroupBuilder.build();
167 }
168}