blob: df4e72f1b11e8ee12b9108f8e6d4b7d61260a7db [file] [log] [blame]
Carmelo Cascone7f75be42017-09-07 14:37:02 +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.p4runtime.ctl;
18
Carmelo Cascone87892e22017-11-13 16:01:29 -080019import org.onosproject.net.pi.model.PiCounterId;
20import org.onosproject.net.pi.model.PiCounterType;
Carmelo Cascone7f75be42017-09-07 14:37:02 +020021import org.onosproject.net.pi.model.PiPipeconf;
22import org.onosproject.net.pi.runtime.PiCounterCellData;
23import org.onosproject.net.pi.runtime.PiCounterCellId;
Carmelo Cascone7f75be42017-09-07 14:37:02 +020024import org.onosproject.net.pi.runtime.PiTableEntry;
25import org.slf4j.Logger;
26import p4.P4RuntimeOuterClass.CounterData;
27import p4.P4RuntimeOuterClass.CounterEntry;
28import p4.P4RuntimeOuterClass.DirectCounterEntry;
29import p4.P4RuntimeOuterClass.Entity;
30
31import java.util.Collection;
32import java.util.Map;
33import java.util.Objects;
34import java.util.stream.Collectors;
35
36import static java.lang.String.format;
37import static org.slf4j.LoggerFactory.getLogger;
38import static p4.P4RuntimeOuterClass.Entity.EntityCase.COUNTER_ENTRY;
39import static p4.P4RuntimeOuterClass.Entity.EntityCase.DIRECT_COUNTER_ENTRY;
40
41/**
42 * Encoder/decoder of PI counter IDs to counter entry protobuf messages, and vice versa.
43 */
44final class CounterEntryCodec {
45
46 private static final Logger log = getLogger(CounterEntryCodec.class);
47
48 private CounterEntryCodec() {
49 // Hides constructor.
50 }
51
52 /**
53 * Returns a collection of P4Runtime entity protobuf messages describing both counter or direct counter entries,
54 * encoded from the given collection of PI counter cell identifiers, for the given pipeconf. If a PI counter cell
55 * identifier 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 counter IDs, that will be populated
59 * during the process and that is then needed to aid in the decode process.
60 *
61 * @param cellIds counter cell identifiers
62 * @param counterIdMap counter ID map (empty, it will be populated during this method execution)
63 * @param pipeconf pipeconf
64 * @return collection of entity messages describing both counter or direct counter entries
65 */
66 static Collection<Entity> encodePiCounterCellIds(Collection<PiCounterCellId> cellIds,
67 Map<Integer, PiCounterId> counterIdMap,
68 PiPipeconf pipeconf) {
69 return cellIds
70 .stream()
71 .map(cellId -> {
72 try {
73 return encodePiCounterCellId(cellId, counterIdMap, pipeconf);
74 } catch (P4InfoBrowser.NotFoundException | EncodeException e) {
75 log.warn("Unable to encode PI counter cell id: {}", e.getMessage());
76 return null;
77 }
78 })
79 .filter(Objects::nonNull)
80 .collect(Collectors.toList());
81 }
82
83 /**
84 * Returns a collection of PI counter cell data, decoded from the given P4Runtime entity protobuf messages
85 * describing both counter or direct counter entries, for the given counter ID map (populated by {@link
86 * #encodePiCounterCellIds(Collection, Map, PiPipeconf)}), and pipeconf. If an entity message cannot be encoded, it
87 * is skipped, hence the returned collection might have different size than the input one.
88 *
89 * @param entities P4Runtime entity messages
90 * @param counterIdMap counter ID map (previously populated)
91 * @param pipeconf pipeconf
92 * @return collection of PI counter cell data
93 */
94 static Collection<PiCounterCellData> decodeCounterEntities(Collection<Entity> entities,
95 Map<Integer, PiCounterId> counterIdMap,
96 PiPipeconf pipeconf) {
97 return entities
98 .stream()
99 .filter(entity -> entity.getEntityCase() == COUNTER_ENTRY ||
100 entity.getEntityCase() == DIRECT_COUNTER_ENTRY)
101 .map(entity -> {
102 try {
103 return decodeCounterEntity(entity, counterIdMap, pipeconf);
104 } catch (EncodeException | P4InfoBrowser.NotFoundException e) {
105 log.warn("Unable to decode counter entity message: {}", e.getMessage());
106 return null;
107 }
108 })
109 .filter(Objects::nonNull)
110 .collect(Collectors.toList());
111 }
112
113 private static Entity encodePiCounterCellId(PiCounterCellId cellId, Map<Integer, PiCounterId> counterIdMap,
114 PiPipeconf pipeconf)
115 throws P4InfoBrowser.NotFoundException, EncodeException {
116
117 final P4InfoBrowser browser = PipeconfHelper.getP4InfoBrowser(pipeconf);
118
119 int counterId;
120 Entity entity;
121 // Encode PI cell ID into entity message and add to read request.
Carmelo Cascone87892e22017-11-13 16:01:29 -0800122 switch (cellId.counterType()) {
Carmelo Cascone7f75be42017-09-07 14:37:02 +0200123 case INDIRECT:
Carmelo Casconecb0a49c2017-10-03 14:32:23 +0200124 counterId = browser.counters().getByName(cellId.counterId().id()).getPreamble().getId();
Carmelo Cascone7f75be42017-09-07 14:37:02 +0200125 entity = Entity.newBuilder().setCounterEntry(CounterEntry.newBuilder()
126 .setCounterId(counterId)
Carmelo Cascone87892e22017-11-13 16:01:29 -0800127 .setIndex(cellId.index())
Carmelo Cascone7f75be42017-09-07 14:37:02 +0200128 .build())
129 .build();
130 break;
131 case DIRECT:
Carmelo Casconecb0a49c2017-10-03 14:32:23 +0200132 counterId = browser.directCounters().getByName(cellId.counterId().id()).getPreamble().getId();
Carmelo Cascone7f75be42017-09-07 14:37:02 +0200133 DirectCounterEntry.Builder entryBuilder = DirectCounterEntry.newBuilder().setCounterId(counterId);
Carmelo Cascone87892e22017-11-13 16:01:29 -0800134 if (!cellId.tableEntry().equals(PiTableEntry.EMTPY)) {
135 entryBuilder.setTableEntry(TableEntryEncoder.encode(cellId.tableEntry(), pipeconf));
Carmelo Cascone7f75be42017-09-07 14:37:02 +0200136 }
137 entity = Entity.newBuilder().setDirectCounterEntry(entryBuilder.build()).build();
138 break;
139 default:
Carmelo Cascone87892e22017-11-13 16:01:29 -0800140 throw new EncodeException(format("Unrecognized PI counter cell ID type '%s'", cellId.counterType()));
Carmelo Cascone7f75be42017-09-07 14:37:02 +0200141 }
142 counterIdMap.put(counterId, cellId.counterId());
143
144 return entity;
145 }
146
147 private static PiCounterCellData decodeCounterEntity(Entity entity, Map<Integer, PiCounterId> counterIdMap,
148 PiPipeconf pipeconf)
149 throws EncodeException, P4InfoBrowser.NotFoundException {
150
151 int counterId;
152 CounterData counterData;
153
154 if (entity.getEntityCase() == COUNTER_ENTRY) {
155 counterId = entity.getCounterEntry().getCounterId();
156 counterData = entity.getCounterEntry().getData();
157 } else {
158 counterId = entity.getDirectCounterEntry().getCounterId();
159 counterData = entity.getDirectCounterEntry().getData();
160 }
161
162 // Process only counter IDs that were requested in the first place.
163 if (!counterIdMap.containsKey(counterId)) {
164 throw new EncodeException(format("Unrecognized counter ID '%s'", counterId));
165 }
166
167 PiCounterId piCounterId = counterIdMap.get(counterId);
168
Carmelo Cascone87892e22017-11-13 16:01:29 -0800169 if (!pipeconf.pipelineModel().counter(piCounterId).isPresent()) {
170 throw new EncodeException(format(
171 "Unable to find counter '%s' in pipeline model", counterId));
172 }
173 PiCounterType piCounterType = pipeconf.pipelineModel().counter(piCounterId).get().counterType();
174
Carmelo Cascone7f75be42017-09-07 14:37:02 +0200175 // Compute PI cell ID.
176 PiCounterCellId piCellId;
177
Carmelo Cascone87892e22017-11-13 16:01:29 -0800178 switch (piCounterType) {
Carmelo Cascone7f75be42017-09-07 14:37:02 +0200179 case INDIRECT:
180 if (entity.getEntityCase() != COUNTER_ENTRY) {
181 throw new EncodeException(format(
182 "Counter ID '%s' is indirect, but processed entity is %s",
183 piCounterId, entity.getEntityCase()));
184 }
Carmelo Cascone87892e22017-11-13 16:01:29 -0800185 piCellId = PiCounterCellId.ofIndirect(piCounterId,
Carmelo Cascone7f75be42017-09-07 14:37:02 +0200186 entity.getCounterEntry().getIndex());
187 break;
188 case DIRECT:
189 if (entity.getEntityCase() != DIRECT_COUNTER_ENTRY) {
190 throw new EncodeException(format(
191 "Counter ID '%s' is direct, but processed entity is %s",
192 piCounterId, entity.getEntityCase()));
193 }
194 PiTableEntry piTableEntry = TableEntryEncoder.decode(entity.getDirectCounterEntry().getTableEntry(),
195 pipeconf);
Carmelo Cascone87892e22017-11-13 16:01:29 -0800196 piCellId = PiCounterCellId.ofDirect(piCounterId, piTableEntry);
Carmelo Cascone7f75be42017-09-07 14:37:02 +0200197 break;
198 default:
Carmelo Cascone87892e22017-11-13 16:01:29 -0800199 throw new EncodeException(format("Unrecognized PI counter ID type '%s'", piCounterType));
Carmelo Cascone7f75be42017-09-07 14:37:02 +0200200 }
201
202 return new PiCounterCellData(piCellId, counterData.getPacketCount(), counterData.getByteCount());
203 }
204}