blob: e06f54ea3565e1653bf15d7aa50153e9563e1921 [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
Carmelo Casconea4a89fb2019-08-20 02:03:10 -070037import java.util.Objects;
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
Daniele Morod900fe42021-02-11 16:12:57 +010097 // Check if the table associated with the action profile supports only
98 // one-shot action profile programming.
99 boolean isTableOneShot = actionProfileModel.tables().stream()
100 .map(tableId -> pipeconf.pipelineModel().table(tableId))
101 .allMatch(piTableModel -> piTableModel.isPresent() &&
102 piTableModel.get().oneShotOnly());
103 if (isTableOneShot) {
104 throw new PiTranslationException(format(
105 "Table associated to action profile '%s' supports only one-shot action profile programming",
106 actionProfileId));
107 }
108
Carmelo Cascone99c59db2019-01-17 15:39:35 -0800109 // Check group validity.
110 if (actionProfileModel.maxGroupSize() > 0
111 && group.buckets().buckets().size() > actionProfileModel.maxGroupSize()) {
112 throw new PiTranslationException(format(
113 "too many buckets, max group size for action profile '%s' is %d",
114 actionProfileId, actionProfileModel.maxGroupSize()));
115 }
116
Carmelo Cascone4f73fac2019-03-19 15:57:59 -0700117 // If not INDIRECT, we set the maximum group size as specified in the
118 // model, however this might be highly inefficient for some HW targets
119 // which pre-allocate resources for the whole group.
120 final int maxGroupSize = group.type() == GroupDescription.Type.INDIRECT
Carmelo Casconea7e60c62019-03-21 17:58:12 -0700121 ? 1 : actionProfileModel.maxGroupSize();
Carmelo Cascone4f73fac2019-03-19 15:57:59 -0700122
Carmelo Cascone99c59db2019-01-17 15:39:35 -0800123 final PiActionProfileGroup.Builder piActionGroupBuilder = PiActionProfileGroup.builder()
124 .withId(PiActionProfileGroupId.of(group.id().id()))
125 .withActionProfileId(groupKey.actionProfileId())
Carmelo Cascone4f73fac2019-03-19 15:57:59 -0700126 .withMaxSize(maxGroupSize);
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200127
128 // Translate group buckets to PI group members
Carmelo Cascone99c59db2019-01-17 15:39:35 -0800129 final PiPipelineInterpreter interpreter = getInterpreterOrNull(device, pipeconf);
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200130 short bucketIdx = 0;
131 for (GroupBucket bucket : group.buckets().buckets()) {
132 /*
133 FIXME: the way member IDs are computed can cause collisions!
Carmelo Cascone99c59db2019-01-17 15:39:35 -0800134 Problem: In P4Runtime action profile members, i.e. action buckets,
135 are associated to a numeric ID chosen at member insertion time. This
136 ID must be unique for the whole action profile (i.e. the group table
137 in OpenFlow). In ONOS, GroupBucket doesn't specify any ID.
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200138
139 Solutions:
Carmelo Cascone99c59db2019-01-17 15:39:35 -0800140 - Change GroupBucket API to force application wanting to perform
141 group operations to specify a member id.
142 - Maintain state to dynamically allocate/deallocate member IDs, e.g.
143 in a dedicated service, or in a P4Runtime Group Provider.
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200144
Carmelo Cascone99c59db2019-01-17 15:39:35 -0800145 Hack: Statically derive member ID by combining groupId and position
146 of the bucket in the list.
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200147 */
Carmelo Casconea4a89fb2019-08-20 02:03:10 -0700148 final int memberId = Objects.hash(group.id(), bucketIdx);
149 if (memberId == 0) {
150 throw new PiTranslationException(
151 "GroupBucket produces PiActionProfileMember " +
152 "with invalid ID 0");
153 }
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200154 bucketIdx++;
155
Carmelo Cascone99c59db2019-01-17 15:39:35 -0800156 final PiTableAction tableAction = translateTreatment(
157 bucket.treatment(), interpreter,
158 groupKey.tableId(), pipeconf.pipelineModel());
wu66460c92018-10-23 11:36:30 +0800159 if (tableAction == null) {
Carmelo Cascone99c59db2019-01-17 15:39:35 -0800160 throw new PiTranslationException(
161 "bucket treatment translator returned null");
wu66460c92018-10-23 11:36:30 +0800162 }
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200163
164 if (tableAction.type() != ACTION) {
165 throw new PiTranslationException(format(
Carmelo Cascone99c59db2019-01-17 15:39:35 -0800166 "action of type '%s' cannot be used in action profile members",
167 tableAction.type()));
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200168 }
169
Carmelo Cascone99c59db2019-01-17 15:39:35 -0800170 final PiActionProfileMember member = PiActionProfileMember.builder()
171 .forActionProfile(groupKey.actionProfileId())
172 .withId(PiActionProfileMemberId.of(memberId))
173 .withAction((PiAction) tableAction)
174 .build();
175
176 piActionGroupBuilder.addMember(member, bucket.weight());
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200177 }
178
179 return piActionGroupBuilder.build();
180 }
181}