/*
* Copyright (c) 2017 Pantheon Technologies s.r.o. and others. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/epl-v10.html
*/
package org.opendaylight.openflowplugin.impl.protocol.deserialization.multipart;
import io.netty.buffer.ByteBuf;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
import org.opendaylight.openflowjava.protocol.api.extensibility.DeserializerRegistry;
import org.opendaylight.openflowjava.protocol.api.extensibility.DeserializerRegistryInjector;
import org.opendaylight.openflowjava.protocol.api.extensibility.OFDeserializer;
import org.opendaylight.openflowjava.protocol.api.util.EncodeConstants;
import org.opendaylight.openflowjava.util.ByteBufUtils;
import org.opendaylight.openflowjava.util.ExperimenterDeserializerKeyFactory;
import org.opendaylight.openflowplugin.extension.api.path.ActionPath;
import org.opendaylight.openflowplugin.impl.protocol.deserialization.util.ActionUtil;
import org.opendaylight.openflowplugin.impl.protocol.deserialization.util.InstructionUtil;
import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action;
import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.ActionBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.ActionKey;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.InstructionBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.InstructionKey;
import org.opendaylight.yang.gen.v1.urn.opendaylight.multipart.types.rev170112.multipart.reply.MultipartReplyBody;
import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.common.types.rev130731.TableFeaturesPropType;
import org.opendaylight.yang.gen.v1.urn.opendaylight.table.types.rev131026.TableConfig;
import org.opendaylight.yang.gen.v1.urn.opendaylight.table.types.rev131026.multipart.reply.multipart.reply.body.MultipartReplyTableFeaturesBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.table.types.rev131026.set.field.match.SetFieldMatch;
import org.opendaylight.yang.gen.v1.urn.opendaylight.table.types.rev131026.table.feature.prop.type.table.feature.prop.type.ApplyActionsBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.table.types.rev131026.table.feature.prop.type.table.feature.prop.type.ApplyActionsMissBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.table.types.rev131026.table.feature.prop.type.table.feature.prop.type.ApplySetfieldBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.table.types.rev131026.table.feature.prop.type.table.feature.prop.type.ApplySetfieldMissBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.table.types.rev131026.table.feature.prop.type.table.feature.prop.type.InstructionsBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.table.types.rev131026.table.feature.prop.type.table.feature.prop.type.InstructionsMissBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.table.types.rev131026.table.feature.prop.type.table.feature.prop.type.MatchBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.table.types.rev131026.table.feature.prop.type.table.feature.prop.type.NextTableBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.table.types.rev131026.table.feature.prop.type.table.feature.prop.type.NextTableMissBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.table.types.rev131026.table.feature.prop.type.table.feature.prop.type.WildcardsBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.table.types.rev131026.table.feature.prop.type.table.feature.prop.type.WriteActionsBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.table.types.rev131026.table.feature.prop.type.table.feature.prop.type.WriteActionsMissBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.table.types.rev131026.table.feature.prop.type.table.feature.prop.type.WriteSetfieldBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.table.types.rev131026.table.feature.prop.type.table.feature.prop.type.WriteSetfieldMissBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.table.types.rev131026.table.feature.prop.type.table.feature.prop.type.match.MatchSetfieldBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.table.types.rev131026.table.feature.prop.type.table.feature.prop.type.next.table.TablesBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.table.types.rev131026.table.feature.prop.type.table.feature.prop.type.next.table.miss.TablesMissBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.table.types.rev131026.table.feature.prop.type.table.feature.prop.type.wildcards.WildcardSetfieldBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.table.types.rev131026.table.features.TableFeatures;
import org.opendaylight.yang.gen.v1.urn.opendaylight.table.types.rev131026.table.features.TableFeaturesBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.table.types.rev131026.table.features.TableFeaturesKey;
import org.opendaylight.yang.gen.v1.urn.opendaylight.table.types.rev131026.table.features.table.features.TableProperties;
import org.opendaylight.yang.gen.v1.urn.opendaylight.table.types.rev131026.table.features.table.features.TablePropertiesBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.table.types.rev131026.table.features.table.features.table.properties.TableFeatureProperties;
import org.opendaylight.yang.gen.v1.urn.opendaylight.table.types.rev131026.table.features.table.features.table.properties.TableFeaturePropertiesBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.table.types.rev131026.table.features.table.features.table.properties.TableFeaturePropertiesKey;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class MultipartReplyTableFeaturesDeserializer implements OFDeserializer<MultipartReplyBody>, DeserializerRegistryInjector {
private static final Logger LOG = LoggerFactory.getLogger(MultipartReplyTableFeaturesDeserializer.class);
private static final byte PADDING_IN_MULTIPART_REPLY_TABLE_FEATURES = 5;
private static final byte MAX_TABLE_NAME_LENGTH = 32;
private static final byte MULTIPART_REPLY_TABLE_FEATURES_STRUCTURE_LENGTH = 64;
private static final byte COMMON_PROPERTY_LENGTH = 4;
private static final TableFeaturesMatchFieldDeserializer MATCH_FIELD_DESERIALIZER =
new TableFeaturesMatchFieldDeserializer();
private DeserializerRegistry registry;
@Override
public MultipartReplyBody deserialize(ByteBuf message) {
final MultipartReplyTableFeaturesBuilder builder = new MultipartReplyTableFeaturesBuilder();
final List<TableFeatures> items = new ArrayList<>();
while (message.readableBytes() > 0) {
final int itemLength = message.readUnsignedShort();
final TableFeaturesBuilder itemBuilder = new TableFeaturesBuilder()
.setTableId(message.readUnsignedByte());
message.skipBytes(PADDING_IN_MULTIPART_REPLY_TABLE_FEATURES);
final String name = ByteBufUtils.decodeNullTerminatedString(message, MAX_TABLE_NAME_LENGTH);
final byte[] match = new byte[EncodeConstants.SIZE_OF_LONG_IN_BYTES];
message.readBytes(match);
final byte[] write = new byte[EncodeConstants.SIZE_OF_LONG_IN_BYTES];
message.readBytes(write);
items.add(itemBuilder
.setKey(new TableFeaturesKey(itemBuilder.getTableId()))
.setName(name)
.setMetadataMatch(new BigInteger(1, match))
.setMetadataWrite(new BigInteger(1, write))
.setConfig(readTableConfig(message))
.setMaxEntries(message.readUnsignedInt())
.setTableProperties(readTableProperties(message,
itemLength - MULTIPART_REPLY_TABLE_FEATURES_STRUCTURE_LENGTH))
.build());
}
return builder
.setTableFeatures(items)
.build();
}
private final TableConfig readTableConfig(ByteBuf message) {
final long input = message.readUnsignedInt();
final boolean deprecated = (input & 3) != 0;
return new TableConfig(deprecated);
}
private final TableProperties readTableProperties(ByteBuf message, int length) {
final List<TableFeatureProperties> items = new ArrayList<>();
int tableFeaturesLength = length;
int order = 0;
while (tableFeaturesLength > 0) {
final int propStartIndex = message.readerIndex();
final TableFeaturesPropType propType = TableFeaturesPropType.forValue(message.readUnsignedShort());
int propertyLength = message.readUnsignedShort();
final int paddingRemainder = propertyLength % EncodeConstants.PADDING;
tableFeaturesLength -= propertyLength;
final int commonPropertyLength = propertyLength - COMMON_PROPERTY_LENGTH;
final TableFeaturePropertiesBuilder propBuilder = new TableFeaturePropertiesBuilder()
.setOrder(order)
.setKey(new TableFeaturePropertiesKey(order));
switch (propType) {
case OFPTFPTINSTRUCTIONS:
propBuilder.setTableFeaturePropType(new InstructionsBuilder()
.setInstructions(new org.opendaylight.yang.gen.v1.urn.opendaylight.table.types
.rev131026.table.feature.prop.type.table.feature.prop.type.instructions
.InstructionsBuilder()
.setInstruction(readInstructions(message, commonPropertyLength))
.build())
.build());
break;
case OFPTFPTINSTRUCTIONSMISS:
propBuilder.setTableFeaturePropType(new InstructionsMissBuilder()
.setInstructionsMiss(new org.opendaylight.yang.gen.v1.urn.opendaylight.table.types
.rev131026.table.feature.prop.type.table.feature.prop.type.instructions.miss
.InstructionsMissBuilder()
.setInstruction(readInstructions(message, commonPropertyLength))
.build())
.build());
break;
case OFPTFPTNEXTTABLES:
propBuilder.setTableFeaturePropType(new NextTableBuilder()
.setTables(new TablesBuilder()
.setTableIds(readNextTableIds(message, commonPropertyLength))
.build())
.build());
break;
case OFPTFPTNEXTTABLESMISS:
propBuilder.setTableFeaturePropType(new NextTableMissBuilder()
.setTablesMiss(new TablesMissBuilder()
.setTableIds(readNextTableIds(message, commonPropertyLength))
.build())
.build());
break;
case OFPTFPTWRITEACTIONS:
propBuilder.setTableFeaturePropType(new WriteActionsBuilder()
.setWriteActions(new org.opendaylight.yang.gen.v1.urn.opendaylight.table.types.rev131026
.table.feature.prop.type.table.feature.prop.type.write.actions.WriteActionsBuilder()
.setAction(readActions(message, commonPropertyLength))
.build())
.build());
break;
case OFPTFPTWRITEACTIONSMISS:
propBuilder.setTableFeaturePropType(new WriteActionsMissBuilder()
.setWriteActionsMiss(new org.opendaylight.yang.gen.v1.urn.opendaylight.table.types
.rev131026.table.feature.prop.type.table.feature.prop.type.write.actions.miss
.WriteActionsMissBuilder()
.setAction(readActions(message, commonPropertyLength))
.build())
.build());
break;
case OFPTFPTAPPLYACTIONS:
propBuilder.setTableFeaturePropType(new ApplyActionsBuilder()
.setApplyActions(new org.opendaylight.yang.gen.v1.urn.opendaylight.table.types.rev131026
.table.feature.prop.type.table.feature.prop.type.apply.actions.ApplyActionsBuilder()
.setAction(readActions(message, commonPropertyLength))
.build())
.build());
break;
case OFPTFPTAPPLYACTIONSMISS:
propBuilder.setTableFeaturePropType(new ApplyActionsMissBuilder()
.setApplyActionsMiss(new org.opendaylight.yang.gen.v1.urn.opendaylight.table.types
.rev131026.table.feature.prop.type.table.feature.prop.type.apply.actions.miss
.ApplyActionsMissBuilder()
.setAction(readActions(message, commonPropertyLength))
.build())
.build());
break;
case OFPTFPTMATCH:
propBuilder.setTableFeaturePropType(new MatchBuilder()
.setMatchSetfield(new MatchSetfieldBuilder()
.setSetFieldMatch(readMatchFields(message, commonPropertyLength))
.build())
.build());
break;
case OFPTFPTWILDCARDS:
propBuilder.setTableFeaturePropType(new WildcardsBuilder()
.setWildcardSetfield(new WildcardSetfieldBuilder()
.setSetFieldMatch(readMatchFields(message, commonPropertyLength))
.build())
.build());
break;
case OFPTFPTWRITESETFIELD:
propBuilder.setTableFeaturePropType(new WriteSetfieldBuilder()
.setWriteSetfield(new org.opendaylight.yang.gen.v1.urn.opendaylight.table.types.rev131026
.table.feature.prop.type.table.feature.prop.type.write.setfield.WriteSetfieldBuilder()
.setSetFieldMatch(readMatchFields(message, commonPropertyLength))
.build())
.build());
break;
case OFPTFPTWRITESETFIELDMISS:
propBuilder.setTableFeaturePropType(new WriteSetfieldMissBuilder()
.setWriteSetfieldMiss(new org.opendaylight.yang.gen.v1.urn.opendaylight.table.types
.rev131026.table.feature.prop.type.table.feature.prop.type.write.setfield.miss
.WriteSetfieldMissBuilder()
.setSetFieldMatch(readMatchFields(message, commonPropertyLength))
.build())
.build());
break;
case OFPTFPTAPPLYSETFIELD:
propBuilder.setTableFeaturePropType(new ApplySetfieldBuilder()
.setApplySetfield(new org.opendaylight.yang.gen.v1.urn.opendaylight.table.types.rev131026
.table.feature.prop.type.table.feature.prop.type.apply.setfield.ApplySetfieldBuilder()
.setSetFieldMatch(readMatchFields(message, commonPropertyLength))
.build())
.build());
break;
case OFPTFPTAPPLYSETFIELDMISS:
propBuilder.setTableFeaturePropType(new ApplySetfieldMissBuilder()
.setApplySetfieldMiss(new org.opendaylight.yang.gen.v1.urn.opendaylight.table.types
.rev131026.table.feature.prop.type.table.feature.prop.type.apply.setfield.miss
.ApplySetfieldMissBuilder()
.setSetFieldMatch(readMatchFields(message, commonPropertyLength))
.build())
.build());
break;
case OFPTFPTEXPERIMENTER:
case OFPTFPTEXPERIMENTERMISS:
final long expId = message.readUnsignedInt();
message.readerIndex(propStartIndex);
final OFDeserializer<TableFeatureProperties> propDeserializer = registry
.getDeserializer(ExperimenterDeserializerKeyFactory
.createMultipartReplyTFDeserializerKey(EncodeConstants.OF13_VERSION_ID, expId));
// TODO: Finish experimenter table features (currently using OFJava deserialization only to skip
// bytes)
propDeserializer.deserialize(message);
LOG.debug("Table feature property type {} is not handled yet.", propType);
break;
}
if (paddingRemainder != 0) {
message.skipBytes(EncodeConstants.PADDING - paddingRemainder);
tableFeaturesLength -= EncodeConstants.PADDING - paddingRemainder;
}
items.add(propBuilder.build());
order++;
}
return new TablePropertiesBuilder()
.setTableFeatureProperties(items)
.build();
}
private List<SetFieldMatch> readMatchFields(ByteBuf message, int length) {
final List<SetFieldMatch> matchFields = new ArrayList<>();
final int startIndex = message.readerIndex();
while ((message.readerIndex() - startIndex) < length) {
MATCH_FIELD_DESERIALIZER
.deserialize(message)
.map(matchFields::add)
.orElseGet(() -> {
message.skipBytes(2 * EncodeConstants.SIZE_OF_SHORT_IN_BYTES);
return Boolean.FALSE;
});
}
return matchFields;
}
private List<Short> readNextTableIds(ByteBuf message, int length) {
final List<Short> tableIds = new ArrayList<>();
int nextTableLength = length;
while (nextTableLength > 0) {
tableIds.add(message.readUnsignedByte());
nextTableLength -= 1;
}
return tableIds;
}
private List<org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list
.Instruction> readInstructions(ByteBuf message, int length) {
final List<org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list
.Instruction> instructions = new ArrayList<>();
final int startIndex = message.readerIndex();
int offset = 0;
while ((message.readerIndex() - startIndex) < length) {
try {
instructions.add(new InstructionBuilder()
.setKey(new InstructionKey(offset))
.setOrder(offset)
.setInstruction(InstructionUtil
.readInstructionHeader(EncodeConstants.OF13_VERSION_ID, message, registry))
.build());
offset++;
} catch (ClassCastException | IllegalStateException e) {
message.skipBytes(2 * EncodeConstants.SIZE_OF_SHORT_IN_BYTES);
}
}
return instructions;
}
private List<Action> readActions(ByteBuf message, int length) {
final List<Action> actions = new ArrayList<>();
final int startIndex = message.readerIndex();
int offset = 0;
while ((message.readerIndex() - startIndex) < length) {
try {
actions.add(new ActionBuilder()
.setKey(new ActionKey(offset))
.setOrder(offset)
.setAction(ActionUtil.readActionHeader(EncodeConstants.OF13_VERSION_ID, message, registry,
ActionPath.FLOWSSTATISTICSUPDATE_FLOWANDSTATISTICSMAPLIST_INSTRUCTIONS_INSTRUCTION_INSTRUCTION_APPLYACTIONSCASE_APPLYACTIONS_ACTION_ACTION))
.build());
offset++;
} catch (ClassCastException | IllegalStateException e) {
message.skipBytes(2 * EncodeConstants.SIZE_OF_SHORT_IN_BYTES);
}
}
return actions;
}
@Override
public void injectDeserializerRegistry(DeserializerRegistry deserializerRegistry) {
registry = deserializerRegistry;
}
}