[ONOS-7583]Adding PI flow rules via REST API and unit tests
Change-Id: I15537097e7b6f8138f8ae43676d5dd22d8b9093a
diff --git a/core/common/src/main/java/org/onosproject/codec/impl/CriterionCodec.java b/core/common/src/main/java/org/onosproject/codec/impl/CriterionCodec.java
index ec1b061..4be8008 100644
--- a/core/common/src/main/java/org/onosproject/codec/impl/CriterionCodec.java
+++ b/core/common/src/main/java/org/onosproject/codec/impl/CriterionCodec.java
@@ -73,6 +73,14 @@
static final String TRIBUTARY_SLOT_LEN = "tributarySlotLen";
static final String TRIBUTARY_SLOT_BITMAP = "tributarySlotBitmap";
static final String ODU_SIGNAL_TYPE = "oduSignalType";
+ static final String PI_MATCHES = "matches";
+ static final String PI_MATCH_FIELD_ID = "field";
+ static final String PI_MATCH_TYPE = "match";
+ static final String PI_MATCH_VALUE = "value";
+ static final String PI_MATCH_PREFIX = "prefixLength";
+ static final String PI_MATCH_MASK = "mask";
+ static final String PI_MATCH_HIGH_VALUE = "highValue";
+ static final String PI_MATCH_LOW_VALUE = "lowValue";
@Override
public ObjectNode encode(Criterion criterion, CodecContext context) {
diff --git a/core/common/src/main/java/org/onosproject/codec/impl/DecodeCriterionCodecHelper.java b/core/common/src/main/java/org/onosproject/codec/impl/DecodeCriterionCodecHelper.java
index e8056e2..9b1b7c2 100644
--- a/core/common/src/main/java/org/onosproject/codec/impl/DecodeCriterionCodecHelper.java
+++ b/core/common/src/main/java/org/onosproject/codec/impl/DecodeCriterionCodecHelper.java
@@ -33,6 +33,9 @@
import org.onosproject.net.PortNumber;
import org.onosproject.net.flow.criteria.Criteria;
import org.onosproject.net.flow.criteria.Criterion;
+import org.onosproject.net.flow.criteria.PiCriterion;
+import org.onosproject.net.pi.model.PiMatchFieldId;
+import org.onosproject.net.pi.model.PiMatchType;
import java.util.HashMap;
import java.util.Map;
@@ -109,6 +112,7 @@
decoderMap.put(Criterion.Type.TUNNEL_ID.name(), new TunnelIdDecoder());
decoderMap.put(Criterion.Type.ODU_SIGID.name(), new OduSigIdDecoder());
decoderMap.put(Criterion.Type.ODU_SIGTYPE.name(), new OduSigTypeDecoder());
+ decoderMap.put(Criterion.Type.PROTOCOL_INDEPENDENT.name(), new PiDecoder());
}
private class EthTypeDecoder implements CriterionDecoder {
@@ -582,6 +586,89 @@
}
}
+ private class PiDecoder implements CriterionDecoder {
+ @Override
+ public Criterion decodeCriterion(ObjectNode json) {
+ PiCriterion.Builder builder = PiCriterion.builder();
+ JsonNode matchesNode = nullIsIllegal(json.get(CriterionCodec.PI_MATCHES),
+ CriterionCodec.PI_MATCHES + MISSING_MEMBER_MESSAGE);
+ if (matchesNode.isArray()) {
+ for (JsonNode node : matchesNode) {
+ String type = nullIsIllegal(node.get(CriterionCodec.PI_MATCH_TYPE),
+ CriterionCodec.PI_MATCH_TYPE + MISSING_MEMBER_MESSAGE).asText();
+ switch (PiMatchType.valueOf(type.toUpperCase())) {
+ case EXACT:
+ builder.matchExact(
+ PiMatchFieldId.of(
+ nullIsIllegal(node.get(CriterionCodec.PI_MATCH_FIELD_ID),
+ CriterionCodec.PI_MATCH_FIELD_ID +
+ MISSING_MEMBER_MESSAGE).asText()),
+ HexString.fromHexString(nullIsIllegal(node.get(CriterionCodec.PI_MATCH_VALUE),
+ CriterionCodec.PI_MATCH_VALUE +
+ MISSING_MEMBER_MESSAGE).asText(), null));
+ break;
+ case LPM:
+ builder.matchLpm(
+ PiMatchFieldId.of(
+ nullIsIllegal(node.get(CriterionCodec.PI_MATCH_FIELD_ID),
+ CriterionCodec.PI_MATCH_FIELD_ID +
+ MISSING_MEMBER_MESSAGE).asText()),
+ HexString.fromHexString(nullIsIllegal(node.get(CriterionCodec.PI_MATCH_VALUE),
+ CriterionCodec.PI_MATCH_VALUE +
+ MISSING_MEMBER_MESSAGE).asText(), null),
+ nullIsIllegal(node.get(CriterionCodec.PI_MATCH_PREFIX),
+ CriterionCodec.PI_MATCH_PREFIX +
+ MISSING_MEMBER_MESSAGE).asInt());
+ break;
+ case TERNARY:
+ builder.matchTernary(
+ PiMatchFieldId.of(
+ nullIsIllegal(node.get(CriterionCodec.PI_MATCH_FIELD_ID),
+ CriterionCodec.PI_MATCH_FIELD_ID +
+ MISSING_MEMBER_MESSAGE).asText()),
+ HexString.fromHexString(nullIsIllegal(node.get(CriterionCodec.PI_MATCH_VALUE),
+ CriterionCodec.PI_MATCH_VALUE +
+ MISSING_MEMBER_MESSAGE).asText(), null),
+ HexString.fromHexString(nullIsIllegal(node.get(CriterionCodec.PI_MATCH_MASK),
+ CriterionCodec.PI_MATCH_MASK +
+ MISSING_MEMBER_MESSAGE).asText(), null));
+ break;
+ case RANGE:
+ builder.matchRange(
+ PiMatchFieldId.of(
+ nullIsIllegal(node.get(CriterionCodec.PI_MATCH_FIELD_ID),
+ CriterionCodec.PI_MATCH_FIELD_ID +
+ MISSING_MEMBER_MESSAGE).asText()),
+ HexString.fromHexString(nullIsIllegal(node.get(CriterionCodec.PI_MATCH_LOW_VALUE),
+ CriterionCodec.PI_MATCH_LOW_VALUE +
+ MISSING_MEMBER_MESSAGE).asText(), null),
+ HexString.fromHexString(nullIsIllegal(node.get(CriterionCodec.PI_MATCH_HIGH_VALUE),
+ CriterionCodec.PI_MATCH_HIGH_VALUE +
+ MISSING_MEMBER_MESSAGE).asText(), null)
+ );
+ break;
+ case VALID:
+ builder.matchValid(
+ PiMatchFieldId.of(
+ nullIsIllegal(node.get(CriterionCodec.PI_MATCH_FIELD_ID),
+ CriterionCodec.PI_MATCH_FIELD_ID +
+ MISSING_MEMBER_MESSAGE).asText()),
+ nullIsIllegal(node.get(CriterionCodec.PI_MATCH_VALUE),
+ CriterionCodec.PI_MATCH_VALUE +
+ MISSING_MEMBER_MESSAGE).asBoolean());
+ break;
+ default:
+ throw new IllegalArgumentException("Type " + type + " is unsupported");
+ }
+ }
+ } else {
+ throw new IllegalArgumentException("Protocol-independent matches must be in an array.");
+ }
+
+ return builder.build();
+ }
+ }
+
/**
* Decodes the JSON into a criterion object.
*
diff --git a/core/common/src/main/java/org/onosproject/codec/impl/DecodeInstructionCodecHelper.java b/core/common/src/main/java/org/onosproject/codec/impl/DecodeInstructionCodecHelper.java
index e810274..6753fae 100644
--- a/core/common/src/main/java/org/onosproject/codec/impl/DecodeInstructionCodecHelper.java
+++ b/core/common/src/main/java/org/onosproject/codec/impl/DecodeInstructionCodecHelper.java
@@ -27,6 +27,7 @@
import org.onlab.packet.TpPort;
import org.onlab.packet.VlanId;
import org.onlab.util.HexString;
+import org.onlab.util.ImmutableByteSequence;
import org.onosproject.codec.CodecContext;
import org.onosproject.net.flow.ExtensionTreatmentCodec;
import org.onosproject.core.GroupId;
@@ -49,6 +50,13 @@
import org.onosproject.net.flow.instructions.L3ModificationInstruction;
import org.onosproject.net.flow.instructions.L4ModificationInstruction;
import org.onosproject.net.meter.MeterId;
+import org.onosproject.net.pi.model.PiActionId;
+import org.onosproject.net.pi.model.PiActionParamId;
+import org.onosproject.net.pi.runtime.PiAction;
+import org.onosproject.net.pi.runtime.PiActionGroupId;
+import org.onosproject.net.pi.runtime.PiActionGroupMemberId;
+import org.onosproject.net.pi.runtime.PiActionParam;
+import org.onosproject.net.pi.runtime.PiTableAction;
import org.slf4j.Logger;
import java.util.Map;
@@ -267,6 +275,54 @@
+ subType + " is not supported");
}
+ /**
+ * Decodes a protocol-independent instruction.
+ *
+ * @return instruction object decoded from the JSON
+ * @throws IllegalArgumentException if the JSON is invalid
+ */
+ private Instruction decodePi() {
+ String subType = nullIsIllegal(json.get(InstructionCodec.SUBTYPE),
+ InstructionCodec.SUBTYPE + InstructionCodec.ERROR_MESSAGE).asText();
+
+ if (subType.equals(PiTableAction.Type.ACTION.name())) {
+ PiActionId piActionId = PiActionId.of(nullIsIllegal(
+ json.get(InstructionCodec.PI_ACTION_ID),
+ InstructionCodec.PI_ACTION_ID + InstructionCodec.MISSING_MEMBER_MESSAGE).asText());
+ JsonNode params = json.get(InstructionCodec.PI_ACTION_PARAMS);
+
+ PiAction.Builder builder = PiAction.builder();
+ PiActionParam piActionParam;
+ PiActionParamId piActionParamId;
+ if (params != null) {
+ for (Map.Entry<String, String> param : ((Map<String, String>)
+ (context.mapper().convertValue(params, Map.class))).entrySet()) {
+ piActionParamId = PiActionParamId.of(param.getKey());
+ piActionParam = new PiActionParam(piActionParamId,
+ ImmutableByteSequence.copyFrom(
+ HexString.fromHexString(param.getValue(), null)));
+ builder.withParameter(piActionParam);
+ }
+ }
+
+ return Instructions.piTableAction(builder.withId(piActionId).build());
+ } else if (subType.equals(PiTableAction.Type.ACTION_GROUP_ID.name())) {
+ PiActionGroupId piActionGroupId = PiActionGroupId.of(nullIsIllegal(
+ json.get(InstructionCodec.PI_ACTION_GROUP_ID),
+ InstructionCodec.PI_ACTION_GROUP_ID + InstructionCodec.MISSING_MEMBER_MESSAGE).asInt());
+
+ return Instructions.piTableAction(piActionGroupId);
+ } else if (subType.equals(PiTableAction.Type.GROUP_MEMBER_ID.name())) {
+ PiActionGroupMemberId piActionGroupMemberId = PiActionGroupMemberId.of(nullIsIllegal(
+ json.get(InstructionCodec.PI_ACTION_GROUP_MEMBER_ID),
+ InstructionCodec.PI_ACTION_GROUP_MEMBER_ID + InstructionCodec.MISSING_MEMBER_MESSAGE).asInt());
+
+ return Instructions.piTableAction(piActionGroupMemberId);
+ }
+ throw new IllegalArgumentException("Protocol-independent Instruction subtype "
+ + subType + " is not supported");
+ }
+
private Instruction decodeStatTrigger() {
String statTriggerFlag = nullIsIllegal(json.get(InstructionCodec.STAT_TRIGGER_FLAG),
InstructionCodec.STAT_TRIGGER_FLAG + InstructionCodec.ERROR_MESSAGE).asText();
@@ -450,8 +506,11 @@
return decodeExtension();
} else if (type.equals(Instruction.Type.STAT_TRIGGER.name())) {
return decodeStatTrigger();
+ } else if (type.equals(Instruction.Type.PROTOCOL_INDEPENDENT.name())) {
+ return decodePi();
}
+
throw new IllegalArgumentException("Instruction type "
- + type + " is not supported");
+ + type + " is not supported");
}
}
diff --git a/core/common/src/main/java/org/onosproject/codec/impl/EncodeCriterionCodecHelper.java b/core/common/src/main/java/org/onosproject/codec/impl/EncodeCriterionCodecHelper.java
index 9d8ac65..efdc9ff 100644
--- a/core/common/src/main/java/org/onosproject/codec/impl/EncodeCriterionCodecHelper.java
+++ b/core/common/src/main/java/org/onosproject/codec/impl/EncodeCriterionCodecHelper.java
@@ -15,6 +15,7 @@
*/
package org.onosproject.codec.impl;
+import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.onlab.util.HexString;
import org.onosproject.codec.CodecContext;
@@ -42,6 +43,7 @@
import org.onosproject.net.flow.criteria.OchSignalTypeCriterion;
import org.onosproject.net.flow.criteria.OduSignalIdCriterion;
import org.onosproject.net.flow.criteria.OduSignalTypeCriterion;
+import org.onosproject.net.flow.criteria.PiCriterion;
import org.onosproject.net.flow.criteria.PortCriterion;
import org.onosproject.net.flow.criteria.SctpPortCriterion;
import org.onosproject.net.flow.criteria.TcpPortCriterion;
@@ -49,6 +51,13 @@
import org.onosproject.net.flow.criteria.UdpPortCriterion;
import org.onosproject.net.flow.criteria.VlanIdCriterion;
import org.onosproject.net.flow.criteria.VlanPcpCriterion;
+import org.onosproject.net.pi.model.PiMatchType;
+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.net.pi.runtime.PiValidFieldMatch;
import java.util.EnumMap;
@@ -123,6 +132,7 @@
formatMap.put(Criterion.Type.DUMMY, new FormatDummyType());
formatMap.put(Criterion.Type.ODU_SIGID, new FormatOduSignalId());
formatMap.put(Criterion.Type.ODU_SIGTYPE, new FormatOduSignalType());
+ formatMap.put(Criterion.Type.PROTOCOL_INDEPENDENT, new FormatProtocolIndependent());
// Currently unimplemented
formatMap.put(Criterion.Type.ARP_OP, new FormatUnknown());
formatMap.put(Criterion.Type.ARP_SPA, new FormatUnknown());
@@ -145,7 +155,6 @@
formatMap.put(Criterion.Type.UDP_DST_MASKED, new FormatUnknown());
formatMap.put(Criterion.Type.SCTP_SRC_MASKED, new FormatUnknown());
formatMap.put(Criterion.Type.SCTP_DST_MASKED, new FormatUnknown());
- formatMap.put(Criterion.Type.PROTOCOL_INDEPENDENT, new FormatUnknown());
}
@@ -479,6 +488,102 @@
}
}
+ private ObjectNode parsePiMatchExact(PiExactFieldMatch exactFieldMatch) {
+
+ ObjectNode matchExactNode = context.mapper().createObjectNode();
+ matchExactNode.put(CriterionCodec.PI_MATCH_FIELD_ID, exactFieldMatch.fieldId().id());
+ matchExactNode.put(CriterionCodec.PI_MATCH_TYPE, PiMatchType.EXACT.name().toLowerCase());
+ matchExactNode.put(CriterionCodec.PI_MATCH_VALUE,
+ HexString.toHexString(exactFieldMatch.value().asArray(),
+ null));
+ return matchExactNode;
+ }
+
+ private ObjectNode parsePiMatchLpm(PiLpmFieldMatch lpmFieldMatch) {
+
+ ObjectNode matchLpmNode = context.mapper().createObjectNode();
+ matchLpmNode.put(CriterionCodec.PI_MATCH_FIELD_ID, lpmFieldMatch.fieldId().id());
+ matchLpmNode.put(CriterionCodec.PI_MATCH_TYPE, PiMatchType.LPM.name().toLowerCase());
+ matchLpmNode.put(CriterionCodec.PI_MATCH_VALUE,
+ HexString.toHexString(lpmFieldMatch.value().asArray(),
+ null));
+ matchLpmNode.put(CriterionCodec.PI_MATCH_PREFIX, lpmFieldMatch.prefixLength());
+
+ return matchLpmNode;
+ }
+
+ private ObjectNode parsePiMatchTernary(PiTernaryFieldMatch ternaryFieldMatch) {
+
+ ObjectNode matchTernaryNode = context.mapper().createObjectNode();
+ matchTernaryNode.put(CriterionCodec.PI_MATCH_FIELD_ID, ternaryFieldMatch.fieldId().id());
+ matchTernaryNode.put(CriterionCodec.PI_MATCH_TYPE, PiMatchType.TERNARY.name().toLowerCase());
+ matchTernaryNode.put(CriterionCodec.PI_MATCH_VALUE,
+ HexString.toHexString(ternaryFieldMatch.value().asArray(),
+ null));
+ matchTernaryNode.put(CriterionCodec.PI_MATCH_MASK,
+ HexString.toHexString(ternaryFieldMatch.mask().asArray(),
+ null));
+
+ return matchTernaryNode;
+ }
+
+ private ObjectNode parsePiMatchRange(PiRangeFieldMatch rangeFieldMatch) {
+
+ ObjectNode matchRangeNode = context.mapper().createObjectNode();
+ matchRangeNode.put(CriterionCodec.PI_MATCH_FIELD_ID, rangeFieldMatch.fieldId().id());
+ matchRangeNode.put(CriterionCodec.PI_MATCH_TYPE, PiMatchType.RANGE.name().toLowerCase());
+ matchRangeNode.put(CriterionCodec.PI_MATCH_HIGH_VALUE,
+ HexString.toHexString(rangeFieldMatch.highValue().asArray(),
+ null));
+ matchRangeNode.put(CriterionCodec.PI_MATCH_LOW_VALUE,
+ HexString.toHexString(rangeFieldMatch.lowValue().asArray(),
+ null));
+
+ return matchRangeNode;
+ }
+
+ private ObjectNode parsePiMatchValid(PiValidFieldMatch validFieldMatch) {
+
+ ObjectNode matchValidNode = context.mapper().createObjectNode();
+ matchValidNode.put(CriterionCodec.PI_MATCH_FIELD_ID, validFieldMatch.fieldId().id());
+ matchValidNode.put(CriterionCodec.PI_MATCH_TYPE, PiMatchType.VALID.name().toLowerCase());
+ matchValidNode.put(CriterionCodec.PI_MATCH_VALUE, validFieldMatch.isValid());
+
+ return matchValidNode;
+ }
+
+ private class FormatProtocolIndependent implements CriterionTypeFormatter {
+ @Override
+ public ObjectNode encodeCriterion(ObjectNode root, Criterion criterion) {
+ final PiCriterion piCriterion = (PiCriterion) criterion;
+ ArrayNode matchNodes = context.mapper().createArrayNode();
+ for (PiFieldMatch fieldMatch : piCriterion.fieldMatches()) {
+ switch (fieldMatch.type()) {
+ case EXACT:
+ matchNodes.add(parsePiMatchExact((PiExactFieldMatch) fieldMatch));
+ break;
+ case LPM:
+ matchNodes.add(parsePiMatchLpm((PiLpmFieldMatch) fieldMatch));
+ break;
+ case TERNARY:
+ matchNodes.add(parsePiMatchTernary((PiTernaryFieldMatch) fieldMatch));
+ break;
+ case RANGE:
+
+ matchNodes.add(parsePiMatchRange((PiRangeFieldMatch) fieldMatch));
+ break;
+ case VALID:
+
+ matchNodes.add(parsePiMatchValid((PiValidFieldMatch) fieldMatch));
+ break;
+ default:
+ throw new IllegalArgumentException("Type " + fieldMatch.type().name() + " is unsupported");
+ }
+ }
+ return (ObjectNode) root.set(CriterionCodec.PI_MATCHES, matchNodes);
+ }
+ }
+
private class FormatDummyType implements CriterionTypeFormatter {
@Override
diff --git a/core/common/src/main/java/org/onosproject/codec/impl/EncodeInstructionCodecHelper.java b/core/common/src/main/java/org/onosproject/codec/impl/EncodeInstructionCodecHelper.java
index 65a6665..7e2f2df 100644
--- a/core/common/src/main/java/org/onosproject/codec/impl/EncodeInstructionCodecHelper.java
+++ b/core/common/src/main/java/org/onosproject/codec/impl/EncodeInstructionCodecHelper.java
@@ -33,6 +33,11 @@
import org.onosproject.net.flow.instructions.L2ModificationInstruction;
import org.onosproject.net.flow.instructions.L3ModificationInstruction;
import org.onosproject.net.flow.instructions.L4ModificationInstruction;
+import org.onosproject.net.flow.instructions.PiInstruction;
+import org.onosproject.net.pi.runtime.PiAction;
+import org.onosproject.net.pi.runtime.PiActionGroupId;
+import org.onosproject.net.pi.runtime.PiActionGroupMemberId;
+import org.onosproject.net.pi.runtime.PiActionParam;
import org.slf4j.Logger;
import static org.onlab.util.Tools.toHexWithPrefix;
@@ -240,6 +245,38 @@
}
}
+ /**
+ * Encode a protocol-independent instruction.
+ *
+ * @param result json node that the instruction attributes are added to
+ */
+ private void encodePi(ObjectNode result) {
+ PiInstruction piInstruction = (PiInstruction) instruction;
+ result.put(InstructionCodec.SUBTYPE, piInstruction.action().type().name());
+ switch (piInstruction.action().type()) {
+ case ACTION:
+ final PiAction piAction = (PiAction) piInstruction.action();
+ result.put(InstructionCodec.PI_ACTION_ID, piAction.id().id());
+ final ObjectNode jsonActionParams = context.mapper().createObjectNode();
+ for (PiActionParam actionParam : piAction.parameters()) {
+ jsonActionParams.put(actionParam.id().id(),
+ HexString.toHexString(actionParam.value().asArray(), null));
+ }
+ result.set(InstructionCodec.PI_ACTION_PARAMS, jsonActionParams);
+ break;
+ case ACTION_GROUP_ID:
+ final PiActionGroupId piActionGroupId = (PiActionGroupId) piInstruction.action();
+ result.put(InstructionCodec.PI_ACTION_GROUP_ID, piActionGroupId.id());
+ break;
+ case GROUP_MEMBER_ID:
+ final PiActionGroupMemberId piActionGroupMemberId = (PiActionGroupMemberId) piInstruction.action();
+ result.put(InstructionCodec.PI_ACTION_GROUP_MEMBER_ID, piActionGroupMemberId.id());
+ break;
+ default:
+ throw new IllegalArgumentException("Cannot convert protocol-independent subtype of" +
+ piInstruction.action().type().name());
+ }
+ }
/**
* Encodes a extension instruction.
@@ -336,6 +373,10 @@
encodeL4(result);
break;
+ case PROTOCOL_INDEPENDENT:
+ encodePi(result);
+ break;
+
case EXTENSION:
encodeExtension(result);
break;
diff --git a/core/common/src/main/java/org/onosproject/codec/impl/FlowEntryCodec.java b/core/common/src/main/java/org/onosproject/codec/impl/FlowEntryCodec.java
index 4e4fe2b..f0a53e1 100644
--- a/core/common/src/main/java/org/onosproject/codec/impl/FlowEntryCodec.java
+++ b/core/common/src/main/java/org/onosproject/codec/impl/FlowEntryCodec.java
@@ -43,7 +43,7 @@
final ObjectNode result = context.mapper().createObjectNode()
.put("id", Long.toString(flowEntry.id().value()))
- .put("tableId", flowEntry.tableId())
+ .put("tableId", flowEntry.table().toString())
.put("appId", strAppId)
.put("groupId", flowEntry.groupId().id())
.put("priority", flowEntry.priority())
diff --git a/core/common/src/main/java/org/onosproject/codec/impl/InstructionCodec.java b/core/common/src/main/java/org/onosproject/codec/impl/InstructionCodec.java
index 6e36ec0..75192a0 100644
--- a/core/common/src/main/java/org/onosproject/codec/impl/InstructionCodec.java
+++ b/core/common/src/main/java/org/onosproject/codec/impl/InstructionCodec.java
@@ -65,6 +65,11 @@
static final String STAT_PACKET_COUNT = "packetCount";
static final String STAT_DURATION = "duration";
+ static final String PI_ACTION_ID = "actionId";
+ static final String PI_ACTION_GROUP_ID = "groupId";
+ static final String PI_ACTION_GROUP_MEMBER_ID = "memberId";
+ static final String PI_ACTION_PARAMS = "actionParams";
+
static final String MISSING_MEMBER_MESSAGE =
" member is required in Instruction";
static final String ERROR_MESSAGE =