[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