Daniele Moro | 04a62d1 | 2021-10-06 17:37:36 +0200 | [diff] [blame] | 1 | from tests.USECASE.SegmentRouting.dependencies.Testcaselib import \ |
| 2 | Testcaselib as run |
| 3 | from tests.USECASE.SegmentRouting.dependencies.trex import Trex |
| 4 | import json |
| 5 | |
| 6 | |
| 7 | class QOSNonMobileTest: |
| 8 | |
| 9 | def runTest(self, main, test_idx, n_switches): |
Jon Hall | dd05bbc | 2022-01-27 12:14:50 -0800 | [diff] [blame^] | 10 | try: |
| 11 | run.initTest(main) |
| 12 | main.log.info(main.Cluster.numCtrls) |
| 13 | main.Cluster.setRunningNode(3) |
| 14 | run.installOnos(main, skipPackage=True, cliSleep=5) |
Daniele Moro | 04a62d1 | 2021-10-06 17:37:36 +0200 | [diff] [blame] | 15 | |
Jon Hall | dd05bbc | 2022-01-27 12:14:50 -0800 | [diff] [blame^] | 16 | # Use the first available ONOS instance CLI |
| 17 | onos_rest = main.Cluster.active(0).REST |
| 18 | onos_cli = main.Cluster.active(0).CLI |
Daniele Moro | 04a62d1 | 2021-10-06 17:37:36 +0200 | [diff] [blame] | 19 | |
Jon Hall | dd05bbc | 2022-01-27 12:14:50 -0800 | [diff] [blame^] | 20 | # Load traffic config for the current test case |
| 21 | cfgFile = "%s/tests/CASE_%d.json" % (main.configPath, test_idx) |
| 22 | with open(cfgFile) as cfg: |
| 23 | testCfg = json.load(cfg) |
Daniele Moro | 04a62d1 | 2021-10-06 17:37:36 +0200 | [diff] [blame] | 24 | |
Jon Hall | dd05bbc | 2022-01-27 12:14:50 -0800 | [diff] [blame^] | 25 | trex = Trex() |
| 26 | trex.setup(main.TRexClient) |
Daniele Moro | 04a62d1 | 2021-10-06 17:37:36 +0200 | [diff] [blame] | 27 | |
Jon Hall | dd05bbc | 2022-01-27 12:14:50 -0800 | [diff] [blame^] | 28 | original_flows_number = onos_cli.checkFlowCount() |
Daniele Moro | 04a62d1 | 2021-10-06 17:37:36 +0200 | [diff] [blame] | 29 | |
Jon Hall | dd05bbc | 2022-01-27 12:14:50 -0800 | [diff] [blame^] | 30 | main.step("Add and verify Slices and Traffic Classes") |
| 31 | added_slices = True |
| 32 | new_flows = 0 # one for every new TC in SLICE and 1 for every Flow Classifier |
| 33 | for slice_name in main.params["SLICING"]["slices"]: |
| 34 | slice = main.params["SLICING"]["slices"][slice_name] |
| 35 | if "slice_id" not in slice: |
| 36 | continue |
| 37 | slice_id = int(slice["slice_id"]) |
| 38 | onos_rest.addSlice(slice_id=slice_id, debug=True) |
| 39 | slices_onos = onos_rest.getSlices(debug=True) |
| 40 | # Verify the slice has been added |
| 41 | added_slices = added_slices and \ |
| 42 | {"SliceId": slice_id} in json.loads(slices_onos) |
Daniele Moro | 04a62d1 | 2021-10-06 17:37:36 +0200 | [diff] [blame] | 43 | |
Jon Hall | dd05bbc | 2022-01-27 12:14:50 -0800 | [diff] [blame^] | 44 | tcs = [] |
| 45 | for tc in slice.get("traffic_classes", "").split(","): |
| 46 | onos_rest.addTrafficClassToSlice(slice_id=slice_id, |
| 47 | traffic_class=tc, |
| 48 | debug=True) |
| 49 | tcs.append({"TrafficClass": tc}) |
| 50 | new_flows += 1 |
| 51 | tcs_onos = onos_rest.getTrafficClasses(slice_id=slice_id, |
| 52 | debug=True) |
| 53 | # Verify the TC has been added to the slice |
| 54 | added_slices = added_slices and \ |
| 55 | sorted(json.loads(tcs_onos)) == sorted(tcs) |
| 56 | utilities.assert_equal( |
| 57 | expect=True, |
| 58 | actual=added_slices, |
| 59 | onpass="Slices and Traffic Classes installed in slicing service", |
| 60 | onfail="Error in installing Slices and Traffic Classes in slicing service" |
| 61 | ) |
| 62 | |
| 63 | main.step("Add and verify slicing traffic classifier") |
| 64 | flows_in_slicing = True |
| 65 | for slicing_cfg_name in main.params["SLICING"]["traffic_classification"]: |
Daniele Moro | 04a62d1 | 2021-10-06 17:37:36 +0200 | [diff] [blame] | 66 | new_flows += 1 |
Jon Hall | dd05bbc | 2022-01-27 12:14:50 -0800 | [diff] [blame^] | 67 | slicing_config = main.params["SLICING"]["traffic_classification"][ |
| 68 | slicing_cfg_name] |
Daniele Moro | 04a62d1 | 2021-10-06 17:37:36 +0200 | [diff] [blame] | 69 | |
Jon Hall | dd05bbc | 2022-01-27 12:14:50 -0800 | [diff] [blame^] | 70 | traffic_selector = self.__cleanupTrafficSelector(slicing_config.get("traffic_selector", [])) |
| 71 | onos_rest.addSlicingClassifierFlow( |
| 72 | slice_id=int(slicing_config.get("slice_id", "0")), |
| 73 | traffic_class=slicing_config.get("traffic_class", |
| 74 | "BEST_EFFORT"), |
| 75 | traffic_selector=traffic_selector, |
| 76 | debug=True |
| 77 | ) |
| 78 | # Verify classifier flows |
| 79 | onos_flows = json.loads(onos_rest.getSlicingClassifierFlow( |
| 80 | slice_id=int(slicing_config.get("slice_id", "0")), |
| 81 | traffic_class=slicing_config.get("traffic_class", |
| 82 | "BEST_EFFORT"), |
| 83 | debug=True |
| 84 | )) |
| 85 | flows_in_slicing = flows_in_slicing and traffic_selector in onos_flows |
Daniele Moro | 04a62d1 | 2021-10-06 17:37:36 +0200 | [diff] [blame] | 86 | |
Jon Hall | dd05bbc | 2022-01-27 12:14:50 -0800 | [diff] [blame^] | 87 | utilities.assert_equal( |
| 88 | expect=True, |
| 89 | actual=flows_in_slicing, |
| 90 | onpass="Traffic Classifier Flows installed in slicing service", |
| 91 | onfail="Error in installing Classifier Flows in slicing service" |
Daniele Moro | 04a62d1 | 2021-10-06 17:37:36 +0200 | [diff] [blame] | 92 | ) |
Daniele Moro | 04a62d1 | 2021-10-06 17:37:36 +0200 | [diff] [blame] | 93 | |
Jon Hall | dd05bbc | 2022-01-27 12:14:50 -0800 | [diff] [blame^] | 94 | run.checkFlows( |
| 95 | main, |
| 96 | minFlowCount=original_flows_number + (new_flows * n_switches) |
Daniele Moro | 04a62d1 | 2021-10-06 17:37:36 +0200 | [diff] [blame] | 97 | ) |
Jon Hall | dd05bbc | 2022-01-27 12:14:50 -0800 | [diff] [blame^] | 98 | |
| 99 | main.step("Send traffic with TRex") |
| 100 | for flow in testCfg["flows"]: |
| 101 | trex.createFlow(flow) |
| 102 | results = trex.sendAndReceiveTraffic(testCfg["duration"]) |
| 103 | trex.verifyCongestion(results) |
| 104 | |
| 105 | trex.logPortStats() |
| 106 | for flow in testCfg["flows"]: |
| 107 | trex.logFlowStats(flow) |
| 108 | |
| 109 | # Assert Flow Stats |
| 110 | for flow in testCfg["flows"]: |
| 111 | if trex.isFlowStats(flow): |
| 112 | main.step("{}: Assert RX Packets".format(flow)) |
| 113 | trex.assertRxPackets(flow) |
| 114 | main.step("{}: Assert Dropped Packets".format(flow)) |
| 115 | trex.assertDroppedPacket(flow) |
| 116 | main.step("{}: Assert 99.9 Percentile Latency".format(flow)) |
| 117 | trex.assert99_9PercentileLatency(flow) |
| 118 | |
| 119 | main.step("Remove and verify slicing traffic classifier") |
| 120 | no_flows_in_slicing = True |
| 121 | for slicing_cfg_name in main.params["SLICING"]["traffic_classification"]: |
| 122 | slicing_config = main.params["SLICING"]["traffic_classification"][ |
| 123 | slicing_cfg_name] |
| 124 | |
| 125 | traffic_selector = self.__cleanupTrafficSelector(slicing_config.get("traffic_selector", [])) |
| 126 | onos_rest.removeSlicingClassifierFlow( |
| 127 | slice_id=int(slicing_config.get("slice_id", "0")), |
| 128 | traffic_class=slicing_config.get("traffic_class", |
| 129 | "BEST_EFFORT"), |
| 130 | traffic_selector=traffic_selector, |
| 131 | debug=True |
| 132 | ) |
| 133 | flow = onos_rest.getSlicingClassifierFlow( |
| 134 | slice_id=int(slicing_config.get("slice_id", "0")), |
| 135 | traffic_class=slicing_config.get("traffic_class", |
| 136 | "BEST_EFFORT"), |
| 137 | debug=True |
| 138 | ) |
| 139 | no_flows_in_slicing = no_flows_in_slicing and flow == "[]" |
| 140 | |
| 141 | utilities.assert_equal( |
| 142 | expect=True, |
| 143 | actual=no_flows_in_slicing, |
| 144 | onpass="Traffic Classifier Flows removed in slicing service", |
| 145 | onfail="Error in removing Classifier Flows in slicing service" |
Daniele Moro | 04a62d1 | 2021-10-06 17:37:36 +0200 | [diff] [blame] | 146 | ) |
Daniele Moro | 04a62d1 | 2021-10-06 17:37:36 +0200 | [diff] [blame] | 147 | |
Jon Hall | dd05bbc | 2022-01-27 12:14:50 -0800 | [diff] [blame^] | 148 | main.step("Remove and verify Slices and Traffic Classes") |
| 149 | removed_slices = [] |
| 150 | for slice_name in main.params["SLICING"]["slices"]: |
| 151 | slice = main.params["SLICING"]["slices"][slice_name] |
| 152 | if "slice_id" not in slice: |
| 153 | continue |
| 154 | slice_id = int(slice["slice_id"]) |
| 155 | for tc in slice.get("traffic_classes", "").split(","): |
| 156 | # BEST_EFFORT must be removed as last, or we can leave it, |
| 157 | # it will be removed when removing the slice |
| 158 | if tc != "BEST_EFFORT": |
| 159 | onos_rest.removeTrafficClassToSlice(slice_id=slice_id, |
| 160 | traffic_class=tc, |
| 161 | debug=True) |
| 162 | # Do not try to remove the Default Slice! |
| 163 | if slice_id != 0: |
| 164 | onos_rest.removeSlice(slice_id=slice_id, debug=True) |
| 165 | removed_slices.append(slice_id) |
Daniele Moro | 04a62d1 | 2021-10-06 17:37:36 +0200 | [diff] [blame] | 166 | |
Jon Hall | dd05bbc | 2022-01-27 12:14:50 -0800 | [diff] [blame^] | 167 | slices_onos = json.loads(onos_rest.getSlices(debug=True)) |
| 168 | utilities.assert_equal( |
| 169 | expect=True, |
| 170 | actual=not any([{"SliceId": slice_id} in slices_onos for slice_id in |
| 171 | removed_slices]), |
| 172 | onpass="Slices and Traffic Classes removed from slicing service", |
| 173 | onfail="Error in removing Slices and Traffic Classes from slicing service" |
| 174 | ) |
Daniele Moro | 04a62d1 | 2021-10-06 17:37:36 +0200 | [diff] [blame] | 175 | |
Jon Hall | dd05bbc | 2022-01-27 12:14:50 -0800 | [diff] [blame^] | 176 | run.checkFlows(main, minFlowCount=original_flows_number) |
| 177 | finally: |
| 178 | main.step("Teardown") |
| 179 | trex.teardown() |
| 180 | run.cleanup(main) |
Daniele Moro | 04a62d1 | 2021-10-06 17:37:36 +0200 | [diff] [blame] | 181 | |
| 182 | def __cleanupTrafficSelector(self, traffic_selector): |
| 183 | ts = { |
| 184 | "criteria": [traffic_selector[criteria] for criteria in |
| 185 | traffic_selector]} |
| 186 | # Cleanup the traffic selector, by converting into integer the |
| 187 | # required fields, conversion is required for checking the result |
| 188 | # from ONOS |
| 189 | for criteria in ts["criteria"]: |
| 190 | if "udpPort" in criteria: |
| 191 | criteria["udpPort"] = int(criteria["udpPort"]) |
| 192 | return ts |