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 | |
Carmelo Cascone | 848d1f5 | 2022-01-27 18:15:58 -0800 | [diff] [blame] | 30 | main.step("Verify slices and traffic Classes") |
| 31 | |
| 32 | slices_onos = onos_rest.getSlices(debug=True) |
| 33 | |
| 34 | # Sanity check for the API, at least the default slice should be there. |
| 35 | utilities.assert_equal( |
| 36 | expect=True, |
| 37 | actual={"SliceId": 0} in json.loads(slices_onos), |
| 38 | onpass="Default slice verified in slicing service", |
| 39 | onfail="Error in verifying default slice in slicing service" |
| 40 | ) |
| 41 | |
Jon Hall | dd05bbc | 2022-01-27 12:14:50 -0800 | [diff] [blame] | 42 | for slice_name in main.params["SLICING"]["slices"]: |
| 43 | slice = main.params["SLICING"]["slices"][slice_name] |
| 44 | if "slice_id" not in slice: |
| 45 | continue |
| 46 | slice_id = int(slice["slice_id"]) |
Carmelo Cascone | 848d1f5 | 2022-01-27 18:15:58 -0800 | [diff] [blame] | 47 | utilities.assert_equal( |
| 48 | expect=True, |
| 49 | actual={"SliceId": slice_id} in json.loads(slices_onos), |
| 50 | onpass="Verified presence of slice %s in slicing service" % slice_id, |
| 51 | onfail="Slice %s not found in slicing service" % slice_id |
| 52 | ) |
Daniele Moro | 04a62d1 | 2021-10-06 17:37:36 +0200 | [diff] [blame] | 53 | |
Carmelo Cascone | 848d1f5 | 2022-01-27 18:15:58 -0800 | [diff] [blame] | 54 | tcs = slice.get("traffic_classes", "").split(",") |
| 55 | |
Jon Hall | dd05bbc | 2022-01-27 12:14:50 -0800 | [diff] [blame] | 56 | tcs_onos = onos_rest.getTrafficClasses(slice_id=slice_id, |
| 57 | debug=True) |
Carmelo Cascone | 848d1f5 | 2022-01-27 18:15:58 -0800 | [diff] [blame] | 58 | for tc in tcs: |
| 59 | utilities.assert_equal( |
| 60 | expect=True, |
| 61 | actual={"TrafficClass": tc} in json.loads(tcs_onos), |
| 62 | onpass="Verified presence of TC %s for slice %s in slicing service" % (tc, slice_id), |
| 63 | onfail="TC %s not found for slice %s in slicing service" % (tc, slice_id) |
| 64 | ) |
Jon Hall | dd05bbc | 2022-01-27 12:14:50 -0800 | [diff] [blame] | 65 | |
Carmelo Cascone | 848d1f5 | 2022-01-27 18:15:58 -0800 | [diff] [blame] | 66 | main.step("Add and verify traffic classifier flows") |
| 67 | new_flows = 0 |
| 68 | for flow_name in main.params["SLICING"]["traffic_classification"]: |
Daniele Moro | 04a62d1 | 2021-10-06 17:37:36 +0200 | [diff] [blame] | 69 | new_flows += 1 |
Carmelo Cascone | 848d1f5 | 2022-01-27 18:15:58 -0800 | [diff] [blame] | 70 | flow_config = main.params["SLICING"]["traffic_classification"][ |
| 71 | flow_name] |
Daniele Moro | 04a62d1 | 2021-10-06 17:37:36 +0200 | [diff] [blame] | 72 | |
Carmelo Cascone | 11bd442 | 2022-02-07 18:22:24 -0800 | [diff] [blame] | 73 | traffic_selector = self.__normalizeTrafficSelector(flow_config.get("traffic_selector")) |
Jon Hall | dd05bbc | 2022-01-27 12:14:50 -0800 | [diff] [blame] | 74 | onos_rest.addSlicingClassifierFlow( |
Carmelo Cascone | 848d1f5 | 2022-01-27 18:15:58 -0800 | [diff] [blame] | 75 | slice_id=int(flow_config.get("slice_id")), |
| 76 | traffic_class=flow_config.get("traffic_class"), |
Jon Hall | dd05bbc | 2022-01-27 12:14:50 -0800 | [diff] [blame] | 77 | traffic_selector=traffic_selector, |
| 78 | debug=True |
| 79 | ) |
Carmelo Cascone | 848d1f5 | 2022-01-27 18:15:58 -0800 | [diff] [blame] | 80 | |
Carmelo Cascone | 11bd442 | 2022-02-07 18:22:24 -0800 | [diff] [blame] | 81 | actual_selectors = json.loads(onos_rest.getSlicingClassifierFlows( |
Carmelo Cascone | 848d1f5 | 2022-01-27 18:15:58 -0800 | [diff] [blame] | 82 | slice_id=int(flow_config.get("slice_id")), |
| 83 | traffic_class=flow_config.get("traffic_class"), |
Jon Hall | dd05bbc | 2022-01-27 12:14:50 -0800 | [diff] [blame] | 84 | debug=True |
| 85 | )) |
Carmelo Cascone | 848d1f5 | 2022-01-27 18:15:58 -0800 | [diff] [blame] | 86 | utilities.assert_equal( |
| 87 | expect=True, |
Carmelo Cascone | 11bd442 | 2022-02-07 18:22:24 -0800 | [diff] [blame] | 88 | actual=self.__containsTrafficSelector(actual_selectors, traffic_selector), |
Carmelo Cascone | 848d1f5 | 2022-01-27 18:15:58 -0800 | [diff] [blame] | 89 | onpass="Classifier flow %s installed" % flow_name, |
| 90 | onfail="Classifier flow %s not found after insert" % flow_name |
| 91 | ) |
Daniele Moro | 04a62d1 | 2021-10-06 17:37:36 +0200 | [diff] [blame] | 92 | |
Jon Hall | dd05bbc | 2022-01-27 12:14:50 -0800 | [diff] [blame] | 93 | run.checkFlows( |
| 94 | main, |
| 95 | minFlowCount=original_flows_number + (new_flows * n_switches) |
Daniele Moro | 04a62d1 | 2021-10-06 17:37:36 +0200 | [diff] [blame] | 96 | ) |
Jon Hall | dd05bbc | 2022-01-27 12:14:50 -0800 | [diff] [blame] | 97 | |
| 98 | main.step("Send traffic with TRex") |
| 99 | for flow in testCfg["flows"]: |
| 100 | trex.createFlow(flow) |
| 101 | results = trex.sendAndReceiveTraffic(testCfg["duration"]) |
| 102 | trex.verifyCongestion(results) |
| 103 | |
| 104 | trex.logPortStats() |
| 105 | for flow in testCfg["flows"]: |
| 106 | trex.logFlowStats(flow) |
| 107 | |
| 108 | # Assert Flow Stats |
| 109 | for flow in testCfg["flows"]: |
| 110 | if trex.isFlowStats(flow): |
| 111 | main.step("{}: Assert RX Packets".format(flow)) |
| 112 | trex.assertRxPackets(flow) |
| 113 | main.step("{}: Assert Dropped Packets".format(flow)) |
| 114 | trex.assertDroppedPacket(flow) |
| 115 | main.step("{}: Assert 99.9 Percentile Latency".format(flow)) |
| 116 | trex.assert99_9PercentileLatency(flow) |
| 117 | |
Carmelo Cascone | 848d1f5 | 2022-01-27 18:15:58 -0800 | [diff] [blame] | 118 | main.step("Remove and verify traffic classifier flows") |
| 119 | for flow_name in main.params["SLICING"]["traffic_classification"]: |
| 120 | flow_config = main.params["SLICING"]["traffic_classification"][ |
| 121 | flow_name] |
Jon Hall | dd05bbc | 2022-01-27 12:14:50 -0800 | [diff] [blame] | 122 | |
Carmelo Cascone | 11bd442 | 2022-02-07 18:22:24 -0800 | [diff] [blame] | 123 | traffic_selector = self.__normalizeTrafficSelector(flow_config.get("traffic_selector")) |
Jon Hall | dd05bbc | 2022-01-27 12:14:50 -0800 | [diff] [blame] | 124 | onos_rest.removeSlicingClassifierFlow( |
Carmelo Cascone | 848d1f5 | 2022-01-27 18:15:58 -0800 | [diff] [blame] | 125 | slice_id=int(flow_config.get("slice_id")), |
| 126 | traffic_class=flow_config.get("traffic_class"), |
Jon Hall | dd05bbc | 2022-01-27 12:14:50 -0800 | [diff] [blame] | 127 | traffic_selector=traffic_selector, |
| 128 | debug=True |
| 129 | ) |
Carmelo Cascone | 11bd442 | 2022-02-07 18:22:24 -0800 | [diff] [blame] | 130 | actual_selectors = json.loads(onos_rest.getSlicingClassifierFlows( |
Carmelo Cascone | 848d1f5 | 2022-01-27 18:15:58 -0800 | [diff] [blame] | 131 | slice_id=int(flow_config.get("slice_id")), |
| 132 | traffic_class=flow_config.get("traffic_class"), |
Jon Hall | dd05bbc | 2022-01-27 12:14:50 -0800 | [diff] [blame] | 133 | debug=True |
Carmelo Cascone | 11bd442 | 2022-02-07 18:22:24 -0800 | [diff] [blame] | 134 | )) |
Carmelo Cascone | 848d1f5 | 2022-01-27 18:15:58 -0800 | [diff] [blame] | 135 | utilities.assert_equal( |
Carmelo Cascone | 11bd442 | 2022-02-07 18:22:24 -0800 | [diff] [blame] | 136 | expect=False, |
| 137 | actual=self.__containsTrafficSelector(actual_selectors, traffic_selector), |
Carmelo Cascone | 848d1f5 | 2022-01-27 18:15:58 -0800 | [diff] [blame] | 138 | onpass="Classifier flow %s removed from slicing service" % flow_name, |
| 139 | onfail="Unable to remove classifier flow %s from slicing service" % flow_name |
| 140 | ) |
Daniele Moro | 04a62d1 | 2021-10-06 17:37:36 +0200 | [diff] [blame] | 141 | |
Jon Hall | dd05bbc | 2022-01-27 12:14:50 -0800 | [diff] [blame] | 142 | run.checkFlows(main, minFlowCount=original_flows_number) |
| 143 | finally: |
| 144 | main.step("Teardown") |
| 145 | trex.teardown() |
| 146 | run.cleanup(main) |
Daniele Moro | 04a62d1 | 2021-10-06 17:37:36 +0200 | [diff] [blame] | 147 | |
Carmelo Cascone | 11bd442 | 2022-02-07 18:22:24 -0800 | [diff] [blame] | 148 | def __normalizeTrafficSelector(self, traffic_selector): |
Daniele Moro | 04a62d1 | 2021-10-06 17:37:36 +0200 | [diff] [blame] | 149 | ts = { |
Carmelo Cascone | 11bd442 | 2022-02-07 18:22:24 -0800 | [diff] [blame] | 150 | "criteria": [traffic_selector[criterion] for criterion in |
Daniele Moro | 04a62d1 | 2021-10-06 17:37:36 +0200 | [diff] [blame] | 151 | traffic_selector]} |
Carmelo Cascone | 11bd442 | 2022-02-07 18:22:24 -0800 | [diff] [blame] | 152 | # Converts the required fields into integer, required to compare them |
| 153 | # with the API result from ONOS. |
| 154 | for criterion in ts["criteria"]: |
| 155 | if "udpPort" in criterion: |
| 156 | criterion["udpPort"] = int(criterion["udpPort"]) |
| 157 | elif "protocol" in criterion: |
| 158 | criterion["protocol"] = int(criterion["protocol"]) |
Daniele Moro | 04a62d1 | 2021-10-06 17:37:36 +0200 | [diff] [blame] | 159 | return ts |
Carmelo Cascone | 11bd442 | 2022-02-07 18:22:24 -0800 | [diff] [blame] | 160 | |
| 161 | def __containsTrafficSelector(self, actual_selectors, expected_selector): |
| 162 | # actual_selectors = [{"criteria":[{"type":"IP_PROTO","protocol":17},{"type":"UDP_DST","udpPort":200}]}] |
| 163 | expected_criteria = expected_selector["criteria"] |
| 164 | for actual_selector in actual_selectors: |
| 165 | actual_criteria = actual_selector["criteria"] |
| 166 | if len(actual_criteria) != len(expected_criteria): |
| 167 | continue |
| 168 | for actual_criterion in actual_criteria: |
| 169 | # actual_criterion = {"type":"IP_PROTO","protocol":17} |
| 170 | if actual_criterion not in expected_criteria: |
| 171 | # Next selector |
| 172 | break |
| 173 | else: |
| 174 | # We found all criteria in this selector. |
| 175 | return True |
| 176 | return False |