ONOS-7077 Openflow 1.5 OXS and stat trigger support

Change-Id: I006bcd3d8eac451a780c7e5c69a12298ead14281
diff --git a/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilderVer15.java b/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilderVer15.java
new file mode 100644
index 0000000..7ba893a
--- /dev/null
+++ b/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilderVer15.java
@@ -0,0 +1,301 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * 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.provider.of.flow.impl;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.driver.DriverService;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.flow.StatTriggerField;
+import org.onosproject.net.flow.StatTriggerFlag;
+import org.onosproject.net.flow.instructions.Instruction;
+import org.onosproject.net.flow.instructions.Instructions;
+import org.projectfloodlight.openflow.protocol.OFFactory;
+import org.projectfloodlight.openflow.protocol.OFFlowAdd;
+import org.projectfloodlight.openflow.protocol.OFFlowMod;
+import org.projectfloodlight.openflow.protocol.OFFlowModFlags;
+import org.projectfloodlight.openflow.protocol.OFOxsList;
+import org.projectfloodlight.openflow.protocol.OFStatTriggerFlags;
+import org.projectfloodlight.openflow.protocol.action.OFAction;
+import org.projectfloodlight.openflow.protocol.action.OFActionGroup;
+import org.projectfloodlight.openflow.protocol.action.OFActionMeter;
+import org.projectfloodlight.openflow.protocol.action.OFActionOutput;
+import org.projectfloodlight.openflow.protocol.action.OFActionSetQueue;
+import org.projectfloodlight.openflow.protocol.instruction.OFInstruction;
+import org.projectfloodlight.openflow.protocol.match.Match;
+import org.projectfloodlight.openflow.protocol.oxs.OFOxs;
+import org.projectfloodlight.openflow.types.OFBufferId;
+import org.projectfloodlight.openflow.types.OFGroup;
+import org.projectfloodlight.openflow.types.OFPort;
+import org.projectfloodlight.openflow.types.TableId;
+import org.projectfloodlight.openflow.types.U32;
+import org.projectfloodlight.openflow.types.U64;
+
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+
+import static org.projectfloodlight.openflow.protocol.OFStatTriggerFlags.ONLY_FIRST;
+import static org.projectfloodlight.openflow.protocol.OFStatTriggerFlags.PERIODIC;
+
+/**
+ * Flow mod builder for OpenFlow 1.5+.
+ */
+public class FlowModBuilderVer15 extends FlowModBuilderVer13 {
+
+    /**
+     * Constructor for a flow mod builder for OpenFlow 1.5.
+     *
+     * @param flowRule      the flow rule to transform into a flow mod
+     * @param factory       the OpenFlow factory to use to build the flow mod
+     * @param xid           the transaction ID
+     * @param driverService the device driver service
+     */
+    protected FlowModBuilderVer15(FlowRule flowRule, OFFactory factory,
+                                  Optional<Long> xid,
+                                  Optional<DriverService> driverService) {
+        super(flowRule, factory, xid, driverService);
+    }
+
+    @Override
+    public OFFlowMod buildFlowAdd() {
+        Match match = buildMatch();
+        List<OFAction> deferredActions = buildActions(treatment.deferred(), false);
+        List<OFAction> immediateActions = buildActions(treatment.immediate(), true);
+        List<OFInstruction> instructions = Lists.newLinkedList();
+
+        if (treatment.clearedDeferred()) {
+            instructions.add(factory().instructions().clearActions());
+        }
+        if (!immediateActions.isEmpty()) {
+            instructions.add(factory().instructions().applyActions(immediateActions));
+        }
+        if (!deferredActions.isEmpty()) {
+            instructions.add(factory().instructions().writeActions(deferredActions));
+        }
+        if (treatment.tableTransition() != null) {
+            instructions.add(buildTableGoto(treatment.tableTransition()));
+        }
+        if (treatment.writeMetadata() != null) {
+            instructions.add(buildMetadata(treatment.writeMetadata()));
+        }
+        if (treatment.statTrigger() != null) {
+            instructions.add(buildStatTrigger(treatment.statTrigger()));
+        }
+
+
+
+        long cookie = flowRule().id().value();
+
+        OFFlowAdd fm = factory().buildFlowAdd()
+                .setXid(xid)
+                .setCookie(U64.of(cookie))
+                .setBufferId(OFBufferId.NO_BUFFER)
+                .setInstructions(instructions)
+                .setMatch(match)
+                .setFlags(Collections.singleton(OFFlowModFlags.SEND_FLOW_REM))
+                .setPriority(flowRule().priority())
+                .setTableId(TableId.of(flowRule().tableId()))
+                .setHardTimeout(flowRule().hardTimeout())
+                .build();
+
+        return fm;
+    }
+
+    @Override
+    public OFFlowMod buildFlowMod() {
+        Match match = buildMatch();
+        List<OFAction> deferredActions = buildActions(treatment.deferred(), false);
+        List<OFAction> immediateActions = buildActions(treatment.immediate(), true);
+        List<OFInstruction> instructions = Lists.newLinkedList();
+
+
+        if (!immediateActions.isEmpty()) {
+            instructions.add(factory().instructions().applyActions(immediateActions));
+        }
+        if (treatment.clearedDeferred()) {
+            instructions.add(factory().instructions().clearActions());
+        }
+        if (!deferredActions.isEmpty()) {
+            instructions.add(factory().instructions().writeActions(deferredActions));
+        }
+        if (treatment.tableTransition() != null) {
+            instructions.add(buildTableGoto(treatment.tableTransition()));
+        }
+        if (treatment.writeMetadata() != null) {
+            instructions.add(buildMetadata(treatment.writeMetadata()));
+        }
+        if (treatment.statTrigger() != null) {
+            instructions.add(buildStatTrigger(treatment.statTrigger()));
+        }
+
+        long cookie = flowRule().id().value();
+
+        OFFlowMod fm = factory().buildFlowModify()
+                .setXid(xid)
+                .setCookie(U64.of(cookie))
+                .setBufferId(OFBufferId.NO_BUFFER)
+                .setInstructions(instructions)
+                .setMatch(match)
+                .setFlags(Collections.singleton(OFFlowModFlags.SEND_FLOW_REM))
+                .setPriority(flowRule().priority())
+                .setTableId(TableId.of(flowRule().tableId()))
+                .setHardTimeout(flowRule().hardTimeout())
+                .build();
+
+        return fm;
+    }
+
+    private List<OFAction> buildActions(List<Instruction> treatments, Boolean immediateActions) {
+        if (treatment == null) {
+            return Collections.emptyList();
+        }
+
+        boolean tableFound = false;
+        List<OFAction> actions = new LinkedList<>();
+
+        //Meter action handling
+        if (null != treatment.metered() && immediateActions) {
+            OFAction meterAction = buildMultipleMeterAction(treatment.metered());
+            actions.add(meterAction);
+        }
+
+        for (Instruction i : treatments) {
+            switch (i.type()) {
+                case NOACTION:
+                    return Collections.emptyList();
+                case L0MODIFICATION:
+                    actions.add(buildL0Modification(i));
+                    break;
+                case L1MODIFICATION:
+                    actions.add(buildL1Modification(i));
+                    break;
+                case L2MODIFICATION:
+                    actions.add(buildL2Modification(i));
+                    break;
+                case L3MODIFICATION:
+                    actions.add(buildL3Modification(i));
+                    break;
+                case L4MODIFICATION:
+                    actions.add(buildL4Modification(i));
+                    break;
+                case OUTPUT:
+                    Instructions.OutputInstruction out = (Instructions.OutputInstruction) i;
+                    OFActionOutput.Builder action = factory().actions().buildOutput()
+                            .setPort(OFPort.of((int) out.port().toLong()));
+                    if (out.port().equals(PortNumber.CONTROLLER)) {
+                        action.setMaxLen(OFPCML_NO_BUFFER);
+                    }
+                    actions.add(action.build());
+                    break;
+                case GROUP:
+                    Instructions.GroupInstruction group = (Instructions.GroupInstruction) i;
+                    OFActionGroup.Builder groupBuilder = factory().actions().buildGroup()
+                            .setGroup(OFGroup.of(group.groupId().id()));
+                    actions.add(groupBuilder.build());
+                    break;
+                case QUEUE:
+                    Instructions.SetQueueInstruction queue = (Instructions.SetQueueInstruction) i;
+                    OFActionSetQueue.Builder queueBuilder = factory().actions().buildSetQueue()
+                            .setQueueId(queue.queueId());
+                    actions.add(queueBuilder.build());
+                    break;
+                case TABLE:
+                    //FIXME: should not occur here.
+                    tableFound = true;
+                    break;
+                case EXTENSION:
+                    actions.add(buildExtensionAction(((Instructions.ExtensionInstructionWrapper) i)
+                            .extensionInstruction()));
+                    break;
+                default:
+                    log.warn("Instruction type {} not yet implemented.", i.type());
+            }
+        }
+
+        if (tableFound && actions.isEmpty()) {
+            // handles the case where there are no actions, but there is
+            // a goto instruction for the next table
+            return Collections.emptyList();
+        }
+        return actions;
+    }
+
+    private OFOxsList getOFOxsList(Map<StatTriggerField, Long> statTriggerMap) {
+        OFFactory factory = factory();
+        List<OFOxs<?>> ofOxsList = Lists.newArrayList();
+        for (Map.Entry<StatTriggerField, Long> entry : statTriggerMap.entrySet()) {
+            switch (entry.getKey()) {
+                case DURATION:
+                    ofOxsList.add(factory.oxss().buildDuration().setValue(U64.of(entry.getValue())).build());
+                    break;
+                case IDLE_TIME:
+                    ofOxsList.add(factory.oxss().buildIdleTime().setValue(U64.of(entry.getValue())).build());
+                    break;
+                case BYTE_COUNT:
+                    ofOxsList.add(factory.oxss().buildByteCount().setValue(U64.of(entry.getValue())).build());
+                    break;
+                case FLOW_COUNT:
+                    ofOxsList.add(factory.oxss().buildFlowCount().setValue(U32.of(entry.getValue())).build());
+                    break;
+                case PACKET_COUNT:
+                    ofOxsList.add(factory.oxss().buildPacketCount().setValue(U64.of(entry.getValue())).build());
+                    break;
+                default:
+                    log.warn("Unsupported Stat Trigger field");
+                    break;
+            }
+        }
+        return OFOxsList.ofList(ofOxsList);
+    }
+
+    private Set<OFStatTriggerFlags> getStatTriggerFlag(StatTriggerFlag flag) {
+        Set<OFStatTriggerFlags> statTriggerFlagsSet = Sets.newHashSet();
+        switch (flag) {
+            case PERIODIC:
+                statTriggerFlagsSet.add(PERIODIC);
+                break;
+            case ONLY_FIRST:
+                statTriggerFlagsSet.add(ONLY_FIRST);
+                break;
+            default:
+                break;
+        }
+        return statTriggerFlagsSet;
+    }
+
+    /**
+     * Meter action builder.
+     *
+     * @param meterInstruction meter instruction
+     * @return meter action
+     */
+    protected OFAction buildMultipleMeterAction(Instructions.MeterInstruction meterInstruction) {
+        OFActionMeter.Builder meterBuilder = factory().actions().buildMeter()
+                .setMeterId(meterInstruction.meterId().id());
+        return meterBuilder.build();
+    }
+
+    protected OFInstruction buildStatTrigger(Instructions.StatTriggerInstruction s) {
+        OFInstruction instruction = factory().instructions().statTrigger(getStatTriggerFlag(s.getStatTriggerFlag()),
+                getOFOxsList(s.getStatTriggerFieldMap()));
+        return instruction;
+    }
+}