Add FlowMessageDeserializer 89/50189/19
authorTomas Slusny <tomas.slusny@pantheon.tech>
Thu, 5 Jan 2017 15:54:46 +0000 (16:54 +0100)
committerTomas Slusny <tomas.slusny@pantheon.tech>
Wed, 15 Feb 2017 15:13:41 +0000 (16:13 +0100)
Inject FlowMessageDeserializer into OFJ.
This deserializer will deserialize raw bytes into FlowMessage.

Change-Id: Ic098a80a6d0b97fbf8ab3761d15a09de3d46defd
Signed-off-by: Tomas Slusny <tomas.slusny@pantheon.tech>
openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/protocol/deserialization/MessageDeserializerInjector.java
openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/protocol/deserialization/messages/FlowMessageDeserializer.java [new file with mode: 0644]
openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/protocol/deserialization/AbstractDeserializerTest.java
openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/protocol/deserialization/messages/FlowMessageDeserializerTest.java [new file with mode: 0644]

index c0e0564219df9b3b155620602cb408591a9afc61..c5198ccd511838838d0ec7ee6e3d6ec3ab39b537 100644 (file)
@@ -16,6 +16,8 @@ import org.opendaylight.openflowjava.protocol.api.extensibility.OFDeserializer;
 import org.opendaylight.openflowjava.protocol.api.keys.MessageCodeKey;
 import org.opendaylight.openflowjava.protocol.api.keys.TypeToClassKey;
 import org.opendaylight.openflowjava.protocol.api.util.EncodeConstants;
+import org.opendaylight.openflowplugin.impl.protocol.deserialization.messages.FlowMessageDeserializer;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.FlowMessage;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.OfHeader;
 
 public class MessageDeserializerInjector {
@@ -28,6 +30,8 @@ public class MessageDeserializerInjector {
         // Inject new message deserializers here using injector created by createInjector method
         final Function<Integer, Function<Class<? extends OfHeader>, Consumer<OFDeserializer<? extends OfHeader>>>> injector =
                 createInjector(provider, EncodeConstants.OF13_VERSION_ID);
+
+        injector.apply(14).apply(FlowMessage.class).accept(new FlowMessageDeserializer());
     }
 
     /**
@@ -49,4 +53,4 @@ public class MessageDeserializerInjector {
         };
     }
 
-}
\ No newline at end of file
+}
diff --git a/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/protocol/deserialization/messages/FlowMessageDeserializer.java b/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/protocol/deserialization/messages/FlowMessageDeserializer.java
new file mode 100644 (file)
index 0000000..871ba56
--- /dev/null
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 2016 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.messages;
+
+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.keys.MessageCodeKey;
+import org.opendaylight.openflowjava.protocol.api.util.EncodeConstants;
+import org.opendaylight.openflowjava.protocol.impl.util.InstructionConstants;
+import org.opendaylight.openflowplugin.api.openflow.protocol.deserialization.MessageCodeExperimenterKey;
+import org.opendaylight.openflowplugin.extension.api.path.ActionPath;
+import org.opendaylight.openflowplugin.extension.api.path.MatchPath;
+import org.opendaylight.openflowplugin.impl.protocol.deserialization.key.MessageCodeActionExperimenterKey;
+import org.opendaylight.openflowplugin.impl.protocol.deserialization.key.MessageCodeMatchKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.FlowCookie;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.FlowMessage;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.FlowMessageBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.FlowModFlags;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.InstructionsBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.MatchBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.Instruction;
+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.model.match.types.rev131026.Match;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.common.types.rev130731.FlowModCommand;
+
+import io.netty.buffer.ByteBuf;
+
+public class FlowMessageDeserializer implements OFDeserializer<FlowMessage>, DeserializerRegistryInjector {
+
+    private static final byte PADDING = 2;
+
+    private static final MessageCodeKey MATCH_KEY = new MessageCodeMatchKey(EncodeConstants.OF13_VERSION_ID,
+            EncodeConstants.EMPTY_VALUE, Match.class,
+            MatchPath.FLOWSSTATISTICSUPDATE_FLOWANDSTATISTICSMAPLIST_MATCH);
+
+    private DeserializerRegistry registry;
+
+    @Override
+    public FlowMessage deserialize(ByteBuf message) {
+        final FlowMessageBuilder builder = new FlowMessageBuilder()
+            .setVersion((short) EncodeConstants.OF13_VERSION_ID)
+            .setXid(message.readUnsignedInt())
+            .setCookie(new FlowCookie(BigInteger.valueOf(message.readLong())))
+            .setCookieMask(new FlowCookie(BigInteger.valueOf(message.readLong())))
+            .setTableId(message.readUnsignedByte())
+            .setCommand(FlowModCommand.forValue(message.readUnsignedByte()))
+            .setIdleTimeout(message.readUnsignedShort())
+            .setHardTimeout(message.readUnsignedShort())
+            .setPriority(message.readUnsignedShort())
+            .setBufferId(message.readUnsignedInt())
+            .setOutPort(BigInteger.valueOf(message.readUnsignedInt()))
+            .setOutGroup(message.readUnsignedInt())
+            .setFlags(createFlowModFlagsFromBitmap(message.readUnsignedShort()));
+
+        message.skipBytes(PADDING);
+
+        final OFDeserializer<Match> matchDeserializer = registry.getDeserializer(MATCH_KEY);
+        builder.setMatch(new MatchBuilder(matchDeserializer.deserialize(message)).build());
+
+        final int length = message.readableBytes();
+
+        if (length > 0) {
+            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) {
+                final int type = message.getUnsignedShort(message.readerIndex());
+                OFDeserializer<Instruction> deserializer = null;
+
+                if (InstructionConstants.APPLY_ACTIONS_TYPE == type) {
+                    deserializer = registry.getDeserializer(
+                            new MessageCodeActionExperimenterKey(
+                                EncodeConstants.OF13_VERSION_ID, type, Instruction.class,
+                                ActionPath.NODES_NODE_TABLE_FLOW_INSTRUCTIONS_INSTRUCTION_APPLYACTIONSCASE_APPLYACTIONS_ACTION_ACTION_EXTENSIONLIST_EXTENSION,
+                                null));
+                } else if (InstructionConstants.WRITE_ACTIONS_TYPE == type) {
+                    deserializer = registry.getDeserializer(
+                            new MessageCodeActionExperimenterKey(
+                                EncodeConstants.OF13_VERSION_ID, type, Instruction.class,
+                                ActionPath.NODES_NODE_TABLE_FLOW_INSTRUCTIONS_INSTRUCTION_WRITEACTIONSCASE_WRITEACTIONS_ACTION_ACTION_EXTENSIONLIST_EXTENSION,
+                                null));
+                } else {
+                    Long expId = null;
+
+                    if (EncodeConstants.EXPERIMENTER_VALUE == type) {
+                        expId = message.getUnsignedInt(message.readerIndex() +
+                                2 * EncodeConstants.SIZE_OF_SHORT_IN_BYTES);
+                    }
+
+                    deserializer = registry.getDeserializer(
+                            new MessageCodeExperimenterKey(
+                                EncodeConstants.OF13_VERSION_ID, type, Instruction.class, expId));
+                }
+
+                instructions.add(new InstructionBuilder()
+                        .setKey(new InstructionKey(offset))
+                        .setOrder(offset)
+                        .setInstruction(deserializer.deserialize(message))
+                        .build());
+
+                offset++;
+            }
+
+            builder.setInstructions(new InstructionsBuilder()
+                    .setInstruction(instructions)
+                    .build());
+        }
+
+        return builder.build();
+    }
+
+    private static FlowModFlags createFlowModFlagsFromBitmap(int input) {
+        final Boolean _oFPFFSENDFLOWREM = (input & (1 << 0)) > 0;
+        final Boolean _oFPFFCHECKOVERLAP = (input & (1 << 1)) > 0;
+        final Boolean _oFPFFRESETCOUNTS = (input & (1 << 2)) > 0;
+        final Boolean _oFPFFNOPKTCOUNTS = (input & (1 << 3)) > 0;
+        final Boolean _oFPFFNOBYTCOUNTS = (input & (1 << 4)) > 0;
+        return new FlowModFlags(_oFPFFCHECKOVERLAP, _oFPFFNOBYTCOUNTS, _oFPFFNOPKTCOUNTS, _oFPFFRESETCOUNTS,
+                _oFPFFSENDFLOWREM);
+    }
+
+    @Override
+    public void injectDeserializerRegistry(DeserializerRegistry deserializerRegistry) {
+        registry = deserializerRegistry;
+    }
+
+}
diff --git a/openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/protocol/deserialization/messages/FlowMessageDeserializerTest.java b/openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/protocol/deserialization/messages/FlowMessageDeserializerTest.java
new file mode 100644 (file)
index 0000000..b25cd90
--- /dev/null
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 2016 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.messages;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+import org.opendaylight.openflowjava.protocol.api.util.EncodeConstants;
+import org.opendaylight.openflowjava.protocol.api.util.OxmMatchConstants;
+import org.opendaylight.openflowjava.protocol.impl.util.ActionConstants;
+import org.opendaylight.openflowjava.protocol.impl.util.InstructionConstants;
+import org.opendaylight.openflowjava.util.ByteBufUtils;
+import org.opendaylight.openflowplugin.impl.protocol.deserialization.AbstractDeserializerTest;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.PopPbbActionCase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.FlowMessage;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.FlowModFlags;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.Instruction;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.ApplyActionsCase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.common.types.rev130731.FlowModCommand;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.UnpooledByteBufAllocator;
+
+public class FlowMessageDeserializerTest extends AbstractDeserializerTest {
+
+    private static final byte PADDING = 2;
+
+    private static final int TYPE = 14;
+    private static final int XID = 42;
+    private static final FlowModCommand COMMAND = FlowModCommand.OFPFCADD;
+    private static final int BUFFER_ID = 26;
+    private static final int OUT_PORT = 22;
+    private static final int OUT_GROUP = 25;
+
+    private static final boolean SEND_FLOWREM = true;
+    private static final boolean RESET_COUNTS = false;
+    private static final boolean NO_PKTCOUNTS = true;
+    private static final boolean NO_BYTCOUNTS = true;
+    private static final boolean CHECK_OVERLAP = false;
+
+    private static final FlowModFlags FLAGS = new FlowModFlags(
+            CHECK_OVERLAP, NO_BYTCOUNTS, NO_PKTCOUNTS, RESET_COUNTS, SEND_FLOWREM);
+    private static final long COOKIE = 12;
+    private static final long COOKIE_MASK = 14;
+    private static final int TABLE_ID = 2;
+    private static final int IDLE_TIMEOUT = 20;
+    private static final int HARD_TIMEOUT = 35;
+    private static final int PRIORITY = 100;
+    private static final int OXM_MATCH_TYPE_CODE = 1;
+    private static final int MPLS_LABEL = 135;
+
+    private ByteBuf buffer;
+
+    @Override
+    protected void init() {
+        buffer = UnpooledByteBufAllocator.DEFAULT.buffer();
+    }
+
+    @Test
+    public void deserialize() throws Exception {
+        // Flow header
+        buffer.writeByte(TYPE);
+        buffer.writeShort(EncodeConstants.EMPTY_LENGTH);
+        buffer.writeInt(XID);
+        buffer.writeLong(COOKIE);
+        buffer.writeLong(COOKIE_MASK);
+        buffer.writeByte(TABLE_ID);
+        buffer.writeByte(COMMAND.getIntValue());
+        buffer.writeShort(IDLE_TIMEOUT);
+        buffer.writeShort(HARD_TIMEOUT);
+        buffer.writeShort(PRIORITY);
+        buffer.writeInt(BUFFER_ID);
+        buffer.writeInt(OUT_PORT);
+        buffer.writeInt(OUT_GROUP);
+        buffer.writeShort(ByteBufUtils.fillBitMask(0,
+                    FLAGS.isSENDFLOWREM(),
+                    FLAGS.isCHECKOVERLAP(),
+                    FLAGS.isRESETCOUNTS(),
+                    FLAGS.isNOPKTCOUNTS(),
+                    FLAGS.isNOBYTCOUNTS()));
+        buffer.writeZero(PADDING);
+
+        // Match header
+        int matchStartIndex = buffer.writerIndex();
+        buffer.writeShort(OXM_MATCH_TYPE_CODE);
+        int matchLengthIndex = buffer.writerIndex();
+        buffer.writeShort(EncodeConstants.EMPTY_LENGTH);
+
+        // MplsLabel match
+        buffer.writeShort(OxmMatchConstants.OPENFLOW_BASIC_CLASS);
+        buffer.writeByte(OxmMatchConstants.MPLS_LABEL << 1);
+        buffer.writeByte(EncodeConstants.SIZE_OF_INT_IN_BYTES);
+        buffer.writeInt(MPLS_LABEL);
+
+        // Match footer
+        int matchLength = buffer.writerIndex() - matchStartIndex;
+        buffer.setShort(matchLengthIndex, matchLength);
+        int paddingRemainder = matchLength % EncodeConstants.PADDING;
+        if (paddingRemainder != 0) {
+            buffer.writeZero(EncodeConstants.PADDING - paddingRemainder);
+        }
+
+        // Instructions header
+        int instructionStartIndex = buffer.writerIndex();
+        buffer.writeShort(InstructionConstants.APPLY_ACTIONS_TYPE);
+        int instructionLengthIndex = buffer.writerIndex();
+        buffer.writeShort(EncodeConstants.EMPTY_LENGTH);
+        buffer.writeZero(InstructionConstants.PADDING_IN_ACTIONS_INSTRUCTION);
+
+        // POP PBB action
+        buffer.writeShort(ActionConstants.POP_PBB_CODE);
+        buffer.writeShort(ActionConstants.GENERAL_ACTION_LENGTH);
+        buffer.writeZero(ActionConstants.PADDING_IN_ACTION_HEADER);
+
+        // Count total length of instructions
+        buffer.setShort(instructionLengthIndex, buffer.writerIndex() - instructionStartIndex);
+
+        // Deserialize and check everything
+        final FlowMessage message = (FlowMessage) getFactory()
+            .deserialize(buffer, EncodeConstants.OF13_VERSION_ID);
+
+        assertEquals(XID, message.getXid().intValue());
+        assertEquals(COMMAND.getIntValue(), message.getCommand().getIntValue());
+        assertEquals(MPLS_LABEL, message.getMatch().getProtocolMatchFields().getMplsLabel().intValue());
+        assertEquals(1, message.getInstructions().getInstruction().size());
+
+        final Instruction instruction = message.getInstructions().getInstruction().get(0).getInstruction();
+        assertEquals(ApplyActionsCase.class, instruction.getImplementedInterface());
+
+        final ApplyActionsCase applyActions = ApplyActionsCase.class.cast(instruction);
+        assertEquals(1, applyActions.getApplyActions().getAction().size());
+        assertEquals(PopPbbActionCase.class, applyActions.getApplyActions().getAction().get(0)
+                .getAction().getImplementedInterface());
+    }
+
+}