Add mininet dependency files for stratum

Change-Id: I4b5d79c2a2267a7df59c2cb351a78b21032d5529
diff --git a/TestON/tests/USECASE/SegmentRouting/SRBridging/SRBridging.params b/TestON/tests/USECASE/SegmentRouting/SRBridging/SRBridging.params
index 7a2e9fa..460b769 100644
--- a/TestON/tests/USECASE/SegmentRouting/SRBridging/SRBridging.params
+++ b/TestON/tests/USECASE/SegmentRouting/SRBridging/SRBridging.params
@@ -19,7 +19,7 @@
         <bmv2SwitchType>bmv2</bmv2SwitchType>
         <stratumRoot>~/stratum</stratumRoot>
         <topology>trellis_fabric.py</topology>
-        <lib>routinglib.py,trellislib.py</lib>
+        <lib>routinglib.py,trellislib.py,stratum.py</lib>
         <conf>bgpdbgp1.conf,bgpdbgp2.conf,bgpdr1.conf,bgpdr2.conf,dhcpd6.conf,dhcpd.conf,zebradbgp1.conf,zebradbgp2.conf</conf>
         <trellisOar>/home/sdn/segmentrouting-oar-3.0.0-SNAPSHOT.oar</trellisOar>
         <t3Oar>/home/sdn/t3-app-3.0.0-SNAPSHOT.oar</t3Oar>
diff --git a/TestON/tests/USECASE/SegmentRouting/SRBridging/SRBridging.params.stratum b/TestON/tests/USECASE/SegmentRouting/SRBridging/SRBridging.params.stratum
index c7d0305..9219001 100644
--- a/TestON/tests/USECASE/SegmentRouting/SRBridging/SRBridging.params.stratum
+++ b/TestON/tests/USECASE/SegmentRouting/SRBridging/SRBridging.params.stratum
@@ -21,7 +21,7 @@
         <switchPrefix>bmv2</switchPrefix>
         <stratumRoot>~/stratum</stratumRoot>
         <topology>trellis_fabric.py</topology>
-        <lib>routinglib.py,trellislib.py</lib>
+        <lib>routinglib.py,trellislib.py,stratum.py</lib>
         <conf>bgpdbgp1.conf,bgpdbgp2.conf,bgpdr1.conf,bgpdr2.conf,dhcpd6.conf,dhcpd.conf,zebradbgp1.conf,zebradbgp2.conf</conf>
         <trellisOar>/home/sdn/segmentrouting-oar-3.0.0-SNAPSHOT.oar</trellisOar>
         <t3Oar>/home/sdn/t3-app-3.0.0-SNAPSHOT.oar</t3Oar>
diff --git a/TestON/tests/USECASE/SegmentRouting/SRBridging/SRBridging.params.tofino b/TestON/tests/USECASE/SegmentRouting/SRBridging/SRBridging.params.tofino
index 633f3c2..72a840e 100644
--- a/TestON/tests/USECASE/SegmentRouting/SRBridging/SRBridging.params.tofino
+++ b/TestON/tests/USECASE/SegmentRouting/SRBridging/SRBridging.params.tofino
@@ -20,7 +20,7 @@
         <switchPrefix>tofino</switchPrefix>
         <stratumRoot>~/stratum</stratumRoot>
         <topology>trellis_fabric.py</topology>
-        <lib>routinglib.py,trellislib.py</lib>
+        <lib>routinglib.py,trellislib.py,stratum.py</lib>
         <trellisOar>/home/sdn/segmentrouting-oar-3.0.0-SNAPSHOT.oar</trellisOar>
         <t3Oar>/home/sdn/t3-app-3.0.0-SNAPSHOT.oar</t3Oar>
     </DEPENDENCY>
diff --git a/TestON/tests/USECASE/SegmentRouting/SRDhcprelay/SRDhcprelay.params b/TestON/tests/USECASE/SegmentRouting/SRDhcprelay/SRDhcprelay.params
index 0cddcd8..e3282f3 100644
--- a/TestON/tests/USECASE/SegmentRouting/SRDhcprelay/SRDhcprelay.params
+++ b/TestON/tests/USECASE/SegmentRouting/SRDhcprelay/SRDhcprelay.params
@@ -16,7 +16,7 @@
         <useCommonTopo>True</useCommonTopo>
         <useBmv2>False</useBmv2>
         <topology>trellis_fabric.py</topology>
-        <lib>routinglib.py,trellislib.py</lib>
+        <lib>routinglib.py,trellislib.py,stratum.py</lib>
         <conf>dhcpd.conf,dhcpd6.conf,bgpdr1.conf,bgpdr2.conf,bgpdbgp1.conf,zebradbgp1.conf,bgpdbgp2.conf,zebradbgp2.conf</conf>
         <trellisOar>/home/sdn/segmentrouting-oar-3.0.0-SNAPSHOT.oar</trellisOar>
         <t3Oar>/home/sdn/t3-app-3.0.0-SNAPSHOT.oar</t3Oar>
diff --git a/TestON/tests/USECASE/SegmentRouting/SRDhcprelay/SRDhcprelay.params.stratum b/TestON/tests/USECASE/SegmentRouting/SRDhcprelay/SRDhcprelay.params.stratum
index b285c8f..6f12598 100644
--- a/TestON/tests/USECASE/SegmentRouting/SRDhcprelay/SRDhcprelay.params.stratum
+++ b/TestON/tests/USECASE/SegmentRouting/SRDhcprelay/SRDhcprelay.params.stratum
@@ -19,7 +19,7 @@
         <bmv2SwitchType>stratum</bmv2SwitchType>
         <stratumRoot>~/stratum</stratumRoot>
         <topology>trellis_fabric.py</topology>
-        <lib>routinglib.py,trellislib.py</lib>
+        <lib>routinglib.py,trellislib.py,stratum.py</lib>
         <conf>dhcpd.conf,dhcpd6.conf,bgpdr1.conf,bgpdr2.conf,bgpdbgp1.conf,zebradbgp1.conf,bgpdbgp2.conf,zebradbgp2.conf</conf>
         <trellisOar>/home/sdn/segmentrouting-oar-3.0.0-SNAPSHOT.oar</trellisOar>
         <t3Oar>/home/sdn/t3-app-3.0.0-SNAPSHOT.oar</t3Oar>
diff --git a/TestON/tests/USECASE/SegmentRouting/SRDynamicConf/SRDynamicConf.params b/TestON/tests/USECASE/SegmentRouting/SRDynamicConf/SRDynamicConf.params
index 89885f3..7a04891 100644
--- a/TestON/tests/USECASE/SegmentRouting/SRDynamicConf/SRDynamicConf.params
+++ b/TestON/tests/USECASE/SegmentRouting/SRDynamicConf/SRDynamicConf.params
@@ -16,7 +16,7 @@
         <useCommonTopo>True</useCommonTopo>
         <useBmv2>False</useBmv2>
         <topology>trellis_fabric.py</topology>
-        <lib>routinglib.py,trellislib.py</lib>
+        <lib>routinglib.py,trellislib.py,stratum.py</lib>
         <trellisOar>/home/sdn/segmentrouting-oar-3.0.0-SNAPSHOT.oar</trellisOar>
         <t3Oar>/home/sdn/t3-app-3.0.0-SNAPSHOT.oar</t3Oar>
     </DEPENDENCY>
diff --git a/TestON/tests/USECASE/SegmentRouting/SRMulticast/SRMulticast.params b/TestON/tests/USECASE/SegmentRouting/SRMulticast/SRMulticast.params
index 1fb91da..3619459 100644
--- a/TestON/tests/USECASE/SegmentRouting/SRMulticast/SRMulticast.params
+++ b/TestON/tests/USECASE/SegmentRouting/SRMulticast/SRMulticast.params
@@ -16,7 +16,7 @@
         <useCommonTopo>True</useCommonTopo>
         <useBmv2>False</useBmv2>
         <topology>hagg_fabric.py</topology>
-        <lib>routinglib.py,trellislib.py</lib>
+        <lib>routinglib.py,trellislib.py,stratum.py</lib>
         <conf>bgpdbgp1.conf,bgpdbgp2.conf,bgpdr1.conf,bgpdr2.conf,dhcpd6.conf,dhcpd.conf,zebradbgp1.conf,zebradbgp2.conf</conf>
         <trellisOar>/home/sdn/segmentrouting-oar-3.0.0-SNAPSHOT.oar</trellisOar>
         <t3Oar>/home/sdn/t3-app-3.0.0-SNAPSHOT.oar</t3Oar>
diff --git a/TestON/tests/USECASE/SegmentRouting/SRMulticast/SRMulticast.params.flex b/TestON/tests/USECASE/SegmentRouting/SRMulticast/SRMulticast.params.flex
index 1902cff..06cf9a3 100644
--- a/TestON/tests/USECASE/SegmentRouting/SRMulticast/SRMulticast.params.flex
+++ b/TestON/tests/USECASE/SegmentRouting/SRMulticast/SRMulticast.params.flex
@@ -16,7 +16,7 @@
         <useCommonTopo>True</useCommonTopo>
         <confName>flex</confName>
         <topology>hagg_fabric.py</topology>
-        <lib>routinglib.py,trellislib.py</lib>
+        <lib>routinglib.py,trellislib.py,stratum.py</lib>
         <conf>bgpdbgp1.conf,bgpdbgp2.conf,bgpdr1.conf,bgpdr2.conf,dhcpd6.conf,dhcpd.conf,zebradbgp1.conf,zebradbgp2.conf</conf>
         <trellisOar>/home/sdn/segmentrouting-oar-3.0.0-SNAPSHOT.oar</trellisOar>
         <t3Oar>/home/sdn/t3-app-3.0.0-SNAPSHOT.oar</t3Oar>
diff --git a/TestON/tests/USECASE/SegmentRouting/SRRouting/SRRouting.params b/TestON/tests/USECASE/SegmentRouting/SRRouting/SRRouting.params
index 557f03d..8b8909e 100644
--- a/TestON/tests/USECASE/SegmentRouting/SRRouting/SRRouting.params
+++ b/TestON/tests/USECASE/SegmentRouting/SRRouting/SRRouting.params
@@ -16,7 +16,7 @@
         <useCommonTopo>True</useCommonTopo>
         <useBmv2>False</useBmv2>
         <topology>hagg_fabric.py</topology>
-        <lib>routinglib.py,trellislib.py,trellis_fabric.py</lib>
+        <lib>routinglib.py,trellislib.py,trellis_fabric.py,stratum.py</lib>
         <conf>bgpdbgp1.conf,bgpdbgp2.conf,bgpdr1.conf,bgpdr2.conf,dhcpd6.conf,dhcpd.conf,zebradbgp1.conf,zebradbgp2.conf</conf>
         <trellisOar>/home/sdn/segmentrouting-oar-3.0.0-SNAPSHOT.oar</trellisOar>
         <t3Oar>/home/sdn/t3-app-3.0.0-SNAPSHOT.oar</t3Oar>
diff --git a/TestON/tests/USECASE/SegmentRouting/SRRouting/SRRouting.params.flex b/TestON/tests/USECASE/SegmentRouting/SRRouting/SRRouting.params.flex
index 5351709..1eb4d61 100644
--- a/TestON/tests/USECASE/SegmentRouting/SRRouting/SRRouting.params.flex
+++ b/TestON/tests/USECASE/SegmentRouting/SRRouting/SRRouting.params.flex
@@ -16,7 +16,7 @@
         <useCommonTopo>True</useCommonTopo>
         <confName>flex</confName>
         <topology>hagg_fabric.py</topology>
-        <lib>routinglib.py,trellislib.py,trellis_fabric.py</lib>
+        <lib>routinglib.py,trellislib.py,trellis_fabric.py,stratum.py</lib>
         <conf>bgpdbgp1.conf,bgpdbgp2.conf,bgpdr1.conf,bgpdr2.conf,dhcpd6.conf,dhcpd.conf,zebradbgp1.conf,zebradbgp2.conf</conf>
         <trellisOar>/home/sdn/segmentrouting-oar-3.0.0-SNAPSHOT.oar</trellisOar>
         <t3Oar>/home/sdn/t3-app-3.0.0-SNAPSHOT.oar</t3Oar>
diff --git a/TestON/tests/USECASE/SegmentRouting/SRRouting/SRRouting.params.stratum b/TestON/tests/USECASE/SegmentRouting/SRRouting/SRRouting.params.stratum
index aff3842..38edc6f 100644
--- a/TestON/tests/USECASE/SegmentRouting/SRRouting/SRRouting.params.stratum
+++ b/TestON/tests/USECASE/SegmentRouting/SRRouting/SRRouting.params.stratum
@@ -19,7 +19,7 @@
         <bmv2SwitchType>stratum</bmv2SwitchType>
         <stratumRoot>~/stratum</stratumRoot>
         <topology>hagg_fabric.py</topology>
-        <lib>routinglib.py,trellislib.py,trellis_fabric.py</lib>
+        <lib>routinglib.py,trellislib.py,trellis_fabric.py,stratum.py</lib>
         <conf>bgpdbgp1.conf,bgpdbgp2.conf,bgpdr1.conf,bgpdr2.conf,dhcpd6.conf,dhcpd.conf,zebradbgp1.conf,zebradbgp2.conf</conf>
         <trellisOar>/home/sdn/segmentrouting-oar-3.0.0-SNAPSHOT.oar</trellisOar>
         <t3Oar>/home/sdn/t3-app-3.0.0-SNAPSHOT.oar</t3Oar>
diff --git a/TestON/tests/USECASE/SegmentRouting/dependencies/stratum.py b/TestON/tests/USECASE/SegmentRouting/dependencies/stratum.py
new file mode 100644
index 0000000..e6deb97
--- /dev/null
+++ b/TestON/tests/USECASE/SegmentRouting/dependencies/stratum.py
@@ -0,0 +1,285 @@
+# coding=utf-8
+# Copyright 2018-present Open Networking Foundation
+# SPDX-License-Identifier: Apache-2.0
+
+'''
+This module contains a switch class for Mininet: StratumBmv2Switch
+
+Prerequisites
+-------------
+1. Docker- mininet+stratum_bmv2 image:
+$ cd stratum
+$ docker build -t <some tag> -f tools/mininet/Dockerfile .
+
+Usage
+-----
+From withing the Docker container, you can run Mininet using the following:
+$ mn --custom /root/stratum.py --switch stratum-bmv2 --controller none
+
+Advanced Usage
+--------------
+You can use this class in a Mininet topology script by including:
+
+from stratum import ONOSStratumBmv2Switch
+
+You will probably need to update your Python path. From within the Docker image:
+
+PYTHONPATH=$PYTHONPATH:/root ./<your script>.py
+
+Notes
+-----
+This code has been adapted from the ONOSBmv2Switch class defined in the ONOS project
+(tools/dev/mininet/bmv2.py).
+
+'''
+
+import json
+import multiprocessing
+import os
+import socket
+import threading
+import time
+
+from mininet.log import warn
+from mininet.node import Switch, Host
+
+DEFAULT_NODE_ID = 1
+DEFAULT_CPU_PORT = 255
+DEFAULT_PIPECONF = "org.onosproject.pipelines.basic"
+STRATUM_BMV2 = 'stratum_bmv2'
+STRATUM_INIT_PIPELINE = '/root/dummy.json'
+MAX_CONTROLLERS_PER_NODE = 10
+BMV2_LOG_LINES = 5
+
+
+def writeToFile(path, value):
+    with open(path, "w") as f:
+        f.write(str(value))
+
+
+def pickUnusedPort():
+    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+    s.bind(('localhost', 0))
+    addr, port = s.getsockname()
+    s.close()
+    return port
+
+
+def watchdog(sw):
+    try:
+        writeToFile(sw.keepaliveFile,
+                    "Remove this file to terminate %s" % sw.name)
+        while True:
+            if StratumBmv2Switch.mininet_exception == 1 \
+                    or not os.path.isfile(sw.keepaliveFile):
+                sw.stop()
+                return
+            if sw.stopped:
+                return
+            if sw.bmv2popen.poll() is None:
+                # All good, no return code, still running.
+                time.sleep(1)
+            else:
+                warn("\n*** WARN: switch %s died ☠️ \n" % sw.name)
+                sw.printLog()
+                print("-" * 80) + "\n"
+                # Close log file, set as stopped etc.
+                sw.stop()
+                return
+    except Exception as e:
+        warn("*** ERROR: " + e.message)
+        sw.stop()
+
+
+class StratumBmv2Switch(Switch):
+    # Shared value used to notify to all instances of this class that a Mininet
+    # exception occurred. Mininet exception handling doesn't call the stop()
+    # method, so the mn process would hang after clean-up since Bmv2 would still
+    # be running.
+    mininet_exception = multiprocessing.Value('i', 0)
+
+    nextGrpcPort = 50001
+
+    def __init__(self, name, json=STRATUM_INIT_PIPELINE, loglevel="warn",
+                 cpuport=DEFAULT_CPU_PORT, pipeconf=DEFAULT_PIPECONF,
+                 onosdevid=None,
+                 **kwargs):
+        Switch.__init__(self, name, **kwargs)
+        self.grpcPort = StratumBmv2Switch.nextGrpcPort
+        StratumBmv2Switch.nextGrpcPort += 1
+        self.cpuPort = cpuport
+        self.json = json
+        self.loglevel = loglevel
+        self.tmpDir = '/tmp/%s' % self.name
+        self.logfile = '%s/stratum_bmv2.log' % self.tmpDir
+        self.netcfgFile = '%s/onos-netcfg.json' % self.tmpDir
+        self.chassisConfigFile = '%s/chassis-config.txt' % self.tmpDir
+        self.pipeconfId = pipeconf
+        self.longitude = kwargs['longitude'] if 'longitude' in kwargs else None
+        self.latitude = kwargs['latitude'] if 'latitude' in kwargs else None
+        if onosdevid is not None and len(onosdevid) > 0:
+            self.onosDeviceId = onosdevid
+        else:
+            # The "device:" prefix is required by ONOS.
+            self.onosDeviceId = "device:%s" % self.name
+        self.nodeId = DEFAULT_NODE_ID
+        self.logfd = None
+        self.bmv2popen = None
+        self.stopped = True
+        # In case of exceptions, mininet removes *.out files from /tmp. We use
+        # this as a signal to terminate the switch instance (if active).
+        self.keepaliveFile = '/tmp/%s-watchdog.out' % self.name
+
+        # Remove files from previous executions
+        self.cleanupTmpFiles()
+        os.mkdir(self.tmpDir)
+
+    def getOnosNetcfg(self):
+        basicCfg = {
+            "managementAddress": "grpc://localhost:%d?device_id=%d" % (
+                self.grpcPort, self.nodeId),
+            "driver": "stratum-bmv2",
+            "pipeconf": self.pipeconfId
+        }
+
+        if self.longitude and self.latitude:
+            basicCfg["longitude"] = self.longitude
+            basicCfg["latitude"] = self.latitude
+
+        netcfg = {
+            "devices": {
+                self.onosDeviceId: {
+                    "basic": basicCfg
+                }
+            }
+        }
+
+        return netcfg
+
+    def getChassisConfig(self):
+        config = """description: "stratum_bmv2 {name}"
+chassis {{
+  platform: PLT_P4_SOFT_SWITCH
+  name: "{name}"
+}}
+nodes {{
+  id: {nodeId}
+  name: "{name} node {nodeId}"
+  slot: 1
+  index: 1
+}}\n""".format(name=self.name, nodeId=self.nodeId)
+
+        intf_number = 1
+        for intf_name in self.intfNames():
+            if intf_name == 'lo':
+                continue
+            config = config + """singleton_ports {{
+  id: {intfNumber}
+  name: "{intfName}"
+  slot: 1
+  port: {intfNumber}
+  channel: 1
+  speed_bps: 10000000000
+  config_params {{
+    admin_state: ADMIN_STATE_ENABLED
+  }}
+  node: {nodeId}
+}}\n""".format(intfName=intf_name, intfNumber=intf_number, nodeId=self.nodeId)
+            intf_number += 1
+
+        return config
+
+    def start(self, controllers):
+
+        if not self.stopped:
+            warn("*** %s is already running!\n" % self.name)
+            return
+
+        writeToFile("%s/grpc-port.txt" % self.tmpDir, self.grpcPort)
+        with open(self.chassisConfigFile, 'w') as fp:
+            fp.write(self.getChassisConfig())
+        with open(self.netcfgFile, 'w') as fp:
+            json.dump(self.getOnosNetcfg(), fp, indent=2)
+
+        args = [
+            STRATUM_BMV2,
+            '-device_id=%d' % self.nodeId,
+            '-chassis_config_file=%s' % self.chassisConfigFile,
+            '-forwarding_pipeline_configs_file=%s/pipe.txt' % self.tmpDir,
+            '-persistent_config_dir=%s' % self.tmpDir,
+            '-initial_pipeline=%s' % STRATUM_INIT_PIPELINE,
+            '-cpu_port=%s' % self.cpuPort,
+            '-external_stratum_urls=0.0.0.0:%d' % self.grpcPort,
+            '-local_stratum_url=localhost:%d' % pickUnusedPort(),
+            '-max_num_controllers_per_node=%d' % MAX_CONTROLLERS_PER_NODE,
+            '-write_req_log_file=%s/write-reqs.txt' % self.tmpDir,
+            '-logtostderr=true',
+            '-bmv2_log_level=%s' % self.loglevel,
+        ]
+
+        cmd_string = " ".join(args)
+
+        try:
+            # Write cmd_string to log for debugging.
+            self.logfd = open(self.logfile, "w")
+            self.logfd.write(cmd_string + "\n\n" + "-" * 80 + "\n\n")
+            self.logfd.flush()
+
+            self.bmv2popen = self.popen(cmd_string, stdout=self.logfd, stderr=self.logfd)
+            print "⚡️ %s @ %d" % (STRATUM_BMV2, self.grpcPort)
+
+            # We want to be notified if stratum_bmv2 quits prematurely...
+            self.stopped = False
+            threading.Thread(target=watchdog, args=[self]).start()
+
+        except Exception:
+            StratumBmv2Switch.mininet_exception = 1
+            self.stop()
+            self.printLog()
+            raise
+
+    def printLog(self):
+        if os.path.isfile(self.logfile):
+            print "-" * 80
+            print "%s log (from %s):" % (self.name, self.logfile)
+            with open(self.logfile, 'r') as f:
+                lines = f.readlines()
+                if len(lines) > BMV2_LOG_LINES:
+                    print "..."
+                for line in lines[-BMV2_LOG_LINES:]:
+                    print line.rstrip()
+
+    def cleanupTmpFiles(self):
+        self.cmd("rm -rf %s" % self.tmpDir)
+
+    def stop(self, deleteIntfs=True):
+        """Terminate switch."""
+        self.stopped = True
+        if self.bmv2popen is not None:
+            if self.bmv2popen.poll() is None:
+                self.bmv2popen.terminate()
+                self.bmv2popen.wait()
+            self.bmv2popen = None
+        if self.logfd is not None:
+            self.logfd.close()
+            self.logfd = None
+        Switch.stop(self, deleteIntfs)
+
+
+class NoOffloadHost(Host):
+    def __init__(self, name, inNamespace=True, **params):
+        Host.__init__(self, name, inNamespace=inNamespace, **params)
+
+    def config(self, **params):
+        r = super(Host, self).config(**params)
+        for off in ["rx", "tx", "sg"]:
+            cmd = "/sbin/ethtool --offload %s %s off" \
+                  % (self.defaultIntf(), off)
+            self.cmd(cmd)
+        return r
+
+
+# Exports for bin/mn
+switches = {'stratum-bmv2': StratumBmv2Switch}
+
+hosts = {'no-offload-host': NoOffloadHost}