[ONOS-7893] TerminalDevice driver for OpenConfig transponders.
Includes FlowRuleProgrammable and DeviceDiscovery behaviours.
The patch enables the configuration of client and line sides of the transponder.
FlowRules coming from the compilation of OpticalConnectivity and OpticalCircuit intents are translated in proper netconf/xml messages.
The patch also fixes an issue in the OpticalCircuiIntentCompiler to avoid overwriting among generated flowrules in case of bidirectional intents.
The patch also extend optical-rest app to enable deletion of OpticalCircuit intents.
An example of OpenConfig TerminalDevice xml datastore is included.

patch 2: Addressed comments by Ramon Casellas. Checkstyle.

patch 3: Fixed typo in OpticalConnectivityIntentCompiler.

patch 4: Checkstyle.

patch 5: Addressed comments by Andrea Campanella.

patch 6: Introduced Jira tag

patch 7: Update commit message.

patch 8: Ranaming files.

patch 9: Checkstyle.

Change-Id: Ia50bc0877ecd314aaf48cdb8465451a68313ce3a
diff --git a/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/openconfig/TerminalDeviceFlowRule.java b/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/openconfig/TerminalDeviceFlowRule.java
new file mode 100644
index 0000000..fb6eead
--- /dev/null
+++ b/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/openconfig/TerminalDeviceFlowRule.java
@@ -0,0 +1,244 @@
+/*
+ * Copyright 2018-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.
+
+ * This work was partially supported by EC H2020 project METRO-HAUL (761727).
+ */
+
+package org.onosproject.drivers.odtn.openconfig;
+
+import org.onosproject.net.OchSignal;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.flow.DefaultFlowRule;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.flow.criteria.Criterion;
+import org.onosproject.net.flow.criteria.OchSignalCriterion;
+import org.onosproject.net.flow.criteria.OchSignalTypeCriterion;
+import org.onosproject.net.flow.criteria.PortCriterion;
+import org.onosproject.net.flow.instructions.Instruction;
+import org.onosproject.net.flow.instructions.Instructions;
+import org.onosproject.net.flow.instructions.L0ModificationInstruction;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.List;
+import java.util.Set;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+public class TerminalDeviceFlowRule extends DefaultFlowRule {
+    private static final Logger log = LoggerFactory.getLogger(TerminalDeviceFlowRule.class);
+
+    public enum Type {
+        CLIENT_INGRESS,
+        CLIENT_EGRESS,
+        LINE_INGRESS,
+        LINE_EGRESS
+    }
+
+    //As generated by the OpticalConnectivityIntentCompiler
+    private static final int NUM_CRITERIA_LINE_EGRESS_RULE = 3;
+    private static final int NUM_INSTRUCTIONS_LINE_EGRESS_RULE = 1;
+    private static final int NUM_CRITERIA_LINE_INGRESS_RULE = 1;
+    private static final int NUM_INSTRUCTIONS_LINE_INGRESS_RULE = 2;
+
+    //As generated by the OpticalCoircuitIntentCompiler
+    private static final int NUM_CRITERIA_CLIENT_RULES = 1;
+    private static final int NUM_INSTRUCTIONS_CLIENT_RULES = 1;
+
+    public Type type;
+
+    private PortNumber inPortNumber;
+    private PortNumber outPortNumber;
+    private OchSignal ochSignal;
+    private String connectionName;
+
+
+    public TerminalDeviceFlowRule(FlowRule rule, List<PortNumber> linePorts) {
+        super(rule);
+
+        Set<Criterion> criteria = rule.selector().criteria();
+        List<Instruction> instructions = rule.treatment().immediate();
+
+        /*Rules for TerminalDevice are generated in OpticalPathIntentCompiler with two types of intents
+        --- OpticalConnectivity intent compilation generates following flow rules
+        OPTICAL LINE level at INGRESS node -- criteria: input port; output port, OChSignal;
+        OPTICAL LINE level at EGRESS node -- criteria: input port, OChSignal and OCh type; instruction: output port
+        --- OpticalCircuit intent compilation generates following flow rules
+        CLIENT PORT at INGRESS node -- criteria: input port (OduClt); instruction output port (Och)
+        CLIENT PORT at EGRESS node -- criteria: input port (Och); instruction output port (OduClt)*/
+
+        checkArgument((criteria.size() == NUM_CRITERIA_LINE_EGRESS_RULE) ||
+                (criteria.size() == NUM_CRITERIA_LINE_INGRESS_RULE) ||
+                (criteria.size() == NUM_CRITERIA_CLIENT_RULES),
+                "Wrong size of flow rule criteria for TerminalDevice size" + criteria.size());
+
+        checkArgument((instructions.size() == NUM_INSTRUCTIONS_LINE_EGRESS_RULE) ||
+                        (instructions.size() == NUM_INSTRUCTIONS_LINE_INGRESS_RULE) ||
+                        (instructions.size() == NUM_INSTRUCTIONS_CLIENT_RULES),
+                "Wrong size of flow rule instructions for TerminalDevice size " + instructions.size());
+
+        //This is EGRESS rule on the LINE side
+        if ((criteria.size() == NUM_CRITERIA_LINE_EGRESS_RULE) &&
+                (instructions.size() == NUM_INSTRUCTIONS_LINE_EGRESS_RULE)) {
+            log.debug("Building the TerminalDeviceFlowRule for LINE_EGRESS");
+            type = Type.LINE_EGRESS;
+
+            criteria.forEach(
+                    c -> checkArgument(c instanceof OchSignalCriterion ||
+                                    c instanceof OchSignalTypeCriterion ||
+                                    c instanceof PortCriterion,
+                            "Incompatible flow rule criteria for ADD TerminalDevice: " + criteria
+                    )
+            );
+            instructions.forEach(
+                    c -> checkArgument(c instanceof Instructions.OutputInstruction,
+                            "Incompatible flow rule instruction for ADD TerminalDevice: " + instructions
+                    )
+            );
+
+            ochSignal = criteria.stream()
+                    .filter(c -> c instanceof OchSignalCriterion)
+                    .map(c -> ((OchSignalCriterion) c).lambda())
+                    .findAny()
+                    .orElse(null);
+
+            inPortNumber = criteria.stream()
+                    .filter(c -> c instanceof PortCriterion)
+                    .map(c -> ((PortCriterion) c).port())
+                    .findAny()
+                    .orElse(null);
+
+            outPortNumber = ((Instructions.OutputInstruction) instructions.get(0)).port();
+
+            checkArgument(linePorts.contains(outPortNumber),
+                    "Incompatible output port for DROP TerminalDevice");
+
+        }
+
+        //This is INGRESS rule on the LINE side
+        if ((criteria.size() == NUM_CRITERIA_LINE_INGRESS_RULE) &&
+                (instructions.size() == NUM_INSTRUCTIONS_LINE_INGRESS_RULE)) {
+            log.debug("Building the TerminalDeviceFlowRule LINE_INGRESS");
+            type = Type.LINE_INGRESS;
+
+            criteria.forEach(
+                    c -> checkArgument(
+                            c instanceof PortCriterion,
+                            "Incompatible flow rule criteria for ADD TerminalDevice: " + criteria
+                    )
+            );
+            instructions.forEach(
+                    c -> checkArgument(c.type() == Instruction.Type.L0MODIFICATION ||
+                                    c.type() == Instruction.Type.OUTPUT,
+                            "Incompatible flow rule instruction for ADD TerminalDevice: " + instructions
+                    )
+            );
+
+            inPortNumber = criteria.stream()
+                    .filter(c -> c instanceof PortCriterion)
+                    .map(c -> ((PortCriterion) c).port())
+                    .findAny()
+                    .orElse(null);
+
+            checkArgument(linePorts.contains(inPortNumber),
+                    "Incompatible input port for DROP TerminalDevice");
+
+            ochSignal = instructions.stream()
+                    .filter(c -> c.type() == Instruction.Type.L0MODIFICATION)
+                    .map(c -> ((L0ModificationInstruction.ModOchSignalInstruction) c).lambda())
+                    .findAny()
+                    .orElse(null);
+
+            outPortNumber = instructions.stream()
+                    .filter(c -> c.type() == Instruction.Type.OUTPUT)
+                    .map(c -> ((Instructions.OutputInstruction) c).port())
+                    .findAny()
+                    .orElse(null);
+        }
+
+        //This is INGRESS or EGRESS rule on the CLIENT side
+        if ((criteria.size() == NUM_CRITERIA_CLIENT_RULES) &&
+                (instructions.size() == NUM_INSTRUCTIONS_CLIENT_RULES)) {
+
+            criteria.forEach(
+                    c -> checkArgument(
+                            c instanceof PortCriterion,
+                            "Incompatible flow rule criteria for ADD TerminalDevice: " + criteria
+                    )
+            );
+            instructions.forEach(
+                    c -> checkArgument(c.type() == Instruction.Type.OUTPUT,
+                            "Incompatible flow rule instruction for ADD TerminalDevice: " + instructions
+                    )
+            );
+
+            inPortNumber = criteria.stream()
+                    .filter(c -> c instanceof PortCriterion)
+                    .map(c -> ((PortCriterion) c).port())
+                    .findAny()
+                    .orElse(null);
+
+            outPortNumber = instructions.stream()
+                    .filter(c -> c.type() == Instruction.Type.OUTPUT)
+                    .map(c -> ((Instructions.OutputInstruction) c).port())
+                    .findAny()
+                    .orElse(null);
+
+            ochSignal = null;
+
+            if (linePorts.contains(outPortNumber)) {
+                type = Type.CLIENT_INGRESS;
+            } else {
+                type = Type.CLIENT_EGRESS;
+            }
+        }
+
+        if (type == Type.LINE_EGRESS) {
+            connectionName = "LineEgress-LinePort-" + inPortNumber.toString()
+                    + "-ochSig-" + ochSignal.centralFrequency().toString();
+        }
+        if (type == Type.LINE_INGRESS) {
+            connectionName = "LineIngress-LinePort-" + outPortNumber.toString()
+                    + "-ochSig-" + ochSignal.centralFrequency().toString();
+        }
+        if (type == Type.CLIENT_EGRESS) {
+            connectionName = "ClientEgress-LinePort-" + inPortNumber.toString()
+                    + "-ClientPort-" + outPortNumber.toString();
+        }
+        if (type == Type.CLIENT_INGRESS) {
+            connectionName = "ClientIngress-ClientPort-" + inPortNumber.toString()
+                    + "-LinePort-" + outPortNumber.toString();
+        }
+
+        log.info("TerminalFlowRule built with name {}", connectionName);
+    }
+
+    public PortNumber inPort() {
+        return inPortNumber;
+    }
+
+    public PortNumber outPort() {
+        return outPortNumber;
+    }
+
+    public OchSignal ochSignal() {
+        return ochSignal;
+    }
+
+    public String connectionName() {
+        return connectionName;
+    }
+}
+