blob: c22d612e67a01c55fe10e9d42ecb9316a902bdcd [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
19import org.onosproject.net.pi.model.PiPipeconf;
20import org.onosproject.net.pi.runtime.PiCounterCellData;
21import org.onosproject.net.pi.runtime.PiCounterCellId;
22import org.onosproject.net.pi.runtime.PiCounterId;
23import org.onosproject.net.pi.runtime.PiDirectCounterCellId;
24import org.onosproject.net.pi.runtime.PiIndirectCounterCellId;
25import org.onosproject.net.pi.runtime.PiTableEntry;
26import org.slf4j.Logger;
27import p4.P4RuntimeOuterClass.CounterData;
28import p4.P4RuntimeOuterClass.CounterEntry;
29import p4.P4RuntimeOuterClass.DirectCounterEntry;
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.COUNTER_ENTRY;
40import static p4.P4RuntimeOuterClass.Entity.EntityCase.DIRECT_COUNTER_ENTRY;
41
42/**
43 * Encoder/decoder of PI counter IDs to counter entry protobuf messages, and vice versa.
44 */
45final class CounterEntryCodec {
46
47 private static final Logger log = getLogger(CounterEntryCodec.class);
48
49 private CounterEntryCodec() {
50 // Hides constructor.
51 }
52
53 /**
54 * Returns a collection of P4Runtime entity protobuf messages describing both counter or direct counter entries,
55 * encoded from the given collection of PI counter cell identifiers, for the given pipeconf. If a PI counter cell
56 * identifier cannot be encoded, it is skipped, hence the returned collection might have different size than the
57 * input one.
58 * <p>
59 * This method takes as parameter also a map between numeric P4Info IDs and PI counter IDs, that will be populated
60 * during the process and that is then needed to aid in the decode process.
61 *
62 * @param cellIds counter cell identifiers
63 * @param counterIdMap counter ID map (empty, it will be populated during this method execution)
64 * @param pipeconf pipeconf
65 * @return collection of entity messages describing both counter or direct counter entries
66 */
67 static Collection<Entity> encodePiCounterCellIds(Collection<PiCounterCellId> cellIds,
68 Map<Integer, PiCounterId> counterIdMap,
69 PiPipeconf pipeconf) {
70 return cellIds
71 .stream()
72 .map(cellId -> {
73 try {
74 return encodePiCounterCellId(cellId, counterIdMap, pipeconf);
75 } catch (P4InfoBrowser.NotFoundException | EncodeException e) {
76 log.warn("Unable to encode PI counter cell id: {}", e.getMessage());
77 return null;
78 }
79 })
80 .filter(Objects::nonNull)
81 .collect(Collectors.toList());
82 }
83
84 /**
85 * Returns a collection of PI counter cell data, decoded from the given P4Runtime entity protobuf messages
86 * describing both counter or direct counter entries, for the given counter ID map (populated by {@link
87 * #encodePiCounterCellIds(Collection, Map, PiPipeconf)}), and pipeconf. If an entity message cannot be encoded, it
88 * is skipped, hence the returned collection might have different size than the input one.
89 *
90 * @param entities P4Runtime entity messages
91 * @param counterIdMap counter ID map (previously populated)
92 * @param pipeconf pipeconf
93 * @return collection of PI counter cell data
94 */
95 static Collection<PiCounterCellData> decodeCounterEntities(Collection<Entity> entities,
96 Map<Integer, PiCounterId> counterIdMap,
97 PiPipeconf pipeconf) {
98 return entities
99 .stream()
100 .filter(entity -> entity.getEntityCase() == COUNTER_ENTRY ||
101 entity.getEntityCase() == DIRECT_COUNTER_ENTRY)
102 .map(entity -> {
103 try {
104 return decodeCounterEntity(entity, counterIdMap, pipeconf);
105 } catch (EncodeException | P4InfoBrowser.NotFoundException e) {
106 log.warn("Unable to decode counter entity message: {}", e.getMessage());
107 return null;
108 }
109 })
110 .filter(Objects::nonNull)
111 .collect(Collectors.toList());
112 }
113
114 private static Entity encodePiCounterCellId(PiCounterCellId cellId, Map<Integer, PiCounterId> counterIdMap,
115 PiPipeconf pipeconf)
116 throws P4InfoBrowser.NotFoundException, EncodeException {
117
118 final P4InfoBrowser browser = PipeconfHelper.getP4InfoBrowser(pipeconf);
119
120 int counterId;
121 Entity entity;
122 // Encode PI cell ID into entity message and add to read request.
123 switch (cellId.type()) {
124 case INDIRECT:
Carmelo Casconecb0a49c2017-10-03 14:32:23 +0200125 counterId = browser.counters().getByName(cellId.counterId().id()).getPreamble().getId();
Carmelo Cascone7f75be42017-09-07 14:37:02 +0200126 PiIndirectCounterCellId indCellId = (PiIndirectCounterCellId) cellId;
127 entity = Entity.newBuilder().setCounterEntry(CounterEntry.newBuilder()
128 .setCounterId(counterId)
129 .setIndex(indCellId.index())
130 .build())
131 .build();
132 break;
133 case DIRECT:
Carmelo Casconecb0a49c2017-10-03 14:32:23 +0200134 counterId = browser.directCounters().getByName(cellId.counterId().id()).getPreamble().getId();
Carmelo Cascone7f75be42017-09-07 14:37:02 +0200135 PiDirectCounterCellId dirCellId = (PiDirectCounterCellId) cellId;
136 DirectCounterEntry.Builder entryBuilder = DirectCounterEntry.newBuilder().setCounterId(counterId);
137 if (!dirCellId.tableEntry().equals(PiTableEntry.EMTPY)) {
138 entryBuilder.setTableEntry(TableEntryEncoder.encode(dirCellId.tableEntry(), pipeconf));
139 }
140 entity = Entity.newBuilder().setDirectCounterEntry(entryBuilder.build()).build();
141 break;
142 default:
143 throw new EncodeException(format("Unrecognized PI counter cell ID type '%s'", cellId.type()));
144 }
145 counterIdMap.put(counterId, cellId.counterId());
146
147 return entity;
148 }
149
150 private static PiCounterCellData decodeCounterEntity(Entity entity, Map<Integer, PiCounterId> counterIdMap,
151 PiPipeconf pipeconf)
152 throws EncodeException, P4InfoBrowser.NotFoundException {
153
154 int counterId;
155 CounterData counterData;
156
157 if (entity.getEntityCase() == COUNTER_ENTRY) {
158 counterId = entity.getCounterEntry().getCounterId();
159 counterData = entity.getCounterEntry().getData();
160 } else {
161 counterId = entity.getDirectCounterEntry().getCounterId();
162 counterData = entity.getDirectCounterEntry().getData();
163 }
164
165 // Process only counter IDs that were requested in the first place.
166 if (!counterIdMap.containsKey(counterId)) {
167 throw new EncodeException(format("Unrecognized counter ID '%s'", counterId));
168 }
169
170 PiCounterId piCounterId = counterIdMap.get(counterId);
171
172 // Compute PI cell ID.
173 PiCounterCellId piCellId;
174
175 switch (piCounterId.type()) {
176 case INDIRECT:
177 if (entity.getEntityCase() != COUNTER_ENTRY) {
178 throw new EncodeException(format(
179 "Counter ID '%s' is indirect, but processed entity is %s",
180 piCounterId, entity.getEntityCase()));
181 }
182 piCellId = PiIndirectCounterCellId.of(piCounterId,
183 entity.getCounterEntry().getIndex());
184 break;
185 case DIRECT:
186 if (entity.getEntityCase() != DIRECT_COUNTER_ENTRY) {
187 throw new EncodeException(format(
188 "Counter ID '%s' is direct, but processed entity is %s",
189 piCounterId, entity.getEntityCase()));
190 }
191 PiTableEntry piTableEntry = TableEntryEncoder.decode(entity.getDirectCounterEntry().getTableEntry(),
192 pipeconf);
193 piCellId = PiDirectCounterCellId.of(piCounterId, piTableEntry);
194 break;
195 default:
196 throw new EncodeException(format("Unrecognized PI counter ID type '%s'", piCounterId.type()));
197 }
198
199 return new PiCounterCellData(piCellId, counterData.getPacketCount(), counterData.getByteCount());
200 }
201}