blob: a76ab7c4d8c123c1a87984408d60ed5021e9246f [file] [log] [blame]
Carmelo Cascone99c59db2019-01-17 15:39:35 -08001/*
2 * Copyright 2019-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 com.google.protobuf.Message;
20import com.google.protobuf.TextFormat;
21import org.onosproject.net.pi.model.PiPipeconf;
22import org.onosproject.net.pi.runtime.PiEntity;
23import org.slf4j.Logger;
24
25import java.util.List;
26import java.util.Objects;
27import java.util.stream.Collectors;
28
29import static com.google.common.base.Preconditions.checkNotNull;
30import static java.lang.String.format;
31import static org.slf4j.LoggerFactory.getLogger;
32
33/**
34 * Abstract implementation of a codec that translates PI entities into P4Runtime
35 * protobuf messages and vice versa.
36 *
37 * @param <P> PI entity class
38 * @param <M> P4Runtime protobuf message class
39 */
40abstract class AbstractP4RuntimeCodec<P extends PiEntity, M extends Message> {
41
42 protected final Logger log = getLogger(this.getClass());
43
44 protected abstract M encode(P piEntity, PiPipeconf pipeconf,
45 P4InfoBrowser browser)
46 throws CodecException, P4InfoBrowser.NotFoundException;
47
48 protected abstract P decode(M message, PiPipeconf pipeconf,
49 P4InfoBrowser browser)
50 throws CodecException, P4InfoBrowser.NotFoundException;
51
52 /**
53 * Returns a P4Runtime protobuf message that is equivalent to the given PI
54 * entity for the given pipeconf.
55 *
56 * @param piEntity PI entity instance
57 * @param pipeconf pipeconf
58 * @return P4Runtime protobuf message
59 * @throws CodecException if the given PI entity cannot be encoded (see
60 * exception message)
61 */
62 public M encode(P piEntity, PiPipeconf pipeconf)
63 throws CodecException {
64 try {
65 return encode(piEntity, pipeconf, browserOrFail(pipeconf));
66 } catch (P4InfoBrowser.NotFoundException e) {
67 throw new CodecException(e.getMessage());
68 }
69 }
70
71 /**
72 * Returns a PI entity instance that is equivalent to the P4Runtime protobuf
73 * message for the given pipeconf.
74 *
75 * @param message P4Runtime protobuf message
76 * @param pipeconf pipeconf pipeconf
77 * @return PI entity instance
78 * @throws CodecException if the given protobuf message cannot be decoded
79 * (see exception message)
80 */
81 public P decode(M message, PiPipeconf pipeconf)
82 throws CodecException {
83 try {
84 return decode(message, pipeconf, browserOrFail(pipeconf));
85 } catch (P4InfoBrowser.NotFoundException e) {
86 throw new CodecException(e.getMessage());
87 }
88 }
89
90 /**
91 * Same as {@link #encode(PiEntity, PiPipeconf)} but returns null in case of
92 * exceptions, while the error message is logged.
93 *
94 * @param piEntity PI entity instance
95 * @param pipeconf pipeconf
96 * @return P4Runtime protobuf message
97 */
98 public M encodeOrNull(P piEntity, PiPipeconf pipeconf) {
99 try {
100 return encode(piEntity, pipeconf);
101 } catch (CodecException e) {
102 log.error("Unable to encode {}: {} [{}]",
103 piEntity.getClass().getSimpleName(),
104 e.getMessage(), piEntity.toString());
105 return null;
106 }
107 }
108
109 /**
110 * Same as {@link #decode(Message, PiPipeconf)} but returns null in case of
111 * exceptions, while the error message is logged.
112 *
113 * @param message P4Runtime protobuf message
114 * @param pipeconf pipeconf pipeconf
115 * @return PI entity instance
116 */
117 public P decodeOrNull(M message, PiPipeconf pipeconf) {
118 try {
119 return decode(message, pipeconf);
120 } catch (CodecException e) {
121 log.error("Unable to decode {}: {} [{}]",
122 message.getClass().getSimpleName(),
123 e.getMessage(), TextFormat.shortDebugString(message));
124 return null;
125 }
126 }
127
128 /**
129 * Encodes the given list of PI entities, skipping those that cannot be
130 * encoded, in which case an error message is logged. For this reason, the
131 * returned list might have different size than the returned one.
132 *
133 * @param piEntities list of PI entities
134 * @param pipeconf pipeconf
135 * @return list of P4Runtime protobuf messages
136 */
137 public List<M> encodeAll(List<P> piEntities, PiPipeconf pipeconf) {
138 checkNotNull(piEntities);
139 return piEntities.stream()
140 .map(p -> encodeOrNull(p, pipeconf))
141 .filter(Objects::nonNull)
142 .collect(Collectors.toList());
143 }
144
145 /**
146 * Decodes the given list of P4Runtime protobuf messages, skipping those
147 * that cannot be decoded, on which case an error message is logged. For
148 * this reason, the returned list might have different size than the
149 * returned one.
150 *
151 * @param messages list of protobuf messages
152 * @param pipeconf pipeconf
153 * @return list of PI entities
154 */
155 public List<P> decodeAll(List<M> messages, PiPipeconf pipeconf) {
156 checkNotNull(messages);
157 return messages.stream()
158 .map(m -> decodeOrNull(m, pipeconf))
159 .filter(Objects::nonNull)
160 .collect(Collectors.toList());
161 }
162
163 /**
164 * Same as {@link #encodeAll(List, PiPipeconf)} but throws an exception if
165 * one or more of the given PI entities cannot be encoded. The returned list
166 * is guaranteed to have same size and order as the given one.
167 *
168 * @param piEntities list of PI entities
169 * @param pipeconf pipeconf
170 * @return list of protobuf messages
171 * @throws CodecException if one or more of the given PI entities cannot be
172 * encoded
173 */
174 public List<M> encodeAllOrFail(List<P> piEntities, PiPipeconf pipeconf)
175 throws CodecException {
176 final List<M> messages = encodeAll(piEntities, pipeconf);
177 if (piEntities.size() != messages.size()) {
178 throw new CodecException(format(
179 "Unable to encode %d entities of %d given " +
180 "(see previous logs for details)",
181 piEntities.size() - messages.size(), piEntities.size()));
182 }
183 return messages;
184 }
185
186 /**
187 * Same as {@link #decodeAll(List, PiPipeconf)} but throws an exception if
188 * one or more of the given protobuf messages cannot be decoded. The
189 * returned list is guaranteed to have same size and order as the given
190 * one.
191 *
192 * @param messages list of protobuf messages
193 * @param pipeconf pipeconf
194 * @return list of PI entities
195 * @throws CodecException if one or more of the given protobuf messages
196 * cannot be decoded
197 */
198 public List<P> decodeAllOrFail(List<M> messages, PiPipeconf pipeconf)
199 throws CodecException {
200 final List<P> piEntities = decodeAll(messages, pipeconf);
201 if (messages.size() != piEntities.size()) {
202 throw new CodecException(format(
203 "Unable to decode %d messages of %d given " +
204 "(see previous logs for details)",
205 messages.size() - piEntities.size(), messages.size()));
206 }
207 return piEntities;
208 }
209
210 private P4InfoBrowser browserOrFail(PiPipeconf pipeconf) throws CodecException {
211 final P4InfoBrowser browser = PipeconfHelper.getP4InfoBrowser(pipeconf);
212 if (browser == null) {
213 throw new CodecException(format(
214 "Unable to get P4InfoBrowser for pipeconf %s", pipeconf.id()));
215 }
216 return browser;
217 }
218}