blob: c30e2c83ec88c89f202606cf79c88290d10a924d [file] [log] [blame]
Frank Wangd7e3b4b2017-09-24 13:37:54 +09001/*
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.p4runtime.ctl;
18
19import org.onosproject.net.pi.model.PiMeterType;
20import org.onosproject.net.pi.model.PiPipeconf;
21import org.onosproject.net.pi.runtime.PiMeterBand;
22import org.onosproject.net.pi.runtime.PiMeterCellConfig;
23import org.onosproject.net.pi.runtime.PiMeterCellId;
24import org.onosproject.net.pi.model.PiMeterId;
25import org.onosproject.net.pi.runtime.PiTableEntry;
26import org.slf4j.Logger;
27import p4.P4RuntimeOuterClass.MeterConfig;
28import p4.P4RuntimeOuterClass.MeterEntry;
29import p4.P4RuntimeOuterClass.DirectMeterEntry;
30import p4.P4RuntimeOuterClass.Entity;
31
32import java.util.Collection;
33import java.util.Map;
34import java.util.Objects;
35import java.util.stream.Collectors;
36
37import static java.lang.String.format;
38import static org.slf4j.LoggerFactory.getLogger;
39import static p4.P4RuntimeOuterClass.Entity.EntityCase.*;
40
41/**
42 * Encoder/decoder of PI meter cell configurations to meter entry protobuf messages, and vice versa.
43 */
44final class MeterEntryCodec {
45
46 private static final Logger log = getLogger(MeterEntryCodec.class);
47
48 private MeterEntryCodec() {
49 // Hides constructor.
50 }
51
52 /**
53 * Returns a collection of P4Runtime entity protobuf messages describing both meter or direct meter entries,
54 * encoded from the given collection of PI meter cell configurations, for the given pipeconf. If a PI meter cell
55 * configurations cannot be encoded, it is skipped, hence the returned collection might have different size than the
56 * input one.
57 * <p>
58 * This method takes as parameter also a map between numeric P4Info IDs and PI meter IDs, that will be populated
59 * during the process and that is then needed to aid in the decode process.
60 *
61 * @param cellConfigs meter cell configurations
62 * @param meterIdMap meter ID map (empty, it will be populated during this method execution)
63 * @param pipeconf pipeconf
64 * @return collection of entity messages describing both meter or direct meter entries
65 */
66 static Collection<Entity> encodePiMeterCellConfigs(Collection<PiMeterCellConfig> cellConfigs,
67 Map<Integer, PiMeterId> meterIdMap,
68 PiPipeconf pipeconf) {
69 return cellConfigs
70 .stream()
71 .map(cellConfig -> {
72 try {
73 return encodePiMeterCellConfig(cellConfig, meterIdMap, pipeconf);
74 } catch (P4InfoBrowser.NotFoundException | EncodeException e) {
75 log.warn("Unable to encode PI meter cell id: {}", e.getMessage());
76 log.debug("exception", e);
77 return null;
78 }
79 })
80 .filter(Objects::nonNull)
81 .collect(Collectors.toList());
82 }
83
84 /**
85 * Returns a collection of PI meter cell configurations, decoded from the given P4Runtime entity protobuf messages
86 * describing both meter or direct meter entries, for the given meter ID map (populated by {@link
87 * #encodePiMeterCellConfigs(Collection, Map, PiPipeconf)}), and pipeconf. If an entity message cannot be encoded,
88 * it is skipped, hence the returned collection might have different size than the input one.
89 *
90 * @param entities P4Runtime entity messages
91 * @param meterIdMap meter ID map (previously populated)
92 * @param pipeconf pipeconf
93 * @return collection of PI meter cell data
94 */
95 static Collection<PiMeterCellConfig> decodeMeterEntities(Collection<Entity> entities,
96 Map<Integer, PiMeterId> meterIdMap,
97 PiPipeconf pipeconf) {
98 return entities
99 .stream()
100 .filter(entity -> entity.getEntityCase() == METER_ENTRY ||
101 entity.getEntityCase() == DIRECT_METER_ENTRY)
102 .map(entity -> {
103 try {
104 return decodeMeterEntity(entity, meterIdMap, pipeconf);
105 } catch (EncodeException | P4InfoBrowser.NotFoundException e) {
106 log.warn("Unable to decode meter entity message: {}", e.getMessage());
107 return null;
108 }
109 })
110 .filter(Objects::nonNull)
111 .collect(Collectors.toList());
112 }
113
114 private static Entity encodePiMeterCellConfig(PiMeterCellConfig config, Map<Integer, PiMeterId> meterIdMap,
115 PiPipeconf pipeconf)
116 throws P4InfoBrowser.NotFoundException, EncodeException {
117
118 final P4InfoBrowser browser = PipeconfHelper.getP4InfoBrowser(pipeconf);
119
120 int meterId;
121 Entity entity;
122 //The band with bigger burst is peak if rate of them is equal,
123 //if bands are not specificed, using default value(0).
124 long cir = 0;
125 long cburst = 0;
126 long pir = 0;
127 long pburst = 0;
128 PiMeterBand[] bands = config.meterBands().toArray(new PiMeterBand[config.meterBands().size()]);
129 if (bands.length == 2) {
130 if (bands[0].rate() > bands[1].rate()) {
131 cir = bands[1].rate();
132 cburst = bands[1].burst();
133 pir = bands[0].rate();
134 pburst = bands[0].burst();
135 } else {
136 cir = bands[0].rate();
137 cburst = bands[0].burst();
138 pir = bands[1].rate();
139 pburst = bands[1].burst();
140 }
141 }
142
143 // Encode PI cell ID into entity message and add to read request.
144 switch (config.cellId().meterType()) {
145 case INDIRECT:
146 meterId = browser.meters().getByName(config.cellId().meterId().id()).getPreamble().getId();
147 entity = Entity.newBuilder().setMeterEntry(MeterEntry
148 .newBuilder().setMeterId(meterId)
149 .setIndex(config.cellId().index())
150 .setConfig(MeterConfig.newBuilder()
151 .setCir(cir)
152 .setCburst(cburst)
153 .setPir(pir)
154 .setPburst(pburst)
155 .build())
156 .build())
157 .build();
158 break;
159 case DIRECT:
160 meterId = browser.directMeters().getByName(config.cellId().meterId().id()).getPreamble().getId();
161 DirectMeterEntry.Builder entryBuilder = DirectMeterEntry.newBuilder()
162 .setMeterId(meterId)
163 .setConfig(MeterConfig.newBuilder()
164 .setCir(cir)
165 .setCburst(cburst)
166 .setPir(pir)
167 .setPburst(pburst)
168 .build());
169
170 if (!config.cellId().tableEntry().equals(PiTableEntry.EMTPY)) {
171 entryBuilder.setTableEntry(TableEntryEncoder.encode(config.cellId().tableEntry(), pipeconf));
172 }
173 entity = Entity.newBuilder().setDirectMeterEntry(entryBuilder.build()).build();
174 break;
175 default:
176 throw new EncodeException(format("Unrecognized PI meter cell ID type '%s'",
177 config.cellId().meterType()));
178 }
179 meterIdMap.put(meterId, config.cellId().meterId());
180
181 return entity;
182 }
183
184 private static PiMeterCellConfig decodeMeterEntity(Entity entity, Map<Integer, PiMeterId> meterIdMap,
185 PiPipeconf pipeconf)
186 throws EncodeException, P4InfoBrowser.NotFoundException {
187
188 int meterId;
189 MeterConfig meterConfig;
190
191 if (entity.getEntityCase() == METER_ENTRY) {
192 meterId = entity.getMeterEntry().getMeterId();
193 meterConfig = entity.getMeterEntry().getConfig();
194 } else {
195 meterId = entity.getDirectMeterEntry().getMeterId();
196 meterConfig = entity.getDirectMeterEntry().getConfig();
197 }
198
199 // Process only meter IDs that were requested in the first place.
200 if (!meterIdMap.containsKey(meterId)) {
201 throw new EncodeException(format("Unrecognized meter ID '%s'", meterId));
202 }
203
204 PiMeterId piMeterId = meterIdMap.get(meterId);
205 if (!pipeconf.pipelineModel().meter(piMeterId).isPresent()) {
206 throw new EncodeException(format("Unable to find meter '{}' in pipeline model", meterId));
207 }
208
209 PiMeterType piMeterType = pipeconf.pipelineModel().meter(piMeterId).get().meterType();
210 // Compute PI cell ID.
211 PiMeterCellId piCellId;
212
213 switch (piMeterType) {
214 case INDIRECT:
215 if (entity.getEntityCase() != METER_ENTRY) {
216 throw new EncodeException(format(
217 "Meter ID '%s' is indirect, but processed entity is %s",
218 piMeterId, entity.getEntityCase()));
219 }
220 piCellId = PiMeterCellId.ofIndirect(piMeterId, entity.getMeterEntry().getIndex());
221 break;
222 case DIRECT:
223 if (entity.getEntityCase() != DIRECT_METER_ENTRY) {
224 throw new EncodeException(format(
225 "Meter ID '%s' is direct, but processed entity is %s",
226 piMeterId, entity.getEntityCase()));
227 }
228 PiTableEntry piTableEntry = TableEntryEncoder.decode(entity.getDirectMeterEntry().getTableEntry(),
229 pipeconf);
230 piCellId = PiMeterCellId.ofDirect(piMeterId, piTableEntry);
231 break;
232 default:
233 throw new EncodeException(format("Unrecognized PI meter ID type '%s'", piMeterType));
234 }
235
236 PiMeterCellConfig.Builder builder = PiMeterCellConfig.builder();
237 builder.withMeterBand(new PiMeterBand(meterConfig.getCir(), meterConfig.getCburst()));
238 builder.withMeterBand(new PiMeterBand(meterConfig.getPir(), meterConfig.getPburst()));
239
240 return builder.withMeterCellId(piCellId).build();
241 }
242}