Merge "Fix missing IP_PROTO in slicing classifier flows"
diff --git a/TestON/drivers/common/api/controller/onosrestdriver.py b/TestON/drivers/common/api/controller/onosrestdriver.py
index 7ce7749..927cd25 100755
--- a/TestON/drivers/common/api/controller/onosrestdriver.py
+++ b/TestON/drivers/common/api/controller/onosrestdriver.py
@@ -2465,7 +2465,7 @@
         self.__slicingClassifierFlow( slice_id, traffic_class, traffic_selector,
                                      ip, port, debug, method="DELETE" )
 
-    def getSlicingClassifierFlow( self, slice_id, traffic_class, ip="DEFAULT",
+    def getSlicingClassifierFlows( self, slice_id, traffic_class, ip="DEFAULT",
                                   port="DEFAULT", debug=False ):
         try:
             if ip == "DEFAULT":
@@ -2482,9 +2482,11 @@
                     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 )
+                    # FIXME: use plural in slicing service API
+                    #  TrafficSelector actually points to an array of selectors.
+                    traffic_selectors = json.loads( output ).get( 'TrafficSelector' )
+                    assert traffic_selectors is not None, "Error parsing json object"
+                    return json.dumps( traffic_selectors )
                 else:
                     main.log.error( "Error with REST request, response was: %s: %s" %
                                     ( response[ 0 ], response[ 1 ] ) )
@@ -2496,75 +2498,6 @@
             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" ):
diff --git a/TestON/tests/USECASE/SegmentRouting/QOSNonMobile/QOSNonMobile.params b/TestON/tests/USECASE/SegmentRouting/QOSNonMobile/QOSNonMobile.params
index 56e859a..51c916e 100644
--- a/TestON/tests/USECASE/SegmentRouting/QOSNonMobile/QOSNonMobile.params
+++ b/TestON/tests/USECASE/SegmentRouting/QOSNonMobile/QOSNonMobile.params
@@ -41,20 +41,28 @@
                 <slice_id>1</slice_id>
                 <traffic_class>BEST_EFFORT</traffic_class>
                 <traffic_selector>
-                    <criteria1>
+                    <ipProto>
+                        <type>IP_PROTO</type>
+                        <protocol>17</protocol>
+                    </ipProto>
+                    <udpDst>
                         <type>UDP_DST</type>
                         <udpPort>100</udpPort>
-                    </criteria1>
+                    </udpDst>
                 </traffic_selector>
             </slice_1_be>
             <slice_1_rt>
                 <slice_id>1</slice_id>
                 <traffic_class>REAL_TIME</traffic_class>
                 <traffic_selector>
-                    <criteria1>
+                    <ipProto>
+                        <type>IP_PROTO</type>
+                        <protocol>17</protocol>
+                    </ipProto>
+                    <udpDst>
                         <type>UDP_DST</type>
                         <udpPort>200</udpPort>
-                    </criteria1>
+                    </udpDst>
                 </traffic_selector>
             </slice_1_rt>
         </traffic_classification>
diff --git a/TestON/tests/USECASE/SegmentRouting/QOSNonMobile/dependencies/QOSNonMobileTest.py b/TestON/tests/USECASE/SegmentRouting/QOSNonMobile/dependencies/QOSNonMobileTest.py
index faaeb04..d53b3f8 100644
--- a/TestON/tests/USECASE/SegmentRouting/QOSNonMobile/dependencies/QOSNonMobileTest.py
+++ b/TestON/tests/USECASE/SegmentRouting/QOSNonMobile/dependencies/QOSNonMobileTest.py
@@ -70,7 +70,7 @@
                 flow_config = main.params["SLICING"]["traffic_classification"][
                     flow_name]
 
-                traffic_selector = self.__cleanupTrafficSelector(flow_config.get("traffic_selector", []))
+                traffic_selector = self.__normalizeTrafficSelector(flow_config.get("traffic_selector"))
                 onos_rest.addSlicingClassifierFlow(
                     slice_id=int(flow_config.get("slice_id")),
                     traffic_class=flow_config.get("traffic_class"),
@@ -78,14 +78,14 @@
                     debug=True
                 )
 
-                onos_flows = json.loads(onos_rest.getSlicingClassifierFlow(
+                actual_selectors = json.loads(onos_rest.getSlicingClassifierFlows(
                     slice_id=int(flow_config.get("slice_id")),
                     traffic_class=flow_config.get("traffic_class"),
                     debug=True
                 ))
                 utilities.assert_equal(
                     expect=True,
-                    actual=traffic_selector in onos_flows,
+                    actual=self.__containsTrafficSelector(actual_selectors, traffic_selector),
                     onpass="Classifier flow %s installed" % flow_name,
                     onfail="Classifier flow %s not found after insert" % flow_name
                 )
@@ -120,21 +120,21 @@
                 flow_config = main.params["SLICING"]["traffic_classification"][
                     flow_name]
 
-                traffic_selector = self.__cleanupTrafficSelector(flow_config.get("traffic_selector", []))
+                traffic_selector = self.__normalizeTrafficSelector(flow_config.get("traffic_selector"))
                 onos_rest.removeSlicingClassifierFlow(
                     slice_id=int(flow_config.get("slice_id")),
                     traffic_class=flow_config.get("traffic_class"),
                     traffic_selector=traffic_selector,
                     debug=True
                 )
-                onos_flow = onos_rest.getSlicingClassifierFlow(
+                actual_selectors = json.loads(onos_rest.getSlicingClassifierFlows(
                     slice_id=int(flow_config.get("slice_id")),
                     traffic_class=flow_config.get("traffic_class"),
                     debug=True
-                )
+                ))
                 utilities.assert_equal(
-                    expect="[]",
-                    actual=onos_flow,
+                    expect=False,
+                    actual=self.__containsTrafficSelector(actual_selectors, traffic_selector),
                     onpass="Classifier flow %s removed from slicing service" % flow_name,
                     onfail="Unable to remove classifier flow %s from slicing service" % flow_name
                 )
@@ -145,14 +145,32 @@
             trex.teardown()
             run.cleanup(main)
 
-    def __cleanupTrafficSelector(self, traffic_selector):
+    def __normalizeTrafficSelector(self, traffic_selector):
         ts = {
-            "criteria": [traffic_selector[criteria] for criteria in
+            "criteria": [traffic_selector[criterion] for criterion 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"])
+        # Converts the required fields into integer, required to compare them
+        # with the API result from ONOS.
+        for criterion in ts["criteria"]:
+            if "udpPort" in criterion:
+                criterion["udpPort"] = int(criterion["udpPort"])
+            elif "protocol" in criterion:
+                criterion["protocol"] = int(criterion["protocol"])
         return ts
+
+    def __containsTrafficSelector(self, actual_selectors, expected_selector):
+        # actual_selectors = [{"criteria":[{"type":"IP_PROTO","protocol":17},{"type":"UDP_DST","udpPort":200}]}]
+        expected_criteria = expected_selector["criteria"]
+        for actual_selector in actual_selectors:
+            actual_criteria = actual_selector["criteria"]
+            if len(actual_criteria) != len(expected_criteria):
+                continue
+            for actual_criterion in actual_criteria:
+                # actual_criterion = {"type":"IP_PROTO","protocol":17}
+                if actual_criterion not in expected_criteria:
+                    # Next selector
+                    break
+            else:
+                # We found all criteria in this selector.
+                return True
+        return False