Update QoS tests for netcfg-based logic

Slices are pre-provisioned via netcfg. Creating/removing them via REST
APIs is no longer supported, so the checks for the corresponding flow
insertions have been removed.

Also, to improve debugging, we make assertions more granular and logging
more detailed, e.g., we now check failures for individual flow
insertions and print the flow name.

Also 2, we rename all relevant instances of qfi to tc to avoid confusion.
UP4 now exposes only TCs. The mapping between QFI and TC is up to
PFCP-Agent.

Change-Id: If1dc65680bf3ad3bcc0091c1435c2553198c8ba6
diff --git a/TestON/tests/USECASE/SegmentRouting/QOS/QOS.params b/TestON/tests/USECASE/SegmentRouting/QOS/QOS.params
index 1861658..3bdbd04 100644
--- a/TestON/tests/USECASE/SegmentRouting/QOS/QOS.params
+++ b/TestON/tests/USECASE/SegmentRouting/QOS/QOS.params
@@ -32,7 +32,8 @@
                 <teid>100</teid>
                 <up_id>10</up_id>
                 <down_id>11</down_id>
-                <qfi>0</qfi> <!-- Best Effort -->
+                // TODO: we should pass also the slice_id
+                <tc>0</tc> <!-- Best Effort -->
                 <five_g>False</five_g>
             </ue1>
             <ue2>
@@ -40,7 +41,7 @@
                 <teid>200</teid>
                 <up_id>20</up_id>
                 <down_id>21</down_id>
-                <qfi>2</qfi> <!-- Real Time -->
+                <tc>2</tc> <!-- Real Time -->
                 <five_g>False</five_g>
             </ue2>
         </ues>
diff --git a/TestON/tests/USECASE/SegmentRouting/QOS/QOS.py b/TestON/tests/USECASE/SegmentRouting/QOS/QOS.py
index 45414f1..aa4d94f 100644
--- a/TestON/tests/USECASE/SegmentRouting/QOS/QOS.py
+++ b/TestON/tests/USECASE/SegmentRouting/QOS/QOS.py
@@ -6,7 +6,7 @@
     def CASE1(self, main):
         main.case("Leaf Edge with Mobile Traffic Classification")
         # Leaf-Edge-Mobile
-        # Attach 2 UEs with different QFI
+        # Attach 2 UEs with different TC
         # Generate traffic with Trex for the two UEs
         # --> no packet drop on RT flow, reasonable latency on RT flow
         try:
@@ -26,7 +26,7 @@
     def CASE2(self, main):
         main.case("Leaf Pair Link with Mobile Traffic Classification")
         # Leaf Infra Mobile Traffic
-        # Attach 2 UEs with different QFI
+        # Attach 2 UEs with different TC
         # Generate traffic with TRex from UEs towards PDN, generating congestion
         # on the 40Gbps pair link
         # --> no packet drop on RT flow, reasonable latency on RT flow
diff --git a/TestON/tests/USECASE/SegmentRouting/QOSNonMobile/QOSNonMobile.params b/TestON/tests/USECASE/SegmentRouting/QOSNonMobile/QOSNonMobile.params
index 6bd2069..56e859a 100644
--- a/TestON/tests/USECASE/SegmentRouting/QOSNonMobile/QOSNonMobile.params
+++ b/TestON/tests/USECASE/SegmentRouting/QOSNonMobile/QOSNonMobile.params
@@ -18,10 +18,23 @@
 
     <SLICING>
         <slices>
+            <!-- Should match slices defined in netcfg-->
+            <slice0>
+                <slice_id>0</slice_id>
+                <traffic_classes>BEST_EFFORT,REAL_TIME</traffic_classes>
+            </slice0>
             <slice1>
                 <slice_id>1</slice_id>
-                <traffic_classes>BEST_EFFORT,REAL_TIME</traffic_classes>
+                <traffic_classes>BEST_EFFORT,CONTROL,REAL_TIME,ELASTIC</traffic_classes>
             </slice1>
+            <slice2>
+                <slice_id>2</slice_id>
+                <traffic_classes>BEST_EFFORT,REAL_TIME,ELASTIC</traffic_classes>
+            </slice2>
+            <slice3>
+                <slice_id>3</slice_id>
+                <traffic_classes>BEST_EFFORT,REAL_TIME</traffic_classes>
+            </slice3>
         </slices>
         <traffic_classification>
             <slice_1_be>
diff --git a/TestON/tests/USECASE/SegmentRouting/QOSNonMobile/dependencies/QOSNonMobileTest.py b/TestON/tests/USECASE/SegmentRouting/QOSNonMobile/dependencies/QOSNonMobileTest.py
index f599c5d..faaeb04 100644
--- a/TestON/tests/USECASE/SegmentRouting/QOSNonMobile/dependencies/QOSNonMobileTest.py
+++ b/TestON/tests/USECASE/SegmentRouting/QOSNonMobile/dependencies/QOSNonMobileTest.py
@@ -27,69 +27,68 @@
 
             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
+            main.step("Verify slices and traffic Classes")
+
+            slices_onos = onos_rest.getSlices(debug=True)
+
+            # Sanity check for the API, at least the default slice should be there.
+            utilities.assert_equal(
+                expect=True,
+                actual={"SliceId": 0} in json.loads(slices_onos),
+                onpass="Default slice verified in slicing service",
+                onfail="Error in verifying default slice in slicing service"
+            )
+
             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)
+                utilities.assert_equal(
+                    expect=True,
+                    actual={"SliceId": slice_id} in json.loads(slices_onos),
+                    onpass="Verified presence of slice %s in slicing service" % slice_id,
+                    onfail="Slice %s not found in slicing service" % slice_id
+                )
 
-                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 = slice.get("traffic_classes", "").split(",")
+
                 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"
-            )
+                for tc in tcs:
+                    utilities.assert_equal(
+                        expect=True,
+                        actual={"TrafficClass": tc} in json.loads(tcs_onos),
+                        onpass="Verified presence of TC %s for slice %s in slicing service" % (tc, slice_id),
+                        onfail="TC %s not found for slice %s in slicing service" % (tc, slice_id)
+                    )
 
-            main.step("Add and verify slicing traffic classifier")
-            flows_in_slicing = True
-            for slicing_cfg_name in main.params["SLICING"]["traffic_classification"]:
+            main.step("Add and verify traffic classifier flows")
+            new_flows = 0
+            for flow_name in main.params["SLICING"]["traffic_classification"]:
                 new_flows += 1
-                slicing_config = main.params["SLICING"]["traffic_classification"][
-                    slicing_cfg_name]
+                flow_config = main.params["SLICING"]["traffic_classification"][
+                    flow_name]
 
-                traffic_selector = self.__cleanupTrafficSelector(slicing_config.get("traffic_selector", []))
+                traffic_selector = self.__cleanupTrafficSelector(flow_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"),
+                    slice_id=int(flow_config.get("slice_id")),
+                    traffic_class=flow_config.get("traffic_class"),
                     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"),
+                    slice_id=int(flow_config.get("slice_id")),
+                    traffic_class=flow_config.get("traffic_class"),
                     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"
-            )
+                utilities.assert_equal(
+                    expect=True,
+                    actual=traffic_selector in onos_flows,
+                    onpass="Classifier flow %s installed" % flow_name,
+                    onfail="Classifier flow %s not found after insert" % flow_name
+                )
 
             run.checkFlows(
                 main,
@@ -116,62 +115,29 @@
                     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]
+            main.step("Remove and verify traffic classifier flows")
+            for flow_name in main.params["SLICING"]["traffic_classification"]:
+                flow_config = main.params["SLICING"]["traffic_classification"][
+                    flow_name]
 
-                traffic_selector = self.__cleanupTrafficSelector(slicing_config.get("traffic_selector", []))
+                traffic_selector = self.__cleanupTrafficSelector(flow_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"),
+                    slice_id=int(flow_config.get("slice_id")),
+                    traffic_class=flow_config.get("traffic_class"),
                     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"),
+                onos_flow = onos_rest.getSlicingClassifierFlow(
+                    slice_id=int(flow_config.get("slice_id")),
+                    traffic_class=flow_config.get("traffic_class"),
                     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"
-            )
+                utilities.assert_equal(
+                    expect="[]",
+                    actual=onos_flow,
+                    onpass="Classifier flow %s removed from slicing service" % flow_name,
+                    onfail="Unable to remove classifier flow %s from slicing service" % flow_name
+                )
 
             run.checkFlows(main, minFlowCount=original_flows_number)
         finally:
diff --git a/TestON/tests/USECASE/SegmentRouting/UP4/UP4.params b/TestON/tests/USECASE/SegmentRouting/UP4/UP4.params
index 0e5bdb9..f4c3173 100644
--- a/TestON/tests/USECASE/SegmentRouting/UP4/UP4.params
+++ b/TestON/tests/USECASE/SegmentRouting/UP4/UP4.params
@@ -39,7 +39,7 @@
                 <teid>100</teid>
                 <up_id>10</up_id>
                 <down_id>11</down_id>
-                <qfi>0</qfi>
+                <tc>0</tc>
                 <five_g>False</five_g>
             </ue1>
             <ue2>
@@ -47,7 +47,7 @@
                 <teid>200</teid>
                 <up_id>20</up_id>
                 <down_id>21</down_id>
-                <qfi>0</qfi>
+                <tc>0</tc>
                 <five_g>False</five_g>
             </ue2>
             <ue3>
@@ -55,7 +55,7 @@
                 <teid>201</teid>
                 <up_id>30</up_id>
                 <down_id>31</down_id>
-                <qfi>0</qfi>
+                <tc>0</tc>
                 <five_g>False</five_g>
             </ue3>
         </ues>
diff --git a/TestON/tests/USECASE/SegmentRouting/dependencies/up4.py b/TestON/tests/USECASE/SegmentRouting/dependencies/up4.py
index fe5ddf9..2951158 100644
--- a/TestON/tests/USECASE/SegmentRouting/dependencies/up4.py
+++ b/TestON/tests/USECASE/SegmentRouting/dependencies/up4.py
@@ -50,8 +50,8 @@
                 <teid>200</teid>
                 <up_id>20</up_id>
                 <down_id>21</down_id>
-                <!-- QFI == TC, QFI = 0 means BEST EFFORT -->
-                <qfi>2</qfi>
+                <!-- TC 0 means BEST EFFORT -->
+                <tc>2</tc>
                 <five_g>False</five_g>
             </ue2>
         </ues>
@@ -401,21 +401,21 @@
             ue_address, tunn_peer_id)
 
     def upTerminationOnosString(self, ue_address, up_id=None, ctr_id_up=None,
-                                qfi=None, **kwargs):
+                                tc=None, **kwargs):
         if up_id is not None:
             ctr_id_up = up_id
         return "TerminationUL{{Match(ue_addr={})->(CTR_ID={}, TC={})}}".format(
-            ue_address, ctr_id_up, qfi)  # TC == QFI
+            ue_address, ctr_id_up, tc)
 
     def downTerminationOnosString(self, ue_address, teid=None, down_id=None,
-                                  ctr_id_down=None, teid_down=None, qfi=None,
+                                  ctr_id_down=None, teid_down=None, tc=None,
                                   **kwargs):
         if down_id is not None:
             ctr_id_down = down_id
         if teid is not None:
             teid_down = teid
         return "TerminationDL{{Match(ue_addr={})->(TEID={}, CTR_ID={}, QFI={}, TC={})}}".format(
-            ue_address, teid_down, ctr_id_down, qfi, qfi)  # TC == QFI
+            ue_address, teid_down, ctr_id_down, tc, tc)
 
     def gtpTunnelPeerOnosString(self, ue_name, down_id=None, tunn_peer_id=None,
                                 **kwargs):
@@ -429,8 +429,8 @@
     def __sanitizeUeData(ue):
         if "five_g" in ue and type(ue["five_g"]) != bool:
             ue["five_g"] = bool(strtobool(ue["five_g"]))
-        if "qfi" in ue and ue["qfi"] == "":
-            ue["qfi"] = 0
+        if "tc" in ue and ue["tc"] == "":
+            ue["tc"] = 0
         return ue
 
     def attachUe(self, ue_name, ue_address,
@@ -438,34 +438,34 @@
                  teid_up=None, teid_down=None,
                  ctr_id_up=None, ctr_id_down=None,
                  tunn_peer_id=None,
-                 qfi=None, five_g=False):
+                 tc=None, five_g=False):
         self.__programUp4Rules(ue_name,
                                ue_address,
                                teid, up_id, down_id,
                                teid_up, teid_down,
                                ctr_id_up, ctr_id_down,
                                tunn_peer_id,
-                               qfi, five_g, action="program")
+                               tc, five_g, action="program")
 
     def detachUe(self, ue_name, ue_address,
                  teid=None, up_id=None, down_id=None,
                  teid_up=None, teid_down=None,
                  ctr_id_up=None, ctr_id_down=None,
                  tunn_peer_id=None,
-                 qfi=None, five_g=False):
+                 tc=None, five_g=False):
         self.__programUp4Rules(ue_name,
                                ue_address,
                                teid, up_id, down_id,
                                teid_up, teid_down,
                                ctr_id_up, ctr_id_down,
                                tunn_peer_id,
-                               qfi, five_g, action="clear")
+                               tc, five_g, action="clear")
 
     def __programUp4Rules(self, ue_name, ue_address,
                           teid=None, up_id=None, down_id=None,
                           teid_up=None, teid_down=None, ctr_id_up=None,
                           ctr_id_down=None, tunn_peer_id=None,
-                          qfi=0, five_g=False, app_id=DEFAULT_APP_ID,
+                          tc=0, five_g=False, app_id=DEFAULT_APP_ID,
                           action="program"):
         if up_id is not None:
             ctr_id_up = up_id
@@ -528,8 +528,7 @@
         matchFields['app_id'] = str(app_id)
         # Action params
         actionParams['ctr_idx'] = str(ctr_id_up)
-        # 1-1 mapping between QFI and TC
-        actionParams['tc'] = str(qfi)
+        actionParams['tc'] = str(tc)
         if not self.__add_entry(tableName, actionName, matchFields,
                                 actionParams, entries, action):
             return False
@@ -547,8 +546,8 @@
         actionParams['teid'] = str(teid_down)
         actionParams['ctr_idx'] = str(ctr_id_down)
         # 1-1 mapping between QFI and TC
-        actionParams['tc'] = str(qfi)
-        actionParams['qfi'] = str(qfi)
+        actionParams['tc'] = str(tc)
+        actionParams['qfi'] = str(tc)
         if not self.__add_entry(tableName, actionName, matchFields,
                                 actionParams, entries, action):
             return False