blob: e3efae2afead802a2e8aaffe7792ea98bb70c9fb [file] [log] [blame]
Ray Milkeyd43fe452015-05-29 09:35:12 -07001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2015-present Open Networking Foundation
Ray Milkeyd43fe452015-05-29 09:35:12 -07003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package org.onosproject.codec.impl;
17
Jian Lidab72562016-04-12 14:10:32 -070018import com.fasterxml.jackson.databind.JsonNode;
Jian Li1ef82db2016-03-03 14:43:21 -080019import com.fasterxml.jackson.databind.node.ObjectNode;
Cem Türker3baff672017-10-12 15:09:01 +030020import com.google.common.collect.Maps;
Jian Lidab72562016-04-12 14:10:32 -070021import org.onlab.osgi.DefaultServiceDirectory;
22import org.onlab.osgi.ServiceDirectory;
Konstantinos Kanonakis9215ff22016-11-04 13:28:11 -050023import org.onlab.packet.EthType;
Ray Milkeyd43fe452015-05-29 09:35:12 -070024import org.onlab.packet.IpAddress;
25import org.onlab.packet.MacAddress;
26import org.onlab.packet.MplsLabel;
Hyunsun Moonfab29502015-08-25 13:39:16 -070027import org.onlab.packet.TpPort;
Ray Milkeyd43fe452015-05-29 09:35:12 -070028import org.onlab.packet.VlanId;
Yafit Hadar5796d972015-10-15 13:16:11 +030029import org.onlab.util.HexString;
Frank Wang74ce2c12018-04-11 20:26:45 +080030import org.onlab.util.ImmutableByteSequence;
Jonathan Harte3bcfc32016-08-16 17:12:49 -070031import org.onosproject.codec.CodecContext;
Jian Lice8c5602016-03-03 21:43:24 -080032import org.onosproject.core.GroupId;
Ray Milkeyd43fe452015-05-29 09:35:12 -070033import org.onosproject.net.ChannelSpacing;
Jian Lidab72562016-04-12 14:10:32 -070034import org.onosproject.net.Device;
35import org.onosproject.net.DeviceId;
Ray Milkeyd43fe452015-05-29 09:35:12 -070036import org.onosproject.net.GridType;
Ray Milkeyd43fe452015-05-29 09:35:12 -070037import org.onosproject.net.OchSignal;
Yafit Hadar5796d972015-10-15 13:16:11 +030038import org.onosproject.net.OduSignalId;
Ray Milkeyd43fe452015-05-29 09:35:12 -070039import org.onosproject.net.PortNumber;
Jian Lidab72562016-04-12 14:10:32 -070040import org.onosproject.net.device.DeviceService;
Carmelo Casconecb4327a2018-09-11 15:17:23 -070041import org.onosproject.net.flow.ExtensionTreatmentCodec;
Cem Türker3baff672017-10-12 15:09:01 +030042import org.onosproject.net.flow.StatTriggerField;
43import org.onosproject.net.flow.StatTriggerFlag;
Jian Lidab72562016-04-12 14:10:32 -070044import org.onosproject.net.flow.instructions.ExtensionTreatment;
Ray Milkeyd43fe452015-05-29 09:35:12 -070045import org.onosproject.net.flow.instructions.Instruction;
46import org.onosproject.net.flow.instructions.Instructions;
47import org.onosproject.net.flow.instructions.L0ModificationInstruction;
Yafit Hadar5796d972015-10-15 13:16:11 +030048import org.onosproject.net.flow.instructions.L1ModificationInstruction;
Ray Milkeyd43fe452015-05-29 09:35:12 -070049import org.onosproject.net.flow.instructions.L2ModificationInstruction;
50import org.onosproject.net.flow.instructions.L3ModificationInstruction;
Hyunsun Moonfab29502015-08-25 13:39:16 -070051import org.onosproject.net.flow.instructions.L4ModificationInstruction;
Jian Li47b26232016-03-07 09:59:59 -080052import org.onosproject.net.meter.MeterId;
Frank Wang74ce2c12018-04-11 20:26:45 +080053import org.onosproject.net.pi.model.PiActionId;
54import org.onosproject.net.pi.model.PiActionParamId;
55import org.onosproject.net.pi.runtime.PiAction;
Frank Wang74ce2c12018-04-11 20:26:45 +080056import org.onosproject.net.pi.runtime.PiActionParam;
Carmelo Casconecb4327a2018-09-11 15:17:23 -070057import org.onosproject.net.pi.runtime.PiActionProfileGroupId;
58import org.onosproject.net.pi.runtime.PiActionProfileMemberId;
Frank Wang74ce2c12018-04-11 20:26:45 +080059import org.onosproject.net.pi.runtime.PiTableAction;
Jian Lidab72562016-04-12 14:10:32 -070060import org.slf4j.Logger;
Ray Milkeyd43fe452015-05-29 09:35:12 -070061
Cem Türker3baff672017-10-12 15:09:01 +030062import java.util.Map;
Konstantinos Kanonakis9215ff22016-11-04 13:28:11 -050063import java.util.regex.Matcher;
64import java.util.regex.Pattern;
65
Jian Li1ef82db2016-03-03 14:43:21 -080066import static org.onlab.util.Tools.nullIsIllegal;
Cem Türker3baff672017-10-12 15:09:01 +030067import static org.onosproject.codec.impl.InstructionCodec.STAT_PACKET_COUNT;
Jian Lidab72562016-04-12 14:10:32 -070068import static org.slf4j.LoggerFactory.getLogger;
Ray Milkeyd43fe452015-05-29 09:35:12 -070069
70/**
71 * Decoding portion of the instruction codec.
72 */
Ray Milkey6d7968e2015-07-06 14:30:02 -070073public final class DecodeInstructionCodecHelper {
Ray Milkey9c9cde42018-01-12 14:22:06 -080074 private static final Logger log = getLogger(DecodeInstructionCodecHelper.class);
Ray Milkeyd43fe452015-05-29 09:35:12 -070075 private final ObjectNode json;
Jonathan Harte3bcfc32016-08-16 17:12:49 -070076 private final CodecContext context;
Konstantinos Kanonakis9215ff22016-11-04 13:28:11 -050077 private static final Pattern ETHTYPE_PATTERN = Pattern.compile("0x([0-9a-fA-F]{4})");
Ray Milkeyd43fe452015-05-29 09:35:12 -070078
79 /**
80 * Creates a decode instruction codec object.
81 *
82 * @param json JSON object to decode
Ray Milkeyef794342016-11-09 16:20:29 -080083 * @param context codec context
Ray Milkeyd43fe452015-05-29 09:35:12 -070084 */
Jonathan Harte3bcfc32016-08-16 17:12:49 -070085 public DecodeInstructionCodecHelper(ObjectNode json, CodecContext context) {
Ray Milkeyd43fe452015-05-29 09:35:12 -070086 this.json = json;
Jonathan Harte3bcfc32016-08-16 17:12:49 -070087 this.context = context;
Ray Milkeyd43fe452015-05-29 09:35:12 -070088 }
89
90 /**
91 * Decodes a Layer 2 instruction.
92 *
93 * @return instruction object decoded from the JSON
94 * @throws IllegalArgumentException if the JSON is invalid
95 */
96 private Instruction decodeL2() {
Jayasree Ghosh8aca6772016-10-04 03:32:11 +053097 String subType = nullIsIllegal(json.get(InstructionCodec.SUBTYPE),
98 InstructionCodec.SUBTYPE + InstructionCodec.ERROR_MESSAGE).asText();
Ray Milkeyd43fe452015-05-29 09:35:12 -070099
100 if (subType.equals(L2ModificationInstruction.L2SubType.ETH_SRC.name())) {
101 String mac = nullIsIllegal(json.get(InstructionCodec.MAC),
102 InstructionCodec.MAC + InstructionCodec.MISSING_MEMBER_MESSAGE).asText();
103 return Instructions.modL2Src(MacAddress.valueOf(mac));
104 } else if (subType.equals(L2ModificationInstruction.L2SubType.ETH_DST.name())) {
105 String mac = nullIsIllegal(json.get(InstructionCodec.MAC),
106 InstructionCodec.MAC + InstructionCodec.MISSING_MEMBER_MESSAGE).asText();
107 return Instructions.modL2Dst(MacAddress.valueOf(mac));
108 } else if (subType.equals(L2ModificationInstruction.L2SubType.VLAN_ID.name())) {
109 short vlanId = (short) nullIsIllegal(json.get(InstructionCodec.VLAN_ID),
110 InstructionCodec.VLAN_ID + InstructionCodec.MISSING_MEMBER_MESSAGE).asInt();
111 return Instructions.modVlanId(VlanId.vlanId(vlanId));
112 } else if (subType.equals(L2ModificationInstruction.L2SubType.VLAN_PCP.name())) {
113 byte vlanPcp = (byte) nullIsIllegal(json.get(InstructionCodec.VLAN_PCP),
114 InstructionCodec.VLAN_PCP + InstructionCodec.MISSING_MEMBER_MESSAGE).asInt();
115 return Instructions.modVlanPcp(vlanPcp);
116 } else if (subType.equals(L2ModificationInstruction.L2SubType.MPLS_LABEL.name())) {
117 int label = nullIsIllegal(json.get(InstructionCodec.MPLS_LABEL),
118 InstructionCodec.MPLS_LABEL + InstructionCodec.MISSING_MEMBER_MESSAGE).asInt();
119 return Instructions.modMplsLabel(MplsLabel.mplsLabel(label));
120 } else if (subType.equals(L2ModificationInstruction.L2SubType.MPLS_PUSH.name())) {
121 return Instructions.pushMpls();
122 } else if (subType.equals(L2ModificationInstruction.L2SubType.MPLS_POP.name())) {
Sivachidambaram Subramaniandab7f4b2017-05-15 12:28:12 +0530123 if (json.has(InstructionCodec.ETHERNET_TYPE)) {
124 return Instructions.popMpls(getEthType());
125 }
Ray Milkeyd43fe452015-05-29 09:35:12 -0700126 return Instructions.popMpls();
127 } else if (subType.equals(L2ModificationInstruction.L2SubType.DEC_MPLS_TTL.name())) {
128 return Instructions.decMplsTtl();
129 } else if (subType.equals(L2ModificationInstruction.L2SubType.VLAN_POP.name())) {
130 return Instructions.popVlan();
131 } else if (subType.equals(L2ModificationInstruction.L2SubType.VLAN_PUSH.name())) {
Konstantinos Kanonakis9215ff22016-11-04 13:28:11 -0500132 if (json.has(InstructionCodec.ETHERNET_TYPE)) {
133 return Instructions.pushVlan(getEthType());
134 }
Ray Milkeyd43fe452015-05-29 09:35:12 -0700135 return Instructions.pushVlan();
Hyunsun Moon7080a0d2015-08-14 19:18:48 -0700136 } else if (subType.equals(L2ModificationInstruction.L2SubType.TUNNEL_ID.name())) {
137 long tunnelId = nullIsIllegal(json.get(InstructionCodec.TUNNEL_ID),
138 InstructionCodec.TUNNEL_ID + InstructionCodec.MISSING_MEMBER_MESSAGE).asLong();
139 return Instructions.modTunnelId(tunnelId);
Seyeon Jeong8d3cad22020-02-28 01:17:34 -0800140 } else if (subType.equals(L2ModificationInstruction.L2SubType.MPLS_BOS.name())) {
141 return Instructions.modMplsBos(json.get("bos").asBoolean());
Ray Milkeyd43fe452015-05-29 09:35:12 -0700142 }
Seyeon Jeong8d3cad22020-02-28 01:17:34 -0800143
Ray Milkeyd43fe452015-05-29 09:35:12 -0700144 throw new IllegalArgumentException("L2 Instruction subtype "
145 + subType + " is not supported");
146 }
147
148 /**
149 * Decodes a Layer 3 instruction.
150 *
151 * @return instruction object decoded from the JSON
152 * @throws IllegalArgumentException if the JSON is invalid
153 */
154 private Instruction decodeL3() {
Jayasree Ghosh8aca6772016-10-04 03:32:11 +0530155 String subType = nullIsIllegal(json.get(InstructionCodec.SUBTYPE),
156 InstructionCodec.SUBTYPE + InstructionCodec.ERROR_MESSAGE).asText();
Ray Milkeyd43fe452015-05-29 09:35:12 -0700157
158 if (subType.equals(L3ModificationInstruction.L3SubType.IPV4_SRC.name())) {
159 IpAddress ip = IpAddress.valueOf(nullIsIllegal(json.get(InstructionCodec.IP),
160 InstructionCodec.IP + InstructionCodec.MISSING_MEMBER_MESSAGE).asText());
161 return Instructions.modL3Src(ip);
162 } else if (subType.equals(L3ModificationInstruction.L3SubType.IPV4_DST.name())) {
163 IpAddress ip = IpAddress.valueOf(nullIsIllegal(json.get(InstructionCodec.IP),
164 InstructionCodec.IP + InstructionCodec.MISSING_MEMBER_MESSAGE).asText());
165 return Instructions.modL3Dst(ip);
166 } else if (subType.equals(L3ModificationInstruction.L3SubType.IPV6_SRC.name())) {
167 IpAddress ip = IpAddress.valueOf(nullIsIllegal(json.get(InstructionCodec.IP),
168 InstructionCodec.IP + InstructionCodec.MISSING_MEMBER_MESSAGE).asText());
169 return Instructions.modL3IPv6Src(ip);
170 } else if (subType.equals(L3ModificationInstruction.L3SubType.IPV6_DST.name())) {
171 IpAddress ip = IpAddress.valueOf(nullIsIllegal(json.get(InstructionCodec.IP),
172 InstructionCodec.IP + InstructionCodec.MISSING_MEMBER_MESSAGE).asText());
173 return Instructions.modL3IPv6Dst(ip);
174 } else if (subType.equals(L3ModificationInstruction.L3SubType.IPV6_FLABEL.name())) {
175 int flowLabel = nullIsIllegal(json.get(InstructionCodec.FLOW_LABEL),
176 InstructionCodec.FLOW_LABEL + InstructionCodec.MISSING_MEMBER_MESSAGE).asInt();
177 return Instructions.modL3IPv6FlowLabel(flowLabel);
Jonathan Hartcc962d82016-08-09 16:52:22 -0700178 } else if (subType.equals(L3ModificationInstruction.L3SubType.TTL_IN.name())) {
179 return Instructions.copyTtlIn();
180 } else if (subType.equals(L3ModificationInstruction.L3SubType.TTL_OUT.name())) {
181 return Instructions.copyTtlOut();
182 } else if (subType.equals(L3ModificationInstruction.L3SubType.DEC_TTL.name())) {
183 return Instructions.decNwTtl();
Rory Savagedaeb9492020-02-18 14:35:50 -0500184 } else if (subType.equals(L3ModificationInstruction.L3SubType.IP_DSCP.name())) {
185 int ipDscp = nullIsIllegal(json.get(InstructionCodec.IP_DSCP),
186 InstructionCodec.IP_DSCP + InstructionCodec.MISSING_MEMBER_MESSAGE).asInt();
187 if ((ipDscp < Byte.MIN_VALUE) || (ipDscp > Byte.MAX_VALUE)) {
188 throw new IllegalArgumentException("Value " + ipDscp + " must be single byte");
189 }
190 return Instructions.modIpDscp((byte) ipDscp);
Ray Milkeyd43fe452015-05-29 09:35:12 -0700191 }
192 throw new IllegalArgumentException("L3 Instruction subtype "
193 + subType + " is not supported");
194 }
195
196 /**
197 * Decodes a Layer 0 instruction.
198 *
199 * @return instruction object decoded from the JSON
200 * @throws IllegalArgumentException if the JSON is invalid
201 */
202 private Instruction decodeL0() {
Jayasree Ghosh8aca6772016-10-04 03:32:11 +0530203 String subType = nullIsIllegal(json.get(InstructionCodec.SUBTYPE),
204 InstructionCodec.SUBTYPE + InstructionCodec.ERROR_MESSAGE).asText();
Ray Milkeyd43fe452015-05-29 09:35:12 -0700205
Sho SHIMIZUcc137a92016-03-11 15:10:54 -0800206 if (subType.equals(L0ModificationInstruction.L0SubType.OCH.name())) {
Ray Milkeyd43fe452015-05-29 09:35:12 -0700207 String gridTypeString = nullIsIllegal(json.get(InstructionCodec.GRID_TYPE),
208 InstructionCodec.GRID_TYPE + InstructionCodec.MISSING_MEMBER_MESSAGE).asText();
209 GridType gridType = GridType.valueOf(gridTypeString);
210 if (gridType == null) {
211 throw new IllegalArgumentException("Unknown grid type "
212 + gridTypeString);
213 }
214 String channelSpacingString = nullIsIllegal(json.get(InstructionCodec.CHANNEL_SPACING),
215 InstructionCodec.CHANNEL_SPACING + InstructionCodec.MISSING_MEMBER_MESSAGE).asText();
216 ChannelSpacing channelSpacing = ChannelSpacing.valueOf(channelSpacingString);
217 if (channelSpacing == null) {
218 throw new IllegalArgumentException("Unknown channel spacing "
219 + channelSpacingString);
220 }
221 int spacingMultiplier = nullIsIllegal(json.get(InstructionCodec.SPACING_MULTIPLIER),
222 InstructionCodec.SPACING_MULTIPLIER + InstructionCodec.MISSING_MEMBER_MESSAGE).asInt();
223 int slotGranularity = nullIsIllegal(json.get(InstructionCodec.SLOT_GRANULARITY),
224 InstructionCodec.SLOT_GRANULARITY + InstructionCodec.MISSING_MEMBER_MESSAGE).asInt();
225 return Instructions.modL0Lambda(new OchSignal(gridType, channelSpacing,
226 spacingMultiplier, slotGranularity));
227 }
228 throw new IllegalArgumentException("L0 Instruction subtype "
229 + subType + " is not supported");
230 }
231
232 /**
Yafit Hadar5796d972015-10-15 13:16:11 +0300233 * Decodes a Layer 1 instruction.
234 *
235 * @return instruction object decoded from the JSON
236 * @throws IllegalArgumentException if the JSON is invalid
237 */
238 private Instruction decodeL1() {
Jayasree Ghosh8aca6772016-10-04 03:32:11 +0530239 String subType = nullIsIllegal(json.get(InstructionCodec.SUBTYPE),
240 InstructionCodec.SUBTYPE + InstructionCodec.ERROR_MESSAGE).asText();
Yafit Hadar5796d972015-10-15 13:16:11 +0300241 if (subType.equals(L1ModificationInstruction.L1SubType.ODU_SIGID.name())) {
242 int tributaryPortNumber = nullIsIllegal(json.get(InstructionCodec.TRIBUTARY_PORT_NUMBER),
243 InstructionCodec.TRIBUTARY_PORT_NUMBER + InstructionCodec.MISSING_MEMBER_MESSAGE).asInt();
244 int tributarySlotLen = nullIsIllegal(json.get(InstructionCodec.TRIBUTARY_SLOT_LEN),
245 InstructionCodec.TRIBUTARY_SLOT_LEN + InstructionCodec.MISSING_MEMBER_MESSAGE).asInt();
246 byte[] tributarySlotBitmap = null;
247 tributarySlotBitmap = HexString.fromHexString(
248 nullIsIllegal(json.get(InstructionCodec.TRIBUTARY_SLOT_BITMAP),
249 InstructionCodec.TRIBUTARY_SLOT_BITMAP + InstructionCodec.MISSING_MEMBER_MESSAGE).asText());
250 return Instructions.modL1OduSignalId(OduSignalId.oduSignalId(tributaryPortNumber, tributarySlotLen,
251 tributarySlotBitmap));
252 }
253 throw new IllegalArgumentException("L1 Instruction subtype "
254 + subType + " is not supported");
255 }
256
257 /**
Hyunsun Moonfab29502015-08-25 13:39:16 -0700258 * Decodes a Layer 4 instruction.
259 *
260 * @return instruction object decoded from the JSON
261 * @throws IllegalArgumentException if the JSON is invalid
262 */
263 private Instruction decodeL4() {
Jayasree Ghosh8aca6772016-10-04 03:32:11 +0530264 String subType = nullIsIllegal(json.get(InstructionCodec.SUBTYPE),
265 InstructionCodec.SUBTYPE + InstructionCodec.ERROR_MESSAGE).asText();
Hyunsun Moonfab29502015-08-25 13:39:16 -0700266
267 if (subType.equals(L4ModificationInstruction.L4SubType.TCP_DST.name())) {
268 TpPort tcpPort = TpPort.tpPort(nullIsIllegal(json.get(InstructionCodec.TCP_PORT),
269 InstructionCodec.TCP_PORT + InstructionCodec.MISSING_MEMBER_MESSAGE).asInt());
270 return Instructions.modTcpDst(tcpPort);
271 } else if (subType.equals(L4ModificationInstruction.L4SubType.TCP_SRC.name())) {
272 TpPort tcpPort = TpPort.tpPort(nullIsIllegal(json.get(InstructionCodec.TCP_PORT),
273 InstructionCodec.TCP_PORT + InstructionCodec.MISSING_MEMBER_MESSAGE).asInt());
274 return Instructions.modTcpSrc(tcpPort);
275 } else if (subType.equals(L4ModificationInstruction.L4SubType.UDP_DST.name())) {
276 TpPort udpPort = TpPort.tpPort(nullIsIllegal(json.get(InstructionCodec.UDP_PORT),
277 InstructionCodec.UDP_PORT + InstructionCodec.MISSING_MEMBER_MESSAGE).asInt());
278 return Instructions.modUdpDst(udpPort);
279 } else if (subType.equals(L4ModificationInstruction.L4SubType.UDP_SRC.name())) {
280 TpPort udpPort = TpPort.tpPort(nullIsIllegal(json.get(InstructionCodec.UDP_PORT),
281 InstructionCodec.UDP_PORT + InstructionCodec.MISSING_MEMBER_MESSAGE).asInt());
282 return Instructions.modUdpSrc(udpPort);
283 }
284 throw new IllegalArgumentException("L4 Instruction subtype "
285 + subType + " is not supported");
286 }
287
Frank Wang74ce2c12018-04-11 20:26:45 +0800288 /**
289 * Decodes a protocol-independent instruction.
290 *
291 * @return instruction object decoded from the JSON
292 * @throws IllegalArgumentException if the JSON is invalid
293 */
294 private Instruction decodePi() {
295 String subType = nullIsIllegal(json.get(InstructionCodec.SUBTYPE),
296 InstructionCodec.SUBTYPE + InstructionCodec.ERROR_MESSAGE).asText();
297
298 if (subType.equals(PiTableAction.Type.ACTION.name())) {
299 PiActionId piActionId = PiActionId.of(nullIsIllegal(
300 json.get(InstructionCodec.PI_ACTION_ID),
301 InstructionCodec.PI_ACTION_ID + InstructionCodec.MISSING_MEMBER_MESSAGE).asText());
302 JsonNode params = json.get(InstructionCodec.PI_ACTION_PARAMS);
303
304 PiAction.Builder builder = PiAction.builder();
305 PiActionParam piActionParam;
306 PiActionParamId piActionParamId;
307 if (params != null) {
308 for (Map.Entry<String, String> param : ((Map<String, String>)
309 (context.mapper().convertValue(params, Map.class))).entrySet()) {
310 piActionParamId = PiActionParamId.of(param.getKey());
311 piActionParam = new PiActionParam(piActionParamId,
312 ImmutableByteSequence.copyFrom(
313 HexString.fromHexString(param.getValue(), null)));
314 builder.withParameter(piActionParam);
315 }
316 }
317
318 return Instructions.piTableAction(builder.withId(piActionId).build());
Carmelo Casconecb4327a2018-09-11 15:17:23 -0700319 } else if (subType.equals(PiTableAction.Type.ACTION_PROFILE_GROUP_ID.name())) {
320 PiActionProfileGroupId piActionGroupId = PiActionProfileGroupId.of(nullIsIllegal(
321 json.get(InstructionCodec.PI_ACTION_PROFILE_GROUP_ID),
322 InstructionCodec.PI_ACTION_PROFILE_GROUP_ID + InstructionCodec.MISSING_MEMBER_MESSAGE).asInt());
Frank Wang74ce2c12018-04-11 20:26:45 +0800323
324 return Instructions.piTableAction(piActionGroupId);
Carmelo Casconecb4327a2018-09-11 15:17:23 -0700325 } else if (subType.equals(PiTableAction.Type.ACTION_PROFILE_MEMBER_ID.name())) {
326 PiActionProfileMemberId piActionProfileMemberId = PiActionProfileMemberId.of(nullIsIllegal(
327 json.get(InstructionCodec.PI_ACTION_PROFILE_MEMBER_ID),
328 InstructionCodec.PI_ACTION_PROFILE_MEMBER_ID + InstructionCodec.MISSING_MEMBER_MESSAGE).asInt());
Frank Wang74ce2c12018-04-11 20:26:45 +0800329
Carmelo Casconecb4327a2018-09-11 15:17:23 -0700330 return Instructions.piTableAction(piActionProfileMemberId);
Frank Wang74ce2c12018-04-11 20:26:45 +0800331 }
Daniele Morod900fe42021-02-11 16:12:57 +0100332 // TODO: implement JSON decoder for ACTION_SET
Frank Wang74ce2c12018-04-11 20:26:45 +0800333 throw new IllegalArgumentException("Protocol-independent Instruction subtype "
334 + subType + " is not supported");
335 }
336
Cem Türker3baff672017-10-12 15:09:01 +0300337 private Instruction decodeStatTrigger() {
338 String statTriggerFlag = nullIsIllegal(json.get(InstructionCodec.STAT_TRIGGER_FLAG),
339 InstructionCodec.STAT_TRIGGER_FLAG + InstructionCodec.ERROR_MESSAGE).asText();
340
341 StatTriggerFlag flag = null;
342
343 if (statTriggerFlag.equals(StatTriggerFlag.ONLY_FIRST.name())) {
344 flag = StatTriggerFlag.ONLY_FIRST;
345 } else if (statTriggerFlag.equals(StatTriggerFlag.PERIODIC.name())) {
346 flag = StatTriggerFlag.PERIODIC;
347 } else {
348 throw new IllegalArgumentException("statTriggerFlag "
349 + statTriggerFlag + " is not supported");
350 }
351 if (!json.has(InstructionCodec.STAT_THRESHOLDS)) {
352 throw new IllegalArgumentException("statThreshold is not added");
353 }
354 JsonNode statThresholdsNode = nullIsIllegal(json.get(InstructionCodec.STAT_THRESHOLDS),
355 InstructionCodec.STAT_THRESHOLDS + InstructionCodec.ERROR_MESSAGE);
356 Map<StatTriggerField, Long> statThresholdMap = getStatThreshold(statThresholdsNode);
357 if (statThresholdMap.isEmpty()) {
358 throw new IllegalArgumentException("statThreshold must have at least one property");
359 }
360 return Instructions.statTrigger(statThresholdMap, flag);
361 }
362
363 private Map<StatTriggerField, Long> getStatThreshold(JsonNode statThresholdNode) {
364 Map<StatTriggerField, Long> statThresholdMap = Maps.newEnumMap(StatTriggerField.class);
365 for (JsonNode jsonNode : statThresholdNode) {
366 if (jsonNode.hasNonNull(InstructionCodec.STAT_BYTE_COUNT)) {
367 JsonNode byteCountNode = jsonNode.get(InstructionCodec.STAT_BYTE_COUNT);
368 if (!byteCountNode.isNull() && byteCountNode.isNumber()) {
369 statThresholdMap.put(StatTriggerField.BYTE_COUNT, byteCountNode.asLong());
370 }
371 } else if (jsonNode.hasNonNull(STAT_PACKET_COUNT)) {
372 JsonNode packetCount = jsonNode.get(STAT_PACKET_COUNT);
373 if (!packetCount.isNull() && packetCount.isNumber()) {
374 statThresholdMap.put(StatTriggerField.PACKET_COUNT, packetCount.asLong());
375 }
376 } else if (jsonNode.hasNonNull(InstructionCodec.STAT_DURATION)) {
377 JsonNode duration = jsonNode.get(InstructionCodec.STAT_DURATION);
378 if (!duration.isNull() && duration.isNumber()) {
379 statThresholdMap.put(StatTriggerField.DURATION, duration.asLong());
380 }
381 } else {
382 log.error("Unsupported stat {}", jsonNode.toString());
383 }
384 }
385
386 return statThresholdMap;
387 }
388
Hyunsun Moonfab29502015-08-25 13:39:16 -0700389 /**
Jian Lidab72562016-04-12 14:10:32 -0700390 * Decodes a extension instruction.
391 *
392 * @return extension treatment
393 */
394 private Instruction decodeExtension() {
395 ObjectNode node = (ObjectNode) json.get(InstructionCodec.EXTENSION);
396 if (node != null) {
397 DeviceId deviceId = getDeviceId();
398
399 ServiceDirectory serviceDirectory = new DefaultServiceDirectory();
400 DeviceService deviceService = serviceDirectory.get(DeviceService.class);
401 Device device = deviceService.getDevice(deviceId);
402
Jonathan Harte3bcfc32016-08-16 17:12:49 -0700403 if (device == null) {
404 throw new IllegalArgumentException("Device not found");
405 }
406
Jian Lidab72562016-04-12 14:10:32 -0700407 if (device.is(ExtensionTreatmentCodec.class)) {
408 ExtensionTreatmentCodec treatmentCodec = device.as(ExtensionTreatmentCodec.class);
Jonathan Harte3bcfc32016-08-16 17:12:49 -0700409 ExtensionTreatment treatment = treatmentCodec.decode(node, context);
Jian Lidab72562016-04-12 14:10:32 -0700410 return Instructions.extension(treatment, deviceId);
411 } else {
Jonathan Harte3bcfc32016-08-16 17:12:49 -0700412 throw new IllegalArgumentException(
413 "There is no codec to decode extension for device " + deviceId.toString());
Jian Lidab72562016-04-12 14:10:32 -0700414 }
415 }
416 return null;
417 }
418
419 /**
420 * Returns device identifier.
421 *
422 * @return device identifier
423 * @throws IllegalArgumentException if the JSON is invalid
424 */
425 private DeviceId getDeviceId() {
426 JsonNode deviceIdNode = json.get(InstructionCodec.DEVICE_ID);
427 if (deviceIdNode != null) {
428 return DeviceId.deviceId(deviceIdNode.asText());
429 }
430 throw new IllegalArgumentException("Empty device identifier");
431 }
432
433 /**
Jian Li70dffe42016-03-08 22:23:02 -0800434 * Extracts port number of the given json node.
435 *
436 * @param jsonNode json node
437 * @return port number
438 */
439 private PortNumber getPortNumber(ObjectNode jsonNode) {
440 PortNumber portNumber;
Jayasree Ghosh8aca6772016-10-04 03:32:11 +0530441 JsonNode portNode = nullIsIllegal(jsonNode.get(InstructionCodec.PORT),
442 InstructionCodec.PORT + InstructionCodec.ERROR_MESSAGE);
443 if (portNode.isLong() || portNode.isInt()) {
444 portNumber = PortNumber.portNumber(portNode.asLong());
445 } else if (portNode.isTextual()) {
446 portNumber = PortNumber.fromString(portNode.textValue());
Jian Li70dffe42016-03-08 22:23:02 -0800447 } else {
448 throw new IllegalArgumentException("Port value "
Jayasree Ghosh8aca6772016-10-04 03:32:11 +0530449 + portNode.toString()
Jian Li70dffe42016-03-08 22:23:02 -0800450 + " is not supported");
451 }
452 return portNumber;
453 }
454
455 /**
Konstantinos Kanonakis9215ff22016-11-04 13:28:11 -0500456 * Returns Ethernet type.
457 *
458 * @return ethernet type
459 * @throws IllegalArgumentException if the JSON is invalid
460 */
461 private EthType getEthType() {
462 String ethTypeStr = nullIsIllegal(json.get(InstructionCodec.ETHERNET_TYPE),
463 InstructionCodec.ETHERNET_TYPE + InstructionCodec.MISSING_MEMBER_MESSAGE).asText();
464 Matcher matcher = ETHTYPE_PATTERN.matcher(ethTypeStr);
465 if (!matcher.matches()) {
466 throw new IllegalArgumentException("ETHERNET_TYPE must be a four digit hex string starting with 0x");
467 }
468 short ethernetType = (short) Integer.parseInt(matcher.group(1), 16);
469 return new EthType(ethernetType);
470 }
471
472 /**
Ray Milkeyd43fe452015-05-29 09:35:12 -0700473 * Decodes the JSON into an instruction object.
474 *
475 * @return Criterion object
476 * @throws IllegalArgumentException if the JSON is invalid
477 */
478 public Instruction decode() {
Jayasree Ghosh8aca6772016-10-04 03:32:11 +0530479 String type = nullIsIllegal(json.get(InstructionCodec.TYPE),
480 InstructionCodec.TYPE + InstructionCodec.ERROR_MESSAGE).asText();
Ray Milkeyd43fe452015-05-29 09:35:12 -0700481
482 if (type.equals(Instruction.Type.OUTPUT.name())) {
Jian Li70dffe42016-03-08 22:23:02 -0800483 return Instructions.createOutput(getPortNumber(json));
Ray Milkey2be39ed2016-02-22 15:54:19 -0800484 } else if (type.equals(Instruction.Type.NOACTION.name())) {
485 return Instructions.createNoAction();
Jian Li1ef82db2016-03-03 14:43:21 -0800486 } else if (type.equals(Instruction.Type.TABLE.name())) {
Jayasree Ghosh8aca6772016-10-04 03:32:11 +0530487 return Instructions.transition(nullIsIllegal(json.get(InstructionCodec.TABLE_ID),
488 InstructionCodec.TABLE_ID + InstructionCodec.MISSING_MEMBER_MESSAGE).asInt());
Jian Lice8c5602016-03-03 21:43:24 -0800489 } else if (type.equals(Instruction.Type.GROUP.name())) {
Seyeon Jeong8188da12020-03-03 12:49:48 -0800490 // a group id should be an unsigned integer
491 Long id = nullIsIllegal(json.get(InstructionCodec.GROUP_ID),
492 InstructionCodec.GROUP_ID + InstructionCodec.MISSING_MEMBER_MESSAGE).asLong();
493 GroupId groupId = new GroupId(id.intValue());
Jian Lice8c5602016-03-03 21:43:24 -0800494 return Instructions.createGroup(groupId);
Jian Li47b26232016-03-07 09:59:59 -0800495 } else if (type.equals(Instruction.Type.METER.name())) {
Jayasree Ghosh8aca6772016-10-04 03:32:11 +0530496 MeterId meterId = MeterId.meterId(nullIsIllegal(json.get(InstructionCodec.METER_ID),
497 InstructionCodec.METER_ID + InstructionCodec.MISSING_MEMBER_MESSAGE).asLong());
Jian Li47b26232016-03-07 09:59:59 -0800498 return Instructions.meterTraffic(meterId);
Jian Li70dffe42016-03-08 22:23:02 -0800499 } else if (type.equals(Instruction.Type.QUEUE.name())) {
Jayasree Ghosh8aca6772016-10-04 03:32:11 +0530500 long queueId = nullIsIllegal(json.get(InstructionCodec.QUEUE_ID),
501 InstructionCodec.QUEUE_ID + InstructionCodec.MISSING_MEMBER_MESSAGE).asLong();
ke han74702102016-11-29 14:57:29 +0800502 if (json.get(InstructionCodec.PORT) == null ||
503 json.get(InstructionCodec.PORT).isNull()) {
504 return Instructions.setQueue(queueId, null);
505 } else {
506 return Instructions.setQueue(queueId, getPortNumber(json));
507 }
Ray Milkeyd43fe452015-05-29 09:35:12 -0700508 } else if (type.equals(Instruction.Type.L0MODIFICATION.name())) {
509 return decodeL0();
Yafit Hadar5796d972015-10-15 13:16:11 +0300510 } else if (type.equals(Instruction.Type.L1MODIFICATION.name())) {
511 return decodeL1();
Ray Milkeyd43fe452015-05-29 09:35:12 -0700512 } else if (type.equals(Instruction.Type.L2MODIFICATION.name())) {
513 return decodeL2();
514 } else if (type.equals(Instruction.Type.L3MODIFICATION.name())) {
515 return decodeL3();
Hyunsun Moonfab29502015-08-25 13:39:16 -0700516 } else if (type.equals(Instruction.Type.L4MODIFICATION.name())) {
517 return decodeL4();
Jian Lidab72562016-04-12 14:10:32 -0700518 } else if (type.equals(Instruction.Type.EXTENSION.name())) {
519 return decodeExtension();
Cem Türker3baff672017-10-12 15:09:01 +0300520 } else if (type.equals(Instruction.Type.STAT_TRIGGER.name())) {
521 return decodeStatTrigger();
Frank Wang74ce2c12018-04-11 20:26:45 +0800522 } else if (type.equals(Instruction.Type.PROTOCOL_INDEPENDENT.name())) {
523 return decodePi();
Ray Milkeyd43fe452015-05-29 09:35:12 -0700524 }
Frank Wang74ce2c12018-04-11 20:26:45 +0800525
Ray Milkeyd43fe452015-05-29 09:35:12 -0700526 throw new IllegalArgumentException("Instruction type "
Frank Wang74ce2c12018-04-11 20:26:45 +0800527 + type + " is not supported");
Ray Milkeyd43fe452015-05-29 09:35:12 -0700528 }
Ray Milkeyd43fe452015-05-29 09:35:12 -0700529}