[SDFAB-644] QoS test for with non-mobile traffic classification

Change-Id: I69e4617fbee1628b3427f2f8be60dec2c8aa95e8
diff --git a/TestON/drivers/common/api/controller/onosrestdriver.py b/TestON/drivers/common/api/controller/onosrestdriver.py
index 7027408..ec90a6d 100755
--- a/TestON/drivers/common/api/controller/onosrestdriver.py
+++ b/TestON/drivers/common/api/controller/onosrestdriver.py
@@ -2393,3 +2393,226 @@
         except Exception:
             main.log.exception( self.name + ": Uncaught exception!" )
             main.cleanAndExit()
+
+    def getSlices( self, ip="DEFAULT", port="DEFAULT", debug=False ):
+        try:
+            output = None
+            if ip == "DEFAULT":
+                main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
+                ip = self.ip_address
+            if port == "DEFAULT":
+                main.log.warn( self.name + ": No port given, reverting to port " +
+                               "from topo file" )
+                port = self.port
+            url = "/fabrictna/slicing/slice"
+            response = self.send( url=url, ip = ip, port = port, base="/onos" )
+            if response:
+                if 200 <= response[ 0 ] <= 299:
+                    output = response[ 1 ]
+                    if debug:
+                        main.log.debug(self.name + ": read: " + output)
+                    slices = json.loads( output ).get( 'Slices' )
+                    assert slices is not None, "Error parsing json object"
+                    return json.dumps( slices )
+                else:
+                    main.log.error( "Error with REST request, response was: %s: %s" %
+                                    ( response[ 0 ], response[ 1 ] ) )
+                    return main.FALSE
+        except ( AttributeError, AssertionError, TypeError ):
+            main.log.exception( self.name + ": Object not as expected" )
+            return None
+        except Exception:
+            main.log.exception( self.name + ": Uncaught exception!" )
+            main.cleanAndExit()
+
+    def addSlice( self, slice_id, ip="DEFAULT", port="DEFAULT", debug=False ):
+        self.__slices( slice_id, ip, port, debug, method="POST" )
+
+    def removeSlice( self, slice_id, ip="DEFAULT", port="DEFAULT", debug=False ):
+        self.__slices( slice_id, ip, port, debug, method="DELETE" )
+
+    def getTrafficClasses( self, slice_id, ip="DEFAULT", port="DEFAULT", debug=False ):
+        try:
+            if ip == "DEFAULT":
+                main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
+                ip = self.ip_address
+            if port == "DEFAULT":
+                main.log.warn( self.name + ": No port given, reverting to port " +
+                               "from topo file" )
+                port = self.port
+            url = "/fabrictna/slicing/tc/%d" % slice_id
+            response = self.send( url=url, ip = ip, port = port, base="/onos" )
+            if response:
+                if 200 <= response[ 0 ] <= 299:
+                    output = response[ 1 ]
+                    if debug:
+                        main.log.debug(self.name + ": read: " + output)
+                    traffic_classes = json.loads( output ).get( 'TrafficClasses' )
+                    assert traffic_classes is not None, "Error parsing json object"
+                    return json.dumps( traffic_classes )
+                else:
+                    main.log.error( "Error with REST request, response was: %s: %s" %
+                                    ( response[ 0 ], response[ 1 ] ) )
+                    return main.FALSE
+        except ( AttributeError, AssertionError, TypeError ):
+            main.log.exception( self.name + ": Object not as expected" )
+            return None
+        except Exception:
+            main.log.exception( self.name + ": Uncaught exception!" )
+            main.cleanAndExit()
+
+    def addTrafficClassToSlice( self, slice_id, traffic_class,
+                                ip="DEFAULT", port="DEFAULT", debug=False ):
+        self.__trafficClass( slice_id, traffic_class, ip, port, debug, method="POST" )
+
+    def removeTrafficClassToSlice( self, slice_id, traffic_class,
+                                   ip="DEFAULT", port="DEFAULT", debug=False ):
+        self.__trafficClass( slice_id, traffic_class, ip, port, debug, method="DELETE" )
+
+    def addSlicingClassifierFlow( self, slice_id, traffic_class, traffic_selector,
+                                  ip="DEFAULT", port="DEFAULT", debug=False ):
+        self.__slicingClassifierFlow( slice_id, traffic_class, traffic_selector,
+                                     ip, port, debug, method="POST" )
+
+    def removeSlicingClassifierFlow( self, slice_id, traffic_class, traffic_selector,
+                                     ip="DEFAULT", port="DEFAULT", debug=False ):
+        self.__slicingClassifierFlow( slice_id, traffic_class, traffic_selector,
+                                     ip, port, debug, method="DELETE" )
+
+    def getSlicingClassifierFlow( self, slice_id, traffic_class, ip="DEFAULT",
+                                  port="DEFAULT", debug=False ):
+        try:
+            if ip == "DEFAULT":
+                main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
+                ip = self.ip_address
+            if port == "DEFAULT":
+                main.log.warn( self.name + ": No port given, reverting to port " +
+                               "from topo file" )
+                port = self.port
+            url = "/fabrictna/slicing/flow/%d/%s" % ( slice_id, traffic_class )
+            response = self.send( url=url, ip = ip, port = port, base="/onos" )
+            if response:
+                if 200 <= response[ 0 ] <= 299:
+                    output = response[ 1 ]
+                    if debug:
+                        main.log.debug(self.name + ": read: " + output)
+                    traffic_selector = json.loads( output ).get( 'TrafficSelector' )
+                    assert traffic_selector is not None, "Error parsing json object"
+                    return json.dumps( traffic_selector )
+                else:
+                    main.log.error( "Error with REST request, response was: %s: %s" %
+                                    ( response[ 0 ], response[ 1 ] ) )
+                    return main.FALSE
+        except ( AttributeError, AssertionError, TypeError ):
+            main.log.exception( self.name + ": Object not as expected" )
+            return None
+        except Exception:
+            main.log.exception( self.name + ": Uncaught exception!" )
+            main.cleanAndExit()
+
+    def __slices( self, slice_id, ip="DEFAULT", port="DEFAULT", debug=False,
+                  method="POST" ):
+        try:
+            if debug:
+                main.log.debug( self.name + ": %s Slice" % method )
+                main.log.debug( self.name + ": Slice ID: %d" % slice_id )
+            if ip == "DEFAULT":
+                main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
+                ip = self.ip_address
+            if port == "DEFAULT":
+                main.log.warn( self.name + ": No port given, reverting to port " +
+                               "from topo file" )
+                port = self.port
+            url = "/fabrictna/slicing/slice/%d" % slice_id
+            response = self.send( method=method,
+                                  url=url, ip = ip, port = port,
+                                  base="/onos", data = {} )
+            if response:
+                if "200" in str( response[ 0 ] ):
+                    main.log.info( self.name + ": Successfully %s Slice ID: %d " % ( method, slice_id ) )
+                    return main.TRUE
+                else:
+                    main.log.error( "Error with REST request, response was: %s: %s" %
+                                    ( response[ 0 ], response[ 1 ] ) )
+                    return main.FALSE
+        except NotImplementedError as e:
+            raise # Inform the caller
+        except ( AttributeError, TypeError ):
+            main.log.exception( self.name + ": Object not as expected" )
+            return None
+        except Exception:
+            main.log.exception( self.name + ": Uncaught exception!" )
+            main.cleanAndExit()
+
+    def __trafficClass( self, slice_id, traffic_class, ip="DEFAULT", port="DEFAULT",
+                        debug=False, method="POST" ):
+        try:
+            if debug:
+                main.log.debug( self.name + ": %s Traffic Class" % method )
+                main.log.debug( self.name + ": Slice ID: %d, Traffic Class: %s" % ( slice_id, traffic_class ) )
+            if ip == "DEFAULT":
+                main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
+                ip = self.ip_address
+            if port == "DEFAULT":
+                main.log.warn( self.name + ": No port given, reverting to port " +
+                               "from topo file" )
+                port = self.port
+            url = "/fabrictna/slicing/tc/%d/%s" % ( slice_id, traffic_class )
+            response = self.send( method=method,
+                                  url=url, ip = ip, port = port,
+                                  base="/onos", data = {} )
+            if response:
+                if "200" in str( response[ 0 ] ):
+                    main.log.info( self.name + ": Successfully %s " % method +
+                                   "Slice ID: %d, Traffic Class: %s" % ( slice_id, traffic_class ) )
+                    return main.TRUE
+                else:
+                    main.log.error( "Error with REST request, response was: %s: %s" %
+                                    ( response[ 0 ], response[ 1 ] ) )
+                    return main.FALSE
+        except NotImplementedError as e:
+            raise # Inform the caller
+        except ( AttributeError, TypeError ):
+            main.log.exception( self.name + ": Object not as expected" )
+            return None
+        except Exception:
+            main.log.exception( self.name + ": Uncaught exception!" )
+            main.cleanAndExit()
+
+    def __slicingClassifierFlow( self, slice_id, traffic_class, traffic_selector,
+                                 ip="DEFAULT", port="DEFAULT", debug=False,
+                                 method="POST" ):
+        try:
+            if debug:
+                main.log.debug( self.name + ": %s Slicing Classifier Flow" % method )
+                main.log.debug( self.name + ": Slice ID: %d, Traffic Class: %s, Traffic Selector: %s" % (
+                    slice_id, traffic_class, self.pprint( traffic_selector ) ) )
+            if ip == "DEFAULT":
+                main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
+                ip = self.ip_address
+            if port == "DEFAULT":
+                main.log.warn( self.name + ": No port given, reverting to port " +
+                               "from topo file" )
+                port = self.port
+            url = "/fabrictna/slicing/flow/%d/%s" % ( slice_id, traffic_class )
+            response = self.send( method=method,
+                                  url=url, ip = ip, port = port,
+                                  data=json.dumps( traffic_selector ),
+                                  base="/onos" )
+            if response:
+                if "200" in str( response[ 0 ] ):
+                    main.log.info( self.name + ": Successfully %s Slicing Classifier Flow for " % method +
+                                   "Slice ID: %d, Traffic Class: %s" % ( slice_id, traffic_class ) )
+                    return main.TRUE
+                else:
+                    main.log.error( "Error with REST request, response was: %s: %s" %
+                                    ( response[ 0 ], response[ 1 ] ) )
+                    return main.FALSE
+        except NotImplementedError as e:
+            raise # Inform the caller
+        except ( AttributeError, TypeError ):
+            main.log.exception( self.name + ": Object not as expected" )
+            return None
+        except Exception:
+            main.log.exception( self.name + ": Uncaught exception!" )
+            main.cleanAndExit()
diff --git a/TestON/tests/USECASE/SegmentRouting/QOSNonMobile/QOSNonMobile.params b/TestON/tests/USECASE/SegmentRouting/QOSNonMobile/QOSNonMobile.params
new file mode 100644
index 0000000..8ef7e1b
--- /dev/null
+++ b/TestON/tests/USECASE/SegmentRouting/QOSNonMobile/QOSNonMobile.params
@@ -0,0 +1,175 @@
+<PARAMS>
+    <testcases>1,2</testcases>
+
+    <GRAPH>
+        <nodeCluster>pairedleaves</nodeCluster>
+        <builds>20</builds>
+        <jobName>QOS</jobName>
+        <branch>master</branch>
+    </GRAPH>
+
+    <persistent_setup>True</persistent_setup>
+
+    <kubernetes>
+        <appName>onos-classic</appName>
+        <namespace>tost</namespace>
+    </kubernetes>
+
+    <SLICING>
+        <slices>
+            <slice1>
+                <slice_id>1</slice_id>
+                <traffic_classes>BEST_EFFORT,REAL_TIME</traffic_classes>
+            </slice1>
+        </slices>
+        <traffic_classification>
+            <slice_1_be>
+                <slice_id>1</slice_id>
+                <traffic_class>BEST_EFFORT</traffic_class>
+                <traffic_selector>
+                    <criteria1>
+                        <type>UDP_DST</type>
+                        <udpPort>100</udpPort>
+                    </criteria1>
+                </traffic_selector>
+            </slice_1_be>
+            <slice_1_rt>
+                <slice_id>1</slice_id>
+                <traffic_class>REAL_TIME</traffic_class>
+                <traffic_selector>
+                    <criteria1>
+                        <type>UDP_DST</type>
+                        <udpPort>200</udpPort>
+                    </criteria1>
+                </traffic_selector>
+            </slice_1_rt>
+        </traffic_classification>
+    </SLICING>
+
+    <TREX>
+        <port_stats>0,2,3</port_stats>
+        <flows>
+            <BE_TO_ENB>
+                <name>Best Effort</name>
+                <l1_bps>2000000000</l1_bps>
+                <trex_port>0</trex_port>
+                <packet>
+                    <pktlen>1400</pktlen>
+                    <ip_src>10.32.11.125</ip_src>
+                    <ip_dst>10.32.11.124</ip_dst>
+                    <eth_src>40:A6:B7:22:AB:40</eth_src>
+                    <eth_dst>00:00:0A:4C:1C:46</eth_dst>
+                    <udp_dport>100</udp_dport>
+                </packet>
+            </BE_TO_ENB>
+            <RT_TO_ENB>
+                <name>Real Time</name>
+                <l1_bps>24000000</l1_bps> <!-- Smaller than the smaller RT max BW -->
+                <trex_port>0</trex_port>
+                <packet>
+                    <pktlen>1400</pktlen>
+                    <ip_src>10.32.11.125</ip_src>
+                    <ip_dst>10.32.11.124</ip_dst>
+                    <eth_src>40:A6:B7:22:AB:40</eth_src>
+                    <eth_dst>00:00:0A:4C:1C:46</eth_dst>
+                    <udp_dport>200</udp_dport>
+                </packet>
+                <latency_stats>true</latency_stats>
+                <flow_id>10</flow_id> <!-- Mandatory when latency_stats=true -->
+                <delay>50000</delay> <!-- wait 50 ms till start to let queues fill up -->
+                <expected_min_received>1</expected_min_received>
+                <expected_max_dropped>0</expected_max_dropped>
+                <expected_99_9_percentile_latency>100</expected_99_9_percentile_latency>
+            </RT_TO_ENB>
+            <BE_1_TO_PDN>
+                <name>Best Effort 1</name>
+                <l1_bps>40000000000</l1_bps>
+                <trex_port>2</trex_port>
+                <packet>
+                    <pktlen>1400</pktlen>
+                    <ip_src>10.32.11.124</ip_src>
+                    <ip_dst>10.32.11.125</ip_dst>
+                    <eth_src>40:A6:B7:22:AB:20</eth_src>
+                    <eth_dst>00:00:0A:4C:1C:46</eth_dst>
+                    <udp_dport>100</udp_dport>
+                </packet>
+            </BE_1_TO_PDN>
+            <BE_2_TO_PDN>
+                <name>Best Effort 2</name>
+                <l1_bps>25000000000</l1_bps>
+                <trex_port>3</trex_port>
+                <packet>
+                    <pktlen>1400</pktlen>
+                    <ip_src>10.32.11.123</ip_src>
+                    <ip_dst>10.32.11.125</ip_dst>
+                    <eth_src>40:A6:B7:22:AB:21</eth_src>
+                    <eth_dst>00:00:0A:4C:1C:46</eth_dst>
+                    <udp_dport>100</udp_dport>
+                </packet>
+            </BE_2_TO_PDN>
+            <RT_TO_PDN>
+                <name>Real Time</name>
+                <l1_bps>24000000</l1_bps> <!-- Smaller than the smaller RT max BW -->
+                <trex_port>3</trex_port>
+                <packet>
+                    <pktlen>1400</pktlen>
+                    <ip_src>10.32.11.123</ip_src>
+                    <ip_dst>10.32.11.125</ip_dst>
+                    <eth_src>40:A6:B7:22:AB:21</eth_src>
+                    <eth_dst>00:00:0A:4C:1C:46</eth_dst>
+                    <udp_dport>200</udp_dport>
+                </packet>
+                <latency_stats>true</latency_stats>
+                <flow_id>10</flow_id> <!-- Mandatory when latency_stats=true -->
+                <delay>50000</delay> <!-- wait 50 ms till start to let queues fill up -->
+                <expected_min_received>1</expected_min_received>
+                <expected_max_dropped>0</expected_max_dropped>
+                <expected_99_9_percentile_latency>100</expected_99_9_percentile_latency>
+            </RT_TO_PDN>
+        </flows>
+    </TREX>
+
+    <TOPO>
+        <switchNum>2</switchNum>
+        <linkNum>2</linkNum>
+    </TOPO>
+
+    <ONOS_Logging>
+        <org.onosproject.p4runtime.ctl.client>DEBUG</org.onosproject.p4runtime.ctl.client>
+        <org.onosproject.p4runtime.ctl.client.writerequestimpl>TRACE</org.onosproject.p4runtime.ctl.client.writerequestimpl>
+        <org.onosproject.segmentrouting>DEBUG</org.onosproject.segmentrouting>
+        <org.onosproject.gnmi.ctl>TRACE</org.onosproject.gnmi.ctl>
+        <org.omecproject.up4>TRACE</org.omecproject.up4>
+        <org.stratumproject.fabric.tna.slicing>TRACE</org.stratumproject.fabric.tna.slicing>
+    </ONOS_Logging>
+    <ONOS_Logging_Reset>
+        <org.onosproject.p4runtime.ctl.client>INFO</org.onosproject.p4runtime.ctl.client>
+        <org.onosproject.p4runtime.ctl.client.writerequestimpl>INFO</org.onosproject.p4runtime.ctl.client.writerequestimpl>
+        <org.onosproject.segmentrouting>DEBUG</org.onosproject.segmentrouting>
+        <org.onosproject.gnmi.ctl>INFO</org.onosproject.gnmi.ctl>
+        <org.omecproject.up4>INFO</org.omecproject.up4>
+        <org.stratumproject.fabric.tna.slicing>INFO</org.stratumproject.fabric.tna.slicing>
+    </ONOS_Logging_Reset>
+
+    <ENV>
+        <cellName>productionCell</cellName>
+        <cellApps>drivers,fpm,lldpprovider,hostprovider,netcfghostprovider,drivers.bmv2,org.opencord.fabric-tofino,pipelines.fabric,org.stratumproject.fabric-tna,drivers.barefoot,segmentrouting,t3,up4</cellApps>
+    </ENV>
+
+    <DEPENDENCY>
+        <useCommonConf>False</useCommonConf>
+        <useCommonTopo>True</useCommonTopo>
+        <useBmv2>True</useBmv2>
+        <bmv2SwitchType>stratum</bmv2SwitchType>
+        <switchPrefix></switchPrefix>
+        <stratumRoot>~/stratum</stratumRoot>
+        <topology>trellis_fabric.py</topology>
+        <lib></lib>
+    </DEPENDENCY>
+
+    <SCALE>
+        <size>3</size>
+        <max>3</max>
+    </SCALE>
+
+</PARAMS>
diff --git a/TestON/tests/USECASE/SegmentRouting/QOSNonMobile/QOSNonMobile.py b/TestON/tests/USECASE/SegmentRouting/QOSNonMobile/QOSNonMobile.py
new file mode 100644
index 0000000..6615a25
--- /dev/null
+++ b/TestON/tests/USECASE/SegmentRouting/QOSNonMobile/QOSNonMobile.py
@@ -0,0 +1,28 @@
+class QOSNonMobile:
+
+    def __init__(self):
+        self.default = ''
+
+    def CASE1(self):
+        main.case("Leaf Edge with NON-Mobile Traffic Classification")
+        try:
+            from tests.USECASE.SegmentRouting.QOSNonMobile.dependencies.QOSNonMobileTest import QOSNonMobileTest
+        except ImportError as e:
+            main.log.error("Import not found. Exiting the test")
+            main.log.error(e)
+            main.cleanAndExit()
+
+        test = QOSNonMobileTest()
+        test.runTest(main, test_idx=1, n_switches=2)
+
+    def CASE2(self):
+        main.case("Leaf Pair Link with NON-Mobile Traffic Classification")
+        try:
+            from tests.USECASE.SegmentRouting.QOSNonMobile.dependencies.QOSNonMobileTest import QOSNonMobileTest
+        except ImportError as e:
+            main.log.error("Import not found. Exiting the test")
+            main.log.error(e)
+            main.cleanAndExit()
+
+        test = QOSNonMobileTest()
+        test.runTest(main, test_idx=2, n_switches=2)
diff --git a/TestON/tests/USECASE/SegmentRouting/QOSNonMobile/QOSNonMobile.topo b/TestON/tests/USECASE/SegmentRouting/QOSNonMobile/QOSNonMobile.topo
new file mode 100644
index 0000000..377a168
--- /dev/null
+++ b/TestON/tests/USECASE/SegmentRouting/QOSNonMobile/QOSNonMobile.topo
@@ -0,0 +1,56 @@
+<TOPOLOGY>
+    <COMPONENT>
+        <ONOScell>
+            <host>localhost</host>  # ONOS "bench" machine
+            <user>jenkins</user>
+            <password></password>
+            <type>OnosClusterDriver</type>
+            <connect_order>50</connect_order>
+            <jump_host></jump_host>
+            <home>~/onos</home>   # defines where onos home is on the build machine. Defaults to "~/onos/" if empty.
+            <COMPONENTS>
+                <kubeConfig>~/.kube/dev-pairedleaves-tucson</kubeConfig>  # If set, will attempt to use this file for setting up port-forwarding
+                <useDocker>True</useDocker>  # Whether to use docker for ONOS nodes
+                <docker_prompt>\$</docker_prompt>
+                <cluster_name></cluster_name>  # Used as a prefix for cluster components. Defaults to 'ONOS'
+                <diff_clihost>True</diff_clihost> # if it has different host other than localhost for CLI. True or empty. OC# will be used if True.
+                <karaf_username>karaf</karaf_username>
+                <karaf_password>karaf</karaf_password>
+                <web_user>karaf</web_user>
+                <web_pass>karaf</web_pass>
+                <karafPrompt_username>karaf</karafPrompt_username>
+                <rest_port></rest_port>
+                <prompt></prompt>  # TODO: we technically need a few of these, one per component
+                <onos_home>~/onos/</onos_home>  # defines where onos home is on the target cell machine. Defaults to entry in "home" if empty.
+                <nodes> 3 </nodes>  # number of nodes in the cluster
+                <up4_port>51001</up4_port> # Port where the UP4 P4Runtime server is listening
+            </COMPONENTS>
+        </ONOScell>
+
+        <!-- No need for any HostDriver components, traffic is being generated by TRex-->
+        <TRexClient>
+            <host>localhost</host>
+            <type>TrexClientDriver</type>
+            <connect_order>5</connect_order>
+            <COMPONENTS>
+                <trex_address>10.76.28.72</trex_address> <!-- Compute2 -->
+                <trex_config>trex_config.yaml</trex_config> <!-- relative path starting from ./dependencies-->
+                <force_restart>True</force_restart>
+                <software_mode>False</software_mode>
+                <trex_library_python_path>/home/jenkins/trex_python</trex_library_python_path>
+            </COMPONENTS>
+        </TRexClient>
+
+    <!--  This component is not needed, but required to use the Testcaselib  -->
+        <NetworkBench>
+            <host>10.76.28.66</host>
+            <user>jenkins</user>
+            <password></password>
+            <type>NetworkDriver</type>
+            <connect_order>1</connect_order>
+            <COMPONENTS>
+            </COMPONENTS>
+        </NetworkBench>
+
+    </COMPONENT>
+</TOPOLOGY>
diff --git a/TestON/tests/USECASE/SegmentRouting/QOSNonMobile/__init__.py b/TestON/tests/USECASE/SegmentRouting/QOSNonMobile/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/TestON/tests/USECASE/SegmentRouting/QOSNonMobile/__init__.py
diff --git a/TestON/tests/USECASE/SegmentRouting/QOSNonMobile/dependencies/QOSNonMobileTest.py b/TestON/tests/USECASE/SegmentRouting/QOSNonMobile/dependencies/QOSNonMobileTest.py
new file mode 100644
index 0000000..9a52313
--- /dev/null
+++ b/TestON/tests/USECASE/SegmentRouting/QOSNonMobile/dependencies/QOSNonMobileTest.py
@@ -0,0 +1,190 @@
+from tests.USECASE.SegmentRouting.dependencies.Testcaselib import \
+    Testcaselib as run
+from tests.USECASE.SegmentRouting.dependencies.trex import Trex
+import json
+
+
+class QOSNonMobileTest:
+
+    def runTest(self, main, test_idx, n_switches):
+        run.initTest(main)
+        main.log.info(main.Cluster.numCtrls)
+        main.Cluster.setRunningNode(3)
+        run.installOnos(main, skipPackage=True, cliSleep=5)
+
+        # Use the first available ONOS instance CLI
+        onos_rest = main.Cluster.active(0).REST
+        onos_cli = main.Cluster.active(0).CLI
+
+        # Load traffic config for the current test case
+        cfgFile = "%s/tests/CASE_%d.json" % (main.configPath, test_idx)
+        with open(cfgFile) as cfg:
+            testCfg = json.load(cfg)
+
+        trex = Trex()
+        trex.setup(main.TRexClient)
+
+        original_flows_number = onos_cli.checkFlowCount()
+
+        main.step("Add and verify Slices and Traffic Classes")
+        added_slices = True
+        new_flows = 0  # one for every new TC in SLICE and 1 for every Flow Classifier
+        for slice_name in main.params["SLICING"]["slices"]:
+            slice = main.params["SLICING"]["slices"][slice_name]
+            if "slice_id" not in slice:
+                continue
+            slice_id = int(slice["slice_id"])
+            onos_rest.addSlice(slice_id=slice_id, debug=True)
+            slices_onos = onos_rest.getSlices(debug=True)
+            # Verify the slice has been added
+            added_slices = added_slices and \
+                           {"SliceId": slice_id} in json.loads(slices_onos)
+
+            tcs = []
+            for tc in slice.get("traffic_classes", "").split(","):
+                onos_rest.addTrafficClassToSlice(slice_id=slice_id,
+                                                 traffic_class=tc,
+                                                 debug=True)
+                tcs.append({"TrafficClass": tc})
+                new_flows += 1
+            tcs_onos = onos_rest.getTrafficClasses(slice_id=slice_id,
+                                                   debug=True)
+            # Verify the TC has been added to the slice
+            added_slices = added_slices and \
+                           sorted(json.loads(tcs_onos)) == sorted(tcs)
+        utilities.assert_equal(
+            expect=True,
+            actual=added_slices,
+            onpass="Slices and Traffic Classes installed in slicing service",
+            onfail="Error in installing Slices and Traffic Classes in slicing service"
+        )
+
+        main.step("Add and verify slicing traffic classifier")
+        flows_in_slicing = True
+        for slicing_cfg_name in main.params["SLICING"]["traffic_classification"]:
+            new_flows += 1
+            slicing_config = main.params["SLICING"]["traffic_classification"][
+                slicing_cfg_name]
+
+            traffic_selector = self.__cleanupTrafficSelector(slicing_config.get("traffic_selector", []))
+            onos_rest.addSlicingClassifierFlow(
+                slice_id=int(slicing_config.get("slice_id", "0")),
+                traffic_class=slicing_config.get("traffic_class",
+                                                 "BEST_EFFORT"),
+                traffic_selector=traffic_selector,
+                debug=True
+            )
+            # Verify classifier flows
+            onos_flows = json.loads(onos_rest.getSlicingClassifierFlow(
+                slice_id=int(slicing_config.get("slice_id", "0")),
+                traffic_class=slicing_config.get("traffic_class",
+                                                 "BEST_EFFORT"),
+                debug=True
+            ))
+            flows_in_slicing = flows_in_slicing and traffic_selector in onos_flows
+
+        utilities.assert_equal(
+            expect=True,
+            actual=flows_in_slicing,
+            onpass="Traffic Classifier Flows installed in slicing service",
+            onfail="Error in installing Classifier Flows in slicing service"
+        )
+
+        run.checkFlows(
+            main,
+            minFlowCount=original_flows_number + (new_flows * n_switches)
+        )
+
+        main.step("Send traffic with TRex")
+        for flow in testCfg["flows"]:
+            trex.createFlow(flow)
+        trex.sendAndReceiveTraffic(testCfg["duration"])
+
+        trex.logPortStats()
+        for flow in testCfg["flows"]:
+            trex.logFlowStats(flow)
+
+        # Assert Flow Stats
+        for flow in testCfg["flows"]:
+            if trex.isFlowStats(flow):
+                main.step("{}: Assert RX Packets".format(flow))
+                trex.assertRxPackets(flow)
+                main.step("{}: Assert Dropped Packets".format(flow))
+                trex.assertDroppedPacket(flow)
+                main.step("{}: Assert 99.9 Percentile Latency".format(flow))
+                trex.assert99_9PercentileLatency(flow)
+
+        main.step("Remove and verify slicing traffic classifier")
+        no_flows_in_slicing = True
+        for slicing_cfg_name in main.params["SLICING"]["traffic_classification"]:
+            slicing_config = main.params["SLICING"]["traffic_classification"][
+                slicing_cfg_name]
+
+            traffic_selector = self.__cleanupTrafficSelector(slicing_config.get("traffic_selector", []))
+            onos_rest.removeSlicingClassifierFlow(
+                slice_id=int(slicing_config.get("slice_id", "0")),
+                traffic_class=slicing_config.get("traffic_class",
+                                                 "BEST_EFFORT"),
+                traffic_selector=traffic_selector,
+                debug=True
+            )
+            flow = onos_rest.getSlicingClassifierFlow(
+                slice_id=int(slicing_config.get("slice_id", "0")),
+                traffic_class=slicing_config.get("traffic_class",
+                                                 "BEST_EFFORT"),
+                debug=True
+            )
+            no_flows_in_slicing = no_flows_in_slicing and flow == "[]"
+
+        utilities.assert_equal(
+            expect=True,
+            actual=no_flows_in_slicing,
+            onpass="Traffic Classifier Flows removed in slicing service",
+            onfail="Error in removing Classifier Flows in slicing service"
+        )
+
+        main.step("Remove and verify Slices and Traffic Classes")
+        removed_slices = []
+        for slice_name in main.params["SLICING"]["slices"]:
+            slice = main.params["SLICING"]["slices"][slice_name]
+            if "slice_id" not in slice:
+                continue
+            slice_id = int(slice["slice_id"])
+            for tc in slice.get("traffic_classes", "").split(","):
+                # BEST_EFFORT must be removed as last, or we can leave it,
+                # it will be removed when removing the slice
+                if tc != "BEST_EFFORT":
+                    onos_rest.removeTrafficClassToSlice(slice_id=slice_id,
+                                                        traffic_class=tc,
+                                                        debug=True)
+            # Do not try to remove the Default Slice!
+            if slice_id != 0:
+                onos_rest.removeSlice(slice_id=slice_id, debug=True)
+                removed_slices.append(slice_id)
+
+        slices_onos = json.loads(onos_rest.getSlices(debug=True))
+        utilities.assert_equal(
+            expect=True,
+            actual=not any([{"SliceId": slice_id} in slices_onos for slice_id in
+                            removed_slices]),
+            onpass="Slices and Traffic Classes removed from slicing service",
+            onfail="Error in removing Slices and Traffic Classes from slicing service"
+        )
+
+        run.checkFlows(main, minFlowCount=original_flows_number)
+
+        main.step("Teardown")
+        trex.teardown()
+        run.cleanup(main)
+
+    def __cleanupTrafficSelector(self, traffic_selector):
+        ts = {
+            "criteria": [traffic_selector[criteria] for criteria in
+                         traffic_selector]}
+        # Cleanup the traffic selector, by converting into integer the
+        # required fields, conversion is required for checking the result
+        # from ONOS
+        for criteria in ts["criteria"]:
+            if "udpPort" in criteria:
+                criteria["udpPort"] = int(criteria["udpPort"])
+        return ts
diff --git a/TestON/tests/USECASE/SegmentRouting/QOSNonMobile/dependencies/__init__.py b/TestON/tests/USECASE/SegmentRouting/QOSNonMobile/dependencies/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/TestON/tests/USECASE/SegmentRouting/QOSNonMobile/dependencies/__init__.py
diff --git a/TestON/tests/USECASE/SegmentRouting/QOSNonMobile/dependencies/tests/CASE_1.json b/TestON/tests/USECASE/SegmentRouting/QOSNonMobile/dependencies/tests/CASE_1.json
new file mode 100644
index 0000000..e1a540d
--- /dev/null
+++ b/TestON/tests/USECASE/SegmentRouting/QOSNonMobile/dependencies/tests/CASE_1.json
@@ -0,0 +1,5 @@
+{
+  "flows": ["BE_TO_ENB", "RT_TO_ENB"],
+  "duration": 10
+}
+
diff --git a/TestON/tests/USECASE/SegmentRouting/QOSNonMobile/dependencies/tests/CASE_2.json b/TestON/tests/USECASE/SegmentRouting/QOSNonMobile/dependencies/tests/CASE_2.json
new file mode 100644
index 0000000..bf312d3
--- /dev/null
+++ b/TestON/tests/USECASE/SegmentRouting/QOSNonMobile/dependencies/tests/CASE_2.json
@@ -0,0 +1,5 @@
+{
+  "flows": ["BE_1_TO_PDN","BE_2_TO_PDN", "RT_TO_PDN"],
+  "duration": 10
+}
+
diff --git a/TestON/tests/USECASE/SegmentRouting/QOSNonMobile/dependencies/trex_config.yaml b/TestON/tests/USECASE/SegmentRouting/QOSNonMobile/dependencies/trex_config.yaml
new file mode 100644
index 0000000..6fbeddb
--- /dev/null
+++ b/TestON/tests/USECASE/SegmentRouting/QOSNonMobile/dependencies/trex_config.yaml
@@ -0,0 +1,19 @@
+# TRex Port ID=0 --> PCI BUS: d8:00.0, Linux Intf: enp216s0f0 connected to leaf1/0 (PDN)
+# TRex Port ID=1 --> PCI BUS: d8:00.1, Linux Intf: enp216s0f1 not connected, but required by TRex to have an even number of interfaces
+# TRex Port ID=2 --> PCI BUS: 5e:00.0, Linux Intf: enp94s0f0 connected to leaf2/0 (eNodeB)
+# TRex Port ID=3 --> PCI BUS: 5e:00.1, Linux Intf: enp94s0f1 connected to leaf2/4
+
+- version: 2
+  port_limit: 4
+  interfaces: [ 'd8:00.0', 'd8:00.1', '5e:00.0', '5e:00.1']
+  port_bandwidth_gb: 40
+  c: 20
+  port_info:
+    - src_mac: 40:A6:B7:22:AB:40
+      dest_mac: 00:00:0A:4C:1C:46
+    - src_mac: 40:A6:B7:22:AB:41
+      dest_mac: 00:00:0A:4C:1C:46
+    - src_mac: 40:A6:B7:22:AB:20
+      dest_mac: 00:00:0A:4C:1C:46
+    - src_mac: 40:A6:B7:22:AB:21
+      dest_mac: 00:00:0A:4C:1C:46
diff --git a/TestON/tests/USECASE/SegmentRouting/dependencies/trex.py b/TestON/tests/USECASE/SegmentRouting/dependencies/trex.py
index 9b15811..d710d07 100644
--- a/TestON/tests/USECASE/SegmentRouting/dependencies/trex.py
+++ b/TestON/tests/USECASE/SegmentRouting/dependencies/trex.py
@@ -173,6 +173,10 @@
             packet["gtp_teid"] = int(packet["gtp_teid"])
         if "pktlen" in packet.keys():
             packet["pktlen"] = int(packet["pktlen"])
+        if "udp_dport" in packet.keys():
+            packet["udp_dport"] = int(packet["udp_dport"])
+        if "udp_sport" in packet.keys():
+            packet["udp_sport"] = int(packet["udp_sport"])
         return packet
 
     @staticmethod