blob: fc60dcd655a6a27942bd0c62e494cf5dda842a3a [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
Carmelo Cascone81929aa2018-04-07 01:38:55 -070019import org.onosproject.net.pi.model.PiMeterId;
Frank Wangd7e3b4b2017-09-24 13:37:54 +090020import org.onosproject.net.pi.model.PiMeterType;
21import org.onosproject.net.pi.model.PiPipeconf;
Carmelo Cascone81929aa2018-04-07 01:38:55 -070022import org.onosproject.net.pi.model.PiTableId;
Frank Wangd7e3b4b2017-09-24 13:37:54 +090023import org.onosproject.net.pi.runtime.PiMeterBand;
24import org.onosproject.net.pi.runtime.PiMeterCellConfig;
25import org.onosproject.net.pi.runtime.PiMeterCellId;
Frank Wangd7e3b4b2017-09-24 13:37:54 +090026import org.onosproject.net.pi.runtime.PiTableEntry;
27import org.slf4j.Logger;
Carmelo Cascone81929aa2018-04-07 01:38:55 -070028import p4.P4RuntimeOuterClass;
Frank Wangd7e3b4b2017-09-24 13:37:54 +090029import p4.P4RuntimeOuterClass.DirectMeterEntry;
30import p4.P4RuntimeOuterClass.Entity;
Carmelo Cascone81929aa2018-04-07 01:38:55 -070031import p4.P4RuntimeOuterClass.MeterConfig;
32import p4.P4RuntimeOuterClass.MeterEntry;
Frank Wangd7e3b4b2017-09-24 13:37:54 +090033
34import java.util.Collection;
Carmelo Cascone81929aa2018-04-07 01:38:55 -070035import java.util.Collections;
Frank Wangd7e3b4b2017-09-24 13:37:54 +090036import java.util.Objects;
37import java.util.stream.Collectors;
38
39import static java.lang.String.format;
Carmelo Cascone81929aa2018-04-07 01:38:55 -070040import static org.onosproject.p4runtime.ctl.P4RuntimeUtils.indexMsg;
Frank Wangd7e3b4b2017-09-24 13:37:54 +090041import static org.slf4j.LoggerFactory.getLogger;
Carmelo Cascone81929aa2018-04-07 01:38:55 -070042import static p4.P4RuntimeOuterClass.Entity.EntityCase.DIRECT_METER_ENTRY;
43import static p4.P4RuntimeOuterClass.Entity.EntityCase.METER_ENTRY;
Frank Wangd7e3b4b2017-09-24 13:37:54 +090044
45/**
Carmelo Cascone81929aa2018-04-07 01:38:55 -070046 * Encoder/decoder of PI meter cell configurations to meter entry protobuf
47 * messages, and vice versa.
Frank Wangd7e3b4b2017-09-24 13:37:54 +090048 */
49final class MeterEntryCodec {
50
51 private static final Logger log = getLogger(MeterEntryCodec.class);
52
53 private MeterEntryCodec() {
54 // Hides constructor.
55 }
56
57 /**
Carmelo Cascone81929aa2018-04-07 01:38:55 -070058 * Returns a collection of P4Runtime entity protobuf messages describing
59 * both meter or direct meter entries, encoded from the given collection of
60 * PI meter cell configurations, for the given pipeconf. If a PI meter cell
61 * configurations cannot be encoded, it is skipped, hence the returned
62 * collection might have different size than the input one.
Frank Wangd7e3b4b2017-09-24 13:37:54 +090063 *
Carmelo Cascone81929aa2018-04-07 01:38:55 -070064 * @param cellConfigs meter cell configurations
65 * @param pipeconf pipeconf
66 * @return collection of entity messages describing both meter or direct
67 * meter entries
Frank Wangd7e3b4b2017-09-24 13:37:54 +090068 */
69 static Collection<Entity> encodePiMeterCellConfigs(Collection<PiMeterCellConfig> cellConfigs,
Frank Wangd7e3b4b2017-09-24 13:37:54 +090070 PiPipeconf pipeconf) {
Carmelo Cascone81929aa2018-04-07 01:38:55 -070071 final P4InfoBrowser browser = PipeconfHelper.getP4InfoBrowser(pipeconf);
72
73 if (browser == null) {
74 log.error("Unable to get a P4Info browser for pipeconf {}", pipeconf.id());
75 return Collections.emptyList();
76 }
77
Frank Wangd7e3b4b2017-09-24 13:37:54 +090078 return cellConfigs
79 .stream()
80 .map(cellConfig -> {
81 try {
Carmelo Cascone81929aa2018-04-07 01:38:55 -070082 return encodePiMeterCellConfig(cellConfig, pipeconf, browser);
Frank Wangd7e3b4b2017-09-24 13:37:54 +090083 } catch (P4InfoBrowser.NotFoundException | EncodeException e) {
84 log.warn("Unable to encode PI meter cell id: {}", e.getMessage());
85 log.debug("exception", e);
86 return null;
87 }
88 })
89 .filter(Objects::nonNull)
90 .collect(Collectors.toList());
91 }
92
93 /**
Carmelo Cascone81929aa2018-04-07 01:38:55 -070094 * Returns a collection of P4Runtime entity protobuf messages to be used in
95 * requests to read all cells from the given meter identifiers. Works for
96 * both indirect or direct meters. If a PI meter identifier cannot be
97 * encoded, it is skipped, hence the returned collection might have
98 * different size than the input one.
Frank Wangd7e3b4b2017-09-24 13:37:54 +090099 *
Carmelo Cascone81929aa2018-04-07 01:38:55 -0700100 * @param meterIds meter identifiers
101 * @param pipeconf pipeconf
102 * @return collection of entity messages
103 */
104 static Collection<Entity> readAllCellsEntities(Collection<PiMeterId> meterIds,
105 PiPipeconf pipeconf) {
106 final P4InfoBrowser browser = PipeconfHelper.getP4InfoBrowser(pipeconf);
107
108 if (browser == null) {
109 log.error("Unable to get a P4Info browser for pipeconf {}", pipeconf.id());
110 return Collections.emptyList();
111 }
112
113 return meterIds
114 .stream()
115 .map(meterId -> {
116 try {
117 return readAllCellsEntity(meterId, pipeconf, browser);
118 } catch (P4InfoBrowser.NotFoundException | EncodeException e) {
119 log.warn("Unable to encode meter ID to read-all-cells entity: {}",
120 e.getMessage());
121 return null;
122 }
123 })
124 .filter(Objects::nonNull)
125 .collect(Collectors.toList());
126 }
127
128 /**
129 * Returns a collection of PI meter cell configurations, decoded from the
130 * given P4Runtime entity protobuf messages describing both meter or direct
131 * meter entries, and pipeconf. If an entity message cannot be encoded, it
132 * is skipped, hence the returned collection might have different size than
133 * the input one.
134 *
135 * @param entities P4Runtime entity messages
136 * @param pipeconf pipeconf
Frank Wangd7e3b4b2017-09-24 13:37:54 +0900137 * @return collection of PI meter cell data
138 */
139 static Collection<PiMeterCellConfig> decodeMeterEntities(Collection<Entity> entities,
Carmelo Cascone81929aa2018-04-07 01:38:55 -0700140 PiPipeconf pipeconf) {
141 final P4InfoBrowser browser = PipeconfHelper.getP4InfoBrowser(pipeconf);
142
143 if (browser == null) {
144 log.error("Unable to get a P4Info browser for pipeconf {}", pipeconf.id());
145 return Collections.emptyList();
146 }
147
Frank Wangd7e3b4b2017-09-24 13:37:54 +0900148 return entities
149 .stream()
150 .filter(entity -> entity.getEntityCase() == METER_ENTRY ||
151 entity.getEntityCase() == DIRECT_METER_ENTRY)
152 .map(entity -> {
153 try {
Carmelo Cascone81929aa2018-04-07 01:38:55 -0700154 return decodeMeterEntity(entity, pipeconf, browser);
Frank Wangd7e3b4b2017-09-24 13:37:54 +0900155 } catch (EncodeException | P4InfoBrowser.NotFoundException e) {
156 log.warn("Unable to decode meter entity message: {}", e.getMessage());
157 return null;
158 }
159 })
160 .filter(Objects::nonNull)
161 .collect(Collectors.toList());
162 }
163
Carmelo Cascone81929aa2018-04-07 01:38:55 -0700164 private static Entity encodePiMeterCellConfig(PiMeterCellConfig config,
165 PiPipeconf pipeconf,
166 P4InfoBrowser browser)
Frank Wangd7e3b4b2017-09-24 13:37:54 +0900167 throws P4InfoBrowser.NotFoundException, EncodeException {
168
Frank Wangd7e3b4b2017-09-24 13:37:54 +0900169 int meterId;
170 Entity entity;
Carmelo Cascone81929aa2018-04-07 01:38:55 -0700171 MeterConfig meterConfig;
172
173 PiMeterBand[] bands = config.meterBands()
174 .toArray(new PiMeterBand[config.meterBands().size()]);
Frank Wangd7e3b4b2017-09-24 13:37:54 +0900175 if (bands.length == 2) {
Carmelo Cascone81929aa2018-04-07 01:38:55 -0700176 long cir, cburst, pir, pburst;
177 // The band with bigger burst is peak if rate of them is equal.
178 if (bands[0].rate() > bands[1].rate() ||
179 (bands[0].rate() == bands[1].rate() &&
180 bands[0].burst() >= bands[1].burst())) {
Frank Wangd7e3b4b2017-09-24 13:37:54 +0900181 cir = bands[1].rate();
182 cburst = bands[1].burst();
183 pir = bands[0].rate();
184 pburst = bands[0].burst();
185 } else {
186 cir = bands[0].rate();
187 cburst = bands[0].burst();
188 pir = bands[1].rate();
189 pburst = bands[1].burst();
190 }
Carmelo Cascone81929aa2018-04-07 01:38:55 -0700191 meterConfig = MeterConfig.newBuilder()
192 .setCir(cir)
193 .setCburst(cburst)
194 .setPir(pir)
195 .setPburst(pburst)
196 .build();
197 } else if (bands.length == 0) {
198 // When reading meter cells.
199 meterConfig = null;
200 } else {
201 throw new EncodeException("number of meter bands should be either 2 or 0");
Frank Wangd7e3b4b2017-09-24 13:37:54 +0900202 }
203
Frank Wangd7e3b4b2017-09-24 13:37:54 +0900204 switch (config.cellId().meterType()) {
205 case INDIRECT:
Carmelo Cascone81929aa2018-04-07 01:38:55 -0700206 meterId = browser.meters()
207 .getByName(config.cellId().meterId().id())
208 .getPreamble().getId();
209 MeterEntry.Builder indEntryBuilder = MeterEntry.newBuilder()
210 .setMeterId(meterId)
211 .setIndex(indexMsg(config.cellId().index()));
212 if (meterConfig != null) {
213 indEntryBuilder.setConfig(meterConfig);
214 }
215 entity = Entity.newBuilder()
216 .setMeterEntry(indEntryBuilder.build()).build();
Frank Wangd7e3b4b2017-09-24 13:37:54 +0900217 break;
218 case DIRECT:
Carmelo Cascone81929aa2018-04-07 01:38:55 -0700219 DirectMeterEntry.Builder dirEntryBuilder = DirectMeterEntry.newBuilder()
220 .setTableEntry(TableEntryEncoder.encode(
221 config.cellId().tableEntry(), pipeconf));
222 if (meterConfig != null) {
223 dirEntryBuilder.setConfig(meterConfig);
Frank Wangd7e3b4b2017-09-24 13:37:54 +0900224 }
Carmelo Cascone81929aa2018-04-07 01:38:55 -0700225 entity = Entity.newBuilder()
226 .setDirectMeterEntry(dirEntryBuilder.build()).build();
Frank Wangd7e3b4b2017-09-24 13:37:54 +0900227 break;
228 default:
Carmelo Cascone81929aa2018-04-07 01:38:55 -0700229 throw new EncodeException(format("unrecognized PI meter type '%s'",
Frank Wangd7e3b4b2017-09-24 13:37:54 +0900230 config.cellId().meterType()));
231 }
Frank Wangd7e3b4b2017-09-24 13:37:54 +0900232
233 return entity;
234 }
235
Carmelo Cascone81929aa2018-04-07 01:38:55 -0700236 private static Entity readAllCellsEntity(PiMeterId meterId,
237 PiPipeconf pipeconf,
238 P4InfoBrowser browser)
239 throws P4InfoBrowser.NotFoundException, EncodeException {
240
241 if (!pipeconf.pipelineModel().meter(meterId).isPresent()) {
242 throw new EncodeException(format(
243 "not such meter '%s' in pipeline model", meterId));
244 }
245 final PiMeterType meterType = pipeconf.pipelineModel()
246 .meter(meterId).get().meterType();
247
248 switch (meterType) {
249 case INDIRECT:
250 final int p4InfoMeterId = browser.meters()
251 .getByName(meterId.id())
252 .getPreamble().getId();
253 return Entity.newBuilder().setMeterEntry(
254 P4RuntimeOuterClass.MeterEntry.newBuilder()
255 // Index unset to read all cells
256 .setMeterId(p4InfoMeterId)
257 .build())
258 .build();
259 case DIRECT:
260 final PiTableId tableId = pipeconf.pipelineModel()
261 .meter(meterId).get().table();
262 if (tableId == null) {
263 throw new EncodeException(format(
264 "null table for direct meter '%s'", meterId));
265 }
266 final int p4TableId = browser.tables().getByName(tableId.id())
267 .getPreamble().getId();
268 return Entity.newBuilder().setDirectMeterEntry(
269 P4RuntimeOuterClass.DirectMeterEntry.newBuilder()
270 .setTableEntry(
271 // Match unset to read all cells
272 P4RuntimeOuterClass.TableEntry.newBuilder()
273 .setTableId(p4TableId)
274 .build())
275 .build())
276 .build();
277 default:
278 throw new EncodeException(format(
279 "unrecognized PI meter type '%s'", meterType));
280 }
281 }
282
283 private static PiMeterCellConfig decodeMeterEntity(Entity entity,
284 PiPipeconf pipeconf,
285 P4InfoBrowser browser)
Frank Wangd7e3b4b2017-09-24 13:37:54 +0900286 throws EncodeException, P4InfoBrowser.NotFoundException {
287
Frank Wangd7e3b4b2017-09-24 13:37:54 +0900288 MeterConfig meterConfig;
Frank Wangd7e3b4b2017-09-24 13:37:54 +0900289 PiMeterCellId piCellId;
290
Carmelo Cascone81929aa2018-04-07 01:38:55 -0700291 if (entity.getEntityCase() == METER_ENTRY) {
292 String meterName = browser.meters()
293 .getById(entity.getCounterEntry().getCounterId())
294 .getPreamble()
295 .getName();
296 piCellId = PiMeterCellId.ofIndirect(
297 PiMeterId.of(meterName),
298 entity.getMeterEntry().getIndex().getIndex());
299 meterConfig = entity.getMeterEntry().getConfig();
300 } else if (entity.getEntityCase() == DIRECT_METER_ENTRY) {
301 PiTableEntry piTableEntry = TableEntryEncoder.decode(
302 entity.getDirectMeterEntry().getTableEntry(),
303 pipeconf);
304 piCellId = PiMeterCellId.ofDirect(piTableEntry);
305 meterConfig = entity.getDirectMeterEntry().getConfig();
306 } else {
307 throw new EncodeException(format(
308 "unrecognized entity type '%s' in P4Runtime message",
309 entity.getEntityCase().name()));
Frank Wangd7e3b4b2017-09-24 13:37:54 +0900310 }
311
Carmelo Cascone81929aa2018-04-07 01:38:55 -0700312 return PiMeterCellConfig.builder()
313 .withMeterCellId(piCellId)
314 .withMeterBand(new PiMeterBand(meterConfig.getCir(),
315 meterConfig.getCburst()))
316 .withMeterBand(new PiMeterBand(meterConfig.getPir(),
317 meterConfig.getPburst()))
318 .build();
Frank Wangd7e3b4b2017-09-24 13:37:54 +0900319 }
Carmelo Cascone81929aa2018-04-07 01:38:55 -0700320}