|
| 1 | +import json |
| 2 | +import logging |
| 3 | +import pytest |
| 4 | +from tests.common.helpers.assertions import pytest_assert |
| 5 | +from tests.common.gu_utils import apply_patch, delete_tmpfile, expect_op_success, generate_tmpfile |
| 6 | +from tests.generic_config_updater.add_cluster.helpers import add_content_to_patch_file, \ |
| 7 | + change_interface_admin_state_for_namespace, get_cfg_info_from_dut |
| 8 | + |
| 9 | +pytestmark = [ |
| 10 | + pytest.mark.topology("t2") |
| 11 | + ] |
| 12 | + |
| 13 | +logger = logging.getLogger(__name__) |
| 14 | + |
| 15 | + |
| 16 | +# ----------------------------- |
| 17 | +# Helper functions that modify configuration via apply-patch |
| 18 | +# ----------------------------- |
| 19 | + |
| 20 | +def apply_patch_remove_qos_for_namespace(duthost, |
| 21 | + namespace, |
| 22 | + apply=True, |
| 23 | + verify=True, |
| 24 | + patch_file=""): |
| 25 | + """ |
| 26 | + Applies a patch to remove QoS configurations for a specific namespace on the DUT host. |
| 27 | +
|
| 28 | + This function removes QoS configurations from the specified namespace by applying a patch on the DUT host. |
| 29 | + It can optionally verify that the QoS settings have been removed after the operation. |
| 30 | +
|
| 31 | + Applies changes at configuration paths: |
| 32 | + - /<namespace>/BUFFER_PG |
| 33 | + - /<namespace>/BUFFER_QUEUE |
| 34 | + - /<namespace>/PORT_QOS_MAP |
| 35 | + - /<namespace>/QUEUE |
| 36 | + """ |
| 37 | + |
| 38 | + logger.info("{}: Removing QoS for ASIC namespace {}".format( |
| 39 | + duthost.hostname, namespace) |
| 40 | + ) |
| 41 | + json_patch = [] |
| 42 | + paths_to_remove = ['BUFFER_PG', 'BUFFER_QUEUE', 'PORT_QOS_MAP', 'QUEUE'] |
| 43 | + for path in paths_to_remove: |
| 44 | + json_patch.append({ |
| 45 | + "op": "remove", |
| 46 | + "path": "/{}/{}".format(namespace, path) |
| 47 | + }) |
| 48 | + |
| 49 | + tmpfile = generate_tmpfile(duthost) |
| 50 | + |
| 51 | + if apply: |
| 52 | + |
| 53 | + try: |
| 54 | + output = apply_patch(duthost, json_data=json_patch, dest_file=tmpfile) |
| 55 | + expect_op_success(duthost, output) |
| 56 | + if verify is True: |
| 57 | + # verify CONFIG_DB |
| 58 | + for path in paths_to_remove: |
| 59 | + logger.info("Verifying CONFIG_DB is cleared for path {}.".format(path)) |
| 60 | + pytest_assert(not get_cfg_info_from_dut(duthost, path, namespace), |
| 61 | + "Found unexpected QoS config for {} in CONFIG_DB.".format(path)) |
| 62 | + logger.info("CONFIG_DB successfully verified that doesn't contain QoS config.") |
| 63 | + # verify APPL_DB |
| 64 | + appl_db_tables = ['BUFFER_PG_TABLE', 'BUFFER_QUEUE_TABLE'] |
| 65 | + for table in appl_db_tables: |
| 66 | + cmd = "sonic-db-cli -n {} APPL_DB keys {}:*".format(namespace, table) |
| 67 | + logger.info("Verifying APPL_DB table {} is cleared.".format(table)) |
| 68 | + pytest_assert(not duthost.shell(cmd)["stdout"], |
| 69 | + "Found unexpected QoS config for {} in APPL_DB.".format(table)) |
| 70 | + logger.info("APPL_DB successfully verified that doesn't contain QoS config.") |
| 71 | + # verify ASIC_DB |
| 72 | + asic_db_tables = ['SAI_OBJECT_TYPE_QUEUE'] |
| 73 | + for table in asic_db_tables: |
| 74 | + cmd = "sonic-db-cli -n {} ASIC_DB keys *:{}:*".format(namespace, table) |
| 75 | + logger.info("{}: Verifying ASIC_DB table {} is cleared.".format(path, table)) |
| 76 | + # pytest_assert(duthost.shell(cmd)["stdout"] == '{}', |
| 77 | + # "Found unexpected QoS config for {} in ASIC_DB.".format(table)) |
| 78 | + # W/A until verifying if ASIC_DB clearance fro QUEUE is an issue. |
| 79 | + if duthost.shell(cmd)["stdout"] != '{}': |
| 80 | + logger.warning("Found unexpected QoS config for {} in ASIC_DB.".format(path)) |
| 81 | + else: |
| 82 | + logger.info("ASIC_DB successfully verified that doesn't contain QoS config.") |
| 83 | + # logger.info("ASIC_DB successfully verified that doesn't contain QoS config.") |
| 84 | + finally: |
| 85 | + delete_tmpfile(duthost, tmpfile) |
| 86 | + else: |
| 87 | + add_content_to_patch_file(json.dumps(json_patch, indent=4), patch_file) |
| 88 | + |
| 89 | + |
| 90 | +def apply_patch_add_qos_for_namespace(duthost, |
| 91 | + namespace, |
| 92 | + qos_config, |
| 93 | + apply=True, |
| 94 | + verify=True, |
| 95 | + patch_file=""): |
| 96 | + """ |
| 97 | + Applies a patch to add QoS configuration for a specific namespace on the DUT host that had been previously removed |
| 98 | + from function 'apply_patch_remove_qos_for_namespace'. |
| 99 | +
|
| 100 | + This function adds QoS configuration for the specified namespace by applying a patch on the DUT host. |
| 101 | + It utilizesn the qos_config dictionary that includes all the requried information to add. |
| 102 | + Optionally, it can verify the applied changes to ensure they meet the expected parameters. |
| 103 | +
|
| 104 | + Args: |
| 105 | + duthost (object): DUT host object where the patch to add interfaces will be applied. |
| 106 | + namespace (str): The namespace where the network interfaces should be added. |
| 107 | + qos_config (dict): A dictionary containing the QoS configuration parameters to be applied. |
| 108 | + verify (bool, optional): If True, verifies the configuration after applying the patch. Defaults to True. |
| 109 | +
|
| 110 | + Returns: |
| 111 | + None |
| 112 | +
|
| 113 | + Raises: |
| 114 | + Exception: If the patch or verification fails. |
| 115 | + """ |
| 116 | + logger.info("{}: Adding QoS for ASIC namespace {}".format( |
| 117 | + duthost.hostname, namespace) |
| 118 | + ) |
| 119 | + json_patch = [] |
| 120 | + for path, value in list(qos_config.items()): |
| 121 | + json_patch.append({ |
| 122 | + "op": "add", |
| 123 | + "path": "/{}/{}".format(namespace, path), |
| 124 | + "value": value |
| 125 | + }) |
| 126 | + |
| 127 | + if apply: |
| 128 | + |
| 129 | + tmpfile = generate_tmpfile(duthost) |
| 130 | + logger.info("Temporary file: {}".format(tmpfile)) |
| 131 | + try: |
| 132 | + output = apply_patch(duthost, json_data=json_patch, dest_file=tmpfile) |
| 133 | + expect_op_success(duthost, output) |
| 134 | + if verify is True: |
| 135 | + # verify CONFIG_DB |
| 136 | + for path, value in list(qos_config.items()): |
| 137 | + logger.info("Verifying CONFIG_DB is added back for path {}.".format(path)) |
| 138 | + pytest_assert(get_cfg_info_from_dut(duthost, path, namespace) == value, |
| 139 | + "Didn't find expected QoS config for {} in CONFIG_DB.".format(path)) |
| 140 | + logger.info("CONFIG_DB successfully verified to contain expected QoS config.") |
| 141 | + # verify APPL_DB |
| 142 | + appl_db_tables = ['BUFFER_PG', 'BUFFER_QUEUE'] |
| 143 | + for table in appl_db_tables: |
| 144 | + cmd = "sonic-db-cli -n {} APPL_DB keys {}_TABLE:*".format(namespace, table) |
| 145 | + logger.info("Verifying APPL_DB table {} includes valid config.".format(table)) |
| 146 | + pytest_assert(len(duthost.shell(cmd)["stdout"].split('\n')) == len(qos_config.get(table)), |
| 147 | + "Didn't find expected config for {} in APPL_DB.".format(table)) |
| 148 | + logger.info("APPL_DB successfully verified to include QoS config.") |
| 149 | + # verify ASIC_DB |
| 150 | + asic_db_tables = ['SAI_OBJECT_TYPE_QUEUE'] |
| 151 | + for table in asic_db_tables: |
| 152 | + cmd = "sonic-db-cli -n {} ASIC_DB keys *:{}:*".format(namespace, table) |
| 153 | + logger.info("Verifying ASIC_DB table {} includes valid config.".format(table)) |
| 154 | + pytest_assert(duthost.shell(cmd)["stdout"] != '{}', |
| 155 | + "Found empty QoS config for {} in ASIC_DB.".format(table)) |
| 156 | + logger.info("ASIC_DB successfully verified to include QoS config.") |
| 157 | + finally: |
| 158 | + delete_tmpfile(duthost, tmpfile) |
| 159 | + else: |
| 160 | + add_content_to_patch_file(json.dumps(json_patch, indent=4), patch_file) |
| 161 | + |
| 162 | + |
| 163 | +# ----------------------------- |
| 164 | +# Test Definitions |
| 165 | +# ----------------------------- |
| 166 | + |
| 167 | +@pytest.mark.disable_loganalyzer |
| 168 | +def test_load_qos(duthosts, |
| 169 | + rand_one_dut_front_end_hostname, |
| 170 | + enum_rand_one_asic_namespace): |
| 171 | + """ |
| 172 | + Verifies QoS changes in the configuration path via the `apply-patch` mechanism, |
| 173 | + specifically for the following configuration tables: |
| 174 | + BUFFER_PG, BUFFER_QUEUE, PORT_QOS_MAP, and QUEUE. |
| 175 | +
|
| 176 | + Steps involved: |
| 177 | + 1. **Backup of existing configuration**: The current configuration in the aforementioned tables is saved. |
| 178 | + 2. **Removal operation**: The `apply-patch remove` command is used to delete any info related to these config paths. |
| 179 | + 3. **Addition operation**: The initial saved configuration is restored using the `apply-patch add` command. |
| 180 | +
|
| 181 | + During both the removal and addition phases, the following verifications are performed: |
| 182 | + - Ensure the changes have been correctly applied. |
| 183 | + - Confirm that the changes are properly reflected in `CONFIG_DB`. |
| 184 | + - Validate the propagation of changes to relevant tables in both `APPL_DB` and `ASIC_DB`. |
| 185 | +
|
| 186 | + Parameters: |
| 187 | + - `duthosts`: The DUT (Device Under Test) hosts participating in the test. |
| 188 | + - `rand_one_dut_front_end_hostname`: The randomly selected hostname of one front-end DUT. |
| 189 | + - `enum_rand_one_asic_namespace`: The randomly selected asic namespace. |
| 190 | +
|
| 191 | + """ |
| 192 | + |
| 193 | + duthost = duthosts[rand_one_dut_front_end_hostname] |
| 194 | + |
| 195 | + config_facts = duthost.config_facts( |
| 196 | + host=duthost.hostname, source="running", namespace=enum_rand_one_asic_namespace |
| 197 | + )['ansible_facts'] |
| 198 | + |
| 199 | + buffer_pg_info = get_cfg_info_from_dut(duthost, 'BUFFER_PG', enum_rand_one_asic_namespace) |
| 200 | + buffer_queue_info = get_cfg_info_from_dut(duthost, 'BUFFER_QUEUE', enum_rand_one_asic_namespace) |
| 201 | + port_qos_map_info = get_cfg_info_from_dut(duthost, 'PORT_QOS_MAP', enum_rand_one_asic_namespace) |
| 202 | + queue_info = get_cfg_info_from_dut(duthost, 'QUEUE', enum_rand_one_asic_namespace) |
| 203 | + qos_config = {'BUFFER_PG': buffer_pg_info, |
| 204 | + 'BUFFER_QUEUE': buffer_queue_info, |
| 205 | + 'PORT_QOS_MAP': port_qos_map_info, |
| 206 | + 'QUEUE': queue_info} |
| 207 | + |
| 208 | + # shutdown interfaces for namespace |
| 209 | + change_interface_admin_state_for_namespace(config_facts, |
| 210 | + duthost, |
| 211 | + enum_rand_one_asic_namespace, |
| 212 | + status='down', |
| 213 | + apply=True, |
| 214 | + verify=True) |
| 215 | + # remove qos for namespace |
| 216 | + apply_patch_remove_qos_for_namespace(duthost, |
| 217 | + enum_rand_one_asic_namespace, |
| 218 | + apply=True, |
| 219 | + verify=True) |
| 220 | + # add qos for namespace |
| 221 | + apply_patch_add_qos_for_namespace(duthost, |
| 222 | + enum_rand_one_asic_namespace, |
| 223 | + qos_config, |
| 224 | + apply=True, |
| 225 | + verify=True) |
| 226 | + # startup interfaces for namespace |
| 227 | + change_interface_admin_state_for_namespace(config_facts, |
| 228 | + duthost, |
| 229 | + enum_rand_one_asic_namespace, |
| 230 | + status='up', |
| 231 | + apply=True, |
| 232 | + verify=True) |
0 commit comments