blob: a6268bb7e4a6b890c2a30bfff252a184b0afdf33 [file] [log] [blame]
/*
* Copyright 2015-present Open Networking Laboratory
*
* 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.codec.impl;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.onlab.osgi.DefaultServiceDirectory;
import org.onlab.osgi.ServiceDirectory;
import org.onlab.packet.EthType;
import org.onlab.packet.IpAddress;
import org.onlab.packet.MacAddress;
import org.onlab.packet.MplsLabel;
import org.onlab.packet.TpPort;
import org.onlab.packet.VlanId;
import org.onlab.util.HexString;
import org.onosproject.codec.CodecContext;
import org.onosproject.codec.ExtensionTreatmentCodec;
import org.onosproject.core.GroupId;
import org.onosproject.net.ChannelSpacing;
import org.onosproject.net.Device;
import org.onosproject.net.DeviceId;
import org.onosproject.net.GridType;
import org.onosproject.net.OchSignal;
import org.onosproject.net.OduSignalId;
import org.onosproject.net.PortNumber;
import org.onosproject.net.device.DeviceService;
import org.onosproject.net.flow.instructions.ExtensionTreatment;
import org.onosproject.net.flow.instructions.Instruction;
import org.onosproject.net.flow.instructions.Instructions;
import org.onosproject.net.flow.instructions.L0ModificationInstruction;
import org.onosproject.net.flow.instructions.L1ModificationInstruction;
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.meter.MeterId;
import org.slf4j.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static org.onlab.util.Tools.nullIsIllegal;
import static org.slf4j.LoggerFactory.getLogger;
/**
* Decoding portion of the instruction codec.
*/
public final class DecodeInstructionCodecHelper {
protected static final Logger log = getLogger(DecodeInstructionCodecHelper.class);
private final ObjectNode json;
private final CodecContext context;
private static final Pattern ETHTYPE_PATTERN = Pattern.compile("0x([0-9a-fA-F]{4})");
/**
* Creates a decode instruction codec object.
*
* @param json JSON object to decode
* @param context codec context
*/
public DecodeInstructionCodecHelper(ObjectNode json, CodecContext context) {
this.json = json;
this.context = context;
}
/**
* Decodes a Layer 2 instruction.
*
* @return instruction object decoded from the JSON
* @throws IllegalArgumentException if the JSON is invalid
*/
private Instruction decodeL2() {
String subType = nullIsIllegal(json.get(InstructionCodec.SUBTYPE),
InstructionCodec.SUBTYPE + InstructionCodec.ERROR_MESSAGE).asText();
if (subType.equals(L2ModificationInstruction.L2SubType.ETH_SRC.name())) {
String mac = nullIsIllegal(json.get(InstructionCodec.MAC),
InstructionCodec.MAC + InstructionCodec.MISSING_MEMBER_MESSAGE).asText();
return Instructions.modL2Src(MacAddress.valueOf(mac));
} else if (subType.equals(L2ModificationInstruction.L2SubType.ETH_DST.name())) {
String mac = nullIsIllegal(json.get(InstructionCodec.MAC),
InstructionCodec.MAC + InstructionCodec.MISSING_MEMBER_MESSAGE).asText();
return Instructions.modL2Dst(MacAddress.valueOf(mac));
} else if (subType.equals(L2ModificationInstruction.L2SubType.VLAN_ID.name())) {
short vlanId = (short) nullIsIllegal(json.get(InstructionCodec.VLAN_ID),
InstructionCodec.VLAN_ID + InstructionCodec.MISSING_MEMBER_MESSAGE).asInt();
return Instructions.modVlanId(VlanId.vlanId(vlanId));
} else if (subType.equals(L2ModificationInstruction.L2SubType.VLAN_PCP.name())) {
byte vlanPcp = (byte) nullIsIllegal(json.get(InstructionCodec.VLAN_PCP),
InstructionCodec.VLAN_PCP + InstructionCodec.MISSING_MEMBER_MESSAGE).asInt();
return Instructions.modVlanPcp(vlanPcp);
} else if (subType.equals(L2ModificationInstruction.L2SubType.MPLS_LABEL.name())) {
int label = nullIsIllegal(json.get(InstructionCodec.MPLS_LABEL),
InstructionCodec.MPLS_LABEL + InstructionCodec.MISSING_MEMBER_MESSAGE).asInt();
return Instructions.modMplsLabel(MplsLabel.mplsLabel(label));
} else if (subType.equals(L2ModificationInstruction.L2SubType.MPLS_PUSH.name())) {
return Instructions.pushMpls();
} else if (subType.equals(L2ModificationInstruction.L2SubType.MPLS_POP.name())) {
if (json.has(InstructionCodec.ETHERNET_TYPE)) {
return Instructions.popMpls(getEthType());
}
return Instructions.popMpls();
} else if (subType.equals(L2ModificationInstruction.L2SubType.DEC_MPLS_TTL.name())) {
return Instructions.decMplsTtl();
} else if (subType.equals(L2ModificationInstruction.L2SubType.VLAN_POP.name())) {
return Instructions.popVlan();
} else if (subType.equals(L2ModificationInstruction.L2SubType.VLAN_PUSH.name())) {
if (json.has(InstructionCodec.ETHERNET_TYPE)) {
return Instructions.pushVlan(getEthType());
}
return Instructions.pushVlan();
} else if (subType.equals(L2ModificationInstruction.L2SubType.TUNNEL_ID.name())) {
long tunnelId = nullIsIllegal(json.get(InstructionCodec.TUNNEL_ID),
InstructionCodec.TUNNEL_ID + InstructionCodec.MISSING_MEMBER_MESSAGE).asLong();
return Instructions.modTunnelId(tunnelId);
}
throw new IllegalArgumentException("L2 Instruction subtype "
+ subType + " is not supported");
}
/**
* Decodes a Layer 3 instruction.
*
* @return instruction object decoded from the JSON
* @throws IllegalArgumentException if the JSON is invalid
*/
private Instruction decodeL3() {
String subType = nullIsIllegal(json.get(InstructionCodec.SUBTYPE),
InstructionCodec.SUBTYPE + InstructionCodec.ERROR_MESSAGE).asText();
if (subType.equals(L3ModificationInstruction.L3SubType.IPV4_SRC.name())) {
IpAddress ip = IpAddress.valueOf(nullIsIllegal(json.get(InstructionCodec.IP),
InstructionCodec.IP + InstructionCodec.MISSING_MEMBER_MESSAGE).asText());
return Instructions.modL3Src(ip);
} else if (subType.equals(L3ModificationInstruction.L3SubType.IPV4_DST.name())) {
IpAddress ip = IpAddress.valueOf(nullIsIllegal(json.get(InstructionCodec.IP),
InstructionCodec.IP + InstructionCodec.MISSING_MEMBER_MESSAGE).asText());
return Instructions.modL3Dst(ip);
} else if (subType.equals(L3ModificationInstruction.L3SubType.IPV6_SRC.name())) {
IpAddress ip = IpAddress.valueOf(nullIsIllegal(json.get(InstructionCodec.IP),
InstructionCodec.IP + InstructionCodec.MISSING_MEMBER_MESSAGE).asText());
return Instructions.modL3IPv6Src(ip);
} else if (subType.equals(L3ModificationInstruction.L3SubType.IPV6_DST.name())) {
IpAddress ip = IpAddress.valueOf(nullIsIllegal(json.get(InstructionCodec.IP),
InstructionCodec.IP + InstructionCodec.MISSING_MEMBER_MESSAGE).asText());
return Instructions.modL3IPv6Dst(ip);
} else if (subType.equals(L3ModificationInstruction.L3SubType.IPV6_FLABEL.name())) {
int flowLabel = nullIsIllegal(json.get(InstructionCodec.FLOW_LABEL),
InstructionCodec.FLOW_LABEL + InstructionCodec.MISSING_MEMBER_MESSAGE).asInt();
return Instructions.modL3IPv6FlowLabel(flowLabel);
}
throw new IllegalArgumentException("L3 Instruction subtype "
+ subType + " is not supported");
}
/**
* Decodes a Layer 0 instruction.
*
* @return instruction object decoded from the JSON
* @throws IllegalArgumentException if the JSON is invalid
*/
private Instruction decodeL0() {
String subType = nullIsIllegal(json.get(InstructionCodec.SUBTYPE),
InstructionCodec.SUBTYPE + InstructionCodec.ERROR_MESSAGE).asText();
if (subType.equals(L0ModificationInstruction.L0SubType.OCH.name())) {
String gridTypeString = nullIsIllegal(json.get(InstructionCodec.GRID_TYPE),
InstructionCodec.GRID_TYPE + InstructionCodec.MISSING_MEMBER_MESSAGE).asText();
GridType gridType = GridType.valueOf(gridTypeString);
if (gridType == null) {
throw new IllegalArgumentException("Unknown grid type "
+ gridTypeString);
}
String channelSpacingString = nullIsIllegal(json.get(InstructionCodec.CHANNEL_SPACING),
InstructionCodec.CHANNEL_SPACING + InstructionCodec.MISSING_MEMBER_MESSAGE).asText();
ChannelSpacing channelSpacing = ChannelSpacing.valueOf(channelSpacingString);
if (channelSpacing == null) {
throw new IllegalArgumentException("Unknown channel spacing "
+ channelSpacingString);
}
int spacingMultiplier = nullIsIllegal(json.get(InstructionCodec.SPACING_MULTIPLIER),
InstructionCodec.SPACING_MULTIPLIER + InstructionCodec.MISSING_MEMBER_MESSAGE).asInt();
int slotGranularity = nullIsIllegal(json.get(InstructionCodec.SLOT_GRANULARITY),
InstructionCodec.SLOT_GRANULARITY + InstructionCodec.MISSING_MEMBER_MESSAGE).asInt();
return Instructions.modL0Lambda(new OchSignal(gridType, channelSpacing,
spacingMultiplier, slotGranularity));
}
throw new IllegalArgumentException("L0 Instruction subtype "
+ subType + " is not supported");
}
/**
* Decodes a Layer 1 instruction.
*
* @return instruction object decoded from the JSON
* @throws IllegalArgumentException if the JSON is invalid
*/
private Instruction decodeL1() {
String subType = nullIsIllegal(json.get(InstructionCodec.SUBTYPE),
InstructionCodec.SUBTYPE + InstructionCodec.ERROR_MESSAGE).asText();
if (subType.equals(L1ModificationInstruction.L1SubType.ODU_SIGID.name())) {
int tributaryPortNumber = nullIsIllegal(json.get(InstructionCodec.TRIBUTARY_PORT_NUMBER),
InstructionCodec.TRIBUTARY_PORT_NUMBER + InstructionCodec.MISSING_MEMBER_MESSAGE).asInt();
int tributarySlotLen = nullIsIllegal(json.get(InstructionCodec.TRIBUTARY_SLOT_LEN),
InstructionCodec.TRIBUTARY_SLOT_LEN + InstructionCodec.MISSING_MEMBER_MESSAGE).asInt();
byte[] tributarySlotBitmap = null;
tributarySlotBitmap = HexString.fromHexString(
nullIsIllegal(json.get(InstructionCodec.TRIBUTARY_SLOT_BITMAP),
InstructionCodec.TRIBUTARY_SLOT_BITMAP + InstructionCodec.MISSING_MEMBER_MESSAGE).asText());
return Instructions.modL1OduSignalId(OduSignalId.oduSignalId(tributaryPortNumber, tributarySlotLen,
tributarySlotBitmap));
}
throw new IllegalArgumentException("L1 Instruction subtype "
+ subType + " is not supported");
}
/**
* Decodes a Layer 4 instruction.
*
* @return instruction object decoded from the JSON
* @throws IllegalArgumentException if the JSON is invalid
*/
private Instruction decodeL4() {
String subType = nullIsIllegal(json.get(InstructionCodec.SUBTYPE),
InstructionCodec.SUBTYPE + InstructionCodec.ERROR_MESSAGE).asText();
if (subType.equals(L4ModificationInstruction.L4SubType.TCP_DST.name())) {
TpPort tcpPort = TpPort.tpPort(nullIsIllegal(json.get(InstructionCodec.TCP_PORT),
InstructionCodec.TCP_PORT + InstructionCodec.MISSING_MEMBER_MESSAGE).asInt());
return Instructions.modTcpDst(tcpPort);
} else if (subType.equals(L4ModificationInstruction.L4SubType.TCP_SRC.name())) {
TpPort tcpPort = TpPort.tpPort(nullIsIllegal(json.get(InstructionCodec.TCP_PORT),
InstructionCodec.TCP_PORT + InstructionCodec.MISSING_MEMBER_MESSAGE).asInt());
return Instructions.modTcpSrc(tcpPort);
} else if (subType.equals(L4ModificationInstruction.L4SubType.UDP_DST.name())) {
TpPort udpPort = TpPort.tpPort(nullIsIllegal(json.get(InstructionCodec.UDP_PORT),
InstructionCodec.UDP_PORT + InstructionCodec.MISSING_MEMBER_MESSAGE).asInt());
return Instructions.modUdpDst(udpPort);
} else if (subType.equals(L4ModificationInstruction.L4SubType.UDP_SRC.name())) {
TpPort udpPort = TpPort.tpPort(nullIsIllegal(json.get(InstructionCodec.UDP_PORT),
InstructionCodec.UDP_PORT + InstructionCodec.MISSING_MEMBER_MESSAGE).asInt());
return Instructions.modUdpSrc(udpPort);
}
throw new IllegalArgumentException("L4 Instruction subtype "
+ subType + " is not supported");
}
/**
* Decodes a extension instruction.
*
* @return extension treatment
*/
private Instruction decodeExtension() {
ObjectNode node = (ObjectNode) json.get(InstructionCodec.EXTENSION);
if (node != null) {
DeviceId deviceId = getDeviceId();
ServiceDirectory serviceDirectory = new DefaultServiceDirectory();
DeviceService deviceService = serviceDirectory.get(DeviceService.class);
Device device = deviceService.getDevice(deviceId);
if (device == null) {
throw new IllegalArgumentException("Device not found");
}
if (device.is(ExtensionTreatmentCodec.class)) {
ExtensionTreatmentCodec treatmentCodec = device.as(ExtensionTreatmentCodec.class);
ExtensionTreatment treatment = treatmentCodec.decode(node, context);
return Instructions.extension(treatment, deviceId);
} else {
throw new IllegalArgumentException(
"There is no codec to decode extension for device " + deviceId.toString());
}
}
return null;
}
/**
* Returns device identifier.
*
* @return device identifier
* @throws IllegalArgumentException if the JSON is invalid
*/
private DeviceId getDeviceId() {
JsonNode deviceIdNode = json.get(InstructionCodec.DEVICE_ID);
if (deviceIdNode != null) {
return DeviceId.deviceId(deviceIdNode.asText());
}
throw new IllegalArgumentException("Empty device identifier");
}
/**
* Extracts port number of the given json node.
*
* @param jsonNode json node
* @return port number
*/
private PortNumber getPortNumber(ObjectNode jsonNode) {
PortNumber portNumber;
JsonNode portNode = nullIsIllegal(jsonNode.get(InstructionCodec.PORT),
InstructionCodec.PORT + InstructionCodec.ERROR_MESSAGE);
if (portNode.isLong() || portNode.isInt()) {
portNumber = PortNumber.portNumber(portNode.asLong());
} else if (portNode.isTextual()) {
portNumber = PortNumber.fromString(portNode.textValue());
} else {
throw new IllegalArgumentException("Port value "
+ portNode.toString()
+ " is not supported");
}
return portNumber;
}
/**
* Returns Ethernet type.
*
* @return ethernet type
* @throws IllegalArgumentException if the JSON is invalid
*/
private EthType getEthType() {
String ethTypeStr = nullIsIllegal(json.get(InstructionCodec.ETHERNET_TYPE),
InstructionCodec.ETHERNET_TYPE + InstructionCodec.MISSING_MEMBER_MESSAGE).asText();
Matcher matcher = ETHTYPE_PATTERN.matcher(ethTypeStr);
if (!matcher.matches()) {
throw new IllegalArgumentException("ETHERNET_TYPE must be a four digit hex string starting with 0x");
}
short ethernetType = (short) Integer.parseInt(matcher.group(1), 16);
return new EthType(ethernetType);
}
/**
* Decodes the JSON into an instruction object.
*
* @return Criterion object
* @throws IllegalArgumentException if the JSON is invalid
*/
public Instruction decode() {
String type = nullIsIllegal(json.get(InstructionCodec.TYPE),
InstructionCodec.TYPE + InstructionCodec.ERROR_MESSAGE).asText();
if (type.equals(Instruction.Type.OUTPUT.name())) {
return Instructions.createOutput(getPortNumber(json));
} else if (type.equals(Instruction.Type.NOACTION.name())) {
return Instructions.createNoAction();
} else if (type.equals(Instruction.Type.TABLE.name())) {
return Instructions.transition(nullIsIllegal(json.get(InstructionCodec.TABLE_ID),
InstructionCodec.TABLE_ID + InstructionCodec.MISSING_MEMBER_MESSAGE).asInt());
} else if (type.equals(Instruction.Type.GROUP.name())) {
GroupId groupId = new GroupId(nullIsIllegal(json.get(InstructionCodec.GROUP_ID),
InstructionCodec.GROUP_ID + InstructionCodec.MISSING_MEMBER_MESSAGE).asInt());
return Instructions.createGroup(groupId);
} else if (type.equals(Instruction.Type.METER.name())) {
MeterId meterId = MeterId.meterId(nullIsIllegal(json.get(InstructionCodec.METER_ID),
InstructionCodec.METER_ID + InstructionCodec.MISSING_MEMBER_MESSAGE).asLong());
return Instructions.meterTraffic(meterId);
} else if (type.equals(Instruction.Type.QUEUE.name())) {
long queueId = nullIsIllegal(json.get(InstructionCodec.QUEUE_ID),
InstructionCodec.QUEUE_ID + InstructionCodec.MISSING_MEMBER_MESSAGE).asLong();
if (json.get(InstructionCodec.PORT) == null ||
json.get(InstructionCodec.PORT).isNull()) {
return Instructions.setQueue(queueId, null);
} else {
return Instructions.setQueue(queueId, getPortNumber(json));
}
} else if (type.equals(Instruction.Type.L0MODIFICATION.name())) {
return decodeL0();
} else if (type.equals(Instruction.Type.L1MODIFICATION.name())) {
return decodeL1();
} else if (type.equals(Instruction.Type.L2MODIFICATION.name())) {
return decodeL2();
} else if (type.equals(Instruction.Type.L3MODIFICATION.name())) {
return decodeL3();
} else if (type.equals(Instruction.Type.L4MODIFICATION.name())) {
return decodeL4();
} else if (type.equals(Instruction.Type.EXTENSION.name())) {
return decodeExtension();
}
throw new IllegalArgumentException("Instruction type "
+ type + " is not supported");
}
}