Extract and publish p4runtime protocol utils in separate artifact
For consumption by third-party apps built via mvn
Change-Id: Ic9c1eccb2519b2952cdb632e1f07ccefbba00396
diff --git a/protocols/p4runtime/utils/BUILD b/protocols/p4runtime/utils/BUILD
new file mode 100644
index 0000000..a521bbb
--- /dev/null
+++ b/protocols/p4runtime/utils/BUILD
@@ -0,0 +1,8 @@
+COMPILE_DEPS = CORE_DEPS + [
+ "//protocols/p4runtime/proto:onos-protocols-p4runtime-proto",
+ "//deps:com_google_protobuf_protobuf_java",
+]
+
+osgi_jar_with_tests(
+ deps = COMPILE_DEPS,
+)
diff --git a/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/codec/AbstractCodec.java b/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/codec/AbstractCodec.java
new file mode 100644
index 0000000..2bb75a3
--- /dev/null
+++ b/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/codec/AbstractCodec.java
@@ -0,0 +1,258 @@
+/*
+ * Copyright 2019-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.p4runtime.ctl.codec;
+
+import com.google.common.collect.ImmutableList;
+import com.google.protobuf.Message;
+import com.google.protobuf.TextFormat;
+import org.onosproject.net.pi.model.PiPipeconf;
+import org.onosproject.p4runtime.ctl.utils.P4InfoBrowser;
+import org.onosproject.p4runtime.ctl.utils.PipeconfHelper;
+import org.slf4j.Logger;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static java.lang.String.format;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Abstract implementation of a general codec that translates pipeconf-related
+ * objects into protobuf messages and vice versa.
+ *
+ * @param <P> object
+ * @param <M> protobuf message class
+ * @param <X> metadata class
+ */
+abstract class AbstractCodec<P, M extends Message, X> {
+
+ protected final Logger log = getLogger(this.getClass());
+
+ protected abstract M encode(P object, X metadata, PiPipeconf pipeconf,
+ P4InfoBrowser browser)
+ throws CodecException, P4InfoBrowser.NotFoundException;
+
+ protected abstract P decode(M message, X metadata, PiPipeconf pipeconf,
+ P4InfoBrowser browser)
+ throws CodecException, P4InfoBrowser.NotFoundException;
+
+ /**
+ * Returns a protobuf message that is equivalent to the given object for the
+ * given metadata and pipeconf.
+ *
+ * @param object object
+ * @param metadata metadata
+ * @param pipeconf pipeconf
+ * @return protobuf message
+ * @throws CodecException if the given object cannot be encoded (see
+ * exception message)
+ */
+ public M encode(P object, X metadata, PiPipeconf pipeconf)
+ throws CodecException {
+ checkNotNull(object);
+ try {
+ return encode(object, metadata, pipeconf, browserOrFail(pipeconf));
+ } catch (P4InfoBrowser.NotFoundException e) {
+ throw new CodecException(e.getMessage());
+ }
+ }
+
+ /**
+ * Returns a object that is equivalent to the protobuf message for the given
+ * metadata and pipeconf.
+ *
+ * @param message protobuf message
+ * @param metadata metadata
+ * @param pipeconf pipeconf pipeconf
+ * @return object
+ * @throws CodecException if the given protobuf message cannot be decoded
+ * (see exception message)
+ */
+ public P decode(M message, X metadata, PiPipeconf pipeconf)
+ throws CodecException {
+ checkNotNull(message);
+ try {
+ return decode(message, metadata, pipeconf, browserOrFail(pipeconf));
+ } catch (P4InfoBrowser.NotFoundException e) {
+ throw new CodecException(e.getMessage());
+ }
+ }
+
+ /**
+ * Same as {@link #encode(Object, Object, PiPipeconf)} but returns null in
+ * case of exceptions, while the error message is logged.
+ *
+ * @param object object
+ * @param metadata metadata
+ * @param pipeconf pipeconf
+ * @return protobuf message
+ */
+ private M encodeOrNull(P object, X metadata, PiPipeconf pipeconf) {
+ checkNotNull(object);
+ try {
+ return encode(object, metadata, pipeconf);
+ } catch (CodecException e) {
+ log.error("Unable to encode {}: {} [{}]",
+ object.getClass().getSimpleName(),
+ e.getMessage(), object.toString());
+ return null;
+ }
+ }
+
+ /**
+ * Same as {@link #decode(Message, Object, PiPipeconf)} but returns null in
+ * case of exceptions, while the error message is logged.
+ *
+ * @param message protobuf message
+ * @param metadata metadata
+ * @param pipeconf pipeconf pipeconf
+ * @return object
+ */
+ private P decodeOrNull(M message, X metadata, PiPipeconf pipeconf) {
+ checkNotNull(message);
+ try {
+ return decode(message, metadata, pipeconf);
+ } catch (CodecException e) {
+ log.error("Unable to decode {}: {} [{}]",
+ message.getClass().getSimpleName(),
+ e.getMessage(), TextFormat.shortDebugString(message));
+ return null;
+ }
+ }
+
+ /**
+ * Encodes the given list of objects, skipping those that cannot be encoded,
+ * in which case an error message is logged. For this reason, the returned
+ * list might have different size than the returned one.
+ *
+ * @param objects list of objects
+ * @param metadata metadata
+ * @param pipeconf pipeconf
+ * @return list of protobuf messages
+ */
+ private List<M> encodeAllSkipException(
+ Collection<P> objects, X metadata, PiPipeconf pipeconf) {
+ checkNotNull(objects);
+ if (objects.isEmpty()) {
+ return ImmutableList.of();
+ }
+ return objects.stream()
+ .map(p -> encodeOrNull(p, metadata, pipeconf))
+ .filter(Objects::nonNull)
+ .collect(Collectors.toList());
+ }
+
+ /**
+ * Decodes the given list of protobuf messages, skipping those that cannot
+ * be decoded, on which case an error message is logged. For this reason,
+ * the returned list might have different size than the returned one.
+ *
+ * @param messages list of protobuf messages
+ * @param metadata metadata
+ * @param pipeconf pipeconf
+ * @return list of objects
+ */
+ private List<P> decodeAllSkipException(
+ Collection<M> messages, X metadata, PiPipeconf pipeconf) {
+ checkNotNull(messages);
+ if (messages.isEmpty()) {
+ return ImmutableList.of();
+ }
+ return messages.stream()
+ .map(m -> decodeOrNull(m, metadata, pipeconf))
+ .filter(Objects::nonNull)
+ .collect(Collectors.toList());
+ }
+
+ /**
+ * Encodes the given collection of objects. Throws an exception if one or
+ * more of the given objects cannot be encoded. The returned list is
+ * guaranteed to have same size and order as the given one.
+ *
+ * @param objects list of objects
+ * @param metadata metadata
+ * @param pipeconf pipeconf
+ * @return list of protobuf messages
+ * @throws CodecException if one or more of the given objects cannot be
+ * encoded
+ */
+ List<M> encodeAll(Collection<P> objects, X metadata, PiPipeconf pipeconf)
+ throws CodecException {
+ checkNotNull(objects);
+ if (objects.isEmpty()) {
+ return ImmutableList.of();
+ }
+ final List<M> messages = encodeAllSkipException(objects, metadata, pipeconf);
+ if (objects.size() != messages.size()) {
+ throw new CodecException(format(
+ "Unable to encode %d entities of %d given " +
+ "(see previous logs for details)",
+ objects.size() - messages.size(), objects.size()));
+ }
+ return messages;
+ }
+
+ /**
+ * Decodes the given collection of protobuf messages. Throws an exception if
+ * one or more of the given protobuf messages cannot be decoded. The
+ * returned list is guaranteed to have same size and order as the given
+ * one.
+ *
+ * @param messages list of protobuf messages
+ * @param metadata metadata
+ * @param pipeconf pipeconf
+ * @return list of objects
+ * @throws CodecException if one or more of the given protobuf messages
+ * cannot be decoded
+ */
+ List<P> decodeAll(Collection<M> messages, X metadata, PiPipeconf pipeconf)
+ throws CodecException {
+ checkNotNull(messages);
+ if (messages.isEmpty()) {
+ return ImmutableList.of();
+ }
+ final List<P> objects = decodeAllSkipException(messages, metadata, pipeconf);
+ if (messages.size() != objects.size()) {
+ throw new CodecException(format(
+ "Unable to decode %d messages of %d given " +
+ "(see previous logs for details)",
+ messages.size() - objects.size(), messages.size()));
+ }
+ return objects;
+ }
+
+ /**
+ * Returns a P4Info browser for the given pipeconf or throws a
+ * CodecException if not possible.
+ *
+ * @param pipeconf pipeconf
+ * @return P4Info browser
+ * @throws CodecException if a P4Info browser cannot be obtained
+ */
+ P4InfoBrowser browserOrFail(PiPipeconf pipeconf) throws CodecException {
+ checkNotNull(pipeconf);
+ final P4InfoBrowser browser = PipeconfHelper.getP4InfoBrowser(pipeconf);
+ if (browser == null) {
+ throw new CodecException(format(
+ "Unable to get P4InfoBrowser for pipeconf %s", pipeconf.id()));
+ }
+ return browser;
+ }
+}
diff --git a/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/codec/AbstractEntityCodec.java b/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/codec/AbstractEntityCodec.java
new file mode 100644
index 0000000..0b39367
--- /dev/null
+++ b/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/codec/AbstractEntityCodec.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2019-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.p4runtime.ctl.codec;
+
+import com.google.protobuf.Message;
+import org.onosproject.net.pi.model.PiPipeconf;
+import org.onosproject.net.pi.runtime.PiEntity;
+import org.onosproject.net.pi.runtime.PiHandle;
+import org.onosproject.p4runtime.ctl.utils.P4InfoBrowser;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Abstract implementation of a specialized codec that translates PI runtime
+ * entities and their handles into P4Runtime protobuf messages and vice versa.
+ * Supports also encoding to "key" P4Runtime Entity messages used in read and
+ * delete operations.
+ *
+ * @param <P> PI runtime class
+ * @param <H> PI handle class
+ * @param <M> P4Runtime protobuf message class
+ * @param <X> metadata class
+ */
+public abstract class AbstractEntityCodec
+ <P extends PiEntity, H extends PiHandle, M extends Message, X>
+ extends AbstractCodec<P, M, X> {
+
+ protected abstract M encodeKey(H handle, X metadata, PiPipeconf pipeconf,
+ P4InfoBrowser browser)
+ throws CodecException, P4InfoBrowser.NotFoundException;
+
+ protected abstract M encodeKey(P piEntity, X metadata, PiPipeconf pipeconf,
+ P4InfoBrowser browser)
+ throws CodecException, P4InfoBrowser.NotFoundException;
+
+ /**
+ * Returns a P4Runtime protobuf message representing the P4Runtime.Entity
+ * "key" for the given PI handle, metadata and pipeconf.
+ *
+ * @param handle PI handle instance
+ * @param metadata metadata
+ * @param pipeconf pipeconf
+ * @return P4Runtime protobuf message
+ * @throws CodecException if the given PI entity cannot be encoded (see
+ * exception message)
+ */
+ public M encodeKey(H handle, X metadata, PiPipeconf pipeconf)
+ throws CodecException {
+ checkNotNull(handle);
+ try {
+ return encodeKey(handle, metadata, pipeconf, browserOrFail(pipeconf));
+ } catch (P4InfoBrowser.NotFoundException e) {
+ throw new CodecException(e.getMessage());
+ }
+ }
+
+ /**
+ * Returns a P4Runtime protobuf message representing the P4Runtime.Entity
+ * "key" for the given PI entity, metadata and pipeconf.
+ *
+ * @param piEntity PI entity instance
+ * @param metadata metadata
+ * @param pipeconf pipeconf
+ * @return P4Runtime protobuf message
+ * @throws CodecException if the given PI entity cannot be encoded (see
+ * exception message)
+ */
+ public M encodeKey(P piEntity, X metadata, PiPipeconf pipeconf)
+ throws CodecException {
+ checkNotNull(piEntity);
+ try {
+ return encodeKey(piEntity, metadata, pipeconf, browserOrFail(pipeconf));
+ } catch (P4InfoBrowser.NotFoundException e) {
+ throw new CodecException(e.getMessage());
+ }
+ }
+}
diff --git a/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/codec/ActionCodec.java b/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/codec/ActionCodec.java
new file mode 100644
index 0000000..3ae46c9
--- /dev/null
+++ b/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/codec/ActionCodec.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2019-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.p4runtime.ctl.codec;
+
+import com.google.protobuf.ByteString;
+import org.onlab.util.ImmutableByteSequence;
+import org.onosproject.net.pi.model.PiActionId;
+import org.onosproject.net.pi.model.PiActionParamId;
+import org.onosproject.net.pi.model.PiPipeconf;
+import org.onosproject.net.pi.runtime.PiAction;
+import org.onosproject.net.pi.runtime.PiActionParam;
+import org.onosproject.p4runtime.ctl.utils.P4InfoBrowser;
+import p4.config.v1.P4InfoOuterClass;
+import p4.v1.P4RuntimeOuterClass;
+
+import static java.lang.String.format;
+import static org.onosproject.p4runtime.ctl.codec.Utils.assertSize;
+
+/**
+ * Codec for P4Runtime Action.
+ */
+public final class ActionCodec
+ extends AbstractCodec<PiAction, P4RuntimeOuterClass.Action, Object> {
+
+ @Override
+ protected P4RuntimeOuterClass.Action encode(
+ PiAction piAction, Object ignored, PiPipeconf pipeconf, P4InfoBrowser browser)
+ throws CodecException, P4InfoBrowser.NotFoundException {
+ final int actionId = browser.actions()
+ .getByName(piAction.id().toString()).getPreamble().getId();
+ final P4RuntimeOuterClass.Action.Builder actionMsgBuilder =
+ P4RuntimeOuterClass.Action.newBuilder().setActionId(actionId);
+ for (PiActionParam p : piAction.parameters()) {
+ final P4InfoOuterClass.Action.Param paramInfo = browser.actionParams(actionId)
+ .getByName(p.id().toString());
+ final ByteString paramValue = ByteString.copyFrom(p.value().asReadOnlyBuffer());
+ assertSize(format("param '%s' of action '%s'", p.id(), piAction.id()),
+ paramValue, paramInfo.getBitwidth());
+ actionMsgBuilder.addParams(P4RuntimeOuterClass.Action.Param.newBuilder()
+ .setParamId(paramInfo.getId())
+ .setValue(paramValue)
+ .build());
+ }
+ return actionMsgBuilder.build();
+ }
+
+ @Override
+ protected PiAction decode(
+ P4RuntimeOuterClass.Action message, Object ignored,
+ PiPipeconf pipeconf, P4InfoBrowser browser)
+ throws P4InfoBrowser.NotFoundException {
+ final P4InfoBrowser.EntityBrowser<P4InfoOuterClass.Action.Param> paramInfo =
+ browser.actionParams(message.getActionId());
+ final String actionName = browser.actions()
+ .getById(message.getActionId())
+ .getPreamble().getName();
+ final PiAction.Builder builder = PiAction.builder()
+ .withId(PiActionId.of(actionName));
+ for (P4RuntimeOuterClass.Action.Param p : message.getParamsList()) {
+ final String paramName = paramInfo.getById(p.getParamId()).getName();
+ final ImmutableByteSequence value = ImmutableByteSequence.copyFrom(
+ p.getValue().toByteArray());
+ builder.withParameter(new PiActionParam(PiActionParamId.of(paramName), value));
+ }
+ return builder.build();
+ }
+}
diff --git a/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/codec/ActionProfileGroupCodec.java b/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/codec/ActionProfileGroupCodec.java
new file mode 100644
index 0000000..5110274
--- /dev/null
+++ b/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/codec/ActionProfileGroupCodec.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2019-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.p4runtime.ctl.codec;
+
+import org.onosproject.net.pi.model.PiActionProfileId;
+import org.onosproject.net.pi.model.PiPipeconf;
+import org.onosproject.net.pi.runtime.PiActionProfileGroup;
+import org.onosproject.net.pi.runtime.PiActionProfileGroupHandle;
+import org.onosproject.net.pi.runtime.PiActionProfileGroupId;
+import org.onosproject.net.pi.runtime.PiActionProfileMemberId;
+import org.onosproject.p4runtime.ctl.utils.P4InfoBrowser;
+import p4.v1.P4RuntimeOuterClass.ActionProfileGroup;
+
+/**
+ * Codec for P4Runtime ActionProfileGroup.
+ */
+public final class ActionProfileGroupCodec
+ extends AbstractEntityCodec<PiActionProfileGroup, PiActionProfileGroupHandle, ActionProfileGroup, Object> {
+
+ @Override
+ public ActionProfileGroup encode(
+ PiActionProfileGroup piGroup, Object ignored, PiPipeconf pipeconf,
+ P4InfoBrowser browser)
+ throws P4InfoBrowser.NotFoundException {
+ final ActionProfileGroup.Builder msgBuilder = keyMsgBuilder(
+ piGroup.actionProfile(), piGroup.id(), browser)
+ .setMaxSize(piGroup.maxSize());
+ piGroup.members().forEach(m -> {
+ // TODO: currently we don't set "watch" field
+ ActionProfileGroup.Member member = ActionProfileGroup.Member.newBuilder()
+ .setMemberId(m.id().id())
+ .setWeight(m.weight())
+ .build();
+ msgBuilder.addMembers(member);
+ });
+ return msgBuilder.build();
+ }
+
+ @Override
+ protected ActionProfileGroup encodeKey(
+ PiActionProfileGroupHandle handle, Object ignored,
+ PiPipeconf pipeconf, P4InfoBrowser browser)
+ throws P4InfoBrowser.NotFoundException {
+ return keyMsgBuilder(handle.actionProfile(), handle.groupId(), browser)
+ .build();
+ }
+
+ @Override
+ protected ActionProfileGroup encodeKey(
+ PiActionProfileGroup piEntity, Object metadata,
+ PiPipeconf pipeconf, P4InfoBrowser browser)
+ throws P4InfoBrowser.NotFoundException {
+ return keyMsgBuilder(piEntity.actionProfile(), piEntity.id(), browser)
+ .build();
+ }
+
+ private ActionProfileGroup.Builder keyMsgBuilder(
+ PiActionProfileId actProfId, PiActionProfileGroupId groupId,
+ P4InfoBrowser browser)
+ throws P4InfoBrowser.NotFoundException {
+ return ActionProfileGroup.newBuilder()
+ .setGroupId(groupId.id())
+ .setActionProfileId(browser.actionProfiles()
+ .getByName(actProfId.id())
+ .getPreamble().getId());
+ }
+
+ @Override
+ public PiActionProfileGroup decode(
+ ActionProfileGroup msg, Object ignored, PiPipeconf pipeconf,
+ P4InfoBrowser browser)
+ throws P4InfoBrowser.NotFoundException {
+ final PiActionProfileGroup.Builder piGroupBuilder = PiActionProfileGroup.builder()
+ .withActionProfileId(PiActionProfileId.of(
+ browser.actionProfiles()
+ .getById(msg.getActionProfileId())
+ .getPreamble().getName()))
+ .withId(PiActionProfileGroupId.of(msg.getGroupId()))
+ .withMaxSize(msg.getMaxSize());
+ msg.getMembersList().forEach(m -> {
+ final int weight;
+ if (m.getWeight() < 1) {
+ log.warn("Decoding group with invalid weight '{}', will set to 1",
+ m.getWeight());
+ weight = 1;
+ } else {
+ weight = m.getWeight();
+ }
+ piGroupBuilder.addMember(
+ PiActionProfileMemberId.of(m.getMemberId()), weight);
+ });
+ return piGroupBuilder.build();
+ }
+}
diff --git a/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/codec/ActionProfileMemberCodec.java b/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/codec/ActionProfileMemberCodec.java
new file mode 100644
index 0000000..183d827
--- /dev/null
+++ b/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/codec/ActionProfileMemberCodec.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2019-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.p4runtime.ctl.codec;
+
+import org.onosproject.net.pi.model.PiActionProfileId;
+import org.onosproject.net.pi.model.PiPipeconf;
+import org.onosproject.net.pi.runtime.PiActionProfileMember;
+import org.onosproject.net.pi.runtime.PiActionProfileMemberHandle;
+import org.onosproject.net.pi.runtime.PiActionProfileMemberId;
+import org.onosproject.p4runtime.ctl.utils.P4InfoBrowser;
+import p4.v1.P4RuntimeOuterClass;
+import p4.v1.P4RuntimeOuterClass.ActionProfileMember;
+
+import static org.onosproject.p4runtime.ctl.codec.Codecs.CODECS;
+
+/**
+ * Codec for ActionProfileMember.
+ */
+public final class ActionProfileMemberCodec
+ extends AbstractEntityCodec<PiActionProfileMember, PiActionProfileMemberHandle, ActionProfileMember, Object> {
+
+ @Override
+ public ActionProfileMember encode(
+ PiActionProfileMember piEntity, Object ignored,
+ PiPipeconf pipeconf, P4InfoBrowser browser)
+ throws CodecException, P4InfoBrowser.NotFoundException {
+ return keyMsgBuilder(
+ piEntity.actionProfile(), piEntity.id(), browser)
+ .setAction(CODECS.action().encode(
+ piEntity.action(), null, pipeconf))
+ .build();
+ }
+
+ @Override
+ protected ActionProfileMember encodeKey(
+ PiActionProfileMemberHandle handle, Object ignored,
+ PiPipeconf pipeconf, P4InfoBrowser browser)
+ throws P4InfoBrowser.NotFoundException {
+ return keyMsgBuilder(handle.actionProfileId(), handle.memberId(), browser)
+ .build();
+ }
+
+ @Override
+ protected ActionProfileMember encodeKey(
+ PiActionProfileMember piEntity, Object metadata,
+ PiPipeconf pipeconf, P4InfoBrowser browser)
+ throws P4InfoBrowser.NotFoundException {
+ return keyMsgBuilder(
+ piEntity.actionProfile(), piEntity.id(), browser)
+ .build();
+ }
+
+ private P4RuntimeOuterClass.ActionProfileMember.Builder keyMsgBuilder(
+ PiActionProfileId actProfId, PiActionProfileMemberId memberId,
+ P4InfoBrowser browser)
+ throws P4InfoBrowser.NotFoundException {
+ return P4RuntimeOuterClass.ActionProfileMember.newBuilder()
+ .setActionProfileId(browser.actionProfiles()
+ .getByName(actProfId.id())
+ .getPreamble().getId())
+ .setMemberId(memberId.id());
+ }
+
+ @Override
+ public PiActionProfileMember decode(
+ ActionProfileMember message, Object ignored,
+ PiPipeconf pipeconf, P4InfoBrowser browser)
+ throws P4InfoBrowser.NotFoundException, CodecException {
+ final PiActionProfileId actionProfileId = PiActionProfileId.of(
+ browser.actionProfiles()
+ .getById(message.getActionProfileId())
+ .getPreamble()
+ .getName());
+ return PiActionProfileMember.builder()
+ .forActionProfile(actionProfileId)
+ .withId(PiActionProfileMemberId.of(message.getMemberId()))
+ .withAction(CODECS.action().decode(
+ message.getAction(), null, pipeconf))
+ .build();
+ }
+}
diff --git a/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/codec/CloneSessionEntryCodec.java b/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/codec/CloneSessionEntryCodec.java
new file mode 100644
index 0000000..337d76a
--- /dev/null
+++ b/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/codec/CloneSessionEntryCodec.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2019-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.p4runtime.ctl.codec;
+
+import org.onosproject.net.pi.model.PiPipeconf;
+import org.onosproject.net.pi.runtime.PiCloneSessionEntry;
+import org.onosproject.net.pi.runtime.PiCloneSessionEntryHandle;
+import org.onosproject.p4runtime.ctl.utils.P4InfoBrowser;
+import p4.v1.P4RuntimeOuterClass;
+
+import static org.onosproject.p4runtime.ctl.codec.Codecs.CODECS;
+
+/**
+ * Codec for P4Runtime CloneSessionEntry.
+ */
+public final class CloneSessionEntryCodec
+ extends AbstractEntityCodec<PiCloneSessionEntry, PiCloneSessionEntryHandle,
+ P4RuntimeOuterClass.CloneSessionEntry, Object> {
+
+ @Override
+ protected P4RuntimeOuterClass.CloneSessionEntry encode(
+ PiCloneSessionEntry piEntity, Object ignored,
+ PiPipeconf pipeconf, P4InfoBrowser browser) throws CodecException {
+ return P4RuntimeOuterClass.CloneSessionEntry.newBuilder()
+ .setSessionId(piEntity.sessionId())
+ .addAllReplicas(
+ CODECS.preReplica().encodeAll(
+ piEntity.replicas(), null, pipeconf))
+ .setClassOfService(piEntity.classOfService())
+ .setPacketLengthBytes(piEntity.maxPacketLengthBytes())
+ .build();
+ }
+
+ @Override
+ protected P4RuntimeOuterClass.CloneSessionEntry encodeKey(
+ PiCloneSessionEntryHandle handle, Object metadata,
+ PiPipeconf pipeconf, P4InfoBrowser browser) {
+ return P4RuntimeOuterClass.CloneSessionEntry.newBuilder()
+ .setSessionId(handle.sessionId()).build();
+ }
+
+ @Override
+ protected P4RuntimeOuterClass.CloneSessionEntry encodeKey(
+ PiCloneSessionEntry piEntity, Object metadata,
+ PiPipeconf pipeconf, P4InfoBrowser browser) {
+ return P4RuntimeOuterClass.CloneSessionEntry.newBuilder()
+ .setSessionId(piEntity.sessionId()).build();
+ }
+
+ @Override
+ protected PiCloneSessionEntry decode(
+ P4RuntimeOuterClass.CloneSessionEntry message, Object ignored,
+ PiPipeconf pipeconf, P4InfoBrowser browser) throws CodecException {
+ return PiCloneSessionEntry.builder()
+ .withSessionId(message.getSessionId())
+ .addReplicas(
+ CODECS.preReplica().decodeAll(
+ message.getReplicasList(), null, pipeconf))
+ .withClassOfService(message.getClassOfService())
+ .withMaxPacketLengthBytes(message.getPacketLengthBytes())
+ .build();
+ }
+}
diff --git a/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/codec/CodecException.java b/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/codec/CodecException.java
new file mode 100644
index 0000000..6ef77f2
--- /dev/null
+++ b/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/codec/CodecException.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2019-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.p4runtime.ctl.codec;
+
+/**
+ * Signals an error during encoding/decoding of a PI entity/protobuf message.
+ */
+public final class CodecException extends Exception {
+
+ /**
+ * Creates a new exception with the given explanation message.
+ *
+ * @param explanation explanation
+ */
+ public CodecException(String explanation) {
+ super(explanation);
+ }
+}
diff --git a/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/codec/Codecs.java b/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/codec/Codecs.java
new file mode 100644
index 0000000..30e8d9d
--- /dev/null
+++ b/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/codec/Codecs.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2019-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.p4runtime.ctl.codec;
+
+/**
+ * Utility class that provides access to P4Runtime codec instances.
+ */
+public final class Codecs {
+
+ public static final Codecs CODECS = new Codecs();
+
+ private final ActionCodec action;
+ private final ActionProfileGroupCodec actionProfileGroup;
+ private final ActionProfileMemberCodec actionProfileMember;
+ private final CounterEntryCodec counterEntry;
+ private final DirectCounterEntryCodec directCounterEntry;
+ private final DirectMeterEntryCodec directMeterEntry;
+ private final EntityCodec entity;
+ private final FieldMatchCodec fieldMatch;
+ private final HandleCodec handle;
+ private final MeterEntryCodec meterEntry;
+ private final MulticastGroupEntryCodec multicastGroupEntry;
+ private final CloneSessionEntryCodec cloneSessionEntry;
+ private final PreReplicaCodec preReplica;
+ private final PacketInCodec packetIn;
+ private final PacketMetadataCodec packetMetadata;
+ private final PacketOutCodec packetOut;
+ private final TableEntryCodec tableEntry;
+
+ private Codecs() {
+ this.action = new ActionCodec();
+ this.actionProfileGroup = new ActionProfileGroupCodec();
+ this.actionProfileMember = new ActionProfileMemberCodec();
+ this.counterEntry = new CounterEntryCodec();
+ this.directCounterEntry = new DirectCounterEntryCodec();
+ this.directMeterEntry = new DirectMeterEntryCodec();
+ this.entity = new EntityCodec();
+ this.fieldMatch = new FieldMatchCodec();
+ this.handle = new HandleCodec();
+ this.meterEntry = new MeterEntryCodec();
+ this.multicastGroupEntry = new MulticastGroupEntryCodec();
+ this.cloneSessionEntry = new CloneSessionEntryCodec();
+ this.preReplica = new PreReplicaCodec();
+ this.packetIn = new PacketInCodec();
+ this.packetMetadata = new PacketMetadataCodec();
+ this.packetOut = new PacketOutCodec();
+ this.tableEntry = new TableEntryCodec();
+ }
+
+ public EntityCodec entity() {
+ return entity;
+ }
+
+ public HandleCodec handle() {
+ return handle;
+ }
+
+ public PacketOutCodec packetOut() {
+ return packetOut;
+ }
+
+ public PacketInCodec packetIn() {
+ return packetIn;
+ }
+
+ TableEntryCodec tableEntry() {
+ return tableEntry;
+ }
+
+ FieldMatchCodec fieldMatch() {
+ return fieldMatch;
+ }
+
+ ActionCodec action() {
+ return action;
+ }
+
+ ActionProfileMemberCodec actionProfileMember() {
+ return actionProfileMember;
+ }
+
+ ActionProfileGroupCodec actionProfileGroup() {
+ return actionProfileGroup;
+ }
+
+ PacketMetadataCodec packetMetadata() {
+ return packetMetadata;
+ }
+
+ MulticastGroupEntryCodec multicastGroupEntry() {
+ return multicastGroupEntry;
+ }
+
+ CloneSessionEntryCodec cloneSessionEntry() {
+ return cloneSessionEntry;
+ }
+
+ PreReplicaCodec preReplica() {
+ return preReplica;
+ }
+
+ DirectMeterEntryCodec directMeterEntry() {
+ return directMeterEntry;
+ }
+
+ MeterEntryCodec meterEntry() {
+ return meterEntry;
+ }
+
+ CounterEntryCodec counterEntry() {
+ return counterEntry;
+ }
+
+ DirectCounterEntryCodec directCounterEntry() {
+ return directCounterEntry;
+ }
+}
diff --git a/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/codec/CounterEntryCodec.java b/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/codec/CounterEntryCodec.java
new file mode 100644
index 0000000..deac993
--- /dev/null
+++ b/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/codec/CounterEntryCodec.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2019-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.p4runtime.ctl.codec;
+
+import org.onosproject.net.pi.model.PiCounterId;
+import org.onosproject.net.pi.model.PiPipeconf;
+import org.onosproject.net.pi.runtime.PiCounterCell;
+import org.onosproject.net.pi.runtime.PiCounterCellHandle;
+import org.onosproject.net.pi.runtime.PiCounterCellId;
+import org.onosproject.p4runtime.ctl.utils.P4InfoBrowser;
+import p4.v1.P4RuntimeOuterClass;
+
+/**
+ * Codec for P4Runtime CounterEntry.
+ */
+public final class CounterEntryCodec
+ extends AbstractEntityCodec<PiCounterCell, PiCounterCellHandle,
+ P4RuntimeOuterClass.CounterEntry, Object> {
+
+ @Override
+ protected P4RuntimeOuterClass.CounterEntry encode(
+ PiCounterCell piEntity, Object ignored, PiPipeconf pipeconf,
+ P4InfoBrowser browser)
+ throws P4InfoBrowser.NotFoundException {
+ return keyMsgBuilder(piEntity.cellId(), browser)
+ .setData(P4RuntimeOuterClass.CounterData.newBuilder()
+ .setByteCount(piEntity.data().bytes())
+ .setPacketCount(piEntity.data().packets())
+ .build())
+ .build();
+ }
+
+ @Override
+ protected P4RuntimeOuterClass.CounterEntry encodeKey(
+ PiCounterCellHandle handle, Object metadata, PiPipeconf pipeconf,
+ P4InfoBrowser browser)
+ throws P4InfoBrowser.NotFoundException {
+ return keyMsgBuilder(handle.cellId(), browser).build();
+ }
+
+ @Override
+ protected P4RuntimeOuterClass.CounterEntry encodeKey(
+ PiCounterCell piEntity, Object metadata, PiPipeconf pipeconf,
+ P4InfoBrowser browser)
+ throws P4InfoBrowser.NotFoundException {
+ return keyMsgBuilder(piEntity.cellId(), browser).build();
+ }
+
+ private P4RuntimeOuterClass.CounterEntry.Builder keyMsgBuilder(
+ PiCounterCellId cellId, P4InfoBrowser browser)
+ throws P4InfoBrowser.NotFoundException {
+ final int counterId = browser.counters().getByName(
+ cellId.counterId().id()).getPreamble().getId();
+ return P4RuntimeOuterClass.CounterEntry.newBuilder()
+ .setCounterId(counterId)
+ .setIndex(P4RuntimeOuterClass.Index.newBuilder()
+ .setIndex(cellId.index()).build());
+ }
+
+ @Override
+ protected PiCounterCell decode(
+ P4RuntimeOuterClass.CounterEntry message, Object ignored,
+ PiPipeconf pipeconf, P4InfoBrowser browser)
+ throws P4InfoBrowser.NotFoundException {
+ final String counterName = browser.counters()
+ .getById(message.getCounterId())
+ .getPreamble()
+ .getName();
+ return new PiCounterCell(
+ PiCounterCellId.ofIndirect(
+ PiCounterId.of(counterName), message.getIndex().getIndex()),
+ message.getData().getPacketCount(),
+ message.getData().getByteCount());
+ }
+}
diff --git a/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/codec/DirectCounterEntryCodec.java b/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/codec/DirectCounterEntryCodec.java
new file mode 100644
index 0000000..46d0f3f
--- /dev/null
+++ b/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/codec/DirectCounterEntryCodec.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2019-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.p4runtime.ctl.codec;
+
+import org.onosproject.net.pi.model.PiPipeconf;
+import org.onosproject.net.pi.runtime.PiCounterCell;
+import org.onosproject.net.pi.runtime.PiCounterCellHandle;
+import org.onosproject.net.pi.runtime.PiCounterCellId;
+import org.onosproject.p4runtime.ctl.utils.P4InfoBrowser;
+import p4.v1.P4RuntimeOuterClass;
+
+import static org.onosproject.p4runtime.ctl.codec.Codecs.CODECS;
+
+/**
+ * Codec for P4Runtime DirectCounterEntryCodec.
+ */
+public final class DirectCounterEntryCodec
+ extends AbstractEntityCodec<PiCounterCell, PiCounterCellHandle,
+ P4RuntimeOuterClass.DirectCounterEntry, Object> {
+
+ @Override
+ protected P4RuntimeOuterClass.DirectCounterEntry encode(
+ PiCounterCell piEntity, Object ignored, PiPipeconf pipeconf,
+ P4InfoBrowser browser)
+ throws CodecException {
+ return keyMsgBuilder(piEntity.cellId(), pipeconf)
+ .setData(P4RuntimeOuterClass.CounterData.newBuilder()
+ .setByteCount(piEntity.data().bytes())
+ .setPacketCount(piEntity.data().packets())
+ .build())
+ .build();
+ }
+
+ @Override
+ protected P4RuntimeOuterClass.DirectCounterEntry encodeKey(
+ PiCounterCellHandle handle, Object metadata, PiPipeconf pipeconf,
+ P4InfoBrowser browser)
+ throws CodecException {
+ return keyMsgBuilder(handle.cellId(), pipeconf).build();
+ }
+
+ @Override
+ protected P4RuntimeOuterClass.DirectCounterEntry encodeKey(
+ PiCounterCell piEntity, Object metadata,
+ PiPipeconf pipeconf, P4InfoBrowser browser)
+ throws CodecException {
+ return keyMsgBuilder(piEntity.cellId(), pipeconf).build();
+ }
+
+ private P4RuntimeOuterClass.DirectCounterEntry.Builder keyMsgBuilder(
+ PiCounterCellId cellId, PiPipeconf pipeconf)
+ throws CodecException {
+ return P4RuntimeOuterClass.DirectCounterEntry.newBuilder()
+ .setTableEntry(CODECS.tableEntry().encodeKey(
+ cellId.tableEntry(), null, pipeconf));
+ }
+
+ @Override
+ protected PiCounterCell decode(
+ P4RuntimeOuterClass.DirectCounterEntry message, Object ignored,
+ PiPipeconf pipeconf, P4InfoBrowser browser)
+ throws CodecException {
+ return new PiCounterCell(
+ PiCounterCellId.ofDirect(
+ CODECS.tableEntry().decode(
+ message.getTableEntry(), null, pipeconf)),
+ message.getData().getPacketCount(),
+ message.getData().getByteCount());
+ }
+}
diff --git a/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/codec/DirectMeterEntryCodec.java b/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/codec/DirectMeterEntryCodec.java
new file mode 100644
index 0000000..3bedfbf
--- /dev/null
+++ b/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/codec/DirectMeterEntryCodec.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2019-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.p4runtime.ctl.codec;
+
+import org.onosproject.net.pi.model.PiPipeconf;
+import org.onosproject.net.pi.runtime.PiMeterBand;
+import org.onosproject.net.pi.runtime.PiMeterCellConfig;
+import org.onosproject.net.pi.runtime.PiMeterCellHandle;
+import org.onosproject.net.pi.runtime.PiMeterCellId;
+import org.onosproject.p4runtime.ctl.utils.P4InfoBrowser;
+import p4.v1.P4RuntimeOuterClass;
+
+import static org.onosproject.p4runtime.ctl.codec.Codecs.CODECS;
+
+/**
+ * Codec for P4Runtime DirectMeterEntryCodec.
+ */
+public final class DirectMeterEntryCodec
+ extends AbstractEntityCodec<PiMeterCellConfig, PiMeterCellHandle,
+ P4RuntimeOuterClass.DirectMeterEntry, Object> {
+
+ @Override
+ protected P4RuntimeOuterClass.DirectMeterEntry encode(
+ PiMeterCellConfig piEntity, Object ignored, PiPipeconf pipeconf,
+ P4InfoBrowser browser)
+ throws CodecException {
+ return P4RuntimeOuterClass.DirectMeterEntry.newBuilder()
+ .setTableEntry(CODECS.tableEntry().encode(
+ piEntity.cellId().tableEntry(), null, pipeconf))
+ .setConfig(MeterEntryCodec.getP4Config(piEntity))
+ .build();
+ }
+
+ @Override
+ protected P4RuntimeOuterClass.DirectMeterEntry encodeKey(
+ PiMeterCellHandle handle, Object metadata, PiPipeconf pipeconf,
+ P4InfoBrowser browser)
+ throws CodecException {
+ return keyMsgBuilder(handle.cellId(), pipeconf).build();
+ }
+
+ @Override
+ protected P4RuntimeOuterClass.DirectMeterEntry encodeKey(
+ PiMeterCellConfig piEntity, Object metadata,
+ PiPipeconf pipeconf, P4InfoBrowser browser)
+ throws CodecException {
+ return keyMsgBuilder(piEntity.cellId(), pipeconf).build();
+ }
+
+ private P4RuntimeOuterClass.DirectMeterEntry.Builder keyMsgBuilder(
+ PiMeterCellId cellId, PiPipeconf pipeconf)
+ throws CodecException {
+ return P4RuntimeOuterClass.DirectMeterEntry.newBuilder()
+ .setTableEntry(CODECS.tableEntry().encodeKey(
+ cellId.tableEntry(), null, pipeconf));
+ }
+
+ @Override
+ protected PiMeterCellConfig decode(
+ P4RuntimeOuterClass.DirectMeterEntry message, Object ignored,
+ PiPipeconf pipeconf, P4InfoBrowser browser)
+ throws CodecException {
+ return PiMeterCellConfig.builder()
+ .withMeterCellId(PiMeterCellId.ofDirect(
+ CODECS.tableEntry().decode(
+ message.getTableEntry(), null, pipeconf)))
+ .withMeterBand(new PiMeterBand(message.getConfig().getCir(),
+ message.getConfig().getCburst()))
+ .withMeterBand(new PiMeterBand(message.getConfig().getPir(),
+ message.getConfig().getPburst()))
+ .build();
+ }
+}
diff --git a/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/codec/EntityCodec.java b/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/codec/EntityCodec.java
new file mode 100644
index 0000000..e063faa
--- /dev/null
+++ b/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/codec/EntityCodec.java
@@ -0,0 +1,185 @@
+/*
+ * Copyright 2019-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.p4runtime.ctl.codec;
+
+import org.onosproject.net.pi.model.PiPipeconf;
+import org.onosproject.net.pi.runtime.PiActionProfileGroup;
+import org.onosproject.net.pi.runtime.PiActionProfileMember;
+import org.onosproject.net.pi.runtime.PiCloneSessionEntry;
+import org.onosproject.net.pi.runtime.PiCounterCell;
+import org.onosproject.net.pi.runtime.PiEntity;
+import org.onosproject.net.pi.runtime.PiMeterCellConfig;
+import org.onosproject.net.pi.runtime.PiMulticastGroupEntry;
+import org.onosproject.net.pi.runtime.PiPreEntry;
+import org.onosproject.net.pi.runtime.PiTableEntry;
+import org.onosproject.p4runtime.ctl.utils.P4InfoBrowser;
+import p4.v1.P4RuntimeOuterClass;
+
+import static java.lang.String.format;
+import static org.onosproject.p4runtime.ctl.codec.Codecs.CODECS;
+
+/**
+ * Codec for P4Runtime Entity.
+ */
+public final class EntityCodec extends AbstractCodec<PiEntity, P4RuntimeOuterClass.Entity, Object> {
+
+ @Override
+ protected P4RuntimeOuterClass.Entity encode(
+ PiEntity piEntity, Object ignored, PiPipeconf pipeconf, P4InfoBrowser browser)
+ throws CodecException {
+ final P4RuntimeOuterClass.Entity.Builder p4Entity = P4RuntimeOuterClass.Entity.newBuilder();
+ switch (piEntity.piEntityType()) {
+ case TABLE_ENTRY:
+ return p4Entity.setTableEntry(
+ CODECS.tableEntry().encode(
+ (PiTableEntry) piEntity, null, pipeconf))
+ .build();
+ case ACTION_PROFILE_GROUP:
+ return p4Entity.setActionProfileGroup(
+ CODECS.actionProfileGroup().encode(
+ (PiActionProfileGroup) piEntity, null, pipeconf))
+ .build();
+ case ACTION_PROFILE_MEMBER:
+ return p4Entity.setActionProfileMember(
+ CODECS.actionProfileMember().encode(
+ (PiActionProfileMember) piEntity, null, pipeconf))
+ .build();
+ case PRE_ENTRY:
+ final PiPreEntry preEntry = (PiPreEntry) piEntity;
+ switch (preEntry.preEntryType()) {
+ case MULTICAST_GROUP:
+ return p4Entity.setPacketReplicationEngineEntry(
+ P4RuntimeOuterClass.PacketReplicationEngineEntry.newBuilder()
+ .setMulticastGroupEntry(CODECS.multicastGroupEntry().encode(
+ (PiMulticastGroupEntry) piEntity, null, pipeconf))
+ .build())
+ .build();
+ case CLONE_SESSION:
+ return p4Entity.setPacketReplicationEngineEntry(
+ P4RuntimeOuterClass.PacketReplicationEngineEntry.newBuilder()
+ .setCloneSessionEntry(CODECS.cloneSessionEntry().encode(
+ (PiCloneSessionEntry) piEntity, null, pipeconf))
+ .build())
+ .build();
+ default:
+ throw new CodecException(format(
+ "Encoding of %s of type %s is not supported",
+ piEntity.piEntityType(),
+ preEntry.preEntryType()));
+ }
+ case METER_CELL_CONFIG:
+ final PiMeterCellConfig meterCellConfig = (PiMeterCellConfig) piEntity;
+ switch (meterCellConfig.cellId().meterType()) {
+ case DIRECT:
+ return p4Entity.setDirectMeterEntry(
+ CODECS.directMeterEntry().encode(
+ meterCellConfig, null, pipeconf))
+ .build();
+ case INDIRECT:
+ return p4Entity.setMeterEntry(
+ CODECS.meterEntry().encode(
+ meterCellConfig, null, pipeconf))
+ .build();
+ default:
+ throw new CodecException(format(
+ "Encoding of %s of type %s is not supported",
+ piEntity.piEntityType(),
+ meterCellConfig.cellId().meterType()));
+ }
+ case COUNTER_CELL:
+ final PiCounterCell counterCell = (PiCounterCell) piEntity;
+ switch (counterCell.cellId().counterType()) {
+ case DIRECT:
+ return p4Entity.setDirectCounterEntry(
+ CODECS.directCounterEntry().encode(
+ counterCell, null, pipeconf))
+ .build();
+ case INDIRECT:
+ return p4Entity.setCounterEntry(
+ CODECS.counterEntry().encode(
+ counterCell, null, pipeconf))
+ .build();
+ default:
+ throw new CodecException(format(
+ "Encoding of %s of type %s is not supported",
+ piEntity.piEntityType(),
+ counterCell.cellId().counterType()));
+ }
+ case REGISTER_CELL:
+ default:
+ throw new CodecException(format(
+ "Encoding of %s not supported",
+ piEntity.piEntityType()));
+ }
+ }
+
+ @Override
+ protected PiEntity decode(
+ P4RuntimeOuterClass.Entity message, Object ignored, PiPipeconf pipeconf, P4InfoBrowser browser)
+ throws CodecException {
+ switch (message.getEntityCase()) {
+ case TABLE_ENTRY:
+ return CODECS.tableEntry().decode(
+ message.getTableEntry(), null, pipeconf);
+ case ACTION_PROFILE_MEMBER:
+ return CODECS.actionProfileMember().decode(
+ message.getActionProfileMember(), null, pipeconf);
+ case ACTION_PROFILE_GROUP:
+ return CODECS.actionProfileGroup().decode(
+ message.getActionProfileGroup(), null, pipeconf);
+ case METER_ENTRY:
+ return CODECS.meterEntry().decode(
+ message.getMeterEntry(), null, pipeconf);
+ case DIRECT_METER_ENTRY:
+ return CODECS.directMeterEntry().decode(
+ message.getDirectMeterEntry(), null, pipeconf);
+ case COUNTER_ENTRY:
+ return CODECS.counterEntry().decode(
+ message.getCounterEntry(), null, pipeconf);
+ case DIRECT_COUNTER_ENTRY:
+ return CODECS.directCounterEntry().decode(
+ message.getDirectCounterEntry(), null, pipeconf);
+ case PACKET_REPLICATION_ENGINE_ENTRY:
+ switch (message.getPacketReplicationEngineEntry().getTypeCase()) {
+ case MULTICAST_GROUP_ENTRY:
+ return CODECS.multicastGroupEntry().decode(
+ message.getPacketReplicationEngineEntry()
+ .getMulticastGroupEntry(), null, pipeconf);
+ case CLONE_SESSION_ENTRY:
+ return CODECS.cloneSessionEntry().decode(
+ message.getPacketReplicationEngineEntry()
+ .getCloneSessionEntry(), null, pipeconf);
+ case TYPE_NOT_SET:
+ default:
+ throw new CodecException(format(
+ "Decoding of %s of type %s not supported",
+ message.getEntityCase(),
+ message.getPacketReplicationEngineEntry().getTypeCase()));
+ }
+ case VALUE_SET_ENTRY:
+ case REGISTER_ENTRY:
+ case DIGEST_ENTRY:
+ case EXTERN_ENTRY:
+ case ENTITY_NOT_SET:
+ default:
+ throw new CodecException(format(
+ "Decoding of %s not supported",
+ message.getEntityCase()));
+
+ }
+ }
+}
diff --git a/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/codec/FieldMatchCodec.java b/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/codec/FieldMatchCodec.java
new file mode 100644
index 0000000..f289d5d
--- /dev/null
+++ b/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/codec/FieldMatchCodec.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright 2019-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.p4runtime.ctl.codec;
+
+import com.google.protobuf.ByteString;
+import org.onlab.util.ImmutableByteSequence;
+import org.onosproject.net.pi.model.PiMatchFieldId;
+import org.onosproject.net.pi.model.PiPipeconf;
+import org.onosproject.net.pi.runtime.PiExactFieldMatch;
+import org.onosproject.net.pi.runtime.PiFieldMatch;
+import org.onosproject.net.pi.runtime.PiLpmFieldMatch;
+import org.onosproject.net.pi.runtime.PiRangeFieldMatch;
+import org.onosproject.net.pi.runtime.PiTernaryFieldMatch;
+import org.onosproject.p4runtime.ctl.utils.P4InfoBrowser;
+import p4.config.v1.P4InfoOuterClass;
+import p4.v1.P4RuntimeOuterClass;
+
+import static java.lang.String.format;
+import static org.onlab.util.ImmutableByteSequence.copyFrom;
+import static org.onosproject.p4runtime.ctl.codec.Utils.assertPrefixLen;
+import static org.onosproject.p4runtime.ctl.codec.Utils.assertSize;
+
+/**
+ * Codec for P4Runtime FieldMatch. Metadata is expected to be a Preamble for
+ * P4Info.Table.
+ */
+public final class FieldMatchCodec
+ extends AbstractCodec<PiFieldMatch, P4RuntimeOuterClass.FieldMatch,
+ P4InfoOuterClass.Preamble> {
+
+ private static final String VALUE_OF_PREFIX = "value of ";
+ private static final String MASK_OF_PREFIX = "mask of ";
+ private static final String HIGH_RANGE_VALUE_OF_PREFIX = "high range value of ";
+ private static final String LOW_RANGE_VALUE_OF_PREFIX = "low range value of ";
+
+ @Override
+ public P4RuntimeOuterClass.FieldMatch encode(
+ PiFieldMatch piFieldMatch, P4InfoOuterClass.Preamble tablePreamble,
+ PiPipeconf pipeconf, P4InfoBrowser browser)
+ throws CodecException, P4InfoBrowser.NotFoundException {
+
+ P4RuntimeOuterClass.FieldMatch.Builder messageBuilder = P4RuntimeOuterClass
+ .FieldMatch.newBuilder();
+
+ // FIXME: check how field names for stacked headers are constructed in P4Runtime.
+ String fieldName = piFieldMatch.fieldId().id();
+ P4InfoOuterClass.MatchField matchFieldInfo = browser.matchFields(
+ tablePreamble.getId()).getByName(fieldName);
+ String entityName = format("field match '%s' of table '%s'",
+ matchFieldInfo.getName(), tablePreamble.getName());
+ int fieldId = matchFieldInfo.getId();
+ int fieldBitwidth = matchFieldInfo.getBitwidth();
+
+ messageBuilder.setFieldId(fieldId);
+
+ switch (piFieldMatch.type()) {
+ case EXACT:
+ PiExactFieldMatch fieldMatch = (PiExactFieldMatch) piFieldMatch;
+ ByteString exactValue = ByteString.copyFrom(fieldMatch.value().asReadOnlyBuffer());
+ assertSize(VALUE_OF_PREFIX + entityName, exactValue, fieldBitwidth);
+ return messageBuilder.setExact(
+ P4RuntimeOuterClass.FieldMatch.Exact
+ .newBuilder()
+ .setValue(exactValue)
+ .build())
+ .build();
+ case TERNARY:
+ PiTernaryFieldMatch ternaryMatch = (PiTernaryFieldMatch) piFieldMatch;
+ ByteString ternaryValue = ByteString.copyFrom(ternaryMatch.value().asReadOnlyBuffer());
+ ByteString ternaryMask = ByteString.copyFrom(ternaryMatch.mask().asReadOnlyBuffer());
+ assertSize(VALUE_OF_PREFIX + entityName, ternaryValue, fieldBitwidth);
+ assertSize(MASK_OF_PREFIX + entityName, ternaryMask, fieldBitwidth);
+ return messageBuilder.setTernary(
+ P4RuntimeOuterClass.FieldMatch.Ternary
+ .newBuilder()
+ .setValue(ternaryValue)
+ .setMask(ternaryMask)
+ .build())
+ .build();
+ case LPM:
+ PiLpmFieldMatch lpmMatch = (PiLpmFieldMatch) piFieldMatch;
+ ByteString lpmValue = ByteString.copyFrom(lpmMatch.value().asReadOnlyBuffer());
+ int lpmPrefixLen = lpmMatch.prefixLength();
+ assertSize(VALUE_OF_PREFIX + entityName, lpmValue, fieldBitwidth);
+ assertPrefixLen(entityName, lpmPrefixLen, fieldBitwidth);
+ return messageBuilder.setLpm(
+ P4RuntimeOuterClass.FieldMatch.LPM.newBuilder()
+ .setValue(lpmValue)
+ .setPrefixLen(lpmPrefixLen)
+ .build())
+ .build();
+ case RANGE:
+ PiRangeFieldMatch rangeMatch = (PiRangeFieldMatch) piFieldMatch;
+ ByteString rangeHighValue = ByteString.copyFrom(rangeMatch.highValue().asReadOnlyBuffer());
+ ByteString rangeLowValue = ByteString.copyFrom(rangeMatch.lowValue().asReadOnlyBuffer());
+ assertSize(HIGH_RANGE_VALUE_OF_PREFIX + entityName, rangeHighValue, fieldBitwidth);
+ assertSize(LOW_RANGE_VALUE_OF_PREFIX + entityName, rangeLowValue, fieldBitwidth);
+ return messageBuilder.setRange(
+ P4RuntimeOuterClass.FieldMatch.Range.newBuilder()
+ .setHigh(rangeHighValue)
+ .setLow(rangeLowValue)
+ .build())
+ .build();
+ default:
+ throw new CodecException(format(
+ "Building of match type %s not implemented", piFieldMatch.type()));
+ }
+ }
+
+ @Override
+ public PiFieldMatch decode(
+ P4RuntimeOuterClass.FieldMatch message, P4InfoOuterClass.Preamble tablePreamble,
+ PiPipeconf pipeconf, P4InfoBrowser browser)
+ throws CodecException, P4InfoBrowser.NotFoundException {
+
+ String fieldMatchName = browser.matchFields(tablePreamble.getId())
+ .getById(message.getFieldId()).getName();
+ PiMatchFieldId headerFieldId = PiMatchFieldId.of(fieldMatchName);
+
+ P4RuntimeOuterClass.FieldMatch.FieldMatchTypeCase typeCase = message.getFieldMatchTypeCase();
+
+ switch (typeCase) {
+ case EXACT:
+ P4RuntimeOuterClass.FieldMatch.Exact exactFieldMatch = message.getExact();
+ ImmutableByteSequence exactValue = copyFrom(exactFieldMatch.getValue().asReadOnlyByteBuffer());
+ return new PiExactFieldMatch(headerFieldId, exactValue);
+ case TERNARY:
+ P4RuntimeOuterClass.FieldMatch.Ternary ternaryFieldMatch = message.getTernary();
+ ImmutableByteSequence ternaryValue = copyFrom(ternaryFieldMatch.getValue().asReadOnlyByteBuffer());
+ ImmutableByteSequence ternaryMask = copyFrom(ternaryFieldMatch.getMask().asReadOnlyByteBuffer());
+ return new PiTernaryFieldMatch(headerFieldId, ternaryValue, ternaryMask);
+ case LPM:
+ P4RuntimeOuterClass.FieldMatch.LPM lpmFieldMatch = message.getLpm();
+ ImmutableByteSequence lpmValue = copyFrom(lpmFieldMatch.getValue().asReadOnlyByteBuffer());
+ int lpmPrefixLen = lpmFieldMatch.getPrefixLen();
+ return new PiLpmFieldMatch(headerFieldId, lpmValue, lpmPrefixLen);
+ case RANGE:
+ P4RuntimeOuterClass.FieldMatch.Range rangeFieldMatch = message.getRange();
+ ImmutableByteSequence rangeHighValue = copyFrom(rangeFieldMatch.getHigh().asReadOnlyByteBuffer());
+ ImmutableByteSequence rangeLowValue = copyFrom(rangeFieldMatch.getLow().asReadOnlyByteBuffer());
+ return new PiRangeFieldMatch(headerFieldId, rangeLowValue, rangeHighValue);
+ default:
+ throw new CodecException(format(
+ "Decoding of field match type '%s' not implemented", typeCase.name()));
+ }
+ }
+}
diff --git a/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/codec/HandleCodec.java b/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/codec/HandleCodec.java
new file mode 100644
index 0000000..7a9c727
--- /dev/null
+++ b/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/codec/HandleCodec.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2019-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.p4runtime.ctl.codec;
+
+import org.onosproject.net.pi.model.PiPipeconf;
+import org.onosproject.net.pi.runtime.PiActionProfileGroupHandle;
+import org.onosproject.net.pi.runtime.PiActionProfileMemberHandle;
+import org.onosproject.net.pi.runtime.PiCloneSessionEntryHandle;
+import org.onosproject.net.pi.runtime.PiCounterCellHandle;
+import org.onosproject.net.pi.runtime.PiHandle;
+import org.onosproject.net.pi.runtime.PiMeterCellHandle;
+import org.onosproject.net.pi.runtime.PiMulticastGroupEntryHandle;
+import org.onosproject.net.pi.runtime.PiPreEntryHandle;
+import org.onosproject.net.pi.runtime.PiTableEntryHandle;
+import org.onosproject.p4runtime.ctl.utils.P4InfoBrowser;
+import p4.v1.P4RuntimeOuterClass;
+
+import static java.lang.String.format;
+import static org.onosproject.p4runtime.ctl.codec.Codecs.CODECS;
+
+public final class HandleCodec extends AbstractCodec<PiHandle, P4RuntimeOuterClass.Entity, Object> {
+
+ @Override
+ protected P4RuntimeOuterClass.Entity encode(
+ PiHandle piHandle, Object ignored, PiPipeconf pipeconf,
+ P4InfoBrowser browser)
+ throws CodecException {
+ final P4RuntimeOuterClass.Entity.Builder p4Entity = P4RuntimeOuterClass.Entity.newBuilder();
+
+ switch (piHandle.entityType()) {
+ case TABLE_ENTRY:
+ return p4Entity.setTableEntry(
+ CODECS.tableEntry().encodeKey(
+ (PiTableEntryHandle) piHandle, null, pipeconf))
+ .build();
+ case ACTION_PROFILE_GROUP:
+ return p4Entity.setActionProfileGroup(
+ CODECS.actionProfileGroup().encodeKey(
+ (PiActionProfileGroupHandle) piHandle, null, pipeconf))
+ .build();
+ case ACTION_PROFILE_MEMBER:
+ return p4Entity.setActionProfileMember(
+ CODECS.actionProfileMember().encodeKey(
+ (PiActionProfileMemberHandle) piHandle, null, pipeconf))
+ .build();
+ case PRE_ENTRY:
+ final PiPreEntryHandle preEntryHandle = (PiPreEntryHandle) piHandle;
+ switch (preEntryHandle.preEntryType()) {
+ case MULTICAST_GROUP:
+ return p4Entity.setPacketReplicationEngineEntry(
+ P4RuntimeOuterClass.PacketReplicationEngineEntry.newBuilder()
+ .setMulticastGroupEntry(CODECS.multicastGroupEntry().encodeKey(
+ (PiMulticastGroupEntryHandle) piHandle, null, pipeconf))
+ .build())
+ .build();
+ case CLONE_SESSION:
+ return p4Entity.setPacketReplicationEngineEntry(
+ P4RuntimeOuterClass.PacketReplicationEngineEntry.newBuilder()
+ .setCloneSessionEntry(CODECS.cloneSessionEntry().encodeKey(
+ (PiCloneSessionEntryHandle) piHandle, null, pipeconf))
+ .build())
+ .build();
+ default:
+ throw new CodecException(format(
+ "Encoding of handle for %s of type %s is not supported",
+ piHandle.entityType(),
+ preEntryHandle.preEntryType()));
+ }
+ case METER_CELL_CONFIG:
+ final PiMeterCellHandle meterCellHandle = (PiMeterCellHandle) piHandle;
+ switch (meterCellHandle.cellId().meterType()) {
+ case DIRECT:
+ return p4Entity.setDirectMeterEntry(
+ CODECS.directMeterEntry().encodeKey(
+ meterCellHandle, null, pipeconf))
+ .build();
+ case INDIRECT:
+ return p4Entity.setMeterEntry(
+ CODECS.meterEntry().encodeKey(
+ meterCellHandle, null, pipeconf))
+ .build();
+ default:
+ throw new CodecException(format(
+ "Encoding of handle for %s of type %s is not supported",
+ piHandle.entityType(),
+ meterCellHandle.cellId().meterType()));
+ }
+ case COUNTER_CELL:
+ final PiCounterCellHandle counterCellHandle = (PiCounterCellHandle) piHandle;
+ switch (counterCellHandle.cellId().counterType()) {
+ case DIRECT:
+ return p4Entity.setDirectCounterEntry(
+ CODECS.directCounterEntry().encodeKey(
+ counterCellHandle, null, pipeconf))
+ .build();
+ case INDIRECT:
+ return p4Entity.setCounterEntry(
+ CODECS.counterEntry().encodeKey(
+ counterCellHandle, null, pipeconf))
+ .build();
+ default:
+ throw new CodecException(format(
+ "Encoding of handle for %s of type %s is not supported",
+ piHandle.entityType(),
+ counterCellHandle.cellId().counterType()));
+ }
+ case REGISTER_CELL:
+ default:
+ throw new CodecException(format(
+ "Encoding of handle for %s not supported",
+ piHandle.entityType()));
+ }
+ }
+
+ @Override
+ protected PiHandle decode(
+ P4RuntimeOuterClass.Entity message, Object ignored,
+ PiPipeconf pipeconf, P4InfoBrowser browser)
+ throws CodecException {
+ throw new CodecException("Decoding of Entity to PiHandle is not supported");
+ }
+}
diff --git a/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/codec/MeterEntryCodec.java b/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/codec/MeterEntryCodec.java
new file mode 100644
index 0000000..b210709
--- /dev/null
+++ b/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/codec/MeterEntryCodec.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2019-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.p4runtime.ctl.codec;
+
+import org.onosproject.net.pi.model.PiMeterId;
+import org.onosproject.net.pi.model.PiPipeconf;
+import org.onosproject.net.pi.runtime.PiMeterBand;
+import org.onosproject.net.pi.runtime.PiMeterCellConfig;
+import org.onosproject.net.pi.runtime.PiMeterCellHandle;
+import org.onosproject.net.pi.runtime.PiMeterCellId;
+import org.onosproject.p4runtime.ctl.utils.P4InfoBrowser;
+import p4.v1.P4RuntimeOuterClass;
+
+/**
+ * Codec for P4Runtime MeterEntry.
+ */
+public final class MeterEntryCodec
+ extends AbstractEntityCodec<PiMeterCellConfig, PiMeterCellHandle,
+ P4RuntimeOuterClass.MeterEntry, Object> {
+
+ static P4RuntimeOuterClass.MeterConfig getP4Config(PiMeterCellConfig piConfig)
+ throws CodecException {
+ if (piConfig.meterBands().size() != 2) {
+ throw new CodecException("Number of meter bands should be 2");
+ }
+ final PiMeterBand[] bands = piConfig.meterBands().toArray(new PiMeterBand[0]);
+ long cir, cburst, pir, pburst;
+ // The band with bigger burst is peak if rate of them is equal.
+ if (bands[0].rate() > bands[1].rate() ||
+ (bands[0].rate() == bands[1].rate() &&
+ bands[0].burst() >= bands[1].burst())) {
+ cir = bands[1].rate();
+ cburst = bands[1].burst();
+ pir = bands[0].rate();
+ pburst = bands[0].burst();
+ } else {
+ cir = bands[0].rate();
+ cburst = bands[0].burst();
+ pir = bands[1].rate();
+ pburst = bands[1].burst();
+ }
+ return P4RuntimeOuterClass.MeterConfig.newBuilder()
+ .setCir(cir)
+ .setCburst(cburst)
+ .setPir(pir)
+ .setPburst(pburst)
+ .build();
+ }
+
+ @Override
+ protected P4RuntimeOuterClass.MeterEntry encode(
+ PiMeterCellConfig piEntity, Object ignored, PiPipeconf pipeconf,
+ P4InfoBrowser browser)
+ throws P4InfoBrowser.NotFoundException, CodecException {
+ final int meterId = browser.meters().getByName(
+ piEntity.cellId().meterId().id()).getPreamble().getId();
+ return P4RuntimeOuterClass.MeterEntry.newBuilder()
+ .setMeterId(meterId)
+ .setIndex(P4RuntimeOuterClass.Index.newBuilder()
+ .setIndex(piEntity.cellId().index()).build())
+ .setConfig(getP4Config(piEntity))
+ .build();
+ }
+
+ @Override
+ protected P4RuntimeOuterClass.MeterEntry encodeKey(
+ PiMeterCellHandle handle, Object metadata, PiPipeconf pipeconf,
+ P4InfoBrowser browser)
+ throws P4InfoBrowser.NotFoundException {
+ return keyMsgBuilder(handle.cellId(), browser).build();
+ }
+
+ @Override
+ protected P4RuntimeOuterClass.MeterEntry encodeKey(
+ PiMeterCellConfig piEntity, Object metadata, PiPipeconf pipeconf,
+ P4InfoBrowser browser)
+ throws P4InfoBrowser.NotFoundException {
+ return keyMsgBuilder(piEntity.cellId(), browser).build();
+ }
+
+ private P4RuntimeOuterClass.MeterEntry.Builder keyMsgBuilder(
+ PiMeterCellId cellId, P4InfoBrowser browser)
+ throws P4InfoBrowser.NotFoundException {
+ final int meterId = browser.meters().getByName(
+ cellId.meterId().id()).getPreamble().getId();
+ return P4RuntimeOuterClass.MeterEntry.newBuilder()
+ .setMeterId(meterId)
+ .setIndex(P4RuntimeOuterClass.Index.newBuilder()
+ .setIndex(cellId.index()).build());
+ }
+
+ @Override
+ protected PiMeterCellConfig decode(
+ P4RuntimeOuterClass.MeterEntry message, Object ignored,
+ PiPipeconf pipeconf, P4InfoBrowser browser)
+ throws P4InfoBrowser.NotFoundException {
+ final String meterName = browser.meters()
+ .getById(message.getMeterId())
+ .getPreamble()
+ .getName();
+ return PiMeterCellConfig.builder()
+ .withMeterCellId(PiMeterCellId.ofIndirect(
+ PiMeterId.of(meterName), message.getIndex().getIndex()))
+ .withMeterBand(new PiMeterBand(message.getConfig().getCir(),
+ message.getConfig().getCburst()))
+ .withMeterBand(new PiMeterBand(message.getConfig().getPir(),
+ message.getConfig().getPburst()))
+ .build();
+ }
+}
diff --git a/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/codec/MulticastGroupEntryCodec.java b/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/codec/MulticastGroupEntryCodec.java
new file mode 100644
index 0000000..bcc4555
--- /dev/null
+++ b/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/codec/MulticastGroupEntryCodec.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2019-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.p4runtime.ctl.codec;
+
+import org.onosproject.net.pi.model.PiPipeconf;
+import org.onosproject.net.pi.runtime.PiMulticastGroupEntry;
+import org.onosproject.net.pi.runtime.PiMulticastGroupEntryHandle;
+import org.onosproject.p4runtime.ctl.utils.P4InfoBrowser;
+import p4.v1.P4RuntimeOuterClass;
+
+import static org.onosproject.p4runtime.ctl.codec.Codecs.CODECS;
+
+/**
+ * Codec for P4Runtime MulticastGroupEntry.
+ */
+public final class MulticastGroupEntryCodec
+ extends AbstractEntityCodec<PiMulticastGroupEntry, PiMulticastGroupEntryHandle,
+ P4RuntimeOuterClass.MulticastGroupEntry, Object> {
+
+ @Override
+ protected P4RuntimeOuterClass.MulticastGroupEntry encode(
+ PiMulticastGroupEntry piEntity, Object ignored,
+ PiPipeconf pipeconf, P4InfoBrowser browser) throws CodecException {
+ return P4RuntimeOuterClass.MulticastGroupEntry.newBuilder()
+ .setMulticastGroupId(piEntity.groupId())
+ .addAllReplicas(
+ CODECS.preReplica().encodeAll(
+ piEntity.replicas(), null, pipeconf))
+ .build();
+ }
+
+ @Override
+ protected P4RuntimeOuterClass.MulticastGroupEntry encodeKey(
+ PiMulticastGroupEntryHandle handle, Object metadata,
+ PiPipeconf pipeconf, P4InfoBrowser browser) {
+ return P4RuntimeOuterClass.MulticastGroupEntry.newBuilder()
+ .setMulticastGroupId(handle.groupId()).build();
+ }
+
+ @Override
+ protected P4RuntimeOuterClass.MulticastGroupEntry encodeKey(
+ PiMulticastGroupEntry piEntity, Object metadata,
+ PiPipeconf pipeconf, P4InfoBrowser browser) {
+ return P4RuntimeOuterClass.MulticastGroupEntry.newBuilder()
+ .setMulticastGroupId(piEntity.groupId()).build();
+ }
+
+ @Override
+ protected PiMulticastGroupEntry decode(
+ P4RuntimeOuterClass.MulticastGroupEntry message, Object ignored,
+ PiPipeconf pipeconf, P4InfoBrowser browser) throws CodecException {
+ return PiMulticastGroupEntry.builder()
+ .withGroupId(message.getMulticastGroupId())
+ .addReplicas(
+ CODECS.preReplica().decodeAll(
+ message.getReplicasList(), null, pipeconf))
+ .build();
+ }
+}
diff --git a/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/codec/P4DataCodec.java b/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/codec/P4DataCodec.java
new file mode 100644
index 0000000..d55b614
--- /dev/null
+++ b/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/codec/P4DataCodec.java
@@ -0,0 +1,224 @@
+/*
+ * Copyright 2019-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.p4runtime.ctl.codec;
+
+import com.google.protobuf.ByteString;
+import org.onlab.util.ImmutableByteSequence;
+import org.onosproject.net.pi.model.PiData;
+import org.onosproject.net.pi.runtime.data.PiBitString;
+import org.onosproject.net.pi.runtime.data.PiBool;
+import org.onosproject.net.pi.runtime.data.PiEnumString;
+import org.onosproject.net.pi.runtime.data.PiErrorString;
+import org.onosproject.net.pi.runtime.data.PiHeader;
+import org.onosproject.net.pi.runtime.data.PiHeaderStack;
+import org.onosproject.net.pi.runtime.data.PiHeaderUnion;
+import org.onosproject.net.pi.runtime.data.PiHeaderUnionStack;
+import org.onosproject.net.pi.runtime.data.PiStruct;
+import org.onosproject.net.pi.runtime.data.PiTuple;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+import static p4.v1.P4DataOuterClass.P4Data;
+import static p4.v1.P4DataOuterClass.P4Header;
+import static p4.v1.P4DataOuterClass.P4HeaderStack;
+import static p4.v1.P4DataOuterClass.P4HeaderUnion;
+import static p4.v1.P4DataOuterClass.P4HeaderUnionStack;
+import static p4.v1.P4DataOuterClass.P4StructLike;
+
+/**
+ * Encoder/decoder of PI Data entry to P4 Data entry protobuf messages, and vice
+ * versa.
+ * <p>
+ * TODO: implement codec for each P4Data type using AbstractP4RuntimeCodec.
+ */
+final class P4DataCodec {
+
+ private P4DataCodec() {
+ // Hides constructor.
+ }
+
+ private static P4Header encodeHeader(PiHeader piHeader) {
+ P4Header.Builder builder = P4Header.newBuilder();
+ int i = 0;
+ for (ImmutableByteSequence bitString : piHeader.bitStrings()) {
+ builder.setBitstrings(i, ByteString.copyFrom(bitString.asArray()));
+ i++;
+ }
+ return builder.setIsValid(piHeader.isValid()).build();
+ }
+
+ private static PiHeader decodeHeader(P4Header p4Header) {
+ List<ImmutableByteSequence> bitStrings = p4Header.getBitstringsList().stream()
+ .map(bit -> ImmutableByteSequence.copyFrom(bit.asReadOnlyByteBuffer()))
+ .collect(Collectors.toList());
+
+ return PiHeader.of(p4Header.getIsValid(), bitStrings);
+ }
+
+ private static P4HeaderUnion encodeHeaderUnion(PiHeaderUnion headerUnion) {
+
+ P4HeaderUnion.Builder builder = P4HeaderUnion.newBuilder();
+ if (headerUnion.isValid()) {
+ builder.setValidHeader(encodeHeader(headerUnion.header()));
+ builder.setValidHeaderName(headerUnion.headerName());
+ } else {
+ // An empty string indicates that none of the union members are valid and
+ // valid_header must therefore be unset.
+ builder.setValidHeaderName("");
+ }
+
+ return builder.build();
+ }
+
+ private static PiHeaderUnion decodeHeaderUnion(P4HeaderUnion p4HeaderUnion) {
+
+ return PiHeaderUnion.of(p4HeaderUnion.getValidHeaderName(),
+ decodeHeader(p4HeaderUnion.getValidHeader()));
+ }
+
+ private static P4StructLike encodeStruct(PiStruct piStruct) {
+ P4StructLike.Builder builder = P4StructLike.newBuilder();
+ builder.addAllMembers(piStruct.struct().stream()
+ .map(P4DataCodec::encodeP4Data)
+ .collect(Collectors.toList()));
+ return builder.build();
+ }
+
+ private static PiStruct decodeStruct(P4StructLike p4StructLike) {
+
+ return PiStruct.of(p4StructLike.getMembersList().stream()
+ .map(P4DataCodec::decodeP4Data)
+ .collect(Collectors.toList()));
+ }
+
+ private static P4StructLike encodeTuple(PiTuple piTuple) {
+ P4StructLike.Builder builder = P4StructLike.newBuilder();
+ builder.addAllMembers(piTuple.tuple().stream()
+ .map(P4DataCodec::encodeP4Data)
+ .collect(Collectors.toList()));
+ return builder.build();
+ }
+
+ private static PiTuple decodeTuple(P4StructLike p4StructLike) {
+
+ return PiTuple.of(p4StructLike.getMembersList().stream()
+ .map(P4DataCodec::decodeP4Data)
+ .collect(Collectors.toList()));
+ }
+
+ static P4Data encodeP4Data(PiData piData) {
+
+ P4Data.Builder builder = P4Data.newBuilder();
+ switch (piData.type()) {
+ case BITSTRING:
+ builder.setBitstring(ByteString.copyFrom(((PiBitString) piData).bitString().asArray()));
+ break;
+ case ENUMSTRING:
+ builder.setEnum(((PiEnumString) piData).enumString());
+ break;
+ case ERRORSTRING:
+ builder.setError(((PiErrorString) piData).errorString());
+ break;
+ case BOOL:
+ builder.setBool(((PiBool) piData).bool());
+ break;
+ case STRUCT:
+ builder.setStruct(encodeStruct((PiStruct) piData));
+ break;
+ case TUPLE:
+ builder.setTuple(encodeTuple((PiTuple) piData));
+ break;
+ case HEADER:
+ builder.setHeader(encodeHeader((PiHeader) piData));
+ break;
+ case HEADERSTACK:
+ P4HeaderStack.Builder headerStack = P4HeaderStack.newBuilder();
+ int i = 0;
+ for (PiHeader header : ((PiHeaderStack) piData).headers()) {
+ headerStack.setEntries(i, encodeHeader(header));
+ i++;
+ }
+ builder.setHeaderStack(headerStack.build());
+ break;
+ case HEADERUNION:
+ builder.setHeaderUnion(encodeHeaderUnion((PiHeaderUnion) piData));
+ break;
+ case HEADERUNIONSTACK:
+ P4HeaderUnionStack.Builder headerUnionStack = P4HeaderUnionStack.newBuilder();
+ int j = 0;
+ for (PiHeaderUnion headerUnion : ((PiHeaderUnionStack) piData).headerUnions()) {
+ headerUnionStack.setEntries(j, encodeHeaderUnion(headerUnion));
+ j++;
+ }
+ builder.setHeaderUnionStack(headerUnionStack.build());
+ break;
+ default:
+ break;
+ }
+
+ return builder.build();
+ }
+
+ static PiData decodeP4Data(P4Data p4Data) {
+ PiData piData = null;
+
+ switch (p4Data.getDataCase()) {
+ case BITSTRING:
+ piData = PiBitString.of(ImmutableByteSequence.copyFrom(p4Data.getBitstring().asReadOnlyByteBuffer()));
+ break;
+ case BOOL:
+ piData = PiBool.of(p4Data.getBool());
+ break;
+ case TUPLE:
+ piData = decodeTuple(p4Data.getTuple());
+ break;
+ case STRUCT:
+ piData = decodeStruct(p4Data.getStruct());
+ break;
+ case HEADER:
+ piData = decodeHeader(p4Data.getHeader());
+ break;
+ case HEADER_UNION:
+ piData = decodeHeaderUnion(p4Data.getHeaderUnion());
+ break;
+ case HEADER_STACK:
+ piData = PiHeaderStack.of(p4Data.getHeaderStack().getEntriesList().stream()
+ .map(P4DataCodec::decodeHeader)
+ .collect(Collectors.toList()));
+ break;
+ case HEADER_UNION_STACK:
+ piData = PiHeaderUnionStack.of(p4Data.getHeaderUnionStack()
+ .getEntriesList().stream()
+ .map(P4DataCodec::decodeHeaderUnion)
+ .collect(Collectors.toList()));
+ break;
+ case ENUM:
+ piData = PiEnumString.of(p4Data.getEnum());
+ break;
+ case ERROR:
+ piData = PiErrorString.of(p4Data.getError());
+ break;
+ case DATA_NOT_SET:
+ break;
+ default:
+ break;
+ }
+
+ return piData;
+ }
+}
diff --git a/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/codec/PacketInCodec.java b/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/codec/PacketInCodec.java
new file mode 100644
index 0000000..e4de896
--- /dev/null
+++ b/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/codec/PacketInCodec.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2019-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.p4runtime.ctl.codec;
+
+import org.onosproject.net.pi.model.PiPacketOperationType;
+import org.onosproject.net.pi.model.PiPipeconf;
+import org.onosproject.net.pi.runtime.PiPacketOperation;
+import org.onosproject.p4runtime.ctl.utils.P4InfoBrowser;
+import p4.config.v1.P4InfoOuterClass;
+import p4.v1.P4RuntimeOuterClass;
+
+import static org.onlab.util.ImmutableByteSequence.copyFrom;
+import static org.onosproject.p4runtime.ctl.codec.Codecs.CODECS;
+
+/**
+ * Codec for P4Runtime PacketIn. Only decoding is supported, as encoding is not
+ * meaningful in this case (packet-ins are always generated by the server).
+ */
+public final class PacketInCodec
+ extends AbstractCodec<PiPacketOperation,
+ P4RuntimeOuterClass.PacketIn, Object> {
+
+ private static final String PACKET_IN = "packet_in";
+
+ @Override
+ protected P4RuntimeOuterClass.PacketIn encode(
+ PiPacketOperation piEntity, Object ignored, PiPipeconf pipeconf,
+ P4InfoBrowser browser)
+ throws CodecException {
+ throw new CodecException("Encoding of packet-in is not supported");
+ }
+
+ @Override
+ protected PiPacketOperation decode(
+ P4RuntimeOuterClass.PacketIn message, Object ignored,
+ PiPipeconf pipeconf, P4InfoBrowser browser)
+ throws CodecException, P4InfoBrowser.NotFoundException {
+ final P4InfoOuterClass.Preamble ctrlPktMetaPreamble = browser
+ .controllerPacketMetadatas()
+ .getByName(PACKET_IN)
+ .getPreamble();
+ return PiPacketOperation.builder()
+ .withType(PiPacketOperationType.PACKET_IN)
+ .withMetadatas(CODECS.packetMetadata().decodeAll(
+ message.getMetadataList(), ctrlPktMetaPreamble, pipeconf))
+ .withData(copyFrom(message.getPayload().asReadOnlyByteBuffer()))
+ .build();
+ }
+}
diff --git a/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/codec/PacketMetadataCodec.java b/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/codec/PacketMetadataCodec.java
new file mode 100644
index 0000000..3f78085
--- /dev/null
+++ b/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/codec/PacketMetadataCodec.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2019-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.p4runtime.ctl.codec;
+
+import com.google.protobuf.ByteString;
+import org.onosproject.net.pi.model.PiPacketMetadataId;
+import org.onosproject.net.pi.model.PiPipeconf;
+import org.onosproject.net.pi.runtime.PiPacketMetadata;
+import org.onosproject.p4runtime.ctl.utils.P4InfoBrowser;
+import p4.config.v1.P4InfoOuterClass;
+import p4.v1.P4RuntimeOuterClass;
+
+import static org.onlab.util.ImmutableByteSequence.copyFrom;
+
+/**
+ * Coded for P4Runtime PacketMetadata. The metadata is expected to be a Preamble
+ * of a P4Info.ControllerPacketMetadata message.
+ */
+public final class PacketMetadataCodec
+ extends AbstractCodec<PiPacketMetadata,
+ P4RuntimeOuterClass.PacketMetadata, P4InfoOuterClass.Preamble> {
+
+ @Override
+ protected P4RuntimeOuterClass.PacketMetadata encode(
+ PiPacketMetadata piEntity, P4InfoOuterClass.Preamble ctrlPktMetaPreamble,
+ PiPipeconf pipeconf, P4InfoBrowser browser)
+ throws P4InfoBrowser.NotFoundException {
+ final int metadataId = browser
+ .packetMetadatas(ctrlPktMetaPreamble.getId())
+ .getByName(piEntity.id().id()).getId();
+ return P4RuntimeOuterClass.PacketMetadata.newBuilder()
+ .setMetadataId(metadataId)
+ .setValue(ByteString.copyFrom(piEntity.value().asReadOnlyBuffer()))
+ .build();
+ }
+
+ @Override
+ protected PiPacketMetadata decode(
+ P4RuntimeOuterClass.PacketMetadata message,
+ P4InfoOuterClass.Preamble ctrlPktMetaPreamble,
+ PiPipeconf pipeconf, P4InfoBrowser browser)
+ throws P4InfoBrowser.NotFoundException {
+ final String packetMetadataName = browser
+ .packetMetadatas(ctrlPktMetaPreamble.getId())
+ .getById(message.getMetadataId()).getName();
+ final PiPacketMetadataId metadataId = PiPacketMetadataId
+ .of(packetMetadataName);
+ return PiPacketMetadata.builder()
+ .withId(metadataId)
+ .withValue(copyFrom(message.getValue().asReadOnlyByteBuffer()))
+ .build();
+ }
+}
diff --git a/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/codec/PacketOutCodec.java b/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/codec/PacketOutCodec.java
new file mode 100644
index 0000000..6d020c3
--- /dev/null
+++ b/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/codec/PacketOutCodec.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2019-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.p4runtime.ctl.codec;
+
+import com.google.protobuf.ByteString;
+import org.onosproject.net.pi.model.PiPipeconf;
+import org.onosproject.net.pi.runtime.PiPacketOperation;
+import org.onosproject.p4runtime.ctl.utils.P4InfoBrowser;
+import p4.config.v1.P4InfoOuterClass;
+import p4.v1.P4RuntimeOuterClass;
+
+import static org.onosproject.p4runtime.ctl.codec.Codecs.CODECS;
+
+/**
+ * Codec for P4Runtime PacketOut. Only encoding is supported, as decoding is not
+ * meaningful in this case (packet-outs are always generated by the client).
+ */
+public final class PacketOutCodec
+ extends AbstractCodec<PiPacketOperation,
+ P4RuntimeOuterClass.PacketOut, Object> {
+
+ private static final String PACKET_OUT = "packet_out";
+
+ @Override
+ protected P4RuntimeOuterClass.PacketOut encode(
+ PiPacketOperation piPacket, Object ignored, PiPipeconf pipeconf,
+ P4InfoBrowser browser)
+ throws CodecException, P4InfoBrowser.NotFoundException {
+ final P4InfoOuterClass.Preamble ctrlPktMetaPreamble = browser
+ .controllerPacketMetadatas()
+ .getByName(PACKET_OUT)
+ .getPreamble();
+ return P4RuntimeOuterClass.PacketOut.newBuilder()
+ .addAllMetadata(CODECS.packetMetadata().encodeAll(
+ piPacket.metadatas(), ctrlPktMetaPreamble, pipeconf))
+ .setPayload(ByteString.copyFrom(piPacket.data().asReadOnlyBuffer()))
+ .build();
+
+ }
+
+ @Override
+ protected PiPacketOperation decode(
+ P4RuntimeOuterClass.PacketOut message, Object ignored,
+ PiPipeconf pipeconf, P4InfoBrowser browser)
+ throws CodecException {
+ throw new CodecException("Decoding of packet-out is not supported");
+ }
+}
diff --git a/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/codec/PreReplicaCodec.java b/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/codec/PreReplicaCodec.java
new file mode 100644
index 0000000..7402b14
--- /dev/null
+++ b/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/codec/PreReplicaCodec.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2019-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.p4runtime.ctl.codec;
+
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.pi.model.PiPipeconf;
+import org.onosproject.net.pi.runtime.PiPreReplica;
+import org.onosproject.p4runtime.ctl.utils.P4InfoBrowser;
+import p4.v1.P4RuntimeOuterClass;
+
+import static java.lang.String.format;
+
+/**
+ * Codec for P4Runtime PRE Replica.
+ */
+public class PreReplicaCodec extends AbstractCodec<PiPreReplica,
+ P4RuntimeOuterClass.Replica, Object> {
+
+ @Override
+ protected P4RuntimeOuterClass.Replica encode(
+ PiPreReplica replica, Object ignore,
+ PiPipeconf pipeconf, P4InfoBrowser browser)
+ throws CodecException, P4InfoBrowser.NotFoundException {
+ final int p4PortId;
+ try {
+ p4PortId = Math.toIntExact(replica.egressPort().toLong());
+ } catch (ArithmeticException e) {
+ throw new CodecException(format(
+ "Cannot cast 64 bit port value '%s' to 32 bit",
+ replica.egressPort()));
+ }
+ return P4RuntimeOuterClass.Replica.newBuilder()
+ .setEgressPort(p4PortId)
+ .setInstance(replica.instanceId())
+ .build();
+ }
+
+ @Override
+ protected PiPreReplica decode(
+ P4RuntimeOuterClass.Replica message, Object ignore,
+ PiPipeconf pipeconf, P4InfoBrowser browser)
+ throws CodecException, P4InfoBrowser.NotFoundException {
+ return new PiPreReplica(
+ PortNumber.portNumber(message.getEgressPort()),
+ message.getInstance());
+ }
+}
diff --git a/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/codec/TableEntryCodec.java b/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/codec/TableEntryCodec.java
new file mode 100644
index 0000000..513b4fa
--- /dev/null
+++ b/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/codec/TableEntryCodec.java
@@ -0,0 +1,224 @@
+/*
+ * Copyright 2019-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.p4runtime.ctl.codec;
+
+import org.onosproject.net.pi.model.PiPipeconf;
+import org.onosproject.net.pi.model.PiTableId;
+import org.onosproject.net.pi.runtime.PiAction;
+import org.onosproject.net.pi.runtime.PiActionProfileGroupId;
+import org.onosproject.net.pi.runtime.PiActionProfileMemberId;
+import org.onosproject.net.pi.runtime.PiCounterCellData;
+import org.onosproject.net.pi.runtime.PiMatchKey;
+import org.onosproject.net.pi.runtime.PiTableAction;
+import org.onosproject.net.pi.runtime.PiTableEntry;
+import org.onosproject.net.pi.runtime.PiTableEntryHandle;
+import org.onosproject.p4runtime.ctl.utils.P4InfoBrowser;
+import p4.config.v1.P4InfoOuterClass;
+import p4.v1.P4RuntimeOuterClass;
+
+import java.util.OptionalInt;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static java.lang.String.format;
+import static org.onosproject.p4runtime.ctl.codec.Codecs.CODECS;
+
+/**
+ * Codec for P4Runtime TableEntry.
+ */
+public final class TableEntryCodec
+ extends AbstractEntityCodec<PiTableEntry, PiTableEntryHandle,
+ P4RuntimeOuterClass.TableEntry, Object> {
+
+ @Override
+ protected P4RuntimeOuterClass.TableEntry encode(
+ PiTableEntry piTableEntry, Object ignored, PiPipeconf pipeconf,
+ P4InfoBrowser browser)
+ throws CodecException, P4InfoBrowser.NotFoundException {
+ final P4RuntimeOuterClass.TableEntry.Builder tableEntryMsgBuilder =
+ keyMsgBuilder(piTableEntry.table(), piTableEntry.matchKey(),
+ piTableEntry.priority(), pipeconf, browser);
+ // Controller metadata (cookie)
+ tableEntryMsgBuilder.setControllerMetadata(piTableEntry.cookie());
+ // Timeout.
+ if (piTableEntry.timeout().isPresent()) {
+ // FIXME: timeout is supported in P4Runtime v1.0
+ log.warn("Found PI table entry with timeout set, " +
+ "not supported in P4Runtime: {}", piTableEntry);
+ }
+ // Table action.
+ if (piTableEntry.action() != null) {
+ tableEntryMsgBuilder.setAction(
+ encodePiTableAction(piTableEntry.action(), pipeconf));
+ }
+ // Counter.
+ if (piTableEntry.counter() != null) {
+ tableEntryMsgBuilder.setCounterData(encodeCounter(piTableEntry.counter()));
+ }
+ return tableEntryMsgBuilder.build();
+ }
+
+ @Override
+ protected P4RuntimeOuterClass.TableEntry encodeKey(
+ PiTableEntryHandle handle, Object metadata, PiPipeconf pipeconf,
+ P4InfoBrowser browser) throws CodecException, P4InfoBrowser.NotFoundException {
+ return keyMsgBuilder(handle.tableId(), handle.matchKey(),
+ handle.priority(), pipeconf, browser).build();
+ }
+
+ @Override
+ protected P4RuntimeOuterClass.TableEntry encodeKey(
+ PiTableEntry piEntity, Object metadata, PiPipeconf pipeconf,
+ P4InfoBrowser browser) throws CodecException, P4InfoBrowser.NotFoundException {
+ return keyMsgBuilder(piEntity.table(), piEntity.matchKey(),
+ piEntity.priority(), pipeconf, browser).build();
+ }
+
+ @SuppressWarnings("OptionalUsedAsFieldOrParameterType")
+ private P4RuntimeOuterClass.TableEntry.Builder keyMsgBuilder(
+ PiTableId tableId, PiMatchKey matchKey, OptionalInt priority,
+ PiPipeconf pipeconf, P4InfoBrowser browser)
+ throws P4InfoBrowser.NotFoundException, CodecException {
+ final P4RuntimeOuterClass.TableEntry.Builder tableEntryMsgBuilder =
+ P4RuntimeOuterClass.TableEntry.newBuilder();
+ final P4InfoOuterClass.Preamble tablePreamble = browser.tables()
+ .getByName(tableId.id()).getPreamble();
+ // Table id.
+ tableEntryMsgBuilder.setTableId(tablePreamble.getId());
+ // Field matches.
+ if (matchKey.equals(PiMatchKey.EMPTY)) {
+ tableEntryMsgBuilder.setIsDefaultAction(true);
+ } else {
+ tableEntryMsgBuilder.addAllMatch(
+ CODECS.fieldMatch().encodeAll(
+ matchKey.fieldMatches(),
+ tablePreamble, pipeconf));
+ }
+ // Priority.
+ priority.ifPresent(tableEntryMsgBuilder::setPriority);
+ return tableEntryMsgBuilder;
+ }
+
+ @Override
+ protected PiTableEntry decode(
+ P4RuntimeOuterClass.TableEntry message, Object ignored,
+ PiPipeconf pipeconf, P4InfoBrowser browser)
+ throws CodecException, P4InfoBrowser.NotFoundException {
+ PiTableEntry.Builder piTableEntryBuilder = PiTableEntry.builder();
+
+ P4InfoOuterClass.Preamble tablePreamble = browser.tables()
+ .getById(message.getTableId()).getPreamble();
+
+ // Table id.
+ piTableEntryBuilder.forTable(PiTableId.of(tablePreamble.getName()));
+
+ // Priority.
+ if (message.getPriority() > 0) {
+ piTableEntryBuilder.withPriority(message.getPriority());
+ }
+
+ // Controller metadata (cookie)
+ piTableEntryBuilder.withCookie(message.getControllerMetadata());
+
+ // Table action.
+ if (message.hasAction()) {
+ piTableEntryBuilder.withAction(decodeTableActionMsg(
+ message.getAction(), pipeconf));
+ }
+
+ // Timeout.
+ // FIXME: how to decode table entry messages with timeout, given that
+ // the timeout value is lost after encoding?
+
+ // Match key for field matches.
+ piTableEntryBuilder.withMatchKey(
+ PiMatchKey.builder()
+ .addFieldMatches(CODECS.fieldMatch().decodeAll(
+ message.getMatchList(),
+ tablePreamble, pipeconf))
+ .build());
+
+ // Counter.
+ if (message.hasCounterData()) {
+ piTableEntryBuilder.withCounterCellData(decodeCounter(message.getCounterData()));
+ }
+
+ return piTableEntryBuilder.build();
+ }
+
+ private P4RuntimeOuterClass.TableAction encodePiTableAction(
+ PiTableAction piTableAction, PiPipeconf pipeconf)
+ throws CodecException {
+ checkNotNull(piTableAction, "Cannot encode null PiTableAction");
+ final P4RuntimeOuterClass.TableAction.Builder tableActionMsgBuilder =
+ P4RuntimeOuterClass.TableAction.newBuilder();
+ switch (piTableAction.type()) {
+ case ACTION:
+ P4RuntimeOuterClass.Action theAction = CODECS.action()
+ .encode((PiAction) piTableAction, null, pipeconf);
+ tableActionMsgBuilder.setAction(theAction);
+ break;
+ case ACTION_PROFILE_GROUP_ID:
+ tableActionMsgBuilder.setActionProfileGroupId(
+ ((PiActionProfileGroupId) piTableAction).id());
+ break;
+ case ACTION_PROFILE_MEMBER_ID:
+ tableActionMsgBuilder.setActionProfileMemberId(
+ ((PiActionProfileMemberId) piTableAction).id());
+ break;
+ default:
+ throw new CodecException(
+ format("Building of table action type %s not implemented",
+ piTableAction.type()));
+ }
+ return tableActionMsgBuilder.build();
+ }
+
+ private PiTableAction decodeTableActionMsg(
+ P4RuntimeOuterClass.TableAction tableActionMsg, PiPipeconf pipeconf)
+ throws CodecException {
+ P4RuntimeOuterClass.TableAction.TypeCase typeCase = tableActionMsg.getTypeCase();
+ switch (typeCase) {
+ case ACTION:
+ P4RuntimeOuterClass.Action actionMsg = tableActionMsg.getAction();
+ return CODECS.action().decode(
+ actionMsg, null, pipeconf);
+ case ACTION_PROFILE_GROUP_ID:
+ return PiActionProfileGroupId.of(
+ tableActionMsg.getActionProfileGroupId());
+ case ACTION_PROFILE_MEMBER_ID:
+ return PiActionProfileMemberId.of(
+ tableActionMsg.getActionProfileMemberId());
+ default:
+ throw new CodecException(
+ format("Decoding of table action type %s not implemented",
+ typeCase.name()));
+ }
+ }
+
+ private P4RuntimeOuterClass.CounterData encodeCounter(
+ PiCounterCellData piCounterCellData) {
+ return P4RuntimeOuterClass.CounterData.newBuilder()
+ .setPacketCount(piCounterCellData.packets())
+ .setByteCount(piCounterCellData.bytes()).build();
+ }
+
+ private PiCounterCellData decodeCounter(
+ P4RuntimeOuterClass.CounterData counterData) {
+ return new PiCounterCellData(
+ counterData.getPacketCount(), counterData.getByteCount());
+ }
+}
diff --git a/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/codec/Utils.java b/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/codec/Utils.java
new file mode 100644
index 0000000..c72489a
--- /dev/null
+++ b/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/codec/Utils.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2019-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.p4runtime.ctl.codec;
+
+import com.google.protobuf.ByteString;
+
+import static java.lang.String.format;
+
+/**
+ * Codec utilities.
+ */
+final class Utils {
+
+ private Utils() {
+ // Hide default construction
+ }
+
+ static void assertSize(String entityDescr, ByteString value, int bitWidth)
+ throws CodecException {
+
+ int byteWidth = (int) Math.ceil((float) bitWidth / 8);
+ if (value.size() != byteWidth) {
+ throw new CodecException(format(
+ "Wrong size for %s, expected %d bytes, but found %d",
+ entityDescr, byteWidth, value.size()));
+ }
+ }
+
+ static void assertPrefixLen(String entityDescr, int prefixLength, int bitWidth)
+ throws CodecException {
+
+ if (prefixLength > bitWidth) {
+ throw new CodecException(format(
+ "wrong prefix length for %s, field size is %d bits, but found one is %d",
+ entityDescr, bitWidth, prefixLength));
+ }
+ }
+}
diff --git a/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/codec/package-info.java b/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/codec/package-info.java
new file mode 100644
index 0000000..25cee2b
--- /dev/null
+++ b/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/codec/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2019-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Classes to translates from PI framework-related objects to P4Runtime protobuf
+ * messages, and vice versa.
+ */
+package org.onosproject.p4runtime.ctl.codec;
diff --git a/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/utils/P4InfoBrowser.java b/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/utils/P4InfoBrowser.java
new file mode 100644
index 0000000..29cddff
--- /dev/null
+++ b/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/utils/P4InfoBrowser.java
@@ -0,0 +1,348 @@
+/*
+ * Copyright 2019-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.p4runtime.ctl.utils;
+
+
+import com.google.common.collect.Maps;
+import com.google.protobuf.Message;
+import p4.config.v1.P4InfoOuterClass.Action;
+import p4.config.v1.P4InfoOuterClass.ActionProfile;
+import p4.config.v1.P4InfoOuterClass.ControllerPacketMetadata;
+import p4.config.v1.P4InfoOuterClass.Counter;
+import p4.config.v1.P4InfoOuterClass.DirectCounter;
+import p4.config.v1.P4InfoOuterClass.DirectMeter;
+import p4.config.v1.P4InfoOuterClass.MatchField;
+import p4.config.v1.P4InfoOuterClass.Meter;
+import p4.config.v1.P4InfoOuterClass.P4Info;
+import p4.config.v1.P4InfoOuterClass.Preamble;
+import p4.config.v1.P4InfoOuterClass.Table;
+
+import java.util.Map;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static java.lang.String.format;
+
+/**
+ * Utility class to easily retrieve information from a P4Info protobuf message.
+ */
+public final class P4InfoBrowser {
+
+ private final EntityBrowser<Table> tables = new EntityBrowser<>("table");
+ private final EntityBrowser<Action> actions = new EntityBrowser<>("action");
+ private final EntityBrowser<ActionProfile> actionProfiles = new EntityBrowser<>("action profile");
+ private final EntityBrowser<Counter> counters = new EntityBrowser<>("counter");
+ private final EntityBrowser<DirectCounter> directCounters = new EntityBrowser<>("direct counter");
+ private final EntityBrowser<Meter> meters = new EntityBrowser<>("meter");
+ private final EntityBrowser<DirectMeter> directMeters = new EntityBrowser<>("direct meter");
+ private final EntityBrowser<ControllerPacketMetadata> ctrlPktMetadatas =
+ new EntityBrowser<>("controller packet metadata");
+ private final Map<Integer, EntityBrowser<Action.Param>> actionParams = Maps.newHashMap();
+ private final Map<Integer, EntityBrowser<MatchField>> matchFields = Maps.newHashMap();
+ private final Map<Integer, EntityBrowser<ControllerPacketMetadata.Metadata>> ctrlPktMetadatasMetadata =
+ Maps.newHashMap();
+
+ /**
+ * Creates a new browser for the given P4Info.
+ *
+ * @param p4info P4Info protobuf message
+ */
+ public P4InfoBrowser(P4Info p4info) {
+ parseP4Info(p4info);
+ }
+
+ private void parseP4Info(P4Info p4info) {
+ p4info.getTablesList().forEach(
+ entity -> {
+ tables.addWithPreamble(entity.getPreamble(), entity);
+ // Index match fields.
+ int tableId = entity.getPreamble().getId();
+ String tableName = entity.getPreamble().getName();
+ EntityBrowser<MatchField> matchFieldBrowser = new EntityBrowser<>(format(
+ "match field for table '%s'", tableName));
+ entity.getMatchFieldsList().forEach(m -> matchFieldBrowser.add(m.getName(), null, m.getId(), m));
+ matchFields.put(tableId, matchFieldBrowser);
+ });
+
+ p4info.getActionsList().forEach(
+ entity -> {
+ actions.addWithPreamble(entity.getPreamble(), entity);
+ // Index action params.
+ int actionId = entity.getPreamble().getId();
+ String actionName = entity.getPreamble().getName();
+ EntityBrowser<Action.Param> paramBrowser = new EntityBrowser<>(format(
+ "param for action '%s'", actionName));
+ entity.getParamsList().forEach(p -> paramBrowser.add(p.getName(), null, p.getId(), p));
+ actionParams.put(actionId, paramBrowser);
+ });
+
+ p4info.getActionProfilesList().forEach(
+ entity -> actionProfiles.addWithPreamble(entity.getPreamble(), entity));
+
+ p4info.getCountersList().forEach(
+ entity -> counters.addWithPreamble(entity.getPreamble(), entity));
+
+ p4info.getDirectCountersList().forEach(
+ entity -> directCounters.addWithPreamble(entity.getPreamble(), entity));
+
+ p4info.getMetersList().forEach(
+ entity -> meters.addWithPreamble(entity.getPreamble(), entity));
+
+ p4info.getDirectMetersList().forEach(
+ entity -> directMeters.addWithPreamble(entity.getPreamble(), entity));
+
+ p4info.getControllerPacketMetadataList().forEach(
+ entity -> {
+ ctrlPktMetadatas.addWithPreamble(entity.getPreamble(), entity);
+ // Index control packet metadata metadata.
+ int ctrlPktMetadataId = entity.getPreamble().getId();
+ String ctrlPktMetadataName = entity.getPreamble().getName();
+ EntityBrowser<ControllerPacketMetadata.Metadata> metadataBrowser = new EntityBrowser<>(format(
+ "metadata field for controller packet metadata '%s'", ctrlPktMetadataName));
+ entity.getMetadataList().forEach(m -> metadataBrowser.add(m.getName(), null, m.getId(), m));
+ ctrlPktMetadatasMetadata.put(ctrlPktMetadataId, metadataBrowser);
+ });
+ }
+
+ /**
+ * Returns a browser for tables.
+ *
+ * @return table browser
+ */
+ public EntityBrowser<Table> tables() {
+ return tables;
+ }
+
+ /**
+ * Returns a browser for actions.
+ *
+ * @return action browser
+ */
+ public EntityBrowser<Action> actions() {
+ return actions;
+ }
+
+ /**
+ * Returns a browser for action profiles.
+ *
+ * @return action profile browser
+ */
+ public EntityBrowser<ActionProfile> actionProfiles() {
+ return actionProfiles;
+ }
+
+ /**
+ * Returns a browser for counters.
+ *
+ * @return counter browser
+ */
+ public EntityBrowser<Counter> counters() {
+ return counters;
+ }
+
+ /**
+ * Returns a browser for direct counters.
+ *
+ * @return direct counter browser
+ */
+ public EntityBrowser<DirectCounter> directCounters() {
+ return directCounters;
+ }
+
+ /**
+ * Returns a browser for meters.
+ *
+ * @return meter browser
+ */
+ public EntityBrowser<Meter> meters() {
+ return meters;
+ }
+
+ /**
+ * Returns a browser for direct meters.
+ *
+ * @return table browser
+ */
+ public EntityBrowser<DirectMeter> directMeters() {
+ return directMeters;
+ }
+
+ /**
+ * Returns a browser for controller packet metadata.
+ *
+ * @return controller packet metadata browser
+ */
+ public EntityBrowser<ControllerPacketMetadata> controllerPacketMetadatas() {
+ return ctrlPktMetadatas;
+ }
+
+ /**
+ * Returns a browser for params of the given action.
+ *
+ * @param actionId action identifier
+ * @return action params browser
+ * @throws NotFoundException if the action cannot be found
+ */
+ public EntityBrowser<Action.Param> actionParams(int actionId) throws NotFoundException {
+ // Throws exception if action id is not found.
+ actions.getById(actionId);
+ return actionParams.get(actionId);
+ }
+
+ /**
+ * Returns a browser for match fields of the given table.
+ *
+ * @param tableId table identifier
+ * @return match field browser
+ * @throws NotFoundException if the table cannot be found
+ */
+ public EntityBrowser<MatchField> matchFields(int tableId) throws NotFoundException {
+ // Throws exception if action id is not found.
+ tables.getById(tableId);
+ return matchFields.get(tableId);
+ }
+
+ /**
+ * Returns a browser for metadata fields of the controller packet metadata.
+ *
+ * @param controllerPacketMetadataId controller packet metadata identifier
+ * @return metadata browser
+ * @throws NotFoundException controller packet metadata cannot be found
+ */
+ public EntityBrowser<ControllerPacketMetadata.Metadata> packetMetadatas(int controllerPacketMetadataId)
+ throws NotFoundException {
+ // Throws exception if controller packet metadata id is not found.
+ ctrlPktMetadatas.getById(controllerPacketMetadataId);
+ return ctrlPktMetadatasMetadata.get(controllerPacketMetadataId);
+ }
+
+ /**
+ * Browser of P4Info entities.
+ *
+ * @param <T> protobuf message type
+ */
+ public static final class EntityBrowser<T extends Message> {
+
+ private String entityName;
+ private final Map<String, T> names = Maps.newHashMap();
+ private final Map<String, String> aliasToNames = Maps.newHashMap();
+ private final Map<Integer, T> ids = Maps.newHashMap();
+
+ private EntityBrowser(String entityName) {
+ this.entityName = entityName;
+ }
+
+ /**
+ * Adds the given entity identified by the given name, alias (nullable) and id.
+ *
+ * @param name entity name
+ * @param alias entity alias or null
+ * @param id entity id
+ * @param entity entity message
+ */
+ private void add(String name, String alias, int id, T entity) {
+ checkNotNull(name);
+ checkArgument(!name.isEmpty(), "Name cannot be empty");
+ checkNotNull(entity);
+ names.put(name, entity);
+ ids.put(id, entity);
+ if (alias != null && !alias.isEmpty()) {
+ aliasToNames.put(alias, name);
+ }
+ }
+
+ /**
+ * Adds the given entity identified by the given P4Info preamble.
+ *
+ * @param preamble P4Info preamble protobuf message
+ * @param entity entity message
+ */
+ private void addWithPreamble(Preamble preamble, T entity) {
+ checkNotNull(preamble);
+ add(preamble.getName(), preamble.getAlias(), preamble.getId(), entity);
+ }
+
+ /**
+ * Returns true if the P4Info defines an entity with such name, false otherwise.
+ *
+ * @param name entity name
+ * @return boolean
+ */
+ public boolean hasName(String name) {
+ return names.containsKey(name);
+ }
+
+ /**
+ * Returns the entity identified by the given name, if present, otherwise, throws an exception.
+ *
+ * @param name entity name or alias
+ * @return entity message
+ * @throws NotFoundException if the entity cannot be found
+ */
+ public T getByName(String name) throws NotFoundException {
+ if (hasName(name)) {
+ return names.get(name);
+ } else {
+ final String hint = aliasToNames.containsKey(name)
+ ? format("Did you mean '%s'? Make sure to use entity names in PI IDs, not aliases",
+ aliasToNames.get(name))
+ : "";
+ throw new NotFoundException(entityName, name, hint);
+ }
+ }
+
+ /**
+ * Returns true if the P4Info defines an entity with such id, false otherwise.
+ *
+ * @param id entity id
+ * @return boolean
+ */
+ public boolean hasId(int id) {
+ return ids.containsKey(id);
+ }
+
+ /**
+ * Returns the entity identified by the given id, if present, otherwise, throws an exception.
+ *
+ * @param id entity id
+ * @return entity message
+ * @throws NotFoundException if the entity cannot be found
+ */
+ public T getById(int id) throws NotFoundException {
+ if (!hasId(id)) {
+ throw new NotFoundException(entityName, id);
+ }
+ return ids.get(id);
+ }
+ }
+
+ /**
+ * Signals tha an entity cannot be found in the P4Info.
+ */
+ public static final class NotFoundException extends Exception {
+
+ public NotFoundException(String entityName, String key, String hint) {
+ super(format(
+ "No such %s in P4Info with name '%s'%s",
+ entityName, key, hint.isEmpty() ? "" : " (" + hint + ")"));
+ }
+
+ public NotFoundException(String entityName, int id) {
+ super(format("No such %s in P4Info with id '%d'", entityName, id));
+ }
+ }
+}
diff --git a/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/utils/PipeconfHelper.java b/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/utils/PipeconfHelper.java
new file mode 100644
index 0000000..f5afd41
--- /dev/null
+++ b/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/utils/PipeconfHelper.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2019-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.p4runtime.ctl.utils;
+
+import com.google.common.cache.Cache;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.collect.Maps;
+import com.google.protobuf.ExtensionRegistry;
+import com.google.protobuf.TextFormat;
+import org.onosproject.net.pi.model.PiPipeconf;
+import org.slf4j.Logger;
+import p4.config.v1.P4InfoOuterClass.P4Info;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.Map;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+
+import static org.onosproject.net.pi.model.PiPipeconf.ExtensionType.P4_INFO_TEXT;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Utility class to deal with pipeconfs in the context of P4Runtime.
+ */
+public final class PipeconfHelper {
+
+ private static final int P4INFO_BROWSER_EXPIRE_TIME_IN_MIN = 10;
+ private static final Logger log = getLogger(PipeconfHelper.class);
+
+ private static final Cache<Long, P4InfoBrowser> BROWSERS = CacheBuilder.newBuilder()
+ .expireAfterAccess(P4INFO_BROWSER_EXPIRE_TIME_IN_MIN, TimeUnit.MINUTES)
+ .build();
+ private static final Map<Long, P4Info> P4INFOS = Maps.newConcurrentMap();
+
+ private PipeconfHelper() {
+ // hide.
+ }
+
+ /**
+ * Extracts and returns a P4Info protobuf message from the given pipeconf. If the pipeconf does not define any
+ * extension of type {@link PiPipeconf.ExtensionType#P4_INFO_TEXT}, returns null;
+ *
+ * @param pipeconf pipeconf
+ * @return P4Info or null
+ */
+ public static P4Info getP4Info(PiPipeconf pipeconf) {
+ return P4INFOS.computeIfAbsent(pipeconf.fingerprint(), piPipeconfId -> {
+ if (!pipeconf.extension(P4_INFO_TEXT).isPresent()) {
+ log.warn("Missing P4Info extension in pipeconf {}", pipeconf.id());
+ return null;
+ }
+
+ InputStream p4InfoStream = pipeconf.extension(P4_INFO_TEXT).get();
+ P4Info.Builder p4iInfoBuilder = P4Info.newBuilder();
+ try {
+ TextFormat.getParser().merge(new InputStreamReader(p4InfoStream), ExtensionRegistry.getEmptyRegistry(),
+ p4iInfoBuilder);
+ } catch (IOException ex) {
+ log.warn("Unable to parse P4Info of pipeconf {}: {}", pipeconf.id(), ex.getMessage());
+ return null;
+ }
+
+ return p4iInfoBuilder.build();
+ });
+ }
+
+ /**
+ * Returns a P4Info browser for the given pipeconf. If the pipeconf does not define any extension of type
+ * {@link PiPipeconf.ExtensionType#P4_INFO_TEXT}, returns null;
+ *
+ * @param pipeconf pipeconf
+ * @return P4Info browser or null
+ */
+ public static P4InfoBrowser getP4InfoBrowser(PiPipeconf pipeconf) {
+ try {
+ return BROWSERS.get(pipeconf.fingerprint(), () -> {
+ P4Info p4info = PipeconfHelper.getP4Info(pipeconf);
+ if (p4info == null) {
+ return null;
+ } else {
+ return new P4InfoBrowser(p4info);
+ }
+ });
+ } catch (ExecutionException e) {
+ log.error("Exception while accessing the P4InfoBrowser cache", e);
+ return null;
+ }
+ }
+}
diff --git a/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/utils/package-info.java b/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/utils/package-info.java
new file mode 100644
index 0000000..ae09455
--- /dev/null
+++ b/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/utils/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2019-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Utility classes for the P4Runtime protocol subsystem.
+ */
+package org.onosproject.p4runtime.ctl.utils;